/* ★ v86 P0 新增：必须在 layout-v83.css / skin-layout.css **之后**加载。
   原因（实测）：写在 layout-v83.css 末尾时，浏览器只解析出 123 条顶层规则，
   本块（位于文件最末）**根本没被解析**—— 注入同样式到页面里则溢出立刻 12px→0。
   ⇒ 独立成文件、放在最后加载，是本项目既有的 p0-fixes 同款做法。 */
/* ============================================================================
 * v86｜classic 皮肤（无 data-skin）窄屏横向溢出 —— 用户主诉「界面能向左滑 / 右边一大块空」
 * ============================================================================
 * 【实测基线（smoke/_v86_probe_classic.js + _v86_dbg_topbar.js，可复跑）】
 *   classic @390：clientWidth=390  scrollWidth=**531**  溢出 **141px**  canScrollX=true
 *   classic @320：clientWidth=320  scrollWidth=**531**  溢出 **211px**
 *   ⇒ 7 个页面（home/board/calendar/team/settings/members/courses）**全部 531**，因为顶栏是全局的。
 *   对照：indigo / slate / ink 三套皮肤 @390/@320 的 scrollWidth 都 = clientWidth（零溢出）。
 *   ⇒ **用户（skinPref='classic'）是唯一受影响的**，与 team-lead 转述的 ui-scan 结论一致。
 *
 * 【🔴 真正的元凶（我自己的诊断探针 CSSOM 溯源 + 逐项验证得出，不是推测）】
 *   诊断实测（classic @390）：
 *     #app > .topbar        width=390  flex=0 1 auto      shrink=1  minWidth=0
 *       ├─ div.brand       x=10  w=122  right=132        flex=0 0 auto shrink=0
 *       ├─ nav#nav         w=0（classic 窄屏 display:none，不计）
 *       └─ div.me          x=140 **w=391** right=531  ← 🔴 自己就比视口宽 141px
 *     .me 的计算样式：**flex="0 0 auto"、flexShrink=0、minWidth=0**、overflow=clip
 *     .me 子元素（宽 → 不许收缩的原因）：
 *       input#globalSearch.search-in  w=100  flex=1 1 60px shrink=1  minW=0   ← 唯一可缩
 *       button#btnSyncNow.btn         w=64   flex=**0 0 auto** shrink=0
 *       button#themeBtn.btn           w=44   flex=**0 0 auto** shrink=0
 *       button#todoChip.btn           w=0    （≤420 已隐藏）
 *       span#meChip.chip              w=91   flex=**0 0 auto** shrink=0
 *       button#btnSwitch.btn          w=68   flex=**0 0 auto** shrink=0
 *       子元素宽合计 367 + 间隙 30 = 397 ⇒ .me 的 max-content 就是 ~391px
 *
 *   ⇒ **`.me` 带 `flex:0 0 auto`（不许收缩）⇒ 它的宽度 = max-content 391px，
 *      与视口无关**（320 和 390 量出来都是 391），于是必然溢出。
 *
 * 【元凶规则的出处 —— 已定位到行】
 *   `p0-fixes.css:255` `@media (max-width: 820px)` 块内的 `p0-fixes.css:267-271`，
 *   规则内容是：".topbar .me, .topbar .chip, .topbar .btn { flex: none; }"
 *   这条对**所有皮肤**生效（含 classic），本意是「不许把按钮压扁」，
 *   但它**连整个 .me 容器一起锁死了** ⇒ 容器自己撑出屏幕。
 *
 * ⚠⚠⚠ **本行曾把上面那条规则用注释引号包起来写（里面含嵌套的块注释起止符），
 *   结果那个嵌套的开注释符把外层注释**提前闭合**了**，
 *   导致本文件从那一行往后的所有 @media 块**全部不被浏览器解析**
 *   （实测：文件里有 4 个 @media，浏览器只认出 3 条顶层规则；
 *     而把同样内容注入页面里，溢出立刻从 12px 变 0px ⇒ 证明规则本身是对的）。
 *   ⇒ **教训（本项目栽过同类坑）**：CSS 注释里**绝不能出现块注释的起止符**。
 *     要引用别人的规则，直接用引号写纯文本，别把注释符包进去。
 *
 * 【🔴 为什么 v85 那批修复对 classic 无效（team-lead 指出的核心问题）】
 *   v85 我写的窄屏顶栏修复（`layout-v83.css:1717-1729`、`:1865` 起那几块）
 *   **全部带 `[data-skin]` 前缀** ⇒ 「恢复原样（classic）」的用户完全拿不到。
 *   ⇒ 这类问题以后必须**同时**提供「无 data-skin 时也生效」的对等规则。
 *   ⚠ 我已全局排查：本文件 1560 行之后我追加的 30 条 `[data-skin]` 规则里，
 *     **在窄屏下会导致 classic 拿到错误旧样式**的有 3 类（下面都补了对等版）：
 *       ① 顶栏 `.me` / `.search-in` 的收缩规则（本块，最严重）
 *       ② `#nav{display:none}` —— 但 classic 走的是 `styles.css:1094` 的同款规则，
 *          实测 nav 在 classic 窄屏确实是 w=0，**这条没问题**，不用补
 *       ③ 统计卡片等高 —— classic 用的是 `styles.css:642` 的 6 列，
 *          实测溢出为 0、卡片无参差，**不在本次症状链上**，本轮不动
 *
 * 【✅ 修法：只解「容器被锁死」，并按优先级减件（不是无脑缩小搜索框）】
 *   ⚠⚠ **为什么不能只靠收缩搜索框**（team-lead 让我先证伪，我算过了）：
 *     390px 档：.me 可用 = 390-20(padding)-122(brand)-8(gap) = **240px**
 *     而 .me 内**固定件**（同步64+主题44+头像91+切换68）= **267px**，间隙 30px
 *     ⇒ 即使把搜索框压到 **0px**，仍是 267+30 = 297 > 240 ⇒ **必然溢出 57px**
 *     320px 档更差：可用 170px，缺 **127px**
 *   ⇒ **必须减件**。分级减法（与 v85 那批的思路一致，但这次要给 classic 也生效）：
 *     ① ≤480px：隐藏「🔄同步」—— 手机有下拉刷新（`app.js` 的 bindSwipe），非刚需
 *     ② ≤480px：头像 chip 只留头像（藏 `.me-name`）—— 省 ~45px
 *     ③ ≤420px：「待办」已在 v85 隐藏（classic 走 styles.css，没有这条 ⇒ 这里补）
 *     ④ ≤360px：「切换身份」限宽（保底，宁可截短也不溢出）
 *   算：390 档减掉同步(64)+名字(45) = 固定件 267-109 = 158 + 间隙 30 = 188 ≤ 240
 *        ⇒ 搜索框得 240-188 = **52px**（可点，≥44px 触摸友好）✅
 *        320 档再限宽切换身份(68→46) = 固定件 136 + 30 = 166 ≤ 170
 *        ⇒ 搜索框得 **4px** ⇒ 所以 320 档还要把搜索框设 min-width:0 并允许隐藏占位
 *
 * 【⚠ 为什么不用「把 .me 改成可收缩」来一劳永逸】
 *   试过（诊断第④步）：给 `.me` 加 `overflow:hidden` ⇒ 溢出**一点没降**（仍 141px），
 *   因为 `flex:none` 让它先按 max-content 定宽再裁，裁掉的只是视觉，滚动宽度不变。
 *   ⇒ 唯一有效的是**把 flex 改成可收缩** + **减件**，二者必须一起做。
 *
 * 【作用域纪律 —— 本项目栽过 3 次】
 *   全部包在 `@media (max-width:1023px)` 内（不是 760/820），
 *   因为 v85 已把顶栏的修复断点统一到 1023；820~1023px 段 classic 同样会溢出。
 *   ⚠ 每条都是**追加**，不动 styles.css / p0-fixes.css 既有行。
 * ========================================================================== */
@media (max-width: 1023px) {
  /* ══ ① 解除容器锁死（这是「必溢出」的总闸）══
     🔴 对应用户现象：「界面能向左滑」「右边有一个很大的空」
     ⚠ 只把 `flex:none` 改成可收缩，**不**动子按钮的 flex:none ——
        按钮压扁比溢出更难看（项目记忆：p0-fixes 原注释「不许被压扁」是对的，要尊重）。
        真正该做的是「容器可收缩 + 子件保持原尺寸 + 减件」。 */
  #app > .topbar > .me {
    flex: 0 1 auto !important;      /* ← 关键：0 0 auto ⇒ 0 1 auto（允许收缩） */
    min-width: 0 !important;        /* 允许收缩到内容以下（配合下面减件） */
    max-width: 100% !important;     /* 🔴 关键：不超过 topbar 内容宽 ⇒ 杜绝溢出 */
  }
  /* 搜索框：吃掉 .me 收缩后剩下的全部空间（宽度由 flex 决定，不再是固定 160px） */
  #app > .topbar > .me .search-in,
  #app > .topbar > .me #globalSearch {
    flex: 1 1 40px !important;
    min-width: 0 !important;
    width: auto !important;         /* 🔴 压掉 styles.css:1185 的 width:160px —— 固定宽会盖过 flex-basis */
  }
  /* 🔴 v86 二次修正：「切换身份」与「主题」这两个**文字按钮**也必须允许收缩。
     【为什么第一版漏了它们】第一版只让「容器 + 搜索框」可收缩，其余子件保持 flex:none。
     实测（classic @390，注入等价规则后实测）：`.me` 的 flex 确实变成了 0 1 auto，
     但**宽度仍是 262px、溢出只从 141 降到 12** ⇒ 因为 .me 是 flex 容器，
     **它自己不会小于子件的 max-content 之和**。
     逐项实测的子件宽度（classic @390，减件后）：
       搜索框 100 + 主题 44 + 头像 32 + 切换身份 68 + 3 个间隙×6 = 262  ← 正好等于 .me 宽度
     ⇒ 搜索框已经压到 40px 下限了，剩下 44+32+68=144 是三个 flex:none 的文字/图标件，
       **一个都不肯让位** ⇒ 容器没得缩。
     ✅ 修法：给这三个也开 `flex: 0 1 auto` + `min-width: 0`，
        并让 #btnSwitch 的 max-width 真正生效（它有 4 个汉字，限宽后配合 overflow 裁切）。
        ⚠ 仍然**不**改它们的 padding / 字号 —— 压扁按钮比溢出难看，这条要守住。 */
  #app > .topbar > .me #btnSwitch,
  #app > .topbar > .me #themeBtn,
  #app > .topbar > .me .btn {
    flex: 0 1 auto !important;
    min-width: 0 !important;
  }
  /*「切换身份」在极窄屏限宽：宁可截短也绝不许溢出到屏幕外（用户截图里被裁成半个字）
     ⚠ v88 已撤销本条，改为按实测所需宽度给足 —— 见文件末尾 v88【第6撤】。
        保留在文件里仅作历史对照，勿直接删（删了会丢掉「为何曾这么写」的上下文）。*/
  #app > .topbar > .me #btnSwitch {
    max-width: 62px !important;
    overflow: hidden !important;
    white-space: nowrap !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
 * v88【第6撤】顶栏文字被截断 —— 用户原话：
 * 「搜索框是变小了，但是为什么其他几个组件不会自适应变大一些来显示全部的文字」
 * 手机截图实况：「青志社团」「搜索 ·」「🌙」「头像」「切换身」← 最后一个字被吃掉了。
 * ---------------------------------------------------------------------------
 * 【根因 —— 实测得来（smoke/_v88_topbar_text.js），不是猜】
 *   判据 T1（scrollWidth <= clientWidth 才算「文字完整」）实测 @412：
 *     #btnSwitch clientW=60  scrollW=66  ⇒ 差 6px，4 个汉字装不下
#                                  ⇒ overflow:hidden 把它裁成「切换身」
 *     #themeBtn clientW=42  scrollW=47  ⇒ 差 5px，🌙 emoji 被裁掉一横
 *     #meChip 32/32 完整、#teamBrand 56/56 完整
 *
 *   真凶：上面第 ① 块末尾（本文件 122-127 行，v86 为修「横向溢出」加的）：
 *       #btnSwitch { max-width: 62px !important; overflow: hidden !important; }
 *   ⚠ 那条的**意图是对的**（宁可截短也绝不许溢出屏幕），
 *     但它把「不许溢出」实现成了「裁字」，而不是「让字有地方放」。
 *
 *   🔴 关键实测结论：**顶栏根本没满，空间是够的**
 *     @412：最右件右边界=335，topbar 宽=412 ⇒ 右侧空着 77px
 *     @390：最右件=335 / 390       ⇒ 空着 55px
 *     @360：最右件=319 / 360       ⇒ 空着 41px
 *     ⇒ 「文字被裁」与「横向溢出」**不是同一件事**：
 *       是先前那条 max-width 把空间**空着不用**、同时又**裁掉了字**。
 *       用户说的「不会自适应变大一些」正是这个现象。
 *
 * 【修法】取消裁字，按**实测所需宽度**给足
 *   ① #btnSwitch：撤掉 max-width/overflow 限制，改用 width:auto
 *      ⇒「切换身份」4 个字（实测需 66px）完整显示。
 *   ② #themeBtn：同样撤掉限宽，让 emoji 不被裁（实测需 47px）。
 *   ③ #meChip：允许缩到 0 但配合 ellipsis 显示省略号，
 *      **不裁成半个字**（头像名字本身是次要信息，缩写比半个字好）。
 *   ④ 保留 overflow-x 兜底：若真放不下，允许整块横向滚动而不是裁字。
 *
 * ⚠ 为什么不用「缩小字号」：用户要的是「显示全部的文字」，
 *   缩字号仍是缩小，且中文在小字号下可读性差。
 * ⚠ 为什么不用「换行」：顶栏高度会被撑开，把下面内容顶下去。
 * ⇒ 唯一正确方向就是「给够宽度」。实测证明空间够。
 * ========================================================================== */
