/* Page-specific styles for index.html (lobby + live game + login screen).
   :root variables, base reset, body, button/.btn-sm/.theme-toggle,
   .lobby-pagination, .shell, #appHeader base, .panel* live in shared.css,
   shared between this page and any other page that includes it. */

.eyebrow {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 13.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
  margin: 0 0 10px;
}

h1 {
  font-family: "Noto Serif Thai", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: clamp(32px, 5vw, 48px);
  margin: 0 0 8px;
  text-wrap: balance;
  color: var(--paper);
}

.lede { max-width: 62ch; color: var(--paper-dim); font-size: 18px; margin: 0 0 28px; }

.layout { display: flex; gap: 28px; align-items: stretch; flex-wrap: wrap; justify-content: center; }

.goban-wrap { position: relative; flex: 0 0 auto; min-width: 320px; }

.goban {
  position: relative;
  background: linear-gradient(155deg, var(--wood-hi) 0%, var(--wood) 45%, var(--wood-dark) 100%);
  border-radius: 6px;
  padding: 10px;
  width: fit-content;
  margin: 0 auto;
  box-shadow:
    0 2px 0 rgba(255,255,255,0.15) inset,
    0 5px 0 var(--wood-dark),
    0 7px 0 #5e401f,
    0 14px 24px rgba(0,0,0,0.5);
}

canvas#board { display: block; aspect-ratio: 1 / 1; border-radius: 2px; cursor: pointer; }

/* Grows to fill whatever's left in the sidebar (useful on 13x13/19x19,
   where the sidebar is stretched taller than its other sections need),
   but never below ~5 message lines. min-height:0 down this whole chain
   is what makes flex actually respect that outer bound instead of
   growing with every new message — without it, a flex item's default
   min-height is its content size, which silently overrides flex-grow's
   ceiling and is what let the log balloon past the board before. */
.ingame-chat { flex: 1 1 80px; min-height: 80px; display: flex; flex-direction: column; }
.ingame-chat .chat-layout { flex: 1; min-height: 0; flex-direction: column; gap: 8px; }
/* A real floor on the log itself (not just the whole chat block) so it
   always shows a couple lines of history — without this, a cramped
   viewport could shrink the block down to its bare min-height, leaving
   the input row its usual size and almost nothing for the log above it.
   This is a floor only: on any screen with room to spare, the flex:1
   above still grows the log well past it, same as before. */
.ingame-chat .chat-log { flex: 1; height: auto; min-height: 40px; }
.ingame-chat .chat-input-row { flex: 0 0 auto; flex-direction: row; }

.reaction-bar { display: flex; gap: 5px; flex-wrap: wrap; flex: 0 0 auto; margin-bottom: 6px; }
.reaction-btn {
  font-size: 17px;
  line-height: 1;
  padding: 5px 8px;
  border-radius: 8px;
  background: var(--ink-3);
  border: 1px solid var(--border);
  cursor: pointer;
  transition: transform 100ms ease, background 120ms ease;
}
.reaction-btn:hover:not(:disabled) { background: var(--hover-bg); transform: translateY(-1px); }
.reaction-btn:disabled { opacity: 0.35; cursor: not-allowed; }

/* Floating reactions pop up over the board itself (where both players are
   actually looking) rather than staying tucked away in the sidebar. */
.reaction-overlay { position: absolute; inset: 0; pointer-events: none; overflow: hidden; border-radius: inherit; }
.floating-reaction {
  position: absolute;
  bottom: 8%;
  font-size: 38px;
  transform: translateX(-50%);
  animation: reaction-float 1.6s ease-out forwards;
  filter: drop-shadow(0 4px 10px rgba(0,0,0,0.45));
}
@keyframes reaction-float {
  0% { transform: translate(-50%, 0) scale(0.5); opacity: 0; }
  15% { transform: translate(-50%, -10px) scale(1.15); opacity: 1; }
  80% { transform: translate(-50%, -150px) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -190px) scale(0.9); opacity: 0; }
}

