/* =========================================================
   DESIGN TOKENS
   Colors taken from the original Figma frames (navy and light),
   with contrast fixes for WCAG AA.
   ========================================================= */
:root {
  /* palette */
  --navy: #4f5f7c;
  --navy-deep: #435270;
  --linen: #e8d8c9;
  --linen-soft: #f3ebe2;
  --orange: #f3701e;
  --ink: #282828;
  --sky: #d0d9ec;            /* was #9aaed2 — too low contrast on navy */
  --thread-ua: #b3261e;      /* vyshyvanka red */
  --thread-va: #f3701e;      /* València orange */

  /* roles — dark theme (default, the original look) */
  --bg: var(--navy);
  --surface: var(--linen);
  --text: var(--linen);
  --text-muted: var(--sky);
  --cell-bg: var(--linen);
  --cell-fill: var(--ink);
  --grid-line: #b9aa9b;
  --grid-line-strong: var(--ink);
  --accent: var(--orange);
  --on-accent: var(--ink);   /* ink on orange passes AA, linen on orange does not */
  --deco: rgba(208, 217, 236, 0.08);
  --shadow: 0 8px 24px rgba(40, 40, 40, 0.18);

  /* type */
  --font-pixel: "Press Start 2P", "Pixelify Sans", monospace; /* Pixelify covers Cyrillic */
  --font-text: "IBM Plex Mono", ui-monospace, monospace;
  --fs-title: clamp(28px, 6vw, 48px);
  --fs-ui: 12px;
  --fs-small: 10px;
  --fs-body: 14px;

  /* space — 8px scale */
  --s1: 4px;
  --s2: 8px;
  --s3: 16px;
  --s4: 24px;
  --s5: 32px;
  --s6: 48px;
  --s7: 64px;

  --radius: 5px;
  --btn-w: 168px;
  --thread: var(--thread-va);

  /* header */
  --header-bg: var(--linen);
  --header-text: var(--navy);
  --header-muted: #6b5f53;
  --header-border: var(--orange);

  /* "marked empty" cross — every cell background is light, so one value
     works in all palettes (≥ 4.4:1 on linen, 6.2:1 on white) */
  --mark: #6b5f53;

  /* selected tab / tool */
  --active-bg: var(--surface);
  --active-text: var(--ink);
}

/* =========================================================
   Each collection has its own palette.
   The sun/moon button switches to the collection's
   alternative mode (data-mode="alt").
   ========================================================= */

/* València, alt — light linen */
:root[data-collection="va"][data-mode="alt"] {
  --bg: var(--linen-soft);
  --surface: #ffffff;
  --text: var(--ink);
  --text-muted: #6b5f53;
  --cell-bg: #ffffff;
  --cell-fill: var(--navy);
  --grid-line: #d9cdc0;
  --grid-line-strong: var(--navy);
  --deco: rgba(79, 95, 124, 0.06);
  --shadow: 0 8px 24px rgba(79, 95, 124, 0.12);
  --active-bg: var(--navy);
  --active-text: #ffffff;
}

/* Ukraine, native — white linen, red and black thread,
   the classic vyshyvanka pair. No blue: white + blue + red
   would read as the Russian flag. */
:root[data-collection="ua"] {
  --red: #c4452d;            /* white text on it passes AA */
  --black-thread: #1f1d1b;
  --bg: #f8f4ee;
  --surface: #ffffff;
  --text: var(--ink);
  --text-muted: #6b5f53;
  --cell-bg: #ffffff;
  --cell-fill: var(--black-thread);
  --grid-line: #d8cfc4;
  --grid-line-strong: var(--black-thread);
  --accent: var(--red);
  --on-accent: #ffffff;
  --thread: var(--red);
  --deco: rgba(196, 69, 45, 0.08);
  --shadow: 0 8px 24px rgba(31, 29, 27, 0.12);
  --header-bg: var(--red);
  --header-text: #ffffff;
  --header-muted: #fff5f0;   /* 4.6:1 on red — was #fbeee8, 4.37:1 */
  --header-border: var(--black-thread);
  --active-bg: var(--black-thread);
  --active-text: #ffffff;
}

