/* ============================================================================
   app.css —— 布局与组件（DESIGN.md 的制图世界 / F 稿）
   ★ 所有颜色一律引用 tokens.css 的变量；本文件不出现色值字面量。
   ★ 2026-10-22 死代码清理（用户拍板直接执行）：删掉 64 个「全仓零引用」的 classic 时代类名
     （`.topbar*` / `.brand*` / `.pill` / `.rail*` / `.pane*` / `.d-*` / `.rd-*` / `.foot-*` /
      `.bal` / `.word` / `.sec*` / `.toolbar*` / `.nav-home` / `.nav-me` 等）对应的 **136 个规则块**
     与 128 条选择器分支，82139 B → 71703 B（−12.7%）。判据＝类名在 `app/templates`、`app/**/*.py`、
     `app/**/*.js`、其余 CSS 与四套自测脚本的**边界匹配**中零命中（`cls="cover"` 是唯一传参宏，
     已单列核对；无动态拼接类名）。死类名在任何标记里都不会出现 ⇒ 这些选择器永不匹配 ⇒ 删除
     **渲染零变化**。递归处理了媒体查询嵌套，空查询块一并删掉（残留 0）；删除后花括号配平 0/0。
     ★ 连带处置：`_selftest_security.py` 的 H10 断言原读本文件 `.rd-frame`（该规则已随清理删除，
     阅读器现行走 `tech.css` 的 `.t-rd-frame`）⇒ 已改读 tech.css，断言意图不变。
   ========================================================================== */

/* ---------- reset ---------- */
*,
*::before,
*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body,
h1,
h2,
h3,
p,
dl,
dd,
dt,
ul,
li,
figure{margin:0;padding:0}
ul{list-style:none}
img,
svg{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
input,
select{font:inherit;color:inherit}

/* ---------- browser surfaces（DESIGN.md §4：不用浏览器默认）---------- */
::selection{background:var(--cyan);color:var(--sel-fg)}
::-moz-selection{background:var(--cyan);color:var(--sel-fg)}
*{scrollbar-color:var(--teal) var(--scroll-track);scrollbar-width:thin}
::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:var(--scroll-track)}
::-webkit-scrollbar-thumb{background:var(--teal);border:3px solid var(--scroll-track);border-radius:99px}
::-webkit-scrollbar-thumb:hover{background:var(--teal-light)}
::-webkit-scrollbar-corner{background:var(--scroll-track)}
:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:3px}
a{color:inherit}
h1,
h2,
h3{font-weight:700}
.num{font-variant-numeric:tabular-nums}

/* ---------- 场：坐标网格就是纸本身 ---------- */
body{
  font-family:var(--sans);
  font-size:15px;
  line-height:1.7;
  color:var(--ink);
  background-color:var(--navy);
  background-image:var(--grid-image);
  background-size:24px 24px;
  overflow-x:hidden;
  min-height:100vh;
  display:flex;
  flex-direction:column;
}
.sprite{position:absolute;width:0;height:0;overflow:hidden}
.wrap{width:100%;max-width:1240px;margin:0 auto;padding:0 var(--gutter)}
main{flex:1 1 auto}
.skip{position:absolute;left:-9999px;top:0;z-index:60;background:var(--teal);color:var(--plate-ink);padding:10px 16px;border-radius:0 0 10px 0}
.skip:focus{left:0}

/* ---------- 图签栏 (title block bar) ---------- */

.balance{display:flex;align-items:baseline;gap:5px;font-variant-numeric:tabular-nums}
.balance .n{font-size:17px;font-weight:700;color:var(--accent-text);letter-spacing:-.01em}
.balance .u{font-size:11px;color:var(--ink-3)}
.bar-chip{
  font-size:12.5px;color:var(--ink-2);border:1px solid var(--navy-line);
  border-radius:var(--r-chip);padding:5px 11px;
}
.bar-btn{
  font-size:12.5px;color:var(--ink-2);border:1px solid var(--navy-line);
  border-radius:var(--r-chip);padding:5px 11px;display:inline-flex;align-items:center;gap:7px;
  transition:color .3s var(--ease),border-color .3s var(--ease),background-color .3s var(--ease),transform .3s var(--ease);
}
.bar-btn:hover{color:var(--ink);border-color:var(--cyan)}
.bar-btn:active{transform:scale(.97)}
.bar-btn .ic{width:15px;height:15px;stroke-width:1.7}
/* 换肤开关：当前不是深色时用「已按下」的高亮 */
#theme-toggle[aria-pressed="true"]{color:var(--ink);border-color:var(--cyan);background:color-mix(in srgb,var(--cyan) 16%,transparent)}

/* ---------- 页首（紧凑，不做营销长页）---------- */
.page-head{padding:22px 0 2px}
.page-head h1{font-size:clamp(26px,5.6vw,40px);line-height:1.14;letter-spacing:-.014em;max-width:none;white-space:nowrap}
/* ★ v1.55（用户 2026-09-28）：标题「放一行、字体稍大一点」。
   ① 放大：34px → 40px 上限（clamp 下限同步 24→26px，窄屏不缩太狠）。
   ② 单行：原 `max-width:18ch` 只有约 8 个汉字宽 ⇒ 13 字标题必折成 2 行。
      现改为 `max-width:none` + `white-space:nowrap` ⇒ 强制一行；
      ★ 窄屏放不下时由 @media 降级为「允许换行 + 限宽」，绝不横向溢出。 */
@media (max-width:479px){
  .page-head h1{white-space:normal;max-width:14ch}
}
.page-sub{margin-top:10px;font-size:13.5px;color:var(--ink-2);max-width:52ch}
/* ★ v1.55：英文副标题——比中文正文更小、单行不折。
   ★ v1.55b（用户 2026-09-28）：英文**加大** 12px → 16px（窄屏 13.5px），
     颜色同时由 --ink-3 提到 --ink-2（16px 下 ink-3 对比偏弱）。仍远小于 40px 标题，层级不变。
   ★ 模板用 `<bdi lang="en">` 而非裸英文：让浏览器套用英文断行/字距规则，
     不套中文 line-break 规则（后者可能把单词从中间劈开）。 */
.page-sub-en{font-size:16px;color:var(--ink-2);letter-spacing:.005em;max-width:none;white-space:nowrap}
@media (max-width:479px){ .page-sub-en{font-size:13.5px;white-space:normal} }

/* ---------- toolbar ---------- */

.search{position:relative;display:flex;align-items:center;max-width:420px;width:100%}
.search .ic{position:absolute;left:13px;width:16px;height:16px;color:var(--ink-3);stroke-width:1.7;pointer-events:none}
.search input{
  width:100%;background:var(--field-veil);border:1px solid var(--navy-line);border-radius:var(--r-chip);
  padding:10px 40px 10px 38px;font-size:14px;caret-color:var(--cyan);-webkit-appearance:none;appearance:none;
  transition:border-color .3s var(--ease),background-color .3s var(--ease);
}
.search input::placeholder{color:var(--ink-3)}
.search input:hover{border-color:var(--navy-line)}
.search input:focus{outline:none;border-color:var(--cyan)}
.search input:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.count{font-size:12.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}

/* ---------- sheet grid ---------- */


.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px 12px}
/* ★ v1.58（用户 2026-09-29 拍板「标签要可以点」）：卡片外层由 `<a>` 改为 `<div>`。
   原因：标签要变成真链接，而 `<a>` 里嵌 `<a>` 是**非法嵌套交互元素**（PRD AC4 禁止）。
   整卡的可点区域改由内嵌的**拉伸链接** `.sheet-link` 承担：绝对定位铺满 + `z-index:1`。
   ⇒ 语义分工（同屏两个去处，各归其位）：
        · `.ctag` 标签链接 = 进**标签筛选**，`z-index:2` **压在上面**，自己收点击；
        · `.sheet-link`   = 进**讲义详情**，铺满整卡、垫在下面，接住其余区域的点击。
   ★ 为什么用空链接 + `aria-label`：这是 stretched-link 的标准做法，能保住"整卡可点"
     这一既有体验（不因加标签链接而退化），同时保证**零嵌套**。 */
.sheet{display:block;position:relative;color:inherit}
.sheet>.sheet-link{position:absolute;inset:0;z-index:1}
/* 焦点环仍画在封面上（原 `.sheet:focus-visible .plate`）。容器已是 `<div>`、不可聚焦，
   故改由拉伸链接的 `:focus-visible` 经**兄弟选择器**触发（`.plate` 是其后续兄弟）。 */
.sheet-link:focus-visible{outline:none}
.sheet-link:focus-visible ~ .plate{outline:2px solid var(--cyan);outline-offset:3px}
.plate{
  position:relative;display:block;aspect-ratio:3/4;border-radius:var(--r-plate);overflow:hidden;
  background:var(--teal);
  box-shadow:var(--plate-shadow);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);
}
.plate img.cover{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.sheet:hover .plate{transform:translateY(-4px);box-shadow:var(--plate-shadow-hover)}
.sheet:active .plate{transform:translateY(-1px) scale(.99)}
.plate::after{content:"";position:absolute;inset:6px;border:1px solid rgba(238,243,245,.17);border-radius:7px;pointer-events:none;z-index:3}
.plate .tex{position:absolute;inset:0;width:100%;height:100%}
.plate .fig{position:absolute;left:8%;right:8%;top:25%;height:41%;color:rgba(238,243,245,.92);stroke:currentColor;fill:none;stroke-width:var(--sw);stroke-linecap:round;stroke-linejoin:round}
.plate-top{position:absolute;top:13px;left:13px;right:13px;display:flex;align-items:center;justify-content:space-between;gap:8px;z-index:4}
.plate-top .field{border:1px solid rgba(238,243,245,.44);border-radius:4px;padding:1px 7px;font-size:10.5px;letter-spacing:.08em;color:var(--plate-ink)}
.plate-top .chip{border:1px solid rgba(238,243,245,.44);border-radius:var(--r-chip);padding:1px 8px;font-size:10px;color:var(--plate-ink)}
.plate .block{
  position:absolute;left:0;right:0;bottom:0;z-index:4;
  display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  padding:8px 13px;border-top:1px solid rgba(238,243,245,.3);background:rgba(9,28,36,.36);
}
.plate .block .l{font-size:10.5px;color:var(--on-plate);letter-spacing:.02em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plate .block .r{font-size:10.5px;color:var(--on-plate);font-variant-numeric:tabular-nums;flex:0 0 auto}
.plate .block .r b{font-size:16px;font-weight:700;color:var(--plate-ink);letter-spacing:-.01em}
.plate.is-owned{background:var(--teal-deep)}
.plate.is-owned .fig{color:rgba(238,243,245,.55)}
.plate.is-owned::after{border-color:rgba(238,243,245,.1)}
.plate.is-owned .block{background:rgba(9,28,36,.5)}
.sheet .meta{margin-top:10px;padding:0 1px}
.sheet .meta h3{
  font-size:13px;line-height:1.5;font-weight:600;color:var(--ink);
  letter-spacing:-.005em;text-wrap:pretty;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  transition:color .3s var(--ease)
}
.sheet:hover .meta h3{color:var(--ink-strong)}
/* ---------- 卡片标题下的标签行（v1.53 建 · **v1.57 按钮化 · v1.58 可点 · v1.59 圆角定胶囊**）----------
   ★ v1.57（用户 2026-09-29 拍板）：「换一个展现样式，**一个标签表现为一个按钮**」。
   ★ v1.58（用户 2026-09-29 拍板）：「**要可以点**，点了之后进到一个标签筛选结果的页面」
     ⇒ `.ctag` 由 `<span>` 改为**真链接 `<a>`**（外观不变，只加"能点"）。
   ★ v1.59（用户 2026-09-29 拍板）：圆角 = **`--r-chip`（999px 全胶囊）**，
     **推翻我在 v1.57 自行选定的 6px**。
   设计取向（用户要求「注意设计感」）：
   ① **不与标题抢**——标题 h3 是 13px/600，标签是 11px/400 + --ink-3，明显退居二线；
   ② **按钮外观 = 描边/圆角/内边距**；v1.58 起它同时是**真链接**，
      故必须 `position:relative;z-index:2` 压在整卡拉伸链接之上（见 `.sheet` 说明），
      并自带 `text-decoration:none`（容器已不是 `<a>`，不再代为取消下划线）。
   ③ ★★ **圆角用 `--r-chip` 胶囊 —— 但这条曾经是反的，沿革必须写清（否则后人会以为自相矛盾）**：
      · **v1.57（6px）**：我当时的理由是「标签是**标注**，用胶囊会与封面 chip / 筛选栏控件撞脸」
        —— 该判断在"标签**不可点**"的前提下是成立的，但它**从未经用户拍板**（H 已指出）。
      · **v1.58（可点）**：标签变成**真链接**（点一下即按该标签筛选）⇒ 它与**顶部筛选栏功能同类**。
        此时"长得像筛选栏"**不再是撞脸，而是一致性**：暗示"这里可以点"，用户一眼认出。
      · **v1.59（胶囊）**：用户看过**两种效果的实拍对照图**后拍板胶囊
        （对照图存于协同库 `标签可点进筛选v1.58-20260929-0653\圆角对照-*.png`）。
      ★ 因此**删除了 v1.57 那条「不得用 `--r-chip`」的反面断言** —— 这是**需求本身被用户变更**，
        不是放宽判据；同时**新增正面断言**钉住"圆角必须是 `var(--r-chip)`"。
      ★ 仍**不受** v1.53 AC8「不用胶囊」约束 —— 该条已被用户两次推翻（按钮化、可点），
        PRD 需由 H 重写（见协同库 v1.57/v1.58 待审核件 §8/§8-1）。
   ④ ★ **去掉 v1.53 的前导 2px 竖线**：标签自带描边后已能自锚，竖线反成杂物。
   ⑤ **单行不换行**（`nowrap` + `overflow:hidden`）：卡片高度必须齐平（网格）。
      宁可让末尾按钮**截断**（每个 `.ctag` 自身 `min-width:0` + `ellipsis`），也不破版；
      完整清单仍靠 `title` 悬停可见。
   ⑥ 颜色只用既有令牌，**不引入新色**（`--navy-line` 发丝边 + `--ink-3` 三级文字）；
      深浅主题自动跟随；悬停时才升到 `--ink`/`--ink-2` + 青绿描边（表明"这个可以点"）。
*/
.sheet .meta .ctags{
  display:flex;flex-wrap:nowrap;align-items:center;gap:5px;
  margin-top:6px;overflow:hidden
}
.sheet .meta .ctag{
  flex:0 1 auto;min-width:0;display:inline-block;
  /* ★ v1.58：`.ctag` 已是真链接（点它进标签筛选）⇒
     ① 自身 `text-decoration:none`（容器已不是 `<a>`，不再代为取消下划线）；
     ② `position:relative;z-index:2` **压在整卡拉伸链接之上**，才能收到自己的点击。 */
  position:relative;z-index:2;text-decoration:none;
  padding:1px 7px;font-size:11px;line-height:1.5;color:var(--ink-3);
  /* ★ v1.59：圆角 = `--r-chip`（999px 全胶囊），与顶部筛选栏 `.bar-chip` 同一令牌 ⇒
     功能同类（点一下即筛选）就用同一形状（用户 2026-09-29 拍板，推翻 v1.57 的 6px）。 */
  border:1px solid var(--navy-line);border-radius:var(--r-chip);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:color .3s var(--ease),border-color .3s var(--ease)
}
/* 单个标签自身的悬停反馈（比只靠整卡 hover 更能表明"这一个可以点"）。 */
.sheet .meta .ctag:hover{color:var(--ink);border-color:var(--accent-text)}
.sheet:hover .meta .ctag{color:var(--ink-2);border-color:var(--accent-text)}
/* `+K` 不是标签，不套按钮皮，只作计数提示（与标签同字号同色，视觉上明显弱于按钮）。 */
.sheet .meta .ctag-more{flex:0 0 auto;padding:1px 2px;font-size:11px;line-height:1.5;color:var(--ink-3)}
.sheet:hover .meta .ctag-more{color:var(--ink-2)}
/* ★ 手机行卡（≤479px）：卡片变横向、文字列窄 ⇒ 按钮再收一档内边距与间隙，避免撑破（AC10）。 */
@media (max-width:479px){ .sheet .meta .ctags{gap:4px;margin-top:3px} .sheet .meta .ctag{font-size:10.5px;padding:1px 5px} }

/* ---------- empty（复述当前筛选条件）---------- */
.empty{padding:40px 0 30px;text-align:center}
.empty h3{font-size:18px}
.empty p{margin-top:10px;font-size:13.5px;color:var(--ink-2);max-width:46ch;margin-left:auto;margin-right:auto}
.empty .act{margin-top:20px;display:flex;justify-content:center;gap:14px;flex-wrap:wrap}

/* ---------- detail ---------- */
.detail{padding:22px 0 120px}
.back{display:inline-flex;align-items:center;gap:8px;font-size:13.5px;color:var(--ink-2);transition:color .3s var(--ease);text-decoration:none}
.back:hover{color:var(--ink)}
.back .ic{width:16px;height:16px;stroke-width:1.8}
.d-platewrap{max-width:380px;width:100%;margin:0 auto}
.d-platewrap .vt{position:relative;display:block;aspect-ratio:3/4;border-radius:var(--r-lg);overflow:hidden;background:var(--teal);box-shadow:var(--plate-shadow-lg)}
.d-platewrap .vt img.cover{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.d-platewrap .vt::after{content:"";position:absolute;inset:8px;border:1px solid rgba(238,243,245,.17);border-radius:8px;pointer-events:none;z-index:3}
.d-platewrap .vt.is-owned{background:var(--teal-deep)}
.d-platewrap .vt.is-owned .fig{color:rgba(238,243,245,.44)}
.d-platewrap .tex{position:absolute;inset:0;width:100%;height:100%}
.d-platewrap .fig{position:absolute;left:8%;right:8%;top:24%;height:42%;color:rgba(238,243,245,.92);stroke:currentColor;fill:none;stroke-width:.62;stroke-linecap:round;stroke-linejoin:round}
.d-platewrap .block{position:absolute;left:0;right:0;bottom:0;z-index:4;display:flex;align-items:baseline;justify-content:space-between;padding:10px 16px;border-top:1px solid rgba(238,243,245,.3);background:rgba(9,28,36,.36);font-size:12px;color:var(--on-plate)}
.d-platewrap .block b{font-size:19px;font-weight:700;color:var(--plate-ink);font-variant-numeric:tabular-nums}
.d-platewrap .ptop{position:absolute;top:16px;left:16px;right:16px;display:flex;justify-content:space-between;align-items:center;z-index:4}
.d-platewrap .field{border:1px solid rgba(238,243,245,.44);border-radius:4px;padding:2px 9px;font-size:12px;letter-spacing:.08em;color:var(--plate-ink)}
.d-platewrap .chip{border:1px solid rgba(238,243,245,.44);border-radius:var(--r-chip);padding:2px 10px;font-size:11.5px;color:var(--plate-ink)}
.spec{display:grid;grid-template-columns:auto 1fr;gap:0;margin-top:20px;border-top:1px solid var(--navy-line);max-width:420px}
.spec dt{font-size:12.5px;color:var(--ink-3);padding:9px 18px 9px 0;border-bottom:1px solid var(--navy-line)}
.spec dd{font-size:13.5px;color:var(--ink);padding:9px 0;border-bottom:1px solid var(--navy-line);font-variant-numeric:tabular-nums}
.buy{margin-top:24px;display:flex;flex-wrap:wrap;align-items:center;gap:14px}
.buy .price{font-size:30px;font-weight:700;color:var(--accent-text);line-height:1;font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.buy .price span{font-size:.42em;font-weight:600;color:var(--ink-2);margin-left:4px}
.chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:14px}
.chips > *{font-size:12px;color:var(--ink-2);border:1px solid var(--navy-line);border-radius:6px;padding:3px 9px;text-decoration:none}
.rows{max-width:640px;margin-top:4px}
.rows .row{display:grid;grid-template-columns:82px 1fr;gap:14px;padding:12px 0;border-bottom:1px solid var(--hairline);font-size:13.5px}
.rows .row .k{color:var(--ink-3);font-size:12.5px}
.rows .row .v{color:var(--ink-2)}
.rows .row.warn .v{color:var(--warn)}
.buybar{
  position:fixed;left:0;right:0;bottom:0;z-index:35;
  background:var(--navy-deep);border-top:1px solid var(--navy-line);
  padding:10px var(--gutter) calc(10px + env(safe-area-inset-bottom,0px));
}

/* ---------- 按钮（主按钮：尾部箭头嵌在独立圆形里）---------- */
.btn{
  display:inline-flex;align-items:center;gap:9px;
  background:var(--teal);color:var(--plate-ink);border-radius:var(--r-chip);
  padding:9px 9px 9px 20px;font-size:15px;font-weight:600;text-decoration:none;
  transition:transform .4s var(--ease),background-color .4s var(--ease);
}
.btn:hover{background:var(--teal-light);transform:translateY(-2px)}
.btn:active{transform:translateY(0) scale(.985)}
.btn .ic{width:14px;height:14px;stroke-width:2}
.btn-quiet{border:1px solid var(--navy-line);border-radius:var(--r-chip);padding:8px 18px;font-size:14px;color:var(--ink-2);transition:color .3s var(--ease),border-color .3s var(--ease),transform .3s var(--ease);text-decoration:none;display:inline-block}
.btn-quiet:hover{color:var(--ink);border-color:var(--cyan)}
.btn-quiet:active{transform:scale(.97)}

/* ---------- footer（本轮占位；功能入口后续轮）---------- */
.foot{margin-top:56px;border-top:1px solid var(--navy-line);background:var(--navy-deep)}
.foot .mark{width:20px;height:20px;color:var(--cyan-soft);stroke-width:1.7}
.foot .line{font-size:12.5px;color:var(--ink-3)}

/* ---------- motion：只做一个编排过的进场时刻 ---------- */
@keyframes rise{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.enter{animation:rise .42s var(--ease) both;animation-delay:var(--d,0ms)}
@media (prefers-reduced-motion:reduce){
  .enter{animation:none!important}
  *{transition-duration:.01ms!important}
}

/* ---------- responsive（移动端优先）---------- */
@media (min-width:601px){
  :root{--gutter:28px;--bar-h:58px;--sw:1.05}
  body{font-size:15.5px}
  .grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:30px 18px}
  .page-head{padding-top:32px}
  .d-platewrap{margin:0}
  .detail{padding-bottom:80px}
  .buybar{position:static;background:none;border:0;padding:0;margin-top:26px}
}
@media (min-width:1024px){
  :root{--gutter:32px;--sw:.8}
  .grid{grid-template-columns:repeat(4,minmax(0,1fr));gap:36px 20px}
}

/* ============================================================================
   M4 第 2 轮新增组件：表单 / 提示 / 确认下单 / 表格 / 阅读器 / 静态长文
   ★ 颜色一律引用 tokens.css 变量；深浅两套自动生效，本文件不写死色值。
   ========================================================================== */

/* ---------- 窄栏（登录/注册/充值/静态页）---------- */


/* ---------- 表单 ---------- */
.form{margin-top:24px}
.fld{display:block;margin:0 0 16px}
.fld label{display:block;font-size:12.5px;color:var(--ink-2);letter-spacing:.02em;margin-bottom:6px}
/* ★★ v1.68 修复：这条规则**必须排除复选框/单选按钮**。
   原写法 `.fld input{… appearance:none …}` 会把 `<input type="checkbox">` 也套上文本框外观：
   `appearance:none` 连**原生的对勾**一起抹掉、`width:100%` 再把它拉成空白长条
   ⇒ 点下去其实生效了，但**看不到勾**，用起来就像"选不了"（用户 2026-09-30 实报）。
   排除后复选框恢复原生渲染；`.agree` / 下方新增的勾选框规则负责它的大小与配色。 */
.fld input:not([type="checkbox"]):not([type="radio"]){
  width:100%;background:var(--field-veil);border:1px solid var(--navy-line);border-radius:var(--r-card);
  padding:11px 13px;font-size:14.5px;color:var(--ink);font-family:inherit;
  -webkit-appearance:none;appearance:none;
  transition:border-color .3s var(--ease),background-color .3s var(--ease);
}
.fld input::placeholder{color:var(--ink-3)}
.fld input:hover{border-color:var(--cyan-soft)}
.fld input:focus{outline:none;border-color:var(--cyan)}
.fld input:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.fld .hint{display:block;margin-top:6px;font-size:11.5px;color:var(--ink-3)}
.agree{display:flex;align-items:flex-start;gap:10px;margin:6px 0 18px;font-size:13px;color:var(--ink-2);line-height:1.7}
.agree input[type="checkbox"]{
  flex:0 0 auto;width:17px;height:17px;margin-top:2px;accent-color:var(--teal);
  background:var(--field-veil);border:1px solid var(--navy-line);border-radius:4px;
}
.agree input[type="checkbox"]:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.agree a{color:var(--accent-text);text-decoration:none;border-bottom:1px solid var(--hairline)}
.agree a:hover{color:var(--ink-strong);border-bottom-color:var(--accent-text)}

/* ★ v1.68：**表单里的勾选框**统一样式（不限于 `.agree`）。
   凡在 `.fld` 内的复选框/单选按钮都给一个原生、17px、可点、有对勾的样子，
   并与 `.agree` 同一套配色（`accent-color:var(--teal)`），避免"每个表单各写一套"。 */
.fld input[type="checkbox"],
.fld input[type="radio"]{
  flex:0 0 auto;width:17px;height:17px;padding:0;margin:0 7px 0 0;vertical-align:-2px;
  accent-color:var(--teal);border:1px solid var(--navy-line);border-radius:4px;
}
.fld input[type="checkbox"]:focus-visible,
.fld input[type="radio"]:focus-visible{
  outline:2px solid var(--cyan);outline-offset:2px;
}
.inline-form{display:inline}

/* ---------- 提示条 ---------- */
.alert,
.notice{
  display:flex;align-items:flex-start;gap:9px;
  border:1px solid var(--navy-line);border-radius:var(--r-card);
  padding:11px 14px;margin:18px 0;font-size:13.5px;line-height:1.7;
}
.alert{border-color:var(--warn);color:var(--warn)}
.notice{color:var(--ink-2);background:var(--sunken)}
.notice.ok{border-color:var(--teal);color:var(--ink)}
.notice .ic,
.alert .ic{flex:0 0 auto;width:16px;height:16px;margin-top:3px;stroke-width:1.8}
.notice b{color:var(--ink);font-weight:700}

/* ---------- 确认下单面板（不退款声明 + 必勾确认）---------- */
.confirm{
  flex:1 1 100%;margin-top:16px;padding:16px 18px;
  border:1px solid var(--teal);border-radius:var(--r-card);background:var(--sunken);
}
.confirm .agree{margin:16px 0 6px}
.confirm .alert{margin:6px 0 0}
.shortfall{
  margin-top:14px;font-size:13.5px;color:var(--ink-2);line-height:1.8;
  border-left:3px solid var(--navy-line);padding-left:12px;
}
.shortfall b{color:var(--ink);font-variant-numeric:tabular-nums}
.shortfall a{color:var(--accent-text);text-decoration:none;border-bottom:1px solid var(--hairline)}
.shortfall a:hover{color:var(--ink-strong);border-bottom-color:var(--accent-text)}

/* ---------- 顶栏链接 ---------- */


/* ---------- 个人中心 ---------- */

.sheet .meta .sub{display:block;margin-top:4px;font-size:11.5px;color:var(--ink-3)}

/* ---------- 表格（窄屏内滚动，不影响页面宽度）---------- */

.tbl{width:100%;border-collapse:collapse;font-size:13px}
.tbl th,
.tbl td{padding:10px 12px;text-align:left;border-bottom:1px solid var(--hairline);white-space:nowrap;vertical-align:top}
.tbl thead th{font-size:12px;font-weight:600;color:var(--ink-3);letter-spacing:.04em;background:var(--sunken)}
.tbl tbody tr:last-child td{border-bottom:0}
.tbl td{color:var(--ink-2)}
.tbl .num{font-variant-numeric:tabular-nums;text-align:right}
.tbl .mono{font-family:var(--mono);font-size:12.5px;color:var(--ink)}

/* ---------- 阅读器（sandbox iframe）---------- */
.rd{display:flex;flex-direction:column;min-height:calc(100vh - var(--bar-h))}
.rd-bar-in{
  width:100%;max-width:1240px;margin:0 auto;padding:10px var(--gutter);
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
}
/* ★ v1.60（用户 2026-09-29 拍板）：阅读器顶栏的「返回详情 / 回首页」改为**胶囊按钮**
   —— **深色底 + 浅色字**。
   ★ **v1.61（用户 2026-09-29 追加指令）**：「详情页的『返回全部讲义』也需要改为胶囊按钮」
     ⇒ 作用域由 `.rd-bar-in .back` **扩为全站 `.back`**（`.back` 全站只有 3 处：阅读器顶栏
     2 枚 ＋ 详情页 1 枚，用户已点名全都要胶囊；已无"会误伤"的第三处）。
   ★ **底色为什么用 `--teal` 而不是 `--navy`/`--navy-deep`**：只有 `--teal` 系在**深浅两主题下
     都是深色** —— `--navy` 在浅色主题是近白 `#fbfcfc`、`--navy-deep` 是 `#eef2f4`，
     拿它们当"深底"名不副实；`--teal` 两主题恒为 `#1a6b7a`（tokens.css 注明"青绿实底不变"）。
   ★ 文字用 `--plate-ink`（两主题皆 `#fff`），与既有青绿实底按钮（`.abtn` / `.rd-fb-dl` / 选中的
     `.pill`）**同一套语汇**；形状复用 `--r-chip`（999px），与**同一栏**里的 `.rd-mode`
     （「免费预览 · 前 N 节」）本就是胶囊 ⇒ 两者同族。
   ★ **图标不用另配**：`.ic` 是 `stroke:currentColor` ⇒ 随文字一起变浅色。
   ★ 键盘焦点时形状不变：全局 `:focus-visible{border-radius:3px}` 在 **L24**、本条在**其后**，
     两条同为 0-1-0 ⇒ 后者胜出（**不要**把本条移到文件前面，否则聚焦时胶囊会被压成 3px 方角）。 */
