/* ==========================================================================
   达播工作台 — layout.css
   主区域 / 顶栏（面包屑·用户）/ 页面容器
   ========================================================================== */

/* ---- 主区域 ----
   2026-09-19 晚：乐哥拍板**取消「整体悬浮」**，改为「方案A：贴边」（他原话：
   「上面做的那个悬浮菜单还有侧边栏很奇怪，你看下怎么优化，或者不要悬浮？」→ 选 A）。

   上一版是「侧栏 + 内容列各一块 14px 圆角面板，内缩 2~8px 浮在桌面色上」。
   实测两个毛病：
     ① 缝宽不对等（左/中/右 2px、上/下 8px，差 4 倍）→ 读起来像"没对齐"
     ② **滚动后两块面板顶边错开 8px**（侧栏 fixed、内容列随文档流，见 base.css 侧栏注释）
   本版：margin 全部归零、直角、无阴影，左边界贴着侧栏右缘（侧栏自带 1px 分隔线）。

   ⚠️ 为什么保留 `overflow: clip`（即使已经不需要它来裁圆角了）：
     `.topbar` / `.page-tabs-bar` 是**吸顶**的（sticky），而 sticky 需要一个不含滚动容器的祖先链。
     `clip` 只裁剪、**不创建滚动容器**；换成 `hidden` 会创建 → 吸顶在结构上直接失效（实测顶栏跑到 −312）。
     ⇒ 想删这一行前，先用「滚动 320px 后顶栏是否仍在 0」验证。 */
.app-main {
  flex: 1;
  min-width: 0;
  /* ⭐ 2026-09-25 Gashora 骨架落地：
     ▸ margin-left 240 → **+28px**（= 侧栏左缝 + 岛右缝，各一个 --frame-gap）。
     ▸ margin-top 0 → **calc(frame-gap + tabs-bar-height + frame-gap) = 112px**（值随令牌走）
        （顶栏 fixed 后脱离文档流，内容区要自己让位）。
     ▸ background 白 → **transparent**：这是"内容区白色大卡"能浮起来的前提 ——
        原先 .app-main 铺白底（顶栏/标签栏与它连成一块的旧设计），
        现在底盘是灰底（--color-bg），白卡（.page-stack）浮在它上面。
        ⚠️ 若这里留白底，灰底透不出来，.page-stack 的圆角外就会露出一圈**直角白边**
          （实测踩到：乐哥报「正文背景还是直角」就是这个 —— 父容器白底在子容器圆角缝外露出）。 */
  /* ⚠️ 算式必须与"谁来留右边缝"配合（改前算错过一次，内容区右侧超出视口 14px）：
     · 左侧：侧栏左缝(14) + 侧栏宽(240) = 254 ⇒ 内容列从 254 起（侧栏右缘处）
     · 右侧：**交给 .page-stack 的 margin-right（一个 --frame-gap）** ——
       所以这里**不能**再加第二个 frame-gap（那会让右缘 = 254+1172 = 1426+…超出）。
     ⇒ 左 = 241? 不，用 clear 算式：margin-left = sidebar-width + frame-gap
       （侧栏宽含左缝的承载），右缘由 page-stack 的 margin-right 承担。 */
  margin-left: calc(var(--sidebar-width) + var(--frame-gap));
  /* ⭐ 2026-09-28 骨架定稿：顶栏贴顶（top:0）后，内容区从「顶栏下边 + 一个悬浮缝」起 ——
     margin-top = tabs-bar-height + frame-gap（原公式是顶栏也有上缝时的 frame-gap×2 版本）。 */
  margin-top: calc(var(--tabs-bar-height) + var(--frame-gap));
  margin-right: 0;
  margin-bottom: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  background: transparent;
  border-radius: 0;
  overflow: clip;                          /* ⚠️ 见上方注释：只能是 clip，不能是 hidden */
  transition: margin-left var(--motion-base) var(--motion-ease-standard);
}

.app-shell.is-collapsed .app-main {
  margin-left: calc(var(--sidebar-width-icon) + var(--frame-gap));
}

/* ---- 顶栏已撤（2026-09-19 结构重排）----
   乐哥：「现在不是有菜单标签了吗，那路径是不是可以不展示」→ 撤掉面包屑那一行，
   标签栏升为第一行。收益实测：内容区顶边 102px → 56px，**多出 46px**。
   依据：全站 24 个页面（14 主 + 10 二级/详情）全部有页面内标题，
   加上侧栏高亮项与分组名，"我在哪"本来就有三层在讲 —— 面包屑是最下面那层重复的。

   ⚠️ 保留 `.topbar` 节点（`display:none`）而不是从 HTML 删掉：
      全站仍有代码查询 `el.topbar`（app.js 的 el 赋值、若干兼容分支），
      删节点会让那些查询变成 null 并在别处抛错。留一个不占高度的空壳最省事。
   ⚠️ 若要恢复面包屑：删掉这里的 display:none，并把 .page-tabs-bar 的 top 改回
      var(--header-height)、品牌区高度改回 calc(header + tabs)（见 base.css），
      同时恢复 app.js 里给 `.breadcrumb__link` 绑跳转的那段。 */
.topbar {
  display: none;
}

/* ⚠️ `.topbar__left` / `.topbar__right` 已随顶栏一起停用（2026-09-19）。
   保留空规则块是为了留痕：这两个类曾是顶栏的左右两半，
   现在它们的内容分别搬去了 `.page-tabs-bar__left` / `__tabs` / `__tools`
   （搜索入口也回到了 `__tools` 里、通知左边）与侧栏的 `#sidebar-footer`。
   若将来恢复顶栏可从这里改回。 */
.topbar__left, .topbar__right { display: none; }

/* 顶栏全局搜索：胶囊浅底，与列表搜索框同风格 */
.global-search {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 36px;
  padding: 0 var(--space-4);
  background: var(--color-bg);
  border-radius: var(--radius-pill);
  min-width: 240px;
  width: 280px;
}
.global-search__icon {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-muted);
}
.global-search__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  outline: none;
  font-size: var(--font-size-sm);
  color: var(--color-fg);
}
.global-search__input::placeholder {
  color: var(--color-placeholder);
}
.global-search {
  position: relative;
}
.global-search__panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  /* ⚠️⚠️ 面板**必须比输入框宽**，不能用 left:0;right:0（那样只有 280px）。
     实测（原型阶段）：280px 时右侧的「跳转 →」把菜单名挤到 0 宽，
     「提成总览」整行只剩一个图标 —— 看起来像 bug，实际是宽度不够。
     用 max-content 让内容自己撑开；上下限保证不会太窄也不会盖满屏幕。
     max-width 用 100vw 而不是固定 px：否则窄屏（<560px）时会溢出视口。 */
  min-width: 420px;
  width: max-content;
  max-width: min(560px, calc(100vw - 32px));
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
  max-height: 420px;
  overflow-y: auto;
  z-index: 300;
  padding: var(--space-2);
}
.gs-group__title {
  padding: var(--space-2) var(--space-3) 4px;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-muted);
  letter-spacing: 0.02em;
}
.gs-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.gs-row:hover,
.gs-row.is-active {
  background: var(--color-surface-2);
}
.gs-row__icon {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-muted);
}
.gs-row__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* ⚠️ flex:1 不可省 —— 没有它，右侧的「跳转 →」/路径会把这列压到 0 宽，
     菜单名直接消失（见 .global-search__panel 的注释）。 */
  flex: 1;
  min-width: 0;
}
.gs-row__label {
  font-size: var(--font-size-sm);
  color: var(--color-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gs-row__sub {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 功能项目标页面的「跳转 →」提示（功能项与数据项的区别标识） */
.gs-row__go {
  margin-left: auto;
  flex-shrink: 0;
  white-space: nowrap;
  font-size: var(--font-size-xs);
  color: var(--color-accent-text);
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.gs-empty {
  padding: var(--space-4);
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--color-muted);
}

/* ---------- 「问 AI」那一行（贴在面板底部，视觉上与前两类结果分开） ---------- */
.gs-ai {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-2) calc(var(--space-2) * -1) calc(var(--space-2) * -1);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border-soft);
  background: var(--color-primary-50);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  cursor: pointer;
}
.gs-ai:hover,
.gs-ai.is-active {
  background: var(--color-primary-100);
}
.gs-ai__icon {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-primary-500);
}
.gs-ai__ask {
  flex-shrink: 0;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
}
.gs-ai__q {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gs-ai__hint {
  margin-left: auto;
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--color-meta);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* ---------- AI 回答区 ---------- */
.gs-answer {
  padding: var(--space-3) var(--space-3) var(--space-2);
}
.gs-answer__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  margin-bottom: var(--space-2);
}
.gs-answer__spin {
  display: inline-flex;
  animation: gs-spin 1s linear infinite;
}
@keyframes gs-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.gs-answer__body {
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  line-height: 1.7;
}
.gs-answer__body--dim {
  color: var(--color-muted);
}
/* 数据来源：**每条 AI 回答都必须带**（否则数字无从核对 —— 见用户铁律"必须拿外部基准比"）。
   做成可点链接，点一下跳到那个页面，页面上是多少一眼能核。 */
.gs-answer__src {
  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);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.gs-answer__srclink {
  color: var(--color-accent-text);
  text-decoration: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.gs-answer__srclink:hover {
  text-decoration: underline;
}
.gs-answer__sep {
  color: var(--color-border);
}
.gs-answer__warn {
  margin-top: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-muted);
  min-width: 0;
}

.breadcrumb__sep {
  color: var(--color-meta);
}

.breadcrumb__link {
  color: var(--color-muted);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  border-radius: var(--radius-sm);
  transition: color var(--motion-fast) var(--motion-ease-standard);
}
.breadcrumb__link:hover {
  color: var(--color-accent-text);
}

.breadcrumb__current {
  color: var(--color-fg);
  font-weight: var(--font-weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* ⭐ 2026-09-26 两档制：36 → **34（紧凑档）**
     ⚠️ 它主要出现在**表格行内**（变更日志/编辑/删除那些图标钮）。
        改成 34 后与同排的 `.btn--sm`（也是 34）**完全齐平** ——
        改前是 36 vs 30（差 6px），业务总表操作列肉眼可见的错位。
     ✅ 顶栏不受影响：`.page-tabs-bar .icon-btn`（0,2,0，见本文件 1067 行）与
        `.page-tabs-bar__tools #notify-bell`（48px）都是**更具体**的规则，会覆盖本条。
     ✅ 手机端不受影响：下方 media 查询里 `.icon-btn { 44px }` 仍生效（触摸目标 44）。 */
  width: var(--control-height-sm);
  height: var(--control-height-sm);
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-fg-2);
  /* ⚠️ 必须带上 `transform`（2026-09-13 补，ui-ux-pro-max 优先级 7「state-transition」）：
     按下态用了 `transform: scale(0.94)` 做反馈，而这里原来只过渡 background-color/color
     → **缩放会瞬间跳变**（实测确认：规则生效但无过渡）。
     「状态变化要平滑、不能 snap」是动效规范的基本要求。
     时长沿用 --motion-fast(150ms)，落在规范的 150–300ms 区间。 */
  transition: background-color var(--motion-fast) var(--motion-ease-standard),
    color var(--motion-fast) var(--motion-ease-standard),
    transform var(--motion-fast) var(--motion-ease-standard);
}

.icon-btn:hover {
  background: var(--color-surface-2);
  color: var(--color-fg);
}

.icon-btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* 用户信息 */
.user-chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3) var(--space-1) var(--space-1);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--motion-ease-standard);
}

.user-chip:hover {
  background: var(--color-surface-2);
}

.user-avatar {
  /* 顶部 1px 补偿中文字面偏上，首字母视觉居中 */
  padding-top: 1px;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-100);
  color: var(--color-accent-text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  flex-shrink: 0;
}

.user-meta {
  display: flex;
  flex-direction: column;
  line-height: var(--font-line-height-tight);
}

.user-name {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
}

