/* ============================================================
   立直麻将计分 · 全局视觉规范

   设计基调：通透清新的毛玻璃（glassmorphism）
     主色 = 场况青 aqua，与立直盘同源（盘面里本来就有 #3FAEBA），
            但整站走清透路线，所以升格为唯一强调色。
     材质 = 半透明玻璃面 + 背景虚化 + 1px 高光边 + 柔和投影。

   为什么背景必须有颜色：
     毛玻璃的本质是把「身后的东西」糊掉。背景若是纯色，玻璃面无从表现，
     只会变成一块灰。所以 body::before 铺了一层柔和的极光渐变，
     玻璃面才有东西可糊，通透感才成立。

   主题：深色为默认；系统偏好浅色时自动切换（同一套令牌，两种取值）。

   说明：立直盘盘面所需的书法/日式字体已子集化内嵌（fonts/*.woff2），
   不依赖系统字体、也无需联网，离线可用且体积仅 ~10KB。
   ============================================================ */

/* ---------------- 内嵌字体（立直盘专用） ----------------
   霞鹜文楷繁體 Bold：中央「東x局」的楷书（OFL 开源）。
     ⚠️ 必须用繁體字库！马善政楷书等简体向字体只有简体「东」，没有繁体「東」，
        中央面板的「東」会回退成系统字体，与「局」风格不一致（2026-09-13 踩过）。
   思源黑体日文版 Bold：四角风牌字的日式繁体结构（厚重黑体，OFL 开源）。
   均只子集化保留盘面所需字符，体积极小。
   `?v=` 是缓存开关：字体更新后必须 +1，否则手机端会继续用旧的缓存副本。 */
@font-face {
  font-family: "RiiKai";
  src: url("fonts/RiiKai-subset.woff2?v=20260913") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "RiiWind";
  src: url("fonts/SourceHanSansJP-Bold-subset.woff2?v=20260913") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------------- 令牌：深色（默认） ---------------- */

:root {
  color-scheme: dark;

  /* 表面层次：由「薄玻璃」到「厚玻璃」。
     深色主题刻意用「深色烟玻璃」而不是「加白」：
     加白会让玻璃面随身后极光变亮，上面的浅色文字就没了对比度；
     烟玻璃则无论身后多亮，都能把面压回深色，文字始终够 AA，
     同时仍保留约 40% 透光，极光的颜色照样透得出来。 */
  --surface-2: rgba(18, 24, 34, 0.52); /* 内嵌底：输入框、内板 */
  --surface: rgba(18, 23, 33, 0.58); /* 常规面：卡片、面板、行 */
  --surface-3: rgba(24, 30, 42, 0.72); /* 抬起面：悬停、关闭钮 */
  /* 浮动层（雀士下拉菜单）：它悬在**任意内容**之上，是唯一不依附于某个面的玻璃，
     所以比常规抬起面更实一点。用 --surface-3 时，底下的输入框占位文字会透上来，
     列表看起来像混进了两行字（实测深色/浅色两套主题都明显）。 */
  --float-fill: rgba(21, 27, 37, 0.94);

  /* 玻璃的两条边：上边吃光（高光），轮廓线极淡 */
  --glass-edge: rgba(255, 255, 255, 0.15);
  --glass-hi: inset 0 1px 0 rgba(255, 255, 255, 0.10);

  --hairline: rgba(255, 255, 255, 0.10);
  --hairline-strong: rgba(255, 255, 255, 0.20);

  /* 文字：三级灰阶（冷调，配合青色主色） */
  --text: #eaf2f5;
  --text-2: #b7c4cd;
  --text-3: #a2afb9;

  /* 主色：aqua。--brand 既能当按钮底（配 --brand-ink），也能当文字色 */
  --brand: #4fc7d4;
  --brand-2: #37a9b8;
  --brand-ink: #06232a; /* 主色底上的文字（深墨青） */
  --brand-bg: rgba(79, 199, 212, 0.14);
  --brand-tint: rgba(79, 199, 212, 0.17); /* 选中态填充 */
  --brand-line: rgba(79, 199, 212, 0.44); /* 选中态描边 */
  --brand-text: #7fe0ea; /* 选中态文字（在半透明青底上够 AA） */
  --brand-shadow: rgba(79, 199, 212, 0.22);

  --dealer: #d0353f; /* 亲家 / 危险（色块用） */
  --danger-ink: #ff9aa2; /* 危险色当文字用时 */
  --pos: #ff7a82; /* 得分（中式约定：正 = 红） */
  --neg: #5fd3b0; /* 失分（中式约定：负 = 绿） */
  /* 状态胶囊（听 / 不听 之类） */
  --good-bg: rgba(95, 211, 176, 0.16);
  --good-ink: #8ee6c2;
  --bad-bg: rgba(217, 59, 73, 0.18);
  --bad-ink: #ff9aa2;

  /* 形状：统一刻度，全站只用这六档 */
  --r-xs: 8px;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-xl: 24px;
  --r-pill: 999px;

  /* 模糊强度：一个令牌统一控制整站玻璃厚度 */
  --glass-filter: blur(20px) saturate(180%);
  --glass-filter-strong: blur(30px) saturate(190%);

  /* 层次：彩色柔光，不用纯黑硬阴影（纯黑会压死通透感） */
  --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.28), 0 1px 2px rgba(0, 0, 0, 0.22);
  --shadow-md: 0 12px 36px rgba(0, 0, 0, 0.40), 0 3px 10px rgba(0, 0, 0, 0.28);
  --shadow-lg: 0 30px 74px rgba(0, 0, 0, 0.56), 0 10px 26px rgba(0, 0, 0, 0.34);
  --ring: 0 0 0 3px rgba(79, 199, 212, 0.28);

  /* 液态玻璃按钮：偏蓝调（常规操作）与红调（危险操作）两套。
     同一套「半透明渐变 + 顶部高光 + 细边框 + 毛玻璃 + 光晕投影」语言，
     只换色相，保证全站按钮质感一致、语义靠颜色区分。
     半透明（≈0.66~0.72）+ backdrop-filter = 身后极光透过按钮的液态通透感。 */
  --btn-blue-1: rgba(72, 118, 220, 0.66);
  --btn-blue-2: rgba(48, 90, 190, 0.72);
  --btn-blue-ink: #ffffff;
  --btn-blue-glow: rgba(72, 118, 220, 0.30);
  --btn-blue-edge: rgba(255, 255, 255, 0.26);
  --btn-blue-hi: rgba(255, 255, 255, 0.42);

  /* 紧凑胶囊按钮（雀士卡上那类 12px/700 的小按钮）单独一档蓝。
     为什么不能直接复用 --btn-blue-*：那套是给 16px 主按钮调的观感，浅色主题那版
     偏亮 —— 白字压上去实测只有 4.42:1，差一点点不到 4.5（正文级字号不享受
     "大字"放宽）。深色主题同值就够（实测 7.2:1），所以两套主题只在浅色下调深。
     改这里的值必须重跑 _preview/_pc_contrast.py（它是唯一的判据）。 */
  --pill-blue-1: rgba(72, 118, 220, 0.66);
  --pill-blue-2: rgba(48, 90, 190, 0.72);

  --btn-red-1: rgba(210, 68, 82, 0.66);
  --btn-red-2: rgba(178, 44, 56, 0.72);
  --btn-red-ink: #ffffff;
  --btn-red-glow: rgba(210, 68, 82, 0.28);
  --btn-red-edge: rgba(255, 255, 255, 0.26);
  --btn-red-hi: rgba(255, 255, 255, 0.34);

  /* 麻将牌底纹（玩家首页背景）。三层：牌身 / 牌边 / 牌面图案。
     深色主题里牌面靠「提亮」浮现（比身后的极光亮一档），所以用白系低透明度；
     数值刻意很小 —— 底纹是背景，抢戏就输了。改这几个值要看一眼实拍。 */
  --mj-face: rgba(226, 240, 248, 0.055);
  --mj-edge: rgba(226, 240, 248, 0.15);
  --mj-ink: rgba(226, 240, 248, 0.24);

  /* 我的数据面板：进度条填充（跟着主色走，浅色主题会自己解析成深青）与折线节点底色。
     节点必须**不透明**：半透明的话折线穿过圆点时会在圈里透出线头，看着像断了一节。 */
  --md-bar: linear-gradient(90deg, var(--brand), var(--brand-2));
  --md-node: #121a24;

  /* 遮罩 */
  --mask: rgba(6, 14, 18, 0.34);

  --tabbar-h: 72px;
  /* 悬浮底部导航**真实占位**（高度 + 距底偏移 + 呼吸），由 layout.js 用 ResizeObserver
     实测后写在 <html> 的行内样式上。
     ⚠️ 为什么不能只有一个常量：--tabbar-h=72 是人手估的，而实测占位是 96px
     （胶囊高 82 + bottom 14），差 24px ⇒ 页面最后一排按钮被压在导航底下
     （实测「退出房间」被压掉 306×49px、「解散对局」306×24px、「我的数据」排序按钮 127×29px）。
     常量与真实几何之间没有任何机制保证一致 —— 换个字号、改句文案、加个安全区都会让它再次失准。
     所以真正的机制是「谁占空间谁自己报尺寸」，常量只做 JS 未跑/量不到时的兜底。 */
  --tabbar-space: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));

  /* .content 的上下内边距。抽成令牌是因为 .score 的高度必须把它减掉：
     否则 内容高(100dvh - tabbar) + 内边距(28) + tabbar = 100dvh + 29，
     页面凭空多出 29px 可滚动高度，sticky 的 tabbar 就会压住最下面一排按钮
     （实测「解散对局」被压掉 19px）。 */
  --content-pad-t: 18px;
  --content-pad-b: calc(10px + env(safe-area-inset-bottom, 0px));

  /* #root 的桌面端上边距（移动端为 0，768px 断点里改 20px）。
     同样要点名，.score 算高度时得把它减掉。 */
  --root-pad-t: 0px;

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", system-ui, sans-serif;
}

/* ---------------- 令牌：浅色（跟随系统） ---------------- */

@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;

    --surface-2: rgba(255, 255, 255, 0.56);
    --surface: rgba(255, 255, 255, 0.70);
    --surface-3: rgba(255, 255, 255, 0.84);
    --float-fill: rgba(255, 255, 255, 0.97);

    --glass-edge: rgba(255, 255, 255, 0.82);
    --glass-hi: inset 0 1px 0 rgba(255, 255, 255, 0.95);

    --hairline: rgba(84, 122, 148, 0.16);
    --hairline-strong: rgba(84, 122, 148, 0.28);

    --text: #17242e;
    --text-2: #414e59;
    --text-3: #515e6a;

    --brand: #0e7c8c;
    --brand-2: #0a6472;
    --brand-ink: #ffffff;
    --brand-bg: rgba(14, 124, 140, 0.10);
    --brand-tint: rgba(14, 124, 140, 0.13);
    --brand-line: rgba(14, 124, 140, 0.42);
    --brand-text: #0a5f6c;
    --brand-shadow: rgba(14, 124, 140, 0.28);

    --dealer: #c42b38;
    --danger-ink: #b32330;
    --pos: #ad3227;
    --neg: #0d675a;
    --good-bg: rgba(18, 121, 106, 0.11);
    --good-ink: #0e6355;
    --bad-bg: rgba(196, 43, 56, 0.11);
    --bad-ink: #a11f2b;

    --shadow-sm: 0 2px 10px rgba(52, 90, 120, 0.09), 0 1px 2px rgba(52, 90, 120, 0.06);
    --shadow-md: 0 12px 34px rgba(52, 90, 120, 0.14), 0 3px 10px rgba(52, 90, 120, 0.08);
    --shadow-lg: 0 30px 70px rgba(52, 90, 120, 0.22), 0 10px 26px rgba(52, 90, 120, 0.12);
    --ring: 0 0 0 3px rgba(14, 124, 140, 0.22);

    --btn-blue-1: rgba(52, 102, 200, 0.88);
    --btn-blue-2: rgba(40, 80, 170, 0.90);
    --btn-blue-ink: #ffffff;
    --btn-blue-glow: rgba(52, 102, 200, 0.30);
    --btn-blue-edge: rgba(255, 255, 255, 0.32);
    --btn-blue-hi: rgba(255, 255, 255, 0.42);

    /* 小按钮专用蓝（深一档）：同值那版在白卡上白字只有 4.42:1，压深到实测 6.6:1。
       色相不变，所以和全站蓝色仍然是一家。 */
    --pill-blue-1: rgba(38, 78, 168, 0.94);
    --pill-blue-2: rgba(30, 62, 142, 0.96);

    --btn-red-1: rgba(192, 40, 52, 0.88);
    --btn-red-2: rgba(158, 30, 41, 0.90);
    --btn-red-ink: #ffffff;
    --btn-red-glow: rgba(192, 40, 52, 0.28);
    --btn-red-edge: rgba(255, 255, 255, 0.30);
    --btn-red-hi: rgba(255, 255, 255, 0.32);

    /* 浅色主题的牌面要反过来「压深」才看得见：浅色底上再加白等于没画。
       所以这里用深墨青的低透明度，做成一版"刻痕"。 */
    --mj-face: rgba(255, 255, 255, 0.58);
    --mj-edge: rgba(23, 36, 46, 0.10);
    --mj-ink: rgba(23, 36, 46, 0.18);

    /* --md-bar 不用重写：它内部引用的 var(--brand) 会解析成浅色主题这一版深青。 */
    --md-node: #ffffff;

    --mask: rgba(28, 52, 68, 0.26);
  }
}

