/* ==================== 基础 ==================== */
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
:root {
  --tile-w: 52px;   /* 标准牌宽（高度由 3:4 比例自动算出，改宽即整体缩放） */
  --tile-sw: 37px;  /* 小牌宽：牌河 / 副露 / 对手手牌 */
  --bar-w: 24px;    /* 对手手牌背的宽度：定死，配合 .bars 的固定格子，牌数变化不再引起重排 */
  --claim-band: 58px;  /* #me-area 顶部留给"应答条/倒计时"的带子：它浮动在这一条里，既不压牌河，也不参与布局 */
  --hs: 1;          /* 手牌大小倍率（手机「A」按钮切换 1 / 1.28 / 1.6） */
  --vh: 100vh;      /* 动态视口：支持 dvh 用 dvh，JS 再按 innerHeight 校准 */
  --st: env(safe-area-inset-top, 0px);
  --sb: env(safe-area-inset-bottom, 0px);
  --sl: env(safe-area-inset-left, 0px);
  --sr: env(safe-area-inset-right, 0px);
  --avail-w: calc(100vw - var(--sl) - var(--sr));
  --hand-w: calc(min(var(--tile-w), (var(--avail-w) - 26px) / 14.9) * var(--hs) * 14.9);   /* 自家手牌整行宽度：右下角开关靠它贴到牌边 */
}
@supports (height: 100dvh) { :root { --vh: 100dvh; } }
body {
  font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
  background: radial-gradient(ellipse at 50% 30%, #1d6a46 0%, #0e3d27 70%) fixed;
  color: #f5efe0;
  overscroll-behavior: none;              /* 禁止下拉刷新误触 */
  -webkit-text-size-adjust: 100%;         /* 禁止横屏时字体自动放大 */
  text-size-adjust: 100%;
}
.hidden { display: none !important; }
.view {
  min-height: var(--vh);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 12px calc(12px + var(--sr)) 12px calc(12px + var(--sl));
}

/* ==================== 控件 ==================== */
button {
  font: inherit; cursor: pointer; border: 0; border-radius: 10px; padding: 9px 16px;
  background: #e8dcc0; color: #33291a; transition: transform .06s, filter .15s;
  touch-action: manipulation; user-select: none; -webkit-user-select: none; min-height: 40px;
}
button:active { transform: scale(.96); filter: brightness(.94); }
button.primary { background: linear-gradient(#ffd679, #f2b23e); font-weight: 700; color: #4a3008; }
button.big { padding: 13px 22px; font-size: 17px; width: 100%; }
button.ghost { background: rgba(255,255,255,.14); color: #f5efe0; padding: 6px 12px; font-size: 13px; min-height: 34px; }
button.danger { background: #c0564a; color: #fff; }
button:disabled { filter: grayscale(.7) brightness(.8); cursor: default; }
input, select {
  font: inherit; border: 0; border-radius: 10px; padding: 10px 12px;
  background: #fdf7e7; color: #33291a; outline: none; max-width: 100%;
}
select { min-height: 40px; }
.muted { opacity: .75; } .small { font-size: 12px; }

/* ==================== 首页 / 大厅 ==================== */
.home-card {
  width: min(92vw, 360px); display: flex; flex-direction: column; gap: 14px; text-align: center;
  background: rgba(0,0,0,.28); border-radius: 20px; padding: 22px;
  padding-top: calc(26px + var(--st)); padding-bottom: calc(26px + var(--sb));
}
.home-card h1 { font-size: 30px; letter-spacing: 2px; } .home-card h1 small { font-size: 14px; opacity: .8; }
.home-card .row { display: flex; gap: 8px; } .home-card .row input { flex: 1; min-width: 0; text-transform: uppercase; }
/* 首页两个动作分主次：主＝金底大按钮，次＝描边按钮。
   直连打开是「创建房间」为主（DOM 原序）；好友分享链接打开时 app.js 给 .home-card 挂上
   .invited，主次对调（按钮类名由 JS 换）＋ 位置对调（靠下面的 order），一眼就知道该点哪个。 */
.home-actions { display: flex; flex-direction: column; gap: 10px; }
.home-card .sep { display: flex; align-items: center; gap: 10px; font-size: 12px; color: rgba(245, 239, 224, .55); }
.home-card .sep::before, .home-card .sep::after { content: ''; flex: 1; height: 1px; background: rgba(255, 255, 255, .18); }
button.alt {
  background: rgba(255, 255, 255, .1); color: #f5efe0; border: 1px solid rgba(255, 255, 255, .24);
  font-size: 14px; padding: 11px 14px; min-height: 44px;
}
button.alt:active { transform: scale(.98); filter: brightness(1.2); }
/* 邀请横幅：只有走分享链接才显示（app.js 去掉 .hidden） */
.home-invite {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: linear-gradient(135deg, rgba(255, 214, 121, .24), rgba(255, 214, 121, .08));
  border: 1px solid rgba(255, 214, 121, .5); border-radius: 14px;
  padding: 10px 12px; font-size: 15px; font-weight: 700; color: #ffe9ad;
}
.home-invite .inv-ic { font-size: 20px; }
/* 邀请态：品牌让位，标题缩小，重心全在「加入这一桌」 */
.home-card.invited h1 { font-size: 20px; letter-spacing: 1px; } .home-card.invited h1 small { font-size: 12px; }
.home-card.invited .row { order: -1; flex-wrap: wrap; gap: 10px; }
.home-card.invited .sep { order: 0; }
.home-card.invited #btn-create { order: 1; }
/* 房间码独占一行、居中放大：像递到手上的房牌，核对一眼就点下面的大按钮 */
.home-card.invited .row input { flex: 1 1 100%; text-align: center; font-size: 22px; font-weight: 700; letter-spacing: 9px; padding: 12px 6px 12px 15px; }
@media (max-width: 420px) { .home-card.invited .row input { letter-spacing: 7px; padding-left: 12px; } }
.lobby-card {
  width: min(94vw, 520px); background: rgba(0,0,0,.28); border-radius: 20px; padding: 22px;
  display: flex; flex-direction: column; gap: 14px;
  padding-top: calc(22px + var(--st)); padding-bottom: calc(22px + var(--sb));
}
.lobby-card h2 { font-size: 20px; } .code { letter-spacing: 4px; color: #ffd679; font-size: 26px; }
.invite { display: flex; gap: 8px; flex-wrap: wrap; } .invite input { flex: 1 1 150px; min-width: 0; font-size: 12px; color: #555; }
.invite button { flex: 0 0 auto; }
.seats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.seat { background: rgba(255,255,255,.08); border-radius: 12px; padding: 12px; display: flex; flex-direction: column; gap: 4px; border: 1px solid transparent; min-width: 0; }
.seat.me { border-color: #ffd679; } .seat.empty { align-items: center; color: rgba(255,255,255,.4); justify-content: center; min-height: 76px; }
.seat .nm { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seat .tag { font-size: 12px; opacity: .8; } .seat .ok { color: #8ef0a8; } .seat .off { color: #f0a08e; }
.options { display: flex; flex-direction: column; gap: 8px; font-size: 14px; } .options .chk { display: flex; gap: 8px; align-items: center; }
.options select { flex: 1; min-width: 0; }
/* 出牌音效行：两个开关 + 方言下拉 + 试听，窄屏换行也不挤 */
.sfx-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.sfx-row .chk { flex: 0 1 auto; }
.sfx-row select { flex: 0 1 auto; min-width: 92px; padding: 6px 10px; min-height: 36px; font-size: 13px; }
.btns { display: flex; gap: 8px; flex-wrap: wrap; } .btns button { flex: 1 1 auto; }
.loglist { font-size: 12px; opacity: .8; max-height: 90px; overflow: auto; list-style: none; -webkit-overflow-scrolling: touch; }

/* ==================== 聊天（大厅 + 牌桌：文本 + 语音） ==================== */
/* 大厅内嵌一块；牌桌走右侧抽屉（与「记录」同款、互斥）。都在牌桌布局之外，动不到牌池几何。 */
.chat-box {
  display: flex; flex-direction: column; gap: 8px;
  border-top: 1px solid rgba(255, 255, 255, .12); padding-top: 12px;
}
.chat-hd { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 14px; }
.chat-hd .ct { font-weight: 700; }
.chat-hd .chk { display: flex; gap: 6px; align-items: center; font-size: 13px; opacity: .9; }
.chat-list {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 168px; overflow-y: auto; padding: 2px 2px 2px 0;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}
.cmsg { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; max-width: 100%; }
.cmsg.me { align-items: flex-end; }
.cname { font-size: 11px; opacity: .62; font-style: normal; padding-left: 2px; }
.cbub {
  background: rgba(255, 255, 255, .10); border-radius: 4px 12px 12px 12px;
  padding: 6px 10px; font-size: 14px; line-height: 1.45; max-width: 88%;
  word-break: break-word; overflow-wrap: anywhere;
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px;
}
.cmsg.me .cbub { background: linear-gradient(#ffd679, #f2b23e); color: #4a3008; border-radius: 12px 4px 12px 12px; }
.cmsg.me .ctime { opacity: .55; }
.ctime { font-size: 10px; opacity: .5; font-style: normal; flex: 0 0 auto; }
.ctxt { white-space: pre-wrap; }
.csys { align-self: center; font-size: 11px; opacity: .5; }
.vmsg {
  min-height: 0; padding: 7px 12px; font-size: 14px; border-radius: 10px;
  background: rgba(93, 156, 236, .22); color: #eaf3ff; border: 1px solid rgba(140, 190, 255, .42);
}
.vmsg.playing { background: rgba(255, 214, 121, .3); color: #ffe9ad; border-color: rgba(255, 214, 121, .6); }
.chat-quick { display: flex; flex-wrap: wrap; gap: 6px; }
.qchip {
  min-height: 0; padding: 7px 11px; font-size: 12px; border-radius: 999px;
  background: rgba(255, 255, 255, .1); color: #f5efe0; border: 1px solid rgba(255, 255, 255, .18);
}
.chat-in { display: flex; gap: 6px; align-items: center; }
.chat-in input { flex: 1 1 auto; min-width: 0; }
.chat-in .mic { flex: 0 0 auto; padding: 8px 12px; font-size: 17px; touch-action: none; user-select: none; -webkit-user-select: none; }
.chat-in .mic.on { background: rgba(255, 214, 121, .3); color: #ffe9ad; }
.chat-in .send { flex: 0 0 auto; font-weight: 700; }
#chat-nohint { line-height: 1.5; }
/* 牌桌抽屉：右侧滑出（同 .glog 的手感），自带 safe-area，横屏刘海不压输入行 */
.gchat {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(84vw, 330px);
  background: rgba(8, 32, 20, .97); border-radius: 14px 0 0 14px; z-index: 42;
  display: flex; flex-direction: column; gap: 8px;
  padding: calc(14px + var(--st)) calc(12px + var(--sr)) calc(14px + var(--sb)) 12px;
  transform: translateX(103%); transition: transform .22s ease;
  box-shadow: -6px 0 18px rgba(0, 0, 0, .5);
}
.gchat.open { transform: none; }
.gchat-hd { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 700; flex: 0 0 auto; }
.gchat-hd .chk { display: flex; gap: 5px; align-items: center; font-size: 12px; font-weight: 400; opacity: .9; }
.gchat-hd button { margin-left: auto; }
.gchat .chat-list { max-height: none; flex: 1 1 auto; min-height: 0; }
.gchat .chat-in, .gchat .chat-quick { flex: 0 0 auto; }
/* 计分抽屉：左侧滑出（右半边被 记录/聊天 占了，番表这种长文更吃宽）。
   内容与聊天共用 .logmask 遮罩与互斥逻辑；桌面也做成抽屉（顶栏「计分」常驻）。 */
.gscore {
  position: fixed; top: 0; left: 0; bottom: 0; width: min(88vw, 380px);
  background: rgba(8, 32, 20, .97); border-radius: 0 14px 14px 0;
  z-index: 52;   /* 结算入口也用它查番：要浮在结算遮罩（z50）之上 */
  display: flex; flex-direction: column; gap: 6px;
  padding: calc(14px + var(--st)) 12px calc(14px + var(--sb)) calc(12px + var(--sl));
  transform: translateX(-103%); transition: transform .22s ease;
  box-shadow: 6px 0 18px rgba(0, 0, 0, .5);
}
.gscore.open { transform: none; }
.gscore-hd { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 700; flex: 0 0 auto; }
.gscore-hd button { margin-left: auto; }
.gscore-body { flex: 1 1 auto; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.sc-room {
  font-size: 12px; color: #ffe9ad; background: rgba(255, 214, 121, .1);
  border: 1px solid rgba(255, 214, 121, .35); border-radius: 9px; padding: 6px 9px;
}
.gscore-body h4 { margin: 12px 0 4px; font-size: 13px; color: #d9c9a3; }
.sc-rule { margin: 0; padding-left: 18px; font-size: 12.5px; line-height: 1.65; }
.sc-rule li { margin: 2px 0; }
table.sc-fans { width: 100%; border-collapse: collapse; font-size: 12px; line-height: 1.5; }
.sc-fans td { padding: 4px 6px; border-bottom: 1px solid rgba(255, 255, 255, .08); text-align: left; }
.sc-fans td:nth-child(2), .sc-fans td:nth-child(4) { text-align: right; color: #ffe9ad; white-space: nowrap; width: 18%; }
.sc-note { font-size: 11px; opacity: .75; margin-top: 10px; }
/* 排位抽屉：外壳复用 .gscore（同为左滑、同浮在结算遮罩之上），这里只排行子里 */
.gboard .bd-sub { font-size: 12px; color: #a9c3b0; padding: 2px 0 8px; flex: 0 0 auto; }
.gboard .bd-list { flex: 1 1 auto; min-height: 0; overflow: auto; -webkit-overflow-scrolling: touch; display: flex; flex-direction: column; }
.bd-row { display: flex; align-items: center; gap: 8px; padding: 9px 6px; border-bottom: 1px solid rgba(255,255,255,.08); font-size: 14px; }
.bd-row.lead { background: rgba(255, 214, 121, .09); border-bottom-color: transparent; border-radius: 9px; }
.bd-rank { flex: 0 0 20px; text-align: center; font-weight: 800; color: #9f8f6f; }
.bd-row.lead .bd-rank { color: #ffd77a; }
.bd-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-tag { font-size: 11px; opacity: .65; font-weight: 400; }
.bd-dealer { font-style: normal; font-size: 11px; margin-left: 5px; padding: 0 5px; border-radius: 6px; background: rgba(231, 76, 60, .28); color: #ffb1a6; border: 1px solid rgba(231, 76, 60, .5); }
.bd-delta { flex: 0 0 40px; text-align: right; font-size: 12px; }
.bd-score { flex: 0 0 auto; min-width: 40px; text-align: right; font-weight: 800; font-variant-numeric: tabular-nums; }
.bd-gap { flex: 0 0 42px; text-align: right; font-size: 11px; color: #9f8f6f; }
.gboard .bd-foot { flex: 0 0 auto; padding-top: 10px; }
/* 链接式小按钮（大厅/结算/排位里进番表用，不抢主按钮的视觉重量） */
.linkish {
  background: none; border: none; color: #ffe9ad; font-size: 12px; padding: 4px 2px;
  min-height: 0; text-decoration: underline; text-underline-offset: 3px; opacity: .9; text-align: left;
}
.linkish:active { opacity: .6; }
/* 表头聊天键与红点角标 */
#btn-chat { position: relative; padding-left: 9px; padding-right: 9px; }
.badge {
  position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; line-height: 17px;
  padding: 0 4px; border-radius: 999px; background: #e74c3c; color: #fff;
  font-size: 11px; font-weight: 700; font-style: normal; text-align: center;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, .35);
}
/* 牌桌左下角消息预览：浮在 #me-area 上沿之外（应答带的正上方、左手边）——
   绝对定位不参与布局，出现/到点收掉都动不到牌桌几何；也永远碰不到居中的应答钮
   和右下角的自动过/托管开关。新条目在下、旧的往上顶；最多 3 条、各挂 7 秒；
   点一条 = 开抽屉看全部。 */
#chat-corner {
  position: absolute; left: calc(4px + var(--sl)); bottom: 100%; margin-bottom: 3px; z-index: 17;
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  width: min(58vw, 260px); cursor: pointer;
}
.cc-item {
  display: flex; align-items: baseline; gap: 6px; max-width: 100%; min-width: 0;
  padding: 4px 10px; border-radius: 999px; font-size: 12px; line-height: 1.35;
  background: rgba(6, 26, 16, .86); color: #f5efe0; border: 1px solid rgba(255, 214, 121, .28);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .4); overflow: hidden;
}
.cc-item b { flex: 0 1 auto; max-width: 8.5em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #ffe9ad; font-weight: 700; }
.cc-item span { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-item.new { animation: cc-in .28s ease-out; }
@keyframes cc-in { from { opacity: 0; transform: translate(-8px, 4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .cc-item.new { animation: none; } }
@media (min-width: 1024px) { #chat-corner { width: min(30vw, 280px); } }
/* 语音消息浮层：两态——录音（脉动的 🎤 + 计时 + 停止/取消）与试听（播放器 + 发送/丢弃）。
   取消在两个阶段都在场；到 20 秒自动停下进试听而不是直接发。 */
.rec-mask {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .45); z-index: 60;
  display: flex; align-items: center; justify-content: center;
}
.rec-bubble {
  background: rgba(8, 32, 20, .96); border: 1px solid rgba(255, 214, 121, .5); border-radius: 16px;
  padding: 16px 20px 14px; text-align: center; display: flex; flex-direction: column; gap: 8px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .5); max-width: min(86vw, 340px);
}
.rec-bubble #rec-ic { font-size: 30px; line-height: 1; animation: rec-pulse 1.2s ease-in-out infinite; }
.rec-mask.confirm #rec-ic { animation: none; }   /* 试听态不晃图标 */
#rec-t { font-size: 22px; font-weight: 800; color: #ffe9ad; font-variant-numeric: tabular-nums; }
#rec-tip { font-size: 12px; opacity: .78; }
#rec-t.warn { color: #ff9d90; animation: rec-blink 1s steps(2, end) infinite; }
@keyframes rec-pulse { 50% { transform: scale(1.12); } }
@keyframes rec-blink { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .rec-bubble #rec-ic, #rec-t.warn { animation: none; } }
.rec-prev { width: 100%; height: 40px; }
.rec-btns { display: flex; gap: 10px; justify-content: center; }
.rec-btns button {
  flex: 1 1 0; min-width: 92px; min-height: 40px; border-radius: 12px; font-size: 15px; font-weight: 700;
  border: 1px solid rgba(255, 255, 255, .18); background: rgba(255, 255, 255, .08); color: #f5efe0;
}
#rec-x { border-color: rgba(255, 130, 115, .5); color: #ffb4a8; }
#rec-ok { border-color: rgba(126, 231, 135, .5); color: #b7f5be; background: rgba(38, 92, 46, .4); }
.rec-mask.confirm #rec-ok { background: rgba(38, 118, 52, .55); }


/* ==================== 牌（矢量牌面 /tiles/*.svg，3:4） ==================== */
.tile {
  width: var(--tile-w); aspect-ratio: 3 / 4;
  background: linear-gradient(#fffef6, #ece3c8);
  border: 1px solid #bdb083; border-radius: 6px;
  box-shadow: 0 2px 0 #a2946a, 0 3px 5px rgba(0, 0, 0, .4);
  display: inline-flex; align-items: center; justify-content: center;
  overflow: hidden; flex: 0 0 auto;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.tile img { width: 100%; height: 100%; display: block; pointer-events: none; }
.tile.tile-back {                       /* 牌背：CSS 背景，DOM 里不必放 <img> */
  background: url("/tiles/back.svg") center / cover no-repeat, linear-gradient(#55a878, #2c6e48 65%, #255c3f);
  border-color: #1f4c33; box-shadow: 0 2px 0 #1a3f2b, 0 3px 5px rgba(0,0,0,.4);
}
.tile.small { width: var(--tile-sw); border-radius: 5px; box-shadow: 0 1px 0 #a2946a, 0 2px 3px rgba(0,0,0,.35); }
/* 牌河"最新打出的一张"：只用 :last-child 标（牌河要能只追加不重写，不能靠 JS 加类） */
.river.q > .tile:last-child { outline: 2px solid rgba(255,214,121,.95); outline-offset: -1px; }
/* 刚摸进手的那一张：一圈细金框在米白牌面上太安静，叠了四层——亮金框 + 深色衬圈（把牌和邻牌隔开）
   + 外发光 + 抬起 4px + 左上角「摸」牌签，扫一眼就知道该打哪张。z-index 抬起以免被邻牌压住。 */
.tile.drawn {
  position: relative; z-index: 1; transform: translateY(-4px);
  outline: 3px solid #ffe9ad; outline-offset: -2px;
  box-shadow: 0 0 0 2px #0b2c1c, 0 0 12px 3px rgba(255,214,121,.6), 0 3px 0 #a2946a, 0 5px 8px rgba(0,0,0,.45);
}
.tile.drawn::after {
  content: '摸'; position: absolute; left: 0; top: 0; z-index: 2; pointer-events: none;
  background: #ffd679; color: #4a3300; font-weight: 700; font-size: 10px; line-height: 1.35;
  padding: 0 3px; border-radius: 4px 0 6px 0;
}
.tile.sel { transform: translateY(-12px); outline: 3px solid #ffd679; outline-offset: 0px; }   /* 选中态排在后面，要盖过新牌框 */
@media (max-width: 420px) {
  .tile.drawn { outline-width: 2px; }                    /* 牌窄，别用粗框吃掉牌面，靠衬圈/发光/抬起说话 */
  .tile.drawn::after { display: none; }                  /* 牌太小就别压字，一个"摸"字要占掉半张牌 */
}

.tile.taken { opacity: .35; }
@media (hover: hover) {
  .tile.clickable { cursor: pointer; }
  .tile.clickable:hover { transform: translateY(-5px); }
}
.meld-g { display: inline-flex; gap: 1px; position: relative; }
/* 副露角标：只标「杠」（明杠/暗杠/补杠共用一个「杠」字——暗杠少两张、补杠多一张，看不出来）。
   碰以前也贴一个角标，纯属多余：三张一样的明摆着，谁不知道是碰。暗杠牌面呈牌背、
   补杠压在碰牌上，这两样本身就把出处说清了，字形差异（3 张 vs 4 张）比角标更直接。 */
.mtag.mtag-gang {
  display: none; position: absolute; left: 1px; top: -1px; z-index: 2;
  background: rgba(0,0,0,.62); color: #ffe9ad; font-size: 9px; line-height: 1.3;
  padding: 0 2px; border-radius: 3px; pointer-events: none;
}

/* ==================== 牌桌（桌面：三列网格） ==================== */
#view-game { justify-content: flex-start; padding: 0; }
#me-area .tile { border-radius: 7px; }
.gh {
  width: 100%; display: flex; align-items: center; gap: 8px;
  padding: calc(8px + var(--st)) 14px 8px; background: rgba(0,0,0,.35); font-size: 14px;
  position: relative; z-index: 5;
}
.gh #g-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.gh #g-info { margin-left: auto; opacity: .85; white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }   /* 窄屏多一个喇叭键也不挤出横向滚动：标题/信息可被压缩 */
.gh .hbtn { display: none; flex: 0 0 auto; }
.gh #btn-fs.on { background: rgba(255,214,121,.26); color: #ffe9ad; }
.gh #btn-sfx { padding-left: 9px; padding-right: 9px; }          /* 喇叭只有图标，收窄一点 */
.gh #btn-sfx.off { opacity: .55; }
/* 自动过 / 托管：不贴屏幕角，而是贴着自家手牌，点起来省事。
   窄屏手牌占满整行 → 浮在手牌上沿的应答带右侧；宽屏两侧有余量 → 挪到手牌右侧。
   （牌桌专属；大厅与表头都不放这两个开关） */
#ctrl-corner {
  position: absolute; z-index: 17; display: flex; flex-direction: row; gap: 6px; align-items: center;
  bottom: calc(100% - var(--claim-band)); right: calc(4px + var(--sr));   /* 贴着副露/手牌那一排的上沿 */
}
#ctrl-corner button {
  min-height: 0; padding: 5px 9px; font-size: 12px; border-radius: 9px; font-weight: 600;
  background: rgba(0,0,0,.42); color: #e7ded0; border: 1px solid rgba(255,255,255,.16);
  box-shadow: 0 2px 6px rgba(0,0,0,.35);
}
#ctrl-corner button.on { background: rgba(255,214,121,.30); color: #ffe9ad; border-color: rgba(255,214,121,.55); }
@media (min-width: 1024px) {                        /* 宽屏手牌两侧有余量：直接坐到手牌右手边 */
  #ctrl-corner {
    top: auto; bottom: 30px; right: auto; flex-direction: column; align-items: flex-start;
    left: min(calc(50% + var(--hand-w) / 2 + 10px), calc(100% - 96px));
  }
}
#app:fullscreen { display: flex; flex-direction: column; min-height: 100vh; }
#app:-webkit-full-screen { display: flex; flex-direction: column; }
#g-net { color: #ffb26b; font-weight: 700; white-space: nowrap; }
#g-net.hidden { display: none; }
.table-wrap {
  flex: 1; width: 100%; max-width: 1220px; margin: 0 auto;
  display: grid; grid-template-columns: 220px minmax(0, 1fr) 220px; grid-template-rows: auto minmax(0, 1fr) auto;
  column-gap: 10px; row-gap: 6px; padding: 8px 12px; position: relative;
}
.opp { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.opp.top { grid-column: 1 / 4; grid-row: 1; flex-direction: row; align-items: flex-start; flex-wrap: wrap; justify-content: center; gap: 12px; }
#seat-left { grid-column: 1; grid-row: 2; }
#seat-right { grid-column: 3; grid-row: 2; }
.opp .who { background: rgba(0,0,0,.38); border-radius: 10px; padding: 4px 10px; display: flex; gap: 7px; align-items: center; font-size: 13px; flex: 0 0 auto; max-width: 100%; }
.opp .who .ds { background: #c0392b; color: #fff; border-radius: 4px; padding: 0 5px; font-size: 12px; }
.opp .who .wn { font-weight: 700; max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opp .who .bb { background: #5d9cec; color: #08263f; border-radius: 4px; font-size: 11px; padding: 0 4px; }
.opp .who .offb { background: #c0564a; color: #fff; border-radius: 4px; font-size: 11px; padding: 0 4px; }
.opp .who .sc { color: #ffd679; font-weight: 700; }
.opp .who .cnt { display: none; background: rgba(255,255,255,.16); border-radius: 5px; padding: 0 5px; font-size: 11px; }
/* 轮到谁：名牌描一圈金环。环必须画在**边框盒内侧**（inset）——
   左右两家（横屏 .opp.side）和矮屏对家（.opp.top）的容器都带 overflow，溢出按圆角盒裁切：
   outline / 外扩阴影画在盒子外面，左右两侧恰好被容器整条裁没，上下却在盒内躲过刀，
   于是用户看到「只剩上下和圆角，左右竖线没了」。inset 阴影永远画在元素自己的盒内，
   任何祖先 overflow 都裁不到它；一圈完整、跟圆角走，1.5px 也比旧 2px 描边秀气。
   外加一圈极淡的内发光，让名牌在深色底上更“亮起来”，同样不会被裁。 */
.opp.active .who { box-shadow: inset 0 0 0 1.5px #ffd679, inset 0 0 8px rgba(255,214,121,.30); }
.opp .melds { display: flex; gap: 5px; flex-wrap: wrap; }
.opp.top .melds { max-width: 460px; }
.opp.side .melds { flex-direction: column; align-items: flex-start; gap: 4px; }
/* 对手手牌：固定 7 列 × 2 行的格子，牌数变少只是留空格，格子数与盒子高度都不变。
   （以前这里也按 --tile-sw=37 排，190px 只放得下 5 张 → 13 张 3 行、10 张 2 行、5 张 1 行，
     每次有人碰/杠/摸牌，面板高度就跳 50px，整张牌桌跟着抖一下。） */
.bars {
  display: grid; grid-template-columns: repeat(7, var(--bar-w));
  grid-auto-rows: calc(var(--bar-w) * 4 / 3); gap: 2px;
  height: calc(var(--bar-w) * 4 / 3 * 2 + 2px); flex: 0 0 auto; overflow: hidden;
}
.bars .tile.small { width: var(--bar-w); border-radius: 4px; box-shadow: 0 1px 0 #1a3f2b, 0 2px 3px rgba(0,0,0,.35); }
.felt {
  grid-column: 2; grid-row: 2;
  display: flex; flex-direction: column; gap: 8px;
  background: rgba(0,0,0,.22); border-radius: 16px; padding: 10px; min-height: 288px; overflow: hidden;
}
.mid { display: flex; align-items: center; justify-content: center; gap: 16px; flex: 1; min-width: 0; font-size: 14px; flex-wrap: wrap; }
#mid-turn .big-turn { font-size: 17px; font-weight: 700; color: #ffe9ad; }
#mid-wall { opacity: .85; white-space: nowrap; }
#mid-claim { background: rgba(0,0,0,.32); border-radius: 8px; padding: 2px 8px; display: flex; gap: 8px; align-items: center; max-width: 100%; }
.riverrow { display: flex; flex: 1; gap: 8px; align-items: stretch; min-height: 0; }
.myriver { display: flex; flex-direction: column; gap: 3px; align-items: center; min-height: 0; }
.river { display: flex; flex-wrap: wrap; gap: 1px; justify-content: center; align-content: flex-start; min-height: 39px; }
.river.q.top { max-width: 68%; margin: 0 auto; min-height: 0; }
.river.q.side { flex: 0 0 auto; width: 155px; max-height: 240px; overflow-y: auto; justify-content: flex-start; min-height: 0; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }  /* 定高：不让自己把整页顶出滚动条 */
.river.q.bottom { max-width: 620px; min-height: 0; }
#me-area { grid-column: 1 / 4; grid-row: 3; display: flex; flex-direction: column; gap: 8px; align-items: center; padding: var(--claim-band) 0 8px; min-width: 0; max-width: 100%; position: relative; }
#my-hand {
  display: flex; flex-wrap: wrap; gap: 1px; justify-content: center;
  width: 100%; max-width: 100%; min-width: 0; overflow-x: auto; overflow-y: hidden;
  padding: 16px 2px 4px;                 /* 上方留白：选中抬起的牌不被容器裁掉 */
  -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain;
  scrollbar-width: none; touch-action: pan-x;
}
#my-hand::-webkit-scrollbar { display: none; }
#my-hand .tile { width: calc(min(var(--tile-w), (var(--avail-w) - 26px) / 14.9) * var(--hs)); }
#my-melds { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; max-width: 100%; min-height: calc(var(--tile-sw) * 4 / 3 + 3px); }  /* 没有副露也占住这一行：碰完牌桌面不再往上跳 */
.hint { font-size: 13px; opacity: .85; min-height: 17px; text-align: center; }
/* 应答条 / 倒计时：浮在手牌上方，不参与布局。
   它以前坐在 #me-area 的第一行，每次出现/消失（每轮都要好几回）都把这一栏撑高几十 px，
   牌池随即被压扁——看着就像整张桌面被缩放了一下。绝对定位后桌面高度与它完全无关。 */
.claim-bar {
  /* 用 left/right + margin:auto 居中，不用 left:50% + translateX —— 后者的收缩宽度只算"左半边"，
     按钮会被挤成两三行（实测 189x136），反而更挡桌面。 */
  position: absolute; left: 0; right: 0; bottom: calc(100% - var(--claim-band)); margin: 0 auto;
  width: max-content; max-width: calc(100% - 8px); z-index: 18;   /* 高于右下角开关：应答时按钮优先 */
  display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 10px; align-items: center;
  background: rgba(0,0,0,.55); padding: 8px 14px; border-radius: 14px;
  box-shadow: 0 3px 10px rgba(0,0,0,.4);
}
.claim-bar button { font-weight: 700; font-size: 16px; padding: 10px 20px; }
.claim-bar .b-hu { background: #e74c3c; color: #fff; } .claim-bar .b-peng { background: #e8a33d; color: #3a2606; } .claim-bar .b-gang { background: #5d9cec; color: #08263f; } .claim-bar .b-pass { background: #999; color: #fff; }
/* 倒计时：自足的小胶囊（细亮边 + 极浅底 + 圆角），不借黑盒当边框。
   中央横幅和应答条共用一套——黑盒只淡淡垫一层，视觉上"边框"就是这条 1px 亮边，不再显粗。 */
#mid-claim .cd, .claim-bar .cd { align-self: center; font-weight: 700; min-width: 26px; text-align: center;
  padding: 1px 8px; border: 1px solid rgba(255,255,255,.24); border-radius: 8px; background: rgba(255,255,255,.08); }
/* 轮到自己但没键可点（不能自摸/暗杠/补杠）时，条里只剩这一个倒计时——这是牌桌上最常见的一帧，
   黑条按四键那档留内距就等于给一个小数字套了圈粗黑边（用户："倒计时黑色边框有点粗"）。
   缩成贴身胶囊、底色再淡一点、补一条极细亮边定义轮廓。:has 的老浏览器自然回退成原样，不会渲染错。 */
.claim-bar:has(> .cd:only-child) { padding: 2px 6px; background: rgba(0,0,0,.34); box-shadow: 0 0 0 1px rgba(255,255,255,.10), 0 3px 10px rgba(0,0,0,.4); }
.glog {
  position: fixed; right: 6px; top: calc(46px + var(--st)); width: 200px; max-height: 36vh; overflow: auto;
  font-size: 11px; background: rgba(0,0,0,.4); border-radius: 10px; padding: 8px; opacity: .9;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain; z-index: 40;
}
.glog div { padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,.07); }
.rbtns { display: flex; gap: 10px; justify-content: center; margin-top: 6px; flex-wrap: wrap; }
.rbtns button { flex: 1 1 auto; min-width: 140px; }
.logmask { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 39; }

/* ==================== 结算 / 提示 ==================== */
.mask { position: fixed; inset: 0; background: rgba(0,0,0,.62); display: flex; align-items: center; justify-content: center; z-index: 50; padding: 10px calc(10px + var(--sr)) calc(10px + var(--sb)) calc(10px + var(--sl)); }
.result-card {
  width: min(94vw, 660px); max-height: 92%; overflow: auto; -webkit-overflow-scrolling: touch;
  background: #16412d; border: 1px solid rgba(255,214,121,.5); border-radius: 16px; padding: 18px;
  display: flex; flex-direction: column; gap: 12px;
}
.result-card h3 { color: #ffd679; font-size: 22px; text-align: center; }
.rrow { display: flex; align-items: center; gap: 10px; background: rgba(255,255,255,.06); padding: 8px 10px; border-radius: 10px; flex-wrap: wrap; }
.rrow b { flex: 0 0 auto; min-width: 64px; }
.rrow .rtiles { display: flex; flex-wrap: wrap; gap: 0; min-width: 0; }
.rrow .rmelds { display: flex; flex-wrap: wrap; gap: 8px; }   /* 结算副露：碰/明杠/补杠/暗杠（牌沿用 .rrow .tile 的尺寸） */
.rrow .tile { width: 30px; box-shadow: 0 1px 0 #a2946a; }
.rrow .delta { margin-left: auto; font-weight: 800; font-size: 17px; } .pos { color: #8ef0a8; } .neg { color: #f0a08e; }
#toast {
  position: fixed; top: calc(14px + var(--st)); left: 50%; transform: translateX(-50%);
  background: rgba(0,0,0,.8); color: #fff; padding: 10px 18px; border-radius: 10px; z-index: 99;
  font-size: 14px; max-width: 90vw; text-align: center;
}

/* ==================== 方向引导条（手机：建房/准备竖屏，开局横屏） ==================== */
/* 顶部浮层，不压手牌和碰杠胡按钮；z-index 低于日志遮罩，抽屉打开时被盖住 */
.rot {
  position: fixed; left: 50%; transform: translateX(-50%); top: calc(52px + var(--st));
  z-index: 38; display: flex; align-items: center; gap: 8px;
  max-width: calc(100vw - var(--sl) - var(--sr) - 14px);
  /* 防御：#app 全屏后是 flex 列容器，个别移动浏览器（微信 X5 / 老 WebKit）会把 fixed 的 .rot
     当 flex item 拉伸成整屏大胶囊（用户反馈"弹窗太大"）。钉死 flex 尺寸＝宽高只按内容撑，
     max-height 兜底：无论哪种拉伸都压回胶囊高度，顶边仍贴 top。 */
  flex: 0 0 auto; align-self: center;
  max-height: 88px;
  background: rgba(10,40,26,.95); border: 1px solid rgba(255,214,121,.5); border-radius: 999px;
  padding: 6px 8px 6px 12px; box-shadow: 0 4px 14px rgba(0,0,0,.45);
  color: #ffe9ad; font-size: 13px;
}
.rot .rot-ic { font-size: 17px; line-height: 1; flex: 0 0 auto; animation: rot-nudge 1.5s ease-in-out infinite; }
.rot .rot-tx { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rot .rot-tx.wx { white-space: pre-line; text-overflow: clip; line-height: 1.3; text-align: left; }   /* 微信引导是两句话，放开换行 */
.rot button { flex: 0 0 auto; min-height: 42px; padding: 6px 13px; font-size: 13px; border-radius: 999px; }
.rot .rot-x { background: rgba(255,255,255,.13); color: #f5efe0; }
@keyframes rot-nudge { 0%, 58%, 100% { transform: rotate(0deg); } 18%, 40% { transform: rotate(-88deg); } }
@media (orientation: landscape) and (max-height: 600px) { .rot { top: calc(40px + var(--st)); } }
@media (prefers-reduced-motion: reduce) { .rot .rot-ic { animation: none; } }

/* 横屏手机（非常矮）：再压一档行高 */
/* ==================== 中屏（平板竖屏 / 小笔记本） ==================== */
@media (max-width: 1080px) {
  :root { --tile-w: 44px; --tile-sw: 31px; --bar-w: 20px; }   /* 对手手牌跟着收，7 列仍放得下 */
  .table-wrap { grid-template-columns: 170px minmax(0, 1fr) 170px; }
  .river.q.side { width: 131px; }
  /* 日志改成右侧抽屉（点表头「记录」滑出），窄屏不再常驻侧栏 */
  .glog {
    display: block; top: 0; right: 0; bottom: 0; width: min(80vw, 300px); max-height: none;
    border-radius: 14px 0 0 14px; padding: calc(14px + var(--st)) 12px calc(14px + var(--sb));
    background: rgba(8,32,20,.96); transform: translateX(102%); transition: transform .22s ease;
    font-size: 13px; z-index: 41; box-shadow: -6px 0 18px rgba(0,0,0,.5); opacity: 1;
  }
  .glog.open { transform: none; }
  .gh .hbtn { display: inline-block; }        /* 窄屏出现「牌面大小 / 日志」按钮 */
}

/* 桌面/平板横窗（宽但很矮）：仍走三列牌桌，只缩小牌面并允许牌河滚动 */
@media (orientation: landscape) and (min-width: 1024px) and (max-height: 600px) {
  :root { --tile-w: 40px; --tile-sw: 21px; }
  /* 关键：flex/grid 子项默认 min-height:auto，会被"内容高度"顶住不缩，
     于是整张牌桌比视口高、把"我"的手牌推到屏幕外。这里逐层放开 min-height，让牌河自己滚。 */
  .table-wrap { grid-template-rows: auto minmax(0, 1fr) auto; }
  .felt, .riverrow, .myriver, .river { min-height: 0; }
  .river.q.side { max-height: 100%; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  .river.q.top { max-height: 34%; overflow-y: auto; }
  .myriver { flex: 0 1 auto; }
  .river.q.bottom { max-height: 100%; overflow-y: auto; }
  #me-area { gap: 4px; padding-bottom: 4px; }
  .opp.top { max-height: 84px; overflow-y: auto; }
}

/* ==================== 手机紧凑布局（竖屏手机 + 横屏矮窗共用基础） ==================== */
/* 注意：横屏那条只吃到 1023px，宽而矮的桌面窗口仍走上面的三列牌桌 */
@media (max-width: 760px), (orientation: landscape) and (max-height: 600px) and (max-width: 1023px) {
  :root {
    --tile-w: min(46px, calc((var(--avail-w) - 26px) / 14.9));
    /* 桌上小牌（四条河/副露）：尽量大，但用 min() 钉住"不超过自己手牌的宽度"，免得牌河比手牌还抢眼 */
    /* 应答条带子：四键浮条实测 57px 高 + 6px 间距 */
    --claim-band: 63px;
    --tile-sw: min(var(--tile-w), clamp(20px, 7vw, 30px));
  }
  .view { padding: 0; justify-content: flex-start; }
  /* 内容比视口高时（横屏大厅）用 margin:auto 居中才不会把顶部裁到滚不到 */
  .home-card, .lobby-card { margin: auto; }
  #view-game { height: var(--vh); overflow: hidden; }
  .gh { padding: calc(5px + var(--st)) calc(8px + var(--sr)) 5px calc(8px + var(--sl)); gap: 6px; font-size: 12px; }
  .gh button { min-height: 30px; padding: 4px 9px; font-size: 12px; border-radius: 8px; }
  .gh #btn-leave { color: #ffc9c0; background: rgba(192,86,74,.35); }
  .gh .hbtn { display: inline-block; }
  /* ===== 按方位摆：对家在上、上家在左、下家在右、我在下；四条牌河在中央按同方位排 =====
     中间一行用 minmax(0,1fr) 可压缩，这样对手副露再多也不会把"我"那一行挤掉 */
  .table-wrap {
    display: grid; grid-template-columns: minmax(50px, 20%) minmax(0, 1fr) minmax(50px, 20%);
    grid-template-rows: auto minmax(0, 1fr) auto; gap: 3px;
    height: 100%; min-height: 0; padding: 4px 6px;
    padding-left: calc(6px + var(--sl)); padding-right: calc(6px + var(--sr));
    padding-bottom: calc(4px + var(--sb)); overflow: hidden;
  }
  .opp { min-width: 0; gap: 3px; flex-direction: column; align-items: stretch; padding-top: 2px; }
  .opp.top {
    grid-column: 1 / 4; grid-row: 1;
    /* 对家做成一张"竖着三行"的小卡片：名牌 / 一排牌背 / 副露一行。
       以前是横着 wrap 的一行，0 副露 24px 高、4 副露 58px 高，牌池顶边一局里要从 71 跳到 105；
       现在每一行高度都是定的（副露不换行、超了横滚），面板高度恒定，牌池不再跟着抖。 */
    display: flex; flex-direction: column; align-items: center; flex-wrap: nowrap; gap: 2px;
    max-height: none; overflow: visible;
  }
  .opp.top .who { order: 1; flex-wrap: nowrap; max-width: 100%; font-size: 11px; padding: 2px 7px; }
  .opp.top .who .wn { max-width: 96px; }
  .opp.top .bars {
    order: 2; display: flex; flex-wrap: nowrap; align-items: flex-start; justify-content: center;
    gap: 1px; width: auto; max-width: 100%; height: auto; overflow: hidden;
  }
  .opp.top .bars .tile.small { width: clamp(9px, calc((var(--avail-w) - 60px) / 15), 14px); }
  .opp.top .melds {
    order: 3; flex: 0 0 auto; width: 100%; max-width: 100%;
    height: calc(var(--tile-sw) * 4 / 3 + 8px); min-height: 26px;      /* 没有副露也占住这一行，卡片高度恒定 */
    flex-wrap: nowrap; align-items: flex-start;
    justify-content: flex-start; justify-content: safe center;           /* 放得下就居中，放不下从左边滚起（safe 不会把开头滚丢） */
    overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
  }
  .opp.top .melds::-webkit-scrollbar { display: none; }
  .opp.top .melds .meld-g { flex: 0 0 auto; }
  /* 左右两家：贴着牌桌两侧，纵向排自己那一摞（名牌一行 / 竖着一排牌背 / 副露一摞） */
  #seat-left { grid-column: 1; grid-row: 2; justify-content: flex-start; }
  #seat-right { grid-column: 3; grid-row: 2; justify-content: flex-start; }
  .opp.side { max-height: 100%; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  .opp.side .who .wn { flex: 1 1 100%; }        /* 名字独占一行，别让"机/庄/分/张"随机插到名字后面 */
  .opp.side .bars {
    order: 2; display: flex; flex-direction: column; flex-wrap: nowrap; align-items: flex-start;
    gap: 1px; height: auto; max-width: 100%; overflow: visible;
  }
  .opp.side .bars .tile.small { width: clamp(8px, calc(var(--tile-sw) * 0.52), 12px); }
  .opp.side .melds { order: 3; }
  /* ---- 中央牌池：交叉网格。左右两条河贯穿整张桌（四个角不再空着），
         四条河各自按"槽位"排满，日常一局到头都不用滑动查看。 ---- */
  .felt {
    grid-column: 2; grid-row: 2;                        /* 外层 table-wrap 仍是"左家 | 牌桌 | 右家"三列 */
    gap: 2px; padding: 4px; border-radius: 12px; min-height: 0; overflow: hidden;
    display: grid;
    /* 轨道必须写成"具体能放几张牌"，不能用 auto：auto 轨按 min-content 夹，
       实测会把左右河夹回 1~2 列、上下河夹成 1 行（内容再多也只给一行高，剩下的只能滚）。 */
    grid-template-columns: calc(var(--tile-sw) * 2 + 3px) minmax(0, 1fr) calc(var(--tile-sw) * 2 + 3px);
    grid-template-rows: minmax(calc(var(--tile-sw) * 4 / 3 + 2px), 1fr) auto minmax(calc(var(--tile-sw) * 4 / 3 + 2px), 1fr);
  }
  .riverrow, .myriver { display: contents; }              /* 让三个包裹层退出布局，河直接当网格项摆位 */
  #river-left { grid-column: 1; grid-row: 1 / 4; }
  #river-right { grid-column: 3; grid-row: 1 / 4; }
  .river.q.top { grid-column: 2; grid-row: 1; }
  .mid { grid-column: 2; grid-row: 2; justify-self: center; }
  .river.q.bottom { grid-column: 2; grid-row: 3; }
  .river.q.top, .river.q.bottom {
    width: 100%; max-width: 100%; margin: 0; min-height: calc(var(--tile-sw) * 4 / 3 + 2px);
    overflow-y: auto;                                     /* 安全阀：超出 1fr 那份高度才滚 */
  }
  /* 左右河：竖着排，排满一列再往旁边开一列。竖屏 2 列 × ~15 行 = 30 张，
     比一局里任何人都能打的牌还多 → 正常情况下根本用不到滚动。 */
  .river.q.side {
    width: 100%; min-width: 0; flex: 0 0 auto; max-height: 100%;        /* 桌面那条 240px 的帽子在手机上不适用：这里由轨高定 */
    flex-direction: column; flex-wrap: wrap; align-content: flex-start; justify-content: flex-start;
    max-height: 100%; overflow: hidden; overflow-x: auto;                    /* 超出 30 张才横向滑 */
    touch-action: pan-x; scrollbar-width: none;
  }
  .river.q.side::-webkit-scrollbar { display: none; }
  .river { min-height: 0; }
  .riverrow { min-height: 0; }
  .mid { flex-direction: column; align-items: center; gap: 2px; font-size: 11px; min-width: 0; overflow: hidden; }
  .mid > div { min-width: 0; max-width: 100%; }
  #mid-turn .big-turn { font-size: 12px; text-align: center; }
  #mid-claim { padding: 2px 6px; gap: 4px; font-size: 10px; flex-wrap: wrap; justify-content: center; }   /* 黑盒内距收薄：倒计时旁的黑边不再显粗 */
  #me-area {
    grid-column: 1 / 4; grid-row: 3; gap: 5px; padding: var(--claim-band) 0 0; min-width: 0;
    /* 应答条/倒计时浮动在上面这条带子里（--claim-band），既不压牌河，出现消失也都不动桌面。 */
  }
  .opp .who { font-size: 11px; padding: 3px 6px; gap: 4px; border-radius: 8px; flex-wrap: wrap; }
  .opp .who .wn { max-width: 100%; }
  .opp.top .who .wn { max-width: 84px; }
  .opp .melds { flex: 1 1 auto; flex-direction: row; align-items: flex-start; gap: 3px; min-width: 0; flex-wrap: wrap; }
  .opp.top .melds { max-width: none; justify-content: center; }
  .opp.side .melds { flex-direction: column; align-items: flex-start; }    /* 竖着摞，符合"这一家在自己那一侧" */
  /* 左右栏很窄：杠牌是四张一组，小牌按栏宽再收一点才不会横着溢出 */
  .opp.side .tile.small { width: clamp(13px, calc(5vw - 2px), var(--tile-sw)); }
  .opp .who .cnt { display: inline-block; }   /* 牌背照常显示，名牌上再补一个「N张」计数 */
  .mtag.mtag-gang { display: block; }         /* 触屏没有 hover，杠的种类直接标出（碰已不贴标） */
  #me-area .tile { border-radius: 4px; }
  .tile.drawn { transform: translateY(-3px); }                 /* 牌小，抬少一点，靠金框和牌签说话 */
  .tile.sel { transform: translateY(-10px); }                  /* 必须排在 .drawn 之后，否则选中的牌会被压回去 */
  .tile.drawn::after { font-size: 9px; line-height: 1.45; padding: 0 2px; border-radius: 3px 0 5px 0; }
  #my-hand { flex-wrap: nowrap; padding: 14px 2px 2px; }
  #my-hand.hoverflow { justify-content: flex-start; }   /* 放大到溢出时改左对齐，避免滚不到边 */
  .hint { font-size: 12px; min-height: 15px; }
  .claim-bar { gap: 8px; padding: 7px 10px; border-radius: 14px; }
  .claim-bar button { flex: 0 1 auto; min-width: 66px; padding: 11px 14px; font-size: 15px; border-radius: 10px; }
  .claim-bar .cd { min-width: 38px; padding: 1px 8px; font-size: 14px; background: rgba(255,255,255,.08); }
  /* 结算：底部弹出面板 */
  .mask { align-items: flex-end; padding: 0; }
  .result-card { width: 100%; max-height: 94%; border-radius: 18px 18px 0 0; padding: 16px 12px calc(18px + var(--sb)); }
  .result-card h3 { font-size: 19px; }
  .rrow { padding: 7px 8px; gap: 6px; }
  #toast { top: calc(10px + var(--st)); }
}

/* ==================== 手机横屏：方位照旧，只按"高度稀缺"重调尺寸 ==================== */
/* 之前把三家并成一行、四条河并成一条横向带，省了高度但完全看不出方位，还得额外标"这是谁的河"；
   现在保持 对家在上 / 上家在左 / 下家在右 / 我在下，中央牌河池仍是 上-左-中-右-下 */
@media (orientation: landscape) and (max-height: 600px) and (max-width: 1023px) {
  :root {
    --tile-w: min(40px, calc((var(--avail-w) - 26px) / 14.9));
    --tile-sw: min(var(--tile-w), clamp(17px, 6.6vh, 26px));   /* 横屏按高度定尺，同样不超手牌宽 */
    --claim-band: 46px;                                        /* 横屏浮条只有一行 40px */
  }
  .home-card, .lobby-card { max-width: calc(100vw - var(--sl) - var(--sr) - 8px); }
  .table-wrap {
    grid-template-columns: minmax(54px, 12.5%) minmax(0, 1fr) minmax(54px, 12.5%);
    grid-template-rows: auto minmax(0, 1fr) auto; gap: 2px 4px; padding-top: 3px;
  }
  .opp { gap: 2px; }
  .opp .who { font-size: 10px; padding: 2px 5px; }
  .opp.top .who { font-size: 11px; }                   /* 对家牌子小，名字别比竖屏还细 */
  /* 横屏高度金贵：三家回到"一行式"（副露仍不换行、超了横滚）。对手手牌仍照常显示牌背
     ——v3 曾在这里整排 display:none、名牌上只剩「N张」，应用户要求退回原来的牌背显示；
     高度还是省：牌背比竖屏再收小，对家一小横排，左右两家 3 行一组往旁边开列（整块 ≤50px 高） */
  .opp.top { flex-direction: row; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: 3px 6px; max-height: 74px; }
  .opp.top .bars .tile.small { width: clamp(8px, calc(3.4vh), 13px); }   /* 横屏牌背宽高都按可用高度收小 */
  /* 左右两家的牌背在竖屏是"一整竖列"，横屏那一栏只有两三百 px 高：改成 3 行一组、往旁边开列 */
  .opp.side .bars {
    flex-wrap: wrap; align-content: flex-start; overflow: hidden;
    max-height: calc(clamp(8px, var(--tile-sw) * 0.52, 12px) * 4 / 3 * 3 + 2px);   /* 3 行牌背（3:4）+ 2 条缝 */
  }
  .opp.side .who .wn { flex: 0 1 auto; }                     /* 横屏栏宽 104px，名字可以跟徽章挤在同一行，别占一整行 */
  .opp.top .melds {
    flex-direction: row; flex: 0 1 auto; width: auto; max-width: 100%;
    height: auto; min-height: 0; overflow-x: auto;
  }
  .opp.side .melds { flex-direction: column; }         /* 左右两家各自往下一摞 */
  /* 横屏高度紧、宽度足：左右河从"2 列多行"改成"5 列少行"，轨宽跟着一起放大 */
  .felt { padding: 3px 4px; gap: 2px; border-radius: 10px;
    grid-template-columns: calc(var(--tile-sw) * 5 + 8px) minmax(0, 1fr) calc(var(--tile-sw) * 5 + 8px); }
  .river.q.side { width: calc(var(--tile-sw) * 5 + 8px); }
  /* 河带的高度分配沿用上面紧凑布局那套（上/左/右/下 + 贴底优先保我），这里只改横向排布 */
  .mid { flex-direction: row; gap: 6px 10px; }         /* 中央信息横排，省出一行给牌河 */
  #mid-claim { max-height: 100%; }
  #mid-claim .tile { width: var(--tile-sw); }
  #my-hand { justify-content: center; padding-top: 12px; }
  .claim-bar { padding: 5px 6px; gap: 5px; }
  .claim-bar button { min-height: 38px; padding: 7px 4px; font-size: 14px; }
  .result-card { max-height: 88%; }
  .rrow .tile { width: 24px; }
  /* 横屏高度紧：大厅聊天列表收矮，抽屉窄一点少压牌桌右侧那条河 */
  .chat-list { max-height: 120px; }
  .gchat { width: min(52vw, 300px); }
}

/* 超窄手机（≤380px 竖屏） */
@media (max-width: 380px) {
  :root { --tile-sw: min(var(--tile-w), clamp(18px, 6vw, 24px)); }
  /* 胡/杠/碰/过 + 倒计时在这类屏上要刚好排成一行：
     浮动的应答条一旦换行就有 108px 高（实测 320 宽），会盖掉小半张牌桌。 */
  .claim-bar { gap: 6px; padding: 7px 8px; }
  .claim-bar button { min-width: 56px; padding: 11px 8px; font-size: 14px; }
  .claim-bar .cd { min-width: 34px; padding: 0 4px; font-size: 13px; }
  #mid-wall { display: none; }        /* 小屏高度金贵：余牌头顶已经写着，中央让给"轮到你" */
  .home-card h1 { font-size: 25px; }
  .seats { gap: 6px; }
  .seat { padding: 9px; }
  .code { font-size: 22px; letter-spacing: 3px; }
}

/* 触屏：放大点按目标、禁止长按选中/拖图 */
@media (pointer: coarse) {
  button { min-height: 44px; padding: 10px 16px; }
  .gh button { min-height: 32px; padding: 4px 10px; }
  /* 聊天里的次级键按 36px 一档收着：快捷语两行吃掉半屏就不叫聊天了（发送/🎤 仍走全局 44px） */
  .qchip { min-height: 34px; padding: 6px 12px; }
  .vmsg { min-height: 36px; }
  input, select { font-size: 16px; }          /* <16px 时 iOS 聚焦会强制放大整页 */
  .invite input { font-size: 13px; }
  #view-game { user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: manipulation; }
  .river { touch-action: pan-y; }
  .tile.sel { transform: translateY(-9px); }
  .mtag.mtag-gang { display: block; }
  .home-card, .lobby-card { width: min(94vw, 420px); }
}

/* 极矮横屏（多数手机横屏 380~430px）：再把行高压一档 */
@media (orientation: landscape) and (max-height: 460px) and (max-width: 1023px) {
  :root { --tile-w: min(34px, calc((var(--avail-w) - 26px) / 14.9)); --tile-sw: min(var(--tile-w), clamp(17px, 6vh, 23px)); }
  .gh { font-size: 11px; padding-top: calc(2px + var(--st)); padding-bottom: 2px; }
  .gh button { min-height: 26px; padding: 2px 7px; font-size: 11px; }
  .opp.top { max-height: 58px; }
  .opp .who { font-size: 10px; padding: 2px 6px; }
  .felt { padding: 4px; gap: 2px; }                     /* 极矮横屏只再收一点内边距；轨宽沿用上面横屏的"左右 5 列" */
  #me-area { gap: 3px; }
  #my-hand { padding-top: 10px; }
  .hint { display: none; }
  .claim-bar { padding: 3px 6px; }
  .claim-bar button { min-height: 34px; padding: 5px 3px; font-size: 13px; }
  .result-card { max-height: 96%; }
}

/* ≤380 高的横屏手机（640×360 这类）：上下两条河只剩两行高，
   左右各让出一列给中间 —— 中间太窄时每行只放 9 张，24 张要排三行就装不下了。 */
@media (orientation: landscape) and (max-height: 380px) and (max-width: 1023px) {
  .felt { grid-template-columns: calc(var(--tile-sw) * 4 + 7px) minmax(0, 1fr) calc(var(--tile-sw) * 4 + 7px); }
  .river.q.side { width: calc(var(--tile-sw) * 4 + 7px); }
}