.user-role {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

.user-avatar-img {
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  display: block;
}

/* ---- 个人中心下拉面板 ---- */
.profile-wrap {
  position: relative;
}
.profile-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 260px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-raised);
  overflow: hidden;
  z-index: var(--z-dropdown);
}
.profile-menu__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--color-border-soft);
}
/* ⚠️⚠️ 防"内容撑破面板"（2026-09-22 修，真实 bug）：
   实测生产库 22 个活跃用户里 **10 个的 username 是 40 字符的 `ou_` open_id**
   ⇒ 菜单头那行内容宽 233px，把面板内容撑到 **305px**，而面板只有 **223px**
   ⇒ 面板 `overflow:hidden` 把右侧裁掉 ⇒ 菜单项文字被切（「修改头」「已绑定」「重新授」）、
      新加的「审批中心」角标也看不见。
   ⇒ 两手都要：① 源头不显示 open_id（见 app.js profileHtml）；
             ② 这里做**结构性兜底** —— flex 子项 `min-width:0` 才能让省略号生效
                （`min-width` 默认 `auto` 会按内容撑开，是这类"文字撑破容器"的通用根因），
                再配 `overflow:hidden + text-overflow:ellipsis`。
   ⚠️ 只加 `overflow:hidden` 而漏 `min-width:0` 是无效的（这是最容易漏的一步）。 */
.profile-menu__header > div {
  min-width: 0;
  overflow: hidden;
}
.profile-menu__name,
.profile-menu__sub {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.profile-avatar {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-100);
  color: var(--color-accent-text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  flex-shrink: 0;
  overflow: hidden;
}
.profile-menu__name {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.profile-menu__sub {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  margin-top: 2px;
}
.profile-menu__body {
  padding: var(--space-2);
}
.profile-menu__footer {
  padding: var(--space-2);
  border-top: 1px solid var(--color-border-soft);
}
.profile-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: none;
  background: none;
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  color: var(--color-fg);
  cursor: pointer;
  text-align: left;
}
.profile-menu__item:hover {
  background: var(--color-surface-2);
}
.profile-menu__item--danger {
  color: var(--color-danger);
}
.profile-menu__item--danger:hover {
  background: var(--color-danger-soft));
}
/* ⛔ 这里曾有 `.profile-menu__badge`（待审角标）与 `.profile-menu__sep`（分隔线）——
   2026-09-22 随「审批中心」从「我的」下拉移出而删除（入口改到侧栏「我的」分组，见 MINE_MENU）。
   ⚠️ 保留「菜单头不显示 open_id」那组规则（在 .profile-menu__header 之后）——
      那是独立发现的真 bug 修复：10 个用户的 username 是 40 字符 open_id，
      会把面板内容撑到 305px（面板仅 223px）⇒ overflow:hidden 裁掉菜单文字。 */
.profile-menu__soon {
  margin-left: auto;
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

/* ---- 通知中心（铃铛 + 下拉） ---- */
.notify-wrap {
  position: relative;
}
.notify-badge {
  /* ⭐⭐ 2026-09-26 修（乐哥实报「右上角通知的数字，遮住图标了」）。
     ──────────────────────────────────────────────────────────────────────────
     🐞 根因：原值是 `top: 3px; right: 1px` —— 按**按钮方框**定位、完全没考虑图标在哪。
        而图标是**居中**的（18px 居中于 40px 按钮 ⇒ 上沿在 11px、右缘在 29px），
        于是 16px 高、宽到 28px 的「99+」直接压在图标上：
        实测重叠 **水平 18px × 垂直 8px**（几乎整个盖住）。
        ⚠️ 这不是新 bug：按钮还是 48px 圆形的时代就已重叠（实测 15×5px），
           2026-09-26 A6 把按钮缩到 40px 方盒后**放大**成了"整个遮住"。
     ⭐ 修法：把定位改成**按图标上沿锚定**（徽章下沿 ≤ 图标上沿），并让徽章**右上外溢**
        一点（角标的标准形态：骑在角上，而不是压在按钮里）。
     算式（两种按钮尺寸都成立，改按钮尺寸时要重新验算）：
        · 桌面按钮 40 / 图标 18 ⇒ 图标上沿 = (40−18)/2 = **11**
          ⇒ 徽章 top = 11 − 16(徽章高) = **−5**（下沿正好贴图标上沿，零重叠）
        · 移动端按钮 44（见下方 @media 的 44px 规则）⇒ 图标上沿 = 13
          ⇒ 同一条 top:−5 下沿落在 11，比图标还高 2px（更安全）
        · right: −3 ⇒ 徽章右缘外溢按钮 3px（右侧是工具组的 10px gap，不会挤到邻居）；
          水平上「99+」(宽 28) ⇒ 左缘 15，比图标左缘 11 略靠右 ⇒ 读作"角标"而非"居中压在头上"
     ⚠️ 徽章宽的上限就是「99+」（app.js 的 refreshBadge：c > 99 ? '99+' : String(c)），
        最宽 28px，上面算式按最宽情况算的。
     ⚠️ 顶栏上下留白各 16px（栏 72 / 按钮 40）⇒ 外溢 5px 不会顶到栏边。
     ⚠️ 摇铃铛动画摇的是 `#notify-bell svg`、不是本元素 ⇒ 徽章不会跟着晃（见下方 bell-shake）。 */
  position: absolute;
  top: -5px;
  right: -3px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-danger);
  color: #fff;
  font-size: var(--font-size-xs);
  line-height: 16px;
  text-align: center;
  pointer-events: none;
  box-sizing: border-box;
}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-09-26 动效：通知铃铛「轻摇」（有新消息时）
   ──────────────────────────────────────────────────────────────────────────────
   触发点：app.js 的 `refreshBadge()` —— **未读数变多**时给铃铛加 `.is-shake`。
   为什么挂在那里：那是"真的有新消息来了"的唯一可靠信号（轮询拿到的数字涨了）。

   ⚠️⚠️ 三个"不做"，都是刻意的：
     ① **首次拿到数字时不摇**（页面刚打开，整页已在进场动画里，再摇一下是重复提示）；
     ② **数字不变或变少时不摇**（每轮轮询都摇 = 从"提示"变成"骚扰"，
        用户会开始忽略它 —— 那这个动效就是负资产）；
     ③ 摇的是**里面的 svg**、不是 `.icon-btn` 本身：
        按钮自己有 hover/active 的 transform，动画与它争同一个属性会互相打断。
   ⚠️ `transform-origin: 50% 12%`（铃铛顶部）：摇铃是"绕着挂点摆"，
      以中心为轴会变成"整颗铃在转"，像个转盘。 */
#notify-bell.is-shake svg {
  transform-origin: 50% 12%;
  animation: bell-shake 640ms var(--motion-ease-in-out);
}
@keyframes bell-shake {
  0%, 100% { transform: rotate(0); }
  16% { transform: rotate(11deg); }
  36% { transform: rotate(-8deg); }
  56% { transform: rotate(5deg); }
  76% { transform: rotate(-2.5deg); }
}
.notify-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 340px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-raised);
  overflow: hidden;
  z-index: var(--z-dropdown);
}
.notify-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border-soft);
}
.notify-panel__title {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
}
.notify-panel__readall {
  border: none;
  background: none;
  font-size: var(--font-size-xs);
  color: var(--color-accent-text);
  cursor: pointer;
  padding: 0;
}
/* ---- 通知分类页签（2026-09-22 新增，乐哥：「按照类型做下分类，现在都混一起很乱」）----
   ⚠️ 尺寸/配色一律复用既有令牌，**不新造颜色**（本项目纪律：新造"全站孤例的颜色"
      是"看着突兀"的头号原因）。激活态用 `--color-primary-50` 底 + `--color-accent-text` 字，
      与全站"选中态"语言一致（同侧栏选中项、tab 选中项）。
   ⚠️ 横向可滚动：5 个页签（全部/待我审批/待办/审批结果/其它）在 340px 面板里会挤，
      中文标签不能靠缩字号解决，故允许横向滚动、且隐藏滚动条（不是让文字折行）。
   ⚠️ 页签数字 = **该类型的未读数**（不是总数）—— 语义与邮箱未读一致。
      数字为 0 时不渲染（`is-alert` 的红底只在「待我审批」有未读时出现）。 */
