* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  padding: 24px;
  background: #0b1220;
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* ================= 阶段机 =================
   body[data-phase] 决定当前阶段，视图和区块按它显隐。
   "对战时不能切场景"这类约束是靠这里的规则实现的，不是靠 JS 逐个 disable。 */

.view {
  display: none;
  max-width: 1320px;
  margin: 0 auto;
}

body[data-phase="title"]      .view[data-view="title"] { display: block; }
body[data-phase="divination"] .view[data-view="divination"] { display: block; }
body[data-phase="codex"]      .view[data-view="codex"] { display: block; }
body[data-phase="ladder"]     .view[data-view="ladder"] { display: block; }
body[data-phase="tank"]       .view[data-view="tank"] { display: block; }
body[data-phase="rooms"]      .view[data-view="rooms"] { display: block; }
body[data-phase="reports"]    .view[data-view="reports"] { display: block; }
/* 复盘阶段沿用对战视图：战报是盖在定格的画面上的，玩家能看见最后的局面 */
body[data-phase="match"]  .view[data-view="arena"] { display: block; }
body[data-phase="result"] .view[data-view="arena"] { display: block; }

/* 切屏动画已经有了（css/system.css 的 `body.screen-transition .view`，
   由 screens.js 挂类触发）。这里**不要再写第二份**——两份动画会叠加，
   而且我那份挂在 `.view` 上会在每次重渲染时都播一遍。
   转场只此一种，这是文档 3.7 立的规矩。 */

/* 单个区块声明自己只属于哪些阶段 */
[data-phase-only] { display: none !important; }
/* `:not([hidden])` 是必需的，不是保险。
   这条规则的选择器权重（0,2,1）高于全局的 `[hidden]`（0,1,0），两边又都带
   !important——所以**阶段规则会把 hidden 顶掉**：一个 JS 明明设了 hidden
   的元素照样显示。"人不齐，直接开打"在比赛开始后仍然挂着就是这么来的
   （HudView 确实设了 `forceStartBtn.hidden = true`，只是没生效）。
   语义上也该这样：阶段说"这一屏有它"，JS 说"此刻不要它"——后者更具体。 */
body[data-phase="title"]  [data-phase-only~="title"]:not([hidden]),
body[data-phase="match"]  [data-phase-only~="match"]:not([hidden]),
body[data-phase="result"] [data-phase-only~="result"]:not([hidden]) { display: revert !important; }

/* ---- 枢纽：每个系统一张卡 ---- */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(174px, 1fr));
  gap: var(--sp-2);
  margin-top: var(--sp-5);
  max-width: 640px;
}

.hub-card {
  --sys: var(--ink-dim); /* 每张卡在下面被覆盖成自己系统的色相 */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-1);
  padding: var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-4);
  text-align: left;
  /* 玻璃压在水面上：上缘一道内高光，底色往系统色偏一点点。 */
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.055), transparent 42%),
    color-mix(in srgb, var(--sys) 7%, rgba(16, 28, 48, 0.82));
  border: 1px solid color-mix(in srgb, var(--sys) 22%, transparent);
  border-radius: var(--r-md);
  cursor: pointer;
  overflow: hidden;
  transition:
    transform var(--t-base) var(--ease-out),
    border-color var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out),
    background var(--t-base) var(--ease-out);
}

/* 左缘一道系统色的竖条。比整卡染色克制得多，但扫一眼就能分辨。 */
.hub-card::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--sys);
  opacity: 0.75;
  transition: opacity var(--t-base) var(--ease-out);
}

.hub-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--sys) 55%, transparent);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 42%),
    color-mix(in srgb, var(--sys) 13%, rgba(20, 34, 56, 0.9));
  box-shadow: var(--lift-2), 0 0 0 1px color-mix(in srgb, var(--sys) 18%, transparent);
}
.hub-card:hover::before { opacity: 1; }
.hub-card:active { transform: translateY(0); }

/* 图标给一个系统色的圆底，让它读成"可以点的东西"而不是一个字符。 */
.hub-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  margin-bottom: 2px;
  border-radius: var(--r-sm);
  font-size: 16px;
  line-height: 1;
  background: color-mix(in srgb, var(--sys) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sys) 26%, transparent);
}

.hub-name { font-size: var(--fs-md); font-weight: 600; color: var(--ink); }
.hub-desc { font-size: var(--fs-sm); color: var(--ink-dim); line-height: 1.45; }

/* 六个系统各自的色相。写在卡片上而不是内联，是为了让"哪一屏是什么颜色"
   只有一处定义——面包屑和各屏标题都从同一个变量取。 */
.hub-card[data-goto="divination"] { --sys: var(--sys-divination); }
.hub-card[data-goto="codex"]      { --sys: var(--sys-codex); }
.hub-card[data-goto="ladder"]     { --sys: var(--sys-ladder); }
.hub-card[data-goto="tank"]       { --sys: var(--sys-tank); }
.hub-card[data-goto="rooms"]      { --sys: var(--sys-rooms); }
.hub-card[data-goto="reports"]    { --sys: var(--sys-reports); }

/* 还没东西看的界面压暗 + 标一句原因。**在进去之前**就说清楚，
   而不是让玩家点进一个空屏再自己猜。 */
.hub-card.dimmed { opacity: 0.55; }
.hub-card.dimmed:hover { opacity: 0.8; }

.hub-note {
  margin-top: 3px;
  font-size: 10.5px;
  color: var(--other);
}

/* ---- 面包屑 ---- */
/* ---- 面包屑 ----
   它同时是"我在哪一屏"的色标：`--sys` 由 body[data-phase] 决定（见下面
   那组规则），当前位置和左边那道竖条都用它。这样枢纽上那张卡的颜色
   会**一路跟进这一屏**，玩家才会把颜色和系统绑起来。 */
.breadcrumb {
  --sys: var(--ink-dim);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-base);
  color: var(--ink-faint);
}

.crumb-home {
  padding: 3px 11px;
  font-size: var(--fs-base);
  background: transparent;
  border-color: color-mix(in srgb, var(--sys) 30%, var(--line));
  color: var(--ink-dim);
  border-radius: var(--r-pill);
  transition: color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}

.crumb-home:hover {
  color: var(--sys);
  border-color: color-mix(in srgb, var(--sys) 60%, transparent);
}

.crumb-here {
  position: relative;
  padding-left: var(--sp-2);
  color: var(--ink);
  font-weight: 600;
}
/* 当前位置前面一颗系统色的圆点 */
.crumb-here::before {
  content: '';
  position: absolute;
  left: -1px;
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2px;
  border-radius: 50%;
  background: var(--sys);
  box-shadow: 0 0 8px color-mix(in srgb, var(--sys) 60%, transparent);
}

/* 当前在哪一屏 → 面包屑用哪个色。和枢纽卡片是同一组变量。 */
body[data-phase="divination"] .breadcrumb { --sys: var(--sys-divination); }
body[data-phase="codex"]      .breadcrumb { --sys: var(--sys-codex); }
body[data-phase="ladder"]     .breadcrumb { --sys: var(--sys-ladder); }
body[data-phase="tank"]       .breadcrumb { --sys: var(--sys-tank); }
body[data-phase="rooms"]      .breadcrumb { --sys: var(--sys-rooms); }
body[data-phase="reports"]    .breadcrumb { --sys: var(--sys-reports); }

/* 每个界面的内容容器。单栏——一屏一件事，不再需要两栏去塞六个卡片。 */
.screen-body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 880px;
  margin: 0 auto;
  min-width: 0;
}

/* ---- 投放卡：全局浮层 ---- */
/* 它由占卜和鱼谱两处触发，所以不能属于其中任何一个界面。
   固定在底部：无论玩家跳到哪个备战界面，"你有一条鱼待投放"都跟着他。 */
/* ---- 投放确认浮层 ---- */
.deploy-overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(4, 9, 16, 0.72);
  backdrop-filter: blur(3px);
  animation: feed-in 0.18s ease-out;
}

.deploy-card {
  width: min(520px, 100%);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
}

.deploy-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.deploy-actions .primary-btn { flex: 1; }
.deploy-actions .ghost-btn { flex: 0 0 auto; }

body[data-phase="match"] .deploy-overlay,
body[data-phase="result"] .deploy-overlay { display: none; }

/* ---- 投放摘要：三维潜质 ---- */
.deploy-name {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 10px;
}
.deploy-label { font-size: 16px; font-weight: 700; color: var(--ink); }
.deploy-code {
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 11px;
  /* 鱼码是要**逐字抄给别人**的，卡在 4.5 的及格线上不够——抄错一个字符
     就是找不到那条鱼。原来的 #5c708c 在这张卡的底色上只有 3.45，
     连 AA 都没到。--ink-dim 给到 6.7。 */
  color: var(--ink-dim);
}

/* 三条潜质各占一行，条形图 + 数值。
   原来是"觅食 0.91 · 恐惧 0.15 · 捕猎 0.94"挤成一行——数字都在，但
   **看不出哪个高哪个低**，而那正是玩家想知道的第一件事。 */
