/* ============== 全局 ============== */
/* Noto Sans 本地字体 — 仅覆盖拉丁 + IPA 音标，中文回退系统字体 */
@font-face {
  font-family: 'Noto Sans Local';
  src: url('/fonts/NotoSans-Regular-2.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-024F, U+0250-02AF, U+0300-036F, U+1D00-1D7F, U+1E00-1EFF, U+2070-209F, U+20A0-20CF, U+2100-214F, U+2150-218F, U+2190-21FF, U+2200-22FF, U+2300-23FF, U+25A0-25FF, U+2600-26FF, U+2700-27BF, U+FE20-FE2F;
}
@font-face {
  font-family: 'Noto Sans Local';
  src: url('/fonts/NotoSans-Bold-5.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-024F, U+0250-02AF, U+0300-036F, U+1D00-1D7F, U+1E00-1EFF, U+2070-209F, U+20A0-20CF, U+2100-214F, U+2150-218F, U+2190-21FF, U+2200-22FF, U+2300-23FF, U+25A0-25FF, U+2600-26FF, U+2700-27BF, U+FE20-FE2F;
}
@font-face {
  font-family: 'Noto Sans Local';
  src: url('/fonts/NotoSans-Italic-3.ttf') format('truetype');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-024F, U+0250-02AF, U+0300-036F, U+1D00-1D7F, U+1E00-1EFF, U+2070-209F, U+20A0-20CF, U+2100-214F, U+2150-218F, U+2190-21FF, U+2200-22FF, U+2300-23FF, U+25A0-25FF, U+2600-26FF, U+2700-27BF, U+FE20-FE2F;
}
@font-face {
  font-family: 'Noto Sans Local';
  src: url('/fonts/NotoSans-BoldItalic-4.ttf') format('truetype');
  font-weight: 700;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-024F, U+0250-02AF, U+0300-036F, U+1D00-1D7F, U+1E00-1EFF, U+2070-209F, U+20A0-20CF, U+2100-214F, U+2150-218F, U+2190-21FF, U+2200-22FF, U+2300-23FF, U+25A0-25FF, U+2600-26FF, U+2700-27BF, U+FE20-FE2F;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  /* 浅色是默认。深色见下面的 [data-theme="dark"] 块。
     color-scheme 显式声明 = 告诉浏览器「本站自己管配色」，
     浏览器就不会再擅自「自动深色/强制深色」把页面翻来翻去
     (那是 2026-11-09「整屏一白一暗」闪烁的头号嫌疑)。 */
  color-scheme: light;
  --fr-blue: #002395;
  --fr-white: #fff;
  --fr-red: #ED2939;
  --bg: #faf6f0;
  --card: #ffffff;
  --surface: #ffffff;
  --surface-2: #fafbfc;
  --surface-3: #f9fafb;
  --surface-4: #f3f4f6;
  --text: #1f2937;
  --text-strong: #0f172a;
  --text-2: #4b5563;
  --muted: #6b7280;
  --muted-2: #9ca3af;
  --muted-3: #c4c8d0;
  --border: #e5e7eb;
  --border-strong: #d1d5db;
  --border-soft: #f0f1f4;
  --accent: #002395;
  --accent-hover: #001a73;
  /* 实心块底色（按钮 / 选中标签 / 头像 / 字卡 / 徽章）—— 上面压的是白字。
     亮色下取值 = --accent，所以亮色像素完全不变；
     暗色下换成 #1d4ed8，白字对比度 6.70:1（而不是白字压 #60a5fa 的 2.4:1）。
     Lucienne 2026-11-09 拍板。 */
  --accent-solid: #002395;
  --accent-solid-hover: #001a73;
  --accent-2: #2563eb;
  --accent-3: #1e3a8a;
  --accent-soft: #eff6ff;
  --accent-border: #93c5fd;
  --success: #16a34a;
  --success-2: #059669;
  --success-text: #065f46;
  --success-soft: #d1fae5;
  --success-border: #bbf7d0;
  --warning: #f59e0b;
  --warning-2: #d97706;
  --warning-text: #92400e;
  --warning-soft: #fef3c7;
  --warning-border: #fde68a;
  --highlight-soft: #fef08a;
  --danger: #dc2626;
  --danger-2: #ef4444;
  --danger-text: #991b1b;
  --danger-soft: #fee2e2;
  --danger-border: #fecaca;
  --purple: #8b5cf6;
  --purple-2: #7c3aed;
  --purple-text: #6b21a8;
  --purple-soft: #f3e8ff;
  --purple-border: #c084fc;
  --pink-soft: #fce7f3;
  --pink-border: #c9aec0;
  --info: #06b6d4;
  --info-2: #0ea5e9;
  --info-text: #155e75;
  --info-soft: #cffafe;
  --info-border: #7dd3fc;
  --indigo: #6366f1;
  --indigo-text: #4338ca;
  --indigo-soft: #eef2ff;
  --teal-soft: #aadddd;
  --brown-text: #6b5a3a;
  --on-dark: #cfe3ff;
  /* 实心块前景色：亮色 = 白字（与改动前逐字节相同），暗色 = 深墨色压浅色底。
     2026-11-09 评审 Important-2：暗色把主色提亮后，白字压浅绿/浅黄只有 1.6:1。 */
  --on-success: #fff;
  --on-success-alt: #fff;
  --on-warning: #fff;
  --on-danger: #fff;
  --on-muted: #fff;
  /* 墨色实心块底色（徽章类）：亮色暗色都是 #0f172a，白字压上去 15:1。
     不能用 --text-strong —— 它的暗色值是近白的 #f1f5f9，会变成白底白字。 */
  --ink: #0f172a;
  /* ---- Task 5 新增：暗色校准用的成对变量 ----
     亮色取值 = 改动前的原值（逐像素不变的前提），只有暗色取值可以变。 */
  /* 实心块前景色（压在 --accent-2 浅蓝底上；暗色下白字只有 1.67:1） */
  --on-accent-2: #fff;
  /* 强提示条（.toast-strong）：底色与前景拆开，暗色用更深的绿底配白字 */
  --toast-bg: #059669;
  --toast-fg: #fff;
  /* 代码块：恒定深底 + 浅字（暗色下不能变成浅底浅字） */
  --code-bg: #1f2937;
  --code-fg: #f9fafb;
  /* 通知卡「评语预览」框：半透明浅底（暗色下要压住正文文字，必须换深底） */
  --preview-bg: rgba(255,255,255,0.55);
  /* 实心块上的半透明叠加层（白字徽标/序号）：暗色改用半透明黑 */
  --overlay-on-solid: rgba(255,255,255,.28);
  --pdf-bg: #525659;
  --shadow: 0 4px 20px rgba(0, 35, 149, 0.08);
  /* ---- 别名槽位：亮色值必须与原 hex 完全一致，否则像素会变 ---- */
  --surface-3-alt: #f8fafc;
  --surface-4-alt: #f1f5f9;
  --surface-3-alt2: #fafafa;
  --surface-3-alt3: #f5f5f5;
  --surface-3-alt4: #fafbff;
  --border-soft-alt: #eef0f3;
  --border-strong-alt: #cbd5e1;
  --border-soft-alt2: #e6e9ee;
  --border-soft-alt3: #eceef1;
  --border-strong-alt2: #d8dde6;
  --text-alt: #1e293b;
  --text-alt2: #374151;
  --text-2-alt: #475569;
  --muted-alt: #64748b;
  --accent-2-alt: #1e40af;
  --accent-2-alt2: #3b82f6;
  --accent-2-alt3: #1d4ed8;
  --accent-soft-alt: #dbeafe;
  --accent-soft-alt2: #bae6fd;
  --accent-soft-alt3: #bfdbfe;
  --info-text-alt: #0e7490;
  --info-text-alt2: #0369a1;
  --info-text-alt3: #0c4a6e;
  --info-soft-alt: #f0f9ff;
  --info-soft-alt2: #e0f2fe;
  --success-alt: #10b981;
  --success-alt2: #22c55e;
  --success-text-alt: #4f9b6a;
  --success-text-alt2: #15803d;
  --success-text-alt3: #166534;
  --success-text-alt4: #047857;
  --success-text-alt5: #064e3b;
  --success-text-alt6: #6ee7b7;
  --success-soft-alt: #dcfce7;
  --success-soft-alt2: #f0fdf4;
  --success-soft-alt3: #ecfdf5;
  --success-soft-alt4: #f3faf5;
  --success-border-alt: #a7f3d0;
  --success-border-alt2: #86c5a1;
  --warning-alt: #fbbf24;
  --warning-alt2: #e8a23a;
  --warning-alt3: #e67e22;
  --warning-text-alt: #b45309;
  --warning-text-alt2: #78350f;
  --warning-text-alt3: #a16207;
  --warning-text-alt4: #7c2d12;
  --warning-text-alt5: #9a3412;
  --warning-soft-alt: #fffbeb;
  --warning-soft-alt2: #fef9f0;
  --warning-soft-alt3: #fefce8;
  --warning-soft-alt4: #ffedd5;
  --warning-soft-alt5: #fed7aa;
  --warning-soft-alt6: #fffdf5;
  --warning-soft-alt7: #fff7ed;
  --warning-border-alt: #fcd34d;
  --warning-border-alt2: #f5e6c8;
  --warning-border-alt3: #f97316;
  --warning-border-alt4: #fb923c;
  --danger-2-alt: #dc3545;
  --danger-text-alt: #b91c1c;
  --danger-text-alt2: #be123c;
  --danger-text-alt3: #b55a68;
  --danger-soft-alt: #fef2f2;
  --danger-soft-alt2: #ffe4e6;
  --danger-soft-alt3: #fdf5f6;
  --danger-border-alt: #fca5a5;
  --danger-border-alt2: #f87171;
  --danger-border-alt3: #d99ba4;
  --purple-2-alt: #a855f7;
  --purple-text-alt: #7e22ce;
  --purple-text-alt2: #6d28d9;
  --purple-text-alt3: #a78bfa;
  --purple-soft-alt: #faf5ff;
  --purple-soft-alt2: #ede9fe;
  --muted-alt2: #94a3b8;
  --border-alt: #e2e8f0;
}

html[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f172a;
  --card: #1e293b;
  --surface: #1e293b;
  --surface-2: #263449;
  --surface-3: #334155;
  --surface-4: #3f4a5f;
  --text: #e2e8f0;
  --text-strong: #f1f5f9;
  --text-2: #cbd5e1;
  --muted: #94a3b8;
  --muted-2: #64748b;
  --muted-3: #475569;
  --border: #334155;
  --border-strong: #475569;
  --border-soft: #263449;
  --accent: #60a5fa;
  --accent-hover: #93c5fd;
  --accent-solid: #1d4ed8;
  --accent-solid-hover: #1e40af;
  --accent-2: #7dd3fc;
  --accent-3: #93c5fd;
  --accent-soft: rgba(96, 165, 250, .14);
  --accent-border: rgba(96, 165, 250, .42);
  --success: #34d399;
  --success-2: #059669;
  --success-text: #6ee7b7;
  --success-soft: rgba(52, 211, 153, .14);
  --success-border: rgba(52, 211, 153, .42);
  --warning: #fbbf24;
  --warning-2: #d97706;
  --warning-text: #fcd34d;
  --warning-soft: rgba(251, 191, 36, .14);
  --warning-border: rgba(251, 191, 36, .42);
  --highlight-soft: rgba(250, 204, 21, .22);
  --danger: #f87171;
  --danger-2: #ef4444;
  --danger-text: #fca5a5;
  --danger-soft: rgba(248, 113, 113, .14);
  --danger-border: rgba(248, 113, 113, .42);
  --purple: #a78bfa;
  --purple-2: #8b5cf6;
  --purple-text: #d8b4fe;
  --purple-soft: rgba(167, 139, 250, .14);
  --purple-border: rgba(167, 139, 250, .42);
  --pink-soft: rgba(244, 114, 182, .14);
  --pink-border: rgba(244, 114, 182, .42);
  --info: #22d3ee;
  --info-2: #0ea5e9;
  --info-text: #67e8f9;
  --info-soft: rgba(34, 211, 238, .14);
  --info-border: rgba(34, 211, 238, .42);
  --indigo: #818cf8;
  --indigo-text: #a5b4fc;
  --indigo-soft: rgba(129, 140, 248, .14);
  --teal-soft: rgba(45, 212, 191, .16);
  --brown-text: #d6c3a5;
  --on-dark: #cfe3ff;
  --on-success: #052e16;
  --on-success-alt: #052e16;
  --on-warning: #422006;
  --on-danger: #450a0a;
  --on-muted: #0f172a;
  --ink: #0f172a;
  --on-accent-2: #052e16;
  --toast-bg: #065f46;
  --toast-fg: #fff;
  --code-bg: #0f172a;
  --code-fg: #e2e8f0;
  --preview-bg: rgba(15,23,42,0.62);
  --overlay-on-solid: rgba(0,0,0,.38);
  --pdf-bg: #1e293b;
  --shadow: 0 4px 20px rgba(0, 0, 0, 0.45);
  /* ---- 别名槽位的暗色值 = 同族 role 的暗色值 ---- */
  --surface-3-alt: #334155;
  --surface-4-alt: #3f4a5f;
  --surface-3-alt2: #334155;
  --surface-3-alt3: #334155;
  --surface-3-alt4: #334155;
  --border-soft-alt: #263449;
  --border-strong-alt: #475569;
  --border-soft-alt2: #263449;
  --border-soft-alt3: #263449;
  --border-strong-alt2: #475569;
  --text-alt: #e2e8f0;
  --text-alt2: #e2e8f0;
  --text-2-alt: #cbd5e1;
  --muted-alt: #94a3b8;
  --accent-2-alt: #7dd3fc;
  --accent-2-alt2: #7dd3fc;
  --accent-2-alt3: #7dd3fc;
  --accent-soft-alt: rgba(96, 165, 250, .14);
  --accent-soft-alt2: rgba(96, 165, 250, .14);
  --accent-soft-alt3: rgba(96, 165, 250, .14);
  --info-text-alt: #67e8f9;
  --info-text-alt2: #67e8f9;
  --info-text-alt3: #67e8f9;
  --info-soft-alt: rgba(34, 211, 238, .14);
  --info-soft-alt2: rgba(34, 211, 238, .14);
  --success-alt: #34d399;
  --success-alt2: #34d399;
  --success-text-alt: #6ee7b7;
  --success-text-alt2: #6ee7b7;
  --success-text-alt3: #6ee7b7;
  --success-text-alt4: #6ee7b7;
  --success-text-alt5: #6ee7b7;
  --success-text-alt6: #6ee7b7;
  --success-soft-alt: rgba(52, 211, 153, .14);
  --success-soft-alt2: rgba(52, 211, 153, .14);
  --success-soft-alt3: rgba(52, 211, 153, .14);
  --success-soft-alt4: rgba(52, 211, 153, .14);
  --success-border-alt: rgba(52, 211, 153, .42);
  --success-border-alt2: rgba(52, 211, 153, .42);
  --warning-alt: #fbbf24;
  --warning-alt2: #fbbf24;
  --warning-alt3: #fbbf24;
  --warning-text-alt: #fcd34d;
  --warning-text-alt2: #fcd34d;
  --warning-text-alt3: #fcd34d;
  --warning-text-alt4: #fcd34d;
  --warning-text-alt5: #fcd34d;
  --warning-soft-alt: rgba(251, 191, 36, .14);
  --warning-soft-alt2: rgba(251, 191, 36, .14);
  --warning-soft-alt3: rgba(251, 191, 36, .14);
  --warning-soft-alt4: rgba(251, 191, 36, .14);
  --warning-soft-alt5: rgba(251, 191, 36, .14);
  --warning-soft-alt6: rgba(251, 191, 36, .14);
  --warning-soft-alt7: rgba(251, 191, 36, .14);
  --warning-border-alt: rgba(251, 191, 36, .42);
  --warning-border-alt2: rgba(251, 191, 36, .42);
  --warning-border-alt3: rgba(251, 191, 36, .42);
  --warning-border-alt4: rgba(251, 191, 36, .42);
  --danger-2-alt: #ef4444;
  --danger-text-alt: #fca5a5;
  --danger-text-alt2: #fca5a5;
  --danger-text-alt3: #fca5a5;
  --danger-soft-alt: rgba(248, 113, 113, .14);
  --danger-soft-alt2: rgba(248, 113, 113, .14);
  --danger-soft-alt3: rgba(248, 113, 113, .14);
  --danger-border-alt: rgba(248, 113, 113, .42);
  --danger-border-alt2: rgba(248, 113, 113, .42);
  --danger-border-alt3: rgba(248, 113, 113, .42);
  --purple-2-alt: #8b5cf6;
  --purple-text-alt: #d8b4fe;
  --purple-text-alt2: #d8b4fe;
  --purple-text-alt3: #d8b4fe;
  --purple-soft-alt: rgba(167, 139, 250, .14);
  --purple-soft-alt2: rgba(167, 139, 250, .14);
  --muted-alt2: #94a3b8;
  --border-alt: #334155;
}
/* html 也要一份底色 —— 否则下拉回弹(overscroll)时露出来的是浏览器默认白底,
   在浅色页面上就是「闪一下白」。 */
html { background: var(--bg); }
body {
  font-family: 'Noto Sans Local', 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', 'Twemoji Mozilla', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', 'Arial Unicode MS', sans-serif;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  line-height: 1.5;
}
input, textarea, select, button { font-family: inherit; }
h1, h2, h3, h4 { font-family: 'Playfair Display', serif; font-weight: 700; }
.hidden { display: none !important; }
.emoji { font-style: normal; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

code { background: var(--surface-4); padding: 0.1rem 0.35rem; border-radius: 4px; font-size: 0.85em; }

/* ============== 登录 ============== */
.auth-container {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  background:
    radial-gradient(circle at 20% 20%, rgba(237, 41, 57, 0.05), transparent 40%),
    radial-gradient(circle at 80% 80%, rgba(0, 35, 149, 0.08), transparent 40%),
    var(--bg);
}
.auth-card {
  background: var(--card);
  width: 100%; max-width: 420px;
  border-radius: 16px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
.flag-stripe { height: 6px; background: linear-gradient(to right, var(--fr-blue) 33.3%, var(--fr-white) 33.3%, var(--fr-white) 66.6%, var(--fr-red) 66.6%); }
.brand { text-align: center; font-size: 2.4rem; color: var(--accent); padding: 1.5rem 1.5rem 0.25rem; }
.brand-sub { text-align: center; color: var(--muted); font-size: 0.9rem; padding: 0 1.5rem 1.25rem; }
.tabs { display: flex; border-bottom: 1px solid var(--border); margin: 0 1.5rem; }
.tab {
  flex: 1; background: none; border: none; padding: 0.85rem;
  font-size: 0.95rem; color: var(--muted); cursor: pointer;
  border-bottom: 2px solid transparent; font-weight: 500; font-family: inherit;
}
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.form { display: none; padding: 1.5rem; gap: 0.85rem; flex-direction: column; }
.form.active { display: flex; }
.form label { display: flex; flex-direction: column; font-size: 0.85rem; color: var(--muted); gap: 0.35rem; }
.form input, .form select, .form textarea {
  font-family: inherit; font-size: 0.95rem; padding: 0.6rem 0.75rem;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--text);
}
.form input:focus, .form select:focus, .form textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0, 35, 149, 0.1);
}
.form textarea { resize: vertical; min-height: 60px; }
.checkbox { flex-direction: row !important; align-items: center; gap: 0.5rem !important; color: var(--text) !important; }

/* ============== 按钮 ============== */
.btn-primary {
  background: var(--accent-solid); color: white; border: none;
  padding: 0.7rem 1.2rem; border-radius: 8px;
  font-size: 0.95rem; font-weight: 600; cursor: pointer; font-family: inherit;
  transition: background 0.15s, transform 0.05s;
}
.btn-primary:hover { background: var(--accent-solid-hover); }
.btn-primary:disabled { background: var(--muted); color: var(--on-muted); cursor: not-allowed; }
.btn-primary.big { padding: 0.95rem 1.5rem; font-size: 1.05rem; width: 100%; }
.btn-primary.sm { padding: 0.4rem 0.75rem; font-size: 0.8rem; }
.btn-secondary {
  background: var(--card); color: var(--accent); border: 1px solid var(--accent);
  padding: 0.45rem 0.85rem; border-radius: 6px; font-size: 0.85rem;
  cursor: pointer; font-family: inherit; font-weight: 500;
}
.btn-secondary:hover { background: rgba(0, 35, 149, 0.05); }
.btn-secondary.sm { padding: 0.35rem 0.7rem; font-size: 0.8rem; }
.btn-danger {
  background: var(--surface); color: var(--danger); border: 1px solid var(--danger);
  padding: 0.45rem 0.85rem; border-radius: 6px; font-size: 0.85rem;
  cursor: pointer; font-family: inherit; font-weight: 500;
}
.btn-danger:hover { background: var(--danger); color: var(--on-danger); }
.btn-ghost {
  background: none; border: 1px solid var(--border);
  padding: 0.45rem 0.85rem; border-radius: 6px;
  font-size: 0.85rem; color: var(--muted); cursor: pointer; font-family: inherit; font-weight: 500;
}
.btn-ghost:hover { color: var(--accent); border-color: var(--accent); }

/* 深色模式开关（#theme-toggle-btn）。
   为什么要有文字标签：光一个 emoji 太像装饰图标，看不出是干什么的；
   加了「跟随系统 / 深色模式 / 浅色模式」之后一眼就知道。
   为什么用中性底（--surface-2）而不是品牌蓝：顶栏已经有好几个彩色按钮了
   （法语词汇绿 / 语法练习蓝 / 默写变位橙），再来一块彩色就分不清谁是谁了，
   中性药丸底 + 深色文字反而最跳眼。 */
#theme-toggle-btn {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: .45rem .85rem;
  font-weight: 600;
}
#theme-toggle-btn:hover {
  background: var(--surface-3);
  color: var(--text);
  border-color: var(--accent);
}
/* 标签与图标之间留白用 margin 而不是 gap —— 按钮不是 flex 容器（见下方说明） */
#theme-toggle-btn .theme-lbl { margin-left: .4rem; white-space: nowrap; }

