/* =====================================================================
   仓库快递包裹库存管理系统 —— 界面样式
   ===================================================================== */
:root {
  --brand: #3b5bdb;
  --brand-2: #5b7cfa;
  --brand-dark: #2b46b8;
  --brand-soft: #eef2ff;

  --ok: #0f9d58;
  --ok-soft: #e7f7ee;
  --warn: #d98410;
  --warn-soft: #fdf3e2;
  --danger: #dc3a3a;
  --danger-soft: #fdeceb;

  --ink: #161a23;
  --ink-2: #3d4453;
  --muted: #7a8494;
  --line: #e4e8f0;
  --line-2: #eef1f6;
  --bg: #f3f5fa;
  --card: #ffffff;

  --sidebar-1: #131a2e;
  --sidebar-2: #1b2440;

  --r-sm: 8px;
  --r: 12px;
  --r-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(19, 26, 46, .06), 0 1px 3px rgba(19, 26, 46, .04);
  --shadow: 0 4px 12px rgba(19, 26, 46, .07), 0 1px 3px rgba(19, 26, 46, .05);
  --shadow-lg: 0 18px 40px rgba(19, 26, 46, .14);
  --font: "Microsoft YaHei UI", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: "Cascadia Mono", "JetBrains Mono", Consolas, "Courier New", monospace;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; font-family: var(--font); color: var(--ink); background: var(--bg);
  font-size: 14px; line-height: 1.55; -webkit-font-smoothing: antialiased;
}
button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
a { color: var(--brand); text-decoration: none; }
svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
[hidden] { display: none !important; }
::selection { background: #c7d2fe; }

/* 滚动条 */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #cbd2e0; border-radius: 20px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: #aab4c8; background-clip: content-box; }

/* ============================ 登录页 ============================ */
.login-screen {
  position: fixed; inset: 0; display: grid; place-items: center; z-index: 60;
  background: radial-gradient(1200px 700px at 15% 10%, #2b3a6b 0%, transparent 60%),
              radial-gradient(1000px 600px at 85% 90%, #3b5bdb 0%, transparent 55%),
              linear-gradient(140deg, #101728 0%, #1a2440 55%, #131a2e 100%);
  overflow: hidden;
}
.login-deco { position: absolute; inset: 0; opacity: .5;
  background-image: radial-gradient(rgba(255,255,255,.07) 1px, transparent 1px);
  background-size: 26px 26px; }
.login-card {
  position: relative; width: 400px; max-width: calc(100vw - 32px);
  background: rgba(255,255,255,.98); border-radius: 20px; padding: 34px 32px 26px;
  box-shadow: 0 30px 70px rgba(0,0,0,.45); animation: popIn .45s cubic-bezier(.2,.9,.3,1.2);
}
@keyframes popIn { from { opacity: 0; transform: translateY(18px) scale(.97); } to { opacity: 1; transform: none; } }
.login-brand { display: flex; gap: 14px; align-items: center; margin-bottom: 26px; }
.login-logo {
  width: 54px; height: 54px; flex: none;
  display: grid; place-items: center;
}
.login-logo img { width: 100%; height: 100%; object-fit: contain; display: block; filter: drop-shadow(0 6px 13px rgba(59,91,219,.30)); }
.login-brand h1 { font-size: 17px; margin: 0; line-height: 1.35; letter-spacing: .2px; }
.login-brand p { margin: 3px 0 0; font-size: 11px; color: var(--muted); letter-spacing: .2px; line-height: 1.4; }
.login-form { display: grid; gap: 15px; }
.login-error { color: var(--danger); font-size: 13px; min-height: 18px; margin-top: -6px; }
.login-tip { margin-top: 20px; padding-top: 16px; border-top: 1px dashed var(--line); font-size: 12px; color: var(--muted); text-align: center; }
.login-tip b { color: var(--brand); font-family: var(--mono); }

/* ============================ 表单元素 ============================ */
.field { display: grid; gap: 6px; }
.field > span { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.input, .select, .textarea {
  width: 100%; padding: 10px 12px; border: 1.5px solid var(--line); border-radius: var(--r-sm);
  background: #fff; outline: none; transition: border-color .16s, box-shadow .16s;
}
.input:hover, .select:hover, .textarea:hover { border-color: #ccd4e3; }
.input:focus, .select:focus, .textarea:focus { border-color: var(--brand); box-shadow: 0 0 0 3.5px rgba(59,91,219,.13); }
.input:disabled, .select:disabled { background: #f5f7fb; color: var(--muted); cursor: not-allowed; }
.textarea { resize: vertical; min-height: 96px; font-family: var(--mono); font-size: 13px; line-height: 1.7; }
.select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5L6 8.5L10 4.5' stroke='%237a8494' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center; padding-right: 30px; }
.input-lg { padding: 12px 14px; font-size: 15px; }
.input-scan { font-family: var(--mono); font-size: 17px; letter-spacing: .6px; padding: 12px 14px; }

/* 扫码/手工输入框用 textarea：<input> 会按规范剥离粘贴内容里的换行符，
   一次粘多个运单号就会变成首尾相连的一长串（曾导致 43 单只入库 1 条）。
   rows=1 + 自适应高度，视觉与单行输入框一致；超长时最多显示 3 行并内部滚动。 */
.input-scan-area {
  display: block; width: 100%; min-height: 46px; max-height: 104px;
  line-height: 1.45; resize: none; overflow-y: auto; white-space: pre-wrap; word-break: break-all;
}

.check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; user-select: none; }
.check input { width: 16px; height: 16px; accent-color: var(--brand); cursor: pointer; }

/* 分段选择器 */
.segmented { display: inline-flex; background: #eef1f7; border-radius: 10px; padding: 3px; gap: 3px; flex-wrap: wrap; }
.segmented button {
  border: 0; background: transparent; padding: 7px 14px; border-radius: 8px; cursor: pointer;
  font-size: 13px; font-weight: 600; color: var(--ink-2); transition: all .16s; white-space: nowrap;
}
.segmented button:hover { color: var(--ink); background: rgba(255,255,255,.7); }
.segmented button.on { background: #fff; color: var(--brand); box-shadow: var(--shadow-sm); }
.segmented.types button.on[data-t="normal"] { color: var(--ok); }
.segmented.types button.on[data-t="prohibited"],
.segmented.types button.on[data-t="no_customer_code"],
.segmented.types button.on[data-t="no_target_warehouse"] { color: var(--danger); }

/* ============================ 按钮 ============================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 16px; border-radius: var(--r-sm); border: 1.5px solid transparent;
  font-weight: 600; font-size: 13.5px; cursor: pointer; transition: all .16s; white-space: nowrap;
  background: #fff; color: var(--ink-2);
}
.btn svg { width: 16px; height: 16px; }
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none !important; }
.btn-primary { background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%); color: #fff; box-shadow: 0 3px 10px rgba(59,91,219,.3); }
.btn-primary:hover { box-shadow: 0 6px 16px rgba(59,91,219,.4); }
.btn-ok { background: linear-gradient(135deg, #0f9d58 0%, #22b573 100%); color: #fff; box-shadow: 0 3px 10px rgba(15,157,88,.3); }
.btn-danger { background: linear-gradient(135deg, #d93a3a 0%, #e5605f 100%); color: #fff; box-shadow: 0 3px 10px rgba(217,58,58,.28); }
.btn-warn { background: linear-gradient(135deg, #d98410 0%, #eda23a 100%); color: #fff; }
.btn-ghost { border-color: var(--line); background: #fff; color: var(--ink-2); }
.btn-ghost:hover { border-color: #c6cfe0; background: #fafbfe; }
.btn-soft { background: var(--brand-soft); color: var(--brand); border-color: transparent; }
.btn-soft:hover { background: #e2e8ff; }
.btn-lg { padding: 13px 24px; font-size: 15px; border-radius: 10px; }
.btn-lg svg { width: 19px; height: 19px; }
.btn-sm { padding: 6px 11px; font-size: 12.5px; }
.btn-xs { padding: 4px 9px; font-size: 12px; border-radius: 6px; }
.btn-block { width: 100%; }
.icon-btn {
  width: 32px; height: 32px; display: grid; place-items: center; border-radius: 9px;
  border: 0; background: transparent; color: inherit; cursor: pointer; transition: background .16s; opacity: .8;
}
.icon-btn:hover { background: rgba(255,255,255,.12); opacity: 1; }
.icon-btn.dark:hover { background: #eef1f7; }
.icon-btn svg { width: 17px; height: 17px; }
.icon-btn.danger:hover { background: var(--danger-soft); color: var(--danger); }

/* ============================ 主框架 ============================ */
.app { display: grid; grid-template-columns: 232px 1fr; height: 100vh; overflow: hidden; }

/* ---------------- 左侧导航「图标栏」模式（2026-10-13） ----------------
   背景：宽表页（运单资料查询 22 列）最缺的就是横向空间。侧栏 232px 里有 170px
   是纯文字，收起后这 170px 直接给表格，等于凭空多显示 1~2 列。
   做法：只切换 .app 上有没有 .rail 这个类，其余样式全部挂子选择器，
   不动任何页面的结构。图标栏下鼠标移到图标上仍能看到名字（title 由 app.js 补）。 */
.app.rail { grid-template-columns: 62px 1fr; }
.app.rail .side-brand { padding: 14px 0; justify-content: center; }
.app.rail .side-brand-text { display: none; }
.app.rail .side-logo { width: 32px; height: 32px; }
.app.rail .nav-group { display: none; }
.app.rail .side-nav { padding: 10px 8px 4px; }
.app.rail .nav-item { justify-content: center; padding: 10px 0; gap: 0; }
.app.rail .nav-item > span { display: none; }
.app.rail .nav-item.on::before { left: -8px; }
.app.rail .conn-status { display: none; }
.app.rail .side-foot { padding: 12px 8px 14px; }
.app.rail .side-user { justify-content: center; }
.app.rail .side-user-info { display: none; }

/* 顶栏里的收起/展开按钮 */
.rail-btn { flex: none; align-self: center; width: 32px; height: 32px; padding: 0; justify-content: center; }
.rail-btn svg { width: 16px; height: 16px; }

.sidebar {
  display: flex; flex-direction: column; background: linear-gradient(180deg, var(--sidebar-1) 0%, var(--sidebar-2) 100%);
  color: #c8d0e3; position: relative; z-index: 5;
  /* ★★ 2026-10-13 第 99 轮根因修复 —— 少了这两行，1080p 下侧栏底部「系统设置」永远看不见：
     .app 是 grid + height:100vh，.sidebar 作为 grid item 的 min-height 默认是 auto，
     于是它**不肯收缩到内容高度以下**（菜单总高 1407 + brand 71 + foot 89 = 1567px），
     直接长到 1567px 溢出 1080px 的行高；.app 又是 overflow:hidden → 底部被硬裁。
     与此同时 .side-nav 的 flex:1 + overflow-y:auto 永远拿不到高度约束 → 从不滚动
     （实测 clientH === scrollH === 1407，与视口高无关，这就是铁证）。
     min-height:0 解开 min-height:auto 的锁，让 .sidebar 老实地被钉在 100vh 内，
     .side-nav 这才真正开始滚动。overflow:hidden 兜住圆角/阴影不溢出。 */
  min-height: 0; overflow: hidden;
}
.side-brand { display: flex; align-items: center; gap: 11px; padding: 14px 16px 12px; border-bottom: 1px solid rgba(255,255,255,.07); }
.side-logo { width: 38px; height: 38px; flex: none; display: grid; place-items: center; }
.side-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.side-brand-text { display: grid; min-width: 0; }
.side-brand-text strong {
  color: #fff; font-size: 12.5px; font-weight: 600; line-height: 1.35; letter-spacing: .2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.side-nav { flex: 1; overflow-y: auto; padding: 8px 8px 4px; }
.side-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); }
/* 2026-10-13 第 99 轮：菜单密度收紧（配合 .sidebar 的 min-height:0 修复）。
   原值 nav-group 37px + nav-item 42px × 27 项 + 7 组 = 1407px，
   1080p 下要滚 487px 才能够到「系统设置」。收紧后：
     nav-group 15→8px 上内边距（37→27px，省 70px）
     nav-item padding 9.5→6.5px、margin-bottom 2→1px（42→35px，省 189px）
   ≈ 1148px，1080p（可视 920）滚动量从 487px 降到约 230px，字号完全不动。
   ★ 别再往下压 —— 35px 已经接近桌面端点击目标的舒适下限。 */
.nav-group { font-size: 10.5px; letter-spacing: 1.2px; color: #5d6b8f; padding: 8px 10px 4px; font-weight: 700; text-transform: uppercase; }
.nav-item {
  display: flex; align-items: center; gap: 11px; padding: 6.5px 12px; border-radius: 9px;
  color: #b6c0d8; cursor: pointer; transition: all .16s; font-size: 13.5px; font-weight: 500; margin-bottom: 1px; position: relative;
}
.nav-item svg { width: 18px; height: 18px; flex: none; opacity: .85; }
.nav-item:hover { background: rgba(255,255,255,.07); color: #fff; }
.nav-item.on { background: linear-gradient(100deg, rgba(91,124,250,.32) 0%, rgba(91,124,250,.10) 100%); color: #fff; font-weight: 600; }
.nav-item.on::before { content: ""; position: absolute; left: -10px; top: 50%; transform: translateY(-50%); width: 3px; height: 20px; border-radius: 0 3px 3px 0; background: var(--brand-2); }

/* 2026-10-08：侧栏数字徽标（异常订单待处理条数）已按用户要求移除，
   连带这三行 CSS 一起删掉，别留死代码。需要时再按同样的规则加回来。 */

.side-foot { padding: 10px 12px 12px; border-top: 1px solid rgba(255,255,255,.07); }
.conn-status { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: #7d8bb0; margin-bottom: 8px; padding-left: 4px; }
.conn-status i { width: 7px; height: 7px; border-radius: 50%; background: #38d39f; box-shadow: 0 0 0 3px rgba(56,211,159,.18); }
.conn-status.off i { background: #e5605f; box-shadow: 0 0 0 3px rgba(229,96,95,.18); }
.side-user { display: flex; align-items: center; gap: 10px; }
.avatar { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; font-weight: 700; color: #fff; flex: none;
  background: linear-gradient(135deg, #5b7cfa, #8b5cf6); font-size: 14px; }
.side-user-info { flex: 1; min-width: 0; display: grid; line-height: 1.3; }
.side-user-info strong { color: #fff; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-user-info span { font-size: 11px; color: #7d8bb0; }
.net-tag { justify-self: start; margin-top: 2px; font-size: 10px !important; color: #ffe0b0 !important;
  background: rgba(239, 159, 39, .28); border: 1px solid rgba(239, 159, 39, .5);
  border-radius: 4px; padding: 0 5px; line-height: 15px; }
#loginNet { margin-bottom: 10px; }

.main {
  display: flex; flex-direction: column; overflow: hidden;
  /* 与 .sidebar 同源：grid item 的 min-height:auto 会让它撑破 100vh 行高。
     这里目前没暴露（.view-host 自带 overflow-y:auto），但属于同一个坑，一并钉死。 */
  min-height: 0;
}
.topbar {
  height: 62px; flex: none; display: flex; align-items: center; justify-content: space-between;
  padding: 0 24px; background: #fff; border-bottom: 1px solid var(--line); gap: 16px;
}
.topbar-title { display: flex; align-items: baseline; gap: 12px; min-width: 0; }
.topbar-title h2 { margin: 0; font-size: 17.5px; font-weight: 700; letter-spacing: .2px; }
.topbar-title span { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar-tools { display: flex; align-items: center; gap: 10px; flex: none; }
.tool-btn {
  display: inline-flex; align-items: center; gap: 6px; padding: 6.5px 12px; border-radius: 9px;
  border: 1.5px solid var(--line); background: #fff; cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--ink-2); transition: all .16s;
}
.tool-btn svg { width: 15px; height: 15px; }
.tool-btn:hover { border-color: #c6cfe0; background: #fafbfe; }
.tool-btn.off { color: var(--muted); }
.tool-btn.on { color: var(--ok); border-color: #b6e5cd; background: var(--ok-soft); }
.clock { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; font-family: var(--mono); }

.view-host { flex: 1; overflow-y: auto; padding: 22px 24px 40px; }
/* 2026-10-13：2000px 以内的屏把左右留白从 24 收到 16 —— 竖向留白不变（不影响观感），
   横向省下的 16px 在宽表页就是「最后一列露不露出来」的差别。 */
@media (max-width: 1999px) { .view-host { padding: 20px 16px 36px; } }

/* ============================ 卡片 / 面板 ============================ */
/* min-width:0 —— 让卡片作为 grid 子项时可被压缩，宽表格才能在卡内横向滚动而不撑破页面 */
.card { background: var(--card); border-radius: var(--r-lg); border: 1px solid var(--line); box-shadow: var(--shadow-sm); min-width: 0; }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 15px 18px; border-bottom: 1px solid var(--line-2); flex-wrap: wrap; }
.card-head h3 { margin: 0; font-size: 14.5px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
.card-head h3 svg { width: 17px; height: 17px; color: var(--brand); }
.card-head .sub { font-size: 12px; color: var(--muted); font-weight: 400; }
.card-body { padding: 18px; }
.card-foot { padding: 13px 18px; border-top: 1px solid var(--line-2); display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }

.grid { display: grid; gap: 18px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1280px) { .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 1000px) { .grid-3, .grid-2 { grid-template-columns: 1fr; } }

/* 统计卡 */
.stat { display: flex; align-items: center; gap: 14px; padding: 17px 18px; background: #fff; border-radius: var(--r-lg); border: 1px solid var(--line); box-shadow: var(--shadow-sm); transition: all .18s; }
.stat:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.stat-ico { width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center; flex: none; }
.stat-ico svg { width: 22px; height: 22px; }
.stat-ico.blue { background: var(--brand-soft); color: var(--brand); }
.stat-ico.green { background: var(--ok-soft); color: var(--ok); }
.stat-ico.amber { background: var(--warn-soft); color: var(--warn); }
.stat-ico.red { background: var(--danger-soft); color: var(--danger); }
.stat-ico.violet { background: #f3edff; color: #7c4dff; }
.stat-body { display: grid; min-width: 0; }
.stat-body b { font-size: 25px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; }
.stat-body span { font-size: 12.5px; color: var(--muted); }

/* ============================ 作业区（上架/下架） ============================ */
.scan-panel { background: linear-gradient(135deg, #ffffff 0%, #f8faff 100%); }
.scan-box { position: relative; }
.scan-box .scanner-dot {
  position: absolute; right: 13px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ok); pointer-events: none;
}
.scan-box .scanner-dot i { width: 7px; height: 7px; border-radius: 50%; background: currentColor; animation: pulse 1.6s infinite; }
@keyframes pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(1.35); } }
.scan-label { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 700; color: var(--ink-2); margin-bottom: 8px; letter-spacing: .2px; }
.scan-label svg { width: 16px; height: 16px; color: var(--brand); }
.scan-label .step-no { width: 19px; height: 19px; border-radius: 50%; background: var(--brand); color: #fff; display: grid; place-items: center; font-size: 11px; font-weight: 700; }

.order-card { border-radius: var(--r); border: 1.5px solid var(--line); background: #fff; padding: 16px; margin-top: 14px; }
.order-card.is-new { border-color: #b9c8ff; background: #fafbff; }
.order-card.is-warn { border-color: #f2d29a; background: #fffdf6; }
.order-card.is-err { border-color: #f5b6b3; background: #fffafa; }
.order-card.is-ok { border-color: #a9dfc3; background: #f7fdfa; }
.order-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.order-trk { font-family: var(--mono); font-size: 21px; font-weight: 700; letter-spacing: .8px; word-break: break-all; }
.order-meta { display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: 13px; color: var(--ink-2); }
.order-meta b { color: var(--ink); }

.result-banner {
  display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-radius: var(--r);
  font-weight: 600; margin-top: 14px; animation: slideIn .28s ease-out; font-size: 14.5px;
}
@keyframes slideIn { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
.result-banner svg { width: 22px; height: 22px; flex: none; }
.result-banner.ok { background: var(--ok-soft); color: #0a7c45; border: 1.5px solid #b6e5cd; }
.result-banner.warn { background: var(--warn-soft); color: #a06008; border: 1.5px solid #f2d29a; }
.result-banner.error { background: var(--danger-soft); color: #b3261e; border: 1.5px solid #f5b6b3; }
.result-banner .rb-msg { flex: 1; min-width: 0; }

.big-status { display: grid; place-items: center; gap: 6px; padding: 26px 12px; border-radius: var(--r); font-weight: 700; animation: popIn .2s; }
.big-status.ok { background: var(--ok-soft); color: var(--ok); }
.big-status.error { background: var(--danger-soft); color: var(--danger); }
.big-status.warn { background: var(--warn-soft); color: var(--warn); }
.big-status svg { width: 44px; height: 44px; stroke-width: 2.4; }
.big-status span { font-size: 14px; }

/* 货物类型选择 */
.type-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 9px; }
@media (max-width: 860px) { .type-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.type-card {
  border: 1.5px solid var(--line); border-radius: 10px; padding: 10px 12px; cursor: pointer; background: #fff;
  transition: all .16s; display: grid; gap: 2px; position: relative; overflow: hidden;
}
.type-card:hover { border-color: #c6cfe0; background: #fafbfe; }
.type-card strong { font-size: 13.5px; font-weight: 600; }
.type-card em { font-size: 11.5px; color: var(--muted); font-style: normal; }
.type-card.on { border-color: var(--brand); background: var(--brand-soft); box-shadow: 0 0 0 3px rgba(59,91,219,.1); }
.type-card.on strong { color: var(--brand); }
.type-card.abn.on { border-color: var(--danger); background: var(--danger-soft); box-shadow: 0 0 0 3px rgba(220,58,58,.1); }
.type-card.abn.on strong { color: var(--danger); }
.type-card .dot { position: absolute; right: 0; top: 0; width: 0; height: 0; border-left: 22px solid transparent; border-top: 22px solid transparent; }
.type-card.abn.on .dot { border-top-color: var(--danger); }

/* ---- 上架第 1 步：当前货架状态条 ---- */
.loc-bar {
  display: flex; align-items: center; gap: 12px; margin-bottom: 16px; padding: 11px 13px;
  border: 1.5px solid #b9c8ff; border-radius: var(--r);
  background: linear-gradient(135deg, #f7f9ff 0%, #eef3ff 100%);
}
.loc-bar > svg { width: 20px; height: 20px; color: var(--brand); flex: none; }
.loc-bar-main { flex: 1; min-width: 0; }
.loc-bar-title { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.loc-bar-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* ---- 上架第 2 步：未选货架时的占位 ---- */
.scan-box.is-locked { opacity: .72; }
.scan-box.is-locked .input-scan { background: #f4f6fa; cursor: not-allowed; }
.locked-hint {
  display: flex; align-items: center; gap: 7px; padding: 11px 13px;
  font-size: 12.5px; color: var(--muted);
  border: 1.5px dashed var(--line); border-radius: var(--r); background: #fafbfe;
}
.locked-hint svg { width: 15px; height: 15px; flex: none; }
.type-hint { margin-top: 8px; font-size: 12px; color: var(--muted); }
.type-hint kbd {
  display: inline-block; min-width: 17px; padding: 1px 5px; margin: 0 1px;
  border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 4px;
  background: #fff; font-family: inherit; font-size: 11px; text-align: center; color: var(--ink-2);
}
#inTypes:focus { outline: none; box-shadow: 0 0 0 3px rgba(59, 91, 219, .14); border-radius: var(--r); }

/* ============================ 表格 ============================ */
.table-wrap { overflow-x: auto; border-radius: var(--r); min-width: 0; }
/* 横向滚动条加粗一点 —— 宽表（运单资料 22 列 / 异常订单 15 列）在窄屏上要靠它，
   原来 10px 太细，用户常常意识不到右边还有列（他们于是去缩放浏览器，字就小了）。 */
.table-wrap::-webkit-scrollbar { height: 12px; }
.table-wrap::-webkit-scrollbar-thumb { background: #b9c2d4; border-radius: 20px; border: 3px solid transparent; background-clip: content-box; }
.table-wrap::-webkit-scrollbar-thumb:hover { background: #8d99b1; background-clip: content-box; }
table.data { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
table.data th {
  background: #f7f9fc; font-weight: 700; font-size: 12.5px; color: var(--ink-2); text-align: left;
  padding: 11px 14px; border-bottom: 1px solid var(--line); white-space: nowrap; position: sticky; top: 0; z-index: 2;
}
table.data td { padding: 10px 14px; border-bottom: 1px solid var(--line-2); vertical-align: middle; }

/* ---------------- 宽表模式的历史沿革（★ 别再改回去） ----------------
 * 2026-10-13 第 98 轮：曾把三张宽表改成「fluid 压缩式自适应」——列宽只给最小宽、
 *   表头允许换行、单元格任意断词、角标拆行、长文本 clip1 单行省略。
 *   目的：让 22 列的运单资料表在 1920 屏上一屏装下，不用缩放浏览器。
 *
 * 2026-10-13 第 100 轮：**用户反馈这个方案「太不协调」** —— 列被压得太窄，
 *   每格都在换行：「中通快…」断两行、尺寸「0.59 / 28.5 / 12.6」竖着堆三行、
 *   「汽车配件」竖排、「GYD-20261010-E」折成 4 行，整张表支离破碎、比原来更难读。
 *   用户明确要求：**恢复原来的舒展列宽，看不全就用横向滚动条**。
 *   → 于是整条 fluid 方案撤回：
 *     · 三张表回到 `class="data"` + 固定列宽（th 的 width）+ 整表 min-width
 *     · 屏幕不够宽时由 .table-wrap 出横向滚动条（见上方 .table-wrap 规则），**字号不变**
 *     · 下面的 table.data.fluid 规则**整组删除**（已无任何表引用，留着就是死代码）
 *
 * ⚠ 教训：**不要为了「塞进屏幕」而压缩列宽** —— 中文表格压到 60px 以下就开始逐字换行，
 *   观感比横向滚动条差得多。宁可让用户拖一下表格，也不要牺牲可读性。
 *   横向滚动条本身早已加粗（12px）并在汇总条文案里提示「可左右滑动查看全部列」。 */
table.data tbody tr { transition: background .12s; }
table.data tbody tr:hover { background: #f8faff; }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data .mono { font-family: var(--mono); font-weight: 600; letter-spacing: .3px; }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
table.data .ctr { text-align: center; }
table.data th.ctr { text-align: center; }
table.data .actions { display: flex; gap: 5px; justify-content: flex-end; }
table.data.compact td { padding: 7px 12px; }
/* 2026-10-08：明细表加了勾选框，勾中的整行给一层淡底色 ——
   只靠方框本身不够醒目，翻页回来也看不出「这几条我勾过」。 */
table.data tbody tr.row-on { background: var(--brand-soft, #dfe4fb); }
table.data tbody tr.row-on:hover { background: #d3dbf8; }
table.data input[type=checkbox] { width: 15px; height: 15px; cursor: pointer; vertical-align: middle; }
.tr-muted { color: var(--muted); }

/* 徽标 */
.badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 700; white-space: nowrap; line-height: 1.6; }
.badge i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge.ok { background: var(--ok-soft); color: #0a7c45; }
.badge.err { background: var(--danger-soft); color: #b3261e; }
.badge.warn { background: var(--warn-soft); color: #a06008; }
.badge.info { background: var(--brand-soft); color: var(--brand); }
.badge.gray { background: #eef1f6; color: #5d6577; }
.badge.violet { background: #f3edff; color: #6d3ddb; }

/* 空状态 */
.empty { display: grid; place-items: center; gap: 8px; padding: 46px 20px; color: var(--muted); text-align: center; }
.empty svg { width: 44px; height: 44px; opacity: .35; stroke-width: 1.5; }
.empty b { font-size: 14px; color: var(--ink-2); }
.empty span { font-size: 12.5px; }

/* 分页 */
.pager { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pager button { min-width: 30px; height: 30px; padding: 0 8px; border: 1.5px solid var(--line); background: #fff; border-radius: 7px; cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.pager button:hover:not(:disabled) { border-color: #c6cfe0; background: #fafbfe; }
.pager button.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.pager button:disabled { opacity: .45; cursor: not-allowed; }
.pager .info { font-size: 12.5px; color: var(--muted); margin-right: 6px; }
.pager .info b { color: var(--ink-2); }
/* 「每页显示 [N] 条记录」：整条靠右对齐，跟页码按钮拉开距离 */
.pager .pager-size { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; margin-right: 0; }
.pager .pager-size-sel { height: 30px; padding: 0 6px; border: 1.5px solid var(--line); border-radius: 7px; background: #fff; font-size: 12.5px; font-weight: 600; color: var(--ink-2); cursor: pointer; }
.pager .pager-size-sel:hover { border-color: #c6cfe0; }
.pager .pager-size-sel:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(43,70,184,.12); }

/* 筛选条 */
.filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.filters .field { min-width: 140px; flex: 0 1 auto; }
.filters .field.grow { flex: 1 1 220px; }

/* ---------------- 运单资料查询页：压缩查询区高度，把空间让给明细表 ----------------
   明细表列多、行高有限，查询区占一行就少看一行数据，所以这里整体收一收：
   ①「单号互查」卡片：标签高度压到原来的 2/3（字号保持不变，只是更紧凑）
   ②「关键词 + 批量单号」并到同一行：批量单号从整行 textarea 改成单行输入
   —— 两处合计能省出约 2 行高度。 */
.wq-one-card > .card-body { padding: 13px 18px; }
/* 「单号互查」标题后的提示胶囊：这个框一次只查一个单号，不做批量
   （要批量核对多个单号走下面的「批量单号」框）。
   做成小胶囊、跟在标题右边（h3 本身是 flex，gap 8px 自带间距）。
   字号比标题小一档，行高与上下内边距压到最小 —— 它的总高（11.5 + 3×2 ≈ 17.5px）
   小于标题本身的行高（14.5px 字号 → 约 23px），所以卡片头高度一点不涨，
   不会把这之前专门压缩过的「单号互查」卡片又撑回去。 */
.wq-one-hint {
  font-size: 11.5px; font-weight: 600; line-height: 1;
  padding: 3px 8px; border-radius: 999px;
  background: var(--brand-soft); color: var(--brand);
}
.wq-one-row { display: flex; gap: 10px; align-items: center; }
.wq-one-row .wq-scan-flat {
  flex: 1; min-width: 240px;
  /* 高度压到约 2/3：原扫描框 12px 上下内边距 + 17px 字号 ≈ 47px，这里收到 ≈ 32px。
     字号仍保持 17px（扫描枪 / 大字号可读性不变），只收内边距与行高。 */
  font-family: var(--mono); font-size: 17px; letter-spacing: .6px;
  padding: 5px 12px; line-height: 1.35;
}
.wq-one-row .btn { flex: none; height: 32px; padding: 0 16px; }

/* 关键词与批量单号同一行：批量单号用单行 textarea，超高时内部滚动，不撑高整行。
   按截图标注，两个标签都挪到各自输入框【前方】（同行左侧），不再压在框上面。 */
/* 间距按「同组挨紧、组间拉开」来排：
     - 标签 ↔ 它自己的输入框：10px（本来就是一对，挨着才看得出归属）
     - 组 ↔ 组（关键词组 / 批量单号组 / 按钮组）：16px
   以前一律 8~10px，标签、输入框、按钮全等距排成一排，彼此挨得太紧、分不清哪两个是一组。 */
.filters.wq-kw-row { align-items: center; gap: 16px; }
/* 行内标签：标签 + 输入框横向并排，垂直居中对齐 */
.filters.wq-kw-row .wq-inline { display: flex; align-items: center; gap: 10px; min-width: 0; }
.filters.wq-kw-row .wq-inline > span {
  flex: none; white-space: nowrap;
  font-size: 14px; font-weight: 600; color: var(--ink-2);
}
/* 按钮组：组内 8px，作为一个整体跟前面的输入框保持 16px 距离 */
.wq-kw-btns { display: flex; gap: 8px; flex: none; }
/* 筛选条与关键词行之间给一点垂直呼吸，原来两行是贴着的（间距 0）。
   注意别为了压高度把这个距离也收掉 —— 收掉两行又会糊在一起。 */
.wq-filters + .wq-kw-row { margin-top: 14px; }

/* 筛选卡片整体：删掉底部那行「到仓时间」提示后，上下内边距再收一档（18 → 14），
   省下的高度全留给下面的运单明细表。横向仍保持 18px，跟卡片左右对齐一致。 */
.wq-filter-card { padding: 14px 18px; }

/* ---------------- 筛选条（到仓时间 / 海关申报 / … / 排序）与下方关键词行对齐 ----------------
   截图要求：这一整行「跟下面的关键词 / 批量单号一样调整，看上去要对称」。
   下方那行的基准是：标签 12px、输入框高 40px、行距 10px。
   这里把筛选条的每个控件也统一到同一基准，上下两行的框高与标签字号完全一致，
   视觉上就是一条整齐的网格，而不是上面胖一圈、下面瘦一圈。 */
.filters.wq-filters { gap: 10px; }
.filters.wq-filters .field { min-width: 132px; }
.filters.wq-filters .field > span { font-size: 12px; }
/* 统一所有输入控件的高度：用 height + box-sizing 锁死，不靠 padding 撑，
   避免 select / input / datetime-local 三种控件因为内建内边距不同而各高各的 */
.filters.wq-filters .input,
.filters.wq-filters .select {
  height: 40px; padding: 0 12px; box-sizing: border-box; font-size: 13px;
}
/* datetime-local 会比普通输入框略宽（日期 + 时间 + 秒），给它单独的合理宽度 */
.filters.wq-filters .wq-date { min-width: 178px; }
.filters.wq-filters .wq-date-to { min-width: 132px; }

/* 关键词 + 批量单号：这一行的「标签」和「框里录入的内容」统一放大到 14px。
   历史沿革：关键词框曾压到 8px（6pt）、批量单号 11px —— 那是为了给明细表省高度，
   结果小到没法看清录入内容、核对单号时很吃力，所以两级放大到 14px：
   8px/11px → 12px → 14px。整体略大于页面其它输入框（13px），因为这两个框
   是拿来「粘贴 + 对着结果核对」的，可读性优先。

   高度一律保持 40px 不变：这一行和上方筛选条的高度对齐（用户明确要求「看上去要对称」），
   而且框高不变 = 明细表的起始位置不变，不会因为放大字号而少看一行数据。

   批量单号的框高固定 40px，要让 14px 的字还能露出「整 2 行」（不出现半行被裁的难看状态），
   得把行高写成整数 16px、内边距收到 3px：
   可用高 40 − 上下边框 2 = 38px，再减上下内边距 6px = 32px 内容区 = 16px × 2 行，正好两行。
   （若沿用 line-height:1.2 = 16.8px/行，2 行需 33.6px 会溢出 1.6px、冒滚动条并裁掉半行。） */
.filters.wq-kw-row .wq-inline:first-child { flex: none; }
#wqKw {
  width: 296px; flex: none;
  font-size: 14px; line-height: 1.4;
  padding: 6px 10px; height: 40px;
}

/* 批量单号：标签长（写明「可选，多个用换行 / 空格 / 逗号分隔」）、内容也长，
   占住这一行的剩余宽度。 */
.filters.wq-kw-row .wq-inline-nos { flex: 1 1 300px; }
.wq-nos-inline {
  flex: 1; min-width: 160px;
  font-family: var(--mono); font-size: 14px; line-height: 16px;
  padding: 3px 12px; min-height: 40px; max-height: 40px;
  resize: none; overflow-y: auto; white-space: pre-wrap; word-break: break-all;
}
.filters.wq-kw-row .btn { height: 40px; }

/* 标签 / 提示条 */
.note { display: flex; gap: 9px; align-items: flex-start; padding: 11px 14px; border-radius: var(--r-sm); font-size: 12.5px; line-height: 1.6; }
.note svg { width: 16px; height: 16px; flex: none; margin-top: 1px; }
.note.info { background: var(--brand-soft); color: #2b46b8; }
.note.warn { background: var(--warn-soft); color: #8a5406; }
.note.ok { background: var(--ok-soft); color: #0a7c45; }
.note.danger { background: var(--danger-soft); color: #b3261e; border: 1px solid #f5b6b3; }

/* 粘贴单号查询后「没查到的单号」标签（运单资料查询页） */
.wqMissNo {
  display: inline-block; padding: 2px 7px; border-radius: 4px;
  background: #fff; border: 1px solid #f0c0be; color: #b3261e;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 12px; white-space: nowrap;
}

/* 危险操作卡片（系统设置 → 安全与数据 → 清空业务资料） */
.card.danger-card { border: 1.5px solid #f0c0be; }
.card.danger-card > .card-head { background: var(--danger-soft); border-radius: var(--r) var(--r) 0 0; }
.card.danger-card > .card-head h3 { color: #b3261e; }
.card.danger-card > .card-head h3 svg { color: #b3261e; }
.danger-text { color: #b3261e; }
.check > span { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 6px; line-height: 1.6; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.stat-chip { display: inline-flex; align-items: baseline; gap: 6px; padding: 6px 12px; background: #f5f7fb; border-radius: 9px; font-size: 12.5px; }
.stat-chip b { font-size: 15px; font-variant-numeric: tabular-nums; }

/* 统计大数字卡（发货资料「总票数 / 总重量」等） */
.stat-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
/* 4 个一行的变体（截单出货统计的箱级口径那一排：整箱实重 / 体积重 / 计费重 / 方数）。
   ⚠ 窄屏断点必须跟着写，否则内联式覆盖会把 4 列一直保持到手机上。 */
.stat-grid.cols4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1280px) { .stat-grid.cols4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 780px) { .stat-grid { grid-template-columns: 1fr; } .stat-grid.cols4 { grid-template-columns: 1fr; } }
.stat-box { display: grid; gap: 3px; padding: 15px 17px; border-radius: var(--r-lg); background: #f6f8fc; border: 1px solid var(--line); min-width: 0; }
.stat-box small { font-size: 12.5px; color: var(--muted); }
.stat-box b { font-size: 27px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; color: var(--ink, #101728); }
.stat-box b i { font-size: 14px; font-weight: 600; font-style: normal; margin-left: 3px; color: var(--muted); }
.stat-box span { font-size: 12px; color: var(--muted); }
.stat-box.brand { background: var(--brand-soft); border-color: transparent; }
.stat-box.brand b { color: #2b46b8; }
.stat-box.ok { background: var(--ok-soft); border-color: transparent; }
.stat-box.ok b { color: #0a7c45; }
/* 2026-10-09 计费账单页：账单合计用橙色底突出（与 brand 蓝 / ok 绿 区分开）。
   只加一个新变体，不动 .stat-box 本体 —— 别的地方照旧。 */
.stat-box.money { background: var(--warn-soft); border-color: transparent; }
.stat-box.money b { color: #a06008; }

/* ============================ 弹窗 ============================ */
.modal-host { position: fixed; inset: 0; z-index: 70; display: grid; place-items: center; background: rgba(16,23,40,.5); backdrop-filter: blur(3px); animation: fadeIn .18s; padding: 20px; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal { background: #fff; border-radius: var(--r-lg); box-shadow: var(--shadow-lg); width: 520px; max-width: 100%; max-height: calc(100vh - 40px); display: flex; flex-direction: column; animation: popIn .25s cubic-bezier(.2,.9,.3,1.15); }
.modal.wide { width: 780px; }
.modal.xwide { width: 1000px; }
/* 箱级明细（15 列）要横向放得下，才需要比 xwide 更宽的一档 */
.modal.xxwide { width: min(1320px, 100%); }
.modal-head { padding: 17px 20px; border-bottom: 1px solid var(--line-2); display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.modal-head h3 { margin: 0; font-size: 16px; font-weight: 700; }
.modal-body { padding: 20px; overflow-y: auto; display: grid; gap: 15px; }
.modal-foot { padding: 14px 20px; border-top: 1px solid var(--line-2); display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }

/* ============================ Toast ============================ */
.toast-host { position: fixed; top: 18px; right: 18px; z-index: 90; display: grid; gap: 10px; justify-items: end; pointer-events: none; }
.toast {
  display: flex; align-items: center; gap: 11px; padding: 12px 17px; border-radius: var(--r); background: #fff;
  box-shadow: var(--shadow-lg); border-left: 4px solid var(--brand); font-size: 13.5px; font-weight: 600;
  min-width: 250px; max-width: 420px; animation: toastIn .3s cubic-bezier(.2,.9,.3,1.2); pointer-events: auto;
}
@keyframes toastIn { from { opacity: 0; transform: translateX(30px) scale(.96); } to { opacity: 1; transform: none; } }
.toast.out { animation: toastOut .25s forwards; }
@keyframes toastOut { to { opacity: 0; transform: translateX(30px) scale(.96); } }
.toast svg { width: 18px; height: 18px; flex: none; }
.toast.ok { border-left-color: var(--ok); color: #0a7c45; }
.toast.error { border-left-color: var(--danger); color: #b3261e; }
.toast.warn { border-left-color: var(--warn); color: #a06008; }
.toast.info { border-left-color: var(--brand); color: var(--brand-dark); }
.toast .tmsg { flex: 1; min-width: 0; }

/* ============================ 库位标签预览 ============================ */
.label-preview-grid { display: flex; flex-wrap: wrap; gap: 14px; }
/* 2026-10-09 用户要求：库位号与条码上下隔得太开 → gap 3px → 1px（与打印版的 0.2mm 同量级）。
   ⚠ 只改这一处（.label-pv 是本页预览专用类），不动全站别的 flex 间距。 */
.label-pv {
  border: 1px solid #d8dee9; border-radius: 6px; background: #fff;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; overflow: hidden; position: relative; padding: 4px;
}
.label-pv .lp-title { font-size: 9px; color: #6b7280; letter-spacing: 1px; line-height: 1; flex: none; }
.label-pv .lp-code { font-weight: 800; color: #0d1220; letter-spacing: .5px; line-height: 1.1; max-width: 96%; overflow: hidden; white-space: nowrap; flex: none; }
.label-pv .lp-bc { flex: none; display: block; line-height: 0; }
.label-pv .lp-bc svg { width: 100%; height: 100%; display: block; }
.label-pv .lp-under { font-size: 10px; font-weight: 600; color: #1f2937; line-height: 1; flex: none; }
.label-pv .lp-foot { font-size: 8px; color: #9aa3b2; line-height: 1; flex: none; }

/* 库位网格 */
.loc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; max-height: 420px; overflow-y: auto; padding: 3px; }
.loc-item { border: 1.5px solid var(--line); border-radius: 10px; padding: 10px 12px; cursor: pointer; transition: all .15s; background: #fff; position: relative; }
.loc-item:hover { border-color: #c6cfe0; background: #fafbfe; }
.loc-item.on { border-color: var(--brand); background: var(--brand-soft); }
.loc-item.on::after { content: "✓"; position: absolute; right: 8px; top: 6px; color: var(--brand); font-weight: 700; }
.loc-item .lc { font-family: var(--mono); font-weight: 700; font-size: 13.5px; letter-spacing: .4px; }
.loc-item .lm { font-size: 11.5px; color: var(--muted); display: flex; justify-content: space-between; gap: 6px; margin-top: 3px; }
.loc-item.disabled { opacity: .55; background: #f7f8fb; }

.bar-row { display: grid; grid-template-columns: 96px 1fr 46px; align-items: center; gap: 10px; font-size: 12.5px; margin-bottom: 9px; }
.bar-track { height: 9px; border-radius: 999px; background: #eef1f6; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--brand) 0%, var(--brand-2) 100%); transition: width .5s; }
.bar-fill.red { background: linear-gradient(90deg, #e5605f, #dc3a3a); }
.bar-fill.green { background: linear-gradient(90deg, #34c07a, #0f9d58); }
.bar-fill.amber { background: linear-gradient(90deg, #eda23a, #d98410); }
.bar-row .val { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }

.trend { display: flex; align-items: flex-end; gap: 10px; height: 160px; padding: 8px 0; }
.trend-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }
.trend-bars { display: flex; gap: 3px; align-items: flex-end; height: 100%; width: 100%; justify-content: center; }
.trend-bars i { width: 11px; border-radius: 4px 4px 0 0; display: block; min-height: 2px; transition: height .5s; }
.trend-bars i.in { background: linear-gradient(180deg, #5b7cfa, #3b5bdb); }
.trend-bars i.out { background: linear-gradient(180deg, #34c07a, #0f9d58); }
.trend-col small { font-size: 10.5px; color: var(--muted); white-space: nowrap; }

/* 工作台首页下面这两排 ——（近 7 天收发趋势 / 在库货物类型分布）与（库位存放量 TOP 8 / 最近操作动态）
 * ★ 四张卡片统一等高（2026-10-09 用户要求：后一排跟前一排一样高）。
 *   高度取「前一排的自然高度」= 卡片头 53.5 + 上下内边距 36 + 趋势图 160 + 上下边框 2 ≈ 251.5 → 取 252px。
 *   高度锁死后，内容超出（如「最近操作动态」最多 12 条日志、8 个库位）改为在卡片内部滚动，
 *   不再把整排撑高、也不在 TOP 8 卡片底下留一大片空白。
 * ⚠ 只在宽屏（≥1001px）锁高：窄屏 .grid-2 会变成单列纵向堆叠，那时不该再强制定高。
 * ⚠ 只作用于挂了 .dash-row 的容器（工作台这两排），不动全站 .grid-2 / .card。 */
.dash-row { --dash-card-h: 252px; }
.dash-row > .card { display: flex; flex-direction: column; min-height: 0; }
.dash-row > .card > .card-head { flex: none; }
.dash-row > .card > .card-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
@media (min-width: 1001px) {
  .dash-row { grid-auto-rows: var(--dash-card-h); }
  /* 空态提示（「暂无…」）在锁高卡片里改为撑满可用高度、自身上下内边距归零 ——
   * 否则 .empty 那 46px 的大内边距会把内容顶出可视区、平白多出一条滚动条（内容其实看得见，只是难看）。
   * ⚠ 放在这条媒体查询里：窄屏不锁高时保持原来的空态高度，不受影响。 */
  .dash-row > .card > .card-body > .empty { height: 100%; padding-top: 0; padding-bottom: 0; }
}

.legend { display: flex; gap: 16px; font-size: 12px; color: var(--muted); flex-wrap: wrap; }
.legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; margin-right: 5px; }

.kv { display: grid; grid-template-columns: 92px 1fr; gap: 8px 14px; font-size: 13px; align-items: baseline; }
.kv dt { color: var(--muted); font-size: 12.5px; }
.kv dd { margin: 0; word-break: break-all; }

.spin { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================ 自适应 ============================ */
.work-layout { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 1180px) {
  .work-layout { grid-template-columns: 1fr; }
}

/* 「扫描上架 / 扫描下架」两页的四张卡：**等宽 + 同一排两张等高**（2026-10-09 用户要求）。
 * 旧版是「左右两列各一个容器、列内上下堆叠」，两列各排各的、上下对不齐；现在四张卡直接放进
 * 同一个 2 列 grid，DOM 顺序按「左→右、上→下」排，同一排的两张靠 align-items: stretch 自动等高。
 * grid-template-rows 给第 1 / 第 2 排一个下限（357px / 273px = 「扫描上架」两排的自然高度）：
 * 这样「扫描下架」内容少（最高 255px）也能撑到跟「扫描上架」一样大 —— 用户要求两页卡片大小一致。
 * ⚠ 是 minmax(下限, auto) 不是死高度：上架作业台选了货架后会长到 ~632px，那一排跟着长，另一排不动
 *   （用户明确选的就是「等宽 + 同排等高」，不要「四张死等高」）。
 * ⚠ 只作用于挂了 .work-scan 的这两页 —— .work-layout（库位标签打印页）不受影响。 */
.work-scan {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: minmax(357px, auto) minmax(273px, auto);
  gap: 36px 18px;
  align-items: stretch;
}
/* 空态提示（「本次还没有上架 / 下架记录」）在卡片里居中，别缩在顶上 */
.work-scan > .card > .card-body > .empty { min-height: 100%; }
@media (max-width: 1180px) {
  .work-scan { grid-template-columns: 1fr; grid-template-rows: none; }
}
@media (max-width: 980px) {
  .app { grid-template-columns: 70px 1fr; }
  .side-brand { justify-content: center; padding: 16px 8px; }
  .side-brand-text, .side-user-info, .side-user .icon-btn { display: none; }
  .nav-group { display: none; }
  .nav-item { justify-content: center; padding: 11px 0; }
  .nav-item span { display: none; }
  .nav-item.on::before { display: none; }
  .side-user { justify-content: center; }
  .view-host { padding: 16px 14px 40px; }
  .topbar { padding: 0 14px; }
  .topbar-title span { display: none; }
  .clock { display: none; }
}

/* ============================ 运单资料 ============================ */
.drop-zone {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  padding: 30px 20px; border: 2px dashed #cfd6e4; border-radius: var(--r); cursor: pointer;
  background: #fafbfe; color: var(--ink-2); text-align: center; transition: all .16s;
}
.drop-zone:hover, .drop-zone.on { border-color: var(--brand); background: var(--brand-soft); }
.drop-zone > svg { width: 30px; height: 30px; color: var(--brand); }
.drop-zone b { font-size: 14px; color: var(--ink); }
.drop-zone span { font-size: 12.5px; color: var(--muted); }

.map-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 1180px) { .map-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 860px) { .map-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.map-grid .field span { font-size: 12px; }

.pair-box { display: grid; grid-template-columns: 1fr auto 1fr; gap: 12px; align-items: stretch; }
.pair-side {
  padding: 12px 14px; border: 1.5px solid var(--line); border-radius: var(--r-sm);
  background: #fafbfe; display: flex; flex-direction: column; gap: 6px; min-width: 0;
}
.pair-side.on { border-color: var(--brand); background: var(--brand-soft); }
.pair-side small { font-size: 12px; color: var(--ink-2); line-height: 1.5; }
.pair-side b { font-size: 16px; word-break: break-all; }
.pair-link { display: grid; place-items: center; color: var(--brand); }
.pair-link svg { width: 22px; height: 22px; }
@media (max-width: 720px) {
  .pair-box { grid-template-columns: 1fr; }
  .pair-link { transform: rotate(90deg); }
}

/* ---------- 海关申报标记：可选的两张标记卡片 ---------- */
.customs-pick { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.customs-opt {
  position: relative; overflow: hidden; cursor: pointer; user-select: none;
  display: grid; gap: 5px; padding: 14px 16px 14px 20px;
  border: 1.5px solid var(--line); border-radius: var(--r-sm);
  background: #fafbfe; transition: border-color .14s, background .14s;
}
.customs-opt:hover { border-color: #ccd4e3; }
.customs-opt > b { display: flex; align-items: center; gap: 8px; font-size: 15px; }
.customs-opt > b svg { width: 17px; height: 17px; }
.customs-opt > span { font-size: 12.5px; color: var(--ink-2); line-height: 1.5; }
.customs-opt input { position: absolute; opacity: 0; pointer-events: none; }
.customs-opt::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: transparent; transition: background .14s;
}
/* 海关申报标记：申报通过 = 绿色图标，已提交申报 = 橙色图标 */
.customs-opt.ok.on, .customs-opt.green.on { border-color: #0f9d58; background: #e7f7ee; }
.customs-opt.ok.on > b, .customs-opt.green.on > b { color: #0a7c45; }
.customs-opt.ok.on > b svg, .customs-opt.green.on > b svg { color: #0f9d58; }
.customs-opt.ok.on::before, .customs-opt.green.on::before { background: #0f9d58; }

.customs-opt.warn.on, .customs-opt.orange.on { border-color: #e07b00; background: #fdf1e0; }
.customs-opt.warn.on > b, .customs-opt.orange.on > b { color: #b35c00; }
.customs-opt.warn.on > b svg, .customs-opt.orange.on > b svg { color: #e07b00; }
.customs-opt.warn.on::before, .customs-opt.orange.on::before { background: #e07b00; }

/* 橙色角标（与 .badge.ok / .badge.err 同族） */
.badge.orange { background: #fdf1e0; color: #b35c00; }


/* 入库订单查询页：把上方「当日入库汇总」卡压紧，给下方明细表腾出可视高度
 * （明细才是这页高频使用的部分）。
 * 2026-10-07 用户要求删掉原右侧「单号即时查询」卡、汇总卡向右拉满整行，
 * 于是原来的 .scan-top 两列分栏不再需要，改为单卡紧凑样式。 */
.daily-card .card-head { padding: 7px 14px; }
.daily-card .card-head h3 { font-size: 14px; margin: 0; }
.daily-card .card-body { padding: 8px 14px 10px; }
.daily-card .btn { padding: 4px 10px; font-size: 12.5px; }
/* 图高在 views5.js 里压到 60px；类型汇总做成紧凑 chips 一行 */
.sub-tight { font-size: 11.5px; margin-bottom: 2px !important; }
.chips-tight { gap: 5px; }
.chips-tight .stat-chip { padding: 2px 8px; font-size: 12px; border-radius: 7px; }
.chips-tight .stat-chip b { font-size: 13px; }
.chip-label { font-size: 11.5px; color: var(--muted, #888); align-self: center; margin-right: 2px; }
.input-sm { padding: 4px 8px; font-size: 13px; }
/* 2026-10-09（第 64 轮）用户要求：当日入库汇总下面那排胶囊（日期 + 到仓票数 / 总实重 /
   总体积重 / 平均实重 / 单票最重 / 已挂运单 / 待归属，共 7~8 颗）**间距再扩大一倍**。
   它们挂的是 .chips-tight（gap 5px），这里用 id 选择器盖住（id 优先级高于类），
   只作用于本页这一排 —— 不动全站 .chips / .chips-tight。
   ⚠ 卡片体那个 gap:8px 是「胶囊行 ↔ 下面图表」的**纵向**间距，不是这个，别一起改。 */
#isDChips { min-height: 0; gap: 10px; }
/* 明细卡的筛选行（2026-10-07 用户要求：单号录入框不再单独占一行，与「到仓时间从 / 到」同一行）。
 * 从左到右：到仓时间从 → 到 → 归属状态 → 排序 → 单号录入框（吃剩余宽度）→ 查询 → 清空。
 * 录入框用 flex:1 抢空间；min-width 兜底，窄屏时整行 wrap 换行，不会把录入框挤到 0 宽。 */
.filter-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.filter-row .field { margin-bottom: 0; }
.filter-row .field-lookup { flex: 1; min-width: 240px; }
/* 筛选项标签：统一 12px，且**不许换行**。
 * 2026-10-08 用户要求：「国内运单号录入」这个标签一换行就变两行，
 * 整行高度被顶高，录入框看起来就比左边的日期框高一截。
 * 定死 nowrap + 12px 后，这一行的所有标签都只占一行，高度基准才拉得齐。 */
.filter-row .field { min-width: 0; }
.filter-row .field > span { font-size: 12px; white-space: nowrap; }

/* 2026-10-08 用户要求：录入框高度必须与左侧「到仓时间 / 归属状态 / 排序」完全一致。
 * 原来这个 textarea 带一堆内联样式（padding:7px 10px、line-height:1.45、min-height:0），
 * 而左边是 .input 的 padding:10px 12px —— 两者各算各的，实测差出一截。
 * 这里用「height + box-sizing」把两边都锁到同一把尺子上，不靠 padding 撑：
 *   40px 行高 · 12px 内边距 · 13px 字号 · 单行 18px 行距（18 + 上下 padding 就整齐）。
 * ⚠ 不能给 min-height —— autoGrow 是靠改 height 长高的，给了 min-height 会顶住不长。
 * 内容超过一行时由 autoGrow 把 height 改大，整行跟着变高，仍与左侧底部对齐。 */
.filter-row .input,
.filter-row .select { height: 40px; padding: 0 12px; box-sizing: border-box; font-size: 13px; }
.filter-row .field-lookup textarea {
  display: block; width: 100%;
  height: 40px; min-height: 0; box-sizing: border-box;
  padding: 10px 12px; font-size: 13px; line-height: 18px;
  resize: none; overflow: hidden;
}
/* 单号录入框比其他筛选项宽（要粘一列单号），但仍与它们同高 */
.filter-row .field-lookup { min-width: 260px; }
/* 查询 / 清空两个按钮与左侧控件同高，整行底边齐平 */
.filter-row > .btn { height: 40px; box-sizing: border-box; padding-top: 0; padding-bottom: 0; }

/* ---------------- 异常订单维护页：三张卡片统一收高（2026-10-08 用户要求各缩 1/3） ----------------
   用户要求：异常订单概况 / 筛选 / 处理（勾选后可批量）三张卡片高度各缩减约 1/3，
   目的是把省下来的高度全让给下面的「异常订单列表」。
   收的地方（卡片头 + 卡片体各收一档，合起来约 1/3）：
     card-head  padding 15px 18px → 8px 14px  （并去掉标题下那条分隔线，头部不再像独立一栏）
     card-body  padding 18px      → 10px 14px
     h3 字号    14.5px            → 13.5px
     标签字号   12px              → 11.5px，标签与输入框的间距也收 2px
   注意 .ab-compact 只挂在这一页的卡片上，**不动全站 .card 的默认值** ——
   否则工作台 / 运单查询那些页的卡片会跟着一起瘦，影响面太大。 */
.ab-compact > .card-head { padding: 8px 14px; border-bottom: 0; }
.ab-compact > .card-head h3 { font-size: 13.5px; }
.ab-compact > .card-head h3 svg { width: 15px; height: 15px; }
.ab-compact > .card-head .sub { font-size: 11.5px; }
/* 2026-10-09 用户要求：卡片头里只有「标题 + 说明」两项时，说明要**紧跟在标题后面**（默认会被
   space-between 顶到整行最右边，离标题隔着大半张卡，读起来不像同一句话），字号再降一档到 10.5px。
   ⚠ 只给挂了 .ab-head-inline 的那张卡头用 —— 默认那张卡头右边可能还要放别的东西，不能一起挪。
   2026-10-09 第 55 轮：批量处理删掉后，两张卡头都挂 .ab-head-inline（另一张原本的
   「已勾选 N 条」计数跟着批量功能一起没了），说明文字变长，所以补 flex 收缩：
   flex:1 1 auto + min-width:0 让长说明在窄屏能换行而不是把标题后面的悬停提示「i」挤出卡片。 */
.ab-compact > .card-head.ab-head-inline { justify-content: flex-start; gap: 12px; }
.ab-compact > .card-head.ab-head-inline .sub { font-size: 10.5px; flex: 1 1 auto; min-width: 0; }
.ab-compact > .card-body { padding: 10px 14px; }
.ab-compact .filter-row { gap: 8px; }
.ab-compact .field { gap: 4px; }
.ab-compact .field > span { font-size: 11.5px; }
/* 概况卡的统计胶囊也收一档：这几颗胶囊是概况卡体的唯一内容，
   它们的总高就是卡片体的高，所以胶囊本身必须一起收，否则光收内边距看不出效果。 */
.ab-compact .chips { gap: 6px; }
.ab-compact .stat-chip { padding: 4px 10px; font-size: 12px; }
.ab-compact .stat-chip b { font-size: 13.5px; }
/* 处理卡的说明文字：用户明确要求「把提示内容的字体减小」。
   五行提示原文是 12.5px / 行距 1.6，是整张处理卡最大的一块高；
   这里降到 11.5px、行距 1.5、内边距 11px14px → 8px11px，图标同步缩到 14px。
   提示是操作说明不是数据，压小不影响读数，但能省下约 1/4 的高度。 */
.ab-compact .note { font-size: 11.5px; line-height: 1.5; padding: 8px 11px; gap: 7px; }
.ab-compact .note svg { width: 14px; height: 14px; }
.ab-compact .note b { font-size: 11.5px; }
/* 「筛选行 + 说明条」的间距：views6.js 里内联写的就是 margin-top:8px，这里不再用
 * !important 去覆盖它 —— 2026-10-08 审核时删掉了原来那条 .note[style*="margin-top"] 规则：
 * 内联值改过之后它已经不起作用，留着只会让人以后改了内联却发现不生效、白找半天。 */

/* ---------------- 异常订单维护页：概况胶囊与三个按钮同一行（2026-10-09 用户要求） ----------------
   用户要求：胶囊那一行（…待客户回复 / 已处理 / 已转正常 / 按类型…无目标仓库）后面，
   依次跟上「重新同步」「刷新」，「下载模板」放到这一行的最右边。
   做法：
     · 三个按钮放进 #abSummary **所在的同一个 flex 行**（.ab-sum-row），不在卡片头里；
     · .ab-summary 用 display:contents —— 让里面的胶囊**直接成为这一行的 flex 子项**，
       否则整块胶囊是一个子项，「重新同步」只能排在整块后面、跟最后一颗「无目标仓库」
       中间隔着卡片体的内边距，看着不像同一行；
     · .ab-sum-spacer 吃掉剩余宽度，把「下载模板」顶到最右边。
   ⚠ 只在本页用，不改全站 .chips / .card-head。 */
.ab-sum-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ab-sum-row > .ab-summary { display: contents; }
.ab-sum-spacer { flex: 1 1 12px; min-width: 12px; }
/* 概况拉取失败时会渲染一条提示条，它得独占一行，不能跟胶囊挤在一起把按钮顶飞 */
.ab-sum-row > .note { flex: 1 1 100%; }

/* ---------------- 异常订单维护页：「处理（勾选后可批量）」的悬停提示（2026-10-09 用户要求） ----------------
   用户要求：原来那三段说明（自动转正常 / 违禁品不自动转 / 缺项不给转）
   挪到「处理（勾选后可批量）」这几个字后面，**默认隐藏，鼠标移过去才显示**。
   目的很直接：这三段话占着一整块高度，而这一页最值钱的空间是最下面的列表。
   做法：挂在字后面的一个小「i」，说明装进 .ab-tip-box，平时 opacity:0 + visibility:hidden
   （visibility 一起切，否则看不见的浮层仍会吃掉鼠标事件），hover 时才浮出来。
   注意：
     · 只在本页用（类名 ab-tip），**不动全站任何通用提示样式**；
     · 浮层给 z-index:60 并限制最大宽度，避免被下面列表卡的卡片背景盖住或撑破布局；
     · 触发点也支持键盘 focus（tabindex="0"），不看鼠标的人也能唤出。 */
.ab-tip { position: relative; display: inline-flex; align-items: center; cursor: help;
  color: var(--muted); font-weight: 400; }
.ab-tip > svg { width: 14px; height: 14px; display: block; }
/* ⚠ 2026-10-13 实测修掉一个「幽灵横向滚动条」：
   原来隐藏态用的是 opacity:0 + visibility:hidden —— 元素仍然参与布局，
   一个 520px 宽、绝对定位的框会把 #viewHost 的 scrollWidth 撑宽 467px。
   结果是本页在任何分辨率下都多出一条**滚不到任何内容的**横向滚动条，
   而且它还把整页的「需要宽度」抬到 2600px 以上，用户只能靠缩放浏览器来回避。
   改法：隐藏态直接 display:none（彻底不参与布局）；显隐动画改用 animation。
   同时把锚点从 left:0 换成 right:0 —— 这个「i」在卡片头里是被 .sub 顶到最右边的，
   向左展开才不会在悬停时又把页面撑宽。 */
.ab-tip-box { position: absolute; top: calc(100% + 6px); right: 0; left: auto; z-index: 60;
  display: none;
  width: max-content; max-width: min(460px, 60vw);
  padding: 8px 11px; border-radius: 8px;
  background: #26313d; color: #fff;
  font-size: 11.5px; line-height: 1.6; font-weight: 400;
  box-shadow: 0 8px 22px rgba(0, 0, 0, .24); }
@keyframes abTipIn { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
.ab-tip:hover .ab-tip-box,
.ab-tip:focus .ab-tip-box,
.ab-tip:focus-within .ab-tip-box { display: block; animation: abTipIn .14s ease both; }
.ab-tip-box b { color: #ffd79a; font-weight: 700; }

/* ---------------- 页面级卡片间距：一屏内几张卡片之间的留白（2026-10-09 用户要求） ----------------
   用户要求：一屏里那几张卡片挨得太紧，把**卡片之间的间距再扩大一倍**。
   原来各页顶层容器写的是内联 `style="gap:18px"`（异常订单维护页是 14px —— 第 53 轮为把高度让给列表收过一档）。
   做法：
     · 把所有顶层卡片容器的内联 gap **换成 .page-stack 这个类**（内联样式优先级最高，
       留着内联就得给每条加 !important，以后调间距要逐页去翻，容易漏）；
     · 间距只在这一个地方定义 —— 18px → **36px**、异常订单页的 14px → **28px**，正好各翻一倍；
     · 异常订单维护页用 .ab-compact 的子选择器单独给 28px（它本来就比别的页紧一档，
       直接跟 36px 会看不出「这一页刻意收紧」的意图）。
   ⚠ 只作用在**顶层容器**上（类名叫 page-stack 而不是改 .grid 的默认 gap）——
     卡片体内部那些 `<div class="grid" style="gap:10px/12px/14px">`（表单行、说明块）
     不属于「卡片之间的间距」，没有被换过类名，一行都不会受影响。 */
.page-stack { display: grid; gap: 36px; }
/* 异常订单维护页在 views6.js 里给顶层容器补了 page-stack-tight（不用 CSS :has —— 老浏览器不支持） */
.page-stack-tight { gap: 28px; }

/* ---------------- 异常订单列表：卡片头里的「共 N 条 + 排序」靠右一组（2026-10-09 用户要求第 1 条） ----------------
   .card-head 是 space-between：直接塞三个裸子项，「共 N 条」会被摆到卡片正中间，
   夹在标题和排序框中间，看着像两栏错开。所以把这两样包进 .ab-list-head 一起推到右边。
   排序下拉本身也收一档（列表卡不是筛选卡，那个 .field 的标签不该抢标题的注意力）：
   标签与下拉同一行、字号 11.5px、下拉高度 30px，正好卡在卡片头里不撑高卡片。
   ⚠ 只在本页用（类名 ab-list-head / ab-sort），不动全站 .card-head / .field。 */
.ab-list-head { display: flex; align-items: center; gap: 12px; margin-left: auto; }
.ab-sort { display: flex; align-items: center; gap: 6px; }
.ab-sort > span { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.ab-sort > .select { height: 30px; padding: 0 26px 0 8px; font-size: 12px; min-width: 118px; }

/* ---------------- 异常订单列表：操作列图标与「操作」表头对齐（2026-10-08 用户要求） ----------------
   问题：全站 `.actions` 是 `justify-content: flex-end`（按钮贴单元格右边），
        而这个表头原来是 `ctr`（居中）→ 标题在左、图标在右，看着像两栏错位。
   解法（只动这一页，不动全局 `.actions` —— 那个类全站 12 处在用）：
     ① views6.js 里把该列的 `ctr` 去掉 → 表头左对齐（与其余列同风格）；
     ② 这里把按钮组改成靠左，并用 `margin-left:-8px` 抵消按钮自身的留白：
        `.icon-btn` 是 32px 宽的方框、里面 17px 的图标居中 → 图标图形距按钮左边缘 7.5px，
        不减掉这 7.5px，图标就会比标题首字右移一格，看着仍旧"没对齐"。
        抵消后：标题文字左缘 ≈ 图标图形左缘（都在单元格 padding-left 14px 处）。 */
table.data .ab-actions { justify-content: flex-start; margin-left: -8px; }

/* ---------------- 异常订单导入卡：紧凑拖放区（2026-10-08 新增导入功能） ----------------
   这一页本来就有概况 / 筛选 / 处理三张卡，再加一张导入卡会把最下面的
   「异常订单列表」挤得更矮（而列表才是这页真正要用的）。
   所以拖放区用页面专用类 .ab-drop 收成一行：图标 30px→22px、内边距 30px→10px、
   说明文字改成横排 —— 平时只占一行高，选了文件之后预览区才在下面展开。
   ⚠ 不改全站 .drop-zone：运单资料 / 入库订单那两页的拖放区是大块落点，需要存在感。 */
.ab-drop { flex-direction: row; flex-wrap: wrap; gap: 10px; padding: 10px 16px; }
.ab-drop > svg { width: 22px; height: 22px; }
.ab-drop b { font-size: 13px; }
.ab-drop span { font-size: 11.5px; }


/* ---------------- 运单资料录入：13 步顺序引导 + 快递检索复选（2026-10-12） ----------------
   这一页从「一堆并排的输入框」改成「按 1→11 有顺序的步骤行」，让操作员顺着往下填。
   ⚠ 只加 .wbe-* 前缀的页面专用类，不动全局 .filter-row / .field，避免影响其它页。 */
.wbe-step { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
/* 步骤序号气泡：小、圆、定宽，让左侧数字列对齐 */
.wbe-no {
  flex: 0 0 22px; width: 22px; height: 22px; line-height: 22px; text-align: center;
  border-radius: 50%; background: var(--brand, #2b6cf6); color: #fff;
  font-size: 12px; font-weight: 700; margin-bottom: 11px;
}
.wbe-req { color: var(--danger, #d64545); font-weight: 700; font-size: 11px; }

/* 快递公司检索复选控件：输入框 + 下拉候选 + 已选标签 */
.wbe-courier { position: relative; }
.wbe-courier-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; min-height: 20px; align-items: center; }
.wbe-tag {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 8px;
  background: var(--brand-soft, #e8f0fe); color: var(--brand, #2b6cf6);
  border-radius: 14px; font-size: 12.5px; font-weight: 600;
}
.wbe-tag i { cursor: pointer; font-style: normal; font-weight: 700; opacity: .6; padding: 0 1px; }
.wbe-tag i:hover { opacity: 1; color: var(--danger, #d64545); }
.wbe-courier-menu {
  position: absolute; z-index: 40; left: 0; right: 0; top: 100%; margin-top: 4px;
  background: #fff; border: 1px solid var(--line, #e2e6ee); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(20,30,60,.14); max-height: 260px; overflow: auto; padding: 4px;
}
.wbe-courier-item {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  border-radius: 6px; cursor: pointer; font-size: 13.5px;
}
.wbe-courier-item:hover { background: var(--hover, #f3f6fb); }
.wbe-courier-item.on { background: var(--brand-soft, #e8f0fe); color: var(--brand, #2b6cf6); font-weight: 600; }
.wbe-ck { width: 16px; display: inline-block; text-align: center; font-weight: 700; }
.wbe-courier-empty { padding: 10px; color: var(--muted, #8a94a6); font-size: 13px; text-align: center; }

/* 「尺寸 / 到仓时间」折叠区：机器已带出，默认收起，减少页面高度 */
.wbe-more { border-top: 1px dashed var(--line, #e2e6ee); padding-top: 10px; }
.wbe-more > summary { cursor: pointer; font-size: 13px; color: var(--muted, #8a94a6); user-select: none; }
.wbe-more > summary:hover { color: var(--brand, #2b6cf6); }

/* ---------------- 第 2 步：机测数据胶囊（2026-10-13） ----------------
   扫到单号后，在「国内快递单号」录入框正下方用胶囊列出机器带过来的
   实重 / 尺寸 / 体积重 / 入库时间，操作员核对着往下填，不用回头看上面的卡。 */
.wbe-pills {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 7px; padding: 7px 9px;
  background: var(--brand-soft, #eef4ff); border: 1px dashed var(--brand, #2b6cf6);
  border-radius: 8px;
}
.wbe-pills-cap { font-size: 12px; color: var(--muted, #8a94a6); margin-right: 2px; }
.wbe-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 13px;
  background: #fff; border: 1px solid var(--brand, #2b6cf6);
  font-size: 12.5px; line-height: 1.5; white-space: nowrap;
}
.wbe-pill b { color: var(--muted, #8a94a6); font-weight: 600; font-size: 11.5px; }
.wbe-pill i { font-style: normal; font-weight: 700; color: var(--brand, #2b6cf6); }
/* 查到单号但这票没有机测数据时的提示（避免「什么都不显示」让人以为功能坏了） */
.wbe-pills-none { font-size: 12.5px; color: var(--warn, #b26a00); }

/* ---------------- 第 9 步：收货客户代码固定 ST 前缀（2026-10-13） ----------------
   左边 ST 是不可编辑的装饰，右边输入框只收数字；视觉上拼成一个整体输入框。 */
.wbe-prefix-wrap {
  display: flex; align-items: stretch; width: 100%; min-width: 150px;
  border: 1px solid var(--line, #e2e6ee); border-radius: 7px;
  background: #fff; overflow: hidden;
}
.wbe-prefix {
  display: inline-flex; align-items: center; padding: 0 10px;
  background: var(--brand-soft, #eef4ff); color: var(--brand, #2b6cf6);
  font-weight: 700; font-size: 13.5px; letter-spacing: .5px;
  border-right: 1px solid var(--line, #e2e6ee); user-select: none;
}
.wbe-prefix-wrap .input {
  border: none !important; border-radius: 0 !important; box-shadow: none !important;
  flex: 1; min-width: 0; background: transparent;
}
.wbe-prefix-wrap:focus-within { border-color: var(--brand, #2b6cf6); box-shadow: 0 0 0 3px rgba(43,108,246,.12); }
