/* ============================================================
   棋牌房 · 样式体系
   设计语言：墨绿桌呢 + 木框 + 鎏金点缀 + 毛玻璃面板
   动效原则：只在该被看见的地方动（出牌、炸弹、胡牌、轮次）
   ============================================================ */

:root {
  --felt: #0d3b2e;
  --felt-2: #12523f;
  --felt-3: #0a2c22;
  --wood: #3a2417;
  --wood-2: #54351f;
  --gold: #f0c674;
  --gold-2: #c9983f;
  --ink: #10231d;
  --paper: #f7f3e8;
  --red: #c0392b;
  --blue: #4f9cf5;
  --green: #46d38a;
  --purple: #b48ce8;
  --text: #ecf5f0;
  --muted: #9dbcae;
  --panel: rgba(255, 255, 255, 0.06);
  --panel-2: rgba(0, 0, 0, 0.28);
  --panel-br: rgba(255, 255, 255, 0.12);
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  --radius: 14px;
  --ease-pop: cubic-bezier(0.2, 1.4, 0.4, 1);
  --card-w: 58px;
  --card-h: 82px;
  --tile-w: 40px;
  --tile-h: 54px;
  /* 手牌压叠：每张露出多宽。跟着牌宽走，小屏也不会挤成细条 */
  --card-slice: 24px;
  --card-slice-joker: 38px;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', system-ui, -apple-system, sans-serif;
  color: var(--text);
  background:
    radial-gradient(1200px 700px at 50% -10%, var(--felt-2), var(--felt) 55%, var(--felt-3)) fixed;
  -webkit-font-smoothing: antialiased;
  /* 牌桌上禁止整页橡皮筋，聊天记录等内滚区不受影响 */
  overscroll-behavior: none;
}

button,
input {
  font-family: inherit;
}

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

.app {
  min-height: 100vh;
  min-height: 100dvh;
  /* 定高而不是 min-height：牌桌必须正好占满可视区，多余内容交给 .stage 内滚，
     否则 360~430 高的横屏手机会把桌面底边（含退出/声音按钮）顶到屏幕外 */
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
}

.hidden {
  display: none !important;
}

/* ---------- top bar ---------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 20px;
  padding-top: calc(10px + env(safe-area-inset-top));
  border-bottom: 1px solid var(--panel-br);
  background: rgba(0, 0, 0, 0.22);
  backdrop-filter: blur(8px);
  position: sticky;
  top: 0;
  z-index: 30;
}

.brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}

.brand-mark {
  color: var(--gold);
  font-size: 22px;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
}

.brand-text {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 2px;
  white-space: nowrap;
}

.brand-sub {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.conn {
  font-size: 12px;
  flex: 0 0 auto;
}

.conn-on {
  color: var(--green);
  animation: breathe 2.4s ease-in-out infinite;
}

.conn-off {
  color: #e05b5b;
}

.conn-text {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}

.user-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--panel-br);
  font-size: 13px;
  max-width: 40vw;
}

.user-chip b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.user-chip button {
  background: none;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  font-size: 12px;
  padding: 2px 4px;
}

.user-chip button:hover {
  color: var(--gold);
}

/* ---------- layout ---------- */
.stage {
  flex: 1;
  min-height: 0;
  padding: 22px;
  max-width: 1240px;
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  overflow: auto; /* .app 定高后，超出的内容在这层滚，不撑大文档 */
  overscroll-behavior: contain;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--panel-br);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: var(--shadow);
  backdrop-filter: blur(4px);
}

.panel-title {
  margin: 0 0 12px;
  font-size: 15px;
  letter-spacing: 1px;
  color: var(--gold);
}

.screen {
  animation: fade 0.25s ease;
}

@keyframes fade {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

@keyframes breathe {
  50% {
    opacity: 0.55;
  }
}

/* ---------- buttons ---------- */
.btn {
  padding: 9px 16px;
  border-radius: 10px;
  border: 1px solid var(--panel-br);
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
  cursor: pointer;
  font-size: 14px;
  white-space: nowrap;
  transition: transform 0.08s ease, filter 0.15s ease, border-color 0.15s ease;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.btn:hover:not(:disabled) {
  filter: brightness(1.18);
}

.btn:active:not(:disabled) {
  transform: translateY(1px) scale(0.98);
}

.btn:disabled {
  opacity: 0.42;
  cursor: not-allowed;
}

.btn.primary {
  background: linear-gradient(180deg, var(--gold), var(--gold-2));
  border-color: var(--gold-2);
  color: #2a1c06;
  font-weight: 700;
  box-shadow: 0 2px 8px rgba(240, 198, 116, 0.25);
}

.btn.ghost {
  background: transparent;
}

.btn.danger {
  background: rgba(192, 57, 43, 0.85);
  border-color: #8f2d22;
  color: #fff;
}

.btn.block {
  width: 100%;
  padding: 12px;
  font-size: 15px;
}

.btn.tiny {
  padding: 4px 10px;
  font-size: 12px;
  border-radius: 8px;
}

.btn.big {
  padding: 12px 22px;
  font-size: 16px;
  font-weight: 700;
  border-radius: 12px;
}

.btn.warn {
  background: linear-gradient(180deg, #f2d08a, #d0a54a);
  border-color: #b08b33;
  color: #2a1c06;
  font-weight: 700;
}

.btn.emoji {
  font-size: 15px;
  padding: 4px 8px;
  line-height: 1.2;
}

/* ---------- auth ---------- */
.auth-panel {
  max-width: 380px;
  margin: 40px auto;
}

.tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.tab {
  flex: 1;
  padding: 9px;
  border-radius: 10px;
  border: 1px solid var(--panel-br);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 14px;
}

.tab.active {
  background: var(--gold);
  border-color: var(--gold);
  color: #2a1c06;
  font-weight: 700;
}

.form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  color: var(--muted);
}

input {
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--panel-br);
  background: rgba(0, 0, 0, 0.25);
  color: var(--text);
  font-size: 15px;
  outline: none;
}

input:focus {
  border-color: var(--gold);
}

.hint {
  font-size: 12px;
  color: var(--muted);
  margin: 14px 0 0;
  line-height: 1.6;
}

/* ---------- lobby ---------- */
.lobby-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
  margin-bottom: 20px;
}

.game-card {
  position: relative;
  overflow: hidden;
  padding: 18px;
  border-radius: var(--radius);
  border: 1px solid var(--panel-br);
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.1), rgba(0, 0, 0, 0.22));
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.15s ease, box-shadow 0.15s ease;
  display: flex;
  flex-direction: column;
}

.game-card::after {
  /* 斜切高光：hover 时像牌面反光一样扫过 */
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(255, 255, 255, 0.14) 45%, transparent 60%);
  transform: translateX(-100%);
  transition: transform 0.5s ease;
  pointer-events: none;
}

.game-card:hover {
  transform: translateY(-3px);
  border-color: var(--gold);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
}

.game-card:hover::after {
  transform: translateX(100%);
}

.game-card:active {
  transform: translateY(-1px) scale(0.995);
}

.game-card h3 {
  margin: 0 0 6px;
  font-size: 19px;
  color: var(--gold);
  letter-spacing: 1px;
}

.game-card p {
  margin: 0;
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.6;
  min-height: 40px;
}

.game-card .gc-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 10px;
}

.game-card .gc-badge {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.3);
  color: var(--muted);
}

.game-card .gc-icon {
  width: 46px;
  height: 46px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  font-size: 22px;
  color: #2a1c06;
  background: linear-gradient(160deg, var(--gold), var(--gold-2));
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.18), 0 4px 10px rgba(0, 0, 0, 0.3);
}

