/* ============ 包装订单管理系统 ============ */
:root {
  --bg: #f3f5f8;
  --card: #ffffff;
  --line: #e3e7ee;
  --text: #232a35;
  --muted: #6b7280;
  --brand: #1f6feb;
  --brand-dk: #1756c0;
  --green: #2f9e44;
  --red: #e03131;
  --orange: #e8590c;
  --amber: #f08c00;
  --radius: 10px;
  --shadow: 0 1px 3px rgba(20, 30, 60, .08);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* 关键：横向滑/划到页面边缘时，不让浏览器把"返回/前进"手势接走，
   否则在触屏/触摸板上划一下就会把当前页面（如新建订单）带走 */
html, body { overscroll-behavior-x: none; }
body {
  font-family: "Segoe UI", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif;
  background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.5;
}
.hidden { display: none !important; }
a { color: var(--brand); text-decoration: none; }

/* ---------- 登录 ---------- */
#login-view {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  /* ⚠ 方向必须是 column。默认的 row 下，下面那个 .icp-foot 会变成**第二个 flex 项、
     被横排到登录卡片的右边**（症状是「备案号跑到登录框旁边了」）。
     column + center 让它自然落在卡片正下方、水平居中，**一个定位属性都不用加**。
     ⚠ 别为了挪位置给它加 position:fixed/absolute：卡片实测高 ≈369px，矮屏
     （横屏手机 ≈375px，或窗口拖得很矮）时卡片下沿正好落在那条上 ⇒ 备案号横压在
     卡片下沿（2026-09-23 两个方案都实测过，都会压字）；column 排布则让容器跟着
     内容长高、永远不会压。
     ⚠ 别给这里补 `justify-content: safe center`（2026-09-23 试过，是个**恒等变换**）：
     容器是 `height:auto + min-height:100vh`，内容比屏幕高时它会**跟着长到内容那么高**，
     于是容器内**没有富余空间**，`justify-content` 无物可分配 ⇒ center 与 safe center
     必然同解；内容比屏幕矮时两者也都是居中。矮屏上要靠滚动才看得到页脚是正常现象，
     **不是**「居中把顶部裁掉」那一类 bug（那种 bug 需要容器高度被钉死才会出现）。 */
  gap: 14px;
  background: linear-gradient(135deg, #eaf2ff 0%, #f6f8fc 60%);
}
/* 登录页底部那一块：版权行 + ICP 备案号（工信部要求首页挂号并链到 beian.miit.gov.cn）。
   两行之间用 <br> 分行、同吃这一条规则，所以这里没有「行间距」要管。
   位置完全靠上面 `#login-view` 的 flex 排布，**本规则只负责字号和颜色** —— 没有
   left/right/bottom，也没有 position，正因为如此它在任何屏幕高度下都不会压到卡片。
   ⚠ 别把它挪出 `#login-view`：它是靠父级被 .hidden 才跟着消失的（登录后不出现）。 */
.icp-foot {
  font-size: 12px; color: var(--muted); text-align: center;
}
.icp-foot a { color: inherit; }
.icp-foot a:hover { text-decoration: underline; }
.login-card {
  width: 360px; max-width: 92vw; background: var(--card); border-radius: 14px;
  padding: 34px 30px 26px; box-shadow: 0 10px 30px rgba(20, 40, 90, .12);
}
.login-brand { font-size: 20px; font-weight: 700; text-align: center; }
.login-sub { color: var(--muted); text-align: center; font-size: 12px; margin: 6px 0 22px; }
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 5px; }
.field input, .field select, .field textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 14px; background: #fff; color: var(--text);
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(31,111,235,.12);
}

/* 新建订单「订单类型」单选胶囊（成品单 / 半成品单 / 定制单） */
.ot-radios { display: flex; flex-wrap: wrap; gap: 8px; }
.ot-radios .ot-r {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 12px;
  border: 1px solid var(--line); border-radius: 999px; background: #fff;
  font-size: 13px; color: var(--text); cursor: pointer; user-select: none;
}
.ot-radios .ot-r:hover { border-color: var(--brand); }
.ot-radios .ot-r input { width: auto; height: auto; padding: 0; margin: 0; border: none; box-shadow: none; accent-color: var(--brand); cursor: pointer; }
.ot-radios .ot-r input:focus { box-shadow: none; }
.ot-radios .ot-r:has(input:checked) {
  border-color: var(--brand); background: #eef5ff; color: var(--brand-dk); font-weight: 600;
}

