@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800;900&display=swap');

/*
 * buildstarted.games — Design System
 * ====================================
 * Single source of truth for color, typography, layout, and components.
 * Every game imports this file and then adds only game-specific overrides.
 *
 * Table of contents
 * -----------------
 * 1.  Tokens (CSS variables)
 * 2.  Reset
 * 3.  Base layout  (body → header → board-wrap → controls)
 * 4.  Header
 * 5.  HUD
 * 6.  Board wrap
 * 7.  Controls bar
 * 8.  Game buttons
 * 9.  Dialog / new-game modal
 * 10. Result panel (slide-up)
 * 11. Stat cards
 * 12. Primary & secondary buttons
 * 13. Animations & keyframes
 * 14. Utility
 */

/* ─────────────────────────────────────────────────────────────────────────────
   1. Tokens
   ───────────────────────────────────────────────────────────────────────────── */

:root {
    /* Brand blues — used for header, controls bar, primary buttons, active cells */
    --color-blue:      #3b88c3;
    --color-blue-dark: #3272a3;

    /* Semantic state colors */
    --color-win:       #28a745;   /* success / win */
    --color-win-dark:  #1e7035;
    --color-loss:      #d94040;   /* error / mine hit / loss */
    --color-loss-dark: #a82020;
    --color-warn:      #ffc107;   /* caution / hint */

    /* Surface & background */
    --color-bg:        #f0f0f0;   /* light surface wash (pads, chips) — NOT the page */
    --color-surface:   #ffffff;   /* card / panel / dialog surface */

    /* Page backdrop — deep blue gradient every game floats on */
    --color-deep-1:    #2c5f8f;
    --color-deep-2:    #1c4066;
    --color-deep-3:    #122b45;
    --bg-page: radial-gradient(120% 90% at 50% 0%, var(--color-deep-1) 0%, var(--color-deep-2) 48%, var(--color-deep-3) 100%);

    /* Light text for anything sitting directly on the backdrop */
    --color-text-on-deep:      #e9f1f7;
    --color-text-on-deep-soft: rgba(233,241,247,.65);

    /* Text */
    --color-text:      #1c2d3a;   /* primary text */
    --color-text-soft: #888888;   /* secondary / label text */

    /* Borders */
    --color-border:    #e0e0e0;
    --color-border-mid:#cccccc;

    /* Elevation — box-shadow presets */
    --shadow-sm:   0 2px 5px rgba(0,0,0,.18);
    --shadow-md:   0 4px 14px rgba(0,0,0,.18);
    --shadow-card: 0 4px 14px rgba(50,114,163,.40), inset 0 1px 0 rgba(255,255,255,.15);
    --shadow-up:   0 -4px 24px rgba(0,0,0,.18);   /* slide-up panel */

    /* Typography — emoji/symbol fonts stay in the stack so glyphs like
       🚩 ⚑ ⏱ ⚓ ✕ never dead-end in the webfont */
    --font: 'Nunito', 'Segoe UI', system-ui, -apple-system,
            'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', sans-serif;

    /* For icon-only elements: symbol fonts first so pictographs win */
    --font-symbols: 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji', var(--font);

    /* Radii */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;
}


/* ─────────────────────────────────────────────────────────────────────────────
   2. Reset
   ───────────────────────────────────────────────────────────────────────────── */

*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}


/* ─────────────────────────────────────────────────────────────────────────────
   3. Base layout
   ─────────────────────────────────────────────────────────────────────────────
   Every game uses a full-viewport flex column:

     <body>
       <header>…</header>
       <main id="board-wrap">…</main>   ← flex: 1, scrollable
       <div id="controls">…</div>
       <aside>…</aside>                  ← result panel, fixed bottom
       <dialog id="newdialog">…</dialog>
     </body>
   ───────────────────────────────────────────────────────────────────────────── */

body {
    background: var(--color-deep-3);
    background-image: var(--bg-page);
    display: flex;
    flex-direction: column;
    font-family: var(--font);
    height: 100dvh;
    overflow: hidden;
    user-select: none;
}


