/* ============================================================
   班主任工作平台 · 样式
   ------------------------------------------------------------
   设计要点
     · 对比度：选中态用高饱和描边 + 深色文字，未选中态明显更浅
     · 移动端：栅格自动折叠、横向滚动表格给出滑动提示、
       点按目标不小于 44px、弹窗在手机上占满宽度
   ============================================================ */

:root {
    /*
     * ★ 声明「本页只有浅色」（2026-09-25 跨设备适配）。
     *   不加这一条，两种设备会自己动手改颜色：
     *   · 安卓 Chrome 开着「强制深色」时会把整页颜色**反转**，姓名卡的绿/蓝/橙三态、
     *     表格斑马纹全花掉（这个功能是给没有深色模式的网页自动加暗色用的）；
     *   · Windows 11 系统深色模式下，下拉框、复选框、滚动条会变黑底白字，
     *     跟页面白底打架。
     *   写死 light（HTML 的 <meta name="color-scheme" content="light"> 也有同样作用，
     *   两处都写是为了老版本浏览器也能吃到）。
     */
    color-scheme: light;

    /*
     * 手机键盘当前盖住的高度（px）。真机上由 core.js 的 KeyboardGuard 写进来，
     * 桌面（没有键盘概念）恒为 0。凡是 `position: fixed` 的东西（弹窗遮罩、
     * 底部的操作条）都用它在键盘弹起时往上收 —— 一处 CSS 管全部弹窗。
     */
    --kbh: 0px;

    /*
     * ★★ 2026-10-04 UI 改版（用户拍板方案 E「融合·拇指优先」）：色板整体换成
     *    Apple 系统色（iOS systemGray6 背景 / systemBlue 强调 / 语义色三态）。
     *    ★ 只换「值」不换「名字」—— 全站两千多处引用一个字不用改；
     *      哪个组件要调形态（圆角 / 描边 / 材质）去它自己的规则里改。
     *    ★ 考勤三态（present / personal / sick）的**区分度口径不变**：
     *      绿 / 蓝 / 橙三色照旧一眼分得清（ui.md 的硬规矩），只是换成了 iOS 语义色。
     *    ★ 文字色用的是 iOS 的「无障碍深色变体」（systemGreen dark = #248A3D 这套），
     *      不是原生 #34C759 —— 那个绿压白底对比度只有 2.2:1，当文字看不清。
     */
    --c-bg: #F2F2F7;
    --c-surface: #ffffff;
    --c-surface-2: #F0F0F5;
    /*
     * ★★ 2026-10-05 用户第 3 条：**输入类控件的填充单独一个变量**。
     *   起因：输入框在 2026-10-04 晚改成「浅灰底、无描边」后，用户发现白卡片上的
     *   下拉框 / 输入框「区分度不明显了」，而且学生页的搜索框跟**表头**糊成一片
     *   （表头的底正好也是 --c-surface-2，两个是同一个值 #F0F0F5，只是靠在一起了）。
     *   修法：控件比表头**深一档** —— iOS 的 systemGray5（#E5E5EA）。
     *   为什么不用描边解决：用户上一轮明确要「无描边」（苹果那套填充式输入框），
     *   所以这里靠**填充深浅**分层，不靠线。
     *   ⚠️ 改这里要连 `.seg`（分段控件）一起看：它的槽是 rgba(118,118,128,.12)（≈#EEEEEF），
     *      比本值浅一档，是故意的（槽要比控件浅，滑块才浮得起来）。
     */
    --c-field: #E5E5EA;
    --c-border: rgba(60, 60, 67, .14);        /* iOS separator（发丝线） */
    --c-border-strong: rgba(60, 60, 67, .30);
    --c-text: #0e0e11;
    --c-text-2: #58585f;
    --c-text-3: #7c7c83;

    --c-primary: #007AFF;
    --c-primary-d: #0062CC;
    --c-primary-l: #E5F1FF;
    /* ★ 2026-10-04 晚 用户第 1 条：班级胶囊那个小圆点专用 —— **Tiffany 蓝**。 */
    --c-tiffany: #0ABAB5;
    --c-success: #248A3D;
    --c-success-l: #E1F6E8;
    /*
     * ★ 2026-10-05 用户第 5 条：作息时间里「正在进行」那一行原来用的是 `--c-*-l`
     *   （接近白的浅色），用户说「这个配色太浅了」。这两档是**同色系深一档的浅底** ——
     *   一眼看得出「就是这一行」，又不至于像实心色块那么刺眼（苹果那种「淡色选中态」）。
     *   配对文字色仍是 #0b5e3f / #0b4c7d，对比度都在 6:1 以上。
     */
    --c-success-fill: #C6E9D2;
    --c-info-fill: #CBE3FF;
    --c-warn: #a86405;
    --c-warn-l: #fdf0d6;
    /*
     * 「今天」专用色（2026-09-25 加）。
     * 不与 --c-warn 复用：节假日的格子底色 / 边框本来就是米黄橙（#fdf1e3 / #f0cfa4），
     * 再用同一个橙去圈「今天」，两个橙圈叠在 34px 的小格子上根本分不出来 ——
     * 用户截图反馈的「校历今天的边框不够清楚」就是这个原因。
     * 这里换成一个**深砖橙**：比节假日的浅橙重得多，又和主色蓝、调休紫、假期青都不撞。
     */
    --c-today: #c4471a;
    --c-danger: #D70015;
    --c-danger-l: #FDE9E8;
    --c-info: #007AFF;
    --c-info-l: #E5F1FF;
    --c-purple: #6a45cc;
    --c-purple-l: #ece5fb;
    /*
     * ★ 2026-09-27 新增：**品红 / 玫红** —— 「他这天去替别人上课」（代别人）。
     * 为什么要再加一个色：代课统计 里已经用掉 蓝（本班没课）/ 灰（没课）/
     * 橙（有课）/ 紫（代课）/ 红（被代课）/ 绿（正常上课、已安排）六种，
     * 用户要求「这个标签换个颜色，跟之前的都不能一样」，还特别提了区分度。
     * 选品红而不是青 / 黄 / 棕：青跟绿（#12855a）只差 30° 上下、投影仪上容易糊，
     * 黄和棕在浅底上当**文字色**对比度不够；品红（H≈304°）离紫（256°）48°、
     * 离红（3°）更远，而且比紫更艳、更暖，放在一起一眼分得开。
     * 对比度：#a3189b 压白底 6.6:1、压 --c-served-l 5.5:1，都过 WCAG AA。
     */
    --c-served: #a3189b;
    --c-served-l: #fbe7f6;

    /*
     * ★ 2026-10-05 晚 新增：**调休上课**专用色（苹果 systemPurple）。
     *
     * 用户原话：「调休的外边框是虚线，改成实线，这个颜色更换为其他颜色。
     * 但必须整体符合苹果色调，同时又要有区分度，调休上班就不要用灰这样的颜色了。」
     *
     * 选苹果紫的理由：校历上已经用掉的色相是 ——
     *   寒暑假 青绿(#0f766e，≈173°) · 周末/节假日 暖橙(#9a4a06，≈28°)
     *   · 有作业 蓝(#007AFF，211°) · 今天 砖橙(#c4471a，18°)
     * 紫色（#AF52DE 约 286°）是剩下最空的一段，跟上面四个都拉得开。
     * （代课统计那支品红 `--c-served` 约 304°，离它只有 18°，
     *   但一个在校历、一个在代课统计，**不会同框出现**，不影响辨别。）
     */
    --c-makeup: #AF52DE;        /* 边框 / 角标描边 */
    --c-makeup-d: #7A3E9D;      /* 压在浅紫底上的**文字色**（对比度 5.6:1，过 WCAG AA） */
    --c-makeup-l: #EFE3FA;      /* 格子底色 */

    /*
     * 考勤三态的专属色：出勤绿 / 事假蓝 / 病假橙。
     * 单独拎出来是因为这三色要在**很多地方**保持一致 —— 姓名卡、统计卡、
     * 请假名单的小标签、可点的数字，全都要是同一套，改一处就处处跟着变。
     * 之所以不让「出勤」沿用中性灰：一屏姓名卡扫过去，绿蓝橙三色一眼就能数出人，
     * 灰色跟白色卡片太像，反而要凑近看。
     */
    --c-att-present: #248A3D;
    --c-att-present-l: #E1F6E8;
    --c-att-personal: #007AFF;
    --c-att-personal-l: #E5F1FF;
    --c-att-sick: #C25E00;
    --c-att-sick-l: #FDEDD3;

    /* 内容区的最大宽度：顶栏 / 内容 / 页脚按它一起居中（大屏不贴左边）。
       ★ 2026-10-01 用户第 8 条：1920 / 2K / 2560 的屏上原来只占中间 1440，
         两边留白太多、看着像「缩在一小块」。现在放到 1920 ——
         1920 及以下的屏仍然是「满宽减 22px 边距」（1440 的显示**一点没变**），
         超宽屏（2K / 4K）居中在 1920，两侧留白均匀。 */
    --shell-max: 1920px;

    /* ------------------------------------------------------------------
       表格「内部上下滚动」的统一高度（2026-10-01 用户第 16 条）
       ------------------------------------------------------------------
       用户原话：「所有内置的上下滚动表格显示的大小都是 12 行（不算表头）……
                并将类似的统一起来，方便后期更改」。
       他选的是**自适应、上限 12 行**：矮屏少显示几行，高屏最多 12 行。
       要改行数只动 --tbl-rows 这一个数（--tbl-row-h / --tbl-head-h 是量出来的实际值，
       带操作按钮的表行高约 53px、表头约 41px；换字号 / 按钮尺寸后要重量一次）。
       ⚠️ 用它的容器必须同时挂 `.table-wrap.tb-scroll`（表头吸顶也在那条规则里）。 */
    --tbl-rows: 12;
    --tbl-row-h: 53px;
    --tbl-head-h: 41px;
    /* 矮屏时按视口比例收（笔记本电脑 900px 高的屏上大约显示 8~9 行） */
    --tbl-max-vh: 66vh;

    /* ★ 2026-10-04 晚（苹果扁平化收尾）：圆角统一成 iOS 的两档 ——
       控件 10px、卡片 14px（小件 8px）；原来是 12 / 16 / 8 三档混用，不够统一。 */
    --radius: 10px;
    --radius-lg: 14px;
    --radius-sm: 8px;
    /* ★ 2026-10-04：阴影整体收轻 —— iOS 的层次靠「发丝线 + 极浅投影」，不靠重影 */
    --shadow-s: 0 1px 2px rgba(0, 0, 0, .05);
    --shadow-m: 0 4px 14px rgba(0, 0, 0, .08);
    --shadow-l: 0 14px 38px rgba(0, 0, 0, .16);
    --sidebar-w: 226px;
    /* ★★ 2026-10-04 用户第 10 条：侧栏「品牌栏」与内容区「顶栏」必须**一样高** ——
       否则两条横线（各自的下边框）对不齐，左边一条右面一条，看着很脏。
       实测原来是 品牌栏 61px / 顶栏 66px，差 5px；**以左边为基准**压顶栏。
       改这里之前先量两边：品牌栏 = 26px 校徽 + 上下内边距，顶栏 = ☰(40px) 与两行标题。 */
    --head-h: 61px;
    --tap: 44px;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}

html,
body {
    height: 100%;
}

body {
    /*
     * 字体栈：**故意不下载任何网络字体**，只用各系统自带的那套。
     *   · 中文网页字体动辄 3~8MB，这个项目的用户里有不少老手机 / 老电脑，
     *     让首屏多等几百 KB 的字体不划算（同一台服务器上还跑着数据库和接口）；
     *   · 系统字体在各自平台上本来就是这个平台「最清楚」的字形，抗锯齿、 hinting
     *     都按屏幕调过；换一套网络字体反而在 Windows 上发虚。
     * 顺序说明（从左到右命中第一个可用的）：
     *   system-ui / -apple-system / BlinkMacSystemFont → macOS(iOS) 用系统 SF + 苹方
     *   "Segoe UI" / Roboto                             → Windows / 原生安卓
     *   "PingFang SC" / "HarmonyOS Sans SC" / "MiSans"  → 中文：苹果 / 鸿蒙 / 小米
     *   "Noto Sans SC" / "Source Han Sans SC"           → 很多安卓 ROM 与 Linux 自带
     *   "Microsoft YaHei" / "WenQuanYi Micro Hei"       → Windows / 老 Linux 兜底
     * ⚠️ 别把 Roboto 挪到 "PingFang SC" 后面 —— 国产 ROM 上会先命中 Roboto，
     *    中文再回退，同一行里中英文粗细不一致（看着像两套字体）。
     */
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "PingFang SC", "HarmonyOS Sans SC", "MiSans", "Noto Sans SC",
        "Source Han Sans SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
    background: var(--c-bg);
    color: var(--c-text);
    font-size: 14px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -webkit-text-size-adjust: 100%;
}

button,
input,
select,
textarea {
    font-family: inherit;
    font-size: inherit;
    color: inherit;
}

h1,
h2,
h3,
h4 {
    /* ★ 2026-10-04 晚（苹果扁平化收尾）：标题从 700 收到 **600（semibold）** ——
       苹果极少用 700+，一粗就显得「重」、不够干净。 */
    font-weight: 600;
    line-height: 1.35;
}

code {
    font-family: var(--font-mono, ui-monospace, Menlo, Consolas, monospace);
}

/* ============ 布局 ============ */
.app {
    display: flex;
    min-height: 100vh;
}

/* 侧栏默认收起：点顶栏 ☰ 才滑出，内容区始终占满整宽 */
.sidebar {
    width: var(--sidebar-w);
    flex-shrink: 0;
    /* ★ 2026-10-04 方案 E：磨砂材质（macOS/iPadOS 侧栏的标志性外观）。
       半透明白 + saturate 提艳 + blur —— 底下是内容滑过的影子。 */
    background: rgba(249, 249, 251, .82);
    -webkit-backdrop-filter: saturate(1.8) blur(20px);
    backdrop-filter: saturate(1.8) blur(20px);
    border-right: 1px solid var(--c-border);
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 60;
    transform: translateX(-100%);
    transition: transform .26s cubic-bezier(.22, 1, .36, 1);
    will-change: transform;
}

.sidebar.open {
    transform: translateX(0);
    box-shadow: var(--shadow-l);
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    /* ★ 2026-10-04 用户第 10 条：高度写死成 --head-h（= 顶栏高度），
       两条横线才严格在同一水平线上。垂直内边距交给 align-items 居中。 */
    box-sizing: border-box;
    height: var(--head-h);
    padding: 0 16px;
    border-bottom: 1px solid var(--c-border);
}

/* 侧栏顶部的校徽（原来是 📚 emoji）。真图片、固定 26px —— 各端一模一样 */
.brand-icon {
    width: 26px;
    height: 26px;
    display: block;
    flex-shrink: 0;
    border-radius: 50%;
}

.brand strong {
    font-size: 15.5px;
    letter-spacing: -.2px;
}

/*
 * ★★ 2026-10-07 多学校：品牌下面那行「所属学校」（本校 / 外校 / 外校1…）。
 *
 * 用户要求（原话）：「侧栏品牌下加一行」，并说明两所学校的数据是分开的
 * （「两套数据不互相打架」）—— 所以登录的人**必须一眼知道自己现在在哪一所学校**：
 * 「六年级1班」在两所学校各有一个，看错了就会改错数据。
 *
 * ★ 形态（2026-10-07 第二批改）：**不分本校 / 外校，一律素色小字**。
 *   用户原话：「本校和外校这个提示胶囊把显示格式统一素色小字」——
 *   原来外校套的是琥珀胶囊，跟本校的素灰小字不一样；现在两所学校同一形态：
 *   一个灰点 + 校名，跟侧栏其它字一个调子。
 *   `data-school` 属性**保留**（挂在元素上不花任何代价，将来想再分档还有得用），
 *   只是不再有 CSS 按它上色了。
 */
.brand-text {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    line-height: 1.2;
}

.school-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 2px;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--c-text-3);
    white-space: nowrap;
}

.school-tag::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--c-text-3);
    flex-shrink: 0;
}

/*
 * ★★ 这里原来有两条规则，给「不是本校」的那几所套琥珀胶囊 —— 2026-10-07 第二批
 *   用户要求统一成素色小字，**已整段删掉**（连 `::before` 那条一起）。
 *   别再补回来：本校 / 外校现在长得一模一样，登录的人靠**校名本身**认路
 *   （校名写在那行小字里，hover 还有完整说明）。
 *   ★ 也**别在这里写回那条旧选择器的原文** —— harness §[103] 是拿字符串反查的，
 *     写在注释里照样算「规则还在」，会误报。
 */

.nav {
    flex: 1;
    overflow-y: auto;
    padding: 10px 10px 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    -webkit-overflow-scrolling: touch;
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 12px;
    min-height: var(--tap);
    border: none;
    background: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--c-text-2);
    font-size: 14px;
    font-weight: 500;
    text-align: left;
    transition: background .15s, color .15s;
    width: 100%;
}

.nav-item:hover {
    background: var(--c-surface-2);
    color: var(--c-text);
}

.nav-item.active {
    background: var(--c-primary);
    color: #fff;
    font-weight: 600;
    box-shadow: var(--shadow-s);
}

/*
 * ★★ 侧栏图标 = **服务器上的彩色 SVG 文件**（2026-09-25 用户要求：换彩色、
 *    资源放服务器；在那之前是 emoji，再之前是内联线条 SVG）。
 *
 * 一路换过来的原因：
 *   · emoji（🍚 🔁 🕐…）的样子由操作系统的 emoji 字体决定，Mac / Windows /
 *     安卓各一套，Windows 老字体还会把一个字形拆成两个压到文字上（2026-09-23
 *     在 Windows 上真见过「代课统计」两行字叠在一起）；
 *   · 内联线条 SVG 跨设备一致了，但是**单色**的，用户觉得不够好看；
 *   · 现在 = `public/img/nav/*.svg`（Twemoji，彩色），用 <img> 引用 ——
 *     每台设备都是同一张彩色图，断网、换设备也一样。
 *
 * ⚠️ `<img>` 不能像 <svg> 那样跟着文字变色，所以**选中态（蓝底）时图标保持原色**：
 *    彩色图标压在蓝底上依然看得清（这就是选彩色的代价，也是用户要的效果）。
 * ⚠️ 别再把 width/height 去掉换成 min-width —— 那是给 emoji 打补丁用的。
 * ⚠️ 也别改回 `width:auto`：SVG 没有固有尺寸，会塌成 0 宽。
 * ⚠️ 换图标只要替换 public/img/nav/ 里的同名文件，**不用动 index.html**
 *    （见该目录下的 README.txt）。
 */
.nav-ico {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    /* 透明底 + 内联 SVG 的选中态不会有黑框：img 默认是 inline，加这两条更稳 */
    display: block;
    object-fit: contain;
}

.nav-sep {
    height: 1px;
    background: var(--c-border);
    margin: 8px 6px;
}

.side-foot {
    padding: 12px 14px 16px;
    border-top: 1px solid var(--c-border);
}

.conn-badge {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--c-text-2);
    font-weight: 500;
}

/* ★ 2026-10-05 用户第 4 条：连上了就**不显示**这条 —— `hidden` 挡不住 display:flex，得单配一条 */
.conn-badge[hidden] {
    display: none;
}

.conn-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--c-text-3);
    flex-shrink: 0;
}

.conn-badge.online .conn-dot {
    background: var(--c-success);
    box-shadow: 0 0 0 3px var(--c-success-l);
}

.conn-badge.local .conn-dot {
    background: var(--c-warn);
    box-shadow: 0 0 0 3px var(--c-warn-l);
}

/* 连不上服务器 —— 红色（黄那档留给了「本机预览模式」，测试/预览才会出现） */
.conn-badge.blocked .conn-dot {
    background: var(--c-danger);
    box-shadow: 0 0 0 3px var(--c-danger-l);
}

/* ★ 2026-10-04 用户第 3 条：侧栏那句「数据自动保存 · 支持一次性导出」已删
   （离线模式整套移除之后它就没意义了），对应的 .side-foot-tip 规则一并删。 */

.main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    /*
     * 左右内边距 = 把内容区「居中」的那一段空白。
     * 顶栏那条细分隔线还是要占满整屏（不然宽屏上会出现半截线），
     * 所以不缩窄顶栏本身，而是把里面的内容推到与 .content 同一条竖线上 ——
     * 大屏上标题与下面的卡片左右对齐，不会一个贴左一个居中。
     *
     * ★ 2026-10-01 用户第 1 条：「所有页面中，这一栏的高度缩小一点」——
     *   上下内边距 14 → 9（顶栏从 76px 收到 66px）。
     *   再小就顶到「☰ 菜单」按钮（40px）与标题两行（约 48px）了，
     *   所以 9px 是既能收窄、又不挤的安全值；改动前先量一遍 48 + 2×padding。
     */
    /* ★ 2026-10-04 用户第 10 条：顶栏压到与侧栏品牌栏同高（--head-h）——
       原来是 9px 内边距 → 66px，比左边高 5px，两条横线对不齐。
       现在 6px + min-height，内容（☰ 40px / 两行标题 ≈ 48px）刚好塞得下；
       万一哪天顶栏内容更高，它会自己长高（那时两条线会再次错开，记得一起调 --head-h）。 */
    box-sizing: border-box;
    min-height: var(--head-h);
    padding: 6px max(22px, calc((100% - var(--shell-max)) / 2));
    background: rgba(255, 255, 255, .94);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--c-border);
    position: sticky;
    top: 0;
    z-index: 40;
    flex-wrap: wrap;
}

/* ★ 左块 = 「☰ 菜单 + 标题」，与右边的 .topbar-actions 同为 flex:1 1 0（等宽），
   中间的「当前班级」胶囊才会精确落在中线上。
   菜单按钮必须包在左块**里面**（见 index.html 那段注释）—— 让它单独成一份的话，
   左边就多出 40px，胶囊会往右偏 20 多像素。 */
.topbar-left {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 1 1 0;
    min-width: 0;
}

.topbar-title {
    /* 在左块里吃掉剩下的宽度（标题 + 副标题长了就自己省略） */
    flex: 1 1 auto;
    min-width: 0;
}

/* ============ 当前班级指示器（顶栏） ============
   当前班级是全局设置，作业/考勤/成绩/座位/值日/学生信息都按它取数，
   放在顶栏是为了让「现在看的是哪个班」在任何页面都一目了然。

   ★ 2026-10-04 用户选定「方案 1 · 极简灰胶囊」：
     灰底 + 深色字 + 一个 6px 小圆点；**末尾不带**那个「可切换」的小尖头
     （用户明确不要）。
   为什么换掉旧样式：旧的是「浅蓝底 + 一轮蓝描边 + 深蓝粗体字 + 14px 遮罩图标」——
     三层颜色各不相同，描边看着发脏；14px 的遮罩图小尺寸下还会糊。
     现在一层灰底收掉全部，颜色只靠一个点表达。 */
.class-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 5px 11px;
    border-radius: 999px;
    background: #EFEFF3;
    border: none;
    color: var(--c-text);
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    max-width: 190px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.class-chip b {
    font-weight: 600;
}

/* 小圆点：代替原来的学位帽遮罩图（.cc-ico）——
   6px 的点缩到多小都清晰，也不会因为遮罩图加载失败而静默变空白。
   ★★ 颜色 = **账号角色**（2026-10-05 用户第 2 条定的口径）：
        · 青绿（Tiffany 蓝）→ 超级管理员；
        · 浅灰（#C7C7CC）  → 管理员（见下面的 .class-chip.as-admin）。
      原来锁死时还会在末尾挂一个 🔒 emoji，**已经撤掉**（用户：「不要锁的图片了」）：
      emoji 的颜色由系统写死、各平台长相还不一样，小尺寸发糊；一个点色足够。
      「为什么切不动」那句话仍在 chip.title 里。 */
.class-chip .cc-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    /* ★ 2026-10-04 晚 用户第 1 条：小圆点用 **Tiffany 蓝**（--c-tiffany）。 */
    background: var(--c-tiffany);
    flex: 0 0 auto;
}

/* 工具栏里的班级下拉：窄一点，别把搜索框挤没了 */
.field.class-pick {
    min-width: 132px;
    flex: 0 0 auto;
}

/* 行内版班级下拉（作业统计的标签栏右侧） */
.class-pick-inline {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex-shrink: 0;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--c-text-2);
    white-space: nowrap;
}

.class-pick-inline select {
    min-width: 132px;
    min-height: 36px;
    padding: 6px 28px 6px 11px;
}

/* 作业统计顶部：标签 + 提示 + 班级切换，始终排成一行 */
.hw-tabbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.hw-tabbar .hint {
    flex: 1;
    min-width: 0;
}

.topbar-title h1 {
    font-size: 19px;
    letter-spacing: -.3px;
}

.topbar-title p {
    font-size: 12.5px;
    color: var(--c-text-2);
    margin-top: 1px;
}

/* 首页日期后面的秒级时钟 */
.live-clock {
    display: inline-block;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--c-primary);
    background: var(--c-primary-l);
    border-radius: 6px;
    padding: 0 6px;
    margin: 0 2px;
    letter-spacing: .3px;
}

.topbar-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    justify-content: flex-end;
    /* 与 .topbar-title 等宽 —— 顶栏「三等分」居中靠的就是这一条 */
    flex: 1 1 0;
}

/* 展开导航：侧栏收起后这是唯一的入口，所以放大加粗 */
.btn-menu {
    width: 40px;
    height: 40px;
    font-size: 19px;
    border: 1.5px solid var(--c-border-strong);
    background: var(--c-surface);
    flex-shrink: 0;
}

.btn-menu:hover {
    border-color: var(--c-primary);
    color: var(--c-primary);
}

.content {
    flex: 1;
    /*
     * 宽度靠「左右内边距」限住并居中，而不是 max-width。
     * max-width 只能把盒子收窄，收窄之后它还是贴在左边 ——
     * 1920 的屏上右边空出一大片，看着像没适配。用 max() 两边各留
     * (100% - 1440)/2 的空白就自然居中了，窄屏又退回 22px 的正常边距。
     */
    padding: 18px max(22px, calc((100% - var(--shell-max)) / 2)) 28px;
    width: 100%;
}

/* ★ 2026-10-04 用户第 6 条：整页页脚删掉（「班主任工作平台 · 阿里云 ECS + SQLite」），
   .page-foot 规则连同元素一起删。 */

/* ============ 通用组件 ============ */
.card {
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    /* ★ 2026-10-04 晚（苹果扁平化收尾）：普通卡片**去掉投影**，只留 1px 发丝线 ——
       iOS 的分组列表就是「无影、靠分隔线分层」。投影只留给弹层 / 下拉浮层。 */
    padding: 18px 20px;
    margin-bottom: 16px;
}

.card:last-child {
    margin-bottom: 0;
}

.card-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
    flex-wrap: wrap;
}

.card-head h3 {
    font-size: 15px;
    flex: 1;
    min-width: 120px;
}

/* ★ 2026-10-04 晚 用户第 6 条：校历卡片头的**学期标题** —— 三个视图（按月/按学期/按年）
   统一成「<学期> · 共 N 周」，并当**正式标题**显示（比普通 h3 大一号、更粗）。 */
.card-head .cal-term-title {
    font-size: 18px;
    font-weight: 600;
    letter-spacing: -.2px;
}

.card-head .hint,
.hint {
    font-size: 12px;
    color: var(--c-text-2);
    font-weight: 400;
}

.grid-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 16px;
    /*
     * 网格下方也要留出和 .card 一样的 16px。
     * 只把里面的卡片 margin-bottom 抹成 0 是不够的 —— 网格自己没边距，
     * 紧跟着的下一张卡片就贴上来了（总览页「需要注意的学生」原来就是这么贴着的）。
     */
    margin-bottom: 16px;
}

/* 网格是最后一个元素时不留尾巴（跟 .card:last-child 一个道理） */
.grid-2:last-child {
    margin-bottom: 0;
}

/*
 * 网格里的卡片不吃「卡片自带的下边距」。
 * .card 默认 margin-bottom:16px、:last-child 又抹成 0 —— 单列排版时这是对的，
 * 但在网格里会变成「最后一张比同排的多出 16px」，一眼就看出没对齐。
 * 网格的间距一律交给上面的 gap。
 */
.grid-2 > .card {
    margin-bottom: 0;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 14px;
    min-height: 40px;
    /* ★ 2026-10-04 方案 E：胶囊形（iOS 按钮语言）。高度 / 内边距没动 ——
       测试里量的是「控件高度一致」，不量圆角。 */
    border-radius: 999px;
    /* ★ 2026-10-04 晚：描边从 1.5px 收到 **1px** —— 苹果的按钮基本不描边（宁可浅灰底），
       要留也只用发丝线，1.5px 会显「粗」。 */
    border: 1px solid var(--c-border-strong);
    background: var(--c-surface);
    color: var(--c-text);
    font-size: 13.5px;
    font-weight: 500;
    cursor: pointer;
    transition: background .15s, border-color .15s, transform .08s, box-shadow .15s;
    white-space: nowrap;
    line-height: 1.4;
}

.btn:hover {
    background: var(--c-surface-2);
    border-color: var(--c-primary);
    color: var(--c-primary);
}

.btn:active {
    transform: scale(.97);
}

.btn-primary {
    background: var(--c-primary);
    border-color: var(--c-primary);
    color: #fff;
}

.btn-primary:hover {
    background: var(--c-primary-d);
    border-color: var(--c-primary-d);
    color: #fff;
}

.btn-success {
    background: var(--c-success);
    border-color: var(--c-success);
    color: #fff;
}

.btn-success:hover {
    background: #0d6b48;
    border-color: #0d6b48;
    color: #fff;
}

.btn-danger {
    background: var(--c-danger);
    border-color: var(--c-danger);
    color: #fff;
}

.btn-danger:hover {
    background: #a82a22;
    border-color: #a82a22;
    color: #fff;
}

.btn-ghost {
    border-color: transparent;
    background: var(--c-surface-2);
    color: var(--c-text-2);
}

.btn-ghost:hover {
    background: #e7edf6;
    color: var(--c-primary);
}

.btn-sm {
    padding: 6px 11px;
    min-height: 34px;
    font-size: 12.5px;
    border-radius: 6px;
}

.btn-block {
    width: 100%;
}

.btn[disabled] {
    opacity: .5;
    pointer-events: none;
}

.icon-btn {
    width: var(--tap);
    height: var(--tap);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    border-radius: 8px;
    cursor: pointer;
    color: var(--c-text-2);
    font-size: 17px;
    transition: background .15s;
    flex-shrink: 0;
}

.icon-btn:hover {
    background: var(--c-surface-2);
}

/*
 * 按钮里的内联 SVG 图标（☰ 展开导航 / ✕ 关闭弹窗）。
 * ★ 为什么这两个也换成图：`☰`(U+2630) 和 `✕`(U+2715) 虽然哪儿都有，但在部分安卓
 *   机型上会被优先送给 **emoji 字体**去画 —— 结果是一个带底色的彩色方块，
 *   跟旁边干干净净的文字完全不搭。矢量图不受字体影响。
 * ⚠️ 用 `svg` 后代选择器统一尺寸，别去调 `.btn-menu { font-size }` —— 那对 SVG 无效。
 */
.btn-menu svg,
.icon-btn svg {
    width: 21px;
    height: 21px;
    display: block;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.field label {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--c-text-2);
}