.back{
  background:var(--teal);color:var(--plate-ink);
  border-radius:var(--r-chip);padding:5px 13px;font-weight:600;
  transition:background-color .25s var(--ease),color .25s var(--ease)
}
.back:hover{background:var(--teal-mid);color:var(--plate-ink)}
.rd-title{
  flex:1 1 40%;min-width:0;font-size:13.5px;font-weight:600;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
   预览端点 postMessage 报【内容真实高度】⇒ 下面用 min(现有视口式高度, 内容高) 收敛。
   ★ 没收到消息时 --sp-frame-h 未定义 ⇒ min(原式, 原式) = 原式 ⇒ 完全维持原高度（不闪、不塌、绝不为 0）。


/* ---------- 静态长文（用户协议 / 隐私政策 / 著作权声明）---------- */
.doc{padding:34px 0 20px;max-width:70ch}
.doc h1{font-size:clamp(22px,5.4vw,30px);line-height:1.2;letter-spacing:-.012em}
.doc h2{margin-top:32px;font-size:16px;letter-spacing:-.005em;color:var(--ink)}
.doc h2::before{content:"";display:block;width:22px;height:1px;background:var(--cyan-soft);margin-bottom:10px}
.doc p{margin-top:12px;font-size:13.5px;line-height:1.95;color:var(--ink-2)}
.doc ul{margin-top:12px;padding-left:20px}
.doc li{font-size:13.5px;line-height:1.95;color:var(--ink-2);margin-top:6px;list-style:disc}
.doc b{color:var(--ink);font-weight:650}
.doc a{color:var(--accent-text);text-decoration:none;border-bottom:1px solid var(--hairline)}
.doc a:hover{color:var(--ink-strong);border-bottom-color:var(--accent-text)}

/* ---------- 页脚补充 ---------- */

/* ★ 2026-10-05：补 `align-items:center`。本行是 `flex-wrap:wrap` 且原先未声明 align-items
   ⇒ 默认 stretch：纯文字的备案号 `<span>` 会被拉伸到与同排的 `.env-tag` 等高（25.5px），
   而文字仍贴在盒顶（真浏览器实测：上留白 2px / 下留白 8.5px，偏心 −3.3px）。
   ⇒ 开发环境（有 env-tag）页脚两行字一高一低。生产无 env-tag 时看不出，属同族隐患。 */
.beian{margin-top:16px;font-size:11.5px;color:var(--ink-3);display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* ---------- 开发环境标识（Hermes A-2③ 授权 · 2026-09-28）----------
   ★ 为什么要有：开发环境（群晖 8081）与生产环境（阿里云，门户自己的域名）将来并存，
     不加标识就容易"在对的环境做错的事"（在生产库上试写、或把开发库当生产核对）。
   ★ 用 --warn（红，仅警示）：PRD §5.3 允许红色作警示；不用暖色、不用纯黑。
   ★ 生产的域名/地址不写进本文件 —— PRD §3.4 要求 app/ 下不留下生产域名的字样，
     以免隔离 grep 出现无从判断的第二处命中（本注释初版就踩了这个坑）。
   ★ 生产上线（M5）时：改为「生产环境」或整段删除（连同两处模板里的调用）。 */
.env-tag{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;
  color:var(--warn);border:1px solid var(--warn);border-radius:999px;padding:2px 10px;
  letter-spacing:.02em;white-space:nowrap}
.env-tag::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--warn)}

