/* ==========================================================================
   Star HUB丨星盟达人管理后台 — css/cost-chart.css
   「成本构成」环形图 + 可点列表（乐哥 2026-09-15）

   为什么单独开一个文件、且所有类都带 `cc-` 前缀：
     本项目全站几十张表都在用 `.table` / `.is-num` / `.cell-sub`。
     图表是**新增展示层**，若蹭用公共类去调样式，改一处会波及无关页面
     （项目已踩过：把 `.table--zebra` 加在页面上导致 8 张表与 40 张表读法不一致）。
     ⇒ 组件自带作用域：`.cc-scope` 之下的一切样式只服务本组件。

   配色（三组各自的同色系深浅）：
     成本三组用**同色系深浅**表示「同一大类的细分」，跨组才是不同色相 ——
     这样环形图上"蓝色一片＝达人相关、橙色一片＝人力、绿色一片＝项目公共"，
     不用读图例就能看出三级结构（原型阶段乐哥已看过这版配色）。
   ========================================================================== */

/* ⚠️⚠️ 配色变量**必须定义在 `:root`**，不能只挂在 `.cc-scope` 上（2026-09-15 实测踩到）。
   ----------------------------------------------------------------------------
   根因：JS 侧取色走 `UI.cssVar()`，而它的实现是
        `getComputedStyle(document.documentElement).getPropertyValue(name)`
   —— 只认 `:root`（html 元素）上的变量。变量若只定义在 `.cc-scope` 上，
   `cssVar` 读到空串 → `stroke=""` → **环形图整张画不出来**、色点与占比条也全空。
   症状极具迷惑性：DOM 里 `.cc-seg` 元素一个不少（11 个），
   所以"段数 = 11"这类断言照样全绿 —— **断言有效 ≠ 断言全绿**（本项目硬教训）。
   ⇒ 放在 `:root` 即可：CSS 变量本就继承，`.cc-scope` 内的元素照样能用。
   命名带 `cc-` 前缀，不会与全局令牌冲突。
   ========================================================================== */
:root {
  /* 达人相关（蓝） */
  --cc-t1: #0064E0; --cc-t2: #2F82E8; --cc-t3: #5E9BF8;
  --cc-t4: #8CB8FF; --cc-t5: #B0CDFC; --cc-t6: #D3E3FD;
  /* 人力成本（橙） */
  --cc-h1: #E37318; --cc-h2: #F09B45; --cc-h3: #F8C078; --cc-h4: #FBD9BC;
  /* 项目公共（绿） */
  --cc-p1: #2BA471; --cc-p2: #57BE8C; --cc-p3: #8AD4AE; --cc-p4: #BFE8D3;
  /* 收入（蓝灰，与成本刻意区分开） */
  --cc-r1: #0064E0; --cc-r2: #9FB6CE;

  /* ===== 收支瀑布的「逐行同高」变量（乐哥 2026-09-17）=====
     ⚠️⚠️ 必须定义在 `:root`，但**当年记的原因不对**（2026-09-21 订正）：
        原注释写的是"因为它们写在 `.cw-scope` 上，所以那条规则整条没被解析"。
        **真因是一条注释被提前闭合**：本文件下方那段说明里写着
        `（--cc-t*` + 结束标记 + `--cc-h*` + 结束标记 + `--cc-p*）`，
        第一个结束标记就把整段注释**当场结束了** ⇒ 紧跟其后的 `.cw-scope { … }`
        被浏览器当成"野文本 + 规则"**整条丢弃**（实测：坏=257 条规则 / 修=258 条，
        少的正是 `.cw-scope`）。
        ⇒ 所以"写在上面的声明不生效"与"写在哪里"无关，**是上面那段注释坏了**。
        这与本文件顶部"配色令牌要放 `:root`"那条是**两回事**，别混。
        现已修掉那个结束标记；`.cw-scope` 本身是**空标记规则**（只有注释、无声明），
        所以这次修复**没有可见变化**，但它把"以后再往 `.cw-scope` 里写东西会静默失效"
        这颗地雷拆了。
        （证据脚本：`tests/css-comment-balance.test.js` —— 逐字符扫全站 CSS 的注释配平。）
        —— 变量放 `:root` 依然是对的（作用域更宽、不依赖任何规则被解析），保留原判。
     · `--cw-row-h`    单行高度（左瀑布条 / 右成本项 **必须同一个值** → 逐行对齐的关键）
     · `--cw-gap`      行间距（左右一致）
     · `--cw-block-gap` 块间距（收入块↔成本块、成本块↔结论块；左右在**同一位置**各加一份） */
  --cw-row-h: 26px;
  --cw-gap: 6px;
  --cw-block-gap: 14px;
}

.cc-scope {
  /* 作用域标记类（样式归属），配色变量继承自 :root，见上 */
}

/* ---------------- 布局：左环 + 右列表 ---------------- */
.cc-flex { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
.cc-donut-box { position: relative; flex: none; }
.cc-donut-box svg { display: block; }
/* 环心文字：绝对定位盖在 SVG 上（SVG 内写字要自己算换行，不如 DOM 稳） */
.cc-center {
  position: absolute; left: 0; right: 0; text-align: center; pointer-events: none;
}
.cc-center__t { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.5; }
.cc-center__v {
  font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold);
  color: var(--color-fg); letter-spacing: -0.3px; font-variant-numeric: tabular-nums;
}
.cc-center__s { font-size: var(--font-size-xs); color: var(--color-meta); margin-top: 2px; }

/* 列表限宽：真实页面里这块卡片宽约 1240px，不限宽时「名称」那一列
   会把「金额 / 占比条 / 占比」一路推到最右侧，中间空一大片、三列读不成一组
   （实测截图：金额与占比之间隔着约 300px 空白）。限宽后三列成组、扫读顺畅。 */
.cc-list { flex: 1 1 300px; min-width: 280px; max-width: 720px; }

/* 列表行：可点（下钻）。⚠️ 悬停**不用换底色** ——
   卡片默认底就是 --color-surface-2（本项目当 hover 底用的那一档），同色等于没反馈；
   故用「左边框 + 底色淡染」表达可点与选中（与 cost-drill 卡片的选择态一致）。 */
.cc-li {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 5px var(--space-2); border-radius: var(--radius-sm);
  font-size: var(--font-size-sm); border-left: 2px solid transparent;
  transition: background-color var(--motion-fast) var(--motion-ease-standard);
}
.cc-li--pick { cursor: pointer; }
.cc-li--pick:hover { background: var(--color-surface-2); border-left-color: var(--color-border); }
.cc-li.is-on { background: var(--color-primary-50); border-left-color: var(--color-primary-500); }
.cc-li.is-dim { opacity: 0.4; }

.cc-dot { flex: none; width: 9px; height: 9px; border-radius: 2px; }
.cc-li__n { flex: 1 1 auto; color: var(--color-fg); min-width: 0; }
.cc-li__n em {
  font-style: normal; font-size: var(--font-size-xs); color: var(--color-meta);
  margin-left: 5px; white-space: nowrap;
}
.cc-li__v {
  flex: none; width: 84px; text-align: right; color: var(--color-fg);
  font-variant-numeric: tabular-nums;
}
.cc-li__p {
  flex: none; width: 52px; text-align: right; color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}