@media (max-width: 1023px) {
  /* ① 「切换身份」完整显示（实测需 66px；给 72px 余量，容纳不同字重差异） */
  [data-skin] #app > .topbar > .me #btnSwitch,
  :root:not([data-skin]) #app > .topbar > .me #btnSwitch {
    width: auto !important;
    max-width: none !important;
    /* 🔴 撤掉 overflow:hidden —— 它正是「切换身」的直接成因。
       ⚠ 保留 nowrap：允许它横向滚动顶栏，但绝不换行把顶栏撑高。*/
    overflow: visible !important;
    white-space: nowrap !important;
    flex: 0 0 auto !important;
  }
  /* ② 主题按钮（🌙）不裁 emoji（实测需 47px） */
  [data-skin] #app > .topbar > .me #themeBtn,
  :root:not([data-skin]) #app > .topbar > .me #themeBtn {
    width: auto !important;
    max-width: none !important;
    overflow: visible !important;
    flex: 0 0 auto !important;
  }
  /* 🔴 v88修正：#meChip 要能显示「头像 + 名字 + 角色」。
     ⚠ 上一轮我在这里写了 `max-width:72px`，那是我引入的缺陷：
       styles.css:1102 里 `.chip .me-name{max-width:72px}` 已经是**名字本身**的限宽，
       而我又给整个 chip 加了 72px 上限
       ⇒ 72px 扣掉头像(32px) 只剩 40px 给名字 ⇒ `.me-name` 被压成 0 宽
       ⇒ 用户反馈「顶部只会显示头像，不会显示名字」。
       ⚠ 老版本截图里是「平台主管」这样的角色文字 + 头像 + 名字，本项目原本的设计就是这个
         （见 app.js:11136 的渲染：me-lbl + 头像 + me-name + role），不该被裁没。
     ⇒ 改为：chip 宽度随内容（fit-content），名字保留 styles.css 的 72px 上限，
       角色文字在极窄屏才隐藏。
     ⚠ 下限保留 44px（v86 实测顶栏余量只有约 10px，给太大会把别的按钮挤出去）。*/
  [data-skin] #app > .topbar > .me #meChip,
  :root:not([data-skin]) #app > .topbar > .me #meChip {
    /* 🔴 由72px 放宽到 fit-content：72px 装不下「头像+名字+角色」，
       那正是名字消失的原因（见上方说明）。 */
    max-width: fit-content !important;
    min-width: 44px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
  }
  /* 名字本身的上限交还给 styles.css:1102 的 72px，
     这里只保证它不被 chip 的上限二次压扁。*/
  [data-skin] #app > .topbar > .me #meChip .me-name,
  :root:not([data-skin]) #app > .topbar > .me #meChip .me-name {
    max-width: 72px !important;
  }
  /* 极窄屏（≤360）才隐藏角色文字：它是四项里信息价值最低的一项，
     且 styles.css:1101 在 ≤760 已经hide 了「当前」二字（项目原设计，保留）。*/
  @media (max-width: 360px) {
    [data-skin] #app > .topbar > .me #meChip .role,
    :root:not([data-skin]) #app > .topbar > .me #meChip .role { display: none !important; }
  }
  /* ④ 兜底：真放不下时整块可横向滑动，而不是裁字
     ⚠ 下面这两条会覆盖 v86 第① 块里 .me 的 max-width:100%，
       故意如此 —— 上层限宽正是「裁字」的间接来源。*/
  [data-skin] #app > .topbar > .me,
  :root:not([data-skin]) #app > .topbar > .me {
    max-width: 100% !important;
    overflow-x: auto !important;
    overflow-y: hidden !important;
  }
  /* ⑤ 配合：给顶栏本体同样的兜底，避免内部裁切传导成整页横向滚动 */
  #app > .topbar { overflow-x: auto !important; overflow-y: hidden !important; }
}

/* ══ ② ≤480px：减件（对应「右边一大块空」的主因）══
   ⚠ 为什么 ≤480 而不是 ≤760：421~480px 段只藏同步，头像名字还留着，
     经预算计算仍够（见下面注释的算式）⇒ 尽量晚一点减，功能可用性更好。 */
@media (max-width: 480px) {
  /* 「🔄同步」：手机有下拉刷新（app.js bindSwipe），此按钮在手机上非刚需。
     ⚠ 桌面/平板（481~1023px）**保留** —— 触屏平板没有下拉刷新。 */
  #app > .topbar > .me #btnSyncNow {
    display: none !important;
  }
  /*🔴 v88 撤销：头像 chip 只留头像、藏名字的那条规则。
     ⚠ 为什么撤销（用户明确要求）：
       用户原话「顶部只会显示头像，不会显示名字，这个你给他改过来，
       就是会显示我的名字」，并给了老版本截图作参照（顶栏有头像 + 名字 + 角色）。
     ⚠ 原注释写的理由是「省 ~45px，名字仍可在切换身份 → 个人中心看到」，
       但用户要的是**顶栏就能看到自己是谁**——尤其多人共用一台设备时，
       只剩一个头像根本分不清当前登录的是谁。这正是本 chip 存在的原因
       （见 app.js:11130 的注释：「以前只有一个名字，多人共用一台电脑时
       看不出现在是谁登录的，所以加了『当前』+ 头像 + 名字 + 角色」）。
     ⇒ 那 45px 从别处省：搜索框本轮已经收窄到 64px（v88 成果），
       且 ≤480 档本来就已隐藏「🔄同步」与「📌待办」，余量够用。 */
  /*原规则（display:none）已移除，改由下面的 #meChip 宽度规则兜住整体不溢出。*/
  #app > .topbar > .me .chip .me-name {
    /* 不再隐藏：用 inline 让它随内容排开，宽度上限交给上面的 72px 规则 */
    display: inline !important;
  }
  /* 头像 chip 在减件后不该再撑宽度 */
  #app > .topbar > .me #meChip {
    /*🔴 v88：`0 1 auto` → `0 0 auto`（**不可收缩**）。
       ⚠ 为什么必须改：flex 收缩因子=1 意味着「空间不够时优先压缩我」。
         实测（412px，名字换成真实 3~4 字中文名）：
           角色宽 24px，名字「林知远」需 36px 却只拿到 31px ⇒ 被截 5px
           名字「欧阳明轩」需 48px 只拿到 37px ⇒ 被截 11px
         ⇒ 名字仍然显示不全，正是用户要修的那件事。
       ⚠ 为什么 `0 0 auto` 不会重新制造横向溢出（v86踩过的坑）：
         实测 @412 与 @390 的页面横向溢出都是 0px ——
         `.me` 栏还有余量，且 ≤480 档已隐藏「🔄同步」「📌待办」、
         搜索框本轮已收窄到 64px。
         真正兜底的是**上层 `.me` / `.topbar` 的 overflow-x:auto**
         （本文件【第7撤】已设）：万一真放不下，可横向滑动而不是裁字。
       ⚠ 与 #meChip 的 min-width:44px 并存不冲突：44 只是下限，
         实际宽度由内容（头像+名字+角色）决定。*/
    flex: 0 0 auto !important;
    min-width: 44px !important;
    padding: 3px 5px !important;
  }
}

/* ══ ③ ≤420px：待办徽标只留数字（补 classic 缺失的 v85 减法）══ */
@media (max-width: 420px) {
  #app > .topbar > .me #todoChip {
    font-size: 0 !important;                 /* 文字清零，只留数字徽标 */
    padding: 3px 5px !important;
    min-height: 0 !important;
  }
  #app > .topbar > .me #todoChip .badge {
    font-size: 12px !important;               /* 徽标本身必须仍看得见 */
  }
}

/* ══ ④ ≤360px：最后一道保险 ══
   预算（实测值）：320px 档 .me 可用 = 320-20-122-8 = 170px；
   减掉同步(64)+名字(45) 后固定件 = 主题44+头像~24+切换46 = 114 + 间隙 30 = 144 ≤ 170
   ⇒ 搜索框得 26px（还能显示，占位可用）。
   ⚠ 但更窄的机型（≤300px）连这 26px 都没有 ⇒ 让搜索框允许完全压到 0，
     保证「绝不溢出」这条优先级最高。 */
@media (max-width: 360px) {
  #app > .topbar > .me .search-in,
  #app > .topbar > .me #globalSearch {
    min-width: 0 !important;
  }
  #app > .topbar > .me #btnSwitch {
    max-width: 46px !important;
    padding: 5px 6px !important;
    font-size: 12px !important;
  }
  /* 品牌名限死宽度，绝不与右侧区抢空间（否则又是溢出） */
  #app > .topbar > .brand #teamBrand {
    max-width: 56px !important;
  }
  #app > .topbar > .brand {
    flex: 0 1 auto !important;
    min-width: 0 !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
 * 【第 3 项】设置页裸 <code> 横向溢出（team-lead 报的缺陷③，我实测复现）
 * ════════════════════════════════════════════════════════════════════════════
 * 【实测】320×640 切到「设置」页 ⇒ scrollWidth 比 clientWidth 大 65~67px，
 *   超出的元素是两个**裸 `<code>`**（不是 .fatal-addr code）：
 *     +55px  code  宽 350  内容 "https://open.bigmodel.cn/api/paas/v4/cha…"
 *     + 9px  code  宽 304  内容 "https://api.siliconflow.cn/v1/chat/compl…"
 *   那串是 53 个字符的 API 地址，**中间没有空格也没有连字符可供断行**
 *   ⇒ 浏览器找不到断行点 ⇒ 整段撑到 350px 宽。
 * 【根因】`styles.css` 只给 `.fatal-addr code`（2053 行）与 `.fatal-more code`
 *   （2061 行）设了 `word-break:break-all`，**裸 code 没有任何换行规则**。
 * 【修法】给 `.hint code`（AI Key 那几行的容器就是 .hint）补断行；
 *   兜底再给全局 `code` 补一条 —— 因为实测这两处的父级不都是 .hint，
 *   只补 .hint 会漏。为安全用 `overflow-wrap:anywhere` + `word-break:break-all` 双写。
 * ⚠ 只改显示，不断行内容、不改文案。
 * ⚠ 包在 @media(max-width:1023px) 内：桌面端地址本来就放得下，不需要断行。
 * ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1023px) {
  .hint code,
  .fld code,
  .pc-sec code,
  #main code {
    word-break: break-all !important;
    overflow-wrap: anywhere !important;
  }
}


/* ============================================================================
 * v86｜概览页六个统计卡片：窄屏 2 列（用户报「六个东西这样排布不好」+ 给了 2×3 参考图）
 * ============================================================================
 * 【实测基线（smoke/_v86_probe_cards.js，可复跑）】
 *   classic @390：列数 = **6**（53.33px × 6，容器 370px）
 *                卡片 53×182，`.k` 高度 **81px**（「今日课程」被压成竖排单字）
 *   classic @320：列数 = **6**（46px × 6，容器 300px），`.k` 高 **81~101px**
 *   classic @1440：列数 = 6（212px × 6）✅ 桌面本来就对
 *
 * 【🔴 根因 —— 与 team-lead 报告的一处修正】
 *   team-lead 指认 `layout-v83.css:544`
 *     `:root:not([data-skin]) #homePanels .stat-grid { grid-template-columns: repeat(6,1fr) !important }`
 *   **规则本身定位没错，但「它在 @media(min-width:1024px) 内」这个前提不成立。**
 *   ⚠ 我实测那个 @media（`layout-v83.css:461`）在**第 495 行就闭合了**（depth 归零），
 *     第 544 行是**顶层规则** ⇒ **所有宽度都命中**，包括 390/320。
 *   ⚠ 浏览器 CSSOM 也证实：该规则的 media 条件显示为 `@(无)`（无媒体查询）。
 *   ⇒ 所以它在 390px 压过了 `styles.css:1121`（`@media max-width:760px` 的 `repeat(2,1fr)`）
 *      和 `styles.css:1176`（`@media max-width:420px` 的 `repeat(2,1fr)`）——
 *      靠的是 `!important` + 选择器权重（`#homePanels` 是 ID）。
 *
 * 【✅ 修法：窄屏反向覆盖回 2 列（不动 v83 既有行）】
 *   ⚠ 为什么用「窄屏覆盖」而不是「删掉 544」：
 *      544 是 v83.13「恢复原样」的设计意图（把概览还原成 5 面板通栏、6 列统计）。
 *      直接删会改变 classic 用户的**桌面**观感（那是他们主动选的「不要新版」）。
 *      ⇒ 只在 ≤1023px 把它压回 2 列，**桌面 6 列保持原样**。
 *   ⚠ 断点选 1023 而不是 760/420：
 *      平板（768~1023）在 classic 下同样会被 544 压成 6 列 ⇒ 一样要修。
 *
 * 【文字竖排的第二个原因：卡片内元素没有 min-width:0】
 *   6 列时每张卡只有 53px，`.k` 的文字被压到 23px 宽 ⇒ 逐字换行 ⇒ 高 81px。
 *   改成 2 列后每张卡约 180px，文字自然放得下；
 *   但仍给 `.k`/`.v` 加 `white-space:nowrap` 兜底 ——
 *   ⚠ 「待审批请假」5 个字 + padding，2 列下若容器再窄一点仍可能竖排。
 * ========================================================================== */
@media (max-width: 1023px) {
  /* ① 列数压回 2（用户参考图就是 2 列 × 3 行） */
  :root:not([data-skin]) #homePanels .stat-grid,
  #homePanels .stat-grid {
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 8px !important;
  }
  /* ② 兜底：三套皮肤在窄屏也统一 2 列（实测它们走 layout-v83:1105/1462，
        桌面 6 列 / 窄屏另有规则 —— 这里只兜底，不强行改它们的桌面观感） */
  [data-skin] #homePanels .stat-grid {
    grid-template-columns: repeat(2, 1fr) !important;
  }
  /* ③ 卡片内文字不许竖排：nowrap + overflow 保护。
        ⚠ min-width:0 是 grid/flex 子项能在窄容器里正常收缩的关键。 */
  #homePanels .stat-card .k,
  #homePanels .stat-card .v {
    white-space: nowrap !important;
    min-width: 0 !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
  }
  /* ④ 卡片本身别被压成细条：给个最小高度，保证「图标+数字+标签」三行放得下 */
  #homePanels .stat-card {
    min-width: 0 !important;
    min-height: 68px !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   v86 补修批次（由 team-lead 直接追加，避免继续等排期）
   ⚠ 纪律：注释里不出现块注释起止符 —— 本文件此前就因注释嵌套吞掉整块规则，
     而 grep / 字节数 / md5 全部「看起来正常」。引用规则请用纯文本描述。
   ════════════════════════════════════════════════════════════════════════════ */

/* ── ① 深色模式季节标签：像素级实测 1.06:1（几乎不可读）─────────────────
   根因：p0-fixes.css 的 .sb-txt 有一条 background 声明带 !important，
   压过了作者后来想加的深色渐变底衬（那条没带 !important）⇒ 底衬从未渲染；
   而 layout-v83.css 里 .sb-art 强制 opacity:1，整幅水彩插画盖住卡片底色
   ⇒ 浅色文字直接压在高亮插画上。
   ⚠ 验收必须用像素级读数：按 CSS 声明色算会得出 13.88:1（看起来很好），
     那是被插画骗了的假读数。 */

