/* ═══════════════════════════════════════════════════════════════════
   ui.css —— 共用构件。六个界面共用这一份，页面自己那一份 <style> 只放
   这一页独有的版面，不重新定义构件。取值一律从 tokens.css 取。

   这一份里有：页头 / 面板 / 卡片 / 可折叠卡片 / 折叠说明 / 可排序表格 /
   统计块 / 按钮 / 标签 / 提示条 / 一行操作条 / 人名点选片 / 分布条 /
   悬浮详情卡 / 配置表 / 一组卡的小标题 / 结论块。分组页用到前十件，后两件先摆在这里给别的页用。
   ═══════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }

[hidden] { display: none !important; }   /* 带内联 display 的块也要能藏起来 */

html {
  font-synthesis: none;              /* 不许合成假粗体 */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-ui);
  font-size: var(--fs3);
  line-height: var(--lh);
  font-variant-numeric: tabular-nums;
}

b, strong, th, h1, h2, h3, h4 { font-weight: var(--w-strong); }
h1, h2, h3, h4 { margin: 0; font-size: var(--fs4); }

a { color: var(--acc); text-decoration: none; }
a:hover { color: var(--acc-2); text-decoration: underline; }

:focus-visible { outline: 2px solid var(--acc); outline-offset: 1px; }

code, .mono { font-family: var(--font-mono); font-size: var(--fs2); }

.num  { text-align: right; font-variant-numeric: tabular-nums; }
.muted { color: var(--fg3); }
.small { font-size: var(--fs1); }
.sep  { color: var(--fg3); padding: 0 var(--sp1); }
.sp   { flex: 1 1 auto; }

/* 只在本机、只在线上的元素（上线与往返设计 3.138）。线上的引导在根元素上标 data-online；
   藏起来只是不显示，不是权限。 */
html[data-online] [data-local-only], html:not([data-online]) [data-online-only] { display: none !important; }

/* ── 页头 ──────────────────────────────────────────────────────
   标题、副标题、路径，右侧一处放动作。不进固定区：它放的是低频按钮，
   随页面滚走换一屏多几行表格。 */