/* 表头行 */
.cc-li--hd {
  color: var(--color-muted); font-size: var(--font-size-xs); cursor: default;
  border-bottom: 1px solid var(--color-border); border-radius: 0;
  border-left-color: transparent; padding-bottom: 6px;
}
.cc-li--hd:hover { background: none; border-left-color: transparent; }
/* 分组小标题 */
.cc-grp {
  font-size: var(--font-size-xs); font-weight: var(--font-weight-medium);
  color: var(--color-muted); padding: 9px var(--space-2) 3px;
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2);
}
.cc-grp b { color: var(--color-fg-2); font-weight: var(--font-weight-medium); }
/* 占比条（列表行内，极细） */
.cc-bar { flex: none; width: 56px; height: 6px; border-radius: 3px; background: var(--color-border-soft); overflow: hidden; }
.cc-bar > i { display: block; height: 100%; border-radius: 3px; }

/* ---------------- 扇形/图例交互 ---------------- */
.cc-seg { transition: opacity var(--motion-fast) var(--motion-ease-standard); cursor: pointer; }
.cc-seg:hover { opacity: 0.78; }
.cc-seg.is-dim { opacity: 0.22; }
.cc-seg.is-hl { opacity: 1; }

/* ---------------- 下钻面板（点某项后展开它的构成） ---------------- */
.cc-panel {
  margin-top: var(--space-4); padding: var(--space-4);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.cc-panel__hd {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
  color: var(--color-fg); margin-bottom: var(--space-2); flex-wrap: wrap;
}
.cc-panel__hd .cc-sub { font-weight: normal; font-size: var(--font-size-xs); color: var(--color-meta); }
.cc-panel__hd .cc-spacer { margin-left: auto; }
/* 「看逐笔」入口：走全站一致的虚线下划线可点样式语义，
   但这里是**按钮形态**（只有一个动作，不是表格里几十个可点格），故用 outline 按钮。 */
.cc-more {
  border: 1px solid var(--color-border); background: var(--color-surface);
  color: var(--color-accent-text); border-radius: var(--radius-sm);
  padding: 3px 10px; font-size: var(--font-size-xs); cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
}
.cc-more:hover { border-color: var(--color-accent); }

/* 构成明细表（面板内）—— ⚠️ 数字列在这里**靠右**（乐哥 2026-09-15 要求），
   故显式覆盖，不依赖 .is-num 的全局对齐。 */
.cc-tbl { width: 100%; border-collapse: collapse; font-size: var(--font-size-sm); }
.cc-tbl th {
  text-align: left; color: var(--color-muted); font-weight: var(--font-weight-medium);
  font-size: var(--font-size-xs); padding: 6px var(--space-2);
  border-bottom: 1px solid var(--color-border); white-space: nowrap;
}
.cc-tbl td {
  padding: 7px var(--space-2); border-bottom: 1px solid var(--color-border-soft);
  color: var(--color-fg); vertical-align: middle;
}
.cc-tbl tr:last-child td { border-bottom: none; }
.cc-tbl .cc-num { text-align: right; font-variant-numeric: tabular-nums; }
.cc-tbl tfoot td { font-weight: var(--font-weight-medium); border-top: 1px solid var(--color-border); }
.cc-tbl .cc-hint { color: var(--color-meta); font-size: var(--font-size-xs); }
/* ⚠️ 2026-09-17 删除：此处原有 `.cc-tbl .cc-idx`（带描边的圆形徽章）——
   实测看着像"可点的按钮"，而它其实只是行号。新样式在下方「下钻面板 UI 优化」区
   （无边框、无底色、右对齐的纯数字），本节不再重复定义，避免同选择器两处各写一套。 */
/* 残差项：必须显眼 —— 它是"口径缺口"的信号，不能看着像普通项 */
.cc-residual td { background: var(--color-warning-bg); }
.cc-residual .cc-flag { color: var(--color-warning-text); font-weight: var(--font-weight-medium); }

/* 平台子行（成本下钻 · 构成表，2026-09-17）：
   构成项下面按平台展开（微信小店 / 抖音），子行左侧留出缩进 + 竖线，表明从属关系；
   子行第二行是「计算规则 + 数」（净实收 × 费率 × 税系 = 金额），可照着验算。 */
.cc-sub td { background: var(--color-surface); }
.cc-sub-name {
  display: inline-block; padding-left: var(--space-4); position: relative;
  color: var(--color-fg-2); font-weight: var(--font-weight-medium);
}
.cc-sub-name::before {
  content: ''; position: absolute; left: 4px; top: 0.62em;
  width: 6px; height: 6px; border-left: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}
.cc-rule { font-variant-numeric: tabular-nums; padding-left: var(--space-4); }
.cc-rule b { color: var(--color-fg-2); }

.cc-empty { font-size: var(--font-size-sm); color: var(--color-muted); line-height: 1.8; }
.cc-note {
  font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.8;
  margin-top: var(--space-3); padding-top: var(--space-2);
  border-top: 1px dashed var(--color-border);
}
.cc-note b { color: var(--color-fg-2); }

/* 面板内的小标题（如「谁花的」）——用于分开"同一张面板里的两个维度"：
   上面 = 这笔钱**是什么**（构成项）；下面 = 这笔钱**花在谁身上**（按达人）。
   改前它借用了 `.cc-sub`（表格子行的类名）且无任何样式 → 渲染成正文灰字、层级弱，
   两张表看着像一张连续的表。现在：上分隔线 + 加粗 + 右侧附注，明确"换了一个维度"。 */
.cc-sec {
  display: flex; align-items: baseline; gap: var(--space-2);
  margin: var(--space-5) 0 var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.cc-sec__n { font-size: var(--font-size-xs); font-weight: 400; color: var(--color-meta); }

/* ==========================================================================
   下钻面板 UI 优化（乐哥 2026-09-17：「点击某一个成本项不是会展开吗？展开后的页面 ui 优化下」）
   ========================================================================== */

/* 标题行：标题独占视觉重心，口径标签跟随，动作按钮靠右 —— 不再让长句挤在中间。
   （长句已下沉到 `.cc-panel__sub`，见 cost-chart.js；间距见上方 `.cc-panel__hd` 本体） */
.cc-panel__t {
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
  color: var(--color-fg); letter-spacing: -0.1px;
}
/* 副行：说明这一类的粒度（如「该类粒度是『订单』，没有单据可列」） */
.cc-panel__sub {
  font-size: var(--font-size-xs); color: var(--color-meta);
  margin-bottom: var(--space-3);
}

/* 序号：改前是「带描边的圆形徽章」，看着像可点的按钮（实测乐哥会以为能点）。
   改成**纯粹的行号**（无边框、无底色），只在视觉上起"第几项"的作用。 */
.cc-tbl .cc-idx {
  display: inline-block; width: 16px; margin-right: 6px;
  color: var(--color-meta); font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums; text-align: right;
  background: none; border: 0; border-radius: 0; height: auto;
}
/* 行 hover：这张表列多（构成项/金额/占比/占比条/深入），
   没有横贯整行的高亮时，眼睛容易串行（2026-09-17 加）。 */
.cc-tbl tbody tr:hover td { background: var(--color-primary-50); }
/* 残差行有底色（警告），hover 时不能把它盖掉 —— 它本身就是要显眼的信号 */
.cc-tbl tbody tr.cc-residual:hover td { background: var(--color-warning-bg); }

/* 脚注区：口径说明 + 同源校验合并成一段（改前是两行独立灰字，像随口附注、容易被当噪音跳过）。
   `__k` 是「口径」这个小标，`__v` 是内容 —— 让它读起来像"一个说明块"而不是"两句附注"。 */
.cc-note--foot { display: flex; gap: var(--space-2); align-items: flex-start; border-top-style: solid; }
.cc-note__k {
  flex: none; color: var(--color-fg-2); font-weight: var(--font-weight-medium);
  padding: 0 6px; border-radius: 3px; background: var(--color-surface);
  border: 1px solid var(--color-border-soft); line-height: 1.6;
}
.cc-note__v { min-width: 0; }
/* 未归类警告：单独一条、给足视觉重量（它是"口径缺口"，不是普通附注） */
.cc-note--warn {
  color: var(--color-warning-text); border-top-color: var(--color-warning);
  display: flex; align-items: baseline; gap: 5px;
}

/* 图例（下方，用于两个饼并排时压缩高度） */
.cc-mini-legend { margin-top: var(--space-3); }

/* 窄屏：环与列表上下排 */
@media (max-width: 900px) {
  .cc-flex { gap: var(--space-4); }
  .cc-list { flex: 1 1 100%; min-width: 0; }
}

/* ==========================================================================
   以下为「成本看板」v2（乐哥 2026-09-16 定稿）—— 图中图 / 双图对比 / 费比 / 三张图
   全部仍以 `cb-` 前缀作用域隔离，不动任何公共类。
   ========================================================================== */

.cb-scope { }

/* ---------------- 图中图：总图 + 旁边细分 ----------------
   ⚠️⚠️ 「主区 / 细分区」必须各自成组（2026-09-16 修）：
   把 4 个元素（主环/主列表/细分环/细分列表）平铺在同一个 flex 里时，点开细分它们会**互相抢宽度**——
   实测容器 1166px 下主列表被从 420px 压到 349px（三列文字立刻变挤）、整块长高 62px。
   分组后：主区宽度由内容决定（稳定），细分去吃剩余空间；容器不够时细分整体折到下一行。
   ⇒ 结论：**任何「点一下会多出一块」的并排布局，都要先分组再并排**，否则原内容会被挤。 */
.cb-flex { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
.cb-fl { display: flex; gap: var(--space-6); align-items: flex-start; }
/* 主区：宽度由内容决定（环 208 + 间距 + 列表 420 = 652），**不参与收缩** → 永不被挤压 */
.cb-fl--main { flex: 0 0 auto; }
/* ⚠️ 主列表必须**写死宽度**：`.cb-lg` 原靠父级 flex 的剩余空间长大（到 max-width 420），
   分组后主区是 auto 宽、没有"剩余空间"可吃 → 只落到内容自然宽 315px（三列明显变紧）。
   写死 420 后主区 = 652，点开细分仍不变。 */
.cb-fl--main .cb-lg { flex: none; width: 420px; max-width: 100%; }
/* 细分区：吃剩余空间；basis 380 + min-width 380 是「能不能并排」的判据 ——
   主区 652 + 间距 24 + 380 = 1056：容器 ≥ 1056 就并排（满足"点大扇区→旁边出细分"），
   更窄则整体折到下一行（此时主区仍 652，绝不被压扁）。
   ⚠️ 不要写 `flex-basis: auto`：那会用 max-content（环188+间距+列表420=632），
      于是 652+24+632=1308 —— 连 1680 屏都放不下、总是折行，白白丢掉"旁边"的形态。 */
.cb-fl--sub { flex: 1 1 380px; min-width: 380px; }
/* ⚠️⚠️ 必须写这一条：`.cb-fl` 的 `display:flex` 是**作者样式**，会盖掉 `[hidden]` 的
   UA 样式 `display:none`（两层特异性同为 0,1,0，作者优先）→ 不写的话 hidden 完全失效、
   没有选中时细分也一直显示。用双类提到 0,2,0 即可。 */
.cb-fl[hidden] { display: none; }
/* 极窄屏（容器 < 约 680px）兜底：主区固定 652px 会溢出 → 放开让它收缩，
   此时主列表跟着缩（宁可窄，也不能横向溢出）。断点按**视口**写，容器宽 ≈ 视口 - 侧栏 - 内边距。 */
@media (max-width: 1100px) {
  .cb-fl--main { flex: 1 1 auto; min-width: 0; }
  .cb-fl--main .cb-lg { flex: 1 1 auto; width: auto; min-width: 0; }
}
.cb-ring { position: relative; flex: none; }
.cb-ring__c { position: absolute; left: 0; right: 0; text-align: center; pointer-events: none; }
.cb-ring__t { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.5; }
.cb-ring__v { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); color: var(--color-fg); font-variant-numeric: tabular-nums; }
.cb-seg { cursor: pointer; transition: opacity var(--motion-fast) var(--motion-ease-standard); }
.cb-seg:hover { opacity: 0.78; }
.cb-lg { flex: 1 1 240px; min-width: 230px; max-width: 420px; }
.cb-li { display: flex; align-items: center; gap: var(--space-2); padding: 5px var(--space-2);
  border-radius: var(--radius-sm); font-size: var(--font-size-sm); border-left: 2px solid transparent; cursor: pointer; }
.cb-li:hover { background: var(--color-surface-2); border-left-color: var(--color-border); }
.cb-li.is-on { background: var(--color-primary-50); border-left-color: var(--color-primary-500); }
.cb-li--hd { font-size: var(--font-size-xs); color: var(--color-muted); border-bottom: 1px solid var(--color-border);
  border-radius: 0; cursor: default; }
.cb-li--hd:hover { background: none; border-left-color: transparent; }
.cb-dot { flex: none; width: 9px; height: 9px; border-radius: 2px; }
.cb-li__n { flex: 1 1 auto; color: var(--color-fg); min-width: 0; }
/* ⚠️ 名称列**必须能省略，不能逐字换行**（2026-09-16 修，乐哥报障「文字内容挤压」）：
   窄屏下容器变窄，而 `.cb-ring` 是 `flex:none`（环不让位）→ 名称列被压到只剩十几个像素，
   中文没有空格，浏览器就**逐字换行**：表头「构成项」竖成"构/成/项"、数据「招待费成本」
   竖成"招/待/费/成/本"，金额列与占比列（固定 78+50px）被顶出卡片 100px 以上。
   ⇒ 单行 + 省略号：宁可把名字截短（有 title 可悬停），也不能让它竖排把整块撑坏。 */
.cb-li__n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-li__v { flex: none; width: 78px; text-align: right; color: var(--color-fg); font-variant-numeric: tabular-nums; }
.cb-li__p { flex: none; width: 50px; text-align: right; color: var(--color-muted); font-variant-numeric: tabular-nums; }
.cb-bar { flex: none; width: 44px; height: 6px; border-radius: 3px; background: var(--color-border-soft); overflow: hidden; }
.cb-bar > i { display: block; height: 100%; border-radius: 3px; }
/* ⚠️⚠️ 窄屏兜底（2026-09-16 修，乐哥报障「文字内容挤压」）：
   实测容器只剩 ~300px 时，环（208px，flex:none **不让位**）+ 列表被挤在同一行，
   列表的 clientWidth 只剩 67px，而「金额 64 + 占比 44 = 108px」是**固定列宽** ——
   固定列已经超过容器 → 名称列被压到 **0 宽**（逐字竖排），金额/占比被顶出卡片 80px+。
   ⇒ 正确做法：拿不下就**让环与列表上下摞起来**（列向 stack），而不是硬挤在一行。
      环和列表各自获得整行宽度，任何一列都不会被压到 0。 */
@media (max-width: 900px) {
  .cb-flex { flex-direction: column; gap: var(--space-4); }
  .cb-fl { flex-direction: column; gap: var(--space-4); width: 100%; }
  .cb-fl--main { flex: 1 1 auto; }
  /* ⚠️ 细分区在窄屏也要放开 min-width（原来固定 380px，
     `.cb-scope` 只有 301px 时内容宽 380 → **细分列表被裁掉「占比」列**，实测踩到）。
     「能不能并排」只在宽屏有意义；窄屏已经改成上下摞，此时必须允许收缩到容器宽。 */
  .cb-fl--sub { min-width: 0; flex: 1 1 auto; width: 100%; }
  /* 列表占满整行；环居中 */
  .cb-fl--main .cb-lg, .cb-fl .cb-lg { flex: none; width: 100%; max-width: 100%; }
  .cb-fl .cb-ring { align-self: center; }
}
/* 更窄（手机竖屏）：金额/占比列再收一档，保证名称列至少有 ~60px 可读宽度 */
@media (max-width: 480px) {
  .cb-li__v { width: 58px; }
  .cb-li__p { width: 40px; }
}
@media (max-width: 1100px) {
  .cb-li .cb-bar { display: none; }
  .cb-li__v { width: 64px; }
  .cb-li__p { width: 44px; }
}
.cb-empty { color: var(--color-meta); font-size: var(--font-size-sm); text-align: center; padding: var(--space-6) 0; }

/* 筛选条（作用于总图） */
.cb-filter { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; margin-bottom: var(--space-4); }
.cb-filter__lb { font-size: var(--font-size-xs); color: var(--color-muted); }
.cb-filter select { height: 30px; border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  background: var(--color-surface); color: var(--color-fg); font-size: var(--font-size-sm); padding: 0 8px; }
.cb-hint { font-size: var(--font-size-xs); color: var(--color-primary-600); background: var(--color-primary-50);
  padding: 2px 8px; border-radius: var(--radius-sm); }

/* ---------------- 双图对比（绝对值 + 百分比）----------------
   ⚠️⚠️ 三层结构缺一不可（本轮连踩两次，见 cost-board.js 顶部说明）：
      .cb-row__t 轨道（flex:1，提供百分比基准）
      .cb-row__b 条（width:X% 定量）
      .cb-row__b>i 分段（width:Y% 按占比切）
   两次踩的坑都表现为"数元素全绿、图是坏的"：
      ① 分段直接挂轨道 + flex-basis → flex-grow:1 把 13 条拉成一样长
      ② 省掉 .cb-row__b 一层 → <i> 是行内元素、width 不生效 → 条宽全为 0
   -------------------------------- */
/* ---------------- 竖向柱状图的画布容器（2026-09-16 乐哥：横坐标改成达人名）----------------
   ⚠️ Chart.js 必须有一个**有确定高度**的父容器（配 maintainAspectRatio:false），
      否则它会按宽度自己算高度、或塌成 0 高 —— 图就"看不见但元素都在"。
   ⚠️ 高度直接决定「柱内标注能标下几段」：段高 = 段值÷Y轴最大值×绘图区高，
      绘图区高 ≈ 容器高 − 30（轴与标签）。实测 320px 时 37 个段里有 24 段太矮标不下；
      加到 400px 后能标的段明显变多（乐哥要的是"柱子里看到得数"，故宁高勿矮）。
   ⚠️ X 轴达人名是斜排的（52°），容器也要预留标签高度，否则标签被裁。 */
.cb-canvas { position: relative; height: 400px; margin-bottom: var(--space-2); }
/* .cb-canvas--roi（投产比图高度 300px）已于 2026-09-20 随 ROI 删除 —— 无 JS 引用 */
/* 「所有达人成本对比」上下两张图（乐哥 2026-09-16：「这俩表稍微小点，太大了」）
   ⚠️ 降高会**减少柱内标注**（判据：段高 = 段值÷Y轴最大值×绘图区高 ≥ 18px），
      故实测过候选高度再定，不能凭感觉砍：
        400px → 绝对值标出 13/37 段、百分比 33/36 段
        300px → 绝对值标出 11/37 段、百分比 31/36 段（只差 2 段，省下 200px 页面高度）
      再往下（280px）开始明显掉标注，收益也小，故停在 300px。 */
.cb-canvas--cmp { height: 300px; }
.cb-canvas canvas { display: block; }

/* ---------------- 对齐行网格（乐哥 2026-09-16 第三轮）----------------
   「左边柱状图、右边列表，达人名字的行左边柱状和右边列表保持同一行吧，这样看着直观」

   做法：**不并排两个容器**，而是「一行一个 grid」—— 条与数值列写在同一行的不同列里。
   为什么不用"左右两栏各自渲染 + 靠行高相等来对齐"：左栏 `.cb-hb` 行高 24px、
   右栏 `<table>` 行高 30px，行数一多就逐行错开（14 行能错开近百像素），
   而且窄屏折成上下两栏后对齐关系彻底失效。同一个 grid 行天然同行，与行高无关。

   ⚠️ 列宽用 CSS 变量统一定义，**表头行与数据行共用同一套模板** ——
      两边各写一份 `grid-template-columns` 必然漂移（同类问题：
      表头与数据对不齐，比不居中更难发现）。
   ⚠️ 条仍是「轨道 → 条」两层（`.cb-g-bar > i`），`<i>` 必须 `display:block`
      否则 width 不生效、条宽全为 0（本项目在 cb-row/cb-hb 上踩过两次）。 */
.cb-grid { --cb-n: 136px; --cb-v: 96px; --cb-x: 92px;
  display: grid; align-items: center; gap: 10px;
  padding: 7px 6px; border-radius: var(--radius-sm); font-size: var(--font-size-xs); }
.cb-grid--n4 { grid-template-columns: var(--cb-n) minmax(80px, 1fr) var(--cb-v) var(--cb-x); }
.cb-grid--n5 { grid-template-columns: var(--cb-n) minmax(80px, 1fr) var(--cb-v) var(--cb-x) var(--cb-x); }
/* 6 列：名 + 条 + 4 个数值列（成本结构环比：上月/本月/占比变化/金额变化） */
.cb-grid--n6 { grid-template-columns: var(--cb-n) minmax(72px, 1fr) var(--cb-v) var(--cb-v) var(--cb-x) var(--cb-x); }
.cb-grid--hd { color: var(--color-muted); font-size: 11.5px; font-weight: var(--font-weight-medium);
  padding: 4px 6px 7px; border-bottom: 1px solid var(--color-border); }
/* 合计行（tfoot 的等价物）—— 与数据行**同一套列模板**，所以数字天然落在同一列上；
   只在视觉上做区分：顶部分隔线 + 字重。 */
.cb-grid--ft { font-weight: var(--font-weight-medium); border-top: 1px solid var(--color-border);
  padding-top: 9px; margin-top: 2px; }
.cb-grid--ft:hover { background: none; }
.cb-grid:not(.cb-grid--hd):hover { background: var(--color-surface-2); }
.cb-g-n { color: var(--color-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-g-bar { height: 16px; border-radius: var(--radius-sm); background: var(--color-surface-2); overflow: hidden; }
.cb-g-bar > i { display: block; height: 100%; border-radius: var(--radius-sm); }
/* 多条（成本结构环比：上一条=上月、下一条=本月）—— 纵向叠放，整格仍只占一行的位置，
   所以「条」与同一行的数值列依然在同一条水平线上（这正是改这个形态的目的）。 */
.cb-g-bar--multi { display: flex; flex-direction: column; gap: 3px; justify-content: center; height: 24px; }
.cb-g-bar--multi > i { height: 8px; flex: none; box-sizing: border-box; }
/* 「上月」是**空心参考条**：细一档、描边淡一点（2026-09-17 实测调整）
   ----------------------------------------------------------------------------
   第一版让空心条与实心条同高同浓（都是 8px、1.5px 实色描边）→ 实测放大看**两团的
   视觉重量相当**，读者看到的是"两条并列的条"，而不是"一条参考线 + 一条主条"。
   ⇒ 空心条改为 **5px 高 + 1px 描边、透明度 0.85**：
      · 高度差本身就建立了层级（细=次要/过去，粗=当前）
      · 描边要**接近实色**（0.85，不是 0.4）—— 再淡在浅底上又会回到"看不见"的老问题
   语义不变：空心/细 = 上月，实心/粗 = 本月。 */
.cb-g-bar--multi > i:first-child { height: 5px; margin-top: 1.5px; }
.cb-g-bar--multi > i:first-child { opacity: 0.85; }
.cb-g-v { text-align: right; font-variant-numeric: tabular-nums; font-feature-settings: "tnum", "lnum";
  color: var(--color-fg); white-space: nowrap; }
/* ⚠️ 次要列常放**长文本**（如"涉及的达人"名单）—— 必须截断，否则 nowrap 会把它顶出行宽：
   实测 8 个达人名把 548px 的行撑到 1158px（行内横向溢出，看着像表格坏了）。
   完整内容仍可悬停查看（调用方传 title）。 */
.cb-g-v--sub { color: var(--color-muted); overflow: hidden; text-overflow: ellipsis; }
/* 数值格里的**第二行小字**（2026-09-17 加，给「成本结构环比」的本月占比用）——
   把补充信息塞进同一格，避免为了多给一个数而加一列（列一多每列就窄到没法看）。
   ⚠️ 用 `em` 元素承载：`<em>` 默认斜体，必须显式 `font-style: normal` 抹掉，
      否则"占 75.1%"会斜着显示（与旁边数字风格不一致）。 */
.cb-g-em { display: block; font-style: normal; font-size: 11px; line-height: 1.25;
  color: var(--color-muted); font-weight: var(--font-weight-regular); }
/* 「最后一列是长名单」的变体：把最后一列从固定窄列换成可伸展列，
   让名单能多显示几个字（约 200px），而不是只剩 2 个字 + 省略号。
   ⚠️⚠️ 列数必须与 `--n5` **完全一致（5 列）** —— 我第一版只写了 4 列，
      第 5 个格子被挤到第二行（几何断言实测"数值列中心偏离 598px"直接抓到）。
      改模板时先数格子：名 + 条 + (cols-2) 个数值列。 */
.cb-grid--n5.cb-grid--widelast { grid-template-columns: var(--cb-n) minmax(72px, 1fr) var(--cb-v) var(--cb-v) minmax(150px, 1.2fr); }
.cb-grid--hd .cb-g-v { text-align: right; }
@media (max-width: 1100px) {
  .cb-grid { --cb-n: 106px; --cb-v: 86px; --cb-x: 78px; }
  .cb-grid--n5 { grid-template-columns: var(--cb-n) minmax(56px, 1fr) var(--cb-v) var(--cb-x); }
  .cb-grid--n5 > .cb-g-v:last-child { display: none; }
  /* n6 窄屏直接砍掉最后一列（金额变化）—— 剩下 5 列仍能读，且**不会横向溢出**。
     ⚠️ 砍列前先确认「留下的列自己说得清」：上月/本月/占比变化三列已能表达环比的结论。 */
  .cb-grid--n6 { grid-template-columns: var(--cb-n) minmax(48px, 1fr) var(--cb-v) var(--cb-v) var(--cb-x); }
  .cb-grid--n6 > .cb-g-v:last-child { display: none; }
  /* ⚠️⚠️ `--widelast` 是**双类**选择器（特异性 0,2,0），比这里的单类规则（0,1,0）高 ——
     不补这两行的话，窄屏根本收缩不了（实测 1024px 仍算出 5 列、最右列不隐藏）。
     结论：**给某个变体提特异性时，必须同步检查它在媒体查询里的对应规则**。 */
  .cb-grid--n5.cb-grid--widelast { grid-template-columns: var(--cb-n) minmax(56px, 1fr) var(--cb-v) var(--cb-x); }
  .cb-grid--n5.cb-grid--widelast > .cb-g-v:last-child { display: none; }
}
/* 联动画板里的 KPI 小卡（汇总 / 盈亏 / 收入 三个视图共用） */
.cb-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3); margin-bottom: var(--space-4); }
.cb-kpi { border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: 11px 14px; background: var(--color-surface); }
.cb-kpi__t { font-size: var(--font-size-xs); color: var(--color-muted); margin-bottom: 4px; }
.cb-kpi__v { font-size: 19px; font-weight: var(--font-weight-semibold); color: var(--color-fg);
  font-variant-numeric: tabular-nums; }
.cb-kpi__h { font-size: 11.5px; color: var(--color-meta); margin-top: 3px; }

/* ---------------- 联动高亮（乐哥 2026-09-16 第三轮）----------------
   点了扇形 / 筛了达人之后：命中的照常显示，没命中的整体压暗。
   ⚠️ 只压暗、不改布局与字号 —— 变暗的元素仍要能读（用户可能正想核对它）。
   ⚠️ 不给 `.is-dim` 元素加 `pointer-events:none`：hover 仍要能看 tooltip。 */
.cb-focus { font-size: var(--font-size-xs); color: var(--color-fg-2); line-height: 1.7;
  background: var(--color-primary-50); border-left: 3px solid var(--color-primary-500);
  border-radius: var(--radius-sm); padding: 5px 10px; margin-bottom: var(--space-2); }
.cb-focus b { color: var(--color-fg); }
.cb-tbl tr.is-dim,
.cb-mo.is-dim,
.cb-bars__col.is-dim { opacity: .34; }
/* ⚠️⚠️ 行网格的行高亮（2026-09-16 补）——
   此前 `applyFocus` 会给 `.cb-grid` 挂 `is-hl/is-dim`，但**这里没有对应的规则**：
   类加上去了、视觉一点没变。旧断言只查"class 是否存在"所以一直显示绿
   （典型"断言有效但没测到东西"）。
   ⇒ 门禁必须查 **computed 样式**（background-color / opacity 真的变了），不是查 class 名。 */
.cb-grid.is-dim { opacity: .34; }
.cb-grid.is-hl { background: var(--color-primary-50); border-radius: var(--radius-sm); }
.cb-grid.is-hl:hover { background: var(--color-primary-50); }
.cb-tbl td.is-dim, .cb-tbl th.is-dim { opacity: .34; }
.cb-tbl tr.is-hl { background: var(--color-primary-50); }
.cb-tbl td.is-hl, .cb-tbl th.is-hl { background: var(--color-primary-50); color: var(--color-fg); }
.cb-mo.is-hl { background: var(--color-primary-50); border-radius: var(--radius-sm); }

.cb-chart { position: relative; padding-left: 104px; }
.cb-row { display: flex; align-items: center; gap: var(--space-2); padding: 2px 0; font-size: var(--font-size-xs); }
.cb-row__n { position: absolute; left: 0; width: 98px; text-align: right; color: var(--color-fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-row__t { flex: 1 1 auto; height: 20px; background: var(--color-surface-2); border-radius: var(--radius-sm); overflow: hidden; }
.cb-row__b { height: 100%; display: flex; }
.cb-row__b > i { display: block; height: 100%; }
.cb-row__v { flex: none; width: 74px; text-align: right; font-variant-numeric: tabular-nums; font-size: var(--font-size-xs); color: var(--color-fg); }
.cb-axis { position: relative; height: 18px; margin-top: 4px; }
.cb-axis span { position: absolute; transform: translateX(-50%); font-size: 10.5px; color: var(--color-muted); white-space: nowrap; }
/* ⚠️⚠️ 这条规则**必须限定在 .cb-chart 作用域内**（2026-09-16 修，乐哥报障「文字高度重叠、
   整页有看不清的虚线、图表全没了」）：
     它原本是旧「横条图」的**竖向网格线**（给 .cb-chart 里的刻度线用）。
     但 `.cb-grid` 这个类名后来被**对齐行网格组件**（cost-board.js 的 rowsGrid）占用了 ——
     一个类名两套语义，于是每个行网格都被这条规则变成
     `position:absolute; top:0; bottom:0; border-left:1px dashed` →
     实测：全部行网格叠在页面左上角、尺寸被拉成 549×1200px、互相遮挡，
     表现为「KPI 文字重叠 + 满页半透明虚线 + 图表块整片消失」（其实 DOM 里都在）。
     ⚠️ 断言层面它极难抓：DOM 元素一个不少、class 也全对，
     只有量 `getBoundingClientRect()` 才发现尺寸/位置是坏的。
   ⇒ 旧组件（.cb-chart/.cb-row/.cb-axis）如有使用，行为**完全不变**；新行网格不再受污染。 */
.cb-chart .cb-grid { position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--color-border-soft); pointer-events: none; }
.cb-legend { display: flex; gap: var(--space-3); flex-wrap: wrap; font-size: var(--font-size-xs); color: var(--color-muted); margin-bottom: var(--space-2); }
.cb-legend span { display: flex; align-items: center; gap: 5px; }
.cb-legend i { width: 9px; height: 9px; border-radius: 2px; display: block; }
/* ==========================================================================
   图表小节标题（乐哥 2026-09-17 提版式问题后重构）
   ---------------------------------------------------------------------------
   原状：`font-size-sm`(14px) / `margin: 16px 0 8px`。乐哥原话：
     「这个标题字号大一点，和上面的模块（包括图）留一点行间距，现在挨在一起看着非常不直观」

   现版式（三条都是实测取值，不是拍脑袋）：
     · 字号 14px → **16px**（`--font-size-lg`，与「卡片标题」同档；窄屏令牌自动降到 15px）
     · 字重 500 → **600**（标题要能从正文里"跳"出来；16px 用 medium 仍偏软）
     · 距上方 16px → **24px**（`--space-6`）—— 实测原 16px 时标题紧贴上方图表/列表底边，
       视觉上被读成"上一块的一部分"
     · 距下方 8px → **12px**（`--space-3`）—— 原 8px 时标题与其图贴得太近，像图的副标题

   结构改为**两级**（乐哥：「标题很多都是我之前口语化的」）：
     · 主标题 = **名词短语**（是什么），不带括号、不带问句、不带动词短语
     · 副标题 `.cb-sub__hint` = **口径与读法**（怎么读），小字弱色，跟在主标题右侧
   用 flex + baseline 让两者同行对齐；hint 过长时自动换到下一行（row-gap 2px 保证紧贴）。
   ========================================================================== */
.cb-sub {
  display: flex; flex-wrap: wrap; align-items: baseline;
  /* ⚠️ 主副标题间隔用 `--space-3`(12px) 而非 `--space-2`(8px)：
     实测 16px 主标题 + 12px 副标题时，8px 间隔视觉上仍像"粘连的一个词"
     （「成本构成6 大类」读起来像「成本构成6」+「大类」）。 */
  column-gap: var(--space-3); row-gap: 2px;
  font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold);
  color: var(--color-fg); line-height: var(--font-line-height-tight);
  margin: var(--space-6) 0 var(--space-3);
}

/* ---------------- 图 + 表并排 ----------------
   ⚠️ 窄屏必须放开 min-width（2026-09-16）：原来写死 400/360px，
      容器 < 760px 时两栏都放不下 → 内容**越出卡片**（与构成列表同一个坑）。
      窄屏改成上下摞，每栏各得整行宽。 */
.cb-split { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
.cb-split__chart { flex: 1 1 520px; min-width: 400px; }
.cb-split__table { flex: 1 1 440px; min-width: 360px; }
@media (max-width: 900px) {
  .cb-split { flex-direction: column; gap: var(--space-4); }
  .cb-split__chart, .cb-split__table { flex: 1 1 auto; min-width: 0; width: 100%; }
}

/* 横条（投产比等）—— 同样是三层结构 */
.cb-hb { display: flex; align-items: center; gap: var(--space-2); padding: 3px 0; font-size: var(--font-size-xs); }
.cb-hb__n { flex: none; width: 132px; text-align: right; color: var(--color-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-hb__t { flex: 1 1 auto; height: 18px; background: var(--color-surface-2); border-radius: var(--radius-sm); overflow: hidden; }
.cb-hb__b { height: 100%; display: block; border-radius: var(--radius-sm); }
.cb-hb__v { flex: none; width: 66px; text-align: right; font-variant-numeric: tabular-nums; font-size: var(--font-size-xs); }
.cb-hb__x { flex: none; width: 84px; text-align: right; color: var(--color-muted); font-variant-numeric: tabular-nums; font-size: var(--font-size-xs); }

/* 分档柱（盈亏分布） */
.cb-bars { display: flex; align-items: flex-end; gap: var(--space-3); height: 180px; padding: 0 var(--space-1); }
.cb-bars__col { flex: 1 1 0; text-align: center; }
.cb-bars__n { font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); margin-bottom: 3px; }
.cb-bars__bar { border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.cb-bars__lb { font-size: 10.5px; color: var(--color-muted); margin-top: 6px; line-height: 1.4; }

/* 环比双条 */
.cb-mo { padding: 5px 0; }
.cb-mo__hd { display: flex; gap: var(--space-2); align-items: center; font-size: var(--font-size-xs); margin-bottom: 3px; }
.cb-mo__nm { flex: none; width: 100px; color: var(--color-fg); }
.cb-mo__dl { margin-left: auto; font-weight: var(--font-weight-medium); }
.cb-mo__t { height: 9px; border-radius: 3px; }
.cb-up { color: var(--color-danger); }
.cb-down { color: var(--color-success); }

/* 明细表（图+表搭配用）
   ⚠️ 数值列对齐必须**与全站一致**（2026-09-16 被 verify-num-right 抓出）：
      全站规范（乐哥 2026-09-14 定）= **长文本左 / 其余居中 / 数值列靠右、表头居中**。
      我第一版把 `.cb-tbl th` 统一写成 `text-align:left`，导致数值列表头左对齐、
      正文靠右 → **同一列出现两条参考线**（正是我在原型里批评过的方案②那种别扭）。
      ⇒ 数值列表头保持**居中**（与全站 .table thead th 一致），正文靠右。 */
.cb-tbl { width: 100%; border-collapse: collapse; font-size: var(--font-size-xs); }
.cb-tbl th { text-align: left; color: var(--color-muted); font-weight: var(--font-weight-medium);
  font-size: 11.5px; padding: 6px 8px; border-bottom: 1px solid var(--color-border); white-space: nowrap; }
.cb-tbl th.is-num { text-align: center; }
.cb-tbl td { padding: 6px 8px; border-bottom: 1px solid var(--color-border-soft); color: var(--color-fg); }
.cb-tbl tr:last-child td { border-bottom: none; }
.cb-tbl tfoot td { font-weight: var(--font-weight-medium); border-top: 1px solid var(--color-border); }
.cb-tbl .cb-n { text-align: right; font-variant-numeric: tabular-nums; font-feature-settings: "tnum", "lnum"; }
.cb-tbl .cb-hint { color: var(--color-meta); font-size: 11px; }
.cb-warnrow { background: var(--color-warning-bg); }
.cb-hot { color: var(--color-danger); font-weight: var(--font-weight-semibold); }
.cb-pill { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 10.5px;
  background: var(--color-surface-2); border: 1px solid var(--color-border); color: var(--color-muted); }
.cb-pill--warn { background: var(--color-warning-bg); border-color: transparent; color: var(--color-warning-text); font-weight: var(--font-weight-medium); }
.cb-note { font-size: var(--font-size-xs); color: var(--color-muted); line-height: 1.8;
  margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--color-border); }
.cb-note b { color: var(--color-fg-2); }

@media (max-width: 1100px) {
  .cb-split__chart, .cb-split__table { flex: 1 1 100%; min-width: 0; }
  .cb-chart { padding-left: 88px; }
  .cb-row__n { width: 82px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   2026-09-16 乐哥报障后新增：构成项可点 / 横向堆叠条 / 项目成本分组
   ══════════════════════════════════════════════════════════════════════════ */

/* 构成项"可点"的显式外观 + 键盘可达。
   ⚠️ `.cb-li` 早就有 cursor:pointer 与 hover（所以**看着**能点），
      但**点击事件从来没挂上** → 乐哥报障「点击也没反应」（另一半原因见 cost-board.js 的 noPick）。
      现在由 `--pick` 明确声明"这一行确实可点"，并给出 focus 环（键盘可达）。
   ⚠️ 反过来：不可点的行必须收回 pointer，否则又变成"看着能点却点不动"。 */
.cb-li--pick:focus-visible { outline: 2px solid var(--color-primary-500); outline-offset: -1px; }
.cb-li:not(.cb-li--pick) { cursor: default; }

/* ⚠️⚠️ 多段**横向拼接**成一根构成条（每个达人的费用由不同色块组成）——
   与 `--multi`（**纵向叠放**，给"上月/本月两条对比"用）语义完全不同，
   绝不能共用同一个类：纵向叠放会让"构成"变成两条并列的细线，读不出占比。 */
.cb-g-bar--stack { display: flex; flex-direction: row; }
.cb-g-bar--stack > i { flex: none; border-radius: 0; }
.cb-g-bar--stack > i:first-child { border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); }
.cb-g-bar--stack > i:last-child { border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }

/* 焦点态堆叠柱容器高度（比汇总态 400px 略低：柱数更少、本页更长） */
.cb-canvas--stack { height: 360px; }

/* 副标题：口径与读法（「怎么读」），跟在主标题右侧同一行基线对齐。
   ⚠️ 字号用 `--font-size-xs`(12px) 而非 sm(14px)：它必须与 16px 主标题**拉开层级**，
      同尺寸会让两级标题糊成一句话（这正是改造前"看着不清晰"的原因之一）。 */
.cb-sub__hint { font-weight: var(--font-weight-regular); color: var(--color-meta); font-size: var(--font-size-xs); }

/* 项目成本分组头：固定成本（工资） / 项目归属 */
.cb-grp { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  margin: var(--space-3) 0 var(--space-2); padding: 6px var(--space-2);
  background: var(--color-surface-2); border-radius: var(--radius-sm);
  font-size: var(--font-size-xs); color: var(--color-fg); font-weight: var(--font-weight-medium); }
.cb-grp em { font-style: normal; font-weight: var(--font-weight-regular); color: var(--color-meta); }
.cb-grp b { font-variant-numeric: tabular-nums; }

/* 项目成本逐项行：名称 / 金额 / 占比 / 备注 */
.cb-pjrow { display: grid; grid-template-columns: minmax(120px, 1.4fr) 96px 72px minmax(80px, 1fr);
  align-items: center; gap: 10px; padding: 6px var(--space-2); border-radius: var(--radius-sm);
  font-size: var(--font-size-xs); }
.cb-pjrow:hover { background: var(--color-surface-2); }
.cb-pjrow__n { color: var(--color-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-pjrow__v { text-align: right; font-variant-numeric: tabular-nums; color: var(--color-fg); }
.cb-pjrow__p { text-align: right; font-variant-numeric: tabular-nums; color: var(--color-meta); }
.cb-pjrow__h { color: var(--color-meta); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 「每个人的报销」平铺在报销行下面（只有几个人，平铺比"点进去才看得到"更好读） */
.cb-pjpeople { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4);
  padding: 2px var(--space-2) var(--space-3); }
.cb-pjpeople > span { display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--font-size-xs); color: var(--color-meta); }
.cb-pjpeople i { width: 7px; height: 7px; border-radius: 2px; background: var(--color-chart-3); flex: none; }
.cb-pjpeople b { color: var(--color-fg); font-variant-numeric: tabular-nums; }
.cb-pjpeople em { font-style: normal; color: var(--color-meta); font-size: 11px; }

/* 窄屏：项目成本行的"备注"列会让名称被挤扁，直接收掉（备注是次要信息） */
@media (max-width: 1100px) {
  .cb-pjrow { grid-template-columns: minmax(96px, 1.2fr) 88px 60px; }
  .cb-pjrow__h { display: none; }
}

/* ==========================================================================
   收支瀑布（`cw-` 前缀）—— 月度利润明细「收支构成」卡，乐哥 2026-09-17 选定「方案 A」
   --------------------------------------------------------------------------
   为什么放在本文件而不是新开一个 css：
     它=用的是这里的同一套配色令牌（--cc-t* / --cc-h* / --cc-p*）。配色若分两处定义，
     环形图与瀑布条迟早不同色 —— 同一笔钱两个颜色，用户会以为是两个东西。
   前缀用 `cw-`（不蹭 `cc-`）：两者布局与语义不同，混在同一前缀下以后无法分别改。

   ⚠️ 盈亏颜色**不在这里定义**：用全站既有的 `num-pos` / `num-neg`
      （css/table.css：盈利=红、亏损=绿，涨红跌绿）。项目 2026-09-13 刚因
      "盈利=绿"写反而修过一轮（css/pages.css 有记录），不要再写第三份颜色定义。
   ========================================================================== */
.cw-scope {
  /* 作用域标记类（样式归属）。
     ⚠️ 行高变量 `--cw-row-h` / `--cw-gap` / `--cw-block-gap` **定义在文件顶部 `:root`**，
        不在这里 —— 变量放 `:root` 作用域更宽、也不依赖本规则被解析。
        ⚠️ 但注意：本规则**曾经真的整条不生效**，原因**不是**"东西写在这里"，
        而是**它上面那段注释被提前闭合**了（详见文件顶部 `:root` 处的订正说明）。
        这类"注释写坏 ⇒ 后面的规则被静默丢弃"已有静态门禁：`tests/css-comment-balance.test.js`。 */
}

/* ---------------- 顶部：先回答"本月赚了多少" ---------------- */
.cw-head {
  display: flex; align-items: flex-end; gap: var(--space-6); flex-wrap: wrap;
  padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-border-soft);
  margin-bottom: var(--space-5);
}
.cw-big {
  font-size: 28px; font-weight: var(--font-weight-semibold);
  letter-spacing: -0.4px; line-height: 1.15; font-variant-numeric: tabular-nums;
}
.cw-big small { font-size: var(--font-size-sm); font-weight: 400; color: var(--color-muted); margin-right: 6px; }
.cw-head__sub { font-size: var(--font-size-sm); color: var(--color-meta); margin-top: 3px; font-variant-numeric: tabular-nums; }
.cw-head__r { margin-left: auto; text-align: right; }
.cw-rate { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); font-variant-numeric: tabular-nums; }
/* 「占收入」这三个字要跟着数字，否则 58.7% 会被当成系统别处的「盈亏率(÷GSV) 13.9%」（同页同词两个数） */
.cw-rate small { font-size: var(--font-size-xs); font-weight: 400; color: var(--color-muted); margin-right: 5px; }
.cw-rate__t { font-size: var(--font-size-xs); color: var(--color-meta); }
/* 并排的第二个口径（盈亏率 ÷GSV）：小一号、明确写分母 —— 消除同页两个"率"的歧义 */
.cw-rate__alt { font-size: var(--font-size-xs); color: var(--color-meta); margin-top: 3px; font-variant-numeric: tabular-nums; }

/* ---------------- 主体：左瀑布 + 右清单 ---------------- */
.cw-side { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
.cw-main { flex: 1; min-width: 420px; }
.cw-list { width: 300px; flex: none; display: flex; flex-direction: column; gap: var(--cw-gap); }

.cw-wf { display: flex; flex-direction: column; gap: var(--cw-gap); }
.cw-row {
  display: grid; grid-template-columns: 90px 1fr 106px 58px;
  gap: var(--space-3); align-items: center;
  /* 行高固定 —— 与右侧 `.cw-lr` 用同一个变量，这是逐行对齐的关键 */
  height: var(--cw-row-h);
}
.cw-lbl {
  font-size: var(--font-size-sm); color: var(--color-fg-2); text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cw-track { height: 20px; background: var(--color-surface-2); border-radius: 4px; overflow: hidden; }
.cw-track > i { display: block; height: 100%; border-radius: 4px; }
.cw-val { font-size: var(--font-size-sm); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cw-pct { font-size: var(--font-size-xs); color: var(--color-meta); text-align: right; font-variant-numeric: tabular-nums; }
/* 分隔线：与标签列右缘对齐（90px 标签 + 12px 间距），把"成本"与"最后剩下的"分开 */
/* 结论行（左侧「盈亏」/ 右侧「总成本」）之前的横线 = 原来的 `.cw-sep`。
   ⚠️ 改成 `border-top` 而**不是**独立 div（2026-09-17）：独立 div 会**多占一行**
      （1px 高 + 一个 gap），导致左侧比右侧多一行 → 盈亏行与总成本行错开。
      用 border 后左右行数严格相同，横线也自然跟着这一行走。 */
.cw-row--end, .cw-lr--sum {
  border-top: 1px solid var(--color-border);
  margin-top: var(--cw-block-gap);
}
/* ⚠️ 这里刻意**不写 `padding-top`**：`.cw-lr` 的 `padding: 0 6px` 定义在本规则**之后**，
   同特异性下后者胜，写了也会被重置成 0 —— 留下一条"看着有 padding、实际没有"的声明，
   比不写更容易误导。行高 26px + `align-items:center` 已有约 3px 的自然留白。 */
.cw-row--end .cw-lbl, .cw-row--end .cw-val { font-weight: var(--font-weight-semibold); }
/* 「收入」与第一个成本项之间留块间距（乐哥 2026-09-17：「收入那条线是不是应该和
   下面其他支出模块也留点间距」）——
   ⚠️ 必须**左右各加一份**（左侧加在收入行、右侧加在表头行）：
      只加一侧会让后续所有行错位。 */
.cw-wf > .cw-row:first-child { margin-bottom: var(--cw-block-gap); }
/* 收入拆项副行：仅在"确实有其他收入"时出现（见 cost-waterfall.js），多数月不占版面 */
/* ⚠️ 2026-09-17 删除 `.cw-revsub` 规则：收入拆项已并入卡片顶部副行，
   JS 不再生成这个元素；留着死规则会让后人以为它还在用。 */

.cw-row--pick { cursor: pointer; border-radius: 4px; }
.cw-row--pick:hover, .cw-row--pick.is-on { background: var(--color-primary-50); }
.cw-row--pick:hover .cw-lbl, .cw-row--pick.is-on .cw-lbl { color: var(--color-accent-text); }
.cw-row--pick.is-on .cw-lbl { font-weight: var(--font-weight-medium); }
.cw-row--pick:focus-visible, .cw-lr--pick:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

/* ---------------- 0 值项折叠 ----------------
   默认收起（实测 17 项里 9 项是 ¥0，全列出来会把有值的淹掉）；折叠行一点就展开。
   ⚠️ 恢复时必须还原成 `grid` —— `.cw-row` 与 `.cw-lr` 都是 grid 布局，
      写成 `display:block` 会让列全部串位（"展开后串列"这类错最难发现）。 */
.cw-scope .cw-zero { display: none; }
.cw-scope.is-showzero .cw-zero { display: grid; }

/* ---------------- 图例 ---------------- */
.cw-legend {
  display: flex; gap: var(--space-4); flex-wrap: wrap;
  margin-top: var(--space-4); font-size: var(--font-size-xs); color: var(--color-meta);
}
.cw-legend span { display: inline-flex; align-items: center; gap: 5px; }
.cw-dot { display: inline-block; width: 9px; height: 9px; border-radius: 2px; flex: none; vertical-align: middle; }

/* 口径/读法说明：这张卡的解释文字与其数字同等重要（不写清"基准是什么"就会被误读） */
.cw-note { margin-top: var(--space-3); font-size: var(--font-size-xs); color: var(--color-meta); line-height: 1.7; }
.cw-note b { color: var(--color-fg-2); }

/* ---------------- 右侧成本清单 ---------------- */
/* 左右两侧的每一行都用 `--cw-row-h` —— 右侧成本项与左侧瀑布条**逐行同高**。
   ⚠️ `.cw-lh`（成本项表头）也必须占**整整一行**：它对齐的是左侧的「收入」行。
      改前它只有 padding 撑出来的 27px，而左侧收入行是 28px 步进 → 从这里就开始错位。 */
.cw-lh, .cw-lr {
  display: grid; grid-template-columns: 1fr 96px 62px;
  gap: var(--space-2); align-items: center;
  height: var(--cw-row-h);
}
.cw-lh {
  font-size: var(--font-size-xs); color: var(--color-meta);
  padding: 0 6px; border-bottom: 1px solid var(--color-border);
  /* 与左侧「收入」行的 `margin-bottom` 成对出现（见 `.cw-wf > .cw-row:first-child`）——
     两侧都加同样的块间距，后续所有行才仍然逐行对齐。 */
  margin-bottom: var(--cw-block-gap);
}
.cw-lh span:not(:first-child) { text-align: right; }
.cw-lr {
  padding: 0 6px; font-size: var(--font-size-sm);
  border-bottom: 1px solid var(--color-border-soft); border-radius: 4px;
}
.cw-lr__n {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--color-fg-2);
}
.cw-lr__n .cw-dot { margin-right: 6px; }
.cw-lr__v, .cw-lr__p { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cw-lr__p { font-size: var(--font-size-xs); color: var(--color-meta); }
.cw-lr--sum { border-bottom: 0; font-weight: var(--font-weight-semibold); }
.cw-lr--sum .cw-lr__n { color: var(--color-fg); }
.cw-lr--pick { cursor: pointer; }
.cw-lr--pick:hover, .cw-lr--pick.is-on { background: var(--color-primary-50); }
.cw-lr--pick.is-on .cw-lr__n { color: var(--color-accent-text); }

.cw-fold {
  display: flex; align-items: center; gap: 4px; width: 100%;
  margin-top: var(--space-3); padding: 6px 10px;
  font-family: inherit; font-size: var(--font-size-xs); color: var(--color-meta);
  text-align: left; background: var(--color-surface-2); border: 0; border-radius: 6px; cursor: pointer;
}
.cw-fold:hover { color: var(--color-fg-2); }
.cw-fold__i { display: inline-flex; transition: transform 0.15s ease; }
.cw-scope.is-showzero .cw-fold__i { transform: rotate(180deg); }

/* 构成面板：点某一条后就地展开（复用 CostChart.compositionPanel，样式沿用 .cc-panel） */
.cw-panel:not(:empty) { margin-top: var(--space-5); }

@media (max-width: 900px) {
  .cw-main { min-width: 0; }
  .cw-list { width: 100%; }
  .cw-row { grid-template-columns: 76px 1fr 96px 54px; }
  .cw-sep, .cw-revsub { margin-left: 88px; }
}