/* ---------------- 基础 ---------------- */

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* 底色铺在 html 上：它会传播到画布层。
   body 保持透明，body::before 才能把极光画在「内容之下、画布之上」。 */
html {
  height: 100%;
  background: linear-gradient(168deg, #0c1418 0%, #0f1822 48%, #121428 100%);
}

body {
  min-height: 100%;
  font-family: var(--font);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* 极光背景：玻璃面要糊的就是它。
   fixed + z-index:-1 → 滚动时背景不动，玻璃掠过光斑，通透感才活。 */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(58% 42% at 4% -6%, rgba(28, 120, 132, 0.40) 0%, rgba(28, 120, 132, 0) 62%),
    radial-gradient(52% 40% at 104% 4%, rgba(40, 82, 152, 0.36) 0%, rgba(40, 82, 152, 0) 60%),
    radial-gradient(60% 46% at 98% 88%, rgba(84, 62, 148, 0.32) 0%, rgba(84, 62, 148, 0) 62%),
    radial-gradient(56% 44% at 0% 96%, rgba(20, 92, 96, 0.28) 0%, rgba(20, 92, 96, 0) 60%);
}

@media (prefers-color-scheme: light) {
  html {
    background: linear-gradient(168deg, #f1f8fa 0%, #eef3fa 46%, #f5f1fb 100%);
  }
  .score-detail {
    background: linear-gradient(168deg, #f1f8fa 0%, #eef3fa 46%, #f5f1fb 100%);
  }
  body::before {
    background:
      radial-gradient(58% 42% at 6% -4%, rgba(126, 226, 214, 0.52) 0%, rgba(126, 226, 214, 0) 62%),
      radial-gradient(52% 40% at 102% 6%, rgba(140, 196, 250, 0.48) 0%, rgba(140, 196, 250, 0) 60%),
      radial-gradient(60% 46% at 96% 84%, rgba(180, 168, 246, 0.38) 0%, rgba(180, 168, 246, 0) 62%),
      radial-gradient(56% 44% at 2% 92%, rgba(255, 210, 186, 0.33) 0%, rgba(255, 210, 186, 0) 60%),
      radial-gradient(72% 52% at 50% 50%, rgba(255, 255, 255, 0.50) 0%, rgba(255, 255, 255, 0) 70%);
  }
}

button,
input,
select,
textarea {
  font-family: inherit;
}

button {
  -webkit-tap-highlight-color: transparent;
}

::selection {
  background: var(--brand-bg);
  color: var(--text);
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-thumb {
  background: var(--hairline-strong);
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-track {
  background: transparent;
}

/* 全站焦点环：统一青色 */
:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

#root {
  max-width: 460px;
  margin: 0 auto;
  min-height: 100vh;
  min-height: 100dvh;
}

/* ---------------- 玻璃面统一处理 ----------------
   所有「有底面」的元素共用同一套玻璃参数：
   半透明底（令牌给）+ 背景虚化（这里给）+ 1px 高光边。
   单独处理 backdrop-filter 而不是塞进令牌，
   是因为 backdrop-filter 不接受 var() 组合里的逗号简写。 */

.tabbar,
.auth,
.auth-tabs,
.field input,
.field select,
.rule-hint,
textarea,
.grow,
.seat .card,
.badge,
.center,
.tabs2,
.seg,
.sort,
.row,
.profile-head,
.menu,
.form,
.form-row select,
.form-row input[type="number"],
.btn-mini,
.tgl,
.sh-close,
.chip,
.preview,
.btn-cancel,
.sheet,
.live-game,
.md-hero,
.md-progress,
.md-cell,
.md-card,
.stats-head {
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
}

/* ==================== 布局规约（改版式前先读这一段） ====================
 *
 * 这套版式只有三种东西，任何新加的盒子都必须能归到其中一类：
 *
 *   ① 流内底框（卡片 / 面板 / 容器 / 按钮）
 *      一律待在普通流里，兄弟间距由父容器的 gap 给。
 *      ⛔ 不许用 position:absolute 摆内容 —— 那样它就脱离了"后面那个跟着我走"
 *         的联动关系，一旦内容变长（名字换行、图加载完、数字位数+1）就地重叠。
 *
 *   ② 装饰层（底纹、极光、遮罩底色）
 *      允许绝对定位，但必须同时满足 [data-deco] 的三条约束（见下方规则块）。
 *      宿主用 [data-host] 提供定位上下文与层叠隔离 ⇒ 装饰永远在内容之下、永远跟着宿主一起长。
 *
 *   ③ 常驻浮层（底部导航 tabbar、全屏层、底部弹层）
 *      允许 position:fixed。但**必须自己申报占用的空间**：layout.js 用 ResizeObserver
 *      实测它的高度与偏移，写成 --tabbar-space，内容层的底部内边距直接吃这个变量。
 *      ⛔ 不许再往 CSS 里写"导航大概占 72px"这种估算常量 —— 常量与真实几何之间
 *         没有任何机制保证一致，今天 72 对、明天改个字号就是错的。
 *
 * 三条保障机制（都在 layout.js，也在 _preview/_layout_audit.js 里有同一套判据）：
 *   · 实测自量自留 —— 浮层报尺寸，内容按尺寸让位（结构性消除，首选）
 *   · 天际线下推   —— 流内标记了 data-surface 的兄弟若仍相交，按需要的高度下推（兜底）
 *   · 重叠审计     —— window.__layoutReport() 随时可查，工装按它断言"底框两两不相交"
 *
 * 尺寸预留：所有 <img> 必须有确定的盒子（CSS 尺寸或 aspect-ratio）。
 * 未预留的图加载完会把后面所有内容往下推 —— 既是跳动，也是"重叠"的常见来源。
 *
 * ==================================================================== */

/* ---------------- 应用框架 ---------------- */

.app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

.content {
  flex: 1;
  /* 底部留出悬浮 tabbar 的空间，否则内容会被固定底部的导航胶囊遮住。
     ⚠️ 用 --tabbar-space（实测占位）而不是 --tabbar-h（估算常量）—— 见令牌处的说明。
     ⚠️ 这里不再单独加 env(safe-area) —— --tabbar-space 的含义已经是「屏幕底部一切非内容
        的占位」，实测值里含安全区、JS 未跑时的兜底值也含（见 :root），再减一次就是重复扣。 */
  padding: var(--content-pad-t) 16px var(--tabbar-space);
}

/* 次级页（已隐藏底部导航）不再需要为悬浮 tabbar 预留底部空间 */
.app.no-tabbar .content {
  padding-bottom: var(--content-pad-b);
}

/* 增量渲染的挂载点：只要是个「透明的壳」，不参与布局。
   display:contents 让 .score / .start-screen 表现得像 .content 的直系子元素，
   布局与改造前逐像素一致；但挂载点本身在 DOM 里是稳定的，
   内容串没变就能整段跳过 innerHTML 重建（入场动画因此不会重播）。 */
.mount {
  display: contents;
}

.tabbar {
  position: fixed;
  left: 50%;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  backdrop-filter: var(--glass-filter-strong);
  -webkit-backdrop-filter: var(--glass-filter-strong);
  border: 1px solid var(--glass-edge);
  /* 弱化的柔和投影 */
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.16), var(--glass-hi);
}

.tabbar .tab {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 84px;
  min-height: 60px;
  padding: 6px 14px;
  color: var(--text-3);
  border: none;
  background: none;
  cursor: pointer;
  transition: color 0.18s ease, transform 0.18s ease;
}

.tabbar .tab .t-ico {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 38px;
  transition: transform 0.18s ease;
}

.tabbar .tab .t-ico svg {
  height: 38px;
  width: auto;
  display: block;
}

.tabbar .tab:active {
  transform: scale(0.94);
}

.tabbar .tab.on {
  color: var(--brand-text);
}

.tabbar .tab.on .t-ico {
  transform: translateY(-1px) scale(1.06);
}

/* ---------------- 登录注册 ---------------- */

.auth {
  max-width: 330px;
  margin: 44px auto 0;
  padding: 26px 22px 22px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-md), var(--glass-hi);
}

.auth-title {
  text-align: center;
  font-size: 23px;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--text);
  margin-bottom: 20px;
}

.auth-tabs {
  display: flex;
  background: var(--surface-2);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-bottom: 18px;
  padding: 3px;
  gap: 3px;
  box-shadow: var(--glass-hi);
}

.auth-tabs button {
  flex: 1;
  min-height: 40px;
  padding: 10px 0;
  font-size: 15px;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-2);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}

/* 选中态：半透明青色填充，比实心块更「通透」 */
.auth-tabs button.on {
  background: var(--brand-tint);
  color: var(--brand-text);
  border: 1px solid var(--brand-line);
  font-weight: 700;
  box-shadow: var(--glass-hi);
}

.field {
  margin-bottom: 13px;
}

.field label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  margin-bottom: 6px;
}

.field input {
  width: 100%;
  min-height: 46px;
  padding: 11px 13px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  font-size: 15px;
  background: var(--surface-2);
  color: var(--text);
  outline: none;
  box-shadow: var(--glass-hi);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.field input:focus {
  border-color: var(--brand);
  box-shadow: var(--ring), var(--glass-hi);
}

.field input::placeholder {
  color: var(--text-3);
}

/* 音效页「选择雀士」：与 .field input 同规格（大表单控件），只补 chevron 留白 */
.field select {
  width: 100%;
  min-height: 46px;
  padding: 11px 42px 11px 13px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  font-size: 15px;
  background-color: var(--surface-2);
  color: var(--text);
  outline: none;
  box-shadow: var(--glass-hi);
  transition: border-color 0.18s ease, box-shadow 0.18s ease,
    background-color 0.18s ease;
}

.btn-primary {
  width: 100%;
  min-height: 48px;
  padding: 13px;
  background: linear-gradient(180deg, var(--btn-blue-1), var(--btn-blue-2));
  color: var(--btn-blue-ink);
  border: 1px solid var(--btn-blue-edge);
  border-radius: var(--r-md);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.06em;
  cursor: pointer;
  margin-top: 8px;
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
  box-shadow: 0 8px 22px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease,
    border-color 0.15s ease;
}

.btn-primary:hover {
  filter: brightness(1.08);
  border-color: var(--btn-blue-hi);
}

.btn-primary:active {
  transform: translateY(1px) scale(0.99);
  box-shadow: 0 4px 12px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.btn-primary:focus-visible {
  outline: none;
  box-shadow: var(--ring), 0 8px 22px var(--btn-blue-glow);
}

.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  filter: none;
}

/* 返回上一级：统一透明玻璃按钮。半透明玻璃底 + 毛玻璃 + 细边框，
   默认放内容左上角（作为页面第一个元素左对齐）。全站位置/尺寸/图标/交互一致。 */
.back-btn {
  display: flex;
  align-items: center;
  gap: 5px;
  width: fit-content;
  min-height: 40px;
  padding: 0 15px 0 11px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
  box-shadow: var(--glass-hi);
  margin-bottom: 14px;
  transition: border-color 0.16s ease, color 0.16s ease, transform 0.16s ease,
    background 0.16s ease;
}

.back-btn .back-ico {
  font-size: 19px;
  line-height: 1;
  font-weight: 700;
  margin-top: -1px;
}

.back-btn:hover {
  border-color: var(--brand-line);
  color: var(--brand-text);
  background: var(--surface-3);
}

.back-btn:active {
  transform: scale(0.96);
}

.back-btn:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* 玩家计分页的 sticky 头部里：返回按钮不收缩、不额外留底部间距 */
.pg-head .back-btn {
  flex: 0 0 auto;
  margin-bottom: 0;
}

.error {
  color: var(--danger-ink);
  font-size: 13px;
  line-height: 1.5;
  margin-top: 10px;
  text-align: center;
}

/* ---------------- 扫码页 / 房间码面板 ---------------- */

.sub-head {
  font-size: 13px;
  color: var(--text-2);
  margin: 6px 0 12px;
  line-height: 1.6;
}

/* 页面主标题（如玩家首页「我的对局」）：更大更醒目，aqua 渐变文字 + 底部渐变饰线。
   padding-bottom 不再放饰线 —— 改用 ::after 紧跟 padding（与 margin 共享间距区间），
   避免视觉上与下方卡片边框重叠。 */
.page-title {
  font-size: 28px;
  font-weight: 800;
  letter-spacing: 0.03em;
  line-height: 1.2;
  margin: 2px 0 0;          /* 下边距让位给 ::after，装饰与下文之间更"呼吸" */
  background: linear-gradient(120deg, var(--brand), var(--brand-text));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  padding-bottom: 14px;     /* 装饰线 + 下卡片之间至少 14px 的呼吸距离 */
  position: relative;
}

.page-title::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 6px;              /* 落在 padding 中间，离开文字 + 离开下面卡片都更远 */
  width: 44px;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--brand), transparent);
}

