/* solver.css — the puzzle screen, #/puzzle/<id> (DESIGN.md §4).
 *
 * Everything is sized from the viewport: solver.js sets --cs (cell size), --grid-w,
 * --board-w (the board column), --bar-w (the clue bar and the Check strip: the
 * column less the print shadow, never narrower than a readable bar, so a small
 * grid is centred under it) and --rail-w on .sv-app and picks a layout in data-layout:
 *   stack (phone / portrait tablet) · side (tablet) · desk (≥1100) · wide (≥1500).
 * The puzzle's colour is --pc (also --accent) on .sv-app.
 */

/* ============================================================== frame */
.sv-app {
  --pc: var(--accent);
  --n: 15; --cs: 40px; --gl: 1px; --gb: 3px;
  --grid-w: 600px; --board-w: 607px; --bar-w: 600px; --rail-w: 0px;
  --sv-pad: 16px; --G: 24px; --pad-h: 0px;
  --clue-fs: clamp(15.5px, .95vw + 2px, 25px);
  --bar-fs: clamp(18px, 1.55vw, 32px);
  flex: 1; min-height: 0; position: relative;
  display: flex; flex-direction: column;
  background: var(--paper);
}
.sv-app .appbar { flex: none; }

.sv {
  flex: 1; min-height: 0; position: relative;
  display: grid; column-gap: var(--G);
  grid-template-columns: var(--board-w) minmax(0, 1fr) minmax(0, 1fr) var(--rail-w);
  grid-template-rows: minmax(0, 1fr);
  padding: var(--sv-pad) var(--sv-pad) calc(var(--sv-pad) + var(--pad-h));
}
.sv-clues { display: contents; }
.sv-col { position: relative; min-width: 0; min-height: 0; }
/* Column rules between board, lists and rail (§2.4). */
.sv-list::before, .sv-rail::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: calc(var(--G) / -2);
  border-left: 1px solid var(--hair); pointer-events: none;
}

.sv-app[data-layout="desk"] .sv { grid-template-columns: var(--board-w) minmax(0, 1fr) minmax(0, 1fr); }
.sv-app[data-layout="desk"] .sv-rail,
.sv-app[data-layout="side"] .sv-rail,
.sv-app[data-layout="stack"] .sv-rail { display: none; }
.sv-app[data-layout="wide"] .sv-cutbtn { display: none; }

/* ============================================================== board */
.sv-board { display: flex; flex-direction: column; gap: 12px; }
.sv-gridwrap { position: relative; flex: none; width: var(--grid-w); }
.sv-app:not([data-layout="stack"]) .sv-gridwrap { margin-left: max(0px, calc((var(--bar-w) - var(--grid-w)) / 2)); }
/* A window too short for the bar, a 20px-cell grid and the Check strip
   (fit() sets .is-tight): the board scrolls — wheel, keys and the cursor all
   reach it — between the clue bar pinned at its top and the Check strip pinned
   at its foot, so the button never leaves the screen (§11). */
.sv-app.is-tight:not([data-layout="stack"]) .sv-board {
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--hair) transparent;
}
.sv-app.is-tight:not([data-layout="stack"]) .sv-bar { position: sticky; top: 0; z-index: 6; }
/* Nothing here changes a size fit() measures, or the state would flip on
   the next fit. */
.sv-app.is-tight:not([data-layout="stack"]) .sv-foot {
  position: sticky; bottom: 0; z-index: 6;
  background: var(--paper); box-shadow: 0 -7px 0 var(--paper);
}

/* ---- the current-clue bar */
.sv-bar {
  position: relative; width: var(--bar-w); flex: none;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas: "dir clue nav" "dir foot foot";
  min-height: calc(2 * 1.3 * var(--bar-fs) + 24px);
  background: var(--card); color: var(--ink);
  border: 2px solid var(--btn-line); box-shadow: 4px 4px 0 var(--btn-line);
  transition: background-color .2s ease;
}
.sv-bar-dir {
  grid-area: dir;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-width: calc(var(--bar-fs) * 3.2); padding: 8px 12px;
  background-color: var(--pc); color: #fff; border-right: 2px solid var(--btn-line);
  background-image: radial-gradient(rgba(0,0,0,.22) 1.1px, transparent 1.5px); background-size: 6px 6px;
}
/* White or ink, whichever reads on the puzzle colour (base.css label rule). */
@supports (color: color(from red srgb-linear r g b)) { .sv-bar-dir { color: color(from var(--pc) var(--on-of)); } }
.sv-bar-num { font: 900 calc(var(--bar-fs) * 1.5)/.9 var(--f-head); font-variant-numeric: lining-nums; }
.sv-bar-way { font: 700 11.5px/1 var(--f-label); letter-spacing: .17em; text-transform: uppercase; }
.sv-bar-clue {
  grid-area: clue; align-self: center; margin: 0; min-width: 0;
  padding: 10px 10px 10px 18px;
  font: 500 var(--bar-fs)/1.3 var(--f-body); color: var(--ink);
  overflow-wrap: break-word;
}
.sv-bar-enum { font: 600 .74em/1 var(--f-grid); color: var(--muted); white-space: nowrap; letter-spacing: .02em; }
.sv-bar-foot { grid-area: foot; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; padding: 0 12px 11px 18px; min-width: 0; }
.sv-bar:not(.has-foot) .sv-bar-foot { display: none; }
.sv-bar-nav { grid-area: nav; display: flex; gap: 7px; padding: 9px 9px 0 0; align-self: start; }
.sv-bar.is-ok { background: var(--ok-bg); }
.sv-bar.is-ok .sv-bar-clue { color: var(--ok-ink); }
.sv-bar.is-rev { background: var(--rev-bg); }
.sv-bar.is-ok .sv-bar-dir { box-shadow: inset 0 -6px 0 var(--ok); }
.sv-bar.is-rev .sv-bar-dir { box-shadow: inset 0 -6px 0 var(--rev); }

/* ---- the grid */
.sv-grid {
  display: grid;
  grid-template-columns: repeat(var(--n), var(--cs));
  grid-auto-rows: var(--cs);
  gap: var(--gl);
  width: var(--grid-w); height: var(--grid-w);
  background: var(--cell-line);
  border: var(--gb) solid var(--cell-line);
  box-shadow: 6px 6px 0 var(--btn-line);
  user-select: none; -webkit-user-select: none;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  outline: none;
}
/* The cursor square is the focus indicator; keep the print shadow rather
   than base.css's generic [tabindex] ring. */