/* ─────────────────────────────────────────────────────────────────────────────
   4. Header
   ─────────────────────────────────────────────────────────────────────────────
   Blue bar at the top. Contains game title (left) and HUD (right).

   <header>
     <h1>Game Name <span class="gameinfo">…</span></h1>
     <div class="hud">…</div>
   </header>
   ───────────────────────────────────────────────────────────────────────────── */

header {
    align-items: center;
    background:
        linear-gradient(180deg, rgba(255,255,255,.10) 0%, rgba(255,255,255,0) 60%),
        linear-gradient(160deg, var(--color-blue) 0%, var(--color-blue-dark) 100%);
    border-bottom: 1px solid rgba(255,255,255,.14);
    box-shadow: 0 2px 12px rgba(0,0,0,.28);
    color: #fcfcfc;
    display: grid;
    flex: 0 0 auto;
    grid-template-columns: 1fr auto 1fr;
    height: 2.75em;
    padding: 0 .75em;
    position: relative;
    z-index: 10;
}

header h1 {
    font-size: 1.05em;
    font-weight: 800;
    letter-spacing: .03em;
    text-align: center;
    text-shadow: 0 1px 2px rgba(0,0,0,.25);
    white-space: nowrap;
}

header > :last-child { justify-self: end; }

/* Narrow screens: the fixed 3-column grid squeezes the HUD until pill text
   wraps. Switch to a wrapping flex row — the HUD drops to its own line only
   when it doesn't fit beside the title. */
@media (max-width: 640px) {
    header {
        column-gap: .6em;
        display: flex;
        flex-wrap: wrap;
        height: auto;
        min-height: 2.5em;
        padding: .3em .6em;
        row-gap: .25em;
    }

    header h1 {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .home-link { flex: 0 0 auto; }

    .hud {
        flex: 0 1 auto;
        flex-wrap: wrap;
        font-size: .78em;
        gap: .35em;
        justify-content: center;
        margin-inline: auto;
        row-gap: .3em;
    }
}

.home-link {
    color: #fff;
    font-size: .8em;
    font-weight: 500;
    letter-spacing: .01em;
    text-decoration: none;
    white-space: nowrap;
}
.home-link:hover { opacity: .8; }

.gameinfo {
    font-size: .75em;
    font-weight: 400;
    opacity: .75;
    margin-left: .5em;
}

.gameinfo__item {
    font-weight: 700;
    opacity: 1;
}


/* ─────────────────────────────────────────────────────────────────────────────
   5. HUD  (heads-up display in header — timers, counters)
   ─────────────────────────────────────────────────────────────────────────────
   <div class="hud">
     <span class="hud-item hud-mines">10</span>
     <span class="hud-item hud-timer">0:00</span>
   </div>
   ───────────────────────────────────────────────────────────────────────────── */

.hud {
    display: flex;
    font-size: .85em;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    gap: .45em;
}

.hud-item {
    background: rgba(0,0,0,.18);
    border: 1px solid rgba(255,255,255,.14);
    border-radius: 999px;
    box-shadow: inset 0 1px 2px rgba(0,0,0,.15);
    padding: .22em .8em;
    white-space: nowrap;
}

/* HUD icon prefixes (⚑ ⏱ ✕ ◎ …) need the symbol fonts to render everywhere */
.hud-item::before { font-family: var(--font-symbols); }


/* ─────────────────────────────────────────────────────────────────────────────
   6. Board wrap
   ─────────────────────────────────────────────────────────────────────────────
   Fills all remaining space between header and controls.
   Centers its content both axes; scrolls if content overflows.

   <main id="board-wrap">
     <div id="board">…</div>
   </main>
   ───────────────────────────────────────────────────────────────────────────── */

#board-wrap {
    align-items: center;
    color: var(--color-text-on-deep);
    display: flex;
    flex: 1 1 auto;
    justify-content: center;
    overflow: auto;
    padding: .75em;
}


/* ─────────────────────────────────────────────────────────────────────────────
   7. Controls bar
   ─────────────────────────────────────────────────────────────────────────────
   Blue bar at the bottom. Mirrors the header.
   Respects iOS safe-area-inset-bottom via max().

   <div id="controls">
     <button class="game-button" id="new-btn">New game</button>
     <button class="game-button" id="hint-btn">Hint</button>
   </div>
   ───────────────────────────────────────────────────────────────────────────── */

