/* DoctorAgent 后台样式。
   移动端优先——医生本人每天用手机看，桌面是运维用的。

   ## 配色来自 RssHub 后台的默认紫

   `~/Projects/RssHub/app/web_static/admin.css` 的 `:root`（Ant Design purple-6
   #722ed1 + 淡紫底 #f7f5fb）。那边有 6 套备选色（geek-blue / polar-green /
   cyan / lime / french-magenta / sunset-orange）加默认紫共七套，
   这里取的是默认那套。**只搬颜色，不搬代码**——两套系统零耦合（约定 1）。

   ## 装饰和数据分开两套颜色

   `--series-1/--good/--warning/--critical/--deemph` 是**数据色**，图表里的
   每根线每个块都从它们来。界面装饰（导航胶囊、按钮、聚焦环）走 `--accent-*`。
   分开的意义是：改装饰永远不会悄悄改掉图表的可读性。

   ⚠️ 改数据色**必须重跑 CVD 校验**，不能凭眼睛：
     node <dataviz skill>/scripts/validate_palette.js \
          "#722ed1,#8f7da3,#d9483b" --mode light --surface "#ffffff"
   这三个是层级配比条 A/B/C 三段并置的实际取值，当前结果：
   浅色 CVD ΔE 14.8 / 常视 18.6，深色（#a06fe8,#6f7b8a,#e06a55）8.6 / 17.4，
   都在阈值之上。唯一 FAIL 是 B 级那格的彩度低于下限——**那是故意的**，
   B 级本来就是「中性、不强调」的一档，而每一段都直接印着文字标签
   （「B 级 36%」），identity 不靠颜色单独承载。

   需要淡色底时一律用 `color-mix(… currentColor …)` 从文字色现算，
   不要另写一个十六进制值：另写一份就会和数据色漂移，而且漂移是渐进的，
   没有哪一次改动看起来是错的。 */

:root {
  color-scheme: light;
  --surface: #ffffff;
  --plane: #f3f6f7;
  --sunken: #e9eef1;
  --ink: #202832;
  --ink-2: #4d5a67;
  --muted: #75828e;
  --grid: #e2e8eb;
  --baseline: #c7d1d7;
  --border: rgba(32, 40, 50, 0.10);
  --border-strong: rgba(32, 40, 50, 0.19);
  --series-1: #722ed1;
  --good: #389e0d;
  --warning: #d48806;
  --serious: #e07a3c;
  --critical: #d9483b;
  /* 状态色当**文字**用时要另取一档深的。
     #389e0d 做色块没问题，但白底上只有 3.5:1，12px 的「↑ 22」读起来发虚；
     #237804 是 5.6:1。同理 --warning 做文字用 --warning-ink。
     深色主题反过来——底是近黑，文字要更亮。 */
  --good-ink: #237804;
  --warning-ink: #a05a00;
  /* B 级那档。原来是 #d8d1c7，白字压上去只有 1.5:1，那行标签基本读不出来；
     #8f7da3 到 3.7:1，同时也是紫的近邻灰，和主色是一家人。 */
  --deemph: #8f7da3;

  /* 装饰用。渐变只走同色相的深浅两端——跨色相的渐变在数据界面里会
     被读成「这里有两种含义」。 */
  --accent: #6931c6;
  --accent-deep: #4c2195;
  --accent-soft: rgba(105, 49, 198, 0.10);
  --ring: rgba(105, 49, 198, 0.30);
  --teal: #087f78;
  --teal-soft: rgba(8, 127, 120, 0.09);
  --warm: #c96b2c;
  --canvas-grid: rgba(32, 40, 50, 0.025);
  --topbar-height: 58px;

  --radius: 7px;
  --radius-lg: 8px;
  /* 三级阴影。平面界面里深度是最便宜的层级线索：
     1 = 贴在纸上（卡片）· 2 = 浮起来（悬停、输入框）· 3 = 盖住别的东西（抽屉）
     投影带紫调（照抄 RssHub 的 rgba(43,18,76)）：中性黑投影落在淡紫底上
     会发灰发脏，同色相的投影才干净。 */
  --shadow-1: 0 1px 2px rgba(26, 37, 47, .05), 0 4px 14px rgba(26, 37, 47, .045);
  --shadow-2: 0 2px 4px rgba(26, 37, 47, .07), 0 10px 26px rgba(26, 37, 47, .10);
  --shadow-3: 0 12px 44px rgba(43, 18, 76, .22);
}

/* 暗色不是浏览器自动反色，而是同一套紫的夜间版本（同样照 RssHub 的暗色块）。 */
:root[data-theme="dark"] {
    color-scheme: dark;
    --surface: #1b1724;
    --plane: #121018;
    --sunken: #0c0a11;
    --ink: #f0eaf8;
    --ink-2: #cfc3dd;
    --muted: #b4a5c6;
    --grid: #332841;
    --baseline: #4a3862;
    --border: rgba(240, 234, 248, 0.10);
    --border-strong: rgba(240, 234, 248, 0.20);
    /* 数据色是**另选的一套步进**，不是把浅色那组调亮：
       #722ed1 在近黑底上压不住，而直接用 RssHub 的 #b37feb 会和 B 级灰
       撞到常视 ΔE 11.4（校验器判硬 FAIL）。这三个是重新步进出来的。 */
    --series-1: #a06fe8;
    --good: #6abe4a;
    --warning: #e3a83a;
    --serious: #e8965c;
    --critical: #e06a55;
    --good-ink: #95de64;
    --warning-ink: #e3a83a;
    --deemph: #6f7b8a;

    --accent: #b37feb;
    --accent-deep: #9254de;
    --accent-soft: rgba(179, 127, 235, 0.16);
    --ring: rgba(179, 127, 235, 0.42);
    --teal: #55c9bd;
    --teal-soft: rgba(85, 201, 189, 0.12);
    --warm: #eaa36e;
    --canvas-grid: rgba(240, 234, 248, 0.025);

    /* 深色下阴影几乎看不见，层级改由边框承担——照抄浅色的阴影值
       只会得到一圈脏灰。 */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow-2: 0 2px 6px rgba(0, 0, 0, .45), 0 10px 28px rgba(0, 0, 0, .35);
    --shadow-3: 0 12px 48px rgba(0, 0, 0, .60);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--plane);
  background-image:
    linear-gradient(90deg, var(--canvas-grid) 1px, transparent 1px),
    linear-gradient(var(--canvas-grid) 1px, transparent 1px);
  background-size: 32px 32px;
  color: var(--ink);
  /* 中文字重挑选：system-ui 在 macOS 上落到苹方、Windows 上落到微软雅黑，
     但 Linux 容器里会落到没有中文的 Cantarell，整页变成方框。显式列出来。 */
  font: 15px/1.6 "Avenir Next", Avenir, "PingFang SC",
        "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 键盘可见的聚焦环。默认的 outline 在圆角元素上是个方框，很难看，
   但直接 `outline:none` 会让键盘用户彻底看不见焦点——所以是替换不是删除。 */
:where(a, button, select, input, textarea, summary):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* 她在手机上用，动效只做「确认操作发生了」这一件事，不做表演 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ── 顶栏 ── */
/* 毛玻璃：顶栏是 sticky 的，内容会从它下面穿过去。不透明的白条会把
   「还有内容在上面」这个线索切掉，半透明+模糊则保留了它。 */
.top {
  display: flex; align-items: center; gap: 14px;
  min-height: var(--topbar-height);
  padding: 10px 18px; padding-top: max(10px, env(safe-area-inset-top));
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  -webkit-backdrop-filter: saturate(150%) blur(18px);
  backdrop-filter: saturate(150%) blur(18px);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--teal) 12%, transparent),
              0 8px 26px rgba(26, 37, 47, .045);
  position: sticky; top: 0; z-index: 50; flex-wrap: wrap;
}
@supports not (backdrop-filter: blur(1px)) { .top { background: var(--surface); } }

.brand {
  display: flex; align-items: center; gap: 8px; font-weight: 700;
  color: var(--ink); text-decoration: none; letter-spacing: 0;
  font-size: 16.5px;
}
/* 品牌标记：一个脉搏形状。纯文字的标题在一排导航里没有视觉锚点，
   眼睛每次都要读字才知道自己在哪套系统里。 */