.notify-tabs {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border-soft);
  overflow-x: auto;
  scrollbar-width: none;
}
.notify-tabs::-webkit-scrollbar {
  display: none;
}
.notify-tab {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  border: none;
  background: none;
  padding: 4px var(--space-2);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--motion-fast) var(--motion-ease-standard),
              color var(--motion-fast) var(--motion-ease-standard);
}
.notify-tab:hover {
  background: var(--color-surface-2);
  color: var(--color-fg);
}
.notify-tab.is-active {
  background: var(--color-primary-50);
  color: var(--color-accent-text);
  font-weight: var(--font-weight-medium);
}
.notify-tab__n {
  font-size: var(--font-size-xs);
  line-height: 1;
  color: inherit;
  opacity: 0.75;
}
/* 「待我审批」有未读 → 数字做成红底白字（最需要动手的一类，视觉上给足权重） */
.notify-tab.is-alert .notify-tab__n {
  min-width: 16px;
  padding: 2px 4px;
  border-radius: var(--radius-pill);
  background: var(--color-danger);
  color: #fff;
  opacity: 1;
  text-align: center;
  box-sizing: border-box;
}
.notify-panel__body {
  max-height: 400px;
  overflow-y: auto;
}
.notify-item {
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  border-bottom: 1px solid var(--color-border-soft);
}
.notify-item:last-child {
  border-bottom: none;
}
.notify-item:hover {
  background: var(--color-surface-2);
}
.notify-item.is-unread {
  background: var(--color-info-soft));
}
.notify-item__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.notify-item__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-danger);
  flex-shrink: 0;
}
.notify-item:not(.is-unread) .notify-item__dot {
  opacity: 0;
}
.notify-item__title {
  flex: 1;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.notify-item__time {
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  flex-shrink: 0;
}
.notify-item__content {
  margin-top: 4px;
  font-size: var(--font-size-xs);
  color: var(--color-muted);
  line-height: 1.5;
}
.notify-empty {
  padding: var(--space-6);
}

/* ---- 主内容 ---- */
.page {
  flex: 1;
  /* 上边距 24（不是 32）—— 2026-09-19 乐哥：「下面有点多留白」。
     实测根因：上 32 ＋ .page-header 的 margin-top 24 = **56px 空档**（5 个页面实测全是 56）。
     那 24 是**标签栏出现之前**定的 —— 当时 .page 顶上直接是顶栏，需要拉开距离；
     现在标签栏已吸顶、且与正文之间有一条分隔线，这 24 就是**重复**的。
     现改法：上 24 ＋ header 的 8 = 32px，与左右 32 的 gutter 对齐成一个方正的角。

     ⭐ 2026-09-25 UI 美化第四刀（呼吸感）：左右 gutter 由 32 加到 **40**、
        底部 32 → **40**，上边距保持不变（24）。
     为什么只加左右下、不加上：上边距与「标签栏吸顶 + 分隔线」是配套关系（见上），
     改动它会重新制造那段被删掉的 56px 空档；
     而左右下加大后，内容区四周形成一个**更宽松的边框**，
     正是"呼吸感"最直接的来源（内容不贴边）。
     ⚠️ 实测代价：几乎为零 —— 1440 宽下内容区由 1185 → 1169（-16px，1.3%），
        表格列宽有富余、不触发折行；一屏可见行数保持 9 行不变。 */
  /* ⭐ 2026-09-28 上边距 24 → 16（--space-6 → --space-4，乐哥：「内容区高一点点，
     左边菜单栏显得偏高了」—— 实测内容区从 y=86 起、页头内容 y=110 才出现，
     而侧栏岛 y=86 即满铺菜单 ⇒ 内容区顶部留白比侧栏多 24px，视觉上侧栏"偏高"。
     上边距收 8px 后内容起点 102，与侧栏岛的落差 24 → 16，观感趋平。
     ⚠️ 左右 40 / 下 40 不动（呼吸感来源）；.page-header 的 margin-top 8 不动，
     页头包盒后「上留白 16 + 页头 margin 8」= 24px，仍在舒适区间。 */
  padding: var(--space-4) var(--space-10) var(--space-10);
  width: 100%;
  /* ⭐ 2026-09-27：无档位页（表格列表等）加**全宽上限 1600** ——
     乐哥：「那个模块的宽度就需要调整」。去掉白大卡后，无档位页面
     在 2560 超宽屏下会被拉到 **2183px**（卡片塞满、反而显挤）。
     ▸ 上限取 1600：达人列表等宽表在 1920 视口下内容区 1543、**不横滚**（实测），
       上限比它大即安全；更大屏（2560）则封顶 1600、左右留白居中。
     ▸ 乐哥自己的屏（~1700）：内容区 1338 < 1600，**上限不生效、无变化**。
     ▸ `.page--narrow`(1360) / `.page--table`(1520) 特异性更高，会覆盖本条 ⇒ 二级页不受影响。 */
  max-width: 1600px;
  margin-left: auto;
  margin-right: auto;
}

/* ==========================================================================
   多标签页（2026-09-18 乐哥：「能有个达人页面的标签，点击直接切过来，
   内容还是我切换之前维护的那些，丝毫未动」）

   ▸ 两个部件：
     · `.page-tabs-bar` 标签栏 —— 面包屑栏之下、页面之上
     · `.page-stack`    页面栈 —— **所有标签的页面同时存在于 DOM 中**，
       非活动的只是不显示。这是"丝毫未动"的实现基础：
       切标签不做任何重建，筛选条件 / 滚动位置 / 展开的行 / 填一半的表单都原样保留。

   ▸ 为什么"只让活动容器持有 id=page-root"（见 app.js 的 activateTab）：
     全站有 8 处 `document.getElementById('page-root')` 以及大量依赖该 id 的逻辑，
     逐个改成"按标签查找"既繁琐又必漏。改成**活动容器独占该 id**后，
     那些代码一行不改就自动拿到"当前活动页"——这是本次改造能收敛在单文件的关键。

   ---------------------------------------------------------------------------
   ▸ 外观（2026-09-19 乐哥：「有点丑，可以做成 chrome 浏览器标签的样子吗」）
     改成 Chrome 的页签形态。**关键是这一条，不是配色**：
         「激活标签的底色 == 内容区的底色」
       Chrome：窗口顶是深灰槽、内容区是白 ⇒ 激活标签用**白**，白色"流"进内容区，
               视觉上连成一片，标签栏底部的分隔线在激活标签处自然消失。
       我们：内容区是**灰**（--color-bg #F5F6F8）、白的是**卡片** ⇒
             激活标签必须用**灰**，槽要比灰更深一档。
       ⚠️ 我第一版照抄 Chrome 用了白色标签，结果白块浮在灰页面上、比改版前更像
          "一张贴纸"（自测截图一看就露馅）。改对底色后才成立。
       ⚠️ 所以**不要**把 .page-tab.is-active 的 background 改成 --color-surface（白）。

     Chrome 页签的四要素（缺一个就不像）：
       ① 标签从「槽」里升起来：激活的高（36）非激活的矮（32），底部对齐
       ② 激活标签底部**两个凹角**（反圆角），把槽色平滑过渡掉
       ③ 激活标签底边**盖住**分隔线（margin-bottom:-1px + z-index:2）
       ④ 关闭按钮：非激活 hover 才出现、激活常显（降噪；实测 5 个标签 5 个 × 很吵）
   ========================================================================== */
.page-stack {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* ⚠️ 这里**不能**写 width:100% —— 它是"父容器内容宽"，再加上本元素的
     margin-left/right(各 14px) 就会**超出父容器 28px**（实测右侧溢出视口、内容被裁）。
     块级元素默认 width:auto 会自己扣掉 margin ⇒ 填满可用宽度且不溢出。
     （2026-09-25 实测踩到：乐哥截图里内容区右侧被切掉就是这个。） */
  width: auto;
  /* ⭐⭐ 2026-09-27：**去掉「内容区白色大圆角卡」** —— 模块卡直接落在页面灰底上。
     ──────────────────────────────────────────────────────────────────────────
     乐哥（给了 PMS「新建项目」参考图）：「整个 UI 的二级页面，我们应该按照项目管理系统
     里面那个模型去做」+「中间那个白色的框有点突兀」「上面的板块也都是散的」。

     ▸ 改前（2026-09-25 Gashora 落地）：内容区是一张 #FCFCFC 白卡浮在灰底上，
       模块卡再嵌在它里面。**问题：白卡底 #FCFCFC 与页面底 #F6F6F6 只差 6 阶**，
       肉眼几乎看不出这层白卡 —— 它是一层"看不见的中间层"，
       却让模块卡只能靠「与白卡差 3 阶 + 1px 边框」划界（边界偏弱）。
     ▸ 改后：白卡移除，模块卡直接落 #F6F6F6 上 ⇒ 卡片与底的色差由 **3 阶升到 9 阶**，
       边界反而更清楚；同时不再有"白框套白卡"的嵌套感，与参考图的扁平形态一致。
     ▸ 实测（1440×900，列表/表单/详情/看板四类一致）：内容区 **+16px 宽**
       （如达人列表 1047→1063）、内容上移 8px、**一屏可见行数不变**。
     ▸ margin **保留**：它是"内容列"与视口右/下边缘的缝（属浮岛体系，见 frame-flush 门禁）。
     ▸ padding **归零**：原 8/8/12 的唯一用途是"让白卡的白色边与内部卡之间留白"，
       白卡没了它即失去意义；四周呼吸交给 `.page` 的 40px gutter（已足够宽松）。
     ⛔ 不许再给它 background / border-radius / box-shadow —— 它现在是**纯留白容器**。
        （门禁 tests/page-stack-plain.test.js 会拦；另注意
          `--color-surface-canvas` 令牌已随之删除，别再引用）*/
  background: transparent;
  border-radius: 0;
  margin: 0 var(--frame-gap) var(--frame-gap);
  padding: 0;
  box-shadow: none;
}

/* ⭐ 2026-09-25 Gashora 骨架落地：**隐藏页面标题**（乐哥：「每个页面左上角那个菜单名，
   是不是可以不要了」）—— 标题与「侧栏当前项 + 顶栏标签」重复讲同一件事，
   去掉后内容区直接从"操作行（状态筛选 TAB + 操作按钮）"开始。
   ⚠️ 页面标题有**两套类名**，必须都覆盖（只覆盖一套会漏掉看板）：
      · `.page-title`             —— 40 个常规页面（达人管理/货品/…）
      · `.dashboard-overview__title` —— 看板首页的「首页」（历史遗留的另一套）
   ▸ **保留**（与标题不同义、有信息量）：
      · `.dashboard-overview__date`（看板的「达人排期与业绩概览」）
      · `.page-desc`（7 个页面的标题补充描述）
   ▸ 状态筛选 TAB 与操作按钮在 page-header 里是**独立节点**，本规则不影响它们。 */
.page-title,
.dashboard-overview__title,
/* ⭐ 2026-09-25（第二轮）乐哥：「看板首页上面那个小字不要」——
   指看板标题区的描述行「达人排期与业绩概览」（`.dashboard-overview__date`）。
   它随标题一起隐藏后，整个 `.dashboard-overview` 只剩空盒 ⇒ 下面的 margin 收紧为 0
   （见 .dashboard-overview 那条），内容区顶部不再空出一段。 */
.dashboard-overview__date { display: none; }

/* ⭐ 2026-09-28 豁免：二级页页头（.detail-head）里的标题**恢复显示**（乐哥：「另外其他
   二级页面标题这个，一起修了」）——
   09-25 隐藏标题是为**列表页**设的（「左上角那个菜单名」与侧栏当前项 + 顶栏标签讲同一件事）；
   但表单页/明细页/报销页统一用 `.detail-head`（`UI.headNav` 产出）当页头，
   它们的标题讲的是「**我在干什么**」（「新增达人」「编辑排期」「结算明细」），
   与菜单名不同义 —— 一并隐藏后页头只剩孤零零一个「返回」按钮
   （实测 #/schedule/1/edit 的 head 内可见文字仅「返回」、#/influencers/new 同样）。
   ⚠️ 只豁免 `.detail-head` 内：列表页 `.page-header` 的标题保持隐藏
      （09-25 决策不变；且 Tab 贴左依赖「标题容器被隐藏」，见 table.css 的同名规则）。 */
.detail-head .page-title { display: block; }

/* ⭐ 2026-09-28（第二轮）标题旁的 ? ——「小字说明收进问号」（乐哥拍板）
   ──────────────────────────────────────────────────────────────────────────
   触发：`headNav({ title, tip })` 时由 ui.js 挂在 `<h1 class="page-title">` 内。
   为什么图标要单独调大：`.th-note` 默认图标 13px（配 12-13px 小字），
   而页头标题是 24px —— 13px 的 ? 贴在旁边几乎看不见，视觉上像脏点。
   ⚠️ 只调 `.detail-head` 内的，卡内/表头的小字 ⓘ 维持 13px 不动
      （那里周围都是 12-13px 文字，调大会喧宾夺主）。
   ⚠️ 用 `margin-left: 6px`（不是默认的 3px）：大字号之间的呼吸要宽一档。 */
.detail-head .page-title .th-note--title {
  margin-left: 6px;
  color: var(--color-gray-400);
  vertical-align: 2px;         /* 视觉居中：图标略高于基线，与 24px 文字重心对齐 */
}
.detail-head .page-title .th-note--title svg { width: 17px; height: 17px; }
.detail-head .page-title .th-note--title:hover { color: var(--color-accent-text); }
/* 非活动标签的页面：**保留在 DOM 中**，仅不显示（勿改成 display:none 之外的做法，
   也不要移除节点 —— 移除就等于丢失用户没保存的状态） */
.page-stack > .page.is-tab-hidden {
  display: none;
}

/* 标签栏（2026-09-19 第二版）。

   ▸ 乐哥反馈的原话：「标签下面有一条线，另一个是颜色问题，都很突兀」。
     实测出来的两个根因，**都是为上一版改版新造的、全站只此一处的颜色**：
       · 槽底 #E3E6EA —— 全站唯一，比系统里最深的边框（--color-border #E5E8EC）还重
       · 槽底线 #D2D7DD —— 全站唯一，比任何边框都深 → 就是那条"多余的线"
     而系统里**早有一整套 tab 语言**（页面内 `.tabs`：选中＝主色文字＋底部主色下划线，
     选中态用 --color-accent-bg #EDF4FE，全站 10 多处）。窗口标签栏却新造了一套外来语言，
     所以看着"像个外人"。⇒ 本版改成**复用既有语言**，不再引入任何新颜色。

   ▸ 本版做法（方案 1「顶区一体」）：
     面包屑栏与标签栏**同为白**、去掉中间那条线，两行并成一个「顶部区」
     （都在回答"我在哪"）；激活标签 = 全站选中态同款浅蓝胶囊。
     结构性收益：整个页面**只剩一条横向分隔线**（在"顶部区"与正文之间）。

   ▸ ⚠️ 必须配套的两件事（实测出来的，不是可选）：
     ① `.topbar` 的 border-bottom 要**删掉** —— 线挪到这里来。否则两行之间多一条线，
        加上本条的线就是两条（实测现状正是 2 条：topbar 的 + 槽的深线）。
     ② 本栏必须 `position: sticky; top: var(--header-height)`。
        因为 `.topbar` 是粘顶的、本栏原先不粘 —— 实测滚 300px 后本栏跑到 top:-244，
        线随之上移，滚动时"归属"就断了。粘住之后两行**整体吸顶共用一个底边**。
        （实测：祖先链无 overflow 约束，sticky 可用；z-index 取 1099 < topbar 的 1100，
         保证顶栏的全局搜索面板 / 个人中心下拉弹出来时**不会被本栏切掉**。）
   ▸ 2026-09-19 结构重排：面包屑那一行已撤，**本栏升为第一行**（`top: 0`）。
     收益：内容区顶边 102px → 56px。高度沿用 `--tabs-bar-height`，**没有新造尺寸**
     —— 这样 analytics.js 的 `headerHeight()`（读 --header-height 当吸顶高度）无需改动即正确。
     ⚠️ 上面那句里的 56px 是那次重排时的值，之后又调过两次（56→60→52）。
        当前值以 design-tokens 的 `--tabs-bar-height` 为准，**这里不复述具体数字**
        （注释里写死数值会过期，本项目踩过"注释与代码打架"的坑）。
   ▸ 2026-09-20 高度收回：乐哥「最上面的标签，是不是太大，太占位置了」。
     实测依据与最终取值统一写在 design-tokens 的 `--tabs-bar-height` 注释里（只写一处）。
   ▸ 本栏拆成三个并列容器（见 index.html），**动态区与静态区分开**：
       `__left`  移动端菜单开关（静态，从原顶栏搬来）
       `__tabs`  标签（动态，renderTabsBar 每次重建它）
       `__tools` 通知/刷新/关闭（静态，全局委托绑定 → 重建容器也不丢事件）
     ⚠️ 拆分是必须的：原先 `renderTabsBar` 做 `bar.innerHTML = html` 整体重建，
        通知放进去会被每次切页冲掉（事件一并丢失）。
   ========================================================================== */
.page-tabs-bar {
  /* ⭐⭐ 2026-09-25 Gashora 骨架落地：顶栏从「内容区内的第一行」改为**贯穿全宽的浮起圆角卡**。
     参考图（乐哥指定）：最上面一行左右贯穿 —— 左上 logo、中间留白、接着搜索框、
     右边通知、最后头像胶囊框。
     改动三处：
       ① `position: sticky; top:0` → `fixed`（脱离文档流、相对视口定位 ⇒ 才能横跨侧栏上方）；
          配 `.app-main` 的 `margin-top: calc(frame-gap + 顶栏高 + frame-gap)` 给它让位。
       ② 加四周留白（top/left/right = --frame-gap）+ 圆角 16 + 柔和阴影 ⇒ 从"贴边一条"变成"浮起一张卡"。
       ③ 去掉 `border-bottom`（那条 #E5E8EC 分隔线属"贴边条"形态，卡片形态靠阴影分界）。
     高度 52 → 96 → 84 → **72**（2026-09-26 A6 定稿，历史链见 design-tokens 的 --tabs-bar-height）。
     ▸ 高度引用 `--tabs-bar-height`（当前 72px）—— 本栏高度的唯一来源。
     ▸ `--header-height`（吸顶高度）在骨架落地后语义改变：= frame-gap + --tabs-bar-height（当前 86），
       供 analytics.js 的 headerHeight() 定位分析页目录条（贴在顶栏下沿）。
     ⭐ 2026-09-26 A6：栏体改**半透明毛玻璃**（乐哥：「顶栏做一点透明，正文内容滚上去时更好看」）。
       底 = 72% 白 + backdrop-filter 18px 模糊/1.7 饱和 —— 正文滚到栏后时透出隐约色块。
       ⚠️ 栏内各段（__left/__tabs/__tools）**不许再有实色底**，否则毛玻璃上贴白块（见 __tools 注）。
       ⚠️ backdrop-filter 在内容未滚到栏后时不可见，验证时必须**先滚动再截图**（原型脚本都这么干）。 */
  display: flex;
  align-items: center;
  gap: 0;                             /* 间距交给内部各段自己管，避免空容器仍占 gap */
  height: var(--tabs-bar-height);   /* 唯一来源：令牌（当前 72px）*/
  flex: 0 0 auto;
  padding: 0 24px;
  position: fixed;
  /* ⭐ 2026-09-28 骨架定稿（乐哥拍板）：顶栏**贴边贯穿全宽（透白）+ 侧栏悬浮白岛**。
     ▸ top/left/right = 0（贴边贯穿），去圆角、去浮起阴影；
     ▸ **透白保留**（rgba(255,255,255,0.72) + 毛玻璃）：内容滚动透出但模糊、文字清晰；
     ▸ 底部一条 1px **细条**（--color-frame-border）与正文分界 —— 乐哥指定「细条」分隔。 */
  top: 0; left: 0; right: 0;
  margin: 0;
  border-radius: 0;
  z-index: var(--z-sticky);
  /* ⭐ 2026-09-28 统一「克制玻璃」（乐哥：顶栏/侧栏/浮岛/Tab 用同一套效果）——
     原为单值 rgba(255,255,255,.72) + blur(18px) saturate(1.7)，现改引用统一玻璃令牌：
     透白渐变（上实下透）+ blur20 + saturate180，与侧栏岛 / 浮岛 / 吸顶 Tab **同一套参数**。
     ⚠️ 顶栏是"贴顶贯穿"形态（无圆角、无外投影）—— 玻璃只换底与模糊；
        底部 1px 边框保留（它是与正文的分界，见下方 border-bottom）。 */
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: none;
  border-bottom: 1px solid var(--color-frame-border);
  /* ⚠️ 横向滚动已下移到 `__tabs` —— 本栏若自己 overflow，会连 `__tools` 一起滚走 */
  overflow: visible;
}
.page-tabs-bar__left {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  /* ⭐ 2026-09-25 Gashora 骨架落地：菜单按钮与 logo 之间 12px。
     ⚠️ 只此一处定义（同选择器的样式都写在这里，不要另起块 ——
        两块同选择器靠"谁在后面"决定生效，是本项目反复踩过的隐性依赖）。 */
  gap: 12px;
  /* ⭐ 2026-09-26 A6：品牌区**固定宽** ⇒ 标签区起点 = 正文列左缘 + 16
     （乐哥：「标签开始从正文那一列左边最上面开始」「然后再偏右一点点」）。
     算式：正文列左缘 = frame-gap(14) + --sidebar-width(240) + frame-gap(14) = 视口 268px；
     顶栏 left 14 + padding-left 24 ⇒ 栏内容从视口 38 起 ⇒ 本区宽 = 268+16−38 = 246px
     = --sidebar-width + --frame-gap − 24 + 16。
     ⚠️ 写的是令牌算式不是死数：侧栏宽/缝/padding 任一改，对齐自动跟随。
     ⚠️ 侧栏折叠态（64px）桌面端不可达（折叠按钮已隐藏），故按展开态对齐即可。 */
  width: calc(var(--sidebar-width) + var(--frame-gap) - 24px + 16px);
}


.page-tabs-bar__tabs {
  /* ⭐ 2026-09-26 A6：**标签页区恢复显示**（乐哥：「这样之前的标签（多标签导航）还能放进来」）。
     恢复方式 = 只删掉 9-25 加的那条 `display: none`（DOM 与交互逻辑一直都在，
     等宽由 app.js 的 equalizeTabWidths 统一，超出本区横向滚动、不显示滚动条）。
     ──────────────────────────────────────────────────────────────────────────────
     历史：9-25 Gashora 落地时按乐哥「顶部标签可以不要了」隐藏（只 display:none、
     不删 DOM 不停逻辑，当时注释就写明"将来恢复删掉这一条即可"）。
     ⚠️ 恢复后本区重新成为 flex:1 的弹性项（负责把工具组推到右边）⇒
        `.page-tabs-bar__tools` 的 margin-left:auto **必须删掉**（两个 auto 会平分空白，
        踩过 377px 空白的坑；门禁 C2 守着这一条）。
     ⭐ 标签区起止（乐哥拍板）：起点 = 正文列左缘 + 16（由 __left 固定宽实现，见那边算式）、
        终点 = 工具区左缘；1440 宽下实测可放 8 个标签，更多时区内横滚。 */
  display: flex;
  align-items: center;
  /* 标签间距 6（2026-09-19 晚 4 → 6）—— 乐哥反馈顶栏"挤"。
     ⭐ 实测：挤的主因是间距不是高度，4px 让相邻标签几乎贴在一起。 */
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;                       /* 允许收缩，否则标签把工具组挤出屏幕 */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;              /* 标签多了横向滚动，但不显示滚动条 */
}
.page-tabs-bar__tabs::-webkit-scrollbar { height: 0; }

.page-tab {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;             /* 内容在标签内居中 —— 见下方"等宽"的说明 */
  gap: 6px;
  /* 标签高 30（2026-09-26 A6：乐哥「尺寸可以小点」；原型逐轮拍板，1440 宽实测 8 个全放下）。
     ────────────────────────────────────────────────────────────────────────────────
     历史：34（09-20 定，配 52 栏）→ 本值 30（配 72 栏：上下各留 21px，比图标盒 40 小一档，
     标签是"次级导航"、图标盒是"主控件"，体量上就要分出层级）。
     ⚠️ 本值必须与 --tabs-bar-height **成对**调：两次历史改版都是两者一起动，
        只改一个会让标签在栏里显得过小或过挤。 */
  height: 30px;
  padding: 0 8px;                      /* 左右各 8：配合栏的 24 让图标落在 32px 线上 */
  border: none;
  /* 胶囊圆角（= --radius-seg，全站分段控件 / Tab 项同款）。
     上一版是「6px 6px 0 0 + 底部两个凹角」的 Chrome 页签形态，本版不再需要两者同值。 */
  border-radius: var(--radius-seg);
  font-size: 12.5px;                   /* A6：var(--font-size-sm)=13 → 12.5（乐哥「尺寸可以小点」）*/
  color: var(--color-muted);
  white-space: nowrap;
  cursor: pointer;
  min-width: 104px;                    /* ⭐ 2026-09-26 自适应下限：保证 **4 字菜单名完整可读**
                                          （固定开销 47px + 13px×4字 = 99，取 104 余量）。
                                          ⚠️ 与 app.js 的 MIN_TAB 同值（门禁锁）。
                                          ⛔ 别为了"多塞几个"压小它 —— 实测 72px 时 12 个标签
                                             全截成"业..""达.."，读不出名字（09-26 试过）。
                                             放不下的交给标签区横向滚动。 */
  max-width: 200px;                    /* 防长页面名把标签撑得很宽（与 JS 的 CAP_TAB 同值，门禁锁） */
  transition: background var(--motion-fast) var(--motion-ease-standard),
    color var(--motion-fast) var(--motion-ease-standard);
}
/* ⚠️ 标签宽度**由 JS 统一**（app.js 的 equalizeTabWidths），不靠这里的 min-width 撑。
   ────────────────────────────────────────────────────────────────────────────────
   乐哥 2026-09-19：「标签因为文字长度不一样，导致标签长度不一致，看着很奇怪」。
   实测现状：首页 66 / 我的报销 94 / 成本总览 94 / 月度利润明细 122 —— 参差不齐。
   做法：多标签时**等宽**（测出当前打开这几个里最宽的自然宽度，全部设成同一个宽度），
        但宽度的**取值自适应可用空间**（2026-09-26 乐哥：「别固定8个了，做成自适应宽度吧，
        有的浏览器页面缩小后，应该能显示更多」）：
          · 空间够 → 贴合最宽的自然宽（不多占）；
          · 空间不够 → 等比例压缩、能显示更多个；压到 min-width 下限后仍不够 → 横向滚动。
        ⚠️ 具体算式在 app.js 的 equalizeTabWidths（含 MIN/CAP 两个常量，与本节同值）。
     · 为什么不能用 CSS：flex 子项宽度天然由内容决定，"取兄弟节点的最大宽度"
       CSS 没有对应表达 —— `1fr` 是**平分容器**（5 个标签会各撑到 217px，喧宾夺主），
       固定 min-width 又会被更长的名字打破（菜单名从 2 字到 6 字都有）。
     · 为什么加 `justify-content: center`：等宽之后短标签（"首页"只有 66px 的内容）
       若靠左对齐，右侧会空出一大块 —— 与"单标签右边空 26px"是同一个病。
       居中后每个标签的"图标 + 文字"整体居中，长短名视觉重量一致。
       （这条对单标签无影响：那时宽度 = 内容宽，居中与靠左等价。）
     · 单标签不合宽（见 .page-tab--last）—— 只有一个标签时不存在"不一致"，
       且它不显示 ×，贴合内容才不空。 */
.page-tab:hover { background: var(--color-gray-50); color: var(--color-fg); }
.page-tab__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* ⭐ 2026-09-26 自适应宽度配套：flex 子项默认 `min-width: auto`（= 文字的最小内容宽），
     不加这条，标签被压窄时文字**不会**用省略号收尾，而是把标签顶宽/溢出 ——
     自适应压缩就完全失效（这是 flexbox 省略号的经典前提，别删）。 */
  min-width: 0;
}
/* 标签左侧的菜单图标 —— 相当于浏览器的 favicon：多个标签并存时靠它快速辨认。
   非激活用次级灰（不抢视线）、激活用主色（与当前页呼应）。 */