.deploy-stats { display: grid; gap: 6px; margin-bottom: 12px; }
.ds-row {
  display: grid;
  grid-template-columns: 40px 1fr 34px;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}
.ds-name { color: var(--ink-dim); }
.ds-track {
  height: 7px;
  border-radius: var(--r-pill);
  background: #12233a;
  overflow: hidden;
}
.ds-fill { height: 100%; border-radius: var(--r-pill); }
.ds-fill.forage { background: var(--stat-forage); }
.ds-fill.fear { background: var(--stat-fear); }
.ds-fill.hunt { background: var(--stat-hunt); }
.ds-value {
  font-family: "SFMono-Regular", Consolas, monospace;
  color: var(--ink);
  text-align: right;
}

.deploy-affixes { display: grid; gap: 5px; margin-bottom: 10px; }
.da-row {
  display: grid;
  grid-template-columns: 30px 1fr;
  gap: 8px;
  align-items: baseline;
  font-size: 11.5px;
  padding: 5px 8px;
  border-radius: var(--r-sm);
  background: #0c1626;
}
.da-part { color: #7f93ad; font-size: 10.5px; }
.da-name { color: var(--ink); font-weight: 600; }
.da-desc { grid-column: 2; color: var(--ink-dim); font-size: 10.5px; }

.deploy-bonus {
  padding: 7px 10px;
  border-radius: var(--r-sm);
  margin-bottom: 10px;
  font-size: 11.5px;
}
.deploy-bonus.set { background: rgba(232, 184, 75, 0.1); border: 1px solid rgba(232, 184, 75, 0.3); color: var(--other); }
.deploy-bonus.mark { background: rgba(199, 155, 216, 0.1); border: 1px solid rgba(199, 155, 216, 0.3); color: #c79bd8; }

.deploy-desc {
  font-size: 11.5px;
  color: var(--ink-dim);
  font-style: italic;
  line-height: 1.5;
  padding-left: 9px;
  border-left: 2px solid #22384f;
}

/* ---- 天梯 ---- */
.ladder-mine, .ladder-board {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12.5px;
  color: #a9bcd6;
}

.ladder-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.028), transparent 60%), #0e1a2e;
  border: 1px solid #1f3350;
  border-radius: var(--r-sm);
  transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}
.ladder-row:hover { border-color: var(--line-bright); }

.ladder-row .lr-name {
  flex: 1;
  min-width: 0;
  color: var(--ink);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ladder-row .lr-rating {
  font-family: "SFMono-Regular", Consolas, monospace;
  color: var(--other);
  font-variant-numeric: tabular-nums; /* 数字等宽，一列分数才对得齐 */
  font-weight: 600;
}
.ladder-row .lr-sub { font-size: var(--fs-sm); color: var(--ink-faint); white-space: nowrap; }

/* 我的那一行：左缘竖条 + 粉染。和排行榜里"我这一行"是同一套语言——
   同一个意思在全站只用一种表达，玩家才不用重新学。 */
.ladder-row.is-mine {
  position: relative;
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: linear-gradient(90deg, rgba(204, 79, 176, 0.18), rgba(204, 79, 176, 0.04));
}
.ladder-row.is-mine::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  border-radius: var(--r-pill) 0 0 var(--r-pill);
  background: var(--accent-bright);
}

