/* =========================================================================
   卡车车费 · 开票工具 —— 全局样式
   使用者是每天做账的人：桌面优先、密度高、字号小而清楚、颜色克制。
   除非确有必要，页面模块不要再写自己的 CSS，一律复用下面这套类。

   2026-10-22 视觉按 PortPro（app.portpro.io/loads）那套重做：令牌出处见 docs/research/portpro-design-tokens.md，
   参考截图在 docs/design-references/portpro-*.jpg。要点：Inter 12px 正文、#172a41 字、#f1f4f8 底、#377df6 主色、
   蓝色侧栏 + 白色顶栏、表格不套白框而是「每行一张白卡」（border-spacing 0 2px，悬停左侧 2px 蓝条）、
   控件 32px 高 / 3px 圆角 / 灰底输入框、徽章 10px 粗体白字。类名一个没改，页面模块不用动。

   【可复用类清单】（页面模块直接用这些 class，别造新的）
   按钮      .btn  .btn-primary  .btn-danger  .btn-ghost  .btn-sm
   表单      .input  .select  .textarea  .field  .field-label  .field-hint  .form-grid
   容器      .card  .card-head  .card-body  .toolbar  .stat-card  .stat-grid
   表格      .table-wrap（横向滚动） .table-scroll（限高 + 粘性表头） .table
             列对齐：给 th/td 加 .right（右对齐 + 等宽数字） .center .nowrap
   状态徽章  .badge  .badge-pending  .badge-invoiced  .badge-paid  .badge-void  .badge-overdue
   弹窗      .modal-backdrop  .modal  .modal-head  .modal-title  .modal-body  .modal-foot
   提示      .toast（由 core.toast() 生成，页面不用手写）
   零散      .empty  .muted  .right  .row  .col  .gap  .gap-lg  .grow  .wrap  .tag  .pager
             .ellipsis  .mono  .sep  .link  .danger-text  .ok-text  .warn-text
   说明      .help-tip-wrap / .help-tip / .help-tip-text（由 core.helpTip() 生成，页面不用手写）
             解释性的文字一律进 helpTip；状态、警告、报错、结果、空列表那一句照旧直接显示
   ========================================================================= */

/* ---------- 配色令牌（亮色，PortPro 原值） ---------- */
:root {
  /* PortPro 灰阶 */
  --gray-15: #f8f9fb; --gray-25: #f8f9fd; --gray-50: #f1f4f8; --gray-100: #dbe2eb; --gray-200: #bfccda;
  --gray-300: #a3b3c6; --gray-400: #879aaf; --gray-500: #687d96; --gray-600: #4e657f; --gray-700: #394e66;
  --gray-800: #293b51; --gray-900: #172a41;
  --primary-50: #ecf1fe; --primary-100: #c0d5fc; --primary-200: #98b9fb; --primary-500: #377df6;
  --primary-600: #316add; --primary-700: #2554ad; --primary-800: #1f4893;

  --bg: var(--gray-50);
  --panel: #ffffff;
  --panel-2: var(--gray-15);
  --fg: var(--gray-900);
  --fg-soft: var(--gray-700);
  --muted: var(--gray-500);
  --border: var(--gray-100);
  --border-strong: var(--gray-200);

  --accent: var(--primary-500);
  --accent-hover: var(--primary-600);
  --accent-fg: #ffffff;
  --accent-soft: var(--primary-50);

  --danger: #eb4e3d;
  --danger-hover: #d8402f;
  --ok: #3f9d2b;            /* PortPro 的 #65ca44 做底色够亮、做字色太浅，字色压深一档 */
  --ok-bg: #65ca44;
  --warn: #c97a1d;
  --warn-bg: #f19c38;
  --info-bg: #29b6f6;

  --th-bg: var(--gray-50);  /* 表头 = 页面底色：PortPro 的表头是「浮」在灰底上的 */
  --zebra: #ffffff;         /* PortPro 没有斑马纹，行与行之间靠 2px 灰缝分开 */
  --row-hover: #f6f8ff;
  --row-sel: var(--primary-50);

  --btn-bg: #ffffff;
  --btn-bg-hover: var(--gray-50);
  --input-bg: var(--gray-50);   /* PortPro 输入框是灰底，聚焦才变白 */

  /* 侧栏：PortPro 蓝；悬停 / 当前项翻成白底深字 */
  --side-bg: var(--primary-500);
  --side-brand-bg: var(--primary-700);
  --side-fg: #ffffff;
  --side-icon: var(--primary-100);
  --side-fg-strong: #ffffff;
  --side-hover: #f0f6fa;
  --side-hover-fg: #172940;
  --side-active: #ffffff;
  --side-active-fg: #172940;
  --side-border: rgba(255, 255, 255, .15);
  --side-shadow: 0 15px 35px rgba(20, 28, 52, .2);

  --shadow: none;
  --shadow-lg: 0 10px 30px rgba(23, 42, 65, .18);

  --badge-pending-bg: var(--gray-300);  --badge-pending-fg: #ffffff;
  --badge-invoiced-bg: var(--info-bg);  --badge-invoiced-fg: #ffffff;
  --badge-paid-bg: var(--ok-bg);        --badge-paid-fg: #ffffff;
  --badge-void-bg: rgba(120, 144, 156, .15); --badge-void-fg: #4d6774;
  --badge-overdue-bg: var(--danger);    --badge-overdue-fg: #ffffff;

  --side-w: 70px;           /* 平时只有图标（PortPro 原样，用户 2026-10-23）；鼠标过去展开到 --side-w-open */
  --side-w-open: 225px;
  --side-cur: var(--side-w);  /* 侧栏此刻的宽：顶栏和正文按它让位，展开时一起往右平移、收回时一起往左（用户 2026-10-23） */
  --top-h: 60px;
  --radius: 3px;
  --radius-sm: 3px;
  --radius-row: 5px;
}

/* ---------- 配色令牌（暗色）：PortPro 没有暗色，这里只把同一套关系搬到深底上 ---------- */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #10141a;
    --panel: #1a1f27;
    --panel-2: #1f252e;
    --fg: #e5e9ef;
    --fg-soft: #c2cad4;
    --muted: #8b95a3;
    --border: #2b323c;
    --border-strong: #3a424e;

    --accent: #4f8cff;
    --accent-hover: #6ba0ff;
    --accent-fg: #0b1220;
    --accent-soft: #1b2a44;

    --danger: #e2564f;
    --danger-hover: #f06b64;
    --ok: #4ade80;
    --warn: #fbbf24;

    --th-bg: #10141a;
    --zebra: #1a1f27;
    --row-hover: #232c3a;
    --row-sel: #26344a;

    --btn-bg: #232a34;
    --btn-bg-hover: #2b333f;
    --input-bg: #161b22;

    --side-bg: var(--primary-800);
    --side-brand-bg: #152e61;
    --side-hover: rgba(255, 255, 255, .12);
    --side-hover-fg: #ffffff;
    --side-active: #ffffff;
    --side-active-fg: #172940;

    --shadow-lg: 0 12px 34px rgba(0, 0, 0, .6);

    --badge-void-bg: #262b33;     --badge-void-fg: #8b95a3;
  }
}

/* ---------- 基础 ---------- */
* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--bg);
  color: var(--fg);
  /* PortPro 是 Inter；本机没装就退到系统字体，中文退 Noto Sans SC / 微软雅黑 */
  font-family: Inter, "Noto Sans SC", system-ui, -apple-system, "Segoe UI", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif;
  font-size: 12px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}

/* 金额、编号、日期全是数字对齐场景：等宽数字让上下位数对得整整齐齐 */
body { font-variant-numeric: tabular-nums; }

h1, h2, h3, h4 { margin: 0 0 10px; font-weight: 500; line-height: 1.3; color: var(--fg); }
h1 { font-size: 18px; }
h2 { font-size: 16px; }
h3 { font-size: 14px; }
p { margin: 0 0 8px; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, .mono { font-family: ui-monospace, Consolas, "Courier New", monospace; font-size: 11.5px; }
hr, .sep { border: 0; border-top: 1px solid var(--border); margin: 12px 0; }

::-webkit-scrollbar { width: 8px; height: 10px; }   /* 横向那条厚一点、深一点：宽表格靠它拖，得一眼看见（用户 2026-10-23） */
::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: var(--gray-300); }
::-webkit-scrollbar-track { background: transparent; }

/* ---------- 布局：固定蓝侧栏 + 固定白顶栏 + 右侧内容 ---------- */
.app { min-height: 100%; }

.sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--side-w);
  display: flex;
  flex-direction: column;
  background: var(--side-bg);
  color: var(--side-fg);
  z-index: 51;
  overflow: hidden;
  transition: width .1s ease-out, box-shadow .1s ease-out;
}
/* 鼠标过去（或键盘焦点在里面）才展开；移开就收回去。顶栏和正文跟着 --side-cur 平移（body:has），不是盖在内容上 */
.sidebar:hover, .sidebar:focus-within { width: var(--side-w-open); box-shadow: var(--side-shadow); }
body:has(.sidebar:hover), body:has(.sidebar:focus-within) { --side-cur: var(--side-w-open); }
.sidebar:not(:hover):not(:focus-within) .brand-text,
.sidebar:not(:hover):not(:focus-within) .brand-sub,
.sidebar:not(:hover):not(:focus-within) .nav-item span,
.sidebar:not(:hover):not(:focus-within) .side-foot { display: none; }
.sidebar:not(:hover):not(:focus-within) .brand { padding: 0; justify-content: center; }
.sidebar:not(:hover):not(:focus-within) .nav-item { justify-content: center; padding: 0; margin: 0 15px 2px; }
.sidebar:not(:hover):not(:focus-within) .nav-group-label { height: 1px; padding: 0; margin: 6px 18px 8px; background: rgba(255, 255, 255, .22); color: transparent; overflow: hidden; }
.nav-item span { white-space: nowrap; }

/* 品牌块：和 PortPro 一样是顶栏同高（60px）的一块深蓝，白色图标 + 名字 */
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--top-h);
  padding: 0 16px;
  color: var(--side-fg-strong);
  background: var(--side-brand-bg);
}
.brand-mark {
  width: 28px; height: 28px;
  flex: 0 0 28px;
  display: grid; place-items: center;
  border-radius: 6px;
  background: rgba(255, 255, 255, .14);
  color: #fff;
}
.brand-mark svg { width: 18px; height: 18px; }
.brand-text { font-weight: 600; font-size: 13px; letter-spacing: .3px; white-space: nowrap; }
.brand-sub { font-size: 11px; color: var(--side-fg); opacity: .7; }

.nav { flex: 1 1 auto; overflow-y: auto; padding: 10px 0 14px; }
.nav::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .3); }
.nav-group-label {
  font-size: 10.5px;
  color: var(--side-fg);
  opacity: .6;
  padding: 8px 20px 3px;
  letter-spacing: .6px;
  text-transform: uppercase;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 34px;
  padding: 0 10px;
  margin: 0 10px 2px;
  border-radius: 6px;
  color: var(--side-fg);
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition: background-color .1s ease-out, color .1s ease-out;
}
.nav-item svg { width: 18px; height: 18px; flex: 0 0 18px; color: var(--side-icon); transition: color .1s ease-out; }
.nav-item:hover { background: var(--side-hover); color: var(--side-hover-fg); text-decoration: none; }
.nav-item:hover svg { color: var(--accent); }
.nav-item.active { background: var(--side-active); color: var(--side-active-fg); font-weight: 600; }
.nav-item.active svg { color: var(--accent); }

