/* ==========================================================================
   C++ 少儿编程教学系统（默认站名：林老师的题库）—— 界面样式（浅色主题）
   ========================================================================== */
:root {
  --bg: #f4f6fb;
  --panel: #ffffff;
  --panel-2: #fafbfe;
  --border: #e3e8f2;
  --border-strong: #cfd8e8;
  --text: #1e293b;
  --text-2: #475569;
  --muted: #7b8aa1;
  --primary: #4f7cff;
  --primary-dark: #3a63e0;
  --primary-soft: #eef3ff;
  --green: #16a34a;
  --green-soft: #eafaef;
  --amber: #d97706;
  --amber-soft: #fef6e7;
  --red: #e11d48;
  --red-soft: #fdeef1;
  --violet: #7c3aed;
  --violet-soft: #f3eefe;
  --cyan: #0891b2;
  --cyan-soft: #e6f7fb;
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 6px 18px rgba(16, 24, 40, .06);
  --shadow-lg: 0 12px 40px rgba(16, 24, 40, .16);
  --mono: "Cascadia Mono", Consolas, "Courier New", monospace;

  /* ---- 皮肤变量（积分商城换肤会覆写这几项）----
     侧栏 / 顶栏单独给一档，暗色皮肤里它们要比卡片更深，层次才出得来。
     品牌渐变单独给两端颜色，否则 logo 永远蓝紫、换肤像没换。 */
  --side-bg: #ffffff;
  --top-bg: #ffffff;
  --brand-a: #4f7cff;
  --brand-b: #7c3aed;
  --toast-bg: #1e293b;
  --toast-fg: #ffffff;
  --on-primary: #ffffff;   /* 主色底上的字：浅色皮肤用白，暗色皮肤用深色 */
}

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0; height: 100%;
  background: var(--bg); color: var(--text);
  font-family: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", system-ui, -apple-system, sans-serif;
  font-size: 14px; line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--primary); text-decoration: none; }
button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }

/* ★ 必须有这一条：作者的 .app{display:flex} / .auth-wrap{display:flex} 会盖掉浏览器的
   [hidden]{display:none}，导致「登录页和主界面同时显示」（上半屏登录、下半屏已登录）。
   hidden 属性是切换登录页/主界面的唯一手段，这里必须用 !important 兜住。 */
[hidden] { display: none !important; }

/* ---------- 布局 ---------- */
.app { display: flex; height: 100vh; overflow: hidden; }

.sidebar {
  width: 218px; flex: 0 0 218px; background: var(--side-bg);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; overflow: hidden;
}
.brand {
  padding: 18px 18px 14px; border-bottom: 1px solid var(--border);
}
.brand .logo {
  display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 15.5px;
  letter-spacing: .2px;
}
.brand .mark {
  width: 30px; height: 30px; border-radius: 9px; flex: 0 0 30px;
  background: linear-gradient(135deg, var(--brand-a), var(--brand-b));
  color: #fff; display: grid; place-items: center;
  font-family: var(--mono); font-size: 12px; font-weight: 700;
}
.brand .sub { color: var(--muted); font-size: 11.5px; margin-top: 5px; padding-left: 39px; }

.nav { padding: 10px 10px 18px; overflow-y: auto; flex: 1; }
.nav .group { color: var(--muted); font-size: 11px; padding: 12px 10px 5px; letter-spacing: .6px; }
.nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 8.5px 11px; border-radius: var(--radius-sm);
  color: var(--text-2); font-size: 13.5px; margin-bottom: 1px; cursor: pointer;
  transition: background .13s, color .13s;
}
.nav a:hover { background: var(--panel-2); color: var(--text); }
.nav a.active { background: var(--primary-soft); color: var(--primary-dark); font-weight: 600; }
.nav a .ic { width: 17px; text-align: center; font-size: 14px; opacity: .95; }
.nav a .badge {
  margin-left: auto; background: var(--border); color: var(--text-2);
  font-size: 11px; padding: 1px 7px; border-radius: 20px; font-weight: 600;
}
.nav a.active .badge { background: var(--panel); color: var(--primary-dark); }

.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.topbar {
  height: 56px; flex: 0 0 56px; background: var(--top-bg); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; padding: 0 22px; gap: 14px;
}
.topbar h1 { font-size: 16px; margin: 0; font-weight: 700; }
.topbar .crumb { color: var(--muted); font-size: 13px; }
.topbar .spacer { flex: 1; }
.content { flex: 1; overflow-y: auto; padding: 20px 22px 60px; }

/* ---------- 通用组件 ---------- */
.card {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); margin-bottom: 16px;
}
.card > .hd {
  padding: 13px 17px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 10px;
}
.card > .hd h3 { margin: 0; font-size: 14.5px; font-weight: 700; }
.card > .hd .spacer { flex: 1; }
.card > .hd .hint { color: var(--muted); font-size: 12.5px; font-weight: 400; }
.card > .bd { padding: 16px 17px; }
.card > .bd.tight { padding: 0; }

.btn {
  display: inline-flex; align-items: center; gap: 6px; justify-content: center;
  padding: 7px 14px; border-radius: var(--radius-sm); border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--text-2); cursor: pointer; font-size: 13px;
  transition: all .13s; white-space: nowrap;
}
.btn:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); }
.btn.primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.btn.primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); color: var(--on-primary); }
.btn.danger { color: var(--red); border-color: #f3c6d1; }
.btn.danger:hover { background: var(--red-soft); border-color: var(--red); }
.btn.ghost { border-color: transparent; background: transparent; }
.btn.ghost:hover { background: var(--panel-2); }
.btn.sm { padding: 4px 10px; font-size: 12.5px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.field { margin-bottom: 13px; }
.field > label { display: block; font-size: 12.5px; color: var(--text-2); margin-bottom: 5px; font-weight: 600; }
.field > label .req { color: var(--red); }
input[type=text], input[type=number], input[type=date], select, textarea {
  width: 100%; padding: 7.5px 11px; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); background: var(--panel); outline: none; transition: border .13s, box-shadow .13s;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79, 124, 255, .12);
}
textarea { resize: vertical; min-height: 76px; line-height: 1.65; }
select { cursor: pointer; }

.row { display: grid; gap: 13px; }
.row.c2 { grid-template-columns: 1fr 1fr; }
.row.c3 { grid-template-columns: repeat(3, 1fr); }
.row.c4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1080px) { .row.c3, .row.c4 { grid-template-columns: 1fr 1fr; } }

