/* ==========================================================================
   布局与组件
   断点：S ≤480 / M 481-767 / L 768-1279 / XL ≥1280（移动优先，min-width 增量覆盖）
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg-0);
  color: var(--text-1);
  font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
               "Microsoft YaHei", "Source Han Sans SC", sans-serif;
  font-size: var(--fs-md);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

body {
  /* 深色主题下的微弱"魔法尘埃"底纹，纯 CSS，零图片 */
  background-image:
    radial-gradient(1200px 600px at 50% -10%, rgba(139, 107, 255, 0.12), transparent 60%),
    radial-gradient(900px 500px at 90% 110%, rgba(245, 178, 60, 0.08), transparent 60%);
  background-attachment: fixed;
}
:root[data-theme="light"] body { background-image: none; }

button { font: inherit; color: inherit; }
img { display: block; max-width: 100%; }

/* ---------------------------------------------------------------- 骨架布局 */

#app {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  padding-bottom: env(safe-area-inset-bottom);
}

#hud {
  flex: 0 0 auto;
  position: sticky;
  top: 0;
  z-index: 30;
  padding-top: env(safe-area-inset-top);
  background: linear-gradient(180deg, var(--bg-1), color-mix(in srgb, var(--bg-1) 88%, transparent));
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(8px);
}

#view {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

#nav {
  flex: 0 0 auto;
  position: sticky;
  bottom: 0;
  z-index: 30;
  border-top: 1px solid var(--line);
  background: var(--bg-1);
}

/* ---------------------------------------------------------------- HUD */

.hud-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;          /* 需求 7：窄屏时货币网格换到标题下一行，不横向溢出 */
  gap: 6px;
  padding: 6px 8px;
  min-height: var(--hud-h);
}

.hud-title {
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--gold-1);
  letter-spacing: 0.08em;
  white-space: nowrap;
  margin-right: auto;
}

.res {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  min-width: 0;
}

