/* ==========================================================================
   维吾尔文智能校对系统 · Web 端样式
   设计要点：
     · 全站 RTL（维吾尔文从右向左），但控件标签保留中文副标便于操作
     · 维文字体统一使用随应用分发的 UKIJCJK（见下方 @font-face），
       行高放宽（阿拉伯字母有上下加符）
     · 差错标记沿用 develop.md 4.1.1 的红/橙/蓝三级严重度配色
   ========================================================================== */

/* ==========================================================================
   维文正文字体 —— 全站统一
   --------------------------------------------------------------------------
   字体文件随应用一起分发（static/fonts/UKIJCJK.woff2），而不是走
   `http(s)://httaxi.silubaba.com.cn/fonts/...` 外链。三个理由：
     ① 不依赖外部域名的可用性与证书；站点不可达时界面不会退化成方框
     ② 省一次跨域 DNS + TLS + 1.2MB 传输（字体是首屏渲染的阻塞资源）
     ③ 与 app.css 同源同版本，`no-cache` 策略下可一起失效、一起更新

   `font-display: swap`：字体未就绪时先用系统字体渲染，下载完再替换。
   若用 block，1.2MB 字体会让首屏出现最长 3s 的空白文字（FOIT），
   对正文是维文的应用来说体验明显更差。

   `unicode-range` 限定在阿拉伯字母区段：中文副标、数字、英文等
   交由 `--ui` 的系统字体渲染，避免用维文字体去显示它并不擅长的字形。
   ========================================================================== */
@font-face {
  font-family: "UKIJCJK";
  /* 【同时声明 woff2 与 ttf，让浏览器自选】
     两行顺序有意义：浏览器取**第一个它能解码的**，不是第一个下载成功的。
     因此体积小、压缩率高的 woff2 放前面，ttf 作为兜底
     （老浏览器、或 woff2 文件没能随包分发时仍能工作）。
       · woff2 —— 优先。同字体通常比 ttf 小 30–50%
        · ttf   —— 兜底。你手上拿到的原始格式就是它，免转换也能跑
     两个文件都缺失时，font-display: swap 会让文本用系统字体先渲染，
     界面不会出现方框或长时间空白。 */
  src: url("/static/fonts/UKIJCJK.woff2") format("woff2"),
       url("/static/fonts/UKIJCJK.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  /* 【这里刻意**不写** unicode-range】
     原设计用 `unicode-range: U+0600-06FF, …` 把本字体限定在阿拉伯字母
     区段，中文/英文交给 --ui 系统字体，理由是"避免用维文字体渲染它
     不擅长的字形"。

     但本字体命名为 UKIJCJK（CJK = 中日韩），**含汉字字形**，且需求是
     "全系统统一使用 UKIJCJK"。若继续保留 unicode-range，中文区段
     （U+4E00-9FFF）会被排除在外，中文仍走系统字体 —— 全站字体不统一。

     去掉后本字体会作用于**所有字符**。若某字符该字体确实没有，
     浏览器仍按 font-family 链回退到 --ui，不会出现方框。 */
}

/* ==========================================================================
   ① 设计令牌（Design Tokens）
   --------------------------------------------------------------------------
   令牌层是这次升级的地基：颜色、间距、圆角、阴影、层级、动效全部收敛到这里。
   之前这些值散落在 2300 行 CSS 里以字面量形式出现（同一个灰有好几种写法、
   同一个阴影在三个文件里各写一遍），改一处永远改不干净。

   【为什么暗色用 `[data-theme]` 而不是媒体查询】
   三种状态必须同时成立：跟随系统 / 强制浅色 / 强制深色。
   只有媒体查询做不到"强制浅色"（系统是深色时仍要能回到浅色）。
   因此规则是**属性驱动**，`data-theme` 由 JS 写在 <html> 上：
     <html data-theme="dark">   强制深色
     <html data-theme="light">  强制浅色
     <html>（无属性）           跟随系统 —— 由下方 @media 兜底
   JS 在首屏之前就写入该属性，避免深色用户看到一闪的白屏（FOUC）。

   【RTL 约定】下列语义变量全部保持 RTL 语义不变：
     --bg/--panel/--text/--border/--primary 含义与改造前一致，只是取值变化。
     布局相关的逻辑属性（inline-start 等）一律不碰。
   ========================================================================== */
:root {
  color-scheme: light;

  /* ---- 字体 ---- */
  /* 【全站统一使用 UKIJCJK】
     原先 --ui 的第一顺位是系统无衬线字体（Segoe UI / PingFang SC …），
     UKIJCJK 只用在 --mono-uy（维文）上 —— 结果是中文界面走中文字体、
     维文走 UKIJCJK，同一页面出现两套字形。

     按"全系统使用 UKIJCJK"的要求，把它提到 --ui 首位：中文、维文、
     数字、英文全部优先用同一套字形，版面观感统一。

     后面保留系统字体兜底，作用有二：
       ① 该字体缺某个字形时（生僻字、特殊符号）能继续渲染，不出方框；
       ② 字体文件未能随包分发时，界面退化为系统字体，仍可用。
     注意 UKIJCJK 之后先放维文字体再放中文字体：若字体缺维文字形，
     优先由同为阿拉伯字母设计的 UKIJ Tuz Tom 接住，字形风格更接近。 */
  --ui-ukij: "UKIJCJK", "UKIJ Tuz Tom", "Alkatip Basma Tom",
             "Noto Naskh Arabic", "Scheherazade New", "Amiri",
             "PingFang SC", "Microsoft YaHei", "Segoe UI", system-ui,
             sans-serif;
  --mono-uy: "UKIJCJK", "UKIJ Tuz Tom", "Alkatip Basma Tom",
             "Noto Naskh Arabic", "Scheherazade New", "Amiri",
             "Times New Roman", serif;
  --ui: var(--ui-ukij);
  /* 代码/命令/路径用等宽。
     【为什么 UKIJCJK 不放在等宽链首位】
     等宽字体的价值在于**字符宽度一致**（代码缩进对齐、表格里的
     版本号/路径列），而 UKIJCJK 是比例字体，放首位会让这些内容
     参差不齐 —— 比"字形不统一"更影响可读性。
     因此等宽链保持拉丁等宽优先；维文/中文在代码块里极少出现，
     真出现时也会落到后面的 UKIJCJK（放在等宽字体之后作为兜底），
     不会退化成方框。 */
  --mono: "SFMono-Regular", "JetBrains Mono", Consolas, "Courier New",
          "UKIJCJK", monospace;

  /* ---- 中性色阶（浅色）----
     由"表面"到"文字"共 8 级。此前代码里 #f4f6f9 / var(--n-1) / var(--n-1) /
     var(--n-2) 混用，肉眼几乎分不出但让界面显得"脏"；现在统一走阶梯。

     【本段曾有一处严重缺陷（本轮修复）】
     `--n-1: var(--n-1)` / `--primary: var(--primary)` 这类**自引用**在
     CSS 中是无效的：变量不能引用自己，浏览器会判定该变量为
     "guaranteed-invalid"，于是使用它的属性整条失效。
     后果是**只有浅色模式坏掉**（深色段与 @media 段都有明确字面值）：
     主色按钮、成功状态、页面底色全部无颜色，界面呈灰白"褪色"状。
     实测：浅色下 `--primary` / `--ok` / `--accent` / `--n-1` 全为空值。
     这里全部改为**字面值**，彻底消除自引用。
     【防回归】自测脚本已加断言：:root 内不允许出现 var(--x) 引用自身。 */
  --n-0:  #ffffff;      /* 卡片/面板 */
  --n-1:  #f5f7fb;      /* 页面底色（带一点蓝调，让白卡片"浮"起来） */
  --n-2:  #eef2f8;      /* 次级填充、悬停 */
  --n-3:  #dfe5ef;      /* 分隔线 */
  --n-4:  #c3ccdb;      /* 强分隔线、控件描边 */
  --n-5:  #7b8798;      /* 占位符、禁用文字（原 #94a0b3 在白底上仅 2.65:1，达不到 3:1 底线） */
  --n-6:  #5f6b7f;      /* 次要文字 */
  --n-7:  #3a4457;      /* 次级正文 */
  --n-8:  #171d2b;      /* 正文 */

  /* ---- 语义色（浅色）----
     【为什么额外定义 --*-rgb 通道变量】
     界面里有大量半透明语义色（`rgba(229,57,53,.13)` 这类），
     此前是**写死的字面色**，深色模式下完全不变 —— 于是深色底上出现
     一块刺眼的亮红，且这些色与 --error 的实际取值并不同源，
     改一处主题色要满文件搜三遍还未必找全。
     改成通道变量后，半透明色统一写成
         rgba(var(--error-rgb), .13)
     主题切换时自动跟随，且**与主色永远同源**，不会再漂移。 */
  --primary-rgb:      29, 78, 216;
  --error-rgb:        200, 30, 30;
  --warning-rgb:      180, 83, 9;
  --info-rgb:         11, 107, 158;
  --ok-rgb:           4, 108, 78;

  --primary:       #1d4ed8;   /* 主色：加深至在 --primary-soft 上也达标 */
  --primary-dark:  #1e40af;
  --primary-soft:  #e6efff;
  --primary-line:  #b9d2fb;
  --on-primary:    #ffffff;

  --error:         #c81e1e;   /* 原 #dc2626 在 --error-soft 上仅 3.95:1，加深至达标 */
  --error-dark:    #991b1b;
  --error-soft:    #fee2e2;
  --warning:       #b45309;   /* 原 #d97706 在白底上 4.2:1，加深 */
  --warning-dark:  #92400e;
  --warning-soft:  #fef3c7;
  --info:          #0b6b9e;   /* 原 #0e7dbe 在 --info-soft 上 3.90:1，加深 */
  --info-soft:     #e0f2fe;
  --ok:            #046c4e;   /* 原 #059669 在白底仅 3.77:1、在 --ok-soft 上 3.32:1 */
  --ok-dark:       #03543b;
  --ok-soft:       #d1fae5;

  --accent:        #7c3aed;   /* 强调紫：用于"新增/精选"类弱强调 */

  /* ---- 功能模块专属色（彩色语义化的核心）----
     【为什么每个模块要有自己的色】
     七个标签页此前共用同一个主色，切换时只有内容变、色彩不变，
     用户在深色/密集界面里很难靠余光确认"我现在在哪个模块"。
     给每个模块一个**色相独立、饱和度一致**的专属色后，
     标签选中态、模块标题、卡片描边、图标底色全部同色系呼应，
     既保留了专业工具的克制（不花哨），又一眼可辨。
     取值统一取 600–700 档饱和度，保证白底上对比度 ≥ 4.5:1。 */
  --mod-check-rgb:   37, 99, 235;    /* 校对 · 蓝 */
  --mod-dict-rgb:    109, 40, 217;   /* 词典 · 紫 */
  --mod-build-rgb:   15, 118, 110;   /* 建库 · 青 */
  --mod-ocr-rgb:     194, 65, 12;    /* OCR  · 橙 */
  --mod-review-rgb:  157, 23, 100;   /* 审核 · 洋红 */
  --mod-status-rgb:  4, 108, 78;     /* 状态 · 绿 */
  --mod-admin-rgb:   51, 65, 85;     /* 管理 · 石板灰（克制的运维色） */

  /* ---- 语义别名（供组件层引用，避免直接依赖 --n-* 数字）---- */
  --bg:            var(--n-1);
  --panel:         var(--n-0);
  --panel-2:       var(--n-2);
  --panel-3:       var(--n-3);
  --border:        var(--n-3);
  --border-strong: var(--n-4);
  --text:          var(--n-8);
  --text-dim:      var(--n-6);
  --text-faint:    var(--n-5);

  /* ---- 阴影用的通道（深色下需整体加深，故也走变量）---- */
  --shadow-rgb:    20, 30, 50;
  --shadow-alpha-1: .06;
  --shadow-alpha-2: .08;
  --shadow-alpha-3: .14;

  /* ---- 当前模块的主题色 ----
     由 JS 在每个视图切换时写入 `--mod-rgb`（如 "234, 88, 12"），
     所有模块化组件只引用 `--mod-rgb` / `--mod`，
     因此新增模块不必再改 CSS —— 后台数据驱动。 */
  --mod-rgb: var(--mod-check-rgb);
  --mod:     rgb(var(--mod-rgb));

  /* ---- 圆角 ---- */
  --radius-sm: 6px;
  --radius:    10px;
  --radius-lg: 14px;
  --radius-pill: 999px;

  /* ---- 阴影（两级，浅色）---- */
  --shadow-sm: 0 1px 2px rgba(var(--shadow-rgb), var(--shadow-alpha-1));
  --shadow:    0 1px 3px rgba(var(--shadow-rgb), var(--shadow-alpha-2)),
               0 4px 16px rgba(var(--shadow-rgb), var(--shadow-alpha-1));
  --shadow-lg: 0 8px 32px rgba(var(--shadow-rgb), var(--shadow-alpha-3));
  /* 焦点环跟随主色，不再是写死的旧蓝 rgba(21,101,192) —— 换主色时
     焦点态会一起变，避免"按钮蓝、焦点圈是另一种蓝"的错配。 */
  --shadow-focus: 0 0 0 3px rgba(var(--primary-rgb), .28);

  /* ---- 间距阶梯（8 的倍数，含 4 的半档）---- */
  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;
  --sp-5: 20px;  --sp-6: 24px;  --sp-8: 32px;

  /* ---- 层级（此前 50/60/200/900/950 散落各处，无统一语义）---- */
  --z-sticky:  50;
  --z-drop:    60;
  --z-overlay: 200;
  --z-modal:   900;
  --z-panel:   950;
  --z-toast:  1200;

  /* ---- 动效 ---- */
  --dur-fast: .12s;
  --dur:      .18s;
  --ease:     cubic-bezier(.4, 0, .2, 1);
}

/* ==========================================================================
   ② 深色模式
   --------------------------------------------------------------------------
   配色思路：不用纯黑（#000）做底 —— 纯黑与白色正文对比过大，长文本阅读
   时会出现光晕（halation）。改用带蓝调的深灰 #12161f，正文降为 #e4e8f0。

   主色在深色下**必须提亮**：原来的 var(--primary) 在白底上对比度 4.9:1 达标，
   但在 #12161f 上只有 2.1:1 —— 完全不可读。故深色态换成 #6fb3ff 系列。

   所有"软色"（--*-soft）不是把浅色值调暗，而是重新给一个**低饱和深色底**，
   这样 badge / 提示条在深色下才不会变成刺眼的荧光块。
   ========================================================================== */
[data-theme="dark"] {
  color-scheme: dark;

  --n-0:  #1a1f2a;   /* 卡片 */
  --n-1:  #12161f;   /* 页面底 */
  --n-2:  #232a36;   /* 次级填充 */
  --n-3:  #2c3442;   /* 分隔线 */
  --n-4:  #3b4553;   /* 强分隔线 */
  --n-5:  #6b7686;   /* 占位符 */
  --n-6:  #98a3b3;   /* 次要文字 */
  --n-7:  #c2cad6;   /* 次级正文 */
  --n-8:  #e4e8f0;   /* 正文 */

  --primary:       #6ea8fe;
  --primary-dark:  #9cc7ff;
  --primary-soft:  #17253c;
  --primary-line:  #2c4a72;
  --on-primary:    #0b1220;

  --error:         #ff8a8a;
  --error-dark:    #ffb3b3;
  --error-soft:    #3a1e1e;
  --warning:       #fbbf24;
  --warning-dark:  #fcd34d;
  --warning-soft:  #3a2c12;
  --info:          #56c4f0;
  --info-soft:     #14293a;
  --ok:            #4ade80;
  --ok-dark:       #86efac;
  --ok-soft:       #14301f;

  --accent:        #a78bfa;

  /* 【半透明通道在深色下必须整体调亮】
     浅色用的 `--primary-rgb: 37,99,235` 放在 #12161f 上几乎不可见，
     而大量组件是 `rgba(var(--primary-rgb), .14)` 这种"浅色底"。
     深色下把通道换成高亮值，同一段 CSS 就能得到正确的发光效果，
     不必给每个组件写一套深色特例。 */
  --primary-rgb:  110, 168, 254;
  --error-rgb:    255, 138, 138;
  --warning-rgb:  251, 191, 36;
  --info-rgb:     86, 196, 240;
  --ok-rgb:       74, 222, 128;

  /* 模块色同步提亮，保证深色底上色相可辨 */
  --mod-check-rgb:   96, 165, 250;
  --mod-dict-rgb:    167, 139, 250;
  --mod-build-rgb:   45, 212, 191;
  --mod-ocr-rgb:     251, 146, 60;
  --mod-review-rgb:  244, 114, 182;
  --mod-status-rgb:  74, 222, 128;
  --mod-admin-rgb:   148, 163, 184;

  --shadow-rgb:    0, 0, 0;
  --shadow-alpha-1: .4;
  --shadow-alpha-2: .45;
  --shadow-alpha-3: .55;

  --shadow-sm: 0 1px 2px rgba(var(--shadow-rgb), var(--shadow-alpha-1));
  --shadow:    0 1px 3px rgba(var(--shadow-rgb), var(--shadow-alpha-2)),
               0 6px 20px rgba(var(--shadow-rgb), var(--shadow-alpha-1));
  --shadow-lg: 0 10px 40px rgba(var(--shadow-rgb), var(--shadow-alpha-3));
  --shadow-focus: 0 0 0 3px rgba(var(--primary-rgb), .4);
}

/* 跟随系统：仅在用户**没有**手动指定主题时生效（无 data-theme 属性） */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;

    --n-0:  #1a1f2a;
    --n-1:  #12161f;
    --n-2:  #232a36;
    --n-3:  #2c3442;
    --n-4:  #3b4553;
    --n-5:  #6b7686;
    --n-6:  #98a3b3;
    --n-7:  #c2cad6;
    --n-8:  #e4e8f0;

    --primary:       #6ea8fe;
    --primary-dark:  #9cc7ff;
    --primary-soft:  #17253c;
    --primary-line:  #2c4a72;
    --on-primary:    #0b1220;

    --error:         #ff8a8a;
    --error-dark:    #ffb3b3;
    --error-soft:    #3a1e1e;
    --warning:       #fbbf24;
    --warning-dark:  #fcd34d;
    --warning-soft:  #3a2c12;
    --info:          #56c4f0;
    --info-soft:     #14293a;
    --ok:            #4ade80;
    --ok-dark:       #86efac;
    --ok-soft:       #14301f;

    --accent:        #a78bfa;

    --primary-rgb:  110, 168, 254;
    --error-rgb:    255, 138, 138;
    --warning-rgb:  251, 191, 36;
    --info-rgb:     86, 196, 240;
    --ok-rgb:       74, 222, 128;

    --mod-check-rgb:   96, 165, 250;
    --mod-dict-rgb:    167, 139, 250;
    --mod-build-rgb:   45, 212, 191;
    --mod-ocr-rgb:     251, 146, 60;
    --mod-review-rgb:  244, 114, 182;
    --mod-status-rgb:  74, 222, 128;
    --mod-admin-rgb:   148, 163, 184;

    --shadow-rgb:    0, 0, 0;
    --shadow-alpha-1: .4;
    --shadow-alpha-2: .45;
    --shadow-alpha-3: .55;

    --shadow-sm: 0 1px 2px rgba(var(--shadow-rgb), var(--shadow-alpha-1));
    --shadow:    0 1px 3px rgba(var(--shadow-rgb), var(--shadow-alpha-2)),
                 0 6px 20px rgba(var(--shadow-rgb), var(--shadow-alpha-1));
    --shadow-lg: 0 10px 40px rgba(var(--shadow-rgb), var(--shadow-alpha-3));
    --shadow-focus: 0 0 0 3px rgba(var(--primary-rgb), .4);
  }
}