.side-foot {
  border-top: 1px solid var(--side-border);
  padding: 10px 16px 12px;
  font-size: 11px;
  color: var(--side-fg);
  opacity: .75;
  line-height: 1.45;
}
.side-foot .path {
  display: block;
  margin-top: 3px;
  word-break: break-all;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 10px;
  opacity: .85;
}

/* 顶栏：PortPro 的白色 topbar —— 左边全局搜索（Search Everything…），右边公司名 + 头像 */
.topbar {
  position: fixed;
  top: 0; right: 0;
  left: var(--side-cur);
  height: var(--top-h);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 30px 0 24px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  z-index: 40;
  transition: left .1s ease-out;
}
.top-search { position: relative; flex: 0 1 454px; }
.top-search svg {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 18px; height: 18px; color: var(--gray-500); pointer-events: none;
}
.top-search .input {
  height: 30px;
  padding-left: 36px;
  border: 1px solid transparent;
  border-radius: 50px;
  background: transparent;
  font-size: 12px;
}
.top-search .input:focus { background: var(--gray-50); border-color: var(--border); box-shadow: none; }
.top-search .input::placeholder { color: var(--gray-500); }
.top-right { margin-left: auto; display: flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 500; color: var(--fg); white-space: nowrap; }
.top-avatar {
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--gray-50); color: var(--fg);
  font-size: 13px; font-weight: 500; text-transform: uppercase;
}
.top-company b { font-weight: 500; }

.main {
  margin-left: var(--side-cur);
  min-height: 100vh;
  padding: calc(var(--top-h) + 10px) 20px 40px;
  transition: margin-left .1s ease-out;
}

/* ---------- 顶部加载条 ---------- */
.progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 2.5px;
  z-index: 90;
  background: transparent;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity .1s ease;
}
.progress.on { opacity: 1; }
.progress::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 34%;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: progress-slide 1.05s infinite ease-in-out;
}
@keyframes progress-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(390%); }
}

/* ---------- toast ---------- */
.toasts {
  position: fixed;
  top: calc(var(--top-h) + 10px); right: 16px;
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(420px, 46vw);
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 9px 12px;
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--fg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  box-shadow: var(--shadow-lg);
  font-size: 12px;
  cursor: pointer;
  animation: toast-in .1s ease-out;
}
.toast.out { opacity: 0; transform: translateX(10px); pointer-events: none; transition: all .1s ease-in; }
.toast-icon {
  flex: 0 0 16px;
  width: 16px; height: 16px;
  margin-top: 1px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 700; color: #fff;
  background: var(--muted);
}
.toast-msg { white-space: pre-wrap; word-break: break-word; }
.toast-ok { border-left-color: var(--ok-bg); }
.toast-ok .toast-icon { background: var(--ok-bg); }
.toast-err { border-left-color: var(--danger); }
.toast-err .toast-icon { background: var(--danger); }
.toast-info { border-left-color: var(--accent); }
.toast-info .toast-icon { background: var(--accent); }
@keyframes toast-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* ---------- 按钮（PortPro btn-outline-light / btn-primary：32px、3px 圆角、12px 500） ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--fg);
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
  transition: background-color .1s ease, border-color .1s ease;
}
.btn:hover:not(:disabled) { background: var(--btn-bg-hover); border-color: var(--border-strong); }
.btn:active:not(:disabled) { transform: translateY(.5px); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn svg { width: 16px; height: 16px; }

.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-danger:hover:not(:disabled) { background: var(--danger-hover); border-color: var(--danger-hover); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--fg-soft); }
.btn-ghost:hover:not(:disabled) { background: var(--btn-bg-hover); border-color: transparent; color: var(--fg); }

.btn-sm { height: 26px; padding: 0 8px; font-size: 11.5px; }
.btn-sm svg { width: 13px; height: 13px; }

/* ---------- 表单控件（PortPro form-control：32px、3px 圆角、灰底、聚焦变白） ---------- */
.input, .select, .textarea {
  width: 100%;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--input-bg);
  color: var(--fg);
  font: inherit;
  font-size: 12px;
  transition: background-color .1s ease, border-color .1s ease;
}
.textarea { height: auto; min-height: 64px; padding: 7px 10px; line-height: 1.5; resize: vertical; }
.select { padding-right: 4px; cursor: pointer; }
/* 表格行里的小下拉（发件邮箱等）：和 .btn-sm 一样高 */
.select.select-sm { height: 26px; font-size: 11.5px; padding: 0 2px; max-width: 100%; }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--panel);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.input:disabled, .select:disabled, .textarea:disabled { background: var(--gray-50); color: var(--muted); cursor: not-allowed; }
.input::placeholder, .textarea::placeholder { color: var(--muted); }
.input.right, .input[type="number"] { text-align: right; font-variant-numeric: tabular-nums; }
.input[type="date"] { padding-right: 4px; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; vertical-align: -3px; }

.field { margin-bottom: 12px; }
.field-label { display: block; margin-bottom: 5px; font-size: 12px; color: var(--fg); font-weight: 500; }
.field-hint { margin-top: 4px; font-size: 11px; color: var(--muted); }
.field .row { gap: 6px; }
/* 弹窗里的两列表单 */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 15px; }
.form-grid .full { grid-column: 1 / -1; }

/* ---------- 卡片 / 工具条（PortPro card：白底、3px 圆角、没有边框和阴影） ---------- */
.card {
  background: var(--panel);
  border: 0;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 15px;
  margin-bottom: 12px;
}
.card.tight { padding: 0; }
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 15px;
  border-bottom: 1px solid var(--border);
  font-weight: 500;
  font-size: 14px;
}
.card-body { padding: 15px; }

.toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}
/* 运输订单页：筛选一行 + 批量操作一行（一直在：勾选时冒出来会把表格往下顶，连着勾几行会点错，见 jobs.js buildToolbar）。
   第二行框起来：一眼看得出「这一排是对勾选的单做的」，和上面的筛选不是一回事；有勾选时换成浅蓝底 */
.jobs-toolbar { margin-bottom: 12px; }
.jobs-toolbar > .toolbar { margin-bottom: 0; }
.toolbar.bulk-actions {
  margin-top: 8px;
  padding: 6px 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.toolbar.bulk-actions.has-picked { background: var(--accent-soft); border-color: var(--primary-100); }
.toolbar .input, .toolbar .select { width: auto; min-width: 120px; }
/* 工具条上的搜索框照 PortPro「Search The Board…」：白底药丸 */
.toolbar .search { min-width: 220px; border-radius: 50px; background: var(--panel); }
.toolbar .search:focus { background: var(--panel); }
.toolbar .spacer { flex: 1 1 auto; }

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 52px;
  padding: 10px 0;
  margin-bottom: 4px;
}
.page-head h1 { margin: 0; font-size: 18px; font-weight: 500; }

/* ---------- 统计卡（PortPro ncard / bbox：白卡里套 1px 灰框小盒） ---------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}
.stat-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  padding: 10px 12px;
}
.stat-card .stat-label { font-size: 12px; color: var(--gray-400); }
.stat-card .stat-value {
  font-size: 20px;
  font-weight: 500;
  line-height: 1.25;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}
.stat-card .stat-sub { font-size: 11px; color: var(--muted); margin-top: 3px; }
.stat-card.is-pending .stat-value { color: var(--warn); }
.stat-card.is-invoiced .stat-value { color: var(--accent); }
.stat-card.is-paid .stat-value { color: var(--ok); }
.stat-card.is-overdue .stat-value { color: var(--danger); }
.stat-card.clickable { cursor: pointer; transition: border-color .1s ease, background-color .1s ease; }
.stat-card.clickable:hover { border-color: var(--accent); background: var(--row-hover); }

/* ---------- 表格（PortPro table-card：每行一张白卡，表头浮在灰底上） ---------- */
.table-wrap {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow-x: auto;           /* 宽表格自己横向滚，绝不把整页撑歪 */
}
/* 需要粘性表头时给 .table-wrap 再加 .table-scroll（限高才有滚动，粘性才有意义） */
/* 高度由 core.fitTableScroll 按表格在页面里的位置算（--table-max），横向滚动条永远停在表格底下、不被挤出视口 */
.table-scroll { max-height: var(--table-max, calc(100vh - 250px)); overflow-y: auto; }

.table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0 2px;
  font-size: 12px;
}
.table th, .table td {
  padding: 8px 10px;
  text-align: left;
  border-bottom: 0;
  vertical-align: middle;
  /* 表格是主战场：默认单行 + 省略号，配合 core.table() 自动挂的 title 可 hover 看全文 */
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  height: 30px;
  padding: 5px 10px;
  background: var(--th-bg);
  color: var(--gray-500);
  font-weight: 600;
  font-size: 11px;
  white-space: nowrap;
}
.table thead th.sortable { cursor: pointer; user-select: none; }
.table thead th.sortable:hover { color: var(--fg); }
.table thead th .sort-ind { margin-left: 4px; font-size: 9px; color: var(--gray-200); }
.table thead th.sorted .sort-ind { color: var(--accent); }
.table tbody td { background: var(--panel); }
.table tbody td:first-child { border-radius: var(--radius-row) 0 0 var(--radius-row); }
.table tbody td:last-child { border-radius: 0 var(--radius-row) var(--radius-row) 0; }
.table tbody tr:nth-child(even) td { background: var(--zebra); }
.table tbody tr:hover td { background: var(--row-hover); }
/* 悬停行首格左侧 2px 蓝条（PortPro 原样：background-image 画的，不占位、不挤文字） */
.table tbody tr:hover td:first-child { background-image: linear-gradient(to right, var(--accent) 2px, transparent 2px); }
.table tbody tr.selected td { background: var(--row-sel); }
.table tbody tr.clickable { cursor: pointer; }
.table tbody tr.empty-row:hover td { background: transparent; background-image: none; }
/* 白卡里的表格：行和卡同色，2px 灰缝看不见 —— 改回细线分行 */
.card .table, .modal .table { border-spacing: 0; }
.card .table tbody td, .modal .table tbody td { border-bottom: 1px solid var(--border); border-radius: 0; }
.card .table tbody tr:last-child td, .modal .table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr.empty-row td { max-width: none; white-space: normal; }
/* 展开的费用明细行（车费记录页） */
.table tbody tr.detail-row td { background: var(--panel-2); white-space: normal; max-width: none; padding: 10px 15px; }
.table td.wrap { white-space: normal; }
.table .col-check { width: 30px; padding-right: 0; }
.table .col-actions { width: 1%; white-space: nowrap; }
/* 操作列吸在表格右边（2026-10-02，.col-sticky：运输订单页、物流跟踪页那两张宽表用；列定义里 class 和 thClass 都要带上）：
   表格十几二十列，横着滚的时候「编辑 / 删除 / 查」一直在手边，不用先拖到底。
   吸住的格子后面有别的格子滚过去，底色必须不透明：
   · 平常的行这里给 --panel（行卡的底色）；
   · 斑马 / 悬停 / 选中那三条规则（上面的 tr:nth-child(even) td、tr:hover td、tr.selected td）优先级比这条高，照旧盖上来，
     所以这里**不许**把优先级写得比它们高（写成 tbody tr td.col-sticky 就会把悬停、选中的底色压掉）；
   · 作废（is-void）、LFD 快到（lfd-alert）只改字色 / 第一格的红边，不动底色。
   表头那一格同时吸顶和吸右，z-index 要压过别的表头格。 */