.game-card .gc-foot {
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.game-card .gc-go {
  margin-left: auto;
  font-size: 12px;
  color: var(--muted);
}

.lobby-side {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.join-row {
  display: flex;
  gap: 10px;
}

.code-input {
  flex: 1;
  text-transform: uppercase;
  letter-spacing: 4px;
  font-size: 18px;
  text-align: center;
}

.stats-row {
  display: grid;
  grid-template-columns: 52px 1fr 44px auto;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--panel-br);
  font-size: 13px;
}

.stats-row:last-of-type {
  border: 0;
}

.stats-row b {
  color: var(--gold);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.stats-name {
  color: var(--text);
}

.stats-bar {
  height: 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.stats-bar i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--gold-2), var(--gold));
  transition: width 0.6s var(--ease-pop);
}

/* ---------- room ---------- */
.room-panel {
  max-width: 760px;
  margin: 0 auto;
}

.room-head {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--panel-br);
}

.room-name {
  font-size: 20px;
  font-weight: 700;
  color: var(--gold);
}

.room-code-box {
  margin-top: 8px;
  font-size: 13px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 8px;
}

.room-code-box b {
  font-size: 24px;
  letter-spacing: 6px;
  color: #fff;
  font-family: ui-monospace, Menlo, monospace;
}

.room-rules {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.8;
  text-align: right;
}

.seats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin: 18px 0;
}

.seat {
  padding: 14px;
  border-radius: 12px;
  border: 1px dashed var(--panel-br);
  background: rgba(0, 0, 0, 0.18);
  text-align: center;
  transition: border-color 0.2s ease, transform 0.15s ease;
}

.seat.filled {
  border-style: solid;
  border-color: var(--gold-2);
}

.seat.mine {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(240, 198, 116, 0.35), 0 6px 18px rgba(0, 0, 0, 0.3);
}

.seat.ready {
  animation: readyGlow 1.8s ease-in-out infinite;
}

@keyframes readyGlow {
  50% {
    border-color: var(--green);
  }
}

.seat-empty {
  color: var(--muted);
  font-size: 13px;
}

.seat-name {
  font-size: 15px;
  font-weight: 600;
  margin-bottom: 6px;
  word-break: break-all;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.seat-tags {
  display: flex;
  gap: 6px;
  justify-content: center;
  flex-wrap: wrap;
}

.tag {
  font-size: 11px;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: var(--muted);
}

.tag.ready {
  background: #1c7a4d;
  color: #eafff3;
}

.tag.bot {
  background: #3a5a80;
  color: #e8f2ff;
}

.tag.host {
  background: var(--gold);
  color: #2a1c06;
}

.tag.off {
  background: #8c3b3b;
  color: #ffecec;
}

.tag.me {
  background: var(--gold);
  color: #2a1c06;
  font-weight: 700;
}

/* 头像徽标：房间列表、牌桌座位、结算表共用。seat-N 只是配色，不代表座位强弱。 */
.avatar {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: 11.5px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.5px;
  color: var(--ink);
  background: var(--gold-2);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.3),
    0 1px 3px rgba(0, 0, 0, 0.4);
  user-select: none;
}

.avatar.seat-0 {
  background: #f0c674;
}

.avatar.seat-1 {
  background: #7fd0a0;
}

.avatar.seat-2 {
  background: #8ec5f0;
}

.avatar.seat-3 {
  background: #e2a6df;
}

/* 回合倒计时环：startClock 往 .hand 上写 --p（剩余百分比），转完一圈就该出牌了 */
.clock {
  position: relative;
  display: inline-block;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.32);
  box-shadow: inset 0 0 0 1px var(--panel-br);
}

.clock .hand {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(var(--gold) calc(var(--p, 100) * 1%), rgba(255, 255, 255, 0.08) 0);
  -webkit-mask: radial-gradient(circle, transparent 0 54%, #000 56%);
  mask: radial-gradient(circle, transparent 0 54%, #000 56%);
}

.clock.urgent .hand {
  background: conic-gradient(var(--red) calc(var(--p, 100) * 1%), rgba(255, 255, 255, 0.08) 0);
}

.room-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}

.room-actions-main {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  width: 100%;
}

.room-actions-main .btn.big {
  min-width: 128px;
}

/* ---------- table shell ---------- */
/* 牌桌/球台一律填满 .stage 剩下的空间：桌面高度由视口决定，内部各排自适应收缩，
   这样任何分辨率都不会把某一排顶到屏幕外。 */
#screen-table:not(.hidden) {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.table-wrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
}

.table {
  position: relative;
  width: 100%;
  /* 高度跟着视口走：牌河变长只在桌内裁切/滚动，绝不上顶手牌 */
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  border-radius: 44% / 34%;
  background:
    radial-gradient(120% 120% at 50% 18%, #17604a, var(--felt) 60%, var(--felt-3));
  border: 10px solid var(--wood);
  box-shadow:
    inset 0 0 60px rgba(0, 0, 0, 0.45),
    inset 0 2px 0 rgba(255, 255, 255, 0.06),
    0 0 0 1px rgba(0, 0, 0, 0.5),
    var(--shadow);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: visible;
}

/* 桌呢细噪点：静态平铺，几乎零开销，去掉"塑料感" */
.table::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.05;
  pointer-events: none;
}

.table-info {
  align-self: center;
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 12px;
  color: var(--muted);
  background: rgba(0, 0, 0, 0.3);
  padding: 5px 14px;
  border-radius: 999px;
  position: relative;
  z-index: 1;
}