/* 深色模式开关里的图标（#theme-toggle-btn 的唯一图形内容）。
   为什么要包一层 span：Apple Color Emoji 的 ascent+descent 是 1.76em，
   而中文（Noto Sans Local）只有 1.54em —— emoji 直接当按钮内容时，
   按钮行高会比同排的「改密码 / 退出」高 3px（38.375 vs 35.375），
   把 .app-header 从 69.766px 撑到 71.375px，整页内容被推下 1px。
   span 用 line-height:0 让 emoji 不参与行高计算 → 按钮高度与兄弟按钮完全一致，
   顶栏高度回到原值；emoji 仍是 1em，图标大小和基线位置跟原来一模一样。

   ⚠ 按钮**不要**改成 inline-flex / flex：flex 容器会让每个子元素各自居中，
   图标那个零高度的盒子会被挪到「文字行的垂直中点」上，emoji 就比文字高了 5px。
   保持普通行内流，图标 span 的基线才会和文字落在同一条基线上。 */
.theme-ico {
  font-size: 1.05em;
  line-height: 0;
  display: inline-block;
}

/* 窄屏：跟着 .btn-ghost 的尺寸一起缩（ID 选择器优先级高于 class，必须单独写） */
@media (max-width: 768px) {
  #theme-toggle-btn { padding: .35rem .6rem; font-size: .78rem; }
  #theme-toggle-btn .theme-lbl { margin-left: .3rem; }
}

