@charset "UTF-8";
/**
 * Minesweeper — DOM grid styling.
 *
 * The board uses CSS Grid. --mgm-cols and --mgm-rows are set inline
 * by JS at buildBoard() time. Cell size scales by difficulty since
 * hard mode (16x30) needs smaller cells to fit common viewports.
 */

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

/* -------- Difficulty tabs — modern pill style -------- */
.mgm-tabs {
    display: inline-flex;
    gap: 4px;
    margin-bottom: 16px;
    padding: 4px;
    background: var(--mg-bg-2);
    border-radius: var(--mg-radius-sm);
}
button.mgm-tab,
.mgm-tab {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--mg-ink-muted);
    font-family: var(--mg-body);
    font-size: 13px;
    font-weight: 600;
    padding: 7px 14px;
    border-radius: var(--mg-radius-xs);
    cursor: pointer;
    transition: color .12s, background .12s;
}
button.mgm-tab:hover,
.mgm-tab:hover {
    color: var(--mg-ink);
    background: transparent;
}
button.mgm-tab.is-active,
.mgm-tab.is-active {
    background: var(--mg-bg-3);
    color: var(--mg-ink);
    box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}

.mg-theme-arcade .mgm-tabs {
    background: transparent;
    padding: 0;
    border-bottom: 1px solid var(--mg-line);
    padding-bottom: 8px;
}
.mg-theme-arcade .mgm-tab {
    border: 1px solid var(--mg-line);
    border-radius: var(--mg-radius-xs);
}
.mg-theme-arcade .mgm-tab.is-active {
    color: var(--mg-accent);
    border-color: var(--mg-accent);
    background: transparent;
}

/* -------- Stage -------- */
.mgm-stage {
    position: relative;
    overflow-x: auto;       /* hard mode (16x30) overflows on narrow viewports */
    padding-bottom: 6px;
}

/* When the game hasn't started yet, the dialog is showing and the
   board behind is just decorative — there's nothing to scroll to.
   Lock scroll so an accidental swipe doesn't move the empty board
   under the dialog. Once the game starts (or ends after a boom,
   which the player will want to scroll across to inspect every
   revealed mine on a hard 16×30 board), scrolling is restored. */
.mgm[data-state="idle"] .mgm-stage {
    overflow-x: hidden;
}

.mgm-board {
    /* Desktop cell sizes — calibrated against a typical content-area
       width of 800-900px. Easy (9 cols) at 44px ~= 400px, medium (16
       cols) at 38px ~= 615px, hard (30 cols) at 28px ~= 855px. Cells
       scale down by mode so wide hard boards still fit. */
    --mgm-cell: 44px;
    display: grid;
    grid-template-columns: repeat(var(--mgm-cols, 9), var(--mgm-cell));
    grid-auto-rows: var(--mgm-cell);
    gap: 2px;
    padding: 6px;
    background: var(--mg-bg-3);
    border-radius: var(--mg-radius-sm);
    width: max-content;
    margin: 0 auto;
}
.mg-theme-arcade .mgm-board {
    border: 1px solid var(--mg-line-strong);
}

.mgm-board[data-difficulty="medium"] {
    --mgm-cell: 38px;
}
.mgm-board[data-difficulty="hard"] {
    --mgm-cell: 28px;
}

/* -------- Cell -------- */
button.mgm-cell,
.mgm-cell {
    appearance: none;
    background: var(--mg-bg-2);
    border: 0;
    border-radius: var(--mg-radius-xs);
    padding: 0;
    margin: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
    font-family: var(--mg-display);
    font-size: 18px;
    font-weight: 700;
    line-height: 1;
    color: var(--mg-ink);
    user-select: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background .08s ease;
    /* relative positioning so bad-flag ✕ overlay can absolute-position correctly */
    position: relative;
}
.mg-theme-clean .mgm-cell {
    box-shadow: 0 1px 0 rgba(0,0,0,0.25);
}
.mg-theme-arcade .mgm-cell {
    background: var(--mg-bg-2);
    border: 1px solid var(--mg-line-strong);
    border-radius: 2px;
    box-shadow:
        inset 1px 1px 0 rgba(255,255,255,0.08),
        inset -1px -1px 0 rgba(0,0,0,0.35);
}