.ladder-rank {
  width: 1.8em;
  flex-shrink: 0;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
/* 榜首三名和观战排行榜用同一组金银铜——跨屏一致比各屏好看更重要。 */
.ladder-board .ladder-row:nth-child(1) .ladder-rank { color: #f0cf7a; }
.ladder-board .ladder-row:nth-child(2) .ladder-rank { color: #c6d2df; }
.ladder-board .ladder-row:nth-child(3) .ladder-rank { color: #cc9a6a; }

/* ---- 顶栏与阶段指示 ---- */
.topbar {
  max-width: 1320px;
  margin: 0 auto var(--sp-4);
  padding-bottom: var(--sp-3);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  /* 一道向右淡出的分隔线。整条实线会把顶栏切成一个"栏"，
     淡出的那种只是暗示"上面是导航、下面是内容"。 */
  border-bottom: 1px solid transparent;
  border-image: linear-gradient(90deg, var(--line) 0%, transparent 78%) 1;
}

.phase-steps {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: #46586f;
}

.step {
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
}

body[data-phase="title"]  .step[data-step="title"],
body[data-phase="lobby"]  .step[data-step="lobby"],
body[data-phase="match"]  .step[data-step="match"],
body[data-phase="result"] .step[data-step="result"] {
  color: var(--accent-bright);
  border-color: var(--accent);
  background: rgba(204, 79, 176, 0.12);
  font-weight: 600;
}

/* ---- 两栏骨架 ---- */
.lobby-grid,
.arena-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 20px;
  align-items: start;
}

.lobby-main,
.arena-main,
.lobby-side,
.arena-side {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

@media (max-width: 1180px) {
  .lobby-grid,
  .arena-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 卡片：所有面板统一长这样 ---- */
.card {
  border: 1px solid #22344f;
  border-radius: var(--r-md);
  padding: 14px 16px;
  background: #101c30;
}

.card h2 {
  font-size: 14px;
  margin: 0 0 4px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.card h3 {
  font-size: 11px;
  color: var(--ink-dim);
  margin: 14px 0 6px;
  font-weight: 600;
}

.card .hint { max-width: none; }

/* 主行动按钮。**一屏最多一个**——两个同等份量的主按钮等于没有主按钮。 */
.primary-btn {
  background: linear-gradient(135deg, var(--accent), #a83c92);
  border-color: #e066c8;
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 12px rgba(204, 79, 176, 0.25);
}

.primary-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, var(--accent-bright), var(--accent));
  border-color: var(--accent-bright);
  box-shadow: 0 3px 18px rgba(204, 79, 176, 0.4);
}

.primary-btn:disabled { box-shadow: none; }

/* 次级按钮：只有轮廓，用在"再看看""取消"这类不希望被误点的地方。 */
.ghost-btn {
  background: transparent;
  border-color: var(--line-bright);
  color: var(--ink-dim);
}
.ghost-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.05);
  color: var(--ink);
}

.deploy-card {
  border-color: #3a8f5a;
  /* 投放卡是"绿灯"：它出现就意味着可以下水了，所以底色偏绿。
     加一点纵向渐变让它不像一块平的色板。 */
  background: linear-gradient(180deg, #143020, #0e2117);
}

.deploy-summary {
  font-size: 12.5px;
  color: #cfe8d8;
  line-height: 1.7;
  margin-bottom: 12px;
}

.deploy-summary strong { color: var(--good); }

.arena-actions {
  display: flex;
  gap: 8px;
}

#scene-select {
  width: 100%;
  margin-bottom: 6px;
}

h1 {
  font-size: 18px;
  margin: 0 0 4px;
}

.hint {
  font-size: 13px;
  color: var(--ink-dim);
  margin: 0 0 12px;
  max-width: 480px;
  line-height: 1.5;
}

.canvas-wrap {
  position: relative;
  line-height: 0; /* 去掉 canvas 底部的行内间隙，不然事件流会被顶偏 */
}

#world-canvas {
  background: #0e1a2e;
  border: 1px solid #1f3350;
  border-radius: var(--r-sm);
  /* 后备缓冲固定 960×600，但显示尺寸得跟着视口收。不收的话，画布在
     860px 的窗口里仍然是 961px 宽，**从左右两侧同时顶出页面**
     （实测 left=-58、right=903），逼出一条横向滚动条。
     max-width + height:auto 会按 canvas 的固有比例等比缩，HUD 挂在
     .canvas-wrap 上一起缩，不会错位。
     缩放之后 `clientX - rect.left` 不再等于画布坐标，点击换算见
     main.js 的 canvasPoint()——漏了这一步就是"点鱼点偏，而且偏移量
     随窗口宽度变"，宽屏上完全测不出来。 */
  max-width: 100%;
  height: auto;
}

/* 事件流的排版骨架。**位置不在这里定**——它只出现在 `.hud-top` 里，由
   arena.css 摆到顶部右侧。这里原先还有 `position:absolute; left/bottom;
   width:60%; max-width:460px`，是"压在画布左下角"那版的遗留，早被
   arena.css 的 `position:static; width:auto; max-width:none` 顶掉了，
   删掉的是那几条。
   下面这些**没删，是因为还在用**：竖排、贴底、不吃指针事件都由这条提供，
   arena.css 只覆盖 gap 和 align-items。 */
.event-feed {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 5px;
  pointer-events: none;
  line-height: 1.5;
}

.feed-item {
  padding: 5px 9px;
  border-radius: var(--r-sm);
  background: rgba(10, 18, 32, 0.78);
  border-left: 2px solid #3a5070;
  color: #a9bcd6;
  font-size: 12px;
  animation: feed-in 0.25s ease-out;
}

/* 玩家自己的鱼死亡判词：必须比普通事件醒目得多，这是玩家复盘的唯一入口。 */
.feed-item.verdict {
  background: rgba(42, 20, 36, 0.92);
  border-left: 3px solid var(--accent);
  color: #f0d8e8;
  font-size: 13px;
  padding: 8px 11px;
}

.feed-item.verdict .verdict-tag {
  display: block;
  color: var(--accent-bright);
  font-weight: 600;
  font-size: 11px;
  margin-bottom: 2px;
}

.feed-item.fading {
  opacity: 0;
  transition: opacity 0.6s ease-out;
}

@keyframes feed-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---- 局末战报：盖在画布上的一层，样式从简，信息对就行 ---- */
/* ---- 复盘面板 ----
   **结算屏不是"盖在鱼缸上的一层"，它就是这一屏本身。**

   原来是 `position:absolute; inset:0` 钉在画布里，于是内容被压进 601px
   高的框，长报告只能在里面套一个滚动条——而外面还剩几百像素空白。
   嵌套滚动条在这种地方永远是错的：玩家滚外层没反应，滚内层又不知道
   自己在滚什么。

   改成正常文档流：面板要多高就多高，滚的是页面。

   （原来这里还有一条 `min-height: 601px`，是照着当时那块固定 601px 高的画布
   写的。画布现在会随视口变尺寸（窄屏能到 369），那个数字就成了一个没有出处
   的魔数；而报告本身怎么也比它高——桌面分栏态 873、手机 1643——所以它既不
   起作用又在误导。删掉，让内容自己决定高度。） */
.report-panel {
  position: relative;
  inset: auto;
  background: rgba(8, 14, 26, 0.93);
  border-radius: var(--r-sm);
  line-height: 1.5;
  animation: feed-in 0.3s ease-out;
}

/* 结算时画布让位：报告是主角，底下那张静止的鱼缸没有信息量。
   （画布留着不销毁——回到大厅还要用它。） */
/* 结算屏把局内 HUD 整套摘掉。
   顶栏（计时/场地）、底栏（神谕/视角/放弃）、右上角排行榜——**它们全是
   "对战中"的东西**：计时归零了、神谕用不上了、视角没有画面可切了。
   排行榜更直接：它是贴着画布右上角的浮层，画布一藏，它就正好压在报告上
   （实测 178×223 盖住报告右上）。
   结算要的名次已经在报告里重画成一张能读的表了，见 .report-standings。 */
body[data-phase="result"] .hud-top,
body[data-phase="result"] .hud-bottom,
body[data-phase="result"] .hud-leaderboard { display: none; }

body[data-phase="result"] #world-canvas { display: none; }
body[data-phase="result"] .canvas-wrap {
  line-height: normal;
  /* 画布一 `display:none`，.canvas-wrap 就没有宽度来源了——它是
     `.arena-stage`（flex，居中）里的一个 flex item，**收缩到内容宽度**。
     实测面板只剩 518px，占 1297px 舞台的 40%，体型曲线和四张数据卡全挤成
     一条。这是上一批把面板从 `position:absolute; inset:0` 挪进正常文档流时
     带出来的：那时候它的宽度是画布给的，画布一藏就没了。
     显式给回画布的宽度——顺带保证 对战→结算 切屏时布局不跳。 */
  width: 100%;
  /* 宽屏下让报告用掉横向空间，不再挤在画布那 960px 里——理由见
     .report-inner 的分栏说明。窄屏仍然跟画布同宽，切屏不跳。 */
  max-width: 1200px;
}

.report-panel[hidden] {
  display: none;
}

.report-inner {
  padding: 18px 22px 22px;
}

/* ---- 宽屏分栏 ----
   竖着一条下来在手机上是对的（一屏一件事，拇指往下滑）。但桌面上同一个
   布局就变成了**960px 宽、1264px 高的一条**：右边一大片空着，玩家却要滚动
   才能看完一局的结论。结算屏的信息量本来就该一屏放下——它是"看一眼就决定
   要不要再来一局"的地方，滚动是额外的一道门槛。

   分成两栏：左边"数据"（最长的一块），右边"排名 / 那一刻 / 建议"。
   判词满宽压顶——它是这一屏的第一句话；CTA 和按钮组满宽收底。
   实测竖排 1264px → 分栏约 800px。

   断点取 1000px：低于它两栏各自不到 460px，四张数据卡会被挤到换行，
   分栏就反而变高了。 */
@media (min-width: 1000px) {
  .report-inner {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    grid-template-areas:
      'verdict verdict'
      'data    standings'
      'data    moment'
      'data    sugg'
      'cta     cta'
      'acts    acts';
    /* 最后一行吃掉剩余高度，两栏不等高时不会互相拉扯 */
    grid-template-rows: auto auto auto 1fr auto auto;
    column-gap: var(--sp-5);
    align-items: start;
  }

  .report-inner > .report-verdict-act { grid-area: verdict; }
  .report-inner > .ra-data { grid-area: data; }
  .report-inner > .ra-standings { grid-area: standings; }
  .report-inner > .ra-moment { grid-area: moment; }
  .report-inner > .report-suggestions { grid-area: sugg; }
  .report-inner > .report-register-cta { grid-area: cta; }
  .report-inner > .report-actions { grid-area: acts; }

  /* 分栏后每块自己收底部间距，靠 grid 的行距统一管更容易失控 */
  .report-inner > .report-act { margin-bottom: var(--sp-4); }
}

/* 复盘屏里唯一的 h2 就是判词，别在这儿统一压字号——
   这条规则（0,1,1）比 .report-verdict（0,1,0）强，会把该最大的那句压到最小。
   **同一条陷阱还咬了第二口**：原来这里写的是 `margin: 0 0 8px`，那个简写
   把 margin-left/right 一并设成了 0，于是 .report-verdict 的 `margin: 0 auto`
   被覆盖，判词整块偏到了左边——文字自己是居中的（继承了 text-align），
   所以看起来"居中了但又没完全居中"，最难查的那种。
   只写块方向的外边距，不碰行内方向。 */
.report-panel h2 {
  margin-top: 0;
  margin-bottom: 8px;
}

.report-panel h3 {
  font-size: 12px;
  margin: 16px 0 6px;
  color: var(--ink-dim);
  font-weight: 600;
}

.report-headline {
  font-size: 13px;
  color: #a9bcd6;
}

.report-headline strong {
  color: var(--accent-bright);
  font-size: 15px;
}

/* 曲线外面套一层，是为了把**文字挪出 SVG**。
   这张 SVG 用的是 `preserveAspectRatio="none"`——对一条折线来说这没问题
   （它本来就该横向铺满），但同一个非等比缩放会把里面的一切都拉伸：
   viewBox 宽 320 铺到 960 是 3 倍，高 80 铺到 100 只有 1.25 倍，于是
   "峰值 45.0" 这几个字被横向拉成 2.4 倍宽，峰值那个圆点被拉成椭圆。
   **文字和圆形永远不该待在非等比缩放的 SVG 里**，所以它们改用 HTML 定位。 */
.report-curve-wrap {
  position: relative;
  margin: var(--sp-3) 0 var(--sp-4);
}

.report-curve {
  display: block; /* 行内元素会在底下留一条基线缝，看着像边框没对齐 */
  width: 100%;
  height: 96px;
  background: #0c1626;
  border: 1px solid #1f3350;
  border-radius: var(--r-xs);
}

.curve-peak {
  position: absolute;
  top: 6px;
  left: 10px;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  pointer-events: none;
}

/* 峰值那一点：HTML 的小圆点，圆的就是圆的。 */
.curve-dot {
  position: absolute;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: #ff8ad4;
  box-shadow: 0 0 0 2px rgba(255, 138, 212, 0.22);
  pointer-events: none;
}

/* 时间分配和词条这两块原来是 4~5px 的行距，五行 + 三行连在一起读起来是
   一整片，眼睛没有落点。**行距是分组的手段**：行内 6px、块间 20px，
   "这是一组、那是另一组"就不用画分隔线也看得出来。 */
.report-timesplit {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--sp-5);
}

.split-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: #a9bcd6;
}

.split-label {
  width: 3.2em;
  flex-shrink: 0;
}

.split-bar {
  flex: 1;
  height: 9px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.split-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  /* 0% 的那几行也要看得出"这里是 0"而不是"这一行坏了"——
     给一个最小可见宽度做不到（0 就是 0），所以靠槽本身的底色表达。 */
  min-width: 0;
}

.split-pct {
  width: 2.6em;
  text-align: right;
  flex-shrink: 0;
  font-family: "SFMono-Regular", Consolas, monospace;
}

.report-deaths,
.report-affixes {
  font-size: 12px;
  color: #a9bcd6;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.death-row {
  padding: 6px 9px;
  background: rgba(42, 20, 36, 0.6);
  border-left: 2px solid var(--accent);
  border-radius: var(--r-xs);
}

.death-row .death-time {
  color: var(--accent-bright);
  font-weight: 600;
  margin-right: 6px;
}

.affix-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 8px;
  background: #0e1a2e;
  border-radius: var(--r-xs);
}

.affix-row .affix-count {
  font-family: "SFMono-Regular", Consolas, monospace;
  color: var(--ink);
}

.affix-row .affix-count.zero {
  color: var(--bad);
}

.affix-row .affix-count.passive {
  color: var(--ink-dim);
}