.form-msg { font-size: 0.85rem; text-align: center; min-height: 1.2em; }
.form-msg.error { color: var(--danger); }
.form-msg.success { color: var(--success); }

.toast {
  position: fixed; top: 20px; right: 20px;
  background: var(--success); color: var(--on-success);
  padding: 0.6rem 1.2rem; border-radius: 8px; z-index: 200; font-size: 0.9rem;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.toast.error { background: var(--danger); color: var(--on-danger); }

/* ============== 主应用 ============== */
.app-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 1rem 2rem; background: var(--surface);
  border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10;
}

/* ---- 统一顶栏 (js/nav.js) ---- */
.app-header .user-info { display: flex; align-items: center; gap: .4rem; }
.app-header .nav-btn-vocab { font-weight: 600; background: var(--success-alt); color: var(--on-success-alt); padding: .4rem 1rem; border-radius: 6px; text-decoration: none; }
.app-header .nav-btn-grammar { font-weight: 600; background: var(--accent-solid); color: #fff; padding: .4rem 1rem; border-radius: 6px; text-decoration: none; }
.app-header .nav-btn-conjugaison { font-weight: 600; background: var(--warning); color: var(--on-warning); padding: .4rem 1rem; border-radius: 6px; text-decoration: none; }
.app-header .user-info { flex: 0 0 auto; }
.app-header .nav-actions { display: flex; align-items: center; gap: .5rem; flex: 1 1 auto; flex-wrap: wrap; justify-content: flex-end; }
/* 当前所在页面: 降低不透明度, 学生一眼知道自己在哪 */
.app-header .nav-current { opacity: .55; box-shadow: inset 0 0 0 2px rgba(255,255,255,.35); }
.app-header .nav-bell { position: relative; font-size: 1rem; }
.app-header .nav-bell #notify-badge {
  position: absolute; top: -4px; right: -6px; background: var(--danger); color: var(--on-danger);
  font-size: .65rem; padding: .1rem .35rem; border-radius: 999px; min-width: 16px; text-align: center;
}
.brand.small { font-size: 1.4rem; padding: 0; text-align: left; }
.user-info { display: flex; align-items: center; gap: 0.75rem; font-size: 0.9rem; flex-wrap: wrap; }
#user-greeting { color: var(--text); font-weight: 500; }
.role-badge {
  background: #ED2939; color: white; font-size: 0.7rem;
  padding: 0.15rem 0.5rem; border-radius: 999px; font-weight: 600;
}
.role-badge:empty { display: none; }