/* Ukraine, alt — dark, inspired by Borshchiv black embroidery */
:root[data-collection="ua"][data-mode="alt"] {
  --bg: #1f1d1b;
  --surface: var(--linen-soft);
  --text: var(--linen-soft);
  --text-muted: #c9bfb4;
  --cell-bg: var(--linen-soft);
  --cell-fill: #1f1d1b;
  --grid-line: #cbbfb2;
  --grid-line-strong: #1f1d1b;
  --deco: rgba(243, 235, 226, 0.06);
  --shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  --header-border: var(--linen-soft);
  --active-bg: var(--linen-soft);
  --active-text: var(--ink);
}

/* ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  transition: background-color 0.3s ease, color 0.3s ease;
  overflow-x: hidden;
}

button { font: inherit; color: inherit; cursor: pointer; }

:focus-visible {
  outline: 4px solid var(--text);  /* accent orange was 2.2:1 on navy */
  outline-offset: 3px;
}

/* --text is the page color; on light header / card use their own ink */
.header :focus-visible { outline-color: var(--header-text); }
.story :focus-visible { outline-color: var(--ink); }

/* screen-reader only */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- header ---------- */
.header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: var(--s3) var(--s4);
  background: var(--header-bg);
  color: var(--header-text);
  border-bottom: 6px solid var(--header-border);
  transition: background-color 0.3s ease;
}

.brand {
  grid-column: 2;
  text-align: center;
}

.brand__title {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: var(--fs-title);
  font-weight: 400;
  letter-spacing: 0.08em;
}

.brand__tagline {
  margin: var(--s2) 0 0;
  font-family: var(--font-text);
  font-size: 12px;
  color: var(--header-muted);
}

.header__tools {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 2px solid var(--header-text);
  border-radius: var(--radius);
  background: transparent;
  color: var(--header-text);
}

.icon-btn svg { width: 18px; height: 18px; }

/* language switch */
.lang {
  display: inline-flex;
  border: 2px solid var(--header-text);
  border-radius: var(--radius);
}

/* no overflow: hidden on .lang / .mode — it clipped the focus ring;
   the end buttons get the inner radius instead */
.lang button:first-child,
.mode button:first-child { border-radius: 3px 0 0 3px; }
.lang button:last-child,
.mode button:last-child { border-radius: 0 3px 3px 0; }

.lang button {
  min-width: 44px;
  height: 36px;
  padding: 0 var(--s2);
  border: 0;
  background: transparent;
  color: var(--header-text);
  font-family: var(--font-pixel);
  font-size: 10px;
}

.lang button[aria-pressed="true"] { background: var(--header-text); color: var(--header-bg); }

/* Cyrillic in the pixel fallback looks smaller — nudge it up */
:root:lang(uk) .btn,
:root:lang(uk) .segmented button { font-size: 14px; }
:root:lang(uk) .story__title { font-size: 20px; }
.icon-btn[aria-pressed="true"] { background: var(--header-text); color: var(--header-bg); }

/* ---------- main layout ---------- */
/* wrapper of header + main: no box on larger screens, one screen on phones */
.app { display: contents; }

.main {
  position: relative;
  max-width: 1120px;
  margin: 0 auto;
  padding: var(--s5) var(--s3) var(--s4);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s4);
}

/* segmented controls: collection + level */
.segmented {
  display: inline-flex;
  padding: var(--s1);
  gap: var(--s1);
  border: 2px solid var(--text-muted);
  border-radius: var(--radius);
}

.segmented button {
  padding: var(--s2) var(--s3);
  border: 0;
  border-radius: 3px;
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-pixel);
  font-size: var(--fs-ui);
  line-height: 1.4;
  white-space: nowrap;
}

.segmented button[aria-pressed="true"] {
  background: var(--active-bg);
  color: var(--active-text);
}