.table-info b {
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.table-tools {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  position: relative;
  z-index: 1;
}

.tools-left,
.tools-right {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}

.emote-bar {
  display: flex;
  gap: 4px;
}

/* 座位牌：对家/上家的昵称、标签、剩余张数与倒计时都挂在这里。
   基础尺寸是桌面档，小屏与横屏各自在 @media 里压缩。 */
.player {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  min-width: 104px;
  max-width: 220px;
  padding: 7px 10px;
  border-radius: 12px;
  background: rgba(5, 24, 18, 0.46);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
  font-size: 12px;
  color: var(--text);
  transition:
    box-shadow 0.18s var(--ease-pop),
    background 0.18s;
}

.player.active {
  background: rgba(11, 44, 33, 0.72);
  box-shadow:
    inset 0 0 0 1px rgba(240, 198, 116, 0.5),
    0 0 16px rgba(240, 198, 116, 0.16);
}

.player.me {
  background: rgba(8, 34, 25, 0.6);
}

.player.won {
  opacity: 0.82;
}

.player.team-0 {
  box-shadow: inset 3px 0 0 0 #f0c674;
}

.player.team-1 {
  box-shadow: inset 3px 0 0 0 #8ec5f0;
}

.player.active.team-0 {
  box-shadow:
    inset 3px 0 0 0 #f0c674,
    0 0 16px rgba(240, 198, 116, 0.18);
}

.player.active.team-1 {
  box-shadow:
    inset 3px 0 0 0 #8ec5f0,
    0 0 16px rgba(142, 197, 240, 0.18);
}

.p-name {
  display: flex;
  align-items: center;
  gap: 5px;
  font-weight: 700;
  min-width: 0;
}

.p-name > span:nth-child(2) {
  max-width: 11ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.p-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.p-count {
  color: var(--muted);
  font-size: 11px;
}

.p-last {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-top: 2px;
}

/* 麻将座位上的吃/碰/杠：一排排往下叠 */
.p-melds {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin-top: 5px;
}

.emote-bubble {
  align-self: flex-start;
  margin-top: 3px;
  padding: 2px 9px;
  border-radius: 12px 12px 12px 3px;
  background: var(--paper);
  color: var(--ink);
  font-size: 15px;
  line-height: 1.4;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}

/* Seats, centre and the discard rivers live in one grid: nothing is absolutely
   positioned any more, so panels can never land on top of the hand. */
.felt-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(116px, 0.85fr) minmax(0, 3fr) minmax(116px, 0.85fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    '. top .'
    'left center right'
    '. bottom .';
  gap: 10px;
  position: relative;
  z-index: 1;
}

.cell {
  min-width: 0;
  min-height: 0;
}

.cell-top {
  grid-area: top;
  justify-self: center;
}

.cell-left {
  grid-area: left;
  align-self: center;
}

.cell-right {
  grid-area: right;
  align-self: center;
}

.cell-bottom {
  grid-area: bottom;
  justify-self: center;
}

.cell-center {
  grid-area: center;
  display: flex;
  align-items: stretch;
  justify-content: center;
  /* 不裁：中间行被压矮时（360x640 上只有 61px，中央展示要 88px），
     裁掉的就是别人刚出的牌。宁可溢出压到上下两格的空白区，也不能丢信息。
     手牌那层 z-index 更高，永远盖得住。 */
  overflow: visible;
  position: relative;
}

.center-slot {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ---------- hand + cards ---------- */
.hand-wrap {
  margin-top: auto;
  padding-top: 10px;
  padding-bottom: env(safe-area-inset-bottom);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  position: relative;
  z-index: 2;
}

/* 扑克压叠只露左上角点数（QQ 斗地主式）；麻将不叠，每张铺开 */
.hand-area .mj + .mj {
  margin-left: 2px;
}

.hand-area .pc + .pc {
  margin-left: calc(var(--card-slice) - var(--card-w));
}

/* 「10」是两位数，按普通压叠宽度只够露出那个「1」（截图里就是这张牌看不清）。
   给它后面那张多让 7px，10 就整张露出来；整行变宽后装不下时手牌自己换行。 */
.hand-area .pc[data-rank='10'] + .pc {
  margin-left: calc(var(--card-slice) + 7px - var(--card-w));
}

/* 10 用横向压缩而不是缩字号：各档 font-size 规则各有归属（桌面 19px、手机 14px），
   这里抢不过也不该抢；transform 不改占位宽度，所以整行不会因此变长。 */
.hand-area .pc[data-rank='10'] .r {
  transform: scaleX(0.9);
  transform-origin: left top;
  letter-spacing: -0.5px;
}

/* 滑动选牌靠 pointer 拖拽，必须让浏览器别把这套动作当成手势/缩放 */
.hand-area .pc,
.hand-area .mj {
  touch-action: none;
}

/* 手牌永远是"右边压左边"：选中的牌只抬起、不提到最上层。
   给它加 z-index 会让抬起的牌盖住右边那张的露出区，压在下方的牌就点不动了。 */
.hand-area .pc {
  z-index: 1;
}

.hand-area .draw-gap {
  margin-left: 6px;
}

.voice-box {
  display: flex;
  align-items: center;
  gap: 6px;
}

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

.counter.collapsed {
  margin-right: auto;
}

.cc-row {
  display: flex;
  gap: 3px;
  flex-wrap: wrap;
}

.cc {
  min-width: 26px;
  padding: 2px 3px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.32);
  border: 1px solid var(--panel-br);
  text-align: center;
  line-height: 1.15;
}

.cc .cc-r {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: var(--gold);
}

.cc .cc-n {
  display: block;
  font-size: 11px;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

.cc.spent {
  opacity: 0.34;
}

.hand-area {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0;
  padding: 6px 0 2px;
  min-height: 88px;
}

.pc {
  position: relative;
  width: var(--card-w);
  height: var(--card-h);
  border-radius: 7px;
  background: linear-gradient(180deg, #fffef8, #ece6d6);
  border: 1px solid #b9ad93;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
  color: var(--ink);
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform 0.1s ease, box-shadow 0.1s ease;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 4px 5px;
  font-family: ui-rounded, 'PingFang SC', system-ui, sans-serif;
}

/* 触屏没有真正的悬停：点一下 :hover 会粘在那张牌上，看着就像被选中了。
   抬起效果只在真有指针悬停的设备上给。 */
@media (hover: hover) {
  .pc:hover {
    transform: translateY(-3px);
  }
}

.pc.sel {
  transform: translateY(-14px);
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.45), 0 0 0 2px var(--gold);
}

.pc.red {
  color: var(--red);
}

.pc .r {
  font-size: 19px;
  font-weight: 800;
  line-height: 1;
}

.pc .s {
  font-size: 15px;
  line-height: 1;
}

.pc .big {
  position: absolute;
  right: 4px;
  bottom: 2px;
  font-size: 22px;
  opacity: 0.22;
}

.pc.joker {
  background: linear-gradient(180deg, #fff8e6, #f2e2bd);
}

/* 王：单字横排，与普通牌占地一致，叠牌时不再探出一大条 */
.pc.joker .r {
  font-size: 19px;
  font-weight: 800;
}

.pc.joker .s {
  font-size: 16px;
}

/* 大小王也是普通占地：不再给后一张额外留距（王已是单字，够窄） */
.hand-area .pc.joker + .pc {
  margin-left: calc(var(--card-slice) - var(--card-w));
}

.pc.back {
  background: repeating-linear-gradient(45deg, #7b2d2d, #7b2d2d 6px, #943b3b 6px, #943b3b 12px);
  border-color: #5c2020;
  cursor: default;
}

.pc.mini {
  width: 34px;
  height: 48px;
  padding: 2px 3px;
  border-radius: 5px;
  cursor: default;
}

.pc.mini .r {
  font-size: 13px;
}

.pc.mini .s {
  font-size: 11px;
}

.pc.dim {
  opacity: 0.45;
}

/* 掼蛋逢人配：鎏金描边 + 右上角「配」标 */
.pc.wild {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(240, 198, 116, 0.55), 0 2px 6px rgba(0, 0, 0, 0.35);
}

.wild-badge {
  position: absolute;
  top: -7px;
  right: -6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--gold), var(--gold-2));
  color: #2a1c06;
  font-size: 10px;
  font-weight: 800;
  display: grid;
  place-items: center;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4);
}

/* 最新摸进的牌：一圈稳定金边。以前是 1.1s 无限呼吸，盯着手牌看非常晃眼 */
.pc.hot,
.mj.hot {
  outline: 2px solid var(--gold);
  outline-offset: 1px;
}

/* 发牌动效：新局第一帧，手牌依次落入手中 */
.pc.deal-in,
.mj.deal-in {
  animation: dealIn 0.34s var(--ease-pop) backwards;
}

@keyframes dealIn {
  from {
    opacity: 0;
    transform: translateY(26px) rotate(-7deg) scale(0.92);
  }
}

.group {
  display: flex;
  gap: 2px;
  padding: 3px 5px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.22);
  margin: 2px 0;
}

/* ---------- mahjong tile ---------- */
.mj {
  position: relative;
  width: var(--tile-w);
  height: var(--tile-h);
  border-radius: 5px;
  background: linear-gradient(180deg, #fdfdf6, #e6e2d2);
  border: 1px solid #a89e85;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.35), inset 0 -4px 0 rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform 0.1s ease;
  font-weight: 700;
}

.mj svg {
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

@media (hover: hover) {
  .mj:hover {
    transform: translateY(-3px);
  }
}

.mj.sel {
  transform: translateY(-13px);
  box-shadow: 0 0 0 2px var(--gold), 0 5px 10px rgba(0, 0, 0, 0.4);
}

.mj.back {
  background: linear-gradient(180deg, #2f7d63, #1c5643);
  border-color: #123b2e;
  cursor: default;
}

.mj.mini {
  width: 26px;
  height: 36px;
  border-radius: 4px;
  cursor: default;
}

.mj.dim {
  opacity: 0.5;
}

.mj-center {
  width: 100%;
  max-width: 620px;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  justify-content: center;
  justify-content: safe center;
  /* 牌河随对局变长：桌内滚动保持最新可见，绝不撑高桌面 */
  overflow-y: auto;
  scrollbar-width: none;
}

.mj-center::-webkit-scrollbar {
  display: none;
}

.rivers {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

.river {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  align-content: flex-start;
  padding: 4px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.22);
  min-height: 44px;
}

.river .who {
  width: 100%;
  font-size: 10px;
  color: var(--muted);
}

.river.mine {
  outline: 1px solid rgba(240, 198, 116, 0.45);
}

.center-box {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  /* 中央区也给个恒定高度：出牌前后只是内容换血，盒子本身不位移 */
  min-height: 112px;
}

.last-play {
  display: flex;
  gap: 3px;
  justify-content: center;
  margin-top: 4px;
}

/* 每出一手，中央牌组弹一下：反馈"这手牌落桌了" */
.last-play.pop {
  animation: popIn 0.28s var(--ease-pop);
}

@keyframes popIn {
  from {
    transform: scale(0.72);
    opacity: 0.4;
  }
}

.pass-tag {
  font-size: 12px;
  color: var(--muted);
  background: rgba(0, 0, 0, 0.3);
  padding: 3px 10px;
  border-radius: 999px;
}

/* ---------- action bar ---------- */
/* 高度恒定是硬约束：等别人出牌时是文字、轮到自己时是按钮，
   两者高度必须一致，否则手牌会被顶得上蹿下跳。 */
.action-bar {
  position: relative;
  display: flex;
  gap: 10px;
  justify-content: center;
  align-items: center;
  /* 按钮多时换行而不是横向滚：居中 + overflow:auto 的组合会把最左那颗推出容器，
     既看不见也滚不回来（360 宽的手机上"出牌/不出"就是被这样吃掉 16px） */
  flex-wrap: wrap;
  overflow: visible;
  padding: 12px;
  margin: 0 auto;
  min-height: 74px;
  background: rgba(0, 0, 0, 0.42);
  border: 1px solid var(--panel-br);
  border-radius: 14px;
  max-width: 860px;
  width: 100%;
}

.action-bar::-webkit-scrollbar {
  display: none;
}

/* 可碰/杠/胡的响应窗：金色呼吸边，别让玩家错过该抢的牌 */
.action-bar.hot {
  border-color: var(--gold);
  animation: hotGlow 1.2s ease-in-out infinite;
}

@keyframes hotGlow {
  50% {
    box-shadow: 0 0 0 4px rgba(240, 198, 116, 0.18);
  }
}

/* 剩余时间条：贴在行动条顶边 */
.timebar {
  position: absolute;
  top: 0;
  left: 10px;
  right: 10px;
  height: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  overflow: hidden;
}

.timebar i {
  display: block;
  height: 100%;
  width: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--gold-2), var(--gold));
}

.timebar.urgent i {
  background: linear-gradient(90deg, #b04a4a, #e05b5b);
  animation: urgentBlink 0.7s steps(2) infinite;
}

.muted {
  color: var(--muted);
  font-size: 12px;
}

/* ---------- chat / toasts / modal ---------- */
.chat-dock {
  position: fixed;
  left: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom));
  right: auto;
  width: 240px;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

/* 默认只留一颗按钮，点开才出现聊天记录和输入框 */
.chat-dock .chat-box {
  display: none;
  width: 100%;
}

.chat-dock.open .chat-box {
  display: flex;
}

.chat-toggle {
  white-space: nowrap;
}

.chat-box {
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--panel-br);
  border-radius: 12px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  backdrop-filter: blur(6px);
}

.chat-log {
  max-height: 150px;
  overflow-y: auto;
  font-size: 12px;
  line-height: 1.6;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.chat-log .who {
  color: var(--gold);
}

.chat-phrases {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: thin;
}

.chip {
  flex: 0 0 auto;
  font-size: 11.5px;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--panel-br);
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.chip:hover {
  border-color: var(--gold);
  color: var(--gold);
}

.chat-row {
  display: flex;
  gap: 6px;
}

.chat-row input {
  flex: 1;
  padding: 7px 9px;
  font-size: 13px;
}

.toasts {
  position: fixed;
  top: 66px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 60;
  pointer-events: none;
  width: max-content;
  max-width: 92vw;
}

.toast {
  padding: 9px 18px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.72);
  border: 1px solid var(--panel-br);
  font-size: 13.5px;
  animation: pop 0.2s ease;
  box-shadow: var(--shadow);
  text-align: center;
}

.toast.err {
  border-color: #b04a4a;
  color: #ffd8d8;
}

.toast.ok {
  border-color: var(--gold);
  color: var(--gold);
}

@keyframes pop {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
}

.modal-mask {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.62);
  display: grid;
  place-items: center;
  z-index: 70;
  padding: 20px;
  animation: fade 0.18s ease;
  backdrop-filter: blur(2px);
}