/* ---------- 商品设置 · 字段编辑器 ---------- */
.ff-grid { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.ff-grid > .ff-card { flex: 1 1 300px; min-width: 0; }
/* 商品设置 →「商品字段」和「定制产品字段」两张卡：比同行的「材质/规格选项」宽，且两张严格同宽。
   宽度 = basis + (grow/Σgrow)·剩余空间。两张宽卡 basis 相同、grow 相同（1.5），
   所以无论容器多宽，这两张算出来的宽度永远相等 —— 要「一样宽」就得让 basis 和 grow 都一致，
   只改其中一个都会让两张随容器宽度错开。
   basis 330 而非 540：两张宽卡 + 一张窄卡若按 540 起算，1400px 容器里 540×2+300+32 = 1412 放不下，
   「材质/规格选项」会被挤到第二行。330 起算合计 992px 仍在一行，窄屏才照旧换行落整宽。
   1400px 容器下实测 ≈ 483 / 483 / 402。 */
.ff-grid > .ff-card-wide { flex: 1.5 1 330px; }

/* ---------- 系统设置 · 数据维护卡里的并排行 ----------
   四段说明两两并排（②③ 一行、④⑤ 一行），不再一条压一条。
   两栏等宽：basis 相同 + grow 相同 ⇒ 任何容器宽度下两栏严格相等（与上面两张宽卡同一套算法）。
   basis 320：两个 320 + gap 16 = 656px 起才并排，比这窄（手机/窄窗）自动落回上下堆叠。 */
.maint-row { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.maint-row > * { flex: 1 1 320px; min-width: 0; }
.ff-row {
  display: flex; align-items: flex-start; gap: 10px; padding: 6px 2px;
  border-bottom: 1px solid var(--line);
}
.ff-row:last-child { border-bottom: none; }
.ff-main { flex: 1; min-width: 0; }
.ff-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ff-line input[data-key="label"] {
  flex: 1; min-width: 140px; padding: 7px 10px; border: 1px solid var(--line);
  border-radius: 8px; font-size: 13px; background: #fff; color: var(--text);
}
.ff-line select { padding: 7px 9px; border: 1px solid var(--line); border-radius: 8px; font-size: 13px; background: #fff; }
.ff-line input:focus, .ff-line select:focus { outline: none; border-color: var(--brand); }
.ff-badge {
  font-size: 11px; padding: 2px 7px; border-radius: 10px; white-space: nowrap;
}
.ff-b-builtin { background: #eef1f6; color: #5b6472; }
.ff-b-custom { background: #e6f2ff; color: var(--brand-dk); }
.ff-static { font-size: 12px; color: var(--muted); }
.ff-toggles { margin-top: 6px; display: flex; align-items: center; gap: 12px; font-size: 12px; color: var(--muted); }
.ff-toggles label.ff-chk { display: flex; align-items: center; gap: 4px; cursor: pointer; }
.ff-toggles label.ff-chk input { accent-color: var(--brand); }
.ff-field { margin-top: 6px; max-width: 300px; }
.ff-field label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.ff-field input { width: 100%; padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; font-size: 13px; }
.ff-actions { display: flex; align-items: center; gap: 5px; flex: none; }
.ff-actions .btn-sm { padding: 4px 8px; }
.ff-max { font-size: 11px; color: var(--muted); }
.craft-row .ff-line input {
  flex: 1; min-width: 120px; padding: 7px 10px; border: 1px solid var(--line);
  border-radius: 8px; font-size: 13px; background: #fff; color: var(--text);
}
.craft-row .ff-line input:focus { outline: none; border-color: var(--brand); }

/* ---------- 一组胶囊式勾选框 ----------
   用到两处：订单工艺类型（新建订单 / 订单信息编辑），以及商品资料里「复选框」类型的
   自定义字段（见 app.js productFormFields）。类名带 craft 是历史原因，做法是通用的。 */

.craft-checks { display: flex; flex-wrap: wrap; gap: 8px; }
.craft-checks .craft-ck {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 12px;
  border: 1px solid var(--line); border-radius: 999px; background: #fff;
  font-size: 13px; color: var(--text); cursor: pointer; user-select: none;
}
.craft-checks .craft-ck:hover { border-color: var(--brand); }
.craft-checks .craft-ck input { accent-color: var(--brand); margin: 0; cursor: pointer; }
.craft-checks .craft-ck:has(input:checked) {
  border-color: var(--brand); background: #eef5ff; color: var(--brand-dk); font-weight: 600;
}
/* 订单行里的自定义字段单元格（新建订单页按「新建订单」开关出列；见 app.js cfCellHTML）。
   在窄格子里把控件收紧：输入/下拉限宽别撑破表，胶囊勾选框缩小一号、间距收窄。 */
.cf-cell { min-width: 110px; }
.cf-cell input, .cf-cell select { max-width: 100%; }
.cf-cell .craft-checks { gap: 4px; }
.cf-cell .craft-ck { padding: 3px 8px; font-size: 12px; }

/* 产品明细里每行的「工艺」列（仅当订单信息勾了工艺才显示，列名/格子联动隐藏） */
#no-lines-table.no-crafts .craft-col,
#no-lines-table.no-crafts .craft-cell { display: none; }
.craft-cell { min-width: 150px; }
.craft-cell .cc {
  display: inline-flex; align-items: center; gap: 3px; margin: 2px 8px 2px 0;
  font-size: 12px; color: var(--text); cursor: pointer; user-select: none; white-space: nowrap;
}
.craft-cell .cc input { accent-color: var(--brand); margin: 0; cursor: pointer; }
.craft-cell .cc:has(input:checked) { color: var(--brand-dk); font-weight: 600; }

/* 「新增/编辑客户」弹窗里那张意向表的工艺格：勾选框**排成一排不折行**。
   `data-intents="modal"` 是意向表容器上的标记（页面详情那份是 "page"），所以这条只作用于
   弹窗 —— 订单行那格（`#no-lines-table`，还是 150px 的窄格）照样折行，那儿折行是对的。
   min-width 只是**下限**：工艺是「商品设置 · 产品工艺」里可配的，条数多了表格会自己变宽
   （`.modal-body` 是 overflow:auto，撑不下就横向滚，不会把勾选框挤没）。 */
[data-intents="modal"] .craft-cell { min-width: 340px; }
[data-intents="modal"] .craft-checks { flex-wrap: nowrap; }
.login-err { color: var(--red); font-size: 13px; min-height: 18px; margin-bottom: 4px; }

/* ---------- 订单列表 · 行内展开（产品明细与进度） ---------- */
.order-expand-row td { padding: 4px 14px 14px; background: #fafbfc; }
.inline-expand { font-size: 13px; }
.ie-head { display: flex; align-items: center; gap: 14px; margin: 6px 0 10px; }
.ie-title { font-weight: 600; font-size: 14px; }
.ie-link { color: var(--brand); font-size: 12px; cursor: pointer; text-decoration: none; }
.ie-link:hover { text-decoration: underline; }
.tbl-compact { font-size: 12px; }
.tbl-compact th, .tbl-compact td { padding: 5px 8px; }
.login-tip { margin-top: 16px; font-size: 12px; color: #a0a7b4; text-align: center; }
.btn-block { width: 100%; }

/* ---------- 按钮 ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  border: 1px solid transparent; border-radius: 8px; padding: 7px 14px;
  font-size: 13px; cursor: pointer; background: #eef1f6; color: var(--text);
  transition: filter .12s, background .12s; white-space: nowrap; user-select: none;
}
.btn:hover { filter: brightness(.97); }
.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:hover { background: var(--brand-dk); }
.btn-green { background: var(--green); color: #fff; }
.btn-red { background: #fff; color: var(--red); border-color: #f3c1c1; }
.btn-red:hover { background: #fdecec; }
.btn-ghost { background: transparent; color: #d7deea; border-color: rgba(255,255,255,.22); }
.btn-sm { padding: 3px 9px; font-size: 12px; border-radius: 7px; }
.btn-xs { padding: 1px 8px; font-size: 12px; border-radius: 6px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* ---------- 整体布局（左侧竖菜单 + 右侧页签区） ---------- */
.layout { display: flex; height: 100vh; overflow: hidden; }
.sidebar {
  flex: none; width: 108px; background: #f7f8fa; border-right: 1px solid var(--line);
  display: flex; flex-direction: column; min-height: 0;
}
.sidenav { flex: 1; overflow-y: auto; padding: 8px 6px; }
.sn-item {
  display: flex; align-items: center; gap: 7px; padding: 7px 8px; margin-bottom: 2px;
  border-radius: 6px; cursor: pointer; color: #3a4352; font-size: 13px; user-select: none; white-space: nowrap;
}
.sn-item .ic { flex: none; width: 15px; text-align: center; }
.sn-item:hover { background: #eef1f6; color: var(--text); }
.sn-item.active { background: var(--brand); color: #fff; font-weight: 600; }
/* 右侧飞散子菜单：悬停/点击一级项，在它右边弹出一个浮层框 */
.sn-hint { flex: none; font-size: 10px; color: var(--muted); margin-left: auto; }
.sn-flyout {
  position: fixed; display: none; min-width: 150px; z-index: 70;
  background: #fff; border: 1px solid var(--line); border-radius: 9px; padding: 5px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); white-space: nowrap;
}
.sn-flyout.open { display: block; }
.sn-flyout-item {
  display: block; padding: 7px 10px; border-radius: 6px; cursor: pointer;
  font-size: 12px; color: #3a4352; user-select: none;
}
.sn-flyout-item:hover { background: #eef1f6; color: var(--text); }
.sn-flyout-item.active { background: #e3ecfb; color: var(--brand); font-weight: 600; }
/* 右上角：用户下拉菜单（与页签同行） */
.tabbar-user { flex: none; position: relative; margin-left: auto; padding: 0 8px 0 12px; border-left: 1px solid var(--line); display: flex; align-items: center; }
/* 当前公司那颗胶囊。**默认隐藏**，由 coChipRender() 打开 —— 单人单公司时它整块不出现，
   顶栏跟改造前一模一样。 */
.co-chip-wrap { position: relative; display: none; }
.co-chip-wrap.on { display: block; }
.co-chip {
  display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 10px;
  border: 1px solid var(--line); background: #f7f9fc; border-radius: 15px;
  font: inherit; font-size: 12px; color: var(--text); cursor: default; max-width: 200px;
}
.co-chip.can-switch { cursor: pointer; }
.co-chip.can-switch:hover { background: #eef1f6; }
.co-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: #37b26c; }
#co-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.co-caret { flex: none; font-size: 9px; color: var(--muted); }
/* 只有点得开的时候才画那个小三角 —— 不能切的人看到一个箭头会去点它 */
.co-chip:not(.can-switch) .co-caret { display: none; }
.co-menu-head { padding: 6px 10px 7px; font-size: 12px; color: var(--muted); }
.co-item.cur { color: var(--brand); font-weight: 600; }
.co-item .co-tick { float: right; color: var(--brand); }
.co-sep { height: 1px; background: var(--line); margin: 4px 0; }
.tu-trigger {
  display: flex; align-items: center; gap: 6px; height: 34px; padding: 0 8px;
  border: none; background: transparent; border-radius: 8px; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--text); white-space: nowrap;
}
.tu-trigger:hover { background: #eef1f6; }
.tu-avatar {
  flex: none; width: 22px; height: 22px; border-radius: 50%; background: var(--brand); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600;
}
.tu-name { font-size: 12px; color: var(--text); max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.tu-caret { flex: none; font-size: 10px; color: var(--muted); }
.dropmenu {
  position: absolute; right: 4px; top: calc(100% + 4px); min-width: 158px; z-index: 60;
  background: #fff; border: 1px solid var(--line); border-radius: 9px; padding: 5px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); display: none;
}
.dropmenu.open { display: block; }
.dm-head {
  padding: 7px 10px 8px; border-bottom: 1px solid var(--line); margin-bottom: 4px;
  font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dm-item {
  display: block; width: 100%; text-align: left; padding: 8px 10px;
  border: none; background: transparent; border-radius: 6px; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--text);
}
.dm-item:hover { background: #eef1f6; }
/* 总看板的「合计」行：钉在表尾、跟上面几家的行明显分开。
   只加粗+底色，不换颜色 —— 这一行是同样口径的加法结果，不是「异常」。 */
.tbl tbody tr.row-total td { border-top: 2px solid var(--line); background: #f7f9fc; font-weight: 600; }

.main-col {
  flex: 1; min-width: 0; height: 100vh; display: flex; flex-direction: column;
  background: var(--bg);
}

/* ---------- 顶部页签条 ---------- */
.tabbar {
  flex: none; height: 40px; background: #fff; border-bottom: 1px solid var(--line);
  display: flex; align-items: stretch; padding: 0 8px; z-index: 30;
}
.tablist {
  flex: 1; min-width: 0; display: flex; align-items: stretch; overflow-x: auto;
  overscroll-behavior-x: contain; scrollbar-width: none;
}
.tablist::-webkit-scrollbar { display: none; }
.tab {
  display: inline-flex; align-items: center; gap: 5px; padding: 0 10px 0 14px;
  white-space: nowrap; font-size: 13px; color: var(--muted); cursor: pointer;
  border-bottom: 2px solid transparent; border-right: 1px solid #eef1f4;
  user-select: none; flex: none;
}
.tab:hover { background: #f7f9fc; color: var(--text); }
.tab.active { color: var(--brand); font-weight: 600; border-bottom-color: var(--brand); background: #f2f7ff; }
.tab .pin { font-size: 11px; }
.tab-x {
  border: none; background: none; cursor: pointer; color: #aab2bf; font-size: 14px;
  line-height: 1; padding: 0 0 0 4px; margin-left: 2px; border-radius: 4px;
}
.tab-x:hover { color: var(--red); }

/* ---------- 页签容器 ---------- */
#pane-root { flex: 1; min-height: 0; overflow: auto; background: var(--bg); }
.tabpane { display: none; padding: 14px 18px 60px; }
.tabpane.active { display: block; }
/* 模块页内顶部工具行 */
.page-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 14px; }
.page-title h2 { margin: 0; font-size: 18px; }
.page-title .sub { color: var(--muted); font-size: 13px; }
.page-title .pt-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* ---------- 列表页：左筛选 + 右内容 ---------- */
.list-page { display: flex; align-items: flex-start; gap: 14px; }
.filters {
  flex: none; width: 224px; background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: 12px;
  position: sticky; top: 6px; max-height: calc(100vh - 120px); overflow: auto;
}
.fi-title { font-size: 12px; color: var(--muted); margin: 14px 0 8px; font-weight: 600; }
.fi-title:first-child { margin-top: 2px; }
.fi-row { display: flex; gap: 6px; margin-bottom: 8px; }
.fi-row .search { flex: 1; width: auto; }
/* 左筛选里的下拉（入库记录页的 入库人 / 产品编码）。裸 select 在这条筛选栏里既没宽度
   规则也没内边距：`width` 取内容宽，长编码会把 224px 的栏顶破。跟 .fi-row .search 同宽口径。 */
.fi-row select {
  flex: 1; width: auto; min-width: 0; border: 1px solid var(--line); border-radius: 8px;
  padding: 5px 8px; font-size: 13px; background: #fff; color: #3a4352;
}
.fi-radio {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; text-align: left; border: none; background: none; cursor: pointer;
  padding: 6px 10px; border-radius: 7px; font-size: 13px; color: #3a4352; margin-bottom: 2px;
}
.fi-radio:hover { background: #f2f5fa; }
.fi-radio.on { background: #e7f0ff; color: var(--brand-dk); font-weight: 600; }
.fi-radio .cnt { font-size: 12px; opacity: .6; font-variant-numeric: tabular-nums; }
.content { flex: 1; min-width: 0; }

/* ---------- 数据看板：顶部 KPI 卡排 + 订单页式左筛选 + 表内滚动 ---------- */
.filters .fi-row > select {
  flex: 1; min-width: 0; width: 100%;
  border: 1px solid var(--line); border-radius: 20px; padding: 5px 10px;
  font-size: 13px; background: #fff; color: var(--text);
}
.filters .fi-row > select:focus { outline: none; border-color: var(--brand); }
#bd-kpis { display: flex; gap: 10px; flex-wrap: wrap; padding: 0 0 14px; flex: none; }
#bd-kpis .kpi {
  flex: 1 1 0; min-width: 118px; max-width: 200px;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 9px 12px 8px;
}
#bd-kpis .kpi .k { font-size: 20px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--text); }
#bd-kpis .kpi .t { color: var(--muted); font-size: 12px; margin-top: 3px; white-space: nowrap; }
#bd-kpis .kpi.ud .k { color: var(--amber); }
#bd-kpis .kpi.od .k { color: var(--brand); }
#bd-kpis .kpi.dl .k { color: var(--green); }
#bd-tblwrap { min-height: 140px; }
#bd-tblwrap .bd-empty { padding: 44px 16px; text-align: center; color: var(--muted); }

/* 看板关键字框下面的快捷词按钮（词表来自 系统设置 → 看板快捷关键词）。
   走 flex-wrap：词多/词长时自动换行，不撑破左筛选栏。 */
.filters .fi-qks { flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.filters .fi-qk {
  border: 1px solid var(--line); background: #fff; color: #3a4352;
  border-radius: 999px; padding: 3px 10px; font-size: 12px; cursor: pointer;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.filters .fi-qk:hover { background: #f2f5fa; }
.filters .fi-qk.on { background: #e7f0ff; border-color: var(--brand); color: var(--brand-dk); font-weight: 600; }

/* ---------- 页签右键菜单 ---------- */
.ctxmenu {
  position: fixed; z-index: 150; min-width: 150px; background: #fff;
  border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 30px rgba(20, 30, 60, .16); padding: 5px;
}
.ctxmenu .cm-item {
  display: block; width: 100%; text-align: left; border: none; background: none;
  padding: 7px 12px; border-radius: 7px; font-size: 13px; cursor: pointer; color: var(--text);
}
.ctxmenu .cm-item:hover { background: #f0f4fa; }
.ctxmenu .cm-sep { height: 1px; background: var(--line); margin: 4px 6px; }
.ctxmenu .cm-item.danger { color: var(--red); }
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.spacer { flex: 1; }

/* ---------- 卡片 ---------- */
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 16px; margin-bottom: 16px;
}
.card h3 { margin: 0 0 12px; font-size: 15px; display: flex; align-items: center; gap: 8px; }
.card h3 .sub { font-weight: normal; color: var(--muted); font-size: 12px; }
/* 窄卡：整页三分之一宽。min-width 兜住窄屏，别把里面的按钮组挤扁 */
.card.card-third { width: 33.333%; min-width: 280px; }
/* 半宽卡：放 .ff-grid 里两张并排。basis 扣掉 gap，min-width 让窄屏自动落回整宽。
   保留 .card 自带的 margin-bottom —— flex 容器的行高把它算进去，正好和下一张卡留出间距 */
.card.card-half { flex: 1 1 calc(50% - 8px); min-width: 280px; }

/* ---------- 统计方块 ---------- */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; margin-bottom: 16px; }
.tile {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 14px 16px; cursor: pointer;
}
.tile:hover { border-color: var(--brand); }
.tile .n { font-size: 28px; font-weight: 700; line-height: 1.1; }
.tile .t { color: var(--muted); font-size: 13px; margin-top: 2px; }
.tile.red .n { color: var(--red); }
.tile.amber .n { color: var(--amber); }
.tile.green .n { color: var(--green); }
.tile.blue .n { color: var(--brand); }

/* ---------- 表格 ---------- */
table.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
table.tbl-grid { table-layout: fixed; }   /* 产品明细表：按 colgroup 固定列宽，表头/数据逐列对齐 */
table.tbl th {
  text-align: left; color: var(--muted); font-weight: 600; font-size: 12px;
  padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
table.tbl td { padding: 8px; border-bottom: 1px solid #eef1f6; vertical-align: top; }
table.tbl tbody tr.clickable { cursor: pointer; }
table.tbl tbody tr.clickable:hover { background: #f6f9ff; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
table.tbl-left .num { text-align: left; }   /* 订单列表：数字列也统一左对齐 */
tr.row-red td { background: #fff4f4; }
tr.row-mute td { color: #9aa2af; }
tr.row-over td { background: #fdeaea; }
.ov-badge, .chip {
  display: inline-block; padding: 1px 8px; border-radius: 20px; font-size: 12px; line-height: 1.6;
}
.ov-badge { background: var(--red); color: #fff; font-weight: 600; }
.chip-blue { background: #e7f0ff; color: var(--brand-dk); }
.chip-green { background: #e4f6e7; color: #1b7f30; }
.chip-red { background: #fdeaea; color: var(--red); }
.chip-gray { background: #eef1f6; color: var(--muted); }
.chip-amber { background: #fff3e0; color: var(--amber); }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.mono { font-variant-numeric: tabular-nums; font-family: Consolas, monospace; }
/* 单号前的订单类型标记：成=成品  定=定制  半=半成品生产单 */
.ortag { display: inline-block; min-width: 18px; padding: 0 3px; margin-right: 6px; border-radius: 4px;
  text-align: center; color: #fff; font-size: 11px; line-height: 18px; font-weight: 600; vertical-align: 1px; }
.ortag.ot-f { background: var(--brand); }   /* 成 普通订单 */
.ortag.ot-c { background: #9b59b6; }        /* 定 定制订单 */
.ortag.ot-h { background: var(--orange); }  /* 半 半成品生产单 */

/* 阶段徽标 */
.st { display: inline-block; padding: 2px 9px; border-radius: 6px; font-size: 12px; white-space: nowrap; }
.st-pending { background: #eef1f6; color: #5b6472; }
.st-semi { background: #e7f0ff; color: var(--brand-dk); }
.st-finished { background: #e4f6e7; color: #1b7f30; }

/* ---------- 面板分区 ---------- */
.panel-title { font-weight: 600; margin: 4px 0 10px; display: flex; align-items: center; gap: 8px; }
.panel-title .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.dot-red { background: var(--red); }
.dot-green { background: var(--green); }
.dot-blue { background: var(--brand); }
.dot-amber { background: var(--amber); }
.emptynote { color: var(--muted); text-align: center; padding: 26px 0; }

/* ---------- 动态 feed ---------- */
.feed-item {
  display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px dashed #eef1f6; align-items: baseline;
}
.feed-item .at { color: #a0a7b4; font-size: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.feed-item .who { color: var(--muted); font-size: 12px; white-space: nowrap; }
.feed-item .what { flex: 1; }
.feed-item a { color: var(--text); }
.feed-item:hover a { color: var(--brand); }

/* ---------- 筛选条 ---------- */
.filter-bar { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; align-items: center; }
.fbtn { border: 1px solid var(--line); background: #fff; padding: 4px 12px; border-radius: 20px; cursor: pointer; font-size: 13px; color: var(--text); }
.fbtn.active { background: var(--brand); border-color: var(--brand); color: #fff; }
.fbtn .cnt { opacity: .7; font-size: 12px; margin-left: 2px; }
.search {
  border: 1px solid var(--line); border-radius: 20px; padding: 5px 12px; width: 200px; font-size: 13px; background: #fff;
}

/* ---------- 订单详情 ---------- */
.oid-title { font-size: 16px; font-weight: 700; }
.oid-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px 22px; margin-top: 12px; }
.oid-meta .mi label { display: block; font-size: 12px; color: var(--muted); }
.oid-meta .mi .v { font-size: 15px; }
.line-note { color: var(--muted); font-size: 12px; }
.del-chip { display: inline-flex; align-items: center; gap: 5px; background: #f3f6fb; border-radius: 8px; padding: 2px 7px; margin: 2px 6px 2px 0; font-size: 12px; }
.actioncell { display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
/* 订单详情相关表：操作列按钮与表头「操作」一起左对齐（列表主表 / 行内展开 / 单笔详情明细） */
table[data-tblk="orders"] .actioncell,
table[data-tblk="orders-exp"] .actioncell,
table[data-tblk="order-detail"] .actioncell { justify-content: flex-start; }

/* ---------- 弹窗 ---------- */
#modal-root { position: fixed; inset: 0; z-index: 100; display: none; }
#modal-root.on { display: flex; align-items: center; justify-content: center; background: rgba(15,23,42,.45); }
.modal {
  background: #fff; border-radius: 14px; width: 560px; max-width: 94vw; max-height: 88vh;
  display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,.25);
}
/* 宽版弹窗（目前只有「新增/编辑客户」）——那张意向产品表列多，560px 里会挤成一团；
   工艺那格还要把勾选框排成一排（那两条作用域规则在 `.craft-cell` 那一段下面）。
   max-width: 94vw 那条继续兜窄屏，所以手机上不会被撑出界。 */
.modal.modal-wide { width: 1240px; }
.modal-head { padding: 14px 18px; font-weight: 700; font-size: 15px; border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; }
.modal-body { padding: 16px 18px; overflow: auto; }
.modal-foot { padding: 12px 18px; border-top: 1px solid var(--line); display: flex; justify-content: flex-end; gap: 8px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; }
.fs { font-size: 12px; color: var(--muted); }
/* 「新增/编辑客户」里的 省 / 市 / 区县 三个下拉排成一行。
   ⚠ 三个都要 `min-width:0`：`<select>` 的默认最小宽度是「最长那个选项的宽度」，
   而「区 / 县」那一栏里最长的区名很长，不给 0 的话第三格会把整行顶破、
   把右边那个「全地址」框挤出去（grid 的 1fr 拦不住内容的最小宽度）。 */
.area-row { display: flex; gap: 6px; }
.area-row select { flex: 1 1 0; min-width: 0; }
.area-row select:disabled { background: var(--bg); color: var(--muted); }
.modal .field { margin-bottom: 10px; }



/* ---------- toast ---------- */
#toast {
  position: fixed; left: 50%; bottom: 30px; transform: translateX(-50%);
  background: #1d2533; color: #fff; padding: 9px 18px; border-radius: 30px;
  font-size: 14px; z-index: 200; box-shadow: 0 6px 20px rgba(0,0,0,.25);
}
#toast.err { background: var(--red); }

/* ---------- misc ---------- */
.grid-cust { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.cust-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 13px 15px; }
.cust-card .nm { font-weight: 600; font-size: 15px; }
.cust-card .meta { color: var(--muted); font-size: 12px; margin-top: 3px; }
.cust-actions { margin-top: 9px; display: flex; gap: 6px; flex-wrap: wrap; }
.kbd-hint { font-size: 12px; color: #a0a7b4; margin: 6px 0; }
.lead-form .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.lead-form .row3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
.lead-form .row4 { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px; }

/* 明细行内输入控件 */
table.tbl input, table.tbl select {
  padding: 5px 7px; border: 1px solid var(--line); border-radius: 7px;
  font-size: 13px; background: #fff; color: var(--text);
}
table.tbl input:focus { outline: none; border-color: var(--brand); }
/* 新建订单「产品明细」的商品编码：只显示、打不了字。去掉边框与底色，读起来就是「直接显示」，
   但仍是真正的 <input>（三处接线靠 .value 取值/赋值，换成 span 会静默失效）。
   选择器钉住 [readonly]：哪天有人把 readonly 摘了、编码又能手打了，这一格会立刻变回输入框的样子。
   只动边框和底色，**别动 padding**：全局 `* { box-sizing: border-box }` 下 width:90px 是含边框的，
   透明边框仍占着那 1px，所以保留基础 padding 才能让编码文字和旁边「材质/规格」「数量」的文字
   精确对齐在同一个 x 上；写 padding-left:0 会让它整格往左飘 7px。 */
table.tbl input[data-lk="product_code"][readonly] {
  border-color: transparent; background: transparent;
  cursor: default; color: var(--muted);
}
table.tbl input[data-lk="product_code"][readonly]:focus { border-color: transparent; }
.w70 { width: 70px; } .w90 { width: 90px; } .w120 { width: 120px; } .w140 { width: 140px; }
.num-i { text-align: right; }
.btn-lg { padding: 10px 24px; font-size: 15px; }
.modal .x { border: none; background: none; font-size: 15px; color: #98a1b0; cursor: pointer; padding: 2px 6px; }
.modal .x:hover { color: var(--red); }
.del-chip .btn { padding: 0 5px; font-size: 11px; border: none; color: var(--red); background: none; }

/* ---------- 商品资料 ---------- */
/* 行内产品名 + “选”按钮 */
.pkcell, .pkline { display: flex; align-items: center; gap: 6px; }
.pkcell .w120 { flex: 1; width: auto; min-width: 90px; }
.pkline input[data-k="product"] { flex: 1; }
.btn-pick {
  flex: none; padding: 4px 8px; font-size: 12px; border-radius: 6px; cursor: pointer;
  background: #e7f0ff; color: var(--brand-dk); border: 1px solid #cfe0fa; white-space: nowrap;
}
.btn-pick:hover { background: #dbe9ff; }

/* 商品选择弹窗列表 */
#pp-list { max-height: 320px; overflow: auto; border: 1px solid var(--line); border-radius: 10px; }
.pp-item {
  display: flex; align-items: baseline; gap: 10px; padding: 8px 12px; cursor: pointer;
  border-bottom: 1px solid #f0f3f8;
}
.pp-item:last-child { border-bottom: none; }
.pp-item:hover { background: #f0f6ff; }
.pp-code { flex: none; min-width: 80px; }
.pp-name { flex: 1; font-weight: 600; }
.pp-spec { flex: 1; text-align: right; font-size: 12px; }

/* 多选模式（明细行的「选」按钮）：每行左侧一个勾选框 + 下面一条全选/计数条。
   方块尺寸抄 #prod-selall，但那条被 ID 限死在商品列表里，这里用不上。 */
#pp-list .pp-multi { align-items: center; }   /* 原来的 baseline 会让方块跟文字错位 */
#pp-list .pp-sel { flex: none; width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }
#pp-list .pp-on { background: #eef5ff; }      /* 勾中的整行染色，扫一眼就知道选了哪几条（盖得住 hover） */
#pp-bar { margin: 10px 0 0; }
#pp-bar .pp-selall { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }
#pp-bar .pp-selall:disabled { cursor: not-allowed; }

/* 导入预览行 */
.pp-imp-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 4px;
  border-bottom: 1px dashed #eef1f6; font-size: 13px;
}
.pp-imp-row .mono { flex: none; min-width: 70px; }
.pp-imp-row > span:nth-child(3) { flex: 1; }
.pp-imp-row .chip { flex: none; }

/* ---------- 窄屏：竖排折叠（不破版即可） ---------- */
@media (max-width: 900px) {
  .layout { flex-direction: column; height: auto; min-height: 100vh; overflow: visible; }
  .sidebar {
    width: 100%; flex-direction: row; align-items: center;
    border-right: none; border-bottom: 1px solid var(--line); flex: none;
  }
  .sidebar-logo { height: auto; border-bottom: none; flex: none; }
  .sidenav { display: flex; overflow-x: auto; padding: 6px; flex: 1; }
  .sn-item { flex: none; margin-bottom: 0; }
  .tu-name { display: none; }
  .tabbar-user { padding-left: 6px; }
  .main-col { height: auto; min-height: calc(100vh - 52px); }
  .tabpane { padding: 12px; }
  .list-page { flex-direction: column; }
  .filters { width: 100%; position: static; max-height: none; }
  .search { width: 130px; }
}

/* ---------- 商品资料：批量选择/删除（主管及以上） ---------- */
.selbar { display: flex; align-items: center; gap: 12px; margin: 0 0 10px; }
.selbar .prod-del:disabled { opacity: .5; cursor: not-allowed; }
#prod-table .sel-col { width: 34px; text-align: center; }
#prod-table .sel-col input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }
#prod-selall { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }

/* ---------- 订单列表：只勾选、不删除，所以只需要列宽 + 方块两件事 ---------- */
/* 订单表没有 id（是 data-tblk="orders"），上面那三条 #prod-table 的选择器吃不到它。
   勾选列的类名必须逐字是 sel-col：列宽机制（decorateTable / UI_CW）靠它把这一列
   排除在「可拖列」之外，改个名字会让所有老用户已存的订单列宽被静默丢掉。 */
[data-tblk="orders"] .sel-col { width: 34px; text-align: center; }
[data-tblk="orders"] .sel-col input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }
#ord-selall { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; margin: 0; }

/* ---------- 四个列表页：锁高布局（左筛选+表头钉住，只滚表格区） ---------- */
.tabpane.flush.active {
  display: flex; flex-direction: column;
  height: 100%; overflow: hidden; padding: 14px 18px 16px;
}
.tabpane.flush.active .page-title { flex: none; }
.tabpane.flush.active .list-page { flex: 1 1 auto; min-height: 0; }
.tabpane.flush.active .filters { align-self: flex-start; }
.tabpane.flush.active .content {
  align-self: stretch; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
}
.tabpane.flush.active .tblwrap {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
/* 滚动容器内钉住表头：用 separate，避免 border-collapse 让 sticky 失效。
   只作用于 tblwrap 的直接子表（行内展开的嵌套表不粘） */
.tabpane.flush.active .tblwrap > table.tbl { border-collapse: separate; border-spacing: 0; }
.tabpane.flush.active .tblwrap > table.tbl > thead th {
  position: sticky; top: 0; z-index: 2; background: #fff;
}
.tabpane.flush.active .pagerbar { flex: none; }

/* 底部状态条：共 N 条 · 每页 X 条 · 第 Y/Z 页 · 翻页按钮 */
.pagerbar {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 10px 2px 0; font-size: 13px;
}
.pagerbar .pg-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.pagerbar .pg-btns { display: inline-flex; gap: 5px; }
.pg-psel {
  padding: 3px 6px; border: 1px solid var(--line); border-radius: 7px;
  background: #fff; color: var(--text); font-size: 13px; cursor: pointer;
}
.pg-psel:focus { outline: none; border-color: var(--brand); }
.pg-btn {
  min-width: 30px; height: 26px; padding: 0 9px;
  border: 1px solid var(--line); background: #fff; border-radius: 7px;
  font-size: 13px; line-height: 1; color: var(--text); cursor: pointer;
}
.pg-btn:hover:not(:disabled) { border-color: var(--brand); color: var(--brand-dk); }
.pg-btn:disabled { opacity: .45; cursor: not-allowed; }

/* 窄屏：恢复整页滚动，不做锁高 */
@media (max-width: 900px) {
  .tabpane.flush.active { display: block; height: auto; overflow: visible; padding: 12px; }
  .tabpane.flush.active .list-page { flex: none; }
  .tabpane.flush.active .content { display: block; overflow: visible; }
  .tabpane.flush.active .tblwrap { max-height: 62vh; }
}

/* ---------- 列宽拖拽 + 右下角悬浮操作钮 ---------- */
table[data-tblk] th { position: relative; }
table[data-tblk].tbl-fix { table-layout: fixed; }
table[data-tblk] .lk-hdl {
  position: absolute; top: 0; right: -3px; height: 100%; width: 9px;
  cursor: col-resize; z-index: 4; touch-action: none; user-select: none;
}
table[data-tblk] .lk-hdl::after {
  content: ""; position: absolute; right: 3px; top: 8px; bottom: 8px; width: 2px;
  background: rgba(31, 111, 235, 0); transition: background .12s; border-radius: 1px;
}
table[data-tblk] .lk-hdl:hover::after,
body.lkdrag table[data-tblk] .lk-hdl::after { background: rgba(31, 111, 235, .5); }
body.lkdrag { cursor: col-resize; user-select: none; }

#ui-cluster {
  position: fixed; right: 16px; bottom: 14px; z-index: 45;
  display: flex; align-items: center; gap: 8px;
  background: #fff; border: 1px solid var(--line); border-radius: 10px;
  padding: 6px 8px; box-shadow: 0 4px 14px rgba(20, 30, 60, .14);
}
#ui-cluster.hidden { display: none !important; }
/* 悬浮钮常驻右下角：给 flush 列表底部分页条让出右侧空间，避免遮住“末页/下一页” */
.tabpane.flush.active .pagerbar { padding-right: 250px; }
/* 交付看板这一页不是 .flush（是一摞卡片），吃不到上面那条 ⇒ 分页条得单独让位。
   这一页的悬浮钮确实在（UI_SUPPORTED 含 dashboard）。 */
.card .pagerbar { padding-right: 250px; }
@media (max-width: 900px) {
  #ui-cluster { right: 8px; bottom: 8px; }
  .tabpane.flush.active .pagerbar { padding-right: 0; }
  .card .pagerbar { padding-right: 0; }
}

/* ---------- 数据看板（未完成订单按商品编码汇总，风格同订单详情） ---------- */
.bd-expand-row td { padding: 4px 14px 14px; background: #fafbfc; }
.bd-caret { font-size: 12px; white-space: nowrap; }

/* ---------- 半成品子系统 ---------- */
/* 新建订单「可用半成品 / 用半成品加工」两列 */
/* 新建订单「未交付」列（成品/定制）：只读数字，选完商品后由 refreshLineOpenQty 填。
   刻意**不设颜色** —— 数据看板那一列就是裸 <b>，本列照抄；涂橙/红会被读成「逾期」，
   而逾期是看板另一列 ov-badge 的事，两码事。也不设 color: var(--green)，
   免得跟右边「可用半成品」的绿看成一回事。 */
.open-qty-cell { min-width: 70px; white-space: nowrap; }
.semi-avail-cell { min-width: 84px; white-space: nowrap; }
.semi-avail-cell b { color: var(--green); }
.semi-use-cell { min-width: 108px; white-space: nowrap; }
.semi-use { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; cursor: pointer; user-select: none; }
.semi-use input { accent-color: var(--brand); margin: 0; cursor: pointer; }
.semi-use input:disabled { cursor: not-allowed; }
/* 订单详情 / 行内展开 里“用半成品加工”小注 */
.line-note .semi-note { color: var(--green-dk, #1a7a3a); }
/* 关联成品：勾选清单 + 搜索框 */
#sl-list { max-height: 46vh; overflow: auto; margin-top: 6px; border: 1px solid var(--line); border-radius: 8px; }
.row-check {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer;
  border-bottom: 1px solid var(--line); font-size: 13px; user-select: none;
}
.row-check:last-child { border-bottom: none; }
.row-check:hover { background: #f4f7fb; }
.row-check input { accent-color: var(--brand); margin: 0; cursor: pointer; }
.row-check .muted { margin-left: 2px; }

/* ---------- 商品/半成品 新增弹窗：款式编码 搜索带入 ---------- */
.pk-hint { font-size: 12px; color: var(--muted); margin-top: 4px; line-height: 1.5; }
.pk-drop { margin-top: 6px; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: #fff; }
.pk-none { padding: 8px 10px; font-size: 12px; color: var(--muted); }
.pk-item { display: flex; align-items: baseline; gap: 6px; padding: 7px 10px; cursor: pointer; font-size: 13px; line-height: 1.4; }
.pk-item + .pk-item { border-top: 1px solid var(--line); }
.pk-item:hover, .pk-item:focus { background: #f1f5ff; outline: none; }
.pk-item .pk-st { flex: none; color: #1f6feb; font-family: Consolas, monospace; font-weight: 600; min-width: 44px; }
.pk-item .pk-name { font-weight: 600; }
.pk-item .pk-sub { flex: 1; min-width: 0; color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 候选混了类型时标一下来源（半成品新增会连成品一起搜） */
.pk-item .pk-tag { flex: none; display: inline-block; margin-right: 6px; padding: 0 5px; border-radius: 4px; background: #eef2f7; color: #5b6b7f; font-size: 11px; }

/* ---------- 关联成品 弹窗底部 全选 ---------- */
.sl-master { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; color: var(--text); cursor: pointer; padding: 2px 6px; user-select: none; border-radius: 6px; }
.sl-master:hover { background: #f1f5ff; }
.sl-master input { accent-color: var(--brand); margin: 0; cursor: pointer; }

/* ---------- 半成品资料库 行内“关联详情”展开 ---------- */
.sl-expand-row td { padding: 4px 14px 14px; background: #fafbfc; }
.sl-expand-row .sl-head { font-size: 13px; font-weight: 600; color: var(--text); margin: 2px 0 8px; }
.sl-expand-row .sl-head b { color: var(--brand); }

/* ---------- 报价参数（系统 → 报价参数 六个标签页 + 每页一个编辑弹窗） ---------- */
/* ⚠ 这一套**刻意新开 `.qp-tab*` 命名空间**，不复用 `.tab` / `.tablist` / `.tabbar` /
   `.tabpane` / `.tab-x` —— 那几个类名全被文档页签系统（switchTab）占着，复用的话
   两边样式互相串，而且 CSS 不报错、只是某个地方悄悄变样。 */
.qp-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
.qp-tab {
  padding: 6px 12px; font-size: 13px; cursor: pointer; font-family: inherit;
  border: 1px solid var(--line); background: #fff; color: var(--text); border-radius: 8px;
}
.qp-tab:hover { border-color: var(--brand); }
.qp-tab.is-on { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.qp-tabpane { }
.qp-edit { float: right; }
/* 总表：一页一张卡，每组一张两列小表（参数名 / 当前值） */
.qp-grp + .qp-grp { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 14px; }
.qp-grp-head { font-size: 13px; font-weight: 700; margin-bottom: 6px; display: flex; align-items: baseline; gap: 8px; }
.qp-grp-head .qp-unit { font-weight: normal; color: var(--muted); font-size: 12px; }
.qp-tbl th { width: 240px; }
.qp-tbl tr.qp-sub th { color: var(--text); font-weight: 600; background: #f7f9fc; }
.qp-human { font-weight: normal; color: #1f6feb; font-family: Consolas, monospace; }
/* 编辑弹窗：宽一点，每格一个输入框，能自动折行；参数多所以 .modal-body 要能滚（见 .modal-tall） */
.modal.modal-tall { width: 900px; }
.modal.modal-tall .modal-body { min-height: 0; }
.qp-fgrp + .qp-fgrp { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 12px; }
.qp-fgrp-head { font-size: 13px; font-weight: 700; margin-bottom: 6px; }
.qp-fsub { font-size: 12px; font-weight: 600; color: var(--text); margin: 10px 0 6px; }
.qp-fgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px 12px; }
.qp-fcell { display: block; }
.qp-fcell > span { display: block; font-size: 12px; color: var(--muted); margin-bottom: 3px; }
.qp-fcell input, .qp-fcell select { width: 100%; padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; font-size: 13px; font-family: inherit; background: #fff; }
.qp-fcell input:focus, .qp-fcell select:focus { outline: none; border-color: var(--brand); }
/* 能增删的那几页：每条记录外面一层框，右上角 ↑ ↓ 删除。
   ⚠ ↑↓/删除 是**绝对定位**的，所以格子那一栏必须留出右边的空档（padding-right），
     不留的话最后那一列输入框会滑到按钮底下 —— 打得进去、也点得着删除，看着像没事。 */
.qp-frow { position: relative; border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; margin: 8px 0; background: #fcfdfe; }
.qp-frow > .qp-fgrid { padding-right: 200px; }
.qp-facts { position: absolute; top: 8px; right: 8px; display: flex; gap: 4px; }
.qp-fadd { margin-top: 8px; }
/* 盒型展开系统那一页的「公式行」（2026-09-15 增量五·第二轮）。一个部件 = 部件名 + 两条公式
   + 两个常数 + 三个动作，在 900px 的弹窗里排在同一个网格里。
   下面四样（qp-fhead / qp-fwide / qp-fbtn / qp-fhint）都**没有 data-k**，
   只负责排版与动作，不进提交表；`kind:"hint"` 也**不是**错误提示，只是一句提醒，
   所以是灰底不是红底（红底留给「公式算不出来」那条 #flatWarn）。 */
.qp-fcell.qp-fhead { grid-column: 1 / -1; font-size: 12px; font-weight: 700; color: var(--muted);
  border-top: 1px dashed var(--line); padding-top: 6px; margin-top: 4px; }
.qp-fcell.qp-fwide { grid-column: 1 / -1; }
.qp-fcell.qp-fbtn { display: flex; gap: 6px; }
.qp-fcell.qp-fbtn > .btn { white-space: nowrap; }
/* 「公式里没写 C，这个常数不算数」。⚠ 这一格**必须与输入框等高**（下面那句话是对着
   右边那个常数框说的），矮一截就会串到上一行的对齐上去。 */
.qp-fcell.qp-fhint { display: flex; align-items: center; padding: 6px 8px; border-radius: 8px;
  background: #f4f7fb; font-size: 12px; color: var(--muted); }

/* ---------- 定制报价（报价 → 定制报价） ---------- */
/* 这一整段是从 _mockup_quote.html 的 <style> 搬过来的。搬运时四处**刻意的改动**：
     ① 原型那句 body { padding: 18px 20px 60px } 没搬 —— style.css 的 body 不设 padding，
        搬过来会给全站每一页（含门户 public.html）都加一圈内缩。
     ② 原型那句 svg { display:block; max-width:100%; height:auto } 收窄成 .mk-figbox svg。
        今天全项目 0 个内联 svg，但全局 svg 选择器是哑的，哪天加个图标就会静默变形。
     ③ 原型的 td.craft-cell / td.craft-cell input 改名 td.qc-craft-cell
        （只改选择器，数值一个没动；2026-09-15 那一格又整个换了形状，见下面那条规则）。
        style.css 上面已有 .craft-cell 给订单行的产品工艺格用，而原型的 td.craft-cell 是
        (0,1,1) 优先级更高 —— 同名搬过来会把订单行那一格变成 display:flex; flex-wrap:wrap，
        不报错、只是别的页悄悄变了样。
     ④ 原型顶栏 .mk-bar 那三条没搬 —— ERP 的页头是 .page-title，没有任何元素带 mk-bar。
   另外 table.mk td.num input 那条也没搬：工艺单价卡改成只读之后，table.mk 里再没有输入框了。 */

/* 报价页顶上的横幅：告诉用户这一屏是按哪套参数在算 */
.qp-banner {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin: 0 0 14px; padding: 9px 12px; border-radius: 8px;
  background: #f1f5ff; border: 1px solid #cdddfb; color: #24405e; font-size: 13px;
}
/* 「按快照」必须是显眼的黄条 —— 否则用户改完参数回来会以为报价页坏了 */
.qp-banner-snap { background: #fff8e6; border-color: #f3d99b; color: #7a5200; }
/* 快照读不出来、退回当前参数算：红条。这一条比黄条更要紧 —— 黄条说的是「按老参数算」，
   红条说的是「按**新**参数算，金额可能跟当初不一样」，不说清楚就没人看得出来。 */
.qp-banner-bad { background: #fdecec; border-color: #f0b4b4; color: #8c2b2b; }

/* 四张卡：上排三张并排（展开图 / 报价单 / 报价表），报价输入整宽沉到最下面。
   排位靠 flex 的 order —— 源文件里报价输入写在最前，读代码顺着填单顺序走。 */
.mk-page { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.mk-page > .card { flex: 1 1 0; min-width: 280px; margin-bottom: 0; }
.mk-page > .card.mk-wide { flex: 1 1 100%; min-width: 0; order: 2; }
@media (max-width: 1000px) {
  .mk-page > .card { flex: 1 1 100%; }
}

/* 表单区：自适应多列（现在整宽了，列数随屏幕走） */
.mk-in { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px 16px; }
.mk-in .field { margin-bottom: 0; }
.mk-in .wide { grid-column: 1 / -1; }

/* 最后一行：现在只剩「加价」一个字段了。
   原先同行的「配件」那一整排（绳子/提手/配饰）2026-09-14 整排撤掉，
   改成了部件表里逐行的列（绳子/提手/其它）—— 配件本来就是按部件走的，
   摊在整单那一行只能整单一个价，跟「天盒用绳、地盒不用」对不上。
   加价还是那个小数字框，单独占一整行太浪费，所以照旧定宽。 */
.mk-bottom { display: flex; gap: 12px 20px; align-items: flex-start; flex-wrap: wrap; }
/* 150 不是随手挑的：加价那个标签「加价（元）不乘系数」约 108px 宽（原先那句尾巴
   「，直接加总额」占了 70px，那句删了，宽度跟着收）。
   卡在 108 上会随子像素左右横跳（有时一行、有时折两行），一折行输入框就矮 28px。
   给足余量，标签稳定一行。 */
.mk-bottom .bt-extra { flex: 0 0 150px; }
/* 客户 / 备注：跟「加价」同一行。客户名短，给 220；备注是可长的自由文本，让它吃掉富余。
   存单按钮排在最后靠右 —— 它是这一页的收尾动作，不该跟输入框混在一堆。 */
.mk-bottom .bt-cust { flex: 0 0 220px; }
.mk-bottom .bt-remark { flex: 1 1 260px; min-width: 200px; }
.mk-bottom .bt-save { flex: 0 0 auto; margin-left: auto; text-align: right; }
.mk-bottom .bt-save .btn { padding: 7px 16px; }
.mk-bottom .bt-save .mk-sub { margin-top: 4px; }

/* 报价记录页的搜索条 */
.qrec-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.qrec-bar .search {
  flex: 0 1 320px; padding: 6px 10px; border: 1px solid var(--line);
  border-radius: 8px; font-size: 13px; background: #fff; color: var(--text);
}
.qrec-bar .mk-sub { margin-left: 2px; }

/* 第一行：提交方式 + 盒型 + 数量（整单一级的参数） */
.mk-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mk-top .ot-radios { margin: 0; }
.mk-top .sep { width: 1px; height: 20px; background: var(--line); margin: 0 6px; }
.mk-top label.lb { color: var(--muted); font-size: 12px; }
.mk-top select, .mk-top input {
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 13px; background: #fff; color: var(--text);
}
.mk-top select { width: 128px; }
.mk-top input { width: 96px; text-align: right; font-variant-numeric: tabular-nums; }

/* 部件表：一个部件一行。行数跟着盒型的部件数走，2 个就 2 行、4 个就 4 行 */
table.mk-pt { width: 100%; border-collapse: collapse; margin-top: 2px; }
/* 列加到 17 列之后，窄屏压到 600 上下就到底了（那是各格控件自己的最小宽度，再压就装不下）。
   与其让整页横向滚（把上面三张卡片一起拖出去），不如让这张表在自己的框里滚：
   宽屏时它照样撑满、不出现滚动条；只有真的窄到装不下时才在表内滚。 */
#parts { overflow-x: auto; }
/* 展开公式算不出来时那条红字（挂在部件表上方）。刻意做成**看得见的一行**而不是
   toast：toast 三秒就没了，而这张表的金额会一直按 0 算下去。
   红框红字 + 加粗，扫一眼就知道「这里有个必须先解决的东西」。 */
#flatWarn {
  margin: 2px 0 8px; padding: 6px 10px; border: 1px solid #f3c1c1; border-radius: 7px;
  background: #fdeaea; color: var(--red); font-size: 12px; line-height: 1.7;
}
#flatWarn b { font-weight: 600; }
table.mk-pt th {
  font-size: 11px; color: var(--muted); font-weight: 600; text-align: left;
  padding: 4px 4px 6px; white-space: nowrap; line-height: 1.35;
}
table.mk-pt td { padding: 3px 4px; }
table.mk-pt tbody tr + tr td { border-top: 1px solid #eef1f6; }
table.mk-pt td.nm { font-size: 12px; color: var(--muted); white-space: nowrap; padding-left: 0; }
table.mk-pt input, table.mk-pt select {
  width: 100%; padding: 4px 6px; border: 1px solid var(--line); border-radius: 7px;
  font-size: 12.5px; background: #fff; color: var(--text);
}
table.mk-pt input[type=number] { text-align: right; font-variant-numeric: tabular-nums; }
table.mk-pt th.num { text-align: right; }
/* 禁用（"不印刷"时那个专色数）和只读（公式算出来的展开尺寸）都长这样 */
table.mk-pt input[disabled], table.mk-pt input[readonly] {
  background: #f7f8fa; color: #6b7280; border-style: dashed;
}
table.mk-pt input[disabled] { color: #c2c8d2; }
table.mk-pt input[readonly]:focus, table.mk-pt input[disabled]:focus {
  border-color: var(--line); box-shadow: none;
}
td.ink-cell { display: flex; gap: 4px; align-items: center; }
td.ink-cell .pk-spots { width: 44px; flex: none; }

/* 「工艺」那一格（在「总价」右边）：一个「设置」按钮 + 选完之后的摘要文字，
   下面压着六个**隐藏**的数字框（qpSnapshotParts 的 own() 照旧从它们读）。
   2026-09-15 用户要求：工艺从「一个下拉 + 一列小框」改成「一列 + 一个四选弹窗」，
   所以原来那条 160px 的「工艺数量」列连同 td.mk-craft-cell 两条规则一起下岗了 ——
   那几个 46px 的小框搬进了弹窗（见下面 .qc-in）。 */
td.qc-craft-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
.qc-craft-val { font-size: 11px; line-height: 1.35; color: #555; word-break: break-word; }
.qc-craft-val.none { color: #aaa; }

/* ---- 表面工艺弹窗（.qc-sec 四块：烫金 / UV / 压纹 / 激凸） ---- */
.qc-sec { border: 1px solid var(--line); border-radius: 8px;
          padding: 9px 11px; margin-bottom: 8px; }
.qc-sec-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.qc-on-lab { display: flex; align-items: center; gap: 5px;
             font-weight: 600; font-size: 13.5px; min-width: 62px; cursor: pointer; }
.qc-rate { font-size: 12px; color: #6b7280; font-variant-numeric: tabular-nums; }
.qc-sec-in { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 7px; }
.qc-in-lab { display: flex; align-items: center; gap: 4px; font-size: 12.5px; color: #555; }
/* 46px —— 跟原来表格里那一列同一个小框尺寸，靠 style.css 第 17 行那个全局
   * { box-sizing: border-box }「写 46 就占 46」。别在这条上再叠 box-sizing。 */
.qc-in { width: 46px; flex: none; }
.qc-note { font-size: 12px; color: #6b7280; line-height: 1.6; margin-top: 4px; }

/* 用纸面积 / 该行总价：都是算出来的数，不是可填字段 —— 所以是 span 不是 input，
   长着 Text 的样子而不是输入框的样子（不然用户会去点它、以为填了能改） */
table.mk-pt td.calc { font-size: 12.5px; text-align: right; white-space: nowrap;
                      font-variant-numeric: tabular-nums; padding-right: 7px; }
/* 该行总价 = 这一行的成本。加粗一点，它跟「成本合计」对得上 */
table.mk-pt td.calc.sum { font-weight: 600; }

/* 行尾那个 ×：删行。整行都得能点中（手机上 4px 的 × 谁点得着），但别喧宾夺主 */
table.mk-pt td.del-cell { text-align: center; padding-left: 0; }
table.mk-pt .mk-del {
  border: none; background: none; color: #c2c8d2; cursor: pointer;
  font-size: 15px; line-height: 1; padding: 2px 6px; border-radius: 6px;
}
table.mk-pt .mk-del:hover { color: #d64545; background: #fdecec; }
/* 只剩最后一行时禁用它 —— 一个部件都没有的报价单没法算（展开图也画不出来） */
table.mk-pt .mk-del:disabled { color: #e6e9ee; background: none; cursor: not-allowed; }

/* 新增行按钮：贴在部件表下面、靠左，跟表格的左边线对齐 */
.mk-addrow { margin-top: 8px; }

/* 明细/报价表：两列表 */
table.mk { width: 100%; border-collapse: collapse; font-size: 13px; }
table.mk th { text-align: left; color: var(--muted); font-weight: 600; font-size: 12px;
              padding: 6px 8px; border-bottom: 1px solid var(--line); }
table.mk td { padding: 7px 8px; border-bottom: 1px solid #eef1f6; }
table.mk tr.sum td { border-top: 1px solid var(--line); border-bottom: none; padding-top: 9px; }
table.mk tr.grand td { border-top: 2px solid var(--text); font-weight: 700; font-size: 14px; }
table.mk td.k { color: var(--muted); }
/* 明细里的小字分项（上机/专色），不参与视觉层级 */
table.mk tr.subline td { color: var(--muted); font-size: 12px; padding: 2px 8px 2px 24px; border-bottom: none; }
table.mk tr.subline + tr td { border-top: 1px solid #eef1f6; }
.mk-idx { display: inline-block; width: 16px; color: #aab2bf; }

/* 报价单卡内左右分：左边明细表，右边说明 */
.mk-doc { display: flex; gap: 16px; align-items: flex-start; }
.mk-doc-table { flex: 1 1 auto; min-width: 0; }
.mk-doc-side { flex: 0 0 150px; min-width: 0; }
.mk-doc-side .mk-note { margin-top: 0; }
/* 报价单的明细表：行距收紧，把卡再压矮一点 */
table.mk.tight td { padding: 5px 6px; }
table.mk.tight tr.subline td { padding: 1px 6px 1px 20px; }
table.mk.tight tr.grand td { padding-top: 7px; }
@media (max-width: 560px) {
  .mk-doc { flex-wrap: wrap; }
  .mk-doc-table, .mk-doc-side { flex: 1 1 100%; }
  .mk-doc-side { border-top: 1px dashed var(--line); padding-top: 12px; }
}

/* 客户表里的部件明细：小一号的紧凑表 */
table.mkp { width: 100%; border-collapse: collapse; font-size: 12.5px; margin: 4px 0 2px;
            border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
table.mkp th { text-align: left; font-size: 11px; color: var(--muted); font-weight: 600;
               padding: 5px 8px; background: #fafbfd; border-bottom: 1px solid var(--line); }
table.mkp td { padding: 5px 8px; border-bottom: 1px solid #eef1f6; }
table.mkp tr:last-child td { border-bottom: none; }
table.mkp td.num, table.mkp th.num { text-align: right; font-variant-numeric: tabular-nums; }

.mk-sub { color: var(--muted); font-size: 12px; }
.mk-incl td { color: var(--muted); background: #fafbfd; }

/* 示意图的框 */
.mk-figbox { border: 1px solid var(--line); border-radius: 8px; background: #fcfdff;
             padding: 8px; display: flex; justify-content: center; align-items: center; min-height: 118px; }
.mk-figbox svg { display: block; max-width: 100%; height: auto; }   /* 列变窄时图自己缩 */

.mk-note { font-size: 12px; color: var(--muted); margin-top: 8px; line-height: 1.7; }
