/* 课程学习平台 · 全局样式（app.css）
 * 设计令牌唯一来源；页面级样式见 pages.css（学习端）/ admin.css（管理端）。
 * 约束：浅色、单主色（青蓝）、无渐变无图标字体；顶栏 + 横向导航 + 1180 栅格。
 * 所有组件只用 CSS/SVG 实现，不引入外部字体与图标库。
 *
 * 分节：1 令牌 2 基础/可达性 3 顶栏 4 导航 5 布局 6 卡片 7 按钮/标签 8 学习页/播放器
 *       9 测验 10 表单/表格 11 提示/统计/对话 12 骨架/空态/错误/模态/toast 13 登录
 *       14 登录态辅助 15 响应式（1199/1023/767/480）
 */
:root{
  /* 颜色（对比度与 设计规范 v2.0 §2.1 一致，均 ≥4.5:1） */
  --c-bg:#f5f7f9;
  --c-card:#fff;
  --c-line:#e4e9ee;
  --c-line2:#eef2f6;
  --c-ink:#1e2a36;
  --c-ink2:#55636f;
  --c-ink3:#5f6b77;
  --c-ink4:#8794a1;
  --c-primary:#2f7d8f;
  --c-primary-d:#26697a;
  --c-primary-l:#e7f2f5;
  --c-on-primary:#fff;
  --c-accent:#26705f;
  --c-accent-l:#e8f5f2;
  --c-ok:#2c7550;
  --c-ok-l:#e9f5ee;
  --c-warn:#8f5a1a;
  --c-warn-l:#fbf1e3;
  --c-danger:#b3453f;
  --c-danger-l:#fbeeee;
  --c-mask:rgba(15,26,32,.45);

  /* 字号 6 档 */
  --fs-xs:11px;
  --fs-sm:12px;
  --fs-md:13px;
  --fs-lg:14px;
  --fs-xl:16px;
  --fs-xxl:20px;

  /* 间距 / 圆角 / 阴影 / 命中区 */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px;
  --radius:10px; --radius-s:8px;
  --shadow:0 1px 2px rgba(30,42,54,.04), 0 4px 14px rgba(30,42,54,.05);
  --shadow-lg:0 10px 34px rgba(30,42,54,.09);
  --tap:44px;
  --wrap:1180px;

  /* 兼容别名：存量页面/组件使用旧命名，统一映射到新令牌 */
  --bg:var(--c-bg); --card:var(--c-card); --line:var(--c-line); --line2:var(--c-line2);
  --ink:var(--c-ink); --ink2:var(--c-ink2); --ink3:var(--c-ink3);
  --primary:var(--c-primary); --primary-d:var(--c-primary-d); --primary-l:var(--c-primary-l);
  --accent:var(--c-accent); --accent-l:var(--c-accent-l);
  --warn:var(--c-warn); --warn-l:var(--c-warn-l);
  --ok:var(--c-ok); --ok-l:var(--c-ok-l);
  --danger:var(--c-danger); --danger-l:var(--c-danger-l);
}

/* ---------- 1. 基础 ---------- */
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  font-family:-apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",system-ui,sans-serif;
  background:var(--c-bg); color:var(--c-ink); font-size:var(--fs-lg); line-height:1.65;
  -webkit-font-smoothing:antialiased;
}
#app{display:flex; flex-direction:column; min-height:100vh}
a{color:var(--c-primary); text-decoration:none}
a:hover{color:var(--c-primary-d)}
h1,h2,h3,h4{line-height:1.35}
img,svg{max-width:100%}
code{font-family:"Cascadia Mono",Consolas,monospace; font-size:var(--fs-sm); background:var(--c-line2);
  padding:1px 5px; border-radius:4px; color:#2b4a54}
button{font-family:inherit}
p,li,dd,td{overflow-wrap:anywhere}

/* 可达性基础：键盘焦点环、skip link、屏幕阅读器文本、减少动效 */
:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:2px; border-radius:6px}
.main:focus{outline:none}
.sr-only{position:absolute !important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0,0,0,0); white-space:nowrap; border:0}
.skip-link{position:absolute; left:-9999px; top:8px; z-index:100; background:var(--c-primary); color:var(--c-on-primary);
  padding:10px 16px; border-radius:8px; font-size:var(--fs-md); text-decoration:none}
.skip-link:focus, .skip-link:focus-visible{left:8px; color:var(--c-on-primary); outline:2px solid var(--c-primary-d); outline-offset:3px}
.boot{padding:64px 24px; text-align:center; color:var(--c-ink3); font-size:var(--fs-lg)}
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important}
}

/* ---------- 2. 顶栏 ---------- */
.hd{background:var(--c-card); border-bottom:1px solid var(--c-line); position:sticky; top:0; z-index:60}
.hd-in{max-width:var(--wrap); margin:0 auto; height:58px; padding:0 24px; display:flex; align-items:center; gap:18px}
/* 命中区：logo 链接实测 23px 高（差 1px 不达标）；纵向 padding 扩到 35px，负 margin 抵消，顶栏高度不变 */
.logo{display:flex; align-items:baseline; gap:8px; font-size:var(--fs-xl); font-weight:700; letter-spacing:.2px;
  color:var(--c-ink); white-space:nowrap; cursor:pointer; padding:6px 0; margin:-6px 0}
.logo:hover{color:var(--c-ink)}
.logo b{color:var(--c-primary); font-weight:700}
.logo small{font-size:var(--fs-xs); color:var(--c-ink3); font-weight:400; letter-spacing:0}
.hd-search{flex:1; max-width:360px; display:flex; align-items:center; gap:8px;
  height:36px; border:1px solid var(--c-line); border-radius:18px; background:var(--c-bg); padding:0 14px}
.hd-search:focus-within{border-color:var(--c-primary-d); box-shadow:0 0 0 3px rgba(47,125,143,.18)}
/* 命中区：输入框本身只有 15px 高，靠外层胶囊「看起来」可点；撑满 36px 胶囊高度，
   点胶囊任意位置都能聚焦（视觉不变，因为输入框无边框/背景）。 */
.hd-search input{border:0; background:transparent; outline:none; flex:1; font-size:var(--fs-md); color:var(--c-ink); min-width:0;
  height:100%; align-self:stretch}
