/* ============ 1. 基础变量 ============ */
:root{
  --ink:#1c2b3a;
  --ink2:#3d5570;
  --muted:#6f869e;
  --line:rgba(126,157,192,.26);
  --line2:rgba(126,157,192,.14);
  --gold:#d9a23c;
  --gold-soft:#f3c75f;
  --violet:#7c5cd6;
  --violet-soft:#a98bf0;
  --teal:#1fa87a;
  --rose:#dc5b6e;
  --blue:#3b8fd4;
  --glass-bg:linear-gradient(145deg,rgba(255,255,255,.86),rgba(249,252,255,.70) 46%,rgba(240,247,255,.78));
  --glass-line:rgba(255,255,255,.78);
  --shadow-lg:0 24px 56px -24px rgba(45,80,125,.42), 0 2px 8px rgba(60,95,140,.08);
  --shadow-md:0 14px 32px -16px rgba(45,80,125,.38);
  --radius:24px;
  --fs:17px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth;font-size:var(--fs)}
body{
  margin:0;color:var(--ink);
  font-family:"PingFang SC","Microsoft YaHei","Hiragino Sans GB","Noto Sans CJK SC","Source Han Sans SC",Arial,sans-serif;
  font-variant-numeric:tabular-nums;
  background:
    radial-gradient(1100px 620px at 8% -6%, rgba(120,190,255,.34), transparent 62%),
    radial-gradient(900px 560px at 96% 2%, rgba(243,199,95,.30), transparent 60%),
    radial-gradient(1000px 700px at 70% 104%, rgba(112,214,186,.26), transparent 62%),
    radial-gradient(760px 520px at 22% 78%, rgba(169,139,240,.20), transparent 64%),
    linear-gradient(175deg,#fbfdff 0%,#f3f8ff 46%,#eef5ff 100%);
  min-height:100vh;
  -webkit-font-smoothing:antialiased;
}
button,input,select,textarea{font:inherit;color:inherit}
button{cursor:pointer;border:0;background:none}
h1,h2,h3,h4,p{margin:0}
::selection{background:rgba(124,92,214,.22)}
/* 滚动条 */
*::-webkit-scrollbar{width:11px;height:11px}
*::-webkit-scrollbar-track{background:rgba(180,205,230,.18);border-radius:99px}
*::-webkit-scrollbar-thumb{background:linear-gradient(180deg,rgba(140,175,215,.72),rgba(120,160,205,.72));border-radius:99px;border:2px solid transparent;background-clip:padding-box}
*::-webkit-scrollbar-thumb:hover{background:linear-gradient(180deg,rgba(110,150,200,.9),rgba(90,135,190,.9));background-clip:padding-box}

/* ============ 2. 液态玻璃通用件 ============ */
.glass{
  position:relative;
  background:
    radial-gradient(130% 90% at 10% -14%, rgba(255,255,255,.96), rgba(255,255,255,0) 56%),
    radial-gradient(100% 80% at 102% 116%, rgba(146,196,255,.34), rgba(255,255,255,0) 62%),
    var(--glass-bg);
  -webkit-backdrop-filter:blur(24px) saturate(190%);
  backdrop-filter:blur(24px) saturate(190%);
  border:1px solid var(--glass-line);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.98),
    inset 0 -1px 2px rgba(150,185,220,.34),
    var(--shadow-lg);
}
.glass-soft{
  background:
    radial-gradient(120% 90% at 8% -16%, rgba(255,255,255,.92), rgba(255,255,255,0) 58%),
    linear-gradient(150deg,rgba(255,255,255,.66),rgba(246,251,255,.50));
  -webkit-backdrop-filter:blur(16px) saturate(170%);
  backdrop-filter:blur(16px) saturate(170%);
  border:1px solid rgba(255,255,255,.66);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.92), 0 12px 28px -18px rgba(45,80,125,.40);
}
.app{max-width:1820px;margin:0 auto;padding:16px}

/* ============ 3. 按钮（液态玻璃） ============ */
.btn{
  position:relative;display:inline-flex;align-items:center;justify-content:center;gap:.42em;
  padding:.60em 1.05em;border-radius:999px;
  font-size:.94rem;font-weight:700;letter-spacing:.01em;white-space:nowrap;
  color:#25405c;
  background-image:
    linear-gradient(118deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.92) 48%,rgba(255,255,255,0) 66%),
    linear-gradient(160deg,rgba(255,255,255,.90),rgba(238,246,255,.72));
  background-size:260% 100%,100% 100%;
  background-position:175% 0,0 0;
  background-repeat:no-repeat;
  border:1px solid rgba(255,255,255,.82);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.98), inset 0 -2px 3px rgba(140,180,220,.30), 0 8px 20px -10px rgba(50,90,140,.52);
  -webkit-backdrop-filter:blur(14px) saturate(180%);
  backdrop-filter:blur(14px) saturate(180%);
  transition:background-position .55s cubic-bezier(.22,.8,.25,1), transform .16s ease, box-shadow .22s ease, color .2s;
  overflow:hidden;
}
.btn:hover{background-position:-70% 0,0 0;transform:translateY(-2px);box-shadow:inset 0 1px 0 rgba(255,255,255,1), inset 0 -2px 3px rgba(140,180,220,.28), 0 14px 26px -12px rgba(50,90,140,.60)}
.btn:active{transform:translateY(0) scale(.975)}
.btn:focus-visible{outline:3px solid rgba(59,143,212,.42);outline-offset:2px}
.btn.primary{color:#fff;text-shadow:0 1px 2px rgba(20,60,110,.30);
  background-image:
    linear-gradient(118deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.62) 48%,rgba(255,255,255,0) 66%),
    linear-gradient(160deg,#67b8f5,#3f8fdc 56%,#3f7fd0);
  border-color:rgba(255,255,255,.62);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.66), inset 0 -2px 4px rgba(20,60,110,.34), 0 14px 26px -12px rgba(45,110,190,.72)}
.btn.gold{color:#5a3c02;text-shadow:0 1px 0 rgba(255,255,255,.42);
  background-image:
    linear-gradient(118deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.78) 48%,rgba(255,255,255,0) 66%),
    linear-gradient(160deg,#ffdf8f,#f0be4c 58%,#dfa32f);
  border-color:rgba(255,255,255,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.80), inset 0 -2px 4px rgba(160,110,10,.30), 0 14px 26px -12px rgba(198,142,30,.72)}
.btn.danger{color:#a8384c;
  background-image:
    linear-gradient(118deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.90) 48%,rgba(255,255,255,0) 66%),
    linear-gradient(160deg,rgba(255,255,255,.94),rgba(255,236,240,.80));
  border-color:rgba(255,214,222,.9);box-shadow:inset 0 1px 0 rgba(255,255,255,.98), inset 0 -2px 3px rgba(230,160,175,.34), 0 8px 20px -12px rgba(190,80,105,.55)}
.btn.ok{color:#0d6e4d;
  background-image:
    linear-gradient(118deg,rgba(255,255,255,0) 30%,rgba(255,255,255,.90) 48%,rgba(255,255,255,0) 66%),
    linear-gradient(160deg,rgba(255,255,255,.94),rgba(228,250,240,.82));
  border-color:rgba(190,236,214,.9);box-shadow:inset 0 1px 0 rgba(255,255,255,.98), inset 0 -2px 3px rgba(120,205,170,.34), 0 8px 20px -12px rgba(30,150,110,.50)}
.btn.ghost{background-image:none;background:rgba(255,255,255,.44)}
.btn.ghost:hover{background:rgba(255,255,255,.72);background-image:none}
.btn.sm{padding:.42em .78em;font-size:.86rem}
.btn.xl{padding:.78em 1.5em;font-size:1.06rem}
.btn.wide{width:100%}
.btn[disabled]{opacity:.45;pointer-events:none}

