/*
 * ============================================================================
 * 弈欣 GEO 生成式引擎优化智能分发系统
 * 版权所有 (c) 2026 RYANYUU　保留所有权利
 * ----------------------------------------------------------------------------
 * 源码作者 ：RYANYUU
 * 联系方式 ：18802036367
 * 版权声明 ：本源码由作者独立开发，并已登记计算机软件著作权
 *           （软著登记号：【请向作者索取】）。
 *           未经著作权人书面许可，任何单位或个人不得以任何形式公开传播、
 *           复制、修改、反编译或转载本源码，亦不得将本源码及其衍生作品用于
 *           出售、出租或商业性分发；违者将依法追究法律责任。
 * ============================================================================
 */
/* ==========================================================================
   弈欣GEO（YIXIN GEO）— design system
   --------------------------------------------------------------------------
   统一标准（依据 Apple HIG）：
     · 颜色：语义化 token，浅色/深色双套，全部满足 4.5:1（正文）与 3:1（大字/粗体）
     · 字重：仅使用 400 / 500 / 600 / 700（避免 550 / 650 这类合成字重）
     · 字号：对齐 macOS 文本样式（Title1 22 / Title2 17 / Title3 15 / Body 13 /
             Callout 12 / Footnote 11），最小 11px（macOS 最小 10pt）
     · 圆角：四档 + 胶囊，禁止任意数值
     · 焦点：所有可交互元素有可见 focus-visible 环
     · 动效：尊重 prefers-reduced-motion / prefers-contrast
   ========================================================================== */

:root {
  color-scheme: light dark;

  /* ---- 圆角（四档 + 胶囊）---- */
  --r-xs: 6px;    /* 骨架、滚动条、图例色块、小标签 */
  --r-sm: 8px;    /* 按钮、输入框、下拉、chip、代码块 */
  --r-md: 12px;   /* 卡片 */
  --r-lg: 16px;   /* 弹窗 / 浮层 / 登录卡 */
  --r-pill: 999px;/* 胶囊：徽标、状态标签、开关型 chip */

  /* ---- 字号（macOS 文本样式）---- */
  --fs-display: 22px;  /* 页面标题  Title 1 */
  --fs-title-2: 17px;  /* 区块标题  Title 2 */
  --fs-title-3: 15px;  /* 卡片标题  Title 3 */
  --fs-headline: 13px; /* 强调正文  Headline */
  --fs-body: 13px;     /* 正文      Body      */
  --fs-callout: 12px;  /* 次要正文  Callout   */
  --fs-footnote: 11px; /* 辅助说明  Footnote  */

  /* ---- 字重（仅 4 档）---- */
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* ---- 间距（8pt 节奏）---- */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px;

  /* 语义间距：全局只允许用这几档 */
  --gap-btn: 8px;        /* 同一组按钮之间 */
  --gap-group: 12px;     /* 工具条内不同功能组之间 */
  --gap-stack: var(--sp-4);   /* 卡片之间、字段之间 */
  --pad-page: var(--sp-6);    /* 页面内边距 */
  --pad-card: var(--sp-5);    /* 卡片内边距 */
  --pad-card-head: 14px var(--sp-5);
  --w-sidebar: 232px;
  --h-toolbar: 52px;
  --w-content: 1360px;   /* 内容最大宽度（控制阅读行长） */

  /* ---- 安全距离（全局唯一来源，各视图不得自定）----
     定义：元素内"墨迹"（文字 / 图标）到自身内边框的最小水平距离。
     分档依据来自真实 Chrome 截图 + 像素级测量（不靠读 CSS 下结论）。 */
  --safe-x: 12px;        /* 控件 / 卡片：内容距左右边框（与 .coverage-item 既有要求一致） */
  --safe-x-chip: 9px;    /* 徽标 / 标签：小 pill，按比例收窄 */
  --safe-x-icon: 6px;    /* 纯图标按钮：28px 圆内放 14px 图标 */

  /* ---- 下拉箭头：自绘，替代浏览器原生箭头 ----
     原生箭头是浏览器画在元素右边缘上的**像素**，不是 DOM 节点，
     padding-right 只能推文字、推不动它 —— 实测它的右边缘距右边框只有 2.5px。
     因此这里 appearance:none 关掉原生箭头，改用 background-image 自绘：
     位置由 --select-arrow-inset 精确决定，深浅两种外观各存一份 SVG
     （SVG 背景图无法继承 currentColor，只能用变量切换）。 */
  --select-arrow-inset: var(--safe-x);   /* 箭头右边缘距下拉框右边框 */
  --select-arrow-size: 12px;             /* 箭头视觉尺寸 */
  --select-arrow-gap: 8px;               /* 文字右边界与箭头左边缘 */
  --select-pad-r: calc(var(--select-arrow-inset) + var(--select-arrow-size) + var(--select-arrow-gap)); /* = 32px */
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.6 6 7.6l3-3' stroke='%235f6b7c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* ---- Liquid Glass（功能层专用）---- */
  --glass-fill: rgba(255, 255, 255, .72);
  --glass-fill-strong: rgba(255, 255, 255, .84);
  --glass-border: rgba(255, 255, 255, .55);
  --glass-hairline: rgba(15, 23, 42, .08);
  --glass-blur: 28px;
  --glass-saturate: 1.5;

  /* ---- 浅色外观 ---- */
  --bg: #f5f7fb;
  --bg-elev: #ffffff;
  --bg-subtle: #f0f3f9;
  --bg-hover: #f7f9fc;

  /* ---- 深色边栏（两种外观下都保持深色）---- */
  --sidebar: #0f172a;            /* 回退用实底（降低透明度 / 增强对比度时） */
  --sidebar-fill: rgba(15, 23, 42, .90);
  --sidebar-hover: rgba(255, 255, 255, .09);
  --sidebar-active: #2563eb;
  --sidebar-text: #e2e8f0;       /* 与深色底的对比度见报告 */
  --sidebar-text-2: #a8b6c8;
  --sidebar-line: rgba(148, 163, 184, .18);
  --sidebar-badge: rgba(148, 163, 184, .20);

  --border: #e3e8f0;
  --line: #e3e8f0;             /* 兼容视图内联用法 */
  --border-strong: #cbd5e1;

  --text: #0f172a;             /* 17.85:1 on white */
  --text-2: #475569;           /*  7.58:1 on white */
  --text-3: #5f6b7c;           /*  5.41:1 on white / 4.87:1 on #f0f3f9 */

  --accent: #2563eb;           /* 强调文字/图标 5.17:1 on white */
  --accent-hover: #1d4ed8;
  --accent-solid: #2563eb;     /* 实心按钮底色，白字 5.17:1 */
  --accent-soft: #eff6ff;
  --primary: #2563eb;          /* 兼容旧命名 */
  --primary-hover: #1d4ed8;
  --primary-soft: #eff6ff;

  --success: #047857;          /* 5.48:1 on white */
  --success-soft: #ecfdf5;
  --success-text: #065f46;     /* 7.29:1 on #ecfdf5 */
  --warning: #b45309;          /* 4.82:1 on white */
  --warning-soft: #fffbeb;
  --warning-text: #92400e;     /* 6.84:1 on #fffbeb */
  --danger: #b91c1c;           /* 6.03:1 on white */
  --danger-soft: #fef2f2;
  --danger-text: #991b1b;      /* 7.60:1 on #fef2f2 */
  --danger-solid: #dc2626;     /* 白字 4.53:1 */
  --danger-hover: #b91c1c;     /* 悬停：更深，白字 6.47:1 */
  --purple: #6d28d9;
  --purple-soft: #f5f3ff;
  --purple-text: #5b21b6;      /* 8.19:1 on #f5f3ff */
  --cyan: #0e7490;

  /* 涨跌（中国习惯：涨红跌绿） */
  --up: #b91c1c;
  --down: #047857;

  --focus-ring: rgba(37, 99, 235, .38);

  --shadow-sm: 0 1px 2px rgba(15, 23, 42, .06);
  --shadow: 0 2px 8px rgba(15, 23, 42, .07);
  --shadow-lg: 0 16px 40px rgba(15, 23, 42, .16);

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

/* ---------------------------------------------------------------- 深色外观 */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1c1c1e;
    --bg-elev: #2c2c2e;
    --bg-subtle: #3a3a3c;
    --bg-hover: #343436;

    --sidebar: #161618;
    --sidebar-fill: rgba(22, 22, 24, .92);
    --sidebar-hover: rgba(255, 255, 255, .09);
    --sidebar-active: #0a6ed1;
    --sidebar-text: #e5e5ea;
    --sidebar-text-2: #a1a1a6;
    --sidebar-line: rgba(255, 255, 255, .10);
    --sidebar-badge: rgba(255, 255, 255, .14);

    --border: #3a3a3c;
    --line: #3a3a3c;
    --border-strong: #48484a;

    --text: #f5f5f7;             /* 15.63:1 */
    --text-2: #c7c7cc;           /* 10.10:1 */
    /* #98989d 在 --bg-subtle 上仅 3.95:1，改用 #aeaeb2（5.13:1 / 6.30:1） */
    --text-3: #aeaeb2;

    /* 下拉箭头深色版：SVG 背景图不吃 currentColor，只能按外观各存一份。
       颜色跟随 --text-3（深色下是浅灰），保证在 --bg-elev 上依然清晰。 */
    --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.6 6 7.6l3-3' stroke='%23aeaeb2' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

    --accent: #409cff;           /* 6.01:1 on #1c1c1e */
    --accent-hover: #6ab4ff;
    --accent-solid: #0a6ed1;     /* 白字 5.04:1 */
    --accent-soft: #10294a;
    --primary: #409cff;
    --primary-hover: #6ab4ff;
    --primary-soft: #10294a;

    --success: #30d158;
    --success-soft: #10321c;
    --success-text: #6ee79a;
    --warning: #ff9f0a;
    --warning-soft: #3a2a08;
    --warning-text: #ffc861;
    /* #ff453a 在卡片上仅 4.09:1，改用 #ff6b60（4.99:1） */
    --danger: #ff6b60;
    --danger-soft: #3d1614;
    --danger-text: #ff8b82;
    --danger-solid: #a8281f;   /* 白字 7.03:1 */
    --danger-hover: #c0392b;   /* 悬停：更亮，白字 5.43:1（深色背景下必须变亮才像可点） */
    --purple: #bf5af2;
    --purple-soft: #2c1740;
    --purple-text: #d7a6f5;
    --cyan: #64d2ff;

    --up: #ff6b60;
    --down: #30d158;

    --focus-ring: rgba(64, 156, 255, .45);

    --glass-fill: rgba(44, 44, 46, .66);
    --glass-fill-strong: rgba(44, 44, 46, .84);
    --glass-border: rgba(255, 255, 255, .12);
    --glass-hairline: rgba(255, 255, 255, .09);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
    --shadow: 0 2px 10px rgba(0, 0, 0, .55);
    --shadow-lg: 0 20px 48px rgba(0, 0, 0, .65);
  }
}

/* -------------------------------------------------------------- 增强对比度 */
@media (prefers-contrast: more) {
  :root {
    --text-3: #475569;
    --text-2: #334155;
    --border: #94a3b8;
    --line: #94a3b8;
    --border-strong: #64748b;
    --focus-ring: rgba(37, 99, 235, .75);
  }
}
@media (prefers-color-scheme: dark) and (prefers-contrast: more) {
  :root {
    --text-3: #c7c7cc;
    --text-2: #e5e5ea;
    --border: #6c6c70;
    --line: #6c6c70;
    --border-strong: #8e8e93;
    --focus-ring: rgba(64, 156, 255, .85);
  }
}

/* ------------------------------------------------------------------- base */
* { box-sizing: border-box; }
html, body, #app { height: 100%; margin: 0; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
               "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* 统一标题层级：字号 + 字重，不再逐处硬编码 */
h1, h2, h3, h4 { margin: 0; font-weight: var(--fw-semibold); letter-spacing: -.01em; color: var(--text); }
h1 { font-size: var(--fs-display); }
h2 { font-size: var(--fs-title-2); }
h3 { font-size: var(--fs-title-3); }
h4 { font-size: var(--fs-headline); }
p { margin: 0 0 10px; }

code {
  font-family: var(--mono);
  font-size: var(--fs-footnote);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  /* 行内代码同样是一个"小 pill"：左右内边距与 .badge/.pill 统一到同一档 */
  padding: 1px var(--safe-x-chip);
  color: var(--text);
}
.mono { font-family: var(--mono); font-size: var(--fs-footnote); }
.num { font-variant-numeric: tabular-nums; font-family: var(--mono); font-size: var(--fs-callout); }

/* ---------------------------------------------------------------- 图标系统 */
/*
 * 图标统一走 CSS mask：把 SVG 当遮罩、用 background-color 上色。
 *
 * 为什么不用 <img>：web/assets/icons/ 下的 SVG 是 iconfont 导出的**填充型**图标，
 *   有的硬编码 fill="#000000"、有的不带 fill（默认黑），viewBox 也从 24 变成 1024。
 *   用 <img> 在深色主题下就是一团黑，且无法染色。
 *   mask 只取 alpha 通道 —— 遮罩里 fill 是什么颜色都无所谓，
 *   图标颜色一律由 currentColor 决定，深浅两套主题自动跟随。
 *
 * 尺寸用 1em，图标跟着字号走：替换掉 emoji 后，各处的视觉大小自动对齐，
 * 不需要为每个位置单独调尺寸。
 */