.mgm-cell:hover:not(:disabled):not(.is-revealed) {
    background: var(--mg-bg-3);
}
.mgm-cell:focus-visible {
    outline: 2px solid var(--mg-accent);
    outline-offset: -2px;
}

/* Revealed = sunken */
.mgm-cell.is-revealed {
    background: var(--mg-bg);
    box-shadow: none;
    cursor: default;
}
.mg-theme-arcade .mgm-cell.is-revealed {
    background: var(--mg-bg-3);
    border-color: var(--mg-line);
}

/* Flag — proper flag SVG via mask-image so the glyph follows theme
 * accent color via background-color.
 *
 * Color choice (v0.22.62): white (#fff) instead of red. Rationale:
 * traditional Minesweeper iconography uses red, but in our dark theme
 * the red flag visually fights with the dark cell background and the
 * red mine-hit cell. A white flag has better contrast against the
 * dark cell, reads as cleaner/more premium, and stays distinguishable
 * from the mine-hit state (which now uses black bg + white glow,
 * different shape entirely so no confusion).
 *
 * Centered via absolute + inset:0 + flex, NOT block + auto-margin.
 * The cell already has `position: relative`, so absolute positioning
 * inside it gives us a child the exact size of the cell which we
 * then flex-center the SVG mask within. Block-level pseudo with
 * fixed width/height would land in the top-left of the cell instead
 * of being centered like the digit numbers are.
 */
.mgm-cell.is-flagged {
    background: var(--mg-bg-2);
}
.mgm-cell.is-flagged::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: #ffffff;
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 21V3h1.5v3h11.2c.5 0 .8.6.4 1l-2.6 3 2.6 3c.4.4.1 1-.4 1H6.5v7H5z'/></svg>");
    mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 21V3h1.5v3h11.2c.5 0 .8.6.4 1l-2.6 3 2.6 3c.4.4.1 1-.4 1H6.5v7H5z'/></svg>");
    -webkit-mask-size: 60% 60%;
    mask-size: 60% 60%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    pointer-events: none;
    /* Subtle white glow — gives the flag a slight neon presence in
     * arcade theme and clean theme alike, without being aggressive. */
    filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.35));
}
.mg-theme-arcade .mgm-cell.is-flagged::before {
    background-color: #ffffff;
    /* Stronger glow in arcade theme to match its neon visual vocabulary */
    filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.6));
}

/* Mine reveal — non-triggered mines shown after game over.
 *
 * Color choice (v0.22.62): white star instead of pink, to match the
 * white-flag aesthetic and give the "all the mines I missed" reveal
 * a calmer, more neutral feel than the bright pink-on-dark mismatch.
 * The triggering mine (is-mine-hit below) uses a dramatic black-bg
 * + white-glow treatment to distinguish "this is the one that killed
 * you" from "this is one of the mines you didn't hit". */
.mgm-cell.is-mine {
    background: var(--mg-bg-3);
}
.mgm-cell.is-mine::before {
    content: "✸";
    color: #ffffff;
    font-size: 16px;
    /* Soft glow so the white star reads as luminous, not just plain
     * white. Subtle in clean theme, bumped in arcade below. */
    text-shadow: 0 0 4px rgba(255, 255, 255, 0.45);
}
.mg-theme-arcade .mgm-cell.is-mine::before {
    font-size: 18px;
    text-shadow: 0 0 7px rgba(255, 255, 255, 0.65);
}

/* The triggering mine — "黑底白光邊" (v0.22.62).
 *
 * Visual contract: when the player clicks a mine, the SPECIFIC cell
 * that ended the game gets a black background and a brightly-glowing
 * white star. This stands out from the other revealed mines (white
 * star on neutral grey bg) so the player immediately sees which mine
 * was the fatal one. The black bg also reads as "scorched" — the
 * crater left by the explosion — without resorting to red gore.
 *
 * Three layered light effects:
 *   1. inset white box-shadow → simulates the "rim light" of an
 *      explosion: brighter near the cell edges, darker at center
 *   2. outer white box-shadow → spillover bloom outside the cell,
 *      catching the eye
 *   3. strong text-shadow on the ✸ glyph → the star itself glows
 *      hottest
 *
 * Keyboard / motion preferences: no animation (the bright glow
 * already draws attention; an animation would feel like a trauma
 * trigger for someone who just lost). Static frame, max impact. */
