/*!
 * zzm.global · 赵子民设计系统 v4「纸 · 墨 · 朱」
 * https://ui.zhaozimin.cn  ·  v0.2.0（2026-09-23）  ·  锁版本地址：https://ui.zhaozimin.cn/v/0.2.0/zzm.global.css
 *
 * 用法：直接引入即可（已去掉 .zzm-v4 作用域前缀，样式对全页生效）
 * 规范：https://ui.zhaozimin.cn/DESIGN.md  ·  目录：https://ui.zhaozimin.cn/llms.txt
 * 字体：系统字体栈，无网络字体（中文 PingFang SC → Hiragino Sans GB → Microsoft YaHei）
 */

/* ════════ baseline.css · 宿主基线（Tailwind preflight 的必要子集 + 减少动效） ════════ */

/* ============================================================================
 * 宿主基线 —— ui.zhaozimin 是真源
 *
 *   edu 的 v4 组件运行在 Tailwind v4 preflight 之上：box-sizing、标题/段落零外距、图片块级……
 *   脱离 edu 单独使用时，这些「看不见的前提」缺席就会让尺寸差几个像素。
 *   这里只收组件实际依赖的最小子集，全部包在 :where() 里——零特异性，永不覆盖组件自己的样式。
 *   末段 reduced-motion 逐字取自 edu web/src/index.css（v4 的 keyframes.css 注明由它兜底）。
 * [PROTOCOL]: 变更时更新此头部，然后检查 CLAUDE.md
 * ========================================================================== */

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

:where(h1, h2, h3, h4, h5, h6, p, blockquote, figure, ul, ol, dl, dd, pre) { margin: 0; }
:where(h1, h2, h3, h4, h5, h6) { font-size: inherit; font-weight: inherit; }
:where(img, svg, video, canvas) { display: block; vertical-align: middle; }
:where(img, video) { max-width: 100%; height: auto; }
:where(button, input, select, textarea) { font: inherit; letter-spacing: inherit; color: inherit; }
:where(button) { background-color: transparent; }

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ════════ tokens.css · 设计令牌 ════════ */

/* ============================================================================
 * v4 设计令牌 —— 知识星图「纸·墨·朱」调色板（canonical: edu.zhaozimin/project/知识星图 v4.dc.html）
 *
 *   命名策略：v4 一律用 `--zzm-*` 前缀，与旧 Ollama 令牌(--accent/--background…)物理隔离，
 *   避免 v4 品牌朱色污染 shadcn 的 --accent(极淡灰 hover 面)。v4 屏引用 var(--zzm-accent,#A6402F)。
 *
 *   --zzm-accent 的运行时值由 ThemeRoot 注入(默认 #A6402F，与 DC 源 var(--accent,#A6402F) 兜底一致)；
 *   此处 :root 兜底值保证任何脱离 ThemeRoot 的 v4 片段仍有正确朱色。
 * [PROTOCOL]: 变更时更新此头部，然后检查 CLAUDE.md
 * ========================================================================== */