.app-main { max-width: 1080px; margin: 0 auto; padding: 2rem 1.5rem 4rem; }
.hero { margin-bottom: 1.5rem; }
.hero h2 { font-size: 1.8rem; color: var(--accent); }
.hero-sub { color: var(--muted); font-size: 0.9rem; margin-top: 0.25rem; }

.card {
  background: var(--card); border-radius: 12px; padding: 1.5rem;
  box-shadow: var(--shadow); margin-bottom: 1.5rem;
}
.card h3 { font-size: 1.15rem; color: var(--accent); margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem; }

/* ============== 课程卡片网格 ============== */
.course-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.25rem;
}
.course-card {
  border-radius: 12px; padding: 1.5rem; color: white;
  box-shadow: var(--shadow); cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
  position: relative; overflow: hidden;
  min-height: 160px;
  display: flex; flex-direction: column; justify-content: space-between;
}
.course-card:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); }
.course-card .ct-title { font-family: 'Playfair Display', serif; font-size: 1.3rem; font-weight: 700; line-height: 1.3; }
.course-card .ct-desc { font-size: 0.85rem; opacity: 0.9; margin-top: 0.5rem; }
.course-card .ct-progress { margin-top: 1rem; }
.course-card .ct-bar { background: rgba(255,255,255,0.3); border-radius: 4px; height: 6px; overflow: hidden; margin-top: 0.4rem; }
.course-card .ct-bar-fill { background: white; height: 100%; transition: width 0.3s; }
.course-card .ct-meta { display: flex; justify-content: space-between; font-size: 0.8rem; opacity: 0.9; }
.course-card .ct-type { font-size: 0.7rem; padding: 0.15rem 0.5rem; background: rgba(255,255,255,0.25); border-radius: 999px; display: inline-block; margin-bottom: 0.5rem; }

/* ============== 任务列表 ============== */
.task-list { display: flex; flex-direction: column; gap: 0.6rem; }
.task-item {
  display: flex; align-items: center; gap: 1rem;
  padding: 1rem 1.25rem; background: var(--card);
  border-radius: 10px; box-shadow: var(--shadow);
  border-left: 4px solid var(--border);
  transition: transform 0.1s;
}
.task-item:hover { transform: translateX(2px); }
.task-item.unlocked { border-left-color: var(--accent); cursor: pointer; }
.task-item.done { border-left-color: var(--success); }
.task-item.graded { border-left-color: var(--warning); }
.task-item.locked { opacity: 0.6; cursor: not-allowed; background: var(--surface-3); }
.task-item .ti-num {
  width: 2rem; height: 2rem; border-radius: 50%;
  background: var(--accent-solid); color: white;
  display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 0.9rem; flex-shrink: 0;
}
.task-item.locked .ti-num { background: var(--muted); color: var(--on-muted); }
.task-item.done .ti-num { background: var(--success); color: var(--on-success); }
.task-item.graded .ti-num { background: var(--warning); color: var(--on-warning); }
.task-item .ti-main { flex: 1; min-width: 0; }
.task-item .ti-title { font-weight: 600; font-size: 1rem; }
.task-item .ti-meta { font-size: 0.8rem; color: var(--muted); margin-top: 0.2rem; }
.task-item .ti-status { font-size: 0.75rem; padding: 0.2rem 0.65rem; border-radius: 999px; font-weight: 600; flex-shrink: 0; }
.status-done { background: var(--success-soft-alt); color: var(--success-text-alt3); }
.status-graded { background: var(--warning-soft); color: var(--warning-text); }
.status-pending { background: var(--accent-soft-alt); color: var(--accent-2-alt); }
.status-locked { background: var(--surface-4); color: var(--muted); }

/* ============== 任务详情 ============== */
.task-detail-header {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 1rem; margin-bottom: 1.5rem; flex-wrap: wrap;
  padding-bottom: 1rem; border-bottom: 1px dashed var(--border);
}
.task-detail-header h2 {
  font-size: 2rem; font-weight: 700; color: var(--accent);
  margin-bottom: 0.4rem; letter-spacing: -0.01em; line-height: 1.3;
}
.task-detail-header .task-meta {
  color: var(--text); font-size: 0.95rem; margin-top: 0.5rem;
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem;
}
.task-detail-header .task-meta a {
  color: var(--accent); text-decoration: none; font-weight: 500;
  padding: 0.15rem 0.5rem; background: rgba(0, 35, 149, 0.06);
  border-radius: 4px; transition: background 0.15s;
}
.task-detail-header .task-meta a:hover { background: rgba(0, 35, 149, 0.12); }
.task-detail-header .task-meta .meta-divider { color: var(--muted); }
.task-detail-header .task-meta .meta-require {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.2rem 0.6rem; background: var(--warning-soft); color: var(--warning-text);
  border-radius: 12px; font-size: 0.85rem; font-weight: 500;
}
.section-title { font-family: 'Playfair Display', serif; font-size: 1.05rem; color: var(--accent); margin: 1.5rem 0 0.75rem; display: flex; align-items: center; gap: 0.5rem; }
.section-title::before { content: ''; width: 4px; height: 18px; background: var(--accent); border-radius: 2px; }

/* 资料列表 */
.material-list { display: flex; flex-direction: column; gap: 0.6rem; }
.material-item {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.75rem 1rem; background: var(--bg); border-radius: 8px;
  font-size: 0.9rem;
}
.material-item .mi-icon { font-size: 1.3rem; }
.material-item .mi-name { flex: 1; word-break: break-all; }
.material-item .mi-size { color: var(--muted); font-size: 0.8rem; }
.material-item .mi-actions { display: flex; gap: 0.4rem; }

/* 文本材料(老师发的说明) */
.text-material {
  background: var(--surface);
  border: 1px solid var(--border); border-left: 4px solid var(--accent);
  padding: 1.25rem 1.5rem 1.4rem; border-radius: 10px;
  white-space: pre-line; word-break: break-word; overflow-wrap: anywhere;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', sans-serif;
  font-size: 1.15rem; line-height: 1.7; word-spacing: normal; letter-spacing: normal;
  color: var(--text); box-shadow: 0 1px 3px rgba(0, 35, 149, 0.08);
  max-width: 820px; margin-bottom: 1rem; transition: box-shadow 0.2s;
  tab-size: 4;
}
.text-material:hover { box-shadow: 0 2px 8px rgba(0, 35, 149, 0.14); }
.text-material + .text-material { margin-top: 0.25rem; }
/* 段落视觉分组（描述里换行的段落空隙） */
.text-material p,
.text-material div { margin: 0.35em 0; padding: 0; line-height: 1.6; }
.text-material p:first-child,
.text-material div:first-child { margin-top: 0; }
.text-material p:last-child,
.text-material div:last-child { margin-bottom: 0; }
/* bugfix 2026-10-08 Lucienne 反馈: task 详情 markdown 列表项之间"空一行"
   parseMarkdown 把 "2. xxx" 转 <ol> + 后续单换行, 加上默认 0.4rem margin 累积像空行
   改成 0.2rem 紧凑 + line-height 适度 (1.5) */
.text-material ol,
.text-material ul { margin: 0.2rem 0 0.2rem 1.5rem; line-height: 1.5; }
.text-material ol li,
.text-material ul li { margin: 0.1rem 0; }

/* bugfix 2026-10-08 Lucienne 反馈: 学生 text_body 里的空 <p>/<div> (contenteditable 两次回车产生) 渲染时空一行
   用 :empty 选择器完全隐藏空段落 */
.text-material p:empty,
.text-material div:empty { display: none; }

/* bugfix 2026-10-16 Lucienne (第 2 版): 这条规则**必须整个删掉**, 不能改写成 br+br+br。

   为什么 br+br+br 也不行 —— CSS 兄弟选择器**不把文本节点算作兄弟**。
   `A<br><br>B<br><br>C` 里的 A / B / C 都是文本节点, 在 CSS 眼里这个序列是
   **4 个连续的 br 兄弟**, 于是 `br + br + br` 命中第 2、3、4 个 → 全被 display:none,
   渲染成 `A / BC`。

   parseMarkdown 的输出恰好全是这种结构:
     `<span class="md-num">1.</span>Voici ... <br><br>二、...`
   两段中文之间的 <br><br> 之间只隔着**文本节点**, CSS 认为它们和后面的 br 连成一片,
   于是"源码里的空行"被成片吃掉 —— 任务说明里一/二/三大题之间、序号行之间、
   【对话场景设定】前面、"作业小贴士"前面的空行全部消失。

   实测 (极简对照页 A<br><br>B<br><br>C):
     无 class                       → A / 空行 / B / 空行 / C   正确
     .text-material 原样            → A / 空行 / BC              错误
     .text-material 禁用本规则      → A / 空行 / B / 空行 / C   正确

   Google Docs 粘贴的冗余空段由上一条 `p:empty / div:empty { display: none }` 处理,
   不需要用 br 选择器 —— 兄弟选择器在这里根本不成立, 只会误伤 markdown 空行。

/* Google Docs 复制的冗余 font face/size tag 简化 */
.text-material font[face] { font-family: inherit; }
.text-material font[size] { font-size: inherit; }
/* 描述卡片标题小标签 */
.text-material-label {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.78rem; font-weight: 600; color: var(--accent);
  background: rgba(0, 35, 149, 0.08); padding: 0.25rem 0.65rem;
  border-radius: 12px; margin-bottom: 0.65rem;
}
/* bugfix 2026-10-08 Lucienne: 学生端 1⃣️ 等 keycap emoji 序号 fallback 到黑色方块
   用 .num-circle 替代 — 蓝色圆圈数字 UI,所有浏览器统一显示 */