.gi {
  display: inline-block;
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  vertical-align: -.125em;
  background-color: currentColor;
  -webkit-mask-image: var(--gi);
  mask-image: var(--gi);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
/* 品牌 logo（服务商）：彩色位图，按原样显示、不参与染色，也不吃主题色 */
.gi-provider {
  background-color: transparent;
  background-image: var(--gi);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  -webkit-mask-image: none;
  mask-image: none;
}

/* ------------------------------------------------------ 焦点与键盘可用性 */
/* 只加焦点环，不改任何几何属性（否则聚焦时按钮形状会变） */
:where(a, button, select, input, textarea, [tabindex]):focus-visible,
.tab:focus-visible, .chip:focus-visible, .nav-item:focus-visible, .modal-close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* 鼠标点击不显示焦点环，键盘操作才显示 */
:focus:not(:focus-visible) { outline: none; }

/* ------------------------------------------------------------------ layout */
/* 环境底色：玻璃层需要「有东西可反射」，同时让品牌色进入内容层 */
.app-shell {
  display: flex; height: 100%; overflow: hidden;
  position: relative;
  background:
    radial-gradient(1100px 560px at 8% -8%, rgba(37, 99, 235, .10) 0%, transparent 60%),
    radial-gradient(900px 520px at 100% 0%, rgba(139, 92, 246, .08) 0%, transparent 58%),
    var(--bg);
}
@media (prefers-color-scheme: dark) {
  .app-shell {
    background:
      radial-gradient(1100px 560px at 8% -8%, rgba(10, 132, 255, .13) 0%, transparent 60%),
      radial-gradient(900px 520px at 100% 0%, rgba(191, 90, 242, .10) 0%, transparent 58%),
      var(--bg);
  }
}

.sidebar {
  width: var(--w-sidebar); flex: 0 0 var(--w-sidebar);
  display: flex; flex-direction: column;
  overflow-y: auto;
  /* 功能层：深色 Glass regular —— 仍是玻璃（可模糊背景），但底色为深色，
     两种外观下都保持深色边栏的视觉。 */
  background: var(--sidebar-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border-right: 1px solid var(--sidebar-line);
  color: var(--sidebar-text);
  transition: width .22s ease, flex-basis .22s ease;
}
.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-thumb { background: rgba(148, 163, 184, .38); border-radius: var(--r-pill); }
.sidebar > * { position: relative; }  /* 让文字压在玻璃之上 */

/* 隐藏边栏（HIG：让人们可以收起边栏） */
.app-shell.no-sidebar .sidebar { width: 0; flex-basis: 0; border-right: 0; overflow: hidden; }

/* --------------------------------------------------------------------------
   窄屏：边栏变抽屉
   --------------------------------------------------------------------------
   以前只把宽度从 232px 降到 200px，390px 的手机上正文只剩 190px：
   卡片标题被压成一行两三个字，指标数字竖着排。窄屏必须让边栏**离开文档流**，
   正文拿满宽度；展开时用遮罩盖住内容，点遮罩或点任一菜单项即收起。
   -------------------------------------------------------------------------- */
.sidebar-backdrop {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(15, 23, 42, .44);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
@media (max-width: 860px) {
  .app-shell.is-narrow .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 80;
    width: min(272px, 84vw); flex-basis: auto;
    transform: translateX(-102%);
    transition: transform .24s cubic-bezier(.32, .72, 0, 1);
    box-shadow: 0 24px 60px rgba(2, 6, 23, .38);
  }
  .app-shell.is-narrow.drawer-open .sidebar { transform: none; }
  /* 抽屉态下 .main 必须占满：否则 flex 仍按边栏宽度让位 */
  .app-shell.is-narrow .main { width: 100%; }
  .app-shell.is-narrow .content { padding: 14px; }
  .app-shell.is-narrow .topbar { padding: 0 10px; gap: 6px; }

  /*
   * 顶栏在 390px 上放不下「引导 + 二维码 + 品牌选择 + 授权 + 头像 + 姓名 + 角色」。
   * 原来的表现：右端那串「演示账号 / 月度授权」被挤成竖排并被裁掉半个字。
   * 窄屏只保留"动作"，身份信息移进抽屉底部（.sidebar-foot）。
   * 尺寸要算过：40(菜单) + 86(引导) + 30(二维码) + 108(品牌) + 28(头像) + 4×6(间距) + 20(内边距)
   * = 336 < 390，留 54px 余量给长品牌名。
   *
   * ⚠ 不要给 .guide-chip 加 max-width / overflow:hidden —— 试过了，实测反弹：
   *   胶囊被压到 76px 后文字被裁，墨迹距边框只剩 0.7px，
   *   全站安全距离体检直接报 45 处违规（`.guide-chip` 15 处 × 3 条规则）。
   *   这个胶囊里是**有文字的**，它没有可压缩的余量；顶栏里该让步的是空白与弹性间距。
   *   （下面 .guide-chip 那段注释也写了同一件事，这里是"为什么窄屏也没压"的出处。）
   */
  .app-shell.is-narrow .user-chip .small { display: none; }
  .app-shell.is-narrow .user-chip { padding: 0; background: none; }
  .app-shell.is-narrow .license-chip { display: none; }
  .app-shell.is-narrow .brand-picker > label { display: none; }
  .app-shell.is-narrow .brand-picker { min-width: 0; }
  .app-shell.is-narrow .brand-picker select { max-width: 108px; }
  .app-shell.is-narrow .topbar-group { min-width: 0; }
  /*
   * 触控：小号按钮 27px 高时手指很难点准（实测全站普遍 27px），窄屏抬到 34px。
   * ⚠ 不要再加 min-width：内容列表的操作列是按图标宽度排的固定槽位，
   * 一给 34px 最小宽度，四个按钮就互相压住（实测重叠 59%）。
   * 图标按钮的横向可点区域靠 padding 解决，不靠最小宽度。
   */
  .app-shell.is-narrow .btn-sm,
  .app-shell.is-narrow .btn-xs { min-height: 34px; }
  .app-shell.is-narrow .collapse-toggle { min-height: 34px; }
}
@media (max-width: 860px) and (prefers-reduced-motion: reduce) {
  .app-shell.is-narrow .sidebar { transition: none; }
}

/* 抽屉底部账号区（只在窄屏渲染）：姓名 / 角色 / 退出登录 */
.sidebar-foot {
  margin-top: auto; padding: 14px 16px 18px;
  border-top: 1px solid var(--sidebar-line);
  display: flex; flex-direction: column; gap: 10px;
  /*
   * 17 个菜单项在 844px 上正好占满抽屉，底部账号区会被推到滚动区之外 ——
   * 用户要"退出登录"就得先把菜单滚到底。贴底常驻，退出登录永远在视线里。
   */
  position: sticky; bottom: 0; z-index: 2;
  background: var(--sidebar);
}
.sidebar-user { display: flex; align-items: center; gap: 10px; min-width: 0; }
.sidebar-user-text { min-width: 0; }
.sidebar-user-name {
  color: var(--sidebar-text); font-size: var(--fs-body); font-weight: var(--fw-semibold);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-user-sub {
  color: var(--sidebar-text-2); font-size: var(--fs-footnote);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-foot .btn { width: 100%; justify-content: center; }

.brand {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-4) 14px;
  color: #fff;
  border-bottom: 1px solid var(--sidebar-line);
}
.brand-logo {
  width: 32px; height: 32px; flex: 0 0 32px; border-radius: var(--r-sm);
  display: grid; place-items: center; overflow: hidden;
}
.brand-logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand-text { line-height: 1.25; min-width: 0; }
/* 自定义名称可能很长：单行省略，完整名称走 title 悬停 —— 撑破侧栏比截断更糟 */
.brand-title {
  font-size: var(--fs-headline); font-weight: var(--fw-semibold); letter-spacing: .2px; color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.brand-sub { font-size: var(--fs-footnote); color: var(--sidebar-text-2); letter-spacing: .8px; white-space: nowrap; }

/* ── 品牌外观预览（系统设置 → 品牌外观）─────────────────────────────
   按侧栏的真实配色画一块小面板，而不是用浅色卡片"示意"一下：
   浅色底上看得很清楚的名字，放到深色侧栏里可能就糊了。 */
.brand-preview { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.brand-preview-frame {
  width: 248px; padding: 14px 16px; border-radius: var(--r-md);
  background: linear-gradient(160deg, #0f172a, #111c33);
  border: 1px solid rgba(148, 163, 184, .22);
  box-shadow: var(--sh-2);
}
/* 复用侧栏的 .brand 结构，但去掉它自带的分隔线与外边距 */
.brand-preview-frame .brand { padding: 0; border-bottom: 0; }
.brand-preview-hint { display: flex; flex-direction: column; gap: 4px; max-width: 320px; }
.brand-preview-hint b { font-size: var(--fs-callout); color: var(--text); }
.brand-preview-hint span { font-size: var(--fs-footnote); color: var(--text-3); line-height: 1.6; }

/* logo 上传：左边是当前图，右边是动作按钮 */
.brand-upload { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.brand-upload-thumb {
  width: 56px; height: 56px; border-radius: var(--r-sm); flex: 0 0 56px;
  display: grid; place-items: center; overflow: hidden;
  background: var(--card-2); border: 1px solid var(--line);
}
.brand-upload-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand-upload-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
/* <label> 当按钮用：禁用态要自己画，label 上没有 :disabled */
.brand-upload-actions .btn.is-disabled { opacity: .45; cursor: not-allowed; pointer-events: none; }
.brand-save-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-4); }

.nav { padding: var(--sp-2) var(--sp-2) var(--sp-5); flex: 1; }
.nav-group-label {
  font-size: var(--fs-footnote); color: var(--sidebar-text-2);
  letter-spacing: .8px; padding: 14px 10px 6px; font-weight: var(--fw-semibold);
}
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border-radius: var(--r-sm);
  color: var(--sidebar-text); cursor: pointer; font-size: var(--fs-body);
  transition: background .13s ease, color .13s ease; user-select: none;
}
.nav-item:hover { background: var(--sidebar-hover); color: #fff; text-decoration: none; }
.nav-item.active { background: var(--sidebar-active); color: #fff; font-weight: var(--fw-semibold); }
.nav-item.active .nav-icon { color: #fff; }
/* 图标位固定 17px 方块：原来 emoji 是 17px 文本位，换 svg 后保持同一栅格与左对齐 */
.nav-icon { width: 17px; height: 17px; flex: 0 0 17px; }
.nav-badge {
  margin-left: auto; font-size: var(--fs-footnote);
  background: var(--sidebar-badge); color: var(--sidebar-text);
  padding: 1px 7px; border-radius: var(--r-pill);
}
.nav-item.active .nav-badge { background: rgba(255, 255, 255, .26); color: #fff; }

.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }

/* ---------------------------------------------------------------- 全站页脚 */
/*
 * 版权信息出现在每个页面底部。
 * 两处放置方式不同、但视觉一致：
 *   · 登录后：跟在 <main> 之后，随内容滚动（内容短时被 .content 的 flex:1 顶到底部）
 *   · 登录/授权等整屏页：用 .standalone 包一层 flex 列，页脚自然落到屏幕底部，
 *     不会把整屏页撑出滚动条（直接 append 的话 min-height:100% 会被顶下去）
 */
.app-foot {
  flex: 0 0 auto;
  padding: 14px var(--pad-page) 18px;
  text-align: center;
  font-size: var(--fs-footnote);
  color: var(--text-3);
  border-top: 1px solid var(--border);
}
/* 版权行与法律文件行分两行：条款入口在**每个页面**都有，和版权挤在一行时
   窄屏会被折成参差不齐的三段，反而看不出哪个是可点的链接。 */
.app-foot-legal { margin-top: 6px; line-height: 1.9; }
/* 备案号：单独一行、居中，颜色比版权行深一档（--text-2）。
   备案规定要求它"清晰可见并链接到工信部网站"—— 和版权同样淡的灰
   容易被认定为刻意弱化，所以这里刻意不用 --text-3。 */
.app-foot-icp { margin-top: 4px; line-height: 1.8; }
.app-foot-icp a { color: var(--text-2); text-decoration: none; }
.app-foot-icp a:hover { color: var(--accent); }
/* 公安备案：图标 + 号码。图标是 36×40 的位图，缩到 20×22 显示 ——
   按原比例缩，不拉伸；inline-flex 让它和旁边的 ICP 文字同一基线，
   否则会像"贴在行里的一张图"。 */
.app-foot-icp .beian-sep { margin: 0 8px; opacity: .5; }
.app-foot-icp .gongan { display: inline-flex; align-items: center; gap: 6px; vertical-align: middle; }
.app-foot-icp .gongan img { display: block; width: 20px; height: 22px; }
.app-foot-icp .gongan:hover { color: var(--accent); }
/* 整屏页（登录 / 授权 / 分享）的页脚：去掉分隔线，避免和整屏视觉打架 */
.standalone { min-height: 100%; display: flex; flex-direction: column; }
.standalone-body { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.standalone .app-foot { border-top: 0; padding-bottom: 10px; }

/* 滚动容器：工具条放在其中并 sticky，内容可滚到玻璃下方 → 真正的 scroll edge effect */
.scroller { flex: 1; overflow-y: auto; overflow-x: hidden; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 30;
  height: var(--h-toolbar); flex: 0 0 var(--h-toolbar);
  display: flex; align-items: center; gap: var(--gap-group);
  padding: 0 var(--pad-page);
  /* 功能层玻璃：不再使用实心底色（toolbars.md：减少工具条背景与着色控件） */
  background: var(--glass-fill-strong);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border-bottom: 1px solid var(--glass-hairline);
}
/* 滚动边缘：内容与工具条相接处渐隐，而不是硬碰硬 */
.topbar::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 14px;
  pointer-events: none;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--bg) 55%, transparent), transparent);
}
.topbar-title { font-size: var(--fs-title-2); font-weight: var(--fw-semibold); white-space: nowrap; }
.topbar-spacer { flex: 1; }
/* 工具条分区：最多三组（toolbars.md：Minimize the number of groups） */
.topbar-group { display: flex; align-items: center; gap: var(--gap-btn); min-width: 0; }

/* 边栏开关是固定 28px 的圆形按钮，**不许被后面的东西挤窄**：
   一旦被压到 27px，图标距边缘只剩 5.8px，既破坏圆形、也低于图标类 6px 的下限
   （真实发生过：顶栏新加了一个胶囊，这个按钮被 flex 压掉 1px）。
   顶栏里要收缩的应该是文字，不是圆形控件。 */
.topbar > .btn-icon { flex: 0 0 auto; }

.brand-picker {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  /* 右侧留一点安全距离：原来这里是 0，下拉框的盒子直接贴着胶囊边框（实测只剩 1px），
     箭头与圆角内弧视觉上会"咬"在一起，长品牌名时更明显。 */
  padding: 4px 6px 4px var(--safe-x); background: var(--bg-elev); font-size: var(--fs-body);
  /* 自身也要能收缩，否则整个胶囊会把顶栏挤出去 */
  min-width: 0;
}
.brand-picker > label { white-space: nowrap; flex: 0 0 auto; }
.brand-picker select {
  border: 0; font: inherit; color: var(--text);
  outline: none; cursor: pointer;
  /*
   * 关键：flex 项的 min-width 默认是 auto（= 内容宽度），下拉框的元素宽度又由
   * **最宽的那个 option** 决定 —— 于是品牌名一长，下拉框就"顶破"胶囊边框，
   * 文字与边框叠在一起。min-width:0 允许它收缩，再用省略号收尾。
   */
  flex: 0 1 auto; min-width: 0; max-width: 190px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* 标签与下拉框之间已有 8px 的 flex gap，这里不再叠加左内边距 */
  padding-left: 0;
  /* 必须用 background-color 而不是 background 简写：
     简写会把下面自绘箭头的 background-image 一起重置掉。 */
  background-color: transparent;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right var(--select-arrow-inset) center;
  background-size: var(--select-arrow-size) var(--select-arrow-size);
}

/* ------------------------------------------------- 顶栏「扫码加微信」支持入口 */
/*
 * 悬停 / 键盘聚焦展开开发者微信二维码。
 *
 * 两个必须守住的点：
 *   ① 卡片要右对齐到图标（而不是居中）—— 这个入口紧贴顶栏右侧的"当前品牌"，
 *      236px 宽的卡片若居中会有一半探出视口右边缘，被 .scroller 的
 *      overflow-x: hidden 直接裁掉（而且看不出来，因为卡片本身还在 DOM 里）。
 *   ② 用 opacity + visibility 收起，而不是 display:none —— 保留过渡动画，
 *      同时 visibility:hidden 会让它不可命中（命中测试拿不到），
 *      所以"折起来了"这件事是可校验的（offsetParent 对 visibility:hidden 仍非空，
 *      只查 offsetParent 会误判成可见）。
 */
.support-entry {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: 0 0 30px;
  /* 图标尺寸走 1em（.gi 的规则），顶栏默认字号只有 13px，
     直接继承会让二维码小成一个点 —— 这里提到与 ☰ 同量级。 */
  font-size: 17px;
  border-radius: var(--r-sm);
  color: var(--text-3);
  cursor: pointer;
  outline: none;
}
.support-entry:hover,
.support-entry:focus-visible { color: var(--accent); background: var(--bg-hover); }
.support-entry:focus-visible { box-shadow: 0 0 0 2px var(--accent-soft); }

.support-card {
  position: absolute; top: calc(100% + 9px); right: 0;
  width: 232px; padding: 10px;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-lg);
  /* 收起态：不可见 + 不可命中 + 不参与点击 */
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-4px);
  transition: opacity .16s ease, transform .16s ease, visibility .16s ease;
}
.support-entry:hover .support-card,
.support-entry:focus-visible .support-card,
.support-entry:focus-within .support-card {
  opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0);
}
.support-card-img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-sm); border: 1px solid var(--border);
}
.support-card-text {
  margin-top: 9px; font-size: var(--fs-callout); line-height: 1.5;
  color: var(--text-2); text-align: center;
}
.support-card-text b { color: var(--text); font-weight: var(--fw-semibold); }

.license-chip {
  display: flex; align-items: center; gap: 6px; font-size: var(--fs-callout);
  border-radius: var(--r-pill); padding: 4px 11px; white-space: nowrap;
  font-weight: var(--fw-semibold); border: 1px solid transparent;
}
.license-chip.is-ok {
  background: var(--success-soft); color: var(--success-text);
  border-color: color-mix(in srgb, var(--success) 32%, transparent);
}
.license-chip.is-warn {
  background: var(--warning-soft); color: var(--warning-text);
  border-color: color-mix(in srgb, var(--warning) 32%, transparent);
}
.license-chip.is-bad {
  background: var(--danger-soft); color: var(--danger-text);
  border-color: color-mix(in srgb, var(--danger) 32%, transparent);
}
.user-chip {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 3px 10px 3px 3px; border-radius: var(--r-pill); border: 1px solid transparent;
}
.user-chip:hover { background: color-mix(in srgb, var(--text) 7%, transparent); }
.user-name { font-weight: var(--fw-medium); line-height: 1.2; }
.avatar {
  width: 28px; height: 28px; border-radius: 50%; flex: 0 0 28px;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6); color: #fff;
  display: grid; place-items: center; font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
}

.content { flex: 1; padding: var(--pad-page); }
.content-inner { max-width: var(--w-content); margin: 0 auto; }

/* ------------------------------------------------------------------- cards */
/* ==========================================================================
   页面骨架：page-head → hero → 卡片堆叠
   间距只使用 --gap-stack / --pad-page / --pad-card 三档，不再逐处手写
   ========================================================================== */
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-4); margin-bottom: var(--gap-stack);
}
.page-head-text { min-width: 0; }
.page-head h1 { font-size: var(--fs-display); font-weight: var(--fw-semibold); }
.page-head-sub { margin: 4px 0 0; font-size: var(--fs-callout); color: var(--text-3); }
/* 主操作集中在尾部（toolbars.md：primary action 放 trailing 侧） */
.page-head-actions { display: flex; align-items: center; gap: var(--gap-btn); flex-shrink: 0; }