/* 紧跟在主标题下方的任何卡片/公告框，都额外再留 14px，
   防止背景层叠造成的「边框重叠」错觉。 */
.page-title + .rule-hint,
.page-title + .pg-note,
.page-title + .menu,
.page-title + .section-title,
.page-title + .list,
.page-title + .placeholder {
  margin-top: 14px;
}

.rule-hint {
  font-size: 13px;
  line-height: 1.65;
  color: var(--text-2);
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  box-shadow: var(--glass-hi);
}

.foot-note {
  color: var(--text-2);
  font-size: 13px;
  line-height: 1.6;
  margin-top: 12px;
}

.auth-note {
  text-align: center;
  font-size: 12px;
  margin-top: 14px;
}

/* ---------------- 空状态 ---------------- */

.placeholder {
  text-align: center;
  color: var(--text-2);
  padding: 56px 16px;
  font-size: 15px;
  line-height: 1.7;
}

/* ---------------- 牌桌主界面 ---------------- */

.score {
  display: flex;
  flex-direction: column;
  /* 必须把 .content 的内边距和 #root 的桌面端上边距一起减掉，否则整页比视口高
     29~49px，sticky 的 tabbar 会把最下面一排按钮压掉一截。
     ⚠️ tabbar 那一项用 --tabbar-space（实测占位），不是 --tabbar-h（估算常量）：
     常量少算了 24px，正好是「荣和/自摸/流局/罚符」四个结算按钮被压掉的高度（实测各 8px）。 */
  height: calc(
    100vh - var(--tabbar-space) - var(--content-pad-t) - var(--root-pad-t)
  );
  height: calc(
    100dvh - var(--tabbar-space) - var(--content-pad-t) - var(--root-pad-t)
  );
  overflow: hidden;
}

/* 盘桌尺寸容器 */
.table {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-lg);
}

.table-area {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  container-type: size;
}

.table-area .table {
  width: min(100cqw, 100cqh);
}

.start-screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 0 32px;
  text-align: center;
}

/* 「创建房间」图片按钮：替换原「开局」文字按钮，交互（data-act=open-setup）不变 */
.btn-create-room {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  /* 缩小到原尺寸的 70% */
  width: min(54.6vw, 210px);
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.15s ease, filter 0.15s ease;
}

.btn-create-room img {
  width: 100%;
  height: auto;
  display: block;
  filter: drop-shadow(0 10px 26px rgba(0, 0, 0, 0.30));
  transition: filter 0.15s ease;
}

.btn-create-room:hover img {
  filter: brightness(1.05) drop-shadow(0 12px 30px rgba(0, 0, 0, 0.36));
}

.btn-create-room:active {
  transform: scale(0.96);
}

.score-line {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-top: 6px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.score-line .pos {
  color: var(--pos);
  font-weight: 700;
}
.score-line .neg {
  color: var(--neg);
  font-weight: 700;
}
.score-line .zero {
  color: var(--text-2);
}

/* 座位与牌面元素（落座大厅沿用；对战视图由 style-battle.css 接管）
   ⚠️ 别看这一整块像是被 style-battle.css 全覆盖了 —— 逐条比对计算样式
   （_preview/_fp_run.js，14 个视图逐元素 80 项属性）后确认：`.badges` / `.badge` /
   `.badge.wind` / `.name-row` / `.card .avatar` / `.name` / `.center` 全都在生效，
   整块删掉会让大厅的文字与徽标回退成浏览器默认（实测 .badge.wind 的
   font-weight 由 800 掉回 400、字号由 13px 掉回默认）。
   真正被 `.battle .seat .card` 逐条重写的只有 `.seat .card` 的几何与底色，
   所以那里只保留没被重写的声明。 */

.seat {
  display: flex;
}

/* 被覆盖的：position / width / height / display / flex-direction / align-items /
   justify-content / gap / padding / background / border-radius / box-shadow
   —— 这 12 条 `.battle .seat .card` 全部重写了（特异性 0,3,0 > 0,2,0）。
   只有 transition 没人重写，留下。
   另外删掉的三条都实测过是死的：
   · `.seat .card:active { transform: scale(0.98) }` —— 卡片的 transform 是**内联**
     `transform:rotate(Ndeg)`（第 561 行），内联样式压过任何非 !important 的作者规则。
     实测：往 `.seat .card` 里写同属性 `transform: scale(0.98)`，计算值仍是被内联写死的
     `matrix(-1, 0, 0, -1, 0, 0)`，纹丝不动 ⇒ 那个 :active 永远不会生效。
   · `.seat .card.dealer` / `.badge.dealer` —— 大厅标记只产出 `class="card"` 与
     `class="badge wind"`，从来没有元素同时拿到 dealer。 */
.seat .card {
  transition: transform 0.16s ease, box-shadow 0.16s ease;
}

.badges {
  display: flex;
  gap: 3px;
}

.badge {
  font-size: 9px;
  padding: 1px 6px;
  border-radius: var(--r-xs);
  background: var(--surface-2);
  color: var(--text-2);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.badge.wind {
  font-size: 13px;
  font-weight: 800;
}

.name-row {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.card .avatar {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--brand), var(--brand-2));
  color: var(--brand-ink);
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.30);
}

