/* =============================================================================
   排课系统 · 设计系统层（design-system.css）
   -----------------------------------------------------------------------------
   定位：**叠加层**，必须排在 style.css 之后加载（index.html 中已保证顺序）。
   语言：现代精致企业风 —— 语义化令牌 + 玻璃拟态 + 微动效。
        日常教务 / 课表保持「亮色专业」观感（阳光分班大屏另走深色旗舰，见 P3）。

   改本文件前必读的铁律
   -----------------------------------------------------------------------------
   1. **只改「表现」，绝不改「布局」**：禁止修改 display / position / width /
      height / padding / margin / flex / grid / overflow 等影响盒子模型的属性。
      课表网格、手动调课表、合班面板的布局是历史踩坑冻结过的，动一个像素就可能
      越界或压栏。
      允许：background / color / border / border-radius / box-shadow /
            backdrop-filter / transition / transform(仅 hover 微交互) /
            font-weight / letter-spacing / gradient / filter。
   2. **令牌优先**：改整体观感优先改 :root 令牌，所有引用 var() 的规则自动跟随，
      避免逐条覆盖导致的风格分裂。
   3. **ac2-* 覆盖必须提权**：教务域样式由 academic.js **运行时注入** <style
      id="acad2-style">，加载晚于本文件、同特异性下会赢。故覆盖统一使用
      `body .ac2-xxx`（0,1,1）压过 `.ac2-xxx`（0,1,0）。
   4. **令牌单权威**：全站 `:root` 令牌**只在本文件定义**。style.css 的
      `:root` 已于 #1172 清空（仅留指针注释 + 使用点兜底值）。新增令牌一律
      写这里，禁止在 style.css / 内联 style / JS 里再定义 `--*`。
      判据工具：`python _audit_tokens.py` → 期望 `TOKEN_AUDIT_OK`。
   5. **零依赖**：不引入三方库、不依赖构建；原生 CSS 变量，可平滑演进数十年。

   -----------------------------------------------------------------------------
   变更记录
   -----------------------------------------------------------------------------
   #1172 合并两套 :root —— style.css 的 33 个变量并入本文件（15 个冲突取本文件
         值、3 个独有变量 --sidebar-width/--sidebar-collapsed/--font-family 迁入）。
   #1173 补齐间距 10 档 / 字号+行高 9 对 / 字重 4 档 / 层级 12 级。
   #1174 圆角七档制（xs4 / sm8 / md12 / lg16 / xl20 / 2xl24 / full999）。
   #1175 新增第 11 节「基础组件层（.ds-*）」—— 卡片 / 表格 / 空态 / 页头 /
         步骤条 / 按钮 / 徽章 / 指标卡，全部走令牌、0 硬编码。
         配套 JS：frontend/js/ds-components.js（window.DS，纯字符串零依赖）。
   ============================================================================= */

/* ═══════════════════════════════════════════════════════════════════════
   1. 令牌层（语义化 · 升级既有 + 新增未来令牌）
   ═══════════════════════════════════════════════════════════════════════ */