/* 英雄卡：内容层，使用标准材质 + 品牌色，不用玻璃（materials.md：玻璃不进内容层） */
.hero {
  display: flex; align-items: stretch; gap: var(--sp-8);
  padding: var(--sp-6) var(--sp-8);
  margin-bottom: var(--gap-stack);
  border-radius: var(--r-lg);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--border));
  background:
    radial-gradient(620px 240px at 0% 0%, color-mix(in srgb, var(--accent) 14%, transparent) 0%, transparent 62%),
    var(--bg-elev);
  box-shadow: var(--shadow-sm);
}
.hero-body { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; }
.hero-eyebrow {
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
  color: var(--accent); letter-spacing: .4px; margin-bottom: 6px;
}
.hero-title { font-size: 19px; font-weight: var(--fw-semibold); letter-spacing: -.01em; margin: 0 0 6px; }
.hero-sub { margin: 0; font-size: var(--fs-callout); color: var(--text-2); max-width: 62ch; }
.hero-actions { display: flex; align-items: center; gap: var(--gap-btn); margin-top: var(--sp-4); flex-wrap: wrap; }
/* 指标区：每项按内容自适应宽度（介于 92px 与内容宽度之间），留足间距不拥挤 */
.hero-stats {
  display: grid; grid-auto-flow: column;
  /* 每项至少 112px，内容更长时按内容自然变宽 —— 数值位数多、标签更长都不会挤 */
  grid-auto-columns: minmax(112px, max-content);
  align-items: center; gap: 36px;
  padding-left: var(--sp-8); border-left: 1px solid var(--border);
  flex-shrink: 0;
}
.hero-stat { display: flex; flex-direction: column; justify-content: center; text-align: left; min-width: 0; }
.hero-stat b {
  font-size: 28px; font-weight: var(--fw-semibold); line-height: 1.15;
  letter-spacing: -.02em; white-space: nowrap;
}
.hero-stat span { font-size: var(--fs-footnote); color: var(--text-3); margin-top: 4px; white-space: nowrap; }
@media (max-width: 1180px) {
  .hero { flex-direction: column; gap: var(--sp-5); }
  .hero-stats {
    padding-left: 0; padding-top: var(--sp-4);
    border-left: 0; border-top: 1px solid var(--border);
    grid-auto-flow: row; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: var(--sp-4);
  }
}

/* 指标行：同一行内展示多个数值，每项按内容长度自适应宽度 */
.metric-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 12px 40px; }
.metric-cell { min-width: 0; }
.metric-cell .n {
  font-size: 28px; font-weight: var(--fw-semibold);
  letter-spacing: -.02em; line-height: 1.15; white-space: nowrap;
}
.metric-cell .l { font-size: var(--fs-footnote); color: var(--text-3); margin-top: 3px; white-space: nowrap; }
.metric-cell.is-primary .n { color: var(--accent); }

/* 卡片堆叠节奏由 .mb-16（= --gap-stack）统一控制，避免自动外边距与工具类叠加 */
.card {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-sm);
}
.card-pad { padding: var(--pad-card); }
.card-head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--pad-card-head); border-bottom: 1px solid var(--border);
}
.card-head h2, .card-head h3 { font-size: var(--fs-headline); font-weight: var(--fw-semibold); }
.card-head-actions { margin-left: auto; display: flex; gap: var(--gap-btn); align-items: center; }

/* --------------------------------------------------------------------------
   窄屏卡片头：标题与操作各占一行
   --------------------------------------------------------------------------
   390px 上「文章数据与收录趋势」+ 三个区间胶囊挤在一行：标题被挤成
   「…收录趋」+「势」（单个字掉到第二行），胶囊则折成 2+1。
   窄屏让标题独占一行、操作另起一行；胶囊不折行（横向滚动），保持一组控件的整体感。
   -------------------------------------------------------------------------- */
@media (max-width: 560px) {
  .card-head { flex-wrap: wrap; align-items: flex-start; row-gap: 8px; }
  .card-head > h2, .card-head > h3 { flex: 1 1 100%; min-width: 0; }
  .card-head-actions { margin-left: 0; flex: 1 1 100%; }
  .card-head-actions .chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .card-head-actions .chips::-webkit-scrollbar { display: none; }
}
.card-body { padding: var(--pad-card); }
/* 卡片内部刷新（切时间区间）：只让本卡片轻微淡出 + 角标转圈，
   页面骨架与其它卡片保持不动，避免整页重绘带来的跳动 */
.card-body { position: relative; }
.card-body.is-loading > * { opacity: .45; transition: opacity .18s ease; }
.card-body.is-loading::after {
  content: ''; position: absolute; top: 14px; right: 14px;
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-top-color: var(--accent);
  animation: geo-spin .7s linear infinite;
}
@keyframes geo-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .card-body.is-loading::after { animation: none; opacity: .6; }
}
/* 卡片内的次级容器：统一用底色代替手写背景色 */
.card-soft { background: var(--bg-subtle); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 14px 16px; }

.grid { display: grid; gap: var(--gap-stack); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.grid-auto { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
@media (max-width: 1180px) { .grid-4, .grid-5 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .grid-2, .grid-3 { grid-template-columns: 1fr; } }

/* ==========================================================================
   资产卡片网格（品牌资产页的 5 个页签共用）
   一行 2 列；同一行的卡片等高，以该行内容最高的那张为准（grid 默认 stretch）；
   卡片内部用 flex 纵向排布，操作按钮组 margin-top:auto 永远贴底对齐。
   ========================================================================== */
.asset-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap-stack);
  align-items: stretch;          /* 同排等高 */
}
.asset-card {
  display: flex;
  flex-direction: column;        /* 让内部可以撑开 + 贴底 */
  height: 100%;                  /* 跟随行高，短内容也拉平 */
  min-width: 0;
}
/* 正文区可增长：内容短的卡片靠它把按钮推到底部。
   padding-bottom 保证「最后一行内容 ↔ 分隔线」至少 10px，短卡片也不会贴线。 */
.asset-card-body { flex: 1 1 auto; min-width: 0; padding-bottom: 10px; }
/* 操作按钮组：永远贴在卡片底部，且自带一道分隔线，视觉上"落底"。
   分隔线上下各留 10px：上方由 .asset-card-body 的 padding-bottom 提供，
   下方由这里的 padding-top 提供。 */
.asset-card-actions {
  flex: 0 0 auto;
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: var(--gap-btn);
  flex-wrap: wrap;
}
/* 简介类文本限制行数，避免个别长文案把整行卡片拉得过高 */
.asset-card-body .clamp-3 {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
@media (max-width: 900px) { .asset-grid { grid-template-columns: 1fr; } }

/* 资产引用自检：固定 3 列 × 2 行，卡片尺寸完全一致（不随内容自适应高度），
   保证 6 张卡在视觉上整齐划一；超出部分裁掉而不是把卡片撑高。 */
.coverage-grid {
  display: grid; gap: var(--gap-group) var(--gap-stack);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr;
}
.coverage-item {
  /* 安全内边距：内容与卡片边框至少留 12px（要求 ≥10px）。
     之前写死 height:128px，而内容实测需要约 137px（标题行 + 两段各 2 行正文），
     多出来的部分被 overflow:hidden 裁掉，最后一行正好压在底边上。
     改为 min-height + 栅格行等高：内容永远不会被裁，六张卡高度依然一致。 */
  --coverage-safe: 12px;
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--coverage-safe) 14px;
  min-width: 0;
  min-height: 160px;            /* 容纳「已填写 / 附件解析 / 被引用」三行，仍留 12px 内边距 */
  display: flex; flex-direction: column;
  overflow: hidden;             /* 兜底：正常情况下不会触发裁切 */
}
.coverage-item .coverage-fill { flex: 1 1 auto; min-height: 0; }
.coverage-item .clamp-2 {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
@media (max-width: 1180px) { .coverage-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .coverage-grid { grid-template-columns: 1fr; } }

/* 可折叠区块（卡片头部即可折叠开关，默认收起） */
.collapse-toggle {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; cursor: pointer; padding: 5px var(--safe-x);
  border-radius: var(--r-pill); color: var(--text-2);
  font-size: var(--fs-callout); font-weight: var(--fw-medium);
  /*
   * 两行都不能省，否则「展开 / 收起」会被压成竖排单字。
   *
   * 卡片头部是 flex 行，头部空间紧张时 flex 项会按 flex-shrink 收缩。
   * 中文可以逐字换行 ⇒ 这个按钮的 min-content 宽度只有**一个字**那么宽，
   * 于是它会被一路压到最窄，视觉上就成了「▸ 展 / 开」上下两行 ——
   * 而且因为它同时带着 .ctl-h 的固定 height:30px，多出来的行还会溢出按钮外。
   * nowrap 让它不再可换行，flex:none 让它不再参与收缩（宁可让左侧标题变窄，
   * 也不要把按钮压变形）。左侧标题块有 min-width:0，本来就该由它先让位。
   */
  white-space: nowrap; flex: none;
}
.collapse-toggle:hover { background: var(--bg-hover); color: var(--text-1); }
.collapse-toggle .chev { display: inline-block; transition: transform .18s ease; }
.collapse-toggle .lbl { display: inline-block; }
.collapse-toggle[aria-expanded="true"] .chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .collapse-toggle .chev { transition: none; } }

/* -------------------------------------------------------------- stat cards */
.stat {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 16px 18px; box-shadow: var(--shadow-sm);
}
.stat-label { font-size: var(--fs-callout); color: var(--text-2); display: flex; align-items: center; gap: 6px; }
.stat-value { font-size: 26px; font-weight: var(--fw-semibold); letter-spacing: -.02em; margin-top: 5px; line-height: 1.2; }
/* 统计卡里放的是文字而非数字时，降到标题字号，避免大字撑破 */
.stat-value.is-text { font-size: var(--fs-title-2); letter-spacing: 0; line-height: 1.45; margin-top: 8px; }
.stat-value small { font-size: var(--fs-title-3); font-weight: var(--fw-medium); color: var(--text-3); margin-left: 3px; }

/* 关键数字（GEO 评分、套餐价格）——全站同一尺寸与字重 */
.metric-lg { font-size: 28px; font-weight: var(--fw-semibold); letter-spacing: -.02em; line-height: 1.15; color: var(--accent); }

/* 服务商图标（emoji）统一尺寸 */
.provider-icon { font-size: 20px; line-height: 1; }
.fw-semibold { font-weight: var(--fw-semibold); }

/* 说明型列表 */
.list-muted { margin: 10px 0 14px; padding-left: 18px; color: var(--text-2); }
.stat-foot { font-size: var(--fs-footnote); color: var(--text-3); margin-top: 5px; display: flex; align-items: center; gap: 6px; }
.delta-up { color: var(--up); font-weight: var(--fw-semibold); }
.delta-down { color: var(--down); font-weight: var(--fw-semibold); }

/* ----------------------------------------------------------------- buttons */
/* macOS 默认控件 28×28，最小 20×20；主按钮以底色而非描边区分层级 */
/* 按钮统一使用最大化圆角（胶囊形），与徽标/状态标签的圆角语言一致 */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font: inherit; font-size: var(--fs-body); font-weight: var(--fw-medium);
  min-height: 28px; padding: 5px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--text);
  cursor: pointer;
  transition: background .13s ease, border-color .13s ease, opacity .13s ease, transform .06s ease;
  white-space: nowrap;
}
.btn:hover:not(:disabled) { background: var(--bg-subtle); }
.btn:active:not(:disabled) { transform: scale(.985); }        /* 必须有点按反馈 */
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn-primary { background: var(--accent-solid); border-color: var(--accent-solid); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-danger { background: var(--danger-solid); border-color: var(--danger-solid); color: #fff; }
/* 悬停必须"更亮/更聚焦"，不能"更暗"。旧写法 filter: brightness(.92) 在深色模式下
   把 #a8281f 压暗成 #9a241b，几乎融进深色背景 —— 看起来像按钮被禁用了。
   改成和 .btn-primary 同一套做法：显式换一个 --danger-hover 底色。 */
.btn-danger:hover:not(:disabled) { background: var(--danger-hover); border-color: var(--danger-hover); }
.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover:not(:disabled) { background: var(--bg-subtle); }
/* 大号按钮：只用于英雄卡等单一焦点动作 */
.btn-lg { min-height: 34px; padding: 7px 20px; font-size: var(--fs-body); border-radius: var(--r-pill); }

/* 玻璃按钮：只在功能层（工具条、浮层）内使用，随所在玻璃面一起浮动 */
.btn-glass {
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter: blur(16px) saturate(1.4);
  border-color: var(--glass-border);
  color: var(--text);
}
.btn-glass:hover:not(:disabled) { background: var(--glass-fill-strong); }

/* 图标按钮：无边框圆形，命中区 28×28（macOS 默认控件尺寸） */
.btn-icon { width: 28px; height: 28px; min-height: 28px; padding: 0; border-radius: var(--r-pill); border-color: transparent; background: transparent; color: var(--text-2); }
.btn-icon:hover:not(:disabled) { background: var(--bg-subtle); color: var(--text); }

/* 列表行内的次要操作：无边框 + 语义文字色（避免每行都出现实心按钮） */
.btn-quiet { border-color: transparent; background: transparent; color: var(--accent); font-weight: var(--fw-medium); }
.btn-quiet:hover:not(:disabled) { background: var(--accent-soft); border-color: transparent; }
.btn-quiet-danger { border-color: transparent; background: transparent; color: var(--danger); font-weight: var(--fw-medium); }
.btn-quiet-danger:hover:not(:disabled) { background: var(--danger-soft); border-color: transparent; }

/* 液态玻璃按钮：半透明填充 + 背景模糊 + 发丝描边，圆角为最大值（胶囊） */
.btn-glass-accent,
.btn-glass-danger {
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  backdrop-filter: blur(14px) saturate(1.5);
  border-color: var(--glass-border);
  font-weight: var(--fw-medium);
  box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}
.btn-glass-accent { color: var(--accent); }
.btn-glass-danger { color: var(--danger); }
/* 悬停：加一层淡色底的同时把文字压深，保证在有色底上仍 ≥4.5:1
   （只加底色会让蓝字落到 4.24:1，不达标） */
.btn-glass-accent:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 14%, var(--glass-fill-strong));
  border-color: color-mix(in srgb, var(--accent) 34%, transparent);
  color: var(--accent-hover);
}
.btn-glass-danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--danger) 14%, var(--glass-fill-strong));
  border-color: color-mix(in srgb, var(--danger) 34%, transparent);
}
/* 深色外观下悬停底本身也是暗色，危险色需要提亮才够对比（4.18:1 → 5.15:1） */
@media (prefers-color-scheme: dark) {
  .btn-glass-danger:hover:not(:disabled) { color: var(--danger-text); }
}
/* 玻璃不可用时退化为普通描边按钮，保证始终可辨认 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .btn-glass-accent { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
  .btn-glass-danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
}

/* 小尺寸仍保证 ≥20px 命中区（macOS 最小值），圆角同样最大化 */
.btn-sm { min-height: 24px; padding: 3px 12px; font-size: var(--fs-callout); border-radius: var(--r-pill); }
.btn-xs { min-height: 24px; padding: 2px 10px; font-size: 12px; border-radius: var(--r-pill); }
.btn-block { width: 100%; }

/* ------------------------------------------------------------------ badges */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-footnote); font-weight: var(--fw-medium);
  padding: 2px var(--safe-x-chip); border-radius: var(--r-pill);
  background: var(--bg-subtle); color: var(--text-2); white-space: nowrap;
}
.badge-success { background: var(--success-soft); color: var(--success-text); }
.badge-warning { background: var(--warning-soft); color: var(--warning-text); }
.badge-danger  { background: var(--danger-soft);  color: var(--danger-text); }
.badge-info    { background: var(--accent-soft);  color: var(--accent); }
.badge-purple  { background: var(--purple-soft);  color: var(--purple-text); }
/* 兼容视图里直接使用的状态色名 */
.b-warn { background: var(--warning-soft); color: var(--warning-text); }