.table th.col-sticky, .table td.col-sticky { position: sticky; right: 0; border-left: 1px solid var(--border); }
.table td.col-sticky { background: var(--panel); z-index: 1; }
.table thead th.col-sticky { z-index: 3; background: var(--th-bg); }
/* 物流跟踪页的操作格：五个小按钮排成两行（一行排开要占 300px，吸在右边会挡住半张表）。
   按钮压矮一点（22px）：两行按钮 ≈ 左边「柜号 + 提单号」两行字的高度，不把每一行撑高；
   176px 放得下最宽的那一行「查 · 船司 · 认不出码头」，再窄会折成三行 */
.track-actions { display: flex; flex-wrap: wrap; gap: 4px 5px; justify-content: flex-end; width: 176px; }
.track-actions .btn { height: 22px; padding: 0 7px; font-size: 11.5px; }

/* 行内编辑：点格子里的值就地改。.input/.select 原本 32px 高，会把行撑高，
   每开一格整行跳一下，所以压成 24px 和操作列的小按钮一样高 */
.table td .cell-val { cursor: text; border-bottom: 1px dashed transparent; }
.table td .cell-val:hover { border-bottom-color: var(--muted); }

/* 号码旁边的复制小图标：平时淡一点不抢眼，指上去才明显；复制成功变成绿色对勾 */
.copy-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; min-height: 0; margin: 0 0 0 3px; padding: 0;
  border: 0; border-radius: 3px; background: transparent; box-shadow: none;
  color: var(--muted); opacity: .55; cursor: pointer; vertical-align: middle; flex: none; line-height: 1;
}
.copy-btn:hover { opacity: 1; color: var(--accent); background: var(--accent-soft); }
.copy-btn:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
.copy-btn.is-copied { opacity: 1; color: var(--ok); background: transparent; }
.copy-btn svg { display: block; pointer-events: none; }

/* 悬停说明 ⓘ（core.helpTip）。解释性的文字都收在这里面，悬停 / 聚焦 / 点一下才出（2026-09-17 用户要界面简洁）。
   弹层用 fixed + JS 定位：弹窗正文、表格格子、横向滚动区都会把绝对定位的东西裁掉 */