.text-material .num-circle,
.num-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5em;
  height: 1.5em;
  padding: 0 0.3em;
  background: var(--accent-solid, #002395);
  color: white;
  border-radius: 50%;
  font-weight: 700;
  font-size: 0.85em;
  margin-right: 0.4em;
  vertical-align: middle;
  line-height: 1;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}



/* perf 2026-10-08: 上传进度条 (XHR upload.onprogress 驱动) */
.upload-progress {
  position: relative;
  height: 26px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  margin-top: 0.6rem;
}
.upload-progress-bar {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  background: linear-gradient(90deg, var(--accent-2-alt2), var(--info));
  transition: width 0.2s ease;
  width: 0%;
}
.upload-progress-text {
  position: absolute;
  left: 0; right: 0; top: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.82rem; font-weight: 600;
  color: var(--text);
  mix-blend-mode: difference;
  font-variant-numeric: tabular-nums;
}
/* 上传/文件预览 */
.upload-zone {
  border: 2px dashed var(--border); border-radius: 10px;
  padding: 1.5rem; text-align: center; cursor: pointer;
  background: var(--bg); transition: all 0.15s;
}
.upload-zone:hover, .upload-zone.dragover {
  border-color: var(--accent); background: rgba(0, 35, 149, 0.03);
}
.upload-zone p { color: var(--muted); font-size: 0.9rem; }
.upload-zone .icon { font-size: 2rem; }
.file-list { margin-top: 1rem; display: flex; flex-direction: column; gap: 0.4rem; }
.file-row {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.75rem; background: var(--bg); border-radius: 6px;
  font-size: 0.85rem;
}
.file-row .fr-name { flex: 1; word-break: break-all; }
.file-row .fr-size { color: var(--muted); font-size: 0.75rem; }
.file-row audio, .file-row video { height: 32px; max-width: 280px; }
.file-row img { max-width: 200px; max-height: 120px; border-radius: 4px; }

/* 媒体(学生提交的文件预览) */
.media-block { margin-top: 0.75rem; }
.media-block audio, .media-block video { width: 100%; max-width: 480px; border-radius: 8px; }
.media-block img { max-width: 100%; max-height: 400px; border-radius: 8px; border: 1px solid var(--border); }

/* 提交状态条 */
.submission-bar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.75rem 1rem; border-radius: 8px;
  background: linear-gradient(135deg, rgba(0, 35, 149, 0.05), rgba(0, 35, 149, 0.02));
  border: 1px solid rgba(0, 35, 149, 0.1);
  margin-bottom: 1rem;
}
.submission-bar.graded { background: linear-gradient(135deg, rgba(245, 158, 11, 0.08), rgba(245, 158, 11, 0.02)); border-color: rgba(245, 158, 11, 0.2); }
.submission-bar strong { color: var(--accent); }
.submission-bar.graded strong { color: var(--warning-text); }

/* 老师批改面板 */

/* bugfix 2026-10-07: 写作批改弹窗 sentence-list 内部独立滚动
   之前 textarea autosize 让整个 modal 越拉越长, 用户得手动往上找当前编辑的句子
   现在 sentence-list 限高 + 内部 overflow, modal 标题位置固定, 新加的句子自动出现在 list 内底部
   配合 .tcf-sentence-list 同样适用 TCF 3 个独立 Tâche 列表 */
.sentence-scroll-container {
  max-height: 50vh;
  overflow-y: auto;
  padding-right: 0.5rem;
  margin-right: -0.5rem;
  scroll-behavior: smooth;
  /* bugfix 2026-11-06 (Lucienne「打字越多越往下」): 关掉浏览器的「滚动锚定」。
     Chrome 会在容器内内容尺寸变化时自动调 scrollTop, 和 admin.html 里我们自己实现的
     「文本框往上长」补偿抢方向盘 —— 实测每敲一行 scrollTop 被浏览器偷偷多推 ~20px,
     下面还没批的句子就跟着飘。关掉之后滚动位置完全由 JS 的补偿逻辑独占。 */
  overflow-anchor: none;
}
/* 内嵌 sentence-list 时 */
#sentence-list.sentence-scroll-container,
.tcf-sentence-list.sentence-scroll-container {
  /* just composes */
}

.grading-box {
  background: linear-gradient(135deg, rgba(245, 158, 11, 0.08), rgba(245, 158, 11, 0.02));
  border: 1px solid rgba(245, 158, 11, 0.2);
  border-radius: 10px; padding: 1.25rem; margin-top: 1rem;
}
.grading-box h4 { color: var(--warning-text); margin-bottom: 0.75rem; font-size: 1rem; }

/* =====================================================
   Lucienne 2026-10-07 UI 整理: 简洁大气
   - 统一 card 风格 (.task-card)
   - 统一 meta-pill (圆角 label)
   - 简化 section-heading (无 emoji 装饰)
   ===================================================== */
.task-card {
  background: var(--surface);
  border-radius: 14px;
  padding: 1.5rem 1.75rem;
  margin-bottom: 1.25rem;
  border: 1px solid var(--border);
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04), 0 1px 2px rgba(15, 23, 42, 0.03);
}
.task-card-muted {
  background: var(--surface-2);
  border-color: var(--surface-4-alt);
}
/* 已完成 / 已提交的练习卡片 (2026-10-23)
   之前 .done 只在 JS 里加 class 但没有对应样式, 完成态看不出区别。
   弱化背景 + 绿色左边框表示"已完成"。 */
.task-card.done {
  background: var(--surface-2);
  border-color: var(--border-alt);
  border-left: 3px solid var(--success);
}
.task-card.done .task-card-title {
  color: var(--muted);
}
.task-card-warm {
  background: var(--warning-soft-alt);
  border-color: var(--warning-border);
}
.task-card-cool {
  background: var(--info-soft-alt);
  border-color: var(--accent-soft-alt2);
}
.task-card-success {
  background: var(--success-soft-alt3);
  border-color: var(--success-border-alt);
}
.task-card-danger {
  background: var(--danger-soft-alt);
  border-color: var(--danger-border);
}

.task-card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  flex-wrap: wrap;
}
.task-card-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-strong);
  letter-spacing: -0.02em;
  line-height: 1.3;
  margin: 0;
}
.task-card-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--muted-alt);
  margin-top: 0.5rem;
}
.task-card-meta a {
  color: var(--accent-2-alt);
  text-decoration: none;
  font-weight: 500;
}
.task-card-meta a:hover { text-decoration: underline; }
.task-card-meta .meta-divider {
  color: var(--border-strong-alt);
  margin: 0 0.1rem;
}

.section-heading {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.75rem;
  display: block;
}
.section-heading-lg {
  font-size: 0.85rem;
  margin: 1.75rem 0 0.85rem;
}

.meta-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 500;
  background: var(--surface-4);
  color: var(--text-2);
  line-height: 1.2;
}
.meta-pill-primary  { background: var(--accent-soft); color: var(--accent-2-alt); }
.meta-pill-success  { background: var(--success-soft); color: var(--success-text); }
.meta-pill-warning  { background: var(--warning-soft); color: var(--warning-text); }
.meta-pill-danger   { background: var(--danger-soft); color: var(--danger-text); }
.meta-pill-neutral  { background: var(--surface-4); color: var(--text-2); }
.meta-pill-solid    { background: var(--ink); color: white; }

.task-action-group {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}

.grade-card {
  background: var(--warning-soft-alt);
  border-radius: 12px;
  padding: 1.25rem 1.5rem;
  margin-bottom: 1rem;
  border-left: 4px solid var(--warning);
}
.grade-card-blue   { background: var(--info-soft-alt); border-left-color: var(--info-2); }
.grade-card-green  { background: var(--success-soft-alt2); border-left-color: var(--success-alt); }
.grade-card-yellow { background: var(--warning-soft-alt); border-left-color: var(--warning); }
.grade-card-red    { background: var(--danger-soft-alt); border-left-color: var(--danger-2); }
.grade-card-purple { background: var(--purple-soft-alt); border-left-color: var(--purple-2-alt); }