.hd-search input:focus-visible{outline:none}
.hd-search input::placeholder{color:var(--c-ink3)}
/* 命中区：原 padding:0 让「搜索」按钮只有 24×16，鼠标要点准字，触屏基本点不中。
   胶囊高 36，按钮撑满高度即可，不改变外观。 */
.hd-search button{border:0; background:transparent; color:var(--c-ink3); cursor:pointer; font-size:var(--fs-sm);
  padding:0 2px; min-height:36px; min-width:32px; display:inline-flex; align-items:center; justify-content:center}
.hd-search button:hover{color:var(--c-primary)}
.hd-search-cancel{display:none !important; color:var(--c-primary) !important}
.hd-search-toggle{display:none; border:1px solid var(--c-line); background:var(--c-card); color:var(--c-ink2);
  width:38px; height:38px; border-radius:9px; cursor:pointer; align-items:center; justify-content:center}
.hd-search-toggle:hover{border-color:var(--c-primary); color:var(--c-primary)}
.hd-search-toggle:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:2px}
.nav-toggle{display:none; border:1px solid var(--c-line); background:var(--c-card); color:var(--c-ink2);
  width:38px; height:38px; border-radius:9px; cursor:pointer; flex-direction:column; align-items:center; justify-content:center; gap:4px}
.nav-toggle span{display:block; width:16px; height:2px; border-radius:1px; background:currentColor}
.nav-toggle:hover{border-color:var(--c-primary); color:var(--c-primary)}
.hd-right{margin-left:auto; display:flex; align-items:center; gap:12px; position:relative}
.hd-user{display:flex; align-items:center; position:relative}
.hd-user-btn{display:flex; align-items:center; gap:9px; border:0; background:transparent; padding:3px 4px;
  border-radius:8px; cursor:pointer; color:inherit; font-family:inherit; text-align:left}
.hd-user-btn:hover{background:var(--c-bg)}
.avatar{width:30px; height:30px; border-radius:50%; background:var(--c-primary-l); color:var(--c-primary);
  display:grid; place-items:center; font-weight:600; font-size:var(--fs-md); flex:none}
.u-txt{display:flex; flex-direction:column; line-height:1.25}
.u-txt .nm{font-weight:600; color:var(--c-ink); font-size:var(--fs-md)}
.u-txt .rl{font-size:var(--fs-xs); color:var(--c-ink3)}
.user-menu{position:absolute; right:0; top:calc(100% + 6px); min-width:148px; background:var(--c-card);
  border:1px solid var(--c-line); border-radius:10px; box-shadow:var(--shadow-lg); padding:6px; z-index:70}
.user-menu a,.user-menu button{display:block; width:100%; text-align:left; border:0; background:transparent;
  color:var(--c-ink2); font-size:var(--fs-md); padding:8px 12px; border-radius:6px; cursor:pointer}
.user-menu a:hover,.user-menu button:hover{background:var(--c-primary-l); color:var(--c-primary)}
.linkbtn{border:0; background:transparent; color:var(--c-ink2); font-size:var(--fs-sm); cursor:pointer;
  padding:6px 8px; border-radius:6px; min-height:32px}
.linkbtn:hover{background:var(--c-bg); color:var(--c-primary)}
.btn-login{border:1px solid var(--c-primary); color:var(--c-primary); background:var(--c-card);
  border-radius:7px; padding:6px 16px; font-size:var(--fs-md); cursor:pointer; min-height:34px;
  display:inline-flex; align-items:center}
.btn-login:hover{background:var(--c-primary-l); color:var(--c-primary-d)}

/* ---------- 3. 主导航 ---------- */
.nav{background:var(--c-card); border-bottom:1px solid var(--c-line)}
.nav-in{max-width:var(--wrap); margin:0 auto; padding:0 24px; display:flex; gap:2px; overflow-x:auto;
  scrollbar-width:none; -webkit-overflow-scrolling:touch}
.nav-in::-webkit-scrollbar{display:none}
.nav a{position:relative; padding:11px 15px; font-size:var(--fs-md); color:var(--c-ink2); white-space:nowrap;
  display:inline-flex; align-items:center; min-height:44px}
.nav a:hover{color:var(--c-ink)}
.nav a.on{color:var(--c-primary); font-weight:600}
.nav a.on::after{content:""; position:absolute; left:15px; right:15px; bottom:-1px; height:2px; background:var(--c-primary)}

/* ---------- 4. 布局 ---------- */
.wrap{max-width:var(--wrap); margin:0 auto; padding:24px 24px 64px; width:100%}
.auth-wrap{width:100%; padding:0}
.main{flex:1 0 auto}
.sec{margin-bottom:22px}
.sec-h{display:flex; align-items:baseline; gap:10px; margin-bottom:12px; flex-wrap:wrap}
.sec-h h1,.sec-h h2{font-size:var(--fs-xl); font-weight:600}
.sec-h .note{font-size:var(--fs-sm); color:var(--c-ink3)}
/* 命中区：文字链只有 20px 高。用负 margin 抵消 padding，行高不变但可点区域到 32px */
.sec-h .more{margin-left:auto; font-size:var(--fs-sm); display:inline-flex; align-items:center;
  min-height:32px; padding:0 2px; margin-block:-6px}
.card{background:var(--c-card); border:1px solid var(--c-line); border-radius:var(--radius);
  box-shadow:var(--shadow); padding:18px 20px; min-width:0}
.card+.card{margin-top:14px}
.card-title{font-size:var(--fs-xl); font-weight:600; margin-bottom:3px}
.card-sub{font-size:var(--fs-sm); color:var(--c-ink3); margin-bottom:14px}
.grid{display:grid; gap:14px; grid-template-columns:minmax(0,1fr)}
.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.g3{grid-template-columns:repeat(3,minmax(0,1fr))}
.g4{grid-template-columns:repeat(4,minmax(0,1fr))}
.row{display:flex; align-items:center; gap:10px}
.between{justify-content:space-between}
.wraprow{flex-wrap:wrap}
.spacer{flex:1}
.muted{color:var(--c-ink3); font-size:var(--fs-sm)}
.empty{padding:26px 4px; text-align:center; color:var(--c-ink3); font-size:var(--fs-md)}
.ft{border-top:1px solid var(--c-line); background:var(--c-card); padding:18px 24px}
.ft-in{max-width:var(--wrap); margin:0 auto; display:flex; gap:8px; flex-wrap:wrap; align-items:center;
  font-size:var(--fs-sm); color:var(--c-ink3)}