.page-tab__ico {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-meta);
}
/* A6：标签缩到 30px 高后，图标同步收到 13px（svg 自带 16px 属性值，这里覆盖） */
.page-tab__ico svg { width: 13px; height: 13px; }
.page-tab.is-active .page-tab__ico { color: var(--color-accent-text); }

/* 激活标签：全站选中态同款浅蓝胶囊 + 主色文字。
   ⚠️ 与上一版的关键区别：
      · 不再「抬高 4px + 与内容区同色 + 盖住分隔线」—— 那是 Chrome 页签的做法，
        要求"激活标签的颜色 == 内容区的颜色"；现在改成胶囊，这条约束不再适用。
      · 颜色取 --color-accent-bg(#EDF4FE) / --color-accent-text(#0064E0)，
        与页面内 `.tabs` 的选中态、以及全站 10 多处的选中态**同一套语言**。
      · 高度与未激活**相同**（32px）：胶囊靠底色区分，不靠体量。 */
.page-tab.is-active {
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
  font-weight: var(--font-weight-medium);
}

.page-tab__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;                         /* A6：16 → 12（标签 30px 高的配套，见 .page-tab 注）*/
  height: 12px;
  flex-shrink: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-meta);
  line-height: 1;
  cursor: pointer;
  /* ⚠️⚠️ × **常显**（2026-09-19 乐哥实报「上面的标签页关闭的 x 看不到了」，本轮改）。
     ────────────────────────────────────────────────────────────────────────────
     上一版是"非当前页的标签平时 opacity:0，hover 才出现"（为了降噪）。两个后果：
       ① 乐哥直接反馈「看不到」—— 大多数标签不是当前页，所以大多数标签没有 ×；
       ② ⭐ 更隐蔽的一个：**它占位但不可见**。× 是 flex 子项（16px），
          即使 opacity:0 也照样占 16px 主轴空间 ⇒ 居中算法把它算进去，
          而眼睛看不见它 ⇒ **非激活标签的内容视觉上偏左**。
          实测：「我的报销」标签等宽 117px，可见内容落在 [8, 86]，
          右侧空 31px 里其实藏着 16px 的隐形按钮 —— 看着就像"没对齐"。
          等宽之后这个错位感更明显（短标签"首页"可见内容只有 50px、右侧空 45px）。
     ⇒ 改成常显但**弱化**（opacity .5）：既让人看得见能关、又不像激活态那样抢视线，
        顺带把"隐形占位"这个观感问题一并解决。
     ⚠️ 对比度：.5 的 --color-meta(#8595A4) 在白底上约 1.9:1 —— 作为"可点的次级控件"
        够用（与全站 .icon-btn 的弱化思路一致），hover 时提到 1 并加灰底。 */
  opacity: 0.5;
  transition: opacity var(--motion-fast) var(--motion-ease-standard),
    background var(--motion-fast) var(--motion-ease-standard);
}
.page-tab:hover .page-tab__close { opacity: 1; }
.page-tab.is-active .page-tab__close { opacity: 1; }
.page-tab__close:hover { background: var(--color-gray-300); color: var(--color-fg); }
.page-tab__close svg { display: block; }
/* 最后一个标签（= 全站只剩这一个页面）：不给关闭按钮（关掉就没页面了）——由 app.js 加该类。
   ⚠️⚠️ 同时必须**撤掉 min-width: 92px**（2026-09-19 乐哥实报：
      「只有一个的时候文字也没居中，右边还空了」）。
      多标签时那条 min-width 是"防标签被挤成一小团"的保护，有用；
      但**唯一标签**本来就不显示 ×、宽度只剩 [图标 + 文字]，
      min-width 会把右边撑出一块**没有任何内容的空白** —— 实测：
        「首页」标签 92px、内容 50px ⇒ 右侧空 26px（肉眼就是"文字靠左、右边缺一块"）；
        「我的报销」标签 92px、内容 56px ⇒ 空 20px。
      撤掉后宽度贴合内容，左右留白各 8px，偏心率实测 = 0。 */