input[type="text"],
input[type="date"],
input[type="number"],
input[type="month"],
input[type="password"],
input[type="time"],
input[type="search"],
select,
textarea {
    padding: 9px 11px;
    min-height: 40px;
    /* ★ 2026-10-04 晚（苹果扁平化收尾）：输入框改成 iOS 那套 ——
       **浅灰填充、无描边（平时）、圆角 10**；描边只在聚焦时出现（见下面 :focus）。
       同时补上 input[type="month"]（计费月份改回原生 month 后要用到这条）。
       ★ 2026-10-05 用户第 3 条：填充色从 --c-surface-2 换成**深一档的 --c-field** ——
         原来那个 #F0F0F5 跟「表头底色」是同一个值，白卡片上又淡又分不开。 */
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--c-field);
    outline: none;
    transition: border-color .15s, box-shadow .15s, background .15s;
    width: 100%;
    max-width: 100%;
}

textarea {
    min-height: 90px;
    resize: vertical;
    line-height: 1.6;
}

input:focus,
select:focus,
textarea:focus {
    border-color: var(--c-primary);
    box-shadow: 0 0 0 3px rgba(29, 95, 216, .18);
}

select {
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5 6 8.5l4-4' stroke='%23475467' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    padding-right: 28px;
}

input[type="checkbox"] {
    width: auto;
    min-height: auto;
    accent-color: var(--c-primary);
    width: 18px;
    height: 18px;
}

.toolbar {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
}

/* ------------------------------------------------------------------
   考勤记录弹窗（📊 考勤记录）顶部那行「起始日期 / 结束日期 / 查询」
   ------------------------------------------------------------------
   ★ 2026-10-05 用户第 1 条：左边日期框是全局的 **40px**，右边「查询」按钮是 `.btn-sm`
     的 **34px** —— 同一行两个控件不一样高，看着就是「没对齐」。这里统一成 40px。
   ⚠️ `height` 和 `min-height` 要一起写：`.btn` 是 inline-flex + padding，只写
      min-height 时，某些浏览器里实际高度仍由 padding 撑出来，会差 1~2px。
   ⚠️ 按钮字号顺带提到 13.5px —— 40px 高的框配 12.5px 的字会显得空（日期框里是 14px）。
   ================================================================== */
.toolbar.att-query-bar input {
    height: 40px;
    min-height: 40px;
}

.toolbar.att-query-bar .btn {
    height: 40px;
    min-height: 40px;
    font-size: 13.5px;
}

.toolbar .field {
    min-width: 130px;
}

/* ------------------------------------------------------------------
   横排字段：标签在左、值在右，**同一行**
   ------------------------------------------------------------------
   ★ 2026-10-01 用户要求（手机截图第 3 条）：考勤页「📅 考勤日期」下面本来另起一行放
     「2026-10-20 周二 · 上课日」，白占一行高度 —— 要求挪到标签**后面**、留一点间距。
   加这个类就行（`.field.field-inline`），以后哪个字段想横排直接套。
   与 meal-bar 那套（`.toolbar.meal-bar .field`）是同一个思路，只是那边绑死在餐费页。
   ------------------------------------------------------------------ */
.field.field-inline {
    flex-direction: row;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.field.field-inline > label {
    white-space: nowrap;
}

.field.field-inline .hw-none {
    color: var(--c-text-2);
    font-size: 13px;
}

/* hidden 就是 hidden（第二份清单，第一份在 1191 行）：`.card-head` 默认是 flex，
   会把它自己身上的 display:flex 顶掉 UA 的 [hidden]{display:none}。
   ★ 2026-10-01：考勤页学生卡片头改成「没筛人时整块收起」，就是靠这条生效的。 */
.card-head[hidden] {
    display: none !important;
}

.spacer {
    flex: 1;
}

/* 统计小卡 */
.stats-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}

.stat {
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 14px 16px;
    box-shadow: var(--shadow-s);
    min-width: 0;
}

.stat .num {
    font-size: 28px;
    font-weight: 800;
    line-height: 1.25;
    letter-spacing: -.6px;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stat .lbl {
    font-size: 12.5px;
    color: var(--c-text-2);
    font-weight: 600;
}

.stat .sub {
    font-size: 12px;
    color: var(--c-text-3);
    margin-top: 2px;
    line-height: 1.5;
}

.stat .bar {
    height: 6px;
    border-radius: 4px;
    background: var(--c-border);
    margin-top: 8px;
    overflow: hidden;
}

.stat .bar > i {
    display: block;
    height: 100%;
    border-radius: 4px;
    transition: width .5s ease;
}

.stat.t-ink .num {
    color: var(--c-text);
}

.stat.t-blue .num {
    color: var(--c-primary);
}

.stat.t-blue .bar > i {
    background: var(--c-primary);
}

.stat.t-green .num {
    color: var(--c-success);
}

.stat.t-green .bar > i {
    background: var(--c-success);
}

.stat.t-red .num {
    color: var(--c-danger);
}

.stat.t-red .bar > i {
    background: var(--c-danger);
}

.stat.t-amber .num {
    color: var(--c-warn);
}

.stat.t-amber .bar > i {
    background: var(--c-warn);
}

.stat.t-purple .num {
    color: var(--c-purple);
}

.stat.t-purple .bar > i {
    background: var(--c-purple);
}

/* ============================================================
   代课统计 的统计卡（2026-09-25 用户要求换风格：原来那种大卡又高又散，
   且**明确不要**跟作业统计那条细统计条 `.stat-strip` 长一个样）
   做法：**左色条紧凑卡**
     · 左侧 3px 竖条按语义染色（正常 / 部分被代课 / 全部被代课 一眼分开）；
     · 右边**数字与名称同一行**（数字 20px、名称 12px 跟在后面）；
     · 副标题压成 11px 灰字贴在下一行，超长省略号，不撑破格子。
   整块高约 62px（原 `.stat` 大卡约 100px），信息一条没少。
   ★ 类名沿用 `.stat / .num / .lbl / .sub` —— 页面逻辑与验收脚本都靠它们取数，
     只多挂一个 `.t2s` 修饰类换排版；**公用的 `.stat` 一个字没动**
     （总览 / 成绩 / 作息 / 设置 / 代课汇总还在用原来那套）。
   ⚠️ 这些规则必须排在 `.stat .num` / `.stat .lbl` / `.stat .sub` **之后**
     （权重相同，后来者胜），挪到前面会被公用规则盖回去。
   ============================================================ */
.t2s {
    position: relative;
    padding: 8px 12px 8px 15px;
    overflow: hidden;
}

.t2s::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background: var(--c-text-3);
}

.t2s-h {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
}

.t2s .num {
    flex: 0 0 auto;
    font-size: 20px;
    line-height: 1.2;
    letter-spacing: -.4px;
}

.t2s .lbl {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.t2s .sub {
    margin-top: 3px;
    font-size: 12px;
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.t2s.t-ink::before { background: var(--c-text-3); }
.t2s.t-blue::before { background: var(--c-primary); }
.t2s.t-green::before { background: var(--c-success); }
.t2s.t-red::before { background: var(--c-danger); }
.t2s.t-amber::before { background: var(--c-warn); }
.t2s.t-purple::before { background: var(--c-purple); }
.t2s.t-present::before { background: var(--c-att-present); }
.t2s.t-personal::before { background: var(--c-att-personal); }
.t2s.t-sick::before { background: var(--c-att-sick); }

/* 考勤那三色在公用的 .stat 上是**没有**配色的（只认 ink/blue/green/red/amber/purple），
   这里给代课统计 补上：数字与左色条同色，语义才连得上。 */
.t2s.t-present .num { color: var(--c-att-present); }
.t2s.t-personal .num { color: var(--c-att-personal); }
.t2s.t-sick .num { color: var(--c-att-sick); }

/* 顶部这排是**固定 6 张**：窗口一窄，auto-fit 会排成 4+2，尾巴上孤零零两张很难看。
   干脆钉成 3+3（再窄就 2+2+2），每行都是满的。
   ⚠️ 只管 `#t2Counts` —— 汇总档是 4 张卡，钉成 3 列反而会变 3+1。 */
@media (max-width: 1340px) {
    #t2Counts {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (max-width: 640px) {
    #t2Counts {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* 标签 */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 9px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 600;
    background: #eef2f7;
    color: var(--c-text-2);
    line-height: 1.6;
    white-space: nowrap;
}

.chip.blue {
    background: var(--c-info-l);
    color: #10558a;
}

.chip.red {
    background: var(--c-danger-l);
    color: #9c221b;
}

.chip.green {
    background: var(--c-success-l);
    color: #0b5e3f;
}

.chip.amber {
    background: var(--c-warn-l);
    color: #8a5403;
}

.chip.purple {
    background: var(--c-purple-l);
    color: #4b2d9c;
}

.chip.gray {
    background: #eef2f7;
    color: var(--c-text-2);
}

/* 表格 */
.table-wrap {
    overflow-x: auto;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    -webkit-overflow-scrolling: touch;
}

/* 会「很长」的名单表：固定高度、表内纵向滚动，表头吸顶跟着走。
   ★ 表头吸顶这里用 box-shadow 补下边框，不能用 border-bottom：
     table.tb 是 border-collapse: collapse，collapse 下 sticky 表头的 border 会被
     滚过去的行"吃掉"（滚起来就没有那条线了）。
   ★★ 2026-10-01 用户第 16 条：**所有页面上「内部上下滚动」的表格都用这个类**，
     高度统一由 --tbl-rows 决定（自适应、上限 12 行，见顶部那三个变量的注释）。
     ⚠️ 弹窗里的表格**不用**这个类，也**不要**自己写行内 `max-height` / `overflow`：
        弹窗本来就有高度上限（`.modal` 88vh）+ 正文可滚（`.modal-body`），
        表里再套一条滚动条就成了「大滚动条里套一根小滚动条」，手机上尤其别扭
        （2026-10-06 用户指着考勤记录的「按日期汇总」报的）。
        ★ 弹窗里的表格一律**让它自然撑开、由弹窗统一滚**。
        ★ 同一条也管**弹窗里的其它滚动区**：挑学生的卡片列表（请假登记 `#leaveList`、
          值日分组 `#dgList`）同理 —— 去掉行内高度，让弹窗统一滚（用户 2026-10-06 第 4 条）。 */
.table-wrap.tb-scroll {
    max-height: min(calc(var(--tbl-head-h) + var(--tbl-rows) * var(--tbl-row-h) + 6px),
            var(--tbl-max-vh));
    overflow-y: auto;
}

.table-wrap.tb-scroll table.tb thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    box-shadow: inset 0 -1.5px 0 var(--c-border);
}

/* ★ 2026-10-01 用户要求（手机截图第 5 条）：餐费「学生明细」这张表也要**表头吸顶** ——
   下拉能看到金额那一列是什么，不然滚到第 20 行就只剩一列数字。
   跟 `.tb-scroll` 同一套写法（box-shadow 代替 border-bottom，原因见上面那条注释）；
   区别只有一个：**高度不由这里定** —— 餐费页是在 `.table-wrap` 上写 inline 的 max-height:60vh。
   ⚠️ 用到这个类的前提：外层 `.table-wrap` 必须真能纵向滚（有 max-height 或 overflow:auto）。 */
.table-wrap table.tb.table-sticky thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    box-shadow: inset 0 -1.5px 0 var(--c-border);
}

table.tb {
    width: 100%;
    border-collapse: collapse;
    font-size: 13.5px;
    min-width: 460px;
}

table.tb th {
    background: var(--c-surface-2);
    padding: 10px 12px;
    text-align: left;
    font-weight: 600;
    color: var(--c-text-2);
    border-bottom: 1.5px solid var(--c-border);
    white-space: nowrap;
    font-size: 12.5px;
}

table.tb td {
    padding: 9px 12px;
    border-bottom: 1px solid #eaeff6;
}

table.tb tr:last-child td {
    border-bottom: none;
}

table.tb tbody tr:hover td {
    background: #f6f9fd;
}

table.tb .num {
    font-variant-numeric: tabular-nums;
}

table.tb tr.row-flash td {
    background: var(--c-primary-l) !important;
}

/* 可点击排序的表头 */
table.tb th.th-sort {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

table.tb th.th-sort:hover {
    background: var(--c-primary-l);
    color: var(--c-primary-d);
}

th.th-sort .sort-ind {
    display: inline-block;
    margin-left: 4px;
    font-size: 11.5px;
    color: var(--c-primary);
    vertical-align: 1px;
}

/* 成绩录入表的冻结列 */
table.tb th.sticky-l1,
table.tb td.sticky-l1 {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--c-surface);
}

table.tb th.sticky-l2,
table.tb td.sticky-l2 {
    position: sticky;
    left: 48px;
    z-index: 2;
    background: var(--c-surface);
}

table.tb thead th.sticky-l1,
table.tb thead th.sticky-l2 {
    z-index: 5;
    background: var(--c-surface-2);
}

table.tb tfoot td.sticky-l1 {
    background: var(--c-surface-2);
}

/* 分段控件 */
.seg {
    display: inline-flex;
    /* ★ 2026-10-04 晚（苹果扁平化收尾）：底色从偏蓝的 #e7edf6 换成 iOS 的**中性灰**
       （systemGray 半透明）—— 苹果的分段控件就是「中性灰槽 + 白色滑块」。 */
    background: rgba(118, 118, 128, .12);
    border-radius: 10px;
    padding: 3px;
    gap: 2px;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.seg button {
    border: none;
    background: none;
    padding: 8px 14px;
    min-height: 36px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--c-text-2);
    transition: background .15s, color .15s;
    white-space: nowrap;
}

.seg button.active {
    background: var(--c-primary);
    color: #fff;
    box-shadow: var(--shadow-s);
}

.sub-seg {
    margin-bottom: 14px;
}

/* 空态 */
.empty {
    text-align: center;
    padding: 36px 16px;
    color: var(--c-text-2);
    line-height: 1.9;
}

/* hidden 就是 hidden：作者样式里的 display:flex 会把 UA 的 [hidden]{display:none} 顶回去，
   .class-chip / .user-box / .nav-item 这类元件都踩过这个坑（未登录时顶栏中路露出一个空胶囊） */
.class-chip[hidden],
.user-box[hidden],
.nav-item[hidden],
.nav-sep[hidden] {
    display: none !important;
}

/* ---------------- 登录 / 账号 ---------------- */
.login-remember {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 2px 0 12px;
    font-size: 12.5px;
    color: var(--c-text-2);
    cursor: pointer;
    user-select: none;
}

.login-remember input {
    width: 15px;
    height: 15px;
    margin: 0;
    accent-color: var(--c-primary);
    cursor: pointer;
}

.login-tip {
    margin-top: 12px;
    font-size: 12px;
    color: var(--c-text-3);
    line-height: 1.7;
}

.login-tip b {
    color: var(--c-text-2);
}

/* 侧栏左下角的「当前登录用户」——
   两行布局：第一行 头像 + 显示名 + 退出，第二行 整宽显示角色 · 班级。
   侧栏只有 226px，挤在一行里的话文字只剩 85px，班级名会被截掉。 */
.user-box {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 9px 10px;
    margin-bottom: 9px;
    border-radius: 10px;
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    min-width: 0;
}

/* 第一行：头像 + 名字 + 退出按钮 */
.user-box .ub-top {
    display: flex;
    align-items: center;
    gap: 9px;
    min-width: 0;
}

.user-box .ub-ava {
    flex: none;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--c-primary);
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}

.user-box .ub-info {
    flex: 1;
    min-width: 0;
}

.user-box .ub-info b {
    display: block;
    font-size: 13px;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 第二行：角色 · 班级（整宽，班级名一般一行放得下） */
.user-box .ub-sub {
    font-size: 12px;
    color: var(--c-text-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.user-box .btn {
    flex: none;
    padding: 4px 8px;
    font-size: 12px;
}

/* ★★ 2026-10-05 用户第 2 条：点色改成**按角色**（不再按「能不能切班」）——
     超管 = 青绿（--c-tiffany）、**管理员 = 浅灰** #C7C7CC（iOS systemGray3）。
   用户原话：「只要是管理员就一律浅灰、只要是超管就一律青绿」。
   ⚠️ 类名是 `.as-admin`（角色），跟 `.locked`（确实切不动、只用于 chip.title 的说明）
      **不是一个东西**，别再把它俩合并。 */
.class-chip.as-admin .cc-dot {
    background: #C7C7CC;
}

/*
 * 未登录总览顶部的引导条。
 * 这是「请你登录」，不是「出问题了」—— 用主色调，别用警告的橙。
 */
.notice-bar.guest-bar {
    background: var(--c-primary-l);
    border-color: var(--c-primary);
    color: #16407f;
}

.notice-bar.guest-bar b {
    color: #123a75;
}

/*
 * 未登录时卡片里的占位。
 * 结构跟登录后一样（同样的 .empty 排版），只加一层淡斜纹 ——
 * 一眼看出这是「待解锁」，而不是「真的没有数据」。
 */
.guest-empty {
    background: repeating-linear-gradient(45deg,
            rgba(29, 95, 216, .055) 0 12px, transparent 12px 24px);
    border-radius: 10px;
    padding: 20px 12px;
}

/*
 * 只读账号（模块配成「仅查看」）：把页内那些「写」交互关掉。
 * 守卫照样会拦（数据层 + 云端各一道），这里只是别让人白点一次、
 * 也别让人以为点了没反应是坏了。
 */
.view-readonly .seat-grid,
.view-readonly .rest-list,
.view-readonly .tile,
.view-readonly .score-in,
.view-readonly .duty-task-chip,
.view-readonly [data-pick] {    pointer-events: none;
}

/* 关掉交互的元件别再显示手型，免得误导 */
.view-readonly .tile,
.view-readonly [data-pick] {
    cursor: default;
}

/* 只读模块里被禁用的输入框：视觉上也压成「不能改」的样子 */
.view-readonly input:disabled,
.view-readonly select:disabled,
.view-readonly textarea:disabled {
    background: var(--c-surface-2);
    color: var(--c-text-2);
    cursor: not-allowed;
}

/* 成绩录入框：不只是不能点，视觉上也压成「只读」的样子 */
.view-readonly .score-in {
    background: var(--c-surface-2);
    color: var(--c-text-2);
}

/* 只读账号的横幅：跟「数据不完整」的提示条一个样式，换个底色 */
.notice-bar.ro-bar {
    background: #eef2f8;
    border-color: #c3cedd;
    color: #43536b;
}

.notice-bar.ro-bar b {
    color: #2c3a52;
}

/* ---------------- 账号管理表 ---------------- */
.acc-table td {
    vertical-align: middle;
}

.acc-tag {
    display: inline-block;
    margin-right: 4px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 12px;
    background: var(--c-surface-2);
    color: var(--c-text-2);
    border: 1px solid var(--c-border);
}

.acc-tag.super {
    background: #ede9fe;
    color: #5b21b6;
    border-color: #c4b5fd;
}

.acc-tag.ok {
    background: #e8f6ec;
    color: #1c6b38;
    border-color: #b7e0c4;
}

.acc-tag.none {
    background: #fbeee6;
    color: #8a4b16;
    border-color: #eccdae;
}

/*
 * ★★ 2026-10-07 多学校：账号表里的「所属」那一格。
 *   本校 = 中性灰（跟大多数账号一样，不抢眼）；外校 = 琥珀底，
 *   一眼扫出「这几个账号看的是另一套数据」。
 */
.acc-tag.school {
    background: var(--c-warn-l);
    color: var(--c-warn);
    border-color: #e6c58a;
}

.acc-tag.school.main {
    background: var(--c-surface-2);
    color: var(--c-text-2);
    border-color: var(--c-border);
}

/*
 * ★ 2026-09-27 用户第 2 条：同一个班被两个账号同时设为班主任 —— **允许**，
 *   所以这里不是「出错」的红色，而是「你得知道」的琥珀色，挂在班级名后面。
 */
.acc-tag.dup {
    background: var(--c-warn-l);
    color: var(--c-warn);
    border-color: #e6c58a;
    margin-left: 4px;
    white-space: nowrap;
}

.acc-sub {
    margin-left: 6px;
    font-size: 12px;
    color: var(--c-text-3);
}

.acc-warn {
    color: var(--c-warn);
    font-size: 12.5px;
}

/*
 * 「班级名单 → 班主任」那一列（2026-09-27 用户第 6 条）。
 *   · .head-sel —— 下拉。列宽 158px，所以这里要**能压缩**：
 *     老师名字最多三四个字，`min-width:0` + `max-width:100%` 保证长名字也不会把表格顶宽；
 *   · .head-lock —— 由账号单向同步过来的，改不动，只显示一个锁 + 名字。
 *     颜色用主色淡底，一眼看出它跟旁边可选的下拉不是一回事。
 */
.head-sel {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    padding: 4px 6px;
    font-size: 12.5px;
}

.head-lock {
    display: inline-block;
    max-width: 100%;
    padding: 2px 8px;
    border-radius: 6px;
    background: var(--c-primary-l, #e8f0fe);
    color: var(--c-primary, #1a56c4);
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}

.acc-form .acc-perm {
    margin-top: 14px;
    padding: 12px 14px;
    border: 1px solid var(--c-border);
    border-radius: 10px;
    background: var(--c-surface-2);
}

.acc-perm-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 10px;
}

.acc-perm-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
    gap: 8px 14px;
}

.acc-perm-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.acc-perm-name {
    flex: 1;
    font-size: 13px;
    color: var(--c-text);
}

.acc-perm-row select {
    width: 96px;
    padding: 4px 6px;
    font-size: 12.5px;
}

.acc-perm-fixed {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
    gap: 6px 14px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed var(--c-border);
    font-size: 12.5px;
    color: var(--c-text-3);
}

/* 账号弹窗里的「任教班级」勾选表：一个老师可以带多个班（2026-09-23） */
.acc-cls-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 6px 10px;
    margin-top: 6px;
    padding: 10px 12px;
    border: 1.5px solid var(--c-border);
    border-radius: 10px;
    background: var(--c-surface-2);
}

.acc-cls-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    cursor: pointer;
}

/* 主班：一定在内，勾死不给取消 —— 免得老师以为可以把这个班也去掉 */
.acc-cls-item.main {
    color: var(--c-text-3);
    cursor: default;
}

.acc-cls-item input:disabled + span {
    font-weight: 600;
}

/* 数据不完整时的提示条（比如某一年的法定节假日还没公布） */
.notice-bar {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 10px 14px; margin-bottom: 14px;
    background: var(--c-warn-l); border: 1px solid var(--c-warn);
    border-radius: 10px; color: #7a4b00; font-size: 13px; line-height: 1.65;
}
.notice-bar .nb-ico { font-size: 15px; line-height: 1.5; flex: none; }
.notice-bar .nb-body { flex: 1; }
.notice-bar b { color: #7a4b00; }
.notice-bar .btn { flex: none; margin-top: 2px; }

.empty .big {
    font-size: 34px;
    display: block;
    margin-bottom: 8px;
    opacity: .85;
}

/* ============ 日历（作业统计） ============ */
.cal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}

.cal-head strong {
    font-size: 15px;
}

.cal-week {
    display: grid;
    /* ★ 同上：7 列用 minmax(0, 1fr)（见 .sc-m-grid 那段注释） */
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
    margin-bottom: 6px;
}

.cal-week span {
    text-align: center;
    font-size: 12px;
    color: var(--c-text-2);
    font-weight: 600;
}

.cal-grid {
    display: grid;
    /* ★ 同上：7 列用 minmax(0, 1fr)，别让内容把网格撑破 */
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
}

.cal-cell {
    position: relative;
    border: 1.5px solid transparent;
    background: var(--c-surface-2);
    border-radius: 9px;
    min-height: 46px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 4px 2px;
    transition: transform .1s, box-shadow .15s, border-color .15s;
}

.cal-cell.blank {
    background: transparent;
    pointer-events: none;
    border-color: transparent;
}

.cal-cell .d {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.1;
    color: var(--c-text-2);
}

/* 没有作业：浅灰、无标记 */
.cal-cell.no-hw {
    background: #f2f5fa;
    border-color: #e4eaf3;
}

.cal-cell.no-hw .d {
    color: var(--c-text-3);
    font-weight: 500;
}

.cal-cell .nodot {
    display: block;
    width: 14px;
    height: 3px;
    margin-top: 3px;
    border-radius: 2px;
    background: #dbe2ec;
}

/* 有作业：蓝底、深色数字、带数量角标 —— 与无作业形成明显区别 */
.cal-cell.has-hw {
    background: var(--c-primary-l);
    border-color: var(--c-primary);
}

.cal-cell.has-hw .d {
    color: var(--c-primary-d);
    font-weight: 800;
}

.cal-cell .dot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 16px;
    padding: 0 5px;
    margin-top: 2px;
    border-radius: 9px;
    background: var(--c-primary);
    color: #fff;
    font-size: 11.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/* 「今天」的完整画法在**文件末尾**（那段要压过节假日 / 周末 / 调休几条同权重的
   `.d` 颜色规则，只能放在最后）。这里只留一个兜底。
   ★ 2026-09-25 晚第二版：细描边 + 淡外发光，取消原来那圈 3.5px 粗内描边。 */
.cal-cell:not(.selected).today {
    border-color: var(--c-today);
    border-width: 2px;
    box-shadow: 0 0 0 2.5px rgba(196, 71, 26, .16);
}

.cal-cell.selected {
    background: var(--c-primary);
    border-color: var(--c-primary-d);
    box-shadow: 0 0 0 3px rgba(29, 95, 216, .28);
}

.cal-cell.selected .d {
    color: #fff;
}

.cal-cell.selected .dot {
    background: #fff;
    color: var(--c-primary-d);
}

.cal-cell.selected .nodot {
    background: rgba(255, 255, 255, .6);
}

.cal-legend {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: 12px;
    font-size: 12px;
    color: var(--c-text-2);
}

.cal-legend span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.cal-legend .lg {
    width: 14px;
    height: 14px;
    border-radius: 4px;
    display: inline-block;
}

.cal-legend .lg.has-hw {
    background: var(--c-primary-l);
    border: 1.5px solid var(--c-primary);
}

.cal-legend .lg.no-hw {
    background: #f2f5fa;
    border: 1.5px solid #e4eaf3;
}

.cal-legend .lg.today {
    background: transparent;
    border: 1.5px solid var(--c-today);
    box-shadow: 0 0 0 2px rgba(196, 71, 26, .16);
}

/*
 * index.html 顶部那张「图标总表」（<svg> 里一堆 <symbol>）。
 * <symbol> 本身不会被画出来，这里只是把承载它的 <svg> 彻底摁掉，
 * 免得某些老浏览器给它留出 0 高度也会占一行。
 */
.icon-sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* ============ 学生格子（作业 / 考勤 / 代课统计 三处共用） ============ */
/*
 * ★★ 2026-09-25 用户要求：这三处的姓名卡**大小必须统一**。
 *    尺寸取三处实测值的平均：
 *      宽  127 / 129 / 129  → 128px（网格最小列宽）
 *      高  107 /  74 /  96  →  92px（卡片最小高）
 *      姓名 28 /  17 /  19  →  21px
 *      间距 12 /  10 /  10  →  10px
 *    以前作业页是「大号姓名卡」（120px / 90px / 28px 字），跟考勤、代课统计 明显不一样。
 * ⚠️ 别再给 `.tile-grid.big` 单独放大 —— 它现在只剩类名，尺寸与普通格子一字不差。
 *    真要调尺寸就改这里的公共值，并且**三张页面都量一遍**（别只看作业页）。
 */
.tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
    gap: 10px;
}

/*
 * 作业统计（「全部作业」档）那张网格历史上叫「大号姓名卡」，markup 与测试都用着这个类名，
 * 所以类名保留；尺寸跟 `.tile-grid` 完全相同，没有「放大」这回事了。
 */
.tile-grid.big {
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
    gap: 10px;
}

.tile {
    background: var(--c-surface);
    border: 2px solid var(--c-border-strong);
    border-radius: var(--radius);
    padding: 12px 6px;
    text-align: center;
    cursor: pointer;
    user-select: none;
    position: relative;
    transition: transform .1s, box-shadow .15s, border-color .15s, background .15s;
    min-height: 92px;   /* ★ 三处统一（作业 / 考勤 / 代课统计） */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.tile:hover {
    box-shadow: var(--shadow-m);
}

.tile:active {
    transform: scale(.96);
}

.tile .nm {
    font-size: 21px;    /* ★ 三处统一（作业 28 / 考勤 17 / 代课2 19 → 21） */
    font-weight: 700;
    line-height: 1.35;
    word-break: break-all;
}

.tile .st {
    font-size: 12px;    /* ★ 三处统一（12.5 / 11.5 / 12 → 12） */
    margin-top: 2px;
    font-weight: 700;
    letter-spacing: .2px;
}

.tile .mark {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--c-purple);
    color: #fff;
    font-size: 13px;
    display: none;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    box-shadow: 0 0 0 2px #fff;
}

.tile.selected {
    border-color: var(--c-purple);
    background: var(--c-purple-l);
    box-shadow: 0 0 0 4px rgba(106, 69, 204, .32);
    transform: scale(1.03);
}

.tile.selected .mark {
    display: flex;
}

/* 作业三态：饱和描边 + 深色文字，彼此区别明显 */
.tile.s-not_submitted {
    background: #fde7e4;
    border-color: var(--c-danger);
}

.tile.s-not_submitted .nm {
    color: #8f1f17;
}

.tile.s-not_submitted .st {
    color: var(--c-danger);
}

.tile.s-submitted {
    background: #dfeefc;
    border-color: var(--c-info);
}

.tile.s-submitted .nm {
    color: #10558a;
}

.tile.s-submitted .st {
    color: var(--c-info);
}

.tile.s-corrected {
    background: #dcf4e8;
    border-color: var(--c-success);
}

.tile.s-corrected .nm {
    color: #0b5e3f;
}

.tile.s-corrected .st {
    color: var(--c-success);
}

/*
 * 考勤三态：**绿 / 蓝 / 橙**，三色互不相邻，扫一眼就能分清。
 * 三层一起拉开：底色、2px 描边、姓名与状态字色 —— 只靠底色深浅在投影仪上会糊。
 */
.tile.a-present {
    background: var(--c-att-present-l);
    border-color: var(--c-att-present);
}

.tile.a-present .nm {
    color: #075139;
}

.tile.a-present .st {
    color: #0b6b47;
}

/* 事假：蓝 */
.tile.a-personal {
    background: var(--c-att-personal-l);
    border-color: var(--c-att-personal);
}

.tile.a-personal .nm {
    color: #0b4c7d;
}

.tile.a-personal .st {
    color: #17629e;
}

/* 病假：橙 */
.tile.a-sick {
    background: var(--c-att-sick-l);
    border-color: var(--c-att-sick);
}

/* 姓名上方那行学号也跟着状态色走（跟作业页 .tile.s-* .no 一个路子） */
.tile.a-present .no {
    color: #075139;
}

.tile.a-personal .no {
    color: #0b4c7d;
}

.tile.a-sick .no {
    color: #8a4403;
}

.tile.a-sick .nm {
    color: #8a4403;
}

.tile.a-sick .st {
    color: #a35a05;
}

/* 状态字上再压一撮同色底纹：形状和深浅都不同，色弱也分得开 */
.tile.a-present .st,
.tile.a-personal .st,
.tile.a-sick .st {
    padding: 0 6px;
    border-radius: 999px;
}

.tile.a-present .st {
    background: rgba(18, 133, 90, .14);
}

.tile.a-personal .st {
    background: rgba(26, 117, 189, .14);
}

.tile.a-sick .st {
    background: rgba(217, 119, 6, .16);
}