.segmented button[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

.segmented small {
  display: block;
  margin-top: var(--s1);
  font-size: 10px;
  opacity: 0.7;
}

.levels button { min-width: 112px; }

/* progress dots: one per puzzle of the level.
   solved = filled, not yet = hollow, current = ring around it —
   shape, not color alone, tells the states apart */
.dots {
  display: flex;
  justify-content: center;
  margin: calc(var(--s3) * -1) 0;   /* sits close to the level switch */
}

.dots[hidden] { display: none; }

/* collection + level wrapper: no box here, one row on phones */
.tabs { display: contents; }
.tab__short { display: none; }

.meta { display: contents; }

.dot {
  display: grid;
  place-items: center;
  width: 44px;
  height: 36px;
  padding: 0;
  border: 0;
  background: transparent;
}

.dot::before {
  content: "";
  width: 10px;
  height: 10px;
  border: 2px solid var(--text-muted);
  border-radius: 50%;
}

.dot.is-solved::before { background: var(--text); border-color: var(--text); }

.dot[aria-current="true"]::before {
  border-color: var(--text);
  box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--text);
}

.dot:focus-visible { outline-offset: -2px; }

/* timer */
.status {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  font-family: var(--font-pixel);
}

.timer { font-size: 28px; color: var(--text); }
.best { font-size: var(--fs-small); color: var(--text-muted); }

/* ---------- game area ---------- */
.stage {
  align-self: stretch;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: var(--s4);
}

/* the cell grid sits in the middle column, so it stays in the centre
   of the screen; the row clues hang to its left. When there is no room
   on the left, the 1fr column grows to the clue width and the grid
   moves right only as far as it has to.
   Clue space is reserved for the longest clues of the whole level
   (--rc-*, --cc-count, set in ui.js), so the grid does not jump
   from puzzle to puzzle. */