.report-suggestions {
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.suggestion-row {
  padding: 9px 12px;
  background: rgba(20, 38, 26, 0.7);
  border-left: 3px solid #3a8f5a;
  border-radius: var(--r-xs);
  font-size: 12.5px;
  color: #cfe8d8;
}

.report-actions {
  display: flex;
  gap: 8px;
  margin-top: 18px;
}


.match-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
  padding: 8px 12px;
  background: #101c30;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  font-size: 13px;
}

#match-status {
  font-weight: 600;
}

#camera-select,
#scene-select {
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  padding: 4px 6px;
}

.scene-hint {
  margin-top: -4px;
}

#match-results {
  max-width: none;
  margin-top: 8px;
}


.ring-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid;
}

.controls {
  display: flex;
  gap: 8px;
  margin-top: 6px;
}


button {
  flex: 1;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  border: 1px solid #2c4468;
  /* 上缘一道内高光：按钮是"抬起来的一层"，纯平色读不出可按。 */
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), transparent 55%), #16233a;
  color: var(--ink);
  cursor: pointer;
  font-size: var(--fs-base);
  transition:
    background var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}

button:hover:not(:disabled) {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), transparent 55%), #1c2d4a;
  border-color: #3a5a86;
}

/* 按下去要有位移。**这是最便宜也最有效的"点到了"反馈**——
   没有它，慢网络下玩家会怀疑自己没点上，然后连点。 */
button:active:not(:disabled) { transform: translateY(1px); }

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* 键盘焦点必须看得见。这是可访问性的底线，也不只是可访问性——
   用键盘走一遍表单是最快的自测方式，看不见焦点就没法走。 */
button:focus-visible,
input:focus-visible,
select:focus-visible,
label:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 2px;
}

input:focus,
select:focus {
  border-color: color-mix(in srgb, var(--glow) 45%, var(--line));
}

button:disabled:hover {
  background: #16233a;
}


.chat-log {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  min-height: 180px;
  max-height: 300px;
  overflow-y: auto;
  padding: var(--sp-3);
  /* 对话区往深里压一档：它是"看进去"的东西，不是"抬起来"的面板。
     底部留一道极淡的内阴影，暗示还有内容在下面。 */
  background: linear-gradient(180deg, #0b1626, #0e1a2e);
  border: 1px solid #22344f;
  border-radius: var(--r-md);
  margin-bottom: var(--sp-3);
  scrollbar-width: thin;
  scrollbar-color: #22384f transparent;
}
/* 空态：还没说过话的时候，这里是一个 180px 高、带边框、里面什么都没有的
   盒子——看着像加载失败，而不是"在等你开口"。
   占卜师故意不先说话（那是用户定的：一进来就自说自话，玩家还没决定要干
   什么），所以这句提示只能由界面自己给。用 ::before 而不是往 DOM 里插一个
   占位元素：插进去的东西要记得在第一条消息到达时删掉，而 :empty 不会忘。 */
.chat-log:empty {
  align-items: center;
  justify-content: center;
}
.chat-log:empty::before {
  content: '说说你想要一条什么样的鱼';
  color: var(--ink-faint);
  font-size: var(--fs-sm);
  letter-spacing: 0.04em;
}

.chat-log::-webkit-scrollbar { width: 7px; }
.chat-log::-webkit-scrollbar-track { background: transparent; }
.chat-log::-webkit-scrollbar-thumb { background: #22384f; border-radius: var(--r-pill); }

.chat-bubble {
  max-width: 82%;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-base);
  line-height: 1.55;
  /* 每条气泡都从对应的一侧滑进来。对话是有来有往的，
     入场方向跟着说话的人走，读起来才有"谁在说"的节奏。 */
  animation: bubble-in var(--t-base) var(--ease-out);
}

@keyframes bubble-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

.chat-bubble.player {
  align-self: flex-end;
  background: linear-gradient(135deg, #22456e, #1a3454);
  border: 1px solid #2c5583;
  color: var(--ink);
  /* 靠说话人那一侧的圆角收一档：这是气泡"指向"说话人的最轻做法，
     比画一个尖角干净得多。 */
  border-bottom-right-radius: var(--r-xs);
}

.chat-bubble.diviner {
  align-self: flex-start;
  background: linear-gradient(135deg, #32224a, #241834);
  border: 1px solid #45305e;
  color: #e8d9ff;
  border-bottom-left-radius: var(--r-xs);
  /* 字体族在 tokens.css 里统一指定（全站只有这里和判词用笔触体）；
     字号留在组件这边，改气泡排版时不用翻两个文件。 */
  font-size: var(--fs-md);
  line-height: 1.8;
  box-shadow: 0 0 18px rgba(160, 111, 208, 0.1);
}

@media (prefers-reduced-motion: reduce) {
  .chat-bubble { animation: none; }
}

.chat-input-row {
  display: flex;
  gap: 8px;
}

.chat-input-row input {
  flex: 1;
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  padding: 8px;
}

#llm-log {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  max-height: 220px;
  overflow-y: auto;
}

#llm-log li {
  border-top: 1px solid #1f3350;
  padding: 8px 0;
  font-size: 12px;
  color: #a9bcd6;
  line-height: 1.5;
}

#llm-log li:first-child {
  border-top: none;
}

#llm-log .log-desc {
  color: var(--ink);
  font-weight: 600;
}

#llm-log .log-params {
  font-family: "SFMono-Regular", Consolas, monospace;
}

.parts-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 6px;
}

.part-slot {
  position: relative;
  flex: 1 1 30%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 6px;
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  text-align: center;
  transition: border-color 0.2s, background 0.2s;
  cursor: default;
}

.part-tooltip {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translate(-50%, 4px);
  width: max-content;
  max-width: 200px;
  background: #16233a;
  border: 1px solid #2c4468;
  border-radius: var(--r-sm);
  padding: 6px 8px;
  font-size: 11px;
  font-weight: normal;
  line-height: 1.4;
  color: var(--ink);
  text-align: left;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
  z-index: 10;
}

.part-slot:hover .part-tooltip {
  opacity: 1;
}

.part-slot.positive {
  border-color: #3a8f5a;
  background: #10261a;
}

.part-slot.negative {
  border-color: #a04848;
  background: #2a1414;
}

.part-icon {
  font-size: 16px;
}

.part-label {
  font-size: 11px;
  color: var(--ink-dim);
}

.part-value {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
}

.part-slot.positive .part-value {
  color: var(--good);
}

.part-slot.negative .part-value {
  color: var(--bad);
}

.stat-preview {
  flex-basis: 100%;
  text-align: center;
  font-size: 12px;
  color: #a9bcd6;
  font-family: "SFMono-Regular", Consolas, monospace;
}

.example-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}

.example-chip {
  flex: 0 0 auto;
  padding: 5px 10px;
  border-radius: var(--r-pill);
  border: 1px solid #2c4468;
  background: #16233a;
  color: #a9bcd6;
  font-size: 12px;
  cursor: pointer;
}

.example-chip:hover {
  background: #1c2d4a;
  color: var(--ink);
}

.set-preview {
  flex-basis: 100%;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  color: #f0d060;
  min-height: 1.2em;
}

.preview-warning {
  flex-basis: 100%;
  text-align: center;
  font-size: 11px;
  color: #f0c060;
  min-height: 1.4em;
}


.inspector-content {
  font-size: 12px;
  color: #a9bcd6;
  line-height: 1.6;
}

.inspector-content .inspector-name {
  color: var(--ink);
  font-weight: 600;
  font-size: 13px;
  display: block;
  margin-bottom: 4px;
}

.inspector-content .inspector-affix {
  display: inline-block;
}

.inspector-content .inspector-affix.positive {
  color: var(--good);
}

.inspector-content .inspector-affix.negative {
  color: var(--bad);
}

.inspector-content .inspector-set {
  display: block;
  margin-top: 6px;
  color: #f0d060;
  font-weight: 600;
}

#llm-log .log-set {
  color: #f0d060;
}

/* ---- M2：每日水域横幅 ---- */
/* 今日水域横幅。它出现在每一屏，所以要**足够安静**——但又必须一眼认出来，
   因为它是当天所有对局的前提条件。折中：左缘一道金条 + 极淡的金底，
   本身不发光，靠位置和色相被认出来。 */
.daily-banner {
  position: relative;
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-4);
  margin-bottom: var(--sp-2);
  background: linear-gradient(90deg, rgba(232, 184, 75, 0.13), rgba(232, 184, 75, 0.02) 62%);
  border: 1px solid rgba(232, 184, 75, 0.16);
  border-left: 3px solid var(--other);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
  color: #d8c79a;
  line-height: 1.55;
}

.daily-banner strong { color: #f0d090; }

/* ---- M2：神谕 ---- */
.oracle-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  padding: 7px 10px;
  background: #101c30;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
}

.oracle-label {
  font-size: 12px;
  color: var(--ink-dim);
  flex: 0 0 auto;
}

.oracle-btn {
  flex: 0 0 auto;
  padding: 5px 10px;
  font-size: 12px;
  border-radius: var(--r-pill);
}