.res-icon {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  border-radius: 50%;
}
.res-gold .res-icon { background: radial-gradient(circle at 35% 30%, #FFE9B0, var(--res-gold)); }
.res-crystal .res-icon { background: radial-gradient(circle at 35% 30%, #D9CCFF, var(--res-crystal)); }
.res-xp .res-icon { background: radial-gradient(circle at 35% 30%, #BEF0FF, var(--res-xp)); }
.res-build .res-icon { background: radial-gradient(circle at 35% 30%, #FFD2C7, var(--res-build)); }
.res-pet .res-icon { background: radial-gradient(circle at 35% 30%, #D3F7CF, var(--res-pet)); }
.res-skin .res-icon { background: radial-gradient(circle at 35% 30%, #FBD4F3, var(--res-skin)); }
/* 需求 7：中文名直接可见（不只是 title），用短名；数字优先保证可读 */
.res-name { color: var(--text-3); flex: 0 0 auto; }
.res-val { overflow: hidden; text-overflow: ellipsis; margin-left: auto; font-weight: 600; }

/* 需求 7：6 项货币带中文名，一行塞不下 —— 改成 3 列 × 2 行的整齐网格。
   窄屏（标题 + 网格放不下一行）时 flex-wrap 让网格独占一整行，宽度足以容纳 3 列；
   宽屏则与标题同行。绝不允许横向溢出，也不允许换行错乱（网格列宽严格相等）。 */
.hud-res {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
  margin-left: auto;
  flex: 1 1 300px;
  min-width: 0;
}

/* ③-2 HUD 上的高性能模式开关（与「更多」页的同名开关共用 settings.highPerf）。
   默认画质下**不占位**（display:none）——这是状态指示 + 一键退出入口，不是常驻按钮：
   常驻会把整个 HUD 行顶高一截，移动端视觉回归基线（mobile-golden 14 个组合）会全部飘红。
   只在高性能模式出现，既是"当前开着省电模式"的提示，也是顺手关掉它的入口。 */
/* 选择器必须带元素名：.btn 定义在文件更后面（266 行），同特异性下它后写会赢，
   display:none 会被 .btn 的 inline-flex 顶掉 —— 写成 button.hud-perf（0,1,1）才压得住。 */
button.hud-perf {
  display: none;
  flex: 0 0 auto;
  min-height: 26px;
  padding: 0 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--bg-2);
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.high-perf button.hud-perf { display: inline-flex; align-items: center; }
.hud-perf[aria-pressed="true"] {
  border-color: var(--gold-3);
  color: var(--gold-1);
}

/* 限时增益条（05 §1.10）：只在有增益时出现，不占位、不挤压货币行 */
.hud-buffs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}
.buff-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  color: var(--gold-1);
  background: rgba(255, 199, 61, 0.12);
  border: 1px solid rgba(255, 199, 61, 0.38);
}
.buff-chip b { font-variant-numeric: tabular-nums; font-weight: 700; }

/* ---------------------------------------------------------------- 导航 */

/* 6 个主标签（新增「装备」页，05 §1.3）：列数写死会漏掉新页签，
   这里用 auto-fit + 1fr，加页签不再需要同步改 CSS。 */
.nav-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  padding-bottom: env(safe-area-inset-bottom);
}

.nav-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: var(--nav-h);
  padding: 6px 2px;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--text-3);
  font-size: var(--fs-xs);
  transition: color var(--fx-dur) var(--fx-ease);
  position: relative;
  min-width: 0;
}
.nav-btn[aria-current="page"] { color: var(--gold-1); }
.nav-btn[aria-current="page"]::after {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 26px;
  height: 2px;
  background: var(--gold-2);
  border-radius: 0 0 3px 3px;
}
/* 内联 SVG 图标的统一笔触。本项目的图标（纹章 / 导航 / 关闭）全部是**描边式线稿**，
   而 SVG 里 <path>/<line>/<circle> 的默认值是 `fill: black` + `stroke: none` ——
   深色主题下就渲染成纯黑剪影，更糟的是 <line> 干脆不画（英雄详情右上角的 × 就是这么丢的）。
   这里按设计意图补回笔触；颜色一律用 currentColor，随按钮的激活/未激活态自动变化
   （.nav-btn 本身 color 在 text-3 与 gold-1 之间切，图标跟着走）。
   注意 .crest 自带白色笔触，其声明在本规则**之后**，纹章不受影响。 */
.nav-ico {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.nav-lbl { white-space: nowrap; }

.nav-dot {
  position: absolute;
  top: 6px;
  right: 50%;
  margin-right: -18px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--danger);
}

/* ---------------------------------------------------------------- 通用面板 */

.panel {
  padding: 12px;
  padding-bottom: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 100%;
}

.panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.panel-head h2 {
  margin: 0;
  font-size: var(--fs-lg);
  color: var(--text-1);
  white-space: nowrap;
}
.panel-head .spacer { margin-left: auto; }

.card {
  background: var(--bg-1);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  box-shadow: var(--shadow-card);
  min-width: 0;
}

.muted { color: var(--text-2); }
.dim { color: var(--text-3); }
.tiny { font-size: var(--fs-xs); }
.small { font-size: var(--fs-sm); }
.num { font-variant-numeric: tabular-nums; }
.center { text-align: center; }
.row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.row > .grow { flex: 1 1 auto; min-width: 0; }
.wrap { flex-wrap: wrap; }

/* ---------------------------------------------------------------- 按钮 */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--touch-min);
  padding: 0 16px;
  border-radius: var(--radius);
  border: 1px solid var(--line-strong);
  background: linear-gradient(180deg, var(--bg-3), var(--bg-2));
  color: var(--text-1);
  font-size: var(--fs-md);
  font-weight: 600;
  cursor: pointer;
  transition: transform var(--fx-dur) var(--fx-ease), filter var(--fx-dur) var(--fx-ease);
  white-space: nowrap;
}
.btn:active { transform: scale(0.97); }
.btn:focus-visible { outline: 2px solid var(--gold-1); outline-offset: 2px; }

.btn-primary {
  background: linear-gradient(180deg, var(--gold-1), var(--gold-2) 55%, var(--gold-3));
  border-color: var(--gold-3);
  color: var(--on-gold);
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.45);
}

.btn-arcane {
  background: linear-gradient(180deg, var(--arcane-1), var(--arcane-2) 55%, var(--arcane-3));
  border-color: var(--arcane-3);
  color: #FFF;
}

.btn-sm { min-height: 34px; padding: 0 10px; font-size: var(--fs-sm); }
.btn-block { width: 100%; }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.5);
  /* 禁用态原来是「金按钮 + 灰掉」= 中间调灰底配深/浅字，只有 2.2~2.8:1 一团糊。
     两主题各取自己的主文字色：深底浅字 / 浅底深字，靠 opacity 表达"不可用"。 */
  color: var(--text-1);
}

/* ---------------------------------------------------------------- 战场 */

.battlefield {
  position: relative;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(139, 107, 255, 0.14), transparent 62%),
    linear-gradient(180deg, var(--bg-2), var(--bg-1));
  padding: 7px 8px 6px;          /* 瘦身：收紧内边距（原 10px） */
  overflow: hidden;
  min-width: 0;
}

/* ---------------------------------------------------------------- 棋盘（5 列 × 2 行）
   单位位置由 col/row 写成 grid-column / grid-row（1-based），sim 与 UI 共用
   sim/board.js 的 assignBoard，不存在"两套摆法"。 */

.bf-grid {
  display: grid;
  grid-template-columns: repeat(var(--bf-cols, 5), 1fr);
  /* minmax(0,1fr)：纯 1fr 会被内容的 min-content 顶破（立绘把行撑到 80px+，
     战场总高失控）。行高必须死死锁在 定高/行数 上。 */
  grid-template-rows: repeat(var(--bf-rows, 2), minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  gap: 3px;
  height: 112px;                   /* 瘦身：战场总高的主要来源，直接给网格定高 */
  position: relative;
  min-width: 0;
  --lunge: -6px;                    /* 推进时的位移量，方向随 <root> 的 dir-* 翻转 */
}
.dir-left .bf-grid { --lunge: 6px; }

.bf-cell {
  min-height: 0;                    /* 去掉 aspect-ratio:1/1，行高改由网格轨道决定 */
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.028);
  border: 1px dashed rgba(255, 255, 255, 0.06);
  min-width: 0;
}

/* 推进反馈：220ms 的一次"顶进"。grid-column 是不可动画的离散属性，
   所以位移是瞬时的，这里用一次整体轻推把"我迈了一格"读出来。 */
.bf-grid.advance { animation: bfAdvance 220ms var(--fx-ease); }
@keyframes bfAdvance {
  0%   { transform: translateX(0); }
  45%  { transform: translateX(var(--lunge)); }
  100% { transform: translateX(0); }
}

.unit {
  position: relative;
  width: 58px;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
/* 棋盘内的单位跟格子同宽，天然自适应 360px（58px 立绘在 5 列下会挤爆网格） */
.bf-grid .unit {
  width: 100%;
  max-width: 52px;                  /* 瘦身：略收窄，立绘更「竖」 */
  height: 100%;                    /* 占满所在格高，立绘才能「立」起来 */
  align-self: center;
  justify-self: center;
  z-index: 2;
}
/* 名字与血条改为叠在格底：把整格高度都让给立绘（纵向站立），
   二者压在立绘与宠物之上，保证可读。
   二者是上下两条独立的带：名字带贴底（行盒收紧到 1.15），
   血条带整体抬到名字带上方，中间留净空，互不侵入。 */
.bf-grid .unit-name {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  z-index: 3;
  line-height: 1.15;               /* 收紧行盒：默认行高会让 9px 字顶到 13.5px，顶进血条带 */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
}
.bf-grid .unit-hp {
  position: absolute;
  left: 1px;
  right: 1px;
  bottom: 13px;                    /* 血条底边抬到名字行盒（约 10.4px）之上，留 ~2.6px 净空 */
  z-index: 3;
}

.unit-art {
  width: 100%;
  flex: 1 1 auto;                   /* 占满可用高度：英雄纵向站立、贴格底 */
  min-height: 0;
  object-fit: contain;             /* 竖构图，不裁切 */
  object-position: bottom;         /* 贴格底 */
  filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.55));
  transition: transform var(--fx-dur) var(--fx-ease);
  /* 翻转与放大都走变量，避免和下面的 .acting 抢 transform */
  --mirror: 1;
  --art-scale: 1;
  transform: scaleX(var(--mirror)) scale(var(--art-scale));
  position: relative;              /* 宠物节点 .unit-pet 要把 z-index 压在立绘之下 */
  z-index: 2;
}
/* 敌方立绘水平翻转。只翻 .unit-art：翻整个 .unit 会把单位名也照成镜像，读不了。
   方向由 <battlefield> 的 dir-* 类决定，CSS 里不出现任何写死的方向。
   源图统一面朝左：dir-right（我方在左往右打）时敌方本来就朝左，不翻；
   dir-left（我方在右往左打）时敌方在左应朝右，才需要镜像。 */
.dir-left  .unit[data-side="enemy"] .unit-art { --mirror: -1; }
.unit[data-boss="1"] .unit-art { --art-scale: 1.25; }
/* 立绘缺失时的占位：用阵营纹章顶上，避免深色背景上出现一片空白 */
.unit-art-none {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px dashed var(--line-strong);
}
.unit-art-none svg { opacity: 0.7; }