/* 桌面与手机通用：只要是深色主题就加底衬 */
:root[data-theme="dark"] .season-banner .sb-txt {
  background: linear-gradient(180deg, rgba(0,0,0,.62), rgba(0,0,0,.42)) !important;
  color: #ffffff !important;
  text-shadow: 0 1px 3px rgba(0,0,0,.95) !important;
  border-radius: 8px;
  padding: 2px 6px;
}
:root[data-theme="dark"] .season-banner .sb-hi {
  color: #ffffff !important;
  text-shadow: 0 1px 3px rgba(0,0,0,.95) !important;
}
:root[data-theme="dark"] .season-banner .sb-tip,
:root[data-theme="dark"] .season-banner .sb-cloth {
  color: #e9edf2 !important;
  text-shadow: 0 1px 2px rgba(0,0,0,.9) !important;
}

/* 恢复原样皮肤（无 data-skin）同样要覆盖：
   v83 的深色修复都带 [data-skin] 前缀 ⇒ classic 用户一条都拿不到。 */
:root[data-theme="dark"]:not([data-skin]) .season-banner .sb-txt {
  background: linear-gradient(180deg, rgba(0,0,0,.62), rgba(0,0,0,.42)) !important;
  color: #ffffff !important;
  text-shadow: 0 1px 3px rgba(0,0,0,.95) !important;
  border-radius: 8px;
  padding: 2px 6px;
}
:root[data-theme="dark"]:not([data-skin]) .season-banner .sb-hi {
  color: #ffffff !important;
  text-shadow: 0 1px 3px rgba(0,0,0,.95) !important;
}
:root[data-theme="dark"]:not([data-skin]) .season-banner .sb-tip,
:root[data-theme="dark"]:not([data-skin]) .season-banner .sb-cloth {
  color: #e9edf2 !important;
  text-shadow: 0 1px 2px rgba(0,0,0,.9) !important;
}

/* ── ② 裸 code 溢出 ──────────────────────────────────────────────────────
   设置页里一个 53 字符、无空格的网址（open.bigmodel.cn 的接口地址）
   浏览器找不到断行点 ⇒ 实测 right=391 超出 390 视口。
   旧样式只给 .fatal-addr code 与 .fatal-more code 设了断行，裸 code 没有。 */
.hint code,
.panel code,
.card code,
.view code {
  word-break: break-all !important;
  overflow-wrap: anywhere !important;
  white-space: normal !important;
}

/* ── ③ 课表 / 成员表格列宽 ──────────────────────────────────────────────
   实测 th.mx-mem 与 td.mx.k-free 横向压掉 23px；320px 下表格宽 911px。 */
@media (max-width: 1023px) {
  table.mx-table {
    table-layout: fixed !important;
    width: 100% !important;
    max-width: 100% !important;
  }
  table.mx-table th,
  table.mx-table td {
    min-width: 0 !important;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* 窄屏优先「不丢信息」：给外层容器加横向滚动兜底 */
  .mx-wrap,
  .mx-table-wrap {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
  }

  /* ── v97：空闲矩阵「成员名被截断」修复 ──────────────────────────────────
   *
   * 【用户原话】成员列表里「平台主管」「浩明」这类名字被截断成"平"、"浩"一个字。
   *
   * 【🔴 根因不是 team-lead 说的 styles.css:1849 的 ellipsis —— 那是症状，不是原因】
   *   上面这个 1023px 块（同文件 477-495 行）自己就是元凶，它有**两个**副作用：
   *     ① `table.mx-table th{min-width:0!important}`（485 行）
   *        ⇒ 干掉了 styles.css:1846 的 `.mx-table th:first-child{min-width:74px}`，
   *          也干掉了 847 行的 110px。**实测 min-width 计算值 = 0px**。
   *     ② `table-layout:fixed`（479 行）+ 表格 min-width:640px（styles.css:938）
   *        ⇒ 13 列**均分** 640px，没有任何一列能多拿。
   *   两者叠加的实测结果（390×844，Chromium）：
   *        th.mx-mem 实际只有 **44.92px**
   *        扣掉 padding(2+8) + 头像 18 + gap 7 = 35px
   *        ⇒ .mx-name 只剩 **clientWidth = 10px**，而单个汉字 14px
   *        ⇒ scrollWidth 57 / clientWidth 10，**溢出 47px**，一个字都显示不全。
   *   ⇒ 所以「加 white-space:normal 让它换行」这一条**单独做没有用**：
   *     10px 宽的盒子连一个 14px 的汉字都放不下，换行也只是换成一列更窄的省略号。
   *     实测确认：只加换行、列宽不变，判据仍全绿（即 bug 依旧）。
   *
   * 【修法：必须给列显式 width，且断点必须与肇事块一致（1023px，不是 760px）】
   *   ⚠ 断点写成 760px 会漏掉 761~1023px：实测 768×1024 / 1023×768 两个视口
   *     成员名照样被截断（因为肇事块是 1023px）。已在探针里实测确认。
   */
  /* ① 成员列给显式宽度（min-width 会被上面的 min-width:0!important 吃掉，
        所以 width 和 min-width 都要写）。
        92px = padding(2+8) + 头像18 + gap7 + 4个汉字(4×14=56) + 余量1 */
  table.mx-table th.mx-mem,
  table.mx-table thead th:first-child {
    width: 92px !important;
    min-width: 92px !important;
  }
  /* ② 名字允许换行：超过 4 字（92px 装不下）的长名换行显示，而不是省略号。
        word-break:break-all 让「欧阳娜娜丰丰丰」这种无空格长串也能断。 */
  .mx-name {
    white-space: normal !important;
    word-break: break-all;
    overflow-wrap: anywhere;
    line-height: 1.15;
  }
  /* ③ 换行后行高变高，th 若仍钉死 28px 会把第二行切掉/糊到隔壁行
        ⇒ 放开高度，但保留 28px 下限 ⇒ 单行时观感与现在完全一致。 */
  table.mx-table tbody th.mx-mem {
    height: auto !important;
    min-height: 28px;
    padding-top: 3px;
    padding-bottom: 3px;
  }
  /* ④ 表头时间段 "08:30-09:15"（实测需 72px）在窄列（41px）里必然放不下。
        ⚠ v86 之前它是**溢出糊到右邻列 16.66px**（th 是 overflow:visible，
          实测用 Range 量文字墨迹右边界 - th 右边界 = +16.66px），
          也就是窄屏表头的时间是**糊在隔壁列上的**，不是干净裁切。
        这里让它换成两行（"08:30-" / "09:15"）：不糊邻列、信息也不丢
        （完整时间本来就在 th 的 title 里）。 */
  table.mx-table thead th em {
    white-space: normal !important;
    word-break: break-all;
    overflow-wrap: anywhere;
    line-height: 1.15;
    margin-top: 1px;
  }
}

/* ── ④ 开屏动画窄屏适配 ────────────────────────────────────────────────
   用户截图：「青志」「青志社团」明显偏右、下方文案被截。
   常见成因：容器用固定宽度或 margin auto 在窄屏算错。 */
@media (max-width: 480px) {
  .intro-ov .intro-logo {
    font-size: clamp(28px, 11vw, 64px) !important;
    letter-spacing: 4px !important;
    max-width: 92vw !important;
    text-align: center !important;
  }
  .intro-ov .intro-props,
  .intro-ov .intro-fx,
  .intro-ov .intro-slogan {
    max-width: 92vw !important;
    margin-left: auto !important;
    margin-right: auto !important;
    text-align: center !important;
  }
}
/* ============================================================================
 * v86｜设置页「⑫ 显示比例（大小）」三档按钮触摸目标过小
 * ============================================================================
 * 【用户原话】设置页把滑块拖到 85%、三个按钮（小一档/标准/大一档）**点了没反应**，
 *   用户自己都写了「这个按钮不是正好点得到」。
 *
 * 【🔴 实测结论：与team-lead 的推断不同，三条前提都要更正】
 *   我用 smoke/_v86_probe_zoom.js 实测（副本库 + 自建测试账号，390×844）：
 *
 *   ❌ 前提一：「点了没反应，因为没调render()」—— **不成立**
 *     实测点「大一档」：
 *       <html> style.zoom : (空) → **1.15**（确实变了）
 *       实际渲染宽度      : brand 124→**142**px、me 238→**216**px（肉眼可见地变大了）
 *     ⇒ `style.zoom` 是**布局属性**，浏览器会立即重排，**本来就不需要 render()**。
 *     ⇒ 所以**我没有加 render()** —— 加了只会引入闪烁/重影（团队邮件里担心的那个副作用），
 *        属于「为不存在的问题改代码」。若team-lead 的浏览器里真的没变化，
 *        那多半是**他看的是别的时间点/别的元素**，请让他按我这三条数据复核。
 *
 *   ❌ 前提二：「toast 文案多了『× 2』，是 GIF 改动误伤」—— **不成立**
 *     实测 toast 文本 = `"已切到 115%"`（`app.js:9367`）。
 *     源码：`toast('已切到 ' + Math.round(v * 100) + '%');` —— **没有 × 2**。
 *     `git diff HEAD -- public/assets/app.js` 里也**没有任何 `× 2` 的增删**。
 *     全项目 grep `× *2` 只命中中文注释里的「30×24h」「200×200」等，与 toast 无关。
 *     ⇒ 用户截图里那个「× 2」**我解释不了**，不排除是 toast 队列里两条消息
 *       叠在一起、或截图被手动标注过。**我没找到代码依据，所以没改。**
 *     ⚠ 若用户真看到，请让他截图时**把整条 toast 都框进去**（可能是上一条残留）。
 *
 *   ❌ 前提三：「未登录时 dcLoadPrefs 提前 return 导致比例不持久化」—— **实测不成立**
 *     实测：未登录时写 `localStorage.tc_ui_zoom=0.85` → 刷新后**仍是 "0.85"**，
 *     没被冲掉；已登录时点按钮 → 刷新后 `style.zoom` 仍是 1.15、`localStorage` 仍是 1.15。
 *     ⇒ **两态都持久化。** 我**没有改 dcLoadPrefs** ——
 *       那处`if (!S.me) { uiZoomApply(S.uiZoom); return; }` 只是「不读服务端偏好」，
 *       而 `uiZoomApply()` 本身**无条件写 localStorage**（`app.js:22779`），
 *       所以未登录态的写入路径本来就是通的。
 *     ⚠ 另：`app.js:22265` 读 `localStorage.getItem(UI_ZOOM_LS)`，而 `var UI_ZOOM_LS`
 *       声明在 22772 行（行号更大 ⇒ 执行时为 `undefined`）。这**看起来**像个 bug，
 *       但 `localStorage.getItem(undefined)` 返回 `null`、被 `parseFloat` 吃掉，
 *       而真正生效的是 `uiZoomApply` 里的写入 —— **实测无影响，故未动**。
 *
 *   ✅ **真正的问题只有一个：触摸目标太小**（实测数据）
 *     按钮 class 是 `btn tiny ghost`，`styles.css:1111`（@media max-width:760px）
 *     把 `.btn.tiny` 压成 `min-height:26px; padding:3px 9px; font-size:12px`。
 *     实测三档按钮（390px 视口，100% 与 85% 下同值）：
 *       「小一档（看得更多）」 128×**27**px
 *       「标准」                44×**27**px
 *       「大一档（看得更清）」128×**27**px
 *     高度 27px 远低于触摸建议的 **≥44px**（WCAG 2.5.5 / 平台 HIG 的最小目标）。
 *     ⚠ 而且**用户把比例调小（85%）时视觉更小**、更难点 —— 与用户主诉吻合。
 *
 * 【修法】只加触摸目标，不动 padding/font-size（那会把设置页撑得很难看）：
 *   min-height:44px + min-width:44px，用 `padding` 撑开而不是 `height`
 *   ⇒ 视觉字号不变（12px 仍 12px，用户仍能读清），只是可点区域变大。
 * ⚠ **为什么不用 .btn.tiny 全局改**：那会波及设置页**所有** tiny 按钮（几十个），
 *   改变整个设置页的行距与密度 —— 影响面远超本问题。
 *   ⇒ 只针对 `[data-zoom]` 三枚按钮，加作用域。
 * ⚠ **为什么不限 @media**：桌面端按钮也是 27px，但鼠标指针不依赖触摸目标尺寸。
 *   我仍加上 `min-height:44px` 于窄屏以下（用户主诉场景），
 *   桌面保持原样以免影响整体密度。
 * * ⚠ **为什么用52px 而不是 44px**（实测踩到的，第二轮才发现）
 *   我第一版写 `min-height:44px`，实测在 **zoom=100%** 下确实是 44px ✅，
 *   但**用户所在的 zoom=85% 下实测只有 37px**（44 × 0.85 = 37.4）⇒ 判据仍 FAIL。
 *   ⚠ 原因：`<html>` 上的 `style.zoom` 会把**所有尺寸一起缩放**，
 *     所以「44px 的按钮」在 85% 下渲染出来只有 37px —— **触摸目标是渲染尺寸，不是 CSS 声明值**。
 *   ⇒ 反推：要让 **85%（用户当前值）** 下渲染出 44px，CSS 值需 44 / 0.85 ≈ **52px**。
 *     取 52px 后：100% → 52px（仍 ≥44 ✅）、85% → 44.2px（✅ 刚好达标）。
 *   ⚠ 诚实说明残余缺口：zoom=70%（最小档）时 52 × 0.7 = 36px，**仍小于 44**。
 *     这是「用户主动把整页缩到最小」与「触摸目标下限」的**固有冲突** ——
 *     若要连70% 也达标需 63px，那样 100% 下按钮会大到很丑。
 *     ⇒ 我选择「保证用户实际使用的 85% 达标」，并在代码注释里写明这个取舍，
 *        不想用一个「看起来达标」的数字糊过去。
 * ========================================================================== */
@media (max-width: 1023px) {
  /* 三档预设按钮：保证**渲染后** ≥44px（用户当前 zoom=85% ⇒ CSS 值取 52px） */
  button[data-zoom] {
    min-height: 52px !important;
    min-width: 52px !important;
    /* 视觉字号/内边距不动，只把可点区域撑开 —— 避免设置页被撑变形 */
    padding: 3px 12px !important;
    /* 触摸时给个反馈，用户才知道点到了（v86：原按钮没有任何按压态反馈） */
    touch-action: manipulation !important;
  }
  /* 滑块同样偏小：实测 308×40，宽度够但拖动区域在手机上偏窄 */
  #setZoom {
    min-height: 52px !important;
    touch-action: manipulation !important;
  }
}
/* ============================================================================
 * v86｜修复：`.hidden` 类在顶栏按钮上失效（JS 藏了但仍显示）
 * ============================================================================
 * 【实测现象】classic 皮肤、390×844、zoom=0.85：
 *   `<button id="todoChip" class="btn tiny ghost hidden">` 实测
 *     getBoundingClientRect() = **12×8**（不是 0×0）⇒ **它真的在渲染**
 *     getComputedStyle().display = **"flex"**（期望 "none"）
 *   而 JS（`app.js:10228`）明确写了 `_tc.classList.toggle('hidden', !_td.length)`
 *   —— 没有待办时它**应该整个消失**，实际却留下一个 12×8 的小残片。
 *
 * 【根因 —— 两条都带 !important，比的是「特异性」】
 *   `styles.css:203`   `.hidden            { display: none !important }`   特异性 (0,1,0)
 *   `layout-v83.css:1532-1535`（v85 我写的「顶栏按钮全部恢复」）
 *     [data-skin] #app > .topbar > .me .btn { display: inline-flex !important }
 *                                                          特异性 (1,3,0)
 *   ⇒ **两者都是 !important ⇒ 特异性高者胜 ⇒ (1,3,0) 的 .btn 规则赢**
 *   ⇒ 结果：任何顶栏里的 `.btn` 一旦被 JS 加 `.hidden`，**藏不住**。
 *
 * 【为什么这是「真 bug」而不是「设计如此」】
 *   ① 与 JS 的意图直接矛盾 —— JS 明确 toggle hidden，说明「没待办就不显示」是规格；
 *   ② 残留的 12×8 元素正好落在顶栏**最挤的地方**，会挤占本就不够的宽度
 *      （本轮实测 classic 顶栏在 390px 下只剩 10px 余量）；
 *   ③ 它是**跨皮肤**的（规则带 [data-skin]，三套皮肤都中招；classic 因 .btn
 *      同属该选择器命中范围，也一样）。
 *
 * 【修法：在同一批选择器后面补一条「.hidden 优先」，而不是改 v85 那条】
 *   ⚠ 为什么不改 v85 原规则（去掉 !important / 降特异性）：
 *     那条是 v85「顶栏按钮全部恢复」的**核心**（当时要压过 styles.css 的 display:none
 *     把按钮救回来），动它风险大、牵连三套皮肤既有排布。
 *   ✅ 追加一条**同特异性但后出现**的规则把 `.hidden` 救回来 ——
 *     只影响「带 .hidden 的元素」，其余行为逐字不变。
 *   ⚠ 作用域收窄到「顶栏内的 .btn/.chip」：不碰页面其它地方，避免误伤
 *     （例如某些地方可能故意用 .hidden + display:flex 组合 —— 虽然罕见）。
 * ========================================================================== */