* { box-sizing: border-box; }

/* ==========================================================================
   ③ 可访问性基线
   --------------------------------------------------------------------------
   · prefers-reduced-motion：前庭功能障碍用户会被位移/缩放的动效引发
     眩晕。这是**可访问性要求**，不是可选项。用 !important 是必要的 ——
     动效散落在几十条 transition 里，逐条改不可能收敛。
   · :focus-visible：键盘导航必须有可见焦点环；鼠标点击则不显示
     （避免每次点按钮都留一圈蓝边）。
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* 输入类控件用光晕而非描边环：描边环会与控件自身边框叠成双线，显脏 */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-color: var(--primary);
}

/* 屏幕阅读器专用文字（视觉隐藏但可朗读） */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* 键盘用户跳过导航直达主内容（Tab 第一下出现） */
.skip-link {
  position: absolute;
  inset-inline-start: 50%;
  top: -100px;
  transform: translateX(50%);
  z-index: calc(var(--z-toast) + 1);
  background: var(--primary);
  color: var(--on-primary);
  padding: 10px 20px;
  border-radius: 0 0 var(--radius) var(--radius);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: 0; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 15px;
}

/* ---------------------------------------------------------------- 顶栏 */
.topbar {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  box-shadow: 0 1px 2px rgba(var(--shadow-rgb), .04);
  /* 【为什么允许换行】顶栏内容（品牌 + 标签 + 引擎徽章 + 用户徽章）
     自然宽度约 900px。窄窗口下若禁止换行，尾部元素会被挤出视口
     （实测 689px 宽时用户徽章坐标溢出 —— 点不到，表现为
     "登录后找不到退出按钮"）。允许换行保证徽章任何宽度下都可达。 */
  flex-wrap: wrap;
  row-gap: 8px;
  /* 【RTL 对齐说明 — 这个顶栏被反复改坏过四次，务必读完再动】
     本页 dir=rtl，flex 主轴起点在**右边**，DOM 顺序直接映射为
     物理**右 → 左**。当前 DOM 是 brand → tabs → engine-badge → user-menu，
     故天然排成：品牌在物理最右、身份徽章在物理最左 ——
     这正是 RTL 页面的两端对齐效果。**这里什么都不用做。**

     ⚠ 真凶从来不是"顺序"，而是下面这条**亚像素换行**：
     .tabs 若用 flex-grow:1，单行解算会把它撑到 783.09px，使整行
     总额达 1400.09px，仅比可用 1400px 超出 **0.09px** → 触发换行，
     顶栏诡异地变成两行。已改为 `flex: 0 1 auto`（见 .tabs 注释）。

     四次失败记录（目标本身就设错了，外加三次错误手法）：
       ✗ 目标错误：想把 .user-menu 推到"物理右端" —— 但物理右端
         已被 .brand 占据（RTL 下 DOM 首元素），两者不可能同时贴右；
         这是**需求冲突**，不是 CSS bug。
       ✗ 手法① margin-inline-start: auto → rtl 下 inline-start 是**右**侧，
         徽章被顶到品牌区上（同侧重叠）
       ✗ 手法② margin-inline-end: auto → 方向虽对，但 flex-wrap 下
         auto margin 会吸走整行剩余空间，让该元素**独占一行**
       ✗ 手法③ order: 4 → order 是**逻辑**排序，rtl 下指向物理最左，
         与意图相反（实测坐标与修改前逐位相同）

     教训：RTL 下不要用 order / auto margin 表达"物理右端"；
     在该语言环境下 DOM 顺序天然就是正确的物理顺序。 */
}

.brand { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }

.logo {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--primary), color-mix(in srgb, var(--primary) 70%, #ffffff));
  color: var(--on-primary);
  border-radius: 9px;
  font-family: var(--mono-uy);
  font-size: 24px;
  line-height: 1;
  padding-bottom: 4px;
}

.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.brand-text strong { font-size: 15px; font-family: var(--mono-uy); }
.brand-text small { color: var(--text-dim); font-size: 11.5px; }

/* 标签页 —— 顶栏里**唯一**的弹性元素 */
/* 让它吸收所有空余空间，brand/engine-badge/user-menu 便自然分贴两端。
   不要给它 order，也不要给其他子项加 order/auto margin：
   本页 dir=rtl，DOM 顺序**已经**就是正确的物理顺序（右→左），
   order 是逻辑序，加了反而会把元素映射到相反的一端。
   （这一点被反复改坏过三次，详见 .topbar / .user-menu 的注释。）

   ⚠ flex-grow 用 0 而非 1 —— 这是个**实测出来的亚像素坑**：
   用 flex-grow:1 时，单行解算会把 .tabs 撑到 783.09px，
   导致整行总额 = 267.59+783.09+150.77+136.55+54 = **1400.09px**，
   比可用宽度 1400px 超出 **0.09px** → 触发 flex-wrap 换行！
   表现为"1440 宽下顶栏莫名其妙变成两行"，而 0.09px 是谁都看不见的。
   改成 `flex: 0 1 auto`（grow=0，只允许收缩）后，.tabs 保持其
   max-content 559.2px，总额 1208.11px，留出 231.89px 余量，
   在 1208px 以上都不会换行。
   同理 overflow-x:auto 也不该在宽屏下出现（559.2 < 可用空间）。 */
.tabs {
  display: flex; gap: 4px; margin-inline-start: 8px;
  flex: 0 1 auto; min-width: 0;
  flex-wrap: wrap;
  /* 极窄时标签区自身横向滚动，而不是把徽章挤出去 */
  overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab { flex: 0 0 auto; }

.tab {
  display: flex; flex-direction: column; align-items: center;
  gap: 1px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-dim);
  padding: 6px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--mono-uy);
  font-size: 14px;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.tab span {
  font-family: var(--ui);
  font-size: 10.5px;
  color: var(--text-dim);
  opacity: .85;
}
.tab:hover { background: var(--n-2); color: var(--text); }

/* ==========================================================================
   标签页 · 彩色模块化
   --------------------------------------------------------------------------
   【设计意图】
   七个标签此前**共用同一个主色**：选中态都是同一种蓝。在密集界面里
   用户需要先"读"标题才能确认自己在哪个模块，切换标签时视觉上只有
   一小块底色在动。这里让每个标签带上自己模块的色相 ——
   一个色相独立、饱和度一致的专属色，选中时：
     · 底色 = 该模块色的极浅底（6% 不透明度）
     · 描边 = 该模块色的浅描边（32%）
     · 文字 = 该模块色的深色版
   切换模块时色彩随之变化，余光即可辨认当前位置。

   【为什么用 --mod-rgb 通道 + 三个透明度，而不是给每个标签写一条规则】
   写七条 `.tab[data-view="dict"] { … }` 意味着**每加一个模块就要改 CSS**，
   且七份近乎相同的声明会各自漂移（改了一处忘了另六处）。
   这里改为：每个标签只需给出**自己的通道值**（一行 data 属性已在
   HTML 里），视觉细节由下面这一条规则统一决定。
   新增模块时只加一行 `--mod-xxx-rgb`，视觉自动到位。

   【为什么用 color-mix 而不是再定义一堆 --mod-x-soft】
   软色（浅底）本身是"模块色 + 透明度"的直接函数。让它由公式派生，
   就不可能出现"某模块的软色和主色不同源"的错配。
   color-mix 在 Chrome 111+ / Safari 16.2+ / Firefox 113+ 均可用，
   本项目前端要求现代浏览器（已使用 :has、color-mix 等特性）。
   ========================================================================== */
.tab.active {
  background: rgba(var(--tab-rgb), .10);
  border-color: rgba(var(--tab-rgb), .32);
  color: color-mix(in srgb, rgb(var(--tab-rgb)) 78%, var(--text));
}
.tab.active span {
  color: color-mix(in srgb, rgb(var(--tab-rgb)) 88%, var(--text-dim));
}
/* 选中标签底部再加一条 2px 色条：在 RTL 密集排版里，色条比底色
   更容易被扫视到（底色变化只有几个百分点的明度差）。 */
.tab.active::after {
  content: "";
  position: absolute;
  inset-inline: 14px;
  bottom: 1px;
  height: 2px;
  border-radius: 2px;
  background: rgb(var(--tab-rgb));
}
.tab { position: relative; }