.hd {
  display: flex; align-items: baseline; gap: var(--sp2);
  padding: var(--sp2) var(--sp4);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.hd__back { font-size: var(--fs2); color: var(--fg2); }
.hd__back:hover { color: var(--acc); }
.hd__title { font-size: var(--fs4); }
.hd__sub { font-size: var(--fs2); color: var(--fg3); }
.hd__actions { margin-left: auto; display: flex; gap: var(--sp1); align-items: center; }

/* ── 面板：插在页头下面，出现之后一直占地方（个性化、说明、审核） ── */
.panel {
  background: var(--surface2);
  border-bottom: 1px solid var(--line);
  padding: var(--sp3) var(--sp4);
}
.panel[hidden] { display: none; }
.panel__grid {
  display: grid; gap: var(--sp3) var(--sp4);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  max-width: 1100px;
}
.panel__row { display: flex; flex-direction: column; gap: var(--sp1); }
.panel__row > span { font-size: var(--fs1); color: var(--fg3); }

/* ── 卡片 ─────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: var(--card-line) solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp3);
}
.card__hd { display: flex; align-items: baseline; gap: var(--sp2); margin-bottom: var(--sp2); }
.card__hd h2, .card__hd h3 { font-size: var(--fs4); }
.card__sub { font-size: var(--fs2); color: var(--fg3); }

/* ── 可折叠卡片：折起时标题右边必须显示规模 ────────────────────
   钩子：<section class="card fold" data-fold-key="…" data-open="true|false">
   行为：点 .fold__hd 整行开合，写 data-open，并把 key 存进个性化。 */
/* 不能给它 overflow：里面那张表比卡宽时，clip 会把多出的列剪掉，
   而且不计进右栏的横向滚动，那几列就永远读不到。圆角改由标题行自己担。 */
.fold { padding: 0; }
.fold__hd {
  border-radius: var(--r-md) var(--r-md) 0 0;
  display: flex; align-items: baseline; gap: var(--sp2); width: 100%;
  padding: var(--sp2) var(--sp3);
  background: none; border: 0; color: inherit; font: inherit; text-align: left;
  cursor: pointer;
}
.fold__hd:hover { background: var(--hover); }
.fold__tri { color: var(--fg3); font-size: var(--fs1); width: 1em; }
.fold[data-open="true"] .fold__tri { transform: rotate(90deg); }
.fold__hd h2 { font-size: var(--fs4); }
.fold__scale { margin-left: auto; font-size: var(--fs1); color: var(--fg3); }
.fold__body { padding: 0 var(--sp3) var(--sp3); }
.fold[data-open="false"] .fold__body { display: none; }
.fold[data-open="false"] .fold__hd { border-radius: var(--r-md); }

/* ── 折叠说明：读一次就够的东西不常驻。没有底、没有边框、没有规模 ──
   用原生 <details>，无脚本也能开合；data-fold-key 供个性化记状态。 */
.note { font-size: var(--fs1); color: var(--fg3); }
.note > summary {
  cursor: pointer; list-style: none; width: max-content; max-width: 100%;
  color: var(--fg3); padding: var(--sp1) 0;
}
.note > summary::marker, .note > summary::-webkit-details-marker { display: none; }
.note > summary::before { content: "▸ "; }
.note[open] > summary::before { content: "▾ "; }
.note > summary:hover { color: var(--acc); }
.note__body { padding: 0 0 var(--sp2) 1.1em; max-width: 78ch; }
.note__body b { color: var(--fg2); }
/* 一处有几段折叠说明时并排成一行；打开的那一段自己占满一行（`设计.md` 3.18） */
.notes { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 var(--sp4); }
.notes > .note[open] { flex-basis: 100%; }

/* 一句就说完、不值得折的解释：灰字一行，跟在它解释的东西下面 */
.hint { font-size: var(--fs1); color: var(--fg3); max-width: 82ch; }
.hint b { color: var(--fg2); }

/* ── 可排序表格 ────────────────────────────────────────────────
   钩子：th[data-sort="键"][aria-sort="none|ascending|descending"]。
   表格所在层级不设 overflow：表头靠 sticky 吸附，套壳会吸错。
   窄屏由页面自己横向滚。 */
.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs2); }
.tbl th, .tbl td {
  padding: var(--cell-y) var(--cell-x);
  text-align: left; vertical-align: baseline;
  border-bottom: 1px solid var(--line);
}
.tbl thead th {
  position: sticky; top: var(--stick-h, 0px); z-index: 2;
  background: var(--surface2); color: var(--fg);
  font-size: var(--fs1); font-weight: var(--w-strong);
  white-space: nowrap; cursor: pointer; user-select: none;
}
.tbl thead th[data-sort]:hover { color: var(--acc); }
.tbl thead th[aria-sort="ascending"]::after  { content: " ▲"; color: var(--acc); font-size: .8em; }
.tbl thead th[aria-sort="descending"]::after { content: " ▼"; color: var(--acc); font-size: .8em; }
.tbl tbody tr:hover { background: var(--hover); }
.tbl tbody tr[aria-selected="true"] { background: var(--sel); }
.tbl td.num, .tbl th.num { text-align: right; }
/* 宽松版式：偶数行铺底，不画横线 */
.tbl tbody tr:nth-child(even) { background: color-mix(in srgb, var(--zebra) calc(var(--zebra-on) * 100%), transparent); }
:root[data-density="loose"] .tbl th, :root[data-density="loose"] .tbl td { border-bottom: 0; }
:root[data-density="loose"] .tbl thead th { border-bottom: 1px solid var(--line); }

/* ── 统计块：整块占两行，不占三行。数值不缩 ────────────────── */
.stats { display: flex; align-items: stretch; gap: var(--sp4); padding: var(--sp2) 0; flex-wrap: wrap; }
.stats__grp { display: flex; align-items: stretch; gap: var(--sp4); }
.stats__grp + .stats__grp { border-left: 1px solid var(--line); padding-left: var(--sp4); }
.stat__k { font-size: var(--fs1); color: var(--fg3); white-space: nowrap; }
.stat__v { font-size: var(--fs5); font-weight: var(--w-strong); line-height: 1.15; white-space: nowrap; }
.stat__v small { font-size: var(--fs1); color: var(--fg3); font-weight: 400; margin-left: var(--sp1); }
.stat__v .up { font-size: var(--fs1); color: var(--ok); }
.stat__v .down { font-size: var(--fs1); color: var(--bad); }