.board {
  --n: 5;
  --cell: min(calc(360px / var(--n)), calc((100vw - 140px) / var(--n)));
  --clue-fs: clamp(10px, calc(var(--cell) * 0.28), 13px);
  --clue-lh: 1.5;
  --clue-gap: calc(var(--cell) * 0.18);
  --clue-gap-col: calc(var(--cell) * 0.08);
  --rc-count: 1;
  --rc-chars: 1;
  --cc-count: 1;
  --clue-pad: var(--s2);       /* space between the clues and the grid */
  justify-self: stretch;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: auto auto;
  font-family: var(--font-pixel);
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

.clues-col {
  grid-column: 2;
  grid-row: 1;
  display: grid;
  grid-template-columns: repeat(var(--n), var(--cell));
  align-items: end;
  padding-bottom: var(--clue-pad);
  /* border-box: the padding is part of the reserve */
  min-height: calc(var(--cc-count) * var(--clue-fs) * var(--clue-lh) + (var(--cc-count) - 1) * var(--clue-gap-col) + var(--clue-pad));
}

.clues-row {
  grid-column: 1;
  grid-row: 2;
  justify-self: end;
  display: grid;
  grid-template-rows: repeat(var(--n), var(--cell));
  justify-items: end;
  /* the clue font is monospaced: 1ch is one digit */
  font-size: var(--clue-fs);
  padding-right: var(--clue-pad);
  min-width: calc(var(--rc-chars) * 1ch + (var(--rc-count) - 1) * var(--clue-gap) + var(--clue-pad));
}

.clue {
  display: flex;
  gap: var(--clue-gap);
  font-size: var(--clue-fs);
  color: var(--text);
  transition: opacity 0.25s ease;
}

.clues-col .clue {
  flex-direction: column;
  align-items: center;
  line-height: var(--clue-lh);
  gap: var(--clue-gap-col);
}

.clues-row .clue { align-items: center; }

/* solved line → clue fades: the player sees progress */
.clue.is-done { opacity: 0.3; }

/* row/column under the pointer — a ring, so the digits keep full contrast */
.clue.is-hover {
  outline: 2px solid var(--text);
  outline-offset: 2px;
  border-radius: 2px;
}

.cells {
  grid-column: 2;
  grid-row: 2;
  display: grid;
  grid-template-columns: repeat(var(--n), var(--cell));
  border: 3px solid var(--grid-line-strong);
  border-radius: var(--radius);
  background: var(--cell-bg);
  box-shadow: var(--shadow);
  overflow: hidden;
  -webkit-touch-callout: none;   /* long-press marks a cell, no iOS callout */
}

.cell {
  position: relative;
  width: var(--cell);
  height: var(--cell);
  border: 0;
  border-right: 1px solid var(--grid-line);
  border-bottom: 1px solid var(--grid-line);
  background: var(--cell-bg);
  padding: 0;
}

/* every 5th line is thicker — easier counting on 10×10 and 15×15 */
.cell.edge-r { border-right: 3px solid var(--grid-line-strong); }
.cell.edge-b { border-bottom: 3px solid var(--grid-line-strong); }
.cell.last-c { border-right: 0; }
.cell.last-r { border-bottom: 0; }

.cell.is-filled { background: var(--cell-fill); }

.cell.is-marked::before,
.cell.is-marked::after {
  content: "";
  position: absolute;
  inset: 50% 22%;
  height: 2px;
  background: var(--mark);
  transform: rotate(45deg);
}
.cell.is-marked::after { transform: rotate(-45deg); }

/* two-tone ring: the dark part shows on empty cells, the light part on filled ones */
.cell:focus-visible {
  outline: 4px solid var(--grid-line-strong);
  outline-offset: -4px;
  box-shadow: inset 0 0 0 7px var(--cell-bg);
}

/* wrong cell: accent ring + light hatching, so it does not rely on color alone */
.cell.is-wrong {
  box-shadow: inset 0 0 0 3px var(--accent);
  background-image: repeating-linear-gradient(45deg, var(--cell-bg) 0 2px, transparent 2px 6px);
}

@media (hover: hover) {
  .board:not(.is-won) .cell:hover { box-shadow: inset 0 0 0 3px var(--grid-line-strong), inset 0 0 0 5px var(--cell-bg); }
}

/* win: every filled cell becomes a cross-stitch */
.board.is-won .cell.is-filled {
  background-color: var(--cell-bg);
  background-image:
    linear-gradient(45deg, transparent 40%, var(--thread) 40% 60%, transparent 60%),
    linear-gradient(-45deg, transparent 40%, var(--thread) 40% 60%, transparent 60%);
  background-size: 76% 76%;
  background-position: center;
  background-repeat: no-repeat;
  animation: stitch 0.45s ease both;
  animation-delay: var(--d, 0ms);
}

.board.is-won .cell.is-marked::before,
.board.is-won .cell.is-marked::after { display: none; }

@keyframes stitch {
  from { background-size: 0 0; }
  to   { background-size: 76% 76%; }
}

/* ---------- controls under the board ---------- */
.controls {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s3);
}

.mode {
  display: inline-flex;
  border: 2px solid var(--text-muted);
  border-radius: var(--radius);
}

.mode button {
  display: grid;
  place-items: center;
  width: 48px;
  height: 44px;
  border: 0;
  background: transparent;
  color: var(--text-muted);
}

.mode button[aria-pressed="true"] { background: var(--active-bg); color: var(--active-text); }
.mode svg { width: 16px; height: 16px; }

.btn {
  min-width: var(--btn-w);
  height: 48px;
  padding: 0 var(--s4);
  border-radius: var(--radius);
  font-family: var(--font-pixel);
  font-size: var(--fs-ui);
  text-transform: lowercase;
  transition: transform 0.15s ease, background-color 0.2s ease;
}

.btn:active { transform: translateY(1px); }

/* one primary action — the only orange button */
.btn--primary {
  border: 2px solid var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--shadow);
}

.btn--ghost {
  border: 2px solid var(--text-muted);
  background: transparent;
  color: var(--text);
}

.btn--ghost:hover { border-color: var(--text); }

/* reset / solution: text button here, icon + caption on phones */
.btn--icon svg { display: none; }