.ft-dot{color:var(--c-line)}

/* ---------- 5. 课程卡 / 推荐卡 ---------- */
.ccard{display:flex; flex-direction:column; gap:9px; background:var(--c-card); border:1px solid var(--c-line);
  border-radius:var(--radius); box-shadow:var(--shadow); padding:16px; cursor:pointer; transition:.15s;
  color:inherit; text-decoration:none; min-height:236px}
.ccard:hover, .ccard:visited{color:inherit; text-decoration:none}
.ccard:hover{border-color:#c9dee4; box-shadow:0 2px 4px rgba(30,42,54,.05),0 8px 20px rgba(30,42,54,.07);
  transform:translateY(-1px)}
/* task-22：封面色块改为「窄条 + 一级分类全名」——40px 窄条不再吃 1/3 卡高；
   文本为完整一级分类名（最长 4 字，容器够），超出由 ellipsis 兜底 */
/* 宽度不再交给 flex 拉伸：1440 下 .ccard 是纵向 flex，整条会铺满 334px，
   一个四字分类名居中的浅色横条看起来像占位横幅；375 下同一组件却是左侧 72×40 小色块，
   同一个组件两套观感。这里统一成「贴左的胶囊 chip」，两种宽度观感一致。 */
.cover{height:40px; border-radius:var(--radius-s); background:var(--c-primary-l); color:var(--c-primary);
  display:flex; align-items:center; justify-content:center; padding:0 12px;
  font-size:var(--fs-sm); font-weight:600; letter-spacing:0; white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis; flex:none; align-self:flex-start;
  width:fit-content; min-width:56px; max-width:100%}