.name {
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.center {
  grid-area: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: var(--surface-2);
  color: var(--text);
  border-radius: var(--r-md);
  box-shadow: var(--glass-hi);
}

.center .round {
  font-size: 22px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.03em;
}

.center .meta-line {
  font-size: 13px;
  font-weight: 700;
  opacity: 0.92;
}
.center .sub {
  font-size: 12px;
  opacity: 0.78;
}

/* ---------------- 结算操作条 ---------------- */

.settle-bar {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  padding: 12px 0 16px;
}

.settle-opt {
  min-height: 48px;
  padding: 13px 0;
  font-size: 15px;
  font-weight: 800;
  color: var(--btn-blue-ink);
  background: linear-gradient(180deg, var(--btn-blue-1), var(--btn-blue-2));
  border: 1px solid var(--btn-blue-edge);
  border-radius: var(--r-md);
  cursor: pointer;
  letter-spacing: 0.08em;
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
  box-shadow: 0 6px 18px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease,
    border-color 0.15s ease;
}

.settle-opt:hover {
  filter: brightness(1.08);
  border-color: var(--btn-blue-hi);
}

.settle-opt:active {
  transform: translateY(1px);
  box-shadow: 0 3px 10px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.settle-opt:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* ---------------- 立直盘全屏（仅管理员） ---------------- */

/* 盘面下方右对齐的「全屏显示」入口（不占盘面、不挡风牌角） */
.board-toolbar {
  display: flex;
  justify-content: flex-end;
  padding: 0 0 6px;
}

/* 全屏覆盖层：position:fixed 铺满视口（不用 HTML Fullscreen API，因此 Esc/返回键
   都无法退出），只保留盘面 + 一个「退出全屏」按钮。 */
.board-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  background: #0e1016;
}

.bfs-stage {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;  /* 真正的垂直居中 */
  justify-content: center;
  container-type: size;
  padding: 12px;
}

.bfs-stage .table {
  width: min(100cqw, 100cqh);
  height: min(100cqw, 100cqh);
  margin: auto;  /* 双保险：align+margin */
}

/* 放大（全屏）状态：底托由白色透明自动切换为黑色透明，仍无边框、居中 */
.board-fullscreen .table.board-table {
  background: rgba(0, 0, 0, 0.32);
}

.bfs-bar {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  padding: 4px 16px calc(16px + env(safe-area-inset-bottom, 0px));
}

.bfs-exit {
  min-height: 44px;
  padding: 10px 28px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
  background: var(--surface-3);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  cursor: pointer;
  box-shadow: var(--glass-hi), 0 6px 18px rgba(0, 0, 0, 0.32);
  transition: transform 0.15s ease, border-color 0.15s ease;
}

.bfs-exit:hover {
  border-color: var(--brand-line);
}

.bfs-exit:active {
  transform: translateY(1px);
}

/* ---------------- 底部弹层 ---------------- */

.sheet-mask {
  position: fixed;
  inset: 0;
  background: var(--mask);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 50;
  padding: 0;
  animation: maskIn 0.2s ease;
}

@media (min-width: 640px) {
  .sheet-mask {
    align-items: center;
    padding: 24px;
  }
}

.sheet {
  width: 100%;
  max-width: 460px;
  max-height: 88dvh;
  background: var(--surface);
  backdrop-filter: var(--glass-filter-strong);
  -webkit-backdrop-filter: var(--glass-filter-strong);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 10px 18px calc(18px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: var(--shadow-lg), var(--glass-hi);
  animation: sheetIn 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (min-width: 640px) {
  .sheet {
    border-radius: var(--r-xl);
  }
}

.sheet::before {
  content: "";
  display: block;
  width: 42px;
  height: 4px;
  margin: 4px auto 10px;
  border-radius: var(--r-pill);
  background: var(--hairline-strong);
}

@keyframes maskIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes sheetIn {
  from {
    transform: translateY(24px);
    opacity: 0.6;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* 同一种弹层的后续刷新（busy 翻转 / 报错文案变化）不再重播入场动画：
   每次刷新都重播 maskIn 会在手机上表现为整屏闪一下。 */
.sheet-mask.no-anim {
  animation: none;
}

.sheet-mask.no-anim .sheet {
  animation: none;
}

/* 弹层内部的「次级玻璃」不再二次模糊。
   番符胶囊 / 点数预览 / 提示卡 / 取消·确认按钮本身就压在一层毛玻璃（.sheet）上，
   再模糊一次肉眼几乎分辨不出，却要为每一层多付一次背景重采样 ——
   弹层里这类元素有十几个，嵌套 backdrop-filter 正是点击时掉帧的主因。
   ⚠️ 名单必须**成套**：只砍一部分会让同为 --surface-2 的卡片渲染出深浅两块
   （实测 .preview 230 / .rule-hint 247，接缝处一条 17 级的亮度台阶，像两张卡叠在一起）。
   只砍弹层内部，遮罩 (.sheet-mask) 与面板 (.sheet) 的观感保持不变。 */
.sheet .chip,
.sheet .preview,
.sheet .rule-hint,
.sheet .btn-cancel,
.sheet .sh-close,
.sheet .tgl,
.sheet .btn-mini {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* 弹层里的**表单控件**也必须抹掉 backdrop-filter —— 这里的理由不是性能，是层叠顺序：
   Chromium 会把带 backdrop-filter 的元素提升成独立合成层，绘制时**盖过**
   position:absolute + z-index 的下拉菜单。实测（_preview/_ac_check.html，492×761）：
   规则网格里的 select / number 输入框整片压住 .ac-menu，7 项里有 4 项点不到、
   命中的是 select；把菜单 z-index 从 5 提到 80 **反而更糟**（5 项被挡），
   因为 menu 的 z 只在 .ac 的层叠上下文里排序，跟合成层不是同一套序。
   清零后遮挡归 0。所以：弹层内凡是可能和下拉重叠的控件，一律不许起合成层。
   ⚠️ 必须点名到源规则的同一层级：上面的玻璃名单里写的是
   `.form-row input[type="number"]`（特异性 0,2,1），只用 `.sheet input`（0,1,1）
   压不住它 —— 实测改了 select 却漏了 number 输入框。 */
.sheet .form-row select,
.sheet .form-row input[type="number"],
.sheet .seat-controls input,
.sheet select,
.sheet input[type],
.sheet textarea {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.sh-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.sh-title {
  font-size: 17px;
  font-weight: 800;
  letter-spacing: 0.01em;
}

.sh-close {
  width: 32px;
  height: 32px;
  border: 1px solid var(--glass-edge);
  border-radius: 50%;
  background: var(--surface-2);
  font-size: 15px;
  color: var(--text-2);
  cursor: pointer;
  box-shadow: var(--glass-hi);
  transition: all 0.16s ease;
}

.sh-close:hover {
  background: var(--surface-3);
  color: var(--text);
}
.sh-close:active {
  transform: scale(0.92);
}

.sec-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-2);
  margin: 12px 0 7px;
  letter-spacing: 0.02em;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.chip {
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  border: 1.5px solid var(--hairline-strong);
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  transition: all 0.16s ease;
}

.chip:active {
  transform: scale(0.96);
}

.chip.on {
  background: var(--brand-tint);
  color: var(--brand-text);
  border-color: var(--brand-line);
  font-weight: 700;
}

.chip.t {
  background: var(--good-bg);
  color: var(--good-ink);
  border-color: var(--good-ink);
}

.chip.n {
  background: var(--bad-bg);
  color: var(--bad-ink);
  border-color: var(--bad-ink);
}

.preview {
  margin-top: 12px;
  padding: 11px 13px;
  background: var(--surface-2);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  box-shadow: var(--glass-hi);
  font-size: 13px;
  line-height: 1.65;
  font-variant-numeric: tabular-nums;
}

/* 两张同色卡片上下相邻时（数据预览 + 提示）不能贴合：
   .preview 只给了 margin-top、.rule-hint 完全没有外边距，两者直接共边 ——
   实测 .preview 底 331 / .rule-hint 顶 329，等于负 1px 重叠，
   圆角处露缺口、中间连成一条重边线，看起来就是「两张卡叠在一起」。 */
.preview + .rule-hint {
  margin-top: 10px;
}

.sh-foot {
  display: flex;
  gap: 10px;
  margin-top: 16px;
  padding-bottom: 2px;
}

.sh-foot button {
  flex: 1;
  min-height: 48px;
  padding: 13px;
  font-size: 15px;
  border-radius: var(--r-md);
  cursor: pointer;
  font-weight: 700;
  transition: all 0.16s ease;
}

.sh-foot button:active {
  transform: translateY(1px);
}

.btn-cancel {
  background: linear-gradient(180deg, var(--btn-blue-1), var(--btn-blue-2));
  color: var(--btn-blue-ink);
  border: 1px solid var(--btn-blue-edge);
  box-shadow: 0 6px 18px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.btn-cancel:hover {
  filter: brightness(1.07);
  border-color: var(--btn-blue-hi);
}

.btn-cancel:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  filter: none;
}

.btn-ok {
  background: linear-gradient(180deg, var(--btn-blue-1), var(--btn-blue-2));
  color: var(--btn-blue-ink);
  border: 1px solid var(--btn-blue-edge);
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
  box-shadow: 0 6px 18px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.btn-ok:hover {
  filter: brightness(1.08);
  border-color: var(--btn-blue-hi);
}

.btn-ok:disabled {
  opacity: 0.42;
  cursor: not-allowed;
  box-shadow: none;
  filter: none;
  transform: none;
}

/* 二次确认弹层里的「确认解散」：这一层里它才是主操作，用红调液态玻璃（与蓝调同款质感）。 */
.btn-ok.danger {
  background: linear-gradient(180deg, var(--btn-red-1), var(--btn-red-2));
  color: var(--btn-red-ink);
  border: 1px solid var(--btn-red-edge);
  box-shadow: 0 6px 18px var(--btn-red-glow), inset 0 1px 0 var(--btn-red-hi);
}

.btn-ok.danger:hover {
  filter: brightness(1.07);
}

.save-bar {
  margin-top: 4px;
}

/* 三个大按钮并排（终局=蓝 / 分数记录=白 / 解散=红）：
   等宽等高同层级，小屏优先、极窄屏退化为两行（终局+分数记录 / 解散）。
   gap 与 margin-top 对齐单列时代 .btn-primary/.btn-danger 的节奏。 */
.save-bar-row {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

.save-bar-row > button {
  flex: 1 1 0;
  min-width: 0;
  margin-top: 0; /* 并排时不再吃单列按钮的 margin-top */
}

@media (max-width: 360px) {
  .save-bar-row {
    flex-wrap: wrap;
  }
  .save-bar-row > button {
    flex: 1 1 calc(50% - 4px);
  }
}

/* 白调大按钮（分数记录入口）：与蓝/红同款液态玻璃质感，中性白底 ——
   全站按钮只有蓝（主操作）/红（破坏性）/白（查看类）三类，这是第三类。 */
.btn-plain {
  min-height: 48px;
  padding: 13px 8px;
  background: var(--surface-3);
  color: var(--text);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
  box-shadow: var(--shadow-sm), var(--glass-hi);
  transition: transform 0.15s ease, border-color 0.15s ease, filter 0.15s ease;
}

.btn-plain:hover {
  filter: brightness(1.07);
  border-color: var(--brand-line);
}

.btn-plain:active {
  transform: translateY(1px) scale(0.99);
}

.btn-plain:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* ---------------- 分数记录（详情页） ---------------- */
/* 第八轮起盘面不再外嵌分数列表，样式只服务独立详情页 .sd-row 内的明细行：
   .sl-delta = 得分/失分明细（涨红跌绿），.sl-cum = 该手后累计分。
   模块本体（.score-log/.sl-row/.sl-seq 等）已随外嵌列表一起删除。 */
.sl-delta {
  margin-top: 4px;
  font-size: 12px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}

.sl-delta i {
  font-style: normal;
  margin: 0 5px;
  opacity: 0.5;
}

.sl-delta b.up {
  color: var(--pos);
  font-weight: 700;
}

.sl-delta b.down {
  color: var(--neg);
  font-weight: 700;
}

.sl-empty {
  opacity: 0.6;
}

/* 详情页空态（一手都没结算过时）：居中弱化提示 */
.score-log-empty {
  font-size: 13px;
  color: var(--text-2);
  text-align: center;
  padding: 8px 0 2px;
}

.sl-cum {
  margin-top: 3px;
  font-size: 12px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}

.sl-cum i {
  font-style: normal;
  margin: 0 5px;
  opacity: 0.5;
}

.sl-cum b {
  color: var(--text);
  font-weight: 600;
}

/* ---------------- 分数记录详情页（全屏覆盖层） ---------------- */
/* 与「立直盘全屏」同款铺满视口，但背景跟随主题（深浅色各一套），
   只保留返回按钮 + 逐手场次/本场/得失分/累计分列表。 */
.score-detail {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  background: linear-gradient(168deg, #0c1418 0%, #0f1822 48%, #121428 100%);
}

.sd-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--glass-edge);
  background: var(--surface);
}

.sd-title {
  font-size: 16px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: 0.02em;
}

.sd-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 14px calc(16px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.sd-row {
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  box-shadow: var(--glass-hi);
}

.sd-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}

.sd-round {
  flex: none;
  font-size: 13px;
  font-weight: 800;
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}

.sd-honba {
  flex: none;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-text);
  background: var(--brand-bg);
  border-radius: var(--r-pill);
  padding: 1px 9px;
  font-variant-numeric: tabular-nums;
}

.sd-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}

.save-msg {
  text-align: center;
  color: var(--text-2);
  font-size: 13px;
  margin-top: 8px;
}

/* 破坏性操作（解散对局）：红调液态玻璃，与蓝调按钮同款质感、靠红色提示不可逆。 */
.btn-danger {
  display: block;
  width: 100%;
  margin-top: 10px;
  padding: 13px;
  font-size: 15px;
  font-weight: 700;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--btn-red-1), var(--btn-red-2));
  color: var(--btn-red-ink);
  border: 1px solid var(--btn-red-edge);
  backdrop-filter: var(--glass-filter);
  -webkit-backdrop-filter: var(--glass-filter);
  box-shadow: 0 6px 18px var(--btn-red-glow), inset 0 1px 0 var(--btn-red-hi);
  cursor: pointer;
  transition: all 0.16s ease;
}

.btn-danger:hover {
  filter: brightness(1.07);
  border-color: var(--btn-red-hi);
}

.btn-danger:active {
  transform: translateY(1px) scale(0.99);
  box-shadow: 0 3px 10px var(--btn-red-glow), inset 0 1px 0 var(--btn-red-hi);
}

.btn-danger:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  filter: none;
  transform: none;
}

/* ---------------- 开局设置 ---------------- */

.section-title {
  font-size: 13px;
  font-weight: 800;
  color: var(--text-2);
  margin: 6px 0 9px;
  letter-spacing: 0.03em;
}

/* ---------------- 数据统计 ---------------- */

.stats-head {
  position: sticky;
  top: 0;
  z-index: 10;
  background: linear-gradient(180deg, var(--surface-3) 68%, transparent);
  backdrop-filter: var(--glass-filter-strong);
  -webkit-backdrop-filter: var(--glass-filter-strong);
  margin: -18px -16px 12px;
  padding: 16px 16px 4px;
}

.tabs2 {
  display: flex;
  background: var(--surface-2);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  overflow: hidden;
  margin-bottom: 12px;
  padding: 3px;
  gap: 3px;
  box-shadow: var(--glass-hi);
}

.tab2 {
  flex: 1;
  min-height: 40px;
  padding: 10px 0;
  font-size: 14px;
  font-weight: 600;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-2);
  cursor: pointer;
  transition: all 0.18s ease;
}

.tab2.on {
  background: var(--brand-tint);
  color: var(--brand-text);
  border: 1px solid var(--brand-line);
  font-weight: 700;
}

.segment {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.seg {
  flex: 1;
  min-height: 40px;
  padding: 9px 0;
  font-size: 14px;
  font-weight: 600;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-2);
  cursor: pointer;
  box-shadow: var(--glass-hi);
  transition: all 0.18s ease;
}

.seg.on {
  background: var(--brand-tint);
  color: var(--brand-text);
  border-color: var(--brand-line);
  font-weight: 700;
}

.sort-row {
  display: flex;
  flex-wrap: wrap;      /* 文案变长/容器变窄时换行，而不是把第三个按钮挤出容器 */
  gap: 8px;
  margin-bottom: 12px;
}

.sort {
  flex: 1;
  min-height: 34px;
  padding: 7px 0;
  font-size: 13px;
  font-weight: 600;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-2);
  cursor: pointer;
  box-shadow: var(--glass-hi);
  transition: all 0.18s ease;
}

.sort.on {
  background: var(--brand-tint);
  color: var(--brand-text);
  border-color: var(--brand-line);
  font-weight: 700;
}

.list {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.row {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  padding: 11px 13px;
  box-shadow: var(--shadow-sm), var(--glass-hi);
  cursor: pointer;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}

.row:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md), var(--glass-hi);
  border-color: var(--hairline-strong);
}

.row:active {
  transform: translateY(0) scale(0.99);
}

.row.disabled {
  opacity: 0.45;
  pointer-events: none;
}

.rank-num {
  width: 22px;
  text-align: center;
  font-size: 16px;
  font-weight: 800;
  color: var(--text-2);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--brand), var(--brand-2));
  color: var(--brand-ink);
  font-size: 15px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.30), var(--shadow-sm);
}

.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 可上传头像（雀士管理里「我的雀士」的头像）：hover 放大 + 右下角「＋」角标 */
.avatar-upload {
  cursor: pointer;
  position: relative;
  transition: transform 0.16s ease, box-shadow 0.16s ease;
}

.avatar-upload:hover {
  transform: scale(1.1);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 2px var(--brand-line),
    var(--shadow-sm);
}

.avatar-upload::after {
  content: "＋";
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--brand);
  color: var(--brand-ink);
  font-size: 12px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.32);
}

/* 已上传头像（有 <img>）时不再显示「＋」角标 —— 否则头像右下角会叠一个
   蓝绿色加号，看起来像坏了。无头像（首字母占位）时才提示「可上传」。 */
.avatar-upload:has(img)::after {
  display: none;
}

.row-body {
  flex: 1;
  min-width: 0;
}