/* result of "check" — visible and announced (role="status") */
.feedback {
  min-height: 1.5em;
  margin: 0;
  font-family: var(--font-pixel);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--text);
  text-align: center;
}

.hint {
  max-width: 1120px;
  margin: 0 auto var(--s6);
  padding: 0 var(--s3);
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
}

/* ---------- story card after solving ---------- */
.story {
  width: min(400px, 100%);
  padding: var(--s4);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
  border-top: 6px solid var(--thread);
}

.story[hidden] { display: none; }

.story__eyebrow {
  margin: 0 0 var(--s2);
  font-family: var(--font-pixel);
  font-size: var(--fs-small);
  color: #6b5f53;              /* orange text on white fails contrast — the thread color lives in the top border */
}

.story__title {
  margin: 0;
  font-family: var(--font-pixel);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
}

.story__native {
  margin: var(--s1) 0 var(--s3);
  font-size: 13px;
  color: #6b5f53;
}

.story__text {
  margin: 0 0 var(--s4);
  font-size: var(--fs-body);
  line-height: 1.6;
}

.story .btn--primary { width: 100%; }

/* close button — only the phone bottom sheet needs it */
.story { position: relative; }
.story:focus { outline: none; }   /* focused by script only, so screen readers start here */

.story__close {
  display: none;
  place-items: center;
  position: absolute;
  top: var(--s2);
  right: var(--s2);
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink);
}

.story__close svg { width: 16px; height: 16px; }

/* ---------- footer ---------- */
.footer {
  padding: var(--s4) var(--s3) var(--s5);
  text-align: center;
  font-family: var(--font-pixel);
  font-size: var(--fs-small);
  color: var(--text-muted);
}

.footer a { color: inherit; }

/* ---------- decorative stitches in the background ---------- */
.deco {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
}

.deco span {
  position: absolute;
  width: 24px;
  height: 24px;
  background-image:
    linear-gradient(45deg, transparent 38%, var(--deco) 38% 62%, transparent 62%),
    linear-gradient(-45deg, transparent 38%, var(--deco) 38% 62%, transparent 62%);
}

body { position: relative; z-index: 0; }

/* ---------- responsive ---------- */
@media (min-width: 900px) {
  .stage.has-story {
    grid-template-columns: minmax(0, 1fr) 360px;
    align-items: center;
    gap: var(--s6);
  }
}