/*
 * 考勤的第四种形态：**休息日**（2026-09-27 用户要求）。
 *
 * 周末 / 法定节假日 / 寒暑假春秋假这几天，按校历不用到校 ——
 * 卡片再写着「出勤」是**假数据**（谁都没登记，只是默认值），
 * 投影出来家长还以为孩子周六也上学。
 *
 * 所以这几天默认显示「休」：灰蓝底 + 斜纹 + 虚线描边，
 * 一看就跟「出勤（绿）/ 事假（蓝）/ 病假（橙）」不是一个东西，
 * 也跟左侧校历里休息日的冷灰调子对得上。
 * ★ 只压**没有登记过**的卡片 —— 老师真手工记了哪天（出勤 / 请假），
 *   那几张照旧按三态颜色显示，记录不会被藏起来。
 * ★ 2026-09-28：这张卡**是可以点的**（点一下从「休」变「出勤」，循环回到「休」），
 *   所以鼠标手势与按下反馈都跟别的卡片一样；原来那句 `cursor: default` 是
 *   「这张卡不能点」时代留下的，容易让人以为按不动。
 */
.tile.a-rest {
    background:
        repeating-linear-gradient(45deg, rgba(100, 116, 139, .09) 0 5px, rgba(100, 116, 139, 0) 5px 11px),
        #edf1f7;
    border-color: #c6d0de;
    border-style: dashed;
}

/* 多选模式下选中它：跟别的卡一样亮紫框 + 右上角 ✓（`.tile.selected` 那份规则管外观，
   这里只把灰字拉回正常色 —— 紫底上的灰字看着像「灰掉了」） */
.tile.a-rest.selected .nm,
.tile.a-rest.selected .st,
.tile.a-rest.selected .no {
    color: var(--c-text);
}

.tile.a-rest .nm {
    color: #64748b;
}

.tile.a-rest .no {
    color: #94a3b8;
}

.tile.a-rest .st {
    color: #64748b;
    background: rgba(100, 116, 139, .13);
}

/* 餐费统计：「正在查看的是哪一条历史」那一行、低年级周三没有午饭的提示 */
.meal-view {
    margin-top: 10px;
    padding: 7px 12px;
    border-radius: 10px;
    background: var(--c-surface-2);
    border: 1px dashed var(--c-border-strong);
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--c-text-2);
}

.meal-view b {
    color: var(--c-text);
    font-variant-numeric: tabular-nums;
}

/* 历史下拉：一条 = 一次统计，名字比较长，字号收一点 */
#mealPick {
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
}

.meal-note {
    font-size: 12.5px;
    margin-top: 8px;
    padding: 7px 12px;
    border-radius: 10px;
    background: #fdf0d6;
    border: 1px solid var(--c-warn);
    color: #8a5403;
}

.batchbar {
    display: none;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    background: var(--c-purple-l);
    border: 1.5px solid var(--c-purple);
    border-radius: var(--radius);
    padding: 10px 14px;
    margin-bottom: 14px;
}

.batchbar.show {
    display: flex;
}

.batchbar .info {
    font-weight: 700;
    color: #4b2d9c;
    font-size: 13.5px;
    margin-right: 4px;
}

/* ★ 2026-10-08 学生信息页的「批量删除」条：里面装的是删除操作，配色跟考勤那条（紫色）分开 ——
   红色是**危险**的意思，别让人把这一条当成"批量改状态"随手按下去。 */
.batchbar.danger {
    background: var(--c-danger-l);
    border-color: var(--c-danger);
}

.batchbar.danger .info {
    color: var(--c-danger);
}

/* 勾选列：勾子本身不用很大，但**整格**都能点（手机上尤其重要） */
.tb td.st-chk-cell {
    text-align: center;
    cursor: pointer;
    padding-left: 6px;
    padding-right: 6px;
}

.tb td.st-chk-cell input[type="checkbox"],
.tb thead th input[type="checkbox"] {
    width: 16px;
    height: 16px;
    margin: 0;
    cursor: pointer;
    vertical-align: middle;
}

/* 被勾中的那一行：给点底色，滚到哪儿都能一眼看出"这几个要删" */
.tb tbody tr.row-sel td {
    background: var(--c-danger-l);
}

/* ============ 作业页布局 ============ */
.hw-layout {
    display: grid;
    grid-template-columns: 250px minmax(0, 1fr);
    gap: 14px;
    align-items: start;
}

.hw-left {
    position: sticky;
    top: 84px;
}

/* 左侧日历整体收小：格子变矮、日期字号调小，把版面让给右侧的大号姓名卡 */
.hw-left .card-head {
    margin-bottom: 10px;
}

.hw-left .card-head h3 {
    font-size: 13.5px;
}

.hw-left .cal-head strong {
    font-size: 13.5px;
}

.hw-left .cal-week {
    margin-bottom: 4px;
}

.hw-left .cal-week span {
    font-size: 11.5px;
}

.hw-left .cal-grid {
    gap: 3px;
}

.hw-left .cal-cell {
    min-height: 34px;
    border-radius: 7px;
    padding: 2px 1px;
}

.hw-left .cal-cell .d {
    font-size: 12px;
    line-height: 1.15;
}

.hw-left .cal-cell .dot {
    min-width: 14px;
    height: 12px;
    padding: 0 4px;
    margin-top: 1px;
    border-radius: 7px;
    font-size: 11px;
}

.hw-left .cal-cell .nodot {
    width: 10px;
    height: 2px;
    margin-top: 2px;
}

.hw-left .cal-legend {
    margin-top: 8px;
    font-size: 12px;
}

/* 右侧顶部操作栏只占一行，省下的高度全留给下面的姓名格子 */
.hw-bar {
    padding: 12px 16px;
    margin-bottom: 12px;
}

.hw-bar .field {
    margin: 0;
}

.hw-bar .hw-pick {
    min-width: 190px;
}

.hw-bar .hw-pick select {
    min-height: 36px;
    padding: 6px 28px 6px 11px;
}

/* 作业统计工具栏里的「排序」下拉 */
.hw-bar .hw-sort,
.hw-right .hw-sort {
    min-width: 148px;
    flex: 0 0 auto;
}

.hw-right .hw-sort select {
    min-height: 36px;
    padding: 6px 28px 6px 11px;
}

/*
 * 统计条 .stat-strip —— 一条扁平的横条，替掉原来的那排 .stat 大卡
 * （2026-09-25 用户连着反馈三次「太占空间」：作业统计、考勤记录、餐费统计）。
 *
 * 设计要点：
 *   · 数字 + 名称排一行（数字 19px 粗体上色，名称 12.5px 灰字），
 *     比例 / 口径说明用小灰字跟在后面（`.ss-sub`），不用另起一行；
 *   · 要表达「整体怎么分的」再多一条 6px 高的**堆叠条**（`.ss-bar`）——
 *     比几根独立的进度条既省地方又直观；
 *   · 一行排不下就折行（`flex-wrap`），手机上再收一档字号。
 *   ⇒ 一排 100px 的卡片压到 ~34px，一屏能多看 3~4 行名单/明细。
 *
 * ⚠️ **公用的 `.stat` / `.stats-row` 一个字没动**：
 *    总览（看板，要气势）、成绩分析、作息、设置、代课汇总还在用那套大卡。
 *    要推广到哪个页面，改那个页面的调用就行（`core.js` 的 `statStripHTML()`）。
 *
 * 颜色沿用公用那套 `t-*` 名字（`.ss-*` 与 `.stat` 各写各的，互不影响）：
 *    t-ink/t-blue/t-green/t-red/t-amber/t-purple + 考勤专用 t-present/t-personal/t-sick
 */
.stat-strip {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 18px;
    padding: 8px 15px 9px;
    margin-bottom: 12px;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-s);
}

.stat-strip .ss {
    display: flex;
    align-items: baseline;
    gap: 5px;
    min-width: 0;
}

.stat-strip .ss-num {
    font-size: 19px;
    font-weight: 800;
    line-height: 1.3;
    letter-spacing: -.4px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.stat-strip .ss-lbl {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--c-text-2);
    white-space: nowrap;
}

.stat-strip .ss-sub {
    font-style: normal;
    font-size: 12px;
    color: var(--c-text-3);
    font-variant-numeric: tabular-nums;
}

.stat-strip .ss-bar {
    flex: 1 1 100%;
    display: flex;
    height: 6px;
    margin-top: 7px;
    border-radius: 4px;
    background: var(--c-border);
    overflow: hidden;
}

.stat-strip .ss-bar > i {
    display: block;
    height: 100%;
    transition: width .5s ease;
}

/* 数字的颜色 */
.stat-strip .ss.t-ink > .ss-num {
    color: var(--c-text);
}

.stat-strip .ss.t-blue > .ss-num {
    color: var(--c-primary);
}

.stat-strip .ss.t-green > .ss-num {
    color: var(--c-success);
}

.stat-strip .ss.t-red > .ss-num {
    color: var(--c-danger);
}

.stat-strip .ss.t-amber > .ss-num {
    color: var(--c-warn);
}

.stat-strip .ss.t-purple > .ss-num {
    color: var(--c-purple);
}

.stat-strip .ss.t-present > .ss-num {
    color: var(--c-att-present);
}

.stat-strip .ss.t-personal > .ss-num {
    color: var(--c-att-personal);
}

.stat-strip .ss.t-sick > .ss-num {
    color: var(--c-att-sick);
}

/* 堆叠条每一段的颜色（与上面同一套名字；t-ink 是兜底，忘写 cls 时也不会变成透明条） */
.stat-strip .ss-bar > i.t-ink {
    background: var(--c-text-3);
}

.stat-strip .ss-bar > i.t-blue {
    background: var(--c-primary);
}

.stat-strip .ss-bar > i.t-green {
    background: var(--c-success);
}

.stat-strip .ss-bar > i.t-red {
    background: var(--c-danger);
}

.stat-strip .ss-bar > i.t-amber {
    background: var(--c-warn);
}

.stat-strip .ss-bar > i.t-purple {
    background: var(--c-purple);
}

.stat-strip .ss-bar > i.t-present {
    background: var(--c-att-present);
}

.stat-strip .ss-bar > i.t-personal {
    background: var(--c-att-personal);
}

.stat-strip .ss-bar > i.t-sick {
    background: var(--c-att-sick);
}

/* 学生卡片上的学号：不再用长方框，直接排在姓名正上方（字号保持 11px 不变） */
.tile .no {
    display: block;
    min-height: 13px;
    font-size: 12px;
    font-weight: 800;
    line-height: 1.15;
    letter-spacing: .3px;
    color: var(--c-text-2);
    pointer-events: none;
}

/* 卡片本身有状态色时，学号跟着变色，和姓名一起读 */
.tile.s-not_submitted .no {
    color: var(--c-danger);
}

.tile.s-submitted .no {
    color: var(--c-info);
}

.tile.s-corrected .no {
    color: var(--c-success);
}

.tile.selected .no {
    color: rgba(255, 255, 255, .92);
}

/* ============================================================
   「选人」列表里名字前面的学号（请假登记 / 值日分组）
   ------------------------------------------------------------
   2026-09-25：这两处选人的搜索框改成支持「拼音 + 学号」之后，
   把班内学号 `#N` 也放到名字前面 —— 否则老师搜了「12」找到人，
   却看不出哪个是 12 号。做小做淡，不抢名字。
   ============================================================ */
.pick-no {
    font-style: normal;
    font-size: 12px;
    font-weight: 700;
    opacity: .7;
    margin-right: 4px;
    font-variant-numeric: tabular-nums;
}

.hw-none {
    display: flex;
    align-items: center;
    min-height: 36px;
    font-size: 13.5px;
    color: var(--c-text-3);
}

.hw-list {
    max-height: 62vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    -webkit-overflow-scrolling: touch;
}

.hw-list-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 12px;
    min-height: var(--tap);
    border: 1.5px solid var(--c-border);
    background: var(--c-surface);
    border-radius: 10px;
    cursor: pointer;
    text-align: left;
    transition: border-color .15s, background .15s;
}

.hw-list-item:hover {
    border-color: var(--c-primary);
    background: var(--c-surface-2);
}

.hw-list-item.active {
    border-color: var(--c-primary);
    background: var(--c-primary-l);
}

.hw-list-date {
    font-size: 12px;
    color: var(--c-text-3);
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
}

.hw-list-name {
    flex: 1;
    font-size: 13.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hw-list-badge {
    font-size: 12px;
    color: var(--c-primary);
    background: var(--c-primary-l);
    border-radius: 9px;
    padding: 1px 7px;
    flex-shrink: 0;
    font-weight: 600;
}

.hw-list-item.active .hw-list-badge {
    background: var(--c-primary);
    color: #fff;
}

/* ============================================================
   座位调整：顶部工具条（布局参数 + 排座按钮）
   ------------------------------------------------------------
   用户 2026-09-25 反馈「红框太占空间」：原来是上下两条 `.toolbar`，
   上一条放 行数/列数/两个过道 + 应用布局，下一条放 随机/智能/标记/清空/打印，
   白吃掉一整行高度（约 74px）—— 合成一条。
   ★★ 2026-10-01 用户第 5 条（电脑截图）又提了两点，都在这里改：
     · 「黄色方框里的内容改为一行显示」：4 个字段原来是「标签在上、输入框在下」占两行，
       现在标签在左、输入框在右，四个字段各自一行；
     · 「蓝色方框（输入框）与红色方框（随机排座按钮）大小、高度一致」：
       输入框 min-height 34px = `.btn-sm` 的高度，整条工具栏现在齐平。
   ⚠️ 宽度要算总数：4 个框 + 6 个按钮 + 8px 间距，1080 宽的窗口（可用约 1029px）
      放不下会折成两行（现在约 1057px）；1200 以上的窗口一行放得下。
   ============================================================ */
.toolbar.seat-bar {
    gap: 8px;
}

.toolbar.seat-bar .field {
    min-width: 0;
    flex: 0 0 auto;
    /* 标签在左、控件在右（`.field` 默认竖排，这里改成横排 = 用户说的「一行显示」） */
    flex-direction: row;
    align-items: center;
    gap: 6px;
}

.toolbar.seat-bar .field label {
    font-size: 12px;
    white-space: nowrap;
}

/* 行数 / 列数 / 两个过道：四个框一样大，高度对齐旁边的按钮 */
.toolbar.seat-bar .seat-num input,
.toolbar.seat-bar .seat-aisle input {
    width: 76px;
    min-height: 34px;
    padding: 5px 8px;
    text-align: center;
}

/* 窄屏（≤900px）按钮被全局规则压到 32px —— 输入框跟着一起收，四个框与按钮仍然齐平 */
@media (max-width: 900px) {

    .toolbar.seat-bar .seat-num input,
    .toolbar.seat-bar .seat-aisle input {
        min-height: 32px;
    }
}

@media (max-width: 620px) {

    .toolbar.seat-bar .seat-num input,
    .toolbar.seat-bar .seat-aisle input {
        width: 68px;
    }
}

/* ============ 座位表 ============ */
.seat-stage {
    overflow-x: auto;
    padding: 18px 14px 14px;
    background: linear-gradient(180deg, #fbfcfe, #f2f5fa);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    -webkit-overflow-scrolling: touch;
}

/* 讲台：字大、有图案，作为整张座位表的视觉锚点 */
.podium {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin: 0 auto 22px;
    padding: 16px 24px;
    background: linear-gradient(180deg, #f0f5fd, #dfe9f8);
    border: 2px dashed #a9bcd8;
    border-radius: 16px;
    box-shadow: 0 2px 0 #cdd9ea inset;
    color: #2c4a75;
}

.podium .podium-ico {
    font-size: 34px;
    line-height: 1;
}

.podium .podium-txt {
    font-size: 24px;
    font-weight: 800;
    letter-spacing: 10px;
    text-indent: 10px;
    line-height: 1;
}

/* 性别图例 */
.seat-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin: 4px 0 12px;
    font-size: 12.5px;
    color: var(--c-text-2);
}

.seat-legend .lg {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
}

.seat-legend .lg i {
    width: 16px;
    height: 16px;
    border-radius: 5px;
    border: 2px solid var(--c-border-strong);
    display: inline-block;
}

.seat-legend .lg-male i {
    background: #d9ecfd;
    border-color: #1a75bd;
}

.seat-legend .lg-female i {
    background: #fde4f2;
    border-color: #c2418f;
}

.seat-legend .lg-unknown i {
    background: #f1f3f7;
    border-color: #98a2b3;
}

.seat-legend .lg-empty i {
    background: repeating-linear-gradient(45deg, #f4f7fb, #f4f7fb 4px, #e6ebf3 4px, #e6ebf3 8px);
    border-style: dashed;
}

.seat-grid {
    display: grid;
    gap: 9px;
    min-width: min-content;
    margin: 0 auto;
}

.seat {
    background: var(--c-surface);
    border: 2px solid var(--c-border-strong);
    border-radius: 10px;
    min-height: 68px;
    min-width: 82px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: grab;
    position: relative;
    transition: transform .1s, box-shadow .15s, border-color .15s;
    padding: 5px 3px;
    user-select: none;
}

.seat:hover {
    box-shadow: var(--shadow-m);
    border-color: var(--c-primary);
}

.seat.dragging {
    opacity: .45;
}

/* 姓名大、学号小 */
.seat .sname {
    font-size: 18px;
    font-weight: 800;
    word-break: break-all;
    line-height: 1.25;
    text-align: center;
}

.seat .sno {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--c-text-3);
    line-height: 1.2;
    margin-top: 1px;
    letter-spacing: .3px;
}

.seat .pos {
    position: absolute;
    top: 2px;
    left: 5px;
    font-size: 11px;
    color: var(--c-text-3);
}

.seat .plus {
    font-size: 20px;
    color: var(--c-text-3);
}

.seat .ban {
    font-size: 17px;
    color: var(--c-text-3);
}

.seat.empty {
    background: repeating-linear-gradient(45deg, #f4f7fb, #f4f7fb 6px, #e9eef6 6px, #e9eef6 12px);
    color: var(--c-text-3);
    border-style: dashed;
}

.seat.disabled {
    background: #e7ebf2;
    border-style: dashed;
    opacity: .5;
    cursor: not-allowed;
}

/* ---- 按性别填充：男 / 女 / 未填，三种底色互不相同 ---- */
.seat.g-male {
    background: #d9ecfd;
    border-color: #1a75bd;
}

.seat.g-male .sname {
    color: #0f4c78;
}

.seat.g-female {
    background: #fde4f2;
    border-color: #c2418f;
}

.seat.g-female .sname {
    color: #8a1f5f;
}

.seat.g-unknown {
    background: #f1f3f7;
    border-color: #98a2b3;
}

.seat.g-unknown .sname {
    color: #475467;
}

.seat.drop-target {
    border-color: var(--c-primary);
    box-shadow: 0 0 0 4px rgba(29, 95, 216, .3);
    transform: scale(1.05);
}

/* ★ 2026-10-07「交换两列」模式里**已选中的那一列**：整列描边 + 浅底，
   一眼看出"第一列选的是哪一列"，再点另一列就互换。 */
.seat.swap-col {
    border-color: var(--c-primary);
    background: rgba(29, 95, 216, .10);
    box-shadow: inset 0 0 0 2px rgba(29, 95, 216, .45);
}

.seat.aisle {
    visibility: hidden;
    pointer-events: none;
    min-width: 22px;
    border: none;
}

/* ============ 待安排学生 ============ */
.rest-list {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.rest-chip {
    position: relative;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 8px 14px 7px;
    border-radius: 12px;
    border: 2px solid var(--c-border-strong);
    background: var(--c-surface);
    cursor: grab;
    font: inherit;
    color: var(--c-text);
    transition: transform .1s, box-shadow .15s, border-color .15s;
}

.rest-chip:hover {
    box-shadow: var(--shadow-m);
    border-color: var(--c-primary);
}

.rest-chip.dragging {
    opacity: .45;
}

.rest-chip.drop-before {
    box-shadow: -4px 0 0 0 var(--c-primary);
}

/* ★ 2026-10-01 用户要求（手机截图第 13 条）：**学号在上、姓名在下**（原来反着来），
   两行都居中 —— 卡片本身是 column + align-items:center，所以只要换 DOM 顺序就行
   （见 view-seating.js 的 rest-chip）。字号、内边距、卡片尺寸都不动。 */
.rest-chip .rp-name {
    font-size: 18px;
    font-weight: 800;
    line-height: 1.2;
}

.rest-chip .rp-no {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--c-text-3);
    line-height: 1.1;
}

.rest-chip.g-male {
    background: #d9ecfd;
    border-color: #1a75bd;
}

.rest-chip.g-male .rp-name {
    color: #0f4c78;
}

.rest-chip.g-female {
    background: #fde4f2;
    border-color: #c2418f;
}

.rest-chip.g-female .rp-name {
    color: #8a1f5f;
}

.rest-chip.g-unknown {
    background: #f1f3f7;
    border-color: #98a2b3;
}

.rest-chip.picked {
    border-color: var(--c-primary-d);
    box-shadow: 0 0 0 3px rgba(29, 95, 216, .25);
    transform: translateY(-2px);
}

/* ============ 课表 ============ */
/*
 * 课表查询那一行里的「班级 / 教师」下拉（用户 2026-09-25 反馈：别拉满整行，
 * 按内容宽度、最多 280px；2026-10-01 又要求「把班级下拉收窄，让右边的
 * 『共 N 节课 / 周』留在同一行」）。
 * ⚠️ 宽度**只写在这里**，别回写 inline —— inline 会盖掉下面那条窄屏覆盖，
 *    手机上就收不下来，「共 N 节课 / 周」又会被挤到第二行（用户截图第 11 条）。
 */
.toolbar .field.tt-pick {
    flex: 0 0 auto;
    min-width: 0;
    width: 240px;
    max-width: 240px;
}

.toolbar .field.tt-pick select {
    width: 100%;
    min-width: 0;
    text-overflow: ellipsis;
}

/* ≤620px：`.toolbar .field { min-width: 100% }` 那条会把字段撑满整行（就是「共 N 节课 / 周」
   被挤下去的原因），这里用更高的优先级把它按回去，并且再收窄一档 —— 窄屏一行放得下。 */
@media (max-width: 620px) {
    .toolbar .field.tt-pick {
        width: 158px;
        max-width: 158px;
    }
}

.tt-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* 固定表格布局：列宽平分、每行等高，整张表看起来才整齐 */
table.tt {
    border-collapse: separate;
    border-spacing: 5px;
    width: 100%;
    min-width: 720px;
    table-layout: fixed;
}

table.tt th:first-child,
table.tt td.period-cell {
    width: 104px;
}

table.tt th {
    font-size: 14px;
    color: var(--c-text-2);
    font-weight: 700;
    padding: 8px 4px;
    height: 38px;
    text-align: center;
    white-space: nowrap;
    background: var(--c-surface-2);
    border-radius: 8px;
}

table.tt th.today-col {
    background: var(--c-primary-l);
    color: var(--c-primary-d);
}

table.tt td {
    height: 68px;
    padding: 0;
    vertical-align: middle;
}

/* 节次列：只留「第 N 节」，上课时间不再显示，字号放大到一眼能看清 */
table.tt td.period-cell {
    background: var(--c-surface-2);
    border-radius: 10px;
    text-align: center;
    padding: 4px 6px;
    font-size: 15px;
    color: var(--c-text-2);
    line-height: 1.35;
}

table.tt td.period-cell .p-no,
table.tt td.period-cell strong {
    display: block;
    font-size: 16px;
    color: var(--c-text);
}

.tt-cell {
    background: var(--c-surface);
    border: 1.5px solid var(--c-border);
    border-radius: 10px;
    padding: 6px 5px;
    text-align: center;
    /* ★★ 2026-10-04 用户报「鼠标移到课表上还是小手」：
       课表查询页从此**只读**（改课表在「数据与设置」），格子不再是可点控件 ——
       光标必须是箭头，hover 高亮也一并撤掉，否则等于在骗人「这里能点」。
       唯一还能点的地方是设置里的编辑面板 #tteSheet（下面单独给它 pointer）。 */
    cursor: default;
    height: 68px;
    box-sizing: border-box;
    transition: border-color .15s, box-shadow .15s;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 1px;
    overflow: hidden;
}

/* ★ 只有「数据与设置 → ✏️ 课表导入与编辑」那个面板里的格子还能点（2026-10-04）。
   查询页的格子上这两种状态一律不存在，鼠标移过去就是普通箭头。 */
#tteSheet .tt-cell {
    cursor: pointer;
}

#tteSheet .tt-cell:hover {
    border-color: var(--c-primary);
    box-shadow: var(--shadow-s);
}

.tt-cell .sub {
    font-weight: 800;
    font-size: 17px;
    line-height: 1.25;
}

.tt-cell .tch {
    font-size: 12.5px;
    color: var(--c-text-2);
}

.tt-cell.blank {
    border-style: dashed;
    color: var(--c-text-3);
    background: var(--c-surface-2);
}

/* ---- 课表（班级 / 教师两个视图共用）：照着学校用的纸质课表排 ----
   实线网格 + 左边一列「上午 / 下午」纵向合并，一眼能看出半天排了几节。
   ⚠️ 班级课表原来用的是分离式圆角格子，2026-09-23 用户要求统一成这一套。 */
.tt-title {
    text-align: center;
    font-size: 19px;
    letter-spacing: 1px;
    margin: 2px 0 10px;
}

.tt-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    font-size: 12.5px;
    color: var(--c-text-2);
    padding: 0 2px 6px;
}

/* 中间那句「点格子可改学科与教师」（只给能改的账号看）—— 整行现在只剩它 */
.tt-meta-hint {
    flex: 1;
    text-align: center;
    color: var(--c-text-3);
}

.tt-meta:empty {
    display: none;
}

/* 列宽靠 colgroup 钉死：上午 / 下午 + 节次两列只占很小一条，
   剩下的宽度全给星期一到星期五 —— 课表要看的是上什么课（用户 2026-09-23） */
table.tt.tt-sheet col.tt-c-part {
    width: 30px;
}

table.tt.tt-sheet col.tt-c-period {
    width: 42px;
}

table.tt.tt-sheet {
    border-collapse: collapse;
    border-spacing: 0;
    min-width: 620px;
}

table.tt.tt-sheet th,
table.tt.tt-sheet td {
    border: 1px solid var(--c-border);
    border-radius: 0;
    background: var(--c-surface);
}

table.tt.tt-sheet th:first-child {
    width: auto;
}

table.tt.tt-sheet th {
    height: 40px;
}

table.tt.tt-sheet td {
    height: 64px;
}

table.tt.tt-sheet th.tt-part-h {
    background: var(--c-surface-2);
}

table.tt.tt-sheet td.tt-part {
    width: 30px;
    padding: 4px 0;
    background: var(--c-surface-2);
    color: var(--c-text-2);
    font-size: 13px;
    text-align: center;
    vertical-align: middle;
    letter-spacing: 4px;
    writing-mode: vertical-rl;
    text-orientation: upright;
}

table.tt.tt-sheet td.period-cell {
    width: 42px;
    background: var(--c-surface);
    padding: 4px 0;
}

/* 第几节只是坐标，不抢戏：淡一点、细一点，眼睛先落到学科上 */
table.tt.tt-sheet td.period-cell .p-no {
    font-size: 15px;
    font-weight: 600;
    color: var(--c-text-3);
}

/* 今天那一列：表头实心蓝底、整列淡蓝，扫一眼就知道今天星期几 */
table.tt.tt-sheet th.today-col,
table.tt.tt-sheet td.today-col {
    background: var(--c-primary-l);
}

table.tt.tt-sheet th.today-col {
    background: var(--c-primary);
    border-color: var(--c-primary-d);
    color: #fff;
    font-weight: 800;
    font-size: 15px;
    box-shadow: inset 0 -3px 0 var(--c-primary-d);
}

table.tt.tt-sheet .tt-cell {
    border: none;
    border-radius: 0;
    min-height: 0;
    height: 100%;
}

/* 个人课表的空课格：跟纸一样留白，不要班级课表那种「待编辑」的灰底 */
table.tt.tt-sheet .tt-cell.blank {
    background: var(--c-surface);
    border-style: none;
}

/* 「任教情况」卡片：老师一周教什么、带哪些班、什么时间 */
.tt-duty {
    display: flex;
    flex-direction: column;
}

.tt-duty-row {
    display: flex;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px dashed var(--c-border);
    font-size: 13.5px;
    line-height: 1.75;
}

.tt-duty-row:last-child {
    border-bottom: none;
}

.tt-duty-k {
    flex: 0 0 76px;
    color: var(--c-text-2);
    font-size: 12.5px;
}

.tt-duty-v {
    flex: 1;
    min-width: 0;
    word-break: break-word;
}

/* ============ 值日 ============ */
.duty-day {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 12px 0;
    border-bottom: 1px solid var(--c-border);
}

.duty-day:last-child {
    border-bottom: none;
}

.duty-date {
    width: 92px;
    flex-shrink: 0;
    font-size: 12.5px;
    color: var(--c-text-2);
    line-height: 1.5;
}

.duty-date strong {
    display: block;
    font-size: 14px;
    color: var(--c-text);
}

.duty-body {
    flex: 1;
    min-width: 0;
}

.duty-group-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 4px 0;
}

.member-chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    border-radius: 20px;
    font-size: 12.5px;
}

/* ============ 成绩分析 ============ */
.conc-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.conc-list li {
    position: relative;
    padding-left: 20px;
    font-size: 13.5px;
    line-height: 1.75;
    color: var(--c-text);
}

.conc-list li::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 9px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--c-primary);
}

.diag-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
}

.diag-card {
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 13px 15px;
    background: var(--c-surface-2);
}

.diag-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}

.diag-head strong {
    font-size: 15px;
}

.diag-metrics {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    font-size: 12.5px;
    color: var(--c-text-2);
    margin-bottom: 8px;
}

.diag-metrics b {
    color: var(--c-text);
    font-variant-numeric: tabular-nums;
}

.diag-text {
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--c-text-2);
}

.corr-table td.corr-cell,
.corr-table th {
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.corr-table tbody th {
    background: var(--c-surface-2);
}

.dist-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
}

.dist-label {
    width: 88px;
    flex-shrink: 0;
    font-size: 12.5px;
    color: var(--c-text-2);
}

.dist-bar {
    flex: 1;
    height: 12px;
    background: var(--c-border);
    border-radius: 6px;
    overflow: hidden;
}

.dist-bar > i {
    display: block;
    height: 100%;
    background: var(--c-primary);
    border-radius: 6px;
    transition: width .5s ease;
}

.dist-num {
    width: 56px;
    text-align: right;
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
    color: var(--c-text-2);
}

/* ============ Toast / Modal / Loading ============ */
/*
 * ★★ 2026-10-04 晚（第三批）用户第 4 条：「确定整个系统没有特别深的颜色」。
 *   这块原来是近黑的 #101828（L*≈8，全站唯一的「深色面」，比正文黑还深），
 *   压在只有浅色的界面上很跳。改成**浅色胶囊**：白底 + 轻毛玻璃 + 发丝线 + 深色字。
 *   ⚠️ 别只把底色改浅就完事 —— 少了毛玻璃、描边和阴影三样，白底提示会糊在
 *      白卡片上看不见（这个提示是「保存成功」这类反馈，看不见等于没提示）。
 */
.toast {
    position: fixed;
    left: 50%;
    bottom: 34px;
    transform: translateX(-50%) translateY(90px);
    background: rgba(255, 255, 255, .94);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    backdrop-filter: saturate(180%) blur(14px);
    border: 1px solid var(--c-border);
    color: var(--c-text);
    padding: 12px 22px;
    border-radius: 10px;
    font-size: 13.5px;
    font-weight: 500;
    box-shadow: var(--shadow-l);
    opacity: 0;
    transition: all .32s cubic-bezier(.22, 1, .36, 1);
    z-index: 9000;
    pointer-events: none;
    max-width: min(90vw, 460px);
    text-align: center;
}

.toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

.modal-mask {
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, .5);
    backdrop-filter: blur(3px);
    display: none;
    align-items: center;
    justify-content: center;
    padding: 18px;
    z-index: 8000;
}

.modal-mask.open {
    display: flex;
}

.modal {
    background: var(--c-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-l);
    width: 100%;
    max-width: 640px;
    max-height: 88vh;
    display: flex;
    flex-direction: column;
    animation: pop .22s cubic-bezier(.22, 1, .36, 1);
}

@keyframes pop {
    from {
        opacity: 0;
        transform: scale(.96) translateY(12px);
    }

    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

.modal-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px 12px;
    border-bottom: 1px solid var(--c-border);
}

.modal-head h3 {
    flex: 1;
    font-size: 16px;
}

.modal-body {
    padding: 16px;
    overflow-y: auto;
    flex: 1;
    -webkit-overflow-scrolling: touch;
}

.modal-foot {
    padding: 12px 16px 14px;
    border-top: 1px solid var(--c-border);
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    flex-wrap: wrap;
}

.modal-foot:empty {
    display: none;
}

.loading {
    position: fixed;
    inset: 0;
    background: rgba(255, 255, 255, .6);
    backdrop-filter: blur(2px);
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 9500;
}

.loading.show {
    display: flex;
}

.spin {
    width: 34px;
    height: 34px;
    border: 3px solid var(--c-border-strong);
    border-top-color: var(--c-primary);
    border-radius: 50%;
    animation: rot .7s linear infinite;
}

@keyframes rot {
    to {
        transform: rotate(360deg);
    }
}

/* ============ 登录 ============ */
.login-overlay {
    position: fixed;
    inset: 0;
    background: linear-gradient(150deg, #e8eefa, #dde6f6 60%, #d6e0f3);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    z-index: 9999;
}

.login-overlay[hidden] {
    display: none;
}

.login-card {
    background: var(--c-surface);
    border-radius: 20px;
    box-shadow: var(--shadow-l);
    padding: 32px 28px 24px;
    width: 100%;
    max-width: 370px;
    text-align: center;
}

/* 登录卡上的校徽（原来是 📚 emoji）。56px 圆图，两倍屏用 128px 的源图刚好清楚 */
.login-logo {
    width: 56px;
    height: 56px;
    display: block;
    margin: 0 auto 10px;
    border-radius: 50%;
}

.login-card h1 {
    font-size: 19px;
    margin-bottom: 4px;
}

.login-sub {
    color: var(--c-text-2);
    font-size: 13px;
    margin-bottom: 20px;
}

.login-card input {
    text-align: center;
    padding: 12px 14px;
    margin-bottom: 12px;
}

.login-err {
    color: var(--c-danger);
    font-size: 12.5px;
    min-height: 18px;
    margin-top: 8px;
}

.login-local {
    border: none;
    background: none;
    color: var(--c-text-2);
    font-size: 12.5px;
    cursor: pointer;
    text-decoration: underline;
    margin-top: 4px;
}

.login-local:hover {
    color: var(--c-primary);
}

/* ============ 响应式 ============ */
.only-mobile {
    display: none;
}

/* 侧栏默认收起，遮罩在所有屏幕尺寸下都要能用 */
.sidebar-mask {
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, .4);
    z-index: 55;
    display: none;
}

.sidebar-mask.show {
    display: block;
}

/* —— 平板 / 窄屏 —— */
@media (max-width: 1000px) {
    .hw-layout {
        grid-template-columns: 1fr;
    }

    .hw-left {
        position: static;
    }

    .hw-list {
        max-height: 260px;
    }

}

/* —— 手机 —— */
@media (max-width: 900px) {
    .only-mobile {
        display: inline-flex;
    }

    .content {
        padding: 14px 12px calc(24px + env(safe-area-inset-bottom));
    }

    .topbar {
        padding: 10px 12px;
        gap: 8px;
    }

    .topbar-title h1 {
        font-size: 17px;
    }

    .topbar-title p {
        font-size: 12px;
    }

    .class-chip {
        max-width: 130px;
        padding: 4px 9px;
        font-size: 12px;
    }
}

/* —— 手机 —— */
@media (max-width: 620px) {

    /* ★ 手机上三处也统一（作业 / 考勤 / 代课统计）。
       2026-09-25 用户反馈「手机适配的字号再大一点」—— 姓名 16 → 19px、状态 11 → 12.5px，
       卡片最小高跟着 76 → 88px，三处还是一样高。
       ⚠️ 列宽下限**故意用 92px 而不是更大**：360~430px 的手机都要能排下**一行 3 个**
         （390px 量出来是 107×88）。取 96px 以上时，360px 的老安卓会被挤成一行 2 个，
         一屏看不了几个人。改这个值务必在三档视口（360 / 390 / 430）各量一遍。 */
    .tile-grid,
    .tile-grid.big {
        grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
        gap: 8px;
    }

    .tile {
        min-height: 88px;
        padding: 10px 4px;
    }

    .tile .nm {
        font-size: 19px;
    }

    .tile .st {
        font-size: 12.5px;
    }

    .stat .num {
        font-size: 23px;
    }

    .stats-row {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 9px;
    }

    .stat {
        padding: 11px 12px;
    }

    /* 手机上统计条要能折行：字号与间距一起收小，比例小字留着（很省地方且有用） */
    .stat-strip {
        gap: 3px 13px;
        padding: 8px 12px 9px;
    }

    .stat-strip .ss-num {
        font-size: 17px;
    }

    .stat-strip .ss-lbl {
        font-size: 12px;
    }

    .stat-strip .ss-sub {
        font-size: 12px;
    }

    .card {
        padding: 14px 13px;
        border-radius: var(--radius);
    }

    .toolbar .field {
        min-width: 100%;
    }

    /*
     * 按钮的 flex-basis 必须是 auto。
     * 写成 `flex: 1`（basis 0）时，浏览器算「这一行放不放得下」用的是 0，
     * 于是所有按钮都判定为「放得下」，挤在一行里再也换不了行 ——
     * 最后一行就被挤出卡片外面（餐费页的「复制金额」原来就是这么被切掉的）。
     */
    .toolbar .btn {
        flex: 1 1 auto;
    }

    /* .spacer 是「把后面的按钮推到右边」用的；手机上要换行，它只会白占位置 */
    .toolbar .spacer {
        display: none;
    }

    /*
     * 顶栏的动作按钮：另起一行、靠左、按内容自然宽度排，放不下就换行。
     *
     * 关键是**别给 flex: 1**：
     *   · flex:1（basis 0）会让只有一个按钮的页面（未登录时的「登录」）被拉成
     *     一条占满整行的大蓝条，看着像个页头横幅；
     *   · 也会让一行按钮互相挤到放不下、又不换行，最后顶着右边缘被切掉。
     * 换成 flex: 0 1 auto 之后，按钮该多大就多大，多了就换下一行，一个都不会藏起来。
     */
    .topbar-actions {
        order: 3;
        width: 100%;
        justify-content: flex-start;
    }

    .topbar-actions .btn {
        flex: 0 1 auto;
    }

    /* 卡片标题旁的那行小字（比如「点击卡片循环切换…」）另起一行，别跟标题挤 */
    .card-head .hint {
        flex-basis: 100%;
    }

    /* 请假名单：手机上掉成一列 */
    .leave-list {
        grid-template-columns: 1fr;
    }

    /* 弹窗在手机上接近全屏 */
    .modal-mask {
        padding: 0;
        align-items: flex-end;
    }

    .modal {
        max-width: 100% !important;
        max-height: 92vh;
        border-radius: 18px 18px 0 0;
    }

    .modal-foot {
        flex-direction: column-reverse;
        padding-bottom: calc(14px + env(safe-area-inset-bottom));
    }

    .modal-foot .btn {
        width: 100%;
    }

    /* 表格横向滚动时给出可滑动的视觉提示 */
    .table-wrap {
        position: relative;
        background:
            linear-gradient(to right, var(--c-surface) 30%, rgba(255, 255, 255, 0)),
            linear-gradient(to left, var(--c-surface) 30%, rgba(255, 255, 255, 0)) 100% 0;
        background-repeat: no-repeat;
        background-size: 32px 100%, 32px 100%;
        background-attachment: local, local;
    }

    /*
     * 手机上的表格：min-width 从「420px 硬底线」改成「内容自己撑」。
     * 420px 装不下 7 列的餐费明细，浏览器只好把每一列都压到最窄 ——
     * 「演示学生1」被压成一个字一行、金额被切掉，看着就是「显示错误」。
     * 让表格按内容撑开、由 .table-wrap 横向滚动，每列都完整，才是手机上该有的样子。
     */
    table.tb {
        min-width: max-content;
    }

    table.tb th,
    table.tb td {
        white-space: nowrap;
    }

    /* 会很长的那一列（请假明细等）除外：一行截断 + 省略号，别把表撑到天边 */
    table.tb th.clip,
    table.tb td.clip {
        max-width: 46vw;
    }

    table.tb th.wrap,
    table.tb td.wrap {
        white-space: normal;
    }

    .duty-day {
        flex-direction: column;
        gap: 6px;
    }

    .duty-date {
        width: auto;
        display: flex;
        align-items: baseline;
        gap: 8px;
    }

    .cal-cell {
        min-height: 42px;
    }

    .seg {
        width: 100%;
    }

    .seg button {
        flex: 1;
    }

    .hw-list {
        max-height: 220px;
    }

    /* 手机上日历格子保留可点按的高度 */
    .hw-left .cal-cell {
        min-height: 40px;
    }

    .hw-left .cal-cell .d {
        font-size: 12.5px;
    }
}

/* —— 超小屏 —— */
@media (max-width: 420px) {
    /* 超小屏：只收列宽，**不再单独把字号调小**。
       ⚠️ 这里原来写的是 `.tile .nm { font-size: 15px }`，而它比 620px 那一档更靠后，
         于是 360~420px 的手机（也就是绝大多数手机）字号全被压回 15px ——
         用户在手机上看到「字太小」就是这个原因。字号一律只在 620px 那一档定。 */
    .tile-grid,
    .tile-grid.big {
        grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
    }

    .stats-row {
        grid-template-columns: 1fr 1fr;
    }

    .stat .num {
        font-size: 21px;
    }
}

/* 支持触屏的设备：加大点按区域 */
@media (hover: none) {

    .btn,
    .nav-item,
    .seg button {
        min-height: var(--tap);
    }

    .btn-sm {
        min-height: 38px;
    }
}

/* 打印 */
@media print {

    .sidebar,
    .topbar,
    .batchbar,
    .toolbar,
    .no-print,
    .topbar-actions {
        display: none !important;
    }

    .main {
        margin-left: 0;
    }

    body {
        background: #fff;
    }

    .card {
        box-shadow: none;
        border-color: #ccc;
        page-break-inside: avoid;
    }

    .hw-layout {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
   工作日 / 休息日标记（中国法定节假日 + 调休）
   ------------------------------------------------------------
   全站统一的「休 / 班」小角标，凡是出现日期的地方都用它，
   这样一眼就能分清今天是不是要上学。
   ============================================================ */
.day-tag {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 5px;
    font-size: 11.5px;
    font-weight: 800;
    line-height: 1;
    vertical-align: middle;
    flex-shrink: 0;
}

/* 休息日（法定节假日 / 周末） */
.day-tag.off {
    background: #fde8d3;
    color: #b45309;
    border: 1px solid #f0bd85;
}

/* 调休上班日：虽然是周六周日，但要上学 */
.day-tag.work {
    background: var(--c-primary);
    color: #fff;
}

/* 日历里的休息日：底色比别的日期暖一些；调休日用虚线蓝框 */
.cal-cell.is-weekend:not(.selected) {
    background: #eaedf3;
    border-color: #dee3ec;
}

.cal-cell.is-holiday:not(.selected) {
    background: #fdf1e3;
    border-color: #f0cfa4;
}

.cal-cell.is-holiday:not(.selected) .d {
    color: #b45309;
}

/* ★ 2026-10-05 晚：「调休上课」那两条规则在早先重写配色时**被写过两遍**
   （这里一遍、下面「五种状态」那一节又一遍，内容一模一样，只有后面那份生效）。
   这一批把它**收敛到下面那一处**（那里有完整注释），这里只留个指路牌 ——
   否则以后改调休的配色只改了这儿，会被下面那份悄悄盖回去，怎么改都不生效。 */

.cal-cell .cal-tag {
    position: absolute;
    top: 2px;
    right: 3px;
    display: inline-flex;
    line-height: 0;
}

.cal-cell .cal-tag .day-tag {
    height: 13px;
    min-width: 13px;
    padding: 0 3px;
    font-size: 11px;
}

.cal-legend .lg.rest {
    background: #fdf1e3;
    border: 1.5px solid #f0cfa4;
}

/* ⚠️ 下面这一组 `.cal-legend .lg.*` 是**旧版图例**的遗留样式（那时图例画的是小色块）。
   现在的图例改成「拿一个真的日格当样例」（`.sc-lg-demo`），HTML 里已经没有 `.lg` 了。
   ★ 2026-10-05 晚：这里也跟着换成新的调休配色 —— 不是为了它自己，
     而是怕以后有人搜「调休用的什么紫」搜到这儿，照着旧值又抄回去。 */
.cal-legend .lg.makeup {
    background:
        repeating-linear-gradient(45deg, rgba(175, 82, 222, .15) 0 3px, rgba(175, 82, 222, 0) 3px 8px),
        var(--c-makeup-l);
    border: 1.5px solid var(--c-makeup);
}

/* ============================================================
   日历右下角的「节假日数据来源」一行
   ------------------------------------------------------------
   ★ 2026-10-04 用户要求：这行整个从日历页拿掉（数据源已收归后端，
   手动同步只留「数据与设置」一个入口）—— HTML 生成处（core.js 的
   buildCalendarHolidayLine）与这条样式一起删。
   ============================================================ */
/* 设置页的节假日同步卡片 */
.holiday-sync-row td {
    font-size: 13px;
}

.holiday-state {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
}

.holiday-state.synced {
    background: #e7f6ec;
    color: #1a7f37;
}

.holiday-state.builtin {
    background: var(--c-surface-2);
    color: var(--c-text-2);
}

.holiday-state.unpublished,
.holiday-state.none {
    background: #fdf1e3;
    color: #b45309;
}

/* 年级分析：班级 × 学科热力表 */
.heat-tb .heat-cell {
    text-align: center;
    font-weight: 600;
}

/* ============================================================
   顶栏的「当前班级」标签：居中显示
   ------------------------------------------------------------
   ★ 2026-09-25 改成**左右等宽**排布来居中，不再用绝对定位：
        [左块：☰ 菜单 + 标题 · flex:1 1 0] [班级胶囊] [右块：按钮 · flex:1 1 0]
     左右两块都 `flex:1 1 0`（等宽），中间的胶囊就精确落在顶栏中线上。
     绝对定位（`position:absolute; left:50%`）虽然也能居中，但它是**脱出文档流**的：
     标题或副标题一长、右侧按钮一多，胶囊就**压在上面** —— 看不清也点不着。
     ⚠️ 别再改回绝对定位，也别把「☰ 菜单」挪出 .topbar-left ——
        `.topbar-left` 与 `.topbar-actions` 那两处 `flex:1 1 0` 跟这里是**一套**，
        少一处、或多出一份，胶囊就会偏。
    ============================================================ */
.class-chip {
    flex: 0 0 auto;
}

/* 窄屏（≤1000px）：中线让不出来（标题 + 胶囊 + 按钮挤不下一行），
    退回「挨着标题排，排不下就折行」，并保持贴右，别在折行后孤零零落在左边。 */
@media (max-width: 1000px) {
    .topbar-left {
        flex: 1 1 auto;
    }

    .topbar-actions {
        flex: 0 1 auto;
    }

    .class-chip {
        margin-left: auto;
    }
}

/* ============================================================
   课表「本周统计」：每个学科一块，学科名与节数待在同一块里，
   不会和隔壁学科的进度条、数字挤在一起造成误读。
   ============================================================ */
.week-stat {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 14px;
}

.week-stat-item {
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    border-radius: 10px;
    padding: 10px 12px;
}

.week-stat-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 8px;
}

.week-stat-name {
    font-size: 14px;
    font-weight: 700;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.week-stat-count {
    margin-left: auto;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text-2);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.week-stat-bar {
    height: 9px;
    border-radius: 5px;
    background: var(--c-border);
    overflow: hidden;
}

.week-stat-bar > i {
    display: block;
    height: 100%;
    border-radius: 5px;
}

/* ============================================================
   值日排班：休息日与调休的视觉区分
   ============================================================ */
.duty-day.is-rest {
    background: #f6f8fb;
    border-radius: 10px;
    padding-left: 10px;
    padding-right: 10px;
}

.duty-day.is-holiday {
    background: #fef7ee;
}

.duty-day.is-makeup {
    background: #f3edff;
}

.duty-date .day-tag {
    margin-left: 4px;
    vertical-align: 1px;
}

/* 组内分工：人名 + 任务 */
.duty-task-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
}

.duty-task-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: 999px;
    font-size: 12.5px;
    line-height: 1.5;
}

.duty-task-chip .who {
    font-weight: 700;
    color: var(--c-text);
}

.duty-task-chip .what {
    color: var(--c-primary-d);
    font-weight: 600;
}

.duty-task-chip .what.none {
    color: var(--c-text-3);
    font-weight: 500;
}

/* ============================================================
   校历（学期 / 周次 / 寒暑假）
   ------------------------------------------------------------
   作业统计与校历页共用同一份日历：
     月视图 —— 7 列日期 + 左侧一列「第几周」
     年视图 —— 12 个小月历，假期与周末靠底色区分
   ============================================================ */
.sc-head {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.sc-head strong {
    font-size: 15px;
    min-width: 96px;
    text-align: center;
}

.sc-head .spacer {
    flex: 1;
}

/*
 * ★ 2026-10-04 晚（第二批）用户第 2 条：校历的导航要**居中**。
 *   改成三栏网格：[左：按月/按学期/按年][中：导航][右：说明或空]，
 *   导航精确落在卡片中线上。只有校历页用（它传 viewSwitch）；
 *   作业统计没传 viewSwitch，仍是上面那条左对齐 flex，不受影响。
 */
.sc-head-center {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 6px;
}

.sc-head-center .sc-left {
    justify-self: start;
    min-width: 0;
}

.sc-head-center .sc-mid {
    justify-self: center;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.sc-head-center .sc-right {
    justify-self: end;
    min-width: 0;
}

/*
 * ★★ 2026-10-04 晚（第三批）用户第 3 条：**窄屏必须折行**。
 *   三栏网格在电脑上很好看（导航精确落中线），但在手机上会出事：
 *   `1fr auto 1fr` 里的 `1fr` 等于 `minmax(auto, 1fr)` —— 两侧**缩不过各自内容的宽度**，
 *   一行塞不下时左右两栏就互相压上去。用户手机截图里「按学期」和「2026 年 10 月」
 *   糊成一团，就是这个原因。
 *   窄屏改成**三行居中**：① 按月 / 按学期 / 按年　② « ‹ 2026 年 10 月 › »　③ 说明（有才显示）。
 *   判据取 760px：分段控件（约 210px）+ 导航（约 210px）+ 两处间距，
 *   一行至少要 480px 才不挤；留足余量，平板竖屏也一起走这套。
 */
@media (max-width: 760px) {
    .sc-head-center {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        align-items: center;
        gap: 8px 6px;
    }

    .sc-head-center .sc-left,
    .sc-head-center .sc-mid,
    .sc-head-center .sc-right {
        flex: 1 1 100%;
        min-width: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 6px;
    }

    /* 空栏（月视图右边没有 picks）别占一整行，否则白多出 8px 空档 */
    .sc-head-center .sc-right:empty {
        display: none;
    }
}

/* 年 / 月 / 日 三个下拉 —— 校历里的年月日都要能直接选 */
.sc-picks {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.sc-picks label {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12.5px;
    color: var(--c-text-2);
}

.sc-picks select {
    padding: 4px 6px;
    font-size: 13px;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    background: #fff;
    color: var(--c-text);
}

.sc-view {
    margin-left: 4px;
}

.cal-week.with-wk,
.sc-grid {
    display: grid;
    /* ★ 同上：7 列一律 minmax(0, 1fr)，否则窄屏上会被内容撑破 */
    grid-template-columns: 58px repeat(7, minmax(0, 1fr));
    gap: 4px;
}

.cal-week.noweek,
.sc-grid.noweek {
    grid-template-columns: repeat(7, minmax(0, 1fr));
}

.sc-grid {
    margin-bottom: 2px;
}

/* 每行最左边的「第几周」 */
.sc-wk {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    border-radius: 9px;
    font-size: 12px;
    font-weight: 600;
    color: var(--c-text-3);
    min-height: 46px;
    padding: 2px;
    text-align: center;
    line-height: 1.25;
}

/* 作业页左栏的日历是「看一眼就点」的辅助区，比校历页那一套再收小一号 */
.hw-left .sc-wk {
    min-height: 32px;
    font-size: 12px;
    padding: 0 2px;
}

.hw-left .sc-grid .cal-cell {
    min-height: 32px;
}

/* 学期里的上课日：白底、描边清楚；假期：淡青底 */
.cal-cell.in-term:not(.selected):not(.is-holiday):not(.is-weekend):not(.is-makeup) {
    background: #fff;
    border-color: #dbe4f0;
}

.cal-cell.is-vacation:not(.selected) {
    background: #e7f6f3;
    border-color: #a7ded3;
}

.cal-cell.is-vacation:not(.selected) .d {
    color: #0f766e;
}

/* 「假」角标：寒暑假 / 春假 / 秋假 */
.day-tag.vac {
    background: #0f766e;
    color: #fff;
    border: 1px solid #0b5d57;
}

.sc-legend .lg.sc-term {
    background: #fff;
    border: 1.5px solid #dbe4f0;
}

.sc-legend .lg.sc-vac {
    background: #e7f6f3;
    border: 1.5px solid #a7ded3;
}

/* ---------- 年视图 ---------- */
.sc-year {
    display: grid;
    /* ★ 2026-10-04 用户报「按年在手机上变形（月份卡被截断）」：
       min(212px,100%) 保证列宽永远不超过容器宽；配合下面的 min-width:0
       把「1fr 最小 = 内容宽」那条 grid 默认规则拆掉 —— 手机上再窄也只收缩、不外溢。 */
    grid-template-columns: repeat(auto-fill, minmax(min(212px, 100%), 1fr));
    gap: 12px;
}

.sc-year > .sc-month {
    min-width: 0;
}

.sc-month {
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 8px;
}

.sc-m-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 6px;
    margin-bottom: 5px;
}

.sc-m-head b {
    font-size: 13px;
}

.sc-m-head span {
    font-size: 12px;
    color: var(--c-text-3);
}

/* ★★ 2026-10-04（真机实测抓到的病根）：这里原来是 repeat(7, 1fr) ——
   `1fr` 的最小值是 `auto`，也就是**内容的最小宽度**（一个格子里是两位数字 + 角标）。
   7 列一叠加，网格的最小宽度就比月份卡还宽，于是「卡里的内容撑破卡」→
   整页文档被撑到 446px（手机视觉视口只有 393px）→ 安卓浏览器把布局视口一起放大、
   整页缩小约 12%，看起来就是「按年一团乱、右边被切」。
   ★ 修法：`minmax(0, 1fr)` —— 最小宽度写死 0，网格才会跟着卡片一起收缩。
   ⚠️ 凡是 `repeat(N, 1fr)` 的格子都可能踩这个坑（grid blowout），别改回去。 */
.sc-m-week,
.sc-m-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 2px;
    min-width: 0;
}

.sc-m-week span {
    text-align: center;
    font-size: 11px;
    color: var(--c-text-3);
    font-weight: 600;
}

.sc-mn {
    position: relative;
    border: 1px solid transparent;
    background: #fff;
    border-radius: 5px;
    min-height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    color: var(--c-text-2);
    cursor: pointer;
    padding: 0;
    font-variant-numeric: tabular-nums;
}

.sc-mn.blank {
    background: transparent;
    pointer-events: none;
}

.sc-mn.st-class {
    background: #fff;
    border-color: #e2e8f2;
}

.sc-mn.st-vacation {
    background: #dff3ef;
    border-color: #a7ded3;
    color: #0f766e;
}

.sc-mn.st-weekend,
.sc-mn.st-holiday {
    background: #fbeadb;
    border-color: #f0cfa4;
    color: #b45309;
}

/* 调休上课：与月视图 / 学期视图同一套紫底斜纹（年视图格子小，底纹稍密）
   ★ 2026-10-05 晚：跟着月视图一起换成**苹果紫 + 实线**（原来是虚线）。 */
.sc-mn.st-makeup {
    background:
        repeating-linear-gradient(45deg, rgba(175, 82, 222, .18) 0 2px, rgba(175, 82, 222, 0) 2px 6px),
        var(--c-makeup-l);
    border-color: var(--c-makeup);
    color: var(--c-makeup-d);
    font-weight: 800;
}

.sc-mn.st-off-term {
    background: var(--c-surface-2);
    border-color: #e6eaf1;
    color: var(--c-text-3);
}

.sc-mn.has-hw {
    font-weight: 800;
    color: var(--c-primary-d);
    box-shadow: inset 0 -3px 0 var(--c-primary);
}

.sc-mn.today {
    /* 年视图的小月历格子太小（十几 px），只描一圈今天的专用色就够醒目了；
       2026-09-25 晚跟着把描边收细（原来是 2px 内阴影，在小格上显得很重） */
    border-color: var(--c-today);
    border-width: 1.5px;
    box-shadow: none;
    color: var(--c-today);
    font-weight: 700;
}

.sc-mn.selected {
    background: var(--c-primary);
    border-color: var(--c-primary-d);
    color: #fff;
}

/* ---------- 当日详情 + 学期一览 ---------- */
.sc-detail-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
    gap: 14px;
    align-items: start;
}

/* ---------- 校历设置弹窗 ---------- */
.sc-tip {
    font-size: 13px;
    color: var(--c-text-2);
    line-height: 1.7;
    margin-bottom: 12px;
}

.sc-year-block {
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 12px;
    margin-bottom: 12px;
    background: var(--c-surface-2);
}

.sc-yb-head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.sc-yb-head b {
    font-size: 14.5px;
}

.sc-yb-head .spacer {
    flex: 1;
}

.sc-yb-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 10px;
}

.sc-f input[type="date"] {
    width: 100%;
}

.sc-f-hint {
    display: block;
    margin-top: 3px;
    font-size: 12px;
    color: var(--c-text-3);
}

.sc-add {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding-top: 4px;
    font-size: 13px;
    color: var(--c-text-2);
}

@media (max-width: 900px) {
    .cal-week.with-wk,
    .sc-grid {
        grid-template-columns: 42px repeat(7, minmax(0, 1fr));
        gap: 3px;
    }

    .sc-wk {
        font-size: 11.5px;
        min-height: 40px;
    }
}

@media (max-width: 560px) {
    .sc-head strong {
        min-width: 0;
    }

    .sc-year {
        grid-template-columns: repeat(auto-fill, minmax(min(136px, 100%), 1fr));
        gap: 8px;
    }

    .sc-mn {
        min-height: 20px;
        font-size: 11.5px;
    }
}

/* ============================================================
   校历图例：五种状态用「底色 + 底纹 + 角标」三重区分
   ------------------------------------------------------------
   只靠颜色深浅容易看混（尤其投到教室大屏上），所以每种状态
   除了颜色不一样，底纹和形状也不一样，图例里直接带上角标文字。
   ============================================================ */
.sc-legend {
    gap: 16px;
    row-gap: 8px;
    font-size: 12.5px;
}

.sc-legend .lg {
    width: 22px;
    height: 18px;
    border-radius: 5px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-style: normal;
    flex-shrink: 0;
}

.sc-legend .lg b {
    font-size: 11px;
    font-weight: 800;
    line-height: 1;
}

/* ① 学期上课日：白底 + 实线灰蓝描边，没有角标 */
.sc-legend .lg.sc-term {
    background: #fff;
    border: 2px solid #8ea0b8;
}

/* ② 寒暑假 / 春秋假：青绿底 + 斜纹，角标「休」 */
.sc-legend .lg.sc-vac {
    background:
        repeating-linear-gradient(45deg, rgba(15, 118, 110, .30) 0 3px, rgba(15, 118, 110, 0) 3px 6px),
        #dff3ef;
    border: 2px solid #0f766e;
    color: #0b5d57;
}

/* ③ 周末 / 法定节假日：暖橙底 + 圆点，角标「休」 */
.sc-legend .lg.sc-rest {
    background:
        radial-gradient(rgba(180, 83, 9, .40) 1px, transparent 1.1px) 0 0 / 5px 5px,
        #fde8d3;
    border: 2px solid #e08b3c;
    color: #9a4a06;
}

/* ④ 调休上课：紫底斜纹 + 实线紫边，角标「班」（与日格同一套值）
   ⚠️ 同上面那段：`.lg` 是**旧版图例**的遗留类，HTML 里已经没有了。 */
.sc-legend .lg.sc-makeup {
    background:
        repeating-linear-gradient(45deg, rgba(175, 82, 222, .15) 0 3px, rgba(175, 82, 222, 0) 3px 8px),
        var(--c-makeup-l);
    border: 2px solid var(--c-makeup);
    color: var(--c-makeup-d);
}

/* ⑤ 今天：深蓝圆形 —— 形状本身就和其他四个不一样 */
.sc-legend .lg.sc-today {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--c-primary);
    border: 2px solid #fff;
    box-shadow: 0 0 0 2px var(--c-primary);
}

.sc-legend .lg.has-hw,
.sc-legend .lg.no-hw {
    width: 18px;
    height: 18px;
}

/* 日格跟着图例走：假期整段斜纹（不管是不是工作日都放假） */
.cal-cell.is-vacation:not(.selected) {
    background:
        repeating-linear-gradient(45deg, rgba(15, 118, 110, .12) 0 4px, rgba(15, 118, 110, 0) 4px 9px),
        #dff3ef;
    border-color: #7ecfc2;
}

/* 周末：冷静的灰蓝 + 圆点 */
.cal-cell.is-weekend:not(.selected) {
    background:
        radial-gradient(rgba(100, 116, 139, .26) 1px, transparent 1.1px) 0 0 / 6px 6px,
        #eceff5;
    border-color: #cbd5e1;
}

/* 法定节假日：暖橙 + 圆点 */
.cal-cell.is-holiday:not(.selected) {
    background:
        radial-gradient(rgba(180, 83, 9, .26) 1px, transparent 1.1px) 0 0 / 6px 6px,
        #fdf1e3;
    border-color: #e8b878;
}

/* 假期里的「休」角标（比法定节假日的更深一点，两者仍能分开） */
.cal-cell .cal-tag .day-tag.vac {
    background: #0f766e;
    color: #fff;
    border-color: #0b5d57;
}

/* ============================================================
   学期视图：整学期摊成一张纸一样的表格
     月份 | 周次 | 周一 … 周日
   ============================================================ */
.sc-term-head {
    margin-bottom: 10px;
}

.sc-term-title {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.sc-term-title b {
    font-size: 16px;
}

.sc-term-badge {
    background: var(--c-primary);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
    padding: 3px 9px;
    border-radius: 7px;
}

.sc-term-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    margin-top: 7px;
    font-size: 12.5px;
    color: var(--c-text-2);
}

.sc-term-wrap {
    overflow-x: auto;
    border-radius: var(--radius);
}