:root {
    /* ── 品牌色（保留 indigo 品牌资产，提升纯度与层次） ── */
    --primary: #4f46e5;
    --primary-light: #818cf8;
    --primary-dark: #3f37c9;
    --primary-gradient: linear-gradient(135deg, #5b6ef5 0%, #8b5cf6 100%);
    --primary-gradient-soft: linear-gradient(135deg, rgba(91, 110, 245, .10) 0%, rgba(139, 92, 246, .10) 100%);

    /* ── 语义色 ── */
    --accent: #0891b2;
    --accent-gradient: linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%);
    --success: #10b981;
    --success-deep: #059669;
    --warning: #f59e0b;
    --warning-deep: #d97706;
    --danger: #ef4444;
    --danger-deep: #dc2626;
    --info: #3b82f6;

    /* ── 中性面（更冷、更柔的现代企业底） ── */
    --bg: #eef1f7;
    --bg-page: #f4f6fb;
    --bg-card: #ffffff;
    --bg-card-solid: #ffffff;
    --bg-subtle: #f8fafc;
    --bg-sidebar: #0f172a;
    --bg-sidebar-grad: linear-gradient(180deg, #101a31 0%, #0b1220 100%);

    /* ── 文字 ── */
    --text: #0f172a;
    --text-light: #64748b;
    --text-muted: #94a3b8;
    --text-sidebar: #cbd5e1;
    --text-sidebar-dim: #94a3b8;

    /* ── 描边 ── */
    --border: #e4e8f0;
    --border-light: #eef1f6;
    --border-strong: #d7dde8;

    /* ── 阴影（多层柔光，取代单层硬阴影） ── */
    --shadow: 0 1px 2px -1px rgba(15, 23, 42, .05), 0 1px 3px rgba(15, 23, 42, .05);
    --shadow-md: 0 4px 6px -2px rgba(15, 23, 42, .05), 0 10px 24px -6px rgba(15, 23, 42, .10);
    --shadow-lg: 0 10px 20px -6px rgba(15, 23, 42, .10), 0 24px 48px -12px rgba(15, 23, 42, .16);
    --shadow-glow: 0 0 0 1px rgba(99, 102, 241, .14), 0 8px 24px -4px rgba(99, 102, 241, .28);
    --shadow-inset: inset 0 1px 0 rgba(255, 255, 255, .6);

    /* ── 布局 / 字体（#1172 自 style.css 迁入 —— 本文件为唯一权威） ──
       ⚠️ 这三个令牌历史上定义在 style.css，而引用方也在 style.css，导致
          「新体系反向依赖旧文件」。迁到此处后 style.css 侧改用带兜底的
          var(--x, 兜底值)，即使本文件加载失败布局也不会塌。            */
    --sidebar-width: 244px;
    --sidebar-collapsed: 72px;
    --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;

    /* ── 圆角（#1174 · 七档制 · 全站唯一权威）──────────────────────
       取值依据：style.css 历史硬编码分布（8px×46 / 10px×34 / 6px×27 /
       12px×24 / 4px×18 / 11px×11 …），按「就近归并、单点跳变 ≤2px」定档。
       ⚠️ 旧值 --radius 14 / --radius-sm 10 / --radius-lg 18 各归到临近档
          位（12 / 8 / 16），最大跳变 2px，肉眼不可辨；--radius-xl 的 3 处
          引用（modal / login-card / pk-hero）已显式改指 --radius-2xl，
          保持 24px 不变。旧别名 --radius 的 13 处引用已全部直连 --radius-md
          并删除别名 —— 全站零死令牌。                                 */
    --radius-xs:   4px;    /* 微元素：dot / 角标 / 进度条 / 小标签 */
    --radius-sm:   8px;    /* 控件：button / input / chip / tag / 单元格 */
    --radius-md:  12px;    /* 小容器：列表项 / 卡片内块 / 分段控件 */
    --radius-lg:  16px;    /* 容器：card / panel / dropzone */
    --radius-xl:  20px;    /* 大容器：section / side-panel */
    --radius-2xl: 24px;    /* 旗舰：modal / login / hero */
    --radius-full: 999px;  /* 胶囊（正圆请直接写 50%，勿用本档）*/

    /* ── 间距（#1173 · 4px 基准 · 10 档）────────────────────────────
       只声明不使用：现存 2278 处硬编码 px 本轮不动（改 padding/margin
       = 改布局，违反本文件铁律 1）。新写的组件一律走这组令牌。        */
    --space-0:  0;
    --space-1:  4px;
    --space-2:  8px;
    --space-3:  12px;
    --space-4:  16px;
    --space-5:  20px;
    --space-6:  24px;
    --space-8:  32px;
    --space-10: 40px;
    --space-12: 48px;

    /* ── 字号 / 行高（#1173 · 成对使用，避免行高漂移）──────────────
       ⚠️ 命名易混警告：--text / --text-light / --text-muted /
          --text-sidebar* 是「颜色」令牌；本组是「字号」令牌，不是一回事。 */
    --font-size-3xs: 10px;   --leading-3xs: 1.5;
    --font-size-2xs: 11px;   --leading-2xs: 1.5;
    --font-size-xs:  12px;   --leading-xs:  1.6;
    --font-size-sm:  13px;   --leading-sm:  1.6;
    --font-size-md:  14px;   --leading-md:  1.6;
    --font-size-lg:  16px;   --leading-lg:  1.5;
    --font-size-xl:  20px;   --leading-xl:  1.4;
    --font-size-2xl: 26px;   --leading-2xl: 1.3;
    --font-size-3xl: 32px;   --leading-3xl: 1.25;

    /* ── 字重（#1173）── */
    --weight-regular:  400;
    --weight-medium:   500;
    --weight-semibold: 600;
    --weight-bold:     700;

    /* ── 动效 ── */
    --transition: .22s cubic-bezier(.22, 1, .36, 1);
    --ease-quint: cubic-bezier(.22, 1, .36, 1);
    --ease-spring: cubic-bezier(.34, 1.56, .64, 1);

    /* ── 玻璃拟态 ── */
    --glass-bg: rgba(255, 255, 255, .72);
    --glass-bg-strong: rgba(255, 255, 255, .86);
    --glass-blur: 16px;
    --glass-border: rgba(255, 255, 255, .55);

    /* ── 焦点环（无障碍） ── */
    --ring-focus: 0 0 0 3px rgba(99, 102, 241, .16);
    --ring-focus-danger: 0 0 0 3px rgba(239, 68, 68, .16);

    /* ── 层级阶梯（#1173）──────────────────────────────────────────
       ⚠️ 现存 18 种硬编码 z-index（9999×3 / 1200 / 1051 / 1050 / 1041 /
          1040 / 2000 / 100 / 50 / 11 / 10 / 6∕5∕4∕3∕2∕1∕0 …）。
          本轮**只建阶梯、不做批量替换** —— z-index 改错会直接压坏弹窗
          与下拉，属于「行为级」而非「表现级」改动，留待 #1175 组件层
          按组件逐个收口，届时逐项验证层叠效果。                        */
    --z-base:       0;
    --z-raised:     1;
    --z-sticky:     50;
    --z-sidebar:    100;
    --z-topbar:     200;
    --z-dropdown:   400;
    --z-popover:    600;
    --z-tooltip:    700;
    --z-modal:      1000;
    --z-modal-mask: 1040;
    --z-toast:      2000;
    --z-max:        9999;

    /* ── 组件尺寸（#1175 · 供 .ds-* 内部推导，杜绝魔法数字）────────
       为什么不放成组件上的局部变量：_audit_tokens.py / _check_css_struct.py
       只认 `:root` 里的定义（令牌单权威，见铁律 4），写在 .ds-steps 上的
       --x 会被判成「悬空引用」。故统一登记在此。                     */
    --ds-dot:  28px;   /* 步骤条圆点直径 */
    --ds-line: 2px;    /* 步骤条连接线粗细（垂直居中偏移由此推导）*/
}

/* ═══════════════════════════════════════════════════════════════════════
   2. 基础层（滚动条 / 焦点环 / 选中态 / 降低动效）
   全部为纯表现增强，不改变任何布局。
   ═══════════════════════════════════════════════════════════════════════ */

/* 现代细滚动条 */
.main-content,
.sidebar-nav,
.modal,
.table-container,
[style*="overflow"] {
    scrollbar-width: thin;
    scrollbar-color: #cbd5e1 transparent;
}
.main-content::-webkit-scrollbar,
.sidebar-nav::-webkit-scrollbar,
.modal::-webkit-scrollbar,
.table-container::-webkit-scrollbar { width: 8px; height: 8px; }
.main-content::-webkit-scrollbar-thumb,
.sidebar-nav::-webkit-scrollbar-thumb,
.modal::-webkit-scrollbar-thumb,
.table-container::-webkit-scrollbar-thumb {
    background: #cbd5e1;
    border-radius: var(--radius-full);
    border: 2px solid transparent;
    background-clip: content-box;
}
.main-content::-webkit-scrollbar-thumb:hover,
.sidebar-nav::-webkit-scrollbar-thumb:hover,
.modal::-webkit-scrollbar-thumb:hover,
.table-container::-webkit-scrollbar-thumb:hover { background: #94a3b8; background-clip: content-box; }
.main-content::-webkit-scrollbar-track,
.sidebar-nav::-webkit-scrollbar-track,
.modal::-webkit-scrollbar-track,
.table-container::-webkit-scrollbar-track { background: transparent; }

/* 统一焦点环：键盘可达性显著提升，鼠标点击不显示 */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: none;
    box-shadow: var(--ring-focus);
    border-radius: var(--radius-sm);
}

/* 文本选中态 */
::selection {
    background: rgba(99, 102, 241, .18);
    color: var(--text);
}

/* 尊重系统「减少动效」偏好 */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   3. 框架层（侧边栏 / 顶栏 / 导航）
   ═══════════════════════════════════════════════════════════════════════ */

.sidebar {
    background: var(--bg-sidebar-grad);
    box-shadow: 1px 0 0 rgba(255, 255, 255, .05), 16px 0 48px -12px rgba(15, 23, 42, .28);
}

.sidebar-nav { padding: 8px 10px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .14); background-clip: content-box; }

/* 导航项：更细腻的悬停与激活态 */
.nav-item {
    border-radius: var(--radius-md);
    font-weight: 500;
    letter-spacing: .2px;
}
.nav-item:hover {
    background: rgba(255, 255, 255, .075);
    color: #f8fafc;
}
.nav-item.active {
    background: var(--primary-gradient);
    box-shadow: 0 6px 18px -4px rgba(99, 102, 241, .48), inset 0 1px 0 rgba(255, 255, 255, .18);
    font-weight: 600;
}
.nav-item.active::before { border-radius: 0 var(--radius-xs) var(--radius-xs) 0; opacity: .95; }   /* #1174 */

/* 侧边栏品牌徽标 */
.brand-badge {
    background: var(--primary-gradient);
    box-shadow: 0 2px 8px rgba(99, 102, 241, .4);
}

/* 任务选择器：玻璃质感 */
#task-selector {
    background: rgba(255, 255, 255, .07);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: var(--radius-md);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
#task-selector:hover { background: rgba(255, 255, 255, .11); }