#controls {
    align-items: center;
    background:
        linear-gradient(0deg, rgba(0,0,0,.10) 0%, rgba(255,255,255,.06) 100%),
        linear-gradient(160deg, var(--color-blue) 0%, var(--color-blue-dark) 100%);
    border-top: 1px solid rgba(255,255,255,.14);
    box-shadow: 0 -2px 12px rgba(0,0,0,.25);
    display: flex;
    flex: 0 0 auto;
    gap: .5em;
    justify-content: center;
    padding: .5em .75em;
    padding-bottom: max(.5em, env(safe-area-inset-bottom));
}


/* ─────────────────────────────────────────────────────────────────────────────
   8. Game buttons  (inside controls bar or header)
   ─────────────────────────────────────────────────────────────────────────────
   <button class="game-button">New game</button>
   <button class="game-button game-button--active">Flag</button>
   ───────────────────────────────────────────────────────────────────────────── */

.game-button {
    background: linear-gradient(180deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.08) 100%);
    border: 1px solid rgba(255,255,255,.22);
    border-radius: var(--radius-md);
    box-shadow: 0 2px 6px rgba(0,0,0,.18);
    color: #fcfcfc;
    cursor: pointer;
    font-family: var(--font);
    font-size: .95em;
    font-weight: 700;
    padding: .5em 1.25em;
    touch-action: manipulation;
    transition: background .12s, transform .12s, box-shadow .12s;
}

.game-button:hover {
    background: linear-gradient(180deg, rgba(255,255,255,.26) 0%, rgba(255,255,255,.14) 100%);
    box-shadow: 0 4px 10px rgba(0,0,0,.22);
    transform: translateY(-1px);
}
.game-button:active {
    background: rgba(0,0,0,.20);
    box-shadow: 0 1px 3px rgba(0,0,0,.20);
    transform: translateY(0);
}

/* Toggled/active state (e.g. flag mode, notes mode) — solid white inversion
   so an engaged mode can't be confused with the glassy hover state */
.game-button--active,
.game-button--active:hover {
    background: #fff;
    border-color: #fff;
    box-shadow: 0 2px 8px rgba(0,0,0,.25);
    color: var(--color-blue-dark);
    transform: none;
}


/* ─────────────────────────────────────────────────────────────────────────────
   9. Dialog  (new-game modal, settings)
   ─────────────────────────────────────────────────────────────────────────────
   Uses the native <dialog> element with showModal() / close().
   Mode cards use :has(input:checked) or .dialog__mode--active for selection.

   <dialog id="newdialog">
     <h2>New game</h2>
     <div class="dialog__modes">
       <button class="dialog__mode dialog__mode--active" data-diff="easy">
         <strong>Easy</strong><span>9 × 9 · 10 mines</span>
       </button>
       …
     </div>
     <button id="cancel">Cancel</button>
   </dialog>

   — OR with radio labels —

   <label class="dialog__mode">
     <input type="radio" name="turn" value="1" checked>
     <span class="dialog__mode-inner"><strong>Draw 1</strong><span>Easy</span></span>
   </label>
   ───────────────────────────────────────────────────────────────────────────── */

dialog {
    border: none;
    border-radius: var(--radius-xl);
    box-shadow: 0 12px 48px rgba(0,0,0,.35);
    margin: 10vh auto 0;
    max-width: 320px;
    padding: 1.75em 1.5em 1.5em;
    width: calc(100% - 2em);
}

dialog[open] { animation: dialog-in .28s cubic-bezier(.34,1.4,.64,1); }

@keyframes dialog-in {
    from { opacity: 0; transform: translateY(14px) scale(.96); }
}

dialog::backdrop {
    backdrop-filter: blur(3px);
    background: rgba(10,25,40,.50);
}

dialog h2 {
    color: var(--color-text);
    font-size: 1.2em;
    font-weight: 700;
    margin-bottom: 1.25em;
    text-align: center;
}

