
  /* ===== 标题字体：得意黑子集（MaDao Title） =====
 * 字形取自得意黑 Smiley Sans v2.0.1（SIL OFL 1.1，作者 atelierAnchor），
 * 用 fontTools 子集化为只含标题页用字的 woff2（约 17KB）。
 * 该字体许可声明保留字体名 <Smiley> / <得意黑>，子集版属修改版，
 * 故内部字体名与 CSS 家族名统一改为 MaDao Title。
 * 许可全文见 vendor/fonts/LICENSE-SmileySans.txt，重建命令见 vendor/fonts/README.md。 */
@font-face {
  font-family: 'MaDao Title';
  src: url('vendor/fonts/maodao-title-subset.woff2?v=20270180') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: block;   /* 本地文件加载极快，避免回退字体闪一下 */
}

  /* ===== 全局暗色画风，不依赖任何图片 ===== */
  :root {
    --bg: #0b0e14;
    --bg2: #131722;
    --bg3: #1b2130;
    --panel: #171c29;
    --panel2: #20273a;
    --border: #31405f;
    --text: #d8dee9;
    --muted: #8a97ad;
    --accent: #e0a458;
    --accent2: #6fc3df;
    --good: #7bc96f;
    --bad: #e06c75;
    --warn: #e5c07b;
    --magic: #c678dd;
    --danger: #ff7b8a;
    --shadow: 0 8px 30px rgba(0,0,0,.45);
    /* 滚动条半透明变量（v0.3.49）：暗色用白、浅色主题用黑，避免浅色底上近隐形 */
    --scrollbar-thumb: rgba(255,255,255,.18);
    --scrollbar-thumb-hover: rgba(255,255,255,.3);
  }
  * { box-sizing: border-box; }
  /* ===== 全站滚动条不显眼化（v0.3.49） =====
   * 所有滚动容器（window、配方面板、战斗日志、物品列表等）统一为细窄半透明条：
   * - *::-webkit-scrollbar 一条规则覆盖全部元素滚动条（含页面窗口滚动）；
   * - thumb 用半透明白 rgba(255,255,255,.18) 而非写死颜色，暗色主题通用，
   *   hover 略深至 .3；track 全透明；
   * - Firefox 无 ::-webkit-scrollbar，走 scrollbar-width/scrollbar-color 兜底，
   *   一条规则通吃窗口与元素滚动条。 */
  *::-webkit-scrollbar { width: 6px; height: 6px; }
  *::-webkit-scrollbar-track { background: transparent; }
  *::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 3px; }
  *::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
  * { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb) transparent; }
  html, body { height: 100%; }
  body {
    margin: 0;
    /* 页面底色跟随主题：先给主题纯色兜底，再用主题强调色混出两团柔光，
     * 不支持 color-mix 时只留 var(--bg)。 */
    background: var(--bg);
    background:
      radial-gradient(circle at 20% 20%, color-mix(in srgb, var(--accent) 12%, var(--bg)) 0%, transparent 40%),
      radial-gradient(circle at 80% 80%, color-mix(in srgb, var(--accent) 6%, var(--bg)) 0%, transparent 45%),
      var(--bg);
    color: var(--text);
    font-family: "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
    line-height: 1.5;
  }
  button, input, textarea, select {
    font: inherit;
    color: var(--text);
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .45rem .8rem;
    cursor: pointer;
    transition: background .15s, border-color .15s, transform .05s;
  }
  button:hover { background: var(--panel2); border-color: var(--accent); }
  button:active { transform: translateY(1px); }
  input, textarea { width: 100%; }
  textarea { resize: vertical; min-height: 70px; }
  ::placeholder { color: #5b6a80; }
  .hidden { display: none !important; }
  #app { min-height: 100vh; }
  .screen {
    max-width: 1100px;
    margin: 0 auto;
    padding: 1rem;
  }
  /* ===== 标题界面：简约静态版（无 Canvas 场景、无任何动画） =====
   * 结构：.title-wrap > .title-content > 标题 / 副标题 / 菜单 / 提示。
   * 配色一律取主题变量（--bg / --bg2 / --panel2 / --text / --muted / --accent），
   * 切换主题时 applyTheme() 改写 :root 变量，本页即时跟随，不再出现固定色。 */
  .title-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 1.5rem;
    position: relative;
    overflow: hidden;
    /* 兜底：不支持 color-mix 的浏览器只上主题双色渐变 */
    background: linear-gradient(180deg, var(--bg) 0%, var(--bg2) 55%, var(--bg) 100%);
    /* 顶部一抹主题强调色微光，浓淡固定，颜色随主题 */
    background:
      radial-gradient(ellipse 110% 70% at 50% 0%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 62%),
      linear-gradient(180deg, var(--bg) 0%, var(--bg2) 55%, var(--bg) 100%);
  }

  .title-content {
    width: min(720px, 100%);
    text-align: center;
    padding: 1.5rem 1rem;
    position: relative;
    z-index: 3;
  }
  /* 主标题：得意黑（Smiley Sans）锋锐斜切字形，颜色取主题强调色，无动画 */
  .title-name {
    text-align: center;
    font-family: 'MaDao Title', "方正粗黑宋简体", "华文中宋", "Songti SC", STSong, SimSun, serif;
    font-size: clamp(2.8rem, 7vw, 4.8rem);
    font-weight: 400;        /* 该字体只有单一字重，禁用合成加粗，保持原始笔形 */
    font-synthesis: none;
    letter-spacing: .12em;
    text-indent: .12em;      /* 抵消末字右侧的字距留白，保证视觉居中 */
    line-height: 1.2;
    margin: 0 0 .9rem;
    color: var(--accent);
    background: none;
    -webkit-text-fill-color: currentColor;
    animation: none;
  }
  /* 副标题：主题弱化色，拉开与标题的层次。
   * 深色主题的 --muted 是固定灰蓝，直接用它会在翠绿 / 猩红这类主题下发脏，
   * 所以优先取文本色与背景色的混合，随主题一起偏色；不支持 color-mix 时回退 --muted。 */
  .title-sub {
    text-align: center;
    color: var(--muted);
    color: color-mix(in srgb, var(--text) 62%, var(--bg));
    margin: 0 0 2.2rem;
    font-size: .95rem;
    letter-spacing: .18em;
  }
  .menu-buttons {
    display: grid;
    gap: .6rem;
    margin: 0 auto;
    max-width: 300px;
    width: 100%;
  }
  .menu-buttons button {
    font-size: 1rem;
    padding: .78rem 1rem;
    letter-spacing: .1em;
    box-shadow: none;
  }
  /* 标题页的按钮一律取主题中性色：:is() 让特异性压过 .btn-* 的写死色与浅色主题的
   * 按钮覆盖，否则切到苍白废土 / 翠绿魔导 / 猩红深渊后按钮仍是固定的深蓝深绿。 */
  .title-wrap .menu-buttons button:is(.btn-primary, .btn-good, .btn-magic, .btn-danger) {
    background: var(--bg2);
    border: 1px solid var(--border);
    color: var(--text);
  }
  .title-wrap .menu-buttons button:is(.btn-primary, .btn-good, .btn-magic, .btn-danger):hover {
    background: var(--panel2);
    border-color: var(--accent);
  }
  .title-bottom-right {
    position: fixed;
    right: 12px;
    bottom: 10px;
    z-index: 120;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
  }
  /* 标题页左下角的联系方式（用户要求）：B站与测试群，纯文字、可选中复制。
   * 与右下角那排按钮对称：一个贴左下、一个贴右下。 */
  .title-bottom-left {
    position: fixed;
    left: 12px;
    bottom: 10px;
    z-index: 120;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    text-align: left;
  }
  .title-contact {
    font-size: .72rem;
    color: var(--muted);
    opacity: .88;
    line-height: 1.5;
  }

  /* ===== 存档动画（用户口径）=====
   * 画面正中「存档中。。。」，三个句号依次浮现再依次隐去、循环；
   * 遮罩把底层界面模糊掉并吃掉所有指针事件（点不动）；JS 随机播 1~2 秒后移除。 */
  .saving-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(10, 12, 18, .38);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
    cursor: wait;
    user-select: none;
  }
  .saving-text {
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: .08rem;
    color: var(--text);
    text-shadow: 0 2px 10px rgba(0, 0, 0, .55);
  }
  /* 每个字（含句号）都规律地上下浮动，相位依次错开，整排看起来像海浪。
   * 句号除浮动之外还保留「依次浮现再依次隐去」的循环，两个动画并行，互不干扰
   * （一个改 transform，一个改 opacity）。 */
  .saving-text .ch {
    display: inline-block;
    animation: saveWave 1.4s ease-in-out infinite;
  }
  .saving-text .ch:nth-child(1) { animation-delay: 0s; }
  .saving-text .ch:nth-child(2) { animation-delay: .1s; }
  .saving-text .ch:nth-child(3) { animation-delay: .2s; }
  .saving-text .ch:nth-child(4) { animation-delay: .3s; }
  .saving-text .ch:nth-child(5) { animation-delay: .4s; }
  .saving-text .ch:nth-child(6) { animation-delay: .5s; }
  .saving-text .dot { opacity: 0; animation: saveDotCycle 1.2s ease-in-out infinite, saveWave 1.4s ease-in-out infinite; }
  /* 用 nth-child（不是 nth-of-type）：句号与「存档中」都是 span，
   * nth-of-type 会把「存」算成第 1 个，规则就落空了。 */
  .saving-text .dot:nth-child(4) { animation-delay: .3s, .3s; }
  .saving-text .dot:nth-child(5) { animation-delay: .5s, .4s; }
  .saving-text .dot:nth-child(6) { animation-delay: .7s, .5s; }
  @keyframes saveWave {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-6px); }
  }
  @keyframes saveDotCycle {
    0%, 15%   { opacity: 0; }
    35%, 65%  { opacity: 1; }
    85%, 100% { opacity: 0; }
  }
  /* ===== 存档成功（用户口径）：动画播完后换成稍大的「存档成功！」。
   * 原来的金色耶稣光已按用户要求去掉，只留文字。 ===== */
  .save-done {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .save-done-text {
    position: relative;
    z-index: 1;
    font-size: 2rem;
    font-weight: 700;
    letter-spacing: .1rem;
    color: #ffe9a8;
    text-shadow: 0 0 14px rgba(232, 195, 74, .85), 0 2px 10px rgba(0, 0, 0, .6);
  }
  .title-clear-btn {
    font-size: .72rem;
    padding: .3rem .55rem;
    opacity: .88;
  }
  /* 清除存档按钮同样跟随主题：btn-danger 的深红只在暗红主题下协调，
   * 换到苍白废土 / 翠绿魔导 / 薄荷奶昔就会突兀。 */
  .title-bottom-right .title-clear-btn.btn-danger {
    background: var(--bg2);
    border-color: var(--border);
    color: var(--muted);
  }
  /* 标题页的提示与版本号跟随同一套主题弱化色（口径与副标题一致） */
  .title-wrap .hint,
  .title-bottom-right .title-version {
    color: var(--muted);
    color: color-mix(in srgb, var(--text) 55%, var(--bg));
  }
  .title-version {
    font-size: .68rem;
    opacity: .45;
    letter-spacing: .08em;
    user-select: none;
  }
  /* ===== 语义按钮：颜色由主题变量派生 =====
   * 每类先给一个写死色兜底（不支持 color-mix 的老浏览器），再用
   * color-mix 把主题色与底色调和，切主题时四类按钮一起换色。
   * candy / mint 两个浅色主题在文件后半段另有专属覆盖。 */
  .btn-primary {
    background: #2b344a;
    background: color-mix(in srgb, var(--accent) 16%, var(--bg2));
    border-color: #42577d;
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
  }
  .btn-danger {
    background: #3a1e24;
    background: color-mix(in srgb, var(--bad) 20%, var(--bg2));
    border-color: #7d3a44;
    border-color: color-mix(in srgb, var(--bad) 50%, var(--border));
  }
  .btn-magic {
    background: #2a2340;
    background: color-mix(in srgb, var(--magic) 20%, var(--bg2));
    border-color: #6a3f8a;
    border-color: color-mix(in srgb, var(--magic) 50%, var(--border));
  }
  .btn-good {
    background: #1d3525;
    background: color-mix(in srgb, var(--good) 18%, var(--bg2));
    border-color: #3f7d52;
    border-color: color-mix(in srgb, var(--good) 50%, var(--border));
  }
  .hint { color: var(--muted); font-size: .85rem; text-align: center; }

  /* ===== 标题界面：窄屏收紧（简约版，无动画） ===== */
  @media (max-width: 640px) {
    .title-content { padding: 1.2rem .8rem; }
    .title-name { font-size: clamp(2.4rem, 12vw, 3.4rem); letter-spacing: .08em; text-indent: .08em; }
    .title-sub { font-size: .84rem; letter-spacing: .12em; margin-bottom: 1.6rem; }
    .menu-buttons { max-width: 100%; }
  }
  .form-row { margin-bottom: .8rem; }
  .form-row label { display:block; margin-bottom: .3rem; color: var(--accent2); font-size: .9rem; }
  .game-shell {
    max-width: 1200px;
    margin: 0 auto;
    padding: .6rem;
  }
  .topbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: .6rem .8rem;
    margin-bottom: .8rem;
  }
  .topbar .title-sm {
    font-weight: bold;
    color: var(--accent);
    font-size: 1.2rem;
    margin-right: .4rem;
  }
  .topbar .spacer { flex: 1; }
  .chip {
    display: inline-block;
    background: var(--bg2);
    border: 1px solid var(--border);
    padding: .15rem .55rem;
    border-radius: 999px;
    font-size: .78rem;
    color: var(--text);
  }
  .status-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: .7rem;
    margin-bottom: .8rem;
  }
  .char-status-grid {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(3, auto);
    gap: .25rem .9rem;
    align-items: center;
  }
  /* 角色状态面板（用户需求）：左侧预留 3:4 头像位，右侧是状态条。
   * aspect-ratio 把宽度换算成高度，头像换图时不会撑破面板；
   * 图片尚未提供时仍占住这个位置，将来把图片放进 images/ 按 id 命名即可无缝接上。 */
  .char-panel {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
  }
  .char-panel .char-panel-body { flex: 1 1 auto; min-width: 0; }
  .char-avatar-3x4 {
    flex: 0 0 96px;
    width: 96px;
    aspect-ratio: 3 / 4;
    background: var(--bg3);
    border: 1px dashed var(--border);
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    cursor: pointer;
  }
  .char-avatar-3x4:hover { border-color: var(--accent, var(--border)); }
  .char-avatar-3x4 img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .char-avatar-hint { font-size: .72rem; color: var(--text-dim); text-align: center; line-height: 1.5; }
  /* 顶部只剩角色栏：状态网格铺满一行，不再走两列的窄排版 */
  .char-panel .char-status-grid {
    grid-auto-flow: row;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: none;
    gap: .1rem .8rem;
  }
  /* 注：竖屏那份「头像 + 角色名」的 .char-id-row 由 JS 按方向渲染（见 ui.module.js 的
   * isPortraitNow），横屏根本不生成这个元素，所以这里不再需要「默认隐藏」的兜底规则 ——
   * 原先的 display:none 兜底一旦样式表没刷新，就会两份都露出来（用户实测报过的两个头像）。 */
  .log-float {
    position: fixed;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 60;
    display: flex;
    align-items: stretch;
    gap: .4rem;
    width: max-content;
    max-width: 22vw;
    height: 56vh;
    min-height: 56vh;
    max-height: 56vh;
    background: var(--panel);
    border: 1px solid var(--border);
    border-right: none;
    border-radius: 12px 0 0 12px;
    padding: .55rem .6rem;
    box-shadow: 0 4px 14px rgba(0,0,0,.28);
    color: var(--text);
    pointer-events: none;
    overflow: hidden;
  }
  .log-float-title {
    flex: 0 0 auto;
    writing-mode: vertical-rl;
    text-orientation: upright;
    letter-spacing: .12em;
    color: var(--accent);
    font-weight: 800;
    font-size: 1.15rem;
    display: flex;
    align-items: center;
  }
  .log-float-body {
    flex: 1 1 auto;
    min-width: 3.6em;
    height: 100%;
    overflow: hidden;
    font-size: .7rem;
    line-height: 1.2;
    word-break: break-word;
    writing-mode: vertical-lr;
    text-orientation: upright;
    letter-spacing: .03em;
  }
  .panel {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: .8rem;
    box-shadow: 0 4px 12px rgba(0,0,0,.2);
  }
  .panel h3 {
    margin: 0 0 .5rem;
    font-size: 1rem;
    color: var(--accent);
  }
  /* 标题行两端对齐：把「整理」按钮放到面板右上角（用户需求）。 */
  .panel h3.panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
  }
  .panel h3.panel-head button {
    flex: 0 0 auto;
    font-size: .72rem;
    padding: .15rem .55rem;
    line-height: 1.5;
  }
  /* 魔法书（用户口径）：摊开的厚书，书签露在书口上方，左右页各一枚翻页箭头。 */
  /* 魔法书不要「面板底」（用户口径）：把弹窗的卡片背景、边框、内边距与阴影全部去掉，
   * 让书自己悬在遮罩上；overflow 必须放开，否则书底下的纸边会被裁掉——那正是书看着很扁的原因。 */
  .modal:has(.magic-book) {
    width: min(1060px, 95vw);
    max-height: none;
    overflow: visible;
    background: none;
    border: none;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
  }
  .magic-book { max-width: 940px; margin: 0 auto; padding: 1.1rem .5rem 0; }
  .book-tabs {
    position: relative;
    z-index: 2;
    display: flex;
    gap: .35rem;
    padding-left: 1.2rem;
    margin-bottom: -1px;
  }
  .book-tab {
    padding: .3rem 1.05rem .45rem;
    border: 1px solid #7a6038;
    border-bottom: none;
    border-radius: 7px 7px 0 0;
    background: linear-gradient(#c9a86a, #a9884c);
    color: #33260f;
    font-size: .79rem;
    letter-spacing: .1rem;
    cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.35);
  }
  .book-tab[data-on="1"] {
    background: linear-gradient(#eadaa9, #d6c089);
    color: #2a1f0c;
    font-weight: 700;
    transform: translateY(-2px);
  }
  /* 书页厚度：书底下错开叠两层纸边，右下方露出一截，看上去是一沓纸而不是一张卡片。 */
  .book-stack { position: relative; }
  .book-stack::before,
  .book-stack::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 5px 12px 12px 5px;
    box-shadow: 0 0 0 1px rgba(60,44,18,.5);
  }
  .book-stack::before { transform: translate(7px, 9px); background: #e0d1ad; }
  .book-stack::after { transform: translate(14px, 18px); background: #c9b78f; }
  .book-spread {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: 1fr 1fr;
    /* 定高：书有确定的厚度比例，不再被内容多少压成一条（用户口径「太扁」）。 */
    height: clamp(400px, 72vh, 680px);
    border: 2px solid #6b5228;
    border-radius: 5px 12px 12px 5px;
    background: linear-gradient(180deg, #f8f1de, #ebe0c4);
    box-shadow: 0 26px 54px rgba(0,0,0,.62);      /* 悬空：整本书浮在界面上方 */
    overflow: hidden;
  }
  .book-half { position: relative; display: flex; min-height: 0; height: 100%; }
  /* 书脊：两页之间一条深缝，两侧各压一层内阴影 */
  .book-half:first-child::after {
    content: "";
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: 18px;
    background: linear-gradient(90deg, rgba(60,44,18,0), rgba(60,44,18,.5));
    pointer-events: none;
  }
  .book-half:last-child::before {
    content: "";
    position: absolute;
    top: 0; left: 0; bottom: 0;
    width: 18px;
    background: linear-gradient(270deg, rgba(60,44,18,0), rgba(60,44,18,.26));
    pointer-events: none;
  }
  .book-half:first-child { box-shadow: inset -16px 0 22px -18px rgba(60,44,18,.95); }
  .book-half:last-child { box-shadow: inset 16px 0 22px -18px rgba(60,44,18,.5); }
  /* 单页版式（用户口径：不要大片空白）：书眉 / 正文 / 页脚三栏，
   * 正文这一栏上下居中，内容少的时候由两栏把它托在页面中间。
   * justify-content 用 safe center —— 内容超过一页时不会把顶部顶出可视区。 */
  .book-page {
    flex: 1; min-width: 0; color: #3f3220; overflow-y: auto;
    display: flex; flex-direction: column;
    padding: 1rem 1.35rem .85rem;
  }
  .book-head {
    flex: none; text-align: center;
    font-size: .72rem; letter-spacing: .34rem; color: #8d7850;
    padding-bottom: .45rem; border-bottom: 1px solid rgba(122,96,58,.22);
  }
  .book-body {
    flex: 1 1 auto; min-height: 1;
    display: flex; flex-direction: column;
    justify-content: safe center;
  }
  .book-foot {
    flex: none; text-align: center;
    font-size: .68rem; letter-spacing: .08rem; color: #a08c66;
    padding-top: .5rem;
  }
  /* 章首页：类别大字加一行「已习得 N 个」，别让整页只剩两个字 */
  .book-cover-body { align-items: center; gap: .75rem; overflow: hidden; }
  .book-cover-title { font-size: 1.9rem; letter-spacing: .42rem; color: #7a5a1c; text-indent: .42rem; }
  .book-cover-sub { font-size: .76rem; letter-spacing: .16rem; color: #9a8459; }
  .book-blank .book-body { align-items: center; }
  .book-todo { color: #a08e69; opacity: .55; font-size: 1.05rem; letter-spacing: .22rem; }
  /* 魔法详情：字号与行距按书页放大，把版面撑起来 */
  .book-spell-body { gap: .1rem; }
  .book-spell h4 { margin: 0 0 .25rem; font-size: 1.32rem; letter-spacing: .05rem; color: #6d4c12; }
  .book-spell .small { font-size: .84rem; color: #7c6a4c; }
  .book-effect { font-size: .89rem; line-height: 1.95; color: #8a6413; }
  .book-desc { font-size: .92rem; line-height: 2; color: #4a3c2b; margin-top: .55rem; }
  .book-use { margin-top: .85rem; }
  .book-nav {
    position: absolute;
    bottom: .7rem;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 1px solid #9c8050;
    border-radius: 50%;
    background: rgba(255,255,255,.55);
    color: #6d4c12;
    line-height: 1;
    cursor: pointer;
  }
  .book-nav:hover { background: #fff8e6; }
  .book-nav-left { left: .7rem; }
  .book-nav-right { right: .7rem; }
  .book-hint { text-align: center; margin: .6rem 0 0; }
  .bar-row { margin: .35rem 0; }
  .bar-label {
    display: flex;
    justify-content: space-between;
    font-size: .78rem;
    color: var(--muted);
    margin-bottom: .1rem;
  }
  .bar {
    height: 9px;
    background: var(--bg3);                                   /* 兜底 */
    background: color-mix(in srgb, var(--text) 12%, var(--bg));
    /* 用户口径：状态条不要那么圆（原来是 8px 的全圆角），改成 2px 的小圆角 */
    border-radius: 2px;
    overflow: hidden;
  }
  .bar > i {
    display: block;
    height: 100%;
    /* 用户口径：条的颜色不要渐变，一律纯色 */
    border-radius: 2px;
    background: #6fbf8b;
  }
  .bar.bad > i { background: #d4646c; }
  .bar.warn > i { background: #d9b25f; }
  .bar.mp > i { background: #9a7fe0; }
  .bar.magic > i { background: #5fb3d4; }
  /* 异化值：颜色由 JS 按当前数值现算（血红 → 黑红），这一条只是内联样式缺失时的兜底 */
  .bar.corrupt > i { background: #8f1219; }
  .tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin-bottom: .8rem;
  }
  .tabs button {
    background: var(--bg2);
    border-color: var(--border);
    padding: .4rem .8rem;
    font-size: .9rem;
  }
  .tabs button.active {
    background: var(--panel2);                                /* 兜底 */
    background: color-mix(in srgb, var(--accent) 16%, var(--bg2));
    border-color: var(--accent);
    color: var(--accent);
  }
  .tabs button.blocked {
    opacity: .55;
    cursor: not-allowed;
    border-style: dashed;
  }
  .tabs button.blocked:hover {
    background: var(--bg2);
    border-color: var(--border);
  }
  .tabs button.tab-right {
    margin-left: auto;
  }
  .grid2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: .8rem;
  }
  .arrival-title {
    font-size: 1.05rem;
    font-weight: normal;
    text-align: center;
    margin: .2rem 0 .7rem;
  }
  .arrival-station {
    font-size: 2.4rem;
    font-weight: 900;
    color: var(--accent);
    letter-spacing: .06em;
    vertical-align: middle;
  }
  .battle-layout {
    position: relative;
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: clamp(1rem, 8vw, 5rem);
    min-height: 260px;
    margin-bottom: .5rem;
  }
  .battle-enemy {
    position: static;
    width: min(300px, 38%);
    margin-left: auto;
  }
  .battle-player {
    position: static;
    width: min(300px, 38%);
    margin-top: 3.5rem;
  }
  /* 单人战斗：两块面板等宽（38%），中间留出至少半个面板宽的空隙（19%）。
   * 原来的 46% + 8vw 只留了不到四分之一面板的空隙，看起来两块面板贴在一起。 */
  .battle-layout {
    column-gap: min(150px, 19%);
  }
  /* 战斗面板布局：敌人格在上方一排依次排开（左 / 中 / 右），玩家格固定在右下，
   * 不与敌人格对齐。面板宽高比统一 5:3（即 3:5 的比例横放），角色与敌人完全一致；
   * 空出来的敌人格由渲染层补同尺寸占位块，所以人数变化时位置与尺寸都不动。
   * 面板宽度固定（--bp-w），不随敌人数量或弹窗宽度变化。 */
  .battle-layout.group {
    --bp-w: 320px;
    display: grid;
    grid-template-columns: repeat(3, var(--bp-w));
    justify-content: center;
    align-items: start;
    column-gap: calc(var(--bp-w) / 2);   /* 面板之间半个面板宽的空隙 */
    row-gap: 40px;
    /* 与标题、弹窗边缘都留出余量（用户要求面板别贴着标题和左边缘） */
    margin-top: 22px;
    min-height: 0;
  }
  .battle-layout.group .battle-enemy,
  .battle-layout.group .battle-player {
    width: 100%;
    margin: 0;
    /* 统一长宽比：宽 5 / 高 3（320 × 192） */
    aspect-ratio: 5 / 3;
    min-height: 0;
  }
  .battle-layout.group .slot-0 { grid-column: 1; grid-row: 1; }
  .battle-layout.group .slot-1 { grid-column: 2; grid-row: 1; }
  .battle-layout.group .slot-2 { grid-column: 3; grid-row: 1; }
  .battle-layout.group .battle-player { grid-column: 3; grid-row: 2; }
  /* 空位占位块：占着格子但不显示 */
  .battle-layout.group .is-empty { visibility: hidden; }
  /* 但打在空位上的伤害数字要看得见：这一发把目标打死时面板已经变成占位块，
   * 若连数字一起隐藏，玩家会以为「魔法打上去毫无反馈」。 */
  .battle-layout.group .is-empty .battle-pop-num { visibility: visible; }
  /* 敌方面板可点：点一下就锁定它，普攻与魔法都打锁定目标 */
  .battle-layout .battle-enemy { cursor: pointer; }
  .battle-layout .battle-enemy.battle-target {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 55%, transparent);
  }
  .battle-target-tag { color: var(--accent); font-weight: 800; }
  /* 事件选项行：多个选项等距居中排开，间距比普通按钮行大（用户要求「不要过近」）。
   * 选择器必须写成 .row.event-choices 并带 !important —— 本文件末尾的
   * `.row { justify-content: flex-end !important }` 与它同特异性且位置更靠后，
   * 只写 .event-choices 会被它压回右对齐（AGENTS 里记过这个坑）。 */
  .row.event-choices {
    display: flex;
    flex-wrap: wrap;
    justify-content: center !important;
    align-items: center;
    gap: 1.5rem !important;
    margin: 1.1rem 0 .5rem;
  }
  /* 胜利弹窗正文：按模板换行显示「击败 名×N / 获得战利品：……」 */
  .battle-victory-text { line-height: 1.8; }
  /* 怪物名 / 群名 / 等级按档位上色，配色取自成就卡片（ach-r1~r5）：
   *   弱小 棕铜、普通 红铜、精英 亮银流光、领主 金色流光、主宰 彩虹流动。
   * battle-title-text 是整行标题（含图标与等级），battle-tier-name 保留给单独的名字用。 */
  .battle-tier-name,
  .battle-title-text { font-weight: 800; }
  .battle-title-text { display: inline-block; }
  .battle-tier-t1 { color: #ecdcc6; }
  .battle-tier-t2 { color: #f0cdb4; }
  .battle-tier-t3 {
    color: #eef3f8;                                    /* 不支持 background-clip 时的兜底 */
    background: linear-gradient(105deg, #67727f 0%, #cbd5e0 28%, #a9b4c0 52%, #eef3f8 78%, #77818e 100%);
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: ach-sheen 7s linear infinite;
  }
  .battle-tier-t4 {
    color: #fff6d0;
    background: linear-gradient(105deg, #a8801c 0%, #ffe89a 25%, #d5a326 50%, #ffd257 75%, #a8801c 100%);
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: ach-sheen 4.5s linear infinite;
    filter: drop-shadow(0 0 6px rgba(255, 210, 87, .45));
  }
  .battle-tier-t5 {
    color: #ffffff;
    background: linear-gradient(120deg, #7a1f6f, #b23a2e, #c98a1c, #2e8b57, #2a5fb0, #7a1f6f);
    background-size: 300% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: ach-flow 6s linear infinite;
    filter: drop-shadow(0 0 6px rgba(255, 255, 255, .35));
  }
  /* 战斗日志永远压在面板之上：面板的入场/撞击动画会短暂放大位移，
   * 不加这条时放大后的面板会盖住日志区。 */
  .battle-log { position: relative; z-index: 3; }
  /* 面板名字加粗（用户要求） */
  .battle-enemy-name,
  .battle-player-name { font-weight: 800; }
  /* 魔法光球：从施放者面板飞向受击者面板；位置（left/top 与飞行位移）由 JS 用视口坐标给定。
   * z-index 要高于 .modal-backdrop(1000)，否则会被遮罩层盖住。 */
  .magic-bolt {
    position: fixed;
    left: 0;
    top: 0;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    pointer-events: none;
    z-index: 1200;
    background: radial-gradient(circle at 34% 34%, #fff 0%, var(--bolt-color, #c9b8ff) 46%, rgba(0, 0, 0, 0) 78%);
    box-shadow: 0 0 12px var(--bolt-color, #c9b8ff), 0 0 26px var(--bolt-color, #c9b8ff);
    will-change: transform, opacity;
  }
  /* 火焰拖尾：沿飞行方向在身后拉出一条渐隐光带。
   * 注意不要再写 rotate(var(--bolt-angle)) —— 光球元素本身已经按飞行方向旋转过，
   * 伪元素跟着父元素一起转，这里再转一次就成两倍角度，尾迹会指错方向（用户报的 bug）。
   * 圆端（光球本体）在飞行前方朝向受击者，尾端向后指向释放者。 */
  .magic-bolt::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 76px;
    height: 14px;
    transform: translate(-100%, -50%);
    transform-origin: 100% 50%;
    border-radius: 999px;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, var(--bolt-color, #c9b8ff) 100%);
    filter: blur(4px);
    opacity: .9;
  }
  /* 给自己加的治疗 / 护盾：原地脉冲，不带拖尾 */
  .magic-bolt.self-cast::after { display: none; }
  /* 战斗中的子菜单（魔法 / 道具）：叠在战斗界面之上的浮层，不再替换整个弹窗
   * （用户要求：选择魔法时不应该隐藏战斗界面）。 */
  .battle-submenu {
    position: absolute;
    inset: 0;
    z-index: 6;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, .58);
    border-radius: 14px;
  }
  .battle-submenu-box {
    width: min(680px, 100%);
    max-height: 100%;
    overflow: auto;
    padding: .9rem 1rem;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow);
  }
  .battle-submenu-box h2 { margin: 0 0 .5rem; color: var(--accent); }
  /* 魔力不够的条目：只做弱化提示，按钮仍可点（点了会说明差多少魔力） */
  .battle-submenu-lacking { opacity: .72; }
  /* 战斗弹窗加宽：敌人格一排三块（各 320px）加半个面板的间隔，
   * 两侧与标题上方都留出余量（用户要求面板别贴着标题和左边缘） */
  .modal:has(.battle-scene) { width: min(1500px, 96%); }
  /* 窄屏（≤760px）：群居网格塌成一列，与既有竖向布局一致 */
  @media (max-width: 760px) {
    .battle-layout.group {
      grid-template-columns: 1fr;
      grid-template-rows: auto;
      column-gap: 0;
      row-gap: 1rem;
    }
    .battle-layout.group .slot-0,
    .battle-layout.group .slot-1,
    .battle-layout.group .slot-2,
    .battle-layout.group .battle-player {
      grid-column: 1;
      grid-row: auto;
    }
  }
  @media (max-width: 760px) {
    .battle-layout {
      flex-direction: column;
      gap: 1rem;
      min-height: 0;
    }
    .battle-enemy,
    .battle-player {
      width: 100%;
      margin-left: 0;
      margin-top: 0;
    }
  }
  .item-card, .npc-card, .facility-card, .log-card {
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: .65rem .75rem;
    margin: .35rem 0;
  }
  /* 制造面板「已解锁制造」区：一行 4 格（需求 3/4） */
  .cell-grid-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
  }
  /* 制造配方格子（需求 10）：默认描边卡片，选中态高亮 */
  .cell {
    min-height: 82px;
    background: var(--bg3);
    border: 2px solid var(--border);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: .72rem;
    text-align: center;
    padding: 6px;
    gap: 2px;
  }
  .cell.selected {
    border-color: var(--accent);
    background: var(--panel2);
  }
  /* 蓝图格子：蓝色边框 + 浅蓝底（已习得蓝图，只读展示） */
  .cell-blue, .item-card.cell-blue {
    border-color: #6fc3df;
    background: rgba(111, 195, 223, .12);
  }
  /* 基础配方 / 料理格子：橙色边框 + 浅橙底（基础配方可选制作；图鉴料理卡片） */
  .cell-orange, .item-card.cell-orange {
    border-color: #e0a458;
    background: rgba(224, 164, 88, .12);
  }
  /* 烹饪左栏原料列表：一行两个、左对齐、可滚动（需求 7） */
  .cook-ing-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .2rem .8rem;
    max-height: 430px;
    overflow: auto;
    align-content: start;
  }
  /* 列表行靠左（烹饪「原料」/「已习得料理」/ 简易制作「已解锁制造」）：名称、材料标记、
   * 操作按钮从左端依次排列。
   * 必须带 !important —— 本文件末尾的 `.row { justify-content: flex-end !important }`
   * 会压过内联的 space-between/flex-start，把整行内容推到右端（即「内容没有靠左」的根因）。 */
  .list-row-left {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start !important;
    gap: .4rem;
    padding: 2px 0;
  }
  /* 配方 / 料理名（用户要求「让已解锁的配方名字显眼一点」）：
   * 用于烹饪「已习得料理」与简易制作「已解锁制造」列表行，覆盖 .small 的暗淡小字。 */
  .recipe-name {
    font-size: .95rem;
    font-weight: 800;
    color: var(--accent);
    letter-spacing: .02em;
  }
  .log-name { font-weight: 800; color: inherit; }
/* 日志状态变化文字加粗（如 列车完整度-5、恢复体力、异化值+3） */
b.log-stat { font-weight: 800; color: var(--warn); }
/* 战斗日志滚动容器：固定高度 170px（含 padding，box-sizing 已保证）。
 * 用固定 height 而非 max-height：战斗开始时日志仅 1~2 行，若用 max-height，
 * 日志区会随战斗记录逐行长高，撑高整个战斗弹窗，导致 .battle-actions 按钮行
 * 持续下移；固定高度让日志从一开始就在 170px 区域内滚动，
 * 战斗弹窗总高度全程恒定，按钮位置不再变化。 */
.battle-log { height: 170px; overflow-y: auto; padding-right: 4px; box-sizing: border-box; }
/* 滚动条样式由顶部全局规则统一接管（v0.3.49），此处不再单独定制 */
/* ===== 战斗表现层：面板撞击动画 + 伤害数字/MISS 弹出 ===== */
/* 浮动数字需要相对受击面板定位（原面板 position: static，用更高特异性覆盖，不影响 flex 布局） */
.battle-layout .battle-enemy,
.battle-layout .battle-player {
  position: relative;
}
/* 战斗入场动画（双方面板从天而降）：由 Web Animations API 驱动
 * （js/modules/combat.module.js 的 playBattleIntro），本文件不提供对应 class ——
 * 位移与缩放全部写在关键帧里，避免同名 CSS 动画偶发重放失败（同 .battle-lunge-* 的教训）。
 * 常量 FX_INTRO_MS(900ms)、落地比例 FX_INTRO_FALL(0.55)、起始放大 FX_INTRO_SCALE(2.5)、
 * 起始模糊 FX_INTRO_BLUR(10px)、起始不透明度 FX_INTRO_OPACITY(0.3) 都在该模块顶部。
 * 敌人面板自视口左上角、角色面板自视口右下角落下，起点由 battleIntroOffset 实测计算，
 * 保证放大后的面板整块都在视口内；入场期间 .battle-actions 的按钮被禁用。 */
/* 入场期间解除弹窗裁切：起点在弹窗之外，.modal 的 overflow: auto 会把面板切掉，
 * 表现为「一开始双方面板都不完全显示」（是父容器的滚动裁剪，不是 z-index 层级问题）。
 * 这个 class 由 setBattleIntroOverflow 添加，动画结束或取消时立刻移除。 */
.modal.battle-intro-open {
  overflow: visible;
}
/* 入场期间战斗按钮被禁用，给一个明确的不可用外观（只作用于战斗按钮行，
 * 不动 .vn-actions 之外其它位置的 disabled 观感）。 */
.battle-actions button:disabled {
  opacity: .55;
  cursor: not-allowed;
}
/* 撞击动画（重做版）：攻击面板斜向快速撞向受击面板并压入重叠，随后快速回原位。
 * **播放现由 Web Animations API 驱动**（js/game/13-combat.js playPanelLunge 用
 * element.animate() 直接播放 transform 位移，不再依赖本文件 class 重放——CSS 同名
 * 动画在特定帧/窗口状态下偶发重放失败，见 13-combat.js 中函数注释）。
 * 下方 .battle-lunge-right/.battle-lunge-left 与 @keyframes 保留为 WAAPI 不可用时的
 * 兜底，playPanelLunge 已不再添加这两个 class（只加仅提供 z-index 的
 * .battle-lunge-anim）；方向由 battleLungeVector 返回的带符号位移决定，class 不再承载方向。
 * 时序：0% 原位 → 35% 到达接触位（快速去程）→ 45%
 * 保持接触（受击方颤抖窗口）→ 100% 回原位（快速回程），总时长 0.4s。
 * 关键：不设 fill-mode: forwards，动画 100% 回到 translate(0,0)，复位不依赖
 * DOM 重建；时长必须与 js/game/13-combat.js 的 FX_LUNGE_MS(400) 同口径，
 * 且 < FX_ROUND_DELAY_MS(800)，否则敌人回合会在玩家动画未播完时进场。 */
.battle-lunge-right,
.battle-lunge-left {
  animation-duration: 0.4s;
  animation-timing-function: cubic-bezier(.2, .75, .3, 1);
  z-index: 5; /* 攻击面板盖在受击面板上，重叠时呈"压过去"的观感 */
}
.battle-lunge-right { animation-name: battle-lunge-right; }
.battle-lunge-left { animation-name: battle-lunge-left; }
/* WAAPI 主驱动 class：由 playPanelLunge 添加，仅提供撞击期间的 z-index
 * （攻击面板盖在受击面板上，重叠时呈"压过去"的观感）。JS 侧会先移除
 * battle-lunge-right/left 再加本 class，避免 CSS 动画与 WAAPI 双动画叠加冲突。 */
.battle-lunge-anim {
  z-index: 5;
}
@keyframes battle-lunge-right {
  0%   { transform: translate(0, 0); }
  35%  { transform: translate(var(--lunge-x, 130px), var(--lunge-y, -18px)); }
  45%  { transform: translate(var(--lunge-x, 130px), var(--lunge-y, -18px)); }
  100% { transform: translate(0, 0); }
}
@keyframes battle-lunge-left {
  0%   { transform: translate(0, 0); }
  35%  { transform: translate(var(--lunge-x, 130px), var(--lunge-y, 18px)); }
  45%  { transform: translate(var(--lunge-x, 130px), var(--lunge-y, 18px)); }
  100% { transform: translate(0, 0); }
}
/* 受击面板命中颤抖：命中（hit）时才触发（MISS 不抖），0.3s 内左右小幅抖动 4 次。
 * 延迟 0.15s 起抖 = 接触时刻（35% × 0.4s ≈ 0.14s）对齐，颤抖正好发生在两面板
 * 贴住时；若修改上方 keyframes 的接触百分比或动画时长，须同步重算此延迟。
 * 与撞击 class 分离：受击面板只加 battle-shake，攻击面板只加 battle-lunge-*。
 * 注：这套只给普攻用（跟着撞击动画的接触时刻起抖）。魔法命中改走下面的
 * .battle-hit-shake —— 没有撞击配合，用大幅无延迟的抖动才看得出来。 */
.battle-shake {
  animation: battle-shake 0.3s linear 0.15s;
}
@keyframes battle-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-5px); }
  30% { transform: translateX(5px); }
  45% { transform: translateX(-4px); }
  60% { transform: translateX(4px); }
  75% { transform: translateX(-3px); }
}
/* 魔法命中的颤抖：幅度更大、无延迟、带一点挤压感，让「被魔法打中」一眼能看出来 */
.battle-hit-shake {
  animation: battle-hit-shake .42s cubic-bezier(.36, .07, .19, .97);
}
@keyframes battle-hit-shake {
  0%   { transform: translate(0, 0) scale(1); }
  12%  { transform: translate(-11px, 3px) scale(1.03); }
  26%  { transform: translate(10px, -3px) scale(1.03); }
  40%  { transform: translate(-8px, 2px) scale(1.02); }
  55%  { transform: translate(7px, -2px) scale(1.01); }
  70%  { transform: translate(-5px, 1px) scale(1); }
  85%  { transform: translate(3px, 0) scale(1); }
  100% { transform: translate(0, 0) scale(1); }
}
/* 伤害数字/MISS：视口定位（position: fixed，坐标由 JS 按受击面板中心算出），
 * 不挂在面板里 —— 命中时紧接着会渲染一次血条，挂面板里会被重建清掉，
 * 扣血与数字就不同步了。迅速放大（scale 0.3→1.25）并快速淡出，
 * 总时长 600ms（需求 500~700ms），动画结束（animationend）由 JS 移除 DOM，防泄漏。 */
.battle-pop-num {
  position: fixed;
  z-index: 1200;
  font-weight: 800;
  font-size: 30px;
  line-height: 1;
  pointer-events: none;
  white-space: nowrap;
  transform: translate(-50%, -50%);
  transform-origin: center center;
  text-shadow: 0 1px 3px rgba(0,0,0,.7), 0 0 2px rgba(0,0,0,.8);
  animation: battle-pop 0.6s ease-out forwards;
}
.battle-pop-num.dmg {
  color: #ff6a4d;                                       /* 兜底：亮红，深色面板上看得清 */
  color: color-mix(in srgb, #ff3b2f 72%, #ffd166);
  text-shadow: 0 0 6px rgba(0, 0, 0, .95), 0 2px 4px rgba(0, 0, 0, .9), 0 0 14px rgba(255, 90, 60, .75);
}
.battle-pop-num.miss { color: #ffffff; }
@keyframes battle-pop {
  0% { transform: translate(-50%, -50%) scale(0.3); opacity: 1; }
  55% { transform: translate(-50%, -50%) scale(1.25); opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(1.1); opacity: 0; }
}
/* 战斗状态徽章：角色名旁小号显示状态与层数，点击查看详情 */
.battle-status-badges {
  display: inline-flex;
  flex-wrap: wrap;
  gap: .2rem .3rem;
  margin-left: .35rem;
  max-width: 100%;
  vertical-align: middle;
}
.battle-status-badge {
  display: inline-block;
  font-size: .68rem;
  line-height: 1.2;
  padding: .12rem .35rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  cursor: pointer;
  white-space: nowrap;
  vertical-align: middle;
  user-select: none;
  transition: filter .15s, border-color .15s;
}
/* 战斗状态徽章：颜色改由主题的好/坏色派生，浅色主题下不再是一块深绿深红 */
.battle-status-badge.good {
  color: #c8f5bd;                                             /* 兜底 */
  color: var(--good);
  background: rgba(33, 60, 40, .85);
  background: color-mix(in srgb, var(--good) 22%, var(--bg));
  border-color: rgba(123, 201, 111, .5);
  border-color: color-mix(in srgb, var(--good) 50%, var(--border));
}
.battle-status-badge.bad {
  color: #ffc9ce;                                             /* 兜底 */
  color: var(--bad);
  background: rgba(64, 30, 36, .85);
  background: color-mix(in srgb, var(--bad) 22%, var(--bg));
  border-color: rgba(224, 108, 117, .5);
  border-color: color-mix(in srgb, var(--bad) 50%, var(--border));
}
.battle-status-badge:hover {
  filter: brightness(1.3);
  border-color: var(--accent);
}
  .item-name { font-weight: bold; }
  .item-meta { color: var(--muted); font-size: .75rem; }
  .tag {
    display:inline-block;
    background: var(--bg3);                                   /* 兜底 */
    background: color-mix(in srgb, var(--text) 14%, var(--bg));
    border-radius:6px;
    padding:0 .35rem;
    font-size:.7rem;
    margin-left:.3rem;
    color:var(--accent2);
  }
  .tag.type {
    color: var(--text);
    background: var(--panel);
    background: color-mix(in srgb, var(--text) 18%, var(--bg));
  }
  .tag.good {
    color: var(--good);
    background: color-mix(in srgb, var(--good) 20%, var(--bg));
  }
  .tag.bad {
    color: var(--bad);
    background: color-mix(in srgb, var(--bad) 20%, var(--bg));
  }
  /* ===== 四星及以上装备的外发光（背包 / 杂物栏 / 仓库 / 装备栏 / 商品栏通用） =====
   * 颜色按品质档走：完美=紫、传说=金、神话=粉紫、龙鳞=彩虹；三星及以下不发光。
   * 光效**直接挂在格子自身的 box-shadow 上**：box-shadow 跟着元素的 border box 与圆角走，
   * 格子是 88×76 也好、96×96 也好、长方形卡片也好，光效的比例永远与格子一致 ——
   * 用伪元素画描边就会脱离格子本身的宽高比（用户报的「格子不是 1:1、光效却是 1:1」）。
   * 判定口径见 util.module.js 的 itemGlowClass()。 */
  .item-glow {
    animation: itemGlowBreath 2.6s ease-in-out infinite;
  }
  @keyframes itemGlowBreath {
    0%, 100% { box-shadow: 0 0 0 1px var(--glow-c, #cfd6e0), 0 0 9px 1px var(--glow-s, rgba(207, 214, 224, .7)); }
    50%      { box-shadow: 0 0 0 2px var(--glow-c, #cfd6e0), 0 0 18px 5px var(--glow-s, rgba(207, 214, 224, .7)); }
  }
  .item-glow-g4 { --glow-c: #b07ce8; --glow-s: rgba(176, 124, 232, .78); }
  .item-glow-g5 { --glow-c: #e8b23d; --glow-s: rgba(232, 178, 61, .82); }
  .item-glow-g6 { --glow-c: #e07ab0; --glow-s: rgba(224, 122, 176, .85); }
  /* 六星（神话）：格子的外发光本身要彩色流转（用户口径）。
   * 用关键帧逐段换 box-shadow 的颜色，不动格子里的文字颜色 —— 与龙鳞彩虹同一手法。 */
  .item-glow-g6 { animation: mythGlow 6s linear infinite; }
  @keyframes mythGlow {
    0%   { box-shadow: 0 0 0 1px #e07ab0, 0 0 10px 2px rgba(224, 122, 176, .75); }
    25%  { box-shadow: 0 0 0 2px #e8c34a, 0 0 14px 4px rgba(232, 195, 74, .78); }
    50%  { box-shadow: 0 0 0 2px #5ad0a8, 0 0 14px 4px rgba(90, 208, 168, .78); }
    75%  { box-shadow: 0 0 0 2px #5aa9e6, 0 0 14px 4px rgba(90, 169, 230, .78); }
    100% { box-shadow: 0 0 0 1px #e07ab0, 0 0 10px 2px rgba(224, 122, 176, .75); }
  }
  /* 六枚龙鳞：彩虹外发光（逐档换 box-shadow 的颜色，不动格子里文字的颜色） */
  .item-glow-dragon { animation: dragonGlow 4.5s linear infinite; }
  @keyframes dragonGlow {
    0%   { box-shadow: 0 0 0 1px #ff6b6b, 0 0 12px 2px rgba(255, 107, 107, .75); }
    25%  { box-shadow: 0 0 0 2px #ffd93d, 0 0 16px 4px rgba(255, 217, 61, .78); }
    50%  { box-shadow: 0 0 0 2px #6bcbff, 0 0 16px 4px rgba(107, 203, 255, .78); }
    75%  { box-shadow: 0 0 0 2px #a86bff, 0 0 16px 4px rgba(168, 107, 255, .78); }
    100% { box-shadow: 0 0 0 1px #ff6b6b, 0 0 12px 2px rgba(255, 107, 107, .75); }
  }
  /* 无障碍：命中减少动效时保留一圈静态光，停掉呼吸 */
  @media (prefers-reduced-motion: reduce) {
    .item-glow, .item-glow-dragon { animation: none; box-shadow: 0 0 0 2px var(--glow-c, #cfd6e0), 0 0 12px 2px var(--glow-s, rgba(207, 214, 224, .7)); }
  }

  /* ===== 角色状态面板的状态徽章（可点击查看详情） ===== */
  .status-badge-row {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin: 0 0 .5rem;
  }
  .status-badge {
    font-size: .76rem;
    padding: .18rem .55rem;
    border-radius: 999px;
    cursor: pointer;
    background: var(--bg2);
    border: 1px solid var(--border);
    color: var(--text);
  }
  .status-badge.good {
    color: var(--good);
    background: color-mix(in srgb, var(--good) 14%, var(--bg2));
    border-color: color-mix(in srgb, var(--good) 55%, var(--border));
  }
  .status-badge.bad {
    color: var(--bad);
    background: color-mix(in srgb, var(--bad) 14%, var(--bg2));
    border-color: color-mix(in srgb, var(--bad) 55%, var(--border));
  }
  .status-badge:hover {
    border-color: var(--accent);
  }
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.66);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    z-index: 1000;
  }
  .modal {
    width: min(720px, 100%);
    max-height: 85vh;
    overflow: auto;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 1rem;
    box-shadow: var(--shadow);
  }
  .modal h2 { margin: 0 0 .5rem; color: var(--accent); }
  .modal h3 { color: var(--accent2); }
  .modal .close-x {
    float: right;
    background: none;
    border: none;
    font-size: 1.2rem;
    color: var(--muted);
  }
  .row {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: .5rem 0;
  }
  .full { width: 100%; }
  .small { font-size: .78rem; color: var(--muted); }
  .log-card {
    border-left: 4px solid var(--border);
    font-size: .85rem;
  }
  .log-card .day { color: var(--accent2); font-weight: bold; }
  .end-banner {
    background: linear-gradient(145deg, var(--panel2), var(--panel));
    border: 1px solid var(--border);
    border-color: color-mix(in srgb, var(--magic) 55%, var(--border));
    border-radius: 16px;
    padding: 1.5rem;
    text-align: center;
    margin: 1rem 0;
  }
  .end-banner h2 { color: var(--accent); margin: 0; }
  .button-row button { margin-right: .3rem; }
  .desc { color: var(--muted); font-size: .9rem; }
  @media (max-width: 640px) {
    .title-name { font-size: clamp(2.8rem, 14vw, 4rem); }
    .topbar { flex-direction: column; align-items: stretch; }
    .log-float { width: max-content; max-width: 34vw; height: 48vh; min-height: 48vh; max-height: 48vh; padding: .4rem .45rem; }
  }

/* 行动按钮整体靠右 */
.panel .row { justify-content: flex-end; }
.panel > .row { justify-content: flex-end; }

/* 所有确认/行动按钮区域统一靠右 */
.row {
  justify-content: flex-end !important;
}
.row .full {
  width: 100%;
}

/* 仓库车厢标题行（需求）：标题靠左。必须写在这里并提高优先级 —— 上面的
 * `.row { justify-content: flex-end !important }` 同为 !important 且优先级相同，
 * 只按源码顺序决胜，任何写在它前面的单类规则都会被压掉（实测标题仍在右端）。
 * 用 space-between：标题贴左，有厨房时右侧的「烹饪」按钮仍贴右；没有按钮时单项靠左。 */
.panel .row.wh-title-row {
  justify-content: space-between !important;
  align-items: center;
}

/* 存档卡片的标题行（游戏内「读取存档 / 选择覆盖」与标题页「继续游戏」的存档列表）：
 * 标题靠左、动作按钮或提示靠右。
 * 与 .wh-title-row 同理，必须写在这里并提高优先级 —— 上面的
 * `.row { justify-content: flex-end !important }` 会把内联的 space-between 压掉，
 * 标题因此被推到右端（用户反馈）。标题页的卡片不在 .panel 里，所以两种写法都列上。 */
.row.save-slot-head,
.panel .row.save-slot-head {
  justify-content: space-between !important;
  align-items: center;
}

/* 浅色主题滚动条：黑半透明，避免白 thumb 在浅底上近隐形（v0.3.49 待办闭合） */
body[data-theme="candy"], body[data-theme="mint"] {
  --scrollbar-thumb: rgba(0,0,0,.18);
  --scrollbar-thumb-hover: rgba(0,0,0,.30);
}

/* ===== 浅色可爱主题：奶糖梦境 ===== */
body[data-theme="candy"] {
  background:
    radial-gradient(circle at 20% 20%, #ffeaf3 0%, transparent 45%),
    radial-gradient(circle at 80% 80%, #e8fff5 0%, transparent 50%),
    var(--bg);
  color: var(--text);
}
body[data-theme="candy"] .title-card {
  background: linear-gradient(145deg, #fff9fc, #ffeaf3);
  border-color: #f6c6dc;
  box-shadow: 0 8px 30px rgba(214, 122, 164, .18);
}
/* 标题颜色已由主题变量接管，不再叠加主题色光晕（保持简洁） */
body[data-theme="candy"] .title-sub,
body[data-theme="candy"] .hint,
body[data-theme="candy"] .small,
body[data-theme="candy"] .bar-label,
body[data-theme="candy"] .item-meta,
body[data-theme="candy"] .desc { color: var(--muted); }
body[data-theme="candy"] .btn-primary { background: #ffd6e8; border-color: #f49ac1; color: #6d4157; }
body[data-theme="candy"] .btn-danger { background: #ffd6de; border-color: #e27a8e; color: #8c3f4f; }
body[data-theme="candy"] .btn-magic { background: #f3dcff; border-color: #d59ae8; color: #6b3d7a; }
body[data-theme="candy"] .btn-good { background: #d8f5e2; border-color: #8fd6a8; color: #2f6b45; }
body[data-theme="candy"] .bar { background: #f7dce8; }
body[data-theme="candy"] .tag { background: #ffe3ef; color: #a56d84; }
body[data-theme="candy"] .tag.type { background: #f9dbe9; color: #6d4157; }
body[data-theme="candy"] .tag.good { background: #d8f5e2; color: #2f7a52; }
body[data-theme="candy"] .tag.bad { background: #ffd6de; color: #a8465b; }
body[data-theme="candy"] .modal-backdrop { background: rgba(109, 65, 87, .22); }
body[data-theme="candy"] .end-banner { background: linear-gradient(145deg, #fff0f7, #ffe7f1); border-color: #f49ac1; }
body[data-theme="candy"] .log-card { border-left-color: #f6c6dc; }

/* ===== 浅色可爱主题：薄荷奶昔 ===== */
body[data-theme="mint"] {
  background:
    radial-gradient(circle at 20% 20%, #e4fff5 0%, transparent 45%),
    radial-gradient(circle at 80% 80%, #eefaff 0%, transparent 50%),
    var(--bg);
  color: var(--text);
}
body[data-theme="mint"] .title-card {
  background: linear-gradient(145deg, #f7fffb, #e4fff5);
  border-color: #b5e8d8;
  box-shadow: 0 8px 30px rgba(94, 201, 168, .18);
}
/* 标题颜色已由主题变量接管，不再叠加主题色光晕（保持简洁） */
body[data-theme="mint"] .title-sub,
body[data-theme="mint"] .hint,
body[data-theme="mint"] .small,
body[data-theme="mint"] .bar-label,
body[data-theme="mint"] .item-meta,
body[data-theme="mint"] .desc { color: var(--muted); }
body[data-theme="mint"] .btn-primary { background: #d8fff0; border-color: #5ec9a8; color: #2f5d52; }
body[data-theme="mint"] .btn-danger { background: #ffd6de; border-color: #e27a8e; color: #8c3f4f; }
body[data-theme="mint"] .btn-magic { background: #dff2ff; border-color: #7fc5e8; color: #315d75; }
body[data-theme="mint"] .btn-good { background: #d8f5e2; border-color: #8fd6a8; color: #2f6b45; }
body[data-theme="mint"] .bar { background: #d6f5e8; }
body[data-theme="mint"] .tag { background: #dcfff0; color: #4f8b79; }
body[data-theme="mint"] .tag.type { background: #e3f9f1; color: #2f5d52; }
body[data-theme="mint"] .tag.good { background: #d8f5e2; color: #2f7a52; }
body[data-theme="mint"] .tag.bad { background: #ffd6de; color: #a8465b; }
body[data-theme="mint"] .modal-backdrop { background: rgba(47, 93, 82, .22); }
body[data-theme="mint"] .end-banner { background: linear-gradient(145deg, #ecfff7, #dcfff0); border-color: #5ec9a8; }
body[data-theme="mint"] .log-card { border-left-color: #b5e8d8; }

/* ===== 浅色主题按钮选中/悬停修复 ===== */
body[data-theme="candy"] .tabs button,
body[data-theme="candy"] button {
  background: var(--bg2);
  border-color: var(--border);
  color: var(--text);
}
body[data-theme="candy"] button:hover {
  background: var(--panel2);
  border-color: var(--accent);
}
body[data-theme="candy"] .tabs button.active {
  background: var(--panel2);
  border-color: var(--accent);
  color: #6d4157;
}
body[data-theme="candy"] .btn-primary { background: #ffd6e8; border-color: #f49ac1; color: #6d4157; }
body[data-theme="candy"] .btn-danger { background: #ffd6de; border-color: #e27a8e; color: #8c3f4f; }
body[data-theme="candy"] .btn-magic { background: #f3dcff; border-color: #d59ae8; color: #6b3d7a; }
body[data-theme="candy"] .btn-good { background: #d8f5e2; border-color: #8fd6a8; color: #2f6b45; }

body[data-theme="mint"] .tabs button,
body[data-theme="mint"] button {
  background: var(--bg2);
  border-color: var(--border);
  color: var(--text);
}
body[data-theme="mint"] button:hover {
  background: var(--panel2);
  border-color: var(--accent);
}
body[data-theme="mint"] .tabs button.active {
  background: var(--panel2);
  border-color: var(--accent);
  color: #2f5d52;
}
body[data-theme="mint"] .btn-primary { background: #d8fff0; border-color: #5ec9a8; color: #2f5d52; }
body[data-theme="mint"] .btn-danger { background: #ffd6de; border-color: #e27a8e; color: #8c3f4f; }
body[data-theme="mint"] .btn-magic { background: #dff2ff; border-color: #7fc5e8; color: #315d75; }
body[data-theme="mint"] .btn-good { background: #d8f5e2; border-color: #8fd6a8; color: #2f6b45; }

/* ===== galgame VN 对话层（z-index 在 modal 之下，交易/送礼弹窗可盖在其上） ===== */
#vn-layer {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  /* 半透明 + 背景虚化：保留游戏界面原貌作为沉浸式背板，对话元素自身保持不透明 */
  background: rgba(11, 14, 20, 0.66);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: var(--text);
  font-family: "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif;
  padding: 1rem;
}
#vn-layer.vn-hidden { display: none !important; }

/* VN 居中容器：不再占满全屏（类似 openModal 弹窗尺寸） */
#vn-layer .vn-container {
  width: min(90vw, 820px);
  max-height: min(85vh, 720px);
  display: flex;
  flex-direction: column;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: var(--shadow);
  overflow: hidden;
}

.vn-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .4rem .7rem;
  background: var(--bg3);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  gap: .4rem;
  flex-wrap: wrap;
  font-size: .8rem;
}
.vn-favor {
  display: flex;
  align-items: center;
  gap: .3rem;
  font-size: .78rem;
}
.vn-favor-icon { font-size: .9rem; }
.vn-favor-stage { font-weight: 600; min-width: 2.6em; }
.vn-favor-bar {
  width: 52px; height: 5px;
  background: var(--bg); border-radius: 4px; overflow: hidden;
  display: inline-block;
}
.vn-favor-bar i {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent2));
  border-radius: 4px;
  transition: width .3s;
}
.vn-favor-num { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .7rem; }
.vn-controls { display: flex; gap: .25rem; }
.vn-controls button {
  font-size: .7rem; padding: .2rem .4rem;
  background: var(--bg); border-color: var(--border);
}
.vn-controls button.vn-on { background: var(--accent); color: #000; border-color: var(--accent); }

.vn-stage {
  flex: 1;
  display: flex;
  align-items: stretch;
  padding: .5rem 1rem;
  gap: .6rem;
  overflow: hidden;
  cursor: pointer;
  min-height: 0;
}
.vn-portrait {
  width: min(120px, 24vw);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: .3rem;
}
.vn-portrait-img {
  width: 100%; height: auto;
  border-radius: 12px;
  border: 1px solid var(--border);
}
.vn-portrait-ph {
  width: 100%; aspect-ratio: 3/4;
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: clamp(1.6rem, 5vw, 2.8rem);
  color: var(--muted);
  opacity: .55;
}
.vn-portrait-name {
  font-weight: 700;
  text-align: left;
  font-size: .8rem;
  color: var(--text);
  width: 100%;
  padding: .1rem .5rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 小型头像（怪物 / 店长）：44px 方图位，复用小头像组件的图片与剪影占位。
 * 无图时 VN_portraitRetry 会隐藏 img 并显示 .vn-portrait-ph 首字母剪影。 */
.mini-portrait { display:inline-block; width:44px; height:44px; border-radius:8px; overflow:hidden; vertical-align:middle; margin-right:6px; background:rgba(0,0,0,.18); }
.mini-portrait .vn-portrait-ph { width:44px; height:44px; line-height:44px; font-size:20px; }
.mini-portrait .vn-portrait-img { width:44px; height:44px; object-fit:cover; }
.vn-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: .3rem;
  min-width: 0;
}
.vn-nameplate {
  font-size: .9rem; font-weight: 700;
  color: var(--accent2);
  padding: .15rem .5rem;
  background: var(--bg3);
  border-radius: 8px 8px 0 0;
  display: inline-block;
  align-self: flex-start;
}
.vn-textbox {
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: .6rem .7rem;
  min-height: 2.6em;
  position: relative;
  font-size: .9rem;
  line-height: 1.5;
  cursor: pointer;
}
.vn-caret {
  position: absolute;
  right: 8px; bottom: 4px;
  font-size: .65rem;
  color: var(--accent);
  animation: vnCaretPulse 1s ease-in-out infinite;
}
@keyframes vnCaretPulse {
  0%, 100% { opacity: .3; transform: translateY(0); }
  50%      { opacity: 1; transform: translateY(2px); }
}
.vn-hint {
  font-size: .66rem;
  color: var(--muted);
  text-align: center;
  margin: .2rem 0 0;
  opacity: .5;
}
.vn-options {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .3rem;
  padding: .3rem .8rem .5rem;
  flex-shrink: 0;
  max-height: 40vh;
  overflow-y: auto;
}
.vn-options.vn-hidden { display: none; }
.vn-option {
  width: 100%;
  text-align: left;
  padding: .5rem .65rem;
  border-radius: 8px;
  background: var(--bg3);
  border: 1px solid var(--border);
  color: var(--text);
  cursor: pointer;
  font-size: .85rem;
  transition: background .12s, border-color .12s, transform .05s;
}
.vn-option:hover { background: var(--panel2); border-color: var(--accent); }
.vn-option:active { transform: translateY(1px); }

.vn-actions {
  display: flex;
  flex-wrap: wrap;
  /* 用户口径：交易 / 送礼 / 攻击 / 离开这排功能入口一律靠右，别贴着左边的台词区。
   * 折行时每一行也各自靠右；车长那条「无法攻击」的提示是 flex item，
   * 同样落在右侧、排在按钮左边。 */
  justify-content: flex-end;
  gap: .35rem;
  padding: .35rem .7rem .5rem;
  flex-shrink: 0;
  border-top: 1px solid var(--border);
  background: var(--bg);
}
.vn-actions .vn-note { font-size: .72rem; color: var(--muted); align-self: center; }
.vn-actions button { font-size: .76rem; padding: .25rem .45rem; }
.vn-actions button:disabled { opacity: .4; cursor: not-allowed; }

.vn-panel {
  position: absolute;
  inset: 0;
  background: var(--bg2);
  z-index: 10;
  overflow-y: auto;
  padding: .8rem;
}
.vn-panel.vn-hidden { display: none; }
.vn-history-row { font-size: .8rem; margin: .2rem 0; }
.vn-history-row b { color: var(--accent2); }

/* ===== 隐藏测试工具按钮（js/game/18-devtools.js，仅主界面 game-shell 渲染） =====
 * 定位：视口左下角紧贴边缘；字号极小 + 低透明度 + 颜色贴近深色背景，达到
 * 「几乎看不见但可点击」；min-width 留出点击余量；z-index 低于弹窗(1000)/VN层(900)，
 * 不遮挡游戏操作，也不被标题界面渲染（该按钮只随 gameScreenHTML 输出）。 */
.test-btn {
  position: fixed;
  left: 2px;
  bottom: 2px;
  z-index: 50;
  font-size: 8px;
  line-height: 1;
  padding: 3px 6px;
  min-width: 24px;
  color: #141a26;
  background: transparent;
  border: 1px solid rgba(49, 64, 95, 0.28);
  border-radius: 4px;
  opacity: 0.25;
  cursor: pointer;
  transition: opacity .15s;
}
.test-btn:hover { opacity: 0.7; color: var(--text); }
.test-btn:active { transform: none; }

/* ===== 温室（js/game/20-greenhouse.js）：种植区 + 堆肥区 ===== */
.greenhouse-layout {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.greenhouse-plots-panel { flex: 1 1 420px; min-width: 300px; }
.greenhouse-compost-panel { flex: 0 0 210px; }

/* 种植田：6 列网格，正方形田格（限宽避免格子过大） */
.plot-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
  max-width: 640px;
}
.plot-cell {
  aspect-ratio: 1 / 1;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
  font-size: .8rem;
  text-align: center;
  padding: 4px;
  min-width: 0;
  overflow: hidden;
  transition: border-color .15s, transform .1s;
}
.plot-cell:hover { border-color: var(--accent); }
.plot-cell:active { transform: translateY(1px); }
.plot-cell .plot-empty-text { color: var(--muted); font-size: .75rem; }
.plot-cell.locked {
  opacity: .25;
  cursor: default;
  border-style: dashed;
  font-size: .9rem;
}
.plot-cell.locked:hover { border-color: var(--border); }
.plot-cell.crop { background: var(--panel2); border-color: var(--accent); }
.plot-cell.ready { border-color: var(--good); }
.plot-cell .plot-emoji { font-size: 1.5rem; line-height: 1; }
.plot-cell.ready .plot-emoji { font-size: 1.7rem; }
.plot-cell .plot-crop-name { font-size: .82rem; line-height: 1.2; word-break: break-all; }
.plot-cell .plot-remain { color: var(--muted); font-size: .68rem; line-height: 1.2; }
.plot-cell .plot-ready { color: var(--good); font-size: .75rem; font-weight: bold; }

/* 堆肥区：2 格竖排 */
.compost-list { display: flex; flex-direction: column; gap: 10px; }
.compost-cell {
  border: 1px dashed var(--border);
  border-radius: 10px;
  padding: 14px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  text-align: center;
  min-height: 96px;
  background: var(--bg3);
  font-size: .8rem;
  transition: border-color .15s, transform .1s;
}
.compost-cell:hover { border-color: var(--accent); }
.compost-cell:active { transform: translateY(1px); }
.compost-cell.ready { border-style: solid; border-color: var(--good); }
.compost-cell .plot-remain { color: var(--muted); font-size: .7rem; }
.compost-cell .plot-ready { color: var(--good); font-size: .75rem; font-weight: bold; }

/* 收获按钮：作物成熟后这是唯一该点的操作，用绿色实底 + 缓呼吸光圈把它顶出来。
 * 颜色取 var(--good)，各主题（candy/mint 等）会跟着自己的配色走，不会突兀。 */
button.btn-harvest {
  background: var(--good);
  border-color: var(--good);
  color: #10231a;
  font-weight: bold;
  animation: harvestPulse 1.8s ease-in-out infinite;
}
button.btn-harvest:hover {
  background: var(--good);
  border-color: #fff;
  transform: translateY(-1px);
}
@keyframes harvestPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(123, 201, 111, 0); }
  50% { box-shadow: 0 0 0 4px rgba(123, 201, 111, .35); }
}

/* 种植/堆肥选择列表（弹窗内） */
.crop-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px dashed var(--border);
}
.crop-row:last-child { border-bottom: none; }
.crop-info { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ===== 探索界面顶部时钟 =====
 * 探索面板里原本看不到时间（只有顶栏一行小字），这里给一个大号钟面。
 * 昼间走主题金，夜间（18:00 起，与 isNightExploring 的体力惩罚同一口径）转紫色并点出夜间代价。 */
.explore-clock {
  display: flex;
  align-items: center;
  gap: .9rem;
  flex-wrap: wrap;
  padding: .55rem .9rem;
  border: 1px solid var(--accent);
  border-radius: 12px;
  background: linear-gradient(90deg, rgba(224,164,88,.16), rgba(224,164,88,.02));
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
}
.explore-clock .clock-face {
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .05em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 14px rgba(224,164,88,.4);
}
.explore-clock .clock-meta { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
.explore-clock .clock-date { font-size: .95rem; }
.explore-clock .clock-phase { font-size: .8rem; color: var(--muted); }
/* 时钟卡右侧的「跳过时间」（用户口径）：与列车页那个同款动作，就地消磨时间。
 * margin-left: auto 把它推到最右；卡片自带 flex-wrap，窄屏折行时它会落到下一行右侧。 */
.explore-clock .clock-skip { margin-left: auto; flex: 0 0 auto; }
.explore-clock.is-night {
  border-color: var(--magic);
  background: linear-gradient(90deg, rgba(198,120,221,.2), rgba(198,120,221,.03));
}
.explore-clock.is-night .clock-face {
  color: var(--magic);
  text-shadow: 0 0 16px rgba(198,120,221,.5);
}
@media (max-width: 640px) {
  .explore-clock .clock-face { font-size: 1.9rem; }
}

/* ===== 列车速度 / 装甲防御行（顶栏固定列车面板与列车页共用）=====
 * 需求：原先这两项只在列车页用 .small 小字显示，太淡；改为带底色的行内卡片，
 * 数值单独放大加粗（防御文案固定为「列车的装甲足以防御 N 点伤害」）。 */
/* 列车页的速度/装甲/列车币（需求）：不要外框，字号再收一档，靠缩进与颜色分层。 */
.train-stat {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .3rem;
  margin: .22rem 0;
  padding: 0;
  font-size: .86rem;
}
.train-stat-icon { font-size: 1rem; display: inline-block; min-width: 1.5em; text-align: center; }
.train-stat-label { color: var(--text); font-weight: 600; }
/* 数字统一样式（需求）：列车页的速度、装甲、列车币共用一套，大小与字重完全一致。 */
.train-num {
  font-size: 1.05rem;
  font-weight: 800;
  line-height: 1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 12px rgba(224,164,88,.35);
}
.train-stat-unit { color: var(--muted); }
.train-stat-hint { margin-left: auto; color: var(--accent2); font-size: .82rem; }
/* 顶栏固定列车面板（需求）：不要外框、字号小一档。 */
.train-stat.is-compact {
  border: none;
  background: none;
  padding: 0;
  margin: .18rem 0;
  gap: .3rem;
  font-size: .82rem;
}
.train-stat.is-compact .train-num { font-size: .95rem; text-shadow: none; }
/* 列车页时间行（需求）：显示在两个状态条上方。
 * 末尾的「跳过时间」按钮（用户口径）：跟顶栏那个入口同款动作，就地消磨时间；
 * 按钮贴右，时间文字用 span 并把它压平（row 的 align-items 默认拉伸，
 * 不压平的话按钮会把「⏱ 时间」拉到行顶部）。 */
.train-time {
  display: flex;
  align-items: center;
  gap: .5rem;
  color: var(--accent2);
  font-size: .85rem;
  margin: .1rem 0 .45rem;
  font-variant-numeric: tabular-nums;
}
.train-time > span { line-height: 1; }
.train-time button { margin-left: auto; }
/* 列车币行：与速度/装甲同构的排布，数字走 .train-num。 */
.train-coin {
  display: flex;
  align-items: baseline;
  gap: .3rem;
  margin: .22rem 0;
  padding: 0;
  font-size: .86rem;
}

/* 搜索结算：收获物品加粗加大（需求），损失弱化一档以示区分。 */
.loot-gain { color: var(--muted); margin: .4rem 0 0; }
.loot-gain-item {
  font-size: 1.12em;
  font-weight: 800;
  color: var(--accent);
  margin: 0 .1rem;
}
.loot-loss-item { font-size: 1.05em; font-weight: 700; color: var(--danger, #d4674f); margin: 0 .1rem; }
/* 点任意处关闭的弹窗（需求）：整层可点，鼠标给出提示。 */
.modal-backdrop.modal-anywhere { cursor: pointer; }
.modal-dismiss-hint { text-align: center; margin-top: .6rem; opacity: .7; }
/* 搜索结算弹窗里的图纸发现（需求）：获得图纸这类事件在弹窗里突出显示。 */
.blueprint-found {
  color: var(--accent);
  font-weight: 700;
  background: linear-gradient(90deg, rgba(224,164,88,.16), rgba(255,255,255,.02));
  border-left: 3px solid var(--accent);
  padding: .35rem .5rem;
  border-radius: 4px;
  margin-top: .45rem;
}

/* ===== 列车开火（需求）：敌人出现时列车先打一轮，敌人面板抖一下 ===== */
@keyframes trainStrikeShake {
  0%, 100% { transform: translateX(0) rotate(0deg); }
  12% { transform: translateX(-7px) rotate(-0.5deg); }
  26% { transform: translateX(6px) rotate(0.5deg); }
  40% { transform: translateX(-5px) rotate(-0.3deg); }
  55% { transform: translateX(4px) rotate(0.3deg); }
  72% { transform: translateX(-2px); }
  88% { transform: translateX(1px); }
}
.train-strike-shake { animation: trainStrikeShake 0.6s ease-in-out; }

/* ============================================================
 * 主线提示条
 * 停靠的站点上有待触发的主线节点时出现在状态区下方。
 * 只暗示有这么一件事，不写内容；点一下才打开节点。
 * ============================================================ */
.mainline-banner {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: .55rem 0;
  padding: .62rem .95rem;
  border: 1px solid var(--accent);
  border-radius: 10px;
  background: linear-gradient(90deg, rgba(232, 192, 125, .18), rgba(232, 192, 125, .04));
  color: var(--text);
  cursor: pointer;
  animation: mainlinePulse 2.6s ease-in-out infinite;
  user-select: none;
}
.mainline-banner:hover {
  background: linear-gradient(90deg, rgba(232, 192, 125, .3), rgba(232, 192, 125, .1));
}
.mainline-banner-icon {
  color: var(--accent);
  font-size: 1.05rem;
  line-height: 1;
}
.mainline-banner-text {
  flex: 1;
  font-weight: 600;
  letter-spacing: .04em;
}
.mainline-banner-go {
  font-size: .78rem;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding: .1rem .55rem;
  white-space: nowrap;
}
@keyframes mainlinePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(232, 192, 125, 0); }
  50% { box-shadow: 0 0 0 4px rgba(232, 192, 125, .16); }
}

/* ================= 主线手记：摊开的册子 =================
 * 入口在底部页签（背包右边、事件日志左边）。左页写进度与还没走到的线索，
 * 右页是一篇篇日记。纸色走暖调，与外面深色界面区分开。 */
.journal-wrap { padding: .2rem 0 1.4rem; }

.journal-book {
  display: flex;
  max-width: 1120px;
  margin: 0 auto;
  position: relative;
  color: #3b2f21;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255,252,242,.92), rgba(240,229,206,.96)),
    linear-gradient(180deg, #f7f0e1, #ece0c9);
  border: 1px solid #c9b998;
  border-radius: 8px;
  box-shadow: 0 14px 34px rgba(0,0,0,.5), inset 0 0 70px rgba(122,96,58,.14);
  overflow: hidden;
}
/* 稿纸似的极淡横线 */
.journal-book::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .7;
  background: repeating-linear-gradient(180deg, rgba(122,96,58,.055) 0 1px, transparent 1px 30px);
}
.journal-spine {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 46px; transform: translateX(-50%);
  background: linear-gradient(90deg, rgba(122,96,58,0) 0%, rgba(122,96,58,.20) 45%,
    rgba(122,96,58,.28) 50%, rgba(122,96,58,.20) 55%, rgba(122,96,58,0) 100%);
  pointer-events: none;
}
.journal-page { flex: 1 1 0; min-width: 0; padding: 1.5rem 1.9rem 2rem; position: relative; z-index: 1; }
.journal-page-left { padding-right: 2.4rem; }
.journal-page-right { padding-left: 2.4rem; }

.journal-head { font-family: 'MaDao Title', serif; font-size: 1.6rem; letter-spacing: .35rem; color: #57422c; }
.journal-sub { margin-top: .3rem; font-size: .8rem; color: #8b7454; letter-spacing: .06rem; }
.journal-rule { height: 1px; margin: .9rem 0 1rem; background: linear-gradient(90deg, rgba(122,96,58,.55), rgba(122,96,58,.06)); }
.journal-progress { font-size: .86rem; color: #6b5638; letter-spacing: .04rem; }

.journal-clues { margin-top: 1.3rem; }
.journal-clues-title { font-size: .82rem; color: #8b7454; letter-spacing: .18rem; margin-bottom: .5rem; }
.journal-clue { display: flex; align-items: baseline; gap: .45rem; padding: .28rem 0; border-bottom: 1px dashed rgba(122,96,58,.22); }
.journal-clue-dot { width: 5px; height: 5px; border-radius: 50%; background: #a98a5c; flex: 0 0 auto; }
.journal-clue-name { flex: 1 1 auto; font-size: .88rem; }
.journal-clue-at { font-size: .76rem; color: #8b7454; }
.journal-clues-empty { margin-top: 1.2rem; font-size: .82rem; color: #8b7454; }

.journal-entry { padding: .1rem 0 .2rem; }
.journal-date { font-size: .74rem; color: #96795a; letter-spacing: .12rem; }
.journal-entry-title { margin: .18rem 0 .4rem; font-size: 1.02rem; color: #4a3826; letter-spacing: .06rem; }
.journal-entry-text { margin: 0; font-size: .9rem; line-height: 1.95; text-indent: 1.8em; color: #4a3c2b; }
.journal-entry-rule { height: 1px; margin: .9rem 0; background: linear-gradient(90deg, rgba(122,96,58,.3), rgba(122,96,58,0)); }
.journal-empty { padding: 2.4rem 0; text-align: center; font-size: .9rem; line-height: 2; color: #8b7454; }

@media (max-width: 900px) {
  .journal-book { flex-direction: column; }
  .journal-spine { display: none; }
  .journal-page-left { padding-right: 1.9rem; border-bottom: 1px dashed rgba(122,96,58,.3); }
  .journal-page-right { padding-left: 1.9rem; }
}

/* 手记弹窗：册子要立得起来，不能扁成一条。
 * .modal 默认 width 720px、max-height 85vh，这里用 :has() 只对含册子的弹窗放宽。 */
.modal:has(.journal-book) {
  width: min(1080px, 94vw);
  padding: .9rem 1.1rem 1.1rem;
}
.journal-wrap { padding: 0; }
.journal-book { min-height: min(60vh, 540px); }
.journal-page { display: flex; flex-direction: column; }
.journal-page-right { overflow-y: auto; max-height: min(54vh, 480px); }
.journal-page-left { overflow-y: auto; max-height: min(54vh, 480px); }
.journal-spine { width: 40px; }

/* 顶栏拆成两行：第一行是车名与状态，第二行只放常用的四个，贴着右边。
 * .topbar 自身仍是 flex-wrap，这两个 row 各占一整行。 */
.topbar-row { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; flex: 1 1 100%; }
.topbar-row-right { justify-content: flex-end; }

/* 手记左页：已经走到的清单，以及下一件事的侧面提示 */
.journal-walked {
  display: flex; align-items: baseline; gap: .45rem;
  padding: .22rem 0; border-bottom: 1px dashed rgba(122,96,58,.18);
}
.journal-goal {
  margin-top: 1rem; padding: .7rem .8rem;
  border: 1px dashed rgba(122,96,58,.4); border-radius: 6px;
  background: rgba(122,96,58,.06);
}
.journal-goal-title { font-size: .76rem; letter-spacing: .18rem; color: #8b7454; margin-bottom: .35rem; }
.journal-goal-text { font-size: .86rem; line-height: 1.8; color: #4a3c2b; }

/* 手记再窄一些：书不必摊得那么开，窄一点反而更像册子 */
.modal:has(.journal-book) {
  width: min(860px, 92vw);
}


/* ===== 成就系统 =====
 * 图鉴成就卡片与右下角解锁提示共用五档稀有度配色：
 * Ⅰ 木褐 / Ⅱ 红铜 / Ⅲ 亮银 / Ⅳ 金闪闪 / Ⅴ 彩色流动
 * 配色只跟稀有度走，与是否解锁无关。 */

/* 卡片头部一行三件：名称在最左，等级紧挨名称右边且底部平齐，序号靠右且字号更小 */
.item-card.ach-card { border-width: 2px; position: relative; }
.item-card.ach-card .ach-head { display: flex; align-items: flex-end; gap: .45rem; width: 100%; }
.item-card.ach-card .ach-name { font-size: 1rem; letter-spacing: .04rem; }
.item-card.ach-card .ach-rarity { font-size: .72rem; letter-spacing: .06rem; opacity: .92; }
.item-card.ach-card .ach-no {
  margin-left: auto; font-size: .66rem; letter-spacing: .02rem;
  font-variant-numeric: tabular-nums; opacity: .9;
  padding: .04rem .4rem; border-radius: 999px;
  background: rgba(0, 0, 0, .34); border: 1px solid rgba(255, 255, 255, .3);
}
.item-card.ach-card .ach-desc,
.item-card.ach-card .ach-unlock { line-height: 1.7; }
.item-card.ach-card .ach-unlock { opacity: .85; }
.item-card.ach-locked .ach-name { opacity: .6; letter-spacing: .12rem; }
/* 成就列表：纵向排列，互不挤压 */
.ach-list { display: flex; flex-direction: column; gap: .1rem; margin-top: .2rem; }

/* 各档卡片底色 */
.item-card.ach-r1 { background: linear-gradient(160deg, #4a3728 0%, #32261c 100%); border-color: #8a6644; color: #ecdcc6; }
/* 红铜：低对比铜色多段高光，缓慢流动 */
.item-card.ach-r2 {
  background: linear-gradient(105deg, #4a2f24 0%, #63412f 22%, #7d5540 40%, #57382a 58%, #6f4a37 78%, #4a2f24 100%);
  background-size: 200% 100%; animation: ach-sheen 8s linear infinite;
  border-color: #b06a44; color: #f0cdb4;
}
/* 亮银：低对比银灰高光缓慢流动 */
.item-card.ach-r3 {
  border-color: #cbd5e0; color: #eef3f8;
  background: linear-gradient(105deg, #39414b 0%, #67727f 20%, #a9b4c0 38%, #77818e 56%, #4d5762 74%, #96a1ae 88%, #39414b 100%);
  background-size: 200% 100%; animation: ach-sheen 7s linear infinite;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16), 0 0 8px rgba(160, 175, 190, .16);
}
/* 金闪闪：多段金高光扫过，内层提亮边加暖光晕 */
.item-card.ach-r4 {
  border-color: #ffe08a; color: #fff6d0;
  background: linear-gradient(105deg, #4a3708 0%, #a8801c 15%, #ffe89a 30%, #d5a326 44%, #7d5c10 58%, #ffd257 74%, #4a3708 100%);
  background-size: 200% 100%; animation: ach-sheen 4.5s linear infinite;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4), 0 0 14px rgba(255, 210, 87, .42);
}
.item-card.ach-r5 {
  border-color: rgba(255, 255, 255, .55); color: #fff;
  background: linear-gradient(120deg, #7a1f6f, #b23a2e, #c98a1c, #2e8b57, #2a5fb0, #7a1f6f);
  background-size: 300% 100%; animation: ach-flow 6s linear infinite;
}

@keyframes ach-flow {
  from { background-position: 0% 50%; }
  to { background-position: 300% 50%; }
}
/* 金属光泽扫过：银色与金色共用的动态高光 */
@keyframes ach-sheen {
  from { background-position: 0% 50%; }
  to { background-position: -200% 50%; }
}

.ach-toast {
  min-width: 250px; max-width: 340px; padding: .7rem .95rem .78rem;
  border-radius: 12px; border: 2px solid #8a6644;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
  opacity: 0; transform: translateX(40px) scale(.96);
  transition: opacity .35s ease, transform .35s ease;
}
.ach-toast.ach-toast-in { opacity: 1; transform: translateX(0) scale(1); }
.ach-toast.ach-toast-out { opacity: 0; transform: translateX(24px) scale(.98); }
.ach-toast .ach-toast-head { font-size: .74rem; letter-spacing: .12rem; opacity: .92; margin-bottom: .25rem; }
/* 提示正文与卡片同构：名称最左，等级紧挨右边且底部平齐，序号靠右 */
.ach-toast .ach-toast-body { display: flex; align-items: flex-end; gap: .45rem; }
.ach-toast .ach-toast-name { font-size: 1.14rem; font-weight: 700; letter-spacing: .06rem; }
.ach-toast .ach-toast-rarity { font-size: .72rem; letter-spacing: .06rem; opacity: .92; }
.ach-toast .ach-toast-body .ach-no {
  margin-left: auto; font-size: .62rem; letter-spacing: .02rem;
  font-variant-numeric: tabular-nums; opacity: .9;
  padding: .03rem .35rem; border-radius: 999px;
  background: rgba(0, 0, 0, .34); border: 1px solid rgba(255, 255, 255, .3);
}
.ach-toast .ach-toast-desc { margin-top: .3rem; font-size: .82rem; line-height: 1.65; opacity: .92; }

.ach-toast.ach-r1 { background: linear-gradient(160deg, #4a3728 0%, #32261c 100%); border-color: #8a6644; color: #ecdcc6; }
.ach-toast.ach-r2 {
  background: linear-gradient(105deg, #4a2f24 0%, #63412f 22%, #7d5540 40%, #57382a 58%, #6f4a37 78%, #4a2f24 100%);
  background-size: 200% 100%; animation: ach-sheen 6s linear infinite;
  border-color: #b06a44; color: #f0cdb4; box-shadow: 0 0 20px rgba(176, 106, 68, .4);
}
.ach-toast.ach-r3 {
  border-color: #cbd5e0; color: #eef3f8;
  background: linear-gradient(105deg, #39414b 0%, #67727f 20%, #a9b4c0 38%, #77818e 56%, #4d5762 74%, #96a1ae 88%, #39414b 100%);
  background-size: 200% 100%; animation: ach-sheen 9s linear infinite;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16), 0 0 16px rgba(160, 175, 190, .3);
}
.ach-toast.ach-r4 {
  border-color: #ffe08a; color: #fff6d0;
  background: linear-gradient(105deg, #4a3708 0%, #a8801c 15%, #ffe89a 30%, #d5a326 44%, #7d5c10 58%, #ffd257 74%, #4a3708 100%);
  background-size: 200% 100%; animation: ach-sheen 3.6s linear infinite;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4), 0 0 26px rgba(255, 210, 87, .6);
}
.ach-toast.ach-r5 {
  border-color: rgba(255, 255, 255, .6); color: #fff;
  background: linear-gradient(120deg, #7a1f6f, #b23a2e, #c98a1c, #2e8b57, #2a5fb0, #7a1f6f);
  background-size: 300% 100%; animation: ach-flow 5s linear infinite;
  box-shadow: 0 0 30px rgba(255, 255, 255, .5);
}


/* ===== 浅色主题下的成就卡片与提示 =====
 * 深色的木褐与红铜挂在浅色界面上偏重，这里换成同一色系的浅色版本。 */
body[data-theme="candy"] .item-card.ach-r1,
body[data-theme="mint"] .item-card.ach-r1 {
  background: linear-gradient(160deg, #ead9c0 0%, #dcc5a2 100%);
  border-color: #b38d5f; color: #4a3524;
}
body[data-theme="candy"] .item-card.ach-r2,
body[data-theme="mint"] .item-card.ach-r2 {
  background: linear-gradient(105deg, #f2dac8 0%, #e4bda3 22%, #d9a583 40%, #eccdb6 58%, #e0b294 78%, #f2dac8 100%);
  background-size: 200% 100%; animation: ach-sheen 8s linear infinite;
  border-color: #d29a6a; color: #5c3118;
}
body[data-theme="candy"] .item-card.ach-card .ach-no,
body[data-theme="mint"] .item-card.ach-card .ach-no {
  background: rgba(255, 255, 255, .6); border-color: rgba(90, 70, 45, .25);
}
body[data-theme="candy"] .item-card.ach-locked .ach-name,
body[data-theme="mint"] .item-card.ach-locked .ach-name { opacity: .72; }
body[data-theme="candy"] .ach-toast.ach-r1,
body[data-theme="mint"] .ach-toast.ach-r1 {
  background: linear-gradient(160deg, #ead9c0 0%, #dcc5a2 100%);
  border-color: #b38d5f; color: #4a3524;
}
body[data-theme="candy"] .ach-toast.ach-r2,
body[data-theme="mint"] .ach-toast.ach-r2 {
  background: linear-gradient(105deg, #f2dac8 0%, #e4bda3 22%, #d9a583 40%, #eccdb6 58%, #e0b294 78%, #f2dac8 100%);
  background-size: 200% 100%; animation: ach-sheen 6s linear infinite;
  border-color: #d29a6a; color: #5c3118;
}

/* ===== 成就点与成就点商店 =====
 * 等级文本 LvX 沿用成就卡片里「等级 Ⅰ」那种纯文本写法：不带框、不带底色，
 * 只给文字本身配色，从 1 级起每两级换一套
 * （Lv1~2 木褐 / Lv3~4 红铜 / Lv5~6 亮银 / Lv7~8 金闪闪 / Lv9~10 彩色流动），
 * Lv0 不上色，保持默认文字色。 */

/* 成就面板与成就点商店的标题：字号更大、金色（用户口径）。
 * 解锁进度紧跟标题、带括号，不往右边推。 */
h3.ach-title {
  display: flex; align-items: baseline; gap: .4rem;
  font-size: 1.32rem; letter-spacing: .05rem;
  color: #ffd257;
  text-shadow: 0 0 10px rgba(255, 210, 87, .35);
}
h3.ach-title .ach-title-count {
  font-size: .92rem; font-weight: 400; letter-spacing: .05rem;
  opacity: .92; color: #ffe89a; text-shadow: none;
  font-variant-numeric: tabular-nums;
}
body[data-theme="candy"] h3.ach-title,
body[data-theme="mint"] h3.ach-title { color: #a8801c; text-shadow: none; }
body[data-theme="candy"] h3.ach-title .ach-title-count,
body[data-theme="mint"] h3.ach-title .ach-title-count { color: #8a6a10; }

/* 成就面板顶部：商店入口在左，可用成就点紧跟其后 */
.ach-point-row { align-items: center; gap: .55rem; flex-wrap: wrap; }
.ach-point-total { font-size: .95rem; letter-spacing: .03rem; }
.ach-point-total b { font-size: 1.12rem; font-variant-numeric: tabular-nums; }

/* 等级文本：与成就卡片里的「等级 Ⅰ」同构，纯文字 */
.ach-lv {
  font-size: .74rem; letter-spacing: .06rem;
  font-variant-numeric: tabular-nums; white-space: nowrap; opacity: .95;
}
.ach-lv-1 { color: #ecdcc6; }
.ach-lv-2 { color: #f0cdb4; text-shadow: 0 0 8px rgba(176, 106, 68, .6); }
/* 亮银：银灰渐变扫过文字本身 */
.ach-lv-3 {
  background: linear-gradient(105deg, #93a1b0 0%, #f2f7fc 30%, #aab6c4 55%, #ffffff 78%, #93a1b0 100%);
  background-size: 200% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: ach-sheen 7s linear infinite;
}
/* 金闪闪：金色渐变扫过文字，另加一点暖光晕 */
.ach-lv-4 {
  background: linear-gradient(105deg, #a8801c 0%, #ffe89a 26%, #d5a326 48%, #fff6d0 70%, #a8801c 100%);
  background-size: 200% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 0 6px rgba(255, 210, 87, .5));
  animation: ach-sheen 4.5s linear infinite;
}
/* 彩色流动：文字本身就是流动的彩带 */
.ach-lv-5 {
  background: linear-gradient(100deg, #ff9ad5, #ffd257, #7bffb0, #7fc4ff, #ff9ad5);
  background-size: 300% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: ach-flow 6s linear infinite;
}

/* 商店面板：两列铺开全部升级项 */
.ach-shop-points { font-size: .95rem; margin: .2rem 0 .3rem; }
.ach-shop-points b { font-size: 1.16rem; font-variant-numeric: tabular-nums; }
.ach-shop-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .5rem; margin-top: .5rem;
}
@media (max-width: 620px) { .ach-shop-grid { grid-template-columns: 1fr; } }
.ach-shop-card { padding: .5rem .6rem .55rem; }
.ach-shop-top { display: flex; align-items: center; gap: .45rem; }
.ach-shop-name { font-size: .98rem; letter-spacing: .03rem; }
/* 升级按钮一律靠右（用户口径） */
.ach-shop-btn { margin-left: auto; }
.ach-shop-btn button { padding: .18rem .7rem; font-size: .8rem; }
.ach-shop-cost { margin-top: .22rem; }
.ach-shop-cost b { font-variant-numeric: tabular-nums; }
.ach-shop-effect { opacity: .95; }
.ach-shop-desc { opacity: .72; line-height: 1.6; }

/* 成就点提示：页面上方居中，奖杯标记加本次获得的点数总和 */
.ach-point-toast {
  padding: .42rem 1.05rem; border-radius: 999px;
  font-size: .95rem; font-weight: 700; letter-spacing: .05rem;
  color: #fff6d0;
  background: linear-gradient(105deg, #4a3708 0%, #a8801c 18%, #ffe89a 34%, #d5a326 50%, #7d5c10 66%, #ffd257 82%, #4a3708 100%);
  background-size: 200% 100%;
  border: 1px solid #ffe08a;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .5), 0 0 18px rgba(255, 210, 87, .45);
  opacity: 0; transform: translateY(-14px) scale(.96);
  transition: opacity .3s ease, transform .3s ease;
  animation: ach-sheen 3.6s linear infinite;
}
.ach-point-toast.ach-point-toast-in { opacity: 1; transform: translateY(0) scale(1); }
.ach-point-toast.ach-point-toast-out { opacity: 0; transform: translateY(-10px) scale(.98); }

body[data-theme="candy"] .ach-point-toast,
body[data-theme="mint"] .ach-point-toast {
  color: #5c3118; border-color: #d29a6a;
  background: linear-gradient(105deg, #f2dac8 0%, #e4bda3 22%, #d9a583 40%, #eccdb6 58%, #e0b294 78%, #f2dac8 100%);
  background-size: 200% 100%;
  box-shadow: 0 6px 18px rgba(120, 90, 60, .28);
}
body[data-theme="candy"] .ach-lv-1, body[data-theme="mint"] .ach-lv-1 { color: #4a3524; }
body[data-theme="candy"] .ach-lv-2, body[data-theme="mint"] .ach-lv-2 { color: #8a4a20; text-shadow: none; }
/* 亮银与金闪闪的浅色字在浅底上看不清，换成同色系的深色渐变文字 */
body[data-theme="candy"] .ach-lv-3, body[data-theme="mint"] .ach-lv-3 {
  background-image: linear-gradient(105deg, #6b7783 0%, #2f3742 30%, #7d8896 55%, #454e5a 78%, #6b7783 100%);
}
body[data-theme="candy"] .ach-lv-4, body[data-theme="mint"] .ach-lv-4 {
  background-image: linear-gradient(105deg, #8a6a10 0%, #c99a1c 30%, #7d5c10 55%, #b8860b 80%, #8a6a10 100%);
  filter: none;
}
body[data-theme="candy"] .ach-lv-5, body[data-theme="mint"] .ach-lv-5 {
  background-image: linear-gradient(100deg, #b13a8f, #c96a1c, #2f8a54, #2a5fb0, #b13a8f);
}

/* ===== 鱼获展示窗（用户需求） =====
 * 一整层盖在界面上，没有底板、没有关闭按钮：点画面任意位置即关闭。
 * 弹出时给底下的 #app 与 #modal-root 上模糊，视线全给这条鱼。 */

body.fish-catch-on #app,
body.fish-catch-on #modal-root {
  filter: blur(7px) saturate(.92);
  transition: filter .25s ease;
}
.fish-catch-layer {
  position: fixed; inset: 0; z-index: 13000;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; background: transparent;
}
.fish-catch-box {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; max-width: 94vw;
  animation: fishCatchIn .32s ease-out;
}
@keyframes fishCatchIn {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: scale(1); }
}
/* 星级：图片上方居中排开，逐颗从画面上方砸下来（WAAPI 播放，见 fishStarDropFx） */
.fish-catch-stars {
  display: flex; justify-content: center; align-items: flex-end;
  gap: .4rem; min-height: 2.5rem; margin-bottom: .55rem;
}
.fish-catch-star {
  display: inline-block; font-size: 2.1rem; line-height: 1;
  will-change: transform;
  text-shadow: 0 3px 12px rgba(0, 0, 0, .65);
}
/* 图片位：耶稣光在图片后面缓慢旋转，颜色跟着这条鱼的稀有度走。
 * 尺寸按 --fish-scale 缩放（0.5 ~ 3，见 fish.module.js 的 fishCatchScale）：
 * 1× 就是原来的 340px，最大的鱼 3×，视口不够宽高时由 94vw / 76vh 兜住。 */
.fish-catch-figure {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: min(94vw, calc(340px * var(--fish-scale, 1))); isolation: isolate;
}
.fish-catch-img {
  position: relative; z-index: 1;
  width: 100%; height: auto; max-height: 76vh;
  object-fit: contain;
  border-radius: 12px;
  filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .6));
  animation: fishCatchFloat 4.5s ease-in-out infinite;
}
@keyframes fishCatchFloat {
  0%, 100% { transform: translateY(-4px); }
  50% { transform: translateY(4px); }
}
/* 没有图片时的占位框：高:宽 = 1:2，框里写鱼名 */
.fish-catch-ph {
  position: relative; z-index: 1;
  width: 100%; aspect-ratio: 2 / 1;
  display: none; flex-direction: column; align-items: center; justify-content: center; gap: .3rem;
  border: 2px dashed rgba(255, 255, 255, .38); border-radius: 14px;
  background: rgba(10, 16, 26, .34); color: rgba(255, 255, 255, .82);
}
.fish-catch-ph span { font-size: clamp(.9rem, calc(2.6rem * var(--fish-scale, 1)), 2.6rem); line-height: 1; }
.fish-catch-ph em { font-style: normal; font-size: .9rem; letter-spacing: .08rem; }

/* 耶稣光：一圈缓慢旋转的光柱，颜色按稀有度，五星走彩色。
 * 尺寸跟着图片一起缩放：width 取 .fish-catch-figure 的 210%，而那个容器本身按
 * --fish-scale 缩放（0.5 ~ 3），所以光始终是图片的固定倍数，小鱼柔光小、巨物光晕大。
 * 虚化半径原先写死 7px，不随尺寸走 —— 小鱼的柔光会糊得像大鱼，这里按同一系数缩放。 */
.fish-catch-ray {
  position: absolute; left: 50%; top: 50%; z-index: 0;
  width: 210%; aspect-ratio: 1 / 1; transform: translate(-50%, -50%);
  border-radius: 50%; pointer-events: none;
  opacity: .7; filter: blur(calc(7px * var(--fish-scale, 1))); mix-blend-mode: screen;
  background: repeating-conic-gradient(from 0deg,
    rgba(255, 255, 255, 0) 0deg 8deg,
    var(--ray-c, #5aa9e6) 10deg 15deg,
    rgba(255, 255, 255, 0) 17deg 30deg);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, .9) 0%, rgba(0, 0, 0, .35) 44%, transparent 72%);
  mask-image: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, .9) 0%, rgba(0, 0, 0, .35) 44%, transparent 72%);
  animation: fishRaySpin 16s linear infinite;
}
@keyframes fishRaySpin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
.fish-catch-r1 { --ray-c: #ffffff; }
.fish-catch-r2 { --ray-c: #6fc48a; }
.fish-catch-r3 { --ray-c: #5aa9e6; }
.fish-catch-r4 { --ray-c: #b48ce0; }
/* 五星：光柱本身走彩色流转 */
.fish-catch-r5 .fish-catch-ray {
  background: repeating-conic-gradient(from 0deg,
    rgba(255, 255, 255, 0) 0deg 8deg,
    #7a1f6f 10deg 15deg, rgba(255, 255, 255, 0) 17deg 26deg,
    #b23a2e 28deg 33deg, rgba(255, 255, 255, 0) 35deg 44deg,
    #c98a1c 46deg 51deg, rgba(255, 255, 255, 0) 53deg 62deg,
    #2e8b57 64deg 69deg, rgba(255, 255, 255, 0) 71deg 80deg,
    #2a5fb0 82deg 87deg, rgba(255, 255, 255, 0) 89deg 98deg);
  opacity: .78;
}

/* 锻造升品弹窗：彩色旋转耶稣光（与鱼获展示窗同款做法，复用 fishRaySpin 动画）。
 * 光柱是绝对定位的圆形，卡面内容靠 z-index 压在它上面。 */
.forge-up-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 200px;
  overflow: hidden;
}
.forge-up-rays {
  position: absolute; left: 50%; top: 50%; z-index: 0;
  width: 240px; aspect-ratio: 1 / 1; transform: translate(-50%, -50%);
  border-radius: 50%; pointer-events: none;
  opacity: .8; filter: blur(7px); mix-blend-mode: screen;
  background: repeating-conic-gradient(from 0deg,
    rgba(255, 255, 255, 0) 0deg 8deg,
    var(--ray-c, #cfd6e0) 10deg 15deg,
    rgba(255, 255, 255, 0) 17deg 30deg);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, .9) 0%, rgba(0, 0, 0, .35) 44%, transparent 72%);
  mask-image: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, .9) 0%, rgba(0, 0, 0, .35) 44%, transparent 72%);
  animation: fishRaySpin 16s linear infinite;
}
/* 品质光色（白 / 绿 / 蓝 / 紫 / 金 / 彩）：制造弹窗按成品品质挑一档 */
.forge-up-rays.ray-q1 { --ray-c: #cfd6e0; }
.forge-up-rays.ray-q2 { --ray-c: #6fc48a; }
.forge-up-rays.ray-q3 { --ray-c: #5aa9e6; }
.forge-up-rays.ray-q4 { --ray-c: #b48ce0; }
.forge-up-rays.ray-q5 { --ray-c: #e8b23d; }
/* 彩档：光柱本身多色流转 */
.forge-up-rays.ray-q6 {
  opacity: .85;
  background: repeating-conic-gradient(from 0deg,
    rgba(255, 255, 255, 0) 0deg 8deg,
    #7a1f6f 10deg 15deg, rgba(255, 255, 255, 0) 17deg 26deg,
    #b23a2e 28deg 33deg, rgba(255, 255, 255, 0) 35deg 44deg,
    #c98a1c 46deg 51deg, rgba(255, 255, 255, 0) 53deg 62deg,
    #2e8b57 64deg 69deg, rgba(255, 255, 255, 0) 71deg 80deg,
    #2a5fb0 82deg 87deg, rgba(255, 255, 255, 0) 89deg 98deg);
}
.forge-up-inner { position: relative; z-index: 1; text-align: center; }
.forge-up-inner p { margin: .2rem 0; }

/* 名称与尺寸一行 */
.fish-catch-name {
  margin-top: .7rem; display: flex; align-items: baseline; justify-content: center;
  gap: .5rem; flex-wrap: wrap;
  font-size: 1.22rem; font-weight: 700; letter-spacing: .06rem; color: #fff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, .8);
}
.fish-catch-name b { font-size: 1.08rem; font-variant-numeric: tabular-nums; }
.fish-catch-lore {
  margin: .5rem 0 0; max-width: 30rem;
  font-size: .82rem; line-height: 1.7; color: rgba(255, 255, 255, .84);
  text-shadow: 0 1px 6px rgba(0, 0, 0, .85);
}

/* 新纪录：贴在尺寸的右上角，外框与文字都走彩色流光，并一直轻微上下浮动 */
.fish-record-tag {
  position: relative; top: -.62em;
  display: inline-block; padding: .08rem .42rem; border-radius: 6px;
  font-size: .7rem; font-weight: 700; letter-spacing: .06rem;
  border: 2px solid transparent;
  background:
    linear-gradient(#0d121c, #0d121c) padding-box,
    linear-gradient(100deg, #7a1f6f, #b23a2e, #c98a1c, #2e8b57, #2a5fb0, #7a1f6f) border-box;
  background-size: 100% 100%, 300% 100%;
  animation: ach-flow 3s linear infinite, fishRecordFloat 1.9s ease-in-out infinite;
}
.fish-record-tag i {
  font-style: normal;
  background: linear-gradient(100deg, #ff9ad5, #ffd257, #7bffb0, #7fc4ff, #ff9ad5);
  background-size: 300% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: ach-flow 3s linear infinite;
}
@keyframes fishRecordFloat {
  0%, 100% { transform: translateY(-2px); }
  50% { transform: translateY(2px); }
}

/* 浅色主题：鱼获窗没有底板，深色字与深色投影在浅色界面上看不清，换成深字浅晕 */
body[data-theme="candy"] .fish-catch-name,
body[data-theme="mint"] .fish-catch-name { color: #2b2419; text-shadow: 0 2px 10px rgba(255, 255, 255, .9); }
body[data-theme="candy"] .fish-catch-lore,
body[data-theme="mint"] .fish-catch-lore { color: #3a3226; text-shadow: 0 1px 6px rgba(255, 255, 255, .9); }
body[data-theme="candy"] .fish-catch-ph,
body[data-theme="mint"] .fish-catch-ph {
  border-color: rgba(90, 70, 45, .4); background: rgba(255, 255, 255, .5); color: #4a3524;
}
body[data-theme="candy"] .fish-catch-ray,
body[data-theme="mint"] .fish-catch-ray { mix-blend-mode: multiply; opacity: .5; }

/* ===== 手记弹窗：只留册子 =====
 * 弹窗不再画自己的面板，去掉内边距与底色，让册子直接浮在遮罩上，
 * 右上角的 ✕ 钉在书页右上角。 */
.modal:has(.journal-book) {
  width: min(880px, 94vw);
  padding: 0;
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 8px;
  overflow: hidden;
}
.journal-book > .journal-close {
  position: absolute; top: .45rem; right: .7rem; z-index: 3;
  float: none; background: none; border: none;
  font-size: 1.15rem; line-height: 1; color: #8b7454;
}
.journal-book > .journal-close:hover { color: #4a3826; }

/* ===== 图鉴分类格子配色 =====
 * 每个分类一组底色与边色，同类格子保持一致。
 * 蓝图物品的蓝底与料理的橙底是更具体的规则，会盖过分类底色。 */
:root {
  --codex-station-bg: rgba(79, 143, 168, .16);    --codex-station-edge: rgba(79, 143, 168, .55);
  --codex-npc-bg: rgba(138, 107, 176, .16);       --codex-npc-edge: rgba(138, 107, 176, .55);
  --codex-item-bg: rgba(107, 154, 122, .16);      --codex-item-edge: rgba(107, 154, 122, .55);
  --codex-equip-bg: rgba(109, 134, 176, .16);     --codex-equip-edge: rgba(109, 134, 176, .55);
  --codex-ingredient-bg: rgba(168, 160, 90, .16); --codex-ingredient-edge: rgba(168, 160, 90, .55);
  --codex-fish-bg: rgba(88, 166, 178, .16);       --codex-fish-edge: rgba(88, 166, 178, .55);
  --codex-cuisine-bg: rgba(224, 164, 88, .16);    --codex-cuisine-edge: rgba(224, 164, 88, .55);
  --codex-facility-bg: rgba(154, 138, 106, .16);  --codex-facility-edge: rgba(154, 138, 106, .55);
  --codex-monster-bg: rgba(176, 96, 88, .16);     --codex-monster-edge: rgba(176, 96, 88, .55);
  --codex-magic-bg: rgba(122, 118, 208, .16);     --codex-magic-edge: rgba(122, 118, 208, .55);
  --codex-pos-bg: rgba(95, 174, 114, .16);        --codex-pos-edge: rgba(95, 174, 114, .55);
  --codex-neg-bg: rgba(168, 95, 122, .16);        --codex-neg-edge: rgba(168, 95, 122, .55);
}
body[data-theme="candy"], body[data-theme="mint"] {
  --codex-station-bg: rgba(59, 123, 148, .14);    --codex-station-edge: rgba(59, 123, 148, .5);
  --codex-npc-bg: rgba(118, 87, 156, .14);        --codex-npc-edge: rgba(118, 87, 156, .5);
  --codex-item-bg: rgba(87, 134, 102, .14);       --codex-item-edge: rgba(87, 134, 102, .5);
  --codex-equip-bg: rgba(89, 114, 156, .14);      --codex-equip-edge: rgba(89, 114, 156, .5);
  --codex-ingredient-bg: rgba(148, 140, 70, .14); --codex-ingredient-edge: rgba(148, 140, 70, .5);
  --codex-fish-bg: rgba(68, 146, 158, .14);       --codex-fish-edge: rgba(68, 146, 158, .5);
  --codex-cuisine-bg: rgba(204, 144, 68, .14);    --codex-cuisine-edge: rgba(204, 144, 68, .5);
  --codex-facility-bg: rgba(134, 118, 86, .14);   --codex-facility-edge: rgba(134, 118, 86, .5);
  --codex-monster-bg: rgba(156, 76, 68, .14);     --codex-monster-edge: rgba(156, 76, 68, .5);
  --codex-magic-bg: rgba(102, 98, 188, .14);      --codex-magic-edge: rgba(102, 98, 188, .5);
  --codex-pos-bg: rgba(75, 154, 94, .14);         --codex-pos-edge: rgba(75, 154, 94, .5);
  --codex-neg-bg: rgba(148, 75, 102, .14);        --codex-neg-edge: rgba(148, 75, 102, .5);
}
:where(.codex-sec-station) .item-card    { background: var(--codex-station-bg);    border-color: var(--codex-station-edge); }
:where(.codex-sec-npc) .item-card        { background: var(--codex-npc-bg);        border-color: var(--codex-npc-edge); }
:where(.codex-sec-item) .item-card       { background: var(--codex-item-bg);       border-color: var(--codex-item-edge); }
:where(.codex-sec-equip) .item-card      { background: var(--codex-equip-bg);      border-color: var(--codex-equip-edge); }
:where(.codex-sec-ingredient) .item-card { background: var(--codex-ingredient-bg); border-color: var(--codex-ingredient-edge); }
:where(.codex-sec-fish) .item-card       { background: var(--codex-fish-bg);       border-color: var(--codex-fish-edge); }
:where(.codex-sec-cuisine) .item-card    { background: var(--codex-cuisine-bg);    border-color: var(--codex-cuisine-edge); }
:where(.codex-sec-facility) .item-card   { background: var(--codex-facility-bg);   border-color: var(--codex-facility-edge); }
:where(.codex-sec-monster) .item-card    { background: var(--codex-monster-bg);    border-color: var(--codex-monster-edge); }
:where(.codex-sec-magic) .item-card      { background: var(--codex-magic-bg);      border-color: var(--codex-magic-edge); }
:where(.codex-sec-pos) .item-card        { background: var(--codex-pos-bg);        border-color: var(--codex-pos-edge); }
:where(.codex-sec-neg) .item-card        { background: var(--codex-neg-bg);        border-color: var(--codex-neg-edge); }

/* ===== 站点介绍右下角的商店入口 =====
 * 需求：按钮写店名而不是"车站商店"，落在站点介绍右下角，并且比普通按钮显眼。
 * 用主题变量取色，跟随换肤；光环与轻微呼吸让它在面板里第一眼被看到。 */
.shop-entry-row {
  display: flex;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .6rem;
}
.shop-entry-btn {
  font-size: 1.06em;
  font-weight: 700;
  padding: .5rem 1.1rem;
  letter-spacing: .02em;
  border-width: 2px;
  background: color-mix(in srgb, var(--good) 30%, var(--bg2));
  border-color: color-mix(in srgb, var(--good) 75%, var(--border));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--good) 28%, transparent),
              0 4px 14px rgba(0, 0, 0, .35);
  animation: shopEntryBreath 2.6s ease-in-out infinite;
}
.shop-entry-btn:hover {
  transform: translateY(-1px) scale(1.03);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 45%, transparent),
              0 6px 18px rgba(0, 0, 0, .4);
}
@keyframes shopEntryBreath {
  0%, 100% {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--good) 28%, transparent),
                0 4px 14px rgba(0, 0, 0, .35);
  }
  50% {
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--good) 40%, transparent),
                0 4px 18px rgba(0, 0, 0, .4);
  }
}

/* ============================================================
 * 竖屏适配（orientation: portrait，窗口高大于宽时才匹配）
 * 横屏一律落到这段之外，呈现与改动前完全一致。
 * 目标：竖屏少滚动，战斗面板变小并排成 2 × 2。
 * ============================================================ */

/* 列车状态面板里那行「抵达某站」的行程提示：只在竖屏显示。
 * 横屏时站点信息在页面最下方的自动驾驶日志里，位置与呈现都不变。 */
.station-here { display: none; }

/* 主界面收紧：顶栏、状态区、页签都压薄。
 * 重点是 .status-grid —— 它原本是 minmax(230px, 1fr)，窄屏只放得下一列，
 * 列车与角色会上下堆成两屏高度，竖屏固定改成两列。 */
@media (orientation: portrait) {
  .station-here { display: block; margin: .1rem 0 .35rem; }
  .game-shell { padding: .35rem; }
  .topbar { padding: .35rem .45rem; gap: .35rem; margin-bottom: .45rem; }
  .topbar .title-sm { font-size: 1rem; margin-right: .2rem; }
  .topbar-row { gap: .3rem; }
  .chip { padding: .1rem .4rem; font-size: .7rem; }
  /* 竖屏顶栏的按钮行（用户口径：跳过时间与存档/读档/设置/标题同一行）。
   * 这一份 DOM 只由竖屏分支生成（见 ui.module.js 的 gameScreenHTML），
   * 里面只剩「跳过时间 存档 读档 设置 标题」五个，横屏那档尺寸就放得下（实测 320px 屏也够），
   * 所以这里只管折行与间距，不再压小按钮。 */
  .topbar-row-fn { flex-wrap: wrap; gap: .3rem; }
  /* 信息行（车名 + 五个状态 chip）占满一整行，按钮行排在它下面 */
  .topbar-row-info { flex: 1 1 100%; }
  .topbar button { padding: .28rem .5rem; font-size: .78rem; }
  /* 竖屏：状态区里现在只剩角色面板一个（列车面板已按用户口径移除），必须**单列铺满整宽** ——
   * 原先写的两列会让面板只占左半边，看起来就是「宽度和屏幕对不齐」（用户报的问题）。 */
  .status-grid { grid-template-columns: 1fr; gap: .4rem; margin-bottom: .45rem; }
  .status-grid > .panel { padding: .4rem .45rem; }
  .status-grid .panel h3 { font-size: .82rem; }
  .char-status-grid { grid-template-rows: repeat(2, auto); gap: .1rem .45rem; }
  /* 竖屏：角色面板改成上下排（头像不那么宽），头像压到 72px 省高度 */
  .char-panel { flex-direction: column; align-items: stretch; gap: .35rem; }
  .char-avatar-3x4 { flex: 0 0 auto; width: 72px; }
  /* 竖屏角色面板的排法（用户口径）：
   *   ┌────────────────┐
   *   │ 头像  🧙 角色名 │   ← 这一行在面板最上面
   *   │ 生命 … 状态条 … │   ← 状态条整行铺满，排在下面
   *   └────────────────┘
   * 这一行的排法与字号写在 JS 的内联样式里（ui.module.js 的 CHAR_ID_ROW_STYLE），
   * 只有竖屏才会生成这个元素；横屏走「头像在左、正文(h3 + 状态条)在右」的老结构。 */
  /* 竖屏状态条（用户报的「挤压得很厉害、旁边却有大片空白」）根因有两条：
   * ① .char-panel 继承 base 的 align-items: flex-start，纵向排列后横轴变成宽度轴，
   *    flex-start 让正文按**内容宽**收缩 —— 实测 390px 屏上面板 369px 宽，正文只剩 137px，
   *    右边 218px 全是空白；② 正文里再把状态条排成两列，每格只分到 65px，
   *    标签「生命 100/110」在 11.5px 字号下要 90px，于是换行叠成三行，条只有几十像素长。
   * 修法：正文拉伸铺满（min-width:0 允许收缩），状态条在竖屏改**单列**，
   * 每条独占整行（约 340px），标签与数值左右分开、进度条吃满剩余宽度。 */
  .char-panel .char-panel-body { flex: 1 1 auto; width: 100%; min-width: 0; }
  .char-panel .char-status-grid {
    grid-auto-flow: row;
    grid-template-columns: 1fr;
    grid-template-rows: none;
    gap: .05rem .45rem;
    width: 100%;
  }
  /* 标签不换行：单个标签行放不下时省略号收尾，绝不折成三行把状态条压扁 */
  .bar-label { font-size: .72rem; white-space: nowrap; }
  .bar-label > span { overflow: hidden; text-overflow: ellipsis; }
  .bar-label > span:last-child { flex: 0 0 auto; margin-left: .4rem; }
  .bar-row { margin: .18rem 0; }
  /* 角色面板里的属性/战斗数值默认是写死在 HTML 上的两列内联网格（1fr 1fr），
   * 窄屏会把「命中率 88% 对标准敌人」这类长条目挤到换行。竖屏改成按宽度自动折列
   * （每列至少 150px），放不下就掉成一列，条目文字不再被挤断。 */
  .status-grid .char-panel-body > div[style*="grid"] {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)) !important;
  }
  /* 页签十几个，换行会占掉半屏，改成一行横向滚动 */
  .tabs { flex-wrap: nowrap; overflow-x: auto; gap: .25rem; margin-bottom: .45rem; }
  .tabs button { flex: 0 0 auto; padding: .3rem .6rem; font-size: .8rem; }
  .tabs button.tab-right { margin-left: 0; }
  /* 探索页：横屏的 DOM 顺序（站点 → 人物 → 行动 → 状态）原样保留，
   * 竖屏把站点、人物、状态往后挪一档，行动按钮就浮到时间下面。
   * order 相同的元素仍按 DOM 顺序排，所以时间与行动都会留在最前。 */
  .explore-site, .explore-npc, .explore-status { order: 1; }
  /* 探索页行动按钮：竖屏两两一行，括号里的消耗说明隐去（横屏仍是原样一排）。 */
  .explore-actions .row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .4rem; }
  .btn-paren { display: none; }
  /* 人物与商店并排；这两块的 HTML 只由竖屏分支生成，横屏走的是独立面板 + 商店入口。 */
  .explore-side-row { display: flex; gap: .5rem; align-items: flex-start; flex-wrap: wrap; }
  .explore-side-npc { flex: 1 1 52%; min-width: 0; }
  .explore-side-shop { flex: 1 1 40%; min-width: 0; }
  /* 列表卡片也压一压：内容区本身很长，卡片间距是滚动量的主要来源 */
  .item-card, .npc-card, .facility-card, .log-card { padding: .5rem .55rem; margin: .25rem 0; }
}

/* 顶栏里的日期 / 时刻 / 站点名（用户口径）：
 *   ① 日期与时刻拆成两个元素：日期保持普通 chip 尺寸，时刻单独放大；
 *   ② 时刻与站点名都不带框 —— 去掉 chip 的底色、边框与内边距，只留文字；
 *   ③ 放大的是这两个，其他 chip（天数、列车币）仍是原尺寸。
 * 写成带元素名的选择器（.topbar .chip-time）是为了权重高于竖屏块里的
 * `.chip { padding: … }` 与 `.topbar button` 那两条。 */
.topbar .chip-date { font-size: .78rem; }
.topbar .chip-time, .topbar .chip-station {
  font-size: 1.15rem;
  background: none;
  border: none;
  padding: 0;
}
.topbar .chip-time { color: var(--accent2); font-variant-numeric: tabular-nums; }
.topbar .chip-station { color: var(--accent2); }
@media (orientation: portrait) {
  .topbar .chip-date { font-size: .72rem; }
  .topbar .chip-time, .topbar .chip-station { font-size: 1.05rem; }
}
/* 竖屏但屏幕很宽（平板竖屏 768×1024 这类）：状态条仍是单列的话，一条要拉到 730px 长，
 * 看着又细又长。视口 ≥700px 时恢复两列（每列约 360px），六条正好排成 3 行 2 列。
 * 放在竖屏块之后，靠顺序覆盖上面的单列规则。 */
@media (orientation: portrait) and (min-width: 700px) {
  .char-panel .char-status-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .1rem .8rem;
    max-width: 760px;
  }
}

/* 战斗面板：横屏是「敌人一排三格（各 320px）加角色一格」，合计 1280px，
 * 竖屏视口放不下会横向溢出，角色面板被推出屏幕。
 * 竖屏改成 2 × 2 网格：三个敌人格加角色格正好铺满四格，面板跟着视口缩小。
 * 面板高度改由内容决定，避免压到 5:3 比例后文字被裁。 */
@media (orientation: portrait) {
  .battle-layout.group {
    --bp-w: min(240px, 46vw);
    grid-template-columns: repeat(2, minmax(0, var(--bp-w)));
    grid-template-rows: auto auto;
    column-gap: .5rem;
    row-gap: .5rem;
    justify-content: center;
    margin-top: 6px;
  }
  .battle-layout.group .slot-0 { grid-column: 1; grid-row: 1; }
  .battle-layout.group .slot-1 { grid-column: 2; grid-row: 1; }
  .battle-layout.group .slot-2 { grid-column: 1; grid-row: 2; }
  .battle-layout.group .battle-player { grid-column: 2; grid-row: 2; }
  .battle-layout.group .battle-enemy,
  .battle-layout.group .battle-player { aspect-ratio: auto; }
  .battle-layout .panel { padding: .35rem .4rem; }
  .battle-layout .small { font-size: .68rem; line-height: 1.35; }
  .battle-title { font-size: .95rem; }
  .battle-log { height: 96px; }
  .battle-actions button { padding: .32rem .5rem; font-size: .8rem; }
  /* 单人战斗走 .battle-layout 的 flex 分支，同样纵向排列 */
  .battle-layout { flex-direction: column; gap: 1rem; min-height: 0; }
  .battle-enemy, .battle-player { width: 100%; margin-left: 0; margin-top: 0; }
}

/* 日志浮窗固定在右侧中间，竖屏时占 34vw 宽、48vh 高，必然压住主界面内容；
 * 横屏但窗口很窄（≤700px）时同样占去可观宽度，一并隐藏。
 * 日志本身仍可在手记与战斗记录里查看，浮窗只是最新一条的提示。 */
@media (orientation: portrait), (max-width: 700px) {
  .log-float { display: none; }
}

/* 图鉴详情浮层：点图鉴条目时浮在图鉴上方，图鉴列表保持打开、不被替换。
 * 用 fixed 定位而非嵌进列表，这样列表滚到哪儿它都停在屏幕中间。 */
.codex-detail-layer {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(680px, 92vw);
  max-height: 80vh;
  overflow-y: auto;
  z-index: 1200;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 1rem 1.1rem;
  box-shadow: 0 12px 36px rgba(0, 0, 0, .55);
}
.codex-detail-layer[hidden] { display: none; }

/* ============================================================
 * 背包格子与背包布局
 * 格子尺寸原先写在内联样式里，窄容器下会被 flex 压扁；
 * 现在尺寸与「不收缩」都交给 class，竖屏才能整体缩小而不变形。
 * ============================================================ */
.inv-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.inv-grid-key { flex-direction: column; }
.inv-slot {
  /* 长宽固定（用户要求）：整排格子大小一致，不随名字伸缩。
     尺寸取到能放下 5 字物品名，再长的名字在格内换行，不再截断。 */
  width: 88px;
  height: 76px;
  flex: 0 0 auto;
  padding: 5px 4px;
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  cursor: pointer;
  font-size: .7rem;
  line-height: 1.25;
  text-align: center;
}
/* 装备按价值分五档（用户需求）：同档统一，档与档一眼可分。
 * 底色、边框、左上角角标三处同时变，扫一眼就能认出「这是装备」以及「是第几档」。
 * 装备不挂 inv-t-* 类型色，所以这里不会和类型底色打架。 */
.inv-slot.inv-eq-t1 { background: linear-gradient(rgba(154,164,178,.16), rgba(154,164,178,.16)), var(--bg3); border: 2px solid #6b7484; }
.inv-slot.inv-eq-t2 { background: linear-gradient(rgba(123,201,111,.20), rgba(123,201,111,.20)), var(--bg3); border: 2px solid #4e8f45; }
.inv-slot.inv-eq-t3 { background: linear-gradient(rgba(90,169,230,.22), rgba(90,169,230,.22)), var(--bg3); border: 2px solid #3a7cb8; }
.inv-slot.inv-eq-t4 { background: linear-gradient(rgba(176,124,232,.22), rgba(176,124,232,.22)), var(--bg3); border: 2px solid #7c4fb5; }
.inv-slot.inv-eq-t5 { background: linear-gradient(rgba(232,163,61,.24), rgba(232,163,61,.24)), var(--bg3); border: 2px solid #b8791f; }
.inv-slot.inv-eq-t6 { background: linear-gradient(120deg, rgba(224,122,176,.22), rgba(90,169,230,.22)), var(--bg3); border: 2px solid #c05fa0; }
/* 方块右侧紧跟「装备」二字（用户要求）：方块表示档位，文字点明类别。
 * 只加在 .inv-slot 上，装备栏那排 96x96 的部位格不带方块，也不带这两个字。 */
.inv-slot[class*="inv-eq-t"]::after {
  content: '装备';
  position: absolute;
  left: 13px;
  top: 2px;
  font-size: 9px;
  line-height: 1;
  opacity: .85;
}

/* 左上角小方块：装备专属标记，普通物资格没有。 */
.inv-slot.inv-eq-t1, .inv-slot.inv-eq-t2, .inv-slot.inv-eq-t3,
.inv-slot.inv-eq-t4, .inv-slot.inv-eq-t5, .inv-slot.inv-eq-t6 { position: relative; }
.inv-slot.inv-eq-t1::before, .inv-slot.inv-eq-t2::before, .inv-slot.inv-eq-t3::before,
.inv-slot.inv-eq-t4::before, .inv-slot.inv-eq-t5::before, .inv-slot.inv-eq-t6::before {
  content: '';
  position: absolute;
  left: 4px; top: 4px;
  width: 6px; height: 6px;
  border-radius: 2px;
}
.inv-slot.inv-eq-t1::before { background: #9aa4b2; }
.inv-slot.inv-eq-t2::before { background: #7bc96f; }
.inv-slot.inv-eq-t3::before { background: #5aa9e6; }
.inv-slot.inv-eq-t4::before { background: #b07ce8; }
.inv-slot.inv-eq-t5::before { background: #e8a33d; }
/* 第 6 档「彩」：小方块与边框都走彩虹渐变 */
.inv-slot.inv-eq-t6::before { background: linear-gradient(90deg, #e07ab0, #e8c34a, #5ad0a8, #5aa9e6, #b07ce8); }

/* ===== 货架装备的档位标记（用户口径）=====
 * 名称之下一排五角星：颗数与颜色都按品质档走（普通 1 颗 … 神话 6 颗），
 * 卡片边框取同一档的颜色，整张卡扫一眼就知道这件是什么成色。
 * 只作用于交易/商店的卡片（.item-card），背包格子那套 inv-eq-tN 不受影响；
 * 装备之外的商品既不出星也不带 card-grade 类，外观与改动前完全一致。 */
.q-stars { font-size: .82rem; letter-spacing: .12rem; line-height: 1.1; margin: .12rem 0 .1rem; }
.q-stars-1 { color: #9aa4b2; }
.q-stars-2 { color: #7bc96f; }
.q-stars-3 { color: #5aa9e6; }
.q-stars-4 { color: #b07ce8; }
.q-stars-5 { color: #e8a33d; }
/* 第 6 档「神话」：星星本身走彩虹渐变，并且缓慢流动（用户口径：六星的星星颜色要流动） */
.q-stars-6 {
  background: linear-gradient(90deg, #e07ab0, #e8c34a, #5ad0a8, #5aa9e6, #b07ce8, #e8c34a, #e07ab0);
  background-size: 320% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: qStarsFlow 5s linear infinite;
}
@keyframes qStarsFlow {
  0%   { background-position: 0% 50%; }
  100% { background-position: 320% 50%; }
}
.item-card.card-grade-g1 { border: 2px solid #6b7484; }
.item-card.card-grade-g2 { border: 2px solid #4e8f45; }
.item-card.card-grade-g3 { border: 2px solid #3a7cb8; }
.item-card.card-grade-g4 { border: 2px solid #7c4fb5; }
.item-card.card-grade-g5 { border: 2px solid #b8791f; }
.item-card.card-grade-g6 { border: 2px solid #c05fa0; }

/* ===== 背包 / 装备栏等格子里的档位标记（用户口径）=====
 * 装备格子的框取对应档位的颜色，名称之下一行显示对应数量的五角星（普通 1 颗 … 神话 6 颗）。
 * 格子那套 inv-eq-tN 本来就有底色与边框，这里只补星星的字号，以及装备栏九宫格的默认底色 ——
 * 装备栏格子此前把 background / border 写在内联 style 里，档位配色被内联压住，现在交给 CSS。 */
.inv-slot .q-stars,
.equip-slot-cell .q-stars {
  font-size: .58rem;
  letter-spacing: .03rem;
  line-height: 1;
  margin: .06rem 0 0;
  white-space: nowrap;
}
.equip-slot-cell { background: var(--bg3); border: 1px solid var(--border); }
.equip-slot-cell.inv-eq-t1 { background: linear-gradient(rgba(154,164,178,.16), rgba(154,164,178,.16)), var(--bg3); border: 2px solid #6b7484; }
.equip-slot-cell.inv-eq-t2 { background: linear-gradient(rgba(123,201,111,.20), rgba(123,201,111,.20)), var(--bg3); border: 2px solid #4e8f45; }
.equip-slot-cell.inv-eq-t3 { background: linear-gradient(rgba(90,169,230,.22), rgba(90,169,230,.22)), var(--bg3); border: 2px solid #3a7cb8; }
.equip-slot-cell.inv-eq-t4 { background: linear-gradient(rgba(176,124,232,.22), rgba(176,124,232,.22)), var(--bg3); border: 2px solid #7c4fb5; }
.equip-slot-cell.inv-eq-t5 { background: linear-gradient(rgba(232,163,61,.24), rgba(232,163,61,.24)), var(--bg3); border: 2px solid #b8791f; }
.equip-slot-cell.inv-eq-t6 { background: linear-gradient(120deg, rgba(224,122,176,.22), rgba(90,169,230,.22)), var(--bg3); border: 2px solid #c05fa0; }

/* ===== 异化装备：流动的暗红（用户口径「像血一样流动」）=====
 * 星、货架卡片边框、背包格子边框、装备栏格子边框四处统一走暗红，不跟六档配色；
 * 装备栏那层泛光也一并从暗紫改成暗红。 */
@keyframes corruptBloodFlow {
  0%   { background-position: 0% 50%; }
  100% { background-position: 220% 50%; }
}
.q-stars-corrupt {
  background: linear-gradient(90deg, #4a0a0c, #c1242b, #ff6a5a, #8f1219, #4a0a0c);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: corruptBloodFlow 3.4s linear infinite;
}
.item-card.card-grade-corrupt { border: 2px solid #8f1219; }
.inv-slot.inv-eq-corrupt {
  position: relative;
  border: 2px solid #8f1219;
  background: linear-gradient(115deg, rgba(143,18,25,.34), rgba(58,6,9,.42), rgba(198,36,42,.28)), var(--bg3);
  background-size: 240% 100%;
  animation: corruptBloodFlow 3.4s linear infinite;
}
.inv-slot.inv-eq-corrupt::before {
  content: '';
  position: absolute;
  left: 4px; top: 4px;
  width: 6px; height: 6px;
  border-radius: 2px;
  background: #c1242b;
}
.inv-slot.inv-eq-corrupt::after {
  content: '异化';
  position: absolute;
  left: 13px; top: 2px;
  font-size: 9px;
  line-height: 1;
  color: #ff9a8c;
  opacity: .9;
}
.equip-slot-cell.inv-eq-corrupt {
  border: 2px solid #8f1219;
  background: linear-gradient(115deg, rgba(143,18,25,.34), rgba(58,6,9,.42), rgba(198,36,42,.28)), var(--bg3);
  background-size: 240% 100%;
  animation: corruptBloodFlow 3.4s linear infinite;
}

/* ===== 渔具栏（第八轮新增，用户口径）=====
 * 网格取 6 列：鱼竿占 5 列（约 5 格宽，**统计时仍只算 1 格**），余下 1 列正好并排一个普通渔具 ——
 * 「鱼竿和鱼篓 / 饵料可以同行」；两根鱼竿各占 5 列，第二根必然被挤到下一行（5+5 > 6），
 * 也就是「鱼竿和鱼竿不能同行」。格子里用半透明的该竿图片做背景（images/<id>.png）。
 * 写在档位小方块规则之后：同为 ::before，靠顺序压过「装备小方块」。 */
.tackle-grid {
  display: grid;
  grid-template-columns: repeat(6, 88px);
  gap: 6px;
  align-items: stretch;
}
.inv-slot.tackle-slot { position: relative; }
.inv-slot.tackle-rod { grid-column: span 5; width: auto; }
.inv-slot.tackle-slot::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-image: var(--tackle-bg);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  opacity: .36;
  pointer-events: none;
}

/* 道具也带左上角标注（用户要求：与装备一致的做法）：
 * 小方块取类型底色，右侧跟「道具」二字，扫一眼就知道这格是道具而不是材料或料理。 */
.inv-slot.inv-t-item { position: relative; }
.inv-slot.inv-t-item::before {
  content: '';
  position: absolute;
  left: 4px; top: 4px;
  width: 6px; height: 6px;
  border-radius: 2px;
  background: #5a9bdc;
}
.inv-slot.inv-t-item::after {
  content: '道具';
  position: absolute;
  left: 13px;
  top: 2px;
  font-size: 9px;
  line-height: 1;
  opacity: .85;
}

/* 异化装备占用的装备槽：流动的暗红（用户口径：像血一样流动）。
 * 装备槽格子上带内联 border/background，这里必须 !important 才压得住；
 * 与装备价值分档的角标互不冲突，可以叠在一起看。 */
.equip-slot-corrupt {
  border-color: #8f1219 !important;
  background: linear-gradient(115deg, rgba(143,18,25,.40), rgba(58,6,9,.46), rgba(198,36,42,.32)), var(--bg3) !important;
  background-size: 240% 100% !important;
  animation: corruptBloodFlow 3.4s linear infinite, equipCorruptGlow 2.6s ease-in-out infinite;
}
@keyframes equipCorruptGlow {
  0%, 100% { box-shadow: 0 0 7px rgba(150, 20, 26, .55), inset 0 0 9px rgba(110, 10, 16, .45); }
  50% { box-shadow: 0 0 18px rgba(232, 52, 52, .95), inset 0 0 15px rgba(176, 26, 32, .75); }
}

/* 车长交易列表里的好感等级：字号与旁边的「车长」标签一致（用户口径）。 */
.favor-lv-sm { font-size: .7rem; }

/* 好感度显示（用户口径）：不再用进度条，改成「友善 好感度 Lv.3 4/10」的文字。
 * 七档关系各一色：敌对暗红、陌生灰，其余五档按由冷到暖的顺序递进。 */
.favor-t-enemy { color: #b23b3b; }
.favor-t-stranger { color: #8a94a6; }
.favor-t-friendly { color: #5aa9c9; }
.favor-t-familiar { color: #7bc96f; }
.favor-t-trust { color: #5aa9e6; }
.favor-t-close { color: #b07ce8; }
.favor-t-soul { color: #e8a33d; }
.npc-card .npc-card-head { justify-content: flex-start !important; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.npc-favor, .vn-favor-text { font-size: .82rem; white-space: nowrap; }

/* 拖动物品（用户需求）：拖动中的格子变淡，悬停的容器加虚线高亮。
 * 手机端把滚动让给拖动，否则一拖页面就跟着滚。
 * 拖过格子时浏览器会顺手选中文字，这里把容器及其内部的选中一起关掉。 */
.inv-slot.dragging { opacity: .45; }
/* 拖动时跟着光标走的浮层（用户需求）：复制被拖的那一格，fixed 定位、translate 居中在光标上。
 * pointer-events:none 很关键 —— 它既不吃指针事件，也不会被 elementFromPoint 命中而挡住落点判定。 */
.inv-slot.inv-drag-ghost {
  position: fixed;
  z-index: 9999;
  margin: 0;
  pointer-events: none;
  opacity: .92;
  transform: translate(-50%, -50%);
  box-shadow: 0 8px 18px rgba(0,0,0,.45);
}
/* 光标当前压着的那个格子：告示这一格会落在哪。 */
.inv-slot.inv-drop-cell { outline: 2px solid var(--accent); outline-offset: 1px; }
/* 落点的虚线占位格（用户需求）：插进网格流里，把后面的格子推开，标出会插到哪儿。
 * 出现时轻轻弹一下，位置一动就重播，推开的手感更明显。 */
.inv-slot.inv-drop-placeholder {
  margin: 0;
  box-sizing: border-box;
  pointer-events: none;
  background: transparent;
  border: 2px dashed var(--accent);
  box-shadow: none;
  opacity: .8;
  animation: invDropIn .12s ease-out;
}
@keyframes invDropIn {
  from { transform: scale(.86); opacity: .25; }
  to { transform: scale(1); opacity: .8; }
}
/* 拖动期间指针会离开格子甚至离开窗口，必须让格子持续收到事件：
 * 触摸端尤其重要（Chrome 的 touch 默认会 setPointerCapture，但桌面鼠标不会）。
 * 见 ui.module.js 的 invDragStart。 */
.inv-slot { touch-action: none; -webkit-user-select: none; user-select: none; }
[data-drop-zone], [data-drop-zone] * { user-select: none; -webkit-user-select: none; }
[data-drop-zone].inv-drop-hot { outline: 2px dashed #b07ce8; outline-offset: 2px; }

/* 物品名完整显示：任意位置可断行，不省略、不裁切。 */
.inv-slot span { word-break: break-word; overflow-wrap: anywhere; }

/* 每种物品类型的格子底色（用户需求）：类型取自 item.type 的九种取值。
 * 用半透明色叠加在 var(--bg3) 上，换主题时底色跟着走，不会跳成固定色。 */
.inv-slot.inv-t-mat  { background: linear-gradient(rgba(160,140,90,.30), rgba(160,140,90,.30)), var(--bg3); }
.inv-slot.inv-t-ing  { background: linear-gradient(rgba(90,180,120,.26), rgba(90,180,120,.26)), var(--bg3); }
/* 鱼类是单独一类（用户口径）：不跟食材的绿底，给一层水蓝 */
.inv-slot.inv-t-fish { background: linear-gradient(rgba(80,165,215,.30), rgba(80,165,215,.30)), var(--bg3); border-color: #3d7ea6; }
.inv-slot.inv-t-dish { background: linear-gradient(rgba(220,150,80,.28), rgba(220,150,80,.28)), var(--bg3); }
.inv-slot.inv-t-item { background: linear-gradient(rgba(90,150,220,.26), rgba(90,150,220,.26)), var(--bg3); }
.inv-slot.inv-t-eq   { background: linear-gradient(rgba(150,120,220,.28), rgba(150,120,220,.28)), var(--bg3); }
.inv-slot.inv-t-scr  { background: linear-gradient(rgba(80,190,190,.26), rgba(80,190,190,.26)), var(--bg3); }
.inv-slot.inv-t-bp   { background: linear-gradient(rgba(200,180,80,.26), rgba(200,180,80,.26)), var(--bg3); }
.inv-slot.inv-t-coin { background: linear-gradient(rgba(230,190,70,.30), rgba(230,190,70,.30)), var(--bg3); }
.inv-slot.inv-t-mod  { background: linear-gradient(rgba(220,100,110,.26), rgba(220,100,110,.26)), var(--bg3); }
.inv-slot.empty { color: var(--text-dim); cursor: default; }
/* 竖屏：背包独占一行，杂物栏与钥匙栏并排在它下面 */
.inv-stack { display: flex; flex-direction: column; gap: .5rem; }
.inv-bottom-row { display: flex; gap: .5rem; align-items: flex-start; }
.inv-bottom-row > .panel { flex: 1 1 0; min-width: 0; }

@media (orientation: portrait) {
  .inv-slot { width: 78px; height: 70px; padding: 4px 3px; font-size: .66rem; }
  .inv-grid { gap: 5px; }
}

/* ============================================================
 * 工作台三栏：简易制造 / 烹饪 / 炼金 / 锻造 共用
 * 横屏是「材料 | 操作 | 配方」三栏并排，比例 1 : 1.15 : 1。
 * 竖屏视口只放得下一栏多，三栏各自挤到三分之一宽，材料名与按钮逐字换行，
 * 整块被拉成很长的竖条。竖屏改成单列堆叠，栏宽满格。
 * 列车车厢界面不在此列，那套布局原样保留。
 * ============================================================ */
.craft-layout { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: 10px; }
.craft-scroll { max-height: 520px; overflow: auto; text-align: left; }

@media (orientation: portrait) {
  .craft-layout { grid-template-columns: minmax(0, 1fr); gap: .5rem; }
  /* 简易制作竖屏的顺序与横屏相反：先看能造什么，再看选中的配方，最后才是材料。
   * 横屏三栏并排时这个顺序不影响观感，所以只写在竖屏段里。 */
  .craft-layout-mfg > *:nth-child(1) { order: 3; }  /* 背包与仓库材料 */
  .craft-layout-mfg > *:nth-child(2) { order: 2; }  /* 制作 */
  .craft-layout-mfg > *:nth-child(3) { order: 1; }  /* 已解锁制造 */
  /* 单列后每栏都满宽，长列表不让它各占半屏：限高按视口给，滚动留在栏内 */
  .craft-scroll { max-height: 38vh; }
  /* 材料行竖屏两列。窄屏那条规则只在视口 ≤640px 生效，
   * 竖屏窗口更宽时（平板竖屏、桌面拉成竖屏）材料会退化成单列，这里补一份同口径。 */
  .cook-ing-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .2rem .8rem;
    max-height: none;
    overflow: visible;
    align-content: start;
  }
  .list-row-left {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start !important;
    gap: .4rem;
    padding: 2px 0;
  }
  /* 温室：田格保持 6 列。实测 452px 宽下每格约 70px，作物名与倒计时都放得下；
   * 改成 4 列格子是大一圈，但 18 块田要多占两行，整页从 1020px 涨到 1257px，
   * 与「别被拉长」正好相反，所以列数不动。
   * 这里只做两件事：田区独占整宽不挤压，堆肥区不占侧栏、两只桶并排，省下一屏。 */
  .greenhouse-plots-panel { flex: 1 1 100%; min-width: 0; }
  .plot-grid { max-width: none; }
  .plot-cell .plot-crop-name { font-size: .74rem; }
  .greenhouse-compost-panel { flex: 1 1 100%; }
  .compost-list { flex-direction: row; flex-wrap: wrap; }
  .compost-list > * { flex: 1 1 45%; min-width: 0; }
}

/* ===== 钓鱼（用户需求）=====
 * 场景固定显示鱼的上下各 5 米：水面、水底、浮标、鱼线、鱼都会随收线在画面里升降。
 * 鱼的定位是「嘴锁画面中轴」，所以拉出水面时鱼嘴正好落在原浮标的位置。 */
.fish-scene {
  position: relative; height: 200px; margin-top: .6rem;
  border-radius: 10px; overflow: hidden; border: 1px solid var(--border);
  background: linear-gradient(180deg, rgba(158,200,228,.42), rgba(20,38,60,.8));
  font-size: 22px; touch-action: none; user-select: none; cursor: pointer;
}
.fish-water { position: absolute; left: 0; right: 0; background: linear-gradient(180deg, rgba(52,98,138,.4), rgba(4,10,20,.8)); }
.fish-surface { position: absolute; left: 0; right: 0; height: 2px; background: rgba(165,218,246,.65); transition: opacity .2s ease-out; }
.fish-floor { position: absolute; left: 0; right: 0; background: linear-gradient(180deg, #6b6047, #3b3527); border-top: 2px solid #8d8163; transition: opacity .2s ease-out; }
.fish-float {
  position: absolute; width: 13px; height: 13px; margin: -7px 0 0 -6.5px; border-radius: 50%;
  border: 1px solid rgba(0,0,0,.6);
  background: linear-gradient(180deg, #ef5346 0 50%, #dfe4e8 50% 100%);
  box-shadow: 0 1px 3px rgba(0,0,0,.35);
  transition: transform .34s cubic-bezier(.26, 1.35, .5, 1), opacity .18s ease-out;
  opacity: 0;
}
.fish-rope {
  position: absolute; left: 50%; width: 1px; margin-left: -.5px; transform-origin: top center;
  background: rgba(215,215,215,.55); opacity: 0; transition: opacity .18s ease-out;
}
.fish-bait {
  position: absolute; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; border-radius: 50%;
  background: #e0b072; box-shadow: 0 0 0 1px rgba(0,0,0,.45);
  opacity: 0; transition: opacity .18s ease-out;
}
.fish-emoji { position: absolute; left: 50%; transform: translateX(-50%); line-height: 0; opacity: 0; transition: opacity .2s ease-out; }
.fish-emoji span { display: inline-block; line-height: 1; will-change: transform; }
.fish-tenbar {
  position: absolute; left: 10px; right: 10px; top: 10px; height: 8px; border-radius: 4px;
  overflow: hidden; background: rgba(255,255,255,.14); opacity: 0; transition: opacity .2s ease-out;
}
.fish-tenbar i { display: block; height: 100%; width: 0%; background: #5aa9e6; transition: background-color .18s ease-out, width .1s linear; }
.fish-tenbar.warn i { background: #f0a92c; }
.fish-tenbar.danger i { background: #f2685c; animation: fishPulse .55s ease-in-out infinite; }
@keyframes fishPulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }

/* 场景里的元素由挂在 .fish-scene 上的状态类点亮：
 * 抛竿之后浮漂挂在水面（st-cast），咬钩时浮漂被拖进水下（st-hook），
 * 提竿之后线、饵、鱼才登场（st-line），搏斗时张力条才亮。 */
.fish-scene.st-cast .fish-float { opacity: 1; }
/* 咬钩：浮漂还挂在线上，只是被一路拽进水面以下；下沉要快，不跟抛竿那条慢过渡 */
.fish-scene.st-hook .fish-float { opacity: 1; transition: transform .12s linear, opacity .18s ease-out; }
.fish-scene.st-line .fish-rope,
.fish-scene.st-line .fish-bait,
.fish-scene.st-line .fish-emoji { opacity: 1; }
.fish-tenbar.fish-on { opacity: 1; }
/* 体力条挂在鱼的上方，只给长度不给数字：搏斗时才亮，鱼快力竭时转成金色 */
.fish-stabar {
  position: absolute; left: 50%; bottom: 100%; width: 44px; height: 5px;
  margin: 0 0 4px -22px; border-radius: 3px; overflow: hidden;
  background: rgba(255,255,255,.16); opacity: 0; transition: opacity .2s ease-out;
}
.fish-stabar i { display: block; height: 100%; width: 0%; background: #63c07a; transition: background-color .2s ease-out, width .1s linear; }
.fish-stabar.tired i { background: #f0c04a; }
.fish-stabar.fish-on { opacity: 1; }
/* 发力时从鱼身后冒的气泡：十四颗的固定池子，位置与透明度由 fishingBubblesStep 每帧算。
 * 图层排在鱼下面，鱼体挡住一部分才像在水里。 */
.fish-bubbles { position: absolute; left: 0; right: 0; top: 0; bottom: 0; pointer-events: none; opacity: 0; transition: opacity .2s ease-out; }
.fish-scene.st-line .fish-bubbles { opacity: 1; }
/* 背景鱼群：水里本来就有的鱼，只当景物，不参与搏斗 */
.fish-school { position: absolute; left: 0; right: 0; top: 0; bottom: 0; pointer-events: none; overflow: hidden; }
.fish-swim { position: absolute; left: 0; top: 0; display: block; line-height: 1; font-style: normal;
  transform-origin: 50% 50%; backface-visibility: hidden; opacity: 0; transition: opacity .35s ease-out; will-change: transform; }
/* 水深尺：贴右边，每米一格、每五米标数字 */
.fish-ruler { position: absolute; right: 0; top: 0; bottom: 0; width: 34px; pointer-events: none;
  background: linear-gradient(90deg, rgba(6,14,24,0), rgba(6,14,24,.42)); }
.fish-tick { position: absolute; right: 0; height: 1px; width: 7px; background: rgba(190,226,246,.34);
  opacity: 0; transition: opacity .2s ease-out; }
.fish-tick.major { width: 13px; background: rgba(190,226,246,.62); }
.fish-tick b { position: absolute; right: 16px; top: -0.6em; font-size: 10px; font-weight: 400; font-style: normal;
  color: rgba(206,234,250,.72); }
/* 背景鱼发力时尾后的气泡：与搏斗那套分开，几条鱼共用这个池子 */
.fish-puffs { position: absolute; left: 0; right: 0; top: 0; bottom: 0; pointer-events: none; overflow: hidden; }
.fish-puff { position: absolute; left: 0; top: 0; width: 3.2px; height: 3.2px; margin: -1.6px 0 0 -1.6px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.3);
  background: radial-gradient(circle at 34% 30%, rgba(255,255,255,.7), rgba(196,228,247,.24) 60%, rgba(150,200,230,.04));
  opacity: 0; will-change: transform, opacity; }
.fish-bubble {
  position: absolute; left: 0; top: 0; width: 4.4px; height: 4.4px; margin: -2.2px 0 0 -2.2px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.34);
  background: radial-gradient(circle at 34% 30%, rgba(255,255,255,.78), rgba(196,228,247,.3) 58%, rgba(150,200,230,.06));
  opacity: 0; will-change: transform, opacity;
}
/* 水底的草与石头：站在水底那条线上，整体压成低饱和、低明度的冷调 */
.fish-decor { position: absolute; left: 0; right: 0; bottom: 100%; height: 0; }
.fish-decor-item svg { display: block; width: 100%; height: 100%; overflow: visible; }
.fish-decor-item { position: absolute; line-height: 0; filter: saturate(.42) brightness(.72); }
.fish-decor-item.rock { filter: saturate(.25) brightness(.6) hue-rotate(185deg); }

/* 抛竿 / 提竿 / 收线是同一个按钮的三种文案：锁死按钮的盒子、让提示另起一行，
 * 文案再怎么换，按钮的位置与宽度都不动。投饵按钮跟抛竿同一行，排在左边。 */
.fish-btn-row { justify-content: center; }
#fishBtn { width: 9.5em; text-align: center; }
#fishBaitBtn { width: 5.6em; text-align: center; }
#fishHint { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 鱼的星级：一排五角星，亮起的按稀有度着色，其余的只留空心 */
.fish-stars { letter-spacing: .08em; font-size: 1.05em; }
.fish-star { color: rgba(255, 255, 255, .26); }
.fish-star.on.r1 { color: #ffffff; }
.fish-star.on.r2 { color: #6fc48a; }
.fish-star.on.r3 { color: #5aa9e6; }
.fish-star.on.r4 { color: #b48ce0; }
.fish-star.on.r5 { color: #e8c07d; }
.fish-meta { margin: .15rem 0 .45rem; }
/* 竿名换成可选的下拉框：跟抛竿那一行一样居中，宽度够放下「名字 · 等级 · 耐久」 */
.fish-rod-row { justify-content: center; }
.fish-rod-row select { min-width: 13em; text-align: center; }

/* 钓点描述窗里的开场：第二人称写这一带是什么水、你打算怎么下竿。
 * 它是单独的一步窗口，玩家点「准备好了」才进钓鱼界面。 */
.fish-intro {
  margin: .2rem 0 .5rem; padding: .15rem 0 .15rem .7rem;
  border-left: 2px solid var(--border); line-height: 1.7; color: #c9d3db;
}
.fish-intro p { margin: 0; }

/* 鱼篓容器：格子数与鱼篓的边长一致，只装鱼；卖鱼列表按个体一行一条。 */
.fish-grid { display: grid; gap: .35rem; margin-top: .5rem; }
.fish-cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .1rem; min-height: 3.6em; padding: .3rem .2rem; border: 1px dashed var(--border);
  border-radius: 6px; background: rgba(255,255,255,.03);
  font-size: .78rem; line-height: 1.25; text-align: center; color: #9aa4ad;
}
.fish-cell.on { border-style: solid; color: inherit; cursor: pointer; }
.fish-cell.on b { font-weight: 600; }
.fish-cell.on i { font-style: normal; font-size: .72rem; opacity: .75; }
/* 鱼详情弹窗：铺一张该鱼的半透明图片作背景（用户口径）。
 * 遮罩取**主题底色**（color-mix 混出 74% 不透明的 var(--bg)），所以切主题就跟着变，
 * 不会像固定深色那样「不管什么主题都黑压压一片」。 */
.modal { --fish-veil: color-mix(in srgb, var(--bg) 74%, transparent); }
.modal.fish-detail-bg {
  background-image: linear-gradient(var(--fish-veil), var(--fish-veil)), var(--fish-img);
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}
/* 用户口径：篓子里的鱼格子用**半透明的该鱼图片**做背景（images/<鱼 id>.png），
 * 文字仍在最上层；没有图的鱼种类自然只有底色。
 * 图片用 cover 铺满整格（保持比例、超出部分裁掉，不留白），
 * 用负偏移抵消格子的 padding，连内边距一起铺满，超出的方角由 overflow: hidden 裁成圆角。 */
.fish-cell.on { position: relative; overflow: hidden; }
.fish-cell.on::before {
  content: '';
  position: absolute;
  inset: calc(-.3rem - 1px) calc(-.2rem - 1px);
  background-image: var(--fish-bg);
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  opacity: .35;
  pointer-events: none;
}
.fish-cell.on > b, .fish-cell.on > i { position: relative; z-index: 1; }
.fish-sellrow {
  display: flex; justify-content: space-between; align-items: center; gap: .5rem;
  padding: .3rem 0; border-bottom: 1px solid var(--border);
}
.fish-sellrow:last-child { border-bottom: none; }
