@charset "UTF-8";
/*
 * Gomoku styling.
 *
 * Component is single root .mg-gomoku, with two main views toggled
 * via [data-view] attributes (lobby vs match). Inherits theme tokens
 * from the parent .mg-frame (which the multiplayer game render path
 * wraps us in just like single-player games).
 */

.mg-gomoku {
    font-family: var(--mg-body);
    color: var(--mg-ink);
}

/* ---------------------------------------------------------------------
 * Login gate
 * ------------------------------------------------------------------ */
.mg-gomoku__gate {
    text-align: center;
    padding: 32px 16px;
}
.mg-gomoku__gate-msg {
    color: var(--mg-ink-muted);
    margin: 12px 0 0;
}

/* ---------------------------------------------------------------------
 * Lobby
 * ------------------------------------------------------------------ */
.mg-gomoku__section {
    margin-top: 24px;
}
.mg-gomoku__section-title {
    margin: 0 0 12px;
    font-size: 14px;
    font-weight: 600;
    color: var(--mg-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    /* Explicit font-family overrides aggressive site-theme rules
       like `h1,h2,h3 { font-family: ... }` that would otherwise win
       on specificity against inherited frame fonts. */
    font-family: var(--mg-display);
}

.mg-gomoku__match-list {
    list-style: none;
    margin: 0;
    /* Reset ALL padding directions, including the modern logical
       padding-inline-start that browsers default to 40px on <ul>.
       Setting `padding: 0` alone doesn't always override
       padding-inline-start in some site themes that explicitly set it.
       Using both physical and logical properties is belt-and-suspenders. */
    padding: 0;
    padding-inline-start: 0;
    padding-inline-end: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.mg-gomoku__match-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    /* Reset list-item padding leak (see .mg-gomoku__empty above) */
    padding-inline-start: 16px;
    margin: 0;
    background: var(--mg-bg-2);
    border: 1px solid var(--mg-line);
    border-radius: var(--mg-radius-sm);
    flex-wrap: wrap;
    list-style: none;
    transition: border-color .12s, background .12s;
}
.mg-gomoku__match-row:hover {
    border-color: var(--mg-line-strong);
}
.mg-gomoku__match-label {
    flex: 1 1 auto;
    font-size: 14px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.mg-gomoku__match-meta {
    color: var(--mg-ink-subtle);
    font-size: 12px;
    margin-left: 8px;
}
/* Size chip in lobby rows — small pill marking the board size, e.g.
 * "15×15", in front of the player names. Uses the muted/dotted dot
 * styling so it sits visually behind the player names rather than
 * competing with them. */
.mg-gomoku__size-chip {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    color: var(--mg-ink-muted);
    background: var(--mg-bg-1);
    border: 1px solid var(--mg-line);
    border-radius: 4px;
    padding: 2px 6px;
    margin-right: 8px;
    letter-spacing: 0.02em;
    /* Don't shrink when row is tight — chip should always show. */
    flex: 0 0 auto;
    white-space: nowrap;
    vertical-align: middle;
}
.mg-gomoku__match-row .mg-btn {
    flex: 0 0 auto;
}
.mg-gomoku__empty {
    padding: 18px 14px;
    /* Reset li default padding-inline-start — even with list-style:none,
       browsers/themes can apply padding to li elements that pushes
       content to the right. We set our own padding above and need to
       defeat any inherited start-padding. */
    padding-inline-start: 14px;
    margin: 0;
    color: var(--mg-ink-subtle);
    font-size: 13px;
    text-align: center;
    background: var(--mg-bg-2);
    border: 1px dashed var(--mg-line);
    border-radius: var(--mg-radius-sm);
    list-style: none;
}
.mg-gomoku__badge {
    display: inline-block;
    background: var(--mg-accent);
    color: var(--mg-accent-ink);
    padding: 3px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/* ---------------------------------------------------------------------
 * Create-match modal
 *
 * Floating overlay that appears after clicking "建立對局" — gives the
 * user the privacy choice before the room is actually created.
 * Implemented as a fixed-position dialog (not <dialog>) for iOS Safari
 * compatibility. The backdrop is a subtle dim that doesn't fully block
 * the lobby below — feels like a pop-over rather than a hard modal.
 * ------------------------------------------------------------------ */
.mg-gomoku__create-modal {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.55);
    z-index: 9000;
    padding: 16px;
}
.mg-gomoku__create-modal[hidden] {
    display: none;
}
.mg-gomoku__create-card {
    background: var(--mg-bg-2);
    border: 1px solid var(--mg-line-strong);
    border-radius: var(--mg-radius);
    padding: 20px 22px;
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.5);
    width: 100%;
    max-width: 420px;
}
.mg-gomoku__create-title {
    margin: 0 0 14px;
    font-size: 16px;
    font-weight: 600;
    color: var(--mg-ink);
    font-family: var(--mg-display);
}
.mg-gomoku__create-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px;
    background: var(--mg-bg);
    border: 1px solid var(--mg-line);
    border-radius: var(--mg-radius-sm);
    cursor: pointer;
    font-size: 14px;
    transition: border-color .12s;
}
.mg-gomoku__create-row:hover {
    border-color: var(--mg-line-strong);
}
.mg-gomoku__create-row input[type="checkbox"] {
    accent-color: var(--mg-accent);
    margin-top: 2px;
    flex: 0 0 auto;
}
.mg-gomoku__create-hint {
    display: block;
    font-size: 12px;
    color: var(--mg-ink-muted);
    margin-top: 4px;
    line-height: 1.4;
}
.mg-gomoku__create-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 16px;
}