.page-tab--last { min-width: 0; }
.page-tab--last .page-tab__close { display: none; }

/* 标签栏右端的固定操作区（刷新 / 关闭全部），永远贴在右边不随标签滚动。
   ⚠️ 底色必须与标签栏一致（现在都是白）。上一版是 --tab-slot-bg，本版该令牌已删除 ——
     若不改这里，var() 解析失败会让底色变透明，横向滚动时露出一块"镂空"。 */
/* 右侧工具组：刷新 / 关闭全部 / 通知 —— 通知**在最右**（乐哥 2026-09-19：「通知按钮能放最右上角好点」）。
   ⚠️ 本组现在是 index.html 里的**静态节点**（原来由 renderTabsBar 生成），
      因为通知放进去后必须扛得住 renderTabsBar 的整体重建。
   ⚠️ 这里**不要**再写 `margin-left: auto`：本组是 `__tabs`（flex:1）之后的兄弟，
      已经被自然推到右侧。曾经踩过一个坑 —— 本组自带 auto、通知组又加一个 auto，
      两个 auto 把剩余空间**对半分**，结果通知停在半空、右边空出 377px（"空白一大片"）。
      现在由 `__tabs` 独占弹性空间，只有一处 auto 语义。 */
.page-tabs-bar__tools {
  display: flex;
  align-items: center;
  /* ⭐ 2026-09-26 A6：标签页区恢复显示 ⇒ `margin-left:auto` **删掉**（回到上方警告的老规则：
     __tabs 是 flex:1 弹性项时不许有第二个 auto，两个 auto 会平分空白 → 右侧空 377px 的坑；
     门禁 C2 已自动切回"__tabs 可见时禁止"分支）。
     历史：9-25 __tabs 隐藏时这里必须自己 auto 推右；A6 恢复标签后职责归还 __tabs 的 flex:1。 */
  /* 工具间距 6（2026-09-19 晚 2 → 6）—— 原 2px 是「挤」最明显的地方：
     刷新 / 关闭 / 搜索 / 通知 四个控件两两只隔 2px，看着像粘在一起。
     ⚠️ 放宽后总宽增加 3×4 = 12px，已实测各视口下标签区余量仍够（见门禁测试）。 */
  gap: 10px;
  flex: 0 0 auto;
  padding-left: 10px;
  /* ⭐ A6：顶栏已改半透明毛玻璃，本组**不许再有实色底**（否则毛玻璃上贴一块白矩形）。
     原 background: var(--color-surface) 是"标签横滚时右端不镂空"的配套——
     现在栏体自身有半透明底，本组透明即可透出同一份底。 */
  background: transparent;
}
/* 顶栏里的图标按钮：**回归全站基准 36**（2026-09-20）。
   ────────────────────────────────────────────────────────────────────────────
   昨天（09-19 晚）这里是 40，理由只是"跟着顶栏一起加高"。本轮实测发现它其实是个
   **全站孤例**：`.icon-btn` 的基准是 36（layout.css 321 行），100 处在复用，
   只有标签栏把它放大到 40 —— 这正是乐哥说"标签太大、太占位置"的一部分。
   ⇒ 收回 36，与标签栏外的图标按钮同尺寸，不再是孤例。

   ⚠️⚠️ 这条规则（值不管是多少）**必须保留，不许删** —— 它同时是**移动端的闸门**：
       `@media (max-width:768px)` 里有一条 `.icon-btn { width:44px; height:44px }`
       （0,1,0），而本规则是 `.page-tabs-bar .icon-btn`（0,2,0）。
       特异性高的赢、且 **media 查询不改变特异性** ⇒ 本规则在手机上也生效。
       删掉它，手机上标签栏内的这几个按钮会**回落到 44px** ——
       比今天改之前的 40px **还大**，塞进 52px 的栏里只剩 4px 留白（等于没改回）。
       ⇒ 这个坑由 tests/page-tabs.test.js 静态锁住。
   ⚠️⚠️ 用**作用域覆盖**，绝不改全局 `.icon-btn`（见本文件 321 行的 36×36）——
      它被表格行操作、弹窗关闭、列表工具等 **100 处 / 29 个文件**复用
      （`grep -rho icon-btn public/js | wc -l` = 100），改全局会同时撑高那些表格行的
      行高、并让弹窗右上角的关闭按钮变大。
      本项目一贯做法：**改公共类之前先查复用面，用局部覆盖替代全局修改**。 */
.page-tabs-bar .icon-btn { width: 36px; height: 36px; }
/* 通知与「标签操作」之间留一点气口：前者是全站消息、后者是标签的操作，语义不同 */
.page-tabs-bar__tools .notify-wrap {
  margin-left: 6px;
  padding-left: 10px;
  border-left: 1px solid var(--color-border-soft);
}
/* ⚠️⚠️ 这里原本残留着一段**丢了选择器的声明**（`position: sticky; right: 0;
   margin-left: auto; height: 32px; ... }`）—— 那是本规则改版**之前**的旧副本，
   新版（上面 817 行那条）已经把 `margin-left: auto` 去掉了（它会导致通知按钮
   停在半空、右侧空出 377px，见上方 813 行的说明），但旧副本没删干净。

   2026-09-19 修：它不只是"死代码"——**它会把紧接着的下一条规则一起吃掉**：
   CSS 解析器读到孤立的声明时，会一路把它当作下一条规则的「选择器前奏」，
   直到遇到下一个 `{` ⇒ 于是 `.page--narrow { max-width: … }` 被拼进
   这个非法选择器里、**整条规则被丢弃**。
   实测后果：全站 `.page--narrow`（当时的窄版二级页，内容区 1080）**从来没有生效过**，
   所有靠它限宽的页面都是铺满全宽 1456px。
   ⇒ 教训：删规则时**必须删掉整块（含选择器与花括号）**；
      留半截比留着更危险 —— 它会静默破坏后面一条无关的规则。
   ⚠️ 上面这段是**历史记录**，其中的 1080 是 2026-09-14 那版的数值；
      现行数值见下方「两档制」块（2026-09-28 已放宽为 1280 / 1440）。 */

/* ---- 二级页宽度「两档制」（2026-09-14 乐哥拍板；2026-09-28 放宽数值） ----
   背景：二级页此前**三档并存**（内容 1080 / 1392 / 1456），同一个「二级页」身份
   却有三种宽度，看着不像一套东西。现收敛为两档，判据只有一个：
   **页面主体是不是表格**。

   · .page--narrow（内容 1280）—— 无表格的键值 / 表单 / 档案型二级页。
     这类页面铺满全宽（实测内容区 1543px）会把字段拉到两端、视线来回跑，空而散。
   · .page--table（内容 1440）—— 主体是表格的二级页（结算明细 / 提成明细 /
     华大对账明细 / 订单明细）。表格宽度是**列数实打实用掉的**，限到 1280 会出事：
       · 结算明细 10 列表 → 实测**横向滚动 +189px**，末列直接跑出可见区；
       · 订单明细列表   → 实测**表高从 2148px 涨到 4776px**（列被挤得每行折成多行）。
     故两档不是设计偏好，是物理下限。

   ⭐⭐ 2026-09-28 放宽（乐哥：「页面是不是可以整体宽一点，当前宽度不够，
      内容被压缩了，得不偿失」）—— 1144→**1360** / 1360→**1520**。
      依据（乐哥屏 1920 实测，此前记录的"老板屏 1700"经截图反推证伪）：
        · 现状窄档页内容区只有 1064，**左右各空 261px（合计浪费 522px）**，
          是全站被压缩最狠的一类；
        · 明细页内容 1280、左右各空 193px，同样偏窄；
        · 一级列表页 1600（撞上限）内容 1520 —— **已吃满，本次不动**。
      放宽后：窄档内容 1064→**1280**、明细页 1280→**1440**，全站零横滚（15 页 × 4 档实测）。

   ⚠️⚠️ 明细页的**物理天花板是 1520**（不是 1520 这个数字本身可以再调）：
      `.page--table` 挂在**内层** `.page` 上（DOM 是 `.page` › `.page.page--table`），
      外层 `.page` 有 1600 上限（2026-09-27 防超宽屏，门禁锁定），减左右各 40 内边距
      ⇒ 内层最多只能拿到 1520。实测把档位改到 1680 或"不限"，内层**仍是 1520**
      ⇒ 所以 1520 就是本项的最优值，**不要试图再加**（加了也不生效，只留一个假数字）。
   ⚠️ 窄档 1360 与明细页档位改动**必须同步**：改前窄档页比明细页窄 216px（1064 vs 1280），
      两档看着不像一套；改后两档内容区都从 1280 起步（表单页因 --form-max-width 也是 1280）。

   ⚠️ 必须连 **标题区一起限宽**（本类加在 .page 上，而非只加在内容容器上）——
      否则标题贴页面最左、内容居中，左边缘不对齐反而更乱。
   ⚠️ 一级列表页 / 看板**不加任何档位类**：它们的宽度同样被列数用掉，须保持全宽。
   ⚠️ 档位归属是**静态声明**（app.js 的 WIDTH_TIER / detail.js 的 KIND.tier），
      不按「当前渲染出几张表」动态判断 —— 否则同一页面换个数据就换宽度，更乱。

   宽度算式：max-width = 内容区 + 左右各 40 的 --page-padding。 */
