/* ============================================================
 *  生成物，请勿手改：由 tools/build-css.js 从以下源文件拼接
 *  flat.css + weui-look.css + mobile-redesign.css + unify.css + forest-shell.css
 *  重新生成：node tools/build-css.js
 * ============================================================ */

/* ---- flat.css ---- */
/* ============================================================================
   平铺分区层（flat.css）· 2026-10-15
   ----------------------------------------------------------------------------
   用户要求：「重新设计界面，"去掉套嵌卡片的布局方式"，采用更充分利用空间的方案」
     · 用扁平化 / 全宽布局替代嵌套卡片结构
     · 优化间距与排列，内容占据可用区域而不留过多空白
     · 保持信息层级清晰 —— 靠**分区 / 标题 / 视觉分隔**，而不是卡片套卡片
     · 响应式：不同屏幕尺寸下都高效用满空间
     · 避免过深嵌套，提升可读性与可维护性

   为什么单开一层而不是改 admin-kit.css ✗：
     ① 既有那条 .zone 的定义被**很多页面**依赖 ✓ 直接改它风险大 ✗
     ② 单开一层 = **只加 CSS + 一行 <link>** ✓ 不满意就把 link 去掉 ✓ 秒级回退 ✓
     ③ 加载顺序放在最后（tokens → app → admin → admin-kit → **flat**）→ 用等权重覆盖 ✓
        不靠 !important ✗（本仓已有 950 处 ✗ 不再增加 ✓）

   ⚠️ 打印链路（秩序册 / 奖状 / 日表 / 成绩单）**不受影响** ✓
      —— 那些是另开窗口的独立文档，不引这一层 ✓
   ============================================================================ */

/* ---------------------------------------------------------------- ① 分区去卡片
   原来的 .zone = 白底 + 边框 + 圆角 + 阴影（一张卡片 ✗），而 .zone-b 里又放应用自己的
   .card ✗ → 视觉上就是"卡片里套卡片"（体检器实测 17 处 ✓ 最深链 zone > agg-card ✓）
   现在：.zone 变成**扁平分区** —— 只有"标题 + 一条分隔线 + 内容" ✓ 全宽铺满 ✓        */
.pane .zone,
.pane details.zone {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  border-top: 1px solid var(--line);
  padding-top: var(--sp-4);
  margin-bottom: 0;              /* 间距统一由"分隔线 + padding"表达 ✓ 不再叠 margin ✗ */
  padding-bottom: var(--v-rhythm);
}
/* 首个分区不需要顶部分隔线（页头已经和它分开了 ✓） */
.pane .zone:first-of-type,
.pane details.zone:first-of-type { border-top: 0; padding-top: 0; }

/* 区标题：层级靠**字号 + 字重 + 分隔线** ✓ 不靠卡片边框 ✗
   ⚠️ 给标题行一个最小高度 ✗ —— 去掉卡片内边距后它会掉到 22px ✓
      而体检器有一条"分区头 ≥30px"的护栏（那是 2026-10-01「按钮被压成 6px」那次的产物 ✓
      是**真护栏** ✓ 不能为了我的改动去放松它 ✗ → 反过来给足高度 ✓）*/
.pane .zone-h,
.pane .zone > summary { padding: 0 0 var(--sp-3); min-height: 34px; align-items: center; }
.pane .zone > .zone-h h2,
.pane .zone > summary h2 { font-size: 16px; letter-spacing: .2px; }
/* 说明文字贴到标题右边、不另占一行 ✓（省一行高度 = 多一行内容）*/
.pane .zone .zone-hint { font-size: 13px; }

/* 内容区：**去掉两侧内边距** ✓ 让表格/列表真正铺满可用宽度 ✓ */
.pane .zone > .zone-b { padding: 0; }
.pane .zone > .zone-b.flush { padding: 0; }
.pane details.zone > .zone-b { padding: 0; }
/* 只有"表单类"内容才需要两侧留白（输入框贴边很难受 ✓）*/
.pane .zone > .zone-b.form-body { padding: 0 var(--sp-2); }

/* ------------------------------------------------------------ ② 区内的卡片平铺化
   应用自己的 .card / .agg-card / xxx-card 落在分区里时，一律**去卡片外观** ✓
   保留各卡片之间的一条细分隔线做区分（视觉分隔 ✓ 不是嵌套 ✗）                     */
.pane .zone-b .card,
.pane .zone-b .agg-card,
.pane .zone-b .card2,
.pane .zone-b [class$="-card"],
.pane .zone-b [class*="-card "],
.pane .zone-b [class*="card-"] {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin-left: 0;
  margin-right: 0;
}
/* 平级卡片之间：细分隔线（而不是各自一圈边框 ✗）*/
.pane .zone-b > .card + .card,
.pane .zone-b > .agg-card + .agg-card,
.pane .zone-b > [class$="-card"] + [class$="-card"] {
  border-top: 1px solid var(--line-soft);
  padding-top: var(--sp-3);
}

/* ------------------------------------------------------------ ③ 工具条（筛选区）扁平
   同一页里".zone 是卡片、.toolbar 也是卡片"→ 两层壳 ✗ 也一起去掉 ✓               */
.pane .toolbar {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0 0 var(--sp-3);
  border-bottom: 1px solid var(--line);
}

/* ------------------------------------------------------------ ④ 表格铺满（去双重内边距）
   原来：.zone-b 有 18px 内边距 + .tbl-wrap 自己再有边距 → 窄屏上表格被挤窄 ✗
   现在：表格容器不受分区内边距影响 ✓ 数据区域真正用满宽度 ✓                       */
.pane .zone-b .tbl-wrap,
.pane .zone-b .table-wrap,
.pane .zone-b .tblwrap {
  border: 0;
  border-radius: 0;
  margin-left: 0;
  margin-right: 0;
}

/* ------------------------------------------------------------ ⑤ 概览类统计块：平铺成一行
   概要页的 aggregate 小方块（.agg-card）原来一张张卡片排 ✗
   现在改成"数字 + 标签"的平铺单元，块之间只有细分隔线 ✓                            */
.pane .zone-b .agg-card {
  padding: var(--sp-3) var(--sp-3) var(--sp-3) 0;
}
.pane .zone-b .agg-card h3,
.pane .zone-b .agg-card .k { font-size: 13px; color: var(--ink-3); }
.pane .zone-b .agg-card strong,
.pane .zone-b .agg-card .v { font-size: 22px; }

/* ------------------------------------------------------------ ⑥ 响应式：窄屏再收紧
   手机端分区间距从 16px 收到 12px ✓ 标题下留白也收 ✓ 换来的是一屏多一行内容 ✓          */
@media (max-width: 900px) {
  .pane .zone > .zone-b { padding: 0; }
}
@media (max-width: 640px) {
  .pane .zone,
  .pane details.zone { padding-top: var(--sp-3); padding-bottom: var(--sp-3); }
  .pane .zone > .zone-h,
  .pane .zone > summary { padding-bottom: var(--sp-2); min-height: 32px; }
  /* ⚠️ 说明文字**不能直接藏** ✗ —— 有的分区头只有「标题 + 说明」✓
     藏了说明那一行就塌成 13px ✓（后台用例的「分区头 ≥30px」护栏立刻抓到 ✓ 那是真护栏 ✓）
     → 改成**缩小 + 同排**（省高度但保住那一行 ✓）*/
  .pane .zone .zone-hint { font-size: 12px; }
  .pane .zone-h, .pane .zone > .zone-h { flex-wrap: wrap; min-height: 30px; }
  .pane .zone-tools { margin-left: 0; width: 100%; }
}

/* ------------------------------------------------------------ ⑧ KPI 与快捷入口：小卡片阵列也平铺
   `.stat-row .stat`（班级/教师/数据字典/未读待办）与 `.qc a`（快捷入口）原来各自是一张小卡片 ✗
   在"分区已经扁平"之后，这些小卡片会让页面又变成"一格格描边" ✗
   → 改成**数字/入口单元 + 细分隔线**（竖线分隔 ✓ 靠留白组织 ✓ 而不是靠边框 ✗）         */
.pane .stat-row { gap: 0; border-top: 1px solid var(--line); }
.pane .stat-row .stat {
  background: transparent;
  border: 0;
  border-right: 1px solid var(--line-soft);
  border-radius: 0;
  box-shadow: none;
  padding: var(--sp-3) var(--sp-4) var(--sp-3) 0;
}
.pane .stat-row .stat:last-child { border-right: 0; }
.pane .qc { gap: 0; }
.pane .qc a,
.pane .qc button {
  background: transparent;
  border: 0;
  border-right: 1px solid var(--line-soft);
  border-radius: 0;
  box-shadow: none;
  padding: var(--sp-3) var(--sp-3) var(--sp-3) 0;
}
.pane .qc a:hover,
.pane .qc button:hover { transform: none; background: transparent; color: var(--green-800); }

/* ------------------------------------------------------------ ⑦ 门户首页：同样扁平化
   首页的 .card 也是"一层卡片"✓ 保留轻微底色区分 + 细分隔线，去掉边框/阴影/圆角 ✓
   这样"分区感"来自留白与分隔线，而不是一圈圈描边 ✓                                 */
.home .card,
.wrap > .card {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.home .card + .card,
.wrap > .card + .card {
  border-top: 1px solid var(--line);
  padding-top: var(--sp-4);
  margin-top: var(--sp-4);
}

/* ============================================================
   ⑨ 门户首页 · 重排（2026-10-16 用户口径原话：「首页保持现在的内容，重新排版一下布局」）
   ------------------------------------------------------------
   约束：**六张卡一张不少** ✗
     （e2e-home-visual 有一条「六张卡都在」的断言钉着 ✓ 天气/喊话/课程/值日/工作内容/通知 ✓）
     → 只换**位置与比例** ✓ 内容与 id 一字未动 ✓
   改三处：
     ① 欢迎区 → 压成**一条状态条**（原来独占一行、内容只有一句问候 ✗ 正是"顶部浪费" ✓）
     ② 顶部行 → 由"三块挤一行"改成**两块**（课程表拿到半宽 ✓ 原来被压成窄条 ✗ + 远程喊话窄块 ✓）
     ③ 右栏 → 天气卡搬来作伴 ✓（原来只有通知中心一张卡被 flex:1 拉满整列 ✗）
        列宽 300~360 → 320~400 ✓（通知列表也需要宽度 ✓）
   ⚠️ 只作用于**门户首页的 .cols** ✗（后台也用 .cols ✓ 但结构是"分区+表单" ✓ 不能一起改 ✗）
   ⚠️ 窄屏（≤900）本来就是单列 ✓ 整段跳过 ✗ 手机端一个字节不动 ✓
   ============================================================ */
@media (min-width: 901px) {
  /* ① 欢迎区压成状态条（保留 你好/姓名/slogan/铃铛 ✓ 只收高度 ✓）
        ⚠️ 必须带 `body` ✗ —— app.css 里是 `body.portal .hello`（0,2,1 ✓）比我 `.cols .hello`（0,2,0 ✗）高一级 ✓
          → 第一版没压下去（实测 75px ✗ 目标 ≤72 ✓）✓ 加一级后同权重、我在后 ✓ 生效 ✓ */
  body .cols .hello { padding: 8px 14px; margin-bottom: var(--sp-3); }
  /* ⚠️ 行高也要一起收 ✗ —— 只收内距会卡在 73px（阈值 72 ✓ 差 1px ✓ 临界值最容易反复红 ✓）
     收行高后 ≈58px ✓ 离阈值有富余 ✓ 不制造临界 ✓ */
  body .cols .hello h1 { font-size: 19px; margin: 0 0 2px; line-height: 1.25; }
  body .cols .hello p { font-size: 13px; line-height: 1.35; }
  /* ② 顶部行两块：课程表半宽 + 喊话窄块
        （DOM 里喊话在前 ✓ 用 order 把课程表换到左边 ✓ 不改 DOM ✗）*/
  .cols .top-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(160px, 18vw, 220px);
    gap: var(--v-rhythm);
    align-items: stretch;
  }
  .cols .top-row > .course-card { order: 1; }
  .cols .top-row > .shout-card { order: 2; }
  /* ③ 右栏稍宽；天气卡不伸展 ✓ 通知中心照旧伸展到底
        （与左列底边对齐 ✓ 2026-09-29 既定口径 ✓ 不动 ✗）*/
  .cols .col-side { gap: var(--sp-3); }
  .cols .col-side > .weather-card { flex: 0 0 auto; }
}

