/* ============================================================================
 * LexiLab Crossword Maker - grid stylesheet
 * ----------------------------------------------------------------------------
 * Extracted verbatim from crossword-maker/index.html on 2026-08-15 (solve-mode
 * spec, phase P1a). Every rule here styles markup that crossword-maker/
 * render.js emits and nothing else - the two files are a pair, and a page that
 * loads one without the other renders an unstyled or unpopulated grid.
 *
 * Editor chrome, panels, buttons, clue lists, toasts and the print page
 * furniture stay in the page's own <style> block: they are the Maker's UI, not
 * the grid, and the play page has no use for them.
 *
 * Palette tokens come from /assets/theme.css, which every page loading this
 * file already links. Nothing here hardcodes a colour except inside
 * @media print, where the sheet is deliberately forced to pure black on white.
 *
 * NOTE FOR THE TEST SUITE: several rules here are pinned by SOURCE-TEXT
 * assertions in scripts/test-crossword-page.mjs rather than by computed style,
 * because jsdom cannot resolve CSS custom properties at all and drops any
 * shorthand that references one. Those assertions read THIS file. If a rule
 * moves out of here, move its assertion with it in the same commit - a check
 * pointed at a file that no longer contains what it is checking passes while
 * asserting nothing, which is a failure mode this repo has already paid for.
 * ========================================================================== */

/* --- Grid ------------------------------------------------------
 * One renderer, three modes (edit / print / solve). Every cell
 * carries data-row, data-col, data-entry-across and data-entry-down
 * so solve mode is additive rather than a restructure. Spec 5.3.
 * -------------------------------------------------------------- */
.cm-grid {
    display: grid;
    gap: 0;
    justify-content: center;
    margin: 0 auto;
    /* Sized to exactly its own tracks (not the wrapper's full width),
     * and given a positioning context - both load-bearing for
     * .cm-frame-overlay below, which is positioned in px relative to
     * this box's own top-left corner via calc(var(--cell) * N). If
     * this box were wider than its tracks (the old default, which is
     * why justify-content:center was needed at all - a narrow puzzle
     * in a wide preview panel), an overlay at left:0 would land at
     * the BOX's edge, not the first CELL's edge, off by however much
     * centering slack existed. width:fit-content collapses that
     * slack to zero, so the overlay's own coordinate math is always
     * correct regardless of puzzle size vs. panel width. */
    width: fit-content;
    position: relative;
}
.cm-cell {
    width: var(--cell, 30px);
    height: var(--cell, 30px);
    position: relative;
}
.cm-cell.cm-filled {
    background: var(--bg-elev);
}
/* Grid lines are NOT cell borders any more (2026-07-22) - they are
 * separate, absolutely-positioned bars computed once for the whole
 * puzzle (see gridLineSegments/renderGrid) and positioned via
 * calc(var(--cell) * N), the exact same technique already proven
 * reliable for .cm-frame-overlay above. Two earlier per-cell-border
 * schemes were tried and both had real, confirmed bugs: drawing
 * every side unconditionally double-thickened crossing cells
 * (overlapping borders at corners where 3-4 cells meet); the
 * adjacency-ownership fix that replaced it (one cell's border-right/
 * bottom, a neighbour's border-top/left omitted in favour of it)
 * removed the doubling but introduced a different one - a real,
 * confirmed 1-2 device-pixel KINK in what should be one straight
 * line, wherever the owning side switched from row to row down a
 * column (or column to column along a row). Root cause: two
 * different DOM elements each computing "my own box edge, border
 * extending inward" independently round to the nearest device pixel
 * on their own, and there is no guarantee two different elements'
 * independent roundings agree - confirmed visually with a 6x-zoom
 * real-Chromium screenshot showing the exact jog, reported
 * identically in Safari, so not a browser-specific quirk either.
 * A single element per line segment has only one rounding decision
 * to make, so it cannot disagree with itself. */