:root {
  /* 品牌朱（可切 #A6402F / #8C3A31 / #B5543F，ThemeRoot 覆盖）*/
  --zzm-accent: #A6402F;

  /* 面 · 底 */
  --zzm-paper: #F6F6F4;      /* 应用画布 / 侧栏底 */
  --zzm-surface: #FFFFFF;    /* 主内容面 / 卡片 */
  --zzm-ink: #161616;        /* 主文字墨色 */
  --zzm-ink-strong: #1D1D1B; /* 深墨（黑胶囊 CTA）*/

  /* 文字灰阶（由深到浅）*/
  --zzm-text-1: #26261F;
  --zzm-text-2: #3F3F3B;
  --zzm-text-3: #4A4A46;
  --zzm-text-4: #6E6E6A;
  --zzm-text-5: #72726E;
  --zzm-muted: #8B8B85;
  --zzm-muted-2: #A2A29C;
  --zzm-muted-3: #B0B0AA;
  --zzm-faint: #C6C6C0;

  /* 暗底（深色卡/封面/播放器/付款页）*/
  --zzm-dark-card: #131316;
  --zzm-dark-poster: #0E0E10;
  --zzm-dark-pay: #0B0B0C;

  /* 细线 */
  --zzm-hairline: rgba(0, 0, 0, 0.07);
  --zzm-hairline-2: rgba(0, 0, 0, 0.09);
  --zzm-hairline-strong: rgba(0, 0, 0, 0.14);

  /* ── 课时档位信号色（v21.2.0，老大定版：免费绿 / 会员黄 / 拓展红）──
     属「黑白系统里被允许的功能信号色」，同旧 index.css 的 --level-* 先例，集中于此单一真相源。
     每档三件套：实色(accent，后台段选/分组用) / 浅底(bg，用户端 tint 胶囊) / 深字(ink，tint 上的可读字色)。
     lib/courseTiers.js 引用这些令牌，前台 TierPill 与后台徽章不再各写字面色值。 */
  --zzm-tier-free: #16A34A;                  /* 免费 · 绿 */
  --zzm-tier-free-bg: rgba(22, 163, 74, 0.12);
  --zzm-tier-free-ink: #157F3C;
  --zzm-tier-member: #E0920A;                /* 会员 · 黄金 */
  --zzm-tier-member-bg: rgba(224, 146, 10, 0.18);
  --zzm-tier-member-ink: #8A5606;
  --zzm-tier-ext: #A6402F;                   /* 拓展 · 红（品牌朱同族）*/
  --zzm-tier-ext-bg: rgba(166, 64, 47, 0.11);
  --zzm-tier-ext-ink: #A6402F;

  /* 字体栈（与 DC 源一致：系统圆体优先 + CJK 兜底 + 等宽用 SF Mono）*/
  --zzm-font: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --zzm-mono: ui-monospace, "SF Mono", Menlo, monospace;

  /* ── 间距梯（8pt 网格 + 4pt 微调；canonical: styles/v4/SPACING.md，审计器 npm run audit:spacing）──
     一切 padding/margin/gap/组件高度/行高从梯上取值——一体感来自全站共用同一把梯子 */
  --zzm-s-1: 4px;   /* 微调 */
  --zzm-s-2: 8px;   /* 元素内 */
  --zzm-s-3: 12px;  /* 紧凑 */
  --zzm-s-4: 16px;  /* 标准 */
  --zzm-s-5: 20px;  /* 舒适 */
  --zzm-s-6: 24px;  /* 区块内 */
  --zzm-s-8: 32px;  /* 区块间 */
  --zzm-s-10: 40px; /* 大区块 */
  --zzm-s-12: 48px; /* 节内距 */
  --zzm-s-16: 64px; /* 节间距 */

  /* ── 圆角梯：同层级同档（4 微件 / 8 小控件 / 12 内嵌卡 / 16 卡片 / 999 胶囊）── */
  --zzm-r-1: 4px;
  --zzm-r-2: 8px;
  --zzm-r-3: 12px;
  --zzm-r-4: 16px;
  --zzm-r-pill: 999px;
}

/* ════════ keyframes.css · 关键帧 ════════ */

/* ============================================================================
 * v4 关键帧 —— 逐字移植自 DC 源 <helmet>\3c style>（知识星图 v4.dc.html 行 22-31）
 *   全部 zzm 前缀，全局注册无副作用。v4 屏以 inline `animation:` 引用。
 *   reduced-motion 由旧 index.css 的全局媒体查询兜底关停，无需在此重复。
 * [PROTOCOL]: 变更时更新此头部，然后检查 CLAUDE.md
 * ========================================================================== */

@keyframes zzmPulse { 0%, 100% { opacity: .15 } 50% { opacity: .55 } }
@keyframes zzmIn    { from { opacity: 0; transform: translateY(12px) } to { opacity: 1; transform: none } }
@keyframes zzmUp    { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }
@keyframes zzmSlide { from { opacity: 0; transform: translateX(-14px) } to { opacity: 1; transform: none } }
@keyframes zzmGrow  { from { max-height: 0; opacity: 0 } to { max-height: 960px; opacity: 1 } }
@keyframes zzmSpring {
  0%   { transform: translateX(0) }
  38%  { transform: translateX(150px) }
  62%  { transform: translateX(128px) }
  80%  { transform: translateX(142px) }
  92%  { transform: translateX(136px) }
  100% { transform: translateX(138px) }
}
@keyframes zzmIn2   { from { opacity: 0; transform: translateY(12px) } to { opacity: 1; transform: none } }
@keyframes zzmUp2   { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }
@keyframes zzmFade  { from { opacity: 0 } to { opacity: 1 } }
@keyframes zzmPayIn { from { opacity: 0; transform: translateY(20px) scale(.985) } to { opacity: 1; transform: none } }
@keyframes zzmShimmer { 100% { transform: translateX(100%) } }
@keyframes zzmBtnSpin { to { transform: rotate(360deg) } }
/* 一道反光扫过（时不时闪一下）：快扫一次(0→14%)后长歇——档位胶囊 .zzm-pill-shine 与 saving 键
   .zzm-btn--busy 共用这一条，同一视觉语言。reduced-motion 由 index.css 全局兜底关停。 */