.row-name {
  font-size: 15px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.row-sub {
  font-size: 12px;
  color: var(--text-2);
  margin-top: 2px;
  line-height: 1.45;
}

.row-val {
  font-size: 16px;
  font-weight: 800;
  color: var(--brand);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

/* ---------------- 雀士卡片（雀士管理） ----------------
   横排滚动的角色卡：一张卡 = 插图 + 名称 + 状态 + 两个开关（绑定 / 出战）。
   为什么横排而不是竖排：插图是竖构图（立绘 264×380），竖排列表里只能压成小圆
   头像，看不出是哪位雀士；横排卡片能把封面完整摆出来，一屏还能露出下一张的边，
   滚动是自明的（所以隐藏滚动条也不影响可发现性）。

   颜色只表达状态，不做座位式装饰：
     · 「出战中」徽标 / 出战开关的选中态 → 全站唯一选中色 aqua（--brand 系）
     · 「立直音已配置」小圆点 → 同为 aqua（它就是内容已就位的信号）
     · 其余一律中性玻璃面，未出战的卡不做灰化降级。 */

.pcard-row {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  overflow-y: hidden; /* 卡片的投影会被算进滚动高度，不锁死会多出一条纵向滚动条 */
  padding: 4px 2px 12px;
  margin: 0 -2px -6px;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* 触屏上这条横条会盖住卡片下沿，靠右侧露出的卡片边缘提示可滚动 */
}

.pcard-row::-webkit-scrollbar {
  display: none;
}

.pcard {
  flex: 0 0 150px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm), var(--glass-hi);
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}

.pcard:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md), var(--glass-hi);
}

.pcard:active {
  transform: translateY(0) scale(0.99);
}

/* 「出战」是账号级的选中态：沿用全站唯一的选中色（brand aqua），
   不再引入第二套强调色；未出战的卡片保持默认外观，不做灰色降级。 */
.pcard.is-active {
  border-color: var(--brand-line);
  box-shadow: 0 0 0 1px var(--brand-line), var(--shadow-sm), var(--glass-hi);
}

.pcard-art {
  position: relative;
  /* 与 yijiSP_shop.png 同比例：换图不裁构图、不出黑边 */
  aspect-ratio: 264 / 380;
  background: linear-gradient(165deg, var(--brand-bg), var(--surface-3));
  overflow: hidden;
}

.pcard-art img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* 无插图（或无图加载失败被移除）时的占位：一个大首字母。
   始终渲染在 <img> 之下，图一挂掉它自然露出来，不用 JS 换节点。 */
.pcard-initial {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 34px;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--text-2);
}

/* 名字行：小圆头像在**名字前面**，两者垂直居中。
   头像槽位每张卡都有（见 app.js 的说明），所以名字的左边界恒等于
   「卡片内边距 + 头像宽 + 间隙」—— 一行卡片里的名字天然对齐。 */
.pcard-head {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

/* 卡片上的小圆头像：34px，排在名字前面。
   ⚠️ 它原先靠 left/bottom **绝对定位**贴在插图左下角；现在改走文档流，那两个偏移
   必须一并删掉。但**不能顺手写 `position: static`** —— 可上传的那几张带
   `.avatar-upload`，它的「＋」角标是 `::after { position: absolute; right: -2px }`，
   靠 `.avatar-upload{position:relative}` 当定位祖先。这里写 static 特异性更高（0,2,0），
   会把 relative 压掉 ⇒ 角标改以视口为参照，飞到页面右缘外，页面凭空多出 2px 横向溢出。
   （实测：写 static 时 documentElement.scrollWidth 恰好比 clientWidth 大 2px。）
   所以只声明尺寸，定位交给 `.avatar-upload` 自己管。 */
.avatar.pcard-avatar {
  width: 34px;
  height: 34px;
  font-size: 14px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35), var(--shadow-sm);
}

.avatar.pcard-avatar::after {
  width: 15px;
  height: 15px;
  font-size: 11px;
  line-height: 15px;
}

.pcard-info {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 9px 10px 10px;
}

.pcard-name {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  /* 在 .pcard-head 里占满头像右边的剩余宽度，超长名字才轮得到省略号
     （不写这条，flex 项默认按内容撑开，会把整行顶宽、把卡片撑变形）。 */
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pcard-flag {
  flex: none;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 700;
  border-radius: var(--r-pill);
  background: var(--brand-tint);
  border: 1px solid var(--brand-line);
  color: var(--brand-text);
}

.pcard-meta {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-3);
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pcard-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--hairline-strong);
}

.pcard-dot.on {
  background: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-bg);
}

/* ---------------- 卡片内「立直音」控件 ----------------
   一行三段，高度与下面那排 `.tgl.pcard-tgl` 完全一致（30px）。
   ⛔ 为什么高度必须锁死：卡片行是 `overflow-y: hidden` 的横排滚动容器
      （为的是不让卡片投影顶出一条纵向滚动条），运行期一旦长高就会被裁掉一截。
      所以这里四种状态（未配置 / 已配置 / 录音中 / 上传中）**都是同一行、同一高度**，
      只换内容不换行数 —— 录音态也不许比别的状态高哪怕 1px。

   配色沿用全站语法，不引入第二套：
     · 主操作（上传立直音 / 保存）→ 蓝实心，与雀士卡上的「绑定 / 出战」同一副皮
     · 次级操作（录音 / 重传）→ 中性玻璃底 + 细描边（有壳=能点，但语气弱一级）
     · 试听中 → aqua（全站唯一的选中色），因为它表达的是"正在播放这个选中项"
     · 删除 → 只把图标染危险色（一颗方块红按钮在 150px 卡面上太吵） */
.paudio {
  display: flex;
  align-items: center;
  gap: 5px;
  min-height: 30px;
}

.paudio-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 30px;
  min-width: 0;
  padding: 0 7px;
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-2);
  background: var(--surface-3);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: filter 0.16s ease, background 0.16s ease, border-color 0.16s ease,
    color 0.16s ease, transform 0.16s ease;
}

/* 「占满剩余宽度」的修饰类。
   ⛔ 千万不要叫 `.grow` —— 全站已有一个 `.grow`（表单输入框用，`min-height: 42px`、
      `padding: 9px 12px`、`font-size: 14px`），而它排在**本节之后**。
      两个单类选择器特异性相同时按源码顺序决出胜负 ⇒ 那个 `.grow` 会把这里的按钮
      撑成 42px 高、字号也变 14px，整行比别的卡片高出一截（实测真踩过：30 → 42px）。
      （同一个坑在 `.rec-btn` 上踩过一次，当时是改成 `.btn-mini.rec-btn`；这里改用
      与全局零冲突的 `is-wide`。） */
.paudio-btn.is-wide {
  flex: 1 1 auto;
}

/* 纯图标按钮：正方形，宽度与高度对齐，三段式里两边各一个 */
.paudio-btn.is-icon {
  flex: none;
  width: 30px;
  padding: 0;
}

.paudio-btn svg {
  display: block;
  width: 12px;
  height: 12px;
}

.paudio-btn:hover {
  filter: brightness(1.08);
  border-color: var(--brand-line);
  color: var(--brand-text);
}

.paudio-btn:active {
  transform: translateY(1px) scale(0.98);
}

.paudio-btn:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* 主操作：蓝实心 */
.paudio-btn.is-primary {
  background: linear-gradient(180deg, var(--pill-blue-1), var(--pill-blue-2));
  color: var(--btn-blue-ink);
  border-color: var(--btn-blue-edge);
  box-shadow: 0 4px 12px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.paudio-btn.is-primary:hover {
  color: var(--btn-blue-ink);
  border-color: var(--btn-blue-hi);
}

/* 试听进行中：点亮成 aqua —— 全站只有它表达"当前选中" */
.paudio-btn.is-on {
  color: var(--brand-text);
  background: var(--brand-tint);
  border-color: var(--brand-line);
}

/* 删除：只染图标，不给红底 */
.paudio-btn.is-del {
  color: var(--danger-ink);
}

.paudio-btn.is-del:hover {
  color: var(--danger-ink);
  border-color: var(--danger-ink);
}

/* 上传中 / 录音中 / 内置只读：同一行里的文字态，同样 30px */
.paudio-hint {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-size: 11px;
  color: var(--text-3);
}

.paudio.is-rec {
  justify-content: space-between;
}

.paudio.is-rec .paudio-btn {
  flex: none;
}

.paudio.is-rec .paudio-hint b {
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.paudio.is-rec .rec-dot {
  width: 7px;
  height: 7px;
}

/* 内置雀士：一行灰字 + 小锁。给一句解释，而不是让入口凭空消失 —— 否则用户会以为是坏了。 */
.paudio.is-locked {
  gap: 4px;
  font-size: 11px;
  color: var(--text-3);
}

.paudio-lock {
  display: inline-flex;
  flex: none;
  width: 11px;
  height: 11px;
}

.paudio-lock svg {
  display: block;
  width: 11px;
  height: 11px;
}

/* ---------------- 「立直BGM」页（个人中心子页） ----------------

   立直音搬进雀士卡之后，这一节只剩 bgm 一段的样式（旧名「雀士立直音效页」）。
   页面有两种「横向 flex」容易翻车，所以一律改成纵向堆叠：
   行内 flex 里若混进一个 width:100% 的子元素（播放器就是），
   同排的文字会被挤到 0 宽、变成一字一行。 */

.audio-card {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 12px 13px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm), var(--glass-hi);
}

.audio-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.audio-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 「已上传」小胶囊：用正向色，代替原来那句红字确认 */
.audio-tag {
  flex: none;
  padding: 2px 9px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  border-radius: var(--r-pill);
  background: var(--good-bg);
  color: var(--good-ink);
  border: 1px solid var(--good-ink);
}

.audio-sub {
  font-size: 12px;
  color: var(--text-2);
  line-height: 1.45;
}

.audio-player {
  display: block;
  width: 100%;
  height: 40px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
}

.audio-empty {
  padding: 18px 14px;
  text-align: center;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--r-md);
}

/* 原生 file 控件太「浏览器」，只重绘它的按钮部分 */
.file-input {
  width: 100%;
  font-size: 13px;
  color: var(--text-2);
}

.file-input::file-selector-button {
  margin-right: 10px;
  min-height: 36px;
  padding: 7px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  box-shadow: var(--glass-hi);
  cursor: pointer;
  transition: all 0.16s ease;
}

.file-input::file-selector-button:hover {
  border-color: var(--brand);
  color: var(--brand-text);
}

/* 录音：一个红点 + 文案。录音中红点呼吸，给「正在录」一个不动脑的信号
   （.rec-dot / @keyframes rec-pulse 在下面的「表单」节里，两处共用一份） */
/* 用 .btn-mini.rec-btn 而不是 .rec-btn：本节排在「表单」节之前，
   单类选择器会被后面的 .btn-mini 按源码顺序压掉 min-height。 */
.btn-mini.rec-btn {
  width: 100%;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 14px;
}

.rec-card {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 13px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm), var(--glass-hi);
}

.rec-body {
  flex: 1;
  min-width: 0;
}

.rec-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
}

