/* ============================
   十一智源移动端 - 主样式表
   设计风格：浅色、简洁、现代（视觉基线见文末「设计基线统一层」）

   适用范围：index.html 外壳 + pages/*.html（首页 / 总览 / 出勤 / 设置）+ 全局控件。
   登录页与注册页自成一套，样式在 css/login.css。

   ⚠️ 已移除页面（环境监测 / 班级监测 / 周工作计划 / 实时数据 / 旧版出勤按钮）的
      样式已**移出本文件**，留档在 css/legacy.css —— **没有任何页面加载它**。
      恢复旧页面时把需要的段落复制回来，且必须先按本文件末尾的「设计基线统一层」
      改造：那些样式仍是旧的蓝色风格（渐变色块、重阴影、旧圆角）。

   ⚠️ --card-line 是**一整条 border 简写**（1px solid var(--border)），不是颜色：
      只能写 border: var(--card-line);。写成 border: 1px solid var(--card-line)
      会展开成无效值 ⇒ 整条声明被丢弃 ⇒ 边框静默变 0px（不报错、不进控制台）。

   ⚠️ 滚动条全局隐藏（见下方同名段落）：只隐藏视觉，滚动能力不受影响。
      ::-webkit-scrollbar 的优先级按**宿主选择器**算，故 .content-area:: 会盖过 *::。

   ⚠️ border: 1.5px 在 Blink 里被向下取整成 1px（DPR 2 实测）⇒ 源码一律写 1px，
      让"写的"与"渲染的"一致。

   增删页面后重跑 node .workbuddy/tools/probes/scan-css-usage.js 重新盘点死代码
   （该脚本**不剥注释**：注释里写 .xxx 也会被算进类名总数，别当成脚本坏了）。
   ============================ */

/* 全局重置 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
    /* 归档 105：全站禁止文字选中 + 禁止长按/右键弹出系统菜单（复制/搜索/分享等"工作台"浮层）。
       覆盖 index.html 框架内经 app.js innerHTML 注入的全部 pages/*.html（属本文档子树）。
       仓库根其余独立顶层页（login/register/admin/identity/nfc-*/attendance-makeup）同引用本文件，
       故一并生效。 */
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

/* 输入框 / 文本域仍需可编辑、可选中已输入文字，否则会破坏登录与表单上报（safety/repair 等）。
   故仅对表单控件放开选择，页面展示文字依旧全站不可选。 */
input,
textarea,
[contenteditable] {
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    user-select: text;
    -webkit-touch-callout: default;
}

/* html 级的两件事（2026-09-19 移动端自适应）：
   ① `-webkit-text-size-adjust: 100%` —— 不写的话，iOS Safari 在**横屏**下会给
      正文自动放大字号（"text autosizing"），同一个页面竖屏/横屏两套字号，
      表现是「横过来字突然变大、布局被撑破」。100% 表示"别动我的字号"。
   ② 显式给 html 一个底色 —— `viewport-fit=cover` 之后页面会铺到刘海/圆角外，
      没给底色时那块区域会露出浏览器默认白，与 `.app-container` 的 --bg 对不上。 */
html {
    -webkit-text-size-adjust: 100%;
    /* 归档 049：禁止双击缩放（ pinch 由 viewport user-scalable=no 负责），
       同时消除移动端 300ms 点击延迟，点按钮更跟手 */
    touch-action: manipulation;
    background: var(--bg);
}

:root {
    --primary: #3B82F6;
    --primary-light: #DBEAFE;
    --primary-dark: #1E40AF;
    --primary-bg: #EFF6FF;
    --bg: #F0F4F8;
    --card-bg: #FFFFFF;
    --text-primary: #1E293B;
    --text-secondary: #64748B;
    --text-light: #94A3B8;
    --border: #E2E8F0;
    --shadow: 0 2px 8px rgba(59, 130, 246, 0.08);
    --shadow-hover: 0 4px 16px rgba(59, 130, 246, 0.15);
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    --radius-pill: 999px;
    --transition: all 0.3s ease;

    /* ===== 设计基线（与 css/login.css 同源，改这里即全局生效）=====
       2026-09-18 统一风格时新增，用来消灭"同一个东西有多套画法"：

       --brand-glow  品牌光晕。**替代蓝色渐变块**。
                     登录页上轮已确认：logo 自带蓝紫渐变，再垫一层蓝色色块
                     只会把两者糊在一起，还得额外套白底去"托" —— 色块 / 白底 / logo
                     三层叠加正是"丑"的来源。今后品牌区一律用这层极淡光晕。
       --card-line   卡片描边。与登录页输入框同一手法：浅色底上只靠投影的卡片
                     边界发虚，加一条发丝描边"面"才立得住。
                     ⚠️ 取 1px 而不是 1.5px —— 2026-09-18 真机实测（CDP，DPR 2）：
                     Blink 会把小数边框**取整**，`border: 1.5px` 的计算值同样是 1px
                     （登录页 .login-field 一直写 1.5px，量出来也是 1px）。
                     既然渲染结果一样，就按事实写，别让源码骗人。
       --radius-pill 胶囊圆角。分页控件统一用它（旧的"白卡 + 蓝色实心块"已废弃）。 */
    --brand-glow: radial-gradient(58% 76% at 50% 44%, #E4EFFF 0%, rgba(228, 239, 255, 0) 72%);
    --card-line: 1px solid var(--border);
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
    background: var(--bg);
    color: var(--text-primary);
    line-height: 1.6;
    min-height: 100vh;
    overflow: hidden;
    -webkit-font-smoothing: antialiased;
}

/* ============================
   滚动条：全局隐藏（所有页面一致）
   ============================
   本页是"移动端 H5"：真机上滚动条是**覆盖式**的，扫过就消失，本来就不占位。
   但在桌面 Chrome / Electron 里会画出实体滚动条（视口约 15px、`.content-area` 4px），
   又宽又始终可见，一眼就露馅。故统一隐藏。

   ⚠️ 只隐藏**视觉**，不影响**滚动能力**：触摸滑动、鼠标滚轮、键盘方向键 / PageUp，
      以及滚动锚定、`scrollIntoView`、`elementscroll` 事件全部照旧。
   ⚠️ 隐藏后桌面端少了"这里还能往下滚"的提示，靠的是移动端本就依赖的
      「底部内容被裁切」这一视觉线索 —— 这是移动端 H5 的通行做法。
   ⚠️ 覆盖范围必须包含 `html` / `body`：登录页、注册页由 `body.login-body{overflow:auto}`
      整页滚动，滚动条挂在视口上；主应用则由 `.content-area` 内部滚动。
      两处都要盖到，才算"所有页面"。

   为什么要用 `html, body, *` 三层而不是只写一个通配：
   `::-webkit-scrollbar` 的优先级按**宿主选择器**算。原先 `.content-area::-webkit-scrollbar`
   是 (0,1,0)，会盖过 `*::-webkit-scrollbar` 的 (0,0,0) —— 只写通配会"隐藏了但没完全隐藏"。
   所以那条 4px 的老规则已**就地删除**（见下方「滚动条美化」段），此处才用通配收口。 */
html,
body,
* {
    scrollbar-width: none;      /* 标准属性：Firefox 64+ / Chromium 121+ */
    -ms-overflow-style: none;   /* 旧 Edge / IE11 */
}

html::-webkit-scrollbar,
body::-webkit-scrollbar,
*::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;
}

/* ============================
   应用容器
   ============================ */
.app-container {
    display: flex;
    flex-direction: column;
    /* 🔴 100vh 在移动浏览器里**不可靠**：它恒等于「地址栏收起时」的最大可视高度，
       于是地址栏可见时，底部那一截（正好是固定底栏）会被顶到屏幕外。
       dvh（dynamic viewport height）跟着可视高度实时变化，才是移动端该用的单位。
       写法顺序有意义：先 100vh 给不认 dvh 的老内核兜底，再 100dvh 覆盖它 ——
       解析不了 dvh 的浏览器会整条丢弃第二条，自动留在 100vh。 */
    height: 100vh;
    height: 100dvh;
    max-width: 428px;
    margin: 0 auto;
    background: var(--bg);
    position: relative;
}

/* ============================
   顶部标题栏（固定置顶）
   ============================
   2026-09-18 统一风格：由「蓝色渐变色块 + 白色圆角衬底 logo + 白色大字标题」
   改为「白底 + 一条发丝分割线 + 横版 logo 直接落在浅色上 + 深色标题」，
   与登录 / 注册页的品牌区同一套语言。

   为什么去掉白色衬底：横版 logo（logo-bian.png）自带蓝紫渐变与透明背景，
   压在蓝色色块上会糊在一起，才不得不套一层白底去"托" —— 色块 / 白底 / logo
   三层叠加，正是登录页上轮被判"丑"的那个结构，顶栏这里同样不要。 */
.top-bar {
    background: var(--card-bg);
    color: var(--text-primary);
    padding: 14px 20px;
    /* 刘海屏：顶栏要顶到状态栏 / 挖孔下面，否则 logo 与标题会被摄像头挖孔盖住。
       `viewport-fit=cover` 之后这两个环境变量才有非 0 值（见各页 <meta name="viewport">）；
       非刘海设备上 env() 恒为 0 ⇒ 计算结果回到原来的 14px，既有观感不变。 */
    padding-top: calc(14px + env(safe-area-inset-top, 0px));
    display: flex;
    align-items: center;
    gap: 10px;
    /* 由投影改为发丝线：浅色界面上投影会显脏，且与底部导航同一手法 */
    border-bottom: 1px solid var(--border);
    position: relative;
    z-index: 100;
    flex-shrink: 0;
}

/* 横版 logo —— 与 css/login.css 的 .login-logo 同一套裁切规则：
   图形在方形画布上下留白很大，直接按宽度缩放会保留透明边、显得又小又飘，
   故用 aspect-ratio + object-fit: cover 把透明边裁掉。
   ⚠️ height: auto 不能省 —— <img> 标签上的 height="640" 是"表现性提示"，
      会以 height:640px 参与层叠，把 aspect-ratio 挤掉（实测渲染盒会变成
      44×640，图形被裁成一条）。写 height:auto 才能让 aspect-ratio 生效。 */
.top-bar__logo {
    width: 44px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
}

.top-bar__logo img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 631 / 412;
    object-fit: cover;
    object-position: center 40%;
}

.top-bar__title {
    font-size: 17px;
    font-weight: 600;
    color: var(--text-primary);
    letter-spacing: 0.12em;
}

/* ============================
   中间内容区（可滚动）
   ============================ */
/* 🆕 2026-09-26：禁止横向 overscroll 触发系统级返回（避免左滑把整个主框架推走）；
   touch-action: pan-y 让竖向滚动交给系统、横向手势交给我们自己的返回逻辑 */
html, body { overscroll-behavior-x: none; }
.content-area {
    position: relative;   /* 🆕 归档 065：供 .pull-indicator 绝对定位 */
    flex: 1;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    overscroll-behavior-x: none;
    padding: 20px 16px;
    /* 底部留白 = 固定底栏的实高 + 安全区。
       ⚠️ 底栏（.bottom-nav）是 position:fixed、不参与本区的流式布局，所以这段留白是
       它唯一的「不被压住」保障。底栏实高 ≈ padding 12×2 + 图标 22 + 间隔 4 + 文字 15 ≈ 81px，
       取 96px 留余量；再叠加 iPhone 那条 home indicator（约 34px），
       否则最后一屏内容会被底栏或小白条盖住。 */
    padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
}

.page-content {
    display: none;
}

.page-content:not(.hidden) {
    display: block;
    animation: fadeIn 0.3s ease;
}

/* 🔴 归档 `095d`：入场动画**不再用 transform 做位移**。
   原因：`.page-content` 上只要出现非 none 的 transform，它就会同时成为
   containing block 与层叠上下文 —— 页面内所有 `position: fixed` 的东西
   （底部弹层 `.sm-sheet`、`.reason-modal` 等）都会被"困"在这一层里定位，
   且 z-index 被关进该上下文，反被底栏（`.bottom-nav`，z-index:100）盖住。
   真机实证：园长手机上弹层动作栏被底栏遮挡、关闭按钮落在视口外。
   本地/无头 Chrome 因动画结束即清掉 transform 而测不出（见 095d 的 ⑤ 号断言）。
   ⇒ 只保留 opacity 淡入，不再引入 transform。 */
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.hidden {
    display: none !important;
}

/* ============================
   占位页面
   ============================ */
.page-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 80px 20px;
    color: var(--text-light);
}

.page-placeholder i {
    font-size: 64px;
    margin-bottom: 16px;
    opacity: 0.3;
    color: var(--primary);
}

.page-placeholder p {
    font-size: 15px;
    font-weight: 500;
}

/* ============================
   统计图表卡片（总览页：饼图 / 树图 / 矩形树图）
   2026-09-18 新增：接入本地 vendor 的 ECharts。
   容器必须给定高度，否则 ECharts 初始化得到 0 高、图表不显示。
   ============================ */
.chart-card {
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    padding: 14px 14px 10px;
    margin-bottom: 16px;
}

.chart-card__title {
    font-size: 14px;
    font-weight: 500;
    color: var(--text-primary);
    margin-bottom: 8px;
    padding-left: 8px;
    border-left: 3px solid var(--primary);
}

/* ECharts 挂载点：必须显式高度（移动端窄屏统一 260px）。
   ⚠️ 树状图(orient:LR) 在极窄屏可能横向拥挤，真机验证后再调 initialTreeDepth。 */
.chart-box {
    width: 100%;
    height: 260px;
}

/* ============================
   监测页面 - 标签页
   ============================ */

/* ✅ 本段原属**已移除的「监测」页面的分页控件**（2026-09-18 盘点：无任何 HTML 引用）；
   现被 **「总览」页（pages/overview.html）复用**，已不再是遗留样式。
   下面的旧值（白卡片 + 蓝色实心选中块）仍被文末「设计基线统一层」④ 覆盖成胶囊 ——
   旧值保留原样不改：一个属性只应有一处定义，要调外观请去统一层。 */
.tab-bar {
    display: flex;
    background: var(--card-bg);
    border-radius: var(--radius-md);
    padding: 4px;
    margin-bottom: 20px;
    box-shadow: var(--shadow);
}

.tab-btn {
    flex: 1;
    border: none;
    background: none;
    padding: 12px 16px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-secondary);
    cursor: pointer;
    border-radius: var(--radius-sm);
    transition: var(--transition);
}

.tab-btn--active {
    background: var(--primary);
    color: #fff;
    box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

.tab-btn:active {
    transform: scale(0.97);
}

/* 面板显隐：靠 **.hidden 类**切换 —— 总览页（pages/overview.html + js/overview.js）用的就是这条。
   ⚠️ 不能用 hidden 属性：作者样式优先级恒高于浏览器默认样式表，
   带 hidden 属性的面板仍会被下面这条 `:not(.hidden)` 命中而显示出来。
   注意文件下方「实时页面」段还有一条**一模一样的 .tab-content{display:none}**（历史重复），
   那段的 .tab-content.active 已无调用点 —— 要改显隐规则，改这里。 */
.tab-content {
    display: none;
}

.tab-content:not(.hidden) {
    display: block;
    animation: fadeIn 0.3s ease;
}

/* ============================
   底部功能栏（固定置底）
   ============================ */
.bottom-nav {
    position: fixed;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 100%;
    max-width: 428px;
    background: var(--card-bg);
    display: flex;
    justify-content: space-around;
    padding: 12px 0;
    /* 🔴 这里的 second 参数**不是**「安全区为 0 时的默认值」。
       `env(name, fallback)` 的 fallback 只在浏览器**压根不认识 env()** 时生效；
       而任何支持 env() 的浏览器（含所有在用的安卓机、桌面 Chrome）都会把
       `safe-area-inset-bottom` 解析成 **0px** ⇒ 原来的 `env(…, 12px)` 实际算出 0px，
       底栏的文字直接贴着屏幕下边缘（非刘海机上的实际观感）。
       正确写法是把基准值放进 calc，环境变量只当增量。 */
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    /* 与顶栏对称：发丝线分区，不再用投影 */
    border-top: 1px solid var(--border);
    z-index: 100;
    flex-shrink: 0;
}

.nav-btn {
    flex: 1;
    border: none;
    background: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 8px 0;
    cursor: pointer;
    color: var(--text-light);
    font-size: 11px;
    transition: var(--transition);
    position: relative;
}

.nav-btn i {
    font-size: 22px;
    transition: var(--transition);
}

.nav-btn--active {
    color: var(--primary);
}

.nav-btn--active i {
    transform: translateY(-2px);
}

.nav-btn--active::before {
    content: '';
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 24px;
    height: 3px;
    background: var(--primary);
    border-radius: 0 0 4px 4px;
}

.nav-btn:active {
    transform: scale(0.92);
}

/* ============================
   响应式适配
   ============================ */
/* 小屏手机（< 375px） */
@media (max-width: 374px) {
    .top-bar {
        padding: 12px 16px;
    }
    
    /* ⚠️ 只给宽度：高度由 .top-bar__logo img 的 aspect-ratio 推出来。
       同时写 width + height 会顶掉 aspect-ratio → logo 被压成方形。 */
    .top-bar__logo {
        width: 38px;
    }

    .top-bar__title {
        font-size: 16px;
    }
}

/* 大屏手机/平板（> 428px） */
@media (min-width: 429px) {
    .app-container {
        box-shadow: 0 0 40px rgba(0, 0, 0, 0.1);
    }
    
    .bottom-nav {
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    }
}

/* 横屏适配 */
@media (max-height: 500px) {
    .top-bar {
        padding: 10px 16px;
    }
    
    /* 横屏：同样只给宽度（理由见上面 374px 那段） */
    .top-bar__logo {
        width: 34px;
    }

    .top-bar__title {
        font-size: 15px;
    }
    
    .content-area {
        padding: 12px 16px;
        padding-bottom: 70px;
    }
}

/* ============================
   滚动条美化 —— 已废弃（2026-09-18）
   ============================
   原先这里给 `.content-area` 做了一条 4px 的细滚动条（track 透明 + thumb 用 --border）。
   现按需求**所有页面一律不显示滚动条**，本段已整体移除 —— 保留这段说明是因为：
   ① `.content-area::-webkit-scrollbar` 的优先级是 (0,1,0)，**会盖过文件上方全局段里
      `*::-webkit-scrollbar` 的 (0,0,0)**。留一个 `width: 4px` 在这儿，全局隐藏就失效。
   ② 若日后想改回"细滚动条"，改全局段一处即可，不要在 .content-area 上另起一套 ——
      否则同一个东西又是两套画法（本项目的老毛病）。
   全局规则见文件上方「滚动条：全局隐藏（所有页面一致）」。 */

/* ============================
   出勤上报页面
   ============================ */

/* ⚠️ 遗留言：本段属于**已移除的「出勤上报」页面（旧版：纵向班级按钮 + 学生网格，已被 .att-* 取代）**（2026-09-18 盘点：无任何 HTML 引用）。
   保留以备恢复，但视觉仍是旧的蓝色风格 —— 恢复前请先按文末「设计基线统一层」改造，
   否则会把老风格带回来。 */
.page-attendance {
    padding: 0;
}


.section-title {
    font-size: 20px;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 8px 0;
}



/* ============================
   设置页面
   ------------------------------------
   本节样式来自重置前的旧设置页；旧页面删除后一度无人引用。
   设置页（pages/settings.html）**直接复用本节**，并做三处适配：
     ① .settings-header 做成通栏：抵消 .content-area 的 20px / 16px 内边距
     ② .settings-content 不再自带左右内边距（否则与 .content-area 叠加成双倍缩进）
     ③ 新增「待开发项」与「禁用开关」两处样式 —— 本页除退出登录外不实现功能，
        开关一律 disabled，避免「能拨动但不生效」的误导性交互

   2026-09-18 统一风格：头部由「蓝色渐变色块 + 白色圆形衬底 logo + 白色 22px 标题」
   改为「浅色底 + 品牌光晕 + 横版 logo + 深色标题」。
   原来的三层叠加（蓝块 / 白圆 / logo）与登录页上轮被判"丑"的结构完全同款 ——
   logo 自带蓝紫渐变，压在蓝块上会糊在一起，只好再套白圆去"托"。
   ============================ */
.page-settings {
    padding: 0;
}

/* ============================================================
   ICP 备案号页脚（登录页 .login-footer 与设置页 .settings-footer 共用）
   ------------------------------------------------------------
   按《互联网信息服务管理办法》要求：备案号须在页面底部展示，
   且通常链接到工信部备案系统（beian.miit.gov.cn）。
   两页共用 .icp-beian 以保证字号 / 颜色 / 间距一致。 */
.settings-footer {
    padding: 18px 20px calc(18px + env(safe-area-inset-bottom, 0px));
    text-align: center;
    font-size: 12px;
    color: var(--text-light);
}

.icp-beian {
    margin: 4px 0 0 0;
    font-size: 12px;
    line-height: 1.6;
}

.icp-beian a {
    color: var(--text-light);
    text-decoration: none;
}

.icp-beian a:active {
    color: var(--text-secondary);
}

/* 园所控制页（pages/school.html）：整页是若干分组列表，没有通栏品牌区，
   故左右内边距沿用 .content-area 的、只补一个底部留白（与 .settings-content 同一处理）。
   ⚠️ 显式写出来是**故意**的：本项目踩过「类名在用、CSS 却没有任何规则」的坑
   （.identity-actions —— 两个按钮几乎贴在一起），这里不留那种空档。 */
.page-school {
    padding-bottom: 20px;
}

/* ============================================================
   园所控制页 · 身份绑定审核（pages/school.html，2026-09-20）
   ------------------------------------------------------------
   一条待审申请一张卡。为什么**不复用** .identity-teacher 那套：
   那套是「列表项 + 右侧一颗小按钮」的两列布局，而这里要靠底部
   两颗等宽按钮做「通过 / 驳回」—— 两个不可逆动作挤在右侧，误触代价太高。 */
.school-bind__item {
    display: block;
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    background: var(--card-bg);
    padding: 12px 14px;
    margin-bottom: 10px;
}

.school-bind__who {
    display: block;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}

.school-bind__meta {
    display: block;
    margin-top: 3px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-secondary);
}

.school-bind__actions {
    display: flex;
    gap: 10px;
    margin-top: 12px;
}

/* 两颗按钮**等宽**：对称性不是审美偏好，是在告诉用户这是同一级别的两个决定 */
.school-bind__actions .identity-btn {
    flex: 1;
    width: auto;
    margin-top: 0;
}

.settings-header {
    text-align: center;
    padding: 34px 20px 26px;
    /* 与登录 / 注册页品牌区同一套：极淡品牌光晕，不用色块 */
    background: var(--brand-glow);
    /* ① 通栏：抵消 .content-area 的内边距，让品牌区横跨整个内容区。
       负值必须与 .content-area 的 padding 保持一致（当前 20px 16px）。 */
    margin: -20px -16px 22px;
}

/* 横版 logo —— 与 .login-logo / .top-bar__logo 同一套裁切规则。
   ⚠️ height: auto 不能省（标签上的 height 属性会顶掉 aspect-ratio）。 */
.settings-logo {
    width: 96px;
    margin: 0 auto 14px;
}

.settings-logo img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 631 / 412;
    object-fit: cover;
    object-position: center 40%;
}

.school-name {
    font-size: 19px;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 6px 0;
    letter-spacing: 0.06em;
}

.app-version {
    font-size: 12px;
    color: var(--text-light);
    margin: 0;
}

.settings-content {
    /* ② 左右内边距由 .content-area 统一提供，此处不再叠加 */
    padding: 0;
    padding-bottom: 20px;
}

.settings-section {
    margin-bottom: 20px;
}