/* ---------- 响应式补充 ---------- */
@media (min-width:601px){
  .doc{padding:44px 0 24px}
}

/* ============================================================================
   M4 第 3 轮新增：管理后台（8.3）—— 走向"密而清"：信息密度高、表格可读
   ★ 颜色一律引用 tokens.css 变量；深浅两套自动生效，本段不写死色值。
   ========================================================================== */
body.admin{font-size:14px;line-height:1.6}
body.admin .abar{border-bottom:1px solid var(--navy-line);background:var(--navy-deep);position:sticky;top:0;z-index:40}
.abar-in{width:100%;max-width:1480px;margin:0 auto;padding:8px var(--gutter);display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.abrand{display:flex;align-items:center;gap:7px;text-decoration:none;color:var(--ink)}
.abrand .mark{width:19px;height:19px;color:var(--cyan-soft);stroke-width:1.7}
.abrand .sub{font-size:11px;color:var(--plate-ink);background:var(--teal);border-radius:4px;padding:1px 6px;letter-spacing:.06em}
.anav{display:flex;flex-wrap:wrap;gap:2px}
.anav a{font-size:13.5px;color:var(--ink-2);text-decoration:none;padding:5px 11px;border-radius:6px;transition:color .25s var(--ease),background-color .25s var(--ease)}
.anav a:hover{color:var(--ink-strong);background:var(--sunken)}
.anav a[aria-current="page"]{color:var(--plate-ink);background:var(--teal);font-weight:600}
.anav a:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.abar-spacer{flex:1 1 auto}
.alink{font-size:13px;color:var(--accent-text);text-decoration:none;padding:4px 8px;border-radius:6px}
.alink:hover{color:var(--ink-strong);background:var(--sunken)}
.alink.danger{color:var(--warn)}
.alink.danger:hover{color:var(--ink-strong);background:var(--sunken)}
.awrap{width:100%;max-width:1480px;margin:0 auto;padding:20px var(--gutter) 40px}

/* ---------- 页头 ---------- */
.ahead{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 16px;padding:6px 0 16px;border-bottom:1px solid var(--navy-line)}
.ahead h1{font-size:23px;letter-spacing:-.012em}
.ahead-id{font-size:12px;color:var(--ink-3);font-family:var(--mono);margin-left:9px;font-weight:400}
.ahead-sub{flex:1 1 320px;font-size:12.5px;color:var(--ink-3);line-height:1.75}
.ahead-act{display:flex;gap:10px;align-items:center}

/* ---------- 卡片 / 面板 ---------- */
.acard{margin-top:18px;border:1px solid var(--navy-line);border-radius:var(--r-card);background:var(--sunken);padding:14px 16px}
.acard-h{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px}
.acard-h h2{font-size:15px;letter-spacing:-.005em}
.acard-count{font-size:12px;color:var(--ink-3);margin-left:8px;font-weight:400}
.acard-note{font-size:11.5px;color:var(--ink-3)}
.acard-danger{border-color:var(--warn)}
.acard :last-child{margin-bottom:0}

/* ---------- 统计 ---------- */
.astats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:10px;margin-top:18px}
.astat{border:1px solid var(--navy-line);border-radius:var(--r-card);background:var(--sunken);padding:10px 12px;display:flex;flex-direction:column;gap:4px}
.astat .k{font-size:11.5px;color:var(--ink-3)}
.astat b{font-size:20px;font-weight:700;color:var(--accent-text);letter-spacing:-.015em;font-variant-numeric:tabular-nums}
.astat b.sm{font-size:12.5px;color:var(--ink-2);letter-spacing:0}