.dialog__modes {
    display: flex;
    flex-direction: column;
    gap: .6em;
    margin-bottom: 1em;
}

/* Works for both <button> and <label> mode cards */
.dialog__mode {
    align-items: center;
    background: #f5f5f5;
    border: 2px solid var(--color-border);
    border-radius: var(--radius-lg);
    cursor: pointer;
    display: flex;
    font-family: var(--font);
    gap: .75em;
    padding: .85em 1.1em;
    text-align: left;
    transition: border-color .1s, background .1s;
    width: 100%;
}

.dialog__mode strong {
    color: var(--color-text);
    display: block;
    font-size: 1.05em;
    font-weight: 700;
}

.dialog__mode span {
    color: var(--color-text-soft);
    font-size: .82em;
}

.dialog__mode:hover,
.dialog__mode:focus-visible {
    background: #eaf2f8;
    border-color: var(--color-blue);
    outline: none;
}

.dialog__mode:active { background: #d8ebf7; }

/* Selected/active mode — also applied via :has(input:checked) for radio variants */
.dialog__mode--active,
.dialog__mode:has(input:checked) {
    background: linear-gradient(160deg, var(--color-blue) 0%, var(--color-blue-dark) 100%);
    border-color: var(--color-blue-dark);
}

.dialog__mode--active strong,
.dialog__mode:has(input:checked) strong { color: #fff; }

.dialog__mode--active span,
.dialog__mode:has(input:checked) span   { color: rgba(255,255,255,.75); }

/* Hide native radio inputs inside mode labels */
.dialog__mode input[type=radio] {
    opacity: 0;
    position: absolute;
    width: 0;
}

/* Dialog action area */
.dialog__controls {
    display: flex;
    flex-direction: column;
    gap: .5em;
    margin-top: .25em;
}

.dialog__controls button[type=submit] {
    background: linear-gradient(160deg, var(--color-blue) 0%, var(--color-blue-dark) 100%);
    border: none;
    border-radius: var(--radius-md);
    color: #fff;
    cursor: pointer;
    font-family: var(--font);
    font-size: 1em;
    font-weight: 700;
    padding: .7em 0;
    width: 100%;
}

/* Generic cancel / secondary link in dialog */
#cancel,
.dialog__cancel {
    background: none;
    border: none;
    color: var(--color-text-soft);
    cursor: pointer;
    display: block;
    font-family: var(--font);
    font-size: .9em;
    padding: .35em 0;
    text-align: center;
    width: 100%;
}

#cancel:hover,
.dialog__cancel:hover { color: #555; }


/* ─────────────────────────────────────────────────────────────────────────────
   10. Result panel  (slide-up from bottom on win / loss)
   ─────────────────────────────────────────────────────────────────────────────
   Controlled via a class on an ancestor or directly on the element.
   Default: translated off-screen below viewport (invisible, not display:none).
   Active:  transform: none — slides up via CSS transition.

   Pattern A — controlled by body ancestor class (solitaire):
     body:has(.board_won) > aside { transform: none }

   Pattern B — controlled by hidden attribute (mines):
     #result[hidden]  → you remove [hidden] to show

   Recommended: Pattern A (CSS-only, no JS for visibility).

   <aside>  OR  <div id="result">
     <p class="result__title result__title--won">You won!</p>
     <div class="stat-cards">…</div>
     <button class="btn-primary">Play again</button>
     <button class="btn-secondary">New game</button>
   </aside>
   ───────────────────────────────────────────────────────────────────────────── */

.result-panel,
aside {
    background: var(--color-surface);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    bottom: 0;
    box-shadow: var(--shadow-up);
    left: 0;
    padding: 1.5em 1.5em max(1.25em, env(safe-area-inset-bottom));
    position: fixed;
    right: 0;
    text-align: center;
    transform: translateY(100%);
    transition: transform .45s cubic-bezier(.22, 1.2, .36, 1);
    z-index: 50;
}

/* Decorative sheet-grabber bar */
.result-panel::before,
aside::before {
    background: var(--color-border);
    border-radius: 999px;
    content: '';
    display: block;
    height: 5px;
    margin: -.5em auto 1em;
    width: 44px;
}

.result-panel--visible,
.result-panel[hidden] { /* note: use JS to remove [hidden] */ }

/* Win title */
.result__title {
    font-size: 1.5em;
    font-weight: 700;
    margin-bottom: .75em;
}

.result__title--won  { color: var(--color-win); }
.result__title--lost { color: var(--color-loss); }
.result__title--draw { color: var(--color-text-soft); }


/* ─────────────────────────────────────────────────────────────────────────────
   11. Stat cards  (time, moves, score shown in the result panel)
   ─────────────────────────────────────────────────────────────────────────────
   <div class="stat-cards">
     <div class="stat-card">
       <span class="stat-card__value">1:42</span>
       <span class="stat-card__label">Time</span>
     </div>
     <div class="stat-card">
       <span class="stat-card__value">47</span>
       <span class="stat-card__label">Moves</span>
     </div>
   </div>
   ───────────────────────────────────────────────────────────────────────────── */

.stat-cards {
    display: flex;
    gap: .75em;
    justify-content: center;
    margin-bottom: 1.25em;
}

.stat-card {
    align-items: center;
    background: linear-gradient(160deg, var(--color-blue) 0%, var(--color-blue-dark) 100%);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    color: #fff;
    display: flex;
    flex: 1;
    flex-direction: column;
    max-width: 9em;
    padding: .9em 1em;
}

.stat-card__value {
    font-size: 2em;
    font-weight: 700;
    letter-spacing: -.5px;
    line-height: 1.1;
}

.stat-card__label {
    font-size: .68em;
    letter-spacing: .08em;
    margin-top: .3em;
    opacity: .75;
    text-transform: uppercase;
}

/* Personal-record variant — green with a "NEW BEST" badge */
.stat-card--record {
    background: linear-gradient(160deg, var(--color-win) 0%, var(--color-win-dark) 100%);
    box-shadow: 0 4px 14px rgba(40,167,69,.40), inset 0 1px 0 rgba(255,255,255,.15);
}

.stat-card__badge {
    background: rgba(255,255,255,.22);
    border-radius: 999px;
    font-size: .58em;
    font-weight: 700;
    letter-spacing: .08em;
    margin-top: .4em;
    padding: .25em .7em;
    text-transform: uppercase;
}

/* Subtitle line under the result title — streaks, records ("Win streak: 4") */
.result__sub {
    color: var(--color-text-soft);
    font-size: .9em;
    margin: -.4em 0 1em;
}


/* ─────────────────────────────────────────────────────────────────────────────
   12. Primary & secondary buttons
   ─────────────────────────────────────────────────────────────────────────────
   Used inside result panels, dialogs, and anywhere outside the controls bar.

   <button class="btn-primary">Play again</button>
   <button class="btn-secondary">New game</button>
   ───────────────────────────────────────────────────────────────────────────── */

.btn-primary {
    background: linear-gradient(160deg, var(--color-blue) 0%, var(--color-blue-dark) 100%);
    border: none;
    border-radius: var(--radius-md);
    box-shadow: 0 3px 10px rgba(50,114,163,.35);
    color: #fff;
    cursor: pointer;
    display: block;
    font-family: var(--font);
    font-size: 1.05em;
    font-weight: 700;
    padding: .75em 0;
    touch-action: manipulation;
    transition: filter .15s;
    width: 100%;
}

.btn-primary:hover {
    box-shadow: 0 5px 16px rgba(50,114,163,.45);
    filter: brightness(1.07);
    transform: translateY(-1px);
}
.btn-primary:active { filter: brightness(.92); transform: translateY(0); }
.btn-primary { transition: filter .15s, transform .12s, box-shadow .12s; }

.btn-secondary {
    background: none;
    border: none;
    color: var(--color-text-soft);
    cursor: pointer;
    display: block;
    font-family: var(--font);
    font-size: .9em;
    margin-top: .5em;
    padding: .35em 0;
    touch-action: manipulation;
    width: 100%;
}

.btn-secondary:hover { color: #555; }


/* ─────────────────────────────────────────────────────────────────────────────
   13. Animations & keyframes
   ─────────────────────────────────────────────────────────────────────────────
   Shared named keyframes. Game-specific ones live in the game's own CSS.
   ───────────────────────────────────────────────────────────────────────────── */

/* Fade in from transparent */
@keyframes fadein {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Zoom in with slight overshoot (win title) */
@keyframes zoomin {
    0%   { opacity: 0; transform: scale(.2); }
    90%  { opacity: 1; transform: scale(1.1); }
    100% {             transform: scale(1); }
}

/* Shake (invalid move) */
@keyframes shake {
    0%, 100% { transform: translateX(0); }
    20%       { transform: translateX(-6px); }
    40%       { transform: translateX(6px); }
    60%       { transform: translateX(-6px); }
    80%       { transform: translateX(6px); }
}

.shake { animation: shake .4s ease; }

/* Pop with overshoot (successful placement, HUD value change) */
@keyframes pop {
    0%   { transform: scale(1); }
    50%  { transform: scale(1.18); }
    100% { transform: scale(1); }
}

.pop { animation: pop .25s ease; }

/* Drop into place (board deal-in — stagger with animation-delay) */
@keyframes deal-in {
    from { opacity: 0; transform: translateY(-14px) scale(.92); }
    to   { opacity: 1; transform: none; }
}

/* Brief green flash (correct placement, completed row/sequence) */
@keyframes flash-win {
    0%   { background-color: var(--color-win); color: #fff; }
    100% { }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}


/* ─────────────────────────────────────────────────────────────────────────────
   14. Utility
   ───────────────────────────────────────────────────────────────────────────── */

/* Visually hidden but accessible */
.sr-only {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

/* Focus ring — applied to interactive elements that lack a visible :focus style */
:focus-visible {
    outline: 2px solid var(--color-blue);
    outline-offset: 2px;
}


/*
 * ═══════════════════════════════════════════════════════════════════════════════
 * DESIGN DECISIONS — read this before building a new game
 * ═══════════════════════════════════════════════════════════════════════════════
 *
 * Layout model
 * ────────────
 * Every game is a full-viewport flex column (height: 100dvh; overflow: hidden).
 * Never use a scrolling page. Game content scrolls inside #board-wrap only if
 * it overflows (e.g. a large minesweeper grid on a small phone).
 *
 * Header & controls bar
 * ─────────────────────
 * Both are --color-blue. Header is at the top; controls at the bottom.
 * Controls use padding-bottom: max(.5em, env(safe-area-inset-bottom)) for iOS.
 * Buttons in the bar use .game-button. Never put large text or icons there.
 *
 * Result / win screen
 * ────────────────────
 * Use the slide-up aside pattern (section 10 above), NOT a centered modal.
 * The panel lives as a direct child of <body> so position:fixed escapes any
 * contain:layout ancestor.
 * Control visibility with a CSS class or :has() on an ancestor — avoid
 * toggling display:none because it fights the cascade.
 *
 * New game / settings
 * ────────────────────
 * Use the native <dialog> element with showModal(). Mode selection uses
 * .dialog__mode cards (section 9). Never re-invent a custom modal overlay.
 *
 * Colors
 * ──────
 * Primary interactive: --color-blue / --color-blue-dark (gradient).
 * Win state:           --color-win  (#28a745, green).
 * Loss / danger:       --color-loss (#d94040, red).
 * Never use gold, orange, or bright purple as primary colors.
 * Status badges and number indicators follow the same win/loss/neutral palette.
 *
 * Typography
 * ──────────
 * Font: var(--font) = Arial, system-ui, "Segoe UI", sans-serif.
 * All sizes in em/rem. No px font sizes except tiny decorative labels.
 *
 * Touch & mobile
 * ──────────────
 * touch-action: manipulation on all interactive elements (prevents 300ms delay).
 * Long-press for secondary actions (flag, etc.) — fire on release, not on hold.
 * Haptic: navigator.vibrate(25) on successful long-press.
 * Minimum tap target: 44px × 44px.
 *
 * Comments in code
 * ────────────────
 * Write no comments unless the WHY is non-obvious.
 * Never describe what the code does; well-named selectors do that already.
 */