/* 顶栏：玻璃模糊 + 底部细描边 */
.topbar {
    background: var(--glass-bg-strong);
    backdrop-filter: blur(var(--glass-blur)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
    border-bottom: 1px solid var(--border-light);
    box-shadow: 0 1px 3px rgba(15, 23, 42, .03);
}

/* 顶栏按钮 */
.btn-logout {
    border-radius: var(--radius-md);
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}
.btn-logout:hover {
    background: rgba(239, 68, 68, .08);
    color: var(--danger);
    box-shadow: var(--ring-focus-danger);
}
.btn-upgrade {
    background: var(--primary-gradient);
    border-radius: var(--radius-md);
    box-shadow: 0 4px 12px -2px rgba(99, 102, 241, .42);
    transition: transform var(--transition), box-shadow var(--transition);
}
.btn-upgrade:hover {
    transform: translateY(-1px);
    box-shadow: 0 8px 20px -4px rgba(99, 102, 241, .52);
}
.topbar-avatar, .avatar-circle {
    box-shadow: 0 0 0 2px #fff, 0 2px 8px rgba(15, 23, 42, .14);
    transition: transform var(--transition), box-shadow var(--transition);
}
.topbar-avatar:hover { transform: scale(1.06); }

/* ═══════════════════════════════════════════════════════════════════════
   4. 卡片与统计卡（玻璃拟态 · 微动效）
   ═══════════════════════════════════════════════════════════════════════ */

.card {
    background: var(--bg-card);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}
.card:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}
.card-header {
    border-bottom: 1px solid var(--border-light);
}
.card-header h3 {
    font-weight: 650;
    letter-spacing: .3px;
}

