/* 新闻质量审核台样式。
 *
 * 设计令牌与审核台布局取自 新闻质量审核.html（该文件是本页的设计基准）。
 * 评测总览页复用旧看板的 MetricsPanel / DetailGrid 组件，故这里也为它们的类名
 * （.metrics / .kpi / .detail-groups / .gates …）配了同一套令牌下的样式，
 * 而不是把旧看板的 stylesheet 原样搬来——两套视觉语言拼在一起会很割裂。
 */

:root {
  --navy-950: oklch(0.18 0.048 266);
  --navy-900: oklch(0.22 0.055 264);
  --navy-800: oklch(0.28 0.055 262);
  --blue-700: oklch(0.52 0.16 255);
  --blue-600: oklch(0.60 0.16 250);
  --blue-100: oklch(0.95 0.025 248);
  --blue-050: oklch(0.98 0.012 248);
  --ink: oklch(0.25 0.025 260);
  --ink-2: oklch(0.43 0.025 260);
  --muted: oklch(0.58 0.022 260);
  --line: oklch(0.90 0.014 255);
  --line-strong: oklch(0.83 0.022 255);
  --surface: oklch(0.995 0.003 250);
  --canvas: oklch(0.968 0.013 248);
  --green: oklch(0.55 0.135 154);
  --green-bg: oklch(0.96 0.035 153);
  --amber: oklch(0.63 0.15 73);
  --amber-bg: oklch(0.97 0.04 79);
  --red: oklch(0.56 0.19 25);
  --red-bg: oklch(0.96 0.035 25);
  --shadow: 0 14px 34px oklch(0.2 0.05 260 / 0.08);
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC", "Microsoft YaHei", "Segoe UI", system-ui, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}

* { box-sizing: border-box; }
body {
  margin: 0;
  color: var(--ink);
  background: var(--canvas);
  font: 16px/1.7 var(--font);
  -webkit-font-smoothing: antialiased;
}
button, input, textarea, select { font: inherit; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; opacity: .55; }
a { color: inherit; }
svg { display: block; }
h1, h2, h3, h4, p { margin-top: 0; }
:focus-visible { outline: 3px solid oklch(0.7 0.14 250 / 0.45); outline-offset: 2px; }
.mono { font-family: var(--mono); }

.boot, .placeholder {
  display: grid;
  place-content: center;
  gap: 8px;
  min-height: 320px;
  padding: 40px;
  color: var(--muted);
  text-align: center;
}
.placeholder h2 { margin: 0; color: var(--ink); font-size: 19px; }
.placeholder p { max-width: 46ch; margin: 0; font-size: 14px; }

.brand-mark {
  display: grid;
  width: 36px;
  height: 36px;
  place-items: center;
  border-radius: 10px;
  color: oklch(0.96 0.03 250);
  background: var(--navy-900);
  font-size: 12px;
  font-weight: 850;
  letter-spacing: .05em;
}

/* ---------------------------------------------------------------- 登录页 */
.login-page {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 24px;
  background: var(--canvas);
}
.login-card {
  display: grid;
  gap: 16px;
  width: min(420px, 100%);
  padding: 32px;
  border: 1px solid var(--line);
  border-radius: 17px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.login-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.login-brand strong { display: block; font-size: 16px; }
.login-brand span { display: block; color: var(--muted); font-size: 12px; }
.login-field { display: grid; gap: 6px; }
.login-field span { color: var(--ink-2); font-size: 13px; font-weight: 700; }
.login-field input {
  min-height: 42px;
  padding: 8px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--surface);
  color: inherit;
}
.login-field input:focus { border-color: var(--blue-600); outline: none; }
.login-error {
  padding: 10px 12px;
  border: 1px solid oklch(0.86 0.08 25);
  border-radius: 9px;
  color: var(--red);
  background: var(--red-bg);
  font-size: 13px;
}
.login-submit {
  min-height: 44px;
  border: 0;
  border-radius: 10px;
  color: oklch(0.98 0.01 250);
  background: var(--navy-900);
  font-size: 15px;
  font-weight: 800;
}
.login-submit:not(:disabled):hover { background: var(--navy-800); }
.login-note { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.6; }

/* -------------------------------------------------------------- 应用骨架 */
/* **侧边栏 232 -> 200px、左栏 320 -> 380px**（2026-08-22 用户：左栏有点被挤住）。
   省下来的 32px 加上 app-main 左右各收窄 8px，全部给了左边那个列表栏。
   200px 够不够放得下最长那两行，是算过的（都按 14px 粗体、汉字按 1em 估）：
     · 品牌行「新闻质量审核台」= 36(方块) + 11(gap) + 7 字 ≈ 98  → 145px
     · 最长导航项「AI 默认已发布」= 22(左右内边距) + 17(图标) + 10(gap) + ≈100 → 149px
   内容宽 = 200 - 14×2 = 172px，两者都还有 20px 以上的余量，不会折行。
   **再窄就要动字号了** —— 那不是「窄一点」，那是「看起来更挤」。 */
.app-shell { display: grid; grid-template-columns: 200px minmax(0, 1fr); min-height: 100vh; }
.app-main { min-width: 0; padding: 28px clamp(18px, 2vw, 32px) 72px; }

/* 页面右上角的内容语言切换。放在 app-main 顶部而不是各页自己的标题栏里 ——
   三个页面都要它，散在各页迟早会漂成三个位置。 */
.app-topbar { display: flex; justify-content: flex-end; margin: -6px 0 14px; }
.lang-switch { display: inline-flex; align-items: center; gap: 6px; }
.lang-switch > span { margin-right: 2px; color: var(--muted); font-size: 11px; font-weight: 750; }
.lang-switch button {
  min-width: 46px;
  min-height: 28px;
  padding: 3px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  color: var(--ink-2);
  background: var(--surface);
  font-size: 12px;
  font-weight: 750;
}
.lang-switch button:hover { border-color: var(--blue-600); color: var(--blue-700); }
.lang-switch button.on { border-color: var(--blue-600); color: var(--blue-700); background: var(--blue-050); }

/* 未登录读者那组文案里的行内 markdown（见 markdown.js）。
   实体标记 `[名称]()` 没有可跳转的地址，所以渲染成有样式的名字、**不是链接**。 */
.md-entity { color: var(--blue-700); font-weight: 700; }

.sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 20px;
  height: 100vh;
  padding: 20px 14px;
  color: oklch(0.93 0.012 250);
  background: var(--navy-950);
}
.sidebar .brand { display: flex; align-items: center; gap: 11px; }
.sidebar .brand strong { display: block; font-size: 14px; line-height: 1.3; }
.sidebar .brand span { display: block; color: oklch(0.70 0.025 250); font-size: 11px; }
.sidebar .brand-mark { background: oklch(0.34 0.08 255); }

.side-nav { display: grid; gap: 4px; }
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  padding: 8px 11px;
  border: 0;
  border-radius: 9px;
  color: oklch(0.80 0.025 250);
  background: transparent;
  font-size: 14px;
  font-weight: 700;
  text-align: left;
}
.nav-item svg { width: 17px; height: 17px; flex: 0 0 auto; }
.nav-item:hover { color: oklch(0.96 0.02 250); background: oklch(0.28 0.05 260); }
.nav-item.on { color: oklch(0.97 0.02 250); background: oklch(0.34 0.08 255); }

.side-foot { margin-top: auto; display: grid; gap: 12px; }
.reviewer-session { display: flex; align-items: center; gap: 10px; }
.reviewer-avatar {
  display: grid;
  width: 32px;
  height: 32px;
  place-items: center;
  border-radius: 8px;
  color: oklch(0.92 0.09 250);
  background: oklch(0.34 0.08 255);
  font-size: 11px;
  font-weight: 850;
}
.reviewer-session b { display: block; font-size: 13px; }
.reviewer-session span { display: block; margin-top: 1px; color: oklch(0.72 0.025 250); font-size: 11px; }
.logout {
  min-height: 34px;
  border: 1px solid oklch(0.36 0.04 260);
  border-radius: 8px;
  color: oklch(0.84 0.02 250);
  background: transparent;
  font-size: 12px;
  font-weight: 700;
}
.logout:hover { border-color: oklch(0.50 0.06 258); background: oklch(0.26 0.05 262); }

/* -------------------------------------------------------- 工作区（两栏） */
/* 左栏上限 320 -> 380px（2026-08-22）。**要它变宽的是四档筛选与日期下拉那两行**：
   四个按钮（全部 / 普通候选 / 精选候选 / 当前置顶）在 292px 的内容宽里必须折成两行，
   而日期下拉那一项「20260822（124 条 · 另 871 条还没有洞察正文）」是一个原生 select
   —— 它的选中文本**不会换行、只会被裁掉**，读到一半没了比折行更糟。
   380px 时内容宽 = 380 - 14×2 = 352px：四个按钮一行放得下（最宽那个「当前置顶 124」
   约 78px，4×78 + 3×6 = 330），下拉那串按 12px 混排估 ≈ 280px + 箭头，也进得去。
   **中栏几乎没有损失**：侧边栏省了 32、app-main 左右各省 8、栏间距省 4，加起来 52px，
   而左栏只多要 60px。1440px 视口实测算下来中栏 794 -> 784。 */
.workspace { display: grid; grid-template-columns: minmax(300px, 380px) minmax(0, 1fr); gap: 18px; }
/* **min-height 是为了页面别忽长忽短**（2026-08-21）：左栏是 sticky 且
   max-height: calc(100vh - 56px)，中栏换条新闻时会短暂变成一个很矮的占位块 ——
   整页高度缩下去、滚动条跳一下、内容装回来又跳回来。把中栏的下限定成和左栏一样高，
   页面高度就不会因为中栏在换内容而变化。 */