/* Tighter than the shared .plaque spacing below — the in-game sidebar
   stacks four sections (room/status/chat/players) that otherwise made it
   taller than a 9x9 board's own column, leaving the panel visibly
   protruding past where the board's controls end. */
#gameView .plaque { padding: 14px; gap: 10px; }
#gameView .plaque h2 { padding-bottom: 6px; }

.plaque {
  flex: 1 1 300px;
  max-width: 460px;
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.plaque h2, .chat-panel h2 {
  font-family: "Noto Serif Thai", Georgia, serif;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.03em;
  margin: 0;
  color: var(--paper);
  border-bottom: 1px solid var(--line);
  padding-bottom: 12px;
}

.auth-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
  padding-bottom: 12px;
}
.auth-panel-header h2 { border-bottom: none; padding-bottom: 0; }

.status-badges {
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 13.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--paper-dim); }

/* select base, select/button:focus-visible, select:disabled now in shared.css */
input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
input:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }

#playerControls { text-align: center; }
#playerControls .btn-row { justify-content: center; }

/* button base, .primary, .btn-sm now in shared.css */

.score-list { display: flex; flex-direction: column; gap: 10px; }
.score-row { display: flex; align-items: center; justify-content: space-between; font-size: 16px; }
.score-row .label { display: flex; align-items: center; gap: 8px; color: var(--paper-dim); }
.score-row .value { font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-variant-numeric: tabular-nums; font-size: 18px; color: var(--paper); }

.stone-dot { width: 13px; height: 13px; border-radius: 50%; flex: none; }
.stone-dot.black { background: radial-gradient(circle at 32% 28%, #6a6a6a, #050505 70%); box-shadow: 0 0 0 1px #555 inset; }
.stone-dot.white { background: radial-gradient(circle at 32% 28%, #ffffff, #cfcfcf 75%); }

.turn-row { display: flex; align-items: center; justify-content: space-between; background: var(--ink-3); border: 1px solid var(--border); border-radius: 6px; padding: 12px 14px; }
.turn-row .label { font-size: 13.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--paper-dim); }
.turn-row .value { display: flex; align-items: center; gap: 8px; font-family: "Noto Serif Thai", Georgia, serif; font-size: 19px; }
.turn-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--seal); box-shadow: 0 0 0 3px rgba(176,68,50,0.25); }

#status:empty { display: none; }
#status { margin-top: 12px; font-size: 15.5px; font-weight: 600; color: var(--seal-hi); }
#onlineStatus { font-size: 14.5px; color: var(--paper-dim); }

/* footer.credit now in shared.css */

.chat-panel { margin-top: 28px; background: var(--ink-2); border: 1px solid var(--line); border-radius: 8px; padding: 22px; }
.chat-layout { display: flex; gap: 14px; align-items: stretch; }
.chat-log { flex: 1; height: 96px; overflow-y: auto; background: var(--ink-3); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; font-size: 14.5px; display: flex; flex-direction: column; gap: 6px; }
.chat-input-row { flex: 0 0 260px; display: flex; flex-direction: column; gap: 8px; }
.chat-input-row input { flex: 1; min-width: 0; background: var(--ink-3); color: var(--paper); border: 1px solid var(--border); border-radius: 6px; padding: 9px 10px; font-family: inherit; font-size: 15.5px; }

/* ---- Lobby redesign ---- */
.lobby-grid {
  display: grid;
  /* Online-players column widened from 240px — with the early-adopter
     sprout, rating pill, and "คุณ" tag all possibly present on one row,
     240px only left ~20px for the username itself once it was made to
     truncate instead of overflow (see the player-name ellipsis fix). */
  grid-template-columns: 300px 1fr 280px;
  gap: 20px;
  align-items: start;
}
@media (max-width: 980px) { .lobby-grid { grid-template-columns: 1fr; } }

.field-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
.field-label { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--paper-faint); font-weight: 600; }

