/* 达索报备系统看板 —— 样式
 *
 * 配色：以用户指定的四个色为**全部色相**（2026-09-25 定稿）
 *   深蓝  #0F5486  RGB(15 , 84,134)  品牌主色：标题 / 主按钮 / 选中态 / SFT 系列
 *   亮蓝  #1DA9FF  RGB(29 ,169,255)  强调：焦点态 / 进度条 / 标签下划线
 *   暖橙  #E87722  RGB(232,119, 34)  提示：即将到期 / 立即延期 / PFT 系列 / 统计方框数字
 *   中性灰 #97999B  RGB(151,153,155)  次级文字 / 分隔线 / 页面底（整条灰阶的锚点）
 *
 * 两条规矩（有静态守卫盯着，见 tools/check-web.cjs）：
 *   ① **色值只在下面这个 :root 块里出现一次**，别处一律 var() —— 换色只改这一处；
 *      唯一允许的字面量是纯白 #fff（它不属于任何一个色相，且到处都是）。
 *   ② **图形用原色、文字用加深版**：亮蓝对白仅 2.6:1、暖橙仅 3.0:1，画条可以、写小字不行，
 *      所以文字一律走 --accent-ink / --warm-ink（都 ≥5:1）。
 * 例外只有三个语义色（成功绿 / 危险红 / 预查青绿）—— 它们表达的是「对错」与「口径区分」
 * 而不是品牌，改掉会让人看不懂，故保留原值。其中「警示」并入了暖橙家族（--warn → --warm-ink）。
 */

:root {
  /* ---- ① 四个给定主色，原样落地 ---- */
  --brand:  #0F5486;
  --accent: #1DA9FF;
  --warm:   #E87722;
  --gray:   #97999B;

  /* ---- ② 由主色派生的用法色（压暗 / 提亮，不引入第五个色相） ---- */
  --brand-dark:  #0A3F66;   /* 深蓝·压暗：主按钮 hover */
  --brand-100:   #E4EFF7;   /* 深蓝·提亮：表头底 / 浅色徽章底 */
  --brand-50:    #F2F7FB;   /* 深蓝·极浅：行 hover / 浅底块 */
  --brand-line:  #D2E2EF;   /* 深蓝系边框 */
  --accent-ink:  #0A72B4;   /* 亮蓝的**文字**版（对白 5.2:1） */
  --accent-100:  #DFF1FF;   /* 焦点环 */
  --accent-line: #CFE4F5;   /* 信息条边框 */
  --accent-soft: #CFE8FA;   /* 迷你折线的柱 */
  --warm-ink:    #A85410;   /* 暖橙的**文字**版（对白 5.3:1） */
  --warm-bg:     #FDF1E6;
  --warm-line:   #F4D8BC;
  --warm-hover:  #FBEADB;
  --gray-line:   #E4E6E8;
  --gray-line-2: #EFF1F2;
  --gray-thumb:  #C9CCCE;
  --page:        #F6F7F8;
  /* 提示条标签底的三个半透明色 —— 也算"色值的第二处出现"，一并收在这里 */
  --brand-a10: rgba(15, 84, 134, .10);
  --warm-a14:  rgba(232, 119, 34, .14);
  --ok-a12:    rgba(26, 127, 75, .12);

  /* ---- ③ 旧变量名指向新色：别处用的 --ds-blue* 不必逐个改名 ---- */
  --ds-blue:      var(--brand);
  --ds-blue-dark: var(--brand-dark);
  --ds-blue-mid:  var(--accent);   /* 交互蓝（hover 边框 / 焦点）随之明显变亮 */
  --ds-blue-100:  var(--brand-100);
  --ds-blue-50:   var(--brand-50);

  --ink:   #26292C;
  --ink-2: #5E6165;
  --ink-3: var(--gray);
  --line:   var(--gray-line);
  --line-2: var(--gray-line-2);
  --bg:     var(--page);
  --card:   #FFFFFF;

  --muted: #6E7174;
  --muted-bg: var(--gray-line-2);

  /* ---- ④ 语义色：只有「警示」并入暖橙家族，其余三色保留原值 ---- */
  --ok: #1A7F4B;           --ok-bg: #E8F5EE;          --ok-line: #C9E6D6;
  --warn: var(--warm-ink); --warn-bg: var(--warm-bg); --warn-line: var(--warm-line);
  --danger: #B93327;       --danger-bg: #FDECEA;      --danger-line: #F0C8C4;
  /* 预查口径（SFT 的权威口径，见 shared/metrics.js 的 SCOPE_FIELDS）——
     与客户档案口径同为"权威"，但两者数字不可比，所以颜色必须一眼分得开 */
  --pre: #0F6F5C;          --pre-bg: #E6F4EF;         --pre-line: #CBE7DF;
  /* 系统标记「SFT」的青色（采集批次表的 badge）—— 与 --pre 同族但语义无关，故独立命名；
     和 --pre 一样属于"语义色例外"，不参与品牌色相 */
  --teal: #0F6B60;         --teal-bg: #E2F2F0;

  --radius: 10px;
  --radius-sm: 6px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", "Segoe UI", Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.6;
}