.workspace-main { min-width: 0; min-height: calc(100vh - 56px); display: grid; gap: 16px; align-content: start; }

/* 内容换进来时的一次淡入。**只给 120ms 的透明度**，不做位移 —— 位移会把「页面在动」
   这件事放大，而要解决的正是它。尊重 prefers-reduced-motion（下面统一关掉）。 */
@keyframes detail-in { from { opacity: 0; } to { opacity: 1; } }
.workspace-main > .review-shell { animation: detail-in 120ms ease-out; }

@media (prefers-reduced-motion: reduce) {
  .workspace-main > .review-shell { animation: none; }
}

.banner {
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 13px;
  font-weight: 650;
}
.banner.ok { border-color: oklch(0.84 0.08 153); color: oklch(0.40 0.10 154); background: var(--green-bg); }
.banner.no { border-color: oklch(0.86 0.08 25); color: var(--red); background: var(--red-bg); }

/* ------------------------------------------------------------ 待审核列表 */
.pending {
  position: sticky;
  top: 24px;
  align-self: start;
  overflow: hidden;
  max-height: calc(100vh - 56px);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.pending-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
}
.pending-head h2 { margin: 0; font-size: 15px; }
.pending-count { color: var(--muted); font-size: 12px; }
.pending-empty { padding: 28px 16px; color: var(--muted); font-size: 13px; text-align: center; }
.pending-empty small { display: block; margin-top: 8px; font-size: 11px; line-height: 1.6; }
.pending-list { overflow-y: auto; flex: 1; margin: 0; padding: 0; list-style: none; }

.pending-item {
  display: grid;
  gap: 6px;
  width: 100%;
  padding: 12px 16px;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-left: 3px solid transparent;
  background: transparent;
  text-align: left;
}
/* 本次会话已审过（2026-08-26）。**左边一道竖线 + 标题压暗**，不加徽标 ——
   那一行已经有精选/名次/状态/风险四格，再挤一个只会更难扫；而「我审到哪儿了」
   要的是扫一眼看出边界。选中态（.on）的竖线优先，因为「正在看哪条」更要紧。 */
/* 本次会话**打开看过**的（比 is-done 弱一档：只压暗标题，不给竖线）。
   「新」徽标按快照口径刻意不随点开消失，所以「我读到哪儿了」靠这一格答。
   写在 is-done 之前：同一行两个类都在时，动过手的那一档该赢。 */
.pending-item.is-read .pi-title { color: var(--ink-2); font-weight: 600; }
.pending-item.is-done { border-left-color: var(--green); }
.pending-item.is-done .pi-title { color: var(--muted); }
.pending-item:hover { background: var(--blue-050); }
.pending-item.on { border-left-color: var(--blue-600); background: var(--blue-050); }
.pi-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.pi-title {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  color: var(--ink);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.5;
  /* **长 token 必须能从中间断开。** 不给这一条时，`37K views · 168 reactions | Watch |
     Heavy rain...` 这种没有空格可断的长串会直接把行撑出容器 —— `overflow: hidden`
     把它裁掉，所以桌面上看不出来，但**容器宽度已经被撑开了**：390px 手机上实测
     这一行自身溢出 26px。真实库里最长的标题 512 字符、还有整段没有空格的。
     `anywhere` 而不是 `break-word`：后者只在「整行放不下一个词」时才断，而这里是
     「一个词比整行还长」之外的普通挤压也需要断。 */
  overflow-wrap: anywhere;
}
.pi-title em { color: var(--muted); font-weight: 500; }
.pi-meta { display: flex; flex-wrap: wrap; gap: 10px; color: var(--muted); font-size: 11px; }
/* 「新」徽标（2026-08-26）。**蓝色实底，是这一行最该被先看到的东西** ——
   它回答的是「这一条要不要看」，比名次、时间都更靠前。 */
.pi-new { padding: 1px 6px; border-radius: 5px; color: #fff; background: var(--blue-600); font-size: 10px; font-weight: 800; }
/* 「只看未看过」开关（2026-08-26；当天按用户反馈改过一次外观）。
   **单独一行**，与四档那一排刻意分开 —— 那一排是互斥的一维（全部 = 普通 + 精选 + 已退回），
   这是叠在其上的另一维，混进那一排会让那条恒等式在界面上说不通。
   外观与四档共用同一套胶囊（用户原话：原生 checkbox「有点原始」），
   但**不拉伸**（`flex: 0 0 auto`）：它不是一档，不该跟那四个平分宽度。
   上下留白比那一排大一点（10px / 12px），把「另一维」这件事用间距说出来 ——
   贴着放就是用户说的「有点挤」。 */
.queue-toggle { display: flex; align-items: center; gap: 8px; padding: 10px 14px 2px; }
.queue-toggle button {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 4px 11px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--ink-2);
  background: var(--surface);
  font-size: 11.5px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* 勾在未选中时只留一个轮廓般的淡影 —— 让「这是个开关」看得出来，又不抢注意力 */
.queue-toggle button svg { width: 13px; height: 13px; opacity: .35; }
.queue-toggle button:hover:not(:disabled) { border-color: var(--blue-600); color: var(--blue-700); }
.queue-toggle button.on { border-color: var(--blue-600); color: var(--blue-700); background: var(--blue-050); }
.queue-toggle button.on svg { opacity: 1; }
.queue-toggle button:disabled { color: var(--muted); background: var(--canvas); cursor: not-allowed; }
.queue-toggle em { color: var(--muted); font-size: 11px; font-style: normal; }
/* 下架理由（2026-08-26）。**整段显示、能换行** —— 「为什么撤」是已下架页存在的理由，
   截一半等于让人去别处查。`overflow-wrap: anywhere` 与 .pi-title 同一条理由：
   理由是人手打的，出现一长串没有空格的东西完全可能。 */
.pi-reason { margin-top: 6px; padding: 6px 8px; border-left: 2px solid var(--line-strong); border-radius: 0 6px 6px 0; background: var(--canvas); color: var(--ink-2); font-size: 11.5px; line-height: 1.6; overflow-wrap: anywhere; }
.pi-reason b { display: block; margin-bottom: 2px; color: var(--muted); font-size: 10.5px; font-weight: 750; }
.pi-noimg { color: var(--muted); }

.status-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 99px;
  background: var(--blue-050);
  color: var(--ink-2);
  font-size: 11px;
  font-weight: 750;
  white-space: nowrap;
}
.status-badge.s10 { border-color: var(--line-strong); color: var(--muted); }
.status-badge.s20 { border-color: oklch(0.87 0.08 78); color: var(--amber); background: var(--amber-bg); }
.status-badge.s30 { border-color: oklch(0.84 0.08 153); color: var(--green); background: var(--green-bg); }
.status-badge.s40 { border-color: oklch(0.86 0.08 25); color: var(--red); background: var(--red-bg); }
/* 「已退回」不是 publish_status 的取值（它库里就是 10=草稿），所以不走 .sNN 那套类名。
   给琥珀色而不是红：红已经是「已下架」，两者都用红，扫一眼分不出哪个是对外出过事。 */
.status-badge.rejected { border-color: oklch(0.87 0.08 78); color: var(--amber); background: var(--amber-bg); }
/* 「待审核」是这个队列的常态（2026-08-22 实测 5630 条草稿全是它），给中性灰、不给琥珀
   —— 琥珀留给「已退回」，两者同色就分不出来了。取的就是原来 .audit-badge.todo 那套颜色。
   **不走 .sNN**：这个词盖住 publish_status 10 与 20 两个值，不对应任何单一取值。 */
.status-badge.todo { border-color: var(--line-strong); color: var(--muted); background: var(--canvas); }

.risk-pill { border-radius: 99px; padding: 2px 8px; font-size: 11px; font-weight: 750; white-space: nowrap; }
.risk-pill.none { color: var(--muted); background: var(--canvas); }
.risk-pill.low { color: var(--green); background: var(--green-bg); }
.risk-pill.mid { color: var(--amber); background: var(--amber-bg); }
.risk-pill.high { color: var(--red); background: var(--red-bg); }

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 12px;
}
.pager button {
  min-height: 30px;
  padding: 4px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink-2);
  font-size: 12px;
  font-weight: 700;
}
.pager button:not(:disabled):hover { border-color: var(--blue-600); background: var(--blue-050); }

/* ------------------------------------------------------------ 审核台主体 */
.review-shell {
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(340px, .68fr);
  border: 1px solid var(--line);
  border-radius: 17px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.review-authbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 13px 20px;
  border-bottom: 1px solid oklch(0.30 0.045 260);
  color: oklch(0.93 0.012 250);
  background: var(--navy-950);
}
.session-security { display: flex; align-items: center; gap: 8px; color: oklch(0.76 0.035 250); font-size: 11px; }
.session-security::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: oklch(0.72 0.15 153); }

.review-main { min-width: 0; padding: clamp(20px, 2.5vw, 32px); }
.review-side {
  min-width: 0;
  padding: clamp(20px, 2.5vw, 28px);
  border-left: 1px solid var(--line);
  background: var(--blue-050);
}
.review-side h2 { font-size: 17px; }
.review-side > p { color: var(--muted); font-size: 13px; }

