/* §9 breakpoints and §10's single 48px control floor (L3) -- never 44px.
   The full-viewport-height unit that ignores mobile browser chrome is never
   used here; the dynamic/small variants are used instead. */

:root {
    --control-floor: 48px;
    --board-raw: min(70dvh, 640px);
    --board-width: var(--board-raw);
}

/* Nine 1fr columns out of an arbitrary width give cells a fractional size --
   at 360px the board is 344px and cells come out 37.875 and 37.891 wide, two
   different values in the same row. Each cell paints its own 1px right border,
   so those borders land on fractional device pixels and the browser snaps each
   one independently: some render, some vanish. The result is a grid whose
   inner lines look broken, worst on phones where the device pixel ratio is 3.
   Sizing the board so the nine cells divide it evenly makes every cell the
   same whole number of pixels, so every border snaps the same way.
   The 3px is the grid's own border-left, which box-sizing folds into the
   declared width -- the nine cells share what is left, so it is (W - 3) that
   has to divide by 9, not W. Rounding W itself would leave every cell a third
   of a pixel short, which is how this was first written and why it did not
   help.
   Guarded by @supports because a custom property holding an unparseable value
   is only invalid at computed-value time -- .board's inline-size would fall
   back to auto and the board would collapse, rather than merely stay
   unrounded. Each breakpoint below sets --board-raw; this derives from it. */
@supports (width: round(down, 10px, 3px)) {
    :root {
        --board-width: calc(round(down, var(--board-raw) - 3px, 9px) + 3px);
    }
}

* {
    box-sizing: border-box;
}

/* The page column. body owns the full viewport height and the shell takes
   whatever is left over, so the footer lands directly under the shell instead
   of one whole viewport below it -- which is where a shell with its own
   min-height: 100dvh had been putting it. */
body {
    margin: 0;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    background: var(--bg-page);
    color: var(--fg-body);
}

.control {
    min-inline-size: var(--control-floor);
    min-block-size: var(--control-floor);
}

.board {
    inline-size: var(--board-width);
    block-size: var(--board-width);
    aspect-ratio: 1;
}

/* 18 columns rather than 9 so both row shapes divide the width exactly:
   nine digits at span 2, and the two word controls at span 9. On a
   9-column track the control row could only reach 6 columns, leaving a
   ragged third of the bar empty. Widths come from the columns, not from a
   per-button calc, so a span change cannot fall out of step with them. */
.digit-bar {
    display: grid;
    grid-template-columns: repeat(18, 1fr);
    block-size: 60px; /* L2: row 60 = button 48 + 6px padding on each side */
    gap: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

.digit-bar button {
    grid-column: span 2;
    block-size: 48px;
}

.app-shell {
    display: flex;
    flex-direction: column;
    /* Was min-height: 100dvh. Same rendering when the page is short -- the
       shell still fills the viewport -- but as a flex item it stops *at* the
       viewport instead of claiming it and pushing the footer out of sight. */
    flex: 1;
}

/* wide: board can shrink but never overlays. A 2x2 grid -- not a 3-item
   flex row -- because the digit bar (controls-area) is only ever visible
   at this width when touchControls is forced to "show" or a coarse
   pointer is detected on a large screen; a flex row sized for header+board
   alone overflowed once controls-area actually had width, pushing the
   header off-screen (its only content-independent constraint was a *max*
   width, so flex-shrink had nowhere else to take space from). Putting the
   digit bar directly under the board, in its own grid row, means the
   layout only ever has two things competing for horizontal space (header,
   board column), regardless of whether the digit bar is showing.
   The header host is a flex item containing a wrapping button row, so its
   shrink-to-fit width defaults to the row's *unwrapped* max-content size --
   capping it forces the buttons to wrap into a tidy block instead of
   claiming far more horizontal space than they visually use. */
@media (min-width: 1024px) {
    :root {
        --board-raw: min(70dvh, 640px);
    }

    .app-shell {
        display: grid;
        grid-template-columns: minmax(0, 360px) auto;
        grid-template-areas:
            "header board"
            "header controls";
        justify-content: center;
        align-content: start;
        align-items: start;
        column-gap: 32px;
        row-gap: 12px;
        padding-block-start: 24px;
    }

    .app-shell-header-host {
        grid-area: header;
        max-inline-size: 360px;
    }

    .board-area {
        grid-area: board;
    }

    .controls-area {
        grid-area: controls;
    }
}

/* medium: portrait stacks the board over controls; landscape puts them side
   by side, same header-wrapping/grid treatment as the wide breakpoint but
   sized down for the narrower viewport range. */
@media (min-width: 600px) and (max-width: 1023px) {
    :root {
        --board-raw: min(80dvh, 90vw);
    }

    @media (orientation: landscape) {
        :root {
            --board-raw: min(70dvh, 60vw);
        }

        .app-shell {
            display: grid;
            grid-template-columns: minmax(0, 320px) auto;
            grid-template-areas:
                "header board"
                "header controls";
            justify-content: center;
            align-content: start;
            align-items: start;
            column-gap: 24px;
            row-gap: 8px;
            padding-block-start: 16px;
        }

        .app-shell-header-host {
            grid-area: header;
            max-inline-size: 320px;
        }

        .board-area {
            grid-area: board;
        }

        .controls-area {
            grid-area: controls;
        }
    }
}

/* narrow: board fills the viewport width minus a small margin; digit targets
   are exactly board-width/9 with no inter-button gap. */
@media (max-width: 599px) {
    :root {
        --board-raw: calc(100vw - 16px);
    }

    .digit-bar {
        gap: 0;
    }
}