/* 首页两列只在桌面档启用；平板 901–1024 单列（与 app.css 的 1025 档一致） */
@media (min-width: 1025px) {
  .cols { grid-template-columns: minmax(0, 1fr) clamp(320px, 26vw, 400px); }
}

/* ============================================================
   ⑩ 首页「关键事项」（2026-10-17 用户要求：智能导入的关键词接到首页 ✓）
   ------------------------------------------------------------
   放在「每周工作内容」卡**底部** ✗ 不新开一张卡（首页六张卡是既定布局 ✓）
   无数据时整块 hidden ✓ 不占位、不留空标题 ✗
   标签风格与后台关键词列表一致 ✓（同一套绿色药丸 ✓ 一眼认得出是同一批数据 ✓）
   ============================================================ */
@media (max-width: 640px) {
}

/* ---- 值日统计表（2026-10-17：文档导入后在弹窗里直接出表，不再嵌统计仪表盘 ✓）---- */
/* ⚠️ 滚动：`.dstat-view` 原本 overflow:hidden（给 iframe 用的 ✓）
   本地表必须能滚 ✓ 否则第 22 行以后就看不见了（用户当天就报了 ✗）*/
.dstat-view.is-table { overflow: auto; -webkit-overflow-scrolling: touch; }
.dstat-tblwrap { padding: 4px 2px 8px; overflow: visible; }
.dstat-tbl {
  width: 100%; border-collapse: collapse;
  font-size: 13px; color: var(--ink);
  background: var(--surface);
}
.dstat-tbl th, .dstat-tbl td {
  padding: 8px 10px; border-bottom: 1px solid var(--line);
  text-align: left; white-space: nowrap;
}
.dstat-tbl thead th {
  font-weight: 600; color: var(--ink-2);
  background: var(--surface-2);
  position: sticky; top: 0; z-index: 1;
}
.dstat-tbl .n { text-align: right; font-variant-numeric: tabular-nums; }
.dstat-tbl .i { width: 44px; color: var(--ink-3); }
.dstat-tbl .roles { color: var(--ink-2); font-size: 12px; white-space: normal; min-width: 150px; }
/* 值日日期（岗位）：用户要看"哪几天、什么岗位" ✓ 允许换行 ✓ */
.dstat-tbl .when { color: var(--ink-2); font-size: 12px; white-space: normal; min-width: 170px; max-width: 260px; }
.dstat-tbl .zero { color: var(--ink-3); }
.dstat-tbl tbody tr:hover { background: var(--green-050); }
/* 合计行吸底 ✓ 滚到中间也知道总量（用户要"能滚着看全"✓）*/
.dstat-tbl tfoot td { font-weight: 600; background: var(--surface-2); position: sticky; bottom: 0; z-index: 1; }
/* 表头吸顶 ✓（滚动时不至于忘了这列是什么 ✓）*/
.dstat-tbl thead th { box-shadow: 0 1px 0 var(--line); }
/* ---- 手机端统计弹窗（2026-10-18 用户反馈"排版有问题"✓）----
   问题：① 5 个分项列把表撑到 >600px ✓ 必须横滑才看得到"总次数" ✗
        ② 标题 + "数据来自…" + 全屏/关闭 挤在 390px 里 → 标题断成三行 ✗
   做法：① 只保留 **教师 / 角色 / 值班日 / 总次数** 四列（分项摘要已在「角色」里 ✓
           如"值周领导×1、护学岗轮值×1" ✓ 手机上够用 ✓）
        ② 标题单行、"数据来自…"隐藏、按钮缩小 ✓
   ⚠️ 只影响 ≤620px ✗ 桌面端仍是完整 8 列 ✓ */
@media (max-width: 640px) {
  .dstat-tbl { font-size: 13px; }
  .dstat-tbl th, .dstat-tbl td { padding: 8px 6px; }
  .dstat-tbl .roles { min-width: 0; max-width: 108px; }
  .dstat-tbl .when { min-width: 0; max-width: 104px; font-size: 12px; }
  /* 第 6 列起 = 值周领导/值日领导/值日教师/交通/护学 的分项次数 ✓ 手机上收起 ✓ */
  .dstat-tbl thead th:nth-child(n+6),
  .dstat-tbl tbody td:nth-child(n+6),
  .dstat-tbl tfoot td:nth-child(n+6) { display: none; }
  .dstat-tbl .i { width: 30px; }
  /* 表头区：标题独占一行 ✓ 来源说明让位 ✓ 按钮缩小 ✓ */
  .dstat-head { flex-wrap: wrap; row-gap: 6px; padding: 10px 12px; }
  .dstat-head h3 { font-size: 15px; line-height: 1.3; }
  .dstat-actions { gap: 6px; }
  .dstat-actions .dstat-newwin { display: none; }
  .dstat-actions .btn { padding: 6px 10px; font-size: 13px; }
  .dstat-box { width: 100vw; height: 100vh; border-radius: 0; }
  .dstat-mask { padding: 0; }
  .dstat-tblwrap { padding: 2px 0 6px; }
}

/* 值日统计范围切换（2026-10-18 · 全部累计 / 仅本周）✓ 用户要的是跨周累计 ✓ */
.duty-stats-inline .dstat-scope { display: inline-flex; margin: 0 0 8px; }
.dstat-scope .seg-i { font-size: 13px; padding: 5px 12px; }


/* ---- weui-look.css ---- */
/* ============================================================================
   外观层（weui-look.css）· 2026-10-15
   ----------------------------------------------------------------------------
   用户要求：「我要 UI 都换掉，套用模板」
   做法：把 **WeUI（微信官方，MIT）的设计语言**套到**现有类名**上 ——
      · 不引 WeUI 的类名 ✗（引了也管不到我们的 DOM ✗）
      · 不改一行业务 JS ✗（现有几千处 class="btn2 …" 保持原样 ✓）
      · 86 套 e2e 用例按 DOM/类名断言 → **零改动** ✓（这是能"整套换皮"的前提 ✓）

   模板来源与取用范围（F:\worbuddy\素材\UI素材库\纯HTML-CSS模板\WeUI）：
      · 取：**语义令牌**（文字/表面/分隔/圆角/字重/触控尺寸）+ 组件规格（按钮/单元格/
            徽标/对话框/抽屉/toast/开关/列表/输入/searchbar/loading/空态/骨架）
      · 不取：它的品牌绿（#018942 是微信绿 ✗）—— 本校品牌色来自校徽（草绿 #6fd900 系 ✓
              tokens.css 已核算对比度 ✓）→ **结构学 WeUI、颜色用本校** ✓
      · 不取：字体图标（内网/微信加载慢 ✗）· 它的 layout 类 ✗

   与既有层的关系（加载顺序）：
      tokens.css → app.css → admin.css → admin-kit.css → flat.css → **本层**
      本层是**最后一层**：等权重覆盖 ✓ 不引 !important ✗（仓库已有 950 处 ✗ 不再加 ✓）
   ⚠️ 只改「观感」（颜色/圆角/边线/字重/阴影/触控尺寸）
      —— 不动栅格、不动定位、不动间距节奏 ✗（动了就会踩到布局体检 ✓ 那是另一件事 ✓）
   ============================================================================ */

:root {
  /* ---------- ① 取 WeUI 的**语义阶**（值取自模板 base/theme/vars/light.less ✓） ---------- */
  --w-fg-0: #000;                        /* 主要文字 */
  --w-fg-1: rgba(0, 0, 0, .6);           /* 次要 */
  --w-fg-2: rgba(0, 0, 0, .42);          /* 弱化 */
  --w-line-strong: rgba(0, 0, 0, .1);    /* 分隔线（比现有 --line 更"模板味"） */
  --w-bg-page: #ededed;                  /* 页面底（WeUI BG-0）*/
  --w-bg-card: #fff;                     /* 面（WeUI BG-2）*/
  --w-radius-cell: 8px;                  /* 单元格/按钮圆角 */
  --w-radius-sheet: 12px;                /* 对话框/抽屉 */
  --w-tap: 44px;                         /* WeUI 的触控尺寸（正合本仓体检规则 ✓） */
  --w-danger: #fa5151;                   /* WeUI 危险红（比现有 #b3261e 更"模板"） */
  --w-link: #576b95;                     /* WeUI 链接蓝 */
}

/* ============================================================ ①b 卡中卡 → 内层去壳
   体检器实测（2026-10-15）：`card2 > card2`、`card2 > chart-card` 这类"卡片里套卡片" ✗
   用户要求「去掉套嵌卡片的布局方式」✓ → 内层一律去壳（边框/圆角/阴影/底色 ✓）
   层级改由**细分隔线 + 留白**表达 ✓（与 flat.css 同一个口径 ✓）
   ⚠️ 放在外观层而不是各应用 CSS ✗：一处定义、所有接了外观层的页面同时生效 ✓
   ⚠️ 只对"外层已经是卡片"的情况生效 ✓（顶层卡片外观不变 ✓ 免得全站变成一马平川 ✗） */
.card2 .card2,
.card2 [class$="-card"],
.card2 .chart-card,
.card .chart-card {
  /* ⚠️ 这里**必须** !important ✗ —— 成绩助手的 app.css 里有 217 处 !important（历史包袱 ✓）
     实测：不加的话 `.chart-card` 的边框/圆角照样在 ✓ 体检器继续报 card2 > chart-card ✓
     （这是本层唯一一处 !important 白名单 ✓ 只针对"卡中卡去壳"这一件事 ✓ 不加别的 ✗）*/
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
}
.card2 .card2 + .card2,
.card2 [class$="-card"] + [class$="-card"],
.card2 .chart-card + .chart-card {
  border-top: 1px solid var(--line-soft);
  padding-top: var(--sp-3);
  margin-top: var(--sp-3);
}

/* ============================================================ ② 按钮（**统一观感** ✗ 不分主次）
   ⚠️ 这里的口径不是 WeUI 的"主按钮实心绿" ✗ —— 是**本校 2026-10-10 用户明确要求**：
      「三个按钮同一种样式，**不要单独一个绿的**，统一风格与颜色」✓
      这条被 3 条用例钉着（活动助手 UI / 手机端 / 列表按钮视觉权重 ✓）
      实测：我第一版按 WeUI 做了"主按钮实心绿 + 幽灵透明" ✗ → 三条立刻红 ✓
   → 四种写法（默认/主/次/文字）**底、字、边完全一致** ✓
     区分只靠**位置与文案** ✓ 不靠颜色权重 ✗（这正是用户要的 ✓） */
.btn, .btn2, .k-btn,
.btn.primary, .btn2.primary, .btn-primary, .k-btn.primary,
.btn.ghost, .btn2.ghost, .btn.text, .btn2.text {
  background: var(--w-bg-card);
  border: 1px solid var(--line);
  color: var(--ink);
  /* ⚠️ **不设圆角** ✗ —— 应用自己那套 12px 已经是"四个字都一样" ✓
     我在第一版里把它改成 8px ✗ 结果 1 个变 8、3 个还是 12 → **是我制造了不一致** ✓
     （用例报的 {"…|8px":1,"…|12px":3} ✓ 正是它抓出来的 ✓）→ 跟应用走 ✓ */
  font-weight: 500;
  box-shadow: none;
  transition: background-color var(--t-fast), border-color var(--t-fast);
}
/* ⚠️ 必须再 !important 压一次 ✗ —— 各应用 app.css 里还留着更具体的"绿色主按钮"规则 ✓
   （形如 `.xxx .btn2.primary{color:#2f7a0a}` 这种 ✓ 权重高过本层 ✓ 实测字色仍有两种 ✓）
   这是本层第二处（也是最后一处）白名单 !important ✓ 只压"四种写法一致"这一件事 ✓ */
/* 加 `body` 前缀再抬一档权重 ✓ —— 应用里那条是 `.btn2.primary{border-radius:…!important}`（0,2,0 ✓）
   同权重谁在后谁赢 ✗ 但我们不可能知道每一处 ✓ → 用 (0,2,1) 稳压一级 ✓（比堆 !important 干净 ✓）*/
body .btn, body .btn2, body .k-btn,
body .btn.primary, body .btn2.primary, body .btn-primary, body .k-btn.primary,
body .btn.ghost, body .btn2.ghost, body .btn.text, body .btn2.text {
  background: var(--w-bg-card) !important;
  border: 1px solid var(--line) !important;
  color: var(--ink) !important;
}
.btn:hover, .btn2:hover, .k-btn:hover { border-color: var(--green-100) !important; background: var(--green-050) !important; }
/* 危险按钮：语义色，保留（"删除/停用"必须一眼可辨 ✓ 且不在上面那三条断言覆盖的列表里 ✓）*/
.btn.danger, .btn2.danger {
  background: var(--w-danger); border-color: var(--w-danger); color: #fff;
}
/* 手机端：触控尺寸拉到 WeUI 的 44 ✓（这正是手机体检的红线 ✓）
   ⚠️ **宽也要保底** ✗ —— 有的按钮本来正好 44×44 ✓ 被任何一点边框/内边距变化压成 42 ✓
      就够判"触控区 <44" ✗（组卷助手「整面」本轮真踩到 ✓）
      → 不跟应用自己那些高优先级规则纠缠 ✗ 直接给最小宽高 ✓（观感不变 ✓ 只是可点区变大 ✓）*/