.sc-table {
    border-collapse: collapse;
    width: 100%;
    min-width: 640px;
    font-size: 12.5px;
    background: #fff;
}

.sc-table th,
.sc-table td {
    border: 1px solid #dfe5ee;
    text-align: center;
    padding: 0;
    height: 30px;
}

.sc-table thead th {
    background: #eef2f8;
    font-weight: 700;
    color: var(--c-text-2);
    height: 34px;
    font-size: 12.5px;
}

.sc-th-month { width: 64px; }
.sc-th-wk { width: 46px; }

.sc-th-day.wk-end {
    color: #b45309;
}

.sc-td-month {
    font-weight: 800;
    font-size: 13px;
    color: #3d4655;
    width: 64px;
}

.sc-td-wk {
    background: #f7f9fc;
    font-weight: 700;
    color: var(--c-text-2);
    width: 46px;
    font-size: 13px;
}

.sc-td-day {
    position: relative;
    min-width: 42px;
    font-variant-numeric: tabular-nums;
}

.sc-td-day .dn {
    font-weight: 600;
}

.sc-td-day .cal-tag {
    position: static;
    margin-left: 2px;
}

.sc-td-day .cal-tag .day-tag {
    height: 12px;
    min-width: 12px;
    padding: 0 2px;
    font-size: 11px;
}

.sc-td-day[data-date] {
    cursor: pointer;
}

.sc-td-day[data-date]:hover {
    background: var(--c-primary-l);
}

.sc-td-day.st-class { background: #fff; color: var(--c-text); }
.sc-td-day.wk-end { background: #f4f6fa; color: #96a0b2; }

.sc-td-day.st-weekend {
    background: #eceff5;
    color: #6b7688;
}

.sc-td-day.st-vacation {
    background:
        repeating-linear-gradient(45deg, rgba(15, 118, 110, .14) 0 4px, rgba(15, 118, 110, 0) 4px 9px),
        #dff3ef;
    color: #0b5d57;
    font-weight: 700;
}

.sc-td-day.st-holiday {
    background: #fde8d3;
    color: #9a4a06;
    font-weight: 700;
}

.sc-td-day.st-makeup {
    background:
        repeating-linear-gradient(45deg, rgba(175, 82, 222, .15) 0 3px, rgba(175, 82, 222, 0) 3px 8px),
        var(--c-makeup-l);
    color: var(--c-makeup-d);
    font-weight: 800;
    /* 表格单元格用 outline 画边：不占布局，也不会和「今天 / 选中」的 box-shadow 打架。
       ★ 2026-10-05 晚：虚线 → **实线** + 苹果紫（跟月视图的调休格统一）。 */
    outline: 1.5px solid var(--c-makeup);
    outline-offset: -2px;
}

.sc-td-day.st-off-term,
.sc-td-day.out {
    background: #f6f7fa;
    color: #c3cad6;
}

.sc-td-day.is-today {
    box-shadow: inset 0 0 0 2px var(--c-warn);
}

.sc-td-day.is-sel {
    box-shadow: inset 0 0 0 2px var(--c-primary);
}

/* 学期视图表头上「哪个学期」的下拉 */
.sc-term-pick {
    /* 学期名很长（学年 + 学期 + 起止日期），要允许它收缩，
       否则会把 « » 与「按月 / 按学期 / 按年」挤到下一行去 */
    flex: 1 1 220px;
    min-width: 170px;
    max-width: 520px;
    padding: 5px 8px;
    font-size: 13px;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    background: #fff;
    color: var(--c-text);
}

/* 校历设置弹窗：暑假放假后面换行，第二行放秋假 / 春假 */
.sc-yb-grid-2 {
    margin-top: 10px;
}

/* ============================================================
   年级 / 入学年份对照（数据与设置）
   ============================================================ */
.gm-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
    gap: 10px;
}

.gm-cell {
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 10px 10px 11px;
    text-align: center;
}

.gm-grade {
    font-size: 13px;
    font-weight: 700;
    color: var(--c-text-2);
    margin-bottom: 6px;
}

.gm-cell select {
    width: 100%;
}

/* ============================================================
   课表页：按年级对照自动对应班级时的说明条
   ============================================================ */
.tt-map-hint {
    margin-top: 10px;
    padding: 9px 13px;
    font-size: 12.5px;
    line-height: 1.7;
    background: #f0f6ff;
    border: 1px solid #cfe0fb;
    border-radius: 10px;
    color: var(--c-text-2);
}

@media (max-width: 640px) {
    .sc-legend {
        gap: 10px;
    }

    .sc-term-pick {
        min-width: 180px;
        flex: 1;
    }

    .sc-term-title b {
        font-size: 14.5px;
    }
}

/* ============================================================
   校历日格配色（权威版 · 放在文件最后，覆盖前面的旧配色）
   ------------------------------------------------------------
   五种状态，底色 + 底纹 + 角标三重区分，光靠颜色深浅在教室
   大屏上根本分不出来：

     学期上课日       白底 + 深灰实线边
     寒暑假 / 春秋假   青绿底 + 45° 斜纹 + 「休」
     周末 / 法定节假日  暖橙底 + 圆点纹 + 「休」
     调休上课         紫底 + 紫色虚线边 + 「班」（原来用蓝色，
                      和「有作业」的蓝底、「今天」的蓝撞在一起）
     今天           橙色圆框圈出

   周末与法定节假日合并成同一种样子 —— 对老师来说都是「今天
   不用上学」，分开反而要多记一套色；节日的名字依旧在悬浮提示
   与下方「这天」卡片里写清楚。
   ============================================================ */

/* ① 学期上课日 */
.cal-cell.in-term:not(.selected):not(.is-holiday):not(.is-weekend):not(.is-makeup):not(.has-hw) {
    background: #fff;
    border-color: #b6c4d8;
}

.cal-cell.in-term:not(.selected):not(.has-hw) .d {
    color: #33415c;
}

/* ② 寒暑假 / 春秋假：整段斜纹，不管那天是不是工作日都放假 */
.cal-cell.is-vacation:not(.selected) {
    background:
        repeating-linear-gradient(45deg, rgba(13, 148, 136, .22) 0 4px, rgba(13, 148, 136, 0) 4px 9px),
        #d2f0e9;
    border-color: #3aa896;
}

.cal-cell.is-vacation:not(.selected) .d {
    color: #0b5d57;
    font-weight: 800;
}

.day-tag.vac {
    background: #0f766e;
    color: #fff;
    border: 1px solid #0b5d57;
}

/* ③ 周末 / 法定节假日：暖橙底 + 圆点纹 */
.cal-cell.is-weekend:not(.selected),
.cal-cell.is-holiday:not(.selected) {
    background:
        radial-gradient(rgba(194, 65, 12, .32) 1px, transparent 1.1px) 0 0 / 6px 6px,
        #ffe2c8;
    border-color: #eaa25e;
}

.cal-cell.is-weekend:not(.selected) .d,
.cal-cell.is-holiday:not(.selected) .d {
    color: #9a4a06;
}

/*
 * ④ 调休上课：苹果紫底 + 45° 紫色斜纹 + **实线**紫边 +「班」角标。
 *
 * 这一档颜色调过三次：
 *   · 一开始是淡紫底 + 虚线边（#ebe4fc）—— 在学期表 / 年视图的浅色格子里
 *     跟白底几乎分不出来；
 *   · 后来改成饱和实心紫（#6d28d9）—— 月视图里又太重，整块压过去很刺眼；
 *   · 再后来是中间档 #d6c7fb + **紫虚线边**（tailwind violet 那一套）—— 用户 2026-10-05
 *     晚看图说「边框虚线改实线、颜色换掉，要苹果色调、要有区分度，不要用灰」。
 * 现在 = 苹果 systemPurple（`--c-makeup` #AF52DE）+ **实线**，
 * 三种视图（按月 / 按学期 / 按年）与图例共用同一组值，改一处处处跟着变。
 *
 * ⚠️ 边框**只能用 border-color 指定颜色**，别写 `border:` 简写 —— 简写会把
 *    border-style 一起接管，而颜色又会被别人按顺序盖掉，就成了「调休的虚线形状 +
 *    别人的颜色」（详见下面那段注释）。
 */
.cal-cell.is-makeup:not(.selected) {
    background:
        repeating-linear-gradient(45deg, rgba(175, 82, 222, .15) 0 3px, rgba(175, 82, 222, 0) 3px 8px),
        var(--c-makeup-l);
    /*
     * ★★ 2026-10-05 晚 用户要求：**边框虚线改实线、颜色换掉**。
     *
     * ⚠️ 这里**只能写 border-color，绝不能写 `border:` 简写** —— 那正是「又乱又丑」的根因：
     *   原来写的是 `border: 1.5px dashed #7c3aed`（简写），它把 border-style 一并接管成
     *   dashed；而**颜色**会被别人按顺序盖掉：
     *     · 「调休 + 有作业」→ 文件更后面那段 `.cal-cell.has-hw:not(.selected)`
     *       把 border-color 改成主色蓝 → 于是成了**蓝虚线 + 浅蓝底 + 紫角标**（用户截图里那个「20」）；
     *     · 「调休 + 周末」→ 橙色那组把颜色改成暖橙 → 成了**橙虚线**。
     *   结果就是「调休的虚线形状 + 别人的颜色」，两个信号互相打架。
     *
     * 实线由基础规则 `.cal-cell { border: 1.5px solid transparent }` 给；
     * 这里只指定**颜色**。并且 `has-hw` 那条已经补了 `:not(.is-makeup)`（见 7923 行附近），
     * 保证调休的紫边不会再被其它状态顶掉。
     */
    border-color: var(--c-makeup);
}

.cal-cell.is-makeup:not(.selected) .d {
    color: var(--c-makeup-d);
    font-weight: 800;
}

/* 角标：白底紫字，在紫底格子上最清楚 */
.day-tag.work {
    background: #fff;
    color: var(--c-makeup-d);
    border: 1px solid var(--c-makeup);
    font-weight: 900;
}

/* ⑤ 今天：柔和砖橙细描边 + 淡外发光（和文件末尾那段、以及图例里的样例完全一致） */
.cal-cell:not(.selected).today {
    border-color: var(--c-today);
    border-width: 2px;
    box-shadow: 0 0 0 2.5px rgba(196, 71, 26, .16);
}

.cal-cell:not(.selected).today .d {
    color: var(--c-today);
    font-weight: 700;
}

/* ============================================================
   校历图例（权威版）
   ------------------------------------------------------------
   每一项 = 一个「真的日格」样例 + 名称 + 一句说明。
   样例用的就是日历里那套 class，所见即所得。
   ============================================================ */
.sc-legend {
    display: grid;
    /* ★ 2026-10-04 用户要求：图例「完全缩小」—— min(160px,100%) 保证最窄屏也不撑破 */
    grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
    gap: 6px 10px;
    padding: 8px 10px;
    border-radius: 12px;
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    margin-top: 12px;
    font-size: 12.5px;
}

.sc-legend .sc-lg {
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
}

.sc-legend .sc-lg-demo {
    /*
     * ★★ 2026-10-05 晚 用户报「这个图例，电脑端已经变形了」—— 根因就在这几个数上：
     *
     *   样例原本是 26×26，里面同时要装 10.5px 的数字（居中）和 9px 高的「休 / 班」角标。
     *   真机实测：数字占 y 7.5~18.5、角标占 y 16~25 → **重叠 2.5px**，
     *   于是「寒暑假 / 周末 / 调休」三个带角标的样例里，字和角标糊成一团
     *   （另两项「学期上课日 / 今天」没有角标，所以看着正常 —— 这也是它只在部分项上露馅的原因）。
     *
     * 修法与真格子**同一套**：格子放大 + 数字往上让。
     *   32×32、padding 上 1 下 7 → 数字上移 3px；数字占 y 8~19、角标占 y 21~31，间隙 2px 起步。
     *   `justify-content: center` 居中的是**内容盒**（不是整格），所以上下 padding 不等 = 整体上移。
     *
     * ⚠️ 别再把它调回 26px：那点高度装不下「数字 + 角标」两层，一定会再糊。
     * ⚠️ 想让样例跟真格子更接近「略高」的比例，就同时改宽和高，别只改一个。
     */
    flex: 0 0 32px;
    width: 32px;
    min-height: 32px;
    padding: 1px 1px 7px;
    gap: 0;
    justify-content: center;
    border-radius: 7px;
    cursor: default;
    pointer-events: none;
}

.sc-legend .sc-lg-demo .d {
    font-size: 11.5px;
    line-height: 1.05;
}

.sc-legend .sc-lg-demo .cal-tag {
    top: auto;
    bottom: 1px;
    right: 1px;
}

.sc-legend .sc-lg-demo .cal-tag .day-tag {
    /* 样例格子放大到 32px 之后，「休 / 班」角标也跟着回到 10px（跟真格子同一档） */
    height: 10px;
    min-width: 10px;
    padding: 0 1.5px;
    font-size: 9.5px;
}

.sc-legend .sc-lg-demo .dot {
    min-width: 12px;
    height: 10px;
    padding: 0 3px;
    margin-top: 1px;
    font-size: 9.5px;
}

.sc-legend .sc-lg-demo .nodot {
    width: 12px;
    height: 2px;
    margin-top: 2px;
}

.sc-legend .sc-lg-txt {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    min-width: 0;
}

.sc-legend .sc-lg-txt b {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--c-text);
    line-height: 1.25;
}

/* ★ 2026-10-04 用户要求：说明文字整行去掉 —— 图例只留「色块 + 名称」，
   两端一致（电脑上也不显示），要语义看这一条注释就够了。 */
.sc-legend .sc-lg-txt i {
    display: none;
}

@media (max-width: 900px) {
    .sc-legend {
        grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
    }
}

/* ============================================================
   作业统计左栏的日历
   ------------------------------------------------------------
   这一栏是「看一眼、点一下」用的：
     · 不要周次列（那是校历页才需要的信息）
     · 不要整块图例与节假日来源行（占地方）
     · 日期数字放大、格子加高 —— 手指点得准
   「今天」按钮挪到卡片标题后面（标题行里），日历上不再重复一个。
   ============================================================ */
.hw-left .sc-lg-demo,
.hw-left .sc-legend {
    display: none;
}

.hw-left .card-head {
    display: flex;
    align-items: center;
    gap: 8px;
}

.hw-left .card-head h3 {
    flex: 0 0 auto;
}

.hw-left .card-head .hw-today {
    margin-left: auto;
    padding: 3px 10px;
    font-size: 12px;
    min-height: 26px;
}

/* 导航要在一行里放下 « ‹ 2026 年 9 月 › » —— 这些按钮跟日期格子不一样，
   收小一号没关系（真正要点准的是下面的日期） */
.hw-left .sc-head {
    justify-content: space-between;
    gap: 3px;
    flex-wrap: nowrap;
    margin-bottom: 8px;
}

.hw-left .sc-head strong {
    font-size: 14px;
    line-height: 1.3;
    min-width: 0;
    flex: 1 1 auto;
    white-space: nowrap;
    text-align: center;
}

.hw-left .sc-head .icon-btn {
    width: 26px;
    min-width: 26px;
    height: 26px;
    font-size: 13px;
}

.hw-left .cal-week span {
    font-size: 12px;
    font-weight: 600;
}

/*
 * 用户要的是「日期放大、方便点」。
 *
 * 之前左栏 250px：每格只有 26px 宽，却把高度写到 44px —— 又窄又高的长条，
 * 数字挤在里头既不好看也不好点（面积 26×44，但横向只有 26px，手指很容易点偏）。
 * 这里把左栏放到 288px，7 列每格约 35px 见方：
 *   · 面积比原来更大（35×35 > 26×44），横向也够宽，点得准；
 *   · 右侧姓名卡从一行 8 个变成 7 个，但**单卡反而更宽**
 *     （约 146px，原来 124px）—— 姓名更大，站教室大屏前更好认。
 *
 * 只在宽屏生效：窄屏那份「折叠成单列」的断点写在前面，不能在这儿被盖掉。
 */
@media (min-width: 901px) {
    .hw-layout {
        grid-template-columns: 288px minmax(0, 1fr);
    }
}

.hw-left {
    padding: 14px 12px;
}

/* 格子：宽度由 7 等分决定，高度**尽量贴近正方形** */
.hw-left .sc-grid .cal-cell {
    min-height: 0;
    /*
     * ★★ 2026-10-05（晚·第二批）用户要求：「将上端和下端都压缩一下，但是不要让内容重叠，
     *   越接近正方形越好」。
     *
     * 上一版取 1 / 1.3（左栏 288px、7 列 → 格子约 34px 宽 → **约 44px 高**）——
     * 那是为了「三样东西排得开」而留的余量，但用户看图后觉得**太瘦高**。
     *
     * 现在压到 **1 / 1.06**（34px 宽 → **约 36px 高**），几乎就是正方形。
     * 高度一共压掉 8px，靠三件事同时腾地方（缺一条就会撞）：
     *
     *   ① 数字往上挪（见下面 padding：上移量 3px，原来是 2px）；
     *   ② 两个角标各收一档（11 → 10px，跟手机那档一致，见 .dot / .cal-tag .day-tag）；
     *   ③ 格子本身矮下来。
     *
     * 真浏览器实测（电脑 1440，31 格）：数字「底边」到角标「顶边」还剩 **3.1px**
     * （压到 1 / 1.06 但只让 1px 时只剩 0.6px —— 太贴了，等于没余量），三者两两不重叠。
     *   ⚠️ 那 3px 余量是**故意留的**：数字的「中心」位置跟行高无关（flex 居中按内容盒算），
     *      但「底边」会随行高浮动 —— 老师那边是 **Windows（微软雅黑）**，行高跟苹方不一样，
     *      差个 1~2px 就可能贴上。别再把这 3px 吃回去。
     *   ⚠️ 也别再改回 1 / 1.3：那是用户明确说过「要压缩」的那一版。
     */
    aspect-ratio: 1 / 1.06;
    border-radius: 9px;
    /*
     * ★★ 2026-10-05 用户要求：数字**水平居中**、垂直**略微偏上**（不是正中）。
     *
     * 为什么不做「正中」：格子下方左右各有一个角标（左下＝作业数量、右下＝休 / 班），
     * 数字压在正中时离下边太近，一眼看着就是「挤在一起」。上移一点点就分得开。
     *
     * 实现：`justify-content: center` 居中的是**内容盒**，而不是整个格子 ——
     * 上下 padding 不等就能把内容整体挪上去：上移量 = (padding-top − padding-bottom) ÷ 2
     * = (2 − 12) ÷ 2 = **上移 5px**。
     *   （上一版是「上 2 / 下 8」= 上移 3px。这一批把格子压矮了 8px，必须再多让 2px，
     *     否则数字底边离角标只剩 0.6px。）
     *
     * ⚠️ 左右 padding 必须相等（1px），否则就不是水平居中了。
     * ⚠️ 别改回 flex-start：那是「贴着顶边」，不是「略微偏上」。
     * ⚠️ 手机上另有一档（见 ≤900px 那条）：那边格子本来就接近正方形，不需要让位。
     */
    justify-content: center;
    padding: 2px 1px 12px;
}

.hw-left .sc-grid .cal-cell .d {
    font-size: 15px;
    line-height: 1.05;
}

/*
 * 角标挪到**右下角**（原来在右上角，正好压住日期数字）。
 * 格子只有 35px 见方，数字在上面、角标在下面，各占一半，互不遮挡。
 */
.hw-left .sc-grid .cal-cell .cal-tag {
    top: auto;
    bottom: 1px;
    right: 1px;
}

.hw-left .sc-grid .cal-cell .cal-tag .day-tag {
    /*
     * ★ 2026-10-05：宽屏上格子只有约 34px，左下角的「几项作业」和右下角的「休 / 班」
     *   要并排站 —— 原来 12px 高 / 11px 字，两个加起来 30px 出头，实测**差 0.5px 就贴上**
     *   （真浏览器量出来的）。收一档之后两个角标之间留出约 6px。
     *
     * ★ 2026-10-05 晚（第二批）：再收一档 **11 → 10px**，跟手机那档（≤900px 的 .dot）
     *   统一。原因是用户要求把格子压成接近正方形 —— 格子矮了 8px，角标占的高度就得
     *   一起让出来一点，否则数字会贴到角标上（见 .cal-cell 那段注释）。
     *   ⚠️ 别再放大回去；要放大就先把左栏宽度一起加（那是 4 个页面共用的布局，动之前先问）。
     */
    height: 10px;
    min-width: 10px;
    padding: 0 1px;
    font-size: 9.5px;
}

/*
 * 作业数量角标（蓝色小圆点）在这一栏不显示：
 * 底色已经区分了「有作业（蓝）/ 无作业（灰）」，35px 的格子再塞一个数字
 * 只会把日期挤掉。要看几项作业，点一下日期右侧就列出来了。
 */
.hw-left .sc-grid .cal-cell .dot,
.hw-left .sc-grid .cal-cell .nodot {
    display: none;
}

/* 窄屏（手机 / 平板）：左栏不再是固定宽度，按比例会变得很大，退回固定高度 */
@media (max-width: 900px) {
    .hw-left .sc-grid .cal-cell {
        aspect-ratio: auto;
        /* ★ 2026-10-05 用户要求「单个格子可以适当放大」：40 → 44px，
           三样东西（日期 / 左下角作业数 / 右下角休·班）才排得开。 */
        min-height: 44px;
        /*
         * ★ 2026-10-05 晚（第二批）：手机上**把上移量收回来**（12 → 6 = 上移 2px）。
         *
         * 为什么两端不一样：宽屏左栏固定 288px、7 列 → 格子只有约 34px 宽，是**竖长**的，
         * 必须把数字往上让 5px 才塞得下三样；而手机左栏占满整屏 → 格子约 **46×44**，
         * 本来就比正方形还扁一点点，**根本不需要让位**（数字到角标还有 7px 以上）。
         * 硬套 5px 只会让手机上的数字平白往上窜一截，看着别扭。
         *
         * ⚠️ 这一行只改「数字上移多少」，别顺手删掉 —— 删了手机会跟着变成上移 5px。
         */
        padding: 2px 1px 6px;
    }

    .hw-left .sc-grid .cal-cell .d {
        font-size: 14px;
    }
}

/* 屏幕很窄时（校历页的月视图也一样）角标一律靠右下，避免压住日期 */
@media (max-width: 560px) {
    .cal-cell .cal-tag {
        top: auto;
        bottom: 1px;
        right: 1px;
    }
}

/* ============================================================
   学生信息 · 「🏫 班级」卡片
   ------------------------------------------------------------
   班级是这一页的第一等公民（先建班、再导学生），所以给它
   一张独立的卡片：上面一行新建，下面把班级排成一张张卡片。
   ============================================================ */
.cls-new {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    flex-wrap: wrap;
    padding-bottom: 14px;
    margin-bottom: 14px;
    border-bottom: 1px dashed var(--c-border);
}

.cls-new .field {
    margin: 0;
}

.cls-new-hint {
    font-size: 12.5px;
    color: var(--c-text-3);
    padding-bottom: 9px;
}

.cls-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(186px, 1fr));
    gap: 10px;
}

.cls-empty {
    grid-column: 1 / -1;
    padding: 16px;
    border: 1px dashed var(--c-border);
    border-radius: 10px;
    background: var(--c-surface-2);
    font-size: 13px;
    line-height: 1.8;
    color: var(--c-text-3);
}

.cls-card {
    border: 1.5px solid var(--c-border);
    border-radius: 11px;
    background: #fff;
    padding: 10px 12px;
    cursor: pointer;
    transition: border-color .15s, box-shadow .15s, background .15s;
}

.cls-card:hover {
    border-color: #a9bfe4;
    box-shadow: 0 2px 10px rgba(29, 95, 216, .10);
}

.cls-card.on {
    border-color: var(--c-primary);
    background: var(--c-primary-l);
}

.cls-card-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.cls-card-top b {
    font-size: 14px;
    color: var(--c-text);
}

.cls-n {
    font-size: 12px;
    font-weight: 700;
    color: var(--c-primary-d);
    white-space: nowrap;
}

.cls-n.zero {
    color: var(--c-text-3);
    font-weight: 500;
}

.cls-card-bot {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: 8px;
    min-height: 26px;
}

.cls-tag {
    font-size: 12px;
    line-height: 1.6;
    padding: 1px 7px;
    border-radius: 6px;
    background: var(--c-surface-2);
    color: var(--c-text-2);
    border: 1px solid var(--c-border);
}

.cls-tag.src {
    background: #eef4ff;
    border-color: #cfe0fb;
    color: var(--c-primary-d);
}

.cls-lock {
    font-size: 12px;
    color: var(--c-text-3);
    margin-left: auto;
}

.cls-del {
    margin-left: auto;
    padding: 2px 10px;
    font-size: 12px;
    min-height: 24px;
}

/* ============================================================
   数据与设置 · 分模块备份 / 科目 / 班级名单
   ============================================================ */

/* 勾选模块的列表：一行一个模块，右边小字写它对应哪几张表 */
.mod-pick {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 46vh;
    overflow: auto;
}

.mod-pick-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border: 1px solid var(--c-border);
    border-radius: 9px;
    background: var(--c-surface-2);
    cursor: pointer;
}

.mod-pick-row:hover {
    border-color: #a9bfe4;
    background: #fff;
}

.mod-pick-row b {
    font-size: 13px;
    font-weight: 600;
    min-width: 96px;
}

.mod-pick-row span {
    font-size: 12px;
    color: var(--c-text-3);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* 科目小标签 */
.subj-list {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.subj-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 999px;
    background: #eef4ff;
    border: 1px solid #cfe0fb;
    color: var(--c-primary-d);
    font-size: 12.5px;
    font-weight: 600;
}

.subj-del {
    border: none;
    background: transparent;
    color: var(--c-text-3);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    padding: 0 2px;
}

.subj-del:hover {
    color: var(--c-danger);
}

/* 年级查不出来的班（入学年份不在对照范围）：整行淡掉，提醒老师 */
.cls-unknown td {
    color: var(--c-text-3);
}

.cls-unknown td b {
    color: var(--c-text-2);
    font-weight: 600;
}

/* 导入预览里「这批学生会被跳过」的说明 */
.imp-cls-row .skip {
    color: var(--c-text-3);
}

/* ============================================================
   学生信息 · 「📥 导入学生」弹窗
   ============================================================ */
.imp-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 26px 18px;
    border: 2px dashed #c3d3ea;
    border-radius: 12px;
    background: var(--c-surface-2);
    text-align: center;
    cursor: pointer;
    transition: border-color .15s, background .15s;
}

.imp-drop:hover,
.imp-drop.over {
    border-color: var(--c-primary);
    background: var(--c-primary-l);
}

.imp-drop .big {
    font-size: 30px;
    line-height: 1;
}

.imp-drop b {
    font-size: 14px;
    color: var(--c-text);
}

.imp-drop i {
    font-style: normal;
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--c-text-3);
    max-width: 520px;
}

.imp-preview {
    margin-top: 14px;
}

.imp-idle {
    padding: 14px;
    border: 1px dashed var(--c-border);
    border-radius: 10px;
    background: var(--c-surface-2);
    font-size: 13px;
    color: var(--c-text-3);
    text-align: center;
}

/* ============================================================
   学生信息 →「新增 / 编辑学生」弹窗里的两项校验提示
   ------------------------------------------------------------
   2026-09-25 用户要求加两项验证（身份证号、省学籍号）。
   一行一项，跟表单里那两个输入框**上下对齐**，问题显示在它自己那一行，
   不用在一堆文字里找是哪一格填错了。
   ============================================================ */
.sz-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0 6px;
    min-height: 24px;
    line-height: 1.9;
}

.sz-tip {
    font-size: 12.5px;
    color: var(--c-text-3);
}

.sz-list {
    margin: 6px 0 0;
    padding-left: 20px;
    font-size: 12.5px;
    line-height: 1.9;
    color: var(--c-warn);
}

.imp-chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}

.imp-cls {
    margin-top: 12px;
    border: 1px solid var(--c-border);
    border-radius: 10px;
    overflow: hidden;
}

.imp-cls-head {
    padding: 8px 12px;
    background: var(--c-surface-2);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text-2);
    border-bottom: 1px solid var(--c-border);
}

.imp-cls-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 8px 12px;
    font-size: 13px;
    border-bottom: 1px solid var(--c-border);
}

.imp-cls-row:last-child {
    border-bottom: none;
}

.imp-cls-row b {
    min-width: 108px;
}

.imp-cls-row .n {
    font-size: 12px;
    color: var(--c-text-3);
    min-width: 48px;
}

.imp-cls-row .ok {
    color: #0f766e;
    font-size: 12.5px;
}

.imp-cls-row .new {
    color: #b45309;
    font-size: 12.5px;
}

@media (max-width: 640px) {
    .cls-list {
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    }

    .cls-new-hint {
        padding-bottom: 0;
    }
}

/* ============================================================
   作息时间（第 9 个功能）
   ------------------------------------------------------------
   左边的表是「一节一行」的编辑表，上午 / 下午各有一个小节标题行；
   右边「今天的时间轴」按当前时间把每节标成 已结束 / 正在上 / 还没开始。
   ============================================================ */

/*
 * 作息编辑表：固定列宽，别让「名称」把整张表撑得左松右紧。
 * 名称列留给剩余宽度，时间输入框各自站好自己的列。
 */
.sc-edit-table {
    table-layout: fixed;
    width: 100%;
}

/* 列序：1 节次 / 2 类型 / 3 名称 / 4 上午下午 / 5 开始 / 6 结束 / 7 时长 / 8 操作 */
.sc-edit-table th:nth-child(1),
.sc-edit-table td:nth-child(1) {
    width: 56px;
}

.sc-edit-table th:nth-child(2),
.sc-edit-table td:nth-child(2) {
    width: 126px;
}

.sc-edit-table th:nth-child(4),
.sc-edit-table td:nth-child(4) {
    width: 92px;
}

.sc-edit-table th:nth-child(5),
.sc-edit-table td:nth-child(5),
.sc-edit-table th:nth-child(6),
.sc-edit-table td:nth-child(6) {
    width: 116px;
}

.sc-edit-table th:nth-child(7),
.sc-edit-table td:nth-child(7) {
    width: 74px;
}

.sc-edit-table th:nth-child(8),
.sc-edit-table td:nth-child(8) {
    width: 132px;
    white-space: nowrap;
}

.sc-edit-table input,
.sc-edit-table select {
    width: 100%;
    min-width: 0;
    padding: 5px 7px;
}

.sc-edit-table .btn + .btn {
    margin-left: 4px;
}

/* 上移 / 下移：小方钮，不抢「删除」的视觉重量 */
.btn-icon {
    padding: 4px 8px;
    min-width: 30px;
    font-size: 13px;
    line-height: 1.2;
}

.btn-icon[disabled] {
    opacity: .35;
    cursor: not-allowed;
}

/* 非上课的时段（大课间 / 眼保健操 / 午休）：浅蓝底，跟上课的行分开。
   作息页的编辑表和「数据与设置」里的只读一览都用 .tb，一处定义两处生效 */
.tb tr.is-act td {
    background: #eef6fd;
}

.sc-edit-table tr.is-act td:nth-child(1) {
    color: var(--c-text-3);
}

/* 按钮行里那个「加什么类型」的下拉 */
.sc-kind-new {
    padding: 4px 8px;
    font-size: 13px;
    max-width: 150px;
}

/* 模板选择：选中整块要高亮，不然看不出点了哪个 */
.sc-tpl {
    transition: background .12s, border-color .12s;
}

.sc-tpl:hover {
    background: var(--c-surface-2);
}

.sc-tpl:has(input:checked) {
    border-color: var(--c-primary) !important;
    background: var(--c-primary-l);
}