.review-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; margin-bottom: 20px; }
.review-top h2 { max-width: 34ch; margin: 8px 0 0; font-size: 21px; line-height: 1.45; }
.review-top h2 em { color: var(--muted); font-weight: 500; }
.review-top-tools { display: flex; flex: 0 0 auto; flex-direction: column; align-items: flex-end; gap: 9px; }
.original-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  color: var(--blue-700);
  background: var(--surface);
  font-size: 12px;
  font-weight: 750;
  text-decoration: none;
  white-space: nowrap;
}
.original-link:hover { border-color: var(--blue-600); background: var(--blue-050); }
.original-link svg { width: 14px; height: 14px; }
.risk-score { min-width: 76px; padding: 9px 10px; border-radius: 11px; color: var(--red); background: var(--red-bg); text-align: center; }
.risk-score b { display: block; font-size: 21px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.risk-score span { font-size: 11px; }

.model-strip { display: grid; grid-template-columns: repeat(3, 1fr); overflow: hidden; margin: 20px 0; border: 1px solid var(--line); border-radius: 11px; }
.model-strip.empty {
  display: block;
  padding: 13px 15px;
  color: var(--muted);
  background: var(--canvas);
  font-size: 12px;
}
.model { padding: 14px; border-right: 1px solid var(--line); min-width: 0; }
.model:last-child { border-right: 0; }
.model span { display: block; overflow: hidden; color: var(--muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.model b { display: flex; align-items: center; gap: 7px; margin-top: 4px; font-size: 14px; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.model.pass b { color: var(--green); }
.model.fail b { color: var(--red); }

/* --------------------------------------------------------------- 配图核对 */
.image-panel { margin: 20px 0; padding: 16px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.image-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
.image-head h3 { margin: 0; font-size: 15px; }
.image-score { padding: 3px 10px; border-radius: 99px; font-size: 12px; font-weight: 750; white-space: nowrap; }
.image-score.ok { color: var(--green); background: var(--green-bg); }
.image-score.mid { color: var(--amber); background: var(--amber-bg); }
.image-score.bad { color: var(--red); background: var(--red-bg); }
.image-empty { color: var(--muted); font-size: 12.5px; line-height: 1.65; }

.image-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.image-cell { margin: 0; }
.image-zoom {
  display: block;
  overflow: hidden;
  width: 100%;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--canvas);
}
.image-zoom:hover { border-color: var(--blue-600); }
.image-zoom img { display: block; width: 100%; height: 148px; object-fit: contain; background: var(--canvas); }
.image-cell figcaption { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-top: 6px; }
.image-cell figcaption b { font-size: 12px; }
.image-cell figcaption span { overflow: hidden; color: var(--muted); font-size: 10.5px; text-overflow: ellipsis; white-space: nowrap; }
.image-links { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; }
.image-links a { color: var(--blue-700); font-size: 12px; font-weight: 700; text-decoration: none; }
.image-links a:hover { text-decoration: underline; }

.image-lightbox {
  position: fixed;
  z-index: 60;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 40px;
  background: oklch(0.18 0.048 266 / 0.82);
}
.image-lightbox img { max-width: min(1100px, 92vw); max-height: 88vh; border-radius: 10px; background: var(--surface); }
.lightbox-close {
  position: absolute;
  top: 20px;
  right: 24px;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 10px;
  color: oklch(0.96 0.01 250);
  background: oklch(0.30 0.05 262);
  font-size: 22px;
  line-height: 1;
}

.compare-block { margin: 22px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }.compare-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 13px 0; }
.compare-head h3 { margin: 0; font-size: 15px; }
.compare-head span { color: var(--muted); font-size: 12px; }
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; border-top: 1px solid var(--line); }
.compare-copy { min-width: 0; padding: 17px 18px 19px 0; }
.compare-copy + .compare-copy { padding-right: 16px; padding-left: 18px; border-left: 1px solid var(--line); background: var(--blue-050); }
/* 必须是直接子元素：差异标记会在 <p> 里生成大量 <span> 片段，若这条规则命中它们，
   正文就变成一堆 11px、全大写、块级的碎块（栏标题才是 .compare-copy 的直接子 span）。 */
.compare-copy > span { display: block; margin-bottom: 8px; color: var(--muted); font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
/* pre-wrap：结构化正文摊平后用 \n\n 分区块，不保留换行就会把区块标题和正文黏成一句 */
.compare-copy p { margin: 0; overflow-wrap: anywhere; white-space: pre-wrap; color: var(--ink-2); font-size: 14px; line-height: 1.8; }
/* 正文装进定高滚动框。**这一块占多高与正文多长无关** —— 原先「展开全文」会把两三千字
   整段铺进页面，详情页被拉到四五屏，右栏那些要动手的控件被推到看不见的地方。
   高度用 clamp 跟着屏幕走：小屏不至于只剩三行，大屏也不让它独占整页。 */
.compare-scroll {
  max-height: clamp(220px, 44vh, 560px);
  overflow-y: auto;
  /* 滚到底不要顺势把外层页面一起带着滚——那会让人以为自己滚错了地方 */
  overscroll-behavior: contain;
  /* 留出滚动条的位置，别等它出现时把文字挤动一下 */
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  padding-right: 4px;
}
.compare-scroll:focus-visible { outline: 2px solid var(--blue-600); outline-offset: 3px; }
.compare-scroll::-webkit-scrollbar { width: 8px; }
.compare-scroll::-webkit-scrollbar-track { background: transparent; }
.compare-scroll::-webkit-scrollbar-thumb { border: 2px solid transparent; border-radius: 99px; background: var(--line-strong); background-clip: content-box; }
.compare-scroll:hover::-webkit-scrollbar-thumb { background: var(--muted); background-clip: content-box; }
/* 字数放在栏标题里：滚动框自己看不出总长，而「这稿子有多长」是审核前就该知道的 */
.compare-copy .chars { margin-left: 8px; color: var(--muted); font-size: 10.5px; font-style: normal; font-weight: 700; letter-spacing: 0; text-transform: none; }
.compare-copy p.missing { color: var(--muted); font-style: italic; }
.compare-copy .more { display: block; margin-top: 6px; color: var(--muted); font-size: 11px; font-style: normal; }

.dispute {
  margin: 16px 0;
  padding: 15px 17px;
  border: 1px solid oklch(0.88 0.07 75);
  border-radius: 11px;
  color: oklch(0.42 0.07 70);
  background: var(--amber-bg);
  font-size: 13.5px;
}
.dispute b { display: block; margin-bottom: 5px; color: var(--amber); }
.dispute-code { margin-left: 7px; opacity: .62; font-family: var(--mono); font-size: 11.5px; font-weight: 500; }
.dispute.hard-note { border-color: var(--line-strong); color: var(--ink-2); background: var(--canvas); }
.dispute.hard-note b { color: var(--ink); }
.dispute-evidence { margin: 8px 0 0; padding-left: 18px; font-size: 12.5px; }

/* --------------------------------------------------------------- 标签编辑 */
.metadata-review { margin-top: 22px; border-top: 1px solid var(--line-strong); border-bottom: 1px solid var(--line-strong); }
.metadata-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 0 12px; }
.metadata-head h3 { margin: 0; font-size: 15px; }
.metadata-head span { color: var(--muted); font-size: 12px; }
.metadata-row { display: grid; grid-template-columns: 130px minmax(0, 1fr) auto; gap: 16px; align-items: start; padding: 15px 0; border-top: 1px solid var(--line); }
.metadata-title b { display: block; margin-bottom: 3px; font-size: 14px; }
.metadata-title small { display: block; color: var(--muted); font-size: 11px; line-height: 1.5; }
.metadata-content { min-width: 0; }
.token-list { display: flex; flex-wrap: wrap; gap: 7px; min-height: 31px; }
.token-empty { color: var(--muted); font-size: 12px; line-height: 31px; }
.meta-token {
  display: inline-flex;
  align-items: center;
  min-height: 31px;
  gap: 7px;
  padding: 4px 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink-2);
  background: var(--blue-050);
  font-size: 12px;
}
.meta-token em { color: var(--blue-700); font-size: 10px; font-style: normal; font-weight: 800; }
.meta-token.is-disabled { border-style: dashed; color: var(--muted); background: var(--canvas); }
.meta-token.is-disabled em { color: var(--muted); }
.token-remove { display: none; width: 19px; height: 19px; place-items: center; padding: 0; border: 0; border-radius: 5px; color: var(--muted); background: transparent; font: 700 14px/1 var(--font); }
.metadata-row.editing .token-remove { display: grid; }
.token-remove:hover { color: var(--red); background: var(--red-bg); }
/* 正文里识别到、但还没落成关联的实体上那个「+」：一键关联过来。
   只在匹配到稳定 id 且未停用时渲染（没 id 的加不进关联表，规格 :511） */
.token-link { display: grid; width: 19px; height: 19px; place-items: center; padding: 0; border: 0; border-radius: 5px; color: var(--blue-700); background: var(--blue-100); font: 700 14px/1 var(--font); }
.token-link:hover { color: oklch(0.99 0.004 250); background: var(--blue-600); }
/* 实体行没有右侧那颗「编辑」按钮（搜索框常摊开：那一行默认态本来就没东西可看，
   折起来等于什么都不给），所以只有两列 —— 留着三列的定义会在右边多出一个 16px 的空 gap。
   **标签行仍是三列**：它有「编辑 / 收起」按钮，默认只列这条新闻已有的标签。 */
.metadata-row[data-kind="entity"] { grid-template-columns: 130px minmax(0, 1fr); }
/* 「搜不到 + 新建」是下拉里的一行，不是输入框下面另起的一块。
   两个原因：① 位置和体量与候选行一致，敲字时列表只换内容，不会长出一块又缩回去；
   ② .meta-input-row 是 flex，当兄弟节点放进去会和输入框争宽度，把输入框压成一条竖条。
   底色用 amber：新建不是普通的「点选候选」，而是一笔立刻生效、审核台撤不回来的写入。 */
.ent-nohit { border-top: 1px solid var(--line); background: var(--amber-bg); color: var(--ink-2); }
.tag-suggestions li:first-child .ent-nohit, .ent-suggestions > .ent-nohit:first-child { border-top: 0; }
.ent-nohit > span { display: block; font-weight: 700; }
.ent-nohit small { color: var(--muted); }
.ent-nohit-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 7px; }
/* **必须比 `.tag-suggestions button` 更具体。** 那条规则是 (0,1,1)（一个类 + 一个类型），
   会把 width:100% / background:transparent 压到这两颗按钮上 —— 实测的表现是「新建」按钮
   白字压在浅底上、几乎看不见，而且被拉满整行宽度。所以这里一律写成两个类 (0,2,0)：
   类计数优先比较，2 > 1，稳定胜出。**别把 .ent-nohit-actions 前缀去掉。** */