/* ---------- 表格（密） ---------- */
.atbl-wrap{overflow-x:auto;border:1px solid var(--navy-line);border-radius:var(--r-card)}
.atbl{width:100%;border-collapse:collapse;font-size:13px}
.atbl th,
.atbl td{padding:8px 11px;text-align:left;border-bottom:1px solid var(--hairline);white-space:nowrap;vertical-align:middle}
.atbl thead th{font-size:11.5px;font-weight:600;color:var(--ink-3);letter-spacing:.05em;background:var(--sunken);position:sticky;top:0}
.atbl tbody tr:last-child td{border-bottom:0}
.atbl td{color:var(--ink-2)}
.atbl tbody tr:hover td{background:var(--sunken)}
.atbl .num{font-variant-numeric:tabular-nums;text-align:right}
.atbl .mono{font-family:var(--mono);font-size:12.5px;color:var(--ink)}
.atbl a{color:var(--accent-text);text-decoration:none;border-bottom:1px solid var(--hairline)}
.atbl a:hover{color:var(--ink-strong);border-bottom-color:var(--accent-text)}
.atitle{white-space:normal;min-width:190px}
.atags{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}
.atags span{font-size:10.5px;color:var(--ink-3);border:1px solid var(--navy-line);border-radius:5px;padding:0 6px}
.asub{display:block;font-size:11px;color:var(--ink-3)}
.acell-act{display:flex;flex-wrap:wrap;align-items:center;gap:6px}
.ahint{font-size:11px;color:var(--ink-3)}

/* ---------- 状态徽标 ---------- */
.abadge{display:inline-block;font-size:11.5px;line-height:1.6;border:1px solid var(--navy-line);border-radius:5px;padding:0 7px;color:var(--ink-2);white-space:nowrap}
.abadge.ok{border-color:var(--teal);color:var(--ink)}
.abadge.warn{border-color:var(--warn);color:var(--warn)}
.abadge.mute{color:var(--ink-3)}

/* ---------- 表单（密） ---------- */
.aform{margin-top:4px}
.aform-row{display:flex;flex-wrap:wrap;gap:12px 16px;align-items:flex-end}
.aform-row .fld{flex:0 1 200px;margin:0}
.aform-act{display:flex;gap:8px;align-items:center;padding-bottom:2px}
.arow2{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}
.arow3{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px}
.agrid2{display:grid;grid-template-columns:1fr;gap:16px;align-items:start}
.acol{display:block}
.aform .fld{margin-bottom:12px}
.aform .fld label{font-size:12px}
.ainp,
.asel{
  width:100%;background:var(--field-veil);border:1px solid var(--navy-line);border-radius:8px;
  padding:8px 10px;font-size:13.5px;color:var(--ink);font-family:inherit;
  -webkit-appearance:none;appearance:none;
  transition:border-color .25s var(--ease);
}
textarea.ainp{min-height:66px;resize:vertical;line-height:1.6}
.ainp.sm{max-width:110px}
.ainp:focus,
.asel:focus{outline:none;border-color:var(--cyan)}
.ainp:focus-visible,
.asel:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.ainp[type="file"]{padding:6px 8px;font-size:12.5px}
.asel{padding-right:26px;background-image:linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);background-position:calc(100% - 14px) 52%,calc(100% - 9px) 52%;background-size:5px 5px,5px 5px;background-repeat:no-repeat}
.apane{max-width:460px;margin:52px auto;padding:0 0 30px}
.apane h1{font-size:26px;letter-spacing:-.014em}
.apane-sub{margin-top:10px;font-size:12.5px;color:var(--ink-2);line-height:1.8}
.apane-alt{margin-top:22px;font-size:13px}
.apane-alt a{color:var(--accent-text);text-decoration:none}
.apane-alt a:hover{color:var(--ink-strong)}
.apane .aform{margin-top:20px}
.apane .aform .fld{flex:none}
.aspec{display:grid;grid-template-columns:auto 1fr;gap:2px 16px;margin:14px 0;font-size:12.5px}
.aspec dt{color:var(--ink-3);padding:4px 0}
.aspec dd{color:var(--ink-2);padding:4px 0;font-variant-numeric:tabular-nums}
.aspec dd.mono{font-family:var(--mono)}
.aspec code{font-family:var(--mono);font-size:11.5px;color:var(--ink-2)}
.abtns{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:12px}

