/* ==================================================================
 * ui2.css —— 仪表盘视觉层（叠加在 app.css / nav.css 之上，最后加载）
 * ------------------------------------------------------------------
 * 定位：不改任何 HTML 语义、不加 !important 互殴，只在同名选择器上
 * 重写视觉属性，所以任何页面只要引入 header.php 就自动生效。
 *
 * 与 app.css（iOS 扁平风）的差别：
 *   iOS 版   —— 纯灰底 #F2F2F7 + 白卡片 + 0.5px 描边，零层次
 *   本文件   —— 浅蓝渐变底 + 柔和投影 + 16px 大圆角 + 图标芯片，
 *               让「卡片浮在背景上」，这是参考图的观感来源。
 *
 * 禁止事项（血泪）：
 *   1. 不许给 .panel 加 overflow（会裁掉 searchable.css 的搜索下拉）
 *   2. 不许写裸 table / td 选择器（会渗进 doc.css 的合同 A4 排版）
 *   3. 不许改 * 的 margin/padding（seal.php 同时加载 doc.css，会整体位移）
 * ================================================================== */

:root {
  /* 背景：顶部微亮、底部偏冷的浅蓝 */
  --u2-bg-1: #F7F9FD;
  --u2-bg-2: #EEF2FA;
  /* 卡片与描边 */
  --u2-card: #FFFFFF;
  --u2-line: rgba(17, 24, 39, .07);
  --u2-line-2: rgba(17, 24, 39, .11);
  /* 文字 */
  --u2-ink: #111827;
  --u2-ink-2: #374151;
  --u2-muted: #8A94A6;
  /* 主色（比 iOS #007AFF 稍微压暗，压在白底上更稳） */
  --u2-blue: #2E6BE6;
  --u2-blue-d: #2559C9;
  --u2-blue-50: #EDF3FE;
  --u2-blue-100: #DDE8FC;
  /* 圆角与投影 */
  --u2-r: 16px;
  --u2-r-sm: 10px;
  --u2-sh: 0 1px 2px rgba(16, 24, 40, .04), 0 12px 28px -18px rgba(16, 24, 40, .30);
  --u2-sh-h: 0 2px 6px rgba(16, 24, 40, .06), 0 18px 40px -20px rgba(16, 24, 40, .34);

  /* ---- 主题令牌（2026-11 新增）--------------------------------------
     换肤只改这一组值，组件规则一律引用变量，不再出现字面色值。
     assets/css/themes.css 会按 html[data-theme] 整组覆盖，优先级更高。 */
  --u2-card-rgb: 255, 255, 255;
  --u2-ink-rgb: 17, 24, 39;
  --u2-blue-rgb: 46, 107, 230;
  --u2-glow-rgb: 93, 140, 255;
  --u2-glow2-rgb: 150, 122, 255;
  --u2-hover: #F4F7FC;          /* 菜单 / 按钮悬停底 */
  --u2-input: #F5F7FB;          /* 输入框、圆角胶囊底 */
  --u2-soft: #FBFCFE;           /* 卡片内的浅色块 */
  --u2-soft-2: #F1F3F7;         /* 更实的灰底：表头、灰标签 */
  --u2-line-3: #E3E8F1;         /* 比 --u2-line-2 更明显的描边 */
  --u2-faint: #98A2B3;          /* 极弱文字 / 图标 */
  --u2-blue-l: #7EA7F7;         /* 主色渐变亮端 */
  --u2-top: rgba(255, 255, 255, .78);  /* 顶栏毛玻璃 */
  /* 侧栏：深色主题整套换掉（底色、文字、悬停、选中态、分割线） */
  --u2-side: #FFFFFF;
  --u2-side-ink: var(--u2-ink);
  --u2-side-ink-2: var(--u2-ink-2);
  --u2-side-hover: #F4F7FC;
  --u2-side-line: rgba(17, 24, 39, .08);
  --u2-on-bg: var(--u2-blue-50);
  --u2-on-ink: var(--u2-blue);
  --u2-logo-filter: none;
}

/* ---------- 1. 页面底：渐变 + 两处极淡光斑 ---------- */
body {
  background: var(--u2-bg-2);
  color: var(--u2-ink-2);
}
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1100px 520px at 12% -8%, rgba(var(--u2-glow-rgb), .13), transparent 62%),
    radial-gradient(900px 460px at 88% -4%, rgba(var(--u2-glow2-rgb), .11), transparent 60%),
    linear-gradient(180deg, var(--u2-bg-1) 0%, var(--u2-bg-2) 100%);
}

/* ---------- 2. 侧栏：实白 + 发丝线，胶囊选中态加深 ---------- */
.sidebar {
  width: 238px;
  background: var(--u2-side);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-right: 1px solid var(--u2-side-line);
}
/* 桌面端菜单吸顶、自己滚：菜单更长以后整页滚动会把菜单一起卷走，翻到下面的组就看不见了。
   手机上 .sidebar 是 fixed 抽屉（app.css ≤820px），这里必须避开那个断点。 */
@media (min-width: 821px) {
  .sidebar {
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100vh;
    overflow-y: auto;
  }
  /* 滚动菜单时 LOGO 不跟着走 */
  .brand { position: sticky; top: 0; z-index: 2; background: var(--u2-side); }
}
.brand {
  height: 64px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 18px;
  font-size: 15px;
  font-weight: 600;
  color: var(--u2-side-ink);
  border-bottom: 1px solid var(--u2-side-line);
  letter-spacing: -.01em;
  text-decoration: none;
}
/* 品牌区只放 LOGO 图：原来的蓝色渐变小方块（::before）已取消 */
.brand::before { content: none; }
.brand-logo {
  display: block;
  height: 26px;
  width: auto;
  max-width: 100%;
  /* 图是蓝底白字的位图，压白侧栏上不需要额外底 */
  -webkit-user-select: none;
  user-select: none;
}
.nav-tree { padding: 8px 0 28px; }
.nav-sec { border-bottom: 0; }
/* 字号（2026-11 调整）：14px → 14.5px。
 * ui2.css 在 nav.css 之后加载，侧栏文字的最终值以这里为准，
 * nav.css 里同名属性只是没有 ui2 时的底稿，两边要保持一致，改一处记得改另一处。 */
.nav-head {
  margin: 2px 8px;
  padding: 9px 10px;
  border-radius: var(--u2-r-sm);
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: .2px;
  color: var(--u2-side-ink);
}
.nav-head:hover { background: var(--u2-side-hover); }
.nav-sec.active > .nav-head { color: var(--u2-on-ink); }
.nav-sec.active > .nav-head .nav-ico { color: var(--u2-on-ink); }
/* 折叠箭头：原来 10px + 灰，基本看不见；收起时指右、展开时指下，一眼知道能点 */
.nav-caret { font-size: 11px; color: var(--u2-side-ink-2); opacity: .7; }
.nav-sec:not(.open) > .nav-head .nav-caret { transform: rotate(-90deg); }
.nav-sec.open > .nav-head .nav-caret { transform: none; }
.nav-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  color: var(--u2-side-ink-2);
  opacity: 1;
}
/* 子菜单挂一条竖引导线，和组标题形成父子关系 */
.nav-sub { padding: 2px 0 6px; position: relative; }
/* 收起时 max-height:0 挡不住 padding，会白占 8px，顺手抹掉让组长更紧凑 */
.nav-sec:not(.open) > .nav-sub { padding: 0; }
.nav-sub::before {
  content: '';
  position: absolute;
  left: 28px; top: 0; bottom: 8px;
  width: 1px;
  background: var(--u2-side-line);
}
/* 子项（2026-11 调整）：13px/400 → 14px/500，颜色由 --u2-ink-2 提到 --u2-ink。
 * 原因：13px 的汉字在 Windows 的微软雅黑上笔画只有 1px 宽，压在浅灰毛玻璃侧栏上
 * 会糊成一片灰。放大 1px + 加半档字重 + 把字色提一档，三个一起改才有效；
 * 只放大字号会让细笔画被拉长，反而更虚。 */
.nav-sub a {
  margin: 1px 8px 1px 40px;
  padding: 7px 12px;
  border-radius: var(--u2-r-sm);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--u2-side-ink);
}
.nav-sub a:hover { background: var(--u2-side-hover); color: var(--u2-side-ink); }
.nav-sub a.on {
  background: var(--u2-on-bg);
  color: var(--u2-on-ink);
  /* 普通子项现在是 500，选中态要再高一档才能拉开层次（原来靠 400→500 区分） */
  font-weight: 600;
}