@media (max-width: 640px) {
  /* header + game fill exactly one screen (dvh follows Safari's bars);
     the hint and footer sit below it */
  .app {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
  }

  .header {
    grid-template-columns: 1fr auto;
    padding:
      calc(var(--s2) + env(safe-area-inset-top))
      calc(var(--s3) + env(safe-area-inset-right))
      var(--s2)
      calc(var(--s3) + env(safe-area-inset-left));
    border-bottom-width: 4px;
  }
  .brand { grid-column: 1; text-align: left; }
  .brand__title { font-size: 22px; }
  .header__tools { grid-column: 2; }
  .brand__tagline { display: none; }
  .lang button { min-width: 36px; padding: 0 var(--s1); font-size: var(--fs-ui); }   /* ≥12px on buttons */
  .icon-btn { width: 36px; height: 36px; }

  .main {
    flex: 1 1 0;
    min-height: 0;
    width: 100%;
    padding:
      var(--s2)
      calc(var(--s3) + env(safe-area-inset-right))
      calc(var(--s2) + env(safe-area-inset-bottom))
      calc(var(--s3) + env(safe-area-inset-left));
    gap: 6px;
  }

  /* collection + level in one row: UA / VAL · 5 / 10 / 15 */
  .tabs {
    display: flex;
    justify-content: center;
    gap: var(--s2);
    height: 48px;               /* fixed: the "check" message is placed from it */
  }
  .segmented { padding: 2px; gap: 2px; }
  .segmented button,
  .levels button {
    min-width: 44px;
    min-height: 40px;
    padding: 0 var(--s2);
    font-size: var(--fs-ui);
  }
  :root:lang(uk) .segmented button { font-size: var(--fs-ui); }
  .tab__short { display: inline; }
  /* full name and size line stay for screen readers;
     a locked level still shows as the faded button */
  .tab__full,
  .segmented small {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  /* dots and timer share one row */
  .meta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s3);
  }
  .dots { margin: 0; }
  .dot { width: 44px; height: 44px; }
  .status {
    flex-direction: column;
    align-items: center;
    gap: 2px;
  }
  .timer { font-size: 16px; }
  .best:empty { display: none; }
  .board { --clue-lh: 1.05; }

  /* the board takes whatever height is left — main.js sizes the cells */
  .stage {
    flex: 1 1 0;
    min-height: 0;
    align-self: stretch;
    place-items: center;
  }

  /* hard 15×15: compact clues, and the board takes 8px of the side
     padding on each side (the controls keep theirs), so the cells
     get every pixel the width allows */
  :root[data-level="hard"] .stage { margin-inline: calc(-1 * var(--s2)); }
  :root[data-level="hard"] .board {
    --clue-gap: calc(var(--cell) * 0.1);
    --clue-gap-col: 0px;
    --clue-pad: var(--s1);
  }

  /* one row in thumb reach: ■/✕ · check · reset · solution */
  .controls {
    order: 1;                    /* last row, under the "check" message */
    width: 100%;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: var(--s2);
  }
  .mode button { width: 44px; height: 44px; }
  .btn { min-width: 0; padding: 0 var(--s2); }
  #checkBtn { flex: 1 1 auto; }
  .btn--icon {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--s1);
    flex: 0 0 auto;
    min-width: 52px;
    height: 44px;
    padding: 0 var(--s1);
    font-family: var(--font-text);
    font-size: 12px;            /* smallest text on a button */
    line-height: 1.1;
  }
  .btn--icon svg { display: block; width: 18px; height: 18px; }
  :root:lang(uk) .btn--icon { font-size: 12px; }

  /* "check" result covers the dots + timer row for 3 seconds,
     so it costs the board no height and the board does not jump */
  .feedback {
    position: absolute;
    top: calc(var(--s2) + 48px + 6px);   /* main padding + .tabs + gap */
    left: var(--s3);
    right: var(--s3);
    z-index: 1;
    display: grid;
    place-items: center;
    height: 44px;
    min-height: 0;
    white-space: nowrap;
    pointer-events: none;
  }
  .feedback:not(:empty) { background: var(--bg); }

  .hint { display: none; }    /* phones: the game explains itself; the footer stays under the scroll */

  /* story → bottom sheet over the game */
  .story {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    width: auto;
    max-height: 80dvh;
    overflow-y: auto;
    padding:
      var(--s4)
      calc(var(--s3) + env(safe-area-inset-right))
      calc(var(--s3) + env(safe-area-inset-bottom))
      calc(var(--s3) + env(safe-area-inset-left));
    border-radius: 12px 12px 0 0;
    box-shadow: 0 -8px 32px rgba(31, 29, 27, 0.28);
    /* slides in once the stitches are sewn (delay set in ui.js) */
    animation: sheet-in 0.3s ease var(--sheet-delay, 0ms) both;
  }
  .story__close { display: grid; }
  .story__eyebrow,
  .story__title { padding-right: 44px; }

  .deco { display: none; }
}

/* short phones (iPhone SE with Safari's bars): every pixel goes to the board */
@media (max-width: 640px) and (max-height: 600px) {
  .header { padding-top: calc(6px + env(safe-area-inset-top)); padding-bottom: 6px; }
  .brand__title { font-size: 20px; }
}

@keyframes sheet-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* The universal selector alone loses to more specific rules
   (.board.is-won .cell.is-filled, .btn …), so they are listed too. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms;
    animation-delay: 0ms;
    transition-duration: 0.01ms;
  }
  body,
  .header,
  .board,
  .board *,
  .btn { transition: none; }   /* .board: a 0.01ms "all" transition made fitBoard() measure stale cell sizes */
  .board.is-won .cell.is-filled,
  .story { animation: none; }
  .btn:active { transform: none; }
}