.oracle-btn.armed {
  background: #6b4a9e;
  border-color: #9b6fd6;
  color: #f0e4ff;
}

.oracle-status {
  flex: 1;
  text-align: right;
  font-size: 11px;
  color: var(--ink-dim);
}

/* 待施放时画布变成"瞄准"状态 */
.canvas-wrap.aiming #world-canvas {
  cursor: crosshair;
  box-shadow: 0 0 0 2px #9b6fd6;
}

/* ---- M2：鱼谱 / 图鉴 ---- */

.codex-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12px;
  color: #a9bcd6;
  max-height: 340px;
  overflow-y: auto;
  /* 明确禁掉横向滚动：这个列表永远该纵向滚，横向出现滚动条一定是
     里面某个东西撑破了，而不是"内容本来就宽"。 */
  overflow-x: hidden;
  padding-right: 4px;
}

.codex-row {
  padding: 9px 11px;
  background: #0e1a2e;
  border: 1px solid #1f3350;
  border-radius: var(--r-sm);
  line-height: 1.5;
  /* 卡片自己管住宽度。没有这一条的话，超宽的按钮行会把整个列表撑出
     一条横向滚动条（那条又长又丑的滑块就是这么来的）。 */
  min-width: 0;
  overflow: hidden;
}

.codex-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.codex-row .codex-name {
  color: var(--ink);
  font-weight: 600;
  /* 玩家可以起很长的名字，截断而不是撑破卡片。 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.codex-code {
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 10.5px;
  /* 同 .deploy-code：鱼码要抄写，给足对比度。 */
  color: var(--ink-dim);
  flex: 0 0 auto;
}

.codex-row .codex-career {
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 11px;
  color: var(--ink-dim);
}

.codex-row .codex-ancestor {
  font-size: 11px;
  color: #b08fd6;
}

/* ⚠️ 这里**刻意不写 width**。
   原来是 `.codex-row button { width: 100% }`（给更早的单列布局写的），
   它会泄漏进下面的 .codex-actions：danger-btn 是 flex:0 0 auto，
   于是拿 width:100% 当基准尺寸独占一整行，剩下三个被挤成一个字宽的竖排。
   那就是截图里"用/它/出/战"竖着排 + 一条超长横向滚动条的成因。 */
.codex-row .codex-actions button {
  margin-top: 0;
  padding: 4px 6px;
  font-size: 11px;
  white-space: nowrap;
}

/* 词条图例：三档各一个色块 + 名字。词条卡和鱼身描边用的是同一套配色，
   图例摆在图鉴上方一次说清，别处就不用重复解释。 */
.affix-legend {
  display: flex;
  gap: 10px;
  margin: 0 0 7px;
  font-size: 10.5px;
  color: var(--ink-faint);
}

.legend-item::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 4px;
  border-radius: var(--r-xs);
  border: 1px solid currentColor;
  vertical-align: -1px;
}

.legend-item.positive { color: var(--good); }
.legend-item.tradeoff { color: #c9a7f0; }
.legend-item.negative { color: var(--bad); }

/* ---- 异步多人：幽灵鱼面板 ---- */

.ghost-status {
  font-size: 12px;
  color: var(--other);
  padding: 6px 9px;
  background: rgba(232, 184, 75, 0.08);
  border-left: 2px solid var(--other);
  border-radius: var(--r-xs);
}

.reports-list,
.revenge-list,
.my-codes {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  color: #a9bcd6;
}

.report-row {
  padding: 6px 9px;
  background: rgba(232, 184, 75, 0.1);
  border-left: 2px solid var(--other);
  border-radius: var(--r-xs);
  line-height: 1.5;
}

.report-row strong {
  color: #f0d090;
}

/* 「败给了谁」用红而不是战报的金：一个是战功，一个是账。
   两种颜色摆在一起，玩家扫一眼就知道哪几条是要还的。 */
.defeats-list {
  margin-top: 5px;
}

.defeat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: rgba(240, 160, 160, 0.09);
  border-left-color: #c56b6b;
}

.defeat-row strong { color: #e8b0b0; }
.defeat-row .defeat-killer { color: var(--bad); }

.defeat-row .defeat-times {
  color: var(--bad);
  font-weight: 600;
}

.defeat-row button {
  flex-shrink: 0;
  padding: 3px 9px;
  font-size: 11px;
  color: var(--bad);
  background: #2a1a1e;
  border-color: #7a3a3a;
}

.defeat-row button:hover:not(:disabled) { background: #3a2226; }
.defeat-row button:disabled { color: #6b7f96; border-color: #2a3d55; }

.revenge-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  background: rgba(42, 20, 36, 0.7);
  border-left: 2px solid var(--accent);
  border-radius: var(--r-xs);
}

.revenge-row .revenge-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.revenge-row button {
  flex: 0 0 auto;
  padding: 3px 9px;
  font-size: 11px;
}

.code-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-xs);
}

.code-chip .code-value {
  font-family: "SFMono-Regular", Consolas, monospace;
  color: var(--other);
  letter-spacing: 1px;
  font-weight: 600;
}

.code-chip .code-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.code-input-row {
  display: flex;
  gap: 6px;
}

.code-input-row input {
  flex: 1;
  min-width: 0;
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  padding: 6px 8px;
  text-transform: uppercase;
}

.code-input-row button {
  flex: 0 0 auto;
  padding: 6px 12px;
  font-size: 12px;
}

.code-status {
  font-size: 11px;
  color: var(--ink-dim);
  margin-top: 5px;
  min-height: 1.2em;
}

.name-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

.name-row label {
  font-size: 12px;
  color: var(--ink-dim);
  flex: 0 0 auto;
}

.name-row input {
  flex: 1;
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  padding: 7px 8px;
}


.leaderboard-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.leaderboard-list li {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 6px 8px;
  border-radius: var(--r-sm);
  background: #0e1a2e;
  border: 1px solid #1f3350;
  font-size: 12px;
}

.leaderboard-empty {
  color: var(--ink-dim);
  justify-content: center;
}

.lb-rank {
  color: var(--ink-dim);
  font-weight: 600;
  width: 1.4em;
  flex-shrink: 0;
}

.lb-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lb-size {
  font-family: "SFMono-Regular", Consolas, monospace;
  color: var(--ink);
}

.lb-kills {
  font-size: 11px;
  color: var(--ink-dim);
  flex-shrink: 0;
}

.leaderboard-list li.leaderboard-you {
  border-color: var(--accent);
  background: #2a1424;
}

.leaderboard-list li.leaderboard-dead {
  opacity: 0.55;
}

.lb-status {
  font-size: 11px;
  color: var(--bad);
  flex-shrink: 0;
}

.director-toggle {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--ink-dim);
  cursor: pointer;
  flex: 0 0 auto;
}

/* ---- 登录 ---- */
.auth-card {
  max-width: 380px;
  margin: 60px auto;
}

.auth-fields {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 14px 0 12px;
}

.auth-fields input {
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  padding: 9px 10px;
}

.auth-status { min-height: 1.4em; margin-top: 10px; }

.account-bar {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-base);
  color: var(--ink-dim);
}

.account-bar button {
  flex: 0 0 auto;
  padding: 4px 10px;
  font-size: 11px;
}

/* 鱼谱里每条鱼的三个动作：出战 / 当先祖 / 丢回水里 */
.codex-actions {
  display: flex;
  flex-wrap: wrap; /* 窄屏时换行，而不是把每个按钮压成一个字宽 */
  gap: 5px;
  margin-top: 7px;
}

.codex-actions button {
  flex: 1 1 auto;
  min-width: 66px; /* 保证"用它出战"四个字排得下，不会竖起来 */
  padding: 4px 6px;
  font-size: 11px;
}

.codex-actions .use-btn {
  background: #1b4030;
  border-color: #3a8f5a;
  color: var(--good);
}

.codex-actions .use-btn:hover { background: #235340; }

/* 变奏：迭代构筑的快捷入口，视觉上仅次于"出战"。
   用紫色和出战的绿区分开——一个是"就用它"，一个是"改改它"。 */
.codex-actions .vary-btn {
  background: #2a1f40;
  border-color: #6b4fa0;
  color: #c9a7f0;
}

.codex-actions .vary-btn:hover { background: #382a52; }

/* 删除是不可逆的，但它不该抢戏——鱼谱是用来出战的，不是用来清理的。
   所以做成"暗着的红"：找得到，但不会让人手滑点上去。 */
.codex-actions .danger-btn {
  /* 不参与等分，但也不能独占一行——auto 基准 + 不放大。 */
  flex: 0 1 auto;
  min-width: 0;
  color: #6b7f96;
  border-color: #2a3d55;
}

.codex-actions .danger-btn:hover {
  color: var(--bad);
  border-color: #7a3a3a;
  background: #2a1a1e;
}

/* ---- 鱼缸设置 / 房间列表 ---- */
.tank-field {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 7px;
}

.tank-field label {
  flex: 0 0 3em;
  font-size: 12px;
  color: var(--ink-dim);
}

.tank-field input,
.tank-field select {
  flex: 1;
  min-width: 0;
  background: #0e1a2e;
  border: 1px solid #22344f;
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  padding: 6px 8px;
}

.tank-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12px;
  color: #a9bcd6;
  max-height: 260px;
  overflow-y: auto;
}

/* 房间行。左边一枚门的图标块，让一列房间读起来像一排门而不是一堆文字。 */
.tank-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.028), transparent 60%), #0e1a2e;
  border: 1px solid #1f3350;
  border-radius: var(--r-sm);
  transition: border-color var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.tank-row:hover {
  border-color: color-mix(in srgb, var(--sys-rooms) 45%, transparent);
  transform: translateX(2px);
}