/* 每个标签的模块色。用属性选择器而不是给每个标签加内联 style：
   内联 style 会与 CSP 的 style-src 策略冲突，且不易被主题覆盖。 */
.tab[data-view="check"]  { --tab-rgb: var(--mod-check-rgb); }
.tab[data-view="dict"]   { --tab-rgb: var(--mod-dict-rgb); }
.tab[data-view="build"]  { --tab-rgb: var(--mod-build-rgb); }
.tab[data-view="ocr"]    { --tab-rgb: var(--mod-ocr-rgb); }
.tab[data-view="review"] { --tab-rgb: var(--mod-review-rgb); }
.tab[data-view="status"] { --tab-rgb: var(--mod-status-rgb); }
.tab[data-view="admin"]  { --tab-rgb: var(--mod-admin-rgb); }

/* 悬停时也带一点模块色暗示（仅未选中态） */
.tab:not(.active):hover {
  background: rgba(var(--tab-rgb, var(--primary-rgb)), .07);
}

.engine-badge {
  /* 不收缩项；**不要加 order、也不要加任何 auto margin**：
     order 在 rtl 下表达的是逻辑序（会映射到物理左端），
     auto margin 在 flex-wrap 下会让本元素独占一行。
     位置完全由 .tabs 的 flex:1 决定。 */
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--text-dim);
  background: var(--n-2);
  padding: 5px 11px;
  border-radius: 20px;
  white-space: nowrap;
}
.engine-badge .dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--n-5); flex: none;
}
.engine-badge.ready .dot { background: var(--ok); box-shadow: 0 0 0 3px rgba(var(--ok-rgb),.15); }
.engine-badge.busy  .dot { background: var(--warning); }
.engine-badge.fail  .dot { background: var(--error); }

/* ---------------------------------------------------------------- 布局 */
.wrap { max-width: 1500px; margin: 0 auto; padding: 16px 20px 40px; }

.view { display: none; }
.view.active { display: block; }

.toolbar {
  display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.spacer { flex: 1; }

/* ===========================================================================
   `hidden` 属性豁免（务必保留）
   ===========================================================================
   【问题】浏览器 UA 样式表里的 `[hidden] { display: none }` 优先级**极低**，
   任何作者样式表里的 `display` 声明都能盖掉它。所以凡是下面这种写法：

       <button class="btn" id="applyAllBtn" hidden>

   只要 `.btn` 声明了 `display: inline-flex`，hidden 属性就**完全失效**，
   元素照样占位显示——除非它恰好待在一个 display:none 的祖先里，
   那样问题会被掩盖，等祖先一变可见（例如切换标签页）才暴露出来。

   【实测 2026-09-23】`#ocrDownloads` 带 hidden，但 `view-ocr` 标签页激活后
   computed display 仍是 `flex`、`offsetParent` 非空，即"隐藏"的下载区
   实际显示了出来。`#issuesWrap` 内的 `#applyAllBtn` / `#reportBtn` 同理。

   【为什么集中声明在一处】逐条给每个类加 `[hidden]` 太容易漏
   （`.modal-mask[hidden]` 与 `.login-mask[hidden]` 就是这么漏出来的）。
   这里对所有"自带 display 的容器/控件类"统一兜底；新增类只要名字在
   下面即可，不必再改第二处。

   【为什么这里**不能**加 `!important`】
   `.login-mask` 的显隐由 auth.js 的 `_setMask()` 用**内联 style.display**
   控制（见该函数注释：内联样式优先级高于任何选择器，这正是它能压过
   `.login-mask { display: grid }` 的原因）。
   一旦这里的 `[hidden]` 规则加上 `!important`，它就会反过来盖住内联
   样式——`_setMask(true)` 设的 `display: grid` 会被 `display: none`
   压掉，遮罩永远打不开，表现为"深链点进去什么都不弹"。
   不加 `!important` 时：内联样式仍然获胜，遮罩能正常开；而元素没有
   内联 display 时，`[hidden]` 又能按预期收起。
   =========================================================================== */
.btn[hidden],
.pane[hidden],
.build-grid[hidden],
.mini-field[hidden],
.progress-box[hidden],
.ocr-downloads[hidden],
.admin-section[hidden],
.login-mask[hidden],
.modal-mask[hidden],
.jobs-wrap[hidden],
.issues-wrap[hidden],
.manual-pending[hidden],
.manual-feedback[hidden],
.grammar-wrap[hidden],
.user-drop[hidden] { display: none; }

.btn {
  display: inline-flex; flex-direction: column; align-items: center;
  gap: 1px;
  border: 1px solid var(--border-strong);
  background: var(--panel);
  color: var(--text);
  padding: 7px 16px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--mono-uy);
  font-size: 14px;
  line-height: 1.2;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn span { font-family: var(--ui); font-size: 10px; color: var(--text-dim); }
.btn:hover:not(:disabled) {
  border-color: var(--primary);
  color: var(--primary-dark);
  background: var(--primary-soft);
}
/* 按下时轻微下沉 —— 给出"点击已被接收"的即时触觉反馈，
   对延迟反馈的操作（如提交审核）能显著降低重复点击率。 */
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn.primary {
  background: var(--primary); border-color: var(--primary); color: var(--on-primary);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--primary) 40%, transparent);
}
.btn.primary span { color: color-mix(in srgb, var(--on-primary) 80%, transparent); }
.btn.primary:hover:not(:disabled) {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
  color: var(--on-primary);
}

.btn.small { padding: 5px 12px; font-size: 13px; }

/* 超小按钮：用于表格行内操作与选择助手，避免撑破行高 */
.btn.mini { padding: 3px 9px; font-size: 11.5px; border-radius: 6px; }
.btn.mini span { font-size: 9.5px; }
.btn.mini  { padding: 3px 9px; font-size: 12px; line-height: 1.5; }
.btn.mini span { font-size: 9px; }

.file-btn {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px;
  border: 1px dashed var(--border-strong);
  background: var(--panel);
  padding: 7px 16px;
  border-radius: 8px;
  cursor: pointer;
  font-family: var(--mono-uy); font-size: 14px;
  transition: .15s;
}
.file-btn span { font-family: var(--ui); font-size: 10px; color: var(--text-dim); }
.file-btn:hover { border-color: var(--primary); color: var(--primary-dark); }

.switch {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 12px;
  color: var(--text-dim);
  user-select: none;
}
.switch input { margin: 0; cursor: pointer; }
.switch:has(input:checked) {
  border-color: var(--primary); color: var(--primary-dark);
  background: var(--primary-soft);
}

.stats-inline { font-size: 12.5px; color: var(--text-dim); display: flex; gap: 14px; flex-wrap: wrap; }
.stats-inline b { color: var(--text); font-weight: 600; }

/* ------------------------------------------------------------ 编辑区 */
.editor-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
@media (max-width: 980px) { .editor-grid { grid-template-columns: 1fr; } }

.pane {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex; flex-direction: column;
  min-height: 420px;
  overflow: hidden;
}

/* ==========================================================================
   模块容器 · 彩色语义化
   --------------------------------------------------------------------------
   【上边 3px 彩色条】
   面板此前全是统一灰边白底，七八个面板堆在一起时边界感很弱 ——
   用户要靠读标题才知道哪块是哪块。加一条跟随当前模块色的顶边后，
   面板归属一眼可辨，且**不需要给每个面板单独配色**（只读 --mod-rgb）。

   【为什么用 ::before 而不是 border-top】
   border-top 会参与盒模型计算，改变面板高度 3px，而项目里有大量
   `min-height: 420px` 与 flex 布局（校对框要占满剩余高度）——
   多出的 3px 会让内部滚动条位置、textarea 高度全部位移。
   用绝对定位的伪元素叠在顶部，**不占布局空间**，零副作用。
   ========================================================================== */
.pane { position: relative; }
.pane::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  background: linear-gradient(
    to inline-end,
    rgb(var(--mod-rgb)) 0%,
    color-mix(in srgb, rgb(var(--mod-rgb)) 55%, transparent) 100%
  );
  z-index: 1;
  pointer-events: none;     /* 不挡住顶栏里的按钮 */
}

.pane-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border);
  /* 【底色从"灰"换成"模块色的极浅底"】
     pane-head 是每个模块的标题栏，用模块色打底后，切换标签时
     整片工作区的氛围色会跟着变，而不是只有一个小标签在变色。
     透明度取得很低（4%）—— 目的是"氛围"而非"色块"，
     再高就会与内部的表单控件抢视觉焦点。 */
  background: linear-gradient(
    to inline-end,
    rgba(var(--mod-rgb), .055) 0%,
    transparent 62%
  ), var(--n-1);
  font-family: var(--mono-uy);
  font-size: 14px;
}

/* 标题左侧的彩色短竖条：给标题一个视觉锚点（RTL 下自动落在右侧） */
.pane-head::before {
  content: "";
  inline-size: 3px;
  block-size: 15px;
  border-radius: 2px;
  background: rgb(var(--mod-rgb));
  flex: 0 0 auto;
}

.pane-head span span { font-family: var(--ui); font-size: 11px; color: var(--text-dim); }
.counter { font-family: var(--ui); font-size: 11.5px; color: var(--text-dim); }

#inputText {
  flex: 1;
  border: 0; outline: 0; resize: none;
  padding: 14px;
  font-family: var(--mono-uy);
  font-size: 19px;
  line-height: 2.1;
  color: var(--text);
  background: transparent;
}
#inputText::placeholder { color: var(--n-5); font-size: 16px; line-height: 2; }

.result-area {
  flex: 1;
  overflow: auto;
  padding: 14px;
  font-family: var(--mono-uy);
  font-size: 19px;
  line-height: 2.1;
  white-space: pre-wrap;
  word-break: break-word;
}

/* 差错高亮 —— 三级严重度（develop.md 4.1.1） */
.result-area mark {
  border-radius: 3px;
  padding: 1px 1px 2px;
  cursor: pointer;
  background: transparent;
  color: inherit;
  transition: .12s;
}
mark.sev-error   { background: rgba(var(--error-rgb),.14);  border-bottom: 2px solid var(--error); }
mark.sev-warning { background: rgba(var(--warning-rgb),.14);  border-bottom: 2px solid var(--warning); }
mark.sev-info    { background: rgba(var(--info-rgb),.13); border-bottom: 2px solid var(--info); }
mark:hover { filter: brightness(.94); }
mark.active { outline: 2px solid var(--primary); outline-offset: 1px; }

.legend { display: flex; gap: 10px; font-family: var(--ui); font-size: 11px; }
.legend i {
  display: inline-block; width: 10px; height: 10px;
  border-radius: 3px; margin-inline-end: 4px; vertical-align: -1px;
}

.empty-hint {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px;
  height: 100%;
  min-height: 200px;
  color: var(--n-5);
  font-family: var(--mono-uy);
  font-size: 17px;
}
.empty-hint span { font-family: var(--ui); font-size: 12.5px; }

/* ------------------------------------------------------------ 问题列表 */
.issues-wrap {
  margin-top: 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.issues-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--n-1);
}
.issues-head strong { font-family: var(--mono-uy); font-size: 14px; font-weight: 600; }
.issues-head strong span { font-family: var(--ui); font-size: 11px; color: var(--text-dim); font-weight: 400; }

.issue-filters { display: flex; gap: 5px; flex-wrap: wrap; }
.chip {
  border: 1px solid var(--border-strong);
  background: var(--panel);
  border-radius: 20px;
  padding: 3px 11px;
  font-size: 11.5px;
  cursor: pointer;
  color: var(--text-dim);
  transition: .12s;
  white-space: nowrap;
}
.chip:hover { border-color: var(--primary); color: var(--primary-dark); }
.chip.active { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.chip.active.denied { background: var(--error); border-color: var(--error); }

.issue-list {
  list-style: none;
  margin: 0; padding: 0;
  max-height: 460px;
  overflow: auto;
}

.issue-item {
  display: grid;
  grid-template-columns: 52px 1fr auto;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--n-2);
  align-items: start;
}
.issue-item:last-child { border-bottom: 0; }
.issue-item:hover { background: var(--n-1); }
.issue-item.active { background: var(--primary-soft); }

.issue-sev {
  font-size: 10.5px;
  font-weight: 600;
  padding: 3px 6px;
  border-radius: 5px;
  text-align: center;
  color: var(--on-primary);
  margin-top: 2px;
}
.issue-sev.error   { background: var(--error); }
.issue-sev.warning { background: var(--warning); }
.issue-sev.info    { background: var(--info); }

.issue-body { min-width: 0; }
.issue-snippet {
  font-family: var(--mono-uy);
  font-size: 17px;
  line-height: 1.9;
  margin-bottom: 4px;
  word-break: break-word;
}
.issue-snippet .wrong {
  color: var(--error); font-weight: 600;
  text-decoration: line-through;
  text-decoration-color: rgba(var(--error-rgb),.5);
}
.issue-snippet .arrow { color: var(--text-dim); margin: 0 4px; }
.issue-snippet .right { color: var(--ok); font-weight: 600; }
.issue-meta {
  font-size: 11.5px; color: var(--text-dim);
  display: flex; gap: 10px; flex-wrap: wrap;
  font-family: var(--ui);
}
.issue-type-tag {
  background: var(--n-2); border-radius: 4px;
  padding: 1px 6px; font-weight: 600; color: var(--n-6);
}
.issue-type-tag.denied {
  background: var(--error-soft); color: var(--error-dark);
}
.issue-snippet .denied-badge {
  display: inline-block;
  background: var(--error);
  color: var(--on-primary);
  font-family: var(--ui);
  font-size: 11px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 4px;
  margin-inline-start: 6px;
  vertical-align: 2px;
}
.denied-reason {
  color: var(--error-dark);
  font-weight: 500;
}
.denied-reason span {
  background: var(--error-soft);
  color: var(--error-dark);
  padding: 1px 5px;
  border-radius: 3px;
  margin-inline-end: 5px;
  font-weight: 600;
}