.brand-mark {
  width: 30px; height: 30px; border-radius: 7px; flex: none;
  display: grid; place-items: center;
  background: linear-gradient(140deg, var(--accent), var(--accent-deep));
  box-shadow: inset -3px -3px 0 color-mix(in srgb, var(--teal) 38%, transparent),
              0 4px 12px color-mix(in srgb, var(--accent) 20%, transparent);
}
.brand-mark svg { display: block; }

.top nav { display: flex; gap: 2px; flex: 1; }
.top nav a {
  color: var(--ink-2); text-decoration: none; padding: 6px 11px;
  border: 1px solid transparent; border-radius: 7px; font-size: 13.5px;
  transition: background .14s ease, color .14s ease, border-color .14s ease;
}
.top nav a:hover { background: var(--teal-soft); color: var(--teal); }
.top nav a.on {
  background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 22%, transparent);
  color: var(--accent); font-weight: 700;
  box-shadow: inset 0 -2px 0 var(--accent);
}
.top nav a.on:hover { color: var(--accent); }
.top-actions { display: flex; gap: 6px; align-items: center; }
.theme-toggle, .nav-toggle {
  min-height: 32px; border: 1px solid var(--border); border-radius: 9px;
  color: var(--ink-2); background: color-mix(in srgb, var(--surface) 82%, transparent);
  box-shadow: none; padding: 5px 9px; font-size: 12px;
}
.theme-toggle { width: 32px; padding: 0; display: grid; place-items: center; font-size: 16px; }
.theme-toggle:hover, .nav-toggle:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); filter: none; }
.nav-toggle { display: none; }
.who { font-size: 12.5px; color: var(--ink-2); display: flex; gap: 10px; align-items: center;
  padding: 4px 4px 4px 9px; border: 1px solid var(--border); border-radius: 7px;
  background: var(--surface); }
.logout { color: var(--muted); text-decoration: none; }
.logout:hover { color: var(--critical); }

main { padding: 24px 20px 48px; max-width: 1240px; margin: 0 auto; }
/* 只淡入，不做 transform：带 transform 的祖先会把移动端 position:fixed 抽屉
   变成相对祖先定位，表现为抽屉跟着页面内容一起向下偏。 */
main > :not(.chat-shell):not(dialog) { animation: page-in .22s ease-out both; }
@keyframes page-in { from { opacity: 0; } }

/* ── 卡片 ── */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 18px; margin-bottom: 16px;
  box-shadow: var(--shadow-1);
}
/* 标题左侧的小竖条：卡片标题只有 15px，在一屏六张卡里根本切不开段落。
   加粗到 17px 会开始和数字抢注意力，一根 3px 的条便宜得多。 */
.card h2 {
  margin: 0 0 3px; font-size: 15.5px; font-weight: 700; letter-spacing: 0;
  display: flex; align-items: center; gap: 9px;
}
.card h2::before {
  content: ""; width: 7px; height: 7px; border-radius: 2px; flex: none;
  background: var(--teal); transform: rotate(45deg);
  box-shadow: 7px -7px 0 color-mix(in srgb, var(--accent) 38%, transparent);
}
.section-hint { margin: 0 0 12px; font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.card-body { margin-top: 12px; }

/* ── Stat tiles ── */
.tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 12px; margin-bottom: 18px;
}
.tile {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 16px 17px 15px;
  box-shadow: var(--shadow-1);
  transition: box-shadow .16s ease, transform .16s ease;
}
.tile:hover { box-shadow: var(--shadow-2); transform: translateY(-1px); }
/* 状态色渗进卡片顶部，不是描一条实线。
   ⚠️ 试过 3px 实色顶边，结果是四块里三块顶着红杠，整页读起来像在报警——
   而这几个指标本来就常年偏红。产品原则那条「日报不能只报坏消息」说的就是
   这个：数据系统很容易变成批评机器，那样两周就没人看了。
   6% 的渐层足够让四块不再长得一样，又不会先声夺人。 */
.tile::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tile-accent, transparent) 7%, transparent),
    transparent 46%);
}
.tile > * { position: relative; }
.tile-label {
  font-size: 12.5px; color: var(--ink-2); display: flex; align-items: center; gap: 6px;
}
.tile-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.tile-value {
  font-size: 32px; font-weight: 750; margin-top: 5px; letter-spacing: 0;
  line-height: 1.08; font-variant-numeric: tabular-nums;
}
/* 涨跌做成药丸：裸着的红字在一堆灰字里其实不显眼，而这一行恰恰是
   她最该看见的。底色从文字色现算，不另写一份十六进制。 */
.tile-delta {
  font-size: 12px; margin-top: 7px; font-weight: 600;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 11%, transparent);
  font-variant-numeric: tabular-nums;
}
.tile-delta-label { color: var(--muted); font-weight: 400; }
.tile-hint { font-size: 11.5px; color: var(--muted); margin-top: 7px; line-height: 1.5; }

/* ── 横向条形 ── */
.bars { display: flex; flex-direction: column; gap: 7px; }
.bar-row { display: grid; grid-template-columns: 116px 1fr 62px; gap: 10px; align-items: center; }
.bar-label { font-size: 13px; color: var(--ink); min-width: 0; }
.bar-sub { color: var(--muted); font-size: 11.5px; margin-left: 5px; }
.bar-track { background: var(--grid); border-radius: 4px; height: 16px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 0 4px 4px 0; transition: opacity .15s; }
.bar-row:hover .bar-fill { opacity: .82; }
.bar-value {
  font-size: 13px; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--ink); font-weight: 600;
}

/* ── 配比条 ── */
.ratio { display: flex; gap: 2px; height: 34px; border-radius: 6px; overflow: hidden; }
.ratio-seg {
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 12px; font-weight: 600; min-width: 0; overflow: hidden;
  white-space: nowrap;
}

/* ── 折线 ── */
.line-chart { width: 100%; height: auto; display: block; }
.pt circle:first-of-type + circle { transition: r .12s; }
.pt:hover circle + circle { r: 6; }

/* ── 表格 ── */
.table-wrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-1);
}
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { padding: 9px 11px; text-align: left; border-bottom: 1px solid var(--grid); }
/* ⚠️ 这里**不能**做表头吸顶，试过了不生效。
   `.table-wrap` 为了横向滚动设了 `overflow-x:auto`，按 CSS 规范这会把
   `overflow-y` 从 visible 提升成 auto，于是它成了滚动容器——`position:sticky`
   贴的是这个容器而不是视口，而容器本身没有纵向溢出，表头照样滚走
   （实测滚 700px 后表头在 y=-553）。
   要真做成吸顶只能给 wrap 加 max-height 让它纵向也滚，但那是在手机上套一层
   内部滚动区，比列名滚走更烦人。所以只留样式，不留假的吸顶。 */
thead th {
  color: var(--ink-2); font-weight: 600; font-size: 11.5px;
  letter-spacing: .02em; white-space: nowrap;
  background: color-mix(in srgb, var(--sunken) 72%, var(--surface));
  border-bottom: 1px solid var(--border-strong);
}
tbody tr:last-child td { border-bottom: 0; }
/* nowrap 不能省：列多了之后「673.0万」会被折成「673.0」+「万」两行，
   一眼看过去像是两个数。宁可横向滚动（.table-wrap 已经能滚）。 */
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums;
  white-space: nowrap; }
tbody tr { transition: background .12s ease; }
tbody tr:nth-child(even) { background: color-mix(in srgb, var(--plane) 48%, transparent); }
tbody tr:hover { background: var(--teal-soft); }
/* 卡片里的表格做成通栏：卡片自己有边框，表格再来一圈就是双框。
   通栏还能多出 36px 给那 11 个数字列。 */
.card > .table-wrap {
  border: 0; border-radius: 0; background: transparent;
  margin: 0 -18px; width: calc(100% + 36px);
}
/* 卡片里只有一张表时（作品列表就是），让表头贴着卡片顶边。
   留着那 18px 内边距的话，卡片顶部会出现一条白带，下面才是灰表头——
   读起来像两个没对齐的东西叠在一起。 */
.card:has(> .table-wrap:first-child) { overflow: hidden; }
.card > .table-wrap:first-child { margin-top: -18px; }
.card > .table-wrap th:first-child,
.card > .table-wrap td:first-child { padding-left: 18px; }
.card > .table-wrap th:last-child,
.card > .table-wrap td:last-child { padding-right: 18px; }