/* ---------------- 顶栏 ---------------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
  background: var(--card);
  /* 品牌色上沿一道 3px：让主色在首屏就有存在感（顶栏本身是白底，否则主色只在按钮上出现） */
  border-top: 3px solid var(--brand);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 12px; }
.logo {
  width: 40px; height: 40px;
  border-radius: 9px;
  /* 同色系内的微渐变（不是新色相）：一眼像个"标"而不是一块纯色方块 */
  background: linear-gradient(140deg, var(--accent-ink), var(--brand));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 600; letter-spacing: .5px;
  flex: none;
}
.brand h1 { margin: 0; font-size: 16px; font-weight: 600; color: var(--ds-blue); letter-spacing: .2px; }
.brand .sub { margin: 0; font-size: 12px; color: var(--ink-3); }

.topbar-right { display: flex; align-items: center; gap: 12px; flex: none; }
.collect-status {
  font-size: 12px; color: var(--ink-2);
  background: var(--ds-blue-50);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px;
  white-space: nowrap;
}
.collect-status .dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--ok); margin-right: 6px; vertical-align: 1px;
}
.user-chip {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--ink-2);
}
/* 认证方式（本地开发通道 / 飞书）—— 手机上会被隐藏，见移动端断点 */
.user-via { color: var(--ink-3); font-size: 12px; }
.avatar {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--ds-blue-100); color: var(--ds-blue);
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600;
  overflow: hidden;
}
/* 飞书返回头像时用图片；取不到就回落到上面那层的首字母 + 底色 ——
   两条路都在 .avatar 里，所以顶栏不写分支，只决定塞 <img> 还是纯文本。 */
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }

/* ---------------- 报备概览（导航栏上方） ---------------- */
/* 系列色 —— 这两个变量是给**标签文字**用的，所以取"当文字用"的那一档：
   深蓝原色对白 7.98:1 可直接当文字；暖橙对白只有 2.96:1，压深成 #A85410 才够 5.3:1。
   条形另有规定（用原色画，见下方 .ov-bar > i.sft/.pft）——
   两者共用同一个变量是踩过的坑：PFT 的条会跟着文字一起被压暗，整排条发闷。
   刻意不借 --pre（青绿）与语义色来当系列色 —— 那些色在这套界面里已经有
   「预查口径」等含义，拿来当系列色会让读者以为在说口径。

   「重点关注」卡（2026-09-25 用户要求）：**不再自带一个色**，与另外三张卡共用同一套系列色 ——
   第 1 行「监控客户」= SFT 深蓝、第 2 行「有更新」= PFT 暖橙，整排四张卡视觉一致。
   原实现给它单独一个紫色，理由是"沿用 SFT 的蓝会让人以为那是某系统的数"；改统一后，
   这个区分由**卡内每行左侧的文字标签**承担（颜色本来也不是唯一手段）。
   每行用哪个色由 WATCH_ROWS 逐行声明的 cls 决定（见 shared/overview.js）——
   --watch 只作"某行没声明 cls"时的兜底，不再是独立色相。 */
.overview {
  --sft: var(--brand);
  --pft: var(--warm-ink);
  --watch: var(--brand);
  background: var(--card);
  border-bottom: 1px solid var(--line);
  padding: 12px 24px 14px;
}
.ov-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.ov-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.ov-sub { font-size: 12px; color: var(--ink-3); }

/* 列数 = 指标数：三张按系统算的 + 一张按监控客户算的「重点关注」。
   换指标时这里要一起改（列数比指标多会留一块空白，少会让卡片挤成两行）——
   所以数量本身就写在注释里，改的人不必去 app.js 数一遍。 */
.ov-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}
.ov-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  background: var(--ds-blue-50);
}
.ov-card .ov-k {
  font-size: 12px; color: var(--ink-2); font-weight: 500;
  display: flex; align-items: center; gap: 4px;
  margin-bottom: 6px;
}
/* 口径提示用原生 title，不加弹层 —— 这类"说明"看一次就够，不该占版面 */
.ov-card .ov-q {
  width: 13px; height: 13px; flex: none;
  border-radius: 50%; border: 1px solid var(--ink-3); color: var(--ink-3);
  font-size: 9px; line-height: 11px; text-align: center;
  cursor: help; user-select: none;
}
.ov-row {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--ink-2);
  margin-top: 4px;
}
.ov-sys {
  flex: none; width: 26px; font-size: 10px; font-weight: 600;
  letter-spacing: .2px;
}
.ov-sys.sft { color: var(--sft); }
.ov-sys.pft { color: var(--pft); }
.ov-sys.watch { color: var(--watch); }
/* 「重点关注」那两行的标签比 SFT/PFT 长（四个汉字），给它们宽一点 ——
   与系统卡共用 26px 会把字挤成两行，卡片高度就和别的对不齐了 */
.ov-row-wide .ov-sys { width: 54px; }
/* 条形轨道恒占满，条宽 = 该指标内 SFT/PFT 的相对比例（较大者占满） */
.ov-bar {
  flex: 1 1 auto; min-width: 0;
  height: 8px; border-radius: 4px;
  background: var(--gray-line);   /* 条形轨道：中性灰的一条线，整排卡片共用 */
  overflow: hidden;
}
.ov-bar > i { display: block; height: 100%; border-radius: 4px; }
/* ⚠️ 条形要用**原色**，不能跟着 --sft/--pft 走 —— 那两个变量是"当文字用"的色：
   --pft 是暖橙加深版 #A85410（文字需要 ≥4.5:1），拿它画条会让整排条偏暗，
   与"图形用原色"的规矩不符（实测：这一处曾把 PFT 的条画成深棕橙）。 */