.ent-nohit-actions .ent-create-go,
.ent-nohit-actions .ent-create-cancel {
  display: inline-flex;
  width: auto;
  min-height: 28px;
  padding: 4px 12px;
  border-radius: 7px;
  font-size: 11.5px;
  font-weight: 750;
}
.ent-nohit-actions .ent-create-go { border: 0; color: oklch(0.98 0.01 250); background: var(--navy-900); }
.ent-nohit-actions .ent-create-go:not(:disabled):hover { background: var(--navy-800); }
.ent-nohit-actions .ent-create-go:disabled { opacity: .6; }
.ent-nohit-actions .ent-create-cancel { border: 1px solid var(--line-strong); color: var(--ink-2); background: var(--surface); }
.ent-nohit-actions .ent-create-cancel:not(:disabled):hover { border-color: var(--blue-600); background: var(--blue-050); }

/* 标签行与实体行右侧的「编辑 / 完成」。规格 docs/新闻质量审核.html:252-253 里有这条
   规则，重写前端时漏搬了，所以一直是浏览器默认按钮的样子 */
.meta-edit-button {
  align-self: start;
  min-height: 32px;
  padding: 5px 11px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  color: var(--blue-700);
  background: var(--surface);
  font-size: 12px;
  font-weight: 750;
  white-space: nowrap;
}
.meta-edit-button:hover { border-color: var(--blue-600); background: var(--blue-050); }
/* 编辑态下按钮本身也要变样：它此刻的文案是「完成」，得看得出处于打开状态 */
.metadata-row.editing .meta-edit-button { color: var(--ink); background: var(--blue-100); border-color: var(--blue-600); }

.meta-input-row { position: relative; display: flex; gap: 8px; margin-top: 10px; }
.meta-input-row input {
  flex: 1;
  min-width: 0;
  min-height: 34px;
  padding: 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  font-size: 13px;
}
.meta-input-row input:focus { border-color: var(--blue-600); outline: none; }
/* 标签全览：一行一类，全部官方标签铺开。**已选实心、未选描边淡色**（用户口径：
   「新闻中的标签高亮显示，其他置灰」）。置灰只是视觉弱化，仍然可点 —— 真禁用等于把
   编辑器变成只读，那不是「全部列出来让人挑」的意思。
   长尾标签不进这个网格（61 条只有英文名且持续增长，会把 20 条常用的淹掉），
   下面那个搜索框才是它们的入口。 */
.tag-browse { display: grid; gap: 9px; margin-top: 10px; }
.tag-row { display: grid; grid-template-columns: 62px minmax(0, 1fr); gap: 10px; align-items: start; }
.tag-row-label { padding-top: 4px; color: var(--muted); font-size: 11.5px; font-weight: 800; }
.tag-row-label i { display: block; margin-top: 1px; font-size: 10.5px; font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums; }
.tag-row-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.tag-chip {
  padding: 3px 9px;
  border: 1px dashed var(--line-strong);
  border-radius: 99px;
  color: var(--muted);
  background: transparent;
  font-size: 11.5px;
  font-weight: 650;
}
.tag-chip:hover { border-style: solid; border-color: var(--blue-600); color: var(--blue-700); background: var(--blue-050); }
/* 已选：实心 + 实线，和未选拉开一整个层级 —— 只靠颜色深浅区分，扫一屏认不出哪些选了 */
.tag-chip.on {
  border-style: solid;
  border-color: var(--blue-600);
  color: oklch(0.99 0.004 250);
  background: var(--blue-600);
  font-weight: 750;
}
.tag-chip.on:hover { border-color: var(--red); background: var(--red); }

.tag-suggestions {
  position: absolute;
  z-index: 5;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  overflow: hidden;
  margin: 0;
  padding: 4px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow);
  list-style: none;
}
.tag-suggestions button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  font-size: 13px;
  text-align: left;
}
.tag-suggestions button:hover { background: var(--blue-050); }
.tag-suggestions em { color: var(--blue-700); font-size: 10px; font-style: normal; font-weight: 800; }
.tag-suggestions .long-tail { margin-left: auto; color: var(--muted); font-size: 10px; font-style: normal; }
/* 「搜不到」是下拉里的一行，几何跟候选行对齐（padding 与 .tag-suggestions button 一致），
   这样敲字时列表只是换内容，不会整块长出来又缩回去 */
.tag-nohit { padding: 9px 11px; color: var(--muted); font-size: 12px; }
.tag-nohit small { display: block; margin-top: 2px; font-size: 11px; opacity: .85; }

.metadata-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0 14px; color: var(--muted); font-size: 11.5px; }
.metadata-foot strong { color: var(--blue-700); }

/* 实体 token 上的辅助信息。库里 237 组同名共 488 行，而 city_id 0% / website 5.8%
   —— 能用来区分的字段几乎全空，所以国家、命中别名、id 前缀、同名条数都要露出来 */
.ent-meta, .ent-alias, .ent-id { color: var(--muted); font-size: 10px; font-style: normal; }
.ent-alias { color: var(--blue-700); }
.ent-warn { color: var(--amber); font-size: 10px; font-style: normal; font-weight: 700; }
.ent-suggestions button { flex-wrap: wrap; }
.ent-suggestions .ent-id { margin-left: auto; }

/* 实体的两个来源要分栏、分色，别让人以为是同一份数据：
   正文识别 = news_lang.content 里 AI 注入的 `[名称]()`（只读）
   已入库关联 = news_entity_rel（审核台写的那张） */
.ent-source { padding-bottom: 4px; }
.ent-source-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 2px 0 7px; }
.ent-source-head b { font-size: 12.5px; }
.ent-source-head small { color: var(--muted); font-size: 11px; }
.ent-source-head code { color: var(--ink-2); font-size: 10.5px; }
.ent-source + .ent-source-head { margin-top: 12px; padding-top: 11px; border-top: 1px dashed var(--line-strong); }
/* 正文来的 token 用实线蓝边区分于关联表来的 */
.meta-token.from-content { border-color: oklch(0.86 0.07 250); }
/* 正文里有这个名字、实体库里没有 —— 这条要报给数据部，不能悄悄当成识别到了 */
.meta-token.is-unmatched { border-style: dashed; border-color: var(--amber); background: var(--amber-bg); }
.meta-token.is-unmatched em { color: var(--amber); }
.ent-linked, .ent-unlinked { font-size: 10px; font-style: normal; font-weight: 700; }
.ent-linked { color: var(--green); }
.ent-unlinked { color: var(--muted); }
/* 审核台新建、尚待数据部复核的实体。**要和 is-disabled 明显不同**：它当场能关联，
   只是还没进入其他新闻的正文识别词典 —— 画成置灰虚线会让人以为建坏了又把它移掉。 */
.ent-pending { padding: 1px 5px; border-radius: 5px; color: oklch(0.36 0.09 73); background: var(--amber-bg); font-size: 10px; font-style: normal; font-weight: 750; }
.meta-token.is-pending { border-color: var(--amber); background: oklch(0.99 0.014 79); }
/* 只读档的说明徽标：功能已上线但写入未开启，必须在页面上说清，不能只是禁用控件 */
.mode-badge, .lang-badge, .shape-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 5px;
  color: var(--muted);
  background: var(--canvas);
  font-size: 10px;
  font-style: normal;
  font-weight: 700;
}
.shape-badge.ok { color: var(--blue-700); border-color: var(--blue-700); background: var(--blue-050); }
.shape-badge.warn { color: var(--amber); border-color: var(--amber); background: var(--amber-bg); }

/* ------------------------------------------------- 行业影响与分身份影响 */
.impact-review { margin-top: 22px; border-top: 1px solid var(--line-strong); border-bottom: 1px solid var(--line-strong); }
/* 头部右侧那串：摘要 + 徽标 + 展开/收起按钮 */
.impact-review .metadata-head > span { display: inline-flex; align-items: center; gap: 6px; }
/* 收起时也要看得出里面有什么，否则每条都得点开一次才知道是不是空的 */
.impact-summary { color: var(--muted); font-size: 11.5px; font-style: normal; }
.impact-empty { padding: 14px 0 16px; color: var(--muted); font-size: 12.5px; }
.impact-line { padding: 12px 0; border-top: 1px solid var(--line); }
/* 宏观一句话是旁证、不是审核对象，视觉上要弱一档并留出缩进 */
.impact-line.aside { padding-left: 11px; border-left: 3px solid var(--line-strong); border-top: 0; }
.impact-line.aside b { color: var(--muted); font-size: 11.5px; font-weight: 750; }
.impact-line.aside p { color: var(--ink-2); font-size: 12.5px; }
.impact-line b { display: block; margin-bottom: 5px; font-size: 13px; }
.impact-line p { margin: 0; line-height: 1.75; white-space: pre-wrap; }
.impact-line .muted, .impact-review .muted { color: var(--muted); }
/* 折叠按钮要贴到最右边，所以标题行留一个 auto 的伸缩位 */
/* 整行可点（展开 / 收起）。行尾那颗按钮仍是真正的无障碍入口，这里只是把鼠标的命中区
   放大到整行 —— 一行里只有右端一颗小按钮能点，是很容易反复点空的。
   高亮用 box-shadow 往外扩，不用 padding：改 padding 会让行高在 hover 时跳一下。 */