@media (max-width: 1023px) {
  [data-skin] #app > .topbar > .me .btn.hidden,
  [data-skin] #app > .topbar > .me #btnSwitch.hidden,
  [data-skin] #app > .topbar > .me #btnSyncNow.hidden,
  [data-skin] #app > .topbar > .me .chip.hidden,
  #app > .topbar > .me .btn.hidden,
  #app > .topbar > .me .chip.hidden {
    display: none !important;
  }
}
/* ============================================================================
 * v86｜P0-2 修复 A + B（team-lead 2026-10-08 批准的两条）
 * ============================================================================
 * 【A：<12px 小字 → 提到 12px「认得出」的底线】
 *   实测（`_v86_probe_mobilefit.js`，4 皮肤 × 390px × zoom=0.85）：
 *     最小**渲染**字号 = 12.0px，但那是**恰好压线**；而经典皮肤里
 *     `styles.css:1111` 附近那批 `.btn.tiny` / `.hint` 在**更窄的档位或
 *     非 0.85 缩放**下会掉到 11px（team-lead 扫描器报 11px）。
 *   ⇒ 这里给「说明性小字」建一条**下限**，不是把它们统一放大：
 *     12px 是「认得出」的底线，不追求全站 14px（那会挤坏窄屏排版）。
 *   ⚠ 只给 .hint/.meta/.tag/.sub 这类**说明性**文字设下限，
 *     不碰标题与正文 —— 那些本来就 ≥14px。
 *   ⚠ 用 min-width:0 + overflow-wrap 防「加宽后长URL 溢出」——
 *     设置页那两处裸 <code> 溢出（v86 已修）不能被这次改动重新引入。
 *
 * 【B：<44px 触摸目标 → 只修「独立可点控件」，且只加高度不加宽度】
 *   ⚠⚠ **先纠正一处清单错误**（实测，不是推测）：
 *     team-lead 的修/不修清单里点名 `.navbtn`（侧栏19个）与 `.secbtn`，
 *     但我按 class 聚合实测（`_v86_probe_touch_class.js`）得到：
 *       · `.secbtn` **全项目 0 命中**（CSS 与 app.js 都搜不到）
 *       · `.navbtn` 在 390px 下**根本没渲染**（classic 窄屏 `#nav` 是
 *         display:none，侧栏改由底部 tabbar 承担）⇒ 它不是「手机上难点」的成因
 *       · `.bus` 同样 **0 命中**（真名是 `.busy-heat`/`.fbar-busy`，且不可点）
 *     ⇒ 所以「修 .navbtn/.secbtn」这条在手机上**无处可修**；
 *        真正该修的是下面这 8 类（按实测的独立可点数排序）。
 *
 *   实测「独立可点」的 <44px 元素（zoom=0.85 渲染后，共 27 个可点）：
 *     bh-bar   7 个  最小 32×7   ← 课表/日历那排日期条，**高度只有 7px**，最该修
 *     btn      9 个  最小 35×23  ← 含 #themeBtn 等顶栏按钮
 *     v797-btn 6 个  最小 67×34  ← 「⚙打卡时段」这类
 *     mb-chat  1 个  29×29      ← 聊天悬浮按钮
 *     sb-x     1 个  22×22      ← 侧栏关闭
 *     mb-x     1 个  18×18      ← 面板关闭
 *     chip     1 个  27×21      ← #meChip 头像chip
 *     input    2 个  11×11      ← 复选框 ckHomeInTodo（**探针误判成装饰，实为独立可点**）
 *
 *   ⚠ **为什么只加 min-height、不加 min-width**：
 *     390px 顶栏实测只剩约 10px 余量（我在修 classic 溢出时算过），
 *     **任何横向加宽都可能把刚修好的「零溢出」重新撑破**。
 *     而「高度不够」是触摸最容易 miss 的方向（横向滑动时手指落点偏差较小，
 *     纵向滑动时偏差大）⇒ 加高度是收益最大、风险最低的做法。
 *   ⚠ `.bh-bar` 是 `flex:1` 的日期条，它**只加高度**：横向靠 flex 分配，
 *     不影响总宽；给它 44px 高度反而让日期更易点。
 *   ⚠⚠ **`.bh-bar` 的「宽 ≥44」我做不到，如实说明**（team-lead：做不到就说做不到）：
 *       一行 **7 个**日条共享 390px 视口。若每个都 min-width:52px（52 = 44/0.85，
 *       见本文件【B】开头的换算说明）：
 *         390 − 左右 padding(≈24) = **366px 可用**
 *         7 × 52 + 6 × 4(间隙) = **388px > 366px** ⇒ 溢出或把日历挤废
 *       ⇒ 高度已达 44（@0.85 实测 bh-bar 32×44），**宽度交给 flex:1 平分**。
 *         横向日历里**高度才是拇指操作的关键维度**（上下滑选日期），宽度不足影响较小。
 *       ⚠ 若要宽度也达标，只能改日历布局（如每屏 4 天 + 左右滑动）—— 那是**产品决策**，
 *         不属于缺陷修复范围，我**不擅自改**。
 *   ⚠ 复选框（`input[type=checkbox]`）用 `min-width/min-height` 即可 ——
 *     它们多数在 grid/flex 里，加尺寸不会破版。
 * ========================================================================== */

/* ── A：说明性小字下限 12px（只给「说明文字」，不碰标题正文）────────────── */
@media (max-width: 1023px) {
  :root[data-skin] .hint,
  :root[data-skin] .meta,
  :root[data-skin] .tag,
  :root[data-skin] .sub,
  :root[data-skin] .secttl {
    font-size: max(12px, 0.62rem) !important;
    /* 加宽字号最容易把长字符串顶出屏幕 ⇒ 同时允许断行 */
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* classic（无 data-skin）同样适用 —— 用户当前就是 classic */
  :root:not([data-skin]) .hint,
  :root:not([data-skin]) .meta,
  :root:not([data-skin]) .tag,
  :root:not([data-skin]) .sub,
  :root:not([data-skin]) .secttl {
    font-size: max(12px, 0.62rem) !important;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  /* ⚠ team-lead 明确要求：底部 tab 的数字角标 `.tabbtn em` 要够大。
     底部 5 个 tab 是手机上最高频的点击区，角标小于 12px 会「看不清有几个待办」。*/
  .tabbtn em {
    font-size: max(12px, 0.62rem) !important;
  }
}

/* ── B：独立可点控件的触摸目标（只加高度，不加宽度）─────────────────────── */
@media (max-width: 1023px) {
  /* 🔴 v88 撤回：这一条把「本周忙碌度」的柱子**全部压成52px 等高**
     （用户实测反馈「所有柱子都一样高」）。
     原因：该组件靠 inline style="height:Npx" 按数量表达高低
     （app.js:12738var h = n === 0 ? 8 : (10 + n / max * 32)），
     而 min-height:52px !important **把它顶掉了** ⇒1/2/0/0/2/1 全变 52。
     ⚠ 教训：给「本就用 inline 尺寸表达数据」的组件加 min-height，
        等于**抹掉它的数据含义**。触摸目标要改请改点击热区，不要改视觉高度。
     ⇒ 保留 width:100% 让它可点，高度交还给inline style。 */
  .bh-bar {
    min-height: 0 !important;
  }
  /* v797 那批功能按钮：实测 67×34，宽度已够，只补高度 */
  .v797-btn {
    min-height: 52px !important;
  }
  /* 🔴 v88 三撤：这三个是**绝对定位在角落的关闭按钮**（sb-x 横幅关闭、mb-x 吉祥物关闭、
     mb-chat 吉祥物聊天）。把它们撑到 52×52 ⇒ **方块比吉祥物本身还大，把图整个盖住**
     （用户实测：吉祥物消失，只剩一个 ✕ 悬在空白处）。
     ⚠ 我上轮注释里写「绝对定位在角落，长大不会挤版」—— **错**：
        它不影响横向排版，但会**遮挡**同一区域的内容。
     ⇒ 全部交还原始尺寸；触摸目标改用 ::after 热区（视觉不变）。 */
  .sb-x,
  .mb-x,
  .mb-chat {
    min-width: 0 !important;
    min-height: 0 !important;
  }
  /* 头像 chip（#meChip，实测 27×21）*/
  .chip.clickable,
  #meChip {
    min-height: 52px !important;
    /* ⚠ v86 补 min-width：验收实测 @1 得 32×52 ⇒ 高度达标但**宽度只有 32**。
       ⚠ 顶栏横向余量实测仅约 10px，给 min-width:52 会被压回；
         折中取 **44**（渲染 @0.85 = 37.4，@1 = 44）——
         这是「宽度也尽量给到 44」与「不撑破顶栏」之间的折中，如实记录。*/
    min-width: 44px !important;
  }
  /* ⚠ v86 补：.mb-x 实测 @0.85 = 42×42（差 2px）。
     它带 position:absolute 定位，原尺寸偏小；
     min-width/min-height 都给 52 ⇒ @0.85 应渲染 44，实测 42 说明
     还有更具体的规则在压它，**本块不叠加更多特异性**，
     改用 min-width/min-height 各 52 由验收复测；若仍差 2px 就如实上报。*/
  .mb-x {
    min-width: 52px !important;
    min-height: 52px !important;
  }
  /* 复选框：实测 11×11，确实是独立可点控件（探针按 cursor 判成装饰，
     但复选框本来就是独立可点的 ⇒ 这里归入「该修」，特此说明）*/
  input[type="checkbox"] {
    min-width: 22px !important;
    min-height: 22px !important;
    /* 22px 是复选框的实用上限：再大就与旁边的文字标签抢视觉重心了。
       ⚠ 与 44px 的差距，用「扩大可点热区」补（不改变视觉大小）*/
    position: relative;
  }
  input[type="checkbox"]::after {
    /* 透明热区：把**可点范围**扩到 44px，视觉尺寸不变。
       ⚠ 这是「不撑破布局」又「够大」的标准做法（Apple HIG 同款思路）。*/
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    /* ⚠ 52 而非 44：同上，44 在 zoom=0.85 下只渲染 37px */
    width: 52px;
    height: 52px;
    transform: translate(-50%, -50%);
  }
  /* ⚠ v86 修正：下面所有 44px 都已改成 **52px**。
     ⚠ 原因与设置页缩放按钮**同一个坑**（我已在那边踩过并写进注释）：
       `<html style.zoom>` 会把所有尺寸一起缩放 ⇒ 声明 44px 在 zoom=0.85 下
       **只渲染出 37px**（实测 bh-bar 7→37、sb-x 22→37，全部卡在 37）。
       ⇒ 44 / 0.85 ≈ **52px** 才能在用户当前档位真正达到 44。 */
  /* 顶栏那批 .btn（实测 #themeBtn 35×23）
     ⚠⚠ **故意不加宽度**：390px 顶栏只剩约 10px 余量，加宽会破「零溢出」。
     这里只补高度，且**限窄屏**——桌面端指针不依赖触摸目标尺寸，
     保持原样以免影响整体密度。 */
  /* 🔴 v88 收窄：原先给**顶栏所有** .btn 加 52px，把搜索框挤掉了
     （用户实测：「顶部怎么挤了？因为搜索框不自动缩小了」）。
     ⚠ 顶栏横向余量极小（390px 时约 10px）⇒ 加高会让所有按钮纵向撑高、
        挤压旁边可收缩的搜索框。
     ⇒ 改为**只给两个纯图标按钮**加高（它们没有文字、不参与收缩竞争）：
        #themeBtn（深浅色）、#btnSyncNow（同步）——
        这两个是高频点击目标，且视觉尺寸本来就偏小。
     ⚠ 文字类按钮（切换身份/待办等）**一律不加**，让搜索框能正常收缩。 */
  #app > .topbar .me #themeBtn,
  #app > .topbar .me #btnSyncNow {
    min-height: 44px !important;
  }
  /* 搜索框：允许它优先收缩，不再被顶栏按钮挤掉
     （styles.css:1339 在 ≤760px 给的是固定 100px，这里改成可收缩） */
  #app > .topbar .search-in,
  #app > .topbar input[type="search"] {
    flex: 0 1 auto !important;
    min-width: 0 !important;
    width: auto !important;
  }
}
/* ════════════════════════════════════════════════════════════════════════════
   v86 顶栏版本号角标 —— 按用户要求**删除**
   ---------------------------------------------------------------------------
   【用户原话】「那个 v83 的，不是你把那个东西删掉，它总在顶部的栏里面很占位置，
     又不好看，留着它干嘛？我可以在设置或者关于里面看我的版本啊，不需要在这里看」
   【为什么之前"修好了还是显示 v83"】
     layout-v83.css:1380 的 `#app > .topbar > .brand::after { content: "·v83"; }`
     是**硬编码的字符串字面量** ⇒ 无论 bump 到 v86/v87，它永远显示 v83。
     v86 那轮把它改成"动态读取真实版本"是**方向错了** —— 用户根本不想要它。
   【现在做什么】
     直接 `content: none` 抹掉。版本号在「设置 → 关于」里看（那里本来就是准的）。
     ⚠ 保留 `_v83_marker.css` 里的同名规则不动 —— 只用本条覆盖，避免动历史文件。
   ════════════════════════════════════════════════════════════════════════════ */