.grade-card-title {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text-strong);
  margin-bottom: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.dim-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.65rem;
  margin-top: 0.5rem;
}
.dim-cell {
  background: var(--surface);
  border: 1px solid var(--surface-4);
  border-radius: 10px;
  padding: 0.6rem 0.75rem;
}
.dim-cell-label {
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 0.3rem;
}
.dim-cell-value {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
}
.dim-cell-bar {
  margin-top: 0.45rem;
  height: 5px;
  background: var(--surface-4);
  border-radius: 3px;
  overflow: hidden;
}
.dim-cell-bar i {
  display: block;
  height: 100%;
  border-radius: 3px;
  transition: width 0.3s;
}
.dim-cell-success i { background: var(--success-alt); }
.dim-cell-warning i { background: var(--warning); }
.dim-cell-danger  i { background: var(--danger-2); }

.feedback-box {
  background: var(--warning-soft-alt);
  border-left: 3px solid var(--warning);
  padding: 0.85rem 1.1rem;
  border-radius: 0 10px 10px 0;
  white-space: pre-wrap;
  font-size: 0.92rem;
  line-height: 1.65;
  color: var(--text);
  margin-top: 0.5rem;
}

.text-mono {
  background: var(--success-soft-alt2);
  border: 1px solid var(--success-border);
  border-radius: 10px;
  padding: 1rem 1.25rem;
  line-height: 1.7;
  font-size: 0.95rem;
  color: var(--success-text-alt5);
  white-space: pre-wrap;
  word-wrap: break-word;
  margin-top: 0.5rem;
}

.text-block {
  background: var(--surface-3);
  border-radius: 10px;
  padding: 0.9rem 1.15rem;
  line-height: 1.65;
  white-space: pre-wrap;
  word-wrap: break-word;
  margin-top: 0.5rem;
  font-size: 0.95rem;
  color: var(--text);
}

.text-block-listening {
  background: var(--success-soft-alt3);
  border-left: 3px solid var(--success-alt);
  border-radius: 0 10px 10px 0;
  padding: 0.85rem 1.1rem;
  white-space: pre-wrap;
  margin-top: 0.5rem;
  font-size: 0.9rem;
  color: var(--success-text-alt5);
}

.grade-row { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.grade-chip {
  padding: 0.4rem 0.9rem; border: 1px solid var(--border);
  border-radius: 6px; background: var(--surface); cursor: pointer;
  font-family: inherit; font-weight: 600; font-size: 0.9rem;
}
.grade-chip.active { background: var(--accent-solid); color: white; border-color: var(--accent); }

/* 公开作品墙 */
.feed-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1rem;
}
.feed-card {
  background: var(--card); border-radius: 12px; padding: 1.25rem;
  box-shadow: var(--shadow);
}
.feed-card .fc-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.feed-card .fc-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--accent-solid); color: white; display: flex; align-items: center; justify-content: center; font-weight: 600; }
.feed-card .fc-name { font-weight: 600; font-size: 0.9rem; }
.feed-card .fc-task { font-size: 0.75rem; color: var(--muted); }
.feed-card .fc-text { font-size: 0.9rem; line-height: 1.6; white-space: pre-wrap; margin: 0.75rem 0; }
.feed-card .fc-grade { font-size: 0.75rem; color: var(--warning-text); background: rgba(245, 158, 11, 0.1); padding: 0.2rem 0.6rem; border-radius: 999px; display: inline-block; }

/* ============== 老师后台 ============== */
.stats-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem; margin-bottom: 1.5rem;
}
.stat-card {
  background: var(--surface); padding: 1.25rem; border-radius: 12px;
  box-shadow: var(--shadow); text-align: center; border-top: 3px solid var(--accent);
}
.stat-num { font-family: 'Playfair Display', serif; font-size: 2.2rem; font-weight: 700; color: var(--accent); }
.stat-label { font-size: 0.85rem; color: var(--muted); margin-top: 0.25rem; }

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th, td { padding: 0.65rem 0.75rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
th { background: var(--bg); font-weight: 600; color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; }
tr:hover td { background: rgba(0, 35, 149, 0.02); }

.badge-yes { background: var(--success-soft-alt); color: var(--success-text-alt3); padding: 0.15rem 0.6rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; display: inline-block; }
.badge-no { background: var(--danger-soft); color: var(--danger-text); padding: 0.15rem 0.6rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; display: inline-block; }
.badge-disabled { background: var(--surface-4); color: var(--muted); padding: 0.15rem 0.6rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; display: inline-block; }

.row-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; }
.row-actions > button { white-space: nowrap; line-height: 1.2; }
.row-actions > .btn-remind-checkin { color: var(--warning); border-color: var(--warning); }
.row-actions > .btn-remind-grammar { color: var(--purple); border-color: var(--purple); }