.page--narrow {
  max-width: 1360px;   /* 内容区 1280 —— 无表格的二级页 */
  margin-left: auto;
  margin-right: auto;
}
.page--table {
  max-width: 1520px;   /* 内容区 1440 —— 主体是表格的二级页（内层生效；外层 1600 封顶 ⇒ 1520 即上限） */
  margin-left: auto;
  margin-right: auto;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  /* 8（不是 24）—— 与 .page 的 padding-top 24 合成 32px 的上留白。
     原 24 会让两者叠成 56px（见 .page 的注释）。 */
  margin-top: var(--space-2);
  margin-bottom: var(--space-6);
}

/* ⭐ 2026-09-28 页头包盒（乐哥拍板「方案A」，含 TAB 的列表页）：
   页头从「裸放灰底」收进白底圆角卡 —— 与二级页 .detail-head 同规格，
   解决「内容区上面太空 / 左侧菜单栏显高」。类名由 app.js 的 fixPageTabs 打标，
   只作用于「状态筛选 TAB 搬进页头」的页面，无 TAB 的页头不受影响。 */
.page-header--has-tabs {
  align-items: center;
  margin-bottom: var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-frame-border);
  border-radius: var(--radius-lg);
  /* ⭐⭐ 2026-09-28 垂直内边距 12 → **9px**（乐哥：「这个 tab…感觉有一点点高了，要不矮个 10%」）——
     容器高 = 9×2 + 内层框44 + 2(边框) = **64**（原 70，矮 8.6%）；
     无内层框的页面（编辑排期）60 → **54**（正好 −10%，命中乐哥给的幅度）。
     ⚠️ 12 → 9 是**全站四处同改**（`.tabs` / `.sf-shell .tabs` / `.sf-tabs__track` /
        `.page-header--has-tabs`）——只改一处会让"tab 栏高度"重新分叉。
     ⚠️ 9px 不是 `--space-*` 档位值（令牌里没有 9）—— 它由"矮 10%"倒推得到，
        不是随手写的；改它之前先看这句注释。
     ⚠️ 胶囊（34）**不动**：乐哥当天上午刚要求过"胶囊太矮了"提到 34（见该处注释），
        这次说的"高"是**容器**厚，不该再把胶囊压回去。 */
  padding: 9px var(--space-6);
  /* ⭐⭐ 2026-09-28 一级页 tab 也吸顶（乐哥：「另外一级页面 tab 也做悬浮」）——
     吸顶公式与二级页 / 表单页**同一条**（顶栏 72 + 外框留白 14 + 4 = 90px），
     三处必须同值：它们在同一屏里可能前后出现，差 1px 就会被看出来。
     ⚠️ 这个元素同时含「状态筛选 tab（内层小框）」与「右侧操作按钮」——
        整行一起吸顶，与二级页 `.tabs`（同样含 tab + 按钮）行为一致。 */
  position: sticky;
  top: calc(var(--tabs-bar-height) + var(--frame-gap) + 4px);
  z-index: 60;
  /* 贴顶变玻璃是"渐变"而不是突变（乐哥要的是"和其他内容叠加才有"的感觉） */
  transition: background-color var(--motion-base) var(--motion-ease-standard),
              border-color var(--motion-base) var(--motion-ease-standard),
              box-shadow var(--motion-base) var(--motion-ease-standard);
}

/* ⭐⭐⭐ 2026-09-28 一级页 tab「贴顶才变液态玻璃」（乐哥拍板选 L3 档）
   —— 判定见 app.js updateStuck（`rect.top <= stickyTop`，不是固定滚动像素）；
      刚进页面 / 还没贴顶 = 纯白卡零阴影；贴顶那一刻才出玻璃，内容从它下面穿过。 */
.page-header--has-tabs.is-stuck {
  background: var(--liquid-bg);
  backdrop-filter: var(--liquid-blur);
  -webkit-backdrop-filter: var(--liquid-blur);
  border-color: var(--liquid-edge);
  box-shadow: var(--liquid-shadow);
}
/* 内层小框一起做玻璃（乐哥：「内层小框要不要一起做玻璃」→ 做）——
   ⛔ 不做的后果：白卡变玻璃后小框仍是**不透明白底** ⇒ 像"玻璃上贴了张白纸"。 */
.page-header--has-tabs.is-stuck .table-tabs--in-header {
  background: var(--liquid-tray-bg);
  border-color: var(--liquid-tray-edge);
}

.page-title {
  position: relative;
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--font-letter-spacing-display);
  line-height: var(--font-line-height-tight);
  color: var(--color-fg);
}

/* 页面标题左侧竖条装饰条已移除 —— 国产后台旧范式，TDesign / 微信小店均无此元素。
   若需恢复，取消下面注释即可。
.page-title::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 20px;
  border-radius: 2px;
  background: var(--color-accent);
}
*/

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

/* 页面标题旁的「数据说明」问号按钮 */
.page-help-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-left: var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: transparent;
  color: var(--color-muted);
  cursor: pointer;
  vertical-align: middle;
  transition: color .15s, border-color .15s, background .15s;
}
.page-help-btn:hover {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background: var(--color-surface-2);
}

/* 数据说明弹窗内容 */
.help-section { margin-bottom: var(--space-4); }
.help-section:last-child { margin-bottom: 0; }
.help-label {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-accent-text);
  margin-bottom: var(--space-1);
}
.help-text {
  font-size: var(--font-size-sm);
  color: var(--color-fg-2);
  line-height: 1.6;
}

.page-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

/* 页面头部（标题行右侧）的次要按钮：**可见度加强**（2026-09-18 乐哥反馈）
   --------------------------------------------------------------------------
   乐哥原话：「还有个问题，导入导出按钮看着不显眼」。
   原因：`.btn--ghost` 的描边用 `--color-border: #E5E8EC` —— 这是**给卡片留白用的**
   极浅灰，放在同样白色的页面头部里几乎看不见边界，只剩一行灰字悬空；
   而旁边的「新增达人」是蓝底实心 → 主次关系被拉成了「实心按钮 vs 一片文字」。

   ✅ 做法：**只**加强页面头部这一处（`.page-actions` 作用域），不动全局 ghost ——
      全局 `.btn--ghost` 还用在表格操作列（编辑/删除那种图标按钮），
      那些按钮本来就该"轻"（一列里几十个实心描边会非常吵）。
      · 描边提到 `--color-gray-400`（输入框同款，肉眼可辨）
      · 文字用主文字色（不是灰）+ medium 字重
      · hover 转主色（描边 + 文字 + 浅蓝底），与旁边的实心主按钮呼应但不抢主次 */
.page-actions .btn--ghost {
  background: var(--color-surface);
  border-color: var(--color-gray-400);
  color: var(--color-fg);
  font-weight: var(--font-weight-medium);
}

.page-actions .btn--ghost:hover:not(:disabled) {
  border-color: var(--color-accent-text);
  color: var(--color-accent-text);
  background: var(--color-primary-50);
}

/* 桌面端不显示折叠按钮（侧栏固定展开，不支持折叠） */
.sidebar-toggle {
  display: none;
}

/* 响应式：移动端侧栏变抽屉，内容区全屏 */
@media (max-width: 768px) {
  .sidebar-toggle {
    display: inline-flex;
  }
  .sidebar {
    top: 0;
    left: 0;
    bottom: 0;
    width: min(280px, 82vw);
    border-radius: 0 var(--radius-xl) var(--radius-xl) 0;
    transform: translateX(-100%);
    z-index: var(--z-modal);
    transition: transform var(--motion-base) var(--motion-ease-standard);
  }
  .app-shell.sidebar-open .sidebar {
    transform: translateX(0);
  }
  .app-main {
    margin-left: 0;
    padding-right: 0;
    /* 移动端不分「桌面 + 悬浮面板」——内容区全屏，把悬浮相关的四条复位。
       （源顺序在后、特异性相同，所以能覆盖桌面端的规则） */
    margin-top: 0;
    margin-right: 0;
    margin-bottom: 0;
    background: transparent;
    border-radius: 0;
  }
  .sidebar {
    /* 抽屉式：贴左边缘、只圆右侧两角（上面的 .sidebar 块已设，这里补齐悬浮相关的复位） */
    margin: 0;
  }
  .page-stack {
    background: var(--color-bg);
    border-radius: 0;
  }
  .topbar {
    top: 0;
    margin-top: 0;
    border-radius: 0;
    padding: 0 var(--space-4);
  }
  .page {
    padding: var(--space-4);
  }
  .page-header {
    flex-direction: column;
    align-items: stretch;
  }
  .page-actions {
    flex-wrap: wrap;
  }
  .page-actions .btn--lg {
    width: 100%;
  }
  .input,
  .select {
    min-height: 44px;
  }
  .icon-btn {
    width: 44px;
    height: 44px;
  }
  /* ⭐ 顶部标签栏内的图标按钮：移动端**回到 44px**（触摸目标）。
     ────────────────────────────────────────────────────
     ⚠️⚠️ 这条是**必须的**，不是可选的修饰。原因：
       桌面那条 `.page-tabs-bar .icon-btn { 36px }`（特异性 0,2,0）**在手机上也照样生效**
       （media 查询不改变特异性），会把上面这条 `.icon-btn{44}`（0,1,0）盖掉。
       不补本条，手机上的菜单开关 / 刷新 / 关闭 / 搜索 / 通知 全会变成 36px。
     ⭐ 为什么不能只看桌面：菜单开关是**手机上唯一能唤出侧栏的入口**
       （顶栏已撤，见本文件 C4 那条门禁）。36px 命中区偏小：
       Apple HIG 建议 ≥ 44pt、Material 建议 ≥ 48dp。
       （实测过：本轮改动前手机上它是 40px、改后若不补本条会掉到 36px —— 比改前还小。）
     ⭐ 两个场景各自回到既有基准（不是新造一套尺寸）：桌面 36 / 移动端 44。 */
  .page-tabs-bar .icon-btn {
    width: 44px;
    height: 44px;
  }
  /* 栏高在移动端单独加到 56（桌面 52）——
     因为里面装的是 44px 的触摸目标，需要上下各 6px 的留白。
     ⚠️ 必须联动 `--header-height`（分析页目录条的吸顶高度读它），否则手机上目录条会错位。
     ⚠️ 不能把本条写进上面那个公共区域：它只管移动端，而 `--tabs-bar-height` 是全局令牌。 */
  :root {
    --tabs-bar-height: 56px;
    --header-height: 56px;
  }
}

/* 侧栏抽屉遮罩（仅移动端显示） */
.sidebar-backdrop {
  display: none;
}
@media (max-width: 768px) {
  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: color-mix(in srgb, var(--color-gray-900) 40%, transparent);
    z-index: calc(var(--z-modal) - 1);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--motion-base) var(--motion-ease-standard);
  }
  .app-shell.sidebar-open .sidebar-backdrop {
    opacity: 1;
    pointer-events: auto;
  }
}