.help-tip-wrap { display: inline-flex; align-items: center; vertical-align: middle; position: relative; }
.help-tip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; min-height: 0; margin: 0 0 0 4px; padding: 0;
  border: 1px solid var(--border-strong); border-radius: 50%;
  background: transparent; box-shadow: none; color: var(--muted);
  font: 600 10px/1 Georgia, 'Times New Roman', serif; cursor: help; flex: none;
}
.help-tip::before { content: 'i'; }
.help-tip:hover, .help-tip-wrap.is-open .help-tip { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.help-tip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.help-tip-text { display: none; }
.help-tip-wrap.is-open .help-tip-text {
  display: block; position: fixed; z-index: 95;
  max-width: min(380px, 90vw); padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 3px;
  background: var(--panel); color: var(--fg); box-shadow: var(--shadow-lg);
  font-size: 12px; font-weight: 400; line-height: 1.6; letter-spacing: 0;
  white-space: normal; text-align: left; text-transform: none; cursor: auto;
}
.help-tip-text code { font-size: 11.5px; }
/* 表单行：标签 + ⓘ 一行 */
.field-head { display: flex; align-items: center; gap: 2px; margin-bottom: 5px; }
.field-head > .field-label { margin-bottom: 0; }
.table td .cell-locked { cursor: not-allowed; }
.table td .cell-locked:hover { border-bottom-color: transparent; }
.table td .cell-empty { color: var(--muted); }
.table td .cell-edit { height: 24px; padding: 0 6px; font-size: 12px; line-height: 22px; width: 100%; min-width: 72px; }
.table td select.cell-edit { padding-right: 18px; }
.table tfoot td { background: var(--primary-100); font-weight: 600; }   /* PortPro 的 Billing Total 是浅蓝块 */
.table tfoot td:first-child { border-radius: var(--radius-row) 0 0 var(--radius-row); }
.table tfoot td:last-child { border-radius: 0 var(--radius-row) var(--radius-row) 0; }
/* 作废的行整行灰掉 —— 一眼看出这行不算数 */
.table tbody tr.is-void td { color: var(--muted); }
.table tbody tr.is-void td.master { text-decoration: line-through; }

/* 金额列：右对齐 + 等宽数字（三处都要，因为 th/td 都可能单独用） */
.right, .table th.right, .table td.right {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.center, .table th.center, .table td.center { text-align: center; }
.nowrap { white-space: nowrap; }
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- 徽章 / 标签（PortPro badge-sm：10px 粗体白字、3px 圆角） ---------- */
.badge {
  display: inline-block;
  padding: 0 5px;
  border-radius: 3px;
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  white-space: nowrap;
  background: var(--badge-void-bg);
  color: var(--badge-void-fg);
}
.badge-pending  { background: var(--badge-pending-bg);  color: var(--badge-pending-fg); }
.badge-invoiced { background: var(--badge-invoiced-bg); color: var(--badge-invoiced-fg); }
.badge-paid     { background: var(--badge-paid-bg);     color: var(--badge-paid-fg); }
.badge-void     { background: var(--badge-void-bg);     color: var(--badge-void-fg); text-decoration: line-through; }
.badge-overdue  { background: var(--badge-overdue-bg);  color: var(--badge-overdue-fg); }

.tag {
  display: inline-block;
  padding: 0 6px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--panel-2);
  color: var(--fg-soft);
  font-size: 11px;
  line-height: 17px;
  white-space: nowrap;
}
.tag + .tag { margin-left: 4px; }

/* ---------- 弹窗（PortPro：深蓝遮罩 .6，白卡 3px 圆角，标题 16~20px 500） ---------- */
body.modal-open { overflow: hidden; }
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(23, 42, 65, .6);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 5vh 16px 24px;
  overflow-y: auto;
  animation: fade-in .08s ease-out;
}
/* 淡出期间（约 150ms）节点还在 DOM 里：屏蔽点击，免得用户手快点到「正在消失的弹窗」上 */
.modal-backdrop.out { opacity: 0; pointer-events: none; transition: opacity .1s ease-in; }
.modal {
  width: 520px;
  max-width: 100%;
  background: var(--panel);
  border: 0;
  border-radius: 6px;
  box-shadow: var(--shadow-lg);
  animation: modal-in .1s ease-out;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
}
.modal-title { font-weight: 500; font-size: 16px; }
.modal-x {
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  padding: 0 2px;
  cursor: pointer;
}
.modal-x:hover { color: var(--fg); }
.modal-body { padding: 15px 20px; max-height: 68vh; overflow-y: auto; }
.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px 20px;
  border-top: 1px solid var(--border);
  background: var(--panel);
  border-radius: 0 0 6px 6px;
}
.confirm-msg { font-size: 12.5px; line-height: 1.6; white-space: pre-wrap; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ---------- 分页（PortPro page-item：32px，当前页深色底白字） ---------- */
.pager {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  padding: 10px 2px 0;
}
.pager .btn { min-width: 32px; padding: 0 8px; }
.pager .btn.is-on, .pager .btn-primary { background: var(--gray-900); border-color: var(--gray-900); color: #fff; }
.pager-info { margin-right: 6px; font-size: 12px; color: var(--muted); }

/* ---------- 零散 ---------- */
.empty {
  padding: 26px 14px;
  text-align: center;
  color: var(--muted);
  font-size: 12px;
}
.muted { color: var(--muted); }
.link { color: var(--accent); cursor: pointer; }
.link:hover { text-decoration: underline; }
.danger-text { color: var(--danger); }
.ok-text { color: var(--ok); }
.warn-text { color: var(--warn); }

.row { display: flex; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.row.top { align-items: flex-start; }
.col { display: flex; flex-direction: column; }
.gap { gap: 8px; }
.gap-lg { gap: 16px; }
.grow { flex: 1 1 auto; min-width: 0; }

/* 导入预览的行状态配色（新增 / 重复跳过 / 重复更新 / 错误） */
.row-new td { border-left: 3px solid transparent; }
.row-dup_skip td { color: var(--muted); }
.row-dup_update td { background: #fff4e5 !important; color: var(--warn); }
.row-error td { background: #fdecea !important; color: var(--danger); }
.row-off td { opacity: .45; text-decoration: line-through; }

/* 窄屏兜底：这是桌面工具，但笔记本 13 寸也要能用 —— 侧栏收成 PortPro 默认的 70px 图标栏 */
@media (max-width: 1000px) {
  .topbar { padding: 0 16px; }
  .main { padding-left: 12px; padding-right: 12px; padding-bottom: 30px; }
}

/* 打印：界面外壳一律不打（发票打印走 /api/invoices/:id/print 独立页面） */
@media print {
  .sidebar, .topbar, .toolbar, .pager, .progress, .toasts, .btn, .page-head .btn, .help-tip-wrap { display: none !important; }
  .main { margin: 0; padding: 0; }
  .card, .table-wrap { border: 0; box-shadow: none; }
  body { background: #fff; color: #000; }
}

/* =========================================================================
   车费记录页补充 —— 上面那套类没有覆盖到的三处：
   提示条、行内展开的费用明细、弹窗里的费用行编辑器。
   ========================================================================= */

/* 提示条：弹窗顶部「该单已开票」这类说明，比 toast 更持久，用户看完还在 */
.callout {
  display: flex;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  font-size: 12.5px;
  line-height: 1.55;
}
.callout-warn { border-color: var(--warn-bg); background: #fff4e5; color: var(--warn); }
.callout-info { border-color: var(--accent); background: var(--accent-soft); color: var(--fg-soft); }

/* 展开箭头占固定宽度：展开/收起时后面的 master number 不会左右跳 */
.caret { display: inline-block; width: 11px; margin-right: 4px; color: var(--muted); font-size: 9px; }

/* 行内展开的费用明细（也被批量开票弹窗的分组预览复用） */
.detail-box { display: flex; flex-wrap: wrap; gap: 10px 30px; }
.detail-title { margin-bottom: 4px; font-size: 11.5px; font-weight: 600; color: var(--muted); }
.detail-charges { border-collapse: collapse; font-size: 12.5px; }
.detail-charges th, .detail-charges td { padding: 2px 16px 2px 0; border: 0; text-align: left; white-space: nowrap; }
.detail-charges th { font-size: 11.5px; font-weight: 600; color: var(--muted); }
.detail-charges tfoot td { padding-top: 4px; border-top: 1px solid var(--border); font-weight: 600; }
.detail-note { max-width: 520px; white-space: pre-wrap; word-break: break-word; }

/* 弹窗里的费用行：名称自适应，数量/单价/金额定宽，删除按钮贴右 */
.charge-head, .charge-row { display: grid; grid-template-columns: minmax(110px, 1fr) 74px 96px 104px 26px; gap: 6px; align-items: center; }
.charge-head { margin-bottom: 4px; font-size: 11.5px; color: var(--muted); }
.charge-row { margin-bottom: 6px; }
.charge-row .btn { width: 26px; padding: 0; }
.charge-total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  font-weight: 600;
}

/* 批量开票弹窗：客户分组多了也不把弹窗撑长 */
.group-list { max-height: 190px; overflow-y: auto; }

/* 车费记录的合计行钉在表格底部：往下翻 50 行也一直看得见「当前筛选合计」 */
.table-scroll tfoot td { position: sticky; bottom: 0; z-index: 2; }

/* =========================================================================
   客户页 / 设置页补充 —— 上面那套类没有覆盖到的三处：
   费率弹窗里「继承自通用费率」的行、设置页的左表单右预览、发票纸预览。
   ========================================================================= */

/* 通用费率行整行灰掉，和客户自己的专属费率一眼分开
   （选择器带上 .table tbody 才压得过斑马纹那条规则） */
.table tbody tr.rate-inherited td { background: var(--panel-2); color: var(--muted); }

/* 设置页：左边填表、右边看效果；窄屏自动叠成一列 */
.settings-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(230px, 300px);
  gap: 18px;
  align-items: start;
}
@media (max-width: 900px) { .settings-cols { grid-template-columns: 1fr; } }

/* 发票纸预览：固定白底黑字，不跟界面主题变色 —— 它模拟的是打印出来的那张纸 */
.paper-preview {
  background: #fff;
  color: #1b2430;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 14px;
  font-size: 12px;
  line-height: 1.6;
  box-shadow: var(--shadow);
}
.paper-preview .pp-name { font-size: 16px; font-weight: 700; letter-spacing: .2px; }
.paper-preview .pp-line { color: #4a5560; white-space: pre-wrap; word-break: break-word; }
.paper-preview .pp-label { margin-bottom: 2px; font-size: 10.5px; letter-spacing: .6px; text-transform: uppercase; color: #8a95a1; }
.paper-preview .pp-no { font-family: ui-monospace, Consolas, monospace; font-size: 15px; font-weight: 700; word-break: break-all; }
.paper-preview .pp-sep { border-top: 1px solid #e3e7ec; margin: 10px 0; }
.paper-preview .pp-empty { color: #a4adb7; font-weight: 400; }

/* =========================================================================
   车费记录弹窗 v2 —— 按用户那张订单表分区（基本信息 / 地址 / 日期 / 费用明细）。
   用户是照着 Excel 一列一列往下抄的，界面顺序和分区跟表一致，才不会漏字段。
   ========================================================================= */

/* 分区：一条细线 + 一个小标题，比一堆 fieldset 边框安静，长表单也不显得吵 */
.form-section { margin-bottom: 14px; padding-top: 10px; border-top: 1px solid var(--border); }
.form-section:first-child { padding-top: 0; border-top: 0; }
.form-section-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.form-section-title { font-size: 12.5px; font-weight: 600; color: var(--fg-soft); }

/* 三列表单：基本信息 9~10 个字段，两列要拉出五行，弹窗会长得看不见「保存」 */
.form-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 14px; }
.form-grid-3 .full { grid-column: 1 / -1; }
@media (max-width: 760px) { .form-grid-3 { grid-template-columns: 1fr 1fr; } }

/* 车架费天数：这是用户和卡车行对账时的唯一依据，必须一眼看见，所以给它一个高亮块。
   算式（Out 8/13 → Empty In 8/16，两头都算）要和数字并排显示 —— 只给数字，
   对方问「为什么是 4 天」时用户答不上来。 */
.day-box {
  padding: 7px 10px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  font-size: 12.5px;
  line-height: 1.7;
}
.day-box:empty { display: none; }
.day-line { white-space: normal; }
.day-num { font-size: 14px; }

/* 六列费用行（Date / Service / Description / Qty / Rate / Amount + 删除），
   列名和发票明细一一对应。窄屏放不下时整块横向滚动，不压缩输入框宽度。 */
.charge-scroll { overflow-x: auto; }
.charge-head.charge-row-6, .charge-row.charge-row-6 {
  display: grid;
  grid-template-columns: 132px minmax(120px, 1fr) minmax(150px, 1.3fr) 58px 80px 92px 26px;
  gap: 6px;
  align-items: center;
  min-width: 690px;
}

/* 收入行多一列「结给」：这一单的几笔费用常常分属不同的对家
   （drayage 结给卡车行、pierpass 结给码头、ctf 结给清洁公司）。
   整行已经很宽，所以外面套了 .charge-scroll 横向滚动，而不是把每一列再压窄——
   压窄的结果是 Description 只剩三个字，用户得逐个悬停才知道自己填了什么。 */
.charge-head.charge-row-7, .charge-row.charge-row-7 {
  display: grid;
  grid-template-columns: 132px minmax(120px, 1fr) minmax(140px, 1.2fr) 58px 80px 92px minmax(120px, 0.9fr) 26px;
  gap: 6px;
  align-items: center;
  min-width: 830px;
}

/* =========================================================================
   运输订单弹窗 v3 —— 三个 tab（基础信息 / 成本信息 / 收入信息）+ 常驻利润条。
   成本与收入是两套完全不同的数据，混在一张长表单里用户会填串；
   利润条则必须永远在视线里 —— 录单时就看得见这一单赚不赚钱。
   ========================================================================= */

/* 利润条：弹窗正文会滚动，所以钉在顶部。负的上/左/右外边距抵掉 .modal-body 的 14px 内边距 ——
   这样它一开始就贴在滚动区顶端（相当于永远处于「已吸顶」状态），滚动时不会先跳一下再吸住。
   底色必须是不透明的，否则滚过去的正文会从它底下透出来。 */
.profit-bar {
  position: sticky;
  top: 0;
  z-index: 3;
  margin: -14px -14px 12px;
  padding: 12px 14px 10px;
  border-width: 0 0 1px;
  border-radius: 0;
  background: var(--panel-2);
}
.profit-bar .pf-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; }
.profit-bar .pf { display: flex; align-items: baseline; gap: 6px; }
.profit-bar .pf-label { font-size: 11px; letter-spacing: .08em; color: var(--muted); }
.profit-bar .pf-val { font-size: 14px; font-weight: 600; }
.profit-bar .pf-big { font-size: 16px; }
.profit-bar .pf-op { color: var(--muted); font-size: 14px; }

/* 成本行只有四列（Service / Qty / Rate / Amount + 删除）：
   Date 与 Description 是印给客户看的，卡车行的账单上没有对应物，留着只会让人以为漏填了。 */
.charge-head.charge-row-4, .charge-row.charge-row-4 {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) 58px 80px 92px 26px;
  gap: 6px;
  align-items: center;
  min-width: 420px;
}
/* 成本行带 Description（2026-09-17：供应商每一项费用边上可以有 Description，印在付款通知上）：
   Service / Description / Qty / Rate / Amount + 删除 */
.charge-head.charge-row-5, .charge-row.charge-row-5 {
  display: grid;
  grid-template-columns: minmax(130px, 1fr) minmax(140px, 1.2fr) 58px 80px 92px 26px;
  gap: 6px;
  align-items: center;
  min-width: 580px;
}

/* 设置 → 费用项目：Description 和发票上一样放在费用名下面（同一格），不单开一列 ——
   九列挤在一行时 Description 只剩八十几像素，一句话看不全；固定表格布局让各列按给的宽度走，不被表头挤扁 */
.table.fee-table { table-layout: fixed; min-width: 980px; }
/* .table 的格子默认不换行：两个 100% 宽的输入框会并排挤出格子，所以这里放开换行、Description 单独占一行 */
.table.fee-table td { overflow: visible; vertical-align: top; white-space: normal; }
.table.fee-table td .fee-desc { display: block; margin-top: 4px; }

/* 利润条第二行：柜号 + 四个关键日期（成本 / 收入两个 tab 才出，对费用用；值只取订单上的日期）。
   放在吸顶的利润条里，是因为成本分组一长就滚走了，而对费用时要一直看得见这几个日期 */
.profit-bar .key-dates { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--border); font-size: 12.5px; }
.profit-bar .key-dates .kd { display: inline-flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.profit-bar .key-dates .kd-label { font-size: 11px; color: var(--muted); }
.profit-bar .key-dates .kd-val { font-weight: 600; font-variant-numeric: tabular-nums; }
.profit-bar .key-dates .kd-val.is-empty { font-weight: 400; color: var(--muted); }
.profit-bar .key-dates .copy-btn { align-self: center; }
/* 利润条第一行：这是哪一单（MBL · 客户 · 卡车公司 · 进度 / 扣 · 同 MBL 其他柜），三个 tab 都常驻（2026-10-02 升级指南第 4 期）。
   和 key-dates 同一套小格子；同 MBL 其他柜的柜号是按钮，长得像链接，点了开那张单 */
.profit-bar .key-ident { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; margin-bottom: 6px; padding-bottom: 6px; border-bottom: 1px dashed var(--border); font-size: 12.5px; }
.profit-bar .key-ident .kd { display: inline-flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.profit-bar .key-ident .kd-label { font-size: 11px; color: var(--muted); }
.profit-bar .key-ident .kd-val { font-weight: 600; }
.profit-bar .key-ident .kd-val.is-empty { font-weight: 400; color: var(--muted); }
.profit-bar .key-ident .tag { align-self: center; }
.link-btn { border: 0; padding: 0; background: transparent; color: var(--accent); font: inherit; cursor: pointer; }
.link-btn:hover { text-decoration: underline; }
/* 表格里一行字的主题做成可点（点开看正文）：照旧一行、超长省略 */
.link-btn.ellipsis { display: block; max-width: 100%; text-align: left; }

/* 点开看一封信（mailView.js，2026-10-07）：上面信头一格一格，下面正文按纯文本原样换行 */
.mail-view-head { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 4px 10px; font-size: 12.5px; margin-bottom: 10px; }
.mail-view-k { color: var(--muted); }
.mail-view-v { word-break: break-word; }
.mail-view-body { margin: 0; padding: 10px 12px; max-height: 55vh; overflow: auto; white-space: pre-wrap; word-break: break-word;
  font-family: inherit; font-size: 13px; line-height: 1.6; background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; color: var(--fg); }
.mail-view-body mark { background: var(--accent-soft); color: inherit; font-weight: 600; border-radius: 2px; padding: 0 1px; }
.mail-view-empty { padding: 10px 0; }

/* 发票详情 · 费用明细里的柜号和 MBL（2026-09-20 用户：「开发票加上MBL号码」「MBL号码在发票界面也要显示」）。
   两个号写在同一行（柜号 · MBL），和纸上那一行（invoicePrint 的 .line-ids / .line-cont）是同一个样子：
   屏幕上这张表就是照着纸排的，看完直接打印，不用在脑子里做一次换算。
   **不给 MBL 单开一列**：Description 本来就挤，再加一列会把长说明挤成两行 —— 用户选的也是「柜号旁边、同一行」。 */
.inv-line-ids { display: block; margin-bottom: 2px; font-weight: 600; }
.inv-line-ids .copy-btn { font-weight: 400; }

/* =========================================================================
   开票预览 · 所见即所得（2026-09-17 用户：「发票的修改都是所见即所得的方式」）
   左边一列这批发票，右边就是那张纸：点纸上的字直接改，改完自动重新生成。
   纸在 sandbox="allow-same-origin" 的 iframe 里（纸自己的脚本一行都不跑，编辑由外面这页驱动）。
   ========================================================================= */
.modal-body.is-tall { max-height: 82vh; }
.inv-preview { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 12px; align-items: start; }
.inv-preview-list { max-height: calc(82vh - 120px); overflow-y: auto; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); }
.inv-preview-item { padding: 7px 10px; border-bottom: 1px solid var(--border); cursor: pointer; font-size: 12.5px; line-height: 1.45; }
.inv-preview-item:last-child { border-bottom: 0; }
.inv-preview-item:hover { background: var(--row-hover); }
.inv-preview-item.is-on { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.inv-preview-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.inv-preview-item .ipi-no { font-weight: 600; }
.inv-preview-bar .paper-cue-jobs { white-space: nowrap; }
.inv-preview-item .ipi-sub { color: var(--muted); font-size: 11.5px; }
.inv-preview-item .ipi-amt { float: right; font-weight: 600; font-variant-numeric: tabular-nums; }
.inv-preview-paper { position: relative; min-width: 0; }
.inv-preview-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-bottom: 8px; }
.inv-paper-frame { display: block; width: 100%; height: calc(82vh - 150px); min-height: 420px; border: 1px solid var(--border); border-radius: 6px; background: #fff; }
.inv-paper-frame.is-short { height: 70vh; }
.inv-preview-paper.is-busy .inv-paper-frame { opacity: .55; transition: opacity .1s; }
.inv-preview-paper .busy-tag { display: none; position: absolute; top: 44px; right: 14px; padding: 3px 8px; border-radius: 4px; background: var(--accent); color: var(--accent-fg); font-size: 12px; }
.inv-preview-paper.is-busy .busy-tag { display: block; }
/* 纸上点 Service 时弹出的小下拉（换费用项）：fixed 定位，按点的那一格摆 */
.inv-edit-pop { position: fixed; z-index: 96; padding: 6px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--panel); box-shadow: var(--shadow-lg); }
.inv-edit-pop .select { min-width: 220px; height: auto; padding: 4px; }
@media (max-width: 900px) {
  .inv-preview { grid-template-columns: 1fr; }
  .inv-preview-list { max-height: 150px; }
}

/* =========================================================================
   仪表盘经营看板 —— 利润趋势图与亏损行。
   图表是内联 SVG 手画的（零依赖硬约束，不许引图表库），所以这几条样式没法复用现成的类。
   ========================================================================= */

/* 悬停读数框要相对图表定位，容器必须是定位上下文 */
.chart-wrap { position: relative; }
.chart-svg { display: block; width: 100%; height: auto; }

/* 命中区画在柱子底层、铺满整列：柱子再细也能稳稳指中当天（1px 宽的柱子谁都点不准）。
   柱子本身不吃鼠标事件，否则鼠标压在柱子上时底层的命中区就收不到 hover 了。 */
.chart-hit { fill: transparent; }
.chart-hit:hover { fill: var(--accent-soft); }
.chart-bar { pointer-events: none; }

.chart-tip {
  position: absolute;
  z-index: 2;
  top: 6px;
  left: 0;
  min-width: 168px;
  padding: 6px 9px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--panel);
  box-shadow: var(--shadow-lg);
  font-size: 11.5px;
  line-height: 1.65;
  white-space: nowrap;
  /* 不吃鼠标事件：否则读数框会挡住底下的命中区，鼠标一移上去提示就自己消失了 */
  pointer-events: none;
  opacity: 0;
  transition: opacity .1s ease;
}
.chart-tip.on { opacity: 1; }
.chart-tip .ct-date { font-weight: 600; margin-bottom: 2px; }
.chart-tip .ct-row { display: flex; justify-content: space-between; gap: 18px; }

/* 亏损行整行标红 + 行首一道红边：客户表里「哪家在亏钱」必须扫一眼就看见，
   只把利润那一格标红的话，一屏十几行时根本注意不到。 */
.table tbody tr.row-loss td { color: var(--danger); }
.table tbody tr.row-loss td:first-child { box-shadow: inset 2px 0 0 var(--danger); }

/* 运输订单：到港 / 提柜进度 ------------------------------------------------ */
/* LFD 快到（含今天、已过）的行：左边一道红；LFD 那格日期标红加粗 */
.table tbody tr.lfd-alert td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.table td .lfd-hot .cell-val, .table td .lfd-hot .lfd-date { color: var(--danger); font-weight: 700; }
.lfd-tip { margin-left: 4px; font-size: 11px; white-space: nowrap; }
.lfd-tip.lfd-soon { color: var(--danger); font-weight: 700; }
.lfd-tip.lfd-over { color: #fff; background: var(--danger); font-weight: 700; padding: 0 5px; border-radius: 8px; }
.lfd-tip.appt-need { color: var(--warn); font-weight: 600; }
.table td input.cell-date { min-width: 128px; }
/* 进度筛选按钮照 PortPro 看板上「Available 4」：文字 + 灰色计数药丸；选中 = 浅蓝底 + 蓝边 */
.port-chip { gap: 6px; }
.port-chip .port-chip-n { font-variant-numeric: tabular-nums; min-width: 20px; padding: 0 6px; border-radius: 22px; background: var(--gray-100); color: var(--fg); font-size: 11px; font-weight: 600; line-height: 17px; }
.port-chip.chip-warn { color: var(--warn); }
.port-chip.chip-danger { color: var(--danger); }
.port-chip.is-on { background: var(--accent-soft); border-color: var(--accent); }
.port-chip.is-on .port-chip-n { background: var(--accent); color: #fff; }
.port-chip.is-zero { opacity: .55; }

/* 物流跟踪页的进度多选：复用运输订单页那套 .port-chip，补两个颜色 */
.port-chip.chip-ok { color: var(--ok, #16a34a); }
.port-chip.chip-muted { color: var(--muted); }

/* 柜子进度（运输订单 / 物流跟踪 / 进度面板 stagePanel.js 共用） -------------------- */
/* 进度小标签：到码头没提 = 该去提了（橙），空柜已归还 = 这一票走完了（绿），状态未知 = 灰 */
.stage-tag { font-family: inherit; font-weight: 500; }
/* 该派车的两步：可以提柜、仓库卸完等提空柜 */
.stage-tag.stage-ready_pickup, .stage-tag.stage-empty_ready { color: var(--warn); }
.stage-tag.stage-empty_in { color: var(--ok); }
.stage-tag.stage-unknown { color: var(--muted); }
.stage-tag-lg { font-size: 14px; line-height: 22px; padding: 1px 10px; font-weight: 600; }
/* 表格里那一格：长得像标签，其实是按钮（点开面板）；行点击展开明细本来就绕开按钮 */
.stage-btn {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 0; border: 0; background: transparent; color: inherit; font: inherit;
  cursor: pointer; white-space: nowrap;
}
.stage-btn:hover .stage-tag { border-color: var(--accent); }
.stage-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 4px; }
/* 「人工」小记号：人工确认的进度一眼和自动算的分开；已被新消息接上的灰掉 */
.stage-manual { font-size: 10.5px; line-height: 15px; padding: 0 4px; border-radius: 3px; background: var(--accent-soft); color: var(--accent); white-space: nowrap; }
.stage-manual.is-old { background: var(--panel-2); color: var(--muted); }
.stage-count { font-size: 11px; white-space: nowrap; }
.stage-bar { margin-top: -4px; }
/* 码头说的扣 / 能不能提（core.holdTags；运输订单、物流跟踪、调度看板、查码头结果共用）：
   扣 = 红底白字（要人去处理）；只有码头费那一类 = 橙（我们付掉就行）；可提绿、不能提橙 */
.tag.hold-tag { border-color: var(--danger); background: var(--danger); color: #fff; font-weight: 700; cursor: help; }
.tag.hold-tag.is-ours { border-color: var(--warn); background: transparent; color: var(--warn); }
.tag.pick-tag { background: transparent; font-weight: 600; cursor: help; }
.tag.pick-tag.is-yes { border-color: var(--ok); color: var(--ok); }
.tag.pick-tag.is-no { border-color: var(--warn); color: var(--warn); }
/* 运输订单列表 MBL 格上的两个小按钮（2026-10-02 第 4 期）：「+N 柜」= 同 MBL 还有别的柜，点了只看这一票；📎 = 有文件，点了开到「附件」tab */
.tag.sib-tag, .tag.files-tag { background: transparent; cursor: pointer; font: inherit; font-size: 11px; line-height: 16px; margin-left: 4px; }
.tag.sib-tag { border-color: var(--accent); color: var(--accent); }
.tag.sib-tag:hover { background: var(--accent-soft); }
.tag.files-tag { border-color: var(--border); padding: 0 3px; font-size: 13px; }
.tag.files-tag:hover { background: var(--panel-2); }
/* 运输订单列表「送货提醒」那一格里的「该发」：长得像小标签，其实是按钮（点开就是发信确认框）；加急的红底 */
.appt-due-btn {
  padding: 0 7px; border: 1px solid var(--warn); border-radius: 4px; background: transparent; color: var(--warn);
  font: inherit; font-size: 11.5px; font-weight: 600; line-height: 18px; white-space: nowrap; cursor: pointer;
}
.appt-due-btn:hover { box-shadow: inset 0 0 0 1px currentColor; }
.appt-due-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.appt-due-btn.is-urgent { border-color: var(--danger); background: var(--danger); color: #fff; }
/* 面板里的时间线：一步一组，人工改进度的记录一道灰边，进度备注一道蓝边 */
.stage-group { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.stage-group-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; font-size: 12px; }
.stage-log { margin: 4px 0; padding: 2px 0 2px 10px; border-left: 2px solid var(--border-strong); font-size: 12.5px; }
.stage-note { margin: 6px 0; padding: 4px 0 4px 10px; border-left: 2px solid var(--accent); }
.stage-note-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 4px; font-size: 11.5px; }
.stage-note-foot .select.stage-move { width: auto; min-width: 0; height: 24px; padding: 0 4px; font-size: 12px; }
.stage-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.stage-thumb { position: relative; display: inline-block; }
.stage-thumb img {
  display: block; width: auto; height: auto; max-height: 96px; max-width: 180px; object-fit: contain;
  border: 1px solid var(--border); border-radius: 4px; background: var(--panel-2);
}
.stage-thumb-open { display: block; padding: 0; border: 0; background: transparent; cursor: zoom-in; }
.stage-thumb-x {
  position: absolute; top: -6px; right: -6px; width: 18px; height: 18px; padding: 0;
  border: 1px solid var(--border-strong); border-radius: 50%; background: var(--panel);
  color: var(--danger); font-size: 13px; line-height: 15px; cursor: pointer;
}
.stage-thumb-x:disabled { opacity: .5; cursor: not-allowed; }
/* 记备注那一块钉在面板底部：时间线再长，写字 / 贴图的地方也一直看得见。
   负外边距抵掉 .modal-body 的 14px 内边距（和运输订单弹窗的利润条同一个办法）；底色不透明，滚过去的时间线不会透出来 */
.stage-composer {
  position: sticky; bottom: 0; z-index: 3;
  margin: 12px -14px -14px; padding: 10px 14px;
  border-top: 1px solid var(--border); background: var(--panel-2);
}
.stage-composer .textarea { margin-top: 6px; min-height: 58px; }
.stage-composer.is-drag { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-soft); }
.stage-lightbox { text-align: center; }
.stage-lightbox img { max-width: 100%; max-height: 64vh; object-fit: contain; }
.stage-brief-note { margin-top: 4px; font-size: 12px; white-space: normal; word-break: break-word; }

/* =========================================================================
   调度仪表盘（#/dispatch）—— 六块 + 可以直接复制出去的调度文案 + 截图留痕。
   类名一律 disp- 前缀：这一页的东西不许漏到别的页面上去。
   ========================================================================= */

/* 页头下面的两组时间：数据截至 / 上次一键刷新，LA 和北京各占一行
   （一行里塞两个时区的时间，读的人要先分清哪个是哪个才看得下去） */
.disp-times { display: flex; flex-wrap: wrap; gap: 8px 28px; margin-bottom: 12px; font-size: 12px; font-variant-numeric: tabular-nums; }
.disp-time-col { line-height: 1.55; }
.disp-time-label { font-size: 11px; letter-spacing: .06em; }

/* 跑着的时候顶上那条：秒数一直在走，用户才知道它在干活而不是卡死了 */
.disp-busy { align-items: flex-start; }

/* 一家车队 / 一个客户一块 */
.disp-group { padding: 12px 14px; }
.disp-group-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 12px; margin-bottom: 8px; font-size: 13.5px; }
.disp-group .table-wrap { box-shadow: none; }

/* 空柜按 DL 送货地址分堆：地址是这一堆的小标题 */
.disp-addr { margin-top: 10px; }
.disp-addr:first-of-type { margin-top: 4px; }
.disp-addr-head { margin-bottom: 4px; font-size: 12px; font-weight: 600; color: var(--muted); }

/* 只读文案框：照抄 tracking.js 那套 textarea.input.mono，宽度撑满、能手动拉高 */
.disp-text { margin-top: 10px; }
.disp-text-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; font-size: 11.5px; }
.disp-textarea {
  width: 100%;
  height: auto;
  min-height: 86px;
  resize: vertical;
  font-size: 12.5px;
  line-height: 1.6;
  white-space: pre;            /* 文案自己排好了行，别再替它折 */
}

/* 客户那一块里的柜号小块（不铺四张表，太长了） */
.disp-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 6px; font-size: 12px; }
.disp-chips-label { min-width: 104px; font-size: 11.5px; }
.disp-chip { font-size: 11.5px; }

/* 「要注意的 / 问题 / 扣」那几列：一条一行，挤成一行就没人读了 */
.disp-prob { font-size: 12px; line-height: 1.55; white-space: normal; }
/* 第 1 块那一行里的「另有 N 份 DO 待核对」+ 去核对按钮：一句话带一个小按钮，别被挤散 */
.disp-docs-pending { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.disp-docs-pending .btn { height: 22px; padding: 0 8px; font-size: 12px; }

/* 码头说可以提了 —— 这一页唯一的好消息，给它一个颜色 */
.disp-yes { color: var(--ok); font-weight: 600; }

/* LFD 还剩几天（和运输订单页的 .lfd-tip 同一套说法，换个前缀免得串样式） */
.disp-lfd { margin-left: 4px; font-size: 11px; white-space: nowrap; }
.disp-lfd.is-soon { color: var(--danger); font-weight: 700; }
.disp-lfd.is-over { padding: 0 5px; border-radius: 8px; background: var(--danger); color: #fff; font-weight: 700; }

/* 行的两种状态（选择器带上 .table tbody 才压得过斑马纹那条规则）：
   要办的事标出来，已经办完的灰掉但不删——看板要给全貌 */
.table tbody tr.disp-row-hot td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
.table tbody tr.disp-row-done td { color: var(--muted); }
/* 第 4 块里派车单已经发过的行（后端 dispatched_at，2026-10-02 第 5 期）：灰一点，和约到号的一样不抢眼 */
.table tbody tr.disp-row-dispatched td { color: var(--muted); }
/* 运输订单列表「卡车公司」格后的「已派 M/D」：绿边小标签；没发成的橙 */
.tag.dispatched-tag { border-color: var(--ok); color: var(--ok); background: transparent; font-size: 11px; margin-left: 4px; cursor: help; }
.tag.dispatched-tag.is-failed { border-color: var(--warn); color: var(--warn); }

/* 「还柜码头」这一格：长得像文字，点下去是输入框 */
.disp-edit { cursor: pointer; border-bottom: 1px dashed var(--border-strong); }
.disp-edit:hover { color: var(--accent); border-bottom-color: var(--accent); }
.disp-edit.is-default { color: var(--muted); }
.disp-edit:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* 点开之后是个下拉（从码头名录里选，不给自由输入框：这一格的字会原样印进发给车队的文案）。
   码头名字长，宽度给够，别让「Fenix Marine（Pier 300 / APL）」截成一半 */
.table td .select.disp-term-sel { min-width: 200px; height: 26px; padding: 0 4px; font-size: 12.5px; }

/* 截图留痕：缩略图一排，图下两行是 LA 和北京时间 */
.disp-shots { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.disp-shot { margin: 0; width: 176px; }
.disp-shot-open { display: block; padding: 0; border: 0; background: transparent; cursor: zoom-in; }
.disp-shot-open img {
  display: block; width: 100%; height: auto; max-height: 116px; object-fit: cover; object-position: top left;
  border: 1px solid var(--border); border-radius: 4px; background: var(--panel-2);
}
.disp-shot-open:focus-visible img { outline: 2px solid var(--accent); outline-offset: 1px; }
.disp-shot-cap { margin-top: 3px; font-size: 10.5px; line-height: 1.5; color: var(--muted); font-variant-numeric: tabular-nums; }
.disp-shot-what { color: var(--fg-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 一趟刷新里某一步的状态：跑完 / 跳过 / 出错 / 要人处理 —— 这是事实，常显，不进 ⓘ */
.disp-step { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 12px; }
.disp-step-badge { padding: 0 6px; border-radius: 9px; font-size: 11.5px; font-weight: 600; white-space: nowrap; background: var(--panel-2); color: var(--muted); }
.disp-step-badge.is-ok { background: var(--badge-paid-bg); color: var(--badge-paid-fg); }
.disp-step-badge.is-skip { background: var(--panel-2); color: var(--muted); }
.disp-step-badge.is-error { background: var(--badge-overdue-bg); color: var(--badge-overdue-fg); }
.disp-step-badge.is-challenge { background: var(--badge-pending-bg); color: var(--badge-pending-fg); }

/* 大图 */
.disp-lightbox { text-align: center; }
.disp-lightbox img { max-width: 100%; max-height: 64vh; object-fit: contain; }

/* 调度仪表盘：报空超过 14 天的那一折叠块（默认收起，点开才看细节） */
.disp-stale { margin-top: 12px; }
.disp-stale > summary { cursor: pointer; padding: 4px 0; list-style: none; }
.disp-stale > summary::-webkit-details-marker { display: none; }
.disp-stale > summary::before { content: '▸ '; color: var(--muted, #888); }
.disp-stale[open] > summary::before { content: '▾ '; }

/* 运输订单页的「批量查询」结果条：没找到的那几个号码列在下面，一个一块 */
.bulk-bar .bulk-miss {
  display: inline-block; margin: 2px 6px 2px 0; padding: 1px 6px;
  border: 1px solid var(--border); border-radius: 4px;
  background: var(--card, #fff); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* 订单附件 / 邮件里的费用凭证（2026-09-24）：缩略图一格一个，点缩略图放大（图片看图器、PDF 新标签页） */
.att-grid { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.att-tile { position: relative; width: 132px; }
.att-tile.is-off .att-thumb { opacity: .45; }
.att-thumb {
  display: flex; align-items: center; justify-content: center; width: 132px; height: 96px; padding: 0;
  border: 1px solid var(--border); border-radius: 6px; background: var(--panel-2); cursor: zoom-in; overflow: hidden;
}
.att-thumb img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.att-thumb:hover { border-color: var(--border-strong); }
.att-thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.att-pdf { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.att-pdf-tag { padding: 3px 9px; border-radius: 4px; background: var(--danger); color: #fff; font: 700 12px/1.2 system-ui, sans-serif; letter-spacing: .06em; }
.att-pdf-hint { font-size: 10.5px; color: var(--muted); }
.att-remove {
  position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; padding: 0; line-height: 20px;
  border: 1px solid var(--border-strong); border-radius: 11px; background: var(--panel, #fff); color: var(--danger); cursor: pointer; font-size: 14px;
}
.att-cap { margin-top: 4px; font-size: 11.5px; line-height: 1.45; }
.att-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-check { display: flex; align-items: center; gap: 4px; cursor: pointer; min-width: 0; }
.att-check .att-name { flex: 1 1 auto; min-width: 0; }
.att-meta { color: var(--muted); font-size: 10.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-viewer { text-align: center; }
.att-viewer img { max-width: 100%; max-height: 66vh; object-fit: contain; }
.att-sum { font-size: 12px; margin-top: 6px; }

/* POD（签收单，2026-10-15）：状态小标、原图那张卡（红字「原图 · 不发给客户」）、确认框的大图、人工框选的框 */
.tag.pod-chip { background: transparent; font-size: 11px; }
.tag.pod-chip.is-ready { border-color: var(--ok); color: var(--ok); }
.tag.pod-chip.is-wait { border-color: var(--warn); color: var(--warn); }
.tag.pod-chip.is-bad { border-color: var(--danger); color: var(--danger); }
.tag.pod-chip.is-busy { border-color: var(--accent); color: var(--accent); }
.tag.pod-chip.is-off { color: var(--muted); text-decoration: line-through; }
.tag.pod-auto-tag { border-color: var(--accent); color: var(--accent); background: transparent; font-size: 10.5px; }
.pod-badges { display: inline-flex; gap: 4px; margin-left: 6px; vertical-align: middle; }
.pod-raw-label { color: var(--danger); font-weight: 600; font-size: 11px; white-space: nowrap; }
.pod-card { display: flex; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.pod-card.is-rejected { opacity: .6; }
.pod-card-files { display: flex; gap: 8px; flex: 0 0 auto; }
.pod-card-info { flex: 1 1 auto; min-width: 0; }
.pod-reason { font-size: 12px; margin-top: 2px; white-space: pre-wrap; }
.pod-head { padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-2); }
.pod-confirm-img { display: block; margin: 8px 0; text-align: center; }
.pod-confirm-img img { max-width: 100%; max-height: 62vh; object-fit: contain; border: 1px solid var(--border); background: #fff; }
.pod-confirm-facts { font-size: 13px; margin-bottom: 4px; }
.pod-confirm-warns { font-size: 12.5px; margin-bottom: 4px; }
.pod-check-line { display: flex; align-items: center; gap: 6px; margin-top: 6px; font-weight: 600; cursor: pointer; }
.pod-frame-wrap { display: inline-block; max-width: 100%; user-select: none; touch-action: none; cursor: crosshair; line-height: 0; }
.pod-frame-img { display: block; max-width: 100%; max-height: 64vh; pointer-events: none; }
.pod-frame-layer { position: absolute; inset: 0; }
.pod-rect { position: absolute; box-sizing: border-box; }
.pod-rect.is-crop { border: 2px solid var(--accent); box-shadow: 0 0 0 9999px rgba(0, 0, 0, .35); }
.pod-rect.is-blank { background: rgba(255, 255, 255, .85); border: 1px dashed var(--danger); }
.pod-rect.is-drawing { border: 2px dashed var(--warn); }
.pod-rect-x {
  position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; padding: 0; line-height: 16px;
  border: 1px solid var(--danger); border-radius: 9px; background: #fff; color: var(--danger); cursor: pointer; font-size: 12px;
}
.pod-result-img { max-width: 100%; max-height: 60vh; object-fit: contain; border: 1px solid var(--border); background: #fff; }
.pod-group { margin-top: 8px; }
.pod-group-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 4px; font-size: 12.5px; }
.pod-status { font-size: 12.5px; margin-bottom: 8px; line-height: 1.7; }
.pod-pending-row { padding: 8px 0; border-bottom: 1px solid var(--border); }
.pod-pair { display: flex; gap: 8px; align-items: flex-start; margin-top: 6px; }
.pod-pair-act { display: flex; flex-direction: column; gap: 4px; }
.pod-rcpt-row { padding: 4px 0; border-bottom: 1px dashed var(--border); }
.pod-pick-row { padding: 3px 0; }
.inv-pod-line:empty { display: none; }

/* ---------- 运营仪表盘（ops.js）：「一目了然」那几句话，好的绿边、要注意的红边、其余灰边 ---------- */
.ops-insights { display: grid; gap: 6px; }
.ops-insight {
  display: flex; gap: 8px; align-items: flex-start;
  background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--border);
  border-radius: var(--radius); padding: 8px 12px; line-height: 1.55;
}
.ops-insight.is-good { border-left-color: var(--ok); }
.ops-insight.is-bad { border-left-color: var(--danger); }
.ops-insight.is-tip { border-left-color: var(--accent); }
.ops-insight-icon { flex: none; width: 1.2em; text-align: center; }
/* 逐笔核对（opsOrders.js）：展开的那一行里按费用大类的小表别撑满整行 */
.ops-order-detail .table-wrap { max-width: 560px; box-shadow: none; }

/* ---------- 运营仪表盘趋势图（opsChart.js）：一家客户一个颜色 ----------
   八个颜色按固定顺序占槽位（勾选先后），不按排名重排；白底 / 深色面板各一套，2026-10-22 跑过 dataviz 校验
   （散点只用前 3 个：所有两两都分得清；折线 8 个：相邻的分得清）。文字不用这些颜色，只给点 / 线 / 图例小记号 */
:root {
  --series-1: #2a78d6; --series-2: #eb6834; --series-3: #1baf7a; --series-4: #eda100;
  --series-5: #e87ba4; --series-6: #008300; --series-7: #6250d6; --series-8: #e34948;
}
@media (prefers-color-scheme: dark) {
  :root {
    --series-1: #3987e5; --series-2: #d95926; --series-3: #199e70; --series-4: #c98500;
    --series-5: #d55181; --series-6: #008300; --series-7: #9085e9; --series-8: #e66767;
  }
}
.ops-chart .ops-grid { stroke: var(--border); stroke-width: 1; }
.ops-chart .ops-axis-zero { stroke: var(--border-strong); stroke-width: 1; }
.ops-chart .ops-tick { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.ops-chart .ops-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; pointer-events: none; }
.ops-chart .ops-dot { stroke: var(--panel); stroke-width: 2; pointer-events: none; }
.ops-chart .ops-cross { stroke: var(--border-strong); stroke-width: 1; pointer-events: none; }
.ops-legend-item { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; cursor: pointer; }
.ops-key { display: inline-block; flex: none; }
.ops-key.is-dot { width: 9px; height: 9px; border-radius: 50%; }
.ops-key.is-line { width: 14px; height: 3px; border-radius: 2px; }
.ops-key.is-off { background: transparent; box-shadow: inset 0 0 0 1px var(--border-strong); }
.ops-tip-row { white-space: nowrap; }
.ops-tip-key { display: inline-block; width: 12px; height: 3px; border-radius: 2px; margin-right: 6px; vertical-align: middle; }

/* =========================================================================
   整页弹窗 .modal-page（2026-10-22 照 PortPro Load 详情，运输订单弹窗用；core.modal({ cls: 'modal-page' })）：
   几乎占满窗口、灰底；左边 265px 摘要栏（白卡叠着），右边一排图标标签页 + 白卡分区的正文；
   左右各自滚动，标题 / 标签页 / 底部按钮都不动。参考 docs/design-references/portpro-load-detail.jpg
   ========================================================================= */
.modal-page-backdrop { padding: 12px 20px; align-items: stretch; }
.modal.modal-page {
  display: flex;
  flex-direction: column;
  width: calc(100vw - 40px);
  max-width: none;
  height: calc(100vh - 24px);
  background: var(--bg);
  border-radius: 6px;
  overflow: hidden;
}
.modal-page .modal-head { padding: 12px 20px 10px; border-bottom: 0; }
.modal-page .modal-title { font-size: 20px; font-weight: 500; }
.modal-page .lp-title { display: inline-flex; align-items: center; gap: 10px; }
.modal-page .lp-title .badge { font-size: 10px; }
.modal-page .modal-body { flex: 1 1 auto; min-height: 0; max-height: none; padding: 0 20px 10px; overflow: hidden; display: flex; }
.modal-page .modal-body.is-tall { max-height: none; }
.modal-page .modal-foot { background: var(--bg); border-top: 0; padding: 6px 20px 10px; }

.lp { display: flex; gap: 12px; flex: 1 1 auto; min-height: 0; width: 100%; }
.lp-left { flex: 0 0 265px; width: 265px; min-height: 0; overflow-y: auto; padding-right: 2px; }
.lp-card { background: var(--panel); border-radius: 3px; padding: 10px; margin-bottom: 5px; }
.lp-card-title { font-size: 14px; font-weight: 500; margin: 3px 0 6px; }
.lp-strong { font-size: 12px; font-weight: 500; }
.lp-sub { font-size: 12px; color: var(--fg-soft); margin-top: 3px; line-height: 1.4; word-break: break-word; }
.lp-sub.lp-link { color: var(--accent); }
.lp-sub.lp-pre { white-space: pre-wrap; }
.lp-row { display: flex; justify-content: space-between; gap: 8px; margin-top: 6px; font-size: 12px; }
.lp-row .k { color: var(--muted); flex: 0 0 auto; }
.lp-row .v { font-weight: 500; text-align: right; min-width: 0; word-break: break-word; }
/* 「订单摘要」卡 = 原来那条常驻利润条：在左栏里不再吸顶、不再横着排，一行一项 */
.lp-left .callout.profit-bar { position: static; margin: 0; padding: 0; border: 0; border-radius: 0; background: transparent; }
.lp-left .profit-bar .key-ident, .lp-left .profit-bar .key-dates { display: flex; flex-direction: column; align-items: stretch; gap: 6px; font-size: 12px; }
.lp-left .profit-bar .key-ident { margin: 0 0 8px; padding: 0 0 8px; border-bottom: 1px solid var(--border); }
.lp-left .profit-bar .key-dates { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.lp-left .profit-bar .key-ident .kd, .lp-left .profit-bar .key-dates .kd { display: flex; justify-content: space-between; align-items: center; gap: 8px; white-space: normal; }
.lp-left .profit-bar .kd-label { font-size: 12px; color: var(--muted); }
.lp-left .profit-bar .kd-val { font-weight: 500; text-align: right; word-break: break-all; }
.lp-left .profit-bar .pf-row { flex-direction: column; align-items: stretch; gap: 6px; }
.lp-left .profit-bar .pf { justify-content: space-between; }
.lp-left .profit-bar .pf-op { display: none; }
.lp-left .profit-bar .pf-label { letter-spacing: 0; font-size: 12px; }
.lp-left .profit-bar .pf-val { font-size: 13px; }
.lp-left .profit-bar .pf-big { font-size: 15px; }

.lp-main { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.lp-tabs { display: flex; flex: 0 0 auto; background: var(--panel); border-radius: 3px; margin-bottom: 10px; }
.lp-tab {
  flex: 1 1 0; position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
  height: 56px; padding: 8px 10px; margin: 0 3px;
  border: 0; border-radius: 8px 8px 0 0; background: transparent;
  color: var(--muted); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; white-space: nowrap;
  transition: color .1s ease;
}
.lp-tab svg { width: 20px; height: 20px; }
.lp-tab:hover { color: var(--fg); }
.lp-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.lp-tab.is-active { color: var(--fg); font-weight: 600; }
.lp-tab.is-active svg { color: var(--accent); }
.lp-tab.is-active::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--accent); }
.lp-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 2px; }
/* 正文里的分区 = PortPro 的白卡：30px/20px 内边距、16px 标题；原来那条分区上方的细线不要了 */
.modal-page .form-section { background: var(--panel); border: 0; border-radius: 3px; padding: 20px; margin: 0 0 15px; }
.modal-page .form-section:first-child { padding-top: 20px; }
.modal-page .form-section-head { margin-bottom: 15px; }
.modal-page .form-section-title { font-size: 16px; font-weight: 500; color: var(--fg); }
.modal-page .callout { margin-bottom: 15px; }
.modal-page .lp-content > div[style*="display: none"] { display: none; }
/* 基础信息两栏（订单信息 / 地址 在左，日期 / 备注 在右）：按列流下去，各卡高矮不一也不留空洞 */
.lp-grid { columns: 2; column-gap: 15px; }
.lp-grid > .form-section { break-inside: avoid; display: block; }
.lp-grid .form-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 1280px) { .lp-grid { columns: 1; } }
@media (max-width: 1000px) { .lp-left { display: none; } .modal.modal-page { width: calc(100vw - 16px); } .modal-page-backdrop { padding: 8px; } }
.lp-tabs.toolbar { flex-wrap: nowrap; gap: 0; margin-bottom: 10px; }
.modal-subtitle { display: inline-flex; align-items: center; gap: 10px; margin-left: 12px; font-size: 14px; color: var(--fg-soft); }
.modal-subtitle .mono { font-size: 15px; font-weight: 500; color: var(--fg); }
.modal-head .modal-x { margin-left: auto; }

/* =========================================================================
   状态标签一律「色底 + 白字」（2026-10-22 用户：柜子状态、邮箱扫描的状态，系统里的状态都这样 —— PortPro badge 的画法）
   ========================================================================= */
.stage-tag, .tag.stage-tag { border-color: transparent; background: var(--gray-400); color: #fff; font-weight: 600; font-size: 10.5px; }
.stage-tag.stage-on_ship { background: #29b6f6; }        /* 在船上：浅蓝 */
.stage-tag.stage-in_yard { background: #5c6bc0; }        /* 在码头还不能提：靛蓝 */
.stage-tag.stage-ready_pickup { background: #f19c38; color: #fff; }   /* 可以提柜：橙（该派车） */
.stage-tag.stage-load_out { background: #377df6; }       /* 重柜已出码头：主蓝 */
.stage-tag.stage-load_in { background: #26a69a; }        /* 重柜已到仓库：青 */
.stage-tag.stage-empty_ready { background: #ff7043; color: #fff; }    /* 空柜待提：深橙（该派车） */
.stage-tag.stage-empty_out { background: #ab47bc; }      /* 空柜已提走：紫 */
.stage-tag.stage-empty_in { background: #65ca44; color: #fff; }       /* 空柜已还：绿（这一票走完了） */
.stage-tag.stage-unknown { background: #a3b3c6; color: #fff; }        /* 位置未知：灰 */
.stage-tag-lg { font-size: 13px; }
.stage-btn:hover .stage-tag { border-color: transparent; filter: brightness(.92); }
.stage-manual { background: var(--accent); color: #fff; font-weight: 600; }
.stage-manual.is-old { background: var(--gray-300); color: #fff; }
.tag.hold-tag.is-ours { border-color: transparent; background: var(--warn-bg); color: #fff; }
.tag.pick-tag { border-color: transparent; color: #fff; }
.tag.pick-tag.is-yes { background: #65ca44; color: #fff; }
.tag.pick-tag.is-no { background: var(--warn-bg); color: #fff; }
.tag.dispatched-tag { border-color: transparent; background: #65ca44; color: #fff; font-weight: 600; }
.tag.dispatched-tag.is-failed { background: var(--warn-bg); color: #fff; }
.tag.pod-chip { border-color: transparent; background: var(--gray-300); color: #fff; font-weight: 600; }
.tag.pod-chip.is-ready { background: #65ca44; color: #fff; }
.tag.pod-chip.is-wait { background: var(--warn-bg); color: #fff; }
.tag.pod-chip.is-bad { background: var(--danger); color: #fff; }
.tag.pod-chip.is-busy { background: var(--accent); color: #fff; }
.tag.pod-chip.is-off { background: var(--gray-300); color: #fff; text-decoration: line-through; }
.tag.pod-auto-tag, .tag.manual-tag { border-color: transparent; background: var(--accent); color: #fff; font-weight: 600; }
/* 带颜色语义的 .tag（邮箱扫描「未录入 / 多个柜号」、司机电话的结果……）也翻成色底白字 */
.tag.danger-text { border-color: transparent; background: var(--danger); color: #fff; font-weight: 600; }
.tag.warn-text { border-color: transparent; background: var(--warn-bg); color: #fff; font-weight: 600; }
.tag.ok-text { border-color: transparent; background: #65ca44; color: #fff; font-weight: 600; }
.badge.danger-text { background: var(--danger); color: #fff; }
.lfd-tip.lfd-soon, .disp-lfd.is-soon { color: #fff; background: var(--danger); padding: 0 5px; border-radius: 3px; }
.lfd-tip.lfd-over, .disp-lfd.is-over { border-radius: 3px; }
.lfd-tip.appt-need { color: #fff; background: var(--warn-bg); padding: 0 5px; border-radius: 3px; }
.disp-step-badge, .disp-step-badge.is-skip { background: var(--gray-300); color: #fff; border-radius: 3px; }
.disp-step-badge.is-challenge { background: var(--warn-bg); color: #fff; }

/* 列表行首的编辑 / 删除图标按钮（2026-10-22）；字留给读屏 / 测试（.sr-only） */
.btn-icon { width: 26px; padding: 0; }
.btn-icon svg { width: 15px; height: 15px; }
.btn-icon.danger-text:hover:not(:disabled) { background: #fdecea; border-color: var(--danger); }
.row-actions { gap: 4px; }
.table .col-actions { width: 64px; padding-right: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* 订单弹窗「进度 / 备注」tab 里嵌着的进度面板 */
.stage-panel-host .stage-composer { margin-top: 14px; }

/* 2026-10-23 impeccable 走查补的三处细节：选中文字 / 光标套主题色；MBL 格上的回形针是 SVG；表头排序箭头是两枚三角（PortPro 画法，见 core.table） */
::selection { background: var(--primary-100); color: var(--gray-900); }
input, textarea { caret-color: var(--accent); }
.tag.files-tag { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 18px; padding: 0; vertical-align: middle; color: var(--gray-500); }
.tag.files-tag svg { width: 13px; height: 13px; }
.tag.files-tag:hover { color: var(--accent); border-color: var(--accent); }
.table thead th .sort-ind { display: inline-flex; vertical-align: middle; margin-left: 2px; }
.table thead th .sort-ind svg { width: 14px; height: 14px; display: block; }
.table thead th .sort-ind path { fill: var(--gray-200); }
.table thead th .sort-ind path.is-on { fill: var(--accent); }

/* =========================================================================
   整页式列表（2026-10-23 用户：分页钉在页面最下面，一整页显示完）：页头 / 工具条 / 表格 / 底部一行 / 分页 排成一列，
   表格那一块撑满剩下的高度、自己滚，整页不滚。.page-grow = 装表格的那个容器
   ========================================================================= */
.page-fill { display: flex; flex-direction: column; height: calc(100vh - var(--top-h) - 22px); min-height: 0; }
.page-fill > .page-grow, .page-fill .page-grow { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.page-fill .page-grow > .table-wrap { flex: 1 1 auto; min-height: 120px; }
.page-fill .page-grow > .table-wrap.table-scroll { max-height: none; }
.page-fill .card.page-grow { padding-bottom: 10px; }
.page-fill .pager { padding: 8px 2px 0; flex: 0 0 auto; }
.main:has(> .page-fill) { padding-bottom: 12px; }
/* 表格底下那一行（core.tableFooter）：数字粗一点，项与项之间留出一段，别和分页挤在一起 */
.table-foot { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 14px; padding: 8px 2px 0; font-size: 12px; color: var(--fg-soft); flex: 0 0 auto; }
.table-foot .tf-n { font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
.table-foot .tf-sep { margin-left: -14px; }
.table-foot .tf-item { display: inline-flex; align-items: baseline; gap: 5px; }
.table-foot .tf-label { color: var(--muted); }
.table-foot .tf-val { font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
.table-foot .tf-val.danger-text { color: var(--danger); }
.table-foot .tf-note { font-size: 11.5px; }
.page-fill .page-grow > :not(.table-wrap) { flex: 0 0 auto; }

/* 柜子进度那排筛选按钮（core.stageChipBar，2026-10-23 用户：按钮用对应进度标签的颜色，整块是白色色块）
   --chip = 这一步的颜色（和上面 .stage-tag.stage-<v> 同一组值）：没选中 = 白底、彩边、彩字、浅彩计数；选中 = 彩底白字 */
.toolbar.stage-bar { background: var(--panel); border-radius: var(--radius); padding: 8px 12px; margin: 0 0 10px; }
.card .toolbar.stage-bar { padding: 0; }
.port-chip.stage-chip { --chip: var(--gray-700); background: var(--panel); border-color: var(--chip); color: var(--chip); font-weight: 600; }
.port-chip.stage-chip .port-chip-n { background: color-mix(in srgb, var(--chip) 14%, #fff); color: var(--chip); }
.port-chip.stage-chip:hover:not(:disabled) { background: color-mix(in srgb, var(--chip) 10%, #fff); border-color: var(--chip); }
.port-chip.stage-chip.is-on { background: var(--chip); border-color: var(--chip); color: #fff; }
.port-chip.stage-chip.is-on .port-chip-n { background: rgba(255, 255, 255, .28); color: #fff; }
.port-chip.stage-chip-all { --chip: var(--gray-700); }
.port-chip.stage-chip-on_ship { --chip: #29b6f6; }
.port-chip.stage-chip-in_yard { --chip: #5c6bc0; }
.port-chip.stage-chip-ready_pickup { --chip: #f19c38; }
.port-chip.stage-chip-load_out { --chip: #377df6; }
.port-chip.stage-chip-load_in { --chip: #26a69a; }
.port-chip.stage-chip-empty_ready { --chip: #ff7043; }
.port-chip.stage-chip-empty_out { --chip: #ab47bc; }
.port-chip.stage-chip-empty_in { --chip: #65ca44; }
.port-chip.stage-chip-unknown { --chip: #a3b3c6; }

/* 顶栏右上角的齿轮菜单（core.setPageTools / wireTopMenu，2026-10-23） */
.top-gear-wrap { position: relative; }
.top-gear { width: 30px; height: 30px; display: grid; place-items: center; border: 0; border-radius: 50%; background: transparent; color: var(--gray-500); cursor: pointer; }
.top-gear svg { width: 18px; height: 18px; }
.top-gear:hover, .top-gear[aria-expanded="true"] { background: var(--gray-50); color: var(--fg); }
.top-gear:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.top-menu { position: absolute; right: 0; top: 36px; min-width: 170px; padding: 6px; background: var(--panel); border: 1px solid var(--border); border-radius: 3px; box-shadow: var(--shadow-lg); z-index: 60; }
.top-menu[hidden] { display: none; }
.top-menu-item { display: block; width: 100%; padding: 7px 10px; border: 0; border-radius: 3px; background: transparent; color: var(--fg); font: inherit; font-size: 12px; text-align: left; cursor: pointer; white-space: nowrap; }
.top-menu-item:hover { background: var(--gray-50); color: var(--accent); }
.top-menu-sep { height: 1px; margin: 4px 0; background: var(--border); }
/* 测试 / 老页面没有顶栏时菜单挂在 body 上：照样藏着 */
body > .top-menu { position: fixed; top: 60px; right: 20px; }

/* 运输订单页的「筛选」下拉面板（jobs.js buildToolbar，2026-10-23 用户：搜索栏只留一行，其余条件收进下拉） */
.filter-wrap { position: relative; }
.filter-btn { gap: 6px; }
.filter-btn::after { content: '▾'; font-size: 10px; color: var(--muted); }
.filter-btn .filter-n:empty { display: none; }
.filter-btn .filter-n { min-width: 18px; padding: 0 5px; border-radius: 22px; background: var(--accent); color: #fff; font-size: 11px; line-height: 17px; text-align: center; }
.filter-btn.is-on { border-color: var(--accent); color: var(--accent); }
.filter-pop { display: none; position: absolute; left: 0; top: calc(100% + 6px); z-index: 55; width: 560px; max-width: calc(100vw - var(--side-w) - 60px); padding: 12px 14px; background: var(--panel); border: 1px solid var(--border); border-radius: 3px; box-shadow: var(--shadow-lg); }
.filter-wrap.is-open .filter-pop { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; }
.filter-group { min-width: 0; }
.filter-group .filter-label { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.filter-group .row { gap: 6px; }
.filter-pop .select, .filter-pop .input { width: auto; min-width: 120px; max-width: 100%; }
.filter-pop .input[type="date"] { min-width: 128px; }
/* 「筛选」面板里的快捷筛选（到港未预约 / LFD / 有扣 / 该发送货提醒 / 已付未开）一律橙色：边、字、数字、选中底色一个色（2026-10-23 用户） */
.port-chip.stage-chip.quick-chip { --chip: var(--warn-bg); }
/* 批量操作那一行末尾的「!」说明（core.helpTip({ mark: '!' })） */
.help-tip.help-tip-bang::before { content: '!'; }
.help-tip.help-tip-bang { color: var(--warn); border-color: var(--warn-bg); }

/* ---------- 付款批次（bulkMoney.js，2026-10-24）：确认付款 / 收款那一步、一批的明细，上面一块大字总金额 ---------- */
.bulk-confirm { text-align: center; padding: 14px 12px; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius); }
.bulk-confirm-amount { font-size: 30px; font-weight: 700; line-height: 1.2; margin: 2px 0 4px; }
.bulk-confirm-meta { font-size: 12.5px; color: var(--fg-soft, var(--muted)); }
.bulk-confirm-list { max-height: 180px; overflow-y: auto; margin-top: 8px; font-size: 12.5px; display: grid; gap: 3px; }
