/* 马步野的语文课堂 —— 手机优先样式表
   ─────────────────────────────────────────────────────────────
   配色来源：《经典人物资料卡.pptx》主题色板 + 实际用例统计（49 张幻灯片）。
   PPT 原色          → 网页角色
   #232323 近黑       → --ink     正文（压到 #241E1C，带一点暖调，纯黑不刺眼）
   #4E3B30 深棕       → --ink-2   次要文字（原色直接可用）
   #A5644E 陶土棕     → --ink-3 / 用户侧色块 / 竹青替代色
   #F5232D 正红       → --red-vivid  装饰性亮红（页面渐变、焦点环）
   #C52129 正红       → --red     主红：所有承载文字的红色（按钮、标题、强调）
   #D2232D 深正红     → --red-deep / 外链渐变亮端
   #F8B308 金橙       → --gold    主金：金线、标记下划线
   #FFD764 浅金黄     → --gold-soft / --gold-on-red（红底上的金字）
   #AD1F1F 暗红       → --red-dark  外链渐变暗端、按下态
   #FFFF00 亮黄       → --mark-light 考点高亮最亮端
   #F5C8B1 浅桃       → --peach / --clay-tint（降明度后成为整站底色）
   #FBA7AB 浅粉红     → --rose    次级底面 / 导航条 / 输入框面
   #FFFFFF 白         → --card / --on-red
   ─────────────────────────────────────────────────────────────
   底色转译：PPT 里 #F5C8B1 是主背景（49/49 张全有），但它是饱和度不低的
   浅桃色，整屏铺开会让长文阅读疲劳，且次要文字对比度只有 4.24:1。
   做法是保色相、提明度、降饱和：F5C8B1 → #FCF1EC（页面底），
   卡片面用 #FFFCF9，并把「页面底 / 卡片面 / 分隔线」三层拉开，
   使暖粉的调性还在，但正文可以长时间阅读。
   ─────────────────────────────────────────────────────────────
   关键组合对比度（WCAG 2.1，从本表色值实算，非目测）：
     正文    #241E1C / #FCF1EC = 14.82:1（AAA）  #241E1C / #FFFCF9 = 16.08:1（AAA）
     正文    #241E1C / #FBDDD6 = 12.85:1（AAA）  #241E1C / #F6E3D9 = 13.24:1（AAA）
     次要    #6E5A52 / #FCF1EC =  5.83:1（AA）   #6E5A52 / #FFFCF9 =  6.33:1（AA）
     三级    #78635C / #FCF1EC =  5.07:1（AA）   #78635C / #FFFCF9 =  5.50:1（AA）
              #78635C / #F6E3D9 =  4.52:1（AA，最紧的一处，故三级色不再压浅）
     主红    #C52129 / #FFFCF9 =  5.67:1（AA）   白字 / #C52129     =  5.80:1（AA）
     用户侧  浅桃底 #F7C9C1 上 #66544D = 4.79:1（AA）
     考点    深棕 #5A3A00 / 金底 #FFE79B→#FFD764 = 8.41→7.43:1（AAA）
     外链     白字 / 渐变 #A81D24→#C52129 = 7.33→5.80:1（AA）
     焦点环   #F5232D / #FCF1EC =  3.67:1（非文字 UI，≥3:1 即达标）
   红系的用法分界：#F5232D 对白底只有 3.98:1、对白字只有 4.07:1，达不到 4.5:1，
   因此它一律不承载小字号文字 —— 只做渐变、金线对照与焦点环；所有「红字」
   与所有红底白字块都走 #C52129（accent6），这是它同时满足两种方向的原因。
   分隔线、桌面外框、卡片面之间是「面」的层次（1.08–1.19:1），不承担信息传达，
   故不适用 3:1 的 UI 组件对比度要求；卡片的可辨识靠 1px 边框 + 阴影完成。 */

:root {
  /* ── 文字三级 ───────────────────────────────────── */
  --ink: #241E1C;          /* 正文（PPT dk1 #232323 转暖压深） */
  --ink-2: #6E5A52;        /* 次要说明（PPT dk2 #4E3B30 提亮以适配浅底） */
  --ink-3: #78635C;        /* 三级/占位符（PPT accent2 #A5644E 压深达 AA） */

  /* ── 红系 ───────────────────────────────────────── */
  --red: #C52129;          /* 主红：唯一承载小字号的红色 */
  --red-deep: #D2232D;     /* 深正红：渐变的亮端 */
  --red-dark: #A81D24;     /* 暗红：按下态、渐变暗端、外链底色 */
  --red-vivid: #F5232D;    /* 亮红：只做装饰/焦点环，不做文字色 */
  --red-soft: #FBE3E2;     /* 红色淡底：标签、角标 */
  --red-line: rgba(197, 33, 41, .22);
  --red-glow: rgba(197, 33, 41, .12);

  /* ── 金系（考点高亮）────────────────────────────── */
  --gold: #F8B308;         /* 主金：金线、下划线 */
  --gold-soft: #FFD764;    /* 浅金黄：金底暗端 */
  --gold-light: #FFE79B;   /* 金底亮端 */
  --mark-light: #FFFF00;   /* PPT 亮黄：荧光笔本色，作为金色高亮的「最亮端」备选，
                              当前用的是更柔和的 #FFE79B（避免电光黄割裂整体调性）。
                              深棕字对它 9.59:1，需要更醒目的标记时直接换上即可 */
  --mark-ink: #5A3A00;     /* 金底上的深棕字（对金底 7.43–8.41:1） */
  --gold-on-red: #FFE7A8;  /* 红底上的金字，对主红 4.76:1（#FFD764 只有 4.18:1，不达标）。
                              当前版式没有「红底金字」的位置，作为备用令牌保留，
                              以便日后加红色横幅时不必重新验算 */

  /* ── 暖粉底系（PPT 主背景色转译）────────────────── */
  --paper: #FFFCF9;        /* 页面底：两侧那一圈空白。现与 --card 同值；
                              中间 720px 那条走 --bar(#FCF1EC)，比两侧略偏粉。 */
  --card: #FFFCF9;         /* 卡片面 / 导航条 / 输入框面 */
  --rose: #FBDDD6;         /* 次级底面（原 FBA7AB 浅化） */
  --peach: #F6E3D9;        /* 糊块底：引用块、思路梳理 */
  --clay-tint: #F7C9C1;    /* 头像/序号块底（原浅桃 F5C8B1 作小面积色块直接可用） */
  --clay-ink: #66544D;     /* 头像字：陶土棕压深，对 clay-tint 4.79:1 */
  --outline-soft: #F6E3D9; /* 思路梳理块底（原竹青淡底，改用陶土棕系） */
  --outline-ink: #7A4A38;  /* 思路梳理块文字 */

  /* ── 结构层 ─────────────────────────────────────── */
  --line: #F0DCD3;         /* 分隔线：暖粉调，与底色、卡片面可分辨 */
  --on-red: #FFFFFF;       /* 红块上的文字（PPT lt1） */
  --media-bg: #241E1C;     /* 视频未加载底色（替代纯黑） */
  --scrim: rgba(36, 30, 28, .45);        /* 弹层遮罩 */
  /* 键盘挡住的高度（px），由 JS 按 visualViewport 实时写。
     iOS / 微信的 WebView 不会把布局视口缩掉，键盘是**盖在页面上**的，
     固定在底部的东西就会藏在键盘后面（老师实测：点输入框，输入法跳出来压住了输入框）。
     所以这里由我们自己把输入条、底栏、内容区一起抬到键盘之上。 */
  --kb: 0px;
  --viewer-bg: rgba(36, 30, 28, .96);    /* 大图查看器底 */
  --bar: #FCF1EC;          /* 720px 内容列这一整条的底色：顶栏 / 内容区 / 输入区 /
                              底栏 都用它，不透明铺底。不跟着 --paper 走，
                              也不再透出后面的内容。要调这一整条只改这里。 */
  --glass-card-solid: rgba(255, 252, 249, .995);   /* 抽屉头部 .drawer-head 用 */
  --glass-hi: rgba(255, 255, 255, .72);  /* 玻璃条上沿高光 */

  /* 红块/暗底上的白色叠加层。白色叠加层按定义没有固定色值可提取，
     统一收在这里，避免 .play / .viewer-close / .brand-mark 各写一套。 */
  --on-red-18: rgba(255, 255, 255, .18);
  --on-red-14: rgba(255, 255, 255, .14);
  --on-red-16: rgba(255, 255, 255, .16);

  --shadow-ink: 78, 50, 40;              /* 阴影基色（暖棕，供 rgba(var(...), α) 使用） */

  --shadow: 0 6px 24px rgba(var(--shadow-ink), .08);
  --shadow-sm: 0 2px 10px rgba(var(--shadow-ink), .06);
  --shadow-lg: 0 18px 50px rgba(0, 0, 0, .45);   /* 查看器大图投影（唯一中性黑阴影） */
  --inset-sm: inset 0 1px 2px rgba(var(--shadow-ink), .04);

  --topbar-h: 56px;
  --tabbar-h: 64px;        /* 纯文字导航只需容纳一行字，64px 兼顾触摸手感与内容高度；改这里 .scroll / .composer 会同步 */
  --composer-h: 65px;      /* 问答输入条常态高度：1 边框 + 9 + 输入框 46 + 9 ——
                              输入框 46 = 行高 24（16px 字号 × 1.5）+ 上下内边距 20 + 上下边框 2。
                              以前写 58 是按「输入框 40px」估的，实测输入框是 46，于是聊天区
                              被输入条压住 7px（最后一条消息露不全）。改字号就要回来重算这一项。 */
  --radius: 16px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
/* html 铺一层极淡暖粉渐变替代整片浅桃色：既有 PPT 的暖调，长文下也不压眼 */
html, body { margin: 0; padding: 0; height: 100%; }
html {
  scroll-padding-bottom: var(--tabbar-h);
  background: linear-gradient(180deg, var(--paper) 0%, var(--rose) 100%);
  background-attachment: fixed;
  /* ⚠️ 锁住文档本身的滚动：整站外壳（顶栏 / 内容区 / 输入条 / 底栏）都是 fixed，
     文档一点都不需要滚。而**微信内置浏览器**（X5/XWeb）在键盘弹起时会自己去滚页面
     「把输入框带进视野」，fixed 的元素不跟着滚、**光标却是按滚动后的坐标画的** ——
     于是微信里光标被画到页面上方去（老师实测：普通手机浏览器正常、微信里不正常）。
     overflow: hidden 让这个多余滚动无处可去。 */
  overflow: hidden;
}
body {
  background: var(--paper);
  color: var(--ink);
  font: 15px/1.7 -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
        "Microsoft YaHei", "Source Han Sans SC", sans-serif;
  overscroll-behavior-y: none;
  overflow: hidden;
}
button, input, textarea { font: inherit; color: inherit; }
/* iOS 聚焦**字号小于 16px** 的输入框时会把整页放大（`maximum-scale=1` 拦不住），
   表现就是「点一下输入框，画面被放大、光标跑到上面去了」。
   站里留言、便签评论、笔记编辑、搜索、练习填空原来都是 13.5～15px，全踩这条。
   所以在**触屏设备**上把所有会弹键盘的控件统一提到 16px。
   只改字号：各处精心调过的对齐（如练习填空的 33px 缩进、47px 文字列）都是按盒子算的，
   不受字号影响。 */
@media (pointer: coarse) {
  /* ⚠️ 必须 !important：各处输入框都有自己的字号规则（比如
     `.sheet-card .msg-form textarea` 是两条类选择器），比这里的元素选择器更具体 ——
     不加就形同虚设（实测加完留言框还是 14px，量出来才发现）。 */
  input, textarea, select { font-size: 16px !important; }
}
button { border: 0; background: none; cursor: pointer; }
h1, h2, h3 { margin: 0; font-weight: 700; line-height: 1.35; }
p { margin: 0 0 .6em; }
:root { color-scheme: light; }

/* ── 顶栏 ─────────────────────────────────────────── */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; height: var(--topbar-h); z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 14px calc(0px);
  padding-top: env(safe-area-inset-top);
  height: calc(var(--topbar-h) + env(safe-area-inset-top));
  background: var(--bar);
  -webkit-backdrop-filter: saturate(170%) blur(14px);
  backdrop-filter: saturate(170%) blur(14px);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 var(--glass-hi);
}
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .topbar { background: var(--bar); }
}
/* 「我的」页把整条顶栏收起来：这一页的主角是用户名和收藏，logo / 留言信箱 /
   问号在这里只是白占一条高度。其他三个 tab 照旧显示（切页时由 switchView 摘挂）。

   注意：光把顶栏 display:none 是不够的 —— .scroll 是 position:fixed，
   它的 top 由 calc(--topbar-h + safe-area) 顶下去，顶栏没了也不会自己收回来，
   结果就是页面上方留一条 56px 的空白条（本站踩过这个坑）。
   所以这里必须把滚动容器的 top 一并归零；坑外露出来的那条顶栏位置原本还
   承担着「避开刘海」的作用，归零后改由内容的内边距补回来，
   否则第一行字会被刘海/状态栏压住。 */
.topbar.is-hidden { display: none; }
#view-me.no-topbar .scroll {
  top: 0;
  padding-top: calc(14px + env(safe-area-inset-top));
}
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* 朱印：PPT 主红做底，白字 5.80:1 */
.brand-mark {
  width: 32px; height: 32px; border-radius: 9px; flex: none;
  background: linear-gradient(160deg, var(--red-deep), var(--red-dark));
  color: var(--on-red); font-weight: 700;
  display: grid; place-items: center; font-size: 16px; text-indent: -.03em;
  box-shadow: 0 3px 10px var(--red-glow), inset 0 1px 0 var(--on-red-18);
}
.brand-text { display: flex; flex-direction: column; min-width: 0; }
.brand-text strong { font-size: 15.5px; letter-spacing: .3px; }
.brand-text em {
  font-style: normal; font-size: 10.5px; color: var(--ink-3);
  letter-spacing: .1em; margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 通用幽灵圆钮：顶栏「?」与抽屉「返回」共用。
   30px 按触摸目标偏小（Apple HIG 44pt / Material 48dp），统一放大到 40px。
   注意：盒子是 box-sizing: border-box，宽高已经定死，再写 padding 只会向内
   挤内容、不会把可点区域撑大 —— 要扩大命中区只能直接放大盒子或用伪元素外扩，
   这里选前者：40px 已满足「≥40px」，也是 iOS 导航栏返回键的常见直径。
   描边由 --line 换成 --red-line —— --line 对卡片面只有 1.29:1，
   几乎看不见按钮轮廓；--red-line 是主红的 22% 透明度，仍是暖红系。
   字号固定 600，避免「?」这个字符在不同权重下换字形、换宽度。 */
.ghost-btn {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  border: 1px solid var(--red-line); color: var(--ink-2);
  display: grid; place-items: center; font-size: 16px; font-weight: 600;
  background: var(--card);
  transition: border-color .15s, color .15s, background-color .15s;
}
.ghost-btn:active { border-color: var(--red); color: var(--red); background: var(--red-soft); }

/* ── 视图与滚动 ───────────────────────────────────── */
.view { display: none; }
.view.is-active { display: block; }
.scroll {
  /* 本面板的滚动条已在下方设为 width:0（隐藏），不占布局空间，
     因此左右两侧不会因滚动条出现而失衡。 */
  /* 内容区必须有自己的底：原先透明，桌面端会透出外框的深色，
     夹在顶栏与底栏两条浅色玻璃之间就显成三截 */
  background: var(--bar);
  position: fixed; top: calc(var(--topbar-h) + env(safe-area-inset-top));
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--kb, 0px));
  left: 0; right: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 14px 14px 18px;
  scroll-behavior: smooth;
}
/* 问答页底部要让出输入条；输入框最多长到 120px 时也不会盖住最后一条消息
   （#chatScroll 是 index.html 里已有的 id，不需要改结构） */
#chatScroll { padding-bottom: calc(18px + var(--composer-h)); }
#view-chat .scroll { bottom: calc(var(--tabbar-h) + var(--composer-h) + env(safe-area-inset-bottom) + var(--kb, 0px)); }


/* ── 底部导航：纯文字 ──────────────────────────────────
   选中态 = 一块正红色块（用户诉求：一眼看出在哪个板块）。
   色块是 .tab::before 画的，absolute 定位、不占文档流，所以列宽分配、
   文字基线、字号全都不随选中项变化；四个 tab 的色块又是同一个固定
   68×34px 的盒子，切换时只有「哪块亮起来」在变，几何一动不动。
   色块内的三个元素从大到小依次说话：
     ① 整块实心正红（对卡片面 5.67:1，非文字 UI 标记，远超 3:1）
     ② 标签转白（白 / 主红 = 5.80:1，AA）
     ③ 字下一道 12×2px 主金短线（对主红 3.16:1，纯装饰、不承载信息；
        把 PPT 里「红底压一线金」的收尾语言续上，红金仍是唯二强调色）
   触摸区 = 整个 64px 高的 tab 列（约 90px 宽），远大于 44pt 最小点击区。
   注意 --tabbar-h 保持 64px 不变。 */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: var(--kb, 0px); z-index: 25;
  display: flex; align-items: stretch;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--bar);   /* 与顶栏、输入区统一；层次交给 border-top + 阴影 */
  -webkit-backdrop-filter: saturate(170%) blur(14px);
  backdrop-filter: saturate(170%) blur(14px);
  border-top: 1px solid var(--line);
  box-shadow: 0 -1px 0 var(--glass-hi), 0 -10px 22px rgba(var(--shadow-ink), .05);
}
/* 无 backdrop-filter 的旧内核：用近乎不透明的底色兜住，避免文字透上来 */
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .tabbar { background: var(--bar); }
}
.tab {
  position: relative; flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0 4px; color: var(--ink-2); transition: color .16s ease;
}
.tab span {
  font-size: 14px; font-weight: 600; line-height: 1;
  letter-spacing: .14em; text-indent: .14em;   /* 两字标签视觉居中：letter-spacing 会在右侧留空，用同值 text-indent 抵掉 */
  white-space: nowrap;                          /* 360px 窄屏也绝不换行 */
}
/* 选中色块：固定 68×34 的盒子，只有不透明度在动。
   固定尺寸是「不跳动」的关键 —— 四个 tab 无论两个字还是四个字，
   色块都是同一个盒子；盒子本身不参与文档流，标签的字宽也从不参与它的尺寸计算，
   所以切换时列宽、标签基线、字号全都不变（实测：四个 tab 的列盒与标签盒逐像素相同）。
   68 宽是按最长的「独家秘笈」定的：14px + .14em 字距实测 65.8px，再加一点内边距；
   360px 下每列 90px，两侧还剩 11px 余量。
   给 .tab::before 定 z-index: 0、把标签抬到 z-index: 1，
   是为了不依赖「伪元素先于子元素绘制」这个约定，图层关系写成显式的。 */
.tab::before {
  content: ''; position: absolute; z-index: 0;
  top: 50%; left: 50%; width: 68px; height: 34px; border-radius: 9px;
  background: var(--red); box-shadow: 0 3px 9px var(--red-glow);
  transform: translate(-50%, -50%); opacity: 0;
  transition: opacity .18s ease;
}
/* 色块里的那道金线：12×2px，绝对定位、不进文档流。
   关键：定位基准是「列」的 50%（与标签同一条中线），不是色块下沿 ——
   标签高 14px，中线到字底 7px，所以 +12px 时金线正好压在字下 3px、
   距色块底 3px，严格落在色块内部（此前写成 bottom: 5px 是相对 64px 的列，
   金线跑到色块外面去了，实测金线 y837..839 vs 色块底 826.5 就是这么来的）。 */
.tab::after {
  content: ''; position: absolute; z-index: 0;
  top: calc(50% + 12px); left: 50%; width: 12px; height: 2px; border-radius: 999px;
  background: var(--gold);
  transform: translateX(-50%); opacity: 0;
  transition: opacity .18s ease;
}
.tab span { position: relative; z-index: 1; }
.tab.is-active { color: var(--on-red); }
.tab.is-active::before { opacity: 1; }
.tab.is-active::after { opacity: 1; }
.tab:active { color: var(--on-red); }
.tab:active::before { opacity: .72; }
.tab:active::after { opacity: .72; }
/* 色块是 68px 定宽，360px 下每列只有 90px，两侧还剩 11px 余量，
   因此只收字号、不加宽高，保证四个标签都留在一行里、列宽也不被撑动。 */
@media (max-width: 360px) {
  .tab span { font-size: 13.5px; letter-spacing: .08em; text-indent: .08em; }
}

/* ── 通用块 ───────────────────────────────────────── */
.hero { padding: 4px 2px 14px; }
.hero h1 { font-size: 20px; letter-spacing: .5px; line-height: 1.3; }
.hero p {
  margin: 7px 0 0; color: var(--ink-2); font-size: 12.5px;
  letter-spacing: .06em;
}
/* 标题左侧正红短竖：四个板块统一的信息层级语言（15px 粗体对卡片面 5.67:1） */
.section-title {
  font-size: 15px; margin: 22px 2px 8px; padding-left: 10px;
  border-left: 3px solid var(--red); letter-spacing: .04em;
}
.section-note { color: var(--ink-3); font-size: 12.5px; margin: 0 2px 12px; line-height: 1.6; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.pad { padding: 14px; }
.dim { color: var(--ink-3); font-size: 12.5px; }

/* ── 问答 ─────────────────────────────────────────── */
.messages { display: flex; flex-direction: column; gap: 12px; padding-bottom: 8px; }
.msg { display: flex; gap: 8px; max-width: 100%; }
.msg .avatar {
  width: 28px; height: 28px; border-radius: 9px; flex: none; margin-top: 2px;
  display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
/* 配色决定：红系只留给「马老师」一侧，用户侧走浅桃 + 陶土棕，
   这样一屏问答里红色始终只标记一个来源，红的分量不会失控。 */
.msg.user { flex-direction: row-reverse; }
.msg.user .avatar { background: var(--clay-tint); color: var(--clay-ink); }
.msg.ai .avatar { background: var(--red); color: var(--on-red); }
.bubble {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 11px 13px; box-shadow: var(--shadow-sm); max-width: calc(100% - 44px);
  word-break: break-word; overflow-wrap: anywhere;
}
/* 用户气泡改用浅桃底 + 墨色字（12.85:1），不再用满块深色，
   长对话里深色块堆叠会让整页发闷 */
.msg.user .bubble { background: var(--rose); border-color: var(--line); color: var(--ink); border-top-right-radius: 5px; }
.msg.ai .bubble { border-top-left-radius: 5px; }
.bubble h4 { margin: 10px 0 4px; font-size: 14.5px; color: var(--red); }
.bubble h4:first-child { margin-top: 0; }
.bubble ol, .bubble ul { margin: 4px 0 8px; padding-left: 20px; }
.bubble li { margin: 3px 0; }
.bubble strong { color: var(--red); }
.msg.user .bubble strong { color: var(--red); }
.bubble code { background: var(--red-soft); padding: 1px 5px; border-radius: 5px; font-size: 13px; }
.bubble .hr { height: 1px; background: var(--line); margin: 10px 0; }
.think {
  font-size: 12.5px; color: var(--ink-3); background: var(--peach); border-radius: 10px;
  padding: 8px 11px; margin-bottom: 8px; max-height: 92px; overflow: hidden;
  border-left: 3px solid var(--gold-soft);
}
.typing { display: inline-flex; gap: 3px; align-items: center; }
.typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); animation: blink 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: .2s; }
.typing i:nth-child(3) { animation-delay: .4s; }
@keyframes blink { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

.chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 0 6px; }
.chip {
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 14px; font-size: 12.5px; color: var(--ink-2); box-shadow: var(--shadow-sm);
  transition: background .15s, color .15s, border-color .15s;
}
.chip:active { background: var(--red-soft); color: var(--red); border-color: var(--red-line); }