.tank-row::before {
  content: '🚪';
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-sm);
  font-size: 14px;
  background: color-mix(in srgb, var(--sys-rooms) 14%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sys-rooms) 24%, transparent);
}

.tank-row .tank-info { flex: 1; min-width: 0; line-height: 1.5; }
.tank-row .tank-title {
  color: var(--ink);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tank-row .tank-meta { font-size: var(--fs-sm); color: var(--ink-dim); }
.tank-row button { flex: 0 0 auto; padding: 6px 14px; font-size: var(--fs-sm); }

/* ---- 状态陈列页（?gallery）---- */
.gallery-root { max-width: 1320px; margin: 0 auto; }

.gallery-header { margin-bottom: 28px; }
.gallery-header h1 { font-size: 20px; margin: 0 0 6px; }
.gallery-header p { font-size: 12px; color: var(--ink-dim); line-height: 1.6; max-width: 640px; }
.gallery-header a { color: var(--accent-bright); }

.gallery-section {
  margin-bottom: 34px;
  padding-top: 14px;
  border-top: 1px solid #22344f;
}

.gallery-title { font-size: 15px; margin: 0 0 4px; color: var(--ink); }
.gallery-note { font-size: 12px; color: var(--ink-dim); margin: 0 0 14px; max-width: 720px; line-height: 1.6; }

.gallery-section {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: flex-start;
}
.gallery-title, .gallery-note { flex-basis: 100%; }

.gallery-frame {
  flex: 0 1 380px;
  min-width: 300px;
}

.gallery-tag {
  font-size: 11px;
  color: var(--other);
  margin-bottom: 5px;
  font-family: "SFMono-Regular", Consolas, monospace;
}

.gallery-missing {
  flex-basis: 100%;
  padding: 10px 12px;
  background: rgba(160, 72, 72, 0.18);
  border-left: 3px solid #a04848;
  border-radius: var(--r-xs);
  color: var(--bad);
  font-size: 12px;
}

/* 陈列页：真实视图靠 data-phase="gallery" 自然全部不命中而隐藏，
   顶栏单独藏掉；陈列出来的克隆件不受 data-phase-only 影响（属性已被剥掉）。 */
body.gallery-mode .topbar { display: none; }

/* 陈列页里的投放卡要**排在版面里，不要浮在视口上**。
   `.deploy-overlay` 本体是 `position: fixed; inset: 0` 的全屏遮罩——那在游戏
   里是对的，但陈列页把它当成一个组件示例塞进 .gallery-root 之后，fixed 让它
   逃出容器盖住整个视口：**下面每一节都隔着一层 72% 的暗色纱看**。
   而这一页的用途恰恰是"改完样式回来人肉过一遍"，蒙着纱就过不了。 */
body.gallery-mode .gallery-root .deploy-overlay {
  position: relative;
  inset: auto;
  z-index: auto;
  padding: 0;
  background: none;
  backdrop-filter: none;
  animation: none;
}

/* ---- 登录/注册 modal（不再是一道墙）---- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(6, 10, 20, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 20px;
}

.modal-backdrop[hidden] { display: none; }

.modal-backdrop .auth-card {
  margin: 0;
  width: 100%;
  max-width: 380px;
  animation: feed-in 0.25s ease-out;
}

.auth-close {
  width: 100%;
  margin-top: 10px;
  background: transparent;
  border-color: transparent;
  color: var(--ink-dim);
  font-size: 12px;
}

.auth-close:hover { background: #16233a; color: var(--ink); }

/* 注册按钮出现在每一屏的右上角，是游客转正式的唯一入口。
   底色从 --accent(#cc4fb0) 压深到 #a63c8f：白字在原色上只有 3.9:1，
   低于 AA 对小字的 4.5。**压深底色而不是调亮文字**——白已经到头了。 */
#register-cta {
  background: linear-gradient(135deg, #b8449c, #93336f);
  border-color: #cc4fb0;
  color: #fff;
  font-weight: 600;
}

#register-cta:hover {
  background: linear-gradient(135deg, var(--accent), #a83c92);
  border-color: var(--accent-bright);
}

.inline-cta {
  width: 100%;
  padding: 9px;
  font-size: 12px;
  background: rgba(204, 79, 176, 0.12);
  border: 1px dashed var(--accent);
  color: var(--accent-bright);
}

.inline-cta:hover { background: rgba(204, 79, 176, 0.22); }

.report-register-cta {
  width: 100%;
  margin-top: 16px;
  padding: 11px;
  font-size: 12.5px;
  background: rgba(204, 79, 176, 0.16);
  border: 1px solid var(--accent);
  color: var(--accent-bright);
  font-weight: 600;
}

.report-register-cta:hover { background: rgba(204, 79, 176, 0.28); }

/* 「空状态折叠」那套（.card.collapsed / .collapsed-note）已删。
   它当初是给"新玩家备战首屏 5 张空卡"设计的，而那个首屏后来换成了枢纽卡
   布局，**没有任何代码再加这个类**——只剩 ?gallery 里一帧手工拼的演示在用它。
   留着的话，下一个人会以为这是个活状态并去维护它。 */

/* 生成完成后确认卡要抓一次注意力，否则玩家不会发现页面下方多了东西 */
@keyframes card-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(58, 143, 90, 0); }
  35%      { box-shadow: 0 0 0 5px rgba(58, 143, 90, 0.45); }
}

.card.highlight { animation: card-pulse 1.1s ease-out 2; }

/* ---- 做客标识（改进项 C）----
   进别人主场时，"我在做客"这件事要一直可见，否则玩家打完都不知道刚才在哪打的。 */
.venue-chip {
  flex: 0 0 auto;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  font-size: 11px;
  border: 1px solid #2c4468;
  background: #16233a;
  color: var(--ink-dim);
  white-space: nowrap;
}

.venue-chip.away {
  border-color: var(--other);
  background: rgba(232, 184, 75, 0.14);
  color: #f0d090;
}

/* 客场"球衣"：画布边框换色，一眼看出这不是自己的地盘 */
body.away-game #world-canvas {
  border-color: var(--other);
  box-shadow: 0 0 0 1px rgba(232, 184, 75, 0.35);
}

/* 顶栏阶段指示：能跳的地方给手型（改进项 D） */
.step.actionable { cursor: pointer; }
.step.actionable:hover {
  color: var(--ink);
  border-color: #2c4468;
  background: #16233a;
}

/* ---- 天梯：段位与配额 ---- */
.ladder-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 10px 0 6px;
  padding: 8px 12px;
  border-radius: var(--r-md);
  background: rgba(232, 184, 75, 0.08);
  border: 1px solid rgba(232, 184, 75, 0.25);
}
.tier-rating {
  font-size: 26px;
  font-weight: 700;
  color: var(--other);
  margin-right: 8px;
}
.tier-sub { font-size: 11px; color: var(--ink-dim); }
.ladder-quota { font-size: 11.5px; color: var(--ink-dim); white-space: nowrap; }
.ladder-quota.exhausted { color: #d98a8a; }
.ranked-rules { margin-bottom: 10px; }

/* ---- 钱包 ---- */
.wallet-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid rgba(232, 184, 75, 0.35);
  background: rgba(232, 184, 75, 0.12);
  color: var(--other);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.wallet-chip:hover { background: rgba(232, 184, 75, 0.2); }
.wallet-icon { font-size: 11px; }

/* 余额变动时闪一下：玩家刚打完一局回到界面，钱是悄悄变的。 */
@keyframes wallet-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.18); background: rgba(232, 184, 75, 0.4); }
  100% { transform: scale(1); }
}
.wallet-chip.bumped { animation: wallet-pop 520ms ease-out; }

/* ---- 锁定部位选择 ---- */
.modal-extra { display: grid; gap: 6px; margin: 4px 0 12px; }
.modal-extra:empty { display: none; }
.lock-row {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: baseline;
  gap: 8px;
  padding: 7px 10px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
  font-size: 12px;
}
.lock-row:hover { background: rgba(255, 255, 255, 0.07); }
.lock-row:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }
.lock-part { color: var(--ink-dim); }
.lock-affix { color: var(--other); font-weight: 600; }
.lock-desc { grid-column: 1 / -1; color: var(--ink-dim); font-size: 11px; }

