:root {
  --bg: #f5f6f8;
  --panel: #ffffff;
  --line: #e3e5ea;
  --line-strong: #c9ccd4;
  --text: #1d1f24;
  --muted: #6b7080;
  --accent: #2f6fd0;
  --accent-soft: #e8f0fc;
  --ok: #1d9e75;
  --warn: #b5761a;
  --danger: #c0392b;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(20, 24, 35, .06), 0 8px 24px rgba(20, 24, 35, .06);

  /* ---------- 课表列宽（z71）：四个工作表统一口径 ----------
     缘起：以前只写 min-width，而 .tt 是 border-collapse:separate，
     浏览器走「自动表格布局」，列宽由各列内容自己分配 —— 同一份数据在
     不同表/不同日期里宽度都不一样（实测教室列 80/97/100/116/120/143 五花八门）。
     现在改成 table-layout:fixed + 显式列宽变量，四表列宽才真正对齐。
     ⚠ 改这里的数值要同步看 tests/z71-test.js 的断言。 */
  --slot-w: 95px;      /* 时段列（时间）—— 实际值由 app.js computeSlotColWidth() 按内容最大值补写 */
  --room-w: 116px;     /* 教室列 */
  --pt-day-w: 140px;   /* 兼职课表：日期列 */
  --pt-slot-w: 93px;   /* 兼职课表：时段列 */
  --sticky-top: 0px;   /* 表头吸顶偏移（见下方 .tt thead th） */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.55 system-ui, -apple-system, "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
}

body { min-height: 100vh; }
.hidden { display: none !important; }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.spacer { flex: 1 1 auto; }
.err { color: var(--danger); font-size: 12px; min-height: 16px; margin: 6px 0 0; }

/* ---------- 登录 ---------- */
.login {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
}
.login-card {
  width: 100%;
  max-width: 360px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 28px;
  box-shadow: var(--shadow);
}
.login-card h1 { margin: 0 0 4px; font-size: 20px; font-weight: 600; }
.login-card .sub { margin: 0 0 20px; color: var(--muted); font-size: 13px; }
.login-card label { display: block; margin-bottom: 14px; font-size: 13px; color: var(--muted); }
.login-card input {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 9px 11px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font-size: 14px;
  color: var(--text);
  background: #fff;
}
.login-card input:focus { outline: 2px solid var(--accent-soft); border-color: var(--accent); }
.login-card .pwd-row { position: relative; display: block; margin-top: 6px; }
.login-card .pwd-row input { margin-top: 0; padding-right: 64px; }
.pwd-toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  padding: 4px 9px; font-size: 12px; line-height: 1.2; color: var(--muted);
  border: 1px solid var(--line); background: #fff; border-radius: 6px;
}
.pwd-toggle:hover { background: #f0f1f4; }
.pwd-toggle.on { color: var(--accent); border-color: var(--accent); }

button {
  font: inherit;
  cursor: pointer;
  border-radius: 8px;
  border: 1px solid var(--line-strong);
  background: #fff;
  color: var(--text);
  padding: 6px 12px;
  transition: background .12s, border-color .12s;
}
button:hover { background: #f0f1f4; }
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.primary:hover { background: #2760b8; }
button.ghost { background: #fff; }
button:disabled { opacity: .45; cursor: not-allowed; }

/* ---------- 顶栏 ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 18px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand .logo {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  background: var(--accent); color: #fff;
  border-radius: 9px; font-weight: 600;
}
.brand strong { display: block; font-size: 15px; }
.brand .small { display: block; }

.tabs { display: flex; gap: 2px; }
.tabs button {
  border: 0; background: transparent; color: var(--muted);
  padding: 7px 12px; border-radius: 8px;
}
.tabs button:hover { background: #f0f1f4; color: var(--text); }
.tabs button.active { background: var(--accent-soft); color: var(--accent); font-weight: 500; }

.userbox { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.banner { padding: 8px 18px; background: #fff7e6; border-bottom: 1px solid #f0dcae; color: var(--warn); font-size: 13px; }

main { padding: 14px 18px 40px; }
.view { display: block; }

/* ---------- 工具栏 ---------- */
.toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.search {
  padding: 6px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font: inherit;
  min-width: 220px;
  background: #fff;
}
select {
  padding: 6px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  font: inherit;
  background: #fff;
  max-width: 240px;
}
.chk, .inline { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; color: var(--muted); }
.inline select { margin-left: 4px; }
/* 个人课表：姓名改为可输入 + 下拉建议 */
#mine-name-wrap { position: relative; }
#mine-name-wrap #mine-name {
  width: 200px; padding: 5px 8px; font: inherit; font-size: 13px;
  border: 1px solid var(--line-strong); border-radius: 8px 0 0 8px; outline: none;
  background: #fff; color: var(--fg);
}
#mine-name-wrap #mine-name:focus { border-color: var(--accent); }
#mine-name-wrap #mine-name::placeholder { color: #b3b9c2; }
#mine-hint:empty { display: none; }

/* 姓名「输入 + 展开按钮」组合：
   原生 <datalist> 只在**输入了字符之后**才给建议，点它自带的箭头不会展开全部候选，
   老师会以为「没有下拉」。这里补一个真正的按钮，点一下列出全部可选姓名。 */
#mine-name-wrap .combo { display: inline-flex; align-items: center; margin-left: 4px; position: relative; }
#mine-name-wrap .combo-btn {
  height: 30px; width: 28px; padding: 0; cursor: pointer;
  border: 1px solid var(--line-strong); border-left: 0; border-radius: 0 8px 8px 0;
  background: #f6f7f9; color: var(--muted); font-size: 12px; line-height: 1;
}
#mine-name-wrap .combo-btn:hover { background: #eceef2; color: var(--fg); }
#mine-name-wrap .combo.open .combo-btn { background: #e8eaee; }
#mine-name-wrap .combo-menu {
  position: absolute; z-index: 40; top: 34px; left: 0; min-width: 200px; max-height: 300px;
  overflow-y: auto; background: #fff; border: 1px solid var(--line-strong); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.12); padding: 4px;
}
#mine-name-wrap .combo-menu .combo-item {
  padding: 6px 9px; border-radius: 6px; font-size: 13px; cursor: pointer;
  white-space: nowrap; color: var(--fg);
}
#mine-name-wrap .combo-menu .combo-item:hover,
#mine-name-wrap .combo-menu .combo-item.active { background: #eef6f1; color: var(--accent); }
#mine-name-wrap .combo-menu .combo-empty { padding: 8px 9px; font-size: 12px; color: var(--muted); }

.day-tabs { display: flex; gap: 2px; background: #eceef2; padding: 3px; border-radius: 9px; }
.day-tabs button { border: 0; background: transparent; padding: 5px 12px; border-radius: 7px; color: var(--muted); }
.day-tabs button.active { background: #fff; color: var(--accent); font-weight: 500; box-shadow: 0 1px 2px rgba(0,0,0,.06); }

/* z78：自选日期模式下的「已勾选」日期标签 —— 和单日 active（白底）明显区分：
   蓝底白字 + 前缀对勾，一眼看出这几天会被并排显示。 */
.day-tabs button.picked {
  background: var(--accent, #2b5fd9); color: #fff; font-weight: 500;
  box-shadow: 0 1px 3px rgba(43, 95, 217, .35);
}
.day-tabs button.picked::before { content: '✓ '; font-weight: 700; }

.legend { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.legend .lg {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; padding: 2px 8px; border-radius: 20px;
  border: 1px solid var(--line); background: #fff; color: var(--muted); cursor: pointer;
}
.legend .lg.off { opacity: .4; }
.legend .lg i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }

/* z77：管理员可点的改色圆点。原来的 9px 太小、手指点不准，
   这里放大到 14px 并留出点击热区，hover/聚焦都有反馈，让人看出「这能点」。 */
.legend .lg i.lg-pick {
  width: 14px; height: 14px; cursor: pointer;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .14);
  transition: transform .12s ease, box-shadow .12s ease;
}
.legend .lg i.lg-pick:hover {
  transform: scale(1.25);
  box-shadow: 0 0 0 2px rgba(43, 95, 217, .55);
}
.legend .lg i.lg-pick:active { transform: scale(1.05); }

/* z77：老师取色板 */
.cp-card { width: 320px; }
.cp-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px;
  margin: 12px 0 6px;
}
.cp-sw {
  height: 40px; border: 1px solid rgba(0, 0, 0, .14); border-radius: 8px;
  cursor: pointer; padding: 0; transition: transform .1s ease, box-shadow .1s ease;
}
.cp-sw:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0, 0, 0, .16); }
.cp-sw.on {
  box-shadow: 0 0 0 3px var(--accent, #2b5fd9);
  border-color: transparent;
}
@media (max-width: 520px) {
  .cp-card { width: calc(100vw - 32px); }
  .cp-grid { grid-template-columns: repeat(4, 1fr); }
  .cp-sw { height: 46px; }
}

.hint { color: var(--muted); font-size: 12px; margin: 10px 0 0; }

/* ---------- 课表网格 ---------- */
.grid-scroll { overflow: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }

/* z71：固定表格布局 —— 列宽只认我们声明的值，不再被格子内容牵着走。
   这样「课表总表 / 课表查询 / 个人课表查询 / 兼职老师课表」四张表的教室列才会一样宽。 */
.tt { border-collapse: separate; border-spacing: 0; font-size: 12.5px; table-layout: fixed; min-width: 0; }
.tt th, .tt td { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); vertical-align: top; padding: 0; }