.issue-actions { display: flex; flex-direction: column; gap: 5px; }
.issue-actions .btn { padding: 4px 10px; font-size: 12px; }
.issue-actions .btn span { display: none; }

.sugg-list { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 4px; }
.sugg {
  border: 1px solid var(--primary-line); background: var(--primary-soft); color: var(--primary-dark);
  border-radius: 6px; padding: 2px 9px; cursor: pointer;
  font-family: var(--mono-uy); font-size: 15px;
  transition: .12s;
}
.sugg:hover { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }

/* -------------------------------------------------------------- 词典 */
.dict-search {
  display: flex; gap: 10px; margin-bottom: 16px;
}
.dict-search input {
  flex: 1;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 11px 16px;
  font-family: var(--mono-uy);
  font-size: 18px;
  outline: 0;
  background: var(--panel);
}
.dict-search input:focus { border-color: var(--primary); box-shadow: var(--shadow-focus); }

.dict-result {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px;
  min-height: 300px;
}

.dict-word {
  font-family: var(--mono-uy);
  font-size: 30px;
  font-weight: 600;
  margin-bottom: 6px;
  color: var(--primary-dark);
}
.dict-badges { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.badge {
  font-size: 11.5px; padding: 3px 9px; border-radius: 20px;
  background: var(--n-2); color: var(--n-6);
}
.badge.ok      { background: var(--ok-soft); color: var(--ok); }
.badge.warn    { background: var(--warning-soft); color: var(--warning-dark); }
.badge.err     { background: var(--error-soft); color: var(--error-dark); }
.badge.primary { background: var(--primary-soft); color: var(--primary-dark); }

.dict-entry {
  border-top: 1px solid var(--n-2);
  padding: 13px 0;
}
.dict-entry:first-of-type { border-top: 0; }
.dict-sense {
  display: flex; gap: 8px;
  font-family: var(--mono-uy);
  font-size: 17px;
  line-height: 2;
  margin-bottom: 6px;
}
.sense-no {
  flex: none;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--n-2);
  color: var(--n-6);
  font-family: var(--ui);
  font-size: 11px;
  display: grid; place-items: center;
  margin-top: 8px;
}
.dict-row {
  font-size: 12.5px; color: var(--text-dim);
  margin-top: 5px;
  display: flex; gap: 8px;
}
.dict-row b { color: var(--n-6); font-weight: 600; flex: none; }
.dict-row .uy { font-family: var(--mono-uy); font-size: 15px; color: var(--text); }

.morph-box {
  margin-top: 16px;
  border-top: 1px dashed var(--border-strong);
  padding-top: 14px;
}
.morph-title { font-size: 12.5px; font-weight: 600; color: var(--n-6); margin-bottom: 8px; }
.morph-stem {
  font-family: var(--mono-uy); font-size: 20px; margin-bottom: 8px;
}
.morph-stem .suffix { color: var(--primary); }
.morph-kv {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 6px 16px;
  font-size: 12.5px; color: var(--text-dim);
}
.morph-kv b { color: var(--n-6); }

/* -------------------------------------------------------------- 状态 */
.status-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}
.stat-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  /* 看板卡片同样跟随模块色：状态页是绿色系、审计/账号面板是石板灰系 */
  position: relative;
  overflow: hidden;
}
.stat-card::before {
  content: "";
  position: absolute;
  inset-block-start: 0; inset-inline: 0;
  height: 3px;
  background: linear-gradient(
    to inline-end,
    rgb(var(--mod-rgb)) 0%,
    color-mix(in srgb, rgb(var(--mod-rgb)) 45%, transparent) 100%
  );
}
.stat-card h3 {
  margin: 0 0 12px;
  font-size: 13px; font-weight: 600;
  color: color-mix(in srgb, rgb(var(--mod-rgb)) 62%, var(--text));
  display: flex; align-items: center; gap: 7px;
}
.stat-list { display: flex; flex-direction: column; gap: 7px; }
.stat-row {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 13px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--n-2);
}
.stat-row:last-child { border-bottom: 0; padding-bottom: 0; }
.stat-row .k { color: var(--text-dim); }
.stat-row .v { font-weight: 600; font-variant-numeric: tabular-nums; }
.stat-row .v.uy { font-family: var(--mono-uy); font-size: 15px; }

/* -------------------------------------------------------------- 杂项 */
.footer {
  text-align: center;
  padding: 20px;
  font-size: 12px;
  color: var(--n-5);
  font-family: var(--mono-uy);
}
.footer .sep { margin: 0 8px; }

/* ---------------------------------------------------------------- Toast
   【为什么改成一叠而不是一条】
   原实现只有一个 #toast 节点，后一条会**覆盖**前一条。采集任务的失败
   详情往往是多条（每个文件一条错误），原实现下用户只能看到最后一条，
   前面的错误一闪即逝 —— 而"哪几个文件失败了"恰恰是这里最需要的信息。

   现在改为容器 + 多条并存，最多 4 条，超出丢弃最旧的。
   `role="status"` + `aria-live="polite"`：屏幕阅读器会朗读新提示，
   但不会打断用户当前操作（对比 assertive 会强制打断）。 */
.toast-wrap {
  position: fixed;
  inset-inline: 0;
  bottom: 24px;
  display: flex;
  flex-direction: column-reverse;   /* 新提示出现在最上方（贴近视线） */
  align-items: center;
  gap: 8px;
  z-index: var(--z-toast);
  pointer-events: none;             /* 容器不吃点击，只有条目本身可交互 */
}
.toast {
  pointer-events: auto;
  background: var(--n-8);
  color: var(--on-primary);
  padding: 10px 22px;
  border-radius: 8px;
  font-size: 13px;
  line-height: 1.55;
  box-shadow: 0 6px 24px rgba(0,0,0,.28);
  max-width: min(560px, 86vw);
  opacity: 0;
  transform: translateY(10px) scale(.98);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  text-align: center;
}
.toast.show { opacity: 1; transform: translateY(0) scale(1); }
.toast.err  { background: var(--error-dark); }
.toast.ok   { background: var(--ok-dark); }
.toast.warn { background: var(--warning-dark); }

/* ------------------------------------------------ 加载骨架（Skeleton）
   比 spinner 更好的"内容即将出现"暗示：保留了布局占位，
   加载完成时不会发生跳版（layout shift）。 */
.skel {
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    var(--n-2) 25%, var(--n-3) 37%, var(--n-2) 63%);
  background-size: 400% 100%;
  animation: skel-shimmer 1.4s ease-in-out infinite;
  min-height: 12px;
}
@keyframes skel-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
.skel-row { height: 14px; margin-bottom: 9px; }
.skel-row:last-child { margin-bottom: 0; }
.skel-row.w-40 { width: 40%; }
.skel-row.w-60 { width: 60%; }
.skel-row.w-80 { width: 80%; }
.skel-card { height: 74px; border-radius: var(--radius); }
.skel-block { padding: 4px 0; }

/* 减动效偏好下：骨架不做闪光（避免持续动画），退化为静态灰块 */
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; background: var(--n-2); }
}

/* ---------------------------------------------------- 快捷键帮助 */
.kbd-grid { font-size: 13px; }
.kbd-table { width: 100%; border-collapse: collapse; }
.kbd-table td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.kbd-table tr:last-child td { border-bottom: 0; }
.kbd-table td:first-child { width: 130px; white-space: nowrap; }
.kbd-table kbd {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  background: var(--n-2);
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 1px 6px;
  margin: 0 1px;
  color: var(--text);
}
.kbd-note {
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.7;
}
.kbd-note kbd {
  font-family: var(--mono); font-size: 11px;
  background: var(--n-2); border: 1px solid var(--border-strong);
  border-radius: 4px; padding: 0 5px;
}

.spinner {
  display: inline-block;
  width: 14px; height: 14px;
  border: 2px solid rgba(255,255,255,.35);
  border-top-color: var(--on-primary);
  border-radius: 50%;
  animation: spin .7s linear infinite;
  vertical-align: -2px;
}
@keyframes spin { to { transform: rotate(360deg); } }

.loading-block {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 50px 20px;
  color: var(--text-dim);
  font-size: 13.5px;
}
.loading-block .spinner { border-color: var(--primary-line); border-top-color: var(--primary); }

/* ==================================================================
   多源采集（建库）与审核界面
   ==================================================================
   这两块 UI 与校对主流程共用同一套设计变量（--primary / --border …），
   不引入新的配色体系，保证视觉一致。
*/

/* ---------------------------------------------------------- 建库视图 */
.build-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}

.build-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
}

.mini-input {
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-family: var(--mono-uy);
  font-size: 13px;
  background: var(--panel);
  color: var(--text);
  width: 100%;
  box-sizing: border-box;
}
.mini-input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--shadow-focus);
}
.mini-input.inline { width: auto; min-width: 140px; }

#buildText {
  min-height: 210px;
  resize: vertical;
  padding: 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-family: var(--mono-uy);
  font-size: 15px;
  line-height: 2;
  background: var(--panel);
  color: var(--text);
}
#buildText:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--shadow-focus);
}

.build-actions { display: flex; gap: 8px; }

.row-2 { display: flex; gap: 10px; }
.mini-field {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11.5px;
  color: var(--text-dim);
}
.mini-field span { font-family: var(--ui); }
.mini-field input {
  padding: 7px 9px;
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  font-size: 13px;
  width: 100%;
  box-sizing: border-box;
}

.hint-text {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.7;
  color: var(--text-dim);
  background: var(--n-1);
  border-inline-start: 3px solid var(--info);
  padding: 7px 10px;
  border-radius: 6px;
}

/* 文档拖放区 */
.drop-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 150px;
  padding: 18px;
  border: 2px dashed var(--border-strong);
  border-radius: 10px;
  background: var(--n-1);
  cursor: pointer;
  text-align: center;
  transition: all .18s;
  font-family: var(--mono-uy);
  font-size: 13.5px;
  color: var(--text-dim);
}
.drop-zone small { font-family: var(--ui); font-size: 11px; }
.drop-zone .drop-note { color: var(--ok); }
.drop-zone:hover, .drop-zone.over {
  border-color: var(--primary);
  background: var(--primary-soft);
  color: var(--primary-dark);
}
.drop-icon { font-size: 30px; line-height: 1; }

.file-list { display: flex; flex-direction: column; gap: 5px; }
.file-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 9px;
  background: var(--n-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 12px;
}
.file-item .fname { flex: 1; overflow: hidden; text-overflow: ellipsis;
                    white-space: nowrap; }
.file-item .fsize { color: var(--text-dim); font-size: 11px; }
.file-item button {
  border: none; background: none; cursor: pointer; color: var(--error);
  font-size: 15px; line-height: 1; padding: 0 3px;
}

/* 进度提示 */
.progress-box {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  background: var(--primary-soft);
  border-radius: 8px;
  font-size: 12.5px;
  color: var(--primary-dark);
}
.spinner {
  width: 15px; height: 15px; flex: none;
  border: 2px solid var(--primary-line);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------- 任务/数据表 */
.jobs-wrap, .grammar-wrap {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 16px;
  overflow: hidden;
}
.jobs-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--border);
  /* 与 .pane-head 同一套彩色语义：模块色的极浅底 + 右侧渐隐，
     让"审核/状态/管理"这些用 jobs-wrap 包裹的模块也随标签换色。
     两处保持一致的写法，是为了避免出现"有的面板跟着变色、有的不变"
     那种半吊子效果 —— 那比全部不变色更让人觉得界面坏了。 */
  background: linear-gradient(
    to inline-end,
    rgba(var(--mod-rgb), .055) 0%,
    transparent 62%
  ), var(--n-1);
  font-size: 13px;
  position: relative;
}
/* 左侧彩色短竖条：与 .pane-head::before 一致的视觉锚点 */
.jobs-head::before {
  content: "";
  inline-size: 3px;
  block-size: 14px;
  border-radius: 2px;
  background: rgb(var(--mod-rgb));
  flex: 0 0 auto;
}
.jobs-head strong { font-family: var(--ui); font-weight: 600; }

.table-scroll { overflow-x: auto; max-height: 460px; overflow-y: auto; }

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  font-family: var(--mono-uy);
}
.data-table th {
  position: sticky; top: 0;
  background: var(--n-2);
  color: var(--text-dim);
  font-family: var(--ui);
  font-weight: 600;
  font-size: 11.5px;
  text-align: start;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  z-index: 1;
}
.data-table td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--n-3);
  vertical-align: top;
}
.data-table tbody tr:hover { background: var(--n-1); }
.data-table .empty-row {
  text-align: center;
  color: var(--text-dim);
  font-family: var(--ui);
  padding: 22px;
}
.cb-col { width: 34px; }

.review-table td:nth-child(2) { font-size: 14px; font-weight: 600; }
.review-table .sentence-cell {
  max-width: 320px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--text-dim);
  direction: rtl;
}

/* 状态徽章 */
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 10.5px;
  font-family: var(--ui);
  font-weight: 600;
  white-space: nowrap;
}
.badge.queued    { background: var(--n-2); color: var(--n-6); }
.badge.running   { background: var(--info-soft); color: var(--primary); }
.badge.done      { background: var(--ok-soft); color: var(--ok); }
.badge.failed    { background: var(--error-soft); color: var(--error-dark); }
.badge.cancelled { background: var(--n-2); color: var(--n-7); }
.badge.active    { background: var(--ok-soft); color: var(--ok); }
.badge.candidate { background: var(--warning-soft); color: var(--warning-dark); }
.badge.rejected  { background: var(--error-soft); color: var(--error-dark); }
.badge.approved  { background: var(--ok-soft); color: var(--ok); }
.badge.pending   { background: var(--warning-soft); color: var(--warning-dark); }

.badge.reason { background: var(--n-2); color: var(--n-6); margin: 1px 2px; }

/* 高风险来源警示条（imla 等禁止全选通过的来源）
   设计意图：在视觉上把「批量通过」这条路明确阻断，
   避免审核者习惯性全选后一键洗白 35 万条未核查词。 */