.chip-select { display: flex; gap: 6px; flex-wrap: wrap; }
.chip-select input { position: absolute; opacity: 0; pointer-events: none; }
.chip-select label {
  flex: 1 1 0; text-align: center; font-size: 15px; font-weight: 600; color: var(--paper-dim);
  background: var(--ink-3); border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 6px; cursor: pointer; transition: all 120ms ease; white-space: nowrap;
}
.chip-select input:checked + label { background: rgba(224,169,79,0.14); border-color: var(--focus); color: var(--wood-hi); }
.chip-select label:hover { border-color: var(--wood); color: var(--paper); }
.chip-select input:focus-visible + label { outline: 2px solid var(--focus); outline-offset: 2px; }

.create-btn {
  width: 100%; font-size: 16px; padding: 13px 18px;
  background: linear-gradient(180deg, var(--seal-hi), var(--seal));
  box-shadow: 0 6px 16px rgba(176,68,50,0.32);
  transition: transform 100ms ease, box-shadow 100ms ease, background 120ms ease;
}
.create-btn:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 8px 20px rgba(176,68,50,0.4); }

.rule-note { margin: 18px 0 0; padding-top: 16px; border-top: 1px solid var(--line); font-size: 13.5px; color: var(--paper-faint); line-height: 1.65; }
.rule-note strong { color: var(--paper-dim); font-weight: 600; }

.boards-panel .panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.count-pill {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-size: 13.5px; font-weight: 600;
  color: var(--wood-hi); background: rgba(220,184,119,0.12); border: 1px solid rgba(220,184,119,0.3);
  border-radius: 999px; padding: 3px 10px; white-space: nowrap;
}

.board-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 14px 16px; border-radius: 9px; background: var(--ink-3); border: 1px solid var(--line);
  margin-bottom: 10px; flex-wrap: wrap;
}
.board-row:last-child { margin-bottom: 0; }
.board-info { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.host-name { font-weight: 600; font-size: 16px; }
.board-badge {
  font-size: 13.5px; border-radius: 999px; padding: 3px 10px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  background: var(--ink-2); color: var(--paper-dim); border: 1px solid var(--line);
}
.board-badge.badge-waiting { background: rgba(176,68,50,0.14); color: var(--seal-hi); border-color: rgba(176,68,50,0.3); }
.board-badge.badge-playing { background: rgba(95,191,111,0.14); color: var(--good); border-color: rgba(95,191,111,0.3); }
.board-badge.badge-rated { background: rgba(220,184,119,0.14); color: var(--wood-hi); border-color: rgba(220,184,119,0.3); font-weight: 600; }
.board-actions { display: flex; gap: 8px; flex: none; }

/* .lobby-pagination, .btn-sm now in shared.css */

.empty-state { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 38px 20px 30px; color: var(--paper-dim); }
.empty-state .mini-mark {
  width: 52px; height: 52px; border-radius: 12px; margin-bottom: 16px;
  background: linear-gradient(155deg, var(--wood-hi) 0%, var(--wood) 45%, var(--wood-dark) 100%);
  display: flex; align-items: center; justify-content: center; box-shadow: 0 6px 16px rgba(0,0,0,0.35);
}
.empty-state p { margin: 0; font-size: 15px; max-width: 34ch; }
.empty-state .hint { margin-top: 6px; font-size: 14px; color: var(--paper-faint); }

.players-list { display: flex; flex-direction: column; gap: 4px; }
.player-row { display: flex; align-items: center; gap: 9px; padding: 8px 6px; border-radius: 7px; font-size: 15px; min-width: 0; }
.player-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); box-shadow: 0 0 0 3px rgba(95,191,111,0.18); flex: none; }
/* Absorbs the leftover row width so long usernames truncate instead of
   pushing the rating pill/"you" tag past the panel's edge (see the
   bookybookin overflow report). */
.player-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-row .you-tag { margin-left: auto; font-size: 12px; color: var(--paper-faint); font-weight: 600; background: var(--ink-3); border-radius: 999px; padding: 2px 8px; }
/* Single-glyph "joined during launch" marker — additional to, not a
   replacement for, the rating badge (unlike GM/top3), so it sits right
   after the name rather than where the rating pill goes. flex-shrink:0
   keeps it from ever being squeezed into wrapping like the rating pill
   once was (see the tier-top3 fix). */