/* 表里的「上午 / 下午」小节行 */
.sc-part-row td {
    background: var(--c-surface-2);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text-2);
    letter-spacing: .4px;
}

.tl-part {
    font-size: 12.5px;
    font-weight: 700;
    /* ★ 2026-10-05 用户第 1 条：从 --c-text-3（#7c7c83）提到 --c-text-2（#58585f）——
       「上午 / 下午」是这两段的标题，不该比正文还轻。 */
    color: var(--c-text-2);
    margin: 12px 0 6px;
}

.tl-part:first-child {
    margin-top: 0;
}

.tl-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 11px;
    border-radius: var(--radius-sm);
    background: var(--c-surface-2);
}

.tl-row + .tl-row {
    margin-top: 8px;
}

.tl-row .tl-time {
    font-variant-numeric: tabular-nums;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text-2);
    min-width: 104px;
}

.tl-row .tl-name {
    font-weight: 600;
}

.tl-row .tl-tip {
    margin-left: auto;
    font-size: 12.5px;
    /* ★ 2026-10-05 用户第 1 条：「已结束 · 40 分」这行小字原来用最浅的一档（--c-text-3），
       叠上整行的淡出之后基本看不清。提到 --c-text-2。 */
    color: var(--c-text-2);
    white-space: nowrap;
}

/* 大课间 / 眼保健操 / 午休：蓝灰底 + 左侧色条，一眼看出这不是上课 */
.tl-row.is-act {
    background: #eef6fd;
    box-shadow: inset 3px 0 0 var(--c-info);
}

.tl-row.is-act .tl-name {
    color: #12557f;
}

/* ★ 2026-10-05 用户第 5 条：活动（午休 / 大课间…）正在进行 —— 底色加深一档（见 --c-info-fill） */
.tl-row.is-act.is-now {
    background: var(--c-info-fill);
    box-shadow: inset 0 0 0 2px var(--c-info);
}

/* 已经上完的：压淡一点，一眼看出进度。
   ★★ 2026-10-05 用户第 1 条：「作息时间字体颜色还是太浅了」——
     真凶就是这里：原来是 `opacity: .6`，**整行**（时间 / 名称 / 右侧那行小字）
     一起被拉到 60% 不透明。一到下午 / 晚上，页面上几乎每一行都是「已结束」，
     整页看着就是一片灰（用户截图里正是如此）；再叠上时间那档本来就是 --c-text-2，
     就成了「看不清」。
   改法：**降到 .85** —— 够淡，能一眼分出「上过了」；又一眼读得清。
   ⚠️ 别再用 .6 这种「整行打折」的写法：它是乘在**所有**子元素上的，压起来最狠。 */
.tl-row.is-done {
    opacity: .85;
}

/* 正在上的：绿底 + 绿边，和「出勤」一个色系 */
.tl-row.is-now {
    background: var(--c-success-fill);
    box-shadow: inset 0 0 0 2px var(--c-success);
}

.tl-row.is-now .tl-time,
.tl-row.is-now .tl-name {
    color: #0b5e3f;
    /* 正在进行的这一行文字也加粗 —— 光靠底色在投影仪 / 手机上还是不够跳 */
    font-weight: 700;
}

.tl-row.is-act.is-now .tl-time,
.tl-row.is-act.is-now .tl-name {
    color: #0b4c7d;
}

/* ============================================================
   餐费统计 · 「不算餐费的学生」设置弹窗 + 明细里的标记
   ------------------------------------------------------------
   名单可能 40 多人，所以勾选区固定高度、内部滚动；
   名字按行内排列（一行放两三个），比一列长条好点也省高度。
   ============================================================ */
.meal-ex-list {
    max-height: 320px;
    overflow: auto;
    border: 1px solid var(--c-border);
    border-radius: 10px;
    padding: 10px 12px;
    background: var(--c-surface-2, #f8fafc);
}

.meal-ex-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0 16px 10px 0;
    font-weight: 400;
    cursor: pointer;
    white-space: nowrap;
}

.meal-ex-modes {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 6px;
}

.meal-ex-modes label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 400;
    cursor: pointer;
}

/* 明细里「这个人不算餐费」的小标 */
.meal-ex-tag {
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 400;
    color: var(--c-warn);
    background: rgba(240, 180, 60, .18);
    white-space: nowrap;
}

/*
 * ★ 2026-10-01 用户第 5 条（手机上不用左右拖就能看到金额）：
 *   窄屏上让这个小标**另起一行** —— 它本身约 60px 宽（「不计餐费」四个字 + 内边距），
 *   跟姓名挤在同一格会把「姓名」这一列撑宽 40 多像素，后面的「金额」就被推出屏幕了。
 *   另起一行后：姓名列宽仍由姓名自己决定，金额列稳稳落在可视区里；
 *   表格的 `min-width: max-content` 也就不用为了这个标签整体变宽。
 *   （单元格高一点没关系，浏览器验收里那条「姓名不被压成一字一行」要求 < 60px，两行约 46px。）
 */
@media (max-width: 620px) {
    .meal-ex-tag {
        display: block;
        margin: 2px 0 0;
    }

    /*
     * 再把这张表的内边距收一档（左右各 12px → 8px）：7 列一共省下 ~56px，
     * 让「金额」那列稳稳落在屏幕里（实测：金额列 299~385px、可视到 386px —— 只差 1px，
     * 太贴边；收完内边距后有 40px 余量，遇到 4 个字的名字也不会把它顶出去）。
     * ⚠️ 只改 #mealTable 这一张表，别的表（考勤明细 / 班级名单…）不受影响。
     */
    #mealTable th,
    #mealTable td {
        padding-left: 8px;
        padding-right: 8px;
    }
}

.meal-ex-note {
    border-left-color: var(--c-warn);
}

/* ============================================================
   考勤记录：可点的请假数字 + 「具体哪几天 / 哪几个人」弹窗
   ------------------------------------------------------------
   数字做成可点的（虚线下划线 + 手指），老师一眼就知道能点开看明细；
   0 就不做成按钮 —— 点了也是空的。
   ============================================================ */
.att-hit {
    cursor: pointer;
    font-weight: 700;
    text-decoration: underline dotted 1.5px;
    text-underline-offset: 3px;
}

.att-hit.personal {
    color: var(--c-att-personal);
}

.att-hit.sick {
    color: var(--c-att-sick);
}

.att-hit:hover {
    filter: brightness(.85);
}

/* ============================================================
   考勤页卡片里的「📊 考勤记录」按钮
   ------------------------------------------------------------
   顶栏那一排撤掉之后（用户 2026-09-22 要求），这里是进统计的**唯一**入口，
   所以要比旁边「一键全勤 / 请假登记 / 多选」更抢眼：主色实心 + 加粗 + 一点投影。
   只加投影不加位移，免得跟 .btn 自带的 hover 变换打架。
   ============================================================ */
.att-stats-btn {
    font-weight: 600;
    box-shadow: 0 2px 8px rgba(29, 95, 216, .3);
}

.att-stats-btn:hover {
    box-shadow: 0 3px 12px rgba(29, 95, 216, .42);
}

/*
 * ⚠️ 下面这一段 .sub-* 是**现役**公用件（净额三态 / 可排序表头 / 明细表里的班级星期列 /
 *    汇总卡），代课统计页还在用 —— 别因为前缀名叫 sub 就当成遗产删掉。
 *    历史上同名前缀下还有一批只给另一个页面用的规则，已随那个页面整片清掉。
 */
.sub-net {
    font-weight: 600;
    white-space: nowrap;
}

/* 可排序的表头 */
.sub-th {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.sub-th:hover {
    color: var(--c-primary-d);
}

.sub-th .sub-ar {
    display: inline-block;
    min-width: 12px;
    font-size: 11.5px;
    color: var(--c-primary);
}

.sub-th.sub-sorted {
    color: var(--c-primary-d);
    font-weight: 700;
}

.sub-sort-tip {
    margin-top: 10px;
    font-size: 12.5px;
    color: var(--c-text-3);
}

.sub-wd {
    color: var(--c-text-3);
    font-size: 12px;
    margin-left: 4px;
}

/* 明细里新加的「班级」列（2026-09-24：明细是全部班级的） */
.sub-cls {
    color: var(--c-text-2);
    font-weight: 600;
}

/* ---------------- 卡片级「📤 导出 ▾」下拉（2026-09-24 起） ----------------
   谁在用：代课统计的「代课明细」卡、考勤记录的「学生明细」卡
   （2026-09-25 抽成公用：HTML 由 core.js 的 cardExportHTML() 出，
   交互由 bindCardExport() 绑，所以两处的类名是同一套）。
   两个选项：导出图片（PNG）/ 导出 Excel（.xlsx），导的都是**卡片上那一段**的数据。
   ⚠️ 类名跟模块无关（card-exp-*），别照着模块名再起一套。
   --------------------------------------------------------------------------------- */
.card-exp {
    position: relative;
}

.card-exp-caret {
    font-size: 11.5px;
    margin-left: 2px;
    opacity: .85;
}

.card-exp-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 236px;
    padding: 6px;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-m);
}

/* 收起来：display 被上面的 flex 覆盖了，得单独按优先级压回去 */
.card-exp-menu[hidden] {
    display: none;
}

.card-exp-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--c-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.card-exp-item:hover,
.card-exp-item:focus-visible {
    background: var(--c-primary-l);
}

.card-exp-item .ico {
    font-size: 17px;
    line-height: 1;
}

.card-exp-item .tx {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.card-exp-item .tx b {
    font-size: 13.5px;
}

.card-exp-item .tx i {
    font-style: normal;
    font-size: 12px;
    color: var(--c-text-3);
}

/*
 * 手机：下拉别顶出屏幕。
 * ★★ 2026-10-05 用户第 2 条（手机截图）：原来这里只把 min-width 收到 208px，
 *   锚点还是 `right: 0` —— 菜单从**按钮右边缘**往左铺 208px。
 *   而手机上卡片头是 `flex-wrap: wrap`：标题占满一行后，按钮换到第二行**贴着左边**，
 *   于是 208px 往左铺就铺到屏幕外去了（实测左边超出 51px，用户看到的就是「导出图片」
 *   那几个字被裁掉）。现在改成**从按钮左边缘往右铺**，并给一个「最多占满屏宽」的上限。
 *   ⚠️ 只在 ≤620px 改：621~900px（平板）卡片头还在一行里、按钮靠右，`right: 0` 是对的。
 */
@media (max-width: 620px) {
    .card-exp-menu {
        right: auto;
        left: 0;
        min-width: 0;
        width: max-content;
        max-width: calc(100vw - 32px);
    }
}

/* 明细行「操作」列：编辑 + 删除 两个按钮并排（用户 2026-09-24 要求加编辑） */
.sub-ops {
    display: flex;
    gap: 6px;
    flex-wrap: nowrap;
}

@media (max-width: 700px) {
}

/* 最底下的「汇总」 */
.sub-total-card {
    border: 1.5px solid var(--c-primary-l);
}

.sub-total-stats {
    margin-bottom: 4px;
}

.sub-total-line {
    margin: 12px 0 10px;
    font-size: 13px;
    color: var(--c-text-2);
}

/* 汇总里那张「谁代课几节 / 被代课几节 / 公假几节」的表 */
.sub-total-table {
    margin-top: 4px;
}

.sub-cover-num {
    color: var(--c-primary-d);
    font-weight: 600;
}

/* ---------- 「代课净额统计」那张两列表（2026-10-07 第四批，用户第 1 条） ----------
   节数 = 代课 − 被代课：正数（代课多）/ 负数（被代课多）/ 0（互相抵消）三种颜色分开，
   一眼看得出谁净赚、谁倒扣；最后固定一行「公假」= 这段区间公假的总和。
   ★ 只两列 —— 表格给个**宽度上限**（不要一条细长条拉满整屏），
     但外层容器**占满卡片**（滚动条要贴卡片右边），见下面 `.sub-net-row`。 */
.sub-net-table {
    margin-top: 4px;
}

/* 它只有两列（老师 / 节数），不需要 `table.tb` 那条 460px 的最小宽度 ——
   半宽卡片 + 手机屏都会因此白挨一条横向滚动条。 */
.sub-net-table table.tb {
    min-width: 0;
}

/*
 * ★★ 2026-10-09 第十三批（用户第 2 条）：「🧾 代课净额统计」卡 + 「💡 代课统计怎么用」说明卡
 *    **一半一半**并排（用户原话：「将这个卡片左右宽度缩小到现在的一半，另一半放一个使用说明的卡片」）。
 *    · 用 grid 两列；窄屏（≤900px，跟平台其它两栏布局同一个断点）自动落成上下两张。
 *    · 里头两张卡各自取消 `margin-bottom`，由这个容器统一留 16px ——
 *      否则 `.card:last-child { margin-bottom: 0 }` 会让右/下那张贴住下一张卡。
 *    · ★★ 2026-10-09 第十四批（用户第 3 条）：「左右两边卡片大小一样大」——
 *      原来写的是 `align-items: start`（各按各的内容高），两张卡一高一矮；
 *      改成 `stretch`（默认值，显式写出来更明白）：两列**同高**，
 *      矮的那张自动撑到跟高的一样，两卡上下边对齐。
 *      （grid 的 item 就是这两张 .card 本身，没有多余包层，stretch 直接生效。）
 */
.sub-net-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    align-items: stretch;
    margin-bottom: 16px;
}

.sub-net-row .card {
    margin-bottom: 0;
}

@media (max-width: 900px) {
    .sub-net-row {
        grid-template-columns: 1fr;
    }
}

/* 「💡 代课统计怎么用」说明卡 */
.sub-help-list {
    margin: 0;
    padding-left: 1.3em;
    font-size: 13.5px;
    line-height: 1.8;
    color: var(--c-text-2);
}

.sub-help-list li + li {
    margin-top: 7px;
}

.sub-help-list b {
    color: var(--c-text);
}

/* 四个颜色的小圆角标签（跟代课记录档的卡片四态同一套色） */
.sub-help-k {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 5px;
    font-size: 12px;
    font-weight: 600;
}

.sub-help-k.k-ok { background: var(--c-att-present-l); color: #0b6b47; }
.sub-help-k.k-part { background: var(--c-att-sick-l); color: #a35a05; }
.sub-help-k.k-all { background: var(--c-purple-l); color: #40218f; }
.sub-help-k.k-none { background: var(--c-surface-2); color: var(--c-text-3); }

.sub-net-num {
    font-weight: 600;
    font-variant-numeric: tabular-nums;   /* 数字等宽，一列对齐 */
}

.sub-net-pos { color: var(--c-primary-d); }   /* 代课多 → 正数（蓝） */
.sub-net-neg { color: var(--c-warn); }        /* 被代课多 → 负数（琥珀，跟被代课那档同色系） */
.sub-net-even { color: var(--c-text-3); }     /* 抵消 → 0（灰） */

/* 最后那行「公假」：上面一条分隔线，跟上面的老师行分开 */
.sub-net-pub td {
    border-top: 1.5px solid var(--c-border);
}

.sub-net-pub strong,
.sub-net-pub-num {
    color: var(--c-warn);
}

/* ---------- 代课明细的「批量删除」（勾选模式）2026-10-07 第四批，用户第 1 条 ----------
   最左那一列复选框默认不占地方（display:none），表挂上 .t2-multi 才显出来 ——
   进 / 出勾选模式只要切这个 class，不用重建表头。 */
.tb .t2-selcol {
    display: none;
    width: 34px;
    text-align: center;
}

.tb.t2-multi .t2-selcol {
    display: table-cell;
}

/* 勾上的那一行给个浅蓝底，一眼看得出勾了哪些 */
.tb tbody tr.t2-picked > td {
    background: var(--c-info-l);
}

/* 勾选模式那条操作条（卡片头与表格之间） */
.t2-pickbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 0 0 10px;
    padding: 8px 12px;
    border: 1px solid var(--c-border);
    border-radius: 10px;
    background: var(--c-surface-2);
    font-size: 13px;
}

.t2-pick-n {
    color: var(--c-text-2);
}

.t2-pick-n b {
    color: var(--c-danger);
    font-size: 15px;
}

/* 「点两下才删」：第一下只是上膛（变红 + 改字），第二下才真删 */
.t2-pickbar .btn.t2-del.armed {
    background: var(--c-danger);
    border-color: var(--c-danger);
    color: #fff;
}

/* ---------- 「按日期汇总」的两列表（2026-10-07 第四批，用户第 3 条） ----------
   第二列是一串「张三（公）2、李四（私）1」—— 老师多的时候要**自动换行**，
   别往右边拖出一条长尾巴（用户原话：「不要后面拖老长」）。

   ★ 2026-10-07 第七批（用户第 2 条）又加了三件事，动这一块之前先看一遍：
     · 「一行最多就 4 个名字，多了的在这个单元格里另起一行显示」→ JS 把名字按 4 个一组，
       每组一个 `.t2-bd-line`（分组口径在 core.js 的 `substByDateLines`，能单测）；
     · 「限制代课老师那一列的列宽，但是不限制行高」→ 表格改 `table-layout: fixed`，
       日期列吃掉固定宽度 → 老师列被钉住；**行数不设上限**，行高自己撑开；
     · 「当行高发生变化时，日期一定要在高度上居中」→ td 显式 `vertical-align: middle`。
   ⚠️ 别把 `table-layout: fixed` 去掉 —— 去掉之后列宽又变成「按内容撑」，
      名字一多就把列顶宽，等于白改。 */
.t2-bd-table {
    table-layout: fixed;
}

.t2-bd-table th:first-child,
.t2-bd-table td:first-child {
    width: 190px;   /* 日期 + 星期 + 角标够用；剩下的宽度全归「代课老师」那一列 */
}

.t2-bd-table td {
    vertical-align: middle;   /* ★ 行高随名字行数变，日期必须跟着居中 */
}

.t2-bd-names {
    line-height: 1.9;
    white-space: normal;
    word-break: break-word;
}

/* 一组 = 一行（最多 4 个名字）。格子窄的时候 flex 会自己提前折行，
   所以实际每行**只会更少、不会更多** —— 正好是用户说的「最多 4 个」。 */
.t2-bd-line {
    display: flex;
    flex-wrap: wrap;
}

.t2-bd-one {
    white-space: nowrap;
}

/* 「公假 -3」那一项（2026-10-08 第九批，用户第 1 条）：
   它不列人名，用稍沉的色 + 稍重一点，一眼跟旁边的「张三（私）-1」区分开。 */
.t2-bd-pubone {
    color: var(--c-text-2);
    font-weight: 600;
}

/* 「公 / 私」小一号（用户明确要求「分别用小一号的公、私来表示」） */
.t2-bd-mark {
    font-size: .85em;
    color: var(--c-text-2);
    margin-right: 1px;
}

.t2-bd-sep {
    color: var(--c-text-3);
}

/* 日期列可以点（切换「最近在前 / 最早在前」）—— 表头样式复用明细那套 .sub-th / .sub-ar */
.t2-bd-date {
    cursor: pointer;
    white-space: nowrap;
}

/* 日期那一格：再钉一次垂直居中（`.t2-bd-table td` 已经统一管了，这里防别的规则覆盖） */
.t2-bd-datecell {
    vertical-align: middle;
}

/* ---------------- 餐费工具条：**一行**放得下（2026-09-28 用户第 3 条） ----------------
   改之前是两行：「计费月份 / 应收餐费」一行，下面「起始 / 结束 / 历史 / 记录日志 /
   不算餐费」一行；而且两行里的控件还高低不齐（日期框 40px、按钮 34px，截图里很明显）。

   现在合成一行，三条规矩（动这里之前先看一遍）：
     · 标签一律放在控件的**左边**（`.field` 在这里改成横排）—— 字段矮一半，一行才放得下；
     · 这一行里的输入框 / 下拉 / 按钮**高度统一 36px**（下面那两条一起管住三种元素），
       谁也别比谁高；
     · 说明文字（「应收餐费按 9 月吃饭 22 天 × 8 元算…」）**不放在行里** —— 它在行里一长
       就把后面的控件挤到第二行去，所以放行下面单独一行（`.meal-due-tip`）。

   坑：`<select>` 的**最小宽度由最长那一条 option 决定**（历史那条
   「2026-08-26 ~ 2026-09-24 第 9 次 · ¥6224 · …」有 360 多 px），
   所以光收窄字段没用 —— 必须给 select 开 `min-width: 0`，它才肯跟着字段一起缩。
   ------------------------------------------------------------------ */
.meal-bar {
    gap: 6px;
    flex-wrap: nowrap;
    align-items: center;
}

/* 标签横过来放控件左边：一行里最省地方，行高也统一 */
.toolbar.meal-bar .field {
    flex-direction: row;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.toolbar.meal-bar .field > label {
    white-space: nowrap;
    font-size: 12.5px;
}

/* 高度一致：输入框 / 下拉 / 按钮都是 36px（比全局的 40px 矮一点，一行才放得下） */
.toolbar.meal-bar input,
.toolbar.meal-bar select,
.toolbar.meal-bar .btn {
    height: 36px;
    min-height: 36px;
    font-size: 12.5px;
}

.toolbar.meal-bar input,
.toolbar.meal-bar select {
    padding: 5px 9px;
}

/* 各字段的宽度（放不下时先压「历史」那一栏，见下面 .meal-hist） */
/* ★ 2026-10-04：判据是「值要显示完整」—— 计费月份是原生 month 输入框
   （Edge/Chrome 里显示「2026年10月 + 日历图标」，约 120px），应收最多 4 位数，
   日期是「2026/10/01 + 日历图标」。验收脚本会盯着「值有没有被截断」，调这里之前先跑一遍。 */
.toolbar.meal-bar .meal-month input { width: 124px; }
.toolbar.meal-bar .meal-due input {
    width: 84px;
    font-weight: 600;
}
/* ★ 2026-09-28 晚第 1 条：自动算出的应收**直接显示数字**（不再写「自动」二字），
   灰色 + 常规字重 = 自动；老师改过以后没有 is-auto 类，回到深色加粗 = 手填。 */
.toolbar.meal-bar .meal-due input.is-auto {
    color: var(--c-text-3);
    font-weight: 500;
}
.toolbar.meal-bar .meal-date input { width: 112px; }

/* 历史下拉是这一行里唯一肯伸缩的那个：宽屏上它占满剩下的地方，
   窄屏上它先缩（文字用省略号），好让别的按钮留在同一行 */
.toolbar.meal-bar .meal-hist {
    flex: 1 1 120px;
    min-width: 96px;
}

.meal-bar .meal-hist select {
    min-width: 0;
    text-overflow: ellipsis;
}

.meal-bar .btn {
    white-space: nowrap;
    padding-left: 10px;
    padding-right: 10px;
}

/* 「↺ / ↩」这两颗恢复按钮只有图标（意思写在 title 里），窄一点省地方 */
.meal-bar .btn.meal-mini {
    padding-left: 8px;
    padding-right: 8px;
}

/* 行下面那一句「应收餐费怎么算的」—— 从行里挪出来的（见上面第 3 条） */
.meal-due-tip {
    margin-top: 8px;
    font-size: 12px;
    color: var(--c-text-3);
    line-height: 1.6;
}

.meal-due-tip b {
    color: var(--c-text-2);
}

/* ★ 2026-10-04 用户第 7 条：自动算出来的应收后面跟一句橙字提醒 ——
   「自动算的是校历口径，实际收了多少钱请自己核一遍」。 */
.meal-due-tip .meal-warn {
    color: var(--c-warn);
    font-weight: 700;
    margin-left: 2px;
}

/* 窄屏（手机 / 平板竖屏）这一行本来就放不下，允许它自己折行 ——
   宁可占两行，也不要把「🧾 记录日志」挤出屏幕。
   ★ 2026-10-01 用户第 4 条（手机截图）：折行之后**每个字段不许再被压扁** ——
     原来字段默认 `flex-shrink: 1`，挤在一行里会把输入框压窄，
     日期 / 计费月份就被截成「2026/10/0」「2026年10」。
     现在 `flex: 0 0 auto` 让每个字段保持自己的宽度，放不下就整块换行（值一定显示全）。 */
@media (max-width: 860px) {
    .meal-bar {
        flex-wrap: wrap;
    }

    .toolbar.meal-bar .field {
        flex: 0 0 auto;
    }

    /* 「历史」下拉是这一行里唯一肯伸缩的，换行后让它把剩下的地方占满 */
    .toolbar.meal-bar .meal-hist {
        flex: 1 1 160px;
    }

    /* ★ 2026-10-01 用户第 4 条：手机上日期框要**完整显示** ——
       124px 里 Chrome 的「2026/09/02 + 日历图标」差几个像素放不下，会被截成「2026/09/0」。
       窄屏上每行放得下，就多给一点（起始 + 结束 两框 142 + 142 + 标签 ≈ 350px）。 */
    .toolbar.meal-bar .meal-date input {
        width: 136px;
    }

    /* 计费月份是原生 month 输入框：窄屏上给足宽度，保证「2026年10月」不被截断 */
    .toolbar.meal-bar .meal-month input {
        width: 134px;
    }
}

/* ============================================================
   作业统计 · 学生累计里的「未交」数字
   ------------------------------------------------------------
   和考勤那边的 .att-hit 是同一个套路：可点的数字做成「虚线下划线的红色按钮」，
   一眼看出能点开看「是哪几天没交」。0 不套这个类（点了也是空的）。
   ============================================================ */
.miss-hit {
    cursor: pointer;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--c-danger);
    background: none;
    border: none;
    padding: 0 2px;
    font-size: inherit;
    font-family: inherit;
    text-decoration: underline dotted 1.5px;
    text-underline-offset: 3px;
}

.miss-hit:hover {
    filter: brightness(.85);
    background: var(--c-danger-l, rgba(214, 62, 62, .1));
    border-radius: 4px;
}

/* 弹窗里的一句话说明 */
.att-dlg-sub {
    color: var(--c-text-3);
    font-size: 13px;
    margin: 0 0 12px;
}

/* 日期 / 姓名的小卡片 */
.att-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    max-height: 340px;
    overflow-y: auto;
}

.att-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 7px 11px;
    border: 1px solid var(--c-border);
    border-radius: 10px;
    background: #f4f6f9;
    font-size: 13.5px;
    color: var(--c-text-2);
    white-space: nowrap;
}

.att-chip b {
    font-size: 15px;
    font-weight: 700;
    color: var(--c-text);
}

.att-chip i {
    font-style: normal;
    font-size: 12px;
    color: var(--c-text-3);
}

.att-chip.personal {
    background: var(--c-att-personal-l);
    border-color: #b3d6f2;
}

.att-chip.personal b {
    color: #0b4c7d;
}

.att-chip.sick {
    background: var(--c-att-sick-l);
    border-color: #f2cfa4;
}

.att-chip.sick b {
    color: #8a4403;
}

/* ============================================================
   「哪几天 + 那天有谁请假」列表（2026-10-06 用户要求）
   ------------------------------------------------------------
   点顶部「事假 / 病假」的数字弹出来的那个列表：一天一行，
   左边日期 + 星期，右边是当天请假的人名小标签（多个人自动折行）。
   ============================================================ */
.att-day-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 380px;
    overflow-y: auto;
}

.att-day-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--c-border);
    border-radius: 10px;
    background: #f8fafc;
}

.att-day-row .adr-date {
    flex: 0 0 auto;
    padding-top: 3px;
    font-size: 13px;
    font-weight: 700;
    color: var(--c-text);
    white-space: nowrap;
}

.att-day-row .adr-date i {
    margin-left: 5px;
    font-style: normal;
    font-size: 12px;
    font-weight: 400;
    color: var(--c-text-3);
}

.att-day-row .adr-names {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.att-day-row .att-chip {
    padding: 4px 10px;
}

.att-day-row .att-chip b {
    font-size: 13px;
}

/* 统计弹窗顶部的口径说明 */
.att-note {
    margin: 0 0 14px;
    padding: 9px 12px;
    border-radius: 10px;
    background: #f4f6f9;
    color: var(--c-text-2);
    font-size: 12.5px;
    line-height: 1.7;
}

/* ============================================================
   请假名单（考勤记录里「只列有请假的同学」那一块）
   ------------------------------------------------------------
   一个人一张小卡：名字 + 事假/病假各几天 + 具体日期的小标签。
   卡片网格排，手机上自动掉成一列 —— 不用左右拖动就能看全。
   ============================================================ */
.leave-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
    gap: 10px;
}

.leave-card {
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    background: var(--c-surface-2);
    padding: 10px 12px;
    min-width: 0;
}

.lc-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.lc-head > b {
    font-size: 15.5px;
    color: var(--c-text);
}

.lc-tags {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 5px;
}

.lc-tags i {
    font-style: normal;
    font-size: 12px;
    font-weight: 700;
    padding: 1px 8px;
    border-radius: 999px;
    white-space: nowrap;
}

.lc-tags i.personal {
    background: var(--c-att-personal-l);
    color: #0b4c7d;
}

.lc-tags i.sick {
    background: var(--c-att-sick-l);
    color: #8a4403;
}