.cover-c0{background:#e7f2f5; color:#26697a}
.cover-c1{background:#e8f5f2; color:#26705f}
.cover-c2{background:#fbf1e3; color:#8f5a1a}
.cover-c3{background:#f3eef9; color:#5b4b8a}
.cover-c4{background:#eaf3fb; color:#2d5f8a}
.cover-c5{background:#fbeeee; color:#a04a44}
.ccard h3,.ccard .ccard-t{font-size:var(--fs-lg); font-weight:600; line-height:1.45; min-height:2.9em;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden}
.ccard .meta,.ccard-meta{font-size:var(--fs-sm); color:var(--c-ink3)}
.ccard-foot{margin-top:auto; display:flex; flex-wrap:wrap; gap:6px; align-items:center}
.why{background:var(--c-accent-l); border-radius:var(--radius-s); padding:8px 10px;
  font-size:var(--fs-sm); color:var(--c-accent); line-height:1.55}
.why b{font-weight:600}
.scoreline{font-size:var(--fs-sm); color:var(--c-ink3); border-top:1px dashed var(--c-line); padding-top:8px}
.scoreline b{color:var(--c-ink2); font-weight:600}

/* ---------- 6. 按钮 / 标签 / 进度 ---------- */
.btn{display:inline-flex; align-items:center; justify-content:center; gap:6px; border:0; border-radius:7px;
  background:var(--c-primary); color:var(--c-on-primary); font-size:var(--fs-md); min-height:36px;
  padding:8px 16px; cursor:pointer; transition:.15s; white-space:nowrap; text-align:center}
.btn:hover{background:var(--c-primary-d); color:var(--c-on-primary)}
.btn:disabled{background:var(--c-line); color:var(--c-ink3); cursor:not-allowed}
.btn-primary{background:var(--c-primary); color:var(--c-on-primary)}
.btn-ghost,.btn.ghost{background:var(--c-primary-l); color:var(--c-primary)}
.btn-ghost:hover,.btn.ghost:hover{background:#d9ebef; color:var(--c-primary-d)}
.btn.plain{background:var(--c-card); border:1px solid var(--c-line); color:var(--c-ink2)}
.btn.plain:hover{border-color:var(--c-ink3); color:var(--c-ink)}
/* 命中区：设计规范 §4.3「按钮高 36（主）/32（次）」。.sm 原为 30，低于规范下限，
   且与 .btn.quiet 不一致（同一行里两个次要按钮高度不同）。统一到 32。 */
.btn.sm{min-height:32px; padding:5px 12px; font-size:var(--fs-sm)}
.btn.danger{background:var(--c-card); border:1px solid var(--c-danger-l); color:var(--c-danger)}
.btn.danger:hover{background:var(--c-danger-l); color:var(--c-danger)}
.btn-danger{background:var(--c-danger); color:#fff}
.btn-danger:hover{background:#9c3a35; color:#fff}
/* 命中区：文字链只有 16px 高，用上下 padding 撑到 26px，再负 margin 抵消，布局不动 */
.btn-text{background:transparent; border:0; color:inherit; padding:5px 2px; margin:-5px 0; min-height:0;
  text-decoration:underline; cursor:pointer; font-size:inherit; font-family:inherit}
/* 低权重操作（退出学习/取消加入等破坏性或次要动作）：不抢主操作视觉，悬停才转危险色 */
/* 命中区：规范次要按钮 32。原 30 与 .btn.sm 并排时高度不齐。 */
.btn.quiet{background:transparent; border:1px solid transparent; color:var(--c-ink3); min-height:32px;
  padding:5px 10px; font-size:var(--fs-sm)}
.btn.quiet:hover{background:var(--c-danger-l); border-color:var(--c-danger-l); color:var(--c-danger)}
.tag{display:inline-block; font-size:var(--fs-xs); padding:2px 8px; border-radius:6px;
  background:var(--c-primary-l); color:var(--c-primary-d); margin-right:6px}
.tag.gray{background:var(--c-bg); color:var(--c-ink2)}
.tag.ok{background:var(--c-ok-l); color:var(--c-ok)}
.tag.warn{background:var(--c-warn-l); color:var(--c-warn)}
.tag.danger{background:var(--c-danger-l); color:var(--c-danger)}
.bar{height:6px; border-radius:3px; background:var(--c-line); overflow:hidden}
.bar>i{display:block; height:100%; background:var(--c-primary); border-radius:3px; transition:width .3s}
.bar>i.ok{background:var(--c-ok)}
.bar>i.warn{background:var(--c-warn)}
.bar>i.danger{background:var(--c-danger)}
.meter{display:flex; align-items:center; gap:10px; margin-bottom:9px}
.meter .nm{width:96px; font-size:var(--fs-md); flex:none; color:var(--c-ink2)}
.meter .bar{flex:1}
.meter .val{width:44px; text-align:right; font-size:var(--fs-sm); color:var(--c-ink2); flex:none}

/* ---------- 7. 学习页 / 播放器 ---------- */
.study{display:grid; grid-template-columns:236px minmax(0,1fr) 260px; gap:16px; align-items:start}
.study>*{min-width:0}
.tree{background:var(--c-card); border:1px solid var(--c-line); border-radius:var(--radius);
  box-shadow:var(--shadow); padding:14px 8px; font-size:var(--fs-md)}
.tree .hd-row{padding:0 8px 8px; font-size:var(--fs-sm); color:var(--c-ink3); display:flex; justify-content:space-between}
.tree .ch{padding:7px 8px 3px; color:var(--c-ink2); font-weight:600; font-size:var(--fs-sm)}
.tree .it{padding:6px 8px 6px 14px; border-radius:6px; cursor:pointer; color:var(--c-ink2);
  display:flex; gap:8px; align-items:flex-start; width:100%; border:0; background:transparent; text-align:left; font:inherit}
.tree .it:hover{background:var(--c-bg)}
.tree .it.on{background:var(--c-primary-l); color:var(--c-primary); font-weight:600}
.tree .it:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:-2px}
.tree .it .mk{width:12px; flex:none; color:var(--c-ok); font-size:var(--fs-xs); line-height:1.9}
.tree .it .nm{flex:1}
.tree .it .du{font-size:var(--fs-xs); color:var(--c-ink3); flex:none}
/* 目录折叠开关：桌面端目录常驻，开关只在窄屏出现（见下方 767 断点） */
.tree-toggle{display:none; width:100%; align-items:center; justify-content:space-between; gap:8px;
  padding:7px 8px; margin:0 0 2px; border:0; background:transparent; font:inherit; color:var(--c-primary);
  font-size:var(--fs-sm); cursor:pointer; border-radius:6px}
.tree-toggle:hover{background:var(--c-primary-l)}
.tree-toggle:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:-2px}
.tree-toggle .tv{color:var(--c-ink3)}
/* 窄屏目录收起时显示当前小节名，保证「我在哪一节」始终可见；桌面端目录常驻，不显示 */
.tree-cur{display:none; padding:0 8px 4px; font-size:var(--fs-sm); color:var(--c-ink2);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.player{position:relative; background:#0f1a20; border-radius:var(--radius-s); aspect-ratio:16/9;
  display:grid; place-items:center; color:#93aab6; overflow:hidden; min-height:0}
/* 播放器：vplayer 绝对定位铺满 .player（位置必须以本条为准，旧版缺 } 导致失效的重复块已删除） */
.player .vplayer{position:absolute; inset:0; width:100%; height:100%; object-fit:contain;
  background:#111b21; border-radius:var(--radius-s)}
/* 学习页把 <video> 直接挂在 <main class="card"> 下（没有 .player 包裹），
 * 上面的 .player .vplayer 永远选不中它，视频会按原生 1920×1080 渲染并把整页撑破。
 * 这里补一条直属子元素的约束：宽度吃满卡片、16:9、超高时封顶。 */
.card > .vplayer{display:block; width:100%; height:auto; aspect-ratio:16/9;
  max-height:min(58vh, 520px); background:#111b21; object-fit:contain;
  border-radius:var(--radius-s); margin:0 0 10px}
.player .pt{position:absolute; left:16px; bottom:12px; font-size:var(--fs-sm); color:#c3d2da; z-index:2}
.player .ttl{position:absolute; left:16px; top:12px; font-size:var(--fs-sm); color:#c3d2da; z-index:2; right:100px}
.player .play{width:56px; height:56px; border-radius:50%; background:rgba(255,255,255,.13);
  display:grid; place-items:center; cursor:pointer; transition:.15s; border:0}
.player .play:hover{background:rgba(255,255,255,.22)}
.player .play::after{content:""; width:0; height:0; margin-left:5px;
  border-left:16px solid #fff; border-top:10px solid transparent; border-bottom:10px solid transparent}
.player .tip{position:absolute; right:14px; top:12px; font-size:var(--fs-xs); color:#93aab6;
  border:1px solid #3b4d57; border-radius:5px; padding:1px 7px; z-index:2}
.player .video-tip{position:absolute; inset:0; display:grid; place-items:center; text-align:center;
  padding:24px; font-size:var(--fs-md); color:#c3d2da; z-index:3; background:rgba(15,26,32,.55); line-height:1.8}
.resume-bar{background:var(--c-primary-l); color:var(--c-primary-d); border-radius:var(--radius-s);
  padding:9px 13px; font-size:var(--fs-sm); margin-bottom:12px; display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.resume-bar .btn{min-height:30px; padding:4px 12px; font-size:var(--fs-sm)}
/* 窄屏（<=480）：文案独占一行，三个操作并排，避免「继续上次位置」被挤到第二行中间、
   与「关闭提示」错位堆叠（375 宽度实测缺陷）。 */
@media (max-width:480px){
  .resume-bar{gap:8px 10px; padding:10px 12px}
  .resume-bar .resume-text{flex:1 0 100%; line-height:1.6; word-break:break-word}
  .resume-bar .btn{flex:1 1 0; justify-content:center; min-width:0; white-space:nowrap}
  .resume-bar .btn-text{flex:0 0 auto; margin-left:auto}
}
/* 行长（measure）：详情页富文本区 1440 下实测 1090px、每行 78 字，超出中文舒适阅读区。
   限到 44em（≈616px / 44 字）；学习页正文本来只有 562px，此限制不生效。 */
.body-text{font-size:var(--fs-lg); color:#31424e; line-height:1.85; max-width:44em}
.body-text h3,.body-text h2{font-size:var(--fs-xl); margin:16px 0 6px; color:var(--c-ink)}
.body-text p{margin:8px 0}
.body-text ul,.body-text ol{margin:8px 0 8px 20px}
.body-text li{margin:3px 0}
.body-text pre{background:#f3f6f8; border:1px solid var(--c-line); border-radius:8px;
  padding:10px 12px; overflow:auto; font-size:var(--fs-sm); margin:10px 0}
.aside-item{padding:10px 0; border-bottom:1px dashed var(--c-line); font-size:var(--fs-md)}
.aside-item:last-child{border-bottom:0}
.aside-item .k{font-size:var(--fs-xs); color:var(--c-ink3); margin-bottom:2px}
.aside-item .act{font-size:var(--fs-sm); color:var(--c-primary); cursor:pointer}
.lesson-nav{display:flex; gap:10px; margin-top:14px; flex-wrap:wrap}
.progress-line{display:flex; align-items:center; gap:12px; margin-bottom:14px; font-size:var(--fs-sm); color:var(--c-ink3); flex-wrap:wrap}
.progress-line .bar{width:180px; max-width:60vw}

/* ---------- 8. 测验 ---------- */
.quizwrap{max-width:760px; margin:0 auto}
.qhead{display:flex; align-items:center; gap:14px; margin-bottom:14px; flex-wrap:wrap}
.qhead .qidx{font-size:var(--fs-md); color:var(--c-ink2)}
.qdots{display:flex; gap:6px; margin-left:auto; flex-wrap:wrap}
.qdots i{width:22px; height:4px; border-radius:2px; background:var(--c-line); display:block}
.qdots i.done{background:var(--c-primary)}
.qdots i.cur{background:var(--c-accent)}
.qstem{font-size:var(--fs-xl); line-height:1.6; margin-bottom:16px; color:var(--c-ink)}
.qstem .score{font-size:var(--fs-xs); color:var(--c-ink3); margin-left:8px; white-space:nowrap}
.q-opt{display:flex; gap:11px; align-items:flex-start; padding:11px 13px; border:1px solid var(--c-line);
  border-radius:9px; margin-bottom:9px; cursor:pointer; background:var(--c-card); transition:.12s}
.q-opt:hover{border-color:#bcd6dd; background:#fbfdfe}
.q-opt:focus-within{border-color:var(--c-primary-d); box-shadow:0 0 0 3px rgba(47,125,143,.14)}
.q-opt .radio{width:17px; height:17px; border-radius:50%; border:1.5px solid var(--c-ink3); flex:none; margin-top:2px;
  display:grid; place-items:center; font-size:var(--fs-xs); color:#fff}
.q-opt .radio.box{border-radius:4px}
.q-opt.sel{border-color:var(--c-primary); background:var(--c-primary-l)}
.q-opt.sel .radio{border-color:var(--c-primary); background:var(--c-primary)}
.q-opt.sel .radio.box::after{content:"✓"; font-size:var(--fs-xs); color:#fff; line-height:1}
.q-opt.right{border-color:var(--c-ok); background:var(--c-ok-l)}
.q-opt.right .radio{border-color:var(--c-ok); background:var(--c-ok)}
.q-opt.wrong{border-color:var(--c-danger); background:var(--c-danger-l)}
.q-opt.wrong .radio{border-color:var(--c-danger); background:var(--c-danger)}
.q-opt .txt{flex:1; font-size:var(--fs-md)}
.q-opt .key{font-weight:600; color:var(--c-ink2); margin-right:4px}
.qfoot{display:flex; align-items:center; gap:10px; margin-top:18px; flex-wrap:wrap}
.qfoot .hint{font-size:var(--fs-sm); color:var(--c-ink3)}
.qcard{background:var(--c-card); border:1px solid var(--c-line); border-radius:var(--radius); box-shadow:var(--shadow); padding:22px 22px 18px}
.res-head{display:flex; align-items:flex-end; gap:18px; margin-bottom:6px; flex-wrap:wrap}
.res-head .n{font-size:30px; font-weight:700; color:var(--c-primary); line-height:1.1}
.res-head .t{font-size:var(--fs-sm); color:var(--c-ink3)}
.res-list{margin-top:14px}
.res-item{border-top:1px solid var(--c-line); padding:12px 0}
.res-item .ttl{display:flex; gap:8px; align-items:baseline; font-size:var(--fs-md); flex-wrap:wrap}
.res-item .det{font-size:var(--fs-sm); color:var(--c-ink2); margin-top:4px}
.res-item .ana{font-size:var(--fs-sm); color:var(--c-ink2); margin-top:5px; background:var(--c-bg);
  border-radius:6px; padding:8px 10px; line-height:1.6}

/* ---------- 9. 表单 / 表格 ---------- */
.field{margin-bottom:14px; position:relative}
.field label{display:block; font-size:var(--fs-sm); color:var(--c-ink2); margin-bottom:6px; font-weight:500}
.field .req{color:var(--c-danger)}
.input,.select,.textarea{width:100%; border:1px solid var(--c-line); border-radius:var(--radius-s);
  padding:0 12px; height:36px; font-family:inherit; font-size:var(--fs-md); color:var(--c-ink);
  background:var(--c-card); outline:none}
.input:focus,.select:focus,.textarea:focus{border-color:var(--c-primary); box-shadow:0 0 0 3px rgba(47,125,143,.14)}
.input[aria-invalid="true"],.select[aria-invalid="true"],.textarea[aria-invalid="true"]{
  border-color:var(--c-danger); box-shadow:0 0 0 3px rgba(179,69,63,.1)}
.textarea{height:auto; min-height:72px; padding:9px 12px; line-height:1.7; resize:vertical}
.select{cursor:pointer; appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--c-ink3) 50%),linear-gradient(135deg,var(--c-ink3) 50%,transparent 50%);
  background-position:calc(100% - 16px) 16px,calc(100% - 11px) 16px; background-size:5px 5px,5px 5px; background-repeat:no-repeat}
.field-error,.field-err{color:var(--c-danger); font-size:var(--fs-sm); margin-top:4px; line-height:1.5}
.chip{border:1px solid var(--c-line); background:var(--c-card); border-radius:15px; padding:5px 13px;
  font-size:var(--fs-sm); color:var(--c-ink2); cursor:pointer; margin:0 6px 6px 0; transition:.15s; min-height:32px}
.chip:hover{border-color:var(--c-primary); color:var(--c-primary)}
.chip.on{background:var(--c-primary-l); border-color:var(--c-primary); color:var(--c-primary-d); font-weight:600}
.chip:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:2px}
.tablewrap{width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch; border-radius:8px}
table{width:100%; border-collapse:collapse; font-size:var(--fs-md)}
caption{text-align:left}
th{text-align:left; color:var(--c-ink3); font-weight:500; padding:8px 10px; border-bottom:1px solid var(--c-line);
  white-space:nowrap; font-size:var(--fs-sm)}
td{padding:9px 10px; border-bottom:1px solid var(--c-line2); color:var(--c-ink2); vertical-align:top}
tbody tr:hover td{background:#fafcfd}
.stars{display:flex; gap:3px; margin:4px 0 12px}
/* 命中区：评分星星是纯图标按钮，规范要求 36×36；原 30×30 相邻星之间只剩 3px 空隙容易点错。
   32 是次要按钮下限，再大五颗星会在 375 下换行（窄屏另有 44 的规则）。 */
.star{border:0; background:transparent; font-size:22px; line-height:1; color:var(--c-line); cursor:pointer;
  padding:0; min-width:32px; min-height:32px}
.star.on{color:#b98223}
.star:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:1px; border-radius:4px}

/* ---------- 10. 步骤 / 提示 / 统计 / 对话 ---------- */
.steps{display:flex; gap:6px; margin-bottom:18px; flex-wrap:wrap}
.steps div{font-size:var(--fs-sm); color:var(--c-ink3); padding:6px 12px; border-radius:7px; background:var(--c-bg)}
.steps div.on{background:var(--c-primary-l); color:var(--c-primary-d); font-weight:600}
.steps div.done{color:var(--c-ok); background:var(--c-ok-l)}
.hintbar{background:var(--c-primary-l); color:var(--c-primary-d); border-radius:var(--radius-s);
  padding:9px 13px; font-size:var(--fs-sm); margin-bottom:14px}
.warnbar{background:var(--c-warn-l); color:var(--c-warn); border-radius:var(--radius-s);
  padding:9px 13px; font-size:var(--fs-sm); margin-bottom:14px}
.stat{background:var(--c-card); border:1px solid var(--c-line); border-radius:var(--radius);
  box-shadow:var(--shadow); padding:14px 16px}
.stat .n{font-size:22px; font-weight:700; color:var(--c-primary); line-height:1.3}
.stat .l{font-size:var(--fs-sm); color:var(--c-ink3)}
.chat{display:flex; flex-direction:column; height:calc(100vh - 220px); min-height:420px}
.chat-body{flex:1; overflow:auto; padding:4px 2px 8px; display:flex; flex-direction:column}
/* 消息少时第一条气泡不要孤零零吊在顶部、下面留 400px 空白：
 * 给首个子元素 margin-top:auto，把内容压到输入框上方（符合聊天软件习惯）；
 * 内容超高时 auto 自动退化为 0，不会把顶部消息顶出可视区。 */
.chat-body > *:first-child{margin-top:auto}
/* 空会话（只有一条欢迎语）时不撑满视口：整块收到「欢迎语 + 快捷问句 + 输入框」的自然高度，
 * 否则 900px 高的空聊天框里只有一句问候，首屏像坏掉的页面。发出第一条消息后自动恢复满高。 */
.chat.chat-empty{height:auto; min-height:0}
.chat.chat-empty .chat-body{flex:0 0 auto; overflow:visible; justify-content:flex-start}
/* 桌面端反例：空会话若也收成自然高度（约 167px），右侧栏三张卡有 428px 高，
 * 左短右长差 250px，看起来像布局塌了。桌面宽屏有空间，恢复满高并把欢迎语垂直居中；
 * 窄屏（≤767px）仍保持上面的自然高度，避免手机上一屏只有一句问候。 */
@media (min-width: 768px){
  .chat.chat-empty{height:calc(100vh - 220px); min-height:420px}
  .chat.chat-empty .chat-body{flex:1; overflow:auto; justify-content:center}
  .chat.chat-empty .chat-body > *:first-child{margin-top:0}
}
.chat.chat-empty .chat-body > *:first-child{margin-top:0}
.chat.chat-empty .quick{margin-top:14px; margin-bottom:0}
.bubble{max-width:78%; padding:9px 13px; border-radius:12px; font-size:var(--fs-lg); line-height:1.7; margin-bottom:10px}
.bubble.user{background:var(--c-primary-l); color:var(--c-primary-d); margin-left:auto; border-bottom-right-radius:4px}
.bubble.bot{background:var(--c-bg); border:1px solid var(--c-line); border-bottom-left-radius:4px}
.bubble .agent{font-size:var(--fs-xs); color:var(--c-ink3); margin-bottom:4px}
.chat-input{display:flex; gap:10px; padding-top:12px; border-top:1px solid var(--c-line)}
.chat-input input{flex:1}
.quick{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:10px}
.pager{display:flex; align-items:center; gap:14px; margin-top:16px; justify-content:center; flex-wrap:wrap}
.pager a,.pager button{min-width:32px; min-height:32px}

/* ---------- 11. 登录 ---------- */
.auth{min-height:calc(100vh - 58px); display:grid; place-items:center; padding:40px 20px}
.auth-box{width:100%; max-width:412px; background:var(--c-card); border:1px solid var(--c-line);
  border-radius:14px; box-shadow:var(--shadow-lg); padding:28px 28px 24px}
.auth-box h1,.auth-box h2{font-size:var(--fs-xxl); font-weight:700; margin-bottom:4px}
.auth-box p{font-size:var(--fs-sm); color:var(--c-ink3); margin-bottom:20px}
.auth-tabs{display:flex; gap:6px; margin-bottom:18px; background:var(--c-bg); padding:4px; border-radius:9px}
.auth-tabs button{flex:1; border:1px solid transparent; background:transparent; color:var(--c-ink2);
  font-size:var(--fs-md); padding:7px 10px; border-radius:7px; cursor:pointer; min-height:34px}
.auth-tabs button[aria-checked="true"]{background:var(--c-card); border-color:var(--c-line);
  color:var(--c-primary-d); font-weight:600; box-shadow:0 1px 2px rgba(30,42,54,.06)}
.auth-tabs button:focus-visible{outline:2px solid var(--c-primary-d); outline-offset:1px}
.demo{display:flex; gap:8px; margin:14px 0 4px; flex-wrap:wrap}
.demo button{flex:1 1 0; border:1px solid var(--c-line); background:var(--c-bg); border-radius:7px;
  padding:7px 4px; font-size:var(--fs-sm); color:var(--c-ink2); cursor:pointer; min-height:34px}
.demo button:hover{border-color:var(--c-primary); color:var(--c-primary)}
.auth-alt{margin-top:14px; font-size:var(--fs-sm); color:var(--c-ink3); text-align:center}
.auth-alt button{margin-left:4px}

/* ---------- 12. 骨架 / 空态 / 内联错误 / 模态 / toast ---------- */
.sk-line{height:12px; border-radius:6px; margin-bottom:8px;
  background:linear-gradient(90deg,var(--c-line2) 25%,var(--c-bg) 37%,var(--c-line2) 63%);
  background-size:400% 100%; animation:sk 1.3s ease-in-out infinite}
.sk-line.short{width:58%}
.sk-line.title{height:16px; width:78%}
.sk-cover{height:92px; border-radius:8px; margin-bottom:10px;
  background:linear-gradient(90deg,var(--c-line2) 25%,var(--c-bg) 37%,var(--c-line2) 63%);
  background-size:400% 100%; animation:sk 1.3s ease-in-out infinite}
.sk-card{background:var(--c-card); border:1px solid var(--c-line); border-radius:var(--radius);
  padding:16px; box-shadow:var(--shadow)}
.sk-wrap{display:grid; gap:14px; grid-template-columns:repeat(auto-fill,minmax(220px,1fr))}
.sk-lines{padding:6px 0}
.sk-table{padding:2px 0}
.sk-tr{display:flex; gap:12px; padding:10px 6px; border-bottom:1px solid var(--c-line2)}
.sk-tr .sk-line{flex:1; margin:0}
.sk-tr .sk-line.short{flex:.6}
@keyframes sk{0%{background-position:100% 50%}100%{background-position:0 50%}}
.empty[data-sk]{padding:0; border:0; background:transparent; text-align:left}
.empty-state{max-width:440px; margin:8px auto; padding:40px 16px; display:flex; flex-direction:column;
  align-items:center; text-align:center; border:1px dashed var(--c-line); border-radius:12px; background:#fcfdfe}
.empty-state .es-icon{width:52px; height:52px; border-radius:50%; background:var(--c-primary-l); color:var(--c-primary);
  display:grid; place-items:center; margin-bottom:14px}
.empty-state.page-403 .es-icon,.empty-state.page-404 .es-icon{background:var(--c-warn-l); color:var(--c-warn)}
.empty-state .es-title{font-size:var(--fs-xl); font-weight:600; color:var(--c-ink); margin-bottom:6px}
.empty-state .es-desc{font-size:var(--fs-md); color:var(--c-ink3); line-height:1.7; margin-bottom:16px}
.empty-state .es-actions{display:flex; gap:10px; flex-wrap:wrap; justify-content:center}
.inline-alert{display:flex; align-items:center; gap:10px; background:var(--c-danger-l);
  border-left:3px solid var(--c-danger); border-radius:8px; padding:10px 12px; color:var(--c-danger);
  font-size:var(--fs-md); line-height:1.6; margin:12px 0}
.inline-alert-text{flex:1}
.modal-mask{position:fixed; inset:0; background:var(--c-mask); z-index:300; display:grid;
  place-items:center; padding:20px; overflow:auto}
.modal{width:100%; max-width:420px; background:var(--c-card); border-radius:14px;
  box-shadow:0 24px 60px rgba(16,26,34,.3); padding:22px; max-height:calc(100vh - 40px); overflow:auto}
.modal-title{font-size:var(--fs-xl); color:var(--c-ink); margin-bottom:8px}
.modal-body{font-size:var(--fs-md); color:var(--c-ink2); line-height:1.75}
.modal-impact{background:var(--c-warn-l); color:var(--c-warn); border-radius:8px; padding:9px 12px;
  margin-top:12px; font-size:var(--fs-sm); line-height:1.7}
.modal-req{margin-top:14px}
.modal-req label{display:block; font-size:var(--fs-sm); color:var(--c-ink3); margin-bottom:6px}
.modal-actions{display:flex; justify-content:flex-end; gap:10px; margin-top:18px; flex-wrap:wrap}
.modal-actions .btn{min-width:84px}
.toast{position:fixed; left:50%; bottom:24px; transform:translate(-50%,14px); background:#22323d; color:#fff;
  padding:10px 10px 10px 16px; border-radius:8px; font-size:var(--fs-md); opacity:0; pointer-events:none;
  transition:.2s; z-index:200; max-width:92vw; display:flex; align-items:center; gap:8px;
  box-shadow:0 8px 24px rgba(16,26,34,.24)}
.toast.show{opacity:1; transform:translate(-50%,0); pointer-events:auto}
.toast.err{background:#8f3a35}
.toast-msg{flex:1; min-width:0}
.toast-x{border:0; background:transparent; color:inherit; font-size:var(--fs-xl); line-height:1;
  cursor:pointer; padding:0 6px; min-width:28px; min-height:28px; border-radius:5px}
.toast-x:hover{background:rgba(255,255,255,.14)}

/* ---------- 13. 响应式：1199 / 1023 / 767 / 480 ---------- */
@media (max-width:1199px){
  .g4{grid-template-columns:repeat(2,minmax(0,1fr))}
  .study{grid-template-columns:190px minmax(0,1fr) 200px}
}
@media (max-width:1023px){
  .study{grid-template-columns:220px minmax(0,1fr)}
  .study>*:nth-child(3){grid-column:1/-1}
  .chat{height:calc(100vh - 180px)}
}
@media (max-width:767px){
  .hd-in{gap:12px; padding:8px 16px; height:auto; min-height:58px; flex-wrap:wrap}
  .logo small{display:none}
  .nav-toggle{display:inline-flex}
  .nav.nav-collapsed{display:none}
  .nav-in{padding:0 16px}
  .hd-search{display:none}
  .hd-search.open{display:flex; order:10; flex:1 1 100%; max-width:none; margin:2px 0 4px; height:40px}
  .hd-search.open .hd-search-cancel{display:inline-block !important}
  .hd-search-toggle{display:inline-flex; align-items:center; justify-content:center; margin-left:auto}
  .hd-right{margin-left:0}
  .linkbtn{display:none}
  .wrap{padding:16px 16px 44px}
  .g2,.g3,.g4{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* 窄屏：目录收成一行「课程目录 N/M 展开」，先读正文；展开后限高内部滚动，不把正文顶出一屏 */
  .study{display:flex; flex-direction:column; gap:14px; align-items:stretch}
  .study>.player{order:1; width:100%}
  .study>.tree{order:1; width:100%}
  .study>.tree .hd-row{padding-bottom:4px}
  .study>.tree .tree-toggle{display:flex}
  .study>.tree .tree-cur{display:block}
  .study>.tree .tree-list{max-height:44vh; overflow:auto; margin-top:2px}
  .study>.tree.tree-closed .tree-list{display:none}
  .study>main{order:2; scroll-margin-top:72px}
  .study>*:nth-child(3){order:3}
  .player .play{width:48px; height:48px}
  .ccard{min-height:0}
  .auth{min-height:calc(100vh - 58px); padding:24px 16px}
  .auth-box{padding:22px 18px 20px}
  .chat{height:auto; min-height:60vh}
  .sk-wrap{grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
  .tablewrap{overflow-x:auto}
  .tablewrap table{min-width:640px}
  .modal{max-width:none}
}
@media (max-width:480px){
  body{font-size:var(--fs-lg)}
  .hd-in{padding:8px 16px; gap:12px}
  .logo{font-size:var(--fs-lg)}
  .u-txt{display:none}
  .avatar{width:32px; height:32px}
  /* O-07：375 顶栏图标不贴边、不互相挤压——命中区 44、组内 gap≥10、右侧留 16px */
  .hd-user-btn{min-width:var(--tap); min-height:var(--tap); padding:0; justify-content:center}
  .hd-right{gap:10px}
  .nav-in{padding:0 12px}
  .nav a{padding:12px 12px; min-height:44px}
  .wrap{padding:14px 12px 40px}
  .g2,.g3,.g4{grid-template-columns:minmax(0,1fr)}
  /* 课程卡：375 单列、封面窄条 72×40、横向布局 */
  .ccard{display:grid; grid-template-columns:80px minmax(0,1fr); column-gap:12px; row-gap:6px;
    align-items:start; padding:14px; min-height:0}
  .ccard .cover{width:72px; height:40px; font-size:var(--fs-sm); letter-spacing:0; padding:0 6px; align-self:start}
  /* chip 容器必须落在文本列（旧问题：.tags 未指定列 → 掉进 80px 第一列，每个 chip 一行）。
     注：frontend-learn 的 pages.css（在本文件之后加载）在 ≤480 会把它覆盖为 grid-column:1/-1（整行宽），
     目的相同（避免 chip 竖排）；两种取值都不会掉进 80px 首列，实测 tagsW 为整卡宽时属预期。 */
  .ccard>.tags{grid-column:2; min-width:0}
  .ccard>.rscores,.ccard>.rweights,.ccard>.resume-line,.ccard>.bar{grid-column:1/-1}
  .ccard h3,.ccard .ccard-t{min-height:0; margin:0}
  .ccard .meta,.ccard-meta{grid-column:2}
  .ccard>.why,.ccard>.scoreline,.ccard-foot{grid-column:1/-1}
  .sec-h h1,.sec-h h2{font-size:var(--fs-lg)}
  .card{padding:14px}
  .qcard{padding:16px 14px}
  .q-opt{padding:12px}
  .btn,.btn.sm{min-height:var(--tap); font-size:var(--fs-md)}
  .btn-login{min-height:var(--tap)}
  .linkbtn{min-height:var(--tap)}
  .chip{min-height:var(--tap); padding:9px 14px}
  .input,.select{height:var(--tap)}
  .star{min-width:var(--tap); min-height:var(--tap); font-size:24px}
  .hd-search-toggle,.nav-toggle{width:var(--tap); height:var(--tap); flex:none}
  .modal-mask{padding:12px}
  .modal{border-radius:12px; padding:18px 16px}
  .modal-actions{display:grid; grid-template-columns:1fr; gap:8px}
  .modal-actions .btn{min-height:var(--tap)}
  .toast{width:calc(100vw - 24px); max-width:92vw; bottom:calc(16px + env(safe-area-inset-bottom))}
  .empty-state{padding:30px 14px}
  .auth{padding:18px 12px}
  .auth-box{padding:20px 16px 18px}
  .progress-line .bar{width:140px}
  /* 命中区：设计规范 §10.2-13「≤480 所有可点元素命中区 ≥44」。
     这些文字链/图标原本只有 16–24px 高，手指点不中。纯行内元素（<a> 默认 inline）加纵向
     padding 即可扩命中区而不改变行盒高度；inline-block/按钮/链接盒用等量负 margin 抵消，
     所以视觉与行距完全不变，只有可点区域变大。 */
  .logo{padding:11px 0; margin:-11px 0}
  .sec-h .more{min-height:var(--tap); padding:0 4px; margin-block:-12px}
  .btn-text{padding:14px 2px; margin:-14px 0}
  .hd-search button{min-height:var(--tap); min-width:var(--tap)}
  .interest-chip .x{min-width:var(--tap); min-height:var(--tap); margin:-11px -10px -11px 0}
  .course-row-title, .plan-item-title{padding:12px 0}
  .plan-name, .aside-item a{padding:12px 0; margin:-12px 0}
  .tree-toggle{min-height:var(--tap)}
}