.sv-app .sv-grid[tabindex]:focus-visible { outline: none; border-radius: 0; box-shadow: 6px 6px 0 var(--btn-line); }
.sv-app[data-layout="stack"] .sv-grid[tabindex]:focus-visible { box-shadow: 4px 4px 0 var(--btn-line); }
.sv-row { display: contents; }
.sv-c {
  position: relative; overflow: hidden; cursor: pointer;
  background: var(--cell); color: var(--ink);
  transition: background-color .12s ease;
}
.sv-b { background: var(--block); cursor: default; }
.sv-num {
  position: absolute; z-index: 1; left: calc(var(--cs) * .07); top: calc(var(--cs) * .05);
  font: 600 max(7px, calc(var(--cs) * .26))/1 var(--f-grid); color: var(--ink-2);
  pointer-events: none;
}
.sv-ch {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding-top: calc(var(--cs) * .13);
  font: 700 calc(var(--cs) * .58)/1 var(--f-grid); text-transform: uppercase;
  pointer-events: none;
}
.sv-c.is-word { background: var(--hl); }
.sv-c.is-cur { background: var(--hl-cursor); }
.sv-c.is-cur::before {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  box-shadow: inset 0 0 0 2px var(--ink);
}
/* Solved hue (§4.3): cells take --ok-bg and --ok-ink; inside the current
   word the highlight stays but the letters keep their green. */
.sv-c.is-ok { background: var(--ok-bg); color: var(--ok-ink); }
.sv-c.is-ok.is-word { background: var(--hl); }
.sv-c.is-ok.is-cur { background: var(--hl-cursor); }
.sv-c.is-rev { background: var(--rev-bg); color: var(--rev); }
.sv-c.is-rev.is-word { background: var(--hl); }
.sv-c.is-rev.is-cur { background: var(--hl-cursor); }
.sv-c.is-tri::after {
  content: ""; position: absolute; top: 0; right: 0; pointer-events: none;
  width: calc(var(--cs) * .28); height: calc(var(--cs) * .28);
  background: linear-gradient(225deg, var(--rev) 50%, transparent 50%);
}

/* The ink stamp: letters thump, the square washes green (§4.5). The letters
   are already there, so the stamp only moves them — it never fades them
   (base.css's cc-stamp starts at opacity 0 and, with the per-letter delay,
   would blank a long answer for most of a second). The wash starts from a
   tint light enough for --ok-ink letters to stay readable on it. */
@keyframes sv-stamp { 0% { transform: scale(1.3) rotate(-6deg); } 55% { transform: scale(.94) rotate(1deg); } 100% { transform: none; } }
@keyframes sv-okwash { from { background-color: color-mix(in srgb, var(--ok) 32%, var(--ok-bg)); } }
.sv-c.is-stamp { animation: sv-okwash .9s ease-out both; animation-delay: calc(var(--k, 0) * 55ms); }
.sv-c.is-stamp .sv-ch { animation: sv-stamp .5s cubic-bezier(.2,.9,.3,1.25) forwards; animation-delay: calc(var(--k, 0) * 55ms); }
.sv-c.is-pop .sv-ch { animation: cc-stamp .45s cubic-bezier(.2,.9,.3,1.25) both; }
.sv-c.is-nudge .sv-ch { animation: cc-shake .35s both; }

/* ---- paused: the grid is covered while the clock is stopped */
.sv-paused {
  position: absolute; inset: 0; z-index: 5; display: none; place-items: center;
  background-color: color-mix(in srgb, var(--paper) 88%, transparent);
  background-image: radial-gradient(var(--dot) 1.1px, transparent 1.5px); background-size: 7px 7px;
  border: var(--gb) solid var(--cell-line);
}
.sv-app.is-paused .sv-paused { display: grid; }
.sv-app.is-paused .sv-grid .sv-ch { visibility: hidden; }
.sv-app.is-paused .sv-list, .sv-app.is-paused .sv-bar-clue, .sv-app.is-paused .sv-bar-foot {
  filter: blur(6px); pointer-events: none; user-select: none;
}
.sv-paused-card {
  display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center;
  padding: 22px 30px 26px; background: var(--card); border: 2px solid var(--btn-line);
  box-shadow: 5px 5px 0 var(--btn-line); transform: rotate(-1deg); max-width: 88%;
}
.sv-paused-card .kicker { margin: 0; }
.sv-paused-clock { font: 800 clamp(34px, 4vw, 60px)/1 var(--f-grid); font-variant-numeric: tabular-nums; }
.sv-paused-t { margin: 0 0 8px; font: italic 16.5px/1.4 var(--f-body); color: var(--muted); }