/* ── 按钮：主 / 普通 / 透明 / 危险。悬停一律把边框变强调色 ────── */
.btn {
  font: inherit; font-size: var(--fs3); font-weight: var(--w-strong);
  color: var(--fg); background: var(--surface2);
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--sp1) var(--sp2); cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: baseline; gap: var(--sp1);
}
.btn:hover { border-color: var(--acc); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn:disabled:hover { border-color: var(--line); }
.btn--primary { background: var(--acc); color: var(--on-acc); border-color: var(--acc); }
.btn--primary:hover { background: var(--acc-2); border-color: var(--acc-2); }
.btn--ghost { background: transparent; }
/* 危险：字色与边框一直是出错色，不等悬停才变——要和旁边的「改」「确认」一眼分得开 */
.btn--danger { color: var(--bad); border-color: var(--bad-line); }
.btn--danger:not(.btn--ghost) { background: var(--bad-soft); }
.btn--danger:hover { border-color: var(--bad); color: var(--bad); }
.btn--danger:disabled:hover { border-color: var(--bad-line); }
.btn--sm { font-size: var(--fs1); padding: 2px var(--sp1); }

/* ── 选文件：**一个 label 套着真正的 input，不是另一种按钮** ────
   2026-08-29 加。原来是裸的 `<input type="file">`，浏览器画的那个
   「Choose File / 未选择文件」与旁边的 `.btn` 既不同高、也不同字号、
   也不认强调色，`--acc` 换一个色相它纹丝不动。

   **为什么用 label 而不是「按钮 + `input.click()`」**：label 关联的文件框
   点标签就会打开，这是原生行为，不需要脚本；用按钮转发点击的话，
   **脚本没加载上时那个按钮就是死的，而且不报错**。
   `.filebtn` 只负责把真正的 input 藏在自己身上——**它不重画按钮**，
   外观全部来自与它并列的 `.btn`（第十节：取值只有一份实现）。

   **input 不用 `display:none`**：那样键盘走不到它，focus 环也没有了。
   改成铺满 label、透明、由 label 承担 `:focus-within` 的焦点样式。 */
.filebtn { position: relative; overflow: hidden; }
.filebtn input[type="file"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
.filebtn:focus-within { border-color: var(--acc); outline: 2px solid var(--acc); outline-offset: 1px; }

/* ── 标签 ─────────────────────────────────────────────────────── */
.tag {
  display: inline-flex; align-items: center; gap: 2px;
  font-size: var(--fs1); line-height: 1.35;
  padding: 0 var(--sp1); border-radius: var(--r-sm);
  border: 1px solid var(--line); color: var(--fg3); white-space: nowrap;
}
.tag--ok   { color: var(--ok);   border-color: var(--ok-line);   background: var(--ok-soft); }
.tag--warn { color: var(--warn); border-color: var(--warn-line); background: var(--warn-soft); }
.tag--bad  { color: var(--bad);  border-color: var(--bad-line);  background: var(--bad-soft); }
.tag--acc  { color: var(--acc);  border-color: var(--acc-line);  background: var(--acc-soft); }

/* ── 提示条：一处至多一条，只显示最挡路的那一条；没有卡点不显示 ── */
.tip {
  font-size: var(--fs2);
  background: var(--warn-soft); border: 1px solid var(--warn-line);
  color: var(--warn); border-radius: var(--r-sm);
  padding: var(--sp1) var(--sp2);
  text-wrap: pretty;
}
/* 行内的 <b> 不能变成 flex 项，否则一句话被拆成几行；要在右边挂动作的
   那一条另开一个类。 */
.tip--row { display: flex; align-items: baseline; gap: var(--sp2); flex-wrap: wrap; }
.tip--row > span { flex: 1 1 20ch; }
.tip a, .tip .btn { color: inherit; }
.tip--bad { background: var(--bad-soft); border-color: var(--bad-line); color: var(--bad); }
.tip--ok { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }

/* ── 一行操作条：同一行里再分组用竖线，不换行 ────────────────── */
.bar { display: flex; align-items: center; gap: var(--sp2); flex-wrap: wrap; }
/* 列多的表在窄屏上自己横向滚，不把整页撑宽 */
.tblbox { overflow-x: auto; }
.bar .vsep { width: 1px; align-self: stretch; background: var(--line); margin: 0 var(--sp1); }
.bar label { font-size: var(--fs2); color: var(--fg2); display: inline-flex; gap: var(--sp1); align-items: center; }
select, input[type="text"], input[type="number"], input[type="search"] {
  font: inherit; font-size: var(--fs2); color: var(--fg);
  background: var(--surface2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 2px var(--sp1);
}
input[type="range"] { accent-color: var(--acc); }

/* ── 人名点选片：从一个集合里点选若干人 ────────────────────────
   钩子：<button class="pchip" data-person="pid" aria-pressed="false"> */
.pchip {
  font: inherit; font-size: var(--fs2); text-align: left;
  /* 底取次级底而不是卡片底：它多半摆在卡片上，与卡片同底时只剩一条边框，
     深色里几乎看不出来。 */
  background: var(--surface2); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 1px var(--sp1); cursor: pointer;
  display: inline-flex; align-items: baseline; gap: var(--sp1);
}
.pchip:hover { border-color: var(--acc); }
.pchip[aria-pressed="true"] { background: var(--sel); border-color: var(--acc); }
.pchip__n { font-size: var(--fs1); color: var(--fg3); font-variant-numeric: tabular-nums; }
.pchip[aria-pressed="true"] .pchip__n { color: var(--fg2); }
.pchip__core { color: var(--acc); font-size: var(--fs1); }

/* 名牌上的区间条（`65` 4.1.2、4.1.3）。群局系页的名牌与分组页的选人卡片共用这一份。
   刻度**全体共用**，由页面算好传进来；各画各的刻度长度与位置就不可比了。
   钩子：<span class="barcell"><span class="barcell__t"><i style="left;width"></i><b style="left"></b></span></span> */
.barcell { display: inline-flex; align-items: center; gap: var(--sp1); align-self: center; }
.barcell__t { width: 4.5em; height: .5em; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); position: relative; }
.barcell__t > i { position: absolute; top: 0; bottom: 0; background: var(--acc-line); border-radius: var(--r-sm); }
.barcell__t > b { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--acc); }

/* ── 分布条 ───────────────────────────────────────────────────── */
.dist { display: grid; grid-template-columns: max-content 1fr max-content; gap: var(--sp1) var(--sp2); align-items: center; font-size: var(--fs2); }
.dist__bar { height: .7em; background: var(--surface2); border-radius: var(--r-sm); }
.dist__bar > i { display: block; height: 100%; background: var(--acc-line); border-radius: var(--r-sm); }

/* ── 悬浮详情卡：某一个对象的全部字段。位置固定，横向居中贴上方 ── */
.sheet { position: fixed; inset: 0; z-index: 50; background: var(--scrim); overflow: auto; }
.sheet[hidden] { display: none; }
.sheet__box {
  width: min(880px, calc(100% - var(--sp4) * 2));
  margin: var(--sp5) auto;
  background: var(--surface); border-radius: var(--r-md); box-shadow: var(--shadow);
}
.sheet__hd {
  position: sticky; top: 0; background: var(--surface);
  display: flex; align-items: baseline; gap: var(--sp2);
  padding: var(--sp3); border-bottom: 1px solid var(--line);
  border-radius: var(--r-md) var(--r-md) 0 0;
}
.sheet__body { padding: var(--sp3); }

/* ═══ 应用外壳与三态侧栏 ═══════════════════════════════════════
   五个页面共用。页面本身不滚：body 满屏高，页头与面板占固定高，
   .shell 吃掉剩下的，**滚的只有右栏 .main**；侧栏因此天然满高、不随
   内容走。表头照旧 sticky，只是它认的滚动容器变成了右栏。

   侧栏三态写在 <body data-rail>：
     hover  贴边一条图标条，鼠标碰到／键盘聚焦时浮出一张带圆角与投影的卡
     pin    占住一列，右栏自适应变窄，侧栏自己滚
     hide   整个收掉，右栏吃满整屏；页头左上角那个图标按钮把它叫回来
   ═══════════════════════════════════════════════════════════ */
:root { --rail-open: 17em; --rail-mini: 2.9em; }

/* 整页是一个应用外壳：**页面本身不滚**，满屏高，滚的只有右栏。
   侧栏因此满高、不随内容走——原来那版用 sticky 跟着页面滚，页头滚走之后
   侧栏顶上会空出一条，那是把「应用外壳」当成「长页面」做的结果。
   表头照旧 sticky，只是它认的滚动容器变成了右栏，吸在右栏顶端。 */
html, body { height: 100%; }
body { display: flex; flex-direction: column; overflow: hidden; }
.hd, .panel { flex: 0 0 auto; }
/* `--rail-w` 只在 `body[data-rail=…]` 之下才有定义（见下面那三条）。**兜底取 0px，
   也就是「当作没有侧栏」**——那是安全的那一侧：属性缺席时右栏吃满整屏，页面照样能用；
   不给兜底的话整条 `grid-template-columns` 无效，版面直接散掉。

   ⚠ **兜底不是「可以不写那个属性」。** 五页的 `data-rail` 现在由 `app.js` 的 `applyRail`
   在 `DOMContentLoaded` 之后设，所以从解析完到 `bootUI` 跑完这一小段时间里它是缺席的；
   **而 `app.js` 万一没加载上，它会一直缺席。** 2026-08-28 加这个兜底，图的正是那一种情形
   不再把版面弄散。`index.html` 永远没有侧栏，它的 `hide` 直接写在标记里。
   逐条见 `90-技术栈与界面/00-代码索引.md` 第四节。 */
.shell { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: var(--rail-w, 0px) minmax(0, 1fr); }
.railwrap { grid-column: 1; position: relative; height: 100%; min-height: 0; display: flex; }
.main { grid-column: 2; height: 100%; overflow: auto; }

/* 页头左上角那个侧栏开关：与图标条同一套图标按钮 */
.hd__icon {
  width: 2em; height: 2em; display: grid; place-items: center; flex: 0 0 auto;
  border: 0; background: none; color: var(--fg3); border-radius: var(--r-sm); cursor: pointer;
  align-self: center;
}
.hd__icon:hover { background: var(--surface2); color: var(--acc); }
.hd__icon svg { width: 1.15em; height: 1.15em; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
body[data-rail="pin"] .hd__icon { color: var(--acc); }

/* ── 收起时那条图标条 ───────────────────────────────────────── */
.railmini {
  display: none; flex-direction: column; align-items: center; gap: var(--sp1);
  width: var(--rail-mini); height: 100%; padding: var(--sp2) 0;
  background: var(--surface); border-right: 1px solid var(--line);
}
.railmini__b {
  width: 2em; height: 2em; display: grid; place-items: center;
  border: 0; background: none; color: var(--fg3); border-radius: var(--r-sm); cursor: pointer;
}
.railmini__b:hover { background: var(--surface2); color: var(--acc); }
.railmini svg { width: 1.15em; height: 1.15em; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.railmini__gap { flex: 1 1 auto; }

/* ── 侧栏本体 ───────────────────────────────────────────────── */
.rail {
  width: var(--rail-open); min-height: 0; background: var(--surface);
  display: flex; flex-direction: column; gap: var(--sp2);
  padding: var(--sp2) var(--sp3) var(--sp4);
}

/* 一 · 图标条贴边，鼠标碰到（或键盘聚焦）浮出一张卡，移开收回 */
body[data-rail="hover"] { --rail-w: var(--rail-mini); }
body[data-rail="hover"] .railmini { display: flex; }
body[data-rail="hover"] .rail {
  position: absolute; z-index: 6; left: calc(var(--rail-mini) + var(--sp2));
  top: var(--sp2); bottom: var(--sp2); overflow: auto;
  border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow);
  opacity: 0; transform: translateX(-6px); pointer-events: none;
  transition: opacity .12s ease-out, transform .12s ease-out;
}
body[data-rail="hover"] .railwrap:hover .rail,
body[data-rail="hover"] .railwrap:focus-within .rail,
body[data-rail="hover"] .railwrap[data-peek="true"] .rail { opacity: 1; transform: none; pointer-events: auto; }

/* 二 · 钉住一列，右栏自适应变窄，侧栏自己滚 */
body[data-rail="pin"] { --rail-w: var(--rail-open); }
body[data-rail="pin"] .rail { height: 100%; overflow: auto; border-right: 1px solid var(--line); }

/* 三 · 收掉，右栏吃满整屏 */
body[data-rail="hide"] { --rail-w: 0px; }
body[data-rail="hide"] .railwrap { display: none; }


.rail__sec { display: flex; align-items: baseline; gap: var(--sp2); font-size: var(--fs1); color: var(--fg3); }
.rail__sec > b { color: var(--fg2); font-size: var(--fs2); }
.rail__sec .sp { flex: 1 1 auto; }

/* 侧栏里的键值小表（配置摘要、规模） */
.cfg { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp1) var(--sp2); align-items: baseline; font-size: var(--fs2); margin: 0; }
.cfg dt { color: var(--fg3); font-size: var(--fs1); }
.cfg dd { margin: 0; }
.cfg dd .muted { font-size: var(--fs1); }

/* 右栏 */
.main { padding: var(--sp3) var(--sp4) var(--sp5); display: flex; flex-direction: column; gap: var(--sp3); min-width: 0; }
.mainwrap { width: 100%; max-width: 1280px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--sp3); }