@media (max-width: 640px) {
  /* ⚠️ **只保触控尺寸，不碰圆角** ✗ —— 这里再设圆角就会把某一类按钮单独改成 8px ✓
     而应用里那套是 12px → 用例立刻报「主按钮视觉一致」不一致 ✓（本轮连踩两次 ✓）*/
  .btn, .btn2, .k-btn { min-height: var(--w-tap); min-width: var(--w-tap); }
  .btn.ghost, .btn2.ghost, .btn2.sm.ghost { min-width: var(--w-tap); }
}

/* ============================================================ ③ 标签/徽标
   WeUI badge：大圆角胶囊 · 12px（本仓体检要求 ≥12px ✗ 不用它的 10px ✓）
   ⚠️ **不改 padding** ✗ —— 有些页面把 `.chip` 当**可点按钮**用（组卷助手的选项胶囊 ✓）
      压它的内边距 = 把触控区压到 44px 以下 ✗（手机端体检立刻红 ✓ 2026-10-15 本轮真踩过 ✓）
      要小徽标就交给 `.tag`（非交互 ✓）；可点的要保证 ≥44px（下面那条 ✓）*/
.tag, .chip, .badge, .k-tag {
  border-radius: var(--r-pill);
  font-weight: 500;
  border: 0;
}
.tag, .badge, .k-tag { font-size: var(--fs-xs); }
@media (max-width: 640px) {
  /* 可点的小胶囊：给足触控高度 ✓（观感不变 ✓ 只是可点区域变大 ✓）*/
  button.chip, a.chip, button.tag, a.tag, [role="button"].chip, [data-*]:not(div) > .chip {
    min-height: var(--w-tap);
    display: inline-flex;
    align-items: center;
  }
}
.tag.g, .tag.ok { background: var(--green-100); color: var(--green-800); }
.tag.n { background: var(--line-soft); color: var(--ink-2); }
.tag.w { background: var(--warn-bg); color: var(--warn); }
.tag.d { background: var(--danger-bg); color: var(--danger); }

/* ============================================================ ④ 输入/下拉
   WeUI：8px 圆角 + 极浅底 + 聚焦时品牌边线
   ⚠️ 字号锁 16px —— iOS 聚焦 <16px 会把整页放大 ✗（本仓 10-06 踩过 ✓） */
input[type=text], input[type=search], input[type=number], input[type=date],
input[type=password], select, textarea, .inp, .k-input, .hd-sel, .srch {
  border-radius: var(--w-radius-cell);
  border: 1px solid var(--line);
  background: var(--surface-2);
  font-size: 16px;
  transition: border-color var(--t-fast), background-color var(--t-fast);
}
input:focus, select:focus, textarea:focus, .inp:focus, .k-input:focus, .hd-sel:focus, .srch:focus {
  outline: none;
  border-color: var(--green-700);
  background: var(--w-bg-card);
  box-shadow: 0 0 0 3px var(--green-050);
}
/* 表格内的小输入框不放大（密集录入 ✓ 但保证 ≥14px 不触发缩放问题：桌面无此问题 ✓） */
@media (min-width: 641px) {
  .tbl input, table input { font-size: var(--fs-sm); }
}

/* ============================================================ ⑤ 分区/列表（WeUI cell 观感）
   单元格：上边线 1px --w-line-strong、内距 16/12、可点行 hover 变浅底 ✓ */
.zone-h, .zone > summary {
  border-bottom: 1px solid var(--line);
  padding-bottom: var(--sp-2);
}
.list-row, .row-item, .mini-item, .agg-card, .stat-row .stat {
  border-bottom: 1px solid var(--line-soft);
}
.list-row:last-child, .mini-item:last-child, .agg-card:last-child { border-bottom: 0; }
/* 表格：WeUI 式"无竖线、只有横线 + 表头浅底"（比满框更像模板 ✓ 也更适合读数字 ✓） */
table.tbl, table.mt, .k-table { border-collapse: collapse; }
table.tbl th, table.mt th, .k-table th {
  background: var(--surface-2);
  font-weight: 500;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
table.tbl td, table.mt td, .k-table td { border-bottom: 1px solid var(--line-soft); }
table.tbl tr:hover td, table.mt tr:hover td { background: var(--green-050); }
/* 数字列：等宽数字 + 右对齐（扫读不跳行 ✓） */
.tbl td.num, .tbl th.num, .num { font-family: var(--font-num); font-variant-numeric: tabular-nums; }

/* ============================================================ ⑥ 对话框 / 抽屉（WeUI dialog / half-screen）
   圆角 12 · 白面 · 深一点的遮罩 · 手机上从底部升起（手机端专属体验 ✓） */
.modal-box, .modal, .sheet, .k-modal, .ev-modal-box {
  border-radius: var(--w-radius-sheet);
  box-shadow: var(--sh-3);
  border: 0;
}
.modal-mask, .scrim, .ev-modal {
  background: rgba(0, 0, 0, .5);
}
@media (max-width: 640px) {
  .modal-box, .modal, .sheet { border-radius: var(--w-radius-sheet) var(--w-radius-sheet) 0 0; }
}

/* ============================================================ ⑦ 提示 toast（WeUI toast）
   WeUI：深色半透明圆角块 + 白字 + 居中偏下 ✓ */
.ui-toast, .toast, #toastBox > * {
  background: rgba(0, 0, 0, .78);
  color: #fff;
  border-radius: var(--w-radius-cell);
  box-shadow: none;
  border: 0;
  font-size: var(--fs-sm);
}

/* ============================================================ ⑧ 开关/勾选（WeUI switch）
   开关：胶囊 + 品牌绿开启态 ✓ 尺寸 51×31（WeUI 标准 ✓ ≥44 触控 ✓） */
.sw, .switch, .k-switch, label.sw input + span {
  border-radius: var(--r-pill);
}
input[type=checkbox], input[type=radio] { accent-color: var(--green-800); }

/* ============================================================ ⑨ 页签（WeUI tab）
   选中态：品牌绿文字 + 底部 2px 下划线（不用整块填充 ✓ 更干净 ✓） */
.tabs button, .k-tabs button, .tab-btn {
  border: 0;
  background: transparent;
  border-radius: 0;
  border-bottom: 2px solid transparent;
  font-weight: 500;
  color: var(--ink-2);
}
.tabs button.on, .tabs button.active, .k-tabs button.on, .tab-btn.on {
  color: var(--green-800);
  border-bottom-color: var(--green-800);
  background: transparent;
  font-weight: 600;
}

/* ============================================================ ⑩ 空态 / 加载（WeUI msg / loading）
   空态：居中、图标 48px、标题 15/600、说明 13/次要、下面一个主按钮 ✓ */
.empty2:not(td):not(th), .empty:not(td):not(th), .k-empty {
  color: var(--ink-3);
  border: 0;
  background: transparent;
  padding: var(--sp-6) var(--sp-4);
  font-size: var(--fs-sm);
  line-height: var(--lh-base);
}
/* 加载：WeUI 三缺环是它的招牌 ✓ 用纯 CSS 复刻（不引图片 ✗） */
.k-loading, .loading, .spin {
  width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--green-800);
  animation: k-spin .8s linear infinite;
}
@keyframes k-spin { to { transform: rotate(360deg); } }

/* ============================================================ ⑪ 骨架屏（新增 ✓ 长页不再白屏 ✗）
   用法：<div class="k-skeleton" style="height:16px"></div> 多个并排即可 ✓ */
.k-skeleton {
  background: linear-gradient(90deg, var(--line-soft) 25%, var(--surface-2) 37%, var(--line-soft) 63%);
  background-size: 400% 100%;
  animation: k-skel 1.2s ease-in-out infinite;
  border-radius: var(--w-radius-cell);
  min-height: 14px;
}
@keyframes k-skel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* ============================================================ ⑫ 链接（WeUI link 蓝）
   ⚠️ 只在正文/说明里生效 ✗ 不动按钮内的链接（否则按钮文字会变蓝 ✗） */
.zone-b a:not(.btn):not(.btn2), article a, .doc a { color: var(--w-link); text-decoration: none; }
.zone-b a:not(.btn):not(.btn2):hover { text-decoration: underline; }

/* ============================================================ ⑫b 手机端小字兜底（把"看不清"这条治掉 ✓）
   背景：活动助手手机端列表里 `.tag` 9.5px / `.ev-meta` 10.5px、行高 1.3 ✗
        （`apps/活动助手/public/css/app2.css` 里当年"把列表压紧"那段覆盖 ✓）
   这**不是审美问题** ✗：手机端正文 <12px、行高 <1.35 是本仓体检的硬红线 ✓
        也是"手机上看着眯眼、点不准"的直接来源 ✓ → 已在全站手机端体检里红了很久（唯一一条 ✓）
   → 在本层给**手机端**兜底到 12px / 1.4 ✓（桌面端不动 ✗ 桌面信息密度优先 ✓）
   ⚠️ 放在外观层而不是改 app2.css ✗：那一层是"手机端压缩"的历史产物 ✓
      改它要连带核对一堆相关规则 ✓ 这里只需一条兜底 ✓ 回退也只删这一段 ✓ */
@media (max-width: 640px) {
  .tag, .badge, .chip, .ev-meta, .task-meta, .dim, .hint, small {
    font-size: var(--fs-xs) !important;      /* 12px ✓ 不低于体检红线 ✓ */
  }
  .ev-meta, .task-meta, .hint { line-height: 1.4 !important; }
}

/* ============================================================ ⑭ 桌面表格：统一间距 + 消除"过疏"（2026-10-16 用户要求）
   实测（e2e-layout 本轮新增的表格几何断言，不是感觉 ✓）：
     · 平均行高 46~90px ✗（红线 44px）—— 下载中心 **90px**（最高 104 ✗）门户后台 54~59 ✗ 成绩助手后台 52 ✗
     · 下载中心为什么 90px：**首格堆了 6 段文字**（图标 · 名称 · 分类 · 图标尺寸 · 说明 · AI 标记 ✗）
     · 内距各页不一：门户 14px / 课程安排 1px / 值日 4px ✗ —— 这就是"有时过疏、有时过挤" ✗
   改法（全部落在**密度与间距**层 ✓ 不动结构、不动功能 ✓）：
     ① 统一内距 **8px 12px** + 行高 **1.35**（全站一套 ✗ 不再各页各写 ✓）
     ② 表头与**首列** nowrap（短标签被挤成竖排 = 最难看的一种"字体重叠" ✗
        实测：课程安排 396 个窄格、值日 24 个 ✓ 都是首列/表头 ✓）
     ③ 多段文字格 → **两行封顶**（完整内容仍在 DOM ✓ 悬停可看 ✓ 行高不再被撑暴 ✓）
     ④ 纵向居中（原来 top 对齐，矮格与高格错位 → 看着更空 ✗） */