/* 出战宠物：站在目标英雄前面（朝敌侧）。是 .unit 的子节点，贴右、贴底，
   宽度约英雄的 45%，z-index 低于英雄立绘，不拦截点击。方向随 <root> 的 dir-* 翻转。 */
.unit-pet {
  position: absolute;
  right: -16%;
  bottom: 16px;                    /* 抬到名字/血条叠层之上，与英雄并肩贴底 */
  width: 46%;
  height: 52%;                     /* 定高盒子：加载前后不跳变，contain 保比例贴底 */
  object-fit: contain;
  object-position: bottom;
  z-index: 1;                       /* 低于 .unit-art（z-index:2），重叠时英雄压住宠物 */
  pointer-events: none;            /* 纯表现，不挡单位点击 */
  opacity: 0.95;
  border-radius: 999px;            /* 半透明底 + 圆角 + 阴影 */
  background: rgba(0, 0, 0, 0.28);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.12);
}
/* dir-left 下我方朝左，宠物改贴左侧（朝敌侧），不写死方向 */
.dir-left .unit-pet { right: auto; left: -16%; }

/* 出手抬手：带上 mirror 与 art-scale，否则会盖掉上面的翻转/放大 */
.unit.acting .unit-art,
.dir-right .unit[data-side="enemy"].acting .unit-art,
.dir-left  .unit[data-side="enemy"].acting .unit-art {
  transform: translateY(-5px) scale(var(--art-scale)) scaleX(var(--mirror));
}
.unit.dead .unit-art { opacity: 0.25; filter: grayscale(1); }
/* 纹理图集精灵（#6）：承载元素用 background 子矩形显出本格。
   源图 512 正方，元素强制正方以复刻 <img> 的 object-fit:contain。 */
.sprite { background-repeat: no-repeat; }
/* 战斗立绘：原本是 <img> 占满格高 + object-fit:contain(bottom)。
   改为正方承载元素、贴格底，与 object-fit:contain 视觉等价。 */
.sprite.unit-art { flex: 0 0 auto; align-self: flex-end; width: 100%; aspect-ratio: 1 / 1; }
/* 出战宠物：原为 46%×52% 盒子，contain 即正方 46%，强制正方避免背景被拉伸。 */
.sprite.unit-pet { height: auto; aspect-ratio: 1 / 1; }
.unit-name {
  font-size: 9px;                   /* 瘦身：单位名可降到 9px，但不能压没 */
  color: var(--text-2);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* 名字压在图集精灵下缘（立绘不是纯色底，两侧 alpha 不一）。
     给字行加一条"反向"遮罩带：深底用黑带托亮字、浅底用白带压暗字，
     按 var(--unit-scrim) 主题反色，立绘本体一点不动。
     20% 处到满 alpha：字身（内容盒从 5px 起，占带高 ~31%）整段吃满遮罩；
     40% 会让字顶那一行只剩 0.58 黑，压不住浅灰立绘底(#B2B2B3)，实测只有 4.1:1。 */
  padding: 5px 4px 1px;
  background: linear-gradient(180deg, transparent, var(--unit-scrim) 20%);
}
.unit-hp {
  width: 100%;
  height: 4px;                     /* 瘦身：血条略收窄，仍可读 */
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.5);
  overflow: hidden;
  border: 1px solid rgba(0, 0, 0, 0.4);
}
.unit-hp > i {
  display: block;
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--success), #7FE0AC);
  transition: width var(--fx-dur) linear;
}
.unit-hp.enemy > i { background: linear-gradient(90deg, var(--danger), #FF8A8E); }
.unit-shield {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  box-shadow: 0 0 0 2px rgba(76, 154, 255, 0.5) inset;
  pointer-events: none;
}

.unit-faction {
  position: absolute;
  top: 0;
  left: 0;
  width: 12px;
  height: 12px;
  opacity: 0.9;
}

/* 飘字 */
.float-layer { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.float {
  position: absolute;
  font-weight: 700;
  font-size: var(--fs-sm);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  animation: floatUp 900ms var(--fx-ease) forwards;
  white-space: nowrap;
}
.float.dmg { color: var(--float-dmg); }
.float.crit { color: var(--gold-1); font-size: var(--fs-md); }
.float.strong { color: var(--success); }
.float.weak { color: var(--text-2); }
.float.heal { color: var(--float-heal); }

@keyframes floatUp {
  0% { opacity: 0; transform: translateY(6px) scale(0.9); }
  18% { opacity: 1; transform: translateY(0) scale(1.05); }
  100% { opacity: 0; transform: translateY(-34px) scale(1); }
}

/* 战斗控制条（05 §1.9）：暂停 / 快进。始终一行，窄屏才换行 */
.bf-ctrl {
  z-index: 2;                      /* 压在飘字层之上，否则飘字会挡住按钮 */
  margin-top: 4px;                 /* 收紧控制条与战场间的间距 */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}
/* 战场内的按钮用紧凑尺寸（只作用于战场，不影响全局 .btn-sm） */
.bf-ctrl .btn-sm { min-height: 26px; padding: 3px 10px; }

/* 战斗明细日志（第9项 · 方案 B）：战场左、日志右栏常驻；窄屏回退成收起开关 */
.bf-field-wrap {
  display: flex;
  gap: 10px;
  align-items: stretch;
  width: 100%;
}
.bf-field-wrap .battlefield {
  flex: 1 1 auto;
  min-width: 0;                 /* 防止战场内容把双栏撑破 */
  position: relative;           /* 飘字层绝对定位的参照（保持） */
}
.bf-log.right {
  flex: 0 0 270px;
  width: 270px;
  height: 220px;               /* 展开态固定高度：跨场恒定、不被日志行数撑变（修复③）。父子均靠它对齐，战场随之等高 */
  max-height: 430px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  margin-top: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-1);
  overflow: hidden;
}
/* 收起：只隐藏明细列表，标题栏与按钮保留 —— 否则按钮随面板一起消失，玩家收起后无处再开 */
.bf-log.right:not(.is-open) {
  flex: 0 0 40px;
  width: 40px;
  max-height: none;
}
.bf-log.right:not(.is-open) .bf-log-list { display: none; }
.bf-log.right:not(.is-open) .bf-log-title { display: none; }
.bf-log.right:not(.is-open) .bf-log-head { justify-content: center; padding: 6px 2px; }
.bf-log.right:not(.is-open) .bf-log-toggle {
  writing-mode: vertical-rl;
  letter-spacing: 2px;
  min-height: 0;
  padding: 8px 3px;
}
.bf-log-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 4px 8px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
  flex: 0 0 auto;
}
.bf-log-title { font-size: var(--fs-sm); font-weight: 700; color: var(--text-1); }
.bf-log-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 24px;
  padding: 2px 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  color: var(--text-1);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bf-log-toggle:hover { background: var(--bg-3); border-color: var(--gold-2); }
.bf-log-toggle:focus-visible { outline: 2px solid var(--gold-1); outline-offset: 2px; }

.bf-log-list {
  display: block;
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 6px 8px;
  font-size: var(--fs-xs);
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
}
.bf-log-line { white-space: nowrap; }
.lg-dmg { color: var(--text-2); }
.lg-crit { color: var(--gold-1); font-weight: 700; }
.lg-strong { color: var(--success); }
.lg-weak { color: var(--text-3); }
.lg-heal { color: var(--success); }
.lg-buff { color: var(--arcane-1); }
.lg-death { color: var(--danger); }
.lg-win { color: var(--success); font-weight: 700; }
.lg-lose { color: var(--danger); font-weight: 700; }

/* 窄屏（手机）：双栏放不下，回退为上下布局 + 收起开关（默认收起不挤占加点面板） */
@media (max-width: 767px) {
  .bf-field-wrap { flex-direction: column; align-items: stretch; }
  /* 展开时给固定高度：日志行数变化不再改变面板尺寸 */
  .bf-log.right { flex: 0 0 auto; width: 100%; height: 190px; max-height: none; }
  .bf-log.right:not(.is-open) { height: auto; }
  .bf-log.right:not(.is-open) .bf-log-list { display: none; }
  .bf-log.right:not(.is-open) .bf-log-head { justify-content: space-between; padding: 4px 8px; }
  .bf-log.right:not(.is-open) .bf-log-title { display: block; }
  .bf-log.right:not(.is-open) .bf-log-toggle { writing-mode: horizontal-tb; letter-spacing: 0; min-height: 24px; padding: 2px 8px; }
}

/* 手动技能条（实时演算模式）：一个英雄一组按钮，冷却中置灰并显示剩余秒 */
.bf-cast {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 4px;
  min-width: 0;
}
.bf-cast-group {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 4px 5px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  min-width: 0;
}
.bf-cast-hero {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.btn-cast {
  padding: 4px 6px;
  font-size: var(--fs-xs);
  line-height: 1.25;
  text-align: center;
}
.btn-cast.is-cooling {
  opacity: 0.45;
}
.btn-cast:disabled {
  cursor: default;
}

/* 战斗状态 + 控制：**一行**（战斗中 · 第 N 关 / 自动闯关 / 自动技能 / 快进 / 暂停） */
.bf-status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;                 /* 窄屏（手机）允许折行，不横向溢出 */
  row-gap: 4px;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  font-size: var(--fs-sm);
  min-width: 0;
}
/* 状态条里的控制条不再单独占行：抹掉外边距，直接并进这一行 */
.bf-status .bf-ctrl {
  margin-top: 0;
  gap: 4px;
}
/* 这一行塞了 5 个东西，全程按最紧的弹药用：手机上也要压成一行，不横向溢出。
   关掉时（自动闯关/自动技能 变灰）宽度不变，避免一行在开关之间来回跳。 */
.bf-status { padding: 5px 8px; gap: 4px; }
.bf-status > .btn-sm,
.bf-status .bf-ctrl > .btn-sm { padding: 3px 6px; }
.bf-status .tag { padding: 1px 6px; }
/* 这一行里的按钮统一用战场内的紧凑尺寸：否则「自动闯关/自动技能」比旁边的
   「快进/暂停」高出 8px，一行里四种高度，看着像拼错的。 */
.bf-status .btn-sm {
  min-height: 26px;
  padding: 3px 8px;
}
.bf-status .tag {
  padding: 1px 7px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  border: 1px solid var(--line-strong);
  color: var(--text-2);
}
.bf-status.win { border-color: var(--success); }
.bf-status.lose { border-color: var(--danger); }
/* 需求 3：状态条只承载战斗结果。没结果时它是空节点，收起不占一行（避免凭空多出一条灰框）。 */
.bf-status:empty { display: none; }

/* ---------------------------------------------------------------- 英雄卡 */

.hero-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
  min-width: 0;
}