.early-adopter-badge { flex: none; font-size: 13px; line-height: 1; cursor: default; }
.clickable-name { cursor: pointer; text-decoration: none; color: inherit; }
.clickable-name:hover { text-decoration: underline; text-decoration-color: var(--paper-dim); }

/* .dm-* (direct messages panel) now in shared.css */

/* Profile info + game history render inside #profileModal (markup below,
   logic in profile-view.js) — the small "ดูโปรไฟล์ / ทักแชท" popup here
   is just the entry point that opens it. */
.name-action-menu {
  position: fixed;
  z-index: 250;
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  box-shadow: 0 12px 28px rgba(0,0,0,0.4);
  padding: 5px;
  display: flex;
  flex-direction: column;
  min-width: 150px;
}
.name-action-menu button,
.name-action-menu a {
  display: block;
  background: none;
  border: none;
  color: var(--paper);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  text-decoration: none;
  padding: 8px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.name-action-menu button:hover,
.name-action-menu a:hover { background: var(--hover-bg); }

#lobbyView .chat-panel { background: var(--ink); border: 1px solid var(--line); padding: 4px; }
.chat-inner { background: var(--ink-2); border-radius: 9px; padding: 18px 20px; }
.chat-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.chat-head h2 { margin: 0; border-bottom: none; padding-bottom: 0; }
.chat-head .sub { font-size: 13.5px; color: var(--paper-faint); }

@media (max-width: 760px) {
  .plaque { flex-basis: 100%; max-width: none; }
  /* .ingame-chat's min-height+flex-grow only makes sense inside the
     desktop row, where align-items:stretch can hand it leftover height
     from a taller board column. Once .layout stacks there's no such
     row to stretch into, so that budget just caps the box at 110px
     while its actual children (reaction bar + chat log + input) need
     more — the overflow used to render underneath the next section,
     making the chat input untappable. Auto-size instead. */
  .ingame-chat { flex: 0 0 auto; min-height: 0; }
}
@media (max-width: 640px) {
  .chat-layout { flex-direction: column; }
  .chat-input-row { flex-basis: auto; flex-direction: row; }
  /* In column direction, flex:1 (flex-basis:0) overrides the fixed height
     above, letting the log grow with every message instead of scrolling
     within a fixed box — pin it back to a fixed height here. */
  .chat-log { flex: 0 0 auto; height: 160px; }
}

/* ---- Login screen ---- */
#loginScreen {
  min-height: calc(100vh - 100px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 20px 0;
}

.goban-mark { margin: 0 auto 18px; display: block; }

.server-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  color: var(--paper-dim);
  background: var(--ink-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 10px;
  flex: none;
}
.server-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  background: var(--paper-faint);
}
.server-status.connecting .server-status-dot {
  background: var(--focus);
  box-shadow: 0 0 0 3px rgba(224,169,79,0.22);
  animation: server-status-pulse 1.4s ease-in-out infinite;
}
.server-status.online .server-status-dot {
  background: var(--good);
  box-shadow: 0 0 0 3px rgba(95,191,111,0.22);
}
.server-status.offline .server-status-dot {
  background: var(--seal-hi);
  box-shadow: 0 0 0 3px rgba(207,89,64,0.22);
}
.server-status.disabled .server-status-dot {
  background: var(--paper-faint);
  box-shadow: none;
}
@keyframes server-status-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

#loginScreen .eyebrow,
#loginScreen .lede { margin-left: auto; margin-right: auto; }

#authPanel {
  width: 100%;
  max-width: 380px;
  margin: 28px auto 0;
  text-align: left;
  box-shadow: 0 30px 60px rgba(0,0,0,0.45);
}

#authPanel .field input,
#authPanel .field select {
  width: 100%;
}

/* Hidden until JS shows it post-login (shared.css's #appHeader sets
   display:flex; this page-specific override wins via source order). */
#appHeader { display: none; }
/* In gameView the board+sidebar row is centered at its own (narrower than
   the shell) natural width, not stretched edge-to-edge like the lobby
   grid is — so the header needs the same narrower, centered width here or
   it ends up stranded out near the browser edge. 1292px is the widest
   that row gets (19x19 board + gap + sidebar). */
