:root{
  /* 🔴🔴 v91【开屏不适配的根治变量】
     ⚠ 用户真机（iQOO Neo9Pro / OriginOS）实测：开屏遮罩只占屏幕宽度的 80%，
       右侧 252px（20%）**直接透出后面的设置页**（实测该区颜色 =纯白 254,254,254，
       而遮罩内为橙色 241,134,78，边界在 x=1008 处锐利切换）。
       ⚠ 根因不是 object-fit（已用像素测量排除），而是**没有安全区概念**：
         · position:fixed + inset:0 在部分 WebView/厂商 ROM 上不覆盖状态栏与导航栏区域；
         · 100vh 在 OriginOS 上不等于「可视高度」，它可能包含系统栏；
         · 且没有任何 --safe-bottom 变量，于是全屏元素的底部/侧边会露底。
       ⇒ 下面三个变量由 app.js 在运行时按 visualViewport 实测写入（见 setSafeAreaVars()），
         CSS 全部改用它们，不再依赖 vh/vw 猜测。
       ⚠ 初值用 env(safe-area-inset-*)：它在 iOS/部分 Android 上有效，
         在不支持的设备上回退为 0（此时行为等同于原来的 inset:0，不会更糟）。 */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  /* ⚠⚠ v93【这条声明本身是错的，注释原来描述的机制也是错的 —— 一并纠正】
   *
   *【原注释错在哪】原文写的是：
   *   「留空 ⇒ var() 的兜底分支生效 ⇒ 用 100%（由 inset:0 决定）」
   * 🔴 **这是事实错误**，我实测了CSS 自定义属性的空值语义（Edge/Chromium）：
   *       :root{ --empty: ; }
   *       #a{ max-height: var(--empty, 100%) }   → computed = "none"
   *       #b{ max-height: var(--undefined, 100%) } → computed = "100%"
   *   ⇒ 声明了但值为空时，var() **取到的是空值**（声明存在 ⇒ 不走兜底分支），
   *     于是整条 max-height 变成「无上界」（none），**而不是**注释说的 100%。
   *   ⇒ 也就是说：注释声称的「兜底生效」从来没发生过。
   *
   *【为什么它一直没出事 —— 纯属侥幸】
   *   `max-height: none` 恰好等于「不钳制」，而正确做法就是不钳制，
   *   所以结果碰巧是对的。⚠ 但这是**靠巧合正确**，不是靠设计正确：
   *     一旦有人把 --app-vh 用在 `width` / `height` 这类**必须有值**的属性上，
   *     空值会让整条声明失效 ⇒ 元素退回 auto ⇒ 直接布局错乱，且极难排查。
   *
   *【v93 的处置：改成合法且语义正确的初值】
   *   ① 初值给 `100%`：这是**合法**的自定义属性值，且与本变量的用途
   *      （相对包含块的全屏高度）等价 —— 由 inset:0 决定，不依赖 vh 猜尺寸。
   *   ② 绝不能给 100vh：Android WebView 里 100vh = **布局视口**高，
   *      不等于可视高（键盘弹出/状态栏显隐时差一截）⇒ JS 万一没跑成
   *      就会退回 100vh，正是历史上「只占 80%」那类坑的来源。
   *   ③ JS 运行时仍会用 visualViewport 实测值覆盖它（app.js setSafeAreaVars）。
   *
   *【v93 顺带说明：--app-vh 现在已无 CSS 消费者】
   *   唯一的使用点 p1-fixes-v86.css 的 `.intro-img{max-height: var(--app-vh)}`
   *   已在 v93 删除（它有两个独立错误，见该处注释）。
   *   ⇒ JS 侧继续写入该变量**无害**，保留是为了不破坏将来可能的全屏元素用法；
   *     但**现在开屏的铺满已经与它彻底解耦**（这正是 v91.1 想要的结果）。
   */
  --app-vh: 100%;

  --bg:#f4f6fb; --card:#ffffff; --line:#e6eaf2; --line2:#eef1f7;
  --tx:#1c2333; --tx2:#5a6478; --tx3:#697185;
  --pri:#3b6ef5; --pri-soft:#eaf0ff;
  --course:#3b6ef5; --plan:#f0913a; --meet:#8b5cf6; --leave:#e4544f; --free:#22a06b;
  /* ★ v78 圆角体系：全站方框统一走这五档，不再散落硬编码 px。
     xs=小标签/角标，sm=按钮与输入框，md=卡片与面板（原 --r），lg=大面板与弹窗，xl=抽屉与大卡，pill=胶囊。 */
  --r-xs:8px; --r-sm:12px; --r-md:16px; --r-lg:20px; --r-xl:26px; --r-pill:999px;
  --r:16px; --sh:0 1px 2px rgba(20,30,60,.05), 0 8px 24px rgba(20,30,60,.06);
  --track-bg:#f1f4fa; --slot-bg:#fafbff; --gap-bg:#f7f9fd;
  --soft:#f4f6fb;
  --now-color:#e4544f;
  /* ★ 告诉浏览器/WebView 配色由本页接管（配合 html 里的 color-scheme meta，
     关掉安卓「强制深色」叠加；同时让原生控件跟随主题） */
  color-scheme:light;
}
/* ============ v77.6 T3：季节主题全局铺开（底色层） ============
   选择器统一吃 html[data-cal-theme]（显式四季主题 / autoSeason 都会落成季节），
   topbar / 更多抽屉 / 面板 panel-head 统一吃季节变量。
   优先级：dark（深色调暗渐变，保可读） > mascot（看板娘蓝白，见下方覆盖） > 本层（季节渐变底色）。
   本块放在 :root 之后、dark/mascot 之前，靠「同优先级后者胜」让 dark/mascot 覆盖本层。 */
/* 顶部应用栏：当季主色→浅色渐变 */
[data-cal-theme="spring"] .topbar{background:linear-gradient(120deg,#ffe6f0 0%,#fff7fb 100%)}
[data-cal-theme="summer"] .topbar{background:linear-gradient(120deg,#dff5ee 0%,#f2fbf8 100%)}
[data-cal-theme="autumn"] .topbar{background:linear-gradient(120deg,#ffe9d9 0%,#fff7f0 100%)}
[data-cal-theme="winter"] .topbar{background:linear-gradient(120deg,#e6efff 0%,#f5f9ff 100%)}
/* 顶部应用栏 · 深色：调暗渐变 */
[data-theme="dark"][data-cal-theme="spring"] .topbar{background:linear-gradient(120deg,#3a2430 0%,#1a2030 100%)}
[data-theme="dark"][data-cal-theme="summer"] .topbar{background:linear-gradient(120deg,#16332c 0%,#1a2030 100%)}
[data-theme="dark"][data-cal-theme="autumn"] .topbar{background:linear-gradient(120deg,#3a2a1a 0%,#1a2030 100%)}
[data-theme="dark"][data-cal-theme="winter"] .topbar{background:linear-gradient(120deg,#1c2a4a 0%,#1a2030 100%)}
/* 更多抽屉：整体极淡季节渐变 */
[data-cal-theme="spring"] .sheet{background:linear-gradient(180deg,#fff2f7 0%,#fffafc 100%)}
[data-cal-theme="summer"] .sheet{background:linear-gradient(180deg,#eaf9f3 0%,#f7fdfb 100%)}
[data-cal-theme="autumn"] .sheet{background:linear-gradient(180deg,#fff1e6 0%,#fffaf6 100%)}
[data-cal-theme="winter"] .sheet{background:linear-gradient(180deg,#edf4ff 0%,#fafcff 100%)}
/* 宫格卡片：hover/active 随季节主色微染 */
[data-cal-theme="spring"] .sheet-item:hover,[data-cal-theme="spring"] .sheet-item.active{background:#fff0f6;border-color:#f5a8c6}
[data-cal-theme="summer"] .sheet-item:hover,[data-cal-theme="summer"] .sheet-item.active{background:#e7f8f1;border-color:#7cc9b3}
[data-cal-theme="autumn"] .sheet-item:hover,[data-cal-theme="autumn"] .sheet-item.active{background:#fff0e4;border-color:#f0b283}
[data-cal-theme="winter"] .sheet-item:hover,[data-cal-theme="winter"] .sheet-item.active{background:#eaf2ff;border-color:#9db8ec}
/* 宫格卡片右上角：当季小角饰（静态、克制） */
[data-cal-theme="spring"] .sheet-item::after{content:"🌸";position:absolute;top:5px;right:7px;font-size:12px;opacity:.5;pointer-events:none;line-height:1}
[data-cal-theme="summer"] .sheet-item::after{content:"🍃";position:absolute;top:5px;right:7px;font-size:12px;opacity:.5;pointer-events:none;line-height:1}
[data-cal-theme="autumn"] .sheet-item::after{content:"🍁";position:absolute;top:5px;right:7px;font-size:12px;opacity:.5;pointer-events:none;line-height:1}
[data-cal-theme="winter"] .sheet-item::after{content:"❄️";position:absolute;top:5px;right:7px;font-size:12px;opacity:.5;pointer-events:none;line-height:1}
/* 面板 panel-head：顶部极淡季节渐变（日历页 .cal-panel 有更具体规则，优先级更高不动） */
[data-cal-theme="spring"] .panel-head{background:linear-gradient(180deg,#ffeaf3 0%,transparent 100%)}
[data-cal-theme="summer"] .panel-head{background:linear-gradient(180deg,#e7f7f1 0%,transparent 100%)}
[data-cal-theme="autumn"] .panel-head{background:linear-gradient(180deg,#ffefe3 0%,transparent 100%)}
[data-cal-theme="winter"] .panel-head{background:linear-gradient(180deg,#eaf2ff 0%,transparent 100%)}
/* 深色：panel-head / 宫格卡片 调暗，避免浅色染在深底上 */
[data-theme="dark"][data-cal-theme="spring"] .panel-head{background:linear-gradient(180deg,#3a2430 0%,transparent 100%)}
[data-theme="dark"][data-cal-theme="summer"] .panel-head{background:linear-gradient(180deg,#16332c 0%,transparent 100%)}
[data-theme="dark"][data-cal-theme="autumn"] .panel-head{background:linear-gradient(180deg,#3a2a1a 0%,transparent 100%)}
[data-theme="dark"][data-cal-theme="winter"] .panel-head{background:linear-gradient(180deg,#1c2a4a 0%,transparent 100%)}
[data-theme="dark"][data-cal-theme="spring"] .sheet-item:hover,[data-theme="dark"][data-cal-theme="spring"] .sheet-item.active{background:#3a2530;border-color:#a05a76}
[data-theme="dark"][data-cal-theme="summer"] .sheet-item:hover,[data-theme="dark"][data-cal-theme="summer"] .sheet-item.active{background:#16332c;border-color:#3f8a74}
[data-theme="dark"][data-cal-theme="autumn"] .sheet-item:hover,[data-theme="dark"][data-cal-theme="autumn"] .sheet-item.active{background:#38281a;border-color:#a06a3c}
[data-theme="dark"][data-cal-theme="winter"] .sheet-item:hover,[data-theme="dark"][data-cal-theme="winter"] .sheet-item.active{background:#1c2a4a;border-color:#5a7dc0}
/* Q 版看板娘贴纸（更多抽屉头部右侧，随季节换 src） */
.sheet-chibi{height:64px;width:auto;margin-left:auto;object-fit:contain}
/* 深色模式 */
[data-theme="dark"]{
  --bg:#0f1320; --card:#1a2030; --line:#2a3245; --line2:#232a3d;
  /* ★ v79.6 无障碍：--tx3 由 #64748b 提亮到 #8696ad（对 card 4.50 / bg 5.13，原 3.41/3.89）。
     ⚠ **没有直接用 #94a3b8**：那个值与本主题的 --tx2 完全相同，会让两级文字层次消失，
       而任务要求「保留两级文字层次」。#8696ad 达标的同时与 --tx2 仍差 0.94。 */
  --tx:#e2e8f0; --tx2:#94a3b8; --tx3:#8696ad;
  --pri:#5b8def; --pri-soft:#1e2a4a;
  --course:#5b8def; --plan:#f5a623; --meet:#a78bfa; --leave:#f87171; --free:#34d399;
  --sh:0 1px 2px rgba(0,0,0,.2), 0 8px 24px rgba(0,0,0,.25);
  --track-bg:#161b2c; --slot-bg:#1a2030; --gap-bg:#141a28;
  --soft:#1e2638;
  --now-color:#f87171;
  color-scheme:dark;
}
[data-theme="dark"] .topbar{background:rgba(26,32,48,.92)}
[data-theme="dark"] .modal{background:var(--card)}
[data-theme="dark"] .btn{background:var(--card);border-color:var(--line)}
[data-theme="dark"] .btn:hover{border-color:#3a4560;background:#1e2638}
[data-theme="dark"] .btn.ghost{background:var(--card)}
[data-theme="dark"] input,[data-theme="dark"] select,[data-theme="dark"] textarea{background:#161b2c;color:var(--tx);border-color:var(--line)}
[data-theme="dark"] .fbar-track{background:var(--track-bg);border-color:var(--line2)}
[data-theme="dark"] .fbar-busy{background:repeating-linear-gradient(-45deg,#2a3245,#2a3245 5px,#232a3d 5px,#232a3d 10px)}
[data-theme="dark"] .wx-day,[data-theme="dark"] .dcard,[data-theme="dark"] .stat-card{background:var(--slot-bg)}
[data-theme="dark"] .wx-hero{background:linear-gradient(120deg,#1e2a4a,#161b2c)}
[data-theme="dark"] .chip{background:#1e2638}
[data-theme="dark"] .tabbar{background:rgba(26,32,48,.97)}
/* 深色模式：修复硬编码浅色背景+深色文字看不清的问题 */
[data-theme="dark"] .tab.active{background:#1e2638;color:var(--tx)}
[data-theme="dark"] .tl-item.k-course{background:linear-gradient(135deg,#1a2540,#1e2a4a);color:#9bc4ff}
[data-theme="dark"] .tl-item.k-plan{background:linear-gradient(135deg,#2a2010,#332818);color:#ffc090}
[data-theme="dark"] .tl-item.k-meet{background:linear-gradient(135deg,#221a40,#2a2050);color:#cab0ff}
[data-theme="dark"] .tl-item.k-leave{background:linear-gradient(135deg,#2a1818,#331e1e);color:#ff9b97}
[data-theme="dark"] .tl-item.k-free{background:linear-gradient(135deg,#102a1a,#1a3322);color:#6beab0}
[data-theme="dark"] .mc{background:#1a2030;color:#9bc4ff}
[data-theme="dark"] .mc.free{background:#102a1a;color:#6beab0}
[data-theme="dark"] .mc.course{background:#1a2540;color:#9bc4ff}
[data-theme="dark"] .mc.plan{background:#2a2010;color:#ffc090}
[data-theme="dark"] .mc.meet{background:#221a40;color:#cab0ff}
[data-theme="dark"] .slot-pill.good{border-color:#1a4a30;background:#102a1a;color:#6beab0}
[data-theme="dark"] .tag.ok{background:#102a1a;color:#6beab0}
[data-theme="dark"] .tag.no{background:#2a1818;color:#ff9b97}
[data-theme="dark"] .tag.wa{background:#2a2010;color:#ffc090}
[data-theme="dark"] .ok-tip{background:#102a1a;border-color:#1a4a30;color:#6beab0}
[data-theme="dark"] .alarm-banner{background:#2a2010;border-color:#5a4020;color:#ffc090}
[data-theme="dark"] .alarm-banner button{color:#ffc090}
[data-theme="dark"] .mx-table td.k-free{background:#102a1a;color:#6beab0}
[data-theme="dark"] .ktag.soon{background:#102a1a;color:#6beab0}
[data-theme="dark"] .stat-card.clickable:hover{background:#1e2638}
[data-theme="dark"] .dtable{background:var(--card)}
[data-theme="dark"] .dtable th{background:#161b2c;color:var(--tx2)}
[data-theme="dark"] .dtable tr:nth-child(even){background:#161b2c}
[data-theme="dark"] .dtable td,[data-theme="dark"] .dtable th{border-color:var(--line)}
/* ===== 深色模式：清掉所有硬编码的浅色底 =====
   以下元素原本写死 background:#fff，深色主题下文字变量 --tx 是浅色 → 白底白字看不清。
   统一改为跟随主题的 var(--card)/var(--slot-bg)。 */
[data-theme="dark"] .item,
[data-theme="dark"] .notice,
[data-theme="dark"] .wsc,
[data-theme="dark"] .kcard,
[data-theme="dark"] .mrow,
[data-theme="dark"] .pick-item,
[data-theme="dark"] .slot-pill,
[data-theme="dark"] .mchip,
[data-theme="dark"] .dtab,
[data-theme="dark"] .sheet,
[data-theme="dark"] .modalbg .modal{background:var(--card)}
[data-theme="dark"] .btn.danger{background:var(--card);border-color:#5a2a28;color:#ff9b97}
[data-theme="dark"] .dept-sel,
[data-theme="dark"] .role-sel,
[data-theme="dark"] .pick button{background:#161b2c;border-color:var(--line);color:var(--tx2)}
[data-theme="dark"] input:focus,
[data-theme="dark"] select:focus,
[data-theme="dark"] textarea:focus{background:#161b2c}
/* 浅色底的状态类：整块换成深色语义色 */
[data-theme="dark"] .kcard.overdue{background:#2a1818;border-color:#5a2a28}
[data-theme="dark"] .kcard.urgent{background:#2a2010;border-color:#5a4020}
[data-theme="dark"] .read-badge.unread{background:#2a2010;color:#ffc090}
[data-theme="dark"] .bar-row .bb{background:#232a3d}
/* 二维码必须留白底，否则扫不出来——只反转二维码周围的边框色 */
[data-theme="dark"] .qr-cv{border-color:var(--line)}
[data-theme="dark"] .wx-hero{color:var(--tx)}
/* 最近动态行（.item 的 .meta 在小字下最容易糊，单独提亮一档） */
[data-theme="dark"] .item .meta{color:#8fa0bb}
/* ============ v77.4 T3：看板娘大主题（蓝白） ============
   全局底色换肤：覆盖 CSS 变量 + 修正少量硬编码的蓝紫主色。
   与日历四季主题 [data-cal-theme] 共存：calTheme 只管日历页局部配色，bigTheme 管全局底色。
   强调红 #e4544f 保留（badge / 日历头 / 农历节日色都不动）。 */
[data-theme="mascot"]{
  --bg:#eef6fb; --card:#ffffff; --line:#d5e7f2; --line2:#e3eef6;
  /* ★ v79.6 无障碍：--tx3 由 #7d95a4 加深到 #5b7382（对 card 4.98 / bg 4.55，原 3.13/2.86）。
     取 #5b7382 而非备选 #627989：后者对 --bg(#eef6fb) 只有 4.16，**不达标** ——
     .hint 不只出现在卡片上，也出现在页面底色上，只对 card 算是不够的。 */
  --tx:#1b2a36; --tx2:#4f6879; --tx3:#5b7382;
  --pri:#4aa8d8; --pri-soft:#e4f3fb;
  --course:#4aa8d8; --plan:#f0913a; --meet:#8b5cf6; --leave:#e4544f; --free:#22a06b;
  --sh:0 1px 2px rgba(30,90,130,.05), 0 8px 24px rgba(30,90,130,.07);
  --track-bg:#e2eff7; --slot-bg:#f3fafd; --gap-bg:#e9f3f9;
  --soft:#eef6fb;
  --now-color:#e4544f;
  color-scheme:light;
}
[data-theme="mascot"] .topbar{background:rgba(247,252,255,.94)}
[data-theme="mascot"] .tabbar{background:rgba(247,252,255,.97)}
[data-theme="mascot"] .btn.primary:hover{background:#3793c4}
[data-theme="mascot"] .cell.sel{border-color:#aad6ee}
[data-theme="mascot"] .cell:hover{background:#f2f9fd}
[data-theme="mascot"] .navbtn:hover{background:#e5f1f8}
[data-theme="mascot"] .chip{background:#e6f1f8}
[data-theme="mascot"] .tabs{background:#e6f0f7}
[data-theme="mascot"] .auth-wrap{background:radial-gradient(1000px 500px at 20% -10%,#dceef8 0%,transparent 60%),var(--bg)}
[data-theme="mascot"] .sheet-item.active,[data-theme="mascot"] .pick.tags button.on{border-color:#aad6ee}
[data-theme="mascot"] .wsgrid .wsg-c.on{background:#e3f2fa;box-shadow:inset 0 0 0 2px var(--pri)}
/* v77.5 T3：看板娘主色随季 —— 仅 [data-theme=mascot][data-season=…]（autoSeason 开启时由 applyTheme 挂上）。
   颜色对应四季立绘衣服主色：春粉 / 夏青 / 秋橙 / 冬蓝；强调红 #e4544f 保留不动。 */
[data-theme="mascot"][data-season="spring"]{--pri:#f0a7c0;--pri-soft:#fdeef3;--course:#f0a7c0}
[data-theme="mascot"][data-season="summer"]{--pri:#5ec8a8;--pri-soft:#e6f6f0;--course:#5ec8a8}
[data-theme="mascot"][data-season="autumn"]{--pri:#e8944a;--pri-soft:#fdf0e4;--course:#e8944a}
[data-theme="mascot"][data-season="winter"]{--pri:#9cc8e8;--pri-soft:#eaf4fc;--course:#9cc8e8}
[data-theme="mascot"][data-season="spring"] .btn.primary:hover{background:#e0809f}
[data-theme="mascot"][data-season="summer"] .btn.primary:hover{background:#3fb18c}
[data-theme="mascot"][data-season="autumn"] .btn.primary:hover{background:#d67c2e}
[data-theme="mascot"][data-season="winter"] .btn.primary:hover{background:#7fb0d8}
[data-theme="mascot"][data-season="spring"] .cell.sel{border-color:#f5c8d8}
[data-theme="mascot"][data-season="summer"] .cell.sel{border-color:#b2e0d0}
[data-theme="mascot"][data-season="autumn"] .cell.sel{border-color:#f5d0a8}
[data-theme="mascot"][data-season="winter"] .cell.sel{border-color:#c8e2f4}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--bg); color:var(--tx);
  font:14px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
  -webkit-font-smoothing:antialiased;
  /* ★ v78 P0-3：全站数字等宽 —— 课表 / 周视图的时间（08:00、13:30）不再因字符宽度不同而左右跳动，
     数字列（人数、条数、百分比）也自动对齐。只影响数字字形，中文字形不变。 */
  font-variant-numeric:tabular-nums;
}
h1,h2,h3,h4{margin:0}
button{font:inherit;cursor:pointer}
input,select,textarea{font:inherit;color:var(--tx)}
.hidden{display:none !important}

/* ---------- buttons ---------- */
.btn{border:1px solid var(--line);background:#fff;color:var(--tx);border-radius:var(--r-md);padding:8px 14px;transition:.15s;overflow:hidden}
.btn:hover{border-color:#cfd8e8;background:#fbfcff}
.btn.primary{background:var(--pri);border-color:var(--pri);color:#fff}
.btn.primary:hover{background:#2f5fe0}
.btn.ghost{background:#fff}
.btn.danger{background:#fff;border-color:#f3c2c0;color:#c93c37}
.btn.danger:hover{background:#fdf3f2}
.btn.tiny{padding:5px 10px;font-size:13px;border-radius:var(--r-md);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.btn.block{width:100%}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ---------- auth ---------- */
.auth-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;
  background:radial-gradient(1000px 500px at 20% -10%,#e6edff 0%,transparent 60%),var(--bg)}
.auth-card{width:100%;max-width:460px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);
  padding:26px;box-shadow:var(--sh)}
.auth-brand{display:flex;gap:14px;align-items:center;margin-bottom:18px}
.auth-logo,.logo{width:42px;height:42px;border-radius:var(--r-md);background:var(--pri);color:#fff;display:grid;
  place-items:center;font-weight:500;font-size:20px;flex:none;overflow:hidden}
/* ★ logo 里面可以放 <img>（换掉原来的纯文字）。图片必须显式钉死到容器尺寸，
   否则会按原图尺寸（如 240x240）撑爆 32px 的圆形/圆角方块，把整个顶栏挤烂。 */
.logo img{width:100%;height:100%;display:block;object-fit:cover}
.auth-logo img{width:100%;height:100%;display:block;object-fit:cover}
.auth-brand h1{font-size:20px}
.auth-brand p{margin:2px 0 0;color:var(--tx2);font-size:13px}
/* 登录卡里的表单：label 竖排（原来只有 .pane label 有这套，新加的密码框不在 pane 里） */
.auth-card form label{display:flex;flex-direction:column;gap:6px;font-size:13px;color:var(--tx2);margin-bottom:10px}
.auth-card form input{border:1px solid var(--line);border-radius:var(--r-md);padding:10px 12px;background:#fbfcfe;outline:none;font-size:14px;width:100%;box-sizing:border-box;font-family:inherit}
.auth-card form input:focus{border-color:var(--pri);background:#fff}
/* v49：登录页密码「显示/隐藏」切换（👁） */
.pwd-line{display:flex;gap:6px;align-items:stretch}
.pwd-line input{flex:1;min-width:0}
.eye-btn{flex:none;width:44px;border:1px solid var(--line);border-radius:var(--r-md);background:#fbfcfe;color:var(--tx2);
  font-size:16px;line-height:1;cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent}
.eye-btn:hover{border-color:var(--pri);color:var(--pri)}
.eye-btn:active{background:var(--pri-soft)}
[data-theme="dark"] .eye-btn{background:#161b2c;color:var(--tx2)}
.auth-card #pickWrap{margin-bottom:14px}
.auth-card .sec-title{font-size:13px;color:var(--tx3);margin:0 0 8px}
.tabs{display:flex;gap:4px;background:#f1f4fa;padding:4px;border-radius:var(--r-md);margin-bottom:16px}
.tab{flex:1;border:0;background:transparent;padding:7px 4px;border-radius:var(--r-md);color:var(--tx2);font-size:13px}
.tab.active{background:#fff;color:var(--tx);box-shadow:0 1px 3px rgba(20,30,60,.10);font-weight:500}
.pane{display:none;flex-direction:column;gap:12px}
.pane.active{display:flex}
.pane label{display:flex;flex-direction:column;gap:6px;font-size:13px;color:var(--tx2)}
.pane input,.pane textarea,.fld input,.fld select,.fld textarea{
  border:1px solid var(--line);border-radius:var(--r-md);padding:10px 12px;background:#fbfcfe;outline:none;font-size:14px;width:100%}
.pane input:focus,.fld input:focus,.fld select:focus,.fld textarea:focus{border-color:var(--pri);background:#fff}
.row{display:flex;gap:8px}
.row input{flex:1}
.hint-label{color:var(--tx3) !important;font-size:12px !important}
.err{color:#c93c37;font-size:13px;min-height:18px;margin:0}
.auth-foot{margin:14px 0 0;color:var(--tx3);font-size:12px;text-align:center}

/* ---------- layout ---------- */
.topbar{position:sticky;top:0;z-index:40;display:flex;align-items:center;gap:16px;padding:10px 18px;
  background:rgba(255,255,255,.92);backdrop-filter:blur(8px);border-bottom:1px solid var(--line);
  /* 🔴 v128 修（用户截图：「顶部还是方角」）：顶栏整条横贯屏幕、底部两角是方角，
     与下方圆角卡片群不协调 ⇒ 只圆底部两角（顶部两角贴屏幕上沿，圆了也看不见）。 */
  border-radius:0 0 var(--r-lg) var(--r-lg)}
.brand{display:flex;align-items:center;gap:9px;font-weight:500}
.brand .logo{width:32px;height:32px;border-radius:var(--r-md);font-size:14px}
/* 团队名可改（「数据/部门」页可编辑），名字太长不能把顶栏挤变形 */
#teamBrand{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px}
#nav{display:flex;gap:4px;flex:1;flex-wrap:wrap}
.navbtn{border:0;background:transparent;padding:7px 12px;border-radius:var(--r-md);color:var(--tx2);font-size:13px;position:relative}
.navbtn:hover{background:#f1f4fa}
.navbtn.active{background:var(--pri-soft);color:var(--pri);font-weight:500}
/* 鼠标点击按钮后不留黑色描边框；键盘 Tab 导航仍显示焦点框 */
button:focus{outline:none}
button:focus-visible{outline:2px solid var(--pri);outline-offset:2px}
.badge{position:absolute;top:1px;right:2px;min-width:15px;height:15px;padding:0 4px;border-radius:var(--r-md);background:#e4544f;
  color:#fff;font-size:12px;line-height:15px;font-style:normal;text-align:center}
.me{display:flex;align-items:center;gap:8px}
/* ★ v99：顶栏右侧按钮（🔄同步 / 📚换课表 / 🌙 / 切换身份 …）禁止被 flex 压缩或换行，
   否则「📚 换课表」这种较宽的按钮会被挤到文字溢出/截断（用户原话「字露出来了」）。 */
.me .btn{flex:0 0 auto;white-space:nowrap}
.chip{display:inline-flex;align-items:center;gap:6px;background:#f1f4fa;border-radius:var(--r-md);padding:4px 11px;font-size:13px}
.chip .av{width:19px;height:19px;border-radius:50%;color:#fff;display:grid;place-items:center;font-size:12px}
.chip .role{color:var(--tx3);font-size:12px}
/* 「当前 xxx」登录账号标识：让人一眼看出现在是谁在用 */
.chip .me-lbl{font-size:12px;color:var(--tx3);background:rgba(127,127,127,.13);border-radius:var(--r-md);padding:1px 7px}
.chip .me-name{font-weight:500}

/* ⚠ v80b35 曾在此加过「给 #main 顶部让位避免 sticky 顶栏遮挡」，**已撤销**。
   撤销理由（QA 独立复验 docs/_qa_ckbar_复验报告.md，实测）：
     ① **手机端根本没生效**：被下面两条媒体查询整条覆盖 ——
        `:538 @media(max-width:900px){#main{padding:12px}}`
        `:976 @media(max-width:760px){#main{padding:10px 10px …}}`
        实测 padTop：360/390/414 ＝ **10px**（＝ :976 的值）、1280 ＝ 147.4px
        ⇒ 4 个视口里 3 个（正是用户真机宽度）完全没受惠。
     ② **几何自抵消（最致命）**：给 #main 加顶部内边距 P ⇒ 内容下移 P，
        **可滚动距离也正好 +P** ⇒ 滚到底时相对位置几乎不变。
        实测 1280px 现状落点 y=74.9 vs 回退版 y=75.5（只差 0.6px）。
        ⇒ 这一类修法**数学上治不了**末端重叠，不是调参能解决的。
     ③ **副作用大**：#main 是全站容器，1280px 顶部多 147px 空白，
        首页/日历/备忘录/设置**每页**都被顶下去。
     ④ 兜底 `var(--qz-topbar-h, 84px)` 在宽屏**偏小**（实测 131.42px），
        首帧/JS 失败时宽屏少留 47px。
   ⇒ 教训（已进项目 memory）：**修「遮挡」要在让出空间的那一层修**；
     但若元素**本身就卡在滚动行程两端**，任何「整体下移」都会与滚动距离同步抵消
     ⇒ 这种末端重叠必须另找办法（缩窄元素高度、或让关键控件常驻可见），不是加 padding。 */
/* 🔴 v80b38 修一个真缺陷（用户实测报「番茄钟点开始后页面一直翻页、始终不显示」）：
   窄屏下**底部固定标签栏 `.tabbar`（styles.css:945，position:fixed; bottom:0; z-index:120）**
   盖住了番茄钟的「开始专注」按钮 ⇒ 点下去实际点在标签栏上 ⇒ 切了页面（用户看到的「翻页」）
   ⇒ 番茄钟从未启动、状态一直是「空闲」。
   ⚠⚠ 实测证据（真浏览器 + 真实 tap，smoke/_pomo_mobile.js）：
       375×667：按钮 y=666..719，落点 `elementFromPoint` = **`BUTTON.tabbtn`
                < NAV#tabbar**（isBtn=false）⇒ state 停在 `idle`、文案「空闲」
       390×844：落点 = `BUTTON.v797-btn-lg`（正确）⇒ state = `running`「进行中 · 学习」
       414×896：同上，正确
   ⇒ **这不是番茄钟的 bug，是内容区没给固定标签栏留位**。
   ⚠ 为什么用 CSS 变量而不是写死 px：
     标签栏高度含 `env(safe-area-inset-bottom)`（iPhone 底部安全区），
     各机型不同（实测 375 窄屏视口可用高度已被压到 724）⇒ 写死必然漏掉一部分机型。
     `--qz-tabbar-h` 由 app.js 的 `syncTabbarH()` 用 ResizeObserver **实测**写入
     （与既有的 `syncTopbarH()` 同一套机制，app.js:8773 附近）⇒ 跟随机型与横竖屏。
   ⚠ 与 v80b35 那次「给 #main 加顶部让位」**不同、也不会自抵消**：
     那是「让位 P ⇒ 可滚动距离也 +P」（滚到端末尾相对位置不变）；
     这是**底部固定栏遮挡内容**（内容被压在栏下面、根本点不到），
     加底部 padding 让内容**可滚到标签栏上方**是直接有效的。*/
#main{padding:16px 18px calc(var(--qz-tabbar-h, 58px) + 24px);max-width:1360px;margin:0 auto}
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--sh)}
.panel-head{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line2);flex-wrap:wrap}
/* 🔴 v128 修（用户截图：「倒数日·纪念日」等面板顶部还是方角）：
   .panel 本体是圆角（--r），但 .panel-head 的背景（主题渐变/头部色带）是**方形矩形**，
   直接盖在面板顶部两角 ⇒ 圆角被方角背景吃掉。
   ⇒ 给「作为面板第一个子元素的头部」补上与面板一致的顶部圆角（-1px 抵消面板自身 1px 边框）。
   注意：只圆顶部两角；头部自身背景会被自己的 border-radius 裁剪，无需 overflow:hidden。
   任务看板那种裸 panel-head（无背景、不包在 .panel 里）不受影响，天然安全。 */
.panel>.panel-head:first-child{border-radius:calc(var(--r) - 1px) calc(var(--r) - 1px) 0 0}
/* 🔴 v128 补（用户再报：任务看板头部条还是方角）：任务看板的头部是「裸面板头」——
   不包在 .panel 卡片里、直接是 .view 的第一个子元素，但同样吃到主题渐变背景
   （[data-cal-theme] .panel-head 的背景对任意 panel-head 生效）⇒ 顶部两角方角。
   ⇒ 给「直接铺在视图顶部的裸头部」补同款顶部圆角（无 1px 边框抵消，直接用 --r）。
   渐变到底部已淡成透明，底边无需处理；其余位置的裸头部（中间插页）不受影响。 */
.view>.panel-head:first-child{border-radius:var(--r) var(--r) 0 0}
.panel-head h2,.panel-head h3{font-size:14px;flex:1;white-space:nowrap}
/* 公告面板工具栏：不换行、收紧间距，免得「+ 发布」被挤到第二行 */
.panel-head.tight{gap:6px;padding:12px 14px}
.panel-head.tight h3{flex:0 1 auto;font-size:14.5px;margin-right:auto}
.panel-head.tight .tag{font-size:12px;padding:1px 6px;white-space:nowrap}
.panel-head.tight .dept-sel{padding:4px 6px;font-size:12px;max-width:92px}
.panel-head.tight .btn.tiny{padding:4px 8px;font-size:12px}
.panel-body{padding:14px 16px}
.sec-title{font-size:13px;color:var(--tx2);font-weight:500;margin:0 0 8px}
.muted{color:var(--tx3)}

/* ---------- calendar ---------- */
.cal-layout{display:grid;grid-template-columns:minmax(340px,1fr) minmax(360px,1.05fr);gap:16px;align-items:start}
.week-head,.month{display:grid;grid-template-columns:repeat(7,1fr)}
.week-head span{text-align:center;font-size:12px;color:var(--tx3);padding:8px 0}
.month{padding:0 6px 6px}
.cell{border:1px solid transparent;border-radius:var(--r-md);padding:5px 4px 6px;min-height:66px;cursor:pointer;
  display:flex;flex-direction:column;gap:3px;transition:.12s;overflow:hidden}
.cell:hover{background:#f7f9fe}
.cell.out{opacity:.35}
.cell.sel{background:var(--pri-soft);border-color:#bcd0ff}
.cell.today .dnum{background:var(--pri);color:#fff;border-radius:50%;width:22px;height:22px;display:grid;place-items:center}
/* ★ v78 P1-2：日历视觉加厚 —— 今天一眼可见、周末淡底、选中态更明确、事件标签左侧色条 */
.cell.today{background:var(--pri-soft);border-color:#bcd0ff;
  box-shadow:0 0 0 1px var(--pri) inset, 0 2px 8px rgba(59,110,245,.14)}
.cell.today>.cal-top .num{color:var(--pri);font-weight:500}
.cell.we{background:#fafbff}
.cell.we:hover{background:#f2f6ff}
.cell.sel{box-shadow:0 0 0 2px #bcd0ff inset}
.cell.sel.today{box-shadow:0 0 0 2px var(--pri) inset, 0 2px 10px rgba(59,110,245,.18)}
:root[data-theme="dark"] .cell.we{background:rgba(255,255,255,.03)}
:root[data-theme="dark"] .cell.today{box-shadow:0 0 0 1px var(--pri) inset, 0 2px 10px rgba(0,0,0,.4)}
.dnum{font-size:13px;font-weight:500;width:22px;height:22px;display:grid;place-items:center}
/* 日期数字 + 事项圆点同一行：竖排两行太占高，也容易和后面内容挤在一起 */
.cell>.cal-top{display:flex;align-items:center;gap:4px;line-height:1.2;min-width:0}
.cell>.cal-top .num{font-size:13px;font-weight:500;flex:none}
.cell>.cal-top .dots{display:flex;align-items:center;gap:3px;flex:none}
.cell>.dots .dot,.cell>.cal-top .dots .dot{margin-left:0}
.cell .tags{display:flex;flex-wrap:wrap;gap:3px}
/* 月视图单元格里的天气：小到几乎不占位（真要看天气点进日视图） */
.cell>.cal-top .wx-chip.sm,.cell>.wx-chip.sm{display:none}
/* ★ v78 P1-2：事件标签加左侧色条（半透明白）+ 轻投影，多个标签堆叠时层次更清楚 */
.tg{font-size:12px;line-height:15px;padding:0 6px 0 7px;border-radius:var(--r-md);color:#fff;white-space:nowrap;
  max-width:100%;overflow:hidden;border-left:3px solid rgba(255,255,255,.6);box-shadow:0 1px 2px rgba(20,30,60,.14)}
.tg.course{background:var(--course)} .tg.plan{background:var(--plan)}
.tg.meet{background:var(--meet)} .tg.leave{background:var(--leave)}
.legend{display:flex;align-items:center;gap:6px;padding:10px 16px 14px;color:var(--tx3);font-size:12px;flex-wrap:wrap}
.dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-left:8px}
.dot:first-child{margin-left:0}
.c-course{background:var(--course)} .c-plan{background:var(--plan)}
.c-meet{background:var(--meet)} .c-leave{background:var(--leave)} .c-free{background:var(--free)}

/* ---------- day timeline（美化版） ---------- */
.day-head{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line2);flex-wrap:wrap}
.day-head h3{font-size:16px}
.day-head .sub{color:var(--tx3);font-size:13px}
.tl{display:flex;padding:12px 14px 14px;gap:10px}
.tl-labels{flex:none;width:76px;padding-top:0}
.tl-label{height:58px;font-size:12px;color:var(--tx3);display:flex;flex-direction:column;justify-content:center;
  align-items:flex-end;padding-right:8px;border-bottom:1px dashed var(--line2)}
.tl-label b{color:var(--tx2);font-weight:500;font-size:13px}
.tl-label span{font-variant-numeric:tabular-nums}
/* ★ v101 #4：节次合并后的标签（每 N 节一块）。
   ⚠⚠ 只改**显示**：高度由 JS 给（=组内各节高度之和，总高不变），
     这里不写任何 height —— 写死会把「合并后总高 ≠ 原总高」的问题引回来，
     进而让左侧标签与右侧课块错位。
   ⚠ 合并块的底边画成**实线**：虚线（.tl-label 默认的 dashed）表达的是
     「每节之间的分隔」，合并成一块后内部不再有节间分隔，
     继续用虚线会让人以为「这一块里其实还有几节被藏了」——
     正好与「合并是为了清爽」的目的相反。
   ⚠ 字号不缩小：合并后文字是「第1-3节」这种更长的串，
     缩字号会连「第」「节」都看不清；宁可让它占两行的宽度（44px 列宽够放）。 */
.tl-label.merged{border-bottom-style:solid;background:var(--slot-bg,rgba(139,147,167,.05))}
.tl-label.merged b{font-size:12px;line-height:1.2}
.tl-track{position:relative;flex:1;height:696px}
.tl-track:before{content:'';position:absolute;inset:0;border-radius:var(--r-md);background:
  linear-gradient(180deg,#fcfdff,#fafbfe)}
.tl-slot{position:absolute;left:0;right:0;border-bottom:1px dashed var(--line2);cursor:pointer;
  border-radius:var(--r-md);transition:background .12s}
.tl-slot:hover{background:rgba(59,110,245,.055)}
/* v56：5-6 节「午休带」（分钟时间轴，日/周视图共用）—— 标签格 + track slot 淡色斜纹底 */
.tl-label.tl-break{background:repeating-linear-gradient(-45deg, rgba(139,147,167,.07), rgba(139,147,167,.07) 6px,
  rgba(139,147,167,.13) 6px, rgba(139,147,167,.13) 12px)}
/* ★ v101 #4：周课表工具条上的「节次合并」下拉。
   ⚠ 为什么用 <select> 而不是按钮组：6 档（1/2/3/4/6/12）× 每档文案都不同，
     按钮组要占 6 个按钮的位置（工具条已经很长、必然换行）；
     select 只占一个控件宽度，且原生下拉在手机上就是系统选择器、手感最好。 */
.wv-merge-pick{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--tx2);white-space:nowrap}
.wv-merge-pick select{font-size:12px;padding:3px 6px;min-height:28px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--card);color:var(--tx)}
.tl-slot.tl-break{background:repeating-linear-gradient(-45deg, rgba(139,147,167,.05), rgba(139,147,167,.05) 6px,
  rgba(139,147,167,.10) 6px, rgba(139,147,167,.10) 12px)}
.tl-item{position:absolute;border-radius:var(--r-md);padding:7px 10px;overflow:hidden;font-size:12px;line-height:1.35;
  cursor:pointer;border:1px solid transparent;display:flex;flex-direction:column;gap:2px;
  transition:transform .12s,box-shadow .12s}
.tl-item:hover{transform:translateY(-1px);box-shadow:0 6px 18px rgba(20,30,60,.16)}
.tl-item .t{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:.1px}
.tl-item .s{opacity:.78;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  font-variant-numeric:tabular-nums}
.tl-item.k-course{background:linear-gradient(135deg,#e3ecff,#d7e4ff);border-left:3px solid var(--course);color:#1e3c8a;
  box-shadow:0 1px 3px rgba(59,110,245,.14)}
.tl-item.k-plan{background:linear-gradient(135deg,#fff4e4,#ffecd2);border-left:3px solid var(--plan);color:#8c4d14;
  box-shadow:0 1px 3px rgba(240,145,58,.16)}
.tl-item.k-meet{background:linear-gradient(135deg,#f4eeff,#eae0ff);border-left:3px solid var(--meet);color:#553496;
  box-shadow:0 1px 3px rgba(139,92,246,.16)}
.tl-item.k-task{background:linear-gradient(135deg,#e0f3ff,#d1ecff);border-left:3px solid #0ea5e9;color:#0b5c8a;
  box-shadow:0 1px 3px rgba(14,165,233,.16)}
.tl-item.k-leave{background:linear-gradient(135deg,#fdeeed,#fbe0de);border-left:3px solid var(--leave);color:#9c322e;
  box-shadow:0 1px 3px rgba(228,84,79,.14)}
.tl-item.k-free{background:linear-gradient(135deg,#eafaf1,#ddf4e7);border-left:3px solid var(--free);color:#186b45;
  box-shadow:0 1px 3px rgba(34,160,107,.14)}
.tl-item .del{position:absolute;top:4px;right:6px;font-size:12px;opacity:0;transition:.15s;
  background:rgba(255,255,255,.8);border-radius:50%;width:16px;height:16px;line-height:15px;text-align:center}
.tl-item:hover .del{opacity:.75}
.tl-item .del:hover{opacity:1;color:#c93c37}
/* ⚠⚠⚴🔴 v80b52【问题A】触屏上删除按钮**常驻可见 + 够大**
   ⚠⚠⚴ 改前实测根因（用户报「删除没反应」的真因）：
     上面 `.tl-item .del` 是 `opacity:0` + 只在 `:hover` 时显现
     ⇒ **触屏设备没有 hover ⇒ 永远透明、永远点不到**
     ⇒ 而删除的 JS（app.js:13467）绑在 `e.target.dataset.del` 上，
       它是 `.tl-item` 的**子元素** ⇒ 透明时手指命中的是**父元素**，
       `dataset.del` 为空 ⇒ 直接走「打开详情」分支
     ⇒ 用户点「删除」却只弹了详情 ⇒ 看起来就是「没反应」。
   ⚠⚠⚴ **注意：这不是「作者没想到触屏」** ——
     styles.css:1221 / 1329 **已经有** `@media (hover:none){…opacity:1}`
     （.notice.row-card .nc-del 与 .act-row .adel），
     ⇒ **只是漏了 .tl-item .del 这一处** ⇒ 属「漏项」，补齐即可。
   ⚠ 点击目标：原来只有 **16×16px**，远小于移动端 44px 下限
     ⇒ 触屏下放大到 **32×32px**（不硬凑 44px，那会盖住时间轴文字；32px 已达常见可点下限）。
   ⚠⚠⚴ 必须写成 `@media (hover:none),(pointer:coarse)` 两者：
     只写 hover:none 时，**装了鼠标/触控板的触屏笔记本**会走 hover 分支 ⇒ 又点不到。
   ⚠ 位置：top/right 略调，保证 32px 的圆钮不超出卡片边界、不遮时间。 */
@media (hover:none),(pointer:coarse){
  .tl-item .del{opacity:.75;width:32px;height:32px;line-height:31px;font-size:15px;top:2px;right:4px;
    box-shadow:0 1px 4px rgba(0,0,0,.18);z-index:2}
  .tl-item .del:active{opacity:1;color:#c93c37}
}
.day-foot{padding:0 16px 14px;display:flex;gap:8px;flex-wrap:wrap}
/* ★ v78 P0-2：空状态卡片 —— 原来只有一行灰字，现在是一张有插画的空状态卡（四季各一幅） */
.empty{padding:26px 18px 24px;text-align:center;color:var(--tx3);font-size:13px;
  background:linear-gradient(180deg,var(--soft),rgba(255,255,255,0));
  border:1px dashed var(--line2);border-radius:var(--r-md);margin:6px 0}
.empty::before{content:"";display:block;width:64px;height:64px;margin:0 auto 10px;border-radius:50%;
  background:var(--pri-soft) center/40px 40px no-repeat;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect x='11' y='27' width='42' height='26' rx='5' fill='%23ffffff' stroke='%23c3cee2' stroke-width='2'/%3E%3Cpath d='M11 35h12l3 6h12l3-6h12' fill='none' stroke='%23c3cee2' stroke-width='2'/%3E%3Ccircle cx='32' cy='17' r='7' fill='%23dbe5fb'/%3E%3C/svg%3E")}
/* 四季各换一幅插画（春花瓣 / 夏荷叶 / 秋枫叶 / 冬雪花） */
[data-cal-theme="spring"] .empty::before{background-color:#ffe9f2;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect x='11' y='27' width='42' height='26' rx='5' fill='%23ffffff' stroke='%23f0bcd2' stroke-width='2'/%3E%3Cpath d='M11 35h12l3 6h12l3-6h12' fill='none' stroke='%23f0bcd2' stroke-width='2'/%3E%3Cg fill='%23f58ab0'%3E%3Cellipse cx='32' cy='12' rx='5' ry='7'/%3E%3Cellipse cx='38' cy='18' rx='5' ry='7' transform='rotate(60 38 18)'/%3E%3Cellipse cx='26' cy='18' rx='5' ry='7' transform='rotate(-60 26 18)'/%3E%3C/g%3E%3Ccircle cx='32' cy='18' r='3' fill='%23ffe08a'/%3E%3C/svg%3E")}
[data-cal-theme="summer"] .empty::before{background-color:#e4f6ef;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect x='11' y='27' width='42' height='26' rx='5' fill='%23ffffff' stroke='%23a5d8c6' stroke-width='2'/%3E%3Cpath d='M11 35h12l3 6h12l3-6h12' fill='none' stroke='%23a5d8c6' stroke-width='2'/%3E%3Cellipse cx='32' cy='22' rx='11' ry='5' fill='%235cb894'/%3E%3Cpath d='M32 16q-5-7 0-11 5 4 0 11' fill='%23f78fb3'/%3E%3C/svg%3E")}
[data-cal-theme="autumn"] .empty::before{background-color:#fdecdc;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect x='11' y='27' width='42' height='26' rx='5' fill='%23ffffff' stroke='%23e8b98a' stroke-width='2'/%3E%3Cpath d='M11 35h12l3 6h12l3-6h12' fill='none' stroke='%23e8b98a' stroke-width='2'/%3E%3Cpath d='M32 26l4-8 1 5 5-1-4 4 4 4-5-1-1 5-4-8z' fill='%23e8963c'/%3E%3Cpath d='M23 14q5 4 4 9M41 14q-5 4-4 9' stroke='%23c98a4e' stroke-width='1.6' fill='none'/%3E%3C/svg%3E")}
[data-cal-theme="winter"] .empty::before{background-color:#e8f0fc;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect x='11' y='27' width='42' height='26' rx='5' fill='%23ffffff' stroke='%23b5c9e8' stroke-width='2'/%3E%3Cpath d='M11 35h12l3 6h12l3-6h12' fill='none' stroke='%23b5c9e8' stroke-width='2'/%3E%3Cg stroke='%237ea3d8' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M32 10v14M26 14l12 7M38 14l-12 7'/%3E%3C/g%3E%3C/svg%3E")}
/* ★ 重要兜底：下面这些元素只是「名字里带 empty 的空格子」，不是空状态提示，
   不能套上面的空状态卡（否则月视图补位格、周课表空格会变成一张带插画的卡片）。 */
.cell.empty{border-color:transparent;background:transparent;box-shadow:none;padding:5px 4px 6px;min-height:66px;margin:0}
.wsgrid .wsg-c.empty{background:var(--card,#fff);border:0;box-shadow:none;padding:0;min-height:64px;color:#a8b0c2}
.cell.empty::before,.wsgrid .wsg-c.empty::before{display:none}
/* 表格里的空状态（单元格形态，不要画成大卡） */
table td.empty,td.empty{padding:16px;border:0;background:none;box-shadow:none}
table td.empty::before,td.empty::before{display:none}
/* ★ v78 P0-2：骨架屏 —— 加载中不再白屏，用灰色条占位（尊重「减弱动效」） */
.skel{padding:10px 2px}
.skel-row{height:14px;border-radius:var(--r-xs);background:var(--track-bg);margin:10px 0;animation:skelPulse 1.3s ease-in-out infinite}
.skel-row.h20{height:20px}
.skel-row.w40{width:40%}.skel-row.w60{width:60%}.skel-row.w80{width:80%}
.skel-row:nth-child(2){animation-delay:.1s}.skel-row:nth-child(3){animation-delay:.2s}
.skel-row:nth-child(4){animation-delay:.3s}.skel-row:nth-child(5){animation-delay:.4s}
@keyframes skelPulse{0%,100%{opacity:.5}50%{opacity:.95}}
@media (prefers-reduced-motion:reduce){.skel-row{animation:none}}

/* ---------- team matrix ---------- */
.matrix-wrap{overflow:auto}
table.matrix{border-collapse:separate;border-spacing:3px;width:100%;min-width:640px}
table.matrix th{font-size:12px;color:var(--tx2);font-weight:500;padding:4px 2px}
table.matrix th.pn{min-width:44px}
table.matrix th.who{text-align:left;min-width:110px;padding-left:6px}
table.matrix td.who{font-size:13px;padding-left:6px;white-space:nowrap}
table.matrix td.who .av{display:inline-grid;place-items:center;width:20px;height:20px;border-radius:50%;color:#fff;
  font-size:12px;margin-right:6px;vertical-align:-4px}
.mc{height:30px;border-radius:var(--r-md);text-align:center;font-size:12px;color:#fff;background:#eaf1ea;color:#2f6b52}
.mc.free{background:#e3f5ec;color:#1d7a52;font-weight:500}
.mc.course{background:#dfe8ff;color:#22408f}
.mc.plan{background:#ffe8cf;color:#94541a}
.mc.meet{background:#ece2ff;color:#5b3ba8}
.mc.leave{background:#fbdedc;color:#a33632}
.slots{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.slot-pill{border:1px solid var(--line);background:#fff;border-radius:var(--r-md);padding:6px 12px;font-size:13px;display:flex;
  align-items:center;gap:8px}
.slot-pill.good{border-color:#a9dcc3;background:#eefaf3;color:#1d7a52}
.slot-pill button{border:0;background:var(--pri);color:#fff;border-radius:var(--r-md);padding:3px 10px;font-size:12px}

/* ---------- lists ---------- */
.list{display:flex;flex-direction:column;gap:10px}
.item{border:1px solid var(--line);border-radius:var(--r-md);padding:12px 14px;background:#fff}
.item.done{opacity:.6}
.item .top{display:flex;align-items:flex-start;gap:10px}
.item .ttl{font-weight:500;font-size:14px;flex:1}
.item .meta{color:var(--tx3);font-size:12px;margin-top:3px}
.item .desc{color:var(--tx2);font-size:13px;margin-top:6px;white-space:pre-wrap;word-break:break-word}
.item .acts{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.tag{display:inline-block;font-size:12px;padding:1px 8px;border-radius:var(--r-md);background:#f1f4fa;color:var(--tx2)}
.tag.ok{background:#e3f5ec;color:#1d7a52} .tag.no{background:#fbdedc;color:#a33632}
.tag.wa{background:#fff3e6;color:#94541a} .tag.pri{background:var(--pri-soft);color:var(--pri)}
.tag.own{background:#3a2a6b;color:#cbb6ff;font-weight:500}
.grid2{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:16px;align-items:start}
/* ★ v121：考勤页专用布局（用户报「右边空了一大块，组件页面可以大一些」）
   ------------------------------------------------------------------
   【为什么不能用 .grid2】它minmax(330px,1fr) + auto-fill ⇒ 按容器宽自动填列。
   实测用户 1060px 宽的屏：只能放 3 列，但「考勤场次」这一块是**一个超宽面板**，
   后面跟「统计（学期汇总）」「排期设置」⇒ 实际只占满前两列，
   右边整块空白 —— 就是用户截图里的样子。
   ⇒ 这里改成**显式比例** + 更小的最小列宽：
      ① 列更多：minmax 330 → 300px ⇒ 同样屏宽能多放一列
      ② 比例分配：场次列略窄（它内容最长，最容易撑高）、汇总列最宽（要放「各场次一览」）
      ③ 面板内容不再被挤成极窄的长条（截图里「应到 / 缺勤 / / 旷…」被压成竖排就是这个原因）
   ⚠ 写这条注释的纪律 —— **什么条件下会变不安全**：
      若将来某个面板被换进 .grid-att（如需要三栏并列的），
      它的宽度会被这里的比例「固定住」而不是自适应 ⇒ 需同时改这里的列定义。*/
.grid-att{display:grid;gap:16px;align-items:start;
  grid-template-columns:minmax(300px,1fr) minmax(340px,1.35fr) minmax(280px,0.9fr)}
/* 汇总面板里的数字块：宽屏横排更易读，窄屏自动堆叠 */
.grid-att .att-sum-grid{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(120px,1fr))}
/* 首页上半区：今日安排 | 团队公告 | 未来 7 天 三列；窄屏自动堆叠 */
.home-top{display:grid;grid-template-columns:minmax(220px,1fr) minmax(340px,1.35fr) minmax(300px,1.25fr);gap:16px;align-items:start}
.home-top>.panel{margin:0}
@media (max-width:1180px){.home-top{grid-template-columns:minmax(220px,1fr) minmax(340px,1.6fr)}}
@media (max-width:820px){.home-top{grid-template-columns:1fr}}

/* ---------- modal（可拖动） ---------- */
/* ★ v79.6：弹窗拆成「层容器 .modal-layer + 纯遮罩 .modal-mask + 卡片 .modal」三层。
   原因：夜间压暗需要给「遮罩」单独上 filter，但 filter 会**级联到子元素**；
   原结构里 .modal 是 .modal-mask 的子节点，一旦给遮罩加 filter，弹窗表单会被
   连带压暗（实测对比度 4.9→3.9，晚上填表看不清）。拆开后遮罩与卡片是兄弟节点，
   给 .modal-mask 加 filter 只压遮罩、不碰卡片 —— 既压暗又保证表单清晰。 */
.modal-layer{position:fixed;inset:0;z-index:100}
.modal-layer.hidden{display:none}

/* ★★ v80.20【修「备忘录输入条压住打卡时段确认弹窗」】
   🔴 用户截图证实的现象：在备忘录输入「每天早上9点记30个单词」→ 弹出
     「每日待办 → 打卡时段（确认后保存）」对话框，但**备忘录的输入条压在弹窗上面**，
     把弹窗内容（星期选择、时长、标签那一块）遮住了。

   ⚠⚠ 真浏览器实测读数（判据 smoke/_ui3_t1b_cover.js，5 套视口）：
     390×844 → 弹窗卡片 y=254..627，输入条 y=194..255，**重叠 346×1px**；
     360×640 / 375×667 → 重叠 316×61 / 331×61（**整个输入条压在卡片上**）；
     1280×800  → 重叠 480×61。
     重叠区 7×7 密集采样：`elementFromPoint` 命中的**全部**是
     `INPUT#v797MemoInput` / `BUTTON#v797MemoAdd` / `DIV.v797-memo-compose`
     ⇒ 弹窗被输入条**整片盖住**，不是「边缘擦到」。

   ⚠⚠ 根因（层级参照，全部来自真实 CSS，不是推测）：
     `.modal-layer`      z-index **100**（styles.css:524）
     `.v797-memo-compose` z-index **1000**（v797.css:115）← 高出弹窗 10 倍
     `.v797-mbmenu`       z-index **1000**（v797.css:222）
     `.v797-pomo-acts`    z-index **130**（v797.css:723，吸底操作条）
     `.tabbar`            z-index **120**（styles.css:986）
     `#musicMini` 118 / `.sheet-wrap` 140 / `.install-bar` 160 /
     `.alarm-banner` 150 / `.back-top` 900 / `.toast-root` 200 / `.pull-tip` 200
     ⇒ 这一批「页面常驻家具」**全都高过弹窗**。
     ⚠ styles.css:3128 的注释**早就写明了 `.v797-memo-compose` z:1000 这件事**
       —— 冲突是已知的，只是当时被当成「night-dim 漏网层」处理（给它单独上
       filter 压暗），**层级本身没动** ⇒ 夜里不刺眼了，白天照样挡弹窗。

   ✅ 修法：**压低被遮挡者，不抬高弹窗**。
     为什么不是抬高 `.modal-layer`：
       ① styles.css:3100 的注释已说明不能再靠堆更高层蒙版/层级解决 ——
          抬高 .modal-layer 会让它盖住 `.v797-rest-mask`(1040)、`.alarm-mask`(3000)、
          `.kick-mask`(400)、`.tc-lightbox`(500)、`.sch-browser`(300)、
          `.intro-ov`(99999) 这一串**各自独立的全屏流程**，它们必须留在最上面；
       ② 抬高弹窗是「让一个东西去迁就全部东西」，压低家具是「让家具让路」，
          后者才是本项目 v79.5/v79.6 一路在用的做法（见 3097-3123 的注释）。

   ⚠ 选 **99** 而不是「压到 95 压暗层之下」：
     99 **恰好只改「与 .modal-layer(100) 的相对关系」这一件事**，
     与压暗层 95 的关系原样保留 ⇒ v80.9 给 `.v797-memo-compose`/`.v797-mbmenu`
     加的 night-dim filter 不会叠加成「双层压暗」（实测 0.74×遮罩 = 过暗）。
     若改成 90/95 以下，夜间会变两次暗，那是**修一个坏两个**。

   ⚠ 只压「页面常驻家具」这 7 类，**故意不碰** .v797-rest-mask / .alarm-mask /
     .kick-mask / .tc-lightbox / .sch-browser / .sch-bmask / .intro-ov /
     .lesson-banner —— 它们是各自独立的全屏流程，语义上就该压过普通弹窗。 */
body.modal-open .v797-memo-compose,
body.modal-open .v797-mbmenu,
body.modal-open .v797-pomo-acts,
body.modal-open .tabbar,
body.modal-open #musicMini,
body.modal-open .sheet-wrap,
body.modal-open .install-bar,
body.modal-open .alarm-banner,
body.modal-open .back-top,
body.modal-open .toast-root,
body.modal-open .pull-tip{z-index:99}

/* 遮罩 z-index:1、卡片 z-index:2 —— 同一层容器内卡片必须画在遮罩之上，
   否则夜间被 filter 的遮罩会盖到卡片上（等于又压暗了表单）。 */
.modal-mask{position:absolute;inset:0;background:rgba(18,24,40,.42);padding:0;z-index:1}
/* v47 #4：弹窗改为「头/脚固定、仅内容区滚动」的 flex 布局，
   保证确认按钮（在 .foot 里）任何内容高度下都在可视区内、始终可点。 */
.modal{position:absolute;z-index:2;background:var(--card);border-radius:var(--r-md);width:100%;max-width:480px;
  max-height:calc(100vh - 24px);display:flex;flex-direction:column;overflow:hidden;
  box-shadow:0 20px 60px rgba(10,20,50,.25);left:0;top:0}
.modal.dragging{user-select:none;transition:none}
.modal h3{padding:14px 18px;border-bottom:1px solid var(--line2);font-size:14px;display:flex;align-items:center;
  gap:8px;cursor:move;background:var(--card);flex:0 0 auto}
.modal-drag-hint{margin-left:auto;color:var(--tx3);font-size:13px;cursor:grab;letter-spacing:-1px}
.modal h3:active .modal-drag-hint{cursor:grabbing}
.modal .body{padding:16px 18px;display:flex;flex-direction:column;gap:12px;
  flex:1 1 auto;overflow:auto;min-height:0}
.modal .foot{padding:12px 18px;border-top:1px solid var(--line2);display:flex;justify-content:flex-end;gap:8px;
  flex:0 0 auto;background:var(--card)}
/* v48：★ 上面三处原来写死 #fff/#fbfcff —— 深色模式下弹窗头/脚是一条白底（用户截图），
   统一改用 --card（浅色 #fff / 深色 #1a2030），暗色残留白底即消失。 */
.fld{display:flex;flex-direction:column;gap:6px;font-size:13px;color:var(--tx2)}
.fld-row{display:flex;gap:10px}
.fld-row>*{flex:1}
.pick{display:flex;gap:6px;flex-wrap:wrap}
.pick button{border:1px solid var(--line);background:#fff;border-radius:var(--r-md);padding:5px 9px;font-size:13px;color:var(--tx2)}
.pick button.on{background:var(--pri);border-color:var(--pri);color:#fff}
.checks{display:flex;flex-wrap:wrap;gap:6px}
/* v69：智能推荐后「该时段有事」的人 —— 半透明 + 红色小标签，
   让人一眼看出「这些人没被自动勾上」，而不是以为系统漏了人。 */
.checks .chk.busy{opacity:.62}
.checks .chk.busy span em{font-style:normal;color:#c0392b;font-size:12px;margin-left:2px}

/* ---------- toast / alarm ---------- */
/* v73：toast 是纯提示、不可交互 —— 必须 pointer-events:none，
   否则底部居中的错误 toast 会实打实盖住下层按钮（实测盖住登录页「登录」按钮，
   e2e 点 #joinBtn 30s 超时）。 */
.toast-root{position:fixed;left:50%;transform:translateX(-50%);bottom:28px;z-index:200;display:flex;
  flex-direction:column;gap:8px;align-items:center;pointer-events:none}
.toast{background:#22293c;color:#fff;border-radius:var(--r-md);padding:9px 16px;font-size:13px;box-shadow:0 8px 24px rgba(0,0,0,.2);pointer-events:none}
.toast.err{background:#b8362f}
/* v77.6：成功 toast 右侧 32px 当季 ok 贴纸 + 空状态插画（均静态、克制） */
.toast.has-emote{display:flex;align-items:center;gap:8px}
.toast.has-emote .toast-txt{flex:1}
.toast-emote{width:32px;height:32px;flex:none;object-fit:contain}
.emote-empty{display:block;margin:8px auto 2px;opacity:.92}
.alarm-banner{position:fixed;top:64px;left:50%;transform:translateX(-50%);z-index:150;background:#fff5e6;
  border:1px solid #f3c98a;color:#8a5316;border-radius:var(--r-md);padding:10px 14px;box-shadow:var(--sh);
  font-size:13px;max-width:min(680px,92vw);display:flex;gap:10px;align-items:center}
.alarm-banner button{border:0;background:transparent;color:#8a5316;font-size:14px}

@media(max-width:900px){
  .cal-layout{grid-template-columns:1fr}
  #main{padding:12px}
  .topbar{flex-wrap:wrap;gap:8px;padding:8px 12px}
  #nav{order:3;width:100%;overflow:auto}
  .tl-labels{width:62px}
  .stat-grid{grid-template-columns:repeat(3,1fr)}
}

/* ---------- 数据后台 ---------- */
.stat-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;margin-bottom:14px}
.stat-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:12px 14px;box-shadow:var(--sh)}
.stat-card .k{font-size:12px;color:var(--tx3)}
.stat-card .v{font-size:16px;font-weight:500;margin-top:2px}
.stat-card.clickable:hover{border-color:var(--pri);box-shadow:0 2px 8px rgba(59,110,245,.15)}
[data-theme="dark"] .stat-card.clickable:hover{box-shadow:0 2px 8px rgba(59,110,245,.25)}
.data-tabs{display:flex;gap:6px;flex-wrap:wrap;padding:10px 16px 0}
.dtab{border:1px solid var(--line);background:#fff;border-radius:var(--r-md);padding:5px 13px;font-size:13px;color:var(--tx2)}
.dtab.on{background:var(--pri);border-color:var(--pri);color:#fff;font-weight:500}
.table-wrap{max-height:60vh;overflow:auto;border-radius:0 0 var(--r) var(--r)}
.dtable{width:100%;border-collapse:collapse;font-size:12.8px;min-width:640px}
.dtable th{position:sticky;top:0;background:#f6f8fc;color:var(--tx2);text-align:left;font-weight:500;
  padding:8px 12px;border-bottom:1px solid var(--line);white-space:nowrap;z-index:2}
.dtable td{padding:7px 12px;border-bottom:1px solid var(--line2);color:var(--tx);white-space:nowrap;max-width:320px;
  overflow:hidden;text-overflow:ellipsis}
.dtable tr:hover td{background:#fafbff}
.hint{color:var(--tx3);font-size:12px}

/* ---------- ★ v79.6：AI 服务商预设（设置页） ---------- */
/* 全部类名带 ai-prov- 前缀：这个项目有过「裸类名撞车」的历史（.sfx-props 就撞过），
   不自造无前缀的类名。布局只做「别溢出 / 别被压成竖排」的最小约束：
   select 撑满一行、提示行允许换行。 */
.ai-prov-fld select{width:100%;max-width:100%}
.ai-prov-keyhint{line-height:1.6;word-break:break-word;overflow-wrap:anywhere}
.ai-prov-keyhint a{color:var(--pri)}
.ai-prov-saved{font-weight:500}

/* ---------- 安装到手机 ---------- */
.steps{margin:6px 0 0;padding-left:20px;display:flex;flex-direction:column;gap:10px;line-height:1.7}
.steps .gly{font-size:14px}
.ok-tip{background:#eefaf3;border:1px solid #bfe8d2;color:#187a4b;border-radius:var(--r-md);padding:10px 14px;font-size:13px}
.share-box{display:flex;gap:8px;margin-top:10px}
.qr-row{display:flex;align-items:center;gap:10px;margin-top:10px;flex-wrap:wrap}
.qr-cv{flex:0 0 auto;background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:4px;line-height:0}
.qr-row .share-box{margin-top:0}
.share-box input{flex:1;border:1px solid var(--line);border-radius:var(--r-md);padding:9px 12px;background:#fbfcfe;font-size:13px;
  color:var(--tx2);overflow:hidden;text-overflow:ellipsis}
/* v62：复制按钮竖排换行 bug —— 按钮固定不换行、不收缩 */
.share-box .btn{white-space:nowrap;flex:none}

/* ---------- 手机 / 局域网：当前网络 + 网卡选择 ---------- */
.lan-now{background:linear-gradient(135deg,var(--pri-soft),transparent);border:1px solid var(--line2);
  border-radius:var(--r-md);padding:11px 14px;margin:10px 0 14px;overflow:hidden}
.lan-now-l{font-size:12px;color:var(--tx3);letter-spacing:.4px}
.lan-now-v{font-size:20px;font-weight:500;color:var(--tx);margin-top:3px;word-break:break-all;
  display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.lan-if{font-size:12px;font-weight:500;color:var(--tx2);background:rgba(127,127,127,.13);
  border-radius:var(--r-md);padding:2px 9px;white-space:nowrap;flex:none}
.lan-hint{font-size:12px;color:var(--tx2);margin-top:6px;line-height:1.65;word-break:break-word}
.lan-hint.lh-ok{color:#187a4b}
/* ★ 别用 `.warn` 当修饰类名：本文件 545 行有个 `.warn` 是「15×15 圆形小徽标」
   （课表时间异常提示用的），会命中同名元素把 display 变成 inline-block、
   宽度锁死 15px → 表现成整段文字被挤成一列（竖排、每行一个字）。
   这里用 .lh-warn 独立命名，避免与既有通用类撞车。 */
.lan-hint.lh-warn{color:#b3560f;background:#fdf4e8;border-radius:var(--r-md);padding:7px 11px}
.lan-pick{margin-top:14px;border-top:1px dashed var(--line2);padding-top:12px}
.lan-pick-h{font-size:13px;font-weight:500;color:var(--tx2);margin-bottom:8px}
.lan-item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;cursor:pointer;
  background:var(--card,#fff);border:1px solid var(--line);border-radius:var(--r-md);padding:9px 12px;margin-bottom:7px;
  font-family:inherit;color:var(--tx)}
.lan-item:hover{border-color:var(--pri)}
.lan-item.on{border-color:var(--pri);box-shadow:0 0 0 2px var(--pri-soft);background:var(--pri-soft)}
.lan-item b{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap}
.lan-item span{font-size:12px;color:var(--tx3);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lan-item em{font-style:normal;font-size:12px;border-radius:var(--r-md);padding:2px 8px;flex:none;white-space:nowrap}
.lan-item em.ok{background:#e6f7ee;color:#187a4b}
.lan-item em.lan-bad{background:#fdf0e2;color:#b3560f}
.lan-tip{font-size:12px;color:var(--tx3);line-height:1.75;margin-top:6px}
/* 当前 WiFi 名称 chip */
.lan-wifi{font-size:12px;font-weight:500;color:#187a4b;background:#e6f7ee;border-radius:var(--r-md);
  padding:2px 10px;white-space:nowrap;flex:none}
.lan-wifi:empty{display:none}

/* ---------- 个人中心（改密码 / 换头像 / 设验证题目） ---------- */
.pc-head{display:flex;align-items:center;gap:12px;margin-bottom:6px}
.pc-name{font-size:16px}
.pc-sec{border-top:1px dashed var(--line2);margin-top:12px;padding-top:12px}
.pc-sec b{font-size:13px}
.pc-sec .fld{margin-top:8px}
/* 密码查看弹窗 */
.pwd-box{background:var(--pri-soft);border:1px solid var(--line2);border-radius:var(--r-md);padding:10px 14px}
.pwd-row{font-size:13px;color:var(--tx2)}
.pwd-val{font-size:16px;font-weight:500;color:var(--tx);font-variant-numeric:tabular-nums;
  letter-spacing:1px;margin-left:4px}
/* 可点击的账号 chip（点头像/名字进个人中心） */
.chip.clickable{cursor:pointer}
.chip.clickable:hover{background:var(--pri-soft)}

/* ---------- 安装横幅 ---------- */
.install-bar{position:fixed;left:50%;transform:translateX(-50%);bottom:18px;z-index:160;display:flex;gap:10px;
  align-items:center;background:#22293c;color:#fff;border-radius:var(--r-md);padding:10px 14px;font-size:13px;
  box-shadow:0 12px 32px rgba(0,0,0,.28);max-width:min(680px,92vw)}
.install-bar span{flex:1}

/* ---------- 本地版新增组件 ---------- */
.pick-list{display:flex;flex-direction:column;gap:6px}
.pick-item{display:flex;align-items:center;gap:9px;border:1px solid var(--line);background:#fff;border-radius:var(--r-md);
  padding:9px 12px;text-align:left;font-size:14px}
.pick-item:hover{border-color:var(--pri);background:var(--pri-soft)}
.pick-item em{margin-left:auto;font-style:normal;font-size:12px;color:var(--tx3)}
.or-line{position:relative;text-align:center;margin:14px 0;color:var(--tx3);font-size:12px}
.or-line:before{content:"";position:absolute;top:50%;left:0;right:0;height:1px;background:var(--line)}
.or-line span{position:relative;background:var(--card);padding:0 10px}

.item{display:flex;gap:10px;align-items:flex-start;border:1px solid var(--line2);border-radius:var(--r-md);padding:10px 12px;
  background:#fff;transition:.15s}
.item:hover{border-color:var(--line);box-shadow:var(--sh)}
.item.sm{padding:8px 11px}
.item .bar{width:3px;align-self:stretch;border-radius:var(--r-md);background:var(--tx3);flex:none}
.item .ttl{font-weight:500;font-size:13px}
.item .meta{color:var(--tx3);font-size:12px;margin-top:2px}
.item .desc{color:var(--tx2);font-size:13px;margin-top:4px;white-space:pre-wrap}
.item .acts{display:flex;gap:6px;flex-wrap:wrap}
.list{display:flex;flex-direction:column;gap:8px}
.empty{color:var(--tx3);font-size:13px;padding:14px 0;text-align:center}
.bar-row{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.bar-row .bl{width:52px;font-size:12px;color:var(--tx2);flex:none}
.bar-row .bb{flex:1;height:8px;background:#eef1f7;border-radius:var(--r-md);overflow:hidden}
.bar-row .bb i{display:block;height:100%;background:var(--pri);border-radius:var(--r-md)}
.bar-row .bv{width:28px;text-align:right;font-size:12px;color:var(--tx3)}
/* 只作用于列表小色条（.bar）；矩阵格子（.mx）改由 v11 浅色系配色接管（见文件末尾 v11 区块） */
.bar.k-course{background:var(--course)!important}
.bar.k-plan{background:var(--plan)!important}
.bar.k-meet{background:var(--meet)!important}
.bar.k-leave{background:var(--leave)!important}
.bar.k-free{background:var(--free)!important}
.bar.k-task{background:var(--plan)!important}

/* 概览 */
.notice{border:1px solid var(--line2);border-radius:var(--r-md);padding:9px 12px;margin-bottom:8px;background:#fff}
.notice .nt{font-weight:500;font-size:13px;display:flex;align-items:center;gap:8px}
.notice .nt .btn{margin-left:auto}
.notice .nb{color:var(--tx2);font-size:13px;margin-top:3px;white-space:pre-wrap}
.notice .nm{color:var(--tx3);font-size:12px;margin-top:3px}
.week-strip{display:grid;grid-template-columns:repeat(7,1fr);gap:8px}
/* 竖排版：首页右侧窄列里用，一行一天，紧凑不占宽 */
.week-strip.vert{grid-template-columns:1fr;gap:6px}
.week-strip.vert .wsc{display:flex;align-items:center;gap:8px;text-align:left;padding:7px 10px}
.week-strip.vert .wsc .wd{flex:0 0 22px;font-size:13px;font-weight:500;color:var(--tx2)}
.week-strip.vert .wsc .wdd{flex:0 0 auto;margin:0;font-size:13px}
.week-strip.vert .wsc .wtag{flex:1;flex-direction:row;flex-wrap:wrap;justify-content:flex-start;align-items:center;gap:3px}
.week-strip.vert .wsc .wx-chip{margin-left:auto;flex:none}
.wsc{border:1px solid var(--line2);border-radius:var(--r-md);padding:8px 6px;text-align:center;background:#fff}
.wsc.today{border-color:var(--pri);background:var(--pri-soft)}
.wsc .wd{font-size:12px;color:var(--tx3)}
.wsc .wdd{font-size:14px;font-weight:500;margin:1px 0 4px}
.wsc .wtag{display:flex;flex-direction:column;gap:2px;align-items:center}
.tg{font-style:normal;font-size:12px;color:#fff;border-radius:var(--r-md);padding:1px 6px;line-height:1.5}
.tg.c-course{background:var(--course)}
.tg.c-meet{background:var(--meet)}
.tg.c-plan{background:var(--plan)}

/* 全员矩阵 */
.mx-table{width:100%;border-collapse:separate;border-spacing:3px;min-width:720px}
.mx-table th{font-size:12px;color:var(--tx2);font-weight:500;text-align:center;white-space:nowrap}
.mx-table th:first-child{text-align:left;min-width:110px}
.mx-table tbody th{display:flex;align-items:center;gap:6px;text-align:left;font-weight:500;color:var(--tx)}
.mx-table td.mx{height:30px;border-radius:var(--r-md);text-align:center;font-size:12px;color:#fff;cursor:pointer;transition:.12s}
.mx-table td.mx:hover{opacity:.82}
.mx-table td.k-free{background:#eaf6ef;color:#187a4b}
.mx-table td.k-course{background:var(--course)}
.mx-table td.k-meet{background:var(--meet)}
.mx-table td.k-leave{background:var(--leave)}
.mx-table td.k-plan{background:var(--plan)}
.rec{margin-top:14px;border-top:1px solid var(--line2);padding-top:12px}
.rec h4{margin:0 0 8px;font-size:13px}
.rec-item{display:flex;align-items:center;gap:10px;padding:7px 11px;border:1px solid var(--line2);border-radius:var(--r-md);margin-bottom:6px}
.rec-item span{flex:1;font-size:13px}
.date-in{border:1px solid var(--line);border-radius:var(--r-md);padding:5px 9px;font-size:13px;background:#fbfcfe}

/* 看板 */
.kboard{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.kcol{background:#f7f9fd;border:1px solid var(--line);border-radius:var(--r-md);padding:10px;min-height:120px}
.kcol-h{font-size:13px;font-weight:500;margin-bottom:8px;display:flex;align-items:center;gap:6px}
.kcol-h em{font-style:normal;background:#e8ecf5;color:var(--tx2);border-radius:var(--r-md);padding:0 7px;font-size:12px}
.kcol-b{display:flex;flex-direction:column;gap:8px}
.kcard{background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:10px 11px;box-shadow:var(--sh)}
.kcard.done{opacity:.6}
.khead{display:flex;gap:7px;align-items:flex-start}
.khead .ttl{font-weight:500;font-size:13px;flex:1;line-height:1.4}
.ptag{color:#fff;font-size:12px;border-radius:var(--r-md);padding:1px 6px;flex:none;line-height:1.6}
.kcard .desc{font-size:13px;color:var(--tx2);margin-top:5px;white-space:pre-wrap}
.kcard .meta{font-size:12px;color:var(--tx3);margin-top:6px}
.kcard .acts{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}

/* 成员 */
/* 通用头像：圆形，默认 28px；有上传图时图片裁满。上下文可覆盖尺寸
   （.chip .av=19px、.av.sm=22px、.dtl-mem .av=30px、.av.lg=56px） */
.av{width:28px;height:28px;border-radius:50%;color:#fff;display:inline-flex;align-items:center;
  justify-content:center;font-size:12px;flex:none;overflow:hidden;vertical-align:middle;font-style:normal}
.av.has-img{padding:0}
.av.has-img img{width:100%;height:100%;object-fit:cover;display:block;border-radius:50%}
.av.lg{width:56px;height:56px;font-size:22px}
.mlist{display:flex;flex-direction:column;gap:8px}
.mrow{display:flex;align-items:center;gap:11px;border:1px solid var(--line2);border-radius:var(--r-md);padding:10px 12px;background:#fff}
.mrow .minfo{flex:1}
.mrow .minfo .ttl{font-weight:500;font-size:13px}
.mrow .minfo .meta{font-size:12px;color:var(--tx3);margin-top:2px}
.mrow .acts{display:flex;gap:6px}
/* v27：人多/按钮多时按钮不再挤成竖排——整行向右扩展、横向滑动 */
.mrow .acts{flex-wrap:nowrap;overflow-x:auto;max-width:100%;padding-bottom:2px;-webkit-overflow-scrolling:touch}
.mrow .acts .btn,.mrow .acts select{flex:none;white-space:nowrap}
/* v27：成员管理搜索框 */
.msearch{min-width:150px;max-width:200px}
/* v27：成员分页条 */
.pager{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:12px;flex-wrap:wrap}
.pager .pg-btn{min-width:32px;height:32px;border-radius:var(--r-md);border:1px solid var(--line2);background:var(--card);
  color:var(--tx);font-size:13px;cursor:pointer;padding:0 8px}
.pager .pg-btn.on{background:var(--pri);border-color:var(--pri);color:#fff;font-weight:500}
.pager .pg-btn:disabled{opacity:.4;cursor:default}
.pager em{font-size:12px;color:var(--tx3);margin-left:4px;font-style:normal}
/* v27：空闲矩阵宽容度胶囊 */
.tol-row{display:flex;gap:6px;flex-wrap:wrap;margin:2px 0 10px}
.tol-chip{border:1px solid var(--line2);background:var(--card);color:var(--tx2);border-radius:var(--r-md);
  padding:4px 12px;font-size:12px;cursor:pointer;white-space:nowrap}
.tol-chip.on{background:var(--pri);border-color:var(--pri);color:#fff;font-weight:500}
.av.sm,.chip .av{width:22px;height:22px;border-radius:50%;color:#fff;display:grid;place-items:center;font-size:12px;flex:none}

/* 时间轴颜色 */
.tl-item.k-course{background:#e8effe;border-left:3px solid var(--course)}
.tl-item.k-meet{background:#f1ebfe;border-left:3px solid var(--meet)}
.tl-item.k-leave{background:#fdeceb;border-left:3px solid var(--leave)}
.tl-item.k-plan{background:#fdf1e4;border-left:3px solid var(--plan)}
.tl-item.k-free{background:#e8f7ef;border-left:3px solid var(--free)}
.tl-item .t{font-size:13px;font-weight:500;color:var(--tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tl-item .s{font-size:12px;color:var(--tx3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tl-item .del{position:absolute;top:2px;right:4px;font-size:12px;color:var(--tx3);cursor:pointer}
.tl-slot{border-bottom:1px dashed var(--line2)}
.tl-slot:hover{background:#f4f7ff}
.chk{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--line);border-radius:var(--r-md);padding:3px 10px;
  font-size:13px;background:#fff;color:var(--tx2)}
.chk input{margin:0}
/* 任务弹窗「排入日程」小标题 */
.tk-time-hd{font-size:13px;font-weight:500;color:var(--tx2);margin-top:2px}
.tk-time-hd .hint{font-weight:400;color:var(--tx3);font-size:12px}
/* v69：选人筛选条 —— 社团 / 部门下拉 + 智能推荐按钮，一行排开、窄屏自动换行，
   按钮不会被挤成半个（手机上「点不到」的老毛病）。 */
.tk-pick{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:6px 0 4px}
.tk-pick select{flex:1 1 130px;min-width:120px;max-width:190px;height:30px;border:1px solid var(--line);
  border-radius:var(--r-md);padding:0 8px;font-size:13px;background:#fff;color:var(--tx2)}
.tk-pick .btn{flex:0 0 auto;height:30px}

@media(max-width:900px){
  .kboard{grid-template-columns:1fr}
  .stat-grid{grid-template-columns:repeat(3,1fr)}
  .week-strip{grid-template-columns:repeat(4,1fr)}
  .mx-table{min-width:640px}
  .mrow{flex-wrap:wrap}
  /* v68：窄屏成员行按钮区 —— 以前沿用桌面端的「横向滑动条」，手机上按钮被切掉一半、
     要左右滑才能点到（用户原话「这个页面太大了点不到」）。
     改成整行换行的等宽网格：按钮完整可见、单手就能点到，不再依赖横向滑动。 */
  .mrow .acts{
    flex-wrap:wrap;overflow:visible;gap:6px;width:100%;display:grid;
    grid-template-columns:repeat(auto-fill,minmax(92px,1fr))
  }
  .mrow .acts .btn,.mrow .acts select{width:100%;justify-content:center;text-align:center}
}

/* ============ 新增：分钟级时间轴 / 标签 / 天气 / 部门 ============ */
.tl-labels{width:84px}
.tl-label span{font-size:12px;letter-spacing:-.2px}
.tl-gap{height:8px;font-size:12px;color:var(--tx3);text-align:right;padding-right:4px;
  display:flex;align-items:center;justify-content:flex-end;overflow:hidden;white-space:nowrap}
.tl-gapline{position:absolute;left:0;right:0;background:repeating-linear-gradient(
  -45deg,#f7f9fd,#f7f9fd 4px,#f1f4fa 4px,#f1f4fa 8px);border-top:1px dashed var(--line2);
  border-bottom:1px dashed var(--line2)}
.tl-track{height:auto}

/* ---------- 共同空闲条形图 ---------- */
.fbar{margin:2px 0 12px}
.fbar-track{position:relative;height:26px;border-radius:var(--r-md);background:#eef1f7;border:1px solid var(--line2);
  overflow:hidden}
.fbar-busy{position:absolute;top:0;bottom:0;background:repeating-linear-gradient(-45deg,#e3e8f2,#e3e8f2 5px,#dde3ef 5px,#dde3ef 10px);
  border-left:1px solid #d3dae8;border-right:1px solid #d3dae8}
.fbar-free{position:absolute;top:0;bottom:0;background:linear-gradient(180deg,#7fd3a6,#3fae76);
  border-radius:var(--r-md);transition:.2s;min-width:3px;box-shadow:0 1px 3px rgba(63,174,118,.3)}
.fbar-free:hover{filter:brightness(1.06)}
.fbar-free i{position:absolute;left:6px;top:0;bottom:0;display:flex;align-items:center;color:#fff;
  font-style:normal;font-size:12px;font-weight:500;white-space:nowrap;text-shadow:0 1px 1px rgba(0,0,0,.14)}
.fbar-axis{position:relative;height:16px;margin-top:2px}
.fbar-tick{position:absolute;transform:translateX(-50%);font-size:12px;color:var(--tx3);white-space:nowrap}
.fbar-tick:before{content:'';position:absolute;left:50%;top:-4px;width:1px;height:3px;background:var(--line)}
.rec-sum{margin-left:auto;font-style:normal;font-weight:400;font-size:12px;color:var(--tx3)}
.rec h4{display:flex;align-items:center;gap:8px}

.rec-item .rec-time{flex:1;font-size:13px}
.rec-item .rec-time b{font-weight:500}
.rec-dur{font-style:normal;font-size:12px;color:var(--tx3);background:#f1f4fa;border-radius:var(--r-md);
  padding:1px 8px;flex:none}

/* 事件标签 */
.tchip{display:inline-block;font-style:normal;font-size:12px;line-height:1.5;color:#fff;
  border-radius:var(--r-md);padding:0 5px;margin-left:4px;vertical-align:1px;font-weight:500}
.pick.tags button{border-radius:var(--r-md);padding:4px 11px;font-size:13px}
.pick.tags button.on{background:var(--pri-soft);border-color:#b9caf9;color:var(--pri)}

/* 矩阵部分占用 */
.mx-table td.mx{position:relative}
.mx-table td.mx.part::after{content:'';position:absolute;left:0;right:0;bottom:2px;height:2px;
  background:rgba(255,255,255,.75);border-radius:var(--r-md)}
.mx-table td.mx i{position:absolute;top:0;right:3px;font-style:normal;font-size:12px;opacity:.9}
.mx-table tbody th em{display:block;font-style:normal;font-size:12px;color:var(--tx3);font-weight:400}
table td.empty{padding:16px;text-align:center;color:var(--tx3)}

/* 课表时间异常提示（15×15 圆形小徽标，配合 <i class="warn"> 使用）
   🔴 这个 .warn 是「全局裸类名」，任何元素写上 class="warn" 都会被压成 15×15 小圆。
   780 行已经记过一次教训（那边改用 .lh-warn 避开）。**永远不要把 .warn 当按钮/文字修饰类**：
   它会让按钮塌缩到 22.5×15、文字整段不可见（v122d 实测 #trashExport 就是这样坏掉的）。
   要表达"危险/警告按钮"请用 .btn.danger 或 .btn.warnbtn 这类带前缀的类名。 */
.warn{display:inline-block;width:15px;height:15px;line-height:15px;text-align:center;border-radius:50%;
  background:#fdeceb;color:#c93c37;font-style:normal;font-size:12px;font-weight:500}
/* v122d：危险/警告语义的按钮（曾经误用 .warn 导致按钮塌缩，这里给它一个安全的名字） */
.btn.warnbtn{border-color:#f0b9b5;color:#c93c37}
.btn.warnbtn:hover{border-color:#e08b86;background:#fdf5f4}
[data-theme="dark"] .btn.warnbtn{border-color:#6b3a38;color:#ff9b97}
[data-theme="dark"] .btn.warnbtn:hover{border-color:#8a4a47;background:#2a1818}
/* v79.1：文字状态色 —— 与 .warn 徽标分离，避免把状态文字（如「需用 https 地址打开」）
   压进 15×15 小圆里导致不可见。新增专用类承载颜色，旧 .ok 无全局规则（仅加粗）。 */
.txt-warn{color:#c93c37;font-weight:500}
.txt-ok{color:#1f9d57;font-weight:500}
[data-theme="dark"] .txt-warn{color:#ff8a82}
[data-theme="dark"] .txt-ok{color:#5fd38a}
.readonly{display:block;border:1px dashed var(--line);border-radius:var(--r-md);padding:6px 10px;
  font-size:13px;color:var(--tx2);background:#fafbfe;min-height:33px}

/* 天气 */
.wx-search{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.wx-search select{min-width:140px}
.wx-chip{display:inline-flex;align-items:center;gap:3px;font-size:12px;color:var(--tx2);
  background:#f2f6ff;border-radius:var(--r-md);padding:1px 8px;margin-left:6px}
.wx-chip b{font-weight:500;font-size:12px}
.wx-chip.sm{font-size:12px;padding:0 6px;background:transparent;margin-left:2px}
.wx-week{display:grid;grid-template-columns:repeat(7,1fr);gap:8px}
.wx-day{background:#f7f9fd;border:1px solid var(--line2);border-radius:var(--r-md);padding:8px 4px;text-align:center}
.wx-day .wxd{font-size:12px;color:var(--tx3)}
.wx-day .wxi{font-size:20px;line-height:1.6}
.wx-day .wxt{font-size:13px;font-weight:500}
.wx-day .wxn{font-size:12px;color:var(--tx3);min-height:15px}
.wx-day .wxp{font-size:12px;color:var(--pri)}
.wx-week.big .wx-day{padding:12px 6px}
.wx-week.big .wxi{font-size:26px}
.wx-hero{display:flex;align-items:center;gap:18px;padding:14px 16px;border-radius:var(--r-md);
  background:linear-gradient(120deg,#eef4ff,#f7f9fd);margin-bottom:14px}
.wx-hero .ico{font-size:46px}
.wx-hero .tmp{font-size:26px;font-weight:500}
.wx-hero .desc{color:var(--tx2);font-size:13px}
.wx-hero .sub{color:var(--tx3);font-size:12px;margin-top:2px}
.wsc .wx-chip.sm{display:block;margin:2px 0 0}
.month .cell .wx-chip.sm{display:none}

/* ===== v58：天气数据源（多服务器）列表 ===== */
.wx-src-list{display:flex;flex-direction:column;gap:8px}
.wx-src{display:flex;align-items:center;gap:10px;padding:9px 11px;border:1px solid var(--line2);
  border-radius:var(--r-md);background:var(--card,#fff)}
.wx-src.on{border-color:var(--pri);background:var(--pri-soft,#f2f6ff)}
.wx-src.off{opacity:.62}
.wx-src-on{display:inline-flex;align-items:center;gap:6px;flex:none;cursor:pointer}
.wx-src-on input{width:16px;height:16px;flex:none;cursor:pointer}
.wx-src-on b{display:inline-grid;place-items:center;width:19px;height:19px;border-radius:50%;
  background:var(--soft,#eef1f7);color:var(--tx2);font-size:12px}
.wx-src-main{flex:1;min-width:0}
.wx-src-nm{font-size:13px;font-weight:500;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.wx-src-nm .wx-src-en{font-style:normal;font-size:12px;font-weight:400;color:var(--tx3)}
.wx-src-nm .wx-src-tag{font-style:normal;font-size:12px;color:#b45309;background:rgba(240,178,50,.16);
  border-radius:var(--r-md);padding:1px 6px}
.wx-src-desc{font-size:12px;color:var(--tx3);line-height:1.45;margin-top:2px}
.wx-src-err{font-size:12px;color:#c0453f;margin-top:3px;word-break:break-all}
.wx-src-badge{flex:none;font-size:12px;border-radius:var(--r-md);padding:3px 8px;white-space:nowrap;
  background:var(--soft,#eef1f7);color:var(--tx2)}
.wx-src-badge.ok{background:rgba(34,160,107,.13);color:#1d7a51}
.wx-src-badge.bad{background:rgba(228,84,79,.12);color:#c0453f;cursor:help}
.wx-src-badge.idle{background:var(--soft,#eef1f7);color:var(--tx3)}
.wx-src-acts{display:flex;align-items:center;gap:4px;flex:none}
@media(max-width:760px){
  .wx-src{flex-wrap:wrap;gap:7px}
  .wx-src-main{flex:1 1 100%;order:3}
  .wx-src-badge{order:2}
  .wx-src-acts{order:4;margin-left:auto}
}

/* 部门 */
.dgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px}
.dcard{border:1px solid var(--line);border-radius:var(--r-md);padding:12px;background:#fbfcff}
.dhead{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}
.dhead h4{font-size:14px;flex:1}
.dhead .meta{font-size:12px;color:var(--tx3)}
.dmem{display:flex;flex-wrap:wrap;gap:6px}
.mchip{display:inline-flex;align-items:center;gap:4px;background:#fff;border:1px solid var(--line);
  border-radius:var(--r-md);padding:2px 9px 2px 2px;font-size:12px}
.mchip i{font-style:normal;font-size:12px;color:var(--tx3)}
.dcard .acts{display:flex;gap:6px}
.dept-sel,.role-sel{font-size:12px;padding:4px 6px;border:1px solid var(--line);border-radius:var(--r-md);background:#fff}
.item .dept{font-style:normal;font-size:12px;color:var(--tx3);margin-left:6px}
.dlg .ttl{font-size:14px;font-weight:500;margin-bottom:4px}
.dlg .meta{color:var(--tx2);font-size:13px}
.dlg .desc{margin-top:6px;font-size:13px}
.dlg .hint{margin-top:10px}

.p-row{align-items:flex-end}
.p-row .fld:first-child{min-width:110px}
.p-row .fld:nth-child(2){min-width:110px}
.p-del{height:36px}

@media(max-width:900px){
  .wx-week{grid-template-columns:repeat(4,1fr)}
  .tl-labels{width:66px}
  .tl-label span{font-size:12px}
}

/* ============ 手机端适配 ============ */
.tabbar{position:fixed;left:0;right:0;bottom:0;z-index:120;display:none;
  background:rgba(255,255,255,.97);backdrop-filter:blur(12px);border-top:1px solid var(--line);
  padding:6px 4px calc(6px + env(safe-area-inset-bottom,0px) + var(--android-nav-h,0px));box-shadow:0 -2px 14px rgba(20,30,60,.06);
  /* ★ v127：底部白条补圆角（用户报「底下白的是圆角的但表面还是方形」）。
     顶边两角圆化 + overflow:hidden 把内部方形子元素裁进圆角内，消除层级错位。 */
  border-radius:var(--r-lg) var(--r-lg) 0 0;overflow:hidden}
.tabbtn{flex:1;border:0;background:transparent;display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:5px 2px;border-radius:var(--r-md);color:var(--tx3);position:relative;transition:.15s;-webkit-tap-highlight-color:transparent}
.tabbtn .ti{font-size:20px;line-height:1.15;font-style:normal}
.tabbtn em{font-style:normal;font-size:12px;letter-spacing:.2px}
/* ★ v102-B：底部标签栏「周课表」入口图标（emoji 🗓），line-height:1 让图标与下方文字对齐 */
.tabbtn .ti-weektl{font-size:20px;line-height:1}
.tabbtn.active{color:var(--pri);background:var(--pri-soft)}
.tabbtn.active em{font-weight:500}
.tabbtn .badge{top:2px;right:calc(50% - 20px)}

.sheet-wrap{position:fixed;inset:0;z-index:140;background:rgba(20,26,40,.35);display:flex;align-items:flex-end}
.sheet{width:100%;background:var(--card);border-radius:var(--r-lg) var(--r-lg) 0 0;padding:16px 16px calc(20px + env(safe-area-inset-bottom,0px) + var(--android-nav-h,0px));
  animation:sheetUp .22s ease-out;max-height:78vh;overflow:auto}
@keyframes sheetUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
.sheet-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.sheet-head b{font-size:14px}
.sheet-head button{border:0;background:#f1f4fa;width:30px;height:30px;border-radius:50%;font-size:14px;color:var(--tx2)}
.sheet-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.sheet-item{border:1px solid var(--line);background:#fbfcff;border-radius:var(--r-md);padding:14px 6px;
  display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--tx);font-size:13px}
.sheet-item .si{font-size:22px;font-style:normal;line-height:1}
.sheet-item.active{border-color:#b9caf9;background:var(--pri-soft);color:var(--pri);font-weight:500}
.sheet-item .nbadge{position:absolute;top:8px;right:calc(50% - 24px);min-width:16px;height:16px;padding:0 4px;
  border-radius:var(--r-md);background:#e4544f;color:#fff;font-size:12px;line-height:16px;font-style:normal}
.sheet-item{position:relative}

@media(max-width:760px){
  body{padding-bottom:0}
  /* 🔴 v80b38 修：底部留白原为写死 74px，而实测 `.tabbar` 高 **65.59px**。
     ⚠⚠ 为什么 74px 还是不够（用户实测报「番茄钟点开始后一直翻页、始终不显示」的真凶）：
       番茄钟「开始专注」按钮落在 **y=666..719**，而 `.tabbar` 顶部在 **y=658**
       ⇒ 74 − 65.59 = 8.4px 余量，**按钮上半部分仍在标签栏下面**
       ⇒ `elementFromPoint` 落在 `BUTTON.tabbtn < NAV#tabbar`（实测 isBtn=false）
       ⇒ 点「开始」实际是点底部标签 ⇒ 切页面（用户看到的「翻页」）⇒ 番茄钟从未启动。
     ⚠ 为什么必须用变量而不是写死 px：
       `.tabbar` 高度含 `env(safe-area-inset-bottom)`（iPhone 底部安全区），
       **各机型/横竖屏都不同**；`--qz-tabbar-h` 由 app.js 的 `syncTabbarH()`
       用 ResizeObserver 实测写入（与 `--qz-topbar-h` 同一套机制）。
     ⚠⚠ 教训（本项目已栽 3 次，务必记住）：
       改 `#main` 的 padding **必须同时改这条 @media 里的**，
       否则窄屏（≤760px）会被它整条覆盖 —— 我这次就是这样第一版没修好。
       **改完一定要实测 `getComputedStyle(#main).paddingBottom`**，不要看自己写的那行还在不在。*/
  #main{padding:10px 10px calc(var(--qz-tabbar-h, 66px) + 20px)}
  .topbar{padding:8px 10px;gap:8px;flex-wrap:nowrap}
  /* 品牌区：logo 稍放大到 34px（28px 时里面两个人脸看不清），
     且品牌名必须**单行不换行** —— 手机上搜索框+主题+头像+切换身份已把
     空间吃光，名字若允许换行会被折成两行、把顶栏撑到 43px 高。 */
  .topbar .brand{font-size:14px;flex:none;min-width:0;gap:7px}
  .topbar .brand .logo{width:34px;height:34px;border-radius:var(--r-md);font-size:14px}
  #teamBrand{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:84px}
  #nav{display:none}
  .tabbar{display:flex}
  .me{gap:6px}
  .chip{padding:3px 9px;font-size:12px}
  .chip .role{display:none}
  /* 手机上空间小，但「当前是谁」必须看得见：
     藏掉小标签，保留头像 + 名字（名字是账号识别的核心） */
  .chip .me-lbl{display:none}
  .chip .me-name{max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* 「切换身份」4 个字在窄屏会被折成两行（把顶栏撑高）；锁单行，
     并允许搜索框收缩让位 —— 搜索框可压缩，文字块不可。 */
  #btnSwitch{padding:5px 9px;font-size:12px;white-space:nowrap;flex:none}
  /* ★ v99：手机端右侧按钮很多、空间紧张时，让 .me 横向滚动而不是裁切按钮文字
     （「📚换课表」字露出来就是被挤爆了）。按钮已 lock 为不压缩不换行，
     空间不够就整条横向滚，保证任何按钮都完整可见。 */
  .me{min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .me::-webkit-scrollbar{display:none}
  #globalSearch{min-width:0;flex:1 1 60px}

  /* ★ v127：工具栏（日历翻页 ‹ › 今天 / 周视图 / 团队矩阵 …）按钮很多，
     手机上严禁被 flex 压缩（否则 ‹ › 今天 会被挤到文字溢出、超出图标），
     空间不够时整条横向滚动 —— 与顶栏 .me 同一套机制（v99）。 */
  .panel-head{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .panel-head::-webkit-scrollbar{display:none}
  .panel-head h2,.panel-head h3{flex:0 0 auto;white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .panel-head .btn{flex:0 0 auto;white-space:nowrap}

  /* 表单控件触摸友好；v62 按钮紧凑化：原 9px/38px 撑太大，收紧 */
  .btn{padding:6px 12px;min-height:34px}
  .btn.tiny{padding:3px 9px;min-height:26px;font-size:12px}
  /* ===== v62：按钮紧凑化（集中覆盖，便于回滚） ===== */
  .comm-tabs button.ctab{padding:4px 10px;font-size:13px}
  .pick button{padding:4px 8px;font-size:12px}
  .pane input,.pane textarea,.fld input,.fld select,.fld textarea,.date-in{padding:11px 12px;font-size:16px}
  .navbtn{font-size:14px}

  /* 面板 / 栅格改单列 */
  .grid2{grid-template-columns:1fr;gap:12px}
  /* ★ v121：考勤页同理必须单列，否则手机上三个面板并排会挤成一条（见 .grid-att 的注释）*/
  .grid-att{grid-template-columns:1fr;gap:12px}
  .cal-layout{grid-template-columns:1fr}
  .stat-grid{grid-template-columns:repeat(2,1fr);gap:8px}
  .stat-card{padding:10px 12px}
  .stat-card .v{font-size:16px}
  .dgrid{grid-template-columns:1fr}
  .kboard{grid-template-columns:1fr}
  .panel-head{padding:12px 12px;flex-wrap:wrap}
  .panel-body{padding:12px}
  .panel-head h2,.panel-head h3{font-size:14.5px}

  /* 天气 */
  .wx-hero{flex-direction:column;gap:8px;text-align:center;padding:14px 12px}
  .wx-hero .ico{font-size:40px}
  .wx-hero .tmp{font-size:23px}
  .wx-week,.wx-week.big{grid-template-columns:repeat(3,1fr);gap:6px}
  .wx-week.big .wx-day{padding:9px 4px}
  .wx-day .wxi{font-size:16px}
  .wx-day .wxw{font-size:12px;color:var(--tx3)}
  .wx-head{gap:6px}
  .wx-head .date-in{flex:1 1 120px;min-width:0}
  .wx-search{flex-direction:column;align-items:stretch}
  .wx-search select{min-width:0}

  /* 时间轴缩小 */
  .tl-labels{width:56px}
  .tl-label span{font-size:9px}
  .tl-item{padding:3px 6px;font-size:12px}

  /* 弹窗接近全屏、可滚动 */
  .modal-mask{padding:10px}
  /* v47 #4：移动端同样用「头/脚固定」布局（.modal 自带 overflow:hidden），
     这里只收敛 max-height，不再强制 overflow:auto（否则确认按钮又会滚走）。.dlg 保留自带滚动。 */
  .dlg{max-width:100% !important;width:100%;max-height:calc(100vh - 20px);overflow:auto}
  /* v48：★ 弹窗给底部标签栏留高度 —— tabbar 是 fixed 且 z-index(120) 高于弹窗遮罩(100)，
     弹窗若伸到屏幕底，footer 的确认/关闭按钮会被 tabbar 盖住点不到（用户截图）。
     收敛到「标签栏上沿」以上，footer 始终可见可点；标签栏本身也可正常点。 */
  .modal{max-width:100% !important;width:100%;
    max-height:calc(100vh - 20px - 62px - env(safe-area-inset-bottom,0px))}
  .dlg .ttl{font-size:14px}
  .fld-row{flex-direction:column}
  .fld-row .fld{min-width:0 !important}
  .p-row{flex-direction:column;align-items:stretch}

  /* 表格：横向滚动 */
  .table-wrap{overflow-x:auto}
  .dtable{min-width:560px}

  /* 共同空闲条形图 */
  .fbar-track{height:22px}
  .fbar-free i{font-size:12px;left:4px}
  .fbar-tick{font-size:12px}
  .rec-item{flex-wrap:wrap;gap:6px}
  .rec-item .rec-time{font-size:13px;flex:1 1 100%}
}

@media(max-width:420px){
  .stat-grid{grid-template-columns:repeat(2,1fr)}
  .wx-week,.wx-week.big{grid-template-columns:repeat(2,1fr)}
  .sheet-grid{grid-template-columns:repeat(3,1fr);gap:8px}
  .sheet-item{padding:12px 4px;font-size:12px}
}

/* ============ 新增功能样式 ============ */

/* 顶栏搜索框 */
.search-in{border:1px solid var(--line);border-radius:var(--r-md);padding:6px 14px;font-size:13px;
  background:var(--bg);color:var(--tx);outline:none;width:160px;transition:.2s}
.search-in:focus{width:240px;border-color:var(--pri);background:var(--card)}
.search-in::placeholder{color:var(--tx3)}
#themeBtn{font-size:16px;padding:5px 10px;min-width:36px}
/* ⚠ v128 复核：探针曾报「🌙 scrollWidth 47 > 42」，实为 p1-fixes-v86.css 给 .btn.tiny
   加的 52px 触摸热区 ::after（min-width:52px）撑出的假溢出 —— emoji 本体并未被裁。
   且本文件任何 min-width 都会被 p1-fixes-v86.css 的 min-width:0 !important 压掉，
   改这里只会把按钮越改越小 ⇒ 保持原样不动，探针已改为量「文字真实矩形」。 */

/* "现在"指示线 */
.tl-now{position:absolute;left:0;right:0;height:2px;background:var(--now-color);z-index:10;
  pointer-events:none;box-shadow:0 0 6px rgba(228,84,79,.4)}
.tl-now:before{content:'';position:absolute;left:-5px;top:-4px;width:10px;height:10px;border-radius:50%;
  background:var(--now-color);box-shadow:0 0 6px rgba(228,84,79,.5)}
.tl-now span{position:absolute;right:4px;top:-15px;font-size:12px;color:var(--now-color);font-weight:500;
  background:var(--card);padding:0 4px;border-radius:var(--r-md);white-space:nowrap}

/* 周视图 */
.wv-scroll{display:flex;gap:0;overflow-x:auto;min-height:200px}
.wv-scroll>.tl-labels{flex:none;width:52px}
.wv-cols{display:flex;flex:1;min-width:0;gap:1px}
.wv-col{flex:1;min-width:80px;display:flex;flex-direction:column}
.wv-head{display:flex;flex-direction:column;align-items:center;padding:8px 2px 8px;border-bottom:1px solid var(--line);
  font-size:12px;gap:2px;position:sticky;top:0;background:var(--card);
  /* 修复：表头层级必须高于时间轴列(.tl-labels z-index:4)与"现在"线(.tl-now z-index:10)，
     否则周一→周日表头会被时间轴列/现在线盖住、看不全。20 同时高于课程块(auto)。 */
  z-index:20;
  /* ★ v100 修「星期一二三四五六日 被裁掉上半截」（用户原话「他会挡住上面的星期一到星期日的文字」）。
     ────────────────────────────────────────────────────────────────────────
     根因不是层叠（commit 06342f1 已把 z-index 3→20 修过层叠，那次是对的但不完整），
     而是这两个属性：
       height:96px        —— 写死的固定高度
       overflow:hidden    —— 装不下就裁掉
     表头里会竖着堆 5 类内容：wv-dow(星期) / wv-date(几号) / wv-mon(几月) /
     wv-lunar(农历) / cdwDayIcons(倒数日角标) / wxChip(天气 chip)。
     开了农历+倒数日+天气时实测总高 118px > 96px，overflow:hidden 把**最下面**
     那一行裁掉 —— 而 flex-direction:column + justify-content:flex-start
     决定了被裁的正是最后一行，也就是视觉上「压在最下面」的星期字。
     ⇒ 症状是「星期几只剩半截」，而凶手是 overflow，不是 z-index。

     ⚠⚠ 硬约束（不能为了不裁切就改成"各自自适应高度"）：
        **7 天的轨道起点必须一致**，否则装了天气 chip 的周一~周五(高)
        与没装的周六周日(矮)轨道起点错位 → 课块与时间标签列对不齐，
        而且在周五→周六之间会出现一条断缝。这正是 height:96px 当初被引入的原因
        （见下方 v18 注释：「有/无天气 chip 的列轨道起点必须一致」）。
     ⇒ 所以正确解法**不是**放开成 min-height 让各列自己长，而是：
        ① 高度改 min-height（内容装得下就不裁）；
        ② 去掉 overflow:hidden（万一真超高也不裁字，宁可短暂溢出）；
        ③ 由 JS 实测 7 个表头的**最大**高度，写进 --wv-head-h，
           再用 height:var(--wv-head-h) 把 7 列**强制对齐到同一高度**。
        这样既不裁字，又保证轨道起点一致 —— 两个目标同时满足。
        ⚠ var 的兜底值取 96px（=旧固定值）：首帧 JS 还没跑时用它，
          此时若内容超高会短暂溢出 1 帧，下一帧 rAF 就校正了。 */
  min-height:96px;
  height:var(--wv-head-h,96px);
  justify-content:flex-start;box-sizing:border-box;overflow:visible}
.wv-head.weekend .wv-dow{color:var(--leave)}
.wv-head .wv-dow{font-weight:500;font-size:12px;color:var(--tx2);line-height:1.3}
.wv-head .wv-date{font-size:16px;font-weight:500;line-height:1.3;min-width:26px;height:26px;
  display:grid;place-items:center;border-radius:50%;font-variant-numeric:tabular-nums;flex:none}
.wv-head .wv-date.today{background:var(--pri);color:#fff;box-shadow:0 2px 6px rgba(59,110,245,.35)}
.wv-head .wv-mon{font-size:12px;color:var(--tx3);line-height:1.25}
.wv-head.today{background:linear-gradient(180deg,var(--pri-soft),transparent)}
.wv-track{position:relative;flex:none;border-left:1px solid var(--line2)}
.wv-col.weekend .wv-track:before{background:linear-gradient(180deg,#f5f6fa,#f2f4f8)}
.wv-col.weekend .wv-head{background:#f7f8fb}
.wv-col .tl-item{font-size:12px;padding:4px 5px;border-radius:var(--r-md);box-shadow:0 1px 3px rgba(20,30,60,.08);overflow:visible}
.wv-col .tl-item .t{font-size:12px;line-height:1.25;font-weight:500}
.wv-col .tl-item .s{font-size:12px}
.wv-col .tl-item .loc{font-size:9px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.95}
.wk-chip{font-weight:500;color:var(--pri);background:var(--pri-soft);flex:none}
/* ★ v102-B：独立「周课表」整页视图 —— 复用时间轴内核，但整页长滚动（不套内部小滚动盒）。
   ⚠ 桌面端 .wv-scroll 默认 overflow-x:auto + min-height:200px 会变成内部小滚动框，
     与「整页长滚动」诉求相悖 ⇒ 强制 overflow:visible、清掉 min-height，让它展开成普通块，
     由页面（window）滚动。手机端（≤760px）本就 overflow:visible，无需再改。 */
.weektl-view .wv-scroll{overflow:visible;max-height:none;min-height:0}

/* ================= v18：周视图表头不再挤 ================= */
/* 用户反馈周视图表头挤：手机 412px 宽、时间轴占 52px，每列只剩 ~50px，
   而表头竖排了 4 行（星期/日期/月份/天气），天气 chip「🌤24~34°」宽约 48px
   → 溢出/重叠。三处修：
     ① 表头 gap 1→2px、高度 90→96px（呼吸感）
     ② 天气 chip 在周表头里"竖排化"：图标在上、温度在下，并允许换行/缩字号，
        这样 40px 宽也放得下，不再横向撑爆
     ③ 温度写成紧凑形式（去掉空格），并给 chip 限宽 + 居中 */
.wv-head .wx-chip.sm{display:flex;flex-direction:column;align-items:center;gap:0;
  margin:3px 0 0;padding:0;background:transparent;font-size:12px;line-height:1.15;
  max-width:100%;overflow:hidden;white-space:nowrap}
.wv-head .wx-chip.sm b{font-size:9px;font-weight:500;letter-spacing:-.3px}
/* 极窄列（≤380px 手机）：天气只留图标，温度省略，避免任何重叠 */
@media (max-width:380px){
  .wv-head .wx-chip.sm b{display:none}
  .wv-head{height:92px}
}

/* 公告管理弹窗 */
.nt-mg-bar{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
.nt-mg-list{max-height:56vh;overflow:auto;border:1px solid var(--line2);border-radius:var(--r-md)}
.nt-mg-row{display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line2);cursor:pointer;font-size:13px}
.nt-mg-row:last-child{border-bottom:0}
.nt-mg-row:hover{background:var(--pri-soft)}
.nt-mg-row input[type=checkbox]{flex:none;width:15px;height:15px}
.nt-mg-row .nt-mg-t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nt-mg-row .nc-time{color:var(--tx3);font-size:12px;flex:none}

/* 音乐直达小卡 */
.music-card{display:flex;flex-direction:column;align-items:center;text-align:center;padding:22px 16px;
  border:1px solid var(--line2);border-radius:var(--r-md);background:linear-gradient(160deg,#fdf3f3,#f6f8ff)}
.music-card .mc-logo{font-size:44px;line-height:1}
.music-card .mc-name{font-size:16px;font-weight:500;margin-top:8px}
.music-card .mc-desc{color:var(--tx2);font-size:13px;margin-top:6px;line-height:1.7}
.music-card .mc-btns{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:14px}
[data-theme="dark"] .music-card{background:linear-gradient(160deg,#221a1a,#1a2030)}

/* 事件块通用美化（日视图 + 周视图共用） */
.tl-item{box-shadow:0 1px 3px rgba(20,30,60,.08);overflow:visible}
.tl-item .loc{font-size:12px;font-weight:500;margin-top:1px;white-space:normal;word-break:break-word;overflow-wrap:anywhere}
.day-title{display:flex;align-items:center;gap:10px;flex:1}
.day-big{font-size:26px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.day-info{font-size:13px;color:var(--tx2);font-weight:500;display:flex;align-items:center;flex-wrap:wrap;gap:2px}

/* 任务卡片紧急度 */
.kcard.overdue{border-color:#f3c2c0;background:#fff5f4}
.kcard.urgent{border-color:#f5d49b;background:#fffbeb}
.kcard.soon{border-color:#c9e4d0}
[data-theme="dark"] .kcard.overdue{background:rgba(228,84,79,.08)}
[data-theme="dark"] .kcard.urgent{background:rgba(240,145,58,.08)}
.ktag{font-size:12px;padding:1px 6px;border-radius:var(--r-md);font-weight:500;white-space:nowrap}
.ktag.overdue{background:#fdeceb;color:#c93c37}
.ktag.urgent{background:#fef3e6;color:#a06014}
.ktag.soon{background:#eefaf3;color:#187a4b}

/* 公告已读 */
.notice{position:relative;padding:10px 0;border-bottom:1px solid var(--line2);cursor:pointer}
.notice.unread{padding-left:16px}
.notice.unread:before{content:'';position:absolute;left:0;top:14px;width:6px;height:6px;border-radius:50%;background:var(--pri)}
.unread-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--pri);margin-right:6px;flex:none}
.read-badge{font-size:12px;padding:1px 7px;border-radius:var(--r-md);background:#f1f4fa;color:var(--tx3);margin-left:8px}
.read-badge.unread{background:#fff3e6;color:#a06014}

/* 公告紧凑行 + 详情弹窗 */
.notice.row-card{display:flex;align-items:center;gap:8px;padding:10px 10px;border-radius:var(--r-md)}
.notice.row-card:hover{background:#f7f9fe}
[data-theme="dark"] .notice.row-card:hover{background:#1e2638}
.notice .nc-main{flex:1;display:flex;align-items:center;gap:8px;min-width:0}
.notice .nc-title{font-weight:500;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.notice .nc-time{color:var(--tx3);font-size:12px;flex:none;font-variant-numeric:tabular-nums}
/* 公告行尾删除按钮：默认隐藏，鼠标移到这一行才浮现（主管可见） */
.notice.row-card .nc-del{flex:none;width:22px;height:22px;line-height:1;border:none;border-radius:50%;
  background:transparent;color:var(--tx3);font-size:12px;cursor:pointer;opacity:0;transition:opacity .15s,background .15s;padding:0}
.notice.row-card:hover .nc-del{opacity:1}
.notice.row-card .nc-del:hover{background:var(--danger);color:#fff}
/* 触屏设备没有 hover：直接常显，免得点不到 */
@media (hover:none){.notice.row-card .nc-del{opacity:1}}
/* 「去处理」跳转后的高亮行 */
.item.focus-row{background:var(--pri-soft);box-shadow:inset 3px 0 0 var(--pri);border-radius:var(--r-md)}
[data-theme="dark"] .item.focus-row{background:#1b2540}
.notice-dtl .nd-title{font-size:16px;font-weight:500;margin-bottom:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;line-height:1.4}
.notice-dtl .nd-body{font-size:13px;line-height:1.75;white-space:pre-wrap;word-break:break-word;
  background:#f8fafd;border:1px solid var(--line2);border-radius:var(--r-md);padding:12px 14px}
.notice-dtl .nd-meta{color:var(--tx3);font-size:12px;margin-top:8px}
[data-theme="dark"] .notice-dtl .nd-body{background:#161b2c}

/* 搜索结果 */
.sr-h{font-size:13px;color:var(--tx2);margin:14px 0 6px;padding-bottom:4px;border-bottom:1px solid var(--line2)}

/* 下拉刷新提示 */
.pull-tip{position:fixed;top:0;left:50%;transform:translateX(-50%);z-index:200;
  background:var(--card);border:1px solid var(--line);border-radius:0 0 var(--r-md) var(--r-md);
  padding:8px 18px;font-size:13px;color:var(--tx2);box-shadow:var(--sh);opacity:0;transition:.2s}

/* 深色模式时间轴 */
[data-theme="dark"] .tl-slot{background:transparent}
[data-theme="dark"] .tl-gapline{background:repeating-linear-gradient(-45deg,var(--gap-bg),var(--gap-bg) 4px,var(--track-bg) 4px,var(--track-bg) 8px)}
[data-theme="dark"] .cg-break{background:repeating-linear-gradient(-45deg, rgba(255,255,255,.03), rgba(255,255,255,.03) 8px, rgba(255,255,255,.07) 8px, rgba(255,255,255,.07) 16px); color:rgba(255,255,255,.45)}
[data-theme="dark"] .tl-item .del{background:rgba(0,0,0,.3)}
[data-theme="dark"] .wv-col.weekend .wv-head{background:#161b2c}
[data-theme="dark"] .wv-col.weekend .wv-track:before{background:linear-gradient(180deg,#141a28,#161b2c)}
[data-theme="dark"] .wv-head.today{background:linear-gradient(180deg,var(--pri-soft),transparent)}
[data-theme="dark"] .tl-item.k-plan .t{color:#ffc090}
[data-theme="dark"] .tl-item.k-meet .t{color:#cab0ff}
[data-theme="dark"] .tl-item.k-task .t{color:#7ec9f5}
[data-theme="dark"] .tl-item.k-leave .t{color:#ff9b97}
[data-theme="dark"] .tl-item.k-free .t{color:#6beab0}
[data-theme="dark"] .tl-item.k-task{background:linear-gradient(135deg,#122536,#163048)}

/* 周视图移动端
   ★ 关键改动：以前 .wv-col 是 min-width:60px 的固定最小宽，7 列 + 52px 时间轴标签 ≈ 470px，
     比手机屏幕（360~400px）宽 → 必须横向滚动才能看到周五周六，而横滑又会误触发「切换下一周」。
     现在让 7 列「弹性收缩铺满屏幕」（min-width 降到 44px、flex 允许压缩），
     一周 7 天完整可见，不需要横滑，滑动切换周的手势也就不会再和滚动打架。 */
@media(max-width:760px){
  .search-in{width:100px}
  .search-in:focus{width:140px}
  /* ★ v101 #3：左侧节次号列必须**纵向也吸住**，否则下滑时「1/2/3/4节」会一起滚走。
     ⚠ 原来只有 position:sticky; left:0（只吸左），**没有 top** ——
       left:0 只处理横向滚动吸附，纵向完全没吸。用户原话
       「下面的时候我也看不到今天的日期，左边的1234节」里的「左边1234节」就是这条。
     ⚠ top 的值取 var(--wv-head-h)：节次号列的顶部本来就用 padding-top 让出了表头等高
       （app.js renderWeekView 里 labels.style.paddingTop = hh），
       所以它相对滚动容器顶部的偏移恰好等于表头高；不给 top 的话它会一路滑到顶。
     ⚠ z-index 4 < .wv-head 的 20：保证两者交叉时表头压在上层（表头本就在上、节次号在下，
       不冲突；写20 反而会让左侧列盖住表头左边缘）。
     ⚠ background 必须是不透明 var(--card)：吸住之后会盖住下方滚过的课块，
       半透明会透出字。 */
  .wv-scroll>.tl-labels{flex:none;width:44px;position:sticky;left:0;
    top:var(--wv-head-h,96px);z-index:4;background:var(--card)}
  .wv-scroll>.tl-labels .tl-lab{font-size:12px}
  /* ===== v99 #2：手机端周课表（时间轴）一行看全 =====
     用户原话：「时间轴周课表没法在一屏里看全课程信息」。
     与 v98 的「7 列固定宽 + 横滑」相反，改三处：
       ① 7 列**均分铺满屏宽**（flex:1 1 0; min-width:0），不再固定 104px、不再横向滚动；
          列变窄但 7 天同时可见，横滑误切周的手势也消失；
       ② 课块文字**允许换行**（white-space:normal + word-break），
          取消 nowrap+ellipsis 截断，名字 / 教室 / 时间都露全；
       ③ 课表区本身**纵向滚动**（overflow-y:auto + max-height），
          表头吸顶、时间轴列吸左，整周靠滚动看完，不必点进详情。 */
  .wv-scroll{overflow:visible;-webkit-overflow-scrolling:touch;
    /* ────────────────────────────────────────────────────────────────────
       ★ v100：把写死的 max-height:calc(100vh - 160px) 换成三个**实测**变量相减：
         --qz-topbar-h  顶栏实测高（含 safe-area）        app.js syncTopbarH 写
         --wv-chrome    面板头 + 呼吸留白                 app.js renderWeekView 写
         --qz-tabbar-h  底部标签栏实测高（含安全区）        app.js syncTabbarH 写
       一屏能显示的轨道高度 = 视口高 − 顶栏 − 面板头 − 标签栏。

       ★★ v101 #1【本轮修回弹】**去掉 dvh，改用 svh**。
       ────────────────────────────────────────────────────────────────────
       用户原话：「往下滑，他总是会莫名的回弹」。
       根因：**dvh 天生是动态量** —— 它等于「当前可见视口高度」，
       移动端手指下滑时地址栏会收起、视口变高 ⇒ dvh 变 ⇒ max-height 变
       ⇒ 滚动容器高度在滚动过程中**跳变** ⇒ 内容被顶起/拽回 ⇒ 手感就是「莫名回弹」。
       ⚠ 这里要特别说清一个容易搞反的点：v100 的注释写「用 dvh 避免底部被地址栏盖住」，
         那个动机是对的（dvh 确实等于可见区），但**用在了 max-height 上就是错的**：
         max-height 的语义是「这个盒子该有多高」，它需要的是**一个稳定的高度**，
         而不是「跟着视口抖动的量」。正确做法是让滚动容器在一次会话内保持恒定。
       ⇒ svh（small viewport height）= 地址栏**收起**时的视口高，滚动中恒定不变。
         取 svh 而非 vh 的原因：vh 在移动端等于地址栏**展开**时的高度，
         用 vh 会让容器比可见区高出一截（底部一节被地址栏遮住，且要滚动才看得到）。
         svh 是「保守但恒定」的那一个，正好符合「容器要稳定」+「别被遮」两个要求。
       ⚠ 声明顺序（老浏览器兜底）：先给 svh，再给 vh。
         不认识 svh 的老 WebView 会**丢掉 svh 那条**、用后一条 vh 生效，
         不会出现「两条都生效、后者被前者覆盖」以外的问题。
       ⚠ var 兜底值沿用 v100 的口径（52/64/60），JS 未跑时行为与 v100 一致，不倒退。 */
    /* ★ v101 #5：时间轴不再做成「固定高度的内层滚动盒」。
       用户原话：「把那个页面弄长一点，没必要一个界面都这么小，然后里面又往下滑」。
       ⇒ 去掉 max-height / overflow-y:auto，让 .wv-scroll 按内容（AXIS.h）自然撑高、整页滚动；
         日期表头(.wv-head sticky top:0) 与节次列(.tl-labels sticky top:var(--wv-head-h))
         改相对**视口**吸顶，滚动时仍可见。原 v101 #1 的「dvh→svh」方案在此作废。 */
    min-height:60vh;
    /*────── ★★ v101 #2【本轮修「看不到今天的日期/左边1234节」】 ──────
       ⚠⚠ 这是**滚动链**（scroll chaining），不是 sticky 失效。实测证据：
         移动端真触摸（CDP Input.dispatchTouchEvent，连滑 3×500px）：
           现状（无本行）              → window.scrollY=1098，.wv-scroll 顶=-1031，
                                        表头 top=-1031 ⇒ **表头被带出屏幕**
           加本行 contain              → window.scrollY=0，.wv-scroll 顶=67，
                                        表头 top=67 ⇒ **表头稳稳吸住**
         ⇒ 机制：.wv-scroll 内滚范围只有 ~214px（AXIS.h − 容器高），
           手指继续下滑时浏览器把滚动**链式传递**给 window；
           window 一滚，整个 .wv-scroll（含它内部 sticky 的表头）作为一个盒子被平移出视口
           —— sticky 只在**自己的滚动容器内**吸顶，管不了「整个容器被移走」，
           所以表头跟着一起没了。用户看到的就是「下面的时候看不到今天的日期、左边的1234节」。
       ⇒ overscroll-behavior:contain = 滚动到边界后**不把滚动传给祖先**，链条被切断。
         contain 而非 none：contain 仍允许容器内正常滚动，只是不外传；
         none 会额外禁掉纵向的橡皮筋回弹，这里不需要（回弹问题已由上面 svh 解决）。
       ⚠ 与上面 svh 是**两件事**，别混为一谈：
         svh  解决「容器高度在滚动中跳变」 ⇒ 解决回弹（用户原话「莫名的回弹」）
         contain 解决「滚动链把整个容器带走」 ⇒ 解决表头/节次号消失
       ⚠ 只给 .wv-scroll 加，不给 body 加：body 上加 contain 会让整页在触顶后
         完全无法继续滚动（用户想看页面下方时就卡住了），那是另一个副作用。
       ⚠ 老浏览器不认这个属性 ⇒ 整条声明作废、行为退回v100，不倒退。 */
  }
  .wv-cols{flex:1;min-width:0}
  .wv-col{flex:1 1 0;min-width:0;max-width:none}
  .wv-col .tl-item{font-size:11px;padding:3px 4px;line-height:1.25;overflow:visible}
  .wv-col .tl-item .t{font-size:12px;line-height:1.25;font-weight:600;white-space:normal;
    word-break:break-word;overflow-wrap:anywhere}
  .wv-col .tl-item .s{font-size:10px;opacity:.85;white-space:normal;
    word-break:break-word;overflow-wrap:anywhere}
  .wv-col .tl-item .loc{font-size:10px;line-height:1.2;white-space:normal;
    word-break:break-word;overflow-wrap:anywhere;overflow:visible}
  .wv-col .tl-item .tea{font-size:10px;line-height:1.2;opacity:.9;white-space:normal;
    word-break:break-word;overflow-wrap:anywhere;overflow:visible}
}
/* 极窄屏（≤360px）：**只降列宽，不降字号**。
   ⚠ 为什么不能降字号：96 − 4 − 4 − 4 = 84px ⇒ 12px 字仍是 floor(84/12) = 7 字/行；
     而把字号降到 8px 会掉到 10 字/行以下，字号一小反而更看不清（用户要「明显」）。 */
@media(max-width:360px){
  .wv-scroll>.tl-labels{flex:none;width:36px}
  .wv-cols{flex:1;min-width:0}
  .wv-col{flex:1 1 0;min-width:0}
  .wv-col .tl-item{font-size:10px;padding:3px 3px}
}

/* ============ 概览统计卡片：可点击 ============ */
.stat-card.tap{cursor:pointer;position:relative;transition:.15s}
.stat-card.tap:hover{border-color:var(--pri);transform:translateY(-1px)}
.stat-card.tap:hover .k{color:var(--pri)}
.stat-card.tap:active{transform:translateY(0)}
/* 右上角小箭头，暗示"可以点" */
.stat-card.tap:after{content:"›";position:absolute;top:9px;right:11px;font-size:14px;
  color:var(--tx3);opacity:.55;line-height:1}
.stat-card.tap:hover:after{opacity:1;color:var(--pri)}
.stat-card .v em{font-style:normal;font-size:12px;font-weight:500;color:var(--tx3);margin-left:5px}

/* 弹窗里的明细列表（统计卡片点开看） */
.dtl-list{display:flex;flex-direction:column;gap:7px;margin-top:4px}
.dtl-row{display:flex;align-items:flex-start;gap:9px;padding:9px 11px;border-radius:var(--r-md);
  background:var(--slot-bg);border:1px solid var(--line2)}
.dtl-row .dbar{width:3px;align-self:stretch;border-radius:var(--r-md);background:var(--tx3);flex:none}
.dtl-row .dbar.k-course{background:var(--course)}
.dtl-row .dbar.k-meet{background:var(--meet)}
.dtl-row .dbar.k-task{background:var(--plan)}
.dtl-row .dbar.k-leave{background:var(--leave)}
.dtl-row .dbar.k-plan{background:var(--plan)}
.dtl-row .dmain{flex:1;min-width:0}
.dtl-row .dt{font-weight:500;font-size:13px;word-break:break-word}
.dtl-row .dm{color:var(--tx2);font-size:12px;margin-top:3px;word-break:break-word}
.dtl-row .dm b{color:var(--tx);font-weight:500}
.dtl-row .dtool{flex:none;font-size:12px;color:var(--tx3);text-align:right;white-space:nowrap}
.dtl-empty{text-align:center;color:var(--tx3);font-size:13px;padding:26px 0}
.dtl-empty .ico{display:block;font-size:30px;margin-bottom:8px;opacity:.5}
.dtl-sum{font-size:12px;color:var(--tx3);margin-bottom:8px}

/* 弹窗里的成员纵向名单 */
.dtl-mem{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:var(--r-md);
  background:var(--slot-bg);border:1px solid var(--line2)}
.dtl-mem .av{width:30px;height:30px;border-radius:50%;flex:none;display:flex;align-items:center;
  justify-content:center;color:#fff;font-size:13px;font-weight:500}
.dtl-mem .mi{flex:1;min-width:0}
.dtl-mem .mn{font-weight:500;font-size:13px}
.dtl-mem .mr{color:var(--tx3);font-size:12px;margin-top:1px}

/* ============ 最近动态：行尾删除 / 清空 ============ */
.act-row{display:flex;align-items:center;gap:9px;border:1px solid var(--line2);border-radius:var(--r-md);
  padding:8px 11px;background:var(--card);transition:.15s}
.act-row:hover{border-color:var(--line);box-shadow:var(--sh)}
.act-row .amain{flex:1;min-width:0}
.act-row .attl{font-weight:500;font-size:13px;word-break:break-word}
.act-row .ameta{color:var(--tx3);font-size:12px;margin-top:2px}
.act-row .adel{flex:none;width:22px;height:22px;line-height:1;border:none;border-radius:50%;
  background:transparent;color:var(--tx3);font-size:12px;cursor:pointer;opacity:0;
  transition:opacity .15s,background .15s;padding:0}
.act-row:hover .adel{opacity:1}
.act-row .adel:hover{background:var(--danger);color:#fff}
@media (hover:none){.act-row .adel{opacity:1}}
[data-theme="dark"] .act-row{background:var(--slot-bg)}
[data-theme="dark"] .dtl-row{background:#161b2c}
[data-theme="dark"] .dtl-mem{background:#161b2c}

/* ============ 教务内置浏览器 ============ */
.sch-browser{position:fixed;inset:0;z-index:300;background:var(--bg);display:flex;flex-direction:column}
.sch-browser.hidden{display:none !important}
.sch-bbar{display:flex;align-items:center;gap:8px;padding:9px 12px;background:var(--card);
  border-bottom:1px solid var(--line);flex:none;flex-wrap:wrap;
  padding-top:calc(9px + env(safe-area-inset-top,0px))}
.sch-bbar .sbu{flex:1;min-width:120px;font-size:12px;color:var(--tx3);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;background:var(--slot-bg);border:1px solid var(--line2);
  border-radius:var(--r-md);padding:5px 10px}
.sch-bbar .sbhint{flex:1 1 100%;font-size:12px;color:var(--tx3);margin-top:2px}
/* 入口探测失败时的提示条：教务入口页会随机抽风，探测全失败要明确告诉用户，
   别让他对着一直转圈的 iframe 干等 */
.sch-bbar .sbwarn{flex:1 1 100%;font-size:12px;line-height:1.6;margin-top:2px;color:#b3560f;
  background:#fdf4e8;border-radius:var(--r-md);padding:7px 11px}
.sch-bbar .sbwarn.hidden{display:none}
.sch-bframe{flex:1;border:none;width:100%;background:#fff}
[data-theme="dark"] .sch-bframe{background:#121620}
.sch-bmask{position:fixed;inset:0;z-index:310;background:rgba(15,19,32,.5);display:flex;
  align-items:center;justify-content:center;padding:20px}
.sch-bmask .bx{background:var(--card);border-radius:var(--r-md);padding:20px 22px;max-width:400px;
  text-align:center;box-shadow:var(--sh)}
.sch-bmask .bx .bi{font-size:34px;margin-bottom:10px}
.sch-bmask .bx .bt{font-weight:500;font-size:16px;margin-bottom:8px}
.sch-bmask .bx .bd{color:var(--tx2);font-size:13px;line-height:1.7}
.sch-bmask .bx .ba{margin-top:14px;display:flex;gap:8px;justify-content:center;flex-wrap:wrap}
.sch-tip{background:var(--slot-bg);border:1px solid var(--line2);border-radius:var(--r-md);
  padding:10px 12px;font-size:13px;color:var(--tx2);line-height:1.7;margin-bottom:12px}
.sch-tip b{color:var(--tx)}

/* ====================================================================
   深色模式：第二轮彻底清理「残余白底」
   --------------------------------------------------------------------
   上一轮只覆盖了一部分类名，漏掉的是**另一种写法**：不是 background:#fff，
   而是 background:#fbfcfe / #f7f9fd / #e8ecf5 这类「近白色」，
   以及写在 **伪元素**（.tl-track:before）上的浅色渐变。
   这些同样会在深色下变成白块，但搜 #fff 完全搜不到。

   本组用「近白即覆盖」的思路补齐，并统一走 var(--card)/var(--slot-bg)，
   保证以后改主题色只需改一处。
   ==================================================================== */

/* --- ① 输入类控件：普通态就被漏掉了（只写了 :focus）---
   .pane input / .fld input 的默认底是 #fbfcfe，:focus 又变 #fff；
   地址框 .share-box input 同理。这里把普通态 + hover + focus 一次覆盖干净。 */
[data-theme="dark"] .pane input,[data-theme="dark"] .pane textarea,
[data-theme="dark"] .fld input,[data-theme="dark"] .fld select,[data-theme="dark"] .fld textarea,
[data-theme="dark"] .share-box input,[data-theme="dark"] .date-in,
[data-theme="dark"] .row input,[data-theme="dark"] .wx-search input,
[data-theme="dark"] .auth-card form input{
  background:#161b2c;border-color:var(--line);color:var(--tx)}
[data-theme="dark"] .pane input:focus,[data-theme="dark"] .pane textarea:focus,
[data-theme="dark"] .fld input:focus,[data-theme="dark"] .fld select:focus,[data-theme="dark"] .fld textarea:focus,
[data-theme="dark"] .share-box input:focus,[data-theme="dark"] .date-in:focus,
[data-theme="dark"] .auth-card form input:focus{
  background:#1a2233;border-color:var(--pri)}
[data-theme="dark"] input::placeholder,[data-theme="dark"] textarea::placeholder{color:#5a6780}

/* --- ② 日视图时间轴：浅色渐变写在 :before 伪元素上 ---
   .tl-track:before{background:linear-gradient(180deg,#fcfdff,#fafbfe)} 是整片白的元凶。
   注意上一轮把 .tl-slot 设成 transparent 反而把下面的浅色渐变露出来了，
   这里直接换掉伪元素的渐变本身。 */
[data-theme="dark"] .tl-track:before{
  background:linear-gradient(180deg,#1a2030,#161b2c)}
[data-theme="dark"] .tl-slot:hover{background:rgba(91,141,239,.10)}
[data-theme="dark"] .tl-label,[data-theme="dark"] .tl-slot{border-bottom-color:var(--line2)}

/* --- ③ 任务看板：空列底色是 #f7f9fd，卡片是 #fff --- */
[data-theme="dark"] .kcol{background:var(--slot-bg);border-color:var(--line2)}
[data-theme="dark"] .kcol-h em{background:#232a3d;color:var(--tx2)}
[data-theme="dark"] .kcard{background:#161b2c;border-color:var(--line2)}
[data-theme="dark"] .kcard:hover{border-color:var(--line)}

/* --- ④ 弹窗标题栏：.modal h3 是 #fbfcff --- */
[data-theme="dark"] .modal h3{background:var(--slot-bg);border-bottom-color:var(--line)}
[data-theme="dark"] .modal .foot{border-top-color:var(--line)}

/* --- ⑤ 手机/局域网页的地址框 + 复制行 --- */
[data-theme="dark"] .share-box input{background:#161b2c;color:var(--tx3)}

/* --- ⑥ 其余「近白」底色一次性兜底 ---
   写死 #fff 的：.tab.active / .btn.ghost / .pick button / .modal / .dept-sel / .role-sel
   近白的：#e8ecf5 / #f7f9fd / #f4f7ff 等，统一收口。 */
[data-theme="dark"] .tab.active{background:#1e2638;color:var(--tx)}
[data-theme="dark"] .btn.ghost,[data-theme="dark"] .btn.danger{background:var(--card)}
[data-theme="dark"] .pick button{background:#161b2c;color:var(--tx2)}
[data-theme="dark"] .chk{background:#161b2c;border-color:var(--line);color:var(--tx2)}
[data-theme="dark"] .dept-sel,[data-theme="dark"] .role-sel{background:#161b2c;color:var(--tx2)}
[data-theme="dark"] .slots .slot-pill,[data-theme="dark"] .slot-pill{background:var(--card)}
[data-theme="dark"] .slot-pill.good{background:#102a1a;border-color:#1a4a30;color:#6beab0}
[data-theme="dark"] .tl-slot:hover{background:rgba(91,141,239,.10)}

/* --- ⑦ 兜底保险：任何仍带 .bg-white 语义的工具类 --- */
[data-theme="dark"] .bg-white{background:var(--card) !important}

/* --- ⑧ 二维码是唯一必须保持白底的元素，这里再次显式声明，防止被误伤 --- */
[data-theme="dark"] .qr-cv{background:#fff !important}

/* ====================================================================
   ⑨ 最后一批「近白」收口
   上面②只修了日视图 .tl-track:before，但**周视图的周末列**用了同一招
   （.wv-col.weekend .wv-track:before 也是浅色渐变）→ 一起修。
   其余是按 CSS 文件顺序扫出来的同类项：容器底、hover 底、标签底。
   ==================================================================== */

/* 周视图周末列：浅色渐变 + 浅色表头 */
[data-theme="dark"] .wv-col.weekend .wv-track:before{
  background:linear-gradient(180deg,#141a28,#121724)}
[data-theme="dark"] .wv-col.weekend .wv-head{background:#1a2030}
[data-theme="dark"] .wv-track:before{background:linear-gradient(180deg,#1a2030,#161b2c)}

/* 底部弹层（手机 Sheet） */
[data-theme="dark"] .sheet-head button{background:#232a3d;color:var(--tx2)}
[data-theme="dark"] .sheet-item{background:var(--card);border-color:var(--line2)}

/* 分组标签 / 页签容器 / 计数气泡 / hover 底 */
[data-theme="dark"] .tabs{background:#161b2c}
[data-theme="dark"] .navbtn:hover{background:#1e2638}
[data-theme="dark"] .chip{background:#1e2638}
[data-theme="dark"] .chip .me-lbl{background:#2a3346;color:var(--tx3)}
[data-theme="dark"] .tag{background:#232a3d}
[data-theme="dark"] .read-badge{background:#232a3d;color:var(--tx3)}
[data-theme="dark"] .rec-dur{background:#232a3d}
[data-theme="dark"] .cell:hover{background:#1e2638}
[data-theme="dark"] .notice.row-card:hover{background:#1e2638}
[data-theme="dark"] .btn:hover{background:#1e2638;border-color:#3a4560}
[data-theme="dark"] .btn.danger:hover{background:#2a1818}

/* 数据表 */
[data-theme="dark"] .dtable th{background:#161b2c}
[data-theme="dark"] .dtable tr:hover td{background:#1e2638}

/* 天气卡片 */
[data-theme="dark"] .wx-day{background:var(--slot-bg);border-color:var(--line2)}
[data-theme="dark"] .dcard{background:var(--card);border-color:var(--line)}
[data-theme="dark"] .wx-hero{background:linear-gradient(120deg,#1e2a4a,#161b2c)}

/* 进度条轨道 / 只读展示框 / 天气 chip / 异常提示 */
[data-theme="dark"] .fbar-track{background:#161b2c}
[data-theme="dark"] .readonly{background:#161b2c;border-color:var(--line2);color:var(--tx2)}
[data-theme="dark"] .wx-chip{background:#1e2638;color:var(--tx2)}
[data-theme="dark"] .warn{background:#2a1818;color:#ff9b97}

/* 空态 / 提示条的浅底 */
[data-theme="dark"] .ok-tip{background:#102a1a;border-color:#1a4a30;color:#6beab0}

/* 手机 / 局域网：当前网络卡 + 网卡选择（浅底 + 浅色标签统一收口） */
[data-theme="dark"] .lan-now{background:linear-gradient(135deg,#1c2438,transparent);border-color:var(--line2)}
[data-theme="dark"] .lan-if{background:#232a3d;color:var(--tx2)}
[data-theme="dark"] .lan-hint.lh-ok{color:#6beab0}
[data-theme="dark"] .lan-hint.lh-warn{color:#f0b070;background:#2a2010}
[data-theme="dark"] .sch-bbar .sbwarn{color:#f0b070;background:#2a2010}
[data-theme="dark"] .lan-item{background:var(--card);border-color:var(--line);color:var(--tx)}
[data-theme="dark"] .lan-item:hover{border-color:var(--pri)}
[data-theme="dark"] .lan-item.on{background:#1c2438;border-color:var(--pri)}
[data-theme="dark"] .lan-item em.ok{background:#102a1a;color:#6beab0}
[data-theme="dark"] .lan-item em.lan-bad{background:#2a2010;color:#f0b070}
[data-theme="dark"] .lan-wifi{background:#102a1a;color:#6beab0}
[data-theme="dark"] .pwd-box{background:#1c2438;border-color:var(--line2)}
[data-theme="dark"] .chip.clickable:hover{background:#1e2638}

/* 遗漏的状态标签（浅底深字，深色下要换成深底亮字） */
[data-theme="dark"] .ktag.overdue{background:#2a1818;color:#ff9b97}
[data-theme="dark"] .ktag.urgent{background:#2a2010;color:#ffc090}
[data-theme="dark"] .tag.no{background:#2a1818;color:#ff9b97}
[data-theme="dark"] .tag.wa{background:#2a2010;color:#ffc090}
[data-theme="dark"] .tag.ok{background:#102a1a;color:#6beab0}
[data-theme="dark"] .tag.own{background:#2e2350;color:#cbb6ff}
[data-theme="dark"] .mx-table td.k-free{background:#102a1a;color:#6beab0}

/* ====================================================================
   ⑩ 第三轮收口：把「写死浅底/写死深字」的规则**在源头变量化**
   --------------------------------------------------------------------
   症状：深色模式下仍有「白底浅灰字」看不清。根因不是漏了某个类名，
   而是这些规则本身就写死了颜色——例如
     .btn{background:#fff;color:var(--tx)}
   在深色下 color 会跟着变量变成浅色（#e2e8f0），但 background 还是 #fff，
   两者一叠加就是「白底浅灰字」。
   所以本轮不再逐个补 [data-theme="dark"] 覆盖，而是把这些硬编码值
   直接换成变量，让两套主题各自取色——一次修完，不会再漏。
   ==================================================================== */

/* --- ① 按钮族：.btn 默认态底 + ghost 底 + danger 底 + primary hover --- */
.btn{background:var(--card)}
.btn:hover{background:var(--slot-bg)}
.btn.ghost{background:var(--card)}
.btn.danger{background:var(--card)}
.btn.danger:hover{background:#fdf3f2}
.btn.primary:hover{filter:brightness(1.08)}
[data-theme="dark"] .btn:hover{background:#1e2638;border-color:#3a4560}
[data-theme="dark"] .btn.danger:hover{background:#2a1818}

/* --- ② 页签族：.tabs 容器底 + .tab.active 底（白底浅字重灾区）--- */
.tabs{background:var(--slot-bg)}
.tab.active{background:var(--card);color:var(--tx)}

/* --- ③ 输入族：默认底 #fbfcfe / 聚焦底 #fff --- */
.pane input,.pane textarea,.fld input,.fld select,.fld textarea{background:var(--slot-bg)}
.pane input:focus,.fld input:focus,.fld select:focus,.fld textarea:focus{background:var(--card)}

/* --- ④ hover 族：日历格子、导航按钮（浅底 + 深字）--- */
.cell:hover{background:var(--slot-bg)}
.navbtn:hover{background:var(--slot-bg)}

/* --- ⑤ 登录卡片：.auth-card 用 var(--card) 已安全，这里补 logo 前景 --- */
.auth-logo,.logo{color:#fff}
/* ================= v9 新增：选人 / 按时间找人 / 课表识别 / 邀请码 / 闹钟 ================= */

/* ---- 全员空闲页头部：模式切换 + 选人 ---- */
.team-head{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px}
.team-tabs{display:flex;background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:3px;gap:3px}
.ttab{border:none;background:transparent;color:var(--tx2);font-size:13px;padding:7px 14px;border-radius:var(--r-md);cursor:pointer}
.ttab.on{background:var(--pri);color:#fff}
.team-pick{display:flex;gap:8px;align-items:center}

/* ---- 选人弹窗分组 ---- */
.pick-group{margin-bottom:10px}
.pick-gt{font-size:12px;color:var(--tx3);margin:6px 0 4px;font-weight:500}
.pick-group .checks{display:flex;flex-wrap:wrap;gap:6px 12px}

/* ---- 按时间找人 ---- */
.find-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:var(--pri-soft);border-radius:var(--r-md);padding:10px 12px;margin-bottom:12px}
.find-bar label{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--tx2)}
.find-res{display:flex;gap:14px;flex-wrap:wrap}
.find-col{flex:1;min-width:260px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:10px 12px}
.find-col h4{margin:0 0 8px;font-size:13px}
.fp-ok{color:var(--ok,#16a34a)}
.fp-busy{color:#e4544f}
.fp-list{display:flex;flex-wrap:wrap;gap:8px}
.fp-chip{display:inline-flex;align-items:center;gap:6px;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-md);padding:4px 10px 4px 4px;font-size:13px;max-width:100%}
.fp-chip i{font-style:normal;color:var(--tx3);font-size:12px}
.fp-chip .avatar{width:24px;height:24px;font-size:12px}
.fp-why{font-style:normal;color:var(--tx3);font-size:12px;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---- 课表识别导入 ---- */
.si-thumb{margin-top:8px;max-height:180px;overflow:hidden;border-radius:var(--r-md);border:1px solid var(--line)}
.si-thumb img{max-width:100%;display:block}
/* 导入弹窗里的说明文字：.fld 是纵向 flex，会把一段话拆成一行一句（排版碎掉），
   包进 .si-tip 让它作为单个 flex 项正常换行；颜色用 --tx（说明是正文不是表单标签） */
.si-tip{color:var(--tx);font-size:13px;line-height:1.75}
.si-tip b{color:var(--pri)}
.si-tip{font-size:13px;color:var(--tx2);margin:8px 0}
.si-warn{background:#fff7e8;border:1px solid #f0d9a8;color:#8a6116;border-radius:var(--r-md);padding:8px 12px;font-size:13px;margin:8px 0}
[data-theme="dark"] .si-warn{background:#2b2413;border-color:#54421e;color:#e8c87a}
.si-table input,.si-table select{min-width:90px}
.si-table td{vertical-align:middle}
/* v38 ⑧：图片上传缩略图（改为选文件后预览 + 可删） */
.img-thumbs{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.ithumb{position:relative;display:inline-block;width:64px;height:64px;border-radius:var(--r-md);overflow:hidden;border:1px solid var(--bd,#e3e6ea)}
.ithumb img{width:100%;height:100%;object-fit:cover;display:block}
.ithumb .idel{position:absolute;top:0;right:0;width:18px;height:18px;line-height:16px;text-align:center;
  border:0;background:rgba(0,0,0,.55);color:#fff;border-radius:0 0 0 var(--r-xs);cursor:pointer;font-size:12px;padding:0}
/* v38 ③：组织架构页的社团卡片 */
.tn-card{display:flex;gap:12px;align-items:flex-start;padding:12px;border:1px solid var(--bd,#e3e6ea);
  border-radius:var(--r-md);background:var(--bg2,#f8f9fb);margin-bottom:8px}
[data-theme="dark"] .tn-card{background:#1e2228;border-color:#333a44}
.tn-av{width:56px;height:56px;border-radius:var(--r-md);object-fit:cover;border:1px solid var(--bd,#e3e6ea);flex:none;background:#fff}
[data-theme="dark"] .tn-av{background:#1e2228;border-color:#333a44}
.tn-av-ph{width:56px;height:56px;border-radius:var(--r-md);flex:none;display:flex;align-items:center;justify-content:center;
  font-size:24px;background:var(--pri,#3b82f6);color:#fff}
.tn-main{flex:1;min-width:0}
.tn-main .tn-name{font-size:14px;font-weight:500}
.tn-main .tn-intro{margin-top:4px;font-size:13px;color:var(--tx2,#666);white-space:pre-wrap;word-break:break-word}
.tn-meta{margin-top:4px;font-size:12px;color:var(--tx2,#666)}
/* v38：星期批量修正栏（文本里没星期时，逐条改 20 行太痛苦） */
.si-batch{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:8px 0;padding:8px 10px;
  background:var(--bg2,#f6f7f9);border:1px solid var(--bd,#e3e6ea);border-radius:var(--r-md);font-size:13px}
.si-batch select{min-width:90px}
.si-batch .hint{margin:0}
[data-theme="dark"] .si-batch{background:#1e2228;border-color:#333a44}
.si-raw td{font-size:12px;color:var(--tx3);padding-top:0;border-top:none}

/* ---- v33：学校/年级/班级 三级联动下拉 ---- */
/* .cc-lbl 用块级，避免 .fld 的纵向 flex 把标签和控件挤到一行 */
.cc-lbl{display:block;font-size:12px;color:var(--tx2);margin-bottom:4px}
[data-cc] select{width:100%}
[data-cc] input[data-custom]{width:100%}
[data-cc] select[data-lv="school"]{min-width:0}

/* ---- 部门邀请码 ---- */
.code-box{background:var(--pri-soft);border:1px dashed var(--pri);border-radius:var(--r-md);padding:14px;text-align:center}
.code-val{font-size:26px;font-weight:500;letter-spacing:2px;color:var(--pri);font-family:ui-monospace,Consolas,monospace}
.code-meta{font-size:12px;color:var(--tx2);margin-top:6px}
.lh-warn-t{color:#e4544f;font-style:normal}

/* ---- 闹钟弹窗 ---- */
.alarm-mask{position:fixed;inset:0;z-index:3000;background:rgba(8,12,24,.78);display:flex;align-items:center;justify-content:center;animation:alarmIn .25s ease}
.alarm-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:26px 24px 18px;max-width:420px;width:calc(100% - 40px);text-align:center;box-shadow:0 18px 60px rgba(0,0,0,.35)}
.alarm-bell{font-size:46px;animation:alarmShake 1s ease-in-out infinite}
.alarm-card h3{margin:8px 0 12px;font-size:16px;color:var(--tx)}
.alarm-list{display:flex;flex-direction:column;gap:8px;margin-bottom:16px;max-height:40vh;overflow:auto;text-align:left}
.alarm-item{display:flex;gap:10px;align-items:flex-start;background:var(--bg);border:1px solid var(--line);border-radius:var(--r-md);padding:10px 12px}
.alarm-item .ttl{font-size:14px;font-weight:500;color:var(--tx)}
.alarm-item .meta{font-size:12px;color:var(--tx2)}
.alarm-acts{display:flex;gap:10px;justify-content:center}
.alarm-acts .btn{min-width:120px}
.alarm-card .hint{margin-top:10px;font-size:12px;color:var(--tx3)}
@keyframes alarmShake{0%,100%{transform:rotate(0)}20%{transform:rotate(-14deg)}40%{transform:rotate(12deg)}60%{transform:rotate(-8deg)}80%{transform:rotate(6deg)}}
@keyframes alarmIn{from{opacity:0}to{opacity:1}}

/* ---- v39：组织架构（社团/组织平级 × 各自部门）---- */
.tn-block{margin-bottom:20px;padding-bottom:16px;border-bottom:1px dashed var(--bd,#e3e6ea)}
.tn-block:last-of-type{border-bottom:none}
.dintro{margin:8px 0 2px;display:flex;flex-wrap:wrap;gap:6px;align-items:flex-start}
.dintro-txt{flex:1 1 100%;font-size:13px;color:var(--tx2,#666);white-space:pre-wrap;word-break:break-word;
  background:var(--bg2,#f6f7f9);border-radius:var(--r-md);padding:6px 8px}
[data-theme="dark"] .dintro-txt{background:#1e2228}

/* ---- v39：图标动画（每个图标都有轻量动效）---- */
@keyframes icoFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
@keyframes icoWiggle{0%,100%{transform:rotate(0) scale(1)}30%{transform:rotate(-9deg) scale(1.1)}60%{transform:rotate(8deg) scale(1.1)}}
@keyframes icoPop{0%{transform:scale(1)}45%{transform:scale(1.08)}100%{transform:scale(1)}}
/* 常驻图标：轻轻浮动（🏛 社团图标 / 联系方式图标 / 空状态图标 / 页签标签） */
.tn-av-ph,.org-tag,.cc-ic,.dtl-empty .ico,.tn-name{display:inline-block;animation:icoFloat 3.2s ease-in-out infinite}
.cc-ic,.dtl-empty .ico{animation-delay:.4s}
.tn-name{animation-delay:.8s}
/* 交互处：悬停时图标摇摆、按钮轻微弹跳 */
/* 🔴🔴🔴 【别再把 icoPop 挂在 .btn:hover / .ctab:hover 上】
   原因（v122d 实测）：.btn 自己有 border/padding/margin，`transform:scale(1.08)` 会
   把按钮的可命中盒一起放大。鼠标停在按钮边缘时：放大 → 指针重新落在盒内/盒外来回跳
   → hover 反复 on/off → 动画重播 → 叫做「抖动」；更进一步，动画在 45% 关键帧把宽度
   撑大（实测 #trashPurge 435px→566px），点击落点漂移，表现为「点了没用」。
   修法：弹跳只作用在按钮内部的 .ico 图标上（图标是 inline 元素、不参与布局尺寸，
   缩放它不会改变按钮的命中盒）。若将来要给别的控件加 hover 动画，请沿用这个模式：
   动效作用于子元素，绝不作用于被点击元素自身的盒子。 */
.btn:hover>.ico,.ctab:hover>.ico{animation:icoPop .35s ease}
.mchip:hover,.cc-row:hover,.alarm-item:hover{animation:icoWiggle .5s ease}
/* 尊重系统「减少动效」设置 */
@media (prefers-reduced-motion:reduce){
  .tn-av-ph,.org-tag,.cc-ic,.dtl-empty .ico,.tn-name{animation:none}
  .btn:hover>.ico,.ctab:hover>.ico,.mchip:hover,.cc-row:hover,.alarm-item:hover{animation:none}
}

/* ---- 手机端适配 ---- */
@media (max-width:760px){
  .team-head{flex-direction:column;align-items:stretch}
  .team-tabs{justify-content:stretch}
  .ttab{flex:1}
  .find-col{min-width:100%}
  .fp-why{max-width:120px}
  .si-table input{min-width:70px}
  .alarm-card{padding:20px 16px 14px}
}

/* ================= v10：冻结姓名列 / 被挤下线提示 / 日程图片 ================= */
/* 空闲矩阵：横向滚动时第一列（成员姓名）固定不动（用户反馈：左滑看不到是谁） */
.mx-table thead th:first-child{position:sticky;left:0;z-index:3;background:var(--card);box-shadow:4px 0 8px -6px rgba(15,23,42,.35);margin-left:-3px}
.mx-table tbody th{position:sticky;left:0;z-index:2;background:var(--card);box-shadow:4px 0 8px -6px rgba(15,23,42,.35);border-radius:var(--r-sm) 0 0 var(--r-sm);margin-left:-3px}
[data-theme="dark"] .mx-table thead th:first-child,
[data-theme="dark"] .mx-table tbody th{background:#1a2030}
/* 账号在其他设备登录（被挤下线）全屏提示 */
.kick-mask{position:fixed;inset:0;z-index:400;background:rgba(10,14,25,.72);display:flex;align-items:center;justify-content:center;padding:24px}
.kick-card{width:100%;max-width:400px;background:var(--card);border-radius:var(--r-md);padding:28px 22px;text-align:center;box-shadow:0 18px 60px rgba(0,0,0,.4)}
.kick-ico{font-size:40px}
.kick-card h3{margin:10px 0 8px;font-size:16px}
.kick-txt{font-size:13px;color:var(--tx2);line-height:1.8;margin:0 0 16px}
/* 日程图片缩略图（任务看板卡片 / 详情弹窗 / 编辑弹窗共用） */
.dt-imgs{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 2px}
.dt-img-w{position:relative;display:inline-block}
.dt-img-w img{width:64px;height:64px;object-fit:cover;border-radius:var(--r-md);border:1px solid var(--line);cursor:zoom-in;display:block}
.dt-img-x{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;background:#e4544f;color:#fff;font-size:12px;font-style:normal;display:flex;align-items:center;justify-content:center;cursor:pointer;line-height:1}
/* 鍥剧墖鐏锛堢偣鍑绘斁澶э紝鐐瑰嚮浠绘剰澶勫叧闂級 */
.tc-lightbox{position:fixed;inset:0;z-index:500;background:rgba(8,10,18,.88);display:flex;align-items:center;justify-content:center;cursor:zoom-out}
.tc-lightbox img{max-width:92vw;max-height:92vh;border-radius:var(--r-md);box-shadow:0 12px 48px rgba(0,0,0,.5)}

/* ================= v11：空闲矩阵美化 ================= */
/* 手机上一大片深绿「空」块太扎眼 → 空闲用极淡绿（大片留白），有事才用醒目彩底 */
.mx-table td.mx{height:26px;border-radius:var(--r-md);font-size:12px;font-weight:500}
.mx-table td.k-free{background:#f0f9f4;color:#a9d2ba;font-weight:500}
.mx-table td.k-course{background:#e3edfd;color:#2563eb}
.mx-table td.k-meet{background:#efe9fc;color:#7c3aed}
.mx-table td.k-leave{background:#fdeaea;color:#dc2626}
.mx-table td.k-plan{background:#fdf0df;color:#d97706}
.mx-table td.k-task{background:#fdf0df;color:#d97706}
[data-theme="dark"] .mx-table td.k-free{background:#132019;color:#48775c}
[data-theme="dark"] .mx-table td.k-course{background:#14243d;color:#7ea8f4}
[data-theme="dark"] .mx-table td.k-meet{background:#221a38;color:#b49af0}
[data-theme="dark"] .mx-table td.k-leave{background:#331a1c;color:#f08a8a}
[data-theme="dark"] .mx-table td.k-plan{background:#2e2416;color:#eab060}
[data-theme="dark"] .mx-table td.k-task{background:#2e2416;color:#eab060}
/* 列头：节次数字 + 完整时间段（几点到几点）小字 */
.mx-table thead th{padding-bottom:5px}
.mx-table thead th em{display:block;font-size:12px;color:var(--tx3);font-weight:400;margin-top:2px;white-space:nowrap;letter-spacing:-.2px}
/* 图例：色点排列替代长文字 */
.mx-legend{display:flex;flex-wrap:wrap;gap:3px 12px;font-size:12px;color:var(--tx2);align-items:center;margin-top:6px}
.mx-legend i{display:inline-block;width:9px;height:9px;border-radius:var(--r-md);margin-right:4px;vertical-align:-1px}
/* 待审批角标（部门卡片「申请(N)」按钮） */
.req-badge{display:inline-block;background:#e4544f;color:#fff;font-size:12px;border-radius:var(--r-md);padding:0 6px;margin-left:4px;vertical-align:1px}

/* ================= v12：冻结姓名列收窄 + 列头时间段 ================= */
/* 姓名列（sticky 冻结）从 110px 收窄到 74px，右边少挡格子、能看到更多时段；
   内容精简为「头像 + 名字」，部门放 title 悬浮提示 */
.mx-table th:first-child{min-width:74px}
.mx-table tbody th{gap:5px;padding:0 2px}
.mx-table tbody th .av.sm{width:20px;height:20px;font-size:12px}
.mx-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:1.2;flex:1;min-width:0}
/* 姓名列头像与名字竖向排列更省宽：头像在左、名字单行（超出省略号） */
.mx-table tbody th{flex-wrap:nowrap}

/* ================= v13：空闲矩阵视觉美化（圆角 + 柔和卡片风） ================= */
/* 手机上大片「空」格子太扎眼：空闲格改成近白的薄荷底 + 极淡字，
   有事的格子保留彩底但加渐变和更圆的角，整体从"表格"变"卡片"。 */
.mx-table{border-spacing:4px}
.mx-table td.mx{height:28px;border-radius:var(--r-md);
  box-shadow:inset 0 -1.5px 0 rgba(15,23,42,.05);transition:transform .12s,box-shadow .12s,filter .12s}
.mx-table td.mx:hover{transform:translateY(-1px);box-shadow:0 4px 10px rgba(15,23,42,.14);opacity:1}
/* 空闲：近白薄荷，字几乎隐入背景，铺满屏时是一面安静的底色而不是绿墙 */
.mx-table td.k-free{background:linear-gradient(180deg,#f6fcf8,#edf7f1);color:#b7d4c3;font-weight:500}
/* 有事：柔和渐变彩底 */
.mx-table td.k-course{background:linear-gradient(180deg,#e9f1fe,#dde9fc);color:#2563eb}
.mx-table td.k-meet{background:linear-gradient(180deg,#f1ecfd,#e7defb);color:#7c3aed}
.mx-table td.k-leave{background:linear-gradient(180deg,#feecec,#fde0e0);color:#dc2626}
.mx-table td.k-plan,.mx-table td.k-task{background:linear-gradient(180deg,#fdf3e3,#fbeeda);color:#d97706}
/* 部分占用的白点线在新底色上仍清晰 */
.mx-table td.mx.part::after{bottom:3px;height:2.5px;background:rgba(255,255,255,.9)}
[data-theme="dark"] .mx-table td.mx{box-shadow:inset 0 -1.5px 0 rgba(0,0,0,.25)}
[data-theme="dark"] .mx-table td.k-free{background:linear-gradient(180deg,#14211a,#101b15);color:#4e7a60}
[data-theme="dark"] .mx-table td.k-course{background:linear-gradient(180deg,#16263f,#131f34);color:#7ea8f4}
[data-theme="dark"] .mx-table td.k-meet{background:linear-gradient(180deg,#241c3c,#1e1731);color:#b49af0}
[data-theme="dark"] .mx-table td.k-leave{background:linear-gradient(180deg,#351c1e,#2b1517);color:#f08a8a}
[data-theme="dark"] .mx-table td.k-plan,
[data-theme="dark"] .mx-table td.k-task{background:linear-gradient(180deg,#302518,#281e13);color:#eab060}
/* 图例：小方块 → 圆角胶囊标签 */
.mx-legend{gap:4px 8px;margin-top:8px}
.mx-legend span{display:inline-flex;align-items:center;background:var(--bg);
  border:1px solid var(--line);border-radius:var(--r-md);padding:3px 10px 3px 8px;font-size:12px;line-height:1.4}
.mx-legend i{width:10px;height:10px;border-radius:50%;margin-right:5px;vertical-align:0;
  box-shadow:inset 0 0 0 1px rgba(15,23,42,.06)}
/* 共同空闲条：胶囊轨道 + 胶囊空闲段 */
.fbar-track{height:28px;border-radius:var(--r-md)}
.fbar-free{border-radius:var(--r-md);box-shadow:0 1px 4px rgba(63,174,118,.35)}
.fbar-busy{border-left:0;border-right:0}
/* 空闲时段卡片：圆角卡片 + 轻阴影，按钮改胶囊 */
.rec{margin-top:16px;padding-top:14px}
.rec-item{border-radius:var(--r-md);padding:9px 13px;background:linear-gradient(180deg,#ffffff,#fbfcfe);
  box-shadow:0 1px 4px rgba(15,23,42,.06);border:1px solid var(--line2)}
.rec-item .btn{border-radius:var(--r-md)}
.rec-dur{border-radius:var(--r-md);padding:2px 10px}
[data-theme="dark"] .rec-item{background:linear-gradient(180deg,#1c2333,#181e2c)}
/* 姓名列（sticky 冻结）：纯色底 + 右侧阴影悬浮层次，去掉右侧圆角/负边距。
   ⚠ 关键：th 是 display:flex，在 border-collapse:separate + border-spacing 下
   **不会**被拉伸到行高（实测 tr=28px 而 th 仅 18px → 行间露出 14px 空隙）。
   align-self:stretch / height:100% 对表格单元格的 flex 盒都无效，必须给 th 一个
   显式高度 = td 高度（28px），再靠上下 2px 补缝吃掉 border-spacing:4px，
   行与行才会连成一整条。 */
.mx-table thead th:first-child{position:sticky;left:0;z-index:4;background:var(--card);margin-left:0;
  border-radius:0;box-shadow:6px 0 12px -6px rgba(15,23,42,.18)}
.mx-table tbody th{position:sticky;left:0;z-index:3;background:var(--card);margin-left:0;
  border-radius:0;box-sizing:border-box;height:28px;min-height:28px;
  box-shadow:6px 0 12px -6px rgba(15,23,42,.18),0 -2px 0 2px var(--card),0 2px 0 2px var(--card)}
[data-theme="dark"] .mx-table thead th:first-child,
[data-theme="dark"] .mx-table tbody th{background:#1a2030;
  box-shadow:6px 0 12px -6px rgba(0,0,0,.55),0 -2px 0 2px var(--card),0 2px 0 2px var(--card)}
/* 名字与头像：圆标缩小 + 降饱和，横向排列垂直居中 */
.mx-table tbody th{align-items:center;padding-right:8px;gap:7px}
.mx-table tbody th .av.sm{width:18px;height:18px;font-size:12px;filter:saturate(.72)}
.mx-name{font-weight:500}
/* 表头：浅灰底区分内容行，节次数字加粗 + 时间段小字 */
.mx-table thead th{background:#f1f4fa;border-radius:var(--r-md);padding:7px 2px 6px;color:var(--tx2);font-weight:500}
[data-theme="dark"] .mx-table thead th{background:#1b2233}
/* 行悬浮：整行（含左侧成员列）轻微高亮；高亮行的上下补缝色也跟随，保持连续 */
.mx-table tbody tr:hover th{background:var(--pri-soft);
  box-shadow:6px 0 12px -6px rgba(15,23,42,.18),0 -2px 0 2px var(--pri-soft),0 2px 0 2px var(--pri-soft)}
.mx-table tbody tr:hover td.mx{filter:brightness(1.05) saturate(1.05)}
[data-theme="dark"] .mx-table tbody tr:hover th{background:var(--pri-soft);
  box-shadow:6px 0 12px -6px rgba(0,0,0,.55),0 -2px 0 2px var(--pri-soft),0 2px 0 2px var(--pri-soft)}

/* ================= v17：成员列配色统一 + 名片可点 ================= */
/* 用户反馈：成员列一块块白底浮在灰底上"用一样颜色做垫层不好看"。
   根因：成员格用 var(--card) 纯白，而它所在的面板区是 --bg 冷灰调，
   白块与冷灰垫层撞色，看起来像没对齐的补丁。
   修法：整条成员列（表头 + 所有行）统一改用表头同款的浅灰 #f1f4fa，
   配合 v16 的补缝 → 左侧是一条颜色一致的"冻结轨道"，与右侧格子形成
   "轨道 vs 内容"的明确层次，而不是一堆白色补丁。 */
.mx-table thead th:first-child,
.mx-table tbody th{background:#f1f4fa}
[data-theme="dark"] .mx-table thead th:first-child,
[data-theme="dark"] .mx-table tbody th{background:#1b2233}
/* 补缝色也要跟着换成 #f1f4fa，否则行间又露出白缝（这是同一个坑） */
.mx-table tbody th{box-shadow:6px 0 12px -6px rgba(15,23,42,.14),0 -2px 0 2px #f1f4fa,0 2px 0 2px #f1f4fa}
[data-theme="dark"] .mx-table tbody th{box-shadow:6px 0 12px -6px rgba(0,0,0,.5),0 -2px 0 2px #1b2233,0 2px 0 2px #1b2233}
/* 悬浮：整条高亮，补缝同步（不然高亮行会被白缝割开） */
.mx-table tbody tr:hover th{background:var(--pri-soft);
  box-shadow:6px 0 12px -6px rgba(15,23,42,.16),0 -2px 0 2px var(--pri-soft),0 2px 0 2px var(--pri-soft)}
[data-theme="dark"] .mx-table tbody tr:hover th{background:var(--pri-soft);
  box-shadow:6px 0 12px -6px rgba(0,0,0,.5),0 -2px 0 2px var(--pri-soft),0 2px 0 2px var(--pri-soft)}
/* 成员格可点（打开名片）：给一点可点击的暗示 */
.mx-table tbody th.mx-mem{cursor:pointer;transition:background .14s}
.mx-table tbody th.mx-mem:active{background:#e4eaf7}
[data-theme="dark"] .mx-table tbody th.mx-mem:active{background:#222c45}
/* 有联系方式的成员，名字后加一枚极淡的小圆点，暗示"可以点开看名片" */
.mx-table tbody th.mx-mem .mx-name{position:relative}
.mx-table tbody th.mx-mem.has-cc .mx-name::after{content:'';display:inline-block;
  width:5px;height:5px;border-radius:50%;background:var(--pri);opacity:.55;
  margin-left:4px;vertical-align:1px}
/* 「按时间找人」的成员胶囊：可点提示 + 悬浮反馈 */
.fp-chip[data-uid]{cursor:pointer;transition:.14s}
.fp-chip[data-uid]:hover{border-color:var(--pri);background:var(--card)}
.fp-chip.has-cc::after{content:'名片';font-style:normal;font-size:12px;color:var(--pri);
  background:var(--pri-soft);border-radius:var(--r-md);padding:1px 6px;margin-left:2px;line-height:1.5}

/* ---- 名片弹窗 ---- */
.mcard{display:flex;flex-direction:column;gap:14px}
.mcard-top{display:flex;align-items:center;gap:14px}
.mcard-top .av.lg{width:62px;height:62px;font-size:24px;flex:none;
  box-shadow:0 4px 14px rgba(20,30,60,.16)}
.mcard-name{font-size:16px;font-weight:500;display:block}
.mcard-top .meta{margin-top:3px}
.mcard-self{margin-top:5px;font-size:12px;color:var(--pri);background:var(--pri-soft);
  display:inline-block;border-radius:var(--r-md);padding:2px 9px}
.cc-list{display:flex;flex-direction:column;gap:7px}
.cc-row{display:flex;align-items:center;gap:9px;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-md);padding:9px 11px}
[data-theme="dark"] .cc-row{background:var(--slot-bg);border-color:var(--line2)}
.cc-ic{font-size:14px;flex:none;line-height:1}
.cc-lb{font-size:12px;color:var(--tx3);flex:none;width:44px}
.cc-val{flex:1;font-size:13px;font-weight:500;word-break:break-all;color:var(--tx)}
.cc-link{color:var(--pri);text-decoration:none}
.cc-copy{flex:none;border:1px solid var(--line);background:var(--card);color:var(--tx2);
  font-size:12px;border-radius:var(--r-md);padding:3px 11px;cursor:pointer;transition:.14s}
.cc-copy:hover{border-color:var(--pri);color:var(--pri);background:var(--pri-soft)}
[data-theme="dark"] .cc-copy{background:var(--slot-bg);border-color:var(--line)}
.cc-note{display:flex;gap:9px;align-items:flex-start;font-size:13px;color:var(--tx2);
  background:var(--bg);border:1px dashed var(--line);border-radius:var(--r-md);padding:9px 11px;line-height:1.6}
[data-theme="dark"] .cc-note{background:var(--slot-bg)}
.mcard-hide{display:flex;gap:11px;align-items:flex-start;background:var(--bg);
  border:1px solid var(--line);border-radius:var(--r-md);padding:14px}
[data-theme="dark"] .mcard-hide{background:var(--slot-bg);border-color:var(--line2)}
.mcard-hide>span{font-size:20px;line-height:1.2}
.mcard-hide .hint{margin:3px 0 0}
.mcard .empty{text-align:center;padding:10px 0;color:var(--tx3);font-size:13px}
/* 个人中心里的「谁能看到」三选一 */
.pc-sec .pick{display:flex;gap:6px;margin-top:6px}
.pc-sec .pick button{flex:1;border:1px solid var(--line);background:var(--card);color:var(--tx2);
  font-size:13px;padding:8px 6px;border-radius:var(--r-md);cursor:pointer;transition:.14s}
.pc-sec .pick button.on{background:var(--pri);border-color:var(--pri);color:#fff;font-weight:500}
[data-theme="dark"] .pc-sec .pick button{background:var(--slot-bg);border-color:var(--line)}
/* 成员管理页：头像可点 + 名片角标 */
.mrow-av{cursor:pointer;flex:none;border-radius:50%;transition:.14s;display:inline-flex}
.mrow-av:hover{transform:scale(1.06);box-shadow:0 0 0 3px var(--pri-soft)}
.cc-badge{font-size:12px;color:var(--pri);background:var(--pri-soft);border-radius:var(--r-md);
  padding:1px 6px;vertical-align:2px;font-weight:500}
[data-theme="dark"] .mx-table tbody tr:hover th{background:var(--pri-soft);
  box-shadow:6px 0 12px -6px rgba(0,0,0,.55),0 -2px 0 2px var(--pri-soft),0 2px 0 2px var(--pri-soft)}
/* 外层容器平滑滚动 */
.table-wrap{scroll-behavior:smooth}
/* 图例提示文字 */
.mx-legend em{font-style:normal;color:var(--tx3);font-size:12px;opacity:.85}

/* ================= v18：自动更新间隔选择器 ================= */
/* 「数据 → 自动更新」里的间隔胶囊：复用 .pick 的观感，但选项有 5 个，
   手机上要能自动换行、不挤成一坨 */
#pollPick{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
#pollPick button{border:1px solid var(--line);background:var(--card);color:var(--tx2);
  font-size:13px;padding:7px 14px;border-radius:var(--r-md);cursor:pointer;transition:.14s;flex:none}
#pollPick button:hover{border-color:var(--pri);color:var(--pri);background:var(--pri-soft)}
#pollPick button.on{background:var(--pri);border-color:var(--pri);color:#fff;font-weight:500}
[data-theme="dark"] #pollPick button{background:var(--slot-bg);border-color:var(--line)}
[data-theme="dark"] #pollPick button.on{background:var(--pri);color:#fff}

/* ================= v19：离线提示条 ================= */
/* 断网时贴在页面最顶部的一条细提示（不遮挡内容、不挡 tabbar）。
   用 fixed + 安全区适配（iPhone 刘海）；靠 body.offline 控制内容下移。 */
.offline-bar{position:fixed;left:0;right:0;top:0;z-index:60;
  display:flex;align-items:center;gap:8px;
  padding:calc(6px + env(safe-area-inset-top)) 12px 6px;
  background:linear-gradient(90deg,#5b6478,#475066);color:#fff;
  font-size:12px;line-height:1.35;
  box-shadow:0 2px 10px rgba(0,0,0,.18)}
.offline-bar .ob-ico{flex:none;font-size:13px;filter:grayscale(.2)}
.offline-bar .ob-txt{flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.offline-bar .ob-retry{flex:none;border:1px solid rgba(255,255,255,.55);background:rgba(255,255,255,.14);
  color:#fff;font-size:12px;padding:3px 10px;border-radius:var(--r-md);cursor:pointer;transition:.14s}
.offline-bar .ob-retry:hover{background:rgba(255,255,255,.28)}
/* 有离线条时把页面主体往下挪，避免盖住页头
   ★ v100：padding-top / top 不再写死，改读 JS 实测出来的 --ob-h。
     为什么必须实测：手机端（≤760px）`.ob-txt{white-space:normal}` 允许换行，
     「离线模式·显示 49 分钟前刷新数据」这种长文案会折成**两行**，
     条高从桌面 ~34px 涨到 ~52px；而这里原来写死 44px
     ⇒ 下移量不够，顶部那排「月视图/网格/时间轴/课表」切换按钮正好压在条下面，
     按钮文字被盖掉上半截（用户截图：文字"漏出来"）。
     写死数字的病根：文案长度、字体大小、系统字号、safe-area、折叠屏高度全都会变，
     任何一种都会让写死值再次失配。变量由 renderOfflineBar 末尾 syncOfflineBarH() 写。
     34px 只是兜底默认值（桌面端单行时的真实高度），首帧渲染前生效。 */
body.offline #app{padding-top:var(--ob-h,34px)}
body.offline .topbar,body.offline header.top{top:var(--ob-h,34px)}
@media (max-width:760px){
  .offline-bar .ob-txt{white-space:normal;font-size:12px}
  /* ⚠ 这里**故意不再覆盖 padding-top** —— 覆盖值又变回写死的数字，
     正是 v100 要消灭的那类硬编码。手机端的值同样由 JS 实测后写进 --ob-h。 */
}
[data-theme="dark"] .offline-bar{background:linear-gradient(90deg,#3a4152,#2b3140)}

/* ===== v46 =====
   1) 社区中心「发起抽奖 / 发起投票」在手机端被 .btn 全局放大（min-height:38px + 大内边距），
      两个蓝色大块头叠在聊天区非常突兀（用户截图反馈"图标太大不好看"）→ 压成紧凑胶囊小按钮。 */
@media (max-width:760px){
  .acts #addVote,.acts #addDraw,#addVote,#addDraw{
    min-height:0;height:auto;padding:4px 11px;font-size:13px;line-height:1.5;
    border-radius:var(--r-md);display:inline-block;width:auto;flex:0 0 auto;margin:2px 4px 2px 0;
  }
}
/* 2) 服务器版本不一致提醒条：黄色系（与离线条的红色区分），文案更醒目 */
.srv-bar{background:linear-gradient(90deg,#f5b942,#e8a020)}
.srv-bar .ob-txt{color:#4a3000;font-weight:500}
.srv-bar .ob-retry{border-color:rgba(0,0,0,.35);background:rgba(255,255,255,.35);color:#4a3000}
[data-theme="dark"] .srv-bar{background:linear-gradient(90deg,#6b5416,#574310)}
[data-theme="dark"] .srv-bar .ob-txt{color:#ffe9b0}

/* ---- 离线 / 同步 页 ---- */
.off-state{display:flex;align-items:center;gap:10px;padding:12px 14px;border-radius:var(--r-md);background:var(--slot-bg);border:1px solid var(--line)}
.off-state.is-on{background:linear-gradient(180deg,#eaf7ef,#e2f3e9);border-color:#bfe3cd}
.off-state.is-off{background:linear-gradient(180deg,#f5f1e6,#efe9da);border-color:#e0d5ba}
.off-state .os-dot{flex:none;width:10px;height:10px;border-radius:50%;box-shadow:0 0 0 4px rgba(0,0,0,.05)}
.off-state.is-on .os-dot{background:#22a06b}
.off-state.is-off .os-dot{background:#d99b2b}
.off-state .os-main{display:flex;flex-direction:column;gap:2px;min-width:0}
.off-state .os-main b{font-size:14px}
.off-state .os-main span{font-size:12px;color:var(--tx2)}
.off-when{display:flex;align-items:baseline;gap:8px;font-size:14px;font-weight:500;color:var(--tx)}
.off-when em{font-style:normal;font-size:12px;font-weight:400;color:var(--tx2)}

.off-list{display:flex;flex-direction:column;gap:8px}
.off-row{display:flex;gap:10px;align-items:flex-start;padding:9px 11px;border-radius:var(--r-md);background:var(--slot-bg);border:1px solid var(--line)}
.off-row .off-ico{flex:none;font-size:14px;line-height:1.4}
.off-row>div{display:flex;flex-direction:column;gap:2px;min-width:0}
.off-row b{font-size:13px}
.off-row span{font-size:12px;color:var(--tx2);line-height:1.45}

.off-status{display:flex;flex-direction:column;gap:0;border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden}
.off-srow{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px;font-size:13px;background:var(--slot-bg)}
.off-srow+.off-srow{border-top:1px solid var(--line)}
.off-srow b{font-size:12px;font-weight:500;flex:none}
.off-srow b.ok{color:#1e8a5a}
.off-srow b.warn{color:#b7791f}

[data-theme="dark"] .off-state.is-on{background:linear-gradient(180deg,#1b2b23,#182620);border-color:#2c4a3a}
[data-theme="dark"] .off-state.is-off{background:linear-gradient(180deg,#2b2718,#252214);border-color:#4a4326}
[data-theme="dark"] .off-srow b.ok{color:#4bd08b}
[data-theme="dark"] .off-srow b.warn{color:#e0b25a}

/* ================= v23：「切换服务器」搬到「离线 / 同步」页 =================
   原来是个 position:fixed 的悬浮圆按钮，一直盖在右下角内容上
   （用户明确要求搬走）。现在作为「离线 / 同步」页里的一个普通区块。 */
.off-srv{margin-top:14px;padding:11px 12px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--slot-bg)}
.off-srv .osr-head{font-size:12px;color:var(--tx2);margin-bottom:5px}
.off-srv .osr-url{font-size:13px;color:var(--tx);word-break:break-all;line-height:1.5;
  font-family:ui-monospace,Consolas,monospace}

/* ================= v22：连不上服务器的「死页面」改造 =================
   以前 boot() 里整页替换成一屏死字，没有按钮、没有出路 →
   用户「不小心进来」就卡住，只能杀进程重开。
   现在给三个出路：重试 / 返回服务器列表 / 看技术细节，并说清原因。 */
.fatal{max-width:460px;margin:0 auto;padding:56px 22px 40px;text-align:center;font-size:14px;color:var(--tx)}
.fatal-ico{font-size:44px;line-height:1;margin-bottom:12px}
.fatal h2{margin:0 0 10px;font-size:20px}
.fatal-why{margin:0 0 10px;color:var(--tx);font-size:14px;line-height:1.65}
.fatal-addr{margin:0 0 12px;color:var(--tx2);font-size:13px;word-break:break-all}
.fatal-addr code{background:#f1f4fa;border-radius:var(--r-md);padding:3px 8px;font-size:12px}
.fatal-fix{margin:14px 0 22px;padding:12px 14px;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-md);text-align:left;color:var(--tx2);font-size:13px;line-height:1.85}
.fatal-fix b{color:var(--tx)}
.fatal-acts{display:flex;flex-direction:column;gap:9px}
.fatal-acts .btn{width:100%}
.fatal-more{margin-top:22px;text-align:left;font-size:12px;color:var(--tx3)}
.fatal-more summary{cursor:pointer;color:var(--tx3);padding:4px 0}
.fatal-more code{display:block;margin-top:6px;background:#f1f4fa;border-radius:var(--r-md);padding:9px 11px;
  font-size:12px;color:var(--tx2);word-break:break-all;line-height:1.6;white-space:pre-wrap}
[data-theme="dark"] .fatal-fix{background:var(--card);border-color:var(--line)}
[data-theme="dark"] .fatal-addr code,[data-theme="dark"] .fatal-more code{background:#171d2b;color:var(--tx2)}

/* ============ v29：美化 —— 弹窗关闭按钮 ============ */
.modal-drag{position:relative}
.modal-close{position:absolute;top:10px;right:12px;width:30px;height:30px;border:none;background:var(--pri-soft);
  color:var(--pri);border-radius:50%;font-size:14px;line-height:30px;cursor:pointer;transition:transform .15s ease,background .15s ease;
  display:flex;align-items:center;justify-content:center}
.modal-close:hover{transform:rotate(90deg) scale(1.08);background:var(--pri);color:#fff}
.modal-close:active{transform:scale(.92)}

/* ============ v29：美化 —— 页面切换动画 ============ */
@keyframes viewIn{from{opacity:0;transform:translateY(10px) scale(.995)}to{opacity:1;transform:none}}
.view.view-in{animation:viewIn .26s cubic-bezier(.22,.61,.36,1)}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
/* v61 优化：入场动画只在「切换视图」时播放（#main 会临时挂 .anim-in）。
   以前选择器直接命中 .panel/.card/.mrow/.stat-card —— 而每次轮询数据变化都会重建
   innerHTML（新元素）→ 整页重放入场动画，表现为「每 15 秒数据一变整页闪一下」。
   现在拆成两步：真正切页时才加 .anim-in（见 render()），刷新重建不再触发。 */
#main.anim-in .panel,#main.anim-in .card,#main.anim-in .mrow,#main.anim-in .stat-card{animation:fadeUp .3s ease both}

/* 导航/标签图标动效：选中时轻轻弹一下 */
@keyframes iconPop{0%{transform:scale(1)}40%{transform:scale(1.22) translateY(-2px)}100%{transform:scale(1)}}
.navbtn.active{position:relative}
.navbtn.active::after{content:"";position:absolute;left:50%;bottom:4px;width:18px;height:3px;border-radius:var(--r-md);background:var(--pri);transform:translateX(-50%)}
.tabbtn.active .ti{animation:iconPop .45s ease;color:var(--pri)}
.tabbtn.active{color:var(--pri)}
.navbtn.active{color:var(--pri);font-weight:500}

/* Logo 入场呼吸光 */
@keyframes logoPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.06)}}
.brand .logo img{animation:logoPulse 3.2s ease-in-out infinite}
.auth-logo img{animation:logoPulse 3.2s ease-in-out infinite}

/* 卡片悬浮微动 */
.card:hover,.mrow:hover{transform:translateY(-1px);transition:transform .15s ease,box-shadow .15s ease}

/* ============ v29：社区中心 ============ */
.comm-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.comm-tabs .ctab{padding:7px 13px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--card);
  color:var(--tx2);font-size:13px;cursor:pointer;transition:all .15s ease}
.comm-tabs .ctab.on{background:var(--pri);color:#fff;border-color:var(--pri)}
.board-item{border:1px solid var(--line);border-radius:var(--r-md);padding:12px 14px;margin-bottom:10px;background:var(--card)}
.board-item.pin{box-shadow:0 0 0 2px var(--pri-soft)}
.board-item h4{margin:0 0 4px;font-size:14px}
.board-item .meta{font-size:12px;color:var(--tx3);margin-bottom:6px}
.board-item .body{font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word}
.board-imgs{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.board-imgs img{width:84px;height:84px;object-fit:cover;border-radius:var(--r-md);border:1px solid var(--line)}
.chat-box{border:1px solid var(--line);border-radius:var(--r-md);background:var(--card);padding:10px;max-height:52vh;overflow:auto}
.chat-msg{margin:6px 0;display:flex;gap:8px;align-items:flex-start}
.chat-msg .av{width:30px;height:30px;border-radius:50%;flex:0 0 auto;display:flex;align-items:center;justify-content:center;color:#fff;font-size:12px}
.chat-msg .bub{background:var(--pri-soft);border-radius:var(--r-md);padding:7px 10px;font-size:13px;line-height:1.5;max-width:78%}
.chat-msg.me{flex-direction:row-reverse}
.chat-msg.me .bub{background:var(--pri);color:#fff}
.chat-input{display:flex;gap:8px;margin-top:8px}
.chat-input input{flex:1}
.vote-item{border:1px solid var(--line);border-radius:var(--r-md);padding:12px 14px;margin-bottom:10px;background:var(--card)}
.vote-bar{height:8px;border-radius:var(--r-md);background:var(--pri-soft);overflow:hidden;margin:3px 0 8px}
.vote-bar>i{display:block;height:100%;background:var(--pri)}
.pill{display:inline-block;padding:2px 9px;border-radius:var(--r-md);font-size:12px;background:var(--pri-soft);color:var(--pri);margin-left:6px}
.pill.done{background:#e7f7ee;color:var(--free)}
.code-box{font-family:ui-monospace,Consolas,monospace;font-size:16px;letter-spacing:2px;background:var(--pri-soft);
  color:var(--pri);padding:10px 14px;border-radius:var(--r-md);text-align:center;font-weight:500}
.tiny-sel{font-size:12px;color:var(--tx2)}
textarea.big{width:100%;min-height:90px}
/* v61 体验：长页面「回到顶部」浮标 */
.back-top{position:fixed;right:16px;bottom:78px;z-index:900;width:42px;height:42px;border-radius:50%;
  border:1px solid var(--line);background:var(--card);color:var(--tx);font-size:16px;line-height:1;
  cursor:pointer;box-shadow:var(--sh);opacity:.92;transition:opacity .15s ease,transform .15s ease;padding:0}
.back-top:hover{opacity:1;transform:translateY(-2px)}
.back-top.hidden{display:none}
@media (max-width:760px){.back-top{bottom:calc(76px + env(safe-area-inset-bottom,0px) + var(--android-nav-h,0px))}}
@media (prefers-reduced-motion: reduce){.view.view-in,#main.anim-in .panel,#main.anim-in .card,#main.anim-in .mrow,#main.anim-in .stat-card,.card,.mrow,.tabbtn.active .ti,.brand .logo img,.auth-logo img{animation:none}}

/* ===== v29：空闲矩阵分页 + 自定义「允许几人忙」（用户要求：别一次显示太多成员） ===== */
.mx-wrap{max-height:62vh;overflow:auto;-webkit-overflow-scrolling:touch}
.mx-wrap thead th{position:sticky;top:0;z-index:3;background:var(--card)}
.mx-pager{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:10px 0 2px;font-size:13px;color:var(--tx2)}
.mx-pager .mx-pg{color:var(--tx);font-weight:500}
.mx-pager .mx-per{margin-left:auto;display:inline-flex;align-items:center;gap:6px}
.mx-pager select{height:32px;padding:0 6px;border-radius:var(--r-md);border:1px solid var(--line);background:var(--bg);color:var(--tx)}
.tol-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.tol-custom{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--tx2)}
.tol-custom input{width:66px;height:30px;text-align:center;padding:0 6px}

/* ===== v29：组织架构层级（社团 → 部门） ===== */
.org-top{border:1px dashed var(--line);border-radius:var(--r-md);padding:10px 12px;margin-bottom:12px;background:var(--bg)}
.org-lv{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.org-lv.sub{margin-top:8px;padding-top:8px;border-top:1px dashed var(--line)}
.org-tag{font-size:12px;color:var(--tx2);background:var(--card);border:1px solid var(--line);border-radius:var(--r-md);padding:2px 8px}
.org-name{font-size:16px}

/* ===== v40：图标美化（按钮/弹窗标题里开头的 emoji → 渐变小徽标 + 悬停弹跳）
   ★ 只作用于 button/.modal-drag 的直接子节点，不影响 .wx-hero .ico 等已有用途 ===== */
button>.ico,.modal-drag>.ico{display:inline-flex;align-items:center;justify-content:center;
  width:1.5em;height:1.5em;margin-right:.35em;border-radius:.5em;font-size:.92em;line-height:1;flex:none;
  background:linear-gradient(135deg,rgba(99,102,241,.16),rgba(14,165,233,.16));
  box-shadow:inset 0 0 0 1px rgba(99,102,241,.14);
  vertical-align:-.32em;transition:transform .22s ease,box-shadow .22s ease}
button:hover>.ico,button:focus-visible>.ico{transform:translateY(-1px) scale(1.07)}
button:active>.ico{transform:scale(.94)}
.btn.primary>.ico{background:linear-gradient(135deg,rgba(255,255,255,.28),rgba(255,255,255,.14));box-shadow:none}
@media (prefers-reduced-motion: reduce){button>.ico{transition:none}}

/* ===== v40：设置部门弹窗（先选社团 → 再勾部门 + 搜索） ===== */
.md-groupttl{font-size:12px;font-weight:500;color:var(--tx2);margin:10px 0 6px;letter-spacing:.4px}
.md-tnlist{display:flex;flex-direction:column;gap:6px;max-height:180px;overflow:auto}
.md-tn{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--line);
  border-radius:var(--r-md);cursor:pointer;background:var(--card)}
.md-tn:hover{border-color:var(--pri)}
.md-tn.on{border-color:var(--pri);background:var(--pri-soft)}
.md-radio{width:14px;height:14px;border-radius:50%;border:2px solid var(--line);flex:none;position:relative}
.md-tn.on .md-radio{border-color:var(--pri)}
.md-tn.on .md-radio::after{content:"";position:absolute;inset:2px;border-radius:50%;background:var(--pri)}
.md-tn b{font-size:13px}
.md-tn .meta{margin-left:auto;font-size:12px;color:var(--tx2)}
.md-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.md-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:3px 8px;border-radius:var(--r-md);
  background:var(--pri-soft);color:var(--pri);border:1px solid var(--line)}
.md-chip i{font-style:normal;color:var(--tx2)}
.md-chip button{border:0;background:transparent;cursor:pointer;color:var(--tx2);font-size:12px;padding:0 2px}
.md-chip button:hover{color:var(--pri)}

/* ============ v41：底部任务栏动态图标（房子挂今天日期 + 沙漏流动/翻转） ============ */
.tabbtn .ti.ti-house,
.tabbtn .ti.ti-hourglass{font-size:0;line-height:1}
.tabbtn .ti svg{width:22px;height:22px;display:block}

/* —— 房子：小日历显示今天的号 —— */
.ti-house-svg .ti-house-body{fill:none;stroke:var(--tx);stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.ti-house-svg .ti-house-wall{fill:none;stroke:var(--tx);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.ti-house-svg .ti-house-door{fill:none;stroke:var(--tx);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.tabbtn.active .ti-house-svg .ti-house-body,
.tabbtn.active .ti-house-svg .ti-house-wall,
.tabbtn.active .ti-house-svg .ti-house-door{stroke:var(--pri)}
.ti-house-cal{fill:var(--card);stroke:var(--pri);stroke-width:1.1}
.ti-house-cal-top{stroke:var(--pri);stroke-width:1.3;stroke-linecap:round;fill:none}
.ti-house-day{font-size:5.6px;font-weight:800;fill:var(--pri);font-family:inherit}
/* 房子选中时：日历轻轻抖动一下（像被开门带动的挂历） */
@keyframes houseCalSwing{0%,100%{transform:rotate(0)}25%{transform:rotate(4deg)}50%{transform:rotate(-3deg)}75%{transform:rotate(2deg)}}
.tabbtn.active .ti-house-cal{transform-origin:16px 1.6px;animation:houseCalSwing .9s ease}

/* —— 沙漏：沙子流动 + 周期翻转 —— */
.ti-hourglass-svg .ti-hg-frame{fill:none;stroke:var(--tx);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
.tabbtn.active .ti-hourglass-svg .ti-hg-frame{stroke:var(--pri)}
.ti-hg-sand-top{fill:var(--pri);opacity:.92}
.ti-hg-sand-bot{fill:var(--pri);opacity:.55}
.ti-hg-line{fill:none;stroke:var(--tx);stroke-width:1.2;stroke-linecap:round}
.tabbtn.active .ti-hg-line{stroke:var(--pri)}
/* 流动：顶沙渐消、底沙渐满 */
@keyframes hgFlowTop{0%,100%{transform:scaleY(1)}50%{transform:scaleY(.22)}}
@keyframes hgFlowBot{0%,100%{transform:scaleY(.2)}50%{transform:scaleY(1)}}
.ti-hourglass[data-live="1"] .ti-hg-sand-top{transform-origin:top center;animation:hgFlowTop 3.4s ease-in-out infinite}
.ti-hourglass[data-live="1"] .ti-hg-sand-bot{transform-origin:bottom center;animation:hgFlowBot 3.4s ease-in-out infinite}
/* 翻转：整体上下翻转，模拟沙漏倒置 */
@keyframes hgFlip{0%,46%{transform:rotate(0)}50%,96%{transform:rotate(180deg)}100%{transform:rotate(360deg)}}
.ti-hourglass[data-live="1"] .ti-hourglass-svg{animation:hgFlip 6.8s cubic-bezier(.45,.05,.55,.95) infinite}

/* 减少动效：沙漏静止（房子日历保留，因为它是信息不是动效） */
@media (prefers-reduced-motion: reduce){
  .ti-hourglass[data-live="1"] .ti-hg-sand-top,
  .ti-hourglass[data-live="1"] .ti-hg-sand-bot,
  .ti-hourglass[data-live="1"] .ti-hourglass-svg,
  .tabbtn.active .ti-house-cal{animation:none}
}

/* ============ v77.4 T2：五个 tab 图标轻量激活动效（≤500ms，仅切换瞬间触发） ============ */
/* ① 概览：房子小日历里的日期数字弹一下 */
@keyframes houseDayBounce{0%{transform:scale(1)}35%{transform:scale(1.5) translateY(-1.6px)}70%{transform:scale(.88)}100%{transform:scale(1)}}
.tabbtn.active .ti-house-day{transform-box:fill-box;transform-origin:center;animation:houseDayBounce .45s ease}

/* ② 日历：顶头条闪一下 + 整图标轻轻一跳 */
@keyframes calBandFlash{0%,100%{opacity:1}50%{opacity:.5}}
@keyframes calJump{0%{transform:translateY(0)}40%{transform:translateY(-2.4px)}100%{transform:translateY(0)}}
.tabbtn.active .ti-cal-band{animation:calBandFlash .45s ease}
.tabbtn.active .ti-cal-svg{animation:calJump .45s ease}

/* ③ 空闲：沙漏选中时整体快速翻一圈（live 时 svg 自带 6.8s 循环翻转，这里翻的是外层 span，互不打架） */
@keyframes hgFlipOnce{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
.tabbtn.active .ti-hourglass{animation:hgFlipOnce .5s cubic-bezier(.45,.05,.55,.95)}

/* ④ 任务：对勾按笔画一笔画出（stroke-dashoffset 从满到 0） */
.ti-check-svg{width:22px;height:22px;display:block}
.ti-check-stroke{fill:none;stroke:var(--tx);stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:22;stroke-dashoffset:0}
.tabbtn.active .ti-check-stroke{stroke:var(--pri);animation:checkDraw .5s ease forwards}
@keyframes checkDraw{0%{stroke-dashoffset:22}62%{stroke-dashoffset:0}100%{stroke-dashoffset:0}}

/* ⑤ 更多：三线展开又合拢（上/下两线外移、中线缩短） */
.ti-menu-svg{width:22px;height:22px;display:block}
.ti-menu-svg path{fill:none;stroke:var(--tx);stroke-width:2;stroke-linecap:round}
.tabbtn.active .ti-menu-svg path{stroke:var(--pri)}
@keyframes menuSpreadTop{0%,100%{transform:translateY(0)}40%{transform:translateY(-2.6px)}}
@keyframes menuSpreadMid{0%,100%{transform:scaleX(1)}40%{transform:scaleX(.7)}}
@keyframes menuSpreadBot{0%,100%{transform:translateY(0)}40%{transform:translateY(2.6px)}}
.tabbtn.active .ti-menu-l1{animation:menuSpreadTop .5s ease}
.tabbtn.active .ti-menu-l2{transform-box:fill-box;transform-origin:center;animation:menuSpreadMid .5s ease}
.tabbtn.active .ti-menu-l3{animation:menuSpreadBot .5s ease}

/* 减少动效：以上激活动画全部静默 */
@media (prefers-reduced-motion: reduce){
  .tabbtn.active .ti-house-day,
  .tabbtn.active .ti-cal-band,
  .tabbtn.active .ti-cal-svg,
  .tabbtn.active .ti-hourglass,
  .tabbtn.active .ti-check-stroke,
  .tabbtn.active .ti-menu-l1,
  .tabbtn.active .ti-menu-l2,
  .tabbtn.active .ti-menu-l3{animation:none}
}

/* ============ v44：游客（guest）门禁样式 ============ */
/* 受限入口：置灰 + 锁图标（点击弹统一引导浮层，见 app.js showGuestGuide） */
.navbtn.locked{opacity:.42}
.tabbtn.locked{opacity:.42}
.navbtn .lock-ico,.tabbtn .lock-ico{font-size:12px;margin-left:4px;font-style:normal;vertical-align:middle}
.sheet-item.locked{opacity:.42}
.sheet-item .lock-ico{font-size:12px;margin-left:4px;font-style:normal}
/* 课表页「受限」水印条 */
.guest-restrict-bar{
  margin:0 0 10px;padding:8px 12px;border-radius:var(--r-md);
  font-size:13px;line-height:1.5;color:#8a6d1b;
  background:linear-gradient(90deg,rgba(240,189,58,.18),rgba(240,189,58,.06));
  border:1px solid rgba(240,189,58,.4);
}
[data-theme="dark"] .guest-restrict-bar{color:#f0cf6b;background:rgba(240,189,58,.12);border-color:rgba(240,189,58,.3)}
/* 统一引导浮层内容 */
.gg-wrap{text-align:center;padding:6px 4px 2px}
.gg-ico{font-size:36px;line-height:1}
.gg-t{font-size:16px;font-weight:500;margin:10px 0 6px;color:var(--tx,#243044)}
.gg-wrap .hint{text-align:left;margin-top:4px}

/* ============ v44 批次 D：成员管理增强 / 安全日志后台（新增类，语义前缀，避免与旧样式冲突） ============ */
.stbadge{display:inline-block;margin-left:6px;padding:0 6px;border-radius:var(--r-md);font-size:12px;line-height:18px;vertical-align:middle;font-weight:500}
.stbadge.st-locked{background:rgba(240,178,50,.18);color:#e0a020;border:1px solid rgba(240,178,50,.35)}
.stbadge.st-disabled{background:rgba(230,80,80,.16);color:#e05a5a;border:1px solid rgba(230,80,80,.32)}
.stbadge.st-guest{background:rgba(120,140,170,.16);color:#8a9bb5;border:1px solid rgba(120,140,170,.3)}
.stbadge.st-normal{background:rgba(60,180,110,.14);color:#3fb173;border:1px solid rgba(60,180,110,.3)}
mark.hlq{background:rgba(255,214,102,.45);color:inherit;border-radius:var(--r-md);padding:0 1px}
select.msel{max-width:126px;padding:2px 6px;font-size:12px}
.mdetail-top{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.mdetail-name{font-size:16px;font-weight:500}
.mdetail-sub{font-size:12px;color:var(--tx3)}
.mdetail-tb{width:100%;border-collapse:collapse}
.mdetail-tb th{text-align:left;width:92px;color:var(--tx3);font-weight:500;padding:7px 8px;border-bottom:1px solid var(--line2);white-space:nowrap}
.mdetail-tb td{padding:7px 8px;border-bottom:1px solid var(--line2)}
.pw-mask{letter-spacing:2px;color:var(--tx2)}
.sec-table td,.sec-table th{white-space:nowrap}
.sec-type{display:inline-block;padding:0 7px;border-radius:var(--r-md);font-size:12px;line-height:18px;background:rgba(120,140,170,.16);color:var(--tx2)}
.sec-pager{display:flex;align-items:center;gap:10px;justify-content:center;margin-top:10px}

/* ============ v44 需求九 增量样式（dc- 前缀，避免与全局类名撞车） ============ */
/* 组织架构页「只显示我加入的社团」勾选框 */
.dc-onlyjoined{display:inline-flex;align-items:center;gap:6px;margin-left:10px;padding:4px 10px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--pri-soft);color:var(--tx);font-size:13px;cursor:pointer;user-select:none}
.dc-onlyjoined input{margin:0;cursor:pointer}
/* 社团页空态（勾选但本人无社团，边界 18） */
.dc-empty{padding:24px 12px;text-align:center;color:var(--tx2)}
.dc-empty p{margin:4px 0}
.dc-empty .hint{font-size:12px;color:var(--tx3)}
/* 社区中心社团筛选下拉 */
.dc-commfilter-wrap{display:inline-block;margin-left:10px;vertical-align:middle}
.dc-commfilter{font-size:13px;padding:4px 8px;border:1px solid var(--line);border-radius:var(--r-md);background:#fff;color:var(--tx);cursor:pointer;max-width:180px}
.dc-commfilter:focus{outline:none;border-color:var(--pri)}

/* ============ v45 阶段一 P0：绑定向导警示条 / 成员详情操作区（新增类，带 bw-/mdetail- 前缀避免与旧样式冲突） ============ */
.bw-warn{background:rgba(240,178,50,.14);border:1px solid rgba(240,178,50,.38);color:#a87b12;border-radius:var(--r-md);padding:8px 10px;line-height:1.5}
.mdetail-acts{margin-top:12px;padding-top:12px;border-top:1px solid var(--line2);display:flex;align-items:center;gap:10px;flex-wrap:wrap}

/* ============ v61：关于软件和作者（全部 .about- 前缀，避免与既有类名撞车） ============ */
/* 顶部卡片：居中圆形头像 + 应用名 + 版本号 + 构建日期。
   ★ 坑：只给容器定尺寸不够 —— 内部 img 会按原图 240×240 溢出盖住整页，
     必须同时给 .about-av img 定 100%/100% + object-fit:cover。 */
.about-hero{display:flex;flex-direction:column;align-items:center;text-align:center;padding:22px 16px;margin-bottom:14px}
.about-hero .about-av{width:72px;height:72px;border-radius:50%;overflow:hidden;flex:0 0 auto;border:1px solid var(--line);background:var(--pri-soft)}
.about-hero .about-av img{width:100%;height:100%;display:block;object-fit:cover}
.about-hero .about-app{margin:12px 0 2px;font-size:16px;font-weight:500;color:var(--tx)}
.about-hero .about-ver{margin:0;font-size:13px;color:var(--tx2)}
.about-hero .about-build{margin:4px 0 0;font-size:12px;color:var(--tx3)}
.about-panel{margin-bottom:14px}
.about-sec{font-size:14px;font-weight:500;color:var(--tx);margin:0}
.about-list{margin:0;padding-left:20px;line-height:1.85;color:var(--tx);font-size:13px}
.about-list li{margin:2px 0}
.about-tech{display:flex;flex-direction:column;gap:2px;padding:8px 0;border-bottom:1px solid var(--line2)}
.about-tech:last-child{border-bottom:0}
.about-tech b{color:var(--tx);font-size:13px}
.about-tech span{color:var(--tx2);font-size:13px;line-height:1.7}
.about-tbl{width:100%;border-collapse:collapse;font-size:13px}
.about-tbl th,.about-tbl td{padding:9px 12px;border-bottom:1px solid var(--line2);text-align:left;color:var(--tx)}
.about-tbl th{color:var(--tx2);font-weight:500;background:transparent}
.about-tbl td.about-td-flag,.about-tbl th:last-child{text-align:center;width:88px;white-space:nowrap}
.about-yes{color:#16a34a;font-weight:500}
.about-no{color:#dc2626;font-weight:500}
.about-foot{text-align:center}
.about-foot .hint{color:var(--tx3)}
/* v69：ICP 备案号 —— 关于页展示 + 全站页脚（工信部要求页脚展示并可点进备案查询） */
.icp-line{margin:8px 0 0;font-size:13px}
.icp-line a{color:var(--pri,#3b6ef5)}
.site-foot{text-align:center;padding:10px 12px 8px;font-size:12px;line-height:1.7;color:var(--tx3,#8a90a0);
  border-top:1px solid var(--line,#e6e8ef);margin:14px auto 0;max-width:100%}
.site-foot a{color:var(--tx3,#8a90a0);text-decoration:underline}
/* 手机端：#main 已经有 74px 底部留白（给固定标签栏），页脚再往下撑就没意义了 */
@media (max-width:760px){
  .site-foot{margin:10px auto 0;padding:8px 8px 4px;font-size:12px}
}
@media (max-width:760px){
  .about-hero{padding:18px 12px}
  .about-list{font-size:13px;line-height:1.7}
  .about-tbl{font-size:13px}
  .about-tbl th,.about-tbl td{padding:6px 8px}
  .about-tbl td.about-td-flag,.about-tbl th:last-child{width:64px}
}

/* ===== v45 批次 E：课表美化 ===== */
/* E1/E6：课表工具条第二行（视图切换 / 周次 / 背景 / 主题 / 导出） */
.ct-bar { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 10px; padding:8px 10px;
  border-radius:var(--r-md); background:var(--soft, #f4f6fb); }
.ct-bar .ct-seg { display:inline-flex; }
.ct-bar .ct-seg .btn { border-radius:var(--r-xs) 0 0 var(--r-xs); }
.ct-bar .ct-seg .btn + .btn { border-radius:0 var(--r-xs) var(--r-xs) 0; margin-left:-1px; }
.ct-bar .ct-lb { display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--muted, #6b7280); }
.ct-bar .ct-sel, .ct-bar select.ct-sel { padding:5px 8px; border-radius:var(--r-md); border:1px solid var(--line, #d9dee9);
  background:var(--card, #fff); color:inherit; font-size:13px; }
.ct-bar .hint { margin-left:auto; font-size:12px; }

/* E4：表格里的课程图标 / 备注气泡 */
.cs-ic { display:inline-block; margin-right:4px; }
.cs-note { font-style:normal; margin-left:4px; cursor:help; opacity:.75; }

/* E1/E3：周视图网格 */
.cg-wrap { position:relative; border-radius:var(--r-md); overflow:hidden; border:1px solid var(--line, #d9dee9);
  background-size:cover; background-position:center; background-repeat:no-repeat; }
.cg-mask { position:absolute; inset:0; background:#fff; pointer-events:none; border-radius:var(--r-md); }
[data-theme="dark"] .cg-mask { background:rgba(0,0,0,.45); }
/* v58：列数由行内 --cg-days 决定（天数筛选会改它）。
   注意 minmax(78px,1fr) 的「最小 78px」在手机上会把网格撑到 620px 以上、
   被 .cg-wrap{overflow:hidden} 裁掉周末 —— 手机端那条 media query 会把最小宽降到 0。 */
.cg { position:relative; display:grid; grid-template-columns:64px repeat(var(--cg-days, 7), minmax(78px, 1fr));
  grid-auto-rows:44px; gap:1px; padding:1px; }
.cg-cell { background:var(--card, #fff); display:flex; flex-direction:column; align-items:center;
  justify-content:center; font-size:12px; color:var(--muted, #6b7280); }
.cg-corner { font-size:12px; opacity:.8; }
.cg-day { font-weight:500; color:var(--text, #1f2430); font-size:13px; }
.cg-period b { font-size:13px; color:var(--text, #1f2430); }
.cg-period span { font-size:12px; opacity:.75; }
.cg-blk { position:relative; border-radius:var(--r-md); padding:5px 6px; color:#fff; overflow:hidden;
  font-size:12px; line-height:1.25; cursor:grab; touch-action:none; user-select:none;
  box-shadow:0 1px 3px rgba(0,0,0,.18); z-index:2; }
.cg-blk .cg-t { font-weight:500; font-size:13px; word-break:break-all; }
.cg-blk .cg-m { font-size:12px; opacity:.92; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cg-blk .cg-ic { margin-right:3px; }
/* v58：★ 同一时间压着多门课（重修 / 补修）—— 不阻止重叠，只在右上角标一个小数字，
   并用白色虚线描边提示「这一格不止一门课」。悬停 title 里能看到具体是哪几门。 */
.cg-blk.cg-ov { box-shadow:0 1px 3px rgba(0,0,0,.18), 0 0 0 1px rgba(255,255,255,.55) inset; }
.cg-blk .cg-n { position:absolute; top:2px; right:2px; min-width:14px; height:14px; padding:0 3px;
  border-radius:var(--r-md); background:rgba(0,0,0,.42); border:1px solid rgba(255,255,255,.75);
  color:#fff; font-size:12px; line-height:12px; font-style:normal; font-weight:500;
  text-align:center; box-sizing:border-box; pointer-events:none; }
/* v58：网格上方「显示天数」条 */
.cg-bar { display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin:0 0 8px; }
.cg-bar .cg-bar-lb { font-size:12px; color:var(--tx3); }
.cg-bar .cg-seg { display:inline-flex; }
.cg-bar .cg-seg .btn { border-radius:var(--r-xs) 0 0 var(--r-xs); }
.cg-bar .cg-seg .btn + .btn { border-radius:0 var(--r-xs) var(--r-xs) 0; margin-left:-1px; }
.cg-bar select.ct-sel { padding:5px 8px; border-radius:var(--r-md); border:1px solid var(--line, #d9dee9);
  background:var(--card, #fff); color:inherit; font-size:13px; }
.cg-bar .hint { margin-left:auto; font-size:12px; }

/* ============ v58：手机端「网格课表看得全」 ============
   病症（用户截图）：手机上网格课表右边被切掉，周六周日怎么都看不到。
   真因：.cg 的列宽写死 minmax(78px,1fr) → 64 + 7×78 + 8 ≈ 620px，比手机屏（360~430px）宽；
        外层 .cg-wrap{overflow:hidden}（本来是为了背景图/白纱的圆角裁切）把溢出直接**裁掉**，
        既看不到、也没有横向滚动条 —— 用户的感受是「永远看不到周六周日」。
   修法：① 列宽最小宽降到 0（minmax(0,1fr)），7 天严格等分屏幕宽度，一定塞得下；
        ② 节次列 + 字号同步收窄（约 42px 的列宽里内容还看得清）；
        ③ .cg-wrap 允许横向滚动兜底（极窄屏 / 系统放大字体时仍能滑到周末）；
        ④ 配合「显示天数」条（工作日 / 单日）在窄屏上换取可读性。
   ⚠ 这段必须放在上面 .cg / .cg-wrap 基础规则**之后**：同优先级下后写的才生效，
     之前误放在文件前部（基础规则之前），结果被基础规则整段覆盖 —— 表现就是
     「CSS 改了但手机上一点变化都没有」，和缓存问题极像。 */
@media(max-width:760px){
  .cg { grid-template-columns:40px repeat(var(--cg-days, 7), minmax(0, 1fr)); grid-auto-rows:40px; }
  .cg-wrap { overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; }
  .cg-cell { font-size:12px; }
  .cg-corner { font-size:12px; }
  .cg-day { font-size:12px; }
  .cg-period b { font-size:12px; }
  .cg-period span { font-size:7.5px; }
  .cg-blk { padding:2px 3px; border-radius:var(--r-md); font-size:12px; }
  .cg-blk .cg-t { font-size:12px; line-height:1.18; }
  .cg-blk .cg-m { font-size:12px; }
  .cg-blk .cg-ic { margin-right:2px; }
  .cg-blk .cg-n { top:1px; right:1px; min-width:12px; height:12px; line-height:10px; font-size:12px; padding:0 2px; }
  .cg-break { font-size:9px; letter-spacing:1px; }
  .cg-bar { gap:5px; margin-bottom:7px; }
  .cg-bar .cg-bar-lb { font-size:12px; }
  .cg-bar select.ct-sel { padding:4px 6px; font-size:13px; }
  .cg-bar .hint { margin-left:0; flex:1 1 100%; font-size:12px; }
}
/* v56：重叠并排的窄课块 —— 标题/地点单行省略，避免挤爆（.cg-m 原本已省略，这里统一补 .cg-t） */
.cg-blk.cg-narrow .cg-t { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cg-blk.cg-narrow .cg-m { font-size:12px; }
.cg-blk.dragging { opacity:.85; z-index:60; cursor:grabbing; box-shadow:0 6px 18px rgba(0,0,0,.3); }
.cg-target { border:2px dashed var(--pri, #3b6ef5); border-radius:var(--r-md); background:rgba(59,110,245,.10);
  pointer-events:none; z-index:5; }
body.cg-nosel, body.cg-nosel * { user-select:none !important; -webkit-user-select:none !important; }
/* v61：「午休行」= 第 4 节与第 5 节之间的课间（12:00-14:00），是网格里独立的一行（矮行 26px），
   横跨整行显示；不再是覆盖第 5-6 节的带子。pointer-events:none 不挡拖拽/点击。 */
.cg-break { position:relative; pointer-events:none; display:flex; align-items:center;
  justify-content:center; font-size:12px; letter-spacing:2px;
  color:var(--muted, #8b93a7);
  background:repeating-linear-gradient(-45deg, rgba(139,147,167,.06), rgba(139,147,167,.06) 8px,
    rgba(139,147,167,.12) 8px, rgba(139,147,167,.12) 16px); }

/* E5：配色主题弹窗 */
.ctp-list { max-height:280px; overflow:auto; margin-bottom:10px; }
.ctp-row { display:flex; align-items:center; gap:10px; padding:7px 8px; border-radius:var(--r-md); }
.ctp-row + .ctp-row { margin-top:4px; }
.ctp-row.on { background:var(--soft, #f4f6fb); }
.ctp-nm { min-width:84px; font-size:13px; font-weight:500; }
.ctp-sw { display:inline-flex; gap:3px; flex:1; }
.ctp-sw i { width:16px; height:16px; border-radius:var(--r-md); display:inline-block; }

/* ===== v45 F1：开场动画 ===== */
.intro-ov { position:fixed; inset:0; z-index:99999; display:flex; align-items:center; justify-content:center;
  flex-direction:column; overflow:hidden; background:#0b1220; opacity:1; transition:opacity .42s ease;
  /* ⚠⚠⚴ v79.9 批次9【问题二】**遮罩不吃点击**（用户报「记下没用」的真因）。
     症状：开场动画期间（2.6s 播完 + 0.45s 淡出 ≈ 3.05s）**全屏任何按钮都点不到**，
     落点命中的是这层遮罩而不是按钮 ⇒ 零反馈 ⇒ 用户归因成「记下没用」。
     ⚠ 实测四轮对照（tmp/_qa_d12_memo_add_probe.js）：
       ① 刚进页面 0.5s 内点 ⇒ elementFromPoint 命中 DIV.intro-ov、库原始行 Δ=0
       ② 等遮罩消失再点   ⇒ 命中 BUTTON.v797-btn、Δ=1
       ⇒ 唯一变量就是遮罩在不在。
     ⚠⚠⚠ 为什么不能靠降 z-index 解决（我与主理人核过）：
       它是全屏 flex 居中遮罩，降 z 会被内容盖住 ⇒ 动画失效（等于删掉开场）。
     ⚠⚠⚠ 为什么不用「保留 auto + 靠 JS 判断」：
       那仍要靠 JS 猜「这次点击该不该给内容」，猜错就是本项目栽过多次的
       「DOM 里有 ≠ 用户点得到 / 点到了没反应」；
       而 `pointer-events` 是**浏览器级**的命中裁决，语义唯一、不可能歧义。
     ⚠ 一致性：`.intro-fx`（styles.css 下方）与 `.intro-props` 早已是
       pointer-events:none，本条与项目既有模式一致。*/
  pointer-events:none; }
.intro-ov.out { opacity:0; }
/* 🔴 v89【P0·用户原话「还有很多黑色的东西」】
   ⚠ 原值`background:#0b1220`（近黑深蓝）是**开屏底色的默认值**，
     而 mascot 档的 className 是 `intro-ov intro-mascot intro-fx-<季>`
     —— `intro-mascot` 与 `intro-fx-<季>` **都没有自己的背景色规则**
     （styles.css 里只定义了 `.intro-fx-<季> i`，那是粒子的形状与颜色），
     mascot 档也**不带** `intro-<季>` 那个带渐变的 class。
     ⇒ 结论（已核实）：**mascot 档整屏底色就是这个 #0b1220**，
       再叠一块 rgba(0,0,0,.22) 的椭圆投影（.intro-mascot-shadow）。
     这就是用户说的「很多黑色的东西」——它不是局部黑块，是**整屏都是黑的**。
   ⚠ v90【实测发现的坑，这条选择器必须只认 .intro-hero】
     原选择器是 `.intro-ov.intro-mascot`，而 `intro-mascot` 这个 class名
     **本来是给「吉祥物图片」用的**（styles.css:4670 给它写了
     `width:clamp(120px,46vw,260px); height:auto`）。
     一旦它出现在**全屏遮罩容器**上，遮罩就会被当成"一张图片"去解析尺寸：
     宽度被夹到 46vw、高度 auto 塌成 0（因为里面只有绝对定位的立绘）
     ⇒ 实测遮罩 rect = 154×0 / 169×0 / 237×0（三个视口）
     ⇒ 整屏只剩右上角一个「跳过」按钮，开屏等于没有。
     ⇒ 所以 v90 把三档的 ov.className 里的 intro-mascot 全部去掉，
       浅色底色改由 `.intro-ov.intro-hero` 承担（本条 + p1-fixes-v86.css:1530）。
   ⚠ 为什么改成浅色而不是透明：
     开屏期间图片可能还没解码完成，此时底下若是透明会透出 App 的深色页面底
     （同样是黑），依旧难看。给一个与立绘主色协调的**浅暖色**做兜底最稳。 */
.intro-ov.intro-hero {
  background: linear-gradient(165deg, #fdf6ee 0%, #f4f8fd 100%);
}
/* ⚠ v89：文字层压在立绘之上（立绘改为 absolute 底图后需要显式层级） */
.intro-ov .intro-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.intro-ov .intro-logo { font-size:64px; font-weight:800; letter-spacing:8px; color:#fff;
  text-shadow:0 6px 24px rgba(0,0,0,.35); animation:introPop .9s ease both; position:relative; z-index:2; }
.intro-ov .intro-slogan { margin-top:12px; font-size:20px; letter-spacing:6px; color:rgba(255,255,255,.92);
  animation:introFade 1.4s ease both; position:relative; z-index:2; }
.intro-ov .intro-skip { position:absolute; right:16px; top:16px; padding:7px 14px; border-radius:var(--r-md);
  /* 🔴 v89【P1·右上角那个"黑色的东西"】
     ⚠ 原值 `background:rgba(0,0,0,.25)` + `border:1px solid rgba(255,255,255,.6)`，
       落在浅色立绘上就是一块**突兀的黑底白框方块** ——
       这是用户说的"很多黑色的东西"里除整屏底色外的第二处。
     ⚠ 改成半透明浅色 + 深色文字：仍是可点的（对比度靠深色文字保证），
       但不再是一块黑疙瘩。⚠ 深色文字在任何当季立绘上都可读（底色都是浅色调）。*/
  border:1px solid rgba(255,255,255,.34);
  background:rgba(255,255,255,.72); color:#4a3f33;
  font-size:13px; cursor:pointer;
  /* ⚠⚠⚴ 父层已是 pointer-events:none（不吃全屏点击），**这里必须显式恢复**，
     否则「跳过 ▶▶」按钮会跟着一起点不到 ⇒ 开场就彻底无法跳出了。
     ⚠ 这是 pointer-events 的标准语义：子元素可**显式反悔**，覆盖父级的 none。
     ⚠ `.intro-fx` / `.intro-props` 是纯装饰，保持继承来的 none（正确）。*/
  pointer-events:auto; z-index:9; }
/* 🔴 v89：满屏立绘的柔和淡入（唯一的"动画"感，不做位移以免边缘露底）。
   ⚠ 为什么淡入而不是缩放：object-fit:cover 已铺满，
     缩放会让图片边缘短暂离开视口、露出底色 ⇒ 那正是要消灭的黑边。 */
@keyframes introHeroFade { from { opacity:0; } to { opacity:1; } }
.intro-ov .intro-hero-img { animation: introHeroFade .9s ease both; }
.intro-ov.intro-gradient { background:linear-gradient(135deg,#3b6ef5 0%,#22a06b 55%,#0ea5e9 100%); }
.intro-ov.intro-festival { background:linear-gradient(160deg,#fde7f3 0%,#e8f3ff 100%); }
.intro-ov.intro-festival .intro-logo, .intro-ov.intro-festival .intro-slogan { color:#d6456b; text-shadow:none; }
.intro-fx { position:absolute; inset:0; pointer-events:none; }
.intro-fx i { position:absolute; top:-24px; width:10px; height:10px; border-radius:60% 0 60% 0;
  background:#f7a8c4; opacity:.85; animation:introFall linear infinite; }
@keyframes introFall { 0% { transform:translateY(-10vh) rotate(0deg); } 100% { transform:translateY(110vh) rotate(360deg); } }
@keyframes introPop { 0% { transform:scale(.7); opacity:0; } 100% { transform:scale(1); opacity:1; } }
@keyframes introFade { 0% { opacity:0; letter-spacing:18px; } 100% { opacity:1; letter-spacing:6px; } }

/* ===== v45 E7：课前提醒横幅 ===== */
.lesson-banner { position:fixed; left:50%; top:14px; z-index:99990; transform:translate(-50%,-140%);
  display:flex; align-items:center; gap:10px; padding:10px 14px; border-radius:var(--r-md);
  background:linear-gradient(135deg,#f0913a,#e4544f); color:#fff; box-shadow:0 8px 24px rgba(0,0,0,.28);
  font-size:14px; transition:transform .35s ease; max-width:92vw; }
.lesson-banner.show { transform:translate(-50%,0); }
.lesson-banner b { font-size:14px; }
.lesson-banner .lb-x { border:1px solid rgba(255,255,255,.7); background:transparent; color:#fff;
  border-radius:var(--r-md); padding:3px 10px; font-size:12px; cursor:pointer; }

/* v62：多组织身份 —— 成员身份行 chip + 三列身份弹窗（移动端纵向堆叠） */
.idline { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 2px; }
.idchip { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; line-height: 1.4;
  padding: 1px 7px; border-radius:var(--r-md); background: rgba(59,110,245,.10); color: var(--tx2, #55607a);
  border: 1px solid rgba(59,110,245,.18); white-space: nowrap; }
.idchip b { font-weight: 600; color: var(--tx, #1f2740); }
.mid-row { display: grid; grid-template-columns: 1.4fr 1fr 1.4fr auto; gap: 6px; align-items: center;
  padding: 6px 0; border-bottom: 1px dashed rgba(120,130,150,.25); }
.mid-row select { width: 100%; min-width: 0; }
.mid-row .btn.tiny { padding: 2px 8px; }
@media (max-width: 640px) {
  .mid-row { grid-template-columns: 1fr; gap: 4px; }
  .mid-row .btn.tiny.danger { justify-self: end; }
}

/* ==================== v66：统一「社团/部门」范围选择器（图3样式弹窗） ==================== */
.od-sec { font-size: 12px; color: var(--tx2, #6b7488); margin: 6px 0 4px; font-weight: 600; }
.od-list { display: flex; flex-direction: column; gap: 6px; max-height: 240px; overflow-y: auto; }
.od-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  border: 1px solid var(--line, #e6eaf2); border-radius:var(--r-md); cursor: pointer;
  background: var(--card, #fff);
}
.od-row:hover { border-color: var(--pri, #3b6ef5); }
.od-row.on { border-color: var(--pri, #3b6ef5); background: rgba(59, 110, 245, .07); }
.od-row .od-nm { flex: 1; min-width: 0; font-size: 14px; }
.od-row em { font-size: 12px; color: var(--tx2, #6b7488); font-style: normal; white-space: nowrap; }
.od-acts { display: flex; gap: 8px; margin-top: 6px; }
/* ★ v112：发布大型活动的部门/人数列表在手机/平板上显示不全/被压扁，
   取消 .od-list 的内部滚动限制，让列表随弹窗内容区整体滚动，避免被截断。 */
#evDepts.od-list { max-height: none; overflow: visible; gap: 8px; }
#evDepts .od-row { flex-shrink: 0; min-height: 44px; }
#evDepts .evq-n:disabled { opacity: .5; background: var(--bg2, #f4f6fb); }
/* v66：日历/课表三类条目颜色 —— 普通课程=蓝(既有) · 大型活动=橙 · 排班任务=绿 */
.mx.k-cevent { background: #fff3e0 !important; color: #b26a00; }
.mx.k-shift { background: #e8f5e9 !important; color: #1b5e20; }
.cal-item.cevent, .ci-cevent { border-left: 3px solid #ff9800; }
.cal-item.shift, .ci-shift { border-left: 3px solid #4caf50; }
[data-theme="dark"] .mx.k-cevent { background: #4a3410 !important; color: #ffcc80; }
[data-theme="dark"] .mx.k-shift { background: #14351a !important; color: #a5d6a7; }
/* v66：手动升级徽标 */
.badge-manual {
  display: inline-block; font-size: 10px; padding: 1px 6px; border-radius:var(--r-pill);
  background: #fff3e0; color: #b26a00; border: 1px solid #ffcc80; margin-left: 4px;
}
/* v66：冲突告警行 */
.conflict-row td, .conflict-row th { background: #fdeaea !important; }
/* v69：被取消（软取消）的活动 / 排班 —— 淡化但保留在列表里，让人看得出「这条不行了」而不是凭空消失 */
.canceled-row{opacity:.72}
.canceled-row .ttl{text-decoration:line-through;text-decoration-color:#c2c6d0}
.conflict-badge {
  display: inline-block; font-size: 10px; color: #c0392b; background: #fdeaea;
  border: 1px solid #f5b7b1; border-radius:var(--r-pill); padding: 1px 6px; margin-left: 4px;
}
/* v79.1：暗色模式补全 —— 上三处浅底在暗色下需翻成深色系，否则仍是刺眼浅粉/浅橙 */
[data-theme="dark"] .badge-manual { background: #3a2f1c; color: #f5c07a; border-color: #5a421a; }
[data-theme="dark"] .conflict-row td, [data-theme="dark"] .conflict-row th { background: #2a1818 !important; }
[data-theme="dark"] .conflict-badge { background: #2a1818; color: #ff9b97; border-color: #7a3b38; }
/* v66：概览卡片拖拽 */
.dash-card { position: relative; }
.dash-card .drag-handle { cursor: grab; touch-action: none; color: var(--tx2, #6b7488); }
.dash-card.dragging { opacity: .55; outline: 2px dashed var(--pri, #3b6ef5); }
.dash-card.drag-over { outline: 2px solid var(--pri, #3b6ef5); }

/* ==================== v67：我的待办 + 活动/排班工作台 ==================== */
.wb66-todobar{
  display:flex;align-items:center;gap:10px;margin-top:12px;padding:10px 14px;border-radius:var(--r-md);
  background:linear-gradient(90deg,rgba(59,110,245,.10),rgba(59,110,245,.03));
  border:1px solid rgba(59,110,245,.28);cursor:pointer;
}
.wb66-todobar .ib{font-size:16px}
.wb66-todobar .tx{flex:1;min-width:0;font-size:13px;color:var(--tx);line-height:1.4}
.wb66-todobar .tx b{color:var(--pri,#3b6ef5)}
.wb66-todobar .sub{display:block;font-size:12px;color:var(--tx2,#6b7488);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wb66-todobar:hover{border-color:var(--pri,#3b6ef5)}
.nc-go{
  font-size:12px;padding:2px 8px;border:none;border-radius:var(--r-md);cursor:pointer;
  background:var(--pri,#3b6ef5);color:#fff;margin-left:6px;
}
.wb66{width:100%}
.modal .wb66{max-width:none}
.wb66-head{padding:12px 14px;border-radius:var(--r-md);background:var(--bg2,#f6f8fc);border:1px solid var(--line2,#e6eaf2)}
.wb66-title{font-size:14px;font-weight:500;display:flex;flex-wrap:wrap;align-items:center;gap:6px}
.wb66-meta{font-size:12px;color:var(--tx2,#6b7488);margin-top:4px;line-height:1.5}
.wb66-prog{height:8px;border-radius:var(--r-md);background:#e8ecf5;overflow:hidden;margin-top:10px}
.wb66-prog>i{display:block;height:100%;border-radius:var(--r-md);background:linear-gradient(90deg,#3b6ef5,#5b8bff);transition:width .35s ease}
.wb66-progtxt{font-size:12px;color:var(--tx2,#6b7488);margin-top:5px}
.wb66-steps{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.wb66-step{
  flex:1 1 30%;min-width:150px;display:flex;align-items:center;gap:7px;font-size:12px;
  color:var(--tx2,#6b7488);padding:7px 10px;border-radius:var(--r-md);background:var(--bg2,#f6f8fc);
  border:1px solid var(--line2,#e6eaf2);
}
.wb66-step .dot{width:8px;height:8px;border-radius:50%;background:#c7cddb;flex:none}
.wb66-step.done{color:var(--tx,#222)}
.wb66-step.done .dot{background:#3b6ef5}
.wb66-step.now{color:#fff;background:var(--pri,#3b6ef5);border-color:var(--pri,#3b6ef5)}
.wb66-step.now .dot{background:#fff}
.wb66-step.off{opacity:.55}
.wb66-sec{font-size:12px;font-weight:500;color:var(--tx2,#6b7488);margin:14px 0 7px;display:flex;align-items:center;gap:8px}
.wb66-sec .hint{font-weight:400}
.wb66-dept{
  border:1px solid var(--line2,#e6eaf2);border-radius:var(--r-md);padding:10px 12px;margin-bottom:8px;background:var(--card,#fff);
}
.wb66-dept.full{border-color:rgba(27,122,82,.45);background:rgba(232,245,233,.5)}
.wb66-dept-hd{display:flex;justify-content:space-between;align-items:center;font-size:13px}
.wb66-n{font-size:12px;color:var(--tx2,#6b7488)}
.wb66-mini{height:6px;border-radius:var(--r-md);background:#eef1f7;margin:7px 0;overflow:hidden}
.wb66-mini>i{display:block;height:100%;background:#3b6ef5;border-radius:var(--r-md)}
.wb66-dept.full .wb66-mini>i{background:#1b7a52}
.wb66-mem{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:6px}
.wb66-chip{font-size:12px;padding:2px 9px;border-radius:var(--r-md);background:#eef2fb;color:#33406b;border:1px solid #dde4f6}
/* ===== v67：周排班课表（编辑器 + 工作台共用） ===== */
.wsgrid{width:100%;border-collapse:separate;border-spacing:0;font-size:12px;table-layout:fixed}
.wsgrid th,.wsgrid td{border:1px solid var(--line2,#e6eaf2);padding:6px;vertical-align:top}
.wsgrid thead th{background:#f6f8fc;font-weight:500;color:var(--tx2,#6b7488);text-align:center;line-height:1.35}
.wsgrid thead th .ws-d{font-size:12px;font-weight:400}
.wsgrid .wsg-th{background:#f6f8fc;width:172px;text-align:left}
.wsgrid .wsg-ts{display:flex;flex-direction:column;gap:3px}
.wsgrid .wsg-ts input{width:100%;padding:3px 5px;font-size:12px;border:1px solid var(--line2,#e6eaf2);border-radius:var(--r-md)}
.wsgrid .wsg-ops{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}
.wsgrid .wsg-ops .btn{padding:2px 7px;font-size:12px}
.wsgrid .wsg-c{background:var(--card,#fff);cursor:pointer;min-height:64px;transition:background .15s}
.wsgrid .wsg-c:hover{background:#f2f6ff}
.wsgrid .wsg-c.on{background:#e8f0ff;box-shadow:inset 0 0 0 2px #3b6ef5}
.wsgrid .wsg-c.empty{color:#a8b0c2;background:#fbfcfe}
.wsgrid .wsg-ph{color:#a8b0c2;font-size:12px}
.wsgrid .wsg-it{display:block;font-size:12px;color:#33406b;margin-bottom:3px;line-height:1.3}
.wsgrid .wsg-u{
  display:inline-block;max-width:100%;margin:0 3px 3px 0;padding:1px 7px;border-radius:var(--r-md);
  background:#eef2fb;color:#33406b;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.wsedit-head{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:13px;margin-bottom:8px}
.wsedit-head b{color:#33406b}
/* 工作台里的只读周排班课表 */
.wsgrid-m{width:100%;border-collapse:separate;border-spacing:0;font-size:12px;table-layout:fixed}
.wsgrid-m th,.wsgrid-m td{border:1px solid var(--line2,#e6eaf2);padding:6px;vertical-align:top}
.wsgrid-m thead th{background:#f6f8fc;text-align:center;line-height:1.35}
.wsgrid-m thead th em{font-size:12px}
.wsgrid-m .wsg-t{background:#f6f8fc;font-weight:500;white-space:nowrap}
.wsgrid-m .wsg-c{background:var(--card,#fff)}
.wsgrid-m .wsg-c.empty{background:#fbfcfe;color:#bfc6d4}
.wsgrid-m .wsg-it{display:block;color:#33406b;margin-bottom:3px}
.wsgrid-m .wsg-u{display:block;padding:1px 7px;margin-bottom:2px;border-radius:var(--r-md);background:#eef2fb;color:#33406b;font-size:12px}
.wsgrid-m .wsg-u.ok{background:#e6f4ec;color:#1b7a52}
.wsgrid-m .wsg-u.no{background:#fdeaea;color:#b0302a}
.wb66-roster{border:1px solid var(--line2,#e6eaf2);border-radius:var(--r-md);overflow:hidden}
.wb66-rost-row{
  display:flex;align-items:center;gap:8px;padding:8px 12px;font-size:13px;
  border-bottom:1px solid var(--line2,#e6eaf2);background:var(--card,#fff);
}
.wb66-rost-row:last-child{border-bottom:none}
.wb66-rost-row .nm{font-weight:500;min-width:64px}
.wb66-rost-row .dp{color:var(--tx2,#6b7488);font-size:12px;flex:1}
.wb66-reject{
  margin-top:12px;padding:9px 12px;border-radius:var(--r-md);font-size:12px;color:#b0302a;
  background:#fdeaea;border:1px solid #f5b7b1;
}
.wb66-todo{display:flex;flex-direction:column;gap:8px;max-height:60vh;overflow:auto}
.wb66-todo-row{
  display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--r-md);cursor:pointer;
  border:1px solid var(--line2,#e6eaf2);background:var(--card,#fff);
}
.wb66-todo-row:hover{border-color:var(--pri,#3b6ef5)}
.wb66-todo-main{flex:1;min-width:0}
.wb66-todo-main .ttl{font-size:13px;font-weight:500}
.wb66-todo-main .meta{font-size:12px;color:var(--tx2,#6b7488);margin-top:2px}
/* v67：概览换序按钮与把手在标题栏里排整齐 */
.dash-card .panel-head{gap:6px}
.dash-card .panel-head .drag-handle{
  display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
  border-radius:var(--r-md);background:var(--bg2,#f1f4fa);font-size:13px;
}
.dash-card .panel-head .drag-handle:active{cursor:grabbing}
.dash-swap{padding:2px 8px}
@media (max-width:520px){
  .wb66-steps{flex-direction:column}
  .wb66-dept-hd{font-size:12px}
}

/* v67：概览天气/今日 两张卡的顺序（order 换列，最稳）
   注意：栅格自动排版按 order 数值升序，三列里的公告/未来7天两张 .panel 是 order:0，
   若不显式给它们 order:3，它们会排到两张卡前面去（天气卡被挤到第三列）——这就是「点了没效果」的真因。 */
.home-top #dashWx{order:1}
.home-top #dashTask{order:2}
.home-top>.panel:not(.dash-card){order:3}
.home-top[data-order="task"] #dashWx{order:2}
.home-top[data-order="task"] #dashTask{order:1}

/* v75：天气卡「7 天连排」在窄列会溢出卡片（用户截图：天气在第一列时最后一格文字
   顶出卡片边界）。① 兜底裁切；② 天气在第一列（data-order="wx"，窄列）时只显示
   今天+4 天共 5 格；换序后天气到第二列（宽列）自然恢复 7 格。
   ★ 只作用于 >820px：手机端（≤820px）各格约 50px 不溢出，保持 7 天不变。 */
#dashWx .wx-week{overflow:hidden}
/* v75.1：v75 首版只隐藏了第 6/7 格，但 1280px 窄列下 5 格仍溢出 16px —— 根因是
   grid 项默认 min-width:auto，内容（「25~35°」）会把 1fr 的格子撑开、顶破卡片。
   给格子 min-width:0 + 温度文字裁切，1fr 才真正均分不溢出。 */
#dashWx .wx-day{min-width:0;overflow:hidden}
#dashWx .wx-day .wxt,#dashWx .wx-day .wxn{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media (min-width:821px){
  .home-top[data-order="wx"] #dashWx .wx-day:nth-child(n+6){display:none}
}

/* ===== v70：手机端概览并排 + 深色模式补漏 ===== */

/* v70：手机端概览「天气 / 今天」两卡并排（不再整列上下堆叠），
   公告、未来 7 天等非 dash 卡仍整行占满；天气卡压缩为 3 列、隐藏天气文案。 */
/* v72：用户反馈手机上「看不到天气」——恢复 .wxn 天气文字显示但保持紧凑
   （9.5px + nowrap + ellipsis，长天气名如「雷阵雨转多云」截断省略），padding 再收紧。 */
@media (max-width:820px){
  /* v74.1：两卡宽度按内容特征区分——天气卡窄一档（0.88）、今天卡宽一档（1.12）；
     换序（data-order="task"）时列宽定义跟着互换，实现「位置换、大小也跟着换」。 */
  .home-top{grid-template-columns:minmax(0,0.88fr) minmax(0,1.12fr);gap:10px}
  .home-top[data-order="task"]{grid-template-columns:minmax(0,1.12fr) minmax(0,0.88fr)}
  .home-top>.panel:not(.dash-card){grid-column:1/-1}
  #dashWx .wxn{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  #dashWx .wx-week{grid-template-columns:repeat(3,1fr);gap:4px}
  #dashWx .wx-day{padding:5px 2px}
  #dashWx .wx-day .wxi{font-size:16px}
  #dashWx .wx-day .wxt{font-size:12px}
}

/* v70：深色模式补漏 —— 上面 v66/v67/v69 新增的工作台/周排班网格/取消留档/选人筛选条
   仍写死近白底或深色文字，深色主题下会「白底浅字」或深字看不清，这里统一换深色。 */
[data-theme="dark"] .canceled-row .ttl{text-decoration-color:#5a6780}
[data-theme="dark"] .tk-pick select{background:#161b2c;color:var(--tx)}
[data-theme="dark"] .checks .chk.busy span em{color:#f87171}
[data-theme="dark"] .wb66-todobar{background:linear-gradient(90deg,rgba(91,141,239,.14),rgba(91,141,239,.05));border-color:rgba(91,141,239,.30)}
[data-theme="dark"] .wb66-head{background:#161b2c;border-color:var(--line2)}
[data-theme="dark"] .wb66-prog{background:#232a3d}
[data-theme="dark"] .wb66-step{background:#161b2c;border-color:var(--line2);color:var(--tx2)}
[data-theme="dark"] .wb66-step .dot{background:#3a4560}
[data-theme="dark"] .wb66-dept.full{background:rgba(26,74,48,.45);border-color:rgba(52,199,123,.45)}
[data-theme="dark"] .wb66-mini{background:#232a3d}
[data-theme="dark"] .wb66-chip{background:#232a3d;color:#b8c6e8;border-color:#2a3245}
[data-theme="dark"] .wb66-reject{background:#2a1818;color:#ff9b97;border-color:#5a2a28}
[data-theme="dark"] .wsgrid thead th,
[data-theme="dark"] .wsgrid .wsg-th,
[data-theme="dark"] .wsgrid-m thead th,
[data-theme="dark"] .wsgrid-m .wsg-t{background:#161b2c;color:var(--tx2)}
[data-theme="dark"] .wsgrid .wsg-c:hover{background:#1e2638}
[data-theme="dark"] .wsgrid .wsg-c.on{background:#1b2540;box-shadow:inset 0 0 0 2px var(--pri)}
[data-theme="dark"] .wsgrid .wsg-c.empty,
[data-theme="dark"] .wsgrid-m .wsg-c.empty{background:#141a28;color:#64748b}
[data-theme="dark"] .wsgrid .wsg-ph{color:#64748b}
[data-theme="dark"] .wsgrid .wsg-it,
[data-theme="dark"] .wsgrid-m .wsg-it{color:#9bc4ff}
[data-theme="dark"] .wsgrid .wsg-u{background:#232a3d;color:#b8c6e8}
[data-theme="dark"] .wsgrid-m .wsg-u{background:#232a3d;color:#b8c6e8}
[data-theme="dark"] .wsgrid-m .wsg-u.ok{background:#102a1a;color:#6beab0}
[data-theme="dark"] .wsgrid-m .wsg-u.no{background:#2a1818;color:#ff9b97}
[data-theme="dark"] .wsedit-head b{color:#b8c6e8}

/* ===== v70b：学校/年级/班级选项管理（停用态 + chip-x 停用/恢复小钮）===== */
.chip.off{opacity:.58;text-decoration:line-through}
.chip-x{display:inline-grid;place-items:center;width:17px;height:17px;border:0;border-radius:50%;
  background:rgba(127,127,127,.18);color:var(--tx2);font-size:12px;line-height:1;cursor:pointer;
  flex:none;padding:0;margin-left:2px}
.chip-x:hover{background:var(--pri-soft);color:var(--pri)}
[data-theme="dark"] .chip-x{background:rgba(255,255,255,.13);color:var(--tx2)}
[data-theme="dark"] .chip-x:hover{background:var(--pri-soft);color:var(--pri)}
[data-theme="dark"] .chip.off{opacity:.62}

/* ===== v72：内置音乐播放器 + mini 播放条 ===== */
.mu-now{display:flex;align-items:center;gap:12px;margin-bottom:12px}
.mu-cover{width:64px;height:64px;border-radius:var(--r-md);object-fit:cover;flex:none;
  background:var(--pri-soft);box-shadow:var(--sh)}
.mu-cover.ph{display:grid;place-items:center;font-size:28px}
.mu-mi{flex:1;min-width:0}
.mu-mi .mu-t{font-weight:500;font-size:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mu-mi .mu-a{color:var(--tx3);font-size:13px;margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mu-ctrl{margin-bottom:12px}
.mu-ctrl input[type=range]{width:100%}
.mu-times{display:flex;justify-content:space-between;color:var(--tx3);font-size:12px;margin:2px 0 6px}
/* v73（F3）：控制区两行结构 —— 第一行 ⏮ ▶ ⏭ 居中（垂直居中对齐），第二行 循环+音量左右分布 */
.mu-btns{display:flex;align-items:center;justify-content:center;gap:16px}
.mu-sub{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:10px}
.mu-b{border:1px solid var(--line);background:var(--card);width:40px;height:40px;border-radius:50%;
  font-size:16px;cursor:pointer;color:var(--tx);transition:.15s;flex:none}
.mu-b:hover{border-color:var(--pri);color:var(--pri)}
.mu-b.mu-play{width:52px;height:52px;font-size:20px;background:var(--pri);border-color:var(--pri);color:#fff}
.mu-b.mu-play:hover{color:#fff;filter:brightness(1.08)}
.mu-vol{display:inline-flex;align-items:center;gap:6px;color:var(--tx3);font-size:13px}
.mu-vol input[type=range]{width:90px}
.mu-list-head{font-weight:500;font-size:13px;margin:6px 0 8px}
.mu-list-head em{font-style:normal;color:var(--tx3);font-weight:400;font-size:12px;margin-left:4px}
.mu-list{max-height:320px;overflow:auto;border:1px solid var(--line);border-radius:var(--r-md);padding:6px}
.mu-row{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:var(--r-md)}
.mu-row.on{background:var(--pri-soft)}
.mu-rm{flex:1;min-width:0}
.mu-rt{font-weight:500;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mu-row.on .mu-rt{color:var(--pri)}
.mu-now-tag{font-style:normal;color:var(--pri);margin-right:4px}
.mu-ra{color:var(--tx3);font-size:12px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mu-rbtns{flex:none;display:flex;gap:6px}
.mu-add{margin-top:14px;padding-top:12px;border-top:1px solid var(--line2)}
/* v74：内置歌迁移提示条 + 歌单导入行 */
.mu-warn{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:13px;color:#8a5a00;
  background:#fff7e0;border:1px solid #f0dca8;border-radius:var(--r-md);padding:8px 10px;margin-bottom:8px}
.mu-imp-row{display:flex;align-items:center;gap:8px;margin-top:4px;flex-wrap:wrap}
[data-theme="dark"] .mu-warn{background:#2a2210;border-color:#4a3d1a;color:#e8c477}
/* v74.1：音乐页版权提示「过一段时间自己消失」——8 秒淡出，点「我知道了」永久隐藏 */
.mu-tip{transition:opacity 1s}
.mu-tip.mu-tip-fade{opacity:0;pointer-events:none}
.mu-builtin{font-style:normal;font-size:12px;color:var(--tx3);border:1px solid var(--line);
  border-radius:var(--r-md);padding:0 4px;margin-left:5px;vertical-align:1px}
[data-theme="dark"] .mu-builtin{border-color:var(--line2)}
/* mini 播放条：全局固定，手机端位于 tabbar 上方 */
#musicMini{position:fixed;left:12px;right:12px;bottom:14px;z-index:118;
  display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:var(--r-md);
  background:rgba(255,255,255,.97);backdrop-filter:blur(12px);
  border:1px solid var(--line);box-shadow:0 4px 20px rgba(20,30,60,.14);transition:opacity .2s}
#musicMini.hidden{display:none}
#musicMini .mm-info{flex:1;min-width:0;cursor:pointer}
#musicMini .mm-t{font-weight:500;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#musicMini .mm-a{color:var(--tx3);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#musicMini .mm-btn{border:0;background:#f1f4fa;width:34px;height:34px;border-radius:50%;
  font-size:14px;cursor:pointer;color:var(--tx);flex:none}
[data-theme="dark"] #musicMini .mm-btn{background:#1e2638;color:var(--tx2)}
#musicMini .mm-btn:hover{background:var(--pri-soft);color:var(--pri)}
[data-theme="dark"] .mu-b{background:#161b2c;border-color:var(--line2)}
[data-theme="dark"] .mu-b.mu-play{background:var(--pri);border-color:var(--pri);color:#fff}
[data-theme="dark"] .mu-list{border-color:var(--line2)}
[data-theme="dark"] .mu-row.on{background:#1b2540}
[data-theme="dark"] #musicMini{background:rgba(26,32,48,.97);border-color:var(--line2)}
[data-theme="dark"] #musicMini .mm-btn{background:#232a3d;color:var(--tx)}
[data-theme="dark"] #musicMini .mm-btn:hover{background:#1b2540;color:var(--pri)}
[data-theme="dark"] .mu-cover.ph{background:#1b2540}
@media(max-width:760px){
  #musicMini{left:8px;right:8px;bottom:calc(70px + env(safe-area-inset-bottom,0px) + var(--android-nav-h,0px))}
  .mu-b{width:36px;height:36px;font-size:14px}
  .mu-b.mu-play{width:46px;height:46px;font-size:16px}
  /* v73（F3）：手机端隐藏软件内音量条（系统音量足够），循环按钮占满第二行 */
  .mu-vol{display:none}
  .mu-sub{justify-content:flex-start}
  .mu-list{max-height:240px}
}

/* ==================== v77.3：日历增强（tab 图标 / 农历 / 四季主题 / 四季开屏） ==================== */

/* —— ① 底部「日历」tab：动态日历图标（今天日期数字 + 小号星期字） —— */
.ti-cal-svg{width:22px;height:22px;display:block}
.ti-cal-body{fill:var(--card);stroke:var(--tx);stroke-width:1.5;stroke-linejoin:round}
.tabbtn.active .ti-cal-body{stroke:var(--pri)}
.ti-cal-band{fill:url(#calBandWinter)}
.ti-cal-pin{fill:none;stroke:var(--tx);stroke-width:1.4;stroke-linecap:round}
.tabbtn.active .ti-cal-pin{stroke:var(--pri)}
.ti-cal-day{font-size:7.6px;font-weight:800;fill:var(--tx);font-family:inherit}
.tabbtn.active .ti-cal-day{fill:var(--pri)}
.ti-cal-week{font-size:3.6px;font-weight:500;fill:var(--tx2,#5a6478);font-family:inherit;letter-spacing:.2px}
[data-theme="dark"] .ti-cal-body{fill:#161b2c}
[data-theme="dark"] .ti-cal-day{fill:#e8ecf7}
[data-theme="dark"] .tabbtn.active .ti-cal-day{fill:var(--pri)}
[data-theme="dark"] .ti-cal-week{fill:#aab3c9}

/* —— ①b v77.5 T1：日历 tab 头条按季节渐变 + 日期/星期跟着季节主色 —— */
.tabbtn[data-season="spring"] .ti-cal-band{fill:url(#calBandSpring)}
.tabbtn[data-season="summer"] .ti-cal-band{fill:url(#calBandSummer)}
.tabbtn[data-season="autumn"] .ti-cal-band{fill:url(#calBandAutumn)}
.tabbtn[data-season="winter"] .ti-cal-band{fill:url(#calBandWinter)}
.tabbtn[data-season="spring"] .ti-cal-day{fill:#d6456b}
.tabbtn[data-season="summer"] .ti-cal-day{fill:#0f8a6d}
.tabbtn[data-season="autumn"] .ti-cal-day{fill:#d97a29}
.tabbtn[data-season="winter"] .ti-cal-day{fill:#3a6fd8}
.tabbtn[data-season="spring"] .ti-cal-week{fill:#d6456b;opacity:.72}
.tabbtn[data-season="summer"] .ti-cal-week{fill:#0f8a6d;opacity:.72}
.tabbtn[data-season="autumn"] .ti-cal-week{fill:#d97a29;opacity:.72}
.tabbtn[data-season="winter"] .ti-cal-week{fill:#3a6fd8;opacity:.72}
/* 深色模式：日期/星期保持浅色可读（季节主色偏深，在深底上对比度差） */
[data-theme="dark"] .tabbtn .ti-cal-day{fill:#e8ecf7}
[data-theme="dark"] .tabbtn .ti-cal-week{fill:#aab3c9}

/* —— ② 月视图格子 / 日面板：农历小字 —— */
.cell>.lunar{display:block;font-size:12px;line-height:1.15;color:var(--tx3,#8b93a7);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cell>.lunar.hl{color:#e4544f;font-weight:500}
.cell.sel>.lunar{color:var(--tx2,#5a6478)}
.cell.sel>.lunar.hl{color:#e4544f}
.day-info .lunar-full{font-weight:500}
.day-info .lunar-full.hl{color:#e4544f}
[data-theme="dark"] .cell>.lunar{color:#8b93a7}
[data-theme="dark"] .cell>.lunar.hl{color:#ff8fa3}
[data-theme="dark"] .day-info .lunar-full.hl{color:#ff8fa3}
[data-theme="dark"] .cell.sel>.lunar{color:#aab3c9}

/* —— ③ 日历四季主题：挂在 .view[data-view=calendar] 的 data-cal-theme 属性上。
      classic = 默认配色，不写覆盖；其余四套只动日历页（头部渐变 / 选中格 / accent / 农历文字色）。 —— */
[data-cal-theme="spring"] .cal-panel>.panel-head,
[data-cal-theme="spring"] .day-panel>.panel-head{background:linear-gradient(135deg,#ffe1ee,#fff3f8)}
[data-cal-theme="spring"] .month .cell.sel{background:#ffe6f0;border-color:#f5a8c6}
[data-cal-theme="spring"] .day-panel .day-big{color:#d6456b}
[data-cal-theme="spring"] .cell>.lunar{color:#c0567e}
[data-cal-theme="spring"] .cell>.lunar.hl{color:#d6456b}
[data-cal-theme="summer"] .cal-panel>.panel-head,
[data-cal-theme="summer"] .day-panel>.panel-head{background:linear-gradient(135deg,#dff5ee,#f0fbf7)}
[data-cal-theme="summer"] .month .cell.sel{background:#dcf5ec;border-color:#7cc9b3}
[data-cal-theme="summer"] .day-panel .day-big{color:#0f8a6d}
[data-cal-theme="summer"] .cell>.lunar{color:#2f8a72}
[data-cal-theme="summer"] .cell>.lunar.hl{color:#0f8a6d}
[data-cal-theme="autumn"] .cal-panel>.panel-head,
[data-cal-theme="autumn"] .day-panel>.panel-head{background:linear-gradient(135deg,#ffe8d9,#fff6ee)}
[data-cal-theme="autumn"] .month .cell.sel{background:#ffe9d9;border-color:#f0b283}
[data-cal-theme="autumn"] .day-panel .day-big{color:#d97a29}
[data-cal-theme="autumn"] .cell>.lunar{color:#b06a2c}
[data-cal-theme="autumn"] .cell>.lunar.hl{color:#d97a29}
[data-cal-theme="winter"] .cal-panel>.panel-head,
[data-cal-theme="winter"] .day-panel>.panel-head{background:linear-gradient(135deg,#e3edff,#f4f8ff)}
[data-cal-theme="winter"] .month .cell.sel{background:#e2ecff;border-color:#9db8ec}
[data-cal-theme="winter"] .day-panel .day-big{color:#3a6fd8}
[data-cal-theme="winter"] .cell>.lunar{color:#5a7dc0}
[data-cal-theme="winter"] .cell>.lunar.hl{color:#3a6fd8}
/* 四季主题 × 深色模式：头部换深色渐变，文字色提亮 */
[data-theme="dark"][data-cal-theme="spring"] .cal-panel>.panel-head,
[data-theme="dark"][data-cal-theme="spring"] .day-panel>.panel-head{background:linear-gradient(135deg,#3a2430,#241c26)}
[data-theme="dark"][data-cal-theme="spring"] .month .cell.sel{background:#3a2530;border-color:#a05a76}
[data-theme="dark"][data-cal-theme="summer"] .cal-panel>.panel-head,
[data-theme="dark"][data-cal-theme="summer"] .day-panel>.panel-head{background:linear-gradient(135deg,#16332c,#10231e)}
[data-theme="dark"][data-cal-theme="summer"] .month .cell.sel{background:#16332c;border-color:#3f8a74}
[data-theme="dark"][data-cal-theme="autumn"] .cal-panel>.panel-head,
[data-theme="dark"][data-cal-theme="autumn"] .day-panel>.panel-head{background:linear-gradient(135deg,#3a2a1a,#241c12)}
[data-theme="dark"][data-cal-theme="autumn"] .month .cell.sel{background:#38281a;border-color:#a06a3c}
[data-theme="dark"][data-cal-theme="winter"] .cal-panel>.panel-head,
[data-theme="dark"][data-cal-theme="winter"] .day-panel>.panel-head{background:linear-gradient(135deg,#1c2a4a,#141c30)}
[data-theme="dark"][data-cal-theme="winter"] .month .cell.sel{background:#1c2a4a;border-color:#5a7dc0}
[data-theme="dark"][data-cal-theme="spring"] .day-panel .day-big{color:#ff9db8}
[data-theme="dark"][data-cal-theme="summer"] .day-panel .day-big{color:#6beab0}
[data-theme="dark"][data-cal-theme="autumn"] .day-panel .day-big{color:#ffb066}
[data-theme="dark"][data-cal-theme="winter"] .day-panel .day-big{color:#9bc4ff}

/* —— ③b v77.5 T2：四季主题轻量季节装饰（静态、克制，不干扰信息） —— */
[data-cal-theme="spring"] .cal-panel{position:relative}
[data-cal-theme="spring"] .cal-panel::before{content:"🌸";position:absolute;right:12px;bottom:8px;font-size:60px;line-height:1;opacity:.10;pointer-events:none}
[data-cal-theme="spring"] .cal-panel .panel-head::after{content:"🌸";font-size:16px;margin-left:2px;line-height:1}
[data-cal-theme="spring"] .day-panel .day-title::before{content:"🌸 ";font-size:14px}
[data-cal-theme="summer"] .cal-panel{position:relative}
[data-cal-theme="summer"] .cal-panel::before{content:"🍃";position:absolute;right:12px;bottom:8px;font-size:60px;line-height:1;opacity:.10;pointer-events:none}
[data-cal-theme="summer"] .cal-panel .panel-head::after{content:"🍃";font-size:16px;margin-left:2px;line-height:1}
[data-cal-theme="summer"] .day-panel .day-title::before{content:"🍃 ";font-size:14px}
[data-cal-theme="autumn"] .cal-panel{position:relative}
[data-cal-theme="autumn"] .cal-panel::before{content:"🍁";position:absolute;right:12px;bottom:8px;font-size:60px;line-height:1;opacity:.10;pointer-events:none}
[data-cal-theme="autumn"] .cal-panel .panel-head::after{content:"🍁";font-size:16px;margin-left:2px;line-height:1}
[data-cal-theme="autumn"] .day-panel .day-title::before{content:"🍁 ";font-size:14px}
[data-cal-theme="winter"] .cal-panel{position:relative}
[data-cal-theme="winter"] .cal-panel::before{content:"❄️";position:absolute;right:12px;bottom:8px;font-size:60px;line-height:1;opacity:.10;pointer-events:none}
[data-cal-theme="winter"] .cal-panel .panel-head::after{content:"❄️";font-size:16px;margin-left:2px;line-height:1}
[data-cal-theme="winter"] .day-panel .day-title::before{content:"❄️ ";font-size:14px}

/* —— ④ 开场动画 · 四季版 —— */
.intro-ov.intro-spring{background:linear-gradient(160deg,#ffdbe9 0%,#fff0f6 100%)}
.intro-ov.intro-spring .intro-logo,.intro-ov.intro-spring .intro-slogan{color:#d6456b;text-shadow:none}
.intro-ov.intro-summer{background:linear-gradient(160deg,#0f9b8e 0%,#67c6b0 100%)}
.intro-ov.intro-autumn{background:linear-gradient(160deg,#f7b267 0%,#ef6c3f 100%)}
.intro-ov.intro-winter{background:linear-gradient(160deg,#4a7ec4 0%,#a7c7ee 100%)}
/* ★ v77.6 T1：随季节开屏 —— 当季立绘做背景（绝对定位铺满），粒子/logo/slogan 叠其上 */
.intro-ov.intro-season .intro-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.intro-fx-spring i{width:10px;height:10px;border-radius:60% 0 60% 0;background:#f7a8c4;opacity:.85;animation:introFall linear infinite}
.intro-fx-autumn i{width:13px;height:9px;border-radius:2px 60% 2px 60%;background:#e8963c;opacity:.85;animation:introFall linear infinite}
.intro-fx-winter i{width:7px;height:7px;border-radius:50%;background:#eef6ff;opacity:.9;animation:introSnow linear infinite}
@keyframes introSnow{0%{transform:translateY(-10vh) translateX(0)}50%{transform:translateY(50vh) translateX(24px)}100%{transform:translateY(110vh) translateX(-12px)}}
.intro-fx-summer i{top:auto;bottom:-26px;width:8px;height:8px;border-radius:50%;background:rgba(180,240,220,.7);border:1px solid rgba(120,210,180,.9);animation:introRise linear infinite}
@keyframes introRise{0%{transform:translateY(0) scale(.7);opacity:.35}100%{transform:translateY(-115vh) scale(1.2);opacity:.95}}

/* ==========================================================================
   ★ v79.5 开屏动画 · 秋·落叶飘零 强化 + 当季小物件
   --------------------------------------------------------------------------
   ① introFall2 / introRise2 / introSnow2 —— 第二套飘法。
      introParts() 里约 45% 的粒子会随机改用这套（inline animation-name 覆盖），
      原来的 introFall 是「直着往下 + 自转」，这一套加了横向摆幅和不同的旋转方向，
      两套混在一起才有「被风吹乱」的层次感，不会像复制粘贴的一排。
   ========================================================================== */
@keyframes introFall2{
  0%{transform:translateY(-10vh) translateX(0) rotate(0deg)}
  35%{transform:translateY(28vh) translateX(26px) rotate(150deg)}
  70%{transform:translateY(66vh) translateX(-14px) rotate(280deg)}
  100%{transform:translateY(110vh) translateX(10px) rotate(420deg)}
}
@keyframes introRise2{
  0%{transform:translateY(0) translateX(0) scale(.6);opacity:.25}
  50%{transform:translateY(-58vh) translateX(18px) scale(1)}
  100%{transform:translateY(-115vh) translateX(-10px) scale(1.35);opacity:.9}
}
@keyframes introSnow2{
  0%{transform:translateY(-10vh) translateX(0)}
  50%{transform:translateY(50vh) translateX(-28px)}
  100%{transform:translateY(110vh) translateX(16px)}
}

/* --------------------------------------------------------------------------
   ② 当季小物件 .intro-props
      开屏只活 2.6 秒，做成可点的会跟「跳过」按钮抢点击，所以整层 pointer-events:none。
      位置压在底部两侧（吉祥物在中间偏下，小物件左右各一），大屏好看、小屏不挡字。
   -------------------------------------------------------------------------- */
.intro-props{position:absolute;inset:0;pointer-events:none;z-index:1}
.iprop{position:absolute;bottom:11vh;width:104px;height:104px;
  filter:drop-shadow(0 6px 14px rgba(0,0,0,.22));
  animation:introPropIn .9s .2s cubic-bezier(.2,.9,.3,1.3) both}
.iprop svg{width:100%;height:100%;display:block;overflow:visible}
.ip-l{left:7vw;animation-delay:.18s}
.ip-r{right:7vw;animation-delay:.34s}
@keyframes introPropIn{
  0%{opacity:0;transform:translateY(26px) scale(.82)}
  100%{opacity:1;transform:translateY(0) scale(1)}
}
/* 柳枝 / 枝头：随风摆（transform-origin 在枝根，摆起来才像被风吹） */
.ip-sway,.ip-sway2{fill:none;stroke:#7fb069;stroke-width:2.4;stroke-linecap:round}
.ip-sway{animation:ipSway 2.4s ease-in-out infinite;transform-origin:20px 30px}
/* 枫叶那组是实心色块（path 自带 fill），这里只管摆动，不要再套 stroke */
.ip-sway2{stroke:none;animation:ipSway 2.8s ease-in-out infinite;transform-origin:50px 62px}
@keyframes ipSway{0%,100%{transform:rotate(-3.5deg)}50%{transform:rotate(4.5deg)}}
/* 荷花：开屏内缓缓绽一下 */
.ip-bloom{animation:ipBloom 2.6s ease-in-out infinite;transform-origin:56px 54px}
@keyframes ipBloom{0%,100%{transform:scale(.9)}50%{transform:scale(1.06)}}
/* 热茶：三缕水汽往上飘 */
.ip-steam{fill:none;stroke:#fff;stroke-width:2.6;stroke-linecap:round;opacity:.85;
  animation:ipSteam 2.2s ease-in-out infinite}
.ip-steam path:nth-child(2){animation-delay:.35s}
.ip-steam path:nth-child(3){animation-delay:.7s}
@keyframes ipSteam{0%{opacity:0;transform:translateY(4px)}40%{opacity:.9}100%{opacity:0;transform:translateY(-10px)}}
/* 灯笼：暖光呼吸 */
.ip-glow{animation:ipGlow 2s ease-in-out infinite;transform-origin:50px 54px}
@keyframes ipGlow{0%,100%{opacity:.16;transform:scale(.95)}50%{opacity:.42;transform:scale(1.1)}}

/* --------------------------------------------------------------------------
   ③ 随季节·飘零版 .intro-seasonfx
      与 intro-season（立绘华丽版）的区别：这里**不用大图**，整屏纯 CSS 渐变 + 粒子，
      所以冷启动更快、零流量、老机器也不卡。v79.5 新加。
   -------------------------------------------------------------------------- */
.intro-ov.intro-seasonfx{background:linear-gradient(160deg,#3b6ef5 0%,#22a06b 100%)}
.intro-ov.intro-seasonfx.intro-spring{background:linear-gradient(160deg,#ffe3ee 0%,#eaf6ff 55%,#fff7fb 100%)}
.intro-ov.intro-seasonfx.intro-summer{background:linear-gradient(160deg,#0b8f86 0%,#2fb3a0 45%,#8fd8c4 100%)}
.intro-ov.intro-seasonfx.intro-autumn{background:linear-gradient(160deg,#f7b267 0%,#ef6c3f 55%,#c9541f 100%)}
.intro-ov.intro-seasonfx.intro-winter{background:linear-gradient(160deg,#3f6cb5 0%,#7fa5d8 55%,#dce9f8 100%)}
/* 飘零版靠渐变出「纵深」，logo/slogan 统一白字加投影，四季都看得清 */
.intro-ov.intro-seasonfx .intro-logo,.intro-ov.intro-seasonfx .intro-slogan{color:#fff;
  text-shadow:0 4px 18px rgba(0,0,0,.32)}

@media (max-width:760px){
  .iprop{width:78px;height:78px;bottom:13vh}
  .ip-l{left:4vw}
  .ip-r{right:4vw}
}
@media (prefers-reduced-motion:reduce){
  .iprop,.ip-sway,.ip-sway2,.ip-bloom,.ip-steam,.ip-glow{animation:none}
  .ip-steam{opacity:.7}
}

/* —— ⑤ v77.5：默认开屏在 mascot / 四季主题下改走当季立绘（season/*.jpg），
     不再用 CSS 吉祥物渐变（v77.4 的 .intro-mascot 已移除）。 —— */

/* ==========================================================================
   ★ v77.9 ① 设置页「保存设置」工具条
   ========================================================================== */
.set-savebar{position:sticky;top:56px;z-index:20;display:flex;align-items:center;gap:10px;
  margin-bottom:12px;padding:8px 12px;border-radius:var(--r-md);border:1px solid var(--line);
  background:var(--card);box-shadow:0 1px 3px rgba(15,23,42,.06)}
.set-savebar .ss-dot{width:8px;height:8px;border-radius:50%;background:#22a06b;flex:none}
.set-savebar .ss-tip{flex:1;min-width:0;font-size:12px;color:var(--tx2)}
.set-savebar .ss-acts{display:flex;gap:8px;flex-wrap:wrap;flex:none}
.set-savebar.dirty{border-color:#f0913a;background:#fff8ee}
[data-theme="dark"] .set-savebar.dirty{background:#2a2010}
.set-savebar.dirty .ss-dot{background:#f0913a;animation:ssBlink 1s ease-in-out infinite}
.set-savebar.dirty .ss-tip{color:#b4600f;font-weight:500}
@keyframes ssBlink{50%{opacity:.3}}
/* 日历主题弹窗：「随季节变化」那一行的小字 */
.ctp-row.ctp-auto{border:1px dashed var(--line);margin-bottom:8px}
.ctp-row.ctp-auto.on{border-style:solid;border-color:var(--pri);background:var(--pri-soft,#f4f6fb)}
.ctp-nm .ctp-sub{display:block;font-size:12px;font-weight:400;color:var(--tx2);margin-top:2px}

/* ==========================================================================
   ★ v77.9 ② 季节装饰层（#seasonFx）—— 每个界面都有的当季元素
   整层 pointer-events:none；只有 .sfx-prop（道具）自己可点。
   ========================================================================== */
#seasonFx{position:fixed;inset:0;z-index:30;pointer-events:none;overflow:hidden}
#seasonFx .sfx-p{position:absolute;inset:0}
#seasonFx .sfx-p i{position:absolute;top:-24px;border-radius:50%;opacity:.8;will-change:transform}
/* 春：花瓣飘落 */
#seasonFx[data-season="spring"] .sfx-p i{border-radius:60% 0 60% 0;background:#f7a8c4;animation:sfxFall linear infinite}
/* 夏：气泡上浮 */
#seasonFx[data-season="summer"] .sfx-p i{background:radial-gradient(circle at 35% 35%,#ffffff,#8fdcd0);animation:sfxRise linear infinite}
/* 秋：枫叶飘落 */
#seasonFx[data-season="autumn"] .sfx-p i{border-radius:2px 60% 2px 60%;background:#e8963c;animation:sfxFall linear infinite}
/* 冬：飘雪 */
#seasonFx[data-season="winter"] .sfx-p i{background:#eef6ff;animation:sfxSnow linear infinite}
@keyframes sfxFall{0%{transform:translate3d(0,-10vh,0) rotate(0deg);opacity:0}
  12%{opacity:.85}100%{transform:translate3d(6vw,108vh,0) rotate(320deg);opacity:.9}}
@keyframes sfxSnow{0%{transform:translate3d(0,-10vh,0);opacity:0}
  12%{opacity:.9}100%{transform:translate3d(3vw,108vh,0);opacity:.9}}
@keyframes sfxRise{0%{transform:translate3d(0,110vh,0) scale(.55);opacity:.2}
  30%{opacity:.65}100%{transform:translate3d(-4vw,-10vh,0) scale(1.15);opacity:.85}}
/* 风扇 3 档「呼——」把飘浮物吹歪 */
#seasonFx[data-season="spring"].sfx-gust .sfx-p i,
#seasonFx[data-season="autumn"].sfx-gust .sfx-p i{animation-name:sfxFallGust}
#seasonFx[data-season="winter"].sfx-gust .sfx-p i{animation-name:sfxSnowGust}
#seasonFx[data-season="summer"].sfx-gust .sfx-p i{animation-name:sfxRiseGust}
@keyframes sfxFallGust{0%{transform:translate3d(0,-10vh,0) rotate(0deg);opacity:0}
  12%{opacity:.9}100%{transform:translate3d(52vw,108vh,0) rotate(560deg);opacity:.9}}
@keyframes sfxSnowGust{0%{transform:translate3d(0,-10vh,0);opacity:0}
  12%{opacity:.9}100%{transform:translate3d(58vw,108vh,0);opacity:.9}}
@keyframes sfxRiseGust{0%{transform:translate3d(0,110vh,0) scale(.55);opacity:.2}
  30%{opacity:.7}100%{transform:translate3d(-52vw,-10vh,0) scale(1.2);opacity:.85}}
/* 点道具时的一次性迸溅 */
#seasonFx .sfx-b{position:absolute;width:10px;height:10px;border-radius:50%;opacity:.95;
  animation:sfxBurst ease-out forwards}
@keyframes sfxBurst{0%{transform:translate(0,0) scale(.5);opacity:0}
  18%{opacity:1}100%{transform:translate(6px,-52vh) scale(1.1);opacity:0}}
#seasonFx .sfx-b-petal{background:#f7a8c4;border-radius:60% 0 60% 0}
#seasonFx .sfx-b-leaf{background:#e8963c;border-radius:2px 60% 2px 60%}
#seasonFx .sfx-b-snow{background:#eef6ff}
/* ★ v79.3：湿度 → 视觉。阈值沿用 mascotWxLine 那套（≥80 湿 / ≤30 干），
   在固定装饰层叠一层薄雾，pointer-events:none 保证不挡任何点击。 */
#seasonFx.sfx-humid::after,#seasonFx.sfx-dry::after{content:'';position:absolute;inset:0;pointer-events:none}
#seasonFx.sfx-humid::after{background:linear-gradient(180deg,rgba(150,190,220,.20),rgba(150,190,220,.10))}
#seasonFx.sfx-dry::after{background:linear-gradient(180deg,rgba(214,186,140,.16),rgba(214,186,140,.08))}
:root[data-theme="dark"] #seasonFx.sfx-humid::after{background:linear-gradient(180deg,rgba(118,168,220,.26),rgba(118,168,220,.12))}
:root[data-theme="dark"] #seasonFx.sfx-dry::after{background:linear-gradient(180deg,rgba(206,176,116,.20),rgba(206,176,116,.10))}
  /* ★ v79.3：时辰 → 昼夜：装饰层粒子降亮（装饰层自身，不影响其余 UI）。 */
  #seasonFx.sfx-night .sfx-p i{filter:brightness(.72) saturate(.88)}
  /* ★ v79.4：夜间变暗 —— 整页压暗层，独立于 #seasonFx（z-index:30）。
     放在 modal-mask(100) 之下，保证设置弹窗不被压暗；覆盖 topbar(40) / 切换身份栏 / 功能区等
     #seasonFx 够不到的区域。pointer-events:none 不挡点击。 */
  body.night-dim::after{content:'';position:fixed;inset:0;pointer-events:none;z-index:95;
    background:linear-gradient(180deg,rgba(10,14,30,.32),rgba(10,14,30,.18))}
  :root[data-theme="dark"] body.night-dim::after{background:linear-gradient(180deg,rgba(0,4,16,.42),rgba(0,4,16,.24))}
  /* ★ v79.5 修复：手机底部那一排（.tabbar z-index:120）、迷你播放器（#musicMini 118）、
     回到顶部浮标（.back-top 900）、安装条（.install-bar 160）都**高过**压暗层的 95，
     所以「整页压暗」唯独漏掉它们 —— 用户看到的就是「夜里底部还是亮的」。
     这里不能再靠再叠一层更高 z-index 的蒙版：那会把弹窗（modal-mask 100）也一起压暗，
     晚上填表单反而看不清。改成给这几个高层元素单独上 filter，只压它们自己。 */
  body.night-dim .tabbar{filter:brightness(.72) saturate(.88)}
  body.night-dim #musicMini{filter:brightness(.74) saturate(.9)}
  body.night-dim .back-top{filter:brightness(.74) saturate(.9)}
  body.night-dim .install-bar{filter:brightness(.76) saturate(.9)}
  :root[data-theme="dark"] body.night-dim .tabbar,
  :root[data-theme="dark"] body.night-dim #musicMini,
  :root[data-theme="dark"] body.night-dim .back-top,
  :root[data-theme="dark"] body.night-dim .install-bar{filter:brightness(.66) saturate(.86)}
  /* ★ v79.6 修复：v79.5 只补了 tabbar/musicMini/back-top/install-bar 四层，
     漏掉了同样 z 值 > 95 的「全部功能」抽屉（.sheet-wrap z=140）、弹窗遮罩
     （.modal-mask z=100）、闹钟横幅（.alarm-banner z=150）—— 用户点名「全部功能」
     那块夜里没被压暗，正是漏网。这里一并补齐。
     ⚠ 关键原则（沿用作者原注释）：**不能再叠更高 z-index 的蒙版**，否则会连带
       把弹窗里的表单也压暗。所以：
         · 抽屉 / 横幅这类「整块都该暗」的 → 直接给该层上 filter；
         · 弹窗遮罩 → v79.6 已把弹窗拆成「层容器 + 纯遮罩 + 卡片」三层
           （见 .modal-layer 注释），遮罩与卡片是兄弟节点，给遮罩单独上 filter
           就不会连带压暗 .modal 卡片，晚上填表依旧清晰可读。 */
  body.night-dim .sheet-wrap{filter:brightness(.74) saturate(.9)}
  body.night-dim .alarm-banner{filter:brightness(.76) saturate(.9)}
  :root[data-theme="dark"] body.night-dim .sheet-wrap,
  :root[data-theme="dark"] body.night-dim .alarm-banner{filter:brightness(.68) saturate(.88)}
  /* ★★ v80.9【P1-6】补 night-dim 的**漏网层**（用户：「夜间变暗，有好几个输入的地方
     就不受影响」）。这是 v79.5 → v79.6 一路在补的同一类漏网，**这次是最后一处**。

     🔴 实测漏网的两个（浏览器实测 tmp/_p16_diag3.out.txt）：
       · `.v797-memo-compose`（v797.css:100）`position:sticky; **z-index:1000**`
         ⇒ 1000 > 蒙版 95 ⇒ **压在压暗层之上**，而它又不在下面的白名单里
         ⇒ 夜里整页暗了，**它那条「一句话输入」保持全亮**。
         ⚠ 它内部就是 `.v797-memo-input`（备忘录的主输入框）——
           **用户说的「好几个输入的地方」就是这一类**。
       · `.v797-mbmenu`（v797.css:220）同 z=1000 的「⋮」菜单，同样漏网。

     ⚠⚠ 为什么**可以**给它们上 filter（这与作者上面那条「不要压暗表单」并不冲突）：
       作者排除的是**弹窗里的表单卡片**（.modal 卡片）⇒ 理由是「晚上填表依旧清晰」，
       那个取舍**继续保留、本轮不动**。
       而这两条是**页面级常驻元素**（不是弹窗），它们漏网属于「压暗没盖全」，
       与 .tabbar / .sheet-wrap / .alarm-banner **完全同类** ⇒ 同样处理。
     ⚠ 亮度只压到 .74：浅色主题下白底(255) × .74 ≈ 189(=#bdbdbd)，
       配深色文字对比度仍远高于 4.5 ⇒ **可读性不受影响**，不是「为了变暗而变暗」。
     ⚠ 选择器沿用白名单里既有的两段式：浅色一档 + dark 主题更重一档，
       与 .sheet-wrap 的写法逐字同构 ⇒ 将来若要撤，只删这 5 行。*/
  body.night-dim .v797-memo-compose,
  body.night-dim .v797-mbmenu{filter:brightness(.74) saturate(.9)}
  :root[data-theme="dark"] body.night-dim .v797-memo-compose,
  :root[data-theme="dark"] body.night-dim .v797-mbmenu{filter:brightness(.68) saturate(.88)}
  /* ★★ v80.20【修「番茄钟『开始专注』按钮夜里不变暗」】—— v79.5→v79.6→v80.9
     这一路的**最后一块漏网**。同样是「页面级常驻元素 + z>95 ⇒ 压暗盖不到它」，
     所以同样只给它自己上 filter。

     🔴⚠ 真实根因（**不是** `.v797-btn-lg` 自己有问题 —— 真浏览器
       getComputedStyle 实测，判据 smoke/_ui3_t2_night.js）：
         .v797-btn-lg      zIndex=**auto**  position=**static**  filter=none
                           background=rgb(59,110,245)（= var(--pri) 主题蓝，不是白底）
         ↑ 也就是说「按钮很刺眼」这个描述**不准确**：按钮自己是主题蓝、且**没有** z-index，
           它**不是**「浮在压暗层之上」的那一层。
         .v797-pomo-acts   zIndex=**130**  position=**sticky**  filter=none
                           background=**rgb(255,255,255)**（= var(--card) 纯白）
         ↑ **真凶是它的父容器**：v797.css:709-728 的
           `@media(max-width:760px){ .v797-pomo-acts{ position:sticky; z-index:130;
             background:var(--card); … } }` —— 移动端吸底操作条自带**白底**且
           **130 > 95**，整条（白底 + 里面的按钮）**压在压暗层之上**，
           而它不在白名单里 ⇒ 整页暗了、这条全亮。
       ⚠ 为什么「按了没反应/点了翻页」也是它：那条 z=130 本来就是
         v80b38 为了压过 #seasonFx(30) 与 .tabbar(120) 特意写的，**不能动它的 z**。

     ⚠⚠ 为什么压**父容器**而不是给 `.v797-btn-lg` 单独上 filter：
       filter 会**级联到子元素**，给父容器上 = 白底与按钮**一起**压暗，
       才是用户要的「整条跟着页面暗下去」；只压按钮会留下**一圈白底**，
       视觉上更割裂。实测 .v797-btn-lg 是 static+auto ⇒ 单独压它也压不到
       真正显眼的那个白底。

     ⚠ 亮度沿用白名单的浅色一档 .74 / dark 更重一档 .68 ——
       白底 255×.74 ≈ 189，配深色文字对比度仍远高于 4.5；
       与 .sheet-wrap(3120) 写法逐字同构 ⇒ 将来若撤只删这 4 行。
     ⚠⚠ 「filter 会不会把这条的 position:sticky 弄坏」——**实测过，答案是不会**
       （这是我动手前唯一的担心，所以专门量了，判据 smoke/_ui3_t2b_sticky.js）：
         加 filter 前：position=sticky、acts top=666 / bottom=735
         加 filter 后：position=sticky、acts top=666 / bottom=735（**逐像素相同**）
         滚到 scrollY=616 后：acts 落在 y=50..119，**仍在 tabbar(顶 815) 上方**
         ⇒ 吸底行为完好，按钮仍在视口内、落点仍命中按钮自身。
       ⚠ 所以**不需要**为此加 media 查询或降特异性 —— 想到就写、没量就断言
         是本项目栽过多次的坑，这里把读数写下来，省下后来人再怀疑一遍。 */
  body.night-dim .v797-pomo-acts{filter:brightness(.74) saturate(.9)}
  :root[data-theme="dark"] body.night-dim .v797-pomo-acts{filter:brightness(.68) saturate(.88)}
  /* 弹窗：.modal-mask 是**纯遮罩**（与卡片是兄弟节点，见 styles.css 顶部 .modal-layer 注释），
     给它单独上 filter 只压遮罩、不会连带压暗 .modal 卡片 —— 夜里填表依旧清晰。
     同时加深遮罩底色，符合夜间整体暗度。 */
  body.night-dim .modal-mask{filter:brightness(.72) saturate(.88);background:rgba(12,17,30,.6)}
  :root[data-theme="dark"] body.night-dim .modal-mask{filter:brightness(.66) saturate(.86);background:rgba(2,6,18,.7)}
  /* 其余 z>95 的全屏遮罩/横幅，按「加深底色 / 单独 filter」同样处理，
     避免夜里出现「一块亮、一块暗」的割裂感（这些层内容多为纯色覆盖，
     压暗底色即可，不会影响其上的可读内容）。 */
  body.night-dim .alarm-mask{background:rgba(8,12,24,.86)}
  body.night-dim .kick-mask{background:rgba(10,14,25,.8)}
  body.night-dim .tc-lightbox{background:rgba(8,10,18,.92)}
  :root[data-theme="dark"] body.night-dim .alarm-mask{background:rgba(4,6,14,.9)}
  :root[data-theme="dark"] body.night-dim .kick-mask{background:rgba(6,9,18,.86)}
  :root[data-theme="dark"] body.night-dim .tc-lightbox{background:rgba(4,6,12,.94)}
#seasonFx .sfx-b-cool{background:#bfe9ff}
/* ==========================================================================
   ★ v79.5 修复：当季道具「压住正文」—— 改成收纳式小面板（.sfx-dock）
   --------------------------------------------------------------------------
   实拍发现：小物件浮在页面内容上面，把「本周忙碌度」那条面板的标题盖掉了。
   一开始以为是定位值偏了，改了两轮（bottom 72→88→96）截图看**还是压着**，
   于是写了个几何探针去量，结论是：
     · 这不是「差几个像素」，而是**本质问题** —— 页面是可滚动的，
       任何 fixed 定位的浮层，滚到哪就会压住哪一段内容。45/45 个采样点
       全都落在 panel-head / h3 / panel-body 上。
     · 试过「把 #seasonFx 降到 z-index:2」—— 没用。CSS 绘制顺序里，
       z-index 为正的定位元素始终画在 static 内容之上，只有负值才会沉到
       下面，而负值会被 .panel 的不透明背景整块挡住，道具就彻底看不见了。
   ✅ 所以真正的修法是**让常驻占地变小**：默认只留一个 46px 的圆形季节钮
      （和右下角「回到顶部」同级的标准悬浮钮），点开才横向展开一排道具，
      12 秒无操作自动收起。展开是用户主动触发的、临时的，不算「盖住正文」。
   ⚠ 两个别改回去的坑：
     ① 容器叫 .sfx-dock，不能叫 .sfx-props —— 点击分发是「向上找 className
        含 sfx-prop 的节点」，.sfx-props 会被前缀误命中，收纳钮收不到点击；
     ② 层本身保持 z-index:30（见上：降到 2 无效，降到负数会看不见）。
   ========================================================================== */
/* 收纳式小面板：常驻只有一个圆钮 */
#seasonFx .sfx-dock{position:absolute;left:12px;bottom:22px;display:flex;align-items:flex-end;gap:8px}
#seasonFx .sfx-dock-row{display:flex;align-items:flex-end;gap:8px;opacity:0;
  transform:translateX(-12px) scale(.88);transform-origin:0 100%;pointer-events:none;
  transition:opacity .18s ease,transform .22s cubic-bezier(.2,.9,.3,1.25)}
#seasonFx .sfx-dock.open .sfx-dock-row{opacity:1;transform:none;pointer-events:auto}
/* ★ 整层 #seasonFx 是 pointer-events:none（不挡正文点击），所以收纳钮要显式开回来 */
#seasonFx .sfx-dock-btn{width:46px;height:46px;flex:0 0 auto;border-radius:50%;padding:0;
  pointer-events:auto;border:1px solid var(--line);background:var(--card);color:var(--tx);cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 4px 14px rgba(20,30,60,.16);transition:transform .18s ease,box-shadow .18s ease}
#seasonFx .sfx-dock-btn:hover{transform:translateY(-2px)}
#seasonFx .sfx-dock-btn:active{transform:scale(.92)}
#seasonFx .sfx-dock.open .sfx-dock-btn{box-shadow:0 6px 18px rgba(20,30,60,.24)}
#seasonFx .sfx-dock-ico{width:26px;height:26px;display:block}
#seasonFx .sfx-prop{position:relative;width:56px;height:56px;pointer-events:auto;cursor:pointer;
  filter:drop-shadow(0 2px 4px rgba(15,23,42,.18));transition:transform .18s ease}
#seasonFx .sfx-prop:hover{transform:translateY(-3px)}
#seasonFx .sfx-prop:active{transform:scale(.93)}
#seasonFx .sfx-svg{width:100%;height:100%;display:block}
/* 手机端：抬到标签栏之上；正在放音乐时再抬一档（mini 条是全宽的，会撞） */
@media (max-width:760px){
  #seasonFx .sfx-dock{left:8px;bottom:calc(72px + env(safe-area-inset-bottom,0px) + var(--android-nav-h,0px));gap:6px}
  #seasonFx .sfx-dock-btn{width:42px;height:42px}
  #seasonFx .sfx-dock-ico{width:24px;height:24px}
  #seasonFx .sfx-dock-row{gap:6px}
  #seasonFx .sfx-prop{width:48px;height:48px}
}
body.mu-on #seasonFx .sfx-dock{bottom:86px}
@media (max-width:760px){
  body.mu-on #seasonFx .sfx-dock{bottom:calc(136px + env(safe-area-inset-bottom,0px) + var(--android-nav-h,0px))}
}
/* 道具头顶的小气泡（不用 toast，避免刷屏） */
#seasonFx .sfx-bubble{position:absolute;left:50%;top:-6px;transform:translate(-50%,0) scale(.9);
  background:rgba(255,255,255,.96);color:#33415c;font-size:12px;line-height:1.5;padding:3px 8px;
  border-radius:var(--r-md);white-space:nowrap;box-shadow:0 2px 6px rgba(15,23,42,.2);
  opacity:0;pointer-events:none;transition:opacity .18s ease,transform .18s ease}
#seasonFx .sfx-bubble.show{opacity:1;transform:translate(-50%,-12px) scale(1)}
[data-theme="dark"] #seasonFx .sfx-bubble{background:#2a3446;color:#e6edf7}
/* —— 各道具的动画 —— */
.sfx-blades{transform-box:view-box;transform-origin:50px 44px;animation:sfxSpin 1.2s linear infinite}
#seasonFx .sfx-prop[data-lv="2"] .sfx-blades{animation-duration:.6s}
#seasonFx .sfx-prop[data-lv="3"] .sfx-blades{animation-duration:.3s}
@keyframes sfxSpin{to{transform:rotate(360deg)}}
.sfx-sway{transform-box:view-box;transform-origin:50px 0;animation:sfxSway 4.6s ease-in-out infinite}
.sfx-maple{transform-box:view-box;transform-origin:50px 60px;animation:sfxSway 5.4s ease-in-out infinite}
@keyframes sfxSway{0%,100%{transform:rotate(-3deg)}50%{transform:rotate(3deg)}}
.sfx-wstr{fill:none;stroke:#79c06a;stroke-width:2.2;stroke-linecap:round}
.sfx-lotus{transform-box:view-box;transform-origin:54px 56px;transition:transform .5s ease}
#seasonFx .sfx-prop.sfx-bloom .sfx-lotus{transform:scale(1.25) rotate(6deg)}
.sfx-ac-temp{font-weight:500}
.sfx-ac-air path{fill:none;stroke:#9dd7ff;stroke-width:3;stroke-linecap:round;opacity:0;transform-box:view-box}
#seasonFx .sfx-prop.sfx-ac-on .sfx-ac-air path{animation:sfxAir 1.5s linear infinite}
#seasonFx .sfx-prop.sfx-ac-on .sfx-ac-air path:nth-child(2){animation-delay:.35s}
#seasonFx .sfx-prop.sfx-ac-on .sfx-ac-air path:nth-child(3){animation-delay:.7s}
@keyframes sfxAir{0%{opacity:0;transform:translateY(-4px)}30%{opacity:.9}100%{opacity:0;transform:translateY(18px)}}
#seasonFx .sfx-prop.sfx-shake{animation:sfxShake .8s ease}
@keyframes sfxShake{0%,100%{transform:translateX(0) rotate(0)}
  20%{transform:translateX(-4px) rotate(-6deg)}45%{transform:translateX(4px) rotate(6deg)}
  70%{transform:translateX(-3px) rotate(-4deg)}}
/* 春天偶尔掠过的燕子（旧 emoji 版，保留给存量缓存的 HTML；新画的是 SVG 燕子，见下） */
#seasonFx .sfx-bird{position:absolute;top:12%;left:-12%;font-size:22px;opacity:.85;animation:sfxFly 26s linear infinite}
@keyframes sfxFly{0%{left:-12%;top:10%}50%{top:22%}100%{left:108%;top:13%}}
/* ---------------- ★ v79.5 新道具的动画 ---------------- */
/* 春 · 燕子（SVG）：平时轻轻扇翅，点一下连扇四下 */
.sfx-flap{transform-box:view-box;transform-origin:50px 50px;animation:sfxFlapIdle 2.6s ease-in-out infinite}
@keyframes sfxFlapIdle{0%,100%{transform:rotate(-3deg)}50%{transform:rotate(4deg)}}
#seasonFx .sfx-prop.sfx-flap-on .sfx-flap{animation:sfxFlap .32s ease-in-out 4}
@keyframes sfxFlap{0%,100%{transform:rotate(-10deg) translateY(0)}50%{transform:rotate(12deg) translateY(-3px)}}
/* 春 · 纸鸢：点一下开始「乘风」上下摆 */
.sfx-kite{transform-box:view-box;transform-origin:50px 44px}
#seasonFx .sfx-prop.sfx-fly .sfx-kite{animation:sfxKite 3.4s ease-in-out infinite}
@keyframes sfxKite{0%,100%{transform:translate(0,0) rotate(-5deg)}
  35%{transform:translate(7px,-12px) rotate(7deg)}70%{transform:translate(-3px,-4px) rotate(-2deg)}}
/* 夏 · 西瓜：点一下「切开了」（籽变淡） */
.sfx-seeds{transition:opacity .25s ease}
#seasonFx .sfx-prop.sfx-cut .sfx-seeds{opacity:.2}
/* 秋 · 热茶：点一下冒热气 */
.sfx-steam path{fill:none;stroke:#f1d7b4;stroke-width:2.6;stroke-linecap:round;
  opacity:0;transform-box:view-box}
#seasonFx .sfx-prop.sfx-hot .sfx-steam path{animation:sfxSteam 2.2s ease-out infinite}
#seasonFx .sfx-prop.sfx-hot .sfx-steam path:nth-child(2){animation-delay:.5s}
#seasonFx .sfx-prop.sfx-hot .sfx-steam path:nth-child(3){animation-delay:1s}
@keyframes sfxSteam{0%{opacity:0;transform:translateY(4px) scaleY(.8)}
  35%{opacity:.85}100%{opacity:0;transform:translateY(-14px) scaleY(1.15)}}
/* 冬 · 灯笼：点一下亮起暖光 */
.sfx-glow{transition:opacity .4s ease}
#seasonFx .sfx-prop.sfx-lit .sfx-glow{opacity:1;animation:sfxGlowPulse 2.6s ease-in-out infinite}
@keyframes sfxGlowPulse{0%,100%{opacity:.55}50%{opacity:1}}
#seasonFx .sfx-prop.sfx-lit{filter:drop-shadow(0 2px 6px rgba(226,59,50,.35))}
/* 系统「减少动效」时全部静止（无障碍） */
@media (prefers-reduced-motion: reduce){
  #seasonFx .sfx-p i,#seasonFx .sfx-b,#seasonFx .sfx-bird,
  .sfx-blades,.sfx-sway,.sfx-maple,.sfx-flap,.sfx-kite,.sfx-glow,
  #seasonFx .sfx-prop.sfx-hot .sfx-steam path{animation:none !important}
}

/* ==========================================================================
   ★ v77.9 ③ 切换季节主题时，全站跟着换「当季皮肤」（不止日历页）
   只在浅色主题下做淡染色，深色主题保持原样（避免对比度问题）。
   ========================================================================== */
:root[data-theme="light"][data-cal-theme="spring"] .topbar{background:linear-gradient(90deg,#fff5f9,rgba(255,255,255,.92))}
:root[data-theme="light"][data-cal-theme="spring"] .panel{border-color:#f6c8d8}
:root[data-theme="light"][data-cal-theme="spring"] .panel-head{background:linear-gradient(90deg,#fff6fa,#fff)}
:root[data-theme="light"][data-cal-theme="spring"] .navbtn.active{background:#ffe7f0;color:#c2375e}
:root[data-theme="light"][data-cal-theme="spring"] .navbtn.active::after{background:#d6456b}

:root[data-theme="light"][data-cal-theme="summer"] .topbar{background:linear-gradient(90deg,#eefaf7,rgba(255,255,255,.92))}
:root[data-theme="light"][data-cal-theme="summer"] .panel{border-color:#bfe6dc}
:root[data-theme="light"][data-cal-theme="summer"] .panel-head{background:linear-gradient(90deg,#f0fbf8,#fff)}
:root[data-theme="light"][data-cal-theme="summer"] .navbtn.active{background:#dcf5ef;color:#0b7a63}
:root[data-theme="light"][data-cal-theme="summer"] .navbtn.active::after{background:#0f8a6d}

:root[data-theme="light"][data-cal-theme="autumn"] .topbar{background:linear-gradient(90deg,#fff5ec,rgba(255,255,255,.92))}
:root[data-theme="light"][data-cal-theme="autumn"] .panel{border-color:#f3d5b8}
:root[data-theme="light"][data-cal-theme="autumn"] .panel-head{background:linear-gradient(90deg,#fff7f0,#fff)}
:root[data-theme="light"][data-cal-theme="autumn"] .navbtn.active{background:#fdeadb;color:#b1611a}
:root[data-theme="light"][data-cal-theme="autumn"] .navbtn.active::after{background:#d97a29}

:root[data-theme="light"][data-cal-theme="winter"] .topbar{background:linear-gradient(90deg,#f2f7ff,rgba(255,255,255,.92))}
:root[data-theme="light"][data-cal-theme="winter"] .panel{border-color:#cddcf5}
:root[data-theme="light"][data-cal-theme="winter"] .panel-head{background:linear-gradient(90deg,#f6faff,#fff)}
:root[data-theme="light"][data-cal-theme="winter"] .navbtn.active{background:#e4edfb;color:#2f5fbe}
:root[data-theme="light"][data-cal-theme="winter"] .navbtn.active::after{background:#3a6fd8}

/* ==========================================================================
   ★ v78 季节横幅（Season Banner）—— 每个页面顶部一条当季插画 + 当季问候语
   ========================================================================== */
.season-banner{position:relative;margin:0 0 12px;height:96px;overflow:hidden;
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--card);
  box-shadow:var(--sh)}
.season-banner.hidden{display:none}
.season-banner .sb-art{position:absolute;inset:0;width:100%;height:100%;display:block}
.season-banner .sb-txt{position:absolute;left:18px;top:0;bottom:0;display:flex;flex-direction:column;
  justify-content:center;gap:3px;max-width:min(70%,560px);pointer-events:none}
.season-banner .sb-hi{font-size:16px;font-weight:500;color:#26324a;line-height:1.35;
  text-shadow:0 1px 0 rgba(255,255,255,.85)}
.season-banner .sb-tip{font-size:12px;color:#5a6478;line-height:1.4;
  text-shadow:0 1px 0 rgba(255,255,255,.8)}
.season-banner .sb-x{position:absolute;right:10px;top:10px;width:26px;height:26px;line-height:1;
  border:1px solid rgba(40,50,70,.14);border-radius:var(--r-pill);background:rgba(255,255,255,.78);
  color:#5a6478;font-size:13px;cursor:pointer;padding:0}
.season-banner .sb-x:hover{background:#fff;color:#26324a}
/* 深色：文字转浅、加一层暗罩，保证插画之上仍然可读 */
:root[data-theme="dark"] .season-banner .sb-art{opacity:.55}
:root[data-theme="dark"] .season-banner{background:#1b2231;border-color:#2b3446}
:root[data-theme="dark"] .season-banner .sb-hi{color:#eef2fb;text-shadow:0 1px 3px rgba(0,0,0,.55)}
:root[data-theme="dark"] .season-banner .sb-tip{color:#b8c2d6;text-shadow:0 1px 3px rgba(0,0,0,.5)}
/* ★ v79.3：温度 → 穿衣建议（季节横幅 sb-tip 下的第二行）。
   阈值与吉祥物台词同一套五档，保证「他说冻手」与「建议穿羽绒服」不打架。 */
.season-banner .sb-cloth{display:block;font-size:12px;color:#4d5866;line-height:1.4;margin-top:2px}
:root[data-theme="dark"] .season-banner .sb-cloth{color:#c3cddd;text-shadow:0 1px 3px rgba(0,0,0,.5)}
:root[data-theme="dark"] .season-banner .sb-x{background:rgba(30,38,54,.85);border-color:#39445a;color:#c8d2e6}
/* 手机：矮一点、字号收一档 */
@media (max-width:820px){
  .season-banner{height:78px;margin-bottom:10px}
  .season-banner .sb-txt{left:14px;max-width:min(76%,420px)}
  .season-banner .sb-hi{font-size:14px}
  .season-banner .sb-tip{font-size:12px}
}
/* 横幅里的小动画（柳枝摆 / 涟漪 / 落叶转 / 飘雪），尊重「减弱动效」设置 */
@keyframes sbSway{0%,100%{transform:rotate(calc(var(--sway,3deg) * -1))}50%{transform:rotate(var(--sway,3deg))}}
@keyframes sbDrift{0%{transform:translate(0,0)}50%{transform:translate(14px,-8px)}100%{transform:translate(0,0)}}
@keyframes sbSpin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
@keyframes sbSnow{0%{transform:translateY(-6px);opacity:.2}50%{opacity:.95}100%{transform:translateY(16px);opacity:.2}}
@keyframes sbRipple{0%{transform:scaleX(.6);opacity:.15}50%{opacity:.6}100%{transform:scaleX(1.15);opacity:.15}}
.season-banner .sb-sway{transform-origin:top center;animation:sbSway var(--sway-dur,3.6s) ease-in-out infinite}
.season-banner .sb-drift{animation:sbDrift 7s ease-in-out infinite}
.season-banner .sb-fly{animation:sbDrift 9s ease-in-out infinite}
.season-banner .sb-spin{animation:sbSpin 9s linear infinite;transform-origin:center}
.season-banner .sb-snow{animation:sbSnow 5.5s linear infinite}
.season-banner .sb-ripple,.season-banner .sb-ripple2{transform-origin:center;animation:sbRipple 4s ease-in-out infinite}
.season-banner .sb-ripple2{animation-delay:1.3s}
/* ★ v79：风力档位 → 摆动幅度/周期（bucket：2/4/6/8/10，对应蒲福 2-3/4-5/6-7/8-9/10-12 级）。
   --sway / --sway-dur 是 CSS 变量，.sb-sway 的 keyframes 里用 var() 读取，实现「随风级放大摆动」。 */
.season-banner[data-wind="2"]{--sway:1.5deg;--sway-dur:4.5s}
.season-banner[data-wind="4"]{--sway:3deg;--sway-dur:3.6s}
.season-banner[data-wind="6"]{--sway:6deg;--sway-dur:2.8s}
.season-banner[data-wind="8"]{--sway:9deg;--sway-dur:2.2s}
.season-banner[data-wind="10"]{--sway:13deg;--sway-dur:1.6s}
/* ★ v79：天气叠加层 —— 雨（斜线下落）、雪（慢速飘落）、风（横向风线），全内联 SVG + CSS。 */
.season-banner .sb-rain line{stroke:#8fb8e8;stroke-width:2;stroke-linecap:round;
  animation:sbRainFall 1.1s linear infinite}
@keyframes sbRainFall{0%{transform:translateY(-16px);opacity:0}20%{opacity:.7}100%{transform:translateY(44px);opacity:0}}
.season-banner .sb-wxsnow circle{fill:#cfe0f5;animation:sbWxSnow 6s ease-in-out infinite}
@keyframes sbWxSnow{0%{transform:translateY(-10px) translateX(0);opacity:0}50%{opacity:.9}100%{transform:translateY(34px) translateX(12px);opacity:0}}
.season-banner .sb-wind path{stroke:#aebfdd;stroke-width:1.6;fill:none;stroke-linecap:round;
  animation:sbWindLine 1.6s linear infinite}
@keyframes sbWindLine{0%{transform:translateX(-70px);opacity:0}30%{opacity:.55}100%{transform:translateX(220px);opacity:0}}
@media (prefers-reduced-motion:reduce){
  .season-banner .sb-sway,.season-banner .sb-drift,.season-banner .sb-fly,
  .season-banner .sb-spin,.season-banner .sb-snow,
  .season-banner .sb-ripple,.season-banner .sb-ripple2,
  .season-banner .sb-rain,.season-banner .sb-wxsnow,.season-banner .sb-wind{animation:none}
}

/* ==========================================================================
   ★ v78 P1-1：微交互 —— 卡片悬停抬升 + 按钮/列表点击涟漪
   只在指针设备（:hover 生效）上启用，触屏不会残留 hover 态；
   「减弱动效」偏好下全部关掉。
   ========================================================================== */
@media (hover:hover){
  .panel,.tile,.card,.stat-card,.mcard,.dept-card,.act-card,.task-card,.comm-card{
    transition:transform .16s ease, box-shadow .16s ease, border-color .16s ease}
  .panel:hover,.tile:hover,.card:hover,.stat-card:hover,.mcard:hover,.dept-card:hover,
  .act-card:hover,.task-card:hover,.comm-card:hover{
    transform:translateY(-2px);
    box-shadow:0 2px 6px rgba(20,30,60,.06), 0 12px 28px rgba(20,30,60,.09)}
}
:root[data-theme="dark"] .panel:hover,:root[data-theme="dark"] .card:hover,
:root[data-theme="dark"] .mcard:hover,:root[data-theme="dark"] .stat-card:hover{
  box-shadow:0 2px 6px rgba(0,0,0,.35), 0 12px 28px rgba(0,0,0,.45)}
/* 涟漪容器：由 app.js 的 rippleAt(el, ev) 注入 .ripple 子元素 */
.ripple-host{position:relative;overflow:hidden}
.ripple{position:absolute;border-radius:50%;pointer-events:none;transform:scale(0);
  background:currentColor;opacity:.22;animation:rippleGo .5s ease-out forwards}
@keyframes rippleGo{to{transform:scale(2.6);opacity:0}}
@media (prefers-reduced-motion:reduce){
  .panel,.tile,.card,.stat-card,.mcard,.dept-card,.act-card,.task-card,.comm-card{transition:none}
  .ripple{display:none}
}

/* ==========================================================================
   ★ v78 P1-3：深色模式的季节染色（与浅色分开取值）
   浅色那套染色直接搬到深底上会「亮得刺眼、文字对比度不足」，
   这里统一换成低亮度底色 + 高亮度文字，保证深色下依然读得清。
   ========================================================================== */
[data-theme="dark"][data-cal-theme="spring"] .panel{border-color:#5a3a4a}
[data-theme="dark"][data-cal-theme="spring"] .navbtn.active{background:#3a2530;color:#f5bcd0}
[data-theme="dark"][data-cal-theme="spring"] .navbtn.active::after{background:#c76a94}
[data-theme="dark"][data-cal-theme="summer"] .panel{border-color:#2a5548}
[data-theme="dark"][data-cal-theme="summer"] .navbtn.active{background:#16332c;color:#8fdcbc}
[data-theme="dark"][data-cal-theme="summer"] .navbtn.active::after{background:#3f9e80}
[data-theme="dark"][data-cal-theme="autumn"] .panel{border-color:#5a4228}
[data-theme="dark"][data-cal-theme="autumn"] .navbtn.active{background:#38281a;color:#f2b884}
[data-theme="dark"][data-cal-theme="autumn"] .navbtn.active::after{background:#c08040}
[data-theme="dark"][data-cal-theme="winter"] .panel{border-color:#2e4470}
[data-theme="dark"][data-cal-theme="winter"] .navbtn.active{background:#1c2a4a;color:#adc8f2}
[data-theme="dark"][data-cal-theme="winter"] .navbtn.active::after{background:#5a7dc0}
/* 深色：季节装饰粒子与道具整体降亮，避免浅色花瓣/雪片在深底上过曝 */
[data-theme="dark"] #seasonFx .sfx-p{opacity:.45}
[data-theme="dark"] #seasonFx .sfx-prop{opacity:.85}
[data-theme="dark"] #seasonFx .sfx-p i{filter:brightness(.85)}

/* ==========================================================================
   ★ v78 P2-1：本周忙碌度热力条（概览页）
   ========================================================================== */
.busy-heat{margin-top:14px}
.bh-row{display:flex;align-items:flex-end;gap:8px;height:56px;padding:2px 0}
.bh-bar{position:relative;flex:1;min-width:0;border:0;background:#eef1f7;border-radius:var(--r-sm) var(--r-sm) var(--r-xs) var(--r-xs);
  cursor:pointer;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;
  padding-top:2px;transition:height .18s ease, transform .12s ease;color:var(--tx3)}
.bh-bar:hover{transform:translateY(-2px)}
.bh-bar i{font-style:normal;font-size:11px;line-height:1.2;font-weight:500}
.bh-bar em{position:absolute;bottom:-18px;left:0;right:0;text-align:center;font-style:normal;font-size:11px;color:var(--tx3)}
.bh-bar.lv0{background:#eef1f7;color:var(--tx3)}
.bh-bar.lv1{background:#cfe0ff;color:#2c5bb0}
.bh-bar.lv2{background:#8fb3f5;color:#123a86}
.bh-bar.lv3{background:#3b6ef5;color:#fff}
.bh-bar.peak{box-shadow:0 0 0 2px rgba(59,110,245,.35)}
/* ★ v78.4 B1：当天那根柱子的标签是「今」——给它主色 + 加粗，跟其余星期末字拉开区分。 */
.bh-bar.today em{color:var(--tx);font-weight:600}
.bh-legend{display:flex;align-items:center;gap:5px;margin-top:22px;font-size:11px;color:var(--tx3)}
.bh-legend span{width:14px;height:8px;border-radius:var(--r-xs);display:inline-block}
.bh-legend span:not(:first-child){margin-left:8px}
.bh-lv0{background:#eef1f7}.bh-lv1{background:#cfe0ff}.bh-lv2{background:#8fb3f5}.bh-lv3{background:#3b6ef5}
/* 四季各一套忙碌色（春粉 / 夏绿 / 秋橙 / 冬蓝） */
[data-cal-theme="spring"] .bh-bar.lv1{background:#f9cfe0;color:#a8355f}
[data-cal-theme="spring"] .bh-bar.lv2{background:#f2a1c2;color:#7d1f42}
[data-cal-theme="spring"] .bh-bar.lv3{background:#d6456b;color:#fff}
[data-cal-theme="spring"] .bh-lv1{background:#f9cfe0}[data-cal-theme="spring"] .bh-lv2{background:#f2a1c2}[data-cal-theme="spring"] .bh-lv3{background:#d6456b}
[data-cal-theme="summer"] .bh-bar.lv1{background:#c6ecdf;color:#0b6b57}
[data-cal-theme="summer"] .bh-bar.lv2{background:#7ccfb4;color:#064a3c}
[data-cal-theme="summer"] .bh-bar.lv3{background:#0f8a6d;color:#fff}
[data-cal-theme="summer"] .bh-lv1{background:#c6ecdf}[data-cal-theme="summer"] .bh-lv2{background:#7ccfb4}[data-cal-theme="summer"] .bh-lv3{background:#0f8a6d}
[data-cal-theme="autumn"] .bh-bar.lv1{background:#fbe0c4;color:#9a5518}
[data-cal-theme="autumn"] .bh-bar.lv2{background:#f0b57c;color:#7a3c0c}
[data-cal-theme="autumn"] .bh-bar.lv3{background:#d97a29;color:#fff}
[data-cal-theme="autumn"] .bh-lv1{background:#fbe0c4}[data-cal-theme="autumn"] .bh-lv2{background:#f0b57c}[data-cal-theme="autumn"] .bh-lv3{background:#d97a29}
[data-cal-theme="winter"] .bh-bar.lv1{background:#d3e3fa;color:#254fa8}
[data-cal-theme="winter"] .bh-bar.lv2{background:#9dbcf0;color:#12336f}
[data-cal-theme="winter"] .bh-bar.lv3{background:#3a6fd8;color:#fff}
[data-cal-theme="winter"] .bh-lv1{background:#d3e3fa}[data-cal-theme="winter"] .bh-lv2{background:#9dbcf0}[data-cal-theme="winter"] .bh-lv3{background:#3a6fd8}
/* ★ v78.4 B2：peak 强调框 + lv0 空闲色，跟着「随季节变换主题」走。
   原来 peak 写死蓝色 rgba(59,110,245,.35)，开季节主题后柱子变粉/变绿/变橙、框还是蓝 → 违和；
   lv0 那块灰 #eef1f7 在春/夏/秋/冬主题里同样"跳出色板"，一并季节化。
   取值对齐 seasonBannerArt() 的 sky 色（春 #fdeef5 / 夏 #e6f7f1 / 秋 #fff2e6 / 冬 #eaf2ff）。
   ★ 顺序是硬约束：四季规则必须写在下面 [data-theme="dark"] 之前（同级特异性、后来者胜）。 */
[data-cal-theme="spring"] .bh-bar.lv0{background:#fdeef5}
[data-cal-theme="spring"] .bh-lv0{background:#fdeef5}
[data-cal-theme="spring"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(214,69,107,.38)}
[data-cal-theme="summer"] .bh-bar.lv0{background:#e6f7f1}
[data-cal-theme="summer"] .bh-lv0{background:#e6f7f1}
[data-cal-theme="summer"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(15,138,109,.38)}
[data-cal-theme="autumn"] .bh-bar.lv0{background:#fff2e6}
[data-cal-theme="autumn"] .bh-lv0{background:#fff2e6}
[data-cal-theme="autumn"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(217,122,41,.40)}
[data-cal-theme="winter"] .bh-bar.lv0{background:#eaf2ff}
[data-cal-theme="winter"] .bh-lv0{background:#eaf2ff}
[data-cal-theme="winter"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(58,111,216,.38)}
/* 深色：四档降一档亮度，保证柱上的数字仍可读 */
[data-theme="dark"] .bh-bar.lv0{background:#2a3346}
[data-theme="dark"] .bh-bar.lv1{background:#2e4a7a}
[data-theme="dark"] .bh-bar.lv2{background:#3a63a8}
[data-theme="dark"] .bh-bar.lv3{background:#4d7fd0}
/* ★ v78.4 B2：深色下的 peak 描边要**提亮**才看得见 —— 与「柱子填充降亮度」的方向相反，别照抄。
   顺序：通用深色 → 深色+四季（后者特异性更高，天然覆盖前者）。 */
[data-theme="dark"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(141,170,235,.55),0 0 10px rgba(141,170,235,.18)}
[data-theme="dark"][data-cal-theme="spring"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(244,150,180,.55),0 0 10px rgba(244,150,180,.16)}
[data-theme="dark"][data-cal-theme="summer"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(124,207,180,.55),0 0 10px rgba(124,207,180,.16)}
[data-theme="dark"][data-cal-theme="autumn"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(240,181,124,.58),0 0 10px rgba(240,181,124,.18)}
[data-theme="dark"][data-cal-theme="winter"] .bh-bar.peak{box-shadow:0 0 0 2px rgba(157,188,240,.55),0 0 10px rgba(157,188,240,.16)}

/* ==========================================================================
   ★ v78 P2-2：吉祥物小助手（右下角常驻，点一下说话）
   层级 31：季节装饰层(30) 之上、顶栏(40) 之下；
   位置 right:14 / bottom:142 —— 避开底部 tabbar(120) 与「回到顶部」浮标(900)。
   ========================================================================== */
.mascot-buddy{position:fixed;right:14px;bottom:142px;z-index:31;display:flex;flex-direction:column;
  align-items:flex-end;gap:8px;
  /*🔴🔴 v79.9 批次 8【B】修 D4 根因：容器**默认不吃点击**。
   ────────────────────────────────────────────────────────────────────
   症状（实测 tmp/_b_memo_after.txt 的 GEOM）：备忘录列表的 ✕ 按钮
     明明在屏幕上、用户也看得见，却怎么点都没反应——
     `elementFromPoint` 落点 = `DIV.mascot-buddy` / `IMG.mb-face`，EVT_REACHED=0。

   根因：这个容器是 `display:flex` + `align-items:flex-end`，
     宽度被**关闭态的 `.mb-bubble` 撑到 120px**（实测 rect w=120,h=158），
     而气泡自己是 `opacity:0`。
     ⚠⚠ **`opacity:0` 不影响命中测试** —— 只有 `pointer-events:none` 才排除。
       我曾误判"透明容器不吃点击"，实测打脸：把 ✕ 横向移开 48px之后，
       落点从 `IMG.mb-face` 变成了 `DIV.mascot-buddy` ——
       元素**照样**在那 120×158 的矩形里接事件，只是接的是个看不见的空白。
     ⇒ 用户在列表右侧任何位置点下去，事件都被这只"看不见的手"吃掉。

   ⇒ 修法：容器 `pointer-events:none`，只把**真正可点的子块**放回去。
     ⚠ 为什么不直接"让内容让位 120px"：
       那是治标，且要在备忘录右侧永久挖掉 120px 空白（430px 屏上占 28%），
       代价与收益不成比例；而且同样的空白还压在课表/待办/日历的右下角。
     ⚠ 为什么这样改**不会**弄坏吉祥物自己：
       事件监听**全部**绑在子元素上（app.js: `.mb-body.onclick` / `.mb-x.onclick`
       / `#mbChatBtn.onclick`），容器上**零监听器**
       （`#mascotBuddy` 只在 renderMascot / mascotHide / mascotShow 里被读取）。
     ⚠ `.mb-bubble` 不在下面的恢复清单里：它关闭态已有 `pointer-events:none`、
       展开态由 `.mascot-buddy.mb-open .mb-bubble{pointer-events:auto}` 接管，
       两条既有规则的特异性都高于继承值，**不需要也不应该**再写一遍。*/
  pointer-events:none}
/* 真正可点的子块放回命中测试（事件也正是绑在它们身上） */
.mascot-buddy .mb-body,.mascot-buddy .mb-chat{pointer-events:auto}
.mascot-buddy.hidden{display:none}
.mb-body{flex:none;width:60px;height:60px;padding:3px;border:0;cursor:pointer;position:relative;
  border-radius:var(--r-pill);background:var(--card);
  box-shadow:0 6px 18px rgba(30,45,80,.18);
  transition:transform .18s ease,box-shadow .18s ease;-webkit-tap-highlight-color:transparent}
.mb-body img{width:100%;height:100%;object-fit:contain;display:block;border-radius:var(--r-pill)}
.mb-body:hover{transform:translateY(-3px) scale(1.05);box-shadow:0 10px 24px rgba(30,45,80,.24)}
.mb-body:active{transform:scale(.93)}
.mb-bubble{position:relative;max-width:min(76vw,296px);padding:10px 30px 10px 12px;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:0 10px 28px rgba(20,30,60,.16);
  display:flex;align-items:center;gap:10px;
  opacity:0;transform:translateY(8px) scale(.96);pointer-events:none;
  transition:opacity .18s ease,transform .18s ease}
.mascot-buddy.mb-open .mb-bubble{opacity:1;transform:none;pointer-events:auto}
/* 气泡小尾巴：指向下面的吉祥物 */
.mb-bubble::after{content:'';position:absolute;right:24px;bottom:-7px;width:12px;height:12px;
  background:var(--card);border-right:1px solid var(--line);border-bottom:1px solid var(--line);
  transform:rotate(45deg)}
.mb-emote{flex:none;width:42px;height:42px;object-fit:contain}
.mb-txt{min-width:0}
.mb-line{display:block;font-size:13px;font-weight:500;color:var(--tx);line-height:1.45}
.mb-sub{display:block;margin-top:3px;font-size:12px;color:var(--tx3);line-height:1.4}
.mb-x{position:absolute;top:4px;right:6px;width:22px;height:22px;padding:0;border:0;background:transparent;
  color:var(--tx3);font-size:12px;line-height:1;cursor:pointer;border-radius:var(--r-pill)}
.mb-x:hover{background:var(--pri-soft);color:var(--pri)}
/* ★ v78.1：💬 打开对话面板的小按钮（贴在吉祥物头顶上，不抢戏） */
.mb-chat{flex:none;width:34px;height:34px;padding:0;border:1px solid var(--line);cursor:pointer;
  border-radius:var(--r-pill);background:var(--card);color:var(--tx2);font-size:15px;line-height:1;
  box-shadow:0 4px 12px rgba(30,45,80,.16);
  transition:transform .16s ease,color .16s ease;-webkit-tap-highlight-color:transparent}
.mb-chat:hover{transform:translateY(-2px);color:var(--pri)}
.mb-chat:active{transform:scale(.92)}
/* 手机：缩一号，位置再抬一点，别压住底部标签栏 */
@media (max-width:760px){
  .mascot-buddy{right:10px;bottom:126px}
  .mb-body{width:52px;height:52px}
  .mb-bubble{max-width:min(82vw,260px);padding:9px 28px 9px 10px}
  .mb-emote{width:36px;height:36px}
}
/* 关掉动效偏好：只留显隐，不做位移缩放 */
@media (prefers-reduced-motion:reduce){
  .mb-body,.mb-bubble{transition:none}
  .mascot-buddy.mb-open .mb-bubble{transform:none}
}
/* ★ v79：吉祥物天气动态化 —— .mb-holder 包住头像（伞叠头顶 + 风力摆动）。
   chibi 是 webp 位图，伞只能叠一个绝对定位的内联 SVG 盖在头顶；摆动是整张图 transform。 */
.mb-holder{position:relative;display:block;width:100%;height:100%}
.mb-holder .mb-face{width:100%;height:100%}
.mb-umbrella{position:absolute;top:-16px;left:50%;transform:translateX(-50%);
  width:46px;height:46px;display:none;pointer-events:none;filter:drop-shadow(0 2px 3px rgba(0,0,0,.18))}
.mascot-buddy[data-rain="1"] .mb-umbrella{display:block;animation:mbUmbrellaIn .3s ease}
@keyframes mbUmbrellaIn{from{opacity:0;transform:translateX(-50%) translateY(-6px)}
  to{opacity:1;transform:translateX(-50%) translateY(0)}}
/* ★ v79.4：下雪时吉祥物头顶挂一朵雪花（与下雨打伞对称，让"对天气有反应"一眼可见） */
.mascot-buddy[data-snow="1"] .mb-body::after{content:'❄️';position:absolute;top:-8px;right:-8px;font-size:18px;line-height:1;
  pointer-events:none;filter:drop-shadow(0 1px 2px rgba(15,23,42,.35));animation:mbSnowIn .3s ease}
@keyframes mbSnowIn{from{opacity:0;transform:scale(.4)}to{opacity:1;transform:scale(1)}}
/* 风力摆动：data-wind 是 bucket（4/6/8/10，≥5级才非 0），幅度/周期与横幅同表；<5级不摆。 */
@keyframes mbSway{0%,100%{transform:rotate(calc(var(--sway,3deg) * -1))}50%{transform:rotate(var(--sway,3deg))}}
.mascot-buddy[data-wind="4"] .mb-holder{--sway:3deg;--sway-dur:3.6s;animation:mbSway var(--sway-dur) ease-in-out infinite;transform-origin:bottom center}
.mascot-buddy[data-wind="6"] .mb-holder{--sway:6deg;--sway-dur:2.8s;animation:mbSway var(--sway-dur) ease-in-out infinite;transform-origin:bottom center}
.mascot-buddy[data-wind="8"] .mb-holder{--sway:9deg;--sway-dur:2.2s;animation:mbSway var(--sway-dur) ease-in-out infinite;transform-origin:bottom center}
.mascot-buddy[data-wind="10"] .mb-holder{--sway:13deg;--sway-dur:1.6s;animation:mbSway var(--sway-dur) ease-in-out infinite;transform-origin:bottom center}
@media (prefers-reduced-motion:reduce){
  .mascot-buddy .mb-holder,.mascot-buddy .mb-umbrella{animation:none}
}

/* ==========================================================================
   ★ v78.1：吉祥物自定义形象 + AI 台词 + 对话面板
   --------------------------------------------------------------------------
   ① .mascot-prev  设置页的形象预览小圆图
   ② .mb-name       气泡左上角的吉祥物名字
   ③ .mb-think      AI 请求进行中（气泡轻微呼吸，一眼知道它在想）
   ④ .mchat         点「💬 聊两句」弹出的对话面板
   ========================================================================== */
/* ① 设置页预览图：不给 img 显式尺寸会按原图原始像素渲染（手机拍的图能有几千 px，
      直接把设置页撑爆）—— 容器 + img 双层都锁死，跟 .mb-body 同一个防坑写法。 */
.mascot-prev{flex:none;width:40px;height:40px;border-radius:var(--r-pill);
  border:1px solid var(--line);background:var(--bg2);padding:2px;overflow:hidden}
#setMascotPrev{width:40px;height:40px;object-fit:cover;display:inline-block;vertical-align:middle}
.set-hr{border:0;border-top:1px dashed var(--line);margin:12px 0 10px}
.set-sub{font-size:13px;font-weight:600;color:var(--tx);margin-bottom:2px}
/* ② 气泡名字：小徽标，别抢台词的风头 */
.mb-name{display:block;font-style:normal;font-size:11px;font-weight:600;
  color:var(--pri);opacity:.85;margin-bottom:2px;letter-spacing:.2px}
/* ③ AI 思考中：气泡呼吸 + 台词变浅，一眼看出「它在想」 */
.mascot-buddy.mb-think .mb-bubble{animation:mbThink 1.1s ease-in-out infinite}
.mascot-buddy.mb-think .mb-line{opacity:.55}
@keyframes mbThink{0%,100%{box-shadow:0 10px 28px rgba(20,30,60,.16)}
  50%{box-shadow:0 10px 28px rgba(59,110,245,.34)}}
/* ④ 对话面板：桌面右上角浮窗，手机底部大卡片 */
.mchat{position:fixed;right:14px;bottom:210px;z-index:60;
  width:min(92vw,380px);height:min(62vh,470px);
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-xl);
  box-shadow:0 18px 48px rgba(20,30,60,.26);
  opacity:0;transform:translateY(10px) scale(.98);pointer-events:none;
  transition:opacity .18s ease,transform .18s ease}
.mchat.open{opacity:1;transform:none;pointer-events:auto}
.mchat.hidden{display:none}
.mc-head{flex:none;display:flex;align-items:center;gap:9px;padding:10px 10px 10px 12px;
  border-bottom:1px solid var(--line);background:var(--bg2)}
.mc-face{flex:none;width:34px;height:34px;border-radius:var(--r-pill);
  object-fit:cover;background:var(--card);padding:2px;border:1px solid var(--line)}
.mc-title{flex:1;min-width:0}
.mc-title b{display:block;font-size:13px;color:var(--tx)}
.mc-title span{display:block;font-size:11px;color:var(--tx3);margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mc-x{flex:none;width:26px;height:26px;padding:0;border:0;background:transparent;
  color:var(--tx3);font-size:13px;cursor:pointer;border-radius:var(--r-pill)}
.mc-x:hover{background:var(--pri-soft);color:var(--pri)}
.mc-log{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:9px;
  -webkit-overflow-scrolling:touch}
.mc-msg{max-width:82%;padding:8px 11px;border-radius:var(--r-lg);font-size:13px;line-height:1.5;
  word-break:break-word;white-space:pre-wrap}
.mc-msg.me{align-self:flex-end;background:var(--pri);color:#fff;border-bottom-right-radius:var(--r-xs)}
.mc-msg.ai{align-self:flex-start;background:var(--bg2);color:var(--tx);border:1px solid var(--line);
  border-bottom-left-radius:var(--r-xs)}
.mc-msg.sys{align-self:center;max-width:94%;background:transparent;color:var(--tx3);
  font-size:11.5px;text-align:center;padding:2px 4px}
.mc-msg.wait{opacity:.6}
.mc-msg .mc-err{color:var(--danger,#c0392b)}
.mc-quick{flex:none;display:flex;gap:6px;padding:0 10px 8px;overflow-x:auto;
  -webkit-overflow-scrolling:touch}
.mc-quick button{flex:none;white-space:nowrap;font-size:12px;padding:5px 10px;
  border-radius:var(--r-pill);border:1px solid var(--line);background:var(--bg2);
  color:var(--tx2);cursor:pointer}
.mc-quick button:hover{border-color:var(--pri);color:var(--pri)}
.mc-input{flex:none;display:flex;gap:8px;padding:10px;border-top:1px solid var(--line);background:var(--bg2)}
.mc-input input{flex:1;min-width:0}
@media (max-width:760px){
  /* 手机：从底部升起的卡片，但要**抬到底部标签栏（z-index:120，约 58px 高）之上**，
     否则输入框会被标签栏盖住（真机截图实测）。 */
  .mchat{right:8px;left:8px;width:auto;
    bottom:calc(58px + env(safe-area-inset-bottom,0px));
    height:min(64vh,500px);
    border-radius:var(--r-xl) var(--r-xl) 0 0}
}
@media (prefers-reduced-motion:reduce){
  .mchat{transition:none}
  .mascot-buddy.mb-think .mb-bubble{animation:none}
}

/* ==========================================================================
   ★ v78 P2-3：开屏四季版 —— 当季吉祥物 + 当季问候语（v77.3 的粒子之上再叠一层）
   ========================================================================== */
.intro-chibi{position:absolute;left:50%;bottom:12vh;width:118px;height:118px;margin-left:-59px;
  object-fit:contain;filter:drop-shadow(0 8px 18px rgba(0,0,0,.3));animation:introPop .9s .15s ease both}
.intro-greet{position:absolute;left:0;right:0;bottom:calc(12vh + 132px);text-align:center;color:#fff;
  text-shadow:0 4px 14px rgba(0,0,0,.35);animation:introFade 1.4s .25s ease both}
.intro-greet b{display:block;font-size:20px;font-weight:500;letter-spacing:1px}
.intro-greet span{display:block;margin-top:5px;font-size:13px;opacity:.92;letter-spacing:.3px}
@media (max-width:760px){
  .intro-chibi{width:94px;height:94px;margin-left:-47px;bottom:14vh}
  .intro-greet{bottom:calc(14vh + 104px)}
  .intro-greet b{font-size:17px}
}
@media (prefers-reduced-motion:reduce){
  .intro-chibi,.intro-greet{animation:none}
}

/* ============ v79：考试倒计时面板 ============ */
.exam-countdown{margin-bottom:14px}
.exam-row{display:flex;gap:10px;align-items:flex-start;border:1px solid var(--line2);border-radius:var(--r-md);
  padding:10px 12px;background:var(--card);transition:.15s;cursor:pointer}
.exam-row:hover{border-color:var(--line);box-shadow:var(--sh)}
.exam-row .bar{width:3px;align-self:stretch;border-radius:var(--r-md);flex:none}
.ex-main{flex:1;min-width:0}
.ex-name{font-weight:500;font-size:13px;display:flex;align-items:center;gap:8px}
.ex-name .tag{font-size:12px;padding:1px 8px}
.ex-meta{color:var(--tx3);font-size:12px;margin-top:2px}
.ex-tip{color:var(--tx2);font-size:12px;margin-top:4px}
.ex-del{background:none;border:none;color:var(--tx3);font-size:14px;cursor:pointer;padding:2px 4px;line-height:1}
.ex-del:hover{color:var(--leave)}
/* 紧迫度色条 + 天数徽章：>30 中性蓝 / 8-30 提醒橙 / 1-7 紧迫红 / 0 当天绿 / <0 灰 */
.bar.k-exam.calm{background:var(--pri)!important}
.bar.k-exam.warn{background:var(--plan)!important}
.bar.k-exam.urgent{background:var(--leave)!important}
.bar.k-exam.today{background:var(--free)!important}
.bar.k-exam.past{background:var(--tx3)!important}
.tag.ex-tone.calm{background:var(--pri-soft);color:var(--pri)}
.tag.ex-tone.warn{background:#fff3e6;color:#94541a}
.tag.ex-tone.urgent{background:#fbdedc;color:#a33632}
.tag.ex-tone.today{background:#e3f5ec;color:#1d7a52}
.tag.ex-tone.past{background:#f1f4fa;color:var(--tx3)}
[data-theme="dark"] .tag.ex-tone.warn{background:#3a2f1c;color:#f5c07a}
[data-theme="dark"] .tag.ex-tone.urgent{background:#3a2325;color:#f5a0a0}
[data-theme="dark"] .tag.ex-tone.today{background:#1d3327;color:#7ce0b4}

/* ============ v79：倒计时/纪念日泛化（类型徽标 + 教务自动 + 时间待定） ============ */
.tag.ex-type{font-size:12px;padding:1px 8px;border-radius:10px;font-weight:500}
.tag.ex-type-exam{background:var(--pri-soft);color:var(--pri)}
.tag.ex-type-anniversary{background:#fde7f0;color:#b0205f}
.tag.ex-type-birthday{background:#fff3e6;color:#94541a}
.tag.ex-type-custom{background:#f1f4fa;color:var(--tx3)}
[data-theme="dark"] .tag.ex-type-anniversary{background:#3a2330;color:#f0a3c3}
[data-theme="dark"] .tag.ex-type-birthday{background:#3a2f1c;color:#f5c07a}
[data-theme="dark"] .tag.ex-type-custom{background:#2b2f38;color:#aab2c0}
.ex-auto{font-size:11px;color:var(--tx3);background:var(--line2);padding:1px 6px;border-radius:8px;flex:none}
.ex-row.readonly{cursor:default}
.ex-row.readonly:hover{border-color:var(--line2);box-shadow:none}
.ex-row.ex-pending{border-style:dashed;cursor:default}
.ex-row.ex-pending:hover{border-color:var(--line2);box-shadow:none}
.tag.ex-tone.pending{background:#f1f4fa;color:var(--tx3)}
[data-theme="dark"] .tag.ex-tone.pending{background:#2b2f38;color:#aab2c0}

/* ============ v79：倒计时 hero 主卡 + 紧凑网格 + 进度条 ============ */
.ex-hero{display:flex;gap:16px;align-items:stretch;border:1px solid var(--line2);border-radius:var(--r);
  padding:16px;background:var(--card);margin-bottom:12px;position:relative;cursor:pointer}
.ex-hero:hover{border-color:var(--line);box-shadow:var(--sh)}
.ex-hero::before{content:'';position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:var(--r) 0 0 var(--r)}
.ex-hero-calm::before{background:var(--pri)}
.ex-hero-warn::before{background:var(--plan)}
.ex-hero-urgent::before{background:var(--leave)}
.ex-hero-today::before{background:var(--free)}
.ex-hero-past::before{background:var(--tx3)}
.ex-hero-calm{background:linear-gradient(135deg,var(--card),rgba(59,110,245,.07))}
.ex-hero-warn{background:linear-gradient(135deg,var(--card),rgba(224,139,0,.08))}
.ex-hero-urgent{background:linear-gradient(135deg,var(--card),rgba(224,52,44,.07))}
.ex-hero-today{background:linear-gradient(135deg,var(--card),rgba(31,163,74,.08))}
.ex-hero-past{background:linear-gradient(135deg,var(--card),rgba(138,143,152,.08))}
.ex-hero-left{flex:none;display:flex;flex-direction:column;justify-content:center;min-width:90px}
.ex-big-num{font-size:40px;font-weight:700;line-height:1;color:var(--tx);overflow-wrap:break-word}
.ex-big-num span{font-size:16px;font-weight:500;margin-left:2px}
.ex-big-label{font-size:12px;color:var(--tx3);margin-top:6px}
.ex-hero-today .ex-big-num{color:var(--free)}
.ex-hero-past .ex-big-num{color:var(--tx3)}
.ex-hero-urgent .ex-big-num{color:var(--leave)}
.ex-hero-warn .ex-big-num{color:var(--plan)}
.ex-hero-main{flex:1;min-width:0}
.ex-hero-main .ex-name{font-size:15px;flex-wrap:wrap;overflow-wrap:break-word}
.ex-hero>.ex-del{align-self:flex-start;flex:none}
.ex-progress{height:8px;border-radius:6px;background:var(--bg2,#eef1f7);overflow:hidden;margin-top:10px}
.ex-progress>i{display:block;height:100%;border-radius:6px;background:var(--pri,#3b6ef5);transition:width .3s ease}
.ex-hero-warn .ex-progress>i{background:var(--plan)}
.ex-hero-urgent .ex-progress>i{background:var(--leave)}
.ex-hero-today .ex-progress>i{background:var(--free)}
.ex-hero-past .ex-progress>i{background:var(--tx3)}
/* 紧凑卡片网格（2~3 列） */
.ex-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;margin-bottom:12px}
.ex-grid:last-child{margin-bottom:0}
.ex-card{display:flex;flex-direction:column;gap:4px;border:1px solid var(--line2);border-radius:var(--r-md);
  padding:12px 12px 12px 15px;background:var(--card);cursor:pointer;position:relative}
.ex-card:hover{border-color:var(--line);box-shadow:var(--sh)}
.ex-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;border-radius:var(--r-md) 0 0 var(--r-md)}
.ex-card-calm::before{background:var(--pri)}
.ex-card-warn::before{background:var(--plan)}
.ex-card-urgent::before{background:var(--leave)}
.ex-card-today::before{background:var(--free)}
.ex-card-past::before{background:var(--tx3)}
.ex-num{font-size:22px;font-weight:700;line-height:1}
.ex-num span{font-size:12px;font-weight:500;margin-left:2px}
.ex-card-today .ex-num{color:var(--free)}
.ex-card-past .ex-num{color:var(--tx3)}
.ex-card-urgent .ex-num{color:var(--leave)}
.ex-card-warn .ex-num{color:var(--plan)}
.ex-card .ex-name{font-size:13px;font-weight:600;flex-wrap:wrap;overflow-wrap:break-word;padding-right:18px}
.ex-card .ex-meta{font-size:12px}
.ex-card .ex-tip{font-size:11px}
/* ⚠⚠⚴🔴 v80b34【问题4】删除/隐藏两个按钮**重叠**（用户截图上 🗑 与 🙈 挤成一团）
   ⚠⚠⚴ 改前实测根因：`.ex-card>.ex-del{position:absolute;top:8px;right:8px}`
     —— 隐藏与删除**共用 .ex-del 这个 class**，
     ⇒ 两个按钮都被绝对定位到**同一个 (top:8px, right:8px)** ⇒ 完全叠在一起。
   ⇒ 修法：把操作区改成**一个 flex 容器**（普通流、横向排列、有间距），
     按钮不再各自绝对定位。
   ⚠ 为什么不用「给删除加 right:32px」这种偏移补丁：
     按钮宽度随文案变（🙈 隐藏 / 🗑 删除 宽度不同）⇒ 固定偏移在窄屏会再次重叠。
     ⇒ 必须用 flex 让它们自己排开。
   ⚠⚠ 窄屏（390px）尤其重要：卡片右侧空间有限 ⇒ 允许换行（flex-wrap），
     绝不压到内容上。
   ⚠⚠ 选择器要同时覆盖 .ex-card 与 .ex-hero（两张卡的按钮区行为必须一致），
     否则「最近那条」又变回重叠。*/
/* ⚠⚠⚴🔴 v80b34【问题2】未配 AI key 时的可见提示条。
   ⚠ 为什么要有：面板能打开、但只会给本地台词 ⇒ 用户以为「AI 坏了」。
   ⇒ 明确写出「还能用、只是没接 AI、去设置里配 key」，避免误解与反复来问。*/
.mchat-nokey{margin:8px 10px 0;padding:8px 10px;border-radius:8px;font-size:12px;line-height:1.5;color:var(--tx2);background:rgba(127,127,127,.12);border:1px solid rgba(127,127,127,.25)}
body.night .mchat-nokey{color:var(--tx2);background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.12)}

.ex-ops{position:absolute;top:6px;right:6px;display:flex;gap:4px;align-items:center;flex-wrap:wrap;justify-content:flex-end;max-width:calc(100% - 12px)}
.ex-card>.ex-del,.ex-hero>.ex-del{position:static;top:auto;right:auto;flex:none}
.ex-del.ex-del-hard{color:var(--leave)}
.ex-del.ex-hide{color:var(--tx3)}
.ex-card{position:relative}
@media (max-width:480px){.ex-big-num{font-size:28px}}

/* ============ v79：预设胶囊点选 + 更多折叠 ============ */
.ex-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px;max-height:180px;overflow:auto}
.ex-chip{background:var(--bg2,#f1f4fa);border:1px solid var(--line2);border-radius:var(--r-pill);
  padding:5px 12px;font-size:13px;color:var(--tx);cursor:pointer}
.ex-chip:hover{border-color:var(--pri);color:var(--pri)}
.ex-chip:active{background:var(--pri-soft)}
.ex-more{margin-top:8px}
.ex-more summary{cursor:pointer;font-size:12px;color:var(--tx2)}
.ex-more summary:hover{color:var(--pri)}
[data-theme="dark"] .ex-chip{background:#1e2638;border-color:var(--line2)}

/* ============ v79：考试导入工具条 ============ */
.ex-import-bar{display:flex;align-items:center;gap:10px;padding:10px 12px;margin-bottom:10px;
  background:linear-gradient(90deg,rgba(59,110,245,.08),rgba(59,110,245,.02));
  border:1px solid rgba(59,110,245,.25);border-radius:var(--r-md)}
.ex-import-bar .hint{font-size:12px;color:var(--tx2)}
.ex-import-bar .btn[disabled]{box-shadow:none}

/* ============ v79.1：课表整体备注 + 估算日期标记 ============ */
/* 课表页顶部那块「📝 课表备注」（教务把备注写在整张课表底部，不在课块里） */
.sch-note{padding:8px 10px;margin-bottom:10px;border-left:3px solid var(--pri);
  background:var(--pri-soft);border-radius:var(--r-sm);font-size:13px;line-height:1.6}
.sch-note .desc{margin:0;color:var(--tx)}
.sch-note b{color:var(--pri)}
/* 「推算」徽章：教务只给周次、按开学日推算出来的日期 */
.ex-est{display:inline-flex;align-items:center;margin-left:4px;padding:0 5px;font-size:10px;
  line-height:16px;color:#8a6d00;background:#fff4d6;border:1px solid #f0d69a;border-radius:8px}
[data-theme="dark"] .ex-est{color:#ffd77a;background:#3a2f12;border-color:#5c4a1e}
.ex-est-hint{color:var(--tx2);font-size:12px}

/* ============ v79：概览面板流容器 + 真拖拽 ============ */
#homePanels{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start;
  overflow-x:hidden;position:relative}
#homePanels .home-panel{min-width:0;position:relative}
#homePanels .home-panel.full{grid-column:1/-1}
#homePanels .home-panel .panel{margin:0}
#homePanels .stat-grid{margin-bottom:0}
#homePanels .busy-heat{margin-top:0}
#homePanels .exam-countdown{margin-bottom:0}
#homePanels .wb66-todobar{margin-top:0}
/* 拖拽把手（注入到 panel-head） */
.home-panel .panel-head .drag-handle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
  border-radius:var(--r-md);background:var(--bg2,#f1f4fa);font-size:13px;cursor:grab;touch-action:none;color:var(--tx2)}
.home-panel .panel-head .drag-handle:active{cursor:grabbing}
/* stat / todo 无 panel-head，用右上角浮动把手 */
.home-panel .drag-handle.hp-float{position:absolute;top:8px;right:8px;z-index:3;opacity:.4;
  display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
  border-radius:var(--r-md);background:var(--bg2,#f1f4fa);font-size:13px;cursor:grab;touch-action:none;color:var(--tx2)}
.home-panel:hover .drag-handle.hp-float{opacity:1}
/* 拖拽中：原面板半透明占位（保留高度，不塌陷） */
.home-panel.dragging{opacity:.5;outline:2px dashed var(--pri)}
/* ghost 固定跟随 */
.ex-ghost{box-sizing:border-box;border-radius:var(--r);will-change:transform}
/* 插入指示线 */
.drop-indicator{position:absolute;left:0;right:0;height:2px;background:var(--pri);z-index:20;
  border-radius:2px;pointer-events:none;box-shadow:0 0 0 1px rgba(255,255,255,.55)}
/* 桌面端：天气卡在 2 列半宽下只显 5 天（旧 .home-top 窄列同款防溢出） */
@media (min-width:821px){
  #homePanels #dashWx .wx-day:nth-child(n+6){display:none}
}
@media (max-width:820px){
  #homePanels{grid-template-columns:1fr}
}

/* ============ v79.1：实习/实践周提示（周课表 + 日历月视图 + 日面板） ============ */
/* 数据源：教务课表底部「备注」整行 → 按 ; ；切成条目 → 抽「名称 教师 N-M周」。
   命中当前周才渲染；解析不到周次就不显示（宁缺勿噪）。
   ★ 用琥珀色系，跟课程蓝 / 会议紫区分开 —— 实习是「这段时间整周都不在校」的特殊状态。 */
.practice-bar{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;
  padding:7px 10px;margin-bottom:10px;border-left:3px solid #e08a1e;
  background:#fff8ec;border-radius:var(--r-sm);font-size:13px;line-height:1.5}
[data-theme="dark"] .practice-bar{background:#3a2c14;border-left-color:#e8a94a}
/* 日面板里的那份：上面紧贴 panel-head，改用上边距 */
.practice-bar.pb-day{margin:8px 0 0}
.practice-bar .pb-lb{flex:none;padding:1px 7px;border-radius:9px;font-size:11px;font-weight:600;
  color:#7a4b00;background:#f6e2bd;white-space:nowrap}
[data-theme="dark"] .practice-bar .pb-lb{color:#ffd89a;background:#5a421a}
.practice-bar .pb-item{display:inline-flex;align-items:baseline;gap:4px;min-width:0}
.practice-bar .pb-item b{color:#8a4b00;font-weight:600}
[data-theme="dark"] .practice-bar .pb-item b{color:#ffcf87}
.practice-bar .pb-item em{font-style:normal;color:var(--tx2);font-size:12px;white-space:nowrap}
/* 日历日期格里的 🏫 角标：跟着原有的 .dot 一排走，纯 emoji 不加底 */
.pb-dot{font-style:normal;font-size:11px;line-height:1;margin-left:2px;cursor:help}
.legend .pb-dot.lg{margin-left:10px;cursor:default}


/* ══════════════════════════════════════════════════════════════════════
   ⚠⚠⚴ v80 样式**迁移自 public/styles.css**（2026-10-21）
   ⚠⚠⚴ 此前一直追加到 public/styles.css，而 index.html 加载的是
     **assets/styles.css**（26万字节，主样式）⇒ 那些规则**从未生效**
     （症状：图表灰底黑字、柱子是灰的、标签竖排、本期/上期无色差）。
   ══════════════════════════════════════════════════════════════════════ */
/* ★★ v80【概览组件排序弹窗】手机优先：行高一点，手指好按 */
.v80-ho-row{display:flex;align-items:center;gap:8px;padding:9px 4px;border-bottom:1px solid #d8dee6}
.v80-ho-row:last-child{border-bottom:0}
.v80-ho-l{flex:1;font-size:13px;color:#2b2b2b;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v80-ho-btns{display:flex;gap:6px;flex:0 0 auto}
.v80-ho-b{width:34px;height:30px;border-radius:8px;border:1px solid #d8dee6;background:#ffffff;color:#2b2b2b;font-size:14px;cursor:pointer}
.v80-ho-b:disabled{opacity:.35;cursor:default}

/* ★★ v80【打卡格内时长下拉 + 计时按钮】用户：「建议用下拉列表或按钮切换」 */
.v797-ck-dursel{height:22px;max-width:74px;font-size:11px;padding:0 2px;border:1px solid #d8dee6;border-radius:6px;background:#ffffff;color:#2b2b2b}
.v797-ck-tbtn{width:24px;height:22px;border-radius:6px;border:1px solid #d8dee6;background:#ffffff;color:#8a8f99;font-size:12px;cursor:pointer}
.v797-ck-tbtn.is-on{background:#e5534b;border-color:#e5534b;color:#fff}

/* ★★ v80【可拖动钟表】用户：「能不能改成像那种可以拖动的钟表」 */

/* ★★ v80【杂事勾选圈 + 已完成区】用户：「加个圈，勾选就是完成」 */
.chore-home-check{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:22px;height:22px;cursor:pointer}
.chore-home-check input{position:absolute;opacity:0;width:0;height:0}
.chore-home-box{width:19px;height:19px;border-radius:50%;border:2px solid #d8dee6;background:#ffffff;display:block;transition:background .12s,border-color .12s}
.chore-home-check input:checked + .chore-home-box{background:#1f8f6d;border-color:#1f8f6d}
.chore-home-check input:checked + .chore-home-box::after{content:'✓';display:block;color:#ffffff;font-size:12px;line-height:15px;text-align:center;font-weight:700}
.chore-home-donewrap{margin-top:8px;padding-top:8px;border-top:1px dashed #d8dee6}
.chore-home-doneh{margin:0 0 4px;font-size:12px;font-weight:600;color:#8a8f99}
.chore-home-list.is-done .chore-home-item{opacity:.72}
.chore-home-list.is-done .chore-home-txt{text-decoration:line-through}

/* ★★ v80【周记 / 月记弹窗】 */
.v80-j-sum{margin:0 0 10px;padding:9px 10px;border:1px solid #d8dee6;border-radius:10px;background:#ffffff;font-size:13px;color:#2b2b2b}
.v80-j-sum b{color:#1f8f6d;font-size:15px}
.v80-j-lab{display:block;margin:0 0 4px;font-size:12px;color:#8a8f99}
.v80-j-note{width:100%;padding:8px;font-size:14px;line-height:1.5;border:1px solid #d8dee6;border-radius:9px;background:#f6f8fa;color:#2b2b2b;resize:vertical}

/* ★★ v80【钟表记时反馈】用户：「点开要有变化」 */

/* ══════════════════════════════════════════════════════════════════════
   ★★ v80.1【暗色适配】补在我今天新增的组件样式之后
   ⚠⚠⚴ 为什么单独一块：上面那些规则我写的是**亮色硬色值**（本项目
     根本没有 CSS 自定义属性，var(--pri) 之类一律解析失败 → **黑块**，
     用户截图报的就是这个）。
   ⇒ 亮色用硬色值、暗色在这里覆盖，是本项目现有做法
     （对照 v797.css 的 `:root[data-theme="dark"]` 块）。
   ⚠ 覆盖范围必须与「我今天新增的类」**一一对应**，漏一个就会在暗色下露白底
     —— 这正是本项目栽过多次的「dark mode 残余白底」。
   ══════════════════════════════════════════════════════════════════════ */
:root[data-theme="dark"] .v797-ck-cfg,
:root[data-theme="dark"] .v797-ck-cfgrow,
:root[data-theme="dark"] .v80-ho-b,
:root[data-theme="dark"] .chore-home-box,

/* ══════════════════════════════════════════════════════════════════════
   🔴 v80.19【补齐「打卡时段」配置窗缺失的布局样式】
   ⚠⚠⚴ 为什么必须补：`.v797-ck-wds` / `.v797-ck-wd` / `.v797-ck-cfgrow`
     在**整个项目里只有暗色主题引用了它们**（3954-3966 那几行），
     亮色布局**一条都没有** ⇒ 星期勾选框从来没被排过版。
   ⚠ 实测症状（用户 2026-10-06 截图 + 真浏览器复现）：
     7 个星期挤成「一二 / □五□ 六日」两行且互相重叠，
     弹窗里看��清哪几天被勾了 ⇒ 「每日待办 → 打卡时段」这条链路
     即使数据对了，用户也**看不清/点不准**。
   ⚠ 这也是「他总是弄错」的一部分原因：界面本身就有布局缺陷，
     不能全算到识别逻辑头上。
   ══════════════════════════════════════════════════════════════════════ */
.v797-ck-cfglist{display:flex;flex-direction:column;gap:14px}
/* ══════════════════════════════════════════════════════════════════════
   🔴 v80.20【「待添加」视觉标记】
   ⚠⚠⚴ 起因（用户 2026-10-06 反馈「应该是确认完后再添加，而不是先添加后确认」）：
     实测澄清：**数据行为本来就是对的** —— 弹窗打开瞬间 ckSlots=0、备忘录也没写，
     点「取消」什么都不留、点「保存」才写入（smoke/_daily_order.js 实跑证明）。
     ⇒ 用户看到的「已经加进去了」是**视觉误导**：seed 那一行与已保存的时段
       长得一模一样、摆在同一个列表里，读起来像「系统已收下，只是让我看一眼」。
   ⇒ 修法只改**可读性**、不改数据流程（数据已经是对的，改流程反而会引入真 bug）：
     待新增的行加浅色底+ 边框，并在行内挂一个「待添加」徽标。
   ══════════════════════════════════════════════════════════════════════ */
.v797-ck-newhint{background:color-mix(in srgb,var(--pri) 8%,transparent);
  border:1px solid color-mix(in srgb,var(--pri) 28%,transparent);
  padding:8px 10px;border-radius:9px;line-height:1.6}
.v80g-badge{
  display:inline-block;padding:1px 6px;margin:0 2px;border-radius:6px;
  background:var(--pri);color:var(--pri-ink,#fff);font-size:10px;font-weight:700;
}
.v797-ck-cfgrow.is-seed{
  position:relative;
  background:color-mix(in srgb,var(--pri) 7%,transparent);
  border:1px dashed color-mix(in srgb,var(--pri) 45%,transparent);
  border-radius:10px;
  /* ⚠⚠⚴ 徽标改成**绝对定位在左上角外侧**，不再用 padding-left 让位。
     第一版用 `padding-left:30px` 给徽标腾位置 ⇒ 实测**徽标压住了时刻框里的
     「07:00」**（截图里变成「7:00」，左边被盖掉）。
     ⇒ 改为 `top:-9px; left:8px`（放在边框线上，像个标签），
       行内padding 恢复原样 ⇒ **一个像素都不占输入框的位置**。*/
  padding:10px 8px 8px;
}
.v797-ck-seedbadge{
  position:absolute;left:8px;top:-9px;
  padding:1px 6px;border-radius:6px;
  background:var(--pri);color:var(--pri-ink,#fff);
  font-size:9px;font-weight:700;line-height:1.5;white-space:nowrap;
  box-shadow:0 1px 3px rgba(0,0,0,.15);
}
:root[data-theme="dark"] .v797-ck-cfgrow.is-seed{
  background:color-mix(in srgb,var(--pri) 12%,transparent);
  border-color:color-mix(in srgb,var(--pri) 40%,transparent);
}
:root[data-theme="dark"] .v797-ck-newhint{
  background:color-mix(in srgb,var(--pri) 10%,transparent);
  border-color:color-mix(in srgb,var(--pri) 32%,transparent);
}
.v797-ck-cfgrow{
  display:grid;
  /* ⚠ 四列：时刻 / 时长 / 事项 / 删除；星期勾选独占第二行。
     窄屏下事项与星期各占满一行，不挤在一起。*/
  grid-template-columns:auto auto 1fr auto;
  grid-template-areas:"t n l del" "wds wds wds wds";
  gap:8px;align-items:center;
}
.v797-ck-cfgrow .v797-ck-t{grid-area:t}
.v797-ck-cfgrow .v797-ck-n{grid-area:n;width:66px}
.v797-ck-cfgrow .v797-ck-l{grid-area:l;min-width:0}
.v797-ck-cfgrow .v797-ck-del{grid-area:del}
.v797-ck-wds{grid-area:wds;display:flex;gap:6px;flex-wrap:wrap;align-items:center}
/* ⚠ 单个星期标签：固定最小宽度 + 居中 ⇒ 7 个在一行内均分，不会挤成两行错乱。
   ⚠ `flex:1 1 0` + `min-width:34px`：34px 是「☐ + 一」刚好不换行的下限。*/
.v797-ck-wd{
  display:inline-flex;align-items:center;justify-content:center;gap:3px;
  flex:1 1 0;min-width:34px;padding:4px 2px;
  font-size:12px;line-height:1.2;color:var(--tx2,var(--tx,#2b2b2b));
  border:1px solid var(--line);border-radius:8px;cursor:pointer;user-select:none;
}
.v797-ck-wd.on{background:color-mix(in srgb,var(--pri) 14%,transparent);border-color:var(--pri);color:var(--pri);font-weight:600}
.v797-ck-wd input{margin:0;flex:0 0 auto}
/* 🔴 v80.21【让「删除」这个入口一眼可见】
   ⚠⚠⚴ 用户 2026-10-06 反馈「每日打卡无法删除」。实测删除**功能是好的**
     （两条路径都试过：普通时段行删掉后保存生效；seed 行删掉后存 0 条），
     ⇒ 用户找不到入口的根因是这个 ✕ **只有 26px、纯符号、无文字**，
       在手机上极易被当成「关闭按钮」或干脆看不见。
   ⇒ 修法（不改变功能，只提高可发现性）：
     · 常态半透明、**hover / 聚焦时高亮并显示「删除」文字**；
     · 触控设备没有 hover ⇒ 用 `@media (hover:none)` 直接常显文字。
   ⚠ 为什么不用「×」以外的符号：文案比符号更容易被理解，
     这是无障碍的通用做法（图标需配合可访问名称）。 */
.v797-ck-del{
  width:26px;height:26px;line-height:1;border-radius:7px;cursor:pointer;
  border:1px solid var(--line);background:var(--card,#fff);color:var(--tx3,var(--muted));font-size:12px;
  opacity:.55;transition:opacity .12s,border-color .12s,color .12s;
}
.v797-ck-del:hover,.v797-ck-del:focus-visible{
  opacity:1;border-color:#d9534f;color:#d9534f;
  width:auto;min-width:52px;padding:0 8px;font-size:0;font-weight:600;
}
/* ⚠⚠⚴ 伪元素**必须自己声明 font-size**：
     父级用 `font-size:0` 把原HTML 里的「✕」藏掉，
     而 `::after` 会**继承** font-size ⇒ 文字也是 0px ⇒ 看不见
     （实测：按钮 52px 宽、::after content="删除"，但截图里什么都没有）。
   ⇒ 字号写死在 ::after 上，不依赖继承。*/
.v797-ck-del:hover::after,.v797-ck-del:focus-visible::after{
  content:'删除';font-size:11px;font-weight:600;line-height:1;
}
/* 触控设备没有 hover ⇒ 常显文字与高亮，否则用户永远看不到入口。
   ⚠⚠⚴ **文案必须用 ::after 伪元素**，不能靠改 textContent ——
     .v797-ck-del 的 HTML 里写死的是「✕」（pomo.js:ckCfgHTMLFrom），
     在那里加文字会给桌面端也塞一个「删除」，两处都要改、容易漂。
     ⇒ 伪元素把 ✕ 换掉：content 换成「删除」，并把 ✕ 移出可见区。*/
@media (hover:none){
  .v797-ck-del{
    opacity:1;width:auto;min-width:52px;padding:0 8px;font-size:0;
    border-color:#e2b5b3;color:#c94a45;font-weight:600;
  }
  /* ⚠ 同样要自己声明 font-size（父级是 0px，不写就是 0px ⇒ 看不见） */
  .v797-ck-del::after{content:'删除';font-size:11px;font-weight:600;line-height:1}
}
.v797-ck-del:hover{border-color:#d9534f;color:#d9534f}
@media (max-width:400px){
  /*⚠ 窄屏：时刻/时长一行，事项一行，星期一行；
    ⚠⚠⚴ 删除按钮**必须留在同一行**—— 我第一版给它 `grid-template-areas` 单独
      放第三行，结果每条时段下面多出一个孤零零的 ✕ 行，视觉上像多了一条记录，
      用户会以为删不掉（实际能删，但没人愿意去试）。
      ⇒ 改法：把 ✕ 塞进第一行末尾，用绝对定位不占 grid 位。*/
  /*⚠ 窄屏：删除按钮绝对定位在右上角。
    ⚠⚠⚴ 必须给**右侧留出足够空间**：v80.21 让删除钮在触控设备上常显「删除」文字
      （宽约 52px），而这里只留了 30px ⇒ 文字会被弹窗右边裁掉。
      ⇒ padding-right 从 30px 提到 62px（52px 按钮 + 10px 间隙）。*/
  .v797-ck-cfgrow{
    grid-template-columns:auto auto 1fr;
    grid-template-areas:"t n l" "wds wds wds";
    position:relative;padding-right:62px;
  }
  .v797-ck-del{position:absolute;right:0;top:0}
  .v797-ck-wd{min-width:0;padding:4px 0}
}
:root[data-theme="dark"] .v80-j-sum,
:root[data-theme="dark"] .v797-ck-dursel,
:root[data-theme="dark"] .v797-ck-tbtn{background:#212a3d;border-color:#3a465c;color:#e6e9ef}
:root[data-theme="dark"] .v797-ck-cfgrow .v797-ck-t,
:root[data-theme="dark"] .v797-ck-cfgrow .v797-ck-n,
:root[data-theme="dark"] .v797-ck-cfgrow .v797-ck-l,
:root[data-theme="dark"] .v80-j-note{background:#2a344a;border-color:#3a465c;color:#e6e9ef}
:root[data-theme="dark"] .v797-ck-cfghint,
:root[data-theme="dark"] .v797-ck-wd,
:root[data-theme="dark"] .chore-home-when{color:#9aa4b5}
:root[data-theme="dark"] .v80-ho-row{border-bottom-color:#3a465c}
:root[data-theme="dark"] .v80-ho-l{color:#e6e9ef}
:root[data-theme="dark"] .v80-j-lab{color:#9aa4b5}
:root[data-theme="dark"] .v80-j-sum b{color:#5ad6b4}
:root[data-theme="dark"] .v80-ho-b:disabled{opacity:.3}
:root[data-theme="dark"] .chore-home-donewrap{border-top-color:#3a465c}
:root[data-theme="dark"] .chore-home-doneh{color:#9aa4b5}
:root[data-theme="dark"] .chore-home-txt{color:#e6e9ef}

/* ★★ v80.1【统计·趋势 + 环比】用户 2026-10-05：「看不到趋势」「本周和上周做对比」
   ⚠⚠⚴ 用**十六进制硬色值**而不是 var(--x)：
     本项目 :root 里**没有定义任何 CSS 自定义属性**（只有 v797.css 的 --pomo-* 等少数），
     写 var(--pri) 之类会解析失败 → 回退成黑色块（今天已因此被用户截图报过）。
   ⚠ 亮色值取自 v797.css 现有用法（主色 #1f8f6d）；暗色在下面用 data-theme 覆盖。 */
.v80-ck-trend-wrap{margin:8px 0 10px;padding:9px 10px;border:1px solid #d8dee6;border-radius:12px;background:#ffffff}
.v80-ck-tb-hd{display:flex;justify-content:space-between;align-items:baseline;gap:8px;flex-wrap:wrap;font-size:12px;color:#8a8f99}
.v80-ck-tb-hd b{color:#2b2b2b}
.v80-ck-tb-delta{color:#1f8f6d}
.v80-ck-tb-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin:8px 0}
.v80-ck-tb-grid>div{text-align:center;padding:6px 2px;border-radius:9px;background:#f6f8fa}
.v80-ck-tb-grid b{display:block;font-size:14px;color:#1f8f6d}
.v80-ck-tb-grid span{font-size:10px;color:#8a8f99}
/* ⚠ v80.15【撤掉 height:66px】**这才是「文字被压住」的真正源头之一**
   ⚠⚠⚠ 原来这里写死 `height:66px`，而每格内部是三层（`<b>数值` + `<i>柱` + `<span>标签`）
     且 `.v80-tb i` 的高度是**百分比** ⇒ 百分比要相对父容器解析
     ⇒ 父容器写死 66px 时，柱子把数值/标签顶到容器外 ⇒ **文字被压、溢出、叠在一起**。
   ⚠ 前两次我错误地把它「加高到 112px」⇒ **百分比是填满、不是占一部分 ⇒ 柱子更高、文字照样溢出**
     ⇒ 用户连续三次报「没有修好」。
   ⚠ **正解**：`.v80-trend` 改为 `height:auto`（内容自然撑开），
     柱子的百分比改由 `.v80-bar-track`（固定像素高的绘图区）承载 —— 见文件里 v80.15 那组规则。
   ⚠ `align-items:flex-end` 也一起去掉：每格高度不同（0 值格很矮），
     flex-end 会让矮格**贴底对齐**、高格顶出容器 ⇒ 视觉错位。*/
.v80-trend{display:flex;align-items:flex-end;gap:4px;padding-top:6px}
/* ════════════════════════════════════════════════════════════════════
   🔴 v80.15【修根·柱状图文字被压/溢出】同一个病在项目里有**两处**，前两次只治了症状
   ────────────────────────────────────────────────────────────────────
   ⚠⚠⚠ **真正的根因不是「容器太矮」，而是「柱高用百分比 + 容器 height:100%」**：
   ```
   .v80-tb        { height:100% }              ← 每格占满容器
   .v80-tb i      { height:N% }                ← N=100 时 = 填满 .v80-tb 全部高度
   .v80-tb 的子元素: <b>数值</b> + <i>柱</i> + <span>标签</span>
   ```
   ⇒ 柱子（尤其 100% 的那根）**把数值标签和星期标签顶到容器外面**，
     且 `justify-content:flex-end` 让它们从底部溢出 ⇒ **视觉上就是「文字被框框压住」**。
   ⚠ **前两次的错误修法**：我把容器从 66px 加到 112px、从 44px 加到 64px
     ⇒ **百分比是「填满」不是「占一部分」，加高容器只会让柱子更高、文字照样溢出**。
     ⇒ 这就是用户连续三次报「没有修好」的原因。

   ✅ **正确修法（本次）**：给柱子一个**固定的绘图区**，标签放在绘图区**之外**：
     .v80-tb      → 不再 height:100%，改为 flex 列auto（内容自然撑开）
     .v80-tb i    → 包进 .v80-bar-track，**百分比相对 track 解析**（track 有固定像素高）
   ⇒ 柱子永远不会溢出、数值永远在柱子上方、标签永远在最下方。

   ⚠ 为什么不给 track 用百分比：`.v80-tb i` 的高度是**行内 style 的百分比**
     （`style="height:N%"`，见 pomo.js 的 ckTrendBars）⇒ 它需要一个**确定像素高**的父容器
     才能解析 ⇒ track 必须是固定像素。
   ════════════════════════════════════════════════════════════════════ */

/* ① 单期柱图（.v80-trend）：每格 = 数值 + 绘图区 + 标签，三层各占各的 */
.v80-tb{
  flex:1 1 0;display:flex;flex-direction:column;align-items:center;
  min-width:0;
  /* ⚠ 关键：不再 height:100% —— 那会让子元素溢出 */
}
.v80-tb b{display:block;font-size:11px;line-height:1.3;color:var(--pri);
  font-weight:700;white-space:nowrap;margin-bottom:2px}
.v80-bar-track{
  width:100%;height:64px;      /* ← 柱子的百分比相对这个固定高度解析 */
  display:flex;align-items:flex-end;justify-content:center;
}
.v80-tb i{display:block;width:100%;min-height:2px;border-radius:3px 3px 0 0;background:var(--pri)}
.v80-tb span{font-size:10px;line-height:1.2;color:var(--tx3,var(--muted));
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;margin-top:4px}
.v80-ck-tb-note{font-size:10px;color:#8a8f99;margin-top:4px}
/* ★★ v80.12【功能1】柱状图「点击显示该柱数据」
   ⚠⚠⚴ .v80-trend 是 `display:flex;height:66px` 的一行柱区 ⇒ 详情条**另起一块**，
   绝不能当它的 flex 项（否则挤掉 12 根柱的宽度，标签互相压）。
   ⚠ `.v80-tb` 必须给出**可点的手感**（cursor/最小触达区），
   否则用户不知道能点 —— 交互不可见等于不存在。 */
.v80-trend-wrap{margin-top:2px}
.v80-tb{cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
.v80-tb:focus-visible{outline:2px solid #2f9d7c;outline-offset:1px;border-radius:3px}
.v80-tb.is-picked{background:rgba(47,157,124,.14);border-radius:4px}
.v80-tb.is-picked i{background:#1f8f6d}
.v80-tb-hint{font-style:normal;font-size:10px;color:#2f9d7c;margin-left:6px}
.v80-tb-detail{font-size:11px;color:#2b2b2b;background:#f2f6f4;border:1px solid #cfe3da;
  border-radius:5px;padding:5px 7px;margin-top:5px;line-height:1.45;word-break:break-all}
/* ⚠ v80.12【功能2】年维度「今年 vs 去年」区块 */
.v80-ycmp{margin:10px 0 6px;padding-top:8px;border-top:1px dashed #d8dee6}
/* 暗色：必须与上面新增的类一一对应，否则露白底（今天栽过）*/
:root[data-theme="dark"] .v80-ck-trend-wrap{background:#212a3d;border-color:#3a465c}
:root[data-theme="dark"] .v80-ck-tb-hd,
:root[data-theme="dark"] .v80-ck-tb-grid span,
:root[data-theme="dark"] .v80-tb span,
:root[data-theme="dark"] .v80-ck-tb-note{color:#9aa4b5}
:root[data-theme="dark"] .v80-ck-tb-hd b{color:#e6e9ef}
:root[data-theme="dark"] .v80-ck-tb-grid>div{background:#2a344a}
:root[data-theme="dark"] .v80-ck-tb-grid b,
:root[data-theme="dark"] .v80-ck-tb-delta{color:#5ad6b4}
:root[data-theme="dark"] .v80-tb i{background:#4fc79b}
/* ⚠⚠⚴ 暗色必须**逐条**覆盖新增类，否则露白底（本项目今天栽过这个：
   新增了盒子但忘了暗色规则 ⇒ 深色模式下是一块刺眼的白底 + 深色字，看不清）。*/
:root[data-theme="dark"] .v80-tb-hint{color:#5ad6b4}
:root[data-theme="dark"] .v80-tb-detail{background:#2a344a;border-color:#3a465c;color:#e6e9ef}
:root[data-theme="dark"] .v80-tb.is-picked{background:rgba(79,199,155,.2)}
:root[data-theme="dark"] .v80-tb.is-picked i{background:#5ad6b4}
:root[data-theme="dark"] .v80-ycmp{border-top-color:#3a465c}

/* ★★ v80.5【趋势 + 逐日对比】用户 2026-10-05 截图反馈：
   「按周聚合没弄完」「本周vs上周每日时长对比明显没弄完」「有图像和数字会更好」*/
.v80-trend-cap{font-size:10px;color:#8a8f99;margin-bottom:4px}
/* ⚠ 柱顶数字：只看高低读不出「3 分钟还是 3 小时」*/
.v80-tb b{display:block;font-size:9px;color:#1f8f6d;font-weight:600;line-height:1.2;white-space:nowrap}
/* 本期 vs 上期 */
.v80-cmp{margin:10px 0 6px;padding-top:8px;border-top:1px dashed #d8dee6}
.v80-cmp-cap{font-size:11px;color:#2b2b2b;margin-bottom:6px}
.v80-cmp-bars{display:flex;gap:4px;align-items:flex-end}
/* 🔴 v80.16：必须是 **wrap**，不是 nowrap。
   ⚠ legend 用 `flex:0 0 100%` 强制独占一行；配合 `flex-wrap:wrap` 才会真正换行。
   ⚠⚠ 原值 `nowrap` ⇒ basis:100% 换不了行 ⇒ legend 被挤到 7 个 day 之后
   ⇒ 右侧溢出容器（实测 legend right=635 > parent right=397）。*/
.v80-cmp-bars.is-daily{gap:3px;flex-wrap:wrap}
.v80-cmp-col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;min-width:0}
.v80-cmp-col b{font-size:9px;color:#2b2b2b;white-space:nowrap}
.v80-cmp-col i{width:70%;min-height:2px;border-radius:3px 3px 0 0;display:block}
.v80-cmp-col.is-cur i{background:#2f9d7c}
.v80-cmp-col.is-prev i{background:#c9ced6}
.v80-cmp-col span{font-size:9px;color:#8a8f99;margin-top:3px;white-space:nowrap}
/* 逐日：每一天一对柱（本期/上期）*/
/* 🔴🔴 v80.16【修「本周 vs 上周」7 个星期标签全叠在一起】
   ⚠⚠⚴ **根因不是宽度不够，是 day 宽度被算成了 0**（真浏览器实测：
     `.v80-cmp-day` 的 getBoundingClientRect().width === 0），
     而里面的 `.v80-cmp-wd` 是 22px 宽 ⇒ 7 个标签互相重叠 18px，
     视觉上就是左上角那团"乱码"。
   ⚠⚠⚴ **为什么宽度是 0**（这才是真正要修的一处）：
     图例 `.v80-cmp-legend` 和 7 个 day **同处一个 flex 行**，
     而 legend 带 `width:100%` ⇒ 它把整行空间吃光；
     day 是 `flex:1`（= `flex:1 1 0%`）⇒ 每人只分到 gap 的宽度 ⇒ 0。
     ⇒ 修法两条，缺一不可：
       ① legend 用 `flex-basis:100%` **强制换行**（不再参与同一行的分配）；
       ② day 给 `min-width` 兜底（万一将来legend 结构变了，也不会再塌成 0）。*/
/* ⚠⚠⚴ 第三版才修对（判据：必须量到 320px，且断言「7 个 day 平分首行」）：
   前两版都靠 `min-width` 撑 day 宽度 ⇒ **在 320px 下 min-width 根本满足不了**：
     7×38px + 6×4px(gap) = 290px > 容器内容宽 274px
     ⇒ 浏览器判定「7 个 day 放不下一行」⇒ 把第 7 个(周日) 挤到第二行，
       与 legend 共享那一行 ⇒ 实测 day[6] 宽 256px、x 回退到 149（跑到中间）。
   ⇒ 正解：**不给 day 设 min-width**，用 `flex:1 1 0`（basis 0）让 7 个平分整行
     （每格 ~35px，够放「周一」与两位数字），legend 换行独占第二行。*/
.v80-cmp-day{flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center}
.v80-cmp-wd{font-size:9px;color:#8a8f99}
/* ══════════════════════════════════════════════════════════════════════
   🔴 v80.17【让「本周 vs 上周」真的读得出对比】（用户 2026-10-06 截图：
      「他表示不出来这对比」）
   ⚠⚠⚴ 保留柱状图形式（用户明确要柱状图、不要表格），但补齐三样读图必需的东西：
     ① `.is-zero` 零值柱给 2px 底线 ⇒ 两根柱**永远成对出现**
        （原实现 `height:0` ⇒ 上期无数据时那根柱彻底消失，
          图例写着「■ 上期」而画面只有一根柱 ⇒ 看起来根本不是对比图）。
     ② `.v80-cmp-vs` 两期数值**并置**：上=本期、下=上期（分色 + 分行），
        不再用整串 `ckDurText`（那在35px 格子里必然压字）。
     ③ `.v80-cmp-dlt` 差值箭头（↑绿 / ↓红 / 持平灰）⇒ 直接回答「好了还是差了」。
   ══════════════════════════════════════════════════════════════════════ */
.v80-cmp-pair i.is-zero{height:2px !important;min-height:2px;border-radius:1px;opacity:.55}
.v80-cmp-vs{display:flex;flex-direction:column;align-items:center;line-height:1.15;margin-top:3px;gap:1px}
.v80-cmp-vs em{font-style:normal;font-size:10px;font-weight:600;white-space:nowrap;display:flex;align-items:baseline;gap:1px}
.v80-cmp-vs em i{font-style:normal;font-size:8px;font-weight:400;opacity:.7}
/* 🔴 v80.18：单位标注在图例右侧（用户「补一个单位·根据数据自由变化」）
   ⚠ 必须与图例同行放：它解释的是格子里那些数字的单位，
   放在离柱子远的地方（标题旁）会让用户对不上号。*/
.v80-cmp-unit{margin-left:auto;padding-left:10px;font-size:9px;color:var(--tx3,var(--muted));white-space:nowrap}
.v80-cmp-vs .v-cur{color:var(--pri,var(--tx,inherit))}
.v80-cmp-vs .v-prev{color:var(--tx3,var(--muted));font-weight:400;font-size:9px}
.v80-cmp-dlt{font-size:9px;font-weight:700;margin-top:2px;line-height:1.2;white-space:nowrap}
.v80-cmp-dlt.up{color:#1f8f6d}
.v80-cmp-dlt.down{color:#d9534f}
.v80-cmp-dlt.flat{color:var(--tx3,var(--muted));font-weight:400}
.v80-cmp-day.is-idle .v80-cmp-dlt{visibility:hidden}
@media (max-width:400px){
  /*窄屏：单位与「持平」这类弱信息让位，数字与箭头必须留着 */
  .v80-cmp-vs em i{display:none}
}
/* ⚠ v80.15【同病同治】`.v80-cmp-day` 也是三层：`<span .v80-cmp-wd>星期` + `<span .v80-cmp-pair>两根柱` + `<b>数值`
   ⚠ 前一版我只把 `height:44px` 加到 64px ⇒ **同样无效**（百分比是填满，不是占一部分）
   ⇒ 正确：`.v80-cmp-day` 放开高度、`.v80-cmp-pair` 作为**固定像素高的绘图区**承载百分比柱
   ⚠ `display:flex` 等原有属性一个都不能丢（丢了两根柱会竖排）。
   🔴 v80.16：这条**重复声明已删除** —— 它写在上面那条后面、位置更靠后，
      其中的 `flex:1` + `min-width:0` 会把上面 v80.16 的 `min-width:30px` 覆盖掉
      ⇒ 改上面那条等于没改（**同病：CSS 重复声明靠后者胜出**）。*/
.v80-cmp-pair{display:flex;align-items:flex-end;gap:2px;height:56px;width:100%;justify-content:center}
.v80-cmp-pair i{width:34%;min-height:0;border-radius:2px 2px 0 0;display:block}
.v80-cmp-pair i.is-cur{background:#2f9d7c}
.v80-cmp-pair i.is-prev{background:#c9ced6}
.v80-cmp-day b{font-size:8px;color:#2b2b2b;margin-top:2px;white-space:nowrap}
.v80-cmp-day b.up{color:#1f8f6d}
.v80-cmp-day b.down{color:#d9534f}
/* 🔴 v80.16：legend 必须**独占一行**（不能与 7 个 day 抢同一行）。
   ⚠ `min-width:100%` 优先级高于 flex-shrink ⇒ 强制占满整行 ⇒ 必然换行。
   ⚠⚠ **不要给 day 设 min-width**（见上面那条注释：320px 下 7×38px 放不下，
     会把第 7 个 day 挤到第二行与 legend 同行）。*/
.v80-cmp-legend{flex:0 0 100%;min-width:100%;font-size:9px;color:#8a8f99;text-align:right;margin-top:3px}
.v80-cmp-legend span{margin:0 3px 0 8px}
.v80-cmp-legend span.is-cur{color:#2f9d7c}
.v80-cmp-legend span.is-prev{color:#c9ced6}
.v80-cmp-note{font-size:10px;color:#8a8f99}
/* 暗色（与新增类一一对应，否则露白底）*/
:root[data-theme="dark"] .v80-cmp{border-top-color:#3a465c}
:root[data-theme="dark"] .v80-cmp-cap,
:root[data-theme="dark"] .v80-tb b,
:root[data-theme="dark"] .v80-cmp-day b{color:#e6e9ef}
:root[data-theme="dark"] .v80-tb b{color:#5ad6b4}
:root[data-theme="dark"] .v80-trend-cap,
:root[data-theme="dark"] .v80-cmp-note,
:root[data-theme="dark"] .v80-cmp-wd,
:root[data-theme="dark"] .v80-cmp-legend,
:root[data-theme="dark"] .v80-cmp-col span{color:#9aa4b5}
:root[data-theme="dark"] .v80-cmp-col i.is-prev,
:root[data-theme="dark"] .v80-cmp-pair i.is-prev{background:#4a5468}
:root[data-theme="dark"] .v80-cmp-day b.down{color:#ff7b6f}

/* ★★ v80.6【打卡分析】用户：「分析我这周做了什么 什么最多/最少 打卡有无缺勤」*/
.v80-an{margin:8px 0 6px;padding-top:8px;border-top:1px dashed #d8dee6}
.v80-an-cap{font-size:11px;color:#2b2b2b;margin-bottom:6px}
.v80-an-row{display:flex;align-items:center;gap:6px;margin-bottom:4px}
.v80-an-l{font-size:11px;color:#2b2b2b;width:64px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v80-an-bar{flex:1;height:9px;background:#f0f2f5;border-radius:5px;overflow:hidden;min-width:0}
.v80-an-bar i{display:block;height:100%;background:#2f9d7c;border-radius:5px}
.v80-an-row b{font-size:10px;color:#1f8f6d;white-space:nowrap;min-width:38px;text-align:right}
.v80-an-c{font-size:9px;color:#8a8f99;min-width:20px}
.v80-an-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:5px;margin:7px 0}
.v80-an-grid>div{text-align:center;padding:6px 2px;border-radius:9px;background:#f6f8fa}
.v80-an-grid b{display:block;font-size:14px;color:#1f8f6d}
.v80-an-grid span{font-size:9px;color:#8a8f99}
.v80-an-note{font-size:10px;color:#8a8f99}
:root[data-theme="dark"] .v80-an,
:root[data-theme="dark"] .v80-cmp{border-top-color:#3a465c}
:root[data-theme="dark"] .v80-an-cap,
:root[data-theme="dark"] .v80-an-l{color:#e6e9ef}
:root[data-theme="dark"] .v80-an-bar{background:#2a344a}
:root[data-theme="dark"] .v80-an-grid>div{background:#2a344a}
:root[data-theme="dark"] .v80-an-row b{color:#5ad6b4}
:root[data-theme="dark"] .v80-an-c,
:root[data-theme="dark"] .v80-an-note{color:#9aa4b5}

/* ★★ v80.7【倒计时·自由设定时长】用户截图：想像原生时间选择器那样自由填「时:分」*/
.v797-freedur{display:flex;align-items:center;gap:5px;flex-wrap:wrap;margin:6px 0 2px;padding:6px 8px;border:1px solid #d8dee6;border-radius:10px;background:#f6f8fa}
.v797-freedur-lab{font-size:11px;color:#8a8f99;margin-right:2px}
.v797-freedur-box{display:inline-flex;align-items:center;gap:2px;background:#fff;border:1px solid #d8dee6;border-radius:8px;padding:2px 5px}
.v797-freedur-box input{width:38px;padding:3px 2px;font-size:14px;text-align:center;border:0;background:transparent;color:#2b2b2b;font-variant-numeric:tabular-nums}
.v797-freedur-box input:focus{outline:1px solid #1f8f6d;border-radius:4px}
.v797-freedur-box i{font-style:normal;color:#8a8f99;font-size:14px}
.v797-freedur-step{padding:3px 7px;font-size:11px;border:1px solid #d8dee6;border-radius:7px;background:#fff;color:#2b2b2b;cursor:pointer}
.v797-freedur-ok{padding:3px 10px;font-size:11px;border:0;border-radius:7px;background:#1f8f6d;color:#fff;cursor:pointer}
:root[data-theme="dark"] .v797-freedur{background:#2a344a;border-color:#3a465c}
:root[data-theme="dark"] .v797-freedur-lab,
:root[data-theme="dark"] .v797-freedur-box i{color:#9aa4b5}
:root[data-theme="dark"] .v797-freedur-box,
:root[data-theme="dark"] .v797-freedur-step{background:#212a3d;border-color:#3a465c;color:#e6e9ef}
:root[data-theme="dark"] .v797-freedur-box input{color:#e6e9ef}
:root[data-theme="dark"] .v797-freedur-ok{background:#4fc79b;color:#16233a}

/* ══════════════════════════════════════════════════════════════════════
   ★★ v80.1【配色与可读性返工】用户 2026-10-21 截图指出 4 个问题：
   ① 「本周和上期你两个都是黑色的，有什么区别？」
      ⇒ 本期/上期必须**明显不同色**（深/浅同色系）+ 图例 + 数值直标。
   ② 「最多的最少的颜色都一样，那没区别」
      ⇒ 最多用**主色深**，最少用**警示色**，并直标「最多/最少」字样。
   ③ 「一个是确定格数，一是没有大概填格数，有什么区别吗？」
      ⇒ 「完成 0/1」里 **1 是应打卡格数、0 是完成格数**，必须标清哪个是哪个，
         写「完成 0/1」会被读成「完成度 0/1」。
   ④ 「每日那种柱状图，你最起码显示一个数字」
      ⇒ 柱顶标数字（v80.5 已加，但样式没生效 ⇒ 现在才真的显示出来）。
   ⚠⚠⚴ 颜色取自主样式既有变量（--pri / --pri-ink 等），
     避免写死色值在暗色模式下变白底。
   ══════════════════════════════════════════════════════════════════════ */

/* ① 本期 vs 上期：本期=主色实心，上期=浅灰斜纹（打印也能分辨）*/
.v80-cmp-col.is-cur i{background:var(--pri);opacity:1}
.v80-cmp-col.is-prev i{background:var(--pri);opacity:.28}
.v80-cmp-col.is-prev{border-bottom:1px dashed var(--pri)}
.v80-cmp-col b.is-curTxt{color:var(--pri);font-weight:700}
.v80-cmp-col span{color:var(--tx3,var(--muted))}
/* 逐日对比：本期/上期同色系不同深浅 */
.v80-cmp-pair i.is-cur{background:var(--pri);opacity:1}
.v80-cmp-pair i.is-prev{background:var(--pri);opacity:.3}
.v80-cmp-day b.up{color:var(--pri);font-weight:700}
.v80-cmp-day b.down{color:#d9534f}
.v80-cmp-legend{color:var(--tx2,var(--tx,var(--muted)));text-align:left;margin-top:6px}
.v80-cmp-legend span.is-cur{color:var(--pri);font-weight:700}
.v80-cmp-legend span.is-prev{color:var(--pri);opacity:.6}

/* ② 最多 vs 最少：明确色差 + 文字标签 */
.v80-an-row:nth-child(1) .v80-an-bar i{background:var(--pri)}
.v80-an-row:nth-child(1) b{color:var(--pri)}
.v80-an-grid>div:first-child{background:color-mix(in srgb,var(--pri) 12%,transparent)}
.v80-an-grid>div:nth-child(2){background:rgba(217,83,79,.12)}
.v80-an-grid>div:nth-child(2) b{color:#d9534f}

/* ④ 柱顶数字：确保可见（原样式没生效 ⇒ 之前柱子上方什么都没有）*/
.v80-tb b{display:block;font-size:11px;font-weight:700;color:var(--pri);line-height:1.3;
  white-space:nowrap;overflow:visible;text-align:center}
.v80-tb i{background:var(--pri);opacity:.9}
.v80-trend .v80-tb:nth-last-child(-n+1) i{opacity:1}
.v80-trend-cap{color:var(--tx2,var(--muted));font-size:12px;margin-bottom:4px}

/* ③ 分数写清口径：加「格」字样 + 分母说明 */
.v80-ck-tb-grid>div:first-child b::after{content:'';}
.v80-ck-tb-note{color:var(--tx3,var(--muted));font-size:11px;line-height:1.6}

/* 容器别再是灰底黑字：用主样式的卡片底 */
.v80-ck-trend,.v80-an,.v80-cmp{background:var(--card,#fff);border:1px solid var(--line);border-radius:12px;padding:10px;margin:8px 0}
.v80-ck-tb-grid>div,.v80-an-grid>div{background:var(--bg,#f6f8fa);border-radius:9px}
:root[data-theme="dark"] .v80-ck-trend,
:root[data-theme="dark"] .v80-an,
:root[data-theme="dark"] .v80-cmp{background:#212a3d;border-color:#3a465c}
:root[data-theme="dark"] .v80-ck-tb-grid>div,
:root[data-theme="dark"] .v80-an-grid>div{background:#2a344a}
:root[data-theme="dark"] .v80-tb b,
:root[data-theme="dark"] .v80-an-row b,
:root[data-theme="dark"] .v80-ck-tb-delta{color:#5ad6b4}
:root[data-theme="dark"] .v80-cmp-col.is-prev i,
:root[data-theme="dark"] .v80-cmp-pair i.is-prev{background:#4fc79b;opacity:.4}

/* ★★ v80.2【标签排版】用户 2026-10-21 截图：趋势图的日期标签竖着挤成一团。
   ⚠⚠⚴ 之前给 .v80-tb span 加了 transform:scale(.9) + white-space:nowrap，
     在 8 根柱子、每格只有 ~30px 宽时，文字互相压 ⇒ 看起来像竖排。
   ⇒ 标签**只标首尾与峰值**（其余留空），且不缩放、超出用省略号。*/
/* ⚠⚠⚠ v80.15【撤掉v80.14 的错误修法】
   ⚠⚠⚠ **这里曾经被我改成 `height:112px`，那是错的、现已撤销。**
   ⚠ 根因（v80.15 才真正找到）：**不是「容器太矮」，而是「柱高用百分比 + .v80-tb height:100%」**
        .v80-tb { height:100% }   ← 每格占满容器
        .v80-tb i { height:N% }   ← N=100 时= **填满 .v80-tb 的全部高度**
        子元素顺序：`<b>数值` + `<i>柱` + `<span>标签`
        ⇒ 柱子把数值标签与星期标签**顶到容器外面**（`justify-content:flex-end` ⇒ 从底部溢出）
        ⇒ **视觉上就是「文字被框框压住、挡住」**
   ⚠ 百分比是「**填满**」不是「占一部分」⇒ **加高容器只会让柱子更高、文字照样溢出**
     ⇒ 这就是用户连续三次报「没有修好」的原因。
   ⚠ **正解在 `.v80-bar-track` 那组规则里**：给柱子一个**固定像素高的绘图区**，
     标签放在绘图区之外 ⇒ 柱子永不溢出。
     ⚠ 而 `.v80-trend` 本身**必须保持 `height:auto`**（让内容自然撑开）。*/
.v80-trend{gap:6px;height:auto}
.v80-tb{position:relative;min-width:0}
.v80-tb span{
  display:block;font-size:10px;line-height:1.2;color:var(--tx3,var(--muted));
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
  transform:none !important;      /* ⚠ 覆盖 v80 迁移带来的 scale(.9) */
  margin-top:4px;
}
.v80-tb.is-blank span{visibility:hidden}
/* 峰值那一格高亮，避免"哪个最高"要靠猜 */
.v80-tb.is-max span{color:var(--pri);font-weight:700}
/* 逐日对比的星期标签同理：7 格挤 390px 也会竖排 */
.v80-cmp-bars.is-daily{gap:4px}
.v80-cmp-wd{font-size:11px;white-space:nowrap;transform:none !important}
/* 🔴 v80.16：时长文字在窄屏会互相压字（实测 320px 下每格仅 ~38px，
   而「33 分钟」整串要 ~44px ⇒ 相邻格子文字糊成一片，
   正是用户截图里"文字全挤在一起"的第二层）。
   ⇒ 数字与单位分开：数字永远完整显示，
      单位在窄屏由媒体查询隐藏，完整值仍在 title 上⇒ 不丢信息。
   🔴 v80.17：这三条规则**已删除** —— 原本服务于「单根柱下方一串时长」的旧结构，
      现在那串时长已由 `.v80-cmp-vs`（两期并置）承担，
      留着会误导下一个维护者以为 `<b>` 里还装时长。*/
/* 窄屏：藏掉单位，只留数字（避免 7 格互相压字） */
@media (max-width:400px){
  .v80-cmp-day b i{display:none}
}
.v80-cmp-legend{font-size:11px;display:flex;gap:12px;align-items:center}
.v80-cmp-legend span{display:inline-flex;align-items:center;gap:3px}
.v80-cmp-legend span::before{content:'';width:9px;height:9px;border-radius:2px;display:inline-block}
.v80-cmp-legend span.is-cur::before{background:var(--pri)}
.v80-cmp-legend span.is-prev::before{background:var(--pri);opacity:.3}

/* ══════════════════════════════════════════════════════════════════════
   ★★ v80.3【打卡格的交互反馈】用户 2026-10-21 截图问：
   「点击记时后可以看到时间（或者图标会有变化那就更好了）」
   ⚠⚠⚴ 这些规则之前写在 public/styles.css（**未被 index.html 引用**）
     ⇒ 从未生效。已迁到主样式（assets/styles.css）。
   ══════════════════════════════════════════════════════════════════════ */

/* 计时中：按钮变红 + 图标换成「停」+ 整格呼吸（一眼看得出在计时） */
.v797-ck-tbtn.is-on{background:var(--pri);border-color:var(--pri);color:var(--pri-ink,#fff)}
/* 计时中整行加左侧色条：不用盯着按钮也知道这格在跑 */
.v797-ck-row.is-timing{border-left:3px solid var(--pri)}
.v797-ck-row.is-timing .v797-ck-lab::after{
  content:'计时中';margin-left:6px;font-size:10px;color:var(--pri);
  padding:1px 5px;border-radius:6px;background:color-mix(in srgb,var(--pri) 14%,transparent)
}
/* 计时中显示**实时已计时长**（用户要「可以看到时间」）*/
.v797-ck-elapsed{font-size:11px;color:var(--pri);font-variant-numeric:tabular-nums;margin-left:6px}

/* 已完成且有时长：格内直接标出「记了 X」 */
.v797-ck-dur{font-size:10px;color:var(--tx3,var(--muted));margin-left:6px}
.v797-ck-row.is-done{background:color-mix(in srgb,var(--pri) 6%,transparent)}

/* 「⋯」菜单：原本只有"跳过"，太窄且含义不清 ⇒ 加宽 + 说明 */
.v797-ck-skipbtn{white-space:nowrap}
.v797-ck-menu{display:none;gap:4px}
.v797-ck-row.is-open .v797-ck-menu{display:inline-flex}
.v797-ck-menu button{white-space:nowrap;font-size:11px;padding:2px 7px;border:1px solid var(--line);border-radius:6px;background:var(--card);color:var(--tx,var(--text));cursor:pointer}
.v797-ck-menu button:hover{border-color:var(--pri);color:var(--pri)}
/* 下拉与计时按钮在小屏不被挤扁 */
.v797-ck-dursel{max-width:78px}
@media (max-width:360px){.v797-ck-dursel{max-width:62px;font-size:10px}.v797-ck-tbtn{width:22px}}

/* ═══════════════════════════════════════════════════════════════════
   ★★ v80.4【打卡并入待办】用户：「加个设置让我选（打卡组件 or 合并到代办）」
   ⚠⚠⚴ 必须写在 **assets/styles.css**（index.html 真正加载的那个，269KB），
     写进 public/styles.css 的话 86 条规则会**从未生效**（本项目栽过）。
   ═══════════════════════════════════════════════════════════════════ */
.v80-ck-todo{margin:8px 0 0;padding:10px 12px;border:1px solid var(--line);border-radius:12px;background:var(--card)}
.v80-ck-todo-hd{font-size:12px;font-weight:600;opacity:.75;margin-bottom:6px}
.v80-ck-todo-row{display:flex;align-items:center;gap:10px;padding:8px 4px;cursor:pointer;border-radius:8px;transition:background .15s}
.v80-ck-todo-row:hover{background:rgba(127,127,127,.09)}
.v80-ck-todo-row:active{opacity:.6}
/* 未勾选的空圈：语义写进 DOM（class + title），不只靠颜色 */
.v80-ck-todo-box{flex:0 0 auto;width:17px;height:17px;border-radius:50%;border:2px solid var(--pri,var(--pri,#4a8cff));display:inline-block;background:transparent}
.v80-ck-todo-lab{flex:1 1 auto;font-size:13px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.v80-ck-todo-go{flex:0 0 auto;font-size:11px;opacity:.5;white-space:nowrap}
/* 深色模式：空圈在暗背景上要够亮，否则「看不见圈」=「不知道可点」 */
:root[data-theme="dark"] .v80-ck-todo-box{border-color:#5b9cff;background:rgba(91,156,255,.10)}

/* ============================================================================
 * 动画开场 · 吉祥物登场（v88，INTRO_SETS.mascot）
 * ---------------------------------------------------------------------------
 * 用户点名要的「一个开屏动画」。主角是当季吉祥物（chibi/*.webp），
 * 叠加弹入 + 呼吸 + 投影 + 光晕。
 * ⚠ 时长必须 ≤ playIntro() 的 2600ms 收场（app.js:7587），
 *   否则动画还没演完就被摘掉 ⇒ 用户看到「动一半就消失」。
 * ⚠ prefers-reduced-motion：尊重系统设置，去掉动画只留静态形象。
 * ========================================================================== */

.intro-mascot-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.intro-mascot {
  position: relative;
  z-index: 2;
  /* 🔴 v89【P0·用户原话「这个开屏动画不会自动适配」】
     ⚠ 原值是死写的 `width:168px`。核实过：这一整块**没有任何** @media / vw / vh 适配
       （唯一的 @media 是 prefers-reduced-motion，那是关动画不是调尺寸）。
       ⇒ 在 3x 密度手机上，168 CSS px 换算成物理 px 只占屏幕宽度的 15%~46%，
         在窄屏（≤360px）上占比过大、在平板/大屏上又显得很小 ⇒ 「不会自动适配」。
     ⚠ 为什么用 clamp 而不是单纯 vw：
       纯 vw 在超宽屏（折叠屏展开、横屏平板）会把图撑得过大；
       clamp(下限, 视口比例, 上限) 两头都夹住，任何屏幕都合理。
       ⚠ 168px→ 换算成"约占 46vw"，并把上限压在 260px，
         保证大屏上不会糊成一张巨图。 */
  width: clamp(120px, 46vw, 260px);
  height: auto;
  object-fit: contain;
  /* 🔴 v90【实测修正：这条规则必须加作用域】
     原因：本选择器原本是裸 `.intro-mascot`，而这个 class 一旦出现在
     **全屏遮罩容器**（.intro-ov）上，遮罩就会被当成"一张图片"解析尺寸
     —— width 被夹到 46vw、height:auto 塌成 0（实测 rect=154×0/169×0/237×0）
     ⇒ 整屏只剩右上角「跳过」按钮。
     ⇒ 现在限定为 `img.intro-mascot`：只命中吉祥物图片本身，
       遮罩即使再挂同名 class 也不会被套上图片尺寸。
     ⚠ 与 p1-fixes-v86.css 里「v90 已把 intro-mascot 从 ov.className 去掉」是双保险：
       一处防"用错"，一处防"再用"。 */
  /* ⚠ 弹入动画：弹入+ 呼吸缩放 */
  animation:
    introMascotPop 1.05s cubic-bezier(.22, 1.2, .36, 1) both,
    introMascotBreathe 1.5s ease-in-out 1.05s infinite;
}
/* ⚠ v89：矮屏（横屏 / 小屏）再收一档，避免"吉祥物+光晕+投影"这套组合超出可视高度。
   高度按视口高度算，横屏时 vh 很小，不收就会顶出屏幕。 */
@media (max-height: 480px) {
  .intro-mascot { width: clamp(96px, 26vh, 150px); }
}
@keyframes introMascotPop {
  from { opacity: 0; transform: translateY(24px) scale(.72); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
@keyframes introMascotBreathe {
  0%, 100% { transform: translateY(0)    scale(1); }
  50%      { transform: translateY(-5px) scale(1.04); }
}

/* 地面投影：随呼吸同步缩放，制造「站在地上」的重量感
   🔴 v89：透明度从 .22 降到 .12。
     ⚠ 原值 rgba(0,0,0,.22) 在浅色底上是一块**肉眼可见的黑斑**
       （用户抱怨的「很多黑色的东西」之一），且与浅暖底冲突明显。
     ⚠ 降到 .12 后仍保留"脚下有重量"的观感，又不至于变成脏黑块。 */
.intro-mascot-shadow {
  position: relative;
  z-index: 1;
  width: clamp(76px, 30vw, 160px);
  height: 12px;
  margin-top: 2px;
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(90, 70, 50, .12), rgba(90, 70, 50, 0) 72%);
  animation: introShadowBreathe 1.5s ease-in-out 1.05s infinite;
}
@keyframes introShadowBreathe {
  0%, 100% { transform: scaleX(1);    opacity: .9; }
  50%      { transform: scaleX(.86);  opacity: .65; }
}

/* 光晕：慢速呼吸，营造「登场」的仪式感 */
.intro-mascot-glow {
  position: absolute;
  z-index: 0;
  width: clamp(150px, 66vw, 330px);
  height: clamp(150px, 66vw, 330px);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 246, 214, .58), rgba(255, 246, 214, 0) 66%);
  animation: introGlowPulse 1.5s ease-in-out infinite;
}
@keyframes introGlowPulse {
  0%, 100% { transform: scale(.9);  opacity: .55; }
  50%      { transform: scale(1.06); opacity: .95; }
}

/* 夜间模式：光晕压暗，避免白黄光圈在深色页面上刺眼 */
:root[data-theme="dark"] .intro-mascot-glow {
  background: radial-gradient(circle, rgba(120, 140, 200, .34), rgba(120, 140, 200, 0) 66%);
}

@media (prefers-reduced-motion: reduce) {
  .intro-mascot,
  .intro-mascot-shadow,
  .intro-mascot-glow { animation: none !important; }
  .intro-mascot { opacity: 1; transform: none; }
}