.guard-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 8px 0;
  padding: 10px 12px;
  border: 1px solid color-mix(in srgb, var(--warning) 55%, transparent);
  border-left: 4px solid var(--warning-dark);
  border-radius: 6px;
  background: var(--warning-soft);
  color: var(--warning-dark);
  font-size: 12.5px;
  line-height: 1.6;
}
.guard-banner .guard-icon { font-size: 16px; line-height: 1.2; }
.guard-banner .guard-text b { color: var(--warning-dark); }

.progress-mini {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 96px;
}
.progress-bar {
  flex: 1;
  height: 5px;
  background: var(--n-3);
  border-radius: 3px;
  overflow: hidden;
  min-width: 52px;
}
.progress-bar i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--primary), var(--primary));
  border-radius: 3px;
  transition: width .3s;
}

/* ---------------------------------------------------------- 审核视图 */
.review-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
.stat-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 14px;
}
.stat-card .v {
  font-size: 22px;
  font-weight: 700;
  color: var(--primary-dark);
  line-height: 1.25;
}
.stat-card .k {
  font-size: 11.5px;
  color: var(--text-dim);
  margin-top: 3px;
  font-family: var(--ui);
}

.review-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.btn.ok      { background: rgba(var(--ok-rgb),.10);  color: var(--ok);
               border-color: rgba(var(--ok-rgb),.30); }
.btn.ok:hover { background: rgba(var(--ok-rgb),.18); }
.btn.danger  { background: rgba(var(--error-rgb),.08);  color: var(--error);
               border-color: rgba(var(--error-rgb),.28); }
.btn.danger:hover { background: rgba(var(--error-rgb),.16); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* ---------------- 站点源管理：弹窗与表单 ---------------- */
.modal-mask {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: rgba(0,0,0,.42);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 28px 16px; overflow-y: auto;
}
/* `[hidden]` 豁免统一在上面「hidden 属性豁免」区块声明，此处不再重复，
   避免两处规则各自演化、其中一处被改动后无人发现。 */
.modal {
  width: min(920px, 100%);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) + 4px);
  box-shadow: 0 12px 40px rgba(0,0,0,.22);
  display: flex; flex-direction: column;
}
.modal-head {
  display: flex; align-items: center; gap: 4px;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.modal-head strong { font-family: var(--mono-uy); }
.modal-head strong > span {
  display: block; font-family: var(--ui); font-size: 11px;
  color: var(--text-dim); font-weight: 400; margin-top: 2px;
}
.modal-body { padding: 14px 16px; }
.modal-foot {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 16px; border-top: 1px solid var(--border);
}
.form-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 14px;
}
.form-grid .mini-field.wide { grid-column: 1 / -1; }
.form-grid .mini-field > span {
  display: block; font-size: 11px; color: var(--text-dim);
  margin-bottom: 3px;
}
.form-grid .mini-field > span em {
  font-style: normal; opacity: .7; margin-left: 6px; font-size: 10px;
}
.mini-field.inline-field {
  display: flex; align-items: center; gap: 6px;
  align-self: end; padding-bottom: 6px;
}
.modal .mini-input { width: 100%; }
.modal .code {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 11.5px; line-height: 1.6; min-height: 56px; resize: vertical;
}
.form-sec {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 8px 12px; margin-top: 12px;
}
.form-sec > summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600;
  padding: 2px 0; user-select: none;
}
.form-sec > summary > span {
  font-family: var(--ui); font-size: 11px; font-weight: 400;
  color: var(--text-dim);
}
.form-sec .form-grid { margin-top: 10px; }

/* 预检结果 */
.probe-box {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radius);
  border: 1px solid var(--border); font-size: 12px; line-height: 1.7;
}
.probe-box.running { display: flex; align-items: center; gap: 8px; }
.probe-box.good { background: rgba(var(--ok-rgb),.06); border-color: rgba(var(--ok-rgb),.30); }
.probe-box.bad  { background: rgba(var(--error-rgb),.05); border-color: rgba(var(--error-rgb),.26); }
.probe-head { font-weight: 600; margin-bottom: 4px; }
.probe-box.good .probe-head { color: var(--ok); }
.probe-box.bad  .probe-head { color: var(--error); }
.probe-head code {
  font-size: 11px; background: rgba(0,0,0,.06);
  padding: 1px 5px; border-radius: 4px;
}
.probe-meta { color: var(--text-dim); font-size: 11.5px; }
.probe-meta b { color: var(--text); }
.probe-preview {
  margin: 8px 0; padding: 8px 10px; max-height: 130px; overflow: auto;
  background: rgba(0,0,0,.035); border-radius: 6px;
  font-family: var(--mono-uy); font-size: 12.5px; line-height: 1.85;
  white-space: pre-wrap; word-break: break-word;
}
.probe-step {
  display: flex; align-items: baseline; gap: 6px;
  padding: 3px 0; font-size: 11.5px;
  border-bottom: 1px dashed var(--border);
}
.probe-step:last-child { border-bottom: none; }
.probe-step .ps-icon { font-weight: 700; flex: 0 0 12px; }
.probe-step.ok .ps-icon { color: var(--ok); }
.probe-step.bad .ps-icon { color: var(--error); }
.probe-step .ps-name { flex: 0 0 auto; font-weight: 600; }
.probe-step .ps-detail { color: var(--text-dim); }
.probe-step .ps-url {
  display: block; font-size: 10.5px; color: var(--primary);
  word-break: break-all;
}
tr.row-off td { opacity: .55; }
.sites-wrap { margin-top: 18px; }

/* ---------------- 任务列表：概览条与失败说明 ---------------- */
.jobs-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 12px;
  margin-bottom: 10px;
  font-size: 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.jobs-summary:empty { display: none; }
.jobs-summary .jstat {
  padding: 2px 8px;
  border-radius: 10px;
  background: rgba(0,0,0,.04);
  color: var(--text-dim);
  white-space: nowrap;
}
.jobs-summary .jstat b { color: var(--text); }
.jobs-summary .jstat.live.on { background: rgba(var(--ok-rgb),.12); color: var(--ok); }
.jobs-summary .jstat.live.off { background: rgba(0,0,0,.05); }
.jobs-summary .jstat.failed { background: rgba(var(--error-rgb),.10); color: var(--error); }
.jobs-summary .jstat.running { background: rgba(var(--info-rgb),.10); color: var(--accent); }

/* 失败/跳过说明：贴在来源列下方，保证用户一眼看到「为什么没入库」 */
.job-note {
  font-size: 10.5px;
  line-height: 1.5;
  margin-top: 3px;
  padding: 3px 6px;
  border-radius: 4px;
  word-break: break-word;
}
.job-note.err  { background: rgba(var(--error-rgb),.08);  color: var(--error); }
.job-note.warn { background: rgba(var(--warning-rgb),.09);  color: var(--warning-dark); }
tr.row-failed td { background: rgba(var(--error-rgb),.03); }
.job-ops { white-space: nowrap; }
.job-ops .btn.mini { padding: 2px 7px; font-size: 11px; }

/* ---------------- 黑名单剔除提示 ---------------- */
.denied-notice {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--text-dim);
  background: rgba(97,97,97,.06);
  border: 1px solid var(--border);
  border-left: 3px solid var(--n-5);
  border-radius: var(--radius);
}
.denied-notice b { color: var(--text); }
.denied-notice .denied-icon { font-size: 14px; }
.denied-notice .denied-detail {
  flex-basis: 100%;
  font-size: 11px;
  line-height: 1.7;
  padding-top: 4px;
  border-top: 1px dashed var(--border);
  word-break: break-all;
}
.denied-notice code { font-size: 11px; opacity: .8; }

/* ---------------- 批量选择助手 ----------------
   319,230 条待审词不可能逐条点勾选，因此提供有边界的批量选择。
   视觉上刻意与危险操作（通过/驳回）区分：助手是中性灰，
   危险操作是绿/红，避免用户误把「选取」当成「通过」。 */
.review-selectbar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 8px 12px;
  background: var(--panel-2, rgba(0,0,0,.02));
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  font-size: 12px;
}
.review-selectbar .sel-label { color: var(--text-dim); white-space: nowrap; }
.review-selectbar .sel-label > span { display: block; font-family: var(--ui);
                                      font-size: 10px; opacity: .7; }
.review-selectbar .sel-sep { width: 1px; height: 18px;
                             background: var(--border); margin: 0 4px; }
.review-selectbar .sel-hint { font-size: 11px; color: var(--text-dim); }
.review-selectbar .sel-hint.warn { color: var(--warning-dark); font-weight: 600; }

/* ---------------- 分页 ---------------- */
.review-pager {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: 12px;
}
.review-pager:empty { display: none; }
.review-pager .pager-info { color: var(--text-dim); white-space: nowrap; }
.review-pager .pager-info b { color: var(--text); }
.review-pager .pager-ctrl { display: flex; align-items: center;
                            gap: 3px; flex-wrap: wrap; }
.review-pager .pager-btn { min-width: 30px; padding: 3px 6px;
                           justify-content: center; }
.review-pager .pager-btn.active {
  background: var(--accent); color: var(--on-primary);
  border-color: var(--accent, var(--accent)); font-weight: 700;
}
.review-pager .pager-gap { color: var(--text-dim); padding: 0 2px; }
.review-pager .pager-jump { display: flex; align-items: center; gap: 5px;
                            margin-left: auto; color: var(--text-dim); }
.review-pager .pager-jump > span > span { display: block; font-family: var(--ui);
                                          font-size: 10px; opacity: .7; }

/* ══════════════════════════════════════════════════════════════════
   审核页升级：高级筛选 / 词族视图 / 详情面板 / 审核轨迹
   ══════════════════════════════════════════════════════════════════ */

/* ---------------- 高级筛选（可折叠）----------------
   318k 积压的唯一解法是「按频次收敛」，但它不该在默认视图里
   占据空间干扰日常操作，故用 <details> 折叠。 */
.review-adv {
  margin: 8px 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
}
.review-adv > summary {
  cursor: pointer; padding: 8px 12px;
  font-family: var(--mono-uy); font-size: 13px;
  display: flex; align-items: center; gap: 8px;
  user-select: none;
}
.review-adv > summary > span:first-of-type {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
}
.review-adv > summary::-webkit-details-marker { color: var(--text-dim); }
/* 已启用筛选时给出醒目标记，避免用户忘了自己在看子集 */
.adv-badge {
  font-family: var(--ui); font-size: 11px; padding: 1px 7px;
  border-radius: 9px; background: var(--accent); color: var(--on-primary);
}
.adv-badge:empty { display: none; }
.adv-grid {
  display: grid; gap: 10px 14px; padding: 4px 12px 10px;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}
.adv-field { display: flex; flex-direction: column; gap: 3px; }
.adv-field.wide { grid-column: span 2; }
.adv-field > span:first-child {
  font-family: var(--mono-uy); font-size: 12px;
  display: flex; flex-direction: column;
}
.adv-field > span:first-child > span {
  font-family: var(--ui); font-size: 10px; color: var(--text-dim);
}
.adv-hint { font-family: var(--ui); font-size: 10px; color: var(--text-dim); }
.adv-actions {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 8px 12px; border-top: 1px solid var(--border);
}
.adv-actions .spacer { flex: 1; }
#advResultHint { color: var(--accent); font-weight: 600; }

/* ---------------- 视图切换栏 ---------------- */
.review-viewbar {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-bottom: 8px;
}
.review-viewbar .spacer { flex: 1; }
.btn.view-on {
  background: var(--accent); color: var(--on-primary); border-color: transparent;
}

/* ---------------- 行内操作按钮 ---------------- */
.ops-col { width: 132px; }
.row-ops { display: flex; gap: 4px; white-space: nowrap; }
.btn.tiny {
  padding: 2px 6px; font-size: 11px; line-height: 1.5;
  border-radius: 5px; min-width: 0;
}
.btn.tiny.ok  { color: var(--ok);    border-color: rgba(var(--ok-rgb),.35); }
.btn.tiny.bad { color: var(--error); border-color: rgba(var(--error-rgb),.35); }

/* 已在正式库 / 被敏感词闸拦下的行：给出视觉区分，
   避免审稿人重复审一个已通过的词，或徒劳点一个必被拒的词。 */
tr.row-inlex > td { opacity: .62; }
tr.row-barred > td { background: rgba(var(--error-rgb),.05); }
tr.row-barred .is-barred { color: var(--error); font-weight: 600; }

.badge.lex {
  background: rgba(var(--ok-rgb),.13); color: var(--ok);
  font-size: 10px; padding: 1px 5px; border-radius: 4px;
}
.badge.barred {
  background: rgba(var(--error-rgb),.13); color: var(--error);
  font-size: 10px; padding: 1px 5px; border-radius: 4px;
}