/* 可排序表头。整格可点——表头字号 12px，只让文字可点在手机上几乎点不中 */
th:has(.sort) { padding: 0; }
a.sort { display: block; padding: 9px 11px; color: inherit; text-decoration: none;
  white-space: nowrap; transition: background .12s ease, color .12s ease; }
a.sort:hover { background: var(--sunken); color: var(--ink); }
a.sort.on { color: var(--accent); font-weight: 700; }
.card > .table-wrap th:first-child a.sort { padding-left: 18px; }
.card > .table-wrap th:last-child a.sort { padding-right: 18px; }
/* min-width 不能省：互动列加到 11 列后，手机上浏览器会把标题列压到能压的极限，
   「人昏迷后」被折成一行一个字。表格本来就能横滚，宁可滚也不要压。 */
.title-cell { max-width: 460px; min-width: 300px; }
.title-cell a { color: var(--ink); text-decoration: none; }
.title-cell a:hover { text-decoration: underline; }
/* 居中对齐不能少：封面是这一格里最高的东西，flex-start 会把标题顶到
   封面顶边，而右边十一列数字是垂直居中的——一行里两种基线，扫读时
   眼睛要上下跳。 */
.title-flex { display: flex; gap: 11px; align-items: center; }
.title-text { min-width: 0; }

/* 封面。9:16 竖屏——抖音就是这个比例，按 16:9 裁会把人脸切掉一半。
   ⚠️ 宽度只给 48px：试过 64px，9:16 换算下来行高被撑到 120px，一页 60 行
   要滚很久，而封面上那几个字在任何一档尺寸都读不清。它在这里的作用是
   **让她认出是哪条**（画面、机位、字幕色块），不是给人读的，48 够了。
   固定尺寸 + aspect-ratio 是为了占位：不写死的话图片一张张加载进来会把
   表往下顶，她正读到一半的那行会自己跑掉。 */
.cover {
  flex: none; width: 48px; aspect-ratio: 9 / 16; overflow: hidden;
  border-radius: 6px; background: var(--sunken);
  border: 1px solid var(--border);
  /* 没归档到封面时 /cover/ 返回 404，onerror 把 <img> 摘掉，
     露出的就是这个底色块——比浏览器默认那个碎图图标干净得多。 */
}
.cover img { width: 100%; height: 100%; object-fit: cover; display: block; }

@media (max-width: 560px) {
  .cover { width: 40px; }
  .title-cell { min-width: 230px; }
}

/* ── 标签 ── */
.tag {
  display: inline-block; padding: 1.5px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 500; border: 1px solid var(--border);
  color: var(--ink-2); white-space: nowrap; letter-spacing: .01em;
}
/* 底色一律从文字色现算，省得改一次主题要同步四个十六进制值 */
.tag.A, .tag.C, .tag.pin { border-color: transparent; }
.tag.A { color: var(--series-1); }
.tag.B { color: var(--muted); }
.tag.C { color: var(--critical); }
.tag.pin { color: #8a5d00; }
.tag.A, .tag.B, .tag.C, .tag.pin {
  background: color-mix(in srgb, currentColor 13%, transparent);
}
:root[data-theme="dark"] .tag.pin { color: var(--warning); }

/* ── 筛选 ── */
.filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px;
  padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--surface) 92%, transparent); box-shadow: var(--shadow-1); }
.filters select, .filters input {
  font: inherit; font-size: 13px; padding: 8px 11px; border-radius: 7px;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  box-shadow: var(--shadow-1);
  transition: border-color .14s ease, box-shadow .14s ease;
}
.filters select:hover, .filters input:hover { border-color: var(--border-strong); }
.filters select:focus, .filters input:focus {
  outline: 0; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
}

/* 主按钮：渐变 + 一点自己颜色的投影。纯色块在浅灰底上没有重量，
   一屏里分不出「这个能点」和「这个是标签」。 */
button, .filters button {
  font: inherit; font-size: 13px; padding: 8px 17px; border-radius: 7px; border: 0;
  background: var(--accent);
  color: #fff; cursor: pointer; font-weight: 500;
  box-shadow: 0 1px 2px rgba(11,11,11,.10),
              0 3px 10px color-mix(in srgb, var(--accent) 28%, transparent);
  transition: transform .1s ease, box-shadow .14s ease, filter .14s ease;
}
button:hover { filter: brightness(1.06); }
/* 按下去要有位移——手机上没有 hover，这是唯一的「点到了」反馈 */
button:active { transform: translateY(1px); box-shadow: 0 1px 2px rgba(11,11,11,.12); }
button:disabled { opacity: .5; cursor: not-allowed; box-shadow: none; filter: none; }

/* 次要按钮不要主按钮那圈彩色投影。
   ⚠️ 它们各自的 `background` 写在文件后面（类选择器，特异度赢），但**没有
   一个写了 `box-shadow`**——不在这里显式清掉，透明底的幽灵按钮周围会挂着
   一圈蓝光，看着像坏了。加新的次要按钮样式时记得进这个名单。 */
button.ghost, button.chip, .conv-act, .conv-more, .msg-copy {
  box-shadow: none; filter: none;
}
button.ghost {
  transition: border-color .14s ease, color .14s ease, background .14s ease;
}
button.ghost:hover { border-color: var(--accent); color: var(--accent);
  background: var(--accent-soft); filter: none; }
button.ghost.danger:hover { border-color: var(--critical); color: var(--critical);
  background: color-mix(in srgb, var(--critical) 10%, transparent); }

.footnote { font-size: 11.5px; color: var(--muted); margin: 12px 0 0; line-height: 1.55; }
.empty { color: var(--muted); font-size: 13px; margin: 6px 0; }
.pager { display: flex; gap: 10px; align-items: center; margin-top: 14px; font-size: 13px; }
.pager a {
  color: var(--accent); text-decoration: none; padding: 5px 11px; border-radius: 8px;
  border: 1px solid var(--border); transition: background .12s ease;
}
.pager a:hover { background: var(--accent-soft); }

/* ── 登录 ── */
.login-wrap {
  position: relative; max-width: 360px; margin: 11vh auto; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 30px 28px 27px; box-shadow: var(--shadow-2); overflow: hidden;
}
.login-wrap::before { content: ""; position: absolute; inset: 0 0 auto; height: 4px;
  background: linear-gradient(90deg, var(--accent) 0 68%, var(--teal) 68%); }
.login-wrap h1 { font-size: 22px; margin: 0 0 5px; letter-spacing: 0; }
.login-wrap p { color: var(--muted); font-size: 13px; margin: 0 0 20px; }
.login-wrap input {
  width: 100%; font: inherit; padding: 11px 12px; margin-bottom: 10px;
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--surface); color: var(--ink);
  transition: border-color .14s ease, box-shadow .14s ease;
}
.login-wrap input:focus {
  outline: 0; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring);
}
.login-wrap .captcha-row {
  display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 10px;
  align-items: start;
}
.login-wrap .captcha-row input { height: 46px; margin-bottom: 10px; }
.login-wrap .captcha-image {
  width: 128px; height: 44px; margin: 0; padding: 0; overflow: hidden;
  border: 1px solid var(--border); background: var(--surface-2); box-shadow: none;
}
.login-wrap .captcha-image img { display: block; width: 128px; height: 44px; }
.login-wrap button {
  width: 100%; font: inherit; padding: 12px; border-radius: 9px;
  font-weight: 600; margin-top: 4px;
}
.err { color: var(--critical); font-size: 13px; margin-bottom: 10px; }

@media (max-width: 560px) {
  .bar-row { grid-template-columns: 92px 1fr 52px; gap: 8px; }
  .bar-label { font-size: 12px; }
  .tile-value { font-size: 26px; }
  main { padding: 14px 12px 34px; }
}

/* ── 样本不足 / 警示标记 ──
   数值本身不该被藏起来，但读者必须在同一视线里看到「这个数不可信」。 */
.warn-tag {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  font-size: 10.5px; line-height: 16px; border-radius: 999px;
  background: rgba(250, 178, 25, .18); color: #7a5200; white-space: nowrap;
}
:root[data-theme="dark"] .warn-tag { color: var(--warning); }
.bar-row.thin .bar-value { color: var(--muted); font-weight: 500; }
.footnote strong { color: var(--ink); }

