/* ==========================================================================
   巧思AI · 端上骨架样式
   所有前台页面只用这里的类名 + 题目/编辑器自己的样式；颜色一律取 theme.css 令牌。
   断点：手机 ≤560 · 平板竖屏 561-1024 · 平板横屏/电脑 ≥1025，横竖屏另有 orientation 规则。
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  font-family: var(--ms-font);
  background: var(--ms-bg);
  color: var(--ms-text);
  font-size: var(--ms-fs-body);
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

a { color: var(--ms-primary-strong); text-decoration: none; }
img { max-width: 100%; display: block; }
h1, h2, h3, h4 { margin: 0 0 .5em; font-weight: 800; letter-spacing: -.01em; line-height: 1.25; }
h1 { font-size: var(--ms-fs-title); }
h2 { font-size: var(--ms-fs-section); }
h3 { font-size: var(--ms-fs-section); }
p { margin: 0 0 .8em; }
:focus-visible { outline: 3px solid var(--ms-primary); outline-offset: 2px; }

/* ---------- 外壳 ---------- */
.ms-app { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }

.ms-nav {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; gap: 12px;
  flex: none; /* 两栏 shell 里 .ms-app 是 flex 列：不给这条，主区内容一超 100dvh 就按 basis 比例把顶栏
                 一起压扁（实测 1366 上 64→49，折叠左栏时又弹回 64，整页上下跳 15px）。顶栏不参与压缩。 */
  height: var(--ms-nav-h); padding: 0 clamp(12px, 3vw, 28px);
  background: rgba(255, 255, 255, .92);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--ms-border);
}
.ms-nav__brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: var(--ms-fs-section); color: var(--ms-text); }
.ms-nav__mark {
  width: 32px; height: 32px; border-radius: 10px; flex: none;
  background: linear-gradient(135deg, var(--ms-brand-a), var(--ms-brand-b));
  /* 品牌色固定：三套主题下都是同一颗橙标，阴影也跟着品牌色走 */
  box-shadow: var(--ms-brand-shadow);
  display: grid; place-items: center;
}
/* 品牌图形内联在这里、不用 <img>：底板吃 --ms-brand-*、白笔吃 --ms-on-primary，
   三套主题下都不换色。三笔坐标照 brand-mark.svg 底板内框（56 见方），与品牌标记同一份几何。 */
.ms-nav__mark svg { display: block; width: 100%; height: 100%; }
.ms-nav__spacer { flex: 1 1 auto; }
.ms-nav__links { display: flex; align-items: center; gap: 4px; }
.ms-nav__link {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--ms-tap); padding: 0 14px; border-radius: 999px;
  color: var(--ms-text-sub); font-weight: 600;
}
.ms-nav__link.is-active { background: var(--ms-primary-soft); color: var(--ms-primary-strong); }
.ms-nav__link:hover { background: var(--ms-surface-alt); color: var(--ms-text); }

.ms-main { flex: 1 1 auto; width: 100%; max-width: 1440px; margin: 0 auto; padding: clamp(14px, 2.6vw, 30px); }
.ms-main--narrow { max-width: 720px; }
.ms-main--board { max-width: 1600px; }

/* ---------- 两栏 shell（课堂 / 单题 / 考试）通用底座 ----------
   高度锁只加在 html.ms-pane-lock 上，由 DOM 就绪前的 inline 脚本打：JS 关掉时这两栏
   照样存在、内容照样读得到，只是整页可以纵向滚动——不许出现「脚本一挂页面就锁死看不全」。
   列宽与左右内边距全程跟其他页同源（2026-10-21 定：做题页不再单独满幅）。 */
.ms-main--pane { min-height: 0; display: flex; flex-direction: column; }
/* 收竖向内边距只为省一屏高度预算（docs/learn-two-pane.md 9.1 的高度账）；
   手机档与无 JS 档整页本来就能纵滚，不收，卡片才不贴墙。 */
html.ms-pane-lock .ms-main--pane { padding-block: 0; }
html.ms-pane-lock .ms-app { height: 100vh; height: 100dvh; overflow: hidden; }
.ms-body--pane .ms-footer { display: none; }

/* 块体悬浮浮层：只放只读内容（要点的都留在块体内或右栏）。定位由 ms-ui.js 按锚点算。
   .ms-pop-src 是服务端渲染在锚点内部的那份预览：平时不占位，脚本把它 clone 进浮层再画 */
.ms-pop-src { display: none; }
.ms-pop { position: fixed; z-index: 80; display: none; max-width: min(320px, 80vw); }
.ms-pop.is-show { display: block; }
.ms-pop__body {
  padding: 10px 12px; border-radius: var(--ms-radius); background: var(--ms-surface);
  border: 1px solid var(--ms-border-strong); box-shadow: var(--ms-shadow-l);
  font-size: var(--ms-fs-sub); line-height: 1.6; color: var(--ms-text-sub);
}
.ms-pop__body b { color: var(--ms-text); }
.ms-pop__body .ms-badge { font-size: var(--ms-fs-micro); }

/* 手机档（≤560、横屏手机）：块内容不走折叠，自底部升起一层；
   浮层有 60vh 可滚，所以轨道在里面照旧竖着排，不退回旧版那条横向滚动 */
.ms-sheet { position: fixed; inset: 0; z-index: 120; display: none; }
.ms-sheet.is-show { display: block; }
.ms-sheet__mask { position: absolute; inset: 0; background: rgba(24, 20, 15, .42); }
.ms-sheet__panel {
  position: absolute; left: 0; right: 0; bottom: 0; max-height: 60vh; overflow-y: auto;
  overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  padding: 14px 16px max(16px, env(safe-area-inset-bottom));
  background: var(--ms-surface); border-top: 1px solid var(--ms-border-strong);
  border-radius: var(--ms-radius-l) var(--ms-radius-l) 0 0; box-shadow: var(--ms-shadow-l);
}
.ms-sheet__cap { margin: 0 0 10px; font-weight: 900; font-size: var(--ms-fs-section); color: var(--ms-text); }
.ms-sheet__close { width: 100%; min-height: 48px; margin-top: 12px; }