.hero-card {
  position: relative;
  border-radius: var(--radius);
  border: 1px solid var(--line-strong);
  background:
    linear-gradient(160deg, var(--bg-3), var(--bg-1) 62%);
  overflow: hidden;
  cursor: pointer;
  padding: 0;
  text-align: left;
  min-width: 0;
  transition: transform var(--fx-dur) var(--fx-ease);
}
.hero-card:active { transform: scale(0.97); }
.hero-card::before {
  /* 顶部冷高光：制造卡纸厚度感，成本最低的一招 */
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.18);
}
.hero-card::after {
  /* 底部压暗：藏拙，让文字压得住 */
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 46%;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.62));
  pointer-events: none;
}

.hero-card[data-rarity="r"]   { border-color: var(--rarity-r); }
.hero-card[data-rarity="sr"]  { border-color: var(--rarity-sr); }
.hero-card[data-rarity="ssr"] { border-color: var(--rarity-ssr); box-shadow: 0 0 0 1px rgba(168, 85, 247, 0.35), var(--shadow-card); }
.hero-card[data-rarity="ssr"][data-fx-on] .hero-art { filter: drop-shadow(0 0 8px rgba(168, 85, 247, 0.45)); }

.hero-art {
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
  padding: 4px;
}

.hero-meta {
  position: absolute;
  inset: auto 0 0 0;
  padding: 4px 5px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* 文字专用压暗带（只压这一条，不碰上面 54% 的立绘）。
     立绘两侧 alpha 不一（有的英雄浅、有的深），压 0.62 的底罩在字行上
     只剩 ~0.35，白字 / 金星压在浅色立绘本体上只有 2.9~3.5:1。
     这里再叠一条 0.62→0.88 的带子，字行落点必然 ≥0.8 黑，稳定过 4.5:1。 */
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.88));
}
.hero-name {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--on-dark-1);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hero-sub {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  color: var(--on-dark-2);
}

.crest { width: 11px; height: 11px; flex: 0 0 auto; }
.crest path, .crest circle, .crest line, .crest ellipse { fill: none; stroke: #FFF; stroke-width: 1.6; }

.role-dot {
  width: 7px;
  height: 7px;
  border-radius: 2px;
  flex: 0 0 auto;
}
.role-guard { background: var(--role-guard); }
.role-warrior { background: var(--role-warrior); }
.role-ranger { background: var(--role-ranger); }
.role-arcane { background: var(--role-arcane); }

.star-row {
  display: flex;
  gap: 1px;
  font-size: 9px;
  color: var(--gold-on-dark);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
}

.badge-lv {
  position: absolute;
  top: 4px;
  left: 4px;
  z-index: 2;
  padding: 1px 5px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.66);
  border: 1px solid var(--line-strong);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: var(--on-dark-1);
}
.badge-heal {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 2;
  width: 16px;
  height: 16px;
  color: var(--success);
}

