/* ============================================================
   校园通知系统 · 工作台样式

   设计令牌 + 布局骨架 + 通用组件。
   与旧页面互不影响：旧页面只引用 css/style.css，不引用本文件。
   主色沿用管理端既有的微信绿，保证新旧视觉一致。
   ============================================================ */

@font-face {
  font-family: "SourceHanSansSC";
  src: url("../SourceHanSansSC-Medium-2.otf") format("opentype");
  font-display: swap;
}

:root {
  /* 主色 */
  --primary: #07C160;
  --primary-dark: #06AD56;
  --primary-soft: #E8F8EF;

  /* 布局尺寸 */
  --sidebar-w: 224px;
  --topbar-h: 60px;

  /* 文字三级 */
  --t1: #1F2937;
  --t2: #6B7280;
  --t3: #9CA3AF;

  /* 背景与边界 */
  --bg: #F5F7FA;
  --surface: #FFFFFF;
  --border: #E5E7EB;
  --border-strong: #D1D5DB;

  /* 语义色 */
  --blue: #3B82F6;
  --amber: #F59E0B;
  --red: #EF4444;
  --purple: #8B5CF6;

  /* 圆角 */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;

  /* 阴影 */
  --shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .04);
  --shadow-lg: 0 10px 30px rgba(0, 0, 0, .08);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--t1);
  font-family: "SourceHanSansSC", "Microsoft YaHei", -apple-system, "Segoe UI", sans-serif;
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}

/* 图标兜底尺寸：icon() 生成的 svg 只带 viewBox，没有 width/height 属性，
   在 flex 等容器里会被当成可拉伸的替换元素撑开。这里给一个默认尺寸，
   各处再按需覆盖（.stat-label / .empty 等）。 */
.ico { display: block; width: 18px; height: 18px; flex: 0 0 18px; }

/* ---------------- 细滚动条 ----------------
   系统默认粗条在玻璃主题里很突兀：全家桶换成细圆角半透明条——
   只在滚动区域出现，平时淡隐、悬停加深；Firefox 用 scrollbar-color 同款。 */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(100, 116, 139, .42) transparent;
}
*::-webkit-scrollbar { width: 7px; height: 7px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: rgba(100, 116, 139, .34);
  border-radius: 8px;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(100, 116, 139, .58); }
*::-webkit-scrollbar-corner { background: transparent; }

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

.sidebar {
  width: var(--sidebar-w);
  flex: 0 0 var(--sidebar-w);
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  z-index: 40;
}

.brand {
  padding: 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--border);
}
.brand-logo { width: 30px; height: 30px; border-radius: 7px; object-fit: cover; }
.brand-name { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.nav { flex: 1; overflow-y: auto; padding: 8px 8px 24px; }
.nav-group-title {
  padding: 12px 10px 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--t3);
  letter-spacing: .4px;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  color: var(--t2);
  cursor: pointer;
  user-select: none;
  transition: background .15s, color .15s;
}
.nav-item:hover { background: #F3F4F6; color: var(--t1); }
.nav-item.active { background: var(--primary-soft); color: var(--primary-dark); font-weight: 600; }
.nav-item .soon { margin-left: auto; font-size: 11px; color: var(--t3); }

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

.topbar {
  height: var(--topbar-h);
  flex: 0 0 var(--topbar-h);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 20px;
  position: sticky;
  top: 0;
  z-index: 30;
}
/* 仪表盘视图顶栏透明：壁纸层(fixed inset:0)从顶栏区域透出；
   标题文字隐藏（「仪表盘 / 今天的待办与概况」），菜单、刷新与账号区保留。 */
body[data-route="dashboard"] .topbar {
  background: transparent;
  border-bottom-color: transparent;
}
body[data-route="dashboard"] #pageTitle,
body[data-route="dashboard"] #pageSub {
  display: none;
}
body[data-route="dashboard"] .topbar-user {
  border-left-color: rgba(255, 255, 255, .22);
}
/* 透明后文字浮在壁纸（暗色遮罩）上，统一提亮保证可读 */
body[data-route="dashboard"] .topbar {
  color: #fff;
}
body[data-route="dashboard"] .topbar .topbar-sub {
  color: rgba(255, 255, 255, .72);
}
body[data-route="dashboard"] .topbar .btn-ghost {
  color: rgba(255, 255, 255, .9);
}
/* ghost 按钮默认 hover 是浅白实底，浮在暗壁纸上太刺眼：换成柔和玻璃态 */
body[data-route="dashboard"] .topbar .btn-ghost:hover {
  background: rgba(255, 255, 255, .16);
}
.topbar-title { font-size: 17px; font-weight: 600; }
.topbar-sub { color: var(--t3); font-size: 13px; }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.topbar-user {
  margin-left: 14px;
  padding-left: 14px;
  border-left: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
}

.view { flex: 1; padding: 20px; min-width: 0; }

/* ---------------- 卡片 ---------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
}
.card-head {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
}
.card-head .spacer { margin-left: auto; }
.card-body { padding: 16px; }

/* ---------------- 统计卡 ---------------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px;
}
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--t3);
  border-radius: var(--r-md);
  padding: 16px;
  box-shadow: var(--shadow);
}
.stat-card.c-green { border-left-color: var(--primary); }
.stat-card.c-blue { border-left-color: var(--blue); }
.stat-card.c-amber { border-left-color: var(--amber); }
.stat-card.c-purple { border-left-color: var(--purple); }
.stat-label { display: flex; align-items: center; gap: 6px; color: var(--t2); font-size: 13px; }
.stat-label .ico { width: 16px; height: 16px; flex: 0 0 16px; }
.stat-value { margin-top: 8px; font-size: 28px; font-weight: 700; line-height: 1.1; }
.stat-value small { margin-left: 2px; font-size: 14px; font-weight: 500; color: var(--t3); }
.stat-foot { margin-top: 8px; font-size: 12px; color: var(--t3); }

/* ---------------- 仪表盘十二列网格 ----------------
   问候条之下整屏 12 列，行高固定；默认摆法与每张卡的尺寸见 dashboard.js 的
   DASH_DEFAULTS（教师 next/today/week 与超管 schoolcd/school 互斥，各占一套坐标，
   导航 23 张铺在 row12-14 的 col5-12）。

   两个刻意的选择（都是为了让「拖大小」真的有用）：
   ① **12 列**而不是 8 列 —— 每一档宽度步进只有 1/12 屏，可以细调；
   ② **行高固定**（`--dash-row`）而不是 auto —— auto 行的高度由该行最高的内容决定，
      于是拖高度时卡片被内容顶住、看着「拖了没反应」。固定行高后 h 就是确定的视觉高度，
      内容装不下时在卡内滚动（见下面的 .dash-card）。 */
.dash-grid8 {
  --dash-row: 72px;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: var(--dash-row);
  gap: 14px;
}
/* 每张卡的兜底坐标：与 dashboard.js 的 DASH_DEFAULTS 保持一致
   （正常运行由 applyDashLayout 写 inline 样式，这里只保证脚本没跑起来时也不乱） */
.dg-schoolcd  { grid-column: 5 / 8;   grid-row: 1 / 6; }
.dg-school    { grid-column: 1 / 5;   grid-row: 4 / 11; }
.dg-next      { grid-column: 5 / 8;   grid-row: 1 / 3; }
.dg-today     { grid-column: 5 / 8;   grid-row: 3 / 6; }
.dg-week      { grid-column: 1 / 5;   grid-row: 4 / 11; }
.dg-todo      { grid-column: 8 / 13;  grid-row: 7 / 12; }
.dg-leavereg  { grid-column: 11 / 13; grid-row: 3 / 5; }
.dg-classes   { grid-column: 1 / 5;   grid-row: 11 / 14; }
.dg-todaydate { grid-column: 1 / 5;   grid-row: 1 / 4; }
.dg-cal       { grid-column: 8 / 11;  grid-row: 1 / 5; }
.dg-duty      { grid-column: 8 / 10;  grid-row: 5 / 7; }
.dg-notice    { grid-column: 5 / 8;   grid-row: 6 / 9; }
.dg-song      { grid-column: 5 / 8;   grid-row: 9 / 12; }
.dg-devices   { grid-column: 11 / 12; grid-row: 5 / 7; }
.dg-notifstat { grid-column: 10 / 11; grid-row: 5 / 7; }
.dg-checkstat { grid-column: 12 / 13; grid-row: 5 / 7; }
.dg-leavestat { grid-column: 11 / 13; grid-row: 1 / 3; }
/* 站点卡（每个网址一张）：兜底时流式排布不叠卡，正常流程由 applyDashLayout 写精确坐标 */
.dash-grid8 > [class*="dg-nav-"] { grid-column: auto; grid-row: auto; }

/* 卡片高度由网格行高决定：内容超出就在卡内滚动，绝不把整行撑高
   —— 否则拖高度时会被内容顶住，看着像「调节无效」。 */
.dash-grid8 > .dash-card {
  display: flex; flex-direction: column; overflow: hidden;
}
.dash-grid8 > .dash-card > .card-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  /* 不要滑动条：调大小看的是内容随空间变化（滚轮仍可滚），而不是冒出一条滚动条 */
  scrollbar-width: none;                        /* Firefox */
}
.dash-grid8 > .dash-card > .card-body::-webkit-scrollbar { display: none; }
/* 没有 card-body 的（统计行是 stat-grid）也得挡住溢出，缩到最小档时不会糊出卡片外 */
.dash-grid8 > [data-dg] { overflow: hidden; }

/* 上下区块（问候条 → 八列网格）之间留出呼吸感 */
.dash-root > * + * { margin-top: 20px; }

/* ---------------- 仪表盘布局调整：卡片可拖动排序 / 拖大小 ----------------
   抓手插在每张卡 card-head 最左（统计卡无头，悬浮左上角），右下角是大小手柄；
   两者默认隐藏，鼠标悬停到卡片上才露出，不打扰日常浏览。 */