/* ---------- 3. 顶栏：白玻璃 + 居中搜索 ---------- */
.topbar {
  height: 64px;
  padding: 0 24px;
  background: var(--u2-top);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--u2-line);
  gap: 16px;
}
.crumb { font-size: 15px; font-weight: 600; color: var(--u2-ink); letter-spacing: -.01em; }
.topbar-search {
  flex: 1;
  display: flex;
  justify-content: center;
}
.topbar-search form {
  position: relative;
  width: 100%;
  max-width: 380px;
}
.topbar-search .ico {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--u2-muted);
  pointer-events: none;
}
.topbar-search input {
  width: 100%;
  height: 36px;
  padding: 0 14px 0 36px;
  border: 1px solid var(--u2-line-2);
  border-radius: 999px;
  background: var(--u2-input);
  font-size: 13px;
  color: var(--u2-ink);
  transition: background .16s, border-color .16s, box-shadow .16s;
}
.topbar-search input::placeholder { color: var(--u2-muted); }
.topbar-search input:focus {
  outline: 0;
  background: var(--u2-card);
  border-color: var(--u2-blue);
  box-shadow: 0 0 0 3px rgba(var(--u2-blue-rgb), .14);
}
.userbox { gap: 10px; }
.userbox .u-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 10px 0 4px;
  border-radius: 999px;
  background: var(--u2-input);
  color: var(--u2-ink-2);
  font-size: 12.5px;
}
.userbox .u-avatar {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: linear-gradient(140deg, var(--u2-blue-l), var(--u2-blue));
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  display: grid;
  place-items: center;
}
.userbox a {
  height: 34px;
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--u2-line-2);
  color: var(--u2-ink-2);
  font-size: 12.5px;
}
.userbox a:hover { background: #FFF1F0; border-color: #FFD5D2; color: #C4271E; text-decoration: none; }
.content { padding: 22px 24px 40px; }

/* ---------- 4. 页面头 ---------- */
.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin: 2px 0 18px;
}
.page-head h1 {
  font-size: 21px;
  font-weight: 600;
  color: var(--u2-ink);
  letter-spacing: -.015em;
  line-height: 1.3;
}
.page-head .sub { margin-top: 6px; font-size: 13px; color: var(--u2-muted); }
.page-head .actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

/* ---------- 5. 卡片 / 面板 ---------- */
.card,
.panel {
  background: var(--u2-card);
  border: 1px solid var(--u2-line);
  border-radius: var(--u2-r);
  box-shadow: var(--u2-sh);
}
.panel { padding: 18px 20px; margin-bottom: 16px; }
.panel h3 { font-size: 15px; font-weight: 600; color: var(--u2-ink); letter-spacing: -.01em; }
.panel > h3 {
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--u2-line);
}
.cards { gap: 16px; margin-bottom: 16px; }
.card { padding: 16px 18px; box-shadow: var(--u2-sh); }
.card-label { font-size: 13px; color: var(--u2-muted); margin-bottom: 10px; }
.card-value { font-size: 27px; font-weight: 600; color: var(--u2-ink); letter-spacing: -.02em; }