/* ---------- 表格 ---------- */
table.tbl { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.tbl th {
  text-align: left; padding: 10px 13px; background: var(--panel-2);
  color: var(--text-2); font-weight: 600; font-size: 12.5px;
  border-bottom: 1px solid var(--border); white-space: nowrap; position: sticky; top: 0; z-index: 2;
}
table.tbl td { padding: 10px 13px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.tbl tbody tr:hover { background: var(--panel-2); }
table.tbl tbody tr.clickable { cursor: pointer; }
table.tbl .num { font-variant-numeric: tabular-nums; }
/* 卷面页的题目格子：点一下就铺满整屏看全题（跟成绩页表头是同一个读题框）。
   只给这一格手型光标 —— 旁边的勾选框、分值输入框不该看着像能读题。 */
table.tbl td.pi-stem { cursor: pointer; }
table.tbl td.pi-stem:hover { background: var(--primary-soft); }
table.tbl th .hint { color: var(--muted); font-size: 12px; font-weight: 400; }
table.tbl tbody tr.picked { background: var(--primary-soft); }
table.tbl th.pickcol, table.tbl td.pickcell { width: 40px; padding-right: 0; text-align: center; }
table.tbl input[type=checkbox], .pickbar input[type=checkbox], .qpick, .pi-ck {
  width: 15px; height: 15px; vertical-align: -2px; cursor: pointer; accent-color: var(--primary);
}

/* 题库的多选操作条（「全选本列表 / 已选 N 道 / 删除所选」） */
.pickbar {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 17px; background: var(--panel-2); font-size: 13px;
  border-bottom: 1px solid var(--border);
}
.pickbar .spacer { flex: 1; }
/* 卷面页那条批量条比题库的更长（多了「按题型勾选」和分值输入框），窄一点就折行 */
.pickbar.pi-bar { flex-wrap: wrap; row-gap: 8px; }
.pickbar .pickall { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
.empty { text-align: center; color: var(--muted); padding: 34px 0; font-size: 13.5px; }
.empty .big { font-size: 30px; display: block; margin-bottom: 8px; opacity: .5; }

/* ---------- 标签 / 徽章 ---------- */
.tag {
  display: inline-flex; align-items: center; gap: 4px; padding: 1.5px 8px;
  border-radius: 20px; font-size: 12px; line-height: 1.6; white-space: nowrap;
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text-2);
}
.tag.blue { background: var(--primary-soft); border-color: #d6e2ff; color: var(--primary-dark); }
.tag.green { background: var(--green-soft); border-color: #c9f0d6; color: #14773a; }
.tag.amber { background: var(--amber-soft); border-color: #f8e2b8; color: #a35d05; }
.tag.red { background: var(--red-soft); border-color: #f8cdd8; color: #b81b43; }
.tag.violet { background: var(--violet-soft); border-color: #e0d3fb; color: #6329c9; }
.tag.cyan { background: var(--cyan-soft); border-color: #c4e9f2; color: #066b83; }
.tag.gray { background: #f1f3f8; border-color: #e2e6f0; color: var(--muted); }

.dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }

/* ---------- 统计卡 ---------- */
.stats { display: grid; grid-template-columns: repeat(6, 1fr); gap: 13px; margin-bottom: 16px; }
@media (max-width: 1240px) { .stats { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 760px) { .stats { grid-template-columns: repeat(2, 1fr); } }
.stat {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; box-shadow: var(--shadow); position: relative; overflow: hidden;
}
.stat .k { color: var(--muted); font-size: 12.5px; margin-bottom: 6px; }
.stat .v { font-size: 25px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; }
.stat .v small { font-size: 13px; font-weight: 500; color: var(--muted); margin-left: 3px; }
.stat .bar { height: 3px; border-radius: 3px; background: var(--border); margin-top: 10px; overflow: hidden; }
.stat .bar i { display: block; height: 100%; background: var(--primary); border-radius: 3px; transition: width .5s; }

/* ---------- 图表 ---------- */
.chart { width: 100%; }
.chart.h240 { height: 240px; }
.chart.h280 { height: 280px; }
.chart.h320 { height: 320px; }
.chart.h360 { height: 360px; }
.chart.h420 { height: 420px; }

/* ---------- 进度条 ---------- */
.meter { display: flex; align-items: center; gap: 8px; min-width: 132px; }
.meter .track { flex: 1; height: 7px; background: var(--panel-2); border-radius: 6px; overflow: hidden; }
.meter .track i { display: block; height: 100%; border-radius: 6px; }
.meter .val { font-size: 12.5px; font-variant-numeric: tabular-nums; color: var(--text-2); min-width: 42px; text-align: right; }

/* ---------- 模态框 ---------- */
.mask {
  position: fixed; inset: 0; background: rgba(23, 32, 51, .42);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 56px 20px 30px; overflow-y: auto; z-index: 100; backdrop-filter: blur(2px);
}
.modal {
  background: var(--panel); border-radius: 14px; box-shadow: var(--shadow-lg);
  width: 100%; max-width: 560px; animation: pop .16s ease-out;
}
.modal.wide { max-width: 860px; }
.modal.xwide { max-width: 1140px; }
@keyframes pop { from { transform: translateY(-8px) scale(.99); opacity: 0; } to { transform: none; opacity: 1; } }
.modal .mhd { padding: 15px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; }
.modal .mhd h3 { margin: 0; font-size: 15px; font-weight: 700; }
.modal .mhd .x { margin-left: auto; cursor: pointer; color: var(--muted); font-size: 20px; line-height: 1; padding: 0 4px; }
.modal .mhd .x:hover { color: var(--text); }
.modal .mbd { padding: 18px 20px; max-height: 66vh; overflow-y: auto; }
.modal .mft {
  padding: 13px 20px; border-top: 1px solid var(--border);
  display: flex; gap: 9px; justify-content: flex-end; background: var(--panel-2);
  border-radius: 0 0 14px 14px;
}
.modal .mft .spacer { flex: 1; }

/* ---------- 提示 ---------- */
.toasts { position: fixed; right: 20px; bottom: 20px; z-index: 300; display: flex; flex-direction: column; gap: 9px; }
.toast {
  background: var(--toast-bg); color: var(--toast-fg); padding: 10px 16px; border-radius: 9px;
  font-size: 13.5px; box-shadow: var(--shadow-lg); animation: slidein .2s ease-out;
  max-width: 400px; display: flex; gap: 8px; align-items: flex-start;
}
.toast.ok { background: #15803d; }
.toast.err { background: #b91c3c; }
.toast.warn { background: #b45309; }
@keyframes slidein { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }

/* ---------- 成绩录入网格 ---------- */
.gridwrap { overflow: auto; max-height: 62vh; border: 1px solid var(--border); border-radius: var(--radius-sm); }
table.grid { border-collapse: separate; border-spacing: 0; font-size: 13px; }
table.grid th, table.grid td { border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: 0; }
table.grid thead th {
  position: sticky; top: 0; z-index: 3; background: var(--panel-2); padding: 8px 10px;
  font-size: 12px; font-weight: 600; color: var(--text-2); white-space: nowrap;
}
table.grid thead tr.sub th { top: 34px; z-index: 2; font-weight: 400; color: var(--muted); font-size: 11px; padding: 3px 10px; }
table.grid .stick-l { position: sticky; left: 0; z-index: 4; background: var(--panel); }
table.grid thead .stick-l { background: var(--panel-2); z-index: 5; }
table.grid .stick-l2 { position: sticky; left: 128px; z-index: 4; background: var(--panel); }
table.grid thead .stick-l2 { background: var(--panel-2); z-index: 5; }
table.grid tbody tr:hover td { background: var(--panel-2); }
table.grid tbody tr:hover td.stick-l, table.grid tbody tr:hover td.stick-l2 { background: var(--panel-2); }
table.grid td.cell { padding: 0; }
table.grid input.q {
  width: 62px; border: none; outline: none; background: transparent;
  padding: 7px 8px; text-align: center; font-variant-numeric: tabular-nums;
}
table.grid input.q:focus { background: var(--primary-soft); box-shadow: inset 0 0 0 2px var(--primary); }
table.grid input.q.full { color: var(--green); font-weight: 600; }
table.grid input.q.zero { color: var(--red); }

/* 客观题：录分格子下方多一行「学生选了什么 / 填了什么」。
   有它就不用为了核对某一题再单独翻学生答卷 —— 批改时一眼扫完。
   加了一行，把 input 的上下 padding 收一点，行高别涨太多。 */
table.grid td.cell.obj input.q { padding: 6px 8px 1px; }
table.grid .ansmini {
  width: 100%; padding: 0 5px 3px;
  font-size: 10.5px; line-height: 13px; text-align: center;
  color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer; user-select: none;
}
table.grid .ansmini:hover { text-decoration: underline; }
table.grid .ansmini.ok  { color: var(--green); font-weight: 600; }
table.grid .ansmini.bad { color: var(--red); }
table.grid .ansmini.none { color: var(--muted); }

/* 作答详情弹窗 */
.ansdlg { font-size: 13.5px; }
.ansdlg .ansd-hd { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.ansdlg .ansd-hd .spacer { flex: 1; }
.ansdlg .ansd-stem {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; margin-bottom: 12px; color: var(--text-2);
  max-height: 220px; overflow: auto; white-space: pre-wrap;
}
.ansdlg .ansd-row { display: flex; align-items: flex-start; gap: 12px; padding: 7px 0; border-top: 1px solid var(--border); }
.ansdlg .ansd-row .k { width: 74px; flex: none; color: var(--muted); }
.ansdlg .ansd-row .v { flex: 1; white-space: pre-wrap; word-break: break-word; }
.ansdlg .ansd-row .v.my { font-weight: 600; color: var(--text); }
.ansdlg .ansd-row .v.none { color: var(--muted); }
.ansdlg .ansd-row .v.ok { color: var(--green); font-weight: 600; }
table.grid .sticky-total { position: sticky; right: 0; z-index: 3; background: var(--panel); font-weight: 700; }
table.grid thead .sticky-total { background: var(--panel-2); }
table.grid tr.unsubmitted td { background: var(--red-soft); }
table.grid tr.unsubmitted:hover td { background: var(--red-soft); filter: brightness(.97); }
table.grid .sname { padding: 7px 12px; font-weight: 600; white-space: nowrap; }
table.grid select.st {
  border: 1px solid var(--border-strong); border-radius: 6px; padding: 3px 5px;
  font-size: 12px; background: var(--panel); width: 86px;
}

/* ---------- 其它 ---------- */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 7px 14px; font-size: 13.5px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }

.list-plain { list-style: none; padding: 0; margin: 0; }
.list-plain li {
  display: flex; align-items: center; gap: 10px; padding: 8px 0;
  border-bottom: 1px dashed var(--border); font-size: 13.5px;
}
.list-plain li:last-child { border-bottom: none; }

.rank-no {
  width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center;
  font-size: 12px; font-weight: 700; background: var(--panel-2); color: var(--text-2); flex: 0 0 22px;
}
.rank-no.t1 { background: var(--amber-soft); color: var(--amber); }



.feedback-box {
  background: #fcfdff; border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 16px 18px; white-space: pre-wrap; font-size: 13.5px; line-height: 1.9;
  max-height: 460px; overflow-y: auto; font-family: inherit;
}

/* 试卷维度的课后反馈弹窗（一键发家长群）：顶部班级条 + 一块可直接编辑的正文 */
.pfbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pfbar .spacer { flex: 1; }

/* ---------- 作业进度看板（老师 / 管理员只读） ---------- */
.pgrow { cursor: pointer; }
.pgrow:hover { background: var(--panel-2); }
.pgdetail > td { background: var(--panel-2); padding: 0 10px 12px; }
.pgdetail .tbl { background: var(--panel); border-radius: var(--radius-sm); }
.pfedit { max-height: none; min-height: 340px; }
.pfedit:focus { outline: 2px solid var(--primary-soft); border-color: var(--primary); }

/* 月度教学计划：表格内直接编辑（主题 / 日期 / 知识点一点一行） */
.tablewrap { overflow-x: auto; }
table.pf-tbl { background: var(--panel); }
table.pf-tbl td { vertical-align: top; padding: 8px 10px; }
.pf-in {
  width: 100%; box-sizing: border-box; border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 6px 8px; font-size: 13px;
  font-family: inherit; background: var(--panel); color: var(--text); resize: vertical;
}
.pf-in:focus { outline: 2px solid var(--primary-soft); border-color: var(--primary); }
textarea.pf-in { line-height: 1.55; }

.sec-title { font-size: 13px; font-weight: 700; color: var(--text-2); margin: 4px 0 9px; }
.divider { height: 1px; background: var(--border); margin: 14px 0; }
.muted { color: var(--muted); }
.small { font-size: 12.5px; }
.mono { font-family: var(--mono); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.center { text-align: center; }
.flexline { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  padding: 4px 11px; border: 1px solid var(--border-strong); border-radius: 20px;
  cursor: pointer; font-size: 12.5px; background: var(--panel); color: var(--text-2); user-select: none;
}
.chip:hover { border-color: var(--primary); color: var(--primary); }
.chip.on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-dark); font-weight: 600; }
.chip .n { color: var(--muted); font-size: 11.5px; margin-left: 3px; }
.chips.drill-lv { margin: 0 0 13px; }

.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 15px; }
.tabs div {
  padding: 8px 15px; cursor: pointer; font-size: 13.5px; color: var(--text-2);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs div:hover { color: var(--primary); }
.tabs div.on { color: var(--primary-dark); border-bottom-color: var(--primary); font-weight: 600; }

.hero {
  background: linear-gradient(135deg, var(--primary-soft) 0%, var(--violet-soft) 100%);
  border: 1px solid #e0e7fb; border-radius: var(--radius); padding: 16px 20px; margin-bottom: 16px;
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}
.hero .avatar {
  width: 52px; height: 52px; border-radius: 14px; background: var(--panel); border: 1px solid #e0e7fb;
  display: grid; place-items: center; font-size: 20px; font-weight: 700; color: var(--primary);
}
.hero h2 { margin: 0 0 3px; font-size: 18px; }
.hero .spacer { flex: 1; }

.warnbox {
  background: var(--amber-soft); border: 1px solid #f8e2b8; color: #8a5007;
  padding: 11px 14px; border-radius: var(--radius-sm); font-size: 13px; margin-bottom: 14px;
}
.infobox {
  background: var(--primary-soft); border: 1px solid #d6e2ff; color: #2f4fae;
  padding: 11px 14px; border-radius: var(--radius-sm); font-size: 13px; margin-bottom: 14px;
}
.errbox {
  background: var(--red-soft); border: 1px solid #f8cdd8; color: #a8183d;
  padding: 11px 14px; border-radius: var(--radius-sm); font-size: 13px; margin-bottom: 14px;
}

.opt-preview { white-space: pre-wrap; font-size: 13px; color: var(--text-2); font-family: var(--mono); }

.spin {
  width: 15px; height: 15px; border: 2px solid rgba(255,255,255,.4);
  border-top-color: #fff; border-radius: 50%; animation: sp .7s linear infinite; display: inline-block;
}
.spin.dark { border-color: rgba(79,124,255,.25); border-top-color: var(--primary); }
@keyframes sp { to { transform: rotate(360deg); } }

.loading-panel { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 60px 0; color: var(--muted); }

/* ======================================================================== */
/*  登录 / 首次改密
/* ======================================================================== */
.auth-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 28px; background: linear-gradient(160deg, #f2f6ff 0%, var(--bg) 42%, #f7f9fc 100%);
}
.auth-card {
  width: 100%; max-width: 460px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 32px 32px 26px;
  box-shadow: 0 18px 48px rgba(30, 41, 59, .09);
}
.auth-brand { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 22px; }
.auth-brand .mark {
  width: 46px; height: 46px; border-radius: 12px; background: var(--primary);
  color: var(--on-primary); font-weight: 600; font-size: 15px; display: flex; align-items: center;
  justify-content: center; letter-spacing: .3px; flex: 0 0 auto;
}
.auth-brand h1 { margin: 2px 0 5px; font-size: 18px; font-weight: 600; }
.auth-brand p { margin: 0; color: var(--muted); font-size: 12.5px; }
.auth-card .field { margin-bottom: 15px; }
.auth-card input { width: 100%; }
.lg-btn { width: 100%; padding: 12px; font-size: 14.5px; margin-top: 6px; }
.auth-tip { min-height: 20px; font-size: 12.5px; margin-top: 10px; }
.auth-note {
  margin-top: 18px; padding-top: 16px; border-top: 1px dashed var(--border);
  color: var(--muted); font-size: 12px; line-height: 1.85;
}
.auth-note .mono { color: var(--text-2); }

/* ICP 备案号（法律要求公开展示）：登录页底部 + 侧栏底部各一处，压淡不抢眼 */
.icp-line { margin-top: 14px; text-align: center; font-size: 11.5px; line-height: 1.6; }
.icp-line a { color: var(--muted); text-decoration: none; }
.icp-line a:hover { color: var(--primary-dark); text-decoration: underline; }
.sidefoot .icp-line { flex: 1 0 100%; margin-top: 4px; text-align: left; font-size: 11px; opacity: .85; }

/* 侧栏底部当前用户 */
.sidefoot {
  border-top: 1px solid var(--border); padding: 12px 14px 14px;
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
}
.sidefoot .who { flex: 1 0 100%; display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 500; }
.sidefoot .who .role {
  font-size: 11px; font-weight: 400; color: var(--primary-dark);
  background: var(--primary-soft); border-radius: 20px; padding: 1px 8px;
}

/* ======================================================================== */
/*  学生端
/* ======================================================================== */
.todo-ring {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 74px; height: 74px; border-radius: 50%;
  background: var(--primary-soft); color: var(--primary-dark); flex: 0 0 auto;
}
.todo-ring b { font-size: 24px; line-height: 1.05; font-weight: 600; }
.todo-ring span { font-size: 11px; }

.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; margin-bottom: 6px; }
.exam-card { margin-bottom: 0; }
.exam-card .kv { display: grid; grid-template-columns: 76px 1fr; gap: 5px 10px; margin: 0; }
.exam-card .kv dt { color: var(--muted); }
.exam-card .kv dd { margin: 0; }

.small-hero { padding: 16px 20px; margin-bottom: 14px; }

.exam-list { display: flex; flex-direction: column; gap: 14px; padding-bottom: 84px; }
.qcard {
  background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  border-radius: var(--radius-md); padding: 16px 18px 18px;
}
.qcard.right { border-left-color: var(--green); }
.qcard.wrong { border-left-color: var(--red); }
/* 驳回重考里「上一轮做对、已锁定，不用重写」的题：绿边之外再加一点底色，
   跟「已交卷后对着正确答案看」的卡片区分开 */
.qcard.locked { background: #f6fdf8; border-left-color: var(--green); }
.qhd { display: flex; align-items: center; gap: 7px; margin-bottom: 10px; flex-wrap: wrap; }
.qno {
  width: 24px; height: 24px; border-radius: 7px; background: var(--primary-soft);
  color: var(--primary-dark); font-size: 12.5px; font-weight: 600;
  display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.qscore { color: var(--muted); font-size: 12.5px; }
.qstem { font-size: 14px; line-height: 1.75; margin-bottom: 12px; white-space: pre-wrap; }

/* 题号（判断题 J1000、选择题 M1000、编程题 P1000… ，可手工改） */
.qcode {
  display: inline-block; font-family: var(--mono); font-size: 12px; line-height: 1.55;
  padding: 0 6px; border-radius: 6px; background: var(--primary-soft); color: var(--primary-dark);
  border: 1px solid var(--border); margin-right: 4px; vertical-align: 1px; white-space: nowrap;
}
table.tbl td.qcode-cell { white-space: nowrap; width: 78px; }
.qcard .qhd .qcode { background: var(--primary-soft); color: var(--primary-dark); border-color: #cfdcff; font-weight: 600; }

.opts { display: flex; flex-direction: column; gap: 8px; }
.opt {
  display: flex; align-items: flex-start; gap: 10px; padding: 10px 13px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  cursor: pointer; transition: background .15s, border-color .15s;
}
.opt:hover { background: var(--panel-2); border-color: var(--border-strong); }
.opt input { margin: 3px 0 0; flex: 0 0 auto; width: auto; }
.opt .oKey {
  font-weight: 600; color: var(--primary-dark); min-width: 16px; font-size: 13.5px;
}
.opt .oText { font-size: 13.5px; line-height: 1.6; font-family: var(--mono); word-break: break-word; }
.qinput {
  width: 100%; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 13.5px; background: var(--panel);
}
.qinput.mono { font-family: var(--mono); }
.qinput:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

.ansbox { background: var(--panel-2); border-radius: var(--radius-sm); padding: 13px 15px; font-size: 13px; line-height: 1.75; }
.ansbox .mine { margin-bottom: 7px; }
.ansbox .correct { color: var(--green); margin-bottom: 6px; }
.ansbox .expl { color: var(--muted); margin-bottom: 6px; }
.ansbox .got { padding-top: 7px; border-top: 1px dashed var(--border); }

.exam-bar {
  position: sticky; bottom: 0; display: flex; align-items: center; gap: 10px;
  background: rgba(255, 255, 255, .96); border-top: 1px solid var(--border);
  padding: 12px 16px; margin: 0 -22px -22px; border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
.exam-bar .spacer { flex: 1; }

/* ======================================================================== */
/*  编程题：代码框、测试用例、判分结果
/* ======================================================================== */
.codebox {
  margin: 8px 0 0; padding: 12px 14px; background: #f7f9fd; border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-family: var(--mono); font-size: 12.5px; line-height: 1.65;
  white-space: pre-wrap; word-break: break-word; color: var(--text-2); max-height: 320px; overflow: auto;
}
.casebox { display: flex; flex-direction: column; gap: 7px; margin-top: 9px; }
.onecase {
  display: flex; flex-direction: column; gap: 2px; padding: 9px 12px;
  background: var(--panel-2); border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  border-radius: var(--radius-sm); font-size: 12.5px; font-family: var(--mono); line-height: 1.6;
  color: var(--text-2);
}
.onecase b { font-size: 12px; color: var(--muted); font-weight: 600; }
.onecase.ok { border-left-color: var(--green); }
.onecase.bad { border-left-color: var(--red); }
.onecase span { word-break: break-word; }
.qcard .casebox .onecase { background: var(--panel-2); }

/* 样例单块的「输入」「输出」：多行原样显示，配一键复制。
   早先是把换行折成 " ⏎ " 挤在一行，学生复制出来还得自己删，见林老 2026-09-19 反馈。 */
.caseio { display: flex; flex-direction: column; gap: 3px; }
.caseio-head { display: flex; align-items: center; gap: 8px; }
.caseio-head .lbl { font-size: 12px; color: var(--muted); }
.caseio-body {
  margin: 0; padding: 7px 10px; background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.6;
  white-space: pre-wrap; word-break: break-word; color: var(--text);
  max-height: 220px; overflow: auto;
}
.copybtn {
  padding: 3px 8px; border: 1px solid var(--border-strong); background: var(--panel);
  color: var(--text-2); border-radius: 6px; font-size: 11px; line-height: 1;
  font-family: inherit; cursor: pointer;
}
.copybtn:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); }
.copybtn:active { transform: translateY(1px); }

/* ==========================================================================
   课堂笔记（老师发，学生看）—— 2026-09-19
   ========================================================================== */
/* 正文按 .txt 的样子原样显示：等宽字体、保留换行、长行自动折行 */
.notebody {
  margin: 0; padding: 14px 16px;
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 13.5px; line-height: 1.85;
  white-space: pre-wrap; word-break: break-word; color: var(--text);
  max-height: 62vh; overflow: auto;
}
/* 写笔记的大输入框 */
.noteedit {
  min-height: 280px; font-family: var(--mono); font-size: 13.5px; line-height: 1.8;
}
/* 学生端笔记列表 */
.notelist { display: grid; gap: 10px; }
.noteitem {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; background: var(--panel); cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}
.noteitem:hover { border-color: var(--primary); box-shadow: 0 2px 12px rgba(79, 124, 255, .10); }
.noteitem h4 { margin: 0 0 4px; font-size: 15px; display: flex; align-items: center; gap: 6px; }
.noteitem .nt-sum { color: var(--text-2); font-size: 13px; line-height: 1.7; }
.noteitem .nt-meta { color: var(--muted); font-size: 12px; margin-top: 6px; }
/* 勾选框行（发布 / 置顶） */
.chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13.5px; color: var(--text-2); cursor: pointer; }
.chk input { width: auto; margin: 0; }

/* ==========================================================================
   外部题（洛谷 / Codeforces / AtCoder）—— 2026-09-20
   这类题不搬题面也不搬测试点：判题在平台上做，系统只记完成情况。
   ========================================================================== */
/* 卡片网格：老师端「每题完成情况」与学生端「题目清单」共用 */
.extgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; }
.extcard {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px 14px; background: var(--panel-2);
  display: flex; flex-direction: column; gap: 8px;
}
.extcard-hd { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.extcard-t { font-size: 13.5px; color: var(--text); line-height: 1.6; }
.extcard-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 12px; color: var(--muted);
  border-top: 1px dashed var(--border); padding-top: 8px;
}
.extcard-bar .warnnum { color: var(--amber); }
.extcard-f { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.extcard-f input { flex: 1 1 160px; min-width: 0; }

/* 学生端单题里的「去做题 + 贴提交记录」面板（挂在 .qcard 内） */
.extpanel {
  margin-top: 14px; padding: 14px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-2);
}
.extpanel-hd { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.extpanel .field { margin-bottom: 0; }
.extpanel input { max-width: 560px; }

/* 学生端 OJ 题目页顶部的四个统计小格 */
.statrow { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; margin-top: 4px; }
.statbox {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; background: var(--panel-2); text-align: center;
}
.statbox-n { font-size: 22px; font-weight: 600; line-height: 1.35; color: var(--text); }
.statbox-n.green { color: var(--green); }
.statbox-n.amber { color: var(--amber); }
.statbox-n.red { color: var(--red); }
.statbox-l { font-size: 12px; color: var(--muted); }

/* ==========================================================================
   全屏读题（老师端）—— 2026-09-26
   林老反馈：「布置完试题回收后，每次要点开某题都得回题库找题号，读题很麻烦」。
   批改现场就能把一道题铺满整屏，含全部测试点（隐藏的也标出来）与参考答案。
   面板给 min-height:100%，看起来就跟打开了一个新页面一样。
   ========================================================================== */
.qfullmask {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(23, 32, 51, .55); overflow-y: auto;
}
/* max-width 要 ≥ 常见笔记本视口宽（1366 / 1440 都很常见）：这样在它们上面面板是
   「铺满整屏」，而不是两边留一条缝、缝里还透出成绩表 —— 林老要的就是「全屏」。 */
.qfull {
  background: var(--panel); width: 100%; max-width: 1120px; min-height: 100%;
  margin: 0 auto; box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
}
.qfhd {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 13px 26px; background: var(--panel); border-bottom: 1px solid var(--border);
}
.qfno { font-size: 15px; font-weight: 700; color: var(--text); margin-right: 2px; }
/* 关闭按钮永远靠最右：题号题型那排再长也不会把它挤走 */
.qfhd .btn { margin-left: auto; }
.qfbd { padding: 20px 26px 40px; }
.qfload { padding: 70px 0; text-align: center; color: var(--muted); }
.qflabel {
  font-size: 12.5px; font-weight: 700; color: var(--muted);
  letter-spacing: .4px; margin: 22px 0 8px;
}
.qfbd > .qflabel:first-child { margin-top: 0; }
/* 题干：字比正文大一号、行距放开，长题面读起来不费眼 */
.qstem { font-size: 15.5px; line-height: 2; color: var(--text); white-space: pre-wrap; word-break: break-word; }
.qopts {
  margin: 0; padding: 10px 13px; background: var(--panel-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: var(--mono); font-size: 13px; line-height: 1.9;
  white-space: pre-wrap; word-break: break-word; color: var(--text);
}
.qans {
  padding: 10px 13px; background: var(--green-soft); border: 1px solid #bbf7d0;
  border-radius: var(--radius-sm); font-size: 14px;
  white-space: pre-wrap; word-break: break-word; color: var(--text);
}
.qexpl { color: var(--text-2); line-height: 1.85; white-space: pre-wrap; word-break: break-word; }
/* 测试用例：一组一张卡，窄屏自动叠成一列 */
.qcasegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 10px; }
.qcase {
  border: 1px solid var(--border); border-left: 3px solid var(--green);
  border-radius: var(--radius-sm); padding: 9px 11px; background: var(--panel);
  display: flex; flex-direction: column; gap: 7px;
}
/* 隐藏测试点用暖色区分：老师一眼看出「哪几组学生看不到」 */
.qcase.hidden { border-left-color: var(--amber); background: var(--amber-soft); }
.qcasehd { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600; color: var(--text-2); }
.qcase .caseio-body { background: var(--panel); max-height: 150px; }
/* 成绩录入页表头：整格可点，点一下全屏读这道题 */
.qth { cursor: pointer; }
.qth:hover { background: var(--primary-soft); }
.qflink { font-weight: 400; font-size: 11px; color: var(--primary); margin-top: 2px; opacity: .8; }
.qth:hover .qflink { opacity: 1; }
/* 全屏读题时背景不跟着滚 */
body.no-scroll { overflow: hidden; }

/* ===== AI 批卷（编程题） =====
   结果就地展开在「看编程题代码」弹窗里，不另开弹窗（已经两层了，再叠就找不着关闭键）。*/
.aidx { margin-top: 10px; }
.aidx:empty { display: none; }
.aidx-loading {
  display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2);
  padding: 10px 12px; background: var(--panel-2);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
}
.aidx-box {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; background: var(--panel-2);
}
.aidx-sum { margin: 8px 0 2px; font-weight: 600; color: var(--text); line-height: 1.7; }
.aidx-errs { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.aidx-err {
  border-left: 3px solid var(--red); background: var(--panel);
  border-radius: var(--radius-sm); padding: 6px 10px;
  font-size: 13px; line-height: 1.7;
}
.aidx-errt { display: flex; align-items: center; gap: 8px; margin-bottom: 3px; flex-wrap: wrap; }
.aidx-advice {
  margin-top: 8px; padding: 7px 10px; font-size: 13px;
  background: var(--primary-soft); border-radius: var(--radius-sm); color: var(--text);
}
/* 学生档案里的聚合卡片 */
.aidx-stats { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.aidx-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.aidx-item {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 9px 11px; background: var(--panel);
}
.aidx-item .aidx-sum { font-size: 13.5px; font-weight: 500; }

/* ===== 整卷 AI 批卷（进度窗 + 批完的近期结论） ===== */
.aib-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 14px; color: var(--text);
}
.aib-list {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: 12px; max-height: 240px; overflow: auto;
  border-top: 1px solid var(--border); padding-top: 10px;
}
.aib-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 13px; line-height: 1.6;
}
.aib-row .small { color: var(--text-2); }

/* ---------- 留言板 ---------- */
.msglist { display: flex; flex-direction: column; gap: 12px; }
.msgcard {
  display: flex; gap: 12px; padding: 14px 16px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel-2);
}
.msgcard.is-feat { border-color: var(--amber); background: var(--amber-soft); }
/* 隐藏的留言老师端还要看得见（留痕），只是视觉上退后一点 */
.msgcard.is-hidden { opacity: .55; }
.msgava {
  flex: 0 0 38px; width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--primary-soft); color: var(--primary-dark);
  font-weight: 700; font-size: 16px;
}
.msgava.teacher { background: var(--violet-soft); color: var(--violet); }
.msgmain { flex: 1; min-width: 0; }
.msghd { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 13.5px; }
.msghd b { font-size: 14px; }
.msgt { font-size: 12px; color: var(--muted); }
.msgbody { margin-top: 6px; line-height: 1.75; word-break: break-word; }
.msgreply {
  margin-top: 8px; padding: 8px 10px; border-left: 3px solid var(--primary);
  background: var(--primary-soft); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: 13px; color: var(--text-2);
}
.msgacts { margin-top: 10px; display: flex; gap: 6px; flex-wrap: wrap; }

/* ---------- 每周出勤 ---------- */
.attstats {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  margin-bottom: 14px; padding: 10px 14px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
}
.attstats .one { display: flex; align-items: baseline; gap: 5px; font-size: 12.5px; color: var(--text-2); }
.attstats .one b { font-size: 19px; color: var(--text); font-variant-numeric: tabular-nums; }
.attstats .one.k-ok b { color: var(--green); }
.attstats .one.k-leave b { color: var(--primary-dark); }
.attstats .one.k-absent b { color: var(--red); }
.attstats .one.k-late b { color: var(--amber); }

.attgrp { display: flex; gap: 6px; flex-wrap: wrap; }
.attbtn {
  padding: 6px 13px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel); color: var(--text-2);
  font-size: 13px; cursor: pointer; transition: background .12s, border-color .12s, color .12s;
  -webkit-tap-highlight-color: transparent;
}
.attbtn:hover { border-color: var(--border-strong); }
.attbtn.on { font-weight: 600; }
.attbtn.on.k-ok { background: var(--green-soft); border-color: #b7e8c6; color: #14773a; }
.attbtn.on.k-leave { background: var(--primary-soft); border-color: #d6e2ff; color: var(--primary-dark); }
.attbtn.on.k-absent { background: var(--red-soft); border-color: #f8cdd8; color: #b81b43; }
.attbtn.on.k-late { background: var(--amber-soft); border-color: #f8e2b8; color: #a35d05; }

.tbl.att td { vertical-align: middle; }
.tbl.att input[type="text"] { width: 100%; padding: 5px 8px; font-size: 13px; }

/* 出勤表整体：姓名列窄、状态列定宽、备注列自适应（不写死 table-layout，
   否则学生名字长一点就把状态按钮挤到下一行） */
.att { table-layout: auto; }

/* ==========================================================================
   皮肤（积分商城兑换）
   --------------------------------------------------------------------------
   整套界面只有这里定义颜色，换肤 = 覆写一组 CSS 变量，一行 JS 都不用改。
   ★ 暗色皮肤必须同时做三件事，否则会「看着像坏了」：
     1) 把 --green / --amber / --red / --violet / --cyan 调**亮**（深底上的深绿 = 看不见）
     2) 把 --xxx-soft 调**暗**（那是 tag / infobox 的底色，留着浅色会一块块发白）
     3) 把 --on-primary 改成深色（浅紫底配白字只有 2:1 对比度，糊成一团）
   ★ 选择器用 html[data-skin="x"]：特异性 (0,1,1) 高于 :root 的 (0,1,0)，
     所以放文件末尾、放前面都生效；放末尾是为了「加皮肤只改这一处」。
   ========================================================================== */

/* 薄荷绿 —— 入门款：护眼，看代码久一点也不累 */
html[data-skin="mint"] {
  --bg: #eef9f4; --panel: #ffffff; --panel-2: #f6fdf9;
  --border: #d5ebe1; --border-strong: #b3d9c8;
  --text: #12302a; --text-2: #3a5a51; --muted: #6a8b80;
  --primary: #0f9d76; --primary-dark: #0a7d5d; --primary-soft: #e0f6ee;
  --green: #128a5c; --green-soft: #dcf6ea;
  --amber: #a97a08; --amber-soft: #fbf3dc;
  --red: #d6335c; --red-soft: #fde8ee;
  --violet: #0e8fa8; --violet-soft: #e0f4f8;
  --cyan: #0b8f9e; --cyan-soft: #e2f6f8;
  --side-bg: #f6fdf9; --top-bg: #ffffff;
  --brand-a: #0f9d76; --brand-b: #22b8cf;
  --toast-bg: #12302a; --toast-fg: #eafcf5; --on-primary: #ffffff;
}

/* 落日橙 —— 暖橙渐变，像傍晚的机房 */
html[data-skin="sunset"] {
  --bg: #fff7f0; --panel: #ffffff; --panel-2: #fffaf5;
  --border: #f6e3d3; --border-strong: #ebcdb6;
  --text: #3a2318; --text-2: #6b4a37; --muted: #9b7a66;
  --primary: #e8621a; --primary-dark: #c24e0e; --primary-soft: #ffedd8;
  --green: #2f8a4d; --green-soft: #e6f6ea;
  --amber: #b8710a; --amber-soft: #fdf0d9;
  --red: #cf2f4a; --red-soft: #fde6ea;
  --violet: #c2410c; --violet-soft: #ffeadb;
  --cyan: #0e7490; --cyan-soft: #e3f4f8;
  --side-bg: #fffaf5; --top-bg: #ffffff;
  --brand-a: #f76707; --brand-b: #e8590c;
  --toast-bg: #3a2318; --toast-fg: #fff4ea; --on-primary: #ffffff;
}

/* 青山 —— 第一款带图案的：远山 + 飞鸟 + 草地
   ★ 图案不是铺满就算完：下面那条 html[data-skin] body 规则会在图上盖一层
     --art-veil（这里是 50% 的面板色），卡片本身还是实色 —— 不然表格里的字
     就糊在山上了。 */
html[data-skin="forest"] {
  --bg: #f1f8f2; --panel: #ffffff; --panel-2: #f6fbf7;
  --border: #d7e9db; --border-strong: #b3d3bb;
  --text: #1b3226; --text-2: #47634f; --muted: #748f7c;
  --primary: #2f8b5f; --primary-dark: #237049; --primary-soft: #e2f4e8;
  --green: #1f8a52; --green-soft: #dcf5e7;
  --amber: #a67c0a; --amber-soft: #fbf3dc;
  --red: #cc3455; --red-soft: #fde8ee;
  --violet: #0e8f8a; --violet-soft: #dff5f4;
  --cyan: #0b8f9e; --cyan-soft: #e2f6f8;
  --side-bg: #f6fbf7; --top-bg: #ffffff;
  --brand-a: #34a06a; --brand-b: #4fb3a0;
  --toast-bg: #1b3226; --toast-fg: #eefaf2; --on-primary: #ffffff;
  --art: url("skins/forest.svg"); --art-veil: rgba(244, 251, 246, .50);
}

/* 樱花粉 —— 粉白配色，柔和一点 */
html[data-skin="sakura"] {
  --bg: #fdf3f7; --panel: #ffffff; --panel-2: #fff9fb;
  --border: #f6dde7; --border-strong: #eec3d5;
  --text: #3c1f2b; --text-2: #6c4353; --muted: #a2737f;
  --primary: #db2777; --primary-dark: #b01a5e; --primary-soft: #fde8f2;
  --green: #1f8a5a; --green-soft: #e2f7ec;
  --amber: #a86d09; --amber-soft: #fdf1da;
  --red: #cf2145; --red-soft: #fde5ea;
  --violet: #9333ea; --violet-soft: #f5e8fe;
  --cyan: #0e7c9c; --cyan-soft: #e3f3f9;
  --side-bg: #fff9fb; --top-bg: #ffffff;
  --brand-a: #f472b6; --brand-b: #a855f7;
  --toast-bg: #3c1f2b; --toast-fg: #fdeef4; --on-primary: #ffffff;
}

/* 海岸 —— 海平线 + 浪 + 沙滩 */
html[data-skin="beach"] {
  --bg: #eff8fd; --panel: #ffffff; --panel-2: #f5fbff;
  --border: #d3e9f6; --border-strong: #aed4ea;
  --text: #12303f; --text-2: #3d5f70; --muted: #6c8b9c;
  --primary: #1573a8; --primary-dark: #0f5c88; --primary-soft: #dff0fa;
  --green: #1b8a63; --green-soft: #dcf5ec;
  --amber: #a97a08; --amber-soft: #fbf3dc;
  --red: #cf2f4a; --red-soft: #fde6ea;
  --violet: #0f7f95; --violet-soft: #e0f4f8;
  --cyan: #0b8f9e; --cyan-soft: #e2f6f8;
  --side-bg: #f5fbff; --top-bg: #ffffff;
  --brand-a: #1f8fc4; --brand-b: #22b8cf;
  --toast-bg: #12303f; --toast-fg: #eaf7fe; --on-primary: #ffffff;
  --art: url("skins/beach.svg"); --art-veil: rgba(240, 250, 255, .48);
}

/* 雪原 —— 雪山 + 雪堆，最干净的一款 */
html[data-skin="snow"] {
  --bg: #f2f7fd; --panel: #ffffff; --panel-2: #f7fbff;
  --border: #dbe7f4; --border-strong: #bbd0e6;
  --text: #1b2a3a; --text-2: #44586b; --muted: #77899c;
  --primary: #3f6fa8; --primary-dark: #325a8a; --primary-soft: #e4eefa;
  --green: #22896a; --green-soft: #ddf5ed;
  --amber: #a67c0a; --amber-soft: #fbf3dc;
  --red: #cc3455; --red-soft: #fde8ee;
  --violet: #4c63b6; --violet-soft: #e8ecfa;
  --cyan: #0d7f97; --cyan-soft: #e2f4f8;
  --side-bg: #f7fbff; --top-bg: #ffffff;
  --brand-a: #5b8fc9; --brand-b: #8fb8e0;
  --toast-bg: #1b2a3a; --toast-fg: #edf5ff; --on-primary: #ffffff;
  --art: url("skins/snow.svg"); --art-veil: rgba(248, 252, 255, .42);
}

/* 深海夜 —— 暗色主题，晚上刷题不刺眼（全班最想要的那款，所以定价最高之一） */
html[data-skin="ocean"] {
  --bg: #0c1521; --panel: #15202e; --panel-2: #1a2736;
  --border: #25344a; --border-strong: #35496a;
  --text: #e4eefa; --text-2: #b3c6db; --muted: #8199b2;
  --primary: #58a6ff; --primary-dark: #3d8bef; --primary-soft: #16304c;
  --green: #4ade80; --green-soft: #12321f;
  --amber: #fbbf24; --amber-soft: #38290b;
  --red: #fb7185; --red-soft: #3b1520;
  --violet: #a78bfa; --violet-soft: #241a47;
  --cyan: #22d3ee; --cyan-soft: #0d3240;
  --side-bg: #0f1a27; --top-bg: #131e2b;
  --brand-a: #38bdf8; --brand-b: #6366f1;
  --toast-bg: #dbeafe; --toast-fg: #0c1521; --on-primary: #0a1220;
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 6px 18px rgba(0, 0, 0, .4);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, .6);
}

/* 樱空 —— 二次元：樱吹雪落在远山上，配一轮大月亮 */
html[data-skin="anime"] {
  --bg: #fdf1f7; --panel: #ffffff; --panel-2: #fff8fc;
  --border: #f4dcea; --border-strong: #e6bfd8;
  --text: #3a2030; --text-2: #6b4459; --muted: #a2738c;
  --primary: #d64a8f; --primary-dark: #b53476; --primary-soft: #fde4f1;
  --green: #1f8a5a; --green-soft: #e2f7ec;
  --amber: #a86d09; --amber-soft: #fdf1da;
  --red: #cf2145; --red-soft: #fde5ea;
  --violet: #8b5cf6; --violet-soft: #f1e8fe;
  --cyan: #0e7c9c; --cyan-soft: #e3f3f9;
  --side-bg: #fff8fc; --top-bg: #ffffff;
  --brand-a: #f472b6; --brand-b: #a78bfa;
  --toast-bg: #3a2030; --toast-fg: #fdeef5; --on-primary: #ffffff;
  --art: url("skins/anime.svg"); --art-veil: rgba(255, 247, 251, .46);
}

/* 星空 —— 暗色：银河 + 星球。暗色三件事（调亮强调色 / 压暗 soft / on-primary 改深）照做 */
html[data-skin="galaxy"] {
  --bg: #0a0d21; --panel: #131735; --panel-2: #181d40;
  --border: #262c56; --border-strong: #3a4179;
  --text: #e8ecff; --text-2: #b6bde4; --muted: #8890c0;
  --primary: #8b9eff; --primary-dark: #6d80f0; --primary-soft: #1c2350;
  --green: #4ade80; --green-soft: #10321f;
  --amber: #fbbf24; --amber-soft: #38290b;
  --red: #fb7185; --red-soft: #3b1520;
  --violet: #a78bfa; --violet-soft: #241a47;
  --cyan: #22d3ee; --cyan-soft: #0d3240;
  --side-bg: #0d1130; --top-bg: #101435;
  --brand-a: #6366f1; --brand-b: #22d3ee;
  --toast-bg: #e6eaff; --toast-fg: #0a0d21; --on-primary: #0a0d21;
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 6px 18px rgba(0, 0, 0, .4);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, .6);
  --art: url("skins/galaxy.svg"); --art-veil: rgba(10, 13, 33, .38);
}

/* 极光紫 —— 紫青霓虹，全场最亮的一款 */
html[data-skin="aurora"] {
  --bg: #0e0a1d; --panel: #191230; --panel-2: #201741;
  --border: #2c2055; --border-strong: #42327a;
  --text: #ece7ff; --text-2: #bcb0e6; --muted: #8d7dbd;
  --primary: #a78bfa; --primary-dark: #8b5cf6; --primary-soft: #2a1d52;
  --green: #34d399; --green-soft: #10352b;
  --amber: #fcd34d; --amber-soft: #382d0c;
  --red: #fb7185; --red-soft: #3d1622;
  --violet: #22d3ee; --violet-soft: #0d3340;
  --cyan: #67e8f9; --cyan-soft: #0e3342;
  --side-bg: #130e28; --top-bg: #171130;
  --brand-a: #8b5cf6; --brand-b: #22d3ee;
  --toast-bg: #e9e2ff; --toast-fg: #0e0a1d; --on-primary: #0e0a1d;
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 6px 18px rgba(0, 0, 0, .4);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, .6);
}


/* 赛博霓虹 —— 二次元：霓虹太阳 + 透视网格 + 城市剪影。全场最贵的一款 */
html[data-skin="neon"] {
  --bg: #0b0420; --panel: #150a33; --panel-2: #1b0d3f;
  --border: #2c1559; --border-strong: #452188;
  --text: #f2eaff; --text-2: #c3aee8; --muted: #9378c4;
  --primary: #ff4fa3; --primary-dark: #e02c88; --primary-soft: #330f4d;
  --green: #34d399; --green-soft: #0c3329;
  --amber: #fcd34d; --amber-soft: #3a2c0c;
  --red: #fb7185; --red-soft: #3d1622;
  --violet: #22d3ee; --violet-soft: #0d3340;
  --cyan: #67e8f9; --cyan-soft: #0e3342;
  --side-bg: #100630; --top-bg: #140838;
  --brand-a: #ff2d95; --brand-b: #00e5ff;
  --toast-bg: #ffe4f3; --toast-fg: #0b0420; --on-primary: #12042a;
  --shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 6px 18px rgba(0, 0, 0, .4);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, .6);
  --art: url("skins/neon.svg"); --art-veil: rgba(11, 4, 32, .42);
}

/* 带图案的皮肤：图铺在 body 最底层，上面盖一层 --art-veil。
   ★ 为什么用 background 而不是 ::before 加 z-index：
     ::before 要压在整个界面底下就得动层级，侧栏 / 弹窗 / toast 的 z-index 全得重排，
     一改就是满屏叠不对；直接铺在 body 背景上，谁都不用动。
   ★ 卡片是 --panel 实色，所以字永远在最上层，图案只在留白处露脸。 */
html[data-skin] body {
  background-image:
    linear-gradient(var(--art-veil, transparent), var(--art-veil, transparent)),
    var(--art, none);
  background-size: cover, cover;
  background-position: center top, center top;
  background-repeat: no-repeat, no-repeat;
  background-attachment: fixed, fixed;
}

/* ---------- 积分商城 ---------- */
.shophero {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
  padding: 22px 24px; border-radius: var(--radius);
  border: 1px solid var(--border);
  background: linear-gradient(135deg, var(--primary-soft), var(--panel) 72%);
  margin-bottom: 16px;
}
.shophero .big { font-size: 40px; font-weight: 700; line-height: 1; color: var(--primary); font-family: var(--mono); }
.shophero .big span { font-size: 15px; font-weight: 600; margin-left: 4px; color: var(--muted); }
.shophero .lbl { font-size: 12.5px; color: var(--muted); margin-bottom: 7px; letter-spacing: .3px; }
.shophero .formula { font-size: 12.5px; color: var(--text-2); line-height: 1.9; }
.shophero .formula b { color: var(--primary); }
/* 「作业和练习都算 / 满分才算」这行是补充说明，压灰一档，别跟正数抢眼睛 */
.shophero .formula .fx { font-size: 12px; color: var(--muted); }

.shopgrid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
}
.skincard {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); overflow: hidden;
  display: flex; flex-direction: column;
  transition: box-shadow .15s, border-color .15s, transform .15s;
}
.skincard:hover { box-shadow: var(--shadow); border-color: var(--border-strong); transform: translateY(-1px); }
.skincard.on { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-soft); }
/* 预览条：用这套皮肤自己的 4 个颜色画一个迷你界面（侧栏 + 卡片 + 主按钮） */
.skincard .pv {
  height: 86px; position: relative; display: flex; gap: 6px; padding: 9px;
  background: var(--pv-bg, #f4f6fb); border-bottom: 1px solid var(--border);
}
.skincard .pv .col { width: 26px; border-radius: 5px; background: var(--pv-side, #fff); padding: 5px 4px; display: flex; flex-direction: column; gap: 4px; }
.skincard .pv .col i { display: block; height: 3px; border-radius: 2px; background: var(--pv-muted, #c9d4e4); }
.skincard .pv .col i.hot { background: var(--pv-primary, #4f7cff); height: 4px; }
.skincard .pv .body { flex: 1; display: flex; flex-direction: column; gap: 5px; }
.skincard .pv .body .card { flex: 1; border-radius: 5px; background: var(--pv-panel, #fff); border: 1px solid var(--pv-line, #e3e8f2); }
.skincard .pv .body .btn { height: 12px; width: 46px; border-radius: 4px; background: var(--pv-primary, #4f7cff); }
.skincard .pv .tag {
  position: absolute; top: 7px; right: 7px; font-size: 10.5px; font-weight: 700;
  padding: 2px 7px; border-radius: 999px; background: var(--pv-text, #1e293b);
  color: var(--pv-bg, #fff); letter-spacing: .2px;
}
.skincard .info { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.skincard .nm { font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 7px; }
.skincard .nm .ic { color: var(--primary); font-size: 15px; }
.skincard .ds { font-size: 12.5px; color: var(--muted); line-height: 1.55; flex: 1; }
.skincard .ft { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.skincard .price { font-family: var(--mono); font-weight: 700; font-size: 16px; color: var(--primary); margin-right: auto; }
.skincard .price.free { color: var(--green); }
.skincard .price small { font-size: 11px; font-weight: 500; color: var(--muted); margin-left: 2px; }

/* 各皮肤的预览配色（改名时这里跟着改，否则货架上那条预览图不会变） */
.skincard[data-skin="default"] { --pv-bg:#f4f6fb; --pv-side:#fff; --pv-panel:#fff; --pv-line:#e3e8f2; --pv-primary:#4f7cff; --pv-muted:#c9d4e4; --pv-text:#1e293b; }
.skincard[data-skin="mint"]    { --pv-bg:#eef9f4; --pv-side:#fff; --pv-panel:#fff; --pv-line:#d5ebe1; --pv-primary:#0f9d76; --pv-muted:#b9dccd; --pv-text:#12302a; }
.skincard[data-skin="sunset"]  { --pv-bg:#fff7f0; --pv-side:#fffaf5; --pv-panel:#fff; --pv-line:#f6e3d3; --pv-primary:#e8621a; --pv-muted:#ecd0bd; --pv-text:#3a2318; }
.skincard[data-skin="sakura"]  { --pv-bg:#fdf3f7; --pv-side:#fff9fb; --pv-panel:#fff; --pv-line:#f6dde7; --pv-primary:#db2777; --pv-muted:#eec3d5; --pv-text:#3c1f2b; }
.skincard[data-skin="ocean"]   { --pv-bg:#0c1521; --pv-side:#0f1a27; --pv-panel:#15202e; --pv-line:#25344a; --pv-primary:#58a6ff; --pv-muted:#25344a; --pv-text:#e4eefa; }
.skincard[data-skin="aurora"]  { --pv-bg:#0e0a1d; --pv-side:#130e28; --pv-panel:#191230; --pv-line:#2c2055; --pv-primary:#a78bfa; --pv-muted:#2c2055; --pv-text:#ece7ff; }
.skincard[data-skin="forest"]  { --pv-bg:#f1f8f2; --pv-side:#ffffff; --pv-panel:#ffffff; --pv-line:#d7e9db; --pv-primary:#2f8b5f; --pv-muted:#c2ddc9; --pv-text:#1b3226; }
.skincard[data-skin="beach"]   { --pv-bg:#eff8fd; --pv-side:#ffffff; --pv-panel:#ffffff; --pv-line:#d3e9f6; --pv-primary:#1573a8; --pv-muted:#bcdcee; --pv-text:#12303f; }
.skincard[data-skin="snow"]    { --pv-bg:#f2f7fd; --pv-side:#ffffff; --pv-panel:#ffffff; --pv-line:#dbe7f4; --pv-primary:#3f6fa8; --pv-muted:#c8d9ea; --pv-text:#1b2a3a; }
.skincard[data-skin="anime"]   { --pv-bg:#fdf1f7; --pv-side:#ffffff; --pv-panel:#ffffff; --pv-line:#f4dcea; --pv-primary:#d64a8f; --pv-muted:#f0c8dd; --pv-text:#3a2030; }
.skincard[data-skin="galaxy"]  { --pv-bg:#0a0d21; --pv-side:#131735; --pv-panel:#131735; --pv-line:#262c56; --pv-primary:#8b9eff; --pv-muted:#262c56; --pv-text:#e8ecff; }
.skincard[data-skin="neon"]    { --pv-bg:#0b0420; --pv-side:#150a33; --pv-panel:#150a33; --pv-line:#2c1559; --pv-primary:#ff4fa3; --pv-muted:#2c1559; --pv-text:#f2eaff; }

/* 带图案的皮肤：货架上那条迷你预览也把真图铺进去（不然 6 款只看得到配色，
   看不出「青山」和「雪原」差在哪）。图上同样盖一层薄纱，示意块还看得见。 */
.skincard[data-art="forest"] .pv { background-image: linear-gradient(rgba(244,251,246,.55), rgba(244,251,246,.55)), url("skins/forest.svg"); background-size: cover; background-position: center; }
.skincard[data-art="beach"]  .pv { background-image: linear-gradient(rgba(240,250,255,.52), rgba(240,250,255,.52)), url("skins/beach.svg"); background-size: cover; background-position: center; }
.skincard[data-art="snow"]   .pv { background-image: linear-gradient(rgba(248,252,255,.6), rgba(248,252,255,.6)), url("skins/snow.svg"); background-size: cover; background-position: center; }
.skincard[data-art="anime"]  .pv { background-image: linear-gradient(rgba(255,247,251,.5), rgba(255,247,251,.5)), url("skins/anime.svg"); background-size: cover; background-position: center; }
.skincard[data-art="galaxy"] .pv { background-image: linear-gradient(rgba(10,13,33,.5), rgba(10,13,33,.5)), url("skins/galaxy.svg"); background-size: cover; background-position: center; }
.skincard[data-art="neon"]   .pv { background-image: linear-gradient(rgba(11,4,32,.48), rgba(11,4,32,.48)), url("skins/neon.svg"); background-size: cover; background-position: center; }

/* 积分榜 */
.rank { font-variant-numeric: tabular-nums; }
.rank th:first-child, .rank td:first-child { white-space: nowrap; }
.tbl.rank td { vertical-align: middle; }
.rank-no {
  display: inline-grid; place-items: center; width: 24px; height: 24px;
  border-radius: 7px; background: var(--panel-2); border: 1px solid var(--border);
  font-family: var(--mono); font-size: 12px; font-weight: 700; color: var(--text-2);
}
.rank-no.t1 { background: var(--amber-soft); color: var(--amber); border-color: var(--amber); }
.rank-no.t2 { background: var(--primary-soft); color: var(--primary); border-color: var(--primary); }
.rank-no.t3 { background: var(--violet-soft); color: var(--violet); border-color: var(--violet); }
.ptbar { display: flex; align-items: center; gap: 8px; min-width: 140px; }
.ptbar .track { flex: 1; height: 6px; border-radius: 99px; background: var(--panel-2); overflow: hidden; border: 1px solid var(--border); }
.ptbar .track i { display: block; height: 100%; background: var(--primary); border-radius: 99px; }
.ptbar .val { font-family: var(--mono); font-size: 12.5px; font-weight: 700; color: var(--text); }

/* ---- 商城（2026-10-28 第二批：称号 / 头像框 / 昵称颜色 / 实物 / 出题权） ---- */
.shopgrid2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }
.shopcard {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  display: flex; flex-direction: column; overflow: hidden;
}
.shopcard.on { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary) inset; }
.shopcard .hd {
  display: flex; align-items: center; gap: 9px; padding: 12px 14px 0;
  font-size: 13.5px; color: var(--text);
}
.shopcard .hd .ic {
  width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  background: var(--panel-2); color: var(--primary); font-size: 15px; border: 1px solid var(--border);
}
.shopcard .bd { padding: 10px 14px 14px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.shopcard .ds { font-size: 12.5px; color: var(--muted); line-height: 1.65; flex: 1; }
.shopcard .ft { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.shopcard .price { font-family: var(--mono); font-size: 15px; font-weight: 700; color: var(--text); margin-right: auto; }
.shopcard .price small { font-size: 11px; color: var(--muted); margin-left: 2px; font-weight: 400; }
.shopcard .price.free { color: var(--green); }

/* 我的名片：商城顶部那张小卡，买称号/头像框/昵称色就为了它变样 */
.mecard {
  display: flex; align-items: center; gap: 11px; padding-left: 18px;
  border-left: 1px solid var(--border); min-width: 190px;
}
.mecard .av {
  width: 40px; height: 40px; border-radius: 999px; display: grid; place-items: center;
  background: var(--primary-soft); color: var(--primary-dark);
  font-size: 16px; font-weight: 700; flex: none;
}
.mecard .nm { font-size: 14px; font-weight: 600; color: var(--text); }
.mecard .sub { display: flex; align-items: center; gap: 6px; margin-top: 4px; flex-wrap: wrap; }
.pt-dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 999px;
  margin-right: 5px; vertical-align: middle;
}

/* ==========================================================================
   头像（12 张内置 + 上传的照片）与头像框
   --------------------------------------------------------------------------
   一个 .avx 就是一颗圆头像：里面要么是 <img>，要么是姓名首字。
   尺寸用 avx-20 / 28 / 40 / 64 / 88 五档，别的地方要用直接套。
   ★ 头像框做成 box-shadow 外圈、不改盒子尺寸 —— 改成 padding / border 的话，
     戴上框的那一行会比别人高一截，整行文字跟着跳（列表里特别明显）。
   ========================================================================== */
.avx {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; box-sizing: border-box; border-radius: 999px; overflow: hidden;
  border: 2px solid var(--panel); background: var(--primary-soft);
  color: var(--primary-dark); font-weight: 700; line-height: 1; user-select: none;
}
.avx img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.avx b { position: relative; z-index: 0; font-weight: 700; }
.avx-20 { width: 20px; height: 20px; font-size: 10px; border-width: 1px; }
.avx-28 { width: 28px; height: 28px; font-size: 12px; }
.avx-40 { width: 40px; height: 40px; font-size: 15px; }
.avx-64 { width: 64px; height: 64px; font-size: 24px; }
.avx-88 { width: 88px; height: 88px; font-size: 32px; }

/* 四款头像框：外圈光影。颜色一律取皮肤变量，暗色皮肤下不会糊成一片 */
.avx.f_star { box-shadow: 0 0 0 2px var(--violet), 0 0 10px var(--violet-soft); }
.avx.f_double { box-shadow: 0 0 0 1px var(--panel), 0 0 0 3px var(--primary); }
.avx.f_fire { box-shadow: 0 0 0 2px var(--amber), 0 0 10px var(--amber-soft); }
.avx.f_crown { box-shadow: 0 0 0 2px var(--amber), 0 0 0 4px var(--red-soft); }

/* 商城「头像」那一栏：12 张图排成格子，点一下就换 */
.avgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(82px, 1fr)); gap: 10px; }
.avcell {
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
  padding: 12px 6px 9px; text-align: center; cursor: pointer; transition: .15s;
}
.avcell:hover { border-color: var(--primary); transform: translateY(-1px); }
.avcell.on { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.avcell .nm { font-size: 11px; color: var(--muted); margin-top: 7px; }
.avcell.on .nm { color: var(--primary-dark); font-weight: 600; }
.avcell img { border-radius: 999px; display: block; margin: 0 auto; }

/* 上传区：虚线框，一眼能看出「这里能点」 */
.avup {
  border: 1px dashed var(--border-strong); border-radius: 14px; padding: 14px;
  display: flex; align-items: center; gap: 12px; background: var(--panel-2);
}
.avup .txt { font-size: 12.5px; color: var(--muted); line-height: 1.7; }

/* 同学页：一人一张卡片 */
.mategrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 12px; }
.mate {
  background: var(--panel); border: 1px solid var(--border); border-radius: 16px;
  padding: 18px 12px 14px; text-align: center; transition: .15s; position: relative;
}
.mate:hover { border-color: var(--primary); transform: translateY(-2px); box-shadow: var(--shadow); }
.mate.me { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.mate .nm { margin-top: 10px; font-weight: 600; font-size: 14px; line-height: 1.35; }
.mate .sub { font-size: 11.5px; color: var(--muted); margin-top: 4px; min-height: 16px; }
.mate .big { font-size: 19px; font-weight: 700; margin-top: 9px; font-variant-numeric: tabular-nums; }
.mate .big span { font-size: 11px; font-weight: 400; color: var(--muted); margin-left: 2px; }
.mate .rk {
  position: absolute; top: 10px; left: 10px; font-size: 11px; font-weight: 700;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.mate .rk.top { color: var(--amber); }
.mate .me-tag {
  position: absolute; top: 10px; right: 10px; font-size: 10px; padding: 1px 6px;
  border-radius: 999px; background: var(--primary-soft); color: var(--primary-dark);
}

/* 打印试卷：把图案皮肤的背景图关掉。
   一张 A4 铺满山林 / 霓虹，墨盒受不了，表格也看不清。 */
@media print {
  html[data-skin] body { background-image: none !important; }
  .sidebar, .topbar { background-image: none !important; }
}
