/* ==========================================================================
   达播工作台 — pages.css
   数据看板 / 登录 / 回调
   ========================================================================== */

/* ============================ 数据看板 ============================ */

.dashboard-overview {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  /* ⭐ 2026-09-25（第二轮）：标题（首页）与描述（达人排期与业绩概览）都已隐藏
     （隐藏规则在 layout.css 的 `.page-title / .dashboard-overview__title / .dashboard-overview__date`），
     本容器成了**空盒**，但原来那对 margin（上 20 + 下 16）仍在占 36px 空白
     （乐哥：「看板首页……留白太多」的实测构成之一）。
     ⇒ 空盒时不占位：用 `:has()` 判"里面有没有**非 hidden 类**的其它元素"。
     ⚠️ 不写死 `display:none` / `margin:0`：将来哪天把标题或描述恢复，或往里加别的元素，
        这条自动失效 ⇒ 盒子照常复活，不需要回来改 CSS。 */
  margin-top: var(--space-5);
  margin-bottom: var(--space-4);
}
.dashboard-overview:not(:has(> * > :not(.dashboard-overview__title):not(.dashboard-overview__date))) {
  margin: 0;
}

.dashboard-overview__title {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--font-letter-spacing-display);
}

.dashboard-overview__date {
  margin-top: var(--space-1);
  font-size: var(--font-size-sm);
  color: var(--color-muted);
}

.kpi-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

/* 3 张 KPI 卡（经营总览）：必须恒为 3 列，否则复用默认 4 列会让右侧空出一格
   （2026-09-12 乐哥反馈「右边留白」的根因即此）。
   ⚠️ 用 .kpi-row.kpi-row--three 双类提高权重：页面下方还有一组 `.kpi-row` 的媒体查询
      （1400px→4列 / 1100px→3列 / ≤1099px→2列），它们的权重相同但位置更靠后，
      只写单类会被它们翻掉（实测：单类写法下网格仍是 4 列、留白照旧）。
      双类写法与文件顺序无关，稳。 */
.kpi-row.kpi-row--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (min-width: 1100px) and (max-width: 1399px) {
  .kpi-row.kpi-row--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 1099px) {
  .kpi-row.kpi-row--three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 699px) {
  .kpi-row.kpi-row--three { grid-template-columns: minmax(0, 1fr); }
}

/* 经营总览三卡：作为页面结论，用更足的间距与下方明细拉开层级。
   （字号不用改：.kpi-card__value 本身已是 --font-size-3xl=30px。）
   预估态不再整卡套黄框（那会让重点指标看着像警告卡），见下方 .kpi-card--estimate 覆盖。 */
.kpi-row--overview { margin-bottom: var(--space-5); gap: var(--space-5); }
.kpi-row--overview .kpi-card { padding: var(--space-5) var(--space-5) var(--space-4); }

/* 5 张 KPI 卡（达人经营）：默认 4 列会变成 4+1、右侧空三格 —— 必须显式给 5 列；
   窄屏依次退到 3 / 2 / 1 列（与 --three 的断点保持同一套节奏）。
   ⚠️ 不复用 .kpi-row--six：那套规则的 nth-child 跨列是按「6 张 + 特定列数」推的，
      5 张套进去会在 1100~1399 区间留白（2026-09-13 复核其媒体查询时发现）。 */
.kpi-row.kpi-row--five { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 1399px) {
  .kpi-row.kpi-row--five { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 899px) {
  .kpi-row.kpi-row--five { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 599px) {
  .kpi-row.kpi-row--five { grid-template-columns: minmax(0, 1fr); }
}

/* 6 张 KPI 卡（经营看板）：宽屏下让「利润 / 利润率」各跨 2 列，把第二行铺满。
   不做的话 4+2 会在右侧留出两格空白（2026-09-11 扫图发现）。 */
.kpi-row--six > :nth-child(5),
.kpi-row--six > :nth-child(6) { grid-column: span 2; }

@media (min-width: 1100px) and (max-width: 1399px) {
  /* 3 列时：第 6 张跨 2 列，第二行 1 + 2 刚好铺满 */
  .kpi-row--six > :nth-child(5) { grid-column: span 1; }
  .kpi-row--six > :nth-child(6) { grid-column: span 2; }
}
@media (max-width: 1099px) {
  .kpi-row--six > :nth-child(5),
  .kpi-row--six > :nth-child(6) { grid-column: span 1; }
}

/* 经营看板 · 成本大类行内标记（「公共」等）。
   必须不可折行 —— 否则窄列下「公共」会被拆成「公 / 共」两行（2026-09-11 扫图发现）。 */
.an-tag {
  display: inline-block;
  white-space: nowrap;
  font-size: var(--font-size-xs);
  line-height: 18px;
  padding: 0 6px;
  margin-left: 4px;
  color: var(--color-muted);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-sm);
  vertical-align: 1px;
}

/* 「预估」标记（经营看板：T+35 口径下含未满 35 天订单时）——
   橙色 = 提示「这个数还会变」，与红色的错误态区分开。
   同时给整张 KPI 卡换橙色描边，扫一眼就知道这一屏不是最终结算数。
   ⚠️ 选择器必须写成 .kpi-card.kpi-card--estimate（双类）：
      .kpi-card 的单类定义在本文件**更后面**（第 79 行起），单类写法同优先级下会被它覆盖，
      描边看起来像没生效（实测踩过：算出 rgb(237,239,241) 默认灰）。 */
.an-tag--warn {
  color: var(--color-warning-text);
  background: var(--color-warning-bg);
  border-color: var(--color-warning);
}
.kpi-card.kpi-card--estimate,
.kpi-card.kpi-card--estimate:hover {
  border-color: var(--color-warning);
}
/* 经营总览的预估态：不套黄框（重点指标不该看着像警告），改用标签上的小 pill 提示口径。
   乐哥 2026-09-12：三张 KPI 卡是页面结论，视觉上要「亮」而不是「告警」。 */
.kpi-row--overview .kpi-card--estimate,
.kpi-row--overview .kpi-card--estimate:hover { border-color: var(--color-border-soft); }

/* ==========================================================================
   （已删除）盈亏公式条 .an-formula 系列 —— 2026-09-13 清理死 CSS
   背景：这一组共 9 条规则（.an-formula / __item / __item--result / __item--in /
   __item--out / __op / --empty 等）是「盈亏板块」的公式条样式。
   该板块在 2026-09-13 的「达人维度整合」中**已整体搬到「达人经营」页**，
   而公式条的渲染代码同时被移除 → 全项目（public/js + index.html）**零引用**，
   属确认死 CSS（grep 双向核对过）。留着会让「这组类到底谁在用」反复产生误判，
   故整组删除。**若将来要用，从 git 历史取回即可。**
   ========================================================================== */

/* 7 张 KPI 卡：宽屏 4 列，中等屏 3 列，窄屏 2 列 */
@media (min-width: 1400px) {
  .kpi-row { grid-template-columns: repeat(4, 1fr); }
}
@media (min-width: 1100px) and (max-width: 1399px) {
  .kpi-row { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 1099px) {
  .kpi-row { grid-template-columns: repeat(2, 1fr); }
}

.kpi-card {
  /* ⭐ 2026-09-25：加 position:relative —— 供右上角的口径 ⓘ（.th-note.is-corner）锚定。
     为什么放这里而不是给 ⓘ 加 `position:absolute` 就完事：绝对定位必须有定位祖先，
     否则会跑到视口/最近的定位祖先上（那就不在卡片里了）。 */
  position: relative;
  min-width: 0;
  padding: var(--space-5);
  background: var(--color-surface);
  /* ⭐⭐ 2026-09-26 全站卡片划界收敛为「纯线条」（乐哥拍板：按建议做第 3 条）。
     ▸ 改前：`border: 1px solid var(--color-border-soft)` + `box-shadow: var(--shadow-sm)`，
       注释写"卡片在近白底上微微浮起"—— 但 KPI 卡住在 **#FCFCFC 的白大卡内部**（不是灰底上），
       白底上阴影没有可压暗的空间（实测最深 Δ11、3px 内归零，而 1px 线条 Δ18），
       投影等于多一层看不见的"雾"，只留下"两种卡片两种划界"的不一致。
     ▸ 改后：边框统一 `--color-frame-border`（与 .card / .chart-card 同源），无阴影。
     详见 .chart-card 处的完整说明。 */
  border: 1px solid var(--color-frame-border);
  border-radius: var(--radius-lg);
  box-shadow: none;
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
    transform var(--motion-fast) var(--motion-ease-standard);
}

.kpi-card:hover {
  border-color: var(--color-accent);
  /* ⭐ 2026-09-26：hover 阴影（原 `--shadow-md`）一并去掉 —— 白底上"升一档阴影"看不见，
     等于空转；可点的反馈由**描边变主色 + 上移 1px** 承担，比阴影更明确也更克制。 */
  transform: translateY(-1px);
}

/* 强调卡片（如「成本总金额」总览项）：主色浅底 + 主色边框，突出关键数字 */
.kpi-card--accent {
  background: var(--color-primary-50);
  border-color: var(--color-primary-200);
}
.kpi-card--accent .kpi-card__label { color: var(--color-accent-text); }

/* KPI 卡「语义柔底」（乐哥 2026-09-15 起用于工作台首页）
   动机：首页四张卡原本全白底，只能靠读文字分辨指标性质；用底色区分后可一眼分辨
        （蓝=成交 / 绿=净成交 / 橙=排期 / 紫=达人规模）。
   实现：把 `dashboard.js` 里**原本就有的 tone 语义**（原先只染右上角图标）延伸到卡片底色 ——
        不新增概念，只是让已有色调换个位置呈现。
   ⚠️ 为什么做成**修饰类**而不是直接改 `.kpi-card` 的 background：
      `.kpi-card` 被 **14 个页面共用**（见 grep kpi-card public/js/pages），
      直接改 = 全站换底，属结构性改动。加类 → 只有显式加类的页面生效，其余页零影响。
   ⚠️ 类名 `--tone-*` 与 JS 侧 `UI.kpiTones()` 的键一一对应（blue/teal/orange/green/purple）；
      写错键名不会报错、只会**静默退回白底**，故只允许用这 5 个键。
   ⚠️ `border-color: transparent` 是本设计的组成部分：柔底卡片再描一圈边会显脏。
      悬停高亮仍生效 —— 但**靠的不是书写顺序**：`.kpi-card:hover`（特异性 0,2,0）
      高于 `.kpi-card--tone-*`（0,1,0），故它实际写在本段**之前**也能覆盖回来。
      （别按"后面覆盖前面"的直觉判断，本项目已多次踩特异性陷阱，见上方 estimate 段注释。） */
.kpi-card--tone-blue   { background: var(--color-tone-blue-bg);   border-color: transparent; box-shadow: none; }
.kpi-card--tone-teal   { background: var(--color-tone-teal-bg);   border-color: transparent; box-shadow: none; }
.kpi-card--tone-orange { background: var(--color-tone-orange-bg); border-color: transparent; box-shadow: none; }
.kpi-card--tone-green  { background: var(--color-tone-green-bg);  border-color: transparent; box-shadow: none; }
.kpi-card--tone-purple { background: var(--color-tone-purple-bg); border-color: transparent; box-shadow: none; }

/* ⭐ 2026-09-25 Gashora 像素级试妆（乐哥拍板「像素级模仿」）：
   看板 KPI 卡从**柔底整块**改为**白底 + 彩色图标块**（参考图 Gashora 的 KPI 卡形态：
   白卡 + 左上彩色方块图标 + 黑大数字 + 灰标签）。
   ⚠️ 这推翻了 2026-09-19 定的「柔底区分指标」—— 是乐哥看参考图后的新方向，特此前注。
   ⚠️ 只作用于**看板首页**（#kpi-row 作用域）：其他页的柔底卡（经营看板/达人经营等）
      未在本次试妆范围内逐一核对，先不动，待乐哥看过看板效果后再决定是否全站统一。
   图标块颜色改由 `.kpi-card__icon--{tone}` 承载（上方定义），彩色从"整卡"收缩为"图标块"。 */
#kpi-row .kpi-card--tone-blue,
#kpi-row .kpi-card--tone-teal,
#kpi-row .kpi-card--tone-orange,
#kpi-row .kpi-card--tone-green,
#kpi-row .kpi-card--tone-purple {
  background: var(--color-surface);
  /* ⭐ 2026-09-26：描边色统一到 `--color-frame-border`、阴影去掉（全站纯线条收敛）。
     这两条必须跟着显式写 —— 否则一旦 `.kpi-card` 将来变回带阴影，
     看板首页这 5 张会与其它页不一致（这里的覆盖本就以"显式声明"为前提）。 */
  border-color: var(--color-frame-border);
  box-shadow: none;
}

/* 经营总览行（.kpi-row--overview）里的柔底卡：描边必须也是透明
   ⚠️ 为什么单独写一条、且要写成「.kpi-row--overview .kpi-card.kpi-card--tone-x」三重选择器：
      该行有 `.kpi-row--overview .kpi-card--estimate { border-color: var(--color-border-soft) }`
      —— 它是 **0,2,0**，而上面的 `.kpi-card--tone-*` 只有 **0,1,0** → **会被它盖掉**，
      结果就是「柔底上又描了一圈灰边」，正是本设计要避免的「脏」。
      所以这里必须凑到 **0,3,0**（多带一个 `.kpi-card`）才能稳定压过它 ——
      **不要**简化成 `.kpi-row--overview .kpi-card--tone-x`（那是 0,2,0，与它同权重，
      只能靠书写顺序分胜负，属于本项目已反复踩过的隐性依赖）。
   ⚠️ 另一个边界：`--result` 卡片（利润来源链条）**不在这里管** —— 它是 `.an-src-card`，
      不是 `.kpi-card`，走自己那套规则。 */
.kpi-row--overview .kpi-card.kpi-card--tone-blue,
.kpi-row--overview .kpi-card.kpi-card--tone-teal,
.kpi-row--overview .kpi-card.kpi-card--tone-orange,
.kpi-row--overview .kpi-card.kpi-card--tone-green,
.kpi-row--overview .kpi-card.kpi-card--tone-purple { border-color: transparent; }

/* 售后分析三页（售后总览 / 达人售后 / 商品售后）的柔底卡：同样要保证描边透明。
   ⚠️ 先说清这条的**真实作用**（实测过，别误以为它是"新加透明"）：
      `.kpi-card--tone-*`（第 185 行起）**自身已经写了 `border-color: transparent`**，
      而 `.kpi-card`（147 行）的基础边框与它**同为 (0,1,0)** → 目前是**靠书写顺序**让 tone 胜出。
      本组 (0,3,0) 规则的价值是**去掉这个顺序依赖**：将来谁把 `.kpi-card` 挪到 tone 类之后、
      或在该行加一条 (0,2,0) 的边框规则（`--overview` 行就有这种情况，见上方 191 行注释），
      柔底卡都不会突然长出灰边。
   ⚠️ 行类名必须是 `--grid6` / `--grid5`（**不是** `--overview`）——
      售后三页用的是这两个行类，选择器对不上就完全不生效。 */
.kpi-row--grid6 .kpi-card.kpi-card--tone-blue,
.kpi-row--grid6 .kpi-card.kpi-card--tone-teal,
.kpi-row--grid6 .kpi-card.kpi-card--tone-orange,
.kpi-row--grid6 .kpi-card.kpi-card--tone-green,
.kpi-row--grid6 .kpi-card.kpi-card--tone-purple,
.kpi-row--grid5 .kpi-card.kpi-card--tone-blue,
.kpi-row--grid5 .kpi-card.kpi-card--tone-teal,
.kpi-row--grid5 .kpi-card.kpi-card--tone-orange,
.kpi-row--grid5 .kpi-card.kpi-card--tone-green,
.kpi-row--grid5 .kpi-card.kpi-card--tone-purple { border-color: transparent; }

.kpi-card__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}
/* ⭐ 2026-09-25 Gashora 试妆：看板 KPI 卡 top 区 = 图标块 + 标签（横排、垂直居中）。
   参考图是「彩色方块图标在左、灰色标签紧跟其右」。 */
#kpi-row .kpi-card__top {
  justify-content: flex-start;
  gap: 12px;
  margin-bottom: var(--space-4);
}
#kpi-row .kpi-card__label {
  font-size: 13px;
  color: var(--color-muted);
  font-weight: var(--font-weight-medium);
}

.kpi-card__label {
  font-size: var(--font-size-sm);
  color: var(--color-muted);
}

/* ⭐ 2026-09-25 Gashora 像素级试妆：KPI 图标改为**彩色实心圆角方块 + 白图标**
   （参考图特征：~44px 彩色方块，紫/橙/粉/蓝）。原浅蓝底+蓝字太"轻"，压不住大数字。 */
.kpi-card__icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--color-primary-50);
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.kpi-card__icon svg { width: 22px; height: 22px; }
.kpi-card__icon--blue   { background: var(--kpi-icon-blue); }
.kpi-card__icon--green  { background: var(--kpi-icon-green); }
.kpi-card__icon--orange { background: var(--kpi-icon-orange); }
.kpi-card__icon--purple { background: var(--kpi-icon-purple); }
.kpi-card__icon--pink   { background: var(--kpi-icon-pink); }

/* ⚠️ 盈亏正负色必须在这里再声明一次：`.kpi-card__value`（本文件、特异性 0,1,0）
   与 table.css 的 `.num-pos`（0,1,0）**特异性相同**，而本文件**后加载** → 会把正负色覆盖掉。
   2026-09-13 实测：KPI 卡上的「月最终盈亏 -¥13.3万」显示为深灰，而表格里同值已是绿色 —— 两处不一致。
   故用 0,2,0 显式覆盖（全站约定：盈利=红、亏损=绿）。 */
.kpi-card__value.num-pos,
.card__value.num-pos { color: var(--color-danger); }
.kpi-card__value.num-neg,
.card__value.num-neg { color: var(--color-success); }

.kpi-card__value {
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-feature-settings: "tnum", "lnum";
  font-variant-numeric: tabular-nums;
  /* ⭐ 2026-09-25 Gashora 像素级试妆：品牌蓝 → **近黑**（参考图 KPI 数字是黑色超大粗体，
     "信息聚焦、分得清重点"的核心：大数字必须是视觉锚点，不能被颜色分散）。
     ⚠️ 盈亏语义色仍由上方 0,2,0 的 `.num-pos`/`.num-neg` 覆盖（红/绿不受影响）。 */
  color: var(--color-fg);
  line-height: var(--font-line-height-tight);
}

.kpi-card__delta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: var(--space-2);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
}

.kpi-card__delta.is-up { color: var(--color-success); }
.kpi-card__delta.is-down { color: var(--color-danger); }
/* 一张卡上有**多个环比**时的容器（如总盈亏卡：盈亏涨跌幅 + 利润率百分点差）。
   ⚠️ 必须 column 排：`.kpi-card__delta` 自身是 inline-flex，两个直接相邻会挤在同一行
      连成一串（"↑721.8% 较上月 ↑5.08 个百分点 利润率较上月"），读不出这是两组数据
      —— 乐哥 2026-09-16 报障的「文字内容挤压」就是这处。
   容器内单个 delta 不再各自留上边距（间距统一由 gap 控制）。 */
.kpi-card__moms { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; margin-top: var(--space-2); }
.kpi-card__moms > .kpi-card__delta { margin-top: 0; }
/* 「无环比可算」态（全部月份 / 预估口径 / 上月无数据）：灰色说明，不用涨跌色 ——
   否则灰色的「不与上月对比」配上红绿色会被误读成"持平"。 */
.kpi-card__delta.is-none { color: var(--color-muted); font-weight: var(--font-weight-regular); }
/* 「较上月」后缀小字：与涨跌数字同排，弱化以免抢数字的注意力 */
.kpi-card__delta-label { color: var(--color-muted); font-weight: var(--font-weight-regular); }

.kpi-card__sub {
  margin-top: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  line-height: 1.5;
}

/* 净实收卡按渠道（微信小店/抖音）拆分 */
.kpi-src {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--font-size-xs);
  color: var(--color-fg);
}
.kpi-src + .kpi-src { margin-top: 3px; }
.kpi-src__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* 成本构成横向条形 */
.cost-bar-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.cost-bar-item__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
  font-size: var(--font-size-sm);
}
.cost-bar-item__head span:first-child { color: var(--color-fg); }
.cost-bar-item__head .num { color: var(--color-fg-2); font-variant-numeric: tabular-nums; }
.cost-bar-item__track {
  height: 8px;
  border-radius: 999px;
  background: var(--color-gray-100);
  overflow: hidden;
}
.cost-bar-item__fill {
  height: 100%;
  border-radius: 999px;
  background: var(--color-accent-text);
  transition: width var(--motion-base) var(--motion-ease-standard);
}

.chart-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.chart-grid--bottom {
  grid-template-columns: 1fr 1fr;
}

.chart-card {
  min-width: 0;
  overflow: hidden;
  background: var(--color-surface);
  /* ⭐⭐ 2026-09-26 全站卡片划界收敛为「纯线条」（乐哥拍板：按建议做第 3 条）。
     ▸ 改前：`border: 1px solid var(--color-border-soft)` + `box-shadow: var(--shadow-sm)` ——
       这是 2026-09-25「UI 美化第二刀」加的（当时注释写"与 .card / .kpi-card 一致"），
       但同一次改版里 Gashora 骨架又把手 `.card` / `.module-card` 改成了「只描边、不投影」
       ⇒ 那句注释过期了，**同一套卡片留下了两种划界语言**（实测全站 20 个 chart-card
         带阴影、28 个 card 不带），这正是乐哥反复觉得"乱"的一处来源。
     ▸ 改后：边框色统一到 `--color-frame-border`（= 白大卡内模块卡的描边色，与 .card 同源），
       阴影去掉。**理由不是审美偏好，是实测**：逐像素扫边缘，白底 #FCFCFC 上阴影最深只有 Δ11
       且 3px 内衰减到 0，而 1px 线条本身 Δ18 —— 白底上阴影没有可压暗的空间、纯属重复。
     ⚠️ 阴影只留给「浮在灰底上的浮层」（.page-stack 本身 / 弹窗 / 下拉 / 抽屉），
        以及二级 tab 的选中白块（它在**灰色托盘**上，那里阴影有效，见 pages-influencer.css）。 */
  border: 1px solid var(--color-frame-border);
  border-radius: var(--radius-lg);
  box-shadow: none;
}

.chart-card__body {
  padding: var(--space-5);
  min-height: 240px;
  position: relative;
  min-width: 0;
}

.chart-card__body canvas {
  max-width: 100%;
  display: block;
}

.chart-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 240px;
  color: var(--color-muted);
  font-size: var(--font-size-sm);
}

/* 排行条形 */
.ranking-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.ranking-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.ranking-item__rank {
  width: 20px;
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-meta);
  text-align: center;
  font-feature-settings: "tnum", "lnum";
  font-variant-numeric: tabular-nums;
}

.ranking-item__name {
  width: 96px;
  flex-shrink: 0;
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ranking-item__bar {
  flex: 1;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  overflow: hidden;
}

.ranking-item__bar-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
}

.ranking-item__value {
  width: 88px;
  flex-shrink: 0;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  font-feature-settings: "tnum", "lnum";
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* 快捷入口（卡片内横排 4 个） */
.quick-links {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}

.quick-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  /* ⭐ 2026-09-25 UI 美化第二刀：由 `transparent` 改为可见的浅描边。
     为什么：页面底压到近白后，`.quick-link` 的 surface-2 底与页面底几乎同色，
     静态下来是一块"没有边界、软塌塌"的灰块（乐哥要调的「那些框」）。
     给一条极浅描边后入口卡有了明确轮廓，hover 再加深一档（见下）。 */
  border: 1px solid var(--color-border-soft);
  color: var(--color-fg-2);
  cursor: pointer;
  text-align: center;
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
    background var(--motion-fast) var(--motion-ease-standard),
    box-shadow var(--motion-fast) var(--motion-ease-standard);
}

.quick-link:hover {
  border-color: var(--color-border);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

/* 高亮快捷入口（排期日历）：主色浅底 + 主色图标，克制不抢眼 */
.quick-link--highlight {
  background: var(--color-primary-50);
}
.quick-link--highlight .quick-link__icon {
  background: var(--color-accent);
  color: var(--color-accent-on);
  box-shadow: none;
}
.quick-link--highlight .quick-link__label {
  color: var(--color-fg);
}
.quick-link--highlight .quick-link__desc {
  color: var(--color-muted);
}
.quick-link--highlight:hover {
  border-color: var(--color-accent);
  background: var(--color-primary-50);
}

.quick-link__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  /* ⭐ 2026-09-25 UI 美化第二刀：由 `--color-surface-2`（与卡片底**同色** ⇒ 图标盒形同消失、
     只剩一个蓝图标飘着）改为品牌浅蓝底。图标盒现在有明确形状，与高亮入口形成同族层次：
       普通入口 = 浅蓝盒 + 蓝图标 ／ 高亮入口 = 实心蓝盒 + 白图标（见上方 --highlight）。 */
  background: var(--color-primary-50);
  color: var(--color-accent-text);
}

.quick-link__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.quick-link__label {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}

.quick-link__desc {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

/* 移动端：快捷入口降为两列；高亮的「排期日历」占满整行更醒目 */
@media (max-width: 768px) {
  .quick-links {
    grid-template-columns: repeat(2, 1fr);
  }
  .quick-link--highlight {
    grid-column: 1 / -1;
  }
}

/* 首页底部备案号（合规：主页底部中央位置，滚动到底部才出现，非悬浮） */
.site-footer {
  margin-top: var(--space-8);
  padding: var(--space-6) 0;
  text-align: center;
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}
.site-footer a {
  color: var(--color-muted);
  text-decoration: none;
}
.site-footer a:hover {
  color: var(--color-fg-2);
}

/* ============================ 登录（split-screen，参考 Arco Pro） ============================ */

/* ⚠️⚠️ 2026-09-22 视觉重做（乐哥确认原型后落地）—— 左侧 hero 的三条关键变化：
   ① **渐变色 → 背景图**：原来的 `linear-gradient + 装饰环 + 装饰方块` 全部删除。
      装饰环/方块是为纯色渐变做点缀的，压在照片上只会变成"漂浮的白色方块"；
      而且历史上就出过事故（见下方被删掉的 .login-hero__shape 注释里记的
      "装饰横条压住副标题"那次）。现在背景图本身就是视觉主体，不需要再叠装饰。
   ② **居中 → 左对齐**：原型构图是"左侧文字 + 右侧卡片"，
      左对齐让文字与右侧卡片形成明确的视觉分区。
   ③ **文字色 白 → 深**：背景图统一重调成**浅底高调**（三张图文字区亮度 p50 分别是
      门户 0.43 / 财务 0.50 / 达播 0.37）⇒ 深字压浅底最坏 5.7:1；
      白字在浅底上反而会糊（门户原图深蓝底白字只有 2.4:1，这正是乐哥报的"看不清"）。
      ⚠️ 这条是**算出来的，不是审美选择**：重调工具与实测数据见
      `.render-check/login-bg-retune.js`（幂等，每次从原图重算）+ `verify-login-readability.js`
      （多视口 × p05/p95 最坏情况，117 条全过）。 */
.login-root {
  position: relative;
  min-height: 100vh;
  display: flex;
  /* ⚠️ 兜底底色：小屏下 hero 被隐藏（见文件末尾媒体查询），
     此时页面上没有任何背景图 ⇒ 少了这行会变成"白底 + 白卡片"看不出边界。 */
  background: var(--color-bg);
}

/* 左侧品牌视觉区（背景图铺满整屏 + 品牌文字）
   ⚠️⚠️ 用 `position: fixed` 而不是 `absolute` —— 这是"整屏背景图"的一个必踩点：
      项目在 `base.css` 里给 html 设了 **`scrollbar-gutter: stable`**
       （乐哥 2026-09-18 要求："同页面切 tab 也会向右移动然后闪一下" ⇒ 永久预留滚动条槽，
         让各页面的 `width:100%` 容器宽度恒定、不抖）。
       而 `absolute` 的包含块是 `.login-root`，它的宽度**已经扣掉了那 15px 槽**
       ⇒ 背景图只铺到 `视口宽 − 15px`，最右 15px 露出 `.login-root` 的底色
       ⇒ 白底页面看不出来，但**登录页铺满照片时就是一条明显的浅色竖条**（实测抓到）。
       ⇒ 改用 `fixed`：包含块是**视口**（含那 15px）⇒ 背景真正铺满，且**不动全局机制**
         （不去改 `scrollbar-gutter`，否则其他页面的抖动问题会回来）。
     ⚠️ `fixed` 后 hero 脱离文档流 ⇒ 高度靠 `.login-root` 的 `min-height: 100vh` 撑住，
        卡片由 `.login-form-wrap` 自己居中 —— 与改前表现一致（登录页本来就不滚动）。
     ⚠️ 层叠：hero 无 z-index（auto），`.login-form-wrap` 是 `z-index: 2` ⇒ 卡片仍在上层、可点。
     ⚠️ 小屏 `display: none`（见文件末尾媒体查询）对 fixed 同样有效。 */
.login-hero {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  /* ⚠️⚠️ `width: 100vw` 是**必须的**，不能用 `right: 0`（等价 inset:0）：
      项目在 base.css 给 html 设了 **`scrollbar-gutter: stable`**
      （乐哥 2026-09-18 要求修"同页面切 tab 也会向右移动然后闪一下" ⇒ 永久预留滚动条槽，
       让各页面的 `width:100%` 容器宽度恒定不抖）⇒ **包含块宽度已被扣掉那 15px**。
      实测（1440 视口，three-domain 登录页）：
        `inset: 0`          → hero 宽 1425，最右 15px 露出 body 底色 rgb(245,246,248)
                              ⇒ 白底页面看不出来，但**登录页铺满照片时是一条明显的浅色竖条**
        `width: 100vw`      → hero 宽 **1440**，整屏铺满 ✓
      ⚠️ `100vw` **不会**造成横向滚动条：fixed 元素不撑开文档宽度（实测横向溢出仍为 −15px）；
         真有垂直滚动条时它盖在 hero 之上，视觉上也正常。
      ⚠️ 不去改 `scrollbar-gutter` 本身 —— 那是为**所有**页面修的抖动问题，
         在登录页单独关掉会把那个老毛病带回来（且登录页小屏内容变高时真会出现滚动条）。
      ⚠️ 小屏 `display: none`（见文件末尾媒体查询）对 fixed 同样有效。 */
  width: 100vw;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* 左对齐 + 左侧缩进 9.5%（与原型一致；太贴边在宽屏上显得局促） */
  align-items: flex-start;
  padding: 0 6% 0 9.5%;
  overflow: hidden;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  /* 文字色（深字压浅底）—— 可达性结论见上方注释 */
  --lh-ink: #0b1524;
  --lh-ink-sub: #0b1016;
}

/* 三套门面的背景图 —— ⚠️ 只在**桌面**生效（包在媒体查询里）：
   小屏下 hero 是 display:none，图就白下载了（单张约 400KB）。
   ⚠️ 用 class 规则而不是内联 style：内联 style 里的 url 会被**无条件解析**
     ⇒ 三张图（1.3MB）在首屏全部下载。CSS 规则只在匹配时才发请求 ⇒ 只下一张。
   ⚠️ 文件名与门面 key 要能对上（hub→portal / finance→fin / main→dabotai），
     这里刻意保持"物理文件名 = 内容语义"，别为了省事缩写成 a.jpg/b.jpg。 */
@media (min-width: 769px) {
  .login-root--hub .login-hero { background-image: url('/img/login/portal.jpg'); }
  /* ⚠️⚠️ 类名是 `--finance`（**不是** `--fin`）：由 `login.js` 的
     `rootClass() = 'login-root login-root--' + entryKind()` 直接拼出，
     而 `entryKind()` 对财务返回的就是 **`'finance'`**。
     我一度把这里写成 `--fin`（沿用旧名），结果财务门面**没有背景图、强调色也没生效**
     —— 页面照常渲染、控制台零报错，左侧文字落在纯白底上，肉眼看只是"这张图没出来"。
     ⇒ 探针里那条「背景图指向 finance.jpg」就是专门抓这个的（它当场红了）。
     改这里必须与 login.js 的 entryKind() 返回值**逐字对齐**。 */
  .login-root--finance .login-hero { background-image: url('/img/login/finance.jpg'); }
  .login-root--main .login-hero { background-image: url('/img/login/dabotai.jpg'); }
}

/* ⛔ 这里原本是「装饰网格 + 3 个装饰圆环 + 4 个装饰方块」共 8 个纯装饰元素
   —— 2026-09-22 换成背景图后**连同它们的 CSS 一并删除**（login.js 里的对应节点也删了）。
   为什么必须删、而不是"留着反正看不见"：
     ① 它们都是**白色半透明**（rgba(255,255,255,.06~.15)），是给**深色渐变底**做点缀的；
        现在底色是浅色照片 ⇒ 白块压在浅底上什么也看不出来，纯属白占渲染。
     ② 更要紧的是**历史教训**（教训本身保留在下面，代码已删）：
        装饰块用**绝对定位 + 百分比**、内容用**居中** ⇒ 两者在某个视口尺寸必然撞上。
        实测就发生过"装饰横条正好穿过副标题、把文字压花"，蓝底时只是隐约、
        换金色门面后非常明显 —— 这也是"装饰与内容抢位置"的典型。
     ③ 背景图本身就是视觉主体，再叠装饰只会显脏。
   将来若确实要加装饰：**别用绝对定位的百分比**（必撞），
   且必须重新过一遍 `.render-check/verify-login-readability.js`（多视口 × 最坏情况）。 */

/* 左侧品牌区（定位在 hero 之上；hero 本身是背景层，见上） */
.login-hero__brand {
  position: relative;
  /* ⚠️ 层级说明：hero 是 absolute 背景层（z-index:0），表单区是 z-index:2 ⇒
     卡片可点击。brand 在 hero 内部，给它 1 就够（不需要 2，那是更早的写法）。 */
  z-index: 1;
  text-align: left;
  /* 给右侧卡片留呼吸位；窄屏时也不让文字顶到屏幕边 */
  max-width: 520px;
}
/* 公司标识：**透明直接放在背景图上**（乐哥「用透明的，不要白底」）
   ⚠️ 这里推翻了我 2026-09-20 加的白底衬，理由是**背景条件变了**：
      当时 hero 底色是 `linear-gradient(135deg, primary-500, ...)`，
      而公司标识自身含**主色蓝 #0064E0** ⇒ 实测 vs 渐变起点只有 **1.00**
      （标识的蓝块压在蓝底上完全看不见）⇒ 那时白底衬是**必须**的。
      现在 hero 换成了**重调过的浅底照片**（三张图文字区亮度 p50 = 0.43 / 0.50 / 0.37），
      深色标识压浅底的对比度足够 ⇒ 白底衬反而成了"莫名多出来的白方块"。
      而且标识**自带深灰描边**（#595757），那正是为浅底设计的，白/青部分不会消失。
   ⚠️ 但"透明"≠"不做处理"：仍加一层**白色柔光**（drop-shadow 无偏移、大模糊），
      把标识从照片的亮暗交界上轻轻"托"起 —— 它贴图形走、不形成任何块状边界
      （与文字用 text-shadow 是同一个思路）。 */
.login-hero__logo { margin-bottom: var(--space-4); }
.login-hero__logo svg,
.login-hero__logo img {
  width: auto;
  /* ⚠️ 64px 与原型一致（原为 44px）。仍用 height 定尺而不是 64×64 + object-fit：
     后者元素框是正方形、图形在框内缩放，**看起来等比但量不出来**
     （getBoundingClientRect() 返回元素框，核验脚本会误判"被压扁"，实测误报过一次）。 */
  height: 64px;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 1px 6px rgba(255, 255, 255, 0.75));
}
.login-hero__title {
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-semibold);
  letter-spacing: .5px;
  margin-bottom: var(--space-3);
  /* ⚠️⚠️ 文字色与阴影方向 2026-09-22 **同时**改过，两者必须配对：
      原来是「白字 + 黑色 text-shadow」（配深色渐变底）；
      现在字改**深色**（配浅色照片）⇒ 黑色阴影只会把深字糊进背景花纹里。
      ⇒ 深字必须配**白色光晕**：贴着字形把字从亮暗交界上托出来，且不形成块状边界。
      ⚠️ 这条极容易漏：只改字色、不改阴影，症状与"背景太花"一模一样，换背景图也治不好
         （本项目实测：门户左侧大标题最坏只有 2.42:1，正是这个组合造成的）。 */
  color: var(--lh-ink);
  text-shadow: 0 1px 2px rgba(255, 255, 255, 0.62), 0 2px 12px rgba(255, 255, 255, 0.45);
}
.login-hero__subtitle {
  font-size: var(--font-size-base);
  color: var(--lh-ink-sub);
  font-weight: var(--font-weight-medium);
  /* ⚠️ 440px 是按新文案（"广州星盟传媒 · Star Hub 达人管理"）定的宽度；
     三个门面共用，文案最长的达播/财务都是 1~2 行。 */
  max-width: 440px;
  line-height: 1.7;
  /* 与标题同一套：深字配**白色光晕**（不是黑色阴影 —— 理由见 .login-hero__title） */
  text-shadow: 0 1px 2px rgba(255, 255, 255, 0.70), 0 2px 10px rgba(255, 255, 255, 0.50);
  /* ⚠️ 这里删掉了原来的 `opacity: 0.85` —— 它会让文字**实际**对比度低于算出来的值，
     而探针是读 CSS `color` 计算的 ⇒ 会把 opacity 算漏、报出**假绿**
     （本项目在"元素自带半透明背景"上踩过同源问题，判据已改成"先合成再算"）。 */
}
/* 板块功能标签（三个门面各自的功能点，给访客"这里面有什么"的第一印象） */
.login-hero__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.login-hero__tag {
  font-size: var(--font-size-xs);
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  /* ⭐ 白胶囊 + 深字：胶囊自身是**接近不透明的白**（.88），
     压在任何亮度的背景上都保证内部文字 ≥4.5:1（实测最坏 17:1）。
     这是"在照片上放小字"最稳的做法 —— 比给文字加阴影可靠得多。 */
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid rgba(255, 255, 255, 0.96);
  color: var(--lh-ink-sub);
}

/* 右侧表单区 —— ⚠️ 两层要点：
   ① 背景**透明**（原来是不透明的 `--color-bg`）：卡片要"浮在整屏背景图上"，
      中间夹一层不透明底会把图切断、露出一条白色竖区，与左侧照片割裂
      （原型阶段踩过：一层白色渐变蒙层把右侧整块刷白）。
   ② 自身不占背景、靠 `flex:1 + 右对齐` 把卡片推到右边（背景由绝对定位的 hero 铺满）。
   ⚠️ `z-index: 2` 必须高于 hero(0)，否则背景层会盖住卡片、**点不动按钮**。 */
.login-form-wrap {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  /* ⚠️ 右侧留白 2026-09-22 从 **6% 调到 10%**（乐哥：「那个框框再大一点，往左一点，
     太靠右了」）。依据是实测的**左右不对称**：
       1440 视口下，左侧文字只到 x=433，卡片却在 x=940 ⇒ 中间空了 **507px**，
       而右侧只留 101px ⇒ 重心明显偏右。
     改后（1440）：卡片 x≈841，右侧留白≈159 ⇒ 左右都留出了呼吸位。
     ⚠️ 往左移**不能只看 1440**：最窄的桌面视口是 1024（再窄就走移动端布局、
       hero 隐藏了）。1024 下改后卡片 x≈467、左侧文字止于 393 ⇒ 仍有 74px 间距，
       **不重叠**。今后再往左调，必须重新验这个视口的下限。 */
  padding: var(--space-6) 10% var(--space-6) var(--space-6);
  background: transparent;
}

/* 登录卡片：**毛玻璃**（乐哥：「登录框左右有玻璃效果的这种质感」）
   ⭐ 层次（三层叠加，缺一层就显平）：
     ① 半透明底色 rgba(255,255,255,.62) —— 透出背景的**色相**、但保证卡片主体可读
     ② 毛玻璃 backdrop-filter: blur(22px) saturate(180%)
     ③ 高光边框 1px 半透明白边 + 内侧亮线（玻璃顶边的"棱"）
   再加一层 ::before 斜向高光（玻璃最标志性的一笔，见下）。
   ⚠️⚠️ 必须是 `backdrop-filter` 而**不是** `filter`：
      `filter: blur()` 会把**卡片自己的内容**一起糊掉（连字都糊），
      `backdrop-filter` 只虚化卡片**背后**的东西 —— 那才是玻璃。
   ⚠️ 透明度 .62 是实测的平衡点：再透会让卡片里的小字掉到 3.84:1（不达标），
      再实就没玻璃感了。⚠️ 它与"卡片内次级文字加深"是**一组改动**，别只改一个
      （见 .login-card__footer 的注释）。 */
.login-card {
  position: relative;
  /* ⚠️ 宽度 2026-09-22 从 400px 加到 **448px**（乐哥：「那个框框再大一点」）。
     448 与原型（.lform width: 428px）同一档、略大一点；
     ⚠️ 别再往 480+ 加：卡片越宽，右侧留白被挤压得越少，
        "整体偏右"的观感会回来（那正是这次要修的问题）。
     ⚠️ `max-width: 100%` 必须保留 —— 小屏/窄屏靠它兜底，删了会横向溢出。 */
  width: 448px;
  max-width: 100%;
  background: rgba(255, 255, 255, 0.62);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.72);
  border-radius: var(--radius-xl);
  box-shadow:
    0 24px 60px rgba(10, 22, 40, 0.22),      /* 外层投影：把卡片从背景里"抬"起来 */
    0 4px 14px rgba(10, 22, 40, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.95); /* 内侧亮线：玻璃顶边的棱 */
  padding: var(--space-8) var(--space-8);
  overflow: hidden;
}
/* 斜向高光：玻璃最标志性的一笔（左上亮、右下透，边缘全透明不显"色块"）
   ⚠️ 用 ::before 而不是给卡片叠 background-image：后者会盖住毛玻璃的透感。
   ⚠️ 它必须 `pointer-events: none`，否则会挡住卡片内所有点击
      （本项目在"装饰层压住交互"上踩过同源问题）。 */
.login-card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(135deg,
    rgba(255, 255, 255, 0.50) 0%,
    rgba(255, 255, 255, 0.16) 32%,
    rgba(255, 255, 255, 0) 58%,
    rgba(255, 255, 255, 0.10) 100%);
}
/* 卡片内容必须在高光层之上 */
.login-card > * { position: relative; z-index: 1; }

.login-card__header {
  text-align: center;
  margin-bottom: var(--space-6);
}
.login-card__title {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.login-card__desc {
  margin-top: var(--space-2);
  font-size: var(--font-size-sm);
  /* ⚠️ 不再用 `var(--color-muted)`（#5D6C7B，次级色）。
     原因：卡片改成**毛玻璃、浮在照片上**之后，`--color-muted` 压在半透明卡上
     实测最坏只有 **3.75:1**（不够 4.5）—— 背景图越暗的地方越糊。
     ⇒ 登录卡内的次级文字统一加深到 #46505c，与 `.login-card__footer` 同一档。
     ⚠️ 这是"卡片底色/透明度一变，卡内所有浅色文字都要重算"的典型：
        不能只验"卡片标题"（深色、本来就够），**次级文字才是先掉队的那一批**。 */
  color: #46505c;
}

/* ── 扫码登录按钮（布局方案 A：扫码为主，乐哥 2026-09-22 选定）─────────────
   ⚠️⚠️ 按钮底色**不是**品牌标准色，理由是可以算出来的：
       品牌标准色压白字达不到 4.5:1 —— 财务金 #A8741A 是 **4.06**、
       达播橙 #C96E08 是 **3.67**、微信绿 #07C160 只有 **2.38**（白字会发糊）。
       ⇒ 按钮底统一走**同色相深档**；品牌标准色仍用于图标本体 / 焦点框 / 悬停。
       实测（白字压深档）：飞书 #1a6ae0 = 5.02 · 微信 #04803f = 5.04 ·
                          财务 #8f6111 = 5.40 · 达播 #a85505 = 5.29 */
.sso {
  width: 100%;
  height: 48px;
  margin-top: var(--space-3);
  border: none;
  border-radius: var(--radius-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  font-family: inherit;
  color: #fff;
  cursor: pointer;
  transition: filter var(--motion-fast) var(--motion-ease-standard),
    box-shadow var(--motion-fast) var(--motion-ease-standard);
}
.sso:hover { filter: brightness(1.07); box-shadow: 0 6px 18px rgba(14, 26, 43, 0.18); }
.sso:active { transform: translateY(1px); }
.sso--feishu { background: #1a6ae0; }
.sso--wechat { background: #04803f; }
/* 账号密码的"登 录"按钮：强调色**按门面走**（变量定义见各 .login-root--* 规则） */
.sso--primary { background: var(--login-brand-deep, var(--color-accent)); margin-top: var(--space-4); }

/* 品牌图标白片：官方标识是**彩色**的（飞书双色飞鸟 / 微信绿双气泡），
   不能染成白色 ⇒ 用白片托住。这也是各家官方"第三方登录按钮"的通行做法。
   ⚠️ 尺寸（26 / 17）写在这里一处，不在 icons.js 里再传一遍 —— 避免两处各写一份。 */
.brand-chip {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 7px;
  background: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}
.brand-chip img { display: block; width: 17px; height: 17px; object-fit: contain; }

/* 「或」分隔线 */
.login-or {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0 var(--space-2);
  color: #46505c;
  font-size: var(--font-size-xs);
}
.login-or::before,
.login-or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: rgba(15, 30, 50, 0.14);
}

/* 「使用账号密码登录」链接（方案 A：不占一个按钮位）
   ⚠️ 原先这里是"飞书扫码 / 账号密码"两个 tab。改链接的理由：
      tab 把两种方式摆成**同等重要**，而实际绝大多数人走扫码（内部用飞书、外部用微信），
      账号密码只是兜底 ⇒ 层级不该平齐。 */
.login-switch {
  display: block;
  width: 100%;
  padding: var(--space-3);
  background: none;
  border: none;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: #2f3a46;
  text-align: center;
  cursor: pointer;
  transition: color var(--motion-fast) var(--motion-ease-standard),
    background var(--motion-fast) var(--motion-ease-standard);
}
.login-switch:hover { color: var(--color-accent); background: rgba(255, 255, 255, 0.55); }

/* 输入框 —— ⚠️ 在玻璃卡上**不能用半透明填充**：
   卡片本身半透明、输入框再半透明 = 两层透 ⇒ 里面的字发糊。
   ⇒ 用近实白（.94）+ 清晰边框，"这是输入框"一眼可见。 */
.login-field {
  height: var(--control-height);   /* 两档制：46 → 44（标准档，登录表单） */
  display: flex;
  align-items: center;
  padding: 0 var(--space-3);
  margin-top: var(--space-3);
  border: 1px solid rgba(40, 60, 85, 0.28);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.94);
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
    box-shadow var(--motion-fast) var(--motion-ease-standard);
}
.login-field:focus-within {
  border-color: var(--color-accent);
  background: #fff;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 14%, transparent);
}
.login-field input {
  flex: 1;
  min-width: 0;
  height: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--font-size-sm);
  color: #0b1420;
}
.login-field input::placeholder { color: #5a6675; }
.login-field__eye {
  flex: none;
  display: flex;
  padding: 4px;
  border: none;
  background: none;
  color: #46505c;
  cursor: pointer;
}

.login-card__footer {
  margin-top: var(--space-5);
  font-size: var(--font-size-xs);
  /* ⚠️ 颜色从 `var(--color-meta)`（浅灰）**加深到 #46505c**：
     玻璃卡是半透明的，浅灰小字在它上面只有 3.84:1（不够 4.5）。
     ⇒ 这是与"卡片透明度 .62"配套的改动，两者必须一起看（见 .login-card 注释）。 */
  color: #46505c;
  text-align: center;
}

/* 登录页小屏：隐藏品牌区，表单占满（否则左侧 hero 被压没、480px 表单溢出）
   ⚠️⚠️ 这条与前面「背景图只在 min-width:769px 生效」是**一对**，别拆开：
      hero 在小屏是 `display:none` ⇒ 背景图就算加载了也看不见。
      把背景图规则放进 `min-width:769px` 后，小屏**完全不发图片请求**（单张约 400KB）。
      反过来若把背景图写成小屏也生效，手机上会白下载一张看不见的图。
   ⚠️ hero 隐藏后页面上没有任何背景图 ⇒ 靠 `.login-root` 的 `background: var(--color-bg)`
      兜底（见该处注释），否则会变成"白底 + 玻璃卡"看不出边界。 */
@media (max-width: 768px) {
  .login-hero { display: none; }
  .login-form-wrap { flex: 1 1 100%; }
  .login-card { width: 100%; }
}

/* ⚠️ 这里原本是「密码输入框小眼睛」（`.input--password` + `.pwd-toggle`）——
   2026-09-22 登录页改用 `.login-field` / `.login-field__eye`（近实白输入框 +
   玻璃卡上更清晰的边界）后，**这三条已无使用点**，故删除。
   ⚠️ `components.css` 里还有同名的一份定义**故意保留**：那是通用组件层，
      删它没有收益、却可能影响未 grep 到的动态用法（改动面/风险不成正比）。
      若将来确认全仓无引用，再一起清。 */

/* 回调加载页 */
.callback-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-12) 0;
}

.spinner {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  border: 3px solid var(--color-border);
  border-top-color: var(--color-accent-text);
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 1024px) {
  .kpi-row {
    grid-template-columns: repeat(2, 1fr);
  }
  .chart-grid,
  .chart-grid--bottom {
    grid-template-columns: 1fr;
  }
}

/* === 排期时间轴（详情抽屉内）=== */
.timeline {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-2) 0 var(--space-2) var(--space-1);
}
.timeline__item {
  display: flex;
  gap: var(--space-3);
  position: relative;
}
.timeline__item::before {
  content: '';
  position: absolute;
  left: 4px;
  top: 14px;
  bottom: -18px;
  width: 2px;
  background: var(--color-border-soft);
}
.timeline__item:last-child::before { display: none; }
.timeline__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-accent);
  flex-shrink: 0;
  margin-top: 4px;
  z-index: 1;
}
.timeline__body { flex: 1; min-width: 0; }
.timeline__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.timeline__time {
  font-size: var(--font-size-sm);
  color: var(--color-muted);
}
.timeline__detail {
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  margin-top: 2px;
}

/* === 排期时间轴节点卡片（表单内动态节点）=== */
.node-card {
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  margin-top: var(--space-3);
  background: var(--color-surface);
}
.node-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.node-title {
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-base);
  color: var(--color-fg);
}
.form-actions {
  position: sticky;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  background: var(--color-bg);
  border-top: 1px solid var(--color-border-soft);
}

/* ⭐⭐ 2026-09-28 「右下角玻璃浮岛」—— 表单底部保存/取消统一改浮岛
   （乐哥：「保存取消按钮……根据按钮宽度做浮岛，用玻璃效果」+「原来那个又高又窄，不协调」）
   ────────────────────────────────────────────────────────────────────────
   ▸ 尺寸：高度 64px（岛内边距 10 + 按钮 44 + 10）。
     🐞 上一版做成 98px 高（"又高又窄"）的根因：**垂直内边距叠了两层** ——
        内层 .sf-actions__inner 自带 `padding: 16px 0`、外岛又加了 10px。
        ⇒ 本版把内层 padding 归零，垂直间距只有岛这一层。
   ▸ 宽度：交给按钮撑开（按钮 min-width 88 ⇒ 两键岛 ≈224px），不再是"细高条"。
   ▸ 位置：fixed 视口右下（右 40 / 下 28）—— 滚动到哪都能点，不必滚回页尾。
   ▸ 玻璃：**克制档**（与悬浮 Tab 同一套：透光渐变 + blur20 + saturate180 + 上缘高光），
     并保留 1px 边框（--color-frame-border）——白底内容上"白边框 = 没有框"，实测过。
   ⚠️ 只作用**页面级**表单、覆盖两套系统：
      · #page-root .sf-actions     —— 排期/成本/业绩/机制比价/达人/签约公司 表单（6 页）
      · #page-root .form-actions   —— 货品/套餐/样品包 表单
        （排除 --total：报销提单的"合计条"是另一个经调研定稿的形态，不动）
   ⚠️ 弹窗（#modal-root）与抽屉（#drawer-root）挂在 body 级、**不在 #page-root 内**
      ⇒ 弹层里的操作栏保持原样（那里也不该浮动）。
   ⚠️ 岛脱离文档流后不再占位 ⇒ 由下方 `.page:has(...)` 规则补足页底空间（勿删）。 */
#page-root .sf-actions,
#page-root .form-actions:not(.form-actions--total) {
  /* ⭐⭐ 2026-09-28 二次定位（乐哥：「不应该在页面右下角吧，应该在**正文内容**的右下角，
     就是悬浮在内容上面」）——
     改前是 `position: fixed` + 视口右 40px ⇒ 在宽屏/窄内容列下**超出正文卡片右缘**（实测超 39px）。
     改 **sticky + margin-left:auto**：元素在 `.page`（内容列）的文档流内 ⇒ 水平位置天然
     跟随内容列右缘（不再靠视口坐标算），滚到哪都浮在底部 24px。垂直仍"悬浮"、水平"在内容上"。
     ▸ margin-right 24px = 内容列右 padding 减去正文容器内边距 ⇒ 与**卡片右缘对齐**（实测 1347px）。
     ▸ margin-top 16px：与上方最后一张卡留出间距（sticky 在文档流占位，紧贴会顶死）。
     ⚠️ 不再需要 `.page:has(...)` 的页底补位 —— sticky 自身占位（那条已删）。 */
  position: sticky;
  bottom: 24px;
  z-index: 90;
  width: fit-content;
  margin: var(--space-4) var(--space-6) 0 auto;
  /* ⭐⭐ 2026-09-28 与 tab 一起矮 10%（乐哥：「这个 tab 还有右下角的悬浮保存取消，
     感觉有一点点高了，要不矮个 10%」）——
     改前 10px → **12px**：岛高 = 12×2 + 按钮34 + 2(边框) = **60**（原 66，矮 9%）。
     ⚠️ 垂直**加大**但整体变矮，是因为按钮同时从 44 降到 34（见下一条）——
        两者要一起看：只降按钮不加大内边距，岛会缩到 56、按钮贴边。
     ⚠️ 水平 14px 不动（它决定"岛左右与按钮的呼吸"，与高度无关）。 */
  padding: 12px 14px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  flex-wrap: nowrap;
  border: 1px solid var(--color-frame-border);
  border-radius: var(--radius-lg);
  /* ⭐ 统一「克制玻璃」令牌（与顶栏/侧栏/Tab 同一套，见 design-tokens） */
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-lit), var(--glass-lift);
}

/* 岛内的按钮行：清掉两套旧"限宽内层"的 max-width / 垂直 padding（叠层就是它引起的） */
#page-root .sf-actions > .sf-actions__inner,
#page-root .sf-actions > .form-actions__inner,
#page-root .form-actions > .form-actions__inner {
  max-width: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  flex-wrap: nowrap;
}
/* ⭐⭐ 2026-09-28 浮岛里的按钮**降档到与 tab 胶囊同尺寸**（乐哥：
   「然后右下角保存取消的尺寸，和 tab 得一样」）——
   改前 44px（默认 .btn 档），与 tab 胶囊（34）差 10px ⇒ 同屏里两个"操作元素"体量不齐。
   改后取 `--control-height-sm`（34，= 胶囊高，也是列表工具栏那档）。
   ⚠️ 为什么用令牌而不是写 34：它与 tab 胶囊同源（都走 sm 档），
      将来若整体调档，改令牌即可两处同时跟随。
   ⚠️ 只作用于**浮岛内**（`#page-root .sf-actions / .form-actions`）——
      页头按钮（`.page-actions .btn--lg`，一级页的「新增排期」等）**不动**：
      那是"页面级主操作"，本就该比 tab 大一号；乐哥说的是"右下角保存取消"。 */
#page-root .sf-actions .btn,
#page-root .form-actions:not(.form-actions--total) .btn {
  height: var(--control-height-sm);
  min-height: var(--control-height-sm);
}

/* 岛宽由按钮宽度决定（乐哥：「根据按钮宽度」）—— 给按钮一个下限，两键即协调横条 */
#page-root .sf-actions .btn,
#page-root .form-actions:not(.form-actions--total) .btn {
  min-width: 88px;
}

/* ⭐ 2026-09-28：页底补位**已不需要**（原为 fixed 版设计）——
   改为 sticky 后浮岛自身在文档流占位（66px + margin-top 16px），页尾天然留出空间。
   ▸ 原 fixed 版的补位写法（`#page-root.page:has(...) { padding-bottom: ... }`）留作记录：
     当时踩过"后代选择器 `#page-root .page` 永不匹配"的坑（#page-root 就是 main.page 本身）。 */

/* 窄屏：岛退化为贴底全宽条（两键等分），避免悬浮岛压住表单 */
@media (max-width: 760px) {
  #page-root .sf-actions,
  #page-root .form-actions:not(.form-actions--total) {
    margin: var(--space-3) 0 0;    /* 全宽：清掉 auto/右缩进 */
    width: auto;
  }
  #page-root .sf-actions .btn,
  #page-root .form-actions:not(.form-actions--total) .btn {
    flex: 1;
  }
}

/* 手机安全区：岛底距要让开 iPhone 小黑条（替代旧的"给条加 padding-bottom"做法） */
body.is-standalone #page-root .sf-actions,
body.is-standalone #page-root .form-actions:not(.form-actions--total) {
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
}

/* ---- 套餐商品选择器 ---- */
.bundle-picker {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  max-height: 220px;
  overflow-y: auto;
  padding: var(--space-2);
}
.bundle-pick-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
}
.bundle-pick-row:hover {
  background: var(--color-surface-2);
}

/* ---- 样品发放记录：发放样品列（小字，超长折叠） ---- */
.s-prod-summary {
  font-size: var(--font-size-xs);
  color: var(--color-fg-2);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-all;
}
.bundle-pick-name {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- 商品选择器（全站统一）：PC 一行（表格式对齐），手机两行 ---- */
.product-picker {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  max-height: 220px;
  overflow-y: auto;
  padding: var(--space-2);
}
.product-pick-row {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) 130px 80px 90px auto;
  grid-template-areas: "img name code stock price action";
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.product-pick-row:hover { background: var(--color-surface-2); }
.pp-img { grid-area: img; width: 40px; height: 40px; border-radius: 6px; object-fit: cover; }
.pp-img--empty { display: inline-flex; align-items: center; justify-content: center; background: var(--color-surface-2); color: var(--color-meta); }
.pp-name {
  grid-area: name;
  display: flex; align-items: center; gap: var(--space-2);
  min-width: 0;
}
.pp-name-text {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: var(--font-weight-medium); color: var(--color-fg);
}
/* PC：meta 用 display:contents 不占位，让编码/库存/价格各自独立成列、像表格一样对齐 */
.pp-meta { display: contents; }
.pp-code { grid-area: code; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--font-size-sm); color: var(--color-fg-2); }
.pp-code--missing { color: var(--color-warning); }
.pp-stock { grid-area: stock; white-space: nowrap; font-size: var(--font-size-sm); color: var(--color-fg-2); }
.pp-price { grid-area: price; color: var(--color-price); font-weight: var(--font-weight-semibold); white-space: nowrap; }
.pp-action { grid-area: action; justify-self: end; }
.pp-picked { color: var(--color-accent); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); }

@media (max-width: 640px) {
  .product-pick-row {
    grid-template-columns: 48px minmax(0, 1fr) auto;
    grid-template-areas: "img name action" "img meta action";
    row-gap: 2px;
  }
  .pp-img { width: 48px; height: 48px; }
  /* 手机：meta 恢复占位，编码/库存/价格合并到一行 */
  .pp-meta {
    display: flex; align-items: center; gap: var(--space-3);
    grid-area: meta; min-width: 0; overflow: hidden;
    font-size: var(--font-size-sm); color: var(--color-fg-2);
  }
  .pp-code, .pp-stock, .pp-price { grid-area: auto; }
}

/* ---- 排期横向时间轴 ---- */
.sch-timeline {
  position: relative;
  padding: var(--space-4) var(--space-2);
  overflow-x: auto;
}
.sch-tl {
  position: relative;
}

/* 住宿泳道：块左右边界对齐事项节点（圆心到圆心） */
.sch-tl-acc {
  position: relative;
  height: 56px;
  margin-bottom: var(--space-3);
}
.sch-tl-acc__block {
  position: absolute;
  top: 0;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--acc, var(--color-muted)) 14%, #fff);
  border: 1px solid color-mix(in srgb, var(--acc, var(--color-muted)) 38%, #fff);
  overflow: hidden;
}
.sch-tl-acc__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--acc, var(--color-muted));
  flex-shrink: 0;
}
.sch-tl-acc__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.35;
}
.sch-tl-acc__hotel {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sch-tl-acc__sub {
  font-size: var(--font-size-xs);
  color: var(--color-fg-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 节点容器 + 虚线（虚线从首个节点圆心到末个节点圆心） */
.sch-tl-nodes-wrap {
  position: relative;
}
.sch-tl-line {
  position: absolute;
  left: 85px;   /* NODE_W/2：对齐首节点圆心 */
  right: 85px;  /* 对齐末节点圆心 */
  top: 6px;     /* dot 高 14 → 圆心 7，线高 2 → top 6 */
  height: 2px;
  background: var(--color-gray-400);
  z-index: 0;
}
.sch-timeline__nodes {
  display: flex;
  gap: 24px;
  position: relative;
}
.sch-timeline__node {
  position: relative;
  width: 170px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  z-index: 1;
}
.sch-timeline__dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.08);
  flex-shrink: 0;
}
.sch-timeline__date {
  margin-top: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-fg-2);
}
.sch-timeline__time {
  margin-top: 2px;
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}
.sch-timeline__label {
  margin-top: 2px;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.sch-timeline__card {
  margin-top: var(--space-2);
  width: 100%;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-fg);
  line-height: 1.5;
}
/* 时间轴卡片信息行：图标固定 + 标签 + 头像 + 文字，尺寸统一 */
.tl-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 20px;
}
.tl-row-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  color: var(--color-muted);
  flex-shrink: 0;
}
.tl-row-label {
  color: var(--color-muted);
  flex-shrink: 0;
}
.tl-avatar {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-primary-100);
  color: var(--color-accent-text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: var(--font-weight-medium);
  flex-shrink: 0;
  overflow: hidden;
}
.tl-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tl-row-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sch-timeline__session {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px dashed var(--color-border-soft);
}
.sch-timeline__session-title {
  font-weight: var(--font-weight-medium);
}
.sch-timeline__session-date {
  font-size: var(--font-size-xs);
  color: var(--color-fg-2);
}
.sch-timeline__session-time {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

/* ---- 列表展开行 ---- */
.row-expand {
  background: var(--color-surface-2);
}
/* 主播业绩：展开明细按「大类」着色（乐哥 2026-09-11 定：不同大类不同颜色）。
   刻意避开左侧色条——项目铁律明确「不存在左侧色条这类通用强调手法」，改用整行淡底 + 名称前小圆点。 */
.ip-sub--live {
  background: var(--color-primary-50);
}
.ip-sub--private {
  background: var(--color-warning-bg);
}
.ip-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  margin-right: 8px;
  flex: 0 0 auto;
}
.ip-dot--live {
  background: var(--color-accent);
}
.ip-dot--private {
  background: var(--color-warning-text);
}
.row-expand__inner {
  padding: var(--space-4);
}
/* 已结束的排期（列表）：整行淡化，悬停恢复，仍可查看/编辑 */
.table tr.is-past {
  opacity: 0.55;
  transition: opacity var(--motion-fast) var(--motion-ease-standard);
}
.table tr.is-past:hover {
  opacity: 1;
}
/* 订单业绩天维度展开子表：去掉内缩，让子表列与主表对齐 */
.row-expand__inner.so-expand {
  padding: var(--space-2) 0;
}

/* ---- 展开详情网格 ---- */
.detail-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}
.detail-grid > div {
  min-width: 120px;
}

/* ---- 佣金商品选择器 ---- */
.cm-picker {
  margin-top: var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  max-height: 220px;
  overflow-y: auto;
}
.cm-pick {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-size: var(--font-size-sm);
}
.cm-pick:hover {
  background: var(--color-surface-2);
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* 上 3 下 1：补偿中文字面偏上，视觉居中（总高不变） */
  padding: 3px 8px 1px;
  margin: 2px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  font-size: var(--font-size-sm);
}
/* 表格内紧凑胶囊（费用构成等）：高度对齐 .tag(22)，文字视觉居中 */
.cell-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 22px;
  padding: 1px 8px 0;
  margin: 1px 4px 1px 0;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  color: var(--color-fg-2);
  font-size: var(--font-size-xs);
  white-space: nowrap;
}
.chip__x {
  border: none;
  background: none;
  cursor: pointer;
  color: var(--color-muted);
  display: inline-flex;
  padding: 0;
}
.chip__x:hover {
  color: var(--color-danger);
}

/* ---- 费用列表：原始数据展开（横向网格排列，字段卡片式） ---- */
.raw-data {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--space-2);
}
.raw-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2);
  border-radius: var(--radius-sm);
  min-width: 0;
}
.raw-row--wide {
  grid-column: 1 / -1; /* 长字段（用途/备注等）横跨整行 */
}
.raw-row__key {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.raw-row__val {
  font-size: var(--font-size-sm);
  color: var(--color-fg);
  word-break: break-all;
  line-height: 1.5;
}

/* 订单业绩「直播·私域」维度的区块标题 */
.so-section-title {
  /* ⭐ 2026-09-28 提档：14(sm) → 16(lg) + 500 → 600 —— 与 .detail-section__title（板块标题 16/600）
     同档，区块标题不再与正文(14)同号。 */
  font-size: var(--font-size-lg);
  font-weight: 600;
  color: var(--color-fg);
  margin: 4px 0 10px;
}

/* ---- 工作台首页（对齐设计稿） ---- */
/* 卡片头右侧「查看全部」 */
.card-more {
  margin-left: auto;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-accent-text);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.card-more:hover { color: var(--color-accent-hover); }

/* 卡片标题内的「口径」小字（乐哥 2026-09-15 加）
   动机：首页两块图口径不同（达人业绩=GMV、业绩占比=GSV），而标题都不写口径 ——
        两图合计分别是 89.4 万与 80.3 万，数字都对，但并排看容易被误读成
        "同一份数据画了两遍"。把口径贴在标题旁，是成本最低的解法。
   ⚠️ 必须**弱化**（小一号 + muted + 常规字重）：它是标题的限定词、不是标题主体，
      抢视觉就本末倒置了（标题是 --font-size-lg / semibold）。 */
.card__unit {
  margin-left: var(--space-2);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-regular);
  color: var(--color-muted);
  white-space: nowrap;
}

/* 平台分布：横向条形列表 */
.plat-list { display: flex; flex-direction: column; gap: var(--space-4); }
.plat-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.plat-row__name {
  width: 56px;
  flex-shrink: 0;
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
}
.plat-row__bar {
  flex: 1;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  overflow: hidden;
}
.plat-row__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  transition: width var(--motion-base) var(--motion-ease-standard);
}
.plat-row__pct {
  width: 40px;
  flex-shrink: 0;
  text-align: right;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  font-variant-numeric: tabular-nums;
}

/* 今日排期列表 */
.today-list { display: flex; flex-direction: column; }
.today-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
}
.today-item + .today-item { margin-top: var(--space-2); }
.today-item__time {
  flex-shrink: 0;
  width: 92px;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg-2);
  font-variant-numeric: tabular-nums;
}
.today-item__name {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-sm);
  color: var(--color-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 待办提醒列表 */
.todo-list { display: flex; flex-direction: column; gap: var(--space-3); }
.todo-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.todo-item__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.todo-item__text {
  flex: 1;
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
}
.todo-item__count {
  min-width: 24px;
  height: 20px;
  padding: 0 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg-2);
  font-variant-numeric: tabular-nums;
}
.todo-empty {
  padding: var(--space-6) 0;
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--color-muted);
}

/* ---- 移动端：看板/首页紧凑适配 ---- */
@media (max-width: 768px) {
  .chart-card__body {
    padding: var(--space-3);
  }
  .today-item {
    flex-wrap: wrap;
  }
  .today-item__time {
    width: auto;
  }
}
/* 超小屏（手机竖屏）：KPI 卡单列，一眼扫一个指标 */
@media (max-width: 480px) {
  .kpi-row {
    grid-template-columns: 1fr;
    gap: var(--space-3);
  }
  .dashboard-overview {
    margin-top: var(--space-3);
    margin-bottom: var(--space-3);
  }
}

/* ============================ 业绩总表（/performance） ============================ */
/* 卡片统一留白 + Tab 栏与筛选工具栏之间留出呼吸
   （原来 .card 本身没有内边距，一级 Tab 与搜索工具栏直接贴在一起） */
.pf-card {
  padding: var(--space-6);   /* ⭐ 2026-09-28：20(--space-5) → 24(--space-6) —— 与 .module-card/.card__body
                                同标准（乐哥：内部排版乱，卡片内边距全站统一 24px） */
}
/* ⭐ 2026-09-26（乐哥：「点了私域业绩后，一级 tab 不见了」）——
   私域视图下，右上角的「导出订单 / 导入订单」会被 JS 搬进**私域面板的工具栏**里，
   并且整块页面标题行会收起（见 performance.js 的 syncPanelVisibility）。
   本条负责那个按钮组在工具栏里的形态。
   ⚠️⚠️ **刻意不给 `margin-left: auto`**：私域工具栏里的 `#pf-pv-total`（计数）
      已有一处 `margin-left:auto` 负责"把右侧内容整体推过去"。
      若这里再加一个 auto，两个 auto 会**平分**剩余空白 ⇒ 计数被挤到中间、
      与按钮之间拉开一大段（实测过：变成 `[月份][平台]  [计数]  [导出][导入]`）。
      ✅ 正解：让计数那一个 auto 负责定位，按钮组紧跟其后 ⇒
         `[月份][平台] …… [计数][导出订单][导入订单]`（右侧成一簇，间距由 gap 控制）。 */
.toolbar > .toolbar__page-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.pf-card > .tabs {
  /* ⭐ 2026-09-28 下外边距 24 → **16**（乐哥：「tab 栏和下面内容模块间距太大了，
     全局排查要一致」）—— 与全站卡片间距（16）同档。 */
  margin-bottom: var(--space-4);
  /* ⚠️ 左右缩进归零（2026-09-18 修，全站对齐普查发现）：
     外层的 `.card > .tabs` 给了左右 20px 缩进，而 `.pf-card` 自身也有 `padding: 20px`
     → **叠成 40px 双重缩进**，导致本页 TAB 文字落在 **313px**，而同页搜索框在 293px
     （差 20px，TAB 明显偏右）。全站其他 12 个有 TAB 的页面都在 293
     （它们的 TAB 在 `.toolbar` 内、且 `.table-tabs` 的缩进已在 2026-09-14 清零）。
     ⚠️ 只改本页：`.card > .tabs` 那条规则对"卡片本身无内边距"的页面是**正确**的
        （如排期表单 `card sf-card`、达人详情），不能全局去掉。
     ⚠️ 加载顺序保证了本规则生效：pages.css 在 components.css **之后**（见 index.html），
        特异性同为 (0,2,0) → 后者胜。
     ⚠️⚠️ 2026-09-26 托盘化：这里**不能写 `padding: 0`**（原值）——
        它会覆盖 `.tabs` 自己的托盘内边距（`padding: 4px`），把托盘的呼吸清掉。
        缩进一律用 margin 表达（右边距归零即可，因 `.pf-card` 自带 20px 内边距）。
     ⭐ 2026-09-26（第二轮）乐哥实报「业绩总表的二级 tab 样式不对」——
        根因是**上边距双重叠加**：`.card > .tabs` 给了 `margin: 16px 20px`，
        而本页卡片自带 `padding: 20px` ⇒ 托盘上方 = 20 + 16 = **36px**，左右却只有 20px。
        实测：托盘 top=261、卡片内容顶=245（上 36px）；托盘 bottom=301、工具栏 top=321（下 20px）
        ⇒ 托盘**明显偏下**，且与左右不齐。
        ✅ 归零 margin-top：让托盘上方只吃卡片自己的 padding（20px）——
           于是上 20 / 左右 20 / 下 20（`margin-bottom: var(--space-5)`），三边一致。 */
  margin-top: 0;
  margin-left: 0;
  margin-right: 0;
}

/* ============================ 经营看板（/analytics） ============================ */
/* 板块标题：照 7 月版参照看板的「一、二、三」编号 + 副标题（2026-09-11 重做） */
.an-sec {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-6) 0 var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border-soft);
}
.an-sec:first-of-type { margin-top: var(--space-4); }
.an-sec__no {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  align-self: center;
}
.an-sec__title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.an-sec__sub {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

/* ⭐⭐ 2026-09-28 板块卡（乐哥拍板：「经营总览那个标题，和那几个看板卡片，应该在一个白底的卡片里，
   就和本期洞察那个一样」「一个小模块一个板块，这样不是更直观吗」）
   ----------------------------------------------------------------------------
   参照物：电商罗盘（乐哥给的 4 张参考图）/ 项目管理系统原型 v2 ——
   两者都是「**一个小模块 = 一个板块 = 一个白底圆角卡，标题嵌在卡内**」。
   本站既有的样板就是「本期洞察」（.card an-ins，标题在 card__header 里）。

   实现：`cardizeSections()`（analytics.js，渲染后 DOM 包裹）把「.an-sec 标题 + 其后续兄弟」
   统一包进一个 `.an-card`。
   ⛔ **不改各 section 函数的字符串拼接** —— 那 3 个函数（overview/source/five）内部有
      联动与事件绑定（bindSourcePanel / drawSectionFive），包裹只为视觉，不该动其结构；
      DOM 移动节点**保留**监听器 ⇒ 安全。
   ============================================================================ */
.an-card {
  background: var(--color-surface);
  border: 1px solid var(--color-frame-border);
  border-radius: var(--radius-lg);
  padding: var(--space-5) var(--space-5) var(--space-4);
  margin-bottom: var(--space-5);   /* 板块之间 20px（与全站卡片间距一致）*/
}
/* 卡内标题：① 去掉原来的下边线（卡内再多一道横线会与"一个板块一张卡"的边界打架）；
   ② 上边距归零（卡片的 padding 已提供呼吸，否则标题会被推得离卡顶太远）；
   ③ 字号 16 → **20（--font-size-xl「模块标题」）** —— 乐哥：「标题 字号要大点，这样才协调」。 */
.an-card > .an-sec {
  margin: 0 0 var(--space-4);
  padding-bottom: 0;
  border-bottom: 0;
}
.an-card > .an-sec .an-sec__title { font-size: 18px; }   /* ⭐ 乐哥 2026-09-28：「文字标题字号小一点点吧」
                                                            20(--font-size-xl) → **18**：卡内标题是板块内的一级标题，
                                                            比正文(14)和卡片小标题(16)大一档即可；
                                                            20 略压过"本期洞察"的卡片标题，18 与之更协调。
                                                            （18px 在本项目已有先例：base.css 的移动端标题、
                                                             layout.css 的 A6 栏标题） */

/* 板块目录条（#an-nav）：常驻吸顶、点击平滑滚动、滚动高亮。
   定位：贴在全局顶栏（.topbar，sticky top:0，高 --header-height）下沿。
   z-index 900 < 顶栏 --z-sticky(1100)：顶栏始终压在其上，层叠不打架。
   底色取页面底 --color-bg（与 .page 留白同色）→ 视觉最轻，只靠底部细线区分内容；
   当前项用主色字 + 浅主色胶囊，未选中用 muted —— 刻意弱于一级口径 Tab（下划线式），不抢风头。
   结构拆「固定区 + 可滚动区」：overflow 只挂在滚动区，固定区（口径徽章）不被横向滚动带走，
   且固定区自身无 overflow，故徽章的下拉不会被裁剪。 */
.an-nav {
  position: sticky;
  top: var(--header-height);
  z-index: 900;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 46px;
  margin: 0 0 var(--space-4);
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border-soft);
}

/* 固定区：口径徽章，不参与横向滚动（flex:none —— 窄屏不被压扁也不被滚走） */
.an-nav__fixed {
  flex: 0 0 auto;
  position: relative;
  display: flex;
  align-items: center;
  height: 100%;
  padding-right: var(--space-2);
  border-right: 1px solid var(--color-border-soft);
}

/* 可滚动区：右侧目录项独立横滚，窄屏不换行挤成两行；滚动条隐藏，避免吸顶时高度抖动 */
.an-nav__scroll {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.an-nav__scroll::-webkit-scrollbar { display: none; }

.an-nav__item {
  flex: 0 0 auto;
  height: 30px;
  padding: 0 var(--space-3);
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-muted);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-regular);
  line-height: 30px;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard),
              color var(--motion-fast) var(--motion-ease-standard);
}
.an-nav__item:hover { background: var(--color-surface-2); color: var(--color-fg); }
.an-nav__item.is-active {
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-weight: var(--font-weight-medium);
}
.an-nav__item:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* 口径徽章：胶囊 + 主色描边 + 浅主色底，明显区别于目录项（纯文字无边框）——
   一眼看出是「状态」而非「跳转项」，也不与一级口径 Tab（下划线式）撞脸。 */
.an-caliber { position: relative; display: flex; align-items: center; height: 100%; }
.an-caliber__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: var(--control-height-sm);   /* 两档制：30 → 34（紧凑档，分析页口径切换按钮） */
  padding: 0 var(--space-3);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-pill);
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard),
              border-color var(--motion-fast) var(--motion-ease-standard);
}
.an-caliber__btn:hover { background: var(--color-surface-2); }
.an-caliber__btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.an-caliber__label { line-height: 1; }
.an-caliber__caret {
  display: inline-flex;
  align-items: center;
  color: var(--color-accent-text);
  transition: transform var(--motion-fast) var(--motion-ease-standard);
}
.an-caliber__btn[aria-expanded="true"] .an-caliber__caret { transform: rotate(180deg); }

/* 静态口径指示（2026-09-17：经营看板收起口径 tab 栏后，目录条上只剩一个口径）。
   形态与上面的「胶囊按钮」一致（同色同尺寸），但**去掉了按钮感**：
     · 无 caret（不可下拉）、无 :hover 变色（不可点）—— 点击光标会是「坏了的下拉」的错觉；
     · 用 <span> 而不是 <button> —— 屏幕阅读器不该把它报成可操作控件（可访问性，不是样式问题）。
   ⚠️ 必须自己写 `line-height:1`：胶囊按钮里有 `.an-caliber__label{line-height:1}`，
      静态版把文字直接放外层，漏了就比按钮版高 2px（目录条里一眼看出不齐）。 */
.an-caliber__static {
  display: inline-flex;
  align-items: center;
  height: var(--control-height-sm);   /* 两档制：30 → 34（紧凑档，与上方按钮同高） */
  padding: 0 var(--space-3);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-pill);
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  line-height: 1;
  white-space: nowrap;
}

/* 下拉：相对徽章绝对定位（.an-caliber 已 position:relative）→ 目录条吸顶时位置依然正确。
   z-index 950 > 目录条 900、< 顶栏 --z-sticky(1100)：不会盖住顶栏。 */
.an-caliber__menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 168px;
  padding: var(--space-1);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  z-index: 950;
}
.an-caliber__menu[hidden] { display: none; }
.an-caliber__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-fg-2);
  font-size: var(--font-size-sm);
  line-height: 1.4;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard);
}
.an-caliber__opt:hover { background: var(--color-surface-2); color: var(--color-fg); }
.an-caliber__opt:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.an-caliber__opt.is-active { color: var(--color-accent-text); font-weight: var(--font-weight-medium); }
.an-caliber__opt-check { display: inline-flex; align-items: center; color: var(--color-accent); opacity: 0; }
.an-caliber__opt.is-active .an-caliber__opt-check { opacity: 1; }

/* 滚动补偿：走 scrollIntoView / 锚点跳转时，标题不被吸顶条（顶栏 56 + 目录条 46 + 8 呼吸）盖住。
   主路径用 JS 的 window.scrollTo 精确补偿，此处作为兜底，值须与 JS 的 NAV_GAP 保持一致。 */
#an-body .an-sec { scroll-margin-top: calc(var(--header-height) + 54px); }

/* 口径提示条：info（订单归档）/ warn（快照兜底）
   ⚠️ 2026-09-14 乐哥「警示提醒方式没有微信小店的好」→ 去色块，改**细左边框**形态
   ----------------------------------------------------------------------------
   改前：`--color-accent-bg`(#EDF4FE) 或 `--color-warning-bg`(#FEF5E7) 整条铺底 +
         1px 边框 + 圆角，宽 1456px —— 一块 12 万+ 像素的色块。
   为什么要改：与 `.st-cover` 同理（见该处注释）—— 大色块与表格内状态色抢注意力，
       且满屏黄条会导致「警示疲劳」。实测微信小店全图彩色像素只占 0.72%。
   为什么**这条保留左边框**（而 `.st-cover` 是下细线）：
       内容性质不同 —— `.st-cover` 是**单行 + 需要行动**（有按钮），去掉边界仍读得通；
       本 banner 是**多行口径说明**（3~5 行），完全没有边界会被读成正文的一部分。
       左边框是"引文/注释"的通用形态，既轻量又保留"这是一段说明"的语义。
   ⚠️ 文字色改为常规色（--color-fg-2）：原来整条用 warning-text 橙字，
      在**有底色**时靠底色衬托尚可；去掉底色后整段橙字会过抢。
      现在层次是：正文灰 → `<b>` 深色加粗（口径关键值）→ `.an-warn-text` 橙字（真需注意）。
      即「说明用深色、警示用橙色」，与微信小店「正文灰 + 关键橙」同一逻辑。 */
/* 提示条 · 统一形态（2026-09-14 乐哥定稿：浅底 + 圆角 + 左侧图标，**不带关闭叉**）
   ---------------------------------------------------------------
   改前：透明底 + 左侧 3px 竖线（"引文"观感），与提成总览页的"近白底"又是两套。
   改后：与全站统一 —— 引用 --notice-* 那组规格（浅蓝底 + 8px 圆角 + 蓝图标）。
   ⚠️ 字号 12px → 14px：对齐 --font-size-sm（项目规范：中文 12px 以下发虚），
      也与 .st-rule__inner / .alert 同档，全站提示条只有一种字号。 */
.an-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--notice-gap);
  padding: var(--notice-pad-y) var(--notice-pad-x);
  margin-bottom: var(--space-4);
  border-radius: var(--notice-radius);
  border: 1px solid var(--notice-border);
  background: var(--notice-bg);
  font-size: var(--font-size-sm);
  line-height: 1.6;
  color: var(--notice-fg);
}
/* 提示条图标 · 垂直对齐（2026-09-14 乐哥：「刚刚做的警示提醒，那个图标没有上下居中」）
   ────────────────────────────────────────────────────────────────────────────
   改前：每条提示条各写一个 `margin-top`（1/2/3px 共 5 个魔数），而且**全都算得不对**：
         实测 .st-rule__inner 偏上 2.0px；.an-banner 更隐蔽 —— 它的 <span> 里 svg 是
         inline 元素，按 baseline「当字符」排版，位置由字体度量决定（恰好只差 1px 是巧合，
         换个字号就会歪）。
   机制：图标中心必须对齐**首行行盒中心**（提示条可能多行 → 不能 align-items:center，
         那会让多行时图标跑到整块中间）。
         · 有包裹容器 → 「盒子法」：容器高 = 首行行高，svg 在容器内 align-items:center。
           单行时视觉居中、多行时对齐首行，两种情况都对。
         · 裸 svg → 用可推导的 margin：⌈(行高 − 图标高) / 2⌉（16px 是提示条图标的统一尺寸）。
           用 em 表达行高 → 字号一变 margin 自动跟随，不再是手算魔数。 */
.an-banner__ico {
  flex: none;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  height: 1.6em;          /* = 本条的 line-height:1.6，即首行行高（22.4px @14px） */
  color: var(--notice-ico);
}
.an-banner b { color: var(--color-fg); font-weight: var(--font-weight-semibold); }
.an-banner--info {
  border-color: var(--notice-border);
  background: var(--notice-bg);
}
.an-banner--info .an-banner__ico { color: var(--notice-ico); }
/* 警示类**保留橙色语义**（只统一形态）——全改蓝会让"警告"失去辨识度 */
.an-banner--warn {
  border-color: var(--notice-warn-border);
  background: var(--notice-warn-bg);
}
.an-banner--warn .an-banner__ico { color: var(--notice-warn-ico); }

/* 图卡片标题行（参照看板是 13px / 次要字色 + 右侧 hint）
   ==========================================================================
   2026-09-17 乐哥统一图表标题版式（「标题字号大一点」「和上面留一点行间距」）：
     · 字号 14px → **16px**（`--font-size-lg`，与经营看板的 `.cb-sub` 同档）
     · 字重 500 → **600**（16px 用 medium 仍偏软，标题要能从正文里跳出来）
     · 左 padding 16px → **20px**（原比 `.chart-card__body` 的 20px 少 4px，
       标题左边界与图表左边界**对不齐** —— 这是实测发现的额外瑕疵）
     · bottom 0 → **12px**（原为 0，标题底边紧贴图表顶边，"挨在一起"正是如此）
     · 字色 fg-2 → **fg**（标题是主信息，不该用次级色）
   ⚠️ 本类被 3 个页面共用（analytics / aftersale / influencer-profit）→ 改一处三处生效。 */
.an-chart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-3);
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.an-chart-head__h {
  font-size: var(--font-size-xs);
  font-weight: 400;
  color: var(--color-muted);
  /* ⚠️ 不再 `nowrap`（2026-09-16 修）：这类小字常是一整句说明（如
     「数据自 2026-06 起（此前月份无订单，不补零）」），窄屏下 nowrap 会让它
     以单行 502px 的宽度**越出卡片 215px**。改成可换行 + 允许收缩。 */
  white-space: normal;
  min-width: 0;
  overflow-wrap: anywhere;
}
/* ⚠️ 已弃用（2026-09-11）：图表高度改由 JS 侧 chartCard() 传像素 + 内联 style 控制
   （之前是「tall 布尔 + 这里的 340px」隐式耦合，改一个图要同时翻 JS 和 CSS 两处）。
   保留此规则只为兜底，当前没有任何元素使用。 */
.an-chart--tall { height: 340px; }

/* 单列铺满 / 三列分类型 网格 */
.an-grid-1 { grid-template-columns: 1fr; }
.an-type-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}
@media (max-width: 1099px) {
  .an-type-grid { grid-template-columns: 1fr; }
}

/* 分类型对比卡（专场 / 私域 / 混播）：标题行加一个语义色点 + 主色标题字（乐哥 2026-09-15）
   动机：三张卡是同质并列项，需要一个「一眼分成三块」的标识。
   ⚠️ 为什么只在标题行上色、**不给整卡上色**：
      ① 数字区保持白底 → 不影响盈亏红绿的可读性。实测「混播」当前是亏的（绿字），
         绿字放到彩色底上对比度会下降，读数字更费劲；
      ② 三张卡装的是同一类指标、只是分类不同，整卡铺色的视觉分量过重。
   ⚠️ 用**具名修饰类** `--special/--private/--mixed`（由 JS 按 TYPE_META 的 key 挂上），
      不用 `:nth-child(N)` —— 卡片顺序一变，nth-child 会**静默错配颜色**且很难被发现。
   ⚠️ 色点用 `::before` 伪元素而不是新增 `<span>`：卡内容是 innerHTML 拼的，
      加一个真实节点就要同步改渲染代码，纯样式能解决就不动 DOM。
   ⚠️ 颜色一律走令牌（本文件禁止硬编码 hex），这三个正是 `UI.kpiTones()` 用的同族色，
      与 KPI 柔底同源 —— 将来换主色只需改 design-tokens 一处。 */
.an-type-card--special .an-card-title { color: var(--color-tone-blue-fg); }
.an-type-card--private .an-card-title { color: var(--color-tone-teal-fg); }
.an-type-card--mixed   .an-card-title { color: var(--color-tone-purple-fg); }
.an-type-card--special .an-card-title::before,
.an-type-card--private .an-card-title::before,
.an-type-card--mixed   .an-card-title::before {
  content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 8px; vertical-align: 1px;
}
.an-type-card--special .an-card-title::before { background: var(--color-tone-blue-fg); }
.an-type-card--private .an-card-title::before { background: var(--color-tone-teal-fg); }
.an-type-card--mixed   .an-card-title::before { background: var(--color-tone-purple-fg); }
/* 「N 个场次」副标题仍用中性灰 —— 它是量词不是分类标识，跟着主色走会喧宾夺主 */
.an-type-card--special .an-card-title__sub,
.an-type-card--private .an-card-title__sub,
.an-type-card--mixed   .an-card-title__sub { color: var(--color-muted); }

/* 分类型卡内的小统计 */
.an-card-title {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  margin-bottom: var(--space-3);
}
.an-card-title__sub {
  font-size: var(--font-size-xs);
  font-weight: 400;
  color: var(--color-muted);
  margin-left: 6px;
}
.an-stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}
.an-stat__label { font-size: var(--font-size-xs); color: var(--color-muted); }
.an-stat__value { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); margin-top: 2px; }
.an-card-foot {
  margin-top: var(--space-3);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

/* 成本大类下钻行 */
.an-cat-grid {
  display: grid;
  grid-template-columns: 150px 1fr 110px 72px;
  gap: 10px;
  align-items: center;
  font-size: var(--font-size-sm);
}
.an-cat-grid--head {
  margin-bottom: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}
.an-cat-grid--head > :nth-child(3),
.an-cat-grid--head > :nth-child(4) { text-align: right; }
.an-cat-track { background: var(--color-surface-2); border-radius: var(--radius-sm); height: 16px; }
.an-cat-fill { height: 16px; border-radius: var(--radius-sm); }
.an-cat-num { text-align: right; font-variant-numeric: tabular-nums; }
.an-cat-num--muted { color: var(--color-muted); }

/* 面包屑 */
.an-crumb { margin-bottom: var(--space-3); font-size: var(--font-size-xs); }
.an-crumb a { color: var(--color-accent-text); text-decoration: none; }
.an-crumb__sep { color: var(--color-muted); margin: 0 6px; }

.an-flex-between { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.an-link { color: var(--color-accent-text); text-decoration: none; }
.an-warn-text { color: var(--color-warning-text); }

/* ---------------- 盈亏状态标记（圆点 + 文字） ----------------
   状态标记「圆点 + 文字」：圆点取 currentColor，颜色由 JS 内联写在元素上，
   与该行「盈亏」数字用**同一套色规**（正=红 / 负=绿）。
   ⚠️ 刻意不用 .badge 上色：badge 的色是语义色（success=绿），与本系统「涨红跌绿」的数值色相反 ——
      一个绿底 badge 写「亏损」会同时发出两个相反信号，读者不知道该信哪个。

   使用方（两处，同一语义共用同一套样式，不各造一个类）：
     · 达人经营 · 明细表「状态」列 + 页头「本月有 N 位达人在亏」
     · 达人结算 · 展开区（若后续用到）
   2026-09-13：原属经营看板「三、达人盈亏」，该板块并入「达人经营」后样式与语义一并迁移。 */
.pnl-flag { display: inline-flex; align-items: center; gap: 6px; font-size: var(--font-size-xs); white-space: nowrap; }
.pnl-flag::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.pnl-flag--muted { color: var(--color-muted); }

/* 合计行底色。不复用 .so-subtotal：它只在 .table--sub / .table--zebra 下才有样式，
   挂在普通 .table 上其实是"裸"的（看着像没有合计），这里显式给一条本表专用规则。 */
.an-pnl-table tbody tr.an-pnl-total { background: var(--color-surface-2); }
.an-pnl-table tbody tr.an-pnl-total td { font-weight: var(--font-weight-medium); }

/* ---------------- 一、利润来源：公式链 + 展开面板（2026-09-12 新增） ----------------
   视觉参照乐哥给的示例图：横向链条、加粗深色描边的圆角卡片、标签在上金额在下、`=` 与 `−` 连接。
   描边用 1.5px（比常规卡片粗）+ 深色（--color-fg），这是示例图的关键特征，不是笔误；
   描边色通过 --chain-border 间接引用 token，不写裸色值。 */
:root { --chain-border: var(--color-border-soft); }   /* 乐哥 2026-09-12：原为 --color-fg（近黑），与全站卡片浅灰描边不一致，改为统一 */

.an-src-chain-wrap { overflow-x: auto; padding-bottom: 2px; }
/* ⚠️ 2026-09-16 修（乐哥报障「公式是不是漏减了项目成本」）：
   原来是 `display:flex; min-width:150px/卡 + overflow-x:auto`。
   公式在 2026-09-16 从 5 张卡增加到 7 张（总盈亏 / 收入 / 投流 / 达人佣金 /
   达人招待费用 / 提成&人力成本 / 项目成本）后，**实测视口 ≤1512px 时
   最后一张「项目成本」正好被裁出可视区**：
     视口 1512 → 溢出 86px ｜ 1440 → 158px ｜ 1366 → 232px ｜ 1280 → 318px
   而 macOS 的滚动条默认隐藏、容器也没给任何提示 → 用户完全看不出右边还有内容，
   会以为「公式里漏了这项」。**内容被裁掉等于没写**，这是必须修的可用性问题。
   → 改用 grid auto-fit：容器够宽就一行、不够就自动折行，**永不裁切**，且各卡等宽。
   实测修复后：1512/1440/1366/1280 全部完整可见（一行或两行）。 */
.an-src-chain {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: var(--space-2);
  align-items: stretch;
}
/* 每一项 = 「操作符 + 卡片」打包成一个 grid 单元。
   为什么要包：折行时保证减号**跟着卡片走**，不会出现
   「减号留在上一行末尾、卡片孤零零掉到下一行」的割裂观感。

   ── 分组配色（乐哥 2026-09-16：「用颜色区分达人侧 / 公司侧」）────────────
   双通道设计，两个信息各走一路、互不抢占：
     · 左侧色条 = **分组色** → 回答"这笔钱算谁头上"
     · 标题前色点 = **类目色** → 回答"这是哪一项"，**与成本看板同色**
   ⚠️ 为什么只能走"色条 + 色点"、不能整卡上淡底（2026-09-16 实测否决）：
      人力橙底与结果卡暖底、达人蓝底与展开态浅底，**各是同一个 token**（实测色差 0.0）
      → 整卡上底会同时"分不出结果 vs 过程"和"卡片日常就像被点开"，且点开只剩描边反馈。 */
.an-src-term { display: flex; align-items: stretch; gap: var(--space-2); min-width: 0; --grp-c: transparent; }
.an-src-term[data-grp="talent"] { --grp-c: var(--color-chart-1); }   /* 达人侧（投流 / 佣金 / 招待） */
.an-src-term[data-grp="hr"] { --grp-c: var(--color-chart-5); }       /* 提成&人力 */
/* ⚠️ 公司侧**刻意不用绿**（乐哥 2026-09-16：「绿这个色换掉」）：
   绿 --color-chart-3(#2BA471) 与「亏损绿」--color-success(#31A24C) 同色系（色差仅 21），
   而本系统红涨绿跌、**绿 = 亏** → 绿出现在成本卡上容易被读成"这一项亏了"。
   改用靛紫：与达人组的蓝明显区分，也不会与「结果卡暖橙」「展开态浅蓝」撞色。 */
.an-src-term[data-grp="project"] { --grp-c: var(--color-tone-purple-fg); }

/* 组与组之间加宽一格间距 → 5 张成本卡自然聚成 3 簇（达人侧 3 张 / 人力 1 张 / 公司 1 张）。
   只在**每组首项**加左间距，故二、三组各加一次。 */
.an-src-term[data-grp="hr"], .an-src-term[data-grp="project"] { margin-left: 12px; }

/* 「多链共用一个网格」时的强制换行点（2026-09-16 新增，达人经营页的利润构成用它）
   --------------------------------------------------------------------------
   场景：一个页面有**两条公式链**（上面算达人利润、下面算最终盈亏），它们必须
   **卡片同宽、左右对齐**，否则一眼能看出"上下两行不是一套"。
   为什么不能各用一个 `.an-src-chain`：那是 `repeat(auto-fit, …)`，而 **auto-fit 会折叠
   没有项目的空轨道** → 项目少的那条链每张卡被拉伸摊满容器。
   实测（容器均 1414px）：第一条链 5 项 → 276px/卡；第二条链 4 项 → 348px/卡，差 72px。
   → 把两条链放进**同一个** `.an-src-chain`（列宽只由容器决定），第二条链的首项用本类
     强制回到第 1 列开新行。
   ⚠️ 用"强制换行"而不是"靠正好排满"：列数随视口变化，靠数量的边界是脆的（一变就串行）；
      `grid-column: 1` 在任何列数下都成立（列数 5 → 链 1 占整行、链 2 从下一行首列起；
      列数 3 时也可用）。
   ⚠️ `grid-column: 1` 不能配合 `margin-left`（本类的组间距走 padding/无外边距），
      否则第 1 列会被外距推出网格线、与上方卡片对不齐。 */
/* ⚠️ `grid-column: 1` 时**必须把 margin-left 归零**：本类的组间距（data-grp）是 margin-left，
   而网格的第 1 列若带上外边距，卡片会被推出网格线、与上方卡片左边界错开 12px
   （肉眼就是"没对齐"）。
   ⚠️⚠️ `margin-top` **不能只加在换行那一项上**：实测那样只有它自己下移 8px，
      与**同一行的其他项差一行高**（y=788 vs 780），比不加还难看。
      必须用兄弟选择器把整行一起下移 —— `~` 覆盖它后面的所有项，
      换行后即使再折行也在同一行里保持一致。
   （只靠 grid 的 row-gap 8px 读不出"这是两个不同的算式"，两条链会糊成一整片。） */
.an-src-term--brk { grid-column: 1; margin-left: 0; }
.an-src-term--brk, .an-src-term--brk ~ .an-src-term { margin-top: var(--space-2); }
.an-src-card {
  /* 宽度由父级 grid 列宽决定；`.an-src-term` 内用 flex 撑满。
     ⚠️ 不要在这里写 min-width —— 那会重新把链条撑出容器、又变成"最后一张被裁掉"。 */
  flex: 1 1 auto; min-width: 0; display: block; text-align: left;
  /* 1px 浅灰 + 12px 圆角 —— 与 .kpi-card 完全对齐（乐哥 2026-09-12 统一；原为 1.5px 深色 + 8px） */
  background: var(--color-surface); border: 1px solid var(--chain-border);
  /* 左侧 3px 分组色条（方案 F 的"通道一"）。用 inset box-shadow 而不是 border-left：
     ① 不改变盒模型宽度 → 与 grid 列宽、折行计算无冲突（改 border 会挤掉内容宽度）
     ② 描边（border）留给 hover / 选中态用，两个通道互不覆盖
     ⚠️ 只有"成本项"用分组色；结果卡与收入卡不是成本项，--grp-c 未定义 → 回落成 --chain-border，
        视觉上等于没有色条（正是想要的：它们不属于任何费用组）。 */
  box-shadow: inset 3px 0 0 0 var(--grp-c, transparent);
  border-radius: var(--radius-lg); padding: 11px 15px;
  font-family: inherit; cursor: pointer;
  transition: border-color .12s, background .12s;
}
.an-src-card:hover { border-color: var(--color-accent); }
.an-src-card[aria-pressed="true"] { border: 2px solid var(--color-accent); background: var(--color-accent-bg); padding: 10px 14px; }
.an-src-card__n { display: block; font-size: var(--font-size-sm); color: var(--color-muted); margin-bottom: 5px; }
/* 类目色点（方案 F 的"通道二"）：回答"这是哪一项"。
   ⚠️ 色值**必须与成本看板同色**（cost-board.js 的 BIG_COLOR → --color-chart-*），
      否则同一个「投流」在公式链与成本看板是两种颜色 ——
      乐哥 2026-09-16 明确："每个费用都用固定颜色，不要不同地方不一致"。
      改动时两边要一起改（这也是 5/5 一致性断言的依据）。 */
.an-src-card[data-src="ad"] .an-src-card__n::before,
.an-src-card[data-src="commission"] .an-src-card__n::before,
.an-src-card[data-src="ent_talent"] .an-src-card__n::before,
.an-src-card[data-src="hr_all"] .an-src-card__n::before,
.an-src-card[data-src="project_all"] .an-src-card__n::before {
  content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 2px;
  margin-right: 6px; vertical-align: 1px;
}
.an-src-card[data-src="ad"] .an-src-card__n::before { background: var(--color-chart-1); }
.an-src-card[data-src="commission"] .an-src-card__n::before { background: var(--color-chart-2); }
.an-src-card[data-src="ent_talent"] .an-src-card__n::before { background: var(--color-chart-5); }
.an-src-card[data-src="hr_all"] .an-src-card__n::before { background: var(--color-chart-3); }
.an-src-card[data-src="project_all"] .an-src-card__n::before { background: var(--color-chart-4); }
.an-src-card__v {
  display: block; font-size: var(--font-size-2xl); font-weight: 500; line-height: 1.15;
  color: var(--color-fg); font-variant-numeric: tabular-nums;
}
/* ⚠️⚠️ 这里曾有一个「亏损显示成红色」的真 bug，2026-09-15 修 —— 别改回去：
   原写法是一条 `.an-src-card--result .an-src-card__v { color: var(--color-danger) }`（0,2,0），
   本意只是「结果卡默认给个突出的红色」，但它**盖过了** table.css 的 `.num-neg`（0,1,0）。
   而 JS（analytics.js）明明按盈亏正负挂了 `num-pos` / `num-neg` ——
   于是 `num-neg` 那条**永远不生效**：**亏损月的结果卡也显示红色**。
   实测（生产库快照）：2026-06 总盈亏 −¥2.0万、2026-08 为亏损，卡片却都是红色
   （`-¥2.0万` 颜色 = rgb(228,30,63) 红，应为绿）→ 会让人把亏损看成盈利，属会误导决策的那类错。
   → 正确写法：**让符号决定颜色**，按正负各写一条并凑到 0,3,0 压过基类。
      （基类 `.an-src-card__v`（0,1,0）与 `.num-pos`（0,1,0）同权重，而 **pages.css 后加载**，
        所以只写 `.num-pos` 也会被基类的 `color: var(--color-fg)` 盖掉 —— 必须显式写复合选择器。） */
.an-src-card--result .an-src-card__v.num-pos { color: var(--color-danger); }   /* 盈利 = 红 */
.an-src-card--result .an-src-card__v.num-neg { color: var(--color-success); }  /* 亏损 = 绿 */
/* 结果卡加一层暖底，和右边三组成本卡分开（乐哥 2026-09-15）
   用途：现在链条上五张卡长得一模一样，只能靠 = / − 符号判断谁是结果、谁是过程。
   为什么只用 1 个颜色、且只给结果卡：三组成本是**同质并列项**，多色只是噪音；
       而这张卡点一下会展开明细 —— 展开态是「主色描边 + 主色浅底」（见下方 aria-pressed 规则），
       若成本卡也上固定底色，常态色与选中色就会糊在一起，分不出点开了哪张。 */
.an-src-card--result { background: var(--color-tone-orange-bg); border-color: transparent; }
.an-src-card__h { display: block; font-size: var(--font-size-xs); color: var(--color-meta); margin-top: 5px; }
/* 「邀请点击」的副标题用主色蓝（2026-09-19 乐哥：「利润公式里那个点击展开要不要用蓝色字？
   不然很多人都不知道」）。
   ⚠️⚠️ 判据是**副标题在不在邀请动作**，不是**卡片能不能点** ——
      实测 7 张卡**全都可点**（含写着「本月无支出」的两张：点开下方联动画板照样切到
      该项分析、画布正常渲染）。所以用"可点/不可点"来决定颜色是分不出来的，
      而且会让「本月无支出」配着蓝字自相矛盾。
      ⇒ 只有「点击展开 / 点击看走势」这类**动作词**才变蓝；
        「本月无支出 / 口径不支持」是**陈述状态**，没有动作可做，保持灰。
   ⚠️ 颜色复用 `--color-accent-text`（= primary-500 钴蓝）—— 与全站同一套"蓝=可操作"语言：
      `.an-caliber__btn`（蓝色胶囊=可下拉）与 `.an-caliber__static`（刻意去蓝=不可点）就是这对。
      不新造颜色。
   ⚠️ 卡片本身 hover 时**边框**已变蓝（`.an-src-card:hover`），所以副标题用同色是呼应、
      不是冲突（两者一起构成"这张卡活着"的反馈）。 */
.an-src-card__h.is-clickable { color: var(--color-accent-text); font-weight: var(--font-weight-medium); }
/* 选中的卡（aria-pressed）已有蓝底蓝框，副标题若还是蓝会糊在一起 —— 保持蓝但不再加粗 */
.an-src-card[aria-pressed="true"] .an-src-card__h.is-clickable { font-weight: var(--font-weight-regular); }
/* 本月无支出的组：卡片仍展示（否则链条缺一段、看不出「这组没花钱」），但弱化处理，
   避免它和可点的卡长一样、让人白点一下却发现没东西可看 */
.an-src-card.is-zero { border-color: var(--color-border); }
.an-src-card.is-zero .an-src-card__v { color: var(--color-muted); }
.an-src-op { flex: 0 0 auto; align-self: center; font-size: var(--font-size-2xl); font-weight: 500; color: var(--color-fg); padding: 0 2px; }
.an-src-ladder {
  margin-top: var(--space-4); padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-soft);
  font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.8;
}
.an-src-ladder b { color: var(--color-fg-2); font-weight: 600; }

/* 展开面板 */
.an-src-panel__title { font-size: var(--font-size-lg); font-weight: 600; color: var(--color-fg); }
.an-src-panel__total { font-size: var(--font-size-sm); color: var(--color-muted); }
.an-src-pg { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: var(--space-4); }
.an-src-cap { font-size: var(--font-size-xs); color: var(--color-muted); margin-bottom: 6px; }
.an-chart-src--ring { height: 230px; }
.an-chart-src--line { height: 220px; }
.an-chart-src--rank { height: 320px; }
.an-src-item {
  display: grid; grid-template-columns: minmax(0, 1fr) 96px 56px; gap: var(--space-2);
  align-items: center; padding: 8px 10px;
  border: 1px solid transparent; border-radius: var(--radius-md);
}
.an-src-item[role="button"] { cursor: pointer; }
.an-src-item[role="button"]:hover { background: var(--color-surface-2); }
.an-src-item.is-on { background: var(--color-accent-bg); border-color: var(--color-primary-200); }
.an-src-item__n { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.an-src-item__v { font-size: var(--font-size-sm); color: var(--color-fg-2); text-align: right; font-variant-numeric: tabular-nums; }
.an-src-item__p { font-size: var(--font-size-xs); color: var(--color-meta); text-align: right; }
.an-src-note { margin-top: var(--space-3); line-height: 1.8; }
.an-src-note-line {
  font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.9;
  background: var(--color-surface-2); border-radius: var(--radius-md); padding: 10px 12px;
}
.an-src-block { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border-soft); }
.an-src-block__t { font-size: var(--font-size-sm); font-weight: 500; color: var(--color-fg); margin-bottom: 10px; }
.an-src-empty {
  display: flex; align-items: center; justify-content: center; min-height: 92px;
  padding: 14px 16px; border: 1px dashed var(--color-primary-200);
  border-radius: var(--radius-md); background: var(--color-surface-2);
  color: var(--color-meta); font-size: var(--font-size-xs); line-height: 1.7; text-align: center;
}

@media (max-width: 900px) {
  .an-src-pg { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   粉丝体验装（pages/fan-experience.js）
   明细的商品全名很长（含「达人专属」「规格」），必须在单元格内截断，否则表格会被撑爆
   ========================================================================== */
.fe-prod {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 达人维度看板：达人分组行（可点折叠）+ 组内排期行 */
.fe-group td {
  background: var(--color-surface-2);
  font-weight: var(--font-weight-medium);
}
.fe-child td { background: var(--color-surface); }
.fe-child td:first-child { box-shadow: inset 2px 0 0 var(--color-border); }
/* 折叠箭头与达人胶囊之间留口气，否则箭头像贴在名字上 */
.fe-caret { display: inline-flex; vertical-align: middle; margin-right: 6px; color: var(--color-muted); }
/* KPI 行与表格同处一个 card：#fe-body 里的 .kpi-row 拿不到 .card > .kpi-row 的样式，
   用这个类补上与表头左右对齐的内边距（比行内 margin 拼间距可控） */
.kpi-row.fe-board-kpi {
  padding: var(--space-4) var(--space-5) var(--space-3);
  margin-bottom: 0;
}
/* 达人维度表尾说明：与表格左对齐 */
.fe-foot-note { margin: var(--space-3) var(--space-5) var(--space-5); }

/* --------------------------------------------------------------------------
   粉丝体验装 · 导入页（pages/fan-experience-import.js）
   替代原弹窗：三步式「选排期 → 传清单 → 核对明细」
   -------------------------------------------------------------------------- */
.fei-step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.fei-step__no {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  line-height: 1;
}
.fei-step__label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
}
/* 排期选择器在页面里占满整行会太宽，收一收；下拉是文档流内的，不会被卡片裁切 */
.fei-sched-wrap { max-width: 520px; }

/* --------------------------------------------------------------------------
   导入页 · 第 3 步「按商品设置成本」
   从文件里提取去重商品，一个商品设一次单价（同商品成本相同）；
   与下方明细表拉开层次，让人一眼看到「哪里是花钱的地方」
   -------------------------------------------------------------------------- */
.fei-cost-block {
  margin: var(--space-3) 0 0;
  padding: var(--space-4);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
}
.fei-cost-block .table-wrap {
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.fei-cost-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}
.fei-cost-head .fei-step__label { display: inline-flex; align-items: center; gap: 5px; }
.fei-cost-foot {
  display: flex;
  align-items: baseline;
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-soft);
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
}
.fei-cost-foot b { color: var(--color-fg); font-size: var(--font-size-base); }
/* 商品下拉：窄一点，避免把「文件货品名」一列挤没 */
.fei-sel { width: 100%; max-width: 260px; }

/* ==========================================================================
   售后分析 · 页内下钻（达人售后：点达人名 → 整页切成该达人下钻页）
   2026-09-11 乐哥选定「页内切换视图」：像进入一个页面，但不换路由、筛选不丢。
   ========================================================================== */
.af-crumb {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.af-back {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 4px 10px 4px 6px;
  font-size: var(--font-size-sm);
  color: var(--color-accent-text);
  background: var(--color-accent-bg);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard);
}
.af-back:hover { background: var(--color-primary-100); }
.af-back:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.af-crumb__path { font-size: var(--font-size-sm); color: var(--color-muted); }
.af-crumb__path b { color: var(--color-fg); font-weight: var(--font-weight-medium); }
.af-crumb__sep { padding: 0 2px; color: var(--color-muted); }

/* ==========================================================================
   售后分析 · 核算明细清单（乐哥 2026-09-11 要求：每个指标都要能自己核算）
   把所有「分子 ÷ 分母 = 结果」连同数据来源摊开成表，主指标行浅底凸显。
   ========================================================================== */
.af-audit {
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.af-audit__head {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  margin-bottom: var(--space-3);
}
.af-audit__sub {
  font-size: var(--font-size-sm);
  font-weight: 400;
  color: var(--color-muted);
}
.af-audit__table { width: 100%; }
.af-audit__table th { font-size: var(--font-size-xs); color: var(--color-muted); font-weight: var(--font-weight-medium); }
.af-audit__name { white-space: nowrap; color: var(--color-fg-2); }
.af-audit__calc { color: var(--color-fg-2); font-variant-numeric: tabular-nums; }
.af-audit__note { margin-top: 2px; font-size: var(--font-size-xs); color: var(--color-muted); }
.af-audit__res {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--color-fg);
}
/* 主指标行（能直接与上方 KPI 卡对上）：浅底 + 名称/结果加重，一眼区分于参考项 */
.af-audit__main { background: var(--color-surface-2); }
.af-audit__main .af-audit__name { font-weight: var(--font-weight-medium); color: var(--color-fg); }
.af-audit__main .af-audit__res { font-weight: var(--font-weight-medium); }
.af-audit__foot {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-soft);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  line-height: 1.7;
}

/* 售后分析 · 表头口径标注（如「退款率 / 金额口径」两行）
   两个率口径不同，不标出来会被误以为可直接比较。 */
.af-th-sub {
  display: inline-block;
  margin-top: 1px;
  font-size: var(--font-size-xs);
  font-weight: 400;
  color: var(--color-muted);
}

/* ==========================================================================
   售后分析 · 口径定义说明（乐哥 2026-09-11 提供电商标准口径，作为页面注释依据）
   退货率＝衡量「货有没有回来」；退款率＝衡量「钱有没有出去」；两者口径不同。
   ========================================================================== */
.af-def {
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.af-def__head {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  margin-bottom: var(--space-3);
}
.af-def__sub { font-size: var(--font-size-sm); font-weight: 400; color: var(--color-muted); }
/* 两个指标上下排列，各自一块 */
.af-def__item {
  padding: var(--space-3) 0;
  border-top: 1px solid var(--color-border-soft);
}
.af-def__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
}
/* 口径徽标：单数口径 / 金额口径 —— 一眼区分两个指标的口径类型 */
.af-def__badge {
  padding: 1px 8px;
  font-size: var(--font-size-xs);
  font-weight: 400;
  border-radius: 999px;
  background: var(--color-info-bg);
  color: var(--color-info);
}
.af-def__meaning { font-size: var(--font-size-sm); font-weight: 400; color: var(--color-muted); }
/* 公式行：等宽数字、浅底，像「公式」 */
.af-def__formula {
  margin: var(--space-2) 0;
  padding: 6px 10px;
  font-size: var(--font-size-sm);
  font-variant-numeric: tabular-nums;
  background: var(--color-surface-2);
  border-radius: var(--radius-md);
  color: var(--color-fg);
}
.af-def__points {
  margin: 0 0 var(--space-2);
  padding-left: 1.2em;
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  line-height: 1.8;
}
.af-def__points li { list-style: disc; }
/* 退款率的三类来源：紧凑两列（名称 + 单数） */
.af-def__bd {
  margin: 0 0 var(--space-2);
  padding: 0;
  font-size: var(--font-size-sm);
}
.af-def__bd li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 3px 0 3px 1.2em;
  color: var(--color-fg-2);
}
.af-def__bd-label { min-width: 0; }
.af-def__bd-num { color: var(--color-fg); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 本页实测算式 —— 最关键的复核依据，浅主色底强调 */
.af-def__calc {
  margin-top: var(--space-2);
  padding: 6px 10px;
  font-size: var(--font-size-sm);
  font-variant-numeric: tabular-nums;
  background: var(--color-accent-bg);
  border-radius: var(--radius-md);
  color: var(--color-fg-2);
}
.af-def__calc b { color: var(--color-fg); }
.af-def__rel {
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-soft);
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  line-height: 1.8;
}
.af-def__warn {
  margin-top: var(--space-2);
  padding: 6px 10px;
  font-size: var(--font-size-xs);
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  border-radius: var(--radius-md);
}

/* 售后分析 · 退款率分子构成（三类来源 + 在途，互斥相加 = 退款单数） */
.af-bd {
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
.af-bd__head {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  margin-bottom: var(--space-3);
}
.af-bd__sub { font-size: var(--font-size-sm); font-weight: 400; color: var(--color-muted); }
.af-bd__idx { color: var(--color-muted); font-variant-numeric: tabular-nums; }
.af-bd__total { background: var(--color-surface-2); }
.af-bd__foot {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-soft);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

/* ==========================================================================
   售后分析 · KPI 一行 6 个（乐哥 2026-09-11）
   与 .kpi-row--six 的区别：--six 是「4 列 + 第5/6张跨2列」的 4+2 布局；
   --grid6 是**强制一行 6 等分**（售后分析三处卡片都用它）。
   窄屏逐级降列，避免字被挤爆。
   ========================================================================== */
/* 默认 3 列（窄屏安全），≥1440px 才升到一行 6 个。
   实测：6 列时每格约 186px（1440）~ 213px（1600）；「¥1,052,382.50」在 28px 字号下需 194px，
   1400px 下会溢出（实测 194>167），故 6 列模式下把数值字号降到 24px，并把换行阈值提到 1440px。 */
.kpi-row--grid6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (min-width: 1440px) {
  .kpi-row--grid6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .kpi-row--grid6 .kpi-card__value { font-size: var(--font-size-2xl); }
}
@media (max-width: 899px) { .kpi-row--grid6 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* 6 列时卡片更窄：副标题收紧行高，长数字不允许撑破卡片 */
.kpi-row--grid6 .kpi-card__sub { line-height: 1.45; }
.kpi-row--grid6 .kpi-card__value { white-space: nowrap; }

/* 售后分析 · 商品页 KPI 一行 5 个（乐哥 2026-09-12 去掉「未匹配商品行」后）
   与 --grid6 同源，列数从 6 降到 5。
   ⚠️ 5 不能被 3 整除也不能被 2 整除 → 若不管，中窄屏最后一行会留空格
   （实测：3 列时留 384px、2 列时留 376px）。
   故默认（3 列）与窄屏（2 列）都让**第 5 张跨 2 列**，保证每行都铺满：
     3 列：3 + (1+2) 铺满   ／  2 列：2 + 2 + 2 铺满
   ≥1440px 用 5 列一行铺满（每格约 224px，比 6 列的 186px 更宽松，数值字号无需下调）。 */
.kpi-row--grid5 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kpi-row--grid5 > :nth-child(5) { grid-column: span 2; }
@media (min-width: 1440px) {
  .kpi-row--grid5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .kpi-row--grid5 > :nth-child(5) { grid-column: span 1; }
}
@media (max-width: 899px) {
  .kpi-row--grid5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpi-row--grid5 > :nth-child(5) { grid-column: span 2; }
}
.kpi-row--grid5 .kpi-card__sub { line-height: 1.45; }
.kpi-row--grid5 .kpi-card__value { white-space: nowrap; }

/* ==========================================================================
   售后分析 · 口径解释入口 + 弹窗（2026-09-12 乐哥改造）

   原先「核算明细 / 退款率分子构成 / 指标口径说明」是三个各自独立的 <details> 折叠条目，
   要分三次点开才看得全（乐哥：「这种展开折叠不直观」）。
   现改为 **一个入口 → 弹窗**：弹窗内三块全部展开 + 顶部锚点跳转定位。
   ⚠️ 弹窗内刻意不再用折叠/Tab —— 折叠的毛病就是「内容被藏起来」，
      换弹窗后再套一层隐藏等于没改。
   ========================================================================== */
/* 提示条 · 统一形态 + 保留可点击（2026-09-14 乐哥定稿）
   ---------------------------------------------------------------
   本类特殊：它是个**可点击的入口**（点开看「指标口径与核算明细」）→
   统一成浅蓝底 + 同规格圆角，但**保留右侧 `>` 箭头**（那是"可展开"的指示，不能去掉）。
   改前是**白底卡**（--color-surface + 边框），正是乐哥说的"白底的"那一类。 */
.af-explain {
  display: flex;
  align-items: center;
  gap: var(--notice-gap);
  width: 100%;
  text-align: left;
  padding: var(--notice-pad-y) var(--notice-pad-x);
  margin-bottom: var(--space-4);
  background: var(--notice-bg);
  border: 1px solid var(--notice-border);
  border-radius: var(--notice-radius);
  cursor: pointer;
  font-family: inherit;
  /* ⚠️ 必须显式声明字号（2026-09-14 门禁抓出）：
     本元素是 `<button>`，只继承 font-family 时**字号仍取浏览器默认的 13.3333px**
     （不是全站的 14px）→ 实测提示条比其它页小半号，视觉不一致。
     门禁 probe-notice-standard.js 会断言 14px，不要删。 */
  font-size: var(--font-size-sm);
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
              background var(--motion-fast) var(--motion-ease-standard);
}
.af-explain:hover { border-color: var(--color-primary-300); background: var(--color-primary-100); }
.af-explain:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.af-explain__ico { display: inline-flex; color: var(--color-primary-500); flex: none; }
.af-explain__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.af-explain__t { font-size: var(--font-size-base); font-weight: var(--font-weight-medium); color: var(--color-fg); }
.af-explain__s { font-size: var(--font-size-xs); color: var(--color-muted); }
/* 右箭头：明确提示「点了会打开新东西」（比折叠三角更指向"弹出"） */
.af-explain__chev { display: inline-flex; color: var(--color-meta); flex: none; }

/* ---- 弹窗内容 ---- */
.af-ex__nav {
  position: sticky;   /* 锚点条吸顶：滚动长内容时始终能跳转 */
  top: calc(-1 * var(--space-5));
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-2) 0 var(--space-3);
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border-soft);
}
.af-ex__navbtn {
  height: var(--control-height-sm);   /* 两档制：30 → 34（紧凑档，弹窗内锚点导航） */
  padding: 0 var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-fg-2);
  font-family: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
              color var(--motion-fast) var(--motion-ease-standard),
              background var(--motion-fast) var(--motion-ease-standard);
}
.af-ex__navbtn:hover { border-color: var(--color-primary-300); color: var(--color-primary-500); }
.af-ex__navbtn.is-on {
  border-color: var(--color-primary-500);
  background: var(--color-primary-50);
  color: var(--color-primary-600);
}
.af-ex__sec + .af-ex__sec { margin-top: var(--space-5); padding-top: var(--space-5); border-top: 1px solid var(--color-border-soft); }
.af-ex__head { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-3); }
.af-ex__no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: none;
  border-radius: var(--radius-sm);
  background: var(--color-primary-500); color: #fff;
  font-size: var(--font-size-xs); font-weight: var(--font-weight-medium);
}
.af-ex__t { font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
.af-ex__h { font-size: var(--font-size-xs); color: var(--color-muted); }
.af-ex__body { min-width: 0; }

/* 口径标记（乐哥 2026-09-12）：KPI 副标题里把「金额口径 / 单数口径」做成蓝色小标记，
   与后面的算式（灰色）区分开。蓝色 = info 语义，与弹窗内 .af-def__badge 同色，全页一致。
   目的：本页退款率（看钱）与退货率（看单数）口径不同，副标题原先一片灰色、扫读会混。 */
.af-caliber {
  display: inline-block;
  padding: 0 6px;
  border-radius: var(--radius-sm);
  background: var(--color-info-bg);
  color: var(--color-info);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  line-height: 1.6;
  vertical-align: baseline;
}

/* ==========================================================================
   售后分析 · 列头口径提示（乐哥 2026-09-11）
   表头里的「金额口径 / 单数口径」文字太长 → 改成圆圈感叹号图标，悬停显示说明。
   ⚠️ 关键约束：表格外层 .table-wrap 是 overflow-x: auto，按 CSS 规范该方向的 visible
      会被计算成 auto → **tooltip 上下左右都可能被裁**。故：
      · 向右不溢出：tip 用 right:0 定位（贴着图标右缘向左展开）
      · 向下不溢出：tip 向下弹，落在表头下方的表格内容区（仍在容器高度内）
      实测（无头浏览器）确认可见后才上线；若某天表头变成最后一行元素，需改为向上弹。
   ========================================================================== */
.af-hint {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: -2px;
  margin-left: 3px;
  color: var(--color-muted);
  cursor: help;
}
.af-hint:hover,
.af-hint:focus-visible { color: var(--color-accent-text); }
.af-hint:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; border-radius: 2px; }
.af-hint__tip {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  width: max-content;
  max-width: 240px;
  padding: 6px 10px;
  font-size: var(--font-size-xs);
  font-weight: 400;
  line-height: 1.55;
  text-align: left;
  white-space: normal;
  color: var(--color-accent-on);
  background: var(--color-fg);
  border-radius: var(--radius-md);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
}
.af-hint:hover .af-hint__tip,
.af-hint:focus-visible .af-hint__tip { display: block; }

/* ==========================================================================
   售后分析 · 金额「万」展示（乐哥 2026-09-12）
   过万的金额显示为 ¥160.2万，鼠标悬停（或键盘聚焦）浮出精确值。
   复用 .af-hint 的定位策略（向下弹 + right:0 对齐）—— 表格外层是 overflow-x:auto，
   浮层必须落在容器内，否则会被裁（已实测同策略可行）。
   注意：**不加虚线下划线**（乐哥 2026-09-12：到万的那些数字下面不用加虚线，没必要）——
        数字本身就是可读的，加下划线反而让整列显得毛糙。
        「可悬停」的提示由 cursor: help（鼠标移到数字上变问号光标）承担，无静态视觉噪音。
   ========================================================================== */
.af-money {
  position: relative;
  cursor: help;
}
.af-money:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; border-radius: 2px; }
.af-money__tip {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  width: max-content;
  padding: 6px 10px;
  font-size: var(--font-size-xs);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: normal;
  white-space: nowrap;
  color: var(--color-accent-on);
  background: var(--color-fg);
  border-radius: var(--radius-md);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
}
.af-money:hover .af-money__tip,
.af-money:focus-visible .af-money__tip { display: block; }

/* ==========================================================================
   售后分析：斑马纹 · **已按乐哥要求去掉**（2026-09-25）
   --------------------------------------------------------------------------
   ▸ 历史：2026-09-12 乐哥要求给售后分析这 8 张表加斑马纹
     （「这些表格为了看的清楚，错行用很浅的背景色做区分」）；
     2026-09-15 又把它升级成**全站默认**（见 table.css，实现方式改为 `:where()` 低特异性）。
     ⇒ 本段 `.table--zebra` 从那时起就是**历史遗留**（与全站规则重复）。
   ▸ 2026-09-25 乐哥：「表格斑马纹，我觉得是不是可以不可以不要」+「我的报销页也有斑马纹」
     ⇒ 全站去掉斑马纹，本段一并清理（全站共 4 处实现，见 table.css 里的汇总清单）。

   ⚠️ 本段删除后，**特殊行的底色不受影响**（这是删之前专门核对过的）：
       `.af-audit__main`（核算清单主指标行）、`.af-bd__total`（构成合计行）、
       `.so-subtotal`（交叉表合计行）**各自有独立定义**，不依赖 `.table--zebra`：
         · `.af-audit__main` → 本文件 :2501（`.af-audit__main { background: var(--color-surface-2); }`）
         · `.af-bd__total`   → 本文件 :2641
         · `.so-subtotal`    → table.css 的 `.table--sub tbody tr.so-subtotal`（结算明细子表）
       原来那三条 `.table--zebra tbody tr.xxx` 只是"为了压过斑马纹"的**保险规则**，
       斑马纹没了，保险也就不需要了。
   ⚠️ hover 也不受影响：`.table tbody tr:hover`（table.css）本就是 (0,2,2)、优先于一切。
   ⛔ 原规则已删除（保留在此便于回溯）：
        .table--zebra tbody tr:nth-child(even) { background: var(--table-row-alt); }
        .table--zebra tbody tr.af-audit__main,
        .table--zebra tbody tr.af-bd__total,
        .table--zebra tbody tr.so-subtotal { background: var(--color-surface-2); }
        .table--zebra tbody tr:hover { background: var(--table-row-hover); }
      ⚠️ JS 里的 `table--zebra` 类名（`pages/aftersale.js` 共 9 处）**刻意保留不删** ——
         它现在不影响渲染（无对应规则），但留着"这里曾经有斑马纹"的线索；
         若以后要恢复，只需把上面 4 条取消注释即可，不必回头改 JS。
   ========================================================================== */

/* ==========================================================================
   提成页面（2026-09-12）——「我的提成」方案 1 模块卡堆叠式 +「提成总览」方案 2 侧栏主从式
   依据：docs/UI方案-提成与结算确认.md、.render-check/ui-plan-1.png / -2.png（乐哥拍板）
   铁律：容器一律 .module-card 体系（不新造模块容器）；颜色全走 CSS 变量（零裸 hex）；
        相邻卡片 24px 由 .module-card + .module-card 保证；工具栏控件 44px。
   ⚠️ 类名用 .cm-*（产品类）；对比页专用的 .cmp-* 不进产品代码。
   ========================================================================== */

/* —— 方案 1：金额头 + 类目拆解条 —— */
.cm-hero {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: var(--space-8);
  align-items: center;
}
.cm-hero__label { font-size: var(--font-size-sm); color: var(--color-muted); }
.cm-hero__value {
  margin-top: var(--space-1);
  font-size: var(--font-size-4xl);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--font-letter-spacing-display);
  line-height: var(--font-line-height-tight);
  color: var(--color-fg);
  font-variant-numeric: tabular-nums;
}
.cm-hero__sub {
  margin-top: var(--space-3);
  display: flex; flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: var(--font-size-sm);
  color: var(--color-muted);
}
.cm-cat__sub { margin-top: var(--space-1); font-size: var(--font-size-xs); color: var(--color-meta); font-variant-numeric: tabular-nums; }

/* 类目分组头（明细卡内每组一行） */
.cm-grouphead { display: flex; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-3); }
.cm-grouphead__name {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg);
}
.cm-grouphead__meta { font-size: var(--font-size-xs); color: var(--color-muted); }
.cm-grouphead__amt { margin-left: auto; font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg); font-variant-numeric: tabular-nums; }
.cm-more { margin-top: var(--space-2); display: flex; justify-content: flex-end; }
/* 窄屏专用副行（桌面隐藏）：把被折叠的「计算基数 · 点位」并进达人单元格，保证 AC-04 三项不缺 */
.cm-narrow-sub { display: none; }
/* 金额列禁止折行（窄屏下必须整串完整显示） */
.cm-table td:last-child, .cm-table th:last-child { white-space: nowrap; }

/* —— 方案 2：侧栏主从式 —— */
.cm-master {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.cm-plist { display: flex; flex-direction: column; gap: 2px; max-height: 460px; overflow-y: auto; }
.cm-plist__hd { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-3); }
.cm-plist__count { font-size: var(--font-size-xs); color: var(--color-meta); font-variant-numeric: tabular-nums; }
.cm-pitem {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; padding: var(--space-2) var(--space-3);
  border: none; border-radius: var(--radius-md);
  background: transparent; text-align: left; cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard);
}
.cm-pitem:hover { background: var(--color-surface-2); }
.cm-pitem:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.cm-pitem.is-active { background: var(--color-primary-50); }
.cm-pitem__meta { flex: 1; min-width: 0; }
.cm-pitem__name { display: block; font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--color-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-pitem__roles { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; }
.cm-pitem__amt { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg); font-variant-numeric: tabular-nums; white-space: nowrap; }

.cm-heroline { display: flex; align-items: center; gap: var(--space-3); }
.cm-heroline__meta { min-width: 0; }
.cm-heroline__name { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
.cm-heroline__sub { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: 3px; }
.cm-heroline__amt { margin-left: auto; text-align: right; }
.cm-heroline__amt-label { font-size: var(--font-size-xs); color: var(--color-muted); }
.cm-heroline__amt-val {
  font-size: var(--font-size-2xl); font-weight: var(--font-weight-semibold); color: var(--color-fg);
  font-variant-numeric: tabular-nums; letter-spacing: var(--font-letter-spacing-display);
}
.cm-catgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.cm-catbox { padding: var(--space-3); border: 1px solid var(--color-border-soft); border-radius: var(--radius-md); background: var(--color-surface-2); }
.cm-catbox__label { font-size: var(--font-size-xs); color: var(--color-muted); }
.cm-catbox__val { margin-top: var(--space-1); font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg); font-variant-numeric: tabular-nums; }
.cm-catbox__sub { margin-top: 2px; font-size: var(--font-size-xs); color: var(--color-meta); font-variant-numeric: tabular-nums; }
.cm-catbox--total { background: var(--color-accent-bg); border-color: var(--color-primary-200); }
.cm-catbox--total .cm-catbox__label { color: var(--color-accent-text); }

/* 可点的类目卡 = 提成明细页的**类目筛选器**（2026-09-15 乐哥：「点某个类目 → 只看它 → 快速核对」）。
   ⚠️ 新增 `--pick` 修饰类承载"可点"三态，**不动 .cm-catbox 本体** ——
      达人结算页也用同一套类名，在本体上加 cursor:pointer 会让那页的纯展示卡"看着能点"（假可点）。
   三态都要给反馈：默认（能点）/ hover（即将生效）/ is-active（当前生效）。
   只做 hover 不做 active 的话，用户切完看不出"现在是哪一个在生效"，表格少了一半行会以为漏数。 */
.cm-catbox--pick { cursor: pointer; transition: border-color var(--motion-fast) var(--motion-ease-standard), box-shadow var(--motion-fast) var(--motion-ease-standard); }
/* hover 用**边框 + 阴影**而不是换底色：卡片默认底就是 `--color-surface-2`（项目里当 hover 底用的那一档），
   两者同色 → 改背景等于没反馈。 */
.cm-catbox--pick:hover { border-color: var(--color-primary-200); box-shadow: var(--shadow-sm); }
.cm-catbox--pick:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.cm-catbox--pick.is-active { border-color: var(--color-accent); background: var(--color-accent-bg); }
.cm-catbox--pick.is-active .cm-catbox__label { color: var(--color-accent-text); }
/* 「筛选中」小字：给当前生效的那张卡一个明确标记（只靠边框色差，在浅色屏上不够醒目） */
.cm-catbox__on { font-size: var(--font-size-xs); color: var(--color-accent-text); }
.cm-catgrid__tip { margin-top: var(--space-2); }
/* 标题行的筛选态胶囊（含取消按钮）：滚动到表格时卡片已出视野，必须在这儿再写一次"已筛选" */
.cm-filter-tag {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px var(--space-2); border-radius: 999px;
  background: var(--color-accent-bg); color: var(--color-accent-text);
  font-size: var(--font-size-xs); font-variant-numeric: tabular-nums;
}
.cm-filter-tag__x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: inherit; cursor: pointer;
}
.cm-filter-tag__x:hover { background: var(--color-primary-200); }
/* 总览页「可下钻」的角色金额：默认与其它数字同观感（不做成蓝链接，财务表格里满屏蓝会乱），
   但 hover 变主色 + 虚线下划线 —— 既保持扫读性，又让人发现"这里能点"。 */
.co-drill { cursor: pointer; border-bottom: 1px dashed transparent; transition: color var(--motion-fast) var(--motion-ease-standard); }
.co-drill:hover { color: var(--color-accent-text); border-bottom-color: var(--color-accent); }

/* rebuild dry_run 影响面报告（方案 2 顶部「重建人员明细」弹窗） */
.cm-report { display: flex; flex-direction: column; gap: var(--space-4); }
.cm-report__kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.cm-report__kpi { padding: var(--space-3); border: 1px solid var(--color-border-soft); border-radius: var(--radius-md); background: var(--color-surface-2); }
.cm-report__kpi-label { font-size: var(--font-size-xs); color: var(--color-muted); }
.cm-report__kpi-val { margin-top: 2px; font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg); font-variant-numeric: tabular-nums; }
.cm-report__sec { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg); }

/* —— 工作台首页「我的提成」卡片 —— */
.my-comm-card { cursor: pointer; transition: box-shadow var(--motion-fast) var(--motion-ease-standard); }
.my-comm-card:hover { box-shadow: var(--shadow-md); }
.my-comm-card:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.my-comm-card__title { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--color-fg); }
.my-comm-card__amount { font-size: var(--font-size-3xl); font-weight: var(--font-weight-semibold); color: var(--color-fg); font-variant-numeric: tabular-nums; letter-spacing: var(--font-letter-spacing-display); line-height: var(--font-line-height-tight); }
.my-comm-card__label { margin-top: var(--space-1); font-size: var(--font-size-xs); color: var(--color-muted); }
.my-comm-card__cats { margin-top: var(--space-3); font-size: var(--font-size-sm); color: var(--color-fg-2); }

/* —— 排期表单：每人点位（三类角色）—— */
.member-rate-list { display: flex; flex-direction: column; gap: var(--space-2); }
.member-rate-row { display: flex; align-items: center; gap: var(--space-3); min-height: 44px; }
.member-rate-row__name { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-sm); color: var(--color-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-rate-row__input { flex: 0 0 132px; }
.member-rate-row__state { flex: 0 0 auto; font-size: var(--font-size-xs); }
.member-rate-hint { margin-top: var(--space-2); display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--font-size-xs); line-height: 1.7; }
.member-rate-hint--warn { color: var(--color-warning-text); }
.member-rate-hint--info { color: var(--color-muted); }

/* —— 窄屏（≤768px）：方案 1 / 2 统一的收缩策略 —— */
@media (max-width: 768px) {
  .cm-hero { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
  .cm-master { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
  /* 左栏人员列表 → 横向可滑的人员条 */
  .cm-plist { flex-direction: row; overflow-x: auto; overflow-y: hidden; max-height: none; padding-bottom: var(--space-1); }
  .cm-pitem { flex: 0 0 auto; width: auto; padding-right: var(--space-4); }
  .cm-pitem__roles { display: none; }
  .cm-catgrid { grid-template-columns: minmax(0, 1fr); }
  /* 明细细表渐进披露：隐去 直播时间 / 计算基数 / 点位，语义折进达人单元格副行（AC-04 三项仍在） */
  .cm-c-time, .cm-c-base, .cm-c-rate { display: none; }
  .cm-narrow-sub { display: block; }
  .cm-table th, .cm-table td { padding-left: var(--space-2); padding-right: var(--space-2); }
  .cm-table td { overflow-wrap: break-word; }
  .cm-table td:first-child .mono, .cm-table th:first-child { font-size: var(--font-size-xs); }
  .cm-table td:first-child { width: 118px; }
  .cm-table td:last-child { min-width: 78px; }
  .cm-heroline { flex-wrap: wrap; row-gap: var(--space-3); }
  .cm-heroline__amt { margin-left: 0; width: 100%; text-align: left; }
  .cm-plist__count { white-space: nowrap; }
  .cm-report__kpis { grid-template-columns: minmax(0, 1fr); }
  .member-rate-row { flex-wrap: wrap; }
  .member-rate-row__input { flex: 1 1 132px; }
}

/* ⚠️ 已废弃（2026-09-15 晚）：`.member-rate-group`（每人点位分组壳）随
   「按人配点位（助播 / 商务助理）」一起下线 —— 它只被已删除的 `rateGroupHtml()` 使用。
   商务侧的逐人点位是**平铺列表**，只用下面的 `.member-rate-list` / `.member-rate-row`。 */

/* 人员成本页 tab 条：窄屏 6 个 tab 排一行会撑出横向溢出 → 允许换行。
   只作用于本页（#ps-tabs），不动全局 .seg（其他页 tab 数少、行为不变）。 */
@media (max-width: 768px) {
  #ps-tabs { flex-wrap: wrap; padding: 4px; }
  #ps-tabs .seg__btn { flex: 1 1 auto; }
}

/* ==========================================================================
   提成总览：人员表格（2026-09-12 乐哥要求「一行一行」替代原左右分栏）
   · .co-row 整行可点展开（所以 cursor:pointer + hover 反馈要明显）
   · .co-expand 是展开区容器：用 surface-2 与表体区分，避免与相邻行视觉粘连
   ========================================================================== */
.co-row { cursor: pointer; }
.co-row.is-open { background: var(--color-surface-2); }
.co-toggle { color: var(--color-muted); }
.co-ops { white-space: nowrap; }
.co-ops .btn + .btn { margin-left: var(--space-2); }
.co-expand { padding: var(--space-4); background: var(--color-surface-2); }
/* 手动校准弹窗的「校准范围」提示条（2026-09-15 乐哥：「校准是校准那个岗位、那一场，
   还是所有的提成」）—— 校准的实际粒度是「人 × 月」，会把该人该月**全部岗位、全部场次**
   的提成合在一起改。不写清楚，财务会以为是按岗位/按场校准（那两者都不存在）。
   用浅主色底 + 左侧主色竖条：一眼可见、但不与弹窗里的输入区抢注意力。 */
.co-adj-scope {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-primary-200);
  border-left-width: 3px;
  border-radius: var(--radius-md);
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-size: var(--font-size-sm);
  line-height: 1.6;
}

/* ==========================================================================
   达人结算：行内展开「业绩构成」（2026-09-12 乐哥需求）
   关联直播的按场次一行、未关联的归当月私域一行；系数只在展开层展示。
   ========================================================================== */
.st-row { cursor: pointer; }
/* 展开中的主行：底色与展开区一致 + 去掉下边框 → 主行与展开块**读成一个整体**（不是两块） */
.st-row.is-open,
.st-row.is-open:hover { background: var(--color-bg); }
.st-row.is-open > td { border-bottom-color: transparent; }
/* （已删除）.st-toggle —— 首列「展开箭头」单元格已随「点行跳结算明细页」一并移除（乐哥 2026-09-13），
   这两条（.st-toggle 与展开态点亮）不再匹配任何元素。 */

/* ==========================================================================
   展开区（乐哥 2026-09-12 二次优化：要"显眼、好扫读"）
   三条视觉手段，都在既有 token 内：
     ① 分界：底色下沉到页面底（--color-bg）+ 2px 上边界线 → 与上方白表格明确分开
     ② 主次：汇总从"一行平铺 14px"改为「标签在上 / 数字在下的 KPI 格」，合计基数放大加粗
     ③ 顺序：表头用 .th-sub 标出 计算基础 → 各维度之和 → 乘积结果，引导从左到右读
   ========================================================================== */
.st-drill {
  padding: var(--space-4) var(--space-4) var(--space-4);
  background: var(--color-bg);                 /* 下沉一档（原 surface-2 太接近白色，看不出展开） */
  border-top: 2px solid var(--color-border);   /* 明确上边界：一眼看出"从这里开始是展开内容" */
}
/* ⚠️ 桌面端把展开面板宽度**钳到卡片可见宽**（实测踩过，必须保留这条）：
   主表 13 列的内容宽度是 1379px，比卡片可见宽 1134px 宽 → `.table-wrap` 会横向滚动；
   展开行 `<td colspan=13>` 实测宽 1205px，若面板跟着撑满，
   最关键的「合计基数」KPI 格与「该行佣金」列会**落到可见区之外**（实测右侧还差 43px），
   财务得先横向滚动才看得到金额 —— 正是乐哥说的"看不出重点"的元凶之一。
   宽度算式全部用既有 token：视口 − 侧边栏 − 页面左右留白 − 展开行左右内边距 − 卡片边框。
   sticky left 让面板在横向滚动时钉在可见区左侧，不会跟着跑出去。 */
@media (min-width: 1024px) {
  .st-drill {
    position: sticky;
    left: 0;
    width: calc(100vw - var(--sidebar-width) - var(--page-padding) * 2 - var(--space-3) * 2 - 2px);
    max-width: 100%;
  }
}
/* 展开区标题条：给这块内容一个"名字"，扫读时先看到它 */
.st-drill-head {
  display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.st-drill-head svg { flex: none; color: var(--color-muted); }
.st-drill-head__t { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
.st-drill-head__meta { margin-left: auto; font-size: var(--font-size-xs); color: var(--color-meta); }
.st-drill-hint {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3); margin-bottom: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-warning-bg); color: var(--color-warning-text);
  font-size: var(--font-size-sm);
}
/* 汇总：KPI 格（标签在上、数字在下）——「合计基数」为主，放大一级 + 主文字色
   ⚠️ 2026-09-14 与提成明细的 `.cm-catbox` 统一（乐哥反馈「达人结算二级页和提成总览不一样」）：
      原本这里是 `surface 底 + 阴影无边框`，提成那边是 `surface-2 底 + 1px 边框`，
      同为「二级页顶部的 3 格类目汇总」，两套皮 → 看着不是一套东西。
      现统一取提成那套（乐哥拍板「统一为有边框」）：浅底 + 1px 细边框、去掉阴影。
      边框比阴影在浅色底上边界更清晰，也更接近全站卡片的统一语言。
   ⚠️ 改这里会**同时影响**达人结算列表页的行内展开区（同一个 .st-drill 组件）——
      这是有意的：同一个组件只该有一套皮。 */
.st-drill-kpis {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3); margin-bottom: var(--space-4);
}
.st-drill-kpi {
  padding: var(--space-3);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
}
.st-drill-kpi__k { font-size: var(--font-size-xs); color: var(--color-muted); }
.st-drill-kpi__v {
  margin-top: var(--space-1);
  font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg-2);
  font-variant-numeric: tabular-nums;
}
/* 主数字：合计 —— 财务最关心的那个，放大到 20px + 主文字色。
   底色/边框对齐提成明细的 `.cm-catbox--total`（强调底 + 强调边框），
   使两页的「合计格」在整套格子里以同样方式跳出来。 */
.st-drill-kpi--main {
  background: var(--color-accent-bg);
  border-color: var(--color-primary-200);
}
.st-drill-kpi--main .st-drill-kpi__v {
  font-size: var(--font-size-xl); color: var(--color-fg);
}
.st-drill-diff { color: var(--color-warning-text); }
.st-drill-tag {
  display: inline-block; padding: 0 var(--space-2); border-radius: var(--radius-sm);
  background: var(--color-surface); color: var(--color-fg-2); font-size: var(--font-size-xs);
  /* nowrap 必须加：「当月私域」4 字在固定列宽里会被折成两行（实测踩过），
     标签一旦换行就把整行撑高、看着像坏了 */
  white-space: nowrap;
}
.st-drill-tag--priv { background: var(--color-warning-bg); color: var(--color-warning-text); }
/* ---- 展开区「分列展示」：线上佣金 / 线下应结（乐哥 2026-09-12）----
   原来在一个单元格里纵向堆叠各维度，线上金额往往占大头、把"我方要付的线下额"淹没了。
   改为横向分列：一列线上、一列线下应结 → 老板一眼看出"还要给这个达人结多少钱"。
   · 线上佣金：平台直付，我方不付 → **次要色弱化**
   · 线下应结：**我方实际要付的** → **主色加粗**，该行的重点读数
   每列下方 .st-col-sub 给构成（如「线下 2% + 年框 3%」），细分金额放 title 悬停看。 */
.st-col-on {
  color: var(--color-muted);            /* 弱化：不是我们要付的钱 */
  font-feature-settings: "tnum", "lnum";
}
.st-col-off {
  /* ⭐ 「线下应结」是全表最重要的读数：本期我方要付给达人的钱（乐哥 2026-09-13 要求更突出）
     → 主色蓝 + 字号升一档 + 加粗。
     与左侧可跳转的场次编号（.cell-link，主色 500、常规字重、小字号）靠"更深 + 更大 + 更粗"区分，
     一个是"能点"，一个是"要付的钱"，不会混。 */
  color: var(--color-primary-600);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  font-feature-settings: "tnum", "lnum";
}
.st-col-off--zero { color: var(--color-muted); font-weight: var(--font-weight-regular); }
.st-col-sub {
  margin-top: 1px;
  color: var(--color-muted);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-regular);
  line-height: 1.35;
  white-space: nowrap;
}
/* 展开区里两列的单元格：不再参与"行高撑开"，让小字紧贴金额 */
.st-drill .table--sub tbody td.st-col-online,
.st-drill .table--sub tbody td.st-col-offline { vertical-align: top; padding-top: var(--space-2); }

/* ⚠️ 2026-09-14 乐哥拍板「所有列表统一标准」→ **删除本块的表头皮覆盖**（第四套皮）。
   原设计与注释：
     「展开区明细表：与主表拉开差异 —— 表头更弱、行更高、佣金列加重
      （主表表头 44px 灰底；这里表头透明、行高更大，避免"两处表头长得一样"看串行）」
     → 实测：`transparent 底 + 34px 高 + 底对齐`。
   为什么删：乐哥的原话是「列与列之间的间隔、列头的高度和颜色，很多表格都不一致」、
   「你要不统一定一个标准，所有列表页面都按照这个标准改」——
   项目里因此累计了**四套表头皮**（主表 44px 灰底 / 子表 28px 透明 / st-comp 34px 透明 /
   本块 34px 透明底对齐）。统一优于局部区分：展开区与主表的层次现在由**容器边界 + 缩进**承担
   （`.st-drill` 自身的内边距与左侧留白），不再靠"表头长得不一样"。
   保留 `vertical-align: bottom` 已无意义（主表未设、两行表头本就是居中），一并移除。 */
.st-drill .table--sub tbody td {
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  background: transparent;
}
.st-drill .table--sub tbody tr:hover td { background: var(--color-surface-2); }
/* 场次编号：字号略小、等宽，作为"行标签"；其下的人员小字弱化 */
.st-drill .table--sub tbody td:first-child { font-size: var(--font-size-sm); }
.st-drill .table--sub tbody td:first-child .cell-sub { margin-top: var(--space-1); color: var(--color-meta); }
/* 佣金列：整块最重的数字 —— 加粗 + 主文字色 + 稍大一号 */
.st-drill .table--sub tbody td.st-drill-amt {
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-base);
  color: var(--color-fg);
}
/* 私域行：与直播场次行区分（浅黄底，与「当月私域」标签同源） */
.st-drill .table--sub tbody tr.st-drill-row--priv td { background: var(--color-warning-bg); }
.st-drill .table--sub tbody tr.st-drill-row--priv:hover td { background: var(--color-warning-bg); }

/* ==========================================================================
   达人结算：业绩归属「取数规则」备注（乐哥 2026-09-12）
   文案来自服务端 /shop-orders/attribution-rule，前端原样渲染。
   ========================================================================== */
.st-rule { margin-bottom: var(--space-3); }
/* 提示条 · 统一形态（2026-09-14 乐哥：「不要做成现在提成总览里这种**白底**的」）
   ---------------------------------------------------------------
   改前：`background: --color-surface-2`（#FAFBFC，几乎与卡片同色）+ 4px 圆角 + 左竖线
        → 看着就是"白底"，不够醒目。这正是乐哥点名要改的那一条。
   改后：浅蓝底（--notice-bg）+ 8px 圆角 + 蓝图标，与 .an-banner / .alert 完全同规格。
   ⚠️ 去掉左侧竖线：图片里那种是"整块浅底"，没有色条。 */
.st-rule__inner {
  display: flex; align-items: flex-start; gap: var(--notice-gap);
  padding: var(--notice-pad-y) var(--notice-pad-x);
  border-radius: var(--notice-radius);
  border: 1px solid var(--notice-border);
  background: var(--notice-bg);
  color: var(--notice-fg);
  font-size: var(--font-size-sm);
  line-height: 1.6;
}
/* 图标垂直对齐：16px 图标在首行行盒（1.6em = 22.4px @14px）内居中
   → 下移 (1.6em − 16px) / 2 = 3.2px。用 em 表达行高，字号一变自动跟随。
   ⚠️ 必须用 align-self: flex-start（对齐首行）而不是容器的 align-items:center，
      提示条常是多行，整体居中会让图标跑到段落中间。 */
.st-rule__inner svg { flex: none; align-self: flex-start; margin-top: calc((1.6em - 16px) / 2); color: var(--notice-ico); }

/* ⭐ 2026-09-27：'.st-rule' 的危险变体（审批详情里的「钱相关」提醒专用）——
   乐哥规范：「提醒的文字用红色」+ 配套红底更醒目（与 .alert--danger 同色系）。
   用法：`<div class="st-rule st-rule--danger"><div class="st-rule__inner">…</div></div>`
   ⚠️ 只用于**需要审批人停下来看清楚**的提醒（影响历史成本 / 锁定排期不随之更新）；
      普通说明仍用默认的浅蓝形态，不要滥用到所有提示上（全红=没有重点）。 */
.st-rule--danger .st-rule__inner {
  background: var(--color-danger-bg);
  border-color: var(--color-danger-border);
  color: var(--color-danger-strong);
}
.st-rule--danger .st-rule__inner svg { color: var(--color-danger-strong); }

/* 表头副标题：用于在列名下方标注口径（如「无需结算」「不含线上」）—— 2026-09-12
   2026-09-12 二次：改 10.5px 太小不可读 → 用 --font-size-xs，并在展开区表头里兼作"读法引导" */
.th-sub {
  display: block;
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  white-space: nowrap;
  line-height: 1.35;
}
/* 表头旁的「口径提示」图标：圆圈感叹号，**说明只在悬停时以浏览器原生气泡显示**
   （乐哥 2026-09-12：不要把说明文字摊在表头占地方）。
   ⚠️ 用原生 title 而非 CSS 气泡：表格外层 .table-wrap{overflow-x:auto} 会裁掉自定义气泡。
   这里只做视觉：图标与列名同行对齐、悬停时有颜色反馈表示"可查看说明"。 */
.th-note {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin-left: 3px;
  color: var(--color-muted);
  cursor: help;              /* 提示可悬停查看 */
  transition: color var(--motion-fast) var(--motion-ease-standard);
}
.th-note:hover { color: var(--color-accent-text); }
.th-note svg { flex: none; }

/* ⭐ 2026-09-25 「小字注释收进 ⓘ」方案（乐哥选定）——
   变体：把 ⓘ 钉在**父容器右上角**（父容器需 position:relative，如 .kpi-card）。
   ──────────────────────────────────────────────────────────────────────────
   什么内容该收进来（分级，不是一刀切）：
     ✅ 该收：**算式**（¥1,548,186.41 ÷ ¥5,189,547.80 —— 核对用，不常看）
             **长解释**（「这张表回答…」—— 读一次就够）
     ⛔ 不该收：**限定词**（「含已取消」「货已寄回」「占实付总额 29.8%」
                —— 读数字时**必须知道**，收起来反而有害）
              **数据本身**（时间、渠道、分页——那不是注释）
   为什么用右上角而不是跟在文字后面：跟在文字后会**参与换行**、把行撑高（等于没收）；
   右上角是"这块区域的补充信息"的自然位置，且**完全不占内容高度**。
   为什么图标用 info（ⓘ）而不是 alertCircle（❗）：口径说明是"查得到更好"，
   不是"出问题了"——用感叹号会让人误以为有异常。 */
.th-note.is-corner {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  margin-left: 0;              /* 覆盖基础样式的 margin-left:3px（角落定位不需要文字间距） */
  z-index: 1;                  /* 保证在卡片内容之上可点（卡片内可能有滚动/覆盖层） */
}
/* 该列不参与应付（如线上佣金由平台直付）→ 金额用次要色，从视觉上"退出主读数" */
.st-col-muted { color: var(--color-muted); }
/* 展开区表头副标题：承接「计算基础 → 各维度之和 → 乘积结果」的读法引导，语气更明确 */
.st-drill .table--sub thead .th-sub { color: var(--color-meta); }

/* 窄屏：KPI 三格 → 单列；展开区左右内边距收紧（表格交给 .table-wrap 横向滚动） */
@media (max-width: 768px) {
  .st-drill { padding: var(--space-3); }
  .st-drill-kpis { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  .st-drill-kpi { display: flex; align-items: baseline; gap: var(--space-2); padding: var(--space-2) var(--space-3); }
  .st-drill-kpi__v { margin-top: 0; }
  .st-drill-head { flex-wrap: wrap; }
}


/* 展开区「线下应结」列的年框未计原因提示（资格被拦时出现）：
   写明是「未签年框 / 合同日期未填 / 不在合同期」，避免看到 0 以为算错 */
.st-col-note {
  margin-top: 2px;
  color: var(--color-warning-text);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-regular);
  line-height: 1.35;
  white-space: nowrap;
}

/* 展开区「合计」行（乐哥 2026-09-12）：各行的线上/线下应结必须能加总，
   且「线下应结」合计要与顶部结算单的「应付总额」对得上 */
.st-drill .table--sub tbody tr.st-drill-sumrow td {
  border-top: 2px solid var(--color-border);
  background: var(--color-surface-2);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}
.st-drill .table--sub tbody tr.st-drill-sumrow td:first-child {
  color: var(--color-muted);
  font-weight: var(--font-weight-regular);
}

/* 展开区：该行「未满 T+35」金额（暂不计入结算）—— 灰色提示，避免与已计金额混淆 */
.st-col-pending {
  margin-top: 2px;
  color: var(--color-warning-text);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-regular);
  line-height: 1.35;
  white-space: nowrap;
}

/* 展开区底部：「未满 T+35」合计提示条 —— 醒目（财务给达人结算前必须看到） */
.st-pending-bar {
  display: flex;
  align-items: flex-start;
  gap: var(--notice-gap);
  margin-top: var(--space-3);
  padding: var(--notice-pad-y) var(--notice-pad-x);
  /* 与全站提示条同规格（2026-09-14）：圆角 4→8px、描边用更柔和的 warning-border
     （原来是 --color-warning 实色，#F2A918 太跳）。**橙色语义保留**（这是警示不是说明）。 */
  border: 1px solid var(--notice-warn-border);
  border-radius: var(--notice-radius);
  background: var(--notice-warn-bg);
  color: var(--color-warning-text);
  font-size: var(--font-size-sm);
  line-height: 1.5;
}
/* 图标垂直对齐（首行行高 1.5em = 21px @14px → 下移 (1.5em − 16px) / 2 = 2.5px，原手写 2px 偏上 0.5px） */
.st-pending-bar svg { flex: none; align-self: flex-start; margin-top: calc((1.5em - 16px) / 2); }
.st-pending-bar b { font-weight: var(--font-weight-semibold); }

/* 展开区「非福利款 GSV」列（= GSV − 福利款，线下四类的基数）—— 乐哥 2026-09-12 独立成列，
   不再挤在线下应结格里。颜色与 GSV 列一致（都是基数），靠副标题区分用途。 */
.st-col-ns { font-feature-settings: "tnum", "lnum"; }
.st-drill .table--sub tbody td.st-col-nscol { vertical-align: top; padding-top: var(--space-2); }
.st-drill .table--sub tbody tr.st-drill-sumrow td:nth-child(6) { font-feature-settings: "tnum", "lnum"; }

/* 展开区「未满35天」列（下期再结）—— 乐哥 2026-09-12 独立成列。
   它是**业绩**（本期未计入），故用 warning 色提示；与左侧「本期」各列之间加分隔线，
   避免误读成"本期也算过它"。 */
.st-col-pendcol {
  vertical-align: top;
  padding-top: var(--space-2);
  border-left: 1px dashed var(--color-border);
  border-right: 1px dashed var(--color-border);
}
.st-drill .table--sub thead th.st-col-pendhead {
  border-left: 1px dashed var(--color-border);
  border-right: 1px dashed var(--color-border);
}
.st-col-pending { font-feature-settings: "tnum", "lnum"; }
.st-col-none { color: var(--color-muted); }

/* 页顶归属规则条里的「规则」入口（2026-09-13）：
   完整规则收进 hover 气泡，常驻只留精简一句 → 用下划虚线提示"可悬停/聚焦查看"。 */
.st-rule__more {
  margin-left: var(--space-2);
  color: var(--color-primary-500);
  border-bottom: 1px dashed currentColor;
  cursor: help;
  transition: color var(--motion-fast) var(--motion-ease-standard);
}
.st-rule__more:hover { color: var(--color-primary-600); }

/* ==========================================================================
   提成总览：合计行 + 月度合计突出（乐哥 2026-09-13 要求对齐达人结算页的做法）
   ========================================================================== */
/* 月度合计：财务最关心的数 → 主色 + 加粗（与达人结算页「线下应结」同一套视觉语言）
   ⚠️ 选择器必须带 .cm-table td（特异性 0,2,1）：单类 `.cm-co-total` 会被
      pages.css **之后**加载的 pages-influencer.css / pages-schedule.css 里的表格规则覆盖，
      实测表现为"class 加上了、颜色却没变"（2026-09-13 踩过）。 */
.cm-table td.cm-co-total {
  color: var(--color-primary-600);
  font-weight: var(--font-weight-semibold);
  font-feature-settings: "tnum", "lnum";
}
/* 表尾合计行：与数据行区分（上边框 + 浅底 + 加粗），且不可点、不参与展开 */
.cm-foot-row td {
  border-top: 2px solid var(--color-border);
  background: var(--color-surface-2);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}
/* 「合计（N 人 / N 行）」这一个标签格：置灰、不加粗 —— 它是**说明**，不是数字。
   ⚠️ 2026-09-17 从 `td:nth-child(2)` 改成 `td:first-child`：
   原来绑第 2 格，是因为当时两处合计行（华大对账 / 提成总览）多写了一个空 `<td>`，
   让标签落在了第 2 格；而那两处本身是**错的**（表头 9 列、合计行 10 格 → 整行右移一列，
   数字会显示在错误的列下）。现已把它们改成标签落第 1 格（与其它 3 处合计行一致：
   排期明细 colspan=2、提成总览展开区 colspan=6、带货明细 colspan=3 —— 都用 colspan 合并首格）。
   绑 `:first-child` 后三种写法都能命中，不再依赖"首格是不是空的"。 */
.cm-foot-row td:first-child {
  color: var(--color-muted);
  font-weight: var(--font-weight-regular);
}
/* 同理：合计行里的总额也要保证生效（同上，提高特异性） */
.cm-table td.cm-co-sumtotal,
.cm-foot-row td.cm-co-sumtotal {
  color: var(--color-primary-600);
  font-size: var(--font-size-base);
}

/* 达人结算 · KPI 卡里的「无权查看」占位（金额为 null 时显示「—」）：
   用 muted 而不是 0 —— 0 会被读成"确实没有这笔钱"，与"看不到"是两回事。 */
.st-kpi-hidden { color: var(--color-muted); }

/* ============================================================================
   达人结算 · 结算单覆盖提示条（乐哥 2026-09-13）
   「达人结算，8月里为啥只有2个达人」—— 因为列表只装已生成结算单的达人。
   这条提示把「本月有 N 位做过直播、M 位还没结算单」摆出来并给一键补齐入口。
   警示色：这不是错误，但是**该处理的事**（不补生成，这些达人这个月就看不到）。
   ============================================================================ */
/* ⚠️ 2026-09-14 乐哥：「警示提醒方式没有微信小店的好」→ 改**行内强调**（去色块）
   ----------------------------------------------------------------------------
   改前：`#FEF5E7` 浅黄底 + 1px 边框 + 圆角，整条 1456px 通栏色块（12 万+ 像素）。

   为什么改（像素级对比微信小店参考图）：
     · 微信小店全图彩色像素只占 **0.72%**，唯一强调色是橙色 rgb(251,157,59)，
       且**只用在「待发货」这类几个字**上；信息层级靠**文字深浅**，不用色块。
     · 我们的大色块会与表格内的状态色（红「否」#E41E3F、橙「未授权」#F2A918）**抢注意力**；
       而且一旦哪页都有黄条，用户就"看不见"了（**警示疲劳**）。

   改后：透明底 + 只留下方 1px 细线；**关键数字用橙字**挑出；按钮改文字链。
   保留「警示」语义靠三处：图标色 + 关键数字橙字 + 与内容之间那条细线。
   ⚠️ 橙色文字用的是 --color-warning-text（#B25C0C），白底对比度 **4.73** > 4.5 达标；
      不能用 --color-warning（#FF8C42），那个对比度只有 2.4，做正文色不合格。
   ⚠️ 不要改回"有底色"：那种做法已被验证会与表内状态色打架（见上）。 */
/* 提示条 · 统一形态 + 保留操作按钮（2026-09-14 乐哥定稿）
   ---------------------------------------------------------------
   乐哥：「统一色底，保留按钮」
   本类特殊：**带「一键生成」操作按钮**（不是纯说明）→ 统一底色/圆角/图标，
   但按钮照旧右对齐（用 margin-left:auto 推过去）。
   改前是「透明底 + 底部细线」——与提成总览那条又不一样。 */
.st-cover {
  display: flex; align-items: center; gap: var(--notice-gap); flex-wrap: wrap;
  padding: var(--notice-pad-y) var(--notice-pad-x); margin-bottom: var(--space-4);
  border: 1px solid var(--notice-warn-border); border-radius: var(--notice-radius);
  background: var(--notice-warn-bg);
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
}
/* 右侧按钮推到最右（原来是靠 justify/内容撑，统一后固定住） */
.st-cover > .btn, .st-cover > button:last-child { margin-left: auto; }
.st-cover:empty { display: none; }
.st-cover__ic { flex: none; display: inline-flex; align-items: center; color: var(--notice-warn-ico); }
.st-cover__txt { flex: 1; min-width: 260px; line-height: 1.7; }
/* 关键数字/关键词用橙字（对齐微信小店「待发货」的做法）——它是本条里唯一的强调手段 */
.st-cover__txt b { color: var(--color-warning-text); font-weight: var(--font-weight-semibold); }
.st-cover .btn { flex: none; }

/* ============================================================================
   品牌佣金 · 通用佣金条 + 待维护（乐哥 2026-09-13）
   需求：「增加个待维护佣金的筛选（我导入订单导入的商品，但是系统里没维护上游佣金的）」
        「或者设置个通用佣金……通用佣金所有商品都是这个佣金率，我特殊维护的除外」
   ============================================================================ */

/* 通用佣金条：已设置用主色（正常态），未设置用警示色（因为没设时待维护商品会算成 0% → 负数） */
/* 提示条 · 统一形态（2026-09-14）：色底/圆角/内边距对齐 --notice-* 规格；
   本条**带「设置通用佣金」按钮**（保留，右对齐）。 */
.cm-general {
  display: flex; align-items: center; gap: var(--notice-gap); flex-wrap: wrap;
  padding: var(--notice-pad-y) var(--notice-pad-x); margin-bottom: var(--space-3);
  border: 1px solid var(--notice-border); border-radius: var(--notice-radius);
  background: var(--notice-bg);
}
.cm-general--warn { border-color: var(--notice-warn-border); background: var(--notice-warn-bg); }
/* 图标（原本**没有图标**，2026-09-14 补上以与全站一致） */
.cm-general__ico { flex: none; display: inline-flex; align-items: center; color: var(--notice-ico); }
.cm-general--warn .cm-general__ico { color: var(--notice-warn-ico); }
/* 右侧按钮推到最右 */
.cm-general > .btn, .cm-general > button:last-child { margin-left: auto; }
/* 未设置时隐藏空壳（还没加载完数据时不该闪一条空框） */
.cm-general:empty { display: none; }
.cm-gb__label { font-size: var(--font-size-sm); font-weight: 600; color: var(--color-primary-700); flex: none; }
.cm-gb__label--warn { color: var(--color-warning-text); }
.cm-gb__v {
  font-size: 22px; font-weight: 700; color: var(--color-primary-700);
  font-variant-numeric: tabular-nums; flex: none;
}
.cm-gb__v--warn { color: var(--color-warning-text); font-size: 18px; }
.cm-gb__d { font-size: var(--font-size-sm); color: var(--color-fg-2); line-height: 1.7; flex: 1; min-width: 220px; }
.cm-gb__d b { color: var(--color-fg); }
.cm-general .btn { flex: none; }
/* 弹窗里的影响面说明块 */
.cm-gb-note {
  grid-column: 1 / -1; margin-top: var(--space-2); padding: var(--space-3);
  border-radius: var(--radius-md); background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  font-size: var(--font-size-sm); color: var(--color-fg-2); line-height: 1.8;
}
.cm-gb-note b { color: var(--color-fg); }

/* 「待维护」徽标：这不是错误、是需要处理的缺口 —— 用警示色（与"未开票"同一档语义） */
.cm-pend-badge {
  display: inline-block; font-size: 11px; line-height: 16px; padding: 0 6px;
  border-radius: var(--radius-sm); white-space: nowrap; font-weight: var(--font-weight-semibold);
  background: var(--color-warning-bg); color: var(--color-warning-text);
  border: 1px solid var(--color-warning-border);
}
/* 待维护行整行浅底纹：一屏扫过去能直接看出"这几行还没配" */
.table tbody tr.cm-pend-row > td { background: var(--color-warning-bg-soft); }
.table tbody tr.cm-pend-row:hover > td { background: var(--color-warning-bg-hover); }
/* 已卖出的金额：红色（已经在算错、最紧急） */
.cm-pend-sold { color: var(--color-danger-strong); font-weight: var(--font-weight-semibold); font-size: var(--font-size-sm); }

/* 提成总览：身份来源标记（2026-09-13，「兼职助播」专用）。
   刻意与角色 badge **视觉区分**：角色 badge 说的是「在直播里担任什么」（商务/助播/商助），
   这个说的是「以什么身份参与」（成员账号 / 兼职助播）。两者若用同一种 badge 样式，
   并排会出现「助播」+「兼职助播」两个看起来同类的标签，容易误读成"身兼两个角色"。
   虚线边框 = "另一维度"的视觉语言，与实心 badge 拉开。 */
.co-src {
  display: inline-flex;
  align-items: center;
  padding: 0 6px;
  height: 18px;
  font-size: var(--font-size-xs);
  color: var(--color-fg-2);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
}
.co-src + .pnl-flag { margin-left: 4px; }

/* 提成「待补」标记（2026-09-13）：已确认发放后，又到期进来的提成 = 要补给本人的钱。
   用**橙色**（与"预估"同一套 warning 语义）：都是"钱还没落定"的状态。
   ⚠️ 不用红色：红色在本系统是"涨/正数"的数值色，借来表达"待办"会与金额色冲突。 */
.co-carry {
  margin-top: 3px;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-warning-text);
  white-space: nowrap;
  cursor: help;
}
/* 页头汇总徽标：进页面第一眼就要看到「有几笔钱要补」 */
.co-carry-head__badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 var(--space-2);
  margin-right: var(--space-2);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-warning-text);
  background: var(--color-warning-bg);
  border: 1px solid var(--color-warning);
  border-radius: var(--radius-sm);
  cursor: help;
}

/* ==========================================================================
   排期盈亏（schedule-detail 的「盈亏」tab）—— 乐哥 2026-09-13
   结论卡最显眼（盈利/亏损一眼看到），其次收入/成本两侧，再逐笔明细。
   ========================================================================== */
.pf-hero {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  margin: var(--space-3) 0;
}
/* ⚠️ 着色遵循项目统一约定（table.css 的 .num-pos / .num-neg）：
   **盈利=红、亏损=绿**（国内红涨绿跌习惯）。2026-09-13 修正 ——
   原来我用「盈利=绿」写反了，与「经营看板」「主播业绩」的红绿语义冲突
   （同一系统里绿色既表示亏损又表示盈利，看的人会误判）。 */
.pf-hero.is-win { background: var(--color-danger-bg); }
.pf-hero.is-loss { background: var(--color-success-bg); }
.pf-hero__main { flex: none; min-width: 220px; }
.pf-hero__k {
  font-size: var(--font-size-sm);
  color: var(--color-muted);
  margin-bottom: var(--space-1);
}
.pf-hero__v {
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-semibold);
  font-feature-settings: "tnum", "lnum";
  line-height: 1.2;
}
.pf-hero.is-win .pf-hero__v { color: var(--color-danger); }
.pf-hero.is-loss .pf-hero__v { color: var(--color-success); }
.pf-hero__sub {
  font-size: var(--font-size-sm);
  color: var(--color-muted);
  margin-top: var(--space-1);
}
.pf-hero__side { flex: 1; display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-2) var(--space-5); }

/* 收入 / 成本 两侧并排 */
.pf-sides { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); margin-bottom: var(--space-4); }
.pf-side {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}
.pf-side__t {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-muted);
  margin-bottom: var(--space-3);
}
.pf-kv {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-1) 0;
  font-size: var(--font-size-sm);
}
.pf-kv span { color: var(--color-muted); }
.pf-kv b { font-feature-settings: "tnum", "lnum"; font-weight: var(--font-weight-medium); }
.pf-kv--strong b { color: var(--color-primary-600); font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); }
.pf-note {
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px dashed var(--color-border);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  line-height: 1.5;
}

/* 逐笔费用记录：类目分组行 */
.pf-cat td {
  background: var(--color-surface-2);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg-2);
}

@media (max-width: 900px) {
  .pf-hero { flex-direction: column; align-items: flex-start; }
  .pf-sides { grid-template-columns: 1fr; }
}

/* 展开区「预估应结」列（本期 + 未满35天预估）—— 乐哥 2026-09-13
   这是财务跟达人核对"总共要结多少"的数，故给一个浅底与左侧「本期」区隔开。
   颜色用主色（与「线下应结」同族），但加浅底让它更醒目；副标题说明差额来自未满部分。 */
.st-col-fcast {
  color: var(--color-primary-600);
  font-weight: var(--font-weight-semibold);
  font-feature-settings: "tnum", "lnum";
}
/* 主表（达人结算列表页）也用「预估应结」列了 —— 乐哥 2026-09-13 要求提到列表页，不必只能展开看。
   ⚠️ 必须写成 `.table tbody td.st-col-fcast`（0,2,2）：上面那条 `.st-col-fcast`（0,1,0）
      会被全局 `.table tbody td`（0,1,2）压过 → 主色**静默失效**，数字仍是默认近黑。
      实测：改前 getComputedStyle(...).color === rgb(28, 30, 33)。
      这是本项目反复踩的同一个坑（`.num-pos` / `.cm-co-total` / `.kpi-card__value` 都中过），
      **规则：给表格单元格上色 / 加粗，选择器里至少要带上 `.table tbody td` 这一层。**
      （展开区那侧不需要：它的规则是 `.st-drill .table--sub tbody tr.st-drill-sumrow td.st-col-fcast`，
        权重 (0,3,3) 本来就压得住。） */
.table tbody td.st-col-fcast {
  color: var(--color-primary-600);
  font-weight: var(--font-weight-semibold);
}
/* 列表页「线下应结」列（乐哥 2026-09-13：「颜色也改下，不要黑色」）。
   与展开区 `.st-col-off` 是**同一个数**（settleTotal 四类之和），故用**同一套视觉**：
   主色蓝 + 半粗，两处看起来一致，不会让人以为是两个不同的值。
   （此前列表页这格只挂了 `is-num num` + 内联 font-weight，没挂颜色类 → 默认近黑。） */
.table thead th.st-col-payable,
.table tbody td.st-col-payable {
  color: var(--color-primary-600);
  font-weight: var(--font-weight-semibold);
}
/* 「本期 | 预估」的分界线：两列同色同粗，靠这条竖线区分主次（本期 = 现在付，预估 = 含未满）。
   与展开区表头 `th.st-col-fcasthead` 的 border-left 同一种处理，两处观感一致。 */
.table thead th.st-col-fcast,
.table tbody td.st-col-fcast {
  border-left: 1px solid var(--color-border);
}

/* ==========================================================================
   T+35 数值展示规范（2026-09-13 乐哥定：全系统统一，不然很乱）
   核心：**一个数 + 状态标签**。含未满 35 天时挂橙色「预估」标签，全满了不挂任何标签
        （标签的出现本身就是信号：没标签 = 这个数已经定了）。
   三类列的处理：
     · 看业绩/看赚亏（GSV、收入、利润）→ 一列（含未满）+「预估」标签
     · 要结算的钱（应结给达人/员工）    → 两列：截止目前应结 ／ 预估应结
     · 结算依据（结算基数、提成基数）    → 一列（**不含**未满）+ 下面这个灰色小字提示
   ⚠️ 结算依据类**不能用**橙色「预估」标签：它的语义与「预估」相反（这列不含未满），
      而且它乘费率就是应结金额 —— 用同一种橙色标签会让人以为两列是一回事。
      所以另立一种视觉：灰色小字，说明"未满多少没算进来"。
   ========================================================================== */
.t35-hint {
  display: block;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-regular);
  color: var(--color-muted);
  white-space: nowrap;
  cursor: help;
}
/* 金额格里的标签不吃掉小数点的对齐：标签是 inline 的，跟在小字后面不换行 */
td .t35-hint { margin-top: 1px; }

/* 表格单元格里的「预估」标签：比 KPI 卡上的小一号，避免在密集表格里抢视线。
   ⚠️ 用 `.table tbody td .an-tag` 带上表格层：`.an-tag` 本体权重 (0,1,0)，
      会被 `.table tbody td`（0,1,2）的字号压回表格默认值（本项目反复踩的同一个坑）。 */
.table tbody td .an-tag {
  font-size: 11px;
  line-height: 16px;
  padding: 0 5px;
  vertical-align: 1px;
}
.st-drill .table--sub tbody td.st-col-forecast {
  vertical-align: top;
  padding-top: var(--space-2);
  background: var(--color-primary-50);
}
.st-drill .table--sub thead th.st-col-fcasthead {
  background: var(--color-primary-50);
  border-left: 1px solid var(--color-border);
}
.st-drill .table--sub tbody tr.st-drill-sumrow td.st-col-fcast {
  background: var(--color-primary-50);
  font-size: var(--font-size-base);
}

/* ============================================================================
   华大营养对账（应收 / 开票台账）— 2026-09-13
   与「达人结算」同构：分达人主表 + 点行展开商品明细；多一块开票/回款台账。
   ============================================================================ */
/* 达人分账附表 · 工具栏（2026-09-27 乐哥：「这个页面的UI不对，包括日期、时间还有这些」）
   ──────────────────────────────────────────────────────────────────────────
   改前三个毛病：
     ① 标签误用 `.hs-static`（那是**弹窗只读信息块**：灰底 + 边框 + 38px 高），
        放进工具栏像个灰方块，与全站"纯文字标签"不是一个语言。
     ② 日期用浏览器原生 `type="date"` —— 自带黑色方块图标，与全站控件不搭。
     ③ 内联 `gap:8px` 覆盖了 `.toolbar` 标准的 16px ⇒ 整排看着挤。
   现在：标签走纯文字（与业绩总表「筛选：」同规格）、日期走系统 `DateTimePicker`、
       间距回归 `.toolbar` 默认值（页面不再内联 gap）。 */
.ax-label {
  font-size: var(--font-size-sm);
  color: var(--color-muted);
  flex: none;
}
/* 日期之间的连接符：与两侧 34px 控件垂直居中对齐的短横线 */
.ax-tilde {
  color: var(--color-muted);
  font-size: var(--font-size-sm);
  flex: none;
  user-select: none;
}

/* 店铺多选（乐哥 2026-09-27：「店铺选择需要支持多选，因为这里面会涉及到主体」）。
   原生 select 不支持好看的多选 ⇒ 自绘：按钮外观对齐 .select（列表档 34px），
   点开下面板逐项勾选；面板绝对定位、悬浮在内容之上。
   ⚠️ 按钮与面板都走设计令牌（--radius-md / --shadow-md / --color-surface-2），
      不写死颜色 —— 亮/暗主题都跟着走。 */
.ax-stores { position: relative; flex: none; }
.ax-stores__btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 10px;
  min-width: 186px;
  max-width: 300px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--font-size-base);
  cursor: pointer;
}
.ax-stores__btn:hover { background: var(--color-surface-2); }
.ax-stores__sum {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ax-stores__caret {
  margin-left: auto;
  color: var(--color-muted);
  display: inline-flex;
  align-items: center;
}
.ax-stores__panel {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  left: 0;
  min-width: 100%;
  max-height: 260px;
  overflow-y: auto;
  padding: 4px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.ax-stores__opt {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  font-size: var(--font-size-sm);
}
.ax-stores__opt:hover { background: var(--color-surface-2); }
.ax-stores__opt input { margin: 0; flex: none; }

.hs-ledger { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
@media (max-width: 900px) { .hs-ledger { grid-template-columns: 1fr; } }
.hs-ledger__col {
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4); background: var(--color-surface);
}
.hs-ledger__t {
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg);
  margin-bottom: var(--space-2); padding-bottom: var(--space-2); border-bottom: 1px solid var(--color-border);
}
.hs-ledger__row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: 6px 0; font-size: var(--font-size-sm);
}
.hs-ledger__k { color: var(--color-muted); flex: none; }
.hs-ledger__v { color: var(--color-fg); text-align: right; font-variant-numeric: tabular-nums; }

/* 对账金额变了（又有订单满 35 天）：提示「票面要不要跟着改」，用警示色但不做成按钮态 */
.hs-warn {
  display: flex; align-items: flex-start; gap: 6px; margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  background: var(--color-warning-bg); color: var(--color-warning-text);
  font-size: var(--font-size-sm); line-height: 1.7;
}
/* 图标垂直对齐（首行行高 1.7em = 23.8px @14px → 下移 (1.7em − 16px) / 2 = 3.9px，原手写 3px 偏上 0.9px） */
.hs-warn svg { flex: none; align-self: flex-start; margin-top: calc((1.7em - 16px) / 2); }

.hs-row:hover { background: var(--color-surface-2); }
.hs-drill { padding: var(--space-2) 0 var(--space-2) var(--space-3); background: var(--color-surface-2); }
.hs-drill .table--sub { background: var(--color-bg); }
/* ⚠️ 2026-09-14 二级明细页去灰底（乐哥：「华大对账明细的 UI 也不一样，列表怎么有灰色底？」）
   上面两行的灰底是**列表页行内展开区**的设计意图（用浅灰把"展开的明细"和"主表"分层）。
   但二级明细页复用了同一套 HTML（detail.js 渲染 `.detail-drill > .hs-drill > table`），
   于是整个表格区域被铺成灰底、看着像"整块禁用/只读"。
   修法：**只在二级页作用域内**改回白底（transparent 露出白卡片），
         列表页展开区的灰底分层**原样保留**（那里是有意的，改了会破坏主表/明细的层次）。
   ⚠️ 不能直接删上面两行 —— 那会连带改掉列表页展开区的观感。 */
.detail-drill .hs-drill,
.detail-drill .hs-drill .table--sub {
  background: transparent;
}
.hs-sub-sep td {
  font-size: var(--font-size-xs); color: var(--color-muted);
  background: var(--color-surface-2); padding: var(--space-2) var(--space-3);
}

/* 合计行（tfoot 是**独立的行组**，thead/tbody 的规则一律覆盖不到它，必须单独写）：
   · 「应收」主色：本条与上文 `.cm-foot-row td.cm-co-sumtotal` 都能给它上色，
     特异性上**本条胜出**（0,2,2 vs 0,2,1）—— 所以改颜色时两条都要看，
     只改一条会出现「改了不生效」的错觉（2026-09-13 反向验证实测踩到：
     只抹掉 cm-co-sumtotal 时合计行仍是主色，排查后才发现本条在管它）。
   · 分界线：tbody/th 的规则覆盖不到 tfoot，漏写合计行那条竖线会断掉。 */
.table tfoot td.st-col-payable { color: var(--color-primary-600); font-weight: var(--font-weight-semibold); }
.table tfoot td.st-col-fcast { border-left: 1px solid var(--color-border); }

/* 「本月订单都还没满 35 天」的行内提示：用警示色而不是 muted —— 它解释的是"为什么这里是 0"，
   muted 会读成"没数据"，而实际是"数据还没到期"。 */
.hs-pend-note { color: var(--color-warning-text); }

/* ---- 分店铺（2026-09-13 追加：华大分店铺结算）---- */

/* 店铺标签：主表每行、展开区标题、台账卡片标题都用它标出「这是哪个店」。
   用主色系而不是中性灰 —— 分店铺是本页的核心区分维度，扫读时要一眼抓得到。 */
.hs-store {
  display: inline-block; font-size: var(--font-size-xs); line-height: 18px; padding: 0 6px;
  border-radius: var(--radius-sm); white-space: nowrap;
  background: var(--color-primary-50); color: var(--color-primary-700);
  border: 1px solid var(--color-primary-200);
}

/* 台账：按店铺各一张卡片（原来是一张卡片两个列，现在外层再套一层店铺分组） */
.hs-led-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
@media (max-width: 1200px) { .hs-led-grid { grid-template-columns: 1fr; } }
.hs-led-card {
  border: 1px solid var(--color-border); border-radius: var(--radius-lg);
  padding: var(--space-3); background: var(--color-bg);
}
.hs-led-card__hd {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding-bottom: var(--space-2); margin-bottom: var(--space-2); border-bottom: 1px solid var(--color-border);
}
.hs-led-card__amt {
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
  color: var(--color-fg); text-align: right; font-variant-numeric: tabular-nums;
}
.hs-led-card__amt .cell-sub { display: block; font-weight: var(--font-weight-regular); }
.hs-led-card__actions {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--color-border);
}
/* 卡片内的台账两列在窄卡里要能塌成单列（卡片本身只有半屏宽） */
.hs-led-card .hs-ledger { grid-template-columns: 1fr; gap: var(--space-2); }
@media (min-width: 1500px) { .hs-led-card .hs-ledger { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hs-led-card .hs-ledger__col { border: none; padding: 0; background: transparent; }

/* 展开区标题：店铺标签 + 达人说明 + 右侧合计（合计靠右、要显眼 —— 财务对的就是这个数） */
.hs-drill__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) 0 var(--space-2);
}
.hs-drill__sum { margin-left: auto; font-size: var(--font-size-sm); color: var(--color-fg-2); }
.hs-drill__sum b { color: var(--color-primary-700); font-variant-numeric: tabular-nums; }

/* 展开区分段标题（乐哥 2026-09-13：明细分「已满 35 天」+「未满 35 天」两段）——
   一段是已经能开票的、一段是要等订单满期的，视觉上必须一眼能分开。 */
.hs-drill__sec {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  margin: var(--space-3) 0 var(--space-2); padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg);
}
.hs-drill__sec:first-of-type { margin-top: var(--space-1); }
/* 「未满」段用暖色：不是错误，但必须让人意识到"这批钱还没到账期" */
.hs-drill__sec--pend { color: var(--color-warning-text); }
.hs-drill__sec-tag {
  font-size: 11px; line-height: 17px; padding: 0 7px; border-radius: var(--radius-sm);
  background: var(--color-surface-2); color: var(--color-fg-2); border: 1px solid var(--color-border);
  font-weight: var(--font-weight-regular); white-space: nowrap;
}
.hs-drill__sec--pend .hs-drill__sec-tag {
  background: var(--color-warning-bg); color: var(--color-warning-text); border-color: var(--color-warning-border);
}
.hs-drill__sec-num { margin-left: auto; font-size: var(--font-size-sm); font-weight: var(--font-weight-regular); color: var(--color-fg-2); }
.hs-drill__sec-num b { color: var(--color-fg); font-variant-numeric: tabular-nums; }
.hs-drill__sec--pend .hs-drill__sec-num b { color: var(--color-warning-text); }

/* 分段合计行：两段各有一行合计，用来跟主表「截止目前 / 预估」对账 */
/* ⚠️ 必须带 `.table tbody` 层（0,1,2）—— 单类选择器（0,1,0）会被 .table tbody td 压过 */
.table tbody tr.hs-drill__sum-row > td {
  background: var(--color-surface-2); font-weight: var(--font-weight-semibold); color: var(--color-fg);
}

/* 福利款标记（2026-09-13）：福利款的线上佣金按「福利款对外佣金率」算、不按达人常规线上率，
   所以那一行线上率会明显更低（10% vs 40%）。不打标记会被当成"线上率填错了"。
   用暖色（与"需要注意的特例"语义一致）而不是警示红 —— 它本身不是错误。 */
.hs-wel {
  display: inline-block; margin-left: 6px; font-size: var(--font-size-xs); line-height: 16px;
  padding: 0 5px; border-radius: var(--radius-sm); vertical-align: middle; white-space: nowrap;
  background: var(--color-warning-bg); color: var(--color-warning-text);
  border: 1px solid var(--color-warning-border); font-weight: var(--font-weight-semibold);
}
/* 应付星盟为负的行：标记原因（多半是上游佣金率没配）。用警示红 —— 这确实是需要处理的问题。 */
.hs-neg-tag {
  display: inline-block; margin-left: 6px; font-size: var(--font-size-xs); line-height: 16px;
  padding: 0 5px; border-radius: var(--radius-sm); vertical-align: middle; white-space: nowrap;
  background: var(--color-danger-bg); color: var(--color-danger-strong);
  border: 1px solid var(--color-danger-border); font-weight: var(--font-weight-semibold);
}

/* 福利款行的整行底纹：让它在明细里成片可辨（一屏多行时比逐行找标签快） */
.table tbody tr.hs-prod-wel > td { background: var(--color-warning-bg); }
.table tbody tr.hs-prod-wel > td:first-child { box-shadow: inset 3px 0 0 var(--color-warning-accent); }

/* 自定义 SKU：等宽字体（编码类内容用等宽更好逐位核对），过长时省略号 + 悬停看全 */
.hs-sku {
  display: inline-block; max-width: 140px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; vertical-align: bottom;
  font-family: var(--font-family-mono); font-size: var(--font-size-xs); color: var(--color-fg);
}

/* 弹窗里的只读信息块（店铺 + 该店铺应收），与输入框区分开 */
.hs-static {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: 8px var(--space-3); border-radius: var(--radius-sm);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  min-height: 38px;
}

/* 发票文件选择器（支持 PDF，故不复用只收图片的 .img-upload） */
.hs-file { display: flex; align-items: center; gap: var(--space-3); }
.hs-file__box {
  flex: 1; min-width: 0; padding: 8px var(--space-3);
  border: 1px dashed var(--color-border); border-radius: var(--radius-sm);
  font-size: var(--font-size-sm); color: var(--color-fg-2); cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hs-file__box:hover { border-color: var(--color-primary-400); color: var(--color-primary-600); }

/* ==========================================================================
   排期详情 · 订单明细页签（乐哥 2026-09-13）
   点排期编号从提成总览/达人结算下钻过来对数用。规格：订单 + 商品 + 两口径对照 + 自检。
   ========================================================================== */
.sdo-filters { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/* 口径说明条：左侧主色竖线，与页面其他"口径提示"一致（st-pending-bar / hs-tip 同风格） */
.sdo-tip {
  display: flex; gap: var(--space-2); align-items: flex-start;
  background: var(--color-accent-bg); border: 1px solid var(--color-border-soft);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  font-size: var(--font-size-xs); line-height: 1.9; color: var(--color-fg-2);
}
.sdo-tip b { color: var(--color-fg); }
.sdo-em { color: var(--color-fg); font-weight: var(--font-weight-semibold); }

/* 订单状态构成 */
.sdo-chips { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-3); }
.sdo-chips__k { font-size: var(--font-size-xs); color: var(--color-meta); }
.sdo-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--font-size-xs); color: var(--color-meta);
  border: 1px solid var(--color-border-soft); border-radius: var(--radius-lg);
  padding: 3px 10px; background: var(--color-surface);
}
.sdo-chip b { color: var(--color-fg); }

/* 商品单元格：多商品单一行内纵向排列，行间虚线分隔 */
.sdo-prod { line-height: 1.5; padding: 2px 0; }
.sdo-prod + .sdo-prod { border-top: 1px dashed var(--color-border-soft); margin-top: 4px; padding-top: 4px; }
/* 商品第 1 行 = 商品名 +（售后状态标签）。
   ── 三次演进（都记着，避免以后来回改）──
   ① 最初：标签放在第 2 行（.sdo-prod__meta）里 → 那一行被挤爆，标签断成两行、该行 90px。
   ② 2026-09-13 上午：标签移到第 1 行，配 `.sdo-prod__head{display:flex}` + 商品名 `nowrap + ellipsis`。
      挤压解决了，但埋下新问题：商品名 nowrap 会把列撑到 **459px**、整表横向滚 **106px**
      （搜「畅」这类长套餐名时，「净额」「状态」列要左右拖才看得到）。
   ③ 2026-09-13 傍晚（乐哥拍板「商品名可折两行」）：商品名**允许折行**、取消截断。
      商品名既已能折行，就不需要 flex + ellipsis 那套了 → head 改回普通流式布局，
      售后标签改成 inline 紧跟文字（折行时自然跟在**末行之后**，不会孤零零浮在右边）。 */
.sdo-prod__head { line-height: 1.45; }
.sdo-prod__head .sdo-after { white-space: nowrap; margin-left: 4px; }
/* 商品名：**允许折行**（乐哥 2026-09-13 傍晚拍板，订单明细表 / 商品汇总表 两处统一）。
   原来是 `max-width:420px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap` —— 单行截断，
   两个表都被它坑过：
     · 商品汇总表：nowrap 把富余宽度霸住不放 → 窄列（自定义SKU / 类型）被压到断行 / 竖排；
     · 订单明细表：nowrap 使该列的 min-content = 全文宽（实测 344px）→ 列被撑到 459px，整表横向滚。
   改成折行后：窄列可以锁死单行、表格不再横向滚动，长名也能看全（占几行由内容自然决定）。
   `overflow-wrap: break-word` 是兜底 —— 万一出现超长且无断点的片段（长英文串 / 长编码）也不会溢出。 */
.sdo-prod__nm {
  white-space: normal; overflow-wrap: break-word; line-height: 1.45;
}
/* 第 2 行（SKU · 类型 · 数量）：子项**一律不许被压缩**（`flex: 0 0 auto` + nowrap）。
   背景（乐哥 2026-09-13 修）：这一行原来是 `flex-wrap:nowrap` 且子项可压缩，
   当总宽超出单元格时子项会被挤扁 →「套餐」竖排、文字在盒内断行、行高被撑高（实测 90px vs 72px）。
   `flex-wrap: wrap` 作为兜底：万一将来又有超宽内容，它会整块换到第二行，而不是挤成一团。 */
.sdo-prod__meta {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--font-size-xs); color: var(--color-meta); margin-top: 2px;
}
.sdo-prod__meta > * { flex: 0 0 auto; white-space: nowrap; }
/* SKU / 类型 胶囊：**必须显式给字号**（乐哥 2026-09-13 统一）。
   原来不写字号 → 跟着父级走：在「订单明细表」里它们位于 .sdo-prod__meta（12px）内 → 12px；
   在「商品汇总表」里直接放在 td 里（14px）→ 14px。**同一个胶囊在同一页出现两种字号**。
   标签/胶囊按项目惯例统一 12px（与 .st-drill-tag 等一致），改这里两表一起生效。 */
/* ⚠️ 这一行必须 `white-space: nowrap`（乐哥 2026-09-13 修）。
   它是个 15~16 位、带连字符的编码（如 YY013501-0000005）：`normal` 下浏览器会**在连字符处断行**，
   于是「商品汇总」表里每格变 2 行（实测高 35px），11 行累计多占近 300px。
   nowrap 之后它的 min-content = 整串宽（约 115px），表格再也压不动它，只能去压商品名列。 */
.sdo-sku {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--font-size-xs);
  background: var(--color-surface-2); padding: 0 5px; border-radius: var(--radius-sm);
  white-space: nowrap;
}
/* ⚠️ 同样必须 `white-space: nowrap`（乐哥 2026-09-13 修）。
   中文**任意两个字之间都允许断行** → `normal` 下「套餐」被拆成竖排的「套 / 餐」（实测高 40px、2 行），
   这是本次「商品维度 UI 有问题」里最难看的一处。
   注：在「订单明细表」里它位于 .sdo-prod__meta（`> * { white-space:nowrap }`）→ 本来就正常；
   只有「商品汇总表」里它是 td 的直接子元素，才暴露了这个缺陷。 */
.sdo-kind {
  font-size: var(--font-size-xs); border: 1px solid var(--color-border-soft);
  padding: 0 5px; border-radius: var(--radius-sm); white-space: nowrap;
}
.sdo-after { color: var(--color-warning); }
.sdo-unmatched { color: var(--color-danger); font-size: var(--font-size-xs); }

/* ── 「商品汇总」表：把宽度精确让给唯一可换行的商品名列（乐哥 2026-09-13）──────────────
   问题：`table-layout: auto` 下所有列都参与"分配剩余宽度"，商品名列虽然是弹性列却没优先权，
        结果只有 302px（最长的商品名要 344px）→ 长名折成双行、同表行高 46/66 不一致。
   修法：让**除商品名以外的所有列锁死单行**（`nowrap`）。这样它们的 min-content == max-content，
        浏览器压不动它们，只能把富余宽度全部让给唯一还能换行的商品名列。
   ⚠️ 为什么不直接改全站的 `.is-num`：那是 `table.css` 里的公共类，全站几十张表都在用，
      本项目铁律「改公共类前先查复用范围」—— 所以这里**新开一个只给本表用的作用域类**，
      原 `.num` / `.is-num` 一字未动。 */
.sdo-sumtbl thead th.is-num,
.sdo-sumtbl tbody td.is-num,
.sdo-sumtbl tfoot td.is-num { white-space: nowrap; }
/* ⚠️ 这里**不需要**给表头单独写 nowrap —— 全站 `.table thead th` 本来就是 `nowrap`（table.css:27）。
   曾误加过 `.sdo-sumtbl thead th { white-space: nowrap }`，既冗余、配套断言也恒真（假绿），已一并删除。 */

/* 订单号：该列的主信息 → 与其它主信息列同为正常字号（原 12px 与全表 14px 不齐）。
   19 位数字 + 等宽字体，配合加宽后的列宽（190px）刚好一行放下（乐哥 2026-09-13）。 */
.sdo-no { font-size: var(--font-size-sm); }
.sdo-rf { color: var(--color-danger); }
.sdo-ses {
  font-size: var(--font-size-xs); white-space: nowrap;
  background: var(--color-accent-bg); color: var(--color-accent-text);
  padding: 1px 7px; border-radius: var(--radius-lg);
}
.sdo-st {
  font-size: var(--font-size-xs); white-space: nowrap;
  background: var(--color-surface-2); color: var(--color-fg-2);
  padding: 1px 7px; border-radius: var(--radius-sm);
}
.sdo-st--cancel { background: var(--color-danger-bg); color: var(--color-danger); }

/* 对账自检条：通过=绿框（让财务一眼确认对得上）；异常=红框（口径被破坏，必须显眼） */
.sdo-chk {
  margin-top: var(--space-3); padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border-soft);
  border-left: 3px solid var(--color-success);
  background: var(--color-success-bg);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs); line-height: 1.95; color: var(--color-fg-2);
}
.sdo-chk b { color: var(--color-fg); }
.sdo-chk--bad { background: var(--color-danger-bg); border-color: var(--color-danger); }

/* 口径说明里的「当前范围」（全部场次 / PQ000009-1）——用底色胶囊，与正文区分开 */
.sdo-scope {
  display: inline-block; margin-left: 8px;
  font-size: var(--font-size-xs); font-weight: var(--font-weight-regular);
  background: var(--color-accent); color: var(--color-accent-on);
  padding: 1px 8px; border-radius: var(--radius-lg);
}

/* ==========================================================================
   KPI 卡内的可点提示（乐哥 2026-09-13）
   --------------------------------------------------------------------------
   用于「N 张单数据有变化，重新计算」这类"看到问题就能顺手解决"的提示。
   刻意做成**文字链**而不是按钮：4 张卡里通常只有 1 张会出现它，
   做成按钮会显得突兀、也抢了卡片主数字的注意力。
   ========================================================================== */
.kpi-card__act {
  display: block; width: 100%; margin-top: var(--space-1); padding: 0;
  border: 0; background: none; text-align: left; cursor: pointer;
  font-size: var(--font-size-xs); line-height: 1.5;
  color: var(--color-warning-text);
}
.kpi-card__act:hover { text-decoration: underline; }
.kpi-card__act-go { font-weight: var(--font-weight-medium); text-decoration: underline; }
.kpi-card__act:disabled { opacity: 0.6; cursor: default; text-decoration: none; }

/* ==========================================================================
   口径说明条：「完整口径」点击展开（乐哥 2026-09-13）
   --------------------------------------------------------------------------
   改前是 hover 气泡装一整段长文（华大那页 1191 字）—— 读长文别扭、读到一半会消失、
   也没法复制。改成按钮展开：默认只占一行，展开后按主题分小节（小标题 + 要点列表）。
   ⚠️ 与原 .st-rule__more（气泡版）并存：三页改造是分批做的，另一页改造完可一并清掉。
   ========================================================================== */
.st-rule__toggle {
  margin-left: var(--space-2); padding: 0; border: 0; background: none; cursor: pointer;
  font-size: inherit; font-family: inherit; line-height: inherit;
  color: var(--color-primary-500); border-bottom: 1px dashed currentColor;
}
.st-rule__toggle:hover { color: var(--color-primary-600); }
.st-rule__full {
  margin-top: var(--space-2); padding: var(--space-3) var(--space-4);
  background: var(--color-surface-2); border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs); line-height: 1.9; color: var(--color-fg-2);
}
.st-rule__sec + .st-rule__sec {
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px dashed var(--color-border-soft);
}
.st-rule__sec-t { font-weight: var(--font-weight-medium); color: var(--color-fg); margin-bottom: 2px; }
.st-rule__list { margin: 0; padding-left: 18px; }
.st-rule__list li { margin: 0; }

/* ==========================================================================
   结算单展开区 · 「未满 35 天」分段（乐哥 2026-09-13）
   --------------------------------------------------------------------------
   背景：8 月 13 张结算单里 11 张是「全未满」（最大 ¥1,212,806.22 / 2094 单），
   而展开区此前直接返回一句「无业绩明细」—— 未满的钱、单数、场次全都看不到。
   现在把未满部分也**按场次列进同一张表**，用一条分段标题行与已满部分隔开。
   设计要求：既要在视觉上明确"这批还没到期"，又不能让它比"现在能付的钱"还抢眼
   → 用浅灰底 + 左侧虚线，不用红/黄警告色（那是异常的颜色，未满是正常业务状态）。
   ========================================================================== */

/* 分段标题行：跨全表宽的一条小标题 */
.st-drill .table--sub tbody tr.st-drill-segrow td {
  background: var(--color-surface-2);
  border-top: 2px solid var(--color-border);
  border-bottom: 1px solid var(--color-border-soft);
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
  white-space: nowrap;
}
.st-drill-segrow__t {
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg-2);
  margin-right: var(--space-3);
}
.st-drill-segrow__d { color: var(--color-meta); font-size: var(--font-size-xs); }

/* 未满行：浅灰底 + 首列左侧虚线，表明"这一段的性质与上面不同"（还没到期） */
.st-drill .table--sub tbody tr.st-drill-pendrow td {
  background: var(--color-surface);
  color: var(--color-fg-2);
}
.st-drill .table--sub tbody tr.st-drill-pendrow td:first-child {
  border-left: 2px dashed var(--color-border);
}
.st-drill .table--sub tbody tr.st-drill-pendrow:hover td { background: var(--color-surface-2); }

/* 未满合计行：比正常合计更弱（它是"还没到期的预估"，不是现在能付的钱） */
.st-drill .table--sub tbody tr.st-drill-sumrow--pend td {
  border-top: 1px dashed var(--color-border);
  color: var(--color-meta);
}
.st-drill .table--sub tbody tr.st-drill-sumrow--pend td:first-child { color: var(--color-meta); }

/* ==========================================================================
   「本期佣金构成」—— 达人结算展开区（乐哥 2026-09-13）
   原话：「线上佣金/线下·机构/年框/线下·返点/激励佣金 怎么都是空的，我都不知道每个项目
         佣金多少；展开明细只有应结佣金，但结算的是哪个项目的佣金、什么点数结算，我都看不到」
   实测根因：某达人**配了年框 3%** 却没计入（未签合同），页面只有静默的 ¥0.00 + 「无适用维度」，
   一个字都不解释。本区块把五个项目逐项摊开（费率 / 基数 / 金额 / **为什么是这个数**）。
   放在业绩构成表**之前**：先给结论（该付多少、什么点数），再给依据（哪几场算出来的）。
   ========================================================================== */
.st-comp { margin-bottom: var(--space-4); }
/* 与「业绩构成」标题条同款，但用分隔线把两个区块分开，避免看成一整块 */
.st-comp__head {
  display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border-soft);
}
.st-comp__head svg { flex: none; color: var(--color-accent); }
.st-comp__t { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
.st-comp__meta { margin-left: auto; font-size: var(--font-size-xs); color: var(--color-meta); }
.st-comp__empty {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  font-size: var(--font-size-sm); color: var(--color-fg-2);
}
.st-comp__empty b { color: var(--color-fg); }

/* 表格：表头弱化（与业绩构成表保持一致，避免两块表头抢注意力） */
/* ⚠️ 2026-09-14 删除表头皮覆盖（乐哥「所有列表统一标准」）：
   原为 `.st-comp__tbl thead th { background: transparent; color: var(--color-meta);
   font-weight: medium; }` —— 这是**第三套表头皮**（项目里共三套：
   主表 `.table thead th` 浅灰底 44px / 子表 `.table--sub thead th` 透明 28px /
   本表 透明 34px），三者并存就是乐哥反馈的「表头高度和颜色很多表格不一致」。
   现全部并入主表那套，此处不再覆盖任何表头样式。
   注意：tbody 的 `vertical-align: top` 保留（那是内容对齐，与表头无关）。 */
.st-comp__tbl tbody td { vertical-align: top; }

/* 项目名：这一列是"主角"，给主文字色 + 中等字重 */
.st-comp__nm { color: var(--color-fg); font-weight: var(--font-weight-medium); }
/* 「平台直付」小标签：说明这一项不是我方掏钱，避免被误算进应付 */
.st-comp__tag {
  display: inline-block; margin-left: 6px; padding: 0 5px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-2); color: var(--color-meta);
  font-size: var(--font-size-xs); font-weight: var(--font-weight-regular);
}
/* 费率：乐哥要的"什么点数结算"，加粗；「—」表示未配置 */
.st-comp__rate { font-weight: var(--font-weight-semibold); color: var(--color-fg); }
.st-comp__na { color: var(--color-muted); font-weight: var(--font-weight-regular); }
/* 金额：有值时用主色（与「截止目前应结」同色系，一眼认出"这是我方要付的"） */
.st-comp__amt { font-weight: var(--font-weight-semibold); }
.st-comp__amt.is-on { color: var(--color-accent-text); }
/* 状态说明：三种语义各有颜色 —— 未配置=灰（无需行动）、已计入=绿（正常）、
   未计=警示（**这是最需要看的一类**：配了费率却没算出钱，必须显眼） */
.st-comp__st { font-size: var(--font-size-xs); line-height: 1.5; }
.st-comp__st.is-off { color: var(--color-muted); }
.st-comp__st.is-on { color: var(--color-success-text); }
.st-comp__st.is-warn { color: var(--color-warning-text); }
.st-comp__st b { font-weight: var(--font-weight-semibold); }
/* 合计行：与展开区其它合计行同款（上方虚线、次要底色） */
.st-comp__sumrow td {
  border-top: 1px dashed var(--color-border);
  color: var(--color-meta);
}
.st-comp__sumrow td:first-child { font-size: var(--font-size-xs); }
.st-comp__sumrow b { color: var(--color-fg); }

/* 「本表与列表金额不一致」提示条（生成草稿后费率/订单/合同又变过）
   —— 构成表用实时费率，主表用单上快照，两者不等时**必须显式说明**，
   否则用户看到两个不同的数会直接判定"算错了"。配色用警示（不是错误，是"需留意"）。 */
.st-comp__drift {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3); margin-bottom: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-warning-bg); color: var(--color-warning-text);
  font-size: var(--font-size-xs); line-height: 1.7;
}
/* 图标垂直对齐（首行行高 1.7em，本条字号是 xs=12px → 20.4px → 下移 (1.7em − 16px) / 2 = 2.2px，原手写 1px 偏上 1.2px）
   ⚠️ 用 em 而非 px：本条 font-size 是 --font-size-xs，与其它提示条不同档，px 会写错。 */
.st-comp__drift svg { flex: none; align-self: flex-start; margin-top: calc((1.7em - 16px) / 2); }
.st-comp__drift b { font-weight: var(--font-weight-semibold); }

/* ==========================================================================
   统一「明细页」—— 2026-09-13（乐哥：行展开统一改为下级页面）
   ---------------------------------------------------------------------------
   9 处「详情类」展开改为独立明细页（detail.js 承载），这些样式是它的统一外壳。
   与列表页的关系：点行进明细 → 点「返回」回列表（保留筛选与滚动位置）。
   ⚠️ 全部走设计 token，无硬编码色值；间距用 --space-* 保持全站节奏。
   ========================================================================== */

/* 页头：返回按钮 + 标题（+ 右侧操作区）
   ⭐ 2026-09-28 页头包盒（乐哥拍板「方案A」）：白底圆角卡 —— 参考图（PMS）页头是独立白框，
   包盒后内容区顶部有了视觉锚点，与左侧侧栏白岛呼应，解决「内容区上面太空/侧栏显高」。
   ⚠️ align-items 由 flex-start 改 **center**（乐哥：返回、编号文字不居中）——
      页头三件套（返回 34 / 标题文字 / 操作按钮 44）高度参差，顶对齐时上下留白失衡。 */
.detail-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-frame-border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-6);
}

/* ⭐ 2026-09-28「精简页头」（乐哥拍板 A 方案）——**无返回、无标题**时用的形态：
   19 个页面（表单 12 + 明细 6 + 机制比价 1）只保留说明行/操作行，不再包白卡
   （单行灰字包一张白卡很怪；白卡是"页头"的壳，页头没了壳也就不需要）。
   ⚠️ 触发：`headNav` 收到「无 back 且无 title」时自动加本类（ui.js）。 */
.detail-head--bare {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
}
/* 返回：做成明确的"按钮"形态（不是裸文字）—— 技能 §9 要求返回可预测且显眼 */
.detail-back {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: none;
  height: var(--control-height-sm);   /* 两档制：32 → 34（紧凑档，返回按钮） */
  padding: 0 var(--space-3) 0 var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-fg-2);
  font-family: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard),
    color var(--motion-fast) var(--motion-ease-standard),
    border-color var(--motion-fast) var(--motion-ease-standard);
}
.detail-back:hover { background: var(--color-surface-2); color: var(--color-fg); border-color: var(--color-gray-400); }
.detail-back:active { background: var(--color-gray-100); }
.detail-back:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.detail-head__main { flex: 1; min-width: 0; }
.detail-head__act { flex: none; }

/* ⭐ 2026-09-28 页头操作按钮与「页面头部」同规（乐哥：编辑按钮颜色得统一）。
   .detail-head__act 里的 ghost 按钮与 .page-actions 的加强版一致：
   描边 gray-400（可辨）+ 主文字色 + medium 字重 + hover 转主色。 */
.detail-head__act .btn--ghost {
  background: var(--color-surface);
  border-color: var(--color-gray-400);
  color: var(--color-fg);
  font-weight: var(--font-weight-medium);
}
.detail-head__act .btn--ghost:hover:not(:disabled) {
  border-color: var(--color-accent-text);
  color: var(--color-accent-text);
  background: var(--color-primary-50);
}

/* 内容堆叠：多张卡片之间统一间距 */
.detail-stack > * + * { margin-top: var(--space-4); }

/* 键值网格（成本明细）：两列自适应，长值横跨整行避免被截断 */
.dt-kv { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2) var(--space-5); }
.dt-kv__row { display: flex; gap: var(--space-2); min-width: 0; font-size: var(--font-size-sm); line-height: 1.7; }
.dt-kv__row--wide { grid-column: 1 / -1; }
.dt-kv__k { flex: none; width: 88px; color: var(--color-muted); }
.dt-kv__v { min-width: 0; color: var(--color-fg); word-break: break-word; }
/* 窄屏收敛为单列，避免两列挤压 */
@media (max-width: 760px) {
  .dt-kv { grid-template-columns: minmax(0, 1fr); }
}

/* 明细页里的「展开区同款内容」（结算 / 华大对账）：
   这两块原本是列表内的展开区（自带 `.st-drill` / `.hs-drill` 的标题条与内边距），
   直接搬进明细页即可，只需去掉它作为"表格内嵌行"时的多余外边距。 */
.detail-drill .st-drill,
.detail-drill .hs-drill { margin-bottom: 0; }

/* ==========================================================================
   工作台首页改造（乐哥 2026-09-13）
   --------------------------------------------------------------------------
   新增：.chart-grid--three（投流消耗 / 今日排期 / 待办 三列）、.adcost（投流消耗展示）
   ========================================================================== */

/* 三列网格：底栏三块（投流消耗 / 今日排期 / 待办提醒）
   ⚠️ 窄屏必须降级为单列，否则每列 ~380px 会把「今日排期」的表格挤到换行。 */
.chart-grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1279px) {
  .chart-grid--three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 899px) {
  .chart-grid--three { grid-template-columns: minmax(0, 1fr); }
}

/* 投流消耗：主金额大号 + 占 GSV 比的细进度条
   ⚠️ 2026-09-20 删掉「投产比 ROI」那一格（乐哥：「roi 这个整体都不要了」）后，
      以下四条变成死样式、一并移除：
        .adcost__grid（两列 grid）/ .adcost__cell（格子）/ .adcost__k / .adcost__v
      原布局是「两个格子（投产比 / 投流占 GSV）+ 进度条」，现在只剩
      「进度条 + 一行占比说明」—— 占比不再重复呈现两次。
      若将来要再加指标格，从 git 历史里取回这四条即可（当时就是两列布局）。 */
.adcost__main {
  font-size: 26px;
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.adcost__label { margin-top: 2px; font-size: var(--font-size-xs); color: var(--color-muted); }
/* 占比条：投流 ÷ 净成交 —— 用主色填充，直观表达"投流占多少" */
.adcost__bar {
  margin-top: var(--space-4);
  height: 6px;
  border-radius: 999px;
  background: var(--color-surface-2);
  overflow: hidden;
}
.adcost__bar > span {
  display: block; height: 100%;
  background: var(--color-accent);
  border-radius: 999px;
  transition: width var(--motion-base) var(--motion-ease-standard);
}
.adcost__hint { margin-top: var(--space-2); font-size: var(--font-size-xs); color: var(--color-muted); }

/* 「待维护」行的区分标识（2026-09-14 乐哥选：给页面加区分标识）
   ----------------------------------------------------------------------------
   待维护行有两类，成因与优先级完全不同，但长相相似：
     · 有归属订单 → 订单已成交、只是没人维护业绩（编号要维护后才生成）→ **该尽快补维护**
     · 无归属订单 → 多半是订单还没导入（实测订单库只到 8-29，9 月为 0）→ 暂时不用管
   所以有订单的那类用**警示色**把它挑出来（"这是要处理的"），
   无订单的保持 `.cell-sub` 默认弱化灰（"不用管"）。

   ⭐ 2026-09-25：文案从「已归属 N 单 · 待补维护」（132px）缩到「N 单」（约 35px）——
      状态列可用宽只有 52px，原文案会折成 **3 行**、把行高从 42px 撑到 54px。
      完整解释移到表头 ⓘ（见 performance.js 的 状态 列）。
   ⛔ **`color` 是"该处理 vs 不用管"的主信号，不许去掉**（去掉两类又长得一样了）。
   ✅ `white-space: nowrap`：防止更大单数（如 1243 单）在窄列里被折成「1243 / 单」。
      ⚠️ 实测边界：本列可用 52px，`N 单` 在 3 位数以内（43px）稳落一行；
         4 位数约 50px 仍在界内。真出现 5 位以上才可能溢出（本表单场单数远达不到）。 */
.perf-pend-note {
  color: var(--color-warning-text);
  white-space: nowrap;
}

/* ==========================================================================
   月度利润明细表（乐哥 2026-09-14 需求）
   --------------------------------------------------------------------------
   结构：一个月一行 × 收入(2 列) + 支出(三大组动态 N 列) + 总成本/盈亏/盈亏率
   ⚠️ 全部用 `.pl-` 前缀新开作用域类，**不动** .table / .is-num 等公共类
      （项目铁律：改公共类前先查复用范围；新页面新开类最稳）。
   ========================================================================== */
.pl-table { font-variant-numeric: tabular-nums; }
/* 分组表头（"成本 · 达人相关"这一行）：浅底 + 加粗 + 居中，
   与下面一行的细项列名形成层级 —— 不加底色时两级表头看着是一整片，分不清哪个管哪几列。 */
.pl-grouphd {
  text-align: center;
  background: var(--color-surface-2);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg-2);
  border-bottom: 1px solid var(--color-border);
}
/* 细项表头：比分组头弱一档（字号小、常规字重），层级才立得住 */
.pl-table thead tr:last-child th {
  font-weight: var(--font-weight-regular);
  color: var(--color-fg-2);
}
/* 可下钻单元格：虚线下划线（不用实线，避免与链接/正文混淆）+ hover 变色。
   可点但看着不像按钮，是刻意的 —— 整表几十个可点格，全做成按钮会非常吵。 */
.pl-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dashed var(--color-border);
  cursor: pointer;
  transition: color var(--motion-fast) var(--motion-ease-standard),
              border-color var(--motion-fast) var(--motion-ease-standard);
}
.pl-link:hover,
.pl-link:focus-visible {
  color: var(--color-accent-text);
  border-bottom-color: var(--color-accent-text);
}
.pl-link:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 2px; }
/* 总成本 / 盈亏：整表最该被一眼看到的两列，加重 */
.pl-strong { font-weight: var(--font-weight-semibold); }
/* 月份列：不折行（"2026-08" 断成两行会让每行高度不齐） */
.pl-table tbody td:first-child { white-space: nowrap; }
/* 月份格：可点进「当月达人明细」二级页（乐哥 2026-09-15）
   ──────────────────────────────────────────────────────────────────────────
   乐哥原话：「不要虚线，改成蓝色（就是可以跳转那种，这样更显眼）」
   改前走全站 .pl-link（继承文字色 + 虚线下划线），在整表一堆下钻格里不突出。
   改后 = 表格内可跳转链接的标准形态（`.cell-link` 那套：蓝 #0064E0、无下划线、hover 加下划线）。

   ⚠️ **必须保留 `pl-link` 类**（不是换成 `cell-link`）：本页的下钻事件是
      `box.querySelectorAll('.pl-link')` 统一绑定的，类名一换**点击就失效**（且不报错）。
      故这里用「双类 + 提高特异性」覆盖样式，不动 HTML 上的类名。
   ⚠️ 为什么月份要与其他下钻格（虚线条）不同：两者是**不同层级的入口** ——
      月份去的是**页面级二级页**，其他格是**就地弹层下钻**。
      用两种视觉区分层级，比整表清一色虚线更清楚。
   ⚠️ 特异性说明：`.pl-link.pl-month`(0,2,0) 胜过 `.pl-link`(0,1,0)；
      `.pl-link.pl-month:hover`(0,3,0) 胜过 `.pl-link:hover`(0,2,0) —— 不依赖书写顺序。 */
.pl-link.pl-month {
  color: var(--color-primary-500);
  border-bottom: none;
  font-weight: var(--font-weight-semibold);
}
.pl-link.pl-month:hover,
.pl-link.pl-month:focus-visible {
  color: var(--color-primary-600);
  border-bottom: none;
  text-decoration: underline;
}

/* ==========================================================================
   月度利润明细 · 点月份进的「当月达人明细」二级页（乐哥 2026-09-15）
   --------------------------------------------------------------------------
   结构：本月合计 KPI → 达人明细（**行内展开场次**）→ 项目公共成本 → 底部验算条。
   ⚠️ 展开用「行内子表」而不是弹窗/新页面 —— 乐哥原话「不要跳来跳去，其实不直观」，
      想看某位达人这个月播了哪几场，就地展开即可（数据后端已一并返回，无额外请求）。
   ========================================================================== */
.pmd-table { font-variant-numeric: tabular-nums; }
/* 主行可点展开：必须有 hover 反馈，否则用户不知道这一行能点 */
.pmd-table tbody tr.pmd-row { cursor: pointer; }
/* ⭐ 2026-09-25：**斑马纹已按乐哥要求去掉**（全站统一，详见 table.css 的汇总清单）。
   ▸ 原实现（现删除）：本表主行后紧跟**隐藏的展开行**，两者交替 ⇒
     全站斑马纹的 `tbody tr:nth-child(even)` 会全落在隐藏行上、主行一条纹都没有
     （2026-09-15 实测踩到）。故当时由页面按**主行序号**交替加 `.is-alt` 类、这里给底色。
   ▸ 现在全站都没有斑马纹了，这条"绕过隐藏行"的特殊实现自然一并去掉。
     ⚠️ JS 里加 `.is-alt` 的代码（`pages/profit-month-detail.js`）**刻意保留** ——
        它现在不影响渲染（无对应规则），留着可作线索；要恢复只需取消下面这行注释。
   ⛔ 原规则：`.pmd-table tbody tr.pmd-row.is-alt { background: var(--table-row-alt); }`
   ▸ 本表去掉斑马纹后靠什么区分行：`cursor:pointer` + hover 高亮 + `.is-open` 展开态底色（下方两条）。 */
.pmd-table tbody tr.pmd-row:hover > td { background: var(--color-surface-2); }
.pmd-table tbody tr.pmd-row.is-open > td { background: var(--color-surface-2); }
/* 展开箭头：默认朝右，展开后转 90°（用 transition，不用弹跳缓动——全站禁用弹性缓动） */
.pmd-caret {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; color: var(--color-muted);
  transition: transform var(--motion-fast) var(--motion-ease-standard);
}
.pmd-table tbody tr.pmd-row.is-open .pmd-caret { transform: rotate(90deg); }

/* 展开区 = **主表的兄弟行**（不再是嵌套子表）
   --------------------------------------------------------------------------
   ⚠️⚠️ 为什么展开行不能嵌一张子表（2026-09-17 乐哥第二次报同一条 bug）：
      子表是**另一张表**，列宽由它自己的内容独立计算，与外层表头列宽毫无关系 →
      逐格错开。实测（1900px 视口 · 2026-09 数据）外层 13 列宽
      = 50/150/82/132/132/140/138/130/119/117/131/134/99，
      而嵌套子表 13 格全都是 ≈116px（内容相近 → 浏览器平均分），
      「第 1 场」因此落在 **T+35 GSV** 表头下、GSV 值落在「未满 35 天」下，最多偏 92px。
      ⇒ 展开行做兄弟行后，列宽由同一张表统一分配，**对齐是布局的自然结果**，
        不再需要任何"手工同步宽度"（那种做法改一列就会漂，且改一处漏一处）。
   ⚠️ `tr[hidden]` 显式声明 display:none：UA 的 `[hidden]{display:none}` 优先级低于
      任何作者样式，一旦将来有人给 tbody tr 设 display，收起态就会静默失效 —— 多这一行是保险。 */
.pmd-table tbody tr.pmd-sub[hidden] { display: none; }
/* 子行视觉：浅底 + 小一号字 + 更紧凑的上下留白（替代原 `.table--sub` 的「紧凑子表」角色）
   ⚠️ 底色设在 **td** 而不是 tr：全站斑马纹/悬浮是设在 tr 上的，
      设 td 的特异性更高、稳定压过它们（与 table.css 里 `tr.is-sum > td` 同一套做法）。 */
.pmd-table tbody tr.pmd-sub > td {
  background: var(--color-surface-2);
  font-size: var(--font-size-sm);
  padding-top: var(--space-2); padding-bottom: var(--space-2);
}
.pmd-table tbody tr.pmd-sub:hover > td { background: var(--color-surface-2); }
/* 「从属关系」用 inset 阴影表达，**不用 border-left**：本项目 border-left 已被若干
   语义线占用（见 table.css 的 .col-sep 注释），同属性会互相覆盖且极难排查。
   只画在首格（它贴着表格左边缘），视觉上就是那条"这些行属于上面那位达人"的竖线。 */
.pmd-table tbody tr.pmd-sub > td:first-child { box-shadow: inset 3px 0 0 var(--color-border-soft); }

/* 「该场无订单数据」标记（2026-09-17）
   --------------------------------------------------------------------------
   起因：乐哥问「艺芯怎么少了第二场直播」—— 那场确实一笔订单都没有，
   引擎从订单枚举场次导致整行不出现，用户只看到"少了一场"却不知原因。
   现在后端补出这类场次（金额恒为 0），这里给它一个**克制但一眼可辨**的标记：
   不用红色（不是错误、不影响任何金额），用中性警示色，避免看到就以为账错了。 */
.pmd-noorder {
  display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 4px;
  font-size: var(--font-size-xs); line-height: 1.6;
  background: var(--color-warning-bg); color: var(--color-warning-text);
}
.pmd-noorder-row > td { opacity: 0.85; }

/* 展开行下方的**附注行**（跨整行）：排期级成本未计入 / 无订单数据的原因
   --------------------------------------------------------------------------
   ⚠️ 为什么不放在表格最后一格（2026-09-17 实测数据说话）：
      放进「状态」列后，那格文字会参与列宽分配 → 展开一下整表列宽就跳：
        1440px 视口 · 2026-09：状态列 75→163px，其余 12 列各缩约 9%；
        2026-08 那条长说明更糟 —— 被挤在 97px 宽里折成 **199px 高**，竖着排一长条。
      跨整行（colspan=全列数）后，其最小宽度被摊到所有列上（每列十几像素），
      实测展开前后列宽几乎不变，文案也能用满整行。
   ⚠️ 附注行与场次行**同一组**显隐（都带 data-detail-of），由 bindRowToggle 按组切换；
      `tr[hidden]` 同样显式声明 display:none（理由同 .pmd-sub）。 */
.pmd-table tbody tr.pmd-note[hidden] { display: none; }
.pmd-table tbody tr.pmd-note > td {
  background: var(--color-surface-2);
  font-size: var(--font-size-xs);
  color: var(--color-meta);
  padding-top: 2px; padding-bottom: var(--space-2);
  /* 跨列格**不能**设 white-space:nowrap —— 那会让它的最小宽度无法被摊薄，
     整表会被撑宽（本项目"nowrap + 长文案把表格撑破"踩过一次，见 table.css 的注释）。 */
  white-space: normal;
}
/* 附注左侧的从属竖线：与场次行同一条（否则看着像两条线） */
.pmd-table tbody tr.pmd-note > td:first-child { box-shadow: inset 3px 0 0 var(--color-border-soft); }
/* 说明文案在附注行里可以用满整行，不再限制 360px（那个上限是为"塞在窄格里"设的） */
.pmd-table tbody tr.pmd-note .pmd-noorder-tip { display: inline; margin-top: 0; max-width: none; white-space: normal; }
/* ⚠️ 徽标现在放在附注行**句首**（2026-09-17 从"场次"格移过来）：
   那一列实测只有 91px 宽，徽标"无订单数据"会被挤成两行（"无订单 / 数据"）。
   改到跨整行的附注行后一行放得下，也顺了"徽标 + 解释"挨着读的直觉。
   ⇒ 原来的 `margin-left: 6px`（跟在"第 N 场"后面的场景）改成只留右侧间距。 */
.pmd-table tbody tr.pmd-note .pmd-noorder { margin-left: 0; margin-right: var(--space-2); }
/* 徽标与「排期级成本未计入」两段说明之间留出间距（都是 inline，需要显式 margin） */
.pmd-table tbody tr.pmd-note .cell-sub + .cell-sub { margin-top: 0; }
.pmd-table tbody tr.pmd-note .pmd-noorder-tip + .cell-sub { margin-left: var(--space-3); }

/* ==========================================================================
   达人结算单 · 卡片底部口径块（乐哥 2026-09-15：「列表里面少了个底块」）
   --------------------------------------------------------------------------
   参照华大对账那张卡：它在表格下方挂一块口径说明，而达人结算表格下面直接就是分页、没有这块。
   ⚠️ 视觉规格**用全站统一的提示条 token（--notice-*）**：浅底 + 8px 圆角 + info 图标。
      不复用 `.st-rule__inner` 类名（那是"页面级规则条"的语义），但共用同一组 token，
      保证同一屏里两个说明块视觉完全一致；`probe-notice-standard.js` 也会一并覆盖到。
   ⚠️ 左右 margin 用 --space-5：与 .card__header / .pagination 的水平内边距对齐，
      否则这个块会比卡片里的表格"缩进不齐"。
   ========================================================================== */
.st-foot {
  display: flex;
  align-items: flex-start;
  gap: var(--notice-gap);
  margin: 0 var(--space-5) var(--space-5);
  padding: var(--notice-pad-y) var(--notice-pad-x);
  border: 1px solid var(--notice-border);
  border-radius: var(--notice-radius);
  background: var(--notice-bg);
  color: var(--notice-fg);
  font-size: var(--font-size-sm);
  line-height: 1.6;
}
/* 图标垂直对齐：沿用全站提示条的推导法（16px 图标在 1.6em 行盒内居中） */
.st-foot svg { flex: none; align-self: flex-start; margin-top: calc((1.6em - 16px) / 2); color: var(--notice-ico); }
.st-foot b { color: var(--color-fg); font-weight: var(--font-weight-semibold); }

/* ==========================================================================
   角色权限页 · 权限目录（2026-09-17 重构）
   --------------------------------------------------------------------------
   改造前：44 个复选框平铺在 8 个**旧业务域**分组里，与侧栏菜单对不上号 ——
   用户看到「commission:rebate:read」只有一个光秃秃的中文名，不知道：
     ① 它属于侧栏哪个菜单   ② 它是"看不见菜单"还是"看不到某个金额"
   现在：分组 = 侧栏一级分组；组头一行小字列出该组的二级菜单；
        每个权限带**性质角标**（菜单可见 / 页面内操作 / 字段脱敏）+「管什么」说明。
   ⚠️ 角标三色语义必须稳定（与图例逐字对应），改色会让图例变成假的：
         menu   → 主色（蓝）：控制"能不能看到入口"，最重
         action → 中性灰：页面内的按钮级操作
         field  → 橙色：只影响某些金额显示为「—」，最容易被误解，故用警示色
   ========================================================================== */
.perm-group { padding: var(--space-4) 0; border-bottom: 1px solid var(--color-border-soft); }
.perm-group:last-child { border-bottom: 0; }
.perm-group__head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-3); }
/* ⚠️ 用 --font-size-lg（16px）而不是 --font-size-md —— 令牌表里**没有 md**（只有 xs/sm/base/lg/xl…），
   写错的表现是**整条声明被浏览器丢弃**且不报错（本项目 check-css-tokens.js 抓到的）。 */
.perm-group__title { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
/* 组头那行菜单摘要：它是"权限 ↔ 菜单"的锚点，不能省 —— 省了就退回改造前那种"对不上号"的状态 */
.perm-group__menus { font-size: var(--font-size-xs); color: var(--color-muted); }

.perm-list { display: flex; flex-direction: column; gap: var(--space-1); }
/* 每个权限一行：整行可点（label 包住 checkbox），避免只能点 12px 的小方块 */
.perm-item { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-sm); cursor: pointer; font-size: var(--font-size-sm); }
.perm-item:hover { background: var(--color-surface-2); }
.perm-item input[type="checkbox"] { margin-top: 2px; flex: none; }
.perm-item__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.perm-item__top { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.perm-item__label { color: var(--color-fg); }
/* 权限码本身也给出来（等宽字体、弱化）：排查"某个权限没生效"时要能直接对上后端日志 */
.perm-item__code { font-size: var(--font-size-xs); color: var(--color-muted); font-family: var(--font-family-mono);  /* 2026-09-25 修：--font-mono 是拼错的令牌名（一直走 monospace 兜底），正确名 --font-family-mono */ }
.perm-item__scope { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.5; }

/* 性质角标（三色，与图例一一对应） */
.perm-kind { display: inline-flex; align-items: center; height: 18px; padding: 0 var(--space-2);
  border-radius: var(--radius-pill); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium);
  white-space: nowrap; }
.perm-kind--menu { color: var(--color-accent-text); background: var(--color-accent-bg); }
.perm-kind--action { color: var(--color-muted); background: var(--color-surface-2); }
.perm-kind--field { color: var(--color-warning-text); background: var(--color-warning-bg); }

/* 图例：常驻在「全选/清空」同一行右侧（不折叠），否则角标成了没人懂的符号 */
.perm-legend { display: flex; align-items: center; gap: var(--space-2); margin-left: auto; flex-wrap: wrap; }
.perm-legend__t { font-size: var(--font-size-xs); color: var(--color-muted); margin-right: var(--space-2); }

/* ==========================================================================
   经营看板 · 本期洞察（2026-09-17）
   --------------------------------------------------------------------------
   放在 #an-body 最顶部，KPI 卡之上 —— 一进页面先看到"这个月怎么样"。
   设计约束（来自乐哥：「不用很啰嗦，但是很关键」）：
     · 一行一条结论，左标签 + 中结论 + 右行动提示，**扫读三列对齐**
     · 只给"需要注意"的行加底色（risk/warn），正常的行不加 —— 全是底色等于没有重点
     · 数字用页面统一的「万」格式，盈利红/亏损绿与全站一致
   ⚠️ `.an-ins-label` 用固定 56px 宽度：三条以上时标签列必须对齐，
      否则「规模 / 亏损达人 / 口径」长短不一会让结论列参差，扫读时眼睛要来回找。
   ========================================================================== */
.an-ins { margin-bottom: var(--space-6); }
.an-ins-headline {
  font-size: var(--font-size-base);
  color: var(--color-fg);
  line-height: 1.7;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-2);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
}
.an-ins-list { display: flex; flex-direction: column; }
.an-ins-row {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  gap: var(--space-3);
  align-items: baseline;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  line-height: 1.7;
}
.an-ins-row + .an-ins-row { border-top: 1px solid var(--color-border-soft); }
/* 需处理：整行浅警示底（比只染标签更能"跳出来"，但不至于像报错） */
.an-ins-row.is-risk { background: var(--color-warning-bg-soft); }
.an-ins-row.is-warn { background: var(--color-warning-bg-soft); }
.an-ins-label { font-size: var(--font-size-sm); color: var(--color-muted); white-space: nowrap; }
.an-ins-row.is-risk .an-ins-label { color: var(--color-warning-text); font-weight: var(--font-weight-medium); }
.an-ins-row.is-warn .an-ins-label { color: var(--color-warning-text); }
.an-ins-text { color: var(--color-fg); min-width: 0; }
.an-ins-text b { font-weight: var(--font-weight-semibold); }
/* 数字：等宽数字（tabular）保证多行数字右边缘对齐，扫读时不会左右跳 */
.an-ins-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; font-weight: var(--font-weight-medium); }
.an-ins-note { font-size: var(--font-size-xs); color: var(--color-muted); white-space: nowrap; }
.an-ins-row.is-risk .an-ins-note,
.an-ins-row.is-warn .an-ins-note { color: var(--color-warning-text); }

/* AI 追问（2026-09-17）—— 默认收起，点「追问 AI」才展开。
   ⚠️ 输入行用 flex 而不是 grid：窄屏时按钮要能跟着输入框一起收缩，
      grid 的固定列在 375px 下会把"提问"按钮挤出容器。 */
.an-ask { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border-soft); }
.an-ask__row { display: flex; gap: var(--space-2); align-items: center; }
.an-ask__row .input { flex: 1; min-width: 0; }
.an-ask__row .btn { flex: none; }
.an-ask__sug { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-2); }
/* 建议问题做成一排小胶囊：比空白输入框更好上手，也引导用户问"数据里有的东西" */
.an-ask__chip {
  border: 1px solid var(--color-border-soft);
  background: var(--color-surface);
  color: var(--color-fg);
  border-radius: var(--radius-pill);
  padding: 2px var(--space-3);
  font-size: var(--font-size-xs);
  cursor: pointer;
}
.an-ask__chip:hover { border-color: var(--color-accent); color: var(--color-accent-text); }
.an-ask__hint { font-size: var(--font-size-xs); color: var(--color-muted); }
.an-ask__out:not(:empty) { margin-top: var(--space-3); }
.an-ask__loading { font-size: var(--font-size-sm); color: var(--color-muted); display: flex; align-items: center; gap: 6px; }
.an-ask__q { font-size: var(--font-size-sm); color: var(--color-muted); margin-bottom: var(--space-2); }
.an-ask__a {
  font-size: var(--font-size-sm);
  line-height: 1.8;
  color: var(--color-fg);
  background: var(--color-surface-2);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}
/* 来源标注：**必须可见** —— 用户要知道这是 AI 解释，权威数字在上方洞察卡（规则算的、可对账） */
.an-ask__src { font-size: var(--font-size-xs); color: var(--color-muted); margin-top: var(--space-2); }
.an-ask__err { font-size: var(--font-size-sm); color: var(--color-danger); }

/* ============================================================================
   权限配置页：按左侧菜单树布局（2026-09-18 乐哥拍板重构）
   四个区块：菜单名 | 页面可见 | 操作权限 | 金额可见
   ⚠️ 金额 chip 是**全局**的（同一个码在多个菜单下出现，勾一处联动其它处），
      因为打码按字段名判断、与页面无关 —— UI 上以「全局生效」提示。
   ============================================================================ */
.perm-toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; padding-bottom: var(--space-3); margin-bottom: var(--space-3); border-bottom: 1px solid var(--color-border-soft); }
.perm-head-row, .perm-menu-row { display: grid; grid-template-columns: minmax(150px, 1fr) 104px 210px 200px; gap: var(--space-2); align-items: center; }
.perm-head-row { padding: var(--space-2) var(--space-3); font-size: var(--font-size-xs); color: var(--color-muted); border-bottom: 1px solid var(--color-border-soft); }
.perm-menu-row { padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--color-border-soft); }
.perm-menu-row:last-child { border-bottom: 0; }
.perm-menu-row:hover { background: var(--color-surface-2); }
.perm-menu-row__name { color: var(--color-fg); display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.perm-menu-row__note { font-size: var(--font-size-xs); color: var(--color-muted); }
.perm-menu-row__read, .perm-menu-row__act, .perm-menu-row__money { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.perm-read { display: inline-flex; align-items: center; gap: 4px; font-size: var(--font-size-sm); color: var(--color-fg); cursor: pointer; }
.perm-muted { font-size: var(--font-size-xs); color: var(--color-muted); }
.perm-chip { display: inline-block; padding: 1px 7px; border-radius: var(--radius-sm); font-size: var(--font-size-xs); cursor: pointer;
  border: 1px solid var(--color-border-soft); color: var(--color-muted); background: transparent; user-select: none; }
.perm-chip:hover { border-color: var(--color-border); color: var(--color-fg); }
.perm-chip.is-on { background: var(--color-accent-bg); border-color: var(--color-accent-border); color: var(--color-accent-text); }
.perm-chip--money.is-on { background: var(--color-warning-bg); border-color: var(--color-warning-border); color: var(--color-warning-text); }
.perm-group__head--fold { cursor: pointer; user-select: none; align-items: center; }
.perm-fold { font-size: 11px; color: var(--color-muted); width: 12px; }
.perm-group.is-folded .perm-group__body { display: none; }
.perm-global { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-2) 0; }
.perm-item--inline { align-items: center; }

/* ==========================================================================
   「我的报销」页（2026-09-19）
   新增：替票额度条 / 筛选行（tab + 月份 + 日期口径 + 状态）/ 汇总行 / 驳回原因
   ⚠️ 只用既有 design-tokens 变量，不新造颜色 —— 与全站观感一致（本项目已因"孤例颜色"返工过）
   ========================================================================== */

/* 替票额度条：报销的**前置条件**（选「没有发票」要够额度），故放页面顶部一眼可见。
   2026-09-19 改：原来它是个「灰底 + 30px 按钮」的独立条，与周围两套语言都不搭 ——
     ① 底色用 --color-surface-2（=表头灰），视觉重量接近工具栏，像"第二个工具栏"；
     ② 按钮 30px，而页头按钮 44px、工具栏控件 34px，三套尺寸。
   改成：白底 + 1px 边框（与卡片同一语言），按钮升到 34px（= 工具栏控件高度）。
   ⚠️ 不能把按钮写成 `.btn--sm` 就完事 —— 那样它永远 30px；要在自己的类里显式给高。 */
.tt-bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  margin-bottom: var(--space-6);
  background: var(--color-surface);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
  flex-wrap: wrap;
}
.tt-bar__l { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.tt-bar__k { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.tt-bar__v { font-size: 20px; font-weight: var(--font-weight-medium); color: var(--color-accent-text); }
.tt-bar__hint { font-size: var(--font-size-sm); color: var(--color-muted); }
.tt-bar__r { display: flex; gap: var(--space-2); flex-shrink: 0; }
.tt-bar__r .btn { height: var(--control-height-sm); min-height: var(--control-height-sm); }

/* 状态笔数（工具栏右侧）：与 `.table-toolbar__total` 并排，故沿用同一套字号与颜色。
   2026-09-19 改：原来是一个占整行的自造 `.me-sum`（独立第三行、21px 高），
   现在并入工具栏右侧 —— 规范里统计属于工具栏，不该另占一行。 */
.me-counts { display: inline-flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.me-counts__item {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--font-size-xs); color: var(--color-meta); white-space: nowrap;
}
.me-counts__item b { color: var(--color-fg); font-variant-numeric: tabular-nums; }
.me-counts__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-muted); }
.me-counts__dot--warning { background: var(--color-warning); }
.me-counts__dot--success { background: var(--color-success); }
.me-counts__dot--danger  { background: var(--color-danger); }

/* 驳回原因（要让人知道改什么才能重提，否则只能干等） */
.me-reject { color: var(--color-danger); }

/* ---------- 手机端适配 ---------- */
@media (max-width: 768px) {
  .tt-bar { flex-direction: column; align-items: stretch; gap: var(--space-3); }
  .tt-bar__r { justify-content: stretch; }
  .tt-bar__r .btn { flex: 1; min-height: var(--control-height-touch); }
  .tt-bar__hint { display: none; }          /* 手机上省一行（额度本身已说明一切） */
  /* ⚠️ 手机上工具栏右侧必须能换行（2026-09-19 实测踩到）：
     统计并入工具栏后，`.table-toolbar__right` 是 `display:flex` **无 flex-wrap**、
     且其中 `.table-toolbar__total` 带 `white-space: nowrap`（公共类的防折行，不能动），
     于是 360px 下两者挤在同一行、总额被压掉尾数（截图实测：「合计 ¥2,705.0」被"审批中"盖住）。
     ⇒ 补 wrap + 让状态笔数独占一行。作用域限定在本页的工具栏上，不动公共类。 */
  .toolbar--join-card > .table-toolbar__right {
    flex-wrap: wrap;
    width: 100%;
    margin-left: 0;
    justify-content: flex-start;
  }
  .me-counts { width: 100%; gap: var(--space-3); }
}

/* ==========================================================================
   报销表单 · 分区单列布局（2026-09-19）
   乐哥：「报销提交那个页面，ui 整体调整下，现在做的一点都不直观」
   实测三个问题 → 三个改法：
     ① 因果链被打断（两列布局把「大类→类型→成本类目」拆散）→ **单列**
     ② 长说明占 2-3 行 → 收进每区一句「这一区在干什么」，长说明移进提示
     ③ 字段平铺无分组 → **3 个分区卡片**（带序号）
   ⚠️ 只用既有 design-tokens 变量；配色复用 module-card（全站模块卡的既有语言）
   ========================================================================== */

/* 报销人条：默认本人、不需要输入；点「帮他人报销」才展开下拉 */
.rf-who {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-lg);
}
.rf-who__l { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.rf-who__k { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.rf-who__me { font-size: var(--font-size-base); font-weight: var(--font-weight-medium); }
.rf-who__tag {
  font-size: 11px; color: var(--color-fg-2);
  background: var(--color-surface); border: 1px solid var(--color-border-soft);
  border-radius: 999px; padding: 1px 8px;
}
.rf-who__other { margin-bottom: var(--space-4); }

.rf-req { color: var(--color-danger); }

/* 分区卡片：序号圆点 + 标题 + 一句说明 */
.rf-sec { margin-bottom: var(--space-4); }
.rf-sec:last-child { margin-bottom: 0; }
.rf-sec__head { display: flex; gap: var(--space-3); align-items: flex-start; }
.rf-sec__no {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  background: var(--color-accent); color: var(--color-accent-on);
  font-size: 12px; line-height: 22px; text-align: center; margin-top: 1px;
}
.rf-sec__sub { font-size: var(--font-size-sm); color: var(--color-muted); margin-top: 3px; line-height: 1.5; }

/* 分区内字段：单列、纵向间距均匀 */
.rf-sec .module-card__body > div { margin-bottom: var(--space-4); }
.rf-sec .module-card__body > div:last-child { margin-bottom: 0; }

/* 成本类目：从"第 3 行右侧的一个只读框 + 一行说明"改成**紧跟类型的实时反馈条** ——
   它是「大类 + 类型」算出来的**结果**，必须贴着它的两个输入项，否则读不出因果关系。
   ⚠️ 容器 .rf-alloc-cell 里现在只放一个 hidden input（回传用）+ 这条反馈条。 */
.rf-alloc-cell { margin-top: calc(var(--space-2) * -1); }
.rf-alloc-cell > .field-label { display: none; }      /* 反馈条自带语义，不需要再顶一行标签 */
.rf-alloc {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 9px 12px;
  border: 1px dashed var(--color-border-soft);
  border-radius: var(--radius-md);
  background: var(--color-accent-bg);
}
.rf-alloc__k { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.rf-alloc__v { font-size: var(--font-size-base); font-weight: var(--font-weight-medium); color: var(--color-accent-text); }

/* ---------- 手机端（乐哥：「得适配 pc 和手机页面」）---------- */
@media (max-width: 768px) {
  /* 页头「返回 + 标题」在手机上占两行、且标题被挤成小字 —— 改成单行紧凑 */
  .rf-who { padding: var(--space-2) var(--space-3); }
  .rf-who__me { font-size: var(--font-size-sm); }
  /* 手机上分区之间留白收一半：两屏能看完整个表单 */
  .rf-sec { margin-bottom: var(--space-3); }
  .rf-sec .module-card__body > div { margin-bottom: var(--space-3); }
  .rf-sec__sub { display: none; }      /* 手机上一屏寸土寸金：分区说明省掉（标题已表意） */
}

/* ==========================================================================
   手机端独立页（/h5/expense 等）—— 从飞书工作台点进来时，整屏只放表单
   ⚠️ 用「隐藏」而不是「不渲染」：DOM 结构不动，"退出独立模式"只需去掉类；
      也避免为独立页另建一套骨架（那会带来两套要同步维护的布局）。
   ========================================================================== */
.app-shell.is-standalone .sidebar,
.app-shell.is-standalone .sidebar-backdrop,
.app-shell.is-standalone .page-tabs-bar {
  display: none !important;
}
.app-shell.is-standalone .app-main {
  margin-left: 0;          /* 没有侧栏了，内容列从左边开始 */
  width: 100%;
}
/* 独立页没有标签栏，页面顶部即是内容 —— 把 .page 的上内边距收掉，
   否则会出现一条"本应是标签栏"的空白（实测 24px）。 */
.app-shell.is-standalone .page { padding-top: var(--space-4); }

/* ⭐⭐ 财务模块（**2026-09-21 起 = 与达播同构**：有侧栏 + 有标签栏）
   ──────────────────────────────────────────────────────────────────────────
   这里是**第三次**改它的形态，别把某一版当成唯一答案（演进有价值，记下来）：
     · 2026-09-20 之前：无侧栏 + 无标签栏 —— "独立的一层"，自带模块头（`.fin-top`）
     · 2026-09-20：无侧栏 + **有**标签栏（乐哥「上面的标签该保留的也保留」）
       —— 彼时跨板块入口靠标签栏里那个「公司门户 / 达播工作台 / 星盟财务中心」切换器
     · **2026-09-21（现状）**：**有侧栏 + 有标签栏**
       —— 乐哥「不要这个」（删掉标签栏的切换器）+「财务的左上角也做的和现在达播台一样」
       ⇒ 侧栏回归：顶部品牌区 + 中部财务导航 + 底部「返回门户网站 / 我的」；
         跨板块入口改由**侧栏底部的「返回门户网站」**承担（进财务则走门户页的卡片）。
   ⚠️⚠️ 侧栏必须**显式加 `!important` 覆盖**：上面那条
      `.app-shell.is-standalone .sidebar { display: none !important }` 带 `!important`，
      不写就盖不过它。这是本轮最容易漏的一处 —— 漏了的现象是
      **"侧栏内容都渲染对了，屏幕上却什么也看不见"**（DOM 里有、样式上是 none）。
   ⚠️ 同时 `.app-main` 的左边距必须**还回去**：上面那条 is-standalone 规则把它设成
      `margin-left: 0`（前提是"没有侧栏"）。侧栏回来后要让出 `--sidebar-width`，
      否则内容列会被压在侧栏底下；且 `width: 100%` 要一并改成 `auto`，
      不然"240px 外边距 + 100% 宽"会横向撑出滚动条。
   ⚠️ 用 `.is-finance` 这个**显式标记类**（由 showAppShell 加），不用 `:has(.fin)`：
      后者靠"页面内容恰好带某个类"推断，隔了一层，后来者不易发现。 */
.app-shell.is-standalone.is-finance .sidebar {
  display: flex !important;
}
.app-shell.is-standalone.is-finance .app-main {
  margin-left: var(--sidebar-width);
  width: auto;
}
/* ⚠️⚠️ 移动端必须**显式收回**上面那条 `margin-left`。
   ──────────────────────────────────────────────────────────────────────────
   为什么：`@media` 查询**不改变选择器特异性**。上面那条是 3 个类的选择器，
   而移动端的基础规则只有 1 个类 —— 特异性能赢，于是**桌面的 240px 左边距在手机上照样生效**。
   实测（390×844 视口，本轮真踩）：`.app-main` 的 margin-left 被算成 240px
   ⇒ 财务内容只剩 **150px 宽**（屏宽 390），几乎不可用。
   ⚠️⚠️ 而且这个 bug **不报错、也不产生横向滚动条**（内容自适应收窄了）
      —— 只能靠"在窄视口下量宽度"才看得出来。不要只测桌面就宣布完成。
   ⚠️ 反过来 `.sidebar` 那条**不需要**在移动端收回：
      移动端侧栏抽屉是靠 `transform: translateX(-100%)` 移出屏幕的（见 layout.css），
      `display` 本来就是 flex ⇒ 我那条 `display:flex !important` 与它不冲突。
      （若哪天移动端改成用 `display:none` 隐藏侧栏，这里就必须同步补一条覆盖。） */
@media (max-width: 768px) {
  .app-shell.is-standalone.is-finance .app-main {
    margin-left: 0;
    width: auto;
  }
}
.app-shell.is-standalone.is-finance .page-tabs-bar {
  display: flex !important;
}
.app-shell.is-standalone.is-finance .page {
  padding-top: var(--page-padding);
}
/* ==========================================================================
   系统配置板块（`.is-admin`，2026-09-22 第三轮）
   --------------------------------------------------------------------------
   与财务中心（`.is-finance`）**逐条同构**：都是"独立外壳 + 自己的侧栏 + 显示标签栏"。
   ⚠️ 为什么要重复一遍而不是写成 `.is-finance, .is-admin` 的合并选择器：
      两边将来会各自演进（如系统配置不需要标签栏），合并会让"改一边动两边"。
      本项目的偏好是**显式重复**，代价可控、意图清楚。
   ⚠️ 与财务的唯一差别：**系统配置不需要页面标签栏**（它是配置页，不存在"同时开几个
      配置页来回切"的用法）⇒ 这里**不加** `.page-tabs-bar` 那条。
      加了的后果不是报错，而是标签栏空着一条（因为没有可切换的页），看着像坏了。
   ⚠️⚠️ 移动端那条 `margin-left: 0` 的收回复制理由与上面财务那段**完全一样**
      （`@media` 不改变特异性 ⇒ 桌面规则会在手机上继续生效 ⇒ 内容只剩 ~150px 宽）。
   ========================================================================== */
.app-shell.is-standalone.is-admin .sidebar {
  display: flex !important;
}
.app-shell.is-standalone.is-admin .app-main {
  margin-left: var(--sidebar-width);
  width: auto;
}
@media (max-width: 768px) {
  .app-shell.is-standalone.is-admin .app-main {
    margin-left: 0;
    width: auto;
  }
}
.app-shell.is-standalone.is-admin .page {
  padding-top: var(--page-padding);
}

/* ==========================================================================
   公司门户（`.is-portal`，2026-09-22 深夜新增）
   --------------------------------------------------------------------------
   ⚠️ 门户原来是**没有侧栏**的独立外壳（只有顶部品牌栏 + 卡片网格）。
      现在它自己带了设置页（权限管理 / 用户管理 / 操作日志 / 导出记录）⇒ 需要侧栏导航，
      形态与财务中心/系统配置**逐条同构**，这样"三个板块看起来是一套系统"。
   ⚠️ 与 `.is-admin` 的唯一差别：门户**不要**页面标签栏（同 `.is-admin` 的判断 ——
      它的"页"是极少数几个设置页，没有"同时开几个来回切"的用法，加了会空着一条）。
      ⛔ 反过来 `.is-finance` 那条标签栏是**乐哥明确要求保留**的，别照抄这里去删它。
   ⚠️⚠️ 移动端那条 `margin-left: 0` 的收回复制理由与上面两段**完全一样**：
      `@media` 不改变特异性 ⇒ 桌面那条 240px 左边距会在手机上继续生效，
      内容只剩 ~150px 宽，而且**不报错、也不产生横向滚动条**（内容自适应收窄了）。
   ========================================================================== */
.app-shell.is-standalone.is-portal .sidebar {
  display: flex !important;
}
.app-shell.is-standalone.is-portal .app-main {
  margin-left: var(--sidebar-width);
  width: auto;
}
@media (max-width: 768px) {
  .app-shell.is-standalone.is-portal .app-main {
    margin-left: 0;
    width: auto;
  }
}
.app-shell.is-standalone.is-portal .page {
  padding-top: var(--page-padding);
}

/* 手机安全区：底部按钮不被 iPhone 的小黑条压住 */
body.is-standalone .sf-actions { padding-bottom: env(safe-area-inset-bottom, 0); }

/* 分区布局里的控件要撑满单列宽度。
   ⚠️ 必须显式写：`.form-grid .input{width:100%}` 那条只对 form-grid 生效，
      分区布局不是 form-grid → 金额这类原生 input 会退回默认宽度（实测 ~120px，
      与旁边的日期/下拉宽度不一致，看着像坏了）。 */
.rf-sec .input,
.rf-sec .select,
.rf-sec .textarea {
  width: 100%;
}

/* ==========================================================================
   个人替票页（2026-09-19 改版）—— 余额卡 + 上传替票弹窗
   依据：乐哥「上传替票这个页面，ui也整体优化下」（原截图：弹窗 880px 宽、4 个字段排两列、
        输入框被拉到 412px 宽右侧全空、票面金额填了会怎样没说、底部留白；
        余额卡只有「¥0.00 + 一行 14 字规则」，看不到额度是怎么来的）。
   铁律：容器一律走既有 .card / .module-card 体系（不新造模块容器）；
        颜色全走 CSS 变量（零裸 hex）；类名 .tu- 前缀，不改任何公共类。
   ========================================================================== */

/* —— 余额卡：左「能报多少」 / 右「额度怎么来的」 —— */
.tu-bal__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 260px;
  gap: var(--space-8);
  align-items: center;
}
.tu-bal__label { font-size: var(--font-size-sm); color: var(--color-muted); }
.tu-bal__value {
  margin-top: var(--space-1);
  font-size: var(--font-size-4xl);
  font-weight: var(--font-weight-semibold);
  line-height: var(--font-line-height-tight);
  letter-spacing: var(--font-letter-spacing-display);
  /* ⭐ 红色（乐哥 2026-09-19：「个人替票金额要不要用红色展示啊」）
     ────────────────────────────────────────────────────────────────────
     `--color-price` 是本项目全站既有的「金额语言」，不是新造色：
       · base.css 的 `.price`（货品售价/达播价）
       · components.css 的 `.rmk-hl--money`（备注里的金额）
       · pages.css 的 `.pp-price`、报销单底部合计 `.eb-total__v`
     三处结论性金额都是它 ⇒ 这个「可用替票余额」是同一性质（钱的口径），
     跟着走才一致。
     ⚠️ 它**不是**"错误色"：令牌注释写明「价格：与危险色同源，避免多一套红」，
        `--color-price` 就是 `--color-danger` 的语义别名，两者只是用途不同。
     ⚠️ 列表里的「票面金额」列**保持默认色**（不动）：全站 40+ 处表格的金额列
        都是默认色，整列涂红会成为孤例，且一列 20 个红数字会让人误以为"这些有问题"。 */
  color: var(--color-price);
  font-variant-numeric: tabular-nums;
}
.tu-bal__hint { margin-top: var(--space-2); font-size: var(--font-size-sm); color: var(--color-muted); }
/* 右侧明细用一条竖线与大数字分开：左右是「结论 / 依据」两层关系，不是两组并列数据 */
.tu-bal__side {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-left: var(--space-6);
  border-left: 1px solid var(--color-border-soft);
}
.tu-bal__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--font-size-sm);
}
.tu-bal__row > span { color: var(--color-muted); }
.tu-bal__row > b {
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  font-variant-numeric: tabular-nums;
}

/* —— 上传替票弹窗 —— */
/* 顶部提示条：进弹窗第一眼要知道「这张票会变成什么」，而不是先看到一串要填的框 */
.tu-note {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
  border-radius: var(--radius-md);
  background: var(--color-accent-bg);
}
.tu-note__k { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--color-accent-text); }
.tu-note__v { font-size: var(--font-size-sm); color: var(--color-fg-2); }

/* 分区复用 .rf-sec 的结构（数字圆标 + 标题 + 一句说明），与报销提交页同一套视觉语言。
   但弹窗内**不画分隔线**：弹窗自己已有 footer 分隔线，再画一条会像"这里少了一段内容"。 */
.tu-sec { margin-bottom: var(--space-5); }
.tu-sec--last { margin-bottom: 0; }
.tu-sec .module-card--bare { padding-bottom: 0; border-bottom: none; }

.tu-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4) var(--space-5);
}
.tu-span2 { grid-column: 1 / -1; }
/* 字段说明可能是两行，行高按正文给，否则两行会挤在一起 */
.tu-grid .field-hint { line-height: var(--font-line-height-body); }

/* 金额 → 额度 反馈条：与报销提交页 .rf-alloc 同一视觉语言（虚线 + 淡底），
   全站"某字段会影响某个结果"统一长这样，员工一眼认得。 */
.tu-alloc {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: 9px 12px;
  border: 1px dashed var(--color-border-soft);
  border-radius: var(--radius-md);
  background: var(--color-accent-bg);
}
.tu-alloc__k { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.tu-alloc__v {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  color: var(--color-accent-text);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   上传替票 · 「拍发票」大上传框（2026-09-19 第二次改版）
   ──────────────────────────────────────────────────────────────────────────
   改版前是一个 160×160 的小虚线框、文案只写「点击上传图片」，而且它在最下面
   （照片是"选填、收尾补一下"的定位）。现在照片成了**动线的第一步**
   （拍完自动识别 → 后面几项就不用打了），所以它必须：
     · 大（整宽、够高），一眼看出"这里是起点"
     · 文案说清拍了会发生什么（"会自动读出票面信息"）
     · 传完直接显示预览 —— 让人确认"拍的就是这张票"，而不是只留个文件名
   ⚠️ 颜色全走既有令牌：主色虚线框（与报销单的必传凭证同一语言 = "要你做动作"），
      完成后转成功色（"这一步做完了"）。
   ========================================================================== */
.tu-shot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  min-height: 132px;
  padding: var(--space-4);
  border: 1.5px dashed var(--color-primary-200);
  border-radius: var(--radius-md);
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-family: inherit;
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
    background var(--motion-fast) var(--motion-ease-standard);
}
.tu-shot:hover { border-color: var(--color-accent); }
.tu-shot__ico { display: flex; }
.tu-shot__t { font-size: var(--font-size-base); font-weight: var(--font-weight-medium); }
.tu-shot__s { font-size: var(--font-size-xs); color: var(--color-muted); }
/* 识别中：与普通框同高，避免"点完框突然变矮、下面的字段往上跳" */
.tu-shot--busy {
  flex-direction: row;
  gap: var(--space-2);
  cursor: default;
  border-style: solid;
  border-color: var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-fg-2);
  font-size: var(--font-size-sm);
}
.tu-shot__spin {
  width: 14px; height: 14px; flex: 0 0 auto;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: tu-spin .8s linear infinite;
}
@keyframes tu-spin { to { transform: rotate(360deg); } }
/* 已上传：预览图 + 状态行 */
.tu-shot__wrap { width: 100%; }
.tu-shot__img {
  display: block;
  width: 100%;
  max-height: 200px;
  object-fit: contain;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  /* 白底：发票是白纸黑字，透明/深色底会让它看着像"缺了一块" */
  background: #fff;
}
.tu-shot__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-success-text);
}
.tu-shot__bar > span { flex: 1; }

/* 「自动识别」小标：挂在被识别出来的字段标签后面。
   ⚠️ 它的作用不是装饰，而是**告诉用户"这几项是机器填的，重点核对它们"** ——
      全填完却不说哪些是机器填的，用户会把整张表都当成"自己填的、肯定没错"。 */
.tu-auto {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: var(--radius-full);
  background: var(--color-success-bg);
  color: var(--color-success-text);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-regular);
  vertical-align: 1px;
}

/* —— 手机端（乐哥：「得适配 pc 和手机页面」）—— */
@media (max-width: 768px) {
  .tu-bal__grid { grid-template-columns: 1fr; gap: var(--space-4); }
  /* 竖线改横线：单列后"左/右"变成"上/下"，竖线留着会像一条没意义的竖杠 */
  .tu-bal__side {
    padding-left: 0;
    padding-top: var(--space-4);
    border-left: none;
    border-top: 1px solid var(--color-border-soft);
  }
  .tu-grid { grid-template-columns: 1fr; }
  .tu-shot { min-height: 116px; }
  .tu-shot__img { max-height: 160px; }
}

/* ==========================================================================
   多明细报销表单（2026-09-19 乐哥需求 2+3）
   「抬头 + 多笔明细」——填一遍提交多笔，不用一笔一笔分开报。
   铁律：容器走既有 .card / .dt 体系，颜色全走 token（零裸 hex）。

   ▸ 2026-09-19 第二次改版（乐哥：「ui优化下，看要不要参考新建达人那种（只是不要 tab），
     然后部分框加带颜色的框」）：
     骨架从「一整张大 .card」换成 **新建达人页同款** ——
       UI.headNav（返回 + 标题）+ .sf-shell--modules + .sf-body__content（1120 限宽）
       + 一列 .module-card（分区卡）+ 底部 .form-actions 操作条。
     与新建达人的唯一差别就是**没有 .sf-tabs**（报销单是一条纵向动线，不需要切分组）。
     ⚠️ 彩色框只在 4 处（语义化，不是装饰），全部走既有语义令牌：
       汇总条=主色 / 关联排期=主色 / 代报条=警示 / 必传凭证=主色虚线。
       依据 docs/UIUX设计-达播工作台.md 的「绝对禁令 #1」——
       彩色描边只能用于**语义块**（如 --color-warning-border 注释里写的"待结算卡片外框"），
       页面级卡片一律白底 + 中性描边。
   ========================================================================== */
/* 分区卡标题栏右侧那句补充说明（跟着标题走，不另起一行）
   ⚠️ 原来还有一条 `.eb-head__t`（大标题）—— 标题已改由 `.module-card__title` 承担，
      该条已无引用，本轮删掉（本项目踩过"用了不存在的类名"，反向的"定义了没人用的类名"
      同样是死代码，会让下次改版误以为它还有用）。 */
.eb-head__s { font-size: var(--font-size-sm); color: var(--color-muted); }
.eb-head__s--end { margin-left: auto; }

/* 报销人条（默认本人、不让人手输）。代报时切成警示色 —— 见 .eb-who--proxy */
.eb-who {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3); margin-bottom: var(--space-4);
  border: 1px solid var(--color-border-soft); border-radius: var(--radius-md);
  background: var(--color-surface-2);
}
/* 「帮他人报销」展开后：这一条变成"你在替别人报"的提醒。
   用 --color-warning-border / --color-warning-bg-soft（设计规范里给"待结算卡片外框"的那对令牌），
   语义正好：这是一个"要注意"的状态，不是错误（所以不用红）。 */
.eb-who--proxy {
  border-color: var(--color-warning-border);
  background: var(--color-warning-bg-soft);
}
.eb-who__k { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.eb-who__me { font-size: var(--font-size-base); font-weight: var(--font-weight-medium); }
.eb-who-other { margin-bottom: var(--space-4); }

/* 抬头字段网格：两列，长字段（排期）占满整行 */
.eb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
.eb-fld { display: block; }
/* 「票面要素」那一行（2026-09-20）。
   ⚠️ 它**曾经是 3 个手填输入框**（号码/代码/开票日期），现已改成只读汇总 ——
      但仍用 auto-fit 而不是写死列数：窄屏时自动堆成一列，
      否则「票面金额 + 对不上 + 票面要素」三格挤在 375px 里会看不全
      （票号有 20 位）。 */
.eb-grid--inv { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
/* 票面金额那一行（2026-09-20）—— 原来这里是三个手填输入框，
   改成"只读的票面金额汇总 + 可展开的要素清单"（乐哥：「发票代码、发票号这些
   是不是可以不用填了？…用逗号隔开我觉得没意义」）。
   ⚠️ 只读值必须**看起来就不能填**（无边框、无输入框外观）——
      做成 input[readonly] 会让人以为要填、点了没反应。
   ⚠️ 高度与输入框**对齐**（min-height 44px）：它与「费用类型」等字段同处一个网格，
      矮一截会让整行歪掉（本项目对这 1~4px 错位一贯要求对齐）。 */
.eb-ro {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  min-height: 44px; padding: 0 var(--space-3);
  border: 1px dashed var(--color-border-soft); border-radius: var(--radius-md);
  background: var(--color-surface-2);
  font-size: var(--font-size-sm); color: var(--color-fg-2);
}
.eb-ro__v { font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
.eb-ro__n { font-size: var(--font-size-xs); color: var(--color-meta); }
.eb-ro__empty { color: var(--color-meta); }
.eb-ro__why { font-size: var(--font-size-xs); color: var(--color-meta); }
/* 报销金额 > 票面金额的提醒（只提醒不拦）—— 用警告色，但要能一眼看出是"待核对"不是"报错" */
.eb-ro--warn {
  border-style: solid; border-color: var(--color-warning-text);
  background: var(--color-warning-bg); color: var(--color-fg-2);
}
.eb-ro--warn .eb-ro__why { color: var(--color-warning-text); }
.eb-ro__lnk {
  border: none; background: transparent; padding: 0; cursor: pointer;
  color: var(--color-accent-text); font-size: var(--font-size-sm);
}
.eb-ro__lnk:hover { text-decoration: underline; }

/* 票面要素清单（默认 hidden，点「点开查看」才展开）——
   三栏从界面上拿掉了，但要素必须**有地方能看**（财务核对要用）。
   ⚠️ 默认收起而不是展开：它是"给财务核对用"的明细，
      填单的人不需要天天看到 20 位的票号（那是纯噪音）。 */
.eb-invpanel {
  margin-top: var(--space-2); padding: var(--space-3);
  border: 1px solid var(--color-border-soft); border-radius: var(--radius-md);
  background: var(--color-surface-2);
}
.eb-invpanel__row {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: 5px 0; border-bottom: 1px solid var(--color-border-soft);
  font-size: var(--font-size-xs);
}
.eb-invpanel__row:last-of-type { border-bottom: none; }
.eb-invpanel__n {
  flex: 0 0 auto; width: 18px; height: 18px; line-height: 18px; text-align: center;
  border-radius: 50%; background: var(--color-surface); color: var(--color-meta);
  font-size: 11px;
}
.eb-invpanel__src { flex: 0 0 auto; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--color-meta); }
.eb-invpanel__f { color: var(--color-meta); }
.eb-invpanel__f b { color: var(--color-fg-2); font-weight: var(--font-weight-medium); font-variant-numeric: tabular-nums; }
.eb-invpanel__amt { margin-left: auto; font-weight: var(--font-weight-medium); color: var(--color-fg); }
.eb-invpanel__tip { margin-top: 8px; font-size: var(--font-size-xs); color: var(--color-meta); line-height: 1.6; }

.eb-fld .input, .eb-fld .select { width: 100%; }
.eb-fld--full { grid-column: 1 / -1; }

/* 关联排期块：整块套一层主色浅底框。
   为什么单独上色：它是**只有「达人溯源直播」才出现**的必填项，也是这张单
   "钱算到哪场"的唯一依据 —— 漏选会把达人成本错记成项目成本（不报错、只错账）。
   给它一圈主色，是为了让它在一屏字段里一眼被看见。 */
.eb-schedbox {
  border: 1.5px solid var(--color-primary-200);
  border-radius: var(--radius-md);
  background: var(--color-accent-bg);
  padding: var(--space-3) var(--space-4);
}

/* ⚠️ 已删除（2026-09-19 晚，改版后无引用，属于"定义了没人用的类名"）：
   · `.eb-sum` / `.eb-sum__k` / `.eb-sum__v` / `.eb-sum__auto`
     —— 汇总条从卡片内移到了底部固定条，见下面的 `.eb-total*`
   · `.eb-chips` / `.eb-chip[data-sid]` / `.eb-chip__d` / `.eb-chip__x` / `.eb-chip--add`
     —— 排期从"自绘胶囊 + 弹窗"换成了 `UI.schedulePicker`（它自带胶囊样式 `.fp-chip`）
   删而不留的理由：本项目踩过"用了不存在的类名"（卡片头挤成一坨），
   反向的"定义了没人用的类名"同样是负债 —— 下次改版会以为它还有用、照着改。 */

/* 报销人条上的「本人 / 代报」小标签 —— **仍在用**，所以保留 */
.eb-chip--tag { height: 22px; font-size: var(--font-size-xs); color: var(--color-muted); background: var(--color-surface); }

/* ==========================================================================
   底部固定操作条（合计 + 取消/提交）（2026-09-19 乐哥：
   「累计的这个报销费用，要不要一直放在页面右下角」）
   ─────────────────────────────────────────────────────────────────────────
   ⭐ 依据（调研 Expensify / Ramp / 飞书审批后定的）：
      Ramp 的官方说明原话是 "one row per purchase, then subtotals by category
      and a grand total" —— **总计在最后**；三家成熟系统都把合计与"提交"放在同一区。
      我们原来把合计夹在「整单信息」和「费用明细」之间 ⇒ 滚到明细区就完全看不见，
      而这恰恰是用户最需要看到合计的时刻（正在一笔一笔加钱）。

   ⚠️ 用 `position: sticky; bottom: 0`，**不是** `fixed`：
      fixed 脱离文档流，页面短时会浮在半空、窄屏会压住内容；
      sticky 只在"它自己那一行可见"时才粘住，页尾自然落位。全站首次用这个形态，
      已单独验证过（不遮内容、不盖侧栏、手机端正常）。
   ⚠️ `z-index` 要低于侧栏遮罩与弹层（它们用 --z-modal），
      否则日历弹窗会被这条压住。 */
/* ⚠️ sticky / 白底 / 上边线放在**外层**（整宽），限宽放在内层 ——
   两者不能合成一层：合了之后白条只有 1120 宽，两侧会透出页面灰底、
   "条"就不贯穿了（达人表单的 .form-actions 正是这个两层结构）。
   ⚠️ 覆盖达人表单那条公共规则（`.sf-shell > .form-actions` 自带 border-top + 白底），
      用 `--total` 变体精确作用于本页，**不动达人表单**。 */
.sf-shell > .form-actions--total {
  display: block;
  padding: 0;
  position: sticky;
  bottom: 0;
  z-index: 5;
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  /* 顶部一点投影，让它"浮"在明细之上，暗示下面还有内容 */
  box-shadow: 0 -2px 10px color-mix(in srgb, var(--color-gray-900) 6%, transparent);
}
/* 内层限宽（跟随 --form-max-width）居中 —— 合计靠左、按钮靠右，两端都与内容列对齐
   ⭐ 2026-09-28 硬编码 1120 → var(--form-max-width)（「对齐链」第 ④ 环）：
      它是多明细提单页的底部合计条，与 `.sf-body__content` 同宽才对得上；
      不改会变成"内容 1280 / 合计条 1120"，两端各错 80px。 */
.sf-shell > .form-actions--total .eb-total__inner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  max-width: var(--form-max-width);
  margin: 0 auto;
  padding: var(--space-3) var(--space-6);
}
.eb-total__k { font-size: var(--font-size-sm); color: var(--color-fg-2); }
.eb-total__v {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
  /* ⭐ 红色（乐哥定的）：--color-price 是本项目全站既有的"金额语言"
     （货品价格 .price、备注里的金额 .rmk-hl--money 都用它），
     与主营业务里"金额用红"的习惯一致。它不是"错误色"——
     --color-price 在令牌里就是 --color-danger 的语义别名，注释写着"价格：与危险色同源"。 */
  color: var(--color-price);
}
.eb-total__n { font-size: var(--font-size-xs); color: var(--color-muted); }
/* 按钮组推到最右（与"合计在最左"形成两端对齐） */
.eb-total__btns { display: flex; gap: var(--space-3); margin-left: auto; }

/* --------------------------------------------------------------------------
   住宿的「入住 → 离店」：**一个字段分区内**并排两个日期（乐哥：
   「住宿的开始入住和离店，要不要也放同一行，这样填起来更直观」）
   ⚠️ 关键：合成一格（而不是让两个独立单元格"凑"到同一行）。
      在 CSS Grid 里，两个单元格能否同行取决于它前面有几格 ——
      住宿时前面只有"费用类型"1 格 ⇒ 两个日期各占 1 格正好被推到两行；
      以后往前加一个字段又会被打散。合成一格就与"前面有几个字段"无关。
   -------------------------------------------------------------------------- */
.eb-range { display: flex; align-items: center; gap: var(--space-2); }
.eb-range__item { flex: 1 1 0; min-width: 0; }
.eb-range__sep { flex: 0 0 auto; color: var(--color-muted); font-size: var(--font-size-sm); }

/* ⚠️⚠️ 日期控件高度必须对齐标准输入框（44px）。
   `DateTimePicker` 的触发按钮默认 40px（见 datetime-picker.css 的 .dtp-trigger），
   而全站 `.input` / `.select` 是 44px ⇒ 不补这条，日期框会比旁边的输入框**矮 4px**，
   一行里两个框高低不齐（本项目对这类 1~4px 错位一贯要求对齐）。 */
.eb-fld .dtp-trigger { height: 44px; }

/* 明细区：明细卡直接坐在分区卡里，不再需要独立的分区标题行
   （标题已由 .module-card__head 承担） */
.eb-dt .dt__bd { padding: var(--space-4); }
.eb-dt .eb-grid { gap: var(--space-4) var(--space-4); }

/* 「再加一笔」——放在明细分区卡的底部，整宽虚线按钮 */
.eb-add {
  width: 100%; margin-top: var(--space-3); height: 44px;
  border: 1px dashed var(--color-border); border-radius: var(--radius-lg);
  background: transparent; color: var(--color-accent-text);
  font-size: var(--font-size-base); font-family: inherit; cursor: pointer;
}
.eb-add:hover { background: var(--color-accent-bg); border-color: var(--color-accent); }

/* 凭证上传格
   ⚠️ 必传的两格（支付凭证 / 发票）用**主色虚线**而不是原来的灰虚线 ——
      灰虚线看着像"选填的装饰位"，而这两格不传就提交不了。
      已传的仍是绿色实线（.eb-up--done），"传没传"一眼可辨。 */
.eb-grid--up { margin-top: var(--space-4); }
.eb-up {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: 44px; padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--color-primary-200); border-radius: var(--radius-md);
  background: var(--color-accent-bg); color: var(--color-accent-text);
  font-size: var(--font-size-sm); cursor: pointer;
}
.eb-up:hover { border-color: var(--color-accent); }
/* 走替票时发票那格是"不用传"的说明 —— 退回中性灰，别让它看着还能点 */
.eb-up--na {
  border-style: solid; border-color: var(--color-border-soft);
  background: var(--color-surface-2); color: var(--color-muted);
  cursor: default; font-size: var(--font-size-sm);
}
.eb-up--done { border-style: solid; border-color: var(--color-success); background: var(--color-success-bg); color: var(--color-success-text); cursor: default; }
.eb-up__img { width: 32px; height: 32px; object-fit: cover; border-radius: var(--radius-sm); flex: 0 0 auto; }
.eb-up__pdf {
  flex: 0 0 auto; padding: 2px 6px; border-radius: var(--radius-sm);
  background: var(--color-danger-bg); color: var(--color-danger); font-size: var(--font-size-xs);
}
.eb-up__name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--font-size-xs); }
.eb-up__del { border: none; background: transparent; color: var(--color-danger); font-size: var(--font-size-xs); cursor: pointer; }
/* ⚠️ `.eb-up` / `.eb-up--done` 等**单文件时代**的样式已无引用（2026-09-19 深夜改多文件）——
   被下面 `.eb-files` 系列取代。这里**不删**这两个已无引用的类：
   `.eb-up--na`（走替票时发票那格的"不用传"说明）**仍在用**，它与 `.eb-up` 同族、
   删掉基类会让下一条规则无从参照。留着只占几行、不产生视觉效果。 */
.eb-opt { color: var(--color-muted); font-weight: var(--font-weight-regular); font-size: var(--font-size-xs); }

/* ==========================================================================
   多文件上传区（2026-09-19 深夜，乐哥：「需要支持多个文件上传」「支持拖入、粘贴」）
   ──────────────────────────────────────────────────────────────────────────
   形态：已传的文件排成一行 54px 方块（缩略图），末尾一个「＋」追加格。
   ⭐ 为什么横排而不是"一行一个"（实测数据支撑）：
      这块可用宽 **474px** ⇒ 一排能放 **7 个**（54 格 + 8 间距 = 62px/格，474÷62=7）。
      而"一行一个"3 个文件就 185px（比旁边「发票」格高 84px），明细卡跟着变高。
      放不下时会自动折行（flex-wrap 的自然行为）。
      ⚠️⚠️ 实测数据（2026-09-20 逐档探针，**方块的尺寸改过，数据要跟着重测**）：
          · 方块 44px 时：每格占 52px ⇒ 一排 8 个；0/3/6 个 = 46px，8/10 个 = 98px（两行）
          · 方块 **54px 时（当前）**：每格占 62px ⇒ 一排 **7 个**；
            实测 9 个文件 = **118px**（7 + 3 两行）
          ⚠️ 两次都踩过同一件事：**把估算值当实测值写进注释**（44 那版写成"10 个也只占 44px"）。
             ⇒ 凡"能放几个 / 多高"的结论，一律**改完后用探针逐档重量一遍**再写。
   ⚠️ 还有一个"乘数效应"要记住：**方块尺寸 → 每行容量 → 折行阈值 → 上传格高度 → 明细卡高度**。
      所以以后调方块大小时，上面这串数据全都要重测（不是只改一个数就完事）。

   ⚠️ 高度锁在一档固定值（`min-height`），**两格必须等高、且各状态下不变**：
      它要和右列「发票」那格永远顶部对齐 —— 两格高度不同会让整个明细卡的网格歪掉
      （本项目对这 1~4px 错位一贯要求对齐，见 .eb-range 上方那段）。
      ⚠️ 2026-09-20 起空态引导（"点击选择、拖入或粘贴截图"）放进了框**里面**、
         与「＋」**同一行** —— 是行内元素、不新起一行，所以不影响高度。
         （曾经的做法是把这句并进框下面的 field-hint 那一行；那样「发票」那格
          底下空着、用的人完全不知道能拖能粘，见 .eb-guide 上方那段。）
   ⚠️⚠️ 2026-09-20 **再次调高**（乐哥：「上传支付凭证，还有发票的框框要不要高一点，
       现在太矮了」）。量化了"矮"在哪：原来是方块 44px 塞进 46px 容器，
       **上下各只剩 1px** —— 几乎顶到虚线边框，所以显局促
       （对比其他输入框也是 44px，但里面是文字、居中后显得饱满）。
       ⇒ 改为容器 **68px** + 方块 **54px**（上下各留 7px），空态和已传文件都宽松，
         缩略图同时放大约 23%（看发票照片更容易分辨）。
       ⚠️ 代价是每笔明细卡 +22px（5 笔多 110px）—— 这是当时对比三档后乐哥选的档位，
          将来若要再调，别只改容器高度而不改方块（那就退回"只有留白变多"的 A 档观感）。
   ⚠️ 颜色全部走既有 token：主色虚线 + 主色浅底（与旧的单文件上传格同一套语言），
      拖入高亮复用全站既有的 `.upload-drop.is-dragover` 配色（主色实边）。
   ========================================================================== */
.eb-files {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  /* 68px（2026-09-20 调高，乐哥「框框要不要高一点，现在太矮了」）——
     原先 44px 时方块 44px 上下只剩 1px，顶着边框显局促。 */
  min-height: 68px; padding: 0 var(--space-3);
  border: 1px dashed var(--color-primary-200); border-radius: var(--radius-md);
  background: var(--color-accent-bg);
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
              background var(--motion-fast) var(--motion-ease-standard);
}
.eb-files:hover { border-color: var(--color-accent); }
.eb-files:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* 拖入高亮（复用全站 .is-dragover 语言：主色实边 + 稍深底） */
.eb-files.is-dragover { border-style: solid; border-color: var(--color-accent); background: var(--color-primary-50); }
/* 粘贴目标：点过这一格之后才标出来 —— 让"Ctrl+V 会粘到哪"一眼可见。
   ⚠️ 用**实线 + 主色**与"拖入中"区分不开也没关系：两者都是"这一格是当前目标"，
      而且拖入时用户手上有鼠标、粘贴时看的是边框，不会同时发生。 */
.eb-files.is-paste-target { border-style: solid; border-color: var(--color-accent); }
/* 上传中：降低不透明度，让"正在传"有反馈（不转圈 —— 这一格太矮，圈会挤掉方块） */
.eb-files.is-uploading { opacity: .6; pointer-events: none; }

/* ── 空态引导（2026-09-20 乐哥「UI 升级下」）────────────────────────────────
   改前：框里空态只有一个「＋」，右边一大片空白 —— 看不出这格能放几个、
        更看不出可以拖入或粘贴；说明还躲在框**下面**的小字里，且只有「支付凭证」
        那格有。改后引导语进框、与「＋」同一行。
   ⚠️ 是**行内元素、不新起一行** ⇒ 高度仍是 44px（+边框=46），
      与「发票」那格永远对齐 —— 这是这块最不能破的不变量。 */
.eb-guide { font-size: var(--font-size-xs); color: var(--color-meta); white-space: nowrap; }
.eb-guide b { color: var(--color-accent-text); font-weight: var(--font-weight-medium); }

/* 三种状态同一时刻只显示一条；默认只留 idle。
   ⚠️ 用 CSS 切而不是在回调里改 textContent：dragover 每秒触发几十次，
      重建文本节点既浪费、文案还会闪；切类名则由浏览器合成，零抖动。 */
.eb-guide__drop, .eb-guide__paste { display: none; }
.eb-files.is-dragover .eb-guide__idle { display: none; }
.eb-files.is-dragover .eb-guide__drop { display: inline; }
.eb-files.is-paste-target:not(.is-dragover) .eb-guide__idle { display: none; }
.eb-files.is-paste-target:not(.is-dragover) .eb-guide__paste { display: inline; }
/* 已有文件时**不显示任何引导文字**（连拖入/粘贴的实时提示也不显示）：
   ⚠️⚠️ 这里踩过一个边缘问题：文件多到快占满一行时（例如 8 个 ⇒ 已占 424px、
      只剩 47px），那条提示文字（约 112~144px）放不下就会**折行**，
      于是"点一下这一格"会让整格从 46px 跳到 92px、点别处又跳回来 —— 明显的抖动。
   ⭐ 取舍：有文件时不靠文字确认，改靠**边框变实线 + 主色**（空间反馈仍然明确：
      换另一格会立刻看到实线边框移过去）。用户此时已经用过这一格，
      不需要再读一遍"点击选择、拖入或粘贴"；而**高度恒定**比多一句提示更值。
   ⚠️ 隐藏的是**整条 `.eb-guide`**、不是里面的子元素：子元素 display:none 后
      外层 span 仍是 flex item、仍占一个 8px 的 gap，会在「＋」后留出像 bug 的空隙。 */
.eb-files.has-files .eb-guide { display: none; }

/* 单个文件方块（54×54）—— ⚠️ 比上传格（68px）矮 14px，上下各留 7px 的呼吸；
   ⚠️ 这两个数要**成对**改：只改容器高度会让方块重新贴边（退回"只有留白变多"的观感）。 */
.eb-file {
  position: relative; width: 54px; height: 54px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  background: var(--color-surface); overflow: visible;
}
.eb-file__img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-md); display: block; }
/* PDF 没有缩略图 → 显示 PDF 标签（不要留空白，否则看着像没传成功） */
.eb-file__t {
  /* 方块 44→54 后，只靠原来的 xs 字号会显得空 —— 提到 sm */
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
  color: var(--color-danger);
}
.eb-file--pdf { background: var(--color-danger-bg); border-color: var(--color-border); }
/* 已满上限时末尾那个数字格 */
.eb-file--full { font-size: var(--font-size-xs); color: var(--color-muted); background: var(--color-surface-2); }

/* 移除按钮：贴在方块右上角（负偏移）—— 方块只有 44px，放里面会盖住缩略图 */
.eb-file__x {
  /* 方块变大后按钮也略放大（16→18），否则在一个 54px 的方块上显得过小、不好点 */
  position: absolute; top: -6px; right: -6px; width: 18px; height: 18px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-border); border-radius: 50%;
  background: var(--color-surface); color: var(--color-muted);
  cursor: pointer; line-height: 1;
  transition: background var(--motion-fast) var(--motion-ease-standard),
              color var(--motion-fast) var(--motion-ease-standard);
}
/* ⚠️ 白字用 `var(--color-surface)`（= #FFFFFF）而不是裸 `#fff` ——
   本页有门禁"报销表单样式里不许出现裸十六进制颜色"，且全站既有惯例也是这么写的
   （见 components.css 的 .link-btn 区域）。 */
.eb-file__x:hover { background: var(--color-danger); border-color: var(--color-danger); color: var(--color-surface); }

/* 「＋」追加格：与文件方块同尺寸，虚线 → 一眼看出"这里还能加" */
.eb-file-add {
  width: 54px; height: 54px; flex: 0 0 auto; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--color-primary-200); border-radius: var(--radius-md);
  background: var(--color-surface); color: var(--color-accent-text);
  cursor: pointer;
}
.eb-file-add:hover { border-color: var(--color-accent); background: var(--color-accent-bg); }

/* 手机端：方块收到 40px（一行能多放一个），移除按钮稍微往内收一点防出界 */
@media (max-width: 768px) {
  .eb-file, .eb-file-add { width: 40px; height: 40px; }
  .eb-file__x { top: -4px; right: -4px; }
}

/* 排期选择弹窗 */
.eb-ps-list { max-height: 320px; overflow-y: auto; margin-top: var(--space-3); }
.eb-ps-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); cursor: pointer;
}
.eb-ps-row:hover { background: var(--color-surface-2); }
.eb-ps-row.is-on { background: var(--color-accent-bg); }
.eb-ps-inf { flex: 1; font-size: var(--font-size-sm); }
.eb-ps-d { font-size: var(--font-size-xs); color: var(--color-muted); font-variant-numeric: tabular-nums; }
.eb-ps-empty { padding: var(--space-6); text-align: center; color: var(--color-muted); font-size: var(--font-size-sm); }

/* 手机端：单列 + 汇总不换行截断 */
@media (max-width: 768px) {
  .eb-grid { grid-template-columns: 1fr; }
  .eb-sum { flex-direction: column; align-items: flex-start; gap: var(--space-1); }
  .eb-chip { height: 32px; }
}

/* 明细卡片头（.dt__*）—— 2026-09-19 补：
   ⚠️ 这几个类名我先在原型里用了、也在产品代码里直接用了，但**产品 CSS 里从没定义过**
      （原型是独立文件、自带样式，所以原型看着正常）。实测截图发现卡片头变成
      「1交通出行¥860.00删除」全挤成一坨 —— 典型的"用了不存在的类名却不报错"。 */
.eb-dt .dt__hd {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-2);
  border-bottom: 1px solid var(--color-border-soft);
}
.eb-dt .dt__no {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  background: var(--color-accent); color: var(--color-accent-on);
  font-size: 12px; line-height: 22px; text-align: center;
}
.eb-dt .dt__kind { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); }
.eb-dt .dt__amt {
  margin-left: auto; padding-left: var(--space-3);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
  font-variant-numeric: tabular-nums;
}
.eb-dt .dt__del {
  flex: 0 0 auto; border: none; background: transparent;
  color: var(--color-danger); font-size: var(--font-size-sm);
  cursor: pointer; padding: 0 var(--space-1);
}
.eb-dt { border: 1px solid var(--color-border-soft); border-radius: var(--radius-lg); background: var(--color-surface); }
.eb-dt + .eb-dt { margin-top: var(--space-3); }

/* ==========================================================================
   「我的报销」列表按单折叠（2026-09-19，配合多明细报销单）
   ─────────────────────────────────────────────────────────────────────────────
   一张报销单含多笔明细 → 列表折叠成一行摘要（几笔 · 合计 · 事由 · 日期范围 · 状态），
   点击展开看每笔。**单笔的单不折叠**（直接是普通行）—— 库里 260 条多为历史单笔，
   一律折叠等于给所有人凭空多一次点击。

   ⚠️ 展开/收起只切 class（`tr.me-item--child.is-open`），不重新渲染：
      重渲染会丢滚动位置，还会把"正在看第 5 笔"变成"回到列表顶部"。
   ========================================================================== */
.me-sheet > td {
  padding: 0;                             /* 内边距交给内部 .me-sheet__in，避免 td 直接做 flex */
  background: var(--color-surface-2);
  cursor: pointer;
}
.me-sheet:hover > td { background: var(--table-row-hover); }   /* 与全站表格 hover 同一色，别自造 */
.me-sheet__in {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  flex-wrap: wrap;
}
.me-sheet__caret { color: var(--color-muted); font-size: var(--font-size-sm); width: 12px; }
.me-sheet__n { font-weight: var(--font-weight-medium); }
.me-sheet__amt { font-weight: var(--font-weight-medium); font-variant-numeric: tabular-nums; }
.me-sheet__reason { color: var(--color-fg); }
.me-sheet__range { color: var(--color-muted); font-size: var(--font-size-sm); }
.me-sheet__st { margin-left: auto; }

/* 折叠的子明细行：默认收起；展开只在"有 .is-open"时出现 */
tr.me-item--child { display: none; }
tr.me-item--child.is-open { display: table-row; }
/* 缩进一小格 + 左侧竖线，让人一眼看出"这几行属于上面那张单" */
tr.me-item--child > td:first-child {
  padding-left: var(--space-6);
  box-shadow: inset 2px 0 0 var(--color-border-soft);
}

/* 凭证/发票缩略图（约 2KB 的本地小图，原图在飞书） */
.me-thumb {
  display: block; float: left; width: 28px; height: 28px; margin: 0 var(--space-2) 0 0;
  object-fit: cover; border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-sm); cursor: zoom-in;
}
/* 「+N」（2026-09-19 深夜，多文件附件）：跟着缩略图一起 float: left，
   尺寸与缩略图一致（28px）—— 让它读起来是"这一排里的一个"，而不是另一段文字。 */
.me-thumb-more {
  display: flex; float: left; align-items: center; justify-content: center;
  width: 28px; height: 28px; margin: 0 var(--space-2) 0 0;
  border: 1px solid var(--color-border-soft); border-radius: var(--radius-sm);
  background: var(--color-surface-2); color: var(--color-muted);
  font-size: var(--font-size-xs); cursor: default;
}

/* 明细卡片「说明（备注）」尾行（2026-09-19 乐哥指定字段顺序）
   顺序：费用类型 → 类型相关（交通方式/出发到达/日期）→ 金额 → 发票类型
        → 支付凭证 → 发票 → **说明**（这里）
   ⚠️ 独立成一段而不是塞进上面的两列网格：备注可能写得较长（"汉庭王府井店 3 晚"），
      挤在半列里看不全；也与"钱的字段"分开，扫一眼不会混。 */
.eb-grid--tail { margin-top: var(--space-3); }
.eb-fld--full { grid-column: 1 / -1; }

/* ==========================================================================
   登录页 · 三套门面（2026-09-22 定稿）
   ⭐ 分的是**门面**，不是账号：门户 / 财务 / 达播各看各的门面，
      底层 users / 权限 / 会话完全共用。
      （机制说明见 public/js/pages/login.js 顶部的 entryKind / FACADE）

   ⚠️ 背景图不在这里 —— 在文件前部 `.login-root--hub/--fin/--main .login-hero`
      那三条（包在 `@media (min-width:769px)` 里，为了让小屏不下载图片）。
      这里只定义**门面强调色变量**，它们影响控件（焦点框 / 悬停 / 登录按钮底）。
   ⚠️⚠️ `--login-brand-deep` 是**按钮底色**专用，比 `--color-accent` 深一档：
      `--color-accent` 是品牌标准色（用于焦点框/悬停/图标），拿它当按钮底压白字
      达不到 4.5:1（实测财务金 4.06 / 达播橙 3.67 / 微信绿 2.38）。
      ⇒ **两者不要合成一个**（详见 .sso 的注释）。
   ========================================================================== */
.login-root--hub { --login-brand-deep: #0b5fd9; --color-accent: #0b5fd9; }
/* ⚠️ 是 `--finance` 不是 `--fin`（与 login.js 的 entryKind() 返回值逐字对齐，见上） */
.login-root--finance { --login-brand-deep: #8f6111; --color-accent: #a8741a; }
.login-root--main { --login-brand-deep: #a85505; --color-accent: #c96e08; }
/* ⚠️ 这里原本是一条 `.login-root--fin .login-hero` 的**金色渐变**覆盖规则
   （2026-09-20 财务门面 = 金币¥ + 金色调）—— 2026-09-22 换成背景图后**已删除**：
   留着它是无害的死代码，但会让下一个人以为财务门面还是金色渐变。
   门面的区分现在完全由**背景图 + 上面的强调色变量**承担。
   另有一条"把财务 logo 底衬提亮一档"的覆盖也早已删除：logo 底衬已整体改成**透明**
   （理由见 .login-hero__logo 的注释），不存在底衬可调。 */

/* ==========================================================================
   报销审批（2026-09-22 ·《方案-报销流程重构》）
   三个页面共用一套：审批台（.ap-*）｜报销后台（.xb-*）｜审批流配置（.ef-*）
   ⚠️ 前缀选择：`.eb-*` **已被费用明细的编辑表单占用**（见上面的 eb-grid 那一大段），
      故本次全部另起前缀，避免样式串味（串味不会报错，只会"某个按钮忽然变样"）。
   ========================================================================== */

/* ── 审批台 ───────────────────────────────────────────────────────────── */
.ap-tabs { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; margin-bottom: var(--space-3); }
.ap-detail__head { display: flex; flex-direction: column; gap: 4px; margin-bottom: var(--space-3); }
.ap-no { font-size: var(--font-size-lg); font-weight: 600; }
.ap-meta { color: var(--color-muted); font-size: var(--font-size-sm); }
.ap-meta b { color: var(--color-fg); }

/* 四道进度条 —— 让"卡在哪一道"一眼可见（乐哥要求"审批流程要看得见"） */
.ap-steps { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: var(--space-3) 0; }
.ap-step { display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px 3px 4px;
  border: 1px solid var(--color-border); border-radius: 999px; font-size: var(--font-size-sm);
  color: var(--color-meta); background: var(--color-surface); }
.ap-step i { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
  border-radius: 50%; background: var(--color-surface-2); font-style: normal; font-size: 11px; }
.ap-step--done { color: var(--color-muted); }
.ap-step--done i { background: var(--color-success); color: #fff; }
.ap-step--current { border-color: var(--color-accent); color: var(--color-fg); font-weight: 600; }
.ap-step--current i { background: var(--color-accent); color: #fff; }
.ap-step__line { width: 14px; height: 1px; background: var(--color-border); }

.ap-section { margin-top: var(--space-3); }
.ap-section__title { font-size: var(--font-size-sm); font-weight: 600; margin-bottom: 6px; }
.ap-files { display: flex; flex-wrap: wrap; gap: 8px; }
.ap-files img { width: 88px; height: 88px; object-fit: cover; border: 1px solid var(--color-border);
  border-radius: 6px; display: block; }
.ap-attach { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; }

.ap-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-4);
  padding-top: var(--space-3); border-top: 1px solid var(--color-border); }
.ap-hint { color: var(--color-fg-3); font-size: var(--font-size-sm);
  /* ⚠️ 原为 `--color-bg-subtle` —— **该变量全项目没有定义** ⇒ 底色静默丢失
     （提示条看着就是一段裸文字，没有"这是一个提示块"的边界）。
     改用真实存在的 `--color-surface-2`（全站 hover/表头底，浅灰）。 */
  background: var(--color-surface-2); border-radius: 6px; padding: 8px 12px; margin: var(--space-3) 0; }
.ap-reject { color: var(--color-danger); background: var(--color-danger-bg); border-radius: 6px;
  padding: 8px 12px; margin: var(--space-3) 0; font-size: var(--font-size-sm); }
.ap-logs { margin: 0; padding-left: 18px; font-size: var(--font-size-sm); color: var(--color-muted); }
.ap-logs li { margin-bottom: 4px; }

/* 改动记录（谁改的、改了什么、为什么）—— 后续审批人靠它看清"这单被改过" */
.ap-change { border-left: 3px solid var(--color-warning); padding: 8px 12px; margin-bottom: var(--space-2);
  background: var(--color-warning-bg); border-radius: 0 6px 6px 0; }
.ap-change__head { font-size: var(--font-size-sm); font-weight: 600; }
.ap-change__reason { font-size: var(--font-size-sm); color: var(--color-muted); margin: 2px 0 4px; }
.ap-change ul { margin: 0; padding-left: 18px; font-size: var(--font-size-sm); }
.ap-change s { color: var(--color-meta); }

/* 「修改并同意」弹窗里的逐笔字段网格 */
/* ── 「修改并同意」弹窗的字段区（2026-09-23 重做 · 乐哥：「选项选不了、显示的是代码」）
   ▸ 改前：`.ap-field` 是 `inline-flex` + 固定 168px 输入框 ⇒ 7 个纯文本框挤成一片。
   ▸ 改后：**网格 3 列**，控件宽度跟着格子走（下拉/日期组件都能撑满），
     与提报页（`.eb-grid`）的观感一致。
   ⚠️ 之前 `.ap-field > span` 用的是 `--color-text-secondary` ——
      **该变量全项目没有定义**（实测 grep design-tokens 为空）⇒ 颜色静默回落到继承色。
      统一改成真实存在的 `--color-fg-3`（次要文字色）。 */
.ap-modify-item { border: 1px solid var(--color-border); border-radius: 6px; padding: var(--space-3);
  margin-bottom: var(--space-2); }
.ap-modify-item__head { font-size: var(--font-size-sm); font-weight: 600;
  margin-bottom: var(--space-3); display: flex; align-items: baseline; gap: var(--space-3); }
.ap-modify-item__tip { font-weight: 400; font-size: var(--font-size-xs); color: var(--color-fg-3); }
.ap-mgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2) var(--space-3); }
.ap-field { display: flex; flex-direction: column; gap: 2px; font-size: var(--font-size-sm);
  min-width: 0; }
.ap-field > span { color: var(--color-fg-3); }
/* ⚠️ 用 `> *` 而不是 `> input`：日期组件的根是 `.dtp`（div），
   原来只写 `> input` 会让日期框宽度不受控。 */
.ap-field > .input, .ap-field > .select, .ap-field > .dtp { width: 100%; }
/* ⚠️ textarea 也要撑满（`.ap-field > .input` 那条选不中 textarea 里的行高，
   而宽度是能命中的 —— 这里显式写出来是为了防止将来有人把 `> .input` 收窄）。 */
.ap-field > textarea { width: 100%; resize: vertical; }
/* 整行（修改原因这种长文本）—— 不属于网格，直接占满弹窗宽度 */
.ap-field--full { width: 100%; }
/* 区间（入住→离店 / 去程→返程）占两格：两个日期 + 中间的箭头要放得下 */
.ap-field--wide { grid-column: span 2; }
.ap-range { display: flex; align-items: center; gap: var(--space-2); }
.ap-range__i { flex: 1 1 0; min-width: 0; }
.ap-range__sep { color: var(--color-fg-3); flex: 0 0 auto; }

/* ── 报销后台 ─────────────────────────────────────────────────────────── */
.xb-scope { margin-bottom: var(--space-2); }
.xb-filters { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.xb-filters .input { width: 176px; }

/* ── 审批流配置 ─────────────────────────────────────────────────────────
   ⭐⭐ 2026-09-23 修（乐哥：「上面的时间轴是横向的，样式不对，没对上」
        +「点击某一个环节之后，下面的文字很密，里面排列的文字布局、间隔都有问题，
          包括输入框等等」）

   ▸ 时间轴「没对上」的**实测根因**（不是猜的）：
     上一版在每两个节点之间插一个 `.ef-node__line`（宽 32px）当连线，
     而节点自己宽 **239px** ⇒ 圆点右边缘到线头之间留了 **105px 空白**
     ⇒ 看上去是"三段悬空的短线"，根本没连到圆点上（几何上 cy 是对齐的 0 偏差，
        但**视觉上没连起来** —— 这正是「没对上」）。
     ⇒ 与达人排期详情（`.sch-tl-line`）**同一套做法**：改成一条**绝对定位的连续线**，
        从首节点圆心贯穿到末节点圆心、压在节点下层。

   ▸ 「文字很密」的**实测根因**（比间距更要命）：
     面板里 32 个元素有 **29 个是同一个颜色**（`rgb(28,30,33)` 主文字色）——
     标题 / 说明 / 小字 / 按钮文字全一个色 ⇒ 视觉上就是一坨黑字。
     根因：本片 12 处引用了 `--color-text-secondary` / `--color-text-primary` /
     `--color-text-tertiary` / `--color-bg-subtle`，而这**四个变量全项目都没有定义**
     （实测 grep design-tokens / base / components 均为 0）⇒ 颜色静默失效、回落成继承色。
     ⇒ 统一换成真实存在的变量（与 `.field-hint` 的项目标准一致：**12px + --color-muted**）。
   ⚠️ 色阶按项目语义表选（别自造）：
        --color-fg       主文字      --color-muted   次级 / 副标题
        --color-fg-3     三级 / 元信息（= --color-meta）
        --color-surface-2 浅底（全站 hover / 表头底）
   ══════════════════════════════════════════════════════════════════════ */
.ef-flag { border-radius: 6px; padding: 10px 14px; margin-bottom: var(--space-3);
  font-size: var(--font-size-sm); color: var(--color-fg); }
.ef-flag--on { background: var(--color-success-bg); }
.ef-flag--off { background: var(--color-warning-bg); }

/* ── 4 道审批的横向时间轴 ⭐⭐ 2026-09-23 第二版（乐哥两轮反馈后定稿）────────────────
   乐哥第一轮：「时间轴是横向的，样式不对，没对上」   ⇒ 连线改连续轨道线（已修）
   乐哥第二轮：「主管审批环节的颜色怎么是灰色的？」 + 「人的展示方式得有头像、得有名字」
     +「选择人的方式应该按照我们之前定的那种去做」 + 「文字展示方式还是不直观」

   ▸ 颜色语义（三档，与全站状态色**同一套语言**）：
       · **绿** `--color-success` = 能审（人工道已配人 / 主管道能解析出主管）
       · **红** `--color-danger`  = 没人审（人工道一个人都没配 / 主管道解析不出任何人）
       · ⛔ **不再用灰色** —— 本项目里灰 = 未开始/无数据/环境问题，用在"系统自动生效"上
         会被读成"这一道被禁用了"（乐哥就是这么读的）。
   ▸ 轨道线：一条从首节点圆心贯穿到末节点圆心的连续线（`.ef-flow::before`），
     压在节点下层；⛔ 不要再输出节点间连线元素（会和伪元素叠成双线）。 */
.ef-flow {
  position: relative;
  display: flex; align-items: flex-start;
  /* ⚠️ 左右 padding 必须为 0：轨道线用百分比（12.5%/87.5%）对齐节点圆心，
     而绝对定位元素的百分比是相对**包含块的 padding box** ⇒ 有左右 padding 就会算偏。 */
  padding: 20px 0 16px;
  margin-bottom: var(--space-4);
  background: var(--color-surface-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.ef-flow::before {
  content: '';
  position: absolute;
  left: 12.5%; right: 12.5%;
  /* top = 容器 padding-top(20) + 圆点半径(14) - 线高一半(1) = 33
     （圆点紧贴节点顶部，见 .ef-node 的 padding-top: 0） */
  top: 33px;
  height: 2px;
  background: var(--color-gray-300);
  z-index: 0;
}
.ef-node {
  position: relative; z-index: 1;     /* 压在轨道线之上 */
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 0 8px 10px;                /* ⚠️ padding-top 必须是 0（轨道线 top 按此推算） */
  background: transparent; border: 0;
  border-radius: var(--radius-md); cursor: pointer;
  font: inherit; text-align: center;
}
.ef-node__dot {
  width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold);
  color: #fff; background: var(--color-muted);
  /* 描边配色与容器底同色 ⇒ 线从圆点"背后"穿过而不是贴上去（参照物同手法） */
  border: 2px solid var(--color-surface-2);
  box-sizing: border-box;
}
.ef-node--done .ef-node__dot { background: var(--color-success); }
.ef-node--warn .ef-node__dot { background: var(--color-danger); }
.ef-node__name {
  margin-top: var(--space-2);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--color-fg);
}
.ef-node__who { font-size: var(--font-size-xs); color: var(--color-muted); }
.ef-node--warn .ef-node__who { color: var(--color-danger); font-weight: var(--font-weight-medium); }
/* ⭐ 「谁在审」= 头像 + 姓名（乐哥：「人的展示方式得有头像，得有名字」）。
   ⚠️ 头像用 `UI.avatar`（`.cell-person__avatar`，无图走姓名首字）⇒ 这里把它的尺寸
      缩到 18px：默认尺寸是给表格行用的（28px+），放在时间轴节点里会把节点撑高。
   ⚠️ 姓名超长要 ellipsis（节点只有 ~240px 宽）⇒ JS 侧已同时给了 title，悬停可看全。 */
.ef-node__who--names { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
.ef-node__who--names .cell-person__avatar {
  width: 18px; height: 18px; font-size: 9px; flex: 0 0 auto;
}
.ef-node__names {
  font-size: var(--font-size-xs); color: var(--color-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ef-node:hover .ef-node__dot { box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-muted) 18%, transparent); }
.ef-node.is-active .ef-node__dot {
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent) 20%, transparent);
}
.ef-node.is-active .ef-node__name { color: var(--color-accent); font-weight: var(--font-weight-semibold); }
.ef-node:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* 面板：4 个**都渲染在 DOM 里**，只显示当前那道 ——
   见 expense-approval-flow.js 里"设计取舍①"的说明（只渲染当前道会让保存静默清空其他道）。 */
.ef-panel { display: none; }
.ef-panel.is-active { display: block; }

/* ── 单道面板 ────────────────────────────────────────────────────────────
   ⭐ 2026-09-23 第二版：分组从"左侧竖线块"改成**分隔线式**（与项目「角色权限」页的
      `.perm-group` 同一套语言）；面板标题从 14px 升到 16px，与分组标题(14px)拉开一级
      —— 原来两者同字号，加上四段同规格的小灰字 ⇒ 整体读起来"很密"（乐哥原话）。 */
.ef-step {
  border: 1px solid var(--color-border); border-radius: 8px;
  padding: var(--space-4) var(--space-4) 0;
}
.ef-step__head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.ef-step__head b { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg); }
/* 规则说明：项目标准的辅助文字规格（12px + --color-muted），行高放宽到 1.65。
   ⚠️ 每道**最多一行**（文案已在 JS 里精简）—— 这是"不密"的关键。 */
.ef-step__note {
  font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.65;
  margin-bottom: var(--space-2);
}
.ef-step__note b, .ef-group__h b, .ef-notice b, .ef-pick-hint b {
  font-weight: var(--font-weight-semibold); color: var(--color-fg);
}
/* 分组：标题 + 一行说明 + 内容，用分隔线分段（最后一个不画线） */
.ef-group { padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border-soft); }
.ef-group:last-child { border-bottom: 0; }
.ef-group__t {
  display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); color: var(--color-fg);
  margin-bottom: var(--space-1);
}
.ef-group__n { font-weight: var(--font-weight-regular); font-size: var(--font-size-xs); color: var(--color-muted); }
/* 「改完立即生效」—— 必须显眼：否则管理员会一直等底部那个「保存配置」按钮 */
.ef-group__n--live { color: var(--color-accent); font-weight: var(--font-weight-medium); }
.ef-group__h { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.65; margin-bottom: var(--space-2); }
/* 一行式提示（替代原来散落的 5 个琥珀块）：普通=中性说明，--warn=会影响提单/同步 */
.ef-notice { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.65; margin-bottom: var(--space-2); }
.ef-notice--warn {
  background: var(--color-warning-bg); color: var(--color-warning-text);
  border-radius: 6px; padding: 6px 10px;
}
/* 多行提示块（飞书权限诊断 / 孤儿部门）——
   ⚠️ 必须 `display:block` + 行高放宽：这类文案是**完整操作指引**（含权限码、去哪个页面），
      挤成一行会读不完。与上面单行式 `.ef-notice` 的区别就在这里。 */
.ef-notice--block { display: block; line-height: 1.75; padding: 8px 12px; }
.ef-mode { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.ef-radio { display: inline-flex; align-items: center; gap: 5px; font-size: var(--font-size-sm); cursor: pointer; }
.ef-actions { display: flex; justify-content: flex-end; margin-top: var(--space-3); }

/* ⭐ 选人框下方那行状态（FeishuPicker 的配套说明）：
   普通 = 「已选 N 人 · 都能审批这一道」；--warn = 有人没权限（保存时会自动开通）/ 一个人都没选。
   这是"允许从整个通讯录选人"的必要说明 —— 不说清，用户管理里凭空多出账号会让人以为出 bug。 */
.ef-pick-hint { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.65; margin-top: var(--space-2); }
.ef-pick-hint--warn { color: var(--color-warning-text); }

/* ── 第 1 道「逐人单独指定」的一行：`[头像 员工/部门] → [头像 审批人] [换人][移除]`
   ⭐ 加了头像（乐哥：「人的展示方式得有头像，得有名字」）——
      原来是一行纯文字「张乐（总经办 · ou_6968…3c9）的审批人 [下拉]」，读起来很费劲。
   ⚠️ 行内**不放 picker**（picker 每次 render 都挂 document 监听且无 destroy，
      N 行会累积 N 个）⇒ 行内只展示，换人走弹窗。 */
.ef-sup2 {
  display: grid;
  /* ⚠️ 四列（2026-09-23 改）：员工 | 箭头 | 审批人 | 操作。
     原来只有三列 ⇒ 操作按钮只能塞在"审批人"那格里 ⇒ 它们**紧贴在名字后面**，
     右边空掉大半个容器宽度，几行看下来像"没排完版"（乐哥：「这部分的按钮，你看下放哪」）。
     现在给操作单独一列 ⇒ 它被推到行尾，四行的按钮**左边缘对齐成一条竖线**。 */
  grid-template-columns: minmax(0, 260px) 22px minmax(0, 1fr) auto;
  align-items: center; gap: var(--space-2);
  /* 行悬停浅底要盖住整行 ⇒ 用负外边距把内边距"撑出去"，避免左移头像对齐线 */
  margin: 0 calc(-1 * var(--space-2));
  padding: var(--space-2);
  border-radius: var(--radius-md);
}
/* 行间细分隔线：几行叠在一起时不至于糊成一块（最后一个不画） */
.ef-sup2 + .ef-sup2 { border-top: 1px solid var(--color-border-soft); }
/* 悬停：整行浅底 —— 让人一眼看出"这行 + 这行的按钮"是一组 */
.ef-sup2:hover { background: var(--color-surface-2); }
.ef-sup2__who { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
.ef-sup2__who-t {
  display: flex; flex-direction: column; min-width: 0;
  font-size: var(--font-size-sm); color: var(--color-fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 主标题（姓名）与副标题（部门 · 用户名）两级 —— 副标题**不再重复姓名**（见 JS 的 personSub）。
   ⚠️ 主标题要给 line-height，否则两行文字贴在一起（中文字体上下留白小）。 */
.ef-sup2__who-n { line-height: 1.5; }
.ef-sup2__who-s { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.5; }
.ef-sup2__arrow { color: var(--color-fg-3); display: inline-flex; justify-content: center; }
.ef-sup2__sup { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
.ef-sup2__sup-t { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
.ef-chip-name { font-size: var(--font-size-sm); }
/* 行尾操作组：靠右（即"操作"那一列的左沿），两按钮等距 */
.ef-sup2__ops { display: inline-flex; align-items: center; gap: var(--space-1); margin-left: auto; }
/* ── 行内操作按钮：**无边框文字按钮 + 按语义上色**
   ⚠️ 不用 `.btn--ghost`：它带灰边框，和这一屏里真正的输入框（选人组件的搜索框、
      两个录入按钮）长得一样 ⇒ 分不清"哪个能点、点了做什么"。沿用本项目
      `[data-role]`（accent）/ `[data-del]`（danger）那套既有语言。
   ⚠️ 「移除」平时**只红字不铺红底** —— 一行一个红色实心块会让整屏很扎眼；
      红色浅底留到 hover 才出现（破坏性操作"要动手时才提醒"即可）。 */
.ef-act {
  background: transparent; border-color: transparent; color: var(--color-accent-text);
  padding: 0 var(--space-2); gap: 4px;
}
.ef-act:hover:not(:disabled) { background: var(--color-primary-100); border-color: transparent; }
.ef-act--danger { color: var(--color-danger); }
.ef-act--danger:hover:not(:disabled) { background: var(--color-danger-bg); border-color: transparent; }
/* 窄屏（≤900px）：四列挤不下 ⇒ 拆成两行。
   第 1 行：员工  + 箭头            （占满宽度 ⇒ 姓名不会被挤到截断）
   第 2 行：审批人（跨前两列） + 操作按钮（第 3 列，右侧正好空着）
   ⚠️⚠️ 两个坑都实测过：
     ① 操作按钮**不能**跨两行（`grid-row: 1 / 3`）—— 它会在整个行高上垂直居中，
        与"员工""审批人"两行**都不对齐**，看着像浮在半空（390px 截图很明显）；
     ② 操作按钮也**不能留在第 1 行**（`grid-row: 1`）—— 它的宽度（约 132px）
        会挤压第 1 列，实测 390px 下「李功司」的可用宽度只剩 33px（需要 39px）
        ⇒ **姓名被截断**。挪到第 2 行右侧后第 1 行独占宽度，实测不再截断。 */
@media (max-width: 900px) {
  .ef-sup2 { grid-template-columns: minmax(0, 1fr) 22px auto; row-gap: var(--space-1); }
  .ef-sup2__sup { grid-column: 1 / 3; grid-row: 2; padding-left: calc(28px + var(--space-2)); }
  .ef-sup2__ops { grid-column: 3; grid-row: 2; gap: 0; }
  /* ⚠️ 极窄屏（手机）下**收起按钮图标**：实测 390px 视口里整行只剩 251px，
      而两个带图标的按钮要 128px ⇒ 留给姓名的只有 33px（「李功司」需 39px）⇒ **姓名被截断**。
      收起图标（每个省约 18px）并把内边距收到 6px，按钮组降到约 80px，姓名即完整显示。
      ⚠️ 只收图标、**不收文字** —— "换人/移除"这两个词是操作语义本身，不能省。 */
  .ef-act svg { display: none; }
  .ef-act { padding: 0 6px; }
}

/* 「我的报销」单据行的审批环节小字 —— 与状态徽章并排，说明卡在哪一道 */
.me-step { margin-left: 6px; font-size: var(--font-size-xs); color: var(--color-meta); }

/* ── 部门主管（可多个）⭐ 2026-09-23 重做 ──────────────────────────────────
   一行 = 一个部门：`[部门名 · N 人]  [主管 chips / 飞书兜底 / 未指定]  [指定主管]`
   ⚠️ 三列用 grid 且**操作列靠右**（与上面「逐人单独指定」同一套语言）——
      按钮紧贴内容时右边会空掉一大片，几行下来像没排完版（那个问题上一轮刚修过）。 */
.ef-dept { border-top: 1px solid var(--color-border-soft); }
.ef-dept__row {
  display: grid;
  /* ⚠️ 第一列 172px 是**量出来的**（2026-09-23 截图后发现换行才定的）：
     最长部门名「直播旗舰事业部」（7 个中文字 ≈ 98px）+ 「飞书」标签（≈34px）
     + 间距 6px + 两侧 padding 16px ≈ 154px。取 172px 留一点余量。
     原来 150px 不够 ⇒ 部门名被压成两行（"BEATEAM事/业部"），
     连带把「飞书」标签挤到第二行、整行高矮不一 —— 就是"看着没对齐"的根因。
     ⚠️ 第一列**不能**用 `auto`：各行部门名长度不同，auto 会让每行的第二列起点都不同，
        那比现在更乱（列与列必须对齐）。 */
  grid-template-columns: minmax(0, 172px) minmax(0, 1fr) auto;
  align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-2);
  margin: 0 calc(-1 * var(--space-2));
  border-bottom: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
}
.ef-dept__row:hover { background: var(--color-surface-2); }
.ef-dept__name {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  font-size: var(--font-size-sm); color: var(--color-fg); font-weight: var(--font-weight-medium);
}
/* ⛔ 部门名**绝不换行**（超长省略、`title` 里有全名）。
   换行会让每一行高度不一致 —— 几行下来整个列表看着就是歪的。 */
.ef-dept__name-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ef-dept__name .ef-dept__tag { flex: none; }
/* 「N 人」——放在第二列最前（紧邻主管信息）。
   `flex:none` + `nowrap`：绝不允许把"10 人"拆成"10"/"人"两行。 */
.ef-dept__n {
  flex: none; white-space: nowrap;
  font-size: var(--font-size-xs); color: var(--color-muted);
  font-weight: var(--font-weight-regular);
}
/* 「飞书」来源标签（灰）—— 与手工指定的蓝 chips 区分开，一眼看出这个部门是怎么来的 */
.ef-dept__tag {
  font-size: 11px; line-height: 1; padding: 2px 5px; border-radius: 4px;
  background: var(--color-surface-2); color: var(--color-muted); font-weight: var(--font-weight-regular);
}
.ef-dept__who { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; min-width: 0; }
/* 主管 chip：头像 + 姓名 + 移除 ×（蓝底 = 手工指定的生效主管） */
.ef-dept__chip {
  display: inline-flex; align-items: center; gap: 5px; max-width: 220px;
  padding: 2px 4px 2px 3px; border-radius: var(--radius-pill);
  background: var(--color-primary-50); border: 1px solid var(--color-primary-100);
  /* ⭐ 2026-09-23 乐哥两轮要求调小后定稿：`--font-size-sm`(14px) → **12px**。
     ⚠️ 与下面的头像尺寸同属"部门行里的人和名字都收一号"这一次改动。 */
  font-size: var(--font-size-xs); color: var(--color-accent-text);
}
.ef-dept__chip-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠️ 停用的人：整块降透明度 + 姓名加删除线来源不明，用灰底区分（后端给了 active 标记） */
.ef-dept__chip.is-off {
  background: var(--color-surface-2); border-color: var(--color-border);
  color: var(--color-muted); opacity: .75;
}
/* 「已停用」小字 —— ⚠️ 必须与灰底同时出现：只有灰底时用户看不出原因，
   会以为"配了却没生效"是 bug（实际是那个人停用了 ⇒ 这一档回落到飞书负责人）。 */
.ef-dept__off {
  font-size: 11px; line-height: 1; padding: 1px 4px; border-radius: 3px;
  background: var(--color-danger-bg); color: var(--color-danger);
}
.ef-dept__x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border-radius: 50%; border: 0; background: transparent;
  color: inherit; opacity: .55; cursor: pointer; padding: 0;
}
.ef-dept__x:hover { opacity: 1; background: var(--color-danger-bg); color: var(--color-danger); }
/* 飞书负责人：**灰底虚线**表达"它是兜底值、不是你手工指定的"（视觉上与蓝 chip 拉开） */
.ef-dept__chip--feishu {
  background: var(--color-surface-2); border: 1px dashed var(--color-gray-400); color: var(--color-fg-2);
}
.ef-dept__src { font-size: var(--font-size-xs); color: var(--color-muted); }
.ef-dept__multi { font-size: var(--font-size-xs); color: var(--color-muted); }
/* 「0 人 · 待分配员工」—— **手工建的**部门还没人：这是待办，用琥珀色提醒。 */
.ef-dept__zero {
  font-size: var(--font-size-xs); color: var(--color-warning-text);
  background: var(--color-warning-bg); border-radius: 4px; padding: 1px 6px;
}
/* 「暂无本系统员工」—— **飞书来的**空部门：公司有、我们没跟踪到人，属正常现象。
   ⚠️ 必须与上面那个黄色待办**在视觉上分开**：实测 35 个部门里 28 个是这种，
      全打黄标 = 满屏黄色 = 该看的那个被淹掉（"总在报警"等于没报警）。 */
.ef-dept__zero--quiet {
  color: var(--color-muted); background: transparent; padding-left: 0;
}
/* 「未指定」= 要处理的（这个部门的人提不了单）⇒ 用 danger 色，不能是中性灰 */
.ef-dept__none { font-size: var(--font-size-xs); color: var(--color-danger); }
.ef-dept__none b { font-weight: var(--font-weight-semibold); }
.ef-dept__ops { display: inline-flex; align-items: center; gap: var(--space-1); }
.ef-dept__foot {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding-top: var(--space-2);
}
/* 窄屏：部门名与操作各占一行（三列挤不下会把主管姓名压成两三个字） */
@media (max-width: 900px) {
  .ef-dept__row { grid-template-columns: minmax(0, 1fr) auto; row-gap: var(--space-1); }
  .ef-dept__who { grid-column: 1 / -1; grid-row: 2; }
  .ef-dept__ops { grid-column: 2; grid-row: 1; }
}

/* ── 指定 / 换人弹窗（内部是两个 FeishuPicker 单选）────────────────────── */
.ef-add { padding-bottom: var(--space-2); }
.ef-add__r { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.ef-add__r > span { font-size: var(--font-size-sm); color: var(--color-muted); min-width: 84px; flex: 0 0 auto; }
/* ⚠️ 右侧容器要能撑满（FeishuPicker 根元素是 `.fp`，宽度默认按内容） */
.ef-add__r > div { flex: 1; min-width: 0; }

/* 「我的报销」列表的「报销人」列（2026-09-22 加）—— 窄一点，别抢"费用"列的宽度 */
.me-applicant { white-space: nowrap; }

/* 审批详情：明细表「凭证」列（2026-09-22）
   —— 眼睛图标 + 张数角标；点开看该笔的发票与付款凭证（替代原来独立的「发票附件」区块）。 */
.ap-vt { margin-top: 2px; }
.ap-vch { display: inline-flex; align-items: center; gap: 4px; }
.ap-vch__n {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 999px; background: var(--color-surface-2);
  font-size: 11px; line-height: 1; color: var(--color-fg-2);
}
.ap-vch-none { color: var(--color-meta); }
.ap-legend { font-size: var(--font-size-xs); color: var(--color-muted); margin-bottom: var(--space-2); }
/* 凭证弹窗里「发票 / 付款凭证」两组 */
.ap-vch-grp { margin-top: var(--space-3); }
.ap-vch-cnt { margin-left: 6px; font-size: var(--font-size-xs); color: var(--color-muted); font-weight: 400; }

/* ⭐⭐ 2026-09-26 人工验票区（乐哥：「把发票号码打印出来，让财务一笔一笔复制」）────
   财务的工作方式：复制号码 → 税局网站查询 → 回来标「通过 / 有问题」。
   设计遵循三件事：
     ① 号码等宽 + `user-select: all`（点一下全选）+ word-break —— 复制是最高频动作，
        绝不能被截断或选不中；
     ② 操作按钮走紧凑档（34px）—— 它住在弹窗里、不是表单主控件；
     ③ 已验状态用底色区分（通过=浅绿 / 有问题=浅红），一眼扫过去知道哪些验过。 */
.ap-vfy { margin-top: var(--space-2); border: 1px solid var(--color-border-soft); border-radius: var(--radius-md); padding: 10px 12px; }
.ap-vfy--passed { border-color: var(--color-success-bg); background: var(--color-success-bg); }
.ap-vfy--failed { border-color: var(--color-danger-border); background: var(--color-danger-bg); }
.ap-vfy__row { display: flex; align-items: center; gap: 10px; }
.ap-vfy__no { flex: 1; min-width: 0; font-family: var(--font-family-mono); font-size: var(--font-size-sm);
  letter-spacing: .4px; word-break: break-all; user-select: all; -webkit-user-select: all; }
.ap-vfy__ops { display: flex; gap: 6px; flex-shrink: 0; }
/* ⚠️ 高度用**令牌**（两档制纪律：控件高度一律 var(--control-height-sm)，
   不许硬编码 34px —— 见 design-tokens 说明与 tests/control-size-two-tier.test.js） */
.ap-vfy__ops .btn { height: var(--control-height-sm); padding: 0 12px; font-size: var(--font-size-sm); }
.ap-vfy__state { margin-top: 8px; font-size: var(--font-size-xs); color: var(--color-muted); }
.ap-vfy__reason { width: 100%; margin-top: 8px; }
/* 财务道「未验完不能通过」的提示（跟在 footer 按钮上方） */
.ap-approve-hint { font-size: var(--font-size-xs); color: var(--color-danger-strong); margin-bottom: 6px; }

/* 审批详情明细区（2026-09-22 五项优化）
   —— 列序按"审批判断顺序"：# 金额 费用类型+验真 凭证 日期 备注
   —— 限高内部滚动：底部三个按钮（驳回/修改并同意/通过）始终可见 */
.ap-items { max-height: 320px; overflow-y: auto; }
.ap-items thead th { position: sticky; top: 0; z-index: 1; background: var(--color-surface); }
.ap-seq { width: 34px; color: var(--color-meta); }
.ap-amt { font-weight: var(--font-weight-medium); white-space: nowrap; }
/* 验真标签提亮：它决定批不批，不该比备注还弱 */
.ap-vt .tag { font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); }
/* 凭证按钮统一形态：无凭证时置灰但形状一致 */
.ap-vch--none { opacity: .55; }

/* ── 用户管理 · 按部门分组（2026-09-23，乐哥：「用户管理里，用户得分组」）──────
   组头是一行跨列的可点击行，点它折叠/展开该组成员。
   ⚠️ 组头**不复用** `thead th` 的样式：那些是全站表头（`white-space:nowrap`、
      居中等），而组头是"可点击的分区标题"，语义不同 —— 混用会让组头在窄屏把表格撑宽。
   ⚠️ 折叠用 `row.hidden`（JS 侧），这里只负责 caret 的旋转，不写 `display:none` ——
      两边都写会打架（JS 设 hidden=false 时 CSS 的 display:none 仍在）。 */
.u-group > td {
  /* ⭐ 2026-09-23 强化（乐哥：「用户管理里面，用户分组展示方式不行」）：
     原来只是 `--color-surface-2` 的浅灰底，与数据行**几乎同色** ⇒
     扫一眼看不出"这里开始换部门了"，分组等于没做。
     现在用更实的灰底 + 上下描边，让它明确是"一个分区标题"。 */
  background: var(--color-gray-50);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  user-select: none;
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border-soft);
}

/* ⚠️⚠️ **分组头必须显式左对齐**，而且特异性要赢过 `table.css` 的"全表默认居中"——
   写 `.u-group > td { text-align: left }` 是**不够的**（这条我已经踩过一次）。

   ⛔ 乐哥 2026-09-23 报障原话：「用户管理里面，这个不对」（附截图，红框圈在
      「财务 1 人 / 商务 6 人 / 行政 5 人」三处）。实测数据：
        · 分组头 td: `colspan=7`、`text-align: center`、classList 为空
        · 分组头文字左边缘 = **距表格左 601px**
        · 「角色」列区间 = 距表格左 471~635px ⇒ **601 正好落在角色列里**
        ⇒ 观感就是"分组头跑到角色列下面去了"，不像分区标题。

   ⛔ 根因（两条叠加，缺一条都不会出这个现象）：
     ① `table.css` 有一条**全表默认居中**：
          `.table:not(:where(td .table)) > tbody > tr > td { text-align: center; }`  ← 特异性 (0,1,3)
        而我上一轮写的 `.u-group > td` 只有 **(0,1,1)** ⇒ **静默压不过**
        （同一类坑 `table.css` 里 `.is-num` 那段注释也记过：只写 `.table .is-num` 压不过它）。
     ② `app.js` 的 `fixColAlign()` **救不了它**：该机制按「表头文本」判定哪些列是长文本列，
        再给**同列**的 td 打 `.col-align-left`；而分组头是 `colspan=7` 的**单格**，
        实测拿不到这个类（classList 为空）⇒ 只能继承默认的居中。

   ⇒ 所以判据必须写成 `.table:not(:where(td .table)) > tbody > tr.u-group > td`（(0,2,3)，稳赢），
     并且**要放在 `.u-group > td` 之后**（同为分区标题的两条规则，离得近才好一起看）。 */
.table:not(:where(td .table)) > tbody > tr.u-group > td {
  text-align: left;
}

.u-group:hover > td { background: var(--color-surface-2); }
.u-group__i {
  display: inline-flex; align-items: center; vertical-align: middle;
  margin-right: var(--space-2); color: var(--color-muted);
  transition: transform var(--motion-fast) var(--motion-ease-standard);
}
.u-group.is-folded .u-group__i { transform: rotate(-90deg); }
.u-group__t {
  font-weight: var(--font-weight-semibold); color: var(--color-fg);
  font-size: var(--font-size-sm);
}
/* 人数做成**徽章**（原来只是一串小灰字，和部门名糊在一起、扫不出来）。
   ⚠️ 颜色一律走 design-tokens —— 写死色值在暗色主题下会变成看不清的浅底灰字。 */
.u-group__n {
  display: inline-flex; align-items: center; vertical-align: middle;
  margin-left: var(--space-2); padding: 1px 8px;
  border-radius: var(--radius-full);
  background: var(--color-surface); border: 1px solid var(--color-border);
  font-size: var(--font-size-xs); color: var(--color-muted);
}
/* ── 「未填部门」整组转橙色警示 ──────────────────────────────────────────────
   它不是普通部门，是**待处理项**：未配部门 ⇒ 财务模块按部门统计时归不到人，
   报销单的部门列也是空的（详见 userRowHtml 里「未配」的说明）。
   所以它必须在"全部"视图里就有颜色，而不是等人自己点进去发现。 */
.u-group--warn > td { background: var(--color-warning-bg-soft); }
.u-group--warn:hover > td { background: var(--color-warning-bg); }
.u-group--warn .u-group__t { color: var(--color-warning-text); }
.u-group--warn .u-group__n {
  background: var(--color-warning-bg); border-color: var(--color-warning);
  color: var(--color-warning-text); font-weight: var(--font-weight-medium);
}
.u-group__warn {
  margin-left: var(--space-3); vertical-align: middle;
  font-size: var(--font-size-xs); color: var(--color-warning-text);
}

/* ── 用户管理 · 部门 Tab 上的人数（2026-09-23）─────────────────────────────
   贴在 tab 文字右侧的小字。`--warn` 用于「未填部门」那个 tab（同一条警示逻辑）。 */
.u-dept-tab__n { margin-left: 6px; font-size: var(--font-size-xs); color: var(--color-fg-3); }
.u-dept-tab__n--warn { color: var(--color-warning-text); font-weight: var(--font-weight-semibold); }
/* 选中态的 tab 文字已经是强调色，人数跟着继承即可，不要再抢一层色。 */
.table-tabs .seg__btn.is-active .u-dept-tab__n { color: inherit; }

/* ── 用户管理 · 表格（2026-09-23）─────────────────────────────────────────
   ⭐⭐ 为什么需要这两条：乐哥报障「用户管理里面，这个不对」——
      截图红框圈在被切掉的「删除」按钮上（窄窗口下最右侧那颗被裁掉）。

   ⛔ 根因（实测，不是推测）：「用户名」列装的是飞书 36 字符 open_id
      （`ou_a9581258e7b2ff3eec0df1f9331e534f`），**一串没有空格** ⇒ 它的
      `min-content` 就是整串宽 **319px**，而 `table-layout:auto` 下浏览器
      **压不动任何一列低于它的 min-content** ⇒ 整表最小宽被钉在 **1133px**：
        · 视口 1440（容器 1134）→ 刚好塞下
        · 视口 1300（容器 ~1000）→ 溢出 133px ⇒ 最右侧「删除」被切
        · 视口 1280（容器 974）→ 溢出 159px ⇒ 切得更狠（实测数据）

   ⇒ 修法两件事一起做（**只做一件不够**，实测过）：
     ① `.u-username` 限宽 + 省略号 ⇒ 把这一列的 min-content 解放出来
        （只收窄操作列、不处理这一列：实测只省 29px，仍溢出 18px —— 因为省下的
          空间立刻被这一列膨胀吃掉）
     ② 其余列锁单行（`nowrap`）⇒ 它们的 min-content == max-content、浏览器压不动，
        富余宽度只能让给那唯一可换行的列。
        依据：项目既有标准（技能 `tables-and-lines.md`「列宽溢出的正解：
        把宽度让给唯一可换行的列」）。
   ⚠️ 用 `#u-table` 作用域，**新开范围**不动全站 `.table` / `.is-num`（几十张表在用）。
   ⚠️ 用户名列**不能删**：外部账号与飞书账号只能靠它区分（姓名会重名，实测 3 个「张乐」）。
      截断了也要给 `title` 让鼠标悬停能看到全文（模板里已加）。 */
#u-table .u-username {
  display: inline-block;
  /* ⚠️ 限宽值取自"列宽分配合适"的实测（未截断时是 319px，锁单行后能压到 ~240px） */
  max-width: 215px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* inline-block 默认按基线对齐，会比同行其它文字矮一截 —— 必须改掉，否则整行错位 */
  vertical-align: bottom;
}
/* 「外部账号」徽章要跟用户名同行、不折行（它是 inline 元素，会被 nowrap 规则管到） */
#u-table .badge { white-space: nowrap; }
#u-table table tbody td:not(:nth-child(2)) { white-space: nowrap; }

/* 「本地账号」标签（2026-09-23 加）—— 标在"不是飞书组织架构、而是本地账号自填的
   职能标签"（实测 8 个 `local:xxx` 占位账号填着「商务/行政/运营」）的部门行上。
   ⚠️ 用**中性灰**而不是主色：它是"提示你别在这配"的标记，不该比真实部门更显眼。 */
.ef-dept__tag--local { background: var(--color-surface-2); color: var(--color-fg-2); }
/* 「飞书配了负责人但他没系统账号 ⇒ 需手工指定」—— 这是**要处理的**，
   必须与中性的"飞书部门负责人 · 兜底"在视觉上区分开（实测 10 个一级部门里 3 个是这种）。
   ⚠️ `nowrap + ellipsis`：这条文案比"兜底"那行长，若不限宽会**顶到右侧按钮**
      （实测截图看到"手工指定"直接贴在按钮边上，很难看）。
      完整原因在元素的 `title` 里，悬停可见 —— 行内只留结论。 */
.ef-dept__src--warn {
  color: var(--color-warning-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.ef-dept__src--warn b { font-weight: var(--font-weight-semibold); }

/* 别名 chips + 「归入部门」下拉（2026-09-23）——
   用途：把外部账号自填的职能标签（商务/行政/运营）归入真实部门。
   ⚠️ 别名 chip 用**虚线描边**：它与主管 chip（蓝、实线、带头像）是两种东西——
      主管是"人"、别名是"一个名字"，不能长得像。 */
.ef-dept__chip--alias {
  background: transparent;
  border: 1px dashed var(--color-border);  /* 2026-09-25 修：--color-border-strong 不存在（一直走兜底），需要强边框时再到 design-tokens 新增 */
  color: var(--color-fg-2);
  /* ⚠️ 右侧留 6px：它里面也有一个 ×（移除别名）。
     上一版写的 3px 实测**挤在一起**（截图看到 × 贴着"人"字）。 */
  padding-right: 6px;
}
/* 「归入部门…」下拉：紧贴主操作按钮左侧、等高。
   ⚠️ 宽度必须限死：部门名（如"直播旗舰事业部"）很长，不限宽会把整行撑开、
      把中间那列的主管信息挤到换行（上一轮刚修过同类问题）。 */
.ef-dept__merge {
  flex: none; width: 116px; height: 28px;
  padding: 0 6px; font-size: var(--font-size-xs);
}

/* ═══ 部门屏蔽 + 外部账号（2026-09-23 乐哥）═══════════════════════════════ */

/* 「显示已屏蔽」开关 —— 标题行右侧的弱化控件。
   ⚠️ 只在**有已屏蔽部门时**才渲染（没屏蔽过就不出现，减少噪音）。 */
.ef-dept__showtoggle {
  display: inline-flex; align-items: center; gap: 4px;
  margin-left: var(--space-3);
  font-size: var(--font-size-xs); font-weight: var(--font-weight-regular);
  color: var(--color-muted); cursor: pointer;
}
.ef-dept__showtoggle input { margin: 0; }

/* 已屏蔽的行 —— 整行降透明 + 弱化，一眼看出"这行现在不生效"。
   ⚠️ 不做 `display:none`：屏蔽后默认不显示是在**渲染前过滤**掉的，
      这里处理的只是"用户主动勾了显示已屏蔽"的情况 ⇒ 要**能看见但明显不同**。 */
.ef-dept__row.is-blocked { opacity: .55; }
.ef-dept__row.is-blocked .ef-dept__name { text-decoration: line-through; }

/* 屏蔽/恢复的图标按钮：`is-on` = 当前处于"已屏蔽"态（图标是睁眼，提示可恢复）。
   ⚠️ 用描边色而不是主色：它是**次要操作**（绝大多数时候不会点），
      主色会让每行都有一处抢眼的东西、整页发花。 */
.ef-dept__ops .icon-btn.is-on { color: var(--color-warning-text); }

/* ── 外部账号区块 ─────────────────────────────────────────────────────
   一行 = 一个标签：`[标签 · N 人] [人名列表] [归入部门 ▾]`
   ⚠️ 第一列宽度与 `.ef-dept__row` **保持一致**（172px）：两块紧挨着，
      列不对齐会显得很乱（本项目"看着没对齐"的常见成因）。 */
.ef-ext { border-top: 1px solid var(--color-border-soft); }
.ef-ext__row {
  display: grid;
  grid-template-columns: minmax(0, 172px) minmax(0, 1fr) auto;
  align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-2);
  margin: 0 calc(-1 * var(--space-2));
  border-bottom: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
}
.ef-ext__row:hover { background: var(--color-surface-2); }
.ef-ext__tag {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  font-size: var(--font-size-sm); color: var(--color-fg); font-weight: var(--font-weight-medium);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ef-ext__n { font-size: var(--font-size-xs); color: var(--color-muted); font-weight: var(--font-weight-regular); flex: none; }
/* 「待归入」= 这些人现在提不了单，是待办 ⇒ 琥珀色（与"未指定主管"同一种语气）。 */
.ef-ext__todo {
  flex: none; font-size: 11px; line-height: 1; padding: 1px 5px; border-radius: 3px;
  background: var(--color-warning-bg); color: var(--color-warning-text);
}
.ef-ext__who {
  font-size: var(--font-size-xs); color: var(--color-fg-2); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ef-ext__ops { display: inline-flex; align-items: center; gap: var(--space-1); }
/* 归入部门的下拉：与部门行里的「归入部门」同宽同高（视觉一致） */
.ef-ext__merge { flex: none; width: 148px; height: 28px; padding: 0 6px; font-size: var(--font-size-xs); }
/* ⚠️ 「所归入的部门已屏蔽 ⇒ 仍提不了单」—— 必须跨列显示在下一行，
   否则会被挤到第一列里把标签压变形（grid 三列已满）。 */
.ef-ext__bad {
  grid-column: 1 / -1; font-size: var(--font-size-xs); color: var(--color-warning-text);
}
@media (max-width: 900px) {
  .ef-ext__row { grid-template-columns: minmax(0, 1fr) auto; row-gap: var(--space-1); }
  .ef-ext__who { grid-column: 1 / -1; grid-row: 2; }
  .ef-ext__ops { grid-column: 2; grid-row: 1; }
}

/* ═══ 报销审批流 · 部门主管区块（2026-09-23 乐哥四条反馈）═══════════════════════ */

/* ① 卡片内的呼吸空间：给 `#ef-box` 留 8px。
   ⚠️⚠️ **必须是 `#ef-box` 这一层、且必须是 `var(--space-2)`（8px）** ——
      部门行 `.ef-dept__row` 用了 `margin: 0 -8px` + `padding: 0 8px`（"内容对齐父级、
      hover 背景外扩 8px"）。在这层加 8px padding，恰好让两件事同时成立：
        · 普通文字（部门标题/说明）左边距 = 卡片 24px + 8px = **32px**（不再贴边）
        · 部门行因 -8px 负边距回到 24px，其内部 padding 8px ⇒ 内容仍在 **32px** ✓ 对齐不变
      若改成 12px 或加在更外层，正文与部门名就会**差 4px**（看着就是"没对齐"）。 */
#ef-box { padding: var(--space-2); }

/* ② 标题行右侧的两个控件（「显示已屏蔽」+「从飞书同步部门」）——
   乐哥：「从飞书同步部门这个按钮移到右上角，然后显示已屏蔽和这个同步部门按钮放到同一排」。
   ⚠️ `margin-left: auto` 把它们推到最右（不跟着"N 个部门"的字数左右跳）。
   ⚠️ `align-self: center` 覆盖标题行的 `align-items: baseline` ——
      按钮没有文字基线，跟着 baseline 对齐会往下沉半行。 */
.ef-group__tools {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: var(--space-3);
  align-self: center;
}

/* ③ 主管/负责人卡片里的人像（2026-09-23 乐哥**两轮**反馈后定稿）——
   第一轮「人名和头像可以适当调小一点」：28px/14px → 22px/13px
   第二轮「名字还是大，要不再小一点？」：22px/13px → **20px/12px**
   ⚠️⚠️ 定稿值**只留这一条规则**。上一版把两轮的值**都留在文件里**
      （两条同名 `.ef-dept__chip .cell-person__avatar`，靠"后者覆盖前者"生效）——
      那是**坏的**：读代码的人（以及我自己的探针）会先看到 22px 那条，
      以为生效的是 22px（本轮真踩：探针误报"头像 22px"）。
      ⇒ **改样式时要替换旧值，不要追加**；确实需要保留过程，写在注释里、不要留第二条规则。
   ⚠️ 12px 就是本项目的 `--font-size-xs`，是**可读下限**，不要再往下调
      （低于 12px 中文在人像旁会糊成一团，且与「已停用」标签同字重时难区分）。
   ⚠️ 头像基础尺寸定义在 `table.css` 的 `.cell-person__avatar`（28px），
      这里**只在本区块内覆盖**（`.ef-dept__chip` 前缀限定作用域）——
      不能改全局那个：表格人员列是另一套密度，动了会全站变样。
   ⚠️ 字号（12px）**已写在上面的 `.ef-dept__chip` 基础规则里**，这里不再重复覆盖 ——
      两处写同一个属性就是本轮踩过的"留下两条同名规则、读的人不知道哪条生效"。 */
.ef-dept__chip .cell-person__avatar { width: 20px; height: 20px; font-size: 11px; }


/* ═══ 付款批次台账 · 展开看明细（2026-09-23 乐哥：「我勾选完之后的记录在哪？」）═══
   用途：批次行只有批次号/张数/金额时，看不出"这批里是哪几张单"。
   ⚠️ 批次号做成**按钮**而不是给整行加 click —— 行里还有「标记已打款」按钮，
      整行可点会让"想打勾却展开了明细"（本项目在别的表格上踩过同类误触）。 */
.fin-batch-open {
  display: inline-flex; align-items: center; gap: 5px;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  color: inherit; font: inherit;
}
.fin-batch-caret {
  display: inline-flex; color: var(--color-muted);
  transition: transform var(--motion-fast) var(--motion-ease-standard);
}
.fin-batch-open.is-open .fin-batch-caret { transform: rotate(90deg); }
/* ⚠️ `.fin-batch-n` 已**无使用者**（2026-09-23 统一到 BT 时改用 `.fin-badge`）——
   原来它在提报单号后面显示「N 张」，但它**只有字号与颜色、没有 `margin-left`**
   ⇒ 实测小标紧贴单号（看着像被粘住）。
   总览页的「N 笔」用的是 `.fin-badge`（自带 6px 间距 + 浅底）—— 同一种语义
   （单号后的数量小标）该长一样 ⇒ 复用 `.fin-badge`。
   ⚠️ 本行**保留不删**：本项目有并行会话在改同一个文件，删共用 CSS 有风险；
      新代码请不要使用本类，改用 `.fin-badge`。 */
.fin-batch-n { font-size: var(--font-size-xs); color: var(--color-muted); }
.fin-batch-open:hover .fin-code { text-decoration: underline; }

/* 明细行：浅底色 + 左侧缩进，一眼看出它从属于上面那个批次。
   ⚠️ `background` 用 `--color-surface-2`（比白卡片深一档），不能用主色浅底 ——
      后者在"选中行"上会被误读成被选中了。 */
.fin-bdet-row > td { background: var(--color-surface-2); padding: 8px 12px 10px 32px; }
.fin-bdet { display: flex; flex-direction: column; gap: 2px; }
.fin-bdet__item {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto auto;
  align-items: center; gap: var(--space-3);
  padding: 4px 6px; border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
}
.fin-bdet__item:hover { background: var(--color-surface); }
.fin-bdet__code { text-decoration: none; }
.fin-bdet__who { display: flex; align-items: center; gap: 6px; min-width: 0; }
.fin-bdet__amt { font-variant-numeric: tabular-nums; }
.fin-bdet__st { flex: none; }
@media (max-width: 900px) {
  .fin-bdet__item { grid-template-columns: minmax(0, 1fr) auto; row-gap: 2px; }
  .fin-bdet__who { grid-column: 1 / -1; }
}