/* 2026-10-10 Lucienne: "⋯" dropdown (HTML <details> 原生, 无 JS) */
.dropdown { position: relative; }
.dropdown > summary {
  list-style: none; cursor: pointer;
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 6px;
  color: var(--muted); font-size: 1.2rem; line-height: 1;
  user-select: none; background: var(--surface);
  transition: border-color 0.15s, color 0.15s;
}
.dropdown > summary::-webkit-details-marker { display: none; }
.dropdown > summary:hover { border-color: var(--accent); color: var(--accent); }
.dropdown[open] > summary { border-color: var(--accent); color: var(--accent); }
.dropdown-menu {
  position: absolute; right: 0; top: calc(100% + 4px);
  min-width: 140px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  padding: 4px 0; z-index: 10;
}
.dropdown-menu button {
  display: block; width: 100%; padding: 0.5rem 0.9rem;
  background: none; border: none; text-align: left;
  font: inherit; color: var(--text); cursor: pointer;
  font-size: 0.88rem;
}
.dropdown-menu button:hover { background: var(--bg, #f5f5f5); }
.dropdown-menu button.text-danger { color: var(--danger); }
.dropdown-menu button.text-danger:hover { background: rgba(220,38,38,0.08); }

.filter-row { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1rem; align-items: center; }
.filter-row label { font-size: 0.85rem; color: var(--muted); display: flex; align-items: center; gap: 0.3rem; }
.filter-row input, .filter-row select { padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; /* 2026-11-07: 0.85rem = 13.6px < 16px —— iOS 聚焦时自动放大页面。改 1rem。 */ font-size: 1rem; }

/* 弹窗 */
.modal-mask {
  position: fixed; inset: 0; background: rgba(0,0,0,0.4);
  display: flex; align-items: center; justify-content: center;
  z-index: 100; padding: 0.5rem;
}
.modal {
  position: relative;  /* bugfix 2026-10-01: 让 .modal-close absolute 锚到 modal 内部右上角 */
  background: var(--surface); border-radius: 12px; width: 100%;
  max-width: 520px; padding: 1.5rem;
  max-height: 90vh; overflow-y: auto;
}
.modal h3 { color: var(--accent); margin-bottom: 1rem; padding-right: 1.5rem; } /* 给叉按钮留空间 */
.modal-close {
  position: absolute; top: 0.5rem; right: 0.75rem;
  background: transparent; border: none;
  color: var(--danger, #dc2626);
  font-size: 1.6rem; font-weight: 700;
  cursor: pointer; padding: 0.25rem 0.55rem;
  line-height: 1; border-radius: 6px;
  transition: background 0.15s, transform 0.1s;
  z-index: 1;
}
.modal-close:hover { background: rgba(220, 38, 38, 0.1); }
.modal-close:active { transform: scale(0.92); }
.modal .form { display: flex; padding: 0; gap: 0.85rem; box-sizing: border-box; }
/* bugfix 2026-10-14 Lucienne (v2): 完全 revert -1rem 套路,让 children 老实待在 form padding 内部
   原因: 之前用 -1rem 让 children 撑到 form border-box 边缘,但 form background (尤其 .grade-card 的 border-radius) 不能完全覆盖,
   桌面 + iPad 都会出现 children 飘出 form 黄色背景的问题
   现在的方案: form padding 1rem + children 100% (form content 840px),form background 100% (border-box 872px),
   16px 黄色 padding 露在两边当视觉留白 → 简洁、对齐、不溢出 */
.modal .form > *:not(button):not(p):not(hr) { box-sizing: border-box; }
/* Override inline -1rem on textareas / toolbars / inputs (admin.html 里手写的 width:calc(100% + 2rem); margin:0 -1rem;) */
.modal .form [style*="calc(100% + 2rem)"] { width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }
.modal .form hr { box-sizing: border-box; }

/* bugfix 2026-10-14 Lucienne: 移动端 (≤ 600px) revert -1rem 负 margin,避免 form 元素顶到屏幕边缘
   桌面端保持全宽 = form 宽 (跟 toolbar/textarea 持平)
   移动端回到 form content 宽 (form padding 内部) + 适当 modal 留白 */
@media (max-width: 600px) {
  .modal-mask { padding: 0.25rem; }
  .modal { padding: 1rem; max-height: 95vh; }
  /* Revert 桌面端 -1rem trick,让 form 子元素待在 form padding 内部 */
  .modal .form > *:not(button):not(p):not(hr) { width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }
  .modal .form > div[style*="background:var(--warning-soft-alt2)"] { width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }
  .modal .form hr { width: 100% !important; margin-left: 0 !important; margin-right: 0 !important; }
  /* Override inline -1rem on textareas / toolbars / inputs (用 !important 压过 inline style) */
  textarea[style*="calc(100% + 2rem)"],
  input[style*="calc(100% + 2rem)"],
  div[style*="calc(100% + 2rem)"] {
    width: 100% !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
  }
  /* 移动端 textarea rows 略减,避免空白 modal 太高 */
  textarea[rows="10"] { rows: 6; }
}
.modal-actions { display: flex; gap: 0.5rem; justify-content: flex-end; margin-top: 1rem; position: sticky; bottom: 0; background: var(--surface); padding: 0.75rem 0 0.25rem; box-shadow: 0 -4px 8px -4px rgba(0,0,0,0.06); z-index: 5; } /* bugfix 2026-10-13 Lucienne: 批改 modal 内容长 → 拉到最底下才看到「保存修改」 → sticky bottom 一直可见 */

/* 输入类型选择器(老师) */
.input-type-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.input-type-chip {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.6rem 0.9rem; border: 1px solid var(--border);
  border-radius: 8px; cursor: pointer;
  background: var(--surface); font-size: 0.9rem;
}
.input-type-chip input { display: none; }
.input-type-chip:has(input:checked) { background: rgba(0, 35, 149, 0.08); border-color: var(--accent); color: var(--accent); font-weight: 500; }

/* 进度条(学生) */
.progress-bar { background: var(--bg); border-radius: 4px; height: 6px; overflow: hidden; }
.progress-bar-fill { background: var(--success); height: 100%; transition: width 0.3s; }
.progress-cell { display: flex; align-items: center; gap: 0.5rem; }
.progress-pct { font-size: 0.8rem; color: var(--muted); min-width: 35px; }

/* 响应式 */
@media (max-width: 899px) {
  /* 顶栏永远单行: 按钮多的时候横向滑动, 不再折成两行 */
  .app-header { padding: .7rem .8rem; gap: .5rem; flex-wrap: nowrap; }
  .app-header .brand.small { font-size: 1.05rem; white-space: nowrap; flex: 0 0 auto; }
  .app-header .user-info { flex: 0 0 auto; gap: .35rem; padding-bottom: 1px; }
  .app-header .nav-actions {
    flex: 1 1 auto; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; gap: .35rem; padding-bottom: 1px; justify-content: flex-end;
  }
  .app-header .nav-actions::-webkit-scrollbar { display: none; }
  .app-header .nav-actions > * { flex: 0 0 auto; }
  .app-header .btn-ghost, .app-header .btn-primary.sm { padding: .35rem .6rem; font-size: .78rem; white-space: nowrap; }
  .app-header #user-greeting { white-space: nowrap; }
  .app-main { padding: 1.25rem 1rem 3rem; }
  .hero h2 { font-size: 1.4rem; }
  .user-info { font-size: 0.8rem; }
  .stat-num { font-size: 1.8rem; }
  .brand { font-size: 2rem; }
  .input-type-grid { grid-template-columns: 1fr; }
  /* 语法题字号/行距收紧 + 填空输入框更窄 */
  .grammar-html-content { font-size: 1rem !important; line-height: 1.8 !important; padding: 0.5rem 0.25rem !important; }
  .grammar-html-content input.grammar-blank { min-width: 60px !important; max-width: 140px !important; }
  /* 描述卡片字号略小 */
  .text-material { font-size: 1.05rem !important; padding: 1rem 1.1rem 1.15rem !important; }
  /* 任务标题字号略小 */
  .task-detail-header h2 { font-size: 1.6rem !important; }
  /* 课程表格行单列堆叠（避免按钮溢出） */
  .course-row { display: flex !important; flex-direction: column; align-items: stretch !important; gap: 0.4rem; padding: 0.75rem 0.75rem 0.75rem 1rem !important; }
  .course-row > div:nth-of-type(1) { order: 1; flex: none !important; } /* 标题容器（第一个 div，跳过色条 span） */
  .course-row > div:nth-of-type(2) { order: 2; }
  .course-row > div:nth-of-type(3) { order: 3; text-align: left !important; min-width: 0 !important; }
  .course-row > div:nth-of-type(4) { order: 4; opacity: 1 !important; justify-content: flex-start !important; flex-wrap: wrap; }
  /* 隐藏表头 */
  .course-row-header { display: none !important; }
}

/* Desktop ≥1100px：课程行信息部分横排（标题/类型/规模），操作按钮始终独立一行 */
@media (min-width: 1100px) {
  .course-row > div:nth-of-type(1) { flex-direction: row !important; }
}
/* 课程行 hover 高亮（淡淡的灰） */
.course-row:hover { background: var(--surface-3); }
.course-row:last-child { border-bottom: none; }

/* ============== 音频播放器全局美化 ============== */
audio {
  height: 40px;
  border-radius: 8px;
  background: var(--surface-3-alt);
  border: 1px solid var(--border-alt);
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
  transition: border-color 0.2s, box-shadow 0.2s;
  accent-color: var(--accent);
}
audio:hover {
  border-color: var(--border-strong-alt);
  box-shadow: 0 2px 6px rgba(0,0,0,0.06);
}
/* 录音卡片容器 */
.rec-card {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.7rem;
  background: var(--surface-3-alt2);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 0.3rem;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.rec-card:hover {
  border-color: var(--border-strong);
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.rec-card .rec-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  background: var(--accent-solid);
  color: #fff;
  border-radius: 6px;
  font-size: 0.75rem;
  font-weight: 600;
  flex-shrink: 0;
}
.rec-card audio {
  flex: 1;
  /* bugfix 2026-10-16 Lucienne: 36px 太矮根本看不见波形,加到 54px */
  height: 54px;
  border: none;
  box-shadow: none;
  background: transparent;
}
.rec-card-existing { background: var(--success-soft-alt2); border-color: var(--success-border); }
/* 批改 modal 内录音区整体加高 + 加大间距 */
#rec-list { display: flex; flex-direction: column; gap: 0.5rem; }
#rec-list .rec-card { padding: 0.7rem 0.9rem; }
.rec-card .rec-del {
  font-size: 0.7rem;
  padding: 0.2rem 0.5rem;
  border-radius: 6px;
  flex-shrink: 0;
  opacity: 0.6;
  transition: opacity 0.15s;
}
.rec-card .rec-del:hover {
  opacity: 1;
}

/* Markdown 表格 */
.md-table {
  border-collapse: collapse;
  margin: 0.75rem 0;
  width: 100%;
  max-width: 760px;
  font-size: 0.95rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.md-table th, .md-table td {
  border: 1px solid var(--border);
  padding: 0.5rem 0.75rem;
  vertical-align: top;
}
.md-table thead th {
  background: var(--bg);
  font-weight: 600;
  color: var(--accent);
  text-transform: none;  /* 覆盖全局 th 的 uppercase,保持用户原文本 */
  font-size: 0.95rem;     /* 覆盖全局 th 的 0.75rem */
}
.md-table tbody tr:nth-child(even) td {
  background: rgba(0,0,0,0.02);
}
.md-table tbody tr:hover td {
  background: rgba(0, 35, 149, 0.04);
}

/* Modal 宽屏变体 */
.modal.modal-wide { max-width: 720px; }

@keyframes autosave-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.3; transform: scale(0.7); }
}

.toast-strong {
  position: fixed;
  top: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--toast-bg);
  color: var(--toast-fg);
  padding: 0.75rem 1.5rem;
  border-radius: 10px;
  z-index: 9999;
  font-size: 1rem;
  font-weight: 600;
  box-shadow: 0 8px 24px rgba(0,0,0,0.25);
  animation: toast-pop 0.25s ease-out;
}
.toast-strong.error {
  background: var(--danger);
  color: var(--on-danger);
}
@keyframes toast-pop {
  0% { opacity: 0; transform: translate(-50%, -10px); }
  100% { opacity: 1; transform: translate(-50%, 0); }
}

/* 法语虚拟键盘（学生写作任务 · 仅 is_writing=true 显示） */
.french-virtual-keyboard {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.32rem;
  margin: 0.55rem 0 0.7rem;
  padding: 0.55rem 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.french-virtual-keyboard .kb-label {
  font-size: 0.78rem;
  color: var(--muted-2);
  margin-right: 0.45rem;
  font-weight: 500;
  font-style: italic;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.french-virtual-keyboard .kb-shift {
  font-size: 0.7rem;
  padding: 0.28rem 0.55rem;
  background: var(--surface);
  color: var(--text-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  font-weight: 600;
  letter-spacing: 0.04em;
  transition: all 0.12s;
  margin-right: 0.15rem;
  white-space: nowrap;
}
.french-virtual-keyboard .kb-shift:hover {
  background: var(--surface-3);
  border-color: var(--border-strong);
}
.french-virtual-keyboard .kb-shift.active {
  background: var(--warning-soft);
  color: var(--warning-text);
  border-color: var(--warning);
}
.french-virtual-keyboard .kb-key {
  min-width: 32px;
  height: 32px;
  padding: 0 0.45rem;
  font-size: 1.05rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  font-family: 'Georgia', 'Noto Serif', 'Noto Sans Local', serif;
  color: var(--text);
  transition: all 0.1s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 400;
}
.french-virtual-keyboard .kb-key:hover {
  background: var(--surface-3);
  border-color: var(--accent-border);
  color: var(--accent-3);
}
.french-virtual-keyboard .kb-key:active {
  background: var(--accent-soft);
  transform: translateY(1px);
}
@media (max-width: 600px) {
  .french-virtual-keyboard .kb-key { min-width: 28px; height: 28px; font-size: 0.95rem; padding: 0 0.3rem; border-radius: 5px; }
  .french-virtual-keyboard .kb-shift { padding: 0.2rem 0.35rem; font-size: 0.6rem; border-radius: 5px; }
  .french-virtual-keyboard .kb-label { font-size: 0.7rem; margin-right: 0.3rem; }
  .french-virtual-keyboard { padding: 0.45rem 0.55rem; gap: 0.25rem; border-radius: 8px; }
}

/* 富文本编辑器（TCF 写作任务 · 替换 textarea） */
.rich-editor {
  font-family: 'Noto Sans Local', sans-serif;
  min-height: 80px;
  max-height: 600px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 0.6rem 0.7rem;
  outline: none;
  line-height: 1.7;
  font-size: 0.95rem;
  background: var(--surface);
  color: var(--text);
  overflow-y: auto;
  word-wrap: break-word;
  white-space: pre-wrap;
}
.rich-editor:focus {
  border-color: var(--accent-border);
  box-shadow: 0 0 0 2px rgba(147, 197, 253, 0.25);
}
.rich-editor:empty:before {
  content: attr(data-placeholder);
  color: var(--muted-2);
  pointer-events: none;
  font-style: italic;
}
.rich-editor b, .rich-editor strong { font-weight: 700; }
.rich-editor i, .rich-editor em { font-style: italic; }
.rich-editor u { text-decoration: underline; }

.rich-toolbar {
  display: flex;
  gap: 0.3rem;
  margin-top: 0.4rem;
  padding: 0.3rem;
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.rich-btn {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 3px;
  padding: 0.25rem 0.55rem;
  font-size: 0.85rem;
  cursor: pointer;
  min-width: 32px;
  font-family: Georgia, 'Noto Sans Local', serif;
  color: var(--text);
  transition: all 0.12s;
}
.rich-btn:hover {
  background: var(--accent-soft);
  border-color: var(--accent-border);
}
.rich-btn.active {
  background: var(--accent-soft-alt);
  border-color: var(--accent-2-alt2);
  color: var(--accent-3);
}
@media (max-width: 600px) {
  .rich-editor { font-size: 0.9rem; padding: 0.5rem; }
  .rich-btn { padding: 0.2rem 0.4rem; min-width: 28px; font-size: 0.8rem; }
}

/* bugfix 2026-10-09 Lucienne: 条件式练习 (Leçon 7) 表格 verb 列太窄导致单词每个字母一行
   根因: <th></th> 空表头 + 父 .grammar-html-content 的 word-break:break-word 把单词逐字断开
   修法: 强制 verb 列最小宽度 + 禁止换行 + 视觉上让它成为"主语列" */
.grammar-html-content .conj-table {
  border-collapse: collapse;
  width: 100%;
  margin: 0.5rem 0;
}
.grammar-html-content .conj-table th,
.grammar-html-content .conj-table td {
  border: 1px solid var(--border, #e5e7eb);
  padding: 0.5rem 0.6rem;
  text-align: left;
  vertical-align: middle;
  white-space: normal;  /* 默认 allow wrap,但 verb 列单独 override */
}
.grammar-html-content .conj-table th {
  background: rgba(0, 35, 149, 0.05);
  color: var(--accent, #002395);
  font-weight: 600;
  text-align: center;
}
/* verb 列 (第 1 列) 固定最小宽度, 强制不换行, 视觉上变成"主语列" */
.grammar-html-content .conj-table td.verb {
  min-width: 90px;
  white-space: nowrap;
  font-weight: 700;
  color: var(--accent, #002395);
  background: rgba(0, 35, 149, 0.05);
  border-right: 2px solid var(--accent, #002395);
  text-align: left !important;
}
.grammar-html-content .conj-table thead th:first-child {
  min-width: 90px;
  background: rgba(0, 35, 149, 0.05);
}
/* 移动端: 允许 verb 列窄一点 (屏幕小), 但仍然不换行 */
@media (max-width: 600px) {
  .grammar-html-content .conj-table td.verb,
  .grammar-html-content .conj-table thead th:first-child {
    min-width: 60px;
  }
}



/* bugfix 2026-10-16 Lucienne: markdown 正文换行
   原来用 white-space:pre-wrap + .replace(/\n/g,'<br>') 双重处理 → 行距翻倍
   现在只靠 CSS: 保留 \n (pre-wrap), \n\n (空行) 用 <p> 段落语义化渲染 */
/* bugfix 2026-10-16: parseMarkdown 现在已经把 \n 转成 <br>、\n\n 转成 </p><p>,
   所以这里不能再用 pre-wrap (会跟 <br> 双重换行 → 行距翻倍) */
.md-body {
  line-height: 1.75;
  word-wrap: break-word;
  white-space: normal;
}
/* bugfix 2026-10-16 Lucienne: 段落间距完全由 parseMarkdown 输出的 <br> 表达,
   这里**不能再给 ol/ul/p 加 margin** ——
   否则 parseMarkdown 里的 zeroMargin 归零了 inline margin, 这条 CSS 又把 margin 加回来,
   导致源码没空行的地方凭空多出空隙 (源码有空行的地方反而正常)。
   规则: 间距只有一个来源 = <br>。CSS 这边一律不加 margin。 */
.md-body > p:first-child { margin-top: 0; }
.md-body > p:last-child { margin-bottom: 0; }
.md-body ol, .md-body ul { margin: 0; }


/* bugfix 2026-10-16 Lucienne: 批改 modal 录音列表紧凑化
   全局 `audio { height: 40px; border; box-shadow; background }` 会让每条录音又高又花,
   这里在 #rec-list 内彻底去掉装饰并压到 30px, 3 条横排一行。 */
#rec-list { display: flex; }
#rec-list audio {
  height: 30px !important;
  min-height: 30px !important;
  border: none !important;
  box-shadow: none !important;
  background: transparent !important;
  border-radius: 0 !important;
  margin: 0 !important;
  vertical-align: middle;
}


/* bugfix 2026-10-16 Lucienne: 中文 markdown 加粗看不出来
   'Noto Sans Local' 的 unicode-range 不含中文, 中文走 fallback 字体,
   fallback 的 font-weight:700 合成加粗在浅色背景上几乎不可见 →
    Lucienne 以为 <strong> 没渲染 (实际标签一直在 HTML 里)。
   这里显式声明 font-synthesis + 极轻 text-shadow 描边, 让中文加粗真的看得出。 */
.md-body strong,
.md-body b,
.text-material strong,
.text-material b,
.preview-body strong {
  font-weight: 700 !important;
  font-synthesis: weight;
  -webkit-font-smoothing: antialiased;
  text-shadow: 0 0 0.35px currentColor;
}
/* 同理 em / i 中文斜体也很弱, 给一点点横向偏移让它可辨 */
.md-body em,
.text-material em {
  font-synthesis: style;
  font-style: italic;
}


/* bugfix 2026-10-16 Lucienne: 列表序号改用 inline span (不再用 ol/li 块级结构)
   为什么不用 <ol>/<li>:
     <li> 是 display:flex → 在 <ol> 里撑出额外高度;
     <ol> 是块级 → 自己已换行, 再叠 <br> 就是两次换行。
     两者叠加导致"该有空行的地方没空, 不该有的地方凭空多一行" (反复踩坑)。
   现在列表项是普通行内内容, 换行完全由 <br> 控制, 高度问题从根上消失。 */
.md-num {
  font-weight: 700;
  font-size: 1.15em;
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1.5px;
  font-variant-numeric: tabular-nums;
  margin-right: 0.4rem;
}
.md-bullet {
  margin-right: 0.4rem;
  color: var(--text);
}
/* 行内序号后跟内容时, 内容不能紧贴序号 */
.md-num + span, .md-bullet + span { padding-left: 0.05rem; }