.expandable { cursor: pointer; border-radius: 8px; }
.expandable:hover { background: var(--blue-050); box-shadow: 0 0 0 8px var(--blue-050); }
.impact-sub { display: flex; align-items: center; gap: 10px; padding: 12px 0 8px; }
.impact-sub .meta-edit-button { margin-left: auto; }
.impact-sub b { font-size: 13px; }
.impact-sub small { color: var(--muted); font-size: 11px; }
.impact-roles, .impact-nologin { border-top: 1px solid var(--line); }
.impact-card { margin-bottom: 8px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--blue-050); }
/* 勾了「这条有问题」的卡片要一眼看出来——提交后它的 code 会进 targets */
.impact-card.flagged { border-color: var(--amber); background: var(--amber-bg); }
.impact-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 6px; }
.impact-card-head b { font-size: 13px; }
.impact-card-head code { color: var(--muted); font-size: 10.5px; }
.impact-card p { margin: 0; line-height: 1.75; white-space: pre-wrap; }
.impact-label { padding: 1px 7px; border-radius: 5px; color: var(--blue-700); background: #fff; font-size: 10px; font-style: normal; font-weight: 700; }
/* 不在 schema.py 枚举里的 label：原样展示成中性徽标，不吞不猜 */
.impact-label.unknown { color: var(--amber); }
.impact-flag { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; color: var(--ink-2); font-size: 11.5px; }
.impact-order { margin-top: 7px; }
.impact-order button, .impact-raw button {
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--muted);
  background: transparent;
  font: 600 11px var(--font);
}
.impact-nologin ul { margin: 0; padding-left: 20px; }
/* pre-wrap 是必须的：实测 noLogin 里除了每身份一句话，还混着多行的免责声明块
   （"- Scope and time window…\n- The platform provides…"），不保留换行会挤成一行 */
.impact-nologin li { margin-bottom: 6px; line-height: 1.75; white-space: pre-wrap; }
.impact-warn { margin: 8px 0; padding: 9px 11px; border-left: 3px solid var(--amber); border-radius: 0 7px 7px 0; background: var(--amber-bg); font-size: 11.5px; line-height: 1.7; }
.impact-raw { padding: 10px 0; }
.impact-raw pre { max-height: 320px; margin: 8px 0 0; overflow: auto; padding: 10px; border-radius: 7px; background: var(--canvas); font-size: 11px; line-height: 1.6; white-space: pre-wrap; word-break: break-all; }
.impact-verdict { padding: 4px 0 14px; border-top: 1px solid var(--line); }
.impact-choices { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.impact-choices label { display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px; border: 1px solid var(--line); border-radius: 8px; font-size: 12px; }
.impact-choices label.on { border-color: var(--blue-700); color: var(--blue-700); background: var(--blue-050); }

.source-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 18px; color: var(--muted); font-size: 11.5px; }

/* ----------------------------------------------------------- 右侧判断栏 */
.review-field { margin-top: 18px; }
.review-label { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 7px; }
.review-label span { font-size: 13px; font-weight: 750; }
.review-label small { color: var(--muted); font-size: 11px; }
.review-label small.optional { color: var(--blue-700); }
.review-textarea {
  width: 100%;
  min-height: 108px;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  color: inherit;
  font-size: 13.5px;
  line-height: 1.7;
  resize: vertical;
}
.review-textarea:focus { border-color: var(--blue-600); outline: none; }
.review-field-meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 6px; color: var(--muted); font-size: 11px; }
.review-field-meta output { font-variant-numeric: tabular-nums; }

.review-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 20px; }
.review-actions button { min-height: 44px; border-radius: 10px; font-size: 14px; font-weight: 800; }
/* 「这条通过不了」的说明块（正文为空）。**放在按钮正上方**：它要回答的是
   「为什么这个按钮点不动」，离按钮越远越像一句无关的提示。
   琥珀而不是红：这不是审核人做错了什么，是这一行还没准备好。 */
.approve-block { display: flex; gap: 8px; align-items: flex-start; margin: 0 0 10px; padding: 9px 11px; border: 1px solid oklch(0.86 0.07 79); border-radius: 10px; color: oklch(0.42 0.09 73); background: var(--amber-bg); font-size: 12px; line-height: 1.65; }
.approve-block svg { flex: none; width: 15px; height: 15px; margin-top: 2px; }

.review-actions .reject { border: 1px solid oklch(0.86 0.08 25); color: var(--red); background: var(--red-bg); }
.review-actions .reject:not(:disabled):hover { background: oklch(0.93 0.055 25); }
.review-actions .approve { border: 0; color: oklch(0.98 0.01 250); background: var(--navy-900); }
.review-actions .approve:not(:disabled):hover { background: var(--navy-800); }

.approve-confirm { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .2s ease; }
.approve-confirm.open { grid-template-rows: 1fr; }
/* 收起时把「确认通过并发布」从焦点序列里摘掉。高度动画（0fr）只是把内容压扁，
   里面的按钮仍在 Tab 序列里 —— 漏掉的后果是回车直接发布，绕过这一屏唯一的二次确认。
   JSX 那边已有 inert，这里再补一道：visibility 每个浏览器都认，而且能过渡
   （延迟到动画结束再隐藏，展开时立刻可见，动画不受影响）。 */
.approve-confirm > div { overflow: hidden; visibility: hidden; transition: visibility 0s linear .2s; }
.approve-confirm.open > div { visibility: visible; transition-delay: 0s; }
.approve-confirm-inner {
  margin-top: 12px;
  padding: 15px;
  border: 1px solid var(--line-strong);
  border-radius: 11px;
  background: var(--surface);
}
.approve-confirm-title { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; font-size: 14px; font-weight: 800; }
.approve-confirm-title svg { width: 17px; height: 17px; color: var(--blue-700); }
.approve-confirm-inner p { margin: 0 0 12px; color: var(--ink-2); font-size: 12.5px; line-height: 1.65; }
.approve-confirm-actions { display: grid; grid-template-columns: 1fr 1.4fr; gap: 8px; }
.approve-confirm-actions button { min-height: 38px; border-radius: 9px; font-size: 13px; font-weight: 750; }
.cancel-approve { border: 1px solid var(--line-strong); color: var(--ink-2); background: var(--surface); }
.confirm-approve { border: 0; color: oklch(0.98 0.01 250); background: var(--green); }

.review-result {
  margin-top: 16px;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--muted);
  background: var(--surface);
  font-size: 12.5px;
  line-height: 1.65;
}
.review-result.ok { border-color: oklch(0.84 0.08 153); color: oklch(0.40 0.10 154); background: var(--green-bg); }
.review-result.no { border-color: oklch(0.86 0.08 25); color: var(--red); background: var(--red-bg); }

/* ------------------------------------------------------------ 评测总览页 */
.overview { display: grid; gap: 20px; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 28px; }
.eyebrow { color: var(--blue-700); font-size: 12px; font-weight: 850; letter-spacing: .12em; text-transform: uppercase; }
.page-head h1 { margin: 5px 0 7px; font-size: 27px; line-height: 1.25; letter-spacing: -.025em; }
.page-head p { max-width: 72ch; margin: 0; color: var(--muted); font-size: 14px; }
/* 产物来源标记。**回退那一档必须是警示色**：读本机旧结果时页面本身一切正常，
   只有这一行能告诉人「这些数字可能不是最新的」。 */
.artifact-source {
  margin-top: 8px !important;
  padding: 7px 11px;
  border: 1px solid var(--line);
  border-radius: 9px;
  font-size: 12.5px;
  font-weight: 650;
}
.artifact-source.ok { border-color: oklch(0.84 0.08 153); color: oklch(0.40 0.10 154); background: var(--green-bg); }
.artifact-source.warn { border-color: oklch(0.86 0.08 25); color: var(--red) !important; background: var(--red-bg); }
.date-picker { display: grid; gap: 5px; flex: 0 0 auto; }
.date-picker span { color: var(--muted); font-size: 11px; font-weight: 750; }
.date-picker select {
  min-height: 38px;
  padding: 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--surface);
  font-size: 13px;
  font-weight: 700;
}