/* ---------------- 详情面板 ---------------- */
.detail-mask {
  position: fixed; inset: 0; z-index: var(--z-panel);
  background: rgba(0,0,0,.42);
  display: none; align-items: flex-start; justify-content: center;
  padding: 30px 16px; overflow-y: auto;
}
.detail-mask.on { display: flex; }
.detail-panel {
  width: min(680px, 100%);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) + 4px);
  box-shadow: 0 12px 40px rgba(0,0,0,.24);
  display: flex; flex-direction: column;
  max-height: calc(100vh - 60px);
}
.detail-panel.wide { width: min(1000px, 100%); }
.detail-head {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 15px; border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.detail-head strong { font-family: var(--mono-uy); font-size: 15px; }
.detail-head .spacer { flex: 1; }
.detail-body { padding: 14px 15px; overflow-y: auto; }

.det-sec { margin-bottom: 15px; }
.det-sec:last-child { margin-bottom: 0; }
.det-sec > h4 {
  margin: 0 0 6px; font-size: 12px; font-weight: 600;
  color: var(--text-dim);
  display: flex; align-items: center; gap: 6px;
}
.det-sec > h4 .det-n {
  font-family: var(--ui); font-size: 11px; padding: 0 6px;
  border-radius: 8px; background: var(--border); color: var(--text-dim);
}
.det-kv {
  display: grid; grid-template-columns: max-content 1fr;
  gap: 4px 12px; font-size: 12.5px;
}
.det-kv dt { color: var(--text-dim); font-family: var(--ui); font-size: 11.5px; }
.det-kv dd { margin: 0; }
.det-kv dd.mono { font-family: var(--mono-uy); }

/* 词形族：按频次降序的紧凑列表 */
.det-forms { display: flex; flex-wrap: wrap; gap: 4px; }
.det-form {
  font-family: var(--mono-uy); font-size: 12px;
  padding: 2px 7px; border-radius: 5px;
  background: var(--bg); border: 1px solid var(--border);
  cursor: pointer;
}
.det-form:hover { border-color: var(--accent); }
.det-form > em { font-family: var(--ui); font-size: 10px;
                 color: var(--text-dim); font-style: normal; }
.det-form.sel { background: var(--accent); color: var(--on-primary); border-color: transparent; }
.det-form.sel > em { color: color-mix(in srgb, var(--on-primary) 80%, transparent); }

.det-note {
  font-size: 12px; padding: 8px 10px; border-radius: var(--radius);
  background: rgba(var(--warning-rgb),.09);
  border: 1px solid rgba(var(--warning-rgb),.28);
  line-height: 1.6;
}
.det-note.bad {
  background: rgba(var(--error-rgb),.06);
  border-color: rgba(var(--error-rgb),.28);
}
.det-note.good {
  background: rgba(var(--ok-rgb),.06);
  border-color: rgba(var(--ok-rgb),.28);
}

/* 详情面板底部的审核动作 */
.det-actions {
  display: flex; gap: 8px; padding: 11px 15px;
  border-top: 1px solid var(--border); align-items: center;
}
.det-actions .spacer { flex: 1; }

/* ---------------- 审核轨迹表 ---------------- */
.audit-row {
  display: grid;
  grid-template-columns: 150px 92px 1fr 120px;
  gap: 8px; padding: 6px 8px; font-size: 12.5px;
  border-bottom: 1px solid var(--border);
  align-items: baseline;
}
.audit-row.head {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  font-weight: 600; position: sticky; top: 0;
  background: var(--panel); z-index: 1;
}
.audit-row > .a-word { font-family: var(--mono-uy); }
.audit-row > .a-time { font-family: var(--ui); font-size: 11px;
                       color: var(--text-dim); }
.audit-row > .a-note { color: var(--text-dim); font-size: 11.5px;
                       overflow-wrap: anywhere; }
.st-pill { font-size: 10.5px; padding: 1px 7px; border-radius: 9px;
           font-family: var(--ui); }
.st-pill.approved { background: rgba(var(--ok-rgb),.13); color: var(--ok); }
.st-pill.rejected { background: rgba(var(--error-rgb),.13); color: var(--error); }
.st-pill.pending  { background: rgba(var(--warning-rgb),.16); color: var(--warning-dark); }

.audit-summary {
  display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 10px;
  font-size: 12px;
}
.audit-summary .as-item {
  padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--bg);
}
.audit-summary .as-item b { font-family: var(--ui); }

/* 高风险来源警示（详情面板内） */
.det-barred {
  padding: 9px 11px; border-radius: var(--radius);
  background: rgba(var(--error-rgb),.07);
  border: 1px solid rgba(var(--error-rgb),.32);
  font-size: 12.5px; line-height: 1.65; color: var(--error);
}

/* ---------------- 通用工具类 ----------------
   `.dim` / `.mono-uy` 在审核详情、词族、空列表提示中反复使用；
   此前未定义，导致提示文字与词形无法按预期显示。 */
.dim { color: var(--text-dim); font-size: 11.5px; }
.mono-uy { font-family: var(--mono-uy); }

/* ---------------- 语法规则库：概览条与批量操作 ----------------
   规则库此前是**只读**的：41/48 条卡在 candidate 无法启用，
   用户看不出「规则挖出来但其实没生效」。概览条把这件事显式化。 */
.grammar-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 2px 10px;
}
.grammar-bar .jstat {
  font-family: var(--ui); font-size: 11.5px;
  padding: 3px 9px; border-radius: 5px;
  background: var(--bg); border: 1px solid var(--border);
  color: var(--text-dim);
}
.grammar-bar .jstat b { color: var(--text); font-size: 12.5px; }
.grammar-bar .jstat.ok   { border-color: rgba(var(--ok-rgb),.35); color: var(--ok); }
.grammar-bar .jstat.ok b { color: var(--ok); }
.grammar-bar .jstat.warn { border-color: rgba(var(--warning-rgb),.45); color: var(--warning-dark); }
.grammar-bar .jstat.warn b { color: var(--warning-dark); }
#grammarSelectBar { margin-top: 8px; }
#grammarSelectBar .sel-hint { color: var(--text-dim); font-size: 11px; }

/* ---------------- 状态页：语料全景 ----------------
   此前状态页只显示引擎内部状态，用户最关心的
   「语料攒到什么程度了」完全看不到（尽管后端早已备好数据）。 */
.stat-card.wide { grid-column: 1 / -1; }
.stat-card h3 .dim {
  font-family: var(--ui); font-size: 11px; font-weight: 400;
  margin-left: 6px;
}
.stat-row .v.warn-text { color: var(--warning-dark); font-weight: 600; }
.stat-row .v .badge { margin-left: 4px; }

/* 卡片内的说明性小字：解释数字含义（如「为何要重启」）。
   与 .k/.v 区分：它不是一条指标，而是对该卡片的注解。 */
.stat-hint {
  font-size: 11.5px; line-height: 1.55; color: var(--text-dim);
  padding: 7px 0 1px; border-top: 1px dashed var(--border);
  margin-top: 6px;
}

/* 月度柱状图：缺口用红色，让「哪几个月没采到」一眼可见 */
.mo-chart {
  display: flex; align-items: flex-end; gap: 3px;
  overflow-x: auto; padding: 6px 2px 0;
  min-height: 70px;
}
.mo-cell {
  display: flex; flex-direction: column; align-items: center;
  gap: 3px; min-width: 26px; flex: 0 0 auto;
}
.mo-bar {
  width: 16px; border-radius: 3px 3px 0 0;
  background: linear-gradient(180deg, var(--accent), rgba(0,0,0,.18));
  transition: opacity .15s;
}
.mo-cell:hover .mo-bar { opacity: .72; }
.mo-cell.mo-gap .mo-bar {
  background: repeating-linear-gradient(45deg,
    rgba(var(--error-rgb),.65), rgba(var(--error-rgb),.65) 3px,
    rgba(var(--error-rgb),.22) 3px, rgba(var(--error-rgb),.22) 6px);
}
.mo-lab {
  font-family: var(--ui); font-size: 9.5px; color: var(--text-dim);
  white-space: nowrap;
}
.mo-cell.mo-gap .mo-lab { color: var(--error); font-weight: 600; }

/* ---------------- 词典页侧栏 ----------------
   词典页此前是「空搜索框 + 结果区」，用户不知道能查什么、库里有什么。
   侧栏提供概览、近期入库新词（可点击直查）、形态分析示例。 */
.dict-side {
  display: grid; gap: 12px; margin: 14px 0;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.dict-panel {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 14px;
  box-shadow: var(--shadow);
  position: relative;
  overflow: hidden;
}
/* 词典卡片的彩色顶条：进入词典页后这些卡片统一染成紫色系。
   与 .pane::before 手法一致 —— 伪元素绝对定位，不占布局高度，
   因此不会把 auto-fit 网格的列宽/行高算歪。 */
.dict-panel::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  background: linear-gradient(
    to inline-end,
    rgb(var(--mod-rgb)) 0%,
    color-mix(in srgb, rgb(var(--mod-rgb)) 45%, transparent) 100%
  );
}
.dict-panel h4 {
  margin: 0 0 9px; font-size: 13px;
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  /* 标题文字染上模块色的深色版：比纯灰标题更有"这是本模块内容"的归属感 */
  color: color-mix(in srgb, rgb(var(--mod-rgb)) 62%, var(--text));
}
.dict-panel h4 .dim {
  font-family: var(--ui); font-size: 10.5px; font-weight: 400;
}
#dictRecent .det-form, #dictExamples .det-form { cursor: pointer; }

/* ---------------- 积压构成分析 ----------------
   把「318,708 待审」拆成可执行批次，并明确区分
   「离线词表导入」与「日常采集」——后者才是系统健康度指标。 */
.backlog-body { padding: 4px 12px 12px; }
.bk-chips {
  display: grid; gap: 8px; margin-bottom: 10px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.bk-chip {
  display: flex; flex-direction: column; gap: 1px;
  padding: 9px 11px; text-align: left; cursor: pointer;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius); font-family: inherit;
  transition: border-color .15s, transform .1s;
}
.bk-chip:not(:disabled):hover {
  border-color: var(--accent); transform: translateY(-1px);
}
.bk-chip:disabled { opacity: .45; cursor: default; }
.bk-chip .bk-n {
  font-size: 17px; font-weight: 700; font-family: var(--ui);
  color: var(--accent);
}
.bk-chip .bk-l { font-size: 12px; font-weight: 600; }
.bk-chip .bk-s {
  font-size: 10.5px; color: var(--text-dim); font-family: var(--ui);
}
.bk-split {
  display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 9px;
}
.bk-src {
  font-size: 12px; padding: 5px 10px; border-radius: var(--radius);
  background: rgba(var(--warning-rgb),.09);
  border: 1px solid rgba(var(--warning-rgb),.28);
}
.bk-src b { font-family: var(--ui); }
.bk-src.ok {
  background: rgba(var(--ok-rgb),.07);
  border-color: rgba(var(--ok-rgb),.28);
}
.bk-src.dim { background: var(--bg); border-color: var(--border); }
.bk-rec {
  margin: 0; padding-left: 18px; font-size: 12.5px; line-height: 1.75;
}
.bk-rec li { margin-bottom: 2px; }

/* ---------------- 定时采集计划 ----------------
   关键是把「到点了为什么没采」的原因显性化：
   计划暂停 / 周期未到 / 调度器根本没在跑，三者表现完全不同。 */
.jstat-warn { color: var(--warning-dark); }
.jstat-warn .badge.warn {
  background: rgba(var(--warning-rgb),.14);
  border-color: rgba(var(--warning-rgb),.42);
  color: var(--warning-dark);
}
.ok-text { color: var(--ok); font-weight: 600; }
.err-text { color: var(--error-dark); font-weight: 600; }
#schedTable td { vertical-align: top; }
#schedTable .job-ops { white-space: nowrap; }

/* ---------------- 数据统计看板 ----------------
   设计要点：告警在最上方（先说该处理什么），指标卡片在下方。
   趋势图里的**零值日必须留缺口**——被平滑掉的停摆等于隐形故障。 */
.dash-alerts { padding: 0 12px 8px; display: grid; gap: 6px; }
.dash-alert {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 8px 11px; border-radius: var(--radius); font-size: 12.5px;
  line-height: 1.6; border: 1px solid;
}
.dash-alert .da-icon { font-weight: 700; flex: none; }
.dash-alert.err {
  background: rgba(var(--error-rgb),.06); border-color: rgba(var(--error-rgb),.3);
  color: var(--error-dark);
}
.dash-alert.warn {
  background: rgba(var(--warning-rgb),.08); border-color: rgba(var(--warning-rgb),.34);
  color: var(--warning-dark);
}
.dash-alert.ok {
  background: rgba(var(--ok-rgb),.05); border-color: rgba(var(--ok-rgb),.24);
  color: var(--ok-dark);
}
.dash-grid {
  display: grid; gap: 10px; padding: 4px 12px 12px;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
}
.dash-card {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 11px 13px; background: var(--bg);
  display: flex; flex-direction: column; gap: 5px;
}
.dc-head { display: flex; justify-content: space-between; align-items: baseline; }
.dc-uy { font-size: 11px; color: var(--text-dim); }
.dc-zh { font-size: 12px; font-weight: 600; }
.dc-value {
  font-size: 21px; font-weight: 700; font-family: var(--ui);
  color: var(--accent); line-height: 1.15;
}
.dc-small { font-size: 13px; color: var(--text-dim); font-weight: 500; }
.dc-sub { font-size: 11.5px; color: var(--text-dim); line-height: 1.55; }
.dc-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.dc-foot {
  font-size: 10.5px; color: var(--text-dim);
  border-top: 1px dashed var(--border); padding-top: 5px;
  margin-top: auto; line-height: 1.6;
}
/* 迷你趋势图：零值柱为虚线底，一眼看出停摆 */
.spark {
  display: flex; align-items: flex-end; gap: 2px;
  height: 30px; margin: 2px 0;
}
.spark-b {
  flex: 1; min-width: 2px; border-radius: 1px 1px 0 0;
  background: var(--accent); opacity: .55;
}
.spark-b.zero {
  background: repeating-linear-gradient(
    45deg, var(--border), var(--border) 2px, transparent 2px, transparent 4px);
  opacity: .8;
}

/* ==================================================================
   OCR 视图（图片 / PDF 识别 + 排版还原）
   ------------------------------------------------------------------
   布局用「左参数 / 中结果 / 右结构」三栏。中栏最宽，因为识别出的
   维吾尔语正文是主角；右栏给排版结构，让用户一眼看出系统把内容
   理解成了几个段落、有没有误判标题。
   ================================================================== */
.ocr-status {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; margin-bottom: 12px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  font-size: 12.5px; color: var(--text-dim);
}
.ocr-status .dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--text-dim);
}
.ocr-status.ok .dot { background: var(--ok); }
.ocr-status.warn .dot { background: var(--warning); }
.ocr-status.bad .dot { background: var(--error); }
.ocr-status.ok { color: var(--text); }

.ocr-grid {
  display: grid;
  grid-template-columns: minmax(230px, 0.8fr) minmax(320px, 1.9fr) minmax(210px, 0.9fr);
  gap: 12px; align-items: start;
}
@media (max-width: 1100px) { .ocr-grid { grid-template-columns: 1fr; } }