/* 英雄卡「可加点」角标（2026-10-02 用户实测新增）：
   金币够 + 该英雄加点未到上限时才挂，帮玩家一眼看出谁还能提属性。
   左上 .badge-lv 是等级、战斗态 .badge-heal 占右上，
   所以这个角标压右下，不抢位置。 */
/* 需求 ④：已拥有但还没穿的皮肤。与「穿着中」的星芒角标互斥、共用右下角同一格；
   刻意的紫色方牌 + 「肤」字：形状与星芒完全不同，去色灰度下也分得开。 */
.hero-skin-own {
  position: absolute;
  right: 4px;
  bottom: 4px;
  z-index: 2;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.72);
  border: 1px solid var(--arcane-2);
  color: var(--arcane-1);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
}

.hero-up-badge {
  position: absolute;
  right: 4px;
  bottom: 4px;
  z-index: 2;
  padding: 1px 5px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.72);
  border: 1px solid var(--warning);
  font-size: 9px;
  font-weight: 600;
  color: var(--warning);
}

/* ---------------------------------------------------------------- 羁绊条 */

.bond-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid var(--line);
  font-size: var(--fs-sm);
  min-width: 0;
  flex-wrap: wrap;
}
/* 需求 3：战斗前置控件（自动闯关 / 自动技能 / 快进 / 暂停）从状态条搬到 bond-bar 最前，
   与羁绊 chip 同行。bond-ctrl 自身是一行可折行的小控件组，窄屏（手机）允许折行不横向溢出。 */
.bond-ctrl {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  min-width: 0;
}
/* 沿用战场内紧凑按钮尺寸，与旁边 chips 一行等高（否则「自动闯关/自动技能」比「快进/暂停」高出一截） */
.bond-ctrl .btn-sm { min-height: 26px; padding: 3px 6px; }
/* 把「快进/暂停」内部那条 .bf-ctrl 的 4px 顶距抹掉，否则在 bond-bar 里会下沉一行 */
.bond-ctrl .bf-ctrl { margin-top: 0; gap: 4px; }
/* bond-info 只作语义容器，让内部 chips 直接成为 bond-bar 的 flex 子项，保证与控件同行 */
.bond-info { display: contents; }

/* 需求 4：按住持续加点的按钮。触摸设备上必须关掉长按的默认行为
   （系统文本选中 / 长按弹出菜单 / 双击缩放），否则按住加点会被打断。 */
.btn-hold {
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* 需求 6：合并页子标签上的未读红点（如「背包」有新装备时） */
.sub-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 4px;
  border-radius: 50%;
  background: var(--danger);
  vertical-align: middle;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--bg-1);
  font-size: var(--fs-xs);
  white-space: nowrap;
}
.chip.on { border-color: var(--success); color: var(--success); }
.chip.counter-strong { border-color: var(--success); color: var(--success); }
.chip.counter-weak { border-color: var(--danger); color: var(--danger); }

/* 自动加点的属性选择：chip 当按钮使（胶囊外观沿用 .chip，只补交互态） */
button.chip {
  min-height: 28px;
  cursor: pointer;
  transition: transform var(--fx-dur) var(--fx-ease), filter var(--fx-dur) var(--fx-ease);
}
button.chip:active { transform: scale(0.97); }
button.chip:focus-visible { outline: 2px solid var(--gold-1); outline-offset: 2px; }
button.chip:disabled { opacity: 0.45; cursor: not-allowed; filter: grayscale(0.5); }

/* ---------------------------------------------------------------- 进度与数值 */

.bar {
  height: 8px;
  border-radius: 5px;
  background: var(--bg-3);
  overflow: hidden;
  border: 1px solid var(--line);
}
.bar > i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--arcane-2), var(--arcane-1));
}
.bar.hp > i { background: linear-gradient(90deg, var(--success), #7FE0AC); }

.kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 10px;
  font-size: var(--fs-sm);
}
.kv dt { color: var(--text-2); }
.kv dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }

.up { color: var(--success); }
.down { color: var(--danger); }

/* ---------------------------------------------------------------- 弹窗 */

.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--bg-scrim);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  animation: fadeIn 160ms var(--fx-ease);
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.modal {
  width: 100%;
  max-width: 560px;
  max-height: 88dvh;
  overflow-y: auto;
  background: var(--bg-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-pop);
  padding: 14px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
  animation: slideUp 220ms var(--fx-ease);
  min-width: 0;
}
@keyframes slideUp { from { transform: translateY(24px); opacity: 0; } to { transform: none; opacity: 1; } }

.modal-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.modal-head h3 { margin: 0; font-size: var(--fs-lg); }
.modal-close {
  margin-left: auto;
  width: var(--touch-min);
  height: var(--touch-min);
  min-height: var(--touch-min);
  padding: 0;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--bg-2);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  flex: 0 0 auto;
}
/* 关闭图标是两条 <line>：SVG 里 <line> 默认 stroke:none，不给笔触就整条不渲染，
   按钮只剩一个空方块（v0.7.5 修）。笔宽 1.8 比导航图标略粗，保证 44×44 触摸目标里看得清。 */
.modal-close svg {
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}

/* ---------------------------------------------------------------- Toast */

#toast-root {
  position: fixed;
  left: 50%;
  bottom: calc(var(--nav-h) + 16px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  pointer-events: none;
  width: max-content;
  max-width: 92vw;
}
.toast {
  padding: 8px 14px;
  border-radius: 999px;
  background: var(--bg-3);
  border: 1px solid var(--line-strong);
  box-shadow: var(--shadow-pop);
  font-size: var(--fs-sm);
  animation: toastIn 200ms var(--fx-ease);
  text-align: center;
}
.toast.ok { border-color: var(--success); }
.toast.warn { border-color: var(--warning); }
.toast.err { border-color: var(--danger); }
@keyframes toastIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------- 离线结算弹窗 */

.offline-big {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px;
  border-radius: var(--radius);
  background: linear-gradient(160deg, var(--bg-3), var(--bg-2));
  border: 1px solid var(--line-strong);
  text-align: center;
}
.offline-big .v { font-size: var(--fs-xl); font-weight: 700; color: var(--gold-1); }

/* ---------------------------------------------------------------- 招募 */