/* ==========================================================================
   侧栏：搜索入口 + 底部「我的」（2026-09-19 结构重排）
   乐哥：「搜索框放在菜单栏里面（位置你给建议）」「右上角我的 → 挪菜单栏最下面」
   ========================================================================== */

/* ==========================================================================
   标签栏右侧：搜索入口（2026-09-19 乐哥：「搜索框要不继续放到右上角，就是通知左边」）
   ─────────────────────────────────────────────────────────────────────────────
   从侧栏搬过来。三件事决定了它的样子：

   ① 宽度 150px（不是历史那种 280px 的完整输入框）——
      这是"标签区空间"与"框的体量"的取舍。实测标签区余量（4 个标签需 484px）：
        视口 1512：280px 宽时余 319 / 150px 宽时余 449
        视口 1280：280px 宽时余  87 / 150px 宽时余 217
        视口 1180：280px 宽时**缺 13px（标签开始横向滚动）** / 150px 宽时余 117
      ⇒ 280px 会在 13 寸笔记本上把标签挤到滚动；150px 在 1280 以上都稳。
      （150 这个数是照"搜索 + 图标 + / 提示"实际需要的宽度定的，不是随手取的。）

   ② 它是一个**入口**、不是输入框（点击弹 640px 居中弹层）——
      原因见 index.html 的注释：问 AI 的长回答需要 640px，下拉面板装不下。

   ③ 位置在**通知左边**、且属于"全局工具"那一组（刷新/关闭是"标签的操作"，
      搜索与通知是"全站的能力"）⇒ 与通知同组、共用一个左侧分隔线。 */
.gs-entry {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 150px;
  /* 高 40（2026-09-19 晚 34 → 40）—— 与标签（40）和顶栏内图标按钮（40）同一体量。
     原来 34 比邻居矮 2~4px，一行里看着"没对齐、没长齐"。 */
  height: 40px;
  padding: 0 var(--space-3);
  flex-shrink: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  color: var(--color-muted);
  font-size: var(--font-size-sm);
  font-family: inherit;
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease-standard),
    background var(--motion-fast) var(--motion-ease-standard);
}
.gs-entry:hover { border-color: var(--color-accent-text); background: var(--color-surface); }
.gs-entry__icon { display: flex; flex-shrink: 0; opacity: .75; }
.gs-entry__text { flex: 1; text-align: left; }
/* 快捷键提示（`/`）—— 与侧栏那版同款小键帽，告诉新人这功能有快捷键 */
.gs-entry__kbd {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  line-height: 16px;
  padding: 0 5px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-meta);
}
/* 这一组（搜索 + 通知）与左边「标签操作」（刷新/关闭）用一条竖线断开，语义不同 */
.gs-entry-wrap {
  display: flex;
  align-items: center;
  margin-left: 6px;
  padding-left: 10px;
  border-left: 1px solid var(--color-border-soft);
}
/* 通知：同组内与搜索留一点气口（分隔线已由 .gs-entry-wrap 提供，这里不再画第二条） */
.page-tabs-bar__tools .notify-wrap {
  margin-left: 6px;
  padding-left: 0;
  border-left: none;
}
/* 窄屏（≤1100px）：搜索入口收成图标，把宽度还给标签。
   实测 1024px 视口下 150px 入口会让标签区缺 39px；收成图标后够用。
   ⚠️ 只隐藏文字与键帽、**不隐藏整个按钮** —— 搜索在窄屏上更该有入口（侧栏入口已撤）。 */
@media (max-width: 1100px) {
  .gs-entry { width: 36px; padding: 0; justify-content: center; }
  .gs-entry__text, .gs-entry__kbd { display: none; }
}
/* 手机端（≤768px）：工具区本来就只有图标，保持图标形态即可；
   侧栏入口已撤，手机上搜索必须还能用 ⇒ 不隐藏。 */

/* 底部「我的」——基础盒模型在 base.css 的 .sidebar__footer（padding/border-top/flex-shrink），
   这里只写**布局变更**部分，避免同一个类在两处重复声明同一批属性。 */

/* ⚠️ 箭头（chevronDown）必须推到最右（2026-09-19 乐哥：「那个箭头太靠左」）。
   原因：`.user-chip` 是 flex，箭头是第 3 个子元素，默认紧跟在名字后面；
   在原顶栏里它靠得近不明显，搬到 240px 宽的侧栏底部、且 chip 被拉成 100% 宽之后，
   箭头停在中间、右边留一大片空 —— 看起来像没对齐。
   ⇒ `margin-left:auto` 把它顶到右边缘，与下方菜单项右缘对齐。
   ⚠️ 只作用于侧栏底部这一个 chip，不动顶栏/其它位置的 .user-chip。 */
.sidebar__footer .user-chip > .nav-icon {
  margin-left: auto;
  flex-shrink: 0;
}
/* 名字区可收缩：名字长时省略，不把箭头挤出容器 */
.sidebar__footer .user-chip > .user-meta {
  min-width: 0;
  overflow: hidden;
}
.sidebar__footer .user-chip .user-name,
.sidebar__footer .user-chip .user-role {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sidebar__footer .user-chip { width: 100%; }
/* ⚠️ 下拉**向上**弹（在底部）：原顶栏里是 `top: calc(100% + 8px)`，
   搬到底部后那样会弹到屏幕外。改为向上、且左对齐铺满侧栏。 */
.sidebar__footer .profile-menu {
  top: auto;
  bottom: calc(100% + 8px);
  left: 0;
  right: auto;
  width: 100%;
  min-width: 208px;
}

/* ==========================================================================
   全站搜索 · 居中弹层
   --------------------------------------------------------------------------
   ⭐⭐ 2026-09-26 加**入场/退场动画**（乐哥实报：「点击右上角搜索按钮后，弹出了对话框，
   对话框弹出没动画」）—— 全站其他弹层（抽屉 / 弹窗 / toast / 行内菜单）都有
   fade-in / modal-in，只有这里一直是"瞬时显隐"（hidden 切 display，display 不可过渡）。
   ✅ 复用的就是 overlays.css 那套 keyframes 与令牌（语言完全一致，不新造动画）：
        · 遮罩 .gs-overlay → fade-in / fade-out（与 .drawer-overlay 同款）
        · 对话框 .gs-dialog → modal-in / modal-out（与 .modal 同款，弹入带回弹）
   ⚠️ 退场由 JS 加 `[data-closing]` 触发、**播完才 hidden**（见 app.js 的 gsCloseOverlay）——
      hidden 是 display:none，先设就看不到退场。退场倍率 0.62 与全站一致。
   ⚠️ 入场**不需要 JS**：元素从 display:none 恢复显示时，CSS animation 会重新开始（规范行为）。
   ========================================================================== */
.gs-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: color-mix(in srgb, var(--color-gray-900) 38%, transparent);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 12vh var(--space-4) var(--space-4);
  animation: fade-in var(--motion-base) var(--motion-ease) both;
}
.gs-overlay[hidden] { display: none; }
/* 退场：与 .drawer-overlay 同款（时长同倍率、曲线同 --motion-ease-exit） */
.gs-overlay[data-closing] {
  animation: fade-out calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
}
.gs-dialog {
  width: min(640px, 100%);
  max-height: 70vh;
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  animation: modal-in var(--motion-base) var(--motion-ease-pop) both;
}
/* 退场：与 .modal 同款；⚠️ pointer-events:none —— 退场期间对话框不可再点（防误触） */
.gs-dialog[data-closing] {
  animation: modal-out calc(var(--motion-base) * 0.62) var(--motion-ease-exit) both;
  pointer-events: none;
}
.gs-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  height: 52px;
  flex-shrink: 0;
  border-bottom: 1px solid var(--color-border-soft);
}
.gs-head__icon { display: flex; flex-shrink: 0; color: var(--color-muted); }
.gs-head__input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--font-size-md);
  color: var(--color-fg);
}
.gs-head__input::placeholder { color: var(--color-placeholder); }
.gs-head__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-muted);
  cursor: pointer;
}
.gs-head__close:hover { background: var(--color-bg); color: var(--color-fg); }
/* 弹层里的结果面板：不再绝对定位（它是弹层的直接内容），铺满并自己滚 */
.gs-dialog .global-search__panel {
  position: static;
  min-width: 0;
  width: 100%;
  max-width: none;
  max-height: none;
  flex: 1 1 auto;
  overflow-y: auto;
  border: none;
  border-radius: 0;
  box-shadow: none;
}
/* 打开时锁住背景滚动，避免弹层后面还能滚 */
body.gs-open { overflow: hidden; }


/* ══════════════════════════════════════════════════════════════════════════
   ⭐⭐ 2026-09-25 Gashora 骨架落地：顶栏内部三段布局
   ──────────────────────────────────────────────────────────────────────────
   参考图（乐哥指定、逐字描述）：
     「最上面一行左右贯穿 —— 左上角 logo，中间有一部分留白，接着是搜索框，
       右边是通知，最后是头像框（头像框周围做了胶囊状的边框）」
   落位（DOM 由 app.js 的 relocateChrome 迁移，样式在本段）：
     #tabs-left        [菜单按钮] [logo]          ← 左上（菜单按钮是折叠/移动端入口，保留）
     #page-tabs-tabs   **隐藏**（乐哥：「顶部标签我觉得可以不要了，直接去掉」）
     #page-tabs-tools  [搜索] [通知] [头像胶囊]     ← 右上（margin-left:auto 推到最右）
   ══════════════════════════════════════════════════════════════════════════ */


/* 顶栏品牌区（由 app.js 从侧栏 #sidebar-brand 迁入）。
   ⚠️ 尺寸覆盖侧栏那套（.brand-logo 36 / .brand-name__zh 18）—— 顶栏里要更大：
      参考图左上角的 logo 是顶栏的视觉主体（乐哥：「左上角 logo 好像可以大点」→ 46px）。 */
.topbar-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  /* ⭐⭐ 2026-09-26 补（乐哥实报「左上角点击 logo 没法跳回首页了」的**视觉那一半**）：
     本元素由 `relocateChrome()` 从 `#sidebar-brand` 复制而来，而 `.sidebar__brand` 上
     原本就写着 `cursor: pointer` —— **该属性没被复制过来**（innerHTML 只复制内容），
     实测这里一直是 `cursor: auto` ⇒ 就算点击已经修好，鼠标移上去也不变手型、
     用户仍会认为"这里不能点"。
     ⚠️ 修 bug 要修**能感知的那一半**：功能通了但毫无可点暗示 = 用户眼里还是坏的。
     ⚠️ 不要在这里另写焦点环：`activateBrand()` 会给本元素设 `role="button"`，
        而 base.css 的全局规则 `:where(button,[role=button],[tabindex]):focus-visible`
        已经给了 `2px solid --color-accent` + offset 2px（与侧栏那份同一套）。
        重复写一条只会多一处"改了全局这里不生效"的隐患。
     ⚠️ 尤其不要写 `outline: var(--focus-ring)` —— 那个令牌是 **box-shadow 语法**
        （`0 0 0 3px rgba(...)`），当 outline 用会被整条丢弃、焦点环直接消失。 */
  cursor: pointer;
  /* hover 反馈：与 `.sidebar__brand:hover` **同一个令牌**（--color-surface-2 = #FAFBFC），
     两处语言一致 —— 同一个"回首页"的动作，不该因为搬到了顶栏就换一套手感。
     ⚠️ 圆角 + 负外边距：顶栏品牌区两侧没有侧栏那样的通栏留白，
        直接画方块底色会紧贴文字；用 8px 圆角 + 4px 内扩让它看起来像个"药丸"。
     过渡时长用 --motion-fast（hover 类统一档位）。 */
  padding: 4px 8px;
  margin: -4px -8px;
  border-radius: var(--radius-md);
  transition: background-color var(--motion-fast) var(--motion-ease-standard);
}
.topbar-brand:hover {
  background: var(--color-surface-2);
}