.stat-card {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}
.stat-card::before {
    background: var(--primary-gradient);
    opacity: .75;
    height: 3px;
}
.stat-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
    border-color: rgba(99, 102, 241, .22);
}
.stat-card .stat-value {
    background: var(--primary-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    letter-spacing: -.5px;
}

/* ═══════════════════════════════════════════════════════════════════════
   5. 按钮与表单（微交互 · 按压反馈）
   ═══════════════════════════════════════════════════════════════════════ */

.btn {
    border-radius: var(--radius-md);
    font-weight: 550;
    letter-spacing: .2px;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn-primary {
    background: var(--primary-gradient);
    box-shadow: 0 4px 12px -2px rgba(99, 102, 241, .38), inset 0 1px 0 rgba(255, 255, 255, .18);
}
.btn-primary:hover {
    box-shadow: 0 8px 20px -4px rgba(99, 102, 241, .48), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.btn-success { box-shadow: 0 4px 12px -2px rgba(16, 185, 129, .34); }
.btn-warning { box-shadow: 0 4px 12px -2px rgba(245, 158, 11, .34); }
.btn-danger  { box-shadow: 0 4px 12px -2px rgba(239, 68, 68, .34); }

.btn-outline {
    background: #fff;
    border: 1px solid var(--border-strong);
    color: var(--text);
}
.btn-outline:hover {
    background: var(--bg-subtle);
    border-color: var(--primary-light);
    color: var(--primary);
    box-shadow: var(--ring-focus);
}
.btn:disabled {
    box-shadow: none;
    transform: none;
    filter: saturate(.5);
}

/* 表单控件：更清晰的聚焦态 */
.form-control,
.form-input,
input[type="text"],
input[type="password"],
input[type="number"],
input[type="search"],
select,
textarea {
    border-radius: var(--radius-md);
    border: 1px solid var(--border-strong);
    transition: border-color var(--transition), box-shadow var(--transition), background var(--transition);
}
.form-control:focus,
.form-input:focus,
input[type="text"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
select:focus,
textarea:focus {
    border-color: var(--primary-light);
    box-shadow: var(--ring-focus);
    outline: none;
}
.form-control:hover,
.form-input:hover,
select:hover,
textarea:hover { border-color: #c3cbdb; }

/* ═══════════════════════════════════════════════════════════════════════
   6. 表格（现代轻量化）
   ═══════════════════════════════════════════════════════════════════════ */

.table {
    border-radius: var(--radius-md);
    overflow: hidden;
    border: 1px solid var(--border-light);
}
.table th {
    background: var(--bg-subtle);
    color: var(--text-light);
    font-weight: 600;
    font-size: 12px;
    letter-spacing: .4px;
    text-transform: none;
}
.table td {
    border-bottom: 1px solid var(--border-light);
}
.table tbody tr:hover {
    background: rgba(99, 102, 241, .035);
}
.table-container {
    border-radius: var(--radius-md);
    border: 1px solid var(--border-light);
    background: #fff;
    box-shadow: var(--shadow);
}

/* 标签 / 徽章 */
.tag,
.badge {
    border-radius: var(--radius-sm);
    font-weight: 600;
    letter-spacing: .2px;
}
.tag-primary { background: rgba(99, 102, 241, .10); color: var(--primary-dark); }
.tag-success { background: rgba(16, 185, 129, .12); color: var(--success-deep); }
.tag-warning { background: rgba(245, 158, 11, .14); color: var(--warning-deep); }
.tag-danger  { background: rgba(239, 68, 68, .12);  color: var(--danger-deep); }

/* ═══════════════════════════════════════════════════════════════════════
   7. 模态框与 Toast（玻璃 + 入场动效）
   ═══════════════════════════════════════════════════════════════════════ */

.modal-overlay {
    background: rgba(15, 23, 42, .48);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
.modal {
    background: var(--glass-bg-strong);
    backdrop-filter: blur(var(--glass-blur)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-2xl);   /* #1174：大容器锁 24px，不随 xl 档降到 20px */
    box-shadow: 0 24px 64px -12px rgba(15, 23, 42, .32);
    animation: pk-modal-in .28s var(--ease-quint);
}
@keyframes pk-modal-in {
    from { opacity: 0; transform: translateY(12px) scale(.985); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.toast {
    border-radius: var(--radius-md);
    box-shadow: 0 12px 32px -8px rgba(15, 23, 42, .34);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    font-weight: 550;
    letter-spacing: .2px;
}

/* ═══════════════════════════════════════════════════════════════════════
   8. 登录页（第一印象 · 品牌质感升级）
   ═══════════════════════════════════════════════════════════════════════ */

.login-hero {
    position: relative;
    overflow: hidden;
}
/* 极光渐变光晕，取代原先单一 glow，营造「先进」观感 */
.login-hero::after {
    content: '';
    position: absolute;
    inset: -30% -10% auto -10%;
    height: 70%;
    background:
        radial-gradient(ellipse at 20% 30%, rgba(99, 102, 241, .28), transparent 60%),
        radial-gradient(ellipse at 80% 20%, rgba(139, 92, 246, .22), transparent 60%),
        radial-gradient(ellipse at 50% 10%, rgba(6, 182, 212, .16), transparent 60%);
    filter: blur(38px);
    pointer-events: none;
    z-index: 0;
}
.login-hero-inner { position: relative; z-index: 1; }
.login-hero-glow { z-index: 0; }

.login-card {
    border-radius: var(--radius-2xl);   /* #1174：锁 24px */
    box-shadow: 0 24px 64px -16px rgba(15, 23, 42, .22);
    border: 1px solid var(--border-light);
}
.login-feats .lf-ico {
    background: var(--primary-gradient-soft);
    box-shadow: inset 0 0 0 1px rgba(99, 102, 241, .16);
}
.auth-tab {
    border-radius: var(--radius-md);
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}
.auth-tab.active {
    background: var(--primary-gradient);
    color: #fff;
    box-shadow: 0 4px 12px -2px rgba(99, 102, 241, .4);
}

/* ═══════════════════════════════════════════════════════════════════════
   9. 教务域 ac2-* 现代化
   ⚠️ academic.js 运行时注入 <style id="acad2-style">，晚于本文件加载，
      故一律以 `body .ac2-xxx`（0,1,1）提权覆盖 `.ac2-xxx`（0,1,0）。
   ═══════════════════════════════════════════════════════════════════════ */

body .ac2-btn {
    border-radius: var(--radius-md);
    border: 1px solid var(--border-strong);
    background: #fff;
    color: #334155;
    font-weight: 550;
    letter-spacing: .2px;
    transition: transform var(--transition), box-shadow var(--transition),
                background var(--transition), border-color var(--transition);
}
body .ac2-btn:hover {
    transform: translateY(-1px);
    border-color: var(--primary-light);
    color: var(--primary);
    box-shadow: var(--ring-focus);
}
body .ac2-btn:active { transform: translateY(0); }

body .ac2-btn.pri {
    background: linear-gradient(135deg, #0ea5e9, #22d3ee);
    box-shadow: 0 4px 12px -2px rgba(14, 165, 233, .38), inset 0 1px 0 rgba(255, 255, 255, .2);
    border-color: transparent;
    font-weight: 600;
}
body .ac2-btn.pri:hover {
    box-shadow: 0 8px 20px -4px rgba(14, 165, 233, .46);
    color: #fff;
    border-color: transparent;
}
body .ac2-btn.dgr {
    color: var(--danger-deep);
    border-color: rgba(239, 68, 68, .28);
}
body .ac2-btn.dgr:hover {
    background: rgba(239, 68, 68, .07);
    border-color: rgba(239, 68, 68, .45);
    color: var(--danger-deep);
    box-shadow: var(--ring-focus-danger);
}

body .ac2-tbl {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    font-size: 13px;
}
body .ac2-tbl th {
    background: var(--bg-subtle);
    color: var(--text-light);
    font-weight: 600;
    font-size: 12px;
    letter-spacing: .4px;
    border-bottom: 1px solid var(--border);
}
body .ac2-tbl td {
    border-bottom: 1px solid var(--border-light);
    color: #334155;
}
body .ac2-tbl tbody tr:hover {
    background: rgba(99, 102, 241, .035);
}

/* 教务域输入框 / 下拉 / 链接 */
body .ac2-inp,
body .ac2-sel {
    border-radius: var(--radius-md);
    border: 1px solid var(--border-strong);
    transition: border-color var(--transition), box-shadow var(--transition);
}
body .ac2-inp:focus,
body .ac2-sel:focus {
    border-color: var(--primary-light);
    box-shadow: var(--ring-focus);
    outline: none;
}
body .ac2-link {
    color: var(--primary);
    font-weight: 600;
    border-radius: var(--radius-sm);
    transition: background var(--transition), color var(--transition);
}
body .ac2-link:hover {
    background: rgba(99, 102, 241, .08);
    color: var(--primary-dark);
}
/* #1176：body .ac2-empty 已随 .ac2-empty 退役一并删除。
   academic.js 的 60 处空态全部收口到 DS.empty() / DS.loading()，
   样式见第 11.3 节（空态）与第 11.3b 节（加载占位）。 */

/* ═══════════════════════════════════════════════════════════════════════
   10. 未来组件层（.pk-*，供 P2 一体化门户使用，纯新增不冲突）
   ═══════════════════════════════════════════════════════════════════════ */

/* 页面级 Hero 头图 */
.pk-hero {
    position: relative;
    background: var(--primary-gradient-soft);
    border: 1px solid rgba(99, 102, 241, .14);
    border-radius: var(--radius-2xl);   /* #1174：hero 锁 24px */
    box-shadow: var(--shadow);
    overflow: hidden;
}
.pk-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at 85% -20%, rgba(139, 92, 246, .16), transparent 60%);
    pointer-events: none;
}

/* 玻璃卡片 */
.pk-glass {
    background: var(--glass-bg);
    backdrop-filter: blur(var(--glass-blur)) saturate(180%);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}

/* KPI 指标块 */
.pk-kpi {
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-light);
    background: #fff;
    box-shadow: var(--shadow);
    transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.pk-kpi:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
    border-color: rgba(99, 102, 241, .2);
}
.pk-kpi-v {
    background: var(--primary-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    font-weight: 750;
    letter-spacing: -.5px;
}

/* 分区标题 */
.pk-section {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: #fff;
    box-shadow: var(--shadow);
}
.pk-section-hd {
    border-bottom: 1px solid var(--border-light);
    background: linear-gradient(180deg, #fff, var(--bg-subtle));
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* 状态点 */
.pk-dot {
    box-shadow: 0 0 0 3px rgba(16, 185, 129, .14);
}
.pk-dot.warn { box-shadow: 0 0 0 3px rgba(245, 158, 11, .16); }
.pk-dot.err  { box-shadow: 0 0 0 3px rgba(239, 68, 68, .16); }

/* 骨架屏（数据加载态，替代空白） */
.pk-skeleton {
    background: linear-gradient(90deg, #eef1f6 25%, #f6f8fc 50%, #eef1f6 75%);
    background-size: 200% 100%;
    border-radius: var(--radius-sm);
    animation: pk-shimmer 1.4s ease-in-out infinite;
}
@keyframes pk-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   11. 一体化门户（#1169 教务 ↔ 课表）· 班级 360° / 教师 360° / 一致性体检
   ───────────────────────────────────────────────────────────────────────
   纯新增命名空间（.pk-portal* / .pk-side* / .pk-tt* / .pk-check*），
   与既有 style.css、academic.js 运行时注入的 .ac2-* 三方完全隔离。
   布局全部走 CSS Grid / Table，不依赖任何既有布局类，可安全独立演进。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 骨架：左列表 + 右详情 ── */
.pk-portal {
    display: grid;
    grid-template-columns: 272px minmax(0, 1fr);
    gap: 16px;
    align-items: start;
}
@media (max-width: 1100px) {
    .pk-portal { grid-template-columns: 1fr; }
}

/* ── 头部 ── */
.pk-portal-hero { padding: 17px 20px; margin-bottom: 14px; }
.pk-portal-hero h3 {
    margin: 0 0 5px; font-size: 18px; font-weight: 750;
    letter-spacing: -.3px; color: var(--text);
}
.pk-portal-hero p {
    margin: 0; font-size: 12.5px; color: var(--text-muted);
    line-height: 1.65; max-width: 780px;
}

.pk-portal-bar {
    display: flex; align-items: center; gap: 12px;
    flex-wrap: wrap; margin-bottom: 14px;
}

/* ── 分段控件（模式切换）── */
.pk-seg {
    display: inline-flex; padding: 4px; gap: 2px;
    background: var(--bg-subtle);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md);
}
.pk-seg button {
    border: 0; background: transparent; color: var(--text-light);
    font-size: 13px; font-weight: 600; padding: 7px 15px;
    /* #1174：--radius 是待废弃别名，此处直连 md 档（12-3=9px，原 14-3=11px，Δ2px）*/
    border-radius: calc(var(--radius-md) - 3px); cursor: pointer;
    transition: var(--transition); font-family: inherit;
}
.pk-seg button:hover { color: var(--text); }
.pk-seg button.on {
    background: #fff; color: var(--primary);
    box-shadow: var(--shadow); font-weight: 700;
}

/* ── 搜索框 ── */
.pk-search { position: relative; flex: 1; min-width: 170px; max-width: 300px; }
.pk-search input {
    width: 100%; padding: 8px 12px 8px 32px;
    border: 1px solid var(--border); border-radius: var(--radius-md);
    font-size: 13px; background: var(--bg-card); color: var(--text);
    transition: var(--transition); font-family: inherit;
}
.pk-search input:focus {
    outline: 0; border-color: var(--primary); box-shadow: var(--ring-focus);
}
.pk-search::before {
    content: '🔍'; position: absolute; left: 10px; top: 50%;
    transform: translateY(-50%); font-size: 11px; opacity: .45;
    pointer-events: none;
}

/* ── 左栏：列表 ── */
.pk-side {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    box-shadow: var(--shadow);
    overflow: hidden;
    position: sticky;
    top: 12px;
    max-height: calc(100vh - 130px);
    display: flex; flex-direction: column;
}
@media (max-width: 1100px) {
    .pk-side { position: static; max-height: 320px; }
}
.pk-side-hd {
    padding: 11px 14px; border-bottom: 1px solid var(--border-light);
    background: linear-gradient(180deg, #fff, var(--bg-subtle));
    font-size: 11.5px; font-weight: 700; color: var(--text-muted);
    letter-spacing: .6px; display: flex; align-items: center; gap: 8px;
}
.pk-side-hd .n {
    margin-left: auto; font-size: 11px; font-weight: 600;
    color: var(--text-light); background: var(--bg-subtle);
    border-radius: var(--radius-xl); padding: 2px 8px;
}
.pk-side-list { overflow: auto; flex: 1; padding: 6px; }
.pk-side-grp {
    font-size: 11px; font-weight: 700; color: var(--text-light);
    padding: 10px 10px 4px; letter-spacing: .5px;
}
.pk-side-item {
    display: flex; align-items: center; gap: 9px;
    padding: 8px 10px; border-radius: var(--radius-sm);
    cursor: pointer; font-size: 13px; color: var(--text);
    transition: var(--transition); border: 1px solid transparent;
}
.pk-side-item:hover { background: var(--bg-subtle); }
.pk-side-item.on {
    background: var(--primary-gradient-soft);
    border-color: rgba(99, 102, 241, .22);
    color: var(--primary-dark); font-weight: 650;
}
.pk-side-item .sb {
    margin-left: auto; font-size: 11px; color: var(--text-light);
    font-weight: 600;
}
.pk-side-item .av {
    width: 26px; height: 26px; border-radius: var(--radius-sm);
    display: grid; place-items: center;
    font-size: 11px; font-weight: 700; color: #fff; flex: none;
}

/* ── 右栏：详情 ── */
.pk-detail { min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.pk-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 12px;
}
.pk-kpi { padding: 14px 15px; }
.pk-kpi-v { font-size: 24px; line-height: 1.1; }
.pk-kpi-l { font-size: 12px; color: var(--text-muted); margin-top: 3px; }

.pk-panel {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    box-shadow: var(--shadow);
    overflow: hidden;
}
.pk-panel-hd {
    padding: 11px 15px; border-bottom: 1px solid var(--border-light);
    font-size: 13.5px; font-weight: 700; color: var(--text);
    background: linear-gradient(180deg, #fff, var(--bg-subtle));
    display: flex; align-items: center; gap: 8px;
}
.pk-panel-hd .r {
    margin-left: auto; font-size: 11.5px; font-weight: 600;
    color: var(--text-muted);
}
.pk-panel-bd { padding: 14px 15px; }

/* ── 课表网格（门户核心视觉）── */
.pk-tt-wrap { overflow-x: auto; }
.pk-tt {
    width: 100%; border-collapse: separate; border-spacing: 5px;
    table-layout: fixed; min-width: 560px;
}
.pk-tt th {
    font-size: 11.5px; font-weight: 700; color: var(--text-muted);
    padding: 5px 4px; text-align: center; letter-spacing: .5px;
}
.pk-tt th.pk-tt-per { width: 60px; color: var(--text-light); font-weight: 600; }
.pk-tt-cell {
    position: relative; border-radius: var(--radius-sm);
    padding: 7px 8px; min-height: 54px; vertical-align: top;
    border: 1px solid var(--border-light); background: var(--bg-card);
    transition: var(--transition);
}
.pk-tt-cell.has { border-left: 3px solid var(--primary); }
.pk-tt-cell.has:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
    border-color: var(--primary-light);
}
.pk-tt-cell.empty {
    background: repeating-linear-gradient(135deg, transparent,
        transparent 6px, rgba(148, 163, 184, .05) 6px,
        rgba(148, 163, 184, .05) 7px);
    border-style: dashed;
}
.pk-tt-course {
    font-size: 12.5px; font-weight: 700; line-height: 1.25; color: var(--text);
}
.pk-tt-meta {
    font-size: 11px; color: var(--text-muted); margin-top: 3px; line-height: 1.35;
}
.pk-tt-flags {
    position: absolute; top: 5px; right: 5px;
    display: flex; gap: 3px;
}
.pk-tt-flag {
    font-size: 9px; font-weight: 700; border-radius: var(--radius-xs);
    padding: 1px 4px; line-height: 1.5;
}
.pk-tt-flag.lock { background: rgba(245, 158, 11, .15); color: var(--warning-deep); }
.pk-tt-flag.comb { background: rgba(99, 102, 241, .15); color: var(--primary-dark); }
.pk-tt-idx {
    font-size: 11.5px; font-weight: 700; color: var(--text-muted);
    text-align: center; white-space: nowrap;
}

/* ── 进度条（工作量）── */
.pk-bar {
    height: 7px; border-radius: var(--radius-xl);
    background: var(--bg-subtle); overflow: hidden;
}
.pk-bar-fill {
    height: 100%; border-radius: var(--radius-xl);
    background: var(--primary-gradient);
    transition: width .6s var(--ease-quint);
}
.pk-bar-fill.warn { background: linear-gradient(90deg, #f59e0b, #f97316); }
.pk-bar-fill.err  { background: linear-gradient(90deg, var(--danger), var(--danger-deep)); }

/* ── 标签 ── */
.pk-chip {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 3px 9px; border-radius: var(--radius-xl); font-size: 11.5px;
    font-weight: 600; background: var(--bg-subtle);
    color: var(--text-light); border: 1px solid var(--border-light);
}
.pk-chip.pri  { background: var(--primary-gradient-soft); color: var(--primary-dark); border-color: rgba(99, 102, 241, .2); }
.pk-chip.warn { background: rgba(245, 158, 11, .12); color: var(--warning-deep); border-color: rgba(245, 158, 11, .22); }
.pk-chip.err  { background: rgba(239, 68, 68, .1);  color: var(--danger-deep);  border-color: rgba(239, 68, 68, .2); }
.pk-chip.ok   { background: rgba(16, 185, 129, .12); color: var(--success-deep); border-color: rgba(16, 185, 129, .22); }

/* ── 数据表格 ── */
.pk-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.pk-tbl th {
    font-size: 11.5px; font-weight: 700; color: var(--text-muted);
    text-align: left; padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap; letter-spacing: .4px;
}
.pk-tbl td {
    padding: 9px 10px; border-bottom: 1px solid var(--border-light);
    color: var(--text);
}
.pk-tbl tbody tr { transition: background var(--transition); }
.pk-tbl tbody tr:hover { background: var(--bg-subtle); }
.pk-tbl tbody tr:last-child td { border-bottom: 0; }

/* ── 一致性体检项 ── */
.pk-check {
    display: flex; align-items: flex-start; gap: 11px;
    padding: 12px 14px; border-radius: var(--radius-md);
    border: 1px solid var(--border-light);
    background: var(--bg-card); margin-bottom: 9px;
    transition: var(--transition);
}
.pk-check:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
.pk-check .ic {
    width: 28px; height: 28px; border-radius: var(--radius-sm);
    display: grid; place-items: center; font-size: 14px; flex: none;
}
.pk-check.ok   .ic { background: rgba(16, 185, 129, .12); }
.pk-check.warn .ic { background: rgba(245, 158, 11, .13); }
.pk-check.err  .ic { background: rgba(239, 68, 68, .12); }
.pk-check .tx { min-width: 0; }
.pk-check .tx b {
    display: block; font-size: 13.5px; font-weight: 700;
    color: var(--text); margin-bottom: 3px;
}
.pk-check .tx span { font-size: 12.5px; color: var(--text-muted); line-height: 1.6; }

/* ── 空态 ── */
.pk-empty { padding: 52px 20px; text-align: center; color: var(--text-muted); }
.pk-empty .ico { font-size: 34px; opacity: .5; margin-bottom: 10px; }
.pk-empty .t {
    font-size: 14.5px; font-weight: 700;
    color: var(--text-light); margin-bottom: 5px;
}
.pk-empty .d {
    font-size: 12.5px; line-height: 1.7; max-width: 430px; margin: 0 auto;
}

/* ═══════════════════════════════════════════════════════════════════════
   11. 基础组件层（.ds-*, #1175 新增 · 全站通用基础件）
   -------------------------------------------------------------------------
   与第 10 节（.pk-*）的分工
     .pk-* = 一体化门户**专用**视觉（样式较重、偏展示、门户内嵌）
     .ds-* = **全站通用**基础件（卡片 / 表格 / 空态 / 页头 / 步骤条 /
             按钮 / 徽章 / 指标卡），任何页面都能用。
             .ds-empty 是 #1176 收口 98 处空态的**唯一目标形态**。
   JS 侧对应 `frontend/js/ds-components.js`（window.DS，纯字符串、零依赖）。

   ⚠️ 铁律 1 的适用边界（必读，否则会误以为本节违规）
     铁律 1「只改表现、绝不改布局」约束的是**覆盖既有选择器** —— 既有盒模型
     是历史踩坑冻结过的（课表网格 / 手动调课表 / 合班面板），动一像素就可能
     越界或压栏。
     本节 .ds-* 是**纯新增组件**，历史上不存在任何 .ds-* 元素，写
     padding / flex / gap 不会破坏任何既有布局。因此这里**允许且应当**写
     布局属性 —— 也只有这样，第 1 节的 --space-* / --font-size-* +
     --leading-* / --weight-* / --radius-* / --z-* 才被真正消费
     （#1173 建了阶梯、#1174 建了圆角档位，本节是它们的落地出口）。
     所有取值一律走令牌，本文件内 0 硬编码颜色 / 0 硬编码间距 / 0 硬编码圆角。
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 11.1 按钮 ───────────────────────────────────────────────────────── */
.ds-btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--space-2);
    height: 34px; padding: 0 var(--space-4);
    border: 1px solid transparent; border-radius: var(--radius-sm);
    background: var(--bg-card); color: var(--text);
    font-family: inherit;
    font-size: var(--font-size-sm); line-height: 1;
    font-weight: var(--weight-semibold);
    text-decoration: none; white-space: nowrap; cursor: pointer;
    user-select: none;
    transition: background var(--transition), color var(--transition),
                border-color var(--transition), box-shadow var(--transition),
                transform var(--transition);
}
.ds-btn:hover { transform: translateY(-1px); }
.ds-btn:active { transform: translateY(0); }
.ds-btn:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.ds-btn[disabled], .ds-btn[disabled]:hover {
    opacity: .5; cursor: not-allowed; transform: none;
    box-shadow: none;
}
.ds-btn .ds-ico { flex: none; }

.ds-btn-primary {
    background: var(--primary-gradient); color: #fff;
    box-shadow: var(--shadow);
}
.ds-btn-primary:hover { box-shadow: var(--shadow-glow); }

.ds-btn-default {
    background: var(--bg-card); color: var(--text);
    border-color: var(--border-strong); box-shadow: var(--shadow);
}
.ds-btn-default:hover {
    border-color: var(--primary-light); color: var(--primary-dark);
}

.ds-btn-soft {
    background: var(--primary-gradient-soft); color: var(--primary-dark);
    border-color: rgba(99, 102, 241, .2);
}
.ds-btn-soft:hover { border-color: var(--primary); }

.ds-btn-ghost {
    background: transparent; color: var(--text-light);
    border-color: transparent;
}
.ds-btn-ghost:hover { background: var(--bg-subtle); color: var(--text); }

.ds-btn-danger {
    background: var(--danger); color: #fff; box-shadow: var(--shadow);
}
.ds-btn-danger:hover { background: var(--danger-deep); }

.ds-btn-link {
    background: transparent; color: var(--primary);
    height: auto; padding: 0; border-color: transparent;
}
.ds-btn-link:hover { color: var(--primary-dark); text-decoration: underline; }

/* md 与 .ds-btn 基准同值，显式写出以保证「JS 产出的每个 ds-btn-* 类都有规则」 */
.ds-btn-md { height: 34px; padding: 0 var(--space-4); font-size: var(--font-size-sm); }
.ds-btn-sm { height: 28px; padding: 0 var(--space-3); font-size: var(--font-size-xs); }
.ds-btn-lg { height: 40px; padding: 0 var(--space-5); font-size: var(--font-size-md); }

/* ── 11.2 徽章 ───────────────────────────────────────────────────────── */
.ds-badge {
    display: inline-flex; align-items: center;
    height: 20px; padding: 0 var(--space-2);
    border-radius: var(--radius-xs);
    font-size: var(--font-size-3xs); line-height: 1;
    font-weight: var(--weight-semibold); letter-spacing: .3px;
    white-space: nowrap; vertical-align: middle;
}
.ds-badge-neutral { background: var(--bg-subtle);  color: var(--text-light); }
.ds-badge-primary { background: var(--primary-gradient-soft); color: var(--primary-dark); }
.ds-badge-success { background: rgba(16, 185, 129, .12); color: var(--success-deep); }
.ds-badge-warning { background: rgba(245, 158, 11, .13); color: var(--warning-deep); }
.ds-badge-danger  { background: rgba(239, 68, 68, .1);   color: var(--danger-deep); }
.ds-badge-info    { background: rgba(59, 130, 246, .1);  color: var(--info); }

/* ── 11.3 空态（#1176 唯一目标形态）───────────────────────────────────── */
.ds-empty {
    display: flex; flex-direction: column; align-items: center;
    text-align: center;
    padding: var(--space-10) var(--space-5);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--bg-subtle);
}
.ds-empty.is-plain {
    border: 0; background: transparent; padding: var(--space-6) var(--space-4);
}
.ds-empty-md { padding: var(--space-10) var(--space-5); }  /* 显式声明，便于状态自测 */
.ds-empty-sm { padding: var(--space-6) var(--space-4); }
.ds-empty-lg { padding: var(--space-12) var(--space-6); }

.ds-empty-ico {
    display: grid; place-items: center;
    width: 56px; height: 56px; margin-bottom: var(--space-3);
    border-radius: var(--radius-full);
    background: var(--primary-gradient-soft); color: var(--primary);
}
.ds-empty-sm .ds-empty-ico { width: 40px; height: 40px; margin-bottom: var(--space-2); }
.ds-empty-lg .ds-empty-ico { width: 76px; height: 76px; margin-bottom: var(--space-4); }

.ds-empty-t {
    font-size: var(--font-size-md); line-height: var(--leading-md);
    font-weight: var(--weight-semibold); color: var(--text-light);
}
.ds-empty-sm .ds-empty-t { font-size: var(--font-size-sm); }
.ds-empty-lg .ds-empty-t { font-size: var(--font-size-lg); color: var(--text); }

.ds-empty-d {
    margin-top: var(--space-2);
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    color: var(--text-muted); max-width: 460px;
}
.ds-empty-x { margin-top: var(--space-3); width: 100%; }

.ds-empty-a {
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: var(--space-2); margin-top: var(--space-4);
}

/* 色调（只换图标圈配色，边框/底色保持中性，避免整块刺眼）
   ⚠️ neutral 走 .ds-empty-ico 的默认配色，此处显式再写一遍，保证
      「JS 产出的每个 ds-tone-* 类都有对应规则」（由 _test_ds_components.js
      第 13 项强校验，组件与样式不允许脱节）。                        */
.ds-tone-neutral .ds-empty-ico { background: var(--primary-gradient-soft); color: var(--primary); }
.ds-tone-muted   .ds-empty-ico { background: var(--bg-subtle);        color: var(--text-muted); }
.ds-tone-primary .ds-empty-ico { background: var(--primary-gradient-soft); color: var(--primary); }
.ds-tone-success .ds-empty-ico { background: rgba(16, 185, 129, .12); color: var(--success-deep); }
.ds-tone-warning .ds-empty-ico { background: rgba(245, 158, 11, .13); color: var(--warning-deep); }
.ds-tone-danger  .ds-empty-ico { background: rgba(239, 68, 68, .1);   color: var(--danger-deep); }
.ds-tone-info    .ds-empty-ico { background: rgba(59, 130, 246, .1);  color: var(--info); }

/* ── 11.3b 加载占位（#1176：与空态分开 —— 两者语义相反）──────────────────
   差别不在观感而在意图：空态 = 数据已到、确实为空，该给用户 CTA；
   加载 = 数据未到，此时给任何 CTA 都是误导。故独立成一个组件。      */
.ds-loading {
    display: flex; align-items: center; justify-content: center;
    gap: var(--space-3);
    padding: var(--space-10) var(--space-5);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--bg-subtle);
}
.ds-loading.is-plain {
    border: 0; background: transparent; padding: var(--space-6) var(--space-4);
}
.ds-loading-sm { padding: var(--space-6) var(--space-4); }
.ds-loading-md { padding: var(--space-10) var(--space-5); }
.ds-loading-lg { padding: var(--space-12) var(--space-6); }

.ds-loading-t {
    font-size: var(--font-size-sm); line-height: var(--leading-sm);
    font-weight: var(--weight-medium); color: var(--text-muted);
}
.ds-loading-sm .ds-loading-t { font-size: var(--font-size-xs); }
.ds-loading-lg .ds-loading-t { font-size: var(--font-size-md); }

/* 旋转指示器：三段式边框 + 顶部主色 = 缺口感。尺寸按档位跟随，与
   .ds-empty-ico 同样在组件内定死（避免 .ds-* 之间互相依赖令牌）。  */
.ds-spinner {
    flex: none;
    width: 18px; height: 18px;
    border: 2px solid var(--border);
    border-top-color: var(--primary);
    border-radius: var(--radius-full);
    animation: ds-spin .7s linear infinite;
}
.ds-loading-sm .ds-spinner { width: 14px; height: 14px; }
.ds-loading-lg .ds-spinner { width: 24px; height: 24px; }

@keyframes ds-spin { to { transform: rotate(360deg); } }

/* 无障碍：系统开启「减少动态效果」时放慢而不是停掉 —— 完全静止会让用户
   误判为卡死，放慢到 2.4s 仍能传达「进行中」又不会引起不适。        */
@media (prefers-reduced-motion: reduce) {
    .ds-spinner { animation-duration: 2.4s; }
}

/* ── 11.4 页头 ───────────────────────────────────────────────────────── */
.ds-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    flex-wrap: wrap; gap: var(--space-4);
    margin-bottom: var(--space-5);
}
.ds-head-main { min-width: 0; }

.ds-crumb {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--space-2); margin-bottom: var(--space-2);
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    color: var(--text-muted);
}
.ds-crumb a {
    color: var(--text-muted); text-decoration: none;
    transition: color var(--transition);
}
.ds-crumb a:hover { color: var(--primary); }
.ds-crumb-sep { color: var(--border-strong); }
.ds-crumb-cur { color: var(--text-light); font-weight: var(--weight-medium); }

.ds-head-t {
    margin: 0;
    font-size: var(--font-size-2xl); line-height: var(--leading-2xl);
    font-weight: var(--weight-bold); color: var(--text);
    letter-spacing: -.4px;
}
.ds-head-d {
    margin-top: var(--space-2);
    font-size: var(--font-size-sm); line-height: var(--leading-sm);
    color: var(--text-light); max-width: 720px;
}
.ds-head-a {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--space-2);
}

/* ── 11.5 步骤条 ─────────────────────────────────────────────────────── */
.ds-steps { display: flex; align-items: flex-start; }
.ds-step { display: flex; align-items: center; gap: var(--space-3); }
.ds-step-dot {
    display: grid; place-items: center; flex: none;
    width: var(--ds-dot); height: var(--ds-dot); border-radius: var(--radius-full);
    border: 1.5px solid var(--border-strong);
    background: var(--bg-card); color: var(--text-muted);
    font-size: var(--font-size-2xs); font-weight: var(--weight-bold);
    transition: background var(--transition), border-color var(--transition),
                color var(--transition), box-shadow var(--transition);
}
.ds-step-tx b {
    display: block;
    font-size: var(--font-size-sm); line-height: var(--leading-sm);
    font-weight: var(--weight-semibold); color: var(--text-muted);
}
.ds-step-tx span {
    display: block; margin-top: 2px;
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    color: var(--text-muted);
}
.ds-step-line {
    flex: 1; height: var(--ds-line); min-width: 24px;
    /* 垂直居中于圆点：(圆点直径 - 线粗) / 2。由令牌推导，圆点改尺寸自动跟随 */
    margin: calc((var(--ds-dot) - var(--ds-line)) / 2) var(--space-3) 0;
    background: var(--border); border-radius: var(--radius-xs);
    transition: background var(--transition);
}

.ds-step.is-done .ds-step-dot {
    background: var(--success); border-color: var(--success); color: #fff;
}
.ds-step.is-done .ds-step-tx b { color: var(--text-light); }
.ds-step.is-current .ds-step-dot {
    background: var(--primary); border-color: var(--primary); color: #fff;
    box-shadow: var(--shadow-glow);
}
.ds-step.is-current .ds-step-tx b { color: var(--text); }
/* 未开始的步骤弱化（is-todo 是默认态，显式写出以保证「类↔样式」一一对应）*/
.ds-step.is-todo .ds-step-dot { opacity: .7; }
.ds-step.is-todo .ds-step-tx b { font-weight: var(--weight-regular); }
.ds-step-line.is-done { background: var(--success); }

.ds-steps.is-vertical { flex-direction: column; align-items: stretch; }
.ds-steps.is-vertical .ds-step { align-items: flex-start; }
.ds-steps.is-vertical .ds-step-line {
    width: var(--ds-line); height: 18px; min-width: 0; flex: none;
    margin: var(--space-2) 0 var(--space-2) calc((var(--ds-dot) - var(--ds-line)) / 2);
}

/* ── 11.6 卡片 ───────────────────────────────────────────────────────── */
.ds-card {
    display: flex; flex-direction: column;
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--bg-card); box-shadow: var(--shadow);
    overflow: hidden;
}
.ds-card.is-hover { transition: box-shadow var(--transition), transform var(--transition); }
.ds-card.is-hover:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.ds-card-hd {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border-light);
    background: linear-gradient(180deg, #fff, var(--bg-subtle));
}
.ds-card-tx { min-width: 0; }
.ds-card-t {
    margin: 0;
    font-size: var(--font-size-md); line-height: var(--leading-md);
    font-weight: var(--weight-bold); color: var(--text);
}
.ds-card-d {
    margin-top: 2px;
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    color: var(--text-muted);
}
.ds-card-a {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--space-2); margin-left: auto;
}

.ds-card-bd { padding: var(--space-5); }
.ds-card.is-flush .ds-card-bd { padding: 0; }

.ds-card-ft {
    padding: var(--space-3) var(--space-5);
    border-top: 1px solid var(--border-light);
    background: var(--bg-subtle);
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    color: var(--text-light);
}

/* ── 11.7 表格 ───────────────────────────────────────────────────────── */
.ds-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--border-light); border-radius: var(--radius-md);
    background: var(--bg-card);
}
.ds-table {
    width: 100%; border-collapse: collapse;
    font-size: var(--font-size-sm); line-height: var(--leading-sm);
}
.ds-table thead th {
    padding: var(--space-3) var(--space-4);
    background: var(--bg-subtle);
    border-bottom: 1px solid var(--border);
    font-size: var(--font-size-2xs); line-height: var(--leading-2xs);
    font-weight: var(--weight-semibold);
    color: var(--text-light); letter-spacing: .4px;
    text-align: left; white-space: nowrap;
}
.ds-table.is-sticky thead th {
    position: sticky; top: 0; z-index: var(--z-sticky);
}
.ds-table td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-light);
    color: var(--text); vertical-align: middle;
}
.ds-table tbody tr { transition: background var(--transition); }
.ds-table.is-hover tbody tr:hover { background: var(--bg-subtle); }
.ds-table.is-zebra tbody tr:nth-child(even) { background: var(--bg-subtle); }
.ds-table.is-zebra.is-hover tbody tr:nth-child(even):hover { background: var(--border-light); }
.ds-table tbody tr:last-child td { border-bottom: 0; }
.ds-table.is-compact thead th { padding: var(--space-2) var(--space-3); }
.ds-table.is-compact td       { padding: var(--space-2) var(--space-3); }