#appHeader.game-header { max-width: 1292px; margin: 0 auto 8px; }

.app-header-sep { color: var(--paper-faint); font-size: 12px; flex: none; }
/* Every header element up through logoutBtn is flex:none (non-shrinking)
   except the user block, so on a narrow phone with the DM button added
   there's no longer room for everything — brand's own box got squeezed
   below its children's combined width, which don't clip (no overflow:
   hidden) and paint past their box into wherever .app-header-actions
   happens to be positioned, intercepting clicks meant for the avatar.
   Dropping the title text is the least-missed way to reclaim that room:
   the icon alone still reads as "this is the app" once you're using it. */
@media (max-width: 480px) {
  .app-header-title, .app-header-sep { display: none; }
}
/* min-width just enough for the avatar circle to always render inside its
   own box — at min-width:0 a cramped header (e.g. narrow mobile, once
   this became a click target) could shrink this to a genuine 0px box,
   which doesn't hide the avatar (still paints via overflow) but does let
   the next header button visually cover and intercept clicks meant for
   it, since it's no longer actually inside its own element's hit area. */
.app-header-user { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--paper-dim); min-width: 20px; }
.app-header-user-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-header-avatar {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: linear-gradient(155deg, var(--seal-hi), var(--seal));
  color: #fff2ea;
  font-size: 9px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
/* .app-header-actions, .theme-toggle now in shared.css */

/* A join request is easy to miss sitting quietly in the sidebar, so it's
   shown as a blocking modal (plus a sound, wired up in JS) instead. */
#joinRequestBox {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#joinRequestBox .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 28px;
  max-width: 360px;
  width: 100%;
  text-align: center;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  animation: join-request-pop 180ms ease;
}
@keyframes join-request-pop {
  from { transform: scale(0.92); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* Player profile — a modal overlay (not a route change/page load) so
   opening it never disturbs the lobby or a live game underneath, and the
   app stays a single page with no reloads. .panel/.panel-head/.panel-body
   come from shared.css; these just adapt them to a scrollable modal card. */
#profileModal {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#profileModal .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0;
  max-width: 720px;
  width: 100%;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  overflow: hidden;
  animation: join-request-pop 180ms ease;
}
#profileModal .panel-head { display: flex; align-items: center; gap: 10px; flex: none; }
#profileModal .panel-head h2 { flex: 1; min-width: 0; }
#profileModal .panel-body { flex: 1; overflow-y: auto; min-height: 0; }
/* #profileInfoView/#ghReplayView are the two swappable views inside the
   card (JS toggles each to display:flex/none) — they need their own flex
   column + min-height:0 so .panel-body's flex:1/overflow-y:auto above
   actually gets a sizing context to scroll within, instead of growing
   unbounded and getting clipped by .modal-card's overflow:hidden. */
#profileModal #profileInfoView,
#profileModal #ghReplayView {
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

#leaderboardModal {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#leaderboardModal .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0;
  max-width: 480px;
  width: 100%;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  overflow: hidden;
  animation: join-request-pop 180ms ease;
}
#leaderboardModal .panel-head { display: flex; align-items: center; gap: 10px; flex: none; }
#leaderboardModal .panel-head h2 { flex: 1; min-width: 0; }
#leaderboardModal .panel-body { flex: 1; overflow-y: auto; min-height: 0; }
.leaderboard-row {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 6px; border-radius: 7px; font-size: 15px;
}
.leaderboard-row:hover { background: var(--hover-bg); }
.leaderboard-rank { flex: none; width: 26px; color: var(--paper-faint); font-weight: 700; font-size: 13px; }
.leaderboard-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.player-dot.offline { background: var(--paper-faint); box-shadow: none; }
.tier-legend { display: flex; flex-direction: column; gap: 6px; }
.tier-legend-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 6px;
}
.tier-legend-range { font-size: 12.5px; color: var(--paper-dim); font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; }