/* ---- 珍珠流水 ---- */
.ledger { display: grid; gap: 4px; max-height: 300px; overflow-y: auto; }
.ledger-row {
  display: grid;
  grid-template-columns: 52px 1fr auto;
  gap: 8px;
  align-items: baseline;
  font-size: 12px;
  padding: 4px 6px;
  border-radius: var(--r-xs);
  background: rgba(255, 255, 255, 0.03);
}
.ledger-amt { font-weight: 700; text-align: right; }
.ledger-amt.plus { color: #7fd4a0; }
.ledger-amt.minus { color: #d98a8a; }
.ledger-reason { color: #cfe0f5; }
.ledger-time { color: var(--ink-dim); font-size: 10.5px; }

/* ---- 布景编辑器 ---- */
.layout-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin: 10px 0 6px;
}
.deco-palette { display: flex; gap: 6px; flex-wrap: wrap; }
.deco-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* flex 项默认可以被压到内容宽度以下。窄屏上五个标签挤在 282px 里就会
     被压扁，"水草丛"断成"水草 / 丛"——名字断在词中间比换一行难读得多。
     容器本来就 flex-wrap，禁止收缩之后它们会老老实实换行。 */
  flex: 0 0 auto;
  white-space: nowrap;
  padding: 5px 9px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.04);
  font-size: 11.5px;
  cursor: pointer;
}
.deco-chip:hover:not(:disabled) { background: rgba(255, 255, 255, 0.09); }
.deco-chip.selected {
  border-color: var(--other);
  background: rgba(232, 184, 75, 0.16);
}
.deco-chip:disabled { opacity: 0.35; cursor: not-allowed; }
.deco-swatch { width: 10px; height: 10px; border-radius: var(--r-xs); }
.deco-cost { color: var(--ink-dim); font-size: 10.5px; }

.layout-budget { font-size: 11.5px; color: var(--ink-dim); min-width: 110px; }
.budget-bar {
  height: 4px;
  margin-top: 4px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.1);
  overflow: hidden;
}
.budget-fill {
  height: 100%;
  width: 0;
  background: var(--other);
  transition: width 160ms ease-out;
}

.layout-tip { margin: 2px 0 8px; }
.layout-canvas {
  display: block;
  width: 100%;
  max-width: 640px;
  height: auto;
  /* 实际比例由 LayoutEditor 按世界尺寸设成 inline style；
     这里只是个占位，免得图片加载前布局跳动。 */
  aspect-ratio: 3 / 2;
  border-radius: var(--r-md);
  border: 1px solid rgba(255, 255, 255, 0.12);
  cursor: crosshair;
  touch-action: none; /* 指针拖拽改朝向时别让页面跟着滚 */
}

/* 血脉印记：鱼谱里一眼能认出哪条是血统鱼。 */
.codex-mark {
  font-size: 11px;
  color: #c79bd8;
}

/* ---- 图鉴卡片（词条 / 套装 / 血脉）----
   药丸形小标签装不下"这个词条到底干什么"，而那正是玩家最想知道的。
   卡片给描述留了地方，也让"没解锁"能表达得比一排问号更有信息量。 */
.set-codex,
.mark-codex,
.cg-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: 7px;
  margin-bottom: 4px;
}

/* 词条图鉴按槽位分组，所以它自己是一列组，网格在组里面（.cg-grid）。 */
.affix-codex {
  display: flex;
  flex-direction: column;
  gap: 11px;
  margin-bottom: 4px;
}
.cg-grid { margin-bottom: 0; }

/* 组头：槽位名 + 进度条 + "见过几个/共几个"。
   图鉴唯一真正要回答的问题是"还差几个"，所以那个数字必须在。 */