.metrics, .channels {
  display: grid;
  gap: 16px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: 15px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
.kpi { padding: 14px 15px; border: 1px solid var(--line); border-radius: 11px; background: var(--blue-050); }
.kpi.primary { color: oklch(0.95 0.02 250); background: var(--navy-900); border-color: var(--navy-800); }
.kpi-k { color: var(--muted); font-size: 11.5px; font-weight: 700; }
.kpi.primary .kpi-k { color: oklch(0.78 0.03 250); }
.kpi-v { margin-top: 4px; font-size: 26px; font-weight: 800; line-height: 1.15; font-variant-numeric: tabular-nums; }
.kpi-max { margin-left: 3px; color: var(--muted); font-size: 12px; font-weight: 600; }
.kpi.primary .kpi-max { color: oklch(0.72 0.03 250); }
.kpi-grade { margin-left: 8px; padding: 1px 8px; border-radius: 99px; background: oklch(0.34 0.08 255); font-size: 13px; }
.kpi-sub { margin-top: 3px; color: var(--muted); font-size: 11px; }
.kpi.primary .kpi-sub { color: oklch(0.72 0.03 250); }

.judge-line { color: var(--muted); font-size: 12px; }
.judge-line .mono { color: var(--ink-2); }

.avg-panel { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.avg-toggle { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.seg {
  min-height: 32px;
  padding: 4px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink-2);
  font-size: 12.5px;
  font-weight: 700;
}
.seg.on { border-color: var(--blue-600); color: var(--blue-700); background: var(--blue-050); }
.avg-hint, .avg-scope { color: var(--muted); font-size: 11.5px; }
.avg-trend { color: var(--ink-2); font-size: 12.5px; }
.trend-up { color: var(--green); }
.trend-down { color: var(--red); }
.trend-flat { color: var(--muted); }
.avg-trend .pending { color: var(--muted); }
.score-formula { color: var(--muted); font-size: 11.5px; }
.sf-note { margin-left: 6px; color: var(--blue-700); }

.detail-groups { display: grid; gap: 16px; }
.dg-head { display: flex; align-items: baseline; gap: 10px; }
.dg-name { color: var(--ink); font-size: 12.5px; font-weight: 800; }
.dg-hint { color: var(--muted); font-size: 11px; }
.dim-grid, .channel-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; margin-top: 8px; }
.dim-cell, .channel-cell { padding: 11px 13px; border: 1px solid var(--line); border-radius: 10px; background: var(--blue-050); }
.dim-cell.na { opacity: .55; }
.dim-cell .name, .channel-cell .name { color: var(--ink-2); font-size: 12px; font-weight: 700; }
.dval { margin: 3px 0 7px; font-size: 19px; font-weight: 800; font-variant-numeric: tabular-nums; }
.dmax { color: var(--muted); font-size: 11px; font-weight: 600; }
.bar { overflow: hidden; height: 5px; border-radius: 99px; background: oklch(0.90 0.02 250); }
.bar i { display: block; height: 100%; border-radius: 99px; background: var(--blue-600); }

.gates { display: flex; flex-wrap: wrap; gap: 8px; }
.gate-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 99px;
  font-size: 11.5px;
}
.gate-chip .mark { font-weight: 850; }
.gate-chip.pass { border-color: oklch(0.84 0.08 153); color: var(--green); background: var(--green-bg); }
.gate-chip.fail { border-color: oklch(0.86 0.08 25); color: var(--red); background: var(--red-bg); }
.gate-chip .k { color: var(--ink-2); }

/* ----------------------------------------------------- 对照栏的差异标记 */
.d-add { padding: 1px 2px; border-radius: 4px; color: oklch(0.40 0.09 70); background: var(--amber-bg); }
.d-del { padding: 1px 2px; border-radius: 4px; color: var(--muted); background: oklch(0.93 0.012 250); text-decoration: line-through; }
.compare-copy .shape { margin-left: 7px; color: var(--blue-700); font-size: 10px; font-style: normal; font-weight: 800; letter-spacing: 0; text-transform: none; }
.compare-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; padding: 10px 0 13px; border-top: 1px solid var(--line); color: var(--muted); font-size: 11.5px; }
.compare-legend span { display: inline-flex; align-items: center; gap: 6px; }
.compare-legend i { width: 11px; height: 11px; border-radius: 3px; }
.sw-add { background: var(--amber-bg); border: 1px solid oklch(0.86 0.07 78); }
.sw-del { background: oklch(0.93 0.012 250); border: 1px solid var(--line-strong); }
.fallback-summary { margin: 10px 0 0; padding: 11px 12px; border: 1px solid var(--line); border-radius: 9px; color: var(--ink-2); background: var(--surface); font-size: 13px; line-height: 1.7; }
.fallback-summary b { display: block; margin-bottom: 3px; color: var(--muted); font-size: 11px; font-weight: 800; }

/* -------------------------------------------- 精选 / 排名 / 置顶 三种徽标 */
/* `.tag` 此前只在 HTML 示意页里有样式，本仓库的 styles.css 从来没定义过它 ——
   于是 <span class="tag feat-badge"> 只有颜色、没有 padding/边框/字号，渲染成 16px
   裸文本贴在 11px 的状态徽标旁边。这里补上基类（几何与 .status-badge 对齐）。 */
.tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 99px;
  color: var(--ink-2);
  background: var(--blue-050);
  font-size: 11px;
  font-weight: 750;
  white-space: nowrap;
}
.feat-badge { border-color: oklch(0.84 0.10 255); color: var(--blue-700); background: var(--blue-100); }
.pin-badge { border-color: oklch(0.87 0.08 78); color: var(--amber); background: var(--amber-bg); }
.rank-badge { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.pi-rank { flex: 0 0 auto; color: var(--blue-700); font-size: 11px; font-weight: 850; font-variant-numeric: tabular-nums; }
.pi-feat { color: var(--blue-700); font-weight: 700; }
.pi-done { color: var(--green); font-weight: 700; }

/* 队列条目左上角的「精选」标记（质量审核页）。跟 .feat-badge 同一套配色，但更小一档
   ——它挤在 11px 的名次与状态徽标那一行里，用正文号会把整行顶高。 */
.pi-mark {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 3px;
  padding: 1px 7px 1px 5px;
  border: 1px solid oklch(0.84 0.10 255);
  border-radius: 99px;
  color: var(--blue-700);
  background: var(--blue-100);
  font-size: 10.5px;
  font-weight: 850;
  line-height: 1.5;
  white-space: nowrap;
}
.pi-mark svg { width: 11px; height: 11px; fill: currentColor; }

/* --------------------------------------------------------- 精选要闻页 */
/* 这一页与质量审核页同构（左候选 + 中详情 + 右操作栏），所以复用 .workspace /
   .pending / .review-shell / .review-side 那整套，只补下面这几个自己的件。 */

/* 队列筛选：全部 / 建议审核。筛选在服务端分页之前做 */
/* 标题搜索框。**不给 disabled 态** —— 每敲一个字都会发请求（250ms 防抖），
   禁用会让它在请求期间失焦、把后面敲的字吃掉。 */
/* 列表里「配图生成中」的转圈。只在发起的那一端显示（状态在前端），
   出图几十秒，没有反馈审核人会以为点了没反应、再点一次。 */
.pi-spin {
  display: inline-block; width: 11px; height: 11px;
  margin-right: 6px; vertical-align: -1px;
  border: 2px solid var(--line-strong); border-top-color: var(--blue-600);
  border-radius: 50%;
  animation: pi-spin 0.7s linear infinite;
}
@keyframes pi-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .pi-spin { animation-duration: 2.4s; }
}

/* 换图区。放在配图核对下面 —— 判断和动手在同一屏。 */
.image-regen { margin-top: 14px; padding: 13px 14px; border: 1px solid var(--line); border-radius: 11px; background: var(--blue-050); }
.image-regen-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
.image-regen-head b { font-size: 13px; }
.image-regen-head small { color: var(--muted); font-size: 11.5px; }
.image-regen textarea { width: 100%; }
.image-regen-actions { display: flex; gap: 8px; margin-top: 9px; }
.image-regen-actions button { min-height: 36px; padding: 6px 14px; border: 1px solid var(--line-strong); border-radius: 9px; background: var(--surface); font-size: 12.5px; font-weight: 750; }
.image-regen-actions .feat-select { border-color: var(--ink); color: oklch(0.99 0.004 250); background: var(--ink); }
.image-regen-actions button:disabled { opacity: .55; cursor: not-allowed; }
.image-regen-foot { margin-top: 10px; color: var(--muted); font-size: 11px; line-height: 1.7; }

.queue-search { position: relative; display: block; padding: 10px 14px 0; }
.queue-search input {
  width: 100%;
  min-height: 32px;
  padding: 5px 26px 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  font-size: 12px;
}
.queue-search input:focus { border-color: var(--blue-600); outline: none; }
.queue-search input::-webkit-search-cancel-button { display: none; }
.queue-search-clear {
  position: absolute; top: 10px; right: 18px;
  width: 24px; min-height: 32px;
  border: 0; background: none; color: var(--muted); font-size: 15px; line-height: 1;
}
.queue-search-clear:hover { color: var(--ink); }
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* 三个筛选用分。低于 70 标红（与评委的通过线同一个数）；「—」是没评过，不是 0 分。 */
.pi-scores { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 5px; }
.pi-score { color: var(--muted); font-size: 11px; white-space: nowrap; }
.pi-score b { color: var(--ink-2); font-weight: 800; font-variant-numeric: tabular-nums; }
.pi-score.bad b { color: var(--red); }
.pi-score.na b { color: var(--muted); font-weight: 600; }
.pi-meta-tail { margin-top: 5px; }

/* **`flex-wrap` 是 2026-08-22 加的**，那一天档位从三个变成四个（全部 / 普通候选 /
   精选候选 / 当前置顶），而左栏只有 280-320px。没有它，四个 `flex: 1` 的按钮会各自
   被压到放不下四个汉字，文字自己折成两行、按钮高度参差不齐。
   顺带修掉一处**一直没生效的死 CSS**：文件前面有一条
   `.queue-filter button { padding: 4px 6px; font-size: 11.5px; }`，注释写着
   「三档筛选挤在 320px 的左栏里，字号收一档才不折行」—— 但它在这条规则**之前**，
   同样的选择器、同样的属性，后者整条把它盖掉了。也就是说那个「修复」从来没有生效过，
   而现在真的需要它，所以合进下面这条里（12px -> 11.5px、8px -> 6px）。 */