/* 弹窗外壳：只给右上角的关闭叉定位，本身不参与滚动，所以叉不会被内容滚走 */
.modal-shell {
  position: relative;
}

.modal-x {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--gold-2);
  background: rgba(0, 0, 0, 0.5);
  color: var(--gold);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
}

.modal-x:hover {
  background: rgba(0, 0, 0, 0.72);
}

.modal {
  width: min(560px, 94vw);
  max-height: 84vh;
  max-height: 84dvh; /* 系统栏瞬时显隐时弹层会比实际可视区高，滚动条落在看不见的地方 */
  overflow: auto;
  background: linear-gradient(180deg, #14402f, #0c2a20);
  border: 1px solid var(--gold-2);
  border-radius: 16px;
  padding: 22px;
  box-shadow: var(--shadow);
  animation: modalIn 0.24s var(--ease-pop);
}

@keyframes modalIn {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.96);
  }
}

.modal h2 {
  margin: 0 0 14px;
  color: var(--gold);
  font-size: 20px;
  text-align: center;
}

/* ---------- results ---------- */
.result-banner {
  text-align: center;
  padding: 16px 12px 14px;
  border-radius: 14px;
  margin-bottom: 16px;
  border: 1px solid var(--panel-br);
  animation: bannerPop 0.4s var(--ease-pop);
}

.result-banner.win {
  background:
    radial-gradient(120% 140% at 50% 0%, rgba(240, 198, 116, 0.32), rgba(240, 198, 116, 0.06) 60%),
    linear-gradient(180deg, rgba(70, 211, 138, 0.18), transparent);
  border-color: rgba(240, 198, 116, 0.55);
}

.result-banner.lose {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), transparent);
}

@keyframes bannerPop {
  from {
    transform: scale(0.8);
    opacity: 0;
  }
}

.result-banner-title {
  font-size: 26px;
  font-weight: 900;
  letter-spacing: 2px;
  color: var(--gold);
  text-shadow: 0 2px 12px rgba(240, 198, 116, 0.35);
}

.result-banner.lose .result-banner-title {
  color: var(--muted);
  text-shadow: none;
}

.result-banner-score {
  margin-top: 6px;
  font-size: 13px;
  color: var(--muted);
}

.result-score {
  font-size: 22px;
  font-variant-numeric: tabular-nums;
}

.result-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  margin-bottom: 16px;
}

.result-table th,
.result-table td {
  padding: 9px 8px;
  border-bottom: 1px solid var(--panel-br);
  text-align: left;
}

.result-table th {
  color: var(--muted);
  font-weight: 500;
  font-size: 12px;
}

.result-table tr.me {
  background: rgba(240, 198, 116, 0.1);
}

.result-table tr.me td {
  border-bottom-color: rgba(240, 198, 116, 0.3);
}

.result-table td .avatar {
  margin-right: 7px;
  vertical-align: middle;
}

.result-table td .tag.me {
  margin-left: 7px;
}

.score-plus {
  color: #5ee39a;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.score-minus {
  color: #ff8f8f;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.medal {
  margin-left: 6px;
}