@keyframes zzmPillShine { 0% { transform: translateX(-180%) } 14% { transform: translateX(180%) } 100% { transform: translateX(180%) } }

/* ════════ base.css · 画布 · 选区 · 滚动条 · 骨架 · 悬停助手 ════════ */

/* ============================================================================
 * v4 基础样式 —— 移植自 DC 源 <helmet>\3c style>（行 15-35），但【作用域收敛到 .zzm-v4】
 *   旧 index.css 已有全局滚动条/selection/背景；为避免过渡期与旧 App/shadcn 打架，
 *   v4 的画布底、朱色 selection、占位符灰、滚动条一律限定在 .zzm-v4 子树内。
 *   暗色走 DC 的 invert 反色 trick（data-theme=dark），img/video/canvas 反反色还原。
 * [PROTOCOL]: 变更时更新此头部，然后检查 CLAUDE.md
 * ========================================================================== */

body {
  background: var(--zzm-paper);
  color: var(--zzm-ink);
  font-family: var(--zzm-font);
  -webkit-font-smoothing: antialiased;
}

::selection { background: rgba(166, 64, 47, .16); }
input::placeholder { color: #B4B4AF; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, .14);
  border-radius: 8px;
  border: 3px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-track { background: transparent; }

/* 暗色反色 trick（DC 行 32-35）：整树反相 + 色相翻转，媒体/画布二次反相还原真色 */
body[data-theme="dark"] { filter: invert(1) hue-rotate(180deg); }
body[data-theme="dark"] img,
body[data-theme="dark"] video,
body[data-theme="dark"] canvas { filter: invert(1) hue-rotate(180deg); }
body[data-theme="dark"] .zzm-splash { filter: invert(1) hue-rotate(180deg); }
body[data-theme="dark"] .zzm-splash canvas { filter: none; }

/* ── 悬停助手类（还原 DC 的 style-hover，用真伪类而非 JS，作用域 .zzm-v4）── */
.zzm-hovercard { transition: all .16s ease; }
.zzm-hovercard:hover {
  transform: translateY(-3px);
  border-color: rgba(0, 0, 0, .3) !important;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .07);
}
.zzm-pill { transition: all .15s ease; }
.zzm-pill:hover { border-color: rgba(0, 0, 0, .35) !important; }
.zzm-node { transition: opacity .15s ease; }
.zzm-node:hover { opacity: .75; }
.zzm-dark-btn:hover { background: #000000 !important; }


/* ── 侧栏/通用悬停（app-shell/Sidebars 及各屏族共用；命名=行为，勿按屏命名）── */
.zzm-navbtn:hover { background: rgba(0, 0, 0, .07) !important; }
.zzm-iconbtn:hover { background: rgba(0, 0, 0, .07); }
.zzm-userfoot:hover { background: rgba(0, 0, 0, .045); border-color: rgba(0, 0, 0, .07) !important; }
.zzm-ghostline:hover { border-color: rgba(0, 0, 0, .3) !important; color: #161616; }
.zzm-ghostnav:hover { background: rgba(0, 0, 0, .07); color: #161616; }
.zzm-ghostnav-lite:hover { background: rgba(0, 0, 0, .07); color: #161616; }
.zzm-fadelink:hover { color: #8B8B85; }
/* 灰字文字链（components/v4/Breadcrumb 消费）：基色随父容器继承——消费方禁写内联 color，否则压死 :hover；
   键盘焦点用全站统一的朱色光晕（与 .zzm-btn 同款），不落回浏览器默认环 */
.zzm-inklink { color: inherit; transition: color .16s ease; }
.zzm-inklink:hover { color: #161616; }
.zzm-inklink:focus-visible { outline: none; border-radius: 4px; box-shadow: 0 0 0 3px rgba(166, 64, 47, .2); }
.zzm-lift { transition: transform .16s ease; }
.zzm-lift:hover { transform: translateY(-2px); }
.zzm-fade86:hover { opacity: .86; }
.zzm-fade88:hover { opacity: .88; }
.zzm-rowhover:hover { background: rgba(0, 0, 0, .04); }
.zzm-rowhover-lite:hover { background: rgba(0, 0, 0, .03); }
.zzm-hairbtn:hover { border-color: #161616 !important; color: #161616; }
.zzm-accentlink:hover { text-decoration-color: var(--zzm-accent, #A6402F) !important; color: var(--zzm-accent, #A6402F); }
input.zzm-input:focus, textarea.zzm-input:focus { border-color: #161616 !important; }
input.zzm-input-accent:focus { border-color: var(--zzm-accent, #A6402F) !important; }

/* ── 骨架屏（数据加载占位；components/v4/Skeleton 消费）：纸灰底 + 微光扫过 ── */
.zzm-skel {
  position: relative;
  overflow: hidden;
  background: #EFEFEC;
  border-radius: 8px;
}
.zzm-skel::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .65), transparent);
  animation: zzmShimmer 1.4s ease infinite;
}

/* ════════ controls.css · 微拟物质感类（设计系统的灵魂） ════════ */

/* ============================================================================
 * v4 微拟物控件语言（skeuomorphic controls）—— 设计系统的灵魂
 *
 *   物理模型：光从正上方来。每个可交互控件都是一枚「真实的键」——
 *     ① 凸面     竖向微渐变（顶亮→底沉），面是鼓起来的
 *     ② 顶部反光 inset 高光线（键面上缘接住光）
 *     ③ 贴地投影 两层落影（近影锐、远影柔），键浮在纸面上
 *     ④ 反光光晕 外圈 0 0 0 Npx 高光环（键周围一圈反光），hover 时变亮变宽
 *     ⑤ 按压内陷 :active 渐变翻转 + inset 阴影——按下去真的「凹」
 *
 *   铁律：
 *     · 按钮一律【圆角矩形】（sm/md=8px · lg=12px），禁胶囊(999)——胶囊只留给非交互徽章
 *     · 光影只从本文件的质感类取（zzm-btn / zzm-card / zzm-input），禁止散写 boxShadow——
 *       微拟物的一体感来自全站共用同一套光照模型，各写各的光源方向必然穿帮
 *     · backdrop-filter 仍然全域禁用（性能）；渐变/阴影仅经本文件的类生效
 *
 *   变体：默认纸键 / --primary 墨键 / --accent 朱键 / --ghost 幽灵(hover 才成键) / --ondark 暗面键
 *   尺寸：--sm h28 / 默认 h36 / --lg h44；--icon 方形；Shadcn/Radix 确认窗亦在此复用纸面/朱砂/键感
 * [PROTOCOL]: 变更时更新此头部，然后检查 CLAUDE.md
 * ========================================================================== */

/* ── 基键（纸面键）── */
.zzm-btn {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 36px;
  padding: 0 16px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .2px;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  border-radius: 8px;                                   /* 圆角矩形——按钮禁胶囊 */
  border: 1px solid rgba(0, 0, 0, .14);
  color: #3F3F3B;
  background: linear-gradient(180deg, #FFFFFF 0%, #F2F2EF 100%);
  box-shadow:
    0 0 0 3px rgba(255, 255, 255, .55),                 /* ④ 反光光晕环 */
    0 1px 2px rgba(0, 0, 0, .09),                       /* ③ 近影 */
    0 2px 6px rgba(0, 0, 0, .06),                       /* ③ 远影 */
    inset 0 1px 0 rgba(255, 255, 255, .92),             /* ② 顶部反光 */
    inset 0 -1px 0 rgba(0, 0, 0, .05);                  /* ① 底缘收沉 */
  transition: box-shadow .16s ease, transform .16s ease, background .16s ease,
    border-color .16s ease, filter .16s ease, opacity .16s ease;
}
.zzm-btn:hover {
  transform: translateY(-1px);
  border-color: rgba(0, 0, 0, .18);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, .8),
    0 2px 4px rgba(0, 0, 0, .10),
    0 4px 12px rgba(0, 0, 0, .09),
    inset 0 1px 0 rgba(255, 255, 255, .95),
    inset 0 -1px 0 rgba(0, 0, 0, .05);
}
.zzm-btn:active {
  transform: translateY(0);
  background: linear-gradient(180deg, #ECECE9 0%, #F4F4F1 100%);   /* ⑤ 凹面翻转 */
  box-shadow:
    0 0 0 3px rgba(255, 255, 255, .45),
    0 1px 0 rgba(255, 255, 255, .65),
    inset 0 2px 4px rgba(0, 0, 0, .13),
    inset 0 1px 2px rgba(0, 0, 0, .09);
}
.zzm-btn:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 3px rgba(166, 64, 47, .20),                   /* 键盘焦点＝朱色光晕 */
    0 1px 2px rgba(0, 0, 0, .09),
    0 2px 6px rgba(0, 0, 0, .06),
    inset 0 1px 0 rgba(255, 255, 255, .92);
}
/* ── 禁用（非等待）：灰面配灰字——任何变体禁用后都读得清。
      不再用 opacity 淡化整键：变体白字 × 半透明 × 灰面 ＝ 白字隐形事故（后台登录「验证中」曾不可读） ── */
.zzm-btn:disabled {
  cursor: not-allowed;
  transform: none;
}
.zzm-btn:disabled:not(.zzm-btn--busy) {
  color: #A2A29C;
  background: #F2F2EF;
  border-color: rgba(0, 0, 0, .08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7), 0 1px 2px rgba(0, 0, 0, .04);
}
.zzm-btn--ondark:disabled:not(.zzm-btn--busy) {
  color: rgba(255, 255, 255, .45);
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .16);
  box-shadow: none;
}

/* ── Saving / 等待键（--busy）＝设计系统原始按钮本体，只叠加三样，不改基座（v21.2.0 老大定版）：
      ① 一道反光扫过——与档位胶囊同一条 zzmPillShine 关键帧（::after 覆盖整键面、快扫一次后长歇＝
         「时不时闪一下」，非呼吸光晕）；② 不可点击——pointer-events:none（Btn 组件 disabled 亦已禁）；
      ③ 饱和度略低——filter:saturate(.72)（比普通键偏低，朱键最明显、墨键近灰变化细微）。
      变体色相/渐变/边框/五层光影一概不动（墨键仍墨、朱键仍朱）；::before 旋转环保持不变。
      overflow:hidden 只裁剪反光覆盖层，不裁外圈光影（box-shadow/focus 环不受 overflow 裁剪）。 ── */
.zzm-btn--busy {
  position: relative;
  overflow: hidden;
  pointer-events: none;
  opacity: .92;
  filter: saturate(.72);
  transform: none;
}
.zzm-btn--busy::before {
  content: "";
  position: relative;
  z-index: 1;                                            /* 旋转环压在反光层之上 */
  width: 12px;
  height: 12px;
  flex: none;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  opacity: .7;
  animation: zzmBtnSpin .7s linear infinite;
}
/* 反光层：斜向半透明白，从 -180% 扫到 180%（zzmPillShine），一道光从键面掠过 */
.zzm-btn--busy::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  transform: translateX(-180%);
  background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, .45) 50%, transparent 60%);
  animation: zzmPillShine 4.4s ease-in-out infinite;
}

/* ── 墨键（主操作·深墨）── */
.zzm-btn--primary {
  color: #FFFFFF;
  border-color: rgba(0, 0, 0, .85);
  background: linear-gradient(180deg, #3A3A36 0%, #1D1D1B 58%, #141412 100%);
  box-shadow:
    0 0 0 3px rgba(22, 22, 22, .06),
    0 1px 2px rgba(0, 0, 0, .18),
    0 3px 10px rgba(22, 22, 22, .24),
    inset 0 1px 0 rgba(255, 255, 255, .20),
    inset 0 -1px 0 rgba(0, 0, 0, .38);
}
.zzm-btn--primary:hover {
  filter: brightness(1.12);
  border-color: rgba(0, 0, 0, .85);
  box-shadow:
    0 0 0 4px rgba(22, 22, 22, .09),
    0 2px 4px rgba(0, 0, 0, .20),
    0 5px 14px rgba(22, 22, 22, .28),
    inset 0 1px 0 rgba(255, 255, 255, .24),
    inset 0 -1px 0 rgba(0, 0, 0, .38);
}
.zzm-btn--primary:active {
  filter: none;
  background: linear-gradient(180deg, #101010 0%, #22221F 100%);
  box-shadow:
    0 0 0 3px rgba(22, 22, 22, .06),
    inset 0 2px 5px rgba(0, 0, 0, .55),
    inset 0 1px 2px rgba(0, 0, 0, .4);
}

/* ── 朱键（品牌强调·会员/播放/危险动作）── */
.zzm-btn--accent {
  color: #FFFFFF;
  border-color: rgba(122, 42, 29, .9);
  background: linear-gradient(180deg, #C25743 0%, #A6402F 56%, #99392A 100%);
  box-shadow:
    0 0 0 3px rgba(166, 64, 47, .10),                   /* 朱色反光光晕 */
    0 1px 2px rgba(0, 0, 0, .14),
    0 3px 10px rgba(166, 64, 47, .28),
    inset 0 1px 0 rgba(255, 255, 255, .32),
    inset 0 -1px 0 rgba(0, 0, 0, .20);
}
.zzm-btn--accent:hover {
  filter: brightness(1.06);
  border-color: rgba(122, 42, 29, .9);
  box-shadow:
    0 0 0 4px rgba(166, 64, 47, .16),
    0 2px 4px rgba(0, 0, 0, .16),
    0 5px 14px rgba(166, 64, 47, .34),
    inset 0 1px 0 rgba(255, 255, 255, .36),
    inset 0 -1px 0 rgba(0, 0, 0, .20);
}
.zzm-btn--accent:active {
  filter: none;
  background: linear-gradient(180deg, #8E3425 0%, #A03D2C 100%);
  box-shadow:
    0 0 0 3px rgba(166, 64, 47, .10),
    inset 0 2px 5px rgba(0, 0, 0, .34),
    inset 0 1px 2px rgba(0, 0, 0, .24);
}

/* ── 幽灵键（工具条/次级导航）：静默是文字，hover 才「长成键」── */
.zzm-btn--ghost {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  color: #4A4A46;
}
.zzm-btn--ghost:hover {
  transform: translateY(-1px);
  border-color: rgba(0, 0, 0, .14);
  background: linear-gradient(180deg, #FFFFFF 0%, #F2F2EF 100%);
  box-shadow:
    0 0 0 3px rgba(255, 255, 255, .55),
    0 1px 2px rgba(0, 0, 0, .09),
    0 2px 6px rgba(0, 0, 0, .06),
    inset 0 1px 0 rgba(255, 255, 255, .92);
}
.zzm-btn--ghost:active {
  transform: translateY(0);
  border-color: rgba(0, 0, 0, .14);
  background: linear-gradient(180deg, #ECECE9 0%, #F4F4F1 100%);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .12);
}

/* ── 暗面键（深色面上的键：付款页/播放器锁罩/海报态）── */
.zzm-btn--ondark {
  color: #FFFFFF;
  border-color: rgba(255, 255, 255, .28);
  background: linear-gradient(180deg, rgba(255, 255, 255, .16) 0%, rgba(255, 255, 255, .06) 100%);
  box-shadow:
    0 0 0 3px rgba(255, 255, 255, .05),
    0 2px 8px rgba(0, 0, 0, .4),
    inset 0 1px 0 rgba(255, 255, 255, .28),
    inset 0 -1px 0 rgba(0, 0, 0, .3);
}
.zzm-btn--ondark:hover {
  border-color: rgba(255, 255, 255, .4);
  background: linear-gradient(180deg, rgba(255, 255, 255, .22) 0%, rgba(255, 255, 255, .10) 100%);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, .08),
    0 3px 10px rgba(0, 0, 0, .45),
    inset 0 1px 0 rgba(255, 255, 255, .34),
    inset 0 -1px 0 rgba(0, 0, 0, .3);
}
.zzm-btn--ondark:active {
  background: linear-gradient(180deg, rgba(0, 0, 0, .28) 0%, rgba(255, 255, 255, .04) 100%);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, .5);
}

/* ── 视频启动键：唯一允许为圆形的交互键 ───────────────────────────────
   圆形不是装饰例外，而是「点按即开始播放」这个媒体语义的通用符号；导学封面与
   所有课时海报必须共用这一原件，避免同一动作在不同页面长成不同按钮。
   其余文字按钮仍遵循 8/12 圆角矩形，禁止借此扩散成胶囊。 ── */
.zzm-video-play {
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  padding: 0;
  border: 1px solid rgba(0, 0, 0, .16);
  border-radius: 50%;
  cursor: pointer;
  color: #1D1D1B;
  background: linear-gradient(180deg, #FFFFFF 0%, #F2F2EF 100%);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, .24),
    0 2px 4px rgba(0, 0, 0, .16),
    0 6px 16px rgba(0, 0, 0, .22),
    inset 0 1px 0 rgba(255, 255, 255, .96),
    inset 0 -1px 0 rgba(0, 0, 0, .08);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.zzm-video-play--center {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.zzm-video-play:hover {
  border-color: rgba(0, 0, 0, .3);
  box-shadow:
    0 0 0 5px rgba(255, 255, 255, .3),
    0 3px 6px rgba(0, 0, 0, .18),
    0 9px 22px rgba(0, 0, 0, .28),
    inset 0 1px 0 rgba(255, 255, 255, .98),
    inset 0 -1px 0 rgba(0, 0, 0, .08);
}
.zzm-video-play--center:hover { transform: translate(-50%, -50%) scale(1.06); }
.zzm-video-play:active {
  transform: scale(.98);
  background: linear-gradient(180deg, #E9E9E6 0%, #F4F4F1 100%);
  box-shadow:
    0 0 0 4px rgba(255, 255, 255, .24),
    inset 0 2px 5px rgba(0, 0, 0, .16),
    inset 0 1px 2px rgba(0, 0, 0, .10);
}
.zzm-video-play--center:active { transform: translate(-50%, -50%) scale(.98); }
.zzm-video-play:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 4px rgba(166, 64, 47, .28),
    0 2px 4px rgba(0, 0, 0, .16),
    0 6px 16px rgba(0, 0, 0, .22),
    inset 0 1px 0 rgba(255, 255, 255, .96);
}
.zzm-video-play__icon {
  width: 24px;
  height: 24px;
  margin-left: 4px;
  fill: currentColor;
}

/* ── 尺寸（高度落 4pt 网格：28/36/44）── */
.zzm-btn--sm { height: 28px; padding: 0 12px; font-size: 12px; gap: 4px; }
.zzm-btn--lg { height: 44px; padding: 0 24px; font-size: 14px; border-radius: 12px; gap: 8px; }
.zzm-btn--icon { width: 36px; padding: 0; }
.zzm-btn--icon.zzm-btn--sm { width: 28px; }
.zzm-btn--icon.zzm-btn--lg { width: 44px; }
.zzm-btn--block { width: 100%; }

/* ── 主题化确认窗（Shadcn/Radix Dialog）──────────────────────────────────
   浏览器原生 confirm 没有产品的层级、语言和键感；所有后台确认统一经过
   DialogProvider(appearance="v4")。Portal 仍挂在 .zzm-v4 内，故此处可复用
   同一纸面、朱砂、凸键和内凹输入槽，而不是另造一套浮窗皮肤。
   遮罩只降亮度，不模糊：背景仍可辨认当前操作上下文。 ── */
.zzm-dialog-overlay {
  background: rgba(22, 22, 22, .46);
}
.zzm-dialog-panel {
  width: min(calc(100% - 32px), 440px);
  max-width: 440px;
  gap: 16px;
  padding: 24px;
  border: 1px solid rgba(0, 0, 0, .14);
  border-radius: 16px;
  background: #FFFFFF;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, .10),
    0 18px 48px rgba(0, 0, 0, .22),
    inset 0 1px 0 rgba(255, 255, 255, .92);
}
.zzm-dialog-title {
  color: #1D1D1B;
  font-size: 17px;
  font-weight: 700;
  line-height: 24px;
}
.zzm-dialog-description {
  color: #6E6E68;
  font-size: 13px;
  line-height: 20px;
}
input.zzm-dialog-input {
  width: 100%;
}
.zzm-dialog-actions {
  margin-top: 4px;
  gap: 8px;
}

/* ── 可点卡链（zzm-cardlink）：给「裸缩略图+文字」的课节/历史/收藏卡穿上白卡承托，
      悬停＝高亮描边 + 上浮 + 柔影——与综合首页课程卡(zzm-hovercard)同一手感,全站卡片一个词汇。
      内容内嵌圆角取 12(卡 16 内嵌 12,圆角梯嵌套律) ── */
.zzm-cardlink {
  background: #FFFFFF;
  border: 1px solid rgba(0, 0, 0, .09);
  border-radius: 16px;
  padding: 12px;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.zzm-cardlink:hover {
  transform: translateY(-3px);
  border-color: rgba(0, 0, 0, .3);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .09), 0 1px 2px rgba(0, 0, 0, .04);
}

/* ── 已完成课节卡（--done）：容器级完成态——整卡纸面朱染 + 朱描边，一眼可辨，
      取代右上角 18px ✓ 小圆徽（用户不必费力找小标识）。色值＝朱色低透明派生：
      底 .04（比目录当前行 .06 低一档，大面积 tint 更淡）/ 边 .35（zzm-vdrag hover 同系数）。
      必须走类而非 inline borderColor——inline 会永久压过 :hover 的描边高亮（specificity） ── */
.zzm-cardlink--done {
  background: rgba(166, 64, 47, .04);
  border-color: rgba(166, 64, 47, .35);
}
.zzm-cardlink--done:hover {
  border-color: rgba(166, 64, 47, .55);
}

/* ── 微拟物卡片：白面浮在纸上（1px 细线仍在，光影让层级立起来）── */
.zzm-card {
  background: #FFFFFF;
  border: 1px solid rgba(0, 0, 0, .09);
  border-radius: 16px;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, .04),
    0 6px 18px rgba(0, 0, 0, .05),
    inset 0 1px 0 rgba(255, 255, 255, .85);
}

/* ── 输入内凹（既有 .zzm-input 全站自动升级：键是凸的，输入槽是凹的）── */
input.zzm-input,
textarea.zzm-input {
  background: #FDFDFC;
  box-shadow:
    inset 0 1.5px 3px rgba(0, 0, 0, .06),
    inset 0 1px 1px rgba(0, 0, 0, .04);
}
input.zzm-input:focus,
textarea.zzm-input:focus {
  box-shadow:
    inset 0 1.5px 3px rgba(0, 0, 0, .06),
    0 0 0 3px rgba(22, 22, 22, .07);                    /* 聚焦光晕 */
}
input.zzm-input-accent:focus {
  box-shadow:
    inset 0 1.5px 3px rgba(0, 0, 0, .06),
    0 0 0 3px rgba(166, 64, 47, .14);
}

/* ── 档位胶囊闪光（.zzm-pill-shine）：一道反光时不时从名牌上扫过（与 saving 键 --busy 同一条
      zzmPillShine 关键帧）。胶囊几何/配色由 JSX inline 出（TierPill 读 lib/courseTiers）；本类只负责
      裁剪 + 反光 ::after。错峰靠调用方经 CSS 变量 --zzm-shine-delay 逐卡设不同延时，避免齐刷。 ── */
.zzm-pill-shine {
  position: relative;
  overflow: hidden;
}
.zzm-pill-shine::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  transform: translateX(-180%);
  background: linear-gradient(105deg, transparent 34%, rgba(255, 255, 255, .85) 50%, transparent 66%);
  animation: zzmPillShine 4.6s ease-in-out infinite;
  animation-delay: var(--zzm-shine-delay, 0s);
}

/* ════════ extracted.css · 提炼层（样张组件的光影 / 伪类 / 确认窗定位） ════════ */

/* ============================================================================
 * 提炼层样式 —— ui.zhaozimin 是真源（edu 里只有 AdmDesign 的 inline 样张，没有独立组件）
 *
 *   只放三类东西，其余一律 inline（与 v4 组件同一写法）：
 *     ① 光影：光影铁律要求阴影只从质感类取——样张里散写的 boxShadow 在此收编成类
 *     ② 伪类：hover / focus-visible（inline style 写不了伪类）
 *     ③ 布局补齐：确认窗在 edu 里由 shadcn dialog.jsx 的 Tailwind 工具类定位，这里用纯 CSS 补齐，
 *        使 .zzm-dialog-* 脱离 Tailwind 也能 1:1 成立
 *   键盘焦点一律朱色光晕 0 0 0 3px rgba(166,64,47,.2)——与 .zzm-btn:focus-visible 同源。
 * [PROTOCOL]: 变更时更新此头部，然后检查 CLAUDE.md
 * ========================================================================== */

@keyframes zzmDialogIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(.96) }
  to   { opacity: 1; transform: translate(-50%, -50%) }
}

/* ── 确认窗：遮罩只降亮不模糊；面板居中浮起（皮肤仍由 controls.css 的 .zzm-dialog-* 提供）── */
.zzm-dialog-overlay { position: fixed; inset: 0; z-index: 50; animation: zzmFade .2s ease both; }
.zzm-dialog-panel {
  position: fixed; top: 50%; left: 50%; z-index: 51; display: grid;
  transform: translate(-50%, -50%); animation: zzmDialogIn .2s cubic-bezier(.2, .7, .2, 1) both;
}
.zzm-dialog-panel--inline { position: relative; top: auto; left: auto; z-index: auto; transform: none; animation: none; }
.zzm-dialog-header { display: grid; gap: 8px; }
.zzm-dialog-title,
.zzm-dialog-description { margin: 0; }
.zzm-dialog-actions { display: flex; justify-content: flex-end; }

/* ── 轻提示：深墨胶囊（非交互，允许 999）；阴影收编自样张 ── */
.zzm-toast {
  display: inline-block; background: #1D1D1B; color: #FFFFFF; font-size: 12px;
  padding: 9px 18px; border-radius: 999px; white-space: nowrap;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .3);
  animation: zzmUp .25s ease both;
}

/* ── 开关：滑块落影收编成类 ── */
.zzm-switch__knob { box-shadow: 0 1px 3px rgba(0, 0, 0, .25); }
.zzm-switch:disabled .zzm-switch__knob { box-shadow: 0 1px 3px rgba(0, 0, 0, .14); }

/* ── 标签页：悬停提墨（同 .zzm-inklink 的灰→墨） ── */
.zzm-tab { transition: color .16s ease; }
.zzm-tab:hover { color: #161616 !important; }

/* ── 分页：描边格悬停加深（同 .zzm-ghostline 的 .3 描边） ── */
.zzm-page { transition: border-color .16s ease; }
.zzm-page:not(:disabled):hover { border-color: rgba(0, 0, 0, .3) !important; }
.zzm-page:disabled { cursor: not-allowed; }

/* ── 表格表头排序键：悬停提墨 ── */
.zzm-sort { transition: color .16s ease; }
.zzm-sort:hover { color: #161616 !important; }

/* ── 统一键盘焦点 ── */
.zzm-switch:focus-visible,
.zzm-check:focus-visible,
.zzm-tab:focus-visible,
.zzm-page:focus-visible,
.zzm-sort:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(166, 64, 47, .2);
}
