/* ===== 基础 ===== */
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  --gold: #d9b45b;
  --gold-dim: #8d7333;
  --felt: #1d3b2a;
  --felt2: #16301f;
  --ink: #f2ead7;
  --red: #d9534f;
  --green: #4caf7d;
  --blue: #4aa3df;
  --purple: #9b6cc8;
  --mon: #b06a3b;
  --spell: #1f8a6d;
  --trap: #8d4fa8;
  /* 全局动画节奏系数：与 js/ui.js 的 PACE 保持一致，>1 放慢所有动画。
     历史：1.0 → 太快（看不清发生什么）→ 1.7 → 又太慢（AI 都打完了动画还在播）。
     1.3 = 中间值：单张卡的动作看得清，一回合的演出不会拖到玩家回合之后。
     ⚠ 改这里必须同步改 js/core.js 的 YG.PACE（JS 侧的定时器用它）。 */
  --fx: 1.3;
}
html, body { height: 100%; }
body {
  font-family: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", sans-serif;
  background: #0d1117;
  color: var(--ink);
  overflow: hidden;
  user-select: none;
}
b { font-weight: 700; }
.screen { display: none; height: 100%; }
.screen.active { display: flex; flex-direction: column; }

/* ===== 按钮 ===== */
.btn {
  background: linear-gradient(#3a4a63, #26313f);
  color: var(--ink); border: 1px solid #55677f; border-radius: 6px;
  padding: 8px 14px; cursor: pointer; font-size: 14px; font-family: inherit;
  transition: .15s;
}
.btn:hover { border-color: var(--gold); transform: translateY(-1px); }
.btn:active { transform: translateY(1px); }
.btn.primary { background: linear-gradient(#c9a24d, #9a7628); border-color: #e0c073; color: #241a05; font-weight: 700; }
.btn.ghost { background: transparent; }
.btn.big { padding: 14px 44px; font-size: 20px; letter-spacing: 3px; }
.btn.small { padding: 5px 10px; font-size: 12px; }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn.danger { background: linear-gradient(#c0504d, #8e2f2c); border-color: #e08a88; color: #fff; font-weight: 700; }

/* 战况里可点的卡名（点一下弹这张卡的详情） */
.cd-card { color: var(--gold); cursor: pointer; border-bottom: 1px dashed rgba(217,180,91,.6); }
.cd-card:hover { color: #f0d488; }

/* 墓地查看列表 */
.gy-list { max-height: 52vh; overflow: auto; display: flex; flex-direction: column; gap: 4px; margin: 6px 0; }
.gy-item { background: #1a2432; border: 1px solid #2f3d51; border-left: 3px solid var(--mon);
  border-radius: 5px; padding: 5px 8px; font-size: 12px; }
.gy-item.mon { border-left-color: #e8c14a; }
.gy-item .nm { cursor: pointer; color: var(--ink); font-weight: 600; }
.gy-item .nm:hover { color: var(--gold); text-decoration: underline; }
.gy-item small { display: block; color: #8fa0b6; font-size: 10.5px; margin-top: 1px; }

/* ===== 主菜单 =====
   ⚠ 这里**绝不能**用 `justify-content: center` 做垂直居中（2026-09-24 玩家实测：
   「电脑版开头页面最上面都被挡住了，决斗者遗产、怪兽决斗、名字输入都没了，拖不上去看」）。
   原因：flex 容器的 `justify-content:center` 在**内容比容器高**时，溢出会**同时**跑到上方和
   下方，而 `overflow:auto` 只能滚到下方溢出的部分 —— 上半截永远够不着。
   正确做法：容器不做居中，改成给子项 `margin: auto`（auto 外边距在放得下时居中、
   放不下时归零 → 内容从顶部开始，滚动条能滚到最上面）。 */
#menu { position: relative; overflow: auto; align-items: center; }
.menu-bg {
  /* fixed（不是 absolute）：#menu 会滚动，absolute 的背景只贴住第一屏，
     往下滚就会出现没有背景的空档。 */
  position: fixed; inset: 0;
  background:
    radial-gradient(ellipse at 50% 30%, #2a4a63 0%, #10151d 60%, #080a0e 100%);
}
.menu-bg::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(45deg, rgba(255,255,255,.02) 0 2px, transparent 2px 8px);
}
.menu-inner { position: relative; z-index: 1; width: min(1080px, 94vw); min-width: 0; padding: 22px 0; margin: auto 0; }
.title {
  font-size: 46px; letter-spacing: 8px; text-align: center; color: var(--gold);
  text-shadow: 0 0 24px rgba(217,180,91,.45), 0 4px 0 #3a2c0c;
}
.title span { display: block; font-size: 18px; letter-spacing: 12px; color: #cfd6e2; margin-top: 6px; text-shadow: none; }
.subtitle { text-align: center; color: #96a3b5; margin: 10px 0 22px; font-size: 13px; }
.subtitle b { color: var(--gold); }

/* ===== 决斗者名（开局页输入，输入即自动保存到 localStorage: yg_name_v1） =====
   用在对局的 LP 条 / 回合提示、童实野镇排行榜里「我」那一行。 */
.name-row {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: -10px 0 18px; flex-wrap: wrap;
}
.name-label { font-size: 12.5px; color: #96a3b5; letter-spacing: 2px; }
.name-input {
  background: #0f1620; border: 1px solid #35455c; color: var(--ink);
  padding: 7px 12px; border-radius: 6px; width: 220px;
  font-family: inherit; font-size: 13.5px; text-align: center;
}
.name-input:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 2px rgba(217,180,91,.18); }
.name-input::placeholder { color: #5d6b7d; }
.name-hint { font-size: 11px; color: #6f7d90; min-width: 56px; }
/* 当前称号小胶囊（2026-11-12）：和名字同排，未装备时 [hidden] 不占位 */
.name-title {
  font-size: 11px; letter-spacing: 1px; white-space: nowrap;
  color: var(--gold); background: rgba(217,180,91,.1);
  border: 1px solid rgba(217,180,91,.45); border-radius: 999px;
  padding: 2px 9px;
}
.name-title[hidden] { display: none; }
.name-hint.ok { color: #7fd67f; }

/* ===== 决斗内「数值输入」窗（拉之翼神龙支付基本分，2026-10-12） =====
   与 .name-input / 小镇输入框同一套配色，保证弹窗里不出现系统默认白底输入框。 */
.num-input {
  background: #0f1620; border: 1px solid #35455c; color: var(--ink);
  padding: 8px 10px; border-radius: 6px; width: 130px;
  font-family: inherit; font-size: 15px; text-align: center;
}
.num-input:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 2px rgba(217,180,91,.18); }
.num-range { font-size: 12.5px; color: #9fb0c6; margin: 4px 0 10px; }

/* ===== 头像（开局页自选 · 6 张预设） =====
   存档只记**名字**（yg_avatar_v1），图片 URL 由 js/avatar.js 的 Avatar.urlOf() 现算。 */
.avatar-row {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin: -12px 0 18px; flex-wrap: wrap;
}
.av-opts { display: flex; gap: 7px; flex-wrap: wrap; justify-content: center; }
.av-opt {
  width: 42px; height: 42px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 2px solid #35455c; background: #131b26; overflow: hidden;
  transition: .14s; position: relative;
}
.av-opt img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: .62; transition: .14s; }
.av-opt:hover { border-color: var(--gold-dim); transform: translateY(-1px); }
.av-opt:hover img { opacity: .9; }
.av-opt.sel { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(217,180,91,.25), 0 0 14px rgba(217,180,91,.35); }
.av-opt.sel img { opacity: 1; }
.av-opt.sel::after {   /* 选中的小对勾，手机上也一眼看得出选的是哪张 */
  content: '✓'; position: absolute; right: -1px; bottom: -1px;
  width: 15px; height: 15px; line-height: 15px; text-align: center;
  background: var(--gold); color: #241a05; font-size: 10px; font-weight: 700;
  border-radius: 50%;
}

/* 没有头像时的首字占位块（Avatar.urlOf() 返回空时才出现，绝不裂图） */
.av-fallback {
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(#3a4a63, #26313f); color: var(--gold);
  font-weight: 700; border-radius: 50%;
}

/* ===== 头像下拉预览（点任意头像 → 弹一张大头像，点别处收起）=====
   2026-09-24 玩家要求：「所有决斗者的头像，包括自己可以选的头像，如果单独点头像，
   会出来一个下拉，查看头像，这样，点其他地方又没了」。
   凡是带 `data-avname` 的头像（LP 条旁的 .pav / 挑战列表 .oi-av / 排行榜 .rk-av /
   预设选项 .av-opt）都能点开，逻辑见 js/ui.js 的 openAvatarPop。 */
.av-pop {
  position: fixed; z-index: 140;
  display: none; flex-direction: column; align-items: center; gap: 8px;
  padding: 12px 14px 10px; border-radius: 14px;
  background: linear-gradient(#1e2b3d, #10161f);
  border: 1px solid var(--gold-dim);
  box-shadow: 0 18px 46px rgba(0,0,0,.72);
  animation: avpopin .16s ease-out;
}
.av-pop.show { display: flex; }
.av-pop img, .av-pop .av-fallback {
  width: 120px; height: 120px; border-radius: 12px; object-fit: cover;
  border: 2px solid var(--gold); background: #131b26;
  box-shadow: 0 0 22px rgba(217,180,91,.35);
}
.av-pop .av-pop-title { font-size: 11px; color: #c8b48a; letter-spacing: 1px; margin-bottom: -6px;
  max-width: 170px; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.av-pop .av-pop-name { font-size: 14px; color: var(--gold); letter-spacing: 2px; max-width: 150px;
  text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.av-pop .av-pop-tip { font-size: 11px; color: #7c8b9f; }
.av-pop .av-pop-pick {
  margin-top: 2px; padding: 5px 12px; font-size: 12px; font-family: inherit;
  border-radius: 7px; cursor: pointer; color: #241a05; font-weight: 700;
  background: linear-gradient(#c9a24d, #9a7628); border: 1px solid #e0c073;
}
.av-pop .av-pop-pick.cur { background: #2b3646; border-color: #46586f; color: #9fb0c6; cursor: default; }
.av-pop .av-pop-arrow {
  position: absolute; width: 12px; height: 12px; background: #1e2b3d;
  border-left: 1px solid var(--gold-dim); border-top: 1px solid var(--gold-dim);
  transform: rotate(45deg);
}
@keyframes avpopin { from { opacity: 0; transform: translateY(-6px) scale(.94); } to { opacity: 1; transform: none; } }
/* 头像光标提示：让人一眼知道「这个图案是可以点的」 */
[data-avname] { cursor: pointer; }
/* ⚠ 2026-11-10 修「手机上决斗者挑战 / 我的卡组飞出去」：
   原来写 `1.15fr 1fr`，而 fr 的默认最小值是 auto（= 内容的 min-content），
   `.opp-item` 里「头像 + 不许折行的名字 + 势力徽章 + 星级」横向排在
   `.oi-h` 里，min-content 能到 500px 以上 → 栅格列撑破 .menu-inner，
   而手机版 body 是 overflow:hidden，于是整块被切在屏幕右侧、第二栏直接看不见。
   必须用 minmax(0, …) 把「可以缩到 0」的权利交回来，再配合内层 min-width:0 才真的能缩。 */
.menu-cols { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 16px; }
.menu-cols > * { min-width: 0; }
.panel-card {
  background: rgba(20,28,40,.82); border: 1px solid #35455c; border-radius: 10px;
  padding: 14px; min-height: 260px; min-width: 0;
}
.panel-card h3 { font-size: 15px; color: var(--gold); margin-bottom: 10px; letter-spacing: 2px; }
.tag { font-size: 12px; color: #9fb0c6; font-weight: 400; }

.opponent-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; max-height: 320px; overflow: auto; padding-right: 4px; min-width: 0; }
.opp-item {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  background: #1a2432; border: 1px solid #2f3d51; border-radius: 6px;
  padding: 8px 10px; cursor: pointer; font-size: 13px; transition: .12s;
}
.opp-item:hover { border-color: var(--gold); background: #22303f; }
.opp-item.sel { border-color: var(--gold); background: #3a2f10; box-shadow: inset 0 0 0 1px rgba(217,180,91,.4); }
.opp-item .st { font-size: 11px; color: #8fa0b6; }
.opp-item .oi-h { display: flex; justify-content: space-between; align-items: center; gap: 6px; min-width: 0; flex-wrap: wrap; row-gap: 3px; }
.opp-item .oi-n { font-weight: 600; color: #e8eef7; }
.opp-item .oi-s { color: #ffd75e; font-size: 11px; letter-spacing: 1px; }
/* 挑战列表里的决斗者头像（28 位决斗者全有图）。名字行改成「头像 + 名字 + 星级」。 */
.opp-item .oi-top { display: flex; align-items: center; gap: 7px; min-width: 0; flex: 1 1 auto; }
.opp-item .oi-av {
  width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
  border: 1px solid #4a5c74; background: #131b26;
}
.opp-item.sel .oi-av { border-color: var(--gold); }
.opp-item .oi-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opp-item .oi-d { color: #9fb0c6; font-size: 11px; line-height: 1.5; }
.opp-item .oi-r { color: #7f8ea3; font-size: 11px; }
.opp-note { font-size: 12px; color: #8fa0b6; line-height: 1.7; margin: -4px 0 8px; overflow-wrap: anywhere; }
.opp-note b { color: var(--gold); }

.deck-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; min-width: 0; }
.deck-preview {
  display: flex; flex-wrap: wrap; gap: 4px; max-height: 230px; overflow: auto;
  background: #131b26; border: 1px solid #26313f; border-radius: 6px; padding: 8px; min-height: 100px;
  min-width: 0;
}
/* 卡名可能很长（「青眼の究極竜」之类）：允许在格子里折行，别把「我的卡组」整栏顶出去 */
.dp-item { font-size: 11px; padding: 3px 6px; border-radius: 4px; background: #22303f; border-left: 3px solid var(--mon); max-width: 100%; overflow-wrap: anywhere; }
.dp-item.Spell { border-left-color: var(--spell); }
.dp-item.Trap { border-left-color: var(--trap); }

/* 底部按钮排。2026-11-11：「在线对战」搬回来后又多了一个按钮，20px 间距在 1000px 宽的
   窗口下会把「卡片图鉴」挤到第二行 —— 收到的 14px 让 7 个按钮仍能排成一行
   （手机版另有 gap:10px，见 html.ui-mobile .menu-foot）。 */
.menu-foot { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 22px; flex-wrap: wrap; }
.stats { color: #91a0b4; font-size: 13px; }
.stats b { color: var(--gold); }

/* ===== 卡 ===== */
.card {
  position: relative; border-radius: 5px; cursor: default;
  background: #2a2118; border: 2px solid #6b5533;
  display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 2px 6px rgba(0,0,0,.55);
  transition: transform .12s, box-shadow .12s;
}
.card.monster { border-color: #8a5a2c; background: linear-gradient(#c9a06a, #8f6b3c); }
.card.spell   { border-color: #2e8f74; background: linear-gradient(#4fb99a, #1f7a60); }
.card.trap    { border-color: #8b4fa8; background: linear-gradient(#a86fc0, #6b3a85); }
/* 效果怪兽（非「凡骨」）：橘色卡框，与通常/协调怪兽的土黄区分开。
   ⚠ 必须排在 .card.monster 之后（同级选择器，后者覆盖前者）。 */
.card.effect  { border-color: #e06a1f; background: linear-gradient(#ffa64d, #e06a1f); }
/* 融合 = 紫 / 同调 = 白（2026-09-27 玩家指定）。
   ⚠ 类名不能用 fus / syn —— 那两个被「额外卡组召唤落地演出」占用了
      （.card.slamfx.spsum.fus / .syn，见下文的登场动画），混用会互相踩。
   ⚠ 同样必须排在 .card.monster 与 .card.effect 之后才能盖住它们。 */
.card.fusion  { border-color: #8e44c9; background: linear-gradient(#cfa6f0, #7d47b8); }
.card.synchro { border-color: #c6d0dc; background: linear-gradient(#ffffff, #d3dbe4); }
.card .cname {
  font-size: 9px; line-height: 1.15; padding: 2px 3px; color: #23160a;
  font-weight: 700; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card .cstars { font-size: 7px; color: #4a3208; padding: 0 3px; letter-spacing: -1px; height: 9px; }
/* 魔法/陷阱的等高占位行：让它们的插画区和怪兽（有星级那一行）严格等尺寸。
   注意类名不能叫 .spacer —— 全局有 `.spacer { flex: 1 }`（布局用），会把占位行撑成弹性项。 */
.card .cstars.ph { flex: 0 0 9px; min-height: 9px; }
.card .cart {
  flex: 1; margin: 2px 3px; border-radius: 3px; position: relative;
  background: radial-gradient(circle at 50% 40%, #f6e2b8, #c8a86f 70%, #a9884f);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.card .cart .glyph { font-size: 22px; color: rgba(60,40,10,.62); font-weight: 700; }
/* 卡图：铺满卡框，保留原有的属性角标等叠加层。
   卡图源文件只裁了「插画本身」（不含卡框/卡名/文本框），所以直接 cover 铺满即可。 */
.card .cart .cartimg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; border-radius: 3px;
}
/* 有卡图时，占位字形（怪兽取种族首字「魔」，魔法/陷阱取「魔」「陷」）必须彻底隐藏。
   注意 applyArt 是用 insertBefore(img, firstChild) 插入的 —— <img> 排在 .glyph **前面**，
   于是 .glyph 靠 DOM 顺序反而盖在卡图之上，会在插画正中间留下一个半透明的浅色字。
   它只在没有卡图（.cart 的米黄渐变兜底）时才该出现。 */
.card .cart.has-art .glyph { display: none; }
.card .cart .attr {
  position: absolute; top: 2px; left: 2px; font-size: 8px; padding: 0 3px; border-radius: 3px;
  background: rgba(0,0,0,.35); color: #fff;
}
/* 「被装备 / 临时强化」角标：卡面已不显示攻守，但强化必须仍然可见 */
.card .boost {
  position: absolute; top: 2px; right: 2px; z-index: 3;
  font-size: 8px; line-height: 1; padding: 1px 2px; border-radius: 3px;
  background: rgba(18,58,20,.85); color: #8fe08b; border: 1px solid rgba(143,224,139,.65);
}
/* 数值被削弱（装备/临时强化净为负）—— 右上角转红 ▼，与「增强 = 绿 ▲」区分开 */
.card .boost.down {
  background: rgba(74,13,13,.92); color: #ff9b8a; border-color: rgba(255,124,104,.72);
}
.card .ctype { font-size: 7px; text-align: center; color: #3a2708; margin-top: 1px; }
.card .cstat {
  display: flex; justify-content: space-between; font-size: 8px; padding: 1px 3px 2px;
  color: #23160a; font-weight: 700;
}
.card .cstat .def { opacity: .8; }
.card.trap .cname, .card.spell .cname { color: #f3f7f4; }
.card.spell .cart, .card.trap .cart { background: radial-gradient(circle at 50% 40%, #dff3ea, #9fc9b8 70%, #7fae9c); }
.card.trap .cart { background: radial-gradient(circle at 50% 40%, #f0e0f7, #c8a3d8 70%, #a884bd); }
.card.spell .cstat, .card.trap .cstat { color: #f3f7f4; }
.card .cdesc { display: none; }

.card.facedown {
  background:
    repeating-linear-gradient(45deg, #3a2a55 0 6px, #2b1f40 6px 12px);
  border-color: #6b5a97;
}
.card.facedown > * { visibility: hidden; }
.card.facedown::after {
  content: ""; position: absolute; inset: 12%;
  border: 2px solid rgba(217,180,91,.55); border-radius: 4px;
  background: radial-gradient(circle, rgba(217,180,91,.32), transparent 68%);
}
/* 2026-10-07 玩家指定了真实卡背图（ui.js applyCardBack 写 el.style.backgroundImage）：
   有图时去掉斜纹底与那圈金色装饰框 —— 装饰框叠在卡背上像「重影」。 */
.card.facedown.has-back { background-repeat: no-repeat; background-color: transparent; }
.card.facedown.has-back::after { display: none; }
/* 场上盖伏的卡：只露出「盖伏」/「里侧」字样，卡名与攻守一律不外露（对齐原作 showUI） */
.card.facedown.setcard { cursor: pointer; }
.card.facedown.setcard .cname {
  visibility: visible; position: relative; z-index: 2;
  color: #ecdba6; font-size: 10.5px; letter-spacing: 2px; text-shadow: 0 1px 2px #000;
}
.card.set-mz.facedown {
  background: repeating-linear-gradient(45deg, #4d3520 0 6px, #382716 6px 12px);
  border-color: #9a7a4e;
}
.card.set-mz.facedown::after { border-color: rgba(217,180,91,.38); }
.card.sel { box-shadow: 0 0 0 2px var(--gold), 0 0 14px rgba(217,180,91,.75); }
.card.target { box-shadow: 0 0 0 2px #7fd1ff, 0 0 14px rgba(127,209,255,.7); cursor: pointer; }
.card.hit { animation: hit calc(.35s * var(--fx)); }
.card.summonfx { animation: summonfx calc(.5s * var(--fx)); }
.card.slamfx { animation: slamdrop calc(.66s * var(--fx)) cubic-bezier(.2,.9,.3,1.22); }
/* 特殊召唤（融合 / 同调 / 效果特召）：砸落与通常召唤同款，但**卡框镶红边 + 红光**，
   一眼就能分清「这是特殊召唤」而不是通常召唤。 */
.card.slamfx.spsum {
  border-color: #ff5a44;
  box-shadow: 0 0 0 2px rgba(255,90,68,.95), 0 0 24px rgba(255,58,44,.8);
}
/* 额外卡组召唤（融合 / 同调）：红边之上再换成专属配色 ——
   融合紫、同调银白。比效果特召更「重」，一眼分得出这是本回合的重头戏。 */
.card.slamfx.spsum.fus {
  border-color: #a86bff;
  box-shadow: 0 0 0 2px rgba(182,124,255,.95), 0 0 34px rgba(132,58,255,.9),
              inset 0 0 18px rgba(206,160,255,.55);
}
.card.slamfx.spsum.syn {
  border-color: #eaf6ff;
  box-shadow: 0 0 0 2px rgba(234,246,255,.95), 0 0 38px rgba(150,220,255,.92),
              inset 0 0 20px rgba(255,255,255,.6);
}
.card.destroyfx { animation: destroyfx calc(.45s * var(--fx)) forwards; }
@keyframes hit { 0%{transform:scale(1)} 40%{transform:scale(1.18) rotate(-4deg)} 100%{transform:scale(1)} }
@keyframes summonfx { 0%{transform:scale(.3) rotate(-14deg); opacity:0} 60%{transform:scale(1.12); opacity:1} 100%{transform:scale(1)} }
@keyframes destroyfx { 0%{opacity:1} 100%{opacity:0; transform: scale(.7) rotate(9deg); filter: blur(3px)} }
/* 召唤落地：卡牌从上方「砸」进怪兽区 —— 先放大发白降下，触地一沉再回弹 */
@keyframes slamdrop {
  0%   { transform: translateY(-38px) scale(1.75); opacity: 0; filter: brightness(2.6) saturate(.5); }
  46%  { transform: translateY(0) scale(1.07); opacity: 1; filter: brightness(1.55) saturate(1); }
  64%  { transform: translateY(0) scale(.96); }
  100% { transform: translateY(0) scale(1); filter: none; }
}

/* ===== 覆盖层卡牌特效（破坏 / 除外）=====
   render() 会重建整个场地 DOM，所以特效不能挂在场地卡上，
   必须在 #fx 里放一份独立的卡牌副本，动画才播得完整。 */
.fxcard {
  position: absolute; box-sizing: border-box; pointer-events: none;
  transform-origin: 50% 50%; will-change: transform, opacity, filter;
  border-radius: 6px; overflow: visible;
}
/* 破坏：先闪一下白光、整体一涨，然后碎裂塌陷并淡出 */
.fx-destroy { animation: fxdestroy calc(.62s * var(--fx)) cubic-bezier(.2,.7,.3,1) forwards; }
/* 发动源卡的金光闪光（墓地 / 手牌 / 场上发动通用）：亮起放大 → 金芒最盛 → 收缩淡出。
   墓地堆没有单卡 DOM，闪光落在墓地堆的计数牌上（同样是一块发光面板，视觉成立）。 */
@keyframes fxactivate {
  0%   { opacity: 0; transform: scale(.55); filter: brightness(1); }
  28%  { opacity: 1; transform: scale(1.14); filter: brightness(1.75) saturate(1.25); }
  62%  { opacity: 1; transform: scale(1.06); filter: brightness(1.3); }
  100% { opacity: 0; transform: scale(1.2); filter: brightness(1); }
}
.fxcard.fx-activate {
  animation: fxactivate calc(.95s * var(--fx)) ease-out forwards;
  box-shadow: 0 0 20px rgba(255,205,60,.9), 0 0 6px rgba(255,232,130,.95), inset 0 0 12px rgba(255,215,80,.55);
  border: 2px solid rgba(255,222,110,.95);
}
.fx-destroy.heavy { animation-duration: .78s; }
.fx-destroy::after {
  content: ""; position: absolute; inset: -4px; border-radius: 8px;
  background: radial-gradient(circle, rgba(255,236,190,.95), rgba(255,150,60,.55) 55%, transparent 72%);
  animation: fxflash calc(.3s * var(--fx)) ease-out forwards;
}
.fx-destroy.heavy::after {
  inset: -12px;
  background: radial-gradient(circle, rgba(255,255,220,.95), rgba(255,110,40,.6) 50%, transparent 70%);
  animation-duration: .42s;
}
@keyframes fxflash { 0%{opacity:0; transform:scale(.6)} 35%{opacity:1; transform:scale(1.06)} 100%{opacity:0; transform:scale(1.25)} }
/* 裂纹：几道亮线划过卡面，让「碎掉」看得更明白 */
.fx-destroy::before {
  content: ""; position: absolute; inset: 0; border-radius: 6px;
  background:
    linear-gradient(115deg, transparent 41%, rgba(255,255,255,.92) 43%, transparent 45%),
    linear-gradient(65deg,  transparent 57%, rgba(255,255,255,.85) 59%, transparent 61%),
    linear-gradient(160deg, transparent 29%, rgba(255,206,130,.85) 31%, transparent 33%),
    linear-gradient(20deg,  transparent 69%, rgba(255,255,255,.75) 71%, transparent 73%);
  mix-blend-mode: screen;
  animation: fxcrack calc(.55s * var(--fx)) ease-out forwards;
}
@keyframes fxcrack { 0%{opacity:0; transform:scale(.9)} 32%{opacity:1; transform:scale(1)} 100%{opacity:0; transform:scale(1.04)} }
@keyframes fxdestroy {
  0%   { transform: translate(0,0) scale(1) rotate(0deg); opacity: 1; filter: none; }
  14%  { transform: translate(0,-4px) scale(1.13) rotate(-5deg); opacity: 1; filter: brightness(2.1) saturate(.4); }
  30%  { transform: translate(2px,1px) scale(1.05) rotate(4deg); opacity: .96; }
  100% { transform: translate(0,20px) scale(.52) rotate(18deg); opacity: 0; filter: blur(5px) brightness(.5); }
}

/* 魔陷被破坏（送入墓地）：牌更薄、碎得更彻底 —— 裂纹加倍 + 残渣下沉。
   与怪兽的「破坏」区分：这条路径是效果炸掉魔陷（大岚 / 羽毛扫 / 小旋风…）。 */
.fx-destroy.st { animation: fxdestroyst calc(.8s * var(--fx)) cubic-bezier(.2,.7,.3,1) forwards; }
.fx-destroy.st::before {
  background:
    linear-gradient(115deg, transparent 39%, rgba(255,255,255,.95) 41%, transparent 43%),
    linear-gradient(65deg,  transparent 55%, rgba(255,255,255,.9) 57%, transparent 59%),
    linear-gradient(160deg, transparent 27%, rgba(255,214,150,.9) 29%, transparent 31%),
    linear-gradient(20deg,  transparent 67%, rgba(255,255,255,.85) 69%, transparent 71%),
    linear-gradient(85deg,  transparent 47%, rgba(255,190,120,.8) 49%, transparent 51%);
  animation-duration: .7s;
}
@keyframes fxdestroyst {
  0%   { transform: translate(0,0) scale(1) rotate(0deg); opacity: 1; filter: none; }
  16%  { transform: translate(0,-5px) scale(1.16) rotate(7deg); opacity: 1; filter: brightness(2.3) saturate(.35); }
  34%  { transform: translate(-3px,2px) scale(1.06) rotate(-6deg); opacity: .95; }
  100% { transform: translate(0,30px) scale(.42) rotate(-22deg); opacity: 0; filter: blur(6px) brightness(.45); }
}

/* 怪兽被效果送入墓地（神之怒 / 效果直接送墓）：下沉褪色，不是被打碎 */
.fx-togy { animation: fxtogy calc(.68s * var(--fx)) cubic-bezier(.4,.1,.6,.9) forwards; }
@keyframes fxtogy {
  0%   { transform: translateY(0) scale(1); opacity: 1; filter: brightness(1.6) saturate(.55); }
  24%  { transform: translateY(-7px) scale(1.07); opacity: 1; filter: brightness(1.9) saturate(.8); }
  100% { transform: translateY(28px) scale(.58); opacity: 0; filter: blur(3px) brightness(.55); }
}

/* 除外：被吸入异次元漩涡 —— 旋转缩小、边淡出边被紫色裂隙拉走 */
.fx-banish { animation: fxbanish calc(.9s * var(--fx)) cubic-bezier(.4,.1,.7,.9) forwards; }
.fx-banish::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: 190%; height: 190%;
  transform: translate(-50%,-50%); border-radius: 50%;
  background:
    radial-gradient(circle, rgba(0,0,0,.92) 0 18%, rgba(150,90,235,.75) 26%, rgba(80,200,255,.5) 42%, transparent 62%);
  border: 2px solid rgba(178,140,255,.75);
  box-shadow: 0 0 22px rgba(150,90,235,.85), inset 0 0 18px rgba(80,200,255,.6);
  animation: fxrift calc(.9s * var(--fx)) ease-in forwards;
}
@keyframes fxrift {
  0%   { opacity: 0; transform: translate(-50%,-50%) scale(.2) rotate(0deg); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(1.5) rotate(220deg); }
}
@keyframes fxbanish {
  0%   { transform: scale(1) rotate(0deg); opacity: 1; filter: none; }
  22%  { transform: scale(.96) rotate(-8deg); opacity: 1; filter: brightness(1.9) hue-rotate(35deg); }
  100% { transform: scale(.05) rotate(540deg); opacity: 0; filter: blur(4px) brightness(2.2) hue-rotate(80deg); }
}

/* 退路：量不到卡牌位置时，就在格子上放一圈光波，至少让玩家看到「那里出事了」 */
.fx-burst { animation: fxburst calc(.6s * var(--fx)) ease-out forwards; }
@keyframes fxburst {
  0%   { opacity: .95; transform: scale(.85); box-shadow: 0 0 0 0 rgba(255,180,90,.85); }
  100% { opacity: 0; transform: scale(1.3); box-shadow: 0 0 0 26px rgba(255,180,90,0); }
}

/* ===== 召唤落地光环（#fx 层，独立元素不受场地重建影响） =====
   和 .fx-burst 的区别：这是**落在召唤那只怪的格子上**的同心光环，用来给「砸下来」一个着地感。 */
.fxring {
  position: absolute; box-sizing: border-box; pointer-events: none;
  border-radius: 8px; will-change: transform, opacity, box-shadow;
  animation: fxring calc(.7s * var(--fx)) ease-out forwards;
}
.fxring::before {
  content: ""; position: absolute; inset: -34%; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,247,222,.6), rgba(255,222,150,.22) 45%, transparent 68%);
}
@keyframes fxring {
  0%   { opacity: .95; transform: scale(.5);
         border: 2px solid rgba(255,247,222,.95);
         box-shadow: 0 0 0 0 rgba(255,244,214,.85), 0 0 22px rgba(255,228,150,.9); }
  55%  { opacity: .6; transform: scale(1.08); }
  100% { opacity: 0; transform: scale(1.55);
         border-color: rgba(255,247,222,0);
         box-shadow: 0 0 0 34px rgba(255,244,214,0), 0 0 8px rgba(255,228,150,0); }
}
/* 特殊召唤的落地光环：同款同心圈，但整体转红（与卡框红边配套） */
.fxring.sp { animation: fxringsp calc(.7s * var(--fx)) ease-out forwards; }
.fxring.sp::before {
  background: radial-gradient(circle, rgba(255,232,224,.55), rgba(255,96,72,.26) 45%, transparent 68%);
}
@keyframes fxringsp {
  0%   { opacity: .95; transform: scale(.5);
         border: 2px solid rgba(255,158,136,.95);
         box-shadow: 0 0 0 0 rgba(255,120,96,.85), 0 0 22px rgba(255,74,56,.9); }
  55%  { opacity: .6; transform: scale(1.08); }
  100% { opacity: 0; transform: scale(1.55);
         border-color: rgba(255,158,136,0);
         box-shadow: 0 0 0 34px rgba(255,120,96,0), 0 0 8px rgba(255,74,56,0); }
}

/* 融合召唤的落地光环：紫色双圈 + 一道旋转的虚线圈（像炼金阵），范围比一般特召更大。
   ::before 是柔光，::after 是转动的虚线环。 */
.fxring.fus { animation: fxringfus calc(.95s * var(--fx)) ease-out forwards; }
.fxring.fus::before {
  inset: -40%;
  background: radial-gradient(circle, rgba(242,226,255,.72), rgba(150,80,255,.3) 46%, transparent 70%);
}
.fxring.fus::after {
  content: ""; position: absolute; inset: -16%; border-radius: 50%;
  border: 3px dashed rgba(198,146,255,.92);
  box-shadow: 0 0 28px rgba(162,92,255,.85);
  animation: fxringsweep calc(.95s * var(--fx)) linear forwards;
}
@keyframes fxringfus {
  0%   { opacity: .95; transform: scale(.45) rotate(-24deg);
         border: 2px solid rgba(228,200,255,.95);
         box-shadow: 0 0 0 0 rgba(182,112,255,.9), 0 0 30px rgba(150,80,255,.95); }
  60%  { opacity: .7; transform: scale(1.14) rotate(6deg); }
  100% { opacity: 0; transform: scale(1.75) rotate(16deg);
         border-color: rgba(228,200,255,0);
         box-shadow: 0 0 0 48px rgba(182,112,255,0), 0 0 10px rgba(150,80,255,0); }
}
@keyframes fxringsweep {
  0%   { opacity: 0; transform: rotate(0deg) scale(.55); }
  28%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(240deg) scale(1.4); }
}
/* 同调召唤的落地光环：银白多重同心圈 —— 像音叉共振一圈圈荡开，最大最亮 */
.fxring.syn { animation: fxringsyn calc(1s * var(--fx)) ease-out forwards; }
.fxring.syn::before {
  inset: -52%;
  background: radial-gradient(circle, rgba(255,255,255,.82), rgba(150,225,255,.32) 42%, transparent 70%);
}
.fxring.syn::after {
  content: ""; position: absolute; inset: -26%; border-radius: 50%;
  border: 2px solid rgba(228,246,255,.9);
  box-shadow: 0 0 32px rgba(140,215,255,.88), inset 0 0 26px rgba(255,255,255,.5);
  animation: fxsynpulse calc(1s * var(--fx)) ease-out forwards;
}
@keyframes fxringsyn {
  0%   { opacity: .98; transform: scale(.4);
         border: 2px solid rgba(238,250,255,.98);
         box-shadow: 0 0 0 0 rgba(160,225,255,.95), 0 0 36px rgba(120,205,255,.95); }
  55%  { opacity: .75; transform: scale(1.18); }
  100% { opacity: 0; transform: scale(1.9);
         border-color: rgba(238,250,255,0);
         box-shadow: 0 0 0 56px rgba(160,225,255,0), 0 0 12px rgba(120,205,255,0); }
}
@keyframes fxsynpulse {
  0%   { opacity: 0; transform: scale(.5); }
  22%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.9); }
}

/* ===== 落点大字（融合召唤！/ 同调召唤！）=====
   banner 在屏幕中路，这行字直接压在怪兽格上方，玩家视线不用离开场上。 */
.fxword {
  position: absolute; transform: translate(-50%, -100%);
  font-size: 26px; font-weight: 900; letter-spacing: 4px; white-space: nowrap;
  color: #fff; pointer-events: none; z-index: 96;
  text-shadow: 0 0 18px rgba(0,0,0,.92), 0 2px 0 rgba(0,0,0,.8);
  animation: fxwordpop calc(1.15s * var(--fx)) cubic-bezier(.2,.9,.3,1.2) forwards;
}
.fxword.fus {
  color: #ecd9ff;
  text-shadow: 0 0 24px rgba(170,90,255,.95), 0 0 9px rgba(255,255,255,.7), 0 2px 0 #2a0e4d;
}
.fxword.syn {
  color: #f2fbff;
  text-shadow: 0 0 26px rgba(120,210,255,.95), 0 0 11px rgba(255,255,255,.9), 0 2px 0 #06304a;
}
@keyframes fxwordpop {
  0%   { opacity: 0; transform: translate(-50%,-100%) scale(.5); }
  22%  { opacity: 1; transform: translate(-50%,-100%) scale(1.2); }
  40%  { transform: translate(-50%,-100%) scale(1); }
  76%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%,-180%) scale(1.06); }
}

/* ===== 被吹回手牌（弹回）=====
   卡牌从场上旋转着飞向拥有者的手牌区，位移由 JS 量好写进 --dx / --dy。 */
.fx-tohand { animation: fxtohand calc(.74s * var(--fx)) cubic-bezier(.36,.05,.5,1) forwards; }
@keyframes fxtohand {
  0%   { transform: translate(0,0) scale(1) rotate(0deg); opacity: 1; filter: brightness(1.9) saturate(.65); }
  16%  { transform: translate(0,-15px) scale(1.12) rotate(-7deg); opacity: 1; filter: brightness(2.15) saturate(.9); }
  100% { transform: translate(var(--dx, 0px), var(--dy, 120px)) scale(.4) rotate(240deg);
         opacity: 0; filter: blur(1px) brightness(1.3); }
}

/* ===== 控制权转移（心变 / 恋尸魔 / 战华之叛-吕奉先 / 弹跳的蛋糕科夫）=====
   卡片从一个怪兽格飞向另一个怪兽格（可能跨座位），位移由 JS 量好写进 --dx / --dy。
   以前这几张卡是「在自己场地凭空消失、在对方场地凭空出现」，完全没有画面。 */
.fx-move { animation: fxmove calc(.8s * var(--fx)) cubic-bezier(.4,.05,.45,1) forwards; }
@keyframes fxmove {
  0%   { transform: translate(0,0) scale(1) rotate(0deg); opacity: 1; filter: brightness(2.1) saturate(.7); }
  16%  { transform: translate(0,-16px) scale(1.12) rotate(7deg); opacity: 1; filter: brightness(2.2) saturate(.9); }
  78%  { transform: translate(calc(var(--dx, 0px) * .92), calc(var(--dy, 130px) * .92)) scale(1.02) rotate(-5deg); opacity: 1; }
  100% { transform: translate(var(--dx, 0px), var(--dy, 130px)) scale(1.08) rotate(0deg); opacity: 0; filter: brightness(1.5); }
}

/* ===== 卡组 / 墓地之间的飞行（2026-10-20 玩家要求）=====
   ① 从卡组送去墓地（光道 / 首领 扎鲁格 / 守墓的使魔…）：卡组堆 → 墓地堆
   ② 从场上回到卡组（光道武僧 艾琳）：场地格子 → 卡组堆
   ③ 从墓地回到卡组（独角兽之角 / 恶魔之吻 / 光之角 / 死亡精怪）：墓地堆 → 卡组堆
   起点不一定有卡牌 DOM（卡组/墓地只有计数堆），所以这张小卡是 JS 现造的，
   位移由 --dx / --dy 传入，方向对双方都成立（服务端按座位算好 toSelf）。 */
.fxcard.fx-pilecard {
  width: 54px; height: 76px; border-radius: 5px; overflow: hidden;
  background: linear-gradient(160deg, #16283a, #0a1622);
  border: 1px solid rgba(176,214,255,.7);
  box-shadow: 0 3px 12px rgba(0,0,0,.6), 0 0 12px rgba(110,180,255,.35);
}
.fxcard.fx-pilecard img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fx-pilefly { animation: fxpilefly calc(.74s * var(--fx)) cubic-bezier(.4,.06,.5,1) forwards; }
@keyframes fxpilefly {
  0%   { transform: translate(0,0) scale(1.06) rotate(0deg); opacity: 0; filter: brightness(2.2) saturate(.6); }
  18%  { transform: translate(0,-12px) scale(1.14) rotate(-6deg); opacity: 1; filter: brightness(1.8) saturate(.9); }
  72%  { transform: translate(calc(var(--dx, 0px) * .88), calc(var(--dy, 120px) * .88)) scale(1.02) rotate(8deg); opacity: 1; }
  100% { transform: translate(var(--dx, 0px), var(--dy, 120px)) scale(.5) rotate(18deg);
         opacity: 0; filter: blur(2px) brightness(1.15); }
}
.fx-todeck { animation: fxtodeck calc(.78s * var(--fx)) cubic-bezier(.36,.05,.5,1) forwards; }
@keyframes fxtodeck {
  0%   { transform: translate(0,0) scale(1) rotate(0deg); opacity: 1; filter: brightness(1.9) saturate(.7); }
  20%  { transform: translate(0,-18px) scale(1.14) rotate(7deg); opacity: 1; filter: brightness(2.2) saturate(.95); }
  100% { transform: translate(var(--dx, 0px), var(--dy, -140px)) scale(.42) rotate(-210deg);
         opacity: 0; filter: blur(1px) brightness(1.25); }
}
/* 落点轻顶：卡组/墓地是计数堆，不顶一下看不出「落在哪一堆」 */
.pile-bump { animation: pilebump calc(.5s * var(--fx)) ease-out; }
@keyframes pilebump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.1); box-shadow: 0 0 16px rgba(130,200,255,.85); }
  100% { transform: scale(1); }
}

/* ===== 决斗界面 ===== */
#duel { position: relative; }
.duel-wrap { display: flex; height: 100%; gap: 8px; padding: 8px; }
.field {
  flex: 1; display: flex; flex-direction: column;
  background:
    radial-gradient(ellipse at 50% 50%, #24503a 0%, var(--felt) 45%, var(--felt2) 100%);
  border: 2px solid var(--gold-dim); border-radius: 10px; padding: 8px 10px;
  box-shadow: inset 0 0 60px rgba(0,0,0,.5);
  min-width: 0;
}
.side { display: flex; flex-direction: column; gap: 5px; }
.side.me { justify-content: flex-end; }
.pbar { display: flex; align-items: center; gap: 8px; font-size: 12px; }
/* 头像（决斗界面 LP 条旁）：自己 = 玩家选的预设头像，对手 = 决斗者头像 / 联机对手头像。
   没有图时 .pav 保持空 src（浏览器不渲染），不占位也不裂图 —— 见 ui.js setAvatarImg()。 */
.pav {
  width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
  border: 1px solid var(--gold-dim); background: #131b26;
}
.pav:not([src]) { display: none; }
.pname { min-width: 92px; font-weight: 700; color: var(--gold); font-size: 13px; }
.lpwrap { position: relative; flex: 1; height: 16px; background: #2a1d1d; border: 1px solid #6b4a3a; border-radius: 8px; overflow: hidden; }
.lpbar { height: 100%; width: 100%; background: linear-gradient(90deg, #e05a4a, #f0a04b); transition: width .5s ease; }
.lpn { position: absolute; inset: 0; text-align: center; font-size: 11px; line-height: 16px; font-weight: 700; text-shadow: 0 1px 2px #000; }
.pill { background: rgba(0,0,0,.35); border: 1px solid #4b5f4f; border-radius: 10px; padding: 1px 8px; font-size: 11px; }
.pill b { color: #fff; }

.row { display: flex; gap: 6px; align-items: center; justify-content: center; min-height: 96px; }
.row.bottom { justify-content: space-between; }
.zones { gap: 8px; }
.slot {
  width: 74px; height: 100px; border: 1px dashed rgba(217,180,91,.28); border-radius: 5px;
  display: flex; align-items: center; justify-content: center; position: relative;
}
.slot.st { border-color: rgba(127,209,255,.22); }
.slot .card { width: 100%; height: 100%; }
.slot.empty::after { content: "空"; font-size: 10px; color: rgba(255,255,255,.18); }
.slot.target { border-color: #7fd1ff; border-style: solid; box-shadow: 0 0 12px rgba(127,209,255,.55); cursor: pointer; }
.slot.selectable { cursor: pointer; }
/* 🔴 2026-10-18 玩家需求：点开发动的永续陷阱 / 装备魔法，把与它关联的怪兽高亮出来。
   金色（与「选目标」的蓝 .target、「攻击目标」的红 online-target 明确区分）+ 呼吸光。 */
.slot.linked { border-color: #ffd166; border-style: solid; cursor: pointer; animation: linkPulse 1.1s ease-in-out infinite; }
@keyframes linkPulse {
  0%, 100% { box-shadow: 0 0 8px rgba(255,209,102,.5); }
  50% { box-shadow: 0 0 18px rgba(255,209,102,.95); }
}
/* 与怪兽关联的魔陷：右上角「🔗」角标（点这张卡才会亮起上面的 .linked 高亮） */
.slot .badge.link-badge {
  top: -8px; bottom: auto; right: -8px; font-size: 11px; padding: 0 3px; line-height: 1.4;
  background: rgba(18,14,6,.9); color: #ffd166; border-color: rgba(255,209,102,.85); border-radius: 7px;
}
.slot .badge {
  position: absolute; bottom: -2px; right: -2px; font-size: 9px; padding: 0 3px;
  background: #111; color: var(--gold); border: 1px solid var(--gold-dim); border-radius: 3px; z-index: 2;
}
/* 指示物角标（2026-11-09 玩家需求）：熟练的黑魔术师每发动一次魔法卡 +1 个魔力指示物。
   放**左下角**：卡名在顶部、攻/守在右下、🔗 在右上，左下是唯一不挡信息的位置
   （第一版放左上角，实测把卡名遮住了 —— 见 .tmp/ctr-badge.png 对比）。 */
.slot .badge.ctr-badge {
  bottom: -2px; left: -2px; top: auto; right: auto;
  background: rgba(30,20,52,.94); color: #c9b0ff;
  border-color: rgba(167,139,250,.9); border-radius: 4px;
  z-index: 3;
}
/* 手机版格子只有 54~64px 宽，角标缩一档，别和右下角的「攻/守」挤到一起 */
html.ui-mobile .slot .badge.ctr-badge { font-size: 8px; padding: 0 2px; }

.hand { display: flex; gap: 4px; justify-content: center; min-height: 92px; align-items: flex-end; }
.hand .card { width: 66px; height: 90px; cursor: pointer; }
.hand .card:hover { transform: translateY(-8px); z-index: 5; }
.opp-hand .card { width: 44px; height: 62px; cursor: default; }
.opp-hand .card:hover { transform: none; }
.stack { width: 56px; }
.pile {
  width: 56px; height: 80px; border-radius: 5px; font-size: 10px;
  background: repeating-linear-gradient(45deg, #3a2a55 0 6px, #2b1f40 6px 12px);
  border: 2px solid #6b5a97; display: flex; flex-direction: column;
  align-items: center; justify-content: center; color: #cdbde8; gap: 3px; cursor: pointer;
}
.pile b { font-size: 15px; color: #fff; }

.midline {
  height: 22px; margin: 4px 0; display: flex; align-items: center; justify-content: center;
  border-top: 1px solid rgba(217,180,91,.3); border-bottom: 1px solid rgba(217,180,91,.3);
  background: linear-gradient(90deg, transparent, rgba(217,180,91,.14), transparent);
}
.midline span { font-size: 11px; letter-spacing: 4px; color: var(--gold); background: #16301f; padding: 0 10px; }

/* ===== 侧栏 ===== */
.panel { width: 268px; display: flex; flex-direction: column; gap: 8px; }
.turnbox { background: rgba(20,28,40,.9); border: 1px solid #35455c; border-radius: 8px; padding: 8px 10px; }
.turnno { font-size: 12px; color: #9fb0c6; }
.turnno b { color: var(--gold); font-size: 16px; }
.whose { font-size: 14px; font-weight: 700; color: var(--gold); }
.cardview {
  background: rgba(20,28,40,.9); border: 1px solid #35455c; border-radius: 8px;
  padding: 10px; min-height: 190px; font-size: 12px;
}
.cv-empty { color: #6d7c8f; text-align: center; padding-top: 70px; font-size: 12px; }
.cv-art {
  text-align: center; margin-bottom: 8px;
}
.cv-art img {
  max-width: 100%; max-height: 210px; border-radius: 6px;
  border: 1px solid #4a5e78; box-shadow: 0 3px 10px rgba(0,0,0,.5);
}
.cv-name { font-size: 15px; font-weight: 700; color: var(--gold); margin-bottom: 4px; }
.cv-line { color: #b6c3d3; line-height: 1.6; }
/* 2026-10-18：详情里的「🔗 关联…」行（点开发动的永续陷阱 / 装备魔法时出现） */
.cv-link { color: #ffd166; line-height: 1.6; font-size: 12px; margin-top: 2px; }
.cv-line b { color: #fff; }
.cv-desc {
  margin-top: 6px; padding: 6px; background: #131b26; border-left: 3px solid var(--gold-dim);
  border-radius: 3px; color: #cfd9e6; line-height: 1.55; font-size: 11.5px; max-height: 120px; overflow: auto;
}
.cv-no { color: #d08a6a; font-size: 11px; margin-top: 4px; }
/* 凡骨怪兽的 flavor 文本：弱化成斜体，别和「效果文本」混为一谈 */
.cv-flavor {
  background: #10161f; border-left-color: #3a4553;
  color: #93a3b5; font-style: italic;
}
.logbox {
  flex: 1; background: rgba(12,17,24,.92); border: 1px solid #2b3a4d; border-radius: 8px;
  padding: 8px; overflow: auto; font-size: 11.5px; line-height: 1.65;
}
.logbox div { margin-bottom: 2px; word-break: break-all; }
.log-title { color: var(--gold); font-weight: 700; border-bottom: 1px solid #33425a; margin: 4px 0; }
.log-good { color: #8fd6a8; }
.log-bad { color: #ef9a90; }
.log-warn { color: #e8cf89; }
.log-info { color: #b8c4d4; }
.log-chat { color: #7fd3ff; font-weight: 600; }

/* ===== 悬浮操作菜单 ===== */
.actions {
  position: fixed; z-index: 60; display: none; flex-direction: column; gap: 3px;
  background: rgba(14,20,28,.97); border: 1px solid var(--gold-dim); border-radius: 8px;
  padding: 5px; box-shadow: 0 8px 26px rgba(0,0,0,.6); min-width: 128px;
}
.actions.show { display: flex; }
.actions button {
  background: #1e2937; border: 1px solid #3b4d64; color: var(--ink);
  padding: 7px 10px; border-radius: 5px; cursor: pointer; text-align: left; font-size: 13px; font-family: inherit;
}
.actions button:hover { background: #2c3b4e; border-color: var(--gold); }
.actions button:disabled { opacity: .35; cursor: not-allowed; }

/* ===== 弹层 ===== */
/* ⚠ z-index 必须高于 .result（100）：结算弹窗里的「神秘的卡片商人」也有「!」，
   点开要看卡图与详情 —— 若 modal 还在 100 以下，详情会被结算层盖住（点下去像没反应）。 */
.modal { position: fixed; inset: 0; background: rgba(0,0,0,.62); display: none; align-items: center; justify-content: center; z-index: 110; }
.modal.show { display: flex; }
.modal-box {
  background: #151d29; border: 1px solid var(--gold-dim); border-radius: 10px;
  padding: 16px; min-width: 300px; max-width: 620px; max-height: 84vh; overflow: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,.7);
}
.modal-box h4 { color: var(--gold); margin-bottom: 10px; font-size: 15px; }
/* 「决斗规则」弹窗标题行：左边标题、最右边一枚极小的「存档重置」chip
   （2026-10-23 玩家要求：原来底部那个横跨半行的大按钮太显眼 → 缩成标题右侧小按钮，
     底部只保留「明白了」一个主按钮。它是工具入口，不该跟「明白了」抢视觉重量：
     透明底 + 暗红文字，鼠标移上去才微微亮起，误触也不心疼（还有二次确认挡着）。 */
.modal-box .help-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-bottom: 10px;
}
.modal-box .help-head h4 { margin-bottom: 0; }
.modal-box .help-reset-mini {
  flex: 0 0 auto; font-family: inherit; cursor: pointer;
  background: transparent; color: #9c7a7e;
  border: 1px solid #563a3e; border-radius: 999px;
  padding: 2px 9px; font-size: 11px; line-height: 1.5; letter-spacing: .5px;
  transition: color .15s, border-color .15s, background .15s;
}
.modal-box .help-reset-mini:hover,
.modal-box .help-reset-mini:active {
  color: #ffb9ac; border-color: #8a5054; background: rgba(163,51,51,.18);
}
.modal-box p { margin-bottom: 12px; font-size: 13px; line-height: 1.6; }
/* 时点现场信息（发动确认窗的第二行）：对方哪只怪兽在攻击 / 刚召唤 / 发动了哪张卡。
   比标题弱、比卡面正文亮；左侧金线表示它属于「这次事件」而不是卡的效果文本。 */
.modal-box .cf-info {
  color: #cfe0f0; font-size: 12.5px; line-height: 1.5;
  margin: -4px 0 10px; padding-left: 8px; border-left: 2px solid var(--gold-dim);
}
/* 发动确认窗里的「相关怪兽」卡片条（2026-09-28：要能看到名字/星级/属性/种族/攻守）。
   横向可滚：一次可能同时出现「攻击方 + 被攻击方」两张。 */
.modal-box .cf-cards {
  display: flex; gap: 8px; overflow-x: auto; margin: 0 0 10px; padding-bottom: 2px;
}
.modal-box .cf-card {
  display: flex; gap: 8px; align-items: center; flex: 0 0 auto;
  background: #1b2634; border: 1px solid #3b4d64; border-radius: 8px; padding: 6px 10px 6px 6px;
  min-width: 210px; max-width: 320px;
}
.modal-box .cf-card-back { border-style: dashed; opacity: .85; background-size: cover; background-position: center; }
.modal-box .cf-card-art {
  width: 40px; height: 58px; object-fit: cover; border-radius: 4px;
  border: 1px solid #46597a; flex: 0 0 auto; background: #0f1620;
}
.modal-box .cf-card-body { min-width: 0; }
.modal-box .cf-card-name {
  color: var(--gold); font-size: 13px; font-weight: 700; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 240px;
}
.modal-box .cf-card-line { color: #cfe0f0; font-size: 11.5px; line-height: 1.45; }
.modal-box .cf-card-line b { color: #fff; }
.modal-box .cf-delta { margin-left: 6px; font-size: 11px; font-weight: 700; }
.modal-box .cf-delta.up { color: #7fd67f; }
.modal-box .cf-delta.down { color: #ff9b8a; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; flex-wrap: wrap; }
/* ===== 开包结果弹窗（2026-10-17 玩家要求）=====
   「买了卡包应该是出来一个单独的窗口显示抽到了那些卡片，然后每张卡片旁边有感叹号，
    点了就会告诉这个卡片详情」。
   🔴 z-index 105 < #modal 的 110：卡片详情（UI.showBig）也是 #modal，必须盖在开包清单上面，
      关掉详情后清单还在（复用同一个浮层就会「看详情 → 关详情 = 清单也没了」）。 */
.modal.pack-modal { z-index: 105; }
.pack-box { width: min(660px, 94vw); max-width: 660px; }
.pack-box h4 { margin-bottom: 4px; }
.pack-sum { color: #a8b8cc; font-size: 12.5px; line-height: 1.7; margin: 0 0 10px; }
.pack-sum b { color: var(--gold); }
.pack-sum .good { color: #7ed6a5; }
/* 抽到的卡一格一张：左卡图 / 中卡名+数值 / 右「!」（「!」用全工程同一个 .info-dot）
   ⚠ 列宽 260px 起步：三列会让「★2 ATK 900/DEF 500」折成两行（孤零零一个 500），
     两列才放得下一行 —— 660px 弹窗里正好两列。 */
.pull-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; }
.pull-card {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  padding: 7px 8px; border-radius: 8px;
  background: #1b2634; border: 1px solid #33455c;
}
.pull-card.rare { border-color: #b8860b; background: linear-gradient(135deg, #2a2109, #1b2634); }
.pull-art {
  width: 44px; height: 64px; flex: 0 0 auto; border-radius: 4px;
  border: 1px solid #46597a; background: #0f1620;
  object-fit: cover;
}
.pull-art.noart {
  display: flex; align-items: center; justify-content: center;
  color: #48607e; font-size: 10px;
}
.pull-tx { flex: 1 1 auto; min-width: 0; }
.pull-name {
  display: block; color: #eaf2ff; font-size: 13px; line-height: 1.3;
  word-break: break-word;
}
.pull-tx .badge { margin-top: 3px; }
.pull-tx small { display: block; color: #8fa0b6; font-size: 11px; margin-top: 2px; }
.pull-card .info-dot { flex: 0 0 auto; margin: 0; }
/* 手机版：单列（一屏一眼能连着卡名读），弹窗本体沿用 html.ui-mobile .modal-box 的 94vw */
html.ui-mobile .pull-grid { grid-template-columns: 1fr; }
html.ui-mobile .pull-card { padding: 8px; }
html.ui-mobile .pull-art { width: 50px; height: 72px; }
/* ===== 卡库清单弹窗（2026-10-12 玩家要求：卡名 + 类型的可复制文本） =====
   文本块用 <pre>：卡名各自包 <span class="vl-name"> → 纯文本可直接复制、卡名可点开详情。
   ⚠ 必须能选中（默认 <pre> 可选中；这里不动 user-select），且窄屏不撑破弹窗。 */
.vl-seg { display: flex; gap: 5px; margin: 0 0 8px; flex-wrap: wrap; }
.vl-text {
  margin: 0; padding: 10px 12px; background: #10161f; border: 1px solid #2f3d51; border-radius: 8px;
  max-height: 52vh; overflow: auto; white-space: pre; tab-size: 2;
  font-family: ui-monospace, Menlo, Consolas, "Courier New", monospace;
  font-size: 12.5px; line-height: 1.75; color: #cfe0f0;
}
.vl-text .vl-head { color: var(--gold); font-weight: 700; }
.vl-text .vl-name { color: #eaf2ff; cursor: pointer; border-bottom: 1px dashed #4a6180; }
.vl-text .vl-name:hover { color: var(--gold); border-bottom-color: var(--gold); }
/* ===== 卡库清单 · 分享版（2026-10-12 第二轮：线上玩家发来的清单） =====
   .vl-text 是**纯文本**（给复制用）；这里是**分行可点**的卡片格子（给"看 + 点 ! 看详情"用）。
   同一个类型分组顺序、同一个类型标签口径（都走 Town.vaultListData / YG.cardTypeLabel）。
   ⚠ 每行的「!」直接复用卡库那一个 .info-dot —— 别在这里重画一个感叹号。 */
.vs-list { max-height: 52vh; overflow: auto; margin: 0; }
.vs-head {
  position: sticky; top: 0; z-index: 1;
  margin: 8px 0 4px; padding: 3px 2px; color: var(--gold); font-weight: 700; font-size: 12.5px;
  background: #151d28; border-bottom: 1px solid #2f3d51;
}
.vs-head:first-child { margin-top: 0; }
.vs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 1px 10px; }
.vs-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 3px 4px; border-radius: 5px;
  font-size: 13px; color: #eaf2ff; min-width: 0;
}
.vs-row:hover { background: #18222f; }
.vs-row .info-dot { flex: 0 0 auto; margin-right: 0; }
.vs-row .vs-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vs-row .vs-count { flex: 0 0 auto; color: var(--gold); font-size: 12px; font-weight: 700; }
/* 2026-10-13：怪兽在卡名下面补一行「攻击 · 种族 · 卡包」（flex: 1 0 100% 强制独占一行） */
.vs-row .vs-meta { flex: 1 0 100%; color: #8fa0b6; font-size: 11px; margin-left: 16px; }
.vs-tools { margin: 6px 0 4px; flex-wrap: wrap; }
.sel-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; max-height: 46vh; overflow: auto; }
.sel-item {
  background: #1e2937; border: 1px solid #3b4d64; border-radius: 6px; padding: 7px 9px;
  cursor: pointer; font-size: 12.5px; transition: .12s;
}
.sel-item:hover { border-color: var(--gold); background: #2c3b4e; }
.sel-item small { display: block; color: #8fa0b6; font-size: 11px; margin-top: 2px; }
.sel-item .where { color: var(--gold); font-size: 10.5px; }

/* ===== 特效飘字 ===== */
.fx { position: fixed; inset: 0; pointer-events: none; z-index: 90; }
.dmg {
  position: absolute; font-size: 34px; font-weight: 800; color: #ff6a5a;
  text-shadow: 0 0 12px rgba(255,60,40,.8), 0 3px 0 #4a0d08;
  animation: dmgup calc(1.1s * var(--fx)) forwards;
}
.heal { color: #7ce6a4; text-shadow: 0 0 12px rgba(60,220,130,.7); }
@keyframes dmgup {
  0% { transform: translate(-50%, 0) scale(.5); opacity: 0; }
  25% { transform: translate(-50%, -14px) scale(1.25); opacity: 1; }
  100% { transform: translate(-50%, -70px) scale(1); opacity: 0; }
}
.banner {
  position: fixed; top: 40%; left: 50%; transform: translate(-50%, -50%);
  font-size: 40px; font-weight: 800; letter-spacing: 8px; color: var(--gold);
  text-shadow: 0 0 26px rgba(217,180,91,.85), 0 4px 0 #3a2c0c;
  animation: bannerfx calc(.65s * var(--fx)) forwards; z-index: 95; pointer-events: none; white-space: nowrap;
}
/* 带卡图的发动横幅：插画在上、卡名横幅在下（敌我发动通用） */
.banner.has-art {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  letter-spacing: 4px;
}
.banner .banner-art {
  display: block; width: 150px; height: 150px; object-fit: cover;
  border-radius: 8px; border: 2px solid var(--gold);
  box-shadow: 0 0 30px rgba(217,180,91,.75), 0 6px 18px rgba(0,0,0,.6);
}
.banner .banner-text { display: block; }
/* 回合标记横幅（回合开始 / 回合结束）：字号略小、冷色，和「召唤 / 发动 / 攻击」区分开，
   玩家一眼就知道这是回合切换而不是某张卡的动作。 */
.banner.turn {
  font-size: 30px; letter-spacing: 6px; color: #cfe9ff;
  /* 时长必须与 ui.js `_drainBanner` 里 turn 类的 life（700 * PACE）对齐 */
  animation: bannerfx calc(.7s * var(--fx)) forwards;
  text-shadow: 0 0 24px rgba(110,190,255,.9), 0 4px 0 #123048;
}
/* 融合 / 同调召唤的横幅：配色与落地光环配套（紫 / 银白），字比一般动作更大更亮，
   一眼就知道这不是普通召唤。 */
.banner.fus {
  color: #ecd9ff; font-size: 42px; letter-spacing: 6px;
  text-shadow: 0 0 32px rgba(170,90,255,.95), 0 0 10px rgba(255,255,255,.6), 0 4px 0 #2a0e4d;
}
.banner.syn {
  color: #f2fbff; font-size: 42px; letter-spacing: 6px;
  text-shadow: 0 0 34px rgba(120,210,255,.95), 0 0 13px rgba(255,255,255,.85), 0 4px 0 #06304a;
}
/* 发动横幅（魔法 / 陷阱 / 怪兽效果）：**停留加长版**。
   普通横幅 .65s 就淡出，玩家实测「魔法卡没看到就发动，闪太快」—— 卡名 + 卡图
   根本读不完。这里换成 1.05s 的专属 keyframes：快速弹入 → 长时间保持 → 末尾才淡出。
   ⚠ 必须和 ui.js `_drainBanner` 里 cls 'act' 的 life（1050 * PACE）对齐。
   2026-09-24 玩家反馈「动画又太慢了，到我回合都没播完」：由 1.5s 收紧到 1.05s。 */
.banner.act {
  animation: banneractfx calc(1.05s * var(--fx)) forwards;
}
.banner.act.has-art { letter-spacing: 4px; }
@keyframes banneractfx {
  0%   { opacity: 0; transform: translate(-50%,-50%) scale(.7); }
  12%  { opacity: 1; transform: translate(-50%,-50%) scale(1.08); }
  24%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
  88%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(1.1); }
}
@keyframes bannerfx {
  0% { opacity: 0; transform: translate(-50%,-50%) scale(.6); }
  30% { opacity: 1; transform: translate(-50%,-50%) scale(1.1); }
  75% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%,-50%) scale(1.25); }
}

/* ===== 结算 ===== */
/* ⚠ 结算画面必须能滚：content 高了以后，flex 的 align-items:center 会把顶部推出视口且**滚不回来**，
   底部按钮就永远够不着。解法是 align-items:flex-start + 子项 margin:auto —— 空间够时照样居中，
   内容超高时 auto 归零、从顶部排布，滚动条也就正常工作了。 */
.result {
  position: fixed; inset: 0; background: rgba(0,0,0,.8); display: none;
  align-items: flex-start; justify-content: center; z-index: 100;
  overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 20px 12px;
}
.result.show { display: flex; }
.result-box {
  background: linear-gradient(#1b2534, #101722); border: 2px solid var(--gold);
  border-radius: 14px; padding: 34px 54px; text-align: center; box-shadow: 0 0 60px rgba(217,180,91,.28);
  margin: auto;                 /* 配合 .result 的 flex-start：矮时居中，高时从顶部开始 */
}
.result-box h2 { font-size: 36px; color: var(--gold); letter-spacing: 8px; margin-bottom: 10px; }
.result-box p { color: #c3cddb; margin-bottom: 20px; font-size: 14px; }
/* ===== 结算画面 · 对阵头像（我 vs 对手）=====
   只在这一处同时亮出双方头像：一局打完，玩家一眼看清自己跟谁打的
   （挑战者是随机抽的 3 位 + 固定决斗者之脑，名字扫一眼根本记不住是谁）。
   胜方头像描金；名字取自 showResult / online.js 的同一套称呼（「你 / 对手」）。 */
.rw-vs { display: flex; align-items: flex-start; justify-content: center; gap: 16px; margin: 2px 0 16px; }
/* 2026-11-12 称号上线后名字变长：「【小浣熊】武藤游戏」在 74px 里会被截成「【小浣熊】…」。
   改成按内容加宽 + 允许折行（仍保留 min-width 让两侧栏位基本对齐）。 */
.rw-vs .rw-side { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 74px; max-width: 132px; }
.rw-vs .rw-av {
  width: 54px; height: 54px; border-radius: 50%; object-fit: cover;
  border: 2px solid #4a5c74; background: #131b26;
}
.rw-vs .rw-av.win { border-color: var(--gold); box-shadow: 0 0 16px rgba(217,180,91,.45); }
.rw-vs .rw-side small {
  font-size: 11.5px; color: #a9b7c9; max-width: 132px;
  /* 称号 + 名字可能两行（2026-11-12）：宁可折行也不要把「【称号】…」截成省略号 */
  white-space: normal; overflow-wrap: break-word; text-align: center; line-height: 1.35;
}
.rw-vs .rw-side.lose small { color: #7f8ea3; }
.rw-vs .rw-vs-t { align-self: center; color: var(--gold-dim); font-size: 14px; font-weight: 700; letter-spacing: 2px; }
/* 结算画面：特殊胜利（目前只有艾克佐迪亚）—— 单独点出来，别混在普通胜利里 */
.rw-special {
  margin: 0 0 18px; padding: 9px 16px;
  background: rgba(217,180,91,.10); border: 1px solid var(--gold-dim); border-radius: 8px;
  color: var(--gold); font-size: 14px; letter-spacing: 1px; line-height: 1.7;
}
.rw-special small {
  display: block; margin-top: 3px;
  color: #b9c6d6; font-size: 11.5px; letter-spacing: 0; line-height: 1.6;
}

/* ===== 结算弹窗里的奖励明细（普通决斗） ===== */
.rw-extra { margin-top: 2px; }
.rw-box {
  background: #0f1620; border: 1px solid #2f3d51; border-radius: 8px;
  padding: 10px 14px; margin: 0 0 18px; text-align: left; max-width: 460px;
}
.rw-box h4 { color: var(--gold); font-size: 14px; letter-spacing: 2px; margin-bottom: 6px; }
.rw-box .lines { font-size: 13px; color: #d7e2f0; line-height: 1.9; }
.rw-box .lines .good { color: #7ed6a5; }
.rw-box .lines .bad { color: #ff8a8a; }
.rw-box .lines .rare { color: #ffd75e; font-weight: 700; }
.rw-box .lines small { color: #8fa0b6; font-size: 11px; }
/* 「重复获得」提示：奖励不查卡库，抽到已有的卡是正常的，得让玩家看出来 */
.rw-box .lines small.dup { color: #e8a06a; }

/* ===== 结算画面 · 战况（回答「我到底是怎么赢 / 怎么输的」） ===== */
.rw-bt { max-width: 520px; }
.rw-bt h4 small {
  display: block; margin-top: 3px; color: #93a5bb;
  font-size: 11.5px; letter-spacing: 0; font-weight: 400;
}
.rw-bt-tb {
  width: 100%; border-collapse: collapse; margin: 4px 0 10px;
  font-size: 12.5px; color: #cdd8e6;
}
.rw-bt-tb th, .rw-bt-tb td {
  padding: 4px 8px; text-align: right; border-bottom: 1px solid #22303f;
}
.rw-bt-tb th:first-child, .rw-bt-tb td:first-child {
  text-align: left; color: #8fa0b6; width: 84px; font-weight: 400;
}
.rw-bt-tb th { color: var(--gold); font-size: 12px; letter-spacing: 1px; font-weight: 600; }
.rw-bt-tb th.w { color: #7ed6a5; }
.rw-bt-tb th.l { color: #ff8a8a; }
.rw-bt-tb td.up { color: #ffd75e; font-weight: 700; }
.rw-bt-deal {
  margin: 0 0 8px; padding: 6px 10px; border-radius: 6px; text-align: left;
  background: rgba(217,180,91,.10); border: 1px solid var(--gold-dim);
  font-size: 12.5px; color: #e6dcc4; line-height: 1.7;
}
.rw-bt-deal .lbl {
  display: inline-block; margin-right: 8px; padding: 1px 7px; border-radius: 4px;
  background: rgba(217,180,91,.22); color: var(--gold); font-size: 11px; letter-spacing: 1px;
}
.rw-bt-deal b.win { color: #7ed6a5; }
.rw-bt-tl {
  max-height: 168px; overflow-y: auto; text-align: left;
  font-size: 12px; line-height: 1.75; color: #c3cedc;
  border-top: 1px dashed #2f3d51; padding-top: 6px;
}
.rw-bt-ev { padding: 1px 0; }
.rw-bt-ev i {
  display: inline-block; min-width: 32px; margin-right: 6px;
  color: #7f8fa5; font-style: normal; font-size: 11px;
}
.rw-bt-ev.p0 { color: #bcd8ff; }
.rw-bt-ev.p1 { color: #ffd0d0; }
.rw-bt-ev.muted { color: #8fa0b6; }
.rw-foot {
  margin-top: 8px; padding-top: 8px; border-top: 1px dashed #2f3d51;
  font-size: 12px; color: #9fb0c6;
}

/* ===== 卡片商人（决斗胜利后 20% 出现） ===== */
.rw-merchant {
  background: linear-gradient(180deg,#1a140a,#120d06); border: 1px solid #5a4318;
  border-radius: 8px; padding: 10px 14px; margin: 0 0 18px; text-align: left; max-width: 460px;
}
.rw-merchant h4 { color: #ffd75e; font-size: 14px; letter-spacing: 2px; margin-bottom: 4px; }
.rw-merchant .muted { color: #b9a888; font-size: 12px; margin: 2px 0 8px; }
/* 两列等宽：minmax(0,1fr) 而不是 1fr —— 1fr 的 min 是 auto，长卡名会把列撑开、
   把左边那张挤窄（截图里一眼能看出左右不等宽）。 */
.merchant-goods { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
/* 明码标价的商品（稀有卡 / 传说卡）右边挂一枚「!」→ 看这张卡的卡图与详情。
   外面套一层 .mgood 而不是把「!」塞进按钮：<button> 里再放 <button> 是非法结构，
   而且点「!」会连带触发购买。 */
.merchant-goods .mgood { display: flex; align-items: center; gap: 6px; min-width: 0; }
.merchant-goods .mgood .mbtn { flex: 1 1 auto; min-width: 0; }
.merchant-goods .mgood .info-dot { margin-right: 0; }
.merchant-goods .mbtn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: #241a09; border: 1px solid #6b5220; color: #f0e2c0;
  border-radius: 6px; padding: 8px 10px; cursor: pointer; font-family: inherit; text-align: left;
}
.merchant-goods .mbtn:hover:not(:disabled) { background: #31230c; border-color: #ffd75e; }
.merchant-goods .mbtn:disabled, .merchant-goods .mbtn.bought { opacity: .45; cursor: default; }
.merchant-goods .mbtn b { font-size: 13px; color: #ffe9a8; }
.merchant-goods .mbtn small { font-size: 11px; color: #c8b48a; line-height: 1.4; }
.merchant-log { min-height: 18px; margin: 8px 0 4px; font-size: 12px; color: #7ed6a5; line-height: 1.5; }
.merchant-log:empty { display: none; }

/* ===== 编辑器 ===== */
/* 页头：flex:0 0 auto 保证它在任何屏高/换行下都不被内容挤压（被压掉 = 返回按钮消失） */
.bar {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px;
  background: #131b26; border-bottom: 1px solid #2b3a4d;
  flex: 0 0 auto; min-height: 44px;
}
.bar h2 { font-size: 16px; color: var(--gold); letter-spacing: 2px; }
.spacer { flex: 1; }
/* 行高必须显式声明：只写 auto 时，grid 的 align-content 默认 stretch 会把「剩余高度」
   平分给两个 auto 行 —— 于是「卡组槽条」的高度会跟着右侧「当前卡组」的内容变（切到空卡组
   就把 5 个槽位拉成一长条，2026-10-08 玩家实测）。固定第 1 行 = 内容高、第 2 行 = 剩余高度。 */
.editor-body { flex: 1; display: grid; grid-template-columns: 1fr 300px; grid-template-rows: auto minmax(0, 1fr); gap: 10px; padding: 10px; min-height: 0; }
/* 多卡组存档槽条（2026-09-30）：横跨编辑器顶部，点槽位切换卡组，✎ 改名 */
.deck-slots { grid-column: 1 / -1; display: flex; gap: 8px; flex-wrap: wrap; align-items: stretch; }
.deck-slot {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  background: #1a2432; border: 1px solid #2f3d51; border-radius: 7px; padding: 6px 10px;
  cursor: pointer; font-family: inherit; color: #b8c4d4; font-size: 12px; min-width: 92px;
  position: relative; transition: .12s;
}
.deck-slot:hover { border-color: var(--gold); background: #22303f; }
.deck-slot.active { border-color: var(--gold); background: #3a2f10; color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }
.deck-slot.empty { opacity: .62; }
/* 第 5 槽 = 势力出战卡组（AI 分身） */
.deck-slot.fac-slot { border-style: dashed; }
.deck-slot .ds-tag { font-style: normal; font-size: 10px; color: #d9b45b; font-weight: 500; }
.deck-slot.active .ds-tag { color: #f2c14e; }
.deck-slot .ds-name { font-weight: 600; font-size: 13px; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deck-slot.active .ds-name { color: var(--gold); }
.deck-slot .ds-cnt { color: #8fa0b6; font-size: 11px; }
.deck-slot.active .ds-cnt { color: #d8c489; }
.deck-slot .deck-rename {
  position: absolute; top: 4px; right: 6px; color: #8fa0b6; font-size: 13px; line-height: 1;
  padding: 2px 4px; border-radius: 4px;
}
.deck-slot .deck-rename:hover { color: var(--gold); background: rgba(212,175,55,.15); }
.deck-slot.active .deck-rename { color: #d8c489; }
.ed-left { display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.ed-tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ed-tools input {
  background: #0f1620; border: 1px solid #35455c; color: var(--ink);
  padding: 7px 10px; border-radius: 6px; width: 220px; font-family: inherit; font-size: 13px;
}
/* 卡库页筛选 / 排序工具栏（2026-10-13 玩家要求）—— 控件与文案口径对齐卡组编辑器 */
.vault-tools { gap: 8px; }
.vault-tools input {
  background: #0f1620; border: 1px solid #35455c; color: var(--ink);
  padding: 7px 10px; border-radius: 6px; width: 180px; font-family: inherit; font-size: 13px;
}
.vault-tools select.chip { border-radius: 6px; padding: 7px 10px; }
.chips { display: flex; gap: 5px; }
.chip {
  background: #1e2937; border: 1px solid #3b4d64; color: #b8c4d4; padding: 5px 12px;
  border-radius: 20px; cursor: pointer; font-size: 12px; font-family: inherit;
}
.chip.active { background: #3a2f10; border-color: var(--gold); color: var(--gold); }
.ed-pool { flex: 1; overflow: auto; display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 6px; align-content: start; padding-right: 4px; }
.pool-item {
  background: #1a2432; border: 1px solid #2f3d51; border-radius: 6px; padding: 6px 8px;
  cursor: pointer; font-size: 12px; transition: .12s; border-left: 3px solid var(--mon);
}
.pool-item:hover { border-color: var(--gold); background: #22303f; }
.pool-item.Spell { border-left-color: var(--spell); }
.pool-item.Trap { border-left-color: var(--trap); }
.pool-item small { display: block; color: #8fa0b6; font-size: 10.5px; margin-top: 2px; }
/* 卡库没余量 / 已到张数上限：变灰表示暂时放不进卡组 */
.pool-item.empty { opacity: .5; cursor: not-allowed; }
.pool-item.empty:hover { border-color: #2f3d51; background: #1a2432; }
/* 数值行在 DOM 里存两份（见 app.js renderEditor）：桌面多列方格用完整版 .s-wide，
   手机版「一行一条」用紧凑版 .s-narrow。两份都写出来、由 ui-mobile 决定显示哪份，
   是为了点 📱 切界面模式时不用重建 DOM 也能立刻换成对的文案。 */
.pool-item small.s-narrow { display: none; }
.ed-tip { grid-column: 1 / -1; margin: 0 0 4px; line-height: 1.7; }
.ed-right { display: flex; flex-direction: column; gap: 8px; min-height: 0; background: #131b26; border: 1px solid #2b3a4d; border-radius: 8px; padding: 10px; }
.ed-right h3 { font-size: 14px; color: var(--gold); }
.ed-right small { color: #8fa0b6; font-size: 11px; }
.ed-deck { flex: 1; overflow: auto; display: flex; flex-direction: column; gap: 3px; }
/* 🔴 [2026-10-16 玩家要求] 「卡组编辑器 / 测试决斗 / 卡片图鉴，这个位置应该变得长一点」——
 *   右栏这两个 .ed-deck 以前**都是** flex:1，等于把整栏高度**平分**给「主卡组」和「额外卡组」。
 *   可额外卡组常年是空的（或只有 1~2 张，上限也就 6 张），却恒定吃掉一半：
 *   实测手机版主卡组只剩 112px（约 2.9 条，玩家截图里就只看得见两条），电脑版 285px。
 *   现在：主卡组列表（#edDeck / #tmDeck）吃掉**全部**剩余高度；额外卡组列表按**内容**占高
 *   （上限 34%，放满 6 张也不会把主卡组挤没），空着就是 0 高 —— 高度全让给主卡组。
 *   ⚠ 只能按 id 特指：#edExtra / #tmExtra 是**第二个** .ed-deck，
 *     写 `.ed-deck:last-child` / `.ed-deck + .ed-deck` 这类结构选择器一旦有人动 HTML 就会静默失效。 */
#edExtra, #tmExtra { flex: 0 1 auto; max-height: 34%; min-height: 0; }
.ed-item {
  display: flex; justify-content: space-between; align-items: center; gap: 6px;
  background: #1a2432; border: 1px solid #2f3d51; border-radius: 5px; padding: 4px 8px;
  font-size: 12px; cursor: pointer; border-left: 3px solid var(--mon);
}
.ed-item:hover { border-color: var(--red); background: #2a1f24; }
.ed-item.Spell { border-left-color: var(--spell); }
.ed-item.Trap { border-left-color: var(--trap); }
.ed-item small { color: #8fa0b6; }

/* ===== 卡牌悬浮预览：鼠标移到卡组编辑器/菜单里的某张卡上时跟随光标的迷你卡牌窗 =====
   必须挂在 body 层（放 .screen 里会被 display:none 连带隐藏）；
   pointer-events:none 保证它绝不拦截点击 —— 「点一下加入 / 踢出卡组」的原有逻辑不受影响。 */
.card-pop {
  position: fixed; left: 0; top: 0; z-index: 70; display: none;
  width: 234px; padding: 8px;
  background: rgba(12,17,24,.97); border: 1px solid var(--gold-dim); border-radius: 10px;
  box-shadow: 0 12px 34px rgba(0,0,0,.68);
  pointer-events: none; font-size: 11.5px; color: #cfd9e6;
}
.card-pop.show { display: block; }
.card-pop .cp-art { text-align: center; margin-bottom: 6px; }
.card-pop .cp-art img {
  width: 100%; max-width: 210px; display: block; margin: 0 auto;
  border-radius: 6px; border: 1px solid #4a5e78; box-shadow: 0 3px 10px rgba(0,0,0,.5);
}
.card-pop .cp-name { font-size: 13.5px; font-weight: 700; color: var(--gold); margin-bottom: 3px; }
.card-pop .cp-line { color: #b6c3d3; line-height: 1.55; }
.card-pop .cp-line b { color: #fff; }
.card-pop .cp-desc {
  margin-top: 5px; padding: 5px 6px; background: #131b26; border-left: 3px solid var(--gold-dim);
  border-radius: 3px; line-height: 1.5; font-size: 11px; max-height: 104px; overflow: auto;
}
.card-pop .cp-no { color: #d08a6a; font-size: 10.5px; margin-top: 3px; }
.card-pop .cp-stock {
  margin-top: 5px; padding-top: 4px; border-top: 1px solid #2b3a4d;
  color: #8fa0b6; font-size: 10.5px; line-height: 1.5;
}
.card-pop .cp-stock b { color: var(--gold); }

/* 详情弹窗（UI.showBig）复用同一批 .cp-* 片段 —— 但 .card-pop 那套是按 234px 小窗调的，
   弹窗里要放大、加宽、放宽滚动上限。手机版看卡片详情主要靠这个弹窗。 */
.modal-box .cp-line { color: #b6c3d3; line-height: 1.7; font-size: 13px; }
.modal-box .cp-line b { color: #fff; }
.modal-box .cp-desc {
  margin-top: 8px; padding: 8px 10px; background: #131b26;
  border-left: 3px solid var(--gold-dim); border-radius: 4px;
  color: #cfd9e6; line-height: 1.65; font-size: 12.5px;
  max-height: 42vh; overflow: auto; white-space: normal; text-align: left;
}
.modal-box .cp-no { color: #d08a6a; font-size: 11.5px; margin-top: 5px; text-align: left; }
.modal-box .cp-stock {
  margin-top: 8px; padding-top: 6px; border-top: 1px solid #2b3a4d;
  color: #8fa0b6; font-size: 11.5px; line-height: 1.6; text-align: left;
}
.modal-box .cp-stock b { color: var(--gold); }
/* 详情弹窗的大卡图：居中、自适应宽度 */
.modal-box .cv-art.big-art img { max-width: 100%; max-height: min(300px, 42vh); }

/* 滚动条 */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: #0e141c; }
::-webkit-scrollbar-thumb { background: #35455c; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #4a5f7c; }

@media (max-width: 1100px) {
  .panel { width: 220px; }
  .slot { width: 64px; height: 88px; }
  .row { min-height: 86px; }
}

/* ==================== 窄屏兜底（电脑版模式也不能崩） ==================== */
/* 手机上「第一次打开」现在会自动落到手机版（见 index.html 首帧脚本），
   但玩家仍可以手动切回电脑版 —— 那时如果不兜底，同样会变成：
   field 被压到 146px、侧栏 122px、手牌排到 x=362~498（视口只有 390），
   后半截手牌在屏幕外、点了没反应，日志区也被挤出视野。
   所以这里给「电脑版 + 窄屏」一条保命规则：**决斗场纵向堆叠 + 手牌自己横向滑动**，
   保证每一张手牌都在视口内、且堆（卡组/墓地/额外）不会被挤走。 */
@media (max-width: 720px) {
  html:not(.ui-mobile) .duel-wrap { flex-direction: column; height: auto; flex: 1 1 auto; min-height: 0; padding: 4px; gap: 0; }
  html:not(.ui-mobile) .field { padding: 5px 6px; border-radius: 8px; overflow-y: auto; min-height: 0; }
  /* 棋盘内容压紧凑：窄屏宽度放不下大尺寸，4 排区 + 手牌天然 600px+ 会跟侧栏抢高度 */
  html:not(.ui-mobile) .side { gap: 2px; }
  html:not(.ui-mobile) .side.me { justify-content: flex-end; }
  html:not(.ui-mobile) .row { min-height: 0; gap: 3px; }
  html:not(.ui-mobile) .slot { width: 52px; height: 70px; }
  html:not(.ui-mobile) .midline { height: 16px; margin: 2px 0; }
  html:not(.ui-mobile) .hand { min-height: 0; gap: 3px; }
  html:not(.ui-mobile) .hand .card { width: 48px; height: 66px; flex: 0 0 auto; }
  html:not(.ui-mobile) .opp-hand .card { width: 34px; height: 48px; }
  html:not(.ui-mobile) .opp-hand { flex-wrap: nowrap; overflow: hidden; }
  html:not(.ui-mobile) .stack { width: 44px; }
  html:not(.ui-mobile) .pile { width: 44px; height: 58px; }
  html:not(.ui-mobile) .row.bottom { justify-content: flex-start; gap: 4px; flex-wrap: nowrap; }
  html:not(.ui-mobile) .row.bottom > .stack { flex: 0 0 auto; }
  /* 手牌永远是「可伸缩的那一格」，自己横向滚动，绝不让纹这组 >.stack 被挤出屏 */
  html:not(.ui-mobile) .row.bottom > .hand.me-hand {
    flex: 1 1 auto; min-width: 0; justify-content: flex-start;
    overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  }
  html:not(.ui-mobile) .row.bottom > .hand.me-hand::-webkit-scrollbar { display: none; }
  /* 侧栏：窄屏下只占一小块，绝不吞噬棋盘高度（「手牌被日志盖住」的根因修复点） */
  html:not(.ui-mobile) .panel {
    width: auto; flex: 0 0 auto; max-height: 34vh; min-height: 0;
    border-radius: 8px; gap: 6px;
  }
  html:not(.ui-mobile) .panel .turnbox { flex: 0 0 auto; }
  /* 卡牌详情压一压，否则 190px 最小值会顶高侧栏 */
  html:not(.ui-mobile) .panel .cardview { min-height: 0; max-height: 15vh; overflow: auto; flex: 0 1 auto; }
  html:not(.ui-mobile) .panel .logbox { flex: 1 1 auto; min-height: 56px; }
  /* ⚠ 守契约：电脑版在窄屏下**仍是纯电脑版**（侧栏 static 常驻、不显示手机版专用
     📜/抽屉把手）—— 这条由 test-mobile-ui.js 守住。上面的 column + 手牌横向滚动 +
     「侧栏高度封顶 + 棋盘内容压紧凑」三者一起，才让窄屏电脑版可用（手牌整排在棋盘可视框内、
     日志面板在棋盘下方互不遮挡），不必把侧栏变成底部抽屉（那会让电脑版漏出手机版样式，被 test-mobile-ui 判红）。
     想要手机版体验的玩家，窄屏上点一下 📱 即可。 */
}

/* ==================== 阶段按钮 ==================== */
.phase-btns { display: flex; gap: 8px; align-items: center; }
.phase-btns .btn { min-width: 128px; text-align: center; }
.hidden { display: none !important; }

/* ==================== 童实野镇 ==================== */
.wallet {
  display: flex; gap: 14px; align-items: center;
  font-size: 13px; color: #c8d6e8; margin-right: 8px;
}
.wallet b { color: #f0c419; font-size: 15px; }
.w-rate { color: #8fa0b6; font-size: 12px; }

.town-wrap { display: flex; gap: 16px; padding: 14px 20px; height: calc(100vh - 62px); }
.town-body {
  flex: 1; overflow-y: auto; background: #131b26;
  border: 1px solid #26344a; border-radius: 8px; padding: 16px;
}
.town-side {
  width: 260px; flex-shrink: 0; overflow-y: auto;
  background: #131b26; border: 1px solid #26344a; border-radius: 8px; padding: 12px;
}
.town-side h4 { margin: 10px 0 6px; font-size: 13px; color: #f0c419; }
.town-side #townFlow li, .town-side #townMsg li {
  list-style: none; font-size: 12px; color: #9fb0c6;
  padding: 4px 0; border-bottom: 1px dashed #26344a;
}

/* 卡包 */
.packs { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px; }
.pack {
  background: linear-gradient(160deg, #1e2b3c, #16202c);
  border: 1px solid #2f4059; border-radius: 8px; padding: 12px;
  /* 🔴 2026-10-13 玩家报「按钮没对齐」：整列 flex —— 配合下方 .btn 的 margin-top:auto，
     让同一行每个卡包的「购买」按钮都贴在卡片底部、横向对齐（不随描述/价格行数漂移）。 */
  display: flex; flex-direction: column;
}
.pack.locked { opacity: .55; }
.pack h4 { margin: 0 0 6px; font-size: 14px; color: #e8eef7; }
.pdesc { font-size: 12px; color: #93a4ba; min-height: 30px; margin: 0 0 8px; }
.pcost { font-size: 16px; color: #f0c419; font-weight: 700; margin: 0 0 10px; }
.pcost .old { color: #8fa0b6; font-weight: 400; text-decoration: line-through; margin-right: 4px; font-size: 13px; }
.pcost .disc-tag { display: inline-block; background: #e0533d; color: #fff; font-size: 11px; font-weight: 700; padding: 1px 6px; border-radius: 8px; margin-left: 4px; vertical-align: middle; }
.pack .btn { width: 100%; margin-top: auto; }
/* 卡包封面图（2026-10-12 玩家供图）：原图 300px 宽 webp（tools/gen-pack-art.py 产物）；
   2026-10-13 玩家嫌大 → 显示尺寸封顶 140px 宽居中。
   🔴 2026-10-13 玩家报「图片/按钮没对齐」：7 张原图宽高比并不一致（0.671~0.771），
   只封宽度会让封面高矮不齐 → 统一 **300:432 画框 + object-fit:contain**（等比缩放留边，
   不裁切卡面），封面等高后按钮自然对齐。 */
.pack .pimg { display: block; width: 100%; max-width: 140px; aspect-ratio: 300 / 432; object-fit: contain; margin: 0 auto 8px; border-radius: 6px; background: #0d141d; }

/* 卡店 → 「固定卡牌售卖」单独页的入口条（点进去才渲染货架） */
.shop-entry {
  display: flex; align-items: center; gap: 12px; margin-top: 18px; cursor: pointer;
  background: linear-gradient(120deg, #23304a, #182233);
  border: 1px solid var(--gold-dim); border-radius: 10px; padding: 12px 14px;
}
.shop-entry:hover { border-color: var(--gold); background: linear-gradient(120deg, #2a3a57, #1b2637); }
.shop-entry:active { transform: translateY(1px); }
.shop-entry .se-ic { flex: 0 0 auto; font-size: 26px; line-height: 1; }
.shop-entry .se-tx { flex: 1 1 auto; min-width: 0; }
.shop-entry h4 { margin: 0 0 3px; font-size: 14px; color: #f0c419; }
.shop-entry p { margin: 0; font-size: 12px; color: #93a4ba; }
.shop-entry .se-go { flex: 0 0 auto; font-size: 13px; font-weight: 700; color: var(--gold); white-space: nowrap; }

/* 爷爷的卡店 · 固定卡牌售卖货架（单独一页，明码标价） */
.fixed-shop { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.fs-card {
  display: flex; flex-direction: column; cursor: pointer;
  background: linear-gradient(160deg, #1e2b3c, #16202c);
  border: 1px solid #2f4059; border-radius: 8px; padding: 10px 12px;
}
.fs-card.locked { opacity: .55; }
.fs-card h4 { margin: 0 0 6px; font-size: 13px; color: #e8eef7; }
.fs-stat { font-size: 12px; color: #93a4ba; margin: 0 0 8px; flex: 1 1 auto; }
.fs-foot { display: flex; align-items: center; gap: 8px; }
.fs-cost { font-size: 15px; color: #f0c419; font-weight: 700; }
.fs-own { font-size: 11px; color: #8fa0b6; margin-right: auto; }
/* 「ⓘ」与限制卡表的「!」同款圆钮：点了弹卡图 + 详情（UI.showBig），走 townBody 事件委托 */
.fs-info {
  flex: 0 0 auto; width: 22px; height: 22px; line-height: 20px; text-align: center;
  border-radius: 50%; font-size: 13px; font-weight: 700; font-style: normal;
  background: rgba(9,14,20,.92); border: 1px solid var(--gold-dim); color: var(--gold);
  cursor: pointer; user-select: none; -webkit-user-select: none;
}
.fs-info:hover { background: #2c3b4e; border-color: var(--gold); }
.fs-info:active { background: var(--gold-dim); color: #10160f; }

.pull { margin-top: 14px; background: #16202c; border: 1px solid #2f4059; border-radius: 8px; padding: 12px; }
.pull h4 { margin: 0 0 8px; font-size: 14px; color: #f0c419; }
.pull ol { margin: 0; padding-left: 20px; }
.pull li { font-size: 13px; color: #d7e2f0; padding: 3px 0; }
.pull li.rare { color: #ffd75e; font-weight: 700; }
.pull small { display: block; color: #8fa0b6; font-size: 11px; }

.panels { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
.panels .panel-card h4 { margin: 0 0 8px; font-size: 14px; color: #e8eef7; }
.panels input {
  width: 100%; box-sizing: border-box; margin: 8px 0; padding: 8px 10px;
  background: #0f1620; border: 1px solid #2f4059; border-radius: 5px; color: #e8eef7;
}
.panels .btn { width: 100%; }

/* 表格 */
.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th, .tbl td { padding: 7px 9px; border-bottom: 1px solid #24303f; text-align: left; }
.tbl th { color: #93a4ba; font-weight: 600; background: #16202c; }
.tbl td small { display: block; color: #7d8ea6; font-size: 11px; }
/* 排行榜「决斗者」列：头像 + 名字（自己那行是预设头像，其余是决斗者立绘） */
.rk-who { white-space: nowrap; }
.rk-av {
  width: 24px; height: 24px; border-radius: 50%; object-fit: cover;
  vertical-align: -7px; margin-right: 6px; border: 1px solid #3d4d63; background: #131b26;
}
.sname { color: #e8eef7; }
.up { color: #ff5a5a; }
.down { color: #4caf7d; }
.acts { white-space: nowrap; }
.qty { width: 56px; padding: 4px 6px; background: #0f1620; border: 1px solid #2f4059; border-radius: 4px; color: #e8eef7; }
.btn.tiny { padding: 3px 8px; font-size: 12px; }
/* 一键重置筛选（2026-10-22）：和 town 里那三处「重置筛选」同一套外观。
   没有筛选条件时只置灰（仍可点，点了也没事）—— 用 .is-off 而不是 :disabled，
   否则 pointer 事件会被浏览器掐掉，玩家点一下没反应反而像坏了。 */
.btn.filter-reset { flex: 0 0 auto; white-space: nowrap; color: #9fb4d0; border-color: #2f4059; }
.btn.filter-reset:hover { color: #e8eef7; border-color: #4a648a; }
.btn.filter-reset.is-off { opacity: .45; color: #6c7e94; }
/* 预组卡组（2026-11-09 玩家要求）：方向下拉 + 生成按钮，紧跟在「重置筛选」后面 */
.preset-row { display: flex; gap: 6px; align-items: center; flex: 0 0 auto; }
.preset-row select.chip { max-width: 232px; border-radius: 6px; padding: 7px 10px; }
.preset-row .btn { white-space: nowrap; }
html.ui-mobile .preset-row { flex: 1 1 100%; }
html.ui-mobile .preset-row select.chip { flex: 1 1 auto; max-width: none; }
.sec { margin: 16px 0 8px; font-size: 14px; color: #f0c419; }
.muted { color: #8fa0b6; font-size: 13px; }

/* 提示条 */
.town-toast {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 26px;
  z-index: 60; pointer-events: none;
}
.tmsg {
  background: #16283a; border: 1px solid #2f6d8f; color: #d7e9f7;
  padding: 10px 18px; border-radius: 6px; font-size: 13px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .45);
}
.tmsg.bad { background: #3a1a1e; border-color: #8f3a3a; color: #f7d7d7; }

/* ==================== 童实野镇 ==================== */
.town-topbar { display: flex; align-items: center; gap: 18px; margin-bottom: 12px;
  padding: 12px 16px; background: #1e2b3c; border: 1px solid #2f4059; border-radius: 8px; }
.town-topbar h2 { font-size: 20px; color: var(--gold); margin: 0; }
.town-money { margin-left: auto; display: flex; gap: 20px; font-size: 14px; }
.town-money .up { color: var(--gold); font-weight: 700; font-size: 16px; }
.town-money .rate { color: var(--blue); font-size: 13px; align-self: center; }

.town-tabs { display: flex; gap: 10px; margin-bottom: 14px; }
.town-body { flex: 1; overflow-y: auto; padding: 4px 2px 20px; }

/* 卡包 */
.pack-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.pack-card { background: linear-gradient(160deg, #1e2b3c, #16202c); border: 1px solid #2f4059;
  border-radius: 10px; padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.pack-card h4 { color: var(--gold); font-size: 16px; }
.pack-card .pd { color: #93a4ba; font-size: 12px; min-height: 32px; }
.pack-card .pc { font-size: 18px; color: var(--gold); font-weight: 700; }
.pack-card.locked { opacity: .55; }

/* 开包结果 */
.pull-result { margin-top: 16px; background: #16202c; border: 1px solid var(--gold-dim);
  border-radius: 8px; padding: 14px; }
.pull-result h4 { color: var(--gold); margin-bottom: 8px; }
.pull-result li { display: flex; align-items: baseline; gap: 8px; padding: 5px 0;
  border-bottom: 1px dashed #26344a; font-size: 14px; }
.pull-result li small { color: #7d8ea6; font-size: 11px; }
.pull-result .cost { margin-top: 8px; color: #93a4ba; font-size: 13px; }

/* 交易所 */
.exchange-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
.ex-cards { background: #16202c; border: 1px solid #2f4059; border-radius: 8px; padding: 16px; }
.ex-cards h4 { margin-bottom: 6px; color: var(--gold); }
.ex-cards .hint { color: #93a4ba; font-size: 12px; margin-bottom: 10px; }
.ex-cards input { width: 100%; padding: 8px; background: #0f1620; border: 1px solid #2f4059;
  border-radius: 5px; color: var(--ink); margin-bottom: 10px; }
.ex-cards .btn { width: 100%; }
.ex-note { margin-top: 14px; color: #93a4ba; font-size: 13px; text-align: center; }

/* 股票表 */
.stock-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.stock-table th, .stock-table td { padding: 8px; text-align: left; border-bottom: 1px solid #24303f; }
.stock-table th { color: #93a4ba; font-weight: 600; background: #16202c; }
.stock-table .up { color: #ff6b6b; }
.stock-table .down { color: #4caf7d; }
.stock-table .flat { color: #93a4ba; }
.stock-table .sname { font-weight: 600; }
.stock-table .sname small { display: block; color: #7d8ea6; font-weight: 400; font-size: 11px; }
.stock-table input { width: 60px; padding: 4px; background: #0f1620; border: 1px solid #2f4059;
  border-radius: 4px; color: var(--ink); }
.stock-table .btn.small { padding: 4px 10px; font-size: 12px; }

.holdings { margin-top: 16px; background: #16202c; border: 1px solid #2f4059; border-radius: 8px; padding: 14px; }
.holdings h4 { color: var(--gold); margin-bottom: 8px; }
.holdings li { padding: 5px 0; font-size: 13px; border-bottom: 1px dashed #26344a; }
.holdings .up { color: #ff6b6b; }
.holdings .down { color: #4caf7d; }
.muted { color: #8fa0b6; }

/* ==================== 稀有度标签 ==================== */
.rar {
  display: inline-block; padding: 1px 6px; border-radius: 3px;
  font-size: 11px; margin-left: 6px; vertical-align: middle;
}
.rar-van  { background: #4a5260; color: #dfe6f0; }
.rar-nor  { background: #2b6cb0; color: #e6f0ff; }
.rar-hor  { background: #6b2fb0; color: #f0e6ff; }
.rar-yug  { background: #b07d2b; color: #fff3e0; }
.rar-syn  { background: #1f8a70; color: #e6fff5; }
.rar-leg  { background: #b8860b; color: #fffbe6; font-weight: 700; }
.rar-hametsu { background: #8a2f2f; color: #ffe3e3; }
.rar-unknown { background: #555; color: #eee; }

/* 稀有度/卡池标签的独立行（详情弹窗 / 卡店货架）——
   2026-10-07 起标签不再塞进 <h4>，因为「弹窗标题就是卡名」是既有界面契约。 */
.cv-pools { margin: 2px 0 8px; display: flex; flex-wrap: wrap; gap: 4px; }
.cv-pools .rar { margin-left: 0; }

/* 限制卡标记 */
.lim { background: #b02a2a; color: #fff; padding: 0 4px; border-radius: 3px;
       font-size: 10px; margin-left: 5px; }

/* ==================== 童实野镇 ==================== */
.wallet { display: flex; gap: 14px; font-size: 13px; color: #c8d6e8; }
.wallet b { color: var(--gold); }
.wallet .rate { color: #8fa0b6; }

.town-tabs { display: flex; gap: 8px; padding: 10px 16px; border-bottom: 1px solid #2b3a4d; }
.town-body { flex: 1; overflow-y: auto; padding: 16px; }

.town-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.town-card {
  background: #16202c; border: 1px solid #2b3a4d; border-radius: 8px;
  padding: 12px; display: flex; flex-direction: column; gap: 8px;
}
.town-card h4 { color: var(--gold); font-size: 15px; }
.tc-desc { color: #8fa0b6; font-size: 12px; }
.tc-price { color: var(--gold); font-weight: 700; }
.town-result { margin-top: 16px; color: #c8d6e8; font-size: 13px; }
.tc-cost { color: #8fa0b6; margin-top: 8px; }

.pack-list { list-style: none; margin: 8px 0; }
.pack-list li { padding: 5px 0; border-bottom: 1px dashed #2b3a4d; }
.pack-list em { color: var(--gold); font-style: normal; margin-left: 6px; font-size: 11px; }
.pack-list small { color: #8fa0b6; margin-left: 8px; font-size: 11px; }

.stock-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.stock-table th, .stock-table td {
  padding: 7px 8px; text-align: left; border-bottom: 1px solid #2b3a4d;
}
.stock-table th { color: #8fa0b6; font-weight: normal; }
.sname { color: #e8eef7; }
.sname small { display: block; color: #7d8ea6; font-size: 11px; }
.up { color: #ff6b6b; }
.down { color: #4caf7d; }
.flat { color: #8fa0b6; }

.holdings { margin-top: 16px; background: #16202c; border: 1px solid #2b3a4d;
            border-radius: 8px; padding: 12px; }
.holdings ul { list-style: none; margin: 6px 0; }
.holdings li { padding: 4px 0; font-size: 13px; color: #c8d6e8; }

.town-toast {
  position: fixed; left: 50%; bottom: 40px; transform: translateX(-50%) translateY(12px);
  background: #16202c; border: 1px solid var(--gold); color: #e8eef7;
  padding: 10px 18px; border-radius: 6px; font-size: 13px; opacity: 0;
  pointer-events: none; transition: opacity .2s, transform .2s;
}
.town-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ==================== 稀有度标签 ==================== */
.rar { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 11px; margin-left: 6px; }
.rar-leg { background: #8a6d1f; color: #ffe9a8; }
.rar-syn { background: #1f6b5c; color: #c8f5e9; }
.rar-yugo { background: #7a4b1f; color: #ffe0c2; }
.rar-horror { background: #6b2f5f; color: #f3d0ea; }
.rar-normal { background: #26567e; color: #c9e6ff; }
.rar-vanilla { background: #4a4a4a; color: #e0e0e0; }
/* 神之卡（三幻神）：金色渐变，比蕾雅卡更亮，一眼区分 —— 2026-10-08 玩家要求 */
.rar-god { background: linear-gradient(135deg, #f2cb3c, #b8860b); color: #2a1c00;
           font-weight: 700; text-shadow: 0 1px 0 rgba(255,255,255,.35); }

/* ==================== 限制卡标记 ==================== */
.lim { display: inline-block; background: #a03030; color: #ffd9d9; font-size: 10px;
  padding: 0 4px; border-radius: 3px; margin-left: 5px; vertical-align: middle; }

/* ==================== 禁止卡标记（2026-10-10 玩家规则） ====================
   大天使 杰拉特 / 大魔王 魔杰拉 = 禁止卡：拿不到 + 不能编入。
   卡组编辑器条目上挂 .ban，图鉴 / 卡片详情里由 UI.rarityTag 挂 .rar-ban。 */
.ban, .rar-ban { display: inline-block; background: #1b1b1b; border: 1px solid #c0392b;
  color: #ff8a8a; font-size: 10px; font-weight: 700; padding: 0 4px; border-radius: 3px;
  margin-left: 5px; vertical-align: middle; }

/* ==================== 童实野镇 ==================== */
.wallet { display: flex; gap: 16px; align-items: center; font-size: 13px; color: #c8d6e8; }
.wallet b { color: #f0c419; font-size: 15px; }
.wallet .rate { color: #8fa0b6; font-size: 12px; }

.town-tabs { display: flex; gap: 8px; padding: 10px 16px; border-bottom: 1px solid #26344a; }
.town-body { flex: 1; overflow-y: auto; padding: 16px; }

.town-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.town-card {
  background: linear-gradient(160deg, #1e2b3c, #16202c);
  border: 1px solid #2f4059; border-radius: 8px; padding: 14px;
  display: flex; flex-direction: column; gap: 8px;
}
.town-card h4 { margin: 0; font-size: 14px; color: #e8eef7; }
.town-card .tc-desc { font-size: 12px; color: #93a4ba; min-height: 32px; }
.town-card .tc-price { font-size: 17px; color: #f0c419; font-weight: 700; }
.town-card input { padding: 7px 9px; background: #0f1620; border: 1px solid #2f4059;
  border-radius: 5px; color: #e8eef7; }

.town-result { margin-top: 16px; padding: 14px; background: #16202c;
  border: 1px solid #2f4059; border-radius: 8px; }
.town-result h4 { margin: 0 0 8px; font-size: 14px; color: #f0c419; }
.town-result .pack-list { list-style: none; padding: 0; margin: 0; }
.town-result .pack-list li { padding: 5px 0; font-size: 13px; color: #d7e2f0;
  border-bottom: 1px dashed #26344a; }
.town-result .pack-list em { font-style: normal; color: #f0c419; font-size: 11px; margin-left: 6px; }
.town-result .pack-list small { color: #8fa0b6; font-size: 11px; margin-left: 8px; }
.town-result .tc-cost { margin-top: 8px; font-size: 12px; color: #93a4ba; }

.stock-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.stock-table th, .stock-table td { padding: 8px 9px; text-align: left; border-bottom: 1px solid #24303f; }
.stock-table th { color: #93a4ba; font-weight: 600; background: #16202c; }
.stock-table td.sname { color: #e8eef7; font-weight: 600; }
.stock-table .up { color: #ff5a5a; }
.stock-table .down { color: #4caf7d; }
.stock-table .flat { color: #93a4ba; }
.stock-table input { width: 64px; padding: 4px 6px; background: #0f1620;
  border: 1px solid #2f4059; border-radius: 4px; color: #e8eef7; }

.holdings { margin-top: 16px; padding: 14px; background: #16202c;
  border: 1px solid #2f4059; border-radius: 8px; }
.holdings h4 { margin: 0 0 8px; font-size: 14px; color: #f0c419; }
.holdings ul { list-style: none; padding: 0; margin: 0; }
.holdings li { padding: 4px 0; font-size: 13px; color: #c8d6e8; }

.town-toast {
  position: fixed; left: 50%; bottom: 40px; transform: translateX(-50%) translateY(20px);
  background: #16283a; border: 1px solid #2f6d8f; color: #d7e9f7;
  padding: 10px 20px; border-radius: 6px; font-size: 13px;
  opacity: 0; pointer-events: none; transition: opacity .25s, transform .25s;
  box-shadow: 0 6px 20px rgba(0,0,0,.45);
}
.town-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ==================================================================
   以下为最终生效样式（覆盖前面重复定义）
   ================================================================== */

/* ==================== 阶段操作条 ==================== */
.phase-row {
  display: flex; flex-direction: column; gap: 4px;
  padding-top: 5px; margin-top: 2px; border-top: 1px solid rgba(217,180,91,.16);
}
.phase-btns { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.phase-btns .btn { min-width: 118px; text-align: center; flex: 0 1 auto; }
.phase-btns .btn.endturn { min-width: 170px; font-size: 15px; }
.phase-btns .btn.urgent {
  animation: urgPulse 1.4s ease-in-out infinite;
  box-shadow: 0 0 0 0 rgba(224,192,115,.7);
}
@keyframes urgPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(224,192,115,.65); }
  50%      { box-shadow: 0 0 0 7px rgba(224,192,115,0); }
}
.phase-hint { font-size: 11px; color: #93a4ba; min-height: 14px; }
.hidden { display: none !important; }

/* ==================== 童实野镇（最终） ==================== */
#town { background: #0d1117; }
#town .town-wrap { display: flex; gap: 14px; padding: 12px 16px; flex: 1; min-height: 0; }
#town .town-body {
  flex: 1; min-width: 0; overflow-y: auto; padding: 16px;
  background: #131b26; border: 1px solid #26344a; border-radius: 8px;
}
#town .town-side {
  width: 268px; flex-shrink: 0; overflow-y: auto;
  background: #131b26; border: 1px solid #26344a; border-radius: 8px; padding: 12px;
}
#town .town-side h4 { margin: 0 0 8px; font-size: 13px; color: #f0c419; }
.town-log ul { list-style: none; margin: 0; padding: 0; }
.town-log li {
  font-size: 12px; color: #9fb0c6; padding: 5px 0;
  border-bottom: 1px dashed #26344a; line-height: 1.5;
}
.town-log li b { color: #f0c419; font-weight: 400; }

.town-head { margin-bottom: 14px; }
.town-head h3 { font-size: 17px; color: #f0c419; margin: 0 0 4px; }
.town-head p { font-size: 12px; color: #8fa0b6; margin: 0; }

/* 设施地图 */
.fac-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(228px, 1fr)); gap: 12px; }
.fac {
  background: linear-gradient(160deg, #1e2b3c, #16202c);
  border: 1px solid #2f4059; border-radius: 10px; padding: 14px;
  cursor: pointer; transition: .16s; display: flex; flex-direction: column; gap: 6px;
}
.fac:hover { border-color: var(--gold); transform: translateY(-2px); }
.fac .fi { font-size: 26px; line-height: 1; }
.fac h4 { margin: 0; font-size: 15px; color: #e8eef7; }
.fac p { margin: 0; font-size: 12px; color: #93a4ba; line-height: 1.55; min-height: 34px; }
.fac .fnote { font-size: 11px; color: #6f8299; }
.fac.warn { border-color: #8f6a2f; }
.fac.lock { opacity: .6; }

/* 通用小工具条 */
.tool-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0; }
.inp {
  padding: 7px 9px; background: #0f1620; border: 1px solid #2f4059;
  border-radius: 5px; color: #e8eef7; font-family: inherit; font-size: 13px;
}
.inp.w70 { width: 72px; }
.badge { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 11px; }
.badge.ok { background: #1f5c3a; color: #c8f0d9; }
.badge.no { background: #5c3a1f; color: #f0dcc8; }
.badge.done { background: #2b6cb0; color: #dceaf7; }

/* 线上卖家交易所（童实野镇 · 挂单市场，2026-10-07） */
.mk-h { margin: 16px 0 6px; font-size: 14px; color: #f0c419; }
.mk-h small { font-weight: 400; }
#townBody select,
#townBody input[type="number"] {
  padding: 6px 8px; background: #0f1620; border: 1px solid #2f4059;
  border-radius: 5px; color: #e8eef7; font-family: inherit; font-size: 13px;
}
#townBody input[type="number"] { width: 96px; }
#townBody select:disabled,
#townBody input[type="number"]:disabled { opacity: .5; }

/* 熔炼炉 */
.forge-panel {
  background: linear-gradient(160deg, #2a1e2e, #1a1420);
  border: 1px solid #6b3f6b; border-radius: 10px; padding: 16px; margin-bottom: 14px;
}
.forge-panel h4 { margin: 0 0 6px; color: #e08ad0; font-size: 16px; }
.forge-panel .fb { font-size: 22px; color: #f0c419; font-weight: 700; }
.forge-btns { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.forge-btns .btn { padding: 7px 12px; font-size: 13px; }

/* 奖励/结果面板 */
.res-box {
  margin-top: 14px; padding: 14px; border-radius: 8px;
  background: #16202c; border: 1px solid #2f4059;
}
.res-box h4 { margin: 0 0 8px; font-size: 14px; color: #f0c419; }
.res-box .lines { font-size: 13px; color: #d7e2f0; line-height: 1.9; }
.res-box .lines .good { color: #7ed6a5; }
.res-box .lines .bad { color: #ff8a8a; }
.res-box .lines .rare { color: #ffd75e; font-weight: 700; }
.res-box ul { list-style: none; margin: 6px 0 0; padding: 0; }
.res-box li { font-size: 13px; padding: 4px 0; border-bottom: 1px dashed #26344a; color: #d7e2f0; }
.res-box li small { color: #8fa0b6; font-size: 11px; margin-left: 8px; }

/* 圣杯战争 */
.cup-banner {
  background: linear-gradient(120deg, #3a2a0f, #1a1420);
  border: 1px solid #b8860b; border-radius: 10px; padding: 18px; text-align: center;
}
.cup-banner h3 { color: #ffd75e; font-size: 20px; margin: 0 0 8px; letter-spacing: 2px; }
.cup-banner p { color: #c8b48a; font-size: 13px; margin: 4px 0; }
.bracket { display: flex; gap: 14px; margin-top: 12px; }
.bracket .round { flex: 1; }
.bracket .round h5 { font-size: 12px; color: #93a4ba; margin: 0 0 6px; text-align: center; }
.bracket .pair {
  background: #16202c; border: 1px solid #2b3a4d; border-radius: 5px;
  padding: 5px 7px; margin-bottom: 5px; font-size: 12px; color: #c8d6e8;
}
.bracket .pair .me { color: #f0c419; font-weight: 700; }
.bracket .pair .win { color: #7ed6a5; }
.bracket .pair .lose { color: #6f8299; text-decoration: line-through; }

/* 排行榜 / 限制卡表 */
.rank-gold { color: #ffd75e; font-weight: 700; }
.rank-me { background: rgba(217,180,91,.12); }
.lim-card {
  position: relative; padding: 10px 46px 10px 12px;
  background: #16202c; border: 1px solid #5c2b2b; border-left: 3px solid #a03030;
  border-radius: 6px; margin-bottom: 8px;
}
.lim-card h4 { margin: 0 0 4px; font-size: 14px; color: #ffb3b3; }
.lim-card p { margin: 0; font-size: 12px; color: #c8a0a0; line-height: 1.6; }
.lim-card .st { font-size: 11px; color: #8fa0b6; }
/* 限制卡表每张卡最右边的「!」——点了弹卡图 + 详情（UI.showBig）。
   绝对定位在右侧垂直居中，所以条目再高也永远贴在「最右边」；
   独立处理点击并经 townBody 事件委托分发（data-act="card"），不影响卡片其它区域。 */
.lim-info {
  position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; line-height: 24px; text-align: center;
  border-radius: 50%; font-size: 15px; font-weight: 700; font-style: normal;
  background: rgba(9,14,20,.92); border: 1px solid var(--gold-dim); color: var(--gold);
  cursor: pointer; user-select: none; -webkit-user-select: none;
}
.lim-info:hover { background: #2c3b4e; border-color: var(--gold); }
.lim-info:active { background: var(--gold-dim); color: #10160f; }

/* 「!」详情圆钮的**行内版**（我的卡库 / 我的保险柜 / 卡片商人 用）。
   与 .lim-info 同款外观，区别是 .lim-info 绝对定位在整张卡右侧，这里要跟着文字走，
   所以是 inline-block。（固定卡货架的 .fs-info 是第三种：flex 子项。）
   三处都是 townBody / resultBox 上的事件委托，点了走 UI.showBig 看卡图与详情。 */
.info-dot {
  display: inline-block; width: 21px; height: 21px; line-height: 19px; text-align: center;
  border-radius: 50%; font-size: 12px; font-weight: 700; font-style: normal;
  vertical-align: middle; margin-right: 6px;
  background: rgba(9,14,20,.92); border: 1px solid var(--gold-dim); color: var(--gold);
  cursor: pointer; user-select: none; -webkit-user-select: none;
}
.info-dot:hover { background: #2c3b4e; border-color: var(--gold); }
.info-dot:active { background: var(--gold-dim); color: #10160f; }
/* 触屏手指更粗：手机版放大一点（与 .lim-info 的手机版口径一致） */
html.ui-mobile .info-dot { width: 26px; height: 26px; line-height: 24px; font-size: 15px; }
/* 保险柜里那枚「!」是 flex 子项，别被 .sn 的省略号挤扁 */
.safe-row .info-dot { flex: 0 0 auto; margin-right: 0; }

/* 每日任务 */
.task-item {
  display: flex; align-items: center; gap: 12px;
  background: #16202c; border: 1px solid #2b3a4d; border-radius: 8px;
  padding: 12px 14px; margin-bottom: 9px;
}
.task-item.done { border-color: #2b6cb0; background: #14212f; }
.task-item.claimable { border-color: #d9b45b; background: #211d12; box-shadow: 0 0 10px rgba(217,180,91,.35); }
.task-item .ti-main { flex: 1; min-width: 0; }
.task-item .ti-main h4 { margin: 0 0 3px; font-size: 14px; color: #e8eef7; }
.task-item .ti-main p { margin: 0; font-size: 12px; color: #93a4ba; }
/* ⚠ 奖励列绝不能 white-space:nowrap（2026-11-11 玩家报手机成就墙「超级拥挤」）：
   `.ti-main` 是 flex:1 + min-width:0，奖励串一旦 nowrap，其 min-content 宽度会把
   标题/描述挤成「一行一两个字」。改成长奖励自身换行（右对齐），标题列永远拿到 ≥54% 宽。 */
.task-item .ti-rw {
  flex: 0 0 auto; max-width: 46%; font-size: 12px; color: #f0c419;
  text-align: right; line-height: 1.55; overflow-wrap: break-word;
}
.task-item .ti-rw .rw-lb { display: block; font-size: 11px; color: #7f8fa4; }
/* ⚠ 类名必须叫 .task-bar，绝不能叫 .bar ——
   页头是 <header class="bar">，一旦这里也叫 .bar（且带 height:6px / overflow:hidden），
   #editor / #town 的整个页头会被压成 6px 裁掉：返回按钮、标题、钱包栏一起消失。
   这是本工程踩过的真实事故，改名前先确认没有别的 .bar 选择器。 */
.task-bar { height: 6px; background: #0f1620; border-radius: 3px; margin-top: 6px; overflow: hidden; }
.task-bar i { display: block; height: 100%; background: linear-gradient(90deg, #c9a24d, #e0c073); }
/* 进度条 + 进度数字同一行（2026-11-11 成就墙手机上把数字从描述尾巴挪过来，省一行） */
.ti-meter { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.ti-meter .task-bar { flex: 1 1 auto; min-width: 0; margin-top: 0; }
.ti-num { flex: 0 0 auto; font-size: 11px; color: #8fa0b6; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ti-num b { color: #f0c419; font-weight: 700; }

/* 成就墙页头：统计 + 一键领取同一行（2026-11-11 手机上原来各占一行，太占竖向空间） */
.ach-top { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 10px 0 4px; }
.ach-top p { margin: 0; }
.ach-top .btn { flex: 0 0 auto; }
.ach-shown { margin: 0 0 10px; }

/* ===== 成就墙 · 称号选择区（2026-11-11 玩家要求：面板最下面选称号，装备后名字前缀）===== */.ach-title-sec { margin-top: 20px; padding-top: 14px; border-top: 1px solid rgba(217, 180, 91, .22); }
.ach-title-sec > h4 { margin: 0 0 6px; font-size: 15px; color: var(--gold); }
.ach-title-sec > p { margin: 0 0 10px; font-size: 12px; color: #93a4bb; line-height: 1.7; }
.ach-title-grid { display: flex; flex-wrap: wrap; gap: 8px; }
/* 每个称号一张小卡：上面是【称号】，下面是来源成就 —— 装备中描金 */
.ach-title {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-width: 128px; max-width: 100%; padding: 8px 12px; cursor: pointer;
  background: #121a24; border: 1px solid #26313f; border-radius: 9px;
  color: #d8dfea; font: inherit; text-align: left; transition: border-color .15s, background .15s;
}
.ach-title:hover { border-color: #3d4a5c; background: #16202c; }
.ach-title b { font-size: 13px; font-weight: 700; color: #e9dcb4; }
.ach-title span { font-size: 11px; color: #7f8fa4; }
.ach-title.on {
  border-color: var(--gold); background: rgba(217, 180, 91, .12);
  box-shadow: 0 0 12px rgba(217, 180, 91, .28);
}
.ach-title.on b { color: var(--gold); }
.ach-title.off { justify-content: center; align-items: center; color: #93a4bb; font-size: 12px; min-width: 104px; }

/* 提示条危险态 */
.town-toast.bad { background: #3a1a1e; border-color: #8f3a3a; color: #f7d7d7; }

/* ==================================================================
   ★ 底部固定操作条（最终生效）——保证「进入战斗阶段」永远在屏幕上
   ================================================================== */
#duel { padding-bottom: 56px; }
.action-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
  display: flex; align-items: center; gap: 10px;
  padding: 7px 12px;
  background: linear-gradient(180deg, rgba(10,16,24,.90), rgba(10,16,24,.99));
  border-top: 2px solid var(--gold-dim);
  box-shadow: 0 -6px 22px rgba(0,0,0,.6);
  backdrop-filter: blur(6px);
}
.action-bar .ab-pbar { flex: 0 0 230px; width: 230px; }
.action-bar .ab-pbar .pname { min-width: 44px; }
.action-bar .ab-phase {
  flex: 0 0 auto; white-space: nowrap; font-size: 12px; letter-spacing: 2px;
  color: var(--gold); background: #16301f;
  border: 1px solid rgba(217,180,91,.35); border-radius: 12px; padding: 3px 12px;
}
.action-bar .phase-hint {
  flex: 1 1 auto; min-width: 0; font-size: 11px; color: #93a4ba;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.action-bar .phase-btns { flex: 0 0 auto; display: flex; gap: 7px; align-items: center; }
.action-bar .phase-btns .btn { min-width: 0; padding: 7px 13px; font-size: 13px; white-space: nowrap; }
.action-bar .phase-btns .btn.endturn { font-size: 14px; font-weight: 700; padding: 7px 18px; }
/* 【布局铁律】主按钮的宽度必须和阶段无关 —— 它在「结束回合」右边，
   一旦随文案变宽变窄，就会把「结束回合」顶着左右挪（玩家刚记住的位置就变了）。
   做法不是给它定死一个宽度（那会让窄窗口的操作条溢出屏幕），
   而是让三个阶段的主按钮文案一样长：「⚔ 进入战斗阶段 / ⚔ 结束战斗阶段 / ⚔ 结束当前回合」
   —— 都是「⚔ + 6 个汉字」，宽度自然一致。
   回归测试 tools/test-endturn-pos.js 会逐尺寸量矩形，谁改长了文案就红。 */

/* 矮窗口：整体压缩，保证场地也能尽量完整显示 */
@media (max-height: 760px) {
  .slot { width: 62px; height: 86px; }
  .row { min-height: 84px; }
  .hand { min-height: 78px; }
  .hand .card { width: 58px; height: 78px; }
  .opp-hand .card { width: 40px; height: 56px; }
  .pile { width: 50px; height: 68px; }
  .stack { width: 50px; }
  .midline { height: 18px; margin: 2px 0; }
  .field { padding: 6px 8px; }
}
@media (max-height: 620px) {
  .slot { width: 54px; height: 74px; }
  .row { min-height: 72px; }
  .hand .card { width: 50px; height: 68px; }
  .opp-hand .card { width: 34px; height: 48px; }
  .pile { width: 44px; height: 58px; }
  .stack { width: 44px; }
  .action-bar { padding: 5px 10px; gap: 7px; }
  .action-bar .phase-btns .btn { padding: 5px 10px; font-size: 12px; }
}
/* 场地内容仍溢出时允许滚动，绝不把内容藏到屏幕外 */
.field { overflow-y: auto; }
.field > .side { flex-shrink: 0; }

/* 卡组编辑器：实装状态提示 */
.ed-note {
  font-size: 11px; color: #8fa0b6; margin: 6px 0 4px; line-height: 1.6;
}
.ed-note b { color: #7ed6a5; }
/* 手机版专用提示（电脑版隐藏）：只在 html.ui-mobile 下显示，见文件末尾的手机版段 */
.ed-mobile-tip { display: none; }
.pool-item .impl {
  font-size: 9px; padding: 0 4px; margin-left: 4px; border-radius: 3px;
  background: #1f5c3a; color: #c8f0d9; font-weight: 400;
}
/* 卡名整体成块，放不进一行时整体换行，不在字间断开（见 app.js renderEditor） */
.pool-item .nm { display: inline-block; max-width: 100%; }

/* ===== 卡片图鉴 #codex：左＝卡池全部 880 张，右＝选中卡的详情（纯只读）===== */
/* 左列用 minmax(0,1fr) 而不是 1fr：1fr 的最小值是 min-content，筛选条一多就会把
   整列顶宽、把页面撑出横向滚动（手机版同款问题见下方 ui-mobile 注释）。 */
.codex-body { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 10px; padding: 10px; min-height: 0; }
.cx-left { display: flex; flex-direction: column; gap: 8px; min-height: 0; min-width: 0; }
.cx-tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.cx-tools input {
  background: #0f1620; border: 1px solid #35455c; color: var(--ink);
  padding: 7px 10px; border-radius: 6px; width: 220px; font-family: inherit; font-size: 13px;
}
.cx-note { font-size: 11px; color: #8fa0b6; margin: 0 0 2px; line-height: 1.6; }
.cx-note b { color: #7ed6a5; }
.cx-mobile-tip { display: none; }
.cx-pool {
  flex: 1; overflow: auto; display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 6px;
  align-content: start; padding-right: 4px;
}
.cx-item {
  background: #1a2432; border: 1px solid #2f3d51; border-radius: 6px; padding: 6px 8px;
  cursor: pointer; font-size: 12px; transition: .12s; border-left: 3px solid var(--mon);
}
.cx-item:hover { border-color: var(--gold); background: #22303f; }
.cx-item.Spell { border-left-color: var(--spell); }
.cx-item.Trap { border-left-color: var(--trap); }
/* 怪兽卡左侧色条：凡骨黄 / 融合紫 / 同调白（效果·特殊召唤沿用默认 --mon） */
.cx-item.st-vanilla, .pool-item.st-vanilla { border-left-color: #e8c14a; }
.cx-item.st-fusion,  .pool-item.st-fusion  { border-left-color: var(--purple); }
.cx-item.st-synchro, .pool-item.st-synchro { border-left-color: #e8e8e8; }
/* 当前选中（右侧正在显示的那张） */
.cx-item.sel {
  border-color: var(--gold); background: #2c2413;
  box-shadow: inset 0 0 0 1px rgba(214,171,84,.35);
}
.cx-item small { display: block; color: #8fa0b6; font-size: 10.5px; margin-top: 2px; }
.cx-item .nm { display: inline-block; max-width: 100%; }
.cx-item .own {
  font-size: 9px; padding: 0 4px; margin-left: 4px; border-radius: 3px;
  background: #2a3a52; color: #b9cbe4; font-weight: 400;
}
.cx-detail {
  background: #131b26; border: 1px solid #2b3a4d; border-radius: 8px;
  padding: 12px; overflow: auto; min-height: 0;
}
.cx-hint { color: #6d7c8f; font-size: 11.5px; line-height: 1.7; margin-top: 8px; }
/* 详情复用的 .cp-* / .cv-* 片段原本按 .card-pop 的 234px 小窗调过，
   右侧栏有 340px，要放宽字号与滚动上限 —— 与 .modal-box 的处理同理（见 MOBILE-UI.md）。 */
.cx-detail .cp-line { color: #b6c3d3; line-height: 1.7; font-size: 13px; }
.cx-detail .cp-line b { color: #fff; }
.cx-detail .cp-desc {
  margin-top: 8px; padding: 8px 10px; background: #10161f;
  border-left: 3px solid var(--gold-dim); border-radius: 4px;
  color: #cfd9e6; line-height: 1.65; font-size: 12.5px; white-space: normal;
}
.cx-detail .cp-no { color: #d08a6a; font-size: 11.5px; margin-top: 5px; }
.cx-detail .cp-stock {
  margin-top: 8px; padding-top: 6px; border-top: 1px solid #2b3a4d;
  color: #8fa0b6; font-size: 11.5px; line-height: 1.6;
}
.cx-detail .cp-stock b { color: var(--gold); }
.cx-detail .cv-name { font-size: 16px; }
.cx-detail .cv-art.big-art img { max-width: 100%; max-height: min(280px, 38vh); }

/* ⚠ 已废弃（2026-10-23 玩家要求）：主菜单的「⚠ 重置存档」按钮已撤掉，
   改为放进主菜单「规则说明」弹窗底部的「重置存档（从零开始）」。
   样式先留着（免得出问题要回滚），但页面上已经没有这个节点。 */
.warn-reset {
  /* fixed：主菜单会滚动（窗口矮时内容比屏幕高），absolute 会被一起滚走，
     玩家就再也找不到「重置存档」了。 */
  position: fixed; top: 14px; right: 16px; z-index: 30;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid #6b3a2a; background: rgba(60,26,18,.55);
  color: #ffb08a; font-size: 17px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s, transform .15s;
}
.warn-reset:hover { background: rgba(163,51,51,.75); color: #fff; transform: scale(1.08); }

/* 卡组编辑器排序下拉 */
select#edSort {
  padding: 6px 10px; border-radius: 8px; border: 1px solid #3a4a5f;
  background: #1b2735; color: #dbe7f4; font-size: 13px; cursor: pointer;
}

/* ==================================================================
   ★ 界面模式开关：开始界面右上角 📱（手机版）/ 🖥（电脑版）
   ================================================================== */
.ui-mode {
  /* 同 .warn-reset：固定在窗口右上角，菜单滚动时不会消失。
     ⚠ 2026-10-23：原来右边留给 ⚠ 复位按钮（right:58px），那个按钮撤掉后整体右移一格，
       现在是右上角最右一个（🧪 在其左侧 42px）。 */
  position: fixed; top: 14px; right: 16px; z-index: 30;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid #46586f; background: rgba(25,38,54,.72);
  color: #cfe2f5; font-size: 15px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s, transform .15s, border-color .15s, color .15s;
}
.ui-mode:hover { background: rgba(58,110,168,.85); border-color: #8fc4ea; color: #fff; transform: scale(1.08); }
/* 处在手机版时把按钮点亮，一眼看出当前是哪个模式 */
html.ui-mobile .ui-mode {
  border-color: var(--gold); background: rgba(64,50,14,.85); color: var(--gold);
}

/* ==================================================================
   ★ 测试模式入口：开始界面右上角 🧪（📱 左边一格）
   —— 站位与 .ui-mode 完全同一套路（fixed，菜单滚动时不消失），
      只是往左挪 42px，颜色走「实验室」的青绿色，和模式开关区分开。
   ================================================================== */
.test-btn {
  position: fixed; top: 14px; right: 58px; z-index: 30;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid #3d6b5f; background: rgba(19,44,39,.72);
  color: #9fe7cf; font-size: 15px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s, transform .15s, border-color .15s, color .15s;
}
.test-btn:hover {
  background: rgba(38,110,92,.9); border-color: #8fe3c8; color: #fff; transform: scale(1.08);
}

/* ---- 对局里的「测试模式专用」按钮：普通决斗一律不存在 ----
   用 body:not(.in-test) + !important 是为了盖过手机版那一堆
   `.phase-btns .btn { display: ... }` 的覆盖规则（否则手机版会漏出来）。 */
body:not(.in-test) #btnTestRestart,
body:not(.in-test) #btnTestPick { display: none !important; }

/* ---- 观战模式：棋盘只读（2026-10-14）----
   观战复用整块对局界面（renderState / playFx / 卡牌详情），唯一要拦的是
   **会改变对局的按钮** —— 主按钮、额外召唤、跳过战斗、结束回合、测试模式那两个。
   用 #actionBar.spec-on + !important 盖过手机版那一堆 `.phase-btns .btn { display }`
   覆盖规则（与上面测试模式按钮同一个坑）；点「观战」进/出各加减一次这个类，
   所以**不需要**逐个按钮恢复 display（少一条出错路径）。
   ⚠ 「📜 战况」抽屉与「📱 界面切换」刻意保留：观众最需要的就是看日志与卡牌详情。 */
#actionBar.spec-on #btnExtra,
#actionBar.spec-on #btnDirect,
#actionBar.spec-on #btnSkipBattle,
#actionBar.spec-on #btnFinishTurn,
#actionBar.spec-on #btnEndTurn,
#actionBar.spec-on #btnTestRestart,
#actionBar.spec-on #btnTestPick { display: none !important; }
/* 观战时把阶段提示换成观战说明色的低调样式（不改文案，文案由 online.js 给） */
#actionBar.spec-on .phase-hint { color: #9fb0c4; font-style: italic; }

/* 测试模式结算画面里那行「不影响存档」的说明 */
.tm-result-tip {
  margin: 6px 0 2px; font-size: 12px; color: #9fe7cf; text-align: center;
}
.tm-result-tip b { color: #7fe0c0; }

/* ---- 测试模式「不计战绩 / 无奖励」的常驻说明（两处：选卡页横幅、对局内徽标） ----
   这条规则是玩家明确要求的：必须一眼看见「这局不算数、也不发东西」。
   两处都只在测试模式出现，普通决斗与普通页面完全不受影响。 */
.tm-banner {
  margin: 8px 12px 0; padding: 9px 14px;
  border: 1px solid #3d6b5f; border-radius: 8px;
  background: rgba(19,44,39,.66);
  color: #bfe8d8; font-size: 12.5px; line-height: 1.75;
}
.tm-banner b { color: #8fe3c8; }
.tm-banner .tm-win { color: #ff8a8a; font-weight: 700; }
.tm-banner .tm-lose { color: #7fd67f; font-weight: 700; }

/* ---- AI 对打观战：选人面板（2026-10-22 玩家要求）----
   点「⚔️ AI 对打」才展开，默认收着，不占选卡页的高度。 */
.tm-aiduel {
  display: none;                       /* 默认不存在；见 APP.toggleAiDuelPanel */
  margin: 8px 12px 0; padding: 10px 14px;
  border: 1px solid #3d6b5f; border-radius: 8px;
  background: rgba(19,44,39,.72);
}
.tm-aiduel.show { display: block; }
.tm-ad-head { color: #bfe8d8; font-size: 13.5px; font-weight: 700; letter-spacing: .3px; }
.tm-ad-head small {
  display: block; margin-top: 3px;
  font-weight: 400; font-size: 11.5px; color: #8fb7a7; letter-spacing: 0;
}
.tm-ad-head small b { color: #8fe3c8; }
.tm-ad-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 9px;
}
.tm-ad-pick { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: #cfe8dd; }
.tm-ad-pick select {
  min-width: 150px; max-width: 42vw;
  padding: 5px 8px; border-radius: 6px;
  border: 1px solid #3d6b5f; background: rgba(9,26,22,.92); color: #dff5ea; font-size: 12.5px;
}
.tm-ad-vs { color: #f0c419; font-size: 13px; letter-spacing: 1px; }
.tm-ad-tip { margin-top: 7px; font-size: 11.5px; color: #7e9c90; line-height: 1.6; }

/* AI 对打观战：对局里不能出牌 —— 与联机观战 spec-on 同一套口径（整片隐藏操作按钮）。
   单独一个类名，是因为 spec-on 归 online.js 管，两边共用会在退出时互相踩。 */
#actionBar.ai-on #btnExtra,
#actionBar.ai-on #btnDirect,
#actionBar.ai-on #btnSkipBattle,
#actionBar.ai-on #btnFinishTurn,
#actionBar.ai-on #btnEndTurn { display: none !important; }
#actionBar.ai-on .phase-hint { color: #9fb0c4; font-style: italic; }

/* AI 对打观战：「⏸ 暂停 / ▶ 继续」（2026-10-22 玩家要求）。
   默认 hidden（只在观战局由 APP.setAiDuelUI 打开）；暂停中按钮转金色 + 呼吸，
   和旁边「观战中」的冷色分开 —— 玩家一眼能看出「是停了，不是卡住了」。 */
#actionBar.ai-on #btnTestPause.is-paused {
  color: #1a1206; font-weight: 700;
  background: linear-gradient(#f7dd7c, #e2b93c);
  border-color: #f0c419;
  animation: pause-breathe 1.6s ease-in-out infinite;
}
@keyframes pause-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240,196,25,0); }
  50% { box-shadow: 0 0 0 3px rgba(240,196,25,.35); }
}

/* 观战时对方手牌是**明牌**（玩家要求「可以看到两边手牌」）：
   背面卡是 .facedown（不可点），明牌给一点描边 + 手型，点开是卡牌详情。
   卡片比背面卡宽一号（44×62 → 56×78）：44px 宽时卡名只剩 2 个字（「奈落…」），
   纠错时认不出是哪张卡。手机版仍然按 38×54，别把窄屏的全宽挤爆。 */
.opp-hand .card.revealed {
  cursor: pointer;
  width: 56px; height: 78px;
  box-shadow: 0 0 0 1px rgba(143,227,200,.55), 0 2px 6px rgba(0,0,0,.55);
}
html.ui-mobile .opp-hand .card.revealed { width: 38px; height: 54px; }

.tm-badge {
  display: none;                       /* 默认不存在；只有 body.in-test 才显示 */
  align-items: center; white-space: nowrap;
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid #3d6b5f; background: rgba(19,44,39,.72);
  color: #9fe7cf; font-size: 11.5px; letter-spacing: .3px;
}
body.in-test .tm-badge { display: inline-flex; }

/* 手机版专用元素：电脑版一律不显示 */
.panel-grip, .panel-toggle { display: none; }

/* ==================================================================
   ★ 手机版界面（html.ui-mobile）
   —— 只加覆盖、不动电脑版一个像素。开关见开始界面右上角 📱。
   正文按「竖屏手机」重排：单列、更大的点按目标、场地格子按 vw 缩放、
   操作条从「固定悬浮」改为「正常流」，避免挡住场地最后一行。
   ================================================================== */

/* ---- 基础：可视高度用 dvh（跟地址栏伸缩），去掉双击缩放 ---- */
html.ui-mobile body {
  height: 100dvh;
  touch-action: manipulation;              /* 连点卡片不会误触浏览器双击放大 */
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  overflow: hidden;
}

/* ==================== 主菜单 ==================== */
html.ui-mobile #menu { align-items: flex-start; justify-content: flex-start; }
html.ui-mobile .menu-inner { width: 100%; padding: 58px 12px 18px; }
html.ui-mobile .title { font-size: 27px; letter-spacing: 4px; }
html.ui-mobile .title span { font-size: 13px; letter-spacing: 6px; margin-top: 4px; }
html.ui-mobile .subtitle { font-size: 12px; margin: 8px 0 14px; }
html.ui-mobile .menu-cols { grid-template-columns: minmax(0, 1fr); gap: 10px; }
html.ui-mobile .panel-card { min-height: 0; padding: 12px; }
html.ui-mobile .opponent-list { grid-template-columns: minmax(0, 1fr); max-height: 240px; }
html.ui-mobile .opp-item { padding: 10px; }
html.ui-mobile .deck-preview { max-height: 168px; }
html.ui-mobile .deck-actions .btn { flex: 1 1 auto; }
html.ui-mobile .menu-foot { gap: 10px; margin-top: 16px; }
html.ui-mobile .btn.big { padding: 13px 28px; font-size: 17px; letter-spacing: 2px; }
/* 手指比光标粗：整体抬高按钮点按目标（≥40px 高） */
html.ui-mobile .btn { padding: 10px 14px; font-size: 14px; }
html.ui-mobile .btn.small { padding: 9px 12px; font-size: 13px; }

/* ==================== 决斗场 ==================== */
/* 操作条改回正常流：它有多高就占多高，不再悬浮盖住场地最后一行 */
html.ui-mobile #duel { padding-bottom: 0; }
html.ui-mobile .duel-wrap {
  flex-direction: column; height: auto; flex: 1 1 auto; min-height: 0;
  padding: 4px; gap: 0;
}
html.ui-mobile .field { padding: 5px 6px; border-radius: 8px; overflow-y: auto; }
html.ui-mobile .side { gap: 3px; }
html.ui-mobile .row { min-height: 0; gap: 3px; }
html.ui-mobile .zones { gap: 4px; }
/* 格子按视口宽度缩放：5 格 + 间隔在 320px 的窄屏上也塞得下 */
html.ui-mobile .slot { width: min(64px, 17vw); height: min(86px, 23vw); }
html.ui-mobile .hand { min-height: 0; gap: 3px; justify-content: flex-start; flex-wrap: nowrap; }
html.ui-mobile .hand .card { width: 52px; height: 71px; flex: 0 0 auto; }
html.ui-mobile .hand .card:hover { transform: none; }         /* 触屏没有 hover */
html.ui-mobile .opp-hand .card { width: 26px; height: 36px; }
html.ui-mobile .row.bottom { justify-content: flex-start; gap: 4px; flex-wrap: nowrap; }
/* ⚠ 手牌排满时底部这一行会超宽：6 张手牌 6×52+5×3=327px，加三个堆与间隔 ≈450px，
   而手机视口只有 390 左右 → 「墓地 / 额外」被挤出屏幕外，玩家看不到墓地张数
   （2026-09-22 实测：手牌 327px 时 墓地 x=385、额外 x=427，全在视口外）。
   修法：三个堆禁止被压缩且不许收缩宽度，把「可伸缩的那一格」让给手牌 → 手牌自己横向滑动。
   ⚠ 所以 `.stack` 的 flex:0 0 auto 必须写在这条 flex-wrap 规则之后，别被上面的宽度规则盖掉。 */
html.ui-mobile .row.bottom > .stack { flex: 0 0 auto; }
html.ui-mobile .row.bottom > .hand.me-hand { flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden; }
/* 触屏是滑动操作，别让横向滚动条白占一行高度（滚动条会把这一行撑高 ~8px，
   而手机版的场地高度是刚好卡满视口的，多一行就会被压到看不见） */
html.ui-mobile .row.bottom > .hand.me-hand { scrollbar-width: none; }
html.ui-mobile .row.bottom > .hand.me-hand::-webkit-scrollbar { display: none; }
html.ui-mobile .stack { width: 38px; }
html.ui-mobile .pile { width: 38px; height: 54px; font-size: 9px; gap: 1px; }
html.ui-mobile .pile b { font-size: 13px; }
html.ui-mobile .midline { height: 16px; margin: 2px 0; }
html.ui-mobile .midline span { font-size: 10px; letter-spacing: 2px; padding: 0 8px; }
html.ui-mobile .pbar { gap: 5px; font-size: 11px; }
html.ui-mobile .pname { min-width: 46px; font-size: 12px; }
/* 手机版 LP 条一行要塞「头像 + 名字 + LP 条 + 三个 pill」，头像收小到 20px */
html.ui-mobile .pav { width: 20px; height: 20px; }
html.ui-mobile .lpwrap { height: 14px; }
html.ui-mobile .lpn { font-size: 10px; line-height: 14px; }
html.ui-mobile .pill { font-size: 10px; padding: 1px 6px; }

/* 底部操作条：LP / 阶段 / 提示 / 按钮各自成行 */
html.ui-mobile .action-bar {
  position: relative; left: auto; right: auto; bottom: auto; z-index: 1;
  flex: 0 0 auto; flex-wrap: wrap; gap: 5px 8px; padding: 6px 8px;
  padding-bottom: calc(6px + env(safe-area-inset-bottom));
}
html.ui-mobile .action-bar .ab-pbar { flex: 1 1 auto; width: auto; }
html.ui-mobile .action-bar .ab-pbar .pname { min-width: 40px; }
html.ui-mobile .action-bar .ab-phase { padding: 2px 9px; font-size: 11px; letter-spacing: 1px; }
html.ui-mobile .action-bar .phase-hint {
  flex: 1 1 100%; font-size: 10.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-mobile .action-bar .phase-btns {
  /* minmax(0,1fr)：列的宽度只按屏幕分，不随按钮文案长短变化 ——
     否则「额外卡组召唤」这类长文案会把第一列撑宽，「结束回合」跟着往右挪。 */
  flex: 1 1 100%; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 5px;
}
html.ui-mobile .action-bar .phase-btns .btn {
  width: 100%; min-width: 0; padding: 9px 4px; font-size: 12px; white-space: nowrap;
}
/* 「进入战斗阶段」永远排在最上面一整行、最显眼。
   ⚠ 用 order 而不是 grid-row：按钮个数会变（📱 / 📜 都可能出现或隐藏），
   写死 grid-row 会让多出来的按钮撑出隐式列、把操作条顶出屏幕。 */
html.ui-mobile .action-bar .phase-btns .btn.endturn {
  grid-column: 1 / -1; order: -1; padding: 11px 6px; font-size: 15px;
  min-width: 0;   /* 整行铺满，不需要桌面版那条固定宽度 */
}
html.ui-mobile .panel-toggle { display: block; }

/* ---- 战况抽屉：场地不再被侧栏挤掉，需要看日志时再拉起来 ---- */
html.ui-mobile .panel {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 78;
  width: auto; height: 54vh; max-height: 470px; gap: 6px;
  padding: 0 10px calc(10px + env(safe-area-inset-bottom));
  background: rgba(9,14,20,.985); border-top: 2px solid var(--gold-dim);
  border-radius: 14px 14px 0 0; box-shadow: 0 -10px 30px rgba(0,0,0,.72);
  transform: translateY(105%); transition: transform .24s ease;
  pointer-events: none;
}
html.ui-mobile .panel.open { transform: translateY(0); pointer-events: auto; }
html.ui-mobile .panel-grip {
  display: block; margin: 0 -10px; padding: 9px 0 8px; width: calc(100% + 20px);
  text-align: center; font-size: 12px; color: var(--gold);
  background: none; border: none; border-bottom: 1px dashed #2b3a4d;
  font-family: inherit; cursor: pointer; flex: 0 0 auto;
}
html.ui-mobile .panel .turnbox { padding: 6px 10px; flex: 0 0 auto; }
html.ui-mobile .panel .cardview { min-height: 0; max-height: 30vh; overflow: auto; flex: 0 1 auto; }
/* 📜「战况」抽屉只讲战斗：**回合信息 + 战斗日志**，不显示卡牌详情（怪兽信息）。
   卡牌详情只出现在「卡片模式」抽屉里（点了棋盘上的卡才切成那一种，见 .card-mode）。
   以前的完整抽屉也带一块详情区（基础 .cardview 有 min-height:190px）：只要点过一张卡，
   它就一直杵在抽屉里，54vh 的抽屉被吃掉一大半、日志只剩两三行 ——
   用户明确要求「战况那里，不需要显示怪兽信息，只需要显示战斗的信息」。 */
html.ui-mobile .panel:not(.card-mode) .cardview { display: none; }
html.ui-mobile .panel .cv-art img { max-height: 132px; }
html.ui-mobile .panel .cv-empty { padding-top: 24px; }
html.ui-mobile .panel .logbox { min-height: 60px; }

/* ---- 手机版：点卡牌的操作按钮放进抽屉顶部（不浮在棋盘上遮住卡牌与攻/守角标） ---- */
.action-sheet { display: none; }
html.ui-mobile .action-sheet {
  display: flex; flex-wrap: wrap; gap: 6px; padding: 0 0 8px;
  border-bottom: 1px dashed #2b3a4d;
}
html.ui-mobile .action-sheet:empty { display: none; padding: 0; border-bottom: none; }
html.ui-mobile .action-sheet .btn { flex: 1 1 auto; text-align: center; }
html.ui-mobile .action-sheet .btn:disabled { opacity: .38; }

/* ---- 手机版「卡片模式」抽屉：只长到卡牌详情那么高 ----
   完整抽屉是 54vh，会把自己怪兽那一排连同右下角的攻/守角标整块盖住；
   卡片模式下按内容高度收缩、隐藏回合信息与战斗日志、卡图也收小，
   抽屉顶边落在棋盘下沿之下，自己怪兽的攻/守角标始终看得见。
   点棋盘空白处（或选完动作）抽屉会自动收起，操作条不会被长期挡住。 */
html.ui-mobile .panel.card-mode { height: auto; max-height: 60vh; }
html.ui-mobile .panel.card-mode .turnbox { display: none; }
html.ui-mobile .panel.card-mode .logbox { display: none; }
html.ui-mobile .panel.card-mode .cardview { max-height: 40vh; }
html.ui-mobile .panel.card-mode .cv-art img { max-height: 88px; }

/* ==================== 卡组编辑器 ==================== */
html.ui-mobile .bar { flex-wrap: wrap; gap: 6px; padding: 8px 10px; align-items: center; }
html.ui-mobile .bar h2 { font-size: 15px; letter-spacing: 1px; }
html.ui-mobile .bar .spacer { display: none; }        /* 让标题和按钮自己排，不留半行空白 */
/* 手机版页头里的按钮一律不被压缩；返回按钮做大一点，手指点得中 */
html.ui-mobile .bar > .btn, html.ui-mobile .bar > button { flex: 0 0 auto; }
html.ui-mobile .bar > .btn.ghost:first-of-type { padding: 9px 13px; }
html.ui-mobile .bar > .btn.primary { flex: 1 1 auto; text-align: center; }
html.ui-mobile .editor-body {
  display: flex; flex-direction: column; gap: 8px; padding: 8px;
  /* 🔴 2026-10-09 玩家报「手机版根本看不到卡库里的卡」——真凶就是这一块塌陷：
     原来 ed-left 是 `flex: 1 1 auto`，短视口下被右栏（38vh）挤到只剩工具栏的高度，
     里面的左池 `flex: 1`（basis 0）于是被压成 **0px**，卡库一条都不显示；
     而外观上「工具栏下面直接就是当前卡组」，看着像功能没做 / 卡库是空的。
     现在：ed-left 不再被压缩（内容多高就多高），左池保底 148px（≥3 条），
     整页装不下时 editor-body 自己滚 —— 宁可多滚一屏，也不要把卡库挤没。 */
  overflow-y: auto;
}
html.ui-mobile .ed-left { flex: 0 0 auto; }   /* 不再被右栏挤扁（见上） */
html.ui-mobile .ed-tools { gap: 6px; }
/* 搜索框和排序下拉排在同一行（原来是各占一行，白吃掉 40px 高度 ——
   那 40px 让「我的收藏」卡池在 880 高的手机上少看大半张卡）。 */
html.ui-mobile .ed-tools input {
  width: auto; flex: 1 1 150px; min-width: 0; padding: 9px 10px;
}
/* 卡库页工具栏同款：手机版搜索框不写死宽度，跟下拉抢同一行 */
html.ui-mobile .vault-tools { gap: 6px; }
html.ui-mobile .vault-tools input { width: auto; flex: 1 1 130px; min-width: 0; padding: 9px 10px; }
html.ui-mobile .vault-tools select.chip { padding: 9px 10px; }
html.ui-mobile .chips { overflow-x: auto; padding-bottom: 4px; width: 100%; }
html.ui-mobile .chip { flex: 0 0 auto; padding: 7px 13px; }
/* 手机版「我的收藏」（卡池）＝ **一行一条**，和右侧「当前卡组」同款，不要方格。
   走过的弯路记一下：先试过把方格下限提到 158px（两列大卡 168~203px），
   用户看完说「不要这样一个个方格的，参考当前卡组这样一行行的」——
   方格再大也还是一格一格，手机窄屏下一卡占半行、信息被切成两截，
   不如整行条目：左边卡名 + 稀有度/限制/实装标签，右边顶格放紧凑数值
   （★7 2500/2100 · 卡组 1/3 · 库 2），和「当前卡组」的「卡名——ATK」视觉完全一致。
   ⓘ 详情按钮照旧绝对定位在右上角（宿主 padding-right: 33px 给它留位）。 */
html.ui-mobile .ed-pool {
  display: flex; flex-direction: column; gap: 4px; padding-right: 0;
  /* 卡库（可加入的卡）必须**看得见**：保底 148px、上限 36vh（别吃掉整屏）。
     没有 min-height 时它在短视口会被压成 0 —— 就是玩家报的那个 bug。
     ⚠ 上限由 42vh 收到 36vh（2026-10-16）：那 6vh 让给右栏的卡组列表，
       见下面 `html.ui-mobile .ed-right`（玩家要求「这个位置变得长一点」）。
       148px 保底一字未动，短视口下依然至少有 3 条可点。 */
  flex: 1 1 auto; min-height: 148px; max-height: 36vh;
}
html.ui-mobile .pool-item {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 10px; font-size: 13.5px; line-height: 1.4; border-radius: 5px;
}
/* 卡名吃掉剩余宽度，放不下就末尾省略号 —— 不再折行、不再把「异次元的女战士」拆成两截 */
html.ui-mobile .pool-item .nm {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 稀有度 / 限制 / 已实装 这几个标签**不许被压扁**：它们是 flex 子项，
   默认 flex-shrink:1 会把「蕾雅卡」「已实装」压到换行 → 整行从 39px 变 53px，
   看上去像排版坏了。让它们保持原宽、不换行，宽度不够时由卡名去省略。
   ⚠ 稀有度标签是 `<span class="rar">`（不是 <b>），选择器必须两个都写上 ——
     只写 `> b` 时 .rar 依然会被压缩换行。 */
html.ui-mobile .pool-item > b,
html.ui-mobile .pool-item > .rar { flex: 0 0 auto; white-space: nowrap; }
html.ui-mobile .pool-item small { margin: 0; flex: 0 0 auto; font-size: 11px; }
html.ui-mobile .pool-item small.s-wide { display: none; }
html.ui-mobile .pool-item small.s-narrow { display: block; }
html.ui-mobile .ed-right {
  /* 🔴 [2026-10-16 玩家要求] 30vh → 48vh（并加 300px 下限）：
   *   玩家截图里手机版「测试卡组 / 当前卡组」列表只有 112px —— 一屏两条多一点，
   *   而右栏本身只占 38vh。现在整栏 48vh，配合 `#edExtra,#tmExtra{flex:0 1 auto}`
   *   （额外卡组按内容占高）主卡组列表能吃到 7~8 条；短屏（<625px 高）保底 300px，
   *   免得 320×568 这种小机上又被算回 2 条。
   *   ⚠ 整页装不下时 `.editor-body` 自己滚（见上面那段「宁可多滚一屏，也不要把卡库挤没」），
   *     左池的 148px 保底一字未动。回归：tools/test-mobile-ui.js（主卡组列表必须高于额外卡组列表）。 */
  flex: 0 0 auto; height: max(48vh, 300px); min-height: 0; padding: 8px;
}
html.ui-mobile .ed-item { padding: 9px 12px; font-size: 13.5px; }

/* ---- 手机版：卡组编辑器条目的「ⓘ 详情」按钮 ----
   触屏没有 hover，`#cardPop` 迷你预览窗弹不出来，于是手机版根本看不到卡片详情。
   ⓘ 挂在每个条目右上角（绝对定位），点了直接打开完整详情弹窗（UI.showBig）。
   它自己 stopPropagation，所以「点一下加入 / 踢出卡组」的行为一字未改。 */
.ed-info { display: none; }
html.ui-mobile .ed-info {
  display: block; position: absolute; right: 5px; top: 5px;
  width: 24px; height: 24px; line-height: 22px; text-align: center;
  border-radius: 50%; font-size: 14px; font-weight: 400; font-style: normal;
  background: rgba(9,14,20,.92); border: 1px solid var(--gold-dim); color: var(--gold);
  cursor: pointer;
}
html.ui-mobile .ed-info:active { background: var(--gold-dim); color: #10160f; }
/* ⓘ 是绝对定位：宿主条目要 relative，并只预留 33px（= 5px 边距 + 24px 按钮 + 4px 余量）。
   手机版卡池是两列、每列 168~203px，卡名区还有 130+px，9 个字的卡名
   （「异次元的女战士」= 121px @13.5px）能单行放下。 */
html.ui-mobile .pool-item, html.ui-mobile .ed-item { position: relative; padding-right: 33px; }
html.ui-mobile .ed-mobile-tip {
  display: block; font-size: 11px; color: #7d8b9e; line-height: 1.7; margin: 2px 0 4px;
}
html.ui-mobile .ed-mobile-tip b { color: var(--gold); }

/* ==================== 童实野镇 ==================== */
html.ui-mobile #town .town-wrap { flex-direction: column; gap: 8px; padding: 8px; }
html.ui-mobile #town .town-body { flex: 1 1 auto; min-height: 0; padding: 10px; }
html.ui-mobile #town .town-side { width: 100%; flex: 0 0 auto; max-height: 24vh; }
html.ui-mobile .town-grid, html.ui-mobile .fac-grid { grid-template-columns: 1fr; }
/* 手机版童实野镇顶栏的钱包：单独占一整行、排成两列，
   金币 / 流通券 / 荣光碎片 / 决斗之王 四项一眼全见（原来是挤在页头里被裁掉/挤爆）。 */
html.ui-mobile #town .wallet {
  flex: 1 1 100%; order: 9; margin: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px 6px;
}
html.ui-mobile #town .wallet .pill {
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px; padding: 5px 9px; font-size: 11.5px; white-space: nowrap;
}
html.ui-mobile #town .wallet .pill b { color: #f0c419; font-size: 13px; }
html.ui-mobile #town .wallet .rate { grid-column: 1 / -1; text-align: center; font-size: 10.5px; }
html.ui-mobile .town-tabs { overflow-x: auto; padding: 8px 10px; }
html.ui-mobile .town-tabs .btn { flex: 0 0 auto; }
html.ui-mobile .stock-table { font-size: 12px; }
html.ui-mobile .stock-table th, html.ui-mobile .stock-table td { padding: 7px 5px; }
html.ui-mobile .stock-table input { width: 52px; }
html.ui-mobile .bracket { flex-direction: column; gap: 8px; }
/* 任务 / 成就卡在手机上改成**上下两段**（2026-11-11 玩家报成就墙「超级拥挤」）：
   第一段：标题 + 徽章 + 描述 + 进度条（占满整行，不再被右侧奖励串挤成竖排单字）；
   第二段：奖励（可折行）在左、「领取」按钮贴右。原来只有 flex-wrap，奖励串
   white-space:nowrap 的 min-content 会把第一段压到 ~2 字宽。 */
html.ui-mobile .task-item {
  flex-wrap: wrap; align-items: flex-start; gap: 0 10px;
  padding: 11px 12px 12px; margin-bottom: 8px;
}
html.ui-mobile .task-item .ti-main { flex: 1 1 100%; min-width: 0; }
html.ui-mobile .task-item .ti-main h4 {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px;
  margin-bottom: 4px; font-size: 13.5px; line-height: 1.4;
}
html.ui-mobile .task-item .ti-main h4 .badge { font-size: 10.5px; padding: 1px 6px; }
html.ui-mobile .task-item .ti-main p { font-size: 11.5px; line-height: 1.6; }
html.ui-mobile .task-item .ti-main p b { color: #f0c419; }
html.ui-mobile .task-item .ti-rw {
  flex: 1 1 auto; min-width: 0; max-width: none; margin-top: 8px;
  padding-top: 7px; border-top: 1px dashed rgba(255,255,255,.08);
  font-size: 11.5px; text-align: left; line-height: 1.55;
}
html.ui-mobile .task-item .ti-rw .rw-lb { display: inline; margin-right: 5px; }
html.ui-mobile .task-item > .btn.tiny {
  flex: 0 0 auto; align-self: flex-end; margin-top: 8px; min-width: 72px; padding: 7px 12px;
}
html.ui-mobile #town .town-head { margin-bottom: 10px; }
html.ui-mobile #town .town-head h3 { font-size: 15.5px; }
html.ui-mobile #town .town-head p { font-size: 11.5px; line-height: 1.65; }
html.ui-mobile .chips { gap: 4px; }
html.ui-mobile .chips .chip { padding: 5px 10px; font-size: 11.5px; }
/* 成就分类只有 6 个 chip，手机上让它换行铺满两排 —— 横滚条会藏掉「赛事 / 生活」，
   玩家以为没有这两类。其它 9 个 chip 的筛选条（卡图鉴）仍走横滚。 */
/* 7 个 chip（全部 + 6 类）→ 4 列 2 行；3 列会多出一行只剩「生活」一个小方块 */
html.ui-mobile #achFilters { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 5px; overflow-x: visible; padding-bottom: 0; }
html.ui-mobile #achFilters .chip { padding: 6px 4px; text-align: center; }
html.ui-mobile .ti-meter { gap: 6px; margin-top: 5px; }
html.ui-mobile .ti-num { font-size: 10.5px; }
html.ui-mobile .ach-top { gap: 8px; margin: 8px 0 2px; }
/* 390 视口下 12px 的统计串 + 按钮差 2px 会折行 → 各收 0.5px 字号与内边距，一行装下 */
html.ui-mobile .ach-top p { font-size: 11.5px; }
html.ui-mobile .ach-top .btn { padding: 5px 10px; font-size: 11.5px; }
/* 手机版把「当前显示名」收起来：称号区底部已经写了一次，顶部再占一行纯属浪费竖向空间 */
html.ui-mobile .ach-shown { display: none; }
html.ui-mobile .town-toast { bottom: 18px; width: max-content; max-width: 92vw; }

/* ==================== 弹层 / 结算 / 浮层 ==================== */
html.ui-mobile .modal-box {
  min-width: 0; width: 94vw; max-width: 94vw; max-height: 86vh; padding: 14px;
}
/* 手机上留一点可点面积（11px 字在高分屏太小），但仍明显小于「明白了」主按钮 */
html.ui-mobile .modal-box .help-reset-mini { font-size: 11.5px; padding: 4px 10px; }
html.ui-mobile .sel-list { grid-template-columns: 1fr; max-height: 52vh; }
html.ui-mobile .sel-item { padding: 11px 10px; font-size: 13px; }
/* 卡库清单：手机上弹窗更矮，文本块跟着缩；长卡名（「荷鲁斯之黑炎龙 LV8」）横向滚动不折行 */
html.ui-mobile .vl-text { max-height: 46vh; font-size: 12px; }
html.ui-mobile .vl-seg .chip { flex: 1 1 auto; }
/* 分享版清单：窄屏两列太挤 → 单列；行高放大一点，方便手指点「!」 */
html.ui-mobile .vs-list { max-height: 46vh; }
html.ui-mobile .vs-grid { grid-template-columns: 1fr; }
html.ui-mobile .vs-row { padding: 6px 4px; font-size: 13px; }
html.ui-mobile .ct-share-btn { padding: 9px 10px; font-size: 13px; }
html.ui-mobile .modal-actions { justify-content: stretch; }
html.ui-mobile .modal-actions .btn { flex: 1 1 auto; padding: 11px 10px; }
/* 弹层内容变长（联机结算里也挂了战况）时，按钮贴底常驻，不用滚到底才够得着 */
html.ui-mobile .modal-box .modal-actions {
  position: sticky; bottom: 0; z-index: 2;
  margin: 8px -14px -14px; padding: 9px 14px 11px; background: #151d29;
}
html.ui-mobile .result { padding: 12px 8px 18px; }
html.ui-mobile .result-box { width: 94vw; max-width: 94vw; padding: 18px 14px; margin: auto; }
html.ui-mobile .result-box h2 { font-size: 26px; letter-spacing: 4px; }
html.ui-mobile .result-box p { font-size: 13px; margin-bottom: 12px; }
html.ui-mobile .rw-box { max-width: 100%; padding: 10px 12px; margin-bottom: 12px; }
/* 战况整体限高：手机上让「再来一局 / 返回菜单」始终留在屏内，不用滚到底才看得见 */
html.ui-mobile .rw-bt { max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
html.ui-mobile .rw-bt-tl { max-height: 116px; }
/* 按钮区贴底常驻：无论战况多长，操作按钮永远点得到 */
html.ui-mobile .result-box .modal-actions {
  position: sticky; bottom: 0; z-index: 2;
  margin: 10px -14px -18px; padding: 10px 14px 12px;
  background: linear-gradient(rgba(16,23,34,.55), #101722 42%);
}
html.ui-mobile .rw-bt-tb { font-size: 12px; }
html.ui-mobile .rw-special { font-size: 12.5px; padding: 7px 10px; letter-spacing: 0; }
html.ui-mobile .actions { max-width: 88vw; min-width: 156px; }
html.ui-mobile .actions button { padding: 11px 12px; font-size: 14px; }
html.ui-mobile .banner { font-size: 27px; letter-spacing: 3px; max-width: 92vw; }
html.ui-mobile .banner.turn { font-size: 22px; letter-spacing: 2px; }
/* 融合 / 同调召唤横幅在手机上收敛字号（桌上 42px 会溢出），落点大字同理 */
html.ui-mobile .banner.fus,
html.ui-mobile .banner.syn { font-size: 27px; letter-spacing: 3px; }
html.ui-mobile .fxword { font-size: 19px; letter-spacing: 2px; }
html.ui-mobile .banner.has-art { gap: 7px; }
html.ui-mobile .banner .banner-art { width: 116px; height: 116px; }
html.ui-mobile .dmg { font-size: 26px; }
html.ui-mobile .card-pop { width: min(268px, 86vw); }
html.ui-mobile .card-pop .cp-art img { max-width: 100%; }
/* 开局页的决斗者名：手机版别把输入框撑出屏幕 */
html.ui-mobile .name-row { margin: -4px 0 14px; gap: 6px; flex-wrap: wrap; }
html.ui-mobile .name-label { font-size: 12px; letter-spacing: 1px; }
html.ui-mobile .name-input { width: min(210px, 54vw); font-size: 13px; padding: 8px 10px; }
html.ui-mobile .name-hint { min-width: 46px; font-size: 10.5px; }
/* 头像选择行：手机版一排放 6 个 36px 圆（6*36 + 5*6 = 246px，390 宽的屏放得下）。
   「头像」标签与提示文案缩到最小，把宽度全让给 6 个头像。 */
html.ui-mobile .avatar-row { margin: -8px 0 14px; gap: 7px; }
html.ui-mobile .av-opts { gap: 6px; }
html.ui-mobile .av-opt { width: 36px; height: 36px; }
html.ui-mobile .av-opt.sel::after { width: 13px; height: 13px; line-height: 13px; font-size: 9px; }
html.ui-mobile .avatar-row .name-label { font-size: 11.5px; letter-spacing: 1px; }
html.ui-mobile .avatar-row .name-hint { min-width: 0; }
html.ui-mobile .rw-vs { gap: 12px; margin-bottom: 12px; }
html.ui-mobile .rw-vs .rw-av { width: 44px; height: 44px; }
html.ui-mobile .rw-vs .rw-side { min-width: 62px; max-width: 118px; }
html.ui-mobile .rw-vs .rw-side small { max-width: 118px; font-size: 10.5px; }
html.ui-mobile .lim-card, html.ui-mobile .res-box, html.ui-mobile .holdings { padding: 11px; }
/* ⚠ 上面这条会把 .lim-card 的 padding-right 也压回 11px，右侧「!」就会盖住文字 ——
   必须在它之后再把右边距补回来（触屏手指更粗，按钮也放大一点）。 */
html.ui-mobile .lim-card { padding-right: 50px; }
html.ui-mobile .lim-info { right: 10px; width: 30px; height: 30px; line-height: 28px; font-size: 17px; }

/* ==================================================================
   ★ 台词气泡（怪兽 / 魔法 / 陷阱台词）
   数据来自 js/quotes.js（tools/extract-quotes.py 从 C++ 原作 1:1 抽取），
   播报时机由引擎决定：召唤 / 攻击宣言 / 击破 / 被破坏送墓 / 效果发动 / 魔陷发动。
   浮层在 body 层级，pointer-events:none —— 绝不拦截任何点击。
   ================================================================== */
.quote-layer { position: fixed; inset: 0; z-index: 92; pointer-events: none; overflow: hidden; }
.qsay {
  position: absolute; transform: translate(-50%, -100%);
  max-width: 320px; pointer-events: none;
}
/* 贴到屏幕顶端时翻到下方展开，免得飘出可视区 */
.qsay.below { transform: translate(-50%, 6px); }
.qsay-inner {
  position: relative; display: flex; align-items: center; gap: 8px;
  padding: 6px 11px 6px 7px; border-radius: 11px;
  background: linear-gradient(#1e2b3d, #131b26);
  border: 1px solid var(--gold-dim);
  box-shadow: 0 8px 22px rgba(0,0,0,.66);
  animation: qsayfx calc(2s * var(--fx)) forwards;
}
/* 小尾巴：普通朝下（气泡在卡片上方），.below 时朝上 */
.qsay-inner::after {
  content: ''; position: absolute; left: 50%; bottom: -7px; transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-top: 7px solid var(--gold-dim);
}
.qsay.below .qsay-inner::after {
  bottom: auto; top: -7px; border-top: none; border-bottom: 7px solid var(--gold-dim);
}
/* 字幕带模式（不贴任何格子：手机版全部 + 电脑版的「手牌 / 墓地发动」）：
   没有指向目标，小尾巴没意义，去掉。 */
.qsay.band .qsay-inner::after { display: none; }
.qsay-art {
  width: 30px; height: 30px; border-radius: 5px; object-fit: cover; flex: 0 0 auto;
  border: 1px solid var(--gold-dim);
}
.qsay-txt { display: block; font-size: 12.5px; line-height: 1.4; color: #e9f0f8; }
@keyframes qsayfx {
  0%   { opacity: 0; transform: translateY(6px) scale(.94); }
  10%  { opacity: 1; transform: translateY(0) scale(1); }
  82%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-6px); }
}
/* 战斗日志里的台词行：斜体暗色，与「谁发动了什么」的结构化行区分开 */
.log-quote { color: #9fb4cc; font-style: italic; }

/* 手机版：气泡固定成「字幕位」（**不贴怪兽格**，见 ui.js `spawnQuote`）——
   手机上一格才 min(64px,17vw) 宽，而气泡宽 76vw，贴在格子上必然横着压住左右邻居，
   玩家实测就是「台词全部挡住」。所以这里：更扁、更窄、去掉指向尾巴。
   ⚠ 字幕带在**屏幕上半部**（对手手牌行下沿起排）：手指只在下半屏操作，
     贴在底部会被手掌挡住（2026-09-23 玩家实测）。 */
html.ui-mobile .qsay { max-width: 86vw; }
html.ui-mobile .qsay-inner { padding: 5px 10px 5px 6px; gap: 7px; border-radius: 9px; }
html.ui-mobile .qsay-inner::after { display: none; }   /* 字幕位不指向任何格子，尾巴没意义 */
html.ui-mobile .qsay-art { width: 22px; height: 22px; }
/* ⚠ 台词**完整显示**：以前这里写死了「最多两行、多余截断」（为了把两条气泡塞进提示行那一小条里），
   玩家实测「台词没有显示完整」。现在字幕带搬到了屏幕上半部、有整条空间可用，
   于是去掉截断 —— 长台词宁可占 3 行，也不能只给半句。 */
html.ui-mobile .qsay-txt {
  font-size: 11.5px; line-height: 1.35;
}

/* ==================================================================
   ★ 对局内聊天（💬 气泡沫泡）—— 与台词气泡**同一套**（同一个浮层 / 同一套防重叠与出框规则，
   见 ui.js spawnQuote），这里只加「说话人名」与敌我配色，让人一眼分得清谁在说话。
   名字由服务端回发（客户端自报的名字不可信，见 online.js onTalk）。
   ================================================================== */
.qsay.chat .qsay-inner { border-color: rgba(217,180,91,.85); }
/* 对方：偏冷的边框，与自己（金）区分 */
.qsay.chat.peer .qsay-inner { border-color: #4f8bbd; }
.qsay-name {
  flex: 0 0 auto; font-size: 11.5px; font-weight: 700;
  color: var(--gold); white-space: nowrap;
}
.qsay.chat.peer .qsay-name { color: #8fc7f0; }
html.ui-mobile .qsay-name { font-size: 11px; }

/* 对局内聊天输入条：悬浮在本底操作条**正上方**（bottom 由 js 打开时实测写入）。
   放在 #duel 的 .action-bar 里 → 离开对局页会随 .screen 一起隐藏，不需要额外收尾。 */
.ol-talkbar {
  position: fixed; left: 0; right: 0; bottom: 56px; z-index: 71;
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px;
  background: linear-gradient(180deg, rgba(16,26,40,.94), rgba(10,16,24,.98));
  border-top: 1px solid var(--gold-dim);
  box-shadow: 0 -4px 18px rgba(0,0,0,.5);
}
.ol-talkbar .otb-lab { flex: 0 0 auto; font-size: 15px; }
.ol-talkbar input {
  flex: 1 1 auto; min-width: 0;
  background: #16202e; border: 1px solid #33475f; border-radius: 8px;
  color: #e9f0f8; font-size: 14px; padding: 7px 10px; outline: none;
}
.ol-talkbar input:focus { border-color: var(--gold-dim); }
.ol-talkbar input::placeholder { color: #7d8ea3; }
html.ui-mobile .ol-talkbar { padding: 6px 8px; gap: 6px; }
/* ⚠ 手机端输入框字号必须 ≥16px：iOS Safari 在 <16px 的输入框聚焦时会**自动放大整页**，
   放大后固定定位的操作条会跳到奇怪的位置（玩家以为界面坏了）。 */
html.ui-mobile .ol-talkbar input { font-size: 16px; }

/* 观战看门狗提示条（2026-10-15 玩家报「网络观战经常卡住不动了」）：
   对方停止推画面时，让观战者知道「这是静止，不是死了」——没这条提示时，静止与卡死
   在肉眼上完全分不出来，玩家只能反复退出重进。
   ⚠ 位置与排版踩过一次（真机截图暴露）：① 原先把条子挂在**顶部**，正好与台词气泡那条
     字幕带（对手手牌行下方往上排）叠在一起，手机上直接糊成一团；② 按钮没有 nowrap，
     窄屏被 flex 挤成"竖排单字"。所以改成**贴底（操作条正上方，与聊天输入条同处）** +
     全行 nowrap + 按钮 flex:0 0 auto。bottom 由 js 按操作条实测高度写入。 */
.spec-stall {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 96px; z-index: 72;
  display: flex; align-items: center; gap: 8px; max-width: 94vw;
  padding: 6px 12px; border-radius: 999px;
  background: rgba(16,22,32,.94); border: 1px solid var(--gold-dim);
  color: #e8eef6; font-size: 13px; line-height: 1.4; white-space: nowrap;
  box-shadow: 0 6px 20px rgba(0,0,0,.42);
}
.spec-stall .ss-txt { white-space: nowrap; }
.spec-stall .btn { flex: 0 0 auto; white-space: nowrap; }
html.ui-mobile .spec-stall { font-size: 12px; padding: 5px 10px; gap: 6px; }

/* ===== 线上对战（仅主菜单显示） ===== */
.online-lobby {
  width: 100%; margin: 14px 0 4px; padding: 14px 18px;
  background: linear-gradient(180deg, rgba(40,60,90,.35), rgba(20,30,45,.35));
  border: 1px solid #3a557a; border-radius: 12px;
}
.ol-head { display: flex; align-items: center; gap: 12px; }
.ol-head h3 { color: var(--gold); font-size: 18px; margin: 0; }
.ol-status { font-size: 12px; color: #9fd6a8; padding: 2px 8px; border: 1px solid #3a6a4a; border-radius: 10px; }
.ol-note { font-size: 12px; color: #9fb0c4; margin: 8px 0 10px; line-height: 1.6; }
/* max-height 是给「在线玩家名单」用的：人多时滚动，别把页面撑开。
   但「局域网联机三步」这类长说明必须完整可见 —— 被裁掉半行会让人以为界面坏了，
   所以长说明模式（js 里加 .ol-tall）放开限高。 */
.online-list { display: flex; flex-direction: column; gap: 8px; max-height: 220px; overflow-y: auto; }
.online-list.ol-tall { max-height: none; overflow-y: visible; }
.ol-empty { font-size: 13px; color: #8a97a8; }
/* 在线人数：紧跟状态标签，让"有几个人"一眼可见（名单里也含自己） */
.ol-count { font-size: 12px; color: #9fb0c4; padding: 2px 8px; border: 1px solid #2c3e54; border-radius: 10px; }
.ol-count:empty { display: none; }
/* 只有自己在线时的引导（金色，明确指出"怎么让对手出现"） */
.ol-wait { font-size: 12px; color: #ffd27a; background: rgba(255,210,122,.08); border: 1px dashed rgba(255,210,122,.35); border-radius: 8px; padding: 8px 10px; margin: 0; line-height: 1.7; }
.ol-item { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 7px 10px; background: rgba(255,255,255,.04); border: 1px solid #2c3e54; border-radius: 8px; }
/* 线上挑战弹窗：挑战者头像 + 文案（头像排左侧，与文字同一行） */
.cf-who { display: flex; align-items: center; gap: 10px; text-align: left; }
.cf-av {
  width: 46px; height: 46px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
  border: 1px solid var(--gold-dim); background: #131b26;
}
.cf-av.av-fallback { font-size: 18px; }

/* 在线名单里的玩家头像（对方开局页选的预设头像，随 hello 上报、服务端转发）。
   没有头像时这个 <img> 没有 src，浏览器不渲染 —— 名单布局不会因此错位。 */
.ol-av {
  width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
  border: 1px solid #4a5c74; background: #131b26;
}
.ol-av:not([src]) { display: none; }
.ol-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; color: var(--ink); }
/* 胜率：紧跟在名字右边，弱化显示，不抢挑战按钮的位置 */
.ol-rec { flex: 0 0 auto; font-size: 11px; color: #9fb0c4; white-space: nowrap; padding: 2px 7px; border-radius: 9px; background: rgba(159,176,196,.12); }
.ol-rec.none { color: #6f7d90; background: rgba(111,125,144,.10); }
.ol-badge { font-size: 11px; padding: 2px 8px; border-radius: 10px; }
.ol-badge.free { color: #9fd6a8; background: rgba(76,175,125,.15); }
.ol-badge.busy { color: #e0a06a; background: rgba(224,160,106,.15); }
.ol-badge.wait { color: #9fb0c4; background: rgba(159,176,196,.12); }
.ol-challenge { min-width: 84px; }
/* 观战入口（名单里对方正在对战时出现）：弱化为幽灵按钮，别抢「挑战」的位置 */
.ol-watch { flex: 0 0 auto; white-space: nowrap; }
.ol-watch:disabled { opacity: .45; }
.ol-share { font-size: 12px; color: #9fb0c4; margin: 10px 0 2px; line-height: 1.7; padding-top: 8px; border-top: 1px dashed #2c3e54; }
.ol-share a { font-weight: 700; }
/* 点击「在线对战」按钮时高亮面板，提示入口在这里 */
.online-lobby.ol-flash { animation: olFlash 1.8s ease; border-color: var(--gold) !important; }
@keyframes olFlash {
  0%   { box-shadow: 0 0 0 0 rgba(217,180,91,0);   border-color: #3a557a; background: rgba(217,180,91,0); }
  15%  { box-shadow: 0 0 22px 4px rgba(217,180,91,.85); border-color: var(--gold); background: rgba(217,180,91,.14); }
  50%  { box-shadow: 0 0 22px 4px rgba(217,180,91,.85); border-color: var(--gold); background: rgba(217,180,91,.14); }
  100% { box-shadow: 0 0 0 0 rgba(217,180,91,0);   border-color: #3a557a; background: rgba(217,180,91,0); }
}

/* ===== PVP 排名窗（2026-10-19 玩家要求：「在线对战框里加按钮，点了就是 pvp 排名窗口」）=====
   数据只来自服务端 rankData（胜场 desc → 总场 asc → 名字，排序在服务端算好）。
   z-index 115 > #modal 110：理论上从主菜单点开撞不到对局弹窗，真撞上也要盖在上面。 */
.pvp-rank-win {
  position: fixed; inset: 0; background: rgba(0,0,0,.62); display: none;
  align-items: center; justify-content: center; z-index: 115; padding: 16px;
}
.prw-box {
  background: #151d29; border: 1px solid var(--gold-dim); border-radius: 10px;
  padding: 16px; width: min(460px, 94vw); max-height: 84vh; overflow: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,.7);
}
.prw-head { display: flex; align-items: center; gap: 10px; }
.prw-head h3 { color: var(--gold); font-size: 15px; margin: 0; flex: 1; }
.prw-note { font-size: 12px; color: #9fb0c4; line-height: 1.6; margin: 8px 0 10px; }
/* 赛季条（2026-10-21）：当前赛季 + 上赛季第 1 名（PVP 之王）挂在榜单最上面 */
.prw-season { margin: 0 0 10px; }
.prw-season-tag {
  display: inline-block; font-size: 11px; color: #9fb0c4;
  border: 1px solid #3b4d64; border-radius: 99px; padding: 2px 9px; margin-bottom: 6px;
}
.prw-king {
  font-size: 12px; color: #e8d9a8; background: rgba(217,180,91,.10);
  border: 1px solid var(--gold-dim); border-radius: 8px; padding: 7px 10px; line-height: 1.7;
}
.prw-king b { color: var(--gold); font-size: 14px; }
.prw-king small { display: block; color: #9fb0c4; font-size: 11px; }
.prw-king-badge {
  display: inline-block; margin-top: 4px; font-size: 11px; color: #2a1c00;
  background: linear-gradient(#f5d98a, #d9b45b); border-radius: 99px; padding: 1px 9px; font-weight: 800;
}
.prw-king-empty { color: #8a97a8; background: none; border-style: dashed; }
/* 我的战绩条：名字/胜/总/胜率一屏说清，没打过也显示 0（不是藏起来） */
.prw-me {
  font-size: 13px; color: #cfe0f0; background: #1b2634;
  border: 1px solid #3b4d64; border-radius: 8px; padding: 8px 10px; margin-bottom: 10px;
}
.prw-me b { color: var(--gold); }
.prw-me-none { color: #8a97a8; }
.prw-list { display: flex; flex-direction: column; gap: 4px; }
.prw-row {
  display: grid; grid-template-columns: 34px 1fr 44px 44px 44px; align-items: center; gap: 6px;
  padding: 6px 8px; background: rgba(255,255,255,.04);
  border: 1px solid #2c3e54; border-radius: 8px; font-size: 13px;
}
.prw-row.headrow { background: none; border: none; padding: 2px 8px; font-size: 11px; color: #8a97a8; }
.prw-no { color: #8a97a8; text-align: center; }
.prw-name { color: var(--ink); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.prw-num { text-align: right; color: #cfe0f0; font-variant-numeric: tabular-nums; }
/* 我自己那一行：金框 + 金名（服务端按 pid 打 me 标记，不靠名字猜） */
.prw-row.me { border-color: var(--gold); background: rgba(217,180,91,.12); }
.prw-row.me .prw-name { color: var(--gold); }
.prw-on { font-style: normal; color: #7fd67f; font-size: 9px; margin-left: 5px; vertical-align: 2px; }
.prw-empty { font-size: 13px; color: #8a97a8; padding: 14px 8px; text-align: center; line-height: 1.7; }
/* 在线对战框里的排名按钮：靠右站，不抢「连接状态 / 在线人数」的位置 */
.ol-rank-btn { margin-left: auto; flex: 0 0 auto; white-space: nowrap; }
/* 手机版：一行放不下「标题+状态+人数+按钮」→ 允许换行，按钮独占一行仍靠右 */
html.ui-mobile .ol-head { flex-wrap: wrap; row-gap: 8px; }
html.ui-mobile .prw-box { width: min(460px, 96vw); }

/* 对战中的强调描边 */
.card.online-atk { outline: 3px solid var(--gold); box-shadow: 0 0 12px rgba(217,180,91,.7); }
.card.online-sel { outline: 3px solid var(--blue); }
.card.online-target { outline: 3px solid var(--red); }

/* 反应倒计时 */
.prompt-timer { margin-top: 12px; font-size: 13px; color: #e0a06a; text-align: center; letter-spacing: 1px; }
.think-banner {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  background: rgba(20,30,45,.9); border: 1px solid #3a557a; color: var(--ink);
  padding: 6px 16px; border-radius: 20px; font-size: 13px; z-index: 50; pointer-events: none;
}
/* 手机版：在线名单与对战按钮同样适配 */
html.ui-mobile .online-lobby { padding: 12px; }
html.ui-mobile .ol-item { font-size: 13px; }
/* 窄屏：名单一行装不下「名字 + 胜率 + 状态 + 按钮」→ 允许换行，胜率独占下一行 */
html.ui-mobile .ol-item { flex-wrap: wrap; row-gap: 6px; }
html.ui-mobile .ol-name { flex: 1 1 100%; }
html.ui-mobile .ol-rec { flex: 0 1 auto; }
html.ui-mobile .ol-challenge { margin-left: auto; }

/* ---------------- 丢硬币 / 丢色子 动画 ---------------- */
#coinDiceFx {
  position: fixed; inset: 0; pointer-events: none; z-index: 80;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 10px; padding-top: 13vh;
}
.cd-fx {
  opacity: 0; transform: translateY(-14px) scale(.8);
  transition: opacity .26s ease, transform .26s ease;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: rgba(16,24,38,.94); border: 2px solid var(--gold); border-radius: 16px;
  padding: 14px 24px; box-shadow: 0 8px 30px rgba(0,0,0,.5), 0 0 18px rgba(217,180,91,.45);
}
.cd-fx.cd-show { opacity: 1; transform: translateY(0) scale(1); }
.cd-fx.cd-out { opacity: 0; transform: translateY(-8px) scale(.9); }
.cd-coin-face {
  width: 66px; height: 66px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 36px; font-weight: 800; color: #1a1208;
  background: radial-gradient(circle at 35% 30%, #ffe9a8, #d9b45b 68%, #b8893a);
  border: 3px solid #8a6526; animation: cdFlip .55s ease-in-out 2;
}
.cd-die {
  width: 66px; height: 66px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font-size: 52px; line-height: 1; color: var(--gold);
  background: rgba(255,255,255,.06); border: 2px solid #3a557a;
  animation: cdShake .45s ease-in-out 2;
}
.cd-label { font-size: 12px; color: #9fb0c4; text-align: center; max-width: 220px; }
.cd-result { font-size: 16px; font-weight: 700; color: var(--ink); }
@keyframes cdFlip { 0%,100% { transform: rotateY(0); } 50% { transform: rotateY(180deg); } }
@keyframes cdShake {
  0%,100% { transform: rotate(0); }
  25% { transform: rotate(-18deg) translateY(-5px); }
  75% { transform: rotate(18deg) translateY(-5px); }
}
html.ui-mobile .cd-coin-face, html.ui-mobile .cd-die { width: 56px; height: 56px; font-size: 30px; }

/* 卡片图鉴 · 手机版：右侧详情栏收起来（没地方放），改成单列列表 ——
   点条目直接弹 UI.showBig() 详情窗（和卡组编辑器右上角 ⓘ 同一条路）。
   列表与「我的收藏」同款：一行一条，不是方格（见 MOBILE-UI.md）。

   🔴 2026-11-11 玩家报「选了『未拥有』筛选后手机页面又要往右拖一点」的真根因：
   `grid-template-columns: 1fr` 的 1fr 是 **minmax(auto,1fr)** —— 最小值是这一列的
   **min-content**。筛选条 `.chips` 是 nowrap 的一排 chip（9 个加起来 ~400px），
   内层虽然有 overflow-x:auto 可以自己横滚，但它是 flex 子项、撑不出外层的 min-content；
   真正把 min-content 顶到 400 的是这一列里的其它内容，一旦 min-content > 视口，
   列宽就比容器还宽 → `.cx-left` 溢出 → 整页可以往右拖十几像素。
   加第 9 个 chip（未拥有）刚好越过临界点，所以「又」出现了。
   修法同「手机版主菜单排版」那一批：**列用 minmax(0,1fr) 把最小宽钉死在 0**，
   再给链路上每一层 flex 容器 min-width:0，横滚只发生在 .chips 自己身上。 */
html.ui-mobile .codex-body { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 8px; }
html.ui-mobile .cx-detail { display: none; }
html.ui-mobile .cx-mobile-tip { display: block; font-size: 11px; color: #8fa0b6; margin: 0 0 4px; }
html.ui-mobile .cx-tools { gap: 6px; min-width: 0; }
html.ui-mobile .cx-left, html.ui-mobile .chips { min-width: 0; }
/* 搜索框与排序下拉同行，和编辑器同一招（省 40px） */
html.ui-mobile .cx-tools input { flex: 1 1 150px; width: auto; }
html.ui-mobile .cx-pool { display: flex; flex-direction: column; gap: 4px; }
html.ui-mobile .cx-item { display: flex; align-items: center; gap: 6px; padding: 6px 8px; }
html.ui-mobile .cx-item .nm {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-mobile .cx-item small { display: block; margin-top: 0; flex: 0 0 auto; white-space: nowrap; }
/* flex 子项默认 flex-shrink:1：不锁住的话「拥有 N」「蕾雅卡」这类小标签会被压到换行 */
html.ui-mobile .cx-item > b, html.ui-mobile .cx-item > .rar { flex: 0 0 auto; white-space: nowrap; }

/* ===================== 聊天 · 交易（js/online.js 注入 #ctPanel）=====================
   ⚠ 默认必须是 display:flex，不能写 none —— ctRender() 打开面板时是把 inline style 清空
   （`panel.style.display = ''`），写 none 会让面板永远显示不出来（首帧则靠 JS 设 none 藏住）。 */
.ct-overlay {
  position: fixed; inset: 0; z-index: 79; display: flex;
  align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .55);
}
.ct-box {
  width: min(980px, 94vw); height: min(86vh, 780px);
  display: flex; flex-direction: column; overflow: hidden;
  background: #121a26; border: 1px solid var(--gold-dim); border-radius: 12px;
  box-shadow: 0 16px 50px rgba(0, 0, 0, .75);
}
.ct-head {
  display: flex; align-items: center; gap: 8px; padding: 10px 14px; flex: none;
  background: linear-gradient(180deg, #1b2534, #141d29); border-bottom: 1px solid #2b3a4d;
}
.ct-head b { color: var(--gold); font-size: 15px; }
.ct-peer { color: #9fb3c8; font-size: 12.5px; }
.ct-head .spacer { flex: 1; }
.ct-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* --- 选人 --- */
.ct-roster { flex: 1; overflow: auto; padding: 12px; display: flex; flex-direction: column; gap: 6px; }
.ct-row {
  display: flex; align-items: center; gap: 8px; flex: none;
  background: #1a2432; border: 1px solid #2c3c50; border-radius: 8px; padding: 8px 10px;
}
.ct-rn { font-weight: 700; }
.ct-rr { color: #8fa0b6; font-size: 12px; flex: 1; }
.ct-empty2 { color: #8fa0b6; font-size: 13px; line-height: 1.9; padding: 18px 16px; text-align: center; }

/* --- 会话 --- */
.ct-session { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.ct-log { flex: 1; min-height: 88px; overflow: auto; padding: 10px 12px; display: flex; flex-direction: column; gap: 5px; }
.ct-msg { font-size: 13px; line-height: 1.6; display: flex; gap: 6px; align-items: baseline; }
.ct-msg .ct-who { color: var(--gold); flex: none; }
.ct-msg.me .ct-who { color: #7fd67f; }
.ct-msg .ct-txt { word-break: break-word; min-width: 0; }
.ct-msg .ct-time { color: #5f7285; font-size: 11px; margin-left: auto; flex: none; }
.ct-msg.sys {
  color: #9fb3c8; font-size: 12.5px; background: #182231;
  border-left: 2px solid var(--gold-dim); padding: 4px 8px; border-radius: 4px;
}
/* 卡库清单分享条目（2026-10-12）：聊天记录里那一行「谁发来的清单」—— 整行就是按钮，
   点开弹窗看全部卡（见 js/online.js onCtShare / js/town.js openVaultShare）。 */
.ct-msg.ct-share { align-items: center; }
.ct-share-btn {
  flex: 1 1 auto; min-width: 0; text-align: left; cursor: pointer; font: inherit;
  background: #16222f; color: #eaf2ff; border: 1px solid var(--gold-dim); border-radius: 7px;
  padding: 6px 10px; font-size: 12.5px; line-height: 1.5;
}
.ct-share-btn:hover { background: #1d2c3c; border-color: var(--gold); color: var(--gold); }
.ct-share-btn b { color: var(--gold); }
.ct-share-btn .ct-share-hint { color: #8fa0b6; font-size: 11.5px; }
.ct-input { display: flex; gap: 6px; padding: 8px 10px; flex: none; border-top: 1px solid #24344a; background: #141d29; }
.ct-input input {
  flex: 1; min-width: 0; background: #0f1620; border: 1px solid #2c3c50; color: var(--ink);
  border-radius: 6px; padding: 7px 9px; font-size: 13px; font-family: inherit;
}
.ct-input input:focus { outline: none; border-color: var(--gold-dim); }

/* --- 交易区 --- */
.ct-trade { flex: none; max-height: 64%; overflow: auto; border-bottom: 1px solid #24344a; background: #101823; }
.ct-trade-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px 4px; }
.ct-trade-head b { color: var(--gold); }
.ct-tpeer { color: #9fb3c8; font-size: 12.5px; }
.ct-trade-head .spacer { flex: 1; }
.ct-tip { color: #8fa0b6; font-size: 12px; line-height: 1.7; padding: 0 12px 6px; }
.ct-trade-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; padding: 0 12px; }
.ct-tcol { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.ct-tcol h5 { color: var(--ink); font-size: 13px; margin-bottom: 2px; }
.ct-tcol.peer h5 { color: #9fb3c8; }
.ct-sum { color: var(--gold); font-weight: 400; font-size: 12px; }
#ctSearch {
  background: #0f1620; border: 1px solid #2c3c50; color: var(--ink);
  border-radius: 6px; padding: 5px 8px; font-size: 12.5px; font-family: inherit;
}
#ctSearch:focus { outline: none; border-color: var(--gold-dim); }
.ct-list {
  max-height: 190px; overflow: auto; border: 1px solid #24344a; border-radius: 6px;
  padding: 5px; display: flex; flex-direction: column; gap: 3px; background: #0d141d;
}
.ct-card {
  display: flex; align-items: center; gap: 6px; flex: none;
  background: #1a2432; border: 1px solid #2c3c50; border-radius: 5px; padding: 4px 7px; font-size: 12.5px;
}
.ct-card.on { border-color: var(--gold); background: #2a3446; }
.ct-card .ct-cn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-card .ct-own { color: #7f93a8; font-size: 11.5px; flex: none; }
.ct-card.ro { background: #161f2b; border-style: dashed; }
.ct-ctl { display: flex; align-items: center; gap: 4px; flex: none; }
.ct-ctl button {
  width: 22px; height: 22px; line-height: 1; border-radius: 4px; cursor: pointer;
  background: #22303f; border: 1px solid #3b4d64; color: var(--ink); font-size: 13px; font-family: inherit;
}
.ct-ctl button:hover { border-color: var(--gold); }
.ct-ctl .ct-n { min-width: 16px; text-align: center; color: var(--gold); }
.ct-cur { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: #9fb3c8; }
.ct-cur input {
  width: 92px; background: #0f1620; border: 1px solid #2c3c50; color: var(--ink);
  border-radius: 5px; padding: 4px 7px; font-size: 12.5px; font-family: inherit;
}
.ct-cur input.over { border-color: var(--red); color: #ffb4a0; }   /* 填的金额超过自己持有 */
.ct-own2 { color: #6f8398; font-size: 11.5px; margin-left: auto; }
.ct-trade-foot { display: flex; align-items: center; gap: 10px; padding: 10px 12px; }
.ct-lockinfo { font-size: 12.5px; color: #9fb3c8; }
.ct-ok { color: var(--green); }
.ct-invite { padding: 12px; }
.ct-invite p { font-size: 13px; line-height: 1.85; color: #d6e2ee; }
.ct-invite-btns { display: flex; gap: 8px; margin-top: 10px; }
.btn.locked { background: #24382c; border-color: var(--green); color: #c6ecd0; }
.btn.has-news { animation: ctPulse 1.5s infinite; }
@keyframes ctPulse {
  0%, 100% { box-shadow: 0 0 0 rgba(217, 180, 91, 0); }
  50% { box-shadow: 0 0 12px rgba(217, 180, 91, .9); }
}
.ol-chat { padding: 2px 7px; }

/* --- 聊天/交易：手机版 --- */
html.ui-mobile .ct-box { width: 97vw; height: 92vh; border-radius: 8px; }
html.ui-mobile .ct-trade-cols { grid-template-columns: 1fr; }   /* 竖屏放不下两列 → 上下排 */
html.ui-mobile .ct-list { max-height: 116px; }
html.ui-mobile .ct-trade { max-height: 60%; }
html.ui-mobile .ct-row { flex-wrap: wrap; row-gap: 6px; }
html.ui-mobile .ct-rn { flex: 1 1 100%; }
html.ui-mobile .ct-input input { font-size: 14px; }   /* ≥16px 才不会触发 iOS 自动放大，这里取 14 与全局一致 */
html.ui-mobile .ol-chat { margin-left: auto; }

/* ======================================================================
   头像稀缺化（2026-09-24）：预设头像解锁条件 / 决斗者头像 1% 掉落 / 蕾雅卡转头像
   ====================================================================== */
/* 卡牌头像（蕾雅卡转化）：完整显示全卡图，绝不裁剪（玩家明确要求「不要切掉头」）。
   覆盖所有 avatar 容器里 object-fit:cover 的默认口径，所以放最后 + !important。 */
img.av-cardart {
  object-fit: contain !important;
  background: linear-gradient(165deg, #2a2135 0%, #161a26 100%);
  padding: 2px;
  border-radius: inherit;
}

/* --- 童实野镇 · 头像库 --- */
.avlib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 10px; }
.avcard {
  background: #141b26; border: 1px solid #26303f; border-radius: 10px;
  padding: 10px 8px; text-align: center; display: flex; flex-direction: column; gap: 6px; align-items: center;
}
.avcard.owned { border-color: #2f7d4f; }
.avcard.cur { border-color: var(--gold, #d9b45b); box-shadow: 0 0 10px rgba(217, 180, 91, .35); }
.avcard .avimg {
  width: 84px; height: 84px; border-radius: 12px; object-fit: cover; display: block;
  background: #0e131b;
}
.avcard .avimg.av-cardart { width: 96px; height: 96px; }
/* 未解锁：**图必须看得清**（玩家看不到长什么样就不会想解锁）。
   原来是 grayscale(1) brightness(.42)，在深色卡片上几乎全黑 —— 改成轻微降饱和 + 锁角标。 */
.avthumb { position: relative; display: inline-flex; line-height: 0; }
.avcard.locked .avthumb .avimg { filter: saturate(.55) brightness(.86); }
.avlock {
  position: absolute; right: -4px; bottom: -4px; font-style: normal; font-size: 13px;
  line-height: 1; padding: 2px 3px; border-radius: 8px;
  background: #0b1017; border: 1px solid #3a4658; box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
}
.avtitle {
  font-size: 11px; color: var(--gold, #d9b45b); letter-spacing: .5px; margin-bottom: -3px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.avcard h5 { font-size: 13px; color: #e8eef6; margin: 0; }
.avcard .avcond { font-size: 11px; color: #93a3b8; line-height: 1.5; min-height: 30px; }
.avcard .avbar { width: 100%; height: 5px; border-radius: 3px; background: #0e131b; overflow: hidden; }
.avcard .avbar i { display: block; height: 100%; background: linear-gradient(90deg, #2f7d4f, #6fcf97); }
.avcard .badge.done2 { font-size: 10px; color: #9fb0c6; border: 1px solid #3a4658; border-radius: 8px; padding: 1px 6px; }
.avlib-tip { font-size: 12px; color: #7c8b9f; margin: 8px 0 14px; line-height: 1.7; }
.avcard .btn { margin-top: 2px; }

/* --- 童实野镇 · 我的保险柜 --- */
.safe-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
@media (max-width: 720px) { .safe-cols { grid-template-columns: 1fr; } }
.safe-box { background: #141b26; border: 1px solid #26303f; border-radius: 10px; padding: 12px; }
.safe-box h4 { margin: 0 0 8px; font-size: 14px; color: #d9b45b; }
.safe-list { max-height: 340px; overflow: auto; }
.safe-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 4px;
  border-bottom: 1px dashed #202a38; font-size: 13px;
}
.safe-row:last-child { border-bottom: 0; }
.safe-row .sn { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.safe-row .sn small { color: #7c8b9f; margin-left: 6px; }

.safe-empty { color: #7c8b9f; font-size: 12px; padding: 10px 4px; }
/* 保险柜页的筛选工具栏（2026-10-20）：两栏各一套，栏内宽度只有半屏 → 一律换行排 */
.safe-box .tool-row.safe-tools { gap: 6px; margin: 0 0 6px; flex-wrap: wrap; }
.safe-box .tool-row.safe-tools input[type="text"] { flex: 1 1 120px; min-width: 100px; }
.safe-box .tool-row.safe-tools select.chip { flex: 0 1 auto; max-width: 46%; font-size: 12px; }
.safe-box .chips.safe-chips { gap: 4px; margin: 0 0 6px; flex-wrap: wrap; }
.safe-box .chips.safe-chips .chip { font-size: 11px; padding: 3px 7px; }
.safe-count { margin: 0 0 6px; font-size: 12px; }
.safe-bulk { margin: 8px 0 0; flex-wrap: wrap; }
.safe-list { max-height: 300px; }

/* ==================== 天下势力 · 群雄割据（2026-10-25） ==================== */
.faction-wrap { max-width: 860px; margin: 0 auto; }
.fac-h { margin: 14px 0 8px; font-size: 14px; color: var(--gold, #d9b45b); }
.fac-id { display: flex; gap: 10px; flex-wrap: wrap; align-items: stretch; margin-bottom: 10px; }
.fac-id-f { flex: 1 1 260px; background: rgba(20,26,40,.85); border: 1px solid #2c3a52; border-radius: 10px; padding: 10px 12px; }
.fac-badge { display: inline-block; font-size: 12px; font-weight: 700; border-radius: 6px; padding: 2px 8px; }
.fac-id-t { font-size: 13px; font-weight: 700; margin: 6px 0 2px; color: #e8ecf4; }
.fac-id-num { flex: 1 1 220px; display: flex; gap: 6px; }
.fac-id-num > div { flex: 1; background: rgba(20,26,40,.85); border: 1px solid #2c3a52; border-radius: 10px; padding: 8px 4px; text-align: center; }
.fac-id-num b { display: block; font-size: 18px; color: var(--gold, #d9b45b); }
.fac-id-num span { font-size: 10px; color: #9aa5b8; }
.fac-titles { width: 100%; display: flex; gap: 6px; flex-wrap: wrap; }
.fac-title { font-size: 11px; background: rgba(217,180,91,.12); border: 1px solid rgba(217,180,91,.4); color: #e8c96a; border-radius: 20px; padding: 2px 10px; }
.fac-sim { background: rgba(63,159,216,.08); border: 1px solid rgba(63,159,216,.35); border-radius: 10px; padding: 8px 10px; font-size: 12px; color: #bcd6ea; margin-bottom: 10px; }
.fac-sim > div + div { margin-top: 4px; }
.fac-map { position: relative; width: 100%; aspect-ratio: 1 / .82; min-height: 300px; background:
  radial-gradient(circle at 50% 45%, rgba(40,54,80,.55), rgba(12,16,24,.9)); border: 1px solid #2c3a52; border-radius: 12px; overflow: hidden; }
.fac-lines { position: absolute; inset: 0; width: 100%; height: 100%; }
.fac-lines line { stroke: rgba(150,165,190,.22); stroke-width: .35; }
.fac-node { position: absolute; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 1px;
  background: rgba(14,19,30,.92); border: 1px solid var(--nc, #8a93a6); border-radius: 9px; padding: 4px 6px; cursor: pointer; min-width: 62px; }
.fac-node-i { font-size: 15px; line-height: 1; }
.fac-node-n { font-size: 10px; color: #dfe5ef; white-space: nowrap; }
.fac-node-m { font-size: 9px; color: #e8c96a; }
.fac-node.shaken { animation: facPulse 1.2s ease-in-out infinite; border-color: #e0643c; }
.fac-node.sel { box-shadow: 0 0 0 2px var(--gold, #d9b45b); }
@keyframes facPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(224,100,60,.5); } 50% { box-shadow: 0 0 0 6px rgba(224,100,60,0); } }
.fac-detail { background: rgba(20,26,40,.85); border: 1px solid #2c3a52; border-radius: 10px; padding: 10px 12px; margin-top: 10px; }
.fac-guard { display: flex; gap: 10px; align-items: center; margin: 8px 0; }
.fac-g-av { width: 42px; height: 42px; border-radius: 50%; border: 1px solid #3a4a66; }
.fac-mini-badge { font-size: 10px; margin-left: 6px; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; padding: 1px 6px; }
.fac-tip { font-size: 12px; background: rgba(217,180,91,.08); border-left: 3px solid rgba(217,180,91,.6); padding: 6px 10px; border-radius: 0 8px 8px 0; color: #e5d9b8; }
.fac-btns { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.fac-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
.fac-card { background: rgba(20,26,40,.85); border: 1px solid #2c3a52; border-radius: 10px; padding: 9px 11px; font-size: 12px; }
.fac-card.mine { box-shadow: 0 0 0 1px rgba(217,180,91,.5) inset; }
.fac-card-h { font-size: 13px; font-weight: 700; display: flex; justify-content: space-between; align-items: baseline; }
.fac-card-n { margin: 5px 0 3px; color: #c8cede; }
.fac-card-sh { color: #8f9aad; font-size: 11px; min-height: 14px; }
.fac-card-a { margin-top: 6px; font-size: 11px; color: #7fd08a; }
.fac-rank { display: flex; flex-direction: column; gap: 3px; }
.fac-rank-r { display: flex; align-items: center; gap: 8px; background: rgba(20,26,40,.7); border: 1px solid #26324a; border-radius: 8px; padding: 4px 10px; font-size: 12px; }
.fac-rank-r.me { border-color: rgba(217,180,91,.55); color: #e8c96a; }
.fac-rank-i { width: 18px; text-align: center; color: #8f9aad; }
.fac-rank-r b { margin-left: auto; color: var(--gold, #d9b45b); }
.fac-rules { padding-left: 18px; }
.fac-rules li { margin: 3px 0; }
/* ---- 2026-10-26 第二版：驻阵者 / 今日战报 / 成员名单 / 多选弹窗 ---- */
.fac-node.mine { box-shadow: 0 0 0 1px rgba(217,180,91,.65) inset; }
.fac-good { background: rgba(90,190,120,.1); border-color: rgba(90,190,120,.45); color: #b6e8c4; }
.fac-season { background: rgba(217,180,91,.1); border-color: rgba(217,180,91,.5); color: #f0dcae; }
.fac-lock { background: rgba(214,116,88,.12); border-color: rgba(214,116,88,.55); color: #f2c3ad; }
.fac-team { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0 8px; }
.fac-team-m { display: flex; gap: 8px; align-items: center; background: rgba(12,17,26,.7); border: 1px solid #26324a; border-radius: 9px; padding: 5px 9px; font-size: 12px; }
.fac-team-m b { color: #e8ecf4; font-size: 12px; }
.fac-log { background: rgba(12,17,26,.55); border: 1px solid #26324a; border-radius: 9px; padding: 7px 10px; margin-bottom: 8px; font-size: 12px; }
.fac-log b { color: #c8cede; }
.fac-log-r { color: #a9b6c9; margin-top: 3px; line-height: 1.45; }
.fac-card-btn { width: 100%; background: none; border: 0; padding: 0; cursor: pointer; text-align: left; font: inherit; }
.fac-card-btn:hover { text-decoration: underline; }
.fac-members { margin: 6px 0 4px; display: flex; flex-direction: column; gap: 3px; max-height: 330px; overflow: auto; }
.fac-member.me { border-color: rgba(217,180,91,.5); background: rgba(217,180,91,.08); }
.fac-member { display: flex; align-items: center; gap: 6px; font-size: 11px; background: rgba(12,17,26,.6); border: 1px solid #222d42; border-radius: 7px; padding: 3px 7px; }
.fac-member b { font-weight: 600; color: #dfe5ef; }
.fac-member-s { margin-left: auto; font-size: 10px; color: #7fd08a; }
/* 窄屏（手机版单列 / 小窗）：据点卡片缩小、画布加高 —— 否则 10 个据点会互相压叠（2026-10-26 目视） */
@media (max-width: 560px) {
  .fac-map { aspect-ratio: 1 / 1.06; min-height: 0; }
  .fac-node { min-width: 0; padding: 3px 5px; border-radius: 8px; }
  .fac-node-i { font-size: 13px; }
  .fac-node-n { font-size: 9px; }
  .fac-node-m { font-size: 8px; }
}
.fac-member-s.busy { color: #e8c96a; }
.fac-rank-f .fac-rank-r small { margin-left: 6px; }
.fac-pick-list { display: flex; flex-direction: column; gap: 6px; max-height: 46vh; overflow: auto; margin: 8px 0; }
.fac-pick { display: flex; align-items: center; gap: 9px; background: rgba(20,26,40,.9); border: 1px solid #2c3a52; border-radius: 9px; padding: 7px 10px; cursor: pointer; font-size: 13px; }
.fac-pick.on { border-color: rgba(217,180,91,.75); box-shadow: 0 0 0 1px rgba(217,180,91,.35) inset; }
.fac-pick-chk { width: 18px; height: 18px; border-radius: 5px; border: 1px solid #4a5872; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; color: var(--gold, #d9b45b); }
.fac-pick b { font-weight: 600; }
.fac-pick small { margin-left: auto; color: #8f9aad; font-size: 11px; }
/* 选人界面：势力 chip / 徽章 / 宿敌箭头 */
.opp-fac { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 6px; min-width: 0; }
/* 徽章本身不许折行（一行两个字还行），但也不许把整行撑宽：超了就省略号 */
.oi-fac { font-size: 10px; border: 1px solid; border-radius: 10px; padding: 1px 7px; margin-left: 6px; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* 影子决斗者标记（2026-11-07）：卡组照抄真人，赢了给决斗券 */
.oi-shadow { font-size: 10px; border: 1px solid rgba(150,180,255,.55); color: #a9c2ff; border-radius: 10px; padding: 1px 7px; margin-left: 6px; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.oi-rival { display: block; font-size: 11px; color: #e8a87c; margin-top: 2px; }
/* 战果框宿敌对决横幅 */
.rw-rival { background: rgba(224,100,60,.1); border: 1px solid rgba(224,100,60,.4); border-radius: 8px; padding: 7px 10px; margin-bottom: 8px; font-size: 12px; color: #f0c9a8; }
/* 宿敌对决的卡包掉落（2026-11-08）：与横幅同色系但更亮，一眼看见「还多给了一包」 */
.rw-rival-pack { background: rgba(90,200,120,.1); border: 1px solid rgba(90,200,120,.4); border-radius: 8px; padding: 7px 10px; margin-bottom: 8px; font-size: 12px; color: #bfe8c8; line-height: 1.7; }
.rw-rival-pack b { color: #8ee2a4; }

/* ============================================================================
 * 决斗都市 · 千年王座（2026-10-26 第三版）
 * 新闻面板 / 主场标记 / 自建势力 / 主菜单入口 / 世界频道 / 观战眼睛
 * ==========================================================================*/
.fac-mode { margin: 2px 0 10px; font-size: 20px; letter-spacing: .06em; color: var(--gold); }
.fac-home { margin-top: 6px; font-size: 12px; color: #e8d9a8; }
.fac-field {
  margin: 6px 0 10px; padding: 7px 10px; border-radius: 8px; font-size: 12.5px;
  background: rgba(255, 255, 255, .04); border: 1px solid rgba(255, 255, 255, .08);
}
/* ---- 据点新闻 ---- */
.fac-news {
  margin: 10px 0 14px; padding: 10px 12px; border-radius: 10px;
  background: rgba(20, 24, 34, .72); border: 1px solid rgba(255, 255, 255, .08);
  max-height: 260px; overflow: auto;
}
.fac-news .fac-h { margin: 0 0 8px; }
.fac-news-r { font-size: 12.5px; line-height: 1.75; color: #c8cede; padding: 1px 0; }
.fac-news-r.defect { color: #f0b46a; }
.fac-news-r.cap { color: #9fd8a8; }
.fac-news-r.season { color: #f2c14e; }
/* ---- 自建势力面板 ---- */
.fac-create {
  margin: 10px 0 14px; padding: 12px 14px; border-radius: 10px;
  background: linear-gradient(180deg, rgba(242, 193, 78, .10), rgba(242, 193, 78, .02));
  border: 1px solid rgba(242, 193, 78, .35);
}
.fac-create .fac-h { margin-top: 0; }
.fac-create .btn { margin-top: 8px; }
.fac-create-mine { border-color: rgba(242, 193, 78, .55); }
.fac-name-input {
  width: 100%; margin: 10px 0 4px; padding: 9px 12px; border-radius: 8px;
  background: #12161f; color: #f2f4f8; border: 1px solid #39405a; font-size: 14px;
  font-family: inherit;
}
.fac-name-input:focus { outline: none; border-color: var(--gold); }
.fac-node.home .fac-node-m { color: #f2c14e; }
/* ---- 主菜单入口卡片 ---- */
.fac-entry-card { margin: 12px 0; }
.fac-entry-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.fac-entry-news { margin-top: 8px; max-height: 110px; overflow: auto; }
.fac-entry-n { font-size: 12px; line-height: 1.7; color: #b9c0d0; }
.fac-entry-n.defect { color: #f0b46a; }
/* 淘汰赛 / 瑞士轮主菜单入口：并排两个**带说明的小窗口**（2026-11-11 玩家要求：
   不能只是一个光秃秃的按钮，要写清这是什么）。
   🔴 用 minmax(0,1fr)：裸 1fr 的隐式最小值是 min-content，窄屏会被文字顶破（2026-11-10 踩过）。 */
.cup-swiss-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px; margin: 0 0 14px;
}
.cup-swiss-row > * { min-width: 0; }
.mode-card {
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px 14px; border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .10);
  background: rgba(22, 27, 38, .72);
}
.mode-card .mc-h { font-size: 15px; font-weight: 700; color: var(--gold); line-height: 1.35; }
.mode-card .mc-d { margin: 0; font-size: 12px; line-height: 1.65; color: #b9c0d0; }
.mode-card .btn { width: 100%; min-width: 0; margin-top: auto; }
html.ui-mobile .cup-swiss-row { gap: 8px; grid-template-columns: minmax(0, 1fr); }
@media (max-width: 620px) {
  html .cup-swiss-row { grid-template-columns: minmax(0, 1fr); }
}
/* ---- 世界频道 ---- */
.ol-world {
  margin: 10px 0; border-radius: 10px; overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .08); background: rgba(16, 20, 28, .7);
}
.ol-world-h {
  padding: 7px 12px; font-size: 13px; font-weight: 600; color: #dfe5f2;
  background: rgba(255, 255, 255, .05); border-bottom: 1px solid rgba(255, 255, 255, .07);
}
.ol-world-log { height: 150px; overflow: auto; padding: 8px 12px; }
.ol-world-log .olw-r { font-size: 12.5px; line-height: 1.7; color: #c3cadb; word-break: break-word; }
/* 每条发言的时间戳（2026-11-11 玩家要求）：等宽数字，别把眼睛从名字上带走 */
.ol-world-log .olw-t {
  display: inline-block; min-width: 38px; margin-right: 6px;
  font-size: 11px; color: #7f8a9e; font-variant-numeric: tabular-nums;
}
.ol-world-log .olw-r b { color: #8fb7ff; font-weight: 600; }
.ol-world-log .olw-r.me b { color: var(--gold); }
.ol-world-in { display: flex; gap: 8px; padding: 8px 10px; border-top: 1px solid rgba(255, 255, 255, .07); }
.ol-world-in input {
  flex: 1; min-width: 0; padding: 7px 10px; border-radius: 8px; font-size: 13px;
  background: #12161f; color: #eef1f7; border: 1px solid #39405a;
}
.ol-world-in input:focus { outline: none; border-color: var(--gold); }
/* ---- 观战眼睛 ---- */
.spec-eye {
  position: fixed; top: 10px; right: 12px; z-index: 60; display: none;
  padding: 5px 10px; border-radius: 999px; font-size: 12px; color: #e8eefc;
  background: rgba(18, 22, 31, .86); border: 1px solid rgba(140, 190, 255, .45);
  box-shadow: 0 3px 12px rgba(0, 0, 0, .4); pointer-events: none;
}
.spec-eye b { color: #8fb7ff; }
.spec-eye-n { margin-left: 6px; color: #9aa3b8; }
/* ---- 宿敌 ×2 角标 ---- */
.rw-box .rw-rival { position: relative; }
.rw-rival-tag {
  position: absolute; top: 6px; right: 8px; font-size: 15px; font-weight: 800;
  color: #f2c14e; text-shadow: 0 0 8px rgba(242, 193, 78, .6);
}
@media (max-width: 560px) {
  .ol-world-log { height: 120px; }
  .fac-news { max-height: 200px; }
  /* 2026-11-10：聊天·交易的两栏在 360px 手机上各只剩 160px，卡名全被挤没 → 窄屏改单列 */
  .ct-trade-cols { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================================
 * 窄屏兜底（2026-11-10）：就算玩家在手机上没开「手机版界面」（📱 按钮），
 * 主菜单也不许两栏并排 —— 390px 宽塞两栏会挤成 190px，名字/徽章全被省略号吃掉。
 * 阈值 720 与对局布局的 @media 一致；这里只改列数，不动别的样式。
 * ⚠ 必须排在 `html.ui-mobile .menu-cols` 之后：同特异性靠后覆盖。
 * ==========================================================================*/
@media (max-width: 720px) {
  html .menu-cols { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  html .opponent-list { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   防沉迷（2026-11-12 玩家要求）
   - #playTimeHud：左上角常驻倒计时，fixed + pointer-events:none，绝不挡按钮
   - #ptNotice：到点告示，盖在所有界面（含对局）之上
   ========================================================================== */
.pt-hud {
  position: fixed;
  left: 8px;
  top: 8px;
  z-index: 900;
  pointer-events: none;
  font-size: 12px;
  line-height: 1;
  padding: 6px 10px;
  border-radius: 999px;
  color: var(--gold);
  background: rgba(10, 14, 20, .72);
  border: 1px solid rgba(217, 180, 91, .5);
  letter-spacing: .5px;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
  transition: color .2s, border-color .2s;
}
.pt-hud[hidden] { display: none; }
.pt-hud.pt-locked {
  color: #ff8f8f;
  border-color: rgba(217, 83, 79, .65);
  background: rgba(40, 12, 12, .82);
}

/* 对局界面：左上角正好是对手头像 + 名字，倒计时会压住它 ——
   给对手血条行让出 74px，HUD 独占左上角，名字也完整可见。 */
#duel .side.opp .pbar { padding-left: 74px; }

.pt-mask {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(5, 8, 12, .82);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity .18s ease;
}
.pt-mask[hidden] { display: none; }
.pt-mask.show { opacity: 1; }

.pt-card {
  width: 100%;
  max-width: 380px;
  text-align: center;
  padding: 28px 24px 22px;
  border-radius: 14px;
  background: linear-gradient(#20293a, #151b26);
  border: 1px solid rgba(217, 180, 91, .38);
  box-shadow: 0 18px 60px rgba(0, 0, 0, .6);
  color: var(--ink);
}
.pt-card .pt-icon {
  font-size: 34px;
  line-height: 1;
  margin-bottom: 12px;
}
.pt-card h2 {
  font-size: 18px;
  margin: 0 0 12px;
  color: var(--gold);
}
.pt-card p {
  font-size: 13px;
  line-height: 1.9;
  color: #c8b48a;
  margin: 0 0 10px;
}
.pt-card p b { color: var(--ink); }
.pt-card .pt-sub {
  font-size: 12px;
  color: #8f9bab;
  margin-bottom: 18px;
}
.pt-card .btn {
  min-width: 120px;
  padding: 9px 18px;
}

/* 手机版：左上角本就窄，缩小一点避免压住对手名字。
   ⚠ 这里**不要**用 720px —— test-mobile-layout-20261110 用 css.lastIndexOf('@media (max-width: 720px)')
      定位「窄屏兜底块」，多一个 720 块会把那条断言指到错误的地方。 */
@media (max-width: 480px) {
  .pt-hud { font-size: 11px; padding: 5px 8px; left: 6px; top: 6px; }
  .pt-card { padding: 24px 18px 18px; }
  .pt-card h2 { font-size: 16px; }
  /* 手机版血条一行更挤：让位收到 58px（头像 20px + 名字 46px 仍在） */
  #duel .side.opp .pbar { padding-left: 58px; }
}

/* ===== 存档搬家（规则说明 → 存档码）2026-11-12 =====
   换链接 = 换域名 = localStorage 隔离，唯一的搬运方式是「一段可复制的文本」。
   这里只负责把它做得能看清、能选中：手机上一行太窄，所以 textarea 给足高度并允许纵向拉伸。 */
.help-transfer {
  margin: -2px 0 12px; font-size: 12px; line-height: 1.6; color: #9fb0c6;
}
.help-transfer .help-link-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: inherit; font-size: 12px; color: var(--gold);
  text-decoration: underline dotted rgba(217,180,91,.5); text-underline-offset: 3px;
}
.help-transfer .help-link-btn:hover { color: #ffe6a6; }
.modal-box .save-tip { font-size: 12.5px; line-height: 1.6; color: #cfe0f0; margin-bottom: 10px; }
.modal-box .save-tip b { color: #fff; }
.modal-box .save-label {
  font-size: 11.5px; color: #9fb0c6; margin: 12px 0 5px; letter-spacing: .3px;
}
.modal-box .save-code-area {
  display: block; width: 100%; height: 74px; resize: vertical;
  font-family: ui-monospace, Menlo, Consolas, "Courier New", monospace;
  font-size: 11px; line-height: 1.45;
  background: #0e141d; color: #d8e4f2;
  border: 1px solid #3a4a63; border-radius: 8px; padding: 8px 9px;
  word-break: break-all; outline: none;
  /* body 上有全局 user-select:none（防误选卡面），textarea 必须显式放开，否则手机上选不中 */
  user-select: text; -webkit-user-select: text;
}
.modal-box .save-code-area:focus { border-color: var(--gold-dim); }
.modal-box .save-row {
  display: flex; align-items: center; gap: 10px; margin-top: 7px; min-height: 26px;
}
.modal-box .save-msg { font-size: 11px; color: #8fd8b0; line-height: 1.4; }
@media (max-width: 480px) {
  .modal-box .save-code-area { height: 66px; font-size: 10px; }
  .modal-box .save-row { flex-wrap: wrap; gap: 6px; }
  .modal-box .save-msg { flex-basis: 100%; }
}

/* ===== 取档助手（从打不开的老链接里取回存档）2026-11-12 第二批 =====
   老链接 403 之后，存档还被锁在老 origin 的 localStorage 里。三条取回路径都写在这个弹窗里，
   文案偏教学，所以行距放大、步骤之间加分隔线；拖拽书签要看得见是「可以拖的东西」。 */
.modal-box .rescue-step {
  border-top: 1px dashed #2b3a4f; padding: 10px 0 4px; margin-top: 8px;
  font-size: 12.5px; line-height: 1.65; color: #cfe0f0;
}
.modal-box .rescue-step:first-of-type { border-top: 0; }
.modal-box .rescue-step > b { color: var(--gold); font-size: 13px; }
.modal-box .rescue-step ol { margin: 6px 0 4px; padding-left: 18px; }
.modal-box .rescue-step li { margin-bottom: 5px; }
.modal-box .rescue-bm {
  display: inline-block; margin-left: 6px; vertical-align: middle;
  cursor: grab; text-decoration: none;
}
.modal-box .rescue-hint { font-size: 11px; color: #8fa3bb; }
.modal-box .rescue-step p { margin: 4px 0; }

/* 取档助手的备用姿势折叠区（2026-11-12 第二批）
   主路径只有「拖书签 → 开老链接 → 点书签」三步，控制台/手机是备用，
   默认收起，免得把弹窗撑到需要滚动、把「恢复存档」挤出视野。 */
.modal-box details.rescue-more {
  border-top: 1px dashed #2b3a4f; padding: 9px 0 2px; margin-top: 8px;
  font-size: 12.5px; line-height: 1.65; color: #cfe0f0;
}
.modal-box details.rescue-more > summary {
  cursor: pointer; color: var(--gold); font-size: 12.5px; outline: none;
  list-style: none; user-select: none;
}
.modal-box details.rescue-more > summary::-webkit-details-marker { display: none; }
.modal-box details.rescue-more > summary:before { content: '▸ '; color: #8fa3bb; }
.modal-box details.rescue-more[open] > summary:before { content: '▾ '; }
.modal-box details.rescue-more p { margin: 8px 0 6px; font-size: 12px; }
.modal-box details.rescue-more code {
  background: #0e141d; border: 1px solid #2b3a4f; border-radius: 4px;
  padding: 1px 4px; font-size: 11px; color: #d8e4f2;
}
.modal-box .rescue-code { height: 62px; }
/* 手机上的取档引导：这一条只有窄屏才渲染（js 里按 innerWidth 判断），
   为了让它在三步之前就被看见，用暖色左竖线而不是又一段灰字。 */
.modal-box .rescue-mob-hint {
  border-left: 2px solid var(--gold-dim); padding: 2px 0 2px 9px; margin: -2px 0 10px;
  font-size: 12px; line-height: 1.6; color: #e8d9b0;
}
/* 窄屏取档提示里的行内代码（Access is denied 那句要能一眼认出是报错原文） */
.modal-box .rescue-mob-hint code {
  background: #0e141d; border: 1px solid #2b3a4f; border-radius: 4px;
  padding: 1px 4px; font-size: 10.5px; color: #ffd9a0;
}