/* ------------------------------------------------------------------ tables */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
table.tbl th {
  text-align: left; font-weight: var(--fw-semibold); color: var(--text-2);
  font-size: var(--fs-footnote); padding: 10px 14px; background: var(--bg-subtle);
  white-space: nowrap; border-bottom: 1px solid var(--border);
}
table.tbl td { padding: 11px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.tbl tbody tr:hover { background: var(--bg-hover); }
table.tbl tbody tr:last-child td { border-bottom: 0; }
/* 正在执行的那一行（信源罗盘的采集计划）：一次真实提问要一两分钟，
   不给行级提示的话，用户看不出进度停在哪儿。 */
table.tbl tbody tr.is-running { background: var(--accent-soft); }
.tbl-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* 词汇表：词汇 / 来源 / 操作 = 4 : 3 : 3（固定表格布局，比例精确生效） */
.tbl-lex { table-layout: fixed; min-width: 520px; }
.tbl-lex col.c-word { width: 40%; }
.tbl-lex col.c-src  { width: 30%; }
.tbl-lex col.c-act  { width: 30%; }
/* 长词汇在列内换行，不撑破列宽也不截断 */
.tbl-lex .cell-word { word-break: break-word; }
.tbl-lex .cell-word b { font-weight: var(--fw-medium); }
/* 行首序号：作为词汇的一部分（等宽数字 + 三级文字色），不单独占一列 */
.lex-idx {
  display: inline-block; min-width: 20px;
  margin-right: 8px; color: var(--text-3);
  font-family: var(--mono); font-size: var(--fs-footnote);
  font-variant-numeric: tabular-nums;
}

/* 行内操作：同一行、等宽均分（尺寸一致，仅用样式区分层级） */
.tbl-actions-even { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.tbl-actions-even > .btn { flex: 1 1 0; min-width: 0; }

/* ---------------------------------------------- 表格操作列：多按钮定版
 * 4 个按钮 = 2×2（上：生成/编辑，下：发布/删除）
 * 5 个按钮 = 3 + 2（第二行居中）
 * 统一用 grid 定版：按钮等宽、不换行、不因文案长短而错位；
 * 上下各留 4px 内边距，保证按钮不贴住单元格边框。
 */
.tbl-actions-2x2,
.tbl-actions-3-2 {
  display: grid;
  gap: 6px;
  padding: 4px 0;
  min-width: 0;
}
.tbl-actions-2x2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tbl-actions-2x2 > .btn { width: 100%; min-width: 0; }

/* 6 栏栅格：前三个各占 2 栏成一行；第 4、5 个分别居中错开，形成"3 + 2" */
.tbl-actions-3-2 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.tbl-actions-3-2 > .btn { grid-column: span 2; width: 100%; min-width: 0; }
.tbl-actions-3-2 > .btn:nth-child(4) { grid-column: 2 / span 2; }
.tbl-actions-3-2 > .btn:nth-child(5) { grid-column: 4 / span 2; }

/* 定版按钮组里的文字居中，长文案省略而不是把按钮撑高 */
.tbl-actions-2x2 > .btn,
.tbl-actions-3-2 > .btn {
  justify-content: center; text-align: center;
  /* 密排按钮收窄左右内边距，让"登录态授权""重新授权"这类长标签也放得下 */
  padding-left: 8px; padding-right: 8px;
}
.tbl-actions-2x2 > .btn > span,
.tbl-actions-3-2 > .btn > span {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 内容列表：固定行高。
   2×2 按钮组本身高 62px（4 + 24 + 6 + 24 + 4），单元格上下内边距各 11px，
   定 88px 后按钮组上下各还有约 13px 余量，不会贴住行分隔线。 */
.tbl-content td { height: 88px; }
.tbl-content .cell-title { max-width: 340px; word-break: break-word; }

/* 内容计划卡片：选题词数量不定，限高滚动，避免把卡片撑成"高矮不一" */
.plan-topics { max-height: 96px; overflow-y: auto; }

/* ------------------------------------------------- 资料附件：上传区与列表 */
.upload-drop {
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg-subtle);
  padding: 20px 18px;
  text-align: center;
  cursor: pointer;
  transition: background .13s ease, border-color .13s ease;
}
.upload-drop:hover { background: var(--bg-hover); border-color: var(--accent-solid); }
.upload-drop.busy { cursor: progress; opacity: .85; }
.upload-drop-title { font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--text); }
.upload-bar {
  margin: 10px auto 0; max-width: 320px; height: 4px;
  background: var(--bg-elev); border-radius: var(--r-pill); overflow: hidden;
}
.upload-bar > span {
  display: block; height: 100%; width: 0;
  background: var(--accent-solid); transition: width .15s ease;
}
.upload-readers { margin-top: 12px; }
.upload-readers > summary {
  cursor: pointer; font-size: var(--fs-callout);
  font-weight: var(--fw-medium); color: var(--text-2);
}
.attach-row {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg-subtle);
}
.attach-name { word-break: break-all; }

/* ------------------------------------------------ 渠道资源：媒体目录表
 * 「刊例价」表头与下方的价格都左对齐 —— 两列同一起始位置，价格正好落在
 * 表头文字下方（表头 td 若用 .right 让价格右对齐，视觉上会错开一整列）。
 */
.tbl-media .cell-media { max-width: 340px; word-break: break-word; }
.tbl-media .cell-media .small { margin-top: 2px; }
.tbl-media td { vertical-align: top; }
.cell-price { text-align: left; white-space: nowrap; }
.cell-price .small { margin-top: 2px; font-weight: var(--fw-regular); }

/* 标题表：序号 / 标题 / 操作（操作列加宽，保证两个按钮同行且不换行） */
.tbl-title { table-layout: fixed; min-width: 640px; }
.tbl-title col.c-idx { width: 46px; }
.tbl-title col.c-act { width: 248px; }
.tbl-title td { vertical-align: middle; }
.tbl-title .cell-title { word-break: break-word; }
.tbl-title .cell-title b { font-weight: var(--fw-medium); }

/* ------------------------------------------------------------------- forms */
.field { margin-bottom: 14px; }
.field label { display: block; font-size: var(--fs-callout); font-weight: var(--fw-medium); color: var(--text-2); margin-bottom: 5px; }
.field .hint { font-size: var(--fs-footnote); color: var(--text-3); font-weight: var(--fw-regular); margin-top: 4px; }

/* 同行表单：字段、复选框、按钮底部对齐到同一条基线（输入框底边） */
.form-inline { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3); }
.form-inline .field { margin-bottom: 0; }
.form-inline .field label { white-space: nowrap; }
/* 行内复选框：与输入框等高，因此底边自然对齐 */
.check-inline {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 30px; font-size: var(--fs-callout); color: var(--text-2);
  cursor: pointer; white-space: nowrap;
}
.check-inline input[type=checkbox] { width: 15px; height: 15px; min-height: 0; padding: 0; margin: 0; accent-color: var(--accent); flex: 0 0 15px; }
/* 单行输入框 / 下拉：全局使用最大化圆角（胶囊），与按钮、标签同一圆角语言。
   注意：手机号输入框用了 type=tel，若漏在这一条选择器之外，电话框会
   走浏览器原生外观（更高、方形圆角），与同页其它输入明显不一致。
   这里把 tel / url / date / datetime-local / time 一并纳入，避免"漏网"的输入框。 */
input[type=text], input[type=password], input[type=number], input[type=email],
input[type=search], input[type=tel], input[type=url], input[type=date],
input[type=datetime-local], input[type=time], select, textarea {
  width: 100%; font: inherit; font-size: var(--fs-body); color: var(--text);
  min-height: 30px; padding: 6px var(--safe-x); border: 1px solid var(--border-strong);
  border-radius: var(--r-pill); background: var(--bg-elev); outline: none;
  transition: border-color .13s ease, box-shadow .13s ease;
}
/* 多行输入保留圆角矩形：整块胶囊会让首尾两行文字被圆角切掉，不作为选择输入框处理 */
textarea { border-radius: var(--r-lg); }

/* 同一行内混排「输入框 + 按钮 + 折叠开关」时，把三者的高度对齐到 30px。
   各控件默认最小高度本来就不同：输入框 30px、.btn 28px、.btn-sm 24px、
   .collapse-toggle 根本没有最小高度（按行高算），并排看会明显参差。
   用固定 height 而不是只给 min-height：字体度量会让输入框自然高度落在 29~31px 之间，
   只给 min-height 仍可能差 1px。全局 * { box-sizing: border-box }，故 30px 即外框高。
   本类必须放在上面的 input 规则之后：input.ctl-h 与 input[type=text] 特异度相同
   （0,1,1），靠书写顺序取胜；.btn / .btn-sm / .collapse-toggle 均为 (0,1,0)，也一并被覆盖。 */
.ctl-h, input.ctl-h { height: 30px; min-height: 30px; }
/* 下拉框：关掉原生箭头，改自绘 SVG 箭头。
   原生箭头由浏览器绘制且紧贴右边框（实测 2.5px），既无法用 padding 推开、
   也无法在深色外观下保证对比度；自绘后位置完全由 --select-arrow-inset 决定。
   注意 background-position 的定位基准是 padding-box，所以箭头右边缘
   距元素**外**边框 = inset + border-width = 13px。 */
select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: var(--select-pad-r);
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right var(--select-arrow-inset) center;
  background-size: var(--select-arrow-size) var(--select-arrow-size);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
textarea { resize: vertical; min-height: 84px; line-height: 1.65; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-actions { display: flex; gap: 9px; justify-content: flex-end; margin-top: 18px; }

/* ------------------------------------------------------------------- modal */
/* 弹窗属于功能层：遮罩做背景淡化 + 模糊，面板本身用常规玻璃变体（文字密集） */
.modal-mask {
  position: fixed; inset: 0; background: rgba(15, 23, 42, .38);
  display: grid; place-items: center; z-index: 1000; padding: var(--sp-5);
  backdrop-filter: blur(10px) saturate(1.2);
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
}
.modal {
  background: var(--glass-fill-strong);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 1px solid var(--glass-border); border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  width: 100%; max-width: 620px; max-height: 88vh; display: flex; flex-direction: column;
  overflow: hidden;
}
.modal-head { background: transparent; }
.modal-lg { max-width: 900px; }
.modal-xl { max-width: 1200px; }
.modal-head {
  display: flex; align-items: center; padding: 16px 20px; border-bottom: 1px solid var(--border);
}
.modal-head h3 { font-size: var(--fs-title-3); font-weight: var(--fw-semibold); }
.modal-close {
  margin-left: auto; display: grid; place-items: center;
  width: 28px; height: 28px;                      /* 命中区 ≥28px */
  border: 0; background: transparent; font-size: var(--fs-title-3); line-height: 1;
  cursor: pointer; color: var(--text-3);
  border-radius: var(--r-pill);                   /* 圆形，与按钮一致 */
}
.modal-close:hover { background: var(--bg-subtle); color: var(--text); }
.modal-body { padding: 20px; overflow-y: auto; }
.modal-foot {
  display: flex; gap: 9px; justify-content: flex-end;
  padding: 14px 20px; border-top: 1px solid var(--border);
}

/* -------------------------------------------------------------------- tabs */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 18px; flex-wrap: wrap; }
.tab {
  padding: 8px 14px; font-size: var(--fs-body); cursor: pointer; color: var(--text-2);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  font-weight: var(--fw-medium); white-space: nowrap;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: var(--fw-semibold); }

.chips { display: flex; gap: 7px; flex-wrap: wrap; }
.chip {
  font-size: var(--fs-callout); padding: 4px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--bg-elev);
  cursor: pointer; color: var(--text-2);
  transition: border-color .13s ease, background .13s ease, color .13s ease;
}
.chip:hover { border-color: var(--border-strong); }
.chip.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: var(--fw-semibold); }
.chip.disabled, .chip[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.chip .x { margin-left: 5px; opacity: .55; }

/* ------------------------------------------------------------ misc pieces */
.empty { text-align: center; padding: 46px 20px; color: var(--text-3); }
/* 空态图标：原来 34px 是给 emoji 用的字号，现在承载的是 .gi（1em）⇒ 尺寸不变，
   只是补上 inline-flex 居中（.gi 是 inline-block，直接放进块级 div 会左偏）。 */
.empty-icon { font-size: 34px; margin-bottom: 10px; opacity: .7; display: flex; justify-content: center; }
.empty-title { font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--text-2); margin-bottom: 5px; }

.skeleton {
  background: linear-gradient(90deg, var(--bg-subtle) 25%, var(--bg-hover) 37%, var(--bg-subtle) 63%);
  background-size: 400% 100%; animation: sk 1.3s ease infinite;
  border-radius: var(--r-xs); height: 14px;
}
@keyframes sk { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

.toast-wrap { position: fixed; top: 18px; right: 18px; z-index: 2000; display: flex; flex-direction: column; gap: 9px; }
.toast {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm); box-shadow: var(--shadow-lg);
  padding: 11px 15px; font-size: var(--fs-body); min-width: 240px; max-width: 400px;
  animation: toast-in .18s ease;
}
.toast.success { border-left-color: var(--success); }
.toast.error   { border-left-color: var(--danger); }
.toast.warning { border-left-color: var(--warning); }
@keyframes toast-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }

.pager { display: flex; align-items: center; gap: 9px; padding: 12px 16px; justify-content: flex-end; font-size: var(--fs-body); color: var(--text-2); }

.metric-mini { display: flex; align-items: baseline; gap: 5px; }
.metric-mini b { font-size: 19px; font-weight: var(--fw-semibold); }
.metric-mini span { font-size: var(--fs-callout); color: var(--text-3); }

.kv { display: grid; grid-template-columns: 108px 1fr; gap: 9px 14px; font-size: var(--fs-body); }
.kv dt { color: var(--text-2); }
.kv dd { margin: 0; font-weight: var(--fw-medium); }
/*
 * 一个 dt 对应**多行**值时（如"发布链接"：三条发布地址各占一行），
 * 行与行之间必须自己拉开距离 —— dt/dd 的 gap 只管字段之间，管不到 dd 内部。
 *
 * 实测不加这条时，三行链接的相邻间距是 0px / 0px：徽标与域名上下贴死，
 * 读起来像一整块而不是三条。用 flex+gap 而不是给每行 margin：
 * margin 会让最后一行多出一截空白，把 dd 撑高。
 */
.kv-rows { display: flex; flex-direction: column; gap: 5px; }
.kv-rows > * { min-width: 0; }   /* 长域名可收缩，不把 dd 撑出横向滚动 */

.progress { height: 6px; background: var(--bg-subtle); border-radius: var(--r-pill); overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill); transition: width .3s ease; }

.pill-list { display: flex; gap: 6px; flex-wrap: wrap; }
.pill {
  font-size: var(--fs-footnote); padding: 2px var(--safe-x-chip); border-radius: var(--r-xs);
  background: var(--bg-subtle); color: var(--text-2); border: 1px solid var(--border);
}
.pill.ai { background: var(--purple-soft); border-color: color-mix(in srgb, var(--purple) 30%, transparent); color: var(--purple-text); }

/* ------------------------------------------------------------------- login */
.login-page {
  min-height: 100%; display: grid; place-items: center; padding: 30px;
  background:
    radial-gradient(1000px 520px at 12% -10%, rgba(37, 99, 235, .16) 0%, transparent 58%),
    radial-gradient(900px 520px at 100% 108%, rgba(139, 92, 246, .14) 0%, transparent 55%),
    var(--bg);
}
.login-card {
  width: 100%; max-width: 410px; background: var(--bg-elev);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg); padding: 30px 30px 26px;
}
.login-brand { display: flex; align-items: center; gap: 11px; margin-bottom: 22px; }
.login-brand .brand-logo { width: 40px; height: 40px; flex-basis: 40px; font-size: 21px; border-radius: var(--r-sm); }
.login-title { font-size: 19px; font-weight: var(--fw-semibold); }
.login-sub { font-size: var(--fs-callout); color: var(--text-3); }
.login-hint {
  margin-top: 16px; padding: 11px 13px; background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-radius: var(--r-sm); font-size: var(--fs-callout); color: var(--accent); line-height: 1.7;
}
.login-hint code { background: var(--bg-elev); }

/* --------------------------------------------------------------- 法律文件
 * 《用户使用协议》《用户授权协议》《免责声明》—— 注册勾选框、页脚入口、
 * 阅读弹窗三处共用同一套样式（结构见 web/js/legal.js）。
 * 这里只做排版：颜色一律走 token，深色外观自动适配。
 */

/* 注册前的勾选行。文案会换行，所以复选框用 align-items:flex-start 顶到首行基线附近，
   而不是垂直居中 —— 居中会让它随着文案行数变化上下漂移。 */
.legal-agree {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 14px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg-subtle);
}
.legal-agree input[type=checkbox] {
  width: 15px; height: 15px; min-height: 0; padding: 0; margin: 2px 0 0;
  accent-color: var(--accent); flex: 0 0 15px; cursor: pointer;
}
.legal-agree-text {
  min-width: 0; font-size: var(--fs-callout); color: var(--text-2); line-height: 1.75;
}
/* 必须写成 `元素.类` / 后代选择器：表单区有一条 `.field label { display:block }`，
   裸类名会被它按"同特异度、后来者胜"覆盖（详见 .chk-pill 处的说明）。 */
.legal-agree-text label { display: inline; margin: 0; font-weight: var(--fw-regular); color: inherit; cursor: pointer; }