.rec-time {
  margin-top: 2px;
  font-size: 12px;
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.pcard-tgls {
  display: flex;
  gap: 6px;
}

/* 复用全站开关组件 .tgl（同一套选中色与胶囊形），只在这里收尺寸。
   必须写成 .tgl.pcard-tgl：`.tgl` 在文件后面，单类同特异性会被它反压。 */
.tgl.pcard-tgl {
  flex: 1 1 0;
  min-width: 0;
  min-height: 30px;
  padding: 5px 6px;
  font-size: 12px;
}

.tgl.pcard-tgl:disabled {
  /* 与全站禁用态一致（.btn-mini:disabled 也是 0.5）；禁用控件不适用 4.5:1 对比度要求，
     但也不能淡到看不出这里有个开关 —— 它是「先绑定才能出战」的唯一提示。 */
  opacity: 0.5;
  cursor: not-allowed;
}

/* 按钮按「能不能点」分三副皮（2026-09-16 两轮要求叠加）：
     可点态（绑定 / 出战）    → **蓝实心按钮**（全站「蓝＝常规操作」那副皮）
     达成态（已绑定 / 出战中）→ 底色与外框**全部去掉**，只留文字
     锁定态（未解锁）        → 虚线灰框
   壳就是「这儿能点」的信号，所以壳必须一眼看得见 —— 用**实心蓝**而不是淡玻璃底：
   卡片自己就是半透明毛玻璃，淡玻璃底叠上去几乎与卡面同色，「有壳」等于没说
   （改版前的症状正是可点按钮跟旁边的状态文字一样平，用户看不出哪个能点）。
   实心蓝 + 白字把「可点」与卡面拉开一个量级；而且每张卡**最多只有一个**可点按钮
   （绑定位 / 出战位互斥：自己那张是「已绑定·平 + 出战·蓝」，别人的那张是
   「绑定·蓝 + 出战·禁用」，锁定卡两个都不可点）⇒ 一行卡里蓝按钮不会糊成一片。
   ⚠️ 用 `:not(:disabled)` 而不是另加一个类名：可点态是「无额外类」的默认分支，
      再造个 `.is-pick` 就得在 app.js 里多维护一个状态位，只会和 disabled 打架。
   ⚠️ **还必须排掉 `.is-done`**：那个按钮为了支持「再点一次幂等重设」**没挂 disabled**，
      只用 `:not(:disabled)` 会把它一起染成蓝色，达成态当场破功。加上 `:not(.is-done)`
      之后本规则特异性升到 (0,4,0)，无论源码顺序都压得住 `.is-done`(0,3,0) —— 不靠"我排在它前面"。
   ⚠️ 对应地，下面 `.tgl` 的通用悬停/按下反馈写了 `:not(.pcard-tgl)`：雀士卡按钮自带三副皮，
      通用反馈一旦落到 `.is-done` 上，悬停就会给它冒出一圈描边，「无壳」在悬停时破功。 */
.tgl.pcard-tgl:not(:disabled):not(.is-done) {
  background: linear-gradient(180deg, var(--pill-blue-1), var(--pill-blue-2));
  color: var(--btn-blue-ink);
  border: 1px solid var(--btn-blue-edge);
  font-weight: 700;
  box-shadow: 0 4px 12px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

/* 悬停 / 按下 / 键盘焦点三档反馈（`.tgl` 的 transition 已是 all .18s，不必另加）。
   只写交互态 ⇒ **静止时的计算样式一字不变**，静态渲染与无鼠标环境完全不受影响。 */
.tgl.pcard-tgl:not(:disabled):not(.is-done):hover {
  filter: brightness(1.08);
  border-color: var(--btn-blue-hi);
}

.tgl.pcard-tgl:not(:disabled):not(.is-done):active {
  transform: translateY(1px) scale(0.99);
  filter: brightness(0.96);
  box-shadow: 0 2px 8px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.tgl.pcard-tgl:not(:disabled):not(.is-done):focus-visible {
  outline: none;
  box-shadow: var(--ring), 0 4px 12px var(--btn-blue-glow);
}

/* `.is-done`（出战中）虽然技术上可点（幂等重设），对用户就是一次空操作 —— 所以
   **四种交互态一律不给壳、不给反馈**：它是个状态标签，不是操作入口。
   （真要点它也没错，行为不变；只是不该长成按钮的样子去招人点。） */

/* 两个无壳的达成态：
   ⚠️ 无壳态必须连 `box-shadow` 一起清掉：`.tgl` 上的 `--glass-hi` 是一道高光，
      留着它视觉上就是一圈细边，「无边框」会名不副实。
   ⚠️ 无壳态是**不可点**的，所以还得压掉 `.tgl.pcard-tgl:disabled` 的 .5 透明度 ——
      否则「已绑定」会淡成灰色，被读成「这里坏了」。
   ⚠️ 无壳 ≠ 没有对比度要求：文字仍是正文级信息，取色的下限是卡片面上 4.5:1。
   ⚠️ 两个无壳态不能长得**完全**一样，否则「已绑定 出战中」并排就是一串同色的字，
     看不出哪个是"正在用"。按全站「aqua 只表达选中」的规矩分层：
       已绑定 → --text-2（中性事实：这只归你，没什么可说的）
       出战中 → --brand-text（当前选中，与卡片自身的 is-active 描边同源）
     两者都不加底色/边框，仍完全符合「不显示底色和边框线框」。 */
.tgl.pcard-tgl.is-done {
  opacity: 1;
  cursor: default;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--brand-text);
  font-weight: 700;
}

.tgl.pcard-tgl.is-bound:disabled {
  opacity: 1;
  cursor: default;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--text-2);
  font-weight: 700;
}

/* 「未解锁」：虚线灰框 + 保持不透明。语气是「这块锁着」，不是「按钮恰好点不动」，
   所以既不走 .5 透明度也不给任何填充 —— 锁着的东西不该有「已选中」的暗示。 */
.tgl.pcard-tgl.is-lock:disabled {
  opacity: 1;
  cursor: not-allowed;
  background: transparent;
  border-style: dashed;
  border-color: var(--hairline-strong);
  color: var(--text-3);
}

/* 键盘焦点环：**可访问性要求，不是"壳"**。达成态（出战中）虽然长得像状态标签，但它
   在 Tab 顺序里（它可点、只是点击是空操作），没有焦点指示就成了"键盘用户看不见自己
   在哪"——所以也要给环。只给 3px 环、不给底色/边框，且只有键盘触发（鼠标点击不出现）。
   ⚠️ 必须排在三个达成态**后面**：同特异性 (0,3,0)，靠源码顺序才压得住它们的
      `box-shadow: none`。可点态有更高特异性的 (0,5,0) 那条，环 + 光晕一起给。 */
.tgl.pcard-tgl:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* ---------------- 未解锁雀士：灰色上锁画面 ----------------
   灰化只作用于「画面内容」（插图 / 首字母 / 底色），**不含锁标记与文字** ——
   若给 .pcard-art 整体加 filter，锁也在里面，会跟着变灰、反而看不清它在表达什么。
   所以逐块写在具体的子元素上。 */
.pcard.is-locked {
  border-color: var(--hairline);
  box-shadow: var(--shadow-sm);
}

/* 锁定的卡不该有「抬起来」的悬停反馈 —— 那是在暗示可点。 */
.pcard.is-locked:hover,
.pcard.is-locked:active {
  transform: none;
  box-shadow: var(--shadow-sm);
}

.pcard.is-locked .pcard-art {
  /* 无插图时卡片底色也得是灰的，否则未解锁卡会是彩色的（原底色是 brand 渐变） */
  background: var(--surface-3);
}

.pcard.is-locked .pcard-art > img,
.pcard.is-locked .pcard-initial {
  filter: grayscale(1);
  opacity: 0.45;
}

.pcard.is-locked .pcard-name {
  color: var(--text-2);
}

/* 头像也跟着灰化：它在名字前面、和名字同属「这一行讲的是谁」，
   名字灰了头像还亮着会很跳。注意用的是 opacity 而不是 filter —— 头像里可能是一张
   彩色立绘裁图，只降透明度比 grayscale 更省事，也不会因为 filter 建层影响布局。 */
.pcard.is-locked .pcard-avatar {
  opacity: 0.45;
}

/* 上锁遮罩：居中一枚圆形玻璃片 + 锁形图标。
   圆底用 --float-fill（够实的浮动层底色）而不是 --mask —— 卡片底图深浅不可控，
   只有「够实」的底才能保证锁在任何立绘上都看得清。 */
.pcard-lock {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--mask);
}

.pcard-lock svg {
  width: 34px;
  height: 34px;
  padding: 7px;
  border-radius: 50%;
  background: var(--float-fill);
  color: var(--text-2);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

/* ---------------- 个人中心 ---------------- */

.profile-head {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xl);
  padding: 18px 16px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-md), var(--glass-hi);
}

.avatar-lg {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--brand), var(--brand-2));
  color: var(--brand-ink);
  font-size: 22px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.30), 0 6px 16px var(--brand-shadow);
}

.avatar-lg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.uname {
  font-size: 18px;
  font-weight: 800;
  letter-spacing: 0.01em;
}

.sig {
  font-size: 13px;
  color: var(--text-2);
  margin-top: 3px;
  cursor: pointer;
  line-height: 1.5;
}

.menu {
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-bottom: 18px;
  box-shadow: var(--shadow-sm), var(--glass-hi);
}

.menu-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 52px;
  padding: 15px 16px;
  font-size: 15px;
  font-weight: 600;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
  transition: background 0.16s ease;
}

.menu-item:hover {
  background: var(--surface-2);
}
.menu-item:active {
  background: var(--surface-3);
}

.menu-item:last-child {
  border-bottom: none;
}

.menu-item .arrow {
  color: var(--text-3);
  font-size: 18px;
  transition: transform 0.16s ease;
}

.menu-item:hover .arrow {
  transform: translateX(2px);
  color: var(--text-2);
}

/* 录音态的红点：录音中呼吸，给「正在录」一个不动脑的信号（卡片内立直音控件在用）。 */
.rec-dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--dealer);
}

.rec-dot.live {
  animation: rec-pulse 1.1s ease-out infinite;
}

@keyframes rec-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(208, 53, 63, 0.55);
  }
  70% {
    box-shadow: 0 0 0 9px rgba(208, 53, 63, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(208, 53, 63, 0);
  }
}

/* 成功提示：绿色。原来「上传成功」被当成出错，走了红字 */
.ok-msg {
  margin-top: 10px;
  text-align: center;
  font-size: 13px;
  line-height: 1.5;
  color: var(--good-ink);
}

/* ---------------- 表单 ---------------- */

textarea {
  width: 100%;
  min-height: 112px;
  padding: 11px 13px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  font-size: 15px;
  line-height: 1.6;
  background: var(--surface-2);
  color: var(--text);
  outline: none;
  resize: none;
  box-shadow: var(--glass-hi);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

textarea:focus {
  border-color: var(--brand);
  box-shadow: var(--ring), var(--glass-hi);
}

.count {
  text-align: right;
  color: var(--text-2);
  font-size: 12px;
  margin: 6px 0;
  font-variant-numeric: tabular-nums;
}

.grow {
  flex: 1;
  min-height: 42px;
  padding: 9px 12px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  font-size: 14px;
  background: var(--surface-2);
  color: var(--text);
  outline: none;
  box-shadow: var(--glass-hi);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.grow:focus {
  border-color: var(--brand);
  box-shadow: var(--ring), var(--glass-hi);
}

.btn-mini {
  min-height: 36px;
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 600;
  border: 1px solid var(--btn-blue-edge);
  border-radius: var(--r-sm);
  background: linear-gradient(180deg, var(--btn-blue-1), var(--btn-blue-2));
  color: var(--btn-blue-ink);
  cursor: pointer;
  box-shadow: 0 4px 12px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
  transition: all 0.16s ease;
}

.btn-mini:hover {
  filter: brightness(1.07);
  border-color: var(--btn-blue-hi);
}

.btn-mini:active {
  transform: scale(0.97);
  box-shadow: 0 2px 8px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
}

.btn-mini:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  filter: none;
}

.btn-mini.danger {
  background: linear-gradient(180deg, var(--btn-red-1), var(--btn-red-2));
  color: var(--btn-red-ink);
  border-color: var(--btn-red-edge);
  box-shadow: 0 4px 12px var(--btn-red-glow), inset 0 1px 0 var(--btn-red-hi);
}

.btn-mini.danger:hover {
  filter: brightness(1.07);
  border-color: var(--btn-red-hi);
}

.btn-mini.danger:active {
  box-shadow: 0 2px 8px var(--btn-red-glow), inset 0 1px 0 var(--btn-red-hi);
}

.form {
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  padding: 4px 16px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-sm), var(--glass-hi);
}

.form-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 13px 0;
  border-bottom: 1px dashed var(--hairline);
}

.form-row:last-child {
  border-bottom: none;
}

.fl {
  font-size: 15px;
  font-weight: 600;
}

.form-row select,
.form-row input[type="number"] {
  min-height: 40px;
  padding: 7px 9px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-sm);
  font-size: 14px;
  background-color: var(--surface-2);
  color: var(--text);
  outline: none;
  box-shadow: var(--glass-hi);
  transition: border-color 0.16s ease, box-shadow 0.16s ease,
    background-color 0.16s ease;
}

/* 下拉给右侧 chevron 留位；数字输入框不需要 */
.form-row select {
  padding-right: 32px;
}

.form-row input[type="number"]:focus {
  border-color: var(--brand);
  box-shadow: var(--ring), var(--glass-hi);
}

.form-row input[type="number"] {
  width: 94px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.tgl {
  min-width: 48px;
  min-height: 34px;
  padding: 6px 13px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--glass-hi);
  transition: all 0.18s ease;
}

.tgl.on {
  background: var(--brand-tint);
  color: var(--brand-text);
  border-color: var(--brand-line);
  font-weight: 700;
}

/* 开关类按钮的通用交互反馈：悬停微亮，按下下沉 1px 并压暗，键盘焦点出环。
   ⚠️ 只改 `filter` / `transform` / `box-shadow`，**不碰 border-color 与 background** ——
      否则会把 `.on` 的品牌色描边与底色盖掉（悬停时青边忽然变白，很像坏了）。
   ⚠️ 一律带 `:not(:disabled)`：禁用控件不该给出「能点」的响应。
   ⚠️ 一律带 `:not(.pcard-tgl)`：雀士卡按钮自带三副皮（见上面那一整段），
      通用反馈漏到 `.is-done` 上会让它在悬停时冒出一圈描边，「无壳」当场破功。
   ⚠️ 这些规则只在交互态生效，静止时的计算样式与改版前逐字节相同。 */