@media (min-width: 641px) {
  .tbl th, .tbl td,
  table.mt th, table.mt td, table.mt2 th, table.mt2 td,
  .k-table th, .k-table td {
    /* ★ 2026-10-16 P4：桌面再收一档（7px12px → **6px 10px** ✓ 行高 1.3 → 1.25 ✓）
       目的是"一屏 ≥10 行 + 单行格 ≤34px"（体检新加的两条判据 ✓ 见 e2e-layout.js）
       ⚠️ 只收**桌面** ✗ 手机端另有"触控 ≥44px"的硬要求 ✓ 收过头会把触控压死 ✗ */
    padding: 6px 10px !important;
    line-height: 1.25 !important;
    vertical-align: middle !important;
  }
  /* 表头再贴一点（表头是"导读"✗ 不需要和正文一样宽松 ✓）*/
  .tbl th, table.mt th, table.mt2 th, .k-table th { padding-top: 6px !important; padding-bottom: 6px !important; }
  /* 副行（.dim 那种"分类/时间/说明"）再压一点行距 ✓
     一行主文字 + 一行副文字 的格：1.3 + 1.25 才能落进"平均行高 ≤50" ✓ */
  .tbl td .dim, .tbl td small, table.mt td .dim, table.mt td small { line-height: 1.25 !important; }
  .tbl th, table.mt th, table.mt2 th, .k-table th {
    white-space: nowrap !important;
    font-weight: 500 !important;
    background: var(--surface-2) !important;   /* 表头浅底（读表时眼睛不串行 ✓）*/
  }
  /* 首列：短标签列不许竖排 ✗（「一年级」竖着写那种 ✓ 用户截图里的 ✓）
     ⚠️ 用 `:not(.xp)` 收范围 ✗ —— 实测过的表类名：
          .mt ×18（成绩/活动/智能打印/组卷）｜ .tbl/.stat-tbl/.mf-tbl（课程安排）
          .data ×10（班务）｜ (无类名)（成绩助手「班级全科」✓ 材料汇总）｜ **.xp（值日日表 20 列网格 ✗）**
        第一版写成 `table tr > *:first-child`（一刀切 ✓）→ 把 `.xp` 那张宽表首列也 nowrap ✗
        → 表格宽度从 721px 涨到 **1393px** ✗ 布局体检立刻报"表格横滑"✗（本轮自己踩的 ✓）
     ⚠️ 只要 nowrap **就够** ✗ 不给 min-width ✓（浏览器会按内容把首列撑到放得下 ✓）*/
  table:not(.xp) tr > *:first-child { white-space: nowrap !important; }
  /* 多段文字格两行封顶（下载中心首格那种 ✗）*/
  /* ★ 2026-10-16 P4：桌面**收成一行** ✗（原两行 ✓）
     实测：下载中心 10 行表只看到 9 行 ✗（首格"图标 + 名称 + 一行说明"把行高撑到 78px ✓）
     收成一行后行高 ≈62px ✓ 10 行全进首屏 ✓（完整内容本来就在 title 里 ✓ 悬停即看全 ✓）*/
  .dl-meta, .cell-clamp {
    display: -webkit-box !important;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* 表格外壳统一（原来有的圆角有的直角 ✗ 桌面用直角更"数据表"✓）*/
  .tbl-wrap, .tblwrap, .table-wrap { border-radius: 0; }
}

/* ============================================================ ⑯ 门户后台·概览：改**单列**（2026-10-16）
   用户截图反馈：「卡片内外及上下左右存在大量未利用空间 ✗」「电脑端后台布局重新规划 ✓」
   实测根因：概览页用的是 `.cols`（左 1fr + 右 clamp(300~360px)）✗
     · 左列是「子应用运行状态」长表 ✓ 右列只有「学校与学期」一张短卡 ✗
     · 首页那套"最后一张卡 flex:1 吸收高度差"只对首页的 .notif-card 生效 ✗
       → 后台这里右列底部就空出一大片 ✓（既拥挤又空旷的观感来源之一 ✓）
   改法（用户已授权"布局可自由调整" ✓）：后台概览**改单列** ✓
     · 子应用运行状态表拿到整行宽（+约 360px ✓ 宽表更不容易横滑 ✓）
     · 学校与学期变成整行宽的一个分区 ✓ 4 个字段一行排开 ✓ 不留空 ✗
   ⚠️ 只作用于**概览**这一个 pane ✗ 不动其它页的两列结构 ✓
   ⚠️ 阈值 901 与 admin-kit 的窄屏断点对齐 ✓（窄屏本来就是单列 ✗ 不用重复 ✓）*/
@media (min-width: 901px) {
  #pane-ov .cols { grid-template-columns: minmax(0, 1fr); }
  #pane-ov .col-side { display: block; }
}

/* ============================================================ ⑳ 表体骨架屏（2026-10-16 用户要求）
   场景：长表首次加载（监控日志 / 审计文件 / 学生名册 / 课程表 ✓）
   原来：表头下面塞一行灰字「正在读取…」✗ → 第一眼像卡住 ✓（"空旷"观感来源之一 ✓）
   现在：3 条 shimmer 条 ✓ 与门户首页那套同观感 ✓（复用 admin-kit 里的 `.skl` ✓ 只补"放进表格里"这一层 ✓）
   ⚠️ 表头**保持可见** ✗ —— 让用户知道"表在这儿，正在往里填" ✓ 比整体白屏好得多 ✓ */
.sk-rows { padding: 0 !important; }
.sk-rows .skl { padding: 14px 12px; gap: 10px; }
.sk-rows .skl i { height: 10px; }

/* 应用页的**通用骨架屏**（2026-10-16 第三批：把 `<div class="empty2">加载中…</div>` 换掉 ✓）
   为什么单独做一个类 ✗：应用里的加载占位大多是**静态 HTML**（`#clBody`/`#bkBody` 这类 ✓）
   静态占位在 JS 起来之前就画出来了 ✓ 用不了 `UIK.loading()`（那时脚本还没执行 ✓）
   → 直接给一个纯 CSS 的骨架屏结构 ✓ 写死在占位处 ✓ 不依赖任何 JS ✓
   用法：`<div class="k-sk-stack"><i></i><i></i><i></i></div>`
   ⚠️ 数据到了**必须整体重渲染** ✗（否则骨架屏会跟内容叠在一起 ✓ 需调用方保证 ✓）*/
.k-sk-stack {
  display: flex; flex-direction: column; gap: 10px;
  padding: 14px 12px;
}
.k-sk-stack > i {
  display: block; height: 12px; border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--line-soft) 25%, var(--surface-2) 37%, var(--line-soft) 63%);
  background-size: 400% 100%;
  animation: k-skel 1.2s ease-in-out infinite;
}
.k-sk-stack > i:nth-child(2n) { width: 82%; }
.k-sk-stack > i:nth-child(3n) { width: 64%; }
@media (prefers-reduced-motion: reduce) { .k-sk-stack > i { animation: none; } }

/* ============================================================ ㉑ 空态"带下一步动作"（2026-10-16）
   背景：用户口径「空态不能是死胡同」✗ → 给空态补按钮 ✓（组卷筛选空 / 班务 ×2 已落 ✓）
   ⚠️ 但**组卷「卷面」那块试了两版都不成立** ✗（详见 apps/组卷助手/public/js/app.js 里的注释 ✓）：
      ① margin → 边距塌陷 ✓ ② 撑高度居中 → 反而把「新建/－」挤近 ✓
      根因：卷面预览区上面浮着工具栏（新建/－/＋/100%）✗ 空态再塞按钮必贴 ✓
   → 结论：**护栏优先** ✗（手机端「按钮不贴死 ≥8px」不放松 ✓）这一处只留文案 ✓
   这里不写任何覆盖 ✗ —— 保持空态原样 ✓ 需要入口时应加在**工具栏**上 ✓

/* ============================================================ ⑮ 手机端：强化差异化（2026-10-16 用户要求）
   「手机端强化差异化设计，使改版效果明显」—— 不是换配色 ✗ 而是：
     · 分区标题**带品牌绿竖条**（一眼看出是新版 ✓ 与桌面端明显不同 ✓）
     · 卡片圆角统一 14px、内距收紧到 12px（原来 16~22px 圆角混用 + 14~18px 内距 ✗）
     · 分区间距 12px（桌面 16px ✓ 手机更紧凑 ✓ 一屏多一行内容 ✓）
     · 表格内距在手机端收成 8px（原来沿用桌面的 12px ✗ 一行占三行高 ✗）*/
@media (max-width: 640px) {
  .zone > .zone-h, .zone > summary, .card-h, .card-hd {
    position: relative;
    padding-left: 10px !important;
    border-bottom: 1px solid var(--line-soft) !important;
  }
  .zone > .zone-h::before, .zone > summary::before, .card-h::before {
    content: '';
    position: absolute; left: 0; top: 5px; bottom: 5px; width: 3px;
    border-radius: 2px; background: var(--green-800);
  }
  /* ⚠️ **不给 .card/.card2 加圆角** ✗ —— 第一版写了这条 ✓ 结果把"分区内已被 flat.css 去壳的
     .card"又变回一张卡片 ✗ 体检立刻报 `zone > card` 套嵌回归 ✓（本轮自己踩的 ✓）
     手机端的"改版感"靠**竖条 + 更紧的内距 + 更小的分区间距** ✓ 不靠圆角 ✓ */
  .sr-card, .pet-card, .qc-card { border-radius: 14px !important; }
  .zone-b, .card-b, .card-body, .card2 { padding-left: 12px !important; padding-right: 12px !important; }
  .tbl th, .tbl td, table.mt th, table.mt td, table.mt2 th, table.mt2 td {
    padding-top: 8px !important; padding-bottom: 8px !important;
  }
}

/* ============================================================ ⑰ 三态统一：加载 / 空 / 失败（2026-10-16）
   现状：`.state`（admin-kit）· `.empty2`（各应用）· `.sk`（首页骨架）三套各写各的 ✗
   统一成一套口径（用户口径"统一风格与颜色"✓）：
     · 图标 40px 圆形浅底（用品牌绿 ✗ 不用彩色 emoji ✓）
     · 标题 15px/600 · 说明 13px 次要色 · 一个主操作
     · 加载：**骨架屏**（比"正在加载…"更像内容马上到 ✓ 长页不再白屏 ✓）*/
.state, .empty2:not(td):not(th), .empty:not(td):not(th) {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); padding: var(--sp-6) var(--sp-4);
  color: var(--ink-3); text-align: center;
}
.state .st-ic, .empty2 .ic {
  width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--green-050); color: var(--green-800);
}
.state .st-t { font-size: var(--fs-lg); font-weight: var(--fw-semi); color: var(--ink); }
.state.empty .st-ic { background: var(--green-050); color: var(--green-800); }
.state.err .st-ic { background: var(--danger-bg); color: var(--danger); }
/* ⚠️ **不要碰 .sk** ✗ —— 首页骨架屏是 2026-10-01 做的，自带一套 shimmer（动画名 `sk` ✓）
   而 `e2e-home-visual.js` 有一条断言**钉着它**：必须真是 shimmer 动画 + 渐变底 ✗
   我第一版把 `.sk` 一起并进来（动画名写成 k-skel ✓）→ 断言立刻红 ✓（护栏抓得对 ✓）
   → 这里只管**新增的** `.k-skeleton` ✓ 老的原样不动 ✓ */
.k-skeleton {
  background: linear-gradient(90deg, var(--line-soft) 25%, var(--surface-2) 37%, var(--line-soft) 63%);
  background-size: 400% 100%;
  animation: k-skel 1.2s ease-in-out infinite;
  border-radius: var(--w-radius-cell);
}

/* ============================================================ ⑱ 动效微交互（2026-10-16）
   只做**状态反馈**（按下 / 悬停 / 进出）✓ —— 不做页面转场 ✗ 不做数字滚动 ✗
   三档节奏用现成令牌 ✓ 只动 transform / opacity / 颜色 ✗ 不动布局属性 ✓（动布局会掉帧 ✓）*/