.mgm-cell.is-mine-hit {
    background: #050505;
    box-shadow:
        inset 0 0 0 1.5px rgba(255, 255, 255, 0.85),
        inset 0 0 14px rgba(255, 255, 255, 0.45),
        0 0 18px rgba(255, 255, 255, 0.55);
}
.mgm-cell.is-mine-hit::before {
    color: #ffffff;
    text-shadow:
        0 0 8px rgba(255, 255, 255, 1),
        0 0 16px rgba(255, 255, 255, 0.85),
        0 0 24px rgba(255, 255, 255, 0.5);
    font-size: 20px;
    font-weight: 700;
}
.mg-theme-arcade .mgm-cell.is-mine-hit {
    /* Arcade theme: amp up the bloom for the neon vocabulary */
    box-shadow:
        inset 0 0 0 2px rgba(255, 255, 255, 0.95),
        inset 0 0 18px rgba(255, 255, 255, 0.55),
        0 0 26px rgba(255, 255, 255, 0.75);
}

/* Wrong flag (had a flag but no mine) — show ✕ overlay over a faded
 * grey flag, so user sees "you flagged this but there was no mine".
 *
 * Visual contract: stronger red-tinted bg than active flag's bg-2 +
 * a clearly desaturated grey flag (NOT red — the whole point is to
 * say "this isn't a real mine"). The ✕ is large and red so it reads
 * as a strikethrough on the flag.
 *
 * IMPORTANT: this state assumes is-flagged has been REMOVED from the
 * element (see minesweeper.js reveal-on-loss). If both classes coexist
 * the .is-flagged red mask wins specificity (especially in arcade
 * theme) and the user sees a normal-looking red flag instead of a
 * cancelled one. */
.mgm-cell.is-bad-flag {
    background: rgba(255, 69, 58, 0.28);
}
.mgm-cell.is-bad-flag::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: rgba(235, 235, 245, 0.45);
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 21V3h1.5v3h11.2c.5 0 .8.6.4 1l-2.6 3 2.6 3c.4.4.1 1-.4 1H6.5v7H5z'/></svg>");
    mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 21V3h1.5v3h11.2c.5 0 .8.6.4 1l-2.6 3 2.6 3c.4.4.1 1-.4 1H6.5v7H5z'/></svg>");
    -webkit-mask-size: 55% 55%;
    mask-size: 55% 55%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    pointer-events: none;
}
.mgm-cell.is-bad-flag::after {
    content: "✕";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--mg-red);
    font-size: 22px;
    font-weight: 800;
    line-height: 1;
    text-shadow: 0 0 4px rgba(0, 0, 0, 0.5);
    pointer-events: none;
}

/* Number colors — classic Minesweeper hierarchy */
.mgm-cell.is-n1 { color: var(--mg-blue); }
.mgm-cell.is-n2 { color: var(--mg-green); }
.mgm-cell.is-n3 { color: var(--mg-red); }
.mgm-cell.is-n4 { color: var(--mg-purple); }
.mgm-cell.is-n5 { color: var(--mg-orange); }
.mgm-cell.is-n6 { color: var(--mg-cyan); }
.mgm-cell.is-n7 { color: var(--mg-yellow); }
.mgm-cell.is-n8 { color: var(--mg-ink-muted); }

/* -------- Flag-mode toggle button --------
 *
 * v0.22.62: switched accent color from red (#ff453a family) to white.
 * Matches the new white flag glyph in the grid. Keeps "this is a special
 * mode" affordance via the glow effect — only the hue changed.
 */