.ocr-panel {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 12px; display: flex; flex-direction: column; gap: 10px;
  min-width: 0;
  position: relative;
  overflow: hidden;
}
.ocr-panel::before {
  content: "";
  position: absolute;
  inset-block-start: 0; inset-inline: 0;
  height: 3px;
  background: linear-gradient(
    to inline-end,
    rgb(var(--mod-rgb)) 0%,
    color-mix(in srgb, rgb(var(--mod-rgb)) 45%, transparent) 100%
  );
}

/* ---- 拖放区 ---- */
.ocr-drop {
  border: 2px dashed var(--border-strong); border-radius: var(--radius);
  padding: 18px 12px; text-align: center; cursor: pointer;
  transition: border-color .15s, background .15s;
  display: flex; flex-direction: column; gap: 5px; align-items: center;
}
.ocr-drop:hover, .ocr-drop.over {
  border-color: var(--primary); background: var(--primary-soft);
}
.ocr-drop-icon { font-size: 24px; color: var(--primary); line-height: 1; }
.ocr-drop-main {
  font-family: var(--mono-uy); font-size: 13.5px; color: var(--text);
  line-height: 1.6;
}
.ocr-drop-main span, .ocr-drop-sub {
  display: block; font-family: var(--ui); font-size: 11.5px;
  color: var(--text-dim);
}
.ocr-drop-sub { word-break: break-all; }
.ocr-drop-sub.files { color: var(--primary); font-weight: 600; }

/* ---- 参数区 ---- */
.ocr-opts { display: flex; flex-direction: column; gap: 9px; }
.ocr-opt {
  display: flex; flex-direction: column; gap: 4px;
  font-family: var(--mono-uy); font-size: 12.5px; color: var(--text);
}
.ocr-opt.row {
  flex-direction: row; align-items: center; gap: 7px;
  cursor: pointer; font-size: 12.5px; line-height: 1.5;
}
.ocr-opt.row input { flex: none; width: 15px; height: 15px; cursor: pointer; }
.ocr-opt .zh {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  margin-inline-start: 2px;
}
.ocr-opt.row .zh { display: block; }
.ocr-opt select.mini-input { width: 100%; }

.btn.wide { width: 100%; justify-content: center; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* ---- 结果区 ---- */
.ocr-result-head {
  display: flex; align-items: center; gap: 8px;
  padding-bottom: 8px; border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.ocr-result-head strong { font-family: var(--mono-uy); font-weight: 700; }
.ocr-result-head strong span {
  font-family: var(--ui); font-size: 11.5px; color: var(--text-dim);
  font-weight: 500;
}
.ocr-preview {
  font-family: var(--mono-uy);
  font-size: 15px; line-height: 2.0;
  direction: rtl; text-align: right;
  max-height: 62vh; overflow-y: auto;
  padding: 4px 2px;
  /* ⚠ 绝不可加 letter-spacing：会破坏阿拉伯字母系的连写形变 */
  letter-spacing: normal;
}
.ocr-preview p {
  margin: 0 0 12px; text-indent: 2em; text-align: justify;
  text-align-last: right;
}
.ocr-preview p.no-indent { text-indent: 0; }
.ocr-preview h1, .ocr-preview h2, .ocr-preview h3 {
  font-family: var(--mono-uy); line-height: 1.5;
  margin: 16px 0 8px; text-align: right;
}
.ocr-preview h1 { font-size: 21px; }
.ocr-preview h2 { font-size: 18px; }
.ocr-preview h3 { font-size: 16px; }
.ocr-preview .ocr-h {
  display: inline-block; font-family: var(--ui); font-size: 10px;
  color: var(--on-primary); background: var(--primary); border-radius: 3px;
  padding: 1px 5px; margin-inline-start: 6px; vertical-align: middle;
  text-indent: 0;
}
.ocr-preview li {
  margin: 0 0 8px; text-indent: 0; list-style-position: inside;
}
.ocr-preview blockquote {
  margin: 8px 0; padding: 4px 10px; color: var(--text-dim);
  border-inline-start: 3px solid var(--border-strong);
  font-size: 0.94em; text-indent: 0;
}
.ocr-preview .lowconf { text-decoration: underline wavy var(--warning); }
.ocr-preview .ltr-isolate { direction: ltr; unicode-bidi: isolate; }
.ocr-preview .ocr-page-sep {
  font-family: var(--ui); font-size: 10.5px; color: var(--text-dim);
  text-align: center; margin: 14px 0 10px; text-indent: 0;
  border-top: 1px dashed var(--border); padding-top: 6px;
}

/* ---- 排版结构面板 ---- */
.ocr-blocks { display: flex; flex-direction: column; gap: 6px;
  max-height: 62vh; overflow-y: auto; }
.ocr-block {
  border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 8px; font-size: 11.5px;
  display: flex; flex-direction: column; gap: 3px;
}
.ocr-block-tag {
  font-family: var(--ui); font-size: 10px; font-weight: 700;
  align-self: flex-start; padding: 1px 6px; border-radius: 3px;
  color: var(--on-primary); background: var(--text-dim);
}
.ocr-block-tag.heading1, .ocr-block-tag.heading2,
.ocr-block-tag.heading3 { background: var(--primary); }
.ocr-block-tag.list_item { background: var(--ok); }
.ocr-block-tag.caption { background: var(--info); }
.ocr-block-tag.table_like { background: var(--warning); }
.ocr-block-tag.page_header, .ocr-block-tag.page_footer,
.ocr-block-tag.page_number { background: var(--n-5); }
.ocr-block-text {
  font-family: var(--mono-uy); font-size: 12.5px; line-height: 1.75;
  direction: rtl; text-align: right; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
  -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}
.ocr-block-meta {
  font-family: var(--ui); font-size: 10px; color: var(--text-dim);
}

/* ---- 下载区 ---- */
.ocr-downloads {
  border-top: 1px solid var(--border); padding-top: 10px;
  display: flex; flex-direction: column; gap: 7px;
}
.ocr-dl-title {
  font-family: var(--mono-uy); font-size: 12.5px; font-weight: 600;
}
.ocr-dl-title span {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  font-weight: 500;
}
.ocr-dl-btns { display: flex; gap: 6px; flex-wrap: wrap; }
.hint-text {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  line-height: 1.6; margin: 0;
}

/* ==================== 双端数据同步（阶段 6）==================== */
.sync-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  padding: 4px 0;
}
.sync-card {
  border: 1px solid var(--border); border-radius: 8px;
  padding: 12px 14px; background: var(--panel);
}
.sc-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.sc-uy { font-family: var(--mono-uy); font-size: 14px; font-weight: 600; }
.sc-zh {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  background: var(--n-2); border-radius: 4px; padding: 1px 6px;
}
.sc-ver {
  font-size: 17px; font-weight: 600; letter-spacing: .4px;
  word-break: break-all; color: var(--primary);
}
.sc-sub {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  margin-top: 4px; line-height: 1.6;
}
.sc-note {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim);
  line-height: 1.75; margin-top: 10px; padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.sc-note b { color: var(--text); }
.sc-note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 10px;
  background: var(--n-2); border-radius: 3px; padding: 1px 4px;
}
.sync-table {
  width: 100%; border-collapse: collapse;
  font-family: var(--ui); font-size: 11px;
}
.sync-table th {
  text-align: left; font-weight: 600; color: var(--text-dim);
  padding: 4px 6px; border-bottom: 1px solid var(--border);
}
.sync-table td { padding: 5px 6px; border-bottom: 1px solid var(--border); }
.sync-table td.num, .sync-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.sync-table .mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
}

.sync-table td.dim {
  color: var(--text-dim);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px;
}

.sync-guide {
  margin-top: 12px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
}
.sg-title {
  font-family: var(--ui); font-size: 12px; font-weight: 600;
  margin-bottom: 8px;
}
.sg-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; line-height: 1.9;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 10px 12px; margin: 0;
  overflow-x: auto; direction: ltr; text-align: left;
  white-space: pre; color: var(--text);
}

/* ==========================================================================
   阶段 5：RBAC 三级权限 + 操作审计
   --------------------------------------------------------------------------
   三块界面：
     ① 顶栏身份徽章 + 账号下拉（谁在操作、能做什么）
     ② 登录遮罩（鉴权启用且未登录）
     ③ 状态页的审计日志面板 + 账号与权限面板
   配色沿用既有变量（--primary/--ok/--error/--warning），
   不引入新色，避免同一界面出现两套视觉语言。
   ========================================================================== */

/* ---------------------------------------------------- 顶栏身份徽章 */
/* 【RTL 布局要点 — 此元素被改坏过三次，务必读完再动】
   本页 dir=rtl（维吾尔语），flex 主轴起点在**右边**，DOM 顺序
   直接映射为物理**右 → 左**。本元素是顶栏的最后一个子元素，
   因此自然落在**物理最左端**，与物理最右端的品牌区形成两端对齐。
   **这就是正确布局，不需要 order、也不需要任何 auto margin。**

   三次失败记录（都是想把本元素改为"贴物理右端"，目标本身错了 ——
   物理右端已被 .brand 占据，两者不可能同时贴右）：
     ✗ margin-inline-start: auto → rtl 下 inline-start 是**右**侧，
        徽章被顶到品牌区上（同侧重叠）
     ✗ margin-inline-end: auto   → 方向虽对，但 flex-wrap 下
        auto margin 吸走整行剩余空间，本元素**独占一行**被顶到行首
     ✗ order: 4                  → order 是**逻辑**排序，rtl 下
        order:4 指向物理最左（恰好=本来的位置，所以毫无效果，
        实测坐标与修改前逐位相同）

   教训：RTL 下不要用 order / auto margin 表达"物理右端"。
   下拉菜单 .user-drop 用 inset-inline-end:0 对齐，同样是逻辑属性、
   但方向正确（它要贴按钮的 inline-end，与物理方向无关），保持不变。 */
.user-menu { position: relative; flex: 0 0 auto; }

.user-btn {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 10px 5px 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  font-family: var(--ui);
  transition: border-color .15s, box-shadow .15s;
}
.user-btn:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }

.avatar {
  width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--primary); color: var(--on-primary);
  font-weight: 700; font-size: 14px; flex: 0 0 auto;
}
/* 未启用鉴权：用警示色，让"当前无保护"这件事一眼可见 */
.avatar.warn { background: var(--warning); }

.user-meta { display: flex; flex-direction: column; line-height: 1.25; text-align: start; }
.user-name { font-size: 13px; font-weight: 600; }
.user-role { font-size: 11px; color: var(--text-dim); }
.caret { color: var(--text-dim); font-size: 11px; }

.user-drop {
  position: absolute; top: calc(100% + 6px); inset-inline-end: 0;
  min-width: 232px; z-index: var(--z-drop);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 6px;
}
.ud-head {
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 10px 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.ud-head strong { font-size: 13px; }
.ud-head .dim { font-size: 11px; color: var(--text-dim); }
.ud-item {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; width: 100%;
  padding: 8px 10px; border: 0; border-radius: 7px;
  background: transparent; cursor: pointer;
  font-family: var(--ui); font-size: 13px; color: var(--text);
  text-align: start;
}
.ud-item:hover { background: var(--n-2); }
.ud-item span { font-size: 11px; color: var(--text-dim); }
.ud-item.danger { color: var(--error); }
.ud-item.danger span { color: var(--error); opacity: .7; }
.ud-sep { height: 1px; background: var(--border); margin: 4px 0; }

/* ------------------------------------------------ 主题切换按钮
   顶栏上独立的小圆钮（不塞进账号下拉里）：主题是**全站偏好**，
   与"我是谁"无关；未登录时顶栏没有账号菜单，但它仍应可用。
   三态循环：跟随系统 → 浅色 → 深色 → 跟随系统…… */
.theme-btn {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text-dim);
  border-radius: 50%;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  transition: border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              background var(--dur) var(--ease);
}
.theme-btn:hover {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-soft);
}
/* 文字型按钮用于下拉里的主题项 */
.ud-item .theme-val { font-size: 11.5px; color: var(--text-dim); }

/* ---------------------------------------------------- 登录遮罩 */
.login-mask {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  display: grid; place-items: center;
  background: linear-gradient(160deg, var(--n-1), var(--n-3));
  padding: 24px;
}
/* `[hidden]` 豁免统一在上面「hidden 属性豁免」区块声明。
   【这里曾经出过的 bug】注意 `.login-mask { display: grid }` 会压过
   HTML 的 `hidden` 属性（UA 样式表的 `[hidden]{display:none}` 优先级极低），
   导致 `<div id="loginMask" hidden>` 的遮罩**始终可见**，与 auth.js 的
   `_setMask()` 互相打架，`?auth=register` 深链也因此表现异常。
   集中豁免就是为了不再出现"某处漏写"。 */
.login-card {
  width: 100%; max-width: 400px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 8px 40px rgba(var(--shadow-rgb), var(--shadow-alpha-3));
  padding: 26px 26px 20px;
}
.lc-brand { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; }
.lc-brand .logo {
  width: 42px; height: 42px; border-radius: 11px;
  display: grid; place-items: center;
  background: var(--primary); color: var(--on-primary);
  font-family: var(--mono-uy); font-size: 22px;
}
.lc-brand strong { display: block; font-family: var(--mono-uy); font-size: 15px; }
.lc-brand small { color: var(--text-dim); font-size: 12px; }

.lc-field { display: block; margin-bottom: 14px; }
.lc-field > span { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 5px; }
.lc-field > span span { font-family: var(--ui); }
.lc-field input {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--border-strong); border-radius: 8px;
  font-family: var(--ui); font-size: 14px;
  background: var(--n-1);
}
.lc-field input:focus {
  outline: none; border-color: var(--primary);
  box-shadow: var(--shadow-focus);
  background: var(--panel);
}
.lc-msg { min-height: 18px; font-size: 12px; color: var(--text-dim); margin-bottom: 8px; }
.lc-msg.err { color: var(--error); }
.lc-msg.ok { color: var(--ok, var(--ok)); }

