:root {
  --bg: #F7F6F2;
  --surface: #FFFFFF;
  --text: #2C2C2A;
  --muted: #5F5E5A;
  --hint: #888780;
  --line: rgba(0, 0, 0, 0.12);
  --line-strong: rgba(0, 0, 0, 0.22);
  --pos: #3B6D11;
  --neg: #A32D2D;
  --accent: #378ADD;
  --accent-soft: #E6F1FB;
  --accent-text: #0C447C;
  --radius: 12px;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
}
body[data-theme="teal"] { --accent: #1D9E75; --accent-soft: #E1F5EE; --accent-text: #04342C; }
body[data-theme="purple"] { --accent: #534AB7; --accent-soft: #EEEDFE; --accent-text: #26215C; }
body[data-theme="coral"] { --accent: #D85A30; --accent-soft: #FAECE7; --accent-text: #4A1B0C; }
body[data-theme="gray"] { --accent: #5F5E5A; --accent-soft: #F1EFE8; --accent-text: #2C2C2A; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
}
a { color: var(--accent-text); text-decoration: none; }
h1, h2, h3 { font-weight: 500; margin: 0; }
h1 { font-size: 18px; }
h2 { font-size: 15px; }
h3 { font-size: 14px; }
p { margin: 0; }
.wrap { max-width: 1180px; margin: 0 auto; padding: 16px; }
.wrap-narrow { max-width: 460px; margin: 0 auto; padding: 16px; }
.screen-wrap { max-width: 1760px; margin: 0 auto; padding: 18px 22px; }

.hdr {
  background: var(--surface);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 18px;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
/* LOGO 槽：高度由右侧四行标题撑开，图片自身不参与撑高（绝对定位 + 等比缩放），
   因此行数少时 LOGO 不会反过来把页头撑高。 */
.hdr .logo-box { position: relative; align-self: stretch; flex: 0 0 110px; width: 110px; }
.hdr .logo {
  position: absolute; inset: 0; margin: auto;
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain;
}
/* 右侧胶囊区：默认上下紧凑排列、右对齐、整体垂直居中（管理页等通用）。
   投屏页在 .screen-wrap 作用域内另行改为横排（见 108 行注释起的那段）。 */
.hdr .hdr-side {
  align-self: stretch;
  display: flex; flex-direction: column;
  align-items: flex-end; justify-content: center;
  gap: 4px; flex: 0 0 auto;
}
/* ---- 战况焦点牌（2026-10-19 加，仅投屏页使用）----
   右上角是全屏最醒目的战略位。原先这里摆的是活动码——一串字符，天天挂在那儿
   却几乎不承载信息。现在换成一块「焦点牌」：随阶段切换，只显示这一刻全场
   最该看的那一个数（投票期看进度、揭牌后看亮牌、裁判后看一致率与领跑者）。
   活动码挪到页脚次要位置，需要时仍查得到。 */
.focus { text-align: right; line-height: 1.06; min-width: 0; }
.focus-num {
  font-size: clamp(20px, 1.7vw, 32px);
  font-weight: 700;
  color: var(--text);
  letter-spacing: .02em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.focus-lab { margin-top: 2px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.hdr-badges { display: flex; flex-direction: column; gap: 4px; align-items: flex-end; }
.hdr .head-text { flex: 1; min-width: 200px; text-align: center; }
.hdr .head-text .hl { line-height: 1.4; }
.hdr .head-text .hl1 { font-size: 17px; font-weight: 500; color: var(--text); }
.hdr .head-text .hl2 { font-size: 14.5px; color: var(--text); }
.hdr .head-text .hl3 { font-size: 13px; color: var(--muted); }
.hdr .head-text .hl4 { font-size: 12.5px; color: var(--muted); }
.hdr .head-text .hl5 { font-size: 12px; color: var(--muted); }
.hdr .head-text .hl6 { font-size: 11.5px; color: var(--muted); }
.hdr .head-text .hl7 { font-size: 11px; color: var(--muted); }
.hdr .head-text .hl8 { font-size: 10.5px; color: var(--muted); }

/* 行内底色块：贴合文字，长行换行时每段都带底色（box-decoration-break: clone）。
   .plain 表示不设底色，只保留文字本身。 */
.hdr .head-text .hl-bg {
  display: inline-block;
  padding: 2px 14px;
  border-radius: 10px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.hdr .head-text .hl-bg.plain { padding: 0; border-radius: 0; background: transparent; }

/* 投屏页：页头四行整体放大，供投影观看。
   页头三块 A-B-C（2026-10-19 侯博士定版）：
   A＝LOGO 专区，B＝标题区，C＝时钟＋焦点牌区；**A ＝ C 等宽**。
   A、C 各占 flex:1 平分两侧，B 只占内容自身宽度 ——
   于是 B 的中轴 ＝ 整条页头的中轴，标题文字「绝对居中」；
   A 内的 LOGO、C 内的图与文也各自居中。
   C 区纵向两段（2026-10-19 侯博士指令）：**上段课堂时钟、下段徽章＋焦点牌横排**
   —— 时钟自 A 区挪来（A 区整块让给合作 LOGO）；
   下段保持横排，若三行竖叠页头会被撑到 100px 上下，反而吃掉座席区的空间。 */
.screen-wrap .hdr { padding: 18px 22px; }
.screen-wrap .hdr .logo-box { flex: 1 1 0; width: auto; min-width: 160px; }
.screen-wrap .hdr .hdr-side {
  flex: 1 1 0;
  min-width: 160px;
  flex-direction: column;
  align-items: center; justify-content: center;
  gap: 8px;
}
.screen-wrap .hdr .hdr-side .side-row {
  display: flex; flex-direction: row;
  align-items: center; justify-content: center; gap: 12px;
}
.screen-wrap .hdr .head-text { flex: 0 1 auto; min-width: 0; text-align: center; }
.screen-wrap .hdr .focus { text-align: center; }
.screen-wrap .hdr .hdr-badges { align-items: center; }

/* ---- 课堂时间显示器（2026-10-19 侯博士指令，居 C 区上段）----
   原先与 LOGO 同挤 A 区，现挪到 C 区焦点牌上方：上行日期＋星期（小字），
   下行大号时钟（秒级跳动，投屏上看得见"活着"）；A 区整块让给合作 LOGO。 */
.hdr-clock { line-height: 1.18; min-width: 0; text-align: center; }

/* ---- 页头 A／C 区底色（甲方案，2026-10-19 侯博士）----
   留空（.zoned 不挂）＝不分色，整张卡保持白底一体；配上颜色后左右两块各自上色，
   中间标题区（B）始终留白。C 区文字用 --zone-fg（JS 按底色亮度写入，深底白字、
   浅底深字），因此深浅底都不会出现"深底深字"。圆角与色彩块四周留白让它像一枚
   独立的色块，而不是把白卡拦腰截断。 */
.screen-wrap .hdr .logo-box.zoned,
.screen-wrap .hdr .hdr-side.zoned {
  border-radius: var(--radius);
  padding: 6px 10px;
}
.screen-wrap .hdr .logo-box.zoned .logo {
  /* 色块内留白，LOGO 不顶着色块边缘（绝对定位的 inset:0 量的是 padding box，
     靠 max-width/max-height 收进来才真的留出边距） */
  max-width: calc(100% - 12px);
  max-height: calc(100% - 10px);
}
.screen-wrap .hdr .hdr-side.zoned .clock-date { color: var(--zone-fg, var(--muted)); }
.screen-wrap .hdr .hdr-side.zoned .clock-time { color: var(--zone-fg, var(--text)); }
.screen-wrap .hdr .hdr-side.zoned .focus-num { color: var(--zone-fg, var(--text)); }
.screen-wrap .hdr .hdr-side.zoned .focus-lab { color: var(--zone-fg, var(--muted)); }

/* 管理页：A／C 区底色的即时预览条（三块：A 色 / B 留白 / C 色） */
.zone-prev { display: flex; gap: 6px; margin-top: 10px; }
.zone-prev > span {
  flex: 1; min-width: 0; text-align: center; font-size: 12px; padding: 10px 6px;
  border-radius: 10px; border: 0.5px solid var(--line); color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: background .15s, color .15s;
}
.zone-prev .zp-b { background: var(--surface); }
/* --clk-k 是空间不够时的等比收缩系数（JS 写入，见 screen.js 的 fitClockBox）*/
.clock-date { font-size: calc(13px * var(--clk-k, 1)); color: var(--muted); white-space: nowrap; }
.clock-time {
  font-size: calc(clamp(24px, 2.4vw, 44px) * var(--clk-k, 1));
  font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; letter-spacing: .03em; white-space: nowrap;
}
.screen-wrap .hdr .head-text .hl { line-height: 1.32; }
.screen-wrap .hdr .head-text .hl1 { font-size: 30px; }
.screen-wrap .hdr .head-text .hl2 { font-size: 21px; }
.screen-wrap .hdr .head-text .hl3 { font-size: 16px; }
.screen-wrap .hdr .head-text .hl4 { font-size: 14px; }
/* 第 5–8 行：继续往下收一档。留空的行不渲染，所以只有真正填到 8 行时
   页头才会长到这个高度；填不满时页头按实际行数收缩。 */
.screen-wrap .hdr .head-text .hl5 { font-size: 12.5px; }
.screen-wrap .hdr .head-text .hl6 { font-size: 11.5px; }
.screen-wrap .hdr .head-text .hl7 { font-size: 10.5px; }
.screen-wrap .hdr .head-text .hl8 { font-size: 10px; }
.screen-wrap .hdr .head-text .hl-bg { padding: 4px 20px; border-radius: 14px; }
.screen-wrap .hdr .head-text .hl-bg.plain { padding: 0; border-radius: 0; }
/* 手机等窄屏按 1200 设计宽整体缩放（见 screen.html 的 viewport），布局不重排；
   标题无论字号百分比调到多少都绝不折行 —— 折行只会出现在大屏排版被破坏时。 */
.screen-wrap .hdr .head-text .hl { white-space: nowrap; }
.badge {
  display: inline-block;
  font-size: 12px;
  padding: 3px 10px;
  border-radius: 999px;
  background: #F1EFE8;
  color: var(--muted);
  white-space: nowrap;
}
.badge.accent { background: var(--accent-soft); color: var(--accent-text); }
.badge.warn { background: #FAEEDA; color: #854F0B; }
.badge.ok { background: #E1F5EE; color: #0F6E56; }
.badge.err { background: #FCEBEB; color: #A32D2D; }
/* 页脚信息栏（主办方 / 协办方 / 时间 / 地点）
   取网页备案信息的样子：居中、无边框、无底色、静默小字，不与内容区抢注意力。
   留空的信息位本来就不进数组，故整行可能为空 —— 那时脚本会把整块 display:none。 */
.info-bar {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 2px 0;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.6;
  letter-spacing: 0.2px;
  margin-bottom: 14px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  text-align: center;
  gap: 4px 16px;
}

.card {
  background: var(--surface);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 14px;
}
.card-title { font-size: 13px; color: var(--muted); margin-bottom: 10px; }
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.grid2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.grid3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
/* --- 首页门面：未登录访客从官网「DBT训战厅」进来看到的那一屏 ------------ */
/*  宽幅图 → 四行标题 → 模块（统一底色）。三者都可在总控台「首页展示设置」里改。 */
.home-banner { width: 100%; margin: 0 0 14px; border-radius: var(--radius); overflow: hidden; background: var(--accent-soft); }
.home-banner img { display: block; width: 100%; height: auto; }

.home-lines { margin: 0 0 14px; text-align: center; }
.home-lines .hl { white-space: nowrap; line-height: 1.4; }
.home-lines .hl:nth-child(1) { font-size: 30px; font-weight: 700; color: var(--text); }
.home-lines .hl:nth-child(2) { font-size: 21px; color: var(--text); }
.home-lines .hl:nth-child(3) { font-size: 17px; color: var(--muted); }
.home-lines .hl:nth-child(4) { font-size: 15px; color: var(--muted); }
.home-lines .hl:nth-child(5) { font-size: 13px; color: var(--muted); }
.home-lines .hl:nth-child(6) { font-size: 12px; color: var(--muted); }
.home-lines .hl:nth-child(7) { font-size: 11.5px; color: var(--muted); }

/* 手机上五行也要各自单行：字号随屏宽缩放（vw 比例按「最长行恰好一行」取定），
   超长不折行；行内 size% 由 applyHeadlines 在此基准上再乘，仍然自适应。 */
@media (max-width: 640px) {
  .home-lines .hl:nth-child(1) { font-size: min(21px, 4.1vw); }
  .home-lines .hl:nth-child(2) { font-size: min(16px, 3.4vw); }
  .home-lines .hl:nth-child(3) { font-size: min(14px, 3.1vw); }
  .home-lines .hl:nth-child(4) { font-size: min(13px, 2.9vw); }
  .home-lines .hl:nth-child(5) { font-size: min(12px, 2.7vw); }
  .home-lines .hl:nth-child(6) { font-size: min(11px, 2.5vw); }
  .home-lines .hl:nth-child(7) { font-size: min(10.5px, 2.4vw); }
  .home-lines .hl { white-space: nowrap; }
}

/* 每行文字包在 .hl-bg 里（与主屏同一套结构）：勾了底色就是一个色块，
   没勾就是 .plain —— 不加色块、不加内边距，直接露出页面。 */
.home-lines .hl-bg { display: inline-block; padding: 4px 18px; border-radius: 12px; }
.home-lines .hl-bg.plain { padding: 0; border-radius: 0; background: transparent; }

/* 横幅蒙板：盖在整块广告位上（含所有轮播图），颜色/不透明度由后台下发（JS 内联） */
.home-banner { position: relative; }
.home-banner .banner-mask { position: absolute; inset: 0; pointer-events: none; }
/* 3 图轮播：所有层叠在同一位置，靠 .on 淡入淡出切换 */
.home-banner .slides { position: relative; line-height: 0; }
.home-banner .slide { display: block; width: 100%; height: auto;
  position: absolute; inset: 0; opacity: 0; transition: opacity .6s ease; }
.home-banner .slide:first-child { position: static; }   /* 第 1 张撑出高度 */
.home-banner .slide.on { opacity: 1; }
.home-banner .slide:not([src]), .home-banner .slide[src=""] { display: none; }
/* 指示点：只有一张以上才出现（空 innerHTML 时不占位） */
.home-banner .slide-dots { position: absolute; left: 0; right: 0; bottom: 10px;
  display: flex; justify-content: center; gap: 7px; }
.home-banner .slide-dots .dot { width: 9px; height: 9px; border-radius: 50%;
  background: rgba(255, 255, 255, .55); box-shadow: 0 0 0 1px rgba(0, 0, 0, .18);
  cursor: pointer; }
.home-banner .slide-dots .dot.on { background: rgba(255, 255, 255, .95); }
/* 后台：三个槽位各占一行 */
.slot-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }

/* 页头移到页面最底：不再顶着页首，与上面内容拉开距离 */
.hdr-bottom { margin-top: 26px; margin-bottom: 0; }
/* 2026-10-18：品牌行与功能行合并为一行；手机上放不下 40 个字，允许自然折行并居中
   （「不断行」约束针对的是五行大标题——小字页头在手机上折行才是可读的排版） */
.hdr-bottom .t { font-size: 13px; color: var(--muted); }
.hdr-bottom .t .s { opacity: .72; }
@media (max-width: 640px) {
  .hdr-bottom .t { font-size: 11.5px; white-space: normal; text-align: center; }
  .hdr-bottom .head-text { flex: 1 1 100%; }  /* 文字独占整行，徽章另起一行，折行不再被挤 */
  /* 手机：去掉中间那个「·」，并从这里断成两行 —— PC/PAD 保持一行不变 */
  .hdr-bottom .t .dot { display: none; }
  .hdr-bottom .t .s { display: block; }
  /* 手机：「登录」按钮右侧那句双重确认提示，另起一行，不再挤在按钮旁边 */
  #loginHint { display: block; margin-left: 0 !important; margin-top: 8px; }
}

/* 页脚备案区：与官网页脚同一套观感 —— 居中、小字、链接默认不抢眼 */
.site-foot { margin: 16px 0 8px; text-align: center; font-size: 12.5px; line-height: 1.95; color: var(--muted); white-space: nowrap; }
.site-foot a { color: var(--muted); text-decoration: none; }
.site-foot a:hover { color: var(--accent); text-decoration: underline; }
.site-foot .sep { margin: 0 6px; opacity: .6; }
.site-foot .beian { display: inline-block; height: 1.15em; width: auto;
  vertical-align: -0.18em; margin-right: 4px; border: 0; }
@media (max-width: 640px) { .site-foot { font-size: min(11.5px, 2.4vw); } }

/* PAD / 矮屏（2026-10-18）：整页收进一屏，不出现滚动条。
   横幅是宽幅图，越宽的屏它越长高，横屏 PAD 会被它顶出屏幕 —— 所以按**视口高度**
   给它设上限（超了用 cover 裁上下，不变形）；行距同理按屏高收紧，屏越矮越紧。
   判据只取屏高，不看屏宽：竖屏 PAD（高 1024+）本就放得下，不触发，观感与原来一致。
   ★ 2026-10-19 侯博士反馈"总控台顶端图片被裁切，恢复原比例"：
   这组上限只该管**投票页**（整屏不滚动、必须收进一屏的那类页面），
   总控台本就是允许滚动的管理页 —— 原来没加作用域限定，把桌面总控台的横幅也压扁裁切了。
   现限定 body.vote-body：投票页保持收屏，其余页面（总控台等）恢复图片原比例完整显示。 */
@media (min-width: 641px) and (max-height: 900px) {
  body.vote-body .home-banner { margin-bottom: 10px; }
  body.vote-body .home-banner .slides { max-height: 22vh; overflow: hidden; }
  body.vote-body .home-banner .slide { max-height: 22vh; object-fit: cover; }
  .home-lines { margin-bottom: 10px; }
  .home-lines .hl { line-height: 1.18; }
  .hdr-bottom { margin-top: 12px; }
  .site-foot { margin: 10px 0 2px; line-height: 1.7; }
}
@media (min-width: 641px) and (max-height: 780px) {   /* 横屏 PAD：屏更矮，再紧一档 */
  body.vote-body .home-banner .slides { max-height: 18vh; }
  body.vote-body .home-banner .slide { max-height: 18vh; }
  .home-lines .hl { line-height: 1.1; }
}

.grid-side { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 14px; }
/* 投屏页：三栏。核心模块（各班组席位与实时投票结果）居中，辅助模块分配到两侧。
   两翼等宽，所以中间一栏始终位于画面正中，与两侧内容多少无关。 */
.screen-wrap .grid-side {
  /* 侯博士 2026-10-18：把两侧栏间空隙兼并给中央主数据屏 ——
     栏间 gap 14→10，两侧栏 1fr 收窄（下限仍保住积分榜/图例一行不折行），
     中栏 2.4fr→3.2fr，矩阵格子随之放大。 */
  grid-template-columns: minmax(0, 1fr) minmax(0, 3.2fr) minmax(0, 1fr);
  gap: 10px;
  align-items: stretch;
}
.side-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }

/* --- 投屏页：16:9 独屏，整屏不滚动，核心区自适应撑满 --------------------- */
/* 规划为整屏一屏放下：外部不出现页面滚动条，溢出只发生在各自面板内部。 */
body.screen-body { height: 100vh; overflow: hidden; }
.screen-wrap {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 14px 18px;
}
/* 侯博士 2026-10-19：整屏信息密度高，纵向的**结构性间隙**要极致压缩 ——
   LED 条与标题区、标题区与座席区，两处只留 4px 的视觉分隔位。
   注意区分「间隙」与「卡片内边距」：卡片内部留白不动，压的是模块与模块之间。 */
.screen-wrap .hdr { flex: 0 0 auto; margin-bottom: 4px; padding: 8px 18px; }
.screen-wrap .grid-side { flex: 1 1 auto; min-height: 0; }
/* 页脚备案：整屏不滚动，页脚只占固定高度、主区自己让位。
   ★ 2026-10-19 侯博士：**字符最小、高度最窄**，省下来的纵向空间全部让给上边的座席区。
   ⚠ 边界：备案号与公安备案图标是**法定要挂**的，只能压到「看得见的下限」，不能去掉；
     所以这里压的是字号、行高与外边距，不删内容、不用 overflow 裁字。 */
.screen-wrap .site-foot {
  flex: 0 0 auto;
  margin: 2px 0 0;
  padding: 0;
  font-size: 9px;
  line-height: 1.15;
  letter-spacing: 0;
}
/* 透明度挂在内层 .foot-dim 上，而不是整条页脚 —— 否则活动码作为页脚的一员
   会被一起压暗到 62%，需要查码时反而看不清。 */
.screen-wrap .site-foot .foot-dim { opacity: .58; }
.screen-wrap .site-foot .sep { margin: 0 4px; opacity: .5; }
/* 活动码徽章同步收到最小：它是页脚里最高的一件，不收它页脚就窄不下来 */
.screen-wrap .site-foot .foot-code {
  font-size: 9px; opacity: .88; letter-spacing: .01em;
  padding: 0 6px; line-height: 1.35;
}
/* 公安备案图标：与字号同步缩到 1 倍字高，仍保持可辨识、可点击（法规要求） */
.screen-wrap .site-foot .beian { height: 1.05em; vertical-align: -0.15em; margin-right: 3px; }
/* 同上：栏内卡片之间的间隙也收紧一档，把省下的高度全部让给中央座席区 */
.screen-wrap .side-col { min-height: 0; gap: 7px; }
.screen-wrap .side-col .card { margin-bottom: 0; }
/* .grow 的卡吃掉所在栏的剩余高度；.core-card 撑满中栏整列 */
.screen-wrap .card.grow { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.screen-wrap .card.core-card { height: 100%; display: flex; flex-direction: column; }
.screen-wrap .core-foot { flex: 0 0 auto; }
.screen-wrap .card-title.head-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}

/* 置顶 LED 标语屏（2026-10-18 建；2026-10-19 改为**三条独立标语、各自字号**）：
   黑底一条带，三条标语纵向轮播。
   ★★ `--led-h` 是**锁定的屏高**（2026-10-19 侯博士定）：
      - 与字号无关：调大字号**不再撑高**这条屏；
      - 与条数无关：三条都在这一屏里滚，不加高；
      - 与字号上限有关：它同时是字号的天花板 —— 定得太矮，字少的标语就没法再放大，
        故大屏取 60px（约可放大到基准 28px 的 1.8 倍，见下方 .screen-wrap 覆盖）。
   ★ 想改屏高只动这一处 `--led-h`：改后三条标语会自动按新高度重新收敛字号。
   滚动由 JS 用 transform 驱动（见 common.js 的 startLedRoll），
   这里的 transition 时长须与 JS 里的 LED_ROLL_MS 保持一致。 */
.led-bar {
  --led-fs: 20px;
  --led-h: 40px;          /* 管理页预览用的锁定高度 */
  display: block;
  height: var(--led-h);
  background: #161616;
  color: #fff;
  font-size: var(--led-fs);
  font-weight: 700;
  text-align: center;
  /* 相对单位：缩字号自适应时字距跟着缩，否则固定字距会让「缩到放得下」算不准 */
  letter-spacing: 0.04em;
  /* 直角：现场 LED 屏是方方正正的一条，不要胶囊圆角 */
  border-radius: 0;
  padding: 0 18px;
  margin: 0 0 4px;
  overflow: hidden;
  text-shadow: 0 0 10px rgba(255, 255, 255, .28);
}
.led-view { position: relative; height: 100%; overflow: hidden; }
.led-track { transition: transform 0.9s cubic-bezier(.4, 0, .2, 1); will-change: transform; }
.led-line {
  /* 每格＝锁定的屏高：文字在这一格里纵向居中，超出即被 clip，
     但 JS 会先把字号收到刚好放得下（见 common.js 的 fitLedLine），所以正常不会触发。 */
  height: var(--led-h);
  line-height: var(--led-h);
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* ★ 大屏锁高 60px（原 45px 偏矮：字少的标语想再放大就没有空间了，2026-10-19）。
     基准字号仍是 28px＝100%，向上约可放大到 180% 才触到屏高上限。 */
.screen-wrap .led-bar { flex: 0 0 auto; --led-fs: 28px; --led-h: 60px; }

/* 榜单条数分段按钮 */
.seg { display: inline-flex; border: 0.5px solid var(--line); border-radius: 8px; overflow: hidden; }
.seg-btn {
  font-family: inherit; font-size: 12px; padding: 3px 10px;
  background: transparent; border: 0; border-right: 0.5px solid var(--line);
  color: var(--muted); cursor: pointer;
}
.seg-btn:last-child { border-right: 0; }
.seg-btn.on { background: var(--accent-soft); color: var(--accent-text); font-weight: 500; }
.spacer { flex: 1; }

button, .btn {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 14px;
  border-radius: 9px;
  border: 0.5px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
button:hover { background: #FAFAF8; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover { filter: brightness(0.96); background: var(--accent); }
.btn-danger { background: #FCEBEB; border-color: #F09595; color: #A32D2D; }
.btn-ok { background: #E1F5EE; border-color: #5DCAA5; color: #0F6E56; }
.btn-warn { background: #FAEEDA; border-color: #EF9F27; color: #854F0B; }
.btn-sm { font-size: 12px; padding: 5px 10px; border-radius: 7px; }
.btn-lg { font-size: 16px; padding: 14px 20px; border-radius: 11px; }
.btn-block { width: 100%; display: block; }

input[type="text"], input[type="number"], input[type="password"], select, textarea {
  font-family: inherit;
  font-size: 13.5px;
  padding: 8px 10px;
  border: 0.5px solid var(--line-strong);
  border-radius: 9px;
  background: #fff;
  color: var(--text);
  width: 100%;
}
textarea { min-height: 90px; font-family: ui-monospace, Menlo, Consolas, monospace; letter-spacing: 1px; }
/* LED 标语是中文，等宽字体会把字形压扁 —— 标语框改回正文字体 */
/* 三条 LED 标语：一条一行，各有文本框 + 字号滑杆（2026-10-19 起） */
#f_led1, #f_led2, #f_led3 {
  font-family: inherit;            /* 中文标语不用等宽字体，免得字形被压扁 */
  letter-spacing: .04em;
  flex: 1; min-width: 120px;
}
.led-rows { display: grid; gap: 8px; margin-bottom: 10px; }
.led-row { display: flex; align-items: center; gap: 8px; }
.led-idx { font-size: 13px; color: var(--muted); flex: 0 0 auto; width: 14px; }
.led-row input[type="range"] { flex: 0 0 110px; width: 110px; }
.led-pct { flex: 0 0 40px; width: 40px; text-align: right; }
label.f { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.field { margin-bottom: 10px; }

.opts { display: grid; gap: 10px; }
.opt {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 16px 18px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: #fff;
  font-size: 16px;
  cursor: pointer;
  text-align: left;
}
.opt .k {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: #F1EFE8;
  color: #444441;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 500;
  flex: none;
}
.opt.sel { border: 2px solid var(--accent); background: var(--accent-soft); }
.opt.sel .k { background: var(--accent); color: #fff; }
.opt.locked { opacity: 0.5; cursor: not-allowed; }

.big-round { text-align: center; padding: 10px 0 16px; }
.big-round .n { font-size: 40px; font-weight: 500; line-height: 1.1; }
.big-round .l { font-size: 13px; color: var(--muted); }

.matrix { display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 5px; }
.cell {
  position: relative;
  border-radius: 6px;
  min-height: 46px;
  padding: 3px 2px;
  text-align: center;
  border: 0.5px solid var(--line);
  background: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.2;
  transition: background 0.35s ease, color 0.35s ease;
}
.cell .g { font-size: 10.5px; color: var(--muted); }
.cell .o { font-size: 15px; font-weight: 500; }
.cell .d { font-size: 10.5px; }
/* 投屏页：格子尺寸由 JS 按可用空间算出（--cell），字号随之缩放，
   组号与答案编号在任何行列配置下都保持可读。 */
.screen-wrap .matrix { flex: 1 1 auto; min-height: 0; }
.screen-wrap .cell { min-height: 0; padding: 1px; overflow: hidden; }
/* 侯博士 2026-10-19：格子里的组号 / 字母 / 成本要"坐得满"一点。
   字母 o 是主角，系数 0.32→0.40（约 +25%）；组号与成本是配角，0.15→0.17，
   三行合计仍控制在格子高度的 0.85 以内，任何行列配置下都不会撑破格子。 */
.screen-wrap .cell .g { font-size: clamp(10px, calc(var(--cell, 46px) * 0.16), 18px); line-height: 1.12; white-space: nowrap; }
.screen-wrap .cell .o { font-size: clamp(14px, calc(var(--cell, 46px) * 0.40), 52px); line-height: 1.0; }
/* 格内的**正负成本**是全场最该一眼看清的数字，故放到与字母同一量级，并加粗、
   收紧字距与行高。字号由 JS 按实际格宽/格高算出（--d-fs，见 screen.js 的
   sizeCostFont），这里的 clamp 只是 JS 未跑时的兜底值。 */
.screen-wrap .cell .d {
  font-size: var(--d-fs, clamp(11px, calc(var(--cell, 46px) * 0.32), 52px));
  line-height: 1.1; font-weight: 600; letter-spacing: -.01em; white-space: nowrap;
}
/* 裁判阶段（规则第 4 步）：绿＝正确通过，红＝错误。一秒配对，投影上一眼可辨。 */
.screen-wrap .cell.judged.right { background: #EAF3DE; border: 1px solid #639922; }
.screen-wrap .cell.judged.right .o { color: #3B6D11; }
.screen-wrap .cell.judged.wrong { background: #FCEBEB; border: 1px solid #E24B4A; }
.screen-wrap .cell.judged.wrong .o { color: #A32D2D; }
/* 指导决策方案条已并入中央卡标题行（.core-head），故改为行内块、不再独占一行 */
#correctLine {
  flex: 0 0 auto;
  display: inline-block;
  margin: 0;
  padding: 2px 10px;
  border-radius: 6px;
  background: #1f1f1d;
  color: #fff;
  font-size: 14px;
  letter-spacing: .04em;
  white-space: nowrap;
}
#correctLine b { font-size: 18px; margin-left: 6px; color: #9FE1CB; }
/* 中央卡：标题行只占一行的高度，余量全部留给座席矩阵 */
.screen-wrap .card-title.head-row.core-head { margin-bottom: 5px; gap: 12px; }
.cell.masked { background: #B4B2A9; border-color: #B4B2A9; }
.cell.empty { background: #FCFCFA; border: 1px dashed #D3D1C7; color: #B4B2A9; }
.cell.empty .g { color: #B4B2A9; }
.cell.revealed.A { background: #EEEDFE; } .cell.revealed.A .o { color: #534AB7; }
.cell.revealed.B { background: #E1F5EE; } .cell.revealed.B .o { color: #1D9E75; }
.cell.revealed.C { background: #E6F1FB; } .cell.revealed.C .o { color: #378ADD; }
.cell.revealed.D { background: #FAEEDA; } .cell.revealed.D .o { color: #EF9F27; }
.cell.revealed.E { background: #FAECE7; } .cell.revealed.E .o { color: #D85A30; }
.cell.hot { outline: 2px solid var(--accent); outline-offset: -2px; }
.pos { color: var(--pos); }
.neg { color: var(--neg); }
/* ★ 成本数字专用配色（侯博士 2026-10-19 定案）：分数＝成本，越低越优秀
   .pos/.neg 的语义是「好 / 坏」（绿 / 红），与「负号 / 正号」方向相反，
   两者绝不能混用——历史上正是因为拿 .pos 去标正数，才把颜色画反。
   成本数字一律走下面三个类。 */
.c-up   { color: var(--neg); }   /* + 成本上升 → 差 → 红 */
.c-down { color: var(--pos); }   /* − 成本下降 → 好 → 绿 */
.c-flat { color: var(--hint); }  /* 0 / 空 → 中性 */
/* 未完成警示条（裁判台手机端 + 管理页共用，2026-10-19）
   只在本轮卡在第 5 步之前时出现：不兑现奖惩加权，本轮不进累计榜、也开不了下一轮。 */
.todo-bar {
  margin-top: 8px; padding: 9px 11px; border-radius: 8px;
  background: #FAEEDA; color: #854F0B; border: 1px solid #EF9F27;
  font-size: 12.5px; font-weight: 500; line-height: 1.45;
}
/* 「又快又好」决策激励加分：恒为负（成本下降），用 --pos 的绿，别按"负数"上色 */
.spd { color: var(--pos); font-weight: 700; white-space: nowrap; }
.spd-none { color: var(--hint); }
/* 裁判台入口卡（管理页，2026-10-19）：左边一张二维码，右边三步怎么走。
   这张码扫了进**裁判台**，学员的组码扫了进**投票页** —— 两张码分列，别混。 */
.jentry { display: flex; gap: 12px; align-items: flex-start; margin-top: 4px; }
.jentry-qr {
  flex: 0 0 auto; line-height: 0; background: #fff;
  padding: 6px; border: 1px solid var(--line); border-radius: 8px;
}
.jentry-steps { margin: 0; padding-left: 18px; font-size: 12.5px; line-height: 1.65; }

.rank-box { position: relative; height: 250px; overflow: hidden; }
.rank-row {
  position: absolute;
  left: 0;
  right: 0;
  height: var(--rank-row-h, 24px);   /* 投屏页由 JS 按条数算出行高 */
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px;
  border-radius: 6px;
  font-size: 12.5px;
  transition: top 0.8s cubic-bezier(0.34, 1.3, 0.5, 1), opacity 0.5s, background 0.5s;
}
.rank-row .no { display: inline-block; width: 18px; color: var(--hint); }
.rank-row.top1, .rank-row.top2, .rank-row.top3 { background: #FAEEDA; }
.rank-row.top1 .no, .rank-row.top2 .no, .rank-row.top3 .no { color: #854F0B; }
/* 绝对定位的行撑不开父容器，用一根占位杆把滚动高度撑出来 */
.rank-spacer { width: 100%; height: 0; }
/* 投屏页：榜单吃掉左栏剩余高度；行高由 JS 调，装不下才在面板内滚动 */
/* 累计得分图表：每行背后一条按分数归一化的底条，榜单即图 */
.rank-row > span { position: relative; z-index: 1; }   /* 文字压在底条之上 */
.rank-row .rkbar {
  position: absolute; left: 0; top: 0; bottom: 0;
  border-radius: 3px; opacity: .28; z-index: 0;
}
.rank-row .rkbar.pos { background: #639922; }
.rank-row .rkbar.neg { background: #E24B4A; }
/* 成本口径（越低越优）：底条只表示成本高低，用中性色，避免与判罚的红绿混淆 */
.rank-row .rkbar.cost { background: #888780; }
/* 本轮判罚条（右栏） */
.judge-bars { display: flex; flex-direction: column; gap: 8px; }
.jb-row { display: flex; align-items: center; gap: 8px; }
.jb-tag { font-size: 12px; flex: 0 0 auto; width: 56px; }
.jb-tag.right { color: #3B6D11; }
.jb-tag.wrong { color: #A32D2D; }
.jb-bar {
  flex: 1 1 auto; height: 14px; border-radius: 3px;
  background: #F1EFE8; overflow: hidden;
}
.jb-bar i { display: block; height: 100%; }
#jbRightBar { background: #639922; }
#jbWrongBar { background: #E24B4A; }
.jb-row b { font-size: 15px; font-weight: 500; width: 34px; text-align: right; }
.screen-wrap .rank-box { flex: 1 1 auto; min-height: 0; height: auto; overflow: auto; }
.screen-wrap .rank-row { font-size: 14px; }

/* 选项占比：环形图 + 自绘明细（含百分比与正确答案标记） */
.donut-wrap { position: relative; flex: 1 1 auto; min-height: 150px; }
.donut-center {
  position: absolute; inset: 0; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.donut-center b { font-size: 26px; font-weight: 500; line-height: 1.05; }
.donut-center span { font-size: 11.5px; color: var(--hint); }
.pie-detail { margin-top: 10px; flex: 0 0 auto; }
.pie-row {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 7px; border-radius: 7px; font-size: 13px;
}
.pie-row + .pie-row { margin-top: 2px; }
.pie-row .sw2 { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.pie-row .k { width: 16px; font-weight: 500; }
.pie-row .n { flex: 1; color: var(--muted); }
.pie-row .p { font-weight: 500; }
.pie-row.correct { background: var(--accent-soft); }
.pie-row.correct .k, .pie-row.correct .p { color: var(--accent-text); }

.legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--muted); }
.legend .sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; margin-right: 5px; }

/* 待交席位：组号做成色块，大屏一眼可数；超出高度时内部滚动，不撑高页面 */
.miss-list { display: flex; flex-wrap: wrap; gap: 6px; max-height: 300px; overflow: auto; }
.miss-list .miss-chip {
  background: #FAEEDA; color: #854F0B;
  border-radius: 7px; padding: 2px 9px; font-size: 13px; font-weight: 500;
}
.miss-list .miss-none { color: #0F6E56; font-size: 13px; }

table.tbl { width: 100%; border-collapse: collapse; font-size: 12.5px; }
table.tbl th, table.tbl td { text-align: left; padding: 5px 8px; border-bottom: 0.5px solid #F1EFE8; }
table.tbl th { color: var(--muted); font-weight: 400; font-size: 12px; }
table.tbl tr:hover td { background: #FCFCFA; }
.scroll { max-height: 420px; overflow: auto; }

.tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.tab {
  padding: 7px 13px;
  border-radius: 999px;
  border: 0.5px solid var(--line-strong);
  background: #fff;
  font-size: 12.5px;
  cursor: pointer;
}
.tab.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.pane { display: none; }
.pane.on { display: block; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12.5px;
  background: #F1EFE8;
  color: var(--hint);
  cursor: pointer;
  border: 0.5px solid transparent;
}
.chip.done { background: #E1F5EE; color: #0F6E56; }
.chip.now { background: var(--accent-soft); color: var(--accent-text); outline: 1.5px solid var(--accent); }
.chip.on { background: var(--accent); color: #fff; }

.progress { height: 8px; border-radius: 4px; background: #F1EFE8; overflow: hidden; }
.progress > i { display: block; height: 8px; background: var(--accent); }
.progress.ok > i { background: #1D9E75; }

.chart-box { position: relative; width: 100%; height: 160px; }

.seat { display: flex; gap: 10px; align-items: center; font-size: 13px; }
.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  background: #2C2C2A;
  color: #fff;
  padding: 9px 16px;
  border-radius: 9px;
  font-size: 13px;
  z-index: 99;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s;
  max-width: 88vw;
}
.toast.on { opacity: 1; }
.notice { border-radius: 10px; padding: 10px 14px; font-size: 13px; margin-bottom: 12px; }
.notice.info { background: var(--accent-soft); color: var(--accent-text); }
.notice.warn { background: #FAEEDA; color: #633806; }
.notice.err { background: #FCEBEB; color: #791F1F; }
.notice.ok { background: #E1F5EE; color: #085041; }
.qr-cell { text-align: center; background: #fff; border: 0.5px solid var(--line); border-radius: 8px; padding: 8px; }
.qr-cell canvas { width: 100%; height: auto; image-rendering: pixelated; }
.qr-cell .qn { font-size: 12px; font-weight: 500; margin-top: 4px; }
.qr-cell .qc { font-size: 10.5px; color: var(--hint); word-break: break-all; }
.qr-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 10px; }
.qr-grid.big { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.mono { font-family: ui-monospace, Menlo, Consolas, monospace; }
.muted { color: var(--muted); }
.hint { color: var(--hint); font-size: 12px; }
.center { text-align: center; }
.mb0 { margin-bottom: 0; }
.mt12 { margin-top: 12px; }
.nowrap { white-space: nowrap; }

/* --- 页头标题行编辑器（管理页） ------------------------------------------ */
.head-rows { display: flex; flex-direction: column; gap: 8px; }
.head-rows .hrow {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--surface2, #FAFAF8); border: 0.5px solid var(--line);
  border-radius: 10px; padding: 7px 10px;
}
.head-rows .hrow.dragging { opacity: 0.4; }
.head-rows .hrow.over { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.head-rows .hrow .drag {
  cursor: grab; color: var(--hint); font-size: 15px; line-height: 1;
  user-select: none; padding: 0 2px;
}
.head-rows .hrow .drag:active { cursor: grabbing; }
.head-rows .hrow .ord { width: 14px; text-align: center; color: var(--hint); font-size: 12px; }
.head-rows .hrow input[type="text"] { flex: 1 1 200px; min-width: 150px; }
.head-rows .hrow label.hint { display: inline-flex; align-items: center; gap: 4px; }
.head-rows .hrow input[type="range"] { width: 104px; }
.head-rows .hrow input[type="color"] {
  width: 30px; height: 26px; padding: 0; background: none; cursor: pointer;
  border: 0.5px solid var(--line); border-radius: 7px;
}
/* Not ticked = "auto" (follow the theme / auto-contrast). Keep the swatch
   showing the colour that will actually be used -- fading it out made a dark
   auto colour look pale and read as the wrong value. A dashed border carries
   the "you don't set this" meaning instead. */
.head-rows .hrow input[type="color"]:disabled {
  opacity: 1; cursor: not-allowed;
  border-style: dashed; border-color: var(--line-strong);
}
.head-rows .hrow .pct { width: 40px; text-align: right; color: var(--hint); font-size: 12px; }
/* 标题行的「加粗」按钮：选中时实心，一眼看出哪几行是粗的 */
.head-rows .hrow .hb { font-weight: 700; }
.head-rows .hrow .hb.on { background: #2C2C2A; color: #fff; border-color: #2C2C2A; }
.head-rows .hrow .tools { display: flex; gap: 4px; }
.head-rows .hrow .tools button { padding: 4px 8px; font-size: 12px; }
@media (max-width: 900px) {
  .grid-side, .grid2, .grid3 { grid-template-columns: minmax(0, 1fr); }
  .qr-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .matrix { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .qr-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ===== 手机投票页：整屏不滚动 + 四行标题不断行 + 五角形选项 =====
   只作用于 body.vote-body，不碰投屏页 / 管理页 / 总控台。 */
body.vote-body {
  height: 100vh;
  height: 100dvh;          /* 手机浏览器地址栏收起时的真实可视高度 */
  overflow: hidden;
}
body.vote-body .wrap-narrow {
  height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* 页头四行：绝对不折行，超宽由 fitHeadlines() 压字号 */
body.vote-body .hdr .head-text .hl {
  white-space: nowrap !important;
  overflow: hidden;
  text-overflow: clip;
}
/* 手机页头改成两行：第一行 LOGO + 活动码，第二行起四行标题独占整宽。
   原来三者并排，标题只剩 200px 宽，长标题会被压到 10px；
   换成整宽（约 350px）后字号能保住可读性。 */
body.vote-body .hdr {
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
}
/* 一屏硬约束的让位清单：主办/场地信息手机上看不着（投屏页有），
   卡片和外框留白收紧 —— 小屏（667 高）就靠这几项合计 ~80px 保住一屏。 */
body.vote-body .info-bar { display: none; }
body.vote-body .wrap-narrow { padding: 10px 12px; }

/* 平台门面（广告位 + 多行标题）落在投票页顶部：内容与编号跟首页完全一致，
   但投票页是「整屏不滚动」的硬约束（body overflow:hidden，超了是被裁掉而不是
   滚动），所以横幅按屏高限高、行距收紧，给下面的五角形留出地盘。 */
/* 投票页顶部只放三联广告图（不再叠平台多行标题 —— 下面就是本场活动标题行），
   所以图的高度可以按正常比例给。投票页是「整屏不滚动」，横幅仍按屏高设上限，
   并给五角形设上限 —— 不设的话它会把图挤出去，页面底部被裁。 */
body.vote-body .home-facade { flex: 0 0 auto; }
body.vote-body .home-banner { margin-bottom: 8px; }
body.vote-body .home-banner .slides { max-height: 16vh; overflow: hidden; }
body.vote-body .home-banner .slide { max-height: 16vh; object-fit: cover; }
/* 五角形卡片限高：sizePentagon() 读的就是它的可用高，限住了它才会让出地盘 */
body.vote-body .opts-penta-wrap { max-height: 34vh; }
/* 2026-10-18 侯博士：五角形可适当缩小，但高度要**锁死**——
   flex 改成 0 0 auto + 固定 height，无论上下出现什么（提示条等）都不再被挤压；
   sizePentagon() 量的仍是这个固定 clientHeight，行为不变。 */
body.vote-body .opts-penta-wrap { flex: 0 0 auto; height: 30vh; padding: 0; margin-top: 24px; }
/* A 圆顶部与组号行之间留一口气的间距。注意必须用 **margin** 而不是 padding：
   sizePentagon() 量的是 clientHeight（含 padding 不含 margin），用 padding 会
   重新造成「JS 量到的」和「CSS 画出来的」不是同一个数，圆又会变形。 */
/* 高度锁死后，内层正方形改由**高度**推出宽度（aspect-ratio 1:1）——
   原来的 width:100% + max-height 会把正方形压成扁的（圆变椭圆）。
   wrap 的上下 padding 同时归零：clientHeight 与子元素 height:100% 才是同一个数，
   JS 量到的和 CSS 画出来的才不会差出一条缝。 */
body.vote-body .opts-penta { width: auto; height: 100%; max-width: 100%; }
/* 小屏（iPhone SE 一类，667 高）：图再让一档，先保住投票本身 */
@media (max-height: 700px) {
  body.vote-body .home-banner { margin-bottom: 6px; }
  body.vote-body .home-banner .slides,
  body.vote-body .home-banner .slide { max-height: 13vh; }
  body.vote-body .opts-penta-wrap { height: 26vh; }
}
/* 极矮屏（iPhone 5/SE 一代，568 高）：图压到最小 */
@media (max-height: 600px) {
  body.vote-body .home-banner .slides,
  body.vote-body .home-banner .slide { max-height: 10vh; }
  body.vote-body .opts-penta-wrap { height: 20vh; }
}
body.vote-body #main > .card { margin-bottom: 0; padding: 10px 12px; }
body.vote-body .hdr .logo-box {
  order: 1; flex: 0 0 64px; width: 64px; height: 40px; align-self: center;
}
body.vote-body .hdr .hdr-side {
  order: 2; flex: 1 1 auto; flex-direction: row;
  align-items: center; justify-content: flex-end; gap: 4px;
}
body.vote-body .hdr .head-text {
  order: 3; flex: 1 0 100%; min-width: 0; margin-top: 4px;
}

/* 主区吃掉剩余高度，卡片内部再纵向分配，谁都不许把页面撑出屏 */
body.vote-body #main { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
body.vote-body #main > .card {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
}
body.vote-body #main > .card > .row,
body.vote-body #submitBtn,
body.vote-body #main .hint.center { flex: 0 0 auto; }
body.vote-body #main .hint.center { margin-top: 6px !important; font-size: 12px; }

/* 活动码 + 第 N 组：同一行、活动码在前；轮次提示另起一行排在组号下面。
   两行文字居中对齐（不再左对齐）：组号居中于这两行的列宽，轮次行同样居中，
   上下各留 6px 呼吸位 —— 整体居中于卡片首行。 */
/* 主卡首行（2026-10-19 侯博士指令：原先窄屏上折成 2×2 两行，现一行放下）：
   活动码 · 组号 · 轮次说明 · 状态徽章。**绝不能折行** —— 投票页是整屏不滚动，
   折一行就把提交按钮往下挤一格。放不下时不折行，靠 --idk 等比缩小字号
   （fitIdline 写入，套路同投屏页时钟的 --clk-k；下限 0.62，再窄也只能如此）。 */
body.vote-body .vote-idline {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 8px;
  padding: 6px 0; min-width: 0; overflow: hidden;
}
body.vote-body .vote-idline .badge {
  flex: 0 0 auto; white-space: nowrap;
  font-size: calc(12px * var(--idk, 1));
  padding: calc(3px * var(--idk, 1)) calc(10px * var(--idk, 1));
}
/* 中段（组号 + 轮次说明）不参与收缩：盒子缩了文字只会溢出，
   scrollWidth 就量不到真实需求宽度，fitIdline 会失效 —— 让它保持自然宽。 */
body.vote-body .vote-idline .idline-mid {
  flex: 0 0 auto; display: flex; align-items: baseline;
  gap: 6px; white-space: nowrap; min-width: 0;
}
body.vote-body .vote-idline #groupLabel {
  font-size: calc(15px * var(--idk, 1)); font-weight: 500; white-space: nowrap;
}
body.vote-body .vote-idline .hint {
  font-size: calc(12px * var(--idk, 1)); white-space: nowrap;
}
body.vote-body .vote-idline #stateBadge { margin-left: auto; }

/* 置顶本组实时累计成本（2026-10-20 侯博士 A 方案）：只看本组、不含别组。
   数值颜色走成本铁律（负=绿/正=红），与投屏/战绩口径一致。 */
body.vote-body .vote-score {
  display: flex; align-items: baseline; justify-content: center; gap: 8px;
  margin-top: 8px; padding: 5px 10px; border-radius: 8px;
  background: var(--surface2, #FAFAF8);
}
body.vote-body .vote-score .lab { font-size: 12px; color: var(--muted); }
body.vote-body .vote-score .val {
  font-size: 20px; font-weight: 700; letter-spacing: .02em;
  font-variant-numeric: tabular-nums;
}

/* 提示（绑定冲突等）：字号压小一号，位置在主卡片之后（vote.html）——
   它是「顺便看到的」信息，绝不能把投票区往上挤 */
body.vote-body .notice { font-size: 11px; padding: 8px 12px; margin: 8px 0 0; }

/* 五角形：容器始终正方形，尽量占满剩余空间，五个圆按顶点绝对定位 */
.opts-penta-wrap {
  flex: 1 1 auto; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 6px 0;
  /* 关键：不许被内部正方形撑大。高度必须是 flex 分到的剩余高度，
     这样 sizePentagon() 量到的 clientHeight 才是真实可用高度。 */
  overflow: hidden;
}
/* 宽度先定死（= 容器宽），高度由 1:1 推出；真超了再由 max-height 反向收窄。
   注意别写 height:100% —— flex 里百分比高度链会断，容器会塌成 0。 */
.opts-penta {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  max-height: 100%;
  margin: 0 auto;
}
.penta-opt {
  position: absolute;
  transform: translate(-50%, -50%);
  /* 直径 23%：A 的圆心在容器 12% 处，半径 11.5% —— 圆顶落在 0.5%，
     正好留一点余量。原来的 26%（半径 13%）圆顶会越出容器上沿 1%，
     被 .opts-penta-wrap 的 overflow:hidden 削掉一条（看着像压扁了）。 */
  width: 23%;
  height: 23%;
  aspect-ratio: 1 / 1;
  /* 这些是 <button>，会继承全局 button 的 padding:8px 14px —— 小屏上圆被 padding
     撑成椭圆（实测 320 宽屏：37x26）。归零后才是真圆。 */
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: #fff;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .12);
  -webkit-tap-highlight-color: transparent;
  transition: transform .12s ease, box-shadow .12s ease;
}
/* 选中：白圈 + 深色外环，和彩色底都能拉开对比 */
.penta-opt.sel {
  transform: translate(-50%, -50%) scale(1.1);
  box-shadow: 0 0 0 4px #fff, 0 0 0 7px rgba(0, 0, 0, .38), 0 4px 12px rgba(0, 0, 0, .18);
}
.penta-opt.locked { opacity: .4; cursor: not-allowed; }

/* 中心组名：尖朝上的浅色五边形（参考五角星信息图），组名居中显示。
   五个圆围绕它排布 —— A 在正上方，正好给顶点让位。 */
.penta-center {
  position: absolute;
  left: 50%; top: 52%;
  transform: translate(-50%, -50%);
  width: 62%;
  aspect-ratio: 1 / 1;
  clip-path: polygon(50% 0%, 100% 38%, 81% 100%, 19% 100%, 0% 38%);
  background: rgba(83, 74, 183, .10);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  pointer-events: none;      /* 别挡住底下的圆和点击 */
}
.penta-center span {
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  line-height: 1.15;
  text-align: center;
}
/* 中心实时得分：颜色走成本铁律（负=绿/正=红），组名下方小一号 */
.penta-center .penta-score {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---- 状态灯标记（2026-10-18 加）----
   空心 = 灯灭（还没答对过）；实心绿 = 灯亮（已答对过，跨轮持久、永不熄灭）。
   灯是历史累积状态，不泄露本轮答案，故投票期的灰块上同样显示。 */
.cell .lamp {
  position: absolute; top: 4px; right: 4px;
  width: 11px; height: 11px; border-radius: 50%;
  border: 2px solid #9aa0a6; background: transparent;
  z-index: 2; box-sizing: border-box;
}
.cell .lamp.on { background: #639922; border-color: #3B6D11; }

/* 6 列（轮 / 我的决策 / 指导决策 / 结果 / 本轮成本 / 累计成本）在 375 宽的手机上
   要塞得下，故表头与单元格都收紧一档：表头 10.5px、正文 12.5px、左右内边距 1px。 */
.ms-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.ms-table th {
  position: sticky; top: 0; background: var(--surface);
  font-weight: 500; color: var(--muted); font-size: 10.5px; text-align: center;
  padding: 6px 1px; border-bottom: 0.5px solid var(--line); white-space: nowrap;
}
.ms-table td {
  text-align: center; padding: 7px 1px; border-bottom: 0.5px solid var(--line);
  white-space: nowrap;
}
.ms-table tbody tr:last-child td { border-bottom: none; }
.ms-opt {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 5px; color: #fff; font-weight: 500;
  font-size: 12px;
}
.ms-opt.none { background: #F1EFE8; color: #999; }

/* 图例里的灯点：与格子灯同形，但随文字排版（不绝对定位） */
.legend .lamp, .hv-legend .lamp {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  border: 1.5px solid #9aa0a6; background: transparent;
  margin-right: 5px; vertical-align: -1px; box-sizing: border-box;
}
.legend .lamp.on, .hv-legend .lamp.on { background: #639922; border-color: #3B6D11; }

/* ---- 历史总览：组 × 轮 决策轨迹（整屏覆盖，不占三栏）---- */
.history-view {
  position: fixed; left: 0; top: 0; right: 0; bottom: 0;
  background: #fff; z-index: 50;
  display: flex; flex-direction: column; padding: 16px 22px; box-sizing: border-box;
}
.hv-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 10px; flex: 0 0 auto; }
.hv-title { font-size: 22px; font-weight: 500; }
.hv-sub { font-size: 13px; color: var(--muted); }
.hv-legend { margin-left: auto; display: flex; gap: 14px; font-size: 12.5px; color: var(--muted); align-items: center; }
.hv-dot { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 4px; vertical-align: -1px; }
.hv-dot.right { background: #EAF3DE; border: 1px solid #639922; }
.hv-dot.wrong { background: #FCEBEB; border: 1px solid #E24B4A; }
.hv-dot.none { background: #F1EFE8; border: 1px solid #B4B2A9; }
.hv-legend .lamp { position: static; display: inline-block; vertical-align: -1px; margin-right: 4px; }
.hv-scroll { flex: 1 1 auto; overflow: auto; min-height: 0; }
.hv-table { border-collapse: collapse; font-size: 13px; }
.hv-table th, .hv-table td { border: 1px solid #e6e6e6; padding: 4px 9px; text-align: center; white-space: nowrap; }
.hv-table th { background: #f7f7f7; position: sticky; top: 0; z-index: 1; font-weight: 500; }
.hv-g, .hv-cum { background: #fafafa; font-weight: 500; }
.hv-cell { min-width: 46px; font-weight: 500; position: relative; }
.hv-cell.right { background: #EAF3DE; color: #27500A; }
.hv-cell.wrong { background: #FCEBEB; color: #A32D2D; }
.hv-cell.none { background: #F1EFE8; color: #999; }
.hv-cell .lamp { top: 2px; right: 2px; width: 6px; height: 6px; border-width: 1px; }

/* 右下角悬浮切换按钮：投屏机上点一下即可在「实时 / 历史总览」之间切换 */
.view-toggle {
  position: fixed; right: 14px; bottom: 14px; z-index: 60;
  padding: 8px 15px; font-size: 14px; border-radius: 8px; cursor: pointer;
  border: 1px solid rgba(0,0,0,.18); background: rgba(255,255,255,.94); color: #333;
}
.view-toggle:hover { background: #fff; }

/* ---- 手机端「本组战绩」底部抽屉（2026-10-18 定稿）-----------------------
   投票页是「整屏不滚动」硬约束（body.vote-body height:100dvh + overflow:hidden），
   超出一屏一律**被裁掉而非滚动** —— 所以战绩绝不能放在主卡片**之后**（那样永远
   看不见）。改为：主卡片底部一行内嵌入口（不额外占高度）+ 点击升起底部抽屉。
   数据只来自 state 接口的 my_scores：服务端按组号取，只有本组自己的行。 */
.vote-footrow {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 6px;
}
.vote-footrow .hint { flex: 1 1 auto; font-size: 11px; line-height: 1.4; text-align: left; }
.ms-enter {
  flex: 0 0 auto; font-family: var(--sans); font-size: 12px; cursor: pointer;
  color: var(--accent-text); background: var(--accent-soft);
  border: 0.5px solid var(--line); border-radius: 999px;
  padding: 3px 10px; white-space: nowrap; -webkit-tap-highlight-color: transparent;
}
.ms-enter:active { opacity: 0.7; }

/* 抽屉：fixed 满屏遮罩 + 底部升起的面板，不受 body overflow:hidden 影响 */
.ms-sheet {
  position: fixed; inset: 0; z-index: 80; display: flex; align-items: flex-end;
  background: rgba(0, 0, 0, 0.38); -webkit-tap-highlight-color: transparent;
}
.ms-sheet-box {
  width: 100%; max-height: 76vh; background: var(--surface);
  border-radius: 16px 16px 0 0; display: flex; flex-direction: column;
  box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.18); overflow: hidden;
}
.ms-sheet-head {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 14px 16px 10px; border-bottom: 0.5px solid var(--line);
}
.ms-title { font-size: 15px; font-weight: 500; }
.ms-close {
  flex: 0 0 auto; font-family: var(--sans); font-size: 13px; cursor: pointer;
  color: var(--muted); background: #F1EFE8; border: 0.5px solid var(--line);
  border-radius: 999px; padding: 4px 14px; -webkit-tap-highlight-color: transparent;
}
.ms-sheet-body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 0 16px 16px; }
.ms-opt.none { background: #F1EFE8; color: #999; }
.ms-ok { color: var(--pos); font-weight: 500; }
.ms-no { color: var(--neg); font-weight: 500; }
.ms-cum { font-weight: 600; }
/* 「又快又好」激励加分小字：绿色（成本下降＝好），跟惩罚的红色区分开 */
.ms-spd { color: var(--pos); font-size: 10px; font-weight: 600; line-height: 1.2; white-space: nowrap; }
.ms-lamp {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  border: 1px solid var(--line-strong); margin-left: 4px; vertical-align: middle;
}
.ms-lamp.on { background: #639922; border-color: #3B6D11; }
.ms-legend { margin-top: 10px; font-size: 11.5px; color: var(--hint); line-height: 1.55; }
.ms-legend .ms-lamp { margin: 0 2px; }
.ms-empty { color: var(--hint); font-size: 13px; text-align: center; padding: 22px 0; }