/* ---- the strip under the grid: the Check button, its note, the shortcuts */
.sv-foot {
  width: var(--bar-w); flex: none;
  display: flex; align-items: center; gap: 12px;
  padding-top: 11px; margin-top: 6px; border-top: 1px solid var(--rule);
  font: 600 11.5px/1.2 var(--f-label); letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
}
/* The puzzle's name is shown in review, when the Check button has gone. */
.sv-foot-k { display: none; color: var(--ink); white-space: nowrap; }
.sv-app.is-finished .sv-foot-k { display: inline; }
.sv-foot-k::before { content: ""; display: inline-block; width: 9px; height: 9px; margin-right: 8px; background: var(--pc); vertical-align: 0; }
.sv-foot-t { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sv-foot-t.is-review { display: none; }
.sv-app.is-finished .sv-foot-t.is-review { display: block; }

/* ---- the Check button (§11): one verdict on the whole current answer */
.sv-check { display: flex; align-items: center; gap: 12px; min-width: 0; }
.sv-foot .sv-check { flex: 1; }
.sv-app.is-finished .sv-check { display: none; }
.sv-check-btn { flex: none; }
/* Under a big grid the button grows with the screen (40px at a laptop, 50px
   at 2560); fit() measures the strip, so the grid makes room for it. */
.sv-foot .sv-check-btn { height: clamp(40px, 2vw + 4px, 50px); padding: 0 clamp(16px, 1vw, 24px); font-size: clamp(14px, .5vw + 7px, 17px); }
.sv-check-btn .btn-label { font-variant-numeric: lining-nums; }
.sv-check-btn.is-busy { cursor: progress; }
@keyframes sv-busy { from { opacity: 1; } to { opacity: .35; } }
.sv-check-btn.is-busy .icon { animation: sv-busy .5s ease-in-out infinite alternate; }
.sv-check-why { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; }
/* Never an ellipsis: the note wraps to a second line where the strip is
   narrow (two lines of it are shorter than the button, so the strip keeps
   its height). A disabled reason is one word ("Solved"); the sentence is in
   the button's title and read out. */
.sv-check-why > span {
  min-width: 0; overflow: hidden; overflow-wrap: break-word;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
}
.sv-check-why > .sv-check-short { flex: none; white-space: nowrap; }
.sv-check-why .icon { width: 15px; height: 15px; flex: none; stroke-width: 2.6; }
.sv-check.is-blocked .sv-check-why { color: var(--ink-2); }
.sv-check.is-ok .sv-check-why .icon { color: var(--ok); stroke-width: 3; }
.sv-check.is-rev .sv-check-why .icon { color: var(--rev); }
.sv-foot-keys {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  margin: 0; padding: 4px 6px; border: 0; border-radius: 6px; background: none; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: var(--ink-2);
}
.sv-foot-keys:hover { background: var(--paper-2); color: var(--ink); }
.sv-foot-keys .icon { width: 16px; height: 16px; }
.sv-foot-keys kbd { font-size: 11px; padding: 0 5px; min-width: 1.5em; }
/* With the on-screen pad the caption (it points at keyboard shortcuts) goes;
   fit() gives its room to the grid. */
.sv-app.has-pad .sv-foot { display: none; }
/* The stack layout on a keyboard (a window under 700px wide, or portrait):
   the clue bar is pinned at the top of the scrolling pane, and the Check
   strip is pinned along the foot of the window, where the pad is on a phone
   (§11: always in view, never scrolled away with the grid). fit() reads its
   height as the pad's (--pad-h): the grid is sized above it and the pane
   pads its end by it, so every square and every clue scrolls clear of it. */
.sv-app[data-layout="stack"]:not(.has-pad) .sv-foot {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  width: auto; margin: 0; border-top: 2px solid var(--rule);
  padding: 9px max(var(--sv-pad), calc((100% - var(--bar-w)) / 2)) calc(9px + env(safe-area-inset-bottom, 0px));
  background: var(--paper-2);
}

/* ============================================================ lists */
.sv-list { display: flex; flex-direction: column; }
.sv-list-head {
  flex: none; display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  border-top: 4px solid var(--rule); border-bottom: 1px solid var(--rule);
  padding: 10px 2px 9px;
}
.sv-list-title {
  margin: 0; display: flex; align-items: center; gap: 10px;
  font: 700 clamp(16px, 1.12vw, 21px)/1 var(--f-label); letter-spacing: .15em; text-transform: uppercase;
}
.sv-list-title::before { content: ""; width: 14px; height: 14px; flex: none; background: var(--pc); }
.sv-list-count { font: 600 11.5px/1 var(--f-label); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.sv-ol {
  list-style: none; margin: 0; padding: 0 0 18px;
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--hair) transparent;
}

.sv-cl { position: relative; border-bottom: 1px solid var(--hair); transition: background-color .15s ease; }
.sv-cl-main {
  display: grid; grid-template-columns: 1.75em minmax(0, 1fr); column-gap: .55em; align-items: baseline;
  width: 100%; margin: 0; padding: 8px 42px 8px 4px;
  border: 0; background: none; text-align: left; cursor: pointer;
  font: 400 var(--clue-fs)/1.4 var(--f-body); color: var(--ink);
}
.sv-cl-main:focus-visible { outline: 3px solid var(--c-yellow); outline-offset: -3px; }
.sv-cl-n { font: 700 .86em/1 var(--f-grid); text-align: right; font-variant-numeric: tabular-nums; }
.sv-cl-enum { font: 600 .78em/1 var(--f-grid); color: var(--muted); white-space: nowrap; }
.sv-cl-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
  padding: 0 10px 10px calc(4px + 2.3em);
  font-size: var(--clue-fs);
}
.sv-cl .sv-cl-menu { position: absolute; top: 5px; right: 4px; }

/* The ⋯ trigger rests quietly and becomes a proper button on the row in use. */
.sv-cl .sv-cl-menu:not(.is-open) { background: transparent; border-color: transparent; box-shadow: none; color: var(--muted); }
.sv-cl:hover .sv-cl-menu:not(.is-open),
.sv-cl.is-active .sv-cl-menu:not(.is-open),
.sv-cl .sv-cl-menu:focus-visible,
body.touch .sv-cl .sv-cl-menu:not(.is-open) {
  background: var(--card); border-color: var(--btn-line); box-shadow: 2px 2px 0 var(--btn-line); color: var(--ink);
}

.sv-cl:hover { background: color-mix(in srgb, var(--paper-2) 85%, transparent); }
.sv-cl.is-cross { background: color-mix(in srgb, var(--hl) 34%, transparent); }
.sv-cl.is-active { background: var(--hl); box-shadow: inset 5px 0 0 var(--pc); }
.sv-cl.is-active:not(.is-ok):not(.is-rev) .sv-cl-enum,
.sv-cl.is-active:not(.is-ok):not(.is-rev) .sv-cl-n { color: var(--ink); }
/* Solved (§4.3): --ok-bg row, ✓ in --ok, text in --ok-ink, answer after the clue. */
.sv-cl.is-ok { background: var(--ok-bg); }
.sv-cl.is-ok .sv-cl-main { color: var(--ok-ink); }
.sv-cl.is-ok .sv-cl-n { color: var(--ok); }
.sv-cl.is-rev { background: var(--rev-bg); }
.sv-cl.is-rev .sv-cl-n { color: var(--rev); }
.sv-cl.is-ok.is-active, .sv-cl.is-rev.is-active { box-shadow: inset 5px 0 0 var(--pc), inset 0 0 0 2px var(--hl-cursor); }
.sv-cl.is-ok.is-cross, .sv-cl.is-rev.is-cross { box-shadow: inset 0 0 0 1px var(--hl-cursor); }

/* ---- chips in lists and the bar */
.sv-hint, .sv-ans {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; max-width: 100%;
  padding: 0 11px 0 8px; border-radius: 999px; border: 1.5px solid var(--btn-line);
  font: 600 11.5px/1 var(--f-label); letter-spacing: .1em; text-transform: uppercase; white-space: nowrap;
  color: var(--ink);
}
.sv-hint { background: color-mix(in srgb, var(--c-yellow) 42%, var(--card)); }
.sv-hint-def { background: color-mix(in srgb, var(--c-orange) 20%, var(--card)); }
.sv-hint .icon, .sv-ans .icon { width: 14px; height: 14px; flex: none; }
.sv-ans { background: var(--card); cursor: default; }
button.sv-ans { cursor: pointer; transition: transform .08s ease, box-shadow .08s ease; box-shadow: 2px 2px 0 var(--btn-line); }
button.sv-ans:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--btn-line); }
button.sv-ans:active { transform: translate(1px, 1px); box-shadow: 0 0 0 var(--btn-line); }
.sv-ans-ok { border-color: var(--ok); color: var(--ok-ink); }
.sv-ans-ok .icon { color: var(--ok); stroke-width: 3; }
.sv-ans-rev { border-color: var(--rev); color: var(--rev); }
.sv-ans-k { font-size: 10.5px; letter-spacing: .14em; opacity: .9; }
.sv-ans-w { font: 800 12.5px/1 var(--f-grid); letter-spacing: .13em; overflow: hidden; text-overflow: ellipsis; }
/* In a list column an answer chip wraps (FREE / SPIRIT) instead of losing
   letters to an ellipsis: the answer shown after the clue must be readable. */