.lc-dates {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.lc-dates .att-chip {
    padding: 4px 9px;
    font-size: 12.5px;
}

.lc-dates .att-chip b {
    font-size: 13.5px;
}

/* 统计卡上的事假 / 病假：跟姓名卡、小标签用同一套三态色 */
.stat.t-present .num {
    color: var(--c-att-present);
}

.stat.t-present .bar > i {
    background: var(--c-att-present);
}

.stat.t-personal .num {
    color: var(--c-att-personal);
}

.stat.t-personal .bar > i {
    background: var(--c-att-personal);
}

.stat.t-sick .num {
    color: var(--c-att-sick);
}

.stat.t-sick .bar > i {
    background: var(--c-att-sick);
}

/* ============================================================
   表格通用小工具类
   ------------------------------------------------------------
   .nowrap  内容不许折行（日期、姓名这类「折了反而看不懂」的）
   .clip    内容可能很长（比如请假明细），一行截断 + 省略号，
            鼠标停在上面用 title 看全文 —— 比让它把整行撑高强
   ============================================================ */
table.tb th.nowrap,
table.tb td.nowrap {
    white-space: nowrap;
}

table.tb th.clip,
table.tb td.clip {
    max-width: 260px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.att-note b {
    color: var(--c-text);
}

/* ============================================================
   代课统计（以老师为中心）—— 2026-09-24 新增
   ------------------------------------------------------------
   这一页的样式全部用 .t2-* 前缀，**不碰**别处共用的 .sub-* / .hw-* / .card / .tile。
   （.sub-* 是历史前缀，但「净额三态 / 可排序表头 / 明细表的班级星期列 / 汇总卡」这些
     公用件现在还在用它 —— 见文件里 .sub-net 那一段的说明，别当遗产删掉。）
   ============================================================ */

/* ---------- 老师卡片四态 ----------
   跟考勤的绿 / 蓝 / 橙三态同一个思路：底色 + 描边 + 字色三层一起拉开，
   投影仪上、色弱的人都分得清。
   ★ 尺寸（最小高 92px、姓名 21px）走 `.tile` 的公共值，与作业 / 考勤一致，
   这里只管「四态配色」这一件事 —— 别再给 t2-card 单独设尺寸。 */
.tile.t2-card {
    border-width: 2px;
}

/* ⚠️ 这张卡比别人多一行（姓名 + 状态 + 节数），行距与上方留白必须收得比别处紧，
   否则总高会被顶到 98px，三处又不一样高了（实测 92 vs 98）。
   下面这组值刚好让它落在 .tile 的 92px 里，改之前先量。 */
.tile.t2-card .st,
.tile.t2-card .no {
    font-size: 12px;
    margin-top: 1px;
    line-height: 1.3;
}

.tile.t2-card .no {
    color: var(--c-text-3);
}

/* ★ 2026-09-27 新增：卡片上「代别人 N」= 这天他还去替别人上了 N 节。
   单独染成品红（用户要求这个标签换个颜色、跟之前用过的都不一样），
   形状照着上面状态标签那枚小胶囊来 —— 但**字号不放大、内外边距压到最小**：
   「1 节 · 代别人 3」在手机上只有 107px 宽的卡里本来就快顶满，
   稍微放宽一点就会折成两行、把 92px / 76px 的卡高顶破（那三处尺寸统一是钉住的）。 */
.tile.t2-card .no .t2-served-tag {
    color: var(--c-served);
    font-weight: 700;
    padding: 0 4px;
    border-radius: 5px;
    background: var(--c-served-l);
    white-space: nowrap;
}

.tile.t2-card.t2-normal {
    background: var(--c-att-present-l);
    border-color: var(--c-att-present);
}

.tile.t2-card.t2-normal .nm {
    color: #075139;
}

.tile.t2-card.t2-normal .st {
    color: #0b6b47;
}

.tile.t2-card.t2-partial {
    background: var(--c-att-sick-l);
    border-color: var(--c-att-sick);
}

.tile.t2-card.t2-partial .nm {
    color: #8a4403;
}

.tile.t2-card.t2-partial .st {
    color: #a35a05;
}

.tile.t2-card.t2-all {
    background: var(--c-purple-l);
    border-color: var(--c-purple);
}

.tile.t2-card.t2-all .nm {
    color: #40218f;
}

.tile.t2-card.t2-all .st {
    color: #5a34b8;
}

.tile.t2-card.t2-none {
    background: var(--c-surface-2);
    border-color: var(--c-border-strong);
}

.tile.t2-card.t2-none .nm {
    color: var(--c-text-3);
}

.tile.t2-card.t2-none .st {
    color: var(--c-text-3);
}

/* 状态字压一撮同色底纹：形状与深浅都不同，不靠颜色也分得开 */
.tile.t2-card .st {
    padding: 0 6px;
    border-radius: 999px;
}

.tile.t2-card.t2-normal .st {
    background: rgba(18, 133, 90, .14);
}

.tile.t2-card.t2-partial .st {
    background: rgba(217, 119, 6, .16);
}

.tile.t2-card.t2-all .st {
    background: rgba(106, 69, 204, .16);
}

/* 图例上的小圆点（同一套配色，缩小版） */
.t2-legend {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    font-size: 12px;
    color: var(--c-text-2);
    white-space: nowrap;
}

.t2-legend .t2-dot {
    margin-left: 6px;
}

.t2-dot {
    display: inline-block;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    border: 2px solid var(--c-border-strong);
    background: var(--c-surface-2);
    vertical-align: -1px;
}

.t2-dot.t2-normal {
    background: var(--c-att-present);
    border-color: var(--c-att-present);
}

.t2-dot.t2-partial {
    background: var(--c-att-sick);
    border-color: var(--c-att-sick);
}

.t2-dot.t2-all {
    background: var(--c-purple);
    border-color: var(--c-purple);
}

.t2-dot.t2-todo-dot {
    background: var(--c-primary);
    border-color: var(--c-primary);
}

.t2-dot.t2-done-dot {
    background: var(--c-success);
    border-color: var(--c-success);
}

/* 图例第三项：他去替别人的那一节（2026-09-27 加，跟着 .t2-cell.served 的品红） */
.t2-dot.t2-served-dot {
    background: var(--c-served);
    border-color: var(--c-served);
}

/* ---------- 搜索框 ----------
   一套样式三个页面共用（2026-09-27 用户要求把这一条推广开）：
     · 代课统计的「老师」搜索框 —— 历史悠久，类名是 `.t2-search`（那页样式全用 t2- 前缀）；
     · 作业统计 / 考勤记录的「学生」搜索框 —— 新加的，类名 `.name-search`。
   ★ 两套类名**共用同一组规则**（选择器写在一起），不是抄一份 ——
     以后调高度 / 间距只改这一处，别让两边的框长得不一样。
   为什么当初不把 `.t2-search` 也改名：那页的 CSS / 测试 / 截图都按 t2- 走，
   为一个纯样式名去牵动几十处不划算（跟「代课统计」内部仍叫 substitute2 一个道理）。
*/
.t2-search,
.name-search {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.t2-search-ico,
.name-search-ico {
    font-size: 15px;
    flex: 0 0 auto;
}

.t2-search input,
.name-search input {
    flex: 1 1 auto;
    min-width: 0;
    height: 44px;
}

/* ------------------------------------------------------------------
   学生信息的搜索框（2026-10-01 用户第 10、14 条）
   ------------------------------------------------------------------
   第 10 条：原来这一页的搜索是「标签在上、输入框在下」的独立字段，跟考勤 / 作业
            那两个框长得不一样 —— 现在**共用同一个 .name-search 组件**
            （输入框 + 「清空」按钮的行为全在上面那组规则里，改一处三页一起变）。
            ⚠️ 这里**不放 🔍 图标**：上面那条标签「🔍 搜索」本来就带了一个，
               再放一个就是两个放大镜（考勤 / 作业那两页没有标签，才把图标放进去）。
   第 14 条：手机上搜索框与班级下拉并排 —— 标签在同一行、控件在同一行
            （原来每个字段 `min-width:100%` 各占一整行，白占两行）。
*/
/* ★★ 2026-10-05 用户第 4 条：**工具条后面紧跟的表格要留出间距**。
   现场是「数据与设置 → 🔤 姓名拼音对照表」那张卡：「找名字」那一行工具条**直接贴着**
   下面的表格（`.toolbar` 自己没有下边距），而搜索框和表头**底色又都是浅灰** →
   两个浅灰方块零间距，看着就是「贴在一起、还分不出谁是谁」。
   两条一起修：① 这条间距；② 输入框填充加深一档（见 --c-field 那段注释）。
   ⚠️ 写成通用相邻兄弟选择器：以后哪个卡片再「工具条 + 表格」直接贴一起，都会自动带上。
      内联写了 margin-top 的地方（学生导入预览那种）优先级更高，不受影响。
   ★ 已知的两个适用点（2026-10-05 扫过一遍）：
       ① 数据与设置 → 🔤 姓名拼音对照表（#npFind 那行 → #npTable）—— 这次要修的就是它；
       ② 数据与设置 → 作息时间编辑器（schedule-edit.js 那个 .toolbar 后面紧跟 .table-wrap）。
          那一处工具条自带 `margin-bottom:10px`，加完是 10+14=24px —— 偏松但干净，保留。
      要再收紧就改这一个数；别为了某一个卡片把规则改成 `.st-bar` 那种局部写法（试过，是错的）。 */
.toolbar + .table-wrap {
    margin-top: 14px;
}

.toolbar.st-bar .field.st-search {
    flex: 1 1 260px;
}

.toolbar.st-bar .name-search {
    margin-bottom: 0;
    /* 与旁边的班级下拉同高（那套通用规则是 44px，是给「独占一行」的搜索框用的） */
}

.toolbar.st-bar .name-search input {
    height: 40px;
}

/*
 * ★ 2026-10-05 用户第 4 条：搜索框「搬进」任意工具栏时（现在有一处：
 *   数据与设置 → 🔤 姓名拼音对照表的「找名字」），要把这两条一起带上 ——
 *   · 自带的 12px 下边距会让这一格比旁边的控件高，`.toolbar` 又是 flex-end 对齐；
 *   · 40px 高才跟工具栏里别的控件（下拉 / 按钮）一样高（通用规则那条 44px 是给
 *     「独占一行」的搜索框用的，比如考勤 / 作业页）。
 *   跟 `.toolbar.st-bar` 那两条是同一套道理，只是一个跟班级下拉同排、一个跟复选框同排。
 */
.toolbar .name-search {
    margin-bottom: 0;
}

.toolbar .name-search input {
    height: 40px;
}

@media (max-width: 620px) {
    .toolbar.st-bar .field.st-search {
        flex: 1 1 56%;
        min-width: 0;
    }

    .toolbar.st-bar .field.class-pick {
        flex: 1 1 38%;
        min-width: 0;
    }
}

/* ------------------------------------------------------------------
   页面底部的说明块（口径说明 / 使用提示）
   ------------------------------------------------------------------
   ★ 2026-10-01 用户第 2、12 条：餐费页的口径说明、学生信息页的四行使用说明
     原来紧跟在工具栏下面，占着一屏的上半部分 —— 都挪到**页面最下方**。
     样子统一成这个类，以后哪一页还想加「底部说明」直接套。
*/
.page-note {
    font-size: 12.5px;
    line-height: 1.8;
    color: var(--c-text-3);
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 12px 14px;
    margin-bottom: 0;
}

.page-note code {
    font-size: 12px;
}

.page-note b {
    color: var(--c-text-2);
}

/*
 * ★ 2026-10-01 用户要求（手机截图第 3、8、10 条）：手机上这一行原来是**三行**
 *   （🔍 一行、输入框一行、清空一行，见下面 ≤900px 那条媒体查询原来的写法）。
 *   现在三样**并排一行**：图标 15px + gap 10 + 输入框（占满剩下的） + gap 10 + 清空。
 *   顺带修一个老毛病：`hidden` 挡不住 `.btn` 的 display:inline-flex，
 *   所以「清空」在没输入内容时也一直露着 —— 这两条把它按作者本意收起来了。
 */
.name-search .btn[hidden],
.t2-search .btn[hidden] {
    display: none !important;
}

.t2-only {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--c-text-2);
    white-space: nowrap;
    cursor: pointer;
}

.t2-tip,
.t2-rest {
    margin-top: 10px;
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--c-text-3);
}

.t2-rest {
    padding: 7px 12px;
    border-radius: 10px;
    background: var(--c-warn-l);
    border: 1px solid var(--c-warn);
    color: #8a5403;
}

/* ---------- 老师课表弹窗 ---------- */
.t2-sheet-tip {
    margin: 0 0 12px;
    font-size: 12.5px;
    line-height: 1.75;
    color: var(--c-text-2);
}

.t2-sheet-legend {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 8px;
    white-space: nowrap;
}

.t2-sheet-legend .t2-dot {
    margin-left: 6px;
}

.t2-tt-wrap {
    overflow-x: auto;
    padding-bottom: 4px;
}

table.t2-tt {
    width: 100%;
    min-width: 560px;
    border-collapse: separate;
    border-spacing: 6px;
    /* ★ 2026-09-25 用户要求「周一到周五表格间距要一致」。
       默认的 auto 布局按内容分列宽：科目名长的那天（「地方与校本课程（校本）」）
       会被撑得比别的天宽一倍，五列看着参差不齐。
       改成 fixed + `<colgroup>`（JS 里产出）：节次列钉死 84px，其余 5 列平分剩余 —— 
       每列一样宽，间距也自然一致。 */
    table-layout: fixed;
}

table.t2-tt th {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text-2);
    text-align: center;
    padding: 2px 0 4px;
    white-space: nowrap;
}

table.t2-tt th small {
    display: block;
    font-weight: 400;
    color: var(--c-text-3);
    font-size: 12px;
    margin-top: 2px;
}

table.t2-tt th.t2-today-col,
table.t2-tt td.t2-today-col {
    background: rgba(29, 95, 216, .07);
    border-radius: 10px;
}

table.t2-tt td.t2-td {
    padding: 0;
    vertical-align: stretch;
}

/* 节次列（表头 + 每个节次格）的宽度 —— 由 <colgroup> 的 .t2-col-p 决定，
   这里只做兜底：万一 colgroup 没渲染，也尽量按 84px 走。 */
.t2-tt-h,
.t2-tt-p {
    width: 84px;
}

.t2-col-p {
    width: 84px;
}

.t2-tt-p {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text-2);
    text-align: right;
    padding-right: 4px !important;
    white-space: nowrap;
}

.t2-tt-p small {
    display: block;
    font-weight: 400;
    font-size: 11.5px;
    color: var(--c-text-3);
    margin-top: 2px;
}

/* 课表格子：未安排的一律同一种颜色（**不区分科目**，用户明确要求），
   安排好了才换成另一种颜色 —— 扫一眼就知道还差哪几节 */
.t2-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    min-height: 66px;
    padding: 6px 4px;
    border-radius: 10px;
    border: 1.5px solid var(--c-primary);
    background: var(--c-primary-l);
    cursor: pointer;
    text-align: center;
    transition: transform .1s, box-shadow .15s;
}

.t2-cell:hover {
    box-shadow: var(--shadow-m);
}

.t2-cell:active {
    transform: scale(.97);
}

.t2-cell .cls {
    font-size: 12px;
    color: #3c5a8c;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* ⚠️ 这里**故意允许折行**：五列等宽之后，列比原来窄，
   「地方与校本课程（校本）」这种长科目名必须能断成两行，不然会被省略号截掉。
   格子高度会跟着涨，同一行的其他格子由 flex 拉伸，所以还是一行齐平。 */
.t2-cell .sub {
    font-size: 13.5px;
    font-weight: 700;
    color: #1748a8;
    white-space: normal;
    word-break: break-word;
    line-height: 1.3;
    max-width: 100%;
}

.t2-cell .who {
    font-size: 12px;
    color: #4a6b9c;
    white-space: nowrap;
}

.t2-cell.done {
    border-color: var(--c-success);
    background: var(--c-success-l);
}

.t2-cell.done .cls {
    color: #3f6b58;
}

.t2-cell.done .sub {
    color: #075139;
}

.t2-cell.done .who {
    color: #0b6b47;
    font-weight: 700;
}

/*
 * ★ 2026-09-28 晚第 5 条：**已安排 · 公假**的格子（`.done.pub`）跟私人假（绿）区分开 ——
 *   用病假那套橙色（--c-att-sick / -l），跟「公假」在汇总表里的琥珀色胶囊一个系。
 *   图例也多一项（.t2-dot.t2-done-pub-dot）。
 */
.t2-cell.done.pub {
    border-color: var(--c-att-sick);
    background: var(--c-att-sick-l);
}

.t2-cell.done.pub .cls {
    color: #8a5410;
}

.t2-cell.done.pub .sub {
    color: #7a4a08;
}

.t2-cell.done.pub .who {
    color: #a86405;
    font-weight: 700;
}

/* 图例：已安排 · 公假（橙）—— 2026-09-28 晚第 5 条 */
.t2-dot.t2-done-pub-dot {
    background: var(--c-att-sick);
    border-color: var(--c-att-sick);
}

/*
 * ★ 2026-09-27 新增：**他去替别人上**的那一节（用户 2026-09-27 要求
 *   「我安排的代课老师，在他的课表上也要体现出来」）。
 *
 * 跟 `.done`（他自己这节已经安排好人来代）用**同一套版式**，只换颜色 ——
 * 都是「这一节已经有主了」，区别只在于是别人来代他还是他去代别人。
 * 颜色用品红（--c-served），在课表里同时出现蓝（待安排）/ 绿（已安排）
 * / 品红（去替别人）三种，两两都好分。
 */
.t2-cell.served {
    border-color: var(--c-served);
    background: var(--c-served-l);
}

.t2-cell.served .cls {
    color: #7c2b70;
}

.t2-cell.served .sub {
    color: var(--c-served);
}

.t2-cell.served .who {
    color: var(--c-served);
    font-weight: 700;
}

/* 同一个格子里「他自己的课」和「他去替别人」撞在一起（数据上不该出现）——
   只在格子里加一行提醒，不抢主内容的颜色 */
.t2-cell .who.served {
    color: var(--c-served);
    font-weight: 700;
    white-space: normal;
    font-size: 11px;
}

.t2-cell.blank {
    border-style: dashed;
    border-color: var(--c-border);
    background: transparent;
    color: var(--c-text-3);
    cursor: default;
    min-height: 66px;
    box-shadow: none;
}

.t2-cell.blank:active {
    transform: none;
}

.t2-sheet-foot {
    margin-top: 10px;
    font-size: 12.5px;
    color: var(--c-text-3);
}

.t2-sheet-foot b {
    color: var(--c-text);
}

/* ---------- 安排代课弹窗 ---------- */
.t2-assign-head {
    padding: 10px 14px;
    margin-bottom: 14px;
    border-radius: var(--radius);
    background: var(--c-surface-2);
    border: 1px solid var(--c-border);
}

.t2-assign-line {
    font-size: 14px;
    font-weight: 600;
    color: var(--c-text);
    line-height: 1.6;
}

.t2-sep {
    color: var(--c-text-3);
    margin: 0 4px;
}

.t2-assign-sub {
    margin-top: 4px;
    font-size: 12.5px;
    color: var(--c-text-2);
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.t2-assign-done {
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--c-warn-l);
    color: #8a5403;
    font-size: 12px;
}

.t2-field {
    margin-bottom: 14px;
}

.t2-req {
    color: var(--c-danger);
    font-weight: 700;
}

.t2-note {
    color: var(--c-text-3);
    font-weight: 400;
}

.t2-field > small {
    margin-left: 4px;
}

/* ---------- 「代课老师」可搜索的下拉（用户 2026-09-26 要求） ----------
   为什么不用原生 <select>：它搜不了中文 / 拼音，也塞不下「分组 + 有课标记」。
   这里输入框负责搜，隐藏的 #t2Sub 存**确认选中**的那位（保存时读它）。 */
.t2-pick {
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
    height: 46px;
    padding: 0 6px 0 12px;
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius-sm);
    background: var(--c-surface);
}

/* 展开时描边变主色 —— 跟弹窗里其它"正在用的控件"一个观感 */
.t2-pick.open,
.t2-pick:focus-within {
    border-color: var(--c-primary);
    box-shadow: 0 0 0 3px var(--c-primary-l);
}

.t2-pick > input[type="text"] {
    flex: 1 1 auto;
    min-width: 0;
    height: 42px;
    padding: 0;
    border: 0;
    outline: none;
    background: transparent;
    font-size: 14px;
    color: var(--c-text);
}

.t2-pick-btn {
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--c-text-3);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
}

.t2-pick-btn:hover {
    background: var(--c-surface-2);
    color: var(--c-text);
}

/*
 * 候选列表：**就地展开**（不是浮层）。
 * ⚠️ 别改回 position:absolute —— 弹窗正文（`.modal-body`）会裁剪溢出内容，
 *    浮起来的列表只露得出 3 项；候选是全校上百位老师，那样等于没法用。
 *    就地展开撑开弹窗、自身滚动，多高的列表都看得全。
 */
.t2-pick-list {
    margin-top: 6px;
    max-height: 320px;
    overflow-y: auto;
    padding: 4px;
    background: var(--c-surface);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-s);
}

/* 分组标题吸顶：滚到哪儿都看得见"这段是本班没课的" */
.t2-pick-grp {
    position: sticky;
    top: 0;
    z-index: 1;
    margin: 4px 0 2px;
    padding: 4px 8px;
    border-radius: 6px;
    background: var(--c-surface-2);
    font-size: 12px;
    font-weight: 700;
    color: var(--c-text-2);
}

.t2-pick-grp:first-child {
    margin-top: 0;
}

.t2-pick-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 36px;
    padding: 5px 10px;
    border-radius: 6px;
    cursor: pointer;
    /* ★ 2026-09-28 晚第 4 条：这一行**不许换行** —— 原来长句把「黄秉章」三个字
       挤成竖着排了。行内放不下时压 name 的省略号，也不许竖排。 */
    white-space: nowrap;
}

.t2-pick-opt.hl {
    background: var(--c-surface-2);
}

.t2-pick-opt:hover {
    background: var(--c-primary-l);
}

/* 已选中的那位要一眼看得出来（点错的代价是"安排给了别人"） */
.t2-pick-opt.on {
    background: var(--c-primary-l);
    box-shadow: inset 0 0 0 1px var(--c-primary);
}

.t2-pick-nm {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
}

.t2-pick-tag {
    flex: 0 0 auto;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 12px;
    white-space: nowrap;
}

/* ★ 五档都有标签（用户 2026-09-26 要求，2026-09-27 扩到五档）—— 只有"有课"带标记时，
   用户还得回头数分组标题才知道某人属于哪一档。五档各给一套颜色 + 表情，**一个状态一个颜色**：
   本班没课（蓝，最想要的人）/ 其他没课（灰，能用）/ 这会儿有课（橙，在自己的班上课）
   / 代课（紫，已经安排去替别人了 ← 2026-09-27 新增）
   / 被代课（红，自己的课已经别人替了 ← 2026-09-27 新增）。 */
.t2-pick-tag.mine {
    background: var(--c-primary-l);
    color: var(--c-primary-d);
}

.t2-pick-tag.free {
    background: var(--c-surface-2);
    color: var(--c-text-3);
    box-shadow: inset 0 0 0 1px var(--c-border);
}

/* 「有课」用假别那套橙 —— 一眼看出"这人这会儿去不了" */
.t2-pick-tag.busy {
    background: var(--c-warn-l);
    color: #8a5403;
}

/* ★ 2026-09-27 新增：代课（紫）/ 被代课（红）—— 跟上面三档都不撞色 */
.t2-pick-tag.cover {
    background: var(--c-purple-l);
    color: var(--c-purple);
}

.t2-pick-tag.covered {
    background: var(--c-danger-l);
    color: var(--c-danger);
}

.t2-pick-tag.on-tag {
    background: var(--c-success-l);
    color: var(--c-success);
}

/* ★ 2026-09-27 新增：每行最后那句「今日几节课」（用户要求"所有老师的后面都加上"）。
   放最右边、比标签淡一档 —— 它是**参考信息**，不该跟状态标签抢眼睛。
   数字用等宽字形，整列数字才对得齐（一眼扫下来能比大小）。
   ⚠️ 2026-09-28 晚第 4 条：那句「今日 3 节（+1 代课 → 实际 4 节）」被否了（把行挤到换行），
   改成下面这组**四个彩色数字**（.t2-day-nums）；.t2-pick-day 留着兜底（还有别处引用类名）。 */
.t2-pick-day {
    flex: 0 0 auto;
    margin-left: 2px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--c-text-3);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* ★ 2026-09-28 晚第 4 条：课 / 代 / 被 / 共 四个数，一色一意：
     蓝 = 他那天自己的课 ｜ 紫 = 他去替别人（与「代别人」标签同色）
     红 = 别人替他（与「被代课」标签同色）｜ 绿 = 总共的活（课 + 代）。
   横排、标签小字灰色、数字加粗着色；整块 nowrap —— 这一行**不许再折行**。 */
.t2-day-nums {
    flex: 0 0 auto;
    margin-left: 2px;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
    font-size: 11.5px;
    color: var(--c-text-3);
    font-variant-numeric: tabular-nums;
}

.t2-day-nums i {
    font-style: normal;
    display: inline-flex;
    align-items: baseline;
    gap: 2px;
}

.t2-day-nums em {
    font-style: normal;
    font-weight: 700;
    font-size: 12.5px;
}

.t2-day-nums .tn-own { color: var(--c-primary); }
.t2-day-nums .tn-sub { color: var(--c-served); }
.t2-day-nums .tn-cov { color: var(--c-danger); }
.t2-day-nums .tn-all { color: var(--c-success); }

.t2-pick-none {
    padding: 14px 10px;
    text-align: center;
    font-size: 13px;
    color: var(--c-text-3);
    line-height: 1.7;
}

/* 选中「有课」那位时的提示条 —— 只提示，不拦保存 */
.t2-warn {
    margin-top: 8px;
    padding: 8px 12px;
    border: 1px solid #f0cfa4;
    border-radius: var(--radius-sm);
    background: var(--c-warn-l);
    color: #8a5403;
    font-size: 12.5px;
    line-height: 1.7;
}

/* 假别：两枚大按钮，选中态用 CSS 的 :checked 认（跟登记区同一个办法） */
.t2-leave {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
}

.t2-leave-opt {
    position: relative;
    cursor: pointer;
}

.t2-leave-opt input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    margin: 0;
    cursor: pointer;
}

.t2-leave-t {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 46px;
    padding: 6px 10px;
    border: 1.5px solid var(--c-border-strong);
    border-radius: var(--radius);
    background: var(--c-surface);
    font-size: 14px;
    font-weight: 600;
    color: var(--c-text-2);
    transition: background .15s, border-color .15s, color .15s;
}

.t2-leave-t small {
    font-size: 12px;
    font-weight: 400;
    color: var(--c-text-3);
    margin-top: 1px;
}

/*
 * ★★ 2026-10-09（用户）：**私人假这一枚要用「上面那个绿」**。
 *   原来它是主色蓝（--c-primary 系），可同一屏上的「已安排 · 私人假」图例圆点、
 *   课表里已安排的格子都是绿（--c-success / -l）—— 同一个概念两个颜色（用户截图反馈）。
 *   现在两处统一：浅绿底 + 绿边框 + 深绿字（#0b5e3f 是这一套绿的配对文字色，
 *   项目里 .t2-cell.done / 作息「正在进行」那几处都用它，对比度 6:1 以上）。
 *   公假那一枚**没动** —— 它本来就是橙系（--c-warn），跟图例的公假橙是一系。
 *   ⚠️ 要调就调这一对变量，别写死十六进制：浅底 / 边框 / 文字三个要一起换，
 *      只换一个对比度会掉（.t2-leave-public 是同样的三段式，可作对照）。
 */
.t2-leave-private input:checked + .t2-leave-t {
    background: var(--c-success-l);
    border-color: var(--c-success);
    color: #0b5e3f;
}

.t2-leave-public input:checked + .t2-leave-t {
    background: var(--c-warn-l);
    border-color: var(--c-warn);
    color: #8a5403;
}

/* ============================================================
   ★★ 批量录入（用户 2026-09-28 第 2 条）
   「先选一批 → 逐节挑代课老师 → 底部一次保存」的样式
   ============================================================ */

/* 表头「周一 / 周二…」、左上角「节次」、行头「第 N 节」都能点（一次选一批）——
   做成明显"可以点"的样子：手型 + 悬停变底色 */
table.t2-tt th.t2-day-h,
table.t2-tt th.t2-pickall,
table.t2-tt td.t2-row-h {
    cursor: pointer;
    border-radius: 8px;
    transition: background .15s, color .15s;
}

table.t2-tt th.t2-day-h:hover,
table.t2-tt th.t2-pickall:hover,
table.t2-tt td.t2-row-h:hover {
    background: var(--c-primary-l);
    color: var(--c-primary-d);
}

table.t2-tt td.t2-row-h {
    padding: 2px 4px;
}

/* 这一天 / 这一行 / 整张表**全选着** —— 表头给一块实底，一眼看出选到哪儿了 */
table.t2-tt th.t2-day-h.on,
table.t2-tt th.t2-pickall.on,
table.t2-tt td.t2-row-h.on {
    background: var(--c-primary);
    color: #fff;
}

table.t2-tt th.t2-day-h.on small {
    color: rgba(255, 255, 255, .86);
}

/* 选中的格子：外描一圈实线 + 右上角一个 ✓（原来的蓝底不变，免得看不清班级/科目） */
.t2-cell.sel {
    position: relative;
    box-shadow: 0 0 0 2.5px var(--c-primary);
}

.t2-cell.sel::after {
    content: '✓';
    position: absolute;
    top: 1px;
    right: 6px;
    font-size: 12px;
    font-weight: 700;
    color: var(--c-primary);
}

/* 已经挑好代课老师、还没点保存（"待保存"）—— 换成琥珀色，跟"已安排（绿）"分得清 */
.t2-cell.pend {
    border-color: var(--c-warn);
    background: var(--c-warn-l);
}

.t2-cell.pend .who {
    color: #8a5403;
    font-weight: 700;
}

/* 点了保存、但这一节还没挑老师 —— 标红提醒（只在这时候闪两下） */
.t2-cell.need {
    border-color: var(--c-danger);
    background: var(--c-danger-l);
    animation: t2need .5s ease 2;
}

@keyframes t2need {
    50% { box-shadow: 0 0 0 4px rgba(204, 53, 44, .18); }
}

/* ---------- 底部工具栏（用户画的"黑框 = 保存 / 黄框 = 假别"） ----------
   左：已选几节 · 假别【私人假 / 公假】· 清空选择   右：💾 保存 · 关闭 */
.t2-bar-foot {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    flex-wrap: wrap;
}

.t2-bar-text {
    font-size: 13px;
    color: var(--c-text-2);
    white-space: nowrap;
}

/* 进了批量设置模式（选中 ≥ 2 节）就把那句话加重一点 */
.t2-bar-text.batch {
    font-weight: 700;
    color: var(--c-primary-d);
}

/* ★★ 2026-10-07 第八批（用户第 4 条）：「假别」整块**加框加底**并补一个小标题 ——
   原来只是两枚灰白小按钮缩在左下角，很容易当成装饰忽略掉（选错了整批假别都错）。
   ★★ 2026-10-08 第二次调整（用户第 2 条：**太大了，改回适中大小**）：
   保留「假别」小标题与浅框（那是"别当装饰忽略"的保障），但把按钮**压回一行**：
     · 高度 40 → 30px、字号 14.5 → 13px（第八批之前就是 32 / 13 这一档）；
     · 「事假·病假 / 公差·开会」那行小字由**竖排第二行改成同一行**（中间一个点隔开）——
       两行是整块看着"胖"的主因；一行既省高度，也不丢那两句解释。
   → 想再小一点（去掉小字或去掉外框）随时说，改一个数就行。 */
.t2-bar-leave-box {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px 4px 8px;
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius);
    background: var(--c-surface-2);
}

.t2-bar-leave-lb {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--c-text);
    white-space: nowrap;
}

/* 底部这排假别按钮做小（弹窗里那套是两枚大按钮） */
.t2-bar-leave {
    grid-template-columns: repeat(2, auto);
    gap: 6px;
}

.t2-bar-leave .t2-leave-t {
    /* ★ 一行式：主词 + 小字并排（不再竖排两行） */
    flex-direction: row;
    align-items: baseline;
    gap: 5px;
    min-height: 30px;
    padding: 3px 11px;
    font-size: 13px;
    border-radius: var(--radius-sm);
}

/* 小字说明（事假 · 病假 / 公差 · 开会）原来只有 title 里有，现在直接写在主词右边 */
.t2-bar-leave .t2-leave-t small {
    font-size: 11px;
    font-weight: 400;
    margin-top: 0;
    opacity: .8;
}

/* 窄屏（手机）：小字折到下一行，别把两枚按钮挤出屏幕 */
@media (max-width: 560px) {
    .t2-bar-leave-box {
        flex-wrap: wrap;
    }
    .t2-bar-leave .t2-leave-t {
        flex-direction: column;
        align-items: center;
        gap: 1px;
        padding: 3px 12px;
    }
    .t2-bar-leave .t2-leave-t small {
        margin-top: 0;
    }
}

/* ---------- 点第二下、**贴着那一格**弹出的「挑代课老师」小窗 ----------
   ★ 2026-09-28 第 4 条（用户看过第一版之后拍的图提的）：
     第一版是把这块撑在整张课表下面的一大条，用户要的是「在**红框那一格**旁边弹一个
     **小的搜索窗**」。所以它现在是个浮层：只有一格课表那么宽，位置由页面里的
     placeQuick() 按**格子的屏幕坐标**算（默认贴格子左下方，下面放不下就翻上去）。
   ⚠️ 必须是 position:fixed + 一个比弹窗高的 z-index：
     改回 absolute 会掉进弹窗正文（`.modal-body`，overflow:auto）的裁剪范围 ——
     候选列表只剩三行可见（2026-09-27 踩过，只有肉眼看得出，测试不会报）。 */