.topbar-brand .brand-logo,
.topbar-brand .brand-logo svg {
  width: 34px;    /* A6：46 → 34（栏 96→72 的配套；原型实测 34 与 18px 名称、40px 控件成档）*/
  height: 34px;
  display: block;
}
.topbar-brand .brand-name__zh {
  font-size: 18px;   /* A6：24 → 18（同上配套）*/
  font-weight: 700;
  color: var(--color-fg);
  letter-spacing: -0.01em;
}
/* 英文名在顶栏不显示（空间宝贵，中文名已足够识别） */
.topbar-brand .brand-name__en { display: none; }


/* 顶栏里的搜索入口：A6 改为 **40×40 图标盒**（乐哥：「搜索改成图标吧，不要输入框了，
   这样标签就有更多位置」—— 让出 ~200px 给标签区，1440 宽实测 8 个标签全放下）。
   点击仍弹全局搜索弹层（#gs-dialog），交互不变；「/」快捷键也仍在，只是不再显示提示。
   ⚠️ 形态与铃铛/头像同槽：1px 描边 var(--color-border)（=#E1E8F2）+ 透明底（乐哥：「里面不要填充颜色」）+ 圆角 10。
   ⚠️ 文字与 kbd 的隐藏**限定在顶栏作用域**——侧栏等其他处的 .gs-entry 不受影响。 */
.page-tabs-bar__tools .gs-entry {
  width: 40px;
  min-width: 40px;
  height: 40px;
  border-radius: 10px;
  background: transparent;
  border: 1px solid var(--color-border);
  padding: 0;
  justify-content: center;
}
.page-tabs-bar__tools .gs-entry__text,
.page-tabs-bar__tools .gs-entry__kbd { display: none; }
.page-tabs-bar__tools .gs-entry svg { width: 18px; height: 18px; }
/* ⭐⭐ 2026-09-26 修（乐哥实报「搜索图标颜色和通知的不一致」）：
   顶栏这三个盒子（搜索 / 通知 / 头像）是**并列的主控件**，图标必须同一档颜色。
   🐞 差在哪（生产实测，getComputedStyle 取值）：
     · 通知图标 → color rgb(63,71,83) = `--color-fg-2`，**无透明度**
     · 搜索图标 → color rgb(84,101,121) = `--color-muted`，**外加 `.gs-entry__icon` 的 opacity .75**
       ⇒ 实际渲染 ≈ rgb(127,137,150)，比通知**浅两档**。
   ⭐ 归因：`.gs-entry` 的 muted 与 `.gs-entry__icon` 的 .75 都是"搜索还是个输入框"时代的
     产物（输入框里的图标是装饰，弱化合理）；A6 把它改成**与铃铛并列的图标盒**后，
     这层弱化就变成了"两个同款盒子、图标深浅不一"。
   ⚠️ 只在本作用域覆盖（`.gs-entry` 在**侧栏**里仍是带文字的入口，那里保持原样更合适）。 */
.page-tabs-bar__tools .gs-entry { color: var(--color-fg-2); }
.page-tabs-bar__tools .gs-entry__icon { opacity: 1; }
.page-tabs-bar__tools .gs-entry:hover { background: var(--color-topbar-chip-hover); }
.page-tabs-bar__tools .gs-entry-wrap {
  border: 0;
  padding: 0;
}
/* 通知铃铛：A6 与搜索同款 40×40 图标盒（原 48 圆形无底；svg 20 → 18 同步） */
.page-tabs-bar__tools #notify-wrap .icon-btn,
.page-tabs-bar__tools #notify-bell {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: transparent;
  border: 1px solid var(--color-border);
}
.page-tabs-bar__tools #notify-wrap .icon-btn:hover { background: var(--color-topbar-chip-hover); }
.page-tabs-bar__tools #notify-wrap .icon-btn svg { width: 18px; height: 18px; }

/* ── 顶栏里的「我的」：**胶囊（头像 + 名字）**
   ⭐ 2026-09-26 乐哥：「右上角个人中心，在头像右边再加个名字吧，做成胶囊形状」。
   ▸ 形态演进：A5 文字版（头像+名字+角色+箭头，58px 高）→ A6 **纯圆头像**（40px，全文字隐藏）
     → 本版 **胶囊**（40px 高、头像 30px + 名字单行）。
   ▸ 只显示**名字**（`.user-role` 仍隐藏）：乐哥说的是"加个名字"；
     角色（超级管理员）是另一层信息，挤进 40px 胶囊会让文字缩到 11px、反而不清爽。
   ▸ 箭头仍隐藏（A6 的决定，乐哥当时没要求恢复）：菜单照常弹（点击/回车都行）。
   ▸ 宽度自适应：`width: auto`，由「左内边距 5 + 头像 30 + 间距 8 + 名字宽 + 右内边距 12」决定；
     名字长度不同（2~4 字）胶囊宽度随之变化，不会像固定宽那样出现"名字贴边"。
   ⚠️ 这套样式**只作用于顶栏内**（.page-tabs-bar__tools 作用域）——
     侧栏底部那个「我的」是"头像+名字+角色+箭头"的完整版（宽度 100%），不受影响。
   ⚠️ 头像 32 → 30px：40px 胶囊里 32px 头像上下只剩 4px，视觉"顶格"；
     30px 时上下各 5px、左右留白协调（门禁 H8 的尺寸区间已同步放宽为 28~32）。 */
.page-tabs-bar__tools .profile-wrap { position: relative; }
.page-tabs-bar__tools .user-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: auto;
  height: 40px;
  /* ⭐ 2026-09-26 晚：右内边距 12 → 16（乐哥「可以稍微宽一点点」）。
     加了角色行之后，右侧若还是 12px 会显得"文字顶到胶囊边"（角色名 5 个字比名字宽）。
     ⚠️ 只加右内边距、不动高度与圆角 —— 胶囊仍与旁边 40px 的搜索/通知按钮同高。 */
  padding: 0 16px 0 5px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: transparent;
  cursor: pointer;
  box-sizing: border-box;
}
.page-tabs-bar__tools .user-chip:hover { background: var(--color-topbar-chip-hover); }
.page-tabs-bar__tools .user-avatar,
.page-tabs-bar__tools .user-avatar * {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  font-size: 14px;
}
/* ⭐⭐ 2026-09-26 修（乐哥实报「右上角头像，没有居中展示」）。
   ──────────────────────────────────────────────────────────────────────────
   🐞 根因：base.css 的 `.user-avatar` 带 `padding-top: 1px`（那条是给**文字首字母**
     做的基线补偿："张"字在圆里按几何居中会显偏上）。但它对本元素是 `box-sizing: border-box`，
     于是**内容盒高只剩 31px**；而下面那条 `*` 规则把 `img` 强制成 `height: 32px`
      ⇒ 图片比内容盒高 1px、flex 居中后**整体下溢 0.5px**。
      生产实测（getComputedStyle + getBoundingClientRect）：
        图片相对外层圆框 上间距 **4.5px** / 下间距 **3.5px**（差 1px，即中心偏下 0.5px）。
      8 倍放大截图能直接看到上缘那条环比下缘宽。
   ⭐ 修：本作用域内把 padding 归零 ⇒ 内容盒回到 32px、图片正好填满、上下间距回到对称的 4/4。
   ⚠️ 只覆盖顶栏（不改 base）：侧栏底部那个「我的」仍是"头像+名字"的文字版 chip，
      首字母的 1px 补偿在那边是对的 —— 这正是本项目"改公共规则前先看谁在复用"的又一次实践。
   ⚠️ 顶栏里若某用户**没有头像**（显示首字母），少这 1px 补偿的影响远小于"图片明显偏下"；
      两者不可兼得时按"多数场景 + 用户实报"取图片正确（乐哥实报的就是图片态）。 */
.page-tabs-bar__tools .user-avatar { padding: 0; }
/* 名字 + 角色 = **上下两行**（乐哥 2026-09-26 晚：「右上角个人头像那个，可以稍微宽一点点，
   然后名字位置偏上，下面是角色名（字号偏小）」）。
   ⚠️ `.user-meta` 本来就是 `flex-direction: column`（两行结构早就有），
      改前只是把 `.user-role` 设成 `display:none` 只留一行 —— 本次是**放开**它，
      并把行高/间距收紧，让两行在 40px 胶囊里排得下。 */
.page-tabs-bar__tools .user-meta {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 1px;              /* 两行贴近一点 —— 40px 高里塞两行，间距大了会撑爆 */
  line-height: 1.15;
}
.page-tabs-bar__tools .user-name {
  /* 13 → 14px（乐哥 2026-09-26 深夜：「人名大一点点」）。
     配角色 11px ⇒ 两级差 **3px**（改前是 13/12，只差 1px，主次拉不开）。 */
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-fg);
  line-height: 1.15;
  white-space: nowrap;              /* 名字不许换行（换行会把胶囊撑高、与其它 40px 控件不齐） */
  max-width: 7em;                   /* 超长名字上限：防极端值把顶栏右侧挤爆 */
  overflow: hidden;
  text-overflow: ellipsis;
}
/* ⭐ 角色名：更小一档 + 弱化色（乐哥 2026-09-26 深夜：「角色文字再小点」）。
   ⚠️ 用**裸 11px** 而不是 `--font-size-xs`（桌面 12px / 手机 11px）——
      令牌那一档太大，与 14px 的名字只差 2px，主次不够分明。
      11px 是本项目既有的"最小可读档"（cost-chart / finance / workbench 等 8+ 处同值），
      不再往下降（10px 的中文可读性会明显变差）。 */
.page-tabs-bar__tools .user-role {
  display: block;
  font-size: 11px;
  font-weight: var(--font-weight-regular);
  color: var(--color-muted);
  line-height: 1.15;
  white-space: nowrap;
  max-width: 7em;
  overflow: hidden;
  text-overflow: ellipsis;
}
.page-tabs-bar__tools .user-chip .nav-icon { display: none; }  /* 箭头仍隐藏（菜单照弹）*/
/* 「我的」下拉：顶栏里改为**向下弹出**（原来在侧栏底部是向上弹） */
.page-tabs-bar__tools .profile-menu {
  top: calc(100% + 10px);
  bottom: auto;
  right: 0;
  left: auto;
}

/* ⭐⭐ 2026-09-26 A6 配套：顶栏**搜索盒 / 通知盒**在移动端必须回到 44px 触摸目标。
   ──────────────────────────────────────────────────────────────────────────────
   🐞 实测踩到（乐哥报徽章遮挡时顺带查出的**回归**）：
      视口 390 下，通知按钮 40×40、搜索入口 40×40 —— 而同栏的菜单开关是 44×44。
      根因：A6 这两条桌面规则的特异性比移动端那条高，**media 查询不改变特异性**：
        · `.page-tabs-bar__tools #notify-wrap .icon-btn` = (1,2,0)   ← 含 id，赢
        · `.page-tabs-bar .icon-btn`（移动端 44px 那条） = (0,2,0)
      ⇒ 手机上它们停在桌面的 40px（低于 Apple HIG ≥44pt / Material ≥48dp）。
   ⚠️⚠️ 本块**必须放在 A6 那两条规则之后**（同特异性时后写的赢）。
      第一版把它加在了文件上方那个 `@media (max-width: 768px)` 块里（第 ~1400 行），
      结果**不生效**（A6 规则在 1700+ 行，靠后 ⇒ A6 赢）—— 实测复现过。
      这也正是 H6 门禁那条"两条都要显式存在"纪律的延伸：**位置也是契约的一部分**。
   ⚠️ 改 A6 那几个控件的桌面尺寸/选择器时，本块要一起复核。
   ⚠️ 移动端栏高 56px（见上方 `:root` 覆盖）⇒ 44px 控件上下各留 6px；
      徽章的 `top:-5px` 外溢 5px 仍距栏边 1px，不越界（`--tabs-bar-height` 移动端覆盖处已验算）。 */
@media (max-width: 768px) {
  .page-tabs-bar__tools #notify-wrap .icon-btn,
  .page-tabs-bar__tools #notify-bell,
  .page-tabs-bar__tools .gs-entry {
    width: 44px;
    min-width: 44px;
    height: 44px;
  }
}