/* ---------- 按钮 ---------- */
.abtn{display:inline-flex;align-items:center;gap:7px;background:var(--teal);color:var(--plate-ink);border:0;border-radius:8px;padding:8px 16px;font-size:13.5px;font-weight:600;font-family:inherit;cursor:pointer;text-decoration:none;transition:background-color .25s var(--ease),transform .25s var(--ease)}
.abtn:hover{background:var(--teal-light);transform:translateY(-1px)}
.abtn:active{transform:none}
.abtn.sm,
.abtn-q.sm,
.abtn-d.sm{padding:4px 10px;font-size:12.5px;border-radius:6px}
.abtn-q{display:inline-flex;align-items:center;background:transparent;color:var(--ink-2);border:1px solid var(--navy-line);border-radius:8px;padding:7px 14px;font-size:13.5px;font-family:inherit;cursor:pointer;text-decoration:none;transition:color .25s var(--ease),border-color .25s var(--ease)}
.abtn-q:hover{color:var(--ink-strong);border-color:var(--cyan)}
.abtn-d{display:inline-flex;align-items:center;background:transparent;color:var(--warn);border:1px solid var(--warn);border-radius:8px;padding:7px 14px;font-size:13.5px;font-family:inherit;cursor:pointer;transition:color .25s var(--ease),background-color .25s var(--ease)}
.abtn-d:hover{color:var(--plate-ink);background:var(--warn)}
.abtn:focus-visible,
.abtn-q:focus-visible,
.abtn-d:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.abtn[disabled],
.abtn-d[disabled],
.abtn-q[disabled]{opacity:.72;cursor:not-allowed;transform:none}

/* ---------- 提示条 ---------- */
.anote{font-size:12.5px;line-height:1.8;color:var(--ink-3);border-left:2px solid var(--navy-line);padding:2px 0 2px 11px;margin:10px 0}
.anote b{color:var(--ink-2)}
.anote.ok{border-left-color:var(--teal);color:var(--ink-2)}
.anote.warn-note{border-left-color:var(--warn);color:var(--warn)}
.anote a{color:var(--accent-text);text-decoration:none;border-bottom:1px solid var(--hairline)}
.anote.danger-zone{border-left-color:var(--warn);margin-top:16px}
.anote code{font-family:var(--mono);font-size:11.5px;color:var(--ink-2);background:var(--sunken);border-radius:4px;padding:1px 5px}
/* 8.3d：卡密生成回执页的说明条（地址被改写为 ?gen=1 后的解释） */
.anote.gen-note{border-left-color:var(--teal);color:var(--ink-2);margin-bottom:16px}

/* ---------- 卡密明文块 ---------- */
.ckplain{border-color:var(--teal)}
.ckplain .atbl td.ck-code{font-size:14px;letter-spacing:.06em;color:var(--ink)}

/* ---------- 沙箱预览 ---------- */
.pvframe{display:block;width:100%;height:520px;border:1px solid var(--navy-line);border-radius:var(--r-card);background:var(--navy-deep);margin-top:10px}

/* ---------- 页脚 ---------- */
.afoot{margin-top:40px;border-top:1px solid var(--navy-line);background:var(--navy-deep)}
.afoot-in{max-width:1480px;margin:0 auto;padding:16px var(--gutter);display:flex;flex-wrap:wrap;gap:6px 20px;font-size:11.5px;color:var(--ink-3)}