/* Practice (vs AI) — header actions and difficulty picker.
 * The lobby header now has up to 2 buttons (Practice + Create Match).
 * Wraps onto a new line on narrow screens. */
.mg-gomoku__header-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}
.mg-gomoku__difficulty-grid {
    display: grid;
    /* `minmax(0, 1fr)` instead of `1fr` lets columns shrink below their
       min-content width — without it, long uppercase subtitles like
       "EXPECT TO LOSE" force the column wider than the modal and the
       text gets clipped at the right edge. */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    margin-bottom: 8px;
}
@media (max-width: 480px) {
    .mg-gomoku__difficulty-grid {
        grid-template-columns: 1fr;
    }
}
/* These three selectors are all specificity 0,2,1 — same as the global
   `.mg-frame button.mg-btn` rule in core.css that sets uppercase + 0.08em
   letter-spacing. We need to match that specificity (and rely on later
   load order) to actually override the inherited transform/spacing on
   this variant; a plain `.mg-gomoku__difficulty-btn` (0,1,0) loses. */
.mg-gomoku button.mg-gomoku__difficulty-btn {
    /* Override .mg-btn defaults to be a vertical block: title above,
       hint below. Padding generous for touch targets. */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 12px 6px;
    text-align: center;
    line-height: 1.3;
    height: auto;
    /* Kill the inherited uppercase + wide letter-spacing globally on
       this variant — both the title and subtitle inherit from here, so
       this is the right place to reset. The title selector below opts
       back into uppercase for visual punch. */
    text-transform: none;
    letter-spacing: 0;
    font-weight: 400;
    /* Let the button shrink in the grid track. Without these, a long
       child word forces the column wider than the modal allows. */
    min-width: 0;
    box-sizing: border-box;
}
.mg-gomoku button.mg-gomoku__difficulty-btn strong {
    font-size: 15px;
    font-weight: 700;
    color: var(--mg-ink);
    /* Title is always short ("EASY" / "簡單"), so re-apply uppercase
       and slight tracking just here for the heading look. */
    text-transform: uppercase;
    letter-spacing: 0.06em;
    min-width: 0;
}
.mg-gomoku button.mg-gomoku__difficulty-btn span {
    font-size: 11px;
    color: var(--mg-ink-muted);
    font-weight: 400;
    line-height: 1.25;
    /* Subtitle is a sentence; allow wrap onto 2 lines and break inside
       words if absolutely necessary so it never overflows the column. */
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
    max-width: 100%;
    min-width: 0;
}
@media (max-width: 380px) {
    /* Very narrow viewports — drop subtitle font further so the 3-up
       layout still holds on phones held in portrait without stacking. */
    .mg-gomoku button.mg-gomoku__difficulty-btn span { font-size: 10px; }
}
.mg-gomoku__difficulty-btn:hover strong {
    color: var(--mg-accent);
}