.ds-table .ta-left   { text-align: left; }
.ds-table .ta-center { text-align: center; }
.ds-table .ta-right  { text-align: right; }

/* ── 11.8 指标卡 ─────────────────────────────────────────────────────── */
.ds-stat {
    position: relative;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--bg-card); box-shadow: var(--shadow);
}
.ds-stat-ico {
    display: grid; place-items: center;
    width: 32px; height: 32px; margin-bottom: var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--primary-gradient-soft); color: var(--primary);
}
.ds-stat-l {
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    color: var(--text-muted);
}
.ds-stat-v {
    margin-top: var(--space-1);
    font-size: var(--font-size-2xl); line-height: var(--leading-2xl);
    font-weight: var(--weight-bold); color: var(--text);
    letter-spacing: -.5px;
}
.ds-stat-v i {
    margin-left: 3px;
    font-size: var(--font-size-xs); font-style: normal;
    font-weight: var(--weight-medium); color: var(--text-muted);
}
.ds-stat-d {
    margin-top: var(--space-1);
    font-size: var(--font-size-xs); line-height: var(--leading-xs);
    font-weight: var(--weight-semibold);
}
.ds-stat-d.is-up   { color: var(--success-deep); }
.ds-stat-d.is-down { color: var(--danger-deep); }
.ds-stat-d.is-flat { color: var(--text-muted); }
.ds-stat-hint {
    margin-top: var(--space-1);
    font-size: var(--font-size-3xs); line-height: var(--leading-3xs);
    color: var(--text-muted);
}
.ds-stat.ds-tone-primary .ds-stat-ico { background: var(--primary-gradient-soft); color: var(--primary); }
.ds-stat.ds-tone-muted   .ds-stat-ico { background: var(--bg-subtle);        color: var(--text-muted); }
.ds-stat.ds-tone-success .ds-stat-ico { background: rgba(16, 185, 129, .12); color: var(--success-deep); }
.ds-stat.ds-tone-warning .ds-stat-ico { background: rgba(245, 158, 11, .13); color: var(--warning-deep); }
.ds-stat.ds-tone-danger  .ds-stat-ico { background: rgba(239, 68, 68, .1);   color: var(--danger-deep); }
.ds-stat.ds-tone-info    .ds-stat-ico { background: rgba(59, 130, 246, .1);  color: var(--info); }