.ov-bar > i.sft { background: var(--brand); }
.ov-bar > i.pft { background: var(--warm); }
.ov-bar > i.watch { background: var(--watch); }
.ov-v {
  flex: none; min-width: 34px; text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600; color: var(--ink); font-size: 13px;
}

/* ---------------- 标签页 ---------------- */
.tabs {
  display: flex;
  gap: 2px;
  padding: 0 24px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}
.tab {
  appearance: none;
  border: none;
  background: none;
  padding: 12px 16px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink-2);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color .15s, border-color .15s;
}
.tab:hover { color: var(--ds-blue); }
/* 选中标签：文字用品牌深蓝、下划线用亮蓝 —— 一眼看出"当前在哪一页" */
.tab.is-active { color: var(--ds-blue); border-bottom-color: var(--accent); font-weight: 500; }

/* ---------------- 内容区 ---------------- */
main { padding: 20px 24px 48px; }
.panel { display: none; }
.panel.is-active { display: block; }

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px; flex-wrap: wrap;
}
.card-title { font-size: 15px; font-weight: 600; color: var(--ds-blue); }
.card-desc { font-size: 12px; color: var(--ink-3); margin-top: 2px; }

/* ---------------- 概览小卡片 ---------------- */
.stats { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.stat {
  flex: 1 1 120px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
}
.stat .k { font-size: 12px; color: var(--ink-3); }
/* 方框里的**数字**用暖橙（用户指定 #E87722）。
   22px/600 属"大号文字"，暖橙对卡片白底 2.96:1、对选中态浅底约 3.1:1，刚好过 AA 大字门槛（3:1）；
   所以**只给数字用** —— 标签文字（.stat .k）保持中性灰，橙字写小字会明显发涩。 */
.stat .v { font-size: 22px; font-weight: 600; color: var(--warm); line-height: 1.3; }
.stat .v.small { font-size: 16px; }
.stat.is-active { border-color: var(--ds-blue); background: var(--ds-blue-50); cursor: pointer; }

/* ---------------- 筛选栏 ---------------- */
.filters {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
  padding-bottom: 14px; border-bottom: 1px solid var(--line-2); margin-bottom: 14px;
}
.field { display: flex; flex-direction: column; gap: 4px; }
.field label { font-size: 12px; color: var(--ink-3); }
.field select, .field input {
  font-family: inherit; font-size: 13px; color: var(--ink);
  padding: 6px 8px; min-width: 120px;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: #fff;
}
.field input[type="text"] { min-width: 180px; }
.field select:focus, .field input:focus { outline: 2px solid var(--ds-blue-100); border-color: var(--ds-blue-mid); }
.spacer { flex: 1 1 auto; }

/* ---------------- 按钮 ---------------- */
.btn {
  appearance: none; font-family: inherit; font-size: 13px;
  padding: 7px 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: #fff; color: var(--ink);
  cursor: pointer; transition: background .15s, border-color .15s, color .15s;
  white-space: nowrap;
}
.btn:hover { border-color: var(--ds-blue-mid); color: var(--ds-blue); }
.btn-primary { background: var(--ds-blue); border-color: var(--ds-blue); color: #fff; }
.btn-primary:hover { background: var(--ds-blue-dark); border-color: var(--ds-blue-dark); color: #fff; }
.btn-ghost { background: transparent; }
.btn-danger { color: var(--danger); border-color: var(--danger-line); }
.btn-danger:hover { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn-sm { padding: 4px 10px; font-size: 12px; }

/* ---------------- 表格 ---------------- */
.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--ds-blue-50);
  color: var(--ds-blue-dark);
  font-weight: 500; text-align: left;
  padding: 9px 12px; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
tbody td {
  padding: 8px 12px; border-bottom: 1px solid var(--line-2);
  white-space: nowrap; color: var(--ink);
}
tbody tr:hover { background: var(--ds-blue-50); }
tbody tr:last-child td { border-bottom: none; }
td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--ink-2); }
/* 截断从「整个格子」下移到「客户名这一行」：
   格子上有 overflow:hidden 时，名字一长就会把行内的「立即延期」按钮一起裁掉 ——
   按钮被裁掉不会报错，只是静静地不见了。 */
td.customer { font-weight: 500; max-width: 260px; }
td.customer .cname {
  display: block; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.empty { padding: 40px; text-align: center; color: var(--ink-3); font-size: 13px; }

/* Tab3 预查结果：可展开行（区域明细） */
tr.pq-row { cursor: pointer; }
td.pq-sum { max-width: 220px; overflow: hidden; text-overflow: ellipsis; color: var(--ink-2); }
tr.pq-detail-row > td {
  background: var(--ds-blue-50); color: var(--ink-2);
  font-size: 12px; line-height: 1.6;
  white-space: pre-wrap; word-break: break-word;
}

/* ---------------- 徽章 ---------------- */
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 12px; line-height: 18px; white-space: nowrap;
}
.badge.blue { background: var(--ds-blue-100); color: var(--ds-blue-dark); }
.badge.amber { background: var(--warn-bg); color: var(--warn); }
.badge.green { background: var(--ok-bg); color: var(--ok); }
.badge.red { background: var(--danger-bg); color: var(--danger); }
.badge.gray { background: var(--muted-bg); color: var(--muted); }

/* ---------------- 分页 ---------------- */
.pager { display: flex; align-items: center; gap: 10px; padding: 12px 2px 0; font-size: 13px; color: var(--ink-2); }
.pager .spacer { flex: 1; }
.pager select.page-size {
  font-family: inherit; font-size: 13px; color: var(--ink);
  padding: 4px 8px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: #fff;
}

/* ---------------- 卡片头部的操作区 ---------------- */
/* 原先写在内联 style 里，手机上一行塞不下 4 个控件却无法换行；提到类里才能上断点 */
.head-ops { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* 只放单个按钮的操作区（变化记录卡的「补推通知」）：宽窄由按钮自己定，别铺满整行 */
.head-ops.compact { flex: 0 0 auto; }
/* 监控清单卡的操作区（输入框 + 「添加监控」+「立即比对」）：三件套必须**同一行**。
   ⚠️ 这是补一个**布局算术的坑**，不是"顺手调宽"：
   `.head-ops` 是被卡头挤到第二行的 flex 项，它的宽度只等于**自身内容的 max-content**
   （实测 348px），而 `.watch-input` 的 flex-basis(200px) 大于它对 max-content 的贡献
   （≈168px）—— 少算的这三十来 px 正好让**第三个控件**换到第二行
   （用户 2026-09-25 看到的现象：立即比对跑到「添加监控」下面）。
   这种"差一点点就换行"的巧合是**算不准**的：改 placeholder、换字号、加一个按钮都会再犯。
   所以让操作区在换行后**独占并占满整行**（flex-basis:100% 必然另起一行），
   再把输入框限个宽度上限，组内三个控件就永远同行。 */
.head-ops.watch-ops { flex: 1 1 100%; }

/* ---------------- 宽度可控的表单控件 ---------------- */
/* 这三个控件原先都是内联 style 定宽（min-width: 200~260px），手机屏直接被撑爆 */
.field.grow { flex: 1 1 240px; min-width: 0; }
.field input.num-input { min-width: 90px; }
.watch-input {
  /* max-width：操作区占满整行后（见 .head-ops.watch-ops），输入框会一路拉到上千 px ——
     客户名最长也就十来个字，太宽反而不好点。手机上由 768 断点放回 100%。 */
  flex: 1 1 200px; min-width: 0; max-width: 360px;
  font-family: inherit; font-size: 13px; color: var(--ink);
  padding: 7px 10px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: #fff;
}
.watch-input:focus, .page-size:focus {
  outline: 2px solid var(--ds-blue-100); border-color: var(--ds-blue-mid);
}

/* ---------------- 提示条 ---------------- */
.notice {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 14px; border-radius: var(--radius-sm);
  font-size: 13px; margin-bottom: 14px; line-height: 1.6;
}
.notice.info { background: var(--ds-blue-50); border: 1px solid var(--brand-line); color: var(--ds-blue-dark); }
.notice.warn { background: var(--warn-bg); border: 1px solid var(--warn-line); color: var(--warn); }
.notice.ok { background: var(--ok-bg); border: 1px solid var(--ok-line); color: var(--ok); }
.notice .tag {
  flex: none; font-size: 11px; padding: 1px 7px; border-radius: 4px;
  background: var(--brand-a10); color: var(--ds-blue); margin-top: 1px;
}
.notice.warn .tag { background: var(--warm-a14); color: var(--warn); }
.notice.ok .tag { background: var(--ok-a12); color: var(--ok); }

/* ---------------- Tab3 表单 ---------------- */
/* minmax(0,1fr) 而非 1fr：1fr 的轨道最小值是 auto，会被内部的表格（mobile 下 min-width:680px）
   顶开，整页跟着横向溢出 —— 手机上最典型的一种"看不出哪错了"的溢出源。 */
.grid-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }
.grid-2 > .card { min-width: 0; }
@media (max-width: 900px) { .grid-2 { grid-template-columns: minmax(0, 1fr); } }

textarea {
  width: 100%; min-height: 180px; resize: vertical;
  font-family: inherit; font-size: 13px; line-height: 1.7;
  padding: 10px 12px; color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
textarea:focus { outline: 2px solid var(--ds-blue-100); border-color: var(--ds-blue-mid); }

.run-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.run-bar .hint { font-size: 12px; color: var(--ink-3); }

/* ---------------- Tab4 监控 ---------------- */
.watch-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 12px; }
.watch-card {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 14px; background: #fff;
}
.watch-card .name { font-weight: 500; font-size: 14px; margin-bottom: 2px; }
.watch-card .note { font-size: 12px; color: var(--ink-3); margin-bottom: 10px; }
.watch-card .nums { display: flex; gap: 18px; margin-bottom: 10px; }
.watch-num { flex: 1 1 0; min-width: 0; }
.watch-num .label {
  font-size: 11px; color: var(--ink-3); letter-spacing: .3px;
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
}
.watch-num .val { font-size: 18px; font-weight: 600; color: var(--ds-blue); line-height: 1.3; }
.watch-num .delta { font-size: 11px; margin-left: 4px; }
.watch-num .sub { font-size: 11px; color: var(--ink-3); margin-top: 1px; white-space: nowrap; }
/* 零命中时源站给的那句解释比一个光秃秃的 0 长得多，不能 nowrap 撑出去 */
.watch-num .sub.empty-note { white-space: normal; line-height: 1.35; }
.delta.up { color: var(--danger); }
.delta.down { color: var(--ok); }
.spark { display: flex; align-items: flex-end; gap: 2px; height: 24px; margin-bottom: 6px; }
/* 迷你折线：历史柱用亮蓝的浅版、最新一根用亮蓝原色（纯图形，不承载文字） */
.spark i { display: block; width: 8px; background: var(--accent-soft); border-radius: 2px 2px 0 0; }
.spark i:last-child { background: var(--accent); }

/* 三个数字：跟进中 / 已完成 / 已取消 —— 都在监控口径内，统一加底突出 */
.watch-num .metrics { display: flex; flex-wrap: wrap; gap: 4px 8px; margin-top: 2px; }
.watch-num .m {
  font-size: 11px; color: var(--ink-2); white-space: nowrap;
  display: inline-flex; align-items: baseline; gap: 3px;
}
.watch-num .m b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.watch-num .m.focus {
  color: var(--ds-blue-dark); background: var(--ds-blue-50);
  border: 1px solid var(--ds-blue-100); border-radius: 4px; padding: 1px 5px;
}
.watch-num .m.focus b { color: var(--ds-blue); }
.watch-num .m .delta { margin-left: 0; font-size: 10px; }

/* 口径标签：三个口径的数字不可比，必须一眼看出数字是哪来的 */
.scope-tag {
  font-size: 10px; padding: 0 5px; border-radius: 3px; line-height: 15px;
  border: 1px solid transparent; white-space: nowrap;
}
.scope-tag.client { color: var(--ds-blue); background: var(--ds-blue-100); border-color: var(--brand-line); }
.scope-tag.prequery { color: var(--pre); background: var(--pre-bg); border-color: var(--pre-line); }
.scope-tag.list { color: var(--warn); background: var(--warn-bg); border-color: var(--warm-line); }
.scope-tag.stale { color: var(--muted); background: var(--muted-bg); border-color: var(--gray-line); }
.src-link { font-size: 11px; margin-top: 4px; }
.src-link a { color: var(--ds-blue); text-decoration: none; }
.src-link a:hover { text-decoration: underline; }

.watch-card .probe-at { font-size: 11px; color: var(--ink-3); margin-bottom: 8px; }
.watch-card .watch-err {
  font-size: 11px; color: var(--warn); background: var(--warn-bg);
  border-radius: 4px; padding: 4px 6px; margin-bottom: 8px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.watch-card .ops { display: flex; gap: 8px; }
.disabled-row { opacity: .55; }

.log-list { font-size: 13px; }
.log-item {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-bottom: 1px solid var(--line-2);
}
.log-item:last-child { border-bottom: none; }
.log-item .time { color: var(--ink-3); font-size: 12px; font-variant-numeric: tabular-nums; flex: none; width: 132px; }
.log-item .sub { font-size: 11px; color: var(--ink-3); }
.log-item .who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log-item .sent-state { flex: none; font-size: 12px; }

/* 变化明细：把"哪个数字动了"直接摆出来，而不是只给一个总数差 */
.log-item .chg-list { flex: 1.4; min-width: 0; display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; }
.log-item .chg {
  font-size: 12px; color: var(--ink-2); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.log-item .chg.focus {
  color: var(--ds-blue-dark); background: var(--ds-blue-50);
  border: 1px solid var(--ds-blue-100); border-radius: 4px; padding: 1px 6px;
}
.log-item .chg strong { color: var(--ink); }
/* 只动了已退役字段的记录（例如只涨了「有冲突」）：如实说明，但不做强调 */
.log-item .chg.muted { color: var(--ink-3); }
.log-item .chg-total { font-size: 11px; color: var(--ink-3); white-space: nowrap; }

/* 进度条 */
.bar { height: 6px; background: var(--line-2); border-radius: 3px; overflow: hidden; margin: 10px 0; }
/* 全站唯一用到渐变的地方：深蓝 → 亮蓝，方向即进度方向 */
.bar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--brand), var(--accent)); width: 0; transition: width .2s; }