/* ---------- 6. KPI 统计卡 ---------- */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  margin-bottom: 16px;
}
.kpi {
  position: relative;
  background: var(--u2-card);
  border: 1px solid var(--u2-line);
  border-radius: var(--u2-r);
  box-shadow: var(--u2-sh);
  padding: 16px 18px 15px;
  overflow: hidden;
}
.kpi-top { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--u2-muted); }
.kpi-ico {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: var(--u2-blue-50);
  color: var(--u2-blue);
  flex: 0 0 auto;
}
.kpi-ico.g { background: #E9F8EE; color: #1E8E3E; }
.kpi-ico.o { background: #FFF3E4; color: #B25E00; }
.kpi-ico.r { background: #FFEDEB; color: #C4271E; }
.kpi-ico.v { background: #F1EEFE; color: #6D4AE0; }
.kpi-val {
  margin-top: 11px;
  font-size: 28px;
  font-weight: 600;
  color: var(--u2-ink);
  letter-spacing: -.025em;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.kpi-val small { font-size: 14px; font-weight: 500; color: var(--u2-muted); margin-left: 3px; letter-spacing: 0; }
.kpi-sub { margin-top: 5px; font-size: 12px; color: var(--u2-muted); display: flex; align-items: center; gap: 5px; }
.kpi-sub .up { color: #1E8E3E; }
.kpi-sub .down { color: #C4271E; }

/* ---------- 7. 仪表盘栅格 ---------- */
.dash {
  display: grid;
  grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}
.dash > .panel { margin-bottom: 0; }
/* 两等分变体（库存概览、客户概览这类「两块内容都想要宽度」的场合）。
   注意：不要写成内联 grid-template-columns —— 内联样式优先级高于媒体查询，
   手机上就塌不成单栏了（390px 下会挤成两列各 150px，环形图和文字全变形）。 */
.dash.even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.stack { display: flex; flex-direction: column; gap: 16px; }
.stack > .panel { margin-bottom: 0; }

/* 图表容器 */
.chart-wrap { width: 100%; overflow: hidden; }
.chart-wrap svg { display: block; width: 100%; height: auto; }
.chart-legend { display: flex; gap: 16px; font-size: 12px; color: var(--u2-muted); margin-top: 8px; }
.chart-legend i { display: inline-block; width: 8px; height: 8px; border-radius: 999px; margin-right: 6px; }

/* 环形图（用 conic-gradient，零 JS） */
.donut {
  width: 168px;
  height: 168px;
  border-radius: 50%;
  position: relative;
  flex: 0 0 auto;
  -webkit-mask: radial-gradient(circle, transparent 52%, #000 53%);
  mask: radial-gradient(circle, transparent 52%, #000 53%);
}
.donut-holder { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.donut-legend { display: flex; flex-direction: column; gap: 9px; font-size: 12.5px; color: var(--u2-ink-2); }
.donut-legend span { display: flex; align-items: center; gap: 8px; }
.donut-legend i { width: 9px; height: 9px; border-radius: 3px; }
.donut-legend b { margin-left: auto; color: var(--u2-muted); font-weight: 500; }

/* 进度条（库存充足度等） */
.meter { height: 6px; border-radius: 999px; background: var(--u2-soft-2); overflow: hidden; }
.meter > i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--u2-blue-l), var(--u2-blue)); }
.meter.g > i { background: linear-gradient(90deg, #6FD08C, #34C759); }
.meter.o > i { background: linear-gradient(90deg, #FFC46B, #FF9500); }
.meter.r > i { background: linear-gradient(90deg, #FF8A80, #FF3B30); }

/* 待办 / 动态列表 */
.todo-list { display: flex; flex-direction: column; gap: 8px; }
.todo-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 12px;
  border-radius: 12px;
  background: var(--u2-soft);
  transition: background .16s;
}
.todo-item:hover { background: var(--u2-hover); text-decoration: none; }
.todo-num {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  font-size: 12.5px;
  font-weight: 600;
  background: var(--u2-card);
  color: var(--u2-blue);
  box-shadow: 0 1px 2px rgba(16, 24, 40, .08);
  flex: 0 0 auto;
}
.todo-num.warn { color: #B25E00; }
.todo-num.dang { color: #C4271E; }
.todo-txt { font-size: 13px; color: var(--u2-ink-2); flex: 1; min-width: 0; }
.todo-txt small { display: block; color: var(--u2-muted); font-size: 11.5px; margin-top: 2px; }
.feed { display: flex; flex-direction: column; gap: 0; }
.feed-item { position: relative; padding: 0 0 14px 20px; font-size: 12.5px; color: var(--u2-ink-2); }
.feed-item::before {
  content: '';
  position: absolute;
  left: 3px; top: 6px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--u2-blue);
  box-shadow: 0 0 0 3px var(--u2-blue-50);
}
.feed-item::after {
  content: '';
  position: absolute;
  left: 6px; top: 16px; bottom: 0;
  width: 1px;
  background: var(--u2-line-2);
}
.feed-item:last-child { padding-bottom: 0; }
.feed-item:last-child::after { display: none; }
.feed-item small { display: block; color: var(--u2-muted); font-size: 11px; margin-top: 3px; }

/* ---------- 8. 按钮 ---------- */
.btn {
  height: 34px;
  padding: 0 14px;
  border-radius: var(--u2-r-sm);
  border: 1px solid var(--u2-line-3);
  background: var(--u2-card);
  color: var(--u2-ink-2);
  font-size: 13px;
  font-weight: 500;
  box-shadow: 0 1px 1px rgba(16, 24, 40, .03);
  transition: background .14s, border-color .14s, box-shadow .14s, transform .06s;
}
.btn:hover { background: var(--u2-hover); border-color: var(--u2-line-3); text-decoration: none; }
.btn:active { transform: translateY(.5px); }
.btn.primary {
  background: linear-gradient(180deg, var(--u2-blue-l), var(--u2-blue));
  border-color: transparent;
  color: #fff;
  box-shadow: 0 6px 14px -8px rgba(var(--u2-blue-rgb), .9);
}
.btn.primary:hover { background: linear-gradient(180deg, var(--u2-blue), var(--u2-blue-d)); border-color: transparent; }
.btn.success {
  background: linear-gradient(180deg, #3FCB68, #34C759);
  border-color: transparent;
  color: #fff;
}
.btn.danger-soft { background: #FFF3F2; border-color: #FFDBD8; color: #C4271E; box-shadow: none; }
.btn.danger-soft:hover { background: #FFE9E7; border-color: #FFC9C4; }
.btn.sm { height: 28px; padding: 0 8px; font-size: 12px; border-radius: 8px; }

/* 客户名过长时省略，不把整张表推到需要横滚（title 里保留全名） */
table.grid td.cust { max-width: 158px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- 9. 表格 ---------- */
table.grid { font-size: 13px; }
table.grid th,
table.grid td { padding: 10px; border-bottom: 1px solid var(--u2-line); }
table.grid th {
  font-size: 12px;
  font-weight: 500;
  color: var(--u2-muted);
  background: var(--u2-soft);
  border-bottom: 1px solid var(--u2-line-2);
  white-space: nowrap;
}
table.grid tbody tr:hover { background: var(--u2-hover); }
table.grid tbody tr:last-child td { border-bottom: 0; }
table.grid td { color: var(--u2-ink-2); }
table.grid td b, table.grid td strong { font-weight: 600; color: var(--u2-ink); }

/* ---------- 10. 徽章 ---------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 500;
  border: 0;
  background: var(--u2-soft-2);
  color: var(--u2-ink-2);
  white-space: nowrap;
}
.badge.green { background: #E9F8EE; color: #1E8E3E; }
.badge.orange { background: #FFF3E4; color: #B25E00; }
.badge.blue { background: var(--u2-blue-50); color: var(--u2-blue); }
.badge.red { background: #FFEDEB; color: #C4271E; }
.badge.gray { background: var(--u2-soft-2); color: var(--u2-ink-2); }
/* 超小徽章：只剩「产 3 / 送 2」这种计数，给打印列省宽（1440 屏的横滚就靠它和合并列） */
.badge.xs { height: 19px; padding: 0 7px; font-size: 11px; gap: 3px; }

/* ---------- 9. 列表微件 ---------- */
/* 「定制」按钮的已上传标记：主色底上用半透明白点，只是"这件事做过了"的状态灯，
   不额外占宽（5px + 5px 间距，仍在 44px 按钮内），也不抢标签文字。 */
.btn .dot-ok {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .92);
  margin-left: 5px;
  vertical-align: middle;
}
/* 发货列：已发货只留物流公司名（单号移到「物流」页）。长公司名截断，
   否则「德邦物流（大件专线）」这种会把 108px 的列撑开挤出横滚。 */
.ship-cell .ship-co {
  font-size: 11px;
  margin-top: 3px;
  max-width: 96px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 长文本列截断：地址、客户名。
   table.grid 是 min-width:max-content + word-break:keep-all，长地址会把整张表撑出
   横向滚动条，把「信用额度 / 应收余额 / 操作」挤出可视区（实测撑到 1500px+）。
   max-width 在 auto 布局下是硬约束，配合 ellipsis 才能把表压回容器内。 */
table.grid td.addr {
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
table.grid td .cust-name {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 可折叠表单面板（客户档案：表单默认收起，列表当主内容） */
details.panel > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
details.panel > summary::-webkit-details-marker { display: none; }
details.panel > summary h3 { margin: 0; }
details.panel > summary .sum-hint { margin-left: auto; font-size: 12px; color: var(--u2-muted); }
details.panel[open] > summary { margin-bottom: 14px; }
details.panel > summary:hover h3 { color: var(--u2-blue); }
/* 三角指示器 */
details.panel > summary::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 5px solid var(--u2-muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform .16s;
  flex: 0 0 auto;
}
details.panel[open] > summary::before { transform: rotate(90deg); }

/* ---------- 11. 表单控件 ---------- */
input[type=text], input[type=date], input[type=number], input[type=password],
input[type=search], input[type=email], input[type=tel], select, textarea {
  height: 34px;
  padding: 0 12px;
  border: 1px solid var(--u2-line-3);
  border-radius: var(--u2-r-sm);
  background: var(--u2-card);
  color: var(--u2-ink);
  font-size: 13px;
  transition: border-color .14s, box-shadow .14s;
}
textarea { height: auto; padding: 9px 12px; }
select { padding-right: 30px; }
input:focus, select:focus, textarea:focus {
  outline: 0;
  border-color: var(--u2-blue);
  box-shadow: 0 0 0 3px rgba(var(--u2-blue-rgb), .13);
}
.toolbar { gap: 10px; }

/* ---------- 12. 筛选胶囊（状态 chips） ---------- */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.chip {
  height: 32px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border-radius: 999px;
  border: 1px solid var(--u2-line-3);
  background: var(--u2-card);
  color: var(--u2-ink-2);
  font-size: 13px;
}
.chip:hover { background: var(--u2-hover); text-decoration: none; }
.chip.on {
  background: var(--u2-blue-50);
  border-color: var(--u2-blue-100);
  color: var(--u2-blue);
  font-weight: 500;
}
.chip b { font-variant-numeric: tabular-nums; }

/* ---------- 13. 空状态 ---------- */
.empty { padding: 34px 16px; text-align: center; color: var(--u2-muted); font-size: 13px; }
.empty .ico { opacity: .5; margin-bottom: 10px; }

/* ---------- 14. 响应式 ---------- */
@media (max-width: 1180px) {
  .dash, .dash.even { grid-template-columns: minmax(0, 1fr); }
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 820px) {
  .content { padding: 16px 14px 32px; }
  .topbar { padding: 0 14px; height: 56px; }
  .topbar-search { display: none; }
  .kpi-grid { gap: 12px; }
  .kpi { padding: 14px; }
  .kpi-val { font-size: 23px; }
  .page-head { flex-direction: column; align-items: stretch; }
  .page-head h1 { font-size: 19px; }
  .badge { height: 20px; font-size: 11px; }
}
/* 手机上 KPI 保持 2×2（4 张竖排会把首屏吃光），字号收一档 */
@media (max-width: 460px) {
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .kpi { padding: 12px; border-radius: 14px; }
  .kpi-top { font-size: 12px; gap: 6px; }
  .kpi-ico { width: 22px; height: 22px; border-radius: 7px; }
  .kpi-val { font-size: 19px; margin-top: 8px; }
  .kpi-val small { font-size: 11px; }
  .kpi-sub { font-size: 11px; margin-top: 3px; }

  /* 环形图 + 图例在窄屏改上下排：横排时图例只剩 140px，
     仓库名会被 ellipsis 吃掉（「主仓库」变成「主仓」），金额也折成两行。 */
  .donut-holder { flex-direction: column; align-items: stretch; gap: 14px; }
  .donut { width: 148px; height: 148px; margin: 0 auto; }
  .donut-legend { font-size: 12px; gap: 7px; }
}

/* ==================================================================
 * 11. KPI 卡可点击（工作台四张指标卡要能落到明细）
 * ------------------------------------------------------------------
 * 卡片本身是 <a class="kpi">，所以要把 a 的默认样式清掉，
 * 并用右下角的箭头做「可进入」的静态提示（触屏没有 hover，箭头不能只在 hover 出现）。
 * ================================================================== */
a.kpi {
  display: block;
  text-decoration: none;
  color: inherit;
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
a.kpi:hover {
  transform: translateY(-2px);
  box-shadow: var(--u2-sh-h);
  border-color: var(--u2-blue-100);
}
a.kpi:active { transform: translateY(0); }
a.kpi:focus-visible { outline: 2px solid rgba(var(--u2-blue-rgb), .55); outline-offset: 2px; }
.kpi-go {
  position: absolute;
  right: 13px;
  top: 15px;
  width: 16px;
  height: 16px;
  color: var(--u2-faint);
  transition: color .16s ease, transform .16s ease;
}
a.kpi:hover .kpi-go { color: var(--u2-blue); transform: translateX(2px); }

/* ==================================================================
 * 11b. 统计卡（.card）可点击 + 报表页下钻配套件
 * ------------------------------------------------------------------
 * 背景：.kpi 的可点范式（上一节）已经完整，但报表页用的是 .card，全站没有 a.card 规则。
 * 直接给 .card 套 <a> 会命中 app.css:58-59 的全局
 *   a{color:var(--c-primary)}  a:hover{text-decoration:underline}
 * —— 卡片里的数字会变蓝、hover 冒下划线，跟旁边静态卡片的深色数字明显不一致。
 * 所以这里必须显式 color:inherit，并在 hover 里再压一次 text-decoration。
 *
 * position:relative 只加在 a.card 上（.card 本身在别处大量使用，不动它）：
 * .card-go 是绝对定位箭头，而 .card 的祖先 .cards / .panel 都没有定位上下文，
 * 不设相对定位箭头会跑到最近的定位祖先上去（.kpi 没这问题，是它自带 position:relative）。
 *
 * 不写死宽度：.card 是 .cards 栅格的格子，宽度由栅格决定，写死会在移动端塌掉。
 * ================================================================== */
a.card {
  position: relative;
  display: block;
  text-decoration: none;
  color: inherit;
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
a.card:hover {
  transform: translateY(-2px);
  box-shadow: var(--u2-sh-h);
  border-color: var(--u2-blue-100);
  text-decoration: none;
}
a.card:active { transform: translateY(0); }
a.card:focus-visible { outline: 2px solid rgba(var(--u2-blue-rgb), .55); outline-offset: 2px; }
/* 给箭头留位置：否则卡片标题一长就被箭头压住（目前标题都短，属于防御性留白） */
a.card .card-label { padding-right: 24px; }
.card-go {
  position: absolute;
  right: 13px;
  top: 15px;
  width: 16px;
  height: 16px;
  color: var(--u2-faint);
  transition: color .16s ease, transform .16s ease;
}
a.card:hover .card-go { color: var(--u2-blue); transform: translateX(2px); }

/* ---- 口径横幅（modules/stock/query.php 的聚合视图 view=in_stock/out/low） ----
 * 用户必须一眼看出「这不是全部库存，而且仓库筛选被忽略了」，
 * 否则「选了 A 仓数字没变」会被当成故障。 */
.scope-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 14px;
  padding: 10px 14px;
  margin-bottom: 12px;
  border-radius: 12px;
  border: 1px solid #F2E2C4;
  background: #FFF9EF;
  color: #8A5A00;
  font-size: 12.5px;
  line-height: 1.6;
}
.scope-note b { font-weight: 600; }
/* 按钮别跟着横幅文字变成棕色，保持全局按钮外观 */
.scope-note .btn { color: var(--u2-ink-2); }

/* ---- 就地展开的明细（modules/report/ar_ap_sum.php） ----
 * 用「紧跟被点那一行」的 drill-row，所以父表格的列宽不会被撑变形；
 * 嵌套表自己横向滚动（外层 table.grid 是 min-width:max-content，不滚会顶破 td）。 */
tr.drill-row > td { background: var(--u2-hover); }
.drill-box { padding: 4px 2px 2px; }
.drill-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-bottom: 8px;
  font-size: 12.5px;
}
.drill-head b { color: var(--u2-ink); }
.drill-head .btn { margin-left: auto; }
.drill-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.drill-scroll > table.grid { background: var(--u2-card); border-radius: 10px; }

/* ---- 打印：卡片必须回到静态外观 ----
 * 5 个报表页都有打印按钮，而除 doc.css（单据打印专用，报表页不引，见 header.php:36-42）
 * 外全站没有任何 @media print。不补这段的话打印稿上：卡片文字被 a{color} 染成蓝色，
 * 右上角箭头被一起印出来（像一个凭空多出来的 > 符号）。 */
@media print {
  a.kpi,
  a.card {
    transform: none !important;
    box-shadow: none !important;
  }
  .kpi-go,
  .card-go { display: none !important; }
  .scope-note {
    border-color: #DDD;
    background: var(--u2-card);
    color: #000;
  }
  .drill-head .btn { display: none !important; }
}

/* ==================================================================
 * 12. 登录页（该页不引 header.php，样式独立成段）
 * ================================================================== */
.login-page { background: var(--u2-bg-2); }
.login-box {
  width: 396px;
  padding: 40px 34px 30px;
  background: var(--u2-card);
  border: 1px solid rgba(255, 255, 255, .9);
  border-radius: 20px;
  box-shadow: 0 26px 64px -30px rgba(16, 24, 40, .38), 0 2px 6px rgba(16, 24, 40, .05);
}
.login-logo {
  display: block;
  height: 40px;
  width: auto;
  max-width: 100%;
  margin: 0 auto 26px;
}
.login-box label { font-size: 13px; color: var(--u2-muted); font-weight: 500; }
.login-box input { background: var(--u2-hover); border-color: var(--u2-line-2); }
.login-box input:focus { background: var(--u2-card); }
.login-box .hint { color: var(--u2-muted); }
@media (max-width: 460px) {
  .login-box { padding: 32px 22px 26px; }
  .login-logo { height: 34px; margin-bottom: 22px; }
  .kpi-go { right: 11px; top: 13px; }
}

/* ==================================================================
 * 13. 客户详情（modules/base/customer_view.php）
 *     列表页里的客户名统一走 a.cust-link，点开就是客户 360 视图，
 *     解决「KPI 点进去之后链就断了」的问题。
 * ================================================================== */
a.cust-link { color: var(--u2-blue); text-decoration: none; }
a.cust-link b { font-weight: 600; }
a.cust-link:hover { color: var(--u2-blue-d); text-decoration: underline; text-underline-offset: 2px; }

/* 键值信息网格：桌面两栏 3 列 → 单栏 4 列 → 手机 2 列（固定列数比 auto-fit 可控） */
/* 下单频次：4 个统计小格 + 12 个月柱状 */
.stat-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.stat {
  display: block; padding: 12px 14px; border: 1px solid var(--u2-line);
  border-radius: var(--u2-r-sm); background: var(--u2-soft); color: inherit;
  text-decoration: none; min-width: 0; transition: border-color .15s, box-shadow .15s;
}
a.stat:hover { border-color: var(--u2-blue-100); box-shadow: 0 2px 10px rgba(20, 40, 80, .06); background: var(--u2-card); }
.stat-k { display: block; font-size: 12px; color: var(--u2-muted); }
.stat-v { display: block; font-size: 21px; font-weight: 700; color: var(--u2-ink); line-height: 1.35; margin: 2px 0 1px; letter-spacing: .2px; }
.stat-v.sm { font-size: 15.5px; }
.stat-v small { font-size: 12px; font-weight: 500; color: var(--u2-muted); margin-left: 2px; }
.stat-s { display: block; font-size: 12px; color: var(--u2-muted); }

.mchart { display: flex; align-items: flex-end; gap: 6px; margin-top: 16px; padding: 0 2px; }
.mb { flex: 1 1 0; min-width: 0; text-align: center; }
.mb-track { height: 84px; display: flex; align-items: flex-end; justify-content: center; }
.mb-fill {
  display: block; width: 66%; max-width: 26px; min-height: 3px; height: 0;
  border-radius: 4px 4px 2px 2px; background: linear-gradient(180deg, var(--u2-blue-l), var(--u2-blue));
}
.mb.zero .mb-fill { background: var(--u2-soft-2); }
.mb-x { display: block; margin-top: 6px; font-size: 10.5px; color: var(--u2-muted); white-space: nowrap; }

.kv-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 20px; }
.kv { min-width: 0; }
.kv > span { display: block; font-size: 12px; color: var(--u2-muted); margin-bottom: 3px; }
.kv > b { display: block; font-size: 13.5px; color: var(--u2-ink); font-weight: 600; line-height: 1.5; word-break: break-word; }
.kv > b.mono { font-family: Consolas, Menlo, monospace; letter-spacing: .2px; font-weight: 500; }
/* 卡片内的分节小标题（基本信息 / 开票资料 / 备注） */
.kv-sub {
  margin: 18px 0 11px;
  padding-top: 14px;
  border-top: 1px solid var(--u2-line);
  font-size: 12px;
  font-weight: 600;
  color: var(--u2-ink);
  letter-spacing: .01em;
}

/* 宽表在窄屏不再硬压列宽（单号会被断成两行），改成卡片内横向滑动。
   断点跟 .dash 一样取 1180：1024 平板开始就是单栏宽屏了，表格也得跟着滑。 */
@media (max-width: 1180px) {
  .kv-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .tbl-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tbl-scroll > table.grid { width: auto; min-width: 100%; white-space: nowrap; }
}

@media (max-width: 820px) {
  /* 页头按钮一多就顶出屏幕（客户详情页有 4 个），手机允许换行 */
  .page-head .actions { flex-wrap: wrap; flex-shrink: 1; }
  /* 手机上 1 列会把 10 个字段拉成一条长龙，压成 2 列 */
  .kv-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
  /* 下单频次：4 格压成 2×2，柱状图横向紧一点 */
  .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .stat { padding: 10px 12px; }
  .stat-v { font-size: 19px; }
  .stat-v.sm { font-size: 14px; }
  .mchart { gap: 3px; margin-top: 12px; }
  .mb-track { height: 64px; }
  .mb-x { font-size: 10px; transform: none; }
  /* 手机上 12 根柱子会把月份标签挤成一团，只留最近 6 个月 */
  .mchart .mb:nth-child(-n+6) { display: none; }
}
/* 只在手机出现的补充说明 */
.only-sm { display: none; }
@media (max-width: 820px) {
  .only-sm { display: inline; }
}

/* ==================================================================
 * 14. 生产看板 v2（modules/sales/progress.php?tab=pending）
 * ------------------------------------------------------------------
 * 改造前的三个问题：
 *   ① 每行整片铺 8%~10% 语义色（超期淡红 / 超 48h 淡橙），5 行连成一大块
 *      脏奶黄，压在白面板上又闷又旧 —— 这是「不协调」的来源；
 *   ② 单号·客户·交期·进度·状态·操作 六段全靠 flex 挤，客户列 flex:1
 *      把空白全吃掉，右侧四段挤成一坨，行与行之间列也不对齐；
 *   ③ 图例是 4 个 10px 小色块 + 数字，字号跟正文一样，看着像备注。
 * 改造后：
 *   · 行底一律纯白 + 1px 浅描边，状态色只落在「左侧 3px 竖条 / 分组圆点 /
 *     数字 / 交期文案」四处，不再铺满整行；
 *   · 行改 5 列 grid（152 / 1fr / 156 / 176 / 66），列宽固定 → 垂直对齐；
 *   · 交期拆两行（主文 + 相对时间 + 原因小标签），进度由 8 个灰方块改成
 *     「5px 水位条 + 1/8」，一眼看出做到哪一步；
 *   · 图例升级为 4 张统计卡，有单的卡片可点，直接滚到对应分组。
 * 本块必须在 app.css 之后（header.php 里 ui2.css 是最后一个 <link>）。
 * ================================================================== */

/* 分组色只需要一个「光晕」变量，色值沿用 app.css 的 --pb-fg */
.is-rank1 { --pb-halo: rgba(229, 72, 77, .13); }
.is-rank2 { --pb-halo: rgba(217, 119, 6, .13); }
.is-rank3, .is-rank4 { --pb-halo: rgba(var(--u2-blue-rgb), .13); }

.prod-board { padding: 18px 18px 16px; }

/* ---------- 面板头 ---------- */
.prod-board-h { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
.prod-board-h h3 { margin: 0; font-size: 16px; letter-spacing: .2px; }
.pb-h-sum {
  display: inline-flex; align-items: center; height: 22px; padding: 0 9px;
  border-radius: 999px; background: var(--u2-soft-2);
  font-size: 12px; font-weight: 600; color: var(--u2-ink-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.prod-search { margin: 0 0 0 auto; }
.prod-sub { font-size: 12px; margin: 4px 0 14px; }

/* ---------- 统计卡（原图例） ---------- */
.pb-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 18px; }
.pb-stat {
  display: flex; align-items: baseline; gap: 9px;
  padding: 11px 15px; border-radius: 12px;
  background: var(--u2-soft); border: 1px solid var(--u2-line);
  text-decoration: none;
  transition: background .15s, border-color .15s, box-shadow .15s;
}
.pb-stat b {
  font-size: 21px; line-height: 1.15; font-weight: 700; letter-spacing: -.3px;
  color: var(--pb-fg, var(--u2-ink)); font-variant-numeric: tabular-nums;
}
.pb-stat span { font-size: 11.5px; color: var(--u2-muted); white-space: nowrap; }
a.pb-stat:hover {
  background: var(--u2-card); border-color: var(--u2-line-2);
  box-shadow: 0 8px 18px -14px rgba(16, 24, 40, .5);
  text-decoration: none;
}
.pb-stat.is-zero { background: var(--u2-soft); }
.pb-stat.is-zero b { color: var(--u2-faint); }
.pb-stat.is-total { background: var(--u2-hover); border-color: rgba(var(--u2-blue-rgb), .14); }
.pb-stat.is-total b { color: var(--u2-blue); }

/* ---------- 分组标题 ---------- */
.prod-group { scroll-margin-top: 18px; }
.prod-group + .prod-group { margin-top: 20px; }
.prod-group-h {
  display: flex; align-items: center; gap: 8px;
  margin: 0 2px 9px;
  font-size: 12.5px; font-weight: 700; letter-spacing: .2px; color: var(--u2-ink-2);
}
.prod-group-h i {
  flex: 0 0 7px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--pb-fg, var(--u2-faint));
  box-shadow: 0 0 0 3px var(--pb-halo, rgba(152, 162, 179, .14));
}
.prod-group-h span { min-width: 0; }
.prod-group-h b {
  display: inline-flex; align-items: center; height: 18px; padding: 0 7px;
  border-radius: 999px; background: var(--u2-soft-2);
  font-size: 11px; font-weight: 700; color: #8A94A6;
  font-variant-numeric: tabular-nums;
}
.prod-group-h .pb-sep { background: var(--u2-faint); }

/* ---------- 一行：纯白卡片 + 5 列 grid ---------- */
.prod-row {
  position: relative;
  display: grid;
  grid-template-columns: 152px minmax(0, 1fr) 158px 176px 66px;
  align-items: center;
  gap: 0 16px;
  min-height: 54px;
  padding: 9px 14px 9px 17px;
  background: var(--u2-card);
  border: 1px solid var(--u2-line);
  border-radius: 12px;
  color: var(--u2-ink-2);
  text-decoration: none;
  overflow: visible;
  transition: border-color .15s, box-shadow .15s, background .15s;
}
.prod-row + .prod-row { margin-top: 8px; }
.prod-row:hover {
  background: var(--u2-soft); border-color: rgba(var(--u2-ink-rgb), .16);
  box-shadow: 0 10px 20px -16px rgba(16, 24, 40, .55);
  text-decoration: none;
}
.prod-row.is-active {
  border-color: rgba(var(--u2-blue-rgb), .42);
  box-shadow: 0 0 0 3px rgba(var(--u2-blue-rgb), .10);
}
.prod-row.is-active::after {
  content: "当前"; position: absolute; top: -1px; right: -1px;
  padding: 1px 8px; font-size: 10px; line-height: 15px; font-weight: 600;
  color: #fff; background: var(--u2-blue); border-radius: 0 12px 0 10px;
}
/* 左侧状态竖条：上下各留 13px，不顶满整行，比 5px 通栏更收敛 */
.prod-row::before {
  content: ""; position: absolute; left: 0; top: 13px; bottom: 13px;
  width: 3px; border-radius: 0 3px 3px 0;
  background: var(--pb-fg, #D0D5DD);
}

.prod-no {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; font-weight: 700; letter-spacing: .1px;
  color: var(--u2-ink); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.prod-cust {
  min-width: 0; font-size: 13.5px; font-weight: 500; color: var(--u2-ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.prod-due { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.prod-due b {
  font-size: 12.5px; font-weight: 600; color: var(--u2-ink);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.prod-due b.is-none { color: var(--u2-muted); font-weight: 500; }
.prod-rel {
  display: inline-flex; align-items: center; gap: 6px;
  font-style: normal; font-size: 11.5px; color: var(--u2-muted); white-space: nowrap;
}
.prod-rel.is-warn { color: #B45309; font-weight: 600; }
.is-rank1 .prod-rel { color: #C6292E; font-weight: 600; }
.prod-why {
  font-style: normal; font-size: 10.5px; line-height: 15px;
  padding: 0 5px; border-radius: 4px;
  color: #8A94A6; background: #F3F5F9;
}

/* ---------- 进度：徽章 + 水位条 + 1/8 ---------- */
.prod-prog { display: flex; align-items: center; gap: 9px; min-width: 0; }
.prod-prog .pb-tag {
  flex: 0 0 auto; height: 19px; padding: 0 7px; border-radius: 6px;
  background: var(--u2-soft-2); color: var(--u2-ink-2);
  font-size: 11px; font-weight: 600;
}
.prod-prog .pb-tag.pb-blue { background: var(--u2-blue-50); color: var(--u2-blue); }
.pbar { flex: 0 1 70px; height: 5px; border-radius: 3px; background: var(--u2-soft-2); overflow: hidden; }
.pbar > i { display: block; height: 100%; border-radius: 3px; background: var(--u2-blue); }
.prod-steps { font-size: 11.5px; color: var(--u2-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.prod-go { text-align: right; font-size: 12px; font-weight: 600; color: var(--u2-blue); white-space: nowrap; }
.prod-go::after { content: "›"; margin-left: 3px; }
.prod-row:hover .prod-go { color: var(--u2-blue-d); }

/* ---------- 中屏：列宽收一点，水位条让位给客户名 ---------- */
@media (max-width: 1200px) {
  .prod-row { grid-template-columns: 140px minmax(0, 1fr) 142px 150px 62px; gap: 0 12px; }
}
@media (max-width: 980px) {
  .prod-row { grid-template-columns: 132px minmax(0, 1fr) 132px 120px 58px; gap: 0 10px; }
  .pbar { display: none; }
}

/* ---------- 手机（≤820）：每行转三行卡片 ---------- */
@media (max-width: 820px) {
  .prod-board { padding: 15px 13px 13px; }
  .prod-board-h { display: block; }
  .prod-board-h h3 { margin: 0 0 6px; }
  .prod-search { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; margin: 9px 0 0; }
  .prod-search input[type=search] { flex: 1 1 auto; min-width: 0; max-width: none; height: 36px; }
  .prod-search .btn.sm { flex: 0 0 auto; height: 36px; }
  .prod-sub { margin: 8px 0 12px; }

  .pb-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 14px; }
  .pb-stat { padding: 9px 12px; border-radius: 10px; }
  .pb-stat b { font-size: 19px; }
  .pb-stat span { font-size: 11px; }

  .prod-group + .prod-group { margin-top: 16px; }

  .prod-row {
    /* 第二列给死 46%：交期那格里「已等待 11 天 + 合同缺交货期」在 390 下会顶到卡片右边缘，
       用 auto 列宽时文字不换行就会把卡片撑出去。给死宽度 + 允许换行才稳。 */
    grid-template-columns: minmax(0, 1fr) 46%;
    grid-template-areas: "no no" "cust due" "prog prog";
    gap: 7px 10px;
    min-height: 0; padding: 11px 12px 11px 15px;
  }
  .prod-row::before { top: 0; bottom: 0; border-radius: 12px 0 0 12px; }
  .prod-no { grid-area: no; padding-right: 42px; }
  .prod-cust { grid-area: cust; font-size: 14px; font-weight: 600; color: var(--u2-ink); }
  .prod-due { grid-area: due; align-items: flex-end; text-align: right; min-width: 0; }
  /* 手机上 .prod-rel 退回块级：inline-flex 里的文字节点不能折行，长句会把卡片顶出去 */
  .prod-rel { display: block; white-space: normal; line-height: 1.45; }
  .prod-why { display: inline-block; vertical-align: 1px; margin-left: 4px; }
  .prod-prog { grid-area: prog; }
  .prod-go { display: none; }
  .pbar { display: block; flex: 1 1 auto; }
}

/* ---------- 已完成表：与看板同语言（去掉整行淡绿底，改左侧竖条 + 徽章底色） ----------
 * 原来 is-ontime / is-late 给每个 td 铺 9% 的绿，2 行连起来又是一块色斑，
 * 跟看板刚去掉的奶黄是同一个毛病。班级只在 progress.php 用到，不影响其他表。 */
table.grid tbody tr.is-ontime > td,
table.grid tbody tr.is-late > td { background: transparent; }
table.grid tbody tr.is-ontime:hover > td,
table.grid tbody tr.is-late:hover > td { background: var(--u2-soft); }
table.grid tbody tr.is-ontime > td:first-child { border-left: 3px solid #34C759; }
table.grid tbody tr.is-late > td:first-child { border-left: 3px solid var(--c-supp, #F5A524); }
table.grid tbody tr.is-ontime .pb-tag.pb-green { background: #EAF7EF; }
table.grid tbody tr.is-late .pb-tag.pb-orange { background: #FFF5E6; }

/* ==================================================================
 * 第 15 节：省 / 市 / 区县 / 街道乡镇 级联下拉（region.js）
 * ------------------------------------------------------------------
 * 原来是三个自由文本框，用户得自己敲「广东省」「深圳市」「南山区」，
 * 敲错一个字档案就脏了，也没法按地区统计。改成四级联动后，
 * 名称一律以字典为准，拼出来的完整地址也不会缺分隔符。
 * ================================================================== */
.rg-box {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 var(--sp-4);
  margin-bottom: var(--sp-3);
}
.rg-box > label { margin-bottom: var(--sp-2); min-width: 0; }
.rg-box select { text-overflow: ellipsis; }

/* 这一级还没得选（上级没选 / 该级没有下级）：只做视觉弱化，
   不能真的 disabled —— 禁用的控件不随表单提交，老值会被悄悄丢掉 */
.rg-box select.rg-empty {
  color: var(--u2-muted);
  background: rgba(120, 120, 128, .06);
  box-shadow: inset 0 0 0 1px rgba(17, 24, 39, .05);
}

@media (max-width: 820px) {
  .rg-box { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 手机上「街道/乡镇」经常留空，和详细地址并排会挤，这里让它独占一行更好点 */
  .rg-box > label:nth-child(4) { grid-column: 1 / -1; }
}

/* 完整地址旁边的小提示：省市区变了它会自己重拼 */
.rg-hint {
  font-size: 11.5px;
  color: var(--u2-muted);
  margin-left: 6px;
  white-space: nowrap;
}

/* ==================================================================
 * 第 16 节：列表页「省 / 市」筛选条 + 客户批量导入
 * ================================================================== */
.rf-box {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.rf-box select {
  min-width: 130px;
  max-width: 190px;
}
.rf-box select:disabled {
  color: var(--u2-muted);
  background: rgba(120, 120, 128, .06);
}

/* ---------- 批量导入 ---------- */
.imp-drop {
  border: 1.5px dashed var(--u2-line-3);
  border-radius: 12px;
  padding: 22px 18px;
  text-align: center;
  background: var(--u2-soft);
  transition: border-color .15s, background .15s;
}
.imp-drop.is-over {
  border-color: var(--u2-blue);
  background: rgba(var(--u2-blue-rgb), .05);
}
.imp-drop b { display: block; font-size: 14px; margin-bottom: 4px; }
.imp-drop .muted { font-size: 12px; }

.imp-stat {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin: 14px 0 10px;
}
.imp-stat span {
  flex: 1 1 0;
  min-width: 110px;
  border: 1px solid var(--u2-line);
  border-radius: 10px;
  padding: 10px 12px;
  background: var(--u2-card);
}
.imp-stat i {
  display: block;
  font-style: normal;
  font-size: 11.5px;
  color: var(--u2-muted);
  margin-bottom: 3px;
}
.imp-stat b { font-size: 18px; font-weight: 700; }

/* 预览表：一行一条客户，状态列给颜色 */
.tbl-imp td { vertical-align: top; font-size: 12.5px; }
.tbl-imp .loc-cell { line-height: 1.6; }
.tbl-imp .loc-cell small { color: var(--u2-muted); }
.imp-tag {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 20px;
  font-size: 11px;
  line-height: 18px;
  white-space: nowrap;
}
.imp-tag.ok   { background: #e8f5ee; color: #15803d; }
.imp-tag.warn { background: #fff5e6; color: #b45309; }
.imp-tag.bad  { background: #fdecec; color: #b91c1c; }
.imp-tag.skip { background: #eef1f6; color: #64748b; }
.tbl-imp tr.is-skip td { opacity: .55; }

@media (max-width: 820px) {
  .rf-box { width: 100%; }
  .rf-box select { flex: 1 1 46%; min-width: 0; max-width: none; }
  .imp-stat span { flex: 1 1 46%; }
}

/* 发货页：客户档案地址提示 + 一键带入 */
.daddr-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 5px;
  min-width: 0;
}
.daddr-file {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 11.5px;
  color: var(--u2-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.daddr-tools .btn { flex: 0 0 auto; }
@media (max-width: 640px) {
  .daddr-file { white-space: normal; }
}

/* ==================================================================
 * 12. 收款凭证上传（receipt）
 * ------------------------------------------------------------------
 * 只服务 modules/finance/receive.php。视觉沿用 ui2 的描边/圆角/主色，
 * 不引入新色系，避免和 sidebay 里的既有卡片打架。
 * ================================================================== */
.receipt-up { margin-top: 14px; }
.receipt-up-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.receipt-up-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--u2-ink);
}
.receipt-up-head .muted { font-size: 12px; }

/* 拖放区：input 铺满整块并透明，点哪里都能选文件，同时保留拖拽 */
.receipt-drop {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 18px 16px;
  border: 1.5px dashed var(--u2-line-3);
  border-radius: var(--u2-r-sm);
  background: var(--u2-soft);
  color: var(--u2-muted);
  cursor: pointer;
  text-align: center;
  transition: border-color .15s, background .15s;
}
.receipt-drop:hover,
.receipt-drop:focus-within {
  border-color: var(--u2-blue);
  background: var(--u2-blue-50);
}
.receipt-drop input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.receipt-drop svg { width: 24px; height: 24px; color: var(--u2-faint); }
.receipt-drop span { font-size: 13px; color: var(--u2-ink-2); }
.receipt-drop small { font-size: 11.5px; color: var(--u2-muted); }

.receipt-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
}
.receipt-thumb {
  position: relative;
  width: 104px;
  height: 78px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--u2-line-2);
  background: var(--u2-card);
  display: flex;
  align-items: center;
  justify-content: center;
}
.receipt-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.receipt-thumb em {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  font-style: normal;
  font-size: 10px;
  padding: 2px 5px;
  background: rgba(17, 24, 39, .62);
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.receipt-thumb.over { border-color: #C4271E; box-shadow: 0 0 0 2px #FFEDEB inset; }
.receipt-pdf { font-size: 12px; font-weight: 700; color: #C4271E; letter-spacing: .5px; }
.receipt-warn {
  flex: 1 1 100%;
  font-size: 12px;
  color: #C4271E;
  background: #FFEDEB;
  padding: 6px 10px;
  border-radius: 8px;
}

/* 列表里的凭证缩略图 */
.receipt-link {
  position: relative;
  display: inline-block;
  width: 56px;
  height: 40px;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--u2-line-2);
  background: var(--u2-card);
  vertical-align: middle;
}
.receipt-link img { width: 100%; height: 100%; object-fit: cover; display: block; }
.receipt-link span {
  position: absolute;
  right: 2px;
  bottom: 2px;
  font-size: 10px;
  line-height: 14px;
  background: rgba(17, 24, 39, .72);
  color: #fff;
  padding: 0 4px;
  border-radius: 4px;
}

/* ==================================================================
 * 13. 应收账龄 / 逾期催收
 * ================================================================== */
.aging-filter {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.aging-filter .chk {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  color: var(--u2-ink-2);
  cursor: pointer;
  white-space: nowrap;   /* 不换行：否则「只看逾期」会被挤成两行 */
  flex: 0 0 auto;
}
/* app.css 里 input 是 width:100%，会让 checkbox 独占一行把文字顶下去 */
.aging-filter .chk input[type="checkbox"] {
  width: auto;
  height: auto;
  margin: 0;
  flex: 0 0 auto;
}

.aging-table td.strong { font-weight: 600; color: var(--u2-ink); }
.aging-table tr.is-overdue > td { background: #FFFBFB; }
.aging-table tr.aging-row:hover > td { background: var(--u2-hover); }

.aging-name .expand-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--u2-ink);
  font-weight: 500;
  text-decoration: none;
}
.aging-name .expand-toggle:hover { color: var(--u2-blue); }
.expand-toggle .caret {
  display: inline-block;
  width: 10px;
  font-size: 10px;
  color: var(--u2-muted);
  transition: color .15s;
}
.expand-toggle.open .caret { color: var(--u2-blue); }

/* 逾期天数药丸：颜色随严重度加深，90 天以上最刺眼 */
.od {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}
.od-0 { background: #FFF3E4; color: #B25E00; }
.od-1 { background: #FFF7E6; color: #B25E00; }
.od-2 { background: #FFEFE4; color: #B4541A; }
.od-3 { background: #FFE6E0; color: #B33A24; }
.od-4 { background: #FFD9D4; color: #93261B; }

.aging-table td.num.neg { color: #C4271E; }

.aging-note {
  margin-top: 14px;
  padding: 12px 14px;
  background: var(--u2-hover);
  border: 1px solid var(--u2-line);
  border-radius: 10px;
  font-size: 12.5px;
  color: var(--u2-ink-2);
}
.aging-note p { margin: 0 0 6px; font-weight: 600; color: var(--u2-ink); }
.aging-note ul { margin: 0; padding-left: 18px; }
.aging-note li { margin: 3px 0; line-height: 1.65; }

/* KPI 卡的派生变体（首页 / 账龄页共用 .kpi） */
.kpi-val.val-red { color: #C4271E; }
.kpi-val-sm { font-size: 19px; }
.kpi-val .unit { font-size: 12px; font-weight: 500; color: var(--u2-muted); margin-left: 2px; }
.kpi-val .sep { color: var(--u2-faint); font-weight: 400; margin: 0 2px; }
.kpi-sub .due-soon { color: #B25E00; }
.overdue-hint { display: block; margin-top: 3px; color: #C4271E; font-weight: 600; }

/* 打印：凭证上传区、筛选条、展开箭头一律不出现在纸上 */
@media print {
  .receipt-up, .aging-filter, .page-head .actions { display: none !important; }
  .aging-table tr.is-overdue > td { background: transparent; }
  .aging-table tr.aging-row:hover > td { background: transparent; }
  .aging-note { background: transparent; border-color: #ccc; }
  .od { border: 1px solid #999; }
}

/* ==================================================================
 * 第 20 节：布局适配层（2026-11-02）
 * ------------------------------------------------------------------
 * 这一层只管「怎么排」，不碰任何颜色（配色全在 themes.css 的变量里），
 * 所以它和换肤互不影响，放在文件最后以便覆盖上面的组件规则。
 *
 * 解决三件事：
 *   1) 顶栏搜索「错位」—— 原来是 .topbar-search{flex:1} 的余量居中，
 *      左右内容一变宽度，搜索框就跟着偏（1920 下实测偏左 39px）。
 *   2) 档案表单在 1920 上只用了一半宽度，且长标签换行把同排输入框顶下去。
 *   3) 表格与表单在窄屏把卡片撑破；手机只是把电脑版压扁，不是另一套排法。
 * ================================================================== */

/* ---------- 20.1 顶栏：三等分，搜索真正居中 ---------- */
.topbar {
  display: grid;
  /* 左右两侧都取「均分，但不小于自身内容宽度」：
     纯 1fr 时，右侧账号区（头像+姓名+退出约 218px）在中号屏上比 1fr 还宽，
     会反压到搜索框上（900px 实测搜索被挤成 130px、管理员三个字还折成两行）。 */
  grid-template-columns: minmax(0, 1fr) auto minmax(max-content, 1fr);
  align-items: center;
  column-gap: 18px;
}
/* 页面标题过长时省略，不推挤搜索框 */
.topbar .crumb {
  justify-self: start;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topbar-search { justify-self: center; width: clamp(220px, 24vw, 400px); min-width: 0; }
.topbar-search form { max-width: none; }
.topbar .userbox { justify-self: end; }
.topbar .userbox > * { flex: 0 0 auto; }
.topbar .u-chip { white-space: nowrap; }

/* ---------- 20.2 表单栅格：固定列数，不再 auto-fill ----------
 * auto-fill + minmax(200px,1fr) 在 1920 下会算出 7~8 条轨道，
 * 一行 4 个字段只占前 4 条，右边空出一大片；不同行的字段数不同还会各排各的。
 * 固定 4 列后每行字段左中右都对得上，宽度也吃满。 */
.form-row {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 20px;
  align-items: stretch;
}
/* 标签紧贴输入框（4px）。这里不用 margin-top:auto 把输入框压到行底部 ——
 * 同一行里有的标签带灰色小字提示、有的没有，行高被撑高后，没提示的那些
 * 标签和输入框之间会空出一大截，看着像排版散架（销售开单页实测）。
 * 真正会让输入框错行的是「长标签折行」，所以长标签直接在 PHP 里改短。 */
.form-row > label {
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin-bottom: 14px;
}
.form-row > label > input,
.form-row > label > select,
.form-row > label > textarea { margin-top: 4px; }

.rg-box {
  /* 与 .form-row 同一条栅格线：原来 gap 是 16px、form-row 是 20px，
     省市区那一排的下拉框会比上面的输入框略宽，左右竖线对不齐。 */
  gap: 0 20px;
}
.rg-box > label { display: flex; flex-direction: column; }
.rg-box > label > select { margin-top: 4px; }

/* 断点按「卡片实际宽度」定：1024 的笔记本去掉侧栏只剩 714，4 列每列 165 就够用；
   再窄才降到 3 列。跟着视口走是因为侧栏在 820 以下会变成抽屉。 */
@media (max-width: 999px) { .form-row { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 779px) { .form-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 599px) { .form-row { grid-template-columns: minmax(0, 1fr); } }

/* label-over-input 的表单（生产进度补录等）：3 列 → 2 列 → 1 列 */
@media (max-width: 1180px) { .form-rows.cols3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .form-rows, .form-rows.cols3 { grid-template-columns: minmax(0, 1fr); } }

/* ---------- 20.3 表格：放得下就压回来，放不下就横滑 ----------
 * table.grid 全局是 min-width:max-content（为了不把单号断行），卡片只有
 * 700 多像素时它会直接画到卡片外面（实测 953 vs 714）。
 * 收法分三档，靠 .tbl-scroll 容器统一处理：
 *   宽屏：按内容撑开，撑不下就横滑（列宽一点不压）
 *   601~1180：先压回卡片宽度（单元格允许换行），压不动才出横滑
 *   ≤600：不压 —— 手机上一压就是每列一个字，宁可横滑
 * 没包容器的表格仍走下面的兜底规则，绝不会顶出卡片。 */
.tbl-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* 只在表格放不下时才出现的细滚动条：不给提示的话，右侧被切一半的列会让人
   以为又是「表格撑出卡片」，不知道还能往里滑。 */
.tbl-scroll { scrollbar-width: thin; scrollbar-color: rgba(120, 120, 128, .35) transparent; }
.tbl-scroll::-webkit-scrollbar { height: 8px; }
.tbl-scroll::-webkit-scrollbar-track { background: transparent; }
.tbl-scroll::-webkit-scrollbar-thumb { background: rgba(120, 120, 128, .32); border-radius: 8px; }
.tbl-scroll::-webkit-scrollbar-thumb:hover { background: rgba(120, 120, 128, .5); }
.tbl-scroll > table.grid { width: auto; min-width: 100%; }

@media (max-width: 1180px) {
  table.grid { min-width: 0; }
  table.grid th,
  table.grid td { overflow-wrap: break-word; word-break: normal; }
  /* 光放开 min-width 还不够：单元格自己带 nowrap（客户名 / 地址 / 单号），
     表格最小宽度仍是各列 nowrap 宽度之和，实测 1024 下 907px vs 卡片 714px。 */
  table.grid td.cust,
  table.grid td.addr,
  table.grid td.nowrap,
  table.grid th { white-space: normal; }
  /* 客户名内层 span 自带 nowrap + max-width:200，同样会把整列钉在 200px */
  table.grid td .cust-name { white-space: normal; max-width: 100%; }
  /* 有容器的：先试压缩（100% + min-width:0），min-content 实在超过容器
     时表格自己会撑开，滚动条随之出现 —— 不用按页面手工判断「该压还是该滑」 */
  .tbl-scroll > table.grid { width: 100%; min-width: 0; }
  /* 压缩档把单元格左右内边距从 10 收到 6：7 列就省下 56px，
     1024 的客户列表正好从「差 13px，删除键被卡片切一半」变成整好放下。 */
  .tbl-scroll table.grid th,
  .tbl-scroll table.grid td { padding-left: 6px; padding-right: 6px; }
}

/* ---------- 20.4 平板 / 手机顶栏：改成两行流式 ----------
 * 原来 ≤820 直接把搜索框 display:none 藏掉了（手机上想搜还得先回列表页）。
 * 这里改成：第一行 菜单 + 标题 + 头像，第二行整条搜索通栏。
 * 5 寸手机到 11 寸平板都是这套，比原来「藏起来」有用得多。 */
@media (max-width: 820px) {
  .topbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    height: auto;
    min-height: 56px;
    padding: 8px 14px;
    row-gap: 8px;
    column-gap: 10px;
  }
  .topbar .nav-toggle { order: 0; margin-right: 0; }
  .topbar .crumb { order: 1; flex: 1 1 auto; line-height: 38px; }
  .topbar .userbox { order: 2; margin-left: 0; flex: 0 0 auto; gap: 8px; }
  .topbar-search {
    order: 5;
    display: flex;
    flex: 1 1 100%;
    width: 100%;
    margin: 0;
  }
  .topbar-search form { width: 100%; }
}

/* ==================================================================
 * 20.5 手机：换一套排法，不是把电脑版压扁
 * ------------------------------------------------------------------
 * · 顶栏变两行：第一行菜单/标题/头像，第二行整条搜索（原来手机上直接把搜索藏了）
 * · 表单单列、输入框 42px 高、字号 16px（iOS 聚焦不再自动放大整页）
 * · 页头按钮整行等分，工具栏换行
 * · 表格整块横向滑动，文字不被压成一字一行
 * ================================================================== */
@media (max-width: 600px) {
  html { -webkit-text-size-adjust: 100%; }
  .content { padding: 12px 12px 28px; }
  .panel { padding: 14px; border-radius: 14px; margin-bottom: 12px; }

  /* 顶栏在 20.4 已经变两行了，这里只把各块尺寸收到手机档 */
  .topbar { padding: 8px 12px; column-gap: 8px; }
  .topbar .nav-toggle { width: 38px; height: 38px; }
  .topbar .crumb { font-size: 15px; }
  .topbar .userbox { gap: 6px; }
  .topbar .userbox .u-chip { height: 32px; padding: 0 8px 0 3px; }
  .topbar .userbox .u-avatar { width: 24px; height: 24px; }
  .topbar .userbox a { height: 32px; padding: 0 9px; }
  .topbar-search input { height: 38px; font-size: 14px; }

  /* 手机上不放换肤按钮：它靠 hover 展开，触屏上不好用；配色去「我的账号」改。
     选择器要写到 .topbar .userbox 这一层 —— .theme-switch 的 display 定义在
     themes.css 里，而 themes.css 在本文件之后加载，同级选择器会被它盖掉。 */
  .topbar .userbox .theme-switch { display: none; }
  /* 姓名收成头像气泡：手机顶栏一行要塞下菜单、标题、账号、退出，
     长名字（「系统管理员」四个字就 70px）会把标题挤成省略号 */
  .topbar .userbox .u-chip { font-size: 0; gap: 0; padding: 0; width: 32px; justify-content: center; }
  .topbar .userbox .u-chip .u-avatar { font-size: 12px; }

  /* 页头：标题一行，按钮一整行等分 */
  .page-head { gap: 10px; }
  .page-head h1 { font-size: 18px; }
  .page-head .actions { width: 100%; }
  .page-head .actions .btn { flex: 1 1 0; justify-content: center; }

  /* 工具栏换行，筛选框占满一行 */
  .toolbar { flex-wrap: wrap; row-gap: 8px; }
  .toolbar > form { flex: 1 1 100%; }

  /* 输入框：42px 高 + 16px 字，触屏好点，也避免 iOS 聚焦时整页放大 */
  .form-row > label > input,
  .form-row > label > select,
  .form-rows .fr > input,
  .form-rows .fr > select,
  .rg-box select { height: 42px; font-size: 16px; }
  .form-row > label > textarea,
  .form-rows .fr > textarea { font-size: 16px; }
  .form-row > label { margin-bottom: 12px; }

  /* 表格整块横滑：卡片里放不下就滑，不让列被压成一字一行 */
  .panel > table.grid {
    display: block;
    width: 100%;
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  table.grid th,
  table.grid td { white-space: nowrap; }
  table.grid td.addr,
  table.grid td.cust { max-width: 46vw; }
  /* 手机上的滚动容器不压缩：宽度按内容来，宁可横滑 */
  .tbl-scroll > table.grid { width: auto; min-width: max-content; }
  .tbl-scroll table.grid td.cust,
  .tbl-scroll table.grid td.addr,
  .tbl-scroll table.grid td.nowrap,
  .tbl-scroll table.grid td .cust-name { white-space: nowrap; max-width: none; }

  /* 按钮整行：底部操作区在手机上竖排更好点 */
  .actions { flex-wrap: wrap; }
  .actions .btn.primary { flex: 1 1 100%; justify-content: center; }
}

/* 小屏（iPhone SE / 竖屏安卓）：再省一点，把顶栏换肤位让给标题 */
@media (max-width: 400px) {
  .topbar .userbox a { padding: 0 7px; }
  .panel { padding: 12px; }
}

/* ==================================================================
 * 20.6 顶栏搜索框：放大镜压在占位文字上
 * ------------------------------------------------------------------
 * 现象：输入框里「搜」字的左半边被放大镜盖住，看着像错位。
 *
 * 根因（已用 getComputedStyle 实测确认，不是猜的）：
 *   第 211 行 `.topbar-search input` 写了 padding-left:36px，
 *   但第 586 行「11. 表单控件」里有一条
 *     input[type=search], input[type=email], ... { padding: 0 12px }
 *   两者优先级完全相同（0,1,1），而 586 行排在后面 —— 于是左内边距
 *   被打回 12px；图标是 absolute left:12px、宽 16px，占的正是 12~28px，
 *   文字也从 12px 起排，两者重叠 16px。
 *   实测：用 Chrome 打开真实页面读计算值 → padding-left:12px（写的是 36px）、
 *   border-radius:10px（写的是 999px）、height:34px（写的是 36px），
 *   说明整条规则都被后面那条盖掉了，不只是 padding。
 *
 * 修法：用 (0,2,1) 的 `.topbar-search input[type=search]` 重写一遍。
 *   为什么不直接把 586 行改成 :not()：那条规则同时服务
 *   「生产进度搜索」「出货单搜索」等页面内搜索框，动它会连带改到别的页面。
 *   为什么不 !important：本文件开头的《禁止事项》写明了不搞 !important 互殴。
 *
 * 尺寸：左内边距 38px = 图标 left 14 + 图标宽 16 + 间隙 8。
 *   高度 38px（原 36）顺手把点击区做大一点，触屏友好。
 * ================================================================== */
.topbar-search input[type=search] {
  width: 100%;
  height: 38px;
  padding: 0 16px 0 38px;
  border: 1px solid var(--u2-line-2);
  border-radius: 999px;
  background: var(--u2-input);
  color: var(--u2-ink);
  font-size: 13.5px;
  transition: background .16s, border-color .16s, box-shadow .16s;
}
.topbar-search input[type=search]::placeholder { color: var(--u2-muted); }
.topbar-search input[type=search]:focus {
  outline: 0;
  background: var(--u2-card);
  border-color: var(--u2-blue);
  box-shadow: 0 0 0 3px rgba(var(--u2-blue-rgb), .14);
}
/* 图标位置跟着左内边距一起定，避免以后单独改一边又错开 */
.topbar-search .ico { left: 14px; }

/* 手机上框更高一档（拇指点击区），图标靠左一点点跟着调 */
@media (max-width: 600px) {
  .topbar-search input[type=search] { height: 42px; font-size: 16px; padding: 0 16px 0 38px; }
  /* iOS 上 type=search 有自带的圆角与内阴影，去掉才和设计一致 */
  .topbar-search input[type=search] { -webkit-appearance: none; appearance: none; }
}

/* ==================================================================
 * 20.7 明细录入表格在手机上横滑
 * ------------------------------------------------------------------
 * 20.5 里写的是 `.panel > table.grid`，但「明细行」这类表格
 * （table.grid.items：销售开单 / 采购入库 / 采购退货 / 库存盘点 /
 *   仓库调拨 / 收款单 / 付款单 / 银行流水导入 / 发货单）都嵌在 <form>
 * 里面，不是 .panel 的直接子元素 —— 选不中，于是手机上整张表
 * 按内容宽度铺开，最右边的「金额」「删行」按钮被切到屏幕外。
 * 实测 465px 视口：表格 501px、文档 528px（超出 67px）。
 *
 * 这里直接按类名兜住，不用改 10 个 PHP 文件，也不用加 .tbl-scroll 容器。
 * display:block 不影响单元格排版（tr/td 仍组成匿名表格盒），
 * 只是让表格自己变成可横向滚动的盒子 —— 和 20.5 对列表主表的做法一致。
 * ================================================================== */
@media (max-width: 600px) {
  table.grid.items {
    display: block;
    width: 100%;
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ==================================================================
 * 20.8 品牌名：LOGO 旁边补「申钛云 SamTie Cloud」
 * ------------------------------------------------------------------
 * 背景：LOGO 图是 4.8:1 的 SAMTIE 英文字标位图，图里没有中文；
 * 换成「申钛云」之后，界面上一个中文字都没有，只有浏览器标签页能看到。
 *
 * 排法：图在左、名字在右竖排两行。
 *   侧栏 238px，去掉左右 padding 36px 还剩 202px。
 *   LOGO 原高 26px → 宽 124.8px，右边只剩 67px，塞不下「SamTie Cloud」
 *   （10px 字号要 57px，加上间距就顶满了），所以把图降到 22px（宽 105.6px），
 *   右边余 86px —— 英文 57px、中文 39px 都在余量内，右侧留白也够。
 *   不去改 LOGO 图片本身的尺寸比例，只调显示高度，图不会被拉变形。
 *
 * 注意：必须用 `.sidebar a.brand` 这种 (0,2,1) 的选择器。
 *   app.css:70 的 `.sidebar a { display:block; padding:... }` 是 (0,1,1)，
 *   比本文件里 `.brand` 的 (0,1,0) 高 —— 也就是说 .brand 上写的
 *   display:flex / padding:0 18px 其实一直是失效的。
 *   以前品牌区只有一张图，inline 的图放在 block 里看不出来；
 *   现在要塞「图 + 两行字」，不改优先级就一定竖着掉下去。
 * ================================================================== */
.sidebar a.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 64px;
  padding: 0 18px;
}
.brand-logo { height: 22px; }
.brand-name {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  line-height: 1.15;
}
.brand-name b {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--u2-side-ink);
  white-space: nowrap;
}
.brand-name i {
  font-style: normal;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--u2-side-ink-2);
  white-space: nowrap;
}

/* 登录页：LOGO 下面也一样写清楚（那里没有侧栏） */
.login-logo { margin: 0 auto 10px; }
.login-name {
  margin: 0 0 22px;
  text-align: center;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--u2-ink);
  line-height: 1.3;
}
.login-name span {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .06em;
  color: var(--u2-muted);
}

/* ==================================================================
 * 20.9 说明型表格：长文案要能折行
 * ------------------------------------------------------------------
 * table.grid 全局是 min-width:max-content（列表页为了单号不断行而定的），
 * 但「企业微信设置」这类表格里放的是成段说明文字，按 max-content 排会把
 * 表格撑到卡片外面，右侧被 overflow 裁掉 —— 实测 1440 下最后一列约 60px 不可见。
 *
 * 加个 .info 类，只影响显式标记的说明表：宽度跟卡片走，单元格允许折行。
 * 列表类表格不加这个类，行为不变。
 * ================================================================== */
.panel > table.grid.info { min-width: 0; width: 100%; }
.panel > table.grid.info th,
.panel > table.grid.info td {
  white-space: normal;
  overflow-wrap: break-word;
  word-break: normal;
  vertical-align: top;
}
.panel > table.grid.info td .muted { line-height: 1.65; }

/* ==================================================================
 * 20.10 推送矩阵表：手机上横滑
 * ------------------------------------------------------------------
 * 「事件 × 岗位」是一张 7 列的矩阵（1 列事件名 + 6 个岗位），
 * 461px 的手机视口里怎么压都放不下 —— 硬压会把「管理员」挤成两行、
 * 勾选框和文字错位。矩阵这种东西横滑比压缩可读得多。
 *
 * 用类名选择器（table.grid.matrix）而不是 .panel > ...：
 * 这张表在 <form> 里面，不是 .panel 的直接子元素，
 * 20.7 那次就是踩了这个坑（.panel > table.grid 选不中）。
 * ================================================================== */
@media (max-width: 600px) {
  table.grid.matrix {
    display: block;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  table.grid.matrix > thead,
  table.grid.matrix > tbody {
    display: table;
    width: 560px;          /* 6 个岗位列 + 事件列的最小可读宽度 */
    min-width: 100%;
  }
  table.grid.matrix th,
  table.grid.matrix td { white-space: nowrap; }
}

/* 手机上才提示「可左右滑动」：桌面表格本来就放得下，不需要这句 */
.matrix-hint { display: none; }
@media (max-width: 600px) {
  .matrix-hint { display: block; font-size: 12px; margin-top: 8px; }
}