.pull-result {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
}
.pull-item {
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  overflow: hidden;
  background: var(--bg-2);
  animation: popIn 260ms var(--fx-ease) backwards;
}
@keyframes popIn { from { opacity: 0; transform: scale(0.82); } to { opacity: 1; transform: none; } }
.pull-item[data-rarity="SR"] { border-color: var(--rarity-sr); }
.pull-item[data-rarity="SSR"] { border-color: var(--rarity-ssr); box-shadow: 0 0 10px rgba(168, 85, 247, 0.4); }

/* ---------------------------------------------------------------- 详情抽屉 */

.detail-hero {
  display: flex;
  gap: 12px;
  min-width: 0;
}
.detail-hero > img {
  width: 96px;
  height: 96px;
  object-fit: contain;
  flex: 0 0 auto;
  border-radius: var(--radius);
  background: radial-gradient(60% 60% at 50% 40%, rgba(139, 107, 255, 0.2), transparent);
}

.slot-list { display: flex; flex-direction: column; gap: 6px; }
/* 任务 / 成就行：比 slot-row 高一点，因为下面还要挂一条进度条 */
.quest-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  border: 1px solid var(--line);
}
/* 宠物立绘缩略图（宠物面板每行左侧） */
.pet-thumb {
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: var(--bg-3);
  border: 1px solid var(--line);
}
.slot-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  border: 1px solid var(--line);
  min-width: 0;
}
.slot-row .grow { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* 装备品质：条目左侧色条 + 品质名着色。
   色值全部来自 tokens.css 的 --eq-*（六阶），JS 只挂 data-q=档位，
   同档装备一眼可辨，且浅色 / 高对比主题各自有可读值。
   注意：这里只动条目容器与文字，装备立绘 / 图片一个字节都不碰。 */
.eq-row { position: relative; padding-left: 10px; }
.eq-row::before {
  content: '';
  position: absolute;
  left: 0; top: 5px; bottom: 5px;
  width: 3px;
  border-radius: 2px;
  background: var(--eq, var(--line));
}
.eq-row[data-q="common"]    { --eq: var(--eq-common); }
.eq-row[data-q="uncommon"]  { --eq: var(--eq-uncommon); }
.eq-row[data-q="rare"]      { --eq: var(--eq-rare); }
.eq-row[data-q="epic"]      { --eq: var(--eq-epic); }
.eq-row[data-q="legendary"] { --eq: var(--eq-legendary); }
.eq-row[data-q="mythic"]    { --eq: var(--eq-mythic); }
.eq-qname { color: var(--eq, var(--text-2)); font-weight: 700; }

/* ---------------------------------------------------------------- 王国基地 */

/* 12×12 = 144 格领土网格（07 §3 / §11）。
   【为什么不用 1fr】360px 屏上 12 列 1fr 每格只有 25px，手指点不准、图形也放不下。
   改成固定单格尺寸 + 容器滑动：整块 12×38+11×3 ≈ 489px 宽，横向滑着看，
   纵向不再退化成 1815px 的长条，也不必为窄屏改列数（改列数就不是"一块地"了）。 */
.base-scroll {
  overflow: auto;
  max-height: min(62vh, 540px);
  margin: 8px 0 6px;
  padding: 2px;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.base-grid {
  /* 需求①-①「方框尽量填满」：过去是固定 12×38px（合计 489px）——
     在 390px 手机上必然要左右滑，在宽屏上又只居中一小块，两头都不"满"。
     改成 12 等分列 + 格子保持正方形（aspect-ratio:1，不用 grid-auto-rows 定高）：
     窄屏自动缩到放得下，宽屏被 max-width 卡在 489px 不再无脑放大。 */
  --base-gap: 3px;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--base-gap);
  width: 100%;
  max-width: calc(38px * 12 + var(--base-gap) * 11);
  margin: 0 auto;
}
.base-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0;
  /* 列宽是 1fr（见 .base-grid），高度靠 aspect-ratio 跟着走 —— 格子永远是正方形，
     同时不再需要 grid-auto-rows 那个固定值（它正是"填不满"的一半原因）。 */
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--line);
  background: var(--bg-2);
  cursor: pointer;
  overflow: hidden;
  min-width: 0;
  transition: transform var(--fx-dur) var(--fx-ease);
}
.base-cell:active { transform: scale(0.96); }
.base-cell .base-glyph { display: block; line-height: 0; }
/* 图形跟着格子缩放（不再钉 26px）：窄屏格子缩到 ~27px 时图标也不会溢出被裁，
   宽屏封顶 26px，与改动前的观感一致。 */
.base-grid .base-glyph { width: 84%; }
.base-grid .base-glyph svg { width: 100%; height: auto; max-width: 26px; max-height: 26px; }

/* 需求①-②：格内 X/Y 坐标刻度（1 基，「行,列」）。144 格下格内放不下正文，
   这行只当刻度用，绝对定位到左上角、不参与布局。 */
.base-coord {
  position: absolute;
  left: 1px;
  top: 0;
  z-index: 1;
  font-size: 7px;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--text-3);
  pointer-events: none;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85);
}

/* 需求①-③：敌袭横幅。挂在王国页最顶上（invasionBanner），紧贴下面的卡片 */
.base-alert { margin-bottom: 8px; }

/* —— 格子状态（07 §11）：144 格下格内没有文字，状态全靠边框 / 描边 / 角标 —— */
/* 部队所在格：正中的小圆点（在途时跟着当前步走） */
.base-cell.army::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--arcane-1);
  box-shadow: 0 0 0 2px var(--bg-1);
  pointer-events: none;
}
/* 当前正在走的那一格 */
.base-cell.step { outline: 2px solid var(--arcane-1); outline-offset: -2px; }
/* 终点格 */
.base-cell.target { outline: 2px dashed var(--danger); outline-offset: -3px; }
/* 玩家选中、正在下面看详情的那一格 */
.base-cell.sel { outline: 2px solid var(--success); outline-offset: -2px; }