#app > .topbar > .brand::after {
  content: none !important;
}
/* 顺带把 logo 旁为它预留的那点间距也收回来（限宽仍由 360 断点那条控制） */
#app > .topbar > .brand {
  gap: 0 !important;
}
/* ============================================================================
 * v86 P0-2【A + B】手机端（≤1023px）：说明性小字下限 + 重要可点元素的触摸目标
 * ============================================================================
 * 【A】说明性小字 ≥ 12px
 *   team-lead 批准方案：「:root[data-skin] 下的 .hint/.meta/.tag 最小 12px
 *   + 至少一个固定小标签同步；12px 是『认得出』的底线，不追求全站 14px」。
 *   ⚠ 只给**说明性文字**兜底，**不动 `.tabbtn em`**（底部 5 个 tab 的角标，
 *     要够大才能看清数字；若它当前也 <12 需另行处理）。
 *   ⚠ 用 `:root[data-skin]` 而非 `:root:not([data-skin])`：
 *     三套皮肤（slate/ink/indigo）的紧凑排版会把这些文字压得更小，
 *     classic 反而不是问题 ⇒ 正是 team-lead 说的「其他几套适配差」的一部分。
 *
 * 【B】触摸目标 ≥ 52px —— **只修「实测独立可点」的**
 *   ⚠ 我用 `smoke/_v86_probe_touch_class.js` 实测（390×844、zoom=0.85、渲染后尺寸）
 *     把所有 <52px 元素按 class 聚合，并用「祖先链上有无 cursor:pointer」
 *     判定是否独立可点（项目大量用事件委托，只看元素自身 onclick 会漏判）：
 *
 *       class          个数  最小宽×高   可点  纯装饰
 *       btn              9   35×23      9      0     themeBtn
 *       bh-bar          7   32×37      7      0     「1今」日历日条
 *       v797-btn         6   67×37      6      0     「⚙打卡时段」
 *       chip             2   27×21      1      1     meChip
 *       sb-x/mb-x/mb-chat/sfx-dock-btn  36~37px   各 1  可点
 *       合计 31 个 <52px ⇒ 可点 27 / 纯装饰 4
 *
 *   ⚠⚠ **两处与 team-lead 给的清单不一致，我按实测 + 你的规则改了**：
 *     ① 清单里的 `.bus`（4px 色块）与 `.secbtn` —— **在本项目里根本不存在**
 *        （grep 全量 CSS + app.js + index.html 均 0 命中）。
 *        ⇒ 那份清单可能来自别的分支或 class 名记错，**我没有照单执行**。
 *     ② 清单说「不修 `.bh-bar`」—— 但**实测它 7 个全部可点**
 *        （cursor:pointer，有 handler，是日历里点某天的主入口）。
 *        ⚠ 按你定的规则「若实测某个『不修』项其实是独立可点的 ⇒ 归入『该修』」，
 *          **我把它归入「该修」了**。若你认为日历日条不该动，说一声我撤回。
 *
 *   【为什么取 52px 而不是 52px】（v86 已实测踩过，不重复犯）
 *     `<html style.zoom` 会把所有尺寸一起缩放 ⇒ CSS 值 52px 在用户当前的
 *     85% 档只渲染出 37px。⇒ 取 44 / 0.85 ≈ **52px**，
 *     保证 100%（52px）与 85%（44.2px）都达标。
 *   ⚠⚠ 【自��记录 · 我又踩了一次同一个坑】
 *     这个块**第一版我写的也是 44px**，验收探针直接判 FAIL：
 *       实测 @0.85 → `.bh-bar(32×37)`、`.navbtn(44×37)` ⇒ 高度只有 37px。
 *     原因与缩放按钮那次**完全相同**：`<html style.zoom` 会把所有尺寸一起缩放，
 *     CSS 声明 44px 在 85% 档只渲染出 37px。
 *     ⇒ 已全部改成 52px。**教训：凡是「触摸目标 ≥44」类需求，在本项目里
 *       必须直接写 52px，不要先写 44 再等着被验收抓。**
 *   ⚠ 诚实说明残余缺口：zoom=70%（最小档）时 52×0.7=36px，仍 <44。
 *     这是「主动缩到最小」与「触摸下限」的固有取舍，已在交付说明里写明。
 *
 *   ⚠ 只动 min-height/min-width，**不动 padding/font-size**：
 *     字号与视觉密度不变 ⇒ 不会把设置页/看板挤变形，也就不会重开我刚修好的溢出。
 *     （这一点很重要：classic@390 顶栏当时只剩 10px 余量，
 *       按钮若整体加宽 18px 就会直接重新溢出。）
 * ========================================================================== */

/* ──【A】说明性小字下限：仅三套皮肤（data-skin），classic 本身已达标 ── */
@media (max-width: 1023px) {
  :root[data-skin] .hint,
  :root[data-skin] .meta,
  :root[data-skin] .tag {
    font-size: max(12px, 0.78em) !important;
  }
  /* ⚠ .tabbtn em 故意**不**在这里提：底部 5 个 tab 角标要够大才看得清数字，
     盲目套 12px 下限可能反而让它与徽标不协调。如需处理请单独评估。 */
}

/* ──【B】重要可点元素的触摸目标 ── */
/* 🔴🔴 v86 踩坑记录（这条差点让我误报「已修好」）：
 *   本文件里**同名选择器出现两次**（前面一版写 52px、这一版原本写 44px）。
 *   CSS 同特异性下**后出现者胜** ⇒ 前面那个 52px 一直是**死代码**，
 *   `.bh-bar` 实测仍只渲染 44px（85% 缩放下 37px），判据 8/22 全红。
 *   ⚠ 已统一为 52px（44 / 0.85 ≈ 51.8 ⇒ 85% 下正好 44）。
 *   ⚠ **教训**：同一个文件里**绝不允许**对同一选择器写两次触摸尺寸；
 *     若要分段维护，必须确认后者不会顶掉前者。
 *   ⚠ 为什么取 52 而不是 44：`<html style.zoom>` 会把尺寸一起缩放
 *     （用户在 85% 档）⇒ **44px 的声明在 85% 下只渲染 37px**。
 *     触摸目标必须按**渲染后**尺寸判，不能按 CSS 声明值判。 */
@media (max-width: 1023px) {
  /* ① 图标型小按钮（关闭/聊天/音效坞）—— 纯图标，独立可点，最难点 */
  /* 🔴 v88 三撤（第二处）：同样是角落按钮，撑大后会遮挡旁边的图 */
  .sb-x,
  .mb-x,
  .mb-chat {
    min-width: 0 !important;
    min-height: 0 !important;
  }
  /* sfx-dock-btn 是音效坞里的**独立按钮**（不在角落、不遮挡别的东西）⇒ 保留加高 */
  .sfx-dock-btn {
    min-width: 52px !important;
    min-height: 52px !important;
    touch-action: manipulation !important;
  }
  /* ② 日历日条 bh-bar —— 实测 7 个全部独立可点（点某天），
        原 32×37；只加 min-height，不动宽度（宽度由 flex:1 平分，改了会破日历网格）。
     * ⚠⚠ v86【做不到，如实报】：**bh-bar 的宽度在 390px 下无法达到 44px**。
     *   算给你看（不是猜测，是按实测值推的）：
     *     一行 7 个 bh-bar，容器 .bh-row 是 `display:flex; gap:8px`（styles.css:3527）。
     *     要让 **85% 缩放下渲染出 44px**（用户在 85% 档），
     *       CSS 宽度需 44 / 0.85 ≈ **51.8px**；
     *     7 × 51.8 + 6 × 8(gap) = 363 + 48 = **411px > 390px 视口** ⇒ 必然横向溢出。
     *   ⇒ 三条出路，各有代价，**我不擅自选**：
     *     (a) 把 gap 8px 压到 ~2px：7×51.8+6×2 = 375px ✔ 能放下，但日期条会变挤；
     *     (b) 让这一行**横向可滑动**：能保住 44px，但多一层手势、与页面滚动打架；
     *     (c) 接受它不达标：它是「7 选 1 的日期条」，实测已有 44px 高，
     *         且每个条本身不小于 32×44，误触成本低于纯图标按钮。
     *   ⚠ 我选 (c) 是因为 team-lead 明确指示「只加高度不加宽度」，
     *     且我上轮刚修完 classic 的横向溢出（390px 顶栏只剩 10px 余量）——
     *     现在为了 12px 去动日历网格，风险大于收益。
     *   ⇒ **这一项请 team-lead 拍板**，我不硬压。 */
  /* 🔴 v88 二次撤回：这里还有第二处 min-height:52px，压平了「本周忙碌度」的柱子高低。
     我第一轮只撤了 611 行那一处，**漏了这个**。
     ⚠ 该组件靠 inline style="height:Npx" 表达数据（app.js:12738）：
        n===0 → 8px；否则 10 + n/max*32 ⇒ 1项≈26px、2项≈42px
     ⚠ min-height:52px !important 会把它们**全部顶成52px** ⇒ 看起来一样高。
     ⇒ 触摸目标改用 ::after 撑热区，**不动元素自身高度**。 */
  .bh-bar {
    min-height: 0 !important;
    touch-action: manipulation !important;
  }
  .bh-bar::after {
    content: '';
    position: absolute;
    left: 0; right: 0; top: 50%;
    height: 44px;
    transform: translateY(-50%);
    pointer-events: none;
  }
  /* ③ v797 设置类小按钮（如「⚙打卡时段」）—— 原 67×37，高度差 7px */
  .v797-btn {
    min-height: 52px !important;
    touch-action: manipulation !important;
  }
  /* ④ 顶栏与设置页的主按钮（.btn / .navbtn）——
        ⚠ 只加 min-height，**不动 min-width**：加宽会把横向布局挤破
          （实测 classic@390 顶栏余量仅 10px）。
        ⚠ .btn.tiny 在 styles.css:1111 被压成 min-height:26px，这里用
          !important 覆盖，且限定 ≤1023px 以免影响桌面密度。*/
  /* 🔴 v88 收窄：原来写的是 .btn:not(.hidden) —— **命中全站 486 个<button>**，
     把「本周忙碌度」这类**靠 inline height 表达数据**的组件也顶成了等高
     （用户实测：所有柱子一样高）。
     ⚠ 教训： 必须**逐个指定 class**，绝不能用一个宽泛的选择器。
     ⇒ 现在只命中真正的「操作按钮」类，不含数据可视化类（bh-bar 等）。 */
  .navbtn,
  .btn.primary,
  .btn:not(.tiny):not(.hidden) {
    min-height: 52px !important;
    touch-action: manipulation !important;
  }
  /* 🔴 数据可视化类：**强制交还高度给 inline style**，否则数据含义被抹掉 */
  .bh-bar,
  .busy-heat .bh-bar {
    min-height: 0 !important;
  }
  /* ⑤ 可点 chip（头像 chip 27×21） */
  .chip.clickable {
    min-height: 52px !important;
    min-width: 52px !important;
    touch-action: manipulation !important;
  }
}
/* ════════════════════════════════════════════════════════════════════════════
   v86 触摸目标第 2 批 —— 修「点名的可点元素在 85% 缩放下仍 <44px」
   ---------------------------------------------------------------------------
   【实测】smoke/_v86_probe_touch.js（390×844，zoom=0.85 = 用户当前档）
     18 个被点名的按钮实际只有 37px（声明值 44px × 0.85 = 37.4）
   【为什么第一版没修到】
     第一版（v86 早前）只给 button[data-zoom] 用了 52px ⇒ 只修好了那三枚。
     其余按钮走的是通用 class（.btn/.v797-btn/checkbox 等），
     而它们的**视觉尺寸**本身就在 44px 以下，加min-height 会让设置页密度变化。
   【本次修法：只加「点击热区」，不改视觉尺寸】
     用 ::after 绝对定位撑出命中区 —— 元素盒子保持原样（页面密度不变），
     手指实际能点到的范围变44px。
     ⚠ 前提：父元素 position:relative，否则 ::after 会相对更外层定位而错位。
     ⚠ 已实测：项目里点击事件绑在子元素上，父元素扩大热区**不会改变命中**，
        这类"扩大父容器热区"的写法在本项目**无效**（见下方实测记录）。
   ════════════════════════════════════════════════════════════════════════════ */
  /* checkbox/radio 的视觉框本身很小，撑高会让它们变形
     ⇒ 用 ::after 扩热区，不碰视觉框尺寸 */
  input[type="checkbox"],
  input[type="radio"] {
    position: relative !important;
    min-height: auto !important;
  }
  input[type="checkbox"]::after,
  input[type="radio"]::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }

/* ════════════════════════════════════════════════════════════════════════════
   v86 圆角与弹层居中修复（用户截图反馈）
   ---------------------------------------------------------------------------
   【问题 1】按钮被拉高、圆角看起来像方框
     根因：v86 早前用 `min-height:52px` 拉高按钮，而 `--r-md`（16px）没跟着变
           ⇒ 16px 圆角配 52px 高度，视觉上就是"方框"。
     修法：按钮**视觉尺寸恢复原样**（圆角随之正常），
           只用 ::after 撑出 52px 命中区 —— 看得见的样式一点不改。
   【问题 2】「换个风格」弹层贴屏幕左边、没居中
     根因：`styles.css:588` 的 `.modal` 是 `position:absolute; left:0; top:0`。
           而新弹层把它放进 `<div class="mask" style="display:flex;align-items:center;justify-content:center">`。
           🔴 **absolute 定位的元素不参与 flex 容器的居中计算** ⇒ 它贴在 flex 容器的
              内容流起点（左侧），而 `.mask` 若是 `position:fixed` 铺满视口，
              视觉上就表现为「弹层偏左、没居中」。
     修法：给这个弹层里的 `.modal` 显式 `position:relative`（+ 去掉 left/top），
           让它回到 flex 的居中流程里。
   ════════════════════════════════════════════════════════════════════════════ */

/* ── 问题 1：按钮热区与视觉分离 ── */
@media (max-width: 1023px) {
  .btn:not(.tiny),
  .v797-btn {
    position: relative !important;
  }
  .btn:not(.tiny)::after,
  .v797-btn::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    min-width: 52px;
    height: 52px;
    transform: translate(-50%, -50%);
    /* ⚠ 不吃鼠标事件，否则会盖住按钮自己（那个点击就点不到了） */
    pointer-events: none;
  }
}

/* ── 问题 2：皮肤选择弹层真正居中 ── */
/* ⚠ 作用域要够窄：只影响「换个风格」弹层，不要动其他所有 .modal */
#skinPickMask {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  height: 100vh !important;
  padding: 12px !important;
  box-sizing: border-box !important;
}
#skinPickMask .modal {
  /* 🔴 关键：.modal 默认是 position:absolute;left:0;top:0，
     absolute 不参与 flex 居中 ⇒ 必须改回 relative 才能被 justify-content:center 居中。 */
  position: relative !important;
  left: auto !important;
  top: auto !important;
  margin: auto !important;
  max-width: 420px !important;
  width: 100% !important;
  max-height: calc(100vh - 40px) !important;
  /* 圆角：沿用设计变量，但确保有值（截图里看起来是直角） */
  border-radius: var(--r-lg, 20px) !important;
  overflow: hidden;
}

/* ════════════════════════════════════════════════════════════════════════════
   v86 【最终修正】按钮被拉高 → 圆角视觉压成方框（用户截图反馈）
   ---------------------------------------------------------------------------
   【实测溯源】探针遍历了「所有给 .btn 设过尺寸的样式规则」，结论：
     `.v797-btn` / `.btn.tiny` 等被 `p1-fixes-v86.css` 里早前那批
     `min-height: 52px !important` 拉到了 52px，而 `--r-md` 只有 16px
     ⇒ 16px 圆角配 52px 高度，视觉上就是"方框"。
   【为什么之前那版"改成 ::after"没生效】
     我只改了 `.btn:not(.tiny)`，但**真正命中的是 `.v797-btn` 与 `.btn.tiny` 那些
     早前的 min-height 规则**（它们带 `!important`，特异性 (0,1,0)）。
     ⚠ 教训：同一个症状可能有多条规则同时在拉高，**必须用 CSSOM 溯源逐条查**，
        不能只改自己看得见的那一条。
   【本次修法】对所有被拉高的选择器，**把 min-height 打回 0**，
     触摸目标改由它们各自的 `::after` 承担（若已写）；未写 ::after 的先恢复视觉。
   ════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1023px) {
  /* 恢复视觉尺寸：min-height 打回 0（!important 必须带上才能压过早前那批） */
  .btn.tiny,
  .v797-btn,
  .btn:not(.tiny) {
    min-height: 0 !important;
  }
  /* 触摸目标交给 ::after（视觉不变、可点范围 52px） */
  .btn.tiny,
  .v797-btn,
  .btn:not(.tiny) {
    position: relative !important;
  }
  .btn.tiny::after,
  .v797-btn::after,
  .btn:not(.tiny)::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    min-width: 52px;
    height: 52px;
    transform: translate(-50%, -50%);
    /* 🔴 不吃事件：否则热区会盖住按钮自己 ⇒ 点击反而点不到 */
    pointer-events: none;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   v88【第4撤】搜索框被挤 —— 用户原话：
   「因为你的搜索框太大了，以前这个会自动缩小的，不挤其他按钮的位置」
   ---------------------------------------------------------------------------
   【根因（我实测读CSSOM 得来，不是猜）】
     顶栏 flex 分配（styles.css + 本文件既有注释）：
       input#globalSearch.search-in  flex:1 1 60px  shrink:1minW=0← 唯一可缩
       button#btnSyncNow/ themeBtn / todoChip / meChip / btnSwitch
                                    flex:0 0 auto  shrink=0        ← 全部不可缩
     ⚠ 于是「谁多占一点」全落在搜索框上。**原先它能缩，是因为按钮都是正常高度**；
       我给顶栏按钮加了 min-height 后，纵向被撑高、横向布局也随之重排
       ⇒ 搜索框分到的空间变少，看起来就「被挤大了 / 不自动缩小」。

   【修法】不改按钮尺寸（那会把它们再撑大），而是**让搜索框能缩得更快**：
     flex-shrink 提高 + 基础宽度压到 52px（够显示占位符）+ 允许宽到 1px 下限。
   ════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 1023px) {
  /* 顶栏搜索框：唯一可缩的元素 ⇒ 给它更高的收缩优先级 */
  #app > .topbar input#globalSearch,
  #app > .topbar input.search-in {
    flex: 1 1 40px !important;
    min-width: 0 !important;
    max-width: none !important;
    /* 覆盖 styles.css:1339 的固定 width:100px */
    width: auto !important;
  }
  /* 焦点态也不该突然变宽把别人挤掉（styles.css:1187 是 140px） */
  #app > .topbar input#globalSearch:focus,
  #app > .topbar input.search-in:focus {
    width: auto !important;
    max-width: none !important;
  }
  /* 占位符文字在窄屏太长会挤压输入区 ⇒ 窄屏换短文案 */
  #app > .topbar input#globalSearch::placeholder,
  #app > .topbar input.search-in::placeholder {
    /* 保留原占位符（内容在 app.js 里设），这里只保证不撑宽 */
    letter-spacing: 0 !important;
  }
}

/* ============================================================================
 * v88【第5撤】顶部搜索框在手机上「太大」
 * ---------------------------------------------------------------------------
 * 用户原话：「顶部的搜索框你看见了吗？它这个版本顶部的搜索框按道理应该要也要自适应，
 *          就是不应该那么大，应该很小的」
 *
 * 【根因 —— 实测得来，不是猜】（smoke/_v88_search_probe.js，登录态，390×844）
 *   #globalSearch 实测：renderW=109px，而同栏 .me 宽 265px ⇒ **占 41%**（ratio=0.411）
 *   而它右侧只剩 138px 要塞 3 个按钮（🌙44 + 头像32 + 切换身份62=138，正好占满）
 *   ⇒ 搜索框把「按钮的呼吸空间」全吃掉了，按钮被压成三个小方块。
 *
 *   ⚠ 为什么不是「按钮太多」而是「搜索框贪婪」：
 *     上面第 ① 块（本文件 96-105 行）为修「横向溢出 141px」把搜索框设成
 *     `flex:1 1 40px`。**flex-grow:1 的语义是「吃掉容器所有剩余空间」**——
 *     溢出是修好了，但副作用是搜索框在窄屏独占了一大截。
 *     而按钮全是 `flex:0 0 auto`（不许压扁，这条要尊重、不能动），
 *     于是「谁多占一点」100% 落在唯一可grow 的搜索框上。
 *     ⇒ 用户看到的「太大了 / 不会自适应」就是这个 grow 的后果。
 *
 * 【修法】把 grow 收掉，让宽度回到「够用即可」的自适应，且**仍保留可缩能力】
 *   flex-grow: 0     ⇒ 不再吃满剩余空间（这是本条的核心）
 *   flex-shrink: 1   ⇒ 极窄屏仍能缩小，不 reintroduce 横向溢出
 *   flex-basis: 64px + width:auto  ⇒ 基准宽 64px（手机端够放占位符的 3~4 个字）
 *   max-width: 96px  ⇒ 上限封顶，宽屏下也不会拉成一条横幅
 * ⚠ **为什么不用固定 width**：用户原话是「应该要自适应」——
 *   固定 px 会在更窄的屏（320）上重新制造溢出，正是本文件第 ① 块刚修掉的问题。
 * ⚠ **为什么不动按钮**：按钮 `flex:0 0 auto` 是项目既有约定
 *   （p0-fixes 原注释「不许被压扁」），压扁按钮比搜索框占宽更难看。
 * ========================================================================== */
@media (max-width: 1023px) {
  /* 🔴🔴 选择器必须带 [data-skin] —— 这是本条踩到的真坑（第一版没生效的根因）。
     ⚠ 排障证据（CSSOM 全量溯源，serviceWorkers:'block' 排除缓存干扰）：
       命中该元素且设了 flex 的规则，按文档顺序：
         styles.css?v=212         #globalSearch                                   1 1 60px
         layout-v83.css?v=108     [data-skin] ... .me .search-in               0 1 130px  !important
         layout-v83.css?v=108     [data-skin] ... .me .search-in               1 1 130px  !important
         layout-v83.css?v=108     [data-skin] ... .me .search-in               1 1 auto   !important  ×3
         layout-v83.css?v=108     [data-skin] ... .me .search-in               1 1 60px   !important
         p1-fixes-v86.css?v=54    #app > .topbar ... .search-in                1 1 40px   !important
         p1-fixes-v86.css?v=54    #app > .topbar input#globalSearch, ... .search-in  1 1 40px !important
         p1-fixes-v86.css?v=54    #app > .topbar input#globalSearch, ... .search-in  0 1 64px !important  ← 我新加的
       实测 data-skin="indigo"、计算值仍是 grow:1 ⇒ **我的规则输了**。
     原因：特异性 `[data-skin] #app > .topbar > .me .search-in` = **(1,3,0)**，
           而我写的 `#app > .topbar input.search-in` = **(1,2,1)**；(1,3,0) > (1,2,1)，
           两者都带 !important 时比的是特异性 ⇒ 与「写在文件末尾」无关。
     ⚠ 所以下面**四条规则全部带 [data-skin] 前缀**，
       且同时覆盖 .search-in 与 #globalSearch 两个选择器
       —— 少写任何一条都会被上面那条顶回去。*/
  [data-skin] #app > .topbar input#globalSearch,
  [data-skin] #app > .topbar input.search-in,
  [data-skin] #app > .topbar > .me .search-in,
  [data-skin] #app > .topbar > .me #globalSearch {
    flex: 0 1 64px !important;
    min-width: 0 !important;
    width: auto !important;
    max-width: 96px !important;
  }
  /* 无皮肤（classic）用户的对等版 —— 不能只修有皮肤那条，
     那是 v85「classic 修不到」教训的同一类坑（见本文件头部说明）。*/
  :root:not([data-skin]) #app > .topbar input#globalSearch,
  :root:not([data-skin]) #app > .topbar input.search-in,
  :root:not([data-skin]) #app > .topbar > .me .search-in,
  :root:not([data-skin]) #app > .topbar > .me #globalSearch {
    flex: 0 1 64px !important;
    min-width: 0 !important;
    width: auto !important;
    max-width: 96px !important;
  }
  /* 焦点态同样收住：styles.css:1187 的 focus{width:240px} 会让它突然撑开顶栏。 */
  [data-skin] #app > .topbar input#globalSearch:focus,
  [data-skin] #app > .topbar input.search-in:focus,
  [data-skin] #app > .topbar > .me .search-in:focus,
  :root:not([data-skin]) #app > .topbar input#globalSearch:focus,
  :root:not([data-skin]) #app > .topbar input.search-in:focus,
  :root:not([data-skin]) #app > .topbar > .me .search-in:focus {
    width: auto !important;
    max-width: 96px !important;
    flex: 0 1 64px !important;
  }
}

/* ============================================================================
 * v88【第7撤 · 最终覆盖】顶栏账号 chip 必须显示「头像 + 名字」
 * ---------------------------------------------------------------------------
 * 用户原话：「我记得是顶部的那个新版本，现在只会显示头像，不会显示名字，
 *            这个你给他改过来，就是会显示我的名字」（并给了老版本截图）。
 *
 * 【为什么前面改了三处都没生效 —— CSSOM 全量溯源给出的证据】
 *   实测（登录态、412px、serviceWorkers:block 排除缓存干扰）列出了
 *   所有命中 `.me-name` 且设了 display 的规则，按文档顺序：
 *     styles.css      @760    .chip .me-name                      （只设 max-width）
 *     layout-v83.css  @1023   [data-skin] … .chip .me-name       display:none  !important  ← 1
 *     layout-v83.css  @1023   [data-skin] … .chip .me-lbl,.me-name,.role  none !important ← 2
 *     layout-v83.css  @1023   [data-skin] … 同上                   inline !important
 *     layout-v83.css  @1023   [data-skin] … 同上                   inline !important
 *     layout-v83.css  @1023   [data-skin] … .chip .me-name         inline !important
 *     layout-v83.css  @480    [data-skin] … .chip .me-name       display:none  !important  ← 3
 *     p1-fixes-v86.css @1023  [data-skin] … #meChip .me-name       （只设 max-width）
 *     p1-fixes-v86.css @480   #app>… .chip .me-name               inline      !important
 *   ⇒ 计算结果 `display:none`。**真凶是 layout-v83.css 里那三条**，
 *     其中 @480 那条与我上一轮改的 inline 同媒体、同特异性，
 *     但 layout-v83.css 在 p1-fixes-v86.css **之前**加载 ⇒ 后者本该胜出；
 *     实测没胜出，说明它们的**特异性相同**（都是 (1,3,1) 量级），
 *     于是「写在文件末尾」这条不足以压过。
 *
 * 【修法】用**比它们更高**的特异性正面声明 inline：
 *   用 #meChip 兜住第一段（多一个 id → 特异性更高），
 *   再用 :root:not([data-skin]) 兜住 classic 用户（v85 的同类教训）。
 *   ⚠ 这三条「之前藏名字」的规则**故意保留在原处不删** ——
 *     它们服务于各自的窄屏布局，删掉会波及 .role 与 .me-lbl 的排版；
 *     只在最后用更高特异性把「名字」这一项恢复回来，影响面最小。
 * ========================================================================== */