.sv-cl-foot .sv-ans, .sv-cl-foot .sv-hint { height: auto; min-height: 26px; padding-top: 5px; padding-bottom: 5px; white-space: normal; }
.sv-cl-foot .sv-ans { flex-wrap: wrap; row-gap: 3px; border-radius: 14px; }
.sv-cl-foot .sv-ans-w { overflow: visible; white-space: normal; line-height: 1.2; overflow-wrap: anywhere; }

/* ---- feedback */
@keyframes sv-flash { from { outline: 3px solid var(--c-yellow); outline-offset: 1px; } to { outline: 3px solid transparent; outline-offset: 1px; } }
.sv-cl.is-flash, .sv-bar.is-flash { animation: sv-flash 1.2s ease-out; }
.sv-bar .def, .sv-cl .def { text-decoration-color: var(--c-orange); }

/* ============================================================ rail */
.sv-rail { display: flex; flex-direction: column; }
.sv-rail-list {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 28px;
  padding: 24px 14px 30px 6px;
  scrollbar-width: thin; scrollbar-color: var(--hair) transparent;
}
.sv-rail-list > .cutting:nth-child(even), .sv-drawer-list > .cutting:nth-child(even) { transform: rotate(.5deg); }
.sv-cut-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px;
  padding: 22px 16px 24px; border: 1.5px dashed var(--hair);
  background: color-mix(in srgb, var(--card) 60%, transparent);
}
.sv-cut-empty-art { width: 116px; aspect-ratio: 1; background: var(--plate-bg); border: 1px solid var(--rule); overflow: hidden; margin-bottom: 6px; }
.sv-cut-empty-art img { display: block; width: 100%; height: 100%; object-fit: cover; }
.sv-cut-empty-t { margin: 0; font: 800 21px/1.2 var(--f-head); }
.sv-cut-empty-b { margin: 0; font: italic 16px/1.45 var(--f-body); color: var(--muted); max-width: 34ch; }

/* ============================================================ app bar */
/* The save pill (CC.saveStatus, base.css): whether the letters are on the
   server yet. The agreed texts are long ("Offline — kept on this device"),
   and beside the clock and the Cuttings button they would push the puzzle's
   title off a laptop's app bar. So below the desk layout the pill keeps its
   icon and its colour, and a failure gets a word or two of its own; the full
   text stays in its title and its live region. On a phone there is room for
   the icon alone, and it gives even that back to the title while all is
   well: it shows only when something has not been sent. A finished puzzle
   has nothing left to save. */