.cm-grid-line {
    position: absolute;
    background: var(--text-muted);
    pointer-events: none;
}
.cm-num {
    position: absolute;
    top: 1px;
    left: 2px;
    font-size: calc(var(--cell, 30px) * 0.28);
    line-height: 1;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.cm-letter {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: Fraunces, Georgia, serif;
    font-size: calc(var(--cell, 30px) * 0.58);
    color: var(--tile-ink);
    padding-top: calc(var(--cell, 30px) * 0.08);
}
.dark .cm-letter { color: var(--text); }

.cm-gridwrap {
    overflow-x: auto;
    padding: 0.5rem 0 1rem;
}

/* --- Grid word selection (phase 5 autofill) ---------------------
   Filler cells are tinted so a teacher can see, at a glance, which
   letters are theirs and which the tool added - and can click any
   tinted cell to bring up Reroll/Delete/Keep for that whole word,
   right next to the grid. Deliberately a background tint rather
   than a border: borders get lost at the smaller cell sizes a big
   puzzle forces (previewCellPx clamps down to 16px). */
.cm-cell-filler {
    background: var(--accent-soft) !important;
    cursor: pointer;
}
.cm-cell-filler:hover { filter: brightness(0.97); }
.dark .cm-cell-filler:hover { filter: brightness(1.15); }

/* One frame around a whole word, not a box per letter - and, as of
   2026-07-21, a single absolutely-positioned rectangle drawn OVER
   the grid rather than border classes distributed across the
   word's own cells. That replaced a per-cell border-delegation
   scheme (each cell owning only the specific side(s) that sit on
   the OUTSIDE of its run, borrowing the base grid's shared-edge
   ownership rule to avoid double borders) which looked right in
   isolation but broke whenever TWO highlighted words crossed at
   once - e.g. hovering an intersection cell highlights both
   crossing entries (applyHoverLink runs for across AND down there,
   deliberately). The ownership rule is correct for the base black
   grid, where any cell may draw a shared edge with no visible
   difference - but for a highlight, delegating one word's own
   edge onto a neighbouring cell that belongs to a DIFFERENT word
   breaks that word's outline, since the neighbour is also busy
   drawing its own crossing word's frame. Reported by Andreas as
   "misaligned... all over the place" on STRAIGHT/PREDATOR crossing
   at A - reproduced in both Chrome and Safari, ruling out a
   browser quirk. A rectangle sidesteps the whole problem: each
   highlighted entry gets its own independent element, sized and
   positioned directly from its row/col/length, so two crossing
   highlights can freely overlap with no interaction at all. */
.cm-frame-overlay {
    position: absolute;
    box-sizing: border-box;
    border: 3px solid var(--accent-orange);
    border-radius: 2px;
    pointer-events: none;
    z-index: 2;
}

/* --- Solve mode (/crossword-play/) ------------------------------
 * Added 2026-08-15 for spec phase P1b. These live HERE rather than in
 * the play page because they are states layered onto .cm-cell and
 * .cm-letter, sized against the same --cell unit - splitting one
 * grid's styling across two files is what this file exists to stop.
 * The Maker ships a few hundred bytes it never uses; that is the
 * cheaper half of the trade.
 *
 * NOTE THE TWO-CLASS SELECTORS. .cm-cell-cursor on its own is (0,1,0)
 * against .cm-cell.cm-filled's (0,2,0) and would silently lose the
 * cascade while every DOM assertion still passed - the exact trap that
 * .cm-cell-filler's !important above exists to dodge, and that P1a
 * found unpinned by any test. Specificity is the better answer than
 * !important, and scripts/test-crossword-play.mjs asserts the shape of
 * these selectors, not just their presence.
 * -------------------------------------------------------------- */

/* The container takes keyboard focus, so the browser's default outline
 * would draw a box around the whole puzzle. The cursor cell below is
 * the real focus indicator; this keeps a visible ring for keyboard
 * users without doubling it up on the grid itself. */
.cm-grid[data-mode="solve"] { outline: none; }
.cm-grid[data-mode="solve"]:focus-visible {
    box-shadow: 0 0 0 3px var(--ring);
    border-radius: 3px;
}
.cm-grid[data-mode="solve"] .cm-cell.cm-filled { cursor: pointer; }

/* The whole current entry is outlined by .cm-frame-overlay above - the
 * same independent rectangle the Maker uses, never per-cell borders.
 * Only the single cursor cell is tinted. */
/* background-COLOR, not the `background` shorthand. The shorthand resets
 * background-image to none, and the wrong-letter slash below is a
 * background-image on a rule of the SAME specificity - so a cell that is
 * both the cursor and wrong would keep its slash only by source order.
 * That is exactly the kind of accident this file's history is made of. */
.cm-cell.cm-cell-cursor { background-color: var(--accent-soft); }

/* A letter Check found wrong. Marked by FORM rather than colour: a
 * diagonal rule across the cell, the way it is done on paper. The
 * palette has no red, --warn collapses onto --accent-orange in dark
 * mode, and the entry outline is already accent-coloured - so colour
 * alone could not carry this signal in both themes. A drawn line can,
 * and it survives colour-blindness for free. Painted as a gradient on
 * the cell's own background so it is one element with one rounding
 * decision, per the rule at the top of this file. */
.cm-cell.cm-cell-wrong {
    background-image: linear-gradient(
        to bottom right,
        transparent calc(50% - 1px),
        var(--warn) calc(50% - 1px),
        var(--warn) calc(50% + 1px),
        transparent calc(50% + 1px)
    );
}
.cm-cell-wrong .cm-letter { color: var(--warn); }

/* A revealed letter stays legible but reads as not-the-solver's, so a
 * teacher looking over a shoulder can tell at a glance. Deliberately
 * not italic: only Fraunces 600 is shipped, so an italic here would be
 * a synthesised slant. */
.cm-cell-revealed .cm-letter { color: var(--accent-soft-ink); }

/* --- Print ---------------------------------------------------------
 * Only the grid's own print rules live here. The sheet's page setup
 * (@page, the forced-light palette override, .cm-print-page spacing,
 * the clue list and the word bank) stays in the page, because it is
 * worksheet furniture rather than grid rendering.
 * ------------------------------------------------------------------ */
@media print {
    .cm-grid { break-inside: avoid; page-break-inside: avoid; }
    .cm-gridwrap { overflow: visible; padding: 0 0 6mm; }

    .cm-cell.cm-filled {
        background: #FFFFFF;
    }
    .cm-grid-line { background: #000000; }
    .cm-num { color: #000000; font-size: calc(var(--cell) * 0.3); }
    .cm-letter { color: #000000; }
}