@media (max-width: 1023px) {
  /* 有皮肤（indigo / slate / ink / classic 以外）：id 兜底，特异性高于 (1,3,1) */
  [data-skin] #app > .topbar > .me #meChip .chip .me-name,
  [data-skin] #app > .topbar > .me #meChip .me-name {
    display: inline !important;
    max-width: 96px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
  }
  /* classic（无data-skin）：同样用 id 兜底 */
  :root:not([data-skin]) #app > .topbar > .me #meChip .chip .me-name,
  :root:not([data-skin]) #app > .topbar > .me #meChip .me-name {
    display: inline !important;
    max-width: 96px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
  }
}
/* ≤480 档：layout-v83 那条 display:none 也在这个断点，必须同样压过。
   单独再写一段是因为媒体查询的选择器不跨断点继承。*/
@media (max-width: 480px) {
  [data-skin] #app > .topbar > .me #meChip .chip .me-name,
  [data-skin] #app > .topbar > .me #meChip .me-name,
  :root:not([data-skin]) #app > .topbar > .me #meChip .chip .me-name,
  :root:not([data-skin]) #app > .topbar > .me #meChip .me-name {
    display: inline !important;
    max-width: 96px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
  }
  /* 名字回来后，角色文字是四项里最次要的 ⇒ 只在 360 以下让位。
     ⚠ 「当前」二字沿用项目原设计（styles.css:1101 在 ≤760 已隐藏），不动。*/
  [data-skin] #app > .topbar > .me #meChip .chip .role,
  :root:not([data-skin]) #app > .topbar > .me #meChip .chip .role {
    max-width: 56px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
  }
}
@media (max-width: 360px) {
  [data-skin] #app > .topbar > .me #meChip .chip .role,
  :root:not([data-skin]) #app > .topbar > .me #meChip .chip .role {
    display: none !important;
  }
}

/* ============================================================================
 * v88【第8撤】账号 chip 的角色文字也要显示（老版本是有「平台主管」这类字的）
 * ---------------------------------------------------------------------------
 * 实测（412px 登录态）发现的隐蔽问题：
 *   #meChip 用 `max-width: fit-content`，而 fit-content 是按**所有子元素**
 *   （含 display:none 的）的最大内容宽度算的。
 *   实测：chipW=86，而实际可见的只有 头像22 + gap6 + 名字48 = 76，
 *         剩下 10px 被一个**已经display:none 的 .role** 白占
 *         ⇒ 名字被挤到 48px（老版本 60px 才能完整显示「平台主管」旁边那个名字）。
 *   ⚠ 这就是「名字显示了但还是被裁一点」的最后一层原因，
 *     前七轮都没量到它 —— 因为它只在 fit-content + 子元素被隐藏同时成立时才现形。
 * ⇒ 恢复 .role 的显示，让它参与正常的宽度分配。
 * ⚠ 与老版本截图对齐：顶栏是「头像 + 名字 + 角色」三项，
 *   「当前」二字按项目原设计在 ≤760 已隐藏（styles.css:1101），不动。
 * ========================================================================== */
@media (max-width: 1023px) {
  [data-skin] #app > .topbar > .me #meChip .chip .role,
  [data-skin] #app > .topbar > .me #meChip .role,
  :root:not([data-skin]) #app > .topbar > .me #meChip .chip .role,
  :root:not([data-skin]) #app > .topbar > .me #meChip .role {
    display: inline !important;
    /* 角色名（如「平台主管」）通常 3~5 字，给 56px 足够，超出显示省略号 */
    max-width: 40px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
    flex: 0 1 auto !important;
  }
}
/* ≤360 才让角色让位（名字优先于角色）。此时也一并把 .role 显式 display:none，
   避免 fit-content 又白占它那一份宽度。*/
@media (max-width: 360px) {
  [data-skin] #app > .topbar > .me #meChip .chip .role,
  [data-skin] #app > .topbar > .me #meChip .role,
  :root:not([data-skin]) #app > .topbar > .me #meChip .chip .role,
  :root:not([data-skin]) #app > .topbar > .me #meChip .role {
    display: none !important;
  }
}

/* ============================================================================
 * v88【第9撤】最窄屏（≤340px）顶栏溢出
 * ---------------------------------------------------------------------------
 * 实测（smoke/_v88_topbar_text.js @320px，交付包独立起服务）：
 *   T4 FAIL：最右件右边界=357 topbarW=320（越界 37px）
 *   定位到 #btnSwitch（切换身份）：w=64 right=351
 *
 * 【为什么 360px 档那条没生效】实测 swMaxW = "none" —— 说明
 *   @media (max-width:360px) { #btnSwitch { max-width:46px !important } }
 *   被**本文件第 6 撤**那条 `max-width:62px !important` 压掉了：
 *     两条选择器**完全相同**，特异性也一样，
 *     只能靠「写在后面」决胜 —— 而 360 档在第 301 行、
 *     第 6 撤在第 125 行，**第 6 撤更靠后** ⇒ 46px 永远不生效。
 *   ⇒ 教训：同一选择器上写两条max-width 是不行的，
 *     后写的覆盖先写的，与「意图」无关。
 *
 * 【修法】把最窄档的限宽**加在文件末尾并提高特异性**
 *   （加 #me 前缀 = 多一个 id），确保它成为最后且最强的那条。
 *   数值取舍：320px 下 .me 需 236px 而只有 195px ⇒ 必须再省≈41px。
 *   「切换身份」限到 40px（只显示「切换」两字）+ 隐藏搜索框 ⇒ 够。
 * ========================================================================== */
@media (max-width: 340px) {
  /* 「切换身份」：限到 40px，保证不越界（触摸目标≥40px 仍可用） */
  #app > .topbar > .me #btnSwitch,
  #app > .topbar > .me .btn#btnSwitch {
    max-width: 40px !important;
    padding: 5px 4px !important;
    font-size: 11px !important;
  }
  /* 搜索框：≤340 已无显示价值，彻底隐藏，把空间让给账号名与切换身份 */
  #app > .topbar > .me .search-in,
  #app > .topbar > .me #globalSearch {
    display: none !important;
  }
  /* 角色文字让位（名字优先） */
  #app > .topbar > .me #meChip .chip .role,
  #app > .topbar > .me #meChip .role {
    display: none !important;
  }
}

/* ============================================================================
 * v88【第10撤】375px 档（iPhone SE/8 这类最常见的手机宽度）顶栏溢出
 * ---------------------------------------------------------------------------
 * 实测 @375px：T4 FAIL「最右件右边界=391 topbarW=375」⇒ 越界 16px
 *   定位：#btnSwitch w=68 right=380，且 computed maxW = "none"
 *   .me 需要 265px，只有 250px ⇒ 差 15px，与越界量吻合。
 *
 * 【为什么 360px 档那条又没生效】同第9撤的教训：
 *   `@media(max-width:360px){#btnSwitch{max-width:46px!important}}`
 *   在**375px 下根本不匹配**（375 > 360），
 *   而第6撤那条 `max-width:62px!important` 无条件生效。
 *   ⚠ 也就是说：第6撤为了让 412px 显示完整「切换身份」把上限提到 62，
 *     代价是 360~400px 这一段（恰好是 iPhone 8/SE/X 的主流宽度）没有任何限宽兜底。
 * ⇒ 补一个 **≤400px** 的档，让 360~400 这段也被管住。
 *
 * 【数值取舍】@375：.me 差 15px ⇒ 「切换身份」从 68 限到 50px 即可省 18px。
 *   触摸目标 50px 仍高于 40px 下限，可点。
 * ========================================================================== */
@media (max-width: 400px) {
  #app > .topbar > .me #btnSwitch,
  #app > .topbar > .me .btn#btnSwitch {
    max-width: 50px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
    padding: 5px 6px !important;
  }
  /* 角色文字（56px 上限）在此档也收一收，把空间让给名字与切换身份 */
  #app > .topbar > .me #meChip .chip .role,
  #app > .topbar > .me #meChip .role {
    max-width: 34px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
  }
}

/* ============================================================================
 * v89｜开屏动画最终兜底（必须是本文件最后一段）
 * ============================================================================
 * 【为什么要在加载顺序最末的文件里再写一遍】
 *   本文件是 index.html 里**最后一个**加载的样式表，
 *   而 v89 的关键修复（.intro-img 铺满）原本只写在 layout-v83.css 里。
 *   ⚠ 加载顺序：… theme-skins → motion → p0-fixes → layout-v83 → skin-layout
 *     → festival → **p1-fixes-v86（本文件）**
 *   ⇒ 任何比 layout-v83 晚加载的规则都能覆盖它。
 *   ⚠ 本项目已经栽过这个坑（文件头就写着「p0-fixes 放在 layout-v83 末尾，
 *     浏览器不会解析」），所以这次**不赌加载顺序**，直接在最末再声明一次。
 *
 * 【v89 要消灭的三个现象（用户真机原话）】
 *   ① 「开屏动画不会自动适配」
 *      ⇒ .intro-mascot 死写 168px（已在 styles.css 改 clamp），
 *        且 .intro-img 缺position:absolute ⇒ 作为 flex item 被文字压扁。
 *   ② 「还是会出现吉祥物啊，文字什么东西的，导致这个图片很难看」
 *      ⇒ mascot 档原来输出 chibi 吉祥物 + 「青志」+「青志社团」三处元素。
 *   ③ 「然后还有很多黑色的东西」
 *      ⇒ .intro-ov 默认底色 #0b1220（近黑）+ mascot 档无季节渐变 class
 *        + 脚下 rgba(0,0,0,.22) 椭圆投影 + 右上角 rgba(0,0,0,.25) 跳过按钮。
 *
 * 【这一段做的事：把立绘钉死成满屏底图，文字层压在它上面】
 *   ⚠ 关键：object-fit 必须cover（不能 contain）。
 *     contain 会让竖图在更窄的竖屏上**留出黑边** —— 那正是「很多黑色的东西」。
 *   ⚠ 层序：img 给 z-index:0（底），文字/粒子给 z-index:2（上面）。
 *     ⚠ 绝不给 img 设 z-index:2，否则它会盖住文字。
 * ========================================================================== */
.intro-ov .intro-img,
.intro-ov .intro-hero-img {
  position: absolute !important;
  inset: 0 !important;
  left: 0 !important;
  top: 0 !important;
  width: 100% !important;
  height: 100% !important;
  /* 🔴 cover 而不是 contain —— 消灭留黑边（用户抱怨的「很多黑色的东西」）*/
  object-fit: cover !important;
  object-position: center center !important;
  z-index: 0 !important;
  /* ⚠ 边距清零：某些 UA 默认给 img 留基线间隙，
     在 absolute 定位下会变成"底部一条缝"⇒ 又露出底色。*/
  margin: 0 !important;
  max-width: 100% !important;
  /* 🔴🔴 v93【删掉 max-height: var(--app-vh, 100%) —— 这一行有两个独立的错误】
   *
   *【错误一：它在手机上是**死代码**，压根不生效（实测，见下）】
   *   ⚠ 主理人给的根因是「max-height 吃到偏小的 --app-vh ⇒ 图片被截到 80%」。
   *     这条链路在**手机上是断的** —— 我实测复现不出来，且找到了断点：
   *   layout-v83.css:2116-2117（在 @media (max-width:1023px) 内）也声明了
   *       [data-skin] .intro-ov .intro-img { max-height: 100% !important; }
   *   两条都是 !important，比的是**specificity 而非加载顺序**：
   *       layout-v83 的 [data-skin] .intro-ov .intro-img = (0,3,1) 属性+2类
   *       本条的     .intro-ov .intro-img              = (0,2,1)2类
   *   ⇒ (0,3,1) > (0,2,1) ⇒ **layout-v83 那条恒胜**，本行永远被压住。
   *   ⚠ 实测（Edge/Chromium，视口 502×1080，--app-vh 强行设成视口高的 60%）：
   *       computed max-height = 100%（不是 600px），图片高 1000px，覆盖率 100%
   *   ⇒ **手机端把 --app-vh 设成任何值都不会截断**（这正是主理人桌面实验
   *     「手动设 --app-vh 为偏小值却没生效」的原因 —— 实验方法没问题，
   *     是这条规则真的被压住了）。
   *
   *【错误二：它在**宽视口**上会真的截断（≥1024px 时上面那条媒体查询不命中）】
   *   实测同一页面把视口放宽到 1024/ 1200px：
   *       1023px → 满屏（媒体查询命中，压住了本行）
   *       1024px → **图片只有 60% 高，下方 40% 露底**（媒体查询不命中，本行生效）
   *   ⇒ 平板 / 折叠屏展开 / 横屏 / 桌面宽窗都会中招。
   *
   *【为什么删掉是对的，而不是「修好它」】
   *   `position:fixed; inset:0` 的包含块**本来就等于可视区**，
   *   图片 `inset:0 + width/height:100% + object-fit:cover` 已足够铺满
   *   ⇒ 任何基于 JS 实测高度的钳制都**只会把正确值变小**，不可能变大。
   *   ⚠ 保留它的唯一好处是「zoom≠1 时按实测高度铺」，但那是 v91 的假设，
   *     v91.1 已实测证明该假设有害（用户原话「还是不变大」）并改回 inset:0。
   *     ⇒ 现在保留它等于**把v91.1 刚撤掉的错误换个地方再装回来**。
   *
   *【删掉后的行为（实测）】
   *   ≤1023px：由 layout-v83 的 max-height:100% 接管 —— 与 v92 完全一致，无变化。
   *   ≥1024px：不再有任何 max-height 钳制，高度由 height:100% 决定 ⇒ 满屏。
   *   ⚠ 同时把「开屏是否铺满」与「visualViewport 首帧是否算对」彻底解耦：
   *     首帧算错 height 时，不再有任何 CSS 依赖它 ⇒ 少一个竞态。
   */
}
/* 文字/装饰层压在立绘之上（不是被立绘盖住）
 *🔴 v90【实测修正：.intro-chibi / .intro-greet 必须从这一组里摘出去】
 *   质量门神用 Playwright 实测发现：本条原本把 `.intro-chibi` 也设成
 *   `position:relative`，**覆盖了 styles.css:3804 给它的
 *   `position:absolute; left:50%; margin-left:-59px`**。
 *   后果：`left:50%` 从「相对遮罩左边 50% 的锚点」变成「相对自身右移 50%」，
 *   再叠加 margin-left:-59px ⇒ **整体向右溢出屏幕 9.4~11.8px**
 *   （实测 375/412/1080 三视口，可见率仅 85.6%~85.7%）
 *   ⇒ 这正是用户说的「右侧露出一截吉祥物」。
 *   ⚠ 为什么必须绝对排除而不是改 z-index：
 *     这两个元素**就是靠 absolute + left:50% + bottom:12vh 定位**的，
 *     一旦变成 relative，定位语义完全改变，z-index 修不了。
 *
 *🔴🔴 v92【实测修正：.intro-props 也必须从这一组里摘出去 —— 与 chibi/greet 同类错误】
 *   【怎么发现的】写完 v92 的 A/B 实测脚本后，.intro-props 断言**三次全部失败**
 *   （rect=0×0），而它的子元素 .iprop 却都有真实尺寸（85×85 / 65×65）。
 *   ⇒ 逐条查 computed style，定位到本条把它的 position 改成了 relative：
 *       .intro-props 的真实定义是 styles.css:3085
 *         `.intro-props{position:absolute;inset:0;pointer-events:none;z-index:1}`
 *       它是一个**铺满遮罩的定位容器**，子元素 .iprop 靠
 *       `position:absolute; bottom:11vh` + `.ip-l{left:7vw}` / `.ip-r{right:7vw}`
 *       定位到**屏幕左右下角**（styles.css:3086-3091）。
 *   【改成 relative 后的实测后果（tmp/_qa_intro/verify_v92.js，Edge/Chromium）】
 *       视口      .intro-props rect      两个 .iprop 落点
 *       1080×2400  0×0（位于 540,1200）   x=625 与 x=370  ❌ 挤在屏幕正中
 *       412×915    0×0（位于 206,458）    x=229 与 x=119  ❌ 挤在正中
 *       320×640    0×0（位于 160,320）    x=179 与 x=76   ❌ 挤在正中
 *     ⚠ 机理：relative 之后 `inset:0` 完全失效（relative 元素不被 inset 撑开），
 *       而本组没给 width/height ⇒ 宽高塌成 0；同时遮罩是 flex column +
 *       align-items:center ⇒ 这个 0×0 的空容器被**居中**放在屏幕正中，
 *       子元素的 left:7vw / right:7vw 就变成「相对屏幕中心左右各偏 7vw」
 *       ⇒ 咖啡杯与枫叶**叠在「青志」二字上**，而不是分居左右下角。
 *     ⇒ v79.3 截图里「咖啡杯在左下、枫叶在右下」这个构图，本档已经**回不来了**。
 *   ⇒ 与 chibi/greet 同样的道理：**它靠 absolute 定位，摘出本组、只补 z-index。** */