/* 单个导航项不拆行——「作品」被拆成两行很难看也难点 */
.top nav a { white-space: nowrap; }
.top nav { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.top nav::-webkit-scrollbar { display: none; }

/* 窄屏只露出最常用的四个入口。其余入口由显眼的「更多」展开：
   11 个标签全摊开会把首屏内容挤到屏幕外，横向藏起来又会让人根本不知道还有页面。 */
@media (max-width: 620px) {
  .top { gap: 8px; padding: 9px 12px; }
  .brand { font-size: 15px; }
  .brand-mark { width: 26px; height: 26px; }
  .top-actions { margin-left: auto; }
  .nav-toggle { display: inline-flex; align-items: center; justify-content: center; }
  .who { font-size: 0; gap: 0; }
  .who .logout { font-size: 12px; padding: 5px 1px; }
  .top nav {
    flex: 1 0 100%; order: 3; overflow-x: visible; flex-wrap: wrap; row-gap: 4px;
  }
  .top nav a { padding: 5px 10px; font-size: 13.5px; }
  .top nav .nav-secondary { display: none; }
  .top nav .nav-secondary.on,
  .top nav.is-expanded .nav-secondary { display: inline-flex; }
}

/* 折线图在手机上要够高才看得清 */
@media (max-width: 560px) {
  .line-chart { min-height: 190px; }
  .bar-label { font-size: 11.5px; line-height: 1.35; }
  .warn-tag { font-size: 9.5px; padding: 0 5px; }
}
/* 620px 太窄了：卡片有 1100px 宽，折线缩在正中间，两侧各留 240px 白，
   看着像没加载完。840 还不至于让 12 个月的点稀疏到读不出趋势。 */
.line-chart { max-width: 840px; margin: 0 auto; }

/* ─────────── 选题池 ─────────── */

/* 池子体检：这是防漂移的第一道闸，视觉权重要压过下面的列表 */
.pool-health{border:1px solid var(--border);border-left-width:4px;border-radius:10px;
  padding:15px 17px;margin:0 0 14px;background:var(--surface);
  box-shadow:var(--shadow-1)}
.pool-health.good{border-left-color:#0ca30c}
.pool-health.warning{border-left-color:#fab219}
.pool-health.critical{border-left-color:#d03b3b}
.pool-main{font-size:15px}
.pool-main strong{font-size:24px;letter-spacing:-.02em}
.pool-runway{margin-left:12px}
.pool-tiers{display:flex;gap:16px;flex-wrap:wrap;margin:8px 0 4px;font-size:13px}
.pool-tier strong{font-size:15px}

.topic-card{border-top:1px solid var(--border);padding:12px 0}
.topic-card:first-child{border-top:0}
.topic-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
/* 分数是排序依据，给它固定宽度让一列数字对齐、方便扫读 */
.topic-score{font-variant-numeric:tabular-nums;font-weight:600;min-width:3.2em;
  font-size:15px}
.topic-title{font-weight:500;flex:1 1 16em;min-width:0}
.topic-summary{color:var(--ink-2);font-size:13px;margin:4px 0 2px}
.topic-note{font-size:13px;color:var(--ink-2);margin-top:4px}

/* 知识库列表的删除。平时压成灰色，hover/focus 才变红：
   一列全是红按钮的表现是她不敢用这一页，而且红色一多就不再是警告色了。
   `margin-left:auto` 顶到最右，离标题链接尽量远——这一行里最容易点错的
   就是这两个。触屏没有 hover，所以默认态也必须是可读的（不是半透明）。 */
/* 选择器要压过下面那条通用的 `button.danger`（两者特指度相同，靠位置决胜
   太脆了——上次就是它赢了，整列删除按钮全是红的）。 */
.kb-del{margin-left:auto;flex:0 0 auto}
.topic-card .kb-del button.danger{padding:3px 9px;font-size:12px;
  color:var(--muted);border-color:var(--border)}
.topic-card .kb-del button.danger:hover,
.topic-card .kb-del button.danger:focus-visible{
  color:var(--critical);border-color:var(--critical);
  background:color-mix(in srgb, var(--critical) 10%, transparent)}

/* ─────────── 周排期 ─────────── */

/* 窄屏一列（她主要用手机），宽屏铺成七格一周 */
.week{display:grid;gap:8px;grid-template-columns:1fr}
@media (min-width:760px){.week{grid-template-columns:repeat(7,1fr)}}
.slot{border:1px solid var(--border);border-radius:10px;padding:11px;min-height:80px;
  background:var(--surface);box-shadow:var(--shadow-1);
  transition:box-shadow .16s ease,border-color .16s ease}
.slot:hover{box-shadow:var(--shadow-2)}
/* 空槽用虚线而不是灰底：虚线读作「这里缺东西」，灰底读作「这里被禁用」 */
.slot.empty{border-style:dashed;background:transparent;box-shadow:none}
.slot.empty:hover{box-shadow:none;border-color:var(--accent)}
.slot-day{font-size:12px;color:var(--muted);margin-bottom:6px}
.slot-body{font-size:13px;line-height:1.45}
.week-foot{display:flex;align-items:center;gap:12px;margin-top:12px;
  font-size:13px;color:var(--ink-2);flex-wrap:wrap}
form.inline{display:inline}

/* ─────────── 作品页的数据新鲜度条 ─────────── */
/* 左边一句「数据截至…」，右边一个刷新按钮。先说数据多旧再给按钮——
   她想点刷新正是因为怀疑数据旧了，不告诉她就只会反复点。
   窄屏上两者会换行，所以 margin-left:auto 而不是固定宽度。 */
.data-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin:-4px 0 12px}
.data-bar .footnote{margin:0}
.data-bar form.inline{margin-left:auto}
.data-bar button{padding:5px 12px;font-size:12.5px}

/* ─────────── 后台任务手动触发（/system）─────────── */
/* 名称左对齐、按钮右对齐，中间那句「下次约 x 小时后」用 auto 边距推开——
   采集变成随机间隔之后，那句话比按钮本身还重要：它是「这东西还在跑吗」
   唯一的答案。 */
.job-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:9px 0;border-top:1px solid var(--border)}
.job-row:first-of-type{margin-top:10px}
.job-name{font-size:13.5px;flex:1 1 auto}
.job-row form.inline{margin-left:8px}
.job-row button[disabled]{opacity:.55;cursor:default}

/* ─────────── 竞品资产 ─────────── */
.competitor-form{display:grid;grid-template-columns:130px minmax(210px,1.5fr) 150px minmax(150px,1fr) auto;gap:9px;align-items:end}
.competitor-form label{display:grid;gap:5px;font-size:11.5px;color:var(--muted)}
.competitor-form input,.competitor-form select{width:100%;min-width:0;font:inherit;font-size:13px;padding:8px 10px;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--ink)}
.competitor-form input:focus,.competitor-form select:focus{outline:0;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
.competitor-form button{height:35px;white-space:nowrap}
.competitor-notice{padding:10px 12px;margin:0 0 14px;border-left:3px solid var(--accent);background:var(--accent-soft);color:var(--ink-2);font-size:13px}
.competitor-table td{vertical-align:middle}.competitor-table td:first-child{white-space:nowrap}.competitor-actions{white-space:nowrap}.competitor-actions form+form{margin-left:5px}
.competitor-account{display:flex;align-items:center;gap:9px;min-width:145px;color:var(--ink);text-decoration:none}.competitor-account:hover strong,.competitor-work-count:hover,.competitor-profile-link:hover{text-decoration:underline}.competitor-avatar{position:relative;display:grid;place-items:center;flex:none;width:34px;height:34px;border-radius:50%;overflow:hidden;background:var(--accent-soft);color:var(--accent);font-size:13px;font-weight:700}.competitor-avatar img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;background:var(--surface)}.competitor-work-count{color:var(--accent);text-decoration:none;font-variant-numeric:tabular-nums}
.competitor-status.pending,.competitor-status.running{color:#7a5700;background:color-mix(in srgb,#fab219 14%,transparent);border-color:transparent}.competitor-status.ok{color:var(--series-1);background:color-mix(in srgb,var(--series-1) 12%,transparent);border-color:transparent}.competitor-status.partial{color:#8a5d00;background:color-mix(in srgb,#fab219 16%,transparent);border-color:transparent}.competitor-status.error{color:var(--critical);background:color-mix(in srgb,var(--critical) 10%,transparent);border-color:transparent}
.competitor-profile-head{display:flex;gap:14px;align-items:flex-start}.competitor-profile-head .competitor-avatar{width:60px;height:60px;font-size:22px}.competitor-profile-copy{min-width:0}.competitor-profile-title{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:17px}.competitor-profile-title .tag{font-size:10.5px}.competitor-profile-meta{overflow-wrap:anywhere}.competitor-signature{margin:7px 0 5px;color:var(--ink-2);font-size:13px;line-height:1.5;white-space:pre-wrap}.competitor-fetch-note{margin:7px 0 5px;font-size:12px;line-height:1.5;color:var(--ink-2)}.competitor-fetch-note .tag{margin-right:4px}.competitor-profile-link,.competitor-back{font-size:12.5px;color:var(--accent);text-decoration:none}.competitor-work-filter{align-items:center}.competitor-work-filter .competitor-back{padding:7px 3px}.competitor-work-title{min-width:330px;max-width:600px}.competitor-work-title a{color:var(--ink);text-decoration:none}.competitor-work-title a:hover{text-decoration:underline}.competitor-work-description{margin-top:4px;color:var(--ink-2);font-size:12px;line-height:1.45;white-space:pre-wrap}.competitor-work-tags{display:flex;gap:4px;flex-wrap:wrap;margin-top:6px}.competitor-work-tags .tag{font-size:10px;padding:1px 6px}.competitor-metrics{display:flex;gap:7px;flex-wrap:wrap;min-width:180px;font-size:12px}.competitor-metrics span{white-space:nowrap}.competitor-metrics b{color:var(--muted);font-weight:500;margin-right:3px}
.confirm-dialog{width:min(440px,calc(100vw - 28px));padding:0;border:1px solid var(--border-strong);
  border-radius:8px;background:var(--surface);color:var(--ink);box-shadow:var(--shadow-3);
  animation:none;opacity:1}
.confirm-dialog::backdrop{background:rgba(18,20,24,.48);backdrop-filter:blur(2px)}
.confirm-dialog form{display:grid;grid-template-columns:34px minmax(0,1fr);gap:12px 14px;padding:20px}
.confirm-dialog-mark{display:grid;place-items:center;width:30px;height:30px;border-radius:50%;
  color:var(--critical);background:color-mix(in srgb,var(--critical) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--critical) 26%,transparent);font-weight:800}
.confirm-dialog h3{margin:1px 0 6px;font-size:16px;line-height:1.4}
.confirm-dialog p{margin:0;color:var(--ink-2);font-size:13.5px;line-height:1.6}
.confirm-dialog .confirm-dialog-warning{margin-top:8px;color:var(--critical);font-size:12.5px}
.confirm-dialog-actions{grid-column:1/-1;display:flex;justify-content:flex-end;gap:8px;
  padding-top:4px;border-top:1px solid var(--grid)}
button.danger-solid{background:var(--critical);color:#fff;box-shadow:0 3px 10px
  color-mix(in srgb,var(--critical) 24%,transparent)}
@media (max-width:860px){.competitor-form{grid-template-columns:1fr 1fr}.competitor-form .competitor-profile,.competitor-form .competitor-note{grid-column:span 2}.competitor-form button{justify-self:start}}
@media (max-width:520px){.competitor-form{grid-template-columns:1fr}.competitor-form .competitor-profile,.competitor-form .competitor-note{grid-column:auto}.competitor-profile-head{gap:10px}.competitor-profile-head .competitor-avatar{width:48px;height:48px;font-size:18px}.competitor-work-title{min-width:250px}}

/* ─────────── 长期记忆 ─────────── */
.memory-stats{display:flex;gap:18px;flex-wrap:wrap;margin-bottom:12px;color:var(--ink-2);font-size:13px}
.memory-stats strong{color:var(--ink);font-size:20px;font-variant-numeric:tabular-nums;margin-right:3px}
.memory-summary{white-space:pre-wrap;line-height:1.65;padding:12px 14px;margin-bottom:9px;border-left:3px solid var(--accent);background:var(--accent-soft);font-size:13.5px}
.memory-error{color:var(--critical);font-size:12.5px}.memory-candidate{padding:13px 0;border-top:1px solid var(--border)}
.memory-candidate:first-child{border-top:0}.memory-head{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.memory-current{font-size:12.5px;color:var(--muted);margin-top:8px}.memory-proposed{font-size:13.5px;margin-top:5px}
.memory-candidate blockquote{margin:8px 0;padding:7px 10px;border-left:2px solid var(--border);color:var(--ink-2);font-size:12.5px}
.memory-candidate blockquote a{color:inherit}.memory-actions{display:flex;gap:7px;flex-wrap:wrap}.memory-actions form{margin:0}
.memory-facts{min-width:800px}.memory-facts textarea{width:min(430px,48vw);min-width:260px;resize:vertical}
.memory-edit{display:flex;gap:7px;align-items:flex-start}.memory-add{display:grid;grid-template-columns:150px 220px minmax(280px,1fr) auto;gap:9px;align-items:end}
.memory-add label{display:grid;gap:5px;font-size:11.5px;color:var(--muted)}
.memory-add input,.memory-add select,.memory-add textarea,.memory-edit textarea{font:inherit;font-size:13px;padding:8px 10px;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--ink)}
.memory-add button{height:35px;white-space:nowrap}.tag.archived{color:var(--muted);background:var(--surface-2)}
@media (max-width:860px){.memory-add{grid-template-columns:1fr 1fr}.memory-add label:nth-child(3){grid-column:span 2}.memory-add button{justify-self:start}}
@media (max-width:520px){
  .memory-add{grid-template-columns:1fr}.memory-add label:nth-child(3){grid-column:auto}.memory-add button{justify-self:stretch}
  .memory-facts{min-width:0}.memory-facts thead{display:none}.memory-facts tbody{display:block}
  .memory-facts tr{display:grid;grid-template-columns:auto 1fr auto;gap:8px 10px;padding:12px 0;border-bottom:1px solid var(--grid)}
  .memory-facts tr:last-child{border-bottom:0}.memory-facts td{display:block;padding:0;border:0;min-width:0}
  .memory-facts td:nth-child(1){grid-column:1;grid-row:1}.memory-facts td:nth-child(2){grid-column:2;grid-row:1}
  .memory-facts td:nth-child(3){grid-column:1/-1;grid-row:2}.memory-facts td:nth-child(4){grid-column:3;grid-row:1}
  .memory-facts td:nth-child(5){grid-column:1/-1;grid-row:3}.memory-facts td:nth-child(5) button{width:100%}
  .memory-edit{display:grid;grid-template-columns:minmax(0,1fr) auto}.memory-facts textarea{width:100%;min-width:0}
  .memory-audits thead{display:none}.memory-audits tbody{display:block}
  .memory-audits tr{display:grid;grid-template-columns:70px 58px minmax(0,1fr);gap:5px 8px;padding:10px 0;border-bottom:1px solid var(--grid)}
  .memory-audits tr:last-child{border-bottom:0}.memory-audits td{display:block;padding:0;border:0;min-width:0}
  .memory-audits td:nth-child(4){grid-column:1/-1;color:var(--ink-2);overflow-wrap:anywhere}
}

/* ─────────── 守门员提示 ─────────── */

/* 只提示不拦截：所以视觉上是「便签」不是「错误」——
   不用红底、不用大图标，避免把系统变成批评机器 */
.guard{border:1px solid var(--border);border-left-width:3px;border-radius:8px;
  padding:11px 13px;margin-top:10px;background:var(--surface);
  box-shadow:var(--shadow-1)}
.guard.info{border-left-color:#2a78d6}
.guard.warn{border-left-color:#fab219}
.guard-dot{display:inline-block;width:6px;height:6px;border-radius:50%;
  margin-right:7px;vertical-align:middle;background:currentColor;opacity:.5}
.guard-msg{font-size:14px;font-weight:500}
/* 依据必须看得见——藏进 tooltip 等于没有 */
.guard-num{font-size:12px;color:var(--ink-2);margin-top:3px;
  font-variant-numeric:tabular-nums}
.guard-why{font-size:12px;color:var(--muted);margin-top:5px;line-height:1.5}
.guard-tip{font-size:13px;margin-top:6px}
.guard-actions{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;align-items:center}
.guard-actions select{max-width:16em}
/* 「采纳」和「坚持原方案」必须等分量。
   把「坚持原方案」做成主按钮是在推她否决规则；反过来把「采纳」做成主按钮
   是在推她听机器的。这一页的产品原则是**只提示不拦截、决定权在她**——
   那就谁也别推，两个都做成描边按钮。 */
.guard-actions button{background:transparent;color:var(--ink-2);
  border:1px solid var(--border);box-shadow:none;font-weight:500}
.guard-actions button:hover{border-color:var(--accent);color:var(--accent);
  background:var(--accent-soft);filter:none}
button.ghost{background:transparent;color:var(--ink-2);border:1px solid var(--border)}

/* ─────────── Agent 聊天 ─────────── */

/* 窄屏单列（会话列表收进抽屉），宽屏才是 ChatGPT 那样的左列 + 正文。
   她每天在手机上用，桌面是运维用的——先保证手机。 */
.chat-shell{display:flex;gap:22px;position:relative;align-items:flex-start}
/* 侧栏必须避开同样吸顶的全站顶栏；原来的 top:12px 会在页面滚动后钻到顶栏
   下面，于是最上面的“新对话”看起来消失了。侧栏内部再让按钮二次吸顶，
   即使会话列表自己滚动，它也始终留在第一操作位。 */
.chat-side{display:none;flex-direction:column;gap:8px;flex:0 0 232px;
  /* 还要给 main 的底部留白让路，否则滚到聊天末尾时 sticky 会为避免越过
     .chat-shell 底边而反向上移，按钮又会钻到顶栏下面。 */
  min-width:0;max-height:calc(100dvh - var(--topbar-height) - 72px);
  position:sticky;top:calc(var(--topbar-height) + 12px);overflow:hidden;
  padding:10px;background:color-mix(in srgb,var(--surface) 94%,transparent);
  border:1px solid var(--border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1)}
@media (min-width:900px){.chat-side{display:flex}
  .side-toggle{display:none}}
/* 窄屏：抽屉从左侧滑入，盖在内容上 */
@media (max-width:899px){
  .chat-side.open{display:flex;position:fixed;left:0;top:0;bottom:0;z-index:60;
    width:min(84vw,300px);height:100dvh;max-height:none;background:var(--surface);
    border:0;border-right:1px solid var(--border);border-radius:0;padding:14px;
    overflow:hidden;box-shadow:0 0 36px rgba(0,0,0,.18)}
  .new-inline{display:none}
}
.side-backdrop{display:none}
.side-backdrop.on{display:block;position:fixed;inset:0;z-index:55;
  background:rgba(0,0,0,.32)}

.chat-new{display:block;position:sticky;top:0;z-index:2;flex:none;text-align:center;
  text-decoration:none;font-size:13px;font-weight:650;padding:9px 12px;
  border:1px solid color-mix(in srgb,var(--accent) 28%,var(--border));border-radius:7px;
  background:color-mix(in srgb,var(--accent-soft) 62%,var(--surface));color:var(--accent);
  box-shadow:0 4px 12px color-mix(in srgb,var(--accent) 9%,transparent)}
.chat-new:hover{border-color:var(--accent);color:var(--accent);
  background:var(--accent-soft)}
.conv-list{overflow-y:auto;display:flex;flex-direction:column;gap:2px;
  min-height:0;padding-right:2px;scrollbar-color:var(--baseline) transparent}
.conv-bucket{font-size:11px;color:var(--muted);margin:10px 0 3px;
  letter-spacing:.04em}
.conv-bucket.pinned{color:var(--accent);font-weight:650}
.conv-item{--conv-row-bg:var(--surface);position:relative;display:flex;align-items:center;
  min-height:36px;border-radius:7px;
  padding:0 3px 0 9px}
.conv-item:hover{--conv-row-bg:var(--plane);background:var(--conv-row-bg)}
.conv-item.on{--conv-row-bg:color-mix(in srgb,var(--accent) 9%,var(--surface));
  background:var(--conv-row-bg);box-shadow:inset 2px 0 0 var(--accent)}
.conv-item.on .conv-title{color:var(--ink);font-weight:500}
.conv-title{flex:1;min-width:0;display:flex;align-items:center;gap:5px;
  text-decoration:none;color:var(--ink-2);font-size:13px;padding:6px 0;
  overflow:hidden;white-space:nowrap}
.conv-title-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.conv-image-icon{display:inline-grid;place-items:center;flex:0 0 14px;width:14px;height:14px;
  color:var(--teal)}
.conv-image-icon svg{display:block;width:14px;height:14px}
/* 三点悬浮在标题末端，不参与行布局。未悬停时标题能真正用满整行；出现时按钮
   用当前行背景盖住最后几个字，既不挤压标题，也不会让省略位置来回跳。 */
.conv-acts{position:absolute;right:3px;top:50%;transform:translateY(-50%);
  display:grid;place-items:center;width:28px;height:28px;pointer-events:none}
.conv-item:hover .conv-acts,.conv-item:focus-within .conv-acts,
.conv-acts:has(.conv-more[aria-expanded="true"]){pointer-events:auto}
.conv-more{display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;
  border-radius:6px;background:transparent;color:var(--muted);font:inherit;font-size:19px;
  line-height:1;cursor:pointer;opacity:0;pointer-events:none;transition:opacity .12s,background .12s,color .12s}
.conv-item:hover .conv-more,.conv-more:focus-visible,
.conv-more[aria-expanded="true"]{opacity:1;pointer-events:auto}
.conv-item:hover .conv-more,.conv-more:focus-visible,.conv-more[aria-expanded="true"]{
  background:linear-gradient(90deg,transparent 0%,var(--conv-row-bg) 34%)}
.conv-more:hover,.conv-more[aria-expanded="true"]{color:var(--ink)}
@media (hover:none){
  .conv-acts{pointer-events:auto}
  .conv-more{opacity:1;pointer-events:auto;
    background:linear-gradient(90deg,transparent 0%,var(--conv-row-bg) 34%)}
}
.conv-menu{position:fixed;z-index:80;display:grid;width:132px;padding:5px;
  border:1px solid var(--border-strong);border-radius:7px;background:var(--surface);
  box-shadow:var(--shadow-3)}
.conv-menu[hidden]{display:none}
.conv-act{display:block;width:100%;border:0;border-radius:5px;background:transparent;
  color:var(--ink-2);font:inherit;font-size:12.5px;text-align:left;padding:7px 9px;cursor:pointer}
.conv-act:hover,.conv-act:focus-visible{background:var(--plane);color:var(--ink)}
.conv-act.danger{color:var(--critical)}
.conv-act.danger:hover,.conv-act.danger:focus-visible{
  background:color-mix(in srgb,var(--critical) 9%,var(--surface));color:var(--critical)}
.conv-empty{font-size:12.5px;color:var(--muted);padding:8px 2px}

.chat-bar{display:flex;gap:8px;align-items:center}
.new-inline{text-decoration:none;font-size:12.5px;padding:6px 11px;
  border:1px solid var(--border);border-radius:8px;color:var(--ink-2)}
@media (min-width:900px){.new-inline{display:none}}

/* 撑满视口高度，否则 sticky 的输入区在消息少的时候会停在页面中间——
   sticky 只有在容器比视口高时才会真的贴底。 */
.chat{flex:1;min-width:0;display:flex;flex-direction:column;gap:12px;
  min-height:calc(100dvh - var(--topbar-height) - 36px)}
.chat-log{flex:1}
.chat-log{display:flex;flex-direction:column;gap:14px;min-height:50vh}
.msg{max-width:min(46em,100%)}
.msg.assistant{max-width:min(60em,100%)}
.msg.user{align-self:flex-end}
.msg-who{font-size:11.5px;color:var(--muted);margin-bottom:3px}
.msg.user .msg-who{text-align:right}
.msg-body{border:1px solid var(--border);border-radius:14px;padding:11px 14px;
  background:var(--surface);font-size:14px;line-height:1.62;
  overflow-wrap:anywhere;box-shadow:var(--shadow-1)}
/* 她自己打的字不过 Markdown，保留换行 */
.msg.user .msg-body{background:linear-gradient(160deg,var(--accent),var(--accent-deep));
  color:#fff;border-color:transparent;white-space:pre-wrap;
  box-shadow:0 2px 10px color-mix(in srgb,var(--accent) 26%,transparent)}
/* 助手回答是渲染过的 Markdown：块级元素自己带间距，不能再 pre-wrap，
   否则每个标签之间的换行都变成空行，整段被撑成两倍高。
   .markdown-body 那份是给知识库编辑器的（定高 340px、大内边距），
   这里必须逐条压回气泡的尺寸——它和 .msg-body 同特异度且写在后面，
   不显式覆盖就会赢。 */
.msg.assistant .msg-body.markdown-body{min-height:0;white-space:normal;
  padding:12px 15px;border-radius:8px;line-height:1.62;
  border-left:3px solid color-mix(in srgb,var(--teal) 68%,var(--border))}
.msg.assistant .msg-body > :first-child{margin-top:0}
.msg.assistant .msg-body > :last-child{margin-bottom:0}
/* 等待态：正文还空着时给一个呼吸的占位，她要知道「在干活」而不是卡死 */
.msg.pending .msg-body::after{content:'正在思考…';color:var(--muted);
  animation:breathe 1.6s ease-in-out infinite}
@keyframes breathe{0%,100%{opacity:.55}50%{opacity:1}}
.msg-time{margin-left:6px;color:var(--muted)}
.msg-foot{display:flex;flex-direction:column;gap:4px;margin-top:5px}
.msg-tail{display:flex;align-items:center;gap:8px;font-size:11.5px;
  color:var(--muted)}
.msg.user .msg-tail{justify-content:flex-end}
/* 模型名和版本。她要能回答「上周那个回答特别好，当时用的谁」 */
.msg-model{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:11px;background:var(--plane);border:1px solid var(--border);
  border-radius:5px;padding:1px 6px}
.msg-copy{background:transparent;border:0;color:var(--muted);font-size:11.5px;
  padding:2px 5px;cursor:pointer;border-radius:5px}
.msg-copy:hover{background:var(--plane);color:var(--ink)}
.msg-tools{font-size:12px;color:var(--muted)}
.msg-tools code{background:var(--plane);padding:1px 6px;border-radius:4px;
  font-size:11.5px}

.chat-intro{max-width:640px;color:var(--ink-2);font-size:14px;margin:8vh auto 0;
  padding:22px 0 22px 18px;border-left:3px solid var(--teal)}
.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
/* 给可点的例子而不是空输入框——「能问什么」本身就是最大的使用门槛 */
.chip{background:var(--plane);border:1px solid var(--border);color:var(--ink-2);
  border-radius:7px;padding:7px 13px;font-size:13px;cursor:pointer}
.chip:hover{border-color:var(--accent);color:var(--accent);
  background:var(--accent-soft)}

/* 输入区（照 RssHub 的 composer）。
   整块 sticky，上面渐隐——不这样的话消息会「贴着」输入框边缘被切断，
   看不出下面还有没有内容。 */
.chat-composer{position:sticky;bottom:0;z-index:12;padding:14px 0
  max(12px,env(safe-area-inset-bottom));margin-top:6px;
  background:linear-gradient(180deg,rgba(0,0,0,0),var(--plane) 22%)}
.composer-tools{display:flex;align-items:center;gap:10px;margin-bottom:8px;
  min-height:22px}
.model-picker{display:flex;align-items:center;gap:6px;min-width:0}
.model-picker select{height:28px;padding:0 26px 0 11px;border-radius:7px;
  border:1px solid var(--border);background:var(--surface);color:var(--ink-2);
  font:inherit;font-size:12px;cursor:pointer;appearance:none;max-width:14em;
  text-overflow:ellipsis;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23898781' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center}
.model-picker #model-brand{max-width:13em}
.model-picker select:hover{border-color:var(--accent);color:var(--ink)}
.model-picker select:disabled{cursor:not-allowed;opacity:.58}
.chat-status{font-size:12px;color:var(--muted);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:520px){
  .composer-tools{align-items:flex-start;flex-direction:column;gap:6px}
  .model-picker{width:100%}
  .model-picker select{flex:1;min-width:0;max-width:none}
  .chat-status{width:100%;white-space:normal}
}

/* 输入框是个「框」，textarea 无边框长在里面——比两个方块并排耐看，
   而且点框内任意位置都能聚焦，手机上好点得多。 */
.chat-input{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;
  align-items:end;padding:10px;border:1px solid var(--border-strong);border-radius:8px;
  background:var(--surface);box-shadow:var(--shadow-2)}
.chat-input:focus-within{border-color:var(--accent);
  box-shadow:var(--shadow-2),0 0 0 3px var(--ring)}
.chat-input textarea{min-height:44px;max-height:200px;resize:none;border:0;
  outline:0;background:transparent;color:var(--ink);font:inherit;font-size:14px;
  line-height:1.6;padding:8px 6px}
.chat-input button{height:38px;min-width:76px;border:0;border-radius:9px;
  background:var(--series-1);color:#fff;font:inherit;font-size:14px;
  font-weight:500;cursor:pointer}
.chat-input button.stop{background:var(--critical)}

/* ─────────── 知识库编辑器 ─────────── */

.editor{display:flex;flex-direction:column;gap:12px}
.editor-title{font-size:19px;font-weight:600;padding:10px 12px;
  border:1px solid var(--border);border-radius:10px;background:var(--surface);
  color:var(--ink);width:100%;box-sizing:border-box}
/* 窄屏单列（她在手机上多半只是看），宽屏正文 + 侧栏 */
.editor-main{display:grid;gap:14px;grid-template-columns:1fr}
@media (min-width:860px){.editor-main{grid-template-columns:minmax(0,1fr) 260px}}
.editor-write textarea{width:100%;box-sizing:border-box;font:inherit;font-size:14px;
  line-height:1.7;padding:12px;border:1px solid var(--border);border-radius:10px;
  background:var(--surface);color:var(--ink);resize:vertical;min-height:340px;
  box-shadow:var(--shadow-1);transition:border-color .14s ease,box-shadow .14s ease}
.editor-write textarea:focus{outline:0;border-color:var(--accent);
  box-shadow:var(--shadow-1),0 0 0 3px var(--ring)}
.markdown-body{border:1px solid var(--border);border-radius:10px;padding:14px;
  background:var(--surface);min-height:340px;font-size:14px;line-height:1.72;
  box-shadow:var(--shadow-1)}
.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4{
  margin:1.1em 0 .4em;line-height:1.35}
.markdown-body h1{font-size:19px}.markdown-body h2{font-size:17px}
.markdown-body h3{font-size:15px}.markdown-body h4{font-size:14px}
.markdown-body p{margin:.55em 0}
.markdown-body ul,.markdown-body ol{margin:.5em 0;padding-left:1.5em}
.markdown-body blockquote{margin:.6em 0;padding-left:12px;
  border-left:3px solid var(--grid);color:var(--ink-2)}
.markdown-body pre{background:var(--plane);padding:10px 12px;border-radius:8px;
  overflow-x:auto;font-size:12.5px}
.markdown-body code{background:var(--plane);padding:1px 5px;border-radius:4px;
  font-size:12.5px}
.markdown-body pre code{background:none;padding:0}
.md-img{max-width:100%;border-radius:8px}
.markdown-body hr{border:0;border-top:1px solid var(--border);margin:1em 0}
/* 表格在窄屏上一定会超宽。横向滚动是唯一不破坏数据对齐的做法——
   换行或缩字号都会让「按维度对比」读不出来。 */
.md-table{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:.7em 0}
.md-table table{width:auto;min-width:100%;border-collapse:collapse;font-size:12.5px}
.md-table th,.md-table td{border:1px solid var(--border);padding:5px 9px;
  text-align:left;white-space:nowrap}
.md-table th{background:var(--plane);font-weight:600}

.editor-side{display:flex;flex-direction:column;gap:12px;font-size:13px}
.editor-side label{display:flex;flex-direction:column;gap:5px;color:var(--ink-2)}
.editor-side input[type=text],.editor-side input:not([type]),.editor-side select{
  font:inherit;font-size:13px;padding:7px 9px;border:1px solid var(--border);
  border-radius:8px;background:var(--surface);color:var(--ink);width:100%;
  box-sizing:border-box}
.side-label{color:var(--ink-2);display:block;margin-bottom:6px}
/* 主题域有 12 个，全铺开会把侧栏撑得比正文还长 */
.dom-checks{display:flex;flex-direction:column;gap:5px;max-height:190px;
  overflow-y:auto;border:1px solid var(--border);border-radius:8px;padding:8px}
.dom-check{flex-direction:row!important;align-items:center;gap:7px;font-size:12.5px}
.side-inline{flex-direction:row!important;align-items:center;gap:7px}
.editor-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  padding-top:4px}
.chip.on{background:var(--accent);
  color:#fff;border-color:transparent}
button.danger{color:var(--critical);border-color:var(--critical)}

/* ─────────── 账号管理 ─────────── */

.account-form {
  display: grid; grid-template-columns: 1.1fr 1.2fr 1.1fr .9fr auto;
  gap: 10px; align-items: end;
}
.account-form label, .account-edit label, .account-reset label {
  display: flex; flex-direction: column; gap: 5px; color: var(--ink-2); font-size: 12px;
}
.account-form input, .account-form select, .account-edit input, .account-edit select,
.account-reset input {
  width: 100%; min-width: 0; padding: 8px 9px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface); color: var(--ink); font: inherit;
}
.account-notice { color: var(--good-ink); background: color-mix(in srgb, var(--good) 11%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--good) 25%, var(--border)); border-radius: 8px;
  padding: 8px 10px; margin: 0 0 14px; font-size: 13px; }
.account-role.admin { color: var(--accent-deep); }
.account-role.doctor { color: var(--ink-2); }
.accounts-table td { vertical-align: top; }
.accounts-table .tag { margin-left: 5px; }
.account-actions { min-width: 184px; }
.account-actions details > summary { color: var(--accent); cursor: pointer; font-size: 12.5px; }
.account-actions details[open] > summary { margin-bottom: 9px; }
.account-edit, .account-reset { display: grid; gap: 8px; margin-top: 9px; }
.account-active { flex-direction: row !important; align-items: center; gap: 6px !important; }
.account-active input { width: auto; }
@media (max-width: 860px) {
  .account-form { grid-template-columns: 1fr 1fr; }
  .account-form button { justify-self: start; }
}
@media (max-width: 520px) {
  .account-form { grid-template-columns: 1fr; }
}
a.chip{text-decoration:none;display:inline-block}
/* 保存/返回/删除挤在一行，高度不一致会显得界面很糙 */
.editor-actions button,.editor-actions .chip{
  height:34px;line-height:1;padding:0 14px;border-radius:8px;
  display:inline-flex;align-items:center}

/* ─────────── 设置：模型分配 ─────────── */

.llm-roles td{vertical-align:middle}
.llm-roles select{font:inherit;font-size:12.5px;padding:5px 7px;
  border:1px solid var(--border);border-radius:7px;background:var(--surface);
  color:var(--ink);max-width:15em}
/* 模型名很长（gpt-5.2-pro-2025-12-11），窄屏靠表格横向滚动兜住 */
.llm-roles th,.llm-roles td{white-space:nowrap}

/* ─────────── 合规审核 ─────────── */

#text{width:100%;box-sizing:border-box;font:inherit;font-size:14px;line-height:1.7;
  padding:12px;border:1px solid var(--border);border-radius:10px;
  background:var(--surface);color:var(--ink);resize:vertical}
.compl-verdict{font-size:15px;font-weight:600;margin:14px 0 8px}
.compl-verdict.pass{color:var(--good)}
.compl-verdict.warn{color:var(--warning)}
.compl-verdict.block{color:var(--critical)}
/* 「只跑了一半」必须比「通过」更显眼——假的通过比没检查更危险 */
.compl-partial{font-size:12.5px;color:var(--warning);background:var(--plane);
  border:1px solid var(--warning);border-radius:8px;padding:8px 11px;margin:8px 0}
.compl-list{display:flex;flex-direction:column;gap:10px;margin-top:6px}
.compl-item{border:1px solid var(--border);border-left-width:3px;
  border-radius:10px;padding:11px 14px;background:var(--surface);
  box-shadow:var(--shadow-1)}
.compl-item.block{border-left-color:var(--critical)}
.compl-item.warn{border-left-color:var(--warning)}
.compl-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.compl-sev{font-size:12px;font-weight:600}
.compl-item.block .compl-sev{color:var(--critical)}
.compl-item.warn .compl-sev{color:var(--warning)}
.compl-head code{background:var(--plane);padding:1px 6px;border-radius:4px;
  font-size:11px;color:var(--muted)}
.compl-msg{font-size:13.5px;margin:6px 0 4px}
/* 引文是这一页的核心：她要两秒判断「真问题还是机器犯傻」 */
.compl-quote{margin:6px 0;padding:7px 11px;border-left:2px solid var(--grid);
  background:var(--plane);font-size:13px;color:var(--ink-2);border-radius:0 6px 6px 0}
.compl-fix{font-size:13px;color:var(--ink-2);margin:6px 0 2px}

/* ─────────── 脚本共创五步流 ─────────── */

/* 顶栏是 sticky 的，不留这个上边距标题会被压在导航底下 */
.script-title{font-size:19px;margin:4px 0 2px;line-height:1.35}
.script-wrap .footnote{margin-top:0}
.step h2{display:flex;align-items:center;gap:8px}
.step-no{display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:50%;color:#fff;font-size:12px;flex:none;
  background:linear-gradient(160deg,var(--accent),var(--accent-deep));
  box-shadow:0 2px 6px color-mix(in srgb,var(--accent) 30%,transparent)}
.step-busy{color:var(--muted);font-size:13px;animation:breathe 1.6s ease-in-out infinite}
/* 第②步要解释「为什么问这个」——不解释她会觉得系统在为难她 */
.step-why{font-size:13px;color:var(--ink-2);line-height:1.65;margin:0 0 10px}
.picked{font-size:14px;margin:0 0 4px}
.picked-sub{font-size:13px;color:var(--ink-2);margin:0 0 10px}
.warn-cell{color:var(--warning)}

.enemy{border:1px solid var(--border);border-radius:11px;padding:12px 14px;
  margin-bottom:10px;background:var(--surface);box-shadow:var(--shadow-1);
  transition:box-shadow .16s ease}
.enemy:hover{box-shadow:var(--shadow-2)}
.enemy-name{font-size:15px;font-weight:600;margin-bottom:6px}
.enemy-why{font-size:12.5px;color:var(--ink-2);margin:3px 0;line-height:1.6}
.enemy-why span{display:inline-block;min-width:5em;color:var(--muted);
  font-size:11.5px;margin-right:6px}
.enemy-open{font-size:13px;margin:7px 0;padding:6px 10px;background:var(--plane);
  border-radius:6px}

.voices summary{font-size:12.5px;color:var(--muted);cursor:pointer;margin:8px 0 0}
.voices ul{margin:8px 0 0;padding-left:1.2em;font-size:12.5px;color:var(--ink-2);
  line-height:1.7}
.voices li{margin-bottom:6px}

.outline{margin:0;padding-left:0;list-style:none;counter-reset:seg}
.outline li{border-left:2px solid var(--grid);padding:0 0 12px 13px;position:relative}
.outline li:last-child{padding-bottom:0}
.seg-sec{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums;
  margin-right:7px}
.outline strong{font-size:13.5px}
.outline p{margin:4px 0 0;font-size:13.5px;line-height:1.65}

.draft{margin:10px 0}
.titles h3{font-size:13px;margin:14px 0 6px;color:var(--ink-2)}
.title-opt{display:flex;justify-content:space-between;gap:10px;align-items:baseline;
  padding:7px 0;border-bottom:1px solid var(--grid);font-size:13.5px}
.title-opt em{color:var(--muted);font-size:11.5px;font-style:normal;flex:none}
/* 合规结论不能折叠：两次重写都拦不住时稿子照样交出来，她必须一眼看见 */
.compl-inline{margin-bottom:12px}

/* ─────────── 基因打标人工复核 ─────────── */
/* 左边是判断依据、右边是动作。窄屏堆成两行——手机上她多半是扫一遍
   然后按「这屏都对」，单条改判是桌面上的事。 */
.review-card{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap}
.review-main{display:flex;gap:10px;flex:1 1 22em;min-width:0}
.review-text{min-width:0}
.review-title{font-weight:500;line-height:1.45}
.review-why{font-size:12.5px;color:var(--ink-2);margin-top:5px}
.review-why .footnote{display:block;margin:2px 0 0}
.review-act{display:flex;gap:8px;align-items:center;flex:0 0 auto}
.review-act select{font:inherit;font-size:13px;padding:6px 9px;
  border:1px solid var(--border);border-radius:8px;
  background:var(--surface);color:var(--ink);max-width:11em}
.review-bulk{display:flex;gap:10px;align-items:center;flex-wrap:wrap;
  padding:10px 12px;margin-bottom:12px;border-radius:10px;
  background:var(--sunken);border:1px solid var(--border)}
.review-bulk .footnote{margin:0;flex:1 1 16em}