/* 字号 / 字重 / 颜色已由文末「设计基线统一层」② 统一
   （与首页区块标题一致：15px / 700 / 深色 + 左侧品牌色条），这里只保留本页特有的外边距。
   原先这里是 16px / 600 / 灰色 —— 同一个"区块标题"项目里有三套画法，本轮已合并。 */
.settings-section .section-title {
    margin: 0 0 12px 0;
}


.settings-list {
    background: var(--card-bg);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.setting-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border);
    transition: var(--transition);
}

.setting-item:last-child {
    border-bottom: none;
}

/* 🔴 hidden 属性兜底（归档 `070`）：`.setting-item` 上面那条 `display:flex`
   会**盖掉**浏览器默认的 `[hidden]{display:none}` —— 于是「园所账号不显示当前账号/当前身份」
   就变成隐藏失败、两行空壳照旧在页面上。写死 `display:none` 才真的不显示。
   ⚠️ 同款坑本项目已踩过 5 次（.reason-modal / .identity-page / .login-panel / .ov-ai /
   .notif-banner 都各留了一条自己的兜底）—— 之所以没并成一条全局
   `[hidden]{display:none!important}`，是因为那会改变全站渲染，需单独评估后再做。
   ⇒ 本次只补**当前需要**的这一条，范围最小。 */
.setting-item[hidden] {
    display: none;
}

.setting-item:active {
    background: var(--bg);
}

.setting-info {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 1;
}

.setting-info i {
    font-size: 18px;
    color: var(--primary);
    width: 24px;
    text-align: center;
    flex-shrink: 0;
}

.setting-info span {
    font-size: 15px;
    color: var(--text-primary);
    font-weight: 500;
}

.setting-value {
    font-size: 14px;
    color: var(--text-secondary);
}

/* 「切换身份」这类**可点的**条目：沿用 .setting-value 的字号/位置，
   但去掉 <button> 的默认外观，并改成品牌色 + 右箭头语义（在此只做颜色与手感）。
   刻意不新造一套 .setting-action —— 同一个位置出现两种字型就是"不统一"的开始。 */
.setting-value--link {
    border: none;
    background: none;
    padding: 0;
    font-family: inherit;
    color: var(--primary);
    font-weight: 600;
    cursor: pointer;
}

.setting-value--link:active { opacity: 0.6; }

.setting-item > i {
    font-size: 14px;
    color: var(--text-light);
}

/* 开关按钮样式 */
.switch {
    position: relative;
    display: inline-block;
    width: 48px;
    height: 28px;
    flex-shrink: 0;
}

.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #CBD5E1;
    transition: var(--transition);
    border-radius: 28px;
}

.slider::before {
    position: absolute;
    content: "";
    height: 22px;
    width: 22px;
    left: 3px;
    bottom: 3px;
    background-color: white;
    transition: var(--transition);
    border-radius: 50%;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.switch input:checked + .slider {
    background-color: var(--primary);
}

.switch input:checked + .slider::before {
    transform: translateX(20px);
}

/* ③-a 待开发项：**整行灰显**，不响应点击（本页除退出登录外不实现功能）。
   2026-09-20 起消息通知 / 深色模式也走这一套 ——
   只在 JS 里 disabled 是不够的：用户看不见「禁用」，只会以为开关坏了。 */
.setting-item--pending {
    cursor: default;
}

.setting-item--pending:active {
    background: transparent;
}

.setting-item--pending .setting-value {
    color: var(--text-light);
}

/* 图标与标题一并压灰：只灰开关的话，视觉重心还在左边那一行字上，
   「这项不可用」的第一眼印象就没了。 */
.setting-item--pending .setting-info i,
.setting-item--pending .setting-info span {
    color: var(--text-light);
}

/* 「暂不可用」四个字：贴在开关左侧，给灰显一个**文字解释**。 */
.setting-value--muted {
    font-size: 13px;
    margin-right: 10px;
    flex-shrink: 0;
}

/* ③-b 开关暂不可用：灰显 + 不可点击，避免「能拨动但不生效」的误导性交互 */
.switch input:disabled + .slider {
    opacity: 0.45;
    cursor: default;
}

/* 退出登录按钮 */
.logout-btn {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 15px 20px;
    border: none;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    color: #DC2626;
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: var(--shadow);
    transition: var(--transition);
}

.logout-btn i {
    font-size: 17px;
}

.logout-btn:active {
    transform: scale(0.98);
    background: #FEF2F2;
}

/* 捐助条目：稍稍强调（主色图标 + 主色链接），但不做成实心主按钮 ——
   它不是设置页的主操作，抢走注意力反而显得像在催钱 */
.setting-item--donate .setting-info i {
    color: var(--primary);
}

.setting-item--donate .setting-value--link {
    color: var(--primary);
}

/* ============================
   设置页 - 通用底部弹层（修改密码 / 关于 / 捐助 共用）

   与 .reason-modal 同一套观感（底部升起 + 半透明遮罩 + 圆角面板），
   刻意不复用后者：那是「原因采集」专用（字典选项 + 具体情况），
   这里是「表单 / 图文展示」，硬套只会把两边都改歪。
   ============================ */
.settings-sheet {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

.settings-sheet__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}

.settings-sheet__panel {
    position: relative;
    width: 100%;
    max-width: 520px;
    max-height: 86vh;
    min-width: 0;                 /* 子内容不会把弹层撑破到屏幕外 */
    display: flex;
    flex-direction: column;
    padding: 18px 18px calc(18px + env(safe-area-inset-bottom));
    background: var(--card-bg);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: 0 -8px 24px rgba(15, 23, 42, 0.12);
    animation: settings-sheet-up 0.22s ease;
}

@keyframes settings-sheet-up {
    from { transform: translateY(12px); opacity: 0.6; }
    to   { transform: translateY(0);    opacity: 1; }
}

.settings-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 14px;
}

.settings-sheet__title {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
}

.settings-sheet__close {
    padding: 4px 6px;
    border: none;
    background: transparent;
    color: var(--text-light);
    font-size: 16px;
    cursor: pointer;
}

.settings-sheet__body {
    overflow-y: auto;
    min-width: 0;                 /* 内容即使溢出也不会撑宽整个弹层 */
}

/* 弹层内 time/date 输入框去掉原生控件（时钟/日历/清除按钮），避免 intrinsic 宽度撑破 flex/grid */
.settings-sheet__body input[type="time"],
.settings-sheet__body input[type="date"] {
    min-width: 0;
    max-width: 100%;
    -webkit-appearance: none;
    appearance: none;
}
.settings-sheet__body input[type="time"]::-webkit-clear-button,
.settings-sheet__body input[type="time"]::-webkit-inner-spin-button,
.settings-sheet__body input[type="date"]::-webkit-calendar-picker-indicator,
.settings-sheet__body input[type="date"]::-webkit-clear-button {
    display: none;
}

.settings-sheet__msg {
    margin: 10px 0 0;
    font-size: 13px;
    color: #DC2626;
}

/* 🆕 归档 062：iOS 描述文件安装引导（安装到主屏幕） */
.install-guide {
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-primary);
}
.install-guide p {
    margin: 0 0 10px;
}
.install-guide ol {
    margin: 0 0 12px;
    padding-left: 20px;
}
.install-guide ol li {
    margin-bottom: 6px;
}
.install-guide b {
    color: var(--primary);
    font-weight: 600;
}
.install-guide__tip {
    font-size: 12px;
    color: var(--text-light);
}

.settings-sheet__actions {
    display: flex;
    gap: 10px;
    margin-top: 16px;
}

.settings-sheet__btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 13px 10px;
    border: var(--card-line);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}

/* 主按钮沿用全站基线：渐变蓝 + 白字 600（已在 verify-theme-consistency.js 的
   GRADIENT_OK 白名单里登记 —— 蓝色渐变只许出现在主按钮上） */
.settings-sheet__btn--primary {
    border: none;
    color: #fff;
    background: linear-gradient(135deg, var(--primary), var(--primary-dark));
}

.settings-sheet__btn:active {
    transform: scale(0.98);
}

.settings-sheet__btn--primary:disabled {
    opacity: 0.5;
    cursor: default;
}

.settings-sheet [hidden] {
    display: none;
}

/* ============================
   App 自升级弹窗（Android 壳，js/app-update.js）

   复用上面的 .settings-sheet 骨架与 .settings-sheet__btn 按钮 —— 只补
   **弹窗内容**的排版，不新造一套弹层。理由：这套 UI 随时可能在任意页面
   弹出，样式越集中越不容易出现「某个页面漏加载了它的 CSS」。
   ============================ */
.update-version {
    margin: 0 0 6px;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.update-meta {
    margin: 0 0 12px;
    font-size: 12px;
    color: var(--text-light);
}

/* 更新说明：逐条列举。文案来自 update.json 的 notes（由出包工具生成） */
.update-notes {
    margin: 0 0 14px;
    padding-left: 20px;
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-secondary);
}

.update-notes li {
    margin-bottom: 4px;
}

.update-tip {
    margin: 0;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-light);
}

/* 出错态：下载地址无效 / 启动下载失败时，把提示染成警示色。
   刻意只换颜色、不换结构 —— 出错时最怕"整个弹窗换了副样子"，
   老师会以为是另一个界面、不敢再点。 */
.update-tip[data-update-error="1"] {
    color: #DC2626;
}

/* 弹层内的表单字段（修改密码） */
.settings-field {
    display: block;
    margin-bottom: 14px;
}

