/* games.css — 游戏目录补充 + 国际象棋棋盘（大厅/对局）。原语在 ds.css。 */

/* ---------- 面包屑 ---------- */
.crumb { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; color: var(--text-secondary); font-size: 14px; }
.crumb-link { display: inline-flex; align-items: center; gap: 4px; color: var(--text-secondary); text-decoration: none; }
.crumb-link:hover { color: var(--text); }
.crumb-sep { color: var(--text-tertiary); }
.crumb-title { color: var(--text); font-weight: 600; }
.room-chip {
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-secondary);
  border-radius: 999px; padding: 4px 12px; font: inherit; font-size: 12.5px; cursor: pointer;
}
.room-chip:hover { background: var(--surface-muted); color: var(--text); }

/* ---------- 大厅 ---------- */
.chess-wrap { --board-max: 34rem; }
.lobby-hero { display: flex; align-items: center; gap: 16px; margin-bottom: 22px; }
.lobby-piece { width: 46px; height: 46px; }
.lobby-title { font-size: 22px; font-weight: 700; }
.lobby-sub { color: var(--text-secondary); font-size: 14px; margin-top: 2px; }
.lobby-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: 14px; }
.lobby-card { padding: 20px 22px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.lobby-card-title { font-size: 15.5px; font-weight: 650; }
.lobby-card-sub { color: var(--text-tertiary); font-size: 13px; margin-bottom: 6px; }
.join-row { display: flex; gap: 8px; width: 100%; }
.code-input { letter-spacing: 3px; text-transform: uppercase; font-variant-numeric: tabular-nums; max-width: 12rem; }

/* ---------- 对局布局 ---------- */
.room-layout { display: grid; grid-template-columns: minmax(0, var(--board-max)) minmax(15rem, 1fr); gap: 26px; align-items: start; }
.board-col { display: flex; flex-direction: column; gap: 8px; }

.player-bar { min-height: 30px; }
.player-line { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text-secondary); }
.player-line.turn { color: var(--text); font-weight: 600; }
.player-line .dot { width: 11px; height: 11px; border-radius: 50%; border: 1px solid var(--border-strong); flex: none; }
.player-line .dot.w { background: #fff; }
.player-line .dot.b { background: #222; }
.player-line .player-name { color: var(--text); }

.board {
  width: 100%; max-width: var(--board-max); aspect-ratio: 1 / 1;
  display: grid; grid-template-columns: repeat(8, 1fr); grid-template-rows: repeat(8, 1fr);
  border-radius: 12px; overflow: hidden; border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-card);
}
.sq { position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; user-select: none; }
.sq.light { background: #f4f4f6; }
.sq.dark { background: #38383f; }
html[data-theme="dark"] .sq.light { background: #d6d6db; }
html[data-theme="dark"] .sq.dark { background: #1b1b1f; }
.sq.last::before { content: ''; position: absolute; inset: 0; background: rgba(10, 132, 255, .28); }
.sq.sel::before { content: ''; position: absolute; inset: 0; background: rgba(10, 132, 255, .42); }
.sq.target::after {
  content: ''; position: absolute; width: 26%; height: 26%; border-radius: 50%;
  background: rgba(10, 132, 255, .42);
}
.sq.target:has(.pc)::after { width: 82%; height: 82%; background: transparent; box-shadow: inset 0 0 0 4px rgba(10, 132, 255, .42); border-radius: 50%; }
.sq.check::before { content: ''; position: absolute; inset: 0; background: radial-gradient(circle, rgba(217, 45, 32, .55), transparent 72%); }
.pc { width: 88%; height: 88%; pointer-events: none; position: relative; z-index: 1; }
/* 黑子放在深色格上会看不清：给黑子描一圈 1px 浅色边（4 方向 drop-shadow） */
.pc-b {
  filter: drop-shadow(1px 0 0 rgba(255, 255, 255, .5)) drop-shadow(-1px 0 0 rgba(255, 255, 255, .5))
          drop-shadow(0 1px 0 rgba(255, 255, 255, .5)) drop-shadow(0 -1px 0 rgba(255, 255, 255, .5));
}
.coord { position: absolute; font-size: 12px; font-weight: 600; opacity: 1; pointer-events: none; }
.coord.rank { top: 2px; left: 4px; }
.coord.file { bottom: 1px; right: 4px; }
.sq.light .coord { color: #545464; }
.sq.dark .coord { color: #ededf3; }

.status-bar { display: flex; align-items: center; gap: 10px; padding: 10px 0 0; color: var(--text-secondary); font-size: 14.5px; min-height: 26px; }
.status-bar.active { color: var(--brand); font-weight: 650; }
.status-bar.done { color: var(--text); font-weight: 650; }
.chess-actions { display: flex; gap: 10px; padding-top: 12px; flex-wrap: wrap; align-items: center; }
.chess-actions .btn.danger { margin-left: auto; }   /* 危险操作靠右，和辅助操作分开 */

.chess-side { display: flex; flex-direction: column; gap: 14px; }
.side-card { padding: 16px 18px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.side-head { font-size: 13px; color: var(--text-tertiary); }
.code-row { display: flex; align-items: center; gap: 8px; }
.room-code {
  font: inherit; font-size: 22px; font-weight: 700; letter-spacing: 4px;
  background: transparent; border: 0; color: var(--text); cursor: pointer; padding: 0;
}
.room-code:hover { color: var(--brand); }
.side-players { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.side-player { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.side-player .dot { width: 10px; height: 10px; border-radius: 50%; border: 1px solid var(--border-strong); flex: none; }
.side-player .dot.w { background: #fff; }
.side-player .dot.b { background: #222; }
.side-hint { color: var(--text-tertiary); font-size: 12px; }

.moves-table { width: 100%; max-height: 320px; overflow-y: auto; }
.moves-grid { display: grid; grid-template-columns: 2.2rem 1fr 1fr; row-gap: 2px; align-items: center; font-variant-numeric: tabular-nums; }
.mv-no { color: var(--text-tertiary); font-size: 13px; }
.mv { font-size: 14px; color: var(--text); padding: 2px 6px; border-radius: 6px; }
.mv.last { background: var(--brand-soft); color: var(--brand); font-weight: 600; }

@media (max-width: 900px) {
  .room-layout { grid-template-columns: 1fr; }
  .chess-side { order: 0; }          /* 手机：对手 → 棋盘 → 自己 第一屏，房间/着法在下面 */
  .board { max-width: 100%; }
}
@media (min-width: 901px) {
  .wrap.chess-wrap { max-width: 66rem; }
}

/* ---------- 五子棋棋盘（落子在交叉点上：白底 + 发丝线 + 圆子） ---------- */
.gomoku-board {
  position: relative; width: 100%; max-width: var(--board-max); aspect-ratio: 1 / 1;
  background-color: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 12px; box-shadow: var(--shadow-card);
}
.gomoku-grid { position: absolute; inset: 0; width: 100%; height: 100%; }
.gomoku-grid line { stroke: var(--grid-line); stroke-width: 1; }
.gomoku-grid .gstar { fill: var(--text-tertiary); opacity: .55; }
.gpoint {
  position: absolute; transform: translate(-50%, -50%);
  width: calc(100% / var(--gomoku-size, 15) * 0.94); aspect-ratio: 1 / 1;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: transparent; padding: 0; cursor: pointer; border-radius: 50%;
  z-index: 1;
}
.gpoint .stone { width: 100%; height: 100%; border-radius: 50%; position: relative; }
.stone.b { background: radial-gradient(circle at 32% 28%, #4c4c55, #16161a 72%); }
.stone.w { background: radial-gradient(circle at 32% 28%, #ffffff, #dcdce1 78%); box-shadow: inset 0 0 0 1px #c9c9cf; }
.stone.ghost { opacity: 0; transition: opacity .12s; }
.stone.ghost.b { background: radial-gradient(circle at 32% 28%, rgba(76,76,85,.6), rgba(22,22,26,.6) 72%); }
.stone.ghost.w { background: radial-gradient(circle at 32% 28%, rgba(255,255,255,.9), rgba(220,220,225,.9) 78%); }
.gpoint:hover .stone.ghost { opacity: .35; }
.gpoint.last .stone { box-shadow: 0 0 0 2.5px var(--brand); }
.gpoint.win .stone { box-shadow: 0 0 0 3px var(--brand); }
.gpoint.win::before {
  content: ''; position: absolute; inset: -22%; border-radius: 50%;
  background: var(--brand-soft); z-index: -1;
}


/* 大厅游戏图标（棋类用棋子/石图） */
.lobby-icon { width: 46px; height: 46px; border-radius: 14px; background: var(--brand-soft); display: inline-flex; align-items: center; justify-content: center; }
.lobby-icon .lobby-piece { width: 26px; height: 26px; }
.lobby-icon img { width: 26px; height: 26px; }

/* 悔棋请求横幅（需双方同意）：通栏、醒目；不要覆盖 .btn 自己的配色 */
.undo-banner {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: var(--brand-soft); color: var(--text);
  border: 1px solid var(--interactive-border);
  border-radius: 12px; padding: 10px 12px; font-size: 14px; font-weight: 550;
}
.undo-banner .spacer { flex: 1; }
.undo-banner .undo-text { color: var(--brand); }

/* ---------- 对局历史 / 回放 ---------- */
.replay-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.replay-title { color: var(--text); font-size: 14.5px; font-weight: 600; }
.replay-layout { display: grid; grid-template-columns: minmax(0, 32rem) minmax(14rem, 1fr); gap: 22px; align-items: start; }
.replay-board-col { display: flex; flex-direction: column; gap: 8px; }
.replay-board-chess { width: 100%; max-width: 32rem; }
.replay-board-gomoku { width: 100%; max-width: 32rem; }
.replay-stone { position: absolute; width: 6%; aspect-ratio: 1 / 1; height: auto; transform: translate(-50%, -50%); border-radius: 50%; z-index: 1; }
.replay-stone.replay-last { box-shadow: 0 0 0 2.5px var(--brand); }
.replay-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.replay-step { color: var(--text-secondary); font-size: 13px; font-variant-numeric: tabular-nums; min-width: 4.5rem; text-align: center; }
.replay-slider { flex: 1; min-width: 8rem; accent-color: var(--brand); }
@media (max-width: 900px) {
  .replay-layout { grid-template-columns: 1fr; }
}

/* ---------- 开局提示 / 过渡动效 / 对局中放大居中 ---------- */
.board-col { position: relative; }
.board-flash {
  position: absolute; left: 50%; top: 40%; transform: translate(-50%, -50%);
  background: color-mix(in srgb, var(--text) 90%, transparent); color: var(--page);
  padding: 11px 26px; border-radius: 14px; font-size: 19px; font-weight: 700; letter-spacing: 1px;
  opacity: 0; pointer-events: none; z-index: 6; white-space: nowrap;
  box-shadow: var(--shadow-raised);
}
.board-flash.center.on { animation: flash-pop 1.3s var(--ease-out); }
@keyframes flash-pop {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.92); }
  14% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  78% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(.98); }
}
/* 每回合的"轮到你"：自己玩家栏中间的小 pill（top 由 JS 对齐玩家栏中线），轻轻上浮出现、停一会儿再淡出 */
.board-flash.hint {
  transform: translate(-50%, -50%);
  padding: 4px 13px; border-radius: 999px; font-size: 13.5px; font-weight: 650; letter-spacing: .3px;
  background: var(--brand-soft); color: var(--brand);
  border: 1px solid var(--interactive-border); box-shadow: none;
}
.board-flash.hint.alert { background: color-mix(in srgb, var(--danger) 10%, transparent); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.board-flash.hint.on { animation: hint-pop 1.4s var(--ease-out); }
@keyframes hint-pop {
  0% { opacity: 0; transform: translate(-50%, calc(-50% + 6px)) scale(.96); }
  14% { opacity: 1; transform: translate(-50%, -50%); }
  80% { opacity: 1; transform: translate(-50%, -50%); }
  100% { opacity: 0; transform: translate(-50%, -50%); }
}
/* 落子：从略大、略透明"落"到盘面，轻微回弹；棋子落位只做很小的缩放 */
.gpoint.last .stone { animation: stone-drop 240ms var(--ease-spring); }
@keyframes stone-drop { from { transform: scale(1.22); opacity: .4; } to { transform: none; opacity: 1; } }
.sq.last .pc { animation: piece-in 200ms var(--ease-out); }
@keyframes piece-in { from { transform: scale(1.1); opacity: .7; } to { transform: none; opacity: 1; } }
/* 最近一步 / 选中格的高亮淡入，而不是一下子出现 */
.sq.last::before, .sq.sel::before { animation: fadein 160ms ease; }

/* 对局中：棋盘放大、单列居中（房间/着法移到棋盘下方），开局进场动画。
   等待房间保持原来的两栏布局。 */
.board { transition: max-width var(--motion-layout) var(--ease-standard); }
body.game-focus .chess-wrap { --board-max: min(46rem, calc(100dvh - 292px)); max-width: 78rem; padding-top: 14px; padding-bottom: 20px; }
body.game-focus .crumb { display: flex; }         /* 对局中收掉面包屑（离开房间回大厅） */
/* 对局中信息栏紧凑：房间码在右上角 chip 里（点击可复制），这里只留双方与着法 */
body.game-focus .chess-side .side-hint,
body.game-focus .chess-side .side-players { display: none; }
body.game-focus .chess-side .side-card { padding: 12px 14px; }
body.game-focus .chess-side .moves-table { max-height: 108px; }
body.game-focus .room-layout {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
}
/* 进场动画只在"开始对局"那一下播放，终局不再动 */
body.game-live .room-layout { animation: game-in 360ms var(--ease-out); }
/* 轮到的玩家：dot 高亮 + 名字加粗（文字提示交给状态行/中央 flash） */
.player-line.acting .dot { box-shadow: 0 0 0 3px var(--brand-soft); border-color: var(--brand); }
.player-line.acting .player-name { font-weight: 650; }
body.game-focus .board-col { width: min(var(--board-max), 100%); }
body.game-focus .board { margin: 0 auto; }
body.game-focus .chess-side {
  flex-direction: row; width: min(var(--board-max), 100%);
  justify-content: center; align-items: stretch;
}
body.game-focus .chess-side > .card { flex: 1 1 0; min-width: 0; }
body.game-focus .chess-side .moves-table { max-height: 150px; }
@keyframes game-in { from { opacity: .55; transform: translateY(8px) scale(.99); } to { opacity: 1; transform: none; } }
@media (max-width: 900px) {
  body.game-focus .chess-side { flex-direction: column; }
}
.gpoint.playable { cursor: pointer; }
.gpoint.locked { cursor: default; }

/* 落子提交中（棋盘角落的小 spinner，不挡操作） */
.board-col { position: relative; }
.board-col.board-pending::after {
  content: ''; position: absolute; right: 6px; top: 6px;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--border-strong); border-top-color: var(--brand);
  animation: spin .7s linear infinite;
}

/* ---------- Player Bar / 状态 pill / 终局浮层（V4-C） ---------- */
.player-line { gap: 8px; }
.player-line .face.sm {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--surface-muted); color: var(--text-2);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 650;
}
.player-line.acting .face.sm { background: var(--brand-soft); color: var(--brand); }
.player-line .side-label { color: var(--text-tertiary); font-size: 12.5px; }
.status-bar {
  align-self: center; width: auto; padding: 6px 16px; border-radius: 999px;
  background: var(--surface-muted); gap: 8px; margin-top: 4px;
  transition: background-color 240ms ease, color 240ms ease;
}
.status-bar.active { background: var(--brand-soft); }
.status-bar.done { background: var(--surface-muted); }

/* 终局浮层：贴在棋盘上（不改变棋盘几何），轻微粒子 */
.result-overlay {
  position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%);
  display: flex; justify-content: center; pointer-events: none; z-index: 5;
}
.result-card {
  pointer-events: auto; text-align: center;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--border); border-radius: var(--radius-large);
  box-shadow: var(--shadow-raised); padding: 18px 26px;
  animation: result-in 320ms var(--ease-out) 120ms both;   /* 等最后一手落定再出来 */
  backdrop-filter: blur(2px);
}
.result-title { font-size: 24px; font-weight: 750; letter-spacing: .5px; }
.result-sub { color: var(--text-secondary); font-size: 13.5px; margin: 4px 0 14px; }
.result-actions { display: flex; gap: 10px; justify-content: center; }
@keyframes result-in { from { opacity: 0; transform: translateY(8px) scale(.97); } to { opacity: 1; transform: none; } }
.result-overlay .spark {
  position: absolute; left: 50%; top: 50%; width: 5px; height: 5px; border-radius: 50%;
  background: var(--brand); opacity: 0;
  animation: spark-fly .5s ease-out forwards;
}
@keyframes spark-fly {
  0% { opacity: .85; transform: translate(0, 0) scale(1); }
  100% { opacity: 0; transform: translate(calc(var(--dx) * 90px), calc(var(--dy) * 60px)) scale(.4); }
}

/* Replay controls, coordinates and legible white stones. */
.gomoku-frame { padding: 22px 0 0 22px; position: relative; width: 100%; max-width: 32rem; }
.gomoku-cols { position: absolute; top: 0; left: 22px; right: 0; display: grid; grid-template-columns: repeat(15, 1fr); text-align: center; color: var(--text-secondary); font-size: 11px; }
.gomoku-rows { position: absolute; top: 22px; bottom: 0; left: 0; width: 20px; display: grid; grid-template-rows: repeat(15, 1fr); align-items: center; text-align: center; color: var(--text-secondary); font-size: 11px; }
.stone.w { border: 1px solid #697282; box-shadow: 0 1px 3px rgba(0,0,0,.2), inset 0 0 0 1px #fff; }
.replay-winning-line { stroke: var(--brand) !important; stroke-width: .1 !important; stroke-linecap: round; }
.replay-board-gomoku .gomoku-grid { z-index: 2; pointer-events: none; }
.replay-moves button, .moves-grid button.mv { border: 0; background: transparent; text-align: left; color: var(--text); cursor: pointer; font: inherit; min-height: 36px; padding: 4px 8px; border-radius: 6px; }
.moves-grid button.mv:hover, .moves-grid button.mv.last { background: var(--brand-soft); color: var(--brand); }
.replay-controls .btn { min-width: 44px; min-height: 44px; }
.replay-head { flex-wrap: wrap; }
.replay-slider { min-height: 44px; width: 100%; }
.game-connection { color: var(--text-secondary); font-size: 12px; margin-left: auto; }
.game-connection.reconnecting { color: var(--danger); }
.join-row + .field-err:empty { display: none; }

html[data-theme="dark"] .moves-grid button.mv:is(:hover, .last) { color: var(--link); }

.replay-controls { flex-wrap: wrap; }
.replay-controls select { width: auto; min-width: 80px; }
.stone.w { border: 1.5px solid #505968; box-shadow: 0 1px 3px rgba(0,0,0,.25), inset 0 0 0 1px #fff; }

.lobby-title { margin: 0; font-size: 28px; }
.board-col, .chess-side, .lobby-card { min-width: 0; }
body.game-focus .chess-wrap { --board-max: min(42rem, calc(100dvh - 320px)); }
.player-line { padding: 8px 0; min-height: 44px; }
.player-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-turn { margin-left: auto; color: var(--link); font-size: 12px; white-space: nowrap; }
.game-more { position: relative; }
.game-more > summary { list-style: none; cursor: pointer; }
.game-more > summary::-webkit-details-marker { display: none; }
.game-more-menu { position: absolute; z-index: 10; bottom: calc(100% + 8px); left: 0; min-width: 220px; max-width: calc(100vw - 32px); background: var(--surface); border: 1px solid var(--border-strong); border-radius: 12px; box-shadow: var(--shadow-raised); padding: 8px; }
.game-more-menu .btn { width: 100%; border-color: transparent; justify-content: flex-start; }
.game-more-menu .field-hint { padding-inline: 8px; }
.game-action-note { font-size: 12px; color: var(--text-secondary); flex-basis: 100%; }
.game-commentary { padding: 16px 0; border-top: 1px solid var(--border); font-size: 13px; }
.game-commentary strong { font-size: 12px; color: var(--text-secondary); font-weight: 500; }
.game-commentary p { margin: 6px 0 0; }
.chess-actions .btn, .room-code, .room-chip { min-height: 44px; }
.chess-actions { gap: 8px; }
.join-row .input { min-width: 0; flex: 1; }
body.game-live .room-layout { animation: none; }
@media (max-width: 640px) {
  body.game-focus .chess-wrap { --board-max: 100%; padding: 16px; }
  .lobby-card { padding: 20px; }
  .player-line { gap: 6px; font-size: 13px; }
  .player-line .side-label { font-size: 12px; white-space: nowrap; }
  .chess-actions { display: flex; }
  .chess-actions > .btn, .chess-actions > .game-more { flex: 1; }
  .game-more > summary { width: 100%; }
  .chess-actions .btn.danger { margin-left: 0; }
  .game-action-note { font-size: 12px; }
}

/* Coordinates live outside the playable squares. */
.board { width: calc(100% - 24px); margin: 0 0 24px 24px !important; overflow: visible; border: 0; box-shadow: none; }
.coord.rank { left: -22px; top: 50%; transform: translateY(-50%); }
.coord.file { right: 50%; bottom: -22px; transform: translateX(50%); }
.sq.light .coord, .sq.dark .coord { color: var(--text-secondary); }
.result-overlay { position: static; inset: auto; padding: 16px 0; background: none; backdrop-filter: none; display: block; }
.result-overlay .result-card { width: 100%; text-align: left; background: none; border: 0; box-shadow: none; padding: 0; }
.result-overlay .result-actions { justify-content: flex-start; }
.result-overlay .spark { display: none; }