/* ── 右栏顶上一条**不滚的带子**（筛选条、多选操作条）────────────
   用法：.main.main--band > .mainbar + .mainscroll > .mainwrap

   **带子故意不在滚动区里面。** 放进去再给它 sticky 的话，表头就要知道
   带子有多高（那得靠脚本量，而高度随字号倍率与筛选条内容变），量不到就被盖住。
   拆开之后表头的 sticky 只跟 .mainscroll 打交道，--stick-h 保持 0。 */
.main--band { padding: 0; gap: 0; overflow: hidden; }
.mainbar {
  flex: 0 0 auto; background: var(--surface); border-bottom: 1px solid var(--line);
  padding: var(--sp2) var(--sp4);
  display: flex; flex-direction: column; gap: var(--sp1);
}
.mainscroll { flex: 1 1 auto; min-height: 0; overflow: auto; padding: var(--sp3) var(--sp4) var(--sp5); }
.filters { display: flex; flex-direction: column; gap: var(--sp1); }
.filters .bar label { white-space: nowrap; }
/* 多选操作条。**没选中时也占着位置**（里面放一句说明）——否则第一下
   点击会让整片列表往下跳一行，第二下就落到别的行上，浏览器根本不会
   认为发生了双击。一节根本没有多选时整条收掉（`app.js` 的 syncMainbar）。 */