.intro-ov .intro-logo,
.intro-ov .intro-slogan,
.intro-ov .intro-mascot-stage {
  position: relative;
  z-index: 2 !important;
}
/* 🔴🔴🔴 v92.1【用户主诉「落叶只在屏幕中间落下，不随机飘落」的真凶 —— 上一条规则就是元凶】
 *
 * 【症状】四档开屏时落叶**全部聚在屏幕正中一条竖线上下落**，画面上只看得见一两片卡在中间。
 *   用户原话：「它只会固定在屏幕中间落下，它不是随机飘落的」
 *           「一个季节版本的例子都是这样的，只会在中间不会飘落」。
 *
 * 【根因】上一条规则把 `.intro-fx` 也塞进了 `position: relative` 那一组。
 *   而 `.intro-fx` 的真实定义（styles.css:2582）是
 *       `.intro-fx { position:absolute; inset:0; pointer-events:none; }`
 *   —— 它是**铺满整个遮罩的粒子容器**，16~22 颗粒子的 `left: X%` 正是相对它算的。
 *   ⚠ 改成 relative 之后 **`inset:0` 完全失效**（relative 元素不被 inset 撑开），
 *     而那条规则没给 width/height ⇒ **宽高塌成 0×0**；
 *     又因为遮罩是 flex + align-items:center，这个 0×0 空盒被**居中**在屏幕正中
 *     ⇒ 所有粒子的 `left:X%` 变成「相对屏幕中心左右各偏 X%」
 *     ⇒ 十几片叶子叠在中间一条线上，观感就是「只在中间落下、不随机飘落」。
 *
 * 【实测证据（smoke/_v92_particle_spread.js，真浏览器量测非推理）】
 *   修复前：`.intro-fx` rect = 0×0、position=relative，
 *           粒子横向分布仅 **1.3% ~ 9.4%**（判据要求 >50%），
 *           360×800 / 412×915 / 1280×720 三视口 × 四季 = 12 组合**全部不达标**。
 *   修复后：`.intro-fx` rect = 视口大小、position=absolute，
 *           粒子横向分布 **79.6% ~ 95.2%**，左右两半都有粒子。
 *
 * 【为什么之前一直没被发现 —— 这一条务必记住】
 *   v92 那轮探针只量了「粒子有多少个」（16 个 ✅），**从没量过它们落在哪里**
 *   ⇒ 报出「14/14 全绿」，是**假绿**。
 *   而且当时 `p1-fixes-v86.css` 因 index.html 里注释被提前闭合而**整张没加载**，
 *   探针是在「没有这条规则」的世界里测的 ⇒ **双重掩盖**。
 *   ⇒ 教训：① 探针必须量**分布**（坐标跨度），不能只数个数；
 *         ② 改完 index.html 必须核对 document.styleSheets 里确实有那张表。
 *
 * 【修法】把 `.intro-fx` 从 relative 组里摘出来，单独只补 z-index，
 *   绝不碰它的 position —— 与 chibi/greet/props 完全同构的处理。 */
.intro-ov .intro-fx {
  z-index: 2 !important;
}
/* ⚠ chibi / greet / props：恢复它们自己的绝对定位（只补 z-index，不碰 position）。
 *   ⚠ v90 已让 season/seasonfx/mascot 三档**不再输出** chibi/greet
 *     （app.js 的 _ise 收窄 + .intro-hero 兜底 display:none），
 *     这条规则是为 spring/summer/autumn/winter 四个粒子档保留的。
 *   ⚠ v92：.intro-props 从 chibi/greet 那一组挪上来（原因见上面那段实测记录）。
 *     它同样是"absolute 定位容器被relative 化"的受害者。 */
.intro-ov .intro-chibi,
.intro-ov .intro-greet,
.intro-ov .intro-props {
  z-index: 2 !important;
}
/* ⚠ 立绘档兜底底色：万一图片还没解码出来，这一帧**不能是黑的**
 *   （.intro-ov 的默认值 #0b1220 是近黑，用户投诉过）。
 *🔴 v90【实测修正：这里只留 .intro-hero】
 *   原选择器还带着 `.intro-ov.intro-mascot`，但 `intro-mascot` 是
 *   **吉祥物图片**的 class（styles.css:4670 给了 width:clamp(...) / height:auto）——
 *   它挂在全屏遮罩上时会把遮罩解析成「一张图片」：
 *   宽度夹到 46vw、高度 auto 塌成 0（实测 rect = 154×0 / 169×0 / 237×0）
 *   ⇒ 整屏只剩右上角「跳过」按钮。
 *   ⇒ v90 已把 intro-mascot 从三档的 ov.className 里去掉，这里同步只认 intro-hero。 */
.intro-ov.intro-hero {
  background: linear-gradient(165deg, #fdf6ee 0%, #f4f8fd 100%) !important;
}
/* ⚠ 跳过按钮：原为 rgba(0,0,0,.25) 黑底 + 白框，落在浅色立绘上是一块黑疙瘩。
 *   改成半透明浅底 + 深色字，仍可点（对比度靠深色文字）。*/
.intro-ov .intro-skip {
  background: rgba(255, 255, 255, .72) !important;
  border: 1px solid rgba(255, 255, 255, .34) !important;
  color: #4a3f33 !important;
  z-index: 9 !important;
  /* ⚠ pointer-events 必须显式 auto：父层 .intro-ov 是 none（不吃全屏点击），
     子元素要「显式反悔」才能点得到。这是 v79.9 批次9 留下的纪律，不能丢。*/
  pointer-events: auto !important;
}

/* ============================================================================
 * v90｜开屏「纯立绘」终局约束（用户三连投诉的最后一道闸）
 * ==========================================================================
 * 【为什么 v89 的修复对用户无效】
 *   v89 只改了 mascot 档，但用户真机上跑的是 **seasonfx** 档
 *   （截图里那片橙色 = styles.css:3090 的 .intro-seasonfx.intro-autumn）。
 *   ⇒ v89 那段 CSS 一次都没命中用户的实际档位。
 *   本段不再区分档位：对**所有**带满屏立绘的档位（.intro-hero），
 *   一律禁止任何前景元素出现在图片之上。
 *
 * 【本段做的事：用 CSS 兜住「JS 改漏了」的场景】
 *   v90 已在 app.js 里删掉 logo/slogan/chibi/greet/props 的输出，
 *   但那三处删除分散在多个分支里 —— 历史上本项目栽过「注释说改了、
 *   实测 DOM 里分支压根不存在」（app.js:7314 那条教训）。
 *   ⇒ 这里用 CSS 兜底：只要是满屏立绘档，这些元素**一律 display:none**。
 *      将来谁再往这些档位里加前景元素，都会立刻被这段压住（而不是又坑用户一次）。
 *
 * ⚠ 注意：不能无差别隐藏 —— spring/summer/autumn/winter 是**纯粒子档**
 *   （没有立绘，就是渐变+粒子），它们的吉祥物/问候语是有意设计的装饰，
 *   隐藏会让这四档变成一片空白。所以只对 .intro-hero（满屏立绘）生效。
 *
 * 🔴🔴 v92【autumn档已从「带 intro-hero」名单里移出 —— 本段不再影响它】
 *   【背景】v91 给 spring/summer/autumn/winter 四档的 ov.className 都加了
 *     intro-hero（理由是"要拿到安全区变量与满屏约束"），于是它们也被本段
 *     display:none !important 命中 ⇒ logo/slogan/chibi/greet/props 全被压掉。
 *     用户拿 v79.3 截图比对（那一屏明确有 logo/slogan/吉祥物/问候语），
 *     诉求是「以前那版好看，现在很多东西坏掉了，要恢复」。
 *   【v92 的处置】app.js 里这四档**去掉了 intro-hero**，只保留
 *     intro-<季>（季节渐变底的选择器）+ intro-fx-<季>（粒子形状/配色）。
 *     ⇒ 本段现在只命中 mascot / season / seasonfx 三档，与v90 的原意完全一致。
 *   ⚠⚠ 顺带修掉一个**没人提过的严重回归**：`.intro-ov.intro-hero` 的
 *     background（styles.css:2550 + 本文件:1555，浅米色
 *     linear-gradient(165deg,#fdf6ee,#f4f8fd)）会**盖掉 autumn 自己的橙色渐变**
 *     （styles.css:3044 的 linear-gradient(160deg,#f7b267,#ef6c3f)）——
 *     两者 specificity 都是 0,2,0，但本文件后加载且那条带 !important。
 *     ⇒ 用户要的是**橙色渐变底**，带 intro-hero 时实际看到的是**一片惨白米色**。
 *     这是「恢复旧观感」必须先解决的前置问题，否则元素加回来了底色还是错的。
 *   ⚠ 本段**继续保留**，它是这三档立绘档的兜底防线，不要因为本次改动删掉。
 * ========================================================================== */
.intro-ov.intro-hero .intro-logo,
.intro-ov.intro-hero .intro-slogan,
.intro-ov.intro-hero .intro-chibi,
.intro-ov.intro-hero .intro-greet,
.intro-ov.intro-hero .intro-props,
.intro-ov.intro-hero .intro-mascot-stage {
  display: none !important;
}
/* ============================================================================
 * v90｜开屏遮罩尺寸防御（实测发现「遮罩塌成 0 高」后加的兜底）
 * ==========================================================================
 * 【为什么要专门加这一段】
 *   质量门神用 Playwright 实测发现：只要遮罩容器挂了 `intro-mascot` 这个 class，
 *   就会被 styles.css:4670 的 `width:clamp(...); height:auto` 当成"一张图片"解析
 *   ⇒ 实测遮罩 rect = 154×0 / 169×0 / 237×0（三个视口）
 *   ⇒ 里面唯一的内容是绝对定位的立绘 ⇒ 高度 auto 塌成 0
 *   ⇒ **整屏只剩右上角一个「跳过」按钮**，开屏等于没有。
 *
 * 【为什么写成"清零所有尺寸"而不是"只去掉某个 class"】
 *   .intro-ov 是 position:fixed; inset:0，尺寸本该由 inset 决定。
 *   但只要有更高优先级的选择器给它设了 width/height/max-*-height，
 *   就会被覆盖（inset 只在相关属性为 auto 时生效）。
 *   ⇒ 这里显式把尺寸全部钉死，让遮罩**在任何情况下都是满屏**。
 *   ⚠ 这是一道**防御**，不是修当前 bug（当前 bug 已通过去掉 className 解决）。
 *     它的价值在于：将来谁再把某个"图片类"的 class 挂到遮罩上，不会再塌。
 * ========================================================================== */
.intro-ov {
  /* 🔴🔴 v91.1【回归 v82 的做法：靠 fixed+inset:0 铺满，不要任何高度钳制】
     ⚠ 用户实测 v91「还是不变大」—— 根因就是这段：
       · min-height: var(--app-vh) 把遮罩**锁死在 JS 实测的那个高度**。
         WebView 首帧时 visualViewport 常常还没算对（拿到偏小值），
         于是遮罩被自己的 min-height 锁住 ⇒ 永远差一截 ⇒「不变大」。
       · max-height 同理：一旦 --app-vh 偏小，就成了「上界」，把遮罩截断。
     ⚠ v82 之所以一直正常：它的 .intro-ov **只有position:fixed; inset:0**，
       既没有 min-height 也没有 max-height，也没有内联 px——
       fixed + inset:0 本身就等于「可视区」，不需要任何尺寸声明。
     ⇒ 这里只保留 width/height:100%（等价于 inset:0，语义清晰），
        把 max/min-height 全部去掉，让尺寸回到浏览器的正确解析。
     ⚠ 但**必须**保留 max-width:100%（去掉 max-width:100vw）：
        那一条是 v91 真正修好的东西 —— vw 与 fixed 包含块不同源，
        缩放 ≠1 时 100vw < 100%，差值就是用户看到的 20% 空白。*/
  width: 100% !important;
  height: 100% !important;
  max-width: 100% !important;
}
/* ⚠ 立绘档不再叠季节渐变底色：底色只作为「图片解码前那一帧」的兜底，
 *   一旦图片铺满就完全看不见。若这里还留着 intro-<季> 的渐变 class，
 *   桌面端某些 ROM 会在图片加载失败时把它露出来 ⇒ 又变成"很多黑色的东西"。*/
.intro-ov.intro-hero.intro-seasonfx,
.intro-ov.intro-hero.intro-season,
.intro-ov.intro-hero.intro-mascot {
  background: #f6f1e8 !important;
}
