/* ==========================================================================
   Theme tokens
   Every color is a CSS custom property. light-dark() picks the light or dark
   value from the element's color-scheme:
   - :root uses "light dark", so the loading screen follows the system theme;
   - .app-root[data-theme] (rendered by MainLayout.razor) pins light, dark,
     or system. No JavaScript is involved in theming.
   ========================================================================== */
:root {
    color-scheme: light dark;

    --bg:            light-dark(#f6f3ee, #121318);
    --surface:       light-dark(#ffffff, #1b1d24);
    --surface-2:     light-dark(#efe9e1, #232631);
    --border:        light-dark(#e3dbd0, #2c303b);
    --text:          light-dark(#3b3631, #eceef3);
    --text-muted:    light-dark(#6f675e, #a3a8b5);
    --accent:        #6d4aff;
    --accent-text:   #ffffff;
    --accent-soft:   light-dark(#ece7ff, #2a2450);
    --link:          light-dark(#5a36f0, #b3a3ff);
    --board-bg:      light-dark(#cdc1b4, #262a33);
    --cell-bg:       light-dark(rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.22));
    --score-bg:      light-dark(#3b3631, #262a33);
    --score-label:   light-dark(#d9cfc4, #a3a8b5);
    --score-value:   #ffffff;
    --overlay-lose:  light-dark(rgba(246, 243, 238, 0.78), rgba(18, 19, 24, 0.8));
    --overlay-win:   light-dark(rgba(109, 74, 255, 0.55), rgba(109, 74, 255, 0.45));
    --shadow:        light-dark(0 10px 30px rgba(59, 54, 49, 0.12), 0 10px 30px rgba(0, 0, 0, 0.45));
    --code-bg:       light-dark(#f3efe9, #191b22);
    --error:         light-dark(#b42318, #ff8a80);

    /* Tiles: text/background pairs are at least 4.5:1 contrast in both themes. */
    --tile-2-bg:     light-dark(#eee6db, #3e4556);  --tile-2-fg:    light-dark(#4a4038, #e7e9ee);
    --tile-4-bg:     light-dark(#ecdcc0, #505971);  --tile-4-fg:    light-dark(#4a4038, #f2f4f8);
    --tile-8-bg:     #ffad66;                       --tile-8-fg:    #3a1a00;
    --tile-16-bg:    #ff8a4c;                       --tile-16-fg:   #331200;
    --tile-32-bg:    #f2603d;                       --tile-32-fg:   #2a0a00;
    --tile-64-bg:    #c9302c;                       --tile-64-fg:   #ffffff;
    --tile-128-bg:   #f7d154;                       --tile-128-fg:  #3a2c00;
    --tile-256-bg:   #f4c03a;                       --tile-256-fg:  #3a2a00;
    --tile-512-bg:   #eeab22;                       --tile-512-fg:  #2f1f00;
    --tile-1024-bg:  #7b57f5;                       --tile-1024-fg: #ffffff;
    --tile-2048-bg:  #6a3df0;                       --tile-2048-fg: #ffffff;
    --tile-super-bg: linear-gradient(135deg, #d63384, #7b3fe4); --tile-super-fg: #ffffff;

    /* Motion */
    --slide: 110ms;                               /* tile slide, like the original 2048's ~100ms */
    --spawn: 150ms;                               /* new tile scale-in, after the slide */
    --pop: 180ms;                                 /* merged tile pop, after the slide */
    --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);   /* snappy: most of the distance in the first frames */

    --radius: 10px;
    /* Board fits the screen: limited by width, height left after header + footer, and a desktop cap
       that grows a little with the board size (480px at 4x4, 600px at 10x10, 720px at 16x16).
       --n (tiles per side) is set on .game by GameBoard.razor, so the header matches the board. */
    --n: 4;
    --board: min(92vw, calc(100dvh - 280px - env(safe-area-inset-top) - env(safe-area-inset-bottom)), calc(400px + var(--n) * 20px));
    --font: "Inter", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
    --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
}

.app-root[data-theme="light"] { color-scheme: light; }
.app-root[data-theme="dark"]  { color-scheme: dark; }
.app-root[data-theme="system"] { color-scheme: light dark; }

/* ========================================================================== Base */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font);
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-text-size-adjust: 100%;
}

body { position: fixed; inset: 0; }

#app { height: 100%; }

.app-root {
    height: 100%;
    display: flex;
    flex-direction: column;
    background: var(--bg);
    color: var(--text);
    transition: background-color 200ms ease, color 200ms ease;
}

.app-main { flex: 1; min-height: 0; display: flex; flex-direction: column; }

a { color: var(--link); }

/* ========================================================================== Game layout */
.game {
    /* Re-evaluated here because --n is set on this element (custom properties resolve where defined). */
    --board: min(92vw, calc(100dvh - 280px - env(safe-area-inset-top) - env(safe-area-inset-bottom)), calc(400px + var(--n) * 20px));
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: calc(env(safe-area-inset-top) + 12px) 12px 8px;
    touch-action: none; /* swipes are game moves, not scrolls */
}

.top, .sub { width: var(--board); display: flex; align-items: center; justify-content: space-between; gap: 8px; }

.title {
    margin: 0;
    /* The title is the target (2048 … 131072 and beyond): longer numbers shrink to keep room for the scores. */
    font-size: max(24px, calc(clamp(44px, 14vw, 72px) * 4 / max(4, var(--digits, 4))));
    white-space: nowrap;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--text);
}

.title-pending { visibility: hidden; } /* until the saved size is applied: no 2048 flash */
.title-num { display: inline-block; }
/* A size change flips the new name in, like a tile turning over. */
.title-flip { animation: title-flip 360ms cubic-bezier(.2, .8, .3, 1.2) both; }
@keyframes title-flip {
    from { transform: perspective(300px) rotateX(-90deg); opacity: 0; }
    to { transform: none; opacity: 1; }
}

.scores { display: flex; gap: 6px; }

.score-box {
    background: var(--score-bg);
    border-radius: 8px;
    padding: 6px 12px;
    min-width: 64px;
    text-align: center;
    display: flex;
    flex-direction: column;
}
.score-box .label { font-size: 11px; font-weight: 700; text-transform: uppercase; color: var(--score-label); letter-spacing: .06em; }
.score-box .value { font-size: 20px; font-weight: 800; color: var(--score-value); font-variant-numeric: tabular-nums; }

/* The hint gets its own line when the row leaves it less than 160px (phones, and long names like
   "get to 131072!"), instead of wrapping into a narrow column next to the buttons. */
.sub { flex-wrap: wrap; row-gap: 6px; }
.hint { margin: 0; font-size: 14px; color: var(--text-muted); flex: 1 1 160px; }
.hint strong { color: var(--text); }

.actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; margin-left: auto; }

/* ========================================================================== Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--accent);
    color: var(--accent-text);
    border: none;
    border-radius: 8px;
    padding: 10px 16px;
    font-size: 16px;
    font-weight: 700;
    font-family: inherit;
    text-decoration: none;
    cursor: pointer;
    touch-action: manipulation;
    white-space: nowrap;
    transition: transform 120ms ease, filter 120ms ease;
}
.btn:hover { filter: brightness(1.08); }
.btn:active { transform: scale(0.97); }
.btn-sm { padding: 6px 12px; font-size: 14px; }
.btn .icon { width: 16px; height: 16px; }

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    touch-action: manipulation;
    transition: background-color 150ms ease, transform 120ms ease;
}
.icon-btn:hover { background: var(--surface-2); }
.icon-btn:active { transform: scale(0.95); }

/* Split New Game button: main part + caret that opens the board-size menu. */
.split-btn { position: relative; display: inline-flex; }
.split-btn .new-game { border-radius: 8px 0 0 8px; padding-right: 12px; }
.split-btn .new-game-caret { border-radius: 0 8px 8px 0; padding: 10px 8px; border-left: 1px solid rgba(255, 255, 255, 0.35); }
.split-btn .new-game-caret .icon { width: 18px; height: 18px; transition: transform 150ms ease; }
.split-btn .new-game-caret[aria-expanded="true"] .icon { transform: rotate(180deg); }
.size-badge { font-size: 12px; font-weight: 700; padding: 1px 6px; border-radius: 999px; background: rgba(255, 255, 255, 0.22); font-variant-numeric: tabular-nums; }

.menu-backdrop, .dialog-backdrop { position: fixed; inset: 0; z-index: 30; }
.dialog-backdrop { background: rgba(0, 0, 0, 0.35); }

.size-menu {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 31;
    list-style: none; margin: 0; padding: 6px; min-width: 170px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow);
}
.size-option {
    display: flex; align-items: center; gap: 8px; width: 100%;
    padding: 8px 10px; border: 0; border-radius: 6px; background: none; color: inherit;
    font: inherit; font-size: 15px; font-weight: 600; text-align: left; cursor: pointer;
    font-variant-numeric: tabular-nums;
}
.size-option:hover, .size-option:focus-visible { background: var(--surface-2); outline: none; }
.size-option:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
.size-option.checked { color: var(--link); }
.size-option .check { width: 18px; height: 18px; display: inline-flex; }
.size-option .check .icon { width: 18px; height: 18px; }
.size-note { margin-left: auto; font-size: 12px; font-weight: 600; color: var(--text-muted); }

.size-dialog {
    position: fixed; z-index: 31; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: min(360px, calc(100vw - 32px)); padding: 20px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow);
    -webkit-user-select: text; user-select: text;
}
.size-dialog h2 { margin: 0 0 8px; font-size: 20px; }
.size-dialog label { display: block; font-size: 14px; color: var(--text-muted); margin-bottom: 10px; }
.custom-row { display: flex; align-items: center; gap: 10px; }
.custom-row input {
    width: 96px; padding: 8px 10px; font: inherit; font-size: 20px; font-weight: 700;
    color: var(--text); background: var(--bg); border: 2px solid var(--border); border-radius: 8px;
    font-variant-numeric: tabular-nums;
}
.custom-row input:focus { outline: none; border-color: var(--accent); }
.custom-row input[aria-invalid="true"] { border-color: var(--error); }
.custom-preview { font-size: 20px; font-weight: 700; color: var(--text-muted); }
.field-error { margin: 10px 0 0; font-size: 14px; font-weight: 600; color: var(--error); }
.dialog-buttons { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.btn-secondary { background: var(--surface-2); color: var(--text); }

.btn:focus-visible, .icon-btn:focus-visible, .toc-link:focus-visible, .docs-main a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ========================================================================== Board + tiles */
/* Everything on the board scales with --n (tiles per side): the gap shrinks as the board grows,
   --cell is one tile's width, and tile fonts are a fraction of --cell picked by label length.
   The sizes are registered lengths, so they compute to plain px once on .board and tiles inherit a
   number instead of re-evaluating the whole calc()/min()/env() chain in every tile's width, font and
   transform. On a 16x16 board under 4x CPU throttling that halved style recalculation per move. */
@property --board { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --gap { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --cell { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --step { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --tile-radius { syntax: '<length>'; inherits: true; initial-value: 0px; }
.board {
    --gap: clamp(2px, calc(var(--board) * 0.13 / (var(--n) + 1)), 14px);
    --cell: calc((var(--board) - (var(--n) + 1) * var(--gap)) / var(--n));
    --tile-radius: clamp(3px, calc(var(--cell) * 0.07), 6px);
    --step: calc(var(--cell) + var(--gap)); /* one cell over: tile positions are multiples of it */
    position: relative;
    width: var(--board);
    height: var(--board);
    background: var(--board-bg);
    border-radius: var(--radius);
    padding: var(--gap);
    display: grid;
    grid-template-columns: repeat(var(--n), 1fr);
    grid-template-rows: repeat(var(--n), 1fr);
    gap: var(--gap);
    touch-action: none;
    box-shadow: var(--shadow);
}
.board-pending .tile-layer, .board-pending .cell { visibility: hidden; } /* until the saved size is applied */

.cell { background: var(--cell-bg); border-radius: var(--tile-radius); }

/* Tiles sit in a layer over the grid. Each tile's position is an inline transform,
   translate(calc(var(--step) * col), calc(var(--step) * row)), set by GameBoard.razor from a per-size
   cache, so a move only changes transform: the GPU slides it, no layout work. Setting transform
   directly (not per-tile custom properties) matters on big boards: transform is not inherited, so the
   tile's children need no style recalc, and no tile gets its own copy of the custom property map.
   (CSS containment and will-change: transform were measured on 16x16 too and did not help.) */
.tile-layer { position: absolute; inset: var(--gap); pointer-events: none; }

.tile {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--cell);
    height: var(--cell);
    transition: transform var(--slide) var(--ease-out);
    z-index: 1;
    font-size: calc(var(--cell) * 0.5);
}
/* Font by label length (GameBoard adds tl-N): the label fills about 85% of the tile at most. */
.tl-3 { font-size: calc(var(--cell) * 0.44); }
.tl-4 { font-size: calc(var(--cell) * 0.35); }
.tl-5 { font-size: calc(var(--cell) * 0.29); }
.tl-6 { font-size: calc(var(--cell) * 0.245); }
.tl-7 { font-size: calc(var(--cell) * 0.21); }
.tl-8, .tl-9, .tl-10 { font-size: calc(var(--cell) * 0.17); }
.tile-retired { z-index: 0; }   /* merged-away halves slide in underneath the new tile */
.tile-merged  { z-index: 2; }

.tile-inner {
    width: 100%;
    height: 100%;
    border-radius: var(--tile-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.03em;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    background: var(--tile-bg);
    color: var(--tile-fg);
}

/* Spawn and merge wait for the slide (animation-delay), then scale in / pop.
   "backwards" holds the first keyframe (opacity 0) during the delay, so a merged tile stays hidden
   while both of its sources slide in underneath it, and a new tile only appears once the board has
   settled. The class that starts each animation is fixed for the tile's life (GameBoard.ClassFor),
   so a fast next move retargets the slide but never cancels or restarts a scale animation. */
.tile-new .tile-inner    { animation: tile-spawn var(--spawn) var(--ease-out) var(--slide) backwards; }
.tile-merged .tile-inner { animation: tile-pop var(--pop) ease-out var(--slide) backwards; }

@keyframes tile-spawn { from { transform: scale(0.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes tile-pop {
    0%   { transform: scale(0.5);  opacity: 0; }
    45%  { transform: scale(1.12); opacity: 1; }
    100% { transform: scale(1);    opacity: 1; }
}

.tile-2     { --tile-bg: var(--tile-2-bg);     --tile-fg: var(--tile-2-fg); }
.tile-4     { --tile-bg: var(--tile-4-bg);     --tile-fg: var(--tile-4-fg); }
.tile-8     { --tile-bg: var(--tile-8-bg);     --tile-fg: var(--tile-8-fg); }
.tile-16    { --tile-bg: var(--tile-16-bg);    --tile-fg: var(--tile-16-fg); }
.tile-32    { --tile-bg: var(--tile-32-bg);    --tile-fg: var(--tile-32-fg); }
.tile-64    { --tile-bg: var(--tile-64-bg);    --tile-fg: var(--tile-64-fg); }
.tile-128   { --tile-bg: var(--tile-128-bg);   --tile-fg: var(--tile-128-fg); }
.tile-256   { --tile-bg: var(--tile-256-bg);   --tile-fg: var(--tile-256-fg); }
.tile-512   { --tile-bg: var(--tile-512-bg);   --tile-fg: var(--tile-512-fg); }
.tile-1024  { --tile-bg: var(--tile-1024-bg);  --tile-fg: var(--tile-1024-fg); }
.tile-2048  { --tile-bg: var(--tile-2048-bg);  --tile-fg: var(--tile-2048-fg); }
.tile-super { --tile-bg: var(--tile-super-bg); --tile-fg: var(--tile-super-fg); }

.tile-1024 .tile-inner { box-shadow: 0 0 18px 2px rgba(123, 87, 245, 0.35); }
.tile-2048 .tile-inner { box-shadow: 0 0 24px 4px rgba(106, 61, 240, 0.5); }
.tile-super .tile-inner { box-shadow: 0 0 24px 4px rgba(214, 51, 132, 0.4); }

.overlay {
    position: absolute;
    inset: 0;
    border-radius: var(--radius);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    z-index: 10;
    animation: fade 300ms ease-in;
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}
.overlay p { margin: 0; font-size: clamp(36px, 11vw, 56px); font-weight: 800; }
.overlay.lose { background: var(--overlay-lose); }
.overlay.lose p { color: var(--text); }
.overlay.win { background: var(--overlay-win); }
.overlay.win p { color: #ffffff; text-shadow: 0 2px 12px rgba(0, 0, 0, 0.25); }
.overlay-buttons { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* The secret 1x1 board: the overlay waits a beat, so the lone 256 tile can pop in first
   (reduced motion: no animation at all, see the media query below). */
.overlay.instant-win { gap: 10px; padding: 16px; text-align: center; animation: fade 300ms ease-in 450ms backwards; }
.overlay.instant-win p { font-size: clamp(26px, 7vw, 40px); line-height: 1.15; }
.overlay.instant-win .overlay-sub { font-size: 16px; font-weight: 600; opacity: 0.92; margin-bottom: 6px; }
/* 1x1: the winning 256 stays crisp in the upper part of its (board-sized) tile and the message sits
   in a band below it, instead of a blurred giant "256" behind "You made 256!". */
.board[data-size='1'] .tile { font-size: calc(var(--cell) * 0.3); }
.board[data-size='1'] .tile-inner { padding-bottom: 38%; box-sizing: border-box; }
.overlay.instant-win[data-size='1'] {
    justify-content: flex-end;
    padding-bottom: 22px;
    background: linear-gradient(to top, var(--overlay-win) 40%, transparent 72%);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.help { margin: 0; font-size: 13px; color: var(--text-muted); }

.game:focus, .game:focus-visible { outline: none; }

@media (max-height: 560px) {
    .help { display: none; }
    .hint { display: none; }
}

/* ========================================================================== Footer */
.app-footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: baseline;
    gap: 2px 6px;
    padding: 4px 12px calc(env(safe-area-inset-bottom) + 6px);
    font-size: 11px;
    line-height: 1.4;
    color: var(--text-muted);
    -webkit-user-select: text;
    user-select: text;
}
.app-footer .footer-author { font-weight: 600; color: var(--text); }
.app-footer .footer-sha { font-family: var(--mono); color: var(--link); text-decoration: none; }
.app-footer .footer-sha:hover { text-decoration: underline; }
.footer-sep { opacity: 0.6; }

/* ========================================================================== Loading screen (before Blazor starts) */
.loading { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; }
.loading-tile {
    width: 120px; height: 120px; border-radius: 16px;
    background: var(--tile-2048-bg); color: var(--tile-2048-fg);
    display: flex; align-items: center; justify-content: center;
    font-size: 40px; font-weight: 800;
    box-shadow: 0 0 30px 6px rgba(106, 61, 240, 0.35);
    animation: pulse 1.2s ease-in-out infinite;
}
.loading-text { font-size: 16px; color: var(--text-muted); }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }

#blazor-error-ui {
    background: lightyellow;
    color: #222;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }

/* ========================================================================== Docs (DocFX-style) */
.docs { flex: 1; min-height: 0; display: flex; flex-direction: column; padding-top: env(safe-area-inset-top); }

.docs-topbar {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 56px;
    padding: 0 16px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}
.docs-menu-btn { display: none; }
.docs-brand { display: inline-flex; align-items: center; gap: 10px; font-weight: 800; font-size: 18px; color: var(--text); text-decoration: none; }
.brand-tile {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 8px;
    background: var(--tile-2048-bg); color: var(--tile-2048-fg);
    font-size: 11px; font-weight: 800;
}
.docs-topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.docs-body {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 260px minmax(0, 1fr) 220px;
    position: relative;
}

.docs-sidebar, .docs-outline { overflow-y: auto; overscroll-behavior: contain; padding: 18px 14px; }
.docs-sidebar { border-right: 1px solid var(--border); background: var(--surface); }
.docs-outline { border-left: 1px solid var(--border); font-size: 13px; }

.docs-filter {
    display: flex; align-items: center; gap: 8px;
    padding: 6px 10px; margin-bottom: 16px;
    border: 1px solid var(--border); border-radius: 8px;
    background: var(--bg); color: var(--text-muted);
}
.docs-filter .icon { width: 16px; height: 16px; flex-shrink: 0; }
.docs-filter input {
    flex: 1; min-width: 0; border: none; outline: none; background: transparent;
    color: var(--text); font: inherit; font-size: 14px;
    -webkit-user-select: text; user-select: text;
}

.toc-section { margin-bottom: 18px; }
.toc-title { margin: 0 0 6px; padding: 0 8px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); }
.docs ul { list-style: none; margin: 0; padding: 0; }
.toc-link {
    display: block; padding: 6px 8px; border-radius: 6px;
    color: var(--text); text-decoration: none; font-size: 14px;
}
.toc-link:hover { background: var(--surface-2); }
.toc-link.active { background: var(--accent-soft); color: var(--link); font-weight: 700; }
/* Items nested under another page in docs/toc.yml (e.g. a spec's plan and tasks). */
.toc-child .toc-link { padding: 3px 8px 3px 22px; font-size: 13px; color: var(--text-muted); }
.toc-child .toc-link.active { color: var(--link); }

.docs-outline a { display: block; padding: 4px 8px; color: var(--text-muted); text-decoration: none; border-left: 2px solid transparent; }
.docs-outline a:hover { color: var(--link); border-left-color: var(--accent); }
.docs-outline .outline-level-3 a { padding-left: 20px; }

.docs-main {
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 28px clamp(16px, 4vw, 48px) 48px;
    -webkit-user-select: text;
    user-select: text;
    outline: none;
}
.docs-breadcrumb { font-size: 13px; color: var(--text-muted); margin-bottom: 8px; }
.docs-message { padding: 40px 0; color: var(--text-muted); }

.docs-pager { display: flex; justify-content: space-between; gap: 12px; margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--border); }
.pager-link {
    display: flex; flex-direction: column; gap: 2px;
    padding: 10px 14px; border: 1px solid var(--border); border-radius: 8px;
    text-decoration: none; color: var(--text); min-width: 40%;
}
.pager-link.next { text-align: right; margin-left: auto; }
.pager-link:hover { border-color: var(--accent); }
.pager-label { font-size: 12px; color: var(--text-muted); }
.pager-title { font-weight: 700; color: var(--link); }
.docs-source { display: inline-flex; align-items: center; gap: 6px; margin-top: 18px; font-size: 13px; color: var(--text-muted); }
.docs-source .icon { width: 16px; height: 16px; }

/* Markdown content (HTML generated at build time by tools/DocsBuilder) */
.markdown-body { max-width: 820px; font-size: 16px; line-height: 1.65; }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4 { line-height: 1.25; scroll-margin-top: 16px; }
.markdown-body h1 { font-size: 2em; margin: 0 0 0.6em; letter-spacing: -0.02em; }
.markdown-body h2 { font-size: 1.45em; margin: 1.8em 0 0.6em; padding-bottom: 0.3em; border-bottom: 1px solid var(--border); }
.markdown-body h3 { font-size: 1.15em; margin: 1.5em 0 0.5em; }
.markdown-body p, .markdown-body ul, .markdown-body ol, .markdown-body table, .markdown-body blockquote, .markdown-body pre { margin: 0 0 1em; }
.markdown-body ul, .markdown-body ol { padding-left: 1.6em; }
.markdown-body ul { list-style: disc; }
.markdown-body ol { list-style: decimal; }
.markdown-body li + li { margin-top: 0.25em; }
.markdown-body code { font-family: var(--mono); font-size: 0.88em; background: var(--code-bg); padding: 0.15em 0.4em; border-radius: 5px; }
.markdown-body pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; overflow-x: auto; line-height: 1.5; }
.markdown-body pre code { background: none; padding: 0; font-size: 0.85em; }
.markdown-body blockquote { margin-left: 0; padding: 0.4em 1em; border-left: 4px solid var(--accent); background: var(--accent-soft); border-radius: 0 8px 8px 0; }
.markdown-body table { border-collapse: collapse; display: block; overflow-x: auto; }
.markdown-body th, .markdown-body td { border: 1px solid var(--border); padding: 6px 12px; text-align: left; vertical-align: top; }
.markdown-body th { background: var(--surface-2); }
.markdown-body img { max-width: 100%; }
.markdown-body hr { border: none; border-top: 1px solid var(--border); margin: 2em 0; }

/* Mermaid diagrams: pre-rendered SVGs in a light and a dark variant; show the one for the theme. */
.markdown-body .diagram {
    margin: 0 0 1.2em;
    padding: 16px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    overflow-x: auto;
    text-align: center;
}
.markdown-body .diagram img { max-width: 100%; height: auto; }
.diagram-dark { display: none; }
.app-root[data-theme="dark"] .diagram-light { display: none; }
.app-root[data-theme="dark"] .diagram-dark { display: inline; }
@media (prefers-color-scheme: dark) {
    .app-root[data-theme="system"] .diagram-light { display: none; }
    .app-root[data-theme="system"] .diagram-dark { display: inline; }
}
.markdown-body .diagram-missing { text-align: left; }

@media (max-width: 1100px) {
    .docs-body { grid-template-columns: 240px minmax(0, 1fr); }
    .docs-outline { display: none; }
}

@media (max-width: 800px) {
    .docs-menu-btn { display: inline-flex; }
    .docs-body { grid-template-columns: minmax(0, 1fr); }
    .docs-sidebar {
        position: absolute;
        inset: 0 auto 0 0;
        width: min(300px, 85vw);
        z-index: 20;
        transform: translateX(-100%);
        transition: transform 200ms var(--ease-out);
        box-shadow: var(--shadow);
    }
    .docs.menu-open .docs-sidebar { transform: none; }
    .docs-main { padding-top: 20px; }
}

/* ========================================================================== Reduced motion */
@media (prefers-reduced-motion: reduce) {
    .tile { transition: none; }
    .tile-inner, .overlay, .loading-tile, .title-flip { animation: none !important; }
    .app-root, .docs-sidebar, .btn, .icon-btn, .new-game-caret .icon { transition: none; }
}
