/* =============================================================
   成象天工 · 生产管理平台 — 视觉系统 v2（企业级商业风格）

   设计取向：借鉴飞书 / 企业微信一类的企业级后台观感——
   浅色底 + 白卡片 + 1px 细分隔线；克制的阴影；14/22 正文排版；
   4-8px 圆角；单一品牌蓝（#3370ff）承担主要动作，功能色只用于状态。
   仍然零依赖、零 CDN：车间局域网离线可用。

   约定：不改任何 class 名 / DOM 结构，仅替换视觉层；
   深色模式（data-theme="dark"）与车间大屏（body.board）各自独立配色。
   ============================================================= */

:root {
  /* ---- 品牌与功能色 ---- */
  --primary: #3370ff;
  --primary-hover: #245bdb;
  --primary-active: #1a4fd6;
  --primary-soft: #eef3ff;
  --primary-border: #bedaff;

  --ok: #00b42a;
  --ok-soft: #e8ffea;
  --ok-border: #aff0b5;

  --warn: #ff7d00;
  --warn-soft: #fff7e8;
  --warn-border: #ffcf8b;

  --danger: #f53f3f;
  --danger-soft: #ffece8;
  --danger-border: #fdcdc5;

  --info: #0fc6c2;
  --info-soft: #e8fffb;
  --info-border: #aff4f0;

  --purple: #722ed1;
  --purple-soft: #f5e8ff;
  --teal: #0d9488;
  --slate: #4e5969;

  /* ---- 中性色 ---- */
  --bg: #f5f6f7;
  --bg-soft: #f2f3f5;
  --panel: #ffffff;
  --panel-2: #f7f8fa;
  --text: #1d2129;
  --text-2: #4e5969;
  --muted: #86909c;
  --border: #e5e6eb;
  --border-2: #f2f3f5;
  --mask: rgba(0, 0, 0, .45);

  /* ---- 阴影 / 圆角 ---- */
  --shadow: 0 1px 2px rgba(0, 0, 0, .04);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, .07);
  --shadow-lg: 0 8px 28px rgba(0, 0, 0, .12);
  --radius: 8px;
  --radius-sm: 6px;
  --radius-lg: 12px;
  --tap: 44px;

  /* 所有弹层统一「中上方」：水平居中、距顶部这么多（手机端在移动端小节里调大一点）。
     新做弹层直接复用 .modal-backdrop / .sheet-backdrop，不要再写 place-items:center。 */
  --dialog-top: 6vh;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

[data-theme="dark"] {
  --primary: #4a7dff;
  --primary-hover: #6b93ff;
  --primary-active: #86a7ff;
  --primary-soft: #1a2540;
  --primary-border: #2f4370;

  --ok: #23c343;
  --ok-soft: #142c19;
  --ok-border: #1f4a28;

  --warn: #ff9a2e;
  --warn-soft: #33260f;
  --warn-border: #4d3a17;

  --danger: #f76965;
  --danger-soft: #331917;
  --danger-border: #4d2825;

  --info: #14c9c9;
  --info-soft: #0f2a2a;
  --info-border: #1a4545;

  --purple: #a871e8;
  --purple-soft: #241733;

  --bg: #0f1115;
  --bg-soft: #1b1f27;
  --panel: #161a21;
  --panel-2: #1b1f27;
  --text: #e5e6eb;
  --text-2: #a9aeb8;
  --muted: #86909c;
  --border: #2b3038;
  --border-2: #23272f;
  --mask: rgba(0, 0, 0, .6);

  --shadow: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, .5);
  --shadow-lg: 0 8px 28px rgba(0, 0, 0, .55);
}

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

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 22px;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
}

body { padding-bottom: env(safe-area-inset-bottom); }

a { color: var(--primary); text-decoration: none; }
a:hover { color: var(--primary-hover); }

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.4; color: var(--text); }
h1 { font-size: 20px; }
h2 { font-size: 16px; }
h3 { font-size: 15px; }
h4 { font-size: 14px; }
p { margin: 0 0 8px; }
small, .small { font-size: 12px; line-height: 20px; }
.muted { color: var(--muted); }
.mono { font-family: var(--font-mono); font-size: .95em; }
.nowrap { white-space: nowrap; }
.center { text-align: center; }
.right { text-align: right; }
.hidden { display: none !important; }
.grow { flex: 1 1 auto; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.clamp3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.clamp1 { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }

/* ---------- 布局骨架 ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 56px;
  padding: 0 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}
.topbar .brand { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 15px; }
.topbar .logo {
  width: 28px; height: 28px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  background: var(--primary);
  color: #fff; font-size: 14px; font-weight: 600;
}
.topbar .spacer { flex: 1 1 auto; }
.who { font-size: 12px; line-height: 16px; color: var(--muted); text-align: right; }

.container { width: 100%; max-width: 1280px; margin: 0 auto; padding: 16px; }
.container.narrow { max-width: 760px; }
.container.wide { max-width: 1600px; }

.grid { display: grid; gap: 12px; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.row.tight { gap: 4px; }
.row.between { justify-content: space-between; }
.stack { display: flex; flex-direction: column; gap: 8px; }

/* ---------- 卡片 ---------- */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.card.flat { box-shadow: none; }
.card.pad-lg { padding: 24px; }
.card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.card-head h2 { font-size: 15px; font-weight: 600; }
.card-head .grow { min-width: 0; }
.section-title { display: flex; align-items: center; gap: 8px; margin: 20px 0 12px; }
.section-title h2 { font-size: 15px; }

/* ---------- 按钮 ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-height: 32px; padding: 0 14px;
  font-family: inherit; font-size: 14px; font-weight: 400; line-height: 1;
  color: var(--text); background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer; user-select: none; white-space: nowrap;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { background: var(--panel-2); color: var(--primary); }
.btn:active { background: var(--bg-soft); }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(51, 112, 255, .16); }
.btn[disabled] { opacity: .5; cursor: not-allowed; color: var(--muted); }
.btn[disabled]:hover { background: var(--panel); color: var(--muted); }

.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
.btn-primary:active { background: var(--primary-active); border-color: var(--primary-active); }
.btn-ok { background: var(--ok); border-color: var(--ok); color: #fff; }
.btn-ok:hover { background: #009a25; border-color: #009a25; color: #fff; }
.btn-warn { background: var(--warn); border-color: var(--warn); color: #fff; }
.btn-warn:hover { background: #e06c00; border-color: #e06c00; color: #fff; }
/* 列表 / 表单里的「删除」用描边红：不抢主操作的视觉重心；
   弹窗确认按钮仍然是实心红（删除是不可逆动作，必须显眼）。 */
.btn-danger { background: var(--panel); border-color: var(--danger-border); color: var(--danger); }
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.modal-actions .btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.modal-actions .btn-danger:hover { background: #d92c2c; border-color: #d92c2c; color: #fff; }
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--bg-soft); color: var(--text); }

.btn-sm { min-height: 26px; padding: 0 10px; font-size: 13px; }
.btn-lg { min-height: 40px; padding: 0 18px; font-size: 15px; }
.btn-xl { min-height: 48px; padding: 0 22px; font-size: 16px; font-weight: 600; border-radius: var(--radius); }
.btn-block { width: 100%; }
.btn-muted,
.btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }

/* ---------- 表单 ---------- */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.field > label { font-size: 13px; font-weight: 400; color: var(--text-2); }
.field .hint { font-size: 12px; line-height: 18px; color: var(--muted); }

/* 账号表单的「权限」栏：三个勾选框始终显示（2026-10-13 老板要求看得见），
   当前角色不适用的那个置灰（JS 加 .is-na + input disabled），点不了也知道有这一项。
   每个勾选一行：勾选框在上、说明在下（别让说明挤到勾选框右边）。 */
#user-perms-field .stack > div { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; }
#user-perms-field .stack > div.is-na { opacity: .5; }
#user-perms-field .stack > div.is-na .switch { cursor: not-allowed; }
.field .req::after { content: " *"; color: var(--danger); }

input[type="text"], input[type="password"], input[type="tel"], input[type="number"],
input[type="date"], input[type="month"], input[type="time"], input[type="search"],
select, textarea {
  width: 100%; min-height: 32px; padding: 4px 11px;
  font-family: inherit; font-size: 14px; line-height: 22px; color: var(--text);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}
/* 数字输入框**不要上下微调箭头**（2026-10-27 老板：「把这个滑动修改金额数字的去掉，看看有哪些地方有，都去掉」）。
   默认的 spinner 又小又难点，滚轮/方向键一不小心就把金额改掉（改单里的「定制价格」「订金」最容易踩），
   所以全站统一去掉；数字格式仍然由 `inputmode` + 各处的 JS 校验（`yuanToCents` 等）保证。
   Firefox 用 `-moz-appearance`，Chrome / Safari / Edge 用两个 spin-button 伪元素。 */
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }

input::placeholder, textarea::placeholder { color: var(--muted); }
textarea { min-height: 88px; padding: 8px 11px; resize: vertical; }
select {
  appearance: none;
  padding-right: 28px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
input:hover, select:hover, textarea:hover { border-color: #c9cdd4; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(51, 112, 255, .12);
}
.switch { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
.switch input { width: 16px; height: 16px; margin: 0; accent-color: var(--primary); }
.file-input { display: none; }
.upload-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 104px; padding: 12px;
  border: 1px dashed var(--border); border-radius: var(--radius); background: var(--panel-2);
  color: var(--text-2); font-size: 13px; text-align: center; cursor: pointer;
}
.upload-tile:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); }
.upload-tile .icon { font-size: 20px; }

/* 卡片里的小一号上传框（发货中心 ④ 成品实拍）：
   同样的点击 / 拍照 / 相册 / 拖拽 / 粘贴，只是矮一些、字号小一点，别把卡片撑太高 */
.upload-tile-sm { min-height: 58px; padding: 6px 10px; gap: 2px; font-size: 12.5px; }
.upload-tile-sm .icon { font-size: 16px; }

/* 拖拽文件到上传区：高亮一下，让人知道「松手就传这里」
   （哪些区域能拖由 ui.js 的 installDropZones() 决定，认 data-drop-zone） */
[data-drop-zone].is-drop-active {
  border-color: var(--primary) !important;
  border-style: solid !important;
  background: var(--primary-soft) !important;
  color: var(--primary) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent);
}

/* ---------- 徽标 / 状态 ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 2px;
  height: 20px; padding: 0 6px; border-radius: 4px;
  font-size: 12px; font-weight: 400; line-height: 20px; white-space: nowrap;
  background: var(--bg-soft); color: var(--text-2);
}
.badge-red { background: var(--danger-soft); color: var(--danger); }
.badge-amber { background: var(--warn-soft); color: var(--warn); }
.badge-green { background: var(--ok-soft); color: var(--ok); }
.badge-blue { background: var(--primary-soft); color: var(--primary); }
.badge-cyan { background: var(--info-soft); color: #0d9a97; }
.badge-gray { background: var(--bg-soft); color: var(--muted); }
.badge-lg { height: 24px; padding: 0 8px; font-size: 13px; line-height: 24px; }

/* ---------- 指标 ---------- */
.kpi { display: flex; flex-direction: column; gap: 2px; }
.kpi .label { font-size: 13px; font-weight: 400; color: var(--muted); }
.kpi .value {
  font-size: 24px; font-weight: 600; line-height: 32px; letter-spacing: -.2px;
  font-variant-numeric: tabular-nums;
}
.kpi .sub { font-size: 12px; color: var(--muted); }
.kpi.danger .value { color: var(--danger); }
.kpi.ok .value { color: var(--ok); }
.kpi.warn .value { color: var(--warn); }

/*
 * 赝复师端概览：三个数字固定排成一行、铺满整行，窄屏自动缩小字号。
 * （工时那格按规矩不展示给师傅，所以是 3 列而不是 4 列；下面的「全组情况」也是 3 列，
 *   两排左右对齐，卡片宽度一致。）
 */
#my-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
#my-stats .card { padding: 12px 8px; }
#my-stats .kpi { gap: 2px; min-width: 0; align-items: center; text-align: center; }
#my-stats .kpi .label { font-size: 12px; white-space: nowrap; }
#my-stats .kpi .value { font-size: clamp(18px, 5vw, 24px); line-height: 1.2; }
#my-stats .kpi .sub { font-size: 11px; line-height: 16px; overflow-wrap: anywhere; }
@media (max-width: 430px) {
  #my-stats { gap: 6px; }
  #my-stats .card { padding: 10px 6px; }
  #my-stats .kpi .label { font-size: 11px; }
  #my-stats .kpi .sub { font-size: 10px; }
}

/*
 * 5 块卡（2026-10-22 加了「退款」）：今日完工 / 目前超时 / 当日完成情况 / 退款 / 师傅产出。
 * 排版按屏宽分三档，别让它掉成「3+1+1」那种难看的残行：
 *   手机（<560px）→ **2 列**（2+2+1，最后一块占左列）；
 *   平板（560–899px）→ **3 列**（3+2）；
 *   宽屏（≥900px）→ **一行 5 个**（每个都短，一行最整齐）。
 */