/* ── 便签（后台推送的纸条）────────────────────────────────────────
   层级：容器压在 .msg 之上（消息气泡是普通文档流，z-index:auto），
   所以纸条看起来是「贴在内容上面」。收起来时不是把自己藏到别的元素背后
   ——那是做不到的，元素之间没有真正的遮挡关系——而是**滑出可视区**：
   左边缘留在屏幕里、其余部分滑到屏外。观感正好是「塞到卡片后面，只露一角」，
   而且滑入滑出都能做 transition，比真的叠层级更顺。

   状态：.is-in（浮出）/ .is-out（隐入）/ .is-torn（撕碎，动画完就删）      */
.notes-layer {
  position: fixed; inset: 0; z-index: 25;
  pointer-events: none;            /* 容器不吃点击，只有纸条本身吃 */
}
/* 切到别的页（视频课 / 秘笈 / 我的）：整层收掉，连露出的那一角都不留 */
.notes-layer.is-away { display: none; }
.note {
  position: absolute;
  width: min(78vw, 300px);
  /* 底部 34px 是给绝对定位的「撕掉」留的专用空间（按钮高 21 + 距底 12），
     不然长文本会压到按钮上。 */
  padding: 18px 16px 34px;
  border-radius: 3px;
  pointer-events: auto;
  color: #3a3128;
  font-size: 14px; line-height: 1.75;
  white-space: pre-wrap; word-break: break-word;
  /* 纸感：暖色弱阴影 + 细边框；不要重阴影，不然像卡片不像纸 */
  box-shadow: 0 1px 2px rgba(60, 40, 20, .10), 0 8px 22px rgba(60, 40, 20, .16);
  border: 1px solid rgba(120, 90, 40, .12);
  /* 位移、旋转、透明度都过渡；transform 用 cubic-bezier 做出「滑进纸堆」的手感 */
  transition: transform .52s cubic-bezier(.34, 1.16, .5, 1), opacity .42s ease,
              box-shadow .3s ease;
  will-change: transform;
}
/* ⚠️ 隐入的纸条要**浮到最上层**。
   多张便签本来按 DOM 顺序叠（后画的压前面的），会被拖成互相重叠。实测（真实触摸、
   20 轮）约 15% 的轮次出现「隐藏后点击不出来」：document 四个事件都收到了，
   但落点在**盖在上面那张**纸条上 —— 也就是说，收起后露出的那一角被别人压住了，
   手指点过去其实点在别人身上。抬上来就能点到了；隐入的只是一条纸边，
   浮在最上层不挡视线、也不影响观感。
   （同时把露出宽度从 30 调到 46：见 app.js 的 PEEK —— 旋转补偿吃掉约 15px，
     原来实际只剩 15px 宽，手机上本来就很难点中。） */
.note.is-out { z-index: 6; }
/* 纸色主题（后台选，前端上色）。
   问答页的便签（.note--xxx）和收藏条目下自己写的纸条（.fnote--xxx）共用这五条 ——
   一处改色两处都变，不会出现「两种黄不一样」。
   同时把颜色写成 --fpaper 变量：`.fnote` 的底色读这个变量（见下面的 .fnote）。
   ⚠️ 必须走变量，不能在 .fnote 里直接写死一个底色 —— 两条规则特异性相同，
   后写的赢，写死的那个会把这里的颜色整条盖掉（踩过：五色全变黄）。 */