.fan-list {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.fan-chip {
  font-size: 11.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(240, 198, 116, 0.16);
  border: 1px solid rgba(240, 198, 116, 0.4);
  color: var(--gold);
}

.fan-chip.warn {
  background: rgba(224, 91, 91, 0.16);
  border-color: rgba(224, 91, 91, 0.45);
  color: #ffb4b4;
}

.conflict {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 6px;
  flex-wrap: wrap;
}

/* ---------- rules modal ---------- */
.rules-tagline {
  text-align: center;
  color: var(--muted);
  font-size: 13px;
  margin: -6px 0 14px;
}

.rules-body h4 {
  margin: 14px 0 6px;
  color: var(--gold);
  font-size: 14px;
}

.rules-list {
  margin: 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.75;
  color: var(--text);
}

.rules-list li {
  margin: 3px 0;
}

.rules-list kbd {
  display: inline-block;
  min-width: 20px;
  text-align: center;
  padding: 1px 6px;
  margin-right: 7px;
  border-radius: 6px;
  border: 1px solid var(--panel-br);
  border-bottom-width: 2px;
  background: rgba(255, 255, 255, 0.1);
  font-family: ui-monospace, Menlo, monospace;
  font-size: 11.5px;
  color: var(--gold);
}

/* ---------- FX layer（震屏 / 横幅 / 彩带） ---------- */
#fx-layer {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 90;
  overflow: hidden;
}

.fx-banner {
  position: absolute;
  top: 34%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: clamp(30px, 9vw, 62px);
  font-weight: 900;
  letter-spacing: 4px;
  color: var(--gold);
  text-shadow:
    0 0 18px rgba(240, 198, 116, 0.55),
    0 3px 0 rgba(0, 0, 0, 0.35);
  animation: bannerFx 1.5s cubic-bezier(0.2, 1.2, 0.3, 1) forwards;
  white-space: nowrap;
}

.fx-banner.boom {
  color: #ff8a5c;
  text-shadow: 0 0 22px rgba(255, 110, 60, 0.65), 0 3px 0 rgba(0, 0, 0, 0.4);
}

.fx-banner.lose {
  color: #b9c8c0;
  text-shadow: none;
}

.fx-banner.level {
  color: #8fe6b4;
  text-shadow: 0 0 18px rgba(70, 211, 138, 0.5), 0 3px 0 rgba(0, 0, 0, 0.35);
}

@keyframes bannerFx {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
  }
  16% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.12);
  }
  28% {
    transform: translate(-50%, -50%) scale(1);
  }
  76% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -58%) scale(1.02);
  }
}

.fx-flash {
  position: absolute;
  inset: 0;
  background: radial-gradient(60% 50% at 50% 44%, rgba(255, 220, 140, 0.32), transparent 70%);
  animation: flashOut 0.6s ease forwards;
}

.fx-flash.gold {
  background: radial-gradient(70% 60% at 50% 44%, rgba(240, 198, 116, 0.38), transparent 72%);
}

@keyframes flashOut {
  to {
    opacity: 0;
  }
}

.fx-confetti {
  position: absolute;
  top: -18px;
  width: 8px;
  height: 12px;
  border-radius: 2px;
  opacity: 0.95;
  animation-name: confettiFall;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}

@keyframes confettiFall {
  to {
    transform: translate(var(--x, 0), 105vh) rotate(var(--r, 360deg));
    opacity: 0.15;
  }
}

/* 震屏：炸弹/王炸时整桌晃一下 */
.shake {
  animation: shakeIt 0.5s ease-in-out;
}

@keyframes shakeIt {
  10%,
  50% {
    transform: translate(-3px, 2px);
  }
  30%,
  70% {
    transform: translate(3px, -2px);
  }
  90% {
    transform: translate(-1px, 1px);
  }
}

#room-code {
  user-select: all;
  cursor: pointer;
}

/* ============================================================
   响应式
   ============================================================ */

/* ---- 平板 / 小笔记本 ---- */
@media (max-width: 1080px) {
  .stage {
    padding: 16px;
  }
}

/* ---- 手机 ---- */
@media (max-width: 820px) {
  :root {
    --card-w: 42px;
    --card-h: 60px;
    --tile-w: 30px;
    --tile-h: 42px;
    --card-slice: 20px;
    --card-slice-joker: 30px;
  }

  .stage {
    padding: 10px;
  }

  .topbar {
    padding: 8px 12px;
    padding-top: calc(8px + env(safe-area-inset-top));
  }

  .brand-text {
    font-size: 17px;
  }

  .table {
    border-radius: 26% / 20%;
    border-width: 7px;
    padding: 8px;
    gap: 6px;
  }

  .felt-grid {
    grid-template-columns: minmax(70px, 0.72fr) minmax(0, 2.1fr) minmax(70px, 0.72fr);
    /* 中间那行是"别人刚出的牌"，必须有地板：手机竖屏行高被压到 19px 时整块就没了 */
    grid-template-rows: auto minmax(64px, 1fr) auto;
    gap: 6px;
  }

  .player {
    min-width: 66px;
    max-width: 100%; /* 昵称那一列挤不下时缩自己，别把 .stage 顶出横向滚动 */
    padding: 5px 6px;
    font-size: 11px;
  }

  .player .p-name {
    font-size: 12px;
  }

  .avatar {
    width: 20px;
    height: 20px;
    font-size: 11px;
  }

  /* 手牌：靠叠放压宽度，不横向滚动 —— 否则滑动选牌会和滚动打架 */
  #hand-area {
    gap: 0;
    flex-wrap: wrap;
    justify-content: center;
    align-content: flex-start; /* 两行起步，减张也不跳 */
    padding-bottom: 4px;
    min-height: 94px;
  }

  /* 麻将不压叠：每张的花色点数都得看全，宁可换行也不遮 */
  #hand-area .mj + .mj {
    margin-left: 2px;
  }

  #hand-area .mj.sel {
    transform: translateY(-10px);
  }

  #hand-area .pc .r {
    font-size: 14px;
  }

  #hand-area .pc .s {
    font-size: 10px;
  }

  #hand-area .pc .big {
    display: none;
  }

  .mj {
    width: 26px;
    height: 36px;
  }

  .mj.mini {
    width: 20px;
    height: 28px;
  }

  .rivers {
    gap: 4px;
  }

  .river {
    min-height: 32px;
    padding: 2px;
  }

  .action-bar {
    padding: 9px;
    gap: 6px;
    border-radius: 12px;
    min-height: 68px; /* 盖住按钮态高度：纯文字态与按钮态必须同高 */
  }

  .center-box {
    min-height: 88px;
  }

  .action-bar .btn.big {
    flex: 1 1 auto;
    min-width: 76px;
  }

  .btn.big {
    padding: 11px 14px;
    font-size: 14.5px;
  }

  .chat-dock {
    width: min(200px, 52vw);
    left: 8px;
    bottom: calc(8px + env(safe-area-inset-bottom));
  }

  .chat-log {
    max-height: 84px;
  }

  .brand-sub {
    display: none;
  }

  .conn-text {
    display: none;
  }

  .lobby-side {
    grid-template-columns: 1fr;
  }

  .room-code-box b {
    font-size: 20px;
    letter-spacing: 4px;
  }

  .room-rules {
    text-align: left;
  }

  .table-tools {
    justify-content: center;
  }

  .table-info {
    font-size: 11px;
    gap: 10px;
  }

  .modal {
    padding: 16px;
  }

  .result-banner-title {
    font-size: 22px;
  }
}

