:root {
  --primary: #2f6fed;
  --bg: #f4f6fa;
  --card: #ffffff;
  --text: #24324a;
  --muted: #7a869c;
  --border: #e3e8f0;
  --green: #1fa463;
  --orange: #f59e0b;
  --red: #e5484d;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
  line-height: 1.6;
}
/* 全宽显示：内容铺满屏幕（原来 960px 居中会缩在中间），仅保留左右内边距 */
.container { max-width: none; margin: 0 auto; padding: 20px 22px 60px; }
/* 测试日期快捷切换小按钮（录入页 / 总览页） */
.date-chip {
  display: inline-block;
  border: 1px solid var(--border);
  background: #f7f9fd;
  color: #33415c;
  font-size: 12px;
  padding: 3px 9px;
  border-radius: 20px;
  margin: 2px 4px 2px 0;
  cursor: pointer;
  line-height: 1.5;
}
.date-chip:hover { background: #e8effc; border-color: #b9cdf5; color: var(--primary); }
.date-chip.active { background: var(--primary); border-color: var(--primary); color: #fff; }
/* 测试批次切换条（班级详情页，2026-10-12 张老师加）——按测试日期切批次看成绩 */
a.date-chip { text-decoration: none; }
.batch-bar { display: flex; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.batch-lab { font-size: 12px; color: var(--muted); font-weight: 600; margin-right: 8px; }
.date-chip .chip-n { margin-left: 5px; font-size: 11px; opacity: .7; }
.date-chip.active .chip-n { opacity: .95; }
.batch-cur {
  display: inline-block; margin-left: 8px; padding: 1px 9px; border-radius: 6px;
  font-size: 12px; font-weight: 600; color: #fff; background: var(--primary);
  vertical-align: middle; letter-spacing: .3px;
}
a.date-link {
  color: var(--primary); text-decoration: none;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
a.date-link:hover { text-decoration: underline; }
input[type="date"] { font-family: inherit; font-size: 14px; }
header.topbar {
  background: var(--primary);
  color: #fff;
  padding: 14px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
header.topbar .logo { font-size: 18px; font-weight: bold; }
header.topbar a { color: #dce6ff; text-decoration: none; font-size: 14px; margin-left: 16px; }
header.topbar a:hover { color: #fff; }
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 20px;
  margin-top: 20px;
  box-shadow: 0 1px 3px rgba(30, 50, 90, .05);
}
.card h2 { font-size: 17px; margin-bottom: 14px; }
.form-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
input[type=text], input[type=number], input[type=password], select {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 14px;
  background: #fff;
  color: var(--text);
}
input:focus, select:focus { outline: none; border-color: var(--primary); }
button, .btn {
  display: inline-block;
  background: var(--primary);
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 8px 18px;
  font-size: 14px;
  cursor: pointer;
  text-decoration: none;
}
.btn-secondary { background: #eef2f9; color: var(--text); }
.btn-danger { background: var(--red); }
.btn-orange2 { background: var(--orange); }
button:hover, .btn:hover { opacity: .9; }
table.list {
  width: 100%;
  /* sticky（冻结表头 / 冻结列）在 border-collapse:collapse 下会丢边框，改用 separate */
  border-collapse: separate;
  border-spacing: 0;
  margin-top: 12px;
  font-size: 14px;
}
table.list th, table.list td {
  border-bottom: 1px solid var(--border);
  /* 行高压缩：原 8px → 2px，配合下面的行内元素压缩，一屏能多显示约 60% 的学生 */
  padding: 2px 9px;
  text-align: center;
  white-space: nowrap;
  line-height: 1.25;
  /* 顶行对齐：录入页每行是「输入框 + 下方得分」两行结构，行高被撑到 ~48px。
     单元格默认 vertical-align:middle，纯文字的「学号 / 姓名」会被顶到正中间，
     看起来上方全是空白。改成 top 后文字贴着行顶，和输入框第一行对齐。 */
  vertical-align: top;
}
table.list th { background: #f0f4fb; color: var(--muted); font-weight: 600; }
/* 表格里的输入控件也跟着压缩，否则输入框会把行撑高 */
table.list input[type=text], table.list input[type=number] {
  padding: 0 6px; font-size: 13px; border-radius: 4px; line-height: 1.25;
}
table.list select { padding: 0 6px; font-size: 13px; border-radius: 4px; line-height: 1.25; }
/* 成绩录入页：单元格里「输入框 + 得分」要贴紧，选中框本身也不再留内边距 */
table.list .score-input { padding: 0 6px; height: 22px; }
table.list tr.stu-row td { padding-top: 1px; padding-bottom: 1px; }
.tag {
  display: inline-block;
  white-space: nowrap;   /* 「不及格」三个字在窄列里会被拆成竖排 */
  padding: 1px 10px;
  border-radius: 20px;
  font-size: 13px;
  color: #fff;
}
.tag-优秀 { background: var(--green); }
.tag-良好 { background: var(--primary); }
.tag-及格 { background: var(--orange); }
.tag-不及格 { background: var(--red); }
.msg { margin: 12px 0; padding: 10px 14px; border-radius: 8px; font-size: 14px; }
.msg-ok { background: #e8f7ef; color: #14724a; border: 1px solid #bfe8d2; }
.msg-err { background: #fdeeee; color: #b3261e; border: 1px solid #f5c6c6; }
.muted { color: var(--muted); font-size: 13px; }
.stat-grid { display: flex; gap: 14px; flex-wrap: wrap; }
.stat {
  flex: 1 1 160px;
  background: #f7f9fd;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 18px;
  text-align: center;
}
.stat .num { font-size: 26px; font-weight: bold; color: var(--primary); }
.tabs { margin-top: 20px; display: flex; gap: 8px; flex-wrap: wrap; }
.tabs a {
  padding: 7px 16px;
  background: #eef2f9;
  border-radius: 6px;
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
}
.tabs a.active { background: var(--primary); color: #fff; }
textarea {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px;
  font-size: 14px;
  min-height: 140px;
  font-family: Consolas, monospace;
}

/* ---------- 趋势图指标切换标签 ---------- */
.chart-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 4px;
}
.chart-tab {
  background: #eef2f9;
  color: var(--text);
  border: 1px solid transparent;
  border-radius: 20px;
  padding: 5px 14px;
  font-size: 13px;
  cursor: pointer;
  transition: all .15s;
}
.chart-tab:hover { background: #e2e9f5; }
.chart-tab.active {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
}

/* ---------- 等级分布条 ---------- */
.lv-bars { display: flex; flex-direction: column; gap: 8px; }
.lv-row { display: flex; align-items: center; gap: 12px; }
.lv-name { width: 56px; font-size: 13px; color: var(--muted); flex: none; }
.lv-track {
  flex: 1;
  height: 18px;
  background: #eef2f9;
  border-radius: 9px;
  overflow: hidden;
}
.lv-fill {
  height: 100%;
  border-radius: 9px;
  transition: width .5s ease;
  min-width: 0;
}
.lv-num { width: 130px; font-size: 13px; color: var(--text); flex: none; text-align: right; }

/* ---------- 边缘生（班级详情页）---------- */
.edge-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; margin-top: 14px; }
.edge-col {
  position: relative;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px 14px;
  box-shadow: 0 1px 3px rgba(30, 50, 90, .06);
  border-top: 3px solid #cbd5e6;
  overflow: hidden;
}
.edge-top  { border-top-color: #f59e0b; background: linear-gradient(180deg, #fffaf0 0%, #fff 46%); }
.edge-pass { border-top-color: #e5484d; background: linear-gradient(180deg, #fff5f5 0%, #fff 46%); }
.edge-good { border-top-color: #2f6fed; background: linear-gradient(180deg, #f4f8ff 0%, #fff 46%); }
.edge-head { display: flex; align-items: center; gap: 7px; }
.edge-ico { font-size: 15px; line-height: 1; }
.edge-title { font-size: 14px; font-weight: 700; color: var(--text); }
.edge-tag {
  margin-left: auto;
  font-size: 11.5px;
  font-weight: 600;
  padding: 1px 9px;
  border-radius: 20px;
  background: #eef2f9;
  color: var(--muted);
  white-space: nowrap;
}
.edge-pass .edge-tag { background: #fdecec; color: #c62f34; }
.edge-good .edge-tag { background: #e9f0ff; color: #2558c4; }
.edge-top  .edge-tag { background: #fff2db; color: #a86a05; }
.edge-zone { margin: 10px 0 2px; }
.edge-range { font-size: 12px; font-weight: 700; color: var(--text); }
.edge-bar { position: relative; height: 6px; margin: 5px 0 4px; border-radius: 3px; background: #eef2f9; }
.edge-bar i { position: absolute; top: 0; bottom: 0; background: currentColor; border-radius: 3px; }
.edge-pass .edge-bar { color: #e5484d; }
.edge-good .edge-bar { color: #2f6fed; }
.edge-cmp { font-size: 11.5px; color: var(--muted); }
.edge-cmp s { text-decoration-thickness: 1px; }
.edge-rows { list-style: none; margin: 10px 0 0; padding: 0; }
.edge-rows li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  border-top: 1px dashed var(--border);
  font-size: 13px;
}
.edge-rows li:first-child { border-top: none; }
.edge-rk {
  flex: none;
  width: 19px; height: 19px;
  line-height: 19px;
  text-align: center;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 700;
  background: #eef2f9;
  color: var(--muted);
}
.edge-rk.rk1 { background: #f6c445; color: #6b4700; }
.edge-rk.rk2 { background: #c9d3e2; color: #33415c; }
.edge-rk.rk3 { background: #e8b583; color: #6b3f14; }
.edge-nm { flex: 1 1 auto; color: var(--text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.edge-gap { flex: none; font-size: 11.5px; color: var(--muted); }
.edge-sc { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; }
.edge-sc-top  { color: var(--orange); }
.edge-sc-pass { color: var(--red); }
.edge-sc-good { color: var(--primary); }
.edge-empty { margin: 10px 0 0; font-size: 12.5px; color: var(--muted); }
@media (max-width: 900px) { .edge-grid { grid-template-columns: 1fr; } }


/* ---------- 总分等级标签补充色 ---------- */
.tag-优秀 { background: var(--green); }
.tag-良好 { background: var(--primary); }
.tag-及格 { background: var(--orange); }
.tag-不及格 { background: var(--red); }

/* ---------- 单项明细折叠块 ---------- */
details summary { outline: none; }
details[open] summary { margin-bottom: 4px; }

/* ---------- 成绩录入实时算分 ---------- */
.score-input { display: block; }
/* 得分小字：紧贴输入框下方，不留上下列空隙（顶行后原来的留白在下方也一并去掉） */
.item-score {
  font-size: 11px;
  color: #94a3b8;
  margin: 0;
  line-height: 1.05;
  min-height: 12px;
  transition: color .15s;
}
/* 实时总分与等级并排一行：原来总分、等级各占一行，整行高度被这一列撑到 ~70px，
   并排后省下约一行的高度，表格整体更紧凑 */
.live-line {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 5px;
  line-height: 1.15;
}
.live-total { line-height: 1.15; }
.live-level { line-height: 1.15; min-height: 0; }
tr.stu-row:hover td { background: #f8fbff; }

@media (max-width: 640px) {
  .lv-name { width: 48px; font-size: 12px; }
  .lv-num { width: auto; font-size: 12px; }
  .chart-tab { padding: 4px 10px; font-size: 12px; }
  .item-score { font-size: 10px; }

  /* ---------- 手机上冻结「学号」「姓名」两列 ----------
     表格横向滚动时，前两列固定不动，方便核对是谁的成绩。

     ⚠️ 关键前提（两个，缺一个都不生效）：
     1) 表格必须有 class="list freeze-col"、外层必须有 class="table-scroll"
        —— 这些选择器要求 HTML 里真的有这两个类名。旧版 admin.php 用的是
        内联 style="overflow-x:auto" 且表格只有 class="list"，
        那么下面的规则一条都匹配不上，CSS 再对也不会冻结。
     2) table.list 基础样式里有 width:100%，会把表格强行压进容器宽度，
        导致外层 .table-scroll 的 overflow-x 永远不触发横向滚动 —— 那时滚动的
        其实是整个页面，sticky 只相对视口生效，看起来就是「没冻结」。
        所以这里必须把表格改成 max-content，让它真正溢出、由外层容器滚动。 */
  table.list.freeze-col {
    width: max-content;      /* 让表格按内容自然撑开，超出容器才会滚动 */
    min-width: 100%;
    border-collapse: separate;   /* sticky 在 collapse 下边框会丢失，改用 separate */
    border-spacing: 0;
  }
  /* 外层滚动容器：限定最大宽度 + 横向滚动 + iOS 惯性滚动 */
  .table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
  }
  table.list.freeze-col th,
  table.list.freeze-col td {
    border-bottom: none;
    box-shadow: inset 0 -1px 0 var(--border);
    background: #fff;        /* sticky 格必须有不透明背景 */
  }
  table.list.freeze-col th {
    background: #f0f4fb;
  }
  /* 第 1 列：学号（7 位数字，给足宽度避免被裁切） */
  table.list.freeze-col th.col-no,
  table.list.freeze-col td.col-no {
    position: sticky;
    left: 0;
    z-index: 2;
    width: 78px;
    min-width: 78px;
    max-width: 78px;
    padding-left: 6px;
    padding-right: 6px;
  }
  /* 第 2 列：姓名 —— left 值必须等于第 1 列的实际宽度 */
  table.list.freeze-col th.col-name,
  table.list.freeze-col td.col-name {
    position: sticky;
    left: 78px;
    z-index: 2;
    width: 62px;
    min-width: 62px;
    max-width: 62px;
    padding-left: 6px;
    padding-right: 6px;
    /* 冻结区右缘加一道阴影，提示「这边可以左右滑」 */
    box-shadow: inset 0 -1px 0 var(--border), 6px 0 6px -4px rgba(30, 50, 90, .12);
  }
  /* 表头整体固定在顶部；被冻结的两格还要再高一档，否则会被数据行的冻结格盖住 */
  table.list.freeze-col th { position: sticky; top: 0; z-index: 3; }
  table.list.freeze-col th.col-no,
  table.list.freeze-col th.col-name { z-index: 4; }
  /* 冻结格有自己的背景色，会盖住行的 hover 效果，这里补回来 */
  tr.stu-row:hover td.col-no,
  tr.stu-row:hover td.col-name {
    background: #f8fbff;
  }
}

/* ---------- 兜底：平板 / 大屏手机横屏（641px ~ 900px）也要冻结 ----------
   上面那段只在 ≤640px 生效。但「手机端」不等于必然 ≤640px：
   部分大屏手机横屏、平板竖屏的 CSS 宽度会落在 641~900px 之间，
   那时上面整段都不命中，用户就会觉得「手机上没冻结」。
   这里用同一套规则覆盖到 900px，保证只要表格需要横向滚动就始终冻结。 */
@media (min-width: 641px) and (max-width: 900px) {
  table.list.freeze-col {
    width: max-content;
    min-width: 100%;
    border-collapse: separate;
    border-spacing: 0;
  }
  .table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
  }
  table.list.freeze-col th,
  table.list.freeze-col td {
    border-bottom: none;
    box-shadow: inset 0 -1px 0 var(--border);
    background: #fff;
  }
  table.list.freeze-col th { background: #f0f4fb; }
  table.list.freeze-col th.col-no,
  table.list.freeze-col td.col-no {
    position: sticky;
    left: 0;
    z-index: 2;
    width: 78px; min-width: 78px; max-width: 78px;
    padding-left: 6px; padding-right: 6px;
  }
  table.list.freeze-col th.col-name,
  table.list.freeze-col td.col-name {
    position: sticky;
    left: 78px;
    z-index: 2;
    width: 62px; min-width: 62px; max-width: 62px;
    padding-left: 6px; padding-right: 6px;
    box-shadow: inset 0 -1px 0 var(--border), 6px 0 6px -4px rgba(30, 50, 90, .12);
  }
  table.list.freeze-col th { position: sticky; top: 0; z-index: 3; }
  table.list.freeze-col th.col-no,
  table.list.freeze-col th.col-name { z-index: 4; }
  tr.stu-row:hover td.col-no,
  tr.stu-row:hover td.col-name { background: #f8fbff; }
}

/* =====================================================================
   表头冻结（第一行常驻）+ 表格内滚动        —— 2026-09-15 新增
   ---------------------------------------------------------------------
   为什么必须要有「带 max-height 的滚动容器」？
   CSS 规定：overflow-x 只要不是 visible，overflow-y 就会被强制算成 auto。
   所以原来那些 <div style="overflow-x:auto"> 其实早就已经是「双向」滚动容器，
   只是它们没有 max-height、高度永远等于内容高度，纵向永远不会真正滚动；
   而 th { position: sticky; top: 0 } 是相对「最近的滚动祖先」定位的，
   等不到纵向滚动量，表头自然就一直不吸顶 —— 看起来就是「没冻结」。
   给容器加上 max-height 之后，纵向滚动真正发生，表头才会稳定吸顶。
   ===================================================================== */
.tbl-wrap {
  max-width: 100%;
  max-height: 72vh;                 /* 表格最高占屏幕 72%，其余留给页面其它内容 */
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}
.tbl-wrap table.list { margin-top: 0; }

/* 行数不多、要求「一次全铺开」的表格（如 各班明细）：
   解除 72vh 限高，容器高度跟着行数长，不出现容器内滚动条。
   表头仍然吸顶 —— 变成跟着页面滚动吸在视口顶部。 */
.tbl-wrap.tbl-full { max-height: none; overflow: visible; }

/* 表头常驻：纵向滚动表格时，第一行始终吸在容器顶部 */
.tbl-wrap table.list th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: #f0f4fb;              /* 必须不透明，否则下面的数据行会透上来 */
  box-shadow: inset 0 -1px 0 var(--border), 0 3px 6px -4px rgba(30, 50, 90, .18);
}
.tbl-hint {
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}

/* 小屏：表格再矮一点，给页面其它内容留空间 */
@media (max-width: 640px) {
  .tbl-wrap { max-height: 66vh; }
}

/* ---------- 成绩批量导入：预览结果 ---------- */
.prev-sum { display: flex; gap: 10px; flex-wrap: wrap; margin: 6px 0 12px; }
.prev-sum .pill {
  background: #f7f9fd;
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 4px 14px;
  font-size: 13px;
}
.prev-sum .pill b { font-size: 15px; }
.prev-ok   { color: #14724a; font-weight: 600; }
.prev-warn { color: #b45309; font-weight: 600; }
.prev-bad  { color: #b3261e; font-weight: 600; }
.prev-merge { color: #7c3aed; font-weight: 600; }

/* ---------- 成绩录入：去掉数字框自带的上下微调按钮 ----------
   成绩靠手工直接敲，不需要点箭头一点点调；去掉后格子更干净，
   也不会手滑点到箭头把刚填好的数字改掉。
   方向键交给脚本接管：↓ / 回车 往下跳一格（下一个同学）。 */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.score-input { padding-right: 8px; }

/* =====================================================================
   窄列模式（成绩录入 / 全班成绩总览的明细表）        —— 2026-09-23
   ---------------------------------------------------------------------
   项目列压到约 3 个汉字宽（54px），学号 / 姓名列同步收窄，一屏放下更多列。
   注意：
   1) 基础样式 th/td 是 white-space:nowrap，表头不换行列宽就压不下去，
      compact 里必须放开为 normal 才生效；
   2) 冻结列的 left 偏移依赖 col-no 实际宽度，compact 下两者要一起改
      （这段放在文件末尾，同特异性下后者覆盖前面的媒体查询规则）。
   ===================================================================== */
table.list.compact th, table.list.compact td { padding: 7px 3px; }
table.list.compact th { white-space: normal; line-height: 1.25; font-size: 12px; }
table.list.compact td { font-size: 13px; }
/* 项目列：约 3 个汉字宽（56px 刚好放得下「仰卧起坐」四个字，不折行） */
table.list.compact th.item-col,
table.list.compact td.item-col {
  width: 56px; min-width: 56px; max-width: 56px;
}
/* 学号 / 姓名列收窄 */
table.list.compact th.col-no,
table.list.compact td.col-no {
  width: 70px; min-width: 70px; max-width: 70px;
  padding-left: 5px; padding-right: 5px;
}
table.list.compact th.col-name,
table.list.compact td.col-name {
  width: 54px; min-width: 54px; max-width: 54px;
  padding-left: 5px; padding-right: 5px;
}
/* 录入表格的输入框跟随窄列 */
table.list.compact .score-input {
  width: 52px; padding: 6px 2px; text-align: center; font-size: 12px;
}
/* compact + freeze：第 2 列 left 必须等于第 1 列实际宽度 70px */
table.list.compact.freeze-col th.col-no,
table.list.compact.freeze-col td.col-no { left: 0; }
table.list.compact.freeze-col th.col-name,
table.list.compact.freeze-col td.col-name { left: 70px; }

/* 成绩明细里每个项目格下的「单项得分」小字
   着色跟录入页的实时得分一致：≥90 绿 / ≥80 蓝 / ≥60 橙 / <60 红 */
td.has-sub .sub {
  display: block;
  margin-top: 1px;
  font-size: 11px;
  line-height: 1.2;
  font-weight: 600;
}
td.has-sub .sub-a { color: #16a34a; }
td.has-sub .sub-b { color: #2f6fed; }
td.has-sub .sub-c { color: #d97706; }
td.has-sub .sub-d { color: #dc2626; }
/* 跳绳附加分：紧跟在跳绳单项得分后面（如 100.0+12），不另起一行 */
td.has-sub .sub { white-space: nowrap; }
td.has-sub .sub .sub-plus { font-style: normal; font-size: 0.9em; color: #dc2626; margin-left: 1px; }

/* 成绩明细里每个项目格下的「测试日期」小字
   （2026-10-12 张老师：最新汇总模式下，分几次测的项目拼在一行，每项标注自己测于哪天） */
.proj-date {
  display: block;
  margin-top: 1px;
  font-size: 10px;
  line-height: 1.15;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* =====================================================================
   大屏（≥901px）冻结学号 / 姓名列        —— 2026-09-23
   ---------------------------------------------------------------------
   上面那两段冻结规则只覆盖到 900px。但电脑上表格一样会横向滚动，
   不补这段就会出现「手机上是冻结的、电脑上姓名照样滑走」。
   只作用于带 .freeze-wide 的表（成绩总览明细表），避免影响成绩录入表
   —— 录入表靠输入框撑列，改 width:max-content 会逼出多余的横向滚动条。
   ===================================================================== */
@media (min-width: 901px) {
  table.list.freeze-col.freeze-wide {
    width: max-content;
    min-width: 100%;
    border-collapse: separate;
    border-spacing: 0;
  }
  table.list.freeze-col.freeze-wide th,
  table.list.freeze-col.freeze-wide td {
    border-bottom: none;
    box-shadow: inset 0 -1px 0 var(--border);
    background: #fff;
  }
  table.list.freeze-col.freeze-wide th { background: #f0f4fb; }
  table.list.freeze-col.freeze-wide th.col-no,
  table.list.freeze-col.freeze-wide td.col-no {
    position: sticky;
    left: 0;
    z-index: 2;
    padding-left: 5px; padding-right: 5px;
  }
  table.list.freeze-col.freeze-wide th.col-name,
  table.list.freeze-col.freeze-wide td.col-name {
    position: sticky;
    left: 70px;                 /* = compact 下 col-no 的实际宽度 */
    z-index: 2;
    padding-left: 5px; padding-right: 5px;
    box-shadow: inset 0 -1px 0 var(--border), 6px 0 6px -4px rgba(30, 50, 90, .12);
  }
  table.list.freeze-col.freeze-wide th { position: sticky; top: 0; z-index: 3; }
  table.list.freeze-col.freeze-wide th.col-no,
  table.list.freeze-col.freeze-wide th.col-name { z-index: 4; }
}

/* =====================================================================
   一屏看完全班（2026-09-24）        —— 录入成绩 / 成绩总览
   ---------------------------------------------------------------------
   一个班 42~44 人，单栏表格要 1600+ px 高，无论怎么压行高都塞不进一屏，
   所以纵向减半的唯一办法是「并排两栏」：
       把学生按顺序切成前后两半，左边一栏前 22 人、右边一栏后 22 人。
   配套三件事：
   1) 容器放宽 —— .container 默认 max-width:960px，两栏根本放不下，
      宽表页改用 .container-wide；
   2) 行高压缩 —— .fit 把输入框 / 得分小字 / 内边距全部收紧；
   3) 容器一律不限高 —— .tbl-wrap 的 72vh 限制在成绩页全部解除（.page-scores），
      表格完整向下铺开、跟着页面一起滚；**不要表格内部的小滚动条**。
   窄屏（≤1240px）放不下两栏就自动退回单栏（表格变长，照样走页面滚动）。
   ===================================================================== */
.container-wide { max-width: none; }

/* 宽表页：卡片标题压紧，纵向空间全让给学生列表 */
.card-tight { padding-top: 12px; }
.card-tight h2 { margin-bottom: 6px; font-size: 16px; }

/* 两栏之间必须有可见的分隔：否则左栏「总分/等级」紧贴右栏「学号」，
   一眼看过去像一张被截断的宽表，容易把两栏的行看串。 */
.score-grid { display: flex; align-items: flex-start; }
.score-grid > .sg-col { flex: 1 1 0; min-width: 0; padding: 0 14px; }
.score-grid > .sg-col:first-child { padding-left: 0; }
.score-grid > .sg-col:last-child { padding-right: 0; border-left: 1px solid var(--border); }
/* 成绩表一律不限高（含窄屏降级成单栏时）：表格完整向下铺开，超出屏幕就
   跟着页面一起滚，不要表格内部那个小滚动条 —— 滚轮容易卡、表尾还看不见。 */
.page-scores .tbl-wrap { max-height: none; }

/* ---- 超紧凑行（fit）---- */
table.list.fit { width: 100%; }
table.list.fit th, table.list.fit td { padding: 0 2px; }
table.list.fit th { font-size: 11px; line-height: 1.15; padding: 3px 2px; }
table.list.fit td { font-size: 12px; line-height: 1.15; }
/* 录入页：得分小字在输入框「下方」（2026-10-04 按张老师要求，从并排改回上下）。
   上下排布每行比并排多约 7px，所以字号/行高都压到极限，把高度尽量赚回来；
   输入框宽 100% 与分数上下对齐居中，比原来并排时更好读。
   ⚠️ 千万别写成 td { display:flex } —— td 一旦不是 table-cell，同一行里的
   8 个项目格会各自变成块级元素垂直堆叠，行高会炸到 170px+。
   两个元素各自 block，td 保持 table-cell。 */
table.list.fit td.item-col .score-input {
  display: block; width: 100%; min-width: 0; margin: 0;
}
table.list.fit td.item-col .item-score {
  display: block; width: 100%; min-height: 0; height: auto;
  text-align: center; font-size: 9px; line-height: 1.1; overflow: hidden;
  white-space: nowrap;
}
table.list.fit .score-input { height: 19px; padding: 0 2px; font-size: 11px; border-radius: 3px; }
/* 录入页：跳绳得分小字后面的红色附加分（+N） */
table.list.fit td.item-col .item-score .sub-plus {
  font-style: normal; font-weight: 700; color: #dc2626; margin-left: 1px;
}
/* 总览明细表里项目格下的单项得分小字 */
table.list.fit td.has-sub .sub { font-size: 10px; line-height: 1.1; margin-top: 0; }

/* 列宽：按两栏并排放得下来定（本校学号 5 位、姓名最长 4 字） */
table.list.fit th.col-no, table.list.fit td.col-no {
  width: 62px; min-width: 62px; max-width: 62px; padding-left: 3px; padding-right: 3px;
}
table.list.fit th.col-name, table.list.fit td.col-name {
  width: 54px; min-width: 54px; max-width: 54px; padding-left: 3px; padding-right: 3px;
}
table.list.fit th.item-col, table.list.fit td.item-col {
  width: 56px; min-width: 56px; max-width: 56px;
}
/* 总分 / 等级并排一项，别把它挤到换行 */
table.list.fit .live-line { gap: 3px; }
/* 冻结列的 left 偏移要跟着 col-no 的新宽度走（62px，不是 compact 的 70px） */
table.list.fit.freeze-col th.col-name,
table.list.fit.freeze-col td.col-name { left: 62px; }
/* 分栏时表格铺满整栏宽度，别按内容缩成一条 */
table.list.fit.freeze-col.freeze-wide { width: 100%; min-width: 100%; max-width: 100%; }
/* 等级标签在超紧凑行里收紧 */
table.list.fit .tag { padding: 0 6px; font-size: 11px; border-radius: 10px; }

/* 大屏（表格宽度放得下、不会横向溢出）时把外层滚动容器放开：
   .tbl-wrap 一旦是「滚动容器」，sticky 表头就只能相对它定位，而它纵向永不滚动，
   表头就永远不吸顶。overflow:visible 后表格完全参与页面流 —— 页面往下滚时，
   第一行表头会吸在视口顶部常驻，长表滚到中途也知道每一列是什么。 */
@media (min-width: 1400px) {
  .page-scores .tbl-wrap { overflow: visible; }
}

@media (max-width: 1240px) {
  .score-grid { flex-wrap: wrap; }
  .score-grid > .sg-col { flex: 1 1 100%; padding: 0; }
  .score-grid > .sg-col:last-child { border-left: none; margin-top: 18px; }
  /* 降级成单栏后表格会很长 —— 依然不限高，交给页面滚动 */
}

/* =====================================================================
   代入历史身高体重（2026-10-04）
   ---------------------------------------------------------------------
   身高体重这类前后变化很小的项目不必每次重测：老师选一天点「代入」，
   页面把那天每人的身高/体重填进**空的**格子里（已有的值不覆盖），
   并打上标记（浅黄底 + 虚线框），和手测值一眼区分。
   代入值参与实时算分、只作参考；是否写进本次成绩，由保存按钮旁的
   开关「把代入的身高体重一起保存」决定（不勾选 = 只参考不落库）。
   ===================================================================== */
.score-input.is-inherit {
  background: #fdf6e3;
  border: 1px dashed #e0b752;
}
.inherit-row { margin-top: 8px; }
.inherit-row select { padding: 5px 8px; font-size: 13px; }
.btn-mini { padding: 5px 14px; font-size: 13px; }
.chk-inherit {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--text); cursor: pointer; user-select: none;
}
.chk-inherit input { width: 14px; height: 14px; cursor: pointer; flex: none; }


/* =====================================================================
   学生管理（2026-10-09）
   查询栏 / 工具栏 / 添加-导入折叠面板 / 分页 / 行内操作 / 学生详情
   ===================================================================== */
.stu-search { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; }
/* 姓名 / 学号：显式定宽。原来只写了 min-width，实际宽度由 <input> 默认 size=20 撑到
   192px；两个输入框 + 5 个下拉合起来超过 1000px，窄一点的窗口「查询」按钮就被挤到
   第二行（实测 1024px 视口必现）。 */
.stu-search input[type=text] { width: 138px; min-width: 0; flex: 0 0 auto; }
.stu-search select { flex: 0 0 auto; }
.stu-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.stu-toolbar .spacer { flex: 1; }

.pager { display: flex; gap: 6px; align-items: center; justify-content: flex-end; margin-top: 14px; flex-wrap: wrap; }
.pager a, .pager span.cur {
  padding: 4px 11px; border: 1px solid var(--border); border-radius: 6px;
  font-size: 13px; text-decoration: none; color: var(--text); background: #fff;
}
.pager a:hover { border-color: var(--primary); color: var(--primary); }
.pager span.cur { background: var(--primary); color: #fff; border-color: var(--primary); }
.pager .info { margin-right: auto; color: var(--muted); font-size: 13px; }

.act { display: inline-flex; gap: 12px; }
.act a { font-size: 13px; text-decoration: none; color: var(--primary); }
.act a:hover { text-decoration: underline; }
.act a.danger { color: var(--red); }

.stu-head { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.stu-avatar {
  width: 56px; height: 56px; border-radius: 50%; background: var(--primary); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 24px; font-weight: bold; flex: none;
}
.kv { display: flex; flex-wrap: wrap; gap: 6px 26px; font-size: 14px; }
.kv b { color: var(--muted); font-weight: 400; }
/* ===== 右侧抽屉（添加学生 / 批量导入学生）===== */
body.drawer-lock { overflow: hidden; }
.drawer-mask {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0;
  background: rgba(22, 32, 50, .38);
  opacity: 0; visibility: hidden;
  transition: opacity .2s ease, visibility .2s ease;
  z-index: 70;
}
.drawer-mask.open { opacity: 1; visibility: visible; }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 430px; max-width: 94vw;
  background: #fff; z-index: 71;
  box-shadow: -10px 0 30px rgba(18, 36, 72, .16);
  transform: translateX(101%);
  transition: transform .26s cubic-bezier(.33, .8, .4, 1);
  display: flex; flex-direction: column;
}
.drawer.open { transform: translateX(0); }
.drawer-head {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}
.drawer-head h3 { font-size: 16px; }
.drawer-close {
  background: none; border: 0; padding: 0 2px; cursor: pointer;
  font-size: 24px; line-height: 1; color: var(--muted);
}
.drawer-close:hover { color: var(--text); }
.drawer-form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.drawer-body { flex: 1; overflow-y: auto; padding: 18px 20px; }
.drawer-body .fld { margin-bottom: 14px; }
.drawer-body .fld:last-child { margin-bottom: 0; }
.drawer-body label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 6px; }
.drawer-body label.req::after { content: " *"; color: var(--red); }
.drawer-body input[type=text],
.drawer-body input[type=date],
.drawer-body select { width: 100%; }
.drawer-body textarea {
  width: 100%; min-height: 120px; resize: vertical;
  padding: 10px; border: 1px solid var(--border); border-radius: 6px;
  font-family: inherit; font-size: 13px; color: var(--text); background: #fff;
}
.drawer-body textarea:focus { outline: none; border-color: var(--primary); }
.drawer-body .file-box {
  display: block; width: 100%; padding: 12px;
  border: 1px dashed var(--border); border-radius: 8px; font-size: 13px;
}
.drawer-foot {
  flex: none; display: flex; gap: 10px;
  padding: 14px 20px; border-top: 1px solid var(--border);
}
.drawer-sep {
  display: flex; align-items: center; gap: 10px;
  color: var(--muted); font-size: 12px; margin: 18px 0;
}
.drawer-sep::before, .drawer-sep::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.btn-green { background: var(--green); }
.drawer-body .hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
.drawer-body .hint:empty { display: none; }

/* =====================================================================
   学生管理：就地保存（2026-10-09d）
   保存成功的那一行闪一下；结果用右上角轻提示说一声（页面不再整页刷新）
   ===================================================================== */
@keyframes stuFlash { from { background: #fff4cf; } to { background: transparent; } }
table.list tr.srow.flash > td { animation: stuFlash 1.6s ease-out; }

.stu-toast {
  position: fixed; right: 22px; top: 76px; z-index: 90;
  max-width: 62vw;
  background: var(--green); color: #fff;
  font-size: 14px; line-height: 1.45;
  padding: 10px 16px; border-radius: 8px;
  box-shadow: 0 10px 24px rgba(18, 36, 72, .18);
  opacity: 0; transform: translateY(-8px);
  transition: opacity .2s ease, transform .2s ease;
  pointer-events: none;
}
.stu-toast.show { opacity: 1; transform: translateY(0); }
.stu-toast.err { background: var(--red); }

/* 抽屉内的红色错误条（身份证格式不对 / 学号重复等，就地提示不刷新） */
.drawer-err {
  margin: 0 0 14px; padding: 9px 12px;
  background: #fdecec; border: 1px solid #f7c9c9; border-radius: 6px;
  color: #b3261e; font-size: 13px; line-height: 1.5;
}
.drawer-err[hidden] { display: none; }
/* 导入成绩：按班下载模板的选择行（下拉 + 按钮同排，窄屏自动换行） */
.tpl-pick { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tpl-pick > span { font-size: 14px; color: var(--muted); white-space: nowrap; }
.tpl-pick select { width: auto; min-width: 230px; padding: 8px 10px; flex: 0 0 auto; }
.tpl-pick .btn { flex: 0 0 auto; }
/* 全校概况 12 项统计：宽屏排成一行（原来「不及格率」总被甩到第二行）；
   窄屏自动降成 6×2，避免最后一行只剩孤零零一个。 */
.stat-grid-tight { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 10px; }
.stat-grid-tight .stat { flex: none; padding: 12px 6px; }
.stat-grid-tight .stat .num { font-size: 22px; }
@media (max-width: 1299px) {
  .stat-grid-tight { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* ---------- 成绩总览（2026-10-09）：班级行只留「详情」，并按年级用不同色块区分 ---------- */
a.mini-btn {
  display: inline-block; padding: 3px 14px; font-size: 13px; line-height: 1.6;
  border: 1px solid var(--border); border-radius: 6px;
  color: var(--primary); background: #fff; text-decoration: none; white-space: nowrap;
}
a.mini-btn:hover { border-color: var(--primary); background: #eaf2ff; }
table.list tr.row-link:hover > td { box-shadow: inset 0 0 0 9999px rgba(20, 40, 80, .05); }
table.list tr.g1 > td { background: #eaf3ff; }
table.list tr.g2 > td { background: #e9f9f0; }
table.list tr.g3 > td { background: #fff5e3; }
table.list tr.g4 > td { background: #fdeef5; }
table.list tr.g5 > td { background: #e6f8fa; }
table.list tr.g6 > td { background: #f2eeff; }

/* =====================================================================
   成绩总览 → 班级详情：冻结「序号 / 学号 / 姓名」三列 + 首行   —— 2026-10-09
   ---------------------------------------------------------------------
   这张表 16 列，手机上必须左右滑。滑到右边看跳绳 / 仰卧起坐时，
   左边这三列固定不动，永远知道这一行是谁；上下滑动时表头固定在容器顶部。

   ⚠️ 三个前提（缺一不生效）：
   1) 表格 class 必须带 freeze-col + cs-freeze，且三列分别带 col-idx / col-no / col-name；
   2) 表格宽度必须 max-content，否则表格被压进容器宽度、外层永不产生横向滚动；
   3) 外层 .tbl-wrap 必须是滚动容器（有 max-height），sticky 才有参照物。
   下面的选择器带 .tbl-wrap 前缀，优先级高于通用 freeze 规则，
   所以三列的 left 偏移由这里统一说了算（不受 compact / fit 那几段影响）。
   ===================================================================== */
.tbl-wrap table.list.freeze-col.cs-freeze {
  width: max-content;
  min-width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}
.tbl-wrap table.list.freeze-col.cs-freeze th,
.tbl-wrap table.list.freeze-col.cs-freeze td {
  border-bottom: none;
  box-shadow: inset 0 -1px 0 var(--border);
  background: #fff;
}
.tbl-wrap table.list.freeze-col.cs-freeze th { background: #f0f4fb; }
/* 第 1 列：序号 */
.tbl-wrap table.list.freeze-col.cs-freeze th.col-idx,
.tbl-wrap table.list.freeze-col.cs-freeze td.col-idx {
  position: sticky;
  left: 0;
  z-index: 2;
  width: 44px; min-width: 44px; max-width: 44px;
  padding-left: 4px; padding-right: 4px;
}
/* 第 2 列：学号 —— left 必须等于序号列实际宽度 */
.tbl-wrap table.list.freeze-col.cs-freeze th.col-no,
.tbl-wrap table.list.freeze-col.cs-freeze td.col-no {
  position: sticky;
  left: 44px;
  z-index: 2;
  width: 78px; min-width: 78px; max-width: 78px;
  padding-left: 6px; padding-right: 6px;
}
/* 第 3 列：姓名 —— left = 序号 + 学号 的宽度和；右缘加阴影提示「这边能滑」 */
.tbl-wrap table.list.freeze-col.cs-freeze th.col-name,
.tbl-wrap table.list.freeze-col.cs-freeze td.col-name {
  position: sticky;
  left: 122px;
  z-index: 2;
  width: 62px; min-width: 62px; max-width: 62px;
  padding-left: 6px; padding-right: 6px;
  box-shadow: inset 0 -1px 0 var(--border), 6px 0 6px -4px rgba(30, 50, 90, .12);
}
/* 首行表头吸顶；被冻结的三格再高一档，否则会被数据行的冻结格盖住 */
.tbl-wrap table.list.freeze-col.cs-freeze th { position: sticky; top: 0; z-index: 3; }
.tbl-wrap table.list.freeze-col.cs-freeze th.col-idx,
.tbl-wrap table.list.freeze-col.cs-freeze th.col-no,
.tbl-wrap table.list.freeze-col.cs-freeze th.col-name { z-index: 4; }
/* 冻结格背景不透明会盖掉行的 hover，这里补回来（否则鼠标划过看不到是哪一行） */
.tbl-wrap table.list.freeze-col.cs-freeze tbody tr:hover > td { background: #f8fbff; }


/* =====================================================================
   成绩录入 · 自动保存（2026-10-12）
   ---------------------------------------------------------------------
   张老师要求：填完即存，不用最后点「保存全部」。
   反馈只有两个出口，都做得很轻，不占版面：
     ① 页脚那条状态条 —— 绿=全存好、橙=有改动在路上、蓝=正在存、红=有失败；
     ② 行底色 —— 存的过程中淡蓝、存成功后闪一下绿、失败挂红底直到重存成功。
   ===================================================================== */
.autosave-bar {
  display: flex; align-items: center; gap: 7px;
  margin-top: 10px; padding: 7px 12px;
  border: 1px solid var(--border); border-radius: 7px;
  background: #f8fafc; color: var(--muted); font-size: 13px;
  transition: background-color .2s, border-color .2s, color .2s;
}
.autosave-bar .as-icon { font-weight: 700; font-size: 14px; line-height: 1; }
.autosave-bar .as-txt  { font-weight: 600; }
.autosave-bar .as-extra { font-size: 12px; opacity: .82; }
.autosave-bar[data-state="saved"]   { background: #f0fdf4; border-color: #bbf7d0; color: #15803d; }
.autosave-bar[data-state="pending"] { background: #fffbeb; border-color: #fde68a; color: #b45309; }
.autosave-bar[data-state="saving"]  { background: #eff6ff; border-color: #bfdbfe; color: #1d4ed8; }
.autosave-bar[data-state="error"]   { background: #fef2f2; border-color: #fecaca; color: #b91c1c; }

/* 行底色渐变：transition 必须挂在基础规则上，否则 class 一摘就瞬变，没有「淡出」 */
table.list tr.stu-row td { transition: background-color .5s ease; }
tr.stu-row.row-saving > td { background: #eff6ff; }
tr.stu-row.row-saved  > td { background: #dcfce7; }
tr.stu-row.row-error  > td { background: #fef2f2; }

/* =====================================================================
   录入成绩页：学生列醒目 + 整行斑马纹 + 悬停高亮（2026-10-13 张老师）
   ---------------------------------------------------------------------
   背景：录入表是双栏超紧凑行高，一屏一长排输入框，横着扫时很容易串行。
     ① 学号 + 姓名两列打上深一档的底色块，姓名加粗 → 竖向视觉锚带；
     ② 整行隔行斑马纹（连输入框一起染）→ 一眼跟得住行尾；
     ③ 悬停整行高亮 → 当前行一目了然。
   只作用于录入页表格（id 以 scoreTable 开头），总览/班级详情表不受影响。
   ⚠️ 三条硬约束（别顺手改坏）：
     1) 不能无脑给 .score-input 上背景 ——「代入」标记 .is-inherit 是黄色虚线框，
        优先级比这里低，会被整片盖掉；所以染输入框的那两条都带 :not(.is-inherit)。
     2) 自动保存的行状态色（.row-saving/.row-saved/.row-error）必须盖过斑马纹与悬停，
        这几条写在最后，且要把学生列一起覆盖，否则绿行里会留一条蓝色锚带。
     3) 冻结列（≤900px）必须有不透明背景；这里每条都写了实色，天然满足。
   ===================================================================== */
table.list.fit[id^="scoreTable"] tr.stu-row > td {
  /* 悬停要跟手：原来继承的 0.5s 过渡扫行时会拖尾，这里收紧到 0.12s */
  transition: background-color .12s linear;
  background: #fff;
}
table.list.fit[id^="scoreTable"] tr.stu-row:nth-child(even) > td {
  background: #f0f5fc;                      /* ② 隔行斑马纹 */
}
table.list.fit[id^="scoreTable"] tr.stu-row:nth-child(even) > td.item-col .score-input:not(.is-inherit) {
  background: #f7fafe;                      /* 输入框跟着行色走，「一整行」才读得出来 */
}
table.list.fit[id^="scoreTable"] tr.stu-row:hover > td,
table.list.fit[id^="scoreTable"] tr.stu-row:hover > td.col-no,
table.list.fit[id^="scoreTable"] tr.stu-row:hover > td.col-name {
  background: #d7e5fb;                      /* ③ 悬停整行高亮：连学生列一起变，整行统一最直观 */
}
table.list.fit[id^="scoreTable"] tr.stu-row:hover > td.item-col .score-input:not(.is-inherit) {
  background: #e4eefc;
}
/* ① 学生列（学号 + 姓名）：恒定底色当锚带，不参与斑马纹 */
table.list.fit[id^="scoreTable"] tr.stu-row > td.col-no,
table.list.fit[id^="scoreTable"] tr.stu-row > td.col-name {
  background: #e1eaf8;
}
table.list.fit[id^="scoreTable"] tr.stu-row > td.col-name {
  font-weight: 700; color: #1f2d45;
}
table.list.fit[id^="scoreTable"] tr.stu-row > td.col-no {
  color: #56657f;
}
/* ⚠️ 自动保存的行状态色（存中淡蓝 / 存成功闪绿 / 失败红）压过上面所有规则 */
table.list.fit[id^="scoreTable"] tr.stu-row.row-saving > td,
table.list.fit[id^="scoreTable"] tr.stu-row.row-saving > td.col-no,
table.list.fit[id^="scoreTable"] tr.stu-row.row-saving > td.col-name { background: #eff6ff; }
table.list.fit[id^="scoreTable"] tr.stu-row.row-saved > td,
table.list.fit[id^="scoreTable"] tr.stu-row.row-saved > td.col-no,
table.list.fit[id^="scoreTable"] tr.stu-row.row-saved > td.col-name { background: #dcfce7; }
table.list.fit[id^="scoreTable"] tr.stu-row.row-error > td,
table.list.fit[id^="scoreTable"] tr.stu-row.row-error > td.col-no,
table.list.fit[id^="scoreTable"] tr.stu-row.row-error > td.col-name { background: #fef2f2; }

/* =====================================================================
   删除成绩页（2026-10-13）
   ===================================================================== */
table.list.del-batches td { padding-top: 7px; padding-bottom: 7px; }
table.list.del-batches tr:hover td { background: #fdf7f7; }
table.list.del-batches input[type="checkbox"] { width: 15px; height: 15px; cursor: pointer; }
.btn:disabled, .btn[disabled] { opacity: .42; cursor: not-allowed; filter: grayscale(.15); }

/* ---- 班级管理表：鼠标悬停时整行高亮（张老师 2026-10-13）---- */
table.list.cls-table tr > td { transition: background-color .12s ease; }
table.list.cls-table tr:hover > td { background: #e8f1ff; }