/* ============ 4. 顶栏 ============ */
.topbar{
  position:sticky;top:8px;z-index:60;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:12px 16px;border-radius:var(--radius);
  flex-wrap:wrap;row-gap:10px;
}
.brand{display:flex;align-items:center;gap:13px;min-width:0}
.brand-mark{
  width:54px;height:54px;border-radius:17px;display:grid;place-items:center;
  font-weight:900;font-size:1.42rem;color:#fff;letter-spacing:-.02em;
  background:linear-gradient(140deg,#ffb36a,#f0779f 46%,#5ba7f0);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.6), 0 12px 24px -10px rgba(226,110,160,.72);
  flex:0 0 auto;
}
.brand h1{font-size:1.52rem;letter-spacing:.10em;line-height:1.12}
.topbar-mid{
  display:flex;gap:10px;flex:1 1 auto;justify-content:center;
  /* min-width:auto 让这块永远不会被压到比胶囊更窄（原先是 min-width:0，
     窗口一窄胶囊就被压扁，文字却 nowrap 不缩，于是冲出玻璃底） */
  min-width:auto;flex-wrap:wrap;row-gap:8px;
}
.stat-pill{
  display:flex;align-items:center;gap:9px;padding:8px 15px;border-radius:16px;
  background:linear-gradient(160deg,rgba(255,255,255,.80),rgba(244,250,255,.60));
  border:1px solid rgba(255,255,255,.72);box-shadow:inset 0 1px 0 rgba(255,255,255,.96), 0 10px 22px -14px rgba(45,85,130,.5);
  flex:0 0 auto;
}
.stat-pill .k{font-size:.72rem;color:var(--muted);letter-spacing:.06em;white-space:nowrap;flex:0 0 auto}
.stat-pill .v{font-size:1.16rem;font-weight:900;color:#20384f;white-space:nowrap;flex:0 0 auto}
.stat-pill .v.gold{color:#b8801a}
.stat-pill .v.violet{color:#6a48cf}
.header-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}
.save-state{font-size:.76rem;color:var(--muted);white-space:nowrap;padding:0 4px}

/* ============ 5. 面板 ============ */
.layout{display:grid;grid-template-columns:minmax(0,1fr) 384px;gap:16px;align-items:start;margin-top:16px}
.panel{border-radius:var(--radius);overflow:hidden}
.panel-head{
  padding:14px 18px;display:flex;align-items:center;justify-content:space-between;gap:14px;
  border-bottom:1px solid var(--line2);
  background:linear-gradient(180deg,rgba(255,255,255,.58),rgba(255,255,255,.18));
}
.panel-head h3{font-size:1.14rem;letter-spacing:.02em;display:flex;align-items:center;gap:8px}
.panel-head .hint{font-size:.80rem;color:var(--muted);margin-top:4px;font-weight:400;letter-spacing:.01em}
.panel-head .head-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
.panel-body{padding:16px}
.stack{display:grid;gap:16px}
aside.stack{position:sticky;top:calc(var(--topbar-h,104px) + 10px)}
/* ============ 6. 立体赛道（等距渲染） ============ */
.board-body{padding:12px 14px 14px}

/* --- 工具栏 --- */
.board-tools{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:11px}
.board-tools .tool-sep{width:1px;height:22px;background:rgba(150,180,210,.55);margin:0 3px}
.board-tools .btn.on{
  color:#fff;border-color:transparent;
  background:linear-gradient(150deg,#7a5ad8,#5f42bd);
  box-shadow:0 10px 20px -10px rgba(95,66,190,.9), inset 0 1px 0 rgba(255,255,255,.4);
}
.board-tools .btn.icon{width:42px;padding:0;justify-content:center;font-size:1.06rem}
.board-tools .tool-tip{margin-left:auto;font-size:.80rem;color:var(--muted);font-weight:700;letter-spacing:.01em}

/* --- 画布 --- */
.board-canvas-wrap{
  position:relative;width:100%;height:clamp(470px,58vh,780px);
  border-radius:20px;overflow:hidden;
  background:linear-gradient(180deg,#fbfdff,#e9f2fb);
  border:1px solid rgba(255,255,255,.9);
  box-shadow:inset 0 2px 10px rgba(120,155,200,.16), 0 20px 44px -28px rgba(40,80,125,.6);
}
.board-canvas-wrap canvas{display:block;width:100%;height:100%;touch-action:none;cursor:grab}

/* --- 管理台 · 家长留言 --- */
.dm-filter{display:flex;gap:8px;margin-top:11px;flex-wrap:wrap}
.dm-filter button{
  padding:7px 14px;border-radius:999px;font-size:.84rem;font-weight:800;color:#3c5570;
  background:rgba(255,255,255,.72);border:1.5px solid rgba(150,185,220,.42);
}
.dm-filter button.on{
  color:#fff;border-color:transparent;
  background:linear-gradient(150deg,#7a5ad8,#5f42bd);
  box-shadow:0 10px 20px -12px rgba(95,66,190,.9);
}
.dm-admin{display:grid;gap:8px;margin-top:11px;max-height:340px;overflow:auto}
.dm-admin .row{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;border-radius:14px;
  background:rgba(255,255,255,.78);border:1px solid rgba(255,255,255,.9);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9)}
.dm-admin .row.pending{background:linear-gradient(150deg,#fffaf0,#fff3dc);border-color:rgba(240,205,130,.85)}
.dm-admin .row.rejected{background:linear-gradient(150deg,#fff4f6,#ffe9ee);border-color:rgba(246,190,202,.85)}
.dm-admin .txt{flex:1 1 auto;min-width:0;line-height:1.45}
.dm-admin .txt b{display:block;font-size:.96rem;font-weight:800;color:#22384e;word-break:break-word}
.dm-admin .txt small{display:block;margin-top:4px;font-size:.78rem;font-weight:700;color:var(--muted)}
.dm-admin .txt small .st{font-style:normal;font-weight:900;margin-right:6px}
.dm-admin .st.ok{color:#0d6e4d}
.dm-admin .st.no{color:#a8384c}
.dm-admin .st.wait{color:#8a5a06}
.dm-admin .dm-del{flex:0 0 auto;width:30px;height:30px;border-radius:10px;font-size:.9rem;
  color:#a8384c;background:rgba(255,255,255,.9);border:1.5px solid rgba(246,190,202,.8)}
.dm-admin .dm-del:hover{background:#fff1f3}
.dm-admin .none{padding:12px 4px;font-size:.88rem;font-weight:700;color:var(--muted)}

/* --- 小组跳转条 --- */
.jump-bar{display:grid;grid-template-columns:repeat(8,minmax(0,1fr));gap:8px;margin-top:11px}
.jump-btn{
  --gc:#7ab7ff;
  display:flex;align-items:center;gap:8px;min-width:0;
  padding:8px 10px;border-radius:15px;cursor:pointer;font-family:inherit;text-align:left;
  background:linear-gradient(150deg,rgba(255,255,255,.94),rgba(243,249,255,.72));
  border:1px solid rgba(255,255,255,.85);
  box-shadow:inset 0 1px 0 #fff, 0 10px 20px -16px rgba(45,85,130,.7);
  transition:transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .18s, border-color .18s, background .18s;
}
.jump-btn:hover{transform:translateY(-2px);border-color:var(--gc);box-shadow:inset 0 1px 0 #fff,0 14px 24px -16px rgba(45,85,130,.85)}
.jump-btn:active{transform:translateY(0) scale(.985)}
.jump-btn img{
  width:31px;height:31px;border-radius:50%;object-fit:cover;flex:0 0 31px;background:#fff;
  border:2px solid var(--gc);box-shadow:0 4px 9px -4px rgba(45,75,110,.6);
}
.jump-btn .jb-txt{display:flex;flex-direction:column;line-height:1.16;min-width:0}
.jump-btn .jb-txt b{font-size:.86rem;font-weight:900;color:#22384e;white-space:nowrap}
.jump-btn .jb-txt i{font-size:.68rem;font-style:normal;font-weight:700;color:#7f96ac;white-space:nowrap}
.jump-btn.on{
  background:linear-gradient(150deg,#ffffff,#eef5ff);
  border-color:var(--gc);
  box-shadow:0 0 0 2.5px color-mix(in srgb,var(--gc) 34%,transparent), 0 16px 26px -16px rgba(45,85,130,.9);
}

/* ============ 7. 领先榜（赛道） ============ */
.race-list{display:grid;gap:9px}
.race-row{
  display:grid;grid-template-columns:52px 44px minmax(120px,150px) minmax(0,1fr) 92px 74px;
  align-items:center;gap:12px;padding:9px 14px;border-radius:16px;cursor:pointer;
  background:linear-gradient(150deg,rgba(255,255,255,.86),rgba(246,251,255,.62));
  border:1px solid rgba(255,255,255,.7);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95), 0 10px 22px -18px rgba(45,85,130,.5);
  transition:transform .18s, box-shadow .18s, background .3s, border-color .2s;
}
.race-row:hover{transform:translateX(4px);box-shadow:inset 0 1px 0 #fff,0 16px 26px -18px rgba(45,85,130,.6)}
.race-row.active{
  border-color:rgba(122,90,216,.85);
  box-shadow:inset 0 1px 0 #fff, 0 0 0 2.5px rgba(122,90,216,.26), 0 18px 30px -18px rgba(80,55,180,.65);
}
.race-row.top1{background:linear-gradient(150deg,rgba(255,251,236,.95),rgba(255,243,206,.66));border-color:rgba(240,200,110,.85);box-shadow:inset 0 1px 0 #fff,0 0 0 2px rgba(243,199,95,.34),0 18px 30px -18px rgba(190,135,20,.62)}
.race-row.top2{background:linear-gradient(150deg,rgba(255,255,255,.92),rgba(238,244,251,.7));border-color:rgba(196,212,228,.9)}
.race-row.top3{background:linear-gradient(150deg,rgba(255,252,246,.92),rgba(250,240,228,.7));border-color:rgba(232,206,178,.9)}
.race-rank{
  width:44px;height:44px;border-radius:14px;display:grid;place-items:center;font-weight:900;font-size:1.16rem;
  color:#fff;background:linear-gradient(150deg,var(--rc,#93a9c0),var(--rc2,#6d869e));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 8px 16px -8px var(--rc,#93a9c0);letter-spacing:-.02em;
}
.race-av{width:40px;height:40px;border-radius:50%;object-fit:cover;border:2.5px solid var(--gc,#cbd8e6);background:#fff;box-shadow:0 6px 12px -6px rgba(45,75,110,.6)}
.race-name{font-size:1.02rem;font-weight:900;color:#22384e;display:flex;flex-direction:column;line-height:1.22;min-width:0}
.race-name small{font-size:.72rem;font-weight:600;color:var(--muted);letter-spacing:.04em}
.race-bar{
  position:relative;height:22px;border-radius:999px;overflow:hidden;
  background:linear-gradient(180deg,rgba(206,222,238,.72),rgba(224,236,248,.62));
  box-shadow:inset 0 2px 5px rgba(70,105,145,.26), inset 0 -1px 0 rgba(255,255,255,.8);
}
.race-fill{
  position:absolute;left:0;top:0;bottom:0;width:0;border-radius:999px;
  background:var(--gc,#7ab7ff);
  background-image:linear-gradient(90deg,var(--gc,#7ab7ff),color-mix(in srgb,var(--gc,#7ab7ff) 58%,#ffffff));
  box-shadow:0 0 14px -2px var(--gc,#7ab7ff);
  transition:width .75s cubic-bezier(.25,.9,.3,1);
}
.race-fill::after{
  content:"";position:absolute;right:0;top:0;bottom:0;width:26px;border-radius:999px;
  background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,.72));
}
.race-fill .shine{
  position:absolute;inset:0;border-radius:999px;
  background:repeating-linear-gradient(115deg,rgba(255,255,255,.30) 0 8px,rgba(255,255,255,0) 8px 20px);
  animation:barShine 1.5s linear infinite;
}
@keyframes barShine{to{background-position:34px 0}}
.race-step{font-size:1.62rem;font-weight:900;color:#1d344a;text-align:right;line-height:1;letter-spacing:-.03em}
.race-step small{font-size:.70rem;font-weight:700;color:var(--muted);margin-left:2px}
.race-gap{font-size:.80rem;font-weight:800;text-align:right;color:#9a6d10;white-space:nowrap}
.race-gap.lead{color:#c08a12}
.race-gap.zero{color:#93a8bd;font-weight:600}
/* 图例 */
.board-legend{
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:10px;
  font-size:.78rem;font-weight:700;color:#7f96ac;
}
.board-legend i{display:inline-flex;align-items:center;gap:6px;font-style:normal}
.board-legend b{width:15px;height:15px;border-radius:5px;display:inline-block;box-shadow:inset 0 1px 0 rgba(255,255,255,.9),0 2px 5px -2px rgba(60,100,145,.7)}
.lg-base{background:linear-gradient(160deg,#ffffff,#e6f0fa);border:1px solid rgba(146,178,211,.85)}
.lg-pass{background:linear-gradient(160deg,#fffdf3,#f9e6bd);border:1px solid rgba(215,184,118,.9)}
.lg-event{background:linear-gradient(160deg,#faf7ff,#e7dcfd);border:1px solid rgba(146,116,218,.9)}
.lg-fin{background:linear-gradient(160deg,#fffdf0,#ffe6a0);border:1px solid rgba(206,163,58,.95)}
/* ============ 8. 侧栏组件 ============ */
.field{display:grid;gap:6px;min-width:0}
.field label{font-size:.80rem;color:#7a90a6;font-weight:700;letter-spacing:.02em}
.field input,.field select,.field textarea{
  width:100%;background:linear-gradient(170deg,rgba(255,255,255,.94),rgba(246,251,255,.86));
  border:1px solid rgba(168,192,218,.72);border-radius:13px;padding:.56em .72em;outline:none;font-size:.94rem;
  box-shadow:inset 0 2px 5px rgba(90,125,165,.13), 0 1px 0 rgba(255,255,255,.9);
  transition:border-color .18s, box-shadow .18s;
}
.field input:focus,.field select:focus,.field textarea:focus{border-color:#5aa8ec;box-shadow:0 0 0 4px rgba(90,168,236,.18), inset 0 2px 5px rgba(90,125,165,.10)}
.field textarea{resize:vertical;line-height:1.6;font-family:inherit}
.form-row{display:grid;grid-template-columns:minmax(0,1fr) 118px;gap:9px;margin-bottom:10px}
.form-row.one{grid-template-columns:1fr}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:9px}
.score-buttons{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:11px 0}
.score-buttons .btn{padding:.62em 0;font-size:1.02rem}
.quick-pick{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px;margin-bottom:11px}
.quick-pick button{
  display:flex;flex-direction:column;align-items:center;gap:3px;padding:8px 3px;border-radius:14px;
  background:linear-gradient(165deg,rgba(255,255,255,.86),rgba(243,249,255,.66));
  border:1px solid rgba(255,255,255,.78);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95), 0 8px 16px -12px rgba(45,85,130,.55);
  transition:transform .16s, box-shadow .16s, background .2s;
}
.quick-pick button:hover{transform:translateY(-2px);box-shadow:inset 0 1px 0 #fff,0 14px 22px -12px rgba(45,85,130,.65)}
.quick-pick button.on{background:linear-gradient(165deg,#e6f2ff,#d3e8ff);border-color:rgba(120,180,240,.9)}
.quick-pick img{width:32px;height:32px;border-radius:50%;object-fit:cover;border:2px solid var(--gc,#cbd8e6)}
.quick-pick span{font-size:.74rem;font-weight:800;color:#2b435b}
/* 管理面板快捷记分：一行常见原因的按钮，点一下直接生效 */
.qr-row{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin:2px 0 11px}
.qr-row .qr-tip{font-size:.80rem;font-weight:800;color:#7a90a6;margin-right:1px}
.qr-row .qr-tip em{font-style:normal;font-weight:700;color:var(--muted);font-size:.76rem}
.qr-row .btn.qr{flex:0 0 auto;font-size:.85rem;padding:.44em .74em;border-radius:12px}

/* 管理面板 · 授权码（三格输入：原授权码 / 新 / 确认） */
.form-row.three{grid-template-columns:repeat(3,minmax(0,1fr))}
.code-tip{font-size:.79rem;font-weight:700;color:#6b7f95;line-height:1.6}
.code-tip.ok{color:#2c8a63}
.code-tip.warn{color:#c0415a}
.code-key{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:900;letter-spacing:.04em}

.dorm-grid{display:grid;gap:9px;max-height:420px;overflow:auto;padding-right:3px}
.dorm-row{
  padding:11px 12px;border-radius:15px;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:center;
  background:linear-gradient(150deg,rgba(255,255,255,.82),rgba(246,251,255,.58));
  border:1px solid rgba(255,255,255,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95), 0 10px 20px -18px rgba(45,85,130,.6);
}
.dorm-row b{font-size:.94rem;display:block}
.dorm-row small{display:block;color:var(--muted);margin-top:3px;font-size:.76rem;line-height:1.5}
.dorm-actions{display:flex;gap:7px}
.dorm-actions .btn{width:46px;padding:.5em 0;font-size:1.06rem;border-radius:13px}
/* 今天已经加过 / 扣过的那一侧按钮变淡（跨过 0 点自动恢复） */
.dorm-actions .btn.done{opacity:.42;filter:saturate(.45);cursor:default}

.note{font-size:.79rem;color:var(--muted);line-height:1.65}
.note.tip{background:rgba(255,255,255,.55);border-radius:12px;padding:8px 11px;border:1px solid rgba(255,255,255,.7)}

/* 进行中的挑战 */
.task-list{display:grid;gap:11px;max-height:none}
.task{
  position:relative;padding:12px 13px;border-radius:17px;overflow:hidden;
  background:linear-gradient(150deg,rgba(255,255,255,.90),rgba(248,246,255,.70));
  border:1px solid rgba(198,182,238,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.98), 0 14px 28px -22px rgba(90,60,190,.7);
}
.task::before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;background:linear-gradient(180deg,#8a67e0,#5f3cbb)}
.task.urgent{border-color:rgba(240,180,80,.9);box-shadow:inset 0 1px 0 #fff,0 0 0 2px rgba(243,199,95,.30),0 14px 28px -22px rgba(190,130,20,.7)}
.task.urgent::before{background:linear-gradient(180deg,#f5c451,#df9c22)}
.task-top{display:flex;align-items:center;justify-content:space-between;gap:9px;margin-bottom:7px}
.task-name{display:flex;align-items:center;gap:8px;font-size:1.00rem;font-weight:900;color:#2a3f56;min-width:0}
.task-name img{width:30px;height:30px;border-radius:50%;object-fit:cover;border:2px solid var(--gc,#cbd8e6);flex:0 0 30px}
.task-name .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tag{font-size:.72rem;font-weight:900;padding:4px 9px;border-radius:999px;white-space:nowrap;flex:0 0 auto;
  background:linear-gradient(150deg,#fde3a7,#f7cd6c);color:#7d5307;box-shadow:inset 0 1px 0 rgba(255,255,255,.7)}
.tag.violet{background:linear-gradient(150deg,#ded3fb,#c3b0f5);color:#4b2da8}
.tag.rose{background:linear-gradient(150deg,#ffd9e0,#fbb9c6);color:#a8384c}
/* 过了截止日期：只加一个提示标记，任务本身照旧留在列表里（不自动过期） */
.tag.amber{background:linear-gradient(150deg,#ffe9bd,#f8cf85);color:#8a5a00}
.task p{font-size:.85rem;color:#5d7391;line-height:1.62;margin:0 0 10px}
.task-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.task-actions .btn{font-size:.85rem;padding:.46em .82em}
.task-actions select{
  font-size:.85rem;padding:.44em .6em;border-radius:11px;border:1px solid rgba(168,192,218,.8);
  background:rgba(255,255,255,.9);color:#3d5773;box-shadow:inset 0 2px 4px rgba(90,125,165,.1);
}

/* 记录 */
.history{max-height:340px;overflow:auto;display:grid;gap:7px;padding-right:3px}
.log{
  padding:9px 11px;border-radius:13px;font-size:.80rem;line-height:1.58;color:#5a7591;
  background:linear-gradient(150deg,rgba(255,255,255,.78),rgba(246,251,255,.55));
  border:1px solid rgba(255,255,255,.7);box-shadow:0 6px 14px -12px rgba(45,85,130,.6);
}
.log strong{color:#1f364c}.log time{color:#93a8bd;float:right;font-size:.74rem;margin-left:8px}

/* ============ 9. 班级资料 tabs ============ */
.tabs{display:flex;gap:9px;flex-wrap:wrap;padding:14px 16px 0}
.tabs button{
  padding:.5em 1.1em;border-radius:999px;font-size:.92rem;font-weight:800;color:#7890aa;
  background:linear-gradient(160deg,rgba(255,255,255,.78),rgba(240,247,255,.58));
  border:1px solid rgba(255,255,255,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95), 0 8px 16px -14px rgba(45,85,130,.7);
  transition:transform .16s, color .2s, background .25s;
}
.tabs button:hover{transform:translateY(-2px)}
.tabs button.active{color:#fff;background:linear-gradient(160deg,#6fb6f2,#3d87d2);box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 12px 22px -12px rgba(45,110,190,.8)}
.info-section{display:none;padding:14px 16px 18px}
.info-section.active{display:block}
.roster-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
.roster-card{
  padding:12px;border-radius:15px;
  background:linear-gradient(150deg,rgba(255,255,255,.84),rgba(246,251,255,.60));
  border:1px solid rgba(255,255,255,.72);box-shadow:inset 0 1px 0 rgba(255,255,255,.95),0 10px 20px -18px rgba(45,85,130,.6);
}
.roster-card h4{font-size:.94rem;display:flex;align-items:center;gap:7px;margin-bottom:7px}
.roster-card p{font-size:.82rem;line-height:1.72;color:#6a8298}
.leader{color:#b8801a;font-weight:900}
.roster-avatar{width:26px;height:26px;border-radius:50%;object-fit:cover;border:2px solid rgba(255,255,255,.9);background:#fff}
.event-list{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}
.event-card{
  padding:13px;border-radius:16px;
  background:linear-gradient(150deg,rgba(255,255,255,.86),rgba(247,244,255,.62));
  border:1px solid rgba(255,255,255,.74);box-shadow:inset 0 1px 0 rgba(255,255,255,.95),0 12px 24px -20px rgba(90,60,190,.7);
}
.event-card .num{width:30px;height:30px;border-radius:10px;background:linear-gradient(150deg,#9070e6,#6a45c8);display:grid;place-items:center;font-weight:900;font-size:.94rem;color:#fff;box-shadow:0 8px 16px -8px rgba(105,70,200,.9)}
.event-card h4{margin:9px 0 6px;font-size:.96rem;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.event-card h4 em{font-style:normal;font-size:.78rem;font-weight:900;color:#b8801a;background:rgba(243,199,95,.24);padding:2px 8px;border-radius:999px}
.event-card p{margin:0;color:#6b829a;font-size:.82rem;line-height:1.68}

/* ============ 10. 弹窗 ============ */
.modal{position:fixed;inset:0;background:rgba(96,125,160,.26);display:none;place-items:center;z-index:200;padding:18px;
  -webkit-backdrop-filter:blur(16px) saturate(150%);backdrop-filter:blur(16px) saturate(150%)}
.modal.show{display:grid;animation:fadeIn .22s ease both}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.modal-card{width:min(680px,96vw);max-height:92vh;border-radius:26px;overflow:hidden;display:flex;flex-direction:column;
  background:linear-gradient(155deg,rgba(255,255,255,.97),rgba(246,251,255,.94));
  border:1px solid rgba(255,255,255,.86);
  box-shadow:0 40px 90px -30px rgba(35,65,105,.6), inset 0 1px 0 #fff;
  animation:popIn .3s cubic-bezier(.2,1.2,.35,1) both}
@keyframes popIn{from{opacity:0;transform:translateY(18px) scale(.96)}to{opacity:1;transform:none}}
.modal-head{padding:17px 20px;border-bottom:1px solid var(--line2);display:flex;align-items:center;justify-content:space-between;gap:12px;flex:0 0 auto}
.modal-head h3{font-size:1.24rem}
.modal-body{padding:18px 20px;overflow:auto;flex:1 1 auto}
.modal-foot{padding:14px 20px;border-top:1px solid var(--line2);display:flex;gap:9px;justify-content:flex-end;flex-wrap:wrap;flex:0 0 auto;background:rgba(255,255,255,.5)}

/* 管理面板 */
#adminModal .modal-card{width:min(1240px,97vw);height:min(90vh,940px)}
.admin-tabs{display:flex;gap:8px;padding:12px 20px 0;flex-wrap:wrap;flex:0 0 auto}
.admin-tabs button{
  padding:.52em 1.1em;border-radius:999px;font-size:.94rem;font-weight:800;color:#7890aa;
  background:linear-gradient(160deg,rgba(255,255,255,.8),rgba(240,247,255,.6));
  border:1px solid rgba(255,255,255,.72);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95),0 8px 16px -14px rgba(45,85,130,.7);transition:transform .16s,color .2s;
}
.admin-tabs button:hover{transform:translateY(-2px)}
.admin-tabs button.active{color:#fff;background:linear-gradient(160deg,#8b6ae0,#5f3cbb);box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 12px 22px -12px rgba(95,60,190,.85)}
.admin-pane{display:none}
.admin-pane.active{display:block}
.sect{padding:14px 15px;border-radius:18px;margin-bottom:13px;
  background:linear-gradient(150deg,rgba(255,255,255,.74),rgba(246,251,255,.5));
  border:1px solid rgba(255,255,255,.76);box-shadow:inset 0 1px 0 rgba(255,255,255,.95),0 14px 28px -24px rgba(45,85,130,.8)}
.sect > h4{font-size:1.04rem;margin-bottom:10px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.sect > h4 .sub{font-size:.79rem;font-weight:500;color:var(--muted)}

/* 规则卡 */
.rule-card{padding:13px 14px;border-radius:17px;margin-bottom:11px;
  background:linear-gradient(150deg,rgba(255,255,255,.9),rgba(247,244,255,.62));
  border:1px solid rgba(206,192,240,.76);box-shadow:inset 0 1px 0 rgba(255,255,255,.95),0 14px 28px -24px rgba(90,60,190,.8)}
.rule-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:11px}
.rule-head .rn{font-weight:900;font-size:1.0rem;color:#4b2da8;display:flex;align-items:center;gap:7px}
.rule-head .spacer{flex:1 1 auto}
.seg{display:inline-flex;padding:3px;border-radius:999px;background:rgba(210,222,238,.6);box-shadow:inset 0 2px 4px rgba(80,115,155,.2)}
.seg button{padding:.36em .92em;border-radius:999px;font-size:.86rem;font-weight:800;color:#68809a;transition:.2s}
.seg button.on{background:linear-gradient(160deg,#fff,#eaf3ff);color:#3d6ea8;box-shadow:0 4px 10px -4px rgba(45,85,130,.55)}
.switch{display:inline-flex;align-items:center;gap:7px;font-size:.86rem;font-weight:800;color:#68809a;cursor:pointer;user-select:none}
.switch i{width:44px;height:25px;border-radius:999px;background:rgba(190,206,224,.85);position:relative;transition:.22s;box-shadow:inset 0 2px 4px rgba(80,115,155,.28)}
.switch i::after{content:"";position:absolute;left:3px;top:3px;width:19px;height:19px;border-radius:50%;background:#fff;transition:.22s;box-shadow:0 2px 5px rgba(50,85,125,.35)}
.switch.on i{background:linear-gradient(140deg,#7fd6ab,#37b57f)}
.switch.on i::after{transform:translateX(19px)}
.chips{display:flex;gap:7px;flex-wrap:wrap}
.chip{display:inline-flex;align-items:center;gap:6px;padding:.36em .8em;border-radius:999px;font-size:.84rem;font-weight:800;
  background:rgba(255,255,255,.8);border:1px solid rgba(180,202,226,.8);color:#6a8298;transition:.18s;cursor:pointer;user-select:none}
.chip:hover{border-color:rgba(120,175,235,.9)}
.chip.on{color:#4b2da8;background:linear-gradient(150deg,#efe9ff,#ded2fb);border-color:rgba(160,130,235,.9);box-shadow:0 6px 14px -10px rgba(105,70,200,.9)}
.rule-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:10px;align-items:end}
.rule-meta{display:flex;gap:14px;flex-wrap:wrap;font-size:.80rem;color:var(--muted);margin-top:9px;align-items:center}
.rule-meta b{color:#4b2da8}

/* ============ 11. 掉落特效 ============ */
.dropfx{position:fixed;inset:0;z-index:400;display:none;place-items:center;overflow:hidden;
  background:radial-gradient(circle at 50% 45%,rgba(38,20,80,.62),rgba(10,8,26,.90));
  -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px)}
.dropfx.show{display:grid}
.dropfx-inner{position:relative;width:100%;height:100%;display:grid;place-items:center}
.dx-rays{
  position:absolute;left:50%;top:50%;width:160vmax;height:160vmax;transform:translate(-50%,-50%);
  background:conic-gradient(from 0deg,rgba(255,214,120,.30) 0 3deg,rgba(255,214,120,0) 3deg 16deg);
  animation:raySpin 16s linear infinite;opacity:0;transition:opacity .7s;pointer-events:none;
}
.dropfx.hit .dx-rays{opacity:1}
.dx-orb{
  position:absolute;left:50%;top:50%;width:132px;height:132px;margin:-66px 0 0 -66px;border-radius:50%;
  background:radial-gradient(circle at 34% 28%,#fff8e2,#ffd166 34%,#f5a623 62%,#b8681a 100%);
  box-shadow:0 0 60px 18px rgba(255,190,80,.72), inset 0 -14px 24px rgba(150,80,10,.5), inset 0 10px 18px rgba(255,255,255,.7);
  opacity:0;
}
.dx-orb::after{content:"";position:absolute;inset:-16px;border-radius:50%;border:3px solid rgba(255,220,140,.55);animation:orbRing 1.1s ease-out infinite}
@keyframes orbRing{0%{transform:scale(.85);opacity:.9}100%{transform:scale(1.5);opacity:0}}
.dropfx.falling .dx-orb{opacity:1;animation:orbFall .82s cubic-bezier(.55,.06,.72,.32) forwards}
@keyframes orbFall{0%{transform:translateY(-58vh) scale(.55) rotate(-30deg);opacity:0}
  12%{opacity:1}
  80%{transform:translateY(0) scale(1.06) rotate(6deg)}
  100%{transform:translateY(0) scale(1) rotate(0)}}
.dropfx.hit .dx-orb{opacity:0;transition:opacity .34s .1s}
.dx-shock{
  position:absolute;left:50%;top:50%;width:180px;height:180px;margin:-90px 0 0 -90px;border-radius:50%;
  border:7px solid rgba(255,226,150,.95);opacity:0;pointer-events:none;
}
.dropfx.hit .dx-shock{animation:shockwave .85s cubic-bezier(.15,.75,.3,1) forwards}
@keyframes shockwave{0%{transform:scale(.2);opacity:1;border-width:16px}100%{transform:scale(6.5);opacity:0;border-width:1px}}
.dx-shock.b{border-color:rgba(180,215,255,.85);animation-delay:.13s}
.dx-particles{position:absolute;left:50%;top:50%;width:0;height:0}
.dx-particles i{position:absolute;left:0;top:0;width:var(--s);height:var(--s);border-radius:50%;
  box-shadow:0 0 12px 2px currentColor;background:currentColor;
  animation:dxBurst var(--d) cubic-bezier(.1,.72,.3,1) forwards}
@keyframes dxBurst{0%{transform:translate(-50%,-50%) scale(.3);opacity:1}70%{opacity:1}100%{transform:translate(calc(-50% + var(--tx)),calc(-50% + var(--ty))) scale(0);opacity:0}}
.dx-card{
  position:relative;z-index:5;width:min(660px,92vw);
  padding:30px 34px 26px;border-radius:30px;text-align:center;
  background:linear-gradient(158deg,rgba(255,255,255,.98),rgba(247,244,255,.95));
  border:2px solid rgba(255,255,255,.9);
  box-shadow:0 50px 110px -34px rgba(0,0,0,.75), 0 0 0 10px rgba(255,214,120,.20), inset 0 1px 0 #fff;
  opacity:0;transform:perspective(1100px) rotateY(88deg) scale(.8);transform-origin:50% 50%;
}
.dropfx.card .dx-card{animation:cardFlip .72s cubic-bezier(.2,.9,.25,1.04) forwards}
@keyframes cardFlip{from{opacity:0;transform:perspective(1100px) rotateY(88deg) scale(.8)}
  to{opacity:1;transform:perspective(1100px) rotateY(0) scale(1)}}
.dx-badge{display:inline-flex;align-items:center;gap:8px;padding:.4em 1.1em;border-radius:999px;font-size:.90rem;font-weight:900;letter-spacing:.08em;
  color:#6b3f04;background:linear-gradient(150deg,#ffe6a8,#f6c85c);box-shadow:inset 0 1px 0 rgba(255,255,255,.8),0 10px 20px -10px rgba(200,140,20,.9)}
.dx-card h2{font-size:2.5rem;margin:16px 0 10px;letter-spacing:.04em;color:#231a3d;line-height:1.15}
.dx-card .dx-desc{font-size:1.02rem;line-height:1.75;color:#4f5f77;margin:0 auto;max-width:520px}
.dx-who{display:inline-flex;align-items:center;gap:11px;margin:18px auto 5px;padding:8px 18px 8px 8px;border-radius:999px;
  background:linear-gradient(150deg,rgba(255,255,255,.96),rgba(240,247,255,.8));border:1px solid rgba(255,255,255,.8);
  box-shadow:0 12px 24px -16px rgba(45,85,130,.8)}
.dx-who img{width:52px;height:52px;border-radius:50%;object-fit:cover;border:3px solid var(--gc,#cbd8e6);background:#fff}
.dx-who b{font-size:1.14rem;color:#22384e}
.dx-who small{display:block;font-size:.80rem;color:var(--muted);font-weight:600;margin-top:2px}
.dx-target{margin:14px auto 0;max-width:430px}
.dx-target .field label{text-align:left}
.dx-target select{font-size:1rem;padding:.62em .8em}
.dx-actions{display:flex;gap:11px;justify-content:center;margin-top:22px;flex-wrap:wrap}
body.shake{animation:screenShake .42s cubic-bezier(.36,.07,.19,.97)}
@keyframes screenShake{10%,90%{transform:translate3d(-3px,1px,0)}20%,80%{transform:translate3d(5px,-3px,0)}30%,50%,70%{transform:translate3d(-8px,2px,0)}40%,60%{transform:translate3d(8px,-2px,0)}}
.dx-skip{position:absolute;right:22px;top:20px;z-index:8}

/* ============ 12. Toast ============ */
.toast{
  position:fixed;right:22px;bottom:22px;z-index:500;max-width:min(520px,86vw);
  padding:14px 20px;border-radius:17px;font-size:.96rem;font-weight:800;color:#0d5c42;
  background:linear-gradient(150deg,rgba(255,255,255,.97),rgba(230,251,242,.94));
  border:1px solid rgba(150,225,195,.9);
  box-shadow:0 26px 54px -22px rgba(20,110,80,.6), inset 0 1px 0 #fff;
  opacity:0;transform:translateY(16px) scale(.96);pointer-events:none;transition:.3s cubic-bezier(.2,.9,.3,1);
}
.toast.show{opacity:1;transform:none}
.toast.warn{color:#8a5a05;background:linear-gradient(150deg,rgba(255,255,255,.97),rgba(255,246,224,.95));border-color:rgba(245,215,150,.95)}

.footer{padding:20px 4px 10px;text-align:center;color:#7c92a7;font-size:.78rem;line-height:1.8}

/* ============ 13. 演示模式 ============ */
body.present .topbar,
body.present aside.stack,
body.present .info-panel,
body.present .start-strip{display:none!important}
body.present .app{padding:8px;max-width:none}
body.present .layout{grid-template-columns:1fr;margin-top:8px;gap:10px}
body.present .panel-head{padding:9px 14px}
body.present .panel-head .hint{display:none}
body.present{--fs:1rem}
html.present{--fs:19px}
body.present .board-canvas-wrap{height:calc(100vh - 336px);min-height:400px}
body.present .board-tools{margin-bottom:9px}
body.present .jump-bar{grid-template-columns:repeat(8,minmax(0,1fr))}
body.present .jump-btn .jb-txt b{font-size:.94rem}
body.present .race-row{grid-template-columns:56px 46px minmax(130px,190px) minmax(0,1fr) 104px 80px;padding:8px 16px}
body.present .race-step{font-size:1.92rem}
.present-bar{position:fixed;right:18px;top:14px;z-index:120;display:none;gap:9px}
body.present .present-bar{display:flex}

/* ============ 14. 响应式 ============ */
@media(max-width:1560px){
  .layout{grid-template-columns:minmax(0,1fr) 344px}
  .jump-bar{grid-template-columns:repeat(4,minmax(0,1fr))}
}
@media(max-width:1320px){
  :root{--fs:16px}
  .layout{grid-template-columns:1fr}
  aside.stack{position:static}
  .topbar-mid{display:none}
  .roster-grid{grid-template-columns:repeat(3,1fr)}
  .event-list{grid-template-columns:repeat(3,1fr)}
  .board-tools .tool-tip{display:none}
}
@media(max-width:900px){
  .topbar{position:static;flex-wrap:wrap}
  .roster-grid,.event-list{grid-template-columns:repeat(2,1fr)}
  .race-row{grid-template-columns:44px 38px minmax(92px,1fr) minmax(0,1.2fr) 74px;gap:9px}
  .race-gap{display:none}
  .score-buttons{grid-template-columns:repeat(4,1fr)}
  .quick-pick{grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
  .jump-bar{grid-template-columns:repeat(2,minmax(0,1fr))}
  .board-canvas-wrap{height:400px}
  .form-row.three{grid-template-columns:1fr}
}


/* ============ 12. 省电模式（低配希沃白板） ============ */
/* 中档及以下：关掉全屏毛玻璃。backdrop-filter 会强制整屏离屏合成，是白板上最大的卡顿来源 */
html.perf-lite .glass,
html.perf-lite .glass-soft{
  -webkit-backdrop-filter:none;backdrop-filter:none;
}
html.perf-lite .glass{
  background:linear-gradient(160deg,rgba(255,255,255,.95),rgba(247,251,255,.91));
}
html.perf-lite .glass-soft{
  background:linear-gradient(160deg,rgba(255,255,255,.88),rgba(246,251,255,.82));
  border-color:rgba(210,228,245,.9);
}
html.perf-lite .modal{
  -webkit-backdrop-filter:none;backdrop-filter:none;
  background:rgba(96,125,160,.46);
}
html.perf-lite .dropfx{
  -webkit-backdrop-filter:none;backdrop-filter:none;
}
/* 低档：再关掉几处持续重绘的装饰动画 */
html.perf-low .dx-rays{display:none}
html.perf-low .race-fill .shine{display:none}
html.perf-low{
  --shadow-lg:0 14px 30px -22px rgba(45,80,125,.40), 0 1px 4px rgba(60,95,140,.06);
  --shadow-md:0 10px 22px -14px rgba(45,80,125,.32);
}


/* ============ 15. 手机端简化版 ============ */
/* 只在 <html class="is-mobile"> 时生效；桌面与教室白板完全不受影响。
   手机端不做 3D 渲染，只保留：加分科目 / 小组加减分 / 挑战任务判定 / 加分历史 */
.m-app{display:none}
html.is-mobile .app{display:none}
html.is-mobile .m-app{display:block}
html.is-mobile body{overflow-x:hidden}
html.is-mobile .toast{bottom:calc(18px + env(safe-area-inset-bottom,0px))}

.m-app{
  max-width:680px;margin:0 auto;
  padding:0 13px calc(26px + env(safe-area-inset-bottom,0px));
  color:var(--ink);
  -webkit-text-size-adjust:100%;
}

/* ---- 顶栏 ---- */
.m-head{
  position:sticky;top:0;z-index:30;
  display:flex;align-items:center;gap:11px;
  padding:calc(10px + env(safe-area-inset-top,0px)) 2px 11px;
  background:linear-gradient(180deg,#eef5fc 78%,rgba(238,245,252,0));
}
.m-logo{
  width:42px;height:42px;flex:0 0 auto;border-radius:14px;
  display:flex;align-items:center;justify-content:center;
  font-size:17px;font-weight:900;color:#fff;letter-spacing:.01em;
  background:linear-gradient(160deg,#7a4391,#4a2159);
  box-shadow:0 10px 20px -11px rgba(91,44,111,.85), inset 0 1px 0 rgba(255,255,255,.35);
}
.m-head-txt{flex:1;min-width:0}
.m-head-txt h1{font-size:1.24rem;font-weight:900;letter-spacing:.01em;color:#22384e;margin:0}
.m-sync{
  margin-top:3px;font-size:.74rem;font-weight:800;color:var(--muted);
  cursor:pointer;letter-spacing:.01em;
}
.m-icon{
  width:42px;height:42px;flex:0 0 auto;border-radius:14px;cursor:pointer;
  border:1.5px solid rgba(126,157,192,.28);background:rgba(255,255,255,.9);
  font-size:18px;line-height:1;
}

/* ---- 卡片 ---- */
.m-card{
  background:#fff;border:1px solid rgba(126,157,192,.22);
  border-radius:19px;padding:13px;margin-bottom:12px;
  box-shadow:0 10px 24px -19px rgba(45,80,125,.5);
}
.m-card-head{display:flex;align-items:baseline;justify-content:space-between;gap:9px;margin-bottom:11px}
.m-card-head b{font-size:1.02rem;font-weight:900;color:#22384e}
.m-card-head i{font-style:normal;font-size:.72rem;font-weight:800;color:var(--muted);text-align:right}

/* ---- 科目条 ---- */
.m-chips{display:flex;gap:8px;overflow-x:auto;padding:2px 1px 6px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.m-chips::-webkit-scrollbar{display:none}
.m-chip{
  flex:0 0 auto;padding:10px 16px;border-radius:999px;cursor:pointer;
  border:1.5px solid rgba(126,157,192,.3);background:#f5faff;
  color:#4a6a8a;font-size:.92rem;font-weight:900;font-family:inherit;
  transition:transform .1s ease;
}
.m-chip:active{transform:scale(.95)}
.m-chip.on{
  color:#fff;border-color:transparent;
  background:linear-gradient(160deg,#7a4391,#5B2C6F);
  box-shadow:0 9px 18px -11px rgba(91,44,111,.9);
}

/* ---- 小组列表 ---- */
.m-groups{display:grid;gap:9px}
.m-group{
  display:grid;gap:9px;padding:11px;
  border-radius:16px;background:#fbfdff;
  border:1px solid rgba(126,157,192,.2);
  border-left:5px solid var(--gc,#5B2C6F);
  transition:background .18s ease;
}
.m-group.flash{background:#fff8e6}
.m-group-top{display:flex;align-items:center;gap:10px;min-width:0}
.m-av{width:36px;height:36px;flex:0 0 auto;border-radius:12px;object-fit:cover;background:#e9f1f9}
.m-gname{
  flex:1;min-width:0;font-size:1.04rem;font-weight:900;color:#22384e;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.m-gno{font-size:.7rem;font-weight:800;color:var(--muted);margin-left:6px}
.m-lead{
  display:inline-block;margin-left:6px;padding:2px 7px;border-radius:7px;
  font-size:.64rem;font-weight:900;color:#7a5a06;background:#fdeeb8;
  vertical-align:2px;
}
.m-gstep{flex:0 0 auto;font-size:1.3rem;font-weight:900;color:#1d344a;font-variant-numeric:tabular-nums}
.m-gstep small{font-size:.66rem;font-weight:800;color:var(--muted);margin-left:2px}
.m-btns{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}
.m-btn{
  height:46px;border-radius:13px;cursor:pointer;font-family:inherit;
  display:flex;align-items:center;justify-content:center;
  font-size:1rem;font-weight:900;border:1.5px solid transparent;
  transition:transform .09s ease;
}
.m-btn:active{transform:scale(.93)}
.m-btn.minus{background:#fff1f3;color:#b8394f;border-color:#ffd9e0}
.m-btn.plus{background:#eafaf2;color:#0d7a52;border-color:#c6ecda}

/* ---- 挑战任务 ---- */
.m-tasks{display:grid;gap:9px}
.m-task{
  padding:11px;border-radius:16px;background:#fbfdff;
  border:1px solid rgba(126,157,192,.2);
  border-left:5px solid var(--gc,#5B2C6F);
}
.m-task-top{display:flex;align-items:center;gap:9px;font-size:1rem;font-weight:900;color:#22384e;min-width:0}
.m-task-top span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.m-task-top{cursor:pointer}
.m-task-lead{margin:5px 0 0;font-size:.72rem;font-weight:800;color:#8a9bb0}
.m-task-desc{font-size:.8rem;font-weight:600;color:var(--muted);line-height:1.5;margin:8px 0 10px}
.m-sel{
  width:100%;box-sizing:border-box;margin-bottom:8px;padding:10px 12px;
  border-radius:12px;border:1.5px solid rgba(126,157,192,.3);background:#fff;
  font-family:inherit;font-size:.9rem;font-weight:800;color:#2d4a66;
}
.m-tbtns{display:flex;gap:7px;flex-wrap:wrap}
.m-tbtn{
  flex:1 1 auto;min-width:86px;height:42px;border-radius:12px;cursor:pointer;
  font-family:inherit;font-size:.9rem;font-weight:900;border:1.5px solid transparent;
  transition:transform .09s ease;
}
.m-tbtn:active{transform:scale(.95)}
.m-tbtn.ok{background:#eafaf2;color:#0d7a52;border-color:#c6ecda}
.m-tbtn.no{background:#fff1f3;color:#b8394f;border-color:#ffd9e0}
.m-tbtn.gh{background:#f4f8fc;color:#5b7a95;border-color:#dde8f2}

/* ---- 历史 ---- */
.m-history{display:grid;gap:6px}
.m-log{
  padding:9px 11px;border-radius:12px;background:#f8fbfe;
  border:1px solid rgba(126,157,192,.16);
  font-size:.85rem;font-weight:600;color:#3d5570;line-height:1.45;
}
.m-log strong{color:#22384e;font-weight:900}
.m-log .m-subj{font-style:normal;font-weight:900;color:#8a5cb8}
.m-log time{display:block;margin-top:3px;font-size:.68rem;font-weight:800;color:#9badc0}
.m-empty{
  padding:14px;border-radius:13px;text-align:center;
  font-size:.84rem;font-weight:700;color:var(--muted);
  background:#f7fbff;border:1px dashed rgba(126,157,192,.4);line-height:1.6;
}
.m-foot{
  margin-top:16px;text-align:center;
  font-size:.72rem;font-weight:700;color:#9badc0;letter-spacing:.02em;
}


/* ============ 16. 小组成员弹窗 ============ */
.gm-card{width:min(520px,94vw)}
.gm-avatar{
  width:76px;height:76px;border-radius:22px;object-fit:cover;
  display:block;margin:2px auto 12px;background:#e9f1f9;
  box-shadow:0 14px 30px -18px rgba(45,80,125,.7);
}
.gm-meta{text-align:center;margin-bottom:16px}
.gm-meta b{display:block;font-size:1.32rem;font-weight:900;color:#22384e;letter-spacing:.01em}
.gm-meta span{display:block;margin-top:5px;font-size:.84rem;font-weight:800;color:var(--muted)}
.gm-members{display:grid;gap:9px}
.gm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:8px}
.gm-leader{
  display:flex;align-items:center;gap:8px;padding:10px 13px;border-radius:13px;
  background:linear-gradient(160deg,#fdf6e3,#fbeecb);
  border:1px solid rgba(217,162,60,.45);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9);
}
.gm-leader .crown{font-size:1rem;line-height:1}
.gm-leader b{font-size:1.04rem;font-weight:900;color:#7a5a06;cursor:pointer;white-space:nowrap}
.gm-leader .lbl{
  margin-left:auto;padding:2px 10px;border-radius:8px;
  font-size:.68rem;font-weight:900;color:#fff;
  background:linear-gradient(160deg,#dfa93c,#c2871a);
}
.gm-member{
  display:flex;align-items:center;justify-content:center;padding:9px 8px;border-radius:12px;
  background:#f7fbff;border:1px solid rgba(126,157,192,.22);
  font-size:.92rem;font-weight:800;color:#2d4a66;white-space:nowrap;
  cursor:pointer;user-select:none;transition:background .16s ease;
  overflow:hidden;text-overflow:ellipsis;
}
.gm-member:hover{background:#eef6ff}
.gm-tip{font-size:.76rem;font-weight:700;color:var(--muted);margin-right:auto}

/* ============ 17. 批量「待处理挑战」清单 ============ */
.dx-list{display:none;width:100%;max-height:46vh;overflow:auto;margin:4px 0 6px;text-align:left}
.dx-list.on{display:block}
.dx-item{
  display:flex;align-items:center;gap:11px;padding:11px 13px;border-radius:15px;
  background:rgba(255,255,255,.72);border:1px solid rgba(255,255,255,.9);
  box-shadow:0 8px 20px -16px rgba(45,80,125,.6);
  margin-bottom:9px;
}
.dx-item img{width:40px;height:40px;border-radius:13px;object-fit:cover;flex:0 0 auto;background:#e9f1f9}
.dx-item .info{flex:1;min-width:0}
.dx-item .info b{display:block;font-size:1rem;font-weight:900;color:#22384e}
.dx-item .info b em{font-style:normal;font-size:.76rem;font-weight:800;color:var(--muted);margin-left:6px}
.dx-item .info small{display:block;margin-top:3px;font-size:.78rem;font-weight:700;color:#6b8399}
.dx-item .ops{display:flex;gap:7px;flex:0 0 auto}
.dx-item .ops button{
  height:36px;padding:0 13px;border-radius:11px;cursor:pointer;font-family:inherit;
  font-size:.84rem;font-weight:900;border:1.5px solid transparent;
}
.dx-item .ops .ok{background:#eafaf2;color:#0d7a52;border-color:#c6ecda}
.dx-item .ops .gh{background:#f4f8fc;color:#5b7a95;border-color:#dde8f2}

/* ============ 18. 手机端寝室 ============ */
.m-dorms{display:grid;gap:9px}
.m-dorm{
  display:flex;align-items:center;gap:10px;padding:11px;
  border-radius:16px;background:#fbfdff;border:1px solid rgba(126,157,192,.2);
}
.m-dorm-info{flex:1;min-width:0}
.m-dorm-info b{display:block;font-size:.98rem;font-weight:900;color:#22384e}
.m-dorm-info small{display:block;margin-top:4px;font-size:.74rem;font-weight:700;color:var(--muted);line-height:1.5}
.m-dorm-info small em{font-style:normal;color:#8a5cb8;font-weight:900}
.m-dorm-ops{display:flex;gap:7px;flex:0 0 auto}
.m-dorm-ops button{
  width:52px;height:46px;border-radius:13px;cursor:pointer;font-family:inherit;
  font-size:1.1rem;font-weight:900;border:1.5px solid transparent;
}
.m-dorm-ops .plus{background:#eafaf2;color:#0d7a52;border-color:#c6ecda}
.m-dorm-ops .minus{background:#fff1f3;color:#b8394f;border-color:#ffd9e0}
.m-dorm .done{opacity:.45}

/* 待接受徽标与分组标题 */
.task-group-title{
  display:flex;align-items:center;gap:7px;margin:4px 2px 8px;
  font-size:.86rem;font-weight:900;color:#22384e;
}
.task-group-title .dot{width:8px;height:8px;border-radius:50%;background:var(--rose);box-shadow:0 0 0 4px rgba(220,91,110,.16)}
.task.waiting{border-color:rgba(220,91,110,.42);background:linear-gradient(160deg,rgba(255,250,251,.9),rgba(255,243,246,.7))}
.task.waiting .task-top .nm{color:#a8384c}
.m-task.waiting{border-left-color:var(--rose);background:linear-gradient(160deg,#fffafb,#fff4f6)}
.m-badge-wait{
  display:inline-block;margin-left:7px;padding:2px 8px;border-radius:8px;
  font-size:.66rem;font-weight:900;color:#fff;background:linear-gradient(160deg,#e2707f,#c94b60);
  vertical-align:2px;
}

/* ============ 19. 组长标识 · 成员弹窗入口 · 批量待接受 ============ */
/* 快捷记分：组名置顶作「看成员」的入口，中间是头像，点哪儿都能选中该组 */
/* 侧栏是窄列（344~384px）：4 列塞不下 —— `1fr` 带最小内容宽度下限，4 列至少 420px，
   会被 .panel 的 overflow:hidden 把第 4 列裁掉（老师反馈「只能看到六个小组」）。
   改为按可用宽度自动排：每格最小 104px（＝组名 + 头像 + 「组长 xx · N 格」不截断），
   344px 侧栏排 2 列、384px 侧栏排 3 列，窄窗口（侧栏铺满）自动多排，永不横向溢出。 */
aside.stack .quick-pick{grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
.quick-pick button.qp-cell{
  flex-direction:column;align-items:center;gap:3px;min-width:0;
  padding:8px 4px 7px;cursor:pointer;font-family:inherit;
}
.qp-cell .qp-name{
  display:block;max-width:100%;font-size:.76rem;font-weight:900;color:#2b435b;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  border-bottom:1px dashed rgba(126,157,192,.55);padding-bottom:1px;
  transition:color .15s ease,border-color .15s ease;
}
.qp-cell .qp-name:hover{color:#5B2C6F;border-bottom-color:#5B2C6F}
.qp-cell .qp-meta{
  max-width:100%;font-size:.64rem;font-weight:800;color:#8a9bb0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* 挑战卡片里的组长标识 */
.task-name{flex-wrap:wrap;row-gap:3px}
.task-name .nm{flex:1 1 110px}
.leader-chip{
  flex:0 0 auto;padding:2px 8px;border-radius:8px;
  font-size:.68rem;font-weight:900;color:#5a4a12;background:#fdf3d4;
  border:1px solid rgba(217,162,60,.42);white-space:nowrap;
}
.task-name .nm{cursor:pointer}
.task.waiting .leader-chip{color:#8d2f42;background:#ffe9ee;border-color:rgba(220,91,110,.42)}
.task.waiting .task-name .nm{color:#a8384c}
/* 挑战任务不会自动过期：过了截止日期只换个提示色，任务照旧留着 */
.task-tags{display:flex;gap:6px;align-items:center;flex:0 0 auto}
.task.overdue{border-color:rgba(222,176,88,.62)}
.task.overdue::before{background:linear-gradient(180deg,#ecc678,#c9942f)}
.m-task.overdue{border-left-color:#e0ab4a}
.m-badge-over{
  display:inline-block;margin-left:7px;padding:2px 8px;border-radius:8px;
  font-size:.66rem;font-weight:900;color:#6d4600;background:linear-gradient(160deg,#ffe4ac,#f5c96a);
  vertical-align:2px;
}

/* 批量清单处理完毕 */
.dx-done{padding:20px 12px;text-align:center;font-size:.92rem;font-weight:900;color:#0d7a52}
.dx-done small{display:block;margin-top:6px;font-size:.76rem;font-weight:700;color:#5b7a95}

/* 手机端：组名可点开成员，旁边写组长 */
.m-gname{cursor:pointer}
.m-gleader{
  display:inline-block;margin-left:6px;padding:1px 7px;border-radius:8px;
  font-size:.68rem;font-weight:900;color:#5a4a12;
  background:#fdf3d4;border:1px solid rgba(217,162,60,.42);
  vertical-align:2px;
}
.m-date{
  font-family:inherit;font-size:.78rem;font-weight:800;color:#3d5570;
  padding:5px 8px;border-radius:10px;
  border:1.5px solid rgba(126,157,192,.3);background:#fff;
}
.m-note{
  margin-top:9px;font-size:.74rem;font-weight:700;color:var(--muted);
  line-height:1.55;
}

/* ============ 20. 手机端彻底屏蔽桌面端的动态层 ============ */
/* 掉落 / 挑战动画是桌面端专属：手机端连元素都不显示，杜绝任何动画出现在手机上 */
html.is-mobile .dropfx,
html.is-mobile .present-bar,
html.is-mobile .board-canvas-wrap{display:none !important}
/* 家长留言弹幕也是「压在某块棋盘上」的桌面端图层：手机端没有棋盘，弹幕模块
   自己不启动，但这块 div 还留在 body 里，于是变成一条横在屏幕上方的白色带子
   （33% 屏高）＋一个悬空的开关按钮。这里整块隐掉。
   家长查看页（/overview）不使用本文件，且从不加 is-mobile 类，所以家长用手机
   看自己的页面时照常显示棋盘弹幕 —— 这条只管老师手机端的管理界面。 */
html.is-mobile .dm-layer,
html.is-mobile .dm-toggle{display:none !important}
html.is-mobile *{-webkit-tap-highlight-color:transparent}

/* ============ 21. 首帧遮罩 ============
   在线版要等云端数据回来才知道真实格数。不遮住的话会先画出本机旧缓存，
   数据一到再整体跳一次 —— 刷新页面时那一下闪动就是这么来的。
   .app 先透明，主逻辑调 bootReady() 后淡入；遮罩本身延迟 0.5s 才显形，
   所以加载够快时用户根本看不到它。 */
html.booting .app,html.booting .m-app{opacity:0}
html:not(.booting) .app,html:not(.booting) .m-app{opacity:1;transition:opacity .26s ease}
.boot-mask{
  position:fixed;inset:0;z-index:9999;display:none;background:transparent;
  align-items:center;justify-content:center;flex-direction:column;gap:14px;
  font-weight:800;letter-spacing:.08em;color:var(--muted);
}
html.booting .boot-mask{display:flex;opacity:0;animation:bootFade .3s ease .5s forwards}
@keyframes bootFade{to{opacity:1}}
.boot-spin{
  width:34px;height:34px;border-radius:50%;
  border:3px solid rgba(126,157,192,.26);border-top-color:var(--violet);
  animation:bootSpin .8s linear infinite;
}
@keyframes bootSpin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){
  html:not(.booting) .app,html:not(.booting) .m-app{transition:none}
  .boot-spin{animation:none}
  html.booting .boot-mask{animation:none;opacity:1}
}

/* ========== 22. 登录悬浮窗（仅桌面在线端、未登录点修改类按钮时出现） ========== */
.auth-mask{
  position:fixed;inset:0;z-index:400;display:none;align-items:center;justify-content:center;
  background:rgba(28,18,40,.46);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
}
.auth-mask.show{display:flex;animation:authFade .18s ease}
@keyframes authFade{from{opacity:0}to{opacity:1}}
.auth-card{
  width:min(360px,88vw);background:#fff;border-radius:18px;padding:26px 26px 20px;position:relative;
  border-top:4px solid #C9A963;box-shadow:0 24px 70px -18px rgba(40,20,70,.55);
  animation:authPop .2s ease;
}
@keyframes authPop{from{opacity:0;transform:translateY(10px) scale(.97)}to{opacity:1;transform:none}}
.auth-x{
  position:absolute;right:10px;top:10px;width:32px;height:32px;border:none;border-radius:50%;
  background:#f2edf7;font-size:14px;color:#5B2C6F;cursor:pointer;line-height:1;transition:.15s;
}
.auth-x:hover{background:#e6dcee}
.auth-title{font-size:1.15rem;font-weight:800;color:#3d2450;text-align:center}
.auth-sub{font-size:.8rem;color:#8a7f96;text-align:center;margin:6px 0 18px}
.auth-wx{
  width:100%;height:46px;border:none;border-radius:12px;background:#5B2C6F;color:#fff;
  font-size:.95rem;font-weight:700;cursor:pointer;display:flex;align-items:center;
  justify-content:center;gap:8px;transition:.15s;
}
.auth-wx:hover{background:#4a2359}
.auth-wx:disabled{opacity:.6;cursor:default}
.auth-wx svg{width:22px;height:22px;fill:#C9A963;flex:0 0 auto}
.auth-div{display:flex;align-items:center;gap:12px;margin:16px 0;color:#b3a8bf;font-size:.75rem}
.auth-div::before,.auth-div::after{content:'';flex:1;height:1px;background:#ece5f2}
.auth-code{display:flex;gap:8px}
.auth-code input{
  flex:1;min-width:0;height:44px;border:1.5px solid #ddd2e6;border-radius:12px;padding:0 14px;
  font-size:.95rem;color:#3d2450;outline:none;letter-spacing:.2em;background:#fdfcfe;transition:.15s;
}
.auth-code input:focus{border-color:#5B2C6F;box-shadow:0 0 0 3px rgba(91,44,111,.12)}
.auth-code input::placeholder{letter-spacing:.05em;color:#b8aec2}
.auth-code-btn{
  height:44px;padding:0 18px;border:none;border-radius:12px;background:#b3924e;color:#fff;
  font-size:.9rem;font-weight:700;cursor:pointer;transition:.15s;flex:0 0 auto;
}
.auth-code-btn:hover{background:#9c7e3e}
.auth-err{min-height:18px;font-size:.78rem;color:#c0392b;text-align:center;margin-top:10px}
@media(max-width:900px){.auth-mask{display:none !important}}
@media(prefers-reduced-motion:reduce){
  .auth-mask.show,.auth-card{animation:none}
}

/* ========== 23. 登录窗内嵌微信二维码 ========== */
.auth-main[hidden],.auth-qr[hidden]{display:none}
/* 只裁出微信页里的二维码本体（实测二维码在 340 宽视口的 (29,47) 处、282x282），
   页面自带的「微信登录」标题与「使用微信扫一扫登录…」文案都被移出可视区 */
.auth-qr-frame{
  width:286px;height:286px;margin:0 auto;overflow:hidden;border-radius:12px;
  border:1.5px solid #ece5f2;background:#fff;
}
.auth-qr-frame iframe{
  width:340px;height:400px;max-width:none;border:0;display:block;background:#fff;
  margin:-45px 0 0 -27px;
}
.auth-qr-tip{font-size:.8rem;color:#8a7f96;text-align:center;margin-top:12px}
.auth-back{
  display:block;margin:8px auto 0;border:none;background:transparent;color:#5B2C6F;
  font-size:.85rem;cursor:pointer;padding:4px 12px;border-radius:8px;transition:.15s;
}
.auth-back:hover{background:#f2edf7}

/* ========== 24. 挑战判定的「再点一次确认」警示态（2026-09-24 两段式防误触） ==========
   会改分的三个按钮（完成 / 成功 / 失败）第一下只是变成金色呼吸态，
   再点一下才真正结算。写在这里是为了排在 .btn.ok / .m-tbtn.ok 之后生效。 */
.btn.armed,.m-tbtn.armed{
  background-image:none;
  background:linear-gradient(160deg,#ffdf8f,#f0be4c 58%,#dfa32f);
  color:#5a3c02;border-color:rgba(255,255,255,.75);
  animation:armPulse 1.05s ease-in-out infinite;
}
@keyframes armPulse{
  0%,100%{
    transform:scale(1);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.82), inset 0 -2px 4px rgba(160,110,10,.30),
      0 0 0 0 rgba(201,169,99,.62);
  }
  50%{
    transform:scale(1.035);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.82), inset 0 -2px 4px rgba(160,110,10,.30),
      0 0 0 9px rgba(201,169,99,0);
  }
}

/* ========== 25. 随机抽选（全屏，2026-09-27；只读不写数据） ========== */
/* 顺带补上 raySpin：dx-rays 一直在引用它，但源码里从未定义过（掉落层旋转光束静默失效） */
@keyframes raySpin{from{transform:translate(-50%,-50%) rotate(0deg)}to{transform:translate(-50%,-50%) rotate(360deg)}}
.drawfx{position:fixed;inset:0;z-index:420;display:none;place-items:center;overflow:hidden;
  background:radial-gradient(circle at 50% 42%,rgba(59,32,102,.62),rgba(15,9,32,.92));
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.drawfx.show{display:grid;animation:fadeIn .22s ease both}
.drawfx-glow{position:absolute;left:50%;top:46%;width:130vmax;height:130vmax;transform:translate(-50%,-50%);
  background:conic-gradient(from 0deg,rgba(201,169,99,.14) 0 2deg,rgba(201,169,99,0) 2deg 14deg);
  animation:raySpin 24s linear infinite;pointer-events:none}
.drawfx-box{position:relative;width:min(880px,94vw);max-height:92vh;display:flex;flex-direction:column;
  align-items:center;gap:16px;padding:30px 26px 26px;border-radius:28px;
  background:linear-gradient(160deg,rgba(32,19,62,.78),rgba(20,12,42,.88));
  border:1px solid rgba(201,169,99,.34);box-shadow:0 24px 80px rgba(0,0,0,.5)}
.drawfx-title{font-size:15px;letter-spacing:.5em;text-indent:.5em;color:rgba(201,169,99,.92);font-weight:600}
.drawfx-stage{width:min(340px,74vw);min-height:112px;display:grid;place-items:center;border-radius:20px;
  border:1px solid rgba(201,169,99,.45);padding:10px 16px;
  background:linear-gradient(180deg,rgba(255,255,255,.07),rgba(0,0,0,.24));overflow:hidden}
.drawfx-name{font-size:clamp(30px,5vw,46px);font-weight:700;letter-spacing:.1em;text-indent:.1em;color:#f5ead2;
  text-shadow:0 2px 20px rgba(201,169,99,.4);white-space:nowrap}
.drawfx-name.rolling{animation:drawTick .14s linear infinite}
.drawfx-hint{font-size:13.5px;color:rgba(245,234,210,.62);letter-spacing:.3em;text-indent:.3em}
.drawfx-result{display:none;flex-wrap:wrap;gap:12px;justify-content:center;max-width:100%;overflow:auto;padding:4px 2px}
.drawfx.done .drawfx-result{display:flex}
.drawfx.done .drawfx-stage{display:none}
.drawfx.done .drawfx-hint{letter-spacing:.12em;text-indent:.12em;color:rgba(245,234,210,.85)}
.dp-chip{display:flex;flex-direction:column;align-items:center;gap:7px;min-width:132px;padding:15px 20px 13px;border-radius:16px;
  background:linear-gradient(170deg,rgba(255,255,255,.10),rgba(255,255,255,.04));
  border:1px solid rgba(201,169,99,.5);box-shadow:0 6px 22px rgba(0,0,0,.28);
  animation:drawIn .5s cubic-bezier(.2,.9,.3,1.12) both}
.dp-chip .nm{font-size:24px;font-weight:700;color:#f7eed8;letter-spacing:.08em;text-indent:.08em}
.dp-chip .gr{font-size:12.5px;letter-spacing:.14em;display:flex;align-items:center;gap:6px;color:rgba(245,234,210,.72)}
.dp-chip .dot{width:9px;height:9px;border-radius:50%;display:inline-block;box-shadow:0 0 8px rgba(255,255,255,.25)}
/* 单人抽取：放大居中，更有仪式感 */
.drawfx.done.one .drawfx-result{display:grid;place-items:center}
.drawfx.done.one .dp-chip{min-width:min(300px,70vw);padding:26px 34px 22px;border-radius:22px;border-width:1.5px}
.drawfx.done.one .dp-chip .nm{font-size:clamp(38px,7vw,58px)}
.drawfx.done.one .dp-chip .gr{font-size:14px}
.drawfx-done{display:none;margin-top:2px;min-width:132px}
.drawfx.done .drawfx-done{display:inline-block}
.drawfx-close{position:absolute;right:14px;top:12px;color:rgba(245,234,210,.75)}
@keyframes drawTick{0%{transform:translateY(0)}50%{transform:translateY(-3px)}100%{transform:translateY(0)}}
@keyframes drawIn{from{opacity:0;transform:translateY(16px) scale(.9)}to{opacity:1;transform:none}}
/* 人数选择小窗（顶栏入口） */
.dp-modal{width:min(380px,94vw)}
.dp-label{font-size:13px;letter-spacing:.2em;color:var(--ink-2,#5a6b82);margin-bottom:9px;font-weight:600}
.dp-quick{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:13px}
.dpq-btn{min-width:64px}
.dpq-btn.sel{border-color:var(--gold,#C9A963);color:#7a5a12;background:rgba(201,169,99,.14);font-weight:700}
.dp-custom{display:flex;gap:9px;align-items:stretch}
.dp-custom input{width:96px;text-align:center;font-size:17px;font-weight:700;padding:8px 10px;border-radius:12px;
  border:1px solid rgba(90,107,130,.4);background:rgba(255,255,255,.85);font-family:inherit;color:inherit}
.dp-custom input:focus{outline:none;border-color:var(--gold,#C9A963);box-shadow:0 0 0 3px rgba(201,169,99,.22)}
.dp-custom .btn{flex:1}
.dp-modal .hint{margin-top:11px}
html.is-mobile .drawfx{display:none!important}
/* ==================================================================
   家长留言弹幕 · 外观（教室白板 / 家长查看页共用）
   ------------------------------------------------------------------
   单独成文件是因为两个页面都要用：电脑端主界面（index.html，压在整个
   棋盘卡片上方 1/3）与家长查看页（/overview，压在棋盘画布上方 1/3）。
   由 build_multi.py 分别拼进 style.css 与 overview.css，避免同一样式
   在两处各维护一份。管理台里的「家长留言」列表样式不属于弹幕本体，
   仍在 02_board.css 里。
   家长页画布更小，字号/开关尺寸由 11_overview.css 覆写。
   ================================================================== */
/* --- 家长留言弹幕（棋盘上方 1/3 · 电脑端在线版） --- */
.dm-layer{
  position:absolute;left:0;right:0;top:0;height:33.333%;z-index:6;
  overflow:hidden;pointer-events:none;border-radius:20px 20px 0 0;
  background:linear-gradient(180deg,rgba(255,255,255,.42),rgba(255,255,255,.06) 72%,rgba(255,255,255,0));
  transition:opacity .32s ease, visibility .32s;
}
html.dm-off .dm-layer{opacity:0;visibility:hidden}
.dm-lanes{position:absolute;left:0;right:0;top:0;bottom:0}
.dm-lane{position:absolute;left:0;right:0}
.dm-item{
  position:absolute;left:100%;top:0;height:100%;
  display:flex;align-items:center;gap:9px;padding:0 16px 0 12px;
  border-radius:999px;white-space:nowrap;
  font-size:1.00rem;font-weight:800;color:#20384f;letter-spacing:.01em;
  background:linear-gradient(150deg,rgba(255,255,255,.97),rgba(245,250,255,.88));
  border:1.5px solid rgba(255,255,255,.94);
  box-shadow:0 12px 24px -16px rgba(40,80,125,.9), inset 0 1px 0 #fff;
  will-change:transform;
}
/* 动画由 JS 量好宽度、设好时长之后再挂 .run 启动。
   若把 animation-name 直接写在 .dm-item 上，元素一进 DOM 就会以默认
   0s 时长「跑完」一次，animationend 紧接着触发把元素删掉 —— 表现就是
   刚发出去的弹幕一闪就没。 */
.dm-item.run{animation-name:dmRun;animation-timing-function:linear;animation-fill-mode:forwards}
.dm-item.fresh{
  border-color:rgba(232,180,80,.95);
  background:linear-gradient(150deg,#fffaf0,#fff3d6);
  box-shadow:0 12px 26px -14px rgba(198,142,30,.9), inset 0 1px 0 #fff;
}
.dm-item .dm-tag{
  flex:0 0 auto;font-style:normal;padding:3px 10px;border-radius:999px;
  font-size:.82rem;font-weight:900;color:#fff;
  background:var(--gc,#7ab7ff);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.42);
  text-shadow:0 1px 1px rgba(20,50,90,.28);
}
.dm-item b{flex:0 0 auto;font-weight:900;color:#1b3348}
.dm-item b::after{content:"：";font-weight:900;opacity:.55}
.dm-item span{flex:0 0 auto;font-weight:800;color:#2c4a66}
@keyframes dmRun{from{transform:translateX(0)}to{transform:translateX(calc(-1 * var(--dm-dist,1200px)))}}

.dm-empty{
  position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);
  text-align:center;font-size:.94rem;font-weight:800;color:rgba(93,117,144,.72);
  letter-spacing:.04em;
}
.dm-empty[hidden]{display:none}

.dm-toggle{
  position:absolute;right:12px;bottom:12px;z-index:8;
  display:inline-flex;align-items:center;gap:6px;
  padding:8px 15px;border-radius:999px;
  font-size:.86rem;font-weight:800;color:#33485f;letter-spacing:.01em;
  background:linear-gradient(150deg,rgba(255,255,255,.95),rgba(240,247,255,.84));
  border:1px solid rgba(255,255,255,.92);
  box-shadow:0 12px 24px -14px rgba(40,80,125,.85), inset 0 1px 0 #fff;
  -webkit-backdrop-filter:blur(10px) saturate(170%);
  backdrop-filter:blur(10px) saturate(170%);
  transition:transform .16s ease, color .2s, background .2s, box-shadow .2s;
}
.dm-toggle:hover{transform:translateY(-2px)}
.dm-toggle:active{transform:translateY(0) scale(.97)}
.dm-toggle.is-off{color:#93a6ba;background:rgba(255,255,255,.66);box-shadow:0 8px 18px -14px rgba(40,80,125,.7)}
html.perf-lite .dm-toggle{-webkit-backdrop-filter:none;backdrop-filter:none;background:#fff}
html.perf-low .dm-item{box-shadow:none}