/* 返回键不住顶栏，住内容区：标题行里与标题并排（.ms-titleback），做题页落在操作行的按钮组里。
   判据是 G7「返回键不许独占一行」——它必须和同一行里别的东西并排，落点清单见 docs/learn-two-pane.md 7.1。
   热区靠 .ms-backhit 撑到 44px；文字收不收由 .ms-backlabel 在窄屏那几条媒体查询里管，
   aria-label 在锚点上，收起文字不影响读屏。 */
.ms-titleback { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 标题让位给文字换行，而不是把返回键挤到下一行：flex-basis 必须给 0，
   给 auto 时换行算的是 max-content，长标题（learn/finish 的课堂名）会在窄列里逼出第二行。 */
.ms-titleback > h1 { flex: 1 1 0; min-width: 0; margin: 0; }
.ms-backhit { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; }
.ms-backchip {
  display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 12px 0 9px;
  border-radius: 999px; border: 1px solid var(--ms-primary-line); background: var(--ms-surface);
  color: var(--ms-primary-strong); font-weight: 700; font-size: var(--ms-fs-sub); box-shadow: var(--ms-shadow);
}
.ms-backchip svg { width: 14px; height: 14px; flex: none; }
.ms-backhit:hover .ms-backchip { background: var(--ms-primary-soft); border-color: var(--ms-primary); }
.ms-backhit:focus-visible { outline: none; }
.ms-backhit:focus-visible .ms-backchip { border-color: var(--ms-primary); box-shadow: 0 0 0 3px var(--ms-primary-soft); }

.ms-footer { padding: 18px; text-align: center; color: var(--ms-muted); font-size: var(--ms-fs-sub); border-top: 1px solid var(--ms-border); }

/* ---------- 通用块 ---------- */
.ms-hero {
  position: relative; overflow: hidden; border-radius: var(--ms-radius-l);
  padding: clamp(20px, 3.4vw, 40px); color: #fff; margin-bottom: clamp(16px, 2.4vw, 26px);
  background: linear-gradient(120deg, var(--ms-hero-a), var(--ms-hero-b));
  box-shadow: var(--ms-shadow-l);
}
.ms-hero::after {
  content: ""; position: absolute; right: -60px; top: -80px; width: 260px; height: 260px;
  border-radius: 50%; background: rgba(255, 255, 255, .16);
}
.ms-hero h1 { color: #fff; margin: 0 0 6px; }
.ms-hero p { color: rgba(255, 255, 255, .9); margin: 0; max-width: 60ch; }

.ms-section { margin-bottom: clamp(18px, 3vw, 32px); }
/* 导航是 sticky 的：锚点跳转要给区块标题留出 nav 的高度，否则 #packages 落点被压住 */
.ms-section[id] { scroll-margin-top: calc(var(--ms-nav-h) + 12px); }
.ms-section__head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.ms-section__title { font-size: var(--ms-fs-section); font-weight: 800; }
.ms-section__more { margin-left: auto; font-weight: 600; color: var(--ms-text-sub); }
/* 右侧那个文字链接是可点的：给它一个 ≥36px 的触控盒，
   用「内边距 + 等额负外边距」把热区撑开而不改变这一行的基线与占位。 */
a.ms-section__more { padding: 11px 6px; margin: -11px 0 -11px auto; }

.ms-card {
  background: var(--ms-surface); border: 1px solid var(--ms-border);
  border-radius: var(--ms-radius-l); box-shadow: var(--ms-shadow); padding: clamp(14px, 2vw, 22px);
}
.ms-card--flat { box-shadow: none; }
.ms-card--hover { transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
.ms-card--hover:hover, .ms-card--hover:focus-within { transform: translateY(-3px); box-shadow: var(--ms-shadow-l); border-color: var(--ms-primary-line); }

.ms-grid { display: grid; gap: clamp(12px, 1.6vw, 20px); grid-template-columns: repeat(auto-fill, minmax(min(100%, 268px), 1fr)); }
.ms-grid--wide { grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); }

.ms-cover { position: relative; border-radius: var(--ms-radius); overflow: hidden; aspect-ratio: 16/10; background: var(--ms-primary-soft); }
.ms-cover img { width: 100%; height: 100%; object-fit: cover; }
.ms-cover__fallback { position: absolute; inset: 0; display: grid; place-items: center; font-size: 42px; font-weight: 900; color: #fff; background: linear-gradient(135deg, var(--ms-hero-a), var(--ms-hero-b)); }

.ms-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; color: var(--ms-text-sub); font-size: var(--ms-fs-sub); }
.ms-title-link { color: var(--ms-text); font-weight: 800; font-size: var(--ms-fs-body); display: block; margin: 10px 0 6px; }
.ms-title-link:hover { color: var(--ms-primary-strong); }
/* 卡片标题里那个内联 <a> 只有 101×22 的可点面积：同样用负外边距换热区，行高与卡片不动 */
.ms-title-link > a { display: inline-flex; align-items: center; min-height: var(--ms-tap); padding: 0 8px; margin: -13px -8px; }

/* ---------- 徽标 / 标签 ---------- */
.ms-badge {
  display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 9px;
  border-radius: 7px; font-size: var(--ms-fs-micro); font-weight: 700; letter-spacing: .02em;
  background: var(--ms-primary-soft); color: var(--ms-primary-strong);
}
.ms-badge--line { background: transparent; border: 1px solid var(--ms-primary-line); }
.ms-badge--gray { background: var(--ms-surface-alt); color: var(--ms-text-sub); }
.ms-badge--ok { background: var(--ms-success-soft); color: var(--ms-success); }
.ms-badge--warn { background: var(--ms-accent-soft); color: var(--ms-warning); }
.ms-badge--lock { background: var(--ms-surface-alt); color: var(--ms-muted); }

.ms-chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--ms-tap); padding: 0 16px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--ms-border-strong); background: var(--ms-surface);
  color: var(--ms-text-sub); font-weight: 700; font-size: var(--ms-fs-sub);
}
.ms-chip:hover { border-color: var(--ms-primary-line); color: var(--ms-text); }
.ms-chip.is-active { background: var(--ms-primary); border-color: var(--ms-primary); color: var(--ms-on-primary); }
.ms-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.ms-chips__label { align-self: center; font-size: var(--ms-fs-sub); font-weight: 700; color: var(--ms-muted); }
.ms-section__head + .ms-chips,
.ms-chips + .ms-chips { margin-top: 10px; }
.ms-chip .ms-badge { background: var(--ms-surface-alt); color: var(--ms-text-sub); }
.ms-chip.is-active .ms-badge { background: rgba(255, 255, 255, .22); color: var(--ms-on-primary); }