.tgl:not(.pcard-tgl):not(:disabled):hover {
  filter: brightness(1.08);
}

.tgl:not(.pcard-tgl):not(:disabled):active {
  transform: translateY(1px);
  filter: brightness(0.94);
}

.tgl:not(.pcard-tgl):focus-visible {
  outline: none;
  box-shadow: var(--ring), var(--glass-hi);
}

/* ---------------- 开局：座位与雀士 ---------------- */

.seat-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 0;
  border-bottom: 1px dashed var(--hairline);
}

.wind-badge {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-xs);
  background: #191d40;
  color: #aca5a2;
  font-size: 17px;
  font-weight: 700;
  font-family: "KaiTi", "STKaiti", "Kaiti SC", "楷体", serif;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

.wind-badge.dealer {
  background: #be313b;
  color: #231916;
}

.seat-controls {
  flex: 1 1 auto;
  min-width: 0;
}

/* 可输入 + 下拉筛选的雀士组合框（原生 datalist 与自制 ac 容器都兼容） */
.seat-controls input {
  width: 100%;
  min-width: 0;
  padding: 7px 8px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xs);
  font-size: 13px;
  background: var(--surface-2);
  color: var(--text);
  outline: none;
  box-shadow: var(--glass-hi);
}

/* 但 .ac 容器已经接管了样式：把 ac-input 自身重置成「只显示文本 + 透明背景」 */
.ac > .ac-input {
  border: 0;
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  padding: 7px 8px;
}
.ac > .ac-input:focus {
  border: 0;
  box-shadow: none;
}

.seat-controls input::placeholder {
  color: var(--text-3);
}

.seat-controls input:focus {
  border-color: var(--brand);
  box-shadow: var(--ring);
}

.seat-row input[type="number"] {
  width: 74px;
  flex: 0 0 74px;
  padding: 7px 6px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xs);
  font-size: 13px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  background: var(--surface-2);
  color: var(--text);
  outline: none;
  box-shadow: var(--glass-hi);
}

/* ---------------- 智能补全（下拉选择）组件 ---------------- */
/* 输入框：左边文本输入，右边一个 ⌄ 按钮，点开就出列表。
   输入：列表按关键字筛；点 ⌄：列表全量；
   选项：键盘 Enter / 鼠标点击，二者都同步到 hidden [data-seat-player]，
   原监听器照常写 app.setup.names/selections。 */

.ac {
  position: relative;
  display: flex;
  align-items: stretch;
  width: 100%;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xs);
  background: var(--surface-2);
  box-shadow: var(--glass-hi);
  overflow: visible;       /* 菜单要溢出 */
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.ac:focus-within,
.ac.ac-open {
  border-color: var(--brand);
  box-shadow: var(--ring);
}

.ac-input {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  padding: 7px 8px;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  outline: none;
}
.ac-input::placeholder {
  color: var(--text-3);
}

.ac-toggle {
  flex: 0 0 28px;
  width: 28px;
  border: 0;
  border-left: 1px solid var(--glass-edge);
  background: transparent;
  color: var(--text-2);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  border-radius: 0 var(--r-xs) var(--r-xs) 0;
  transition: background 0.15s ease, color 0.15s ease;
}
.ac-toggle:hover {
  background: var(--surface-3);
  color: var(--brand-text);
}
.ac.ac-open .ac-toggle {
  background: var(--brand-tint);
  color: var(--brand-text);
  border-left-color: var(--brand-line);
}
.ac-toggle:active {
  transform: translateY(0.5px);
}

.ac-menu {
  position: absolute;
  left: -1px;            /* 与容器对齐，把容器的边框"压"在菜单下 */
  right: -1px;
  top: calc(100% + 4px);
  /* 要高于弹层内所有同级内容。⚠️ 但 z-index 拦不住带 backdrop-filter 的表单控件
     （合成层不参与这里的 z 序）—— 那一侧由上面的 .sheet select/input 规则清零解决。 */
  z-index: 40;
  list-style: none;
  margin: 0;
  padding: 4px;
  max-height: 220px;     /* 期望高度；实际可用高度由 JS 写成 inline max-height */
  overflow-y: auto;
  overscroll-behavior: contain;   /* 列表滚到底不把滚动传给底下的 .sheet */
  background: var(--float-fill);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-md), var(--glass-hi);
  display: none;
}
.ac.ac-open .ac-menu {
  display: block;
}
/* 下方空间不足时由 JS 挂上 .ac-up → 改为向上弹出（.ac 是 position:relative 容器）。
   没有这一步时，靠近弹层底部的座位行会把菜单顶出 .sheet 的滚动可视区，
   被 .sheet 的 overflow 裁掉（实测 756×441 下裁掉 210/220px，整份列表几乎看不见）。 */
.ac-menu.ac-up {
  top: auto;
  bottom: calc(100% + 4px);
}
.ac-menu.empty::before {
  content: "暂无匹配";
  display: block;
  padding: 8px 10px;
  color: var(--text-3);
  font-size: 12px;
}

.ac-item {
  padding: 8px 10px;
  border-radius: var(--r-xs);
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.12s ease, color 0.12s ease;
}
.ac-item:hover,
.ac-item:focus,
.ac-item.is-active {
  background: var(--brand-tint);
  color: var(--brand-text);
}
.ac-item:active {
  background: var(--brand-line);
}
.ac-item.temp {
  color: var(--text-2);
  border: 1px dashed var(--brand-line);
  margin-top: 4px;
  font-style: italic;
}
.ac-item.empty {
  cursor: default;
  color: var(--text-3);
  font-style: italic;
}

/* ---------------- 规则：紧凑双列 ---------------- */

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 18px;
  background: var(--surface);
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  padding: 2px 16px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-sm), var(--glass-hi);
}

.form-grid .form-row {
  padding: 8px 0;
  gap: 8px;
}

.form-grid .fl {
  font-size: 13px;
  min-width: 0;
}

.form-grid select,
.form-grid input[type="number"] {
  font-size: 13px;
  padding: 5px 6px;
  min-width: 0;
}

.form-grid select {
  flex: 1 1 auto;
  max-width: 100%;
  padding-right: 28px; /* chevron 留位（小字号用稍小的右侧留白） */
}

/* 留够 5 位数（25000/30000）：可用宽度 = 76-12 = 64px ＞ 文本 47px */
.form-grid input[type="number"] {
  width: 76px;
  flex: 0 0 76px;
}

.form-grid .tgl {
  min-width: 42px;
  min-height: 30px;
  padding: 4px 10px;
  font-size: 12px;
}

/* ---------------- 个人首页：进行中的对局 ----------------
   两层结构（照参考稿的「任务进程」卡）：
     ① [状态胶囊] [粗标题：4人 · 東1局]            [我的点数]
        ──────────── 虚线（一层与二层的分界） ────────────
     ② [位次胶囊 + 玩家名单（可换行）]              [报点按钮]
   卡片自带毛玻璃：麻将牌底纹从它背后透上来（.mj-bg 在自己的层上），
   所以卡片里不再单独铺纹样，免得两种纹理打架。 */

.live-game {
  padding: 12px 14px 13px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  box-shadow: var(--shadow-sm), var(--glass-hi);
  cursor: pointer;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}

.live-game:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md), var(--glass-hi);
  border-color: var(--hairline-strong);
}

.live-game:active {
  transform: translateY(0) scale(0.99);
  border-color: var(--brand-line);
}

.lg-head {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* 状态胶囊：进行中 = 主色青；组队中 = 中性灰（还没开局，不该跟进行中抢注意力）。
   小圆点是 currentColor，跟着胶囊文字色走。 */
.lg-status {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-text);
  background: var(--brand-tint);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-pill);
  padding: 3px 9px;
}

.lg-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.lg-status.is-lobby {
  color: var(--text-2);
  background: var(--surface-3);
  border-color: var(--glass-edge);
}

.lg-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: 0.01em;
}

/* 我的点数：这一行里唯一的"数字主角"，所以给最大字号 + 主色 + 等宽数字
   （等宽数字让四张卡的分数右缘对齐，扫一眼就能比大小）。 */
.lg-pts {
  flex: 0 0 auto;
  font-size: 19px;
  font-weight: 800;
  color: var(--brand-text);
  font-variant-numeric: tabular-nums;
}

.lg-body {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--hairline-strong);
}

.lg-sub {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-2);
  /* 名单允许换行（参考稿的第二层也是两行文字），但**最多两行** ——
     一桌四个「账号名（雀士名）」合起来四十多个字，放任换行会把卡片撑成一块砖。 */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.lg-rank {
  display: inline-block;
  margin-right: 6px;
  padding: 1px 7px;
  border-radius: var(--r-pill);
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-ink);
  background: var(--brand);
  vertical-align: 1px;
}

/* 动作按钮：全站「蓝＝常规操作」那副皮的小尺寸版。
   ⚠️ 用 --pill-blue-* 而不是 --btn-blue-*：那一套是给 16px 主按钮调的，
      浅色主题那版白字只有 4.42:1，14px 不享受"大字"放宽。 */
.lg-btn {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 7px 18px;
  font-size: 14px;
  font-weight: 700;
  border-radius: var(--r-sm);
  border: 1px solid var(--btn-blue-edge);
  background-image: linear-gradient(180deg, var(--pill-blue-1), var(--pill-blue-2));
  color: var(--btn-blue-ink);
  cursor: pointer;
  box-shadow: 0 4px 12px var(--btn-blue-glow), inset 0 1px 0 var(--btn-blue-hi);
  transition: filter 0.16s ease, transform 0.16s ease;
}

.lg-btn:hover {
  filter: brightness(1.08);
}

.lg-btn:active {
  transform: translateY(1px) scale(0.99);
  filter: brightness(0.96);
}

.lg-btn:focus-visible {
  outline: none;
  box-shadow: var(--ring), 0 4px 12px var(--btn-blue-glow);
}

/* ---------------- 玩家计分页（?p=<对局id>）---------------- */
/* 独立页面：不套 tabbar。四家卡片是唯一操作入口，点击即报点。 */

.pg-head {
  position: sticky;
  top: 0;
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  padding-top: calc(12px + env(safe-area-inset-top, 0px));
  border-bottom: 1px solid var(--glass-edge);
  background: var(--surface-3);
  backdrop-filter: var(--glass-filter-strong);
  -webkit-backdrop-filter: var(--glass-filter-strong);
  box-shadow: var(--glass-hi);
}

.pg-head-txt {
  flex: 1 1 auto;
  min-width: 0;
}

.pg-title {
  font-size: 17px;
  font-weight: 700;
}

.pg-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--text-3);
}

.pg-note {
  margin: 14px 0 12px;       /* 给上下都留呼吸距离，避免与 .pg-head 底边或四家卡片顶边"贴边" */
  padding: 9px 12px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-2);
  background: var(--brand-bg);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-md);
}

.pg-note b {
  color: var(--brand-text);
}

.pg-list {
  display: grid;
  gap: 10px;
}

/* 玩家卡片：整张卡是 563×141 的内联 SVG（app.js 的 pgCardSvg），
   这里只管「按钮壳」+ 卡内文字的字号字色。
   ⚠️ 卡内文字必须用 SVG 的 fill，写 color 是不生效的。
   卡内字号按 563 宽的画布给，整卡随容器等比缩放 ⇒ 手机上实际字号 ≈ 下值 × 0.64。
   参考稿量出来的落点：昵称墨迹高 22 / 左起 214，分数墨迹高 26 / 左起 214。 */
.pg-card {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.pg-card-svg {
  display: block;
  width: 100%;
  height: auto;
}

.pg-card:active {
  transform: scale(0.985);
}

.pg-card[disabled] {
  cursor: default;
  opacity: 0.72;
}

.pg-card[disabled]:active {
  transform: none;
}

.pg-card-name {
  font-family: var(--font);
  font-size: 24px;
  font-weight: 700;
  fill: #1b70af;
}

.pg-card-score {
  font-family: var(--font);
  font-size: 34px;
  font-weight: 700;
  fill: #000;
  font-variant-numeric: tabular-nums;
  letter-spacing: 1px;
}

.pg-card-initial {
  font-family: var(--font);
  font-size: 44px;
  font-weight: 700;
  fill: #8fa4c2;
}

.pg-card-tag {
  font-family: var(--font);
  font-size: 22px;
  font-weight: 700;
  fill: #fff;
}

.pg-msg {
  margin-top: 12px;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 600;
  border-radius: var(--r-md);
  border: 1px solid transparent;
}

.pg-msg.ok {
  color: var(--good-ink);
  background: var(--good-bg);
  border-color: var(--good-ink);
}

.pg-msg.bad {
  color: var(--bad-ink);
  background: var(--bad-bg);
  border-color: var(--bad-ink);
}

/* 报点弹层里的「谁 → 谁」一句话说明 */
.pg-flow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 4px 0 14px;
  padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--surface-2);
  border: 1px solid var(--glass-edge);
  font-size: 14px;
  font-weight: 600;
  box-shadow: var(--glass-hi);
}