.spin {
  display: inline-block; width: 12px; height: 12px;
  border: 2px solid var(--ds-blue-100); border-top-color: var(--ds-blue);
  border-radius: 50%; animation: spin .7s linear infinite; vertical-align: -2px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------- 站点标记（冲突 / 大客户 / LC case） ---------------- */
/* 这三个在原页面是图标，纯文字表格里极容易漏看，所以做成紧凑徽章 */
.tag-mini {
  display: inline-block; margin-left: 4px; padding: 0 6px;
  font-size: 11px; line-height: 17px; border-radius: 4px;
  background: var(--muted-bg); color: var(--muted);
  white-space: nowrap; vertical-align: 1px;
}
.tag-mini.red { background: var(--danger-bg); color: var(--danger); }
.tag-mini.amber { background: var(--warn-bg); color: var(--warn); }
.tag-mini.green { background: var(--ok-bg); color: var(--ok); }
.tag-mini.blue { background: var(--ds-blue-100); color: var(--ds-blue-dark); }
.tag-mini.gray { background: var(--muted-bg); color: var(--muted); }

td.products { max-width: 260px; white-space: normal; }
td.products .tag-mini { margin-left: 0; margin-right: 4px; margin-bottom: 2px; }
td.customer { max-width: 280px; }
td.customer .proj { font-size: 11px; color: var(--ink-3); font-weight: 400; }

/* 「立即延期」—— 超时日期进入窗口（距今 7~22 天）时出现，点开直达源站报备详情页 */
.btn-extend {
  display: inline-block; margin-top: 3px;
  padding: 1px 7px; border-radius: 4px;
  font-size: 11px; line-height: 17px; font-weight: 400; white-space: nowrap;
  color: var(--warn); background: var(--warn-bg); border: 1px solid var(--warn-line);
  text-decoration: none;
}
.btn-extend:hover { background: var(--warm-hover); border-color: var(--warm); }
td.msg { max-width: 340px; overflow: hidden; text-overflow: ellipsis; color: var(--ink-2); font-size: 12px; }

.badge.teal { background: var(--teal-bg); color: var(--teal); }

code {
  background: var(--ds-blue-50); padding: 0 4px; border-radius: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
}

/* ---------------- 管理员设置 ---------------- */
/* 管理员入口靠右，与四个业务标签拉开距离，降低误点概率 */
.tab-admin { margin-left: auto; color: var(--ink-3); }

.cred-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); margin-bottom: 8px; background: #fff;
}
.cred-main { flex: 1; min-width: 0; }
.cred-name { font-size: 13px; font-weight: 500; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cred-sub { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.cred-sub code { font-size: 11px; }

.cred-form {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--line);
}

/* 管理主体顶部工具条：修改口令 / 退出管理。
   刻意放在 adminBody 内部 —— 原先「退出管理」挂在闸门卡片里，而闸门卡片在验证通过后
   会被整块隐藏，导致那个按钮**永远看不见**。 */
.admin-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.admin-bar .tag-mini { margin-left: 0; }

/* 口令重置表单：三格栅格，窄屏自动堆叠（沿用 .field 的纵向 label+input 结构） */
.pw-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px; align-items: end;
}
.pw-form input { min-width: 0; width: 100%; }