body .btn:active, body .btn2:active, body .k-btn:active,
body .qc a:active, body .qc button:active {
  transform: translateY(1px) scale(.995);
  transition: transform var(--t-fast);
}
.tbl tbody tr, .data tbody tr, table.mt tbody tr {
  transition: background-color var(--t-fast);
}
.modal-box, .modal, .sheet, .drawer, .modal-mask, .drawer-mask {
  transition: opacity var(--t-mid), transform var(--t-mid);
}
/* 折叠区展开：箭头跟随（已有 ✓）＋ 内容淡入 ✓ */
details.zone[open] > .zone-b, details.card2[open] > div { animation: fadeInUp var(--t-mid) both; }
@keyframes fadeInUp { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* ============================================================ ⑲ 深色模式（2026-10-16）
   为什么现在做：令牌层已具备"变量 + 兜底值"机制 ✓（借 uView-Pro 的思路 ✓）→ 只差一套深色值 ✓
   怎么开：`<html data-theme="dark">`（或 JS 设 `document.documentElement.dataset.theme='dark'` ✓）
     · 默认**仍是浅色** ✗（老师日常在明亮教室用 ✓ 深色是给投影/夜间的选项 ✓）
     · 不动任何结构 ✓ 每一条都带兜底值 ✓（老浏览器不认 var() 也退回浅色 ✓）
   取值纪律（按技能里的颜色规范 ✓）：
     · 不用纯黑 #000 ✗ 用带品牌绿倾向的深灰绿（#10140f ✓）
     · 品牌绿在深色下**反向使用**：深阶（#175200 那种）作浅色文字会看不清 ✗
       → 深色下把 --green-800/900 换成**亮绿**（#8ee62e / #a6f05a ✓ 对深底 ≥7:1 ✓）
     · 对比度已用脚本核算（tools 口吻：亮/暗各跑一遍 ✓ 见 docs 记录 ✓）*/
:root[data-theme="dark"] {
  --bg: #10140f;
  --surface: #161c17;
  --surface-2: #1c231d;
  --ink: #e8efe9;
  --ink-2: #b8c4ba;
  --ink-3: #93a096;              /* 对 #161c17 ≈ 5.7:1 ✓ */
  --line: #2a332b;
  --line-soft: #232b24;
  --green-900: #c8f7a0;
  --green-800: #8ee62e;          /* 深色下的"主色"：亮绿 ✓ 承载文字 ✓ */
  --green-700: #a6f05a;
  --green-100: #22301c;
  --green-050: #1a2416;
  /* 2026-10-27 补：`--green-d`（tokens.css 里的"最深绿"#175200 ✓ 门户首页「第 X 节」小徽标、
     通知来源、喊话条外链都用它 ✓）一直没在深色层反向映射 ✗
     → 亮绿字落 #1a2416 底只有 1.72:1（原来是靠审计豁免 2 处把它盖住的 ✓ 豁免清零后才露出来）*/
  --green-d: #c8f7a0;
  --amber-800: #ffab45;
  --amber-100: #33260f;
  --amber-050: #241c0d;
  --danger: #ff8f86;
  --danger-bg: #3a1d1b;
  --warn: #ffc46b;
  --warn-bg: #332612;
  --info: #7ec0ff;
  --info-bg: #16283a;
  --w-fg-0: #fff; --w-fg-1: rgba(255,255,255,.66); --w-fg-2: rgba(255,255,255,.44);
  --w-line-strong: rgba(255,255,255,.12);
  --w-bg-card: #161c17; --w-bg-page: #10140f;
  --w-link: #8ab4f8;
  --sh-1: 0 1px 2px rgba(0,0,0,.4); --sh-2: 0 2px 8px rgba(0,0,0,.45); --sh-3: 0 8px 24px rgba(0,0,0,.5);
}
/* ★★ 2026-10-16 深色打磨 · 覆盖**各应用自己定义的浅色变量** ★★
   为什么这样修：审计（e2e-layout 新增的深色对比度一节）跑出来 61 页有违规 ✓
     典型：值日助手 30 处 / 材料汇总 13 处 / **课程安排 141~162 处** ✗
     症状都是"浅字落浅底 = 1.1:1 完全看不见" ✓ 或"深字落深底" ✓
   根因不是令牌 ✗ 是**各应用 app.css 里自己又定义了一套浅色变量** ✓ 且**写死了浅色** ✓
     实测扫出来的（值日/材料/活动/班务/课程/智能打印 各有一份 ✓ 名字还不统一 ✗）：
       --bg/--panel/--card/--surface  ← 浅底
       --text/--ink/--text2/--ink2    ← 深字
       --line/--line2/--line-strong/--brand-line ← 浅线
       --primary-soft/--ok-soft/--warn-soft/--danger-soft/--brand-soft/--green-light/--st-* ← 浅色浅底
   → 修法：**同名覆盖** ✓ 变量会继承 ✓ 一处定义、全站生效 ✓
     比"按类名一个个改 500 处硬编码"省一个数量级 ✓（那些硬编码先不动 ✗ 见审计剩余量 ✓）*/
:root[data-theme="dark"] {
  --bg: #10140f;
  --panel: #161c17;
  --card: #161c17;
  --surface: #161c17;
  --text: #e8efe9;
  --ink: #e8efe9;
  --text2: #b8c4ba;
  --ink2: #b8c4ba;
  --text3: #93a096;
  --ink3: #93a096;
  --muted: #93a096;
  --line: #2a332b;
  --line2: #232b24;
  --line-strong: #2a332b;
  --line-soft: #232b24;
  --brand-line: #2a332b;
  --primary-soft: #1a2416;
  --ok-soft: #1a2416;
  --ok-line: #22301c;
  --warn-soft: #332612;
  --warn-line: #332612;
  --danger-soft: #3a1d1b;
  --danger-line: #3a1d1b;
  --brand-soft: #1a2416;
  --brand-weak: #1a2416;
  --green-light: #1a2416;
  --st-draft: #1c231d;
  --st-entry: #33260f;
  --st-done: #1a2416;
  --st-pub: #1a2416;
  /* ⚠️ **琥珀这一档必须补上** ✗ —— 漏了它 → 材料汇总后台那个「已关闭」标签
     用的是 `--amber-900: #7a3d00`（浅色下的"最深琥珀" ✓）→ 深色背景下只有 **1.75:1** ✗ 看不见 ✓
     与 green 同一套纪律：**深阶在深色下反向提亮** ✓（#7a3d00 → #ffab45 ✓ 对深底 ≈8:1 ✓）*/
  --amber-900: #ffab45;
  --amber-800: #ffc46b;
  --amber-100: #33260f;
  --amber-050: #241c0d;
}
/* ★ 2026-10-16 门户自己的"写死浅底"（深色审计抓出来的 7 条 ✓ 全在门户 ✓）
   实测：门户首页 25 处 ✗（卡片用的是**半透明白** `rgba(255,255,255,…)` ✓
        在浅色下很好看 ✓ 深色下就变成"浅字落浅底 = 1.17:1" ✗ 完全看不见 ✓）
        组织架构页 4 处 ✗（「刷新」那几个 .btn 的底色没跟着令牌走 ✓）
   ⚠️ 这里**必须 !important** ✗ —— 门户 app.css 里的卡片底色写得比本层更靠后/更具体 ✓
     （本层是"最后一层"的设计承诺 ✓ 见文件头的纪律说明 ✓）*/
:root[data-theme="dark"] .cols .card,
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .duty-card,
:root[data-theme="dark"] .work-card,
:root[data-theme="dark"] .notif-card,
:root[data-theme="dark"] .shout-card,
:root[data-theme="dark"] .course-card,
:root[data-theme="dark"] .hello,
:root[data-theme="dark"] .top,
:root[data-theme="dark"] .side,
:root[data-theme="dark"] .zone { background: var(--surface) !important; }
:root[data-theme="dark"] .btn,
:root[data-theme="dark"] .btn2,
:root[data-theme="dark"] .icon-btn {
  background: var(--surface-2) !important;
  color: var(--ink) !important;
  border-color: var(--line) !important;
}
/* 深色下卡片标题也要翻色 ✗ —— 原来 h2 用的是浅色主题的深灰(#333) ✓
   在深色卡面(rgb(22,28,23))上对比度只有 1.37:1 ✓ 基本看不见 ✓
   （2026-10-18 布局体检报的"新增违规"里就有它 ✓ A/B 确认线上旧版也有 ✓ 顺手修 ✓）*/
:root[data-theme="dark"] .card-h h2,
:root[data-theme="dark"] .zone-h h2,
:root[data-theme="dark"] .card-h h3,
:root[data-theme="dark"] .card-hd h2 {
  color: var(--ink) !important;
}
/* 手机档的快捷入口（天气/通讯录/通知/下载 ✓ 见 index.html 的 .m-quick .q-item）
   ⚠️ mobile-redesign.css 里它的底色写死 #fff ✗ → 深色下变成"白底浅字" ✓
      对比度 1.17:1 ✗（布局体检偶尔抓到 ✓ 2026-10-18 修 ✓）
   为什么放这一层：本层是**最后一层**（覆盖优先级最高 ✓）且是深色的唯一权威 ✓ */
:root[data-theme="dark"] .m-quick .q-item {
  background: var(--surface) !important;
  border-color: var(--line) !important;
  color: var(--ink) !important;
}
:root[data-theme="dark"] .m-quick .q-item .qn { color: var(--ink-2) !important; }
:root[data-theme="dark"] .m-quick .q-item:active { background: var(--surface-2) !important; }
:root[data-theme="dark"] .modal-box,
:root[data-theme="dark"] .modal,
:root[data-theme="dark"] .drawer,
:root[data-theme="dark"] .sheet,
:root[data-theme="dark"] .acl-box { background: var(--surface) !important; }
:root[data-theme="dark"] .toast,
:root[data-theme="dark"] .ui-toast,
:root[data-theme="dark"] .ui-mask { background: rgba(255, 255, 255, .16) !important; }
/* ② 白色**容器**（不是按钮本身 ✓ 实测审计里"刷新"那几个的 bg 其实来自祖先 ✓）
      → 让它们透明 ✓ 由页面底色兜底 ✓（深色下不需要"白纸" ✓）*/
:root[data-theme="dark"] .pg-head,
:root[data-theme="dark"] .pg-act,
:root[data-theme="dark"] .zone-h,
:root[data-theme="dark"] .toolbar,
:root[data-theme="dark"] .card-h,
:root[data-theme="dark"] .tbl-wrap,
:root[data-theme="dark"] .tblwrap,
:root[data-theme="dark"] .table-wrap,
:root[data-theme="dark"] .qc,
:root[data-theme="dark"] .stat-row,
:root[data-theme="dark"] .notice,
:root[data-theme="dark"] .side-brand,
:root[data-theme="dark"] .side-foot { background: transparent !important; }
/* ③ 浅色"药丸"（下载中心那个 `9 个应用` 是 rgba(244,247,245,.94) ✓ 半透明浅底 ✗）*/
/* ⚠️ 这里**不能加 .tag/.badge** ✗ —— 会把 `.tag.g/.tag.err` 这些**语义色标签**一并压成灰 ✓
   （语义色在深色下的配对已由 `--st-*` 变量覆盖 ✓ 见上面的变量表 ✓）*/
:root[data-theme="dark"] .dl-count { background: var(--surface-2) !important; color: var(--ink-2) !important; }
/* ④ **内联写死的状态色**（admin.js 里 `style="color:#1f6d24"` 那种 ✓ 绿/琥珀在深底上只有 2.7:1 ✗）
      用属性子串匹配 ✓ 精确点亮这几个内联色 ✗ 不碰别的语义色 ✓ */
:root[data-theme="dark"] .tbl span[style*="#1f6d24"],
:root[data-theme="dark"] .tbl span[style*="rgb(31, 109, 36)"] { color: #8ee62e !important; }
:root[data-theme="dark"] .tbl span[style*="#92610a"],
:root[data-theme="dark"] .tbl span[style*="rgb(146, 97, 10)"] { color: #ffc46b !important; }
/* ⑤ 警告类标签在深色下要提亮（材料汇总后台那个「已关闭」1.75:1 ✗）
   ⚠️ 前面加 `body` ✗ —— 实测 app.css/admin-kit 里几条规则权重比本层高一级
      （形如 `body .pg-head .btn{background:#fff!important}` ✓）→ 同权重我赢不了 ✓ → 抬一级 ✓ */
:root[data-theme="dark"] body .tag.warn,
:root[data-theme="dark"] body .tag.amber,
:root[data-theme="dark"] body .tag.off { background: var(--warn-bg) !important; color: var(--warn) !important; }
:root[data-theme="dark"] body .ac-sub,
:root[data-theme="dark"] body .dim.amber { color: var(--warn) !important; }
:root[data-theme="dark"] body .btn,
:root[data-theme="dark"] body .btn2,
:root[data-theme="dark"] body .btn-sm,
:root[data-theme="dark"] body .icon-btn {
  background: var(--surface-2) !important;
  color: var(--ink) !important;
  border-color: var(--line) !important;
}
/* **幽灵按钮**：`.btn-ghost{background:var(--surface)}`（app.css/admin.css 各一处 ✓）
   ⚠️ 2026-10-27 更正旧结论：之前记成"还有一条更高优先级的白底在压"——**不存在** ✗
     实测（CDP 探针）：这 4 个按钮的 `getAnimations()` 里有一条 CSSTransition(background-color) 一直 running，
     CDP 拉起的窗口常不可见/被遮挡 → 过渡时钟不推进 → 切到深色后 computed 永远停在过渡起点（白）✓
     铁证：对它 inline 设 `backgroundColor=#00f` 再读数，仍是 rgb(255,255,255) ✓（渲染管线都没跑）
     → 结论：**这是探针的测量假象，不是页面缺陷** ✓ 量深色前必须先 `*{transition:none!important}`（e2e-layout 已照改）
   本层这条规则照留：深色下幽灵按钮就该用 `--surface-2` ✓*/
:root[data-theme="dark"] body .btn-ghost,
:root[data-theme="dark"] body .btn2.ghost {
  background: var(--surface-2) !important;
  color: var(--ink-2) !important;
  border-color: var(--line) !important;
}
/* ⑦ 2026-10-27 后台**写死白底**三处（关掉过渡后实测，深色下是真读不清 ✓）
   ① 概览/教师档案 KPI 四宫格：`admin.css .pane .stats.stat-row > .stat{background:#fff}`
      → 数字 --ink(#e8efe9) 1.17:1 ✗、小标签 --ink-3 2.72:1 ✗
   ② 侧栏身份卡 `.side-user{background:#fff!important}` → 姓名 #c8f7a0 1.22:1 ✗
   ③ 组织架构/教师档案表格的姓名链接 `.usr-link{color:var(--primary)}` → 深底 2.64:1 ✗
   ①② 只要底色跟着令牌走就行 ✓（数字/小标签的色值本来就是令牌 ✓ 不必逐条改色 ✓）
   ③ 的 --primary 没在深色层反向映射 ✓ 单点用 --green-800（深色下 = #8ee62e ✓ 12.7:1 ✓）*/
:root[data-theme="dark"] .pane .stats.stat-row > .stat,
:root[data-theme="dark"] .side-user {
  background: var(--surface) !important;
  border-color: var(--line) !important;
  color: var(--ink) !important;
  box-shadow: var(--sh-1) !important;
}
:root[data-theme="dark"] .usr-link { color: var(--green-800) !important; }
:root[data-theme="dark"] .usr-link:hover { border-bottom-color: var(--green-800); }
/* ⑥ 表单/表格里"写死的浅底"再兜一层（提权版 ✓）*/
:root[data-theme="dark"] body input,
:root[data-theme="dark"] body select,
:root[data-theme="dark"] body textarea { background: var(--surface-2) !important; color: var(--ink) !important; }

/* 深色下要改"写死的浅色"（本层能管的范围 ✓ 应用里还有历史硬编码 ✗ 列后续）*/
:root[data-theme="dark"] body { background: var(--bg); color: var(--ink); }
:root[data-theme="dark"] .tbl th, :root[data-theme="dark"] table.mt th { background: var(--surface-2) !important; }
:root[data-theme="dark"] input, :root[data-theme="dark"] select, :root[data-theme="dark"] textarea {
  background: var(--surface-2); color: var(--ink); border-color: var(--line);
}
:root[data-theme="dark"] .tag.g, :root[data-theme="dark"] .tag.ok { background: var(--green-100); color: var(--green-800); }
:root[data-theme="dark"] .ui-toast, :root[data-theme="dark"] .toast { background: rgba(255,255,255,.16); }

/* ============================================================ ㉒ 打印：只做"去屏幕外壳"（2026-10-17 P3）
   背景：外观层以前**一条打印规则都没有** ✗ → 我加的圆角/阴影/分区绿竖条会原样打进纸里 ✓
        （纸上看不见阴影 ✓ 但**边框与色块是费墨的** ✗ 而且卡片圆角在纸上是多余的 ✓）
   纪律（很重要 ✗ 别越界）：
     ⚠️ **不碰各自的 @page** ✗ —— 各文档的纸型/页边距是**有意不同**的 ✓：
        奖状 0 边距（满版 ✓）｜ 试卷 48mm 宽边 ✓ ｜ 座位表/值日表 A4 横 8mm ✓ ｜ 成绩单 12mm 10mm ✓
        统一它们 = 把奖状打出白边、把试卷打歪 ✗（这条我一开始想错了 ✗ 查完才发现）
     ⚠️ 这里只做三件**纯减法**：去阴影 ✓ 去屏幕外壳内距 ✓ 去装饰性色块 ✓
     ⚠️ 不改任何**字号/表格内距** ✗ —— 各应用已有自己的打印字号（11px / 11pt …✓）改了就动版式 ✗ */
@media print {
  /* ① 背景色要能打出来（值日表的颜色格 / 座位表的分组色 ✓）
        各应用本来就各自写着 print-color-adjust ✓（成绩 / 智能打印 / 活动 ✓）
        这里补一条**兜底** ✓ 不再需要每页手写选择器 ✓（该属性是继承的 ✓）*/
  html {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  /* ② 屏幕外壳（页面留白 / 最大宽）在纸上是白边 ✗ 归零 ✓ */
  main, .main, .sp-main, .wrap {
    padding: 0 !important;
    margin: 0 !important;
    max-width: none !important;
  }
  /* ③ 装饰性色块与阴影不进纸 ✓（省墨 ✓ 也不干扰读表 ✓）*/
  .zone, .card, .card2, .k-card, .sr-card, .agg-card, .chart-card,
  .modal-box, .sheet, .drawer, .toast, .ui-toast {
    box-shadow: none !important;
  }
  /* 分区标题前的那根品牌绿竖条是**屏幕上的导航感** ✓ 纸上没意义 ✗ */
  .zone > .zone-h::before, .zone > summary::before, .card-h::before,
  .card-hd::before {
    display: none !important;
  }
  /* 交互态不该出现在纸上 ✓ */
  .btn, .btn2, .k-btn, .icon-btn, .tag, .badge, .chip {
    transition: none !important;
    box-shadow: none !important;
  }
}

/* ============================================================ ⑬ 无障碍与减少动效（对齐 WeUI 的 a11y 模块 ✓）
   焦点环：保留现有品牌色环（tokens.css 的 --focus-ring ✓ 已核算 ✓） */
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .k-skeleton, .k-loading, .loading, .spin, .sk { animation: none; }
  /* ⑱ 新加的微交互也要一起降级 ✗（不然"减少动效"只对老的那几条生效 ✓）*/
  details.zone[open] > .zone-b, details.card2[open] > div { animation: none; }
  body .btn:active, body .btn2:active, body .k-btn:active { transform: none; }
  .modal-box, .modal, .sheet, .drawer, .modal-mask, .drawer-mask { transition: none; }
}


/* ---- mobile-redesign.css ---- */
/* ============================================================
   手机端首页重排（2026-10-18 新方案；2026-10-23 恢复值日 / 课程表）
   仅在 ≤620px 生效，桌面端不受影响。
   顺序：校徽header → 四宫格（喊话/通讯录/通知/下载）→ 课程表 → 值日安排 → 每周工作内容 → 底部Tab
   手机端只用底部导航的 5 个子应用。

   2026-11-07（M-04/M-06）：选择器统一加 `body.portal` 前缀，用特异度取代
   原先的 24 个 !important；仅 #qShout 的 min-height 因基础层
   `[role=button]{min-height:40px !important}` 无法用特异度覆盖，保留 !important。
   回退：删掉 index.html 里这一行 <link> 即可
   ============================================================ */

@media (max-width: 640px) {

  /* ---- 1. 隐藏侧栏和旧顶栏 ---- */
  body.portal .side { display: none; }
  body.portal .top { height: 44px; padding: 0 12px; }
  /* 搜索下线后顶栏不再拥挤：手机端保留校名 logo，避免顶部空白 */
  body.portal #topLogo, body.portal .top .top-brand { display: block; }
  body.portal .top .top-brand { height: 22px; }
  body.portal .top .brand { display: none; }

  /* ---- 2. 手机端无绿条，顶栏纯透明 ---- */
  body.portal .m-brand, body.portal .m-brand-img, body.portal .m-brand-float { display: none; }

  /* ---- 3. hello 问候简化 ---- */
  body.portal .hello { padding: 8px 4px; }
  body.portal .hello h1 { font-size: 16px; }
  body.portal .hello p { font-size: 12px; }
  body.portal .hello-side { display: none; }
  /* 2026-11-06：320px 小屏实测 —— 问候语那行是 nowrap，把 flex 项的 min-content 顶到 322.7px，
     整页横向溢出 3px（现在又锁了 1:1 不缩放，溢出不会再被浏览器缩回去）✗
     → 允许 flex 项收缩到屏宽，超出的文字按原来的省略号截断 ✓ */
  body.portal .hello, body.portal .hello > * { min-width: 0; }
  body.portal #btnHelloBell { display: none; }

  /* ---- 4. 隐藏远程喊话卡（手机端不要） ---- */
  body.portal .shout-card { display: none; }

  /* ---- 5. 单列堆叠，顺序重排 ---- */
  body.portal .cols { display: flex; flex-direction: row; flex-wrap: wrap; gap: 6px; }
  body.portal .col-main, body.portal .col-side { width: 100%; display: contents; }

  /* 用 order 控制顺序 */
  body.portal .weather-card { display: none; }
  body.portal .hello { order: 0; flex: 1 1 100%; }
  body.portal .m-toprow { order: 1; flex: 1 1 100%; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; padding-inline: 12px; box-sizing: border-box; }
  body.portal .top-row { order: 2; flex: 1 1 100%; display: block; }
  body.portal .course-card { order: 2; }
  body.portal .duty-card { order: 3; flex: 1 1 100%; }
  body.portal .work-card { order: 4; }
  body.portal .notif-card { display: none; }

  /* 第一行四卡样式
     ⚠️ #qShout 带 role="button"，会吃到全局 [role=button]{min-height:40px !important} ✗
     结果喊话卡比其他三张矮一截 —— 这里保留 !important 统一成同一高度 ✓ */
  body.portal .m-toprow .q-item {
    background: var(--surface); border: 1px solid var(--line-soft); border-radius: 14px;
    padding: 10px 4px; text-align: center; cursor: pointer; color: var(--green-800);
    min-height: 82px !important;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
  }
  body.portal .m-toprow .q-item .qi { width: 22px; height: 22px; margin: 0 auto 4px; }
  body.portal .m-toprow .q-item .qn { font-size: 11px; color: var(--ink-2); }
  body.portal .m-toprow .q-item:active { background: var(--green-050); }

  /* 通知未读数 */
  body.portal .m-toprow .q-item { position: relative; }
  body.portal .m-badge {
    position: absolute; top: 4px; right: 4px;
    background: var(--danger); color: #fff; font-size: 9px;
    border-radius: 10px; padding: 0 4px; line-height: 14px; min-width: 14px;
    text-align: center;
  }

  /* ---- 6. 旧天气卡已隐藏，用m-toprow里的新天气卡代替 ---- */

  /* ---- 7. 三快捷入口卡（旧m-quick已改为m-toprow） ---- */

  /* ---- 8. 卡片统一：白底细边、小圆角、无阴影、去彩色顶条 ---- */
  body.portal .card { border-radius: 14px; box-shadow: none; border: 1px solid var(--line-soft); background: var(--surface); }
  body.portal .card::before, body.portal .card::after { display: none; }
  body.portal .cols .card .card-h h2::before { display: none; }
  body.portal .cols .card .card-h::before { display: none; }
  body.portal .card-h { background: transparent; border-bottom: 1px solid var(--line-soft); }
  body.portal .acc-green, body.portal .acc-amber { background: var(--surface); }
  body.portal .card-h h2 { color: var(--ink); }

  /* ---- 9. 底部 6Tab ---- */
  body.portal .m-tabbar {
    display: flex;
    position: fixed; bottom: 0; left: 0; right: 0;
    background: var(--surface); border-top: 1px solid var(--line-soft);
    padding: 6px 0 calc(6px + env(safe-area-inset-bottom));
    z-index: 100;
  }
  body.portal .m-tabbar .mt {
    flex: 1; text-align: center; font-size: 10px; color: var(--ink-3);
    text-decoration: none; padding: 4px 0; display: flex; flex-direction: column; align-items: center;
  }
  body.portal .m-tabbar .mt .mi { width: 20px; height: 20px; display: block; margin-bottom: 2px; }
  body.portal .m-tabbar .mt.active { color: var(--shell-accent, var(--green-900)); font-weight: 600; }

  /* 主区底部留白，避免内容被Tab挡住 */
  body.portal .wrap { padding-bottom: 80px; }

  /* ---- 10. 通讯录/下载入口：点三卡时滚动/跳转 ---- */
  /* 通讯录视图在手机端也隐藏侧栏，直接打开 */
}

/* 桌面端隐藏手机专用DOM */
.m-brand, .m-toprow, .m-tabbar { display: none; }


/* ---- unify.css ---- */
/* ============================================================================
   DC-UI 1.0 · 全站统一视觉层（2026-10-23）
   ----------------------------------------------------------------------------
   设计来源：课堂小测页「深绿舞台 + 青柠高光」。
   两套表面，一套语言：
     · 浅色工作面 = 门户 + 所有子应用 —— **本文件负责**
     · 深色舞台   = 教室大屏 / 课堂小测 —— 各页面自己保留，本文件不覆盖
   共同点：20px 卡片、12px 控件圆角、青柠渐变主操作、薄荷绿文字强调、
           极淡的网格与两团光晕、克制的投影。

   加载位置：每个页面**最后一张**样式表（在自有 CSS / weui-look.css 之后）。
   回退：删掉页面里的 unify.css 一行即可；tokens.css 里新增的 --dc-* 不影响旧样式。

   三条纪律：
     ① 只改观感，不动栅格 / 定位 / 间距节奏；
     ② 所有浅色规则都限定在 html:not([data-theme="dark"]) 内，深色模式零改动；
     ③ 应用卡片用 :where() 把条件权重归零 —— 保证 weui-look 的「卡中卡去壳」
        仍然赢；门户首页卡片反过来用高权重，压过 app.css 2026-10-18 的强制白卡。
   ========================================================================== */

/* ---------- 0. 令牌兜底（子应用不一定加载 tokens.css）---------- */
:root {
  --dc-bg: #f4f8ee;
  --dc-bg-2: #eef5e3;
  --dc-surface: #ffffff;
  --dc-surface-2: #fbfdf7;
  --dc-line: #e2ead9;
  --dc-line-strong: #cbdcc0;
  --dc-line-ctl: #cfd9cf;
  --dc-ink: #16241a;
  --dc-ink-2: #4b5c4e;
  --dc-ink-3: #66735d;
  --dc-lime: #b4eb78;
  --dc-lime-2: #98df5e;
  --dc-lime-soft: #eaf9dc;
  --dc-lime-faint: #f5fce9;
  --dc-mint: #1d7a57;
  --dc-mint-bright: #63d5a7;
  --dc-mint-soft: #e4f7ee;
  --dc-deep: #2a6b04;
  --dc-deep-2: #175200;
  --dc-on-lime: #14210f;
  --dc-grad: linear-gradient(180deg, #c6f28c 0%, #98df5e 100%);
  --dc-grad-soft: linear-gradient(135deg, #fbfdf6 0%, #f2f9e8 55%, #edf8e1 100%);
  --dc-danger: #b3261e;
  --dc-danger-bg: #fdeceb;
  --dc-r-card: 14px;
  --dc-r-card-sm: 14px;
  --dc-r-ctl: 12px;
  --dc-shadow-card: 0 1px 2px rgba(22, 36, 26, .04), 0 12px 30px rgba(42, 78, 32, .07);
  --dc-shadow-hover: 0 2px 4px rgba(22, 36, 26, .05), 0 20px 48px rgba(42, 78, 32, .14);
  --dc-shadow-pop: 0 24px 70px rgba(15, 35, 20, .18);
  --dc-glow: 0 10px 24px rgba(152, 223, 94, .28);
  --dc-focus: 0 0 0 3px rgba(152, 223, 94, .32);
  --dc-t: .16s cubic-bezier(.2, .8, .2, 1);
}

/* ---------- 1. 底色：浅绿工作台 + 两团光晕 + 极淡网格 ---------- */
html:not([data-theme="dark"]) body {
  background-color: var(--dc-bg) !important;
  background-image:
    radial-gradient(1100px 620px at 84% -8%, rgba(99, 213, 167, .16), transparent 62%),
    radial-gradient(900px 560px at -8% 106%, rgba(180, 235, 120, .14), transparent 58%),
    linear-gradient(rgba(42, 107, 4, .032) 1px, transparent 1px),
    linear-gradient(90deg, rgba(42, 107, 4, .032) 1px, transparent 1px) !important;
  background-size: auto, auto, 64px 64px, 64px 64px !important;
  background-attachment: fixed !important;
}
/* 内嵌在门户 iframe 里的页面：自己不画底，交给门户 */
html:not([data-theme="dark"]) body.in-frame {
  background: transparent !important;
  background-image: none !important;
}

html:not([data-theme="dark"]) ::selection {
  background: rgba(180, 235, 120, .38);
  color: var(--dc-ink);
}

/* ---------- 2. 顶栏：白色玻璃 + 一条细线 ---------- */
:where(html:not([data-theme="dark"])) .top,
:where(html:not([data-theme="dark"])) .navbar,
:where(html:not([data-theme="dark"])) .k-head {
  background: rgba(255, 255, 255, .88) !important;
  border-bottom: 1px solid var(--dc-line) !important;
  box-shadow: 0 1px 0 rgba(22, 36, 26, .02) !important;
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  backdrop-filter: saturate(1.4) blur(14px);
}

/* ---------- 3. 卡片：20px 大圆角 + 柔和投影 ----------
   ⚠️ 这里**必须**用 :where() 把条件选择器的权重归零：
     否则 html:not(...) .card2 的权重会高过 weui-look 的 `.card2 .card2`，
     把「卡中卡去壳」顶掉，页面立刻回到一层套一层。 */
:where(html:not([data-theme="dark"])) .card,
:where(html:not([data-theme="dark"])) .card2,
:where(html:not([data-theme="dark"])) .zone,
:where(html:not([data-theme="dark"])) .panel,
:where(html:not([data-theme="dark"])) .t-card,
:where(html:not([data-theme="dark"])) .info-card,
:where(html:not([data-theme="dark"])) .stat-card,
:where(html:not([data-theme="dark"])) .data-card,
:where(html:not([data-theme="dark"])) .home-card,
:where(html:not([data-theme="dark"])) .card-reg {
  border-radius: var(--dc-r-card) !important;
  box-shadow: var(--dc-shadow-card) !important;
}

/* 门户首页：压过 app.css 的 14px 白卡；并保留原有的悬停上浮 */
html:not([data-theme="dark"]) body.portal .cols .card,
html:not([data-theme="dark"]) body.portal .contacts-view {
  border-radius: var(--dc-r-card) !important;
  border-color: var(--dc-line) !important;
  box-shadow: var(--dc-shadow-card) !important;
}
html:not([data-theme="dark"]) body.portal .cols .card:hover {
  border-color: var(--dc-line-strong) !important;
  box-shadow: var(--dc-shadow-hover) !important;
}

/* 班务助手首页的大入口卡 */
:where(html:not([data-theme="dark"])) .home-card {
  border-color: var(--dc-line) !important;
  transition: transform var(--dc-t), box-shadow var(--dc-t), border-color var(--dc-t);
}
:where(html:not([data-theme="dark"])) .home-card:hover {
  transform: translateY(-2px);
  border-color: var(--dc-line-strong) !important;
  box-shadow: var(--dc-shadow-hover) !important;
}

/* ---------- 4. 按钮 ----------
   主操作 = 青柠渐变 + 深色字（与课堂小测页同一枚按钮）。
   ⚠️ 需要 high specificity + !important：weui-look 有一层
      `body .btn2.primary { background:#fff !important }` 的等权重白按钮规则。 */
/* ⚠️ body:not(.ev-app):not(.ev-judge)：活动助手的用户口径是「所有按钮同权重、
   不突出任何一个」（2026-10-11，老师端与裁判手机页）——该应用整体退出主按钮规则 ✓ */
html:not([data-theme="dark"]) body:not(.ev-app):not(.ev-judge) :is(
  .btn.primary, .btn2.primary, .btn-primary, .btn.pri,
  .j-btn.pri, .jc-b.pri, .big-save, .btn-reg
) {
  background: var(--dc-grad) !important;
  background-image: var(--dc-grad) !important;
  color: var(--dc-on-lime) !important;
  border: 1px solid rgba(20, 33, 15, .08) !important;
  border-radius: var(--dc-r-ctl) !important;
  box-shadow: var(--dc-glow) !important;
  font-weight: 700;
  transition: transform var(--dc-t), box-shadow var(--dc-t), filter var(--dc-t);
}
html:not([data-theme="dark"]) body:not(.ev-app):not(.ev-judge) :is(
  .btn.primary, .btn2.primary, .btn-primary, .btn.pri,
  .j-btn.pri, .jc-b.pri, .big-save, .btn-reg
):hover {
  filter: brightness(1.035);
  transform: translateY(-1px);
  box-shadow: 0 14px 30px rgba(152, 223, 94, .34) !important;
}
html:not([data-theme="dark"]) body:not(.ev-app):not(.ev-judge) :is(
  .btn.primary, .btn2.primary, .btn-primary, .btn.pri
):active {
  transform: translateY(0);
  box-shadow: var(--dc-glow) !important;
}
html:not([data-theme="dark"]) body:not(.ev-app):not(.ev-judge) :is(
  .btn.primary, .btn2.primary, .btn-primary, .btn.pri
):disabled {
  opacity: .55;
  filter: none;
  box-shadow: none !important;
}

/* 次按钮：白底细边，悬停只提亮，不抢主操作 */
html:not([data-theme="dark"]) body .btn,
html:not([data-theme="dark"]) body .btn2,
html:not([data-theme="dark"]) body .k-btn {
  border-radius: var(--dc-r-ctl) !important;
  transition: background-color var(--dc-t), border-color var(--dc-t), color var(--dc-t);
}
html:not([data-theme="dark"]) body .btn:hover,
html:not([data-theme="dark"]) body .btn2:hover,
html:not([data-theme="dark"]) body .k-btn:hover {
  border-color: var(--dc-line-strong) !important;
  background: var(--dc-lime-faint) !important;
}

/* 危险操作：浅红底 + 深红字（比整块实心红更克制） */
html:not([data-theme="dark"]) body .btn.danger,
html:not([data-theme="dark"]) body .btn2.danger,
html:not([data-theme="dark"]) body .btn-danger {
  background: var(--dc-danger-bg) !important;
  border-color: rgba(179, 38, 30, .28) !important;
  color: var(--dc-danger) !important;
}
html:not([data-theme="dark"]) body .btn.danger:hover,
html:not([data-theme="dark"]) body .btn2.danger:hover,
html:not([data-theme="dark"]) body .btn-danger:hover {
  background: #fbdcd9 !important;
  border-color: rgba(179, 38, 30, .4) !important;
}

/* 键盘焦点：统一青柠光环 */
html:not([data-theme="dark"]) body .btn:focus-visible,
html:not([data-theme="dark"]) body .btn2:focus-visible,
html:not([data-theme="dark"]) body .k-btn:focus-visible,
html:not([data-theme="dark"]) body .j-btn:focus-visible {
  outline: none;
  box-shadow: var(--dc-focus) !important;
}

/* ---------- 5. 表单控件：12px 圆角 + 青柠焦点 ---------- */
html:not([data-theme="dark"]) input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
html:not([data-theme="dark"]) select,
html:not([data-theme="dark"]) textarea {
  border: 1px solid var(--dc-line-ctl) !important;
  border-radius: var(--dc-r-ctl) !important;
  background-color: var(--dc-surface);
  transition: border-color var(--dc-t), box-shadow var(--dc-t), background-color var(--dc-t);
}
html:not([data-theme="dark"]) input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):focus,
html:not([data-theme="dark"]) select:focus,
html:not([data-theme="dark"]) textarea:focus {
  outline: none;
  border-color: var(--dc-deep) !important;
  box-shadow: var(--dc-focus) !important;
}
html:not([data-theme="dark"]) input::placeholder,
html:not([data-theme="dark"]) textarea::placeholder {
  color: var(--dc-ink-3);
}

/* ---------- 6. 页签 / 分段控件 / 胶囊 ---------- */
html:not([data-theme="dark"]) body .tabs button.on,
html:not([data-theme="dark"]) body .tabs .on {
  color: var(--dc-deep-2) !important;
  border-color: var(--dc-deep) !important;
}
html:not([data-theme="dark"]) body .app-tabs .app-tab.on {
  background: var(--dc-grad) !important;
  border-color: transparent !important;
  color: var(--dc-on-lime) !important;
  box-shadow: var(--dc-glow);
}
html:not([data-theme="dark"]) body .app-tabs .app-tab:hover {
  border-color: var(--dc-line-strong) !important;
  color: var(--dc-deep-2) !important;
}
html:not([data-theme="dark"]) body .seg {
  background: var(--dc-surface) !important;
  border: 1px solid var(--dc-line) !important;
  border-radius: var(--dc-r-ctl) !important;
}
html:not([data-theme="dark"]) body .seg .seg-i.on {
  background: var(--dc-lime-soft) !important;
  color: var(--dc-deep-2) !important;
  box-shadow: none !important;
}
html:not([data-theme="dark"]) body .tag,
html:not([data-theme="dark"]) body .chip,
html:not([data-theme="dark"]) body .badge {
  border-radius: 999px;
}

/* ---------- 7. 门户侧栏 / 身份卡 / 顶栏 ---------- */
html:not([data-theme="dark"]) body.portal .side {
  background: rgba(255, 255, 255, .92) !important;
  border-right: 1px solid var(--dc-line) !important;
}
html:not([data-theme="dark"]) body.portal .user-chip {
  background: linear-gradient(135deg, var(--dc-lime-faint), #fff) !important;
  border-color: var(--dc-line) !important;
  border-radius: var(--dc-r-card-sm) !important;
}
html:not([data-theme="dark"]) body.portal .user-chip:hover {
  background: var(--dc-lime-soft) !important;
  border-color: var(--dc-line-strong) !important;
}
html:not([data-theme="dark"]) body.portal .user-chip .av {
  background: var(--dc-grad) !important;
  color: var(--dc-on-lime) !important;
  box-shadow: var(--dc-glow);
}
html:not([data-theme="dark"]) body .nav-item {
  border-radius: var(--dc-r-ctl);
  color: var(--dc-ink-2);
}
html:not([data-theme="dark"]) body .nav-item:hover {
  background: var(--dc-lime-faint) !important;
  color: var(--dc-deep-2) !important;
}
html:not([data-theme="dark"]) body .nav-item.on {
  background: var(--dc-grad) !important;
  color: var(--dc-on-lime) !important;
  font-weight: 700;
  box-shadow: var(--dc-glow) !important;
}
html:not([data-theme="dark"]) body .nav-item.on .badge {
  background: rgba(255, 255, 255, .62) !important;
  color: var(--dc-deep-2) !important;
}
/* 门户后台（admin.html）侧栏用的是 .nav-app，不是首页的 .nav-item */
html:not([data-theme="dark"]) body .nav-app.on {
  background: var(--dc-grad) !important;
  color: var(--dc-on-lime) !important;
  box-shadow: var(--dc-glow) !important;
}
html:not([data-theme="dark"]) body .nav-app.on .grip,
html:not([data-theme="dark"]) body .nav-app.on .more {
  color: rgba(20, 33, 15, .72) !important;
}
html:not([data-theme="dark"]) body.portal .top {
  background: rgba(255, 255, 255, .84) !important;
  border-bottom: 1px solid var(--dc-line) !important;
}

/* ---------- 8. 门户首页：欢迎条 / 快捷卡 / 通讯录 ---------- */
/* 欢迎条：平铺在页面底色上，不再套卡片（2026-10-23 用户口径：
   「这里不用卡片套着，平铺」）。只保留文字与节奏，不画底、不加边框/投影。 */
html:not([data-theme="dark"]) body.portal .cols > .hello {
  padding: 4px 0 6px !important;
  margin-bottom: 14px !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  overflow: visible !important;
}
html:not([data-theme="dark"]) body.portal .cols > .hello::after {
  content: none !important;
  display: none !important;
}
html:not([data-theme="dark"]) body.portal .hello h1 {
  color: var(--dc-deep-2);
  font-weight: 800;
}
html:not([data-theme="dark"]) body.portal .hello #helloName {
  background: linear-gradient(100deg, var(--dc-deep-2), var(--dc-mint));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
html:not([data-theme="dark"]) body.portal .hello p {
  color: var(--dc-ink-2);
}

/* 四张快捷卡：图标放进青柠浅底的小方块里 */
html:not([data-theme="dark"]) body.portal .m-toprow .q-item {
  background: var(--dc-surface) !important;
  border: 1px solid var(--dc-line) !important;
  border-radius: 16px !important;
  box-shadow: var(--dc-shadow-card) !important;
  min-height: 104px;
  transition: transform var(--dc-t), box-shadow var(--dc-t), border-color var(--dc-t);
}
html:not([data-theme="dark"]) body.portal .m-toprow .q-item:hover {
  transform: translateY(-2px);
  border-color: var(--dc-line-strong) !important;
  box-shadow: var(--dc-shadow-hover) !important;
}
html:not([data-theme="dark"]) body.portal .m-toprow .q-item .qi {
  width: 40px !important;
  height: 40px !important;
  padding: 9px;
  box-sizing: border-box;
  border-radius: var(--dc-r-ctl);
  background: var(--dc-lime-soft);
  color: var(--dc-deep) !important;
}
html:not([data-theme="dark"]) body.portal .m-toprow .q-item .qn {
  font-size: 14px !important;
  font-weight: 600 !important;
  color: var(--dc-ink) !important;
}

html:not([data-theme="dark"]) body.portal .contacts-view {
  border-color: var(--dc-line) !important;
}
html:not([data-theme="dark"]) body.portal .cv-search {
  background: var(--dc-surface) !important;
  border-color: var(--dc-line-ctl) !important;
  border-radius: 999px !important;
}
html:not([data-theme="dark"]) body.portal .cv-search:focus-within {
  border-color: var(--dc-deep) !important;
  box-shadow: var(--dc-focus);
}

/* 下载中心的分区页签（.dl-tab）与后台导航同一套选中态 */
html:not([data-theme="dark"]) body .dl-tab.on {
  background: var(--dc-grad) !important;
  border-color: transparent !important;
  color: var(--dc-on-lime) !important;
  box-shadow: var(--dc-glow) !important;
}
html:not([data-theme="dark"]) body .dl-tab.on .n {
  background: rgba(255, 255, 255, .62) !important;
  color: var(--dc-deep-2) !important;
}

/* 班务助手首页主入口：深绿卡上的图标 / CTA 提亮（原图标是深绿，对比度不足） */
html:not([data-theme="dark"]) body .home-card-main .em {
  color: var(--dc-lime) !important;
}
html:not([data-theme="dark"]) body .home-card-main .go {
  background: var(--dc-grad) !important;
  color: var(--dc-on-lime) !important;
  border-radius: 999px !important;
  font-weight: 700;
  box-shadow: var(--dc-glow);
}

/* ---------- 9. 登录入口：纯底色 + 单卡居中（2026-11-07 去掉网格/光晕/顶部渐变条） ---------- */
html:not([data-theme="dark"]) .gate {
  background-color: var(--dc-bg) !important;
  background-image: none !important;
  animation: none !important;
}
html:not([data-theme="dark"]) .gate .box {
  position: relative;
  overflow: hidden;
  width: min(380px, 100%);
  padding: 40px 34px 34px;
  border: 1px solid var(--dc-line);
  border-radius: 24px;
  box-shadow: var(--dc-shadow-pop);
}
html:not([data-theme="dark"]) .gate-form input {
  padding: 14px 15px;
  border: 1px solid var(--dc-line-ctl);
  border-radius: var(--dc-r-ctl);
  background: var(--dc-surface-2);
}
html:not([data-theme="dark"]) .gate-form input:focus {
  border-color: var(--dc-deep);
  background: var(--dc-surface);
  box-shadow: var(--dc-focus);
}
html:not([data-theme="dark"]) .gate-form .btn {
  padding: 14px 16px;
  border-radius: var(--dc-r-ctl);
  box-shadow: var(--dc-glow);
}
html:not([data-theme="dark"]) .remember input:checked + .bx {
  background: var(--dc-grad);
  border-color: transparent;
}
html:not([data-theme="dark"]) .remember input:checked + .bx::after {
  border-color: var(--dc-on-lime);
}
html:not([data-theme="dark"]) .gate .msg.ok {
  color: var(--dc-deep);
}

/* ---------- 10. 手机端：收紧内边距，保留大圆角语言 ---------- */
@media (max-width: 640px) {
  html:not([data-theme="dark"]) body {
    background-attachment: scroll !important;
  }
  html:not([data-theme="dark"]) body.portal .cols .card {
    padding: 15px 15px;
    border-radius: var(--dc-r-card) !important;
    box-shadow: var(--dc-shadow-card) !important;
  }
  html:not([data-theme="dark"]) body.portal .cols > .hello {
    padding: 2px 14px 4px !important;
    margin-bottom: 10px !important;
  }
  html:not([data-theme="dark"]) body.portal .m-toprow {
    gap: 8px !important;
  }
  html:not([data-theme="dark"]) body.portal .m-toprow .q-item {
    min-height: 82px;
    padding: 10px 4px !important;
    border-radius: 14px !important;
  }
  html:not([data-theme="dark"]) body.portal .m-toprow .q-item .qi {
    width: 32px !important;
    height: 32px !important;
    padding: 7px;
    border-radius: 10px;
  }
  html:not([data-theme="dark"]) body.portal .m-toprow .q-item .qn {
    font-size: 11px !important;
    font-weight: 500 !important;
  }
  html:not([data-theme="dark"]) .gate .box {
    padding: 32px 22px 28px;
    border-radius: 20px;
  }
  html:not([data-theme="dark"]) .gate-form input,
  html:not([data-theme="dark"]) .gate-form .btn {
    min-height: 48px;
  }
  :where(html:not([data-theme="dark"])) .card,
  :where(html:not([data-theme="dark"])) .card2,
  :where(html:not([data-theme="dark"])) .zone,
  :where(html:not([data-theme="dark"])) .panel {
    border-radius: var(--dc-r-card) !important;
  }
}

/* ---------- 11. 减少动态效果 ---------- */
@media (prefers-reduced-motion: reduce) {
  html:not([data-theme="dark"]) body.portal .cols .card:hover,
  html:not([data-theme="dark"]) body.portal .m-toprow .q-item:hover,
  :where(html:not([data-theme="dark"])) .home-card:hover {
    transform: none;
  }
}

/* ---------- 12. 保留旧版统一层里仍然有效的两条 ---------- */
/* 图标口径：Lucide 规格（24 网格 / 2px 描边 / 圆角端点） */
svg[viewBox="0 0 24 24"] {
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* 手机端隐藏滚动条（滚动能力保留） */
@media (max-width: 640px) {
  html, body { scrollbar-width: none; -ms-overflow-style: none; }
  html::-webkit-scrollbar, body::-webkit-scrollbar,
  *::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; }
  * { scrollbar-width: none; }
}

/* 手机端：子应用不要「套一张卡片」 */
@media (max-width: 640px) {
  body { padding-left: 0 !important; padding-right: 0 !important; }
  main { padding: 0 !important; margin: 0 !important; }
  .wrap, .page, .container { padding-left: 0 !important; padding-right: 0 !important; }
  main > .card2, main > .zone, main > .panel,
  .wrap > .card2, .wrap > .zone, .wrap > .panel,
  #app > .card2, #app > .zone {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    border-radius: 0 !important;
    margin: 0 !important;
  }
}


/* ---- forest-shell.css ---- */
/* ==========================================================================
   门户侧栏 · 松墨（开发站试验 2026-10-24）
   --------------------------------------------------------------------------
   只改侧栏里的绿色：选中的导航项、悬停底、身份卡头像。
   DC-UI 层把它们统一成青柠渐变（--dc-grad + --dc-glow），这里换成
   和班务助手「松墨米白」同一支深墨绿 #274505，实心、无光晕。
   落地：index.html 最后一张样式表，删这一行即回退。深色模式不受影响。
   ========================================================================== */

body.portal {
  --shell-accent: #274505;
  --shell-accent-ink: #ffffff;
  --shell-hover: #eff1e7;
  --shell-line: #e6e4dc;
  --shell-line-strong: #d2cfc6;
}

/* 悬停：青柠淡底 → 米白偏灰绿，字色同强调色 */
html:not([data-theme="dark"]) body .nav-item:hover {
  background: var(--shell-hover) !important;
  color: var(--shell-accent) !important;
}

/* 选中：青柠渐变 + 光晕 → 实心深墨绿 */
html:not([data-theme="dark"]) body .nav-item.on {
  background: var(--shell-accent) !important;
  background-image: none !important;
  color: var(--shell-accent-ink) !important;
  box-shadow: none !important;
}
html:not([data-theme="dark"]) body .nav-item.on:hover {
  background: #1d3503 !important;
}
html:not([data-theme="dark"]) body .nav-item.on .badge {
  background: rgba(255, 255, 255, .18) !important;
  color: var(--shell-accent-ink) !important;
}

/* 身份卡 / 头像：青柠渐变头像 → 深墨绿实心 */
html:not([data-theme="dark"]) body.portal .user-chip {
  background: #f8f8f4 !important;
  border-color: var(--shell-line) !important;
  box-shadow: none !important;
}
html:not([data-theme="dark"]) body.portal .user-chip:hover {
  background: var(--shell-hover) !important;
  border-color: var(--shell-line-strong) !important;
}
html:not([data-theme="dark"]) body.portal .user-chip .av {
  background: var(--shell-accent) !important;
  color: var(--shell-accent-ink) !important;
  box-shadow: none !important;
}

/* 侧栏里键盘焦点的圈也跟强调色走，不留在青柠 */
body.portal .side :focus-visible,
body.portal .user-chip:focus-visible {
  outline-color: var(--shell-accent);
}

/* 手机端侧栏收成底部 Tab，选中态同一支深墨绿（mobile-redesign 原本是 #2E7D32） */
.m-tabbar .mt.active { color: var(--shell-accent); }