/* Practice-mode badge inside match view — tells user this isn't ranked.
 *
 * Visibility model: badge is hidden by default at the CSS layer. JS
 * adds `data-practice-active` to the host `.mg-gomoku` root when
 * practice mode is on, which flips the badge to visible. Belt-and-
 * braces — even if a server-side page cache serves stale HTML where
 * the `hidden` attribute was somehow stripped, or if the JS bindings
 * fail to find the element, the CSS rule keeps the badge invisible
 * until practice is genuinely active.
 *
 * Compact pill (not a full-width strip) so it sits unobtrusively in
 * the header row alongside the back-to-lobby button. Single line, no
 * wrap — keeps the badge from inflating to fill the header on narrow
 * mobile screens. The size/difficulty detail is included so the user
 * always knows what they're playing. */
.mg-gomoku__practice-badge {
    display: none;   /* default hidden — JS opts in via data-practice-active */
    align-items: center;
    gap: 6px;
    flex-wrap: nowrap;
    white-space: nowrap;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--mg-accent);
    background: var(--mg-accent-soft, rgba(255,82,82,0.08));
    border: 1px solid var(--mg-accent);
    border-radius: 999px;
    padding: 4px 12px;
    /* Vertical breathing room on desktop so the glowing border has
       space from the frame edge above and the player strip below.
       Other items in the header row (back button, status) keep their
       natural spacing — only the badge needed the extra room because
       its accent border was visually clashing with the frame's
       inner-glow at the top. Mobile overrides to 0 since the
       column-stacked header already gives natural separation. */
    margin: 12px 0;
    font-weight: 600;
    line-height: 1.4;
    flex: 0 0 auto;
    /* Don't stretch to full row width when the parent flex container
       has `align-items: stretch` (which the match-header uses on
       mobile via the @media (max-width:540px) column layout). Without
       this the badge inflates to 100% width on phones. */
    align-self: flex-start;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    box-sizing: border-box;
}
/* Reveal the badge only when JS has put the host into practice mode.
 * This is the canonical visibility gate; the [hidden] attribute on
 * the element itself is now redundant but kept for defensive parity. */
.mg-gomoku[data-practice-active="1"] .mg-gomoku__practice-badge {
    display: inline-flex;
}
.mg-gomoku__practice-badge-sep {
    opacity: 0.45;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
}
.mg-gomoku__practice-badge-detail {
    font-weight: 500;
    /* Make the detail (size/difficulty) feel secondary — slightly
       muted compared to the bright accent of the main label. */
    opacity: 0.85;
    /* Don't shout the difficulty. The badge wrapper has uppercase set
       for the main "Practice Mode" label; the detail (e.g. "中等",
       "17 × 17") reads better in its native casing. */
    text-transform: none;
    letter-spacing: 0;
}

/* ---------------------------------------------------------------------
 * Match view
 * ------------------------------------------------------------------ */
.mg-gomoku__match-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}
.mg-gomoku__status {
    flex: 1 1 auto;
    text-align: right;
    font-size: 14px;
    color: var(--mg-ink-muted);
    min-height: 1.2em;
    word-break: break-all;
    font-weight: 500;
}
/* When status is "your turn", make it stand out via accent color */
.mg-gomoku__status.is-your-turn {
    color: var(--mg-accent);
    text-shadow: 0 0 8px var(--mg-accent-glow);
}

