/* §10 visual regime: two grid-line weights, three number tiers, and five
   state cues -- every cue pairs its color with a non-color signal (shape,
   underline, or badge text) so nothing here relies on color alone. */

[role="grid"] {
    display: grid;
    /* minmax(0, 1fr), never a bare 1fr: `1fr` is shorthand for
       minmax(auto, 1fr), whose floor is the track's min-content size, so a row
       is free to grow *past* its ninth of the board when a cell's line box is
       taller than that. The board keeps its declared block-size either way, so
       what gives is the cells: they overflow the bottom border and collide
       with the digit bar under it. It takes a tall line box to trigger, which
       is why it showed up on a phone and not on a desktop -- with lang="ko" an
       Android device resolves the digits to a Korean fallback font, whose
       normal line height runs far looser than a Latin one's. Pinning the floor
       to 0 makes the nine tracks divide the board exactly, whatever font the
       device picks.
       The explicit row template is still needed on its own account:
       [role="row"] is display:contents, so the 81 cells are the grid's direct
       items in one implicit auto-flow, and without it a row whose cells are
       all empty sizes to less than a ninth of the board's height. */
    grid-template-columns: repeat(9, minmax(0, 1fr));
    grid-template-rows: repeat(9, minmax(0, 1fr));
    border-top: 3px solid var(--line); /* outer: 3px, matched by the last
        cell's own border-right/-bottom below -- see the border-collapse
        note on .cell for why top/left live here but right/bottom don't. */
    border-left: 3px solid var(--line);
}

[role="row"] {
    display: contents;
}

.cell {
    container-type: inline-size;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-cell);
    /* Border-collapse by hand: a plain `border: 1px solid` on every cell
       would draw two independent 1px borders at every shared edge (one
       from each neighboring cell), which never quite line up at the
       sub-pixel boundaries `1fr` columns round to -- the "crooked lines"
       artifact. Each cell instead owns only its right/bottom edge; the
       grid's own border-top/-left above supplies the missing outer edges,
       so every line on the board is drawn exactly once. */
    border-right: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    outline-offset: -2px;
}

/* Box boundaries: every 3rd column/row carries the same 3px weight as the
   board's outer edge. 2px was the original weight and it did not read --
   against a 1px cell line it is a one-pixel difference, which at a 41px
   cell on a phone is not enough to group the nine cells of a box by eye.
   Tripling the weight is what makes the 3x3 structure legible.
   9 is itself a multiple of 3, so these rules also supply the outer
   right/bottom edges that .cell owns; no separate nth-child(9n) rule is
   needed for them. */
.cell:nth-child(3n) {
    border-right-width: 3px;
}

[role="grid"] > [role="row"]:nth-child(3n) .cell {
    border-bottom-width: 3px;
}

.cell-value {
    font-size: 52cqi;
    /* The cell centers this with flex, so the line box only ever adds height
       the digit does not use. Left at `normal` it is the font's own metrics,
       which vary enough between a Latin and a CJK fallback to change the
       board's layout; pinning it to 1 makes the digit occupy the same space on
       every device. */
    line-height: 1;
    color: var(--fg-body);
}

.cell[data-given="1"] {
    background: var(--bg-cell-given);
}

.cell[data-given="1"] .cell-value {
    font-weight: 600;
    color: var(--fg-given);
}

.cell:not([data-given="1"]) .cell-value {
    font-weight: 400;
    color: var(--fg-user);
}

.cell-candidates {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr); /* explicit, matching the columns,
        rather than relying on the grid's default row-stretch behavior. */
}

.candidate {
    /* text-align alone only centers horizontally; without flex centering,
       each digit sits wherever line-height happens to put it in its 1/9th
       square instead of dead-center. */
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    font-size: max(9px, 24cqi); /* cqi unsupported falls back to the 9px floor above it */
    line-height: 1; /* same reason as .cell-value: nine of these stack inside one cell */
    color: var(--fg-candidate);
    text-align: center;
}

/* Selected: 2px solid outline plus a background change (V4-05: never color alone). */
.cell[data-index][tabindex="0"] {
    outline: 2px solid var(--focus);
    background: var(--bg-cell-selected);
}

/* Peer highlight (same row/column/box as selection). */
.cell[data-peer="1"] {
    background: var(--bg-cell-peer);
}

/* Sticky candidate mode: dashed outline plus a text badge, not color alone. */
.cell[data-candidate-mode="1"] {
    outline: 2px dashed var(--focus);
}

.cell[data-candidate-mode="1"]::before {
    /* Set on :root at boot from the message catalogue -- a pseudo-element's
       text cannot be written by JS, so the language reaches it this way. */
    content: var(--i18n-candidate);
    position: absolute;
    top: 2px;
    left: 2px;
    font-size: 8px;
    color: var(--fg-candidate);
}

/* Conflict: underline plus color, never color alone. */
.cell[data-conflict="1"] .cell-value {
    text-decoration: underline;
    text-decoration-thickness: 2px;
    color: var(--fg-conflict);
    background: var(--bg-cell-conflict);
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition-duration: 0ms !important;
        animation-duration: 0ms !important;
    }
}

.cell,
.candidate {
    transition-duration: 120ms; /* well under the 180ms ceiling; reduced-motion above zeroes it */
}