/* ---- 小屏手机 ---- */
@media (max-width: 400px) {
  :root {
    --card-w: 37px;
    --card-h: 53px;
    --tile-w: 26px;
    --tile-h: 37px;
    --card-slice: 17px;
    --card-slice-joker: 26px;
  }

  .btn.big {
    padding: 10px 12px;
    font-size: 14px;
  }

  .table-info {
    display: none; /* 屏幕太挤，房间码在顶栏和座椅上都看得见 */
  }

  /* 顶栏在 360 宽上原本横向溢出 18px（整页能左右晃）：把装饰性文字收掉 */
  .topbar {
    gap: 6px;
    padding: 6px 8px;
    padding-top: calc(6px + env(safe-area-inset-top));
  }

  .brand-mark {
    font-size: 18px;
  }

  .brand-text {
    font-size: 16px;
    letter-spacing: 1px;
  }

  .brand-sub {
    display: none;
  }

  .conn-text {
    max-width: 3.4em;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .user-chip {
    max-width: 32vw;
    gap: 5px;
    padding: 4px 8px;
  }

  #fs-btn .fs-label {
    display: none;
  }

  /* 11ch 的昵称在 360 宽上正好把座位牌顶出列宽，收到 7ch */
  .p-name > span:nth-child(2) {
    max-width: 7ch;
  }

  /* 座位牌里头像+倒计时+牌数这三件不能缩，加起来 76px；列宽只给 70 就会溢出 4px。
     把两侧列的地板抬到 82，中间那列还够放中央出牌区。 */
  .felt-grid {
    grid-template-columns: minmax(82px, 0.9fr) minmax(0, 2fr) minmax(82px, 0.9fr);
  }
}

/* ---- 320 宽这一档（老 iPhone SE / 低端安卓）：再收一次，宁可不漂亮也不能显示不全 ---- */
@media (max-width: 340px) {
  :root {
    --card-w: 34px;
    --card-h: 48px;
    --tile-w: 24px;
    --tile-h: 34px;
    --card-slice: 15px;
    --card-slice-joker: 24px;
  }

  /* 顶栏在 320 上实测横向溢出 14px（连接状态文字 + 昵称胶囊都不可缩）：
     状态点留着就够判断断线了，文字先让位 */
  .conn-text {
    display: none;
  }

  .user-chip {
    max-width: 26vw;
  }

  .brand-text {
    font-size: 15px;
    letter-spacing: 0;
  }

  .p-name > span:nth-child(2) {
    max-width: 4.5ch;
  }

  .felt-grid {
    grid-template-columns: minmax(74px, 0.9fr) minmax(0, 2fr) minmax(74px, 0.9fr);
  }

  .counter {
    gap: 3px;
  }

  .cc {
    min-width: 16px;
  }

  .btn.tiny {
    padding: 3px 6px;
  }
}

/* ---- 全屏（移动端横屏作战的主场景） ---- */
#fs-btn {
  display: none;
}

/* 安卓壳里已经是沉浸式全屏，这个按钮点了没反应，直接不出现 */
html.in-app #fs-btn {
  display: none;
}

/* 窄屏或矮屏（含手机横屏）都显示全屏入口 */
@media (max-width: 820px), (max-height: 500px) {
  #fs-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
  }
}

html:fullscreen,
html:-webkit-full-screen {
  background: var(--felt-3);
}

/* 全屏：app 壳撑满视口，牌桌高度 = 视口 - 顶栏，内部网格自适应收缩 */
html:fullscreen .app,
html:-webkit-full-screen .app {
  height: 100dvh;
}

html:fullscreen .stage,
html:-webkit-full-screen .stage {
  overflow: auto;
  min-height: 0;
}

html:fullscreen .topbar,
html:-webkit-full-screen .topbar {
  padding: 4px 10px;
  padding-top: calc(4px + env(safe-area-inset-top));
}

html:fullscreen .brand-text,
html:-webkit-full-screen .brand-text {
  font-size: 15px;
}

html:fullscreen .stage,
html:-webkit-full-screen .stage {
  padding: 4px;
  max-width: none;
}

/* 全屏下牌桌撑满视口，内部网格自适应收缩，绝不溢出屏幕 */
html:fullscreen #screen-table:not(.hidden),
html:-webkit-full-screen #screen-table:not(.hidden) {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

html:fullscreen .table-wrap,
html:-webkit-full-screen .table-wrap {
  flex: 1;
  min-height: 0;
  display: flex;
}

html:fullscreen .table,
html:-webkit-full-screen .table {
  flex: 1;
  min-height: 0;
  height: auto;
}

html:fullscreen .panel,
html:-webkit-full-screen .panel {
  max-width: none;
}

/* 全屏 + 横屏：桌面铺满，只留最要命的信息 */
@media (max-height: 500px) and (orientation: landscape) {
  html:fullscreen .table-tools,
  html:-webkit-full-screen .table-tools {
    gap: 4px;
  }

  html:fullscreen .player,
  html:-webkit-full-screen .player {
    padding: 4px 7px;
  }

  html:fullscreen .player .p-cards,
  html:-webkit-full-screen .player .p-cards {
    margin-top: 3px;
  }
}

/* ---- 宽屏桌面：桌更大、牌更大、字更松 ---- */
@media (min-width: 1240px) {
  :root {
    --card-w: 68px;
    --card-h: 96px;
    --tile-w: 50px;
    --tile-h: 68px;
    --card-slice: 26px;
    --card-slice-joker: 42px;
  }

  .stage {
    max-width: 1400px;
    padding: 26px;
  }

  .table {
    border-width: 12px;
    padding: 18px 20px;
    gap: 12px;
  }

  .table-info {
    font-size: 13px;
    gap: 18px;
    padding: 7px 18px;
  }

  .player {
    min-width: 138px;
    padding: 10px 12px;
    font-size: 13px;
  }

  .player .p-name {
    font-size: 14px;
  }

  .avatar {
    width: 30px;
    height: 30px;
    font-size: 14px;
  }

  .btn.tiny {
    padding: 5px 11px;
    font-size: 12.5px;
  }

  .btn.big {
    padding: 14px 26px;
    font-size: 17px;
  }

  .action-bar {
    padding: 14px;
    gap: 12px;
    min-height: 86px; /* 大按钮 54px + 上下留白，恒定不跳 */
  }

  .game-card {
    padding: 22px;
  }

  .game-card h3 {
    font-size: 21px;
  }

  .game-card .gc-icon {
    width: 54px;
    height: 54px;
    font-size: 26px;
    border-radius: 14px;
  }
}

@media (min-width: 1600px) {
  :root {
    --card-w: 76px;
    --card-h: 108px;
    --tile-w: 56px;
    --tile-h: 76px;
    --card-slice: 28px;
    --card-slice-joker: 44px;
  }

  .stage {
    max-width: 1560px;
  }
}

/* 触屏设备：按钮给足触摸面积 */
@media (pointer: coarse) {
  .btn {
    min-height: 34px;
  }

  .btn.big {
    min-height: 46px;
  }

  .btn.tiny {
    min-height: 30px;
  }
}

/* ---------- 台球桌（canvas）----------
   球台由 canvas 自己画（台呢 + 库边 + 木框一体），容器不留任何边框和内边距：
   画布拿到多大的视口，球台就等比放大到多大，多余的宽度是页面背景而不是木框。
   玩家信息/房间信息/提示语浮在台面上下的空白里，不额外占行高。 */
.pool-table {
  position: relative;
  width: 100%;
  /* 填满 .table-wrap：横屏手机只有 266~376px 的可用高，任何固定下限都会把
     底部提示条和声音/退出按钮顶到屏幕外（画布自己会按 min(宽,高) 等比缩小） */
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  display: flex;
  align-items: stretch;
  gap: 6px;
}

.pool-stage {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}

.pool-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none; /* 拖拽瞄准不能被浏览器当成滚动手势 */
  cursor: crosshair;
}