.mg-gomoku__players {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
    padding: 14px 16px;
    background: var(--mg-bg-2);
    border: 1px solid var(--mg-line);
    border-radius: var(--mg-radius-sm);
}
.mg-gomoku__player {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 14px;
    padding: 6px 10px;
    border-radius: var(--mg-radius-xs);
    transition: background .15s, box-shadow .15s;
    min-width: 0;
}
.mg-gomoku__player[data-player="2"] {
    justify-content: flex-end;
}
/* Active-turn highlighting: subtle bg + accent-tinted glow */
.mg-gomoku__player.is-active {
    background: var(--mg-bg-3);
    box-shadow: 0 0 0 1px var(--mg-accent), 0 0 12px var(--mg-accent-glow);
}
.mg-gomoku__stone {
    display: inline-block;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    flex: 0 0 auto;
    box-shadow: inset -2px -2px 4px rgba(0,0,0,0.25);
}
.mg-gomoku__stone--black {
    background: radial-gradient(circle at 35% 35%, #555, #000);
    border: 1px solid #000;
}
.mg-gomoku__stone--white {
    background: radial-gradient(circle at 35% 35%, #fff, #bbb);
    border: 1px solid #888;
}
.mg-gomoku__player-name {
    font-weight: 600;
    color: var(--mg-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1 1 auto;
}
.mg-gomoku__clock {
    font-family: var(--mg-mono);
    font-size: 13px;
    color: var(--mg-ink-muted);
    min-width: 44px;
    text-align: center;
    flex: 0 0 auto;
    /* Use tabular numerals so digits don't shift width as time ticks */
    font-variant-numeric: tabular-nums;
}
/* When a clock is below 30 seconds, color it red as a warning */
.mg-gomoku__clock.is-low {
    color: #ff5252;
    font-weight: 700;
}
.mg-gomoku__vs {
    font-size: 11px;
    font-weight: 700;
    color: var(--mg-ink-subtle);
    letter-spacing: 0.12em;
}

/* ---------------------------------------------------------------------
 * Board canvas
 * ------------------------------------------------------------------ */
.mg-gomoku__board-wrap {
    display: flex;
    justify-content: center;
    margin-bottom: 16px;
}
.mg-gomoku__board {
    display: block;
    width: 100%;
    max-width: 600px;
    height: auto;
    border-radius: var(--mg-radius-sm);
    border: 1px solid var(--mg-line);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    cursor: crosshair;
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
    /* Belt-and-suspenders tap-highlight reset (see junglechess.css for
       full rationale) — kills the translucent blue overlay Android
       Chrome paints over <canvas> on tap. Selection / hover state is
       rendered onto the canvas itself. */
    -webkit-tap-highlight-color: transparent;
    outline: none;
}
.mg-gomoku__board:focus,
.mg-gomoku__board:focus-visible {
    outline: none;
}

.mg-gomoku__actions {
    display: flex;
    justify-content: center;
    gap: 12px;
}

/* ---------------------------------------------------------------------
 * Mobile
 * ------------------------------------------------------------------ */
@media (max-width: 540px) {
    .mg-gomoku__match-header { flex-direction: column; align-items: stretch; }
    .mg-gomoku__match-header .mg-btn { align-self: flex-start; }
    /* Mobile column layout already separates rows naturally — drop the
       desktop's 12px badge top/bottom margin so it doesn't double-space. */
    .mg-gomoku__practice-badge { margin: 0; }
    .mg-gomoku__status      { font-size: 13px; text-align: left; }

    /* Compact player strip on mobile — drop the "VS" pill, stack
       names + clocks more efficiently. */
    .mg-gomoku__players {
        padding: 10px 12px;
        gap: 6px;
    }
    .mg-gomoku__vs { font-size: 10px; }
    .mg-gomoku__player {
        padding: 4px 6px;
        font-size: 13px;
        gap: 6px;
    }
    .mg-gomoku__player-name { font-size: 12px; }
    .mg-gomoku__clock { font-size: 12px; min-width: 38px; }
    .mg-gomoku__stone { width: 16px; height: 16px; }

    /* Larger boards (17/19) get a min-width so cells stay tap-friendly.
       At 380px viewport, a 19×19 board would have ~20px cells — below
       the 24px finger-tap minimum. We bump to 460px (≈24px cells) for
       19×19 and 400px (~23.5px cells) for 17×17. The wrap allows
       horizontal scroll when the board exceeds viewport, and the canvas
       opts into pan-x touch handling so a horizontal swipe scrolls
       instead of being eaten by the click guard. Click-without-pan
       still fires normally (tap = click event, swipe ≠ click). */
    .mg-gomoku__board[data-size="17"],
    .mg-gomoku__board[data-size="19"] {
        touch-action: pan-x;
    }
    .mg-gomoku__board[data-size="17"] {
        min-width: 400px;
    }
    .mg-gomoku__board[data-size="19"] {
        min-width: 460px;
    }
    /* Wrap that contains the over-sized canvas needs to scroll-x and
       align left so the user starts at column 1 rather than centred
       (centring would clip both edges equally). The wrap mirrors the
       canvas's size as data-board-size, set by drawBoard. */
    .mg-gomoku__board-wrap[data-board-size="17"],
    .mg-gomoku__board-wrap[data-board-size="19"] {
        overflow-x: auto;
        justify-content: flex-start;
        /* Momentum scroll on iOS. */
        -webkit-overflow-scrolling: touch;
    }

    /* Match list rows stack on narrow screens — name on top, button
       full-width below. */
    .mg-gomoku__match-row { padding: 12px 14px; gap: 10px; }
    .mg-gomoku__match-row .mg-btn { width: 100%; }
    .mg-gomoku__match-label { width: 100%; }
}

/* ---------------------------------------------------------------------
 * Arcade theme tweaks — match the cyberpunk-neon vibe of other games
 * ------------------------------------------------------------------ */
.mg-theme-arcade .mg-gomoku__match-row {
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.02);
}
.mg-theme-arcade .mg-gomoku__badge {
    background: transparent;
    border: 1px solid var(--mg-accent);
    color: var(--mg-accent);
    text-shadow: 0 0 4px var(--mg-accent-glow);
}
.mg-theme-arcade .mg-gomoku__board {
    box-shadow:
        0 0 16px var(--mg-accent-glow),
        0 4px 12px rgba(0, 0, 0, 0.4);
}
.mg-theme-arcade .mg-gomoku__vs {
    color: var(--mg-accent);
    text-shadow: 0 0 6px var(--mg-accent-glow);
}

/* ------------------------------------------------------------------
 * Board-size picker — appears in both Practice and Create-Match modals.
 * Three radio chips arranged in a row, with the chosen size showing a
 * highlighted accent border. The radio input itself is visually hidden
 * but the label-as-chip pattern keeps it fully keyboard-accessible.
 * ------------------------------------------------------------------ */
.mg-gomoku__size-picker {
    /* Reset fieldset chrome */
    border: 0;
    padding: 0;
    margin: 0 0 14px 0;
    /* Layout: legend on its own full-width row, then 3 chip-columns
       below. Grid lets us anchor the legend without needing a wrapper
       div inside the fieldset (which the HTML spec frowns on). */
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: 8px;
    row-gap: 0;
}
.mg-gomoku__size-picker > legend {
    font-size: 11px;
    color: var(--mg-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0;
    margin: 0 0 6px 0;
    grid-column: 1 / -1;   /* full-width legend above the chip row */
}
/* The visually-hidden radio input. Anchored absolutely so it doesn't
   take layout space; preserves keyboard access (Tab + Space toggles). */
.mg-gomoku__size-option {
    display: block;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    /* Position context for the absolutely-positioned radio input. */
    position: relative;
    min-width: 0;
}
.mg-gomoku__size-option > input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 1px;
    height: 1px;
}
.mg-gomoku__size-option > span {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 10px 6px;
    border: 1px solid var(--mg-line);
    border-radius: 4px;
    background: var(--mg-bg-2);
    color: var(--mg-ink);
    font-weight: 600;
    font-size: 14px;
    text-align: center;
    transition: border-color 0.12s ease, background 0.12s ease;
    min-width: 0;
}
.mg-gomoku__size-option > span > small {
    font-size: 10px;
    font-weight: 400;
    color: var(--mg-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.mg-gomoku__size-option:hover > span {
    border-color: var(--mg-line-strong);
    background: var(--mg-bg-1);
}
.mg-gomoku__size-option > input[type="radio"]:checked + span {
    border-color: var(--mg-accent);
    background: var(--mg-accent-soft, var(--mg-bg-1));
    box-shadow: 0 0 0 1px var(--mg-accent);
    color: var(--mg-accent);
}
.mg-gomoku__size-option > input[type="radio"]:focus-visible + span {
    outline: 2px solid var(--mg-accent);
    outline-offset: 2px;
}
@media (max-width: 380px) {
    .mg-gomoku__size-option > span { font-size: 13px; padding: 8px 4px; }
    .mg-gomoku__size-option > span > small { font-size: 9px; }
}