.tt thead th {
  /* top 用变量：桌面端容器内部滚动时贴在容器顶，不被 .topbar 压住 */
  position: sticky; top: var(--sticky-top); z-index: 3;
  background: #f0f2f6;
  padding: 7px 8px;
  text-align: center;
  font-weight: 500;
  width: var(--room-w);      /* z71：min-width → width（fixed 布局下 min-width 不再起决定作用） */
  min-width: 0;
  border-bottom: 1px solid var(--line-strong);
}
.tt thead th .room { font-size: 13px; }
.tt thead th .meta { display: block; font-size: 11px; color: var(--muted); font-weight: 400; }
.tt thead th .addr { display: block; font-size: 11px; color: #9aa0ae; font-weight: 400; }

.tt th.slot, .tt td.slot {
  position: sticky; left: 0; z-index: 2;
  background: #f7f8fa;
  width: var(--slot-w);
  min-width: 0;
  padding: 8px;
  font-weight: 500;
  color: var(--muted);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;   /* 时段文案超宽时裁掉，别把固定列宽顶开 */
  text-overflow: ellipsis;
}
/* 左上角那格同时吸顶又吸左，z-index 必须压过普通表头(3)与时段列(2) */
.tt thead th.corner { left: 0; z-index: 6; text-align: left; width: var(--slot-w); min-width: 0; }

.tt td.cell {
  /* z99：行高恒定 —— 基准行高取「两行班级 + 一行教师」的高度（约 66px）。
     之前是 46px，内容换行后被撑到 66px，同事增删内容时行高 46↔66 来回切，
     整表上下跳动。统一成 66px 后，常规内容增删行高不再变化。 */
  height: 66px;
  width: var(--room-w);
  min-width: 0;
  padding: 4px 7px;
  cursor: pointer;
  transition: box-shadow .1s;
  position: relative;
}
.tt td.cell:hover { box-shadow: inset 0 0 0 2px var(--accent); }
.tt td.cell .c { display: block; font-weight: 500; color: #1d1f24; word-break: break-all; }
.tt td.cell .t { display: block; font-size: 11px; color: #5a6070; margin-top: 1px; }
/* z126：教师行里的助教。与教师名同一行、字号更小、前面一个「助」小标签 ——
   一眼能看出「这是另一个人」，又不至于被当成第二位任课老师。
   用 inline（不是 block）是关键：窄列时浏览器自己折行，宽列时不额外占一行高度。
   ⚠ 不要用 border-left 做分隔：助教名一长就折行，竖线跑到行首像列表符号。
   小标签跟着名字走，折到哪一行都成立。背景用半透明白，压在任何教师底色上都不突兀。 */
.tt td.cell .t .ta {
  font-style: normal;
  font-size: 10.5px;
  color: #6f7686;
  margin-left: 4px;
  word-break: break-all;
}
.tt td.cell .t .ta b {
  font-weight: 600;
  font-size: 9.5px;
  color: #5b6474;
  background: rgba(255, 255, 255, .72);
  border-radius: 3px;
  padding: 0 3px;
  margin-right: 3px;
}
.tt td.cell.empty .c { color: #c3c6ce; font-weight: 400; }
.tt td.cell.dim { opacity: .22; }
.tt td.cell.hit { box-shadow: inset 0 0 0 2px #f0b429; }
.tt td.cell.flash { animation: flash .9s ease-out; }
@keyframes flash { 0% { background: #fff3c4 !important; } 100% { background: transparent; } }

/* ---------- 搬移 / 交换 ---------- */
.tt td.cell.swap-src {
  box-shadow: inset 0 0 0 3px #f59e0b;
  background-image: linear-gradient(rgba(245, 158, 11, .12), rgba(245, 158, 11, .12));
}
.tt td.cell.swap-src::after {
  content: '源';
  position: absolute;
  top: 1px;
  right: 2px;
  font-size: 10px;
  line-height: 1.35;
  background: #f59e0b;
  color: #fff;
  padding: 0 3px;
  border-radius: 3px;
  pointer-events: none;
}

/* ---------- 选中态 + 右下角「编辑」按钮 ----------
   单击格子后：td 加 .selected（蓝边）+ 右下角显示「编辑」按钮（hover 即显，强制显示当 .selected）
   再点同一格不会取消失（用户决策）；切换格子 / Esc / 点空白 = 取消 */
.tt td.cell.selected { box-shadow: inset 0 0 0 2px var(--accent, #4f7cff); background: rgba(79,124,255,.04); }
.tt td.cell.selected.empty .c { color: #1d1f24; }
.tt td.cell .cell-edit-btn {
  position: absolute;
  right: 3px; bottom: 2px;
  font-size: 11px; line-height: 1.2;
  padding: 2px 7px;
  border: 1px solid var(--line-strong, #d4d7de);
  border-radius: 4px;
  background: var(--panel, #fff);
  color: var(--text, #1d1f24);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s;
  z-index: 4;
  user-select: none;
}
.tt td.cell:hover .cell-edit-btn,
.tt td.cell.selected .cell-edit-btn { opacity: 1; }
.tt td.cell .cell-edit-btn:hover {
  background: var(--accent-soft, #e8efff);
  border-color: var(--accent, #4f7cff);
}

/* ---------- z93：单元格批注 ----------
   角标：右上角一枚小 📝（::before，避开 stu-conflict 的 ::after 红点）。
   悬停看全文（title 由 app.js 拼好）。查询等只读视图同样显示。 */
.tt td.cell.has-note::before {
  content: '📝';
  position: absolute;
  top: 1px; right: 3px;
  font-size: 11px;
  line-height: 1;
  opacity: .9;
  pointer-events: none;
  filter: saturate(.85);
}
.tt td.cell.has-note {
  /* 右上角留出角标位置：内容过长时不至于顶到图标下面（字号小，2px 即够） */
  --note-pad: 0;
}
/* 批注按钮：与「编辑」同款，摆在它左边 */
.tt td.cell .cell-note-btn {
  position: absolute;
  right: 46px; bottom: 2px;
  font-size: 11px; line-height: 1.2;
  padding: 2px 7px;
  border: 1px solid #e3cf8f;
  border-radius: 4px;
  background: #fffaf0;
  color: #8a6d1f;
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s;
  z-index: 4;
  user-select: none;
}
.tt td.cell:hover .cell-note-btn,
.tt td.cell.selected .cell-note-btn { opacity: 1; }
.tt td.cell .cell-note-btn:hover { background: #fdf3d7; border-color: #d9bd6a; }

.swap-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 10px 0 0;
  padding: 8px 12px;
  background: #fff8e6;
  border: 1px solid #f0d089;
  border-radius: 8px;
  font-size: 12.5px;
  color: #7a5610;
}
.swap-bar b { color: #8a5a00; }

.toast-btn {
  margin-left: 8px;
  padding: 1px 9px;
  font-size: 12px;
  font-family: inherit;
  background: #fff;
  border: 1px solid currentColor;
  border-radius: 4px;
  color: inherit;
  cursor: pointer;
}

.tt tr.break-row td { background: #fafbfc; color: #a8adba; text-align: center; font-size: 11.5px; padding: 4px; }
/* 日期分隔带：整周「一张表」模式下会有十几条连着出现，做成明显的横线 + 浅底日期名 */
.tt tr.daysep td, .tt tr.daysep th {
  background: #eef0f4;
  font-weight: 500;
  color: var(--text);
  padding: 7px 10px;
  text-align: left;
  position: sticky; left: 0;
}
/* 日期分隔带：每个日期开头一条加粗横线 + 浅底日期名。
   用 row 上的 box-shadow 画粗线，不受 sticky 表头 / collapse 边框的影响。 */
.tt tr.daysep td {
  border-bottom: 1px solid #dfe3e8;
  padding: 6px 10px;
  text-align: center;
  background: linear-gradient(#eef4ec, #e4ece1);
  box-shadow: inset 0 3px 0 0 #9fbb9c;
}
.tt tr.daysep .daysep-name { font-weight: 700; font-size: 13px; color: #1f5a35; letter-spacing: .4px; }
.tt tr.daysep .daysep-term { font-size: 12px; color: var(--muted); margin-left: 8px; }
/* 整周一张表：教室表头允许换行（教室名 + 容纳人数/地址各占一行），列不会被撑得很宽 */
/* ⚠ z71：删掉 min-width:96px / max-width:130px —— 那是「自动布局」时代的补丁，
   现在 table-layout:fixed 下列宽统一由 --room-w 决定，这两个值只会与它打架。 */
.tt.wrap-head thead th { white-space: normal; padding: 6px 6px; }
.tt.wrap-head thead th .meta, .tt.wrap-head thead th .addr { display: block; margin: 0; }

/* ---------- 单元格编辑浮层（z69：贴着被编辑格子的右上方） ----------
   旧方案是 #edit-drawer 从右侧滑出的 320px 抽屉，会把课表挤窄一截；
   现在改成绝对定位的小浮层，坐标由 app.js 的 positionEditor(td) 算好后写进 style.left/top。
   定位上下文仍然是 #view-schedule（position:relative）—— 这样浮层坐标系与 .grid-scroll 的
   滚动偏移天然一致，不需要额外补偿。 */
#view-schedule { position: relative; }                     /* 浮层 absolute 定位上下文（避免影响其他视图） */

/* 未定位前的兜底，避免首帧闪到左上角 */
#editor.popover { left: 0; top: 0; }
/* 最右侧/最上行放不下时，app.js 会加这两个类把浮层翻到左侧 / 下侧。
   这里只声明「翻边后箭头类的小调整」位置，真正的 left/top 仍由 JS 内联样式决定。 */
#editor.popover { transition: opacity .12s ease; }
#editor.popover.hidden { display: none; }                  /* 二次兜底 */

.popover {
  position: absolute;
  z-index: 60;
  width: 268px;
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: 14px;
}
.pop-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; font-size: 13px; font-weight: 500; }
.popover label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 9px; }
.popover input {
  display: block; width: 100%; margin-top: 4px;
  padding: 7px 9px; border: 1px solid var(--line-strong);
  border-radius: 7px; font: inherit; font-size: 13px; background: #fff; color: var(--text);
}
.popover input:focus { outline: 2px solid var(--accent-soft); border-color: var(--accent); }
.pop-actions { display: flex; gap: 6px; margin-top: 12px; }
/* z126：编辑面板多了「助教」一行，管理员还会多出「删除全表相同」——
   四个按钮塞在 268px 宽里，每个只剩 40 多像素，文字被挤成竖排（「保/存」一列一个字）。
   改成 3 列网格：保存 / 清除 / 取消 一行，「删除全表相同」独占一行。
   破坏性操作单独占一行反而更安全，不容易顺手点到。 */
#editor .pop-actions { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
#editor .pop-actions #ed-del-same { grid-column: 1 / -1; order: 2; }
#editor .pop-actions #ed-save,
#editor .pop-actions #ed-clear,
#editor .pop-actions #ed-cancel { order: 1; white-space: nowrap; }

.modal { position: fixed; inset: 0; background: rgba(20,24,35,.34); display: grid; place-items: center; z-index: 80; }
.modal-card { width: 340px; background: #fff; border-radius: 14px; padding: 22px; box-shadow: var(--shadow); }
.modal-card h2 { margin: 0 0 4px; font-size: 16px; font-weight: 600; }
.modal-card label { display: block; font-size: 12px; color: var(--muted); margin: 12px 0 0; }
.modal-card input { display: block; width: 100%; margin-top: 4px; padding: 8px 10px; border: 1px solid var(--line-strong); border-radius: 8px; font: inherit; }

/* ---------- 使用说明书弹窗 ----------
   弹窗本身占满大半屏，内嵌 iframe 直接渲染说明书 HTML（自带样式，自包含无外链）。
   这样不必离开当前页面，也能随时下载或另开标签页细看 / 打印。 */
#manual-modal { place-items: stretch; padding: 24px; background: rgba(20,24,35,.55); }
.manual-card {
  width: 100%; max-width: 1080px; margin: auto;
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  height: min(880px, calc(100vh - 48px));
}
.manual-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px 18px; border-bottom: 1px solid var(--line); background: #fff;
}
.manual-head h2 { margin: 0; font-size: 16px; font-weight: 600; white-space: nowrap; }
/* 角色对应多份时才出现页签；只有一份时整条隐藏 */
.manual-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.manual-tabs:empty { display: none; }
.manual-tab {
  padding: 5px 13px; font-size: 13px; font-family: inherit; cursor: pointer;
  border: 1px solid var(--line); background: #fff; color: var(--muted); border-radius: 999px;
  transition: background .12s, color .12s, border-color .12s;
}
.manual-tab:hover { background: #f2f5f3; color: var(--fg); }
.manual-tab.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.manual-acts { margin-left: auto; display: flex; gap: 6px; align-items: center; }
/* 下载 / 新标签页 用 <a> 承担按钮外观，去掉下划线并对齐 .ghost */
.manual-acts a.ghost {
  display: inline-flex; align-items: center; text-decoration: none;
  padding: 4px 10px; font-size: 12.5px; border-radius: 8px; line-height: 1.6;
}
.manual-body { flex: 1 1 auto; min-height: 0; background: #f7f8fa; }
.manual-body iframe { display: block; width: 100%; height: 100%; border: 0; background: #fff; }
@media (max-width: 640px) {
  #manual-modal { padding: 8px; }
  .manual-card { height: calc(100vh - 16px); }
  .manual-head { padding: 10px 12px; gap: 8px; }
  .manual-head h2 { font-size: 15px; }
  .manual-acts { margin-left: 0; width: 100%; }
}


/* ---------- 分享卡 ---------- */
.share-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 12px;
  display: flex;
  gap: 18px;
  align-items: center;
  flex-wrap: wrap;
}
.share-card .qr { width: 108px; height: 108px; flex: 0 0 auto; }
.share-card .qr img, .share-card .qr canvas { width: 108px; height: 108px; }
.share-card .info { flex: 1 1 320px; min-width: 260px; }
.share-card .link {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  background: #f4f5f8;
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 7px 9px;
  word-break: break-all;
  margin: 6px 0 10px;
}
.share-card .msg { font-size: 12.5px; color: var(--muted); }
.share-card .msg textarea {
  width: 100%; margin-top: 4px; min-height: 62px; resize: vertical;
  font: inherit; font-size: 12.5px; padding: 8px 10px;
  border: 1px solid var(--line-strong); border-radius: 8px; background: #fff; color: var(--text);
}

/* ---------- z71：课表分享弹窗（原来是常驻卡片，现在点了「分享」才出现） ---------- */
.share-modal-card { width: 640px; max-width: 92vw; }
.share-modal-head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
}
.share-modal-head h2 { margin: 0; font-size: 16px; flex: 1 1 auto; }
.share-modal-body { max-height: calc(100vh - 200px); overflow-y: auto; }
/* 弹窗里的分享卡不再需要底部外边距（弹窗自己有 padding） */
.share-modal-body .share-card { margin-bottom: 0; }

/* ---------- z71：时段列宽探针 ----------
   藏在屏幕外量文字宽度用（见 app.js computeSlotColWidth）。
   ⚠ 字体/内边距必须与 .tt th.slot 完全一致，量出来的值才等于真实列宽；
     窄屏下 .slot 的字号也变了，所以这里要跟着媒体查询一起改。 */
.tt-slot-probe {
  font: 500 12.5px/1.55 system-ui, -apple-system, "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
  padding: 8px;
  white-space: nowrap;
}
@media (max-width: 640px) {
  .tt-slot-probe { font-size: 11.5px; padding: 5px 4px; }
}

/* ---------- 通用表格 ---------- */
table.data { border-collapse: collapse; width: 100%; font-size: 13px; }
table.data th, table.data td { border: 1px solid var(--line); padding: 6px 9px; text-align: left; }
table.data thead th { background: #f0f2f6; position: sticky; top: 0; z-index: 2; font-weight: 500; }
table.data tbody tr:nth-child(even) { background: #fafbfc; }
table.data td.num { text-align: right; font-variant-numeric: tabular-nums; }

.cal-table { table-layout: auto; font-size: 12.5px; }
.cal-table th, .cal-table td { white-space: nowrap; }
.cal-table thead th { font-size: 11.5px; padding: 6px 7px; text-align: center; font-weight: 500; }
.cal-table tbody td { padding: 5px 7px; }
.cal-table tbody td:first-child { font-variant-numeric: tabular-nums; }
.cal-table td.mark { text-align: center; cursor: pointer; user-select: none; width: 46px; min-width: 46px; }
.cal-table td.mark:hover { background: var(--accent-soft); }
.cal-table td.mark.on { background: #dff3ea; color: #0f6e56; font-weight: 600; }
.cal-table td.mark.locked { cursor: default; }
/* z115：批量打标后把目标列滚到眼前并闪一下 —— 表格几十列，不闪用户看不出刚刚改的是哪一列 */
.cal-table td.mark.cal-mark-flash {
  animation: cal-mark-flash 1.8s ease-out;
}
.cal-table th.cal-tag-th.cal-tag-flash {
  animation: cal-tag-flash 1.8s ease-out;
}
@keyframes cal-mark-flash {
  0%, 55% { background: #ffe58a; box-shadow: inset 0 0 0 2px #f0a500; }
  100% { background: inherit; box-shadow: none; }
}
@keyframes cal-tag-flash {
  0%, 55% { background: #ffe58a; }
  100% { background: inherit; }
}
@media print {
  .cal-table td.mark.cal-mark-flash, .cal-table th.cal-tag-th.cal-tag-flash { animation: none; }
}
.cal-table td.weekend { background: #fbf6ef; }
.cal-table td.today { box-shadow: inset 0 0 0 2px var(--accent); }

.pager { display: flex; gap: 8px; align-items: center; margin-top: 10px; font-size: 13px; color: var(--muted); }

.chips { display: flex; flex-wrap: wrap; gap: 4px; }
.chip {
  display: inline-block; font-size: 11.5px; padding: 1px 7px;
  border-radius: 20px; background: var(--accent-soft); color: var(--accent);
}

.toast {
  position: fixed; left: 50%; bottom: 32px; transform: translateX(-50%);
  background: #23262d; color: #fff; padding: 9px 18px; border-radius: 22px;
  font-size: 13px; z-index: 100; box-shadow: var(--shadow);
}
.toast.warn { background: var(--warn); }
.toast.bad { background: var(--danger); }

@media (max-width: 720px) {
  .topbar { flex-wrap: wrap; gap: 8px; }
  .tabs { order: 3; width: 100%; overflow-x: auto; }
  main { padding: 10px; }
}

/* ---------------- 教室管理 ---------------- */

/* 表头教室名可点 */
th .room.room-editable { cursor: pointer; border-radius: 6px; padding: 1px 5px; margin: -1px -5px; }
th .room.room-editable:hover { background: var(--accent-soft); color: var(--accent); }
th .room.room-editable::after { content: ' ✎'; font-size: 10px; opacity: .45; }

/* 表头就地改名的输入框 */
.room-rename {
  width: 92px; padding: 3px 6px; font: inherit; font-size: 12.5px;
  border: 1px solid var(--accent); border-radius: 6px; text-align: center;
  outline: none; background: #fff;
}

/* 表头右侧「+ 教室」按钮 */
th.th-add-room { padding: 4px 6px; }
.add-room-btn {
  font-size: 12px; padding: 3px 9px; border-radius: 8px; cursor: pointer;
  border: 1px dashed var(--line-strong); background: transparent; color: var(--muted);
  font-family: inherit; white-space: nowrap;
}
.add-room-btn:hover { border-color: var(--accent); color: var(--accent); border-style: solid; }

.add-room-btn {
  font-size: 12px; padding: 3px 9px; border-radius: 8px; cursor: pointer;
  border: 1px dashed var(--line-strong); background: transparent; color: var(--muted);
  font-family: inherit; white-space: nowrap;
}
.add-room-btn:hover { border-color: var(--accent); color: var(--accent); border-style: solid; }

/* 教室管理弹窗 */
.room-card { width: 520px; max-width: 94vw; }
.room-list { max-height: 46vh; overflow: auto; margin: 12px 0; border-top: 1px solid var(--line); }
.room-row { display: flex; align-items: center; gap: 6px; padding: 7px 0; border-bottom: 1px solid var(--line); }
#room-list .room-idx { width: 20px; font-size: 12px; color: var(--muted); text-align: center; flex: none; margin: 0; }
#room-list .room-name {
  flex: 1 1 0%; width: auto; min-width: 0; margin: 0;
  padding: 6px 9px; border: 1px solid var(--line-strong); border-radius: 7px;
  font: inherit; font-size: 13px;
}
#room-list .room-cap {
  width: 68px; flex: none; margin: 0;
  padding: 6px 8px; border: 1px solid var(--line-strong); border-radius: 7px;
  font: inherit; font-size: 13px;
}
#room-list .room-name:focus, #room-list .room-cap:focus { outline: none; border-color: var(--accent); }
#room-list .mini {
  flex: none; width: 26px; height: 28px; margin: 0; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--line-strong); background: #fff; color: var(--muted);
  font-size: 12px; line-height: 1; font-family: inherit;
}
#room-list .mini:hover { border-color: var(--accent); color: var(--accent); }
#room-list .mini.danger:hover { border-color: var(--danger); color: var(--danger); }
.room-add { display: flex; gap: 6px; align-items: center; }
.room-add input {
  flex: 1; min-width: 0; padding: 7px 10px; border: 1px solid var(--line-strong);
  border-radius: 8px; font: inherit; font-size: 13px;
}
.room-add input.cap { flex: none; width: 76px; }
.room-add input:focus { outline: none; border-color: var(--accent); }
.room-add button { flex: none; }

/* 「兼职专用」日期：数据全局共享，但课表总表的日期栏不显示它。
   面板里给一个勾选框，方便把已有日期一键改成 / 取消兼职专用。 */
.day-add-row .day-ptonly-check {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-size: 12px; color: var(--muted); cursor: pointer; white-space: nowrap;
  padding: 6px 8px; border: 1px solid var(--line-strong); border-radius: 8px;
}
.day-add-row .day-ptonly-check:hover { border-color: var(--accent); color: var(--accent); }
.day-add-row .day-ptonly-check input { margin: 0; cursor: pointer; }

#day-list .day-ptonly {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-size: 12px; color: var(--muted); cursor: pointer; white-space: nowrap;
  margin-left: 6px; user-select: none;
}
#day-list .day-ptonly:hover { color: var(--accent); }
#day-list .day-ptonly input { margin: 0; cursor: pointer; }

/* z89：日期管理面板 —— 插入位置下拉 + 「查询隐藏」开关 + 隐藏行置灰 */
.day-add-row select#day-new-pos {
  flex: none; max-width: 190px; padding: 6px 8px; font: inherit; font-size: 12px;
  border: 1px solid var(--line-strong); border-radius: 8px; background: var(--bg, #fff);
}
#day-list .day-hidden {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-size: 12px; color: var(--muted); cursor: pointer; white-space: nowrap;
  margin-left: 6px; user-select: none;
}
#day-list .day-hidden:hover { color: var(--accent); }
#day-list .day-hidden input { margin: 0; cursor: pointer; }
#day-list .room-row.day-hidden-row .room-name { opacity: .5; }
#day-list .room-row.day-hidden-row .room-idx::after { content: '（隐）'; font-size: 10px; color: var(--muted); }

/* ---------------- 日期列管理 ---------------- */

.day-tabs button { font-family: inherit; cursor: pointer; white-space: nowrap; }
.day-tabs button:hover { color: var(--accent); }

/* 整周 / 日期管理按钮 */
.day-tabs button.day-mgr-btn {
  border: 1px dashed var(--line-strong); background: transparent; color: var(--muted);
  margin-left: 4px;
}
.day-tabs button.day-mgr-btn:hover { border-style: solid; border-color: var(--accent); color: var(--accent); }

/* 双击日期标签就地改名 */
.day-rename {
  padding: 5px 9px; font: inherit; font-size: 13px; border: 1px solid var(--accent);
  border-radius: 7px; outline: none; background: #fff; text-align: center; min-width: 70px;
}

/* ---------------- 时段管理 ---------------- */

/* 课表最左列（时间格）可点改名 */
th.slot.slot-editable { cursor: pointer; }
th.slot.slot-editable:hover { background: var(--accent-soft); }
th.slot.slot-editable::after { content: ' ✎'; font-size: 9px; opacity: .4; font-weight: 400; }
td.slot-editable { cursor: pointer; }
td.slot-editable:hover { background: var(--accent-soft); }
td.slot-editable::after { content: ' ✎'; font-size: 9px; opacity: .35; }

/* 时间格就地改名的输入框 */
.slot-rename {
  padding: 3px 6px; font: inherit; font-size: 12px;
  border: 1px solid var(--accent); border-radius: 6px; outline: none;
  background: #fff; text-align: center;
}

/* 时段管理弹窗 */
#slot-list .room-idx { width: 20px; font-size: 12px; color: var(--muted); text-align: center; flex: none; margin: 0; }
#slot-list .room-name {
  flex: 1 1 0%; width: auto; min-width: 0; margin: 0;
  padding: 6px 9px; border: 1px solid var(--line-strong); border-radius: 7px;
  font: inherit; font-size: 13px;
}
#slot-list .room-name:focus { outline: none; border-color: var(--accent); }
#slot-list .mini {
  flex: none; min-width: 26px; height: 28px; padding: 0 7px; margin: 0; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--line-strong); background: #fff; color: var(--muted);
  font-size: 12px; line-height: 1; font-family: inherit;
}
#slot-list .mini:hover { border-color: var(--accent); color: var(--accent); }
#slot-list .mini.danger:hover { border-color: var(--danger); color: var(--danger); }
#slot-list .mini.warn-on {
  border-color: var(--warn); color: var(--warn); background: #fff8e6; font-weight: 600;
}
#slot-list .mini.warn-on:hover { border-color: var(--warn); color: var(--warn); }

/* ---------------- 角色与权限 ---------------- */

/* 角色说明卡 */
.role-legend {
  display: flex; flex-wrap: wrap; gap: 10px; margin: 4px 0 14px;
}
.role-card {
  flex: 1 1 300px; min-width: 240px;
  display: flex; align-items: center; gap: 9px;
  padding: 9px 12px; border: 1px solid var(--line);
  border-radius: 10px; background: #fbfbfc;
}
.role-chip {
  flex: none; font-size: 12px; font-weight: 600; padding: 3px 10px;
  border-radius: 20px; white-space: nowrap;
}
.role-chip.role-principal { background: #efe3ff; color: #6b3fbf; }
.role-chip.role-admin { background: var(--accent-soft); color: var(--accent); }
.role-chip.role-teacher { background: #eceef1; color: #5a6270; }
.role-desc { font-size: 12px; color: var(--muted); line-height: 1.5; }

/* 表内角色下拉 */
.role-select {
  font: inherit; font-size: 12.5px; padding: 5px 8px; border-radius: 8px;
  border: 1px solid var(--line-strong); background: #fff; cursor: pointer;
  outline: none; min-width: 92px;
}
.role-select:focus { border-color: var(--accent); }
select.role-select.role-principal { border-color: #c3a4f0; background: #faf6ff; color: #6b3fbf; font-weight: 600; }
select.role-select.role-admin { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); font-weight: 600; }
select.role-select.role-teacher { color: var(--muted); }

/* 表内就地编辑 */
.u-edit {
  cursor: pointer; padding: 2px 6px; margin: -2px -6px; border-radius: 6px;
  border-bottom: 1px dashed transparent;
}
.u-edit:hover { background: var(--accent-soft); color: var(--accent); border-bottom-color: var(--accent); }
.u-edit.u-muted { color: var(--muted); cursor: default; }
.u-edit.u-muted:hover { background: transparent; color: var(--muted); border-bottom-color: transparent; }
.u-edit.warn-text { color: var(--warn); }
.u-inline {
  width: 120px; padding: 4px 8px; font: inherit; font-size: 13px;
  border: 1px solid var(--accent); border-radius: 7px; outline: none; background: #fff;
}
tr.u-me td { background: #fcfaff; }
.u-self { margin-left: 6px; font-size: 11.5px; color: var(--muted); }
.u-ops button { margin-right: 4px; }

/* 账号备注（z56）：空备注显示灰色占位，点一下就地写 */
.u-note-empty { color: #b3b9c2; }
/* 角色只读（管理员）：身份只有校长/超管能改，这里只显示文字 */
.u-role-fixed {
  display: inline-block; padding: 2px 8px; border-radius: 6px;
  background: #f4f5f7; color: var(--muted); font-size: 12.5px;
}
.ue-role-fixed {
  margin: 8px 0; padding: 6px 10px; border-radius: 7px;
  background: #f4f5f7; color: var(--muted); font-size: 12.5px;
}

/* 新增账号弹窗 */
.fld {
  display: block; margin: 11px 0; font-size: 12.5px; color: var(--muted);
}
.fld input, .fld select {
  display: block; width: 100%; margin-top: 5px;
  padding: 8px 10px; font: inherit; font-size: 13.5px; color: var(--fg);
  border: 1px solid var(--line-strong); border-radius: 8px; outline: none; background: #fff;
}
.fld input:focus, .fld select:focus { border-color: var(--accent); }

/* ---------------- 课表导入 ---------------- */

.import-card { width: 760px; max-width: 96vw; }
#import-panel textarea {
  width: 100%; box-sizing: border-box; margin: 10px 0 8px;
  padding: 9px 11px; font: 12.5px/1.6 ui-monospace, Consolas, 'Courier New', monospace;
  border: 1px solid var(--line-strong); border-radius: 8px; outline: none;
  background: #fff; resize: vertical; min-height: 120px; color: var(--fg);
}
#import-panel textarea:focus { border-color: var(--accent); }
.imp-row { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }
.imp-row .spacer { flex: 1; }

/* 列映射 */
#imp-map { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: #fbfbfc; margin: 10px 0; }
.imp-map-title { font-size: 12.5px; color: var(--muted); margin-bottom: 8px; }
.imp-map-row { display: flex; flex-wrap: wrap; gap: 8px 14px; }
.imp-col { font-size: 12px; color: var(--muted); display: flex; flex-direction: column; gap: 4px; }
.imp-col select {
  font: inherit; font-size: 12.5px; padding: 4px 7px; border-radius: 7px;
  border: 1px solid var(--line-strong); background: #fff; outline: none; color: var(--fg);
}
.imp-col select:focus { border-color: var(--accent); }

/* 统计与提示 */
#import-panel .imp-stat-line {
  font-size: 13px; padding: 7px 11px; border-radius: 8px; margin: 6px 0; line-height: 1.6;
}
#import-panel .imp-ok { background: var(--accent-soft); color: #0d5c49; }
#import-panel .imp-new { background: #f3eeff; color: #5b3bb0; }
#import-panel .imp-warn { background: #fff6e0; color: #8a6100; }
#import-panel .imp-errs {
  font-size: 12.5px; color: #a33; background: #fdf1f1; border-radius: 8px;
  padding: 8px 11px; margin: 6px 0; line-height: 1.7; max-height: 130px; overflow: auto;
}
#import-panel .imp-errs-title { font-weight: 600; }

/* 模式与执行 */
#import-panel .imp-mode { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; }
#import-panel .imp-mode .chk { font-size: 13px; }
#import-panel .imp-mode input[type=radio] { accent-color: var(--accent); }

/* 导入面板：压过 .modal-card label/input 的通配样式，恢复表单控件正常布局 */
#import-panel .imp-col { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin: 0; }
#import-panel .chk, #import-panel .inline { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
#import-panel input[type="file"],
#import-panel input[type="radio"],
#import-panel input[type="checkbox"] {
  display: inline-block; width: auto; margin: 0; padding: 0;
  border: none; background: transparent; accent-color: var(--accent);
}
#import-panel input[type="file"][hidden] { display: none !important; }

/* ---------------- 鼠标位置（谁在看哪一格） ----------------
   与上面的 .edit-lock 刻意区分开：
     .edit-lock  橙色虚线框 + "某某 编辑中"  = 真要改这一格（别人不能动）
     .ptr-watch  蓝色细边 + 名字角标          = 鼠标只是停在这儿（不影响任何人）

   角标堆在格子右上角，横向并排；人多时靠 flexwrap 换行，不盖住班级名（班级名在左上）。
   气泡不走自定义 tooltip，直接用 title 属性 —— 移动端长按可看，也不会被层级问题坑到。 */
td.cell.ptr-watch { box-shadow: inset 0 0 0 2px #4f7cff; }
td.cell.ptr-watch.edit-lock { box-shadow: inset 0 0 0 2px #4f7cff, 0 0 0 2px #e8590c; }
.ptr-badges {
  position: absolute; top: 0; right: 0;
  display: flex; flex-wrap: wrap; justify-content: flex-end;
  gap: 2px; padding: 1px 2px;
  max-width: 100%;
  pointer-events: none;
  z-index: 3;
}
.ptr-badge {
  font-size: 10px; line-height: 1.5;
  max-width: 84px;
  padding: 0 5px;
  color: #fff;
  border-radius: 0 0 0 6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}
/* 有人看着时鼠标变「眼睛」，一眼区分「只是看」和「正在编辑」 */
td.cell.ptr-watch { cursor: pointer; }

/* 在线名单里也标一下「他正在看哪个格子」 */
.online-item .on-where {
  font-size: 11px; color: #5b6a7d; flex: none;
  max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 跨日期位置提示条：老师在看的日子与当前视图对不上时，用文字兜底说清楚 */
.ptr-day-strip {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: 6px 10px; margin: 0 0 8px;
  background: #f2f6ff; border: 1px solid #d8e3ff; border-radius: 8px;
  font-size: 12px;
}
.ptr-day-strip-label { color: #5b6a7d; }
.ptr-day-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 99px;
  background: #fff; border: 1.5px solid #d8e3ff; color: #1d1f24;
}
.ptr-day-chip i { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.ptr-day-chip em { color: #b46b00; font-style: normal; font-size: 11px; }
.ptr-day-chip.off { border-style: dashed; color: #5b6a7d; }

/* ---------------- 在线名单 + 编辑中角标 ---------------- */
#sync-state { cursor: pointer; }
#sync-state:hover { color: var(--accent); }

.online-card { width: 380px; }
#online-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; max-height: 320px; overflow: auto; }
.online-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--line, #e5e7eb); border-radius: 10px; background: var(--card, #fff); }
.online-item .avatar { width: 32px; height: 32px; border-radius: 50%; color: #fff; font-size: 14px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; flex: none; }
.online-item .on-name { font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.online-item .role-badge { font-size: 11px; padding: 2px 8px; border-radius: 99px; background: #eef2ff; color: #4353b0; flex: none; }
.online-item .role-badge[data-role="principal"] { background: #fff4e0; color: #b46b00; }
.online-item .role-badge[data-role="admin"] { background: #e7f0ff; color: #1c64d9; }
.online-item .role-badge[data-role="teacher"] { background: #f0f3f7; color: #5b6a7d; }
.online-item .dot-on { width: 8px; height: 8px; border-radius: 50%; background: #16a34a; flex: none; box-shadow: 0 0 0 3px rgba(22, 163, 74, .15); }
/* z76：设备标记（手机 / 平板 / 电脑）—— 一眼看出这位老师是拿什么在看课表。
   只用文字，不画图标：11px 宽度下三种设备形状根本分不出来，中文两个字反而清楚，
   而且不依赖 emoji 字体（emoji 里压根没有可靠的「平板」字符）。
   三种设备各给一个淡色底，扫一眼就能区分移动端和桌面端。 */
.online-item .on-device {
  display: inline-flex; align-items: center; flex: none;
  font-size: 11px; line-height: 1.5; padding: 2px 8px; border-radius: 99px;
  border: 1px solid; white-space: nowrap;
}
.online-item .on-device[data-device="phone"] { background: #eef7f0; color: #2f7d4f; border-color: #d6ecdd; }
.online-item .on-device[data-device="tablet"] { background: #f2f0fb; color: #5b46b0; border-color: #e2ddf5; }
.online-item .on-device[data-device="desktop"] { background: #eef3fa; color: #3a6193; border-color: #dbe6f3; }

/* 有人正在编辑的格子：虚线框 + 名字角标 */
td.cell { position: relative; }
td.cell.edit-lock { outline: 2px dashed #e8590c; outline-offset: -2px; cursor: not-allowed; }
td.cell.edit-lock::after {
  content: attr(data-editby) " 编辑中";
  position: absolute; top: 0; right: 0;
  max-width: 100%;
  font-size: 10px; line-height: 1.5;
  padding: 0 5px;
  background: #e8590c; color: #fff;
  border-radius: 0 0 0 6px;
  white-space: nowrap; overflow: hidden; text-overflow: clip;
  pointer-events: none;
  z-index: 2;
}

/* 编辑中的格子若同时有人在看，让橙色角标压住蓝色角标（编辑更重要） */
td.cell.edit-lock .ptr-badges { z-index: 1; }

/* 用户管理：删除按钮（红）、编辑弹窗 */
button.danger { color: #d64545; }
button.danger:hover { background: #fdecec; border-color: #d64545; }
.user-edit-card { width: 420px; }
.u-ops button { padding: 2px 8px; font-size: 12px; }

/* 用户管理：密码列（仅校长） */
.pwd-plain { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; letter-spacing: .5px; }
.pwd-toggle { padding: 1px 6px !important; font-size: 11px !important; }

/* 复制当日课表弹窗 */
.copy-card { width: 460px; }
.copy-row { display: flex; align-items: flex-end; gap: 12px; margin: 12px 0; }
.copy-row label { flex: 1; display: flex; flex-direction: column; gap: 4px; margin: 0; font-size: 13px; color: #5b6a7d; }
.copy-row select { width: 100%; }
.copy-arrow { font-size: 18px; color: #98a6b5; padding-bottom: 8px; }

/* 多选批量操作 */
td.cell.multi-sel { outline: 2px solid #4f7cff; outline-offset: -2px; background: rgba(79, 124, 255, .1); cursor: pointer; }
td.cell.multi-sel:hover { background: rgba(79, 124, 255, .18); }
#btn-multi.active { background: #e8efff; border-color: #4f7cff; color: #2b5fd9; }
.move-pending td.cell:not(.empty):hover, .move-pending td.cell:hover { outline: 2px dashed #4f7cff; outline-offset: -2px; cursor: crosshair; }
/* z81：多选拖拽 —— 拖到哪一格，哪一格给出明确的落点提示 */
td.cell.drop-target { outline: 3px solid #2b5fd9 !important; outline-offset: -3px; background: rgba(43, 95, 217, .22) !important; }
body.multi-dragging { cursor: grabbing; }
body.multi-dragging td.cell.multi-sel { cursor: grab; }

/* 批量修改字段弹窗：压过 .modal-card label/input 的通配样式 */
#multi-edit-panel .modal-card { width: 520px; }
.me-grid { display: grid; grid-template-columns: 48px 1fr 1fr; gap: 8px; align-items: center; margin: 14px 0 10px; }
.me-grid .me-cap { font-size: 13px; font-weight: 600; color: #334155; }
.me-grid .me-h { font-size: 12px; color: #98a6b5; text-align: left; }
.me-grid input { display: block; width: 100%; margin: 0; padding: 8px 10px; border: 1px solid var(--line-strong); border-radius: 8px; font: inherit; }
#me-info { min-height: 18px; }
#me-run:disabled { opacity: .5; cursor: not-allowed; }

/* 查找替换弹窗 */
#replace-panel .modal-card { width: 520px; }
.rp-scope-row { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.rp-scope-row select { flex: 1; padding: 8px 10px; border: 1px solid var(--line-strong); border-radius: 8px; font: inherit; background: #fff; }
#rp-info { min-height: 18px; }
#rp-run:disabled { opacity: .5; cursor: not-allowed; }

/* 顶部系统名称：校长可点击改名 */
#term-label.term-editable { cursor: pointer; border-radius: 6px; padding: 1px 6px; margin: -1px -6px; }
#term-label.term-editable:hover { background: var(--accent-soft); color: var(--accent); }
#term-label.term-editable::after { content: ' ✎'; font-size: 11px; opacity: .45; }
#term-label .term-rename { width: 260px; max-width: 46vw; font: inherit; font-weight: 700; padding: 2px 6px; border: 1px solid var(--accent); border-radius: 6px; }

/* 学生报名：行编辑 */
tr.en-row { cursor: pointer; }
tr.en-row:hover td { background: var(--accent-soft); }
/* 刚新增/刚导入的报名行：短暂高亮一下，一眼看到「它就是刚才那条」 */
.enroll-table tbody tr.en-flash td {
  animation: enFlash 2.6s ease-out forwards;
}
@keyframes enFlash {
  0%, 45% { background: #ffe9a8; }
  100% { background: transparent; }
}
.en-op-col { width: 72px; text-align: center; white-space: nowrap; }
.en-op-col .mini { padding: 2px 10px !important; font-size: 12px !important; }
#enroll-edit-panel .modal-card { width: 380px; }
#enroll-edit-panel .pop-actions .spacer { flex: 1; }

/* 调整记录 + 星号格子 */
.logs-card { width: 560px; }
#logs-list { max-height: 420px; overflow: auto; margin: 10px 0; display: flex; flex-direction: column; gap: 6px; }
.log-item { display: flex; gap: 10px; align-items: baseline; padding: 6px 10px; border: 1px solid var(--line, #e5e7eb); border-radius: 8px; font-size: 13px; }
.log-time { color: #8a97a6; flex: none; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; }
.log-user { font-weight: 600; flex: none; color: #2b5fd9; }
.log-action { word-break: break-all; }
/* ---- z74：调整记录面板的「实时更新」视觉 ----
   面板打开着时，别人改课 → 这里自动重拉列表，新出现的条目飘一下高亮。
   高亮类由 app.js 的 renderLogsList() 挂上、动画放完（2.6s）后自己摘掉，
   所以这里只写「亮一次」的动画，不需要写「怎么恢复」。 */
@keyframes logFresh {
  0%   { background: #fff6d6; border-color: #f2cf6b; box-shadow: 0 0 0 3px rgba(242, 207, 107, .35); }
  60%  { background: #fffbf0; border-color: #f2cf6b; box-shadow: 0 0 0 1px rgba(242, 207, 107, .18); }
  100% { background: transparent; border-color: var(--line, #e5e7eb); box-shadow: none; }
}
.log-item.log-fresh { animation: logFresh 2.6s ease-out 1; }
/* 右上角「已同步 HH:MM:SS（新增 N 条）」—— 给人一个「它真的在跟着更新」的凭据 */
.logs-sync { margin-left: 6px; color: #4a9d6a; font-weight: 600; }
td.cell .c.masked, td.cell.masked-cell { color: #98a6b5; }
td.cell.masked-cell { text-align: center; }
td.cell.masked-cell .c { color: #b3bfcc; letter-spacing: 2px; }

/* ---------- z92：课表查询「临时擦除」----------
   语义：右键擦掉某格，**只影响当前查询的显示** —— 不写库、不动总表。
   视觉上刻意做成「干净的空白格 + 一道极淡的斜纹」：
   · 完全不像有课（所以截图/分享出去看不出这里原本有什么）
   · 又留一点点痕迹，让人自己知道「这格是我擦的」，不至于以为是数据丢了 */
td.cell.erased-cell {
  background: repeating-linear-gradient(135deg, #fbfcfd 0 6px, #f4f6f9 6px 12px) !important;
  cursor: default;
}
td.cell.erased-cell::after {
  content: '';
  position: absolute; inset: 0;
  border: 1px dashed #dfe3ea;
  pointer-events: none;
}
/* 可擦除的格子：右键可用。用极克制的提示 —— 悬停时右上角浮出一个虚无的小圆点，
   平时完全不占视觉（避免在满屏课表上加噪）。 */
td.cell.erasable { position: relative; }
td.cell.erasable:hover { outline: 2px solid #c9d3e2; outline-offset: -2px; }
/* 查询工具栏上的擦除提示条 */
.q-erase-hint {
  margin: 6px 0 0; padding: 7px 12px; border-radius: 9px;
  background: #fff8e6; border: 1px solid #f0dfae; color: #8a6d1f;
  font-size: 12.5px; line-height: 1.5;
}
.q-erase-hint.hidden { display: none; }

/* ---------- 个人课表：隐藏全空的行/列 ---------- */
.mine-empty-hint {
  margin: 18px auto 6px; padding: 14px 18px; max-width: 380px;
  border: 1px dashed var(--line-strong, #d7dbe3); border-radius: 12px;
  background: #fafbfc; color: #7d8794; font-size: 13.5px; text-align: center;
}

/* ---------- 课表查询：独立工作表 ---------- */
.query-toolbar {
  padding: 9px 14px;
  border: 1px solid #c9dcf5;
  border-radius: 10px;
  background: linear-gradient(180deg, #f3f8ff, #eef4fd);
}
.qt-title { font-size: 13.5px; color: var(--accent, #2c6ecb); white-space: nowrap; padding-right: 2px; }
.query-toolbar .q-item { display: inline-flex; align-items: center; gap: 6px; }
.query-toolbar .q-cap { font-size: 13px; color: var(--muted, #7d8794); white-space: nowrap; }
.query-toolbar .q-cls { min-width: 160px; }
.query-toolbar .q-kw { min-width: 160px; }
.query-toolbar .q-expr-item { flex: 1 1 380px; min-width: 320px; }

/* ---- z73：日期多选条（独立一行，不挤工具栏）----
   按钮由 app.js 的 renderQueryDays() 按 mainDays() 动态生成 —— 日期可增删，
   所以这里只写「长什么样」，不写死几个。 */
.q-days-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
  margin-top: 8px; padding: 8px 14px;
  border: 1px solid #c9dcf5; border-radius: 10px;
  background: linear-gradient(180deg, #fbfdff, #f4f8fe);
}
.q-days-cap { font-size: 13px; color: var(--muted, #7d8794); white-space: nowrap; }
.q-days { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; }
.q-day {
  font: inherit; font-size: 13px; line-height: 1;
  padding: 6px 11px; border-radius: 999px;
  border: 1px solid #cfdcea; background: #fff; color: var(--fg, #22303f);
  cursor: pointer; white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.q-day:hover { border-color: var(--accent, #2c6ecb); color: var(--accent, #2c6ecb); }
.q-day.on {
  background: var(--accent, #2c6ecb); border-color: var(--accent, #2c6ecb);
  color: #fff; font-weight: 600;
}
.q-day.on:hover { color: #fff; }
.q-days-bar .mini { font-size: 12.5px; padding: 5px 10px; }
.q-days-hint { white-space: nowrap; }
@media (max-width: 640px) {
  /* 窄屏：日期条横向滚动（日期多，换行会把工具栏顶得很高） */
  .q-days { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 2px; }
  .q-days::-webkit-scrollbar { height: 4px; }
  .q-days-bar { padding: 7px 10px; }
  .q-days-hint { display: none; }   /* 窄屏没地方，隐藏（按钮本身已有 .on 高亮） */
}
.query-count {
  font-size: 13px; font-weight: 500; color: var(--accent, #2c6ecb);
  padding: 3px 10px; border-radius: 20px; background: #eaf1fc; white-space: nowrap;
}
.query-count.muted { color: #97a1ad; font-weight: 400; background: #eef0f3; }
.query-count.zero { color: #b0553a; background: #fdeeea; }

/* 表达式输入框：等宽字体，占满剩余空间 */
.query-toolbar .q-expr {
  flex: 1 1 280px; min-width: 260px;
  padding: 6px 10px; border: 1px solid var(--line-strong, #d7dbe3); border-radius: 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px;
  background: #fff;
}
.query-toolbar .q-expr:focus { outline: none; border-color: var(--accent, #2c6ecb); box-shadow: 0 0 0 2px rgba(44,110,203,.12); }
/* 表达式错误提示（工具栏下方的独立块） */
#q-expr-err { display: none; margin: 6px 2px 0; font-size: 12.5px; color: #b0553a; }
#q-expr-err.on { display: block; }

/* 查询结果：按课表总表网格展示，未命中格空白 */
.query-result { margin-top: 12px; }
.query-result .mine-empty-hint { margin-top: 0; }



/* ---------- 查询结果分享落地页（免登录 /q/<token>） ---------- */
#sharepage { min-height: 100vh; }
#sharepage .topbar { position: sticky; top: 0; z-index: 10; }
#sharepage .brand .muted { display: block; line-height: 1.4; }
.sp-head:empty { display: none; }
.sp-cond {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 12px;
}
.sp-cond .q-cap { font-size: 13px; color: var(--muted, #7d8794); white-space: nowrap; }
.sp-cond strong { font-size: 14px; }

/* ---------------- 学生报名：表格与导入面板 ---------------- */
.enroll-table td:nth-child(1) { width: 56px; }
/* 列宽按 z53 的列序重新分配（教师姓名插在学生姓名之后）：
   # / 班级名称 / 班型 / 学生姓名 / 教师姓名 / 报名状态 / 跟踪人 (+ 操作)
   前 4 列给百分比，其余几列由各自的 .en-xxx-col 定固定宽。 */
.enroll-table td:nth-child(2) { width: 22%; }
.enroll-table td:nth-child(3) { width: 14%; }
.enroll-table td:nth-child(4) { width: 13%; }
.enroll-table tbody tr:hover { background: var(--accent-soft); }
/* 报名状态：行内下拉，只给「未填 / 在读 / 试听」 */
.en-status-col { width: 108px; }
.en-status-sel {
  width: 96px; padding: 3px 6px; font: inherit; font-size: 12.5px; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: 6px; background: #fff; color: var(--muted);
}
.en-status-sel:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(13,110,60,.12); }
.en-status-sel.is-reading { border-color: #0d6e3c; background: #eef8f1; color: #0d6e3c; font-weight: 600; }
.en-status-sel.is-trial { border-color: #d08a1a; background: #fff7e8; color: #9a6200; font-weight: 600; }
/* 教师姓名：从课表算出的只读列（紧跟在「学生姓名」后面）。
   视觉上明显区别于「跟踪人」那个可编辑输入框 —— 这里是纯展示，点不动。 */
/* 教师姓名：与「跟踪人」一样是可编辑的行内输入框（手填一位老师） */
.en-teacher-col { width: 150px; max-width: 150px; }
.en-teacher-empty { color: #b6bfb6; }
.en-teacher-input {
  width: 100%; box-sizing: border-box; padding: 3px 6px; font: inherit; font-size: 13px;
  border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--fg);
}
.enroll-table tbody tr:hover .en-teacher-input { border-color: var(--line); }
.en-teacher-input:focus { outline: none; border-color: var(--accent); background: #fff; box-shadow: 0 0 0 2px rgba(13,110,60,.12); }
.en-teacher-input::placeholder { color: #b6bfb6; }

/* 跟踪人：行内输入框，平时无框，聚焦才显形 */
.en-tracker-col { width: 130px; }
.en-tracker-input {
  width: 100%; box-sizing: border-box; padding: 3px 6px; font: inherit; font-size: 13px;
  border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--fg);
}
.enroll-table tbody tr:hover .en-tracker-input { border-color: var(--line); }
.en-tracker-input:focus { outline: none; border-color: var(--accent); background: #fff; box-shadow: 0 0 0 2px rgba(13,110,60,.12); }
.en-tracker-input::placeholder { color: #b6bfb6; }

/* 老师端：班级/班型/学生姓名是管理侧字段，编辑时置灰只读，避免误改 */
#enroll-edit-panel input.locked {
  background: #f4f6f4; color: var(--muted); cursor: not-allowed;
  border-color: var(--line);
}
#enroll-edit-panel input.locked:focus { border-color: var(--line); box-shadow: none; }

/* 导入面板复用 #import-panel 的观感（选择器独立，避免相互影响） */
#enroll-import-panel textarea {
  width: 100%; box-sizing: border-box; margin: 10px 0 8px;
  padding: 9px 11px; font: 12.5px/1.6 ui-monospace, Consolas, 'Courier New', monospace;
  border: 1px solid var(--line-strong); border-radius: 8px; outline: none;
  background: #fff; resize: vertical; min-height: 120px; color: var(--fg);
}
#enroll-import-panel textarea:focus { border-color: var(--accent); }
#enroll-import-panel .imp-map-cols { display: flex; flex-wrap: wrap; gap: 8px 14px; }
#enroll-import-panel #en-imp-map { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: #fbfbfc; margin: 10px 0; }
#enroll-import-panel .imp-map-row { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-bottom: 8px; }
#enroll-import-panel .imp-map-label { font-size: 12.5px; color: var(--muted); }
#enroll-import-panel .imp-map-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
#enroll-import-panel .imp-map-head { font-size: 12px; color: var(--muted); }
#enroll-import-panel .imp-map-sel {
  font: inherit; font-size: 12.5px; padding: 4px 7px; border-radius: 7px;
  border: 1px solid var(--line-strong); background: #fff; outline: none; color: var(--fg);
}
#enroll-import-panel .imp-map-sel:focus { border-color: var(--accent); }
#enroll-import-panel .imp-ok {
  font-size: 13px; padding: 7px 11px; border-radius: 8px; margin: 6px 0;
  background: var(--accent-soft); color: #0d5c49;
}
#enroll-import-panel .imp-preview { margin-top: 6px; }
#enroll-import-panel .imp-empty { color: #b0b6bf; }
#enroll-import-panel .imp-mode { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; }
#enroll-import-panel .imp-mode .chk,
#enroll-import-panel .chk { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; }
#enroll-import-panel input[type="file"],
#enroll-import-panel input[type="radio"] {
  display: inline-block; width: auto; margin: 0; padding: 0;
  border: none; background: transparent; accent-color: var(--accent);
}
#enroll-import-panel input[type="file"][hidden] { display: none !important; }
#enroll-import-panel .err { color: #a33; font-size: 12.5px; margin: 8px 0 0; }
#enroll-import-panel .err.hidden { display: none; }
/* 底部提交栏：始终可见，主按钮固定在右下角 */
#enroll-import-panel .pop-actions {
  display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 12px 0 0;
  border-top: 1px solid var(--line, #e6e8ec);
  position: sticky; bottom: 0;
  background: #fff; z-index: 2;
}
#enroll-import-panel .pop-actions .spacer { flex: 1; }
#enroll-import-panel #en-imp-run { min-width: 132px; }
#enroll-import-panel #en-imp-run:disabled { opacity: .5; cursor: not-allowed; }
#enroll-import-panel .imp-mode { padding: 8px 0 0; }

/* ---- 导入类弹窗：卡片限高 + 内容区滚动 + 底部操作栏常驻可见 ----
   否则预览表格渲染后卡片会超出视口，底部的「确认导入」被顶到屏幕外 */
#import-panel.modal,
#enroll-import-panel.modal {
  place-items: center; padding: 24px 16px; box-sizing: border-box; overflow: auto;
}
#import-panel .import-card,
#enroll-import-panel .import-card {
  display: flex; flex-direction: column;
  max-height: calc(100vh - 48px); box-sizing: border-box;
  overflow: hidden; padding: 0;
}
/* 卡片里的所有直接子元素放进可滚动区，唯一点击栏固定在底部 */
#import-panel .import-card > *:not(.pop-actions),
#enroll-import-panel .import-card > *:not(.pop-actions) { flex: 0 0 auto; }
#import-panel .import-card > .imp-body,
#enroll-import-panel .import-card > .imp-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 22px 22px 0;
}
#import-panel .import-card > .pop-actions,
#enroll-import-panel .import-card > .pop-actions {
  flex: 0 0 auto; margin: 0; padding: 12px 22px 18px;
  border-top: 1px solid var(--line, #e6e8ec);
  position: static; background: #fff;
}
/* 预览表格在窄屏也能横向滚动，不撑破卡片 */
#import-panel .import-card .imp-preview,
#enroll-import-panel .import-card .imp-preview { width: 100%; }

/* ---------------- 兼职老师课表 ---------------- */
/* z71：同样固定布局，教室列宽与另外三张表保持一致。
   ⚠ min-width 必须为 0：原先写 min-width:50% 是为了「表格别太窄」，
   但在 table-layout:fixed + JS 钉死总宽（=各列之和）之后，这个 50% 下限
   会把表格重新撑宽、多余的宽度再摊回各列 —— 列宽就又对不齐了。 */
.pt-table { border-collapse: separate; border-spacing: 0; background: #fff; min-width: 0; table-layout: fixed; }

/* 免登录兼职课表分享页（/p/<token>）：只读展示，让表格自然撑开、可横向滚动 */
.pt-share-scroll { overflow: auto; max-height: none; }
.pt-share-scroll .pt-table { min-width: 0; }
.pt-share-scroll .pt-table th, .pt-share-scroll .pt-table td { pointer-events: none; }
.pt-table th, .pt-table td { border: 1px solid var(--line, #e6e8ec); padding: 7px 10px; font-size: 13px; }
.pt-table thead th {
  background: #f4f6f9; font-weight: 600; text-align: center; white-space: nowrap;
  position: sticky; top: var(--sticky-top); z-index: 1;
  width: var(--room-w); min-width: 0;
}
.pt-table thead th:first-child, .pt-table thead th:nth-child(2) { z-index: 2; }
/* z71：日期列 / 时段列固定宽度，与下方 colgroup 对应 */
.pt-table thead th.pt-day-th { width: var(--pt-day-w); }
.pt-table thead th.pt-slot-th { width: var(--pt-slot-w); }
.pt-table tbody td { min-width: 0; }
.pt-day-th, .pt-slot-th { color: var(--muted, #7d8794); font-weight: 500; }
.pt-table td.pt-day {
  background: #fafbfc; font-weight: 600; text-align: center; vertical-align: middle;
  color: var(--fg, #26313d); border-right: 2px solid var(--line-strong, #d7dbe3);
}
/* 兼职专用日期：只在兼职课表出现，课表总表看不到 —— 挂个小标提示老师 */
.pt-table td.pt-day .pt-only-badge {
  display: block; margin: 4px auto 0; width: fit-content;
  font-size: 10px; font-weight: 500; line-height: 1.6;
  padding: 0 5px; border-radius: 999px;
  color: #8a6d1f; background: #fdf3d8; border: 1px solid #f0dfa8;
  white-space: nowrap;
}
.pt-table td.pt-slot { background: #fafbfc; color: var(--muted, #7d8794); white-space: nowrap; text-align: center; }
.pt-table td.pt-break {
  background: repeating-linear-gradient(45deg, #f7f8fa, #f7f8fa 8px, #f1f3f6 8px, #f1f3f6 16px);
  color: #a6adb6; text-align: center; font-size: 12px; padding: 4px 10px;
}
.pt-room-th { min-width: 120px; }
.pt-room-th .pt-room-name { display: inline-block; max-width: 9em; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; }
.pt-room-ops { display: inline-flex; gap: 2px; margin-left: 5px; vertical-align: middle; }
.pt-op {
  border: none; background: transparent; color: #9aa3ae; cursor: pointer;
  font-size: 12px; line-height: 1; padding: 3px 4px; border-radius: 5px;
}
.pt-op:hover { background: #e8ecf2; color: var(--fg, #26313d); }
.pt-op.pt-op-del:hover { background: #fdeeea; color: #b0553a; }
/* 班课日期行尾「清」= 清空该天全部标记（z57）。橙色，和右边红色的「删除整行」区分开：
   清标记是轻操作（备注还在、行还在），删行是重操作。 */
.pt-op.pt-op-clear { font-size: 11px; }
.pt-op.pt-op-clear:hover { background: #fdf3e6; color: #a4712b; }
.pt-cell { min-width: 118px; max-width: 200px; height: 46px; text-align: center; vertical-align: middle; }
.pt-cell.filled { border-radius: 8px; }
.pt-cls { font-weight: 600; line-height: 1.3; word-break: break-all; }
.pt-tea { font-size: 11.5px; color: #5c6670; margin-top: 1px; }
.pt-cell.pt-editable { cursor: pointer; }
.pt-cell.pt-editable:hover { box-shadow: inset 0 0 0 2px var(--accent, #2c6ecb); }
#pt-room-panel .modal-card, #pt-edit-panel .modal-card { width: 380px; }

/* 兼职课表：来自课表总表的只读列 */
.pt-main-th { background: #eef4ec !important; }
.pt-main-badge {
  display: inline-block; margin-left: 5px; padding: 1px 7px;
  font-size: 10.5px; font-weight: 600; color: #0d6e3c;
  background: #d9efe0; border: 1px solid #bfe0cb; border-radius: 9px;
  vertical-align: middle;
}
.pt-cell.pt-main-cell { box-shadow: inset 0 0 0 1px rgba(13, 110, 60, .18); }
.pt-cell.pt-main-cell .pt-cls { font-weight: 500; }

/* 兼职课表：「隐藏空白行列」按钮激活态 */
.pt-empty-on { background: #eef4ec; border-color: #0d6e3c; color: #0d6e3c; font-weight: 600; }

/* 班课日期：批量操作条 */
.bulk-bar { display: flex; align-items: center; gap: 8px; padding: 8px 14px; background: #eef4ec; border-bottom: 1px solid #dbe7d8; flex-wrap: wrap; }
.bulk-bar select, .bulk-bar input { padding: 6px 10px; border: 1px solid #c8d8c4; border-radius: 6px; background: #fff; }
.bulk-bar .search { width: 220px; }

/* 班课日期：勾选列 / 备注编辑 / 课程列头操作 */
.cal-sel-col { width: 36px; text-align: center; }
.cal-sel-col input { cursor: pointer; }
.cal-table td.cal-note.editable { cursor: pointer; }
.cal-table td.cal-note.editable:hover { box-shadow: inset 0 0 0 2px #7cb342; }
.cal-note-input { width: 100%; box-sizing: border-box; border: 1px solid #7cb342; border-radius: 4px; padding: 2px 6px; font-size: 13px; }
.cal-tag-th { white-space: nowrap; }
.cal-tag-name { margin-right: 4px; }
.cal-tag-ops { display: inline-flex; gap: 2px; }

/* 班课日期：日期区间设定 */
.cal-date { padding: 5px 8px; border: 1px solid #c8d8c4; border-radius: 6px; background: #fff; font-size: 13px; color: #2c3e2c; }
.cal-date:focus { outline: none; border-color: #0d6e3c; box-shadow: 0 0 0 2px rgba(13,110,60,.15); }

/* 班课日期：删除日期行 */
.cal-del-col { width: 68px; text-align: center; white-space: nowrap; }
.cal-table tbody tr:hover td.cal-del-col .pt-op { opacity: 1; }
.cal-del-col .pt-op { opacity: .45; transition: opacity .15s; margin: 0 1px; }

/* 班课日期：表格区固定高度内部滚动 —— 表头 sticky 常驻，工具栏不再被长表格顶出屏幕 */
#view-calendar .grid-scroll { max-height: calc(100vh - 310px); min-height: 340px; }
#view-calendar .cal-table thead th { position: sticky; top: 0; z-index: 3; }

/* ---------- z71：四个工作表「表格区内部滚动，表头 + 时间列吸住」 ----------
   根因说明（别再踩）：position:sticky 是相对【最近的滚动祖先】定位的。.grid-scroll
   是 overflow:auto，所以它就是那个滚动容器；但桌面端它此前【没有高度上限】，
   内容把元素撑到全高，它自己从不滚动 → 里面的 sticky 表头无处可粘，随页面一起滚走。
   实测（z70）：四表 innerScrolls:false、滚 800px 后 thead 全部消失；
   对照 #view-calendar .grid-scroll 有 max-height → innerScrolls:true → 表头吸住 ✓。
   ↓ 所以这里的修复【不是新增 sticky 规则】，只是给容器一个高度上限。
   ⚠ 必须用【后代选择器】：课表查询的 .grid-scroll 是 JS 动态建在 #query-result
      【里面】的（app.js 建在 innerHTML 内），用 ">" 会匹配不到。 */
@media (min-width: 641px) {
  #view-schedule .grid-scroll,
  #view-query .grid-scroll,
  #view-mine .grid-scroll,
  #view-pt .grid-scroll {
    max-height: calc(100vh - 300px);   /* 300 ≈ 顶栏 63 + 工具栏 + 上下留白 */
    min-height: 320px;
  }
  /* 分块模式（整周视图 / 显示教室）下每块是一张小表，【刻意不吸顶】，
     也不要让每块都冒出独立滚动条 —— 保持原有的自然高度。
     ⚠ 这里必须写成 #view-xx .week-block .grid-scroll 这种带 ID 的选择器：
     上面那组 #view-schedule .grid-scroll 含 ID（权重 1,1,0），
     光写 .week-block .grid-scroll（0,2,0）是压不过它的。 */
  #view-schedule .week-block .grid-scroll,
  #view-query .week-block .grid-scroll,
  #view-mine .week-block .grid-scroll,
  #view-pt .week-block .grid-scroll { max-height: none; min-height: 0; }
}

/* 班课日期：课程筛选 / 批量课程 —— 可手输模糊匹配下拉（combobox）面板 */
.combo-panel {
  position: fixed;
  z-index: 90;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  box-shadow: var(--shadow);
  max-height: 260px;
  overflow-y: auto;
  padding: 4px;
  min-width: 190px;
  -webkit-overflow-scrolling: touch;   /* 手机上惯性滚动 */
  overscroll-behavior: contain;        /* 滚到头不带动页面滚动（否则会触发页面滚动把面板关掉） */
}
.combo-item {
  padding: 7px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.combo-item b { color: var(--accent); }
.combo-item:hover, .combo-item.on { background: var(--accent-soft); }
.combo-item.combo-new { color: #b45309; font-weight: 500; }
.combo-item.combo-new b { color: #b45309; }
.combo-item.combo-empty { color: var(--muted); font-size: 13px; cursor: default; }
/* 「全部课程」：置于候选首项，用分隔线强调它是「不筛选 / 打全部」的快捷项 */
.combo-item.combo-all { color: #075985; font-weight: 600; border-bottom: 1px solid var(--line, #e5e7eb); margin-bottom: 2px; padding-bottom: 6px; }
#cal-bulk-tag { width: 200px; min-width: 160px; }

/* ---------------- 课程详细（寒暑假课程安排表） ---------------- */
.lg-title { font-size: 22px; font-weight: 700; text-align: center; letter-spacing: 6px; margin: 6px 0 10px; color: var(--text); }
.lg-table th, .lg-table td { text-align: center; }
.lg-table td.lg-on { color: var(--accent); font-weight: 700; line-height: 1.35; }
.lg-table td.lg-off { color: transparent; }
.lg-table tr.lg-total td { background: #fff3bf; font-weight: 700; border-top: 2px solid #e8c33a; }
.lg-table td.lg-sum { color: #b45309; font-variant-numeric: tabular-nums; }
.lg-table td.lg-lunar { color: #c2410c; }
/* 课程列表头第二行：时间段 + 教室（均来自课表总表反查） */
.lg-table tr.lg-meta-row th.lg-meta-th {
  background: #f6f8fb; font-weight: 400; padding: 2px 4px 4px; line-height: 1.4;
  vertical-align: top; border-bottom: 2px solid var(--line);
}
.lg-table .lg-slot { display: block; font-size: 11px; font-weight: 700; color: #0369a1; font-variant-numeric: tabular-nums; }
.lg-table .lg-room { display: block; font-size: 11px; color: var(--muted); }
/* 格子内附注：该课这天的时段与教室（与列头同源，方便直接照着排课） */
.lg-table td.lg-on .lg-c-slot { display: block; font-size: 10px; font-weight: 400; color: #0369a1; font-variant-numeric: tabular-nums; }
.lg-table td.lg-on .lg-c-room { display: block; font-size: 10px; font-weight: 400; color: var(--muted); }
/* 联动班级徽章 */
#lg-link { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 999px; background: #e0f2fe; color: #075985; white-space: nowrap; }
#lg-link.hidden { display: none; }
#lg-link b { color: #0369a1; }
#lg-link button.tiny { padding: 1px 6px; font-size: 11px; line-height: 1.5; }
#lg-start, #lg-end { width: 138px; }

/* z113：联动之后一门课都没有时的说明块（替代表格，别让人以为表格坏了） */
.lg-empty-link {
  margin: 18px auto; max-width: 680px; padding: 18px 20px;
  border: 1px dashed #cbd5e1; border-radius: 10px; background: #f8fafc;
  text-align: center;
}
.lg-empty-link .lg-empty-title { margin: 0 0 8px; font-size: 15px; font-weight: 600; color: #334155; }
.lg-empty-link .muted.small { margin: 0; line-height: 1.7; }
@media print { .lg-empty-link { border: 0; background: transparent; } }

/* z113：联动徽章的文字比以前长（「课表查询 9 节 / 3 门课」），窄屏下标题会被撑成三行。
   只缩屏幕上的字号，打印仍用 @media print 里那套 26px —— 所以这里限定 screen。 */
@media screen and (max-width: 640px) {
  .lg-title { font-size: 16px; letter-spacing: 2px; line-height: 1.55; }
}

@media print {
  #tabs, .userbox, #view-ledger .toolbar, #view-ledger .hint, .bulk-bar, #sync-state { display: none !important; }
  .combo-panel, .combo-arrow { display: none !important; }  /* 打印时不输出任何打开中的下拉面板 */
  .view { display: none !important; }
  #view-ledger { display: block !important; }
  .grid-scroll { overflow: visible !important; max-height: none !important; border: none !important; }
  .lg-title { font-size: 26px; margin-bottom: 14px; }
  .lg-table { width: 100%; font-size: 11px; }
  .lg-table th, .lg-table td { border: 1px solid #999 !important; }
  tr.lg-total td { background: #fff3bf !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .lg-table tr.lg-meta-row th.lg-meta-th { background: #f0f0f0 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .lg-table .lg-slot, .lg-table td.lg-on .lg-c-slot { color: #1d4ed8 !important; }
}

/* ---------- 手机端编辑浮层：改为从底部弹出的操作面板（z69） ----------
   窄屏上「贴着格子右上方」没有足够空间，改成固定贴底的全宽面板；层级要压过底部导航条。
   桌面端的 left/top 内联样式在窄屏由 positionEditor() 直接清空（见 app.js）。 */
@media (max-width: 640px) {
  #editor.popover {
    position: fixed;
    left: 0 !important; right: 0; top: auto !important; bottom: 0;
    width: 100%;
    max-height: 76vh;
    overflow: auto;
    border-radius: 16px 16px 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    box-shadow: 0 -8px 28px rgba(20,24,35,.22);
    z-index: 95;                       /* 压过 #mob-actionbar(90) 与 #mob-nav(80) */
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  }
}

/* ---------- 分享账号弹窗：文本域可全选复制 ---------- */
#share-panel .share-text {
  display: block; width: 100%; min-height: 150px; margin-top: 12px;
  padding: 10px 12px; border: 1px solid var(--line-strong); border-radius: 8px;
  font: inherit; font-size: 13px; line-height: 1.6; color: var(--ink, #1d1f24);
  background: #fafbfc; resize: vertical; white-space: pre;
}

/* ---------- 整周视图：每天一个独立表块 ----------
   原来 7 天共用一横排教室列，教室一多就横向溢出、右侧教室看不见。
   改成每天一块后，每块各自带完整教室表头，左右不用滚就能看到本天所有教室。 */
.week-blocks { display: flex; flex-direction: column; gap: 18px; }
/* 注意：这里不能用 overflow:hidden —— 会把内层 .grid-scroll 的横向滚动一起锁死，
   教室列一多右侧就被裁掉。圆角改由头部/表格自身处理。 */
.week-block { border: 1px solid var(--line-strong); border-radius: 10px; background: #fff; }
.week-block-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px;
  padding: 9px 12px; background: #eef1f7;
  border-bottom: 1px solid var(--line-strong);
  border-radius: 10px 10px 0 0;
}
.week-block-head strong { font-size: 14px; color: #1d1f24; }
.week-block-head .week-rooms {
  color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  flex: 1 1 auto; min-width: 0;
}
/* 表块内的表格区：教室列多时横向滚动，保证每间教室都能看到 */
.week-block .grid-scroll { max-height: none; border: 0; border-radius: 0 0 10px 10px; background: transparent; }
.week-block .tt thead th { position: static; top: auto; }  /* 每块自己一屏，表头不必吸顶 */
/* ⚠ z71：上面这条只覆盖了 .tt，兼职课的 .pt-table 没被覆盖到 ——
   它的表头仍是 .pt-table thead th{position:sticky}（那条选择器不匹配 .tt），
   于是「分块模式不吸顶」这条口径在兼职课上没落实（实测 thPos=sticky）。
   四张表口径要一致，这里一并压平。 */
.week-block .pt-table thead th { position: static; top: auto; }
/* 整周视图下每列收窄（教室内容量/地址已在表头紧凑显示），尽量一屏看全一天的教室 */
.week-block .tt { width: auto; }   /* 不撑满容器，避免右侧留一大片空白 */
/* ⚠ z71：这里原来把列宽压到 80px（"min-width:80px"），导致「分块模式」下同一张表
   的教室列宽度又变成另一套（实测 uniq 3~9：周五全 80、周六 97/96/100/100/120…），
   用户要求四表列宽统一。现在只收紧内边距与字号，列宽一律由 --room-w / --slot-w 决定。 */
.week-block .tt thead th { padding: 6px 4px; }
.week-block .tt td.cell { padding: 4px 4px; }
.week-block .tt thead th .room { font-size: 12px; }
.week-block .tt td.cell .c { font-size: 12px; }
/* 整周视图里每块只显示一天，不再需要「周X · 学期」分隔行（已在块头显示） */
.week-block .tt .daysep { display: none; }

/* ---------------- 「显示教室」：按日期分块 ----------------
   勾上后，每个日期单独成块：块头写日期，表内最左是「时间」（行表头），
   右侧是这一天用到的教室列 —— 时间行正对着该天的教室。
   与整周视图同构，所以复用 .week-block 这套结构，只在细节上区分。 */
.day-blocks { gap: 14px; }
.day-blocks .week-block-head { background: #eef1f7; }
/* ⚠ z71：原「分块模式时段列 min-width:92px」的覆盖已删除 —— 那是列宽不统一的元凶之一。
   现在分块模式与单表模式共用同一组列宽变量（--slot-w / --room-w）。 */
/* 兼职课表分块：表里没有日期列，容器不再需要横向滚动边框 */
.pt-dayblock-table { width: auto; margin: 0; }
.week-block .pt-dayblock-table { border-radius: 0; }
.week-block .pt-table td.pt-day,
.week-block .pt-table th.pt-day-th { display: none; }   /* 保险：分块模式不该有日期列 */

@media (max-width: 720px) {
  .week-blocks { gap: 12px; }
  .week-block-head { padding: 7px 9px; }
  .week-block-head strong { font-size: 13px; }
  .week-block-head .week-rooms { display: none; }  /* 手机上行宽有限，教室名看表头即可 */
}

/* ============ 控制台（超级管理员）：学校管理 ============ */

/* 登录后、进入某校前的「选学校」卡片 */
.schoolpick-card { width: 520px; max-width: calc(100vw - 32px); }
.pick-list { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 14px; max-height: 46vh; overflow: auto; }
.pick-item {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--line, #e3e6ee);
  border-radius: 10px; background: #fff; cursor: pointer; text-align: left;
  transition: border-color .12s, background .12s;
}
.pick-item:hover { border-color: #4c7dff; background: #f7f9ff; }
.pick-item[disabled] { opacity: .5; cursor: not-allowed; }
/* 已停用的学校：超管仍可进入，用虚线边框 + 淡红底色做提示，不再禁用按钮 */
.pick-item.is-disabled { border-style: dashed; border-color: #e0b3ae; background: #fdf6f5; }
.pick-item.is-disabled:hover { border-color: #c0392b; background: #fbeceb; }

/* z60：班课日期里，老师自己带的班级列 */
.cal-tag-th.cal-tag-mine { color: #2b6cb0; }
.cal-tag-mine .mine-badge {
  display: inline-block; font-size: 10px; line-height: 1; margin-left: 4px;
  padding: 2px 5px; border-radius: 6px; background: #e8f0fe; color: #2b6cb0; font-weight: 600;
}
#view-calendar td.mark.editable-teacher { cursor: pointer; }
#view-calendar td.mark.editable-teacher:hover { outline: 2px solid #4c7dff; outline-offset: -2px; }
.pick-item .pick-name { font-weight: 600; }
.pick-item .pick-meta { font-size: 12px; color: #7a8194; margin-top: 2px; }
.pick-item .pick-main { flex: 1; min-width: 0; }
.pick-foot { display: flex; justify-content: flex-end; }

/* 控制台统计条 */
.stat-row { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0 16px; }
.stat-card {
  flex: 1 1 128px; min-width: 118px; padding: 12px 14px;
  border: 1px solid var(--line, #e3e6ee); border-radius: 10px; background: #fff;
}
.stat-card .stat-num { font-size: 22px; font-weight: 700; line-height: 1.2; }
.stat-card .stat-lab { font-size: 12px; color: #7a8194; margin-top: 3px; }
.stat-card.warn .stat-num { color: #d9534f; }

/* 控制台表格里的学校名与操作按钮 */
#console-wrap table.data td.school-name { font-weight: 600; }
.school-tag {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 11px; line-height: 18px; vertical-align: middle;
}
.school-tag.on { background: #e8f5ec; color: #2c7a45; }
.school-tag.off { background: #fdeaea; color: #c0392b; }
.school-tag.cur { background: #e8efff; color: #2c53c7; margin-left: 6px; }
.row-acts { display: flex; gap: 6px; flex-wrap: wrap; }
.row-acts button { padding: 3px 9px; font-size: 12px; }
.row-acts button.danger { color: #c0392b; border-color: #f0c4c0; }
.row-acts button.danger:hover { background: #fdeaea; }

/* 控制台「系统设置」卡片：开关类设置项 */
.console-settings { display: flex; flex-direction: column; gap: 10px; margin: 0 0 18px; }
.setting-card {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--card, #fff);
}
.setting-card.on { border-color: #e8b4ae; background: #fef7f6; }
.setting-info { flex: 1; min-width: 0; }
.setting-name { font-weight: 600; margin-bottom: 3px; }
.setting-card.on .setting-name { color: #c0392b; }
.setting-desc { font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.setting-card button { flex: none; }

/* ---------- 课表总表：导出 Excel 菜单 ---------- */
.export-card { width: 430px; }
.export-card .muted { line-height: 1.7; }
.exp-btn {
  display: block; width: 100%; text-align: left; margin-top: 12px; padding: 12px 14px;
  border: 1px solid var(--line-strong); border-radius: 10px; background: #fff;
  cursor: pointer; font: inherit; color: inherit; transition: border-color .15s, background .15s;
}
.exp-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
.exp-btn b { display: block; font-size: 14px; font-weight: 600; }
.exp-btn span { display: block; margin-top: 5px; font-size: 12px; color: var(--muted); line-height: 1.6; }

/* ---------- 分享注册制：邀请条与邀请面板 ---------- */
.invite-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 14px; padding: 11px 14px;
  border: 1px solid var(--line); border-radius: 10px;
  background: #f8faf8;
}
.invite-bar-main { flex: 1 1 auto; min-width: 260px; }
.invite-bar-main strong { margin-right: 8px; font-size: 13.5px; }
.invite-bar-main .muted { line-height: 1.6; }
.invite-bar-actions { flex: 0 0 auto; }

.invite-panel {
  margin-top: 12px; padding: 15px 16px;
  border: 1px solid var(--line); border-radius: 10px; background: #fcfdfc;
}
.invite-head { display: flex; align-items: baseline; flex-wrap: wrap; margin-bottom: 12px; }
.invite-head strong { font-size: 14px; }

/* 校长的「邀请老师 / 邀请学生」两块切换页签。
   一次只展开一块：两块同时铺开太长，二维码也会挤在一起。 */
.invite-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px;
  border-bottom: 1px solid var(--line); padding-bottom: 0; }
.invite-tab {
  position: relative; border: 1px solid transparent; border-bottom: none;
  background: transparent; color: var(--muted); cursor: pointer;
  padding: 7px 16px; font-size: 13px; border-radius: 8px 8px 0 0;
  margin-bottom: -1px; font-family: inherit; transition: background .12s, color .12s;
}
.invite-tab:hover { background: #f2f5f3; color: var(--fg); }
.invite-tab.on {
  background: #fff; border-color: var(--line); color: var(--fg);
  font-weight: 600; box-shadow: 0 1px 0 #fff;
}
.invite-tab .invite-off { margin: 0 0 0 6px; padding: 1px 6px; font-size: 10.5px; }
.invite-off {
  margin-left: 8px; padding: 2px 8px; border-radius: 999px; font-size: 11.5px;
  background: #fdeaea; color: #c0392b;
}
.invite-body { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-start; }
.invite-qr { flex: 0 0 auto; }
.invite-qr img {
  display: block; width: 150px; height: 150px;
  border: 1px solid var(--line); border-radius: 8px; background: #fff;
}
.invite-info { flex: 1 1 320px; min-width: 280px; }
.invite-link {
  padding: 9px 11px; border: 1px dashed var(--line-strong); border-radius: 8px;
  background: #fff; font-size: 12.5px; word-break: break-all; user-select: all;
}
.invite-note { margin: 9px 0 0; line-height: 1.7; }
.invite-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 11px; }
.invite-acts button.danger { color: #c0392b; border-color: #f0c4c0; }
.invite-acts button.danger:hover { background: #fdeaea; }
.invite-joined {
  display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
  margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--line);
}
.invite-chip {
  padding: 2px 9px; border-radius: 999px; font-size: 12px;
  background: #eef2f7; color: #465060;
}

/* 邀请注册页 /join/<邀请码> */
.join-inviter { margin: -4px 0 10px; }
#join-form label { margin-bottom: 12px; }
.join-foot { margin-top: 14px; text-align: center; }
.join-foot a { color: var(--accent); text-decoration: none; }
.join-foot a:hover { text-decoration: underline; }

/* 注册身份选择：两张可点的卡片，选中的那张描边+浅底，一眼看出选了什么 */
.join-role { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 6px; align-items: stretch; }
.join-role-opt {
  display: flex; flex-direction: column;
  margin: 0 !important; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px; background: #fff;
  cursor: pointer; transition: border-color .12s, background .12s;
}
.join-role-opt:hover { border-color: var(--line-strong); background: #fafbfa; }
.join-role-opt input { position: absolute; opacity: 0; pointer-events: none; }
.join-role-opt .join-role-txt { display: block; font-size: 13.5px; font-weight: 600; color: var(--fg); line-height: 1.3; }
.join-role-opt .join-role-hint { display: block; font-size: 11.5px; color: var(--muted); line-height: 1.45; margin-top: 3px; }
/* :has() 让卡片随内部 radio 的选中状态变色（现代浏览器均支持）；
   下面的 .on 是 JS 兜底，老浏览器也照样有反馈。 */
.join-role-opt:has(input:checked), .join-role-opt.on {
  border-color: var(--accent); background: var(--accent-soft, #eef4ff);
}
.join-role-opt:has(input:checked) .join-role-txt, .join-role-opt.on .join-role-txt { color: var(--accent); }
.join-role-opt:has(input:focus-visible) { outline: 2px solid var(--accent-soft, #eef4ff); }
/* 身份被锁定（码本身已定死角色、且只开一种）时的提示条 */
.join-role-lock { margin: 6px 0 0; font-size: 12px; color: var(--muted); }
.join-role[hidden] { display: none !important; }

/* ---------------- 时段：统一 / 按天个性化（z63） ---------------- */

/* 面板顶部「作用范围」选择器：默认全部日期，可切到「仅某一天」 */
.slot-scope-bar {
  display: flex; flex-wrap: wrap; gap: 8px 16px;
  margin: 4px 0 8px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px; background: #fafbfa;
}
.slot-scope-radio {
  display: flex; align-items: center; gap: 6px;
  margin: 0 !important; font-size: 13px; color: var(--fg); cursor: pointer;
}
.slot-scope-radio input { margin: 0; cursor: pointer; }
.slot-scope-radio b { color: var(--accent); font-weight: 600; }

/* 面板里「已单独设置的日期」小标签，点一下跳到那天的时段 */
.slot-custom-box {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 10px; padding-top: 9px; border-top: 1px dashed var(--line);
}
.slot-custom-title { font-size: 12px; color: var(--muted); }
.slot-custom-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 9px; font-size: 12px; border-radius: 999px;
  border: 1px solid var(--accent); color: var(--accent);
  background: var(--accent-soft, #eef4ff); cursor: pointer;
  transition: background .12s;
}
.slot-custom-chip:hover { background: #e2ecff; }

/* 单天模式下「本天独有」的时段行标记 */
.slot-own-badge {
  margin-left: 6px; padding: 1px 6px; font-size: 10.5px;
  border-radius: 999px; color: #8a5a00; background: #fff3d6;
  border: 1px solid #f0d597; white-space: nowrap;
}

/* 总表：某天没有某个时段 → 该行留空（不加「—」，避免被当成空格子） */
th.slot.slot-missing { color: var(--muted); background: #f7f8f7; }
td.cell.cell-missing { background: #f7f8f7; }

/* 兼职课表：同上，某天缺这个时段时留一条灰行 */
td.pt-slot.pt-slot-missing { color: var(--muted); }
td.pt-cell.pt-cell-missing { background: #f7f8f7; }
tr.pt-row-missing td { opacity: .55; }

/* ============================================================================
   z65：手机端适配
   ────────────────────────────────────────────────────────────────────────────
   背景（实测 iPhone 12，390×844）：
     · 顶栏 259px（31% 屏高）+ 课表工具栏 267px = 526px，占屏高 62%，
       表格首行落在 y≈881px —— 进页面**根本看不到课表**；
     · .tabs 里 9 个中文按钮挤在 370px 内按字断行，「课程详细（寒暑假专用）」
       竖排成一列字；
     · td.cell 116px 宽，一屏只看得到 2.5 列，左右来回蹭。

   本区块的做法（**全部包在 ≤640px 媒体查询里，桌面端零变化**）：
     ① 顶栏瘦身成一行；主导航挪到**底部固定标签条**（横向可滑）；
     ② 课表工具栏次要按钮收进「工具」抽屉，主操作留在外面；
     ③ 课表格子收窄到 88px / 时段列 76px，字号内边距一起压，一屏 3.5 列；
     ④ 选中格子后**底部弹操作条**（编辑 / 清除 / 取消），不用够那个 11px 小按钮；
     ⑤ 弹窗、抽屉、各工作表的窄屏堆叠。

   断点取 640px：覆盖 iPhone 全系竖屏（320–430）与横屏（568–932）；iPad 竖屏
   768px 起不进入本区块，避免误伤平板。
   ============================================================================ */

/* ---------- 全局：窄屏基础 ---------- */
@media (max-width: 640px) {
  html { -webkit-text-size-adjust: 100%; }
  body { overflow-x: hidden; }

  /* 底部标签条是 fixed，给主体让出高度，免得最后一行被它压住 */
  main {
    padding: 8px 10px calc(66px + env(safe-area-inset-bottom, 0px));
  }

  /* 搜索框原来硬定 min-width:220px，窄屏会撑破布局 */
  .search { min-width: 0; width: 100%; }
  select { max-width: 100%; }

  /* 提示文字在手机上太占地方：默认折叠到 3 行，点一下展开（见 .hint-fold） */
  .hint { font-size: 12px; line-height: 1.6; }
}

/* ---------- ① 顶栏瘦身 + 底部标签条 ---------- */
/* 桌面端 / 平板：底部标签条不存在 */
.mob-tabbar { display: none; }
.mob-actionbar { display: none; }

@media (max-width: 640px) {
  /* 顶栏压成一行：原来是 flex-wrap:wrap，页签一多就顶成 259px 高 */
  .topbar {
    flex-wrap: nowrap;
    gap: 8px;
    padding: 6px 10px;
    min-height: 52px;
  }
  .brand { gap: 7px; min-width: 0; flex: 1 1 auto; }
  .brand .logo { width: 26px; height: 26px; border-radius: 7px; font-size: 13px; }
  .brand strong { font-size: 14px; }
  .brand .small { font-size: 11px; }
  /* 校名 / 连接状态都单行省略，别把顶栏顶高 */
  .brand > div { min-width: 0; }
  .brand strong, .brand .small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* 顶栏里那排页签藏掉 —— 手机上看底部标签条 */
  .tabs { display: none; }

  /* 用户区：只留「≡」和「退出」，其余（说明书/改密码/切换学校）收进弹出菜单 */
  .userbox { gap: 6px; margin-left: 0; flex: 0 0 auto; }
  .userbox > button { padding: 5px 9px; font-size: 12.5px; }
  #whoami { display: none; }
  #btn-manual, #btn-password, #btn-switch-school { display: none; }

  /* 「≡」更多菜单 */
  #btn-mob-more {
    display: inline-flex !important; align-items: center; justify-content: center;
    width: 36px; height: 34px; padding: 0;
    font-size: 16px; line-height: 1;
  }
  .mob-more-menu {
    position: fixed; top: 54px; right: 8px; z-index: 90;
    min-width: 152px; padding: 5px;
    background: #fff; border: 1px solid var(--line-strong);
    border-radius: 10px; box-shadow: 0 10px 28px rgba(0,0,0,.16);
    display: flex; flex-direction: column;
  }
  .mob-more-menu button {
    border: 0; background: transparent; text-align: left;
    padding: 10px 12px; border-radius: 7px; font-size: 13.5px; color: var(--text);
  }
  .mob-more-menu button:hover { background: #f2f4f7; }
  .mob-more-menu button.hidden { display: none; }

  /* ★ 底部固定标签条：横向可滑，一个按钮 48px 高，手指够得着 */
  .mob-tabbar {
    display: flex;
    position: fixed; left: 0; right: 0; bottom: 0;
    z-index: 60;
    gap: 4px;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0px));
    background: rgba(255,255,255,.97);
    border-top: 1px solid var(--line-strong);
    box-shadow: 0 -2px 12px rgba(20,24,35,.07);
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  .mob-tabbar::-webkit-scrollbar { display: none; }
  .mob-tabbar button {
    flex: 0 0 auto;
    white-space: nowrap;
    min-height: 44px; min-width: 62px;
    padding: 6px 13px;
    border: 0; border-radius: 9px;
    background: transparent; color: var(--muted);
    font-size: 13px; line-height: 1.2;
    scroll-snap-align: center;
  }
  .mob-tabbar button.active {
    background: var(--accent-soft); color: var(--accent); font-weight: 600;
  }
}

/* ---------- ⑤ 底部格子操作条 ---------- */
@media (max-width: 640px) {
  /* 底部格子操作条：用 grid 而不是 flex-wrap —— 一列信息 + 三列等宽按钮。
     之前用 flex + flex:1 1 0 时，按钮的 min-content（「编辑」两字 + padding）
     会把它们挤成竖排，看起来像被压扁了。grid 的 1fr 不受 min-content 影响。 */
  .mob-actionbar {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-template-areas: "info info info" "a b c";
    position: fixed; left: 0; right: 0;
    bottom: calc(62px + env(safe-area-inset-bottom, 0px));  /* 贴在标签条上方 */
    z-index: 59;
    gap: 8px;
    padding: 8px 10px;
    background: #fff;
    border-top: 1px solid var(--line-strong);
    box-shadow: 0 -4px 16px rgba(20,24,35,.10);
  }
  .mob-actionbar.hidden { display: none !important; }
  .mob-actionbar .mab-info {
    grid-area: info;
    font-size: 11.5px; color: var(--muted); line-height: 1.45;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .mob-actionbar button {
    min-width: 0; width: 100%;
    min-height: 44px; font-size: 14px;
    padding: 8px 4px; white-space: nowrap;
  }
  .mob-actionbar button.danger { color: var(--danger); border-color: #e8bcb6; }

  /* 交换 / 多选提示条：手机上文字太长会把课表顶下去，收成两行 + 允许展开 */
  .swap-bar { display: block; }
  .swap-bar > span { display: block; }
  .swap-bar button { margin-top: 6px; width: 100%; min-height: 38px; }
}

/* ---------- ② 工具栏折叠 ---------- */
@media (max-width: 640px) {
  .toolbar { gap: 6px; margin-bottom: 8px; }
  .toolbar > button, .toolbar > .ghost, .toolbar > .primary {
    height: 34px; padding: 0 11px; font-size: 12.5px;
  }
  .toolbar .chk, .toolbar .inline { font-size: 12.5px; }
  .day-tabs { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .day-tabs button { padding: 6px 11px; font-size: 13px; min-height: 34px; }

  /* 「工具 ▾」按钮：只在窄屏出现 */
  .mob-tools-btn { display: inline-flex !important; align-items: center; gap: 4px; }

  /* 被折叠的次要按钮：默认藏起来；.toolbar.mob-tools-open 后铺成两列网格 */
  .toolbar.mob-tools-open > .mob-secondary {
    display: inline-flex;
    flex: 0 0 calc(50% - 3px);
    justify-content: center;
  }
  .toolbar > .mob-secondary { display: none; }
  .toolbar.mob-tools-open > .mob-secondary { display: inline-flex; }

  /* 长提示在手机上折叠 */
  .hint.mob-hint-fold {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden; cursor: pointer; position: relative;
  }
  .hint.mob-hint-fold.mob-hint-open { display: block; -webkit-line-clamp: unset; }
}

/* ---------- ③ 课表紧凑化（保持单表，只压列宽字号） ---------- */
@media (max-width: 640px) {
  /* 内容区高度跟着视口走，整页不用来回滚 */
  #view-schedule .grid-scroll,
  #view-query .grid-scroll,
  #view-mine .grid-scroll,
  #view-pt .grid-scroll {
    max-height: calc(100vh - 232px);
  }

  .tt { font-size: 12px; }
  /* z71：窄屏不另写一套列宽规则了，直接覆盖变量 —— 单表/分块/四张表全跟着变，
     不会出现「手机上是 88px，分块时又变 80px」这种对不齐的情况。
     教室列 116 → 88px；一格 40px 高，一屏能多露两行 */
  :root { --room-w: 88px; --slot-w: 74px; --pt-day-w: 96px; --pt-slot-w: 74px; }
  .tt thead th { padding: 5px 4px; }
  .tt td.cell { height: 40px; padding: 3px 4px; }
  .tt thead th .room { font-size: 12px; }
  .tt thead th .meta, .tt thead th .addr { font-size: 10px; }
  .tt td.cell .c { font-size: 12px; }
  .tt td.cell .t { font-size: 10.5px; }
  /* 时段列窄屏允许折行（"08:25-10:25" 这类长文案），列宽仍由 --slot-w 说了算 */
  .tt th.slot, .tt td.slot {
    padding: 5px 4px; font-size: 11.5px;
    white-space: normal; overflow-wrap: anywhere; line-height: 1.35;
    text-overflow: clip;
  }
  .tt thead th.corner { padding: 5px 4px; font-size: 11.5px; }
  /* 格子里那个 11px 的小按钮手机上不好点，靠底部操作条替代，这里放大一点 */
  .tt td.cell .cell-edit-btn { font-size: 12px; padding: 4px 9px; right: 2px; bottom: 2px; }
  /* z93：批注小按钮手机上同样不好点 → 隐藏，走底部操作条的「批注」 */
  .tt td.cell .cell-note-btn { display: none; }
  /* 单选 / 多选 / 交换等提示条在窄屏堆叠 */
  .swap-bar { font-size: 12px; padding: 7px 10px; gap: 6px; }
  /* 图例一行一个也太高，收紧 */
  .legend { gap: 4px; margin-bottom: 8px; }
  .legend .lg { font-size: 11.5px; padding: 2px 7px; }
  .tt tr.daysep .daysep-name { font-size: 12px; }
}

/* ---------- ④ 弹窗 / 抽屉窄屏铺满 ---------- */
@media (max-width: 640px) {
  .modal { padding: 10px; place-items: stretch; }
  .modal-card {
    width: 100% !important; max-width: 100% !important;
    margin: auto; padding: 16px 14px;
    max-height: calc(100vh - 20px); overflow-y: auto;
    border-radius: 12px;
  }
  /* 里头的通配 input/label 也要跟着窄屏收 */
  .modal-card input, .modal-card select, .modal-card .search { width: 100%; min-width: 0; }
  .pop-actions { flex-wrap: wrap; gap: 6px; }
  .pop-actions button { flex: 1 1 40%; min-height: 40px; }
  #slot-list .room-name { min-width: 0; }

  /* 编辑浮层里改成大按钮，拇指好按 */
  #editor.popover { padding: 14px; }
  #editor .pop-actions button { flex: 1 1 40%; min-height: 42px; font-size: 14px; }

  /* 说明书弹窗全屏 */
  #manual-modal { padding: 0; }
  .manual-card { height: 100vh; max-height: 100vh; border-radius: 0; }
  .manual-head { padding: 9px 10px; gap: 6px; }
  .manual-head h2 { font-size: 14px; }
  .manual-body { min-height: 0; }
}

/* ---------- ⑥ 其它工作表窄屏堆叠 ---------- */
@media (max-width: 640px) {
  /* 班课日期 / 学生报名 的批量条：整条改成纵向可滑，别挤成一坨 */
  .bulk-bar {
    flex-direction: column; align-items: stretch; gap: 6px;
    padding: 8px 10px;
    max-height: 46vh; overflow-y: auto;
  }
  .bulk-bar input, .bulk-bar select, .bulk-bar button { width: 100%; min-height: 36px; }

  /* 邀请条纵向堆叠 */
  .invite-bar { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px; }
  .invite-bar-actions { display: flex; }
  .invite-bar-actions button { flex: 1 1 auto; min-height: 38px; }

  /* 控制台统计卡：一行两张 */
  .stat-row { gap: 8px; }
  .stat-card { flex: 1 1 calc(50% - 4px); min-width: 0; padding: 10px; }
  .stat-card .stat-num { font-size: 19px; }

  /* 选学校列表 / 角色说明卡 */
  .pick-list { max-height: 52vh; }
  .role-legend { flex-direction: column; gap: 8px; }
  .role-card { min-width: 0; flex: 1 1 auto; }

  /* 分享卡：二维码与信息纵向堆叠 */
  .share-card { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; }
  .share-card .qr { align-self: center; }
  .share-card .info { flex: 1 1 auto; min-width: 0; }

  /* 各种工作表里的数据表：保证能横滑，不挤压变形 */
  #view-calendar .grid-scroll,
  #view-enroll .grid-scroll,
  #view-users .grid-scroll,
  #view-console .grid-scroll,
  #view-ledger .grid-scroll {
    max-height: calc(100vh - 250px);
    overflow: auto;
    -webkit-overflow-scrolling: touch;
  }
  table.data { font-size: 12.5px; }
  table.data th, table.data td { padding: 5px 7px; }

  /* 报名 / 用户管理 / 控制台这些数据表：窄屏下如果不给下限，浏览器会把每列
     压到 30px 宽，中文表头就竖排成一列字（「学生姓名」变成「学/生/姓/名」）。
     给表格一个最小宽度 + 让外层横滑，列宽就正常了。 */
  table.data { min-width: 620px; }
  #en-wrap, #u-wrap, #console-wrap, #syslog-wrap, #cal-wrap, #lg-wrap { min-width: 0; }
  #view-enroll .grid-scroll table.data,
  #view-users .grid-scroll table.data,
  #view-console .grid-scroll table.data,
  #view-ledger .grid-scroll table.data { min-width: 640px; }
  /* 班课日期表列多，给它更宽的下限，横滑看 */
  #view-calendar .grid-scroll table.data { min-width: 680px; }
  /* 表格里那列窄的序号 / 开关，别参与压缩 */
  table.data th, table.data td { white-space: nowrap; }
  /* 但备注这种长文本列要允许折行，否则表会宽得离谱 */
  table.data td.wrap, table.data td.note { white-space: normal; min-width: 120px; }

  /* 派生：控制台 / 用户管理里那些带固定宽度的行内控件 */
  #u-search, #cal-search, #cal-tag-filter, #en-search, #lg-tag-filter { width: 100%; min-width: 0; }
  #lg-start, #lg-end, #cal-start, #cal-end { width: 100%; }

  /* 课表查询的表达式行：手机上单独占一整行 */
  .query-toolbar .q-expr-item { flex: 1 1 100%; }
  .query-toolbar .q-expr { width: 100%; }
}

/* ---------- ⑦ 登录 / 注册 / 分享页 ---------- */
@media (max-width: 640px) {
  .login { padding: 16px 12px; place-items: start center; padding-top: 6vh; }
  .login-card {
    max-width: 100%; padding: 20px 16px;
    border-radius: 12px;
  }
  .login-card h1 { font-size: 18px; }
  .login-card input { padding: 10px 11px; font-size: 15px; }  /* ≥16px 可避免 iOS 自动放大 */
  .join-role-opt { align-items: flex-start; }

  /* 分享落地页：表格能横滑 */
  #sharepage .topbar { flex-wrap: wrap; height: auto; }
  #sharepage .query-result { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}

/* ---------- z82：课表查询「语法」说明弹窗 ----------
   （那个按钮从上线起就是坏的：alert 的常量从未定义，点了必抛 ReferenceError；
   就算能弹，手机 webview 也常拦原生 alert。改用应用内弹窗。） */
#query-syntax-help .qsh-card { width: min(560px, calc(100vw - 32px)); max-height: min(640px, calc(100vh - 48px)); display: flex; flex-direction: column; }
#query-syntax-help .qsh-body { overflow-y: auto; -webkit-overflow-scrolling: touch; margin-top: 10px; font-size: 13.5px; line-height: 1.8; color: #334155; }
#query-syntax-help .qsh-cap { margin: 12px 0 2px; font-weight: 600; color: #0f172a; font-size: 13px; }
#query-syntax-help .qsh-cap::before { content: '◆ '; color: #4f7cff; font-size: 11px; }
#query-syntax-help .qsh-body p { margin: 2px 0; }
#query-syntax-help code { display: inline-block; padding: 1px 7px; margin: 1px 2px; background: #eef2fb; border: 1px solid #dbe4f6; border-radius: 6px; font-family: inherit; font-size: 12.5px; color: #2b5fd9; white-space: nowrap; }

/* ---------------- z83：学期管理弹窗 ---------------- */
#term-modal .tm-card { width: min(480px, 92vw); max-height: 82vh; overflow-y: auto; }
#term-modal .tm-tip { color: #6b7280; font-size: 12.5px; margin: 4px 0 10px; line-height: 1.6; }
#term-modal .tm-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
#term-modal .tm-item { display: flex; align-items: center; gap: 8px; padding: 9px 12px;
  border: 1.5px solid #e5e7eb; border-radius: 10px; cursor: pointer;
  transition: border-color .15s, background .15s; }
#term-modal .tm-item:hover { border-color: #2b5fd9; background: #f5f8ff; }
#term-modal .tm-item.tm-cur { border-color: #2b5fd9; background: #eef3ff; }
#term-modal .tm-item .tm-name { flex: 1; font-weight: 600; font-size: 14px; min-width: 0; overflow-wrap: anywhere; }
#term-modal .tm-item.tm-cur .tm-name { color: #2b5fd9; }
#term-modal .tm-cur { flex: none; font-size: 11px; color: #2b5fd9; background: #dbe7ff; padding: 2px 8px; border-radius: 999px; }
#term-modal .tm-ops { display: flex; gap: 4px; flex: none; }
#term-modal .tm-ops button { font-size: 12px; padding: 3px 8px; }
#term-modal .tm-new { border-top: 1px dashed #e5e7eb; padding-top: 10px; margin-bottom: 10px;
  display: flex; flex-direction: column; gap: 8px; }
#term-modal .tm-new.hidden { display: none; }
#term-modal .tm-new-name, #term-modal .tm-new-copy { width: 100%; }
#term-modal .tm-item .term-rename { flex: 1; min-width: 0; font-size: 14px; }

/* z84：新建学期「逐项复制数据」勾选框 */
#term-modal .tm-cd { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; padding: 10px 12px; border: 1px solid var(--line, #e3e6ee); border-radius: 8px; background: var(--soft, #f7f8fb); }
#term-modal .tm-cd-item { display: flex; align-items: center; gap: 7px; font-size: 13px; cursor: pointer; }
#term-modal .tm-cd-item input { accent-color: var(--pri, #4a6cf7); }
#term-modal .tm-cd-off { opacity: .45; pointer-events: none; }

/* z85：学生冲突角标（管理员总表）—— 同一学生被同时段排进多节课 */
td.cell.stu-conflict { box-shadow: inset 0 0 0 2px #e0393e; }
td.cell.stu-conflict::after {
  content: '!';
  position: absolute;
  top: 1px;
  right: 1px;
  width: 14px;
  height: 14px;
  line-height: 14px;
  border-radius: 50%;
  background: #e0393e;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  text-align: center;
  pointer-events: none;
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}

/* z86：编辑面板内嵌「学生冲突原因」卡（点开标红格子即见原因）。
   ★ 限高滚动：基线数据里一格可能关联几十条冲突明细，不限高会把面板撑到
   上千像素、破坏 z69 的贴格定位（面板翻边/夹紧按 offsetHeight 计算）。 */
#editor .ed-conflict { margin: 2px 0 6px; padding: 7px 10px; border: 1px solid #e0393e; border-radius: 8px; background: #fdf1f1; font-size: 12.5px; line-height: 1.6; max-height: 140px; overflow-y: auto; }
#editor .ed-conflict .ec-head { font-weight: 700; color: #c22f35; margin-bottom: 3px; }
#editor .ed-conflict .ec-item { white-space: pre-wrap; word-break: break-all; }

/* z94：总表格子里的「试听」和时间（16点40 / 17点 / 17:40）加粗红色 */
td.cell .c b.aud { color: #d0021b; font-weight: 700; }

/* z98：微信推送 —— 接收人勾选列表 / 名单管理行 */
.wxs-list { max-height: 320px; overflow: auto; margin: 8px 0 4px; border: 1px solid #e3e5ea; border-radius: 8px; }
.wxs-row { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid #f0f2f6; cursor: pointer; }
.wxs-row:last-child { border-bottom: none; }
.wxs-row:hover { background: #f7f9fc; }
.wxs-row input[type=checkbox] { width: 15px; height: 15px; }
.wxs-name { font-weight: 500; color: #1d1f24; }
.wxs-tag { margin-left: auto; font-size: 11.5px; color: #8b91a1; }
.wt-list { max-height: 240px; overflow: auto; margin: 6px 0 10px; }
.wt-row { display: flex; align-items: center; gap: 8px; padding: 7px 4px; border-bottom: 1px solid #f0f2f6; }
.wt-row .wt-name { font-weight: 500; }
.wt-row .wt-tag { margin-left: auto; font-size: 11.5px; color: #8b91a1; }
.wt-form { border-top: 1px dashed #e3e5ea; padding-top: 10px; }

/* ---------- z100：提示条固定占位区 ----------
   之前：对调源条（含「取消选择」按钮）、多选条、实时「正在看」条出现/消失时
   都会插入/移除文档流内容，把它们下方的课表整体顶上/推下（跳动）。
   现在：三类条统一放进 .action-bars 固定占位区，条以浮层显示在同一位置，
   出现或消失只影响本区内部，下方课表纹丝不动。 */
.action-bars {
  position: relative;
  /* 占位高度 = 条自身（约39px）+ 顶部间距10px，恒定不变 */
  min-height: 49px;
}
.action-bars .swap-bar,
.action-bars .ptr-day-strip {
  position: absolute;
  left: 0; right: 0; top: 10px;
  margin: 0;
  z-index: 30;
  box-shadow: 0 4px 14px rgba(20, 24, 35, .08);
}
/* 手机端（≤640px）恢复占流布局：本就靠底部操作条 + 精简文案，且竖屏空间宝贵 */
@media (max-width: 640px) {
  .action-bars { min-height: 0; }
  .action-bars .swap-bar,
  .action-bars .ptr-day-strip {
    position: static;
    margin: 10px 0 0;
  }
}
/* z100 补充：「取消选择」等按钮固定在条右端 —— 提示文字长短变化不改变按钮位置 */
.action-bars .swap-bar > span { flex: 1 1 0; min-width: 0; }
.action-bars .swap-bar > button { margin-left: auto; flex: none; }

/* ---------- z101：登录页「记住账号 / 自动登录」 ---------- */
.login-remember { margin: -4px 0 16px; }
.login-remember .rmk {
  display: flex; align-items: center; gap: 8px;
  margin: 0; font-size: 13px; color: var(--text); cursor: pointer;
  user-select: none;
}
.login-remember .rmk input[type="checkbox"] {
  width: 16px; height: 16px; margin: 0; flex: none;
  accent-color: var(--accent, #4f7cff); cursor: pointer;
}
.login-remember .rmk-tip {
  margin-top: 6px; font-size: 12px; color: var(--muted); line-height: 1.5;
}
.login-remember .rmk-tip.warn { color: #b46b00; }

/* ---------- z103：日期管理「批量删除」 ---------- */
/* 卡片稍加宽：一行里多了勾选框，原来 520px 会把日期名输入框压得太窄 */
#day-panel .modal-card { width: 600px; }

.day-bulk {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 2px 8px;
  font-size: 12.5px; color: var(--muted);
}
.day-bulk .day-pick-all {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  margin: 0; cursor: pointer; user-select: none; color: var(--text);
}
.day-bulk .day-pick-all input { width: 15px; height: 15px; margin: 0; padding: 0; flex: none; cursor: pointer; }
.day-bulk .day-bulk-info { flex: 1 1 auto; min-width: 0; }
.day-bulk #day-bulk-del { flex: none; padding: 5px 12px; font-size: 12.5px; }
.day-bulk #day-bulk-del:disabled { opacity: .45; cursor: not-allowed; }
.day-bulk #day-bulk-del:not(:disabled) { border-color: var(--danger, #d9534f); color: var(--danger, #d9534f); }
.day-bulk #day-bulk-del:not(:disabled):hover { background: var(--danger, #d9534f); color: #fff; }

/* 注意：.modal-card input 是 width:100% + 内边距，勾选框必须显式定尺寸，
   否则会被撑成一整行（实测会把同行的日期名/按钮全挤到卡片外面） */
#day-list .room-row .day-pick {
  flex: 0 0 auto; width: 15px; height: 15px; min-width: 0;
  margin: 0 2px 0 0; padding: 0; display: inline-block;
  border-radius: 3px; accent-color: var(--accent, #4f7cff); cursor: pointer;
}
#day-list .room-row.day-picked-row { background: rgba(79, 124, 255, .07); }

/* ---------- z104：班课日期「按课程批量删除」+ 已筛选课程条 ---------- */
/* 勾选多天后批量条会盖住工具栏里的课程筛选框，这里把当前筛选结果单独亮出来 */
.cal-tagbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 7px 14px; background: #f6f4ef; border-bottom: 1px solid #e6e1d6;
  font-size: 12.5px; color: var(--muted);
}
.cal-tagbar-label { flex: none; color: var(--text); }
.cal-tagbar-chips { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; flex: 1 1 auto; min-width: 0; }
.cal-tagbar-chip {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  background: #fff; border: 1px solid #ded8ca; color: var(--text); font-size: 12px;
  white-space: nowrap; max-width: 180px; overflow: hidden; text-overflow: ellipsis;
}
/* z116：课程名从「标签」变成「打标按钮」—— 勾好日期点一下就打上，全打上了再点就清掉 */
.cal-tagbar-chip {
  font-family: inherit; line-height: 1.6; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.cal-tagbar-chip:hover { background: var(--accent, #4f7cff); border-color: var(--accent, #4f7cff); color: #fff; }
.cal-tagbar-chip:active { transform: translateY(1px); }
.cal-tagbar-chip:focus-visible { outline: 2px solid var(--accent, #4f7cff); outline-offset: 1px; }
/* 勾的这几天全都已经打上这门课 → 实心；只打了一部分 → 虚线框，一眼看出还差几天 */
.cal-tagbar-chip.on {
  background: #e8f6ec; border-color: #7cc48f; color: #1f7a3d; font-weight: 600;
}
.cal-tagbar-chip.on:hover { background: #cfe9d7; border-color: #7cc48f; color: #1f7a3d; }
.cal-tagbar-chip.part { border-style: dashed; border-color: #c9a227; background: #fffbe9; color: #8a6d00; }
.cal-tagbar-chip.part:hover { background: #f6e7a8; border-color: #c9a227; color: #6b5400; }
/* 还没勾日期：点一下只会提示，视觉上先压暗一点 */
.cal-tagbar-chip.idle { opacity: .75; }
/* 「打上标记」留空时指路：脉冲两下，告诉用户点这里 */
.cal-tagbar-chip.hint { animation: cal-chip-hint 1.6s ease-in-out; }
@keyframes cal-chip-hint {
  0%, 100% { box-shadow: 0 0 0 0 rgba(79, 124, 255, 0); }
  35% { box-shadow: 0 0 0 3px rgba(79, 124, 255, .45); background: #eaf0ff; border-color: #4f7cff; }
  70% { box-shadow: 0 0 0 0 rgba(79, 124, 255, 0); }
}
@media print { .cal-tagbar-chip.hint { animation: none; } }
.cal-tagbar button { flex: none; }
.bulk-bar #btn-cal-delcols:disabled { opacity: .45; cursor: not-allowed; }
.bulk-bar #btn-cal-delcols:not(:disabled) { border-color: var(--danger, #d9534f); color: var(--danger, #d9534f); }
.bulk-bar #btn-cal-delcols:not(:disabled):hover { background: var(--danger, #d9534f); color: #fff; }

/* ==================================================================
   z105：登录广告
   ──────────────────────────────────────────────────────────────────
   ① 控制台设置区（.ad-form）：左文案、右二维码、下方校区开关
   ② 登录后弹出的广告窗（.ad-modal）：白卡片居中，二维码在上、文字在下
   手机端（≤640px）单列、二维码缩到 160px，避免一张广告占满整屏。
   ================================================================== */

/* ---------- ① 控制台：广告设置 ---------- */
.console-ad { margin: 0 0 18px; }
.ad-form {
  border: 1px solid var(--line); border-radius: 12px; background: #fff;
  padding: 14px 16px;
}
.ad-top {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding-bottom: 12px; border-bottom: 1px dashed var(--line);
}
.ad-switch {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 600; cursor: pointer; user-select: none;
}
.ad-switch input { width: 16px; height: 16px; margin: 0; flex: none; }

.ad-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 210px;
  gap: 20px; padding: 14px 0;
}
@media (max-width: 760px) { .ad-grid { grid-template-columns: minmax(0, 1fr); } }

/* .ad-lab 自己是 label：文字在上，输入框在下（不能沿用 .modal-card input 的 width:100%） */
.ad-lab { display: block; font-size: 12px; color: var(--muted); }
.ad-lab > input,
.ad-lab > textarea,
.ad-col textarea {
  display: block; width: 100%; margin-top: 5px;
  padding: 8px 10px; border: 1px solid var(--line-strong); border-radius: 8px;
  font: inherit; color: var(--text); background: #fff;
}
.ad-lab > textarea { resize: vertical; line-height: 1.6; }
.ad-lab > input:focus,
.ad-col textarea:focus { outline: 2px solid var(--accent-soft); border-color: var(--accent); }
.ad-col > .ad-lab + .ad-lab { margin-top: 12px; }
.ad-col > .muted { margin: 8px 0 0; }

.ad-qr-box {
  width: 180px; height: 180px; max-width: 100%;
  display: grid; place-items: center; overflow: hidden;
  border: 1px dashed var(--line-strong); border-radius: 10px; background: #fafbfc;
}
.ad-qr-box.has { border-style: solid; border-color: var(--line); background: #fff; }
.ad-qr-box img { width: 100%; height: 100%; object-fit: contain; display: none; }
.ad-qr-hint { font-size: 12px; color: var(--muted); }
.ad-qr-acts { display: flex; gap: 8px; margin-top: 8px; }
.ad-qr-acts button { flex: none; }

.ad-schools { border-top: 1px dashed var(--line); padding-top: 12px; }
.ad-schools-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.ad-schools-head button { flex: none; }
.ad-school-list { display: flex; flex-wrap: wrap; gap: 8px; }
.ad-school-row {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 5px 11px; border: 1px solid var(--line); border-radius: 999px;
  background: #fff; font-size: 13px;
}
.ad-school-row:hover { border-color: var(--line-strong); }
.ad-school-row input { width: 14px; height: 14px; margin: 0; flex: none; }
.ad-school-row.off { opacity: .62; }
.ad-school-tag {
  font-size: 11px; color: var(--muted); background: #f0f2f5;
  padding: 0 5px; border-radius: 4px;
}

.ad-foot {
  display: flex; align-items: center; gap: 10px;
  padding-top: 12px; margin-top: 12px; border-top: 1px dashed var(--line);
}
.ad-foot button { flex: none; }

/* ---------- ② 登录后的广告弹窗 ---------- */
.ad-modal {
  background: rgba(20, 24, 35, .5); z-index: 120;
  opacity: 0; transition: opacity .16s ease;
}
.ad-modal.on { opacity: 1; }
.ad-card:focus { outline: none; }
.ad-card {
  position: relative; width: 380px; max-width: calc(100vw - 32px);
  max-height: calc(100vh - 48px); overflow: auto;
  background: #fff; border-radius: 16px; padding: 22px 22px 16px;
  box-shadow: 0 18px 50px rgba(20, 24, 35, .24);
  transform: translateY(8px) scale(.98); transition: transform .18s ease;
}
.ad-modal.on .ad-card { transform: none; }
.ad-close {
  position: absolute; right: 8px; top: 6px; width: 30px; height: 30px;
  border: 0; background: transparent; color: var(--muted);
  font-size: 22px; line-height: 1; cursor: pointer; border-radius: 50%;
}
.ad-close:hover { background: #f0f2f5; color: var(--text); }
.ad-body { text-align: center; }
.ad-qr {
  width: 200px; height: 200px; object-fit: contain; display: block;
  margin: 4px auto 14px; border-radius: 8px; background: #fff;
}
.ad-title { font-size: 17px; font-weight: 600; margin: 0 0 8px; }
.ad-text {
  white-space: pre-wrap; text-align: left; font-size: 13.5px; line-height: 1.72;
  color: #333; margin: 0;
}
.ad-foot-pop { display: flex; justify-content: center; margin-top: 16px; }
.ad-foot-pop button { min-width: 128px; }

/* 手机端：.modal 在窄屏是 place-items:stretch（让普通弹窗铺满宽），
   广告卡不能跟着拉满高度 —— 内容少时会被撑成一整块白板，看着像页面坏了。 */
@media (max-width: 640px) {
  .ad-modal { place-items: center; padding: 14px; }
  .ad-card { padding: 18px 16px 14px; max-height: calc(100vh - 28px); }
  .ad-qr { width: 160px; height: 160px; }
  .ad-qr-box { width: 140px; height: 140px; }
}

/* ---------- ③ 预览模式（z106） ----------
   预览窗与真实弹窗只差顶部这条提示：淡黄底 + 换文案，一眼能分清
   「我是在看效果」还是「真的弹给我了」。负外边距把它顶到卡片边缘，
   贴着圆角走，像弹窗自带的横幅，而不是塞在正文里的一段话。 */
.ad-card-preview { padding-top: 14px; }
.ad-prev-tip {
  margin: -14px -22px 14px;
  padding: 8px 40px 8px 14px;
  text-align: left;
  font-size: 12px; line-height: 1.55; color: #8a6a12;
  background: #fff8e1; border-bottom: 1px solid #f2e3b3;
  border-radius: 16px 16px 0 0;
}
@media (max-width: 640px) {
  .ad-prev-tip { margin: -18px -16px 14px; padding-right: 36px; }
}

/* ==================================================================
   z107：用户管理 · 校区列 / 校区筛选
   ──────────────────────────────────────────────────────────────────
   超管看到的是全部校区的账号，原来混在一张表里认不出归属。
   校区做成浅蓝胶囊：它只是归属信息，不该比角色（彩色）更抢眼；
   没有归属的老账号用灰胶囊写「未归属」，一眼能看出这是脏数据。
   ================================================================== */
.u-school-filter { max-width: 190px; }
.u-school-cell { white-space: nowrap; }
.u-school {
  display: inline-block; padding: 1px 9px; border-radius: 999px;
  font-size: 12px; line-height: 1.7; white-space: nowrap;
  color: #2f5fa8; background: #eef3fb; border: 1px solid #dbe6f7;
}
.u-school.none {
  color: var(--muted); background: #f4f5f7; border-color: #e6e8ee;
}
.u-empty { text-align: center; color: var(--muted); padding: 24px 0; }

@media (max-width: 640px) {
  .u-school-filter { max-width: 132px; }
}

/* z107：超管的「新增账号建在哪里」提示。
   平时是灰字说明；筛到别的校区时变成琥珀色警告 —— 这时最容易被误建。 */
#u-scope-hint:not(:empty) { margin-right: 14px; }
#u-scope-hint.warn { color: #b26a00; }

/* ==================================================================
   z108：课程详细（寒暑假专用）· 锁定表头
   ──────────────────────────────────────────────────────────────────
   问题：这张表行数 = 日期数（寒暑假三四十天），一屏根本放不下。表头虽然
   早就写了 position:sticky，但 .grid-scroll 桌面端没有高度上限 —— 内容把
   容器撑到全高、容器自己从不滚动，sticky 就无处可粘。这是 z71 记过的同
   一个坑，当时只补了 课表 / 课表查询 / 个人课表 / 兼职课 四张表，漏了这张。
   实测（z107 生产）：滚 471px 后 thead 跑到 -273px，整条表头消失。

   现在：桌面端给容器高度上限 → 表格内部滚动 → 表头全程吸在顶部。
   表头是【两行】：第一行 = 日期/星期/农历/节日 + 课程名，第二行 = 该课的
   时间段 / 教室。第二行必须贴在第一行下面，否则两行会叠在一起。但第一行
   多高由字体、系统缩放、列宽决定，写死会错位 —— 所以由 renderLedger()
   量出实际高度写进 --lg-th1，这里的 31px 只是 JS 还没跑时的兜底。
   ⚠ 打印时 @media print 已把 .grid-scroll 的 max-height 置 none，不受影响。
   ================================================================== */
@media (min-width: 641px) {
  #view-ledger .grid-scroll { max-height: calc(100vh - 250px); min-height: 340px; }
}
#view-ledger .lg-table thead th { position: sticky; top: 0; z-index: 4; }
#view-ledger .lg-table tr.lg-meta-row th { top: var(--lg-th1, 31px); z-index: 3; }

/* ⚠ 必须换成 separate：border-collapse:collapse 时相邻单元格共享边框，sticky 一滚，
   边框不跟着走，列边界上会留下断续的虚线碎片，缝里还能看见滚过去的课程名残影
   （实测「星期」左边、「节日」左边各有一段 1px 虚线和半个字）。分开之后每个格子
   自带 border-box，sticky 时整格一起走，边界就干净了。边框改成单边画，视觉不变。 */
#view-ledger .lg-table { border-collapse: separate; border-spacing: 0; }
#view-ledger .lg-table th,
#view-ledger .lg-table td { border: 0; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
#view-ledger .lg-table thead tr:first-child th { border-top: 1px solid var(--line); }
/* 这两条被上面的单边化盖掉了，按原样补回来 */
#view-ledger .lg-table tr.lg-meta-row th { border-bottom: 2px solid var(--line); }
#view-ledger .lg-table tr.lg-total td { border-top: 2px solid #e8c33a; }

/* 横向：左侧固定列在横滚时吸住。
   生产上这张表有 60 门课，1600px 屏一次只看得到 17 列，横滚几屏后
   就认不出这一行是哪天了 —— 和纵向丢表头是同一个毛病。
   z119：固定列从 4 个变成 5 个（多了「备注」），而且用户可以在「列设置」里
   藏掉其中任意几个。所以这里不再按 nth-child 写死前四列（藏掉「星期」后
   第 2 格变成农历，nth-child(2) 的 left 还是 0，两列会直接叠在一起），
   改成认 .lg-fx 这个类；每个格子的 left 由 lgStickyMetrics 按实际列宽累加后
   直接写到元素上（内联 left 优先于这里的兜底 0）。
   层级：表头左侧格 6 > 表头课程列 4/3 > 数据区左侧格 2 > 普通格 auto。
   ⚠ 必须写 thead tr:first-child 限定 —— 表头第二行（时间段+教室）的 th
     也是 tr 的前几个孩子，只写 th 会把课程列一起钉到左边。 */
#view-ledger .lg-table thead tr:first-child th.lg-fx { left: 0; z-index: 6; }
#view-ledger .lg-table tbody td.lg-fx {
  position: sticky; left: 0; z-index: 2; background: var(--panel);
}
#view-ledger .lg-table tbody tr:nth-child(even) td.lg-fx { background: #fafbfc; }
/* 周末底色 / 合计行黄底要盖回来，否则吸住的几列在彩色行里会露出一块白 */
#view-ledger .lg-table tbody td.lg-fx.weekend { background: #fbf6ef; }
#view-ledger .lg-table tbody tr.lg-total td.lg-fx { background: #fff3bf; }
/* 最后一个固定列的右缘：一条实线 + 一层淡影，横滚时看得出「这几列是浮在上面的」。
   画 2px 而不是 1px：亚像素列宽下边缘总差那么半像素，缝里会透出滚过去的课程名。
   哪个是「最后一个」由 JS 按当前可见列打 .lg-fx-last（藏了列也不会错位）。 */
#view-ledger .lg-table thead tr:first-child th.lg-fx.lg-fx-last,
#view-ledger .lg-table tbody td.lg-fx.lg-fx-last {
  box-shadow: 2px 0 0 var(--line), 8px 0 10px -6px rgba(15, 23, 42, .18);
}
/* 备注列：左对齐、可折行。给个上限，不然一句 60 字的备注会把课程列挤到屏幕外；
   实际高度由该行最高格子决定，整行会跟着变高，不会互相压字。 */
#view-ledger .lg-table th.lg-note,
#view-ledger .lg-table td.lg-note {
  text-align: left; white-space: normal; word-break: break-word;
  /* ⚠ table-layout 是 auto（z118 为了「按内容排」改的）。这种布局下 td 的
     width 只是「建议」，实测写 width:130px 不生效、列还是被压成 96px
     （= min-width 的值）。所以宽度必须靠 min-width 定：150px 大约一行放得下
     11 个汉字，一句「下午两点家长会，地点 A101」折三行就完，不至于把整行撑高；
     max-width 是上限，再长的备注也不会把 60 门课的表格挤到看不见。 */
  min-width: 150px; max-width: 220px;
  line-height: 1.5; color: var(--muted); font-size: 12px;
  padding: 4px 8px;
}
#view-ledger .lg-table tr.lg-total td.lg-note { color: #b45309; }

/* ==================================================================
   z118：课程详细的列宽「按内容排」，不再被拉满整屏
   ──────────────────────────────────────────────────────────────────
   原来 table.data 的 width:100% 会把表撑满容器：只筛出 2 门课时，
   每列被拉到三四百像素宽，字挤在中间、格子空得能跑马，看着像表格坏了。
   改成 width:auto 后列宽 = 内容宽度 + 内边距，课少时表格就短短一截，
   课多（60 列）时内容本来就超出容器，行为与以前一致（照样横向滚动）。
   课程列给个 58px 下限：整列没排课、表头也只有「—」时不至于窄成一条线。
   ⚠ 打印仍要铺满 A4，所以在 @media print 里改回 100%。 */
#view-ledger .lg-table { width: auto; }
#view-ledger .lg-table th.cal-tag-th,
#view-ledger .lg-table td.lg-on,
#view-ledger .lg-table td.lg-off { min-width: 58px; }

/* ==================================================================
   z123：课程详细「隐藏空行」+ 间隔标识
   ──────────────────────────────────────────────────────────────────
   折叠之后中间会出现断口。断口必须**一眼看出来是「藏了几天」**
   而不是「表格缺了一块 / 数据丢了」，所以标识行做得比其他行醒目：
     · 斜纹底（repeating-linear-gradient）+ 上下虚线边 —— 与真实日期行区分开；
     · 左侧一个深色圆角徽章「已隐藏 N 天」，右侧写起止日期与天数；
     · 行首徽章吸左（横滚时跑不出屏幕）；它的 left 由 JS 按实际列宽写内联；
     · 「显示这一段」是个小按钮，点完临时展开（不改变开关本身）。
   ⚠ 打印时保留标识、去掉按钮（纸上点不了），底色转成浅灰网纹，
     避免打印机把深色斜纹印成一片黑。
   ⚠ 简写：`background: <只有渐变、没写颜色>` 时计算值里的 background-color
     会是 transparent，底下的条纹直接透上来。所以颜色一定跟渐变写进同一条。 */
#view-ledger .lg-table tr.lg-gap td {
  background: #f2f6fa repeating-linear-gradient(45deg,
    #eef2f7 0 8px, #f6f9fc 8px 16px);
  border-top: 1px dashed #9fb3c8;
  border-bottom: 1px dashed #9fb3c8;
  padding: 5px 8px;
  text-align: left;
  vertical-align: middle;
}
#view-ledger .lg-table tbody td.lg-gap-side {
  background: #f2f6fa repeating-linear-gradient(45deg,
    #eef2f7 0 8px, #f6f9fc 8px 16px);
  border-top: 1px dashed #9fb3c8;
  border-bottom: 1px dashed #9fb3c8;
  padding: 5px 8px;
  text-align: left;
  vertical-align: middle;
}
/* ⚠ 「看起来没生效」到底是怎么回事（花了两小时才弄清，写清楚免得下次重查）：
     本文件更早处有两条给固定列上底色的规则（都在 #view-ledger 里写死）：
       tbody td.lg-fx { background: var(--panel) }                 —— 现在已删
       tbody tr:nth-child(2n) td.lg-fx { background: #fafbfc }     —— 见下面 2626 行
     它们专挑「.lg-fx」这个类，而标识行首格恰好也挂 .lg-fx（为了吸左）。
     实测（最小页面可复现）：**只要有一条「带 :nth-child 家族伪类、又命中同一个 td」的
     规则存在，后面那条普通的 tr.lg-gap td 规则就整条不生效**；
     把伪类换成别的（tr:nth-child(2n+0 of .x)）或者删掉那条规则，标识色立刻正常。
     这不是特异性问题（加 .lg-fx、加 tbody、调到 0,3,2 都没用），也不是简写问题
     （纯色一样失效），更不是 !important 能稳定解决的（时灵时不灵）。
     稳妥的解法只有一个：**标识行首格别再用 .lg-fx 这个类** —— 它要吸左，
     但吸左是靠 JS 按列宽写内联 left，用 lgStickyMetrics 里认的另一个类就行。
     → 所以 app.js 里标识行首格改成 .lg-gap-side（不再挂 lg-fx），
       下面这些规则也就不必再跟 .lg-fx 抢底色了。 */

/* 标识行首格：吸左（left 由 JS 写内联）+ 右缘虚线。层级与固定列一致，
   但它不挂 .lg-fx —— 原因见上面那段注释。 */
#view-ledger .lg-table tr.lg-gap td.lg-gap-side {
  position: sticky; left: 0; z-index: 2;
  border-right: 1px dashed #9fb3c8;
  white-space: nowrap;
}
#view-ledger .lg-table tr.lg-gap .lg-gap-badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  background: #334155;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .3px;
}
#view-ledger .lg-table tr.lg-gap .lg-gap-range {
  color: #334155;
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
#view-ledger .lg-table tr.lg-gap .lg-gap-show {
  margin-left: 10px;
  font-size: 12px;
  padding: 1px 8px;
  background: #fff;
}
#view-ledger .lg-table tr.lg-gap .lg-gap-show:hover { background: #eef6ff; }
/* 「隐藏空行」按钮的按下态：与别处的 .on 一致（浅蓝底 + 主题色字） */
#btn-lg-hideempty.on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
@media print {
  #view-ledger .lg-table tr.lg-gap td {
    background-image: repeating-linear-gradient(45deg,
      #f2f2f2 0 6px, #fbfbfb 6px 12px) !important;
    background-color: #f7f7f7 !important;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  #view-ledger .lg-table tr.lg-gap .lg-gap-show { display: none; }
  #view-ledger .lg-table tr.lg-gap .lg-gap-badge {
    background: #e5e7eb !important; color: #334155 !important;
    border: 1px solid #94a3b8;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
}

/* ==================================================================
   z125：「标识文案」面板
   ──────────────────────────────────────────────────────────────────
   一句一年改一两次的话，不该占常驻位置 —— 所以是「隐藏空行」旁边一个按钮，
   点开才见。列表里每条都按样例天数（8 天）渲染成**真实句子**给人看，
   而不是「这 {n} 天未排课」这种带花括号的模板串：选的人得先看懂效果。
   ================================================================== */
.lg-gt-card { width: 540px; max-width: 94vw; padding: 18px 18px 14px; }
.lg-gt-tip { margin: 2px 0 0; line-height: 1.6; }
.lg-gt-list {
  margin-top: 12px; border: 1px solid var(--line);
  border-radius: 8px; overflow: hidden; background: #fcfcfd;
}
/* ⚠ 必须带 .lg-gt-card 前缀：.modal-card label / .modal-card input 是给登录框那种
   「一行一个输入框」写的，特异性比单个类高，会把这里的横排压回 display:block、
   把 radio 拉成 width:100%（z119 的列设置踩过一模一样的坑）。 */
.lg-gt-card .lg-gt-item {
  display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 9px 12px; font-size: 13px; color: inherit;
  cursor: pointer; border-bottom: 1px solid var(--line);
}
.lg-gt-card .lg-gt-item:last-child { border-bottom: 0; }
.lg-gt-card .lg-gt-item:hover { background: #f1f5fa; }
.lg-gt-card .lg-gt-item input[type="radio"] {
  display: inline-block; width: auto; margin: 0; padding: 0;
  flex: 0 0 auto; cursor: pointer; accent-color: var(--accent);
}
.lg-gt-txt { flex: 1 1 auto; min-width: 0; }
.lg-gt-note {
  flex: 0 0 auto; font-size: 11px; color: var(--muted);
  background: #eef2f8; border-radius: 4px; padding: 1px 6px;
}
.lg-gt-cusbox { padding: 10px 12px 12px; border-bottom: 1px solid var(--line); background: #f7f9fc; }
.lg-gt-cusbox.hidden { display: none; }
.lg-gt-card .lg-gt-input { width: 100%; box-sizing: border-box; margin: 0; }
.lg-gt-hint { margin-top: 6px; line-height: 1.6; }
/* 预览：照标识行画一遍（斜纹底 + 深色徽章 + 那句说明），改一个字就能看到效果。
   ⚠ 底色和渐变写同一条 —— 只写渐变的话 background-color 会变 transparent，
     卡片底色透上来，斜纹就看不清了（z123 踩过）。 */
.lg-gt-preview {
  margin-top: 12px; padding: 10px 12px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border-radius: 8px;
  border-top: 1px dashed #9fb3c8; border-bottom: 1px dashed #9fb3c8;
  background: #f2f6fa repeating-linear-gradient(45deg, #eef2f7 0 8px, #f6f9fc 8px 16px);
}
.lg-gt-badge {
  flex: 0 0 auto; background: #334155; color: #fff;
  border-radius: 999px; padding: 2px 9px;
  font-size: 12px; font-weight: 600;
}
.lg-gt-pv { font-size: 12.5px; color: #334155; }
.lg-gt-off { margin: 8px 0 0; }
@media (max-width: 640px) {
  .lg-gt-card { padding: 14px 14px 10px; }
}
@media print {
  #lg-gaptext { display: none !important; }
}

/* z118：课程详细「分享图片」预览层 */
.lg-share-mask {
  position: fixed; inset: 0; z-index: 85;
  background: rgba(15, 23, 42, .62);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.lg-share-mask.hidden { display: none; }
.lg-share-card {
  background: var(--panel); border-radius: 12px; overflow: hidden;
  max-width: 94vw; max-height: 92vh; display: flex; flex-direction: column;
  box-shadow: 0 18px 48px rgba(15, 23, 42, .38);
}
.lg-share-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
.lg-share-head strong { font-size: 14px; }
.lg-share-body { overflow: auto; padding: 12px; background: #e9edf3; flex: 1 1 auto; min-height: 0; }
.lg-share-body img { display: block; max-width: 100%; height: auto; background: #fff; box-shadow: 0 2px 10px rgba(15, 23, 42, .16); }
.lg-share-card .lg-share-tip { margin: 0; padding: 9px 14px; border-top: 1px solid var(--line); flex: 0 0 auto; }
@media (max-width: 640px) {
  .lg-share-card { max-width: 100%; max-height: 94vh; }
  .lg-share-head { flex-wrap: wrap; }
}

/* 打印时表格照旧铺满 A4（课少的时候纸面上也不该只占左上角）；
   预览层属于交互浮层，永远不进打印稿。 */
@media print {
  #view-ledger .lg-table { width: 100%; }
  .lg-share-mask { display: none !important; }
}

/* ==================================================================
   z119：课程详细「列设置」面板
   ──────────────────────────────────────────────────────────────────
   一个列表放 60 个勾选框，所以卡片要宽一点、课程那一段单独滚动。
   勾选框用原生 input（跨浏览器样子一致，也自带键盘可达性），
   外面的 label 负责整行可点 —— 点课程名也能勾上，不用非点那个小方块。
   ================================================================== */
.lg-cs-card { width: 460px; max-width: 94vw; padding: 18px 18px 14px; }
.lg-cs-tip { margin: 2px 0 0; line-height: 1.6; }
.lg-cs-sec { margin-top: 14px; }
.lg-cs-head {
  display: flex; align-items: center; gap: 6px;
  margin-bottom: 8px; font-size: 13px;
}
.lg-cs-head strong { font-weight: 600; }
.lg-cs-fx { display: flex; flex-wrap: wrap; gap: 4px 8px; }
.lg-cs-tags {
  margin-top: 8px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px 10px; max-height: 40vh; overflow: auto;
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; background: #fcfcfd;
}
/* ⚠ 必须带 .lg-cs-card 前缀：.modal-card label / .modal-card input 这两条
   （给登录框那种「一行一个输入框」用的）特异性比单个类高，会把这里的
   flex 行压回 display:block、把勾选框拉成 width:100% ——
   第一版就是这样：勾选框独占一行、课程名被挤到下面，看着像坏了。 */
.lg-cs-card .lg-cs-item {
  display: flex; align-items: center; gap: 6px;
  margin: 0; padding: 4px 2px; font-size: 13px; color: inherit;
  cursor: pointer; border-radius: 5px; min-width: 0;
}
.lg-cs-card .lg-cs-item:hover { background: #eef2f8; }
.lg-cs-card .lg-cs-item input[type="checkbox"] {
  display: inline-block; width: auto; margin: 0; padding: 0;
  flex: 0 0 auto; cursor: pointer;
}
.lg-cs-name {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lg-cs-card .lg-cs-search { width: 100%; box-sizing: border-box; margin: 0; }
.lg-cs-acts { margin-top: 14px; }
@media (max-width: 640px) {
  .lg-cs-card { padding: 14px 14px 10px; }
  .lg-cs-tags { grid-template-columns: 1fr; max-height: 34vh; }
}
@media print {
  #lg-colset { display: none !important; }
}