.settings-field__label {
    display: block;
    margin-bottom: 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.settings-field__input {
    width: 100%;
    padding: 12px 14px;
    border: var(--card-line);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    color: var(--text-primary);
    font-family: inherit;
    font-size: 15px;
}

.settings-field__input:focus {
    outline: none;
    border-color: var(--primary);
}

/* 关于十一智源 */
.settings-about__lead {
    margin: 0 0 12px;
    font-size: 16px;
    font-weight: 700;
    color: var(--primary);
}

.settings-about__list {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 8px 14px;
    margin: 0;
    font-size: 14px;
}

.settings-about__list dt {
    color: var(--text-secondary);
}

.settings-about__list dd {
    margin: 0;
    color: var(--text-primary);
    word-break: break-all;
}

.settings-about__note {
    margin: 14px 0 0;
    font-size: 12px;
    color: var(--text-light);
}

/* 捐助开发 */
.settings-donate__text {
    margin: 0 0 14px;
    font-size: 14px;
    line-height: 1.7;
    color: var(--text-secondary);
}

.settings-donate__tabs {
    display: flex;
    gap: 8px;
    margin-bottom: 14px;
}

.settings-donate__tab {
    flex: 1;
    padding: 10px;
    border: var(--card-line);
    border-radius: var(--radius-pill);
    background: var(--card-bg);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
}

.settings-donate__tab.is-active {
    border-color: var(--primary);
    background: var(--primary-bg);
    color: var(--primary);
}

.settings-donate__qr {
    display: flex;
    justify-content: center;
    padding: 8px 0 4px;
}

.settings-donate__qr img {
    width: 200px;
    max-width: 70vw;
    border: var(--card-line);
    border-radius: var(--radius-md);
}

.settings-donate__hint {
    margin: 10px 0 0;
    font-size: 13px;
    text-align: center;
    color: var(--text-primary);
}

.settings-donate__note {
    margin: 8px 0 0;
    font-size: 12px;
    text-align: center;
    color: var(--text-light);
}

/* ============================
   首页 - 用户信息区
   ============================ */
.home-profile {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px;
    margin-bottom: 20px;
    background: var(--card-bg);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}


.home-profile__info {
    flex: 1;
    min-width: 0;
}

.home-profile__greeting {
    margin: 0 0 4px 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 🆕 权限组 · 班级管理范围（归档 `041`）
   如「办公室 · 全部班级」/「教职工 · 待审核」。
   🔴 三段式：组名（主色）· 分隔符（弱）· 范围（次色）—— 让「归属」与「范围」两件事
      在视觉上一眼分开，不用读文字就能分辨哪半边在说什么。 */
.home-profile__scope {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 6px 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.home-profile__scope .scope-dot {
    color: var(--text-light);
    font-weight: 400;
}

.home-profile__scope .scope-range {
    font-weight: 500;
    color: var(--text-secondary);
}

/* 「待审核」是个**状态**而不是范围，配色要区别于正常范围（琥珀色 = 需要留意） */
.home-profile__scope.is-pending .scope-range {
    color: var(--amber, #d97706);
}

/* 🆕 右侧本园 LOGO（归档 `041`）
   🔑 固定 60px 且 `object-fit: contain`：园所 logo 长宽比千差万别，
      `cover` 会把窄长 logo 裁掉，contain + 白色衬底能容下任意比例。
   ⚠️ 衬底用白色而非主色：logo 多为深色图形，落在主色块上容易糊成一片。 */
.home-profile__brand {
    flex: 0 0 60px;
    width: 60px;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
    background: #fff;
    box-shadow: 0 1px 4px rgba(15, 23, 42, 0.08);
    overflow: hidden;
}

.home-profile__logo {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* 「当前身份（角色 · 姓名 · 编号）」一行已于归档 `046` 按需求从首页移除 ——
   顶部卡里的「权限组 · 班级范围」已表达归属信息；样式一并清掉，避免留死代码。 */

.home-profile__meta {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 2px 0;
    font-size: 12px;
    color: var(--text-secondary);
}

.home-profile__meta i {
    font-size: 12px;
    color: var(--primary);
}

.home-profile__date {
    margin: 0;
    font-size: 12px;
    color: var(--text-light);
}

/* 当前登录账号（脱敏手机号），未登录时不渲染文字故不占高度 */
.home-profile__account {
    margin: 2px 0 0 0;
    font-size: 12px;
    color: var(--text-light);
}


/* ============================
   首页 - 区块标题
   ============================ */
.home-section {
    margin-bottom: 20px;
}

.home-section__title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px 0;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.home-section__title::before {
    content: '';
    width: 3px;
    height: 14px;
    border-radius: 2px;
    background: var(--primary);
}

/* ============================
   首页 - 快捷功能
   ============================ */
.home-quick {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}

.home-quick__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 16px 14px;
    border: none;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    box-shadow: var(--shadow);
    cursor: pointer;
    transition: var(--transition);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    text-align: left;
}

.home-quick__item i {
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    font-size: 17px;
    background: var(--primary-bg);
    color: var(--primary);
}

.home-quick__item:active {
    transform: scale(0.97);
    box-shadow: var(--shadow-hover);
}

/* 🆕 「开发中」角标（归档 `041`）
   需求：安全上报 / 维修上报**只有按钮、没有页面**，右上角要挂一个「开发中」标签。
   🔑 为什么用角标而不是把整块置灰：置灰 = 「你有这个功能但不能用」，
      而这里的真实语义是「**功能还没做出来**」—— 角标 + 可点击（弹提示）更诚实。
   ⚠️ 角标必须 `position: absolute` 且父级 `position: relative`：
      直接放进 flex 流里会把按钮文字挤变形。 */
.home-quick__item--wip {
    position: relative;
}

.home-quick__item--wip::after {
    content: '开发中';
    position: absolute;
    top: -6px;
    right: -4px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--text-light);
    color: #fff;
    font-size: 10px;
    font-weight: 600;
    line-height: 1.6;
    letter-spacing: 0.2px;
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.18);
    pointer-events: none;          /* 角标不吞点击 —— 点了仍然弹「开发中」提示 */
}

/* ============================
   首页 - 入口色板
   按钮的底色由 data-tone 决定，在 QUICK_ACTIONS 里指定 tone 即可，
   新增按钮不需要改 CSS。未指定时回落到默认主题蓝。
   ============================ */
.home-quick__item[data-tone="teal"] i   { background: #E1F5EE; color: #0F6E56; }
.home-quick__item[data-tone="amber"] i  { background: #FAEEDA; color: #854F0B; }
.home-quick__item[data-tone="red"] i    { background: #FCEBEB; color: #A32D2D; }
.home-quick__item[data-tone="blue"] i   { background: #E6F1FB; color: #185FA5; }
.home-quick__item[data-tone="purple"] i { background: #EEEDFE; color: #3C3489; }
.home-quick__item[data-tone="green"] i  { background: #EAF3DE; color: #3B6D11; }
.home-quick__item[data-tone="pink"] i   { background: #FBEAF0; color: #993556; }
.home-quick__item[data-tone="gray"] i   { background: #F1EFE8; color: #5F5E5A; }

/* 按钮多于 4 个时收窄为 3 列，图标上、文字下 */
.home-quick--dense {
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.home-quick--dense .home-quick__item {
    flex-direction: column;
    gap: 8px;
    padding: 14px 6px;
    font-size: 12px;
    text-align: center;
}

.home-quick--dense .home-quick__item i {
    width: 34px;
    height: 34px;
    font-size: 15px;
}

/* ============================
   首页 - 本周工作计划
   ============================ */
.home-plan {
    background: var(--card-bg);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.home-plan__day {
    display: flex;
    gap: 12px;
    padding: 13px 16px;
    border-bottom: 1px solid var(--border);
}

.home-plan__day:last-child {
    border-bottom: none;
}

/* 今天：浅底 + 左侧色条 */
.home-plan__day--today {
    background: var(--primary-bg);
    box-shadow: inset 3px 0 0 var(--primary);
}

.home-plan__date {
    width: 44px;
    flex-shrink: 0;
    text-align: center;
}

.home-plan__weekday {
    display: block;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-primary);
}

.home-plan__day--today .home-plan__weekday {
    color: var(--primary);
}

.home-plan__md {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: var(--text-light);
}

.home-plan__items {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.home-plan__item {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-primary);
}

.home-plan__time {
    flex-shrink: 0;
    padding: 1px 7px;
    border-radius: 10px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: 11px;
    font-weight: 600;
}

.home-plan__day--today .home-plan__time {
    background: #fff;
}

.home-plan__text {
    flex: 1;
    min-width: 0;
}

.home-plan__empty {
    font-size: 13px;
    color: var(--text-light);
}

/* —— 085：接入 /api/workplan/current 后新增的区块 ——
   二轮调整（2026-09-26）：range 起止日期区间行已按需求移除；
   结构顺序 days（日内按时段分栏 period）→ remark → meta；分隔线依赖
   .home-plan__day 自带的 border-bottom（最后一天保留底线，正好充当
   「日计划 | 备注」的分隔），meta 为末块无线。 */
.home-plan__hint {
    padding: 16px;
    font-size: 13px;
    color: var(--text-light);
}

/* 日内时段分栏（二轮口径 ❸）：时段名小标题 + 该时段的条目；
   左侧竖线做栏视觉，今天高亮日跟着主题色走 */
.home-plan__period {
    margin-left: 2px;
    padding-left: 10px;
    border-left: 2px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.home-plan__period-name {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-light);
}

.home-plan__day--today .home-plan__period {
    border-left-color: var(--primary);
}

.home-plan__day--today .home-plan__period-name {
    color: var(--primary);
}

/* 备注 = 周计划 content，可含换行 → pre-line 原样保留 */
.home-plan__remark {
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
}

.home-plan__remark-label {
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: 700;
    color: var(--text-light);
}

.home-plan__remark-text {
    font-size: 13px;
    line-height: 1.6;
    color: var(--text-primary);
    white-space: pre-line;
    word-break: break-word;
}

/* 值周领导 / 值周班级：一行两端排列（需求原文「下方在一行中显示」） */
.home-plan__meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px 16px 14px;
}

.home-plan__meta-item {
    min-width: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.home-plan__meta-item i {
    margin-right: 6px;
    font-size: 12px;
    color: var(--primary);
}

/* ============================
   全局轻提示
   ============================ */
.app-toast {
    position: fixed;
    left: 50%;
    /* 悬在底部导航**之上**：导航实高 ≈81px，96px 留一线余量；
       再加安全区，否则刘海机上提示条会被 home indicator 压住。 */
    bottom: calc(96px + env(safe-area-inset-bottom, 0px));
    max-width: 80%;
    padding: 10px 18px;
    border-radius: 20px;
    background: rgba(30, 41, 59, 0.9);
    color: #fff;
    font-size: 13px;
    line-height: 1.5;
    text-align: center;
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, 12px);
    transition: opacity 0.25s ease, transform 0.25s ease;
    z-index: 9999;
}

.app-toast--show {
    opacity: 1;
    transform: translate(-50%, 0);
}

/* 🆕 归档 `093` 追加（2026-09-27）：**常驻提示** —— 主动点击才关闭。
   用于「签到完成」这类需要用户确认看到的结果（普通提示仍 1.8s 自动消失）。
   要点：
     · `pointer-events: auto` —— 普通提示是 none（不挡下面内容），可点的这条必须放开，
       否则点了穿透到下面的页面、toast 永远关不掉。
     · 视觉上给一个「可关闭」的暗示：加一层外描边 + 稍宽的右边距（给 ✕ 留位）。
       实测背景是半透明深色，白色描边足够醒目但不刺眼。
     · 不用 `cursor:pointer`（移动端无意义），但保留 —— 桌面调试时能看出可点。 */
.app-toast--sticky {
    pointer-events: auto;
    cursor: pointer;
    padding-right: 34px;            /* 给右侧 ✕ 留位 */
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35);
}

/* 常驻提示的 ✕（由 ::after 画，不额外加 DOM —— 文案仍走 el.textContent 整体替换）
   ⚠️ `.app-toast` 本身是 `position: fixed`，天然就是定位祖先，故 ::after 的 absolute 有参照。 */
.app-toast--sticky::after {
    content: '关闭 ✕';
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 11px;
    opacity: 0.7;
    white-space: nowrap;
}

/* ============================
   出勤上报页
   结构见 pages/attendance.html，逻辑见 js/attendance.js
   ============================ */
.att-step__head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.att-step__title {
    margin: 0;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.att-step__hint {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-light);
}

/* 返回班级列表 */
.att-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 5px 10px 5px 7px;
    border: none;
    border-radius: 14px;
    background: var(--primary-bg);
    color: var(--primary);
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: var(--transition);
}

.att-back i {
    font-size: 11px;
}

.att-back:active {
    transform: scale(0.96);
}

/* 班级列表：**每行最多 4 个**的自适应网格（归档 `058`）
   ------------------------------------------------------------------
   需求原话：「每行固定最多显示四个班级，平均分配适应屏幕」。

   🔴 用 `repeat(4, minmax(0, 1fr))` 而不是 `auto-fill`：
      `auto-fill/auto-fit` 会按容器宽度**自己决定列数**（390px 屏上可能只排 3 列、
      在 iPad 上排到 6 列），那就不是「固定最多四个」了。
      这里写死 4 列 ⇒ 任何屏宽都是 4 列，每个按钮等宽（1fr），
      窄屏时按钮自然变窄、字号靠 `clamp()` 兜住，不会溢出。

   🔴 `minmax(0, 1fr)` 的 `0` 是必需的：裸 `1fr` 的最小值是 `auto`，
      遇到「宝宝班」这类较长班名时，按钮会被内容撑宽 ⇒ 撑破网格。
      这一条是 flex/grid 里最常被漏掉的溢出陷阱。

   ⚠️ 单班直达时本容器不会被渲染（见 js/class-gate.js），
      故「只有 1 个班」时不会出现「4 格里孤零零 1 格」的观感问题。 */
.att-list {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
}

/* 网格单元里的按钮：拉满格子宽度，内容居中换行。
   原先它是「胶囊」形态（inline-flex + 内容宽度 + 999px 圆角），
   在 4 列网格里必须改成块级填充，否则按钮会缩成一小团贴在格子里。 */
.att-class {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    width: 100%;
    min-height: 52px;
    padding: 8px 6px;
    border: none;
    border-radius: 14px;
    background: var(--card-bg);
    box-shadow: var(--shadow);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.3;
    text-align: center;
    cursor: pointer;
    transition: var(--transition);
    -webkit-tap-highlight-color: transparent;
}

.att-class:active {
    transform: scale(0.95);
    background: var(--primary-bg);
    color: var(--primary);
    box-shadow: var(--shadow-hover);
}

/* 继承按钮的字号与颜色，:active 时才能整体变色 */
.att-class__name {
    font-size: inherit;
    font-weight: inherit;
    color: inherit;
    /* 超长班名在格子内换行而不是撑破网格（配合 .att-class 的 min-width:0 语义） */
    overflow-wrap: anywhere;
}

.att-class__count {
    font-size: 12px;
    font-weight: 500;
    color: var(--text-light);
}

/* 出勤状态三选一：出勤 / 病假 / 事假
   位置在学生清单**上方** —— 它是「标记模式」选择器：选中哪个，点学生就标哪个状态。
   ⚠️ 本轮只做**互斥选中 + 高亮**，不给学生打标、不做上报（下一步）。

   语义色沿用重置前旧版（`.func-btn--present/sick/leave`）的取值，理由是这三个状态
   靠颜色区分才有意义（与"性别不分色"不同：性别是属性，这里是业务状态）。
   ⚠️ 但蓝色一律写 var(--primary)，**不得出现 #3B82F6 字面量**（全局基线）。

   选中态刻意做成「淡色底 + 同色描边 + 同色字」，而不是旧版的「2px 空心描边」：
   浅色极简基线里 2px 描边太重，且只靠描边在 390px 窄屏上分不出选中与未选中。
   未选中的两个保持白底 + 发丝描边 + 次级灰字，视觉上明显退到后面。 */
.att-status {
    display: flex;
    gap: 10px;
    margin-bottom: 14px;
}

.att-status__btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 38px;
    /* ⚠️ --card-line 本身就是**完整 shorthand**（`1px solid var(--border)`），
       只能整体引用 —— 写成 `border: 1px solid var(--card-line)` 会展开成
       `1px solid 1px solid …`，整条声明无效，边框被 button 重置的 `border:none` 吃掉，
       静默变成 0px（2026-09-18 真机量到 border-top-width=0px 才发现）。 */
    border: var(--card-line);
    border-radius: var(--radius-pill);
    background: var(--card-bg);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: var(--transition);
}

.att-status__btn i {
    font-size: 13px;
}

/* 选中：字重加重 + 语义色底/边/字。三个状态各一套，不共用（颜色本身就是区分信息） */
.att-status__btn--active {
    font-weight: 600;
}

.att-status__btn--present.att-status__btn--active {
    border-color: #059669;
    background: #ECFDF5;
    color: #047857;
}

.att-status__btn--sick.att-status__btn--active {
    border-color: #D97706;
    background: #FFFBEB;
    color: #B45309;
}

.att-status__btn--leave.att-status__btn--active {
    border-color: var(--primary);
    background: var(--primary-bg);
    color: var(--primary-dark);
}

/* 点名操作提示：多选这件事不写在界面上没人知道能点，更不会知道「再点一次能取消」 */
.att-tip {
    margin: 0 0 10px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-light);
}

/* 学生名单 */
.att-students {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

/* 一格一个幼儿：只放姓名，单行居中（**不再显示性别**）。
   仍用 flex 居中而非 text-align —— 3 列网格单格宽度会随屏宽变化，靠 flex 居中更稳。

   它是**可点的多选开关**（点一下打标、再点一下撤销），所以用 <button> 而不是 <div>：
   键盘可达与读屏语义都由浏览器给，不必自己补 role / tabindex。 */
.att-student {
    position: relative;          /* 右上角「待提交」圆点的定位基准 */
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 14px 6px;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    box-shadow: var(--shadow);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    text-align: center;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    transition: var(--transition);
    -webkit-tap-highlight-color: transparent;
}

.att-student:active {
    transform: scale(0.96);
}

/* 当日出勤状态 → 卡片配色（未标记的保持白底 + 深色字，视觉上退到后面）。
   三个状态各一套、不共用 —— 颜色本身就是区分信息。

   ⚠️ 一律写成**复合选择器** `.att-student.att-student--x`：文末「设计基线统一层」里的
      `.att-student { border: … }` 是 shorthand，同权重且顺序在后，会把这里单独写的
      border-color 盖回发丝灰（2026-09-18 踩过：border 与 border-color 的覆盖顺序）。 */
.att-student.att-student--present {
    border-color: #059669;
    background: #ECFDF5;
    color: #047857;
}

.att-student.att-student--sick {
    border-color: #D97706;
    background: #FFFBEB;
    color: #B45309;
}

.att-student.att-student--leave {
    border-color: var(--primary);
    background: var(--primary-bg);
    color: var(--primary-dark);
}

/* 本地已标记、尚未提交：右上角一个小圆点，与「已写库」区分开。
   颜色取 currentColor，跟着上面的状态色走，不必再给三套颜色。 */
.att-student.att-student--pending::after {
    content: '';
    position: absolute;
    top: 6px;
    right: 6px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.7;
}

/* 中途离园附加角标：在正常出勤底色（.att-student--present）之上，右上角叠一个「离」字小标签。
   它是**附加事实**（后端 mid_leave_status=4，att_stt_code 仍是 1 出勤），所以只加角标、不动底色。
   颜色取琥珀，与「中途离园」语义对齐（见 .att-student--leavemid）。 */
.att-student__corner {
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    color: #fff;
    background: #D97706;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
    pointer-events: none;
}
/* 同时有「本地待提交」圆点时，把圆点挪到左上角，避免和「离」角标在右上角重叠 */
.att-student.att-student--pending.att-student--midleave::after {
    top: 6px;
    left: 6px;
    right: auto;
}

.att-student__name {
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* ⚠️ 遗留言：性别行（浅灰小字）**已不再渲染**
   —— 需求改为学生格只显示姓名。保留不删：性别字段仍在 fetchStudentList 的映射里，
   恢复展示时把 .att-student 改回 column 排布、并在渲染里补回这一行即可。
   刻意不做蓝 / 粉分色 —— 统一基线的浅色极简里不引入性别色，
   卡片描边与投影已由文末「设计基线统一层」统一给。 */
.att-student__gender {
    max-width: 100%;
    font-size: 11px;
    font-weight: 500;
    line-height: 1.4;
    color: var(--text-light);
}

/* 加载 / 空 / 错误态 */
.att-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 46px 20px;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    box-shadow: var(--shadow);
    font-size: 13px;
    color: var(--text-light);
    text-align: center;
}

.att-state i {
    font-size: 26px;
}

.att-state p {
    margin: 0;
    line-height: 1.6;
}

/* 占位提示的第二行小字说明
   ⚠️ 当前未使用（2026-09-18）：出勤页的学生名单接口已接真实后端，
      「接口未接入」的占位态（.att-state--placeholder + 下面这条 em）失去了唯一调用点。
      保留不删 —— 接口未接入 / 功能未开放这类「不是错误、也没有数据」的场景会反复出现
      （设置页的「待开发」条目即同类），届时直接复用。js/attendance.js 的 renderState 同此。 */
.att-state em {
    font-style: normal;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-light);
    opacity: 0.85;
}

/* 接口未接入等「暂无数据来源」的占位态，与真正的错误态区分开（当前未使用，见上） */
.att-state--placeholder i {
    color: var(--primary);
}

.att-state--error,
.att-state--error i {
    color: #A32D2D;
}

/* 底部提交
   主按钮视觉与登录页 .login-submit 同一基线（渐变蓝 135° + radius-md + 白字 600 + 阴影），
   高度取 46px 而非登录页的 52px —— 出勤页这一屏还有状态按钮与学生网格，
   52px 会把清单挤得过短，46px 仍满足触控区最小高度（≥44px）。

   ⚠️ 旧版这段叫 .bottom-buttons / .bottom-btn--submit（见「底部按钮」遗留段），
   刻意不复用：它带 2px 描边的重置按钮 + 更重的阴影，且与「重置/提交」成对出现；
   本页只有提交一个动作，且沿用当前基线的字重与圆角。 */
.att-submit {
    margin-top: 16px;
}

.att-submit__btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    height: 46px;
    border: none;
    border-radius: var(--radius-md);
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    color: #fff;
    font-family: inherit;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 2px;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(59, 130, 246, 0.35);
    transition: var(--transition);
}

.att-submit__btn i {
    font-size: 15px;
}

.att-submit__btn:active {
    transform: scale(0.98);
    box-shadow: 0 2px 8px rgba(59, 130, 246, 0.4);
}

/* 提交中：此时按钮**真的不可点**（防重复提交），与「没有可提交内容」是两回事 ——
   后者按钮仍然可点，点了会提示先标记学生。 */
.att-submit__btn:disabled {
    cursor: default;
    opacity: 0.65;
    transform: none;
    box-shadow: none;
}

/* ============================
   中途离园上报页
   结构见 pages/leave.html，逻辑见 js/leave.js
   复用出勤页的 .att-step / .att-list / .att-class / .att-students / .att-student 组件；
   本段只补：中途离园状态色、状态小字、底部双按钮。
   ============================ */

/* 中途离园状态（code=4）：琥珀色，与出勤（绿）区分。
   ⚠️ 复合选择器 .att-student.att-student--leavemid（同出勤页，避免被统一层 .att-student 覆盖）。 */
.att-student.att-student--leavemid {
    border-color: #D97706;
    background: #FFFBEB;
    color: #B45309;
}

/* 学生格内的「姓名 + 状态小字」纵向居中（不改 .att-student 自身布局，只包一层） */
.att-student__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-width: 0;
}

.att-student__tag {
    max-width: 100%;
    font-size: 11px;
    font-weight: 500;
    line-height: 1.3;
    opacity: 0.9;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* 底部双按钮：提交中途离园（主）/ 取消中途离园（次）。
   主按钮琥珀渐变 —— 已登记进 verify-theme-consistency.js 的 GRADIENT_OK 白名单。 */
.leave-actions {
    display: flex;
    gap: 10px;
    margin-top: 16px;
}

.leave-actions__btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 46px;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: var(--transition);
    -webkit-tap-highlight-color: transparent;
}

/* 主按钮：提交中途离园（琥珀渐变，与「中途离园」amber 主题一致） */
.leave-actions__btn--primary {
    border: none;
    background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);
    color: #fff;
    box-shadow: 0 4px 14px rgba(217, 119, 6, 0.32);
}

/* 次按钮：取消中途离园（描边 + 浅底，退到后面） */
.leave-actions__btn--default {
    border: var(--card-line);
    background: var(--card-bg);
    color: var(--text-secondary);
}

.leave-actions__btn:active {
    transform: scale(0.98);
}

.leave-actions__btn:disabled {
    cursor: default;
    opacity: 0.6;
    transform: none;
    box-shadow: none;
}

/* ============================================================
   原因采集弹层（出勤病假/事假 + 中途离园共用，js/reason-modal.js 渲染）
   移动端底部抽屉式；确认按钮用实色主按钮（非渐变，不进 GRADIENT_OK 白名单）
   ============================================================ */
.reason-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: flex-end; justify-content: center; }
.reason-modal__mask { position: absolute; inset: 0; background: rgba(15, 23, 42, 0.45); }
.reason-modal__panel {
    position: relative; width: 100%; max-width: 520px; box-sizing: border-box;
    background: var(--card-bg); border-radius: 18px 18px 0 0;
    padding: 20px 18px calc(20px + env(safe-area-inset-bottom));
    box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.18);
    animation: reasonUp .2s ease;
}
@keyframes reasonUp { from { transform: translateY(100%); } to { transform: none; } }
.reason-modal__head { margin-bottom: 14px; }
.reason-modal__title { font-size: 17px; font-weight: 700; color: var(--text); margin: 0; }
.reason-modal__sub { margin: 4px 0 0; font-size: 13px; color: var(--text-secondary); }
/* 幼儿姓名：放到醒目位置（大号 + 主色），进度「第 x / y 位」退为右侧小字 */
.reason-modal__stu { display: flex; align-items: baseline; gap: 8px; margin: 8px 0 0; }
.reason-modal__name { font-size: 20px; font-weight: 700; color: var(--primary); }
.reason-modal__idx { font-size: 12px; color: var(--text-secondary); }
.reason-modal__body { max-height: 60vh; overflow-y: auto; }
.reason-modal__field { margin-bottom: 14px; }
.reason-modal__field:last-child { margin-bottom: 0; }
.reason-modal__label { display: block; font-size: 14px; font-weight: 600; color: var(--text); margin-bottom: 8px; }
.reason-modal__req { color: #DC2626; font-style: normal; margin-left: 2px; }
.reason-modal__options { display: flex; flex-direction: column; gap: 8px; }
.reason-modal__option {
    display: flex; align-items: center; gap: 10px; padding: 12px 14px;
    border: var(--card-line); border-radius: var(--radius-md);
    background: var(--card-bg); cursor: pointer; font-size: 15px; color: var(--text);
}
.reason-modal__option.is-checked { border-color: var(--primary); background: rgba(59, 130, 246, 0.06); }
.reason-modal__option input { accent-color: var(--primary); width: 18px; height: 18px; flex: none; }
/* 具体情况：单行文本框（内容一般不多，比 textarea 省纵向空间） */
.reason-modal__detail {
    width: 100%; box-sizing: border-box; height: 44px; padding: 0 12px;
    border: var(--card-line); border-radius: var(--radius-md);
    font-family: inherit; font-size: 14px; color: var(--text);
    background: var(--card-bg);
}
.reason-modal__hint { display: block; margin-top: 6px; font-size: 12px; color: #DC2626; font-style: normal; }
/* ⚠️ 必须显式写 display:none —— 上面 .reason-modal__hint 的 display:block 会盖掉 [hidden] 的 UA 样式，
   不写的话「选『其他』时必填」红字会在**任何**选中态都一直显示（2026-09-19 真机截图发现；同类坑见 login.css 的 .login-panel[hidden]）。 */
.reason-modal [hidden] { display: none; }
.reason-modal__actions { display: flex; gap: 10px; margin-top: 6px; }
.reason-modal__btn {
    flex: 1; height: 46px; border-radius: var(--radius-md); font-size: 15px; font-weight: 600;
    font-family: inherit; cursor: pointer; transition: transform .1s ease; -webkit-tap-highlight-color: transparent;
}
.reason-modal__btn--cancel { border: var(--card-line); background: var(--card-bg); color: var(--text-secondary); }
.reason-modal__btn--confirm { border: none; background: var(--primary); color: #fff; }
.reason-modal__btn--confirm:disabled { opacity: .5; cursor: default; }
.reason-modal__btn:active { transform: scale(0.98); }

/* ============================================================
   身份页（identity.html，2026-09-19）
   ------------------------------------------------------------
   位置：**登录成功 → 本页 → 主页**。三条分支：唯一身份直通 / 多身份选择 / 无身份绑定。
   它是独立整页（与 login.html / register.html 同类），故整页滚动 + 居中窄栏；
   卡片面（描边 + 投影）交给文末「设计基线统一层」①，本段只管布局与字型 ——
   同一个属性不留两处定义。
   ============================================================ */
.identity-body {
    /* 与登录页同理：整页可滚动（矮屏 / 输入法顶起时都要能滚到底）。
       全局 body 是 overflow:hidden，不覆盖就只能显示首屏。 */
    overflow: auto;
}

.identity-page {
    max-width: 460px;
    margin: 0 auto;
    padding: 26px 20px 36px;
}

/* 品牌区：与登录 / 设置页同一套语言（极淡品牌光晕 + 横版 logo，不用蓝色色块） */
.identity-header {
    text-align: center;
    /* 通栏：抵消 .identity-page 的内边距，让光晕横跨整屏 */
    margin: -26px -20px 18px;
    /* 顶部计入安全区：身份页没有 .top-bar，品牌区就是最顶，刘海屏上 logo 会顶进状态栏 */
    padding: calc(30px + env(safe-area-inset-top, 0px)) 20px 20px;
    background: var(--brand-glow);
}

.identity-logo {
    width: 96px;
    margin: 0 auto 12px;
}

/* 横版 logo —— 与 .login-logo / .top-bar__logo / .settings-logo 同一套裁切规则。
   ⚠️ height: auto 不能省：<img> 上的 height="640" 会以 height:640px 参与层叠，
      把 aspect-ratio 挤掉（实测会被裁成一条）。 */
.identity-logo img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 631 / 412;
    object-fit: cover;
    object-position: center 40%;
}

.identity-title {
    margin: 0 0 4px 0;
    font-size: 19px;
    font-weight: 600;
    color: var(--text-primary);
    letter-spacing: 0.06em;
}

.identity-account {
    margin: 0;
    font-size: 12px;
    color: var(--text-light);
}

/* 阶段卡片：加载 / 自动确认 / 选择 / 绑定 / 错误，同一时刻只显示一张，
   切换靠 HTML 的 hidden 属性（兜底规则见下面的 [hidden]）。 */
.identity-card {
    background: var(--card-bg);
    border-radius: var(--radius-lg);
    padding: 18px;
    margin-bottom: 16px;
}

.identity-card__title {
    margin: 0 0 6px 0;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.identity-card__hint {
    margin: 0 0 14px 0;
    font-size: 13px;
    color: var(--text-secondary);
}

/* 🔴 hidden 必须显式兜底：作者样式的 display 会盖掉 HTML hidden 属性的 UA 规则
   （与 .reason-modal [hidden]、.login-panel[hidden] 同一类坑 —— 本项目已踩过两次）。
   本页五个阶段块全靠 hidden 切换，漏了会出现「加载中 + 错误态同时出现」。 */
.identity-page [hidden] {
    display: none;
}

/* 进行中 / 加载中的一行提示 */
.identity-state {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin: 6px 0;
    font-size: 14px;
    color: var(--text-secondary);
}

.identity-state i { color: var(--primary); }
.identity-state--muted { font-size: 12px; color: var(--text-light); }

/* 唯一身份自动确认：姓名放大，角色 / 园所退为副行 */
.identity-auto__name {
    margin: 2px 0 4px;
    font-size: 20px;
    font-weight: 700;
    color: var(--primary);
}

.identity-auto__meta {
    margin: 0 0 10px;
    font-size: 13px;
    color: var(--text-secondary);
}

/* 选项列表（多身份选择）与教师列表共用同一套外壳间距 */
.identity-options {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* 多身份：整行可点 */
.identity-option {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 12px 14px;
    background: var(--card-bg);
    border-radius: var(--radius-md);
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

.identity-option__main { flex: 1; min-width: 0; }

.identity-option__name {
    display: block;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}

.identity-option__meta {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--text-secondary);
}

.identity-option__arrow { font-size: 13px; color: var(--text-light); }
.identity-option:active { transform: scale(0.995); }

/* 🆕 归档 096：平台管理员卡 —— 它是唯一「进去还要再验一道」的身份，
   给一道左侧色条 + 盾牌图标，好在两三张卡里一眼认出来。
   ⚠️ 用 border-left 而不是 background 改色：整块换底色会让它看起来像
      「选中的那一张」（`.identity-option--current` 另有底色），两件事会混。 */
.identity-option--admin {
    border-left: 3px solid var(--primary, #3B82F6);
    padding-left: 11px;   /* 14px - 3px，补回外框占掉的宽度，保持与其它卡对齐 */
}

.identity-option--admin .identity-option__arrow { color: var(--primary, #3B82F6); }

/* 绑定流程：第 1 步（园所）/ 第 2 步（本人档案）共用 */
.identity-step { display: block; }

.identity-field { display: block; margin-bottom: 10px; }

.identity-field__label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    color: var(--text-secondary);
}

.identity-field__input {
    width: 100%;
    height: 44px;                      /* 与登录页输入框同高：同一个控件不出现两种高度 */
    padding: 0 12px;
    border: var(--card-line);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    font-family: inherit;
    font-size: 15px;
    color: var(--text-primary);
}

.identity-field__input:focus {
    outline: none;
    border-color: var(--primary);
}

/* 搜索行：输入框 + 搜索按钮 */
/* 🔴 输入框与「搜索」按钮**强制上下两行**（用户实测反馈「输入框过小、显示不全」）。
   根因：.identity-btn 是 width:100%，而 <input> 默认按一行 20 字符给最小宽度，
   两者在同一 flex 行里互抢宽度 ⇒ 输入框被压到约 146px，18 位身份证号显示不全。
   改 flex-direction:column 后，两者各自整行宽，号码完整可见。 */
.identity-search {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 12px;
}

/* 两行布局下不再需要 flex:1（否则单列里 input 会被拉伸得怪）；
   让 input 按自身 100% 宽度整行显示即可。 */
.identity-search .identity-field__input { width: 100%; }

/* 搜索按钮也整行宽：两行布局里它本就是独占一行，这里显式锁住，
   免得哪天 .identity-btn 的 width 规则被改回 auto 又变回去。 */
.identity-search .identity-btn { width: 100%; }

/* 教师条目：与「多身份选项」的区别在右侧 —— 这里是一个明确动作（绑定），
   不是整行可点，避免"点到哪儿都在选"的误触。 */
.identity-teacher {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    background: var(--card-bg);
    border-radius: var(--radius-md);
}

.identity-teacher__main { flex: 1; min-width: 0; }

.identity-teacher__name {
    display: block;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.identity-teacher__meta {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--text-secondary);
}

/* 空列表是**正常业务结果**（该园没有教师 / 关键词没匹配上），不是错误态 */
.identity-empty {
    margin: 0;
    padding: 16px 0;
    text-align: center;
    font-size: 13px;
    color: var(--text-light);
}

.identity-tip {
    display: flex;
    gap: 6px;
    margin: 0 0 14px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-secondary);
}

.identity-tip i { color: var(--primary); margin-top: 2px; }

/* 「没查到 → 自主新增」入口：与列表区同宽，给一句话 + 一个出口。
   刻意不并进 .identity-empty —— 那是「结果为空」的陈述，这里要的是一条**出路**。 */
.identity-create-entry { margin-bottom: 12px; }

.identity-create-entry .identity-empty { padding: 12px 0; }

/* 录入表单的消息条：默认中性，`--error` 为失败（本地校验不通过 / 服务端 400·403·404·409）。
   错误用红、而「身份暂不可用」用琥珀 —— 前者是「这次没成」，后者是「你没错」，别混。 */
.identity-msg {
    margin: 0 0 12px;
    padding: 10px 12px;
    border: var(--card-line);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-secondary);
}

.identity-msg--error {
    border-color: #FECACA;
    background: #FEF2F2;
    color: #B91C1C;
}

/* 「身份暂不可用」（profile_code 2001 / 2002）：**告知**而非报错 ——
   用户没做错任何事，是园所档案没建好 / 家长功能还没上线。
   故用琥珀色提示卡（与中途离园角标同一色系），不用错误态的红色。 */
.identity-blocked {
    padding: 12px 14px;
    margin-bottom: 10px;
    border: 1px solid #FDE68A;
    border-radius: var(--radius-md);
    background: #FFFBEB;
}

.identity-blocked__who {
    margin: 0 0 4px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.identity-blocked__reason {
    margin: 0;
    font-size: 12px;
    line-height: 1.5;
    color: #B45309;
}

/* 按钮：主（品牌色实心，与全站主按钮同一画法）/ 次（浅底 + 描边，退到后面） */
.identity-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    height: 46px;
    border: none;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}

.identity-btn--primary {
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    color: #fff;
    box-shadow: var(--shadow);
}

.identity-btn--ghost {
    background: var(--primary-bg);
    color: var(--primary);
    border: var(--card-line);
}

.identity-btn--sm {
    width: auto;
    height: 32px;
    padding: 0 14px;
    font-size: 13px;
    flex-shrink: 0;
}

.identity-btn:active { transform: scale(0.99); }

/* 同一步骤里上下叠放的按钮（下一步 / 上一步） */
.identity-step .identity-btn + .identity-btn { margin-top: 12px; }

/* 按钮组（`.identity-actions`）与上方内容、组内按钮之间的间距。
   ⚠️ 这个类此前**完全没有规则** —— 间距只写在 `.identity-step` 上，而自主录入 /
      blocked / error / 身份列表这几块用的是 `.identity-actions`，
      于是「提交并绑定」和「返回」两个 46px 高的按钮几乎贴在一起（2026-09-20 反馈）。
      这里用 flex + gap 统一给，避免以后再漏；`hidden` 的按钮由
      `.identity-page [hidden]{display:none}` 兜底，不会占 gap。 */
.identity-actions {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 18px;
}

/* ============================================================
   设计基线统一层（2026-09-18）
   ------------------------------------------------------------
   为什么集中放在文件末尾：同权重的规则，**顺序在后的覆盖前面的** ——
   改这一处即"全局统一"，不必在上千行里做几十处分散改动（那种改法既无法验证，也极易漏改）。

   基线来自 css/login.css（登录 / 注册页）：
     浅色底、不用渐变色块、卡片带发丝描边、胶囊分页、图标方圆角浅底。

   ⚠️ **签名级改动仍要就地改**，不在本层加覆盖：一个属性只应有一处定义，
      否则读源码时会看到"旧值写在前面、新值藏在文件末尾盖着"这种误导。
      已就地改掉的：.top-bar 蓝色渐变块 → 白底 + 发丝线；
      .settings-header 蓝块 + 白圆衬底 → 品牌光晕 + 横版 logo。
   ============================================================ */

/* ① 卡片面统一：加发丝描边 + 轻投影
   与登录页的 .login-field 同一手法 —— 浅色底上只靠投影的卡片边界发虚，
   加一条 1px 描边后"面"才立得住（为什么不是 1.5px 见 :root 里 --card-line 的说明）。
   ⚠️ `.att-class` 在 1543 行写了 `border: none`，被这里**后出现的**规则覆盖成描边 ——
      这是刻意的（归档 058 改网格后仍要有卡片边界）。改上面的 `border` 前先看这里。 */
.home-profile,
.home-quick__item,
.home-plan,
.settings-list,
.logout-btn,
.att-class,
.att-student,
.att-state,
.identity-card,
.identity-option,
.identity-teacher {
    border: var(--card-line);
    box-shadow: var(--shadow);
}

/* ② 区块标题统一：15px/700 深色 + 左侧品牌色条
   原先有三套写法（首页 15px/700 带色条、设置页 16px/600 灰字、出勤页 15px/700 无色条），这里三合一。 */
.settings-section .section-title,
.att-step__title {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-left: 0;          /* 设置页原本有 4px 缩进，与首页的色条对齐方式不一致 */
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.settings-section .section-title::before,
.att-step__title::before {
    content: '';
    width: 3px;
    height: 14px;
    border-radius: 2px;
    background: var(--primary);
    flex-shrink: 0;
}

/* 出勤页的标题在 .att-step__head 这个 flex 行里（前面是返回按钮、后面是提示），
   所以色条不能占掉标题该有的伸展空间，下边距也由 .att-step__head 统一给。 */
.att-step__title {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
}

/* ④ 分页控件统一为胶囊（基线是登录页的 .login-tabs：浅灰底 + 白色选中块 + 蓝字）
   ⚠️ 选中类名是 BEM 的 `.tab-btn--active`，与 .nav-btn--active 同风格。
      旧实时页那套 `.tab-btn.active`（非 BEM）已随页面移除，别混用 —— 混了就是"点了没高亮"。 */
.tab-bar {
    display: flex;
    padding: 4px;
    background: rgba(148, 163, 184, 0.16);
    border-radius: var(--radius-pill);
    box-shadow: none;
}

.tab-bar .tab-btn {
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--text-secondary);
    font-weight: 400;
    box-shadow: none;
}

.tab-bar .tab-btn--active {
    background: #fff;
    color: var(--primary);
    font-weight: 600;
    box-shadow: 0 1px 4px rgba(30, 64, 175, 0.14);
}

/* ============================
   小屏适配（≤ 360px：iPhone SE / 小屏安卓）
   2026-09-19 移动端自适应收尾：让最窄的常见机型也能完整放下，杜绝横向滚动。
   只收「内边距 / 字号」这些缩放安全量，不动布局结构；安全区增量照旧保留。
   ============================ */
@media (max-width: 360px) {
    .top-bar {
        padding: calc(14px + env(safe-area-inset-top, 0px)) 14px 14px;
    }
    .content-area {
        padding: 16px 12px calc(96px + env(safe-area-inset-bottom, 0px));
    }
    .identity-page {
        padding: 18px 14px 28px;
    }
    .identity-header {
        padding: calc(24px + env(safe-area-inset-top, 0px)) 14px 16px;
    }
    .identity-card {
        padding: 14px;
    }
    .identity-logo {
        width: 84px;
    }
    .identity-title {
        font-size: 17px;
    }
    .login-header {
        padding-left: 16px;
        padding-right: 16px;
    }
    .login-panel {
        padding: 18px 16px;
    }
    /* 输入框 ≥ 16px：iOS Safari 在聚焦小于 16px 的输入框时会整页缩放，
       小屏上这一下缩放会把布局撑破。 */
    .identity-field__input,
    .identity-search .identity-field__input {
        font-size: 16px;
    }
    /* 班级网格（归档 058）：4 列在 360px 屏上每格约 74px（已扣 gap 与页边距），
       15px 的「宝宝班」会顶到边。降到 14px 并收紧内边距，保证不换行撑破。 */
    .att-list {
        gap: 8px;
    }
    .att-class {
        padding: 7px 4px;
        font-size: 14px;
    }
}

/* ============================
   权限层（2026-09-20）：模块权限隐藏入口、操作权限禁用按钮
   ------------------------------------------------------------
   js/permission.js 只负责**打标记**，视觉落在这里：
     · .perm-hidden      —— 没有模块/操作权限，整个区块不出现（入口级）
     · .is-perm-denied   —— 有模块权限但缺操作权限，控件禁用（按钮级）
     · .perm-note        —— 「只有查看权限」的说明条，避免用户以为页面坏了
   ⚠️ 为什么 .perm-hidden 用 !important：它要盖过页面自己的显隐逻辑
      （如 .att-submit 默认 class="hidden"、由脚本控制显示），
      但不改页面那个 class —— 两边各管各的，互不抢。
   ⚠️ 为什么禁用态另起 .is-perm-denied 而不是只靠 :disabled：
      :disabled 只覆盖 button/input，容器（如 .att-status 状态选择组）需要
      一个能一眼看出「这块不可用」的信号，否则用户会以为按钮坏了。
   ============================ */
.perm-hidden {
    display: none !important;
}

.is-perm-denied {
    opacity: .55;
    cursor: not-allowed;
}

/* 只读说明条：琥珀浅底 —— 是「权限没开」不是「出错了」，别用红色吓人 */
.perm-note {
    margin: 12px 0 0;
    padding: 10px 12px;
    border: 1px solid #FDE68A;
    border-radius: 12px;
    background: #FFFBEB;
    color: #92400E;
    font-size: 13px;
    line-height: 1.55;
}

/* 嵌在设置列表里的那一条：去掉左右留白，与 .setting-item 对齐 */
.perm-note--inline {
    margin: 6px 16px 0;
    font-size: 12px;
    padding: 8px 12px;
}

/* 快捷功能一个都不剩时的兜底文案（不是错误，是「还没给你开」） */
.home-quick__empty {
    margin: 0;
    padding: 14px 0;
    color: var(--text-light, #94A3B8);
    font-size: 13px;
    text-align: center;
}

/* ============================
   应用模块（归档 `041`）
   ============================ */
/* 应用卡片：左图标 + 中标题说明 + 右箭头。
   🔑 用 grid 而不是 flex：说明文字长度不一，flex 下箭头会被长文案推歪。 */
.app-grid {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.app-card {
    position: relative;
    display: grid;
    grid-template-columns: 40px 1fr 14px;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 14px 16px;
    border: none;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    box-shadow: var(--shadow);
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    transition: var(--transition);
}

.app-card:active {
    transform: scale(0.98);
    box-shadow: var(--shadow-hover);
}

.app-card__icon {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    font-size: 17px;
    background: var(--primary-bg);
    color: var(--primary);
}

.app-card__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.app-card__title {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.app-card__desc {
    font-size: 12px;
    color: var(--text-light);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-card__arrow {
    font-size: 12px;
    color: var(--text-light);
}

/* 应用入口状态角标（三态，与 .mg-badge 同族色板，不引入新色系；
   文案写进 HTML（非 CSS ::after），便于三态各显其文、也便于验证脚本按类名断言）：
   --testing 测试中（信息蓝 #E6F1FB/#185FA5）· --dev 开发中（中性灰 #E2E8F0/#475569）
   · --planned 待开发（浅灰 #F1EFE8/#5F5E5A，与 .mg-badge--off 同族） */
.app-card__badge {
    position: absolute;
    top: -7px;
    right: 10px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 600;
    line-height: 1.6;
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.18);
    pointer-events: none;
    white-space: nowrap;
}
.app-card__badge--testing { background: #E6F1FB; color: #185FA5; }
.app-card__badge--dev     { background: #E2E8F0; color: #475569; }
.app-card__badge--planned { background: #F1EFE8; color: #5F5E5A; }

.app-card[data-tone="teal"] .app-card__icon   { background: #E1F5EE; color: #0F6E56; }
.app-card[data-tone="amber"] .app-card__icon  { background: #FAEEDA; color: #854F0B; }
.app-card[data-tone="red"] .app-card__icon    { background: #FCEBEB; color: #A32D2D; }
.app-card[data-tone="blue"] .app-card__icon   { background: #E6F1FB; color: #185FA5; }
.app-card[data-tone="purple"] .app-card__icon { background: #EEEDFE; color: #3C3489; }
.app-card[data-tone="green"] .app-card__icon  { background: #EAF3DE; color: #3B6D11; }
.app-card[data-tone="gray"] .app-card__icon   { background: #F1EFE8; color: #5F5E5A; }

/* ============================
   用户审核（归档 `041`）
   ============================ */
.review-tabs {
    display: flex;
    gap: 8px;
    margin: 12px 0;
}

.review-tab {
    flex: 1;
    padding: 8px 0;
    border: var(--card-line);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    font-family: inherit;
    font-size: 13px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: var(--transition);
}

.review-tab.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
    font-weight: 600;
}

/* 列表项里的状态小标签（已通过 / 已驳回的历史条目看得见自己是什么结果） */
.review-status {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 500;
    background: #F1EFE8;
    color: #5F5E5A;
}

.review-status--0 { background: #FAEEDA; color: #854F0B; }
.review-status--1 { background: #EAF3DE; color: #3B6D11; }
.review-status--2 { background: #FCEBEB; color: #A32D2D; }

/* ============================
   添加学生（归档 `041`）
   ============================ */
.sc-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

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

.sc-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.sc-required {
    color: #A32D2D;
    font-weight: 700;
}

.sc-input {
    width: 100%;
    padding: 10px 12px;
    border: var(--card-line);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    font-family: inherit;
    font-size: 14px;
    color: var(--text-primary);
    /* 🆕 归档 090：iOS Safari 上 <input type="date"> 会套用系统原生控件外观，
       其内在高度比 .sc-input 的盒模型更高 —— 同一行里「开始日期」框会明显
       比「结束日期」框高、上下缘对不齐（真机截图 bc78ac2b…）。
       去掉原生外观 + 统一 line-height/min-height，两个日期框才等高对齐。 */
    -webkit-appearance: none;
    appearance: none;
    line-height: 1.4;
    min-height: 43px;
    box-sizing: border-box;
}

/* 🆕 归档 090：日期 / 时间类输入（含 iOS 原生 date 控件）
   再显式声明一次外观与高度，确保各平台一致、且与同行其它控件等高。 */
.sc-input[type="date"],
.sc-input[type="time"],
.sc-input[type="datetime-local"] {
    -webkit-appearance: none;
    appearance: none;
    min-height: 43px;
    line-height: 1.4;
}

/* iOS 下日期控件内部的伪元素（取值文本 / 清除按钮）默认有额外内边距，
   会让内容行看起来「沉下去」；这里统一压平其内边距，视觉与文本框一致。 */
.sc-input[type="date"]::-webkit-date-and-time-value,
.sc-input[type="time"]::-webkit-date-and-time-value {
    text-align: left;
    margin: 0;
    padding: 0;
    line-height: 1.4;
}

.sc-input:focus {
    outline: none;
    border-color: var(--primary);
}

.sc-tip {
    font-size: 12px;
    color: var(--text-light);
}

/* ============================
   权限分配（归档 `041`）
   ============================ */
.pa-select {
    width: 100%;
    padding: 10px 12px;
    border: var(--card-line);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    font-family: inherit;
    font-size: 14px;
    color: var(--text-primary);
}

.pa-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin: 10px 0;
    font-size: 12px;
    color: var(--text-secondary);
}

.pa-legend__item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.pa-tree {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin: 12px 0;
}

.pa-module {
    border: var(--card-line);
    border-radius: var(--radius-md);
    background: var(--card-bg);
    overflow: hidden;
}

.pa-module__head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    background: var(--primary-bg);
    font-size: 13px;
    font-weight: 700;
    color: var(--primary);
}

.pa-module__code {
    font-size: 11px;
    font-weight: 400;
    color: var(--text-light);
}

.pa-module__body {
    display: flex;
    flex-direction: column;
    padding: 8px 14px 12px;
}

.pa-module__empty {
    font-size: 12px;
    color: var(--text-light);
}

.pa-node {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 0;
    font-size: 13px;
    color: var(--text-primary);
    cursor: pointer;
}

.pa-node__box {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.pa-node__name {
    font-weight: 500;
}

.pa-node__code {
    font-size: 11px;
    color: var(--text-light);
}

/* 🔴 归档 059：「来自用户组」的点标注来源（视觉提示，不再锁死 ——
      新语义下组给的权限可被「独立调整 deny」关掉，所以仍可点击取消）。 */
.pa-node--locked {
    color: var(--text-secondary);
}

.pa-node__tag {
    padding: 1px 6px;
    border-radius: 999px;
    background: #F1EFE8;
    color: #5F5E5A;
    font-size: 10px;
    font-weight: 600;
}

/* 🆕 归档 `079`：来源徽标分三色 —— 灰（来自组）/ 蓝（身份默认）/ 绿（个人直授）/ 红（已禁止）。
   为什么要分色：这三类在页面上都是「小标签」，只靠文字读起来要逐字比对，
   而它们的可操作性完全不同（默认不可用组里收回、直授随时可撤、已禁止可勾回）。
   色值与既有徽标同族（.mg-badge--on / --sev3），不引入新的色系。 */
.pa-node__tag--base  { background: #E6F1FB; color: #185FA5; }   /* 身份默认 */
.pa-node__tag--add   { background: #EAF3DE; color: #3B6D11; }   /* 个人直授 */
.pa-node__tag--deny  { background: #FCEBEB; color: #A32D2D; }   /* 已禁止 */

/* 🆕 归档 `108`：管理端系统角色权限树 —— 每个节点 grant（主勾选）+ deny（禁，高级）。
   与园所端「只有勾 / 不勾」不同，系统角色还要支持「明确禁止」（园所可用 deny 覆盖它）。 */
.pa-node--role { flex-wrap: wrap; }
.pa-node__deny {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-left: auto;
    font-size: 11px;
    color: #A32D2D;
    cursor: pointer;
    user-select: none;
}
.pa-node__deny input { width: 14px; height: 14px; }
/* 只读提示（系统角色权限树 / 园所端系统角色查看） */
.sc-tip--lock {
    margin: 4px 0;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    background: #EDE9FE;
    color: #5B21B6;
    font-size: 12px;
}

/* ============================
   总览 · 概况（归档 `042`）
   ============================ */
/* ① 今日出勤概况卡（AI 尚未接入，仅预留位）
   🔑 用一条左侧主色竖线把它与下面的真实统计区分开：
      上面是「系统在推测」，下面是「库里真实的数」，视觉上必须一眼分得清。 */
.ov-ai {
    border-left: 3px solid var(--primary);
}

.ov-ai__head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
}

.ov-ai__title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    font-weight: 700;
    color: var(--text-primary);
}

.ov-ai__title i {
    color: var(--primary);
}

/* 徽标：AI / 测试中。
   ⚠️ 这两个标签是**合规要求**不是装饰 —— 只要卡片里的内容还不是模型输出，
      就必须一直挂着，别在「看起来做好了」之后悄悄摘掉。 */
.ov-tag {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 600;
    line-height: 1.7;
}

/* ⚠️ 归档 049（2026-09-21）：本项目第四次踩同一个坑 ——
   .ov-tag 的 display:inline-block、.ov-ai__body 的 display:flex 都是作者样式，
   会盖掉 [hidden] 属性的 UA 样式（同 .reason-modal [hidden]、.identity-page [hidden]、
   .login-panel[hidden]）。不兜底的话，「分析中」徽标与 AI 正文空盒子
   会在任何状态下一直显示（2026-09-21 真机截图发现）。 */
.ov-ai [hidden] {
    display: none;
}

.ov-tag--ai {
    background: #EEEDFE;
    color: #3C3489;
}

.ov-tag--beta {
    background: #FAEEDA;
    color: #854F0B;
}

.ov-ai__text {
    font-size: 13px;
    line-height: 1.7;
    color: var(--text-secondary);
}

.ov-ai__note {
    margin-top: 8px;
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-light);
}

/* ② 关键指标：两格等宽，数字用等宽感更强的字重压住 */
.ov-kpi {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
    margin-bottom: 16px;
}

.ov-kpi__item {
    padding: 14px 16px;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    box-shadow: var(--shadow);
}

.ov-kpi__label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-secondary);
}

.ov-kpi__label i {
    color: var(--primary);
}

.ov-kpi__value {
    margin-top: 6px;
    font-size: 26px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-primary);
}

.ov-kpi__sub {
    margin-top: 4px;
    font-size: 11px;
    color: var(--text-light);
}

/* ③ 柱图容器：班级多达十几个，横轴标签要斜排 ⇒ 必须给足高度，
      否则标签被裁掉一半（ECharts 不会报错，只会静静画歪）。 */
.ov-chart {
    height: 300px;
}

/* ============================
   总览 · 请假（归档 `043`）
   ============================ */
/* 三格 KPI（概况是两格，这里是三格 ⇒ 单独一条规则，不覆盖 .ov-kpi 的两列基线） */
.ov-kpi--three {
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.ov-kpi--three .ov-kpi__value {
    font-size: 22px;
}

.ov-kpi--three .ov-kpi__label {
    font-size: 11px;
}

/* 图表下方的一行口径说明。
   🔑 它**不是**装饰 —— 「分母是什么」不说清楚，同一张图能读出两种意思。 */
.ov-note {
    margin-top: 8px;
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-light);
}

/* ============================
   总览 · 出勤热点图（🆕 归档 `051`）
   每天一个方块，按月分组；🔴 归档 089d：颜色越深 = 当日出勤人数越多
   （0-400 人平滑渐变），灰块 = 无上报数据。
   ============================ */
.ov-heatmap {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 8px;
}

.ov-heatmap__month {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

.ov-heatmap__monthName {
    flex: 0 0 28px;
    font-size: 11px;
    color: var(--text-light);
    line-height: 14px;
    padding-top: 1px;
}

.ov-heatmap__cells {
    flex: 1 1 auto;
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
}

.ov-heatmap__cell {
    width: 14px;
    height: 14px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.04);
    border-radius: 3px;
    background: #e5e7eb;            /* 默认 = 无数据灰 */
    cursor: pointer;
    transition: transform .08s ease;
}

.ov-heatmap__cell:active {
    transform: scale(0.9);
}

/* 🔴 归档 089d：固定四档（--1~--4）退役 —— 色值改由 JS 按出勤人数 0-400
   平滑渐变（HSL 插值内联 style）；仅保留无数据灰块兜底 */
.ov-heatmap__cell--empty { background: #e5e7eb; }

/* 图例渐变标尺（0 → 500 人，端点与 overview.js heatColor 同一插值）；
   🔴 089d 二轮：flex:1 独占整行随屏幕宽度伸缩，不标上下限数字、无「无数据」标签 */
.ov-heatmap__scale {
    flex: 1 1 auto;
    height: 12px;
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.04);
    background: linear-gradient(90deg, hsl(160, 71%, 86%), hsl(165, 86%, 20%));
}

.ov-heatmap__detail {
    margin-top: 8px;
    font-size: 12px;
    color: var(--text-primary);
    background: var(--primary-bg);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
}
/* 🔴 hidden 属性兜底（049 同款坑）：作者样式会盖掉浏览器默认 [hidden]{display:none} */
.ov-heatmap__detail[hidden] { display: none; }

.ov-heatmap__legend {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 4px;
    font-size: 11px;
    color: var(--text-light);
}

/* 🔴 089d 二轮：图例只剩独占整行的渐变标尺，__legendItem / __swatch 随之退役删除 */

/* 原因明细表：十几列必然超出屏宽 ⇒ 横向滚动 + 首列冻结。
   ⚠️ 冻结列必须给背景色，否则滚动时下面的数字会透上来。 */
.ov-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    margin-top: 10px;
    border-radius: var(--radius-sm);
    border: var(--card-line);
}

.ov-table {
    border-collapse: collapse;
    width: max-content;
    min-width: 100%;
    font-size: 12px;
}

.ov-th {
    padding: 7px 10px;
    border: var(--card-line);
    background: var(--primary-bg);
    color: var(--text-secondary);
    font-weight: 600;
    white-space: nowrap;
    text-align: center;
}

.ov-th--sub {
    font-weight: 500;
    font-size: 11px;
}

/* 冻结列：班级名与合计都要能一直看见，否则横滚之后不知道在看哪个班 */
.ov-th--sticky {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--primary-bg);
    text-align: left;
}

.ov-th--row {
    font-weight: 600;
    color: var(--text-primary);
}

.ov-td {
    padding: 7px 10px;
    border: var(--card-line);
    color: var(--text-light);
    text-align: center;
    white-space: nowrap;
}

/* 只有「真有人」的格子才上色 —— 十来列全是 0 时，
   把有数的那几格点亮，一眼就能定位到哪个班、哪个原因。 */
.ov-td--hit {
    color: var(--text-primary);
    font-weight: 600;
    background: #FFF7ED;
}

.ov-td--total {
    color: var(--text-primary);
    font-weight: 700;
    background: #F8FAFC;
}

.ov-empty {
    padding: 14px 0;
    font-size: 12px;
    color: var(--text-light);
    text-align: center;
}

/* ============================
   总览 · 厨房（归档 `044`）
   ============================ */
/* 「后续实现」徽标。
   ⚠️ 与「测试中 / AI」同族但语义不同：**那两个是「有东西但不可全信」，
   这个是「还没有东西」**。颜色刻意做成中性灰，别让人误以为已经在跑。 */
.ov-tag--todo {
    background: #F1EFE8;
    color: #5F5E5A;
}

/* 各班来校人数：网格，一个班一格。
   🔑 十几个班排成一列会很长，用网格让厨房一眼扫完；数字压到 24px 是这一页的主角。
   🆕 归档 `089b`：改 auto-fill 自适应 —— 手机窄屏一行 4 班（minmax 74px），
   页面越宽自动排越多列，不再写死三列。 */
.ov-class-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
    gap: 10px;
    margin-top: 10px;
}

.ov-class {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 12px 6px;
    border-radius: var(--radius-md);
    background: var(--primary-bg);
    border: var(--card-line);
    text-align: center;
}

.ov-class__name {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

.ov-class__num {
    font-size: 24px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--primary);
}

/* 🆕 归档 `089b`：`.ov-class__unit`（「人」字尾缀）已随 JS 模板一并移除，规则不再保留。 */

/* 🔴 「0 人」的班刻意压暗 —— 今天大部分班还没上报时，
   把「有数的那几格」凸显出来，比满屏一样的数字更有用。
   注意：这是**数字真的是 0**（已上报且没人来），与过敏区的「—」（无数据）不同。 */
.ov-class[data-zero="1"] {
    background: var(--card-bg);
}

.ov-class[data-zero="1"] .ov-class__num {
    color: var(--text-light);
}

/* ============================
   总览 · 请假明细（归档 `046`）
   ============================ */
/* 拆分后的两张子表（病假明细 / 事假明细）各占一个 section */
.lv-split {
    margin-top: 14px;
}

/* 🆕 归档 `046`：包两张明细子表（病假 / 事假）的容器（改名自 ov-table-wrap）。
   本身不渲染边框，仅做纵向间距；内部布局交给 .lv-split / .ov-table-wrap。 */
.lv-tables {
    display: block;
}
.lv-tables .lv-split + .lv-split {
    margin-top: 18px;
}

.lv-split__title {
    margin: 0 0 8px 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    padding-left: 8px;
    border-left: 3px solid var(--primary);
}

/* 可点的数字格：沿用 --hit 的橙色底，再加「能点」的手势与按压反馈 */
.ov-td--link {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.ov-td--link:active {
    background: #FFEDD5;
}

/* 明细弹层：点击数字后展示该班该原因的具体幼儿名单。
   挂 document.body（position:fixed 覆盖视口），关闭即移除 —— 与 reason-modal 同套路。 */
.lv-detail {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

.lv-detail__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}

.lv-detail__panel {
    position: relative;
    width: 100%;
    max-width: 480px;
    max-height: 72vh;
    display: flex;
    flex-direction: column;
    background: var(--card-bg, #fff);
    border-radius: 16px 16px 0 0;
    overflow: hidden;
}

.lv-detail__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px 10px;
    border-bottom: var(--card-line);
}

.lv-detail__title {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.lv-detail__close {
    border: none;
    background: transparent;
    padding: 4px 8px;
    font-size: 16px;
    color: var(--text-secondary);
    cursor: pointer;
}

.lv-detail__body {
    padding: 6px 16px 20px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}


/* ==========================================================================
   归档 `048` 维护类页面样式（.mg-* 名单型 / .cs-* 配置型）
   --------------------------------------------------------------------------
   覆盖页面：创建教职工 / 班级范围 / 过敏学生 / 学生信息维护 / 教师信息维护。

   🔴 语义色一律成"底 + 字"成对给（与 .app-card[data-tone] 同一套色板）：
      只给 color 不给 background，在浅色底上会读不清；只给 background 不给 color，
      文字会继承 --text-primary（深色）而在深底上发灰。

   ⚠️ --card-line 是**一整条 border 简写**，只能写 `border: var(--card-line);`。
   ========================================================================== */

/* ---- .mg-* 名单型：过滤条 / 名单 / 行 ---- */

.mg-filter {
    display: flex;
    gap: 8px;
    margin-bottom: 10px;
}

.mg-filter > .pa-select {
    flex: 1;
    min-width: 0;
}

/* 🔴 归档 `084` 二次改造：搜索框 / 按钮也作为 .mg-filter 子项时撑满该行。
   工作计划页把「内容搜索」与「筛选 / 重置」各自独占一行，需要它们 flex:1 拉满宽度。 */
.mg-filter > .sc-input {
    flex: 1;
    min-width: 0;
}

.mg-filter > .mg-btn {
    flex: 1;
}

.mg-count {
    font-size: 12px;
    font-weight: 400;
    color: var(--text-secondary);
}

.mg-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* 一行 = 一条档案。左侧主信息可换行收缩，右侧操作区**不压缩**
   （`flex-shrink: 0`）：否则「停用」两个字会被挤成竖排，误触率飙升。 */
.mg-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
}

.mg-row--off {
    opacity: 0.62;
}

.mg-row__main {
    flex: 1;
    min-width: 0;
}

.mg-row__title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.mg-row__meta {
    margin-top: 3px;
    font-size: 12px;
    color: var(--text-secondary);
    word-break: break-all;
}

.mg-row__ops {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* 「不可修改」比「按钮置灰」更诚实：置灰会让人以为"过会儿就能点"，
   而无权就是无权 —— 这里连按钮都不给。 */
.mg-row__locked {
    font-size: 12px;
    color: var(--text-secondary);
}

.mg-btn {
    border: var(--card-line);
    background: var(--card-bg);
    color: var(--text-primary);
    border-radius: var(--radius-pill);
    padding: 5px 12px;
    font-size: 13px;
    cursor: pointer;
    transition: var(--transition);
}

.mg-btn:active {
    background: var(--primary-bg);
}

.mg-btn--danger {
    color: #A32D2D;
    border-color: #F5C6C6;
    background: #FCEBEB;
}

.mg-btn[disabled] {
    opacity: 0.5;
    cursor: default;
}

/* ---- .mg-badge 徽标：底 + 字成对 ---- */

.mg-badge {
    display: inline-block;
    padding: 1px 7px;
    border-radius: var(--radius-sm);
    font-size: 11px;
    font-weight: 500;
    line-height: 1.6;
    background: #F1EFE8;
    color: #5F5E5A;
}

.mg-badge--gender { background: #E6F1FB; color: #185FA5; }   /* 性别 · 中性信息 */
.mg-badge--self   { background: #E1F5EE; color: #0F6E56; }   /* 本人 */
.mg-badge--on     { background: #EAF3DE; color: #3B6D11; }   /* 在岗 / 在册 */
.mg-badge--off    { background: #F1EFE8; color: #5F5E5A; }   /* 已离园 / 已解除 / 离职 */
/* 🆕 归档 `078` 班级状态：`edu_class.class_status` 是**三态** 0 停用 / 1 在读 / 2 毕业。
   毕业用中性蓝 —— 在读是绿（`--on`）、停用是灰（`--off`），
   ⚠️ 不能给毕业也用灰：那是终态，与「只是停用、还能启用回来」在视觉上必须能一眼分开。 */
.mg-badge--grad   { background: #E6F1FB; color: #185FA5; }   /* 毕业（终态） */
.mg-badge--system { background: #EDE9FE; color: #5B21B6; }   /* 系统角色（平台统一定义 · 全局共享） */

/* 过敏严重度：1 轻度 / 2 中度 / 3 重度 —— 数字即语义，按数字命名，不另起别名 */
.mg-badge--sev1 { background: #FAEEDA; color: #854F0B; }
.mg-badge--sev2 { background: #FAEEDA; color: #854F0B; }
.mg-badge--sev3 { background: #FCEBEB; color: #A32D2D; }

/* ---- .mg-result 结果回显（建号成功后的账号 / 初始密码）---- */

.mg-result {
    margin-top: 12px;
    padding: 12px;
    background: var(--primary-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
}

.mg-result__row {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    padding: 4px 0;
    font-size: 13px;
    color: var(--text-primary);
}

.mg-result__row + .mg-result__row {
    border-top: var(--card-line);
}

/* ---- .cs-* 配置型：MODE 单选 + 班级勾选 ---- */

.cs-mode {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 0 0 10px;
}

.cs-mode__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    font-size: 14px;
    color: var(--text-primary);
    cursor: pointer;
}

.cs-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.cs-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.cs-item__box {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
}

.cs-item__name {
    font-size: 14px;
    color: var(--text-primary);
}

/* ---- 应用卡新增两个 tone（归档 048 用了 indigo / cyan）----
   🔴 与 .app-card[data-tone] 既有七色同一色板，避免"新增功能自己发明颜色"。 */
.app-card[data-tone="indigo"] .app-card__icon { background: #EEEDFE; color: #3C3489; }
.app-card[data-tone="cyan"] .app-card__icon   { background: #E1F5EE; color: #0F6E56; }


/* ==========================================================================
   归档 `047` 概况 AI 卡 + 厨房过敏区（补 048 回归发现的 9 个"用了没定义"的类）
   --------------------------------------------------------------------------
   🔴 归档 046 教训：类名用了而 CSS 没定义 ⇒ 页面渲染成裸 HTML，**不报错、不进控制台**，
      只能靠 verify-overview.js 的「C 用到的类名在 style.css 里都有定义」这条断言兜住。
   ========================================================================== */

.ov-ai__refresh {
    border: var(--card-line);
    background: var(--card-bg);
    color: var(--text-secondary);
    border-radius: var(--radius-pill);
    padding: 3px 10px;
    font-size: 12px;
    cursor: pointer;
    transition: var(--transition);
}

.ov-ai__refresh:active {
    background: var(--primary-bg);
}

.ov-ai__body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 8px;
}

.ov-ai__block {
    padding: 8px 10px;
    background: var(--primary-bg);
    border-radius: var(--radius-sm);
}

.ov-ai__label {
    margin: 0 0 4px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.ov-ai__para {
    font-size: 13px;
    line-height: 1.7;
    color: var(--text-secondary);
    white-space: pre-wrap;
}

/* 预警 / 建议两条列表：色带在左侧（不靠颜色单独表意，前面还有标题文字） */
.ov-alert,
.ov-suggest {
    margin: 0;
    padding-left: 18px;
    font-size: 13px;
    line-height: 1.8;
    color: var(--text-secondary);
}

.ov-alert {
    color: #A32D2D;
}

.ov-ai__meta {
    margin-top: 4px;
    font-size: 11px;
    color: var(--text-secondary);
}

/* ---- AI 分析内容子卡（归档 088：五块内容各自独立成卡，标题风格对齐全站
   chart-card__title 左蓝条；#ovAiBody 仅作显隐分组、不带任何盒子样式） ---- */
.ov-ai-body[hidden] {
    display: none;
}

.ov-ai-card[hidden],
.ov-ai-card [hidden] {
    display: none;
}

.ov-ai-card .chart-card__title {
    display: flex;
    align-items: center;
}

.ov-ai-card .chart-card__title i {
    color: var(--primary);
    margin-right: 6px;
    font-size: 13px;
}

/* ---- AI 次日来园预测（归档 090）：大数字突出；底色用品牌光晕
   （不是渐变色块，不违 B 组渐变哨兵） ---- */
.ov-nextday {
    display: flex;
    align-items: baseline;
    gap: 4px;
    padding: 10px 12px;
    margin: 2px 0 8px;
    background: var(--brand-glow);
    border-radius: var(--radius-sm);
}

.ov-nextday__num {
    font-size: 34px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--primary);
}

.ov-nextday__unit {
    font-size: 13px;
    font-weight: 600;
    color: var(--primary);
}

.ov-nextday__sub {
    margin-left: auto;
    font-size: 11px;
    color: var(--text-secondary);
    text-align: right;
}

/* ---- 厨房过敏区 ---- */

.ov-allergy {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 8px;
}

.ov-allergy__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 7px 10px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-sm);
}

.ov-allergy__name {
    font-size: 14px;
    color: var(--text-primary);
}

.ov-allergy__num {
    font-size: 14px;
    font-weight: 600;
    color: #A32D2D;
}

.ov-allergy__src {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
}

.ov-allergy__srcTitle {
    font-size: 12px;
    color: var(--text-secondary);
}

.ov-allergy__tag {
    padding: 2px 8px;
    background: #FCEBEB;
    color: #A32D2D;
    border-radius: var(--radius-pill);
    font-size: 12px;
}

/* ============================================================
   🆕 归档 063：通知中心（铃铛 / 红点 / 横幅 / 通知列表）
   ============================================================ */

/* ---- 顶栏铃铛 + 未读红点 ---- */
.top-bar__bell {
    margin-left: auto;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-size: 20px;
    cursor: pointer;
    flex-shrink: 0;
}
.top-bar__bell:active { color: var(--primary); }

.top-bar__badge {
    position: absolute;
    top: 2px;
    right: 0;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: #E24B4A;
    color: #fff;
    font-size: 10px;
    line-height: 16px;
    text-align: center;
    font-weight: 600;
}

/* ---- 通知横幅 ----
   🔴 top 定位在顶栏**下方**（约 76px），不能盖住顶栏铃铛 —— 否则横幅弹出期间
      用户点不到铃铛（真机实测踩到：position:fixed + top:10px 会把铃铛整个盖住）。 */
.notif-banner {
    position: fixed;
    top: calc(76px + env(safe-area-inset-top, 0px));
    left: 12px;
    right: 12px;
    z-index: 200;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-hover);
    cursor: pointer;
    animation: notif-banner-in .25s ease;
}
/* 🔴 必须显式 display:none —— 上面 .notif-banner 的 display:flex 会盖掉 [hidden] 的 UA 样式，
   不写的话横幅会**一直显示**挡在页面顶部（与 .reason-modal[hidden]、.identity-page[hidden] 同一类坑）。 */
.notif-banner[hidden] { display: none; }
@keyframes notif-banner-in {
    from { opacity: 0; transform: translateY(-12px); }
    to { opacity: 1; transform: translateY(0); }
}
.notif-banner__icon {
    font-size: 18px;
    color: var(--primary);
    padding-top: 2px;
}
.notif-banner__body { flex: 1; min-width: 0; }
.notif-banner__title {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.notif-banner__content {
    font-size: 12px;
    color: var(--text-secondary);
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- 通知中心页 ---- */
.notif-page { padding: 4px 0 20px; }

.notif-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 12px;
}
.notif-filter {
    display: flex;
    gap: 4px;
    background: var(--muted-bg, #F1F5F9);
    border-radius: var(--radius-pill);
    padding: 3px;
}
.notif-filter__tab {
    border: none;
    background: transparent;
    padding: 6px 14px;
    font-size: 13px;
    color: var(--text-secondary);
    cursor: pointer;
    border-radius: var(--radius-pill);
    font-family: inherit;
}
.notif-filter__tab.is-active {
    background: var(--card-bg);
    color: var(--primary);
    font-weight: 600;
    box-shadow: var(--shadow);
}
.notif-read-all {
    border: 1px solid var(--border);
    background: var(--card-bg);
    color: var(--primary);
    padding: 6px 12px;
    font-size: 13px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: inherit;
}

.notif-list { display: flex; flex-direction: column; gap: 10px; }

.notif-item {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 12px 14px;
}
.notif-item--unread {
    border-left: 3px solid var(--primary);
}
.notif-item__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}
.notif-item__type {
    font-size: 11px;
    color: var(--primary);
    background: var(--primary-bg, #EFF6FF);
    padding: 1px 8px;
    border-radius: var(--radius-pill);
}
.notif-item__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #E24B4A;
}
.notif-item__title {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}
.notif-item__content {
    font-size: 13px;
    color: var(--text-secondary);
    margin-top: 4px;
    line-height: 1.6;
}
.notif-item__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid var(--border);
}
.notif-item__time {
    font-size: 12px;
    color: var(--text-light);
}
.notif-item__actions { display: flex; gap: 8px; }
.notif-item__btn {
    border: none;
    background: transparent;
    color: var(--primary);
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
    padding: 2px 4px;
}
.notif-item__btn--del { color: #DC2626; }

.notif-empty,
.notif-loading,
.notif-msg {
    text-align: center;
    color: var(--text-light);
    padding: 40px 0;
    font-size: 13px;
}
.notif-msg { color: #DC2626; }

/* ============================================================
   🆕 归档 064：过敏学生档案（聚合卡片 / 汇总卡 / 过敏史时间线）
   ============================================================ */

/* ---- 按幼儿聚合的档案卡片 ---- */
.ag-student-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
/* 归档 095：名单标题旁的计数（「共 N 人」，与分页口径一致） */
.ag-count {
    margin-left: 8px;
    font-size: 12px;
    font-weight: 400;
    color: var(--text-secondary);
    letter-spacing: 0;
}
.ag-student {
    position: relative;
    display: block;
    width: 100%;
    text-align: left;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 13px 40px 13px 14px;
    cursor: pointer;
    font-family: inherit;
    color: var(--text-primary);
    transition: box-shadow .15s ease, border-color .15s ease;
}
.ag-student:active { border-color: var(--primary); box-shadow: var(--shadow); }
.ag-student__head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 5px;
}
.ag-student__name {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}
.ag-student__meta {
    font-size: 12px;
    color: var(--text-secondary);
    margin-bottom: 7px;
}
.ag-student__allergens {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.ag-chip {
    font-size: 11px;
    color: var(--primary);
    background: var(--primary-bg, #EFF6FF);
    padding: 2px 8px;
    border-radius: var(--radius-pill);
}
.ag-student__arrow {
    position: absolute;
    right: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-light);
    font-size: 14px;
}

/* ---- 档案详情汇总卡 ---- */
.ag-summary {
    display: flex;
    gap: 10px;
}
.ag-summary__item {
    flex: 1;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 14px 8px;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.ag-summary__num {
    font-size: 20px;
    font-weight: 700;
    color: var(--text-primary);
}
.ag-summary__num--sev { color: #A32D2D; }
.ag-summary__label {
    font-size: 11px;
    color: var(--text-secondary);
}

/* ---- 过敏史时间线 ---- */
.ag-timeline {
    display: flex;
    flex-direction: column;
}
.ag-tl {
    position: relative;
    display: flex;
    gap: 12px;
    padding-bottom: 18px;
}
.ag-tl::before {
    content: '';
    position: absolute;
    left: 10px;
    top: 22px;
    bottom: 0;
    width: 2px;
    background: var(--border);
}
.ag-tl:last-child::before { display: none; }
.ag-tl__dot {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    color: #fff;
    background: #34A853;
    position: relative;
    z-index: 1;
}
.ag-tl--removed .ag-tl__dot { background: #9CA3AF; }
.ag-tl__body { flex: 1; min-width: 0; }
.ag-tl__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 3px;
}
.ag-tl__type {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}
.ag-tl--removed .ag-tl__type { color: var(--text-secondary); }
.ag-tl__time {
    font-size: 11px;
    color: var(--text-light);
}
.ag-tl__text {
    font-size: 12px;
    color: var(--text-secondary);
}

/* ============================================================
   🆕 2026-09-26 重做：更醒目、与卡片风格一致
   ============================================================ */
.pull-indicator {
    position: absolute;
    top: -44px;
    left: 50%;
    transform: translateX(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: auto;
    padding: 7px 16px;
    border-radius: 999px;
    background: var(--card-bg);
    border: var(--card-line);
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.12);
    font-size: 14px;
    font-weight: 600;
    color: var(--primary);
    pointer-events: none;
    transition: transform .12s ease;
    z-index: 5;
    white-space: nowrap;
}
.pull-indicator i {
    font-size: 15px;
    transition: transform .2s ease;
}
.pull-indicator--active i {
    transform: rotate(180deg);
}

/* ============================
   数据看板（归档 070）—— 园所页顶部四格
   ============================
   四个数来自同一个接口 `GET /api/school/dashboard`，口径全在服务端。
   🔴 这里刻意**不给数字上色**：看板是给人快速扫一眼的，
      四个数字四种颜色只会让「哪个数异常」这件事反而看不出来；
      颜色只用在左侧 3px 竖条上做分组标识，视觉重量留在数字本身。
   ============================ */
.dash-board {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    padding: 12px 12px 4px;
}

@media (min-width: 640px) {
    .dash-board {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

.dash-cell {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 12px 12px 11px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    border-left: 3px solid var(--primary);
}

.dash-cell[data-tone='green']  { border-left-color: #10B981; }
.dash-cell[data-tone='amber']  { border-left-color: #F59E0B; }
.dash-cell[data-tone='purple'] { border-left-color: #8B5CF6; }

.dash-cell__value {
    font-size: 24px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.dash-cell__label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-secondary);
}

.dash-cell__hint {
    font-size: 11px;
    color: var(--text-light);
}


/* ==========================================================================
   归档 `070` 补齐：管理模块子页用到而 CSS 未定义的 7 个类
   --------------------------------------------------------------------------
   🔴 归档 046 教训（style.css 里已有同款警示）：
      类名用了而 CSS 没定义 ⇒ 页面渲染成**裸 HTML**，不报错、不进控制台，
      只有靠「类名对账」才能发现。以下是 `.workbuddy/tmp/070/check_classes.js`
      扫出来的真实缺口 —— 不是预防性添加，是**已经缺了**。
   ========================================================================== */

/* ---- ① .cs-item 的「一行 = 左信息 + 右操作」变体（班级/账号/学生名单共用）----
   基础 .cs-item 是「整行可点」的勾选样式（cursor:pointer）。
   但名单行本身不可点 —— 可点的是右侧那几个按钮。若沿用 cursor:pointer，
   用户会点整行期待有反应，而实际什么都没发生。 */
.cs-item--row {
    justify-content: space-between;
    gap: 12px;
    cursor: default;
}

.cs-item--row .cs-item__name {
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* 🔴 必须 min-width:0：flex 子项默认 min-width:auto，长姓名不会收缩，
         会把右侧按钮挤出屏幕（窄屏尤其明显，本项目主战场就是窄屏）。 */
    min-width: 0;
    font-weight: 600;
}

/* 🆕 归档 `079`：**教职工名单行**（姓名+档案信息 / 徽标+操作按钮 分两行）。
   ----------------------------------------------------------------------------
   🔴 为什么必须分两行 —— 真机 390px 实拍到的缺陷（jsdom **不做布局**，抓不到）：
      一行里塞「姓名 + 绑定徽标 + 最多四颗操作按钮」时，`.cs-item__ops` 是
      `flex-shrink: 0` 而 `.cs-item__name` 是可收缩项 ⇒ 名字区被压到 **19~41px**，
      副标题（档案编号 · 档案手机号 · 登录手机号）被 `text-overflow: ellipsis`
      截成「**T1001…**」。
      而这三样恰恰是园长用来分辨「这份档案对应哪个登录账号」的**唯一线索** ——
      截掉之后，已绑定的行在界面上与未绑定行看不出区别，本页的核心信息就没了。
      （实测数据见 .workbuddy/tmp/staff-manage-browser/cdp-079.txt：
         T1001003/4/5 的 `名字宽 19px`，`sm-1-list.png` 里副标题只剩 `T1001…`。）

   ✅ 分两行的收益：第一行放得下完整姓名 + 完整档案信息；第二行徽标贴左、按钮贴右，
      按钮再多也是往右堆/换行，永远不会去抢名字的宽度。
   ⚠️ 只加在 `.cs-item--staff` 上（本页专用）—— `.cs-item--row` 还服务着班级管理
      与批量升级弹层，那里的行只有徽标没有按钮组，不需要也不该跟着变两行。 */
.cs-item--staff {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}

.cs-item--staff .cs-item__name {
    width: 100%;
}

.cs-item--staff .cs-item__ops {
    width: 100%;
    flex-wrap: wrap;
    align-items: center;
}

/* 徽标贴左、按钮贴右：徽标是 `.cs-item__ops` 的第一个子元素，
   用 `margin-right: auto` 把它之后的内容整体推向右端（不必再包一层容器）。 */
.cs-item--staff .cs-item__ops > .mg-badge {
    margin-right: auto;
}

/* 宽屏（≥640px）恢复单行 —— 那里放得下，单行更像「名单」而不像卡片堆。
   与 `.dash-board` 由 2 列变 4 列用的是同一个断点。 */
@media (min-width: 640px) {
    .cs-item--staff {
        flex-direction: row;
        align-items: center;
    }
    .cs-item--staff .cs-item__name {
        width: auto;
        flex: 1;
    }
    .cs-item--staff .cs-item__ops {
        width: auto;
    }
}

/* ============================================================================
   🆕 归档 `079`：两个新页的 **hidden 属性兜底**
   ----------------------------------------------------------------------------
   🔴🔴 本项目**没有**全局 `[hidden]{display:none!important}` —— style.css 里明确写着
      「之所以没并成一条全局规则…是因为那会改变全站渲染，需单独评估后再做」（见 `.setting-item` 那段）。
      于是每个页面各自留一条**作用域兜底**：`.identity-page [hidden]` /
      `.reason-modal [hidden]` / `.ov-ai [hidden]` / `.notif-banner[hidden]` /
      `.ov-heatmap__detail[hidden]` / `.settings-sheet [hidden]` / `.setting-item[hidden]`。
   🔴 缺了这条的页面，凡被 JS 用 `hidden` 显隐的元素**全都藏不住** —— 因为
      「作者样式的 display 恒高于浏览器默认样式表」（UA 的 `[hidden]{display:none}` 被盖掉）。
      实测（CDP，390×844，见 .workbuddy/tmp/staff-manage-browser/cdp-079.txt 的
      `[hidden 生效性]` 一行）：本次两个新页共 8 个元素失效，后果分两类：
        ① **权限门形同虚设** —— `#amReviewBtn` / `#amCreateBtn` / `#amReload` /
           `#spSave` / `#spBack` 都是 `.identity-btn`（`display:inline-flex`）⇒
           **没有 `manage.staff.*` 权限的园长照样看得见按钮**。权限判定完全正确、
           控制台一句报错都没有，只有界面上多了一颗不该有的按钮。
        ② **空态 / 目标块同时显示** —— `#amStats`（`.dash-board`，grid）、
           `#amList`（`.mg-list`，flex）、`#spTarget`（`.identity-teacher`，flex）、
           `#spNoTargetActions`（`.identity-actions`，flex）⇒ 未选目标时
           「返回教职工管理」按钮会与空态一起出现。
   ✅ 修法：给**两个页根**加一条作用域兜底。选择器特异性 (0,2,0) 高于
      `.identity-btn` / `.dash-board` / `.mg-list` 等 (0,1,0) ⇒ 不必用 `!important`，
      与既有的 `.identity-page [hidden]` 写法保持一致。
   ⚠️ 这类「作者样式盖掉 hidden」的毛病，**jsdom 测不出来**（它不做级联计算），
      只能靠真机探针（verify-staff-manage-browser.js 的 hidden 生效性扫描）拦。
   ⚠️ 同类问题在 `.page-class-manage` / `.page-student-manage` / `.page-teacher-class`
      上同样存在（它们也没有兜底），影响各自的 `.identity-btn` 入口按钮；
      本轮**未动**（不在 079 范围内），已记入归档 079 的待确认清单。
   ============================================================================ */
/* 🆕 归档 `080`：第三个同类页（校内权限管理）。本页有两个 flex 容器会盖掉 UA 的
   `[hidden]` —— `#scpRoleBulk`（`.scp-bulk`，flex）与 `.identity-actions`（flex）；
   若不兜底，「批量设为」工具条与「保存」按钮会在**不该出现的时候**照样显示
   （例如一个可分配角色都没有时工具条本应藏起来），而控制台一句报错都没有。
   🔴 注释**不能夹在选择器列表的逗号之间** —— 本项目 A37g 断言认的形态是
      「三个选择器紧挨着、紧跟 `{ display:none`」，注释放中间会让正则失配
      （本轮实际踩到一次：功能完全正常，只有对账红了）。 */
.page-account-manage [hidden],
.page-staff-permission [hidden],
.page-school-permission [hidden] {
    display: none;
}

/* ============================================================================
   🆕 归档 `107`：两个新子页的页面级样式（全体教职工 / 限时开放注册）
   ----------------------------------------------------------------------------
   🔴 背景：归档 `107` 建了 pages/staff-all.html 与 pages/open-register-admin.html，
      功能、接线、权限门控都齐，但**页面级 CSS 从未编写**。真机实测
      （.workbuddy/tools/probes/smoke-107.js 的计算样式诊断）：
        · 搜索框   `border-style:inset / border-radius:0px / padding:0px`
        · 筛选按钮 `background:rgb(240,240,240) / border-radius:0px`
        ⇒ 都是浏览器**原生控件**外观，与全站设计语言明显割裂。
        · `.ora-link__row` 未设 flex ⇒「复制」按钮独占整行（实测宽 358px）。
   ✅ 本轮按既有范式补齐，不新造观感：
        · 筛选胶囊 → 抄 .notif-filter / .notif-filter__tab（本文件 notification 段）
        · 输入框   → 与 .sc-input 同值（发丝描边 + radius-sm + 43px 最小高）
        · 紧凑返回 → 抄 .att-back（胶囊小按钮，与标题同一行，不再独占整行）
   ⚠️ 位置：本块必须在 .identity-btn / .sc-input 等基础件**之后**（文件靠后），
      否则同权重的 `.sa-back` / `.ora-link__row .identity-btn` 会被
      `.identity-btn{width:100%}` 盖回通栏（基础件规则在前，同权重后者胜）。
   ============================================================================ */

/* ---- ① 页头：紧凑返回 + 标题同一行 ---- */
.sa-head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.sa-head .section-title {
    margin: 0;
    flex: 1;
    min-width: 0;
}

/* 覆盖 .identity-btn 的通栏 46px —— 本页返回是「胶囊小按钮」，与标题同行 */
.sa-back {
    width: auto;
    height: 34px;
    padding: 0 14px;
    font-size: 13px;
    flex-shrink: 0;
}

/* ---- ② 工具栏：搜索 + 筛选（竖排两行，与移动端窄屏相配）---- */
.sa-toolbar {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 12px 0;
}

.sa-search {
    position: relative;
    display: flex;
    align-items: center;
}

.sa-search > i {
    position: absolute;
    left: 12px;
    font-size: 14px;
    color: var(--text-light);
    pointer-events: none;
}

.sa-search__input {
    width: 100%;
    padding: 10px 12px 10px 34px;
    border: var(--card-line);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    font-family: inherit;
    font-size: 14px;
    color: var(--text-primary);
    line-height: 1.4;
    min-height: 43px;
    box-sizing: border-box;
    /* 🔴 type="search" 在 Chromium / iOS Safari 下自带圆角、白色内阴影与「×」清除钮，
       不压平就与 .sc-input 长得不一样（真机实测 border-style 仍是 inset）。 */
    -webkit-appearance: none;
    appearance: none;
}

.sa-search__input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

.sa-search__input:focus {
    outline: none;
    border-color: var(--primary);
}

/* 筛选：浅底胶囊 + 选中项白底浮起（= .notif-filter 那一套） */
.sa-filter {
    display: inline-flex;
    align-self: flex-start;
    gap: 4px;
    background: var(--muted-bg, #F1F5F9);
    border-radius: var(--radius-pill);
    padding: 3px;
}

.sa-filter__btn {
    border: none;
    background: transparent;
    padding: 6px 14px;
    font-size: 13px;
    color: var(--text-secondary);
    cursor: pointer;
    border-radius: var(--radius-pill);
    font-family: inherit;
}

.sa-filter__btn--on {
    background: var(--card-bg);
    color: var(--primary);
    font-weight: 600;
    box-shadow: var(--shadow);
}

/* ---- ③ 「限时开放注册」生成后的可复制链接框 ---- */
.ora-link {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 12px;
}

.ora-link__row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ora-link__row .sc-input {
    flex: 1;
    min-width: 0;
}

/* 复制按钮在行内，别再撑满整行（覆盖 .identity-btn{width:100%}） */
.ora-link__row .identity-btn {
    width: auto;
    padding: 0 16px;
    flex-shrink: 0;
}

/* ---- ③ 注册码行（.ora-item）：窄屏两行，避免「注册码·令牌 + 剩余 + 两按钮」挤一行溢出 ----
   仿归档 `079` `.cs-item--staff` 的范式。`.cs-item--row` 默认把 name / meta / ops
   三个 flex 子项挤在一行，令牌前 12 字符 +「剩余 xx」+「复制 吊销」在 390px 单行
   放不下，`name` 被压到溢出（实测 scrollWidth 比 clientWidth 大 22px，与「剩余」粘连）。
   做法：name 占满第一行（flex-basis:100%），meta + ops 自然换到第二行；
   容器保留 `.cs-item--row` 的 justify-content:space-between，第二行 meta 贴左、ops 贴右。 */
.ora-item {
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.ora-item .cs-item__name {
    flex: 0 0 100%;
}

/* 宽屏（≥640px）恢复单行 —— 与 `.cs-item--staff` 同一断点 */
@media (min-width: 640px) {
    .ora-item {
        flex-wrap: nowrap;
    }
    .ora-item .cs-item__name {
        flex: 0 1 auto;
    }
}

/* ---- ④ 两页的 [hidden] 兜底（归档 079 同类坑）----
   🔴 本块给 `.ora-link` 加了 `display:flex`、给 `.sa-*` 加了 `display`；
      作者样式一旦写了 display，就会盖掉 UA 的 `[hidden]{display:none}` ⇒
      `#oraLinkWrap[hidden]`（生成前本应藏起来）会一直显示。
      ⚠️ **不能**并进上方 079/080 那条三选择器规则 —— verify-staff-manage.js 的
         A37g 用的是「三个选择器紧挨着、紧跟 `{ display:none`」的定形正则，
         塞第四个选择器会让它失配（功能正常、只有对账红）。故另起一条。 */
.page-staff-all [hidden],
.page-open-register-admin [hidden] {
    display: none;
}

/* 注册角色标签 + 注册页角色提示 */
.ora-role-tag {
    color: var(--primary, #2563EB);
    font-weight: 600;
}
.ora-role {
    margin: 4px 0 10px;
    font-size: 13px;
    color: var(--text-secondary, #64748B);
}

/* ---- ⑤ 注册二维码 + 弹层（归档 107 续：生成可扫的园所注册码）---- */
.ora-qr {
    display: flex;
    justify-content: center;
    background: #fff;
    border: 1px solid var(--card-line, #E2E8F0);
    border-radius: var(--radius-sm, 8px);
    padding: 12px;
    margin: 8px 0;
    max-width: 220px;
}
.ora-qr svg {
    width: 100%;
    height: auto;
    display: block;
}
.ora-qr--lg {
    max-width: 260px;
}

/* 注册码二维码弹层（点列表「二维码」时显示）；position:fixed 脱离 SPA 容器、相对视口 */
.ora-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
}
.ora-modal[hidden] {
    display: none;
}
.ora-modal__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, .45);
}
.ora-modal__box {
    position: relative;
    background: var(--card-bg, #fff);
    border-radius: var(--radius, 12px);
    padding: 16px;
    width: min(86vw, 320px);
    box-shadow: var(--shadow, 0 8px 24px rgba(15, 23, 42, .18));
}
.ora-modal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 600;
    margin-bottom: 8px;
}
.ora-modal__close {
    border: none;
    background: transparent;
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    color: var(--text-secondary, #64748B);
}

/* 必填项红色星号（公开注册页身份证号） */
.sc-req {
    color: var(--danger, #E11D48);
    margin-left: 2px;
}

/* 🆕 归档 `078`：班级卡片整行可点（点一下开「编辑 / 停用 / 删除」弹层）。
   ⚠️ `.cs-item--row` 默认 `cursor: default`（那是「只读展示行」的语义，
      比如学生名单行右侧才有按钮）—— 班级卡片整行是**操作入口**，必须显式改回 pointer，
      否则用户不会想到可以点。 */
.cs-item--tap {
    cursor: pointer;
}

.cs-item--tap:active {
    background: var(--primary-bg);
}

/* 批量升级弹层里「已是最高年级、不可再升」的班级行。
   仍然展示（园长需要看到它为什么不参与升级），只是整行调暗 + 勾选框禁用。 */
.cs-item--disabled {
    opacity: 0.55;
}

/* 🆕 归档 `078`：班级卡片右端的「可点」指示箭头。
   🔴🔴 **绝不能复用 `.cs-item__meta`** —— 那个类带 `font-weight: 400`，
   而 FA7 Free 的 solid / regular **共用同一个 family 名**（`Font Awesome 7 Free`），
   靠**字重**去选字体文件（solid=900 / regular=400）。
   字重被改成 400 ⇒ 浏览器去加载只含 regular 图标的那份字体
   ⇒ solid 的 `\f054` 缺字形 ⇒ **真机上显示成一个豆腐块**（本轮 CDP 实拍到）。
   ✅ 正确写法与既有的 `.app-card__arrow` / `.identity-option__arrow` /
      `.ag-student__arrow` 完全一致：**只设 `font-size` 与 `color`，绝不碰 `font-weight`**。
   ⚠️ 注意 `fa-plus` 这类「字符本身就在系统字体里」的图标看不出问题
      （`+` 任何字体都有），所以这个坑只在 chevron / 图形类图标上暴露 ——
      不能靠「加号看着正常」推断字体没问题。 */
.cs-item__chev {
    font-size: 12px;
    color: var(--text-light);
}

/* 🆕 归档 `078`：把勾选行的「名字」竖排（名字一行、说明一行）。
   与 `.cs-item--row .cs-item__name` 同一套写法，只是不改外层对齐。
   🔴 为什么必须竖排：`.cs-item__name` 默认是行内流向，`<small class="cs-item__meta">`
     会**紧贴**在名字后面 ⇒ 真机上渲染成「小一班小班 → 中班」「毕业班（2026 届）已是
     「毕业班」，不能继续升级」这种连成一串的样子，读不出哪段是名字、哪段是说明。
   （批量升级弹层实拍到的，见 .workbuddy/ 下的 CDP 出图。） */
.cs-item--stack .cs-item__name {
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* 🔴 必须 min-width:0：flex 子项默认 min-width:auto，长名字不收缩会撑破卡片 */
    min-width: 0;
}

/* 姓名下方的次要信息（手机号 · 编号 · 状态 · 时间） */
.cs-item__meta {
    font-size: 12px;
    font-weight: 400;
    color: var(--text-light);
    /* 单行省略，避免长 meta 撑破卡片 */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---- ② 行内操作按钮组 ----
   与 .mg-btn 同一套观感（胶囊 + 发丝描边 + 中性字色），
   刻意不新造一套 —— 同一个项目里两套「小按钮」必然长歪。 */
.cs-item__ops {
    display: flex;
    flex-shrink: 0;
    gap: 6px;
}

.cs-item__op {
    border: var(--card-line);
    background: var(--card-bg);
    color: var(--text-primary);
    border-radius: var(--radius-pill);
    padding: 5px 12px;
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    transition: var(--transition);
}

.cs-item__op:active {
    background: var(--primary-bg);
}

.cs-item__op--danger {
    color: #A32D2D;
    border-color: #F5C6C6;
    background: #FCEBEB;
}

.cs-item__op[disabled] {
    opacity: 0.5;
    cursor: default;
}

/* ---- ③ 底部弹层的「危险」主按钮（驳回 / 删除 一类不可逆动作）----
   🔴 只有危险动作才用红色。全站蓝渐变只许主按钮，
      所以这里也**不能**给 danger 配渐变 —— 红底红字 + 红描边即可。 */
.settings-sheet__btn--danger {
    border-color: #F5C6C6;
    background: #FCEBEB;
    color: #A32D2D;
}

.settings-sheet__btn--danger:disabled {
    opacity: 0.5;
    cursor: default;
}

/* ---- ④ 权限树模块头里的名称（让右侧的 perm_code 靠边对齐）---- */
.pa-module__name {
    flex: 1;
}


/* ---- 归档 `070`：`.mg-result` 的键 / 值两栏 ----
   `.mg-result__row` 本身已是 `justify-content: space-between` 的两栏 flex，
   但**值的长度差异很大**（「必填」vs「选填，填班级名称；留空则进下方的缺省班级」）——
   纯靠 space-between 会让键跟着值的长短左右浮动，几行读下来参差不齐。
   拆成键 / 值两个类：键固定不缩、值右对齐，多行才好扫。
   （`.mg-result` 当前唯一在用消费者是「学生管理 → 导入学生」。） */
.mg-result__key {
    flex-shrink: 0;
    color: var(--text-secondary);
    font-weight: 600;
}

.mg-result__val {
    text-align: right;
    color: var(--text-primary);
}


/* ============================================================================
   🆕🆕 归档 `080`：「校内权限管理」页（pages/school-permission.html）
   ----------------------------------------------------------------------------
   三个页签的行样式各有归属：
     · 页签① 一人一行（勾选 + 姓名/手机号 + **角色下拉**）→ 本段新建 `.scp-row`
     · 页签② 一组一行（整行可点开弹层）        → 复用 `.cs-item--row` / `--tap` / `--stack`
     · 页签③ 一人一行（点进权限调整子页）      → 同上
   ⇒ 只有页签① 需要新类：既有的 `.cs-item`（整行勾选）与 `.cs-item--row`
     （左信息 + 右**按钮组** `.cs-item__ops`）都不合适 ——
     下拉框是表单控件，塞进那套内建按钮样式里会走形。
   ============================================================================ */

/* ---- 页签① 的一行 ---- */
.scp-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--card-bg);
    border-radius: var(--radius-md);
    border: var(--card-line);
    margin-bottom: 8px;
}

/* 勾选框：与既有 `.cs-item__box` 同尺寸，免得一页里出现两种大小的勾选框 */
.scp-row__box {
    flex: none;
    width: 18px;
    height: 18px;
    accent-color: var(--primary);
    cursor: pointer;
}

/* 姓名 + 手机号。`<label for=勾选框>` ⇒ 点这里也能切换勾选（比只点小方框好按得多） */
.scp-row__body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    cursor: pointer;
}

.scp-row__name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.scp-row__meta {
    font-size: 12px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 角色下拉：**不许被压爆**。
   归档 `079` 在 `.cs-item__ops` 上实测过同类问题（可收缩的名字区把右侧挤到 19~41px）。
   这里方向相反：信息区可收缩、下拉固定不缩。
   宽度取 11em 是为了「教职工-一线教师（17 项）」这种长选项不至于只剩两个字；
   上限 46% 保证窄屏上名字仍有一半空间。 */
.scp-row__sel {
    flex: none;
    width: 11em;
    max-width: 46%;
}

@media (max-width: 480px) {
    .scp-row {
        flex-wrap: wrap;
    }
    .scp-row__sel {
        width: 100%;
        max-width: none;
    }
}

/* ---- 页签① 的批量工具条 ----
   ⚠️ 里面的按钮必须显式 `width: auto`：`.identity-btn` 是 `width: 100%`，
      在 flex 容器里会各自撑满一行，工具条直接变成一列。 */
.scp-bulk {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    margin-bottom: 12px;
    background: var(--primary-bg);
    border-radius: var(--radius-md);
}

.scp-bulk__all,
.scp-bulk__btn {
    width: auto;
}

/* 「已选 N 人」推到左侧、把后面的控件挤到右边（`margin-right:auto` 是 flex 里的标准手法） */
.scp-bulk__picked {
    font-size: 13px;
    color: var(--text-secondary);
    margin-right: auto;
}

.scp-bulk__label {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.scp-bulk__select {
    width: auto;
    min-width: 9em;
    max-width: 100%;
}

/* ---- 「组名 + 徽标」一行（页签②③ 的行标题）---- */
.scp-line__title {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

/* ---- 组操作弹层里的竖排动作按钮 ---- */
.scp-sheet-btns {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 14px;
}

/* ---- 弹层里的表单字段 ----
   🔴 刻意**不复用** `.reason-modal__field` / `.reason-modal__label`：
      那两个类的 `color: var(--text)` 引用了一个**从未定义**的 CSS 变量
      （style.css 通篇只有 `--text-primary`；`var(--text)` 全项目仅 4 处，
      且全在 `.reason-modal__*` 里）⇒ 它们的颜色一直是浏览器的「继承值」而不是本意。
      本轮**不去顺手改**那 4 处（会改变出勤/离园工单弹层的视觉，不在 080 范围内，
      已记入待办），本页自建一套颜色明确的。 */
.scp-field {
    margin-bottom: 14px;
}

.scp-field:last-child {
    margin-bottom: 0;
}

.scp-field__label {
    display: block;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 8px;
}


/* ==========================================================================
   过敏档案（pages/allergy.html）—— 弹窗化改造
   --------------------------------------------------------------------------
   ① 需求原文：默认列出全部已有登记的幼儿；点记录弹窗修改 / 解除；
      页面下方「新增过敏信息」→ 弹窗先选幼儿再登记。
   ② 弹窗挂 document.body（与 js/reason-modal.js 同路），故样式与页面解耦。
   ========================================================================== */

/* 🔴 本项目**没有**全局 [hidden] 规则（归档 079 实证：作者样式带 display 就会盖掉
     UA 的 [hidden]{display:none}）⇒ 本页凡用 hidden 显隐的元素都要留这条兜底。
     特异性 (0,2,0) > `.identity-msg` 等 (0,1,0)，**不需要 !important**。 */
.page-allergy [hidden] {
    display: none;
}

/* 新增过敏信息（页面下方主操作按钮） */
.ag-add {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin-top: 14px;
    padding: 13px 16px;
    font-size: 15px;
    font-weight: 600;
    color: #FFFFFF;
    /* 与主操作按钮同款渐变（已登记进 verify-theme-consistency.js 的 GRADIENT_OK 白名单） */
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    border: none;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    cursor: pointer;
}

.ag-add:active {
    transform: scale(0.98);
}

/* 🔴 图标单独一个类且**绝不设 font-weight**：
   FA7 Free 的 solid / regular 共用 family 名、靠 font-weight 选字体文件（solid=900）。
   给 <i> 套一个带 font-weight:400 的文本类会把 900 打掉 ⇒ 字形缺字渲染成 ▯（归档 078）。 */
.ag-add i {
    font-size: 14px;
}

/* 过敏记录：整条可点（<button> 化，须重置 button 默认样式） */
.mg-row--tap {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.mg-row--tap:active {
    transform: scale(0.99);
}

.mg-row__arrow {
    flex-shrink: 0;
    font-size: 12px;
    color: var(--text-light);
}

/* ---- 底部弹层 .ag-sheet ---- */
.ag-sheet {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: flex-end;
}

.ag-sheet__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}

.ag-sheet__panel {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    /* 🔴 归档 `084` 二次改造：宽屏（桌面 / 平板）下弹窗不再拉满整屏，
       封顶 480px 并水平居中；窄屏（手机）仍按 100% 铺满。 */
    max-width: 480px;
    margin: 0 auto;
    max-height: 88vh;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    background: var(--card-bg);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: 0 -8px 24px rgba(15, 23, 42, 0.18);
}

.ag-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}

.ag-sheet__title {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
}

.ag-sheet__close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    font-size: 15px;
    color: var(--text-secondary);
    background: transparent;
    border: var(--card-line);
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.ag-sheet__body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.ag-sheet__body .sc-field {
    margin-bottom: 12px;
}

.ag-sheet__body .pa-select,
.ag-sheet__body .sc-input {
    width: 100%;
}

.ag-sheet__actions {
    display: flex;
    gap: 10px;
    margin-top: 14px;
}

.ag-sheet__btn {
    flex: 1;
    padding: 12px 10px;
    font-size: 15px;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.ag-sheet__btn:active {
    transform: scale(0.98);
}

.ag-sheet__btn--primary {
    font-weight: 600;
    color: #FFFFFF;
    border: none;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
}

.ag-sheet__btn--danger {
    font-weight: 600;
    color: #FFFFFF;
    border: none;
    background: #DC2626;
}

.ag-sheet__btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* 同 .page-allergy：弹窗内部也依赖 hidden 显隐（幼儿选择块 / 解除按钮 / 错误提示） */
.ag-sheet [hidden] {
    display: none;
}

/* 弹窗里的多行文本（工作内容）—— `.sc-input` 是单行控件的样式，textarea 要另给高度 */
.ag-sheet__body textarea.sc-input {
    min-height: 76px;
    resize: vertical;
}


/* ==========================================================================
   工作计划（pages/workplan.html）—— 归档 `084`
   ========================================================================== */

/* 🔴 同 .page-allergy：本项目没有全局 [hidden] 规则，本页用 hidden 显隐必须留兜底 */
.page-workplan [hidden] {
    display: none;
}

/* 分页控件 */
.wp-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 14px;
}

.wp-pager__info {
    flex: 1;
    text-align: center;
    font-size: 13px;
    color: var(--text-secondary);
}

.wp-pager .mg-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* 列表行里「本周暂无内容」的弱化提示 */
.wp-row__meta--empty {
    color: var(--text-light);
    font-style: italic;
}

/* 详情（键值两列） */
.wp-detail__row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 10px 0;
    border-bottom: var(--card-line);
}

.wp-detail__row:last-child {
    border-bottom: none;
}

.wp-detail__label {
    flex-shrink: 0;
    width: 84px;
    font-size: 13px;
    color: var(--text-secondary);
}

.wp-detail__value {
    flex: 1;
    min-width: 0;
    font-size: 14px;
    color: var(--text-primary);
    /* 工作内容可能很长 —— 保留换行、不让它把行撑破 */
    white-space: pre-wrap;
    word-break: break-word;
}

/* 🆕 归档 084·三次改造：创建工作周按钮移至页面顶部，去掉自带的上边距避免顶部留白过大 */
.page-workplan .wp-head .ag-add {
    margin-top: 0;
}

/* 🆕 归档 084·三次改造：弹窗内两字段同行。
   每个 .sc-field flex:1 + min-width:0 —— 输入框 / 下拉随屏收缩，
   绝不挤出屏幕、也绝不顶大弹窗（窄屏两个日期框也能并排且不被截断）。 */
.ag-sheet__row2 {
    display: flex;
    gap: 10px;
    margin-bottom: 12px;
    /* 🆕 归档 090：两个字段按「顶边对齐」而非默认的 stretch，
       避免一侧字段（如 iOS 原生日期控件）更高时把另一侧拉伸出空白，
       使两个输入框上下缘看起来不在同一条线上。 */
    align-items: flex-start;
}

.ag-sheet__row2 .sc-field {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
    /* 🆕 归档 090：字段自身也是 column 容器 —— 让标签 + 输入框自然堆叠，
       不因外部拉伸而在输入框上下留出不等间隙。 */
    align-self: flex-start;
}

/* 🆕 归档 085：iOS Safari 上 <input type="date"> 带有内在最小宽度，
   若输入框自身不把 min-width 压到 0，它会撑出弹性槽（每个 .sc-field 本应约 50% 宽），
   两个日期框互相挤压、并把页面横向撑宽（出现横向滚动）。
   这里给行内输入框 / 下拉补 min-width:0，使其随屏收缩到 100% 槽宽。 */
.ag-sheet__row2 .sc-input,
.ag-sheet__row2 .pa-select {
    width: 100%;
    min-width: 0;
}

/* 🆕 归档 090：同行内的日期 / 时间 / 下拉控件强制等高（min-height 已在 .sc-input 统一定为 43px）。
   真机现象：iOS 上「开始日期」框比「结束日期」框高 ⇒ 两框上下缘错位（截图 bc78ac2b…）。
   根因是 iOS 原生 date 控件的内在高度；统一 appearance + min-height + 这里再兜一次 height，
   三处叠加确保任何平台、任何字段组合都等高对齐。 */
.ag-sheet__row2 .sc-input,
.ag-sheet__row2 .pa-select,
.ag-sheet__row2 .sc-input[type="date"],
.ag-sheet__row2 .sc-input[type="time"] {
    min-height: 43px;
    box-sizing: border-box;
}

/* 🆕 归档 085：兜底 —— 弹窗面板本身绝不横向溢出（双保险，根因仍是上面的 min-width:0） */
.ag-sheet__panel {
    overflow-x: hidden;
}

/* 🆕 归档 084·三次改造：详情页底部「返回列表 + 编辑」同一行，两颗按钮等宽铺满 */
.wp-detail__actions {
    display: flex;
    gap: 12px;
}

.wp-detail__actions .identity-btn {
    flex: 1;
}

/* 🆕 归档 085：详情内联日工作计划 + 日工作调整子视图 */
.wp-detail__daily { margin-top: 12px; }
.wp-detail__daily .wp-daily__empty,
.wp-daily__empty {
    font-size: 13px;
    color: var(--text-light);
    padding: 8px 0;
}
.wp-daily__range { font-size: 13px; color: var(--text-light); margin: 4px 0 0; }
.wp-daily__day {
    border: var(--card-line);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin-bottom: 10px;
    background: var(--card-bg);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
}
.wp-daily__day-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}
.wp-daily__date { font-size: 14px; font-weight: 600; color: var(--text-primary); }

/* 🆕 归档 090：卡片内「新增」按钮 */
.wp-daily__day-add {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 600;
    color: var(--primary-dark);
    background: rgba(59, 130, 246, 0.08);
    border: 1px solid rgba(59, 130, 246, 0.35);
    border-radius: 999px;
    padding: 3px 10px;
    cursor: pointer;
    font-family: inherit;
}
.wp-daily__day-add:hover { background: rgba(59, 130, 246, 0.16); }
.wp-daily__day-add:active { transform: scale(0.97); }

.wp-daily__periods { display: flex; flex-direction: column; gap: 8px; }
/* 🆕 归档 090：时段分区容器（标题 + 多项列表） */
.wp-daily__period {
    border: var(--card-line);
    border-radius: var(--radius-sm);
    padding: 6px 8px;
}
.wp-daily__period-head {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 4px;
}
.wp-daily__items { display: flex; flex-direction: column; gap: 4px; }
.wp-daily__period-empty {
    font-size: 13px;
    color: var(--text-light);
    margin: 2px 0;
}

/* 🆕 归档 090：单项（时间 + 内容 + 编辑/删除） */
.wp-daily__item {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
    background: rgba(59, 130, 246, 0.06);
    border: 1px solid rgba(59, 130, 246, 0.22);
}
.wp-daily__item--empty { background: rgba(148, 163, 184, 0.08); border-color: rgba(148, 163, 184, 0.25); }
.wp-daily__item-time {
    flex: 0 0 auto;
    font-size: 12px;
    font-weight: 600;
    color: var(--primary-dark);
    font-variant-numeric: tabular-nums;
    min-width: 40px;
}
.wp-daily__item-content {
    flex: 1 1 auto;
    font-size: 13px;
    color: var(--text-primary);
    word-break: break-word;
    white-space: pre-line;
}
.wp-daily__item-content--empty { color: var(--text-light); }
.wp-daily__item-actions { flex: 0 0 auto; display: inline-flex; gap: 2px; }
.wp-daily__item-edit,
.wp-daily__item-del {
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-size: 12px;
    padding: 3px 6px;
    border-radius: 6px;
    cursor: pointer;
    line-height: 1;
}
.wp-daily__item-edit:hover { color: var(--primary-dark); background: rgba(59, 130, 246, 0.12); }
.wp-daily__item-del:hover { color: #dc2626; background: rgba(220, 38, 38, 0.10); }

/* 🆕 归档 090：新增/编辑弹窗内 —— 多行自增表单 */
.wp-daily__sheet-date {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 6px;
}
.wp-daily__rows { display: flex; flex-direction: column; gap: 10px; }
.wp-daily__row {
    display: flex;
    align-items: stretch;
    gap: 8px;
    border: var(--card-line);
    border-radius: var(--radius-sm);
    padding: 8px;
}
.wp-daily__row-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px; }
.wp-daily__row-line { display: flex; gap: 8px; }
.wp-daily__row-period { flex: 0 0 96px; }
.wp-daily__row-time { flex: 1 1 auto; }
.wp-daily__row-content { width: 100%; resize: vertical; }
.wp-daily__row-del {
    flex: 0 0 auto;
    align-self: center;
    border: none;
    background: transparent;
    color: var(--text-light);
    font-size: 14px;
    padding: 6px;
    border-radius: 6px;
    cursor: pointer;
}
.wp-daily__row-del:hover { color: #dc2626; background: rgba(220, 38, 38, 0.10); }
.wp-daily__add-row {
    width: 100%;
    margin-top: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--primary-dark);
    background: rgba(59, 130, 246, 0.06);
    border: 1px dashed rgba(59, 130, 246, 0.45);
    border-radius: var(--radius-sm);
    padding: 8px;
    cursor: pointer;
    font-family: inherit;
}
.wp-daily__add-row:hover { background: rgba(59, 130, 246, 0.12); }


/* ==================================================================
   智能考勤-管理（归档 `083`：pages/smart-attendance.html）
   ------------------------------------------------------------------
   ① 顶部五个数值卡（总人数做 hero 通栏，其余 2×2）
   ② 考勤详情：表头 + 行列表（当前恒为空态）
   ③ 底部两颗主按钮 → 两个底部弹层（补签二维码 / 碰一碰点位）
   ================================================================== */

/* 「样式阶段」提示条：把「为什么数值全是 —」讲清楚 */
.sm-stage {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin: 0 0 14px;
    padding: 10px 12px;
    font-size: 12px;
    line-height: 1.6;
    color: #854F0B;
    background: #FAEEDA;
    border-radius: var(--radius-md);
}

.sm-stage i {
    margin-top: 2px;
    flex-shrink: 0;
}

.sm-block {
    margin-bottom: 18px;
}

/* 区块标题：与全站统一（15px / 700 / 深色 + 左侧品牌色条）。
   本页不用 .settings-section（那会带出一整套设置页的卡片间距），故在此显式写一次。 */
.page-sm .section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.page-sm .section-title::before {
    content: '';
    width: 3px;
    height: 14px;
    border-radius: 2px;
    background: var(--primary);
    flex-shrink: 0;
}

/* ---- ① 数值卡 ---- */
.sm-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
}

/* 🔴 minmax(0, 1fr) 的 0 不能省：裸 1fr 的最小值是 auto，长标签会把网格撑破 */
.sm-stat {
    padding: 12px 14px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
}

/* 总人数是这一屏的锚点 ⇒ 通栏放大，其余四项退为 2×2 */
.sm-stat--hero {
    grid-column: 1 / -1;
    padding: 14px 16px;
}

.sm-stat__label {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    color: var(--text-secondary);
}

.sm-stat__label i {
    color: var(--primary);
}

.sm-stat__value {
    display: block;
    margin-top: 6px;
    font-size: 24px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-primary);
}

/* 「—」是占位符不是数字：用次级色，别让人当成 0 */
.sm-stat__value:empty::before,
.sm-stat__value[data-empty] {
    color: var(--text-light);
}

.sm-stat--hero .sm-stat__value {
    font-size: 30px;
}

.sm-stat__sub {
    display: block;
    margin-top: 4px;
    font-size: 11px;
    color: var(--text-light);
}

/* 数值卡的图标底色：与 .app-card 同一套色板，改这里即可换色 */
.sm-stat[data-tone="blue"]   .sm-stat__label i { color: #185FA5; }
.sm-stat[data-tone="green"]  .sm-stat__label i { color: #0F6E56; }
.sm-stat[data-tone="amber"]  .sm-stat__label i { color: #854F0B; }
.sm-stat[data-tone="purple"] .sm-stat__label i { color: #3C3489; }
.sm-stat[data-tone="teal"]   .sm-stat__label i { color: #0F6E56; }
.sm-stat[data-tone="red"]    .sm-stat__label i { color: #C0392B; }

/* 🆕 归档 `095`：统计卡数字可点击下钻 + 左右（早/午）拆分 */
.sm-stat__value--split {
    display: flex;
    align-items: stretch;
    justify-content: space-between;
    gap: 6px;
    font-size: 24px;
}

/* 数字本身是一个 button：去掉 UA 默认外观，做成可点的大字 */
.sm-stat__num {
    -webkit-appearance: none;
    appearance: none;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    margin: 0;
    padding: 2px 4px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-primary);
    font: inherit;
    line-height: 1.05;
    cursor: pointer;
    transition: background .15s ease;
}
.sm-stat__num:hover { background: var(--hover-bg); }
.sm-stat__num:active { background: var(--active-bg); }
.sm-stat__num:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}
.sm-stat__num b {
    font-size: 24px;
    font-weight: 700;
    color: var(--text-primary);
}
.sm-stat__num i {
    font-size: 11px;
    font-style: normal;
    color: var(--text-light);
}
/* 通栏（总人数 / 异常）用更大的字号 */
.sm-stat--hero .sm-stat__num--solo b,
.sm-stat--hero .sm-stat__num b { font-size: 30px; }

.sm-stat__num--solo {
    flex-direction: row;
    align-items: baseline;
    justify-content: flex-start;
    gap: 8px;
}

/* 左右两半之间的竖分隔线 */
.sm-stat__sep {
    align-self: center;
    color: var(--text-light);
    font-weight: 400;
    user-select: none;
}

/* 下钻清单弹层里的行 */
.sm-drill-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 56vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
.sm-drill-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 12px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-sm);
}
.sm-drill-row__name {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}
.sm-drill-row__sub {
    font-size: 12px;
    color: var(--text-secondary);
    text-align: right;
}
.sm-drill-row__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    justify-content: flex-end;
}
.sm-drill-tag {
    font-style: normal;
    font-size: 11px;
    line-height: 1;
    padding: 3px 6px;
    border-radius: 999px;
    color: #854F0B;
    background: #FAEEDA;
    white-space: nowrap;
}

/* ---- ② 考勤详情 ---- */
.sm-table {
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.sm-table__head,
.sm-row {
    display: grid;
    grid-template-columns: 1.4fr 1fr 1fr 0.9fr;
    gap: 8px;
    align-items: center;
    padding: 10px 12px;
}

.sm-table__head {
    font-size: 12px;
    color: var(--text-secondary);
    background: #F8FAFC;
    border-bottom: var(--card-line);
}

.sm-table__list:empty {
    display: none;   /* 没有行时容器不占位，空态才不会跟表头打架 */
}

.sm-row + .sm-row {
    border-top: var(--card-line);
}

.sm-row__name {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sm-row__time {
    font-size: 13px;
    color: var(--text-secondary);
}

.sm-row__state {
    justify-self: end;
    padding: 2px 8px;
    font-size: 11px;
    border-radius: var(--radius-pill);
}

.sm-row__state--done    { color: #0F6E56; background: #E1F5EE; }
.sm-row__state--late    { color: #854F0B; background: #FAEEDA; }
.sm-row__state--leave   { color: #3C3489; background: #EEEDFE; }
.sm-row__state--patch   { color: #185FA5; background: #E6F1FB; }
.sm-row__state--pending { color: #5F5E5A; background: #F1EFE8; }

/* 归档 090：同设备多账号使用预警标记（原 088「新设备」标记沿用同一套样式） */
.sm-row__flag {
    display: inline-block;
    margin-left: 6px;
    padding: 0 6px;
    border-radius: var(--radius-pill, 999px);
    font-size: 11px;
    line-height: 16px;
    font-style: normal;
    font-weight: 600;
    color: #B91C1C;
    background: #FEE2E2;
    vertical-align: middle;
}

/* ---- 空态（列表无数据时）---- */
.sm-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 22px 16px;
    text-align: center;
}

.sm-empty i {
    font-size: 26px;
    color: var(--text-light);
}

.sm-empty p {
    margin: 2px 0 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-secondary);
}

.sm-empty span {
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-light);
}

/* ---- ③ 底部两颗主操作 ---- */
.sm-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin-top: 4px;
}

.sm-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 13px 10px;
    font-size: 15px;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    cursor: pointer;
}

.sm-btn:active {
    transform: scale(0.98);
}

/* ⚠️ 主按钮的渐变已登记进 verify-theme-consistency.js 的 GRADIENT_OK 白名单 */
.sm-btn--primary {
    font-weight: 600;
    color: #FFFFFF;
    border: none;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
}

/* ---- 底部弹层 ---- */
.sm-sheet {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: flex-end;
}

/* 🔴 归档 `095d`：`.sm-sheet` 已被 js/smart-attendance.js **搬到 <body> 下**
   （治本：脱离 `.page-content` 子树，免受祖先 transform / 层叠上下文影响，
   否则真机上弹层会被 `.bottom-nav` 盖住、动作栏被挤出视口）。
   ⚠️ 搬出去以后，本文件里 `.page-sm [hidden]` 那条兜底就管不到它们了；
      而 `.sm-sheet { display: flex }` 会盖掉 UA 自带的 `[hidden] { display: none }`
      ⇒ 一进页面三个弹层会全部亮出来。所以这里必须补一条全局兜底。 */
.sm-sheet[hidden] {
    display: none !important;
}

.sm-sheet__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}

.sm-sheet__panel {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    /* 🔴 归档 `095c`：原 88vh + body 无 flex 约束 ⇒ 清单长时 body 不收缩、
       把动作栏与末行文字挤出可视区（「弹出窗口过低、文字被档」）。
       改为 92vh 并让 head/body/actions 在 flex 列里正确分配。 */
    max-height: 92vh;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    background: var(--card-bg);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: 0 -8px 24px rgba(15, 23, 42, 0.18);
}

.sm-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: 0 0 auto;
    margin-bottom: 12px;
}

.sm-sheet__title {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
}

.sm-sheet__ops {
    display: flex;
    align-items: center;
    gap: 8px;
}

.sm-sheet__close,
.sm-icon-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    font-size: 15px;
    color: var(--text-secondary);
    background: transparent;
    border: var(--card-line);
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.sm-icon-btn {
    color: var(--primary);
    border-color: var(--primary);
}

.sm-sheet__close:active,
.sm-icon-btn:active {
    transform: scale(0.94);
}

.sm-sheet__body {
    /* 🔴 归档 `095c`：flex 列里必须 min-height:0 才能收缩并内部滚动，
       否则内容长时把底部动作栏与末行文字挤出面板可视区。 */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-right: 2px;
}

.sm-sheet__hint {
    margin: 0 0 12px;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-secondary);
}

.sm-sheet__msg {
    margin: 10px 0 0;
    padding: 10px 12px;
    font-size: 12px;
    line-height: 1.6;
    color: #854F0B;
    background: #FAEEDA;
    border-radius: var(--radius-md);
}

.sm-sheet__actions {
    display: flex;
    gap: 10px;
    flex: 0 0 auto;
    margin-top: 14px;
}

.sm-sheet__btn {
    flex: 1;
    padding: 12px 10px;
    font-size: 15px;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.sm-sheet__btn:active {
    transform: scale(0.98);
}

/* ⚠️ 同 .sm-btn--primary：渐变已登记进 GRADIENT_OK 白名单 */
.sm-sheet__btn--primary {
    font-weight: 600;
    color: #FFFFFF;
    border: none;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
}

/* ---- 弹层①：补签二维码 ---- */
.sm-qr {
    display: flex;
    justify-content: center;
    padding: 8px 0 4px;
}

.sm-qr__slot {
    width: 190px;
    height: 190px;
    padding: 10px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
}

.sm-qr__slot svg {
    display: block;
    width: 100%;
    height: 100%;
}

.sm-qr__uid {
    margin: 10px 0 0;
    font-size: 13px;
    text-align: center;
    color: var(--text-secondary);
}

.sm-qr__uid b {
    font-size: 15px;
    color: var(--text-primary);
}

.sm-qr__note {
    margin: 8px 0 0;
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-light);
}

/* ---- 弹层②：碰一碰点位 ---- */
.sm-points:empty {
    display: none;
}

.sm-point {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 0;
    border-bottom: var(--card-line);
}

.sm-point__body {
    flex: 1;
    min-width: 0;
}

.sm-point__name {
    display: block;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sm-point__place {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: var(--text-light);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sm-point__state {
    flex-shrink: 0;
    padding: 2px 8px;
    font-size: 11px;
    color: #5F5E5A;
    background: #F1EFE8;
    border-radius: var(--radius-pill);
}

.sm-point__state.is-on {
    color: #0F6E56;
    background: #E1F5EE;
}

/* 🔴 图标单独一个只设 font-size / color 的类 —— 绝不设 font-weight：
      FA7 Free 的 solid / regular 共用 family 名、靠 font-weight 选字体文件，
      被打断成 400 会去加载只含 regular 的那份 ⇒ 符号字形变豆腐块（归档 078）。 */
.sm-point__op {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    font-size: 15px;
    color: var(--text-secondary);
    background: transparent;
    border: var(--card-line);
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.sm-point__op:active {
    transform: scale(0.94);
}

.sm-point__op--danger {
    color: #DC2626;
}

/* 🔴 归档 079 教训：本页用 hidden 属性显隐两个弹层，而 .sm-sheet 自带
      display:flex —— 作者样式优先级高于 UA 的 [hidden]，不兜底就关不掉。 */
.page-sm [hidden] {
    display: none;
}

/* ---- 分页控件（归档 087）：胶囊分页，与总览 tab 同源 —— 旧的「白卡 + 蓝色实心块」已废弃 ---- */
.sm-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 12px;
}

.sm-pager__btn {
    padding: 7px 16px;
    font-size: 13px;
    font-weight: 600;
    font-family: inherit;
    color: var(--primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: var(--transition);
}

.sm-pager__btn:disabled {
    color: var(--text-light);
    border-color: var(--border);
    cursor: default;
}

.sm-pager__btn:active:not(:disabled) {
    transform: scale(0.97);
}

.sm-pager__text {
    min-width: 88px;
    text-align: center;
    font-size: 13px;
    color: var(--text-secondary);
}

/* ---- 碰一碰点位：创建行（归档 087）---- */
.sm-point-create {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 12px;
}

.sm-input {
    flex: 1 1 140px;
    min-width: 0;
    padding: 10px 12px;
    font-size: 14px;
    font-family: inherit;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
}

.sm-input:focus {
    outline: none;
    border-color: var(--primary);
}

.sm-btn--small {
    flex: 0 0 auto;
    padding: 10px 16px;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    color: #FFFFFF;
    border: none;
    border-radius: var(--radius-md);
    /* ⚠️ 渐变已登记进 verify-theme-consistency.js 的 GRADIENT_OK 白名单 */
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    cursor: pointer;
    transition: var(--transition);
}

.sm-btn--small:active {
    transform: scale(0.97);
}

/* ==========================================================================
   智能考勤配套独立页面（attendance-makeup.html / nfc-signin.html，归档 `087`）
   站点根级、随 index.html 一起发布；复用全站设计基线（--primary / --card-line /
   --radius-md / GRADIENT_OK 渐变白名单）。不引入新色板。
   ========================================================================== */
.am-body {
    background: var(--bg);
    min-height: 100vh;
    min-height: 100dvh;
}

.am-wrap {
    max-width: 428px;
    margin: 0 auto;
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px 16px calc(24px + env(safe-area-inset-bottom, 0px));
}

.am-card {
    width: 100%;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 24px 20px;
    text-align: center;
}

.am-head {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-bottom: 6px;
}

.am-head__icon {
    font-size: 20px;
    color: var(--primary);
}
/* 🔴 FA7 Free 靠 font-weight 选字体文件，图标类一律不设 font-weight（归档 078） */
.am-head__icon, .am-sub i { font-weight: 400; }

.am-title {
    margin: 0;
    font-size: 19px;
    font-weight: 700;
    color: var(--text-primary);
}

.am-sub {
    margin: 0 0 18px;
    font-size: 13px;
    color: var(--text-secondary);
}
.am-sub b { color: var(--primary); }

.am-form {
    text-align: left;
}

.am-field {
    margin-bottom: 16px;
}

.am-label {
    display: block;
    margin-bottom: 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.am-radio {
    display: flex;
    gap: 10px;
}

.am-radio__item {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 11px 10px;
    border: var(--card-line);
    border-radius: var(--radius-pill);
    background: var(--card-bg);
    font-size: 14px;
    font-weight: 600;
    color: var(--text-secondary);
    cursor: pointer;
}

/* 选中的时段：淡色底 + 主色边/字（与全站「选中态」同一语言） */
.am-radio__item:has(input:checked) {
    border-color: var(--primary);
    background: var(--primary-bg);
    color: var(--primary);
}
.am-radio__item input { accent-color: var(--primary); }

.am-input {
    width: 100%;
    padding: 11px 12px;
    font-size: 15px;
    font-family: inherit;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
}
.am-input:focus { outline: none; border-color: var(--primary); }

.am-submit {
    width: 100%;
    height: 46px;
    margin-top: 4px;
    border: none;
    border-radius: var(--radius-md);
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    color: #fff;
    font-family: inherit;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 2px;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(59, 130, 246, 0.35);
    transition: var(--transition);
}
.am-submit:active { transform: scale(0.98); }
.am-submit:disabled { opacity: 0.6; cursor: default; }

.am-msg {
    margin: 14px 0 0;
    padding: 10px 12px;
    font-size: 13px;
    line-height: 1.6;
    border-radius: var(--radius-md);
    text-align: left;
}
.am-msg--ok  { color: #047857; background: #ECFDF5; }
.am-msg--err { color: #A32D2D; background: #FCEBEB; }

.am-back {
    display: inline-block;
    margin-top: 18px;
    font-size: 13px;
    font-weight: 600;
    color: var(--primary);
    text-decoration: none;
}

/* NFC 签到三态 */
.ns-state {
    margin: 8px 0 0;
    padding: 18px 16px;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.6;
    border-radius: var(--radius-md);
    text-align: center;
}
.ns-state--loading { color: var(--text-secondary); background: var(--bg); }
.ns-state--ok      { color: #047857; background: #ECFDF5; }
.ns-state--error   { color: #A32D2D; background: #FCEBEB; }

/* ==========================================================================
   碰一碰签到页 · 四行信息 + 页内登录弹层（归档 `091`）
   复用全站设计基线（--primary / --card-line / --radius-md / --radius-lg /
   --primary-bg / GRADIENT_OK 渐变白名单），不引入新色板。
   🔴 变量只用 :root 里真实存在的（注意全站**没有** `--text`，只有
      --text-primary / --text-secondary / --text-light）。
   ========================================================================== */
.ns-rows {
    margin: 2px 0 0;
    text-align: left;
}

/* 第 ① 行：您好，<姓名>老师 —— 本页最醒目的信息 */
.ns-hello {
    margin: 0 0 12px;
    font-size: 20px;
    font-weight: 700;
    line-height: 1.4;
    color: var(--text-primary);
    text-align: center;
}

/* 第 ②③ 行：键值对（签到点 / 签到时间） */
.ns-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin: 0 0 8px;
    padding: 10px 12px;
    background: var(--bg);
    border-radius: var(--radius-md);
}
.ns-row:last-child { margin-bottom: 0; }

.ns-row__k {
    flex: none;
    font-size: 13px;
    color: var(--text-secondary);
}

.ns-row__v {
    flex: 1;
    min-width: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
    text-align: right;
    word-break: break-all;
}

/* 签到时间用等宽数字，避免每秒刷新时整行左右抖动 */
.ns-row__v--time {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
    letter-spacing: 0.5px;
    color: var(--primary);
}

/* ---------- 页内登录弹层（底部抽屉式，与 .reason-modal 同一语言） ---------- */
.ns-login {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}
.ns-login__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}
.ns-login__panel {
    position: relative;
    width: 100%;
    max-width: 520px;
    box-sizing: border-box;
    background: var(--card-bg);
    border-radius: 18px 18px 0 0;
    padding: 20px 18px calc(20px + env(safe-area-inset-bottom));
    box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.18);
    animation: nsLoginUp 0.2s ease;
}
@keyframes nsLoginUp {
    from { transform: translateY(100%); }
    to   { transform: none; }
}
.ns-login__title {
    margin: 0;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
    text-align: center;
}
.ns-login__sub {
    margin: 4px 0 16px;
    font-size: 13px;
    color: var(--text-secondary);
    text-align: center;
}
.ns-login__form { text-align: left; }

/* 输入框观感对齐 css/login.css 的 .login-field */
.ns-login__field {
    display: flex;
    align-items: center;
    height: 52px;
    margin-bottom: 12px;
    padding: 0 16px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
}
.ns-login__field:focus-within {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-light);
}
.ns-login__field > i:first-child {
    width: 22px;
    flex-shrink: 0;
    font-size: 16px;
    color: var(--text-light);
    text-align: center;
}
.ns-login__field input {
    flex: 1;
    min-width: 0;
    height: 100%;
    padding: 0 12px;
    border: none;
    outline: none;
    background: transparent;
    font-family: inherit;
    font-size: 15px;
    color: var(--text-primary);
}
.ns-login__field input::placeholder { color: var(--text-light); }

.ns-login__eye {
    flex-shrink: 0;
    padding: 8px;
    border: none;
    background: none;
    font-size: 16px;
    color: var(--text-light);
    cursor: pointer;
}

.ns-login__msg { margin: 0 0 12px; }

.ns-login__actions { display: flex; gap: 10px; }
.ns-login__btn {
    flex: 1;
    height: 46px;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.1s ease;
    -webkit-tap-highlight-color: transparent;
}
.ns-login__btn:active { transform: scale(0.98); }
.ns-login__btn--cancel {
    border: var(--card-line);
    background: var(--card-bg);
    color: var(--text-secondary);
}
.ns-login__btn--submit {
    border: none;
    background: var(--primary);
    color: #fff;
}
.ns-login__btn:disabled { opacity: 0.6; cursor: default; }

/* ⚠️ 必须显式写 display:none —— 上面 .ns-login__msg 用了 margin 但未设 display，
   而 .am-msg 未设 display；父级 .ns-login 是 flex，若不写这条，
   [hidden] 的 UA 样式会被层叠覆盖（同类坑见 css/style.css 的 .reason-modal [hidden]
   与 css/login.css 的 .login-panel[hidden]，2026-09-19 真机截图发现）。 */
.ns-login[hidden] { display: none; }
.ns-login__msg[hidden] { display: none; }


/* ==========================================================================
   智能考勤-教职工（pages/smart-attendance-staff.html，归档 `092`）
   与「智能考勤-管理」是一对：那个看全园，这个看自己。
   复用全站设计基线（--primary / --card-line / --radius-md / GRADIENT_OK 渐变白名单），
   沿用 .sm-* 那套数值卡与弹层骨架的形制，但独立命名 .sa-* 避免两页互相牵动。
   ========================================================================== */
.sa-block {
    margin-bottom: 14px;
}

/* ---- ① 4 张计数卡（1 张通栏锚点 + 3 张并排）---- */
.sa-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
}

/* 🔴 minmax(0, 1fr) 的 0 不能省：裸 1fr 的最小值是 auto，长标签会把网格撑破 */
.sa-stat {
    padding: 12px 14px;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
}

/* 「已签到天数」是本页锚点 ⇒ 通栏放大，其余三项并排 */
.sa-stat--hero {
    grid-column: 1 / -1;
    padding: 14px 16px;
}

.sa-stat__label {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    color: var(--text-secondary);
}

.sa-stat__label i {
    color: var(--primary);
}

.sa-stat__value {
    display: block;
    margin-top: 6px;
    font-size: 24px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--text-primary);
}

/* 「—」是占位符不是数字：用次级色，别让人当成 0 */
.sa-stat__value:empty::before,
.sa-stat__value[data-empty] {
    color: var(--text-light);
}

.sa-stat--hero .sa-stat__value {
    font-size: 34px;
}

.sa-stat__sub {
    display: block;
    margin-top: 4px;
    font-size: 11px;
    color: var(--text-light);
}

/* 数值卡的图标底色：与 .sm-stat 同一套色板，改这里即可换色 */
.sa-stat[data-tone="blue"]   .sa-stat__label i { color: #185FA5; }
.sa-stat[data-tone="green"]  .sa-stat__label i { color: #0F6E56; }
.sa-stat[data-tone="amber"]  .sa-stat__label i { color: #854F0B; }
.sa-stat[data-tone="teal"]   .sa-stat__label i { color: #0F6E56; }

/* ---- ② 月份导航 ---- */
.sa-monthbar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    /* 🔴 归档 092-B：月份选择挪到记录下方，间距改 margin-top */
    margin-top: 10px;
}

.sa-monthbar__nav {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    font-size: 13px;
    color: var(--primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: var(--transition);
}

.sa-monthbar__nav:disabled {
    color: var(--text-light);
    cursor: default;
}

.sa-monthbar__nav:active:not(:disabled) {
    transform: scale(0.94);
}

.sa-monthbar__text {
    min-width: 120px;
    text-align: center;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

/* ---- ② 签到清单（倒序）---- */
.sa-list {
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.sa-list:empty {
    display: none;
}

.sa-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
}

.sa-item + .sa-item {
    border-top: var(--card-line);
}

/* 迟到 / 补签的**特殊效果**：左侧一条彩色竖条 + 极浅底色，
   与「状态胶囊」是两套信号（胶囊说状态，竖条说「这一行值得注意」）。 */
.sa-item--late {
    box-shadow: inset 3px 0 0 #D97706;
    background: linear-gradient(90deg, rgba(217, 119, 6, 0.06) 0%, transparent 40%);
}

.sa-item--normal {
    box-shadow: inset 3px 0 0 transparent;
}

.sa-item__main {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 62px;
}

.sa-item__date {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.sa-item__sess {
    font-size: 11px;
    color: var(--text-light);
}

.sa-item__mid {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.sa-item__time {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.sa-item__method {
    font-size: 11px;
    color: var(--text-light);
}

.sa-item__side {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
}

.sa-item__state {
    padding: 2px 9px;
    font-size: 11px;
    border-radius: var(--radius-pill);
}

.sa-item__state--normal  { color: #0F6E56; background: #E1F5EE; }
.sa-item__state--late    { color: #854F0B; background: #FAEEDA; }
.sa-item__state--leave   { color: #3C3489; background: #EEEDFE; }
.sa-item__state--absent  { color: #A32D2D; background: #FCEBEB; }
.sa-item__state--pending { color: #5F5E5A; background: #F1EFE8; }

/* 行内特殊标签（迟到 / 补签） */
.sa-item__tag {
    padding: 1px 8px;
    font-size: 10px;
    font-style: normal;
    border-radius: var(--radius-pill);
}

.sa-item__tag--late {
    color: #854F0B;
    background: #FAEEDA;
}

.sa-item__tag--makeup {
    color: #185FA5;
    background: #E6F1FB;
}

/* ---- 空态 ---- */
.sa-empty {
    padding: 34px 20px;
    text-align: center;
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
}

.sa-empty i {
    font-size: 30px;
    color: var(--text-light);
}

.sa-empty p {
    margin: 10px 0 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-secondary);
}

.sa-empty span {
    display: block;
    margin-top: 6px;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-light);
}

/* ---- ③ 底部主操作 ---- */
/* 🔴 归档 092-B：按钮挪到统计卡下方（流内常规块），不再 sticky 吸底 */
.sa-actions {
    padding: 4px 0 10px;
}

.sa-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    padding: 14px;
    font-size: 16px;
    font-weight: 600;
    font-family: inherit;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow);
    cursor: pointer;
    transition: var(--transition);
}

.sa-btn:active {
    transform: scale(0.99);
}

/* ⚠️ 同 .sm-btn--primary：渐变已登记进 verify-theme-consistency.js 的 GRADIENT_OK 白名单 */
.sa-btn--primary {
    color: #FFFFFF;
    border: none;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
}

/* ---- 弹层（扫码 / 填单）---- */
.sa-sheet {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

.sa-sheet__mask {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}

.sa-sheet__panel {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 428px;
    max-height: 92vh;
    max-height: 92dvh;
    display: flex;
    flex-direction: column;
    background: var(--card-bg);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: var(--shadow);
    animation: sa-rise 0.22s ease-out;
}

@keyframes sa-rise {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

.sa-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 16px 18px 10px;
}

.sa-sheet__title {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
}

.sa-sheet__close {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    font-size: 15px;
    color: var(--text-secondary);
    background: transparent;
    border: none;
    border-radius: var(--radius-pill);
    cursor: pointer;
}

.sa-sheet__close:active {
    transform: scale(0.94);
}

.sa-sheet__body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 0 18px 8px;
}

.sa-sheet__hint {
    margin: 0 0 12px;
    font-size: 12px;
    line-height: 1.7;
    color: var(--text-secondary);
}

.sa-sheet__msg {
    margin: 10px 0 0;
    font-size: 12px;
    line-height: 1.6;
    color: #A32D2D;
}

.sa-sheet__actions {
    display: flex;
    gap: 10px;
    padding: 10px 18px calc(16px + env(safe-area-inset-bottom, 0px));
}

.sa-sheet__btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 12px;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: var(--transition);
}

.sa-sheet__btn:active {
    transform: scale(0.98);
}

/* ⚠️ 同 .sm-sheet__btn--primary：渐变已登记进 GRADIENT_OK 白名单 */
.sa-sheet__btn--primary {
    color: #FFFFFF;
    border: none;
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
}

/* ---- 扫码取景框 ---- */
.sa-scan {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    margin-bottom: 12px;
    background: #0F172A;
    border-radius: var(--radius-md);
    overflow: hidden;
}

.sa-scan__video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.sa-scan__frame {
    position: absolute;
    top: 14%;
    left: 14%;
    width: 72%;
    height: 72%;
    border: 2px solid rgba(255, 255, 255, 0.85);
    border-radius: var(--radius-md);
    box-shadow: 0 0 0 2000px rgba(15, 23, 42, 0.35);
    pointer-events: none;
}

.sa-scan__placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    color: rgba(255, 255, 255, 0.8);
    font-size: 12px;
    text-align: center;
    padding: 0 20px;
}

.sa-scan__placeholder i {
    font-size: 28px;
}

/* ---- 填单字段（时段单选 / 时间 / 备注）---- */
.sa-field {
    margin-bottom: 14px;
}

.sa-label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    color: var(--text-secondary);
}

.sa-input {
    width: 100%;
    padding: 10px 12px;
    font-size: 14px;
    font-family: inherit;
    color: var(--text-primary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    box-sizing: border-box;
}

.sa-input:focus {
    outline: none;
    border-color: var(--primary);
}

/* 🔴 与 style.css 顶部 .sc-input 同款兜底：iOS 原生控件内在高度大于盒模型 ⇒ 不加会与其他输入框不等高 */
.sa-input[type="time"],
.sa-input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    min-height: 42px;
    line-height: 1.4;
}

.sa-radio {
    display: flex;
    gap: 10px;
}

.sa-radio__item {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 10px;
    font-size: 13px;
    color: var(--text-secondary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: var(--transition);
}

.sa-radio__item input {
    accent-color: var(--primary);
    margin: 0;
}

.sa-radio__item:has(input:checked) {
    color: var(--primary);
    border-color: var(--primary);
    font-weight: 600;
}

/* 🔴 归档 079 教训：本页用 hidden 属性显隐两个弹层，而 .sa-sheet 自带
      display:flex —— 作者样式优先级高于 UA 的 [hidden]，不兜底就关不掉。 */
.page-sa [hidden] {
    display: none;
}

/* ==========================================================================
   通讯录（pages/contact.html）· 归档 094

   需求原文：「页面内放置所有教师的电话、岗位、联系方式，可以进行搜索」。
   · 复用 .mg-list / .mg-row 系列（与管理、过敏、工作计划同一套行样式），
     本批只补三个本页专属的小件：岗位徽标、可拨号电话、字段说明。
   🔴 岗位徽标做成 <span> 而不是单独一列：姓名 + 岗位同行，
     窄屏（390px）下不会因为多一列而把电话挤掉行。
   ========================================================================== */
.page-contact .mg-list {
    gap: 8px;
}

/* 岗位徽标：跟在姓名后面，浅底小圆角 */
.ct-duty {
    flex-shrink: 0;
    padding: 1px 7px;
    font-size: 11px;
    font-weight: 500;
    color: var(--text-secondary);
    background: var(--card-bg);
    border: var(--card-line);
    border-radius: 999px;
}

/* 联系方式行：电话做成可点（tel:）—— 点一下直接呼出，这是通讯录的核心交互 */
.ct-contact {
    display: flex;
    align-items: center;
    gap: 6px;
}

.ct-phone {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 13px;
    font-weight: 600;
    color: var(--primary);
    text-decoration: none;
    word-break: break-all;
}

.ct-phone:active {
    opacity: 0.7;
}

.ct-phone--empty {
    font-weight: 400;
    color: var(--text-secondary);
}

/* 字段说明（底部那段「档案暂无职务字段」的诚实标注） */
#ctFieldNote {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin-top: 10px;
}

#ctFieldNote i {
    margin-top: 2px;
    flex-shrink: 0;
}

/* ============================================================
   学习强国积分（「应用 → 学习强国积分」）
   ------------------------------------------------------------
   🔴 本页「识别结果不可手动修改」是由**结构**保证的：pages/study-score.html
      里根本没有输入框，结果写进 <span>.textContent。
      下面这些样式**只管展示** —— 刻意不做成输入框的外观
      （描边、内阴影、placeholder 文案），否则会诱导用户去点、去改，
      而它根本改不了，白白制造挫败感。

   🔴 两行用 label + value 两端对齐（label 左、value 右）：
      视觉上与「只读详情」一致，一眼就能看出是"展示"而不是"填写"。
   ============================================================ */
.ss-result {
    margin-top: 18px;
    border-top: var(--card-line);
}

.ss-row {
    display: flex;
    /* ⚠️ baseline 而非 center：积分数值字号（24px）比标签（14px）大，
       center 对齐会让数字在行内"上下留白不均"，baseline 才是两端文字底部齐平。 */
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 2px;
    border-bottom: var(--card-line);
}

.ss-row__label {
    flex-shrink: 0;
    font-size: 14px;
    color: var(--text-secondary);
}

.ss-row__value {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary);
    text-align: right;
    /* 长姓名不撑破容器（少数民族姓名可到 10 字以上） */
    word-break: break-all;
}

/* 积分数值：加大加粗 + 等宽数字（tabular-nums）—— 等宽的意义是
   不同数字占同样宽度，补拍对比时"位数"一眼可数。 */
.ss-row__value--num {
    font-size: 24px;
    font-weight: 700;
    color: var(--primary);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
}

/* 「不可手动修改」的说明条。语气要明确 —— 用户第一次看到
   只读结果时最自然的疑问就是"我能不能改"，这里直接答掉。 */
.ss-tip {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin: 14px 0 16px;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-light);
}

.ss-tip i {
    margin-top: 2px;
    flex-shrink: 0;
}

.ss-tip strong {
    color: var(--text-secondary);
    font-weight: 600;
}

/* ============================
   星火党建 · 强国积分（归档 `111`）
   ---------------------------------------------------------------
   本区块只服务 pages/study-score.html 与 pages/party-build.html。
   色值一律取 style.css 既有变量（--primary / --card-line / --radius-*），
   不引入新色系 —— 与 .app-card__badge、.ss-row 同一套视觉语言。
   ============================ */

/* --- 顶部概览卡：最近一次积分 + 月度增长 --- */
.ss-hero {
    margin-bottom: 18px;
    padding: 18px 20px;
    border-radius: var(--radius-md);
    background: var(--card-bg);
    box-shadow: var(--shadow);
}

.ss-hero__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.ss-hero__label {
    font-size: 13px;
    color: var(--text-secondary);
}

.ss-hero__month {
    font-size: 12px;
    color: var(--text-light);
}

/* 积分数值：加大加粗 + 等宽数字（tabular-nums）。
   等宽的意义是不同数字占同样宽度，与截图逐位核对时"位数"一眼可数。 */
.ss-hero__num {
    margin-top: 6px;
    font-size: 34px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--primary);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
}

.ss-hero__delta {
    margin-top: 6px;
    font-size: 13px;
    color: var(--text-light);
}

/* 增长 / 减少各有色调，但都不用红绿涨跌色 —— 这是学习积分，
   不是行情；用主色表示"变多了"、次级灰表示"变少了"，不制造涨跌联想。 */
.ss-hero__delta--up   { color: var(--primary);        font-weight: 600; }
.ss-hero__delta--down { color: var(--text-secondary); font-weight: 600; }

.ss-hero__note {
    margin-top: 10px;
    padding-top: 10px;
    border-top: var(--card-line);
    font-size: 12px;
    color: var(--text-light);
}

/* --- 历史清单（每页 10 条）--- */
.ss-hist {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ss-hist__empty {
    padding: 22px 4px;
    text-align: center;
    font-size: 13px;
    color: var(--text-light);
}

.ss-hist__item {
    display: grid;
    /* 序号 | 月份+时间 | 分数+增长（右对齐） */
    grid-template-columns: 22px 1fr auto;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    box-shadow: var(--shadow);
}

.ss-hist__seq {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 999px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: 11px;
    font-weight: 600;
}

.ss-hist__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ss-hist__period {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.ss-hist__meta {
    font-size: 11px;
    color: var(--text-light);
}

.ss-hist__right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
}

.ss-hist__score {
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.ss-hist__delta {
    font-size: 11px;
    font-weight: 600;
}
.ss-hist__delta--up   { color: var(--primary); }         /* 较上次增长 */
.ss-hist__delta--down { color: var(--text-secondary); }   /* 较上次减少 */
.ss-hist__delta--flat { color: var(--text-light); }       /* 持平 / 无对比 */

/* --- 分页条（不足一页时整块隐藏，不留点不动的按钮）--- */
.ss-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 14px;
}

.ss-pager .identity-btn {
    flex: 0 0 auto;
    padding: 8px 14px;
    font-size: 13px;
}

.ss-pager__info {
    font-size: 12px;
    color: var(--text-light);
    text-align: center;
}

/* --- 星火党建入口页（pages/party-build.html）---
   三张子功能卡复用 .app-card（与「应用」页同一套视觉），
   这里只补一个上边距，避免卡片贴着说明文案。 */
.page-party-build .app-grid {
    margin-top: 4px;
}

/* =========================================================================
   🆕 归档 `119`：财务管理（pages/finance.html）· 功能入口容器页
   与星火党建（.page-party-build）同一套视觉语言：说明文案 + 子功能卡网格 + 底部提示。
*/
.page-finance .app-grid {
    margin-top: 4px;
}
.page-finance__note {
    margin: 16px 2px 0;
    font-size: 13px;
    line-height: 1.6;
    color: #8a94a6;
    text-align: center;
}

/* 🆕 归档 `119`：出勤报表（pages/finance-attendance.html）· 底部提示文案 */
.page-finance-attendance__note {
    margin: 16px 2px 0;
    font-size: 13px;
    line-height: 1.6;
    color: #8a94a6;
    text-align: center;
}

/* =========================================================================
   🆕 归档 `113`：积分汇总（pages/study-score-summary.html）
   全园「本月已上报」排行榜。顶部 hero 与分页条**直接复用** .ss-hero / .ss-pager
   （与强国积分页同一套视觉语言），此处只补榜单行自己的样式。
   ========================================================================= */
.sss-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sss-empty {
    padding: 22px 4px;
    text-align: center;
    font-size: 13px;
    color: var(--text-light);
}

/* 一行：名次 | 姓名 + 「上月 → 本月」 | 增长数（右对齐） */
.sss-item {
    display: grid;
    grid-template-columns: 26px 1fr auto;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    box-shadow: var(--shadow);
}

.sss-item__rank {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 999px;
    background: var(--primary-bg);
    color: var(--primary);
    font-size: 12px;
    font-weight: 600;
}

/* 前三名：实心党建红（与 .app-card 的 tone=red 同色系），一眼看出榜头 */
.sss-item__rank--top1,
.sss-item__rank--top2,
.sss-item__rank--top3 {
    background: var(--primary);
    color: #fff;
}

.sss-item__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.sss-item__name {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sss-item__name--none {
    font-weight: 400;
    color: var(--text-light);
}

.sss-item__meta {
    font-size: 11px;
    color: var(--text-light);
}

.sss-item__meta b {
    font-weight: 600;
    color: var(--text-secondary);
}

.sss-item__meta b.sss-item__cur {
    color: var(--text-primary);
}

.sss-item__meta i {
    font-size: 9px;
    opacity: .55;
    margin: 0 1px;
}

.sss-item__delta {
    font-size: 16px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.sss-item__delta--up   { color: var(--primary); }         /* 增长 */
.sss-item__delta--down { color: var(--text-secondary); }   /* 下降 */
.sss-item__delta--flat { color: var(--text-light); }       /* 持平 / 无对比 */