/* 三份文件的链接串：页脚与注册勾选框共用 */
.legal-links { display: inline; }
.legal-link { color: var(--accent); text-decoration: none; border-bottom: 1px solid transparent; }
.legal-link:hover { border-bottom-color: currentColor; }
.legal-sep { color: var(--text-3); }
.legal-ver { color: var(--text-3); font-size: var(--fs-footnote); }
.legal-warn { color: var(--warning-text); }

/* 阅读弹窗正文 */
.legal-doc { font-size: var(--fs-callout); color: var(--text-2); line-height: 1.85; }
.legal-meta {
  display: flex; flex-wrap: wrap; gap: 4px 16px;
  font-size: var(--fs-footnote); color: var(--text-3);
  padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--border);
}
.legal-p { margin: 0 0 9px; }
.legal-sec { margin-top: 18px; }
.legal-h { font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--text); margin: 0 0 9px; }
.legal-items { margin: 0 0 9px; }
.legal-list { margin: 0; padding-left: 20px; }
.legal-list li { margin-bottom: 5px; }
.legal-closing { margin-top: 18px; color: var(--text-3); }
/* 需要提请注意的条款（免除或减轻服务方责任、限制用户权利、加重用户义务、
   争议解决方式）—— 对应格式条款的提示与说明义务，段首以【重要】标记。 */
.legal-p.is-attn, .legal-list li.is-attn {
  font-weight: var(--fw-medium); color: var(--text);
  background: var(--accent-soft); border-left: 3px solid var(--accent);
  border-radius: var(--r-xs); padding: 8px 11px;
}
.legal-list li.is-attn { padding: 6px 10px; }

/* ------------------------------------------------------------------- chart */
.chart-box { position: relative; width: 100%; }
.chart-box svg { display: block; }
/* 网格与坐标文字用 token，深色外观自动适配（原先硬编码 #eef1f6 / #94a3b8，深色下不可见） */
.chart-grid { stroke: var(--border); stroke-width: 1; }
.chart-axis { fill: var(--text-3); font-size: 10px; }
.chart-guide { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.chart-dot { transition: r .12s ease; }
.chart-dot.is-hot { r: 4.4; }
/* 悬停数值提示 */
.chart-tip {
  position: absolute; transform: translate(-50%, -110%);
  pointer-events: none; z-index: 5;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--r-sm); box-shadow: var(--shadow-lg);
  padding: 7px 10px; min-width: 118px; white-space: nowrap;
}
.chart-tip-h { font-size: var(--fs-footnote); color: var(--text-3); margin-bottom: 4px; }
.chart-tip-r { display: flex; align-items: center; gap: 6px; font-size: var(--fs-callout); color: var(--text-2); }
.chart-tip-r i { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 8px; }
.chart-tip-r b { margin-left: auto; color: var(--text); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.chart-legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: var(--fs-callout); color: var(--text-2); margin-top: 10px; }
.chart-legend i { display: inline-block; width: 9px; height: 9px; border-radius: var(--r-xs); margin-right: 6px; }

.wordcloud { display: flex; flex-wrap: wrap; gap: 9px 15px; align-items: center; padding: 6px 0; }
.wordcloud span { line-height: 1.1; }

.bar-row { display: grid; grid-template-columns: 84px 1fr 52px; gap: 10px; align-items: center; font-size: var(--fs-callout); margin-bottom: 8px; }
.bar-track { height: 8px; background: var(--bg-subtle); border-radius: var(--r-pill); overflow: hidden; }
.bar-track > i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--accent); }

.agent-bubble {
  display: flex; gap: 11px; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--bg-subtle); border: 1px solid var(--border); margin-bottom: 10px;
}
/*
 * 图标底（使用引导的「开始之前三件事」与「闭环六步」、CBO 的数字员工头像）。
 *
 * 底色用**纯色**，不用渐变：渐变是写死的两个十六进制（#3b82f6→#8b5cf6），
 * 深浅两套主题下都是同一块蓝紫，和旁边的界面语言对不上；而且同一个渐变铺在
 * 30px 的小方块上，两个色相各占一半，看着像"没调好的色块"而不是一个底。
 * 改成 --accent-solid —— 它是主题令牌，深色外观会自动换成对应色，
 * 且注释里写明与白字对比度 5.17:1（浅）/ 5.04:1（深），白色图标始终看得清。
 */
.agent-avatar {
  width: 30px; height: 30px; flex: 0 0 30px; border-radius: var(--r-sm);
  display: grid; place-items: center; font-size: var(--fs-title-3);
  background: var(--accent-solid); color: #fff;
}
.agent-name { font-weight: var(--fw-semibold); font-size: var(--fs-body); }
.agent-role { font-size: var(--fs-footnote); color: var(--text-3); }
/* 老板（主控）气泡：用强调底色区分角色，不再手写十六进制 */
.agent-bubble.is-boss { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 26%, transparent); }
/*
 * 「老板」头像：改成**一个纯色深石板**（原为 #334155→#64748b 的渐变）。
 * 不能跟着主题令牌走：底色一变浅，写死的白色图标就看不清了 ——
 * 这里要的是一块**在两套主题下都足够深**的底，所以固定色号是刻意的选择。
 */
.agent-avatar.is-boss { background: #475569; }

.cbo-log { max-height: 330px; overflow-y: auto; }

/* 主列宽、侧列固定：指挥台的「下达指令 + 执行记录」是主工作区，任务/指标是旁栏 */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--gap-stack); align-items: start; }
.split > * { min-width: 0; }
@media (max-width: 1000px) { .split { grid-template-columns: 1fr; } }

/* 区块小标题：不再全大写（中文无意义），改为小字 + 字重 + 字距 */
.section-title {
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-3);
  letter-spacing: .6px; margin: 22px 0 12px;
}
.section-title:first-child { margin-top: 0; }

/* ==================== 内容模块通用块（替代内联样式）==================== */

/* 说明 / 提示块 */
.note {
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm); padding: 12px 14px;
  font-size: var(--fs-callout); color: var(--text-2); line-height: 1.7;
}
/*
 * .note 自带边框，所以它**必须**与上下相邻块拉开距离。
 *
 * 为什么单独给这一条：`.note` 默认 margin 是 0，而卡片正文里没人会记得给每个
 * 「提示块」补 mb —— 结果是提示框的边框与上一个块贴在一起（实测：
 * 使用引导的 9 张步骤卡 → 下方提示 = 0px；内容生产的口径卡也同形）。
 * 只对 .note 生效，不动其它相邻关系：CBO 任务行、FAQ 分组这类"刻意贴合成表"的
 * 兄弟节点不受影响。
 *
 * 用 margin-top 而不是 margin-bottom：垂直外边距会与上一块自带的 mb-16 折叠，
 * 不会叠加成双倍。
 */
.card-body > * + .note,
.card-body > .note + *,
.modal-body > * + .note,
.modal-body > .note + * { margin-top: var(--gap-stack); }
.note-warn { background: var(--warning-soft); border-color: color-mix(in srgb, var(--warning) 26%, transparent); border-left-color: var(--warning); color: var(--warning-text); }
.note-ok   { background: var(--success-soft); border-color: color-mix(in srgb, var(--success) 26%, transparent); border-left-color: var(--success); color: var(--success-text); }
.note-soft { background: var(--bg-subtle); border-left-color: var(--border-strong); }

/* 引用链 / 流程条 */
.flow {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0;
}
.flow > span {
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 6px 11px;
  font-size: var(--fs-callout); color: var(--text-2);
}
.flow > i { color: var(--text-3); font-style: normal; }

/* 信源行（本产品的签名元素：信源 + 分档，用于引用与溯源） */
.source-line {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 0 7px 11px; border-left: 2px solid var(--border);
}
.source-line > b { font-weight: var(--fw-medium); color: var(--text); }
.source-line > .tier { font-size: var(--fs-footnote); color: var(--text-3); }

/* 纵向堆叠 */
.stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.stack-sm { display: flex; flex-direction: column; gap: var(--sp-2); }

/* 工具条：卡片头部右侧的一组操作 */
.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* 段落分隔：替代手写 border-top + padding-top */
.section-divider { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 14px; }

/* 引用分析里的「两栏对照」（跨模型差异 / 集中度与时效）：用等边小面板包住左右两栏，
   让两列头部与内容各就各位、不再一列空着显得失衡。面板默认随 grid 等高对齐。 */
.panel-soft {
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 12px 14px;
}
.panel-soft-h { margin-bottom: 8px; }
.panel-soft-foot { margin-top: 10px; font-size: var(--fs-callout); color: var(--text-3); }
.kv-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: var(--fs-callout); padding: 3px 0;
}
.kv-row + .kv-row { border-top: 1px solid var(--border); }
.kv-row > span { color: var(--text-2); }
.kv-row > b { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }

/* 预格式化文本（正文预览、答案片段） */
.pre-block {
  white-space: pre-wrap; word-break: break-word;
  background: var(--bg-subtle); color: var(--text-2);
  border-radius: var(--r-sm); padding: 12px 14px;
  font-size: var(--fs-callout); line-height: 1.75;
}
/* AEO 证据：回答原文默认折叠时只露前 4 行，并做渐隐淡出，提示"下面还有"。 */
.pre-clamp {
  max-height: 7em; overflow: hidden; position: relative;
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}
.scroll-box {
  max-height: 300px; overflow: auto;
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: 12px;
}

/* ==================== AI 生成弹窗（内容生产）====================
   为什么单独定义：.card 只有边框/阴影/圆角，**没有内边距**（内边距在 .card-body 上）。
   把内容直接放进 .card 会贴着边框 —— 这里给自检 / FAQ 面板一个自带安全内边距的容器。 */
.gen-grid {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr) minmax(0, 340px);
  gap: 18px;
  align-items: stretch;                 /* 三列等高 */
  height: min(66vh, 600px);             /* 给明确高度，各列才会"填满"而非按内容塌缩 */
}
/* 三列需要足够宽度（modal-xl 上限 1200px）；不够宽时整列堆叠，避免把内容压变形 */
@media (max-width: 1240px) { .gen-grid { grid-template-columns: 1fr; height: auto; } }

/* 每一列：统一 14px 节奏；列内可独立滚动，三列高度对齐 */
.gen-col {
  display: flex; flex-direction: column; gap: 14px;
  min-width: 0; min-height: 0; overflow-y: auto;
}
.gen-col > .section-title { margin: 0; }
.gen-col-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 28px; }
.gen-col-title { font-size: var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-3); letter-spacing: .6px; }

/* 自带安全内边距的面板 */
.gen-panel {
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 14px 16px;
}
.gen-panel-title {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 10px;
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
  color: var(--text-3); letter-spacing: .6px;
}
.gen-panel-title .grow { margin-left: auto; }
.gen-panel-note { font-size: var(--fs-footnote); font-weight: var(--fw-regular); color: var(--text-3); }

/* 产出预览：正文容器填满列内剩余高度（标题/摘要固定，正文独立滚动） */
.gen-preview-title { font-size: var(--fs-headline); font-weight: var(--fw-semibold); line-height: 1.45; }
.gen-preview-body {
  flex: 1 1 auto; min-height: 140px;    /* 高度自适应：撑满，与左右两列对齐 */
  overflow: auto;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 12px 14px; background: var(--bg-elev);
}
/* 堆叠模式（无固定列高）下改为按内容高度 + 上限，避免单个正文区被无限拉长 */
@media (max-width: 1240px) {
  .gen-col { overflow: visible; }
  .gen-preview-body { flex: 0 1 auto; min-height: 0; max-height: 360px; }
}

/* GEO 自检清单 */
.geo-check { list-style: none; margin: 0; padding: 0; }
.geo-check > li { padding: 8px 0; border-top: 1px solid var(--border); }
.geo-check > li:first-child { border-top: 0; padding-top: 2px; }
.geo-check > li:last-child { padding-bottom: 2px; }
/*
 * 状态标记从文字符号（✓ / ✗ / ○）换成了线性图标。
 * 原来是 align-items: baseline —— 文字符号有基线，图标没有（inline-block 的基线
 * 是它的下边缘），两边一起用 baseline 对齐会让图标整体偏高一点点。
 * 两个子项都是单行，改成 center 更稳，也不会因为图标基线规则改变而漂移。
 */
.geo-check-head { display: flex; align-items: center; gap: 8px; }
.geo-check-state { flex: 0 0 auto; display: inline-flex; align-items: center; line-height: 1; font-weight: var(--fw-semibold); }
.geo-check-state.is-ok  { color: var(--success); }
.geo-check-state.is-bad { color: var(--danger); }
.geo-check-state.is-na  { color: var(--text-3); }
.geo-check-label { font-weight: var(--fw-semibold); }
.geo-check-detail { margin-top: 3px; font-size: var(--fs-footnote); color: var(--text-3); line-height: 1.6; }

/* FAQ 列表 */
.faq-list { margin: 0; padding: 0; }
.faq-item { padding: 8px 0; border-top: 1px solid var(--border); font-size: var(--fs-callout); line-height: 1.7; }
.faq-item:first-child { border-top: 0; padding-top: 2px; }
.faq-item:last-child { padding-bottom: 2px; }
.faq-q { font-weight: var(--fw-semibold); }
.faq-a { color: var(--text-2); margin-top: 3px; }
.faq-k { color: var(--text-3); font-weight: var(--fw-regular); margin-right: 5px; }

/* 写作标准说明（可折叠） */
.gen-collapse {
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 12px 14px;
}
.gen-collapse > summary { cursor: pointer; font-size: var(--fs-callout); font-weight: var(--fw-semibold); }
.gen-collapse[open] > summary { margin-bottom: 10px; }
.std-list { margin: 0; padding-left: 18px; font-size: var(--fs-footnote); color: var(--text-2); }
.std-list > li { margin-bottom: 8px; line-height: 1.65; }
.std-list > li:last-child { margin-bottom: 0; }
.std-list b { color: var(--text); }

/* 键值横排：每项按内容长度自适应宽度，长内容换行而不是被挤压 */
.meta-strip {
  display: flex; flex-wrap: wrap;
  gap: 12px 36px; align-items: flex-start;
}
.meta-strip > div { min-width: 0; }
.meta-strip dt {
  font-size: var(--fs-footnote); color: var(--text-3);
  font-weight: var(--fw-regular); white-space: nowrap; margin-bottom: 3px;
}
.meta-strip dd {
  margin: 0; font-size: var(--fs-body); font-weight: var(--fw-medium);
  color: var(--text); overflow-wrap: anywhere;
}

/* 列表行（问答列表、链接列表） */
.list-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--border);
}
.list-row:last-child { border-bottom: 0; }

/* 语义文字色：不再手写十六进制 */
.text-success { color: var(--success); }
.text-warning { color: var(--warning); }
.text-danger  { color: var(--danger); }
.text-accent  { color: var(--accent); }