.note--yellow, .fnote--yellow { --fpaper: linear-gradient(160deg, #FFF7C4 0%, #FCEFA8 100%); background: var(--fpaper); }
.note--pink,   .fnote--pink   { --fpaper: linear-gradient(160deg, #FFE3E6 0%, #FDD3D8 100%); background: var(--fpaper); }
.note--blue,   .fnote--blue   { --fpaper: linear-gradient(160deg, #DDF0FB 0%, #C9E6F7 100%); background: var(--fpaper); }
.note--green,  .fnote--green  { --fpaper: linear-gradient(160deg, #E1F4DC 0%, #CDEAC6 100%); background: var(--fpaper); }
/* 第五色 = **淡紫**（老师要求，便签和纸条两处都用它，原来这里是白色）。
   ⚠️ 类名和后台的 paper key 仍然叫 `white`，**不要改名**：它是存在数据里的
   （`notes.json` 的 paper 字段，`lib/notes.js` 的 PAPERS 是白名单），改名会让已有的
   便签取不到值、被 normPaper 回退成黄纸 —— 换颜色只在样式里换，名字不动。 */
.note--white,  .fnote--white  { --fpaper: linear-gradient(160deg, #F2E9FC 0%, #E3D4F7 100%); background: var(--fpaper); }
/* 纸的横纹：很淡，模拟便签本的横线，不是重点别做重 */
.note::before {
  content: ''; position: absolute; inset: 30px 0 0;
  background: repeating-linear-gradient(180deg,
    transparent 0, transparent 25px,
    rgba(120, 90, 40, .055) 25px, rgba(120, 90, 40, .055) 26px);
  border-radius: 3px; pointer-events: none;
}
/* 便签正文与操作都压在纹路之上 */
.note .note-text { position: relative; z-index: 1; margin: 0; padding-top: 2px; }

/* ── 便签上的图钉（学生点它 = 把这张便签钉住）─────────────────────
   老师定的规格（2026-09-19）：
     · **针尖不动**，而且针尖落在**便签水平正中**上；
     · 旋转**以针尖为轴**：没按下 45°（PIN_SVG 本身的角度），按下后转到 60°（更正一些）；
     · 没按下时图钉是**灰色**的，按下后**恢复颜色**（红）。
   针尖在按钮里的位置：PIN_SVG 里针尖在 48 格坐标的 (9.15, 40.85)，按钮 34px
   → 换算 34/48 得 (6.5, 29)px。所以 margin-left 用 -6.5px 把**针尖**而不是按钮
   摆到正中（原来是 -17px，把按钮本身居中，针尖就偏左了），transform-origin 也取这一点。 */
.note-pin {
  position: absolute; z-index: 2;
  top: -14px; left: 50%; margin-left: -6.5px;
  width: 34px; height: 34px; padding: 0; line-height: 0;
  background: transparent; border: 0; cursor: pointer;
  transform-origin: 6.5px 29px;
  /* 没按下：图钉是**一个颜色的平面剪影**（见下面那条 fill 规则）——
     形状一眼看得出，针也不会淡（之前用 grayscale+提亮，针被洗得几乎看不见）。 */
  opacity: .95;
  /* ⚠️ 旋转**不写在这一层**，而是写进里面的 svg（见下面 .note-pin svg）。
     原因：filter 是在元素自己的坐标系里算的，这一层要是转了，影子方向会跟着歪
     （按下 -15° 时影子就成了斜的）。层不转、只转 svg，影子就永远**水平向右**。
     影子：没按下浅一点（2px），按下更实更远（3px），方向都是水平向右。 */
  filter: drop-shadow(2px 0 1.5px rgba(60, 40, 20, .26));
  transition: opacity .2s ease, filter .2s ease;
  touch-action: manipulation;
}
/* 图钉本体在这里转：以针尖为轴，没按下 0°（=45°），按下 -15°（=60°） */
.note-pin svg {
  transform: rotate(var(--pin-rot, 0deg));
  transform-origin: 6.5px 29px;
  transition: transform .2s ease;
}
/* 没按下：整枚图钉压成**一个颜色的平面剪影** —— 钉帽、钉杆、底盘、针尖全一个色，
   所以「形状看得清」和「针不淡」两件事同时成立（分成深浅几块反而会让针显得淡）。
   ⚠️ CSS 的 fill 会盖过 SVG 上的 fill 属性；按下时这条选择器不匹配，自带配色就回来了。
   选的是暖调浅灰，压在米黄纸面上刚好：看得出形状，又不抢内容。 */
.note:not(.is-pinned) .note-pin svg path,
.note:not(.is-pinned) .note-pin svg ellipse { fill: #a49d95; }
/* 按下去：以针尖为轴逆时针 15°（45° → 60°，更正），恢复颜色，阴影更实。
   逆时针是负角：SVG 里针已朝左下 45°，再逆时针转才是往「立起来」的方向走。 */
.note.is-pinned .note-pin {
  --pin-rot: -15deg;
  opacity: 1;
  filter: drop-shadow(3px 0 2.5px rgba(60, 40, 20, .36));
}
/* 按下去时，纸面上多一个**针眼的接触阴影**：就在针尖上（绕针尖转，两档都不会跑） */
.note-pin::before {
  content: ''; position: absolute;
  left: 2px; top: 27px;
  width: 9px; height: 3.5px; border-radius: 50%;
  background: rgba(90, 70, 40, .3);
  opacity: 0; transition: opacity .2s ease;
}
.note.is-pinned .note-pin::before { opacity: 1; }
.note-pin svg { display: block; width: 100%; height: 100%; }

/* ── 纸条左下角的评论入口（小气泡 + 条数）─────────────────────────
   和右下角的「撕掉」同一条留白带（纸条 padding-bottom 已留出 34px）。
   评论是「别人也在看」的信号，所以比「撕掉」显一点：常态 .62、有评论时更深。 */
.note-cmts {
  position: absolute; z-index: 1;
  left: 10px; bottom: 10px;
  display: flex; align-items: center; gap: 4px;
  padding: 3px 7px 3px 5px; border: 0; border-radius: 999px;
  background: transparent; cursor: pointer;
  color: rgba(107, 90, 69, .72);
  opacity: .62;
  transition: opacity .22s ease, background .18s ease, color .18s ease;
  touch-action: manipulation;
}
.note-cmts svg { display: block; width: 17px; height: 17px; }
.note-cmts-n { font-size: 11.5px; line-height: 1; font-variant-numeric: tabular-nums; }
.note-cmts-n[hidden] { display: none; }   /* 自己写了 display:flex，得显式压掉 [hidden] */
.note-cmts:active { opacity: 1; background: rgba(120, 90, 40, .14); color: #6b5a45; }
.note.has-cmts .note-cmts,
.note:hover .note-cmts { opacity: .9; color: #8a6f4f; }
/* 按下去的那一下：图钉被压进纸里再回弹。动 SVG 而不是按钮本身 ——
   按钮的 transform 要留给「歪/正」两种状态，别被动画覆盖掉。 */
/* 按下去的那一下：图钉被压进纸里再回弹。动里面的 svg ——
   旋转（--pin-rot）也在 svg 上，所以关键帧里必须**带上它**，
   否则动画期间 transform 被 scale 覆盖，图钉会瞬间跳正再跳回去。 */
.note-pin.is-press svg { animation: pin-press .3s ease-out; }
@keyframes pin-press {
  0%   { transform: rotate(var(--pin-rot, 0deg)) scale(1); }
  45%  { transform: rotate(var(--pin-rot, 0deg)) scale(.84); }
  100% { transform: rotate(var(--pin-rot, 0deg)) scale(1); }
}
/* 「撕掉」做得**淡**：它是破坏性操作，不该在纸面上最抢眼。
   平时几乎融进纸色里，手指按到纸条上（或悬停）才稍微显出来，
   按下去才变红 —— 想撕的人找得到，不想撕的人不会被它吸引。
   位置：**绝对定位钉在纸面右下角**。以前放在文档流里（margin-top 撑着），
   按钮高度只有 21px，容器底边距总有十来像素够不着边，怎么调都像"悬在半空"。
   钉到角上、底部距 12px、右侧距 10px，才是「挪到右下角」。
   便签的 padding-bottom 已留出 34px 给它，长文本不会压上去。 */
.note .note-ops {
  position: absolute; z-index: 1;
  right: 10px; bottom: 12px;
  display: flex; gap: 6px;
  opacity: .3; transition: opacity .25s;
}
.note:hover .note-ops, .note.is-drag .note-ops { opacity: .72; }
.note .note-ops button {
  font: inherit; font-size: 11px; line-height: 1; letter-spacing: .04em;
  color: rgba(107, 90, 69, .75);
  background: transparent; border: 1px solid rgba(120, 90, 40, .13);
  border-radius: 999px; padding: 4px 10px; cursor: pointer;
  transition: color .18s, background .18s, border-color .18s;
}
.note .note-ops button:active { background: rgba(255, 255, 255, .8); color: #b3261e; border-color: rgba(179, 38, 30, .35); }
/* 拖动时的抓手与反馈 */
.note { cursor: grab; touch-action: none; }
.note.is-drag {
  cursor: grabbing;
  box-shadow: 0 6px 22px rgba(60, 40, 20, .3), 0 2px 6px rgba(60, 40, 20, .22);
  opacity: .96;
  transition: none;            /* 拖的时候不要那个 0.52s 的 transform 过渡，会跟手不及 */
}

/* 浮出（默认状态）：微微歪一点，像随手贴上去的 */
.note.is-in { opacity: 1; }
/* 入场：从左侧滑出来并贴到位置。用 animation 而不是让 JS 翻状态 ——
   requestAnimationFrame 那一跳不一定执行到，纸条会卡在滑出去的姿态上。 */
@keyframes note-in {
  0%   { transform: translateX(calc(-100% + 26px)) translateY(10px) rotate(-2.4deg) scale(.97);
         opacity: 0; }
  60%  { opacity: 1; }
  100% { transform: translateX(0) translateY(0) rotate(var(--in-rot, -3.2deg)) scale(1);
         opacity: 1; }
}
/* ── 隐入（藏起来，只留左侧一角）────────────────────────────────────
   位移**不在这里写死**，由 JS 按纸条当前位置算好、以内联样式给（见 app.js 的
   hiddenShift）。原因：纸条可以被学生拖到任何地方，固定的
   `calc(26px - 纸条宽度)` 只对「贴在右边」成立 —— 往左拖过之后再隐藏，
   整张纸会被推出屏幕外，再也点不到（这就是那个 bug）。
   这里只管渐变、提亮、阴影这些跟位置无关的事。
   transform 由 JS 用内联 !important 设置（.note.is-out 是两条类选择器，
   优先级高于内联样式，没办法：得靠 !important 压过去）。 */
.note.is-out {
  opacity: .9;
  box-shadow: 0 1px 2px rgba(60, 40, 20, .12);
}
.note.is-out .note-text { opacity: .45; }
/* 隐入时把操作键藏掉：露出来的应该是「一角纸」，
   不该把「撕掉」按钮也摊在屏幕边上（截图里就是这样，很突兀）。 */
.note.is-out .note-ops { opacity: 0; pointer-events: none; }
/* 这里曾经在右边缘画过一条点线，想提示「纸还在那儿」。
   删掉了：它看着像纸沿上裂开的一道缝，不像装饰（用户看到的就是这条）。
   露出的那一角本身就是纸，颜色和纸纹已经足够说明有问题，不用再加东西。 */

/* 撕掉：从上方裂开、对折、掉下去。动画跑完由 JS 移除节点 */
@keyframes note-tear {
  0%   { transform: translateX(0) rotate(0) scale(1); opacity: 1; }
  28%  { transform: translateX(0) rotate(-6deg) scale(1.03); opacity: 1; }
  100% { transform: translateX(0) translateY(58vh) rotate(19deg) scale(.72);
         opacity: 0; }
}
@keyframes note-rip {          /* 裂口：一条锯齿亮线闪一下 */
  0%   { opacity: 0; }
  22%  { opacity: .9; }
  100% { opacity: 0; }
}
.note.is-torn {
  pointer-events: none;
  animation: note-tear .78s cubic-bezier(.5, .05, .9, .5) forwards;
}
.note.is-torn::before {
  content: ''; position: absolute; left: 0; right: 0; top: 42%; height: 3px;
  background: repeating-linear-gradient(90deg, #fff 0 3px, transparent 3px 6px);
  animation: note-rip .5s ease-out forwards; z-index: 4;
}
/* 纸屑：撕的时候从裂口往两边飘 */
.note-bit {
  position: fixed; z-index: 26; pointer-events: none;
  width: 7px; height: 5px; border-radius: 1px;
  background: #f5e9b8;
  animation: note-bit-fly .95s ease-out forwards;
}
@keyframes note-bit-fly {
  0%   { opacity: 1; transform: translate(0, 0) rotate(0); }
  100% { opacity: 0;
         transform: translate(var(--bx, 30px), var(--by, 60px)) rotate(var(--br, 200deg)); }
}
/* 纸条在飞/滑的时候别让手机选中文字 */
.note, .note * { -webkit-user-select: none; user-select: none; }
/* 点开阅读时可以选中（长按复制提醒内容） */
.note.is-reading, .note.is-reading * { -webkit-user-select: text; user-select: text; }

/* 输入条：1 边框 + 9 + 输入框 46 + 9 = 65px，与 --composer-h 对齐（见上面那个变量） */
.composer {
  position: fixed; left: 0; right: 0;
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--kb, 0px));
  display: flex; gap: 8px; align-items: flex-end;
  padding: 9px 12px; background: var(--bar);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
  backdrop-filter: saturate(160%) blur(12px);
  border-top: 1px solid var(--line);
  box-shadow: 0 -8px 20px rgba(var(--shadow-ink), .045);
  /* ⚠️ 必须高过便签层（.notes-layer 是 25）：纸条是「贴在内容上」的浮层，但**输入条
     是外壳**，被纸条盖住就点不到输入框了。以前纸条 10 秒必自动滑走，压住只是暂时的；
     加了「图钉钉住」之后会永久盖住 —— 学生会看到输入区被纸盖着、点不进去（踩过）。 */
  z-index: 26;
}
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .composer { background: var(--bar); }
}
/* 输入框的定位容器：加号要浮在输入框**内部**右侧，得有个相对定位的父级。
   它自己顶替原来 textarea 占的那份 flex 空间。 */
.input-wrap { position: relative; flex: 1; min-width: 0; display: flex; }
.composer textarea {
  flex: 1; resize: none; width: 100%;
  /* ⚠️ 字号必须 ≥16px：iOS 聚焦**字号小于 16px** 的输入框时会把整页放大
     （`maximum-scale=1` 也拦不住这条行为），表现就是「点一下输入框，画面被放大、
     光标跑到上面去了」。这里原来是继承 body 的 15px，正好踩线，所以显式写 16px。 */
  font-size: 16px;
  /* 上限就是半屏 —— 内容超过它才出滚动条。这里与 JS 的 autoGrow 同口径
     （window.innerHeight * 0.5），JS 没跑到时这条兜底也能撑住。
     别额外套一层 min(…, 260px)：那样在 844px 高的手机上实际只到 260px（约 1/4 屏），
     滚动条会提前冒出来（实测就是这个原因）。 */
  max-height: 50vh; min-height: 40px;
  /* 右边留 40px 给框内的加号，不然打字会钻到加号底下 */
  padding: 10px 40px 10px 13px; border-radius: 20px; border: 1px solid var(--line);
  background: var(--card); outline: none; line-height: 1.5;
  box-shadow: var(--inset-sm);
  transition: border-color .15s, box-shadow .15s;
  /* Firefox 的细滚动条；WebKit 那套见下面 ::-webkit-scrollbar */
  scrollbar-width: thin;
  /* 真出滚动条时给它留位置（见下面的 .has-scroll）。
     这里**不能**写 scrollbar-gutter: stable —— 那句会让浏览器一直占着滚动条的位置
     并把滚动条画出来，于是只打了两个字也看到一条滚动条（网页版实测如此）。 */
}
/* 输入框的滚动条跟站内其他区域保持一致（8px、无箭头、半透明）；
   不写这段就会用浏览器默认那条 15px 带上下箭头的，很突兀。 */
.composer textarea::-webkit-scrollbar { width: 8px; }
.composer textarea::-webkit-scrollbar-track { background: transparent; }
.composer textarea::-webkit-scrollbar-thumb {
  background: rgba(var(--shadow-ink), .18); background-clip: padding-box;
  border: 2px solid transparent; border-radius: 8px;
}
/* 只在真的出滚动条（内容超半屏）时把右侧让开一点，文字才不会贴在滚动条上。
   8px 的滚动条 + 一点余量 ≈ 20px；平时不加，左右保持对称。 */
.composer textarea.has-scroll { padding-right: 50px; }
.composer textarea::placeholder { color: var(--ink-3); }
.composer textarea:focus { border-color: var(--red); box-shadow: 0 0 0 3px var(--red-glow); }
/* 框内右侧的加号：比原来的图片按钮**淡**得多（用户要求的）。
   平时只是个浅灰细「+」（透明度 .5），按下去才染上一点朱红 ——
   它是个安静的入口，不该跟「发送」抢注意力。 */
.plus-btn {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 28px; height: 28px; padding: 0; border: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink-3); opacity: .5;
  cursor: pointer; transition: opacity .18s, color .18s, background .18s;
}
.plus-btn:active { opacity: 1; color: var(--red); background: var(--red-soft); }
@media (hover: hover) { .plus-btn:hover { opacity: .85; } }
/* 发送键：正红实心，白字 5.80:1；渐变到深正红，呼应 PPT 的红色块面 */
.send-btn {
  flex: none; height: 40px; padding: 0 18px; border-radius: 20px;
  background: linear-gradient(150deg, var(--red-deep), var(--red-dark));
  color: var(--on-red); font-weight: 600; font-size: 14px;
  letter-spacing: .04em;
  box-shadow: 0 4px 12px var(--red-glow);
  transition: transform .12s ease, opacity .15s;
}
.send-btn:disabled { opacity: .45; box-shadow: none; }
.send-btn:not(:disabled):active { transform: scale(.97); }

/* ── 发图片 ─────────────────────────────────────────────────────────
   入口是框内的加号（见上面的 .plus-btn），不再是左侧独立的方形按钮。 */
/* 预览条：贴在输入条上方。composer 是 fixed，这条也 fixed（bottom 抬到 composer
   之上），这样不占输入条的布局高度，输入框不会因为它出现而跳动。 */
.img-tray {
  position: fixed; left: 0; right: 0; z-index: 21;
  bottom: calc(var(--tabbar-h) + var(--composer-h) + env(safe-area-inset-bottom) + var(--kb, 0px));
  display: flex; gap: 8px; padding: 8px 12px 2px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  max-width: 720px; margin: 0 auto;
}
.img-tray[hidden] { display: none; }
.img-tray .it {
  position: relative; flex: none; width: 62px; height: 62px;
  border-radius: 10px; overflow: hidden; background: var(--card);
  border: 1px solid var(--line); box-shadow: var(--shadow-sm);
}
.img-tray .it img { width: 100%; height: 100%; object-fit: cover; display: block; }
.img-tray .it button {
  position: absolute; top: 2px; right: 2px; width: 20px; height: 20px;
  border-radius: 50%; border: 0; cursor: pointer; line-height: 1;
  background: rgba(0, 0, 0, .55); color: #fff; font-size: 13px; padding: 0;
}
/* 气泡里的图片：限宽限高 + 圆角，点开看大图 */
.bubble .shot {
  display: block; max-width: 100%; width: auto; max-height: 260px;
  border-radius: 10px; margin: 2px 0 6px; cursor: zoom-in;
  border: 1px solid var(--line);
}
/* 点图看大图 */
.lightbox {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(20, 12, 10, .92);
  display: flex; align-items: center; justify-content: center;
  padding: 18px; cursor: zoom-out;
}
.lightbox[hidden] { display: none; }
.lightbox img { max-width: 100%; max-height: 100%; border-radius: 8px; display: block; }
/* 服务端的说明条（如「图片识别还没开通」）：在马老师的话上方，样式上要和正文区分开 */
.bubble .notice {
  margin: 0 0 8px; padding: 7px 10px; border-radius: 8px;
  background: var(--red-soft); color: var(--red);
  border-left: 3px solid var(--red);
  font-size: 12.5px; line-height: 1.6;
}

/* ── 课程 ─────────────────────────────────────────── */
/* ── 视频课的分类导航（最新 / 阅读 / 作文 / 选择题 / 文言文 / 语基）────
   老师要求「像 B 站那样」：手机一排放不下时**横向划动**看剩下的，不折行、不缩字号。
   · flex + nowrap + overflow-x: auto（滚动条藏掉，不然会占一行）；
   · 划动的手感用 -webkit-overflow-scrolling: touch，并 overscroll-behavior: contain
     把过度滚动拦在栏里（别把整页带着弹）；
   · 粘在顶部：往下翻列表时分类栏一直在（和收藏页的分类栏同样的做法）。
   ⚠️ 这里**可以**用 overflow（不像收藏页那个二级面板会被裁）—— 这一栏没有下拉浮层。 */
.course-tabs {
  position: sticky; top: 0; z-index: 6;
  display: flex; flex-wrap: nowrap; gap: 7px;
  margin: 2px 0 14px; padding: 2px 0 6px;
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  scrollbar-width: none;
  /* ⚠️ **不要给这一栏加背景**：加过一版 `linear-gradient(180deg, var(--paper) 70%, 透明)`，
     老师一眼就看出「导航按钮下面一条白条」—— --paper 比页面底色的暖调更白，
     露在按钮下方就是一条突兀的带子。站内收藏页那个粘顶分类栏本来就没有背景，
     这里保持一致（滚动时内容是**从按钮之间**穿过的，和收藏页一个观感）。 */
}
.course-tabs::-webkit-scrollbar { display: none; }
.course-tabs > button {
  flex: none;                              /* 关键：不许被压扁，放不下就靠划动 */
  font: inherit; font-size: 13px; line-height: 1; padding: 7px 13px;
  border-radius: 999px; cursor: pointer; white-space: nowrap;
  background: var(--card); color: var(--ink-2);
  border: 1px solid var(--line);
  transition: background .18s, color .18s, border-color .18s;
}
.course-tabs > button.is-active {
  background: var(--red-soft); color: var(--red); border-color: var(--red-line, #f3c9c6);
  font-weight: 600;
}
.course-tabs > button:active { transform: scale(.97); }
/* 分类里暂时没有视频时的提示（分类栏是固定的六个，空分类也要能点） */
.course-empty {
  grid-column: 1 / -1;
  padding: 22px 4px; text-align: center;
  font-size: 13px; line-height: 1.8; color: var(--ink-3);
}

/* 视频**两列**排列（老师要求）：手机上一屏能看见两条，找视频快得多。   卡片窄了一半（430px 的手机上每张约 195px），所以内部跟着收：
   · 标题最多两行、简介最多一行（超出省略号），同一行的两张卡才等高、下沿齐平；
   · 内边距和字号都收一档，不然标题会被挤成三四行；
   · 播放键的三角和文字缩小（原来那套是给整宽卡片做的）。 */
.video-list { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.video-list .video-meta { padding: 9px 10px 11px; }
.video-list .video-meta strong {
  font-size: 13.5px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.video-list .video-meta p {
  margin: 4px 0 0; font-size: 11.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.video-list .video-play { gap: 7px; }
.video-list .video-play-icon {
  border-left-width: 13px;
  border-top-width: 8px;
  border-bottom-width: 8px;
}
.video-list .video-play-text { font-size: 11px; letter-spacing: .06em; }
.video-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
/* 整张视频卡就是一个可点区域（点进播放页）：
   鼠标悬停要**变手**，再给一点反馈（边框变深 + 轻微压下），鼠标用户才看得出整块能点。
   ⚠️ 只给 .is-clickable 加 —— 外链卡、未配置卡整块不可点，给了手型是误导。 */
.video-card.is-clickable { cursor: pointer; transition: box-shadow .18s ease, border-color .18s ease, transform .12s ease; }
.video-card.is-clickable:hover { border-color: var(--red-line, #f3c9c6); box-shadow: 0 4px 16px rgba(var(--shadow-ink), .14); }
.video-card.is-clickable:active { transform: scale(.995); }
/* 键盘用户：Tab 走到卡片上时要看得见（role=button + tabindex 已经给了可聚焦性） */
.video-card.is-clickable:focus-visible { outline: 2px solid var(--red-vivid); outline-offset: 2px; }
.video-card video { width: 100%; display: block; background: var(--media-bg); aspect-ratio: 16/9; object-fit: contain; }

/* ── 马老师的读书节目：B 站嵌入 ─────────────────────────────────────
   iframe 不参与布局流，靠外层 padding-top 撑出 16:9 的比例，再让 iframe
   绝对定位铺满 —— 这样手机上也不会因为固定高度而变形。 */
.bili-list { display: flex; flex-direction: column; gap: 14px; }
.bili-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); }
.bili-frame { position: relative; width: 100%; padding-top: 56.25%; background: var(--media-bg); }
.bili-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
/* B 站视频封面（和视频课那套一个做法：单独存的 WebP，不在页面加载时去连 B 站）。
   有封面时整块可点，所以给个手型；没封面仍走下面那枚播放键。 */
.bili-frame.has-poster { cursor: pointer; }
.bili-poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.bili-frame.has-poster:active .bili-poster { opacity: .86; }

/* 播放前只有一个按键，点了才挂 iframe —— B 站播放器不认 autoplay=0，
   一加载就会自己播起来，页面刚打开就出声。 */
.bili-play {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: linear-gradient(160deg, #3A2E28, #241E1C);
  color: var(--on-red); border: 0;
}
.bili-play-icon {
  width: 0; height: 0;
  border-left: 18px solid currentColor;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  margin-left: 4px;                 /* 三角形重心偏左，右移一点才是视觉居中 */
  opacity: .92;
}
.bili-play-text { font-size: 13px; letter-spacing: .1em; opacity: .85; }
.bili-play:active { background: linear-gradient(160deg, #2E2520, #1C1715); }
.bili-meta { display: flex; align-items: baseline; gap: 10px; padding: 10px 12px 12px; }
.bili-meta strong { flex: 1 1 auto; min-width: 0; font-size: 14px; line-height: 1.45; }
.bili-link { flex: none; font-size: 12.5px; color: var(--red); }
.bili-link:active { color: var(--red-dark); }
/* 外链视频（网盘 / B 站）与未配置状态：
   渐变只用能承载白字的红（暗端→主红 = 7.33:1→5.80:1）；
   亮红 #F5232D 对白字只有 4.07:1，因此不上场做底色。 */
.video-link {
  display: flex; align-items: center; gap: 12px; padding: 16px 14px;
  background: linear-gradient(135deg, var(--red-dark), var(--red));
  color: var(--on-red);
  text-decoration: none; font-size: 14.5px; letter-spacing: .02em;
}
.video-link:active { background: linear-gradient(135deg, var(--red-dark), var(--red-deep)); }
/* 播放键：JS 里给的是 emoji 字形 ▶，这里连同文字一起隐掉，
   改用纯 CSS 画的三角（border 技巧），避免不同设备渲染成彩色 emoji。 */
.video-link .play {
  position: relative; flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--on-red-18); display: grid; place-items: center;
  font-size: 0; box-shadow: inset 0 0 0 1px var(--on-red-14);
}
.video-link .play::before {
  content: ''; margin-left: 3px;   /* 三角形光学居中 */
  width: 0; height: 0;
  border-style: solid; border-width: 6px 0 6px 10px;
  border-color: transparent transparent transparent var(--on-red);
}
.video-link .vt { flex: 1; min-width: 0; font-weight: 600; }
/* 外链标识：挂在播放键上，说明点了会跳到站外，不污染标题文案。
   底色用暗红实底而非半透明白，9px 小字才有足够的对比度。 */
.video-link .play::after {
  content: '站外'; position: absolute; left: 50%; top: 100%; transform: translate(-50%, 3px);
  padding: 0 5px 1px; border-radius: 999px; font-size: 9px; font-weight: 500;
  letter-spacing: .04em; line-height: 1.5; white-space: nowrap;
  background: var(--red-dark); color: var(--on-red);
}
.video-off {
  padding: 22px 14px; text-align: center; font-size: 13.5px;
  color: var(--ink-3); background: var(--peach);
}
.video-meta { padding: 11px 13px; }
.video-meta strong { font-size: 14.5px; }
.video-meta p { margin: 3px 0 0; font-size: 12.5px; color: var(--ink-3); }

.series-list { display: flex; flex-direction: column; gap: 10px; }
.series { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); }
.series-head { width: 100%; display: flex; align-items: center; gap: 10px; padding: 13px; text-align: left; }
.series-head .stage {
  flex: none; font-size: 11px; padding: 2px 8px; border-radius: 999px;
  background: var(--red-soft); color: var(--red); font-weight: 600;
}
.series-head .sname { flex: 1; font-weight: 600; font-size: 14.5px; }
.series-head .arrow { color: var(--ink-3); font-size: 17px; line-height: 1; transition: transform .2s, color .2s; }
.series.open .series-head .arrow { transform: rotate(90deg); color: var(--red); }
.series-body { display: none; border-top: 1px solid var(--line); }
.series.open .series-body { display: block; }
.series-note { padding: 9px 13px; font-size: 12.5px; color: var(--ink-3); background: var(--peach); }
.lesson { display: flex; gap: 10px; padding: 11px 13px; border-top: 1px solid var(--line); }
.lesson:first-child { border-top: 0; }
/* 讲次序号：改用陶土棕系，替代原竹青（理由见文件末尾注释） */
.lesson .no {
  flex: none; width: 26px; height: 26px; border-radius: 8px; background: var(--clay-tint);
  color: var(--outline-ink); font-size: 12px; font-weight: 700; display: grid; place-items: center;
}
.lesson .lt { flex: 1; min-width: 0; }
.lesson .lt strong { font-size: 14px; font-weight: 600; }
.lesson .lt em { display: block; font-style: normal; font-size: 11.5px; color: var(--red); margin-top: 1px; }
.lesson .lt span { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 2px; }

/* ── 资料 ─────────────────────────────────────────── */
.search-wrap { margin-bottom: 12px; }
.search-wrap input {
  width: 100%; padding: 11px 15px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--card); outline: none;
  box-shadow: var(--inset-sm);
  transition: border-color .15s, box-shadow .15s;
}
.search-wrap input::placeholder { color: var(--ink-3); }
.search-wrap input:focus { border-color: var(--red); box-shadow: 0 0 0 3px var(--red-glow); }
.grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.mat-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 13px 12px 12px; text-align: left; box-shadow: var(--shadow-sm);
  display: grid; grid-template-rows: auto auto auto 1fr auto; row-gap: 5px;
  min-height: 112px; transition: box-shadow .16s ease, transform .16s ease;
}
.mat-card:active { transform: scale(.985); box-shadow: var(--shadow); }
.mat-card strong { font-size: 14px; line-height: 1.4; }
.mat-card > span { font-size: 11.5px; color: var(--ink-3); line-height: 1.5; }
.mat-card .count { margin-top: auto; padding-top: 4px; font-size: 11px; color: var(--red); font-weight: 600; }

/* 资料角标：JS 里写的是 emoji（💬📜🔤…），此处用 content 直接替换为「一字朱印」。
   emoji 与内容的关联很弱（🏯 表示文化常识、🩺 表示病句），换成分类首字既准确又克制。
   注意：.ico 这个元素本身必须留着，app.js 的搜索靠切换它的父级 .mat-card 的 display 工作。 */
.mat-card .ico {
  display: grid; place-items: center; flex: none;
  width: 22px; height: 22px; border-radius: 7px;
  background: var(--red-soft); color: var(--red); border: 1px solid var(--red-line);
  /* font-size: 0 用来压掉 app.js 写入的 emoji。
     注意 ::before 是「追加」不是「替换」—— 不压掉的话 emoji 会和汉字
     在 22px 的盒子里上下堆叠（12px × 2 行已溢出一行）。 */
  font-size: 0; font-weight: 700; line-height: 1;
}
.mat-card .ico::before {
  content: '文';
  font-size: 12px;
  text-indent: -.04em;   /* 单个汉字在方框里的光学居中 */
}
.mat-card[data-title^="有思想含量的话"] .ico::before { content: '言'; }
.mat-card[data-title^="7+3 作文范文"] .ico::before { content: '范'; }
.mat-card[data-title^="文言文实词"] .ico::before { content: '实'; }
.mat-card[data-title^="18 个虚词记忆口诀"] .ico::before { content: '诀'; }
.mat-card[data-title^="18 个虚词记忆方法"] .ico::before { content: '法'; }
.mat-card[data-title^="成语 520 计划"] .ico::before { content: '成'; }
.mat-card[data-title^="成语专项精练"] .ico::before { content: '练'; }
.mat-card[data-title^="经典人物资料卡"] .ico::before { content: '人'; }
.mat-card[data-title^="中国古代文化常识"] .ico::before { content: '史'; }
.mat-card[data-title^="诗歌常见特殊意象"] .ico::before { content: '诗'; }
.mat-card[data-title^="病句"] .ico::before { content: '病'; }
.mat-card[data-title^="敬辞谦辞汇编"] .ico::before { content: '敬'; }
.mat-card[data-title^="《论语》三十三则"] .ico::before { content: '论'; }
.mat-card[data-title^="时代现象与常识性事实"] .ico::before { content: '时'; }
.mat-card[data-title^="2026 高考作文真题解析"] .ico::before { content: '题'; }
.mat-card[data-title^="推荐给高中生的"] .ico::before { content: '书'; }
.mat-card[data-title^="秘笈怎么用"] .ico::before { content: '用'; }

/* ── 推荐给高中生的 34 本书：卡片网格 ───────────────────────────────
   和秘笈卡片同一套网格与观感，但书没有「一字朱印」（那是给秘笈八本配的
   字），改用右上角的序号角标。 */
.book-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.book-card {
  position: relative;
  display: grid; grid-template-rows: auto 1fr; row-gap: 5px;
  min-height: 92px; padding: 13px 12px 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  text-align: left; box-shadow: var(--shadow-sm);
  transition: box-shadow .16s ease, transform .16s ease;
}
.book-card:active { transform: scale(.985); box-shadow: var(--shadow); }
.book-card .no {
  position: absolute; top: 11px; right: 11px;
  font-size: 11px; font-weight: 700; line-height: 1;
  color: var(--red); font-variant-numeric: tabular-nums;
}
/* 给序号留出位置，长书名不会压到它 */
.book-card strong { font-size: 13.5px; line-height: 1.45; padding-right: 20px; }
.book-card > span { font-size: 11.5px; line-height: 1.5; color: var(--ink-3); }
/* 单本导读顶部的返回：看完一本能回到书目，不必关掉抽屉再重进 */
.book-back {
  display: inline-block; margin-bottom: 12px; padding: 6px 12px;
  font-size: 12.5px; color: var(--red);
  background: var(--red-soft); border: 1px solid var(--red-line); border-radius: 999px;
}
.book-back:active { color: var(--on-red); background: var(--red); }

/* ── 文章 ─────────────────────────────────────────── */
.list { display: flex; flex-direction: column; gap: 9px; }
.list-item {
  width: 100%; text-align: left; background: var(--card); border: 1px solid var(--line);
  border-radius: 13px; padding: 13px; box-shadow: var(--shadow-sm);
  transition: border-color .15s, background .15s;
}
.list-item:active { background: var(--paper); border-color: var(--red-line); }
.list-item strong { display: block; font-size: 14.5px; }
.list-item span { display: block; font-size: 12.5px; color: var(--ink-3); margin-top: 3px; }
.list-item .tag { display: inline-block; font-style: normal; font-size: 11px; color: var(--red); background: var(--red-soft); padding: 1px 7px; border-radius: 999px; margin-bottom: 5px; letter-spacing: .04em; }

/* 范文列表的一行：右边一颗按篇收藏的星标，落在**卡片右上角**。
   星标不跟「打开详情」的按钮重叠：点它只收藏这一篇，不会连带打开详情
   （也就不会误收藏整份）。
   定位基准是 .essay-row（星标是 .list-item 的兄弟，不能挂在按钮里 ——
   <button> 内不允许再嵌 <button>）；按钮留出右侧内边距，免得标题伸到星标底下。
   早先把星标放在卡片右侧、用 align-self: flex-end 贴底，长标题的卡片看着它像
   浮在卡片外面下角，很别扭。 */
.essay-row { position: relative; }
.essay-row .list-item { padding-right: 46px; }
.essay-row .term-fav {
  position: absolute; top: 8px; right: 8px;
  margin-left: 0;              /* 取消 .term-fav 原来的 margin-left:auto */
}

/* ── 抽屉 ─────────────────────────────────────────── */
/* 抽屉是长文阅读面：底色用次级暖粉（rose），内部再把语义变量重绑到更浅的
   卡片面，形成「进入正文」的层次变化，而不是整屏同色。
   注意：只重绑颜色语义变量，像 --line 这类被结构性引用的变量不动。 */
.drawer {
  /* 底色与上一级完全一致：上一级那条内容带是 --bar，详情页原先用 --rose
     （陶土色），点进来会突然变深一档，像是两个不同的页面。 */
  position: fixed; inset: 0; z-index: 40; background: var(--bar);
  display: flex; flex-direction: column;
  animation: slideIn .18s ease-out;
  /* 注意：这里只能写 --paper: var(--card)。原先下面还有一行 --card: var(--card)，
     那是自引用循环，按规范会变成无效值，并且把引用它的 --paper 一起拖坏 —— 
     结果是抽屉里所有 var(--paper) 全部失效（含刚才的抽屉底色，算出透明）。 */
  --paper: var(--card);
  --rose: var(--peach);
  --glass-hi: rgba(255, 255, 255, .9);
}
@keyframes slideIn { from { transform: translateX(6%); opacity: .4; } to { transform: none; opacity: 1; } }
.drawer-head {
  /* 与上一级顶栏对齐：同样的 --bar 底色、同样的 56px 高度
     （8px 内边距 + 40px 返回键，等于 --topbar-h）、同样的 1px 底边线。
     原来的 12px 内边距让头部高 64px，比上一级顶栏高一截。 */
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 10px; padding: 8px 14px;
  padding-top: calc(8px + env(safe-area-inset-top));
  border-bottom: 1px solid var(--line); background: var(--bar);
  box-shadow: 0 1px 0 var(--glass-hi);
}
.drawer-head h2 { font-size: 16px; flex: 1; min-width: 0; letter-spacing: .02em; }

/* 抽屉返回键：只给 #drawerBack 定制图形，.ghost-btn 仍是两处共用的底座
   （顶栏 #aboutBtn 的「?」保持通用样式不动）。

   图形用内联 SVG 画（原来是用两条伪元素拼的，见下方历史记录）。为什么换：
   伪元素那套「上+左边框旋转 45°」做斜线时，3px 的边框转过 45° 后垂直方向
   的厚度变成 3 × √2 ≈ 4.24px（在 DPR 1 上实占 4 个像素行），而尾巴那条横线
   是实打实的 3px —— 于是「斜线比横线粗」，在电脑的普通屏上一眼就能看出来
   （实测像素图：斜线 4 行、横线 3 行，粗约 33%）。这是几何必然，靠调数值
   补不齐：把边框压到 2.5px 又会让斜线偏细并糊边。
   SVG 的 stroke 在两条线上是同一个宽度，天然等宽，还自带圆头圆角，
   屏幕缩放（DPR 1 / 2 / 3）都不变样。
   写法要点：stroke 用 currentColor 才能跟随 .ghost-btn 的红色与按下态；
   fill: none 不加就是个实心黑箭头。 */
#drawerBack {
  color: var(--red);         /* 主红 #C52129，对卡片面约 6.6:1，足够清楚 */
  padding: 0;
  position: relative;        /* 命中区仍是 40×40，视觉上只有箭头 */
  touch-action: manipulation;
  /* 不要圆形底座，只要一个箭头。40×40 保留下来当命中区 ——
     视觉上是一个箭头，可点的范围仍然够手指点。 */
  border: 0;
  border-radius: 0;
  background: transparent;
  display: grid;             /* 让 svg 在 40×40 里居中 */
  place-items: center;
  font-size: 0;              /* 兜底：万一有旧缓存残留的 ← 字形也不显示 */
}
#drawerBack svg {
  width: 21px; height: 21px;
  display: block;
  fill: none;
  stroke: currentColor;
  /* 3px：横线与斜线同宽。DPR 1 上两条线都占 3 个像素行，不会再一边粗一边细。
     圆头圆角让尖端接缝自然，不必再对半像素做补偿。 */
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── 历史记录（2026-09-16 起改用上面的 SVG，此段保留备查）──
   旧做法：index.html 里写 Unicode ←，用 font-size: 0 压掉字形，再用两个伪元素
   拼箭头：::before 是 9×9 盒子（上+左 3px 边框）旋转 -45° 成「<」，
   ::after 是 12×3px 横线。踩过的两个坑：
     ① 伪元素必须绝对定位、叠在同一处 —— 让它们当 grid item 时，grid 自动流
        会把两个伪元素分到不同网格行，尾线被推到箭头下方老远，根本看不见。
     ② ::before 的上+左边框画出来是个直角「」,必须 rotate(-45deg) 才是「<」。
   最后仍无法解决的就是「斜线 4 行、横线 3 行」这条固有偏差。 */

/* 常态已经是红色，按下时改更深一档，保留反馈 */
#drawerBack:active { color: var(--red-dark); background: transparent; border-color: transparent; }
.drawer-body {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
  /* 抽屉里没有任何需要横向滚动的内容，所以显式关掉 x 轴。
     注意 overflow-y: auto 会把 overflow-x 隐含成 auto（计算值就是 auto），
     而 iOS 只要看到 overflow-x 是 auto，就把这个元素当成「横向可滚动容器」——
     于是哪怕内容一点都不溢出（实测 6 种宽度、连亚像素都是 0），横向拖动照样
     回弹，看着像「整页能被左右拖」。overflow-x: hidden 取消的就是这个身份。
     touch-action: pan-y 再补一道：直接告诉浏览器只接管纵向平移手势，横向手势
     连给都不给它，从手势层面断掉回弹。
     overscroll-behavior: contain 则把过度滚动拦在抽屉里，不再传给整页，
     免得滚到顶/底继续滑时触发安卓下拉刷新或 iOS 整页橡皮筋。 */
  overflow-x: hidden;
  touch-action: pan-y;
  overscroll-behavior: contain;
  padding: 14px 14px calc(24px + env(safe-area-inset-bottom));
}
/* 内容左边距统一到 28px（与「18 个虚词记忆方法」那套一致）：
   14px 来自 #drawerBody，这里再给条目补 14px。分隔线仍通到边，只有文字缩进。 */
.drawer-body .entry { padding: 11px 14px; border-bottom: 1px dashed var(--line); }

/* 「秘笈怎么用」：一段一段的说明文字，不是条目，所以不打虚线、不做缩进 */
.drawer-body .guide-p { margin: 0 0 9px; line-height: 1.7; color: var(--ink-2); }
.drawer-body .guide-p:last-child { margin-bottom: 0; }

/* 条目编号：只是索引，别抢内容。等宽数字 + 右对齐，个位数和三位数都能对齐，
   换行时也不会把正文挤歪。字号跟随正文，只靠更浅的颜色和字重区分层级。 */
.item-no {
  display: inline-block;
  /* 原来 2.4em（36px）是给两位数以上的长列表预留的，但绝大多数地方序号只有一两位，
     白占一列把后面的文字推远。收到 1.3em；三位数时 min-width 只是下限，会自己撑开。 */
  min-width: 1.3em;
  margin-right: 4px;
  font-size: inherit;
  font-weight: 400;
  line-height: 1;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  text-align: right;
  vertical-align: baseline;
}
/* 150 句那种一句话占一块的，编号跟在句子前面 */
.drawer-body .quote { display: flex; align-items: baseline; }
.drawer-body .quote .item-no { flex: none; }
/* 句子长、要换行时，让正文挂到编号右边而不是顶到编号底下：
   .quote 改两列，编号占左侧固定一列，正文自成一列。 */
.drawer-body .quote { display: flex; align-items: baseline; }
.drawer-body .quote .item-no { flex: none; }
.drawer-body .quote .q-main { flex: 1 1 auto; min-width: 0; }
/* 虚词详解是「一字一节」，编号挂在标题上（字号同样跟随所在文字）。
   标题下面那份详解必须跟标题里的字对齐 —— 不补左内边距的话它会退回卡片
   左内边距，看着像脱离了条目。补多少 = 标题里编号列的占宽，
   变量在下方「.sec > h3」那几条里定义。 */
.drawer-body .sec .rawbox { padding-left: calc(var(--sec-no-w) + var(--sec-gap)); }

/* ── 有编号的条目：让正文挂在编号右侧 ──────────────────────────────
   下面三处（虚词小节、实词 250、成语 520）都要把正文推进到「编号右边」。
   这里原来共用过一个 --no-w: 40px 的估值，但那个值跟 .item-no 的真实占宽
   早就脱节了：min-width 从 2.4em 收到 1.3em（15px 字号下 19.5px），
   .k 又改成了 flex + gap 8px。于是三处一起偏 —— 实测虚词详解偏 8.5px、
   实词前 99 条偏 8.5px、成语换行后的正文偏 16.5px；实词满三位数编号时
   偏差还会变，1~99 与 100~250 两组互相对不齐。
   教训：「缩进量」不能靠估值，必须由编号列的真实占宽推出来。所以现在
   每处都把自己的编号列定成固定宽，缩进取「列宽 + 间距」，两边恒等，
   以后改字号或间距时也只会同时动一处。 */

/* 实词250：词条名 / 释义 / 例句是三个独立的块，编号只占第一行。
   释义与例句的缩进取「列宽 + gap」，词条名落在编号列右边缘上，两者同一列。

   2026-09-16：正文左边距 30px → 20px（减三分之一），总左边距 58px → 48px。
   等式是 20 = 10 + 10，所以释义、例句、词条名仍然对齐在同一条线上。

   为什么正文缩进做成「10 + 10」而不是更自然的「17 + 3」：
   号牌（编号）是三位数时要占 26.39px 实宽（15px 字号下的实测值），
   而正文要提前到 20px、词条名就得站到 48px —— 若让号牌从左往右排，
   它的右边缘会落在 54.39px，直接压到词条名上 6.39px（实测撞字，
   表现为「250坐」糊成一团）。所以这里让号牌列只占 10px，把省下来的
   宽度全部给 gap，再用 transform 按位数把号牌向左平移：transform 不参与
   布局、不撑盒宽，挪的是「看上去的位置」，词条名与正文的左边缘纹丝不动。

   号牌右边缘统一：三种位数（1 位 / 2 位 / 3 位）的号牌右边缘全部落在
   41.61px 同一条线上，1~250 条一律对齐，与词条名（48）留 6.39px 间隙。
   做法见下方 .item-no 那条：不靠按位数标定位移，而是用 direction: rtl
   让文字必然贴住盒右边缘。号牌最左到 15.22px（三位数），比内容边距 28px
   外露 13px —— 是为了给正文让出 10px 换来的，若觉得太靠边可回收。 */
.drawer-body .entry.has-k { --k-no-w: 10px; --k-gap: 10px; }
/* gap 必须带 .k 才压得住下面那条 `.drawer-body .entry .k { gap: 8px }`
   （星标那节的规则，特异性相同但它出现得更晚）。少写这一层选择器，
   gap 就一直是 8px，缩进会变成 18px 而不是 20px —— 这是个静默偏差，
   数字上看着「差不多」，实测才看得出来。 */
.drawer-body .entry.has-k .k { gap: var(--k-gap); }
/* min-width 必须一起归零：.item-no 自己有 min-width: 1.3em
   （15px 字号下算出来 19.5px），下限压过 width，列宽会顶回 19.5px，
   20px 的缩进就变成 29.5px。归零只影响这一处，其余用到 .item-no 的
   地方（虚词、成语 520、150 句）都另有定宽或自己的布局，不靠这条下限。 */
.drawer-body .entry.has-k .k > .item-no {
  width: var(--k-no-w);
  min-width: 0;
  margin-right: 0;
  /* 号牌右边缘统一，靠的不是猜位移，而是「让文字必然贴住盒右边缘」：
     text-align: right 在文字宽于盒子时行为不可靠（实测 1 位数贴住盒右边缘、
     2/3 位数却从盒左边缘向右溢出，同一条线上位置各不相同）；换成
     direction: rtl 后文字一律紧贴右边缘，三种位数天然落在同一条线上。
     rtl 只影响对齐，数字之间没有强方向字符，所以 "250" 不会被倒序。

     下面的位移是纯收尾，把那条统一线从「盒右边缘」挪到 41.61px，
     与词条名（48）留出 6.39px 间隙。基准线 45.22 是实测值：
     没位移时三种位数都在 45.22，故位移量 = 45.22 − 41.61 = 3.61px，
     三种位数共用同一个值 —— 这正是 rtl 带来的好处，不必按位数各标定。 */
  direction: rtl;
  transform: translateX(3.61px);
}
.drawer-body .entry.has-k .eg { padding-left: calc(var(--k-no-w) + var(--k-gap)); }

/* 成语520：整条是一块（编号 + 成语名 + 释义在同一行），用经典的悬挂缩进：
   整块补上「编号列 + 间隙」的左内边距把正文推出去，再用等量的负 text-indent
   把第一行（也就是编号）拉回最左。只影响第一行，换行后的正文正好落在编号
   右侧 —— 也就是跟成语名同一个起点。
   等式：正文左边缘 = 编号列宽 + 间隙。两个量必须一起改，只动一个的话正文
   和成语名就会差出一两个像素。
   编号列要定宽：不定宽的话它会被 .item-no 的 min-width(1.3em) 按住，而三位数
   编号（本页到 520）实际需要 26.4px，于是从第 100 条起数字会被压得向左溢出到
   内容左边距外面。定宽 20.5px 放得下两位数字，三位数则规规矩矩向左溢出
   （右对齐，所以永远不会撞到成语名）。 */
.drawer-body .entry.has-no {
  --idiom-no-w: 20.5px;                          /* 编号列宽 */
  --idiom-gap: 6px;                              /* 编号与成语名的间隙 */
  padding-left: calc(var(--idiom-no-w) + var(--idiom-gap));   /* = 正文左边缘 */
  text-indent: calc((var(--idiom-no-w) + var(--idiom-gap)) * -1);
}
.drawer-body .entry.has-no .item-no {
  width: var(--idiom-no-w);
  margin-right: var(--idiom-gap);
  /* 这里千万别顺手补一条 text-indent: 0 去「抵消继承」。实测：补了 0，三位数
     编号会向右顶出 5.89px，把间隙吃到只剩 0.11px；保留继承下来的负值，它才会
     规矩地向左溢出 5.89px，间隙足 6px。负 text-indent 正是右对齐在溢出时生效
     的机制，不是需要清掉的干扰。 */
}
.drawer-body .entry:last-child { border-bottom: 0; }
.drawer-body .entry b { color: var(--red); font-weight: 700; }
.drawer-body .entry .k { display: block; font-weight: 600; }
.drawer-body .entry .eg { display: block; color: var(--ink-2); font-size: 13px; margin-top: 2px; }
/* ── 秘笈正文的三种字体格式 ───────────────────────────────────────────
   原文档里老师用三种格式标记内容，含义完全不同：
     加粗 + 下划线   ==考点==    <mark class="key">   真正要背下来的部分
     仅加粗          **词条名**  <strong> / <b>       结构标记
     仅下划线        __要点__    <u>                  看到、理解即可
   关键是别把三者压成同一层。每种格式只绑定一种视觉手段，互不重叠：
     考点   = 主红实线 + 半行淡金底 + 最厚字重     （强）
     词条名 = 最深字色 + 加粗，无任何装饰           （中）
     要点   = 陶土虚线 + 极淡暖底，最轻             （弱）
   学生扫一页的判断顺序：先看有没有红色实线（要背）→ 再看黑粗体（词条名）
   → 最后才注意到虚线（提示）。 */
:is(.drawer-body, .essay-body) {
  /* 下划线与底色都落在字面之下，行高不够时相邻两行会贴成一道色带 */
  line-height: 1.95;
}

/* 一级：考点。底色只铺下半行 38%、不透明度 34% —— 是"笔划过的痕迹"，
   不是一张荧光贴纸，一页几十处也不花；真正的分量交给那条主红实线。 */
.drawer-body mark.key, .essay-body mark.key, .bubble mark.key {
  font-weight: 800;
  -webkit-text-stroke: .02em currentColor;  /* 中文字体多无 800 字重，同色描边补足 */
  text-decoration-line: underline;
  text-decoration-color: var(--red);        /* 主红：本套配色里唯一承载小字号红的颜色 */
  text-decoration-thickness: max(1.5px, .085em);
  text-underline-offset: max(3px, .19em);
  text-decoration-skip-ink: none;           /* 连续实线，扫视时能连成一条可追踪的线 */
  color: inherit;                           /* 不再套深棕字：红线本身已足够辨识 */
  background-image: linear-gradient(to top,
    rgba(255, 215, 100, .34) 0, rgba(255, 215, 100, .34) 38%, transparent 38%);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
  margin: 0 .05em;   /* 用 margin 不用 padding：行内 padding 在换行处会露缝 */
  padding: 0;
  border-radius: 2px;                       /* 硬边改柔边，弱化贴纸感 */
  word-break: keep-all;                     /* 四字以内的考点不被拆到两行 */
  /* 但 keep-all 在 Safari 上严格到「连顿号处都不许断行」，于一整行长考点
     （虚词「要点：介词（凭用在把从）、因为、原因、用来、以致、以…」那种几十字
     连成一条的）完全拒绝换行，直接顶破容器 —— 手机上就表现为整页能横向拖、
     底部冒横向滚动条、文字溢出边线。Chrome 实现宽松（允许在顿号处断），所以
     桌面怎么测都测不出来，只有真机 Safari 犯这个病。
     overflow-wrap: anywhere 补上「最后手段」：平时 keep-all 照样保证四字考点
     不被拆开，只有当整行实在放不下、马上要溢出时才允许从任意位置断开。 */
  overflow-wrap: anywhere;
}

/* 二级：仅加粗。一不换色、二不铺底、三不画线，只借字重与字色建立结构感。
   注意 .drawer-body .entry b（词条名，红色）特异性更高，不会被这条覆盖。 */
:is(.drawer-body, .essay-body) :is(strong, b) {
  font-weight: 700;
  color: var(--ink);                        /* 正文是 --ink-2 灰，只加粗不换色则结构感不足 */
  -webkit-text-stroke: .012em currentColor; /* 同色描边让笔画"实心"，比墨晕干净 */
}

/* 三级：仅下划线。改用虚线 —— 主红实线＝必背、陶土虚线＝提示，
   不需要任何文字说明就能分辨。线色取暖陶土：红色已被考点独占，
   金色细线在暖粉底上对比度太低（约 1.5:1），强光下手机根本看不见。 */
:is(.drawer-body, .essay-body, .bubble) u {
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-decoration-color: #D9A992;
  text-decoration-thickness: max(1px, .07em);
  text-underline-offset: max(2.5px, .16em);
  text-decoration-skip-ink: auto;           /* 口诀里夹英文降部时自动避让 */
  background-color: rgba(214, 166, 143, .10);  /* 像划过的草稿纸，与正文拉开层次 */
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
  padding: .04em .08em;
  border-radius: 2px;
}

/* 嵌套兜底：考点里再套加粗 / 下划线时，避免两层装饰叠成一团 */
:is(.drawer-body, .essay-body, .bubble) mark.key strong { font-weight: 800; -webkit-text-stroke: 0; }
:is(.drawer-body, .essay-body, .bubble) :is(mark.key, strong) u,
:is(.drawer-body, .essay-body, .bubble) u :is(mark.key, strong) {
  text-decoration-line: none; background-color: transparent; padding: 0;
}

/* 手机端：装饰按 em 等比缩小后线会细到发糊，这里给"足厚"托底 */
@media (max-width: 480px) {
  :is(.drawer-body, .essay-body) { line-height: 2; }
  :is(.drawer-body, .essay-body, .bubble) mark.key { text-decoration-thickness: 2px; text-underline-offset: 3px; }
  :is(.drawer-body, .essay-body, .bubble) u {
    text-decoration-thickness: 1.5px; text-underline-offset: 3px; text-decoration-color: #CD9A83;
  }
}
/* 桌面端：字号变大后 em 算出来的线会粗得像马克笔，改回绝对像素 */
@media (min-width: 721px) {
  :is(.drawer-body, .essay-body, .bubble) mark.key { text-decoration-thickness: 2px; text-underline-offset: 4px; }
  :is(.drawer-body, .essay-body, .bubble) u { text-decoration-thickness: 1px; text-underline-offset: 4px; }
}
/* 系统"提高对比度"：放弃所有淡处理，把语义还原成最强形态 */
@media (prefers-contrast: more) {
  :is(.drawer-body, .essay-body, .bubble) mark.key {
    background-image: none; background-color: var(--gold-light);
    text-decoration-color: var(--red-dark); text-decoration-thickness: 2.5px; color: var(--ink);
  }
  :is(.drawer-body, .essay-body) :is(strong, b) { font-weight: 800; }
  :is(.drawer-body, .essay-body, .bubble) u {
    text-decoration-style: solid; text-decoration-color: var(--ink-2);
    text-decoration-thickness: 1.5px; background-color: transparent;
  }
}
/* Windows 高对比度：颜色由系统接管，只能靠线宽线型分层 */
@media (forced-colors: active) {
  :is(.drawer-body, .essay-body, .bubble) mark.key {
    background: Highlight; color: HighlightText;
    -webkit-text-stroke: 0; text-decoration-color: currentColor; text-decoration-thickness: 2px;
  }
  :is(.drawer-body, .essay-body, .bubble) u { text-decoration-style: dotted; text-decoration-color: currentColor; }
}
/* 打印 / 导出 PDF：底色不上纸（省墨、也避免印成灰块），三种线型全部保留 */
@media print {
  :is(.drawer-body, .essay-body, .bubble) mark.key {
    background-image: none; background-color: transparent; padding: 0;
    color: var(--ink); text-decoration-color: #8A1418; text-decoration-thickness: 2px;
  }
  :is(.drawer-body, .essay-body, .bubble) u {
    background-color: transparent; text-decoration-color: #7A5244; text-decoration-style: solid;
  }
}
.legend { font-size: 12.5px; color: var(--ink-3); margin-bottom: 10px; }
.drawer-body h3 { font-size: 15px; margin: 18px 0 8px; color: var(--red); }
.drawer-body h3:first-child { margin-top: 0; }
.drawer-body .sec { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 13px; margin-bottom: 12px; box-shadow: var(--shadow-sm); }
.drawer-body .quote { background: var(--card); border-left: 3px solid var(--red); border-radius: 8px; padding: 10px 14px; margin-bottom: 8px; box-shadow: var(--shadow-sm); }
/* 作者署名：右对齐、字号与正文一致（原来 12px，比正文小一档） */
.drawer-body .quote i {
  display: block; font-style: normal;
  color: var(--ink-3);
  font-size: inherit;
  text-align: right;
  margin-top: 4px;
}
.drawer-body .essay-q { background: var(--peach); border-radius: 10px; padding: 11px 13px; font-size: 13.5px; color: var(--ink-2); white-space: pre-wrap; margin-bottom: 14px; }
.drawer-body .essay-body p { margin: 0 0 10px; text-indent: 2em; }
/* 行文思路梳理：原为竹青淡底，现改用 PPT 的陶土棕系（accent2 派生） */
.drawer-body .outline { background: var(--outline-soft); border-radius: 12px; padding: 12px 14px; margin-top: 14px; }
.drawer-body .outline h4 { margin: 0 0 6px; font-size: 13.5px; color: var(--outline-ink); }
.drawer-body .outline div { font-size: 12.5px; color: var(--outline-ink); padding: 2px 0; }
.drawer-body .searchbar { margin-bottom: 12px; }
.drawer-body .searchbar input { width: 100%; padding: 10px 14px; border-radius: 999px; border: 1px solid var(--line); background: var(--card); outline: none; }
.empty { text-align: center; color: var(--ink-3); font-size: 13px; padding: 30px 0; }
.rawbox { white-space: pre-wrap; font-size: 13.5px; color: var(--ink-2); }

/* ── 弹层 ─────────────────────────────────────────── */
.sheet { position: fixed; inset: 0; z-index: 50; background: var(--scrim); display: grid; place-items: center; padding: 22px; }
.sheet-card {
  background: var(--card); border-radius: 18px; padding: 20px;
  /* 必须给 width：只写 max-width 时，弹层宽度会被内容撑开 —— 密码登录那栏只有两个
     普通输入框，就会比「有输入框 + 按钮横排」的验证码栏窄一截。 */
  width: 100%; max-width: 420px; box-sizing: border-box;
  box-shadow: var(--shadow);
  /* 说明文字可能很长（七个段落），限高并允许滚动，免得顶出小屏、按钮点不到 */
  max-height: calc(100vh - 44px); overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.sheet-card h3 { font-size: 16px; margin-bottom: 10px; }
.sheet-card p { font-size: 13.5px; color: var(--ink-2); margin: 0 0 9px; line-height: 1.75; }
.sheet-card p b { color: var(--red); }          /* 「问答」「视频课」这类板块名前缀 */

/* ── 换头像：自己摆位置 ────────────────────────────────────────────
   老师要求：不要自动裁，要能简单调整位置。做法是一个圆形取景框，
   里面的图片可以拖（按下移动）和缩放（滑块；手机上双指也行）。
   · 取景框**是圆的**：头像最终就显示成圆的，圆里即所见，不用脑补方形裁掉角。
   · 圆外那圈暗色用 `box-shadow: 0 0 0 999px` 铺满（经典取景框做法），
     不用额外做遮罩层。
   · 图片用 transform 移动，`touch-action: none` 是关键 —— 否则手机上拖动会变成滚页面。 */
.av-tip { margin-bottom: 12px !important; }
/* 圆外压暗：**不能用 box-shadow 做遮罩** —— 它和 `overflow: hidden`（圆框要靠它裁图）
   在同一个元素上时，实测那圈阴影不渲染。
   也不能把径向渐变放在 .av-stage 上：stage 上下 padding 不一样（4 / 12），
   渐变中心就比分帧中心低 6px，圆边会露出一圈错位的亮边。
   所以专门套一层 .av-mask，它的盒子**正好等于取景框**，渐变天然同心。
   半径 120px 对应下面 .av-frame 的 240px —— 改尺寸要一起改。 */
.av-stage { display: flex; justify-content: center; padding: 4px 0 12px; }
.av-mask {
  background: radial-gradient(circle at 50% 50%,
    rgba(20, 14, 12, 0) 0 120px, rgba(20, 14, 12, .42) 120px);
}
.av-frame {
  position: relative; width: 240px; height: 240px; border-radius: 50%;
  overflow: hidden; background: var(--media-bg); cursor: grab;
  touch-action: none;
}
.av-frame:active { cursor: grabbing; }
.av-frame img {
  position: absolute; left: 0; top: 0; transform-origin: 0 0;
  user-select: none; -webkit-user-drag: none; pointer-events: none;
  max-width: none;                     /* 站内对 img 有 max-width:100%，这里必须放开 */
}
.av-zoom {
  display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
}
/* 标签不许被挤成两行（「缩放」竖着排过一次，很难看） */
.av-zoom .dim { flex: none; font-size: 12.5px; white-space: nowrap; }
.av-zoom input[type="range"] { flex: 1 1 auto; min-width: 0; accent-color: var(--red-vivid); }
.av-btns { display: flex; gap: 10px; }
.av-btns .auth-later { flex: 1 1 auto; margin: 0; }
.av-btns .primary-btn { flex: 1 1 auto; }
.sheet-card p:last-of-type { margin-bottom: 14px; }
.primary-btn { width: 100%; margin-top: 8px; padding: 12px; border-radius: 12px; background: var(--red); color: var(--on-red); font-weight: 600; }

/* ── 口令门（只挡问答区，资料/课程/文章照常看）────────── */
.passgate {
  /* 必须用 fixed：#view-chat 自身没有高度（里面的 .scroll 和 .composer 都是 fixed），
     用 absolute 的话"居中"是相对一个高度为 0 的容器，结果就贴在屏幕最上面。
     这里直接定位到顶栏与底栏之间，再靠 flex 居中。 */
  position: fixed; z-index: 22;
  top: calc(var(--topbar-h) + env(safe-area-inset-top));
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--kb, 0px));
  left: 0; right: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--paper); padding: 24px;
  overflow-y: auto;                /* 手机弹出键盘、屏幕变矮时还能滚 */
}
@media (min-width: 720px) {
  /* 桌面端跟随页面一起居中呈现 */
  .passgate { left: calc(50% - var(--sbw, 0px) / 2); right: auto; width: 720px; transform: translateX(-50%); }
}
.passgate-inner { width: 100%; max-width: 300px; text-align: center; }
.passgate-mark {
  width: 54px; height: 54px; margin: 0 auto 14px; border-radius: 16px;
  background: linear-gradient(150deg, var(--red-deep), var(--red-dark));
  color: var(--on-red); font-size: 26px; font-weight: 700;
  display: grid; place-items: center;
  box-shadow: 0 6px 18px var(--red-glow);
}
.passgate-inner h2 { font-size: 17px; margin-bottom: 6px; }
.passgate-inner .dim { margin-bottom: 16px; }
.passgate-inner input {
  width: 100%; padding: 13px 16px; margin-bottom: 12px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--card);
  font-size: 22px; letter-spacing: .45em; text-indent: .45em; text-align: center;
  font-variant-numeric: tabular-nums; outline: none;
}
.passgate-inner input:focus { border-color: var(--red); box-shadow: 0 0 0 3px var(--red-glow); }
.passgate-inner input.is-error { border-color: var(--red); animation: shake .32s ease; }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

/* ── 人物资料卡：列表模式（一人一卡，点进沉浸式阅读）────────── */
.card-tip { font-size: 12.5px; color: var(--ink-3); margin-bottom: 12px; }
.cardwall { display: grid; grid-template-columns: 1fr; gap: 12px; }
/* 每位一张卡：正面（资料卡）在上、背面（范文应用）在下，一路单列排下来。
   试过宽屏并排两列，但正反面是同一个人的两张，并排看着像两个人。 */
.pcard-pair { display: grid; grid-template-columns: 1fr; gap: 10px; }
.pcard {
  position: relative;
  display: block; width: 100%; padding: 0;
  border: 1px solid var(--line); border-radius: 13px; overflow: hidden;
  background: var(--card); box-shadow: var(--shadow-sm);
  cursor: pointer; transition: transform .12s ease;
}
.pcard img { width: 100%; display: block; }
.pcard:active { transform: scale(.995); }
/* 正/背面角标，压在缩略图左下角 */
.pcard-tag {
  position: absolute; left: 8px; bottom: 8px;
  font-size: 11px; line-height: 1; letter-spacing: .06em;
  padding: 4px 9px; border-radius: 999px;
  background: rgba(36, 30, 28, .62); color: #FFFFFF;
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}

/* ── 人物资料卡：沉浸式阅读（点击翻面 · 左右滑动换人）─────────
   图片占满可用区域；上下留出关闭/列表按钮与页码的位置。 */
.viewer {
  position: fixed; inset: 0; z-index: 60;
  background: var(--viewer-bg);
  display: flex; align-items: center; justify-content: center;
  touch-action: none;              /* 左右滑动不要带动页面滚动 */
  overscroll-behavior: contain;
  user-select: none; -webkit-user-select: none;
  animation: fadeIn .16s ease-out;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.viewer-stage {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  /* 关键：touch-action 不会继承，必须写在真正接收 pointer 事件的元素上，
     否则手机上左右滑动会被浏览器的滚动/回退手势抢走（真机上表现就是"滑不动"）*/
  touch-action: none;
  padding: calc(14px + env(safe-area-inset-top)) 10px calc(36px + env(safe-area-inset-bottom));
  /* 透视统一放在父元素上：比在每个关键帧里写 perspective() 少一层逐帧矩阵
     运算，浏览器也更容易把里面那张图提升成独立的合成层 —— 3D 翻转是否跟手
     基本取决于这一点。stage 里只有 img 一个子元素，不会波及别处。 */
  perspective: 1400px;
  perspective-origin: 50% 50%;
}
.viewer-stage img {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  border-radius: 8px;
  background: var(--on-red);
  box-shadow: var(--shadow-lg);
  will-change: opacity, transform;
  -webkit-user-drag: none;
}
/* 翻面/翻页时给一点淡入反馈，不炫技 */
.viewer-stage img.is-in { animation: vfade .26s ease-out; }
@keyframes vfade { from { opacity: .25; transform: scale(.985); } to { opacity: 1; transform: none; } }

/* 翻面：卡片绕竖轴转到侧对屏幕，换掉图片后再从另一侧转回来。
   两个 90° 的半程把"换图"藏进看不见的那一瞬间 —— 比单纯淡入更像在翻一张
   实体卡片，也不会让人觉得图片"跳"了一下。
   它和 vfade 是两条互斥的路径：vfade 用 transform: none 收尾，同一个元素上
   会打架，所以翻面时由 JS 改挂 is-flip、不再叠加 is-in。 */
.viewer-stage img.is-flip {
  animation: card-flip 1s linear;
  transform-origin: center center;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  /* 动画期间单独声明一次，确保图片在整个翻转过程里都待在合成层上，
     不会因为中途的绘制变化被降级回主线程光栅化。 */
  will-change: transform;
}
/* 分段缓动，而不是拿一条曲线套满全程。
   用一条 cubic-bezier(.42,0,.34,1) 时，起点和终点附近几乎静止，1 秒的时长
   把"慢启动 + 慢收尾"放大得很明显，中间还是个方向反转，看着就一挫一挫的。
   现在：转出去用 ease-in 一路加速（转到侧面时最快），换图那一瞬不减速，
   转进来用 ease-out 减速收住 —— 速度曲线是从 0 升到峰值、再平滑降到 0。 */
@keyframes card-flip {
  0%    { transform: rotateY(0deg);   animation-timing-function: ease-in; }
  49.5% { transform: rotateY(-90deg); animation-timing-function: linear; }
  50.5% { transform: rotateY(90deg);  animation-timing-function: ease-out; }
  100%  { transform: rotateY(0deg); }
}

/* 左上「列表」：切回列表模式（浏览全部人物） */
.viewer-list {
  position: absolute; z-index: 2;
  top: calc(12px + env(safe-area-inset-top)); left: 14px;
  height: 38px; padding: 0 15px; border-radius: 19px;
  font-size: 13px; letter-spacing: .06em; line-height: 1; color: var(--on-red);
  background: var(--on-red-16); display: grid; place-items: center;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.viewer-close {
  position: absolute; z-index: 2;
  top: calc(12px + env(safe-area-inset-top)); right: 14px;
  width: 38px; height: 38px; border-radius: 50%;
  font-size: 22px; line-height: 1; color: var(--on-red);
  background: var(--on-red-16); display: grid; place-items: center;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
/* 底部一条件：上一张 · 页码 · 下一张。
   放在底部而不是左右两侧：手机屏幕窄，两侧浮层必然压住卡面上的字。
   箭头用伪元素画（上+左 / 上+右边框各转 45°），和抽屉返回键同一套做法 ——
   直接用 Unicode 的 ‹ › 在各个系统上字宽与笔画都不一样。 */
.viewer-bar {
  position: absolute; z-index: 2; left: 50%; transform: translateX(-50%);
  bottom: calc(10px + env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: 16px;
}
.viewer-nav {
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 0;                            /* 按钮内没有文字，压掉可能的字形 */
  color: var(--on-red);
  background: var(--on-red-16);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  transition: background-color .15s;
}
.viewer-nav::before {
  content: ''; display: block;
  width: 10px; height: 10px;
  border-top: 2px solid currentColor;
  border-radius: 2px 0 0 0;
}
#viewerPrev::before {
  border-left: 2px solid currentColor;     /* 上+左 → 旋转后成「<」 */
  transform: rotate(-45deg) translate(1px, 0);
}
#viewerNext::before {
  border-right: 2px solid currentColor;    /* 上+右 → 旋转后成「>」 */
  transform: rotate(45deg) translate(-1px, 0);
}
.viewer-nav:active { background: var(--on-red-18); }

.viewer-page {
  font-size: 12.5px; letter-spacing: .08em;
  color: var(--on-red); opacity: .55;
  min-width: 62px; text-align: center;
}
/* 首次进入提示操作方式，之后自动消失且不再出现 */
.viewer-hint {
  position: absolute; z-index: 2; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  padding: 9px 16px; border-radius: 999px;
  background: var(--on-red-16); color: var(--on-red);
  font-size: 13px; letter-spacing: .04em; white-space: nowrap;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  animation: fadeIn .2s ease-out;
  pointer-events: none;
}

/* ── 无障碍：键盘焦点环 + 尊重「减弱动态效果」───────────────── */
:where(.tab, button, a, input, textarea):focus-visible {
  outline: 2px solid var(--red-vivid);
  outline-offset: 2px;
  border-radius: 8px;
}
.tab:focus-visible { outline-offset: -3px; border-radius: 10px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── 大屏（桌面端居中呈现）───────────────────────── */
@media (min-width: 720px) {
  /* 背景与内容同色：不再用深一档的外框，两侧就不会出现"多出来的一块" */
  body { background: var(--paper); }
  /* 内容留白保持基础值，宽屏下不再额外加宽（加宽会在左侧多出一条空白） */
  .topbar, .composer, .view .scroll, .tabbar { max-width: 720px; margin: 0 auto; }
  .topbar { left: calc(50% - var(--sbw, 0px) / 2); transform: translateX(-50%); width: 720px; }
  .composer { left: calc(50% - var(--sbw, 0px) / 2); transform: translateX(-50%); width: 720px; }
  .scroll { left: calc(50% - var(--sbw, 0px) / 2); transform: translateX(-50%); width: 720px; }
  .tabbar { left: calc(50% - var(--sbw, 0px) / 2); transform: translateX(-50%); width: 720px; }
  /* 秘笈/资料详情走的是抽屉。这里不让"抽屉"和"上一级内容列"两个 720px 盒子
     去对齐 —— 那样太脆：只要两者的 left 算式在任何环境下算出一点差别
     （滚动条补偿、取整、缩放），抽屉边缘就会漏出下面一级页的一角。
     改成不依赖对齐的做法：
       · 抽屉自己铺满全屏 → 一定能盖住下面的一级页；
       · 抽屉底色用 --paper，和页面底一致 → 两侧看起来就是页面留白；
       · 头部和内容各自限宽 720px、由 align-items: center 居中、底色用 --bar
         → 中间那条和上一级是同一个颜色和同一个宽度。
     于是详情页和上一级是同一个版式，且任何环境下都不会露馅。 */
  .drawer {
    left: 0; right: 0; width: auto;
    background: var(--paper);
    align-items: center;
  }
  .drawer-head, .drawer-body {
    width: 100%; max-width: 720px;
    background: var(--bar);
  }
  .grid { grid-template-columns: repeat(3, 1fr); }
  .book-grid { grid-template-columns: repeat(3, 1fr); }
  /* 鼠标端导航：加一点悬停反馈，双击区仍等于整列。
     两个坑：① 别在这里再写 transform —— 色块定位靠 translate(-50%,-50%)，
     覆盖它会把色块推离中心（旧版这里写的是 scaleX，属于历史遗留）；
     ② 红字只能给「未选中」的 tab，否则悬浮在选中项上会变成红字压红块。 */
  .tab:not(.is-active):hover { color: var(--red); }
  .tab:hover::before { opacity: .32; }
  .tab:hover::after { opacity: .32; }
  .tab.is-active:hover::before { opacity: 1; }
  .tab.is-active:hover::after { opacity: 1; }
}

/* ── 竹青色的处置：移除 ────────────────────────────────
   原 --green #4a6b52 / --green-soft #e6efe7 在 PPT 的 clrScheme 与实际用色
   统计里都没有出处（主题色板为暖棕 + 红 + 金，无绿系）。一套配色里多一个
   冷暖对立的色相，会稀释「红 + 金」的话语权，也让底色偏暖的整体调性被割裂。
   它原本承担三处职责，全部改由 PPT 里真实存在的色系接管：
     · .msg.user .bubble / .avatar  → 浅桃 #F7C9C1 + 陶土棕（accent2 派生）
     · .lesson .no 序号块            → 同上
     · .drawer-body .outline 思路块  → 陶土棕淡底 #F6E3D9 + #7A4A38（accent2 压深）
   这样红/金仍然是唯二的强调色，第三个色相只出现在「非强调的容器」里。 */

/* ── 滚动条：保持可见（能看出还有内容），但做细、圆角、不抢视线 ──
   注意面板本身已有 14px 内边距，滚动条落在内边距里，不会压住文字。 */
.scroll { scrollbar-width: thin; scrollbar-color: rgba(var(--shadow-ink), .22) transparent; }
.scroll::-webkit-scrollbar { width: 8px; height: 8px; }
.scroll::-webkit-scrollbar-track { background: transparent; }
.scroll::-webkit-scrollbar-thumb {
  background: rgba(var(--shadow-ink), .2);
  border-radius: 4px;
  border: 2px solid transparent;
  background-clip: content-box;
}
.scroll::-webkit-scrollbar-thumb:hover { background: rgba(var(--shadow-ink), .34); background-clip: content-box; }
.drawer-body::-webkit-scrollbar { width: 8px; }
.drawer-body::-webkit-scrollbar-thumb {
  background: rgba(var(--shadow-ink), .2); border-radius: 4px;
  border: 2px solid transparent; background-clip: content-box;
}

/* ── 长文资料：2026 高考作文真题解析 ──────────────────────────────
   数据由 tools/build_exam2026.py 切好「块」，前端 blocksHtml() 渲染。
   纯长文排版：标题分层、引用块、表格横向可拖。选择器一律带
   .drawer-body 前缀 —— 否则会被上面的 `.drawer-body h3` 覆盖。 */
.drawer-body .blk-meta {
  font-size: 12.5px; line-height: 1.65; color: var(--ink-3);
  background: var(--peach); border-radius: 8px;
  padding: 10px 12px; margin-bottom: 16px;
}
.drawer-body .blk-h1 {
  font-size: 15.5px; color: var(--red);
  margin: 22px 0 10px; padding-top: 14px;
  border-top: 2px solid var(--red-soft);
}
.drawer-body .blk-h1:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.drawer-body .blk-h2 { font-size: 14.5px; color: var(--ink); margin: 16px 0 7px; }
.drawer-body .blk-h3 { font-size: 13.5px; color: var(--ink-2); margin: 13px 0 6px; }
.drawer-body .blk-p { margin: 0 0 10px; line-height: 1.78; }
.drawer-body .blk-ul,
.drawer-body .blk-ol { margin: 0 0 11px; padding-left: 21px; line-height: 1.75; }
.drawer-body .blk-ul li,
.drawer-body .blk-ol li { margin-bottom: 5px; }
.drawer-body .blk-q {
  margin: 0 0 12px; padding: 9px 12px;
  background: var(--peach); border-left: 3px solid var(--red);
  border-radius: 8px; font-size: 13.5px; line-height: 1.75; color: var(--ink-2);
}

/* 表格：手机一屏放不下六列，宁可横拖也不挤成一团 */
.md-table-wrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  margin: 0 0 14px; padding-bottom: 3px;
}
.md-table {
  border-collapse: collapse; font-size: 12.5px; line-height: 1.6;
  /* 列内容不换行：整表按内容撑开，放不下就横向拖 */
  width: max-content; min-width: 100%;
}
.md-table th,
.md-table td {
  border: 1px solid var(--line); padding: 7px 10px;
  text-align: left; vertical-align: top;
  white-space: nowrap;
}
.md-table th {
  background: var(--red-soft); color: var(--red);
  font-weight: 600; white-space: nowrap;
}
/* 列宽交给内容决定（nowrap 已保证可读），不再限制最小宽度 */
/* 隔行淡底：rgba 取的是 --line(#F0DCD3) 的 28% */
.md-table tbody tr:nth-child(even) { background: rgba(240, 220, 211, .28); }

/* ── 34 本书导读 ──────────────────────────────────────────────────
   每本书的导读由 build_content.py 切好（bk.intro 是行数组），前端按
   「小节标题 + 段落」排开。以前是一整块 pre-wrap，长导读看不出层次。 */
.book-intro { font-size: 14px; line-height: 1.8; }
.book-intro .book-line { margin: 0 0 9px; }
.book-intro .book-sec {
  font-size: 13.5px; color: var(--red); font-weight: 700;
  margin: 16px 0 8px; padding-left: 9px;
  border-left: 3px solid var(--red);
}
.book-intro .book-sec:first-child { margin-top: 0; }

/* ── 「其他」按板块分组：拓展资料 / 专项练习 / 马老师说 ───────────
   组标题直接复用 .section-title（正红短竖那套层级语言），这里只管
   组与组之间的距离，以及容器本身不再是网格（网格在每组内部）。 */
.mat-groups { display: block; }
.mat-group + .mat-group > .section-title { margin-top: 26px; }

/* ── 文化常识题库（专项练习）─────────────────────────────────────── */
.drill { padding: 13px 0; border-bottom: 1px dashed var(--line); }
.drill:last-child { border-bottom: 0; }
/* 题干与选项都排成「编号列 + 文字列」两列：文字左边界一致，
   换行后的第二行也贴着文字列，不会缩回最左边。 */
.drill-q { display: flex; align-items: baseline; gap: 9px; font-weight: 600; line-height: 1.7; margin-bottom: 7px; }
.drill-q .item-no {
  /* 编号列定宽，不用 min-width：这些题库动辄上百条（成语精练 192 条），
     两位数编号会把 16px 的下限撑到 19.5px，文字列跟着右移 2.5px ——
     于是第 1 条和第 10 条的正文左边界不在同一条竖直线上。
     定宽 24px 够放两位数（15px 字号实测约 19.5px，留了余量），
     右对齐让 1 和 10 的右边缘也对齐。
     正文左边界 = 14(容器) + 24(编号列) + 9(gap) = 47px，恒定。
     注：gap 原先只有 3px，个位数编号（实测宽 9.27px）到题干文字只隔 3px，
     看着贴在一起；调到 9px 后空隙舒展，同时把下面各处缩进一并从 27 改到 33。 */
  flex: none; width: 24px; min-width: 0; text-align: right;
  line-height: inherit;   /* 全局那条 line-height:1 会让数字浮到题干文字上方 */
  font-weight: 700; color: var(--red);
  margin-right: 0;   /* 间距交给 flex 的 gap：全局 .item-no 那条 margin-right:4px
                        与 gap 叠加后题干文字会比选项字母多出 4px，量出来就是没对齐 */
}
.drill-q .drill-text { flex: 1 1 auto; min-width: 0; }
.drill .opt {
  display: flex; gap: 6px;
  /* 实测关系（都是在这张卡片里量出来的，不是推算）：
       选项文字左边界 = padding-left + 37   （4px → 41、10px → 47、14px → 51）
       选项字母左边界 = padding-left + 14
     取 padding-left 10px ⇒ 选项文字落在 47，与题干文字列（47）和「看答案」（47）
     精确同列；字母落在 24，仍在条目里（条目左边界 14）。
     这里跟着 .drill-q 的 gap 一起调过：gap 由 3 加到 9 后，题干文字列从 41 移到 47，
     选项行必须同步 +6 才不会跟题干错开一截。 */
  padding-left: 10px;
  font-size: 13.5px; line-height: 1.75; color: var(--ink-2); margin-bottom: 3px;
}
/* 选项字母：定宽 + 右对齐（17px 放得下 A/B/C/D）。
   定宽的作用：不同字母宽度不一样（I 比 W 窄），不定宽会让每行的字母右边缘
   参差不齐、进而把选项文字也带歪；定宽后一律同宽。 */
.drill .opt b {
  flex: none; width: 17px; text-align: right;
  color: var(--red); font-weight: 700;
}
.drill .opt span { flex: 1 1 auto; min-width: 0; }
.drill-ans { margin-top: 9px; }
.drill-ans summary {
  display: inline-block; font-size: 12.5px; color: var(--red); cursor: pointer;
  padding: 4px 11px; background: var(--red-soft);
  border: 1px solid var(--red-line); border-radius: 999px;
}
.drill-ans[open] summary { margin-bottom: 8px; }
.drill-ans .ans-line {
  font-size: 13px; line-height: 1.78; color: var(--ink-2);
  background: var(--peach); border-radius: 8px; padding: 9px 11px;
}
.drill-ans .ans-line b { color: var(--red); margin-right: 6px; }

/* ── 四点答题法：先自己写，再点开对答案 ─────────────────────────── */
.drawer-body .af { padding: 14px; }
.af-material {
  background: var(--paper); border-radius: 8px; padding: 10px 12px;
  font-size: 13px; line-height: 1.85; color: var(--ink-2); margin-bottom: 9px;
}
.af-q { font-weight: 600; line-height: 1.7; margin-bottom: 9px; }
/* 作答区：学生先自己写四点，草稿存在本机 */
.af-input {
  display: block; width: 100%; box-sizing: border-box;
  min-height: 88px; resize: vertical; font: inherit;
  font-size: 13.5px; line-height: 1.75; color: var(--ink);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 8px; padding: 10px 12px; margin-bottom: 10px;
}
.af-input::placeholder { color: var(--ink-3); }
.af-input:focus { outline: none; border-color: var(--red-line); box-shadow: 0 0 0 3px var(--red-glow); }
/* 对照答案：默认折叠，点开才看 */
.af-fold { margin-bottom: 8px; font-size: 13px; line-height: 1.85; }
.af-fold > summary {
  display: inline-block; font-size: 12.5px; cursor: pointer;
  padding: 4px 11px; border-radius: 999px;
  background: var(--red-soft); border: 1px solid var(--red-line); color: var(--red);
}
.af-fold[open] > summary { margin-bottom: 8px; }
.af-fold > div { border-radius: 8px; padding: 10px 12px; }
.af-fold > div > div,
.af-fold > div > p { margin: 0 0 5px; }
.af-fold > div > *:last-child { margin-bottom: 0; }
.af-ref > div { background: var(--paper); border-left: 3px solid var(--line); }
.af-mine > div { background: var(--peach); border-left: 3px solid var(--red); }

/* ── 长文目录块（马老师长文）────────────────────────────────────── */
.blk-toc {
  background: var(--paper); border-radius: 8px; padding: 11px 13px;
  margin-bottom: 16px; font-size: 13px; line-height: 1.9; color: var(--ink-2);
}
.blk-toc h4 { font-size: 12.5px; color: var(--ink-3); margin: 0 0 6px; font-weight: 600; letter-spacing: .04em; }

/* ── 52+ 学员范文（题目材料 + 评分细则 + 范文列表 + 讲评）────────── */
.ep-material {
  background: var(--paper); border-radius: 8px; padding: 10px 12px;
  font-size: 13px; line-height: 1.85; color: var(--ink-2); margin-bottom: 9px;
}
.ep-rules { margin-bottom: 11px; font-size: 13px; line-height: 1.8; color: var(--ink-2); }
.ep-rules summary {
  display: inline-block; font-size: 12.5px; color: var(--red); cursor: pointer;
  padding: 4px 11px; background: var(--red-soft);
  border: 1px solid var(--red-line); border-radius: 999px;
}
.ep-rules[open] summary { margin-bottom: 8px; }
.ep-rules > div { margin-bottom: 4px; }
.ep-comments {
  background: var(--peach); border-left: 3px solid var(--red);
  border-radius: 8px; padding: 11px 12px; margin-top: 16px;
  font-size: 13px; line-height: 1.85;
}
.ep-comments h4 { font-size: 12.5px; color: var(--red); margin: 0 0 6px; letter-spacing: .04em; }
.ep-comments > div { margin-bottom: 5px; }

/* ── 成语专项精练：只给解释，学生填成语 ─────────────────────────── */
.drawer-body .drill-word { padding: 12px 0; border-bottom: 1px dashed var(--line); }
.drill-word:last-child { border-bottom: 0; }
.drill-word .drill-q { margin-bottom: 7px; }
.drill-input {
  display: block; box-sizing: border-box;
  /* 与题干文字列对齐：左右各让出「编号列 24 + 间距 9」= 33px。
     宽度必须显式算：<input> 不像 <div>，去掉 width 后不会自动撑满，
     而是退回浏览器默认的 size=20 宽度（实测只剩 179px，框变得很短）。
     也不能写 width:100% —— 它按包含块算、不减去外边距，33 + 354 会溢出。
     所以用 calc(100% - 66px)：354 - 66 = 288，正好落在 47 ~ 335。 */
  width: calc(100% - 66px);
  margin: 0 33px 8px;
  font: inherit; font-size: 14px; color: var(--ink);
  background: var(--card); border: 1px solid var(--line);
  border-radius: 8px; padding: 8px 11px;
}
.drill-input::placeholder { color: var(--ink-3); font-size: 13px; }
.drill-input:focus { outline: none; border-color: var(--red-line); box-shadow: 0 0 0 3px var(--red-glow); }
/* 成语填空（.drill-word）里的答案区：
   · 整块左侧 33px → 「看答案」按钮落在题干文字列 47
   · 框内 .ans-line 的左内边距归零 → 答案文字也从 47 起（它自带 11px，
     加上外层缩进就会比题干多缩一截，这正是之前看到的不对齐）
   · 宽度用 calc(100% - 33px)：这里 100% 指的是父级**内容区**宽度（354-33=321），
     不是整个条目宽；减 33 后为 288，右边缘落在 335，与填空框右缘齐平。
   注意用 .drill-word 而不是 .drill —— 文化常识题库是另一套结构，不会牵连它。 */
.drill-word > .drill-ans { padding-left: 33px; }
.drill-word > .drill-ans .ans-line {
  padding-left: 0;
  width: calc(100% - 33px);
  margin-right: 33px;
}

/* ── 视频课：点击才加载 ──────────────────────────────────────────
   preload="metadata" 会为每个视频读一次尾部 moov（四个合计约 13 MB，
   而且是按流量计费的）。改成先只放一个播放键，点了才创建 <video>：
   打开这一页零视频请求。观感与「马老师的读书节目」那套播放键一致。 */
.video-frame { position: relative; width: 100%; padding-top: 56.25%; background: var(--media-bg); }
.video-frame video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border: 0; display: block; background: var(--media-bg);
}
.video-play {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: linear-gradient(160deg, #3A2E28, #241E1C);
  color: var(--on-red); border: 0;
}
.video-play-icon {
  width: 0; height: 0;
  border-left: 18px solid currentColor;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  margin-left: 4px;                 /* 三角形重心偏左，右移一点才是视觉居中 */
  opacity: .92;
}
.video-play-text { font-size: 13px; letter-spacing: .1em; opacity: .85; }
.video-play:active { background: linear-gradient(160deg, #2E2520, #1C1715); }
/* ── 封面（预览画面）─────────────────────────────────────────────
   封面是单独抽帧生成的 WebP（public/cards/video/，480×270、每张 5–10 KB），
   不是让浏览器读视频拿首帧 —— 那会为每张卡片吃掉几 MB 流量。
   ⚠️ 封面上**不再压「点击播放」**（老师要求去掉）：整张卡都可点，封面本身就是
   「这是个视频」的提示。所以 `.video-play / -icon / -text` 这套现在只服务
   「哪条视频没抽封面」的兜底（深底 + 一枚三角），有封面时压根不渲染。
   历史上那套「暗角 / 胶囊」的写法（.video-card.has-poster …）已随按钮一起去掉。 */
.video-poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}

/* ── 视频播放页（抽屉里，老师要求：列表点进去看，就像 B 站那样）─────────
   列表页只管列（只有封面图，零视频请求），播放器与详情都在这里。 */
.vp-player {
  background: #000; border-radius: 10px; overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.vp-player video {
  display: block; width: 100%; aspect-ratio: 16/9;
  background: #000; object-fit: contain;
}
.vp-title { margin: 13px 0 5px; font-size: 16.5px; line-height: 1.45; letter-spacing: .01em; }
.vp-desc { margin: 0; font-size: 13px; line-height: 1.75; color: var(--ink-2); }
.vp-more-h {
  margin: 20px 0 9px; font-size: 13px; font-weight: 600; color: var(--ink-3);
  letter-spacing: .04em;
}
.vp-more { display: flex; flex-direction: column; gap: 11px; padding-bottom: 8px; }
.vp-more-item {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 0; border: 0; background: transparent; text-align: left;
  cursor: pointer;                 /* button 默认是箭头，鼠标用户看不出能点 */
}
.vp-more-item:active { opacity: .7; }
.vp-more-img {
  flex: none; width: 104px; aspect-ratio: 16/9; border-radius: 8px;
  object-fit: cover; background: var(--media-bg); display: block;
}
.vp-more-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.vp-more-t { font-size: 13.5px; line-height: 1.4; }
.vp-more-d {
  font-size: 11.5px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── 7+3 范文详情：标题居中、正文楷体 ─────────────────────────────
   楷体只挂在 .is-kaiti 上。学员范文（essays_plus）虽然共用 .essay-body，
   但没这个类，所以不受影响 —— 要一起改的话给它也加上即可。 */
.drawer-body .essay-title {
  text-align: center;
  margin: 2px 0 16px;
  font-size: 17px;
  letter-spacing: .02em;
}
.drawer-body .essay-body.is-kaiti {
  font-family: "STKaiti", "Kaiti SC", "KaiTi", "楷体", "华文楷体", "STSong", serif;
  font-size: 15px;
  line-height: 2;
}

/* ── 文言文实词 250：词条红、解释带下划线、例句楷体 ────────────────
   解释与例句原来共用 .eg，这次拆成 .def / .sent 才能分别设样式。
   字体栈与 7+3 范文一致：iOS 走华文楷体，Windows 走楷体。 */
.drawer-body .entry .k.w-red { color: var(--red); }   /* 只给实词那一行，别影响虚词口诀 */
.drawer-body .entry .eg.def {
  color: var(--ink);
  font-size: 14.5px;                /* 原为 13px —— 这是要背的主体内容 */
  line-height: 1.75;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.drawer-body .entry .eg.sent {
  font-family: "STKaiti", "Kaiti SC", "KaiTi", "楷体", "华文楷体", "STSong", serif;
  font-size: 14px;                  /* 原为 13.5px；楷体行高不足会显挤，一并放到 1.85 */
  line-height: 1.85;
}

/* ── 秘笈怎么用：标题红、括号提示红 ───────────────────────────────
   标题是第一条「马老师独家秘笈使用说明」；各条里的全角括号写的是「怎么做」
   （背 / 反复看 / 每三天背一次），标红后一眼能扫到操作要点。 */
.drawer-body .guide-title {
  color: var(--red);
  font-weight: 700;
  font-size: 15.5px;
  margin: 0 0 11px;
}
.drawer-body .guide-p .note { color: var(--red); }

/* ── 「其他」三个栏目之间的切换条 ─────────────────────────────────
   点哪个按钮就只显示哪一栏。不留底色 —— 栏目已经是独立页，不需要遮挡
   滚动内容，按钮下面多一道白条反而碍眼。 */
.group-nav {
  position: sticky; top: 0; z-index: 6;
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 2px 0 12px;
}
.group-nav button {
  font: inherit; font-size: 13px; line-height: 1;
  padding: 7px 14px; border-radius: 999px; cursor: pointer;
  background: var(--card); color: var(--ink-2);
  border: 1px solid var(--line);
  transition: background .18s, color .18s, border-color .18s;
}
.group-nav button:active { transform: scale(.97); }
.group-nav button.is-active {
  background: var(--red-soft); color: var(--red);
  border-color: var(--red-line); font-weight: 600;
}

/* ── 《论语》三十三则：正文楷体 ───────────────────────────────────
   序号用 .quote 里现成的 .item-no（灰），正文走 .q-main。
   字体栈与 7+3 范文、实词例句一致。 */
.drawer-body .quote.analect .q-main {
  font-family: "STKaiti", "Kaiti SC", "KaiTi", "楷体", "华文楷体", "STSong", serif;
  font-size: 15px;
  line-height: 1.9;
}

/* ── 时代现象与常识性事实：按层级排开 ─────────────────────────────
   ① 分组标题（时代现象 / 常识性事实 / eg）红色短竖
   ② 词条（马太效应、鸦片战争…）红字 + 浅底卡，说明并进卡里
   ③ 例句段落 楷体 + 左侧竖线，一眼看出是引文
   ④ 现象列举 圆点条目 */
.drawer-body .ts-h {
  font-size: 15px; color: var(--red);
  margin: 22px 0 10px; padding-left: 10px;
  border-left: 3px solid var(--red); letter-spacing: .04em;
}
.drawer-body .ts-h:first-child { margin-top: 0; }
.drawer-body .ts-term {
  background: var(--paper); border-radius: 8px;
  padding: 10px 12px; margin-bottom: 9px;
}
.drawer-body .ts-term > b { display: block; color: var(--red); font-size: 14.5px; margin-bottom: 4px; }
.drawer-body .ts-term .ts-note { font-size: 13.5px; line-height: 1.8; color: var(--ink-2); }
.drawer-body .ts-entry { font-size: 14px; line-height: 1.8; }
.drawer-body .ts-entry b { color: var(--red); }
.drawer-body .ts-para {
  font-family: "STKaiti", "Kaiti SC", "KaiTi", "楷体", "华文楷体", "STSong", serif;
  font-size: 14px; line-height: 1.9; color: var(--ink-2);
  margin: 0 0 10px; padding: 2px 0 2px 12px;
  border-left: 2px solid var(--line);
}
.drawer-body .ts-item {
  position: relative; font-size: 14px; line-height: 1.8;
  padding: 3px 0 3px 14px;
}
.drawer-body .ts-item::before {
  content: '·'; position: absolute; left: 3px; top: 3px;
  color: var(--red); font-weight: 700;
}

/* ── 课内文言文知识点梳理：条目行分层 ─────────────────────────────
   一段里挤着「条目名 + 古/今说明 + ①②③义项」，不分开的话全是一片黑，
   扫不出「这一条要记的是哪个字」。 */
.blk-p .wy-item { display: block; padding: 3px 0 1px; }
.blk-p .wy-item .wy-no {
  font-style: normal; color: var(--ink-3);
  margin-right: 5px; font-variant-numeric: tabular-nums;
}
.blk-p .wy-item b { color: var(--red); }
.blk-p .wy-note {
  display: block; padding-left: 18px;
  color: var(--ink-2); font-size: 13.5px; line-height: 1.7;
}
.blk-p .wy-sub { display: block; padding-left: 18px; }
.blk-p .wy-sub .wy-no { font-style: normal; color: var(--red); margin-right: 4px; }
.blk-p .wy-line { display: block; }

/* ── 18 个虚词记忆方法：义项黑、例句楷体 ───────────────────────────
   原来整块都是灰褐色小字，义项和例句分不出来。要点行（==…==）走考点高亮，
   其余按行分层。 */
.rawbox .xc-key { display: block; }
.rawbox .xc-sense {
  display: block; color: var(--ink); font-weight: 600;
  padding: 5px 0 2px;
}
.rawbox .xc-eg {
  display: block; padding-left: 16px;
  font-family: "STKaiti", "Kaiti SC", "KaiTi", "楷体", "华文楷体", "STSong", serif;
  font-size: 14px; line-height: 1.85; color: var(--ink-2);
}
.rawbox .xc-term {
  display: block; color: var(--ink); font-weight: 600;
  padding: 6px 0 2px;
}
.rawbox .xc-head {
  display: block; color: var(--ink-3); font-size: 12.5px;
  padding: 8px 0 2px; letter-spacing: .04em;
}
.rawbox .xc-line { display: block; }

/* ── 《论语》末尾的「相关推荐」 ───────────────────────────────────
   这两条是延伸读物（井上靖《孔子》、电影《孔子秘密档案》），不是经文：
   不编号、不用楷体，做成两张小卡。 */
.drawer-body .rec-list { display: flex; flex-wrap: wrap; gap: 8px; }
.drawer-body .rec-item {
  font-size: 14px; line-height: 1.6;
  padding: 8px 13px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: 8px;
}
.drawer-body .rec-item strong { color: var(--red); }

/* ── 「其他」三个独立页：一次只显示一栏 ───────────────────────────
   原来三栏上下排着，靠导航滚动跳转；现在点导航直接换页，页面短了、
   也不再有「滚到哪一栏」的判断问题。 */
.mat-groups .mat-group { display: none; }
.mat-groups .mat-group.is-active { display: block; }
.group-nav button {
  transition: background .18s, color .18s, border-color .18s;
}

/* ── 课内文言文知识点梳理：按册分页 ───────────────────────────────
   337 个块全铺开太长，现在一次只显示一册（必修一到五 + 选修），
   顶部一条切换 —— 按钮样式直接复用 .group-nav。 */
.book-nav { margin-bottom: 16px; }
.book-page { display: none; }
.book-page.is-active { display: block; }

/* 「看答案」按钮与题干文字左对齐（同一列 47px）：答案区没有选项字母占位，
   所以这里用的是「编号列 24 + gap 9」= 33px，不是选项那行的 14px。
   只作用于 .drill（文化常识题库）；.drill-word（成语填空）是另一套结构，
   它的缩进在 .drill-word > .drill-ans 那条规则里，两者不要互相套用。 */
.drill > .drill-ans { padding-left: 33px; }

/* ── 收藏键（详情抽屉右上角）─────────────────────────────────────
   每份资料都能收藏：未收藏是描边胶囊，收藏后变朱红实心。
   抽屉头部本来就是 flex + 标题 flex:1，加这个按键不会挤到标题。 */
.fav-btn {
  flex: none; display: inline-flex; align-items: center; gap: 4px;
  font: inherit; font-size: 12.5px; line-height: 1;
  padding: 7px 11px; border-radius: 999px; cursor: pointer;
  background: var(--card); color: var(--ink-2);
  border: 1px solid var(--line);
  transition: background .18s, color .18s, border-color .18s;
}
.fav-btn:active { transform: scale(.97); }
.fav-btn .fav-star { font-size: 14px; line-height: 1; }
.fav-btn.is-on {
  background: var(--red-soft); color: var(--red);
  border-color: var(--red-line); font-weight: 600;
}

/* ── 登录 / 注册弹层 ─────────────────────────────────────────── */
.auth-tabs {
  display: flex; gap: 6px; margin: 0 0 14px;
  background: var(--paper); border-radius: 10px; padding: 4px;
}
.auth-tabs button {
  flex: 1 1 0; font: inherit; font-size: 13px; line-height: 1;
  padding: 9px 8px; border-radius: 8px; cursor: pointer;
  background: transparent; color: var(--ink-2); border: 0;
}
.auth-tabs button.is-active {
  background: var(--card); color: var(--red); font-weight: 600;
  box-shadow: 0 1px 3px rgba(120, 60, 40, .12);
}
.sheet-card input {
  display: block; width: 100%; box-sizing: border-box;
  font: inherit; font-size: 14px; line-height: 1.4;
  padding: 11px 13px; margin-bottom: 10px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: var(--ink);
}
.sheet-card input:focus { outline: none; border-color: var(--red); }
/* 比 .sheet-card p 更具体，才压得住它那条 margin */
.sheet-card .auth-msg {
  min-height: 17px; margin: 0 0 10px;
  font-size: 12.5px; line-height: 1.5; color: var(--red);
}
.auth-later {
  display: block; width: 100%; margin-top: 10px;
  font: inherit; font-size: 13px; padding: 8px; cursor: pointer;
  background: none; border: 0; color: var(--ink-3);
}
.auth-later:active { color: var(--red); }

/* ── 账户中心 ────────────────────────────────────────────────── */
.me-card {
  background: var(--paper); border-radius: 14px;
  padding: 16px; margin-bottom: 14px;
}
.me-card h3 { font-size: 15px; margin-bottom: 8px; }
.me-card p { font-size: 13.5px; line-height: 1.75; color: var(--ink-2); margin: 0 0 12px; }
.me-card .primary-btn { width: 100%; }

/* 「我的」主页的账户入口：整张卡是一个按钮，点它进次级页。
   顶栏在这一页被收起来了，所以用户名要自己承担「我现在是谁 + 从这儿进去」两件事。 */
.me-userentry {
  display: flex; align-items: center; gap: 12px; width: 100%;
  box-sizing: border-box; text-align: left; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 14px 16px; margin-bottom: 14px;
  transition: border-color .15s, background-color .15s;
}
.me-userentry:active { border-color: var(--red); background: var(--red-soft); }
/* 头像（站内统一组件）：有图显示图，没图显示名字首字 —— 老用户一张图都不传也有头像，
   不会出现一排空白圆圈。圆形 + 一点内描边，让它"落"在纸面上。 */
.avatar {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; overflow: hidden; background: var(--red-soft);
  color: var(--red); font-weight: 600; user-select: none;
  box-shadow: inset 0 0 0 1px rgba(var(--shadow-ink), .06);
}
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar-ini { line-height: 1; transform: translateY(.5px); }
.avatar-22 { width: 22px; height: 22px; font-size: 12px; }
.avatar-44 { width: 44px; height: 44px; font-size: 18px; }
.avatar-56 { width: 56px; height: 56px; font-size: 22px; }
/* 用户条：头像 + （名字 / 手机号）竖排 + 箭头。
   原来是「名字 手机号 ›」一行三格，加了头像后一行摆不下 —— 名字会被挤成
   两三个字加省略号，比原来的观感更差，所以这里改成竖排。 */
.me-userentry-txt { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.me-userentry-name {
  font-size: 16px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.me-userentry-sub {
  font-size: 12.5px; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.me-userentry-arrow { flex: none; font-size: 19px; line-height: 1; color: var(--ink-3); }

/* 账户设置：换头像就是**点身份条上那个头像本身**（老师要求：不单开一条「头像」）。
   右下角一枚小红 ＋ 提示它能点。 */
.avatar-btn {
  position: relative; flex: none; padding: 0; border: 0; background: transparent;
  cursor: pointer; line-height: 0;
}
.avatar-btn:active { opacity: .75; }
.avatar-cam {
  position: absolute; right: -3px; bottom: -3px;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--red-vivid); color: #fff;
  font-size: 12px; line-height: 17px; text-align: center;
  box-shadow: 0 0 0 2px var(--card);
}
.me-avatar-msg { margin: 0 0 6px; }   /* 紧跟身份条下面，不额外占一大条空隙 */

/* 账户设置（次级页）顶部的身份条。
   ⚠️ 这里**不能用 `justify-content: space-between`**：名字块不撑开时，
   多出来的空间会被平分到各元素**之间** —— 头像和用户名之间就空出一大截
   （老师：「用户名和电话离左边的头像太远了」）。改成让名字块自己吃掉中间的空位，
   右侧的权限徽章自然贴到最右边，头像和名字之间只留 gap 的 12px。 */
.me-account-top {
  display: flex; align-items: center; gap: 12px;
}
.me-account-top .me-who { flex: 1 1 auto; min-width: 0; }
.me-who { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.me-who b { font-size: 16px; color: var(--ink); }
.me-who span { font-size: 12.5px; }

/* 权限徽章：等级一目了然。管理员用主红，普通学员用中性色 —— 不靠颜色抢注意力，
   靠「有没有底色」区分。将来加等级时只在这里多一个修饰类。 */
.role-badge {
  flex: none; font-size: 11.5px; line-height: 1; padding: 5px 9px; border-radius: 999px;
  color: var(--ink-2); background: var(--card); border: 1px solid var(--line);
}
.role-badge.is-admin {
  color: var(--red); background: var(--red-soft); border-color: var(--red-line);
}

/* 设置页里成对出现的「标签 — 值」行 */
.acc-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 7px 0; border-bottom: 1px dashed var(--line);
}
.acc-row:last-child { border-bottom: 0; padding-bottom: 0; }
.acc-k { flex: none; font-size: 13px; color: var(--ink-3); }
.acc-v {
  min-width: 0; text-align: right; font-size: 13.5px; color: var(--ink);
  word-break: break-all;
}

/* 次级页里的表单卡（改名 / 改密码） */
.me-form { padding: 14px; }
.me-form input {
  display: block; width: 100%; box-sizing: border-box;
  font: inherit; font-size: 14px; padding: 11px 13px; margin-bottom: 10px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: var(--ink);
}
.me-form input:focus { outline: none; border-color: var(--red); }
.me-form-tip { margin: 10px 0 0 !important; font-size: 12px !important; line-height: 1.65 !important; color: var(--ink-3) !important; }
.me-perm-note { margin: 10px 0 0 !important; font-size: 12.5px !important; line-height: 1.7 !important; }

/* 学习时长：三个数字 + 近 7 天的小柱子 */
.study-nums { display: flex; gap: 10px; text-align: center; }
.study-num {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; gap: 5px;
  background: var(--card); border-radius: 10px; padding: 11px 6px;
}
.study-num b { font-size: 14.5px; color: var(--red); font-weight: 700; letter-spacing: .01em; }
.study-num span { font-size: 11.5px; color: var(--ink-3); }
.study-bars {
  display: flex; align-items: flex-end; gap: 6px;
  height: 62px; margin-top: 14px;
}
.study-col {
  flex: 1 1 0; min-width: 0; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 5px;
}
.study-col i {
  display: block; width: 100%; max-width: 26px; border-radius: 4px 4px 2px 2px;
  background: linear-gradient(180deg, var(--red), var(--red-dark));
}
.study-col span { font-size: 10px; color: var(--ink-3); line-height: 1; }

.me-empty h3 { color: var(--ink-2); }


/* ── 登录弹层：三格（验证码 / 密码 / 注册）────────────────────── */
.auth-tabs button { padding: 9px 4px; font-size: 12.5px; }
.auth-pane { margin-bottom: 2px; }
.auth-tip {
  margin: -4px 0 10px; font-size: 12px; line-height: 1.6; color: var(--ink-3);
}
.sheet-card .auth-row { display: flex; gap: 8px; margin-bottom: 10px; }
.sheet-card .auth-row input { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.auth-row button {
  flex: none; font: inherit; font-size: 13px; line-height: 1;
  padding: 0 14px; border-radius: 10px; cursor: pointer; white-space: nowrap;
  background: var(--red-soft); color: var(--red); border: 1px solid var(--red-line);
}
.auth-row button:disabled { background: var(--paper); color: var(--ink-3); border-color: var(--line); }

/* ── 账户设置（次级页）的表单按钮 ─────────────────────────────── */
/* 退出登录现在在次级页最底下，独占一行 */
.me-logout {
  display: block; width: 100%; box-sizing: border-box;
  margin-top: 4px; font: inherit; font-size: 13.5px; line-height: 1;
  padding: 13px 12px; border-radius: 10px; cursor: pointer;
  background: var(--card); color: var(--ink-2); border: 1px solid var(--line);
}
.me-logout:active { border-color: var(--red); color: var(--red); }
.me-rename-btns { display: flex; gap: 10px; }
.me-save, .me-cancel {
  flex: 1 1 0; font: inherit; font-size: 13px; line-height: 1;
  padding: 11px 8px; border-radius: 10px; cursor: pointer;
}
.me-save { background: var(--red); color: #fff; border: 1px solid var(--red); font-weight: 600; }
.me-cancel { background: var(--card); color: var(--ink-2); border: 1px solid var(--line); }
.me-msg { margin: 10px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--red); }
/* 成功提示用绿色系：出错才该是红的，否则改完名看到红字会以为失败 */
.me-msg.is-ok { color: #2E7D4F; }

/* ── 划动收藏（有思想含量的话 150 句：按句收藏）────────────────────
   一行由两层组成：底下是收藏键，上面是内容本体。向左划开就把上面挪出去，
   露出右侧 78px 的收藏键。 */
.swipe-row {
  position: relative; overflow: hidden;
  border-radius: 8px;
}
.swipe-row .quote,
.swipe-row .entry {
  position: relative; z-index: 1;
  transition: transform .22s cubic-bezier(.2, .8, .3, 1);
}
.swipe-row.is-open .quote,
.swipe-row.is-open .entry { transform: translateX(-78px); }
.swipe-fav {
  position: absolute; top: 0; right: 0; bottom: 8px; width: 78px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  font: inherit; font-size: 12px; line-height: 1; cursor: pointer;
  background: var(--red-soft); color: var(--red);
  border: 0; border-radius: 0 8px 8px 0;
}
.swipe-fav .swipe-star { font-size: 17px; line-height: 1; }
.swipe-fav.is-on { background: var(--red); color: #fff; }

/* 内容**自带底色**的（150 句的 .quote 就是白卡片）：收藏键一直画着也没关系 ——
   收着的时候被卡片盖住，划开时随着卡片滑走一点点露出来。
   内容**没有底色**的（成语 520 的 .entry 是透的）：键会从内容底下透出来，所以这一类
   只在划开后才把键画出来，而且**等 0.2 秒**（content 滑完）再显形，否则滑的过程中
   键会从透明内容底下透出来闪一下。
   ⚠️ 别用「给 .entry 补一层底色」来盖 —— 抽屉底面是 --bar（暖色），补 --card 白底
   会让成语条目变成一条条白的，原来的样子就变了（踩过）。 */
.swipe-row--bare .swipe-fav { visibility: hidden; }
.swipe-row--bare.is-open .swipe-fav { visibility: visible; transition-delay: .2s; }

/* ── 实词词条的收藏星标 ─────────────────────────────────────────
   默认很淡（不透明度 .38、灰褐色、无边框无底色），不抢词条本身的注意力；
   悬停时稍微显形，收藏后变成主红。词条改成 flex 才能把星标推到最右。 */
.drawer-body .entry .k { display: flex; align-items: baseline; gap: 8px; }
.drawer-body .entry .k > .item-no { flex: none; }
.term-fav {
  flex: none; margin-left: auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 22px; padding: 0;
  font: inherit; font-size: 14px; line-height: 1;
  background: none; border: 0; cursor: pointer;
  color: var(--ink-3); opacity: .38;
  transition: opacity .18s, color .18s;
  -webkit-tap-highlight-color: transparent;
}
.term-fav:active, .term-fav:hover { opacity: .8; }
.term-fav.is-on { color: var(--red); opacity: .85; }

/* ── 沉浸式阅读：按人收藏 ───────────────────────────────────────
   与右上角关闭键并排、在它左边。这里底色是深的，所以「淡」用半透明白，
   收藏后点亮成暖黄实心 —— 分量与实词那颗星标一致，不抢卡面。 */
.viewer-fav {
  position: absolute; z-index: 2;
  top: calc(12px + env(safe-area-inset-top)); right: 62px;
  width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font: inherit; font-size: 17px; line-height: 1;
  background: rgba(255, 255, 255, .1); border: 0; cursor: pointer;
  color: #fff; opacity: .45;
  transition: opacity .18s, color .18s, background .18s;
  -webkit-tap-highlight-color: transparent;
}
.viewer-fav:active { opacity: .95; }
.viewer-fav.is-on { opacity: 1; color: #FFD166; background: rgba(255, 209, 102, .16); }

/* ── 虚词小节标题改两列，好把星标推到最右 ───────────────────────
   编号列在这里定宽（18 条，两位数字 19.5px 够放），详解正文靠
   「--sec-no-w + --sec-gap」对齐到标题里的字上 —— 见上方 .sec .rawbox 那条。 */
.drawer-body .sec { --sec-no-w: 19.5px; --sec-gap: 8px; }
.drawer-body .sec > h3 { display: flex; align-items: baseline; gap: var(--sec-gap); }
.drawer-body .sec > h3 > .item-no { flex: none; width: var(--sec-no-w); margin-right: 0; }
.drawer-body .sec > h3 > .sec-title { flex: 1 1 auto; min-width: 0; }

/* ── 诗歌常见特殊意象：词条 + 解释保持原样，右端挂星标 ──────────── */
.drawer-body .imagery-entry { display: flex; align-items: baseline; gap: 8px; }
.drawer-body .imagery-entry .im-main { flex: 1 1 auto; min-width: 0; }

/* ── 文化常识题库：题干那行现在有三列（题号 / 题干 / 星标）────────
   .drill-q 本来就是 flex，星标 margin-left:auto 会自己贴到最右。 */
.drawer-body .drill-q .term-fav { width: 24px; height: 20px; }

/* ── 「我的」收藏：分组 + 筛选，条目保持资料页原样 ─────────────
   收藏是学生的笔记本：学生在资料页看到什么样，收藏后就是什么样，不做折叠、
   也不另造一套排版。内容一多就靠顶部的分组筛选来收。 */
.fav-filters { margin: 4px 0 16px; }
.fav-group { margin-bottom: 24px; }
.fav-group[hidden] { display: none; }
.fav-group-title {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 14px; color: var(--ink); margin-bottom: 11px;
  padding-left: 10px; border-left: 3px solid var(--red);
}
.fav-group-n {
  font-size: 12px; font-weight: 400; color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
/* 整份收藏已取消：这一段只在有老数据时出现，一句说明就够，别把版面撑开 */
.fav-legacy-hint { margin: -3px 0 11px; font-size: 12px; line-height: 1.6; }
/* 收藏的那一条：结构与样式都沿用资料页，只把序号列藏掉 ——
   否则「明明是第 5 条却显示 1」会让人以为收藏错了。

   曾经给每条画过一张卡片壳（边框 + 圆角 + 卡片底色），不加了：这里是白卡叠在
   米白页面上，两层白糊在一起反而更难看。条与条之间靠 .drill-word 自带的虚线
   分隔就够了（那本来就是资料页里每条题目的分隔线）。

   要修的是**缩进**：收藏页里 .drill-input / .drill-ans 还带着资料页那套
   “编号列 24 + gap 9 = 33px” 的缩进，而资料页的题干文字是靠这段缩进对齐的。
   收藏页把序号列藏了，参考答案行却留着 33px，于是同一张卡里
   「题干 47 / 填空框 80 / 看答案 80」两套缩进 —— 看着就是没对齐。
   下面三条把填空框和答案区拉回题干文字那一列（缩进交给 .drill-q 自己算）。 */
.fav-one { margin-bottom: 24px; }
/* 序号列不能 display:none —— 那样 .drill-text 会失去「编号列 24 + gap 9 = 33px」
   的起始位置，题干文字从 47 掉回 14，跟填空框/看答案（47）又错开一整列。
   改成 visibility: hidden：占位保留、数字不显示，题干就还在 47。 */
.fav-one .item-no { visibility: hidden; }
/* 填空框与「看答案」保持资料页那套缩进（33px），这样三者都落在 47：
   题干文字 47 / 填空框 47 / 看答案 47。之前和卡片壳的 padding 叠加才会跑到 62。 */
.fav-one .drill-input { width: calc(100% - 33px); margin: 0 0 8px 33px; }
.fav-one .drill-word > .drill-ans .ans-line { width: auto; margin-right: 0; }
/* 条目右下角两个操作键：写笔记 / 取消收藏。
   原来只有一个「取消收藏」，现在多了一个，所以包一层容器右对齐。 */
.fav-one-ops { display: flex; justify-content: flex-end; gap: 6px; }
.fav-one-del {
  /* 紧贴内容下方、靠右 —— 这就是最初那一版的位置，用户明确说「原来也是在下面，
     但比现在好」。中途我试过钉右上角、又试过往下留 6px 间距，都不如这个：
     它贴着内容，看完「看答案」顺手就能点，视觉上也不跟内容脱节。
     margin-top: 0 —— 按钮贴住上面那条内容；条目之间由 .fav-one 的 24px 拉开，
     这样「取消收藏」明确属于上一条，不会被读成下面那条的。 */
  display: block; margin: 0;
  font: inherit; font-size: 12px; line-height: 1;
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  background: var(--paper); color: var(--ink-3); border: 1px solid var(--line);
}
.fav-one-del:active { border-color: var(--red); color: var(--red); }
/* 「写笔记」和「取消收藏」同一档，但用朱红一点点的描边把它和破坏性的取消区分开 */
.fav-one-note {
  display: block; margin: 0;
  font: inherit; font-size: 12px; line-height: 1;
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  background: var(--paper); color: var(--red); border: 1px solid var(--red-line, #f3c9c6);
}
.fav-one-note:active { background: var(--red-soft); }
/* 收藏页里每一条本来就是「已收藏」，条目自带的收藏键就多余了 —— 何况各资料长得
   还不一样（150 句是划动露出的胶囊、实词虚词是小星标）。统一隐藏，只留右下角
   一个「取消收藏」。 */
.fav-one .swipe-row { overflow: visible; }
.fav-one .swipe-row .quote { transform: none !important; }
.fav-one .swipe-fav,
.fav-one .term-fav { display: none; }

/* 收藏页里的条目挂了 .drawer-body 类，好继承资料页那整套排版（词条红、
   解释下划线、例句楷体……）；但这里不是滚动容器，把它那套布局属性重置掉。 */
.fav-body.drawer-body {
  flex: none; overflow: visible; padding: 0;
}

/* 收藏单条时，属于「整份资料」的标题与说明要去掉：
   虚词口诀的「口诀总纲 / 18 条细则」、题库与例题的「共 N 道 · 先自己做…」。
   这些是整份资料的结构，收藏了其中一条之后再显示就莫名其妙了。
   注意要排除 .essay-title：收藏一篇范文时，那是**这一篇自己的标题**，
   必须留（它是 .fav-one 的直接 h3 子元素，不排除就会被这条一起藏掉，
   正文就成了没有标题的一团）。 */
.fav-one .legend,
.fav-one > h3:not(.essay-title) { display: none; }

/* ── 收藏页的分类下拉列表 ──────────────────────────────────────
   板块多的时候（「独家秘笈」底下一口气八本）不能全铺成标签，所以收成两级：
   主分类按钮 + 点开的二级「下拉列表」。

   面板是**绝对定位的浮层**，所以展开/收起都不占位，这一行主按钮一动不动
   —— 之前是推挤式折叠面板，一展开就把下面的内容顶下去、连按钮行自己都跟着变，
   所以老师说「四个按钮要固定不动」。

   面板挂在外层容器上、靠 top:100% 对齐到按钮栏下方：不能把面板放进主按钮里，
   因为 <button> 内不允许嵌 <button>，解析器会提前闭合外层按钮，整行按钮会散架。
   同一时刻只展开一个；点到列表以外、或往上划都收起。 */
.fav-filters { position: relative; }     /* 面板的定位基准 */
/* 分类按钮：向「独家秘笈」页那四个靠拢（老师：「现在是有点压缩了」）。
   这一排本来就复用了 .group-nav button（字号 13、内边距 7/14），
   是下面那条把左右内边距压到 9px 才显得挤 —— 那条已经删掉。
   现在改成**等宽平分**（flex: 1 1 0），不照抄固定内边距：
   · 每个按钮撑满自己那一份、文字居中，观感比"固定 14px"还舒展（430px 上每枚约 95px）；
   · 任何宽度都**不会折行**，360 / 320px 上也不必再收一档（老一套是内边距 9→8 硬挤）。
   ⚠️ 这里**不能**用 overflow 兜底：二级下拉面板是绝对定位挂在按钮栏下面的，
   overflow 一开就被裁掉（面板会看不见）。 */
.fav-filters { gap: 8px; }
.fav-filters > button {
  flex: 1 1 0; min-width: 0;
  padding: 7px 6px; white-space: nowrap;
}
/* 极窄的老机型（320px）：只收间距和内边距，字号按老师要求不动 */
@media (max-width: 350px) {
  .fav-filters { gap: 5px; }
  .fav-filters > button { padding: 7px 3px; }
}
.fav-subpanel {
  position: absolute; left: 0; top: calc(100% - 6px); z-index: 20;
  display: flex; flex-direction: column; gap: 5px;
  width: 100%; min-width: 160px; max-width: 300px;
  padding: 8px;
  background: var(--card);
  /* 半透明浮层：透出一点下层内容，既保留「它浮在内容之上」的层次，
     又不至于像一块不透明的白板那样把下面的收藏信息整块糊住。
     0.94 是「看得出通透、字仍然清楚」的折中；再低就会影响 12.5px 小字的可读性。 */
  opacity: .94;
  /* 有毛玻璃就再加一层磨砂（老内核没有这个属性，自动忽略，不影响上面那条透明度） */
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow);
  /* 条目多时列表自己滚，不要长到屏幕外面去 */
  max-height: min(56vh, 420px); overflow-y: auto;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}
.fav-subpanel[hidden] { display: none; }
.fav-subitem {                       /* 下拉列表里的一项 */
  display: block; width: 100%; text-align: left;
  font: inherit; font-size: 12.5px; line-height: 1.25;
  padding: 9px 11px; border-radius: 8px; cursor: pointer;
  background: transparent; color: var(--ink-2);
  border: 1px solid transparent;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: background .16s, color .16s;
}
.fav-subitem:active { background: var(--paper); }
.fav-subitem.is-active {
  background: var(--red-soft); color: var(--red);
  border-color: var(--red-line); font-weight: 600;
}
/* 主分类按钮上的小三角：提示这个分类点得开；展开时翻过来 */
.fav-caret {
  display: inline-block; font-style: normal; font-size: 10px; margin-left: 3px;
  opacity: .65; transition: transform .18s;
}
.fav-filters > button[data-open] .fav-caret { transform: rotate(180deg); }

/* ── 问答页底部的配额提示 ───────────────────────────────────────
   平时很轻（一行小字），用完了才变红并给出「去登录」。 */
.quota-line {
  flex: none;
  /* 必须用 fixed —— #view-chat 自身没有高度（里面的 .scroll / .composer 都是 fixed），
     靠 margin-top:auto 或 absolute 都推不动，它会一直贴在屏幕最顶端；
     而顶栏是 fixed + z-index:30，正好把它整条盖住，于是「重新登录 / 去登录」
     这些按钮的真实点击全打在顶栏上、点了没反应（程序化 click() 却能生效，
     所以很容易误判成事件没绑上）。定位到输入条上方，且避开顶栏与底栏。 */
  position: fixed; z-index: 21;
  top: calc(var(--topbar-h) + env(safe-area-inset-top));
  bottom: calc(var(--tabbar-h) + var(--composer-h) + env(safe-area-inset-bottom) + var(--kb, 0px));
  left: 0; right: 0;
  display: flex; align-items: flex-end; justify-content: center;
  max-width: 720px; margin: 0 auto;
  padding: 0 16px 8px;
  font-size: 12px; line-height: 1.5; color: var(--ink-3);
  text-align: center;
  pointer-events: none;          /* 整条不挡内容，只有「去登录 / 重新登录」按钮可点 */
}
.quota-line[hidden] { display: none; }
.quota-line .quota-login { pointer-events: auto; }
.quota-line[hidden] { display: none; }
.quota-line b { color: var(--red); font-weight: 600; }
.quota-line.is-out { color: var(--red); }
.quota-login {
  font: inherit; font-size: 12px; line-height: 1;
  margin-left: 6px; padding: 5px 10px; border-radius: 999px; cursor: pointer;
  /* 按钮文字不许折行：这一行本来就窄，折成两行会把提示条撑成两倍高 */
  white-space: nowrap; flex: none;
  background: var(--red-soft); color: var(--red);
  border: 1px solid var(--red-line);
}

/* ── 留言簿 ─────────────────────────────────────────────────────
   顶栏那个 ✎ 打开它。登录用户能写，游客只能看。 */
.msg-tip { margin: 0 0 12px !important; }
.msg-form { margin-bottom: 16px; }
.sheet-card .msg-form textarea {
  display: block; width: 100%; box-sizing: border-box;
  font: inherit; font-size: 14px; line-height: 1.65;
  padding: 11px 13px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 10px;
  background: #fff; color: var(--ink); resize: vertical;
}
.sheet-card .msg-form textarea:focus { outline: none; border-color: var(--red); }
.msg-form-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.msg-form-foot .dim { font-size: 12px; }
.msg-send { flex: none; width: auto !important; padding: 0 18px; }

.msg-list { max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.msg-item {
  background: var(--paper); border-radius: 10px;
  padding: 11px 13px; margin-bottom: 9px;
}
.msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 5px; }
/* 评论行是按基线对齐的（名字和时间的字底对齐），但头像没有文字基线 ——
   不单独摆正的话它会往下掉一截。 */
.msg-head .avatar { align-self: center; }
.msg-head b { font-size: 13.5px; color: var(--red); }
.msg-head .dim { font-size: 12px; }
.msg-del {
  margin-left: auto; font: inherit; font-size: 11.5px; line-height: 1;
  padding: 5px 9px; border-radius: 999px; cursor: pointer;
  background: #fff; color: var(--ink-3); border: 1px solid var(--line);
}
.msg-del:active { border-color: var(--red); color: var(--red); }
.msg-text {
  margin: 0; font-size: 13.5px; line-height: 1.75; color: var(--ink);
  white-space: pre-wrap; word-break: break-word;
}
.msg-empty { text-align: center; color: var(--ink-3); font-size: 13px; padding: 18px 0; }
.msg-login { margin-top: 6px; }

/* ── 便签评论 ───────────────────────────────────────────────────
   入口是纸条左下角那枚小气泡（.note-cmts）；弹层复用留言簿那套 .msg-* 样式。 */
.cmt-note {
  margin: 0 0 12px !important;
  padding: 9px 12px; border-radius: 9px;
  background: linear-gradient(160deg, #FFF7C4 0%, #FCEFA8 100%);
  color: #3a3128; font-size: 13px; line-height: 1.7;
  border: 1px solid rgba(120, 90, 40, .12);
  white-space: pre-wrap; word-break: break-word;
}
.cmt-mine {
  margin-left: auto; flex: none;
  font-size: 11px; line-height: 1; padding: 3px 7px; border-radius: 999px;
  background: var(--red-soft); color: var(--red);
}
/* ── 对评论再评论（盖楼）───────────────────────────────────────
   楼只盖一层：一条评论 + 它下面的若干回复。回复"回复"时新评论仍挂到同一层楼下，
   只把「回复 @谁」换掉 —— 手机上缩进三层就没法读了。 */
/* 每条评论右侧那一组（「我」标记 + 回复按钮）：靠右对齐，谁在不在都不影响排版 */
.cmt-head-right { margin-left: auto; flex: none; display: flex; align-items: center; gap: 7px; }
.cmt-mine + .cmt-reply-btn { margin-left: 0; }
/* 「回复」做得淡：评论本身是主角，别让一排按钮抢戏 */
.cmt-reply-btn {
  font: inherit; font-size: 11.5px; line-height: 1; padding: 4px 8px;
  border-radius: 999px; cursor: pointer;
  background: transparent; color: var(--ink-3); border: 0;
}
.cmt-reply-btn:active { color: var(--red); background: var(--red-soft); }
/* 楼中楼：缩进 + 一条淡竖线，一眼看出从属关系；去掉卡片底，免得一层层发灰 */
.cmt-replies { margin: 9px 0 0 9px; padding-left: 10px; border-left: 2px solid var(--line); }
.cmt-replies .msg-item { background: transparent; padding: 2px 0 6px; margin: 0; border-radius: 0; }
.cmt-replies .msg-head { margin-bottom: 3px; }
.cmt-replies .msg-text { font-size: 13px; }
/* 「回复 @某某：」前缀 */
.cmt-at { color: var(--red); }
/* 回复对象条：贴在输入框上方，点 × 取消回复 */
.cmt-reply-bar {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 8px; padding: 6px 10px; border-radius: 8px;
  background: var(--peach); color: var(--ink-2); font-size: 12.5px;
}
.cmt-reply-bar b { color: var(--red); }
.cmt-reply-bar button {
  margin-left: auto; font: inherit; font-size: 15px; line-height: 1;
  padding: 2px 7px; border: 0; border-radius: 6px;
  background: transparent; color: var(--ink-3); cursor: pointer;
}
.cmt-reply-bar button:active { color: var(--red); background: #fff; }
/* 马老师的回复：挂在留言下方，用暖底 + 左侧朱红竖线把它和留言区分开。
   游客也能看到 —— 有人回了才是留言簿活着的证据。 */
.msg-reply {
  margin-top: 9px; padding: 9px 11px; border-radius: 8px;
  background: var(--peach); border-left: 3px solid var(--red);
}
.msg-reply b { display: block; font-size: 12px; color: var(--red); margin-bottom: 4px; }
.msg-reply p {
  margin: 0; font-size: 13px; line-height: 1.7; color: var(--ink);
  white-space: pre-wrap; word-break: break-word;
}
.msg-reply .dim { display: block; font-size: 11.5px; margin-top: 4px; }

/* ── 顶栏右侧两个按钮 ───────────────────────────────────────────
   顶栏是 space-between，之前三个子元素（品牌 + 两个按钮）被均匀撑开，
   留言和关于离得老远。包成一个按钮组就贴住了。 */
.topbar-actions {
  display: flex; align-items: center; gap: 8px; flex: none;
}
.topbar-actions .ghost-btn { display: grid; place-items: center; }
.topbar-actions svg { display: block; }

/* ── 引用块（150 句、《论语》三十三则）的序号列收窄 ─────────────
   全局 .item-no 是 min-width: 2.4em（36px），那是给两位数以上的列表预留的；
   在这里序号最多三位数、又紧跟正文，撑这么宽会把正文推到离红线 55px。 */
.drawer-body .quote .item-no {
  min-width: 0;
  margin-right: 7px;
}
/* ── 学生笔记（「我的 → 笔记」）────────────────────────────────────
   风格取向：像朋友圈那样「一条一条读下去」，所以每条是一张独立的白卡，
   卡片之间留白、作者和时间在顶部一行，正文是主角。
   不做花哨的装饰 —— 笔记是给学生自己写和互相看的，干净比好看重要。 */

/* 「我的」页两栏导航：我的收藏 / 笔记 */
.me-tabs {
  display: flex; gap: 8px;
  /* 下边距给足：栏目里的那一行重复标题（「我的收藏」/「笔记」）已经去掉了，
     内容与栏目标签之间的呼吸感由这里来，两栏都吃这条。 */
  margin: 14px 0 20px; padding: 0 2px;
}
.me-tabs button {
  position: relative;
  flex: 1;
  font: inherit; font-size: 14px; font-weight: 600; line-height: 1;
  padding: 11px 8px; border-radius: 10px; cursor: pointer;
  background: var(--card); color: var(--ink-2);
  border: 1px solid var(--line);
  transition: background .18s, color .18s, border-color .18s;
}
.me-tabs button.is-active {
  background: var(--red-soft); color: var(--red); border-color: var(--red-line, #f3c9c6);
}
.me-tab-n {
  font-style: normal; font-weight: 400; font-size: 12px;
  margin-left: 5px; opacity: .7;
}

/* 笔记栏里的两个子标签（我的 / 大家的）复用 .group-nav 的观感，只压一压上边距 */
.note-filters { margin-top: 6px; }
.note-filters .fav-caret { font-style: normal; color: var(--ink-3); }

/* 发布框 */
.note-writer {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 13px; margin: 10px 0 14px; box-shadow: var(--shadow-sm);
}
.note-writer textarea {
  width: 100%; box-sizing: border-box;
  font: inherit; font-size: 14px; line-height: 1.75;
  padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  resize: vertical; outline: none;
}
.note-writer textarea:focus { border-color: var(--red); background: var(--card); }
.nw-foot {
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
}
/* 「仅自己可见」：默认不勾 —— 笔记默认公开，只给自己看是自己主动选的例外。
   flex: none + nowrap 是必须的 —— 这个标签原来在 flex 行里没有固定宽度，
   而右边的「发布」按钮继承了全局 .primary-btn 的 width:100%，把整行都吃掉了，
   于是标签被压到只剩一个字宽、文字竖着排成一列（截图里就是这样）。 */
.nw-private {
  display: inline-flex; align-items: center; gap: 6px;
  flex: none; white-space: nowrap;
  /* 老师要求：这个例外开关**别太醒目** —— 公开才是默认态，它只需要「在那儿、点得到」。
     文字压到 rgba 淡一档（比 --ink-3 再轻）。勾选框的颜色和尺寸统一写在下面那一段里
     —— 那里是**自绘**的，边框和这里的文字同一个颜色（原生灰边比文字重，老师指出来了）。 */
  font-size: 13px; color: rgba(110, 90, 82, .76); cursor: pointer;
  margin-right: auto;
}
/* 「仅自己可见」这类小勾选框：**自绘**，边框用和文字完全一样的颜色。
   原生 checkbox 那道灰边框是浏览器画的，比旁边的淡文字重得多 ——
   老师一眼就看出来了（「勾选框和文字的颜色不一样，更重一些」）。
   自绘之后没勾时只有一圈同色细边，跟文字一样安静；
   勾上才变朱红填色 + 白勾（那一刻「私密」是重要状态，要看得见）。
   同一个控件在「我的笔记」发布框（.nw-private）和收藏条目的写笔记框（.fnote-opt）里都有。 */
.nw-private input,
.fnote-opt input {
  appearance: none; -webkit-appearance: none;
  position: relative;
  width: 13px; height: 13px; flex: none;
  border: 1px solid rgba(110, 90, 82, .76);   /* 与标签文字同色 */
  border-radius: 3.5px;
  background: transparent;
  cursor: pointer;
}
.nw-private input:checked,
.fnote-opt input:checked { background: var(--red); border-color: var(--red); }
/* 白勾：用两条边转 45° 画出来，不引图标 */
.nw-private input:checked::after,
.fnote-opt input:checked::after {
  content: ''; position: absolute; left: 3.6px; top: .8px;
  width: 3px; height: 6px;
  border: solid #fff; border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}/* 发布按钮：必须盖掉 .primary-btn 的 width:100% 和 margin-top:8px，
   不然它会占满整行、把左边的标签挤没。 */
.nw-send {
  flex: none; width: auto; margin-top: 0;
  font-size: 13.5px; padding: 8px 20px; border-radius: 999px; line-height: 1.2;
}
/* 一条笔记 = 一张卡 */
.note-card-item {
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  padding: 13px 15px; margin-bottom: 12px; box-shadow: var(--shadow-sm);
}
.nc-top {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  font-size: 12.5px;
}
.nc-who { font-weight: 600; color: var(--red); }
.nc-when { color: var(--ink-3); }
/* 「仅自己可见」是个需要随时看得见的提醒，别藏。
   公开是默认态，卡片上不再挂「已公开」，所以这里只有这一种徽章样式。 */
.nc-badge {
  margin-left: auto; flex: none;
  font-size: 11px; line-height: 1; padding: 4px 8px; border-radius: 999px;
  background: var(--paper); color: var(--ink-3); border: 1px solid var(--line);
}
.nc-text {
  margin: 0; font-size: 14px; line-height: 1.8; color: var(--ink);
  white-space: pre-wrap; word-break: break-word;
}
/* 笔记引用的那条收藏：整条摊开显示（已经没有「看原文」这个跳转了）
   上面那行只说清「这是什么 + 从哪份资料收的」，内容本身就是下面摊开的那一条 */
.nc-ref {
  display: flex; align-items: center; gap: 8px;
  margin-top: 10px;
  font-size: 12.5px;
}
.nc-ref-label {
  flex: none; font-size: 11px; padding: 3px 7px; border-radius: 5px;
  background: var(--red-soft); color: var(--red);
}
/* .nc-ref-title 只在「那条收藏渲染不出来」的退路里出现（资料下线/数据没拉到） */
.nc-ref-src,
.nc-ref-title {
  flex: 1; min-width: 0; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 摊开的那一条：内容由资料页那套渲染给出（fav-body/drawer-body 提供排版作用域，
   .fav-one 收掉序号列），这里只管外框与内边距。

   ⚠️ 选择器必须写到 .fav-body.drawer-body.nc-ref-item 这么长：容器同时挂着
   fav-body / drawer-body，而 `.fav-body.drawer-body { padding: 0 }`（收藏页那条重置）
   比单个 `.nc-ref-item` 更具体，会把这层内边距整条盖掉 —— 结果是框里的内容贴着边框，
   下沿还多出条目自带的一段边距（截图里那条空白就是这么来的）。
   ⚠️ 最后一块的下边距也要抹掉：条目自带 margin-bottom（如 .quote 的 8px），
   而外面这层有 padding/border，边距穿不出去，就变成容器底部凭空多一条。
   多写两层是因为有的条目外面还裹着 .swipe-row（它自己没有 padding，边距会穿到它下面）。 */
.fav-body.drawer-body.nc-ref-item {
  margin-top: 7px; padding: 9px 11px;
  background: var(--paper); border-radius: 9px;
  border: 1px solid var(--line);
}
.nc-ref-item .fav-one,
.nc-ref-item .fav-one > *:last-child,
.nc-ref-item .fav-one > *:last-child > *:last-child { margin-bottom: 0; }
.nc-foot {
  display: flex; align-items: center; gap: 8px; margin-top: 11px;
}
/* 点赞：**没有外壳**（老师要求：去掉心形外面那个框），就是一颗心 + 数字。
   心单独放大（原来整个按钮 12.5px，心显得太小；18px 之后老师还要求再大一点 → 22px），
   数字仍是 12.5px。内边距留着 —— 它是透明的，只为保住手指点得到的范围。
   点过的状态靠「空心 ♡ → 实心 ♥ + 变红」，不再靠那层药丸底色。
   ⚠️ 心和数字必须各是一个 span：整个按钮一个字号的话，放大心会把数字也带大。 */
.nc-like {
  display: inline-flex; align-items: center; gap: 5px;
  font: inherit; font-size: 12.5px; line-height: 1; padding: 4px 2px;
  border: 0; background: transparent; cursor: pointer;
  color: var(--ink-3);
}
.nc-like-i { font-size: 22px; line-height: 1; }
.nc-like.is-on { color: var(--red); }
.nc-like:active { opacity: .6; }
/* 笔记广场卡片里的评论入口：用的是便签左下角那枚同款气泡（.note-cmts），
   但它是**行内**的（纸条那枚是绝对定位钉在纸角上），所以把定位复位。 */
.note-card-item .nc-cmts {
  position: static; left: auto; bottom: auto; z-index: auto;
  margin-left: 2px; opacity: .7;
}
.note-card-item.has-cmts .nc-cmts { opacity: .92; }
/* 右下角那个淡色的「⋯」：编辑 / 分享 / 设为私密 / 删除 都收进点开的菜单里。
   原来那两颗实体按钮（公开与否、删除）常驻在卡片右下角，太抢眼，也把「读笔记」挤掉。 */
.nc-more {
  margin-left: auto; flex: none;
  width: 30px; height: 26px; padding: 0; line-height: 1;
  font: inherit; font-size: 17px; cursor: pointer;
  background: transparent; border: 0; border-radius: 8px;
  color: var(--ink-3);
}
.nc-more:active { background: rgba(0, 0, 0, .06); color: var(--ink); }
.me-notes-loading { padding: 24px 0; text-align: center; }
/* 「我的」页第一屏的加载占位：收藏要等「收藏条目涉及的资料」回来才画得出，
   这段时间给一句话，别让收藏区一片空白（老师报过「一开始收藏是空的」）。 */
.me-loading { margin: 0; padding: 8px 0; text-align: center; font-size: 13px; }

/* 就地改笔记 */
.nc-edit textarea {
  display: block; width: 100%; box-sizing: border-box;
  font: inherit; font-size: 14px; line-height: 1.75;
  padding: 9px 11px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  resize: vertical; outline: none;
}
.nc-edit textarea:focus { border-color: var(--red); background: var(--card); }
.nc-edit-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 9px; }
.nc-edit-save {
  font: inherit; font-size: 13px; line-height: 1;
  padding: 8px 18px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--red); color: #fff;
}
.nc-edit-save[disabled] { opacity: .6; cursor: default; }
.nc-edit-cancel {
  font: inherit; font-size: 12.5px; line-height: 1;
  padding: 8px 13px; border-radius: 999px; cursor: pointer;
  background: var(--paper); color: var(--ink-3); border: 1px solid var(--line);
}

/* ── 小浮层菜单（笔记卡片的「⋯」、收藏条目下笔记的「⋯」共用）──────────
   跟着按钮弹出的一条窄浮层。挂在 body 上（不放进卡片里），所以 #meBody
   重画不会把它带走。位置由 JS 按锚点按钮算（placePopMenu）：右对齐按钮、
   贴在它下面，下方放不下就翻到上面。铺满全屏的那层只是**透明的接点击层**
   （点别处收起），不是遮罩 —— 三四个字的小菜单没必要把整页压暗。 */
.pop-menu { position: fixed; inset: 0; z-index: 70; background: transparent; }
.pm-card {
  position: absolute;
  min-width: 96px;
  padding: 4px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 11px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .16);
}
.pm-item {
  display: block; width: 100%; box-sizing: border-box;
  font: inherit; font-size: 14px; line-height: 1;
  padding: 11px 16px; text-align: center; white-space: nowrap;
  border: 0; border-radius: 8px;
  background: transparent; color: var(--ink); cursor: pointer;
}
.pm-item:active { background: var(--paper); }
.pm-item.is-danger { color: var(--red); }

/* 整份资料的收藏卡：外面套一层，好放「写笔记」（卡片自己是 button，里面不能再嵌 button） */
.mat-card-wrap { display: flex; flex-direction: column; }
.mat-card-wrap .mat-card { width: 100%; }
.mat-card-note {
  align-self: flex-end;
  margin: 7px 2px 0 0;
  font: inherit; font-size: 12px; line-height: 1;
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  background: var(--paper); color: var(--red); border: 1px solid var(--red-line, #f3c9c6);
}
.mat-card-note:active { background: var(--red-soft); }

/* ── 收藏条目下面的笔记（就地写，不跳转）──────────────────────────
   学生点某一条上的「写笔记」，输入框就在这一条下面展开，存下来还贴在这里。
   默认只是这一条的批注（只有自己看得见、不进「我的笔记」）；勾了「同时发布到
   我的笔记」才进笔记列表，那时才按笔记区的规矩选公开与否。
   左缩进跟着资料页那一列（条目文字在 33px 之外，见上面 .fav-one .drill-input）。 */
.fav-note { margin: 9px 0 0 33px; }
.mat-card-notezone { width: 100%; margin-top: 2px; }
.mat-card-notezone .fav-note { margin-left: 0; }

/* 收藏条目下自己写的那条笔记：**用问答页便签那一套纸感**。
   原来是「桃底 + 左侧红竖条」——和上面那条收藏同一个长相，挨在一起分不清
   哪条是资料、哪条是自己写的。换成便签纸：纸色 + 淡横纹 + 暖色弱阴影，
   一眼就能认出「这是我写的纸条」。 */
.fnote-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 9px; }
.fnote {
  position: relative;
  /* 宽度照抄问答页便签那条：min(78vw, 300px)。便签是「贴上去的一张纸」，
     铺满整行就不像纸了；max-width 兜住窄容器（整份资料卡片里的笔记区更窄）。
     全局是 border-box，所以这个宽度已经含内边距，和便签的算法一致。 */
  width: min(78vw, 300px);
  max-width: 100%;
  padding: 11px 13px 9px;
  border-radius: 3px;
  color: #3a3128;
  /* 底色读纸色变量（.fnote--xxx 那张纸给的，见上面「纸色主题」）；
     兜底是一个纯色而不是再写一遍渐变 —— 类名万一漏了也还有纸色，不会变成透明。
     ⚠️ 这里**不能**写死某一种纸的渐变：同特异性时后写的会赢，
     那样五色会全变成那一种（踩过）。 */
  background: var(--fpaper, #FCEFA8);
  /* 纸感：暖色弱阴影 + 细边框，不要重阴影 —— 重了就成卡片不像纸 */
  box-shadow: 0 1px 2px rgba(60, 40, 20, .10), 0 6px 16px rgba(60, 40, 20, .12);
  border: 1px solid rgba(120, 90, 40, .12);
}
/* 纸的横纹：和便签同一套，很淡，别做重 */
.fnote::before {
  content: ''; position: absolute; inset: 7px 0 0;
  background: repeating-linear-gradient(180deg,
    transparent 0, transparent 25px,
    rgba(120, 90, 40, .055) 25px, rgba(120, 90, 40, .055) 26px);
  border-radius: 3px; pointer-events: none;
}
/* 正文和底部那行都压在纹路之上 */
.fnote > * { position: relative; z-index: 1; }
.fnote-text {
  margin: 0; font-size: 13.5px; line-height: 1.75;
  white-space: pre-wrap; word-break: break-word;
}
/* 底部一行：左边时间、右边「⋯」。状态不再单独挂徽章了 —— 一条批注是不是
   同时发布到「我的笔记」，改的时候在编辑框里能看见，平时不必占一行。 */
.fnote-bar { display: flex; align-items: center; gap: 8px; margin-top: 5px; }
.fnote-when { font-size: 11px; color: rgba(107, 90, 69, .72); }
.fnote-more {
  margin-left: auto; flex: none;
  width: 26px; height: 22px; padding: 0; line-height: 1;
  font: inherit; font-size: 15px; cursor: pointer;
  background: transparent; border: 0; border-radius: 7px;
  color: rgba(107, 90, 69, .55);
}
.fnote-more:active { background: rgba(120, 90, 40, .12); color: #6b5a45; }

/* 就地写的输入框 */
.fnote-writer {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; padding: 10px; box-shadow: var(--shadow-sm);
}
.fnote-writer textarea {
  display: block; width: 100%; box-sizing: border-box;
  font: inherit; font-size: 13.5px; line-height: 1.75;
  padding: 9px 11px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  resize: vertical; outline: none;
}
.fnote-writer textarea:focus { border-color: var(--red); background: var(--card); }
.fnote-foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 8px;
}
.fnote-opt {
  display: inline-flex; align-items: center; gap: 6px;
  /* 和「我的笔记」那个发布框里的「仅自己可见」同款：淡一档、框小一点（老师要求） */
  font-size: 12.5px; color: rgba(110, 90, 82, .76); cursor: pointer; white-space: nowrap;
}
/* .fnote-opt 自己是 inline-flex，会盖掉 [hidden] 的 UA 规则，必须显式写一条 */
.fnote-opt[hidden] { display: none; }
/* 这个框的尺寸/颜色/勾选态统一写在 .nw-private input 那一段里（两处同款） */
.fnote-save {
  margin-left: auto;
  font: inherit; font-size: 13px; line-height: 1;
  padding: 8px 18px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--red); color: #fff;
}
.fnote-save[disabled] { opacity: .6; cursor: default; }
.fnote-cancel {
  font: inherit; font-size: 12.5px; line-height: 1;
  padding: 8px 12px; border-radius: 999px; cursor: pointer;
  background: var(--paper); color: var(--ink-3); border: 1px solid var(--line);
}