.opbar { min-height: 1.9em; display: flex; align-items: center; gap: var(--sp2); flex-wrap: wrap; font-size: var(--fs2); }

/* ── 侧栏里的章节导航（一次只显示一节的页面用它换节）─────── */
.secnav { display: flex; flex-direction: column; }
.secnav button {
  font: inherit; font-size: var(--fs2); color: var(--fg2); text-align: left; cursor: pointer;
  background: none; border: 0; padding: var(--sp1) var(--sp2); border-radius: var(--r-sm);
  display: flex; align-items: baseline; gap: var(--sp2);
}
.secnav button:hover { background: var(--hover); color: var(--acc); }
.secnav button[aria-current="true"] { background: var(--sel); color: var(--fg); }
.secnav button .muted { margin-left: auto; font-size: var(--fs1); }

/* ── 一节：配合 .secnav，同时只显示一节，其余 hidden ─────────────
   2026-09-28 从基底、跨系局、群局系三页的 <style> 收进来，三份逐字相同。 */
.sec { display: flex; flex-direction: column; gap: var(--sp2); }
.sec[hidden] { display: none; }
.sec__hd { display: flex; align-items: baseline; gap: var(--sp2); flex-wrap: wrap; }
.sec__hd h2 { font-size: calc(var(--fs-base) * 1.3); }
.sec__hd .muted { font-size: var(--fs1); }
.sec__hd .bar { margin-left: auto; }

