/* ------------------------------------------------------------------ *
 * EdvardsMC - the games
 *
 * One stylesheet for the shell and every game on it. The palette and the
 * paper-and-brushed-metal feel are the tape deck's (music/assets/css/player.css)
 * so that kokamocis.lv reads as one site whichever page you land on. The
 * tokens below are the same names with the same values; change them there and
 * change them here.
 * ------------------------------------------------------------------ */

:root {
    --ink: #2c3138;
    --ink-soft: #6b7178;
    --ink-faint: #969ba1;
    --line: rgba(30, 45, 70, .35);
    --line-soft: rgba(30, 45, 70, .16);
    --card: rgba(255, 255, 255, .72);
    --paper: #f6f2e9;
    --radius: 12px;
    /* The wooden bike is this red, and so is the tape on the deck. One accent. */
    --brand: #6e1010;
    --accent: #b3392c;
    --accent-soft: rgba(179, 57, 44, .14);
    --good: #3a7d44;
    --bad: #b3392c;
    /* a line's count in the dungeon and the camp once it is met, and once it
       is passed: a bluish green on purpose, so the two read apart to eyes
       that do not tell red from green - and they differ in shape as well */
    --clue-met: #1f8f75;
    --clue-over: #b3392c;

    /* ---- feel, tunable live: Elements, pick <html>, this :root rule ----
       --cell-max is the one that matters most: the biggest a cell is allowed to
       be on a wide screen. Boards fit the viewport below that on their own. */
    --cell-max: 44px;
    --cell-min: 24px;         /* below this the board scrolls sideways instead */
    --cell-gap: 1px;
    --board-gutter: 22px;     /* screen edge to board, each side; a phone gets less below */
    /* the drop shadow every board casts. It has to show on all four sides
       to read as one: with the board hard against the screen's edge only the
       part underneath is visible, and that is a grey bar, not a shadow */
    --board-shadow: 0 6px 16px rgba(0, 0, 0, .18);
    --su-line-w: 3px;         /* the lines between the boxes, and the frame round the grid */
    --reveal-step: 18ms;      /* minesweeper: the ripple, per ring of cells */
    --cell-fade: .28s;        /* a cell changing state */
    --board-in: .35s;         /* a new board arriving */
    --press-hold: 420ms;      /* how long a finger rests before a tap means "flag" */
    --double-tap: 380ms;      /* sudoku: two taps on a cell this close together are one double tap */
    --piece-move: 260ms;      /* checkers: one square of a slide */
    /* --share is the width a board may take, as a share of the screen: two
       seas side by side get .5 each. Set per game in its own rules below. */
    --cpu-think: 450ms;       /* checkers: the pause before the machine answers */
    --dialog-in: .22s;
    --dialog-guard: 350ms;    /* a new dialog ignores clicks this long - see dialog() in core.js */
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
[hidden] { display: none !important; }

html {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

body {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    padding: 0 16px;
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
    background:
        radial-gradient(1100px 700px at 50% 34%, #fdfdfe 0%, #eceef0 42%, #cfd2d6 100%),
        #d3d6da;
    background-attachment: fixed;
    color: var(--ink);
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------------------------ *
 * the top bar: bike, name, the games behind one button, a way to the music
 * ------------------------------------------------------------------ */

.top {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "brand menu deck";
    align-items: center;
    gap: 6px 18px;
    padding: 12px 0 8px;
}

.brand {
    grid-area: brand;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: #383d44;
}

.brand__bike {
    width: 58px;
    height: auto;
    filter: drop-shadow(0 1px 0 #fff);
}

.brand__name {
    font-family: Oswald, "Helvetica Neue", Arial, sans-serif;
    font-weight: 600;
    font-size: 1.45rem;
    letter-spacing: .03em;
    line-height: 1;
    text-shadow: 0 1px 0 #fff, 0 2px 3px rgba(0, 0, 0, .12);
    white-space: nowrap;
}

/* the games menu: a button that names the open game, and a list that drops
   from it */
.menu { grid-area: menu; position: relative; justify-self: start; }
.menu__btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 12px 7px 14px;
    border-radius: 999px;
    border: 1px solid rgba(0, 0, 0, .12);
    background: linear-gradient(180deg, rgba(255, 255, 255, .85), rgba(255, 255, 255, .45));
    box-shadow: 0 1px 0 rgba(255, 255, 255, .8), 0 1px 2px rgba(0, 0, 0, .08);
    color: var(--ink);
    font: 500 .86rem/1 Oswald, "Helvetica Neue", Arial, sans-serif;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
    cursor: pointer;
    touch-action: manipulation;
    transition: background .15s;
}
.menu__btn:hover, .menu.is-open .menu__btn { background: #fff; }
.menu__chev {
    width: 7px;
    height: 7px;
    margin-top: -3px;
    border: solid var(--ink-soft);
    border-width: 0 1.5px 1.5px 0;
    transform: rotate(45deg);
    transition: transform .15s;
}
.menu.is-open .menu__chev { transform: rotate(225deg); margin-top: 3px; }
.menu__list {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    min-width: 220px;
    padding: 6px;
    border-radius: var(--radius);
    border: 1px solid rgba(0, 0, 0, .12);
    background: linear-gradient(180deg, #fff, #eef0f2);
    box-shadow: 0 1px 0 rgba(255, 255, 255, .9) inset, 0 12px 30px rgba(0, 0, 0, .22);
    animation: dialog-in var(--dialog-in) ease both;
}
.tab {
    padding: 10px 14px;
    border-radius: 8px;
    color: var(--ink-soft);
    font: 500 .86rem/1 Oswald, "Helvetica Neue", Arial, sans-serif;
    letter-spacing: .06em;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
}
.tab:hover { color: var(--ink); background: rgba(0, 0, 0, .05); }
.tab[aria-current="page"] {
    color: #fff;
    background: linear-gradient(180deg, #4b5058, #2c3138);
}

.deck-link {
    grid-area: deck;
    font-family: Oswald, "Helvetica Neue", Arial, sans-serif;
    font-size: .78rem;
    letter-spacing: .3em;
    text-transform: uppercase;
    color: var(--ink-faint);
    text-decoration: none;
    white-space: nowrap;
}
.deck-link:hover { color: var(--ink); }

@media (max-width: 760px) {
    .top { gap: 6px 10px; padding-top: 10px; }
    .brand__bike { width: 44px; }
    .brand__name { font-size: 1.15rem; }
    .menu { justify-self: end; }
    .menu__list { left: auto; right: 0; }
}
@media (max-width: 480px) {
    .brand__name { display: none; }
}

/* ------------------------------------------------------------------ *
 * the stage
 * ------------------------------------------------------------------ */

.stage {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 8px 0 24px;
}

.colophon {
    padding: 14px 0 18px;
    text-align: center;
    font-family: Oswald, sans-serif;
    font-size: .72rem;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.colophon a { color: inherit; text-decoration: none; }
.colophon a:hover { color: var(--ink); }

/* ---- the landing ---- */

.home { width: 100%; max-width: 900px; text-align: center; }

.home__title {
    margin: 18px 0 0;
    font-family: Oswald, sans-serif;
    font-weight: 600;
    font-size: clamp(1.7rem, 5vw, 2.5rem);
    letter-spacing: .02em;
    color: #383d44;
    text-shadow: 0 1px 0 #fff, 0 2px 3px rgba(0, 0, 0, .12);
}
.home__lede {
    margin: 6px 0 22px;
    font-family: Oswald, sans-serif;
    letter-spacing: .3em;
    text-transform: uppercase;
    font-size: clamp(.7rem, 2.2vw, .85rem);
    color: var(--ink-faint);
}

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 14px;
    text-align: left;
}

.card {
    display: grid;
    grid-template-columns: 56px 1fr;
    grid-template-areas: "art name" "art tag";
    column-gap: 14px;
    align-items: center;
    padding: 14px 16px;
    border-radius: var(--radius);
    border: 1px solid rgba(0, 0, 0, .1);
    border-bottom-color: rgba(255, 255, 255, .6);
    background: var(--card);
    box-shadow: 0 1px 0 rgba(255, 255, 255, .8), 0 2px 6px rgba(0, 0, 0, .08);
    text-decoration: none;
    color: var(--ink);
    transition: transform .15s, box-shadow .15s;
}
.card:hover {
    transform: translateY(-2px);
    box-shadow: 0 1px 0 rgba(255, 255, 255, .8), 0 6px 14px rgba(0, 0, 0, .12);
}
.card__name {
    grid-area: name;
    font-family: Oswald, sans-serif;
    font-weight: 500;
    font-size: 1.05rem;
    letter-spacing: .04em;
}
.card__tag { grid-area: tag; font-size: .82rem; color: var(--ink-soft); line-height: 1.3; }

/* Each card's little picture is drawn in CSS - a hint of the board the game
   is played on, so the card already looks like what it opens. */
.card__art {
    grid-area: art;
    width: 56px;
    height: 56px;
    border-radius: 8px;
    box-shadow: inset 0 0 0 1px var(--line), 0 1px 0 rgba(255, 255, 255, .8);
    background-color: #e4e6e9;
}
.card__art--minesweeper {
    background:
        linear-gradient(90deg, var(--line) 1px, transparent 1px) 0 0 / 33.33% 100%,
        linear-gradient(180deg, var(--line) 1px, transparent 1px) 0 0 / 100% 33.33%,
        linear-gradient(180deg, #f2f3f5, #d9dcdf);
}
.card__art--sudoku {
    background:
        linear-gradient(90deg, var(--line) 1px, transparent 1px) 0 0 / 33.33% 100%,
        linear-gradient(180deg, var(--line) 1px, transparent 1px) 0 0 / 100% 33.33%,
        var(--paper);
}
.card__art--checkers { background: repeating-conic-gradient(#e8e2d3 0 25%, #6b4a2b 0 50%) 0 0 / 50% 50%; }
.card__art--seabattle {
    background:
        linear-gradient(90deg, rgba(30, 45, 70, .25) 1px, transparent 1px) 0 0 / 25% 100%,
        linear-gradient(180deg, rgba(30, 45, 70, .25) 1px, transparent 1px) 0 0 / 100% 25%,
        linear-gradient(180deg, #d5e6f0, #a4c3d6);
}
.card__art--dungeons {
    background:
        linear-gradient(90deg, rgba(255, 255, 255, .12) 1px, transparent 1px) 0 0 / 25% 100%,
        linear-gradient(180deg, rgba(255, 255, 255, .12) 1px, transparent 1px) 0 0 / 100% 25%,
        #3a3c42;
}
.card__art--tents {
    background:
        linear-gradient(90deg, rgba(30, 45, 70, .2) 1px, transparent 1px) 0 0 / 25% 100%,
        linear-gradient(180deg, rgba(30, 45, 70, .2) 1px, transparent 1px) 0 0 / 100% 25%,
        linear-gradient(180deg, #dcebcc, #b9d19f);
}

/* ---- a game on the stage ---- */

.game {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
}

.game__bar {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 8px 10px;
    width: 100%;
    max-width: 900px;
}
/* the sudoku's second row: the buttons asked for now and then, a little
   apart from the row you play with */
.game__bar--tools { margin-top: 8px; }
/* the sudoku's second row: icons, so it never wraps on a phone - a second
   row that wrapped pushed the pad off the screen */

/* the settings dialog: a switch per line */
.settings { display: flex; flex-direction: column; gap: 12px; text-align: left; margin: 4px 0 14px; }
.switch--row { width: 100%; }
.switch--row .switch__label { text-transform: none; letter-spacing: 0; font: 500 .9rem/1.25 "Helvetica Neue", Arial, sans-serif; }

/* a switch with a sentence for a label. Its state is where the knob sits,
   left or right, as well as the track's colour - so it reads without the
   colour, which matters here. */
.switch {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    font: 500 .78rem/1 Oswald, sans-serif;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-soft);
}
.switch input { position: absolute; width: 1px; height: 1px; margin: -1px; opacity: 0; }
.switch__track {
    position: relative;
    flex: 0 0 34px;
    width: 34px;
    height: 18px;
    border-radius: 9px;
    background: #c9ccd1;
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25);
    transition: background .18s;
}
.switch__track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
    transition: transform .18s;
}
.switch input:checked ~ .switch__track { background: var(--clue-met); }
.switch input:checked ~ .switch__track::after { transform: translateX(16px); }
.switch input:focus-visible ~ .switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch input:checked ~ .switch__label { color: var(--ink); }
/* sudoku: the kind and an icon per rule that is on, same icons as the toggles */
.mode {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 0 6px;
    height: 32px;
    border-radius: 8px;
    background: rgba(0, 0, 0, .05);
    color: var(--ink-soft);
}
.mode__kind { font: 600 .72rem/1 Oswald, sans-serif; letter-spacing: .12em; text-transform: uppercase; padding: 0 4px; }
.mode__rule { display: grid; place-items: center; width: 22px; height: 22px; font-style: normal; }
.mode__rule svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.mode__rule svg text { font-family: "Helvetica Neue", Arial, sans-serif; }
/* kropki or XV with only some of them given: the icon says so by being hollow */
.mode__rule--part { opacity: .6; }
.mode__rule .tog__glyph { font-size: 1.2rem; }

.game__board {
    /* The board is centred. Almost always it fits, and then this is no
       scroll box at all: nothing clips the board's shadow, and no rounding
       of a cell size can make the box a hair narrower than the board and
       grow it a scrollbar - which on a phone is a thin line at the bottom
       of the box, right behind the pad's first row of keys.

       Only a board wider than the room it has - a 50-column minefield on a
       phone - scrolls sideways, so the page never does. core.js sets
       .is-wide on this box from the fit(), and only then does it become a
       scroll box: with padding for the shadow, since a scroll box clips at
       its padding edge, and an equal negative margin to give it back. */
    --stage-clear: 20px;    /* the room the pad keeps below the board: the shadow's reach */
    --stage-pad-x: 0px;
    --stage-pad-y: 0px;
    --stage-pad-b: 0px;
    max-width: 100%;
    overflow: visible;
    padding: var(--stage-pad-y) var(--stage-pad-x) var(--stage-pad-b);
    margin: calc(-1 * var(--stage-pad-y)) calc(-1 * var(--stage-pad-x)) calc(var(--stage-clear) - var(--stage-pad-b));
}
.game__board.is-wide {
    --stage-pad-x: 20px;
    --stage-pad-y: 14px;
    --stage-pad-b: 26px;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
}
/* on a phone the board runs out into the page's side padding, leaving only
   --board-gutter on each side: the page's 16px plus this box's own padding */
@media (max-width: 760px) {
    /* the board takes the width and the page scrolls; the fallback --cell
       arithmetic below drops its height term the same way */
    /* 14px, not 6: the shadow needs the room at the sides or it is only
       seen underneath, as a bar. A tighter shadow than the desktop's, so
       14px is enough - it costs the board 16px of width on a phone. */
    :root { --board-gutter: 14px; --board-fills-width: 1; --board-shadow: 0 4px 10px rgba(0, 0, 0, .22); }
    .game__board {
        margin-left: calc(var(--board-gutter) - 16px - var(--stage-pad-x));
        margin-right: calc(var(--board-gutter) - 16px - var(--stage-pad-x));
        max-width: calc(100% + 2 * (16px + var(--stage-pad-x) - var(--board-gutter)));
    }
    .game__board.is-wide { --stage-pad-x: 14px; }
}

.game__foot {
    min-height: 1.4em;
    font-family: Oswald, sans-serif;
    font-size: .82rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-soft);
    text-align: center;
}
.game__foot.is-good { color: var(--good); }
.game__foot.is-bad  { color: var(--bad); }
/* why a finished board is not the answer: a sentence, and the foot's caps and
   wide tracking are meant for two or three words. Same treatment as a hint. */
.game__foot.is-why {
    color: var(--bad);
    text-transform: none;
    letter-spacing: 0;
    font-size: .95rem;
    max-width: 560px;
    line-height: 1.35;
}

/* the rules, under everything */
.game__rules {
    width: min(100%, 620px);
    margin-top: 10px;
    padding: 14px 18px;
    border-radius: var(--radius);
    background: rgba(255, 255, 255, .5);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06);
    color: var(--ink-soft);
    font-size: .86rem;
    line-height: 1.55;
}
.game__rules:empty { display: none; }
.game__rules h4 {
    margin: 0 0 4px;
    font: 500 .74rem/1 Oswald, sans-serif;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.game__rules p { margin: 0 0 10px; }
.game__rules ul { margin: 0 0 10px; padding-left: 18px; }
.game__rules li { margin: 2px 0; }
.game__rules b { color: var(--ink); font-weight: 600; }
.game__rules kbd {
    display: inline-block;
    padding: 0 5px;
    border-radius: 4px;
    background: #fff;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15), 0 1px 0 rgba(0, 0, 0, .1);
    font: 600 .78em/1.6 "Helvetica Neue", Arial, sans-serif;
    color: var(--ink);
}
.game__rules > :last-child { margin-bottom: 0; }

.rebuilding {
    max-width: 420px;
    margin: 40px 0;
    text-align: center;
    color: var(--ink-soft);
    line-height: 1.5;
}

/* ------------------------------------------------------------------ *
 * shared controls: buttons, chips, fields, readouts
 * ------------------------------------------------------------------ */

.btn,
.chip {
    appearance: none;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 9px;
    padding: 8px 14px;
    background: linear-gradient(180deg, #fff, #dfe2e6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25), inset 0 1px 0 #fff;
    color: var(--ink);
    font: 500 .84rem/1 Oswald, "Helvetica Neue", Arial, sans-serif;
    letter-spacing: .06em;
    text-transform: uppercase;
    cursor: pointer;
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    transition: background .12s, box-shadow .12s, transform .06s;
}
.btn:hover, .chip:hover { background: linear-gradient(180deg, #fff, #e9ebee); }
/* the sudoku's second row: icons, so it never wraps on a phone - a wrapped
   row pushed the pad off the screen. Stated as .btn.btn--icon and after
   .btn, so its zero padding beats the button's: with the button's padding
   left in, the icon sat off-centre */
.btn.btn--icon { width: 36px; height: 31px; padding: 0; display: inline-grid; place-items: center; line-height: 0; }
.btn.btn--icon svg { display: block; width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.btn.btn--icon svg text { font-family: "Helvetica Neue", Arial, sans-serif; }
.btn:active, .chip:active { transform: translateY(1px); box-shadow: 0 0 1px rgba(0, 0, 0, .3), inset 0 1px 0 #fff; }
.btn:disabled, .chip:disabled { opacity: .45; cursor: default; }
.btn--primary {
    color: #fff;
    background: linear-gradient(180deg, #d0483a, var(--accent));
    border-color: #8f2d23;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .3), inset 0 1px 0 rgba(255, 255, 255, .35);
}
.btn--primary:hover { background: linear-gradient(180deg, #d8564a, #ba4034); }
.btn--icon { padding: 8px 10px; }
.btn.is-on,
.chip.is-on {
    color: #fff;
    background: linear-gradient(180deg, #4b5058, #2c3138);
    border-color: #1f2226;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 1px 2px rgba(0, 0, 0, .25);
}

/* A row of chips that act as one control - difficulty, ship size, game mode. */
.chips {
    display: inline-flex;
    gap: 4px;
    padding: 3px;
    border-radius: 11px;
    background: rgba(0, 0, 0, .06);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .12);
}
.chips .chip { box-shadow: none; border-color: transparent; background: transparent; padding: 6px 11px; }
.chips .chip:hover { background: rgba(255, 255, 255, .6); }
.chips .chip.is-on { background: linear-gradient(180deg, #4b5058, #2c3138); border-color: #1f2226; }

.field {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font: 500 .72rem/1 Oswald, sans-serif;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-soft);
}
.field input {
    width: 4.2em;
    padding: 7px 6px;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 8px;
    background: rgba(255, 255, 255, .8);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
    font: 600 .95rem/1 "Helvetica Neue", Arial, sans-serif;
    color: var(--ink);
    text-align: center;
    outline: none;
}
.field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* A counter with a label under it: mines left, seconds, pieces taken. */
/* the clock and the mine count: just the number, in a box the height of the
   buttons beside it, so the bar reads as one row of controls. The label is
   still in the markup for a screen reader - "TIME" under a clock only said
   what the clock already said. */
.readout {
    display: inline-flex;
    align-items: center;
    padding: 7px 11px;
    border: 1px solid transparent;
    border-radius: 9px;
    background: rgba(0, 0, 0, .06);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, .12);
}
.readout__value {
    /* a monospaced face, because Oswald's digits are not all the same width
       and the box grew and shrank every second. The readout is a scoreboard,
       not running text, so a mono face belongs here anyway. */
    font: 600 .88rem/1 ui-monospace, "Cascadia Mono", "Consolas", "DejaVu Sans Mono", monospace;
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
    color: var(--ink);
}
.readout__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.bar-gap { flex: 0 0 8px; }
.bar-break { flex-basis: 100%; height: 0; }

/* ------------------------------------------------------------------ *
 * the board every game is made of
 *
 * A CSS grid of buttons. --cols and --rows come from the game; the cell size
 * is worked out here from the room there is, and the board never asks the page
 * to scroll sideways - it scrolls itself inside .game__board once cells would
 * go under --cell-min.
 *
 * --stage-h is the height the board has to fit in: the viewport less the top
 * bar and the game's own bar, roughly. core.js measures it properly and sets
 * it on the board; this is the fallback.
 * ------------------------------------------------------------------ */

.board {
    --cols: 9;
    --rows: 9;
    --stage-h: calc(100vh - 230px);
    --cell: max(var(--cell-min),
                min(var(--cell-max),
                    calc((100vw * var(--share, 1) - 2 * var(--board-gutter) - (var(--cols) + 1) * var(--cell-gap)) / var(--cols)),
                    calc((var(--stage-h) - (var(--rows) - 1) * var(--cell-gap)) / var(--rows))));
    position: relative;
    display: grid;
    width: max-content;      /* its own size, so the scroll box sees all of it */
    grid-template-columns: repeat(var(--cols), var(--cell));
    grid-auto-rows: var(--cell);
    gap: var(--cell-gap);
    padding: var(--cell-gap);
    border-radius: 6px;
    /* the cells are square and the padding is a pixel, so without this the
       corner cells stick out past the rounded corner - plain to see on a sea */
    overflow: hidden;
    background: var(--line);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .18), var(--board-shadow);
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    touch-action: manipulation;
    animation: board-in var(--board-in) ease both;
}
/* a drawing board: a finger dragged across it paints, so it must not scroll */
.board.is-paint { touch-action: none; }

@keyframes board-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

.cell {
    appearance: none;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    display: grid;
    place-items: center;
    background: #fff;
    color: var(--ink);
    font: 700 calc(var(--cell) * .5) / 1 "Helvetica Neue", Arial, sans-serif;
    cursor: pointer;
    outline: none;
    overflow: hidden;
    transition: background var(--cell-fade), color var(--cell-fade), box-shadow var(--cell-fade);
}
.cell:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); z-index: 1; }

/* the New dialog: a row per choice, its label small and up the left */
.setup { text-align: left; margin: 4px 0 18px; }
.setup__row { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 8px 0; border-top: 1px solid rgba(0, 0, 0, .06); }
.setup__row:first-child { border-top: 0; }
.setup__label {
    font: 500 .7rem/1.4 Oswald, sans-serif;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.setup .chips { flex-wrap: wrap; }
.setup__fields { display: flex; flex-wrap: wrap; gap: 8px; }

/* ------------------------------------------------------------------ *
 * dialog and toast
 * ------------------------------------------------------------------ */

.veil {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: grid;
    place-items: center;
    padding: 16px;
    background: rgba(40, 44, 50, .35);
    backdrop-filter: blur(2px);
    animation: veil-in var(--dialog-in) ease both;
}
@keyframes veil-in { from { opacity: 0; } }
/* a cell changing what it shows - a digit going in, a wall going up, a
   tent pitched - fades to its new state rather than jumping. A browser
   starts the animation when the class arrives, so a state that is merely
   repainted the same does not flicker; the sudoku adds is-new only to the
   cells whose content actually changed, for the same reason. */
@keyframes cell-in { from { opacity: .25; } }
.su .cell.is-new > * { animation: cell-in var(--cell-fade) ease; }
.dd .cell.is-wall, .dd .cell.is-floor,
.tt .cell.is-tent, .tt .cell.is-grass { animation: cell-in var(--cell-fade) ease; }
@media (prefers-reduced-motion: reduce) {
    .su .cell.is-new > *, .dd .cell.is-wall, .dd .cell.is-floor, .tt .cell.is-tent, .tt .cell.is-grass { animation: none; }
}

.dialog {
    width: min(100%, 360px);
    padding: 22px 22px 18px;
    border-radius: var(--radius);
    border: 1px solid rgba(0, 0, 0, .12);
    background: linear-gradient(180deg, #fff, #eef0f2);
    box-shadow: 0 1px 0 rgba(255, 255, 255, .9) inset, 0 18px 40px rgba(0, 0, 0, .3);
    text-align: center;
    animation: dialog-in var(--dialog-in) ease both;
}
@keyframes dialog-in { from { opacity: 0; transform: translateY(10px) scale(.98); } }

.dialog__title {
    margin: 0 0 6px;
    font: 600 1.5rem/1.1 Oswald, sans-serif;
    letter-spacing: .03em;
    color: #383d44;
}
.dialog__text { margin: 0 0 18px; color: var(--ink-soft); line-height: 1.45; }
.dialog__buttons { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }

.toast {
    position: fixed;
    left: 50%;
    bottom: 22px;
    /* above the veil: a toast that answers a button inside a dialog - Share,
       which leaves the dialog standing - must be readable over it */
    z-index: 60;
    transform: translateX(-50%);
    padding: 9px 16px;
    border-radius: 999px;
    background: rgba(44, 49, 56, .92);
    color: #fff;
    font: 500 .8rem/1 Oswald, sans-serif;
    letter-spacing: .08em;
    text-transform: uppercase;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .3);
    animation: toast-in .2s ease both;
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } }

@media (prefers-reduced-motion: reduce) {
    .board, .veil, .dialog, .toast, .cell { animation: none; transition: none; }
}

/* ------------------------------------------------------------------ *
 * Minesweeper
 *
 * The same tiles as the dungeon: every cell is paper with its number printed
 * on, and a covered one wears a stone tile over the top (::after). Opening a
 * cell fades the stone away, and the ripple when a blank opens a whole region
 * is that fade's delay, set per cell by the game from the cell's distance to
 * the tap.
 * ------------------------------------------------------------------ */

.ms {
    --cell-gap: 2px;
    background: transparent;
    box-shadow: none;
}
.ms .cell {
    position: relative;
    background: var(--paper);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1);
    border-radius: 3px;
}
.ms .cell::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 3px;
    background: linear-gradient(180deg, #d7d3c9, #c4bfb3);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
    transition: opacity var(--cell-fade) ease, background var(--cell-fade);
    transition-delay: var(--d, 0ms), 0ms;
}
.ms .cell:hover::after { background: linear-gradient(180deg, #e0dcd2, #cdc8bc); }
.ms .cell.is-peek::after { background: linear-gradient(180deg, #c4bfb3, #b3ad9f); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18); }
.ms .cell.is-open { cursor: default; }
.ms .cell.is-open::after { opacity: 0; pointer-events: none; }

/* what is printed on the paper, or pinned on the key, sits above the key */
.ms .cell::before { position: relative; z-index: 1; }

.ms .cell.is-flag { color: var(--accent); font-size: calc(var(--cell) * .55); }
.ms .cell.is-flag::before { content: "\2691"; }   /* ⚑ */
.ms .cell.is-mine::before {
    content: "";
    width: 42%;
    height: 42%;
    border-radius: 50%;
    background: var(--ink);
    box-shadow: 0 0 0 4px rgba(44, 49, 56, .15);
}
.ms .cell.is-boom { background: var(--accent); }
.ms .cell.is-boom::before { background: #fff; box-shadow: none; }
.ms .cell.is-wrong::after { background: linear-gradient(180deg, #f2d6d2, #e6b6ae); }
.ms .cell.is-wrong::before { content: "\2691"; color: var(--bad); }
.ms .cell.is-wrong { color: var(--bad); font-size: calc(var(--cell) * .55); }

.ms .cell[data-n="1"] { color: #2e63b8; }
.ms .cell[data-n="2"] { color: #3a7d44; }
.ms .cell[data-n="3"] { color: #b3392c; }
.ms .cell[data-n="4"] { color: #4b3f8f; }
.ms .cell[data-n="5"] { color: #8a3b1e; }
.ms .cell[data-n="6"] { color: #2a8a8a; }
.ms .cell[data-n="7"] { color: #2c3138; }
.ms .cell[data-n="8"] { color: #6b7178; }

.ms.is-over .cell { cursor: default; }

/* ------------------------------------------------------------------ *
 * Sudoku
 *
 * Paper cells, box lines drawn as cell borders, and four tints that stack:
 * the chosen cell, its row/column/box, every cell holding the same digit,
 * and a conflict. Sandwich adds a row and a column of sums outside the grid.
 * ------------------------------------------------------------------ */

.su {
    --cell-max: 52px;
    --cell-gap: 1px;
    /* the thin lines, opaque: --line-soft is translucent, and where lines
       are drawn per cell (the sandwich board's shadows) four of them meet
       on the same pixel at every corner and stack darker */
    --su-line: #d3d2cf;   /* rgba(30,45,70,.16) over the paper */
    background: var(--su-line);
    box-shadow: 0 0 0 var(--su-line-w) var(--ink), var(--board-shadow);
}
.su .cell {
    background: var(--paper);
    color: #2e63b8;
    font-weight: 500;
    font-size: calc(var(--cell) * .52);
    transition: background var(--cell-fade), color var(--cell-fade);
}
.su .cell.is-given { color: var(--ink); font-weight: 700; }
/* the three tones, one per cell: the X diagonal's tint underneath them all */
.su .cell.is-diag { background: #ece6d8; }
.su .cell.is-lit { background: #e3e7ee; }
.su .cell.is-same { background: #c6d4ea; }
.su .cell.is-sel { background: #bfd0ea; }
.su .cell.is-bad { color: var(--bad); }
/* a hint: the cells it reasons from ringed, the ones it rules a digit out of dimmed */
.su .cell.is-hint { box-shadow: inset 0 0 0 3px #d98b2b; background: #fbe7c9; }
.su .cell.is-hint-off { background: #ebdcd8; }
.game__foot.is-hint { color: var(--ink); text-transform: none; letter-spacing: 0; font-size: .95rem; max-width: 560px; line-height: 1.35; }
.su .cell.is-wrong { background: #f2d6d2; color: var(--bad); }
.su .cell:hover:not(.is-sel):not(.is-clue) { background: #dfe4ec; }
.su.is-over .cell { color: var(--ink); cursor: default; }

/* the thick lines between boxes: one layer over the whole 9 x 9, above the
   cells and the 1px gaps between them, so nothing thin runs across them.
   Each line is 3px on the gap it sits over: the gap and a pixel of the cell
   either side. With the sums outside (.su--sandwich) the layer starts after
   the half-cell edge track and draws the grid's own frame as well - nothing
   is drawn through the row and column of sums. */
.su { --su-edge: 0px; }
.su--sandwich { --su-edge: calc(var(--cell) * .64 + var(--cell-gap)); }   /* the edge track, same fraction as build() gives the board */
/* A box line has to run all the way into the frame. Without sums the frame is
   the board's own 3px shadow, which sits outside the board's 1px padding - so
   a layer stretched over the cells alone stopped one gap short at each end and
   left a pale pixel where the two should meet. The layer reaches out over that
   padding instead (--su-lines-pad), and the lines move with it. With the sums
   the frame is this layer's own shadow and there is nothing to reach across,
   so the padding is 0 there. A background cannot be painted outside its own
   element, which is why this is the element's size and not the gradients'. */
.su { --su-lines-pad: var(--cell-gap); }
.su--sandwich { --su-lines-pad: 0px; }
.su .lines {
    position: absolute;
    top: calc(var(--cell-gap) + var(--su-edge) - var(--su-lines-pad));
    left: calc(var(--cell-gap) + var(--su-edge) - var(--su-lines-pad));
    width: calc(9 * var(--cell) + 8 * var(--cell-gap) + 2 * var(--su-lines-pad));
    height: calc(9 * var(--cell) + 8 * var(--cell-gap) + 2 * var(--su-lines-pad));
    z-index: 2;
    pointer-events: none;
    background:
        linear-gradient(var(--ink), var(--ink)) calc(3 * var(--cell) + 2 * var(--cell-gap) - 1px + var(--su-lines-pad)) 0 / var(--su-line-w) 100% no-repeat,
        linear-gradient(var(--ink), var(--ink)) calc(6 * var(--cell) + 5 * var(--cell-gap) - 1px + var(--su-lines-pad)) 0 / var(--su-line-w) 100% no-repeat,
        linear-gradient(var(--ink), var(--ink)) 0 calc(3 * var(--cell) + 2 * var(--cell-gap) - 1px + var(--su-lines-pad)) / 100% var(--su-line-w) no-repeat,
        linear-gradient(var(--ink), var(--ink)) 0 calc(6 * var(--cell) + 5 * var(--cell-gap) - 1px + var(--su-lines-pad)) / 100% var(--su-line-w) no-repeat;
}
.su--sandwich .lines { box-shadow: 0 0 0 var(--su-line-w) var(--ink), var(--board-shadow); }
/* the frame is drawn outside the grid, and the board clips its overflow:
   the sums leave half a cell of room above and to the left, so the right
   and bottom need the 2px themselves or the frame is thinner there */
.su--sandwich { padding-right: calc(var(--cell-gap) + var(--su-line-w)); padding-bottom: calc(var(--cell-gap) + var(--su-line-w)); }
/* the frame is the grid's own box, so it carries the board's rounded
   corner - and the four cells at the corners of the 9 x 9 are rounded to
   match, since nothing clips them here */
.su--sandwich .lines { border-radius: 6px; }
.su--sandwich .cell:not(.is-clue)[data-r="1"][data-c="1"] { border-top-left-radius: 5px; }
.su--sandwich .cell:not(.is-clue)[data-r="1"][data-c="9"] { border-top-right-radius: 5px; }
.su--sandwich .cell:not(.is-clue)[data-r="9"][data-c="1"] { border-bottom-left-radius: 5px; }
.su--sandwich .cell:not(.is-clue)[data-r="9"][data-c="9"] { border-bottom-right-radius: 5px; }

/* the sums, outside the grid on a sandwich board, in half a cell */
.su .cell.is-clue {
    background: transparent;
    color: var(--ink-soft);
    font-size: calc(var(--cell) * .36);
    font-weight: 700;
    cursor: default;
    box-shadow: none;
}
/* the sums sit against the grid, the same distance from it on the left and
   on the top: the left column's flush to its right edge, the top row's to
   its bottom. --clue-gap is measured to the ink, not the box - a digit's
   line box carries descender room below the baseline that its right edge
   does not, so the top row gets that much less padding (about .2em) */
.su--sandwich { --clue-gap: 9px; }
.su--sandwich .cell.is-clue[data-c="0"] { justify-items: end; padding-right: calc(var(--clue-gap) - .08em); }   /* .08em: the number's own side padding, for the strike */
.su--sandwich .cell.is-clue[data-r="0"] { align-items: end; padding-bottom: calc(var(--clue-gap) - .14em); }
/* with the sums outside, the board box is bigger than the grid, so the
   frame and the drop shadow belong to .lines, which is the grid exactly -
   and the board must not clip them (it has no rounded corner to protect) */
.su--sandwich { background: transparent; box-shadow: none; overflow: visible; }
.su--sandwich .cell:not(.is-clue) { box-shadow: 0 0 0 1px var(--su-line); }
.su--sandwich .cell.is-clue { box-shadow: none; }
/* a struck sum: the line runs through the number - the number, which sits
   in its own span, not the cell, which is a whole cell wide for the column
   sums and half for the row sums */
.su .cell.is-clue.is-struck { color: var(--ink-faint); opacity: .55; }
.su .clue__n { position: relative; display: inline-block; padding: 0 .08em; }
.su .cell.is-clue.is-struck .clue__n::after {
    content: "";
    position: absolute;
    left: -6%;
    right: -6%;
    top: 50%;
    height: 1.5px;
    background: var(--accent);
    transform: rotate(-14deg);
}
.su .cell.is-clue { position: relative; }

/* pencil marks: a 3 x 3 of small digits */
.su .marks {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    width: 100%;
    height: 100%;
    padding: 5%;
}
.su .marks i {
    display: grid;
    place-items: center;
    font: 600 calc(var(--cell) * .33) / 1 "Helvetica Neue", Arial, sans-serif;
    color: var(--ink-soft);
}
/* a mark holding the digit in focus changes colour and nothing else. It used
   to go to 800 as well, and a mark is set in Arial on Windows, which has
   Regular and Bold and no 800 - so the heavier weight was resolved to another
   face of the family and the digit came out looking like a different font at
   a different size. The cell's own digit never changed weight either. */
.su .marks i.is-same { color: #2e63b8; }

/* the pad: one grid of square keys, three rows, the shape of the phone
   sudoku he plays - the digits 3 x 3, the pens down the right, undo / redo /
   select-several beside the digits, and the outer left column 1|9, cross,
   erase (the marks only with sandwich on). Each key has its named area; the
   key size is the column width, so the keys are square by ratio and
   --pad-key is the most they grow. */
.pad {
    --pad-key: 56px;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    grid-template-areas:
        "pr un d1 d2 d3 pz"
        "cx re d4 d5 d6 px"
        "er ms d7 d8 d9 pc";
    gap: 5px;
    width: min(100%, calc(6 * var(--pad-key) + 5 * 5px));
}
/* without sandwich sums the two slots above erase stay empty: erase keeps
   its place, and so does everything else, whatever kind is on */
.pad__key--d1 { grid-area: d1; } .pad__key--d2 { grid-area: d2; } .pad__key--d3 { grid-area: d3; }
.pad__key--d4 { grid-area: d4; } .pad__key--d5 { grid-area: d5; } .pad__key--d6 { grid-area: d6; }
.pad__key--d7 { grid-area: d7; } .pad__key--d8 { grid-area: d8; } .pad__key--d9 { grid-area: d9; }
.pad .pen--enter { grid-area: pz; } .pad .pen--corner { grid-area: px; } .pad .pen--centre { grid-area: pc; }
.pad__key--undo { grid-area: un; } .pad__key--redo { grid-area: re; } .pad__key--erase { grid-area: er; } .pad__key--multi { grid-area: ms; }
.pad__key--pair { grid-area: pr; } .pad__key--cross { grid-area: cx; }
.pad .pen { width: auto; height: auto; aspect-ratio: 1 / 1; }
.pad__key {
    appearance: none;
    position: relative;
    height: auto;
    aspect-ratio: 1 / 1;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 9px;
    padding: 0;
    background: linear-gradient(180deg, #fff, #dfe2e6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25), inset 0 1px 0 #fff;
    color: #2e63b8;
    font: 600 1.35rem/1 "Helvetica Neue", Arial, sans-serif;
    cursor: pointer;
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    transition: background .12s, transform .06s;
}
.pad__key:hover { background: linear-gradient(180deg, #fff, #e9ebee); }
.pad__key:active { transform: translateY(1px); }
.pad__key.is-held {
    color: #fff;
    background: linear-gradient(180deg, #3f77c9, #2e63b8);
    border-color: #244f93;
}
.pad__key.is-done { opacity: .35; }
.pad__left {
    position: absolute;
    right: 5px;
    bottom: 3px;
    font: 600 .62rem/1 Oswald, sans-serif;
    color: var(--ink-faint);
}
.pad__key.is-held .pad__left { color: rgba(255, 255, 255, .7); }
/* undo, redo, erase: ink rather than digit blue, so the digits stay the
   loud ones; a disabled undo or redo just fades */
.pad__key--tool { color: var(--ink); font-size: 1.6rem; }
.pad__key--erase { font-size: 1.35rem; }
.pad__key--tool:disabled { opacity: .35; cursor: default; }
.pad__key--tool:disabled:active { transform: none; }
/* select several: four small squares, two of them chosen; lit like a pen when on */
.pad__key.is-on {
    color: #fff;
    background: linear-gradient(180deg, #4b5058, #2c3138);
    border-color: #1f2226;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 1px 2px rgba(0, 0, 0, .25);
}
.pad__multi {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 3px;
    width: 22px;
    height: 22px;
    margin: 0 auto;
}
.pad__multi i { display: block; border: 1.5px solid currentColor; border-radius: 2px; opacity: .55; }
.pad__multi i.is-in { background: currentColor; opacity: 1; }
.pad__key[hidden] { display: none; }
@media (max-width: 420px) {
    .pad { gap: 4px; }
    .pad__key { font-size: 1.25rem; border-radius: 7px; }
}

/* ------------------------------------------------------------------ *
 * Checkers
 *
 * Paper and slate squares inside the same ink ring as the sudoku; the
 * pieces live on a layer over the squares and slide from --r,--c to the
 * next with a transition, so a capture reads as a jump and not a teleport.
 * The squares underneath stay buttons for the taps.
 * ------------------------------------------------------------------ */

.ck {
    --cell-max: 64px;
    --cell-gap: 0px;
    padding: 0;
    background: transparent;
    box-shadow: 0 0 0 2px var(--ink), var(--board-shadow);
}
.ck .cell { transition: box-shadow .15s; }
.ck .cell.is-light { background: var(--paper); cursor: default; }
.ck .cell.is-dark  { background: linear-gradient(135deg, #8f959d, #6f757d); }
.ck .cell.is-movable { box-shadow: inset 0 0 0 3px rgba(255, 255, 255, .4); }
.ck .cell.is-sel { box-shadow: inset 0 0 0 3px var(--accent); }
.ck .cell.is-target { box-shadow: inset 0 0 0 3px rgba(179, 57, 44, .8); cursor: pointer; }
.ck .cell.is-target::after {
    content: "";
    width: 28%;
    height: 28%;
    border-radius: 50%;
    background: rgba(179, 57, 44, .8);
}
.ck.is-over .cell { cursor: default; }

.pieces {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2;
}
.piece {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--cell);
    height: var(--cell);
    display: grid;
    place-items: center;
    transform: translate(calc(var(--c) * (var(--cell) + var(--cell-gap))), calc(var(--r) * (var(--cell) + var(--cell-gap))));
    transition: transform var(--piece-move) ease-in-out, opacity var(--piece-move);
}
.piece::before {
    content: "";
    width: 78%;
    height: 78%;
    border-radius: 50%;
    box-shadow: 0 3px 4px rgba(0, 0, 0, .45), inset 0 -3px 0 rgba(0, 0, 0, .25), inset 0 2px 0 rgba(255, 255, 255, .25);
}
.piece--white::before { background: radial-gradient(circle at 40% 35%, #fbf7ee, #d9d1c0 70%, #b8ad98); }
.piece--black::before { background: radial-gradient(circle at 40% 35%, #5a5f66, #2c3138 70%, #1a1d21); }
/* a king wears a ring */
.piece.is-king::after {
    content: "";
    position: absolute;
    width: 42%;
    height: 42%;
    border-radius: 50%;
    border: 3px solid var(--accent);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .35), inset 0 0 0 1px rgba(0, 0, 0, .35);
}
.piece.is-taken { opacity: 0; transform: translate(calc(var(--c) * (var(--cell) + var(--cell-gap))), calc(var(--r) * (var(--cell) + var(--cell-gap)))) scale(.4); }

@media (prefers-reduced-motion: reduce) { .piece { transition: none; } }

/* ------------------------------------------------------------------ *
 * Sea battle
 *
 * Two seas. While the fleet is being placed only yours is shown, large;
 * in the battle they sit side by side, or one above the other on a phone,
 * theirs first because that is the one being tapped.
 * ------------------------------------------------------------------ */

.sb {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 14px 28px;
}
.sb__side { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.sb__title {
    margin: 0;
    font: 500 .78rem/1 Oswald, sans-serif;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.sb--place .sb__side--theirs { display: none; }
/* one size in both phases - the sea must not grow or shrink when the battle
   starts - and the size is that of two side by side */
.sea { --share: .5; --cell-max: 40px; }
@media (max-width: 700px) {
    .sb { flex-direction: column; align-items: center; }
    .sea { --share: 1; --cell-max: 34px; }
}
.sb.is-mine .sea--theirs .cell { cursor: crosshair; }
.sb:not(.is-mine) .sea--theirs .cell { cursor: default; }
.sb--battle .sea--mine .cell { cursor: default; }

/* whose turn: the sea being fired at stands out, the other one steps back */
.sb--battle .sb__side { transition: opacity .25s, filter .25s; }
.sb--battle.is-mine .sb__side--mine,
.sb--battle.is-theirs .sb__side--theirs { opacity: .45; filter: saturate(.3); }
.sb--battle.is-mine .sb__side--theirs .sb__title,
.sb--battle.is-theirs .sb__side--mine .sb__title { color: var(--accent); }
.sb--battle.is-mine .sb__side--theirs .sb__title::after { content: " · your shot"; }
.sb--battle.is-theirs .sb__side--mine .sb__title::after { content: " · their shot"; }

/* the same tiles as the camp and the dungeon, with a hint of water in them;
   a ship is a run of the dungeon's dark wall tiles */
.sea {
    --cell-gap: 2px;
    background: transparent;
    box-shadow: none;
}
.sea .cell {
    position: relative;
    background: linear-gradient(180deg, #dce5eb, #c8d4dc);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1);
    border-radius: 3px;
    color: var(--ink);
}
.sea .cell:hover { background: linear-gradient(180deg, #e5ecf1, #d2dde4); }
.sea .cell.is-ship {
    background: linear-gradient(180deg, #4b4f57, #2c3138);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), inset 0 -2px 0 rgba(0, 0, 0, .35);
}
/* the tile's corners are square on the sides that face a shipmate, so a ship
   is one shape with rounded ends, not a row of loose tiles */
.sea .cell.is-ship:not(.e-top)    { border-top-left-radius: 0; border-top-right-radius: 0; }
.sea .cell.is-ship:not(.e-bottom) { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.sea .cell.is-ship:not(.e-left)   { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.sea .cell.is-ship:not(.e-right)  { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.sea .cell.is-sunk { background: linear-gradient(180deg, #7a2e24, #56201a); }
.sea .cell.is-revealed { opacity: .55; }
.sea .cell.is-ghost { background: linear-gradient(180deg, #b9d19f, #9fbd82); }
.sea .cell.is-ghost-bad { background: linear-gradient(180deg, #f2d6d2, #e6b6ae); }
/* a miss is a dot, a hit a cross */
.sea .cell.is-miss::before {
    content: "";
    width: 20%;
    height: 20%;
    border-radius: 50%;
    background: rgba(44, 49, 56, .35);
}
.sea .cell.is-hit::before,
.sea .cell.is-hit::after {
    content: "";
    position: absolute;
    width: 62%;
    height: 3px;
    border-radius: 2px;
    background: var(--accent);
    transform: rotate(45deg);
}
.sea .cell.is-hit::after { transform: rotate(-45deg); }
.sea .cell.is-sunk.is-hit::before,
.sea .cell.is-sunk.is-hit::after { background: #f2c2b8; }
.sea .cell.is-last { box-shadow: inset 0 0 0 3px rgba(179, 57, 44, .7); }

/* the ship chips: little blocks and a count */
.chip.ship { display: inline-flex; align-items: center; gap: 6px; }
.ship__blocks { display: inline-flex; gap: 2px; }
.ship__blocks i { width: 9px; height: 9px; background: #6b7178; border-radius: 1px; }
.chip.ship.is-on .ship__blocks i { background: #fff; }
.ship__count { font-size: .7rem; color: var(--ink-faint); }
.chip.ship.is-on .ship__count { color: rgba(255, 255, 255, .8); }
.chip.ship.is-done { opacity: .4; }

/* ------------------------------------------------------------------ *
 * Dungeons & Diagrams
 *
 * Unknown cells are dim stone, walls are drawn in dark, a floor mark is a
 * dot. The monster and the chest are two little SVGs, inline so the game
 * is still one CSS file and no request.
 * ------------------------------------------------------------------ */

.dd {
    --cell-max: 52px;
    --cell-gap: 2px;
    background: transparent;
    box-shadow: none;
}
.dd .cell {
    background: linear-gradient(180deg, #d7d3c9, #c4bfb3);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
    border-radius: 3px;
    color: var(--ink);
}
.dd .cell:hover { background: linear-gradient(180deg, #e0dcd2, #cdc8bc); }
/* a wall is brick: two courses, the upper one offset by half a brick, drawn
   as mortar lines over a brown ground - a gradient each way rather than an
   image, so it costs nothing and scales with the cell */
.dd .cell.is-wall {
    background:
        /* the vertical joints: one in the middle of the upper course, two at
           the edges of the lower one */
        linear-gradient(90deg, transparent calc(50% - 1px), rgba(60, 33, 20, .55) calc(50% - 1px), rgba(60, 33, 20, .55) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 100% 50% no-repeat,
        linear-gradient(90deg, rgba(60, 33, 20, .55) 0 1px, transparent 1px calc(100% - 1px), rgba(60, 33, 20, .55) calc(100% - 1px)) 0 100% / 100% 50% no-repeat,
        /* the course between them */
        linear-gradient(180deg, transparent calc(50% - 1px), rgba(60, 33, 20, .55) calc(50% - 1px), rgba(60, 33, 20, .55) calc(50% + 1px), transparent calc(50% + 1px)),
        linear-gradient(180deg, #a9613c, #8a4a2c);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), inset 0 -2px 0 rgba(0, 0, 0, .28);
}
.dd .cell.is-floor { background: var(--paper); }
.dd .cell.is-floor:not(.is-monster):not(.is-chest)::before {
    content: "";
    width: 18%;
    height: 18%;
    border-radius: 50%;
    background: rgba(44, 49, 56, .3);
}
.dd .cell.is-monster,
.dd .cell.is-chest { background: var(--paper); cursor: default; }
.dd .cell.is-monster::before,
.dd .cell.is-chest::before {
    content: "";
    width: 72%;
    height: 72%;
    background: var(--icon) center / contain no-repeat;
}
.dd .cell.is-monster { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 3c-6 0-10 5-10 11v9l3-2 3 3 4-3 4 3 3-3 3 2v-9c0-6-4-11-10-11z' fill='%236d3fa0'/%3E%3Ccircle cx='12' cy='14' r='2.4' fill='%23fff'/%3E%3Ccircle cx='20' cy='14' r='2.4' fill='%23fff'/%3E%3Ccircle cx='12.6' cy='14.4' r='1.1' fill='%23222'/%3E%3Ccircle cx='20.6' cy='14.4' r='1.1' fill='%23222'/%3E%3Cpath d='M12 20h8l-1.5 2.5-1.5-1.5-1.5 1.5-1.5-1.5-1.5 1.5z' fill='%23fff'/%3E%3C/svg%3E"); }
.dd .cell.is-chest { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='4' y='11' width='24' height='16' rx='2' fill='%238a5a2b'/%3E%3Cpath d='M4 13a4 4 0 0 1 4-4h16a4 4 0 0 1 4 4v3H4z' fill='%23a86f36'/%3E%3Crect x='4' y='15' width='24' height='2' fill='%235e3a17'/%3E%3Crect x='13' y='13' width='6' height='7' rx='1' fill='%23e8c35a'/%3E%3Ccircle cx='16' cy='17' r='1.2' fill='%235e3a17'/%3E%3C/svg%3E"); }

.dd .cell.is-clue {
    position: relative;
    background: transparent;
    box-shadow: none;
    color: var(--ink-soft);
    font-size: calc(var(--cell) * .36);
    cursor: default;
}
/* when the board is centred rather than filling the width, it is the tiles
   that should sit in the middle, not the tiles plus the numbers' track: a
   right margin the width of that track shifts the board over by half of it */
@media (min-width: 761px) {
    .dd, .tt { margin-right: calc(var(--cell) * .62 + var(--cell-gap)); }
}
/* The count of a line: a filled badge once its walls are all in, a white
   badge with a thick red ring once there are too many.

   The badge is a square behind the number, drawn on ::before, and the
   number is centred in the clue cell whatever state it is in - the badge
   appearing changes nothing about where the digit sits. (It used to be the
   cell's own background, and the digit was nudged toward the grid until a
   badge appeared and re-centred it, so every count jumped as it was met.)
   The clue track is .62 of a cell, so a .62 square fills it across and the
   number lands over the middle of it. Shared with the camp below. */
/* the badge sits in the clue track, which is .62 of a cell: big enough to
   read as a badge, with room left so it is plainly centred rather than
   filling the track and showing every sub-pixel as a lopsided margin */
.dd, .tt { --clue-badge: calc(var(--cell) * .56); }
.dd .cell.is-clue::before, .tt .cell.is-clue::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: -1;
    width: var(--clue-badge);
    height: var(--clue-badge);
    /* a digit's ink sits a shade below the middle of its line box - about
       .02em for these faces - so the badge goes down by the same, or the
       number reads high in it */
    transform: translate(-50%, calc(-50% + .02em));
    border-radius: 8px;
    background: transparent;
    box-shadow: none;
    transition: background var(--cell-fade), box-shadow var(--cell-fade);
}
/* one weight in every state: a bolder digit is a wider and taller one, so
   thickening it on met would move it, which is the whole point of this */
.dd .cell.is-clue, .tt .cell.is-clue { font-weight: 800; }
.dd .cell.is-clue.is-met, .tt .cell.is-clue.is-met,
.dd .cell.is-clue.is-over, .tt .cell.is-clue.is-over { transition: color var(--cell-fade); }
.dd .cell.is-clue.is-met, .tt .cell.is-clue.is-met { color: #fff; }
.dd .cell.is-clue.is-met::before, .tt .cell.is-clue.is-met::before { background: var(--clue-met); }
.dd .cell.is-clue.is-over, .tt .cell.is-clue.is-over { color: var(--clue-over); }
.dd .cell.is-clue.is-over::before, .tt .cell.is-clue.is-over::before { background: #fff; box-shadow: inset 0 0 0 3px var(--clue-over); }
.dd.is-over .cell { cursor: default; }
/* the won dungeon lights its floor - but a monster and a chest stand on floor
   too, and they keep their paper and their icon. The dots stay: they are the
   path, and taking them away at the end replaced the thing the puzzle was
   about with a blank tint. */
.dd.is-over .cell.is-floor:not(.is-monster):not(.is-chest) { background: #efe6cf; }

/* the pen switch in the dungeon and the camp: a chip with a swatch of the
   mark it draws */
.chips--pen .chip { display: inline-flex; align-items: center; gap: 6px; }
.swatch { width: 12px; height: 12px; border-radius: 2px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18); }
/* the switch's swatch is the mark it draws, so it is brick too */
.swatch--wall {
    background:
        linear-gradient(180deg, transparent calc(50% - .5px), rgba(60, 33, 20, .55) calc(50% - .5px), rgba(60, 33, 20, .55) calc(50% + .5px), transparent calc(50% + .5px)),
        linear-gradient(180deg, #a9613c, #8a4a2c);
}
.swatch--floor { background: var(--paper); position: relative; }
.swatch--floor::after { content: ""; position: absolute; inset: 4px; border-radius: 50%; background: rgba(44, 49, 56, .4); }
.swatch--tent { background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 6L2 27h28z' fill='%23c9552f'/%3E%3Cpath d='M16 6l14 21H16z' fill='%23a8422a'/%3E%3C/svg%3E") center / 80% no-repeat, linear-gradient(180deg, #e9ecd9, #d6dcc2); }
.swatch--grass { background: linear-gradient(180deg, #b9d19f, #9fbd82); }
.chip.is-on .swatch { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35); }

/* ------------------------------------------------------------------ *
 * Tents & Trees
 * ------------------------------------------------------------------ */

.tt {
    --cell-max: 48px;
    --cell-gap: 2px;
    background: transparent;
    box-shadow: none;
}
.tt .cell {
    background: linear-gradient(180deg, #e9ecd9, #d6dcc2);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .1);
    border-radius: 3px;
}
.tt .cell:hover { background: linear-gradient(180deg, #f0f2e4, #dfe4cd); }
.tt .cell.is-grass { background: linear-gradient(180deg, #b9d19f, #9fbd82); }
.tt .cell.is-tree,
.tt .cell.is-tent { cursor: default; }
.tt .cell.is-tree::before,
.tt .cell.is-tent::before {
    content: "";
    width: 74%;
    height: 74%;
    background: var(--icon) center / contain no-repeat;
}
.tt .cell.is-tent { cursor: pointer; background: linear-gradient(180deg, #e9ecd9, #d6dcc2); }
.tt .cell.is-tent.is-bad { background: linear-gradient(180deg, #f2d6d2, #e6b6ae); }
.tt .cell.is-tree { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='14' y='21' width='4' height='8' rx='1' fill='%237a4b24'/%3E%3Cpath d='M16 3l9 11h-5l6 8H6l6-8H7z' fill='%233d7a45'/%3E%3Cpath d='M16 3l9 11h-5l6 8H16z' fill='%23336639'/%3E%3C/svg%3E"); }
.tt .cell.is-tent { --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 6L2 27h28z' fill='%23c9552f'/%3E%3Cpath d='M16 6l14 21H16z' fill='%23a8422a'/%3E%3Cpath d='M16 14l-5 13h10z' fill='%235e2a1a'/%3E%3C/svg%3E"); }

.tt .cell.is-clue {
    position: relative;
    background: transparent;
    box-shadow: none;
    color: var(--ink-soft);
    font-size: calc(var(--cell) * .36);
    cursor: default;
}
.tt.is-over .cell { cursor: default; }
.tt.is-over .cell:not(.is-tree):not(.is-tent):not(.is-clue) { background: linear-gradient(180deg, #b9d19f, #9fbd82); }

/* ---- sudoku: the digit in focus, centred marks, the sandwich marks ---- */

/* an empty cell the digit in focus can no longer go in */


/* pencil marks in the middle: one row, shrinking as they multiply */
/* one row, always - nine of them shrink until they fit */
.su .centre {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    gap: 0;
    width: 100%;
    padding: 0 3%;
    line-height: 1;
    white-space: nowrap;
}
/* the same size as a corner mark; only a crowd of them gets smaller */
.su .centre i {
    font: 600 calc(var(--cell) * .33) / 1 "Helvetica Neue", Arial, sans-serif;
    color: var(--ink-soft);
    font-style: normal;
}
.su .centre.n5 i, .su .centre.n6 i { font-size: calc(var(--cell) * .28); }
.su .centre.n7 i { font-size: calc(var(--cell) * .25); }
.su .centre.n8 i { font-size: calc(var(--cell) * .225); }
.su .centre.n9 i { font-size: calc(var(--cell) * .2); }
.su .centre i.is-same { color: #2e63b8; }   /* colour only - see the corner marks above */

/* the sandwich marks: 1|9 in the middle, or a cross for neither. They are
   the cell's ground - pencil marks are written over them, so they step back
   in weight and sit behind. */
/* centred on the cell and taken out of the flow, so the pencil marks sit
   over them rather than beside them: an absolutely positioned child with
   no offsets stays where it fell, which put the cross at the top of the
   cell and left the marks to lay out under it */
.su .pair, .su .cross {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 0;
    opacity: .5;
}
.su .pair {
    font: 700 calc(var(--cell) * .44) / 1 "Helvetica Neue", Arial, sans-serif;
    letter-spacing: .02em;
    color: #2e63b8;
}
.su .cross { width: 62%; height: 62%; }
.su .cross::before, .su .cross::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    width: 100%;
    height: 2px;
    background: rgba(44, 49, 56, .4);
    transform: rotate(45deg);
}
.su .cross::after { transform: rotate(-45deg); }

/* a sum, tappable, and struck out once its line is done */
.su .cell.is-clue:not(.is-corner) { cursor: pointer; }
.su .cell.is-clue:not(.is-corner):hover { color: var(--ink); }

.pad__key--pair .pad__digit { font-size: 1.05rem; letter-spacing: .04em; }
.pad__cross { position: relative; display: inline-block; width: 16px; height: 16px; vertical-align: middle; }
.pad__cross::before, .pad__cross::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    width: 100%;
    height: 2px;
    background: currentColor;
    transform: rotate(45deg);
}
.pad__cross::after { transform: rotate(-45deg); }

/* the digit sets behind a sandwich sum */
.sums { text-align: left; margin: 0 0 16px; }
.sums__row { display: flex; gap: 10px; align-items: baseline; padding: 4px 0; border-top: 1px solid rgba(0, 0, 0, .06); }
.sums__row:first-child { border-top: 0; }
.sums__n { flex: 0 0 64px; font: 500 .7rem/1.4 Oswald, sans-serif; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint); }
.sums__sets { display: flex; flex-wrap: wrap; gap: 4px; }
.sums__set {
    appearance: none;
    border: 0;
    padding: 3px 8px;
    border-radius: 6px;
    background: rgba(0, 0, 0, .06);
    font: 600 .9rem/1.3 "Helvetica Neue", Arial, sans-serif;
    letter-spacing: .12em;
    color: var(--ink);
    cursor: pointer;
    touch-action: manipulation;
}
.sums__set:hover { background: rgba(0, 0, 0, .1); }
.sums__set.is-off { color: var(--ink-faint); background: transparent; text-decoration: line-through; text-decoration-thickness: 2px; opacity: .6; }

/* the miracle toggles, in the New dialog: one icon each */
.miracle { display: flex; flex-wrap: wrap; gap: 6px; }
.tog {
    width: 40px;
    height: 40px;
    padding: 0;
    display: grid;
    place-items: center;
    color: var(--ink-soft);
}
.tog svg {
    width: 26px;
    height: 26px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.tog svg text { font-family: "Helvetica Neue", Arial, sans-serif; }
.tog__glyph { font: 400 1.6rem/1 "Segoe UI Symbol", "Apple Symbols", "Noto Sans Symbols 2", "DejaVu Sans", sans-serif; }
.tog.is-on { color: #fff; }

/* ---- sudoku: the three pens ---- */
.pens { display: inline-flex; gap: 6px; }
.pen {
    appearance: none;
    position: relative;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 9px;
    background: linear-gradient(180deg, #fff, #dfe2e6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25), inset 0 1px 0 #fff;
    color: var(--ink);
    cursor: pointer;
    touch-action: manipulation;
    display: grid;
    place-items: center;
    transition: background .12s, box-shadow .12s;
}
.pen:hover { background: linear-gradient(180deg, #fff, #e9ebee); }
.pen.is-on {
    color: #fff;
    background: linear-gradient(180deg, #4b5058, #2c3138);
    border-color: #1f2226;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18), 0 1px 2px rgba(0, 0, 0, .25);
}
.pen__big { font: 700 1.5rem/1 "Helvetica Neue", Arial, sans-serif; }
.pen__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    width: 32px;
    height: 32px;
}
.pen__grid i { display: grid; place-items: center; font: 600 .55rem/1 "Helvetica Neue", Arial, sans-serif; font-style: normal; }
.pen__mid { font: 600 .7rem/1 "Helvetica Neue", Arial, sans-serif; letter-spacing: .02em; }
.pen:not(.is-on) .pen__grid i, .pen:not(.is-on) .pen__mid { color: var(--ink-soft); }

/* corner marks and centre marks may share a cell: the corners stay in
   their 3 x 3, the centre row sits over them. Everything written in a cell
   is lifted above the thermometers, which are drawn over the cells */
.su .cell { position: relative; }
.su .marks { position: absolute; inset: 0; z-index: 1; }
.su .centre { position: relative; z-index: 1; }

/* the drawn rules - thermometers, arrows, dots and markers - each one SVG
   stretched over the whole grid, in cell units */
.su .thermos, .su .overlay {
    position: absolute;
    inset: var(--cell-gap);
    width: calc(100% - 2 * var(--cell-gap));
    height: calc(100% - 2 * var(--cell-gap));
    z-index: 0;
    pointer-events: none;
    overflow: visible;
}
.su .thermos polyline {
    fill: none;
    stroke: var(--thermo, rgba(44, 49, 56, .17));
    stroke-width: .3;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.su .thermos circle { fill: var(--thermo, rgba(44, 49, 56, .17)); }

/* the arrows: a ring for the circle, a line to the tip, a head. Thinner
   than a thermometer and in ink rather than a grey slab - an arrow is a
   line to read along, not a shape the digits sit in */
.su .arrows polyline, .su .arrows line, .su .arrows circle {
    fill: none;
    stroke: var(--arrow, rgba(44, 49, 56, .5));
    stroke-width: .055;
    stroke-linecap: round;
    stroke-linejoin: round;
}
/* the dots and markers sit on the line between two cells, above it */
.su .pips { z-index: 3; }
.su .pips .dot { stroke: #2c3138; stroke-width: .035; }
.su .pips .dot--white { fill: #fff; }
.su .pips .dot--black { fill: #2c3138; }
.su .pips .xv__pad { fill: var(--paper); stroke: none; }
.su .pips .xv {
    fill: #2c3138;
    font: 700 .4px "Helvetica Neue", Arial, sans-serif;
    text-anchor: middle;
    dominant-baseline: central;
}
.su .cell.is-sel .digit, .su .cell.is-sel .centre { z-index: 1; }

/* several chosen cells are one shape: the ring runs round the outside only
   (paint() marks each cell's outward sides sel-t/r/b/l), and towards a
   chosen neighbour the cell reaches across the 1px gap. The fill is a
   pseudo-element under everything written in the cell. */
.su .cell.is-sel { isolation: isolate; box-shadow: none; overflow: visible; }  /* .cell clips overflow; the fill must reach the gap */
.su .cell.is-sel::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    /* the inner lines (--in-*) are background strips, not borders: a
       border would mitre against the 2px ring at the corners and leave a
       light triangle at every join */
    background:
        linear-gradient(var(--in-t, transparent), var(--in-t, transparent)) left top / 100% var(--cell-gap) no-repeat,
        linear-gradient(var(--in-b, transparent), var(--in-b, transparent)) left bottom / 100% var(--cell-gap) no-repeat,
        linear-gradient(var(--in-l, transparent), var(--in-l, transparent)) left top / var(--cell-gap) 100% no-repeat,
        linear-gradient(var(--in-r, transparent), var(--in-r, transparent)) right top / var(--cell-gap) 100% no-repeat,
        #bfd0ea;
    pointer-events: none;
}
/* towards a chosen neighbour the cell reaches across the gap, and what it
   draws there is the line between the two - still a line, in a blue of
   its own, so the cells inside the shape can be counted */
.su .cell.is-sel:not(.sel-t)::after { top: calc(-1 * var(--cell-gap)); --in-t: #92aad8; }
.su .cell.is-sel:not(.sel-b)::after { bottom: calc(-1 * var(--cell-gap)); --in-b: #92aad8; }
.su .cell.is-sel:not(.sel-l)::after { left: calc(-1 * var(--cell-gap)); --in-l: #92aad8; }
.su .cell.is-sel:not(.sel-r)::after { right: calc(-1 * var(--cell-gap)); --in-r: #92aad8; }
/* the ring itself: one path over the board, snapped to whole pixels */
.su .sel-ring {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 2;
    pointer-events: none;
    overflow: visible;
}
.su .sel-ring path {
    fill: none;
    stroke: #2e63b8;
    stroke-width: 2;
    stroke-linecap: square;
    shape-rendering: crispEdges;
}

/* ---- sudoku: killer cages and several chosen cells ---- */
.su .digit { position: relative; z-index: 1; }
/* A cage edge is inset --cg-in from the cell's edge and --cg-bw thick. A side
   that faces a cage mate has no line and no inset, so the lines on the other
   sides run to the cell's edge and carry on into the neighbour. Corners where
   two lines meet are rounded; an inward corner gets a little square with two
   sides and a rounded corner that turns the line round to meet the neighbour. */
.su { --cg-in: 3px; --cg-bw: 1.5px; --cg-r: 5px; --cg-line: rgba(44, 49, 56, .55); }
.su .cage {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    border: 0 dashed var(--cg-line);
}
.su .cage.cage-t { top: var(--cg-in); border-top-width: var(--cg-bw); }
.su .cage.cage-b { bottom: var(--cg-in); border-bottom-width: var(--cg-bw); }
.su .cage.cage-l { left: var(--cg-in); border-left-width: var(--cg-bw); }
.su .cage.cage-r { right: var(--cg-in); border-right-width: var(--cg-bw); }
.su .cage.cage-t.cage-l { border-top-left-radius: var(--cg-r); }
.su .cage.cage-t.cage-r { border-top-right-radius: var(--cg-r); }
.su .cage.cage-b.cage-l { border-bottom-left-radius: var(--cg-r); }
.su .cage.cage-b.cage-r { border-bottom-right-radius: var(--cg-r); }
.su .cage__in {
    position: absolute;
    width: calc(var(--cg-in) + var(--cg-bw));
    height: calc(var(--cg-in) + var(--cg-bw));
    border: 0 dashed var(--cg-line);
}
.su .cage__in--tr { top: 0; right: var(--cg-bw); border-right-width: var(--cg-bw); border-bottom-width: var(--cg-bw); border-bottom-right-radius: var(--cg-r); }
.su .cage__in--tl { top: 0; left: var(--cg-bw); border-left-width: var(--cg-bw); border-bottom-width: var(--cg-bw); border-bottom-left-radius: var(--cg-r); }
.su .cage__in--br { bottom: 0; right: var(--cg-bw); border-right-width: var(--cg-bw); border-top-width: var(--cg-bw); border-top-right-radius: var(--cg-r); }
.su .cage__in--bl { bottom: 0; left: var(--cg-bw); border-left-width: var(--cg-bw); border-top-width: var(--cg-bw); border-top-left-radius: var(--cg-r); }
/* the sum sits in the corner, under everything; a digit in that cell is
   drawn a little smaller and lower so the two never overlap */
.su .cage__sum {
    position: absolute;
    top: 4px;
    left: 5px;
    z-index: 0;
    font: 700 calc(var(--cell) * .22) / 1 "Helvetica Neue", Arial, sans-serif;
    color: var(--ink-soft);
}
.su .cell.has-sum .digit { font-size: calc(var(--cell) * .44); padding-top: 14%; }
.su .cell.has-sum .centre { padding-top: 22%; }
.su .cell.has-sum .marks i:first-child { visibility: hidden; }
.su--killer .marks { padding-top: 10%; }
.su .cell.is-sel { box-shadow: inset 0 0 0 2px #2e63b8; }

/* the sums calculator */
.calc { text-align: left; margin-bottom: 14px; }
.calc__in { display: flex; gap: 14px; justify-content: center; margin-bottom: 12px; }
.calc .sums { margin-bottom: 0; }
.sums__none { margin: 6px 0 0; color: var(--ink-faint); font-size: .85rem; }