.sv-save .savestatus-text { white-space: nowrap; }
.sv-app.is-finished .sv-save { display: none; }
@media (max-width: 1099px) {
  .sv-app .sv-save { padding: 0 8px; }
  .sv-app .sv-save .savestatus-text { display: none; }
  .sv-app .sv-save::after { white-space: nowrap; }
  .sv-app .sv-save[data-state="retry"]::after { content: "Not sent"; }
  .sv-app .sv-save[data-state="offline"]::after { content: "Offline"; }
  .sv-app .sv-save[data-state="error"]::after { content: "Not saved"; }
}
@media (max-width: 699px) {
  .sv-app .sv-save::after { content: none !important; }
  .sv-app .sv-save[data-state="saved"], .sv-app .sv-save[data-state="saving"] {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}
/* A guest's sign-in chip (CC.signInChip; solver.js guestChrome). Beside the
   save pill, the clock and the Cuttings button the whole sentence fits only
   a wide desk; below that it is "Sign in" (as base.css has it on any phone),
   and on a phone it gives up its icon too, for the title's sake. It never
   wraps: fit() sizes the grid from the app bar's height. */
.sv-app .signin-chip { white-space: nowrap; flex: none; }
@media (max-width: 1299px) {
  .sv-app .signin-chip .btn-label { display: none; }
  .sv-app .signin-chip .signin-chip-short { display: inline; }
}
@media (max-width: 699px) {
  .sv-app .signin-chip { padding: 0 9px; }
  .sv-app .signin-chip .icon { display: none; }
}
/* A guest's Cuttings drawer can only ever be empty (nothing is confirmed for
   a guest), and below a wide desk the bar has room for the chip or that
   button beside the title, not both — so the chip has its place. */
@media (max-width: 1299px) {
  .sv-app.is-guest .sv-cutbtn { display: none; }
}
.sv-prog {
  display: grid; grid-template-columns: auto auto; align-items: baseline; column-gap: 8px;
  padding-right: 4px;
}
.sv-prog-k { font: 600 10.5px/1 var(--f-label); letter-spacing: .16em; text-transform: uppercase; color: var(--muted); }
.sv-prog-v { font: 800 18px/1 var(--f-grid); font-variant-numeric: tabular-nums; text-align: right; }
.sv-prog-bar {
  grid-column: 1 / -1; display: block; width: 92px; height: 7px; margin-top: 5px;
  border: 1.5px solid var(--btn-line); border-radius: 99px; overflow: hidden; background: var(--paper-3);
}
.sv-prog-fill { display: block; height: 100%; width: var(--pct, 0%); background: var(--pc); transition: width .3s ease; }

.sv-clock {
  display: flex; align-items: stretch; height: 40px; overflow: hidden;
  border: 2px solid var(--btn-line); border-radius: 10px; background: var(--card);
  box-shadow: 2px 2px 0 var(--btn-line);
}
.sv-clock-face { display: flex; flex-direction: column; justify-content: center; padding: 0 10px 0 12px; }
.sv-clock-k { font: 600 9.5px/1 var(--f-label); letter-spacing: .17em; text-transform: uppercase; color: var(--muted); margin-bottom: 3px; }
.sv-clock-digits { font: 700 19px/1 var(--f-grid); font-variant-numeric: tabular-nums; min-width: 3.05em; }
.sv-clock-btn {
  flex: none; width: 38px; margin: 0; padding: 0; display: grid; place-items: center; cursor: pointer;
  border: 0; border-left: 2px solid var(--btn-line); background: var(--pc); color: #fff;
}
.sv-clock-btn:hover { filter: brightness(1.08); }
.sv-clock-btn:focus-visible { outline: 3px solid var(--c-yellow); outline-offset: -4px; }
.sv-clock-btn .icon { width: 17px; height: 17px; }
.sv-clock.is-paused { background: color-mix(in srgb, var(--hl) 55%, var(--card)); }
.sv-clock.is-paused .sv-clock-digits { color: var(--muted); }
.sv-clock.is-hidden-digits .sv-clock-digits { display: none; }
.sv-clock.is-hidden-digits .sv-clock-k { margin: 0; }
.sv-clock.is-done { border-color: var(--btn-line); }
.sv-clock.is-done .sv-clock-k { color: var(--ok); }
.sv-app.is-finished .sv-clock.is-done .sv-clock-face { background: var(--ok-bg); }

/* ============================================================ the pad */
.sv-pad {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: grid; gap: 7px;
  padding: 8px 5px calc(9px + env(safe-area-inset-bottom, 0px));
  background: var(--paper-2); border-top: 2px solid var(--rule);
}
.sv-pad-row { display: flex; justify-content: center; gap: 5px; }
.sv-key {
  flex: 1 1 0; min-width: 0; max-width: 50px; height: 46px; margin: 0; padding: 0;
  display: grid; place-items: center;
  border: 1.5px solid var(--btn-line); border-radius: 8px;
  background: var(--card); color: var(--ink); box-shadow: 0 2.5px 0 var(--btn-line);
  font: 700 18px/1 var(--f-grid);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent; user-select: none; -webkit-user-select: none;
  cursor: pointer;
}
.sv-key:active { transform: translateY(2px); box-shadow: 0 .5px 0 var(--btn-line); background: var(--hl); }
.sv-key-wide { flex: 1.55 1 0; max-width: 76px; font-size: 20px; }
.sv-key-dir { background: var(--pc); color: #fff; }
.sv-key-back { background: var(--btn-ink-bg); color: var(--btn-ink-fg); }
.sv-key .icon { width: 21px; height: 21px; }
/* The Check button rides on top of the keys: between the grid and the pad,
   never behind it. The note beside it may take two short lines. */
.sv-pad > .sv-check { width: 100%; max-width: 560px; margin: 0 auto; padding: 0 2px 1px; gap: 11px; }
.sv-pad .sv-check-btn { height: 40px; padding: 0 15px; }
.sv-pad .sv-check-why { font: italic 14px/1.2 var(--f-body); letter-spacing: 0; text-transform: none; color: var(--muted); }
.sv-pad .sv-check-why > span {
  white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
}
.sv-pad .sv-check.is-blocked .sv-check-why { font-style: normal; color: var(--ink-2); }
/* A tablet's pad is far wider than its keys: the Check button stands to the
   left of them (under the grid) and costs the grid no height at all. */
@media (min-width: 960px) {
  .sv-pad { grid-template-columns: minmax(0, 1fr) minmax(0, 560px) minmax(0, 1fr); column-gap: 16px; }
  .sv-pad > .sv-pad-row { grid-column: 2; }
  .sv-pad > .sv-check {
    grid-column: 1; grid-row: 1 / span 3; align-self: center;
    flex-direction: column; align-items: flex-end; gap: 8px;
    max-width: none; margin: 0; padding: 0 0 0 8px; text-align: right;
  }
  .sv-pad > .sv-check .sv-check-why { flex: none; max-width: 100%; justify-content: flex-end; }
}

/* ============================================================ pop-up cutting */
.sv-pop {
  position: fixed; z-index: 60; right: 18px; bottom: calc(18px + var(--pad-h));
  width: min(400px, calc(100vw - 20px)); max-height: min(72vh, 700px);
  overflow-y: auto; overscroll-behavior: contain;
  padding: 18px 14px 14px 10px;
  transition: opacity .2s ease, transform .2s ease;
  scrollbar-width: thin;
}
.sv-pop.is-out { opacity: 0; transform: translateY(12px); }

/* The strip (phones, short screens): laid over the app bar, so it covers no
   square, not the clue bar and not the pad. One line: what, the word, its
   gloss; the whole strip opens the full cutting. */
.sv-pop.is-strip {
  top: 0; bottom: auto; right: 0; left: auto;
  width: min(640px, 100vw); max-height: none; height: 52px;   /* the app bar, above its colour stripe */
  padding: 0; overflow: hidden;
}
.sv-pop.is-strip.is-out { transform: translateY(-10px); }
@keyframes sv-strip-in { from { transform: translateY(-100%); } }
.sv-strip {
  --cut: var(--ok);
  position: relative; height: 100%;
  display: flex; align-items: stretch;
  background: var(--card); color: var(--ink);
  border-bottom: 1.5px solid var(--btn-line);
  animation: sv-strip-in .28s cubic-bezier(.2,.9,.3,1.05) both;
}
@media (min-width: 641px) { .sv-strip { border-left: 1.5px solid var(--btn-line); } }
.sv-strip.cutting-revealed { --cut: var(--rev); }
.sv-strip-mark {
  flex: none; width: 38px; display: grid; place-items: center;
  background: var(--cut); color: var(--card); border-right: 1.5px solid var(--btn-line);
}
.sv-strip-mark .icon { width: 19px; height: 19px; stroke-width: 3; }
.sv-strip-open {
  flex: 1; min-width: 0; margin: 0; padding: 0 6px 0 10px;
  display: flex; align-items: center; gap: 8px;
  border: 0; background: none; color: inherit; text-align: left; cursor: pointer; font: inherit;
  -webkit-tap-highlight-color: transparent;
}
.sv-strip-open:focus-visible { outline: 3px solid var(--c-yellow); outline-offset: -3px; }
.sv-strip-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.sv-strip-k { font: 700 10px/1 var(--f-label); letter-spacing: .15em; text-transform: uppercase; color: var(--cut); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sv-strip-line { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font: italic 14.5px/1.2 var(--f-body); color: var(--ink-2); }
.sv-strip-w { font: 800 18px/1.1 var(--f-head); font-style: normal; letter-spacing: .03em; color: var(--ink); }
.sv-strip-e { font: 600 11.5px/1 var(--f-grid); font-style: normal; color: var(--muted); }
.sv-strip-read {
  flex: none; display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 10px;
  border: 1.5px solid var(--btn-line); border-radius: 8px; box-shadow: 2px 2px 0 var(--btn-line);
  background: var(--cut); color: var(--card);
  font: 600 12px/1 var(--f-label); letter-spacing: .08em; text-transform: uppercase;
}
.sv-strip-read .icon { width: 15px; height: 15px; }
.sv-strip-x {
  flex: none; width: 40px; margin: 0; padding: 0; display: grid; place-items: center;
  border: 0; border-left: 1px solid var(--hair); background: none; color: var(--ink); cursor: pointer;
}
.sv-strip-x:focus-visible { outline: 3px solid var(--c-yellow); outline-offset: -3px; }
.sv-strip-x .icon { width: 17px; height: 17px; }
@media (max-width: 380px) { .sv-strip-read span { display: none; } .sv-strip-read { padding: 0 7px; } }
.sv-slip { padding-bottom: 14px; }
.sv-slip .cutting-kicker { margin-right: 40px; }
.sv-slip-body { display: flex; gap: 14px; align-items: flex-start; }
.sv-slip-img {
  flex: none; width: 96px; height: 96px; object-fit: cover; margin-top: 4px;
  border: 1px solid var(--rule); background: var(--paper-2);
}
.sv-slip-text { min-width: 0; flex: 1; }
.sv-slip .cutting-gloss { margin: 6px 0 0; font-size: 17px; }
.sv-slip-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--hair); }
.sv-slip-note { font: italic 14.5px/1.3 var(--f-body); color: var(--muted); }

/* ============================================================ toasts */
/* With the key pad, a toast that above the pad would lie over the grid
   (solver.js placeToast) drops in from the top instead, over the app bar as
   a cutting strip does: a little more compact, so two lines clear the pinned
   clue bar under the app bar. */
body.sv-toast-top #toasts,
body.sv-toast-top #toasts[popover] { top: calc(3px + env(safe-area-inset-top, 0px)); bottom: auto; flex-direction: column-reverse; }
body.sv-toast-top #toasts .toast { gap: 10px; padding: 5px 6px 5px 7px; font-size: 15px; line-height: 1.25; transform: translateY(-12px); }
body.sv-toast-top #toasts .toast.is-in { transform: none; }
body.sv-toast-top #toasts .toast.is-out { transform: translateY(-6px); }
body.sv-toast-top #toasts .toast-icon { width: 26px; height: 26px; }
/* Up there it lies over the clock, pause, Cuttings and ⋯: taps go through
   to them, and only the × (at the toast's left end, over the title, clear
   of those controls) takes one. Where the bar has room left of its controls
   (solver.js placeToast: .sv-toast-lean) the toast keeps to that room. */
body.sv-toast-top #toasts .toast { pointer-events: none; }
body.sv-toast-top #toasts .toast-x { pointer-events: auto; order: -1; margin-right: -4px; }
body.sv-toast-lean #toasts,
body.sv-toast-lean #toasts[popover] { left: 8px; right: auto; transform: none; width: var(--toast-lean-w, 300px); align-items: flex-start; }
body.sv-toast-lean #toasts .toast-x { order: 0; margin-right: 0; }
/* Beside the clue lists (solver.js toastLane): centred over the lists, clear
   of the board column and its Check button. */
body.sv-toast-side #toasts,
body.sv-toast-side #toasts[popover] { left: var(--toast-left, 50%); width: min(560px, var(--toast-w, 560px)); }

/* ============================================================ dialogs */
/* The cutting opened on purpose (reveal, "Show the cutting"): the clipping
   itself is the dialog. */
.dialog.sv-cutmodal { max-width: 620px; background: transparent; border: 0; box-shadow: none; }
.sv-cutmodal .dialog-stripe, .sv-cutmodal .dialog-head { display: none; }
.sv-cutmodal .dialog-body { padding: 20px 18px 24px 14px; }
.sv-cutmodal .cutting { box-shadow: 6px 6px 0 var(--btn-line); }
.sv-cutmodal .plate-photo img { max-height: min(360px, 34vh); }
/* From 1100px the clipping is laid out in two columns — words on the left,
   the picture and the fact on the right — so the whole card, parse included,
   fits a 900px-tall screen instead of running off the bottom. */
@media (min-width: 1100px) {
  .dialog.sv-cutmodal:has(.sv-cut-side:not(:empty)) { max-width: min(980px, calc(100vw - 64px)); }
  .sv-cut-cols { display: flex; align-items: flex-start; gap: clamp(20px, 2vw, 32px); }
  .sv-cut-main { flex: 1 1 0; min-width: 0; }
  .sv-cut-side { flex: 0 0 44%; min-width: 0; padding-top: 34px; }   /* clear of the card × */
  .sv-cut-side:empty { display: none; }
  .sv-cut-side .cutting-plate { margin-top: 0; }
  .sv-cut-side .plate-photo img { max-height: min(420px, 46vh); }
}

/* The cuttings drawer, from the right. */
.dialog.sv-drawer {
  margin: 0 0 0 auto; width: min(470px, 100vw); max-width: none;
  height: 100dvh; max-height: 100dvh; border-radius: 0; border-width: 0 0 0 2px;
  box-shadow: -6px 0 0 var(--btn-line), var(--pop-shadow);
  transform: translateX(48px);
}
.dialog.sv-drawer.is-open { transform: none; }
.sv-drawer .dialog-inner { height: 100%; max-height: 100dvh; }
.sv-drawer .dialog-body { flex: 1; min-height: 0; padding: 8px 18px 28px; }
.sv-drawer-list { display: flex; flex-direction: column; gap: 28px; padding: 16px 8px 12px 2px; }

.sv-keys-table th { width: 42%; white-space: nowrap; font: 600 14px/1.3 var(--f-grid); text-transform: none; letter-spacing: 0; color: var(--ink); border-bottom: 1px solid var(--hair); }
.sv-keys-table td { font: 16.5px/1.4 var(--f-body); }
.sv-keys-table kbd { margin: 2px 0; }
.sv-keys-note { margin: 16px 0 0; font: italic 16.5px/1.45 var(--f-body); color: var(--muted); }

/* ============================================================ Extra! */
dialog.sv-extra {
  position: fixed; inset: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; overflow: hidden;
  background: var(--paper); color: var(--ink);
  opacity: 0; transition: opacity .25s ease;
}
dialog.sv-extra.is-open { opacity: 1; }
dialog.sv-extra::backdrop { background: rgba(0,0,0,.45); }
.sv-extra-in {
  height: 100%; overflow-y: auto;
  display: grid; place-items: center;
  padding: clamp(14px, 4vw, 64px);
  background-color: var(--paper);
  background-image: repeating-conic-gradient(from 0deg at 50% 38%,
    color-mix(in srgb, var(--pc) 14%, transparent) 0deg 6deg, transparent 6deg 12deg);
}
@keyframes sv-sheet-in { from { opacity: 0; transform: translateY(26px) rotate(-1.4deg) scale(.97); } to { opacity: 1; transform: rotate(-.4deg); } }
.sv-extra-sheet {
  width: min(1400px, 100%);
  background: var(--card); border: 2px solid var(--btn-line); box-shadow: 10px 10px 0 var(--btn-line);
  padding: clamp(18px, 2.6vw, 46px) clamp(18px, 3vw, 54px) clamp(22px, 3vw, 50px);
  transform: rotate(-.4deg);
  animation: sv-sheet-in .55s cubic-bezier(.2,.9,.3,1.1) both;
}
.sv-extra-ed {
  display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0;
  border-top: 3px solid var(--rule); border-bottom: 1px solid var(--rule); padding: 7px 2px;
  font: 600 12.5px/1.2 var(--f-label); letter-spacing: .17em; text-transform: uppercase; color: var(--ink-2);
}
.sv-extra-mast {
  margin: clamp(4px, 1vw, 12px) 0 clamp(6px, 1vw, 14px); text-align: center;
  font: 400 clamp(64px, 10.5vw, 170px)/.92 var(--f-mast); color: var(--pc);
  text-shadow: 4px 4px 0 color-mix(in srgb, var(--btn-line) 22%, transparent);
}
.sv-extra.is-revealed .sv-extra-mast { color: var(--rev); }
.sv-extra-body {
  display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); gap: clamp(20px, 3.4vw, 60px); align-items: center;
  border-top: 4px solid var(--rule); padding-top: clamp(16px, 2vw, 30px);
}
.sv-extra-text .kicker { margin-bottom: 6px; }
.sv-extra-head { margin: 4px 0 14px; font: 900 clamp(40px, 5.2vw, 96px)/.98 var(--f-head); letter-spacing: -.015em; text-wrap: balance; }
.sv-extra-deck { margin: 0 0 20px; max-width: 46ch; }
.sv-extra-stats {
  display: flex; flex-wrap: wrap; margin: 0 0 26px;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
}
.sv-extra-stat { padding: 12px clamp(14px, 1.8vw, 30px) 12px 0; margin-right: clamp(14px, 1.8vw, 30px); border-right: 1px solid var(--hair); }
.sv-extra-stat:last-child { border-right: 0; margin-right: 0; }
.sv-extra-stat dt { font: 600 11.5px/1.2 var(--f-label); letter-spacing: .15em; text-transform: uppercase; color: var(--muted); }
.sv-extra-stat dd { margin: 5px 0 0; font: 900 clamp(26px, 2.5vw, 44px)/1 var(--f-head); font-variant-numeric: lining-nums tabular-nums; }
.sv-extra-actions { gap: 14px 16px; }
/* The three actions sit on one row whenever the text column can hold them
   (a lone "Front page" on a second row looks unfinished); a long "Next: …"
   label gives way with an ellipsis rather than pushing the row over. */