.cg-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 5px;
  font-size: 11px;
  color: var(--ink-faint);
}
.cg-title {
  color: var(--ink-dim);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.cg-bar {
  flex: 1;
  height: 3px;
  background: rgba(255, 255, 255, 0.07);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.cg-bar > i {
  display: block;
  height: 100%;
  background: var(--ink-faint);
  border-radius: var(--r-pill);
  /* 令牌名要拼对：`--motion-slow` 是不存在的，**一个未定义的自定义属性会让
     整条声明在解析时被丢弃**，没有任何报错——这条进度条因此从来没动过
     （实测 transition-duration 是 0s）。而且运行时审计也查不出来：一条被
     丢弃的声明根本进不了 CSSOM，只有读源码才看得见。 */
  transition: width var(--t-slow) var(--ease-out);
}
.codex-group.complete .cg-bar > i,
.codex-group.complete .cg-count { color: var(--good); background: var(--good); }
.codex-group.complete .cg-count { background: none; }
.cg-count {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}

.codex-card {
  padding: 8px 10px;
  border-radius: var(--r-sm);
  border: 1px solid #1f3350;
  background: #0c1626;
  /* 没解锁的整体压暗，但**不隐藏结构**——玩家能看出"还有一个头部词条
     没见过"，比一排整齐的 ??? 有用得多。 */
  opacity: 0.5;
  min-width: 0;
}
.codex-card.unlocked { opacity: 1; }

.cc-top {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 3px;
}
.cc-part {
  flex: 0 0 auto;
  font-size: 10px;
  padding: 1px 5px;
  border-radius: var(--r-xs);
  background: #16263d;
  color: #7f93ad;
}
.cc-icon { font-size: 13px; flex: 0 0 auto; }
.cc-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cc-owned {
  margin-left: auto;
  flex: 0 0 auto;
  font-size: 10px;
  color: #7fd4a0;
}
.cc-desc { font-size: 11px; color: var(--ink-dim); line-height: 1.45; }
.cc-pieces {
  font-size: 10.5px;
  color: #6f83a0;
  margin-bottom: 3px;
}
.cc-from {
  font-size: 10.5px;
  color: #c79bd8;
  margin-bottom: 3px;
}

/* 档位用左边一道色条表示，比整卡染色克制。 */
/* 图鉴里的词条卡（.cc-affix）。**不叫 .affix-card**——那个名字是占卜屏
   三张翻卡的，撞了会把图鉴卡压成 78px 高的小方块。 */
.cc-affix { border-left: 3px solid #3a4a63; }
.cc-affix.positive.unlocked { border-left-color: #4a9e7a; }
.cc-affix.tradeoff.unlocked { border-left-color: #c9a33c; }
.cc-affix.negative.unlocked { border-left-color: #b05c5c; }
.set-card.unlocked { border-left: 3px solid var(--other); }
.mark-card { border-left: 3px solid #6b4f8a; }
.mark-card.unlocked { border-left-color: #c79bd8; }

/* ---- 血统横幅：印记是这次占卜最重要的信息，给它结构 ---- */
.lineage-banner .banner-mark {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: #c79bd8;
  margin-bottom: 2px;
}
.lineage-banner .banner-from,
.lineage-banner .banner-effect {
  display: block;
  font-size: 11.5px;
  color: #a9bcd6;
}
.lineage-banner .banner-locked {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  color: var(--other);
}

/* ---- 天梯战绩 ---- */
.ladder-tabs { display: flex; gap: 5px; margin: 6px 0 8px; }
.lh-tab {
  padding: 4px 11px;
  font-size: 11.5px;
  border-radius: var(--r-pill);
  background: #0d1a29;
  border: 1px solid #22344f;
  color: var(--ink-dim);
  cursor: pointer;
}
.lh-tab:hover { color: #d6e2f0; border-color: #33507a; }
.lh-tab.active { background: rgba(232, 184, 75, 0.14); border-color: var(--other); color: var(--other); }

.ladder-history { display: grid; gap: 5px; max-height: 320px; overflow-y: auto; overflow-x: hidden; }

.lh-row {
  display: grid;
  grid-template-columns: 52px 26px minmax(0, 1fr) 20px minmax(0, 1.3fr) 44px auto;
  align-items: baseline;
  gap: 7px;
  padding: 6px 9px;
  border-radius: var(--r-sm);
  font-size: 11.5px;
  background: #0c1626;
  /* 胜负用左边一道色条，比整行染色克制——一眼扫下去能看出连胜连败。 */
  border-left: 3px solid #3a4a63;
}
.lh-row.win { border-left-color: #4a9e7a; }
.lh-row.loss { border-left-color: #b05c5c; }
.lh-row.draw { border-left-color: #7f8a99; }

.lh-side { color: var(--ink-dim); font-size: 10.5px; }
.lh-result { font-weight: 700; }
.lh-row.win .lh-result { color: #7fd4a0; }
.lh-row.loss .lh-result { color: #d98a8a; }
.lh-row.draw .lh-result { color: #a9bcd6; }
.lh-fish, .lh-foe {
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lh-vs { color: var(--ink-faint); }
.lh-delta { font-family: "SFMono-Regular", Consolas, monospace; font-weight: 700; text-align: right; }
.lh-delta.plus { color: #7fd4a0; }
.lh-delta.minus { color: #d98a8a; }
.lh-time { color: var(--ink-faint); font-size: 10px; white-space: nowrap; }

/* ---- 各屏的卡片继承系统色 ----
   标题左边一道短竖线，用当前屏的色相。这是"颜色一路跟进来"的最后一环：
   枢纽卡 → 面包屑 → 屏内标题，三处同色，身份才立得住。 */
.screen-body .card > h2 {
  position: relative;
  padding-left: var(--sp-3);
}
.screen-body .card > h2::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.15em;
  bottom: 0.15em;
  width: 3px;
  border-radius: var(--r-pill);
  background: var(--sys, var(--ink-faint));
}

body[data-phase="divination"] .screen-body { --sys: var(--sys-divination); }
body[data-phase="codex"]      .screen-body { --sys: var(--sys-codex); }
body[data-phase="ladder"]     .screen-body { --sys: var(--sys-ladder); }
body[data-phase="tank"]       .screen-body { --sys: var(--sys-tank); }
body[data-phase="rooms"]      .screen-body { --sys: var(--sys-rooms); }
body[data-phase="reports"]    .screen-body { --sys: var(--sys-reports); }

/* ---- 空状态 ----
   新玩家在这些屏上待的时间最长，而它们恰恰是最空的。一句灰字既不好看，
   也没告诉他**下一步该干什么**——空状态是引导的机会，不是占位符。

   三件事：一个图标（让它不像出错）、一句说明、一个出口。 */
.empty-state {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  color: var(--ink-dim);
  border: 1px dashed color-mix(in srgb, var(--sys, var(--line-bright)) 30%, transparent);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.015);
}

.es-icon {
  font-size: 26px;
  opacity: 0.6;
  /* 极慢地上下浮一点。静止的空状态看着像坏了，浮起来就像"在等你"。 */
  animation: es-drift 4.5s ease-in-out infinite;
}
@keyframes es-drift {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

.es-text { font-size: var(--fs-base); line-height: 1.6; max-width: 34em; }
.es-hint { font-size: var(--fs-sm); color: var(--ink-faint); }

.es-cta {
  flex: 0 0 auto;
  margin-top: var(--sp-1);
  padding: 6px 16px;
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--sys, var(--accent)) 45%, transparent);
  color: var(--sys, var(--accent-bright));
}
.es-cta:hover {
  background: color-mix(in srgb, var(--sys, var(--accent)) 14%, transparent);
  border-color: var(--sys, var(--accent));
}

@media (prefers-reduced-motion: reduce) {
  .es-icon { animation: none; }
}

/* ---- 加载骨架 ----
   本地几毫秒就回来了，真实网络不是。没有骨架时玩家进屏看到的是上一次的
   内容或一片空白，然后突然被替换掉——**"正在取"这件事本身必须可见**。

   画成几条灰行而不是转圈：灰行预告了内容的形状，玩家在数据到之前就知道
   这里将来会是一个列表。 */
.skeleton { display: grid; gap: var(--sp-2); }

.sk-row {
  height: 46px;
  border-radius: var(--r-sm);
  background: linear-gradient(
    100deg,
    rgba(255, 255, 255, 0.035) 20%,
    rgba(255, 255, 255, 0.085) 42%,
    rgba(255, 255, 255, 0.035) 64%
  );
  background-size: 260% 100%;
  animation: sk-sweep 1.25s ease-in-out infinite;
}
/* 后面的行稍慢一点起步，扫光就不会像一整块在闪。 */
.sk-row:nth-child(2) { animation-delay: 0.1s; }
.sk-row:nth-child(3) { animation-delay: 0.2s; }
.sk-row:nth-child(4) { animation-delay: 0.3s; }

@keyframes sk-sweep {
  from { background-position: 130% 0; }
  to { background-position: -30% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sk-row { animation: none; }
}

/* "右边是它此刻的样子"这句话，只在预览缸真的在右边的时候才成立——否则它
   是在指一个不存在的东西，玩家会去找，然后以为界面坏了。

   条件系在**预览缸自己**（#divination-grid）上，不是系在"开场面板是不是
   收起来了"。上一版就是后者，结果只挡住了开场屏——**准备屏（勾选锁定/
   看先祖血脉）同样没有预览缸，那句话又冒出来了**。
   描述某个东西的文案，可见性就该跟着那个东西走，不是跟着它的某个兄弟。 */
.diviner-card:has(#divination-grid[hidden]) .hint-when-preview {
  display: none;
}


/* ---- 最终排名（结算屏专用，和局内那块浮层无关）----
   局内那块要小、要半透、要贴边不挡视线；这块相反：满宽、不透明、
   可以慢慢读。同一份数据在两个场合的要求是相反的，所以不该是同一个组件。 */
.report-standings {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.st-row {
  display: grid;
  /* 名次 / 名字 /「你」/ 体型 / 击杀 / 食物
     「你」这一列是**固定宽**、而且每行都存在（空的也占位）。用 auto 的话
     只有我那行有内容，那一行的列宽就和别人不一样——每个 .st-row 是独立的
     grid，列不会跨行对齐，实测整行数字歪出去 31px。 */
  grid-template-columns: 2.2em minmax(0, 1fr) 1.9em 4.2em 3.6em 3.6em;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px var(--sp-3);
  border-radius: var(--r-sm);
  border-left: 2px solid transparent;
  font-size: var(--fs-sm);
  color: var(--ink-dim);
}
.st-row:nth-child(even) { background: rgba(255, 255, 255, 0.022); }

.st-rank {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--ink-faint);
}
.st-row.gold   .st-rank { color: #e8c45b; }
.st-row.silver .st-rank { color: #c6d2df; }
.st-row.bronze .st-rank { color: #cc9a6a; }

.st-name {
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 自己那一行**永远看得见**，哪怕排到第八——结算屏第一个要回答的问题就是
   "我在哪"。 */
.st-row.is-me {
  background: rgba(255, 102, 204, 0.12);
  border-left-color: var(--accent);
}
.st-row.is-me .st-name { font-weight: 600; }
.st-you {
  font-size: var(--fs-xs);
  text-align: center;
  border-radius: var(--r-pill);
  font-weight: 700;
}
/* 空的那些不画药丸，只占位 */
.st-row.is-me .st-you {
  background: var(--accent);
  color: #2a0a1e;
}

.st-size, .st-kills, .st-food {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.st-size { color: var(--ink); }
.st-kills, .st-food { color: var(--ink-faint); font-size: var(--fs-xs); }

/* 窄屏改成 flex 换行：六列挤在 351px 里会把名字压没。
   用 flex 而不是继续排网格——网格要把三个数字塞进同一行得写
   grid-template-areas，而 flex-wrap 自己就会把放不下的挪到第二行。 */
@media (max-width: 560px) {
  .st-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    row-gap: 2px;
  }
  .st-rank { flex: 0 0 auto; min-width: 1.6em; }
  .st-name { flex: 1 1 auto; }
  .st-size, .st-kills, .st-food { text-align: left; }
}


/* ===================================================================
   宽屏：让放网格和画布的那两屏用掉横向空间
   ===================================================================
   `.screen-body` 的 880px 是有意的**阅读列宽**——正文一行太长就难读，
   所以大多数屏不该动它。但鱼谱和鱼缸这两屏放的是网格和画布，不是长文：
   在 1440×800 上它们分别高 1051px 和 1077px，**超出视口 251 / 277px，
   要滚动才能看完**，而右边有 500 多像素一直空着。

   所以按屏放宽 + 分栏，而不是全站放宽。 */

/* ---- 鱼谱 ---- */
@media (min-width: 1100px) {
  .view[data-view="codex"] .screen-body { max-width: 1240px; }

  .codex-cols {
    display: grid;
    /* 词条那栏更宽：它有三组、每组一行卡片，是这一屏最占地方的东西 */
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: var(--sp-5);
    align-items: start;
  }
  /* 分栏后每栏的第一个标题不要再拉开距离，两栏的顶要齐 */
  .codex-col > h3:first-child { margin-top: 0; }
}

/* ---- 我的鱼缸 ---- */
@media (min-width: 1100px) {
  .view[data-view="tank"] .screen-body {
    max-width: 1240px;
    display: grid;
    /* 左边是表单（窄就够），右边是布景编辑器（画布要宽） */
    grid-template-columns: minmax(300px, 0.72fr) minmax(0, 1.6fr);
    align-items: start;
  }
  /* 今日水域横幅横跨两栏——它是这一屏的前提，不属于任何一栏 */
  .view[data-view="tank"] .screen-body > .daily-slot { grid-column: 1 / -1; }
}


/* ---- 战绩与对手 ----
   两张卡问的是两个不同的问题（我的鱼遇到了什么 / 我下一场跟谁打），
   宽屏上并排，不用滚。 */
@media (min-width: 1100px) {
  .view[data-view="reports"] .screen-body {
    max-width: 1240px;
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    align-items: start;
  }
  .view[data-view="reports"] .screen-body > .daily-slot { grid-column: 1 / -1; }
}

/* 天梯战绩的"全部展开"。做成一行贯通的按钮而不是右下角的小链接：
   它是这份列表的延续，不是另一个动作。 */
.lh-more {
  display: block;
  width: 100%;
  margin-top: var(--sp-2);
  padding: var(--sp-2);
  border: 1px dashed rgba(120, 170, 210, 0.22);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  cursor: pointer;
}
.lh-more:hover {
  border-color: rgba(120, 170, 210, 0.45);
  color: var(--ink);
}