.pg-flow-arrow {
  color: var(--brand-text);
  font-size: 12px;
}

.pg-flow-win {
  color: var(--brand-text);
}

/* ---------------- 桌面端适配 ---------------- */

@media (min-width: 768px) {
  #root {
    --root-pad-t: 20px;
    /* 平板（含 iPad Air 820px）铺满屏宽：不再卡 720px，两侧不再留大片空白。
       盘面是方形，靠 .table-area 的 min(100cqw,100cqh) 自动取「宽与高的较小者」，
       所以铺满只会让盘更大、仍被视口高度限制住，不会溢出、不会多出滚动。 */
    max-width: 100%;
    padding: var(--root-pad-t) 20px 0;
  }

  .app {
    background: var(--surface);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    border: 1px solid var(--glass-edge);
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    box-shadow: var(--shadow-md), var(--glass-hi);
    overflow: hidden;
  }

  .sheet {
    max-width: 640px;
  }

  .card .avatar {
    width: 24px;
    height: 24px;
    font-size: 14px;
  }
  .name {
    font-size: 15px;
  }
  .badge.wind {
    font-size: 15px;
  }
}

/* 桌面 / 大平板：超宽屏给一个上限，避免卡片列表被拉得过宽、文字行过长 */
@media (min-width: 1200px) {
  #root {
    max-width: 1080px;
    margin: 0 auto;
  }
}

/* ---------------- 动效偏好 ---------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------- 下拉选择（select）统一美化 ----------------
   两处下拉共用一套视觉：规则选择（.form-row / .form-grid）、
   音效页雀士选择（.field）。appearance:none 抹掉各平台原生箭头，换一个随文字色的
   圆角 chevron；状态（默认/悬停/聚焦/禁用）统一走令牌。
   ⚠️ 原生 select 的下拉面板由操作系统绘制：展开动画无法用 CSS 控制；
   选项的 padding / 行高 / 选中色在 Chromium（Edge/Chrome）里可部分生效，已尽量给出。 */
select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1.5 2.5 6 7 10.5 2.5' fill='none' stroke='%23b7c4cd' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px 8px;
  transition: border-color 0.16s ease, box-shadow 0.16s ease,
    background-color 0.16s ease;
}

select:not(:disabled):hover {
  border-color: var(--hairline-strong);
  background-color: var(--surface-3);
}

select:focus,
select:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: var(--ring), var(--glass-hi);
}

select:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}

select option {
  color: var(--text);
  padding: 8px 14px;
  line-height: 1.55;
}

select option:hover {
  background-color: var(--brand-bg);
}

select option:checked {
  background-color: var(--brand-tint);
  color: var(--brand-text);
  font-weight: 600;
}

@media (prefers-color-scheme: light) {
  select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1.5 2.5 6 7 10.5 2.5' fill='none' stroke='%23414e59' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  }
}

/* ============================================================
   玩家首页：矢量麻将牌底纹
   ============================================================
   牌面只画**纯几何**：筒子的同心圆、索子的竹节条、白板的双框、红中的「口 + 丨」。
   一个字形都不依赖 —— 字牌（萬/東/中）必须落字体，而站内只内嵌了盘面用字那一小份
   子集，缺字会静默回退系统黑体（盘面踩过一次，桌面看不出、手机一眼可见）。
   排列：2×2 张牌为一个重复单元，整体再斜 7°，像刚码好的牌墙。

   ⚠️ 颜色只能落在 CSS 类上，不能写进 SVG 的 presentation 属性 ——
      `fill="var(--mj-ink)"` 这种写法在浏览器里是不保证生效的，一旦不生效整片牌面会变黑。 */

/* 装饰层：可以绝对定位，但被这三条钉死在"背景"这个角色上。
   inset:0 让它的尺寸**跟着宿主内容自动变**（宿主长高了底纹自然跟着长），
   z-index:0 配宿主的 isolation 自成一套层叠顺序，pointer-events:none 不吃点击。 */
[data-deco] {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* 装饰的宿主：提供定位上下文 + 层叠隔离。
   没有这两条时，绝对定位的装饰层会画在普通流内容**之上**（绝对定位比流内内容后画），
   底纹直接盖住卡片文字。 */
[data-host] {
  position: relative;
  isolation: isolate;
}

.mygames {
  position: relative;
  /* 负外边距把底纹铺到 .content 的内边距之外（左右贴齐、上顶到顶），
     否则底纹被裁在那 16px 的内缩里，看着像一张贴歪的纸。
     isolation:isolate 让「底纹 z-index:0 / 内容 z-index:1」自成一套层叠顺序 ——
     不然绝对定位的底纹会盖在卡片文字上面（绝对定位 + z-index:0 比普通流内容后画）。 */
  isolation: isolate;
  margin: calc(-1 * var(--content-pad-t)) -16px 0;
  padding: var(--content-pad-t) 16px 26px;
}

.mygames-body {
  position: relative;
  z-index: 1;
}

.mj-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.9;
  /* 只在最上/最下各留一条很窄的柔化带：底纹要**盖住整份列表**才像"底"，
     早早就淡出会让人以为它只铺了半页。 */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 4%, #000 93%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 4%, #000 93%, transparent 100%);
}

.mj-bg .mj-face {
  fill: var(--mj-face);
  stroke: var(--mj-edge);
}
.mj-bg .mj-ring,
.mj-bg .mj-frame {
  fill: none;
  stroke: var(--mj-ink);
}
.mj-bg .mj-bar {
  fill: var(--mj-ink);
}

/* 对局列表：卡与卡之间要留白，底纹才透得出来 */
.lg-list {
  display: flex;
  flex-direction: column;
  gap: 11px;
  margin-top: 14px;
}

/* ============================================================
   我的数据：整页面板
   ============================================================
   自上而下的信息层级：账号总账（hero）→ 进度条 → 指标格 → 顺位走势 → 两个跳转。
   全部用同一套毛玻璃 + 主色青，与全站材质语言一致。 */

.md-hero {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 12px 14px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-xl);
  /* 顶上压一层极淡的主色，替参考稿那张橙色渐变横幅的位置 */
  background: linear-gradient(180deg, var(--brand-bg), transparent 64%), var(--surface);
  box-shadow: var(--shadow-md), var(--glass-hi);
  /* 把 hero 声明成**尺寸查询容器**：里面的数字按 hero 自身宽度缩放，
     而不是按视口 —— 因为同一块面板既可能住在 320px 的手机里，
     也可能住在桌面端一个 520px 的窄栏里。按视口判断在后者上必然判错。 */
  container-type: inline-size;
}

.md-side {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
}

.md-num {
  /* 跟随 hero 自身宽度缩放（cqw = hero 的 1% 宽）：窄容器里 26px 的「2.35」
     加上两边留白会顶破 .md-side，缩到 20px 就够。上限仍是设计稿的 26px。 */
  font-size: clamp(20px, 7cqw, 26px);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}

.md-label {
  margin-top: 3px;
  font-size: 12px;
  color: var(--text-2);
}

.md-chip {
  display: inline-block;
  max-width: 100%;
  margin-top: 7px;
  padding: 3px 9px;
  font-size: 11px;
  font-weight: 700;
  border-radius: var(--r-pill);
  color: var(--brand-text);
  background: var(--brand-tint);
  border: 1px solid var(--brand-line);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 中央圆徽 = 账号头像（这一页的主体是账号，不是雀士） */
.md-emblem {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}

.md-emblem-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--brand), var(--brand-2));
  color: var(--brand-ink);
  font-size: 24px;
  font-weight: 800;
  overflow: hidden;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.30), 0 8px 20px var(--brand-shadow);
}

.md-emblem-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.md-emblem-name {
  max-width: 86px;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.md-progress {
  margin-top: 12px;
  padding: 12px 14px 11px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  box-shadow: var(--glass-hi);
}

.md-progress-val {
  text-align: center;
  font-size: 15px;
  font-weight: 800;
  color: var(--brand-text);
  font-variant-numeric: tabular-nums;
}

.md-progress-track {
  height: 9px;
  margin-top: 8px;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  border: 1px solid var(--hairline);
  overflow: hidden;
}

.md-progress-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--md-bar);
}

.md-progress-label {
  margin-top: 7px;
  text-align: center;
  font-size: 12px;
  color: var(--text-2);
}

/* 指标格：四列，第二行按剩余数量自然收尾（2 人战 6 格 / 3 人战 7 格 / 4 人战 8 格）。
   ⚠️ minmax(0,1fr) 不能写成 1fr：1fr 的最小尺寸是 auto，5 位数字会把格子撑宽、
      把整行顶出容器（横向溢出）。 */
.md-grid {
  display: grid;
  /* 自适应列数：minmax 的下限取「4 列恰好铺满时的列宽」与 76px 中的较大者 ——
     宽度够就是 4 列（与设计稿一致），窄到铺不下 4 列时自动降到 3 列、2 列，
     永远不会把格子压到内容溢出。写死 repeat(4, 1fr) 在 320px 上每格只有 68px，
     「一位率」配「（17回）」会顶破格子。 */
  grid-template-columns: repeat(auto-fit, minmax(max(76px, calc((100% - 24px) / 4)), 1fr));
  gap: 8px;
  margin-top: 12px;
}

.md-cell {
  padding: 10px 5px 9px;
  text-align: center;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-md);
  background: var(--surface-2);
  box-shadow: var(--glass-hi);
}

.md-cell-label {
  font-size: 11px;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.md-cell-value {
  margin-top: 4px;
  font-size: 15px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* 没有副标的格子留一个最小高度，保证整行等高（不然带「（17回）」的那几格会高一截） */
.md-cell-sub {
  min-height: 14px;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-3);
}

.md-card {
  margin-top: 12px;
  padding: 12px 14px 10px;
  border: 1px solid var(--glass-edge);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  box-shadow: var(--shadow-sm), var(--glass-hi);
}

.md-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.md-card-ico {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  color: var(--brand-text);
  background: var(--brand-tint);
  border: 1px solid var(--brand-line);
}

.md-card-ico svg {
  display: block;
  width: 14px;
  height: 14px;
  fill: currentColor;
}

.md-card-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.md-card-link {
  flex: 0 0 auto;
  padding: 4px 2px;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-text);
  background: none;
  border: none;
  border-radius: var(--r-xs);
  cursor: pointer;
  transition: filter 0.16s ease;
}

.md-card-link:hover {
  filter: brightness(1.12);
}

.md-card-link:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

.md-chart {
  margin-top: 4px;
}

.md-chart-svg {
  display: block;
  width: 100%;
  height: auto;
}

.md-chart-svg .md-grid-line {
  stroke: var(--hairline);
}
.md-chart-svg .md-axis {
  fill: var(--text-3);
  font-size: 11px;
  font-weight: 600;
}
.md-chart-svg .md-line {
  fill: none;
  stroke: var(--brand);
  stroke-linejoin: round;
  stroke-linecap: round;
}
.md-chart-svg .md-node {
  fill: var(--md-node);
  stroke: var(--brand);
}

.md-chart-empty {
  padding: 24px 0 18px;
  text-align: center;
  font-size: 13px;
  color: var(--text-2);
}

/* 两个跳转按钮：走「主色 tonal」而不是蓝实心 ——
   这一页的主操作是看数据，不是点按钮；tonal 够醒目又不跟数字抢。 */
.md-actions {
  display: flex;
  flex-wrap: wrap;      /* 同上：宁可换行，也不让按钮互相压住或顶出容器 */
  gap: 10px;
  margin-top: 12px;
}

.md-act {
  flex: 1;
  min-height: 44px;
  font-size: 15px;
  font-weight: 700;
  border-radius: var(--r-md);
  border: 1px solid var(--brand-line);
  background: var(--brand-tint);
  color: var(--brand-text);
  cursor: pointer;
  transition: filter 0.16s ease, transform 0.16s ease, border-color 0.16s ease;
}

.md-act:hover {
  filter: brightness(1.08);
  border-color: var(--brand);
}

.md-act:active {
  transform: translateY(1px) scale(0.99);
}

.md-act:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

.md-subtitle {
  margin-top: 18px;
}