/* 行动中区（07 §11）：进度条 + 倒计时，每秒只改数字不改结构 */
.base-action { display: flex; flex-direction: column; gap: 6px; }
.base-bar { min-height: 8px; }
.base-toggle { align-items: center; cursor: pointer; }
.base-toggle input { width: 16px; height: 16px; margin: 0; }
/* 目标面板：点格后显示"能干什么 + 要多久" */
.base-target {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 6px 0 8px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-1);
  border: 1px solid var(--line);
}
/* 144 格下格内已不放文字（07 §11），详情走 title 与点开后的目标面板 */
/* 未解锁：锁死感。斜纹比纯灰更能传达"还没到手" */
.base-cell.locked {
  border-style: dotted;
  border-color: var(--line);
  background:
    repeating-linear-gradient(45deg, transparent 0 5px, rgba(255, 255, 255, 0.03) 5px 10px),
    var(--bg-1);
  cursor: not-allowed;
}
.base-cell.empty { border-style: dashed; border-color: var(--line-strong); }
.base-cell.occupied {
  border-style: solid;
  border-color: var(--line-strong);
  background: linear-gradient(160deg, var(--bg-3), var(--bg-2) 70%);
}
/* 手上拿着建筑时，空格子亮起来，明确"现在点的是建造" */
.base-cell.pickable {
  border-color: var(--arcane-2);
  box-shadow: inset 0 0 0 1px rgba(139, 107, 255, 0.25);
}
/* NPC 据点：告警色实线，与自家建筑一眼分开。
   必须排在 .locked 之后 —— 据点大多还没解锁，否则灰斜纹会把告警色盖掉。 */
.base-cell.npc {
  border-style: solid;
  border-color: var(--warning);
  background: linear-gradient(160deg, rgba(255, 183, 77, 0.16), var(--bg-2) 70%);
}
/* 建造 / 升级中（受理即扣费，到点才落成） */
.base-cell.building {
  border-style: dashed;
  border-color: var(--arcane-2);
  background: linear-gradient(160deg, rgba(139, 107, 255, 0.14), var(--bg-2) 70%);
}
.base-stat {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  min-width: 62px;
}
.base-palette {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.base-pick {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  border: 1px solid var(--line);
}
.base-pick.on { border-color: var(--arcane-2); background: var(--bg-3); }
.base-raids { display: flex; flex-direction: column; gap: 6px; }
.base-raid {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  border: 1px solid var(--line);
}
/* 已占领（P0 战役化）：整卡转成成功色描边，与"打了一半"一眼分开。
   两种状态必须能在去色 / 高对比下也分得开，所以兼用右边框粗细 + chip 文案。 */
.base-raid.done {
  border-color: var(--success);
  border-left-width: 3px;
  background: linear-gradient(160deg, rgba(61, 214, 152, 0.12), var(--bg-2) 70%);
}
.chip.ok-chip { border-color: var(--success); color: var(--success); }
/* 战役进度条：波数进度比"打赢没打赢"更需要一眼看到，给它固定高度的细条 */
.base-waves { display: flex; flex-direction: column; gap: 3px; }
/* 资源去向（P1-B）：三笔账 + 还能撑几天，行内换行，窄屏也不塌成竖条 */
.base-uses { display: flex; flex-direction: column; gap: 3px; }
/* 王国科技（P2-C）三态：已解锁 / 可解锁 / 买不起 必须一眼分得开，
   去色（灰阶）主题下靠左边框与底色也能分，所以不只靠颜色。 */
.base-techs { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.base-tech {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-left: 3px solid var(--text-3);
}
.base-tech.ready { border-left-color: var(--success); }
.base-tech.poor { border-left-color: var(--warning); opacity: 0.9; }
.base-tech.blocked { border-left-color: var(--line); background: var(--bg-1); opacity: 0.75; }
.base-tech.on { border-left-color: var(--success); background: linear-gradient(160deg, rgba(61, 214, 152, 0.12), var(--bg-2) 70%); }
/* 预警类文字：告警色，去色主题下靠字重也读得出（不与其它 dim 文字同色） */
.warn-text { color: var(--warning); font-weight: 600; }

/* ---------------------------------------------------------------- 提示块 */

.notice {
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--info);
  background: var(--bg-2);
  font-size: var(--fs-sm);
}
.notice.warn { border-left-color: var(--warning); }
.notice.danger { border-left-color: var(--danger); }
.notice.ok { border-left-color: var(--success); }

/* ---------------------------------------------------------------- 响应式 */

/* M 及以下：战场单位更紧凑。
   注意：`.unit` 的宽度不再在这里调——棋盘内由 `.bf-grid .unit { width:100% }`
   跟着格子走，5 列在 360px 下每格约 62px，正好放得下 58px 立绘。
   这里只调非棋盘语境下的单位宽度（当前无此类语境，留着防回归）。 */
@media (max-width: 480px) {
  .panel { padding: 10px; }
  .hero-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
  .pull-result { grid-template-columns: repeat(5, 1fr); }
  /* 12×12 填满屏宽后每格约 27px：图形跟着格子缩，坐标刻度再降 1px 免得糊成一团 */
  .base-grid .base-glyph { width: 80%; }
  .base-grid .base-glyph svg { max-width: 22px; max-height: 22px; }
  .base-coord { font-size: 6px; }
  .hud-title { display: none; }
}

/* L：单列加宽居中，不提前切三栏（战场是核心视图，宁可留白也不挤） */
@media (min-width: 768px) {
  .panel { max-width: 720px; margin: 0 auto; width: 100%; }
  .modal-scrim { align-items: center; }
  .modal { border-radius: var(--radius-lg); }
}

/* XL：三段式（左导航 + 中央舞台 + 右详情）。1280-1439 右面板抽屉化、舞台保底 560 */
@media (min-width: 1280px) {
  .panel { max-width: 960px; }
}

/* 320px 宽度硬要求：无页面级横向滚动。
   最常见的溢出源是 flex 子项没有 min-width:0（中文长词会顶破容器）。 */
@media (max-width: 360px) {
  .bf-grid { gap: 3px; }
  .bf-grid .unit { max-width: 56px; }
  .unit-name { display: none; }
  .res { padding: 3px 6px; font-size: var(--fs-xs); }
  .hud-row { gap: 4px; }
}

/* 英雄加点：上阵英雄的 2 列网格（窄屏退化为单列），用于远征页下半部 */
.hg-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 8px; }
@media (max-width: 420px) { .hg-grid { grid-template-columns: 1fr; } }

/* 减少动效偏好 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* 桌面滚动条 */
#view::-webkit-scrollbar { width: 8px; }
#view::-webkit-scrollbar-thumb { background: var(--bg-3); border-radius: 4px; }
#view::-webkit-scrollbar-track { background: transparent; }

/* 背包分标签页：标签行与内容间距 */
.bag-tabs { margin: 8px 0; }
.bag-tabs .btn { min-width: 64px; }

/* ================================================================ 体验/性能模式（③-2 / ③-5 / ③-6）
   所有效果都由 <html> 上的根类驱动（applySettings 统一打/摘），纯 CSS、零数值改动。 */

/* ③-2 高性能模式：关阴影、减粒子、降动画、降 DPR 合成开销。
   注意所有规则都挂在 .high-perf 下，默认画质（开关关着）一点不变，
   移动端视觉回归基线（mobile-golden）也就不会被自己的降级开关打红。 */