.t2-quick {
    position: fixed;
    z-index: 8100;
    width: 360px;
    max-width: calc(100vw - 20px);
    padding: 8px 10px 10px;
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius);
    background: var(--c-surface);
    box-shadow: var(--shadow-l);
}

/* `hidden` 属性优先（上面没写 display，才压得住） */
.t2-quick[hidden] {
    display: none;
}

/* 抬头**只占一行**：这窗子本来就小（矮屏上格子旁边只空得出 190px 左右），
   抬头分两行会把候选列表挤到只剩一条缝 —— 见 placeQuick() 里那段"自己收矮" */
.t2-quick-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
    font-size: 12.5px;
    white-space: nowrap;
}

/* 小窗里所有控件都矮一档：这块只有一格课表那么大，
   46px 的输入框一放，候选就没地方了 */
.t2-quick .t2-pick {
    height: 38px;
    padding: 0 4px 0 10px;
}

.t2-quick .t2-pick > input[type="text"] {
    height: 34px;
    font-size: 13.5px;
}

/*
 * 「一年级1班 · 语文」跟在「周一 09-28 · 第 1 节」后面同一行；位置不够就用省略号
 * （格子上面本来就写着班级 / 科目，这行只是补充，挤没了也不影响挑人）。
 * ⚠️ 别再让它 `flex: 1 1 100%` 换到第二行 —— 抬头一高，矮屏上整块就贴不住格子了。
 */
.t2-quick-sub {
    flex: 0 1 auto;
    min-width: 0;
    font-size: 12px;
    color: var(--c-text-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 抬头这一行**只有班级 / 科目那一截可以缩**（缩成省略号）：
   节次和按钮都不能被压扁 —— 它们是主干和出口 */
.t2-quick-head > b,
.t2-quick-head > .btn {
    flex: 0 0 auto;
}

.t2-quick .t2-pick-list {
    max-height: 240px;
}

/* 「取消这次代课」：第一次点是「上膛」，再点一下才真删（不套第二层弹窗） */
.t2-del {
    color: var(--c-danger);
    border-color: var(--c-danger-l);
}

.t2-del.armed {
    background: var(--c-danger-l);
    border-color: var(--c-danger);
    font-weight: 700;
}

/* ---------- 代课汇总档 ---------- */
.t2-range .t2-date {
    min-width: 150px;
    flex: 0 0 auto;
}

.t2-range .field {
    margin: 0;
}

.t2-range-tip {
    margin-top: 10px;
    font-size: 12.5px;
    color: var(--c-text-3);
    /* ★ 2026-09-27（用户要求）：这行「起止两天都算在内 · 明细是全部班级的…」**居中**显示 */
    text-align: center;
}

@media (max-width: 900px) {
    .t2-legend {
        font-size: 12px;
    }

    /*
     * ★ 2026-10-01 用户要求：手机上的搜索框也**一行**（🔍 输入框 清空）。
     *   这一段原来写的是 `flex-wrap: wrap` + `input { flex: 1 1 100% }` ——
     *   输入框独占整行，于是图标一行、输入框一行、清空一行，白占三行高度（截图第 3/8/10 条）。
     *   现在什么都不用改：基础规则就是 nowrap + input 可伸缩，所以这里**故意留空**。
     *   ⚠️ 别再把 wrap / 100% 加回来。
     */
}

@media (max-width: 700px) {
    /* 矮屏上候选项别铺太长 —— 一屏能看清"哪几位是本班没课的"就够，剩下的靠滚 */
    .t2-pick-list {
        max-height: 212px;
    }

    /* 手机上手指点得准：候选项恢复 44px 的点按目标（桌面上 36px 更紧凑） */
    .t2-pick-opt {
        min-height: 44px;
    }

    /* 与桌面一致（都是 560px）：手机上五列同样等宽，靠 .t2-tt-wrap 横向滚动看全，
       窄到 480px 时每列只剩 71px，长科目名会折成三四行、格子被撑得很高。 */
    table.t2-tt {
        min-width: 560px;
    }

    /* 手机上三行卡（姓名 / 状态 / 节数）的两行小字跟 `.tile .st` 取同一个字号，
       否则它比考勤 / 作业那张两行卡高出一截，三处又不一样高了 */
    .tile.t2-card .st,
    .tile.t2-card .no {
        font-size: 12.5px;
    }

    .t2-cell {
        min-height: 58px;
    }

    .t2-range .t2-date {
        min-width: 0;
        flex: 1 1 140px;
    }
}

/* ============================================================
   2026-09-25 用户反馈的三处
   ============================================================ */

/* ---------- ① 总览页可点的统计卡 ----------
   这一块原来只有「最近一次考试」能点（还写着行内 style="cursor:pointer"），
   现在四张都能点：鼠标移上去浮起 + 右上角一个「›」提示可跳转。
   ⚠️ 只加修饰类 .stat-link，不动 .stat 本身（考勤 / 成绩等页面的统计卡共用它）。 */
.stat.stat-link {
    cursor: pointer;
    position: relative;
    transition: box-shadow .15s, border-color .15s, transform .1s;
}

.stat.stat-link:hover {
    box-shadow: var(--shadow-m);
    border-color: var(--c-border-strong);
}

.stat.stat-link:active {
    transform: translateY(1px);
}

.stat.stat-link::after {
    content: '›';
    position: absolute;
    top: 10px;
    right: 12px;
    font-size: 18px;
    font-weight: 700;
    line-height: 1;
    color: var(--c-text-3);
    pointer-events: none;
}

.stat.stat-link:hover::after {
    color: var(--c-primary);
}

/* ---------- ② 作业统计左栏校历：「有作业」的日期要一眼看得出来 ----------
   ★ 原来的 bug（用户 2026-09-25 报的「22 / 23 有数学作业，校历上看不出来」）：
     这套校历后来重写过配色，新的「学期上课日」规则
       .cal-cell.in-term:not(.selected):not(.is-holiday):not(.is-weekend):not(.is-makeup)
     特异性是 (0,6,0)，而老的 `.cal-cell.has-hw` 只有 (0,2,0) —— 数字大的赢，
     所以**蓝底被盖成白底**，有作业的日子跟没作业的长得一模一样。
   修法有两半，缺一不可：
     a) 给那条规则就地加上 `:not(.has-hw)`（见第 4180 / 4185 行），让它别碰有作业的格子；
     b) 把「有作业」的样子加重 —— 蓝底 + 蓝边 + 底部蓝条 + 数字加粗变大，
        四重区别叠在一起，缩到 30 多像素的格子里也分得清。
   ⚠️ 改这套配色时**必须**同时看这两处，别只顾一边。 */
/* ↑ 上面那两条「学期上课日」已经就地加了 `:not(.has-hw)` —— 修的就是它们
   （原来它们把「有作业」的蓝底盖成了白底）。改配色时别把 `:not(.has-hw)` 弄丢。 */

/* 有作业（只有作业统计页会传 countOf，别的页拿不到这个类）
   ★ 2026-10-05 晚：加了 `:not(.is-makeup)` —— 那一天如果同时是**调休上课**，
     底色与边框一律让给「调休」那套紫（调休比「有没有作业」特殊得多：
     那天本来不用上学，却要照常到校）。「有作业」改由**左下角数量角标 +
     下面那条底部蓝条**表达，两个信号各说各的、不再抢同一块底色和边框。 */
.cal-cell.has-hw:not(.selected):not(.is-makeup) {
    background: var(--c-primary-l);
    border-color: var(--c-primary);
    box-shadow: inset 0 -3px 0 var(--c-primary);
}

/* 调休 + 有作业：只补一条底部蓝条（「这天还有作业」），底色/边框仍走紫 */
.cal-cell.is-makeup.has-hw:not(.selected) {
    box-shadow: inset 0 -3px 0 var(--c-primary);
}

/* ★ 同样加 `:not(.is-makeup)`：调休那天即使有作业，数字也留在**紫色**这一族里
   （否则会变成蓝字压在紫底上，两种色系打架 —— 这也是用户说的「太丑」之一）。 */
.cal-cell.has-hw:not(.selected):not(.is-makeup) .d {
    color: var(--c-primary-d);
    font-weight: 800;
}

/*
 * 作业统计左栏格子上**同时显示"几项作业"**：原来这里把数量角标关掉了
 * （怕挤掉日期），但用户要的就是「一眼看出这天有没有作业」——
 * 只靠底色不够，所以把数字角标也放回来。
 *
 * ★★ 2026-10-05 用户要求：**数量角标放左下角**、「休 / 班」留在右下角 ——
 *   两个角标各占一个下角，谁也不挡谁；中间的日期数字水平居中、垂直略微偏上
 *   （见上面 `.hw-left .sc-grid .cal-cell` 的上下不等 padding）。
 *   ⚠️ 别再把它挪回右下角：那样只要遇上「既是调休上课、又有作业」的一天就会撞在一起
 *      （原来靠「两者不会同时出现」侥幸避开的，那不是一条能站住的规矩）。
 */
.hw-left .sc-grid .cal-cell .dot {
    position: absolute;
    left: 1px;
    bottom: 1px;
    display: inline-flex;
    /*
     * ★ 2026-10-05：跟「休 / 班」一样收一档（12 → 11px），两个角标才并排站得下。
     * ★ 2026-10-05 晚（第二批）：再收一档 **11 → 10px**，给「格子压成接近正方形」
     *   腾高度（跟下面 ≤900px 那档已完全一致，所以手机那条覆盖里的尺寸部分删掉了）。
     */
    min-width: 10px;
    height: 10px;
    padding: 0 1.5px;
    margin: 0;
    border-radius: 5px;
    font-size: 9.5px;
    line-height: 1;
    background: var(--c-primary);
    color: #fff;
}

.hw-left .sc-grid .cal-cell.has-hw .d {
    font-size: 16px;
}

@media (max-width: 900px) {
    /*
     * ★★ 2026-10-05 用户第 3 条：手机上**也要让数字居中**。
     *
     * 这里原来把数量角标改回 `position: static`（回到行内、跟在数字下面）——
     * 格子一共才 40px 高，角标占掉下面 13px，`justify-content: center` 只能把
     * 「数字 + 角标」这一整块居中，于是**数字被顶到中心线以上 7px**（真浏览器实测）：
     * 考勤页的格子没有角标 → 居中；作业页有作业的那几天有角标 → 偏高。
     *
     * 现在跟宽屏一个做法：角标**绝对定位**，不占行内高度 → 数字稳稳偏上居中。
     *
     * ★★ 2026-10-05 用户要求：位置从**右下角**改到**左下角** ——
     *   右下角留给「休 / 班」，两个角标各占一个下角，任何一天都不会叠
     *   （原来靠「放假那天没作业」侥幸避开，遇上调休上课 + 有作业就撞了）。
     * ★ 2026-10-05 晚（第二批）：角标尺寸（10px / 9.5px 字）已经**提到上面基础值**里
     *   （宽屏也要同样小才压得出正方形），所以这里只留「位置」，不再重复写尺寸。
     * ⚠️ 别改回 `position: static`：一改回去，有作业的格子数字立刻又不居中了。
     * ⚠️ 也别把这行改回 `right`：左下角是用户点名要的位置。
     */
    .hw-left .sc-grid .cal-cell .dot {
        position: absolute;
        left: 1px;
        bottom: 1px;
    }
}

/* 上一条的两个小尾巴：
   · 选中的那天如果也有作业，蓝色角标压在蓝底上会看不见 → 换成白底深蓝字；
   · 「今天」的描边是 box-shadow 画的，别被上面那条底部蓝条盖掉 → 两个一起画。
     （完整版在文件末尾，那里把白圈 + 描边 + 底部蓝条三层一起写全了） */
.hw-left .sc-grid .cal-cell.selected .dot {
    background: #fff;
    color: var(--c-primary-d);
}

.cal-cell.has-hw:not(.selected).today {
    /* ⚠️ box-shadow 列表**先写的画在最上层** —— 底部那条蓝条必须写在最前面，
       否则会被外面那圈淡橙光圈压掉（「今天有作业」的格子就只剩角标了）。 */
    box-shadow: inset 0 -3px 0 var(--c-primary), 0 0 0 2.5px rgba(196, 71, 26, .16);
}

/* ============================================================
   ⑤ 今天（权威版，2026-09-25 **晚 · 第二版**）
   ------------------------------------------------------------
   第一版（当天上午）为了「够清楚」，画成了三层：3.5px 深砖橙粗内描边、
   中间隔 1px 白圈、日期数字深砖橙特粗。

   傍晚用户指着 25 号那格说「今日日期这个显示太吓人了」。实测那格正好是
   **法定节假日 + 今天 + 已选中**：蓝底上套着一圈 3.5px 橙边，数字又是橙的
   （橙字压蓝底），整格像报警灯。

   第二版的原则：**分清主次，各让一步**
     · 「已选中」优先 —— 蓝底白字，不再往里塞橙圈。默认选中的就是今天，
       这一格本来就一眼看见，再叠一圈橙只是打架。
     · 「今天但没选中」—— 1.5px 柔和砖橙描边 + 一圈很淡的外发光 + 砖橙数字
       （700 重，不再 800），比旁边的格子明显，但不是粗黑框。
     · 「今天 + 有作业」—— 底部那条蓝条（作业数量提示）保留。
     · 图例里的样例句用的是**同一套 class**，自动跟着变（所见即所得）。

   ⚠️ 这段**故意放在文件最后**：`.d` 的颜色有几条同权重的老规则
      （`.cal-cell.is-holiday:not(.selected) .d` 等，权重都是 0,3,1），
      靠源码顺序压过它们。搬到前面去就会被节假日的深琥珀色盖回。
   ⚠️ 所有「今天」的规则都带 `:not(.selected)` —— 选中的那天由「选中态」说了算，
      少了这个 `:not(.selected)` 就会出现「橙字蓝底」（第一版就栽在这）。
   ⚠️ 改颜色请只改 `--c-today`，并顺手看一眼 `.cal-legend .lg.today` 与
      `.sc-mn.today`（年视图）那两处，三处是同一套口径。
   ============================================================ */
/* ⚠️ 描边写 2px 而不是 1.5px：Chrome 会把边框宽度**取整到设备像素** ——
   DPR=1 的普通显示器上 1.5px 会被画成 1px，跟默认那圈边框一样粗（看不出区别）。
   2px 在 DPR=1 上就是 2px、在手机高清屏上略粗一点点，两边都能接受。 */
.cal-cell:not(.selected).today {
    border-color: var(--c-today);
    border-width: 2px;
    box-shadow: 0 0 0 2.5px rgba(196, 71, 26, .16);
}

/* 今天 = 今天 + 有作业：底下那条蓝条（作业数量提示）保留。
   ⚠️ box-shadow 列表**先写的画在最上层**，蓝条要放最前才不会被淡橙光圈压掉。 */
.cal-cell.has-hw:not(.selected).today {
    box-shadow: inset 0 -3px 0 var(--c-primary), 0 0 0 2.5px rgba(196, 71, 26, .16);
}

/* 今天 = 今天 + 选中：**让选中态说事** —— 蓝底白字，不再叠橙圈 */
.cal-cell.selected.today {
    border-color: var(--c-primary-d);
    box-shadow: 0 0 0 3px rgba(29, 95, 216, .28);
}

/* 今天的日期数字：柔和砖橙 + 加粗（列了四种权重，把小日历 / 大日历 / 作业左栏都盖住）
   ⚠️ 必须带 `:not(.selected)`，否则选中那天会变成「橙字蓝底」 */
.cal-cell:not(.selected).today .d,
.hw-left .sc-grid .cal-cell:not(.selected).today .d,
.hw-left .cal-cell:not(.selected).today .d {
    color: var(--c-today);
    font-weight: 700;
}

/* ============================================================
   连不上服务器时的阻断页（2026-09-25 加；2026-10-03 简化）
   ------------------------------------------------------------
   ★ 2026-10-03 用户拍板：平台**必须跑在云服务器上** —— 离线降级与页顶那条
     橙/绿提示条已整套移除，连不上就只走下面的阻断页。
     为的是让老师**一眼知道数据现在在哪**，不出现「看着能存、其实没进学校库」。
   ============================================================ */

/* ---------- 阻断页 ---------- */
.net-block {
    position: fixed;
    inset: 0;
    background: linear-gradient(150deg, #e8eefa, #dde6f6 60%, #d6e0f3);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    /* 比登录层低一点没关系（两者不会同时出现），但要比 loading 低 */
    z-index: 9000;
}

.net-block[hidden] { display: none; }

.net-block-card {
    background: var(--c-surface);
    border-radius: 20px;
    box-shadow: var(--shadow-l);
    padding: 32px 28px 24px;
    width: 100%;
    max-width: 400px;
    text-align: center;
}

.net-block-logo { border-radius: 50%; margin-bottom: 12px; }

.net-block-card h1 {
    font-size: 20px;
    margin: 0 0 8px;
    color: var(--c-text);
}

.net-block-sub {
    font-size: 13px;
    color: var(--c-text-2);
    line-height: 1.7;
    margin: 0 0 18px;
}

.net-block-acts {
    display: flex;
    gap: 8px;
    justify-content: center;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.net-block-foot {
    font-size: 12px;
    color: var(--c-text-3);
    line-height: 1.7;
    margin: 0;
}
/* ============================================================
   手机：输入法弹起时的那点讲究（配合 core.js 的 KeyboardGuard）
   ------------------------------------------------------------
   用户 2026-09-26 的要求：**要调手机键盘的输入框必须留在键盘上面，
   同时留出底部候选空间**。三条缺一不可：

   ① 遮罩往上收（`inset` 的底边让出一个 --kbh）—— 一处 CSS 管所有弹窗，
      不会被键盘盖住下半截（在 iOS 上那一截常常正好是「保存」按钮）；
   ② 文档流补一段等高的空白 —— 页面已经滚到底时，没有它「想把输入框顶上去
      也顶不动」（下面没可滚的余地了）；
   ③ 手机上一律 16px —— iOS 对 <16px 的输入框会在聚焦时放大整页，
      而且缩放完不会自动缩回去。
   ============================================================ */

html.kb-open .modal-mask {
    inset: 0 0 var(--kbh) 0;
}

/* 遮罩缩了之后，弹窗跟着缩（原来 92vh 是按整屏算的） */
html.kb-open .modal {
    max-height: 100%;
}

/*
 * ★ 弹窗里滚动区的**底部留白**：KeyboardGuard 只能「把输入框往上顶」，
 *   顶到底了下面就没空间了 —— 而弹窗的最后几个字段本来就滚到底。
 *   补这一截，最后一个字段下面也留得出候选 / 提示的位置。
 */
html.kb-open .modal-body {
    padding-bottom: 56px;
}

/* 吐司也别落在键盘后面（转圈那层是全屏遮罩，只把它往上收，滚动照样居中） */
html.kb-open .loading {
    inset: 0 0 var(--kbh) 0;
}

html.kb-open .toast {
    bottom: calc(var(--kbh) + 16px);
}

@media (max-width: 900px) {
    html.kb-open .app {
        padding-bottom: var(--kbh);
    }

    /*
     * 输入 16px 会把格子顶大一点 —— 成绩录入那种成百上千的小格子格外明显，
     * 所以同步把它们的**内边距**收一点，格子尺寸跟原来保持一致；
     * 尺寸看着一样，但 iOS 不再自动放大整页。
     */
    /*
     * ★ 这里必须 `!important`：有的组件会单独给输入框定小字号
     *   （校历的「新增类型」就是 `.sc-kind-new { font-size: 13px }`），
     *   **类选择器比标签选择器优先**，会被它盖过去 —— 而 iOS 只要 <16px 就放大整页。
     *   手机上「不放大」比「某个下拉想小一点」重要，所以让这条压住所有组件。
     */
    input,
    select,
    textarea {
        font-size: 16px !important;
    }

    .score-in {
        padding: 2px 4px;
    }

    /*
     * ★ 候选里的名字要跟**输入框里的字一样大**（用户 2026-09-26 要求：上下两处字号一致）。
     *   桌面两边都是 14px，本来一致；手机把输入框抬到 16px 之后（防 iOS 放大整页），
     *   候选里还是 14px —— 看着就是「上面大、下面小」。
     */
    .t2-pick-nm {
        font-size: 16px;
    }

    /*
     * ★ 「今日几节课」在手机上**另起一行**（2026-09-27）。
     *   一行里要装「姓名 + 状态标签 + 四个彩色数字」，390px 宽根本放不下，
     *   硬挤会把姓名压没。所以手机上让这一行可换行、这块占满一整行靠右排，
     *   姓名和标签留在上面一行 —— 信息一点不少，就是行高一点。
     *   ⚠️ 2026-09-28 晚第 4 条：长句换成 .t2-day-nums 四个彩色数字，
     *      这条手机规则跟着改选择器（.t2-pick-day 留着兜底）。
     */
    .t2-pick-opt {
        flex-wrap: wrap;
    }

    .t2-pick-day,
    .t2-day-nums {
        flex: 1 1 100%;
        margin-left: 0;
        text-align: right;
        justify-content: flex-end;
    }

    /*
     * ★ 键盘弹起来时，底部那栏「取消 / 保存」先收起来（只在手机上）。
     *   手机上它是**通栏竖排**的两个按钮（约 67px 高），键盘一挡正好压在键盘上面 ——
     *   屏幕本来就只剩一半，再少一条按钮栏就没法看内容了（用户 2026-09-26 反馈）。
     *   收起键盘（点一下别处 / 键盘上的「完成」）它立刻回来，所以「保存」照样够得着；
     *   选中一位代课老师后也会自动收起键盘（见 view-substitute2.js 的 choose）。
     *   ⚠️ 「输入框在键盘上面 + 下面留候选空间」这两条不受影响（那是 --kbh / ROOM 管的）。
     */
    html.kb-open .modal-foot {
        display: none;
    }

    /*
     * ★ 接着上面那条：按钮栏收起后，它原来占的那 ~67px 也没了 ——
     *   弹窗最后一个输入框下面只剩上面那条 56px 留白，而 KeyboardGuard 要给候选留 150px
     *   （56 不够）→ 那一刻「滚到底也顶不上去」，格子就贴在键盘上沿（2026-09-26 实测：
     *   值日 / 成绩 / 设置里 7 个格子不合格）。
     *   所以手机 + 键盘弹起时把底部留白补到 150px（= ROOM）。
     */
    html.kb-open .modal-body {
        padding-bottom: 150px;
    }
}

@media (max-width: 700px) {
    /*
     * 键盘弹起来以后，候选列表不能往下伸到键盘后面 ——
     * 按 KeyboardGuard 留的那 150px 收一下，剩下的靠滚。
     */
    html.kb-open .t2-pick-list {
        max-height: 150px;
    }
}

/* ============================================================
   2026-09-26 晚 · 跨设备与低性能设备的收尾补丁
   ------------------------------------------------------------
   这一块是「全站体检」之后补的，四条都是**实测出来的**，不是拍脑袋：
   ① 手机上可点元素短边不足 30px（日期翻页的「今天 / « / ‹」只有 26px，
      复选框只有 18px），手指点不准；
   ② 全站小字号已就地分级提过：说明文字 ≥12px、纯装饰徽标 ≥11px
      （原来最小到 8.5px，校历的日期角标在手机上基本看不清）；
   ③ 手机上 `100vh` 会被浏览器地址栏吃掉一截，页面底部够不着；
   ④ 全屏毛玻璃（弹窗遮罩 + 转圈遮罩）在低端手机上 GPU 吃力，还会掉帧。
   ============================================================ */

/* ① 手机上：手指点得到（iOS / 安卓无障碍建议 44px，这里取 32px 兼顾信息密度） */
@media (max-width: 900px) {
    /*
     * ★★ 2026-10-05 用户第 1 条（手机截的作业 / 考勤页校历）：
     *   这条「按钮最小 32×32」原来是**一条龙写 height + width**，
     *   而日历里 **日期格是 `<button class="cal-cell">`、空白格是 `<div class="cal-cell blank">`** ——
     *   于是只有日期格被压到 32px，空白格没被压、按自己那套（40px）。
     *   结果是**同一份日历里有的行 40px、有的行 32px**：
     *   每月第一行 / 最后一行常有空白格 → 那两行明显更高，中间几行矮一截（用户截图）。
     *
     *   修法：拆成两条 —— **宽度照给**（日历格子也要点得着），
     *   高度**放行给日历自己那套规则**（.cal-cell / .hw-left 那几段本来就写好了
     *   46 / 42 / 40，而且空白格与日期格走的是同一批规则 → 必然一样高）。
     *   ⚠️ 与下面 `#view .sc-mn` 那条是同一个道理（年视图小月历也是被这条压坏的）。
     *   ⚠️ 别再把 min-height 合回上面那条 —— 合回去这个 bug 立刻复活。
     */
    #view button, #view .btn, #view [role="button"],
    .tool button, .seg button, .acts button, .modal-foot button {
        min-width: 32px;
    }
    #view button:not(.cal-cell), #view .btn, #view [role="button"],
    .tool button, .seg button, .acts button, .modal-foot button {
        min-height: 32px;
    }
    /*
     * ★★ 2026-10-04（用户报「按年视图仍然部分错位」的真凶）：
     *   上面那条「按钮最小 32×32」把**年视图的小月历格子**也一起算了进去 ——
     *   那些格子是 `<button class="sc-mn">`，所在的 7 列轨道只有 19.5px 宽，
     *   被强制成 32px 后比轨道还宽 → 越靠右的格子越往外挤、互相叠着 →
     *   整个小月历看着就是「错位」。它本来就点得着（只是小），不该吃这条触控规定。
     *   ⚠️ 与 `.sc-m-grid { repeat(7, minmax(0,1fr)) }` 是**配套的两条**：
     *      一条管「轨道能收缩」，一条管「格子别被最小尺寸顶破」；少任何一条都会散。
     */
    #view .sc-mn {
        min-width: 0;
        min-height: 20px;
    }
    /*
     * 系统默认复选框只有 13px，「只看有课的」这种开关经常点不中。
     * ⚠️ 只放大 checkbox，**别碰 radio**：代课的「事假 / 公假」是把 radio 藏起来
     *   做成大按钮的（见 .t2-leave-opt），给它强制 20×20 会在按钮旁边多出一块
     *   小方块，把「三块一样大」的排版顶散（2026-09-26 实测踩到）。
     */
    input[type="checkbox"] {
        width: 20px !important;
        height: 20px !important;
    }
    /* 输入框里的字保持 16px 以上（小于 16 iOS 会自动放大整页） */
    input, select, textarea { font-size: 16px !important; }
}

/*
 * ③ 100vh → 100dvh。
 *   dvh 是「动态视口高度」：地址栏收起 / 展开时跟着变，手机浏览器上
 *   不会出现「页面底部一截被地址栏盖住、怎么滚都够不着」的情况。
 *   老浏览器（不支持 dvh）不认这一条，自动沿用上面的 100vh —— 所以两条都留着。
 */
@supports (min-height: 100dvh) {
    .app { min-height: 100dvh; }
}

/*
 * ④ 毛玻璃降级：手机上关掉。
 *   全屏 backdrop-filter 要先把背后的画面截一遍再模糊，低端手机（尤其
 *   两三个遮罩叠在一起时）会明显掉帧、弹窗出现时卡一下。
 *   桌面保留（视觉层次更清楚），手机用纯色半透明，观感差别很小。
 */
@media (max-width: 900px) {
    .modal-mask, .loading { backdrop-filter: none !important; }
}

/*
 * ⑤ 横滑表格别把整页带跑：iOS Safari 里从屏幕边缘往右滑是「返回上一页」，
 *   老师横着看成绩表时很容易误触发、直接退出页面。
 *   overscroll-behavior-x: contain 让「滑到头就停在自己这一层」，不冒泡给页面。
 */
@media (max-width: 900px) {
    .table-wrap, .tt-wrap, .seat-stage, .t2-tt-wrap, .sc-term-wrap {
        overscroll-behavior-x: contain;
    }
}

/* ============================================================
   手机端 · 底部浮动标签栏（★ 2026-10-04 方案 E 用户拍板）
   ------------------------------------------------------------
   · 电脑端 display:none —— 导航照旧 ☰ 侧栏，桌面零变化；
   · ≤900px 浮在底部：毛玻璃胶囊、五等分（高频四页 + 更多）；
   · 「更多」= 拉出完整侧栏（复用 ☰ 的 toggleSidebar，零新逻辑）；
   · 权限与登录态由 app.js 的 renderNav() 管（没权限的 tab 隐藏、
     未登录整条隐藏）；
   · bottom 叠加 --kbh（键盘高度）+ 安全区 —— 键盘弹起整条上收；
   · z-index 50：压过内容（40），但低于侧栏遮罩（55）—— 抽屉拉开时
     遮罩盖住标签栏，点遮罩关抽屉的手感是对的。
   ============================================================ */
.tabbar {
    display: none;
}

.tab-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 44px;
    padding: 6px 2px;
    border: none;
    background: none;
    cursor: pointer;
    color: var(--c-text-2);
    font-size: 10.5px;
    font-weight: 500;
    -webkit-tap-highlight-color: transparent;
}

.tab-item .tab-ico,
.tab-item .tab-svg {
    width: 22px;
    height: 22px;
    display: block;
    object-fit: contain;
}

/* 「更多」用的是 index.html 内联 sprite 里的 #i-menu（跟顶栏 ☰ 同一个图形），
   fill=currentColor —— active 时跟着变蓝。 */
.tab-item .tab-svg {
    fill: currentColor;
}

.tab-item.active {
    color: var(--c-primary);
    font-weight: 700;
}

.tab-item[hidden] {
    display: none;
}

@media (max-width: 900px) {
    .tabbar {
        display: grid;
        grid-template-columns: repeat(5, minmax(0, 1fr));
        position: fixed;
        left: 10px;
        right: 10px;
        bottom: calc(10px + var(--kbh) + env(safe-area-inset-bottom));
        z-index: 50;
        padding: 4px 6px;
        border-radius: 22px;
        background: rgba(255, 255, 255, .85);
        -webkit-backdrop-filter: saturate(1.8) blur(24px);
        backdrop-filter: saturate(1.8) blur(24px);
        box-shadow: 0 6px 24px rgba(0, 0, 0, .16), inset 0 0 0 1px rgba(60, 60, 67, .08);
        /* ★ 下滑隐藏 / 上滑出现（app.js 的 scroll 监听切 .tab-hide） */
        transition: transform .26s cubic-bezier(.22, 1, .36, 1), opacity .2s;
    }

    /* 滑走了也别彻底消失 —— 留 6px 露头，手指从那儿上滑能拉回来 */
    .tabbar.tab-hide {
        transform: translateY(calc(100% - 6px));
        opacity: .35;
    }

    /* 内容底部留出标签栏的高度，最后一行不被盖住 */
    .content {
        padding-bottom: calc(104px + var(--kbh) + env(safe-area-inset-bottom));
    }
}

/* ============================================================
   校历页 · 卡片头（★ 2026-10-04 晚 用户第 4、5、6 条重做）
   ------------------------------------------------------------
   卡片头里现在**只剩学期标题**一个元素：
     · 「📅 校历」那个小标题删了（顶栏已经写着「校历」，重复）；
     · 两个「今天」（卡片头 + 日历导航行）都删了（用户：两个都删）；
     · 学期标题升级成正式标题 `.cal-term-title`（见上），整行居中显示。
   ⚠️ 随按钮 / 小灰字一起撤掉的旧样式：`.cal-today-m`、`.cal-card-head .hint`、
      `#scCal .sc-head [data-sc="today"]` —— 页面里已无元素引用，一并删掉，不留死样式。
   ============================================================ */
.cal-card-head {
    justify-content: center;
}

.cal-card-head h3 {
    flex: 0 0 auto;
}