#my-team-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
@media (min-width: 560px) {
  #my-team-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 900px) {
  #my-team-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
#my-team-stats .kpi { gap: 2px; min-width: 0; align-items: center; text-align: center; }
#my-team-stats .kpi .label { font-size: 12px; white-space: nowrap; }
#my-team-stats .kpi .value { font-size: clamp(18px, 5vw, 24px); line-height: 1.25; white-space: nowrap; }
#my-team-stats .kpi .sub { font-size: 11px; line-height: 16px; overflow-wrap: anywhere; }
@media (max-width: 430px) {
  #my-team-stats { gap: 6px; }
  #my-team-stats .card { padding: 10px 6px; }
  #my-team-stats .kpi .label { font-size: 11px; }
  #my-team-stats .kpi .sub { font-size: 10px; }
}

.bar-row { display: flex; align-items: center; gap: 12px; padding: 6px 0; }
.bar-row .name { flex: 1 1 40%; min-width: 0; font-size: 14px; }
.bar-track { flex: 1 1 60%; height: 6px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.bar-fill { height: 100%; border-radius: 999px; background: var(--primary); }
.progress { height: 6px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.progress > span {
  display: block; height: 100%; border-radius: 999px;
  background: var(--primary); transition: width .3s ease;
}

/*
 * 「全组情况 → 退款」明细（2026-10-22）：
 * 一块 = 一次退款（客户 / 原因 / 本轮每道工序谁做的 / 每次重做折叠开来看）。
 * 手机上「第 N 道 · 工序名」和「谁做的 · 完成日期」两端对齐，长名字自动换行。
 */
.rf-item { padding: 10px 0; border-bottom: 1px solid var(--border); }
.rf-item:last-child { border-bottom: 0; padding-bottom: 2px; }
.rf-head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.rf-head b { font-size: 15px; }
.rf-reason { margin-top: 2px; font-size: 13px; color: var(--danger); overflow-wrap: anywhere; }
.rf-steps { margin-top: 6px; display: flex; flex-direction: column; gap: 2px; }
.rf-step { display: flex; gap: 8px; align-items: baseline; justify-content: space-between; font-size: 13px; }
.rf-step-name { min-width: 0; overflow-wrap: anywhere; }
.rf-step-who { flex: 0 0 auto; white-space: nowrap; }
.rf-rounds { margin-top: 6px; display: flex; flex-direction: column; gap: 4px; }
.rf-round { background: var(--bg-soft); border-radius: 8px; padding: 5px 8px; }
.rf-round > summary { cursor: pointer; font-size: 13px; overflow-wrap: anywhere; }
.rf-round .rf-step { padding-left: 8px; margin-top: 3px; }

/*
 * 研发记录时间线（2026-10-22 第三轮重做，老板：「太丑了」）。
 * 借 airtable 的克制层级（hairline 卡片 / 深色正文 / 等宽数字时间）+ claude 的阅读节奏
 * （1.6 行高、卡内留白），但**配色仍用项目自己的变量**，只借结构不搬皮肤。
 *
 * ⚠️ 前缀必须是 research-/rt-：app.css 里已经有一套 .timeline/.tl-dot（订单明细的工序时间轴），
 *    同名会互相覆盖。`.rt-dot` 必须是 block（`<span>` 默认 inline，宽高与背景都不生效）。
 */
/*
 * 研发是**整页**打开的（老板 2026-10-22：「不要就弹一个小框，要把整个页面撑满，上面不要留空」）：
 * 两层都用 `showSheet(..., { fullpage: true })`（跟「师傅产出」同一套整页模式）。
 * 内容再套一层限宽容器：手机自然撑满，桌面居中 720px，免得一条品类行拉到 1400 宽。
 */
.research-page { width: 100%; max-width: 960px; margin: 0 auto; }
/* 宽屏（≥720）品类排两列，整页不会一边空一大片；手机仍是单行一个 */
@media (min-width: 720px) {
  .research-page > .list {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
  }
}
/* 接单大厅卡片右上角「明细」（重做原因 + 原因照片，2026-10-23） */
.hall-detail { flex: none; margin-left: 4px; }
.rework-detail-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.rework-detail-head b { font-size: 15px; line-height: 20px; }

/* 返修单的「上次为什么返修」：原因 + 原因照片（2026-10-23） */
.rework-brief {
  margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--warn-border, var(--border));
  border-radius: var(--radius);
  background: var(--warn-bg, #fff9ec);
}
.rework-brief-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.rework-meta { font-size: 12px; line-height: 16px; color: var(--muted); }
/* 卡片上不放照片时的那句「有几张、去哪看」（2026-10-23 老板：卡片去掉返修图片，只看明细） */
.rework-photos-hint { font-size: 12px; line-height: 16px; color: var(--muted); }
.rework-reason {
  margin-top: 4px; font-size: 13px; line-height: 1.5; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.rework-photos { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.rework-photos .photo-thumb.mini { width: 64px; height: 64px; margin-right: 0; }

.research-timeline { position: relative; padding-left: 18px; }
.research-timeline::before {
  content: ''; position: absolute; left: 3px; top: 16px; bottom: 16px;
  width: 2px; background: var(--border-2, var(--border));
}
.rt-item { position: relative; padding-bottom: 10px; }
.rt-item:last-child { padding-bottom: 0; }
.rt-dot {
  display: block;
  position: absolute; left: -18px; top: 18px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 0 0 3px var(--panel);
}
/* 一条记录 = 一张白卡（1px hairline 描边，不堆阴影） */
.rt-card {
  padding: 12px 14px 13px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.rt-head { display: flex; align-items: baseline; gap: 10px; }
.rt-head b { font-size: 15px; font-weight: 600; line-height: 20px; color: var(--text); }
.rt-time {
  margin-left: auto;
  font-size: 12px; line-height: 16px; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rt-body {
  margin-top: 6px;
  font-size: 14px; line-height: 1.6; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
/* 照片一排 72px 正方形（390 宽一行放得下 4 张），照片少时也不拉满整行 */
.rt-photos {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px;
}
/* ⚠️ 必须带上 .mini：`.photo-thumb.mini` 在 558 行写死了 36px，只写 .rt-photos .photo-thumb
   优先级不够，缩略图会一直是 36 的小方块（2026-10-22 截图抓到的）。 */
.rt-photos .photo-thumb.mini {
  width: 72px; height: 72px; aspect-ratio: 1;
  margin-right: 0; vertical-align: top;
  border-radius: var(--radius);
}
/* 空品类：图标 + 一句话 + 直接提交 */
.research-empty { padding: 30px 12px; text-align: center; color: var(--muted); }
.research-empty-ico { display: block; font-size: 30px; line-height: 1; margin-bottom: 10px; }
.research-empty .btn { margin-top: 14px; }

/*
 * 管理端「研发记录」：同一套 `.rt-card` 用网格铺开（桌面 3 列 / 窄屏 1 列）。
 * 卡片高度不齐时按内容各自高（align-items:start），别拉成等高。
 */
.research-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 10px; margin-top: 8px; align-items: start;
}
.research-cards .rt-card { display: flex; flex-direction: column; }
.research-cards .rt-head { flex-wrap: wrap; row-gap: 4px; }
.research-cards .rt-foot { display: flex; justify-content: flex-end; margin-top: 10px; }
/* 两列/三列时照片比师傅端小一档，一行 4 张不挤 */
.research-cards .rt-photos .photo-thumb.mini { width: 56px; height: 56px; }

/**
 * 看板弹层「研发情况」的记录列表：**竖排时间线**（2026-10-23 老板：「这个排序需要竖着排序，
 * 横着时间线看着乱乱的」——两列并排像横着念，改成一条一条往下）。
 * 卡片本身仍复用 `.rt-card`，这里只把网格换成单列 + 左侧一条细竖线和每条的圆点；
 * 设置页那份仍然是 3 列网格（`.research-cards` 默认），两处只差这一个 class。
 *
 * ⚠️ **别用 `.research-timeline` 这个名字**：师傅端研发记录（`operator.js`）早就占用了它
 * （`.research-timeline` + `.rt-item` + `.rt-dot`，见上面 452 行那段）。2026-10-23 第一版
 * 就是撞名了 —— 两个圆点叠在一起、底下的 padding 也被我的规则改掉。这里叫 `.rs-timeline`。
 */
.research-cards.rs-timeline {
  display: flex; flex-direction: column; gap: 8px;
  position: relative; padding-left: 18px;
}
.rs-timeline::before {
  content: ''; position: absolute; left: 5px; top: 10px; bottom: 10px;
  width: 1px; background: var(--border-2, var(--border));
}
.rs-timeline .rt-card { position: relative; width: 100%; }
.rs-timeline .rt-card::before {
  content: ''; position: absolute; left: -18px; top: 15px;
  width: 9px; height: 9px; box-sizing: border-box; border-radius: 999px;
  background: var(--panel); border: 2px solid var(--border-2, var(--border));
}
/* 竖排时「删除」放标题行最右（省掉卡片底部那一行，一行一条更好扫） */
.rs-timeline .rt-del { margin-left: 8px; flex: 0 0 auto; }
.rs-timeline .rt-del .btn {
  padding: 0 .5rem; font-size: 12px; line-height: 22px; min-height: 22px;
}
@media (max-width: 640px) {
  .rs-timeline .rt-head { flex-wrap: wrap; row-gap: 4px; }
  .rs-timeline .rt-del { margin-left: auto; }
}
.research-row {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 12px; cursor: pointer;
}
.research-row:active { background: var(--panel-2); }
.research-row .research-name { display: block; font-size: 15px; font-weight: 600; line-height: 20px; }
.research-row .research-sub { display: block; font-size: 12px; line-height: 16px; overflow-wrap: anywhere; }
.research-row .research-submit { flex: 0 0 auto; }
/* 提交弹层里「品类」那一栏是只读的（品类已经由点「+ 提交」定下来了） */
.research-type-fixed {
  padding: 9px 12px; border-radius: var(--radius);
  background: var(--bg-soft); font-weight: 600;
}

/* ---------- 表格 ---------- */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.table { width: 100%; border-collapse: collapse; font-size: 14px; }
table.table th, table.table td {
  padding: 10px 12px; text-align: left; vertical-align: middle;
  border-bottom: 1px solid var(--border-2);
}
table.table th {
  position: sticky; top: 0; z-index: 1;
  background: var(--panel-2); color: var(--text-2);
  font-size: 13px; font-weight: 500; white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
table.table tbody tr:hover { background: var(--panel-2); }
table.table td.detail-cell { padding: 14px 16px; background: var(--panel-2); }
table.table tr.detail-row > td { border-bottom: 1px solid var(--border); }
table.table tr[data-order-row] { cursor: pointer; }
table.table tr[data-order-row]:hover { background: var(--panel-2); }
table.table tr.overdue-row { background: #fff8f7; }
table.table tbody tr[data-pick-row] { cursor: pointer; }
table.table tbody tr.row-active { background: var(--primary-soft); }
table.table tbody tr.row-active td:first-child { box-shadow: inset 3px 0 0 var(--primary); }

/* ---------- 列表 / 任务卡 ---------- */
.list { display: flex; flex-direction: column; gap: 12px; }
.item {
  padding: 16px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.item.overdue { background: #fffbfa; border-color: var(--danger-border); }
.item.rush { border-left: 3px solid var(--warn); }
.item-head { display: flex; align-items: flex-start; gap: 8px; }
.item-head .title { font-size: 15px; font-weight: 600; }
.meta { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 6px; font-size: 13px; color: var(--muted); }
.meta b { color: var(--text-2); font-weight: 500; }
.task-card { padding: 14px 16px; border-left: 3px solid var(--border); }
.task-card .step-name { font-size: 16px; font-weight: 600; }
.countdown { font-variant-numeric: tabular-nums; font-weight: 600; }
.countdown.warn { color: var(--warn); }
.countdown.danger { color: var(--danger); }

/* ---------- 时间轴 ---------- */
.timeline { display: flex; flex-direction: column; margin-top: 8px; }
.tl-node { display: flex; gap: 10px; }
.tl-rail { display: flex; flex-direction: column; align-items: center; width: 16px; }
.tl-dot { flex: none; width: 8px; height: 8px; margin-top: 7px; border-radius: 50%; background: #c9cdd4; }
.tl-dot.done { background: var(--ok); }
.tl-dot.active { background: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.tl-dot.late { background: var(--danger); }
.tl-line { flex: 1; width: 1px; margin: 2px 0; background: var(--border); }
.tl-body { flex: 1; min-width: 0; padding: 2px 0 14px; }
.tl-body .name { font-size: 14px; font-weight: 500; }

/* ---------- 照片 / 视频 ---------- */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 8px; margin-top: 8px; }
.photo-thumb {
  position: relative; aspect-ratio: 1; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-soft);
}
.photo-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.photo-thumb[data-zoom] { cursor: zoom-in; }
.photo-thumb .del {
  position: absolute; top: 4px; right: 4px;
  display: grid; place-items: center; width: 22px; height: 22px;
  border: none; border-radius: 50%;
  background: rgba(0, 0, 0, .55); color: #fff; font-size: 12px; line-height: 1; cursor: pointer;
}
.photo-thumb .del:hover { background: var(--danger); }
.photo-thumb .size {
  position: absolute; left: 4px; bottom: 4px; padding: 0 5px; border-radius: 4px;
  background: rgba(0, 0, 0, .5); color: #fff; font-size: 11px; line-height: 16px;
}
.photo-thumb .zoom-hint {
  position: absolute; right: 4px; bottom: 4px; padding: 0 6px; border-radius: 4px;
  background: rgba(0, 0, 0, .5); color: #fff; font-size: 11px; line-height: 16px; pointer-events: none;
}
/* 网格项必须 min-width:0：否则图片的固有宽度会把列撑爆，弹层里变成一张大图 */
.photo-grid > * { min-width: 0; }
/* 明细弹层里的照片墙：PC / 平板用大一点的方格（手机端见下方媒体查询） */
.photo-grid.detail { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; }
.photo-thumb.placeholder {
  display: grid; place-items: center; padding: 6px; text-align: center;
  color: var(--muted); font-size: 11px; line-height: 16px; cursor: default;
}
/* 占位块按**真实原因**分色（2026-10-29）：别让「登录失效」看起来像「文件丢了」。
   可点的两种（重新登录 / 重试）才给手型光标。 */
.photo-thumb.placeholder.is-unauth { color: var(--warn); border-color: var(--warn-border, var(--border)); }
.photo-thumb.placeholder.is-unauth,
.photo-thumb.placeholder.is-failed { cursor: pointer; }
.photo-thumb.placeholder.is-missing { color: var(--danger); }
.photo-thumb.mini {
  display: inline-block; width: 36px; height: 36px; aspect-ratio: auto;
  margin-right: 4px; vertical-align: middle;
}
.photo-thumb.is-video { cursor: pointer; }
/* 上一轮交付的成品实拍（维修 / 撤回复位后重发的那张单）：淡一点 + 角标写「上一轮」 */
.photo-thumb.is-stale { opacity: .62; }
.photo-thumb.is-stale img { filter: saturate(.75); }
/* 临时区里「还没确认」的成品实拍：虚线边框 + 角标「待确认」（点确认发货才正式入单） */
.photo-thumb.is-draft { outline: 2px dashed var(--warn); outline-offset: -2px; }
.photo-thumb.is-draft .size { background: var(--warn); }
.photo-thumb .media-fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: var(--bg-soft); color: var(--muted); font-size: 20px;
}
.photo-thumb .play-badge {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: grid; place-items: center; width: 30px; height: 30px; padding-left: 2px;
  border-radius: 50%; background: rgba(0, 0, 0, .5); color: #fff; font-size: 13px;
}
.photo-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 10px; }
.photo-hint { font-size: 13px; color: var(--muted); }
.photo-hint b { color: var(--text); font-weight: 500; }
.photo-strip { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.upload-progress { height: 4px; margin-top: 8px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.upload-progress > span {
  display: block; width: 0; height: 100%; border-radius: 999px;
  background: var(--primary); transition: width .2s ease;
}
.spinner {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--primary);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.lightbox {
  position: fixed; inset: 0; z-index: 80;
  /* 点开照片 = 单看这张图：纯黑不透明，背后看不到网页 */
  display: flex; align-items: center; justify-content: center;
  padding: 0; background: #000; overflow: hidden;
  touch-action: none; user-select: none; -webkit-user-select: none;
  animation: lb-in .16s ease;
}
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }
/* 一条横向轨道，每张占满一屏，左右滑动切换 */
.lb-track { display: flex; width: 100%; height: 100%; will-change: transform; }
.lb-item {
  flex: 0 0 100%; width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.lb-item img {
  display: block; width: auto; height: auto;
  max-width: 100%; max-height: 100%;
  object-fit: contain; border-radius: 0; box-shadow: none;
  transform-origin: center center; -webkit-user-drag: none;
}
/* 参考视频在同一条轨道里播放：用手机自带的播放控件（进度条 / 全屏 / 音量） */
.lb-item video {
  display: block; width: auto; height: auto;
  max-width: 100%; max-height: 100%;
  background: #000; border-radius: 0; box-shadow: none;
  touch-action: auto;
}
/* 播的是「播放代理」时，标题条右边的「看原片」按钮：就地切换播放地址（不跳页） */
.lightbox .caption .lb-orig {
  display: inline-block; margin-left: 8px; padding: 4px 12px;
  border: 1px solid rgba(255, 255, 255, .45); border-radius: 999px;
  background: rgba(255, 255, 255, .12);
  color: #fff; font: inherit; font-size: 12px; line-height: 16px; cursor: pointer;
}
.lightbox .caption .lb-orig:hover { background: rgba(255, 255, 255, .22); }
.lightbox .caption .lb-orig:active { background: rgba(255, 255, 255, .32); }
/* 多张照片时左上角显示「2 / 5」 */
.lb-count {
  position: absolute; top: calc(14px + env(safe-area-inset-top)); left: 14px;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(0, 0, 0, .55); color: #fff; font-size: 12px; line-height: 16px;
}
/* 单号 / 客户 / 产品 / 工序 说明：做成底部一条半透明浮标，不占额外空间 */
.lightbox .caption {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(14px + env(safe-area-inset-bottom));
  width: max-content; max-width: calc(100% - 32px);
  padding: 5px 12px; border-radius: 999px;
  background: rgba(0, 0, 0, .58); color: #fff; font-size: 13px; text-align: center;
}
.lightbox .close {
  position: absolute; top: calc(16px + env(safe-area-inset-top)); right: 16px;
  width: 40px; height: 40px; border: none; border-radius: 50%;
  background: rgba(255, 255, 255, .18); color: #fff; font-size: 18px; cursor: pointer;
}
.lightbox .close:hover { background: rgba(255, 255, 255, .3); }
/* 多张照片时的左右翻页按钮：手机上不用非得滑动，点一下也能翻 */
.lightbox .lb-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center;
  width: 44px; height: 64px; padding: 0 0 4px;
  border: none; border-radius: 10px;
  background: rgba(255, 255, 255, .16); color: #fff;
  font-size: 26px; line-height: 1; cursor: pointer;
  transition: background .15s ease, opacity .15s ease;
}
.lightbox .lb-nav:hover { background: rgba(255, 255, 255, .32); }
.lightbox .lb-nav.prev { left: calc(10px + env(safe-area-inset-left)); }
.lightbox .lb-nav.next { right: calc(10px + env(safe-area-inset-right)); }
.lightbox .lb-nav[disabled] { opacity: .25; cursor: default; }
.lightbox .lb-nav[disabled]:hover { background: rgba(255, 255, 255, .16); }

/* ---------- 分段控件 / 下拉 ---------- */
.segmented {
  display: flex; gap: 2px; padding: 2px; overflow-x: auto;
  background: var(--bg-soft); border-radius: var(--radius-sm);
}
.segmented button {
  flex: 1 1 auto; min-height: 30px; padding: 0 12px; white-space: nowrap;
  border: none; border-radius: 4px; background: transparent; color: var(--text-2);
  font-family: inherit; font-size: 14px; font-weight: 400; cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.segmented button:hover { color: var(--text); }
.segmented button.active { background: var(--panel); color: var(--text); font-weight: 500; box-shadow: var(--shadow); }
.segmented.wrap { flex-wrap: wrap; overflow: visible; }
.segmented.wrap button { flex: 0 0 auto; }
.dropdown { position: relative; flex: 1 1 auto; min-width: 0; }
.dropdown-toggle {
  display: flex; align-items: center; gap: 8px; width: 100%; min-height: 32px;
  padding: 4px 11px; text-align: left; font-family: inherit; font-size: 14px; color: var(--text);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer;
}
.dropdown-toggle:hover { border-color: #c9cdd4; }
.dropdown-toggle .caret {
  flex: none; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid var(--muted);
}
.dropdown-panel {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 252px; overflow-y: auto; overscroll-behavior: contain;
  padding: 4px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-md);
}
.dropdown-item {
  display: flex; align-items: center; gap: 8px; width: 100%; min-height: 32px;
  padding: 4px 8px; border: none; border-radius: 4px; background: transparent;
  font-family: inherit; font-size: 14px; text-align: left; color: var(--text); cursor: pointer;
}
.dropdown-item + .dropdown-item { margin-top: 2px; }
.dropdown-item:hover { background: var(--panel-2); }
.dropdown-item.active { background: var(--primary-soft); color: var(--primary); font-weight: 500; }
.dropdown-item .grow { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.page-title { margin: 4px 0 12px; text-align: center; font-size: 16px; font-weight: 600; }

/* ---------- 接单大厅 ---------- */
.task-card { border-left: 3px solid var(--border); }
.task-card.rush, .task-card.wait-warn { border-left-color: var(--warn); }
.task-card.urgent, .task-card.overdue { border-left-color: var(--danger); }
.task-card.wait-danger { border-left-color: var(--danger); box-shadow: 0 0 0 1px var(--danger-border); }

/* 客户截止临近：卡片呼吸光圈（剩 ≤15 天橙 / ≤10 天红，阈值管理端可配）
   光圈贴在卡片内侧，不占布局空间；呼吸只改明暗不缩放，卡片不会一跳一跳 */
@keyframes due-breathe-warn {
  0%, 100% { box-shadow: inset 0 0 0 1px var(--warn-border), 0 0 5px -2px rgba(255, 125, 0, .35); }
  50% { box-shadow: inset 0 0 0 2px var(--warn), 0 0 9px -1px rgba(255, 125, 0, .45); }
}
@keyframes due-breathe-danger {
  0%, 100% { box-shadow: inset 0 0 0 1px var(--danger-border), 0 0 5px -2px rgba(245, 63, 63, .4); }
  50% { box-shadow: inset 0 0 0 2px var(--danger), 0 0 10px -1px rgba(245, 63, 63, .5); }
}
.task-card.due-warn, .record-row.due-warn { animation: due-breathe-warn 2.6s ease-in-out infinite; }
.task-card.due-danger, .record-row.due-danger { animation: due-breathe-danger 2.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .task-card.due-warn, .record-row.due-warn { animation: none; box-shadow: inset 0 0 0 1px var(--warn); }
  .task-card.due-danger, .record-row.due-danger { animation: none; box-shadow: inset 0 0 0 1px var(--danger); }
}

/* 我的任务：卡片压紧一点，但「客户截止 + 倒计时」这些要紧的字放大加粗 */
#mine-list .task-card { padding: 11px 13px; }
#mine-list .task-card .hall-due { margin-top: 8px; padding: 6px 9px; gap: 6px; }
#mine-list .task-card .hall-due-date { font-size: 17px; }
#mine-list .task-card .hall-due .countdown { font-size: 14px; }
#mine-list .task-card .hall-meta { margin-top: 4px; }
#mine-list .task-card .task-actions { margin-top: 7px; }
#mine-list .task-card .photo-toolbar { margin-top: 7px; }

/* 我的任务卡片（按草图排版）：一行信息 + 一行拍照 + 一行操作 */
#mine-list .task-card { padding: 10px 12px; }
#mine-list .task-card .card-row1 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
#mine-list .task-card .card-title {
  /* 一行放不下就自动换到下一行（不省略、不截断） */
  flex: 1 1 60%; min-width: 0; display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap;
  font-size: 16px; line-height: 22px;
}
#mine-list .task-card .card-title b { color: var(--text); }
#mine-list .task-card .card-dot { color: var(--muted); }
#mine-list .task-card .card-step {
  color: var(--primary); font-weight: 700;
  min-width: 0; overflow-wrap: anywhere;
}
/* 右上角竖排：截止日期在上、客户详情在下 */
#mine-list .task-card .card-side {
  flex: none; margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: 4px;
}
#mine-list .task-card .btn-chip {
  flex: none; display: inline-flex; align-items: center; height: 26px; padding: 0 9px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--panel);
  color: var(--text-2); font-size: 12px; font-family: inherit; cursor: pointer;
}
#mine-list .task-card .btn-chip:hover { border-color: var(--primary-border); color: var(--primary); }
/*
 * 明细弹层副标题行右边那粒次级操作（现在只有「客户明细 → 客户详情 ›」）——
 * 长相跟「我的任务」卡片上的胶囊按钮一致（老板 2026-10-15：直接用那份客户详情的代码，
 * 位置放在「单号 · 共 N 道」这一行的最右边，不占工序列表的地方）。
 */
#stat-sheet-subrow { align-items: center; gap: 8px; margin-top: 2px; }
#stat-sheet-subrow #stat-sheet-sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#stat-sheet-subrow .btn-chip {
  flex: none; display: inline-flex; align-items: center; height: 26px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--panel);
  color: var(--text-2); font-size: 12px; font-family: inherit; cursor: pointer;
}
#stat-sheet-subrow .btn-chip:hover { border-color: var(--primary-border); color: var(--primary); }
/* 剩多少天：加大加粗（不带「客户截止」字样；临期橙、≤10 天或超期红） */
#mine-list .task-card .card-due { flex: none; display: inline-flex; align-items: baseline; gap: 4px; }
#mine-list .task-card .card-due .due-date {
  font-size: 18px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums;
}
#mine-list .task-card .card-due.warn .due-date { color: var(--warn); }
#mine-list .task-card .card-due.danger .due-date { color: var(--danger); }

/* 第 2 行：三种上传方式（视频 / 拍照 / 相册）三个等宽框；加过就横向铺 56×56 缩略图 */
#mine-list .task-card .card-row2 { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
#mine-list .task-card .media-ways {
  flex: 1 1 auto; min-width: 0;
  /* 用 flex 平分（不用固定 3 列网格）：以后把「相册」那格去掉，两块照样是各占一半，不会留个空位 */
  display: flex; gap: 8px;
}
#mine-list .task-card .btn-way {
  flex: 1 1 0; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  height: 36px; padding: 0 6px; overflow: hidden; white-space: nowrap;
  border: 1px dashed var(--border); border-radius: var(--radius-sm);
  background: var(--bg-soft); color: var(--text-2); font-size: 13px; cursor: pointer;
}
#mine-list .task-card .btn-way:active {
  background: var(--primary-soft); border-color: var(--primary-border);
}
/* 加过之后那排再压薄一点，别把卡片撑高 */
#mine-list .task-card .media-ways.is-compact .btn-way { height: 30px; font-size: 12.5px; }
#mine-list .task-card .way-busy { flex: none; font-size: 12px; color: var(--muted); }
#mine-list .task-card .photo-strip {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px;
  overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch;
}
#mine-list .task-card .photo-strip .strip-thumb { flex: 0 0 56px; width: 56px; height: 56px; }
#mine-list .task-card .photo-strip .strip-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 视频格子：中间压一个 ▶，一眼看出这是段视频（点开还是走同一个查看器） */
#mine-list .task-card .photo-strip .strip-thumb.is-video { position: relative; background: #000; }
#mine-list .task-card .photo-strip .strip-thumb .strip-play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .45); color: #fff; font-size: 10px; pointer-events: none;
}
#mine-list .task-card .photo-strip .strip-thumb .del { top: 2px; right: 2px; width: 18px; height: 18px; font-size: 11px; }
#mine-list .task-card .strip-count { flex: none; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* 第 3 行：放弃（极简灰字）+ 确认完成（没拍照时置灰禁用） */
#mine-list .task-card .card-row3 { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
#mine-list .task-card .btn-text-mute {
  flex: none; height: 36px; padding: 0 10px; border: none; background: transparent;
  color: var(--muted); font-family: inherit; font-size: 14px; cursor: pointer;
}
#mine-list .task-card .btn-text-mute:hover { color: var(--danger); }
#mine-list .task-card .btn-complete { min-height: 38px; }
#mine-list .task-card .btn-complete:disabled {
  background: var(--bg-soft); border-color: var(--border); color: var(--muted); cursor: not-allowed;
}

/* 历史记录：底部页码翻页（‹ 1 2 3 … 20 21 22 ›） */
.pager-wrap {
  /* 跟「全部客户」的页码条一个长相（白底栏 + 6px 圆角格子），三个页签的翻页看起来才是同一个产品 */
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 4px;
  margin-top: .7rem; padding: 6px 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
}
/* 列表条数上限的实话实说（「只加载了前 N 条」这类） */
.list-note {
  margin-top: 8px; padding: 6px 10px; border-radius: var(--radius-sm);
  background: var(--bg-soft); color: var(--muted); font-size: 12px; line-height: 17px; text-align: center;
}