/* 字段下方的即时提示（口令长度 / 两次是否一致）。
   注册场景必须在**输入时**反馈，而不是等提交失败才告知 ——
   否则用户白填一遍才被拒。 */
.lc-hint {
  display: block; margin-top: 5px;
  font-size: 11px; color: var(--text-dim);
  font-family: var(--ui);
}
.lc-hint.err { color: var(--error); }
.lc-hint.ok { color: var(--ok, var(--ok)); }
.lc-submit {
  width: 100%; justify-content: center;
  display: flex; align-items: center; gap: 6px;
  padding: 11px; font-size: 14px;
}
.lc-submit.busy { opacity: .6; cursor: progress; }
.lc-note {
  margin-top: 18px; padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-dim); line-height: 1.7;
}
.lc-note code {
  display: inline-block; margin-top: 4px; padding: 3px 6px;
  background: var(--n-2); border-radius: 5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10px; direction: ltr; word-break: break-all;
}

/* 窄弹窗（修改口令） */
.modal.narrow { max-width: 430px; }
.chk-inline {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--text-dim); cursor: pointer;
  white-space: nowrap;
}

/* ---------------------------------------------------- 审计日志面板 */
.au-stats {
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
  padding: 12px 14px; margin-bottom: 10px;
  background: var(--n-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.au-stat { display: flex; flex-direction: column; line-height: 1.2; }
.au-num { font-size: 19px; font-weight: 700; }
.au-stat.bad .au-num { color: var(--error); }
.au-lbl { font-size: 11px; color: var(--text-dim); }
.au-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.au-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px;
  background: var(--n-2); font-size: 11px;
}
.au-chip b { font-weight: 700; }
.au-chip.act { background: var(--primary-soft); color: var(--primary-dark); }

.au-list { display: flex; flex-direction: column; gap: 5px; }
.au-item {
  display: flex; gap: 10px;
  padding: 9px 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  border-inline-start: 3px solid var(--ok);
}
/* 失败记录用红边：越权尝试必须能在长列表里一眼跳出来 */
.au-item.failed { border-inline-start-color: var(--error); background: var(--error-soft); }
.au-mark {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--ok); color: var(--on-primary); font-size: 11px; margin-top: 1px;
}
.au-item.failed .au-mark { background: var(--error); }
.au-main { flex: 1; min-width: 0; }
.au-line1 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.au-action { font-weight: 600; font-size: 13px; }
.au-target {
  font-size: 12px; color: var(--text-dim);
  font-family: var(--mono-uy);
  max-width: 46ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.au-tag.sens {
  padding: 1px 7px; border-radius: 999px;
  background: var(--error-soft); color: var(--error); font-size: 10px;
}
.au-line2 {
  display: flex; flex-wrap: wrap; gap: 10px;
  font-size: 11px; color: var(--text-dim); margin-top: 3px;
}
.au-who { font-weight: 600; color: var(--text); }
.au-role { padding: 0 6px; border-radius: 999px; background: var(--n-2); }
.au-ip, .au-time { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.au-det {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: 5px; font-size: 11px; color: var(--text-dim);
}
.au-det b { color: var(--text); font-weight: 600; }
.au-err {
  margin-top: 5px; padding: 5px 9px;
  background: var(--error-soft); border-radius: 6px;
  font-size: 11px; color: var(--error-dark);
}
.au-more {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 0 4px;
}
.au-list.busy { opacity: .55; }

/* ---------------------------------------------------- 账号与权限面板 */
.ua-matrix {
  padding: 12px 14px; margin-bottom: 14px;
  background: var(--n-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.pm-title { font-size: 13px; font-weight: 600; margin-bottom: 10px; }
.pm-title .dim { font-weight: 400; font-size: 11px; color: var(--text-dim); }
.pm-scroll { overflow-x: auto; }
.pm-table { border-collapse: collapse; min-width: 480px; }
.pm-table th, .pm-table td {
  padding: 6px 12px; text-align: center;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
}
.pm-table th { color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.pm-perm { text-align: start !important; white-space: nowrap; }
.pm-perm code {
  display: block; font-size: 10px; color: var(--text-dim);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  direction: ltr;
}
.pm-yes { color: var(--ok); font-weight: 700; font-size: 15px; }
.pm-no { color: var(--n-4); font-size: 15px; }
.pm-notes { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.pm-note {
  font-size: 11px; color: var(--text-dim);
  padding: 7px 10px; background: var(--panel);
  border: 1px solid var(--border); border-radius: 7px;
}
.pm-note b { color: var(--text); }
.pm-note ul { margin: 4px 0 0; padding-inline-start: 18px; }
.pm-note li { margin-bottom: 2px; line-height: 1.6; }

.ua-table-wrap { margin-bottom: 18px; overflow-x: auto; }
.ua-title { font-size: 13px; font-weight: 600; margin: 0 0 8px; }
.ua-title .dim { font-weight: 400; font-size: 11px; color: var(--text-dim); }
.ua-table { width: 100%; border-collapse: collapse; min-width: 720px; }
.ua-table th, .ua-table td {
  padding: 8px 10px; text-align: start;
  border-bottom: 1px solid var(--border);
  font-size: 12px; vertical-align: middle;
}
.ua-table th { color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.ua-table tr.off { opacity: .5; }
.ua-user { display: flex; flex-direction: column; line-height: 1.35; }
.ua-user b { font-size: 13px; }
.ua-user .dim { font-size: 11px; color: var(--text-dim); }
.ua-role { min-width: 120px; }
.ua-acts { display: flex; flex-wrap: wrap; gap: 5px; }
.ua-acts .btn.mini { padding: 3px 9px; font-size: 11px; }
.badge.warn { background: var(--warning-soft); color: var(--warning); }

/* ---------------------------------------------------- 管理后台 */
.admin-tabs { display: flex; gap: 8px; margin: 14px 0; }
.admin-tab {
  padding: 6px 14px; border: 1px solid var(--border);
  background: var(--panel); border-radius: var(--radius);
  cursor: pointer; font-size: 13px;
}
.admin-tab.active { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.admin-section { display: none; }
.admin-section.active { display: block; }
.pager { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.link-like { background: none; border: none; color: var(--primary); cursor: pointer; text-decoration: underline; font-size: 12px; padding: 0; }
.file-drop {
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 18px;
  text-align: center;
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color .2s, background .2s;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.file-drop:hover, .file-drop.drag {
  border-color: var(--primary);
  background: var(--primary-soft);
}

/* ==========================================================================
   手动提交正确词语（校对页）
   对应需求：「校对的时候有些词语词库没有，支持手动输入校对正确的词语，
             手动输入的词语自动提交到审核状态，手动操作，审核通过以后
             所有用户校对系统正常使用。」

   视觉上刻意与「问题清单」同级、常驻可见：这是用户遇到词库缺词时
   唯一的求助入口，折进二级菜单会让人以为系统没提供这个能力。
   ========================================================================== */
.manual-wrap {
  margin-top: 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.manual-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--n-1);
}
.manual-head strong {
  font-family: var(--mono-uy); font-size: 14px; font-weight: 600;
}
.manual-head strong span {
  font-family: var(--ui); font-size: 11px; color: var(--text-dim); font-weight: 400;
}
.manual-hint {
  font-size: 11.5px; color: var(--text-dim); font-family: var(--ui);
  line-height: 1.5;
}
.manual-hint b { color: var(--primary-dark); }
.manual-pending {
  font-size: 11.5px; font-family: var(--ui);
  background: var(--warning-soft); color: var(--warning-dark);
  border: 1px solid var(--primary-line); border-radius: 20px;
  padding: 2px 10px; cursor: pointer; white-space: nowrap;
}
.manual-pending:hover { background: var(--primary-soft); }

.manual-body {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 12px 14px;
}
.manual-body input {
  flex: 1 1 320px; min-width: 200px;
  padding: 9px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  font-family: var(--mono-uy);
  font-size: 16px;
  background: var(--panel);
  transition: .12s;
}
.manual-body input#manualNote {
  flex: 0 1 240px;
  font-family: var(--ui);
  font-size: 12.5px;
}
.manual-body input:focus {
  outline: none; border-color: var(--primary);
  box-shadow: var(--shadow-focus);
}
.manual-body input:disabled { background: var(--n-2); color: var(--text-dim); }

.manual-feedback {
  margin: 0 14px 14px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 12.5px;
  font-family: var(--ui);
  line-height: 1.7;
  border: 1px solid var(--border);
  background: var(--n-1);
}
.manual-feedback.ok   { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 40%, transparent); color: var(--ok-dark); }
.manual-feedback.warn { background: var(--warning-soft); border-color: color-mix(in srgb, var(--warning) 45%, transparent); color: var(--warning-dark); }
.manual-feedback.err  { background: var(--error-soft); border-color: color-mix(in srgb, var(--error) 40%, transparent); color: var(--error-dark); }
/* 词形用维文字体单列，保证长词与混合文本可读 */
.manual-feedback .mf-word {
  font-family: var(--mono-uy);
  font-size: 15px;
  direction: rtl;
  unicode-bidi: isolate;
}
.manual-feedback ul { margin: 6px 0 0; padding-inline-start: 18px; }
.manual-feedback li { margin: 2px 0; }

/* ==========================================================================
   ④ 响应式
   --------------------------------------------------------------------------
   改造前全站只有两条断点（editor-grid / ocr-grid 塌成单列），
   平板与手机下面板内边距、表格、模态、工具栏全都没做过适配。

   【断点选择】取 640 / 980 / 1200 三档：
     · ≤640  手机竖屏：单列、收紧内边距、表格改横向滚动
     · ≤980  平板/手机横屏：主栅格塌成单列
     · ≤1200 小笔记本：OCR 三栏收成两栏（否则中栏被挤到不可读）

   【RTL 注意】所有内边距/外边距都用逻辑属性（padding-inline 等）
   或成对书写（padding-left + padding-right），
   绝不用单个 padding-left —— 那在 RTL 下会把留白加到错误的一侧。
   ========================================================================== */

/* ---- ≤1200：小笔记本，OCR 三栏→两栏 ---- */
@media (max-width: 1200px) {
  .ocr-grid {
    grid-template-columns: minmax(220px, .9fr) minmax(300px, 1.6fr);
  }
  /* 结构面板掉到第二行、跨满整宽 */
  .ocr-grid > .ocr-panel:last-child { grid-column: 1 / -1; }
}

/* ---- ≤980：平板，主栅格全部塌成单列 ---- */
@media (max-width: 980px) {
  .wrap { padding: 14px 16px 32px; }

  .editor-grid,
  .ocr-grid,
  .dict-side,
  .build-grid,
  .sync-grid,
  .dash-grid { grid-template-columns: 1fr; }

  .review-stats { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }

  /* 审阅/建库的工具栏在窄屏下按钮会挤成一坨，改为均分整行 */
  .review-toolbar,
  .jobs-head { row-gap: 8px; }

  /* 模态接近全屏 */
  .modal { width: min(100%, 100%); }
  .detail-panel.wide { width: 100%; }

  /* 详情面板内部的两列表单塌成一列 */
  .form-grid { grid-template-columns: 1fr; }
  .adv-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .adv-field.wide { grid-column: 1 / -1; }
}

/* ---- ≤640：手机竖屏 ---- */
@media (max-width: 640px) {
  html, body { font-size: 14px; }
  .wrap { padding: 12px 12px 28px; }

  /* 顶栏：品牌文字可省，给标签区让位 */
  .topbar { padding: 8px 12px; gap: 10px; }
  .brand-text small { display: none; }
  .tabs { margin-inline-start: 4px; }
  .tab { padding: 5px 10px; font-size: 13px; }
  .tab span { font-size: 9.5px; }
  .engine-badge { font-size: 11px; padding: 4px 9px; }

  /* 编辑器：降低最小高度，避免手机上一屏塞不满还留大片空白 */
  .pane { min-height: 280px; }
  #inputText { font-size: 17px; line-height: 1.95; }
  .result-area { font-size: 17px; line-height: 1.95; }

  /* 工具栏按钮均分：单行两个，点按目标够大（≥40px 高） */
  .toolbar { gap: 8px; }
  .toolbar > .btn,
  .toolbar > .file-btn { flex: 1 1 auto; justify-content: center; min-height: 40px; }

  /* 表格：允许横向滚动而不是把列压到不可读 */
  .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .data-table { min-width: 640px; }

  /* 弹窗几乎全屏，底部按钮均分 */
  .modal-mask { padding: 12px 8px; }
  .modal-foot { flex-wrap: wrap; }
  .modal-foot .btn { flex: 1 1 auto; justify-content: center; }

  /* 手动提交区：输入独占一行，按钮整行 */
  .manual-body { flex-direction: column; align-items: stretch; }
  .manual-body input { flex: 1 1 auto; width: 100%; }
  .manual-body .btn { width: 100%; }

  /* Toast 靠边一些，避免左右顶满 */
  .toast-wrap { padding-inline: 12px; bottom: 14px; }
  .toast { max-width: calc(100vw - 24px); }

  /* 分页控件换行排开 */
  .review-pager { row-gap: 8px; }
  .review-pager .pager-jump { margin-left: 0; flex-basis: 100%; }
}

/* ---- ≤420：极窄（小屏手机）---- */
@media (max-width: 420px) {
  .brand-text strong { font-size: 13px; }
  .logo { width: 32px; height: 32px; font-size: 20px; }
  .stats-inline { font-size: 11.5px; }
  .dict-word { font-size: 24px; }
}

/* ---- 宽屏优化：≥1600 时放宽主内容，利用横向空间 ---- */
@media (min-width: 1600px) {
  .wrap { max-width: 1640px; }
}

/* ---- 打印：交付说明/审核记录常被打印，去掉交互层 ---- */
@media print {
  .topbar, .toast-wrap, .theme-btn, .skip-link,
  .review-selectbar, .toolbar, .footer { display: none !important; }
  body { background: #fff; }
  .pane, .jobs-wrap, .grammar-wrap, .manual-wrap {
    box-shadow: none; border-color: #ccc; break-inside: avoid;
  }
  .view { display: none; }
  .view.active { display: block; }
}