.dash-grid8 > [data-dg] { position: relative; }
.dg-grip {
  flex: 0 0 12px;
  width: 12px;
  height: 14px;
  cursor: grab;
  opacity: 0;
  background: linear-gradient(#B8C0CC 0 2px, transparent 2px 5px, #B8C0CC 5px 7px, transparent 7px 10px, #B8C0CC 10px 12px);
  transition: opacity .15s;
}
.dg-grip-abs { position: absolute; top: 12px; left: 12px; }
.dash-grid8 > [data-dg]:hover .dg-grip { opacity: .8; }
.dg-handle {
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 14px;
  height: 14px;
  cursor: nwse-resize;
  opacity: 0;
  transition: opacity .15s;
}
.dg-handle::before {
  content: '';
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 8px;
  height: 8px;
  border-right: 2px solid #B8C0CC;
  border-bottom: 2px solid #B8C0CC;
}
.dash-grid8 > [data-dg]:hover .dg-handle { opacity: .9; }

/* 拖动过程态：被拖的卡跟手漂浮（JS 写 transform），半透明 + 置顶；
   pointer-events:none 是关键——让 elementFromPoint 穿透源卡命中底下目标卡 */
.dash-grid8 > [data-dg].dg-dragging {
  opacity: .55;
  pointer-events: none;
  z-index: 5;
  animation: none;
  transition: none;
}
.dash-grid8 > [data-dg].dg-drop-target { box-shadow: 0 0 0 2px var(--primary); }
.dash-grid8 > [data-dg].dg-resizing { cursor: nwse-resize; user-select: none; }
/* 拖大小时在右下角报出「列 × 行」：不然一档一档改，看不出到底调了多少 */
.dash-grid8 > [data-dg].dg-resizing::after {
  content: attr(data-size);
  position: absolute; right: 8px; bottom: 8px; z-index: 3;
  padding: 2px 9px; border-radius: 999px;
  background: var(--primary); color: #fff;
  font-size: 11.5px; font-weight: 600; letter-spacing: .2px;
  pointer-events: none;
}

.dash-reset { margin-left: 2px; font-size: 12px; color: var(--t3); }
.dash-reset:hover { color: var(--t1); }

/* 进场：卡片依次浮起，避免一次性闪现；用户关了动效就整段跳过 */
@keyframes dashIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
.dash-grid8 > * {
  animation: dashIn .34s cubic-bezier(.22, .61, .36, 1) both;
}
.dash-grid8 > *:nth-child(1) { animation-delay: .02s; }
.dash-grid8 > *:nth-child(2) { animation-delay: .06s; }
.dash-grid8 > *:nth-child(3) { animation-delay: .10s; }
.dash-grid8 > *:nth-child(4) { animation-delay: .14s; }
.dash-grid8 > *:nth-child(5) { animation-delay: .18s; }
.dash-grid8 > *:nth-child(6) { animation-delay: .22s; }
.dash-grid8 > *:nth-child(7) { animation-delay: .26s; }
@media (prefers-reduced-motion: reduce) {
  .dash-grid8 > * { animation: none; }
}
/* 首屏动画只播一次：局部刷新会重建统计卡，不禁用的话每分钟闪一下 */
.dash-grid8.no-anim > * { animation: none; }

/* ---------------- 仪表盘统计卡 ----------------
   图标底纹 + 大数值，比左侧一条色边更有分量；数字用等宽字形，
   数值变化时不会左右跳动。 */
.dash-stat {
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  transition: transform .18s, box-shadow .18s, border-color .18s;
}
.dash-stat:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); border-color: var(--border-strong); }
/* 可点的统计卡：卡片本身就是入口，鼠标变手型并在右上角给一个箭头 */
.dash-stat.click { cursor: pointer; }
.dash-stat.click:hover { border-color: var(--primary); }
.dash-stat-go {
  margin-left: auto;
  display: inline-flex;
  color: var(--t3);
  opacity: 0;
  transition: opacity .18s;
}
.dash-stat-go .ico { width: 15px; height: 15px; }
.dash-stat.click:hover .dash-stat-go { opacity: 1; color: var(--primary-dark); }
.dash-stat-top { display: flex; align-items: center; gap: 10px; }
.dash-stat-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 10px;
  background: #F3F4F6;
  color: var(--t2);
}
.dash-stat-label { font-size: 13px; color: var(--t2); }
.dash-stat-value {
  margin-top: 12px;
  font-size: 30px;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.dash-stat-value small { margin-left: 3px; font-size: 14px; font-weight: 500; color: var(--t3); }
.dash-stat-foot { margin-top: 6px; font-size: 12px; color: var(--t3); }
.dash-stat.c-green .dash-stat-ico { background: var(--primary-soft); color: var(--primary-dark); }
.dash-stat.c-blue .dash-stat-ico { background: #EFF6FF; color: var(--blue); }
.dash-stat.c-amber .dash-stat-ico { background: #FFFBEB; color: #B45309; }
.dash-stat.c-purple .dash-stat-ico { background: #F5F3FF; color: var(--purple); }

/* ---------------- 仪表盘卡片 ---------------- */
.dash-card { border-radius: var(--r-lg); }
/* 卡片标题前的小图标底纹，颜色区分内容类别 */
.dash-head-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  border-radius: 8px;
  background: var(--primary-soft);
  color: var(--primary-dark);
}
.dash-head-ico .ico { width: 15px; height: 15px; flex: 0 0 15px; }
.dash-head-ico.blue { background: #EFF6FF; color: var(--blue); }
.dash-head-ico.amber { background: #FFFBEB; color: #B45309; }
.dash-head-ico.purple { background: #F5F3FF; color: var(--purple); }

/* 卡片内的小节标题 */
.dash-sec { margin: 14px 0 8px; font-size: 12px; color: var(--t3); }
.dash-card .card-body > .dash-sec:first-child { margin-top: 0; }

/* 「我的课」默认只列前三节，点「全部」展开当天全部；多余节次默认不占位 */
.my-lessons-sec { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dash-chips .my-more { display: none; }
.dash-chips.show-all .my-more { display: inline-flex; }
/* 「全校课表 · 今天」默认只展开前三个节次，点「全部」摊开全天 */
.school-day .school-slot.my-more { display: none; }
.school-day.show-all .school-slot.my-more { display: block; }

/* 进度条：在线班级占比 */
.dash-meter { height: 6px; border-radius: 999px; background: #F3F4F6; overflow: hidden; }
.dash-meter > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--primary);
  transition: width .4s ease;
}

/* 标签流：班级、今日课程 */
.dash-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.dash-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 13px;
  color: var(--t2);
}
.dash-chip.on { border-color: #CBEEDC; background: var(--primary-soft); color: var(--primary-dark); }
.dash-chip.blue { border-color: #DBEAFE; background: #EFF6FF; color: #1D4ED8; }
.dash-dot { width: 6px; height: 6px; flex: 0 0 6px; border-radius: 50%; background: var(--border-strong); }
.dash-chip.on .dash-dot { background: var(--primary); }
.dash-chip-sub { font-size: 12px; color: var(--t3); }
.dash-chip.on .dash-chip-sub { color: var(--primary-dark); opacity: .8; }
.dash-chip.blue .dash-chip-sub { color: #3B82F6; opacity: .85; }
/* 「未分组」这一格不是班级，用虚线边框区分，并且可点去分组管理 */
.dash-chip.plain { border-style: dashed; cursor: pointer; color: var(--t2); }
.dash-chip.plain:hover { border-color: var(--primary); color: var(--primary-dark); }

/* 「我的课」行式列表：时间在左、科目/班级在右。不用胶囊 —— 一节课的信息量
   放进椭圆里读起来太散，行式对齐才扫得快。 */
.my-lessons { display: flex; flex-direction: column; }
/* 「接下来 3 节」与「全天」两份列表同时渲染：切换只换显示，不重排 DOM，
   这样「全部」能真的把已上过的课（以及它们的换课标识）摊出来 */
.my-lessons .ml-set { display: flex; flex-direction: column; }
.my-lessons .ml-all { display: none; }
.my-lessons.show-all .ml-now { display: none; }
.my-lessons.show-all .ml-all { display: flex; }
.my-lesson {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border);
}
.my-lesson:last-child { border-bottom: 0; }
.ml-time {
  flex: 0 0 auto;
  min-width: 4.2em;
  font-size: 12px;
  color: var(--t3);
  font-variant-numeric: tabular-nums;
}
.ml-main { flex: 1 1 auto; min-width: 0; }
.ml-subj {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--t1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ml-sub {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--t3);
}
.ml-sub-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ml-sub .adj-badge { height: 14px; font-size: 9.5px; }

/* 关注列表的单行：名称过长时省略，不让整行被撑宽 */
.dash-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 6px;
  margin: 0 -6px;
  border-radius: var(--r-sm);
  border-bottom: 1px solid var(--border);
}
.dash-row:hover { background: #FAFBFC; }
.dash-row:last-child { border-bottom: 0; }
.dash-row-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-row-sub { flex: 0 0 auto; font-size: 12px; color: var(--t3); }

/* 可点的行：整行能走到对应的详情页，右侧给一个动作词 + 箭头，
   光有数字没有出路就等于没写。 */
.dash-row.click { cursor: pointer; }
.dash-row.click:hover { background: var(--primary-soft); }
.dash-row-act {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 12px;
  color: var(--primary-dark);
}
.dash-row-act .ico { width: 13px; height: 13px; flex: 0 0 13px; }
/* 通知行：内容一行省略，元信息（范围/时间/回执）另起一行，窄卡里才不挤 */
.notice-row { padding: 8px 0; }
/* 分隔线只加在行与行之间：末尾那行后面还跟着「查看全部」，用 :last-child 会算错 */
.notice-row + .notice-row { border-top: 1px solid var(--border); }
.notice-text {
  font-size: 13px;
  color: var(--t1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 最近通知的正文行：最多两行，超出省略，悬停出完整 title。
   用独立 .notice-txtrow 类，不动全局 .notice-row（通知管理页还在用） */
.notice-txtrow { display: flex; flex-direction: column; gap: 2px; }
.notice-txt {
  font-size: 13px;
  line-height: 1.3;
  color: var(--t1);
  font-weight: 600;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-all;
}
.notice-meta { display: flex; align-items: center; gap: 8px; margin-top: 5px; font-size: 12px; color: var(--t3); }

/* 今日歌曲（广播台歌单）：卡片高度不跟着歌数无限长，多了就在列表内滚动 */
.dash-song-list { max-height: 300px; overflow-y: auto; margin-right: -4px; padding-right: 4px; scrollbar-width: none; }
.dash-song-list::-webkit-scrollbar { display: none; }
.song-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; }
.song-row + .song-row { border-top: 1px solid var(--border); }
.song-no {
  flex: 0 0 20px;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  color: var(--t3);
  font-variant-numeric: tabular-nums;
}
.song-main { flex: 1 1 auto; min-width: 0; }
.song-cover {
  position: relative;
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border-radius: 9px;
  overflow: hidden;
  background: #F3F4F6;
  color: var(--t3);
  display: flex;
  align-items: center;
  justify-content: center;
}
.song-cover > .ico { width: 16px; height: 16px; }
/* 封面来自第三方：没有就只显示垫底的占位图标；加载失败用 alt=""，同样露出占位图标而不破版 */
.song-cover img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.song-title {
  font-size: 13.5px;
  color: var(--t1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.song-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.song-time { flex: 0 0 auto; font-size: 12px; color: var(--t3); font-variant-numeric: tabular-nums; }

.dash-more {
  margin-top: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  font-size: 12.5px;
  color: var(--primary-dark);
  cursor: pointer;
}
.dash-more .ico { width: 13px; height: 13px; flex: 0 0 13px; }
.dash-more:hover { text-decoration: underline; }

/* 待办为空时的「肯定」式空态：三检全齐是好事，不该用中性灰糊过去 */
.dash-done {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: var(--primary-soft);
  color: var(--primary-dark);
  font-size: 13.5px;
}
.dash-done .ico { width: 17px; height: 17px; flex: 0 0 17px; }
.dash-done.gray { background: #F5F7FA; color: var(--t2); }
.dash-done.gray .ico { display: none; }

/* ---------------- 今日待办（个人清单） ----------------
   给账号自己用的备忘录：一条一行、勾完成、可删。*/
.todo-add { display: flex; flex-wrap: nowrap; gap: 6px; margin-bottom: 8px; }
.todo-input { flex: 1 1 0; width: auto; min-width: 0; height: 30px; font-size: 12.5px; }
.todo-add .btn { flex: 0 0 auto; white-space: nowrap; }
.todo-list { display: flex; flex-direction: column; }
.todo-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 2px;
  border-bottom: 1px solid var(--border);
}
.todo-item:last-child { border-bottom: 0; }
.todo-check {
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: #fff;
  color: transparent;
  cursor: pointer;
}
.todo-check .ico { width: 11px; height: 11px; flex: 0 0 11px; }
.todo-item.done .todo-check { border-color: var(--primary); background: var(--primary); color: #fff; }
.todo-text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  color: var(--t1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.todo-item.done .todo-text { color: var(--t3); text-decoration: line-through; }
.todo-del {
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--t3);
  cursor: pointer;
  opacity: .55;
}
.todo-del .ico { width: 22px; height: 22px; flex: 0 0 22px; }
.todo-del:hover { background: #FEF2F2; color: #DC2626; opacity: 1; }
/* 矮卡里收紧行距，别为了舒服的行高把列表挤进滚动 */
.dash-root .dash-grid8 > [data-h="2"] .todo-item { padding-top: 3px; padding-bottom: 3px; }

/* 三检时段提示条（进行中 / 下一段什么时候开始） */
.dash-window {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  margin-bottom: 10px;
  border-radius: var(--r-md);
  background: #F7F9FB;
  font-size: 12.5px;
  color: var(--t2);
}
.dash-window .ico { width: 15px; height: 15px; flex: 0 0 15px; color: var(--t3); }
.dash-window em { margin-left: auto; font-style: normal; color: var(--t3); }
.dash-window.open { background: #FFF7E6; color: #92400E; }
.dash-window.open .ico { color: #B45309; }
.dash-window.open em { color: #B45309; }

/* ---------------- 状态条（第 1 层） ----------------
   问候 + 日期 + 实时时钟 + 天气一句话 + 调休/周末/节日。
   用主色柔和底而不是纯灰，这一块是「人写给人的话」，视觉上要和数据卡分开。 */
.dash-statusbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  padding: 14px 18px;
  border: 1px solid #CBEEDC;
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, #E8F8EF 0%, #F6FBF8 100%);
}
.ds-main { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.ds-greet { font-size: 18px; font-weight: 700; color: #14532D; }
.ds-date { font-size: 13px; color: var(--t2); }
.ds-side { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-left: auto; }
.ds-clock {
  font-size: 14px;
  font-weight: 600;
  color: var(--primary-dark);
  font-variant-numeric: tabular-nums;
}
.ds-weather {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  color: var(--t2);
}
.ds-weather .ico { width: 15px; height: 15px; flex: 0 0 15px; color: #B45309; }
.ds-weather em { font-style: normal; color: #B45309; }
/* 天气是异步填进来的，没填之前别占位也别占 flex 间距 */
.ds-weather:empty { display: none; }
.ds-badges { display: inline-flex; flex-wrap: wrap; gap: 8px; }
.ds-badges:empty { display: none; }

/* ---------------- 本周值周 ----------------
   一行一位值周老师：左边角色色签、右边姓名。整行做成浅底卡片块，
   比一大串文字更好扫；也不用胶囊 —— 胶囊在 2×2 里既占宽又读不出层级。 */
.duty-grid { display: flex; flex-direction: column; gap: 4px; }
.duty-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: rgba(248, 250, 252, .9);
  font-size: 13px;
  color: var(--t1);
}
.duty-role {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 19px;
  padding: 0 5px;
  border-radius: 5px;
  background: #EEF2FF;
  color: #4F46E5;
  font-size: 10.5px;
  font-weight: 600;
  white-space: nowrap;
}
.duty-item:nth-child(2) .duty-role { background: #E0F2FE; color: #0369A1; }
.duty-item:nth-child(3) .duty-role { background: #FEF3C7; color: #B45309; }
.duty-who {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  font-weight: 500;
}
/* 本周考核平均分：紧跟在姓名后面（右对齐），数字等宽；未评分时弱化为「待评」 */
.duty-score {
  flex: 0 0 auto;
  margin-left: auto;
  font-size: 12px;
  font-weight: 700;
  color: #B45309;
  font-variant-numeric: tabular-nums;
  letter-spacing: .2px;
}
.duty-score.empty { font-size: 11.5px; font-weight: 500; color: var(--t3); }
.duty-item.muted {
  padding: 4px 8px;
  border-style: dashed;
  background: transparent;
  color: var(--t2);
  font-size: 12.5px;
}
.duty-grid + .dash-done { margin-top: 12px; }
.duty-grid + .dash-sec { margin-top: 14px; }

/* ---------------- 下一节课倒计时 ----------------
   绿色=还没上课，琥珀=正在上课，灰色=今天已上完。 */
.dash-next {
  padding: 14px 16px;
  margin-bottom: 14px;
  border: 1px solid #CBEEDC;
  border-radius: var(--r-md);
  background: linear-gradient(135deg, #E8F8EF 0%, #F4FBF7 100%);
}
.dash-next-label { font-size: 12px; font-weight: 600; color: var(--primary-dark); }
.dash-next-value {
  margin-top: 4px;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.15;
  color: #14532D;
  font-variant-numeric: tabular-nums;
}
.dash-next-meta {
  margin-top: 6px;
  font-size: 12px;
  color: var(--t2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dash-next.now { border-color: #FDE7B0; background: linear-gradient(135deg, #FFF7E6 0%, #FFFDF7 100%); }
.dash-next.now .dash-next-label { color: #B45309; }
.dash-next.now .dash-next-value { color: #92400E; }
/* 倒计时独占一张卡时不要再留 14px 底距（否则卡片上下不等高） */
.dash-next:last-child { margin-bottom: 0; }

/* 课表限高内滚（.tt-scroll 仍负责横向滚动） */
.dash-tt { max-height: 460px; overflow-y: auto; }

/* 断点按「视口」算：
   ① 窄桌面（≤1280px）12 列太挤，退回两列上下流式排布（行高也交回内容决定）；
   ② 移动/窄窗（≤1000px）单列，统计卡内部两列。 */
@media (max-width: 1280px) {
  .dash-grid8 { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: auto; }
  .dash-grid8 > * { grid-column: 1 / -1; grid-row: auto; }
  /* 窄屏时用户拖出来的 inline 布局会让卡片挤出可视区，强制回到自动流式排布 */
  .dash-grid8 > [data-dg] { grid-column: 1 / -1 !important; grid-row: auto !important; }
  /* 行高交回内容后，卡片不该再内滚（会把内容裁掉） */
  .dash-grid8 > .dash-card { overflow: visible; }
  .dash-grid8 > .dash-card > .card-body { overflow: visible; }
}
@media (max-width: 1000px) {
  .dash-grid8 { grid-template-columns: minmax(0, 1fr); }
  .dash-grid8 > * { grid-column: 1 / -1; }
  .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---------------- 仪表盘·日历卡片 ---------------- */
.cal-week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
  margin-bottom: 6px;
}
.cal-week-cell {
  text-align: center;
  font-size: 12px;
  color: var(--t3);
  padding: 2px 0;
}
.cal-week-cell.cal-weekend { color: var(--amber); }
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}
.cal-cell {
  position: relative;
  min-height: 46px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
  background: var(--surface);
}
.cal-empty { border-color: transparent; background: transparent; }
.cal-weekend .cal-num { color: var(--t3); }
.cal-today {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary) inset;
  background: color-mix(in srgb, var(--primary) 6%, var(--surface));
}
.cal-today .cal-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  margin-top: -3px;
}
.cal-num { font-size: 14px; font-weight: 600; line-height: 1.2; }
.cal-tag {
  font-size: 11px;
  color: var(--t3);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cal-tag.cal-holiday { color: var(--amber); font-weight: 600; }
.cal-tag.cal-term { color: var(--blue); }
.cal-map {
  position: absolute;
  top: 6px; right: 8px;
  font-size: 10px;
  color: var(--amber);
  background: color-mix(in srgb, var(--amber) 12%, var(--surface));
  border-radius: 4px;
  padding: 0 4px;
}
/* 法定节假日休/班角标 */
.cal-badge {
  position: absolute;
  top: 6px; right: 8px;
  font-size: 10px;
  line-height: 1.4;
  border-radius: 4px;
  padding: 0 5px;
  font-weight: 600;
}
.cal-badge-rest { color: var(--green); background: color-mix(in srgb, var(--green) 14%, var(--surface)); border: 1px solid color-mix(in srgb, var(--green) 40%, transparent); }
.cal-badge-work { color: var(--orange); background: color-mix(in srgb, var(--orange) 14%, var(--surface)); border: 1px solid color-mix(in srgb, var(--orange) 40%, transparent); }
.cal-has-holiday { background: color-mix(in srgb, var(--amber) 6%, var(--surface)); }
.cal-is-work { background: color-mix(in srgb, var(--orange) 6%, var(--surface)); }

/* 今天卡（日期 + 天气）的版式在 6.8 节统一维护 */
.cal-legend {
  margin-top: 10px;
  font-size: 12px;
  display: flex;
  gap: 16px;
  align-items: center;
  flex-wrap: wrap;
}
.cal-legend .dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: 5px;
  vertical-align: middle;
}

/* 日期单元格可点：点任意一天弹出当天课表 + 该周值周（弹窗本体用通用 .modal） */
.cal-cell[data-date] { cursor: pointer; }
.cal-cell[data-date]:hover { box-shadow: 0 0 0 1px var(--primary) inset; }

/* ---------------- 按钮 ---------------- */
.btn {
  /* 图标(.ico 是 display:block 的 18×18 svg)与文字必须靠 flex 对齐，
     否则块级 svg 会把文字挤到下一行，出现「图标文字错位」 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 14px;
  font-size: 13px;
  font-family: inherit;
  color: var(--t1);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background .15s, border-color .15s;
}
.btn:hover { background: #F9FAFB; }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-dark); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--t2); }
.btn-ghost:hover { background: #F3F4F6; }
.btn-danger { background: var(--red); border-color: var(--red); color: #fff; }
.btn-danger:hover { background: #DC2626; }
/* 危险但非主操作的按钮：只描红，不填红底，避免一屏全是红色块 */
.btn-danger-ghost { color: var(--red); border-color: #FCA5A5; }
.btn-danger-ghost:hover { background: #FEF2F2; }
/* 列表行内的小按钮：比常规按钮矮一档，一行放得下四五个 */
.btn-sm { padding: 4px 10px; font-size: 12px; border-radius: 6px; }
/* 页内两步删除确认：替代 window.confirm，避免嵌入式 webview 被原生模态框卡住 */
.confirm-tip { margin-right: 2px; font-size: 13px; color: var(--red); }
.btn:disabled { opacity: .55; cursor: not-allowed; }
a.btn { display: inline-flex; align-items: center; text-decoration: none; }

/* ---------------- 徽章 ---------------- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
}
.badge-green { background: var(--primary-soft); color: var(--primary-dark); }
.badge-red { background: #FEE2E2; color: #B91C1C; }
.badge-amber { background: #FEF3C7; color: #B45309; }
.badge-gray { background: #F3F4F6; color: var(--t2); }
.badge-blue { background: #DBEAFE; color: #1D4ED8; }

/* ---------------- 表单 ---------------- */
.field { display: block; margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }
.field-label { display: block; font-size: 13px; color: var(--t2); margin-bottom: 6px; }
.input {
  width: 100%;
  padding: 8px 10px;
  font-family: inherit;
  font-size: 14px;
  color: var(--t1);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  outline: none;
  transition: border-color .15s, box-shadow .15s;
}
.input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
textarea.input { resize: vertical; min-height: 96px; line-height: 1.6; }

.segmented { display: inline-flex; padding: 3px; background: #F3F4F6; border-radius: var(--r-sm); gap: 2px; }
.segmented button {
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  color: var(--t2);
  padding: 6px 14px;
  border-radius: 6px;
  cursor: pointer;
}
.segmented button.on { background: var(--surface); color: var(--t1); font-weight: 600; box-shadow: var(--shadow); }

.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.toolbar .spacer { margin-left: auto; }
/* 有效期行：说明文字 + 一排时长胶囊。单独成行，不跟输入框抢单行宽度。 */
.exp-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.exp-row > .dev-muted { flex: none; }
.exp-pick { flex: 1 1 320px; min-width: 0; }
/* 工具栏里的胶囊按钮不参与收缩，「语音播报」两侧的间距由 gap 负责 */
.add-bar > .pill { flex: 0 0 auto; white-space: nowrap; }
.add-bar > .btn { flex: 0 0 auto; }
.link-btn {
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  color: var(--primary-dark);
  cursor: pointer;
  padding: 0 2px;
}
.link-btn:hover { text-decoration: underline; }

.pick-list { border: 1px solid var(--border); border-radius: var(--r-sm); max-height: 280px; overflow-y: auto; }
.pick-item { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-bottom: 1px solid var(--border); cursor: pointer; }
.pick-item:last-child { border-bottom: 0; }
.pick-item:hover { background: #F9FAFB; }
.pick-item input { width: 15px; height: 15px; accent-color: var(--primary); cursor: pointer; }
.pick-item .name { font-size: 13px; }
.pick-item .meta { margin-left: auto; font-size: 12px; color: var(--t3); }

.result {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  border-radius: var(--r-sm);
  background: var(--primary-soft);
  color: var(--primary-dark);
  font-size: 13px;
  margin-bottom: 14px;
}
.result.err { background: #FEE2E2; color: #B91C1C; }
.result.warn { background: #FEF3C7; color: #B45309; align-items: flex-start; }

/* ---------------- 自定义卡片编写规范弹窗 ---------------- */
.guide-h { font-size: 13px; font-weight: 600; margin: 16px 0 4px; color: var(--t1); }
.guide-sec:first-child .guide-h { margin-top: 0; }
.guide ul { margin: 2px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.9; color: var(--t2); }
.guide-code {
  background: #F6F8FB;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  margin: 8px 0 0;
  font-size: 12px;
  line-height: 1.7;
  color: #334155;
  overflow-x: auto;
  white-space: pre;
}

/* ---------------- 记录列表 ---------------- */
.rec-list { border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.rec-item { padding: 12px 14px; border-bottom: 1px solid var(--border); cursor: pointer; }
.rec-item:last-child { border-bottom: 0; }
.rec-item:hover { background: #F9FAFB; }
.rec-top { display: flex; align-items: center; gap: 8px; }
.rec-top .spacer { margin-left: auto; }
.rec-title { font-size: 14px; font-weight: 600; }
.rec-time { font-size: 12px; color: var(--t3); }
.rec-preview {
  margin-top: 6px;
  font-size: 13px;
  color: var(--t2);
  line-height: 1.6;
  word-break: break-all;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rec-meta { margin-top: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 14px; font-size: 12px; color: var(--t3); }
.rec-detail { padding: 10px 14px 14px; background: #FAFAFA; border-bottom: 1px solid var(--border); }

/* ---------------- 弹窗 ----------------
   账号的权限 / 编辑 / 登录链接这类操作放进弹窗，列表保持一屏可扫。 */
.modal-mask {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(17, 24, 39, .38);
  animation: maskIn .14s ease-out;
}
@keyframes maskIn { from { opacity: 0; } to { opacity: 1; } }
.modal {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  max-height: calc(100vh - 48px);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  animation: modalIn .16s ease-out;
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(8px) scale(.99); }
  to { opacity: 1; transform: none; }
}
.modal-sm { max-width: 420px; }
.modal-lg { max-width: 760px; }
.modal-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}
.modal-title { font-size: 15px; font-weight: 600; }
.modal-x {
  margin-left: auto;
  width: 28px;
  height: 28px;
  font-size: 13px;
  font-family: inherit;
  color: var(--t3);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
.modal-x:hover { background: #F3F4F6; color: var(--t1); }
.modal-body { padding: 18px; overflow-y: auto; }
.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 18px;
  border-top: 1px solid var(--border);
}
@media (max-width: 640px) {
  .modal-mask { padding: 12px; align-items: flex-end; }
  .modal { max-height: calc(100vh - 24px); }
}

/* ---------------- 账号行 ----------------
   操作按钮跟在账号信息右侧同一行，不再另起一行堆在下面。 */
.us-row { display: flex; align-items: center; gap: 12px; cursor: default; }
.us-info { min-width: 0; }
.us-actions {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: flex-end;
}

.rec-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.rec-table th, .rec-table td { padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border); }
.rec-table th { color: var(--t3); font-weight: 500; }
.rec-table tr:last-child td { border-bottom: 0; }

/* ---------------- 校园铃声表格 ----------------
   铃声库、时间表、打铃模式这几张表列多且音频名很长，auto 布局会把「音频」
   一列撑得特别宽、其它列挤成一条。统一改固定布局：列宽以表头 width 为准，
   超长内容换行；窄屏时外层容器横向滚动，不压扁操作按钮。 */
.bell-table { table-layout: fixed; min-width: 820px; font-size: 13px; }
.bell-table-md { min-width: 900px; }
.bell-table-wide { min-width: 1040px; }
.bell-table th, .bell-table td { padding: 8px 10px; vertical-align: middle; }
.bell-table th { white-space: nowrap; }
.bell-table td { word-break: break-word; overflow-wrap: anywhere; }
.bell-table .cell-nowrap { white-space: nowrap; }
.bell-table .cell-pre { white-space: pre-line; }
/* 打铃模式：整行可点选中（蓝色底），当前生效用绿色左边条 + 徽标标识 */
.bell-table tr.mode-row { cursor: pointer; outline: none; }
.bell-table tr.mode-row > td { transition: background .15s; }
.bell-table tr.mode-row:hover > td { background: #F8FAFC; }
.bell-table tr.mode-row.is-selected > td { background: #EFF6FF; }
.bell-table tr.mode-row.is-selected:hover > td { background: #DBEAFE; }
.bell-table tr.mode-row.is-active > td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
.bell-table tr.mode-row:focus-visible > td { background: #EFF6FF; }
.bell-table .ops { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.bell-table .ops .btn { margin: 0; }
.confirm-body { font-size: 14px; line-height: 1.75; color: var(--t2); }
.confirm-body strong { color: var(--t1); }

.pager { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 12px; font-size: 12px; color: var(--t3); }

/* ---------------- 课表网格 ----------------
   课表是 13 节次 × 5 天的密集表格，窄屏必然横向溢出，所以包一层横向滚动容器；
   表头 sticky 让长表滚动时仍能看清是星期几。 */
.tt-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-sm); }
.tt-table { width: 100%; border-collapse: collapse; font-size: 12px; min-width: 660px; }
.tt-table th, .tt-table td {
  border: 1px solid var(--border);
  padding: 5px 7px;
  text-align: center;
  vertical-align: middle;
}
.tt-table thead th {
  background: #F9FAFB;
  color: var(--t2);
  font-weight: 500;
  white-space: nowrap;
  position: sticky;
  top: 0;
}
.tt-table .tt-slot { background: #F9FAFB; color: var(--t2); white-space: nowrap; }
.tt-table .tt-slot .tt-time { display: block; color: var(--t3); font-size: 11px; font-weight: 400; }
.tt-table td.tt-empty { background: #FCFCFD; }
.tt-subj { font-weight: 600; color: var(--t1); }
.tt-meta { color: var(--t3); font-size: 11px; margin-top: 2px; }
/* 临时换课 / 代课的小标识：换课=琥珀、代课=蓝；放在科目/单双周前面，窄格也不会被省略号吃掉 */
.adj-badge {
  display: inline-flex;
  align-items: center;
  height: 15px;
  margin-right: 4px;
  padding: 0 4px;
  border-radius: 4px;
  background: #FEF3C7;
  color: #B45309;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  vertical-align: 1px;
}
.adj-badge.sub { background: #DBEAFE; color: #1D4ED8; }
.tt-odd, .tt-even {
  display: inline-block;
  padding: 0 5px;
  border-radius: 4px;
  font-size: 11px;
  background: var(--primary-soft);
  color: var(--primary-dark);
}
.tt-odd { background: #FEF3C7; color: #B45309; }
.tt-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--t3); }

/* ---------------- 敏感词库（词条以标签流展示，一屏能扫更多） ---------------- */
.word-chips { display: flex; flex-wrap: wrap; gap: 6px; max-height: 400px; overflow-y: auto; }
.word-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 4px 2px 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 13px;
  color: var(--t1);
}
/* 停用的词整条淡化并加删除线，一眼能和启用中的区分开 */
.word-chip.off { opacity: .45; }
.word-chip.off .word-chip-word { text-decoration: line-through; }
/* 删除二次确认时整条转红，代替 window.confirm */
.word-chip.confirming { border-color: #FCA5A5; background: #FEF2F2; opacity: 1; }
.word-chip-word {
  border: 0;
  background: transparent;
  font: inherit;
  color: inherit;
  padding: 0;
  cursor: pointer;
}
.word-chip-x {
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  line-height: 1;
  color: var(--t3);
  padding: 0 4px;
  cursor: pointer;
}
.word-chip-x:hover { color: var(--red); }
.word-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 5px; }
.word-dot.general { background: var(--t3); }
.word-dot.notice { background: var(--blue); }
.word-dot.chat { background: var(--amber); }

/* ---------------- 设备表（设备列表 / 分组管理共用） ---------------- */
.dev-table { width: 100%; border-collapse: collapse; }
.dev-table th {
  padding: 8px 10px;
  text-align: left;
  font-size: 12px;
  font-weight: 500;
  color: var(--t3);
  border-bottom: 1px solid var(--border);
}
.dev-table td { padding: 10px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.dev-row:hover { background: #F9FAFB; }
.dev-row.on { background: var(--primary-soft); }
.dev-name { font-size: 14px; font-weight: 600; }
.dev-id { margin-top: 2px; font-size: 12px; color: var(--t3); word-break: break-all; }
.dev-flags { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
.dev-muted { font-size: 12px; color: var(--t3); }
.dev-foot { margin-top: 10px; font-size: 12px; color: var(--t3); }

.dv-bulk {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 10px; padding: 8px 10px;
  background: #F3F7FF; border: 1px solid var(--border); border-radius: 10px;
}
.dv-bulk b { font-size: 15px; color: var(--primary); }
.dv-bulk .btn[disabled] { opacity: .5; cursor: not-allowed; }
.dev-pick-cell { width: 34px; text-align: center; }

.dev-panel-cell { padding: 0 !important; background: #FAFAFA; }
.dev-panel { padding: 14px 16px; border-bottom: 1px solid var(--border); }
.dev-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0 16px; }
.dev-checks { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 13px; color: var(--t2); }

/* ---------------- 胶囊选择按钮（代替勾选框） ----------------
   勾选框的语义全部保留（多选、可禁用、change 事件、.checked 读取），
   只是把外观做成胶囊：input 透明化后仍占位，视觉部分交给后面的 span，
   于是 input:checked + span 这种兄弟选择器就能驱动高亮，不依赖 :has()。 */
.pill { display: inline-flex; position: relative; cursor: pointer; }
.pill > input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
.pill > span {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 13px;
  color: var(--t2);
  transition: background .12s, border-color .12s, color .12s;
}
.pill:hover > span { border-color: var(--primary); color: var(--primary-dark); }
.pill > input:checked + span { background: var(--primary); border-color: var(--primary); color: #fff; }
.pill > input:disabled + span { opacity: .45; cursor: not-allowed; }
.pill > input:disabled:checked + span { opacity: .6; }
.pill > input:focus-visible + span { box-shadow: 0 0 0 3px var(--primary-soft); }

/* ---------------- 胶囊选项按钮（点一下即选中） ----------------
   一屏几十个可选项时，勾选框太碎也不好点：每个选项直接做成按钮，
   选中就是底色变绿，视觉上和 .pill 的选中态一致。
   与 .pill 的分工：.pill 用在真正需要 <input> 参与表单提交的地方，
   这里用在「点一下就发请求」的权限/范围选择上，所以直接用 <button>。 */
.chip-wrap { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  font-family: inherit;
  font-size: 13px;
  color: var(--t1);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.chip-toggle:hover { border-color: var(--primary); color: var(--primary-dark); }
.chip-toggle.on { background: var(--primary); border-color: var(--primary); color: #fff; }
.chip-toggle.on:hover { color: #fff; }
.chip-toggle-sub { font-size: 12px; color: var(--t3); }
.chip-toggle.on .chip-toggle-sub { color: rgba(255, 255, 255, .85); }
.chip-toggle:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-soft); }
.chip-toggle:disabled { opacity: .5; cursor: not-allowed; }
.chip-toggle:disabled:hover { border-color: var(--border-strong); color: var(--t1); }
.dev-token { margin-top: 12px; display: flex; align-items: center; gap: 8px; }
.dev-panel-actions { margin-top: 14px; display: flex; align-items: center; gap: 10px; }
.dev-panel-actions .spacer { margin-left: auto; }

/* ---------------- 设置行（三检设置等） ---------------- */
.cfg-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border);
}
.cfg-row:last-child { border-bottom: 0; }
.cfg-row .spacer { margin-left: auto; }
.cfg-label { font-size: 14px; font-weight: 600; }

/* ---------------- 聊天会话 ---------------- */
.chat-wrap { display: grid; grid-template-columns: 320px 1fr; gap: 16px; align-items: start; }
.chat-list { max-height: 560px; overflow-y: auto; }
.chat-item {
  padding: 10px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.chat-item:hover { background: #F9FAFB; }
.chat-item.on { background: var(--primary-soft); }
.chat-title { display: flex; justify-content: space-between; gap: 8px; font-size: 14px; font-weight: 600; }
.chat-sub {
  margin: 4px 0 6px;
  font-size: 12px;
  color: var(--t3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-thread { max-height: 420px; overflow-y: auto; padding: 4px 0; }
/* 分组 / 全部设备会话：顶部设备成员列表（限高滚动，点击任意一台可进入其单设备会话） */
.chat-members { margin-bottom: 12px; padding: 10px 12px; background: #F9FAFB; border: 1px solid var(--border); border-radius: var(--r-sm); }
.chat-members-list { max-height: 170px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.chat-members-list .chat-member { padding: 6px 8px; border-radius: var(--r-sm); cursor: pointer; align-items: center; gap: 10px; }
.chat-members-list .chat-member:hover { background: #EEF0F3; }
.chat-msg { margin-bottom: 12px; }
.chat-msg-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.chat-bubble {
  padding: 9px 12px;
  border-radius: var(--r-sm);
  background: #F3F4F6;
  color: var(--t1);
  font-size: 13px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
}
/* 会议消息不走灰底胶囊：直接铺开成一张「屏幕缩略图」，去掉这层内边距与底色 */
.chat-bubble-plain { padding: 0; background: transparent; white-space: normal; }
/* 会话里的图片消息：限高限宽，避免一张大图把消息流撑开 */
.chat-img { display: block; max-width: 240px; max-height: 240px; border-radius: var(--r-sm); }
/* 视频按 file 落库，前端按扩展名认出来内嵌播放，别只给一个下载链接 */
.chat-video { display: block; max-width: 240px; border-radius: var(--r-sm); background: #000; }

/* 输入「##」弹出的系统命令面板（贴在输入框上方） */
.chat-input-bar { position: relative; }
.chat-cmd {
  display: none;
  position: absolute;
  left: 0;
  bottom: calc(100% + 6px);
  z-index: 20;
  width: 100%;
  max-width: 420px;
  padding: 8px 8px 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}
.chat-cmd.show { display: block; }
.chat-cmd-title {
  padding: 2px 6px 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--t3);
  letter-spacing: 1px;
}
.chat-cmd-list { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.chat-cmd-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 8px 10px;
  font-size: 13px;
  color: var(--t1);
  text-align: center;
  background: #F3F4F6;
  border-radius: 6px;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.chat-cmd-item .ico { width: 22px; height: 22px; }
.chat-cmd-item:hover { background: var(--primary); color: #fff; }

/* ---------------- 会议 / 班会 ---------------- */
.chat-modal-mask {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(15, 23, 42, .45);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.chat-modal {
  width: 100%; max-width: 480px; max-height: 88vh; overflow: auto;
  background: var(--surface); border-radius: 14px;
  box-shadow: 0 20px 60px rgba(0,0,0,.25);
  padding: 20px;
}
.chat-modal h3 { margin: 0 0 14px; font-size: 16px; color: var(--t1); }
.chat-modal .field { margin-bottom: 12px; }
.chat-modal .field-label { display: block; margin-bottom: 6px; font-size: 13px; color: var(--t2); }
.mtg-themes { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; max-height: 236px; overflow-y: auto; padding-right: 2px; }
.mtg-theme {
  border: 2px solid transparent; border-radius: 10px; padding: 8px 4px 6px;
  text-align: center; cursor: pointer; background: #F3F4F6; transition: border-color .15s;
}
.mtg-theme.on { border-color: var(--primary); }
.mtg-swatch { width: 100%; height: 34px; border-radius: 6px; margin-bottom: 4px; }
.mtg-theme-name { font-size: 12px; color: var(--t2); }
.mtg-actions { display: flex; gap: 10px; margin-top: 4px; }
/* 图案 / 标题样式 / 时间落款控件 */
.mtg-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.mtg-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 10px; font-size: 12px; color: var(--t2);
  background: #F3F4F6; border: 1.5px solid transparent; border-radius: 999px; cursor: pointer;
  transition: border-color .15s;
}
.mtg-chip.on { border-color: var(--primary); color: var(--t1); background: #E8F5EE; }
.mtg-pv {
  width: 22px; height: 14px; border-radius: 3px; flex: none;
  background: linear-gradient(135deg, #0f3d7e, #1b6fd8);
  position: relative; overflow: hidden;
  display: inline-block; vertical-align: middle;
}
.mtg-pv::after { content: ''; position: absolute; inset: 0; }
.mtg-pv.pat-dots::after { background-image: radial-gradient(rgba(255,255,255,.55) 1.5px, transparent 1.5px); background-size: 8px 8px; }
.mtg-pv.pat-stripes::after { background: repeating-linear-gradient(45deg, rgba(255,255,255,.5) 0 3px, transparent 3px 7px); }
.mtg-pv.pat-grid::after { background-image: linear-gradient(rgba(255,255,255,.45) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.45) 1px, transparent 1px); background-size: 10px 10px; }
.mtg-pv.pat-rings::after { background: radial-gradient(circle, transparent 0 4px, rgba(255,255,255,.55) 4px 5px, transparent 5px 9px, rgba(255,255,255,.55) 9px 10px, transparent 10px); }
.mtg-pv.pat-stars::after { background-image: radial-gradient(circle, #fff 0 1.5px, transparent 2px); background-size: 13px 13px; }
.mtg-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.mtg-select { width: 100%; }
.mtg-time-field { display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--t2); font-size: 13px; }
.mtg-time-field input { accent-color: var(--primary); }
/* ---------------- 会议/班会 v2：主视觉背景图 + 预览框 + 文字样式/位置 ---------------- */
.mtg-theme .mtg-bg-thumb {
  width: 100%; height: 54px; border-radius: 6px; margin-bottom: 4px;
  background-size: cover; background-position: center;
}
.mtg-title-inputs { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* 标题/二级标题支持手动换行 */
.mtg-ta { height: 62px; resize: vertical; line-height: 1.5; padding-top: 8px; padding-bottom: 8px; min-height: 62px; }
/* 16:9 预览框（= 设备端 1920x1080）。宽度被上限约束，所以不能用百分比 padding 撑高
   （百分比 padding 按**包含块**宽度算，包含块比自身宽时会算歪）：高度由 fitScreenPreview()
   按自身宽度定（容器标 data-fit-height），字号同理。 */
.mtg-preview {
  width: 460px; max-width: 100%; margin: 2px auto 14px;
  border-radius: 12px; overflow: hidden; position: relative;
  background: #0a1020; box-shadow: 0 6px 18px rgba(0,0,0,.18);
}
.mtg-pv-bg { position: absolute; left: 0; top: 0; right: 0; bottom: 0; background-size: cover; background-position: center; }
.mtg-pv-shade { position: absolute; left: 0; top: 0; right: 0; bottom: 0; background: transparent; }
.mtg-pv-inner {
  position: absolute; left: 0; top: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column;
  /* 内距对齐客户端：左右 9% 宽、上下 10% 高（CSS 百分比 padding 一律按宽度算，故上下 = 10% x 0.5625）。
     行距/间距按 --pv-k（容器宽/1920）换算，对应设备端的 14px。 */
  padding: 5.625% 9%; gap: calc(var(--pv-k, .34) * 14px);
  pointer-events: none; color: var(--pv-c, #fff);
}
.mtg-pv-inner[data-v="t"] { justify-content: flex-start; }
.mtg-pv-inner[data-v="c"] { justify-content: center; }
.mtg-pv-inner[data-v="b"] { justify-content: flex-end; }
.mtg-pv-inner[data-h="l"] { align-items: flex-start; text-align: left; }
.mtg-pv-inner[data-h="c"] { align-items: center; text-align: center; }
.mtg-pv-inner[data-h="r"] { align-items: flex-end; text-align: right; }
/* 字号由 fitScreenPreview() 按容器宽度写入行内（设备端像素 x --pv-k），这里的值只是兜底。
   换行只认手动回车（white-space: pre，不自动折行）；超宽时由 fitScreenPreview()
   按最长一行整块缩字号，保证同一块文字各行字号一致。 */
.mtg-pv-title { font-weight: 700; line-height: 1.22; font-size: var(--pv-sz, 24px); white-space: pre; max-width: 100%; }
.mtg-pv-sub { font-size: 10px; opacity: .94; white-space: pre; max-width: 100%; }
.mtg-pv-footer { font-size: 8px; opacity: .82; white-space: pre; max-width: 100%; }
.mtg-grid9 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; width: 100%; max-width: 190px; }
.mtg-grid9 .mtg-chip { justify-content: center; text-align: center; padding: 6px 4px; font-size: 12px; }
/* 三块文字（一级标题 / 二级标题 / 落款）各一列：字号 + 字体 + 九宫格位置 */
.mtg-style-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; align-items: start; }
.mtg-style-col { display: flex; flex-direction: column; gap: 6px; }
.mtg-style-name { font-size: 12.5px; font-weight: 600; color: var(--t2); }
.mtg-slim-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
.mtg-color-chips { display: flex; gap: 8px; align-items: center; }
.mtg-color-chip {
  width: 26px; height: 26px; border-radius: 50%; border: 2px solid rgba(0,0,0,.12);
  cursor: pointer; display: inline-block; box-sizing: border-box;
}
.mtg-color-chip.on { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary); }
/* 会话里的会议消息卡（背景图版） */
/* 会议消息气泡 = 设备端全屏画面的等比缩略图。
   高度不能用 padding-top 百分比（它的包含块比自身宽，会算歪），改由 fitScreenPreview()
   按自身宽度定 16:9 的高（容器标 data-fit-height）；内距同理用 --pv-k 换算。 */
.mtg-bubble {
  position: relative; overflow: hidden;
  width: 420px; max-width: 100%;
  border-radius: 10px; color: #fff;
  background-size: cover; background-position: center;
}
.mtg-bubble .mtg-shade { display: none; }   /* 遮罩全透明：不再压暗主视觉图 */
.mtg-bubble .mtg-in {
  position: absolute; left: 0; top: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column;
  padding: calc(var(--pv-k, .22) * 108px) calc(var(--pv-k, .22) * 173px);
  gap: calc(var(--pv-k, .22) * 14px);
  pointer-events: none;
}
.mtg-bubble .mtg-in[data-v="t"] { justify-content: flex-start; }
.mtg-bubble .mtg-in[data-v="c"] { justify-content: center; }
.mtg-bubble .mtg-in[data-v="b"] { justify-content: flex-end; }
.mtg-bubble .mtg-in[data-h="l"] { align-items: flex-start; text-align: left; }
.mtg-bubble .mtg-in[data-h="c"] { align-items: center; text-align: center; }
.mtg-bubble .mtg-in[data-h="r"] { align-items: flex-end; text-align: right; }
/* 字号由 fitScreenPreview() 按容器宽度写入行内（设备端像素 x --pv-k）。
   标题/二级/落款都只认手动换行，超宽时整块缩字号（同一块各行共用一个字号）。 */
.mtg-bubble .mtg-title { font-weight: 700; line-height: 1.22; white-space: pre; max-width: 100%; }
.mtg-bubble .mtg-body { line-height: 1.45; white-space: pre; opacity: .96; max-width: 100%; }
.mtg-bubble .mtg-footer { opacity: .85; white-space: pre; max-width: 100%; }
/* 学校标识固定在左上角（与设备端一致），不参与九宫格定位 */
.mtg-bubble .mtg-school {
  position: absolute; top: 3.5%; left: 3.5%; z-index: 2;
  display: flex; align-items: center;
  gap: calc(var(--pv-k, .22) * 14px);
  pointer-events: none;
}
.mtg-bubble .mtg-school img { height: calc(var(--pv-k, .22) * 56px); border-radius: 3px; background: rgba(255,255,255,.9); padding: 1px; }
.mtg-bubble .mtg-school img.lockup { height: calc(var(--pv-k, .22) * 72px); background: transparent; padding: 0; border-radius: 0; }

/* ---------------- 会议/班会模板 ----------------
   工作台「教务 → 会议班会模板」：模板 = 主视觉背景 + 文字样式 + 学校落款。
   聊天「+ → 会议/班会」只选模板、填文字，样式在这里统一管理。 */
.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }
.tpl-card {
  border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden;
  background: var(--surface); display: flex; flex-direction: column;
  transition: box-shadow .15s ease;
}
.tpl-card:hover { box-shadow: var(--shadow-lg); }
/* 卡片缩略图：同样 16:9（这里是整卡的宽，padding-top 正好撑出等比高度） */
.tpl-pv { position: relative; height: 0; padding-top: 56.25%; overflow: hidden; background: #0a1020; }
.tpl-card .tpl-head { padding: 10px 12px; display: flex; align-items: center; gap: 8px; }
.tpl-card .tpl-name { font-size: 14px; font-weight: 600; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 操作区现在有三个按钮（设为默认 / 编辑 / 删除）：卡片最窄 250px，放不下就让学校名那一行换行，按钮不压缩 */
.tpl-card .tpl-body { padding: 8px 12px 12px; border-top: 1px solid var(--border);
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tpl-card .tpl-sch { font-size: 12px; color: var(--t2); display: flex; align-items: center;
  gap: 6px; min-width: 0; flex: 1 1 100px; }
.tpl-card .tpl-sch img { height: 20px; width: auto; border-radius: 3px; }
.tpl-card .tpl-sch span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpl-card .tpl-acts { display: flex; gap: 6px; flex-shrink: 0; margin-left: auto; }
/* 预览框里的学校标识：与客户端同位（左 3.5%、上 3.5%），尺寸/间距按 --pv-k 等比换算 */
.mtg-pv-school {
  position: absolute; top: 3.5%; left: 3.5%; z-index: 2;
  display: flex; align-items: center; justify-content: flex-start;
  gap: calc(var(--pv-k, .34) * 14px);
  pointer-events: none;
}
.mtg-pv-school .mtg-pv-logo {
  height: calc(var(--pv-k, .34) * 56px); width: auto; object-fit: contain; border-radius: 6px;
  background: rgba(255, 255, 255, .92); padding: 2px;
}
/* 含校名的横版标识：透明底、更高（客户端 72px），去掉白底衬板与圆角 */
.mtg-pv-school .mtg-pv-logo.lockup { height: calc(var(--pv-k, .34) * 72px); background: transparent; padding: 0; border-radius: 0; }
.mtg-pv-school .mtg-pv-sname {
  font-size: calc(var(--pv-k, .34) * 30px); font-weight: 600; color: #fff; opacity: .95;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .45); letter-spacing: 1px;
}
/* Logo 上传区 */
.mtg-logo-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.mtg-logo-prev {
  height: 48px; max-width: 140px; object-fit: contain; border-radius: 8px;
  background: #fff; border: 1px solid var(--border); padding: 4px; display: none;
}
/* 含校名的横版标识：透明底，用棋盘格衬底以便看清白色/黑色文字 */
.mtg-logo-prev.lockup {
  max-width: 260px;
  background:
    linear-gradient(45deg, #e9e9ee 25%, transparent 25% 75%, #e9e9ee 75%),
    linear-gradient(45deg, #e9e9ee 25%, #fff 25% 75%, #e9e9ee 75%);
  background-size: 12px 12px; background-position: 0 0, 6px 6px;
}
.mtg-logo-hint { font-size: 12px; color: var(--t3); }
/* 聊天弹窗里的模板选择条 */
.mtg-tpl-select { display: flex; gap: 8px; flex-wrap: wrap; max-height: 128px; overflow-y: auto; padding-right: 2px; }
.mtg-tpl-chip {
  padding: 6px 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); font-size: 13px; color: var(--t2); cursor: pointer;
  max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mtg-tpl-chip.on { border-color: var(--primary); color: var(--primary); background: #E8F5EE; }

@media (max-width: 860px) {
  .chat-wrap { grid-template-columns: 1fr; }
}

/* ---------------- 空状态 / 加载 ---------------- */
.empty { padding: 44px 20px; text-align: center; color: var(--t3); }
.empty .ico { display: block; width: 34px; height: 34px; margin: 0 auto 10px; }
.skeleton {
  height: 14px;
  border-radius: 6px;
  background: linear-gradient(90deg, #F3F4F6 25%, #E5E7EB 37%, #F3F4F6 63%);
  background-size: 400% 100%;
  animation: sk 1.2s infinite;
}
@keyframes sk { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* ---------------- Toast ---------------- */
.toast-wrap {
  position: fixed;
  top: 18px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.toast {
  padding: 10px 16px;
  border-radius: var(--r-sm);
  background: rgba(31, 41, 55, .94);
  color: #fff;
  font-size: 13px;
  box-shadow: var(--shadow-lg);
  animation: tin .18s ease-out;
}
.toast.err { background: rgba(220, 38, 38, .95); }
@keyframes tin { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ---------------- 遮罩（移动端侧边栏） ---------------- */
.backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .35);
  z-index: 35;
}
.backdrop.show { display: block; }

.menu-btn { display: none; }

/* ---------------- 桌面端侧边栏折叠（汉堡） ----------------
   仪表盘首屏先完整展开菜单，加载完自动加 body.sb-collapsed：
   侧边栏从布局中抽离（fixed）从左侧滑出，main 随即占满全宽完成「折叠全屏」；
   折叠后左上角出现汉堡按钮，点开是浮层（盖在内容上），点遮罩或导航项收起。 */
body.sb-collapsed .sidebar {
  position: fixed;
  left: 0;
  top: 0;
  transform: translateX(-100%);
  transition: transform .32s cubic-bezier(.22, .61, .36, 1);
  box-shadow: none;
}
body.sb-collapsed .sidebar.open {
  transform: none;
  box-shadow: var(--shadow-lg);
}
body.sb-collapsed .menu-btn { display: inline-flex; }

/* ---------------- 响应式 ---------------- */
@media (max-width: 860px) {
  .app { display: block; }
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    transform: translateX(-100%);
    transition: transform .2s;
  }
  .sidebar.open { transform: none; box-shadow: var(--shadow-lg); }
  .menu-btn { display: inline-flex; }
  .view { padding: 14px; }
}

/* ---------------- 广播台：带封面的列表行 ---------------- */

.rd-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
}
.rd-row:last-child { border-bottom: 0; }
.rd-row:hover { background: #F9FAFB; }
.rd-row.on { background: var(--primary-soft); }
.rd-cover, .rd-cover-ph { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 8px; }
.rd-cover { object-fit: cover; background: var(--border); }
.rd-cover-ph {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #F3F4F6;
  color: var(--t3);
}
.rd-main { flex: 1; min-width: 0; }
.rd-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }

/* ---------------- 广播台：悬浮播放条 ----------------
   贴在内容区底部悬浮（left 让开侧栏），窗口收窄到 860px 时侧栏变抽屉，
   播放条要跟着铺满整宽。 */

.rd-bar {
  position: fixed;
  left: calc(var(--sidebar-w) + 20px);
  right: 20px;
  bottom: 16px;
  z-index: 35;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
}
.rd-bar-cover, .rd-bar-cover-ph { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 8px; }
.rd-bar-cover { object-fit: cover; background: var(--border); }
.rd-bar-cover-ph {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #F3F4F6;
  color: var(--t3);
}
.rd-bar-info { flex: 0 1 220px; min-width: 0; }
.rd-bar-title {
  font-size: 14px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rd-bar-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--t3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rd-bar-progress { flex: 1; display: flex; align-items: center; gap: 10px; min-width: 120px; }
.rd-bar-track {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: #E5E7EB;
  cursor: pointer;
  overflow: hidden;
}
.rd-bar-fill { height: 100%; width: 0; border-radius: 999px; background: var(--primary); }
.rd-bar-time { font-size: 12px; color: var(--t3); font-variant-numeric: tabular-nums; }
.rd-bar-ctrl { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }
.rd-bar-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  color: var(--t1);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.rd-bar-btn:hover { background: #F3F4F6; }
.rd-bar-btn.main {
  width: 40px;
  height: 40px;
  border: 0;
  background: var(--primary);
  color: #fff;
}
.rd-bar-btn.main:hover { background: var(--primary-dark); }

/* 每日广播 · 生成进度面板 */
.rd-prog {
  border: 1px solid #E5E9F0;
  border-radius: 10px;
  background: #F8FAFC;
  padding: 12px 14px;
  margin-bottom: 12px;
}
.rd-prog-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--t1);
  margin-bottom: 10px;
}
.rd-prog-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.rd-prog-step {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--t3);
}
.rd-prog-mark {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  background: #E5E9F0;
  color: var(--t3);
}
.rd-prog-step.done .rd-prog-mark { background: #DCFCE7; color: #15803D; }
.rd-prog-step.active { color: var(--primary-dark); font-weight: 600; }
.rd-prog-step.active .rd-prog-mark { background: var(--primary); color: #fff; }
.rd-prog-detail {
  margin-top: 10px;
  font-size: 12px;
  color: var(--t2);
  line-height: 1.6;
  border-top: 1px dashed #E5E9F0;
  padding-top: 8px;
}

@media (max-width: 860px) {
  .rd-bar { left: 16px; right: 16px; }
}
@media (max-width: 640px) {
  .rd-bar-progress { display: none; }
}

/* ---------------- 版本管理页 ---------------- */
.card-title { font-size: 15px; font-weight: 600; color: var(--t1, #333); }
.tag {
  display: inline-block; font-size: 11px; font-weight: 500;
  padding: 1px 8px; border-radius: 999px; margin-left: 6px;
  background: #E8F0FE; color: var(--primary, #2f6fed);
}
.tag-warn { background: #FFF7E6; color: #D4880F; }

/* ---------------- 安全教育板块 ---------------- */
.safety-cat {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; margin-bottom: 2px; border: none; background: transparent;
  border-radius: 8px; font-size: 13px; color: #333; cursor: pointer; text-align: left;
  transition: background .15s;
}
.safety-cat:hover { background: #f2faf5; }
.safety-cat.on { background: #e8f8ef; color: #07C160; font-weight: 600; }
.safety-count { font-size: 11px; color: #bbb; }
.safety-cat.on .safety-count { color: #07C160; }
.safety-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px;
}
.safety-v-card {
  position: relative;
  background: #fff; border: 1px solid #eef1f4; border-radius: 12px; overflow: hidden;
  cursor: pointer; transition: transform .15s, box-shadow .15s;
}
.safety-pick {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  background: rgba(0, 0, 0, .45); border-radius: 6px; padding: 3px 5px; display: flex;
}
.safety-pick input { width: 16px; height: 16px; cursor: pointer; accent-color: #07C160; }
.safety-v-card:hover { transform: translateY(-3px); box-shadow: 0 8px 22px rgba(0, 0, 0, .10); }
.safety-thumb {
  position: relative; aspect-ratio: 16 / 9; background: linear-gradient(135deg, #f5f7fa, #e4e8ec);
  display: flex; align-items: center; justify-content: center;
}
.safety-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* 卡片操作：编辑 / 删除（悬停显示，避免误触） */
.safety-acts {
  position: absolute; right: 6px; bottom: 6px; z-index: 2;
  display: flex; gap: 6px; opacity: 0; transition: opacity .15s;
}
.safety-v-card:hover .safety-acts { opacity: 1; }
.safety-act {
  width: 26px; height: 26px; padding: 0; border: none; border-radius: 8px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .55); color: #fff;
}
.safety-act svg { width: 14px; height: 14px; }
.safety-act:hover { background: rgba(0, 0, 0, .78); }
.safety-act.del:hover { background: #e5484d; }
.safety-noimg { display: flex; align-items: center; justify-content: center; color: #c2c9d1; }
.safety-noimg .ico { width: 38px; height: 38px; flex: 0 0 38px; }
.safety-idx {
  position: absolute; top: 8px; left: 8px; background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 11px; padding: 2px 8px; border-radius: 4px;
}
.safety-bad {
  position: absolute; top: 8px; right: 8px; background: rgba(250, 81, 81, .9); color: #fff;
  font-size: 11px; padding: 2px 8px; border-radius: 4px;
}
.safety-title {
  padding: 10px 12px 2px; font-size: 13px; color: #333; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-weight: 500;
}
.safety-cname { padding: 2px 12px 10px; font-size: 12px; color: #999; }

/* 资源类型角标（视频/图文/课件） */
.safety-badge {
  position: absolute; bottom: 8px; left: 8px; color: #fff; font-size: 11px;
  padding: 2px 9px; border-radius: 999px; letter-spacing: .5px;
}
.safety-badge-video { background: rgba(7, 193, 96, .92); }
.safety-badge-image { background: rgba(45, 108, 223, .92); }
.safety-badge-courseware { background: rgba(250, 140, 22, .92); }

/* 类型筛选（全部/视频/图文/课件） */
.safety-types { display: flex; gap: 8px; flex-wrap: wrap; }
.safety-type {
  display: inline-flex; align-items: center; gap: 5px; padding: 6px 13px;
  border: 1px solid #e4e8ec; border-radius: 999px; background: #fff; color: #555;
  font-size: 13px; cursor: pointer; transition: all .15s;
}
.safety-type:hover { border-color: #07C160; color: #07C160; }
.safety-type.on { background: #07C160; border-color: #07C160; color: #fff; font-weight: 600; }
.safety-type-n { font-size: 11px; opacity: .8; }
.safety-type.on .safety-type-n { color: #eafff3; }

/* ---------------- 教学资源板块（老师私有课件库） ---------------- */
.teach-cat {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 10px; margin-bottom: 2px; border: none; background: transparent;
  border-radius: 8px; font-size: 13px; color: #333; cursor: pointer; text-align: left;
  transition: background .15s;
}
.teach-cat:hover { background: #F1F6FF; }
.teach-cat.on { background: #E8F0FE; color: #1a73e8; font-weight: 600; }
.teach-count { font-size: 11px; color: #bbb; font-variant-numeric: tabular-nums; }
.teach-cat.on .teach-count { color: #1a73e8; }
.teach-cat-del {
  flex: 0 0 auto; font-size: 14px; line-height: 1; color: #c2c9d1;
  padding: 0 2px; border-radius: 4px;
}
.teach-cat-del:hover { color: #e5484d; background: #FEE2E2; }

.teach-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px;
}
.teach-card {
  position: relative; background: #fff; border: 1px solid #eef1f4; border-radius: 12px;
  overflow: hidden; cursor: pointer; transition: transform .15s, box-shadow .15s;
}
.teach-card:hover { transform: translateY(-3px); box-shadow: 0 8px 22px rgba(0, 0, 0, .10); }
.teach-thumb {
  position: relative; height: 112px; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, #eef3fb, #dde7f5); color: #8a97a8;
}
.teach-thumb > .ico { width: 34px; height: 34px; }
/* 封面：盖在图标之上；图没加载出来时是透明的，底下的图标照常可见 */
.teach-cover {
  position: absolute; left: 0; top: 0; right: 0; bottom: 0;
  background-size: cover; background-position: center top; background-repeat: no-repeat;
}
.teach-badge {
  position: absolute; bottom: 8px; left: 8px; color: #fff; font-size: 11px;
  padding: 2px 9px; border-radius: 999px; background: rgba(0, 0, 0, .5); z-index: 1;
}
.teach-acts {
  position: absolute; right: 6px; bottom: 6px; z-index: 2;
  display: flex; gap: 6px; opacity: 0; transition: opacity .15s;
}
.teach-card:hover .teach-acts { opacity: 1; }
.teach-act {
  width: 26px; height: 26px; padding: 0; border: none; border-radius: 8px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .55); color: #fff;
}
.teach-act svg, .teach-act .ico { width: 14px; height: 14px; }
.teach-act:hover { background: rgba(0, 0, 0, .78); }
.teach-act.del:hover { background: #e5484d; }
.teach-title {
  padding: 10px 12px 2px; font-size: 13.5px; color: #333; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.teach-meta {
  display: flex; align-items: center; gap: 8px;
  padding: 2px 12px 10px; font-size: 12px; color: #999;
}
.teach-empty { grid-column: 1 / -1; text-align: center; padding: 60px 0; color: #999; }

/* ==================== 换课 / 代课 ==================== */
/* 仪表盘「我的课表」可点击格子：悬停抬起 + 高亮，暗示可点 */
.tt-table td.tt-pick { position: relative; cursor: pointer; }
.tt-table td.tt-pick:hover {
  background: #EFF6FF; box-shadow: inset 0 0 0 1.5px var(--primary);
}
.tt-table td.tt-pick::after {
  content: ''; position: absolute; top: 6px; right: 6px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--primary); opacity: .0; transition: opacity .15s;
}
.tt-table td.tt-pick:hover::after { opacity: 1; }
/* 空白格也能点（从空闲时段发起换课 / 代课），底色跟不可点的空格保持一致 */
.tt-table td.tt-pick-empty { background: #FCFCFD; }

/* 换课弹窗：选项行里的勾选条 */
.ctl-line {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  font-size: 13px; color: var(--t2); cursor: pointer; user-select: none;
}
.ctl-line input { accent-color: var(--primary); }

/* 换课弹窗：「我的换课」清单 */
.swap-list { display: flex; flex-direction: column; gap: 6px; }
.swap-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: #FCFCFD; font-size: 12.5px;
}
.swap-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.swap-row-main b { font-size: 13px; color: var(--t1); }
.swap-row-main span { color: var(--t3); }
.swap-tag {
  flex: 0 0 auto; padding: 1px 8px; border-radius: 999px; font-size: 11.5px;
  background: var(--primary-soft); color: var(--primary-dark);
}
.swap-tag.warn { background: #FEF3C7; color: #B45309; }

/* ==================== 仪表盘改版：液态玻璃 + 必应壁纸 + 顶部搜索栏（2026-10-12） ====================
   为什么要写在这一大段的**最末尾**：下面这些规则和文件上半部分的 `.dash-*` 同层特异性，
   靠「后出现」取胜。插到中段会被原来的规则盖回去。

   为什么所有选择器都带 `.dash-root` 前缀：`workbench.css` 是整个工作台 SPA **唯一**的样式源，
   改 `:root` 或写无前缀的选择器会波及聊天、卡片、安全教育等所有页面。
   玻璃令牌也定义在 `.dash-root` 上而不是 `:root` —— 不外泄，是这次改版的硬约束。 */

/* ---------------- 1. 玻璃令牌与容器 ---------------- */
.dash-root {
  --gl-bg: rgba(255, 255, 255, .86);
  --gl-bg-soft: rgba(255, 255, 255, .44);
  --gl-br: rgba(148, 163, 184, .26);
  --gl-hi: rgba(255, 255, 255, .85);
  --gl-blur: 16px;
  --gl-sat: 150%;
  --gl-r: var(--r-lg);
  --gl-shadow: 0 1px 1px rgba(15, 23, 42, .04), 0 10px 26px -14px rgba(15, 23, 42, .24);
  --gl-shadow-hi: 0 2px 3px rgba(15, 23, 42, .06), 0 22px 46px -20px rgba(15, 23, 42, .36);
  /* 顺手补齐既有缺口：calendar-card.js 与 .cal-* 一直在用 --green/--orange，
     但 :root 从没定义过。定义在这里只影响仪表盘，不动全局。 */
  --green: var(--primary);
  --orange: #F97316;
  position: relative;
  z-index: 1;                     /* 压在壁纸层（z-index:0）之上 */
}

/* 卡片用玻璃底；搜索栏与问候栏不要背景（见 ·之下的独立规则），只留卡片是玻璃块，
   否则顶部一整条色块压着壁纸，既不简洁、内容也不清楚。 */
.dash-root .dash-card {
  background: var(--gl-bg);
  border: 1px solid var(--gl-br);
  box-shadow: inset 0 1px 0 var(--gl-hi), var(--gl-shadow);
  -webkit-backdrop-filter: blur(var(--gl-blur)) saturate(var(--gl-sat));
  backdrop-filter: blur(var(--gl-blur)) saturate(var(--gl-sat));
}

/* 老内核没有 backdrop-filter 时退回高不透明白底，宁可不好看也不能看不清字 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .dash-root .dash-card { background: rgba(255, 255, 255, .93); }
}

/* ---------------- 2. 背景壁纸层 ----------------
   `pointer-events:none` 是硬要求：拖拽排序用 document.elementFromPoint 命中卡片，
   这一层只要能点到，拖拽就整体失效。
   固定定位 + inset:0 只出现在内容区 —— 侧栏（z-index:40）与顶栏（z-index:30）都是
   更高层的不透明定位元素，会自然把它盖住，所以不必改动共享外壳的样式。
   反过来注意：别给 .dash-root / .view 加 transform/filter/backdrop-filter，
   那会把固定定位的包含块变成它们，整层错位。 */
.dash-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(150deg, #123a2b 0%, #1b5140 46%, #0d2b21 100%);
}
.dash-bg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity .9s ease;
  will-change: transform, opacity;
}
.dash-bg.is-ready .dash-bg-img { opacity: 1; }
/* 极缓慢的呼吸式漂移，让静态壁纸不至于像一张贴纸 */
.dash-bg.is-ready .dash-bg-img { animation: bgDrift 60s ease-in-out infinite alternate; }
@keyframes bgDrift {
  to { transform: scale(1.06) translate3d(-1.2%, -.8%, 0); }
}
.dash-bg-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(8, 20, 14, .34), rgba(8, 20, 14, .52));
}
/* drk=1 表示必应那张原图本身偏暗，遮罩要调淡，否则黑成一团 */
.dash-bg.is-dark .dash-bg-scrim {
  background: linear-gradient(180deg, rgba(8, 20, 14, .16), rgba(8, 20, 14, .30));
}
.dash-bg-credit {
  position: absolute;
  right: 14px;
  bottom: 10px;
  max-width: 62%;
  font-size: 11.5px;
  color: rgba(255, 255, 255, .74);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dash-bg-credit:empty { display: none; }
.dash-bg-credit a { color: inherit; text-decoration: none; }
.dash-bg-credit a:hover { text-decoration: underline; }

/* ---------------- 3. 顶部搜索栏 ---------------- */
.dash-search {
  display: flex;
  align-items: center;
  justify-content: center;        /* 居中：引擎下拉 + 搜索框合成一行浮在壁纸上 */
  gap: 10px;
  padding: 10px 12px;
  margin-top: 18px;               /* 与顶栏拉开距离，别贴太靠上 */
  border-radius: var(--r-lg);
  /* 不要背景：搜索栏透明浮在壁纸上。引擎下拉与输入框各自自带浅底，辨识度不受影响 */
  background: transparent;
  border: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* 搜索栏后面的粒子字花絮：局部容器，canvas 铺满、不拦截鼠标事件。
   粒子「天天进步 人人发展」由 dash-effects.js 逐帧绘制，随搜索栏一起居中。 */
.dash-fx {
  position: relative;
  width: min(760px, 94%);
  height: 208px;
  margin: 4px auto 0;
  border-radius: var(--r-lg);
  overflow: hidden;
}
.dash-fx-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* 引擎选择下拉：按钮（图标 + 箭头）+ 玻璃浮层，交互与样式参考 daohang.html */
.ds-picker { position: relative; flex: 0 0 auto; }
.ds-picker-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 44px;
  padding: 0 10px;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 999px;
  background: rgba(255, 255, 255, .14);
  color: rgba(255, 255, 255, .94);
  cursor: pointer;
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 4px 18px rgba(15, 23, 42, .14);
  transition: background .16s, border-color .16s, color .16s;
}
.ds-picker-btn:hover { background: rgba(255, 255, 255, .26); border-color: rgba(255, 255, 255, .55); color: #fff; }
.ds-picker-ico { display: inline-flex; align-items: center; }
.ds-picker-ico .ds-ico-svg { width: 20px; height: 20px; }
.ds-ico-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}
.ds-ib-itl { background: linear-gradient(135deg, #0E7490, #14B8A6); }
.ds-ib-gg  { background: linear-gradient(135deg, #4285F4, #34A853); }
.ds-ib-sg  { background: linear-gradient(135deg, #FA2262, #8930FD); }
.ds-ib-360 { background: linear-gradient(135deg, #1F7A1F, #3EAF3E); font-size: 10px; }
.ds-caret { width: 14px; height: 14px; color: rgba(255, 255, 255, .55); transition: transform .2s; }
.ds-picker.open .ds-caret { transform: rotate(180deg); }

.ds-picker-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 100;
  min-width: max-content;
  padding: 6px;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: 14px;
  background: rgba(15, 23, 42, .34);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  backdrop-filter: blur(20px) saturate(160%);
  box-shadow: 0 12px 32px rgba(15, 23, 42, .25);
  display: none;
  white-space: nowrap;
}
.ds-picker.open .ds-picker-menu { display: block; animation: dsDropIn .16s ease; }
@keyframes dsDropIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ds-opt {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 14px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: rgba(255, 255, 255, .92);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  transition: background .16s;
}
.ds-opt:hover { background: rgba(255, 255, 255, .2); }
.ds-opt.active { background: rgba(255, 255, 255, .26); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35); }
.ds-opt .ds-ico-svg { width: 19px; height: 19px; }
.ds-opt-name { flex: 0 0 auto; }
.ds-field {
  flex: 1 1 280px;
  min-width: 220px;
  max-width: 640px;               /* 居中时别无限拉宽，留出两侧呼吸感 */
  display: flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 6px 0 16px;
  border: 1px solid rgba(255, 255, 255, .32);
  border-radius: 999px;
  background: rgba(255, 255, 255, .14);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 4px 18px rgba(15, 23, 42, .14);
  transition: background .2s, border-color .2s, box-shadow .2s;
}
.ds-field:focus-within {
  background: rgba(255, 255, 255, .22);
  border-color: rgba(255, 255, 255, .6);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, .14), 0 6px 22px rgba(15, 23, 42, .18);
}
.ds-field-ico { display: inline-flex; flex: 0 0 auto; color: rgba(255, 255, 255, .7); transition: color .2s; }
.ds-field-ico .ico { width: 17px; height: 17px; }
.ds-field:focus-within .ds-field-ico { color: #fff; }
.ds-field input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  font: inherit;
  font-size: 14px;
  color: #fff;
}
.ds-field input::placeholder { color: rgba(255, 255, 255, .5); }
.ds-go {
  flex: 0 0 auto;
  height: 34px;
  padding: 0 20px;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(135deg, #07C160, #06AD56);
  color: #fff;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: filter .16s, transform .16s;
}
.ds-go:hover { filter: brightness(1.07); }
.ds-go:active { transform: scale(.97); }

/* ---------------- 4. 动画 ----------------
   入场动画用**长写法**覆盖，不用 animation 简写：简写会把 animation-delay 重置为 0，
   而逐卡错开的延迟写在上面 .dash-grid8 > *:nth-child(n) 里，会被一起冲掉。 */
.dash-root .dash-grid8 > * {
  animation-name: dashRise;
  animation-duration: .42s;
  animation-timing-function: cubic-bezier(.22, .61, .36, 1);
  animation-fill-mode: backwards;   /* 关键：不能用 both，终态 transform 会盖住 :hover 抬升 */
}
@keyframes dashRise {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to { opacity: 1; transform: none; }
}
/* 局部刷新每 60 秒重建卡片，不禁用的话会一直重播首屏动画 */
.dash-root .dash-grid8.no-anim > * { animation: none !important; }

/* 悬停抬升 */
.dash-root .dash-grid8 > [data-dg] {
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.dash-root .dash-grid8 > [data-dg]:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 1px 0 var(--gl-hi), var(--gl-shadow-hi);
  border-color: rgba(255, 255, 255, .85);
}
/* 拖拽/缩放中不要抬升，否则和跟手的位移打架 */
.dash-root .dash-grid8 > [data-dg].dg-dragging,
.dash-root .dash-grid8 > [data-dg].dg-resizing {
  transform: none;
  box-shadow: none;
}

/* 玻璃高光跟随鼠标（坐标由 dashboard.js 的 bindCardGlow 写 --mx/--my） */
.dash-root .dash-grid8 > [data-dg]::after {
  content: '';
  position: absolute;
  inset: 0;                        /* 用 inset 自撑：窄屏下卡片 overflow 会变 visible，不能靠父级裁剪 */
  z-index: 2;
  border-radius: inherit;
  background: radial-gradient(220px at var(--mx, 50%) var(--my, 0%),
    rgba(255, 255, 255, .5), transparent 65%);
  opacity: 0;
  transition: opacity .28s;
  pointer-events: none;
}
.dash-root .dash-grid8 > [data-dg]:hover::after { opacity: 1; }

/* 抓手与缩放手柄在图片背景上几乎看不见，给它们一片小玻璃底 */
.dash-root .dg-grip,
.dash-root .dg-handle { z-index: 3; }
.dash-root .dg-handle::before { border-color: #64748B; }
.dash-root .dash-grid8 > [data-dg]:hover .dg-handle,
.dash-root .dash-grid8 > [data-dg]:hover .dg-grip { background: rgba(255, 255, 255, .72); border-radius: 5px; }

/* ---------------- 5. 状态条：不要背景，直接浮在壁纸上 ----------------
   绿色渐变 → 玻璃底 → 干脆去底：问候/日期/时钟是仪表盘抬头文字，给一层光晕保证
   在每天深浅不同的壁纸上都可读；左侧 3px 主色条保留「这是状态条」的语义。 */
.dash-root .dash-statusbar {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  text-shadow: 0 1px 2px rgba(255, 255, 255, .8), 0 0 10px rgba(255, 255, 255, .5);
}
.dash-root .dash-statusbar .btn { text-shadow: none; }
.dash-root .ds-greet { color: var(--t1); font-weight: 600; }
.dash-root .ds-date { color: var(--t2); }

/* ---------------- 6. 逐卡美化 ---------------- */

/* 我的课表：倒计时与课表不再两级绿色叠噪 */
.dash-root .dash-next {
  background: rgba(255, 255, 255, .5);
  border: 1px solid rgba(255, 255, 255, .62);
  border-left: 3px solid var(--primary);
  border-radius: var(--r-sm);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8);
}
.dash-root .dash-next.now {
  background: rgba(255, 251, 235, .68);
  border-left-color: var(--amber);
}
.dash-root .dash-next.now .dash-next-label { color: #B45309; }
.dash-root .dash-next-value { font-variant-numeric: tabular-nums; letter-spacing: .2px; }
.dash-root .dash-tt { max-height: 100%; }

/* 统计卡：4 张独立卡（设备在线/今日通知/三检报送/今日请假），各自就是普通玻璃卡；
   卡内是「大数值 + 脚注」，数值用等宽字形，变化时不左右跳。 */
.dash-root .dash-stat-solo .card-body { display: flex; flex-direction: column; justify-content: center; }
.dash-root .dash-stat-solo .dash-stat-value {
  margin-top: 0;
  font-size: 34px;
  font-variant-numeric: tabular-nums;
}
.dash-root .dash-stat-solo .dash-stat-value small { margin-left: 3px; font-size: 14px; font-weight: 500; color: var(--t3); }
.dash-root .dash-stat-solo .dash-stat-foot { margin-top: 6px; }
.dash-root .dash-stat-solo[data-to] { cursor: pointer; }
/* 可点卡的入口箭头：默认隐藏，悬停浮出（沿用原统计卡的语言） */
.dash-root .dash-stat-solo[data-to] .dash-stat-go { display: inline-flex; color: var(--t3); opacity: 0; transition: opacity .18s, color .18s; }
.dash-root .dash-stat-solo[data-to]:hover .dash-stat-go { opacity: 1; color: var(--primary-dark); }

/* 今日待办 / 通用列表行：行首一道类型色条，比整行换底色更清爽 */
.dash-root .dash-row,
.dash-root .notice-row {
  position: relative;
  padding-left: 10px;
}
.dash-root .dash-row::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 3px;
  border-radius: 999px;
  background: var(--primary);
  opacity: .55;
}
.dash-root .dash-row:hover { background: rgba(255, 255, 255, .55); }
.dash-root .dash-row-sub { font-variant-numeric: tabular-nums; }

/* 最近通知：标题单行截断 + 相对时间对齐 */
.dash-root .notice-row { padding-left: 10px; }
.dash-root .notice-row + .notice-row { border-top-color: rgba(255, 255, 255, .5); }

/* 全部班级：进度条与胶囊 */
.dash-root .dash-meter {
  background: rgba(255, 255, 255, .5);
  box-shadow: inset 0 1px 2px rgba(15, 23, 42, .06);
}
.dash-root .dash-chip {
  background: rgba(255, 255, 255, .58);
  border-color: rgba(255, 255, 255, .62);
}
.dash-root .dash-chip:hover { background: rgba(255, 255, 255, .82); }

/* 本周值周：纵向名单，不再做横向胶囊 */
.dash-root .duty-grid {
  align-items: stretch;
  gap: 4px;
}
.dash-root .duty-item {
  flex: 0 0 auto;
}

/* 今日歌曲：行高与封面拉开层次 */
.dash-root .dash-song-list { max-height: 100%; }
.dash-root .song-row { padding: 8px 0; }
.dash-root .song-row + .song-row { border-top-color: rgba(255, 255, 255, .5); }
.dash-root .song-row:hover { background: rgba(255, 255, 255, .45); border-radius: var(--r-sm); }

/* 月历：去边框改填充，今天用主色圆底，格子压扁一点好在 5 行内放下 */
.dash-root .cal-cell {
  min-height: 42px;
  border-color: transparent;
  background: rgba(255, 255, 255, .42);
}
.dash-root .cal-cell.cal-weekend { background: rgba(255, 255, 255, .3); }
.dash-root .cal-cell[data-date]:hover {
  background: rgba(255, 255, 255, .8);
  box-shadow: 0 0 0 1.5px var(--primary) inset;
}
.dash-root .cal-cell.cal-today {
  background: rgba(255, 255, 255, .82);
  border-color: var(--primary);
  box-shadow: 0 0 0 1.5px var(--primary) inset;
}
.dash-root .cal-week-cell { color: rgba(255, 255, 255, .88); text-shadow: 0 1px 2px rgba(0, 0, 0, .35); }
.dash-root .cal-week-cell.cal-weekend { color: #FDE68A; }
.dash-root .dash-sec { color: rgba(255, 255, 255, .8); text-shadow: 0 1px 2px rgba(0, 0, 0, .3); }
.dash-root .dash-card .dash-sec { color: var(--t3); text-shadow: none; }
.dash-root .dash-more { color: var(--primary-dark); }

/* ---------------- 6.5 卡头压缩：标题栏不再吃掉内容高度 ----------------
   仪表盘卡密、行高只有 72px，14px 上下内边距的卡头在 2 行高的卡里要占掉近 1/3。
   压到 8px、字号 13px、图标 22px，把高度让给正文。 */
.dash-root .dash-grid8 .card-head {
  padding: 8px 14px 10px;
  gap: 7px;
  font-size: 13px;
  /* 文字行盒收紧到字身（默认继承的大行高会让文字在 flex 居中时视觉偏上、
     顶到上边距），下边距多给 2px 补偿文字自身的视觉重心，上下均衡 */
  line-height: 1;
  min-height: 0;
  cursor: move;                              /* 整条卡头都是拖拽把手（bindDashDrag） */
}
.dash-root .dash-grid8 .card-head .dash-head-ico { width: 22px; height: 22px; flex: 0 0 22px; border-radius: 7px; }
.dash-root .dash-grid8 .card-head .dash-head-ico .ico { width: 13px; height: 13px; flex: 0 0 13px; }
.dash-root .dash-grid8 .card-head .badge { padding: 2px 8px; font-size: 11px; }
.dash-root .dash-grid8 > .dash-card > .card-body { padding: 12px 14px; }

/* ---------------- 6.6 尺寸自适应：按卡片格数切换内容密度 ----------------
   data-w / data-h 由 applyDashLayout() 写在卡片上（拖大小过程中实时同步）。
   原则是**非必要不滚动**：空间不够时按这个顺序让位 ——
   ① 藏次要块（卡头次要信息 / 小节预告）→ ② 压内边距与行高 → ③ 最后才允许滚动。
   只在宽屏生效：≤1280px 是流式排布（行高交回内容），data-h 不再等于真实视觉高度。 */
@media (min-width: 1281px) {

/* ① 让位：矮卡里卡头的次要信息先撤，把宽度与高度让给正文 */
.dash-root .dash-grid8 > [data-h="1"] .card-head .dev-muted,
.dash-root .dash-grid8 > [data-h="1"] .card-head .badge,
.dash-root .dash-grid8 > [data-h="1"] .card-head .dash-row-sub { display: none; }
.dash-root .dash-grid8 > [data-h="1"] .dash-sec:not(.my-lessons-sec) { display: none; }
/* 窄卡里的脚注截成一行省略号，别折成两行把卡撑高 */
.dash-root .dash-grid8 > [data-w="1"] .dash-stat-foot,
.dash-root .dash-grid8 > [data-w="2"] .dash-stat-foot { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ② 压缩：矮卡收紧内边距与列表行高 */
.dash-root .dash-grid8 > [data-h="1"] .card-body { padding: 8px 12px; }
.dash-root .dash-grid8 > [data-h="2"] .dash-row,
.dash-root .dash-grid8 > [data-h="2"] .notice-row,
.dash-root .dash-grid8 > [data-h="2"] .song-row { padding-top: 5px; padding-bottom: 5px; }
.dash-root .dash-grid8 > [data-h="1"] .dash-row,
.dash-root .dash-grid8 > [data-h="1"] .notice-row,
.dash-root .dash-grid8 > [data-h="1"] .song-row { padding-top: 3px; padding-bottom: 3px; }

/* 统计卡：数值随卡宽缩放；只有 1 行高时改成「数字 + 脚注」一行横排 */
.dash-root .dash-stat-solo[data-w="4"] .dash-stat-value,
.dash-root .dash-stat-solo[data-w="3"] .dash-stat-value { font-size: 30px; }
.dash-root .dash-stat-solo[data-w="2"] .dash-stat-value { font-size: 26px; }
.dash-root .dash-stat-solo[data-w="1"] .dash-stat-value { font-size: 22px; }
.dash-root .dash-stat-solo[data-h="1"] .card-body {
  flex-direction: row;
  align-items: baseline;
  justify-content: flex-start;
  gap: 8px;
}
.dash-root .dash-stat-solo[data-h="1"] .dash-stat-value { font-size: 24px; }
.dash-root .dash-stat-solo[data-h="1"] .dash-stat-foot { margin-top: 0; }

/* 本周值周：2×2 是默认尺寸，空间全给内容 —— 先撤标题与周次，名单/本人/下周都要在 */
.dash-root .dg-duty[data-h="1"] .dash-done,
.dash-root .dg-duty[data-h="1"] .duty-next { display: none; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .card-head { padding: 2px 6px; border-bottom: 0; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .card-head .dash-head-ico,
.dash-root .dg-duty[data-w="2"][data-h="2"] .card-head .duty-title,
.dash-root .dg-duty[data-w="2"][data-h="2"] .card-head .duty-range,
.dash-root .dg-duty[data-w="2"][data-h="2"] .card-head .spacer { display: none; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .card-body { padding: 2px 10px 6px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-grid { gap: 2px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-item { padding: 1px 6px; font-size: 12px; border-radius: 5px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-role { height: 16px; min-width: 30px; padding: 0 4px; font-size: 9.5px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-who { font-size: 12px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-score { font-size: 11px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-score.empty { font-size: 10.5px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-item.muted { padding: 2px 6px; font-size: 11.5px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-grid + .dash-done { margin-top: 4px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .dash-done { padding: 2px 7px; font-size: 11px; gap: 4px; border-radius: 6px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .dash-done .ico { width: 12px; height: 12px; flex: 0 0 12px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-next { margin-top: 4px; }
.dash-root .dg-duty[data-w="2"][data-h="2"] .duty-next .dash-sec { margin: 0 0 2px; font-size: 10.5px; }
/* 不横向滚动：随卡宽换行排布，窄卡纵向一列、宽卡自然多列，真装不下才纵向滚 */
.dash-root .dash-grid8 > .dg-duty .duty-grid { flex-wrap: wrap; overflow-x: visible; padding-bottom: 0; }
.dash-root .dg-duty[data-w="1"] .duty-grid,
.dash-root .dg-duty[data-w="2"] .duty-grid { gap: 4px; }
.dash-root .dg-duty[data-w="1"] .duty-item,
.dash-root .dg-duty[data-w="2"] .duty-item { font-size: 12px; }

/* 周课表：矮卡先压表格行高与字号（不够才滚），meta 行让位 */
.dash-root .dg-week[data-h="3"] .tt-table th,
.dash-root .dg-week[data-h="3"] .tt-table td,
.dash-root .dg-week[data-h="4"] .tt-table th,
.dash-root .dg-week[data-h="4"] .tt-table td { padding-top: 4px; padding-bottom: 4px; font-size: 12px; }
.dash-root .dg-week[data-h="3"] .tt-meta { display: none; }
/* 周课表（窄卡）：宽度不够时不再横向溢出 —— 表格改固定布局均分列宽，
   节次列的作息时间撤掉、格内课程名单行省略；真放不下才走 .tt-scroll 纵向空间 */
.dash-root .dg-week[data-w="4"] .tt-table,
.dash-root .dg-week[data-w="3"] .tt-table,
.dash-root .dg-week[data-w="2"] .tt-table,
.dash-root .dg-week[data-w="1"] .tt-table { min-width: 0; table-layout: fixed; }
.dash-root .dg-week[data-w="4"] .tt-table th,
.dash-root .dg-week[data-w="4"] .tt-table td,
.dash-root .dg-week[data-w="3"] .tt-table th,
.dash-root .dg-week[data-w="3"] .tt-table td,
.dash-root .dg-week[data-w="2"] .tt-table th,
.dash-root .dg-week[data-w="2"] .tt-table td,
.dash-root .dg-week[data-w="1"] .tt-table th,
.dash-root .dg-week[data-w="1"] .tt-table td { padding: 2px 3px; font-size: 11px; }
.dash-root .dg-week[data-w="4"] .tt-table .tt-slot .tt-time,
.dash-root .dg-week[data-w="3"] .tt-table .tt-slot .tt-time,
.dash-root .dg-week[data-w="2"] .tt-table .tt-slot .tt-time,
.dash-root .dg-week[data-w="1"] .tt-table .tt-slot .tt-time { display: none; }
.dash-root .dg-week[data-w="4"] .tt-table .tt-slot,
.dash-root .dg-week[data-w="3"] .tt-table .tt-slot,
.dash-root .dg-week[data-w="2"] .tt-table .tt-slot,
.dash-root .dg-week[data-w="1"] .tt-table .tt-slot { width: 34px; }
.dash-root .dg-week[data-w="4"] .tt-subj,
.dash-root .dg-week[data-w="3"] .tt-subj,
.dash-root .dg-week[data-w="2"] .tt-subj,
.dash-root .dg-week[data-w="1"] .tt-subj { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 月历：矮卡缩小格子的最小高度与角标字号 */
.dash-root .dg-cal[data-h="4"] .cal-cell,
.dash-root .dg-cal[data-h="5"] .cal-cell { min-height: 34px; padding: 4px 6px; }
.dash-root .dg-cal[data-h="3"] .cal-cell { min-height: 28px; padding: 3px 5px; }
.dash-root .dg-cal[data-h="3"] .cal-tag,
.dash-root .dg-cal[data-h="4"] .cal-tag { font-size: 9px; }
.dash-root .dg-cal[data-h="3"] .cal-legend,
.dash-root .dg-cal[data-h="4"] .cal-legend { display: none; }

/* 今天卡：字号档位见 6.8 节（天气卡自己按 data-w/data-h 调） */

}   /* @media (min-width: 1281px) */

/* ---------- 6.7 「距下一节课」「今天的日期」两张无头卡：内容直接当整张卡 ----------
   不再「卡片里再套一个小框」：card-body 去内边距并撑满卡高，
   内层条（倒计时横幅 / 今天日期条）撤掉背景、边框、圆角、底距后铺满整卡，
   倒计时内容垂直居中。 */
.dash-root .dash-grid8 > .dg-next .card-body,
.dash-root .dash-grid8 > .dg-todaydate .card-body,
.dash-root .dash-grid8 > .dg-schoolcd .card-body {
  padding: 0;
  height: 100%;
  display: flex;
}
.dash-root .dash-grid8 > .dg-next .card-body > *,
.dash-root .dash-grid8 > .dg-schoolcd .card-body > * {
  flex: 1 1 auto;
}
.dash-root .dash-grid8 > .dg-next .dash-next,
.dash-root .dash-grid8 > .dg-schoolcd .dash-next {
  border: 0;
  border-radius: 0;
  background: none;
  margin: 0;
  min-height: 72px;
  padding: 12px 18px;
}
.dash-root .dash-grid8 > .dg-next .dash-next,
.dash-root .dash-grid8 > .dg-schoolcd .dash-next {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
}

/* ---------------- 6.8 今天卡（日期 + 天气）+ 月历翻页 + 导航卡 ----------------
   版式：
     .wx-hero  —— 左：天气图标 + 温度 / 天气 / 湿度风力；右：时钟 + 月日星期 + 农历节气
     .wx-strip —— 24 小时 / 7 天分段 + 可横滑的预报格 + 「详情 ›」
   背景：local `assets/weather/*.mp4` 循环天空视频（晴 qing / 多云 duoyun / 阴 yin /
     雨 yu / 雪 xue，均 1920×556），按 data-wx 切换并在视频上压一层暗色渐变，
     保证白色文字在明亮天空上也清晰；视频加载前用浅色渐变兜底。
     雾天在视频上再叠 `.wxb-fog` 雾带，雷雨叠 `.wxb-flash` 白闪。
   ② 月历卡头加 ‹ › 切月按钮（翻页只重绘 card-body，卡头与拖拽绑定不动）。
   ③ 导航卡把参考导航页网址做成全宽卡片（按 dash-nav 权限渲染）。 */
.dash-root .dash-grid8 > .dg-todaydate .card-body {
  position: relative;
  overflow: hidden;
  flex-direction: column;
  padding: 0;
}
.wx-card { position: relative; }
/* 视频加载前的兜底底色（按天气换一套浅色渐变） */
.wx-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, #E9F4FF 0%, #F7FBFF 48%, #EDF9F2 100%);
  transition: background .6s ease;
}
.wx-card[data-wx="sun"]::before    { background: linear-gradient(135deg, #FFF2CE 0%, #F4FAFF 48%, #EBF9F0 100%); }
.wx-card[data-wx="partly"]::before { background: linear-gradient(135deg, #E6F2FF 0%, #F8FCFF 48%, #ECF8F1 100%); }
.wx-card[data-wx="cloud"]::before  { background: linear-gradient(135deg, #E7EEF6 0%, #F7FAFD 48%, #EDF4EF 100%); }
.wx-card[data-wx="rain"]::before   { background: linear-gradient(135deg, #DDE9F6 0%, #F2F7FC 48%, #E5F0EA 100%); }
.wx-card[data-wx="storm"]::before  { background: linear-gradient(135deg, #D8E0EC 0%, #EDF2F8 48%, #E0E9E4 100%); }
.wx-card[data-wx="snow"]::before   { background: linear-gradient(135deg, #E3F5FF 0%, #FAFEFF 48%, #EAF7F3 100%); }
.wx-card[data-wx="fog"]::before    { background: linear-gradient(135deg, #E8ECF1 0%, #F7F9FA 48%, #EBF3EE 100%); }

/* 天空视频 + 压暗层：只保留一层很淡的暗角（左侧稍深，保证白字可读），
   中间尽量透出画面本身，别让整张卡蒙一层灰黑 */
.wx-sky::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(8, 16, 28, .32) 0%, rgba(8, 16, 28, .18) 30%, rgba(8, 16, 28, .04) 58%, rgba(8, 16, 28, .22) 100%),
    linear-gradient(0deg, rgba(8, 16, 28, .30) 0%, rgba(8, 16, 28, .08) 38%, rgba(8, 16, 28, 0) 64%);
}
.wx-sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.wxb-video {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 200%;              /* 素材下半截是拼接的其他画面，只露上半截：容器高度翻倍后裁掉下半 */
  object-fit: cover;
  object-position: center top;
  z-index: 0;
  opacity: 0;
  transition: opacity .6s ease;
}
.wx-card[data-wx] .wxb-video { opacity: 1; }
.wx-sky::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(70% 58% at 12% 0%, rgba(255, 255, 255, .16), rgba(255, 255, 255, 0) 62%);
}
.wx-sky i { position: absolute; display: block; }

/* 太阳：右上角暖光呼吸，带缓慢旋转的光晕 */
.wxb-sun {
  right: 4%;
  top: 2%;
  width: 126px;
  height: 126px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 224, 130, .95) 0%, rgba(255, 203, 84, .55) 34%, rgba(255, 203, 84, 0) 68%);
  opacity: 0;
  animation: wxb-breathe 7s ease-in-out infinite;
}
.wxb-sun::after {
  content: '';
  position: absolute;
  inset: -22%;
  border-radius: 50%;
  background: repeating-conic-gradient(from 0deg, rgba(255, 211, 94, .22) 0 10deg, rgba(255, 211, 94, 0) 10deg 34deg);
  animation: wxb-spin 46s linear infinite;
}
.wx-card[data-wx="sun"] .wxb-sun { opacity: .95; }
.wx-card[data-wx="partly"] .wxb-sun { opacity: .7; }

/* 云：三团不同大小/速度的白云横向漂过 */
.wxb-cloud {
  left: -34%;
  border-radius: 50%;
  background: radial-gradient(ellipse 62% 56% at 42% 56%, rgba(255, 255, 255, .98), rgba(255, 255, 255, .6) 58%, rgba(255, 255, 255, 0) 76%);
  filter: blur(5px);
  opacity: 0;
  animation: wxb-drift linear infinite;
}
.wxb-c1 { top: -8%; width: 60%; height: 38%; animation-duration: 34s; }
.wxb-c2 { top: 20%; width: 46%; height: 30%; animation-duration: 47s; animation-delay: -15s; }
.wxb-c3 { top: 44%; width: 34%; height: 24%; animation-duration: 60s; animation-delay: -34s; }
.wx-card[data-wx="partly"] .wxb-cloud,
.wx-card[data-wx="cloud"] .wxb-cloud,
.wx-card[data-wx="rain"] .wxb-cloud,
.wx-card[data-wx="storm"] .wxb-cloud,
.wx-card[data-wx="snow"] .wxb-cloud,
.wx-card[data-wx="fog"] .wxb-cloud { opacity: .78; }
.wx-card[data-wx="storm"] .wxb-cloud {
  background: radial-gradient(ellipse 62% 56% at 42% 56%, rgba(226, 232, 240, .95), rgba(203, 213, 225, .62) 58%, rgba(203, 213, 225, 0) 76%);
  opacity: .85;
}

/* 雨：斜向落下的细线（背景图块位移，循环无缝） */
.wxb-rain {
  left: -15%;
  right: -15%;
  top: -40%;
  bottom: -40%;
  background-image: linear-gradient(to bottom, rgba(56, 163, 245, 0) 0%, rgba(56, 163, 245, .4) 55%, rgba(56, 163, 245, 0) 100%);
  background-size: 2px 16px;
  background-repeat: repeat;
  transform: rotate(9deg);
  opacity: 0;
}
.wx-card[data-wx="rain"] .wxb-rain,
.wx-card[data-wx="storm"] .wxb-rain { opacity: 1; animation: wxb-rainfall .55s linear infinite; }
.wx-card[data-wx="storm"] .wxb-rain { filter: brightness(.9); }

/* 雪：两层不同速度的小雪点 */
.wxb-snow {
  inset: -10%;
  background-image:
    radial-gradient(circle, rgba(255, 255, 255, .95) 0 1.5px, rgba(255, 255, 255, 0) 2px),
    radial-gradient(circle, rgba(255, 255, 255, .8) 0 1px, rgba(255, 255, 255, 0) 1.6px);
  background-size: 46px 46px, 74px 74px;
  background-position: 0 0, 20px 28px;
  opacity: 0;
}
.wx-card[data-wx="snow"] .wxb-snow { opacity: 1; animation: wxb-snowfall 12s linear infinite; }

/* 雾：两团柔光横向缓慢来回 */
.wxb-fog {
  left: -40%;
  right: -40%;
  top: 26%;
  height: 48%;
  z-index: 2;
  background:
    radial-gradient(58% 62% at 30% 50%, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0) 70%),
    radial-gradient(52% 56% at 68% 42%, rgba(255, 255, 255, .78), rgba(255, 255, 255, 0) 70%);
  filter: blur(6px);
  opacity: 0;
}
.wx-card[data-wx="fog"] .wxb-fog { opacity: 1; animation: wxb-fogmove 22s ease-in-out infinite alternate; }

/* 雷雨：偶尔整卡白闪一下 */
.wxb-flash {
  inset: 0;
  z-index: 2;
  background: rgba(255, 255, 255, .92);
  opacity: 0;
}
.wx-card[data-wx="storm"] .wxb-flash { animation: wxb-flash 13s ease-out infinite; }

/* 视频本身已带太阳/云/雨/雪动效，这几层纯 CSS 的关掉避免重复叠加；
   雾带与闪电保留（视频没有对应的雾/雷元素）。 */
.wx-card .wxb-sun,
.wx-card .wxb-cloud,
.wx-card .wxb-rain,
.wx-card .wxb-snow { display: none; }

@keyframes wxb-breathe {
  0%, 100% { transform: scale(.94); }
  50% { transform: scale(1.06); }
}
@keyframes wxb-spin { to { transform: rotate(360deg); } }
@keyframes wxb-drift {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(300%, 0, 0); }
}
@keyframes wxb-rainfall {
  from { background-position: 0 0; }
  to { background-position: 4px 32px; }
}
@keyframes wxb-snowfall {
  from { background-position: 0 0, 20px 28px; }
  to { background-position: 46px 92px, 94px 176px; }
}
@keyframes wxb-fogmove {
  from { transform: translate3d(-6%, 0, 0); }
  to { transform: translate3d(6%, 0, 0); }
}
@keyframes wxb-flash {
  0%, 90%, 100% { opacity: 0; }
  91% { opacity: .5; }
  92% { opacity: 0; }
  93.5% { opacity: .32; }
  94.5% { opacity: 0; }
}
.wx-hero {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 18px 6px;
}
.wx-main {
  flex: 1 1 56%;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}
.wx-ico-box { display: inline-flex; flex: 0 0 auto; animation: wxb-float 6.5s ease-in-out infinite; }
@keyframes wxb-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}
.wx-num { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.wx-temp { display: flex; align-items: flex-start; line-height: 1; color: var(--t1); }
.wx-temp b {
  font-size: 38px;
  font-weight: 700;
  letter-spacing: -1.5px;
  font-variant-numeric: tabular-nums;
}
.wx-temp i { font-style: normal; font-size: 14px; font-weight: 600; color: var(--t2); margin: 5px 0 0 2px; }
.wx-desc { font-size: 13px; font-weight: 600; color: var(--t1); }
.wx-meta {
  font-size: 11.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wx-side {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 2px;
  text-align: right;
  padding-left: 14px;
  border-left: 1px dashed color-mix(in srgb, var(--border) 85%, transparent);
}
.wx-clock {
  font-size: 26px;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: .5px;
  font-variant-numeric: tabular-nums;
  color: var(--primary);
}
.wx-date { display: flex; align-items: baseline; gap: 6px; font-size: 13px; font-weight: 600; color: var(--t1); }
.wx-date .wx-week { color: var(--primary); }
.wx-lunar {
  max-width: 100%;
  font-size: 11.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wx-tags { display: flex; gap: 5px; margin-top: 1px; }
.wx-tag {
  font-size: 10px;
  line-height: 1.5;
  font-weight: 600;
  border-radius: 4px;
  padding: 0 5px;
  border: 1px solid transparent;
}
.wx-tag.rest { color: var(--green); background: color-mix(in srgb, var(--green) 12%, #fff); border-color: color-mix(in srgb, var(--green) 38%, transparent); }
.wx-tag.work { color: var(--orange); background: color-mix(in srgb, var(--orange) 12%, #fff); border-color: color-mix(in srgb, var(--orange) 38%, transparent); }
.wx-tag.fest { color: var(--amber); background: color-mix(in srgb, var(--amber) 12%, #fff); border-color: color-mix(in srgb, var(--amber) 38%, transparent); }

/* -- 底部预报条 -- */
.wx-strip {
  position: relative;
  z-index: 1;
  flex: 0 0 52px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px 0 18px;
  border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
  background: rgba(255, 255, 255, .58);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.wx-seg {
  flex: 0 0 auto;
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: rgba(255, 255, 255, .8);
}
.wx-seg button {
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 11px;
  line-height: 1.4;
  color: var(--t2);
  padding: 2px 8px;
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.wx-seg button.on { background: var(--primary); color: #fff; font-weight: 600; }
.wx-cells {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 2px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.wx-cells::-webkit-scrollbar { display: none; }
.wx-cell {
  flex: 0 0 auto;
  width: 46px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  border-radius: 8px;
  padding: 2px 0;
}
.wx-cell.now { background: color-mix(in srgb, var(--primary) 10%, transparent); }
.wx-cell-t { font-size: 10px; line-height: 1.2; color: var(--t3); white-space: nowrap; }
.wx-cell-v { font-size: 11.5px; line-height: 1.2; font-weight: 600; color: var(--t1); font-variant-numeric: tabular-nums; }
.wx-cell-v b { font-weight: 700; }
.wx-more {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 11.5px;
  color: var(--primary);
  padding: 4px 2px 4px 6px;
  cursor: pointer;
  white-space: nowrap;
}
.wx-more:hover { text-decoration: underline; }
.wx-wait { font-size: 11.5px; color: var(--t3); }

/* -- 视频背景上的文字：统一白字 + 轻投影，压暗层保证对比度 -- */
.wx-card .wx-temp { color: #fff; text-shadow: 0 2px 10px rgba(0, 0, 0, .30); }
.wx-card .wx-temp i { color: rgba(255, 255, 255, .86); }
.wx-card .wx-desc { color: #fff; text-shadow: 0 1px 8px rgba(0, 0, 0, .38); }
.wx-card .wx-meta { color: rgba(255, 255, 255, .82); text-shadow: 0 1px 6px rgba(0, 0, 0, .38); }
.wx-card .wx-side { border-left-color: rgba(255, 255, 255, .30); }
.wx-card .wx-clock { color: #fff; text-shadow: 0 2px 12px rgba(0, 0, 0, .36); }
.wx-card .wx-date { color: #fff; text-shadow: 0 1px 8px rgba(0, 0, 0, .34); }
.wx-card .wx-date .wx-week { color: rgba(255, 255, 255, .92); }
.wx-card .wx-lunar { color: rgba(255, 255, 255, .84); text-shadow: 0 1px 6px rgba(0, 0, 0, .34); }
/* 预报条改成深色毛玻璃 */
.wx-card .wx-strip {
  border-top-color: rgba(255, 255, 255, .18);
  background: rgba(10, 18, 28, .34);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  backdrop-filter: blur(10px) saturate(1.15);
}
.wx-card .wx-cell-t { color: rgba(255, 255, 255, .72); }
.wx-card .wx-cell-v { color: #fff; }
.wx-card .wx-cell.now { background: rgba(255, 255, 255, .18); }
.wx-card .wx-seg { background: rgba(255, 255, 255, .16); border-color: rgba(255, 255, 255, .24); }
.wx-card .wx-seg button { color: rgba(255, 255, 255, .88); }
.wx-card .wx-seg button.on { background: var(--primary); color: #fff; }
.wx-card .wx-more { color: #fff; text-shadow: 0 1px 6px rgba(0, 0, 0, .4); }
.wx-card .wx-wait { color: rgba(255, 255, 255, .82); }

/* -- 无天气权限时：只显示时钟 + 日期 -- */
.wx-plain {
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 14px 20px;
}
.wx-plain .wx-clock { font-size: 44px; }
.wx-plain-info { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.wx-plain .wx-date { font-size: 15px; }
.wx-plain .wx-lunar { font-size: 12px; }
.wx-term { font-size: 11.5px; color: var(--t3); }

/* -- 天气图标尺寸与动效（动效只发生在图标内部） -- */
.wx-ico { display: block; width: 58px; height: 58px; flex: 0 0 auto; }
.wx-ico.wx-ico-sm { width: 18px; height: 18px; }
.wx-ico.wxd-ico { width: 64px; height: 64px; }
.wx-ico .wx-rays { transform-box: fill-box; transform-origin: center; animation: wx-spin 26s linear infinite; }
.wx-ico .wx-drops line { animation: wx-drop 1.3s linear infinite; }
.wx-ico .wx-drops line:nth-child(2) { animation-delay: .32s; }
.wx-ico .wx-drops line:nth-child(3) { animation-delay: .64s; }
.wx-ico .wx-flakes circle { animation: wx-flake 2.6s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
.wx-ico .wx-flakes circle:nth-child(2) { animation-delay: .5s; }
.wx-ico .wx-flakes circle:nth-child(3) { animation-delay: 1s; }
.wx-ico .wx-fogline { animation: wx-fogmove 3.2s ease-in-out infinite alternate; }
.wx-ico .wx-bolt { animation: wx-flash 3.4s steps(1) infinite; }
@keyframes wx-spin { to { transform: rotate(360deg); } }
@keyframes wx-drop {
  0% { opacity: 0; transform: translateY(-3px); }
  30% { opacity: 1; }
  75% { opacity: 1; }
  100% { opacity: 0; transform: translateY(5px); }
}
@keyframes wx-flake { 0%, 100% { opacity: .45; transform: scale(.85); } 50% { opacity: 1; transform: scale(1.1); } }
@keyframes wx-fogmove { from { transform: translateX(-1.5px); } to { transform: translateX(1.5px); } }
@keyframes wx-flash {
  0%, 84%, 92%, 100% { opacity: 0; }
  86% { opacity: 1; }
  89% { opacity: .1; }
}

/* -- 尺寸档位：按卡片实际宽高切换信息密度（容器查询，拖大拖小即时生效）
      高度档：图标 / 温度 / 时钟 / 预报条高度逐级放大；
      宽度档：先收内边距，再隐次要信息，窄到 2 列以下改成上下排。
      宽度规则写在高度规则之后，冲突时以宽度档为准。 -- */
.dash-root .dash-grid8 > .dg-todaydate {
  container-type: size;
  container-name: wxcard;
}
/* 矮卡（1 行高）：只留 图标 + 温度 + 时钟 + 日期 */
@container wxcard (max-height: 112px) {
  .wx-strip { display: none; }
  .wx-hero { padding: 8px 16px; gap: 10px; }
  .wx-ico { width: 42px; height: 42px; }
  .wx-temp b { font-size: 28px; }
  .wx-clock { font-size: 20px; }
  .wx-meta, .wx-lunar, .wx-tags { display: none; }
  .wx-plain { padding: 10px 18px; }
  .wx-plain .wx-clock { font-size: 28px; }
  .wx-term { display: none; }
  .wxb-sun { width: 76px; height: 76px; }
  .wxb-cloud { filter: blur(4px); }
}
/* 3 行高起：字号与预报条跟着放大 */
@container wxcard (min-height: 200px) {
  .wx-hero { gap: 18px; padding: 12px 20px 8px; }
  .wx-ico { width: 66px; height: 66px; }
  .wx-temp b { font-size: 44px; }
  .wx-clock { font-size: 30px; }
  .wx-date { font-size: 14px; }
  .wx-strip { flex-basis: 56px; }
}
@container wxcard (min-height: 300px) {
  .wx-ico { width: 78px; height: 78px; }
  .wx-temp b { font-size: 50px; }
  .wx-clock { font-size: 34px; }
  .wx-desc { font-size: 14px; }
  .wx-lunar { font-size: 12px; }
  .wx-strip { flex-basis: 60px; }
  .wx-cell { width: 50px; }
  .wx-cell-t { font-size: 10.5px; }
  .wx-cell-v { font-size: 12px; }
}
@container wxcard (min-height: 400px) {
  .wx-hero { gap: 22px; padding: 16px 24px 10px; }
  .wx-ico { width: 88px; height: 88px; }
  .wx-temp b { font-size: 56px; }
  .wx-clock { font-size: 38px; }
  .wx-strip { flex-basis: 64px; }
  .wx-cell { width: 54px; }
  .wx-cell .wx-ico-sm { width: 20px; height: 20px; }
  .wx-cell-v { font-size: 12.5px; }
}
@container wxcard (min-height: 520px) {
  .wx-ico { width: 96px; height: 96px; }
  .wx-temp b { font-size: 62px; }
  .wx-clock { font-size: 42px; }
  .wx-meta { font-size: 12.5px; }
  .wx-strip { flex-basis: 68px; }
  .wx-cell { width: 58px; }
}
/* 宽卡：预报格加宽、留白加大，避免一行内容被拉得太散 */
@container wxcard (min-width: 860px) {
  .wx-hero { padding: 10px 26px 6px; gap: 20px; }
  .wx-strip { padding: 0 18px 0 26px; gap: 12px; }
  .wx-cell { width: 54px; }
  .wx-cell-t { font-size: 10.5px; }
  .wx-cell-v { font-size: 12px; }
  .wx-seg button { font-size: 12px; padding: 3px 10px; }
  .wx-more { font-size: 12px; }
}
@container wxcard (min-width: 1120px) {
  .wx-hero { padding: 10px 32px 6px; gap: 26px; }
  .wx-strip { padding: 0 22px 0 32px; gap: 14px; }
  .wx-cell { width: 60px; }
  .wx-desc { font-size: 14px; }
  .wx-meta { font-size: 12.5px; }
}
/* 宽而矮：横向空间足够，主视觉跟着放大一点 */
@container wxcard (min-width: 1120px) and (max-height: 220px) {
  .wx-ico { width: 66px; height: 66px; }
  .wx-temp b { font-size: 44px; }
}
/* 窄卡：先隐湿度风力，再隐节假日标签 */
@container wxcard (max-width: 480px) {
  .wx-meta { display: none; }
}
@container wxcard (max-width: 380px) {
  .wx-hero { gap: 10px; padding-left: 14px; padding-right: 14px; }
  .wx-side { padding-left: 10px; }
  .wx-tags { display: none; }
  .wx-date { font-size: 12px; }
  .wx-lunar { font-size: 11px; }
  .wx-strip { padding: 0 8px 0 12px; gap: 6px; }
  .wx-seg button { font-size: 10.5px; padding: 2px 6px; }
}
/* 2 列以下：右侧日期块移到天气下方，只留关键信息 */
@container wxcard (max-width: 270px) {
  .wx-hero { flex-direction: column; justify-content: center; gap: 2px; padding: 6px 10px; }
  .wx-main { flex: 0 0 auto; justify-content: center; gap: 6px; }
  .wx-side {
    flex: 0 0 auto; flex-direction: row; align-items: baseline; justify-content: center;
    flex-wrap: wrap; gap: 6px;
    padding-left: 0; border-left: 0; text-align: center;
  }
  .wx-date { flex-wrap: wrap; justify-content: center; font-size: 11.5px; }
  .wx-desc, .wx-meta, .wx-lunar, .wx-tags { display: none; }
  .wx-ico { width: 36px; height: 36px; }
  .wx-temp b { font-size: 24px; }
  .wx-clock { font-size: 18px; }
  .wx-plain { flex-direction: column; gap: 2px; padding: 6px 10px; text-align: center; }
  .wx-plain .wx-clock { font-size: 22px; }
  .wx-plain .wx-date { font-size: 12px; }
}
/* 单列：切换按钮也省掉，预报格占满整条；时钟让位给天气和日期 */
@container wxcard (max-width: 170px) {
  .wx-seg, .wx-more { display: none; }
  .wx-cells { justify-content: flex-start; }
  .wx-strip { padding: 0 6px; }
  .wx-hero { gap: 2px; padding: 6px 8px; }
  .wx-main { gap: 4px; }
  .wx-ico { width: 32px; height: 32px; }
  .wx-temp b { font-size: 22px; }
  .wx-temp i { font-size: 12px; margin-top: 3px; }
  .wx-side .wx-clock { display: none; }
}
/* 2 列且高一点：空间够，把主视觉再放大一档 */
@container wxcard (min-width: 180px) and (max-width: 270px) and (min-height: 200px) {
  .wx-ico { width: 52px; height: 52px; }
  .wx-temp b { font-size: 34px; }
  .wx-clock { font-size: 22px; }
}
/* -- 月历翻页 -- */
.cal-title { font-weight: 600; }
.cal-nav-btn {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 999px;
  border: 1px solid rgba(255,255,255,.5); background: rgba(255,255,255,.3);
  color: var(--t2); font-size: 15px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s, color .15s, transform .1s;
}
.cal-nav-btn:hover { background: var(--primary); border-color: var(--primary); color: #fff; }
.cal-nav-btn:active { transform: scale(.92); }
/* -- 今天卡时钟：秒级走字（bindCardClock 每秒重写 HH:MM:SS），字号档位见 6.8 节 -- */
/* -- 导航站点卡：每个网址一张独立卡（无头），整卡可点、主题色徽标，尺寸档位自适应 -- */
.nav-site-card { cursor: pointer; }
.nav-site-card:hover { box-shadow: var(--gl-shadow-hi); }
.nav-site-card > .ns-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  height: 100%;
  padding: 6px 4px;
  text-decoration: none;
  text-align: center;
  overflow: hidden;
}
.ns-badge {
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent, #3b82f6);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .5px;
  box-shadow: 0 4px 10px color-mix(in srgb, var(--accent, #3b82f6) 45%, transparent);
}
.ns-name {
  font-size: 11px;
  line-height: 1.2;
  font-weight: 500;
  color: var(--t1);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ns-url {
  font-size: 10px;
  color: var(--t3);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: none;
}
/* 高一点 / 宽一点时把域名行也放出来 */
.dash-root .dash-grid8 > [data-h="2"] .ns-url,
.dash-root .dash-grid8 > [data-h="3"] .ns-url { display: block; }
.dash-root .dash-grid8 > [data-w="2"] .ns-badge { width: 36px; height: 36px; flex-basis: 36px; font-size: 14px; }
.dash-root .dash-grid8 > [data-w="3"] .ns-badge { width: 42px; height: 42px; flex-basis: 42px; font-size: 15px; }
/* -- 请假登记卡：无头，整卡点击弹窗登记（拖动由 bindDashDrag 的 leavereg-card 分支处理） -- */
.dg-leavereg { cursor: pointer; }
.dg-leavereg .card-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 14px 12px;
  text-align: center;
}
.dg-leavereg .lr-title { font-size: 15px; font-weight: 700; color: var(--t1); }
.dg-leavereg .lr-sub { font-size: 11.5px; color: var(--t3); }
.dg-leavereg:hover { box-shadow: var(--gl-shadow-hi); }
.dg-leavereg .dash-head-ico { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 9px; }
/* 1×1 最小格：只留「图标 + 请假登记」，副标题让位，字号与内边距一起收紧 */
.dg-leavereg[data-w="1"][data-h="1"] .card-body { gap: 3px; padding: 5px 6px; }
.dg-leavereg[data-w="1"][data-h="1"] .dash-head-ico { width: 20px; height: 20px; flex: 0 0 20px; border-radius: 6px; }
.dg-leavereg[data-w="1"][data-h="1"] .dash-head-ico .ico { width: 12px; height: 12px; flex: 0 0 12px; }
.dg-leavereg[data-w="1"][data-h="1"] .lr-title { font-size: 12px; line-height: 1.2; white-space: nowrap; }
.dg-leavereg[data-w="1"][data-h="1"] .lr-sub { display: none; }
.field-grid { display: flex; flex-direction: column; gap: 2px; }

/* ---------------- 请假登记弹窗 ----------------
   两块式表单（学生信息 / 请假时间），字段两列对齐；日期与节次下面各有一条
   实时提示 —— 共几天、每天从哪节到哪节，提交前就能核对清楚。 */
.lv-form { display: flex; flex-direction: column; gap: 14px; }
.lv-hero {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 13px;
  border: 1px solid #FDE7B0;
  border-radius: var(--r-md);
  background: linear-gradient(135deg, #FFF8E8, #FFFDF6);
}
.lv-hero-ico {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: #FEF3C7;
  color: #B45309;
}
.lv-hero-ico svg { width: 18px; height: 18px; }
.lv-hero-txt { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lv-hero-txt b { font-size: 13.5px; color: var(--t1); }
.lv-hero-txt span { font-size: 12px; color: var(--t2); line-height: 1.45; }
.lv-sec {
  padding: 12px 13px 13px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: rgba(250, 253, 251, .72);
}
.lv-sec-title {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 10px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--primary-dark);
}
.lv-sec-title::before {
  content: '';
  width: 3px;
  height: 13px;
  border-radius: 2px;
  background: var(--primary);
}
.lv-grid2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.lv-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.lv-field > span { font-size: 12px; font-weight: 500; color: var(--t2); }
.lv-field .input { height: 36px; padding: 0 10px; font-size: 13.5px; }
.lv-mt { margin-top: 10px; }
.lv-note {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 8px;
  font-size: 12px;
  color: var(--t3);
}
.lv-note b { color: var(--t1); font-weight: 600; }
.lv-chip {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 7px;
  border-radius: 6px;
  background: var(--primary-soft);
  color: var(--primary-dark);
  font-size: 11.5px;
  font-weight: 600;
}
.lv-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border-radius: var(--r-sm);
  background: #F1F7FF;
  color: #1D4ED8;
  font-size: 12.5px;
  line-height: 1.45;
}
.lv-summary svg { width: 15px; height: 15px; flex: 0 0 auto; }
.lv-summary b { font-weight: 700; }
@media (max-width: 520px) {
  .lv-grid2 { grid-template-columns: 1fr; }
}
/* 月历点击某天 → 详情弹窗（当天课程 + 值周）
   排版目标：一眼看清「第几节、几点、什么课、去哪个班」，所以课程按行对齐成三列，
   节次与时间竖排在左，科目居中放大，班级靠右成徽标。 */
.dd-summary {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 12px; margin-bottom: 14px;
  background: var(--primary-soft); border-radius: var(--r-sm);
  font-size: 12.5px; color: var(--t2);
}
.dd-tag {
  padding: 1px 9px; border-radius: 999px; font-size: 11.5px;
  background: #fff; color: var(--primary-dark);
}
.dd-tag.rest { background: #FEF3C7; color: #B45309; }

.dd-sec { margin-top: 16px; }
.dd-sec:first-of-type { margin-top: 0; }
.dd-sec-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.dd-sec-title { font-size: 13px; font-weight: 600; color: var(--t1); }
.dd-sec-meta { font-size: 12px; color: var(--t3); }

.dd-list { display: flex; flex-direction: column; gap: 6px; }
.dd-item {
  display: grid; grid-template-columns: 82px minmax(0, 1fr) auto;
  align-items: center; gap: 12px;
  padding: 8px 12px;
  font-size: 13px; color: var(--t1);
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
}
.dd-when { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.dd-when b { font-size: 12.5px; font-weight: 600; color: var(--t2); }
.dd-when span { font-size: 11.5px; color: var(--t3); font-variant-numeric: tabular-nums; }
.dd-what { min-width: 0; }
.dd-what b { font-size: 14px; font-weight: 600; }
/* 班级徽标：课表里凡是「课」都带班级，免得换课/代课时弄错班 */
.dd-cls {
  padding: 2px 9px; border-radius: 999px; white-space: nowrap;
  font-size: 11.5px; background: var(--primary-soft); color: var(--primary-dark);
}

.dd-duty-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px;
}
.dd-duty-item {
  display: flex; flex-direction: column; gap: 3px;
  padding: 8px 12px; background: #fff; border: 1px solid var(--border); border-radius: 8px;
}
.dd-duty-item span { font-size: 11.5px; color: var(--t3); }
.dd-duty-item b { font-size: 14px; font-weight: 600; color: var(--t1); }

.dd-empty {
  padding: 14px; text-align: center; font-size: 12.5px; color: var(--t3);
  background: #FCFCFD; border: 1px dashed var(--border); border-radius: 8px;
}
/* 窄屏：班级徽标换到科目下面一行，别把科目挤没 */
@media (max-width: 520px) {
  .dd-item { grid-template-columns: 74px minmax(0, 1fr); }
  .dd-cls { grid-column: 2; justify-self: start; }
}
/* 窄屏下每张分类卡自然换行排列 */
@media (max-width: 1280px) {
  .dg-nav .card-body { overflow-y: auto; }
}
/* ---------------- 天气详情弹窗（openWxDetail） ---------------- */
.wxd-hero {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 2px 2px 14px;
  border-bottom: 1px solid var(--border);
}
.wxd-temp {
  font-size: 38px;
  font-weight: 700;
  line-height: 1;
  color: var(--t1);
  font-variant-numeric: tabular-nums;
}
.wxd-temp i { font-style: normal; font-size: 15px; font-weight: 600; color: var(--t2); margin-left: 2px; }
.wxd-desc {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 5px;
  font-size: 14px;
  font-weight: 600;
  color: var(--t1);
}
.wxd-desc span { font-size: 12px; font-weight: 400; color: var(--t3); }
.wxd-meta { margin-top: 3px; font-size: 12px; color: var(--t2); }
.wxd-tip {
  margin-top: 12px;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  background: var(--primary-soft);
  color: var(--primary-dark);
  font-size: 12.5px;
}
.wxd-sec { margin-top: 18px; }
.wxd-sec h4 { margin: 0 0 10px; font-size: 13px; font-weight: 600; color: var(--t1); }
.wxd-scroll { overflow-x: auto; padding-bottom: 2px; }
.wxd-chart { position: relative; }
.wxd-chart svg { display: block; }
.wxd-hours { display: flex; }
.wxd-hour {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding-top: 6px;
}
.wxd-hour .t { font-size: 10.5px; color: var(--t3); white-space: nowrap; }
.wxd-hour .pop { font-size: 9.5px; color: #3B82F6; }
.wxd-days { display: flex; flex-direction: column; }
.wxd-day {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 2px;
  border-top: 1px dashed var(--border);
}
.wxd-day:first-child { border-top: 0; }
.wxd-day-l { flex: 0 0 56px; display: flex; flex-direction: column; line-height: 1.2; }
.wxd-day-l b { font-size: 12.5px; color: var(--t1); }
.wxd-day-l span { font-size: 10.5px; color: var(--t3); }
.wxd-day-t {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  color: var(--t2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wxd-day-lo, .wxd-day-hi {
  flex: 0 0 34px;
  text-align: right;
  font-size: 12px;
  color: var(--t2);
  font-variant-numeric: tabular-nums;
}
.wxd-day-hi { color: var(--t1); font-weight: 600; }
.wxd-bar {
  flex: 0 0 110px;
  height: 5px;
  border-radius: 999px;
  background: #EEF1F5;
  position: relative;
  overflow: hidden;
}
.wxd-bar i {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, #60A5FA, #FBBF24);
}
.wxd-day-w {
  flex: 0 0 78px;
  text-align: right;
  font-size: 11px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wxd-src { margin-top: 14px; font-size: 11px; color: var(--t3); text-align: right; }
@media (max-width: 520px) {
  .wxd-day-w { display: none; }
  .wxd-bar { flex-basis: 70px; }
  .wxd-hero { gap: 12px; }
  .wxd-temp { font-size: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  .wx-ico *,
  .wx-sky i,
  .wx-sky i::after,
  .wx-ico-box { animation: none !important; }
}

/* ---------------- 7. 窄屏适配 ----------------
   ≤1280px 网格退成两列流式、行高交回内容（见前面那个断点），
   壁纸继续全屏铺没有影响；这里只把搜索栏在窄屏下改成上下两行更好点。 */
@media (max-width: 1280px) {
  .dash-search { padding: 10px; }
  .ds-picker { flex: 0 0 auto; }
  .ds-field { flex: 1 1 100%; max-width: none; }
  .dash-bg-credit { display: none; }
}

/* ---------------- 8. 无障碍：关了动效就全停 ---------------- */
@media (prefers-reduced-motion: reduce) {
  .dash-root .dash-grid8 > *,
  .dash-root .dash-grid8 > [data-dg]::after,
  .dash-bg.is-ready .dash-bg-img,
  .dash-bg-img,
  .ds-go { animation: none !important; transition: none !important; }
  .dash-root .dash-grid8 > [data-dg]:hover { transform: none; }
  .dash-root .dash-grid8 > [data-dg]::after { opacity: 0 !important; }
}
/* 触屏没有 hover，高光那套别留着占一层合成 */
@media (hover: none) {
  .dash-root .dash-grid8 > [data-dg]::after { display: none; }
}

/* ================================================================
   9. 账号级自定义首页 + 状态条反白 + 弹窗升级
   ================================================================ */

/* ---------- 9.1 被本人藏起来的卡：留在 DOM 里（面板还要列它），但不显示、不占格 ---------- */
.dash-root .dash-grid8 > [data-dg].dg-off { display: none !important; }

/* ---------- 9.2 状态条：壁纸偏暗，文字统一反白 ----------
   原来用深色字 + 白色投影，遇到浅色壁纸还能看；但壁纸顶部经常是蓝天/深色，
   深色字直接糊成一片。统一改白字 + 深色投影，任何壁纸下都成立。 */
.dash-root .dash-statusbar { text-shadow: none; }
.dash-root .ds-greet {
  color: #fff;
  font-weight: 700;
  letter-spacing: .2px;
  text-shadow: 0 1px 2px rgba(8, 20, 14, .45), 0 4px 16px rgba(8, 20, 14, .35);
}
.dash-root .ds-date {
  color: rgba(255, 255, 255, .88);
  text-shadow: 0 1px 2px rgba(8, 20, 14, .45);
}
.dash-root .ds-clock {
  color: #fff;
  font-weight: 700;
  text-shadow: 0 1px 2px rgba(8, 20, 14, .45);
}
.dash-root .ds-weather {
  color: rgba(255, 255, 255, .94);
  text-shadow: 0 1px 2px rgba(8, 20, 14, .45);
}
.dash-root .ds-weather .ico { color: #FDE68A; }
.dash-root .ds-weather em { color: #FDE68A; }
.dash-root .ds-badges .badge {
  box-shadow: 0 1px 3px rgba(8, 20, 14, .22);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

/* 状态条右侧的「自定义首页」：玻璃描边按钮，不抢问候语的视觉重心 */
.btn.btn-glass {
  color: #fff;
  background: rgba(255, 255, 255, .16);
  border-color: rgba(255, 255, 255, .44);
  -webkit-backdrop-filter: blur(8px) saturate(1.15);
  backdrop-filter: blur(8px) saturate(1.15);
  box-shadow: 0 1px 3px rgba(8, 20, 14, .18);
  transition: background .16s, border-color .16s, transform .16s, box-shadow .16s;
}
.btn.btn-glass:hover {
  background: rgba(255, 255, 255, .3);
  border-color: rgba(255, 255, 255, .72);
  box-shadow: 0 3px 10px rgba(8, 20, 14, .22);
}
.btn.btn-glass:active { transform: translateY(1px); }
.btn.btn-glass .ico { width: 14px; height: 14px; flex: 0 0 14px; }

/* ---------- 9.3 「自定义首页」面板 ---------- */
.dash-cust { display: flex; flex-direction: column; gap: 18px; }
.dash-cust-tip {
  margin: 0;
  padding: 10px 13px;
  border-radius: var(--r-md);
  background: linear-gradient(135deg, #EAF9F1, #F7FCF9);
  border: 1px solid #D7F0E4;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--t2);
}
.dash-cust-tip b { color: var(--primary-dark); font-weight: 600; }
.dash-cust-group { display: flex; flex-direction: column; gap: 8px; }
.dash-cust-ghead {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--t3);
}
.dash-cust-ghead::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: linear-gradient(90deg, var(--border), transparent);
}
.dash-cust-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(246px, 1fr));
  gap: 8px;
}
.dash-cust-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: #fff;
  cursor: pointer;
  transition: border-color .15s, background .15s, box-shadow .15s;
}
.dash-cust-item:hover {
  border-color: #B7E7CE;
  background: #FAFEFB;
  box-shadow: 0 2px 10px rgba(16, 185, 129, .1);
}
.dash-cust-txt { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.dash-cust-name { font-size: 13px; font-weight: 600; color: var(--t1); }
.dash-cust-desc {
  font-size: 11.5px;
  color: var(--t3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 原生 checkbox 藏起来，用 .dash-cust-sw 画开关；保留键盘可达与 :focus-visible */
.dash-cust-cb {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.dash-cust-sw {
  position: relative;
  flex: 0 0 36px;
  width: 36px;
  height: 20px;
  margin-left: auto;
  border-radius: 999px;
  background: #E2E8F0;
  transition: background .18s;
}
.dash-cust-sw::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(15, 23, 42, .24);
  transition: transform .18s;
}
.dash-cust-cb:checked + .dash-cust-sw { background: var(--primary); }
.dash-cust-cb:checked + .dash-cust-sw::after { transform: translateX(16px); }
.dash-cust-cb:focus-visible + .dash-cust-sw { box-shadow: 0 0 0 3px var(--primary-soft); }
.dash-cust-blocked {
  padding: 10px 13px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
  background: #FAFBFC;
  font-size: 12px;
  line-height: 1.7;
  color: var(--t3);
}

/* 面板底栏：恢复默认靠左，取消/保存靠右 */
.modal-gap { flex: 1 1 auto; }
.btn-danger-ghost.on {
  color: #fff;
  background: var(--red);
  border-color: var(--red);
}
.btn-danger-ghost.on:hover { background: #DC2626; }

/* ---------- 9.4 全站弹窗：更清晰的层级、动效与移动端 ---------- */
body.modal-open { overflow: hidden; }
.modal-mask {
  background: rgba(9, 16, 28, .48);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  animation: maskIn .18s ease-out;
}
.modal-mask.is-closing { animation: maskOut .16s ease-in forwards; }
@keyframes maskOut { to { opacity: 0; } }

.modal {
  border: 1px solid rgba(255, 255, 255, .65);
  outline: none;
  box-shadow: 0 24px 64px -16px rgba(8, 20, 14, .4), 0 2px 8px rgba(8, 20, 14, .12);
  overflow: hidden;
}
.modal-mask.is-closing .modal { animation: modalOut .16s ease-in forwards; }
@keyframes modalOut { to { opacity: 0; transform: translateY(6px) scale(.985); } }

.modal-head {
  padding: 15px 20px;
  background: linear-gradient(180deg, #FCFEFD, #F6FBF8);
}
.modal-title {
  position: relative;
  padding-left: 12px;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--t1);
}
.modal-title::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 3px;
  height: 16px;
  margin-top: -8px;
  border-radius: 999px;
  background: var(--primary);
}
.modal-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  font-size: 13px;
  border-radius: 8px;
  transition: background .15s, color .15s, transform .15s;
}
.modal-x:hover { background: #FEE2E2; color: #B91C1C; transform: rotate(90deg); }
.modal-body { padding: 20px; font-size: 14px; line-height: 1.65; }
.modal-foot {
  padding: 14px 20px;
  background: #FCFDFD;
  flex-wrap: wrap;
}
.modal-sm { max-width: 440px; }
.modal-lg { max-width: 780px; }

@media (max-width: 640px) {
  .modal-mask { padding: 10px; align-items: flex-end; }
  .modal {
    max-height: 92vh;
    border-radius: 16px 16px 0 0;
  }
  .modal-head { padding: 14px 16px; }
  .modal-body { padding: 16px; }
  .modal-foot { padding: 12px 16px; }
  .modal-foot .btn { flex: 1 1 auto; }
  .modal-gap { display: none; }
  .dash-cust-list { grid-template-columns: 1fr; }
}

/* ---------- 9.5 卡片细节：逐卡统一留白、空态与重点数字 ---------- */
/* 卡头图标与标题基线对齐，避免不同卡片标题上下错一像素 */
.dash-root .dash-grid8 .card-head > .dash-head-ico { align-self: center; }
/* 卡片正文首尾元素不留多余外边距，卡与卡之间视觉间距才一致 */
.dash-root .dash-grid8 .card-body > :first-child { margin-top: 0; }
.dash-root .dash-grid8 .card-body > :last-child { margin-bottom: 0; }
/* 空态：虚线描边 + 居中，比一行灰字更像「这里本来就没内容」 */
.dash-root .dash-grid8 .empty {
  min-height: 56px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px;
  border: 1px dashed rgba(100, 116, 139, .3);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .35);
  font-size: 12.5px;
  color: var(--t3);
}
.dash-root .dash-grid8 .empty .ico { width: 20px; height: 20px; opacity: .55; }
/* 统计卡：数字与脚注的层次再拉开一点 */
.dash-root .dash-stat-solo .dash-stat-value { letter-spacing: -.02em; }
.dash-root .dash-stat-solo .dash-stat-foot { color: var(--t3); }
/* 统计卡按用途给一点色彩倾向（只染数值，不整卡上色，避免四张卡各唱各的） */
.dash-root [data-dg="devices"] .dash-stat-value { color: #0F766E; }
.dash-root [data-dg="notifstat"] .dash-stat-value { color: #1D4ED8; }
.dash-root [data-dg="checkstat"] .dash-stat-value { color: #B45309; }
.dash-root [data-dg="leavestat"] .dash-stat-value { color: #7C3AED; }
/* 导航卡：徽标轻微上浮 + 名称两行内截断 */
.dash-root .nav-site-card .ns-badge { transition: transform .18s ease; }
.dash-root .nav-site-card:hover .ns-badge { transform: translateY(-1px) scale(1.05); }
.dash-root .nav-site-card .ns-name { line-height: 1.25; }