.high-perf * {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
}
.high-perf .hero-card[data-fx-on] .hero-art { filter: none; }
/* HUD 的毛玻璃 + 渐变底换成纯色：blur 是逐帧重绘，纯色只画一次。 */
.high-perf #hud { backdrop-filter: none; background: var(--bg-1) !important; }
.high-perf .bf-grid.advance { animation: none !important; }
/* 减少粒子：飘字几乎瞬时消失，不再长时间悬浮占用合成层。 */
.high-perf .float { animation-duration: 80ms !important; }
/* 阴影 / 渐变是最贵的两类合成装饰，换成无阴影 + 纯色底（可读性不变，只差"浮起来"的观感）。 */
.high-perf .card,
.high-perf .btn,
.high-perf .btn-primary,
.high-perf .panel,
.high-perf .modal,
.high-perf .hero-card { box-shadow: none !important; background-image: none !important; }
/* blur 类装饰（毛玻璃 / 发光）是逐帧重绘的大开销，直接摘掉。
   只有纯装饰（fx-glow 发光）才摘 filter；hero-card::after 是英雄名的压暗层，
   属功能性底衬不能摘，改成同色实底 —— 渐变换纯色，开销从逐帧降到一次。 */
.high-perf .fx-glow,
.high-perf .float,
.high-perf .bf-status { filter: none !important; backdrop-filter: none !important; }
.high-perf .hero-card::after { background: rgba(0, 0, 0, 0.62) !important; }

/* ③-6 大字号模式：整体放大 1 档（覆盖设计令牌，全站继承）。 */
:root.big-font {
  --fs-xs: 0.8125rem;
  --fs-sm: 0.9375rem;
  --fs-md: 1.0625rem;
  --fs-lg: 1.3125rem;
  --fs-xl: 1.75rem;
}
/* 大字号叠在极窄屏（≤360px，含 320 极限档）上会顶破 HUD 货币网格与设置页分段按钮行，
   这条把放大幅度退回半档：字仍比默认大（--fs-md 1rem > 0.9375rem），但一行塞得下。
   规则写在上面那条之后，同特异性下后写的赢，无需 extra 选择器。 */
@media (max-width: 360px) {
  :root.big-font {
    --fs-xs: 0.75rem;
    --fs-sm: 0.875rem;
    --fs-md: 1rem;
    --fs-lg: 1.1875rem;
    --fs-xl: 1.4375rem;
  }
}

/* ③-6 高对比模式：提亮文本、加重描边（关键文字/边框加描边）。 */
:root.high-contrast {
  --text-1: #FFFFFF;
  --text-2: #F2F5FF;
  --text-3: #D2DAF2;
  --line: #5B68A0;
  --line-strong: #9AA8DC;
  --bg-1: #0E1326;
  --bg-2: #161D38;
}
/* 浅色主题下的高对比：上一条的深底浅色令牌在浅底上会把字变成白底白字（最坏 1.1:1）。
   本工程 app.css 排在 tokens.css 之后，`:root.high-contrast` 会盖掉 `[data-theme=light]`
   的同名变量，所以浅色必须自己给一套"更深的前景 + 更实的边框"，否则一开高对比就瞎。 */
:root[data-theme="light"].high-contrast {
  --text-1: #000000;
  --text-2: #22263A;
  --text-3: #3B4258;
  --line: #7C86A8;
  --line-strong: #1A2036;
  --bg-1: #FFFFFF;
  --bg-2: #E6EAF6;
  --gold-1: #2E1F00;
  --gold-2: #4A3405;
  --gold-3: #6B4A07;
  --gold-on-dark: #2E1F00;
  --on-gold: #FFFFFF;
}
.high-contrast .card,
.high-contrast .btn,
.high-contrast .panel,
.high-contrast .nav-btn,
.high-contrast .hero-card,
.high-contrast .modal,
.high-contrast .bf-grid .unit {
  border: 1px solid var(--line-strong);
}
.high-contrast .num { text-shadow: 0 0 1px rgba(0, 0, 0, 0.9); }
.high-contrast .res { text-shadow: 0 0 2px #000; }

/* ---------------------------------------------------------------- 需求 ① 物资页填白
   英雄 / 背包 / 抽奖三个子页在手机视口下底部留白 290~520px（实测见
   tools/verify/_tmp_ui_polish.mjs）。页脚由 renderSupply 挂载、只挂这三页。
   样式复用既有 token，不新增颜色。 */
.supply-foot {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.party-strip {
  /* auto-fit + minmax：360px 下 4 个槽位一排，宽屏自动摊开，不会撑出横向滚动 */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(62px, 1fr));
  gap: 6px;
  min-width: 0;
}

.party-slot {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 6px 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-2);
  color: var(--text-1);
  cursor: pointer;
  min-width: 0;
  overflow: hidden;
}
.party-slot .hero-art {
  width: 100%;
  height: 54px;
  aspect-ratio: auto;
  object-fit: contain;
  padding: 0;
}
.party-slot-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
}
.party-slot.is-empty {
  justify-content: center;
  min-height: 88px;
  border-style: dashed;
  background: transparent;
  cursor: default;
}
/* 图鉴里的未招募英雄：只留剪影，名字照给（收集类游戏靠"还差谁"驱动） */
.party-slot.is-locked .hero-art {
  filter: grayscale(1) brightness(0.45);
  opacity: 0.85;
}
.party-slot.is-locked { border-style: dashed; }

/* ③-5 小屏兜底（≤320px，含折叠屏外屏）：无页面级横向滚动、导航可点、战斗区不压垮。
   360px 档已在上面覆盖，这里只补 320 极限宽的几处余量。 */
@media (max-width: 320px) {
  .nav-lbl { font-size: 10px; }
  .nav-btn { padding: 4px 1px; gap: 1px; }
  .nav-ico { width: 18px; height: 18px; }
  .hud-row { gap: 2px; }
  .res { padding: 2px 4px; gap: 2px; }
  .res-val { font-size: 10px; }
  /* ③-2 的 HUD 一键开关在 320px 下压到和货币 chip 同一档字号，才不会被挤到 hud-row 的第三行 */
  .hud-perf { padding: 0 5px; font-size: 10px; }
  .panel { padding: 8px; }
  .bf-grid { gap: 2px; }
  .bf-grid .unit { max-width: 46px; }
  .modal { width: 96vw; }
  .hero-grid { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); }
  /* ① 页脚：320px 下 4 个槽位仍要一排放下（62px×4 + gap 已接近极限，收到 56px） */
  .party-strip { grid-template-columns: repeat(auto-fit, minmax(56px, 1fr)); gap: 4px; }
  .party-slot .hero-art { height: 44px; }
}