.settings-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px; align-items: end;
}
.settings-grid .field.wide { grid-column: 1 / -1; }
.settings-grid .field input { width: 100%; min-width: 0; }

.empty-box {
  padding: 24px; text-align: center; color: var(--ink-3); font-size: 13px;
  border: 1px dashed var(--line); border-radius: var(--radius-sm);
}

/* ---------------- Tab5 新增登记 ---------------- */
/* 区域下拉：宽度跟随内容但给个上限，长区域名不许把卡片头挤到第二行 */
.al-select {
  font-family: inherit; font-size: 13px; color: var(--ink);
  padding: 5px 9px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: #fff; max-width: 200px;
}
.al-meta { font-size: 12px; color: var(--ink-2); line-height: 1.8; margin-bottom: 10px; }
.al-meta strong { color: var(--ds-blue-dark); }
.al-bad { color: var(--danger); }
.al-warn { color: var(--warn); }
.al-warn:not([hidden]) { margin: 4px 0 2px; font-size: 13px; line-height: 1.6; }
.al-loading { color: var(--ink-3); }
.al-sid { display: block; font-size: 11px; color: var(--ink-3); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.al-num { text-align: right; }
/* 数字列表头与数据单元格同侧对齐 —— 表头左对齐、数字右对齐时宽列上看着像错位 */
thead th.num { text-align: right; }
.al-dash { color: var(--ink-3); }
/* 选中行加左侧色条：只靠按钮文案变化，滚到中间时看不出选的是哪一行 */
tr.al-picked > td { background: var(--ds-blue-50); }
tr.al-picked > td:first-child { box-shadow: inset 3px 0 0 var(--ds-blue); }

.al-form { font-size: 13px; }
.al-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 4px 22px; }
.al-col { min-width: 0; }
/* 标签左对齐、控件右列 —— 与源页面 form-horizontal 的观感一致，且窄屏下能整块折行 */
.al-field { display: grid; grid-template-columns: 108px minmax(0, 1fr); align-items: center; gap: 6px 10px; margin-bottom: 9px; }
.al-label { font-size: 12px; color: var(--ink-2); text-align: right; }
.al-req { color: var(--warm); margin-left: 2px; }
.al-input {
  font-family: inherit; font-size: 13px; color: var(--ink);
  padding: 5px 8px; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: #fff; width: 100%; min-width: 0;
}
.al-input:focus { outline: 2px solid var(--ds-blue-100); border-color: var(--ds-blue-mid); }
textarea.al-textarea { min-height: 68px; resize: vertical; line-height: 1.6; }
.al-field-file { align-items: start; }
.al-field-file .al-label { padding-top: 6px; }
.al-nc { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.al-file-input { font-size: 12px; max-width: 230px; }
.al-nc-msg { font-size: 12px; color: var(--ink-3); }
.al-nc-ok { margin-top: 6px; font-size: 12px; color: var(--ok); }

/* 购买产品：源站是 col-md-4 的网格（一行 3 个），这里用 auto-fill 保持"多而密"的观感 */
.al-block { margin-top: 14px; border-top: 1px solid var(--line-2); padding-top: 12px; }
.al-block-head { font-size: 13px; font-weight: 600; color: var(--ds-blue); margin-bottom: 10px; }
.al-prod-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 8px 16px; }
.al-prod { display: grid; grid-template-columns: minmax(0, 1fr) 68px; align-items: center; gap: 8px; min-width: 0; }
.al-prod-name { font-size: 12px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-prod-num { text-align: right; }
.al-hint { font-size: 12px; color: var(--ink-3); line-height: 1.7; margin-top: 10px; }

/* 「登记信息」卡右下角的操作行：预查/提交结论在左、提交按钮在右 */
.al-actions {
  display: flex; align-items: flex-start; justify-content: flex-end; gap: 16px;
  margin-top: 14px; border-top: 1px solid var(--line-2); padding-top: 12px;
}
.al-actions .al-status { flex: 1 1 auto; }
.al-actions .btn { flex: none; }
.al-status { font-size: 13px; line-height: 1.7; color: var(--ink-2); word-break: break-word; }
.al-status.ok { color: var(--ok); }
.al-status.warn { color: var(--warn); }
.al-status.bad { color: var(--danger); }
.al-pre-regions { font-size: 12px; color: var(--ink-2); margin-top: 4px; }

/* ==================================================================
   移动端适配
   ------------------------------------------------------------------
   要解决的问题（都是"桌面能看、手机不能用"的典型）：
     · 顶栏一行塞 4 个控件 → 整页被撑宽，出现横向滚动
     · 监控卡片 minmax(360px) 在 375px 屏上放不下；两个系统并排挤成一团
     · 9 列表格被压成豆腐块，看不清也点不中
     · 变化记录/凭证行是横向 flex，窄屏被挤成一条线
     · 输入框 font-size < 16px 时，iOS 聚焦会自动放大整页（很难受）
     · 触控目标太小（< 40px）点不准
   断点：
     ≤ 900px  仅折两栏网格（见上文 .grid-2）
     ≤ 768px  手机 / 竖屏平板：进入完整移动布局
     ≤ 480px  小屏手机：再压一档留白与字号
   ================================================================== */

/* 触摸设备上没有"悬停"，hover 态会粘住不散 —— 换成 active 反馈 */
@media (hover: none) {
  .btn:hover { border-color: var(--line); color: var(--ink); }
  .btn-primary:hover { background: var(--ds-blue); border-color: var(--ds-blue); color: #fff; }
  .tab:hover { color: var(--ink-2); }
  .tab.is-active:hover { color: var(--ds-blue); }
  tbody tr:hover { background: transparent; }
}

@media (max-width: 768px) {
  /* —— 顶栏：品牌一行、操作一行 —— */
  .topbar {
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 12px;
    padding-top: calc(10px + env(safe-area-inset-top, 0px));
  }
  .brand { flex: 1 1 100%; gap: 10px; }
  .logo { width: 34px; height: 34px; border-radius: 8px; font-size: 12px; }
  .brand h1 { font-size: 15px; }
  .brand .sub { font-size: 11px; line-height: 1.4; }
  .topbar-right { flex: 1 1 100%; justify-content: space-between; gap: 8px; min-width: 0; }
  /* 状态条是唯一可压缩的：min-width:0 + 省略号，别让它把顶栏顶出屏幕 */
  .collect-status { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .user-chip { flex: none; }
  .user-via { display: none; } /* 认证方式说明（· 本地开发通道）在手机上没地方放 */

  /* —— 报备概览：两列。
        它**不进**下面的粘顶规则 —— 让概览随页面滚走、只留标签栏粘住，
        否则手机上首屏会被固定住的概览吃掉一半高度。 —— */
  .overview { padding: 10px 12px 12px; }
  .ov-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .ov-card { padding: 8px 10px; }

  /* —— 标签栏：粘顶 + 横向滑动，翻长表格时不用滚回页首切页 —— */
  .tabs {
    position: sticky; top: 0; z-index: 30;
    padding: 0 4px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { padding: 11px 12px; font-size: 13px; }
  .tab-admin { margin-left: 8px; }

  main { padding: 14px 12px calc(36px + env(safe-area-inset-bottom, 0px)); }
  .card { padding: 12px; margin-bottom: 12px; }
  .card-head { gap: 8px; margin-bottom: 12px; }
  .card-title { font-size: 14px; }

  /* —— 按钮：触控目标不小于 40px，卡片头部的操作区铺满整行 —— */
  .btn { min-height: 40px; padding: 8px 14px; font-size: 14px; }
  .btn-sm { min-height: 34px; padding: 6px 12px; font-size: 13px; }
  .head-ops { width: 100%; }
  .head-ops > * { flex: 1 1 auto; }
  .head-ops .watch-input { flex: 1 1 100%; max-width: none; }
  /* 单按钮的操作区（变化记录卡的「补推通知」）不铺满整行：宽度 auto + 靠右对齐，
     否则手机上会是一条横跨全屏的按钮，看着像页面的主操作 —— 它其实只作用于这一栏的行 */
  .head-ops.compact { width: auto; margin-left: auto; }
  .head-ops.compact > * { flex: 0 0 auto; }

  /* —— 概览卡片：两列 —— */
  .stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 12px; }
  .stat { padding: 10px 12px; }
  .stat .v { font-size: 20px; }
  .stat .v.small { font-size: 16px; }

  /* —— 筛选栏：两列网格，输入框铺满 —— */
  .filters { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; align-items: end; }
  .filters .field { min-width: 0; }
  .filters .field input[type="text"] { min-width: 0; width: 100%; }
  .filters .spacer { display: none; } /* 网格里它只会白占一格 */

  /* —— 表单控件：16px 可阻止 iOS 聚焦时把整页放大 —— */
  .field select, .field input, textarea,
  .watch-input, .page-size { font-size: 16px; min-width: 0; }
  .field select, .field input, .watch-input { width: 100%; }
  .field input.num-input { width: auto; min-width: 80px; }
  textarea { min-height: 140px; }
  .run-bar { gap: 8px; }
  .run-bar .field { flex: 1 1 calc(50% - 4px); }
  .run-bar > .btn { flex: 1 1 auto; }
  .run-bar .hint { flex: 1 1 100%; }

  /* —— 表格：整体横向滚动，别把多列挤成豆腐块 —— */
  .table-wrap { -webkit-overflow-scrolling: touch; }
  .table-wrap table { min-width: 680px; }
  thead th { padding: 9px 10px; }
  tbody td { padding: 9px 10px; }
  td.customer { max-width: 200px; }
  td.pq-sum { max-width: 160px; }
  /* 展开的区域明细在手机上会换很多行，给个上限免得把页面撑太长 */
  tr.pq-detail-row > td { max-height: 240px; overflow: auto; }
  .table-wrap::-webkit-scrollbar { height: 6px; }
  .table-wrap::-webkit-scrollbar-thumb { background: var(--gray-thumb); border-radius: 3px; }

  .pager { flex-wrap: wrap; gap: 8px; }

  /* —— 监控卡片：单列；两个系统由"并排"改"上下"，中间加虚线分隔 —— */
  .watch-list { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .watch-card { padding: 12px; }
  .watch-card .nums { flex-direction: column; gap: 10px; margin-bottom: 8px; }
  .watch-num + .watch-num { border-top: 1px dashed var(--line); padding-top: 10px; }
  .watch-num .val { font-size: 20px; }
  .watch-num .m { font-size: 12px; }
  .watch-card .watch-err { white-space: normal; word-break: break-word; }
  .watch-card .ops .btn { flex: 1; }

  /* —— 变化记录：时间 / 客户 / 明细 / 状态各自成行 —— */
  .log-item { flex-wrap: wrap; gap: 4px 8px; padding: 10px 4px; }
  .log-item .time { flex: 1 1 100%; width: auto; }
  .log-item .who { flex: 1 1 100%; white-space: normal; }
  .log-item .chg-list { flex: 1 1 100%; }
  .log-item .sent-state { flex: none; }

  /* —— 管理员页 —— */
  .cred-row { flex-wrap: wrap; }
  .cred-main { flex: 1 1 100%; }
  .cred-form { gap: 8px; }
  .cred-form .field { flex: 1 1 calc(50% - 4px); }
  .settings-grid { grid-template-columns: minmax(0, 1fr); }
  .empty { padding: 28px 12px; }

  /* —— Tab5 新增登记 ——
     两栏表单在手机上必须折成一栏（源站自己也是这么做的），
     标签也从"左标签右控件"改成"上下堆叠" —— 108px 的标签列在 375px 屏上
     会把控件压到 200px 出头，长一点的地址/邮箱就看不见了。 */
  .al-cols { grid-template-columns: minmax(0, 1fr); }
  .al-field { grid-template-columns: minmax(0, 1fr); }
  .al-label { text-align: left; }
  /* iOS 上 font-size < 16px 的输入框聚焦会放大整页 —— 表单页尤其致命 */
  .al-input, .al-select { font-size: 16px; }
  .al-prod-grid { grid-template-columns: minmax(0, 1fr); }
  .al-field-file .al-label { padding-top: 0; }
}

@media (max-width: 480px) {
  .brand .sub { display: none; } /* 小屏给操作区让位 */
  .card { padding: 10px; }
  .tab { padding: 10px; }
  .stat .v { font-size: 18px; }
  .watch-num .val { font-size: 18px; }
  textarea { min-height: 120px; }
  .filters { grid-template-columns: minmax(0, 1fr); }
  /* 概览：一行一卡。320px 下两列会把条形压到看不见 */
  .ov-grid { grid-template-columns: minmax(0, 1fr); }
  /* 产品网格：小屏一格一行，68px 的数量框再收一点 */
  .al-prod { grid-template-columns: minmax(0, 1fr) 56px; }
  .al-file-input { max-width: 100%; }
}