/* ── 11.9 新手指引 onboarding（#1177）─────────────────────────────────────
   用于「需要先完成前置步骤才能进入」的页签 —— 典型场景是无排课任务。
   由 app.js 的 guardNoTask() 渲染，结构 = 头部（图标+标题+说明）
   + DS.steps({vertical:true}) 三步进度 + 底部行动区。
   纯新增类名，与既有布局零交集，故可直接写布局属性（见本节首铁律说明）。
   ────────────────────────────────────────────────────────────────────── */
.ds-onb {
    max-width: 620px;
    margin: var(--space-8) auto;
    padding: var(--space-6) var(--space-6) var(--space-5);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}
.ds-onb-hd { display: flex; align-items: flex-start; gap: var(--space-4); }
.ds-onb-ico {
    display: grid; place-items: center; flex: none;
    width: 48px; height: 48px; border-radius: var(--radius-full);
    background: var(--primary-gradient-soft); color: var(--primary);
}
.ds-onb-tx h3 {
    margin: 0;
    font-size: var(--font-size-lg); line-height: var(--leading-lg);
    font-weight: var(--weight-semibold); color: var(--text);
}
.ds-onb-tx p {
    margin: var(--space-2) 0 0;
    font-size: var(--font-size-sm); line-height: var(--leading-sm);
    color: var(--text-muted);
}
.ds-onb-steps { margin-top: var(--space-6); padding-left: var(--space-1); }
.ds-onb-acts {
    display: flex; flex-wrap: wrap; gap: var(--space-3);
    margin-top: var(--space-6); padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

/* ── 11.10 响应式 / 无障碍 ───────────────────────────────────────────── */
@media (max-width: 768px) {
    .ds-head { flex-direction: column; align-items: stretch; }
    .ds-head-a { margin-top: var(--space-2); }
    .ds-steps:not(.is-vertical) { flex-direction: column; align-items: stretch; }
    .ds-steps:not(.is-vertical) .ds-step-line {
        width: var(--ds-line); height: 14px; min-width: 0; flex: none;
        margin: var(--space-2) 0 var(--space-2) calc((var(--ds-dot) - var(--ds-line)) / 2);
    }
    .ds-card-hd { flex-wrap: wrap; }
    .ds-card-a { margin-left: 0; width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
    .ds-btn:hover, .ds-card.is-hover:hover { transform: none; }
}
