/* 马步野的语文课堂 —— 手机优先样式表
   ─────────────────────────────────────────────────────────────
   配色来源：《经典人物资料卡.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);        /* 弹层遮罩 */
  --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: 58px;      /* 问答输入条常态高度，需与下面 .composer 的内边距保持一致 */
  --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;
}
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;
}
button, input, textarea { font: inherit; color: inherit; }
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));
  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)); }


/* ── 底部导航：纯文字 ──────────────────────────────────
   选中态 = 一块正红色块（用户诉求：一眼看出在哪个板块）。
   色块是 .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: 0; 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); }

/* 输入条：9+40+9 = 58px，正好等于 --composer-h */
.composer {
  position: fixed; left: 0; right: 0;
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  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);
  z-index: 20;
}
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  .composer { background: var(--bar); }
}
.composer textarea {
  flex: 1; resize: none;
  /* 上限就是半屏 —— 内容超过它才出滚动条。这里与 JS 的 autoGrow 同口径
     （window.innerHeight * 0.5），JS 没跑到时这条兜底也能撑住。
     别额外套一层 min(…, 260px)：那样在 844px 高的手机上实际只到 260px（约 1/4 屏），
     滚动条会提前冒出来（实测就是这个原因）。 */
  max-height: 50vh; min-height: 40px;
  padding: 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: 20px; }
.composer textarea::placeholder { color: var(--ink-3); }
.composer textarea:focus { border-color: var(--red); box-shadow: 0 0 0 3px var(--red-glow); }
/* 发送键：正红实心，白字 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); }

/* ── 课程 ─────────────────────────────────────────── */
.video-list { display: flex; flex-direction: column; gap: 14px; }
.video-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
.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; }

/* 播放前只有一个按键，点了才挂 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); }          /* 「问答」「视频课」这类板块名前缀 */
.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));
  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); }

/* ── 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); }
.me-userentry-name {
  font-size: 16px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.me-userentry-sub {
  flex: 1 1 auto; min-width: 0; 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); }

/* 账户设置（次级页）顶部的身份条 */
.me-account-top {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.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 的收藏键。句子本体自带不透明底色（.quote），所以能盖住下层。 */
.swipe-row {
  position: relative; overflow: hidden;
  border-radius: 8px;
}
.swipe-row .quote {
  position: relative; z-index: 1;
  transition: transform .22s cubic-bezier(.2, .8, .3, 1);
}
.swipe-row.is-open .quote { 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; }

/* ── 实词词条的收藏星标 ─────────────────────────────────────────
   默认很淡（不透明度 .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;
}
/* 收藏的那一条：结构与样式都沿用资料页，只把序号列藏掉 ——
   否则「明明是第 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-del {
  /* 紧贴内容下方、靠右 —— 这就是最初那一版的位置，用户明确说「原来也是在下面，
     但比现在好」。中途我试过钉右上角、又试过往下留 6px 间距，都不如这个：
     它贴着内容，看完「看答案」顺手就能点，视觉上也不跟内容脱节。
     margin-top: 0 —— 按钮贴住上面那条内容；条目之间由 .fav-one 的 24px 拉开，
     这样「取消收藏」明确属于上一条，不会被读成下面那条的。 */
  display: block; margin: 0 0 0 auto;
  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); }
/* 收藏页里每一条本来就是「已收藏」，条目自带的收藏键就多余了 —— 何况各资料长得
   还不一样（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; }     /* 面板的定位基准 */
.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));
  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 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; }
/* 马老师的回复：挂在留言下方，用暖底 + 左侧朱红竖线把它和留言区分开。
   游客也能看到 —— 有人回了才是留言簿活着的证据。 */
.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;
}