/* ---------- 按钮 ---------- */
.ms-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--ms-tap); padding: 0 20px; border-radius: 999px; cursor: pointer;
  border: 1px solid transparent; background: var(--ms-surface-alt); color: var(--ms-text);
  font-weight: 800; font-size: var(--ms-fs-ctl); font-family: inherit; transition: filter .15s ease, transform .1s ease;
}
.ms-btn:hover { filter: brightness(.97); }
.ms-btn:active { transform: translateY(1px); }
.ms-btn[disabled] { opacity: .5; cursor: not-allowed; }
.ms-btn--primary { background: linear-gradient(135deg, var(--ms-hero-a), var(--ms-hero-b)); color: #fff; box-shadow: 0 6px 18px rgba(242, 105, 28, .26); }
.ms-btn--outline { background: var(--ms-surface); border-color: var(--ms-primary-line); color: var(--ms-primary-strong); }
.ms-btn--ghost { background: transparent; color: var(--ms-text-sub); }
.ms-btn--danger { background: var(--ms-danger-soft); color: var(--ms-danger); }
.ms-btn--block { width: 100%; }
.ms-btn--lg { min-height: 56px; padding: 0 30px; font-size: var(--ms-fs-num-sm); }
.ms-btn--sm { min-height: 36px; padding: 0 14px; font-size: var(--ms-fs-ctl); }

.ms-iconbtn {
  width: var(--ms-tap); height: var(--ms-tap); flex: none; display: grid; place-items: center;
  border-radius: 12px; border: 1px solid var(--ms-border); background: var(--ms-surface);
  color: var(--ms-text-sub); cursor: pointer; font-size: var(--ms-fs-num-sm); font-family: inherit;
}
.ms-iconbtn:hover { color: var(--ms-primary-strong); border-color: var(--ms-primary-line); }
.ms-iconbtn.is-active { background: var(--ms-primary); color: #fff; border-color: var(--ms-primary); }

.ms-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ms-row--between { justify-content: space-between; }
.ms-row--end { justify-content: flex-end; }
.ms-grow { flex: 1 1 auto; min-width: 0; }
.ms-hide-sm { display: initial; }

/* ---------- 表单 ---------- */
.ms-field { margin-bottom: 16px; }
.ms-label { display: block; font-weight: 700; font-size: var(--ms-fs-sub); color: var(--ms-text-sub); margin-bottom: 6px; }
.ms-input, .ms-select, .ms-textarea {
  width: 100%; min-height: var(--ms-tap); padding: 10px 14px; font-size: var(--ms-fs-body); font-family: inherit;
  border: 1px solid var(--ms-border-strong); border-radius: var(--ms-radius-s); background: var(--ms-surface); color: var(--ms-text);
}
.ms-input:focus, .ms-select:focus, .ms-textarea:focus { border-color: var(--ms-primary); outline: none; box-shadow: 0 0 0 3px var(--ms-primary-soft); }
.ms-textarea { min-height: 108px; resize: vertical; }
.ms-help { font-size: var(--ms-fs-sub); color: var(--ms-muted); margin-top: 6px; }
.ms-error { font-size: var(--ms-fs-sub); color: var(--ms-danger); margin-top: 6px; }

/* ---------- 状态 ---------- */
.ms-empty { padding: 48px 18px; text-align: center; color: var(--ms-muted); border: 1px dashed var(--ms-border-strong); border-radius: var(--ms-radius-l); }
.ms-empty__icon { font-size: 40px; margin-bottom: 8px; opacity: .6; }
.ms-loading { display: grid; place-items: center; min-height: 40vh; color: var(--ms-text-sub); }
.ms-spinner { width: 34px; height: 34px; border-radius: 50%; border: 3px solid var(--ms-primary-soft); border-top-color: var(--ms-primary); animation: ms-spin .8s linear infinite; }
@keyframes ms-spin { to { transform: rotate(360deg); } }

.ms-toast {
  position: fixed; left: 50%; bottom: 32px; transform: translate(-50%, 20px); z-index: 999;
  padding: 12px 22px; border-radius: 999px; background: rgba(35, 39, 47, .92); color: #fff;
  font-weight: 700; opacity: 0; transition: opacity .2s ease, transform .2s ease; pointer-events: none; max-width: 88vw; text-align: center;
}
.ms-toast.is-show { opacity: 1; transform: translate(-50%, 0); }

.ms-modal { position: fixed; inset: 0; z-index: 900; display: none; place-items: center; background: rgba(24, 20, 15, .42); padding: 18px; }
.ms-modal.is-show { display: grid; }
.ms-modal__box { width: min(520px, 100%); background: var(--ms-surface); border-radius: var(--ms-radius-l); padding: 24px; box-shadow: var(--ms-shadow-l); }

/* ---------- 题板外壳：横屏左右分栏，竖屏上下堆叠 ---------- */
.ms-stage {
  display: grid; gap: clamp(14px, 2vw, 24px);
  grid-template-areas: "head" "board" "pad";
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
.ms-stage__head { grid-area: head; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ms-stage__board { grid-area: board; display: flex; justify-content: center; min-width: 0; }
.ms-stage__pad { grid-area: pad; }
.ms-stage__side { grid-area: side; }

.ms-boardbox {
  background: var(--ms-surface); border: 1px solid var(--ms-border); border-radius: var(--ms-radius-l);
  box-shadow: var(--ms-shadow); padding: clamp(10px, 1.6vw, 18px);
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.ms-boardbox--rotatable { transition: transform .28s cubic-bezier(.2, .8, .2, 1); }

.ms-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* 题板组件自己注入的工具行（数独 .msd-tools、幻方 .msz__head、24 点 .mtf__head、七巧板 .mtg__head）里混排着
   .ms-iconbtn(48px) 与 .ms-btn--sm(36px)：同一行的控件高度差 12px，且 36px 在平板上够不到 44 的热区。
   这里把同一行的可点控件统一成一个热区、文字居中；只改高度与对齐，颜色与形状仍由题板与 theme.css 决定。 */
.msd-tools, .msz__head, .mtf__head, .mtg__head { align-items: center; }
.msd-tools > .ms-btn,
.msd-tools > .ms-chip,
.msd-tools > .ms-iconbtn,
.msz__head > .ms-btn,
.msz__head > .ms-chip,
.msz__head > .ms-iconbtn,
.mtf__head > .ms-btn,
.mtf__head > .ms-chip,
.mtf__head > .ms-iconbtn,
.mtg__head > .ms-btn,
.mtg__head > .ms-chip,
.mtg__head > .ms-iconbtn {
  height: var(--ms-tap); min-height: var(--ms-tap);
  display: inline-flex; align-items: center; justify-content: center; text-align: center;
}
.ms-timer { font-family: var(--ms-num); font-weight: 800; font-size: var(--ms-fs-num); letter-spacing: .02em; color: var(--ms-text); }
.ms-timer.is-urgent { color: var(--ms-danger); }

.ms-progressbar { height: 8px; border-radius: 999px; background: var(--ms-primary-soft); overflow: hidden; }
.ms-progressbar > i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--ms-hero-a), var(--ms-hero-b)); transition: width .3s ease; }

/* 手机 */
@media (max-width: 560px) {
  /* 必须写成 html:root：theme.css 里这条令牌挂在「:root, html[data-theme="sun"]」上，
     带属性的选择器特异性 (0,1,1) 高于单独的 :root，默认主题（橙黄色）下 46px 会被 48px 顶回去。 */
  html:root { --ms-tap: 46px; }
  .ms-hide-sm { display: none; }
  .ms-grid { grid-template-columns: 1fr; }
}

/* 平板竖屏 / 小屏：题板在上，键盘在下 */
@media (min-width: 561px) and (max-width: 1024px) {
  .ms-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
}

/* 平板横屏 / 电脑：题板与操作区左右分栏 */
@media (min-width: 1025px) {
  .ms-stage {
    grid-template-areas: "head head" "board side";
    grid-template-columns: minmax(0, 1.25fr) minmax(300px, .75fr);
  }
  .ms-stage--split { grid-template-areas: "head head" "board pad" "side side"; }
}

@media (orientation: landscape) and (min-width: 700px) and (max-height: 620px) {
  /* 横屏矮窗口（平板横过来）：压缩纵向留白，键盘改成一行 */
  .ms-main { padding: 10px clamp(14px, 3vw, 30px); }
  .ms-hero { padding: 16px 22px; margin-bottom: 14px; }
  .ms-section { margin-bottom: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ==========================================================================
   追加区 · 一：外壳补齐
   这些类名 layout.html / denied.html 已经在用，此前样式缺失，在这里补全。
   热区一律 ≥ var(--ms-tap)（48px，手机断点提到 46px），不依赖 hover 才能操作。
   ========================================================================== */

.ms-skip {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  min-height: var(--ms-tap); padding: 0 18px; display: inline-flex; align-items: center;
  border-radius: 999px; background: var(--ms-surface); color: var(--ms-primary-strong);
  border: 1px solid var(--ms-primary-line); font-weight: 800; transition: top .15s ease;
}
.ms-skip:focus { top: 10px; }

/* 只给读屏器看的名字（按钮上那颗图标本身没有文字时用它补一个） */
.ms-off {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.ms-nav__menu { display: none; }
.ms-nav__brand { min-height: var(--ms-tap); min-width: var(--ms-tap); flex: 0 1 auto; }
.ms-nav__name { min-width: 0; max-width: 30vw; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-nav__mename { min-width: 0; max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 身份标记：老师登录后常驻顶栏姓名右侧，不用点开子菜单就知道当前是教师账号。
   文字固定「教师」二字（2 × 11px ≈ 22px），比原来那整颗「老师视图」导航键窄得多；
   窄屏随 .ms-nav__mename 一起隐藏，见下面 700/900 两档 */
.ms-nav__tag {
  flex: none; padding: 1px 6px; border-radius: 999px; white-space: nowrap;
  background: var(--ms-primary-soft); border: 1px solid var(--ms-primary-line);
  color: var(--ms-primary-strong); font-size: var(--ms-fs-micro); font-weight: 800; line-height: 1.4;
}
.ms-usermenu__meta:empty { display: none; }
.ms-nav__links .ms-nav__link--mobile { display: none; }
/* 导航链接永远单行且不被压扁：让位的只能是字标与成员名（上面两条已允许收缩） */
.ms-nav__links { flex: 0 0 auto; }
.ms-nav__link { flex: none; white-space: nowrap; }

/* 账号子菜单：一颗「头像 + 名字 + 小箭头」的按钮，点开是一列去处。
   面板用绝对定位挂在按钮下面，所以 .ms-usermenu 必须是定位上下文。 */
.ms-usermenu { position: relative; display: flex; align-items: center; flex: none; }
.ms-usermenu__btn {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%;
  height: var(--ms-tap); padding: 0 10px; border: 1px solid transparent; border-radius: 999px;
  background: transparent; color: var(--ms-text-sub); font: inherit; font-size: var(--ms-fs-sub);
  font-weight: 700; cursor: pointer;
}
.ms-usermenu__btn:hover, .ms-usermenu.is-open .ms-usermenu__btn {
  background: var(--ms-primary-soft); border-color: var(--ms-primary-line); color: var(--ms-text);
}
.ms-usermenu__btn:focus { outline: none; box-shadow: 0 0 0 3px var(--ms-primary-soft); border-color: var(--ms-primary); }
.ms-usermenu__caret { flex: none; font-size: var(--ms-fs-micro); line-height: 1; color: var(--ms-muted); transition: transform .15s ease; }
.ms-usermenu.is-open .ms-usermenu__caret { transform: rotate(180deg); }
.ms-usermenu__panel {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 120; display: none;
  flex-direction: column; min-width: 210px; padding: 6px;
  background: var(--ms-surface); border: 1px solid var(--ms-border); border-radius: var(--ms-radius);
  box-shadow: var(--ms-shadow-l);
}
.ms-usermenu.is-open .ms-usermenu__panel { display: flex; }
.ms-usermenu__who { padding: 8px 10px 10px; border-bottom: 1px solid var(--ms-border); margin-bottom: 6px; }
.ms-usermenu__name { display: block; font-weight: 800; color: var(--ms-text); }
.ms-usermenu__meta { display: block; margin-top: 2px; font-size: var(--ms-fs-sub); color: var(--ms-muted); }
.ms-usermenu__item {
  display: flex; align-items: center; min-height: 44px; padding: 0 10px;
  border-radius: var(--ms-radius-s); color: var(--ms-text); font-weight: 700; font-size: var(--ms-fs-ctl);
  text-decoration: none;
}
.ms-usermenu__item:hover, .ms-usermenu__item:focus {
  background: var(--ms-primary-soft); color: var(--ms-primary-strong); outline: none;
}
.ms-usermenu__item--out { margin-top: 4px; border-top: 1px solid var(--ms-border); color: var(--ms-danger); }
.ms-usermenu__item--out:hover, .ms-usermenu__item--out:focus { background: var(--ms-cell-bad); color: var(--ms-danger); }

.ms-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; flex: none; }
.ms-avatar--letter {
  display: grid; place-items: center; width: auto; min-width: 32px; max-width: 64px;
  padding: 0 8px; border-radius: 999px; background: var(--ms-primary-soft);
  color: var(--ms-primary-strong); font-weight: 800; font-size: var(--ms-fs-sub);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}

/* 顶栏配色键：一颗图标按钮，点一下轮切三套主题。「配色」二字和主题名都不再占位，
   名字走 aria-label（layout.html 的 msPaintTheme 每次切换后重写）。
   2026-10-22 两次反馈收敛到这里：第一次「图标太大了」我收的是字形（17→14），方向错了；
   第二次说清楚了他不要的是**那颗 48 见方的着色矩形外框**——所以边框与底色撤掉、
   字形回到原来那档（继承 .ms-iconbtn 的 --ms-fs-num-sm 17px，实测墨迹 17x22）。
   热区一寸没削：仍是 var(--ms-tap) 见方（48/46px，全局 border-box 所以剥边框不缩盒），
   与同排汉堡键、账号按钮同一档，44px 下限保住。hover 改成只换字色，不再画填充块；
   键盘焦点仍画那圈 ring（裸图标没有边框可高亮，否则 Tab 过去看不见在哪）。
   顶栏每页都在，所以规则写在人人加载的 base.css 里：page-shell.css 只有首页/课包/登录页引入，
   「我的」「课堂」「作业」用不上，同一颗键会在不同页面差 8px。 */
.ms-themebtn {
  border: 0; background: none;
  color: var(--ms-primary-strong);
}
.ms-themebtn:hover { color: var(--ms-primary); }
.ms-themebtn:focus { outline: none; box-shadow: 0 0 0 3px var(--ms-primary-line); }
/* 未登录时顶栏那颗「登录」键同上一档：.ms-btn--sm 的 36px 要在这里覆盖，
   所以这条排在 .ms-btn--sm 之后（同优先级，后置者赢）。 */
.ms-nav__signin { height: var(--ms-tap); min-height: var(--ms-tap); padding: 0 18px; font-size: var(--ms-fs-ctl); }
.ms-footer__row { display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; }
.ms-footer__dot { color: var(--ms-border-strong); }
.ms-footer__legal { width: 100%; margin-top: 4px; display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; font-size: var(--ms-fs-micro); color: var(--ms-muted); }
.ms-footer__copy, .ms-footer__icp { font-size: inherit; color: inherit; }
.ms-footer__icp a { color: inherit; text-decoration: none; }
.ms-footer__icp a:hover { color: var(--ms-primary); text-decoration: underline; }

/* ---------- 走不通的那两页（denied / notice 共用一套版式） ---------- */
.ms-deny, .ms-notice {
  max-width: 680px; margin: clamp(6px, 4vh, 44px) auto; text-align: center;
  background: var(--ms-surface); border: 1px solid var(--ms-border);
  border-radius: var(--ms-radius-l); box-shadow: var(--ms-shadow);
  padding: clamp(22px, 4vw, 46px);
}
.ms-deny__mark, .ms-notice__mark {
  width: 76px; height: 76px; margin: 0 auto 18px; border-radius: 50%;
  display: grid; place-items: center; font-size: 30px; line-height: 1;
  background: var(--ms-primary-soft); color: var(--ms-primary-strong);
}
.ms-notice__mark { background: var(--ms-accent-soft); color: var(--ms-warning); }
.ms-deny__title, .ms-notice__title { font-size: var(--ms-fs-title); margin: 0 0 10px; }
.ms-deny__text, .ms-notice__text { color: var(--ms-text-sub); font-size: var(--ms-fs-body); margin: 0 auto 22px; max-width: 44ch; }
.ms-deny__pack, .ms-notice__pack {
  display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap;
  margin: 0 auto 22px; padding: 12px 14px; max-width: 100%;
  border: 1px solid var(--ms-border); border-radius: var(--ms-radius);
  background: var(--ms-surface-alt); font-weight: 700;
}
.ms-deny__acts, .ms-notice__acts { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }
.ms-deny__tip, .ms-notice__tip { margin: 20px 0 0; font-size: var(--ms-fs-sub); color: var(--ms-muted); }

/* ---------- 零散工具 ---------- */
.ms-row--center { justify-content: center; }
.ms-stack { display: grid; gap: 12px; }
.ms-sub { color: var(--ms-text-sub); }
.ms-muted { color: var(--ms-muted); font-size: var(--ms-fs-sub); }
.ms-truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-wrap { overflow-wrap: anywhere; }
.ms-divider { height: 1px; border: 0; margin: 16px 0; background: var(--ms-border); }
.ms-alert {
  display: flex; align-items: flex-start; gap: 10px; margin-bottom: 18px;
  padding: 12px 16px; border-radius: var(--ms-radius);
  background: var(--ms-danger-soft); border: 1px solid var(--ms-danger);
  color: var(--ms-danger); font-weight: 700;
}
.ms-note {
  display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px;
  border-radius: var(--ms-radius); background: var(--ms-primary-soft);
  color: var(--ms-primary-strong); font-size: var(--ms-fs-sub); font-weight: 600;
}

/* ==========================================================================
   追加区 · 二：首页 / 课包 / 课堂 / 登录注册 的页面组件
   命名沿用 .ms-块__元素--修饰；颜色全部走 theme.css 令牌。
   ========================================================================== */

/* ---------- 首屏横幅内部 ---------- */
.ms-hero__inner { position: relative; z-index: 1; display: grid; gap: clamp(14px, 2vw, 24px); }
.ms-hero__title { color: #fff; font-size: var(--ms-fs-display); margin: 0 0 8px; }
.ms-hero__text { color: rgba(255, 255, 255, .92); margin: 0; max-width: 58ch; font-size: var(--ms-fs-body); }
.ms-hero__acts { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ms-hero__stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px; }
.ms-stat {
  padding: 12px 14px; border-radius: var(--ms-radius);
  background: rgba(255, 255, 255, .16); border: 1px solid rgba(255, 255, 255, .30);
}
.ms-stat__num { font-family: var(--ms-num); font-weight: 800; font-size: var(--ms-fs-num); line-height: 1.15; color: #fff; }
.ms-stat__label { font-size: var(--ms-fs-sub); color: rgba(255, 255, 255, .88); }
.ms-btn--invert { background: #fff; color: var(--ms-primary-strong); box-shadow: 0 6px 18px rgba(30, 18, 6, .18); }

/* ---------- 横屏两栏 / 竖屏堆叠的页面骨架 ---------- */
.ms-page { display: grid; gap: clamp(14px, 2vw, 24px); grid-template-columns: minmax(0, 1fr); align-items: start; }
.ms-page__side { display: grid; gap: clamp(12px, 1.6vw, 18px); min-width: 0; }
.ms-page__main { min-width: 0; }

/* ---------- 侧栏卡片里的「一行数值」 ---------- */
.ms-kv { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px dashed var(--ms-border); }
.ms-kv:last-child { border-bottom: none; }
.ms-kv__k { color: var(--ms-text-sub); font-size: var(--ms-fs-sub); }
.ms-kv__v { font-weight: 800; text-align: right; overflow-wrap: anywhere; }
.ms-price { font-family: var(--ms-num); font-weight: 800; color: var(--ms-primary-strong); }
.ms-price--free { color: var(--ms-success); }

/* ---------- 进度环：内联只写 --ms-ring-deg（整数角度） ---------- */
.ms-ring {
  position: relative; width: clamp(88px, 9vw, 108px); aspect-ratio: 1; flex: none;
  border-radius: 50%; background: conic-gradient(var(--ms-primary) var(--ms-ring-deg, 0deg), var(--ms-primary-soft) 0);
}
.ms-ring__hole {
  position: absolute; inset: 11px; border-radius: 50%; background: var(--ms-surface);
  display: grid; place-items: center; text-align: center; line-height: 1.2;
}
.ms-ring__num { font-family: var(--ms-num); font-weight: 800; font-size: var(--ms-fs-num-sm); }
.ms-ring__label { display: block; font-size: var(--ms-fs-micro); color: var(--ms-muted); }

/* ---------- 课包卡片（整张卡就是一个大链接） ---------- */
.ms-pack { display: flex; flex-direction: column; gap: 0; padding: 0; overflow: hidden; color: var(--ms-text); }
.ms-pack .ms-cover { border-radius: 0; }
.ms-pack__body { display: grid; gap: 6px; padding: clamp(14px, 2vw, 20px); }
.ms-pack__foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: auto; padding: 0 clamp(14px, 2vw, 20px) clamp(14px, 2vw, 20px); }
.ms-pack__foot .ms-grow { flex: 1 1 auto; }
.ms-pack--locked { border-style: dashed; }
.ms-pack__head { display: flex; align-items: flex-start; gap: 14px; }
.ms-pack__letter {
  width: 74px; height: 74px; flex: none; border-radius: var(--ms-radius);
  display: grid; place-items: center; font-size: 30px; font-weight: 900; color: #fff;
  background: linear-gradient(135deg, var(--ms-hero-a), var(--ms-hero-b));
}

/* ---------- 一条一行的列表（课堂链 / 作业链 / 题目链共用） ---------- */
.ms-list { display: grid; gap: 12px; }
.ms-item {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 14px; align-items: center;
  padding: 14px clamp(14px, 2vw, 20px); min-width: 0;
  background: var(--ms-surface); border: 1px solid var(--ms-border);
  border-radius: var(--ms-radius-l); box-shadow: var(--ms-shadow);
}
a.ms-item:hover, .ms-item:focus-within { border-color: var(--ms-primary-line); box-shadow: var(--ms-shadow-l); }
.ms-item__idx {
  width: 44px; height: 44px; flex: none; border-radius: 12px; display: grid; place-items: center;
  font-family: var(--ms-num); font-weight: 800; font-size: var(--ms-fs-num-sm);
  background: var(--ms-primary-soft); color: var(--ms-primary-strong);
}
.ms-item__title { display: block; font-weight: 800; font-size: var(--ms-fs-body); color: var(--ms-text); margin: 0 0 4px; }
.ms-item__title:hover { color: var(--ms-primary-strong); }
.ms-item__meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; color: var(--ms-text-sub); font-size: var(--ms-fs-sub); }
.ms-item__act { display: flex; align-items: center; gap: 8px; flex: none; }
.ms-item--done { border-color: var(--ms-primary-line); background: var(--ms-surface-alt); }
.ms-item--done .ms-item__idx { background: var(--ms-success-soft); color: var(--ms-success); }
.ms-item__thumb { width: 44px; height: 44px; border-radius: 10px; object-fit: cover; flex: none; border: 1px solid var(--ms-border); }

/* ---------- 首页的题型直练块 ---------- */
.ms-tile {
  display: flex; align-items: center; gap: 14px; text-align: left; min-width: 0;
  padding: clamp(14px, 2vw, 20px); border-radius: var(--ms-radius-l);
  background: var(--ms-surface); border: 1px solid var(--ms-border); box-shadow: var(--ms-shadow);
  color: var(--ms-text);
}
.ms-tile:hover { border-color: var(--ms-primary-line); box-shadow: var(--ms-shadow-l); }
.ms-tile__icon {
  width: 54px; height: 54px; flex: none; border-radius: 16px; display: grid; place-items: center;
  font-family: var(--ms-num); font-weight: 900; font-size: var(--ms-fs-num-sm);
  background: var(--ms-primary-soft); color: var(--ms-primary-strong);
}
.ms-tile__text { min-width: 0; }
.ms-tile__title { display: block; font-weight: 800; font-size: var(--ms-fs-body); margin-bottom: 2px; }

/* ---------- 二级区块里的「一条摘要 + 一个动作」 ---------- */
.ms-strip {
  display: grid; gap: 14px; align-items: center; grid-template-columns: minmax(0, 1fr);
  padding: clamp(14px, 2vw, 20px); background: var(--ms-surface);
  border: 1px solid var(--ms-border); border-radius: var(--ms-radius-l); box-shadow: var(--ms-shadow);
}
.ms-strip__head { display: flex; align-items: center; gap: 12px; min-width: 0; flex-wrap: wrap; }
.ms-strip__bar { margin-top: 12px; }
.ms-strip__acts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ---------- 登录 / 注册 ---------- */
.ms-auth { max-width: 520px; margin: clamp(6px, 5vh, 48px) auto; }
.ms-auth__card {
  padding: clamp(20px, 3.4vw, 34px); background: var(--ms-surface);
  border: 1px solid var(--ms-border); border-radius: var(--ms-radius-l); box-shadow: var(--ms-shadow-l);
}
.ms-auth__title { font-size: var(--ms-fs-title); margin: 0 0 6px; }
.ms-auth__sub { color: var(--ms-text-sub); margin: 0 0 22px; font-size: var(--ms-fs-body); }
.ms-auth__foot { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--ms-border); text-align: center; color: var(--ms-text-sub); font-size: var(--ms-fs-body); }
.ms-check { display: flex; align-items: center; gap: 10px; min-height: var(--ms-tap); font-weight: 700; color: var(--ms-text-sub); cursor: pointer; }
.ms-check__box { width: 22px; height: 22px; flex: none; accent-color: var(--ms-primary); }
.ms-auth__grid { display: grid; gap: 0 16px; grid-template-columns: minmax(0, 1fr); }
.ms-auth-page { background: var(--ms-surface-alt); }
.ms-form { display: grid; }

/* ---------- 页面里常用的小工具：外边距 / 强调 / 列表行的文字列 ---------- */
.ms-mt { margin-top: 12px; }
.ms-mt-s { margin-top: 8px; }
.ms-strong { font-weight: 800; }
.ms-item__text { min-width: 0; }
.ms-pack__name { margin: 0 0 4px; font-size: var(--ms-fs-title); }
.ms-pack__sub { margin: 0 0 10px; }
.ms-pack__cover {
  width: 74px; height: 74px; flex: none; border-radius: var(--ms-radius);
  object-fit: cover; border: 1px solid var(--ms-border);
}

/* ---------- 首屏里那两枚只在横幅上出现的辅助 ---------- */
.ms-hero__hint { color: rgba(255, 255, 255, .9); font-size: var(--ms-fs-sub); }
.ms-hero__alt { background: transparent; color: #fff; border-color: rgba(255, 255, 255, .55); }

/* ==========================================================================
   追加区 · 三：响应式加固
   320 / 768 竖 / 1024 横 / 1440 / 2560 都不允许出现横向滚动。
   ========================================================================== */

/* 顶栏折叠：≤700px（手机竖屏 / 小窗横屏）把导航收进汉堡菜单。
   原来只在 ≤560 折叠，640 与 768 这一档撑不下 nowrap 的五个链接，
   退出按钮会被顶出容器（实测 nav.scrollWidth 673 / clientWidth 640）。
   这两段必须写在上面所有 .ms-nav__* / .ms-avatar 基础规则之后，
   否则同优先级会被后置的基础规则（display:none / display:flex）覆盖掉。 */
@media (max-width: 700px) {
  .ms-nav__menu { display: grid; }
  .ms-nav__links { display: none; }
  .ms-nav__links.is-open {
    display: flex; position: absolute; top: var(--ms-nav-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch; padding: 10px;
    background: var(--ms-surface); border-bottom: 1px solid var(--ms-border);
    box-shadow: var(--ms-shadow-l);
  }
  .ms-nav__links .ms-nav__link--mobile { display: inline-flex; width: 100%; }
  .ms-nav__links.is-open .ms-nav__link { justify-content: flex-start; }
  /* 折叠态下站名让位（导航链接收进汉堡菜单），顶栏留标记 / 菜单 / 配色键 / 登录键 / 账号按钮。
     「登录」这一颗不跟着收进汉堡：访客进来第一件事就是它，藏进汉堡等于只留给已经知道要点菜单的人。
     汉堡里那条登录/注册继续留着，同一去处多一个入口不算错。 */
  .ms-nav__name, .ms-nav__mename { display: none; }
  /* 返回键窄屏只留箭头：chip 带着「课堂详情」这类长文字，与标题并排时会被挤到换行，
       那颗就变成独占一行（G7）。锚点上的 aria-label 不受影响 */
  .ms-backhit .ms-backlabel { display: none; }
  .ms-nav__signin { padding: 0 12px; }
  .ms-usermenu__btn { padding: 0 6px; gap: 4px; }
}

/* 平板竖屏（701-900）：字标与成员名让位，导航链接缩短内边距，顶栏仍然单行不溢出 */
@media (min-width: 701px) and (max-width: 900px) {
  .ms-nav__name, .ms-nav__mename { display: none; }
  .ms-nav__link { padding: 0 10px; }
}

/* 手机：导航的折叠规则在上面，这里只处理页面组件 */
@media (max-width: 560px) {
  .ms-item { grid-template-columns: auto minmax(0, 1fr); }
  .ms-item__act { grid-column: 1 / -1; justify-content: stretch; }
  .ms-item__act .ms-btn { flex: 1 1 auto; }
  .ms-hero__stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ms-ring { width: 84px; }
  .ms-item__idx { width: 40px; height: 40px; }
}

/* 320px 及以下：能省的水平内边距都省掉。配色键在这档不需要特判了——
   它整个走 .ms-iconbtn 的 48px 见方，原先「配色」二字与 select 的收窄规则随下拉一起删掉 */
@media (max-width: 360px) {
  .ms-nav { gap: 4px; padding: 0 8px; }
  .ms-btn--lg { padding: 0 18px; }
}

/* 平板横屏（含 1024 这一档）/ 电脑：主内容 + 侧栏两栏；竖屏继续上下堆叠 */
@media (min-width: 900px) {
  .ms-page { grid-template-columns: minmax(0, 1fr) minmax(300px, 380px); }
  .ms-page--rev { grid-template-columns: minmax(320px, 420px) minmax(0, 1fr); }
  .ms-page__side { position: sticky; top: calc(var(--ms-nav-h) + 18px); }
}

@media (min-width: 1025px) {
  .ms-strip { grid-template-columns: minmax(0, 1fr) auto; }
  .ms-auth__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ms-auth__grid .ms-span2 { grid-column: 1 / -1; }
}

/* 2560 这种大屏：正文继续居中限宽，别把一行字拉到头都看不完 */
@media (min-width: 1921px) {
  /* `:not(.ms-main--narrow)` 不是保险丝，是实测：这一条与 `:64` 的 `.ms-main--narrow{720px}` 同特异性又写在后面，
     原来会把两张提示页（`public/notice`、`public/denied`）的 720 栏拉成 1560——2560 上量到 `1560@x500`（`/tmp/equalwidth.mjs`）。
     排除之后窄栏在每一档都是 720，宽屏上限也只这一处。 */
  .ms-main:not(.ms-main--narrow) { max-width: 1560px; }
  .ms-auth { max-width: 620px; }
}

/* 登录/注册那张卡的三级宽度：520（:588）→ 620（上一条）→ 660，三级都在本文件，
   原来第三级写在 `page-shell.css` 的 2560 块里（只有那两页链接它），一条阶梯跨两份 CSS 会被读成「重复声明」。 */
@media (min-width: 2560px) {
  .ms-auth { max-width: 660px; }
}