.queue-filter { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 14px 0; }
.queue-filter button {
  flex: 1 1 auto;
  min-width: 60px;
  min-height: 30px;
  padding: 4px 6px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  color: var(--ink-2);
  background: var(--surface);
  font-size: 11.5px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.queue-filter button.on { border-color: var(--blue-600); color: var(--blue-700); background: var(--blue-050); }
.queue-filter button:disabled { opacity: .55; cursor: default; }
/* 正在取这一档的数据。**不用 disabled**：禁用态会吃掉 hover 与按下反馈，看上去就是
   「点了没反应」——2026-08-14 那次误判就是这么来的。这里只给一个描边，按钮照常可点。 */
.queue-filter button[aria-busy="true"] { border-color: var(--blue-600); color: var(--blue-700); }
.queue-filter-note { padding: 8px 14px 0; color: var(--muted); font-size: 11px; line-height: 1.6; }
/* 这一档给不出答案时的告警（产物读不到 / 命中集被截断）。**判据说明 2026-08-21
   整块去掉之后，这里只剩这一种内容** —— 所以它平时完全不渲染，出现就说明真出事了。 */
.queue-filter-note .qfn-warn { display: block; margin-top: 6px; padding: 7px 9px; border: 1px solid oklch(0.86 0.08 25); border-radius: 8px; color: var(--red); background: var(--red-bg); }

/* 取数进行中的提示：左栏顶边一条 2px 来回滑的进度条 + 列表整体压暗。
   纯 CSS、不加任何依赖，也不重排布局（条子是 flex 列里 2px 高的一格）。
   列表**不清空、不换骨架屏**：一次请求 150-280ms，拆了再拼会闪，反而更慢。 */
.queue-progress { flex: 0 0 auto; overflow: hidden; height: 2px; background: var(--blue-050); }
.queue-progress::after {
  content: "";
  display: block;
  width: 40%;
  height: 100%;
  border-radius: 99px;
  background: var(--blue-600);
  animation: queue-slide 900ms ease-in-out infinite;
}
@keyframes queue-slide {
  from { transform: translateX(-100%); }
  to   { transform: translateX(250%); }
}
/* 压暗但**仍可点**：详情按 id 取，跟列表是哪一档无关，没必要在这 200ms 里锁住交互 */
.pending-list.is-busy { opacity: .6; transition: opacity 120ms ease; }

@media (prefers-reduced-motion: reduce) {
  /* 关掉横向滑动，留一个整条的明暗呼吸——完全没有反馈比动效更糟 */
  .queue-progress::after { width: 100%; animation: queue-pulse 1.2s ease-in-out infinite; }
  @keyframes queue-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
}

/* 左栏顶部的日期选择 */
.date-picker.slim { padding: 10px 14px 0; }
.date-picker.slim select { min-height: 32px; font-size: 12px; }
/* 已入选的在列表里要一眼看出来 */
.pending-item.is-feat { border-left: 3px solid var(--blue-600); }

/* **这里 2026-08-22 删掉了九条死样式**（两栏目合并，FeaturedList 那个组件整个没了）：
   .empty-jump / .pi-flag / .audit-badge{,.todo,.done} / .quota-mini{,span,span.quota-warn}
   / .quota-track{,i,i.over} / .quota-line{,::after} / .pi-order / .pi-plain。
   **删的时候必须整块删（选择器 + 声明体 + 花括号）。** 只删选择器行会留下一段没有
   选择器的声明块，而 CSS 的错误恢复会把那些声明当成下一条规则的选择器前奏、
   **连着把下一条规则整条吞掉** —— 仓库里那次「通过/不通过按钮错位」就是这么来的
   （症状在 28 行之后，配色圆角全对、只有 grid 布局没了）。
   `check_css_structure` 里那三条断言（花括号配平 / 中途无负深度 / 顶层无孤立声明）
   就是为这件事装的，删完跑一遍它。 */

.page-head.slim { margin-bottom: 16px; }
.page-head.slim h1 { margin: 4px 0 6px; font-size: 22px; }
.page-head.slim p { font-size: 13px; }

/* 右栏的干预操作，位置与 ReviewPanel 一致 */
.feat-state { display: grid; gap: 10px; margin: 4px 0 18px; }
.feat-state > div { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.feat-state span { color: var(--muted); font-size: 12px; }
.feat-state b { font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums; }
.feat-state b.warn { color: var(--red); }
/* 「比 AI 默认的前 25 条多 N 条」。**刻意不用 warn 的红色**：精选不再有上限，
   人工往上加是正常状态，标红等于告诉人这里出错了 */
.feat-state span i { display: block; margin-top: 2px; color: var(--muted); font-size: 10.5px; font-style: normal; }
.feat-actions { display: grid; gap: 8px; }
.feat-actions button { min-height: 40px; padding: 8px 14px; border: 1px solid var(--line-strong); border-radius: 10px; background: var(--surface); font-size: 13px; font-weight: 800; }
.feat-actions button:disabled { opacity: .5; cursor: not-allowed; }
.feat-actions .feat-select { border-color: var(--ink); color: oklch(0.99 0.004 250); background: var(--ink); }
.feat-actions .feat-drop { border-color: oklch(0.86 0.08 25); color: var(--red); background: var(--red-bg); }
.feat-actions .ghost:hover:not(:disabled) { border-color: var(--blue-600); color: var(--blue-700); background: var(--blue-050); }
/* 右栏两个面板之间的分隔（已发布页：精选编排 + 下架）。**要一条实线**：两块都以
   <h2> 开头、都有一组按钮，只留空白的话读起来像一块里的两段，人会以为下面那组按钮
   属于上面那个标题。 */
.side-split { margin: 22px 0; border-top: 1px solid var(--line); }

.feat-note { margin-top: 12px; padding: 10px 12px; border-left: 3px solid var(--amber); border-radius: 0 8px 8px 0; background: var(--amber-bg); font-size: 11.5px; line-height: 1.7; }
.feat-confirm { margin-top: 14px; padding: 12px 13px; border: 1px solid oklch(0.86 0.08 25); border-radius: 10px; background: var(--red-bg); }
.feat-confirm p { margin: 0 0 10px; color: var(--red); font-size: 12.5px; line-height: 1.65; }
.feat-confirm div { display: flex; gap: 8px; }
.feat-confirm button { min-height: 32px; padding: 4px 13px; border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface); font-size: 12px; font-weight: 750; }
.feat-confirm button.danger { border-color: var(--red); color: oklch(0.99 0.004 250); background: var(--red); }

/* 复位基类的 min-height/居中，否则一句脚注会撑成 320px 高的空盒 */
.placeholder.small { display: block; min-height: 0; padding: 13px 15px; color: var(--muted); font-size: 12px; text-align: left; }

/* --------------------------------------------------------------- 窄屏 */
/* **加宽只在真正宽的屏上生效。** 1320px 以下时左栏回到原来那个上限 —— 那一段里
   `review-shell` 还没塌成单列（那道线在 1180px），中栏要同时装详情体和右边三块面板，
   再从它身上切 60px 给左栏，右边那三块会先撞到自己的 340px 下限、把详情体挤没。 */
@media (max-width: 1320px) {
  .workspace { grid-template-columns: minmax(280px, 312px) minmax(0, 1fr); gap: 20px; }
}
@media (max-width: 1180px) {
  .review-shell { grid-template-columns: minmax(0, 1fr); }
  .review-side { border-left: 0; border-top: 1px solid var(--line); }
}
@media (max-width: 980px) {
  .app-shell { grid-template-columns: minmax(0, 1fr); }
  .sidebar { position: static; flex-direction: row; align-items: center; height: auto; }
  .side-nav { grid-auto-flow: column; }
  .side-foot { margin-top: 0; margin-left: auto; grid-auto-flow: column; align-items: center; }
  .workspace { grid-template-columns: minmax(0, 1fr); }
  .pending { position: static; max-height: 340px; }
  .compare-grid { grid-template-columns: 1fr; }
  .compare-copy + .compare-copy { padding-left: 0; border-left: 0; border-top: 1px solid var(--line); }
  /* **实体那一行必须单独再写一遍。** `.metadata-row[data-kind="entity"]`（上面 588 行）
     的优先级比 `.metadata-row` 高，媒体查询在源码里更靠后**也压不住它** ——
     优先级先于源码顺序。实测 390px 下它算出来是 `130px 180px`：
     「新闻所涉及实体」那一整栏（含实体搜索框）被塞进 180px 的窄缝里。
     这是本文件里唯一一处复合选择器 + grid，别再添第二处；真要添，记得同步这里。 */
  .metadata-row,
  .metadata-row[data-kind="entity"] { grid-template-columns: minmax(0, 1fr); }
  .model-strip { grid-template-columns: 1fr; }
  .model { border-right: 0; border-bottom: 1px solid var(--line); }
  .feature-row { grid-template-columns: 48px minmax(0, 1fr); }
  .fr-actions { grid-column: 2; grid-auto-flow: column; justify-content: start; }
}

/* ---------------------------------------------------------------- 手机 */
/* **2026-08-25 之前手机上是坏的，不是「没优化」而是真的用不了。** 断点只到 980px，
   而那一档是给平板的：它把顶栏拍成一行（`.sidebar` flex-direction: row），
   五个导航项 + 品牌 + 头像 + 退出登录挤在 390px 里放不下。实测 iPhone 14（390px）：
   **整页实际宽 564px、横向溢出 174px**，「退出登录」被切在屏外，导航标签被压成
   竖排一字一行。360px 的安卓机溢出 204px。

   量法与证据：`playwright + 系统 Chrome` 起真浏览器、把接口拦掉喂真标题（库里最长的
   标题 512 字符、有整段无空格的、还有阿拉伯语 RTL），逐元素量右边缘越界与自身溢出。
   ⚠ **判据必须用 `documentElement.clientWidth`，不能用 `window.innerWidth`** ——
   移动模式下后者报的是布局视口，页面一溢出它就跟着变大，于是差值恒为 0、永远量不到。

   640px 这条线：768px 平板竖屏实测干净（那一档 980px 的规则够用），所以只给真手机。 */
@media (max-width: 640px) {
  /* 顶栏：品牌与账号挤第一行，导航独占第二行。
     **导航用横向滚动条带而不是折行**：五项折行要占三行（约 130px），
     而手机上纵向空间是最贵的；条带只占一行，也是这个场景的通行做法。 */
  .sidebar { flex-wrap: wrap; gap: 10px; padding: 10px 12px; }
  .sidebar .brand { flex: 1 1 auto; min-width: 0; gap: 9px; }
  .sidebar .brand strong { font-size: 13px; }
  /* 「News Center」与「已登录」是装饰，手机上让位给真正要点的东西 */
  .sidebar .brand span, .reviewer-session span { display: none; }
  .sidebar .brand-mark { width: 30px; height: 30px; font-size: 12px; }
  .side-foot { order: 1; flex: 0 0 auto; gap: 8px; }
  .reviewer-avatar { width: 28px; height: 28px; }
  .reviewer-session b { font-size: 12px; }
  .side-nav {
    order: 2;
    flex: 1 0 100%;
    grid-auto-columns: max-content;
    gap: 6px;
    overflow-x: auto;
    /* 条带自己滚，别把滚动传给整页 —— 横滑导航时页面跟着上下弹是最难受的一种 */
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .side-nav::-webkit-scrollbar { display: none; }
  /* **必须给「还能往右滑」的提示。** 390px 上五项只露得出三项，没有提示时后两项
     （已下架新闻 / 评测总览）等于不存在 —— 实测截图里就是看不见。
     右缘一道渐隐比滚动条可靠：iOS 上滚动条只在滑动时才出现。
     `mask` 只裁自身、不影响布局；条带滚到底时右缘仍然渐隐，那点代价换的是
     「一眼看得出这里能滑」。 */
  .side-nav {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
  }
  /* 标签不许换行：换行就会被压成竖排一字一行（那正是原来的样子） */
  .nav-item { min-height: 38px; padding: 7px 10px; font-size: 13px; white-space: nowrap; }
  .logout { min-height: 38px; padding: 0 10px; }

  /* 队列：**去掉高度上限**。980px 那一档给它 340px 是为了让详情还能露出来，
     而手机上那个高度只装得下一条半 —— 变成「页面滚 + 卡内滚」两套滚动打架，
     实测 8 条列表只看得见 1 条。手机上让它跟着页面一起滚。 */
  .pending { max-height: none; }
  .pending-list { overflow-y: visible; }

  /* 内容区收紧内边距：390px 上每侧 20px 就吃掉 10% 的可用宽度 */
  .review-main, .review-side { padding: 14px 13px; }
  /* 标题那一行：**必须折行**。它是 flex 行、右边那组工具是 `flex: 0 0 auto`（不能收缩），
     而标题里一串长英文 token 会把 h2 的 min-content 宽度撑出可用空间 —— 于是
     「查看原文」被挤出屏外。实测 430px 上那个按钮右边缘超出 97px，**点不到**。
     三件一起：容器允许折行、标题允许收缩到 0（否则 flex 不肯让它变窄）、长 token 能断。 */
  .review-top { flex-wrap: wrap; gap: 10px; }
  .review-top h2 { min-width: 0; max-width: none; font-size: 19px; overflow-wrap: anywhere; }
  /* 工具组折到第二行之后改成横排 —— 竖排会白占一列高度 */
  .review-top-tools { flex-direction: row; align-items: center; flex-wrap: wrap; }
  .placeholder { min-height: 0; padding: 28px 18px; }

  /* **表单控件一律 16px。** iOS Safari 对 font-size < 16px 的输入框会在聚焦时
     自动放大整页，而且不会自己缩回去 —— 点一下搜索框就得手动捏回来。
     这一条与「视觉上想要小字」冲突，但那点观感换不来「每次点输入框都要捏一次」。 */
  .queue-search input, .date-picker select, .date-picker.slim select,
  .meta-input-row input, .review-textarea, .image-regen textarea,
  .login-field input { font-size: 16px; }

  /* 触摸目标：审核那两颗按钮是这一屏最要紧的动作，别让它们比手指还小 */
  .review-actions { gap: 8px; }
  .review-actions button { min-height: 44px; }
}

/* 逐条评分与给分理由（2026-08-20）。按评委维度分块：维度标题 + 分数 + 评分标准小字，
   下面每个评委一行（分数 + 那一句中文理由）。分数按 80 / 60 两档着色，与列表页同一套
   阈值 —— 两处不一致会让人以为哪边算错了。 */
.rubric-panel { margin: 18px 0; padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
.rubric-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.rubric-head h3 { margin: 0; font-size: 14px; }
.rubric-head-right { display: flex; align-items: center; gap: 10px; }
.rubric-total { color: var(--muted); font-size: 11.5px; font-style: normal; font-weight: 750; font-variant-numeric: tabular-nums; }

.rdim { padding: 12px 0; border-top: 1px solid var(--line); }
.rdim-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
/* 整行可点：光标与 hover 反馈**复用全局 .expandable**（影响面板在用同一套）。
   刻意不在这里另写一份 —— 同样的交互长成两个样子，会让人以为是两种不同的东西；
   而且全局那条已经带了 box-shadow 扩散，再覆一层自己的底色只会打架。 */
.rdim-toggle { display: flex; align-items: center; gap: 9px; margin-left: auto; }
.rdim-toggle em { color: var(--muted); font-size: 10.5px; font-style: normal; font-variant-numeric: tabular-nums; }
.rdim-head b { font-size: 13px; }
.rdim-score { font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }
.rdim-parts { display: flex; flex-wrap: wrap; gap: 8px; }
.rdim-parts i, .rm-who i { font-size: 11px; font-style: normal; font-weight: 750; font-variant-numeric: tabular-nums; }
.rdim-score.ok, .rdim-parts i.ok, .rm-who i.ok { color: var(--green); }
.rdim-score.mid, .rdim-parts i.mid, .rm-who i.mid { color: var(--amber); }
.rdim-score.bad, .rdim-parts i.bad, .rm-who i.bad { color: var(--red); }
.rdim-score.na, .rdim-parts i.na, .rm-who i.na { color: var(--muted); }
.rdim-criteria { margin-top: 3px; color: var(--muted); font-size: 11px; line-height: 1.6; }

/* 重大事实错误：唯一用红框的一块 —— 它是这一屏里最该被看见的东西 */
.rdim-severe { margin-top: 9px; padding: 9px 11px; border: 1px solid oklch(0.86 0.08 25); border-radius: 8px; background: var(--red-bg); font-size: 11.5px; line-height: 1.7; }
.rdim-severe b { display: block; margin-bottom: 3px; color: var(--red); }
.rdim-severe ul { margin: 0; padding-left: 18px; }

.rdim-models { margin: 9px 0 0; padding: 0; list-style: none; display: grid; gap: 7px; }
.rdim-models li { padding: 8px 10px; border-left: 3px solid var(--line-strong); border-radius: 0 8px 8px 0; background: var(--canvas); }
.rdim-models li.severe { border-left-color: var(--red); }
.rm-who { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; margin-bottom: 3px; }
.rm-name { color: var(--ink-2); font-size: 11px; font-weight: 750; }
.rm-conf { color: var(--muted); font-size: 10px; font-style: normal; font-variant-numeric: tabular-nums; margin-left: auto; }
.rm-reason { color: var(--ink-2); font-size: 12px; line-height: 1.7; }
.rm-noreason { color: var(--muted); font-style: italic; }
/* 数据问题（缺溯源字段 / 图片字段对不上）。**中性灰，刻意不给红也不给琥珀** ——
   它要在采集侧修，审核人点通过或不通过都改变不了它。2026-08-20 之前它跟「AI 在编」
   共用同一颗红标，一屏红标里绝大多数其实是管线毛病，真正在编的那几条被淹掉了。 */
.pi-dataflag { padding: 1px 5px; border: 1px solid var(--line); border-radius: 5px; color: var(--muted); font-size: 10px; }

/* 「产物说有图、但这台机器上没有那个文件」那一档。**必须显眼**：这一栏空着时
   审核人会读成「本来就没有图」，然后对着空面板判图片相关性（评测里有这一项）。 */
.image-unavailable {
  display: grid;
  gap: 4px;
  margin: 0 0 12px;
  padding: 9px 12px;
  border: 1px solid oklch(0.86 0.08 70);
  border-radius: 9px;
  background: oklch(0.97 0.03 80);
  font-size: 12.5px;
}
.image-unavailable b { font-size: 13px; }
.image-unavailable span { color: var(--muted); line-height: 1.55; }