/* ---------- 后台响应式：移动端单列 / 桌面多列 (F-5 AC6) ---------- */
@media (min-width:601px){
  .aform-row .fld{flex:0 1 180px}
  .apane{margin:72px auto}
}
@media (min-width:1024px){
  .agrid2{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .agrid2>.acard:first-child{position:sticky;top:60px}
  .ahead h1{font-size:26px}
  .pvframe{height:640px}
}

/* ============================================================================
   8.3e 修复轮（M4 验收发现）
   ① F-5 AC3：搜索含标签 —— 提示文案的样式
   ② F-5 AC6：手机**单列** —— 列表卡改横向行卡
   ③ F-11 AC4：流水按类型筛 —— 筛选栏间距
   ★ 色值一律引用 tokens.css（本段 0 处写死色值）；两套主题自动生效。
   ========================================================================== */

/* ---------- ① 搜索提示（与"显示 N / M 份"同一行）---------- */


/* ---------- ③ 流水类型筛选栏 ---------- */


/* ---------- ② 手机单列：横向行卡 ----------
   ★ 不是把两列并成一列就完事：3/4 的封面在整屏宽下会变成一张巨图（PRD F-5 AC6 要的是"手机单列"，
     不是"把封面放大到一屏"）。所以 ≤600px 改成**行卡** ——
     封面在左（固定 92px 宽、仍守 3/4 比例 ⇒ 123px 高），标题/学科·年级·版本/定价在右。
     行与行之间用一条发丝线分隔（沿用制图世界的表格线 --navy-line），不再有卡间大留白。 */
.rowmeta{display:none}
@media (max-width:600px){
  .grid{grid-template-columns:1fr;gap:0}

  .grid>.sheet{
    display:grid;grid-template-columns:92px minmax(0,1fr);gap:14px;align-items:center;
    padding:13px 0;border-bottom:1px solid var(--navy-line);
  }
  .grid>.sheet:first-child{border-top:1px solid var(--navy-line)}

  /* 版面（plate）保持 3/4；行卡不做整块上浮（会顶到相邻行），改用轻微缩放表达按下 */
  .grid .plate{aspect-ratio:3/4}
  .grid .sheet:hover .plate{transform:none;box-shadow:var(--plate-shadow)}
  .grid .sheet:active .plate{transform:scale(.98)}

  /* 92px 宽的版面里塞不下学科角标与底栏 ⇒ 隐藏，同一信息改由右侧 .rowmeta 承载
     （display:none 同时把它们从无障碍树里摘掉，不会被重复朗读） */
  .grid .plate-top,
.grid .plate .block{display:none}

  .grid .sheet .meta{margin-top:0;padding:0;display:grid;gap:7px;align-content:center}
  .grid .sheet .meta h3{font-size:14px;line-height:1.45;-webkit-line-clamp:3;line-clamp:3}

  .grid .rowmeta{display:grid;gap:7px}
  .rm-line{font-size:11.5px;color:var(--ink-3);letter-spacing:.02em}
  .rm-buy{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
  .rm-price{font-size:14px;font-weight:700;color:var(--accent-text);font-variant-numeric:tabular-nums;letter-spacing:-.01em}
  .rm-price b{font-weight:700}
  .rm-owned{
    font-size:11px;color:var(--ink-2);border:1px solid var(--hairline);
    border-radius:var(--r-chip);padding:1px 8px;white-space:nowrap;
  }
}

/* ============================================================================
   20260921 · 后台分类管理：层级标尺（制图尺寸链）+ 可折叠分组（V4）
   用户 2026-09-21 拍板：V3「层级＝坐标」+ 折叠。参考稿 _sketches/categories/04-ruler-collapsible.html
   ★ 只作用于 .cat-tree（分类管理的三张维度表），不触碰其它后台表格与既有后台规则。
   ★★ 刻度位与行缩进【同源】：--lv1/--lv2/--lv3 只在此定义一次，刻度与行都引用它。
      （"两处各写一套数字"正是本次改造要解决的问题，实现里不能再犯。）
   ★ 本段 0 处写死色值，全部引用 tokens.css 既有令牌；深/浅两套主题自动生效。
   ★ 折叠是纯客户端增强（脚本在模板的 scripts 块里）：无 JS 时行全部可见、表单照常提交。
   ========================================================================== */
.cat-tree{--lv1:26px;--lv2:78px;--lv3:130px}

/* ---------- 层级轴：表头正下方的制图尺寸链 ---------- */
.cat-tree tr.axis th{
  height:52px;padding:0;background:transparent;position:static;
  border-bottom:1px solid var(--navy-line);text-transform:none;letter-spacing:normal;
}
.cat-tree .ruler{position:relative;height:52px;width:100%}
.cat-tree .axisline{position:absolute;left:0;right:0;top:36px;height:1px;background:var(--cyan-soft);opacity:.55}
.cat-tree .tick{position:absolute;top:22px;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:3px}
.cat-tree .tick .lk{font-family:var(--mono);font-size:10.5px;color:var(--ink-3);letter-spacing:.08em}
.cat-tree .tick .ln{width:1px;height:14px;background:var(--cyan-soft);opacity:.7}
.cat-tree .tick .lv-dot{width:5px;height:5px;border-radius:50%;background:var(--cyan);margin-top:-1px}
.cat-tree .tick .lab{position:absolute;top:-2px;font-size:11.5px;color:var(--ink-2);white-space:nowrap;transform:translateY(-100%)}
.cat-tree .tick[data-lv="1"]{left:calc(var(--lv1) + 11px)}
.cat-tree .tick[data-lv="2"]{left:calc(var(--lv2) + 11px)}
.cat-tree .tick[data-lv="3"]{left:calc(var(--lv3) + 11px)}
.cat-tree .ruler .cap{position:absolute;right:0;top:44px;font-family:var(--mono);font-size:10px;color:var(--ink-3);opacity:.7;letter-spacing:.1em}

/* ---------- 行：按级别缩进到刻度 + 制图引线 ---------- */
.cat-tree td.lvc{position:relative;padding-left:11px}
.cat-tree td.lvc .lv-lead{position:absolute;top:-1px;bottom:-1px;width:1px;background:var(--cyan-soft);opacity:.34}
.cat-tree td.lvc .lv-hlead{position:absolute;top:50%;height:1px;background:var(--cyan-soft);opacity:.5}
.cat-tree td.lvc .nm{position:relative;z-index:1;display:inline-flex;align-items:center;gap:6px;max-width:100%;color:var(--ink)}
.cat-tree tr[data-lv="2"] td.lvc .lv-lead{left:calc(var(--lv2) + 11px);opacity:.30}
.cat-tree tr[data-lv="3"] td.lvc .lv-lead{left:calc(var(--lv3) + 11px);opacity:.24}
.cat-tree tr[data-lv="1"] td.lvc .lv-lead{display:none}
.cat-tree tr[data-lv="1"] td.lvc .nm{margin-left:var(--lv1)}
.cat-tree tr[data-lv="2"] td.lvc .nm{margin-left:var(--lv2)}
.cat-tree tr[data-lv="3"] td.lvc .nm{margin-left:var(--lv3)}
.cat-tree tr[data-lv="2"] td.lvc .lv-hlead{left:calc(var(--lv2) + 11px);width:14px}
.cat-tree tr[data-lv="3"] td.lvc .lv-hlead{left:calc(var(--lv3) + 11px);width:14px}
.cat-tree tr[data-lv="1"] td.lvc .lv-hlead{display:none}
/* 一级行整行加重（底色 + 名称加粗）+ 非首行上方一条分组线 */
.cat-tree tbody tr[data-lv="1"] td{background:color-mix(in srgb,var(--sunken) 62%,transparent)}
.cat-tree tbody tr[data-lv="1"]:not(:first-child) td{border-top:1px solid var(--navy-line)}
.cat-tree tbody tr[data-lv="1"]:hover td{background:var(--sunken)}
.cat-tree tr[data-lv="1"] .nm input{font-weight:600}

/* ---------- 级标 Ⅰ/Ⅱ/Ⅲ（一级青绿实底，二/三级描边） ---------- */
.cat-tree .lvtag{display:inline-block;font-family:var(--mono);font-size:10px;line-height:1.4;border-radius:3px;padding:1px 4px;flex:none;color:var(--ink-3)}
.cat-tree .lvtag[data-t="1"]{background:var(--teal);color:var(--on-plate);font-weight:600;border:1px solid var(--teal)}
.cat-tree .lvtag[data-t="2"],
.cat-tree .lvtag[data-t="3"]{border:1px solid var(--navy-line);background:transparent}

/* ---------- 折叠（V4）---------- */
.cat-tree .tog{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:17px;height:17px;padding:0;font-family:inherit;font-size:11px;line-height:1;
  border:1px solid var(--navy-line);border-radius:4px;background:transparent;
  color:var(--ink-3);cursor:pointer;
}
.cat-tree .tog:hover{border-color:var(--cyan);color:var(--ink-strong)}
/* ★★ 修复（2026-09-21，用户实测报出「级的视觉退距与轴刻度对不上」）：
   收起按钮**移出 .nm 的内容流**，绝对定位到级标左侧的固定槽位 ——
   ① 一级行的级标不再被 17px 按钮顶右（原先多偏 23px）；
   ② 行的级标级距因此恒等于轴刻度级距（原先 29px vs 52px）；
   ③ 槽位 left = var(--lv1) - 12px = 14px，让开左侧分组脊（5~7px）且与级标保持 6px 间距。 */
.cat-tree td.lvc .tog{position:absolute;left:calc(var(--lv1) - 12px);top:50%;transform:translateY(-50%);z-index:2}
.cat-tree .tog svg{width:11px;height:11px;transition:transform .2s var(--ease)}
.cat-tree tr.grp-open .tog svg{transform:rotate(90deg)}
.cat-tree .tog[disabled]{opacity:.35;cursor:not-allowed}
.cat-tree .tog[disabled]:hover{border-color:var(--navy-line);color:var(--ink-3)}
/* 收起：属于该组的后代行整组隐藏（display:none，控件不动 —— 隐藏 ≠ 失活） */
.cat-tree tr.hid{display:none}
/* 收起后该组没有后续子行 ⇒ 给一级行补一条收束线，不留孤悬的分组线 */
.cat-tree tr.grp-closed td{border-bottom:1px solid var(--navy-line)}
/* 展开时一级行左侧一条纵向分组脊，把整组括起来 */
.cat-tree tr[data-lv="1"].grp-open td.lvc::after{
  content:"";position:absolute;left:5px;top:0;bottom:-1px;width:2px;
  background:var(--cyan-soft);opacity:.22;border-radius:2px;
}
/* 工具条按钮的按下态（沿用 .abtn-q 既有语汇，只补一个状态样式） */
.cat-tools{margin:0 0 0 auto}
.cat-tools .abtn-q[aria-pressed="true"]{background:var(--teal);color:var(--plate-ink);border-color:var(--teal)}

/* ---------- 8.3f：图标尺寸兜底（缺陷修复：保存后出现巨大圆形图标）----------
   ★ 成因：`.ic` 此前没有全局尺寸，尺寸只由各父级选择器各自声明（.bar-btn/.search/.back/.btn/
     .notice/.alert/.rd-wm 共 7 处），后台共用宏 _m.html 的 msg() 输出的 `<div class="anote ok">`
     不在其中 ⇒ 只剩 `img,svg{display:block;max-width:100%}` ⇒ viewBox 1:1 撑满容器，
     #i-info 的 circle 被渲染成 1289×1289 的巨圆（页面被推长 1335px），且只在保存后（?ok=）出现。 */
.ic{width:16px;height:16px}
/* 提示条内图标：与 .notice/.alert 同规格（flex 声明对非 flex 容器无害，保持与那两条一致） */
.anote .ic{flex:0 0 auto;width:16px;height:16px;margin-top:3px;stroke-width:1.8}

/* ---------- 8.3g：提示条图标与文字同行（8.3f 的收口）----------
   ★ 8.3f 把尺寸修好后，`.anote` 仍是 block ⇒ 图标与文字各占一行（45.5px）；
     `.notice/.alert` 是 flex（图标与文字同行），同族提示条应当对齐。
   ★ 但不能对**全部** `.anote` 生效：全站 34 处 `.anote` 里 26 处是纯文字
     `<p class="anote">`（内含 <b>/<code>/<a>/<br>/宏输出的 <span class="abadge">），
     flex 化会把这些内联子元素变成 flex item —— 实测 15 处可见实例中 12 处被改动：
     大字块被拉成一行（49.2→94px）、`<br>` 失效（49→71.5px）、
     `<caption class="anote">` 宽度从 607 塌到 477.8px。
   ⇒ 只对**含 svg.ic 的**提示条生效。`:has()` 不支持的老浏览器自动不匹配，
     回落成改前行为（图标独占一行），不会更差。 */
.anote:has(.ic){display:flex;align-items:flex-start;gap:9px}

/* ---------- 8.3h：图标描边化（缺陷：stroke-width 设了、stroke 却是 none）----------
   ★ 成因：命中 `<svg class="ic">` 的只有 `img,svg{display:block;max-width:100%}`，
     各父级规则只给 width/height/stroke-width —— **没有任何规则设 fill / stroke** ⇒
     SVG 默认 `fill:black` ⇒ 全部图标渲染成实心黑块（i-info 渲染成黑圆），
     与 sprite 自述「统一笔画」正相反；那三档 stroke-width（1.7 / 1.8 / 2px）此前从未生效过。
   ★ 本规则只匹配 `<svg>` 自身：symbol 内自带 `fill="currentColor"` 的子路径（i-half 的半边填充）
     由 presentation attribute 生效，不受影响 —— 上线后以 48px 近照实测 ◐ 仍在。
   ★ 10 个 `f-*` 图形不走 `.ic`（由 .plate .fig / .d-platewrap .fig 自带 fill:none 渲染），
     本规则不波及；上线前后逐实例复量确认一字未变。 */
.ic{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round}

/* ---------- 8.3i：sprite 消费方兜底（修复 .mark logo 实心黑）----------
   ★ ① 症状/成因：`.ic`（8.3h 前）与 `.mark` 这类「只给尺寸 + stroke-width、不给 fill/stroke」
     的写法 ⇒ 符号内的子路径全部落到 SVG 默认 `fill:black` ⇒ 实心黑块，
     同时 stroke-width（.mark 写了 1.7）形同虚设 —— 与 8.3h 修的 `.ic` 是同一个病。
     漏网原因：8.3h 的普查只按 class 名单（`.ic`）走，而 sprite 的消费方是一组**会新增**的 class，
     每个新用途都是一次静默漏网机会 ⇒ 本次改为**按「<svg> 是否内含 <use>」判定**。
   ★ ② 规则：sprite 消费方一律由本规则统管 —— 新加消费方**不必、也不许**再各自声明 fill/stroke，
     只声明尺寸 / 颜色（color）/ stroke-width 即可。
     ★ 例外（本规则不覆盖、也不该覆盖）：`svg.tex` 是 `<rect fill="url(#pattern)">` 的载体、**不含 `<use>`**，
       其自身 fill 不参与绘制（实测底纹正常）；若给它补 stroke 反而会凭空多出一圈描边。
       将来要兜「非 <use> 的内联 svg」须逐案判定，不可一刀切。
   ★ `.ic`（8.3h）那条保留：语义重复但不冲突 —— class 规则特异性更高，继续管尺寸/颜色。 */
svg:has(use){fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round}
/* 兼容垫片：不支持 `:has()` 的老浏览器（Firefox<121 等）至少让已知的 logo 类走线条渲染 */
.mark{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round}

/* ---------- 顶栏「首页」导航项（F-10 AC7 · v1.17）----------
   ★ 极窄屏（<480px）隐藏本项。依据是实测：该宽度下顶栏固有宽度（logo 111 + 首页 47 +
     登录 47 + 注册 47 + 换肤 71 + 间距 40 + 左右留白 64 ≈ 427px）已超过 390px 视口，
     `topbar-in` 是 flex 单行 ⇒ 各项被压到 ~43px 且**两字标签折成两行**（390px 实测：
     「首页」「我的」「浅色」全部折行）。隐去本项后既有各项恢复自然宽度、且不横向溢出（AC4）。
   ★ 此宽度下回首页仍有两条入口：全站可点的 logo、阅读器页顶栏的「回首页」（该条常在）。



/* ---------- 后台收窄①（2026-09-22）：卡密生成页 5 个档位按钮排成一行 ----------
   实测成因：`.ck-quick` 原先没有任何样式，而它所在的 `.fld` 在 `.aform-row` 里只有
   `flex:0 1 200px`（约 200px 宽），5 个「N 元」按钮各约 50px ⇒ 必然折成两行。
   处置：① 只给「面额」这一个字段放宽 basis（新增 .fld-face，不碰其他表单的 .fld）；
        ② 把这组辅助按钮排成不换行的一行，并轻微收紧内边距与字号。
   ★ 不改 `.abtn-q` / `.aform-row` 本身 ⇒ 后台其他页面不受影响。 */
.aform-row .fld-face{flex:1 1 280px}
.ck-quick{display:flex;flex-wrap:nowrap;gap:6px;margin-top:6px}
.ck-quick .abtn-q{padding:3px 7px;font-size:11.5px;line-height:1.4;white-space:nowrap}

/* ---------- 后台收窄②（2026-09-22）：讲义列表 slug 列（12 位）----------
   目标：列更窄，但★**不截断**（slug 要完整、可复制）。
   做法：缩字号 + 允许在字符间断行；不覆盖别处的 `.atbl td{white-space:nowrap}`。
   ★ 特异性 (0,2,1) 高于 `.atbl .mono`(0,2,0) ⇒ 字号以本条为准，等宽字体仍由 .mono 提供。 */
.atbl td.slug-c{font-size:11px;letter-spacing:.02em;white-space:normal;word-break:break-all;max-width:88px}

/* ---------- 前台顶栏「个人中心」按钮化（PRD F-10 AC8 · v1.19，2026-09-22）----------
   起因：用户意见「前台的『我的』改名为『个人中心』，并增加按钮效果」。
   做法：该项**直接复用既有 `.bar-btn` 词汇** —— 常态描边（1px navy-line + r-chip 圆角 + 5px 11px 内边距）
         + `:hover` 强调色（色转 ink、描边转 cyan）+ `:active` 按下反馈（scale .97）
         ⇒ 与「换肤」按钮同一套尺寸/圆角/内边距语汇，视觉同族、深浅主题一致。
   本类只补 `<a>` 特有的两点差异：
     · 去下划线（.bar-btn 是给 <button> 写的，不带这一条）
     · 四字不换行（否则极窄屏会被拆成「个人/中心」两行）


/* 极窄屏降级（2026-09-22 总控拍定方案 a）：≤374px 时把「换肤」按钮**只留图标**，
   ★ 语义不丢：该按钮自带 `aria-label="切换到浅色主题"` 与 `title`（图标为日/月半圆，无障碍名仍完整）；
   ★ 只隐藏它自己的文字标签 `<span data-theme-label>`，且用 `.topbar-in` 限定作用域
     ⇒ 后台顶栏（`.abar-in` 结构、同样带 data-theme-label）**不受影响**。
   ★ 由此省下 ~25px 后，「个人中心」恢复**标准尺寸**（12.5px / 5px 11px，与换肤同族），不再压缩。
   实测（含桌面滚动条 10px）：一行所需 = logo 111 + 余额 47 + 个人中心 74 + 换肤(带标签) 71 + 4×gap 40 = 343px；
   容器可用宽 = vw - 10(滚动条) - 2×16(gutter) = vw - 42 ⇒ vw ≥ 385 才放得下 ⇒ 取 **389px** 作断点
   （把 385–389 这段"0~4px 余量"的风险区间也纳入只留图标的处置；390px 实测余 5px、无折行 ⇒ 从 390 起恢复标签）。
   ★ 2026-09-22 品牌串改版（F-10 AC9）后此断点放宽到 **419px**：品牌主名 11.5px 时整行需
     139+47+74+71(换肤带标签)+4×gap40 = 371 ≤ innerW ⇒ viewW ≥ 413；取 419 留 6px 余量，
     否则 390–419 会去挤换肤按钮（实测被压到 48px ⇒ 相邻元素变形，违反 F-10 AC9④）。
   ★ 由此省下 ~32px 后，「个人中心」恢复**标准尺寸**（12.5px / 5px 11px，与换肤同族），不再压缩。
   360px 实测：可用 318px，一行 = 111+47+74+39 + 30 = 301 ≤ 318 ✓（余 17px）。 */


/* ---------- F-10 AC9（v1.20）：前台品牌串「Lv.Lab Handouts Studio | 专注讲义」的响应式 ----------
   优先级（PRD F-10 AC9）：① ≥768px 完整串（主名 + `|` + 副标）；② 480–767px 隐藏副标、主名必须完整；
   ③ <480px 主名可降到站内**既有**字号档（15/13.5/12.5/11.5px）；④ 任何宽度不溢出、不折行、不压变形相邻元素。
   ★ 只作用于新增的 .brand-main / .brand-sub 两个钩子类；`.brand .word` / `.brand .sub` 定义本身未动。
   ★ 品牌名一律 `nowrap` ⇒ 任何宽度都不会被折行/截断（放不下时宁可溢出，且按 F-10 AC9⑤ 上报）。 */
/* ★ 实测（本机 Chrome，粗体，含 10px 桌面滚动条；viewW=视口宽、innerW=viewW-42）：
     主名「Lv.Lab Handouts Studio」宽度 = 15px:181 ｜ 13.5px:163 ｜ 12.5px:151 ｜ 11.5px:139
     品牌块 = 图标 22 + 间距 8 + 主名 (+ 副标 62 + 间距 8)；顶栏子项间 gap 10。
     非首页项「首页」在 <480px 已由既有规则隐藏；换肤按钮 ≤389px 只留图标(39)、否则 71。
   分层与算式（全部来自实测，非估算）：
     · ≥768px  副标可显示：需 100+181+47+47+74+71+50 = 570 ≤ innerW ⇒ viewW ≥ 612 ✓（768 实测无溢出）
     · ≤767px  隐藏副标（PRD F-10 AC9② 允许）
     · ≤541px  带「首页」时 15px 主名需 viewW ≥ 542 ⇒ 541 起主名降到最低既有档 11.5px(139) 并去掉图标
               ⇒ 139+47+47+74+71+50 = 428 ≤ 438(480px) ✓
     · ≤389px  换肤只留图标（上一轮已定）⇒ 390px 起：139+47+74+39+40 = 339 ≤ 348 ✓
     ★ 360px：339 > 318(innerW) ⇒ 差 21px，按 PRD F-10 AC9⑤ **上报总控**，不缩写/不隐藏品牌名。 */
.brand-sub{white-space:nowrap}
@media (max-width:767px){ .brand-sub{display:none} }

/* ★ 仅极窄屏（≤540px）：只动"容器属性 + 总控上轮新增元素的内边距"，不碰余额块与其它元素的内容/字号
   · 顶栏子项间距 10px → 6px（容器属性）
   · 「个人中心」内边距 5px 11px → 4px 8px（★ 内容与字号未动）
   · 换肤按钮只留图标（文字 span 隐藏；图标/aria-label/title 均在）
   ★ 断点由 419 放宽到 540 的原因（2026-09-22 实测）：总控核定"品牌图标 ≥420px 显示"后，
     420–520 这一段多出 图标 22 + 间距 8 = 30px，实测把**换肤按钮**挤到 43.5px（自然 71）
     ⇒ 违反 F-10 AC9④「不得压变形相邻元素」。这三条是现有授权手段里唯一能让位的组合。 */

/* ★ 品牌图标仅 ≤419px 隐藏（≥420px 一律显示，含 480/560/768/1280 —— 总控 2026-09-22 核定） */

/* ★ 仅 ≤380px 的专用下限档（站内既有最低档 11.5px；总控 2026-09-22 特批 11px，仍差时授权 10.5px）
   实测（5 位余额 99999）：≤380px 一行为 品牌主名 + 余额 67.6 + 个人中心 68 + 换肤 39 + 4×gap6 24；
   11px 主名 ≈132.9 ⇒ 需 331.5 ≤ innerW ⇒ viewW ≥ 374（故 374–380 用 11px）；
   10.5px 主名 ≈126.9 ⇒ 需 325.5 ⇒ viewW ≥ 368（故 ≤367 用 10.5px）。 */

/* ★ 仅 ≤380px：顶栏子项间隙 6px → 4px（容器级属性；不动任何元素的内容/字号/内边距）
   这是"不缩写品牌名、不隐藏品牌块、不形变既有元素、字号不跌破 10.5px"前提下最后一个杠杆。
   实测（360px + 5 位余额）：品牌 126.8 + 余额 67.6 + 个人中心 68 + 换肤 39 + 4×gap4 16 = 317.4 ≤ innerW 318 ✓
   ★ 边界提示：360px+5 位余额下**余量仅 0.6px**（spacer=0.6）⇒ 这是本布局的极限点，
     再动顶栏任何元素的宽度/内边距/字号前，必须重跑 360px 双余额实测（否则会静默挤压换肤按钮）。 */



   `admin_session` 是与前台会话互相独立的第二套 cookie；后台登录过、前台未登录时，
   它会静默把阅读器放行到「完整正文」，让人误判成"预览失效"（用户实测反馈）。
   ⇒ 凡由管理员会话拿到的完整正文，正文上方明写「管理员视图 / 完整正文 / 未购访客只看前 N 节」。
   ★ 定向追加，不改任何既有规则；配色只用站内 token（深海军蓝 + 强调亮青），不引入新色、不越 DESIGN.md 禁区。
   ★ 位于 iframe **之上**（文档流内）⇒ 不遮挡正文首屏、不位移正文内容；正文页内的同族标注由


/* ---------- ★ §5.8（v1.30）原件下载入口：顶栏按钮 + 右侧浮动钮 ----------
   定向追加，不改任何既有规则；配色只用站内 token（--teal / --cyan / --navy-* / --ink-*），不引入新色。 */
/* 顶栏最右的「下载 PDF」（复用 .btn-quiet 的描边观感，补图标）。
   ★ 故意**不用** margin-left:auto：自动外边距会参与 flex 换行判定，实测 480 宽时把
     下载钮挤到第二行；右端对齐交给 .rd-title 的 flex-grow 自然吸收余量即可。 */
.rd-dl{display:inline-flex;align-items:center;gap:7px;white-space:nowrap}
.rd-dl .ic{width:15px;height:15px;stroke-width:1.9}
/* 浮动钮：★ v1.31 起**常显**（初始即 opacity:1 / visible）。
   原「滚动 ≥260px 淡入」的门槛建立在错误的滚动容器上（阅读器里真正滚动的是讲义 iframe，
   外层页面几乎不滚）⇒ 门槛永不满足 ⇒ 用户看不到浮钮。可见性不再与页面滚动量挂钩。 */
.rd-float{
  position:fixed;right:16px;bottom:88px;z-index:40;
  display:flex;flex-direction:column;gap:10px;
  opacity:1;visibility:visible;
}
.rd-fb{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-width:44px;height:44px;padding:0 16px;
  border:1px solid var(--navy-line);border-radius:var(--r-chip);
  background:var(--navy-deep);color:var(--ink-2);
  font-size:13.5px;font-family:inherit;font-weight:600;line-height:1;
  text-decoration:none;cursor:pointer;
  transition:color .25s var(--ease),border-color .25s var(--ease),background .25s var(--ease);
}
.rd-fb .ic{width:16px;height:16px;stroke-width:1.9}
.rd-fb:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
/* ≤767px：38px 图标圆钮、固定右上（文字隐藏，语义交给 aria-label/title）；
   同档把阅读器顶栏单行化：标题 flex 基准归零 + 下载钮也收成 38px 图标钮
   （不这样做，360/390/480 会因为各项的**假定宽度**超行而折行 —— flex-wrap 按 flex-basis 判行） */
@media (max-width:767px){
  .rd-float{right:12px;bottom:auto;top:calc(var(--bar-h) + 12px)}
  .rd-fb{width:38px;height:38px;min-width:38px;padding:0;border-radius:50%}
  .rd-fb .rd-fb-tx{display:none}
  .rd-fb .ic{width:17px;height:17px}
  .rd-bar-in{gap:8px}
  .rd-title{flex:1 1 0}
  .rd-dl{width:38px;height:38px;padding:0;gap:0;justify-content:center;border-radius:50%}
  .rd-dl .rd-dl-tx{display:none}
}


/* ---------- 品牌块改用真 logo 图（v1.43 起 / v1.45 定稿 · 用户 2026-09-27）----------
   ★ 演进：v1.43 只把「图标」换成真图；v1.45 连「Lv.Lab」文字一起去掉 ——
     该字标是**专用字体**，系统字体排版无法忠实复现（用户原话：「LvLab 也是专用字体」）
     ⇒ 品牌块 = 横版 logo 图（烧瓶 + Lv.Lab 字标）+ 副标文字。
   ★ 底衬 .mark-plate：顶栏/页脚底色都是 --navy-deep，深色主题下 = #101d29（深），
     而 logo 轮廓与字标是深海军蓝 #00152a（同色系）⇒ 直接贴会隐形。
     始终浅色的铭牌底衬在两套主题下都成立：深色主题下它是有意的铭牌块，
     浅色主题下它是 #f7fafb 的淡白铭牌 —— 实测比栏色 #eef2f4 略浅（★ 不是隐形、也不是零装饰：两套主题下都是一块可辨的铭牌）。
   ★ 尺寸按图内比例校准：源图 218×69、字标墨高占 36/69 ⇒ 图渲染 24px 时字标墨高≈12.5px，
     与顶栏其它 15px 文字同级。★ 顶栏品牌块总宽刻意与改造前（≈171px）持平，
     以免 360/390px 既有降级档失效。 */
.mark-plate{
  display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  height:30px;padding:0 9px;border-radius:6px;background:#f7fafb;
  box-shadow:0 0 0 1px var(--navy-line);
}
.mark-plate img{display:block;height:24px;width:auto}
.foot .mark-plate{height:27px;padding:0 8px;border-radius:5px}
.foot .mark-plate img{height:21px}
.abrand .mark-plate{height:26px;padding:0 8px;border-radius:5px}
.abrand .mark-plate img{height:20px}


/* ---------- 封面右上角的品牌标（v1.49 起）：真 logo 纯图标 + 浅色铭牌 ----------
   ★ 原为内联 SVG 手绘烧瓶 `#i-seal`（描边式），已废弃：全站品牌标一律用真 logo 图。
   ★ 浅色铭牌底衬理由同 .mark-plate：封面底是深色版面（蓝晒负片/暗调照片），而 logo 轮廓是
     深海军蓝 #00152a ⇒ 直接贴会看不出 ⇒ 始终给 #f7fafb 淡白铭牌。
   ★ 尺寸对齐旧标（卡片 17px / 详情页 20px 细描边）：真图墨迹更重，故图标取小一档 + 铭牌留白，
     整体视觉体量与旧标持平（不是放大、也不是隐形装饰）。 */
.seal-plate{
  display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  height:20px;padding:0 4px;border-radius:4px;background:#f7fafb;
  box-shadow:0 0 0 1px var(--navy-line);
}
.seal-plate img{display:block;height:14px;width:auto}
/* 详情页大封面：整体大一档（旧标 20px） */
.d-platewrap .ptop .seal-plate{height:24px;padding:0 5px;border-radius:5px}
.d-platewrap .ptop .seal-plate img{height:17px}

/* ---------- 竖版组合（v1.44 起 / v1.45 定稿）：只用于**后台登录页**顶部居中 ----------
   ★ 横版图不再单列大图用在 auth 四页 —— 顶栏品牌块已经是同一张横版图，同屏两次＝重复。
   ★ 后台登录页是例外：该页**不渲染**后台栏的品牌块（见 admin/base.html 的 nav_active 判断），
     故这里放竖版（烧瓶在上 + 字标在下），一个页面只有一个品牌落款。
   ★ 同样加浅色铭牌底衬：该页页面底在深色主题下是 #1a2a3a（深），深蓝 logo 直接贴会看不见。 */
.alock{
  display:flex;justify-content:center;margin:0 auto 14px;width:max-content;max-width:100%;
  background:#f7fafb;border-radius:10px;box-shadow:0 0 0 1px var(--navy-line);padding:12px 16px;
}
.alock img{display:block;height:88px;width:auto}
@media (max-width:479px){ .alock img{height:78px} .alock{padding:10px 13px} }
/* ---------- 讲义表单 · 标签选择器（v1.52 起）----------
   目的（用户 2026-09-28）：把**现有标签**与**近期使用**罗列出来、点击即选，避免遗忘与近似标签重复。
   ★ 视觉语言复用后台既有的 `.bar-chip`（胶囊 + 描边），不引入新调色板。
   ★ 已选中 = 置灰 + 禁用（两组都置灰），避免重复加入。
   ★ `b` = 该标签已关联的讲义数（小字），不给标签名加权 ⇒ 名字仍是最强可读项。 */
.tagpick{margin-top:9px;padding:9px 11px;border:1px solid var(--navy-line);border-radius:var(--r-chip);
  background:var(--field-veil)}
.tagpick-head{font-size:11.5px;color:var(--ink-3);letter-spacing:.06em;margin:0 0 6px}
.tagpick-head:not(:first-child){margin-top:11px}
.tagpick-chips{display:flex;flex-wrap:wrap;gap:6px}
.tagpick-note{font-size:11.5px;color:var(--ink-3);margin-top:7px}
.tagchip{appearance:none;display:inline-flex;align-items:center;gap:5px;cursor:pointer;
  border:1px solid var(--navy-line);border-radius:var(--r-chip);padding:3px 10px;
  font:inherit;font-size:12.5px;color:var(--ink-2);background:transparent;line-height:1.5}
.tagchip b{font-size:10.5px;color:var(--ink-3);font-weight:600}
.tagchip:hover:not(:disabled){color:var(--ink);border-color:var(--accent-text);background:var(--field-veil)}
.tagchip.is-recent{border-color:var(--accent-text)}
.tagchip.is-on,
.tagchip:disabled{opacity:.5;cursor:default}
.tagchip.is-on{border-style:dashed}
.tagchip:focus-visible{outline:2px solid var(--accent-text);outline-offset:2px}
@media (max-width:479px){ .tagchip{font-size:12px;padding:3px 8px} }

/* ★ v1.68 专辑（后台）：候选讲义多选列表。
   讲义标题长短不一，用**单列换行**排列（不用多列），否则长标题会把格子撑歪。
   限高 + 纵向滚动：候选多时不让它把「完成」按钮顶到屏幕外。 */
.apick{margin-top:6px;max-height:280px;overflow:auto;border:1px solid var(--navy-line);
  border-radius:4px;padding:8px 10px;background:var(--field-veil)}
.apick label{display:block;padding:3px 0;line-height:1.5;cursor:pointer}
.apick label:hover{color:var(--ink)}
@media (max-width:479px){ .apick{max-height:220px} }

/* ★ v1.68 专辑（前台首页分组 · B 版轻量胶囊行）
   —— 与筛选栏胶囊同一套语汇；组标题行**不占高度**（胶囊 + 上下留白），
      分组后首屏仍能多露出半排卡片。辑名可点 ⇒ `/?album=<id>`。 */
.alb-head{display:flex;align-items:center;gap:10px;margin:2px 0 12px}
.alb-head:not(:first-child){margin-top:26px}
.alb-chip{display:inline-flex;align-items:baseline;gap:8px;font-size:14px;font-weight:700;
  padding:6px 15px;border-radius:var(--r-chip);background:var(--teal);color:#fff;
  text-decoration:none;line-height:1.4}
a.alb-chip:hover{filter:brightness(1.1)}
.alb-chip.plain{background:transparent;color:var(--ink-2);border:1px dashed var(--navy-line);font-weight:600}
@media (max-width:479px){ .alb-chip{font-size:13px;padding:5px 12px} }