.coin-balance {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 13.5px; font-weight: 700; color: #e6b950;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  flex: none; white-space: nowrap;
}
/* Custom flat two-tone coin (bright gold ring + darker gold-orange face,
   $ embossed) used for balances under 10 — see buildCoinIcon() in
   shop-view.js. Drawn with CSS rather than an emoji so the color/shape is
   exact, not left to whatever glyph the viewer's font/OS substitutes. */
.coin-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 13px; height: 13px; border-radius: 50%;
  background: #ffc738; flex: none;
}
.coin-icon-face {
  display: flex; align-items: center; justify-content: center;
  width: 10px; height: 10px; border-radius: 50%;
  background: #d98d00; color: #ffc738;
  font-weight: 900; font-size: 7px; line-height: 1;
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
}

#shopModal {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#shopModal .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0;
  max-width: 440px;
  width: 100%;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  overflow: hidden;
  animation: join-request-pop 180ms ease;
}
#shopModal .panel-head { display: flex; align-items: center; gap: 10px; flex: none; }
#shopModal .panel-head h2 { flex: 1; min-width: 0; }
#shopModal .panel-body { flex: 1; overflow-y: auto; min-height: 0; }

#gmModal {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#gmModal .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0;
  max-width: 400px;
  width: 100%;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  overflow: hidden;
  animation: join-request-pop 180ms ease;
}
#gmModal .panel-head { display: flex; align-items: center; gap: 10px; flex: none; }
#gmModal .panel-head h2 { flex: 1; min-width: 0; }
#gmModal .panel-body { flex: 1; overflow-y: auto; min-height: 0; }

#announcementModal {
  position: fixed;
  inset: 0;
  z-index: 210;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#announcementModal .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0;
  max-width: 440px;
  width: 100%;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  overflow: hidden;
  animation: join-request-pop 180ms ease;
}
#announcementModal .panel-head { display: flex; align-items: center; gap: 10px; flex: none; }
#announcementModal .panel-head h2 { flex: 1; min-width: 0; }
#announcementModal .panel-body { flex: 1; overflow-y: auto; min-height: 0; }
.announcement-item { background: var(--ink-3); border-radius: 8px; padding: 12px 14px; margin-bottom: 10px; }
.announcement-item-title { font-weight: 700; font-size: 15px; margin-bottom: 4px; }
.announcement-item-body { font-size: 13.5px; color: var(--paper-dim); line-height: 1.55; }
.shop-balance-row { font-size: 14px; color: var(--paper-dim); display: flex; align-items: center; gap: 6px; }
.shop-status { margin-top: 10px; padding: 8px 10px; border-radius: 7px; font-size: 13.5px; background: var(--ink-3); }
.shop-status.ok { color: var(--good); }
.shop-status.err { color: var(--time-warn); }
.shop-theme-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 8px; border-radius: 8px; margin-bottom: 6px;
  background: var(--ink-3);
}
.shop-theme-swatch { width: 22px; height: 22px; border-radius: 50%; flex: none; border: 1px solid rgba(0,0,0,0.3); }
.shop-theme-info { flex: 1; min-width: 0; }
.shop-theme-name { font-weight: 600; font-size: 14.5px; }
.shop-theme-price { font-size: 12.5px; color: var(--paper-dim); font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; }
.shop-theme-buy-btn { flex: none; }
.shop-theme-buy-btn[disabled] { opacity: 0.5; cursor: default; }

/* Create-table settings (board size/time/rated) used to sit inline in the
   lobby sidebar, but kept growing as more options were added (rated/free
   was the third field-group) until the panel felt cluttered before you'd
   even clicked anything. Moved behind the "สร้างกระดาน" button into its own
   modal instead — the sidebar now just has the two entry-point buttons. */
#createTableModal {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,0.65);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
#createTableModal .modal-card {
  background: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0;
  max-width: 420px;
  width: 100%;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  box-shadow: 0 30px 60px rgba(0,0,0,0.5);
  animation: join-request-pop 180ms ease;
}
#createTableModal .panel-head { display: flex; align-items: center; gap: 10px; }
#createTableModal .panel-head h2 { flex: 1; min-width: 0; }