.sv-extra-text { container-type: inline-size; }
@container (min-width: 600px) {
  .sv-extra-actions { flex-wrap: nowrap; gap: 14px 12px; }
  .sv-extra-actions .btn { flex: 0 1 auto; min-width: 0; }
  .sv-extra-actions .btn-ghost { flex: none; height: 44px; padding: 0 16px; font-size: 14px; }
  .sv-extra-actions .btn .btn-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}
/* A little narrower (1100–1250px screens): "Front page" keeps its house icon
   and its accessible name, and the row still holds. */
@container (min-width: 500px) and (max-width: 599px) {
  .sv-extra-actions { flex-wrap: nowrap; gap: 14px 12px; }
  .sv-extra-actions .btn-ghost { flex: none; width: 52px; padding: 0; }
  .sv-extra-actions .btn-ghost .btn-label {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}
.sv-extra-art { position: relative; margin: 0; padding: 18px 18px 0 0; }
.sv-extra-plate {
  aspect-ratio: 3 / 2; overflow: hidden;
  background-color: var(--plate-bg);
  background-image: radial-gradient(rgba(27,25,20,.14) 1.1px, transparent 1.5px); background-size: 7px 7px;
  border: 2px solid var(--btn-line); box-shadow: 7px 7px 0 var(--btn-line); transform: rotate(1.4deg);
}
.sv-extra-plate img { display: block; width: 100%; height: 100%; object-fit: cover; }
.sv-extra-art .plate-caption { margin-top: 14px; }

/* The clean-solve rosette. */
.sv-rosette {
  position: absolute; right: -6px; top: -14px; z-index: 2;
  width: clamp(108px, 9vw, 142px); aspect-ratio: 1; border-radius: 50%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: repeating-conic-gradient(#f5b700 0deg 10deg, #e3a100 10deg 20deg);
  border: 3px solid #16140f; box-shadow: 4px 4px 0 #16140f;
  color: #16140f; transform: rotate(-12deg);
  animation: cc-stamp .6s .35s cubic-bezier(.2,.9,.3,1.3) both;
}
.sv-rosette::before {
  content: ""; position: absolute; inset: 13%; border-radius: 50%;
  background: #fff6d6; border: 2px solid #16140f;
}
.sv-rosette::after {
  content: ""; position: absolute; left: 22%; right: 22%; top: 82%; height: 46%; z-index: -1;
  background: linear-gradient(90deg, #d7263d 0 46%, transparent 46% 54%, #d7263d 54%);
  clip-path: polygon(0 0, 46% 0, 46% 100%, 23% 82%, 0 100%, 54% 0, 100% 0, 100% 100%, 77% 82%, 54% 100%);
  border-bottom: 2px solid #16140f;
}
.sv-rosette-k, .sv-rosette-v { position: relative; z-index: 1; }
.sv-rosette-k { font: 700 13px/1 var(--f-label); letter-spacing: .2em; text-transform: uppercase; }
.sv-rosette-v { font: 900 italic clamp(22px, 2vw, 30px)/1 var(--f-head); }

/* ============================================================ layouts */
/* side: tablet landscape — board beside one scrolling column of clues. */
.sv-app[data-layout="side"] .sv { grid-template-columns: var(--board-w) minmax(0, 1fr); }
.sv-app[data-layout="side"] .sv-clues {
  display: flex; flex-direction: column; gap: 26px; min-height: 0; overflow-y: auto; position: relative;
  scrollbar-width: thin; scrollbar-color: var(--hair) transparent;
}
.sv-app[data-layout="side"] .sv-list::before { display: none; }
.sv-app[data-layout="side"] .sv-list { flex: none; min-height: auto; }
.sv-app[data-layout="side"] .sv-ol { overflow: visible; flex: none; }
.sv-app[data-layout="side"] .sv-clues { border-left: 1px solid var(--hair); padding-left: calc(var(--G) / 2); margin-left: calc(var(--G) / -2); }

/* stack: phones and portrait tablets — bar, grid, then the lists below. */
.sv-app[data-layout="stack"] {
  --bar-fs: clamp(16.5px, 4.3vw, 21px);
  --clue-fs: 17px;
}
.sv-app[data-layout="stack"] .sv {
  display: block; overflow-y: auto; overflow-x: hidden;
  padding: 10px var(--sv-pad) calc(var(--pad-h) + 28px);
  scroll-padding-top: 10px;
}
.sv-app[data-layout="stack"] .sv-board { align-items: center; gap: 10px; }
/* Sticky, and flush under the app bar: Chrome measures a sticky offset from
   the scroller's content edge, so the pane's 10px top padding is taken back. */
.sv-app[data-layout="stack"] .sv-bar {
  position: sticky; top: -10px; z-index: 8; width: 100%; margin-bottom: 10px;
  grid-template-areas: "dir clue clue" "dir foot nav";
  min-height: 0; box-shadow: 3px 3px 0 var(--btn-line);
}
.sv-app[data-layout="stack"] .sv-bar-foot { display: flex; padding: 0 8px 8px 12px; }
.sv-app[data-layout="stack"] .sv-bar-nav { padding: 0 7px 7px 0; align-self: end; gap: 6px; }
.sv-app[data-layout="stack"] .sv-bar-dir { min-width: 58px; padding: 6px 8px; }
.sv-app[data-layout="stack"] .sv-bar-num { font-size: 28px; }
.sv-app[data-layout="stack"] .sv-bar-way { font-size: 10px; letter-spacing: .14em; }
.sv-app[data-layout="stack"] .sv-bar-clue { padding: 9px 10px 6px 12px; }
.sv-app[data-layout="stack"] .sv-gridwrap { margin: 2px auto 0; }
.sv-app[data-layout="stack"] .sv-grid { box-shadow: 4px 4px 0 var(--btn-line); }
.sv-app[data-layout="stack"] .sv-foot { margin-top: 2px; }
.sv-app[data-layout="stack"] .sv-clues { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; margin-top: 26px; }
.sv-app[data-layout="stack"] .sv-list::before { display: none; }
.sv-app[data-layout="stack"] .sv-ol { overflow: visible; }
.sv-app[data-layout="stack"] .sv-prog { display: none; }
/* A grid at its 20px floor that would still run under the pad (solver.js
   snugFit): the bar sits flush under the app bar and the grid close under
   the bar, which gives back the 14–18px the last row was short of. */
.sv-app[data-layout="stack"].is-snug .sv { padding-top: 0; scroll-padding-top: 0; }
.sv-app[data-layout="stack"].is-snug .sv-bar { top: 0; margin-bottom: 4px; }
.sv-app[data-layout="stack"].is-snug .sv-gridwrap { margin-top: 0; }
.sv-t-short { display: none; }
@media (min-width: 620px) {
  .sv-app[data-layout="stack"] .sv-clues { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 28px; }
}

@media (max-width: 1299px) {
  .sv-prog-k { display: none; }
  .sv-prog { grid-template-columns: auto; }
  .sv-prog-bar { width: 64px; }
}
@media (max-width: 1099px) {
  .sv-app .appbar-logo .logo-long { font-size: 21px; }
}
@media (max-width: 699px) {
  .sv-t-long { display: none; }
  .sv-t-short { display: inline; }
  .sv-app .appbar { gap: 8px; padding: 0 8px; }
  .sv-app .appbar-right { gap: 7px; }
  .sv-app .appbar-titles { padding-left: 8px; }
  .sv-app .appbar-kicker { font-size: 10.5px; letter-spacing: .12em; }
  .sv-cutbtn { width: 40px; padding: 0; }
  .sv-cutbtn .btn-label { display: none; }
  .sv-clock-k { display: none; }
  .sv-clock-face { padding: 0 8px 0 9px; }
  .sv-clock-digits { font-size: 17px; }
  .sv-clock-btn { width: 32px; }
  .sv-extra-body { grid-template-columns: minmax(0, 1fr); }
  .sv-extra-art { order: -1; max-width: 360px; justify-self: center; width: 100%; }
  .sv-extra-actions .btn { flex: 1 1 100%; }
  .sv-extra-stat { padding-right: 12px; margin-right: 12px; }
}
@media (max-width: 380px) {
  .sv-key { height: 42px; font-size: 16px; }
  .sv-pad-row { gap: 4px; }
}
/* A short phone (SE-sized and below): the Check row costs the grid height,
   so the keys and the row tighten to give most of it back. */
@media (max-height: 700px) {
  .sv-pad { gap: 5px; padding-top: 6px; padding-bottom: calc(7px + env(safe-area-inset-bottom, 0px)); }
  .sv-key { height: 38px; }
  .sv-pad .sv-check-btn { height: 32px; }
  .sv-pad .sv-check-why { font-size: 13.5px; }
}
/* Shorter still (360×640, 375×667): a 15×15 grid at its 20px floor no longer
   fits above the pad, so the pad gives back a little more. */
@media (max-height: 680px) {
  .sv-pad { gap: 4px; padding-top: 5px; padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px)); }
  .sv-key { height: 35px; }
  .sv-pad .sv-check-btn { height: 30px; }
  .sv-pad .sv-check-why { line-height: 1.1; }
  .sv-app[data-layout="stack"] .sv-bar { margin-bottom: 6px; }
}
/* A phone on its side: shorter keys leave the grid some height. */
@media (max-height: 520px) {
  .sv-pad { gap: 5px; padding-top: 6px; padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px)); }
  .sv-key { height: 36px; }
  .sv-pad .sv-check-btn { height: 32px; }
}
/* …and the pad is far wider than its keys, while every pixel of height is
   grid: the Check button stands to the left of the keys (as on a tablet,
   above) with its note under it, instead of a row of its own on top of them
   that cut the grid's band from ~110px to ~69px. The keys give up a little
   width for it (46px at 667, full 50px at 844). */
@media (orientation: landscape) and (max-height: 520px) and (max-width: 959px) {
  .sv-pad {
    grid-template-columns: fit-content(210px) minmax(0, 560px);
    justify-content: center; column-gap: 14px; padding-left: 8px; padding-right: 8px;
  }
  .sv-pad > .sv-pad-row { grid-column: 2; }
  .sv-pad > .sv-check {
    grid-column: 1; grid-row: 1 / span 3; align-self: center;
    flex-direction: column; align-items: stretch; gap: 6px;
    width: auto; max-width: none; margin: 0; padding: 0;
  }
  .sv-pad > .sv-check .sv-check-why { flex: none; }
  .sv-pad .sv-check-why { font-size: 13.5px; }
}

@media print {
  .sv-pad, .sv-pop, .sv-bar-nav, .sv-cl-menu, .sv-clock-btn, .sv-foot-keys, .sv-check { display: none !important; }
  .sv-app, .sv { height: auto !important; overflow: visible !important; }
  .sv-app .sv-foot { position: static !important; }
  .sv-grid { box-shadow: none; }
}