/* ── 键值小表：一个对象的几项字段，左键右值 ──────────────────
   与 .cfg 形状相近但不是同一件：列距大一档、不按基线对齐。两者都在用，不并。 */
.kv { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp1) var(--sp3); font-size: var(--fs2); margin: 0; }
.kv dt { color: var(--fg3); font-size: var(--fs1); }
.kv dd { margin: 0; }

/* ── 配置表：一项一行，左边名字，右边控件，控件下面跟一句话 ────────
   2026-10-13 加，先给群局系页实力一节的「这一次照什么算」用。原来是一条会折行的操作条，
   五六个控件加一段说明挤在一起，哪句说明是说哪个控件的要靠猜。改成表以后，每句说明就在它说的控件下面。
   说明不另占一列：试过三列，控件多的那一行（十几张地图）会把中间一列撑到很宽，别的行的说明就离控件很远。
   `.form__sep` 是一条横线，把「挑哪批局」和「人给的量」分成两段。 */
.form { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--sp2) var(--sp4); align-items: baseline; }
.form__k { font-size: var(--fs2); color: var(--fg2); white-space: nowrap; }
.form__v { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.form__c { display: flex; flex-wrap: wrap; gap: var(--sp1) var(--sp3); align-items: center; }
.form__c label { font-size: var(--fs2); color: var(--fg2); display: inline-flex; gap: var(--sp1); align-items: center; white-space: nowrap; }
.form__h { font-size: var(--fs1); color: var(--fg3); max-width: 82ch; }
.form__h b { color: var(--fg2); }
.form__sep { grid-column: 1 / -1; border-top: 1px solid var(--line); }
.form [aria-invalid="true"] { border-color: var(--bad); }
.form__k[data-bad="true"] { color: var(--bad); }

/* ── 一摞卡：由脚本整块写进去的几张卡，之间留一档间距（`.sec` 只管它自己的直接子元素）── */
.stack { display: flex; flex-direction: column; gap: var(--sp2); }

/* ── 一组卡的小标题：几张卡答的是同一类问题时，给它们一个共同的名字 ── */
.grp { display: flex; align-items: baseline; gap: var(--sp2); margin-top: var(--sp2); }
.grp h3 { font-size: var(--fs3); color: var(--fg2); }
.grp .muted { font-size: var(--fs1); }
.grp::after { content: ""; flex: 1 1 auto; border-top: 1px solid var(--line); transform: translateY(calc(var(--sp1) * -1)); }

/* ── 结论块：一次检验的结论和紧跟着它的那句限定，放在一个框里 ──────
   三种底：不带状态色（结论不是警示，比如「不拒绝」）、警示（拒绝、指认出了人）、出错（没有结果）。
   **「不拒绝」不用通过色**：通过色会被读成「查过了没问题」，而它说的只是「没看出来」。
   `data-stale="true"` 是「这条结论说的不是眼前这一份」：照旧摆着，压淡。 */
.verdict { display: flex; flex-direction: column; gap: var(--sp1); padding: var(--sp2) var(--sp3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg); }
.verdict__line { display: flex; align-items: baseline; gap: var(--sp2); flex-wrap: wrap; }
.verdict__say { font-size: var(--fs4); }
.verdict--warn { border-color: var(--warn-line); background: var(--warn-soft); }
.verdict--bad { border-color: var(--bad-line); background: var(--bad-soft); }
.verdict[data-stale="true"] { opacity: .7; }
/* 可折叠卡片正文里几块东西之间的竖向间距（原来靠每块自己写 margin-top） */
.fold__body--stack { display: flex; flex-direction: column; gap: var(--sp2); }
.fold[data-open="false"] .fold__body--stack { display: none; }

/* ── 空清单：一块安静的地方。**空不是错误**，所以没有状态色 ─────
   原来三页各抄一份，已经分了叉（行距 sp1 与 sp2、段宽 48ch 与 52ch），收进来时取多数那一份。 */
.empty {
  border: 1px dashed var(--line); border-radius: var(--r-md);
  padding: var(--sp4) var(--sp3); display: flex; flex-direction: column;
  gap: var(--sp1); align-items: center; text-align: center;
}
.empty > b { font-size: var(--fs3); color: var(--fg2); }
.empty p b { color: var(--fg2); }
.empty p { margin: 0; font-size: var(--fs2); color: var(--fg3); max-width: 52ch; }

/* ── 读盘进度条：`app.js` 的 loadJSON 认 #boot / #boot-fill / #boot-msg ───
   #boot 自己的排版各页不同，留在页面里；进度条是同一件。 */
#boot .track { height: 6px; background: var(--surface2); border-radius: var(--r-sm); overflow: hidden; margin: var(--sp2) 0; max-width: 40em; }
#boot .fill { height: 100%; width: 0; background: var(--acc); transition: width .3s; }

/* ── 表里展开一行的其余字段（把常驻列压到能一屏看完）─────── */
.tbl tr.det > td { background: var(--surface2); }
.tbl tr.det:hover > td { background: var(--surface2); }
.kvline { display: flex; flex-wrap: wrap; gap: var(--sp1) var(--sp3); font-size: var(--fs2); }
.kvline > span { color: var(--fg3); }
.kvline b { color: var(--fg); }

/* ── 点选器：从一批人／系里挑一个。锚在触发它的按钮下面 ───────
   与悬浮详情卡不同：这一个是一次选择，选完即走，所以不遮住整页。 */
.picker {
  position: fixed; z-index: 40; width: min(30em, calc(100vw - var(--sp4) * 2));
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow); display: flex; flex-direction: column; max-height: 60vh;
}
.picker__hd { padding: var(--sp2) var(--sp3); border-bottom: 1px solid var(--line); display: flex; align-items: baseline; gap: var(--sp2); }
.picker__hd b { font-size: var(--fs3); }
.picker__note { padding: var(--sp2) var(--sp3) 0; }
.picker__q { padding: var(--sp2) var(--sp3); }
.picker__q input { width: 100%; }
.picker__list { overflow: auto; padding: 0 var(--sp2) var(--sp2); }
.picker__i {
  width: 100%; text-align: left; font: inherit; font-size: var(--fs2); cursor: pointer;
  background: none; border: 0; color: inherit; border-radius: var(--r-sm);
  padding: var(--sp1) var(--sp2); display: flex; flex-direction: column;
}
.picker__i:hover { background: var(--hover); }
.picker__i > span { font-size: var(--fs1); color: var(--fg3); }

@media (max-width: 700px) {
  .hd, .panel { padding-left: var(--sp2); padding-right: var(--sp2); }
  .stats__grp + .stats__grp { border-left: 0; padding-left: 0; }
}

/* 窄屏（≤900px）回到普通长页面：整页滚，侧栏落到内容上方 */
@media (max-width: 900px) {
  html, body { height: auto; }
  body { display: block; overflow: visible; }
  .shell { display: block; }
  body[data-rail] .railwrap { position: static; width: auto; height: auto; display: block; }
  body[data-rail] .rail { position: static; transform: none; opacity: 1; pointer-events: auto;
    width: auto; height: auto; overflow: visible; box-shadow: none;
    border: 0; border-bottom: 1px solid var(--line); border-radius: 0; }
  body[data-rail] .main { height: auto; overflow: visible; }
  body[data-rail="hide"] .railwrap { display: none; }
  .railmini { display: none !important; }
  /* 带子跟着内容走：窄屏上右栏不再是自己的滚动容器 */
  .main--band { overflow: visible; }
  .mainscroll { overflow: visible; padding: var(--sp3) var(--sp2) var(--sp5); }
  .mainbar { padding: var(--sp2); }
}