/* 套餐卡 */
.plan-card { border: 1px solid var(--border); border-radius: var(--r-md); padding: 18px; background: var(--bg-elev); }
.plan-card.is-current { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.feature-row { gap: 7px; padding: 4px 0; align-items: flex-start; }

.muted { color: var(--text-3); }
.small { font-size: var(--fs-callout); }
.nowrap { white-space: nowrap; }
.row { display: flex; align-items: center; gap: 9px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
/*
 * 折行后右侧那一项仍然靠右。
 *
 * space-between 只在"一行放得下两个子项"时把第二项推到右边；
 * 一旦 flex-wrap 把它折到下一行，那一行只有一个子项，
 * space-between 就把它推回左边 —— 元信息（置信度、位置、耗时）会突然跑到
 * 左侧标签的正下方，看着像错位而不是层级。
 *
 * 只对 .row-between.wrap 生效：三项以上的同行栏位靠 space-between 分配空隙，
 * 给末项加 auto 外边距会把中间那项挤到左边去，那是另一个排版，不能一起改。
 */
.row-between.wrap > :last-child { margin-left: auto; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; }
.center { text-align: center; }
.right { text-align: right; }
.hidden { display: none; }
.mt-0 { margin-top: 0; } .mt-8 { margin-top: 8px; } .mt-12 { margin-top: 12px; }
.mt-16 { margin-top: 16px; } .mt-24 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; } .mb-8 { margin-bottom: 8px; } .mb-12 { margin-bottom: 12px; } .mb-16 { margin-bottom: 16px; }

.doc-body { line-height: 1.85; font-size: var(--fs-body); }
.doc-body h1 { font-size: 20px; margin: 0 0 14px; }
.doc-body h2 { font-size: var(--fs-title-2); margin: 20px 0 9px; }
.doc-body p { margin: 0 0 11px; color: var(--text-2); }

/* ---------------------------------- code / snippet blocks (conversion tracking) */
.code-block {
  background: #0f172a; color: #e2e8f0; border-radius: var(--r-sm); padding: 14px 16px;
  overflow-x: auto; font-family: var(--mono);
  font-size: var(--fs-footnote); line-height: 1.6; margin: 0; white-space: pre;
}
.code-block::-webkit-scrollbar { height: 8px; }
.code-block::-webkit-scrollbar-thumb { background: #475569; border-radius: var(--r-pill); }

/* ------------------------------------------------- 玻璃回退（可访问性） */
/* 不支持 backdrop-filter：退化为不透明面板，绝不出现看不清的半透明 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sidebar { background: var(--sidebar); }
  .topbar, .modal, .toast, .glass { background: var(--bg-elev); }
}
/* 系统开启「降低透明度」：直接给不透明底色（边栏保持深色实底） */
@media (prefers-reduced-transparency: reduce) {
  .sidebar { background: var(--sidebar); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .topbar, .modal, .toast, .glass {
    background: var(--bg-elev);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  .modal-mask { background: rgba(15, 23, 42, .58); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
/* 系统开启「增强对比度」：玻璃加实底与更明显的描边 */
@media (prefers-contrast: more) {
  .sidebar {
    background: var(--sidebar);
    -webkit-backdrop-filter: none; backdrop-filter: none;
    border-right-color: var(--border-strong);
  }
  .topbar, .modal, .toast, .glass {
    background: var(--bg-elev);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  .topbar { border-bottom-color: var(--border-strong); }
}

/* 通用玻璃面：仅限浮动的功能层使用 */
.glass {
  background: var(--glass-fill);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 1px solid var(--glass-border);
}

/* ------------------------------------------------------------- 动效收敛 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .skeleton { animation: none; background: var(--bg-subtle); }
  .btn:active:not(:disabled) { transform: none; }
  .progress > i { transition: none; }
}

/* ------------------------------------------------- 响应式：最窄宽度可用 */
@media (max-width: 980px) {
  .sidebar { width: 200px; flex-basis: 200px; }
  .content { padding: 16px; }
}
@media (max-width: 760px) {
  .form-row { grid-template-columns: 1fr; }
  .kv { grid-template-columns: 92px 1fr; }
  .topbar { padding: 0 14px; gap: 10px; }
}

/* ==========================================================================
   授权（SaaS 授权模式）· 子账号 · 订阅版本 · 渠道分发路径
   —— 统一按 apple-design 规范：8pt 间距节奏、四档圆角、单一主操作、
      固定高度/等高卡片、任何网格子项 min-width:0 以防长串撑破容器。
   ========================================================================== */

/* ---------------------------------------------------------- 防溢出基线 */
/* 长串（授权码 / URL / 订单号 / 模型名）必须能断行，否则会撑破卡片与表格 */
.mono, code { overflow-wrap: anywhere; }
.card, .stat, .plan-card, .sub-card, .lic-card, .mode-card, .coverage-item { min-width: 0; }
.table-wrap { overflow-x: auto; max-width: 100%; }
.grid > *, .plan-grid > *, .sub-grid > *, .mode-grid > *, .lic-list > *, .perm-items > * { min-width: 0; }

/* ------------------------------------------------------ 可折叠区块 */
.collapse {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-elev); overflow: hidden;
}
.collapse-head {
  display: flex; align-items: center; gap: var(--gap-group);
  padding: var(--pad-card-head);
}
.collapse-head-text { flex: 1 1 auto; min-width: 0; }
.collapse-title { font-size: var(--fs-title-3); font-weight: var(--fw-semibold); color: var(--text); }
.collapse-body { padding: 0 var(--pad-card) var(--pad-card); }

/* ------------------------------------------------------ 授权入口整页 */
.gate {
  min-height: 100vh; display: grid; place-items: center;
  padding: var(--sp-6) var(--sp-4); background: var(--bg);
}
.gate-card {
  width: 100%; max-width: 560px; display: flex; flex-direction: column;
  gap: var(--sp-5); padding: var(--sp-6);
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
}
.gate-brand { display: flex; align-items: center; gap: var(--sp-3); }
.gate-status {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: 14px 16px; border-radius: var(--r-md); border: 1px solid transparent;
}
.gate-status.is-ok {
  background: var(--success-soft);
  border-color: color-mix(in srgb, var(--success) 30%, transparent);
}
.gate-status.is-bad {
  background: var(--danger-soft);
  border-color: color-mix(in srgb, var(--danger) 30%, transparent);
}
.gate-status-icon { font-size: 20px; line-height: 1.25; flex: 0 0 auto; }
.gate-status-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.gate-body { display: flex; flex-direction: column; gap: var(--sp-4); }
.gate-alert {
  padding: 10px 12px; border-radius: var(--r-sm); font-size: var(--fs-callout);
  background: var(--danger-soft); color: var(--danger-text);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
}
.gate-submit { width: 100%; }
/* 公钥不一致时的修复命令块：命令要能整段复制，所以等宽 + 横向滚动而不是折行
   （折行会把 `| sed ...` 的续行断开，粘到终端就变成两条错命令）。 */
.pk-cmds { margin-top: var(--sp-2); }
.pk-cmds pre {
  margin: 0 0 var(--sp-2); padding: 10px 12px;
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm); font-size: var(--fs-footnote); color: var(--text-2);
  overflow-x: auto; white-space: pre;
}
.pk-cmds .btn { margin-right: var(--sp-2); }
.gate-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-group); flex-wrap: wrap;
}
.gate-durations { border-top: 1px solid var(--border); padding-top: var(--sp-4); }
.gate-durations-title { font-size: var(--fs-callout); color: var(--text-3); margin-bottom: var(--sp-2); }
.gate-durations-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gap-btn); }
/* 离线激活：内网客户粘贴离线授权文件 */
.gate-offline { border-top: 1px solid var(--border); padding-top: var(--sp-4); }
.gate-offline textarea {
  width: 100%; resize: vertical; font-size: 12px; line-height: 1.5;
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: 8px 10px;
  background: var(--bg-subtle); color: var(--text-1);
}
/* 升级提示：被锁定的实例也要看得到"有新版本" */
.gate-upgrade {
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: var(--sp-4); background: var(--bg-subtle);
}
.gate-upgrade-head { display: flex; align-items: baseline; gap: var(--gap-btn); flex-wrap: wrap; margin-bottom: 4px; }
.gate-duration {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 10px; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg-subtle);
}
/* 授权码输入：等宽 + 字距，方便逐位核对 */
.is-code { font-family: var(--mono); letter-spacing: .12em; text-transform: uppercase; }

/* ---------------------------------------------------- 分段控件（时长/渠道） */
.seg {
  display: inline-flex; flex-wrap: wrap; gap: 4px; padding: 3px;
  background: var(--bg-subtle); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.seg-item {
  border: 0; background: transparent; font: inherit; font-size: var(--fs-callout);
  color: var(--text-2); padding: 6px 12px; border-radius: var(--r-xs);
  cursor: pointer; white-space: nowrap;
}
.seg-item:hover { background: var(--bg-hover); }
.seg-item.active {
  background: var(--bg-elev); color: var(--text);
  font-weight: var(--fw-semibold); box-shadow: var(--shadow-sm);
}
/*
 * 胶囊变体：容器与选中项都取最大圆角（--r-pill）。
 *
 * 为什么不直接把 .seg 改成胶囊：它还有两处在用（授权页的时长/渠道选择），
 * 那些是"多选一"的表单控件，方角更贴合表单的整体语言。而「问题从哪来」
 * 是两个互斥方向之间的切换，视觉权重更高，做成胶囊才不像一个下拉框。
 *
 * 必须**同时**改写容器与子项：只改容器的话，选中态仍是 6px 方角，
 * 在胶囊容器里两端会露出尖角（padding 3px 挡不住）。
 */
.seg-pill { border-radius: var(--r-pill); }
.seg-pill .seg-item { border-radius: var(--r-pill); }

/* ------------------------------------------------------------- 提示条 */
.callout {
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px 14px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--bg-subtle);
}
.callout-success {
  background: var(--success-soft); color: var(--success-text);
  border-color: color-mix(in srgb, var(--success) 30%, transparent);
}
/* 需要运营注意但不阻断流程的提示（如离线授权文件未绑定实例指纹）。 */
.callout-warning {
  background: var(--warning-soft); color: var(--warning-text);
  border-color: color-mix(in srgb, var(--warning) 30%, transparent);
}

/* ------------------------------------------------------- 套餐三档（等高） */
.plan-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap-stack); align-items: stretch;
}
.plan-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--pad-card); border: 1px solid var(--border);
  border-radius: var(--r-md); background: var(--bg-elev);
}
.plan-card-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--gap-btn);
}
.plan-card-head h3 { margin: 0; font-size: var(--fs-title-2); min-width: 0; }
.plan-tagline { margin: 0; font-size: var(--fs-callout); color: var(--text-3); line-height: 1.55; }
.plan-price { display: flex; align-items: baseline; gap: 6px; }
.plan-price b { font-size: 26px; letter-spacing: -.02em; line-height: 1.1; }
.plan-specs {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px var(--gap-group); margin: 0;
  padding: var(--sp-3) 0;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.plan-specs > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.plan-specs dt { font-size: var(--fs-footnote); color: var(--text-3); }
.plan-specs dd { margin: 0; font-size: var(--fs-body); font-weight: var(--fw-medium); }
.plan-features { display: flex; flex-direction: column; gap: 9px; }
.plan-features-title {
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
  color: var(--text-3); letter-spacing: .04em;
}
.plan-feature { display: flex; align-items: flex-start; gap: 8px; }
.plan-check { flex: 0 0 auto; color: var(--success); font-weight: var(--fw-bold); line-height: 1.5; }
.plan-feature-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.plan-feature-text b { font-size: var(--fs-callout); font-weight: var(--fw-semibold); }
.plan-feature-text .small { line-height: 1.5; }
.plan-foot { margin-top: auto; display: flex; flex-direction: column; gap: 8px; }
@media (max-width: 1180px) { .plan-grid { grid-template-columns: 1fr; } }

/* --------------------------------------------------------- 子账号卡片 */
.sub-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--gap-stack); align-items: stretch;
}
.sub-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--pad-card); background: var(--bg-elev);
  border: 1px solid var(--border); border-radius: var(--r-md);
}
.sub-head { display: flex; align-items: center; gap: var(--sp-3); }
.sub-avatar {
  width: 36px; height: 36px; flex: 0 0 36px; border-radius: 50%;
  display: grid; place-items: center; font-weight: var(--fw-semibold);
  background: var(--accent-soft); color: var(--accent);
  overflow: hidden;   /* 上传的头像按圆裁切 */
}
.sub-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sub-head-text { flex: 1 1 auto; min-width: 0; }
.sub-name {
  font-weight: var(--fw-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sub-head-text .mono {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sub-role {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-btn); flex-wrap: wrap;
}
.sub-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.sub-actions {
  margin-top: auto; padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: var(--gap-btn);
}

/* ------------------------------------------------- 模块授权勾选矩阵 */
.perm-groups { display: flex; flex-direction: column; gap: var(--sp-4); }
.perm-group-title {
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
  color: var(--text-3); letter-spacing: .04em; margin-bottom: 8px;
}
.perm-items { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gap-btn); }
.perm-item {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg-subtle); cursor: pointer; min-width: 0;
}
.perm-item.checked { border-color: var(--accent); background: var(--accent-soft); }
.perm-item input { flex: 0 0 auto; margin-top: 2px; }
.perm-icon { flex: 0 0 auto; line-height: 1.45; }
.perm-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.perm-text b { font-size: var(--fs-callout); }
.perm-text .small { line-height: 1.45; }
@media (max-width: 900px) { .perm-items { grid-template-columns: 1fr; } }

/* --------------------------------------------------------- 授权码卡片 */
.lic-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: var(--gap-stack); align-items: start;
}
.lic-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--pad-card); background: var(--bg-elev);
  border: 1px solid var(--border); border-radius: var(--r-md);
}
.lic-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-group); flex-wrap: wrap;
}
.lic-code-wrap { display: flex; align-items: center; gap: var(--gap-btn); flex-wrap: wrap; min-width: 0; }
/*
 * 授权卡标题行右侧的「展开/收起」与「审计记录」。
 *
 * 这两颗按钮原本**没有任何样式规则** —— 作为普通 div，里面的 `<button>` 是行内块，
 * 两者之间只剩 HTML 源码里那一个空白字符，视觉上几乎贴在一起
 * （而且这个"间距"还会随换行/格式化变化，是不可控的）。
 * 间距必须由 `gap` 明确给出，用项目自己的「同一组按钮之间」令牌 --gap-btn(8px)。
 * flex: none —— 标题行的信息（授权码+手机号+到期）更长时，让位的是左侧那一块
 * （它有 min-width:0），不是这两个按钮。
 */