/* 两条浮条：上条排双方玩家 + 房间信息 + 按钮，下条只有提示语。
   高度由 poolTable.js 写进 --pool-chrome-* 变量，球台在这两条之下等比适配，
   所以它们永远只站在台面外，不会切走任何可打区域。 */
.pool-bar {
  position: absolute;
  left: 6px;
  right: 6px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}

.pool-bar-top {
  top: 0;
  /* min-height 而不是 height：玩家条在竖屏会换行，高度得让内容说了算，
     由 poolTable.js 量完再写回 --pool-chrome-*，球台才会在它下面适配 */
  min-height: var(--pool-chrome-top, 56px);
}

.pool-bar-bottom {
  bottom: 0;
  min-height: var(--pool-chrome-bottom, 30px);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
}

.pool-bar-bottom .pool-status {
  grid-column: 2;
}

.pool-bar-bottom .pool-buttons {
  grid-column: 3;
  justify-self: end;
}

.pool-buttons {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

.pool-buttons .btn {
  pointer-events: auto;
}

.pool-center {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  flex: 1 1 auto;
  min-width: 0;
}

.pool-info {
  display: flex;
  align-items: center;
  gap: 11px;
  flex-wrap: wrap;
  min-width: 0;
  font-size: 12px;
  color: rgba(238, 245, 240, 0.86);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

.pool-info b {
  color: var(--gold);
  font-weight: 700;
}

.pool-status {
  min-width: 0;
  max-width: 100%;
  font-size: 13px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: rgba(238, 245, 240, 0.82);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

.pool-status.mine {
  color: var(--gold);
}

.pool-status.danger {
  color: var(--warn);
}

/* 玩家牌：头像 + 昵称/标签 + 一排球槽。对手在左、自己在右（整块镜像）。 */
.pool-plate {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  min-width: 0;
  padding: 4px 8px 4px 5px;
  border-radius: 12px;
  background: rgba(5, 24, 18, 0.58);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  font-size: 12px;
  line-height: 1.3;
  transition: box-shadow 0.18s var(--ease-pop);
}

.pool-plate.mine {
  flex-direction: row-reverse;
}

.pool-plate.turn {
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    0 0 0 2px var(--gold);
}

.pp-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  flex: 1 1 auto;
  min-width: 0;
}

.pool-plate.mine .pp-meta {
  align-items: flex-end;
}

.pp-name {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  font-weight: 700;
}

.pp-name b {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 88px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pp-name .tag {
  flex: 0 0 auto;
  font-size: 10px;
  padding: 1px 5px;
  white-space: nowrap;
}

.pp-slots {
  display: flex;
  gap: 3px;
}

/* 球槽：空圈是还在台上的球，填色是已经打进的，末尾黑的是黑八 */
.slot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.26);
}

.slot.potted {
  background: var(--c, var(--gold));
  box-shadow:
    inset 0 -2px 3px rgba(0, 0, 0, 0.4),
    0 0 0 1px rgba(0, 0, 0, 0.35);
}

.slot.stripe.potted {
  background-image: linear-gradient(#fff, #fff);
  background-size: 100% 34%;
  background-repeat: no-repeat;
  background-position: center;
}

.slot.eight {
  display: grid;
  place-items: center;
  background: #12161a;
  color: #f5f2e8;
  font-size: 8px;
  font-weight: 700;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.slot.eight.live {
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.3),
    0 0 0 2px var(--gold);
}

.pp-wins {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 11px;
}

.pool-plate .tag.group.solid {
  background: #6b4a1c;
}

.pool-plate .tag.group.stripe {
  background: #284b6b;
}

/* 很窄的屏（竖屏手机）：中间那组房间信息让位，玩家条先保住两边的球槽 */
@media (max-width: 620px) {
  .pool-center {
    display: none;
  }

  /* 玩家条必须能缩：左右两条按内容排出来 174+157，比竖屏浮条本身（260）还宽，
     不缩就会把整条顶栏顶出屏幕（实测 .stage 横向溢出 11px）。 */
  .pool-plate {
    flex: 0 1 auto;
    min-width: 0;
    gap: 5px;
    padding: 3px 6px 3px 4px;
  }

  .pp-name b {
    max-width: 58px;
  }

  .pp-slots {
    flex-wrap: wrap;
    gap: 2px;
  }

  .slot {
    width: 8px;
    height: 8px;
  }

  .slot.eight {
    font-size: 7px;
  }

  .pp-wins {
    font-size: 10px;
  }

  .pool-bar-top {
    flex-wrap: wrap;
    row-gap: 2px;
  }
}

/* 右栏：杆法盘在上、力度条在下，都在台面外 */
.pool-side {
  position: relative;
  flex: 0 0 auto;
  width: 62px;
  min-height: 0;
  opacity: 0.5;
}

.pool-side.armed {
  opacity: 1;
}

.pool-stick-track {
  /* 顶端位置和长度都由 poolTable.js 直接写在元素上（inline top/height）。
     以前绕了一层 CSS 变量：实测 852x393 上变量已经更新到 77px，元素却仍按 fallback
     的 140px 定位，力量条整条掉出右栏，还把 .stage 顶出 100 多 px 的内滚。
     这里的值只是 JS 量之前的一帧兜底。 */
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  width: 26px;
  height: 120px;
  border-radius: 13px;
  background: rgba(0, 0, 0, 0.4);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
  overflow: hidden;
  touch-action: none;
  cursor: ns-resize;
}

.pool-stick-fill {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 40%;
  background: linear-gradient(180deg, #6fd08c, #f2c94c 62%, #eb5757);
  opacity: 0.9;
}

.pool-stick-handle {
  position: absolute;
  left: 1px;
  right: 1px;
  height: 22px;
  border-radius: 6px;
  background: linear-gradient(180deg, #f7f3e7, #cdc5ae);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

.pool-stick-value {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* 钉在右栏底部（力度条本身预留了这 26px）：之前按 top = 条顶 + 条长 + 5 来放，
     一旦条子被 min-height 兜到比预留空间长，这个百分比就掉到栏外，
     把整条 .stage 顶出 100 多 px 的内滚（实测 852x393）。 */
  bottom: 2px;
  white-space: nowrap;
  font-size: 11px;
  color: var(--gold);
  line-height: 1.2;
}

.pool-spin {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #fffdf6, #ded9c8 62%, #b7b1a1);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.35),
    0 2px 6px rgba(0, 0, 0, 0.35);
  touch-action: none;
  cursor: crosshair;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.pool-spin.off {
  cursor: default;
  filter: grayscale(0.7);
}

.pool-spin::before,
.pool-spin::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
}

.pool-spin::before {
  background: linear-gradient(90deg, transparent 49.2%, rgba(0, 0, 0, 0.16) 49.2% 50.8%, transparent 50.8%);
}

.pool-spin::after {
  background: linear-gradient(180deg, transparent 49.2%, rgba(0, 0, 0, 0.16) 49.2% 50.8%, transparent 50.8%);
}

.pool-spin-dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 13px;
  height: 13px;
  margin: -6.5px 0 0 -6.5px;
  border-radius: 50%;
  background: #d13c32;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.75);
  pointer-events: none;
}

.pool-spin-label {
  position: relative;
  z-index: 1;
  font-size: 10px;
  line-height: 1.5;
  padding: 0 0 3px;
  color: #33301f;
  background: rgba(255, 255, 255, 0.72);
  border-radius: 6px;
}


/* ---------- 账号管理（只有 ADMIN_USERNAMES 里的人能看到入口） ---------- */
.admin-panel {
  max-width: 720px;
  margin: 0 auto;
}
.admin-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.admin-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
}
/* 一行一个账号：主信息吃满剩余宽度，按钮挤不下就自己折行，
   手机上不横向滚动也不裁字（.admin-main 的 min-width:0 是必需的）。 */
.admin-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--panel-br);
}
.admin-row.is-banned {
  opacity: 0.6;
}
.admin-main {
  flex: 1 1 190px;
  min-width: 0;
}
.admin-name {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.admin-name span {
  font-size: 12px;
  opacity: 0.72;
}
.admin-meta {
  font-size: 11px;
  opacity: 0.62;
  margin-top: 2px;
}
.admin-acts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.admin-tag {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(120, 200, 140, 0.16);
}

/* 尊重系统的"减少动态效果" */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ============================================================
   手机横屏（APK 强制横屏 / 浏览器转横屏）：可用高只有 360~430
   ------------------------------------------------------------
   必须留在文件最后：这里全是同特异性的覆盖，靠顺序压过上面各档基础规则。
   之前这段写在台球基础段前面，结果 .pool-table / #hand-area / .pool-side
   的横屏压缩一条都没生效，横屏进台球直接把底部按钮顶出屏幕。
   ============================================================ */
@media (max-height: 500px) and (orientation: landscape) {
  :root {
    --card-w: 38px;
    --card-h: 48px;
    --tile-w: 27px;
    --tile-h: 36px;
    --card-slice: 20px;
    --card-slice-joker: 28px;
  }

  .stage {
    padding: 5px 8px;
    /* 横屏刘海在左/右：这两边必须留 env()，不然牌桌边会被摄像头缺口吃掉 */
    padding-left: calc(8px + env(safe-area-inset-left));
    padding-right: calc(8px + env(safe-area-inset-right));
  }

  /* 大厅也压一档：横屏进来第一屏就该看见几张桌，而不是先滚一段 */
  .lobby-grid {
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 8px;
    margin-bottom: 8px;
  }

  .game-card {
    padding: 8px 10px;
  }

  .game-card .gc-top {
    margin-bottom: 4px;
  }

  .game-card h3 {
    font-size: 15px;
  }

  .game-card p {
    min-height: 0;
    font-size: 11.5px;
    line-height: 1.4;
  }

  .topbar {
    gap: 6px;
    padding: 4px 10px;
    padding-top: calc(4px + env(safe-area-inset-top));
  }

  .brand-text {
    font-size: 15px;
  }

  .brand-sub {
    display: none;
  }

  .table {
    border-width: 6px;
    border-radius: 30% / 22%;
    padding: 6px 8px;
    gap: 4px;
  }

  /* 工具行：宁可多占一行也必须每个按钮都够得着。
     旧写法是 nowrap + overflow:auto 配上居中，左半边被推出容器后连滚都滚不回来。 */
  .table-tools {
    flex-wrap: wrap;
    overflow: visible;
    justify-content: flex-start;
    gap: 4px;
    row-gap: 3px;
  }

  .tools-left {
    flex: 0 1 auto;
    min-width: 0;
  }

  .tools-right {
    flex: 0 1 auto;
    min-width: 0;
  }

  .counter {
    gap: 4px;
  }

  .cc-row {
    gap: 2px;
  }

  .cc {
    min-width: 16px;
    padding: 1px 2px;
  }

  .cc .cc-r,
  .cc .cc-n {
    font-size: 9.5px;
  }

  .table-info {
    display: none;
  }

  .btn.tiny {
    min-height: 28px;
    padding: 3px 7px;
    font-size: 11px;
  }

  .btn.emoji {
    padding: 3px 5px;
    font-size: 12.5px;
  }

  .player {
    min-width: 60px;
    max-width: 100%;
    padding: 3px 6px;
    font-size: 10.5px;
  }

  .player .p-name {
    font-size: 11px;
    margin-bottom: 1px;
  }

  /* 座位牌上的副露让位给中央展示区，座椅不额外撑高 */
  .player .p-last {
    display: none;
  }

  .player .p-cards {
    margin-top: 2px;
  }

  .player .p-count {
    font-size: 10px;
  }

  .avatar {
    width: 16px;
    height: 16px;
    font-size: 9.5px;
  }

  .clock {
    width: 12px;
    height: 12px;
  }

  /* 基础规则用的是 #hand-area（ID），这里必须同级压过它，否则白占 94px */
  #hand-area {
    min-height: 52px;
    padding-bottom: 2px;
  }

  .hand-wrap {
    padding-top: 4px;
    gap: 4px;
  }

  .action-bar {
    padding: 5px 6px;
    min-height: 42px;
  }

  .action-bar .btn.big {
    min-height: 34px;
    padding: 7px 12px;
    font-size: 13px;
  }

  /* 中央区横排：竖着叠是四行（上家出的牌 + 底牌），实测 156px 高，而横屏这一格只有
     107px —— 多出来的部分就盖到工具条和上下座椅上去了（截图里的覆盖）。标签和牌组并排。 */
  .center-box {
    min-height: 0;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 2px 10px;
  }

  .center-box .muted {
    font-size: 10.5px;
  }

  .last-play {
    margin-top: 0;
    gap: 2px;
  }

  /* 麻将中央那张"刚打出的大牌"和牌河里描金边的最后一张是同一张，横屏让位给牌河；
     状态文字和牌河并到同一行，中央这一格只留一条的高度 */
  .mj-center {
    flex-direction: row;
    align-items: center;
    gap: 5px;
    justify-content: flex-start;
  }

  .mj-center > .muted {
    flex: 0 0 auto;
    max-width: 88px;
  }

  .mj-center > .last-play {
    display: none;
  }

  /* 四家牌河并成一条横杠、各自横向滚到最新那张（滚动在 table.js 里做）：
     2x2 那份要 83px，横屏中央给不到，只能盖住座椅 */
  .rivers {
    flex: 1 1 auto;
    min-width: 0;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 4px;
  }

  .river {
    flex-wrap: nowrap;
    overflow-x: auto;
    min-height: 0;
    padding: 2px 3px;
    gap: 1px;
    scrollbar-width: none;
  }

  /* 牌不许被 flex 压扁：不写这条，14 张牌会被挤进 109px 里变成一条薄片 */
  .river > .mj {
    flex: 0 0 auto;
  }

  .river::-webkit-scrollbar {
    display: none;
  }

  .river .who {
    font-size: 10px;
    flex: 0 0 auto;
  }

  /* 座椅一律压成一行：昵称 + 标签 + 吃碰杠，副露太长就在自己那条里横向滚。
     竖着叠实测四组副露能把侧家座椅顶到 212px 高，工具条和操作条全被盖住。 */
  .player {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    padding: 2px 5px;
  }

  .player .p-name {
    margin-bottom: 0;
    flex: 0 0 auto;
  }

  .player .p-count {
    font-size: 10px;
    flex: 0 0 auto;
  }

  .p-melds {
    margin-top: 0;
    flex: 0 1 auto;
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .p-melds > .group {
    flex: 0 0 auto;
  }

  .p-melds::-webkit-scrollbar {
    display: none;
  }

  /* 中央区在 1fr 行里：行被压到几 px 时会被自己的 overflow:hidden 整块切没 */
  .felt-grid {
    grid-template-rows: auto minmax(44px, 1fr) auto;
    gap: 4px;
  }

  .chat-dock {
    width: 168px;
  }

  .chat-log {
    max-height: 46px;
  }

  /* 台球：右栏和玩家条缩到横屏尺寸，画布盒子已经跟着视口走（见 .pool-table） */
  .pool-table {
    gap: 4px;
  }

  .pool-side {
    width: 50px;
  }

  .pool-spin {
    width: 44px;
    height: 44px;
  }

  .pool-plate {
    gap: 5px;
    padding: 2px 6px 2px 4px;
  }

  .pp-name b {
    max-width: 56px;
  }

  .slot {
    width: 8px;
    height: 8px;
  }

  .slot.eight {
    font-size: 7px;
  }
}