/* ---------- 赝复管理员「全组情况」弹层（2026-10-13） ---------- */
.team-summary {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;
  padding: 10px 12px; border-radius: var(--radius); background: var(--primary-soft);
}
.team-summary > div { display: flex; flex-direction: column; gap: 2px; }
.team-summary .k { font-size: 12px; color: var(--text-2); }
.team-summary b { font-size: 20px; line-height: 26px; font-variant-numeric: tabular-nums; }
.team-chart-label { margin: 12px 0 6px; font-size: 12px; color: var(--muted); }
.team-chart { display: flex; gap: 8px; align-items: stretch; height: 148px; }
/* 轴刻度要跟柱子的「轨道区」对齐：上面留数字行 14px、下面留标签行 18px */
.team-axis {
  display: flex; flex-direction: column; justify-content: space-between;
  width: 26px; padding: 14px 0 18px; text-align: right;
  font-size: 11px; line-height: 1; color: var(--muted); font-variant-numeric: tabular-nums;
}
.team-bars { display: flex; flex: 1 1 auto; align-items: stretch; gap: 6px; }
.team-bar { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; height: 100%; }
.team-bar-num { font-size: 11px; line-height: 14px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.team-bar-track { flex: 1 1 auto; min-height: 0; width: 100%; display: flex; align-items: flex-end; justify-content: center; }
.team-bar-fill {
  width: 100%; max-width: 34px; border-radius: 4px 4px 0 0;
  background: var(--primary); opacity: .85;
}
.team-bar.is-today .team-bar-fill { background: var(--ok); opacity: 1; }
.team-bar-label { margin-top: 4px; font-size: 11px; line-height: 14px; color: var(--muted); }
.team-bar.is-today .team-bar-label { color: var(--ok); font-weight: 600; }
.pager-num, .pager-arrow {
  min-width: 30px; height: 30px; min-height: 30px; padding: 0 7px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 6px; background: var(--panel);
  color: var(--text); font-family: inherit; font-size: 13px; font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.pager-num:hover, .pager-arrow:not(:disabled):hover { border-color: var(--primary-border); color: var(--primary); }
.pager-num.active { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.pager-arrow:disabled { opacity: .4; cursor: not-allowed; }
.pager-gap {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 14px; height: 30px; padding: 0; color: var(--muted); font-size: 13px;
}
@media (max-width: 430px) {
  .pager-wrap { gap: 3px; padding: 5px 6px; }
  .pager-num, .pager-arrow { min-width: 28px; height: 28px; min-height: 28px; padding: 0 6px; font-size: 12.5px; }
  .pager-gap { min-width: 12px; height: 28px; }
}
.hall-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.hall-customer { font-size: 16px; font-weight: 600; }
.hall-qty {
  flex: none; padding: 0 6px; border-radius: 5px; line-height: 18px;
  background: var(--primary-soft); color: var(--primary);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.hall-arrow { color: var(--muted); }
.hall-step { font-size: 14px; font-weight: 500; color: var(--primary); }
.hall-due {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin-top: 10px; padding: 8px 10px;
  background: var(--panel-2); border-radius: var(--radius-sm);
}
.hall-due-label { font-size: 12px; color: var(--muted); }
.hall-due-date { font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }
.hall-due-left { font-size: 12px; font-weight: 500; color: var(--warn); }
.hall-meta { margin-top: 6px; font-size: 12px; color: var(--muted); }
.hall-meta-line { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
/* 接单大厅卡片：剩多少天靠右对齐 */
.hall-meta-line .meta-due-wrap { margin-left: auto; }
/* 重做标记：客户名后面就一个字条「重做1 / 重做2」，不再堆长提示 */
.redo-tag {
  flex: none; padding: 0 6px; border-radius: 5px; line-height: 18px;
  background: var(--primary-soft); color: var(--primary); font-size: 12px; font-weight: 500;
}
#mine-list .task-card .redo-tag { align-self: center; }
/* 重做起点默认收起：一个小按钮，点开才出现下拉 */
.hall-start-collapsed { padding: 0; border: none; background: transparent; }
.btn-start-pick {
  width: 100%; height: 34px; padding: 0 12px; margin-top: 8px;
  border: 1px dashed var(--warn-border); border-radius: var(--radius-sm); background: var(--warn-soft);
  color: var(--warn); font-family: inherit; font-size: 13px; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.btn-start-pick.picked { border-style: solid; color: var(--text-2); background: var(--panel-2); border-color: var(--border); }
.meta-small { font-size: 12px; color: var(--muted); }
.meta-due-wrap { display: inline-flex; align-items: baseline; gap: 4px; }
.meta-due-label { font-size: 13px; color: var(--muted); }
.meta-due { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }
.hall-note {
  margin-top: 6px; padding-left: 10px;
  border-left: 2px solid var(--border); font-size: 13px; color: var(--muted);
}
.hall-claim { margin-top: 12px; }

/* 定制产品选择 */
.part-picker { display: grid; flex: 1 1 auto; min-width: 0; gap: 6px; }
.part-picker[data-cols="1"] { grid-template-columns: minmax(0, 38%); justify-content: center; }
.part-picker[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.part-picker[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.part-picker[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.part-picker button {
  display: flex; align-items: center; justify-content: center; gap: 4px;
  min-height: 34px; padding: 6px 8px; line-height: 1.3; text-align: center;
  font-family: inherit; font-size: 13px; font-weight: 400; color: var(--text);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer;
}
.part-picker button:hover { border-color: var(--primary-border); color: var(--primary); }
.part-picker button.active {
  background: var(--primary-soft); border-color: var(--primary-border);
  color: var(--primary); font-weight: 500;
}
.part-picker button.active .badge { background: #fff; color: var(--primary); }
.filter-block {
  padding: 12px 14px; margin-bottom: 12px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
}
.filter-row { display: flex; align-items: center; gap: 10px; }
.filter-row + .filter-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-2); }
.filter-label { flex: none; font-size: 13px; color: var(--muted); }
.filter-row .segmented { flex: 1 1 auto; gap: 4px; padding: 0; background: transparent; }
.filter-row .segmented button { background: var(--panel); border: 1px solid var(--border); }
.filter-row .segmented button.active {
  background: var(--primary-soft); border-color: var(--primary-border);
  color: var(--primary); box-shadow: none;
}
.part-group-title { display: flex; align-items: center; gap: 6px; margin: 12px 0 6px; font-size: 13px; color: var(--muted); }
.part-group-title:first-child { margin-top: 0; }

/* ---------- 粘性操作条 / 底部导航 ---------- */
.sticky-actions {
  position: sticky; bottom: 0; z-index: 30;
  display: flex; gap: 8px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  background: var(--panel); border-top: 1px solid var(--border);
}
.sticky-actions .btn { flex: 1 1 0; }

.tabbar {
  position: fixed; z-index: 40; left: 0; right: 0; bottom: 0;
  display: flex; align-items: stretch;
  background: var(--panel); border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom);
}
.tabbar button {
  flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 52px; padding: 6px 0 8px; border: none; background: none; cursor: pointer;
  font-family: inherit; font-size: 11px; font-weight: 400; color: var(--muted);
  -webkit-tap-highlight-color: transparent;
}
.tabbar button svg { width: 22px; height: 22px; fill: currentColor; opacity: .8; }
.tabbar button.active { color: var(--primary); }
.tabbar button.active svg { opacity: 1; }
.container.has-tabbar { padding-bottom: calc(4.5rem + env(safe-area-inset-bottom)); }
body.has-tabbar .toast-wrap { bottom: calc(4.5rem + env(safe-area-inset-bottom)); }

/* ---------- 弹层 / 提示 ---------- */
.modal-backdrop {
  /**
   * 层级要比底部的「弹层」(.sheet-backdrop, z-index 70) **高**：
   * 老板 2026-10-14 在赝复管理端「全部客户 → 明细」里点「退回上一步」，
   * `confirmDialog` 用的这个遮罩以前是 60，被明细弹层（70）盖住 → 看着像「点了没反应」。
   * 弹窗永远压在弹层上面；图片查看器（80）和提示条（90）仍在它上面。
   */
  position: fixed; inset: 0; z-index: 75;
  display: flex; align-items: flex-start; justify-content: center;
  padding: var(--dialog-top) 16px 24px; background: var(--mask);
  /* 第二道保险（2026-10-22）：万一某个弹层比视口还高，整个遮罩也能滚，别让人卡在「点不到按钮」 */
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.modal {
  width: 100%; max-width: 520px; max-height: 88vh; overflow: auto;
  padding: 20px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
}
.modal h3 { margin-bottom: 10px; font-size: 16px; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }

/* ---------- 上传进度条（弹层 / 录单页共用一份，见 ui.js 的 dialogProgressHtml） ---------- */
.dialog-progress { margin-top: 14px; }
.dialog-progress.hidden { display: none; }
.dialog-progress .dp-line {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 6px;
}
.dialog-progress .dp-label { font-size: 12.5px; color: var(--text-2); }
.dialog-progress .dp-bytes {
  font-size: 12.5px; color: var(--muted); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.dialog-progress .dp-track {
  height: 6px; border-radius: 999px; overflow: hidden; background: var(--primary-soft);
}
.dialog-progress .dp-bar {
  display: block; width: 0; height: 100%; border-radius: 999px;
  background: var(--primary); transition: width .18s ease;
}
.dialog-progress.is-done .dp-bar { background: var(--ok); }

.modal.modal-wide { max-width: 900px; }
/* 明细弹层占满剩余高度（上方留给 --dialog-top，下方留 24px，保证不出屏） */
.modal.modal-detail { width: 100%; max-width: min(96vw, 1080px); max-height: 88vh; padding: 20px 22px 24px; }
.modal-detail-body { min-width: 0; }
.video-modal {
  width: min(92vw, 1000px); padding: 12px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
}
.video-modal video { display: block; width: 100%; max-height: 76vh; border-radius: var(--radius); background: #000; }

.sheet-backdrop {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: flex-start; justify-content: center;
  padding: var(--dialog-top) 12px calc(24px + env(safe-area-inset-bottom));
  background: var(--mask);
}
/**
 * 派单台的「选人」弹层（`#pick-backdrop` 是 `.sheet-backdrop`，默认 z-index 70）
 * 要压在「明细」弹层上面（`#task-modal` / `#matrix-modal` 是 `.modal-backdrop`，75）——
 * 2026-10-14 明细里每道工序都能「派单 / 改派」，不抬高这一层就是**点了没反应**
 * （弹层开在明细后面，跟当年「退回上一步」那个坑一模一样）。图片查看器 80 / 提示条 90 仍在上面。
 */
#pick-backdrop { z-index: 78; }

/* 时间筛选的快捷药丸（2026-10-15 老板要求）：今天 / 近 7 天 / 本月 / 上月 / 全部 */
.date-presets { display: inline-flex; gap: 4px; align-items: center; }
.date-preset {
  height: 28px; padding: 0 9px; font-size: 12.5px; font-family: inherit; cursor: pointer;
  border: 1px solid var(--border); border-radius: 999px; background: var(--panel); color: var(--text);
}
.date-preset:hover { border-color: var(--primary-border, var(--border)); color: var(--primary); }
.date-preset:active { background: var(--primary-soft); }
@media (max-width: 640px) { .date-presets { flex-wrap: wrap; } }
.sheet {
  display: flex; flex-direction: column;
  width: min(100%, 560px); max-height: 84vh;
  padding: 16px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  animation: sheet-pop .2s ease;
}
/* 手机端弹窗也统一「中上方」，只是顶部留白稍大一点（大拇指够得到、视线落点舒服） */
@media (max-width: 640px) {
  :root { --dialog-top: 7vh; }
}
@keyframes sheet-pop { from { transform: scale(.97); opacity: .55; } to { transform: none; opacity: 1; } }
.sheet-head {
  display: flex; align-items: center; gap: 8px; flex: none;
  margin-bottom: 4px; padding-bottom: 10px; border-bottom: 1px solid var(--border-2);
}
.sheet-body { margin-top: 10px; overflow: auto; -webkit-overflow-scrolling: touch; min-height: 0; }
/**
 * 整页模式（`.is-fullpage`）：师傅产出那种宽表用 —— 手机横屏看 11 道工序的矩阵要占满整屏，
 * 半屏抽屉放不下（老板 2026-10-14：「师傅产出是手机横屏观看的，需要放全整个页面」）。
 * 去掉外边距 / 圆角，内容区自己撑满剩余高度并滚动（表本身在里面横向滑动）。
 */
.sheet-backdrop.is-fullpage { align-items: stretch; padding: 0; }
.sheet-backdrop.is-fullpage .sheet {
  width: 100%; max-width: none; max-height: none;
  height: 100vh; height: 100dvh;
  padding: 12px calc(12px + env(safe-area-inset-right)) calc(12px + env(safe-area-inset-bottom))
    calc(12px + env(safe-area-inset-left));
  border: none; border-radius: 0;
}
.sheet-backdrop.is-fullpage .sheet-body { flex: 1 1 auto; }
/**
 * 竖屏时**整块转 90° 当横屏用**（老板 2026-10-14：「师傅产出直接就是横屏的」）：
 * 手机上没法强制锁横屏，这是通行做法 —— 长边当宽度，11 道工序一屏铺得下，不用左右滑。
 * 横屏（orientation: landscape）时不转，照常铺满。
 */
@media (orientation: portrait) {
  .sheet-backdrop.is-fullpage.is-landscape .sheet {
    position: fixed; left: 50%; top: 50%;
    width: 100dvh; height: 100dvw; max-width: none; max-height: none;
    transform: translate(-50%, -50%) rotate(90deg);
    transform-origin: center center;
  }
}
/**
 * 师傅产出**压缩排版**（老板 2026-10-14：「现在就是框太大了……合理的去规划页面的 ui」）：
 * 只在整页模式里生效，数字条、页签、表头、行高、字号都收一档，横屏一屏能看更多。
 */
.sheet-backdrop.is-fullpage .matrix-kpis { gap: 8px; }
.sheet-backdrop.is-fullpage .matrix-kpis .mk { padding: 6px 10px; }
.sheet-backdrop.is-fullpage .matrix-kpis .mk-label { font-size: 11px; }
.sheet-backdrop.is-fullpage .matrix-kpis .mk-value { font-size: 20px; line-height: 26px; }
.sheet-backdrop.is-fullpage .matrix-kpis .mk-sub { font-size: 11px; }
.sheet-backdrop.is-fullpage .segmented button { min-height: 26px; font-size: 13px; }
.sheet-backdrop.is-fullpage .matrix-chart-label,
.sheet-backdrop.is-fullpage .matrix-legend { margin: 4px 0; font-size: 11.5px; }
.sheet-backdrop.is-fullpage .matrix-table thead th { font-size: 12px; line-height: 1.25; }
.sheet-backdrop.is-fullpage .matrix-table tbody td { padding: 4px 6px; font-size: 15px; }
.sheet-backdrop.is-fullpage .matrix-table .name-sub { font-size: 11px; }
.sheet-backdrop.is-fullpage .sheet-head { padding-bottom: 6px; }
.sheet-backdrop.is-fullpage .sheet-body { margin-top: 6px; }
.sheet-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-top: 1px solid var(--border-2); }
.sheet-row:first-child { border-top: none; }
/*
 * 维修单的「维修内容」（2026-10-15 老板：「一个维修原因：红色加大」）：
 * 师傅扫一眼就得看清要修什么，所以红字 + 加大 + 允许换行。
 */
.repair-note { color: var(--danger); font-size: 17px; font-weight: 700; line-height: 1.5; margin-top: 4px; word-break: break-word; }
/* 平板 / 电脑：弹窗放宽一点、留白多一点，看起来更像正式对话框 */
@media (min-width: 641px) {
  .sheet {
    width: min(100%, 640px); max-height: min(86vh, 880px);
    padding: 20px 22px 22px;
    animation: sheet-pop .18s ease;
  }
}

.toast-wrap {
  position: fixed; z-index: 90; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex; flex-direction: column; gap: 8px; width: min(92vw, 460px);
}
.toast {
  padding: 9px 14px; border-radius: var(--radius-sm);
  background: #1d2129; color: #fff; font-size: 14px; font-weight: 400;
  box-shadow: var(--shadow-lg); animation: toast-in .18s ease;
}
.toast.ok { box-shadow: inset 3px 0 0 var(--ok), var(--shadow-lg); }
.toast.err { box-shadow: inset 3px 0 0 var(--danger), var(--shadow-lg); }
.toast.warn { box-shadow: inset 3px 0 0 var(--warn), var(--shadow-lg); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.empty { padding: 40px 16px; text-align: center; color: var(--muted); font-size: 14px; }
.empty .icon { display: block; margin-bottom: 8px; font-size: 28px; }
.skeleton {
  height: 72px; border-radius: var(--radius);
  background: linear-gradient(90deg, var(--bg-soft), var(--panel-2), var(--bg-soft));
  background-size: 200% 100%; animation: sk 1.2s infinite;
}
@keyframes sk { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.dot-online { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--ok); }
.dot-offline { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--muted); }

/* ---------- 登录页 ---------- */
.login-wrap {
  min-height: 100vh; display: grid; place-items: center; padding: 24px;
  background:
    radial-gradient(600px 320px at 12% 6%, rgba(51, 112, 255, .10), transparent 70%),
    radial-gradient(520px 300px at 88% 94%, rgba(15, 198, 194, .08), transparent 70%),
    var(--bg);
}
.login-card { width: 100%; max-width: 420px; }
.role-hint { display: grid; gap: 6px; margin-top: 20px; font-size: 13px; color: var(--muted); }
.role-hint code { padding: 1px 6px; border-radius: 4px; background: var(--bg-soft); font-family: var(--font-mono); }

/* ---------- 车间大屏（固定深色，独立于主题开关） ---------- */
/* 大屏页面连 html 底色一起压深，避免底部露出浅色背景带 */
html:has(body.board) { background: #0b0e14; }
:is(body.board, .board-embed) { background: #0b0e14; color: #e5e6eb; overflow-x: hidden; }
:is(body.board, .board-embed) .topbar { background: #10141c; border-bottom-color: #1f2733; }
:is(body.board, .board-embed) .panel-dark {
  padding: 16px; background: #131823;
  border: 1px solid #1f2733; border-radius: var(--radius-lg);
}
/* KPI 一排 6 格（2026-10-24 修：原来是 auto-fit minmax(220px)，1710 宽的墙上屏会挤成 6 个 262px 的窄格，
 * 而 ¥41,020.00 用 54px 字号直接顶出格子外、标签被 justify-content 推到中缝 —— 整排看着是乱的。
 * 现在：固定 6 等分（窄屏 3 / 手机 2）、标签在上数字在下、左右对齐，钱用 is-money 小一号的字号。 */
:is(body.board, .board-embed) .board-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 1500px) { :is(body.board, .board-embed) .board-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 860px) { :is(body.board, .board-embed) .board-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
:is(body.board, .board-embed) .board-kpi { display: flex; flex-direction: column; align-items: flex-start; gap: .1rem; min-width: 0; }
:is(body.board, .board-embed) .board-kpi .label {
  font-size: clamp(13px, 1vw, 17px); color: #86909c;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(body.board, .board-embed) .board-kpi .value {
  font-size: clamp(28px, 3.2vw, 46px); font-weight: 600; line-height: 1.08;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
:is(body.board, .board-embed) .board-kpi.is-money .value { font-size: clamp(22px, 2.15vw, 32px); }
:is(body.board, .board-embed) .board-title { font-size: clamp(22px, 2.2vw, 34px); font-weight: 600; line-height: 1.18; }
/* 深色主题下正文 h2 是深灰字（#1d2129），在大屏上等于看不见（2026-10-24 修：老板说「排盘都是乱的」，
 * 一半原因就是「接单大厅 / 超时异常预警 / 车间实时动态」三个标题糊在黑底上看不清）。 */
:is(body.board, .board-embed) .board-h2 { font-size: clamp(17px, 1.35vw, 23px); font-weight: 600; color: #e5e6eb; }
:is(body.board, .board-embed) .board-h2-lg { font-size: clamp(18px, 1.55vw, 26px); }
/* 两列等高（默认 stretch）：生产上接单大厅常有 9 条，左列自然会填满；
   别改成 align-items:start，否则大厅只有一两条时左下会空出一大块背景，看着像少了一块面板。 */
:is(body.board, .board-embed) .two-col { display: grid; grid-template-columns: 1.35fr 1fr; gap: 14px; }
@media (max-width: 900px) { :is(body.board, .board-embed) .two-col { grid-template-columns: 1fr; } }
:is(body.board, .board-embed) .feed-item {
  display: flex; gap: 10px; padding: 8px 2px;
  border-bottom: 1px solid #1f2733; font-size: clamp(13px, 1.05vw, 16px);
}
:is(body.board, .board-embed) .feed-time { color: #6b7280; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 墙屏高度不够时（1366×768 / 1710×884 这类 16:9 里偏矮的），把内边距和行距再压一档，
   保证「今天 / 本周 / 本月 + 按师傅 + 六格 KPI + 接单大厅 + 超时预警 + 车间动态」一屏放得下、
   页面不出现滚动条（2026-10-24 实测：不压这一档，1710×884 会超 20px）。 */
@media (max-height: 960px) {
  :is(body.board, .board-embed) .panel-dark { padding: 12px 14px; }
  :is(body.board, .board-embed) .board-target { padding: .65rem .95rem; gap: 12px; }
  :is(body.board, .board-embed) .bt-goalcell { padding: .45rem .7rem; }
  :is(body.board, .board-embed) .bt-track { padding-bottom: 1.85rem; margin-top: .2rem; }
  :is(body.board, .board-embed) .feed-item { padding: 6px 2px; }
  :is(body.board, .board-embed) .board-row, :is(body.board, .board-embed) .two-col { gap: 10px; }
}

/* ---------- 车间大屏：今天 / 本周 / 本月完成道数 + 按师傅明细（2026-10-24 老板：
   「我们是月休息6天，我们要看每天完成了多少道，每周完成了多少道，每个月完成了多少道」
   「需要能看到每个师傅的明细」）----------
   上排三格各带进度条 + 参考线（按时间走到哪应该完成多少），下排每位师傅一行、今天 / 本周 / 本月三列。
   落后 = 橙、达标 = 绿。数值与目标都由后端给（team-stats 的 stepTotals / stepGoals）。 */
/* 左：今天 / 本周 / 本月三格；右：按师傅明细 —— 左右并排（2026-10-24 实测：上下堆会把
   1710×884 的墙屏顶出 175px，页面一滚下半截「超时预警 / 车间动态」就看不见了）。 */
:is(body.board, .board-embed) .board-target {
  display: grid; grid-template-columns: minmax(0, 1.72fr) minmax(0, 1fr);
  gap: 16px; align-items: start; padding: .9rem 1.2rem;
}
@media (max-width: 1180px) { :is(body.board, .board-embed) .board-target { grid-template-columns: 1fr; } }

/* 三格：宽屏并排，窄屏落成一列（别用 auto-fit minmax，格数一变就散） */
:is(body.board, .board-embed) .bt-goals { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 1100px) { :is(body.board, .board-embed) .bt-goals { grid-template-columns: 1fr; } }
:is(body.board, .board-embed) .bt-goalcell {
  min-width: 0; padding: .55rem .85rem; border-radius: 10px;
  background: #10151f; border: 1px solid #1f2733;
}
:is(body.board, .board-embed) .bt-label { font-size: clamp(13px, 1vw, 17px); color: #86909c; }
:is(body.board, .board-embed) .bt-value { display: flex; align-items: baseline; gap: .4rem; margin-top: .1rem; }
:is(body.board, .board-embed) .bt-value > b {
  font-size: clamp(30px, 3.1vw, 46px); font-weight: 700; line-height: 1.05;
  font-variant-numeric: tabular-nums; color: #e5e6eb;
}
:is(body.board, .board-embed) .bt-goal { font-size: clamp(14px, 1.15vw, 19px); color: #86909c; }
:is(body.board, .board-embed) .bt-goal b { color: #c9d1d9; font-weight: 600; }
:is(body.board, .board-embed) .bt-note { margin-top: .25rem; font-size: clamp(12px, .82vw, 13.5px); color: #93a3bb; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
:is(body.board, .board-embed) .bt-note b { color: #e5e6eb; }
:is(body.board, .board-embed) .bt-bar {
  position: relative; height: 14px; border-radius: 999px;
  background: #1b2230; overflow: hidden;
}
:is(body.board, .board-embed) .bt-bar-fill {
  height: 100%; width: 0; border-radius: 999px; background: #3370ff;
  transition: width .45s ease;
}
:is(body.board, .board-embed) .bt-bar-fill.is-behind { background: #ff9a2e; }
:is(body.board, .board-embed) .bt-bar-fill.is-ok { background: #00b42a; }
/* 参考线：「按时间算应该完成到哪」 */
:is(body.board, .board-embed) .bt-bar-mark {
  position: absolute; top: -4px; bottom: -4px; width: 2px;
  background: #e5e6eb; opacity: .5; transition: left .45s ease;
}
/* 进度条 + 刻度 + 「这个点应完成 N 道」：track 留出下面两行的高度，
 * 参考线文案用 absolute 跟着竖线走（left 由 board.js 写，夹在 6%~94% 内不越界）。 */
:is(body.board, .board-embed) .bt-track { position: relative; padding-bottom: 2.05rem; margin-top: .35rem; }
:is(body.board, .board-embed) .bt-scale {
  display: flex; justify-content: space-between;
  font-size: 12px; color: #6b7280; margin-top: .3rem;
}
:is(body.board, .board-embed) .bt-mark-label {
  position: absolute; bottom: 0; left: 0; transform: translateX(-50%);
  font-size: 12px; color: #a8b4c8; white-space: nowrap;
}

/* 按师傅明细：一行一位师傅，今天 / 本周 / 本月三列（人多时并成两列，不用滚屏） */
:is(body.board, .board-embed) .bt-optable-head { display: flex; align-items: baseline; gap: .6rem; margin-bottom: .35rem; flex-wrap: wrap; }
:is(body.board, .board-embed) .bt-optable-title { font-size: clamp(14px, 1.1vw, 18px); font-weight: 600; color: #e5e6eb; }
:is(body.board, .board-embed) .bt-optable-hint { font-size: clamp(11.5px, .9vw, 14px); color: #6b7280; }
:is(body.board, .board-embed) .bt-opcols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 22px; }
/* 右栏放不下两列时落单列：两列各要 4.5rem + 三列数字 ≈ 212px，视口 < 1200 时右栏只剩 ~400px，
   硬排两列等于又把名字挤没（2026-10-24）。 */
@media (max-width: 1200px) { :is(body.board, .board-embed) .bt-opcols { grid-template-columns: 1fr; } }
:is(body.board, .board-embed) .bt-oprow {
  /* ⚠️ 名字列**必须有最小宽度**（2026-10-24 老板：「都看不清信息了」）：原来是 minmax(0, 1fr)，
     在窄的右栏里 1fr 会被三列数字挤到 2px，「王师傅 / 李师傅」整列看不见，只剩今天/本周/本月。 */
  display: grid; grid-template-columns: minmax(4.5rem, 1fr) 44px 44px 52px; align-items: center;
  gap: .35rem; padding: .2rem .2rem; border-bottom: 1px solid #171e29;
  font-size: clamp(13px, 1.05vw, 16px); color: #c9d1d9; font-variant-numeric: tabular-nums;
}
:is(body.board, .board-embed) .bt-oprow > span:not(.bt-opname) { text-align: right; }
:is(body.board, .board-embed) .bt-oprow.is-head { color: #6b7280; font-size: clamp(11.5px, .92vw, 14px); border-bottom-color: #1f2733; }
:is(body.board, .board-embed) .bt-opname {
  font-weight: 600; color: #e5e6eb;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
:is(body.board, .board-embed) .bt-oprow.is-zero { opacity: .42; }
:is(body.board, .board-embed) .bt-empty { padding: .4rem .2rem; font-size: 13px; color: #6b7280; }
/* 接单大厅超出 9 道时那一行提示 */
:is(body.board, .board-embed) .feed-more { padding: .45rem 1rem; color: #6b7280; font-size: 13px; }

/* ---------- 发货中心工作台 ---------- */
.ff-card { display: flex; align-items: center; gap: 10px 16px; flex-wrap: wrap; }
.ff-card > .ff-check { flex: none; }
.ff-card > .ff-body { flex: 1 1 420px; min-width: 0; }
.ff-card > .ff-actions {
  flex: none; margin-left: auto;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
}
.ff-row1 { display: flex; align-items: baseline; flex-wrap: wrap; }
.ff-name { margin-right: 6px; font-size: 16px; font-weight: 600; }
.ff-meta { color: var(--muted); font-size: 13px; }
.ff-meta + .ff-meta::before { content: "·"; margin: 0 8px; opacity: .6; }
.ff-row2 { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; font-size: 14px; }
.ff-row2 .k { margin-right: 4px; color: var(--muted); font-size: 13px; }
.ff-row2 .v { font-weight: 400; }

/* 排单员写的「发货备注」：发货人扫一眼就要看到 → 红字 + 加大 + 左侧红色竖条 */
.ship-note-banner {
  display: flex; align-items: flex-start; gap: .45rem;
  margin-top: .5rem; padding: .45rem .6rem .45rem .55rem;
  border-radius: 10px; border-left: 4px solid var(--danger);
  background: color-mix(in srgb, var(--danger) 9%, transparent);
}
.ship-note-banner .ship-note-tag {
  flex: none; margin-top: .1rem; padding: .05rem .4rem;
  border-radius: 999px; font-size: .72rem; font-weight: 700;
  color: #fff; background: var(--danger); white-space: nowrap;
}
.ship-note-banner .ship-note-text {
  font-size: 1.18rem; line-height: 1.35; font-weight: 800;
  color: var(--danger); overflow-wrap: anywhere;
}
@media (max-width: 640px) {
  .ship-note-banner .ship-note-text { font-size: 1.05rem; }
}

/* 客户明细里的发货备注（排单员自己回看） */
.ship-note {
  margin-top: .2rem; padding: .45rem .6rem;
  border-radius: 10px; border-left: 4px solid var(--danger);
  background: color-mix(in srgb, var(--danger) 9%, transparent);
  font-size: 1.05rem; font-weight: 750; line-height: 1.4;
  color: var(--danger); overflow-wrap: anywhere;
}
.ff-addr { word-break: break-word; }
.ff-copy { padding: 0 2px; border-radius: 4px; cursor: copy; }
.ff-copy:hover { background: var(--primary-soft); color: var(--primary); }
@keyframes ff-attention-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--danger); }
  50% { box-shadow: 0 0 0 8px transparent; }
}
.ff-attention { font-weight: 500; animation: ff-attention-pulse 1.7s ease-out infinite; }
.ff-attention-delay { animation-delay: .85s; }
@media (prefers-reduced-motion: reduce) { .ff-attention { animation: none; } }
@media (max-width: 900px) {
  .ff-card > .ff-body { flex: 1 1 100%; }
  .ff-card > .ff-actions { width: 100%; margin-left: 0; justify-content: stretch; }
  .ff-card > .ff-actions .btn { flex: 1 1 auto; }
  .ff-row2 { font-size: 14px; }
}

/* ---------- 工序链 ---------- */
.step-chain { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
.step-chip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 0 6px; border: 1px solid transparent; border-radius: 4px;
  background: var(--bg-soft); color: var(--text-2); font-size: 12px; line-height: 20px;
}
.step-chip > i { font-style: normal; font-weight: 500; opacity: .7; }
/* 这一单完成这一步算几道（2026-10-28：2 根手指的单一一步算 2 道） */
.step-chip > .step-chip-x { font-style: normal; font-size: 10px; font-weight: 600; opacity: .85; }
.step-chip.done { background: var(--ok-soft); color: var(--ok); }
.step-chip.current { background: var(--primary-soft); border-color: var(--primary-border); color: var(--primary); font-weight: 500; }

/* ---------- 师傅端「我的」页 / 记录 ---------- */
.profile-head .logo { flex: none; }
.profile-title { font-weight: 600; }
.stat-btn {
  position: relative; width: 100%; text-align: center;
  font-family: inherit; color: var(--text); cursor: pointer;
}
.stat-btn:active { transform: scale(.995); }
.stat-btn .chev { position: absolute; top: 8px; right: 8px; }

.row-item {
  display: flex; align-items: center; gap: 8px; width: 100%; min-height: 44px;
  padding: 10px 2px; border: none; border-top: 1px solid var(--border-2);
  background: none; font-family: inherit; font-size: 14px; font-weight: 400;
  color: var(--text); text-align: left; cursor: pointer;
}
.row-item:first-child { border-top: none; }
.row-item:hover { color: var(--primary); }
.row-item .chev { flex: none; }
.chev {
  width: 6px; height: 6px; opacity: .6;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: rotate(-45deg);
}

.info-list { display: flex; flex-direction: column; gap: 2px; }
.info-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 2px; border-top: 1px solid var(--border-2); }
.info-row:first-child { border-top: none; }
.info-key { flex: 0 0 5.2rem; color: var(--muted); font-size: 13px; }
.info-val { flex: 1 1 auto; min-width: 0; font-size: 14px; overflow-wrap: anywhere; }
/* 客户详情里「定制要求」这一条：红色、加大、加粗，一眼看到 */
.info-row.is-strong .info-val { font-size: 16px; font-weight: 700; color: var(--danger); }
/* 客户详情两行摘要：根数 · 类型 / 截止 · 产品排单 · 加急 */
.info-row.info-row-group { align-items: stretch; gap: 6px; }
.info-cell {
  flex: 1 1 0; min-width: 0; padding: 0 6px;
  border-left: 1px solid var(--border-2);
}
.info-row-group.two .info-cell { flex-basis: 0; }
.info-row-group.three .info-cell { flex-basis: 0; }
.info-cell:first-child { padding-left: 2px; border-left: none; }
.info-cell i { display: block; color: var(--muted); font-size: 12px; font-style: normal; line-height: 17px; }
.info-cell b { display: block; font-size: 14px; line-height: 20px; overflow-wrap: anywhere; }
.info-cell.is-important b { color: var(--danger); font-weight: 700; }
@media (max-width: 420px) {
  .info-row.info-row-group { flex-wrap: nowrap; gap: 0; }
  .info-row-group.two .info-cell,
  .info-row-group.three .info-cell { flex: 1 1 0; padding: 0 5px; }
  .info-row-group.two .info-cell:first-child,
  .info-row-group.three .info-cell:first-child { padding-left: 2px; }
  .info-cell i { font-size: 11px; white-space: nowrap; }
  .info-cell b { font-size: 13px; line-height: 19px; }
  .info-row-group.three .info-cell:last-child { flex-grow: .72; }
}

/*
 * 客户详情 →「步骤明细 ›」（2026-10-19 老板要求）：
 * 入口跟「工序进度 x/y 道」同一行、靠右，不占主页面空间。
 */
.step-progress-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: .6rem; }
.step-progress-row .trace-entry {
  flex: 0 0 auto; height: 30px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--panel);
  color: var(--primary); font-family: inherit; font-size: 13px; cursor: pointer;
}
.step-progress-row .trace-entry:active { background: var(--primary-soft); border-color: var(--primary-border); }
/*
 * 步骤明细（师傅端只读）：一道一行 —— 左「第 N 道 工序名」，下一行「谁做的 · 完成 10-15」，
 * 有完工照片就在下面铺一排缩略图。手上这道浅蓝底高亮；后面没开始的整行压灰。
 */
.step-trace { display: flex; flex-direction: column; gap: 6px; }
.step-trace .trace-row {
  padding: 9px 12px; border: 1px solid var(--border-2); border-radius: var(--radius-sm);
  background: var(--panel);
}
.step-trace .trace-row.is-current { border-color: var(--primary-border); background: var(--primary-soft); }
/*
 * 还没轮到的步骤：**只把底色压淡，文字保持正常深色**。
 * 以前整行 `opacity: .55`，车间强光下「待接单」几乎看不清（老板 2026-10-19：「2 优化」）。
 */
.step-trace .trace-row.is-future { background: var(--bg-soft); }
.step-trace .trace-title { font-size: 13.5px; line-height: 19px; }
.step-trace .trace-title b { font-weight: 600; }
.step-trace .trace-no { color: var(--primary); font-weight: 500; }
.step-trace .trace-meta { font-size: 12px; line-height: 17px; color: var(--muted); margin-top: 2px; }
.step-trace .trace-row.is-current .trace-meta { color: var(--primary); }
/*
 * 「待接单」前面那颗浅黄小点（老板 2026-10-19）：
 * 大厅里已经挂出来、等人接的才是黄点；「待前道」没点、整行灰底 ——
 * 师傅一眼分得出「这单能不能接」和「还没轮到」。
 */
/* 「待接单」前面的浅黄小点：步骤明细和「全部客户 → 客户明细」共用 */
.trace-dot {
  display: inline-block; width: 6px; height: 6px; margin-right: 5px;
  border-radius: 50%; background: var(--warn); vertical-align: 1px;
}

.progress-block { margin-top: 10px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--panel-2); font-size: 14px; }
.record-row { display: block; width: 100%; text-align: left; font-family: inherit; color: var(--text); cursor: pointer; }
.record-title { font-size: 14px; }
.record-title .hall-arrow { margin: 0 4px; }
.record-step-no { color: var(--primary); font-weight: 500; }
/*
 * 一单的当前状态（师傅在我的记录里点开一单）：**一行**，不写提示语、不啰嗦。
 * 左：状态胶囊（待接单 / 我在做 / 某某在做 / 已完工 / 已发货 / 已取消 / 待开工）
 * 中：第 N 道 · 工序名    右：已完成/总道数
 * 老板 2026-10-14：「可以看到目前的单子在哪里卡着…」+「不要这种文字提示，文案要简洁」。
 */
.order-stage {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 .55rem; padding: 8px 10px;
  border-radius: var(--radius-sm); background: var(--bg-soft);
}
.order-stage .stage-pill {
  flex: none; padding: 1px 8px; border-radius: 999px;
  font-size: 12px; line-height: 18px; font-weight: 600; white-space: nowrap;
  background: var(--primary-soft); color: var(--primary);
}
.order-stage .stage-pill.warn { background: var(--warn-soft); color: var(--warn); }
.order-stage .stage-pill.ok { background: var(--ok-soft); color: var(--ok); }
.order-stage .stage-pill.muted { background: var(--bg-soft); color: var(--muted); }
.order-stage .stage-step {
  flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.order-stage .stage-count {
  flex: none; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
}

/*
 * 「当日完成情况」里每位师傅一行（可点开看他自己的今日明细）——
 * 老板 2026-10-14：「每个赝复师都单独看，不要放到一起」，行内两行、右侧箭头。
 */
.operator-row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 9px 12px; text-align: left; font-family: inherit; color: var(--text); cursor: pointer;
}
.operator-row .grow { display: block; min-width: 0; }
.operator-row .op-meta { display: block; line-height: 17px; }
.operator-row:active { background: var(--bg-soft); }

/*
 * 「全部工序」（赝复管理员，手机端要瘦）：两行 —— 客户·第N道 工序名 / 单号·师傅·剩余。
 * 老板 2026-10-14：「全部工序这里没有翻页，手机端显示臃肿」。
 */
.all-row { padding: 8px 10px; gap: 8px; }
/*
 * 手机上**不许截断**（老板 2026-10-14 截图：「手机上显示都是这样的」——
 * 工序名被截成「模具标记与灌蜡和…」、剩余时间被截成「剩余 4天2…」）：
 * 标题与副行都改成自动折行，宁可高一两行也别丢字。
 */
.all-row .all-title {
  font-size: 13.5px; line-height: 19px; font-weight: 600;
  white-space: normal; overflow-wrap: anywhere;
}
.all-row .all-meta { line-height: 16px; white-space: normal; overflow-wrap: anywhere; }
.all-row .all-side { display: flex; align-items: center; gap: 6px; flex: none; }

/*
 * 师傅端主标签（接单大厅 / 我的任务 / 我的记录 / 全部工序）：
 * 窄屏**自动换行**，四个都要看得见（老板 2026-10-14 截图：第 4 个「全部工…」被切掉）。
 * 与 §7 手机端「菜单项必须全部可见」同一条规矩。
 */
@media (max-width: 560px) {
  /**
   * 四个标签**一行放下**（老板 2026-10-14：换行「页面很乱」）——
   * 平均分宽 + 缩字号缩内边距，不换行也不横滑。
   *
   * ⚠️ 选择器必须带 `.segmented`：师傅端 / 派单台的分段标签与**管理端的左侧菜单**
   * 都用了 `id="tabs"`，只写 `#tabs button` 会把「平均分宽（flex: 1 1 0）」漏到管理端菜单上 ——
   * 2026-10-15 给管理端菜单加了「车间大屏 / 发货中心 / 派单台」三颗之后，
   * 4 个菜单项的宽度被挤成 8px（字叠在一起）。限定到 `.segmented#tabs` 就互不干扰。
   */
  .segmented#tabs { flex-wrap: nowrap; overflow: visible; }
  .segmented#tabs button { flex: 1 1 0; min-width: 0; padding: 0 4px; font-size: 12.5px; }
  .segmented#tabs button .badge { margin-left: 3px; padding: 0 4px; font-size: 10.5px; }
}
@media (max-width: 400px) {
  .segmented#tabs button { padding: 0 2px; font-size: 11.5px; }
}

/*
 * 「全部工序」那两个下拉（状态 / 师傅）**并排一行**（老板 2026-10-14：「两个拉下选择框，放一行」）：
 * 各占一半宽；宽屏才回到 170px 的窄下拉。
 */
#panel-all .filter-row { flex-direction: row; flex-wrap: nowrap; }
#panel-all .filter-row select { flex: 1 1 0; min-width: 0; max-width: none; }
@media (min-width: 560px) {
  #panel-all .filter-row select { flex: 0 0 auto; max-width: 170px; }
}

/*
 * 「当日完成情况 → 点开某位师傅」里的每道工序：工序名 + 几道，
 * 下面**新单一行、重做一行**，客户名全部列出（按拼音排），一行装不下就自动折行。
 * 老板 2026-10-14：「要显示所有的客户名字，不要等 17 位客户……新单的名字跟重做单的名字分开一行」。
 */
.op-step .op-step-head { font-size: 14px; line-height: 20px; }
.op-step .op-step-line { display: flex; align-items: flex-start; gap: 6px; margin-top: 3px; font-size: 12.5px; line-height: 17px; }
.op-step .op-tag {
  flex: none; padding: 0 6px; border-radius: var(--radius-sm);
  font-size: 11.5px; line-height: 17px; font-weight: 600;
}
.op-step .op-tag.new { color: var(--primary); background: var(--primary-soft); }
.op-step .op-tag.rework { color: var(--warn); background: var(--warn-soft); }
.op-step .op-names { color: var(--text-2); overflow-wrap: anywhere; }

/*
 * 「全部客户 → 明细」：上面是订单基本信息，下面是**整单每一道工序 + 谁在做**。
 * 每道工序下面按状态给一排小操作（老板 2026-10-14：改派 / 退回上一步 / 修改完成人）——
 * 手机上把操作放**下一行**，文字那行就能整宽显示，不挤成一坨。
 */
.op-detail-head {
  padding: 9px 12px; border-radius: var(--radius-sm); background: var(--bg-soft);
}
.op-detail-head .row + .row { margin-top: 4px; }
/*
 * 一行一道工序（老板 2026-10-14：「尽量放一行，放在步骤的后面一个按钮框，
 * 如果放不下的话就放右下角」）：
 *   · 放得下 → 工序名那一行**右边**就是按钮框（`margin-left: auto` 顶到最右）；
 *   · 放不下 → 按钮框自动换到下一行，同样靠右（= 右下角）。
 * `min-width` 给工序名留一个下限，否则文字会一直往左缩、按钮永远不换行。
 */
.all-step { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding: 9px 12px; }
.all-step .all-step-text { flex: 1 1 auto; min-width: 8.5rem; }
.all-step .all-step-title { font-size: 13.5px; line-height: 19px; }
.all-step .all-step-no { color: var(--primary); font-weight: 500; }
.all-step.is-current { border: 1px solid var(--primary-border, var(--border)); background: var(--primary-soft); }
.all-step .step-acts { flex: 0 0 auto; margin-left: auto; display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
.all-step .step-act {
  display: inline-flex; align-items: center; justify-content: center;
  height: 28px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--panel);
  color: var(--text); font-family: inherit; font-size: 12.5px; cursor: pointer;
}
.all-step .step-act:active { background: var(--primary-soft); border-color: var(--primary-border); }
.all-step .step-act.is-primary { color: var(--primary); border-color: var(--primary-border); }
.all-step .step-act.is-danger { color: var(--danger); border-color: var(--danger-border); }
/*
 * 「修改完成人」是纠错用的低频操作：**灰字 + 有框**（老板 2026-10-19：「修改完成人需要有个框」）。
 * 以前是透明无描边，看着像一行说明文字、不像按钮；现在给回正常描边，仍用灰字压住存在感，
 * 别跟「派单 / 改派」抢注意力。
 */
.all-step .step-act.is-quiet { color: var(--muted); border-color: var(--border); background: var(--panel); }
.all-step .step-act.is-quiet:active { background: var(--panel-2); color: var(--text); }
/*
 * 每道工序下面的**完工照片**（老板 2026-10-19）：
 * 单独占一整行（`flex: 1 0 100%` 顶到下一行），一行排 4–5 张 40px 缩略图，点开看大图。
 * 客户明细上半部分（抬头 / 工序文字 / 按钮）完全不动，只有真传了图的步骤才多这一行。
 */
.all-step .all-step-photos {
  flex: 1 0 100%; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px;
}
.all-step .all-step-photos .photo-thumb.mini {
  width: 44px; height: 44px; margin-right: 0;
}

/*
 * 「全部客户」一行 = 一个客户（订单）：左边客户名 + 加急/重做/状态，下面两行进度，
 * 右边一个「明细」。压紧一点，一屏多看几个客户（老板 2026-10-14：「不符合大厂开发的要求」）。
 */
.all-order-row { display: flex; align-items: center; gap: 8px; padding: 9px 12px; }
.all-order-row .grow { min-width: 0; }
.all-order-row .all-order-head { font-size: 14px; line-height: 20px; }
.all-order-row .all-order-head .badge { margin-left: 4px; }
.all-order-row .all-order-line { line-height: 17px; overflow-wrap: anywhere; }

/* 我的记录 / 历史记录：一个客户一行，框压小一点，一屏多看几条 */
#profile-history-list, #done-today-list { gap: 7px; margin-top: 8px; }
#profile-history-list .record-row, #done-today-list .record-row { padding: 9px 12px; }
#profile-history-list .record-title, #done-today-list .record-title { line-height: 19px; }
#profile-history-list .record-row .small, #done-today-list .record-row .small { line-height: 16px; }
/* 点开一条记录（一个订单）后的步骤列表：可上下滑动挑，再点进某一步看照片 */
.record-steps { display: flex; flex-direction: column; gap: 8px; }
.record-step-row {
  display: block; width: 100%; padding: 10px 12px; text-align: left;
  font-family: inherit; color: var(--text); cursor: pointer;
}
.record-step-row:hover { border-color: var(--primary-border); background: var(--panel-2); }

/* ---------- 排单员网页版工作台 ---------- */
/* 9 格（2026-10-28 加了「样品确认中」）：3 列刚好 3×3，4 列会剩一个孤零零的第 5 行第 1 格。 */
.kpi-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi-card {
  position: relative; display: flex; flex-direction: column; gap: 4px;
  padding: 16px; overflow: hidden; text-align: left;
  font-family: inherit; color: var(--text); cursor: pointer;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.kpi-card:hover { border-color: var(--primary-border); box-shadow: var(--shadow-md); transform: translateY(-1px); }
.kpi-card:active { transform: none; }
.kpi-card:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(51, 112, 255, .16); }
.kpi-card[data-tone="blue"] { --tone: var(--primary); --tone-soft: var(--primary-soft); }
.kpi-card[data-tone="amber"] { --tone: var(--warn); --tone-soft: var(--warn-soft); }
.kpi-card[data-tone="green"] { --tone: var(--ok); --tone-soft: var(--ok-soft); }
.kpi-card[data-tone="red"] { --tone: var(--danger); --tone-soft: var(--danger-soft); }
.kpi-card[data-tone="teal"] { --tone: #0d9488; --tone-soft: #e6fffb; }
.kpi-card[data-tone="violet"] { --tone: var(--purple); --tone-soft: var(--purple-soft); }
.kpi-card[data-tone="rose"] { --tone: #d91ad9; --tone-soft: #fff0fb; }
.kpi-card[data-tone="slate"] { --tone: var(--slate); --tone-soft: #f2f3f5; }
/* 样品确认中（2026-10-28）：用 info 青，跟 teal（本月业绩）/ violet / rose / slate 都区分得开 */
.kpi-card[data-tone="cyan"] { --tone: var(--info); --tone-soft: var(--info-soft); }
.kpi-head { display: flex; align-items: center; gap: 8px; }
.kpi-icon {
  display: grid; place-items: center; flex: none;
  width: 24px; height: 24px; border-radius: var(--radius-sm);
  background: var(--tone-soft, var(--primary-soft)); font-size: 13px;
}
.kpi-label { font-size: 13px; font-weight: 400; color: var(--muted); }
.kpi-value {
  font-size: clamp(20px, 1.6vw, 26px); font-weight: 600; line-height: 34px;
  letter-spacing: -.2px; font-variant-numeric: tabular-nums;
}
.kpi-value.alert { color: var(--danger); }

/* 第 8 个框：今日新增客户可以直接在这格里改（不是按钮，避免输入框嵌在按钮里） */
.kpi-card-edit { cursor: default; }
.kpi-card-edit:hover { transform: none; box-shadow: none; border-color: var(--border); }
.kpi-edit { display: flex; align-items: center; gap: 6px; }
.kpi-edit input {
  width: 96px; padding: 2px 10px; font-size: clamp(20px, 1.6vw, 26px); font-weight: 600;
  line-height: 34px; font-variant-numeric: tabular-nums;
  color: var(--text); background: var(--panel-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.kpi-edit input:hover { border-color: var(--primary-border); }
.kpi-edit input:focus { outline: none; border-color: var(--primary); background: var(--panel); }
.kpi-edit-unit { font-size: 13px; color: var(--muted); }

/* 数据框里的红色提醒：完工单还欠尾款时点它去收 */
.kpi-warn {
  margin-top: 2px; padding: 3px 8px; border-radius: var(--radius-sm);
  font-size: 12px; font-weight: 600; color: var(--danger);
  background: var(--danger-soft); border: 1px solid var(--danger-border, var(--danger-soft));
}
/* 数据框里的文字链接（第 8 个框的「看每天明细」） */
.kpi-link {
  margin-left: 6px; padding: 0; font: inherit; font-size: 12px;
  color: var(--primary); background: none; border: none; cursor: pointer;
}
.kpi-link:hover { text-decoration: underline; }
.kpi-sub { font-size: 12px; color: var(--muted); }
/**
 * 「超时风险」框：有客户交期很紧 / 已超期时，副行那行字变红加粗（2026-10-22 老板要求
 * 「排单员那边的这个超时异常也要改」）。大字仍是**工序**超时数，所以不改大字的颜色 ——
 * 客户有风险但工序一道没超时的时候，大字写 0 却变红最容易看懵。
 */
.kpi-card[data-risk="hot"] .kpi-sub { color: var(--danger); font-weight: 600; }
.kpi-chev {
  position: absolute; top: 16px; right: 14px; color: var(--muted); font-size: 12px;
  opacity: 0; transition: opacity .15s ease;
}
.kpi-card:hover .kpi-chev, .kpi-card:focus-visible .kpi-chev { opacity: 1; }

.notice {
  padding: 10px 14px; margin-bottom: 12px;
  background: var(--warn-soft); border: 1px solid var(--warn-border);
  border-radius: var(--radius); font-size: 13px; line-height: 20px;
}
.notice .mono { padding: 0 4px; border-radius: 4px; background: rgba(0, 0, 0, .05); }

.wbench { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-items: start; }
.wbench-main { min-width: 0; }
.wbench-tools {
  /* 一整排放下：状态页签 + 3 个下拉 + 搜索 + 下单时间 + 刷新 + 回收箱
     （2026-10-12 老板反馈「排版不合理，可以小一点整页放下」） */
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding-bottom: 12px; margin-bottom: 8px; border-bottom: 1px solid var(--border);
}
.wbench-tools .segmented { flex: none; }
.wbench-tools input[type="search"] { flex: 1 1 140px; min-width: 110px; max-width: 240px; }
.wbench-tools select { max-width: 110px; }
/* 日期框：默认那种 150px 太占地方，压到刚够显示「2026/10/12」+ 日历图标 */
.wbench-tools input[type="date"] { width: 118px; }
.wbench-tools .segmented button { padding: 5px 9px; }
.wbench-tools .btn { padding: 5px 10px; }

.filter-chip { margin: 10px 0 0; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 1px 6px 1px 8px; border-radius: 4px;
  background: var(--primary-soft); color: var(--primary); font-size: 12px;
}
.chip-x { padding: 0 2px; border: none; background: transparent; color: inherit; font-size: 13px; line-height: 1; cursor: pointer; }

.panel-top { display: flex; align-items: center; gap: 10px; }
.panel-avatar {
  display: grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: var(--radius);
  background: var(--primary-soft); color: var(--primary); font-size: 16px; font-weight: 600;
}
.panel-name { font-size: 16px; font-weight: 600; }
.panel-contact { margin-top: 12px; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.contact-row { display: flex; gap: 10px; padding: 7px 0; font-size: 14px; }
.contact-row + .contact-row { border-top: 1px solid var(--border-2); }
.contact-row .k { flex: 0 0 4.6rem; color: var(--muted); font-size: 13px; }
.contact-row .v { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.panel-block { margin-top: 16px; }
.panel-block h4 { margin-bottom: 6px; color: var(--muted); font-size: 13px; font-weight: 400; }
.panel-block p { margin-bottom: 6px; }
.panel-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.panel-actions .btn { flex: 1 1 auto; }

.step-list { list-style: none; margin: 0; padding: 0; }
.step-item {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 7px 0; border-top: 1px solid var(--border-2); font-size: 14px;
}
.step-item:first-child { border-top: none; }
/**
 * 「8.5 道 · 样品确认」（2026-10-28 老板：「是加一道工序，等于说第 8.5 道工序」）：
 * 夹在主体制作与后道之间的一道特殊工序 —— 用信息色把它和普通工序区分开，
 * 做完（客户认可且样品收回）后转成普通已完成样式。
 */
.step-item.step-gate {
  background: var(--info-soft);
  border-top: 1px dashed var(--info-border);
  border-left: 3px solid var(--info);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin: 4px 0;
}
.step-item.step-gate .step-no { color: var(--info); font-weight: 600; }
/**
 * 「8.5 道」的三种不用动手的状态（2026-10-28 老板：「这个步骤需要展示在所有订单里面」）：
 * 不走样品 / 还没轮到 / 已跳过 —— 都摆出来（工序编号才连贯、一眼看出这单走不走样品），
 * 但不抢眼：去掉信息色底，只留一条灰虚线，说明文字里已经写清「这一道现在算什么」。
 */
.step-item.step-gate.gate-idle {
  background: transparent;
  border: 1px dashed var(--border-2);
  border-left: 3px solid var(--border-2);
  opacity: 0.85;
}
.step-item.step-gate.gate-idle .step-no { color: var(--muted); }
/* 流程块里的「样品确认待处理 · 去处理」：跟流程条留一点间距（2026-10-28） */
.sample-jump { margin-top: 8px; }
.step-item.step-gate.done {
  background: var(--ok-soft);
  border-top-color: var(--ok-border);
  border-left-color: var(--ok);
}
.step-item.step-gate.done .step-no { color: var(--ok); }
/**
 * 工序行右侧的按钮组（派单 / 改派 / 强制释放 …）：放不下就整组换行、仍然靠右，
 * 与师傅端「全部客户 → 明细」里那套摆法一致（老板 2026-10-14 定的）。
 * ⚠️ 跳步派单之后**每一道（11 道）都会有一个「派单」按钮**，`.step-item` 必须允许换行 ——
 * 只靠 flex 收缩的话，手机上（390px）这一行会横向溢出 100px+（2026-10-14 实测，
 * 回归脚本 `_verify-mobile-order-modal.mjs`）。
 */
.step-item .step-acts { margin-left: auto; display: inline-flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
/* 允许收缩：文字块不设下限，宁可整行换行也不要撑破弹层 */
.step-item .grow { min-width: 0; }
/* 「师傅名 + 剩余倒计时 + 状态徽标」是一个整组：桌面上跟着行内排，手机整组换行 */
.step-item .step-meta { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
/**
 * 手机：一行里塞不下「工序名 + 师傅 + 倒计时 + 徽标 + 按钮」——
 * 固定成三行：①序号 + 工序名 · 时间 ②师傅 · 倒计时 · 状态 ③按钮（靠右）。
 * 断行位置固定下来，11 道工序看下来才是齐的。
 */
@media (max-width: 640px) {
  .step-item .step-meta { flex-basis: 100%; }
  .step-item [data-step-operator] { min-width: 0; }
  .step-item .step-acts { flex-basis: 100%; margin-left: 0; }
}
/* 工序行右侧的赝复师名字（2026-10-14 老板要求从工序名后面挪到右边）：
   固定占位 + 右对齐，保证每一行的「剩余倒计时 / 状态徽标 / 干预按钮」上下对齐 */
.step-item [data-step-operator] { display: inline-block; min-width: 4.2rem; text-align: right; }
.step-no {
  display: grid; place-items: center; flex: none;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--bg-soft); color: var(--muted); font-size: 12px; font-weight: 500;
}
.step-item.done .step-no { background: var(--ok-soft); color: var(--ok); }
.step-item.late .step-no { background: var(--danger-soft); color: var(--danger); }
.step-item.done b { color: var(--muted); font-weight: 400; }

.source-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; }
.chk-group { display: inline-flex; align-items: center; gap: 12px; }
.chk-group + .chk-group { padding-left: 16px; border-left: 1px solid var(--border); }
.chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; line-height: 18px; white-space: nowrap; cursor: pointer; user-select: none; }
.chk input {
  position: relative; flex: none; width: 16px; height: 16px; margin: 0;
  appearance: none; -webkit-appearance: none;
  background: var(--panel); border: 1px solid var(--border); border-radius: 4px; cursor: pointer;
}
.chk input:checked { background: var(--primary); border-color: var(--primary); }
.chk input:checked::after {
  content: '✓'; position: absolute; inset: 0;
  display: grid; place-items: center; color: #fff; font-size: 11px; line-height: 1;
}
.chk input:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(51, 112, 255, .16); }

.money-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 12px; font-size: 14px; }
.money-grid > div { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }
.money-grid .k { color: var(--muted); font-size: 13px; }
.money-grid b { font-weight: 500; font-variant-numeric: tabular-nums; }
.money-grid b.warn { color: var(--warn); }
.money-grid b.ok { color: var(--ok); }
.money-grid b.danger { color: var(--danger); }

.flow-list { margin-top: 10px; border-top: 1px solid var(--border); }
.flow-row { display: flex; align-items: baseline; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border-2); font-size: 13px; }
.flow-row:last-child { border-bottom: none; }
.flow-plus { color: var(--ok); }
.flow-minus { color: var(--danger); }

.stat-summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
/* 收款明细头部的分项账（2026-10-29）：一行 chips，手机上自动换行。
   只有「维修费 / 退款」上色 —— 其余保持中性，别让每一格都抢眼（§0.0.1 ①砍噪音）。 */
.money-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.money-chips > span {
  font-size: 12px; line-height: 18px; padding: 2px 7px; border-radius: 4px;
  background: var(--panel-2); color: var(--muted); font-variant-numeric: tabular-nums;
}
.money-chips > span.repair { background: var(--primary-soft); color: var(--primary); }
.money-chips > span.refund { background: var(--danger-soft); color: var(--danger); }
.stat-body { max-height: 62vh; overflow: auto; }
.stat-row { display: flex; align-items: center; gap: 10px; padding: 12px 2px; border-top: 1px solid var(--border-2); }
.stat-row:first-child { border-top: none; }
.stat-row:hover { background: var(--panel-2); }
.stat-row-title { font-weight: 500; }
.stat-row-meta { display: flex; flex-wrap: wrap; gap: 2px 12px; margin-top: 2px; font-size: 12px; color: var(--muted); }
.stat-row-note { margin-top: 2px; font-size: 12px; color: var(--warn); }
.stat-row-money { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; font-size: 13px; }
.stat-amount { white-space: nowrap; font-variant-numeric: tabular-nums; }
.stat-amount.plus { color: var(--ok); }
.stat-amount.minus { color: var(--danger); }

/* ---------------------------------------------------------------- *
 * 「超时风险」弹层（2026-10-27 重做）
 * 老板两次反馈：①「点开看的太模糊了，不够清晰」→ 分档着色 + 写清交期来源；
 * ②「我需要点开能看这个订单的明细啊，而且这个太简陋了」→ 整行可点 + 信息做足。
 * 一行四块：客户/单号/渠道 → 生产进度 → 哪天到期·怎么算的·还剩几天 → 「明细」按钮。
 * ---------------------------------------------------------------- */
.stat-row-due {
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid var(--border-2);
  border-left: 3px solid var(--border-2);
  border-radius: var(--radius-sm);
  margin-top: 6px;
}
.stat-row-due:first-child { margin-top: 0; }
.stat-row-due[data-pick] { cursor: pointer; }
/**
 * 着色（2026-10-27 老板两次反馈后定稿）：
 * 第一次整行铺粉底 → 「不要全是红色的，这个太丑了」。
 * 根因是分档太宽（管理员设的「很紧」≤10 天，列表十几单全落进去），不是颜色本身。
 * 现在**只有真的急才上色**，其它行一律素的：
 *   is-overdue = 已过交期（红）· is-soon = 3 天内（橙）· 其余无类名。
 * 上色只动「左边一道细条 + 天数大字」，不铺底色。
 */
.stat-row-due.is-overdue { border-left-color: var(--danger); }
.stat-row-due.is-overdue .due-risk-days .countdown { color: var(--danger); }
.stat-row-due.is-soon { border-left-color: var(--warn); }
.stat-row-due.is-soon .due-risk-days .countdown { color: var(--warn); }
.stat-row-due[data-pick]:hover { background: var(--panel-2); }
.stat-row-due .stat-row-title { font-size: 14px; line-height: 21px; }
/* 单号 / 渠道 / 下单日期 / 生产进度并成一行，行高压得住 */
.stat-row-due .stat-row-meta { margin-top: 1px; gap: 2px 10px; align-items: center; }

/* 生产进度：进度条 + 已完成 X/Y + 当前第几道谁在做（跟 meta 同一行） */
.due-risk-line { display: inline-flex; align-items: center; gap: 6px; }
.due-risk-bar {
  flex: 0 0 64px; height: 4px; border-radius: 2px;
  background: var(--bg-soft); overflow: hidden;
}
.due-risk-bar > span { display: block; height: 100%; background: var(--primary); }

/* 右侧：剩几天（大字）+ 哪天到期 · 这个日子怎么来的（小字） */
.due-risk-when { flex: 0 0 auto; text-align: right; min-width: 108px; }
.due-risk-days .countdown { font-size: 15px; }
.due-risk-source { margin-top: 1px; font-size: 12px; color: var(--muted); white-space: nowrap; }
/* 「明细」按钮跟行同高就行，别被 flex 拉成一整块 */
.stat-row-due > .btn { flex: 0 0 auto; align-self: center; }

/* 顶部四颗分档小标签：已超期 / 3 天内 / 7 天内 / 没定交期（只是说明，列表不切段） */
.due-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.due-chip {
  display: inline-flex; align-items: center; gap: 4px;
  height: 22px; padding: 0 8px; border-radius: 11px;
  font-size: 12px; color: var(--text-2); background: var(--bg-soft);
}
.due-chip b { font-variant-numeric: tabular-nums; }
.due-chip.is-danger { background: var(--danger-soft); color: var(--danger); }
.due-chip.is-warn { background: var(--warn-soft); color: var(--warn); }
.due-chip.is-gray { background: var(--bg-soft); color: var(--muted); }

@media (max-width: 720px) {
  /* 手机上把「剩几天」挪到客户名右边一行，别把单号挤成两行 */
  .stat-row-due { flex-wrap: wrap; }
  .stat-row-due .grow { flex: 1 1 100%; }
  .due-risk-when { min-width: 0; text-align: left; margin-left: auto; }
  .due-risk-source { white-space: normal; }
}

.rework-box {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px;
  margin-top: 12px; padding: 12px 14px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.rework-box .switch { margin: 0; }

/* ---------- 移动端 ---------- */
@media (max-width: 640px) {
  .grid.cols-4, .grid.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .container { padding: 12px; }
  .card { padding: 14px; }
  .grid { gap: 10px; }
  .kpi .value { font-size: 20px; line-height: 28px; }
  .kpi .sub { font-size: 11px; line-height: 16px; }
  .topbar { min-height: 52px; padding: 0 12px; }
  .topbar .who { font-size: 11px; }
  /* 车间里是戴手套按屏幕：手机上最小的按钮也留足 36px */
  .btn { min-height: 36px; }
  .btn-sm { min-height: 30px; padding: 0 10px; }
  .btn-lg { min-height: var(--tap); }
  .btn-xl { min-height: 52px; }

  .filter-block { padding: 10px 12px; margin-bottom: 10px; }
  .filter-row { flex-direction: column; align-items: stretch; gap: 6px; }
  .filter-label { font-size: 12px; }
  .filter-row .segmented {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 2px;
  }
  .filter-row .segmented::-webkit-scrollbar { display: none; }
  .filter-row .segmented button { flex: 0 0 auto; min-height: 28px; padding: 0 10px; font-size: 13px; }

  .task-actions { flex-direction: column; align-items: stretch; gap: 8px; }
  .task-actions .btn { width: 100%; }
  .task-card .step-name { font-size: 15px; }
  .step-chip { font-size: 11px; }
  /* 详情弹层里的照片墙：等比例裁切的小方格，一行 4-5 张，不再一张图占满屏 */
  .photo-grid.detail { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: 6px; }
}

/* 发货中心：手机端四个 Tab 一行放下（① 发工具 ② 收模具 ③ 线下取模 ④ 发成品） */
@media (max-width: 640px) {
  body.fulfillment .topbar { flex-wrap: nowrap; gap: 6px; }
  body.fulfillment .topbar .brand { white-space: nowrap; }
  body.fulfillment #ff-tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 3px; overflow: visible; }
  body.fulfillment #ff-tabs button { flex: none; min-height: 40px; padding: 4px 2px; font-size: 12.5px; gap: 3px; }
  /* 窄屏下数量角标再收一点，保证四个 Tab 都放得下 */
  body.fulfillment #ff-tabs .badge { padding: 0 4px; min-width: 18px; }
}

/* 超窄屏（≤340px，老 iPhone SE 这类）：再缩一点字号，避免「线下取模 6」被裁掉 */
@media (max-width: 340px) {
  body.fulfillment #ff-tabs button { font-size: 11.5px; }
  body.fulfillment #ff-tabs .badge { min-width: 16px; padding: 0 3px; }
}

/* ---------- 手机端顶栏：标题不许被挤成「一列一个字」 ---------- */
@media (max-width: 640px) {
  /* 放不下就换行，别把标题挤成竖排（2026-10-05 老板手机截图反馈） */
  .topbar { flex-wrap: wrap; row-gap: 8px; min-height: 52px; padding: 8px 12px; }
  .topbar .brand { flex: 1 1 auto; min-width: 0; }
  /* 标题单行显示，位置不够给省略号；绝不一个字符换一行 */
  .topbar .brand > div { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .topbar .who { white-space: nowrap; }

  /* 排单员工作台：动作按钮全部换到标题下面。
     按钮数量**随权限变 1~5 颗**：新增订单 / 全部订单 / 发货中心 / 全部工序 / 派单台 ——
     每一颗都必须出现在这条规则里，漏掉的那颗会留在第一行把标题挤扁（2026-10-29 修：
     「全部工序」「派单台」是 10-13/14 后加的，一直没进这条规则）。 */
  body.planner-web .topbar .brand .small { display: none; }
  body.planner-web #user-box { margin-left: auto; }
  body.planner-web #topbar-new-order,
  body.planner-web #order-center-btn,
  body.planner-web #fulfillment-entry,
  body.planner-web #all-tasks-entry,
  body.planner-web #dispatch-entry { order: 10; flex: 1 1 30%; min-height: 38px; }
}

/* ---------- 弹层：内容自己滚，底部按钮钉住（2026-10-22 改成**所有宽度**都生效） ---------- */
/*
 * 以前整个 .modal 一起滚：手机上要滑很久才看得到「保存修改 / 取消 / 提交重做」，
 * 看着就像「按钮没了」（2026-10-05 手机反馈「点开就是这页，要滑动」）。
 *
 * 约定：**凡是带 .modal-actions 的弹层，根节点必须同时带 .modal-form**，
 * 并把可滚动的内容放进「滚动区」；标题留在上面，.modal-actions 钉在下面。
 * 滚动区按弹层来源认其中一个（谁在谁生效）：
 *   - formDialog 结构：> form[data-dialog-form]
 *   - 手写弹层：> .modal-body（推荐）或 > .rework-body
 * 订单明细（.modal-detail）另走 .modal-detail-body。
 *
 * ⚠️ 2026-10-22 老板报「研发提交弹层没法滑动、点不了确认」：
 * 这套规则以前**只在 ≤640px** 才生效 —— 换到大一点的屏、或手机键盘弹起把可视高度压小，
 * 就不再走 flex：整个 .modal 变成一个超长块，`.modal-actions` 被挤出屏幕，
 * 而 backdrop 是 fixed 的、页面本身不滚 → 看着就是「按钮没了，也滑不动」。
 * 现在 **form 这套规则所有宽度都生效**（modal-detail 仍只在手机档，它本来就只在手机上开）。
 */
.modal.modal-form {
  display: flex; flex-direction: column;
  max-height: 88vh; max-height: 88dvh; overflow: hidden;
}
.modal.modal-form > form[data-dialog-form],
.modal.modal-form > .modal-body,
.modal.modal-form > .rework-body {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
/* 标题 / 说明 / 按钮这些不该被挤扁：只有滚动区允许伸缩 */
.modal.modal-form > *:not(form[data-dialog-form]):not(.modal-body):not(.rework-body) { flex: 0 0 auto; }
.modal.modal-form > .modal-actions {
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
}

@media (max-width: 640px) {
  .modal.modal-detail {
    display: flex; flex-direction: column;
    max-height: 92vh; max-height: 92dvh; overflow: hidden;
  }
  .modal.modal-detail > .modal-detail-body {
    flex: 1 1 auto; min-height: 0;
    overflow-y: auto; -webkit-overflow-scrolling: touch;
  }
  .modal.modal-detail > *:not(.modal-detail-body) { flex: 0 0 auto; }
  /* 弹层里按钮大一点，手指好点 */
  .modal .modal-actions .btn { min-height: 40px; }
}

/* ---------- 手机端表单：字号必须 ≥16px ---------- */
/*
 * iOS Safari 的固定行为：**聚焦字号小于 16px 的输入框时，会自动把整个页面放大**。
 * 排单员点「+ 新增订单」会自动聚焦「客户姓名」，于是页面立刻被放大（2026-10-05 手机反馈）。
 * 这里在手机上把所有输入框 / 下拉 / 文本域统一提到 16px，就不会再跳了 —— 别再改小。
 */
@media (max-width: 640px) {
  input[type="text"], input[type="password"], input[type="tel"], input[type="number"],
  input[type="date"], input[type="month"], input[type="time"], input[type="search"],
  input[type="email"], input[type="url"],
  select, textarea { font-size: 16px; }
}

/* ---------- 桌面 / 平板微调 ---------- */
@media (min-width: 1100px) {
  .sticky-actions { position: static; padding: 0; background: transparent; border: none; }
}
/* 9 格之后一律 3 列（2026-10-28）：4 列会剩一个落单的第 9 格，看着像漏了一张。 */
@media (max-width: 1180px) { .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1100px) { .wbench { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 900px) {
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  /* 9 格排 2 列会多出一个孤格 → 让最后一格通栏，等于「4 行 2 格 + 1 行整宽」 */
  .kpi-grid > .kpi-card:last-child { grid-column: 1 / -1; }
  .wbench-tools input[type="search"] { max-width: none; }
}
@media (max-width: 430px) {
  .kpi-card { padding: 12px; }
  .kpi-value { font-size: 18px; line-height: 26px; }
  .kpi-sub { font-size: 11px; }
  .money-grid { grid-template-columns: minmax(0, 1fr); }
}

/* 同一份数据的两种呈现：PC 表格 / 手机卡片 */
@media (max-width: 900px) { .hide-sm { display: none !important; } }
@media (min-width: 901px) { .hide-lg { display: none !important; } }

/* ---------- 深色模式补丁 ---------- */
/* ---------- 整单流程条：一个环节一盏灯 ---------- */
.btn.btn-muted { opacity: .55; cursor: not-allowed; }

/* 师傅端接单大厅：重做（返修）卡上的返修起点选择 */
.hall-redo { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .45rem; }
.hall-start {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin-top: .5rem; padding: .5rem .6rem;
  border: 1px dashed color-mix(in srgb, var(--primary) 45%, var(--border));
  border-radius: 10px; background: color-mix(in srgb, var(--primary) 6%, var(--panel));
}
.hall-start-label { font-size: .82rem; font-weight: 650; color: var(--primary); }
.hall-start select { flex: 1 1 200px; min-width: 150px; }
.hall-start-hint { flex: 1 1 100%; font-size: .74rem; color: var(--muted); line-height: 1.35; }

/* ---------- 提交重做（返修）弹窗 ---------- */
.modal.modal-rework { width: 100%; max-width: min(94vw, 720px); padding: 0; overflow: hidden; }
.rework-head {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  padding: 1rem 1.15rem .7rem; border-bottom: 1px solid var(--border);
}
.rework-head h3 { margin: 0; font-size: 1.02rem; }
.rework-body { padding: .9rem 1.15rem .35rem; }
.rework-section { margin-bottom: 1rem; }
.rework-label {
  display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap;
  font-size: .84rem; font-weight: 650; color: var(--text); margin-bottom: .45rem;
}
.rework-chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.rework-chips .chk {
  padding: .4rem .6rem; border: 1px solid var(--border); border-radius: 10px;
  background: var(--panel-2); font-size: .88rem; gap: .4rem;
}
.rework-chips .chk:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--border)); }
.rework-chips .chk:has(input:checked) {
  border-color: var(--primary); background: color-mix(in srgb, var(--primary) 8%, var(--panel));
}
.rework-chips .chk-hint { font-size: .72rem; color: var(--muted); font-weight: 400; }
.rework-modes { display: grid; gap: .5rem; }
.rework-mode {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto;
  column-gap: .5rem; row-gap: .15rem; align-items: center;
  padding: .6rem .7rem; border: 1px solid var(--border); border-radius: 12px;
  background: var(--panel); cursor: pointer;
}
.rework-mode input { grid-row: 1 / span 2; width: 17px; height: 17px; accent-color: var(--primary); }
.rework-mode .rm-title { font-weight: 650; font-size: .9rem; }
.rework-mode .rm-desc { font-size: .76rem; color: var(--muted); line-height: 1.35; }
.rework-mode.active {
  border-color: var(--primary); background: color-mix(in srgb, var(--primary) 7%, var(--panel));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent);
}
.rework-mode.active .rm-title { color: var(--primary); }
.rework-due-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.rework-due-row input[type="date"] { max-width: 190px; }
.rework-due-row input[type="time"] { max-width: 130px; }
.modal-rework .modal-actions { padding: 0 1.15rem 1rem; margin-top: .2rem; }

/* ---------- 重做记录（售后追责） ---------- */
.redo-round {
  padding: .6rem .7rem; margin-bottom: .5rem;
  border: 1px solid var(--border); border-radius: 12px; background: var(--panel-2);
}
.redo-round:last-child { margin-bottom: 0; }
.redo-round-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .25rem; }

.flowbar {
  display: flex; align-items: flex-start;
  margin-top: .2rem; padding: .55rem .5rem .6rem;
  border: 1px solid var(--border); border-radius: 14px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
}
.flow-step {
  flex: 1 1 0; min-width: 62px;
  display: flex; flex-direction: column; align-items: center; gap: .2rem;
  padding: .35rem .2rem; border-radius: 12px; text-align: center;
}
.flow-dot {
  width: 26px; height: 26px; border-radius: 50%; flex: none; position: relative;
  display: grid; place-items: center;
  border: 2px solid var(--border); background: var(--panel);
}
.flow-dot::after { content: ''; }
.flow-label { font-size: .82rem; font-weight: 650; line-height: 1.2; }
.flow-note { font-size: .68rem; color: var(--muted); line-height: 1.3; overflow-wrap: anywhere; }

.flow-step.done .flow-dot {
  border-color: var(--ok); background: var(--ok);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--ok) 32%, transparent);
}
.flow-step.done .flow-dot::after { content: '✓'; color: #fff; font-size: .8rem; font-weight: 700; }

.flow-step.current { background: color-mix(in srgb, var(--primary) 8%, transparent); }
.flow-step.current .flow-dot {
  border-color: var(--primary); background: var(--primary);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--primary) 16%, transparent);
}
.flow-step.current .flow-dot::after {
  content: ''; width: 8px; height: 8px; border-radius: 50%; background: #fff;
}
.flow-step.current .flow-label { color: var(--primary); }
.flow-step.current .flow-note { color: color-mix(in srgb, var(--primary) 62%, var(--text)); }

.flow-step.todo .flow-label { color: var(--muted); font-weight: 600; }
.flow-step.cancel .flow-dot { border-color: var(--danger); background: var(--danger); }
.flow-step.cancel .flow-dot::after { content: '×'; color: #fff; font-weight: 700; }

/* 环节之间的连接线：走过的部分染绿 */
.flow-line {
  flex: 1 1 14px; min-width: 10px; height: 2px; border-radius: 2px;
  background: var(--border); margin-top: 19px;
}
.flow-line.reached { background: color-mix(in srgb, var(--ok) 65%, var(--border)); }

/* 列表里的迷你流程（一排小点） */
.flow-dots { display: inline-flex; align-items: center; gap: .18rem; }
.flow-dot-sm {
  width: 9px; height: 9px; border-radius: 50%; display: inline-block;
  background: var(--bg-soft); border: 1px solid var(--border);
}
.flow-dot-sm.done { background: var(--ok); border-color: var(--ok); }
.flow-dot-sm.current { background: var(--primary); border-color: var(--primary); }
.flow-dot-sm.skip { background: transparent; border-style: dashed; }
.flow-dot-sm.cancel { background: var(--danger); border-color: var(--danger); }

@media (max-width: 640px) {
  .flowbar { padding: .5rem .3rem .55rem; }
  .flow-step { min-width: 54px; padding: .3rem .1rem; }
  .flow-dot { width: 22px; height: 22px; }
  .flow-line { margin-top: 16px; min-width: 8px; }
  .flow-label { font-size: .74rem; }
  .flow-note { font-size: .62rem; }
}

[data-theme="dark"] table.table tr.overdue-row { background: #2a1a1a; }
[data-theme="dark"] .item.overdue { background: #221614; }
[data-theme="dark"] .notice .mono { background: rgba(255, 255, 255, .08); }
[data-theme="dark"] .toast { background: #2b3038; }
[data-theme="dark"] input:hover { border-color: #3a4048; }

/* ---------- 打印（拣货 / 工单） ---------- */
@media print {
  .topbar, .sticky-actions, .tabbar, .btn, .segmented { display: none !important; }
  body { background: #fff; }
  .card, .item { border-color: #ddd; box-shadow: none; }
}

/* ---------- 兼容性兜底提示 ----------
   给「不支持 ES Module 的旧浏览器 / 被云加速拦掉脚本的浏览器」用：
   故意只用最朴素的 CSS（不用 :has / color-mix / aspect-ratio），保证老内核也看得见。 */
.compat-warning {
  margin: 0 auto 12px;
  max-width: 760px;
  padding: 12px 16px;
  border: 1px solid #ffcf8b;
  border-left: 4px solid #ff7d00;
  border-radius: 6px;
  background: #fff7e8;
  color: #1d2129;
  font-size: 14px;
  line-height: 22px;
}
.compat-warning b { font-weight: 600; }
.compat-warning .small { font-size: 12px; color: #86909c; }
@media (prefers-color-scheme: dark) {
  html[data-theme="dark"] .compat-warning { background: #33260f; border-color: #4d3a17; color: #e5e6eb; }
}


/* =============================================================
   数据看板（管理端「全厂看板」专用视觉层，2026-09-29 按老板手绘稿改版）
   目标：像专业数据看板那样——彩色分区、数字突出、说明文字用深灰而不是浅灰。
   只新增 .dash-* / .tile-* / .rank-* 这些新 class，不动原有任何样式。
   ============================================================= */
:root {
  --q1: #ffb400;   /* 第一季度 黄 */
  --q2: #7b5cff;   /* 第二季度 紫 */
  --q3: #00b42a;   /* 第三季度 绿 */
  --q4: #f53f3f;   /* 第四季度 红 */
  --dash-ink: #1d2129;      /* 数字正文 */
  --dash-ink-2: #4e5969;    /* 次要说明（比原 --muted 深一档，看得清） */
  --dash-line: #e5e6eb;
}

/* ---------- 顶部标题条 ---------- */
.dash-hero {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  padding: .95rem 1.1rem; margin-bottom: .9rem;
  border-radius: var(--radius-lg);
  background: linear-gradient(120deg, #6f4bff 0%, #7b5cff 45%, #4a7dff 100%);
  color: #fff; box-shadow: 0 8px 22px rgba(111, 75, 255, .22);
}
.dash-hero .hero-icon {
  display: grid; place-items: center; flex: none;
  width: 38px; height: 38px; border-radius: 11px;
  background: rgba(255, 255, 255, .22); font-size: 20px;
}
.dash-hero h2 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: .2px; }
.dash-hero .hero-sub { font-size: 12.5px; color: rgba(255, 255, 255, .88); margin-top: 1px; }
.dash-hero .hero-chips { display: flex; gap: .4rem; flex-wrap: wrap; margin-left: auto; }
.dash-hero .hero-chip {
  padding: .28rem .6rem; border-radius: 999px; font-size: 12.5px; line-height: 18px;
  background: rgba(255, 255, 255, .18); color: #fff; white-space: nowrap;
}
.dash-hero .hero-chip b { font-weight: 600; }

/* ---------- 数据小卡（流水框） ---------- */
.tile {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: .35rem;
  padding: .95rem 1.05rem .9rem;
  background: var(--panel); border: 1px solid var(--dash-line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.tile::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--tile-tone, var(--primary));
}
.tile-head { display: flex; align-items: center; gap: .5rem; }
.tile-title { font-size: 13px; font-weight: 600; color: var(--dash-ink); }
.tile-title .tile-icon { margin-right: .2rem; }
.tile-more {
  margin-left: auto; padding: .12rem .5rem; border-radius: 999px;
  border: 1px solid var(--dash-line); background: var(--panel-2);
  font-size: 12px; color: var(--dash-ink-2); cursor: pointer; white-space: nowrap;
}
/* 卡片右上角两个按钮（明细 + 筛选）包在一起时：整组靠右，组内两个紧挨着（别再各自 auto） */
.tile-head > span .tile-more { margin-left: 0; }
.tile-more:hover { border-color: var(--tile-tone, var(--primary)); color: var(--tile-tone, var(--primary)); background: #fff; }
.tile-value {
  font-size: clamp(21px, 1.9vw, 28px); font-weight: 700; line-height: 34px;
  color: var(--tile-tone, var(--dash-ink)); font-variant-numeric: tabular-nums;
}
/**
 * 「完成单数」卡的大字小一号（2026-10-21 老板：「把 21 单给缩小一点」）。
 *
 * 为什么单独调：这张卡的大字只有「21 单」三四个字，跟旁边的 `¥69,729.99` 用同一档字号时，
 * 字符少反而显得更粗更抢眼，一排卡片并排看着不齐。缩到 24px（上限）跟金额视觉重量接近。
 * 只影响 `.tile-done` 这一张，别把 `.tile-value` 本体改小（其它卡片的金额靠它）。
 */
.tile-done .tile-value { font-size: clamp(19px, 1.5vw, 24px); line-height: 30px; }
.tile-sub { font-size: 12.5px; line-height: 19px; color: var(--dash-ink-2); }
.tile-sub b { color: var(--dash-ink); font-weight: 600; }
.tile-sub .up { color: var(--ok); font-weight: 600; }
.tile-sub .down { color: var(--danger); font-weight: 600; }
.tile-sub .warn { color: var(--warn); font-weight: 600; }
/* 副行里「可以点的字」（本月退款 › / 今日完成工序 N 道 ›）：用主色，别跟旁边的说明文字一样灰 */
.tile-sub .tile-link { color: var(--primary); font-weight: 600; }
/* 链接里的数字跟着链接走一个颜色（否则「46 道 ›」会一半黑一半蓝） */
.tile-sub .tile-link b { color: inherit; font-weight: 600; }
/* 「完成单数」卡副行四段并成一行（2026-10-24 老板：「本月 31 单 · 手指 35 根 · 手套 2 个 ·
 * 今日完成工序 16 道 › 放在一排吧」）。用 flex 折行：宽屏一排，窄屏按段换行、分隔点跟着段走。 */
.tile-line { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: .3rem; row-gap: .05rem; }
.tile-line > span { white-space: nowrap; }
.tile-line .tile-sep { color: var(--dash-ink-2); opacity: .75; }

/**
 * 卡片高度**跟着内容走**（老板 2026-10-15：「提示框的文字是不要太偏上了…那么多空白的地方留着干嘛」）。
 * `.grid` 默认 `align-items: stretch`：同一行里矮的卡会被拉到和最高的卡一样高，
 * 底部就空出一大片（销售流水有三行说明、待收尾款只有一行 → 待收尾款下面空 40 多个像素）。
 * 看板这四行改成 `start`，每张卡正好包住自己的内容。
 */
#dash-flow-row1, #dash-flow-row2, #dash-flow-row3, #dash-flow-row4 { align-items: start; }

/* ---------- 研发 | 反馈：同一张卡左右并排（2026-10-24 老板要求） ----------
 * 老板原话：「在研发这个框这里再加一个分框 变成一个是研发 一个是反馈」。
 * 外层仍是跟重做 / 售后同款的 `.tile.research-card`（左边 4px 色条是整张卡的；
 * 2026-10-24 老板定：除「超时 / 异常」红色外，所有卡一律主蓝），
 * 里面套一层 `.tile-halves`，两个半格之间用一条竖虚线分隔。
 * ⚠️ 只有研发一半时（老后端还没有反馈接口 / 接口挂了）：只剩一个半格，自然铺满，不会多出分隔线。
 */
.research-card .tile-halves { display: flex; align-items: stretch; gap: 0; }
.research-card .tile-half {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; gap: .35rem;
}
.research-card .tile-half + .tile-half {
  border-left: 1px dashed var(--dash-line);
  margin-left: .8rem; padding-left: .8rem;
}
@media (max-width: 600px) {
  .research-card .tile-half + .tile-half { margin-left: .55rem; padding-left: .55rem; }
  .research-card .tile-half .tile-value { font-size: 20px; line-height: 26px; }
  .research-card .tile-half .tile-title { font-size: 12.5px; }
}

/* ---------- 反馈列表（管理端「反馈」弹层，2026-10-24） ---------- */
.fb-list { display: flex; flex-direction: column; gap: .5rem; }
.fb-card {
  border: 1px solid var(--dash-line); border-radius: 10px;
  background: var(--panel-2); padding: .55rem .7rem;
}
.fb-head { display: flex; align-items: center; gap: .5rem; }
.fb-head .btn { margin-left: auto; }
.fb-time { color: var(--dash-ink-2); font-size: 12px; }
.fb-body {
  margin-top: .25rem; font-size: 13.5px; line-height: 1.5;
  color: var(--dash-ink); white-space: pre-wrap; word-break: break-word;
}
.fb-photos { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .4rem; }

/* ---------- 「研发情况」弹层里的品类 chips（2026-10-22） ----------
 * 研发卡本身走的是通用 `.tile`（跟重做 / 售后同一套，见 admin.js 的 `researchCardHtml()`），
 * 看板那一行**没有**专属容器样式；这里只剩弹层里那排品类胶囊。
 */
.rs-sheet-chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.rs-pill {
  flex: 0 0 auto; padding: .18rem .55rem; border-radius: 999px;
  border: 1px solid var(--dash-line); background: var(--panel-2);
  font-size: 12.5px; line-height: 18px; color: var(--dash-ink-2);
  cursor: pointer; white-space: nowrap;
}
.rs-pill b { margin-left: .28rem; color: var(--dash-ink); font-weight: 600; }
.rs-pill:hover { border-color: var(--primary); color: var(--primary); }
.rs-pill:hover b { color: var(--primary); }
/* 本月一条都没有的品类：压淡但**不隐藏**（看得出「这个品类没人碰」） */
.rs-pill.is-zero { opacity: .5; }
/* 弹层里当前选中的品类：主色描边 + 浅底 */
.rs-pill.is-on {
  border-color: var(--primary); color: var(--primary);
  background: color-mix(in srgb, var(--primary) 8%, var(--panel));
}
.rs-pill.is-on b { color: var(--primary); }
@media (max-width: 640px) {
  .rs-sheet-chips { gap: .35rem; }
}


/* ---------- 季度 / 月度 分区卡片 ---------- */
.dash-card {
  background: var(--panel); border: 1px solid var(--dash-line); border-radius: var(--radius-lg);
  padding: 1rem 1.1rem 1.1rem; box-shadow: var(--shadow);
}
.dash-card-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .9rem; flex-wrap: wrap; }
.dash-card-head h3 { margin: 0; font-size: 15px; font-weight: 600; color: var(--dash-ink); }
.dash-card-head .accent {
  width: 4px; height: 16px; border-radius: 999px; background: var(--primary);
}
.dash-card-head .head-note { margin-left: auto; font-size: 12.5px; color: var(--dash-ink-2); }

/* ---------- 赝复师「每日完成」：左边按天看人 + 右边月历（2026-10-28 老板要求） ----------
 * **一份代码两个入口**：管理端「数据看板 → 赝复师」+ 派单台「每日完成」页签，
 * 组件在 `frontend/js/operatorDayBoard.js`（别另写一份样式给第二个入口）。
 * 口径（老板当面定的）：左边是**某一天**；状态三档 完成 / 未完成 / 休息，
 * **没设置就是「未设置」**（系统不猜）；有人点过之后，月历格子底下多一条彩条 =
 * 当天「完成 / 未完成」的人头比例（绿=完成、红=未完成）。
 */
.opbd-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .7rem; flex-wrap: wrap; }
.opbd-title { font-size: 13.5px; font-weight: 600; color: var(--dash-ink); font-variant-numeric: tabular-nums; }
.opbd-sub { font-size: 12.5px; color: var(--dash-ink-2); }
.opbd-sub b { color: var(--dash-ink); font-weight: 600; }
.opbd-date {
  margin-left: auto; display: inline-flex; align-items: center; gap: .35rem;
  font-size: 12.5px; color: var(--dash-ink-2); white-space: nowrap; flex: 0 0 auto;
}
.opbd-date-label { white-space: nowrap; }
.opbd-date input {
  border: 1px solid var(--dash-line); border-radius: 8px; padding: 3px 8px;
  font-size: 12.5px; font-family: inherit; color: var(--dash-ink); background: var(--panel);
}
.opbd-body {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 1.1rem; align-items: start;
}
.opbd-table { min-width: 0; }
.opbd-table th, .opbd-table td { padding-left: .35rem; padding-right: .35rem; }
.opbd-table tr.is-off td { color: var(--muted); }
.opbd-name { font-weight: 600; }
.opbd-zero { color: var(--danger); }
.opbd-sum td { border-top: 1px solid var(--dash-line); }
.opbd-tally { font-size: 12px; color: var(--dash-ink-2); }
.opbd-tally b { color: var(--dash-ink); font-weight: 600; }
.opbd-none { font-size: 12px; color: var(--muted); }
/* 「当天完成 N 道」可点开看明细（2026-10-29 老板要求）：看着像数字、不是按钮壳，但要点得动 */
.opbd-steps {
  font-family: inherit; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
  border: 0; background: transparent; padding: 0; cursor: pointer; color: var(--ok);
  border-bottom: 1px dashed color-mix(in srgb, var(--ok) 45%, transparent);
}
.opbd-steps:hover { color: var(--primary); border-bottom-color: var(--primary); }
.opbd-hint { margin: .5rem 0 0; }
.opbd-marks { display: inline-flex; border: 1px solid var(--dash-line); border-radius: 8px; overflow: hidden; }
.opbd-mark {
  font-family: inherit; font-size: 11.5px; line-height: 22px; padding: 0 .5rem;
  border: 0; border-left: 1px solid var(--dash-line); background: var(--panel);
  color: var(--dash-ink-2); cursor: pointer; white-space: nowrap;
}
.opbd-mark:first-child { border-left: 0; }
.opbd-mark:hover { color: var(--dash-ink); background: var(--panel-2); }
.opbd-mark.is-done.is-on { background: color-mix(in srgb, var(--ok) 18%, var(--panel)); color: var(--ok); font-weight: 600; }
.opbd-mark.is-undone.is-on { background: color-mix(in srgb, var(--danger) 15%, var(--panel)); color: var(--danger); font-weight: 600; }
.opbd-mark.is-rest.is-on { background: color-mix(in srgb, var(--primary) 14%, var(--panel)); color: var(--primary); font-weight: 600; }
.opbd-tag { font-size: 11.5px; padding: .1rem .45rem; border-radius: 999px; border: 1px solid var(--dash-line); }
.opbd-tag.is-done { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, var(--dash-line)); }
.opbd-tag.is-undone { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--dash-line)); }
.opbd-tag.is-rest { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 40%, var(--dash-line)); }
.opbd.is-busy { opacity: .55; pointer-events: none; }
.opbd-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; display: flex; }
.opbd-bar u { display: block; height: 100%; }

.opc-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .55rem; flex-wrap: wrap; }
.opc-total { font-size: 12.5px; color: var(--dash-ink-2); }
.opc-total b { color: var(--dash-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.opc-nav { margin-left: auto; display: flex; gap: .3rem; align-items: center; }
.opc-month {
  font-size: 12.5px; font-weight: 600; color: var(--dash-ink);
  min-width: 6.4rem; text-align: center; font-variant-numeric: tabular-nums;
}
.opc-arrow {
  border: 1px solid var(--dash-line); background: var(--panel-2); color: var(--dash-ink-2);
  border-radius: 7px; font-size: 12.5px; line-height: 24px; padding: 0 .55rem; cursor: pointer;
  font-family: inherit;
}
.opc-arrow:hover { border-color: var(--primary); color: var(--primary); }

.opc-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; margin-bottom: 4px; }
.opc-week span { text-align: center; font-size: 11.5px; color: var(--dash-ink-2); }
.opc-week span.is-weekend { color: var(--muted); }

.opc-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
.opc-cell {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; justify-content: flex-start;
  min-height: 48px; padding: 5px 7px 6px; border-radius: 8px;
  border: 1px solid transparent; background: var(--bg-soft);
  font-family: inherit; font-variant-numeric: tabular-nums; text-align: left;
  color: var(--dash-ink);
}
button.opc-cell { cursor: pointer; }
.opc-cell i { font-style: normal; font-size: 11.5px; color: var(--dash-ink-2); }
.opc-cell b { margin-top: auto; align-self: flex-end; font-size: 14px; font-weight: 600; }
.opc-cell.is-blank { background: transparent; }
.opc-cell.is-future { background: transparent; border: 1px dashed var(--dash-line); opacity: .5; }
.opc-cell.is-zero { background: color-mix(in srgb, var(--danger) 9%, var(--panel)); }
.opc-cell.is-zero b { color: var(--danger); }
.opc-cell.is-l1 { background: color-mix(in srgb, var(--ok) 8%, var(--panel)); }
.opc-cell.is-l2 { background: color-mix(in srgb, var(--ok) 17%, var(--panel)); }
.opc-cell.is-l3 { background: color-mix(in srgb, var(--ok) 29%, var(--panel)); }
.opc-cell.is-l1 b, .opc-cell.is-l2 b, .opc-cell.is-l3 b { color: var(--ok); }
button.opc-cell:hover { border-color: var(--primary); }
.opc-cell.is-today { box-shadow: inset 0 0 0 1.5px var(--primary); }
.opc-cell.is-picked {
  border-color: var(--primary);
  background: color-mix(in srgb, var(--primary) 13%, var(--panel));
}
.opc-cell.is-picked b { color: var(--primary); }

.opc-legend {
  display: flex; align-items: center; gap: .7rem; margin-top: .5rem;
  font-size: 11.5px; color: var(--dash-ink-2); flex-wrap: wrap;
}
.opc-legend b { display: inline-block; width: 9px; height: 9px; border-radius: 2px; margin-right: 4px; vertical-align: -1px; }
.opc-legend-note { margin-left: auto; color: var(--muted); }

@media (max-width: 900px) {
  .opbd-body { grid-template-columns: minmax(0, 1fr); gap: .9rem; }
  .opbd-date { margin-left: 0; width: 100%; }
  .opbd-marks { display: flex; }
  .opbd-mark { flex: 1 1 0; text-align: center; padding: 0 .3rem; }
  /* 手机上只有三列数据列 + 状态列，别让 `.table.dash-table{min-width:520px}` 把它撑爆 */
  .opbd-table.table.dash-table { min-width: 0; }
  .opbd-list .table-wrap { overflow-x: visible; }
}

/* 圆环 */
.ring-tile { text-align: center; }
.ring-tile svg { display: block; margin: 0 auto; }
.ring-tile .ring-name { font-size: 13.5px; font-weight: 600; color: var(--dash-ink); margin-top: .3rem; }
.ring-tile .ring-value { font-size: 12.5px; color: var(--dash-ink-2); margin-top: .1rem; }
.ring-tile .ring-value b { color: var(--dash-ink); }

/* 大数字卡（销售额 / 完成率） */
.big-stat {
  display: flex; align-items: center; gap: .9rem;
  padding: .95rem 1.05rem; border-radius: var(--radius-lg); color: #fff;
  background: linear-gradient(120deg, #7b5cff, #6f4bff);
}
.big-stat.ok { background: linear-gradient(120deg, #00b42a, #23c343); }
.big-stat.warn { background: linear-gradient(120deg, #ff7d00, #ffb400); }
.big-stat .bs-label { font-size: 12.5px; color: rgba(255, 255, 255, .9); }
.big-stat .bs-value { font-size: 26px; font-weight: 700; line-height: 34px; font-variant-numeric: tabular-nums; }
.big-stat .bs-icon {
  margin-left: auto; display: grid; place-items: center; flex: none;
  width: 42px; height: 42px; border-radius: 12px; background: rgba(255, 255, 255, .2); font-size: 20px;
}

/* ---------- 排行榜（TOP 3 彩色横条） ---------- */
.rank-list { display: flex; flex-direction: column; gap: .6rem; }
.rank-row { display: flex; align-items: center; gap: .6rem; }
.rank-name { flex: 0 0 5.2rem; font-size: 13px; color: var(--dash-ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-track { flex: 1 1 auto; height: 16px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.rank-fill { display: block; height: 100%; border-radius: 999px; background: var(--rank-tone, var(--primary)); transition: width .3s ease; }
.rank-value { flex: 0 0 auto; font-size: 13px; font-weight: 600; color: var(--dash-ink); font-variant-numeric: tabular-nums; }
.rank-note { flex: 0 0 auto; font-size: 12px; color: var(--dash-ink-2); }

/* ---------- 表格：彩色表头 + 突出的数字 ---------- */
.table.dash-table thead th {
  background: linear-gradient(120deg, #6f4bff, #7b5cff);
  color: #fff; font-weight: 600; font-size: 12.5px; border-bottom: none;
}
.table.dash-table thead th:first-child { border-top-left-radius: 8px; }
.table.dash-table thead th:last-child { border-top-right-radius: 8px; }
.table.dash-table tbody td { font-size: 13px; color: var(--dash-ink); }
.table.dash-table tbody tr:nth-child(even) { background: var(--panel-2); }
.table.dash-table .num { font-variant-numeric: tabular-nums; font-weight: 600; }
.table.dash-table .num-primary { color: var(--primary); }
.table.dash-table .num-ok { color: var(--ok); }
.table.dash-table .num-warn { color: var(--warn); }
.table.dash-table .num-danger { color: var(--danger); }
.table.dash-table .num-purple { color: #7b5cff; }
.table.dash-table .rate {
  display: inline-block; min-width: 3.6rem; text-align: center;
  padding: .1rem .4rem; border-radius: 999px; font-weight: 600; font-size: 12.5px;
  background: var(--primary-soft); color: var(--primary);
}
.table.dash-table .rate.ok { background: var(--ok-soft); color: var(--ok); }
.table.dash-table .rate.warn { background: var(--warn-soft); color: var(--warn); }
.table.dash-table .rate.danger { background: var(--danger-soft); color: var(--danger); }

/* 看板里的说明文字（比全站 muted 深一档，不刺眼也看得清） */
.dash-ink-2 { color: var(--dash-ink-2); }
.dash-ink { color: var(--dash-ink); font-weight: 600; }

/* 深色模式下看板的文字与分隔线也跟着调深，避免浅色边框刺眼 */
html[data-theme="dark"] {
  --dash-line: #2a2f3a;
  --dash-ink: #e8eaee;
  --dash-ink-2: #b6bfcd;
}

/* ---------- 2026-09-29 收敛配色：少即是多 ---------- */
/* 顶部标题条改成浅色卡片，不再用紫色渐变（原来太抢眼） */
.dash-hero {
  background: var(--panel);
  color: var(--dash-ink);
  border: 1px solid var(--dash-line);
  border-left: 5px solid var(--primary);
  box-shadow: var(--shadow);
}
.dash-hero .hero-icon { background: var(--primary-soft); color: var(--primary); }
.dash-hero h2 { color: var(--dash-ink); }
.dash-hero .hero-sub { color: var(--dash-ink-2); }
.dash-hero .hero-chip {
  background: var(--bg-soft); color: var(--dash-ink-2);
}
.dash-hero .hero-chip b { color: var(--dash-ink); }

/* 表格表头回归浅灰 + 深色字，只让数字和完成率带颜色 */
.table.dash-table thead th {
  background: var(--bg-soft);
  color: var(--dash-ink-2);
  font-weight: 600;
}
.table.dash-table thead th:first-child, .table.dash-table thead th:last-child { border-radius: 8px; }

/* =============================================================
   管理端：左侧导航 + 右侧内容（阿里系后台观感）
   只作用于 body.admin-shell（管理端页面），其它页面不受影响
   ============================================================= */
.admin-shell {
  display: grid;
  grid-template-columns: 216px minmax(0, 1fr);
  min-height: 100vh;
  background: var(--bg);
}

/* ---------- 左侧导航 ---------- */
.admin-sider {
  position: sticky; top: 0; align-self: start;
  height: 100vh;
  display: flex; flex-direction: column;
  padding: 14px 10px 10px;
  background: var(--panel);
  border-right: 1px solid var(--border);
}
.sider-brand { display: flex; align-items: center; gap: 10px; padding: 2px 6px 16px; }
.sider-brand .logo {
  flex: none; width: 34px; height: 34px; border-radius: 9px;
  display: grid; place-items: center; overflow: hidden;
  background: var(--primary); color: #fff; font-size: 16px; font-weight: 600;
}
/**
 * 品牌 logo 换成 `<img src="/img/logo-mark.png">` 之后（2026-10-22），
 * **必须在这里给图片尺寸约束**：原图是 371×341，没约束时它按原尺寸铺开，
 * 手机上直接把整屏糊成一块红（老板截图报的「手机端咋成这样了」）。
 * `admin-apple.css` 里那条 `.logo img { width:26px }` 管苹果风的样子，
 * 这条是**兜底**（万一那层 CSS 没加载 / 被缓存成旧版，也不能炸）。
 */
.sider-brand .logo img {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.sider-title { font-size: 15px; font-weight: 600; color: var(--text); line-height: 20px; }

.sider-menu { display: flex; flex-direction: column; gap: 2px; }
.sider-menu button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: 42px; padding: 0 12px;
  border: none; border-radius: 8px; background: transparent;
  color: var(--text-2); font-family: inherit; font-size: 14px; font-weight: 500;
  text-align: left; cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.sider-menu button .mi { flex: none; width: 20px; text-align: center; font-size: 15px; }
.sider-menu button .mt { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sider-menu button:hover { background: var(--bg-soft); color: var(--text); }
.sider-menu button.active {
  background: var(--primary-soft);
  color: var(--primary);
  font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--primary-border);
}

/**
 * 「车间大屏 / 发货中心 / 派单台」这三颗：**宽屏在左下角那一列（`.sider-foot`），
 * 手机端收进菜单行**（老板 2026-10-15：手机上它们占掉首屏一整行，数据框被推到第二屏）。
 * 两边只渲染一次，靠 `.menu-extra` 的显隐切换。
 */
.sider-menu .menu-extra { display: none; }

.sider-foot { margin-top: auto; display: flex; flex-direction: column; gap: 6px; padding: 8px 6px 2px; }
.sider-foot .btn { justify-content: center; }
.sider-foot .btn .mi { display: none; }

/* ---------- 收起状态：只留图标，右侧内容占满整屏 ---------- */
.admin-shell.is-collapsed { grid-template-columns: 64px minmax(0, 1fr); }
.admin-shell.is-collapsed .sider-brand { justify-content: center; padding-left: 0; padding-right: 0; }
.admin-shell.is-collapsed .sider-brand .sider-title,
.admin-shell.is-collapsed .sider-brand .small { display: none; }
.admin-shell.is-collapsed .sider-menu button { justify-content: center; padding: 0; }
.admin-shell.is-collapsed .sider-menu button .mt { display: none; }
.admin-shell.is-collapsed .sider-menu button .mi { width: auto; font-size: 17px; }
.admin-shell.is-collapsed .sider-foot .btn .mt { display: none; }
.admin-shell.is-collapsed .sider-foot .btn .mi { display: inline; font-size: 15px; }

/* ---------- 右侧主区 ---------- */
.admin-main { min-width: 0; display: flex; flex-direction: column; }
.admin-shell .topbar { position: sticky; top: 0; z-index: 20; }
.page-title { font-size: 16px; font-weight: 600; color: var(--text); }

/* ---------- 手机 / 窄屏：左侧导航变顶部横向菜单 ---------- */
@media (max-width: 900px) {
  .admin-shell { grid-template-columns: minmax(0, 1fr); }
  .admin-sider {
    position: static; height: auto;
    padding: 10px 10px 8px;
    border-right: none; border-bottom: 1px solid var(--border);
  }
  .sider-brand { padding-bottom: 10px; }
  .sider-menu { flex-direction: row; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
  .sider-menu button { flex: 0 0 auto; min-height: 38px; padding: 0 12px; }
  .sider-menu button .mi { display: none; }
  /**
   * ⚠️ 手机上标签**不许参与收缩**（`flex: 0 0 auto`）：整行靠 `overflow-x: auto` 横滑。
   * 之前标签是 `flex: 1 1 auto`，加了「车间大屏 / 发货中心 / 派单台」三颗之后，
   * 收缩压力全落在标签上 → 4 个菜单项被压成只剩左右内边距（宽度 8px、字叠在一起，
   * 2026-10-15 实测抓到的）。
   */
  .sider-menu button .mt { flex: 0 0 auto; }
  /* 手机上这一列整个收起：那三颗跳转挪到菜单行里（`.menu-extra`），别占首屏一整行 */
  .sider-foot { display: none; }
  .sider-menu .menu-extra {
    display: inline-flex; align-items: center; flex: 0 0 auto;
    min-height: 38px; padding: 0 12px; border-radius: 8px;
    color: var(--text-2); font-size: 14px; font-weight: 500;
    text-decoration: none; white-space: nowrap;
  }
  .sider-menu .menu-extra:hover { background: var(--bg-soft); color: var(--text); }
  /* 手机端菜单本来就是横向的，收起功能不适用：强制按展开状态显示，并隐藏收起按钮 */
  .admin-shell.is-collapsed { grid-template-columns: minmax(0, 1fr); }
  .admin-shell.is-collapsed .sider-menu button { justify-content: flex-start; padding: 0 12px; }
  .admin-shell.is-collapsed .sider-menu button .mt { display: inline; }
  #sider-toggle { display: none; }
}

/* =============================================================
   订单表格（订单中心 / 排单员端共用）：收紧排版、突出关键信息
   ============================================================= */
.orders-table { width: 100%; }
.orders-table thead th {
  position: sticky; top: 0; z-index: 2;
  padding: 10px 14px; white-space: nowrap;
  background: var(--bg-soft); color: var(--text-2);
  font-size: 12.5px; font-weight: 600; border-bottom: 1px solid var(--border);
  /**
   * 表头**跟这一列的内容对齐**（2026-10-13 老板：「表头还是没跟订单中心的内容居中，
   * 在我拉动浏览器尺寸的时候会变位置」）：
   * 文字列的内容是左对齐，表头就左对齐；一旦把表头居中，列宽随浏览器宽度变化时
   * 表头文字会左右漂移，永远对不上下面的数据。**别再改回 center。**
   */
  text-align: left;
}
/* 「金额 / 操作」这两列的内容是右对齐（数字贴右边），表头也跟着右对齐 */
.orders-table thead th.ta-right { text-align: right; }
.orders-table tbody td {
  padding: 12px 14px; vertical-align: middle;
  border-bottom: 1px solid var(--border-2);
}
.orders-table tbody tr { transition: background-color .12s ease; }
.orders-table tbody tr:hover { background: var(--primary-soft); }
.orders-table tbody tr.is-active { background: var(--primary-soft); }
/* 超时单：左侧一条红杠，扫一眼就能挑出来 */
.orders-table tbody tr.is-overdue td:first-child { box-shadow: inset 3px 0 0 var(--danger); }

.orders-table .cell-no { min-width: 150px; }
.orders-table .order-no { font-size: 13.5px; font-weight: 600; color: var(--text); }
.orders-table .order-name { font-size: 14.5px; font-weight: 600; color: var(--text); line-height: 20px; }
.orders-table .order-sub { margin-top: 3px; font-size: 12px; line-height: 17px; color: var(--dash-ink-2, var(--text-2)); }

.orders-table .cell-progress { min-width: 150px; }
.orders-table .order-progress-line { display: flex; align-items: center; gap: 6px; }
.orders-table .order-stage { font-size: 12px; color: var(--dash-ink-2, var(--text-2)); }
.orders-table .order-progress { display: block; height: 5px; margin-top: 7px; max-width: 170px; }

.orders-table .cell-current { min-width: 170px; }
.orders-table .order-current { font-size: 13.5px; color: var(--text); }
/* 维权中：「当前工序」那一栏写「维权中 · 工序已下架」（老板 2026-10-15），红字提醒别当正常在制看 */
.orders-table .order-current.is-rights { color: var(--danger); font-weight: 600; }

/**
 * 「截止日期」列（2026-10-16 替掉原来的「耗时」列，老板要求）：
 *   上排 = 截止日期（月日），下排 = 剩余天数（`37 天`，只到天）；
 *   剩余天数按后台设的橙 / 红天数变色（`.countdown.warn/.danger`，在 §订单表格 上面那一段定义）。
 */
.orders-table .cell-due { min-width: 104px; white-space: nowrap; }
.orders-table .order-due {
  font-size: 13.5px; font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.orders-table .order-due.muted { font-weight: 400; }
.orders-table .order-due-sub { margin-top: 2px; font-size: 12.5px; font-variant-numeric: tabular-nums; }

/**
 * 可排序表头（2026-10-16）：表头文字本身是按钮，点一下切档
 * （默认 → 最近到期的在前 ↑ → 最远的在前 ↓ → 回到默认），右侧小箭头给当前档位。
 */
.orders-table .th-sort {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 0; border: 0; background: none; font: inherit; color: inherit;
  font-weight: 600; cursor: pointer; white-space: nowrap;
}
.orders-table .th-sort:hover { color: var(--primary); }
.orders-table .th-sort .sort-arrow { font-size: 11px; color: var(--muted); }
.orders-table .th-sort.is-on { color: var(--primary); }
.orders-table .th-sort.is-on .sort-arrow { color: var(--primary); }

.orders-table .cell-money { text-align: right; min-width: 120px; }
.orders-table .order-money {
  font-size: 15px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}
.orders-table .order-money-sub { margin-top: 3px; }

.orders-table .cell-op { text-align: right; white-space: nowrap; }

/* 订单表格表头改白色（原来浅灰底看着发闷），保留底部细线做分隔 */
.orders-table thead th {
  background: var(--panel);
  color: var(--dash-ink, var(--text));
  border-bottom: 1px solid var(--border);
}
/* 运营列：谁排的单 */
.orders-table .cell-planner { white-space: nowrap; }
.orders-table .order-planner { font-size: 13.5px; font-weight: 600; color: var(--text); }

/* 单号 / 录入那一列不要换行（加了运营列以后变窄了） */
.orders-table .cell-no { min-width: 175px; }
.orders-table .cell-no .order-sub { white-space: nowrap; }

/* ---------- 补录 / 修正工序弹层（2026-10-14 老板：太丑、要简洁、要大厂样式） ----------
 * 表格式栅格：表头 + 四列对齐（已完成 / 工序 / 谁做的 / 完成日期）；
 * 没勾选的行把后两列禁用 + 置灰（先勾上才能填）。窄屏退化成两行一条，不横着挤。
 */
.backfill-head,
.backfill-row {
  display: grid;
  /* 工序名那一列吃剩下的空间（弹层用了 modal-wide，够宽）；其它三列按内容收窄 */
  grid-template-columns: 6rem minmax(0, 1fr) 7.5rem 8.5rem;
  gap: 0.5rem;
  align-items: center;
}
.backfill-head {
  padding: 0 0.1rem 0.35rem;
  font-size: 12px;
  color: var(--text-2);
  border-bottom: 1px solid var(--border);
}
.backfill-list {
  max-height: 52vh;
  overflow: auto;
}
.backfill-row {
  padding: 0.4rem 0.1rem;
  border-bottom: 1px solid var(--border-2);
}
.backfill-row:last-child {
  border-bottom: none;
}
.backfill-check {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
}
.backfill-name {
  font-size: 13px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.backfill-row select,
.backfill-row input[type='date'] {
  width: 100%;
  min-width: 0;
}
.backfill-row:not(.is-done) .backfill-name,
.backfill-row:not(.is-done) select,
.backfill-row:not(.is-done) input[type='date'] {
  color: var(--text-2);
  opacity: 0.6;
}
@media (max-width: 640px) {
  .backfill-head {
    display: none;
  }
  .backfill-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    row-gap: 0.35rem;
  }
  .backfill-check,
  .backfill-name {
    grid-column: 1 / -1;
  }
  .backfill-name {
    white-space: normal;
  }
}

/* ---------- 底部翻页（2026-10-13 老板要求：订单多了用页码翻页，不要「加载更多」） ---------- */
.pager-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-top: .7rem;
}
.pager { display: flex; align-items: center; gap: 4px; }
.pager .pg-btn,
.pager .pg-num {
  min-width: 30px; height: 30px; padding: 0 8px;
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: 13px; line-height: 1;
  color: var(--text-2); background: var(--panel);
  border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}
.pager .pg-btn:hover:not(:disabled),
.pager .pg-num:hover:not(.is-active) {
  border-color: var(--primary-border); color: var(--primary);
}
.pager .pg-num.is-active {
  color: #fff; background: var(--primary); border-color: var(--primary); font-weight: 600;
}
.pager .pg-btn:disabled { opacity: .45; cursor: default; }
.pager .pg-gap { padding: 0 2px; color: var(--text-2); }
.pager .pg-size {
  height: 30px; margin-left: 6px; padding: 0 6px;
  font: inherit; font-size: 13px; color: var(--text-2);
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
}
@media (max-width: 640px) {
  /* 手机上翻页条换行居中，页码按钮加大到手指好点 */
  .pager-bar { justify-content: center; }
  .pager { flex-wrap: wrap; justify-content: center; }
  .pager .pg-btn,
  .pager .pg-num { min-width: 32px; height: 32px; }
}

/*
 * 「全部客户」页码条 —— 老板 2026-10-14：「这个翻页也太丑了吧，重新弄一下，不符合阿里大厂的 ui 风格」。
 * 之前难看的原因：① 容器没带 `pager` 类，`.pg-*` 那套样式压根没生效；② 全局 `select { width:100% }`
 * 把「N 条/页」拉成整条白杠、挤到第二行。
 * 现在照 Ant Design 表格底栏那套排：**一条白色底栏**，左边一排页码、右边条数选择器。
 * 页码格子由 `allPageNumbers()` 卡死在 5 个以内，所以再窄的手机也只有一行（nowrap 兜底）。
 */
#all-orders-pager {
  flex-wrap: nowrap; align-items: center; gap: 4px;
  margin-top: .7rem; padding: 6px 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
}
/* 圆角收到 6px（Ant 的尺寸），数字用常规字色 —— 之前八个 8px 圆角灰字方框看着才「土」 */
#all-orders-pager .pg-btn,
#all-orders-pager .pg-num {
  min-width: 30px; height: 30px; min-height: 30px; padding: 0 7px;
  font-size: 13px; color: var(--text); border-radius: 6px;
}
/* 省略号撑到跟页码一样高，垂直方向就不会差 1px（一格一格的才齐） */
#all-orders-pager .pg-gap {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 14px; height: 30px; padding: 0;
}
/* 「N 条/页」靠右收紧：不许再被全局 `select { width:100% }` 横成白杠 */
#all-orders-pager .pg-size {
  width: auto; flex: 0 1 auto; min-width: 64px; max-width: 104px;
  height: 30px; min-height: 30px; margin: 0 0 0 auto; padding: 0 24px 0 10px;
  font-size: 12.5px; border-radius: 6px;
}
@media (max-width: 430px) {
  #all-orders-pager { gap: 3px; padding: 5px 6px; }
  #all-orders-pager .pg-btn,
  #all-orders-pager .pg-num { min-width: 28px; height: 28px; min-height: 28px; padding: 0 6px; font-size: 12.5px; }
  #all-orders-pager .pg-gap { min-width: 12px; height: 28px; }
  #all-orders-pager .pg-size {
    max-width: 96px; height: 28px; min-height: 28px; padding: 0 22px 0 8px; font-size: 12px;
  }
}

/* ---------- 2026-09-29 反馈修正 ---------- */
/* 「运营」列改回常规字重（之前加粗了，看着太抢眼） */
.orders-table .order-planner { font-weight: 400; color: var(--text); }

/* 管理端内容区背景改成白色（原来灰底，订单中心底部一大片灰） */
.admin-shell { background: var(--panel); }
.admin-main { background: var(--panel); }
.admin-shell .container.wide { min-height: calc(100vh - 56px); padding-bottom: 40px; }

/* 「运营」列恢复加粗（老板要求保留重点） */
.orders-table .order-planner { font-weight: 600; color: var(--text); }

/* ---------- 2026-09-29 数据看板 · 退款：只读 KPI 小方块 ----------
   阿里系观感：白底 + 1px 细边框 + 8px 圆角，数字大而重、说明小而灰，
   只用功能色点一下重点（金额红、笔数蓝），不做渐变 / 阴影堆叠。
   与可点击的 .kpi-card 区分开：.kpi-box 是纯展示，没有悬停位移。 */
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.kpi-box {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.kpi-box .kpi-label { font-size: 13px; font-weight: 400; color: var(--muted); }
.kpi-box .kpi-value {
  font-size: clamp(20px, 1.6vw, 26px);
  font-weight: 600;
  line-height: 34px;
  letter-spacing: -.2px;
  font-variant-numeric: tabular-nums;
}
.kpi-box .kpi-sub { font-size: 12px; color: var(--muted); }
.kpi-box[data-tone='red'] .kpi-value { color: var(--danger); }
.kpi-box[data-tone='amber'] .kpi-value { color: var(--warn); }
.kpi-box[data-tone='blue'] .kpi-value { color: var(--primary); }
.kpi-box[data-tone='violet'] .kpi-value { color: #7b5cff; }
.kpi-box[data-tone='slate'] .kpi-value { color: var(--text); }

/* 退款表格：客户名后面的小字说明、右对齐金额、可点的「详情」 */
.refund-table .cell-when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.refund-table .cell-customer { font-weight: 600; }
.refund-table .cell-reason { color: var(--muted); max-width: 260px; }

/* 流水卡副行里的文字链接（例如「本月退款 ¥900.00 ›」）：可点、悬停变主色 + 下划线 */
.tile-link {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--dash-ink-2, var(--text-2));
  cursor: pointer;
}
.tile-link:hover { color: var(--primary); text-decoration: underline; }
.tile-link .down { font-weight: 600; }

/* =============================================================
   2026-10-05 手机端适配重做（老板反馈：手机上展示不全、一列挤成两列、字被截断）
   原则：手机上**一列铺满**，该换行就换行；表格允许横向滑动；触控目标 ≥ 36px。
   只覆盖 ≤900px / ≤640px，桌面端一个字都不动。
   ============================================================= */

/* ---------- ① 管理端骨架：左侧导航 → 顶部横向菜单（可横滑） ---------- */
@media (max-width: 900px) {
  .admin-shell { display: block; }
  .admin-sider {
    position: sticky; top: 0; z-index: 30;   /* 菜单常驻顶部，滚到哪儿都能切页 */
    height: auto;
    padding: 8px 10px 6px;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .sider-brand { padding: 0 4px 8px; }
  .sider-brand .logo { width: 30px; height: 30px; font-size: 15px; }
  .sider-title { font-size: 14px; }
  /**
   * 关键修复：桌面端 `.sider-menu button` 是 `width: 100%`（竖排一行一个），
   * 窄屏改成横向后这条没被覆盖 —— 结果第一个菜单占满整行、后面几个被挤出屏幕，
   * 看起来就像「菜单只有一个」。这里必须把宽度放开。
   */
  .sider-menu {
    flex-direction: row; flex-wrap: nowrap; gap: 6px;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    padding-bottom: 4px; scrollbar-width: none;
  }
  .sider-menu::-webkit-scrollbar { display: none; }
  .sider-menu button {
    width: auto; flex: 0 0 auto;
    min-height: 36px; padding: 0 12px;
    border-radius: 999px; background: var(--panel-2);
    font-size: 13.5px;
  }
  .sider-menu button .mi { display: none; }
  .sider-menu button .mt { flex: 0 0 auto; }
  .sider-foot { flex-direction: row; flex-wrap: wrap; gap: 6px; margin: 6px 0 0; padding: 0; }
  .sider-foot .btn { min-height: 34px; padding: 0 12px; font-size: 13px; }
  /* 未收起状态下的兼容（手机端菜单本来就是横向的） */
  .admin-shell.is-collapsed .sider-menu button { width: auto; padding: 0 12px; }
  .admin-shell.is-collapsed .sider-menu button .mt { display: inline; }
  /* 顶部菜单已经 sticky，标题栏不用再粘一份 */
  .admin-shell .topbar { position: static; }
}

/* ---------- ② 卡片：手机上一列铺满，数字放大、标题不换行 ---------- */
@media (max-width: 640px) {
  /* ⚠️ 只作用于管理端：师傅端 `#my-stats` 也是 .grid.cols-3（那排小方块手机上并排更好看），
     排单员端 `.grid.cols-2/3` 是录单表单的两列/三列布局 —— 都别被这里带成单列。 */
  .admin-shell .grid.cols-2, .admin-shell .grid.cols-3 { grid-template-columns: minmax(0, 1fr) !important; }
  /* 季度环：两列并排，别 4 个堆成一长条 */
  .admin-shell .grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .tile { padding: .8rem .9rem; border-radius: 12px; }
  .tile-head { flex-wrap: nowrap; gap: .4rem; }
  .tile-title { font-size: 13.5px; }
  .tile-value { font-size: 26px; line-height: 32px; }
  /* 「完成单数」卡的大字窄屏也保持小一号（跟上面 .tile-done 同一口径） */
  .tile-done .tile-value { font-size: 22px; line-height: 28px; }
  .tile-sub { font-size: 12px; line-height: 18px; }
  .tile-more { padding: .1rem .45rem; font-size: 11.5px; }
  .dash-card { padding: .8rem .85rem .9rem; border-radius: 12px; }
  .dash-card-head { gap: .4rem; margin-bottom: .7rem; }
  .dash-card-head .head-note { font-size: 11.5px; }
  .admin-shell .container.wide { padding: 12px 12px calc(28px + env(safe-area-inset-bottom)); }
  /* 只读 KPI 小方块：两列，省一半高度 */
  .admin-shell .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .admin-shell .kpi-box { padding: 10px 12px; }
  .admin-shell .kpi-box .kpi-label { font-size: 12px; }
  .admin-shell .kpi-box .kpi-value { font-size: 20px; line-height: 28px; }
  .admin-shell .kpi-box .kpi-sub { font-size: 11px; }
  /* 顶栏收紧：标题 + 用户 + 主题 + 退出 尽量一行 */
  .topbar { min-height: 48px; padding: 0 12px; gap: 6px; }
  .page-title { font-size: 15px; }
  .who { font-size: 11px; line-height: 14px; }
}

/* ---------- ③ 表格：手机上收紧 + 横向滑动（宽表 swipe 看） ---------- */
@media (max-width: 640px) {
  /* 看板里的表格列多（运营表 8 列），手机上不要硬挤 —— 给个最小宽度让它横向滑动，
     否则「钮啸龙 NXL」会被压成三行。 */
  .table.dash-table { min-width: 520px; }
  .table.dash-table thead th { padding: 8px 10px; font-size: 12px; }
  .table.dash-table tbody td { padding: 8px 10px; font-size: 12.5px; }
  .table.dash-table tbody td:first-child { white-space: nowrap; }
  .orders-table thead th, .orders-table tbody td { padding: 10px 12px; }
  .stat-row { font-size: 13px; }
  .modal { padding: 16px; }
  .modal.modal-detail { padding: 14px; max-height: 92vh; }
}

/* ---------- 2026-10-12 收模弹层：「无需数据」勾选框（非必选） ----------
   勾上 = 这一单拿不到手指照片 / 快递单号（实物当面交接等），照片与单号两块整个收起。
   只留一个勾选框，不写额外说明（老板要求把提示词去掉）。 */
.no-data-box {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: .45rem .7rem;
}

/* ---------- 2026-10-14 派单台（排单分配员） ----------
   与其它页面同一套语言：卡片列表 + 底部操作条；手机上也能单手点。
   只有三处是本页特有的：勾选框(.pick-box)、选人弹层(.pick-row)、批量派单条(.dispatch-bar)。 */
.dispatch-web .kpi-row { margin-bottom: .9rem; }
/* 四个框统一尺寸与字号：原来框很宽、数字很空，收一档更精神 */
.dispatch-web .kpi-box,
.dispatch-web .kpi-card { padding: 12px 14px; gap: 3px; }
.dispatch-web .kpi-box .kpi-value,
.dispatch-web .kpi-card .kpi-value { font-size: 22px; line-height: 30px; }
/* 数据框统一观感：图标 + 标签一行，数字大而重（与可点的第 4 格同一套头部结构） */
.dispatch-web .kpi-box .kpi-head { margin-bottom: 2px; }
.dispatch-web .kpi-box .kpi-icon {
  width: 22px; height: 22px; font-size: 12px;
  background: var(--bg-soft);
}
.dispatch-web .kpi-box[data-tone='amber'] .kpi-icon { background: var(--warn-soft); }
.dispatch-web .kpi-box[data-tone='blue'] .kpi-icon { background: var(--primary-soft); }
.dispatch-web .kpi-box[data-tone='green'] .kpi-icon { background: var(--ok-soft); }
.dispatch-web .kpi-box[data-tone='green'] .kpi-value { color: var(--ok); }
.dispatch-web .kpi-card .kpi-icon { background: var(--purple-soft); }
/* 2026-10-24 第 5 格「全场看板」：桌面一行 5 格（老板：「变成5个框」），窄屏 3 格、手机 2 格 */
@media (min-width: 1101px) {
  .dispatch-web .kpi-row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
@media (max-width: 1100px) and (min-width: 641px) {
  .dispatch-web .kpi-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* 大字后面的完成率（老板 2026-10-24：「道数和完成率都加上」） */
.dispatch-web .kpi-rate {
  margin-left: 6px; font-size: 13px; font-weight: 600; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* 弹层里的三格汇总 / 按师傅表（.floor-sum / .floor-cell / .floor-bar / .floor-table*）已删除：
   2026-10-24 起这层弹层直接渲染车间大屏（js/boardView.js），这些类没有引用了，别照着加回来。 */

.floor-perm { padding: 8px 2px 4px; }

/* 「今天没完成的原因」（派单台全场看板弹层底部，2026-10-24 老板）：「下班前还是没有完成的话，
   需要写清楚原因，这是排单管理员来写，谁写都行，只是记录」。
   这块是**静态 DOM**（放在 #floor-body 之后），所以滚动的是上面那段数据，输入框永远停在底部看得见；
   别再把它塞进 #floor-body —— 那块每次打开都会 innerHTML 重画，写了一半的字会被清掉。 */
#floor-backdrop .sheet-body { flex: 1 1 auto; }
/* 派单台「🏭 全场看板」弹层 = 车间大屏（2026-10-24 老板：「我要看的是车间大屏放到派单台弹层」）。
   版面本身与 board.html 用同一份代码（js/boardView.js 的 boardScreenHtml），这里只管弹层的深色外皮与宽度。
   ⚠️ 大屏那套样式在本文件里统一挂在 `:is(body.board, .board-embed)` 前缀下 —— 只写 body.board 的话，
   弹层里的字会全变成浅色主题的深灰、等于看不见（2026-10-19 灰白配色事故的同类坑）。 */
.board-embed { color: #e5e6eb; }
/* KPI 布局不再单独覆盖：弹层现在铺满整屏，直接跟着大屏那套 6 / 3（≤1500）/ 2（≤860）格走。 */
/* 2026-10-24 老板：「都看不清信息了，等于打开一个新的页面」—— 弹层直接**铺满整个浏览器**（原来限 1240px 宽、
   92vh 高，内容 797px 塞进 399px 的可视区，中段拉滚动条、车间实时动态被切半）+ 右侧按师傅表名字列被挤成 2px。
   铺满以后版面跟车间大屏一样按视口宽度排，关掉就回派单台。 */
#floor-backdrop { padding: 0; align-items: stretch; }
#floor-backdrop .sheet {
  width: 100%; max-width: none;
  height: 100vh; height: 100dvh; max-height: none;
  padding: 14px 20px calc(14px + env(safe-area-inset-bottom));
  border: none; border-radius: 0;
  background: #0b0e14; color: #e5e6eb;
}
#floor-backdrop .sheet-head { border-bottom-color: #1f2733; }
#floor-backdrop .sheet-head h3 { color: #e5e6eb; }
#floor-backdrop .sheet-head .btn { background: #1b2230; border-color: #1f2733; color: #e5e6eb; }
#floor-backdrop #floor-sub { color: #86909c; }
#floor-backdrop .floor-perm { color: #86909c; }
/* 「今天没完成的原因」这块是浅色语义色写的，进深色弹层要逐项压过来 */
#floor-backdrop .goal-note { border-top-color: #1f2733; }
#floor-backdrop .goal-note-head > b { color: #e5e6eb; }
#floor-backdrop .goal-note-hint { color: #86909c; }
#floor-backdrop .goal-note-hint b { color: #e5e6eb; }
#floor-backdrop .goal-note-empty { color: #86909c; }
#floor-backdrop .goal-note-item { border-bottom-color: #1f2733; }
#floor-backdrop .goal-note-row { color: #86909c; }
#floor-backdrop .goal-note-row > b { color: #e5e6eb; }
#floor-backdrop .goal-note-item.is-mine .goal-note-row > b { color: var(--primary); }
#floor-backdrop .goal-note-text { color: #c9d1d9; }
#floor-backdrop .goal-note-form textarea { background: #10151f; border-color: #1f2733; color: #e5e6eb; }
#floor-backdrop .goal-note-actions .btn { background: #1b2230; border-color: #1f2733; color: #e5e6eb; }
#floor-backdrop .goal-note-actions .btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
/* 弹层铺满整屏后，这块要压成一行（2026-10-24 老板：「都看不清信息了」）：标题 + 提示在左，输入框 + 记一条在右，
   记录列表另起一行并限高 74px（多了自己滚）—— 空态从原来的 107~227px 压到 ~50px，把高度让给面板。 */
#floor-backdrop .goal-note {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(240px, 1.05fr);
  grid-template-areas: "head form" "list list";
  gap: 4px 14px; margin-top: 8px; padding-top: 8px;
}
#floor-backdrop .goal-note-head { grid-area: head; }
#floor-backdrop .goal-note-form { grid-area: form; margin-top: 0; align-items: center; }
#floor-backdrop .goal-note-form textarea { min-height: 34px; height: 34px; max-height: 12vh; padding: 6px 8px; }
#floor-backdrop .goal-note-list { grid-area: list; margin: 2px 0 0; max-height: 48px; }
#floor-backdrop .goal-note-list:empty { display: none; }
@media (max-width: 720px) {
  #floor-backdrop .goal-note { grid-template-columns: 1fr; grid-template-areas: "head" "list" "form"; }
  #floor-backdrop .goal-note-form { flex-direction: column; align-items: stretch; }
  #floor-backdrop .goal-note-actions { flex-direction: row; justify-content: flex-end; }
  #floor-backdrop .goal-note-form textarea { height: auto; min-height: 40px; }
}
/* 铺满整屏后 ≥1100 视口让 6 格 KPI 排一整行（少一整行 ~80px，把高度让给下面的面板）。 */
@media (min-width: 1100px) { #floor-backdrop .board-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 640px) { #floor-backdrop .sheet { padding: 10px 10px calc(10px + env(safe-area-inset-bottom)); } }
.goal-note { flex: none; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-2); }
.goal-note-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.goal-note-head > b { font-size: 14px; }
.goal-note-hint { font-size: 12.5px; color: var(--muted); }
.goal-note-hint b { color: var(--text); font-weight: 600; }
.goal-note-hint .is-behind { color: var(--danger); }
.goal-note-list { max-height: 26vh; overflow: auto; margin: 6px 0; }
.goal-note-empty { padding: 6px 2px; font-size: 12.5px; color: var(--muted); }
.goal-note-item { padding: 6px 0; border-bottom: 1px dashed var(--border-2); }
.goal-note-item:last-child { border-bottom: none; }
.goal-note-row { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); }
.goal-note-row > b { color: var(--text); font-weight: 600; }
.goal-note-row .goal-note-acts { margin-left: auto; display: inline-flex; gap: 4px; }
.goal-note-item.is-mine .goal-note-row > b { color: var(--primary); }
.goal-note-text { margin-top: 2px; font-size: 13.5px; line-height: 1.45; white-space: pre-wrap; word-break: break-word; }
.goal-note-form { display: flex; align-items: flex-end; gap: 8px; margin-top: 6px; }
.goal-note-form textarea { flex: 1 1 auto; min-height: 44px; max-height: 22vh; resize: vertical; }
.goal-note-actions { display: flex; flex-direction: column; gap: 6px; flex: none; }
@media (max-width: 640px) {
  .goal-note-form { flex-direction: column; align-items: stretch; }
  .goal-note-actions { flex-direction: row; justify-content: flex-end; }
}
/* 管理端「反馈」弹层里那一批（`/api/goal-notes`，最近 30 天）：
   列表不夹高度（弹层自己滚），与上面师傅反馈分段显示。 */
.goal-note-admin { margin-top: 16px; }
.goal-note-admin .goal-note-list { max-height: none; }
/* 卡片悬停：像可点的行（企业级列表的常规反馈） */
.dispatch-card { transition: border-color .15s ease, box-shadow .15s ease; }
.dispatch-card:hover { border-color: var(--primary-border); box-shadow: var(--shadow); }

/**
 * 维修单卡片（2026-10-15）：跟工序卡一样的位置、一样大的按钮，只多一枚「维修」标记 +
 * 一行维修内容；左侧一道琥珀色细边，扫一眼就知道「这是维修、不是工序」。
 */
.dispatch-card.is-repair { border-left: 3px solid var(--warn); }
.dispatch-card .repair-badge { font-size: 11px; padding: 1px 6px; }
.dispatch-card .repair-line {
  margin-top: 5px;
  font-size: 12.5px;
  line-height: 18px;
  color: var(--text-2);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/**
 * ⚠️ 卡片里的 .card-row1 / .card-title / .card-side / .card-due / .card-row3
 * 在师傅端全部是挂在 `#mine-list` 下的（只对师傅端那一处生效），派单台**必须自己再来一套**，
 * 否则按钮行是 block、按钮被挤到贴边（2026-10-14 老板截图：「派给…」「整单交给…」显示不全）。
 */
/* 卡片整体更紧凑（老板 2026-10-14：「把这个卡片再压缩压缩」）：
   内边距 / 行距 / chip 全部收一档，一屏能多看两三张单 */
.dispatch-card { padding: 10px 13px; }
.dispatch-card .card-row1 { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.dispatch-card .card-title {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14.5px;
}
.dispatch-card .card-title b { color: var(--text); font-weight: 600; }
.dispatch-card .card-dot { color: var(--muted); }
.dispatch-card .card-step { color: var(--text-2); }
/**
 * 工序名后面的「赝复师 某某 / 预派 某某」（老板 2026-10-15 要求从副信息行挪上来）：
 * 小标签灰、名字深一点，做成一个浅底小药丸，跟工序名分开、又不抢客户的视线。
 */
.dispatch-card .card-op {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 0 6px; line-height: 18px;
  font-size: 12px; color: var(--text-2);
  background: var(--bg-soft); border-radius: 4px;
}
.dispatch-card .card-op i { font-style: normal; color: var(--muted); }
.dispatch-card .card-op b { color: var(--text); font-weight: 600; }
/**
 * 工序名前面的「第几道」序号（老板 2026-10-14 三次修正的最终版）：
 *   「名字旁边的步骤加个序号」→「不够醒目」→「纯黑框太丑了」。
 * 结论：**实心品牌蓝 + 白字**的圆角胶囊 —— 醒目、成色好看，也和「派给」主操作用同一个主色。
 * 别再改成纯黑 / 灰底灰字（老板否掉过两轮）。
 */
.dispatch-card .step-no {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 21px;
  height: 21px;
  margin-right: 6px;
  padding: 0 6px;
  font-style: normal;
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: #fff;
  background: var(--primary);
  border-radius: 999px;
}
.dispatch-card .card-side {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  flex: none;
  flex-wrap: wrap;
  gap: 8px;
}
.dispatch-card .card-due { display: inline-flex; align-items: baseline; gap: 4px; }
.dispatch-card .card-due .due-date {
  font-size: 14.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.dispatch-card .card-due.warn .due-date { color: var(--warn); }
.dispatch-card .card-due.danger .due-date { color: var(--danger); }
/* 副信息行：字号小一档、行距收紧（单号 / 第 N 道 / 排单 / 产品 / 共 N 道工序 挤在一行） */
.dispatch-card .dispatch-meta { margin-top: 4px; gap: 2px 12px; font-size: 12px; line-height: 17px; }
.dispatch-card .dispatch-meta b { font-weight: 500; }
/**
 * 卡片底栏：左边细进度条 + 「已完成 3/11 道」，右边操作按钮（一条分割线分开）。
 * 老板 2026-10-14：「不要展开工序吧」—— 工序 chip 不再铺在卡片上（要看全链点「明细」）。
 */
.dispatch-card .card-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 9px;
  padding-top: 9px;
  border-top: 1px solid var(--border-2);
}
.dispatch-card .prog-wrap {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 1 1 160px;
  min-width: 0;
}
.dispatch-card .prog-track {
  flex: 1 1 auto;
  min-width: 48px;
  height: 5px;
  background: var(--bg-soft);
  border-radius: 999px;
  overflow: hidden;
}
.dispatch-card .prog-track > i {
  display: block;
  height: 100%;
  background: var(--primary);
  border-radius: 999px;
  transition: width .2s ease;
}
.dispatch-card .prog-text {
  flex: none;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.dispatch-card .card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-left: auto;
}

/* 勾选框：贴左，别把客户名挤走（36px 触控目标） */
.dispatch-card .pick-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: -5px 0 -5px -5px;
  flex: none;
}
.dispatch-card .pick-box input { width: 20px; height: 20px; accent-color: var(--primary); }

/**
 * 卡片底部按钮：**flex 行 + 按钮不收缩**（`flex: 0 0 auto`）。
 * 老板 2026-10-14 的截图就是这里出的问题：行不是 flex、按钮又被压缩，文字被切掉。
 * 标签也全部改成 2～4 个字、不带省略号（见 js/dispatch.js），确保任何宽度下都显示全。
 */
.dispatch-card .card-actions .btn { flex: 0 0 auto; min-height: 30px; padding: 0 12px; white-space: nowrap; }

/* 卡片之间的间距也收一档（列表本身是 flex 列，gap 12px 太松） */
.dispatch-web .list { gap: 8px; }

/* 选人弹层：一行一位师傅（左边姓名、右边负载），整行可点 */
.pick-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 48px;
  padding: 10px 12px;
  margin-bottom: 6px;
  text-align: left;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 14px;
}
.pick-row:hover { border-color: var(--primary); color: var(--primary); }

/* 批量派单条：钉在底部（手机上别被底部安全区盖住） */
.dispatch-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: var(--panel);
  border-top: 1px solid var(--border);
  box-shadow: 0 -6px 16px rgba(0, 0, 0, .06);
}
.dispatch-web.has-pickbar { padding-bottom: 76px; }

/* ---------- 「师傅本月工序分布」矩阵（点第 4 个框打开，2026-10-14）----------
   师傅行 × 工序列 + 右侧四个小计列。列多，所以整个表横向滑动，
   第一列（师傅名）粘住不动 —— 横滑时才知道这一行是谁。 */
/**
 * 顶部数字条：先给一眼能读懂的总数（老板 2026-10-14：「ui 显示和文字数字显示不够清晰」）。
 * 2026-10-15：改成**紧凑卡**（不再拉满整行）—— 老板看到「一块里全是空白」的观感。
 * 大厂做法：卡片按内容给固定区间宽度、左对齐排开，右边留白比「被拉长的卡」干净得多。
 */
.matrix-kpis {
  display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: .7rem;
}
.matrix-kpis .mk {
  flex: 1 1 150px; max-width: 210px;
  display: flex; flex-direction: column; gap: 1px;
  padding: 9px 12px; background: var(--panel-2);
  border: 1px solid var(--border); border-radius: var(--radius);
  border-left: 3px solid var(--border);
}
.matrix-kpis .mk[data-tone='blue'] { border-left-color: var(--primary); }
.matrix-kpis .mk[data-tone='green'] { border-left-color: var(--ok); }
.matrix-kpis .mk[data-tone='amber'] { border-left-color: var(--warn); }
.matrix-kpis .mk[data-tone='red'] { border-left-color: var(--danger); }
.matrix-kpis .mk[data-tone='violet'] { border-left-color: var(--purple); }
.matrix-kpis .mk-label { font-size: 12px; color: var(--muted); }
.matrix-kpis .mk-value {
  font-size: 22px; font-weight: 600; line-height: 27px; color: var(--text);
  font-variant-numeric: tabular-nums; letter-spacing: -.2px;
}
.matrix-kpis .mk-value i { font-style: normal; font-size: 12.5px; font-weight: 400; color: var(--muted); }
.matrix-kpis .mk-sub { font-size: 11.5px; color: var(--muted); }
.matrix-kpis .mk[data-tone='red'] .mk-value { color: var(--danger); }
.matrix-kpis .mk[data-tone='amber'] .mk-value { color: var(--warn); }
.matrix-kpis .mk[data-tone='blue'] .mk-value { color: var(--primary); }
.matrix-kpis .mk[data-tone='violet'] .mk-value { color: var(--purple); }
/**
 * 一行彩色图例（替掉原来那一大段灰字提示条，老板 2026-10-15：
 * 「文字不够明显，全是灰色的，看看那些提示词可以去掉」）。
 * 颜色和表格里的数字一一对应：蓝 = 新单、琥珀 = 重做、灰点 = 预派。
 */
.matrix-legend {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-bottom: .55rem; font-size: 12.5px; color: var(--text-2);
}
.matrix-legend .lg { display: inline-flex; align-items: center; gap: 5px; }
.matrix-legend .lg i {
  width: 9px; height: 9px; border-radius: 999px; background: var(--border);
  display: inline-block;
}
.matrix-legend .lg-new i { background: var(--primary); }
.matrix-legend .lg-rework i { background: var(--warn); }
.matrix-legend .lg-planned i { background: var(--muted); }
/* 长口径收进折叠：默认只一行「口径说明 ▸」 */
.matrix-note-box { margin-top: .6rem; }
.matrix-note-box > summary {
  cursor: pointer; font-size: 12px; color: var(--muted); list-style: none;
  display: inline-flex; align-items: center; gap: 4px;
}
.matrix-note-box > summary::-webkit-details-marker { display: none; }
.matrix-note-box > summary::before { content: '▸'; font-size: 10px; }
.matrix-note-box[open] > summary::before { content: '▾'; }
.matrix-note-box > summary:hover { color: var(--primary); }
.matrix-note-box #matrix-note { margin-top: .35rem; line-height: 1.7; }
.tag-no-perf {
  display: inline-block; margin-left: 8px; padding: 0 6px; line-height: 18px;
  font-size: 11.5px; color: var(--warn); background: var(--warn-soft); border-radius: 4px;
}
.matrix-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/*
 * 「赝复师」那一列**横向滚动时固定**（2026-10-14 老板：手机上也用这张矩阵表）——
 * 手机上表要左右滑，滑到第 7 道工序时得知道这一行是谁的。
 */
.matrix-table th.matrix-name,
.matrix-table td.matrix-name {
  position: sticky; left: 0; z-index: 1; background: var(--panel);
}
.matrix-table thead th.matrix-name { z-index: 2; }
/* 工序多（11~13 道）+ 三个汇总列：桌面给更宽的弹层（宽表就该有宽抽屉） */
.modal.matrix-modal { max-width: min(96vw, 1440px); }
.matrix-table { min-width: 1080px; }
/**
 * ★ 列宽分配（老板 2026-10-15：「左边这个赝复师这里留空太多了」）：
 * 以前只把工序列定死 70px、姓名列没给宽度，浏览器就把多出来的几百像素**全塞给姓名列**，
 * 于是「赝复师」那一列空一大片。
 * 大厂数据网格的做法：**每一类列都给明确的宽度区间**，富余宽度按比例分给工序列（名字长、更需要地方）。
 *   · 姓名列    128px（够放「文盛涛」+「在做 54 道」）
 *   · 工序列    78px（11~13 道，名字两行内读全）
 *   · 数字列    96px 右对齐（在做 / 其中超时 / 本月接单）
 */
/**
 * 姓名列**收缩到内容宽度**（`width: 1%` + `nowrap` 是表格里的经典写法）：
 * 富余宽度全部让给工序列（工序名长、更需要地方），姓名列不再空一大片。
 */
.matrix-table .matrix-name { width: 1%; }
.matrix-table th.ta-right,
.matrix-table td.ta-right { width: 96px; min-width: 96px; }
/**
 * 表头/数字统一加深放大（老板：「文字不够明显，全是灰色的」）——
 * 表头不再用灰字（`--text-2`），工序名直接用正文色；序号徽标保留浅灰底做层次。
 * 2026-10-15：**整张矩阵居中**（表头 + 单元格 + 数字列都是「老板：这个板块中的这些文字居中」）。
 * 注意这不违反「订单表格表头不许居中」那条硬规矩 —— 那条只管 `.orders-table`（列表型表格，
 * 表头居中会随列宽漂移对不上数据）；矩阵是数据网格，居中是标准做法（`_verify-planner-ui.js`
 * 的断言也只扫 `.orders-table`）。
 */
.matrix-table thead th {
  text-align: center; vertical-align: bottom; white-space: normal;
  max-width: 96px; line-height: 1.3; font-weight: 600;
  color: var(--text);
}
.matrix-table thead th.ta-right, .matrix-table tbody td.ta-right { text-align: center; }
.matrix-table tbody td { vertical-align: middle; text-align: center; }
/**
 * 工序列固定 76px 宽：13 道工序一列一道，表头在 2~3 行内能读全
 * （列多了整表横向滑，别把每列压到 40px 让中文竖着一个字一行）。
 */
.matrix-table .matrix-step {
  width: 78px;
  min-width: 78px;
  font-size: 12px;
  line-height: 1.3;
  color: var(--text);
}
.matrix-table .step-no-inline {
  display: inline-block; min-width: 15px; margin-right: 3px; padding: 0 3px;
  font-size: 10.5px; font-weight: 600; color: var(--muted);
  background: var(--bg-soft); border-radius: 3px;
}
.matrix-table .name-sub {
  display: block; margin-top: 1px; font-size: 11px; font-weight: 400;
  color: var(--muted); white-space: nowrap;
}
/* 单元格：左边新单、右边重做（老板 2026-10-14 定的样式） */
.matrix-table .cell-pair { display: inline-flex; align-items: center; gap: 6px; }
.matrix-table .cell-new {
  min-width: 18px; text-align: right; font-size: 14px; font-weight: 600;
  color: var(--primary); font-variant-numeric: tabular-nums;
}
.matrix-table .cell-sep { width: 1px; height: 12px; background: var(--border); }
.matrix-table .cell-rework {
  min-width: 16px; text-align: left; font-size: 13px;
  color: var(--warn); font-variant-numeric: tabular-nums;
}
.matrix-tip .tip-rework { color: var(--warn); }
.matrix-table .matrix-name {
  position: sticky; left: 0; z-index: 1;
  background: var(--panel); white-space: nowrap;
  box-shadow: 1px 0 0 var(--border);
}
.matrix-table thead .matrix-name { background: var(--panel); }
.matrix-table tbody tr:nth-child(even) .matrix-name { background: var(--panel-2); }
.matrix-table .matrix-done { font-size: 14px; font-variant-numeric: tabular-nums; color: var(--text); }
.matrix-table tbody td .matrix-done { color: var(--primary); }
.matrix-table .matrix-zero { color: var(--border-2); }
.matrix-table .cell-planned { min-width: 16px; text-align: left; font-size: 13px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.matrix-tip .tip-planned { color: var(--text-2); font-weight: 500; }
/* 「待分配」那一行：已经轮到、还没派人 —— 分配员最该盯的一行 */
.matrix-table .matrix-pending-row td { background: var(--warn-soft); }
.matrix-table .matrix-pending-row .matrix-name { background: var(--warn-soft); }
.matrix-table .cell-pending { font-size: 14px; font-weight: 600; color: var(--warn); font-variant-numeric: tabular-nums; }
.matrix-table .matrix-pending-row .matrix-zero { color: var(--warn-border, rgba(255, 145, 0, .35)); }
/* 数字与文字再放大一档、行更高一点 —— 「看清楚」优先于「塞得下」 */
.matrix-table td { font-size: 14px; padding: 12px 10px; text-align: center; }
.matrix-table thead th { padding: 8px 8px 10px; }
.matrix-table .matrix-name b { font-size: 16px; }
.matrix-table .name-sub { font-size: 12px; color: var(--text-2); }
.matrix-table .cell-new { font-size: 16px; font-weight: 700; }
.matrix-table .cell-rework { font-size: 15px; font-weight: 600; }
.matrix-table .cell-planned { font-size: 13px; color: var(--text-2); }
/* 重做那份单独用琥珀色小字（不计绩效，一眼能分出来） */
.matrix-table .matrix-rework {
  display: block; margin-top: 1px; font-size: 11px; color: var(--warn); white-space: nowrap;
}
.matrix-table .th-sub { display: block; font-size: 10.5px; font-weight: 400; color: var(--muted); }
/**
 * 列多（11~13 道工序）时最容易「看挪一格」。大厂后台的宽表标准解法四条，
 * 这里一次做齐（老板 2026-10-14：「这里的框分的不清晰，容易看挪，可以画个线么」）：
 *   ① 竖网格线（每个单元格右侧一条细线）；
 *   ② 斑马纹（偶数行浅底）；
 *   ③ 整行悬停高亮（鼠标划到哪一行，整行亮起来，横着读不会串行）；
 *   ④ 冻结首列（师傅名 sticky left）+ 汇总列左侧一条分组线（跟工序区分开）。
 * 表头是浅底 + 底部深线（`table.table th` 自带），冻结首列在横向滚动时也保持可见。
 */
.matrix-table th,
.matrix-table td { border-right: 1px solid var(--border-2); }
.matrix-table th:last-child,
.matrix-table td:last-child { border-right: 0; }
/* 斑马纹（不要写 tbody，否则权重会压过下面的悬停/合计行） */
.matrix-table tr:nth-child(even) td { background: var(--panel-2); }
.matrix-table tr:nth-child(even) .matrix-name { background: var(--panel-2); }
/* 合计行 */
.matrix-table tr.matrix-total td { background: var(--bg-soft); font-weight: 600; }
.matrix-table tr.matrix-total .matrix-name { background: var(--bg-soft); }
/* 悬停整行 —— 列多的时候靠这条锁定「这一行是谁」 */
.matrix-table tbody tr:hover td,
.matrix-table tbody tr:hover .matrix-name { background: var(--primary-soft); }
/* 右边三列是汇总（本月接单 / 在做 / 预派），左边加一条分组线，跟工序格子分开 */
.matrix-table thead th:nth-last-child(3),
.matrix-table tbody td:nth-last-child(3) { border-left: 2px solid var(--border); }
/**
 * 鼠标「十字定位」（老板 2026-10-15：「我要的只是一个横竖的定位行」）：
 * 划过哪一格 → **整行 + 整列一起高亮**，一眼对上「这位师傅 × 这道工序」。
 * 类名由 `dispatch.js` 的 mouseover 处理加/去（`.row-hl` / `.col-hl`）。
 * 权重必须压过斑马纹与合计行，所以写在斑马纹/合计行之后。
 */
.matrix-table tbody tr.row-hl td,
.matrix-table tbody tr.row-hl .matrix-name,
.matrix-table tbody tr td.col-hl,
.matrix-table thead th.col-hl { background: var(--primary-soft); }
.matrix-table thead th.col-hl { color: var(--primary); }
.matrix-table .matrix-unit { margin-left: 2px; font-size: 11.5px; color: var(--muted); }
.matrix-table .matrix-sub { display: block; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.matrix-table .matrix-sub.matrix-zero { color: var(--border-2); }
.matrix-table .matrix-rework-num { font-size: 14px; color: var(--warn); font-variant-numeric: tabular-nums; }


/**
 * 矩阵下面的「按工序明细」（今日在制 / 今日完成 / 本月已完成 三个页签共用一份，
 * 见 `matrixTable.js` 的 `stepRecordsHtml()`）—— 老板 2026-10-15 定稿：
 * **一道工序一段** → 段里**一位师傅一行** → 行里两行 = `新单 N`（这一轮首单的客户名）
 * + `重做 N`（返修的客户名）。工序 / 师傅 / 客户三层都要有：少一层老板就看不明白。
 * 客户名用顿号连起来自动换行，不带任何可点开 / 可收起的东西（老板：「不需要点开什么的，直接展示」）。
 */
.rec-list { margin-top: .8rem; padding-top: .6rem; border-top: 1px solid var(--border-2); }
/* 明细开头那行小字（这一页的条数 / 截断提示）：12px 灰字，别抢戏。
   现在是一行 flex：左边文字、右边插槽（派单台的「今天领的」入口就在右边，见下面 .rec-head-toggle） */
.rec-list-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
                 font-size: 12px; color: var(--muted); margin-bottom: .25rem; }
.rec-list-head-text { min-width: 0; }
.rec-list-head-right { margin-left: auto; }
.rec-list-foot { font-size: 12px; color: var(--muted); margin-top: .4rem; }
.rec-step-block { padding: .55rem 0; border-top: 1px dashed var(--border-2); }
.rec-step-block:first-of-type { border-top: none; }
.rec-step-head { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.rec-step-head b { font-size: 14.5px; }
.rec-step-count { font-size: 12px; color: var(--muted); }
.rec-op-row { display: flex; align-items: flex-start; gap: .55rem; margin-top: .45rem; }
/* 师傅名：整段里最重的一档（老板 2026-10-15：「师傅名字突出一点」），左边一条细色条当锚点 */
.rec-op {
  flex: 0 0 auto; width: 4.9rem; padding-left: .45rem;
  border-left: 3px solid var(--primary);
  font-size: 15px; font-weight: 700; color: var(--text); line-height: 1.35;
}
.rec-op-lines { flex: 1 1 auto; min-width: 0; }
.rec-line { display: flex; align-items: baseline; gap: .5rem; margin-top: .22rem; }
.rec-line:first-child { margin-top: 0; }
/* 只有一行「灰字小信息」的那种行（按客户的重做单那条 = 单号 · 轮次 · 时间 · 状态 · 在做） */
.rec-line-meta { flex-wrap: wrap; gap: 2px 12px; font-size: 12.5px; color: var(--muted); }
.rec-kind {
  flex: 0 0 auto; min-width: 3.6rem; text-align: center;
  padding: 0 5px; border-radius: 4px; font-size: 11.5px; line-height: 17px;
}
.rec-kind.rec-new { background: var(--primary-soft); color: var(--primary); }
.rec-kind.rec-rework { background: var(--warn-soft); color: var(--warn); }
/* 客户名：压暗一档、字号小一档，让一眼扫过去先看到「师傅」和数字 */
.rec-names { font-size: 12.5px; line-height: 1.65; color: var(--muted); word-break: break-word; }
/* 客户名后「×3」= 这个客户这单是 3 根手指，这一步算 3 道（2026-10-28 按根数折算口径） */
.rec-x { font-style: normal; font-weight: 700; color: var(--primary); }
.rec-empty { margin-top: .6rem; }
@media (max-width: 640px) {
  .rec-op { width: 4.1rem; font-size: 14px; }
  .rec-kind { min-width: 3.4rem; }
  .rec-names { font-size: 12px; }
}

/* ==================================================================
 * 派单台「师傅今天领的步骤」（2026-10-24 老板：「我想看到师傅今天领的步骤」）
 *
 * 位置：「在做 N 道工序（今天接的 …）」那一行的**右边**放一颗入口按钮（.rec-head-toggle），
 * 点开在明细上方展开一块（.tp-box）：师傅 → 他今天领的每一道
 * （第 N 道 · 工序名 · 客户 · 领单时刻 · 新单/重做）。默认收起。
 * 数据与渲染见 `dispatch.js` 的 todayPickToggleHtml() / todayPickHtml()；回退开关 TODAY_PICK_ENABLED。
 * ================================================================== */
.rec-head-toggle {
  display: inline-flex; align-items: center; gap: .3rem;
  min-height: 26px; padding: .1rem .55rem;
  border: 1px solid var(--border-2); border-radius: 999px;
  background: var(--bg-soft); color: var(--primary);
  font-family: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.rec-head-toggle:hover { background: var(--primary-soft); border-color: var(--primary); }
.rec-head-caret { font-size: 11px; color: var(--muted); font-weight: 400; }

.tp-box {
  margin: .1rem 0 .6rem; padding: .5rem .65rem;
  border: 1px solid var(--border-2); border-radius: 10px;
  background: var(--bg-soft);
}
.tp-head { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.tp-head b { font-size: 13.5px; }
.tp-hint { font-size: 11.5px; color: var(--muted); }
.tp-op { padding: .45rem 0; border-top: 1px dashed var(--border-2); }
.tp-op:first-of-type { border-top: none; }
/* 一道一行（窄屏自动折行，仍然左对齐、时间跟在客户后面） */
.tp-row {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  margin-top: .3rem; font-size: 12.5px; line-height: 1.5;
}
.tp-step {
  flex: 0 0 auto; min-width: 3.6rem;
  color: var(--primary); font-weight: 600; font-variant-numeric: tabular-nums;
}
.tp-name { flex: 0 1 auto; min-width: 0; color: var(--text); word-break: break-word; }
.tp-cust { flex: 0 1 auto; color: var(--muted); word-break: break-word; }
/* 时间 + 新单/重做 徽标捆成一组、靠右对齐（整列对齐看得出先后，手机上整组换行） */
.tp-meta {
  margin-left: auto; display: inline-flex; align-items: baseline; gap: .45rem;
  flex: 0 0 auto; flex-wrap: nowrap;
}
.tp-time { color: var(--muted); font-variant-numeric: tabular-nums; }
.tp-row .rec-kind { min-width: 3.1rem; }
@media (max-width: 640px) {
  .tp-row { font-size: 12px; gap: .3rem .4rem; }
  .tp-step { min-width: 3.2rem; }
  /* 手机上让「客户」跟着工序名走，时间 + 徽标那组整组换到下一行右下角 —— 别让客户被推到最右 */
  .tp-name { flex: 0 1 auto; }
}
.matrix-table tr.matrix-total td { font-weight: 600; background: var(--bg-soft); }
.matrix-table tr.matrix-total .matrix-name { background: var(--bg-soft); }

/* 手机上三个数据框挤成一行（否则一进页面先滑过一整屏数字才看到待分配的活） */
@media (max-width: 640px) {
  .dispatch-web .kpi-row {
    /* 现在是 4 个框：2×2 铺开（一屏就能看到「谁做得少」那格的数字） */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px;
    margin-bottom: .6rem;
  }
  .dispatch-web .kpi-box { padding: 8px 9px; gap: 2px; }
  .dispatch-web .kpi-box .kpi-label { font-size: 11.5px; }
  .dispatch-web .kpi-box .kpi-value { font-size: 20px; line-height: 26px; }
  .dispatch-web .kpi-box .kpi-sub { font-size: 11px; line-height: 15px; }
  /* 手机上的触控目标不能小于 36px：卡片按钮单独放大回来（AGENTS §7.4） */
  .dispatch-card .card-actions .btn { min-height: 36px; }
  .dispatch-card .pick-box { width: 36px; height: 36px; margin: -6px 0 -6px -6px; }
}

/* 2026-10-23 老板：排单员 / 派单台 / 发货中心 / 师傅端 / 车间大屏的「单/派/配/师/假」
   方块统一换公司 logo（图片自带底色，所以容器不要蓝底） */
.logo-img {
  background: transparent !important;
  padding: 0 !important;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.logo-img img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  border-radius: inherit;
}
/* 2026-10-24 修：手机上 .logo 没有固定宽高，图片会撑满整张卡（赝复师端截图报的）。
   这里给容器定死尺寸，内联 style 的（车间大屏 44px）仍然优先。 */
.logo-img {
  width: 30px !important;
  height: 30px !important;
  flex: 0 0 auto;
}
@media (max-width: 900px) {
  .logo-img { width: 34px !important; height: 34px !important; }
}

/* ==================================================================
 * 订单中心：手机上的数据框折叠成一行（2026-10-23 老板）
 * 「手机上订单中心要先滚过 8 个数据框才看到列表（实测 686px）」
 *
 * · 只在 body.center-view 且 ≤900px 时出现这一行；
 * · body.kpi-folded 时 #kpi-grid 收起（桌面端永不折叠，见 planner.js 的 setKpiFolded）；
 * · 摘要文字由 renderKpis() 同步（制作工单 / 超时 / 待收）。
 * ================================================================== */
.kpi-mini { display: none; }
@media (max-width: 900px) {
  body.center-view .kpi-mini {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    padding: 0.45rem 0.6rem;
    border-radius: 10px;
    background: var(--bg-soft, #f3f4f6);
  }
  body.center-view .kpi-mini #kpi-mini-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  body.center-view.kpi-folded #kpi-grid {
    display: none;
  }
}
@media (min-width: 901px) {
  /* 桌面端一律不折叠：数据框照旧铺开，摘要行也不出来 */
  body.kpi-folded #kpi-grid {
    display: grid;
  }
}