.lic-head-actions { display: flex; align-items: center; gap: var(--gap-btn); flex: none; }
.lic-code {
  font-family: var(--mono); font-size: var(--fs-headline); font-weight: var(--fw-semibold);
  letter-spacing: .08em; padding: 5px var(--safe-x); cursor: pointer; color: var(--text);
  background: var(--bg-subtle); border: 1px dashed var(--border-strong); border-radius: var(--r-sm);
}
.lic-code:hover { border-style: solid; border-color: var(--accent); color: var(--accent); }
.lic-meta {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px var(--gap-group); margin: 0;
}
.lic-meta > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.lic-meta dt { font-size: var(--fs-footnote); color: var(--text-3); }
.lic-meta dd {
  margin: 0; font-size: var(--fs-callout); font-weight: var(--fw-medium);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
/* 多实例：一条授权码可挂多台子系统，逐台列出（不能只显示最新那台） */
.lic-inst-list { display: flex; flex-direction: column; gap: 4px; width: 100%; min-width: 0; }
.lic-inst { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.lic-inst-name { font-weight: var(--fw-medium); }
.lic-inst-url { min-width: 0; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lic-actions {
  margin-top: auto; padding-top: var(--sp-3); border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: var(--gap-btn);
}
/* 折叠状态下：只留标题行，去掉卡片的纵向留白 */
.lic-card.is-collapsed { gap: 0; }
.lic-card.is-collapsed .lic-head { padding-bottom: 0; }
/* 折叠摘要：手机号 · 时长 · 到期 · 实例数（一行说不完就省略，不换行撑高标题） */
.lic-summary { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/*
 * 操作按钮分两行，**两个兄弟容器**（不是同一个 flex 容器里折行）：
 *   第一行 = 日常运维（延长 / 离线文件 / 推送升级 / 密码重置码）
 *   第二行 = 处置与销毁（锁定 · 解绑 · 撤销 · 删除）
 * 两个容器各自是一个 flex 行，天然上下堆叠；分隔线只画在第一行上方，
 * 第二行只留一点间距 —— 否则会出现两条横线，看着像两个独立区块。
 *
 * `margin-top` 必须给**固定值**：`.lic-actions` 原本是 `margin-top:auto`，
 * 在内容刚好填满时自由空间为 0 ⇒ 横线会紧贴上面的备注文字（实测间距 0px）。
 * 分两段留白才正确：线上方 16px（margin），线下方 12px（padding）。
 */
.lic-actions-ops { border-top: 1px solid var(--border); margin-top: var(--sp-4); }
.lic-actions-danger {
  border-top: 0;
  margin-top: 0;          /* 覆盖 .lic-actions 的 margin-top:auto，紧贴上一行 */
  padding-top: 8px;
}
.lic-created {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-group); flex-wrap: wrap; margin: var(--sp-3) 0;
  padding: 14px 16px; background: var(--bg-subtle);
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
}
.lic-created-code { font-size: var(--fs-title-2); font-weight: var(--fw-bold); letter-spacing: .12em; }
.lic-events { display: flex; flex-direction: column; }
.lic-event {
  display: flex; align-items: flex-start; gap: var(--gap-btn);
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.lic-event:last-child { border-bottom: 0; }
@media (max-width: 900px) { .lic-list { grid-template-columns: 1fr; } }

/* ----------------------------------------------- 内容分发四条路径说明 */
.mode-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap-stack); align-items: stretch;
}
.mode-card {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-4); border: 1px solid var(--border);
  border-radius: var(--r-md); background: var(--bg-subtle);
}
.mode-head { display: flex; align-items: center; gap: 8px; }
.mode-icon { flex: 0 0 auto; font-size: 17px; }
.mode-meta { display: flex; flex-direction: column; gap: 6px; margin: 0; }
.mode-meta > div {
  display: grid; grid-template-columns: 64px minmax(0, 1fr);
  gap: var(--gap-btn); align-items: start;
}
.mode-meta dt { font-size: var(--fs-footnote); color: var(--text-3); }
.mode-meta dd { margin: 0; font-size: var(--fs-callout); color: var(--text-2); line-height: 1.5; }
@media (max-width: 900px) { .mode-grid { grid-template-columns: 1fr; } }
/* --------------------------------------------- CBO 闭环六步进度卡 */
.loop-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap-stack); align-items: stretch;
}
.loop-step {
  display: flex; flex-direction: column; gap: var(--sp-2);
  min-width: 0; padding: var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-subtle);
}
.loop-step.done {
  border-color: color-mix(in srgb, var(--success) 34%, var(--border));
  background: var(--success-soft);
}
.loop-step.blocked {
  border-color: color-mix(in srgb, var(--danger) 42%, var(--border));
  background: var(--danger-soft);
}
.loop-step-head { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.loop-step-icon { flex: 0 0 auto; font-size: 17px; line-height: 1; }
.loop-step-title { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.loop-step-title b {
  font-size: var(--fs-callout);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.loop-step-head .badge { flex: 0 0 auto; white-space: nowrap; }
.loop-step-count { display: flex; align-items: baseline; gap: 6px; }
.loop-step-count .num {
  font-size: 20px; font-weight: var(--fw-semibold); letter-spacing: -.01em; line-height: 1.2;
}
.loop-step-detail { line-height: 1.5; overflow-wrap: anywhere; }
.loop-step .btn { margin-top: auto; align-self: flex-start; }

/* ------------------------------------------------ 数字员工责任岗 */
.emp-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: var(--gap-stack); align-items: stretch;
}
.emp-card {
  display: flex; flex-direction: column; gap: var(--sp-3);
  min-width: 0; padding: var(--pad-card);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-elev);
}
.emp-head { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.emp-avatar {
  width: 36px; height: 36px; flex: 0 0 36px; border-radius: 50%;
  display: grid; place-items: center; font-size: 17px; line-height: 1;
  background: var(--accent-soft);
}
.emp-head-text { flex: 1 1 auto; min-width: 0; }
.emp-head-text .small { display: block; overflow-wrap: anywhere; }
.emp-name {
  font-weight: var(--fw-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.emp-head .badge { flex: 0 0 auto; }
.emp-duty { line-height: 1.55; overflow-wrap: anywhere; }
.emp-metrics {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px var(--gap-group); margin: 0;
  padding: var(--sp-3) 0;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.emp-metrics > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.emp-metrics dt { font-size: var(--fs-footnote); color: var(--text-3); }
.emp-metrics dd { margin: 0; font-size: var(--fs-callout); font-weight: var(--fw-medium); }
.emp-modules { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.emp-modules-label { flex: 0 0 auto; }

/* 在岗人明细已从卡片上移除（成员与权限匹配改在「指派」弹窗里看），
   原先的 .emp-staff* 规则随之删除，避免留下死样式。 */
/* 岗位管理弹窗里的「编辑 / 删除」操作组（贴右） */
.emp-card-actions { display: inline-flex; gap: 6px; flex: 0 0 auto; margin-left: auto; }
/* 行内头像（指派弹窗文案旁） */
.emp-avatar-inline { display: inline-grid; vertical-align: middle; width: 22px; height: 22px; flex: none;
  font-size: 13px; margin-right: 2px; }
/* 岗位管理列表 */
.emp-manage-list { display: flex; flex-direction: column; gap: 8px; max-height: 340px; overflow-y: auto; }
.emp-manage-item {
  display: flex; align-items: center; gap: var(--sp-3); padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--bg-elev);
}
.emp-manage-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.emp-manage-text .small { overflow-wrap: anywhere; }

/* ---------------------------------------------------------------------------
 * 数字员工卡片：让"岗位信息 / 模块 / 在岗人 / 岗位操作"四块纵向分明
 *
 * 之前的问题：编辑·删除挤在标题行右侧、和岗位名抢宽度；「岗位所需模块」
 * 的标签与模块气泡混在一行容易折断；指派按钮在块首、撤下按钮在每条在岗人
 * 里，两者不在同一基线上，看着散。
 *
 * 现在：模块标签独占一行、气泡进一个胶囊底托；指派与撤下合成一组放块首右侧
 * （指派在前）；编辑·删除移到卡片最底部（贴底），与"岗位"主体的区分更清楚。
 * ------------------------------------------------------------------------- */

/* 头像：上传的图片按圆形裁切；emoji / 图标走原有字号继承 */
.emp-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }

/* 岗位所需模块：标签一行，模块**直接**以胶囊按钮列出（不再套底托容器） */
.emp-modules { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.emp-modules-label { flex: 0 0 auto; }
.emp-modules-pills { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
/* 模块名做成"最大化圆角"的胶囊按钮（.pill 全局是 6px 圆角，这里按要求拉满） */
.emp-modules-pills .pill { border-radius: var(--r-pill); }

/*
 * 岗位操作条：贴底，一行两组 —— 左=指派/撤下，右=编辑/删除。
 *
 * 两个细节都是踩出来的：
 *   · 不能再给右组 `margin-left:auto`：它和 `justify-content:space-between`
 *     叠加后会抢走整行剩余空间，导致两组**各自换行**（实测两组 top 相差 35px）。
 *     只用 space-between 就够了。
 *   · `flex-wrap: nowrap` + 按钮不换行：卡片在 grid 里可能只有 ~250px 宽，
 *     允许换行就会把"一行两组"变成"两行"，与设计要求不符；
 *     宁可让按钮靠间距压缩，也不拆行。
 */
.emp-card-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-btn); flex-wrap: nowrap;
  margin-top: auto; padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
.emp-foot-left, .emp-foot-right { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.emp-card-foot .btn { white-space: nowrap; }

/* --------------------------------------------------- 我已同意的协议（系统设置）
 * 每条记录一行：类型 + 时间 + 版本 + IP。用左侧细线分组，不用卡片套卡片 ——
 * 这一块通常只有 1–3 条，再过一层容器会显得空。
 */
.legal-rec {
  padding: 10px 0 10px var(--sp-3);
  border-left: 2px solid var(--border);
  margin-bottom: 8px;
}
.legal-rec:last-child { margin-bottom: 0; }
.legal-open { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ------------------------------------------------------------ 使用引导 */
/* 模块地图里的模块卡：整块可点；未授权的灰显且不可点 */
.guide-mod { display: block; text-decoration: none; color: inherit; }
.guide-mod:hover { border-color: var(--accent); }
.guide-mod.is-off { opacity: .55; cursor: default; }
.guide-mod.is-off:hover { border-color: var(--border); }
/* 「什么时候用」提示：给闭环六步之外的模块补一句"何时该点它" */
.guide-when {
  margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border);
  color: var(--text-2); line-height: 1.6;
}
.guide-when-k {
  /* 水平内边距 8px：徽标类（有底色的小 pill）的安全距离标准是 ≥8px，
     7px 低于该标准 —— 不是要求苛刻，是文字确实离底色边太近。 */
  display: inline-block; margin-right: 6px; padding: 1px 8px;
  border-radius: var(--r-pill); background: var(--accent-soft); color: var(--accent);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold); white-space: nowrap;
}
.guide-faq { padding: 10px 0; border-top: 1px solid var(--border); }
.guide-faq:first-child { border-top: 0; padding-top: 0; }

/* ------------------------------------------------------------ 上手顺序（9 步）
 *
 * 为什么这一段样式单独成组：它要同时出现在三处（顶栏胶囊、英雄卡顺序链、
 * 引导页步骤卡），但表达的是**同一个东西的三种密度**。三处各写一套颜色，
 * 就会出现"顶栏说进行中、卡片说未开始"这种观感上的自相矛盾。
 * 所以三态（done / current / todo）的视觉只在这里定义一次：
 *   done    = 绿（已完成，用 success 语义色）
 *   current = 品牌色 + 实心序号（正在做这一步）
 *   todo    = 虚线 + 灰（还没轮到；不用降透明度 —— 11px 的字再压透明度就不够读了）
 */

/* 顶栏入口：未读时是提醒（琥珀），已读未完成时是中性的进度胶囊，做完即消失 */
.guide-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px var(--safe-x-chip); border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--bg-elev);
  color: var(--text-2); font-size: var(--fs-callout); font-weight: var(--fw-medium);
  white-space: nowrap; text-decoration: none;
  /* 顶栏变窄时**由它收缩**（文字截断），不要挤旁边的圆形按钮 */
  flex: 0 1 auto; min-width: 0;
  transition: border-color .15s ease, color .15s ease;
}
/*
 * 顶栏入口：**不参与收缩**（`flex: none`）。
 *
 * 曾经写成 `flex: 0 1 auto; min-width: 0`，想让它"变窄时自己缩"，
 * 结果胶囊被压到 80px，内容（图标 + 文字）溢出被裁，
 * 文字墨迹距边框只剩 4.7px —— 低于徽标类 8px 的安全距离。
 * 这个胶囊的长度是固定的（「先看使用引导」/「引导 2/9」），没有收缩的必要：
 * 顶栏里该让步的是空白与弹性间距，不是有文字的胶囊。
 */
.guide-chip { flex: none; }
.guide-chip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.guide-chip.is-todo {
  border-color: color-mix(in srgb, var(--warning) 38%, var(--border));
  background: var(--warning-soft); color: var(--warning-text);
}
.guide-chip.is-todo:hover { border-color: var(--warning); color: var(--warning-text); }
.guide-chip .gi { flex: none; }

/* 首次进入的提示带：只在"还没看过引导"时出现，标记已读后永久消失 */
.guide-first {
  display: flex; align-items: flex-start; gap: var(--sp-5); flex-wrap: wrap;
  padding: var(--sp-5) var(--sp-6); margin-bottom: var(--gap-stack);
  border-radius: var(--r-lg); border: 1px solid color-mix(in srgb, var(--warning) 32%, var(--border));
  background: var(--warning-soft);
}
.guide-first-main { flex: 1 1 420px; min-width: 0; }
.guide-first-main > b { font-size: var(--fs-title-2); color: var(--warning-text); }
.guide-first-main p { margin: 6px 0 var(--sp-2); font-size: var(--fs-body); color: var(--text-2); line-height: 1.7; }
.guide-first-meta {
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  font-size: var(--fs-callout); color: var(--text-2); font-variant-numeric: tabular-nums;
}
.guide-first-actions { display: flex; align-items: center; gap: var(--gap-btn); flex-wrap: wrap; padding-top: 2px; }

/* 英雄卡里的顺序链：9 个小胶囊，回答"我现在在第几步" */
.hero-order {
  margin-top: var(--sp-4); padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md); border: 1px solid var(--border); background: var(--bg-subtle);
}
.hero-order-head { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-2); }
.hero-order-head b { font-size: var(--fs-headline); }
.hero-order-head span { font-size: var(--fs-callout); color: var(--text-2); }
.hero-order-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.hero-order-list li {
  display: inline-flex; align-items: center; gap: 6px;
  /* 左右都要 ≥8px（徽标类安全距离）：序号圆是底色块、不是文字，
     但圆圈左边到胶囊边缘仍要留够距离，否则看着像"贴边"。
     原来的 3px 是把它当"内嵌头像"处理的 —— 实测不达标（3px）。 */
  padding: 4px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--bg-elev);
  color: var(--text-2); font-size: var(--fs-callout); white-space: nowrap;
}
.hero-order-list i {
  flex: none; display: inline-grid; place-items: center; width: 20px; height: 20px;
  border-radius: 50%; background: var(--bg-subtle); color: var(--text-3);
  font-style: normal; font-size: 10px; font-weight: var(--fw-semibold);
}
.hero-order-list li.is-done {
  border-color: color-mix(in srgb, var(--success) 32%, var(--border));
  background: var(--success-soft); color: var(--success-text);
}
.hero-order-list li.is-done i { background: var(--success); color: #fff; }
.hero-order-list li.is-current {
  border-color: var(--accent); color: var(--accent); font-weight: var(--fw-semibold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 16%, transparent);
}
.hero-order-list li.is-current i { background: var(--accent); color: #fff; }
.hero-order-list li.is-todo { border-style: dashed; color: var(--text-3); }
.hero-order-note { margin: var(--sp-2) 0 0; font-size: var(--fs-footnote); color: var(--text-2); line-height: 1.65; }

/* 引导页的 9 张步骤卡：一行一步，四栏信息（在哪填 / 当前情况 / 为什么排这里 / 判定标准） */
.guide-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.guide-step {
  display: grid; grid-template-columns: 28px minmax(0, 1fr) auto;
  gap: var(--sp-3); align-items: start;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-elev);
  transition: border-color .15s ease, background .15s ease;
}
.guide-step.is-current { border-color: var(--accent); background: var(--accent-soft); }
.guide-step.is-todo { border-style: dashed; }
.gs-n {
  display: grid; place-items: center; width: 28px; height: 28px;
  border-radius: 50%; border: 1px solid var(--border); background: var(--bg-subtle);
  color: var(--text-2); font-size: var(--fs-callout); font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}
.guide-step.is-done .gs-n { background: var(--success); color: #fff; border-color: transparent; }
.guide-step.is-current .gs-n { background: var(--accent); color: #fff; border-color: transparent; }
.gs-body { min-width: 0; }
.gs-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.gs-head b { font-size: var(--fs-title-3); }
.gs-state {
  padding: 1px var(--safe-x-chip); border-radius: var(--r-pill);
  background: var(--bg-subtle); color: var(--text-2);
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
}
.guide-step.is-done .gs-state { background: var(--success-soft); color: var(--success-text); }
.guide-step.is-current .gs-state { background: var(--accent); color: #fff; }
.gs-where { margin-top: 2px; font-size: var(--fs-footnote); color: var(--text-3); }
.gs-detail { margin-top: 6px; font-size: var(--fs-body); color: var(--text); font-weight: var(--fw-medium); }
.gs-why, .gs-measure { margin-top: 6px; font-size: var(--fs-callout); color: var(--text-2); line-height: 1.65; }
.gs-k {
  display: inline-block; margin-right: 6px; padding: 1px var(--safe-x-chip);
  border-radius: var(--r-pill); background: var(--bg-subtle); color: var(--text-2);
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold); white-space: nowrap;
}
.guide-step.is-current .gs-k { background: var(--bg-elev); }
.gs-go { align-self: start; white-space: nowrap; }
.guide-restart { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-4); }

/* ------------------------------------------------------- 品牌统一口径（内容生产）
 *
 * 一个"生成 + 缺口清单"的小卡：左边说清这条要求是什么，右边一个按钮。
 * 缺口清单不是错误提示 —— 它是"还能补强哪里"，所以用中性底、不用警告色：
 * 用警告色会让"只差禁用词"看起来像坏了。
 */
/*
 * 品牌统一口径卡：正文顶部要让开卡片头那条分隔线。
 *
 * 这里曾经写 `padding-top: 0`，理由是"头部已经有内边距，别重复"——
 * 但卡片头与正文之间的横线（.card-head 的 border-bottom）必须与下方文字留出间距，
 * 否则第一行"已生成 / 还没有生成"直接贴在线上（实测间距 0px）。
 * 用 --sp-4 而不是整档 --pad-card：这张卡的正文比常规卡密一点，但仍然**不是 0**。
 */
.voice-card .card-body { padding-top: var(--sp-4); }
.voice-state { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.voice-missing {
  margin-bottom: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  border: 1px dashed var(--border); border-radius: var(--r-md); background: var(--bg-subtle);
}
.voice-missing ul { margin: var(--sp-2) 0 0; padding-left: 18px; }
.voice-missing li { margin-bottom: 4px; line-height: 1.6; }

/* ------------------------------------------------------- 模型验证失败的诊断
 *
 * 这一块的存在理由：toast 三秒就消失，而排障要抄的是一个完整 URL 与两行响应头。
 * "本地通过、服务器上 404"这种问题，用户需要把地址带到服务器上复跑 ——
 * 所以证据必须留在页面上，并且能一键复制。
 */
.verify-diag {
  margin: 0 0 var(--sp-2); padding: var(--sp-3);
  border: 1px solid color-mix(in srgb, var(--danger) 26%, var(--border));
  border-radius: var(--r-md); background: var(--danger-soft);
}
.vd-head { display: flex; align-items: center; gap: var(--sp-2); }
.vd-toggle {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0; cursor: pointer; text-align: left; color: var(--danger-text);
}
.vd-toggle:hover { color: var(--danger); }
.vd-chev { flex: none; display: inline-block; transition: transform .18s ease; color: var(--danger-text); }
.vd-toggle[aria-expanded="true"] .vd-chev { transform: rotate(90deg); }
.vd-body { margin-top: var(--sp-2); }
.vd-kv { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 4px var(--sp-2); margin: 0; }
.vd-kv dt { font-size: var(--fs-footnote); color: var(--text-2); }
.vd-kv dd { margin: 0; font-size: var(--fs-footnote); color: var(--text); word-break: break-all; overflow-wrap: anywhere; }
/* 窄屏：字段名与值改上下排（两列在 375px 下会把 URL 挤成一列像素） */
@media (max-width: 560px) {
  .vd-kv { grid-template-columns: 1fr; gap: 2px; }
  .vd-kv dt { margin-top: 6px; }
  .vd-kv dt:first-of-type { margin-top: 0; }
}
.vd-body-text { max-height: 72px; overflow: auto; }
.vd-probe {
  margin-top: var(--sp-2); padding: var(--sp-2) var(--safe-x);
  border-radius: var(--r-sm); background: var(--bg-elev); border: 1px solid var(--border);
}
/* 结论行用强调色：这一段是"该怎么做"，不能和上面的原始字段混在一起读 */
.vd-verdict { margin-top: 4px; padding-top: 4px; border-top: 1px dashed var(--border); color: var(--text); }
.vd-curl { margin-top: var(--sp-2); }
.vd-curl pre {
  margin: 4px 0 0; padding: var(--sp-2) var(--safe-x); border-radius: var(--r-sm);
  background: var(--bg-elev); border: 1px solid var(--border);
  font-size: var(--fs-footnote); white-space: pre-wrap; word-break: break-all;
}
@media (prefers-reduced-motion: reduce) { .vd-chev { transition: none; } }
@media (max-width: 760px) {
  /* 窄屏：按钮换到第二行，不挤在说明文字右侧 */
  .guide-step { grid-template-columns: 24px minmax(0, 1fr); }
  .gs-n { width: 24px; height: 24px; }
  .gs-go { grid-column: 2; justify-self: start; }
  .guide-first { padding: var(--sp-4); }
}

/*
 * 常见问题：按模块分组，默认全部收起。
 * 头部用 button 而不是 div —— 键盘可聚焦、可回车展开，且 aria-expanded 能被读屏识别。
 */
.faq-group { border-top: 1px solid var(--border); }
.faq-group:first-child { border-top: 0; }
.faq-head {
  width: 100%; display: flex; align-items: center; gap: 8px;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: 12px 0; color: var(--text); font-size: var(--fs-body);
  /* 同 .collapse-toggle：中文可逐字换行 ⇒ 不给 nowrap 会被压成竖排单字 */
  white-space: nowrap;
}
.faq-head:hover { color: var(--accent); }
.faq-head .badge { margin-left: auto; flex: none; }
.faq-chev {
  flex: none; display: inline-block; color: var(--text-3);
  transition: transform .18s ease;
}
.faq-head[aria-expanded="true"] .faq-chev { transform: rotate(90deg); }
/* 分组图标：承载的是 .gi（inline-block）。inline-flex + center 让它和分组名垂直居中，
   不依赖图标基线（图标没有文字基线，靠 inline 默认对齐会偏高）。 */
.faq-icon { flex: none; display: inline-flex; align-items: center; color: var(--text-3); }
.faq-body { padding: 0 0 12px 20px; }
@media (prefers-reduced-motion: reduce) { .faq-chev { transition: none; } }

/* ------------------------------------------------------------ 头像选择器 */
.avatar-picker {
  position: relative; flex: 0 0 auto; border-radius: 50%; overflow: hidden;
  background: var(--accent-soft); border: 1px solid var(--border);
  display: grid; place-items: center; cursor: pointer;
}
.avatar-picker img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar-picker-fallback { font-size: 20px; line-height: 1; color: var(--text-2); }
.avatar-picker-icon { font-size: 22px; color: var(--text-2); }
/* 覆盖层：整块可点，上传中/悬停给一句提示 */
/* 注意特异度：必须写 `label.avatar-picker-hit`，否则会被表单区的
   `.field label { display:block }` 覆盖（详见 .chk-pill 处的说明）。 */
label.avatar-picker-hit {
  position: absolute; inset: 0; display: grid; place-items: center; cursor: pointer;
  background: rgba(15, 23, 42, .48); opacity: 0; transition: opacity .13s ease;
}
.avatar-picker:hover label.avatar-picker-hit, label.avatar-picker-hit:focus-within { opacity: 1; }
label.avatar-picker-hit input[type=file] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.avatar-picker-hint { font-size: 11px; line-height: 1; color: #fff; font-weight: 600; pointer-events: none; }

/* 「按钮式」变体：不靠 hover 覆盖层，右侧直接给可点的上传 / 移除按钮。
   用于表单里（hover 提示在小尺寸下本来就不好点，也不够明确）。 */
.avatar-field-wrap { display: flex; align-items: center; gap: var(--sp-3); }
.avatar-picker-actions { display: inline-flex; align-items: center; gap: 8px; }
/* 按钮式上传：把 file input 藏起来，用 <label class="btn"> 当触发器 */
.avatar-picker-actions label.btn { position: relative; margin: 0; }
.avatar-picker-actions label.btn input[type=file] {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
  min-height: 0; padding: 0; border: 0; margin: 0;
}
.avatar-picker-actions .disabled, .avatar-picker-actions .disabled input { cursor: not-allowed; opacity: .6; }

.avatar-picker-clear {
  position: absolute; top: -2px; right: -2px; width: 18px; height: 18px; line-height: 1;
  border-radius: 50%; border: 1px solid var(--border); background: var(--bg-elev);
  color: var(--text-3); font-size: 13px; cursor: pointer; display: grid; place-items: center;
}
.avatar-picker-clear:hover { color: var(--danger); border-color: var(--danger); }
.avatar-field { display: flex; align-items: center; gap: var(--sp-3); }
.avatar-field-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* 子账号列表里的头像（与 CBO 同一套观感） */
.sub-cell { display: flex; align-items: center; gap: 9px; min-width: 0; }

/* 多选药丸（模块 / 指标勾选） */
.chk-pills { gap: 7px; }
/*
 * ⚠ 这两条必须用 **`元素.类`** 的写法（而不是裸类名）。
 *
 * 表单区有一条 `.field label { display: block }`，它的特异度是 (0,1,1)，
 * 比单类名 (0,1,0) 高 —— 于是 `<label class="chk-pill">` / `<label class="avatar-picker-hit">`
 * 的 `display:inline-flex / grid` 会被**静默覆盖成 block**：
 *   · 复选框变成 inline-block 走 baseline 对齐 ⇒ 在胶囊里偏上 2.6px；
 *   · 头像 hover 覆盖层的 place-items:center 彻底失效 ⇒ "上传"两个字跑到左上角。
 * 两条症状同一个根因。写成 `label.类名` 后特异度同为 (0,1,1)，
 * 且本文件在表单区之后，按"后来者胜"正常生效。
 */
label.chk-pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px; cursor: pointer;
  padding: 7px 13px; border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--bg-elev); font-size: var(--fs-callout); color: var(--text-2);
  line-height: 1;
}
.chk-pill:hover { border-color: var(--accent); }
label.chk-pill input[type=checkbox] {
  width: 14px; height: 14px; min-height: 0; margin: 0; padding: 0;
  accent-color: var(--accent); flex: 0 0 14px; align-self: center;
}
/* 表单内的小标题（如"修改密码（可选）"） */
.divider-label {
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold); color: var(--text-3);
  letter-spacing: .04em; margin: 14px 0 8px; padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* 指派弹窗里的候选列表 */
.assign-list { display: flex; flex-direction: column; gap: 8px; max-height: 320px; overflow-y: auto; }
.assign-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--gap-group);
  padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg-elev);
}
.assign-item-text { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; min-width: 0; }
.assign-item-text .small { flex: 1 1 100%; }