button.mg-btn.mgm-flag-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--mg-ink);
}
.mgm-flag-toggle .mgm-flag-icon {
    display: inline-block;
    width: 14px;
    height: 14px;
    background-color: #ffffff;
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 21V3h1.5v3h11.2c.5 0 .8.6.4 1l-2.6 3 2.6 3c.4.4.1 1-.4 1H6.5v7H5z'/></svg>");
    mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 21V3h1.5v3h11.2c.5 0 .8.6.4 1l-2.6 3 2.6 3c.4.4.1 1-.4 1H6.5v7H5z'/></svg>");
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.4));
}
/* Active state: white neon glow takes over (matches the flag color) */
button.mg-btn.mgm-flag-toggle.is-active {
    color: #ffffff;
    border-color: rgba(255, 255, 255, 0.85);
    background: rgba(255, 255, 255, 0.06);
    box-shadow:
        0 0 12px rgba(255, 255, 255, 0.35),
        inset 0 0 8px rgba(255, 255, 255, 0.08);
    text-shadow: 0 0 4px rgba(255, 255, 255, 0.5);
    /* Same breathing pulse as primary, now white */
    animation: mgm-flag-pulse 1.4s ease-in-out infinite;
}
.mgm-flag-toggle.is-active .mgm-flag-icon {
    background-color: #ffffff;
}
button.mg-btn.mgm-flag-toggle.is-active:hover,
button.mg-btn.mgm-flag-toggle.is-active:focus-visible {
    color: #ffffff;
    border-color: rgba(255, 255, 255, 0.95);
    background: rgba(255, 255, 255, 0.12);
}
button.mg-btn.mgm-flag-toggle.is-active:active {
    animation: none;
    background: rgba(255, 255, 255, 0.85);
    color: #0a0a0c;
    box-shadow: 0 0 24px 4px rgba(255, 255, 255, 0.55);
}
/* v0.22.64: flag icon must invert to dark when the button is being
 * pressed in its is-active state. Without this, the white flag mask
 * (background-color: #ffffff) disappears against the white button
 * background of :active, leaving an empty space where the icon was —
 * the user reported seeing only the label "翻開" with no flag glyph.
 * The :active state lasts only while the pointer is held down, so
 * this is a brief but jarring state change without the inversion. */
.mgm-flag-toggle.is-active:active .mgm-flag-icon {
    background-color: #0a0a0c;
    filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.35));
}

@keyframes mgm-flag-pulse {
    0%, 100% {
        box-shadow:
            0 0 8px rgba(255, 255, 255, 0.25),
            inset 0 0 6px rgba(255, 255, 255, 0.06);
    }
    50% {
        box-shadow:
            0 0 22px rgba(255, 255, 255, 0.55),
            inset 0 0 12px rgba(255, 255, 255, 0.14);
    }
}
@media (prefers-reduced-motion: reduce) {
    .mgm-flag-toggle.is-active { animation: none !important; }
}

/* When flag-mode is on, change board cursor + add subtle white tint hint */
.mgm-board.is-flag-mode {
    cursor: crosshair;
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.28);
}
.mgm-board.is-flag-mode .mgm-cell:not(:disabled):not(.is-revealed):hover {
    background: rgba(255, 255, 255, 0.10);
}

/* -------- Action bar below the board -------- */
.mgm-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
    flex-wrap: wrap;
}
.mgm-help {
    font-size: 12px;
    color: var(--mg-ink-subtle);
    line-height: 1.4;
    flex-basis: 100%;
    margin-top: 4px;
}

/* -------- Mobile -------- */
@media (max-width: 560px) {
    /* Bigger cells on mobile — easier to tap accurately. The previous
       18-22px cells were below the recommended 44px tap-target minimum
       and made the game frustrating. We bias toward bigger cells and
       let the stage scroll horizontally for hard mode. */
    .mgm-board                            { --mgm-cell: 36px; gap: 3px; padding: 8px; }
    .mgm-board[data-difficulty="medium"]  { --mgm-cell: 30px; }
    .mgm-board[data-difficulty="hard"]    { --mgm-cell: 26px; }
    .mgm-cell                             { font-size: 16px; font-weight: 700; }
    .mgm-cell.is-mine::before             { font-size: 16px; }
    /* SVG flag scales automatically via mask-size: 60% — no width
       override needed. The flag stays proportional to whatever
       mobile cell size the difficulty resolves to. */
    .mgm-cell.is-bad-flag::after          { font-size: 16px; }
    .mgm-tab                              { padding: 7px 12px; font-size: 12px; }
    /* Stack action buttons full-width on narrow screens for easier tapping */
    .mgm-actions .mg-btn                  { flex: 1 1 calc(50% - 5px); justify-content: center; }
}