/* ---------------------------------------------- 任务状态行（旁栏） */
.cbo-task-row {
  align-items: flex-start;
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.cbo-task-row:last-child { border-bottom: 0; padding-bottom: 0; }
.cbo-task-row .badge { flex: 0 0 auto; white-space: nowrap; }
.cbo-task-text {
  line-height: 1.5; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 失败任务的「为什么失败」：最多两行，避免长文案撑破卡片 */
.cbo-task-error {
  margin-top: 2px; line-height: 1.45; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 报告数据溯源条：一眼看出"有实测依据 / 无实测依据 / 模拟引擎" */
.prov-card { border-left: 3px solid var(--border); }
.prov-card.prov-ok   { border-left-color: var(--success); }
.prov-card.prov-warn { border-left-color: var(--warning, #d97706); }

/* 增长闭环引用链路：每一跳一行，标出"有真实连边 / 还是空的" */
.chain-list { display: flex; flex-direction: column; }
.chain-hop { padding: 10px 0; border-bottom: 1px solid var(--border); }
.chain-hop:last-child { border-bottom: 0; padding-bottom: 0; }
.chain-hop-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.chain-hop-head .chain-go { margin-left: auto; flex: 0 0 auto; }
.chain-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: var(--border); }
.chain-dot.ok { background: var(--success); }
.chain-dot.warn { background: var(--warning, #d97706); }
.chain-note { margin-top: 5px; line-height: 1.55; padding-left: 16px; }

/* 系统升级：本地包列表 + 升级流程说明 */
.upgrade-pkg {
  padding: 9px 0; border-bottom: 1px solid var(--border); align-items: center;
}
.upgrade-pkg:last-of-type { border-bottom: 0; }
.upgrade-flow { margin: 0; padding-left: 18px; }
.upgrade-flow li { line-height: 1.7; margin-bottom: 8px; }
.upgrade-flow li:last-child { margin-bottom: 0; }

@media (max-width: 1180px) {
  .loop-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .plan-specs, .lic-meta, .emp-metrics { grid-template-columns: 1fr; }
  .gate-durations-grid { grid-template-columns: 1fr; }
  .loop-grid { grid-template-columns: 1fr; }
}

/* ====================================================== 卡片栅格 / 贴底动作 / 指引块 */
/* 一行 3 列（服务商目录、我的模型接口）；窄屏降级为 2 列、1 列 */
.grid-cards-3 { display: grid; gap: var(--gap-stack); grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1180px) { .grid-cards-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .grid-cards-3 { grid-template-columns: 1fr; } }

/* 卡片内纵向布局：让动作区能被 margin-top:auto 推到贴底 */
.card-fill { display: flex; flex-direction: column; }
.card-foot { margin-top: auto; padding-top: 12px; }

/* 纵向堆叠节奏 */
.stack-16 { display: flex; flex-direction: column; gap: var(--gap-stack); }
.stack-14 { display: flex; flex-direction: column; gap: 14px; }

/* 服务商/模型卡片内的描述与提示：等高分栏，避免长短文案把按钮顶乱 */
.provider-desc { margin: 0 0 10px; min-height: 38px; line-height: 1.55; overflow-wrap: anywhere; }
.provider-hint { margin: 0 0 4px; line-height: 1.5; overflow-wrap: anywhere; }

/* 对接说明块（投放设置折叠内容） */
.guide-block { margin-top: 10px; }
.guide-list { margin: 4px 0 0 18px; padding: 0; }
.guide-list li { line-height: 1.6; overflow-wrap: anywhere; }
.guide-err { margin-top: 4px; line-height: 1.6; overflow-wrap: anywhere; }
.guide-arrow { margin: 0 6px; color: var(--text-3); }

/* ============================================================================
 * 信源透镜（views/source-lens.js）
 *
 * 只加这一块：七步轨道 + 正文高亮 + 模块热度条。
 * 颜色一律取自令牌，深浅两种外观由令牌自己切换 —— 这里不写死任何色值，
 * 否则深色模式下会出现"浅底浅字"的不可读区块。
 * ========================================================================== */

/* ---- 七步轨道 ---- */
.lens-steps {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--sp-2);
}
.lens-step {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-width: 0; padding: 10px 12px; text-align: left;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--r-sm); cursor: pointer; font: inherit;
  transition: border-color .16s ease, background .16s ease, box-shadow .16s ease;
}
.lens-step:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.lens-step.is-active {
  background: var(--accent-soft); border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.lens-step b {
  font-size: var(--fs-callout); font-weight: var(--fw-semibold);
  color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}
.lens-step.is-active b { color: var(--accent); }
.lens-step-n {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-footnote); font-weight: var(--fw-semibold);
  color: var(--text-3); font-variant-numeric: tabular-nums;
}
.lens-step-v {
  font-size: var(--fs-footnote); color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
/* 状态点：绿=拿到了 / 黄=平台不给或没有 / 红=这一步断掉 */
.lens-dot {
  width: 6px; height: 6px; border-radius: 50%; display: inline-block;
  background: var(--text-3);
}
.lens-dot.ok   { background: var(--success); }
.lens-dot.warn { background: var(--warning); }
.lens-dot.bad  { background: var(--danger); }

/* ---- 答案全文：命中处就地染色 ---- */
.lens-answer {
  font-size: var(--fs-body); line-height: 1.9; color: var(--text);
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 14px 16px;
  max-height: 560px; overflow: auto; overflow-wrap: anywhere;
  white-space: normal;
}
.lens-hl {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: inherit; border-radius: 3px; padding: 1px 2px;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
.lens-hl.k-verbatim { background: color-mix(in srgb, var(--success) 22%, transparent); }
.lens-hl.k-rewrite  { background: color-mix(in srgb, var(--accent) 20%, transparent); }
.lens-hl.k-data     { background: color-mix(in srgb, var(--warning) 26%, transparent); }
.lens-hl.k-claim    { background: color-mix(in srgb, var(--purple) 22%, transparent); }

/* ---- 模块热度条：纯色、同一令牌（不做渐变） ---- */
.lens-bar {
  height: 6px; border-radius: var(--r-pill);
  background: var(--bg-subtle); overflow: hidden;
}
.lens-bar > i {
  display: block; height: 100%; border-radius: var(--r-pill);
  background: var(--accent);
  transition: width .3s ease;
}

/* 窄屏：七列挤不下就折行，不横向滚动（横滚会把第 7 步藏到屏幕外） */
@media (max-width: 1180px) { .lens-steps { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .lens-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 420px)  { .lens-steps { grid-template-columns: 1fr; } }

/* ---- 正文里的轻量 markdown 还原（见 source-lens.js 的 mdLite）----
   引擎返回的是 markdown，直接显示会满屏 `##` 与 `**`。这里只还原四种记号，
   全部按"块级行内元素"处理（display:block 的 span），不动高亮所在的字符区间。 */
.lens-answer .md-h {
  display: block; font-weight: var(--fw-semibold); color: var(--text);
  margin: 14px 0 4px; line-height: 1.6;
}
.lens-answer .md-h:first-child { margin-top: 0; }
.lens-answer .md-li { display: block; padding-left: 2px; }
.lens-answer .md-hr {
  display: block; height: 1px; background: var(--border); margin: 12px 0;
}
.lens-answer b { font-weight: var(--fw-semibold); color: var(--text); }

/* ---- 句式体检建议条 ----
   建议句要能一眼扫完、又要能长句折行，所以用等宽 + 允许断词，
   不用 nowrap（长建议句会把整张卡撑出横向滚动）。

   底色用 --bg-elev 而不是 --bg-subtle：它就贴在 .note-soft（bg-subtle）里面，
   同色等于没有色块 —— 建议句会整个消失在提示框的背景里。 */
.lens-rewrite-to {
  display: inline-block; max-width: 100%;
  padding: 2px 8px; border-radius: var(--r-sm);
  background: var(--bg-elev); border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--mono); font-size: var(--fs-footnote);
  overflow-wrap: anywhere;
}

/* 深色下高亮要更实一点：同一个 20% 透明度的色块，压在深底上几乎看不出来，
   而"哪一句被摘走了"正是这一步唯一要看的东西。 */
@media (prefers-color-scheme: dark) {
  .lens-hl            { background: color-mix(in srgb, var(--accent) 34%, transparent); }
  .lens-hl.k-verbatim { background: color-mix(in srgb, var(--success) 42%, transparent); }
  .lens-hl.k-rewrite  { background: color-mix(in srgb, var(--accent) 36%, transparent); }
  .lens-hl.k-data     { background: color-mix(in srgb, var(--warning) 42%, transparent); }
  .lens-hl.k-claim    { background: color-mix(in srgb, var(--purple) 40%, transparent); }
}

/* ---- 段落清单里的正文格 ----
   这张表有几十行，正文格若不限行高，每行会折成四五行，整张表拉到几千像素，
   反而看不清"哪一段被命中"。限两行 + 悬停出全文，扫读与细节两头都不耽误。 */
.lens-cell-text {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2;
  overflow: hidden; line-height: 1.65;
}

/* 段落清单有几十行：限高 + 滚动。
   不限的话，一次观测就能把整页拉到几千像素，用户要滚很久才够到第 6、7 步。
   与系统里 .cbo-log / .assign-list 的做法一致。 */
.lens-table-scroll { max-height: 420px; overflow: auto; }

/* 减少动效：轨道与热度条的过渡对理解内容没有帮助，
   尊重系统级「减弱动态效果」偏好。 */
@media (prefers-reduced-motion: reduce) {
  .lens-step { transition: none; }
  .lens-bar > i { transition: none; }
}
