/* Color Match & Merge — styles
 *
 * One committed visual world: a bright shelf under a summer sky, drawn with
 * cartoon weight — thick ink outlines and hard, unblurred drop shadows. That
 * weight is doing a job, not just a style: the liquid colours are vivid and
 * the ground is vivid, so without an outline around every jar and band the
 * two would sit at the same brightness and flatten into each other.
 *
 * Deliberately single-theme, so there are no prefers-color-scheme blocks —
 * every colour below is painted explicitly and the page holds whatever ground
 * the viewer's own theme paints behind it. */

:root {
  /* the scene */
  --sky-top:   #79cbf2;
  --sky-mid:   #a5e2fa;
  --sky-low:   #d3f1fc;
  --wood:      #f7cd97;
  --wood-edge: #d89a55;
  /* Solid, not translucent: the shelf is drawn as one segment per jar, and
     two overlapping see-through shadows would darken at every join and stripe
     the plank. */
  --wood-under:#a86f36;

  /* surfaces and ink */
  --paper:     #fffdf7;
  --paper-warm:#fff3e0;
  --ink:       #2b2142;
  --ink-soft:  #6f6389;
  --ink-faint: rgba(43, 33, 66, .16);

  /* action */
  --accent:      #ff5d8f;
  --accent-deep: #e0446f;
  --gold:        #ffc93c;
  --grass:       #35c46b;
  /* Reserved for the one action that destroys something. */
  --danger:      #d8365f;

  --lift:   4px;                       /* hard-shadow offset on raised things */
  --stroke: 3px;
  --radius: 16px;
}

/* Pseudo-elements are NOT matched by `*`, and several here are sized boxes
   with borders (the jar stand, the shelf plank edge). Without them listed,
   those render a border-width taller than intended. */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: pan-x pan-y;
}

body {
  /* An explicit colour underneath the gradient: the gradient is a background
     IMAGE, so without this the computed background-color stays transparent
     and the page would composite over whatever ground the host paints. */
  background-color: var(--sky-mid);
  background-image:
    linear-gradient(180deg, var(--sky-top) 0%, var(--sky-mid) 42%, var(--sky-low) 100%);
  background-attachment: fixed;
  color: var(--ink);
  font: 16px/1.55 "Nunito", ui-rounded, "Segoe UI", system-ui, sans-serif;
  font-weight: 600;
  -webkit-font-smoothing: antialiased;
}

/* Polite Carrot startup lockup, shared with Tide-Runner. */
#boot {
  position: fixed;
  inset: 0;
  height: 100vh;
  height: 100dvh;
  z-index: 100;
  background: #000;
  display: grid;
  place-items: center;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.55s ease;
}
#boot.done { opacity: 0; }
.boot-lockup {
  display: grid;
  justify-items: center;
  width: min(68vw, 52vh, 360px);
}
.boot-mark {
  display: block;
  width: 92%;
  height: auto;
  opacity: 0;
  transform: scale(0.94);
  animation: boot-logo 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) 0.18s forwards;
}
.boot-name {
  display: block;
  width: 100%;
  height: auto;
  margin-top: 12px;
  opacity: 0;
  transform: translateY(8px);
  animation: boot-name 0.55s ease 0.72s forwards;
}
@keyframes boot-logo {
  to { opacity: 1; transform: scale(1); }
}
@keyframes boot-name {
  to { opacity: 1; transform: translateY(0); }
}

.app {
  max-width: 1040px;
  margin: 0 auto;
  /* --vvh is the visual viewport, published by app.js. It is preferred over
     dvh because dvh resolves to the viewport with the browser's toolbars
     retracted — the tallest it could be — and on iOS Safari the toolbar is
     drawn over the page, so a board sized to dvh runs underneath it. The dvh
     values stay as the fallback for anything without a visual viewport. */
  height: 100vh;
  height: 100dvh;
  height: var(--vvh, 100dvh);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Keep clear of iOS status bar / notch and Android nav gesture area. */
  padding:
    calc(20px + env(safe-area-inset-top))
    calc(18px + env(safe-area-inset-right))
    calc(20px + env(safe-area-inset-bottom))
    calc(18px + env(safe-area-inset-left));
}

.screen { display: none; flex: 1; min-height: 0; }
.screen.is-active {
  display: flex;
  flex-direction: column;
  animation: rise .34s cubic-bezier(.2, .9, .3, 1.2) both;
}
/* The one place scrolling is welcome — the shelf of up to a hundred tiles is
   deliberately taller than the window. Everything else fits the viewport. */
#screen-levels.is-active { overflow: hidden; }
#screen-levels.is-active .level-grid {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 2px 2px 12px;
}

/* While a puzzle is on screen the whole game is sized to the window: nothing
   scrolls, so the shelf, the buttons and the hint line are all in view at
   once. The jars are what give way — fitBoard() in app.js shrinks them until
   the column fits. The menu is left to scroll normally. */
body.playing { overflow: hidden; }
body.playing .app {
  height: 100vh;
  height: 100dvh;
  height: var(--vvh, 100dvh);
  padding-top: calc(12px + env(safe-area-inset-top));
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
body.playing #screen-game.is-active {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}
/* Zero the vertical margins only — the gap between rows handles that now.
   Blanket `margin: 0` would also clear the `auto` side margins that centre
   the briefing and the hint line. */
body.playing #screen-game.is-active > * { flex: none; margin-block: 0; }
/* If a window is so short that even the smallest readable jars will not fit,
   the shelf scrolls inside itself rather than the whole page scrolling — the
   buttons and the hint line stay where they are.
   
   Selected through the same `#screen-game.is-active >` path as the rule above,
   which would otherwise out-specify it and pin the shelf at `flex: none`. With
   that, the shelf could not shrink, so the valve never opened and the toolbar
   was clipped off the bottom instead. */
body.playing #screen-game.is-active > .shelf-wrap {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}
body.playing .board { margin-block: 0; }
body.playing .shelf-wrap { margin-block: 0; }

@keyframes rise {
  from { opacity: 0; transform: translateY(14px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

/* ───────── buttons ───────── */

.btn {
  appearance: none;
  border: var(--stroke) solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-family: "Baloo 2", "Nunito", sans-serif;
  font-weight: 700;
  font-size: 16px;
  line-height: 1.1;
  padding: 11px 18px;
  border-radius: 13px;
  cursor: pointer;
  box-shadow: 0 var(--lift) 0 var(--ink);
  transition: transform .09s ease, box-shadow .09s ease, background .14s ease;
}
.btn:hover:not(:disabled) { background: var(--paper-warm); }
/* Press the button physically into the page. */
.btn:active:not(:disabled) { transform: translateY(var(--lift)); box-shadow: 0 0 0 var(--ink); }
.btn:disabled { opacity: .45; cursor: default; box-shadow: 0 var(--lift) 0 var(--ink-faint); }

.btn:focus-visible,
.jar:focus-visible,
.diff:focus-visible,
.level-btn:focus-visible,
input:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

.btn--primary { background: var(--accent); color: #fff; }
.btn--primary:hover:not(:disabled) { background: var(--accent-deep); }
.btn--ghost {
  background: rgba(255, 255, 255, .62);
  box-shadow: 0 var(--lift) 0 var(--ink-faint);
}
.btn--ghost:hover:not(:disabled) { background: #fff; }
.btn--quiet { font-size: 15px; color: var(--ink-soft); }
.btn--wide  { width: 100%; }

/* ───────── menu ───────── */

.masthead { text-align: center; margin: 14px 0 30px; }

.masthead__mark {
  display: flex;
  justify-content: center;
  gap: 9px;
  padding-top: 16px;
  margin-bottom: 14px;
}
.masthead__mark span {
  width: 22px; height: 32px;
  border-radius: 4px 4px 9px 9px;
  background: linear-gradient(180deg, rgba(255,255,255,.75) 30%, var(--c) 30%);
  border: var(--stroke) solid var(--ink);
  animation: bob 2.4s ease-in-out infinite;
}
.masthead__mark span:nth-child(2) { animation-delay: .18s; }
.masthead__mark span:nth-child(3) { animation-delay: .36s; }
@keyframes bob { 50% { transform: translateY(-8px) rotate(-4deg); } }

.masthead h1 {
  margin: 0;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: clamp(42px, 9vw, 74px);
  line-height: .98;
  letter-spacing: -.01em;
  color: #fff;
  /* Stroke behind the fill, then a hard drop below: cartoon lettering. */
  -webkit-text-stroke: 7px var(--ink);
  paint-order: stroke fill;
  text-shadow: 0 7px 0 var(--ink);
  text-wrap: balance;
}
.masthead__tag {
  margin: 16px auto 0;
  max-width: 30ch;
  color: var(--ink);
  font-weight: 700;
  font-size: 17px;
  text-wrap: balance;
}

/* ───────── home: two ways in ───────── */

.choices {
  display: grid;
  gap: 16px;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
}

.choice {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 18px 20px;
  text-align: left;
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  box-shadow: 0 6px 0 var(--ink);
  transition: transform .09s ease, box-shadow .09s ease, background .14s ease;
}
.choice:hover { background: var(--paper-warm); }
.choice:active { transform: translateY(6px); box-shadow: 0 0 0 var(--ink); }
.choice:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.choice__body { flex: 1; min-width: 0; line-height: 1.2; }
.choice__name {
  display: block;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 25px;
}
.choice__sub { display: block; color: var(--ink-soft); font-size: 14.5px; font-weight: 700; }
.choice__go {
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 30px;
  color: var(--ink-soft);
  line-height: 1;
}

/* A jar that fills with how far through the campaign you are — the icon says
   something true rather than just sitting there. */
.choice__jar {
  position: relative;
  flex: 0 0 46px;
  width: 46px;
  height: 62px;
  border: var(--stroke) solid var(--ink);
  border-radius: 6px 6px 14px 14px;
  background: rgba(203, 233, 248, .72);
  overflow: hidden;
}
.choice__jar i {
  position: absolute;
  inset: auto 0 0 0;
  height: 0;
  background: var(--gold);
  border-top: 2px solid rgba(43, 33, 66, .22);
  transition: height .4s cubic-bezier(.2, .9, .3, 1.3);
}

/* Three jars at odd heights: a shelf dealt at random. */
.choice__shelf {
  flex: 0 0 46px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  width: 46px;
  height: 62px;
}
.choice__shelf i {
  width: 13px;
  border: 2px solid var(--ink);
  border-radius: 3px 3px 7px 7px;
  background: linear-gradient(180deg, rgba(203, 233, 248, .8) 40%, var(--c) 40%);
}
.choice__shelf i:nth-child(1) { height: 40px; --c: #2fc15e; }
.choice__shelf i:nth-child(2) { height: 58px; --c: #9a53ef; }
.choice__shelf i:nth-child(3) { height: 48px; --c: #ff8700; }

/* Two blobs overlapping into a third, which is the whole mode in one mark. */
.choice__mix {
  position: relative;
  flex: 0 0 46px;
  width: 46px;
  height: 62px;
}
.choice__mix i {
  position: absolute;
  width: 28px;
  height: 28px;
  border: 2px solid var(--ink);
  border-radius: 50%;
}
.choice__mix i:nth-child(1) { left: 0;   top: 6px;  background: #f5423c; }
.choice__mix i:nth-child(2) { right: 0;  top: 6px;  background: #3b7bf7; }
.choice__mix i:nth-child(3) { left: 9px; top: 26px; background: #9a53ef; }

/* A little calendar, drawn the same way as the jar and the shelf beside it. */
.choice__cal {
  position: relative;
  flex: 0 0 46px;
  width: 46px;
  height: 62px;
  align-self: center;
  border: 2px solid var(--ink);
  border-radius: 8px;
  background: var(--paper);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
  padding: 19px 8px 9px;
}
.choice__cal-top {
  position: absolute;
  inset: 0 0 auto 0;
  height: 13px;
  background: var(--accent);
  border-bottom: 2px solid var(--ink);
  border-radius: 5px 5px 0 0;
}
.choice__cal-dot {
  border-radius: 2px;
  background: var(--ink-faint);
}
.choice__cal-dot.is-on { background: var(--grass); }

/* ───────── the calendar of days ───────── */

.cal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 10px;
}
.cal__head h3 {
  margin: 0;
  flex: 1;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 21px;
}
.cal__head .btn { padding: 6px 14px; font-size: 22px; line-height: 1; }

.cal__names, .cal {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.cal__names {
  margin-bottom: 6px;
  color: var(--ink-soft);
  font-weight: 800;
  font-size: 13px;
}

.cal__day {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  background: var(--paper-warm);
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 15px;
  color: var(--ink);
  cursor: pointer;
  padding: 0;
}
.cal__day:hover:not(:disabled) { background: #fff; }
.cal__day:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.cal__day:disabled { opacity: .38; cursor: default; border-style: dashed; }
.cal__day.is-today { box-shadow: 0 0 0 3px var(--gold); }
.cal__day.is-done { background: var(--grass); color: #fff; }
.cal__day.is-done .cal__dot { border-color: #fff; }
.cal--blank { visibility: hidden; }

.cal__dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border: 2px solid var(--ink);
  border-radius: 50%;
}
.cal__dot--classic { background: #3b7bf7; }
.cal__dot--merge { background: var(--accent); }

.cal__legend {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 12px 0 0;
  color: var(--ink-soft);
  font-size: 13.5px;
  font-weight: 700;
}
.cal__legend .cal__dot { margin-left: 8px; }
.cal__legend .cal__dot:first-child { margin-left: 0; }

/* ───────── what mixes with what ───────── */

.recipes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 18px;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 6px;
  padding: 7px 14px;
  list-style: none;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--paper-warm);
}
/* The hidden attribute alone is not enough once a rule sets display: an
   element-level `display: flex` out-specifies the browser's own
   `[hidden] { display: none }`, and the list then goes on taking up its band
   of the board while reporting itself hidden. It cost the campaign's biggest
   level a row of shelf before it was spotted. */
.recipes[hidden] { display: none; }
.recipe { display: flex; align-items: center; gap: 4px; }
.recipe__dot {
  width: 15px;
  height: 15px;
  border: 2px solid var(--ink);
  border-radius: 50%;
}
.recipe__op {
  font-weight: 800;
  font-size: 13px;
  color: var(--ink-soft);
}

/* ───────── a screen below home ───────── */

.subhead {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  max-width: 620px;
  margin: 0 auto 22px;
}
.subhead__title { flex: 1; text-align: center; line-height: 1.15; }
.subhead__title h2 {
  margin: 0;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 27px;
}
.subhead__title span { color: var(--ink-soft); font-size: 14px; font-weight: 700; }
/* keeps the heading centred against the back button */
.subhead__balance { flex: 0 0 96px; }

#screen-levels .level-grid { max-width: none; margin: 0; gap: 10px; }
/* width: 100% matters on every one of these. Each is a flex item in a column
   screen with auto side margins, and auto side margins make a flex item
   shrink to fit its content — so without an explicit width the box silently
   takes the width of its longest line of text, and changes size when that
   text changes. It showed up as the random puzzle card narrowing by 6px when
   a setting with a short description was picked. */
.card--single { width: 100%; max-width: 420px; margin: 0 auto; }

.menu-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
}

.card {
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  padding: 22px;
  box-shadow: 0 6px 0 var(--ink);
}
.card__title {
  margin: 0 0 4px;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 25px;
  line-height: 1.1;
}
.card__body { margin: 0 0 16px; color: var(--ink-soft); font-size: 15px; }
.card__note { margin: 14px 0 0; color: var(--ink-soft); font-size: 14px; min-height: 1.2em; }

.level-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Adapt to whatever room the window gives — a phone gets a tight five,
     an iPad twelve or more. align-content stops the rows drifting apart to
     fill the vertical space when there are only a few of them. */
  grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
  gap: 10px;
  align-content: start;
}

.tile {
  width: 100%;
  /* Slightly wider than tall: five rows of squares made the card overflow the
     window before the Random Puzzle panel beside it had been read. */
  aspect-ratio: 1 / 0.78;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  border: var(--stroke) solid var(--ink);
  border-radius: 12px;
  background: var(--paper-warm);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  box-shadow: 0 var(--lift) 0 var(--ink);
  transition: transform .09s ease, box-shadow .09s ease, background .14s ease;
}
.tile:hover:not(:disabled) { background: #ffe8c6; }
.tile:active:not(:disabled) { transform: translateY(var(--lift)); box-shadow: 0 0 0 var(--ink); }
.tile:disabled {
  cursor: default;
  color: rgba(43, 33, 66, .45);
  background: rgba(255, 255, 255, .6);
  box-shadow: 0 var(--lift) 0 var(--ink-faint);
}
/* the one to play next */
.tile.is-next { background: var(--gold); }

.tile__no {
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 19px;
  line-height: 1;
}
.tile__stars {
  font-size: 8px;
  letter-spacing: .5px;
  line-height: 1;
  color: var(--gold);
}
.tile__stars .off { color: rgba(43, 33, 66, .2); }
/* A locked level has no score to show, so show nothing rather than three
   empty stars — this has to come last to beat the .off colour above. */
.tile:disabled .tile__stars,
.tile:disabled .tile__stars .off { color: transparent; }

.difficulty { display: grid; grid-template-columns: repeat(2, 1fr); gap: 9px; }
.diff {
  flex: 1;
  padding: 11px 6px;
  border-radius: 12px;
  border: var(--stroke) solid var(--ink);
  background: var(--paper-warm);
  color: var(--ink-soft);
  font: inherit;
  font-family: "Baloo 2", sans-serif;
  font-weight: 700;
  font-size: 16px;
  cursor: pointer;
  box-shadow: 0 var(--lift) 0 var(--ink);
  transition: transform .09s ease, box-shadow .09s ease, background .14s ease;
}
.diff:hover { background: #ffe8c6; }
.diff.is-on {
  background: var(--gold);
  color: var(--ink);
  transform: translateY(var(--lift));
  box-shadow: 0 0 0 var(--ink);
}
/* ───────── the difficulty slider ─────────
   Five settings in a two-column grid left an empty cell and three rows of
   buttons — the odd one out is always visible as a gap. Five across in a row
   is not the answer either: at 390px that is 70px a segment and "Extra Hard"
   does not fit. A slider is one row whatever the count, names the setting at
   full size, and is the one control here whose meaning IS an ordering. */
.setting { margin-bottom: 4px; }
.setting__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}
.setting__label {
  font-size: 13px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.setting__name {
  font-family: "Baloo 2", sans-serif;
  font-size: 20px;
  color: var(--ink);
}
.setting__slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 26px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.setting__slider::-webkit-slider-runnable-track {
  height: 10px;
  border-radius: 999px;
  border: var(--stroke) solid var(--ink);
  background: var(--paper-warm);
}
.setting__slider::-moz-range-track {
  height: 10px;
  border-radius: 999px;
  border: var(--stroke) solid var(--ink);
  background: var(--paper-warm);
}
/* The thumb carries the same gold and the same hard shadow as a selected
   button did, so the control still reads as part of the same set of pieces. */
.setting__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  margin-top: -10px;
  border-radius: 50%;
  border: var(--stroke) solid var(--ink);
  background: var(--gold);
  box-shadow: 0 2px 0 var(--ink);
  transition: transform .09s ease;
}
.setting__slider::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border: var(--stroke) solid var(--ink);
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 2px 0 var(--ink);
}
.setting__slider:active::-webkit-slider-thumb { transform: scale(1.08); }
.setting__slider:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 4px;
  border-radius: 999px;
}
/* One label per stop, the ends pulled inward so they sit under the thumb's
   travel rather than under the track's corners. */
.setting__ticks {
  display: flex;
  justify-content: space-between;
  padding: 0 4px;
  margin-top: 2px;
}
.setting__ticks span {
  font-size: 11px;
  color: var(--ink-soft);
  flex: 1;
  text-align: center;
}
.setting__ticks span:first-child { text-align: left; }
.setting__ticks span:last-child { text-align: right; }
.setting__ticks span.is-on { color: var(--ink); font-weight: 700; }

.difficulty__blurb {
  margin: 12px 0 14px;
  color: var(--ink-soft);
  font-size: 14px;
  /* Two lines' worth, always, so picking a setting whose description happens
     to fit on one line does not shorten the card and jump everything under it
     up the screen. The old 2.6em was less than two lines actually take, so it
     only ever held for the short ones. */
  line-height: 1.5;
  min-height: 3em;
}

/* Jar count, as a stepper rather than fourteen buttons or a slider: it stays
   one line on the narrowest phone, and the number is the thing worth reading. */
.jarpick {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 2px 0 16px;
}
/* display:flex beats the browser's own [hidden] rule, the same way .recipes
   does above, so the row has to be told to go away explicitly. */
.jarpick[hidden] { display: none; }
.jarpick__step {
  min-width: 46px;
  padding: 8px 0;
  font-size: 22px;
  line-height: 1;
}
.jarpick__count {
  min-width: 92px;
  text-align: center;
  color: var(--ink-soft);
  font-size: 16px;
}
.jarpick__count b {
  color: var(--ink);
  font-size: 22px;
  margin-right: 4px;
}

.seed {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  color: var(--ink-soft);
  font-size: 14px;
}
.seed input {
  flex: 1;
  min-width: 0;
  background: #fff;
  border: 2px solid var(--ink);
  border-radius: 10px;
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  padding: 8px 11px;
}

/* Only shown when this view genuinely cannot keep progress — better to say so
   than to let someone earn stars that quietly vanish. */
.save-warning {
  margin: 18px auto 0;
  max-width: 46ch;
  padding: 11px 14px;
  border: var(--stroke) solid var(--ink);
  border-radius: 13px;
  background: var(--gold);
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 700;
  text-align: center;
  box-shadow: 0 var(--lift) 0 var(--ink);
}
.save-warning[hidden] { display: none; }

.menu-foot { display: flex; justify-content: center; gap: 11px; margin-top: 28px; }

/* ───────── game chrome ───────── */

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  padding: 11px 16px;
  box-shadow: 0 5px 0 var(--ink);
}
.topbar__title { flex: 1; min-width: 0; text-align: center; }
.topbar__title h2 {
  margin: 0;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 22px;
  line-height: 1.1;
}
.topbar__title span { color: var(--ink-soft); font-size: 13px; font-variant-numeric: tabular-nums; }

.topbar__stats { display: flex; gap: 8px; }
.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.05;
  min-width: 52px;
  padding: 5px 8px;
  border-radius: 11px;
  border: 2px solid var(--ink);
  background: var(--paper-warm);
}
.stat b {
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 21px;
  font-variant-numeric: tabular-nums;
}
.stat small {
  color: var(--ink-soft);
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .07em;
}

.brief {
  margin: 16px auto 0;
  max-width: 56ch;
  text-align: center;
  color: var(--ink);
  font-size: 15px;
  text-wrap: balance;
}

/* ───────── board ───────── */

.board {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-end;
  gap: 34px;
  margin: 26px 0 4px;
}

.board__main {
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* a stand, so the big jar rests on something like the shelf jars do */
.board__main::after {
  content: "";
  width: calc(var(--jar-h, 122px) * 1.24);
  height: 17px;
  margin-top: -2px;
  border: var(--stroke) solid var(--ink);
  border-radius: 7px;
  background: linear-gradient(180deg, var(--wood) 0 55%, var(--wood-edge) 55%);
  box-shadow: 0 5px 0 var(--wood-under);
}

.goal {
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: var(--radius);
  padding: 16px;
  width: 216px;
  box-shadow: 0 5px 0 var(--ink);
}
.goal__row { display: flex; align-items: center; gap: 12px; margin-bottom: 13px; }
.goal__text { min-width: 0; line-height: 1.15; }
.goal__label {
  display: block;
  color: var(--ink-soft);
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.goal__name {
  display: block;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 21px;
  line-height: 1.1;
}

.swatch {
  width: 46px; height: 46px;
  flex: 0 0 46px;
  border-radius: 13px;
  border: var(--stroke) solid var(--ink);
  background: #fff;
  box-shadow: 0 3px 0 var(--ink);
}
.swatch--target {
  display: grid;
  place-items: center;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 19px;
}

.meter { display: flex; align-items: center; gap: 10px; }
.meter__bar {
  flex: 1;
  height: 14px;
  border-radius: 99px;
  border: 2px solid var(--ink);
  background: #fff;
  overflow: hidden;
}
.meter__bar i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width .34s cubic-bezier(.2, .9, .3, 1.3);
}
.meter__value {
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  min-width: 42px;
  text-align: right;
}

/* ───────── jars ───────── */

.jar {
  appearance: none;
  position: relative;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  transition: transform .16s cubic-bezier(.2, .9, .3, 1.3);
}
.jar:disabled { cursor: default; }
.jar:hover:not(:disabled):not(.is-selected) { transform: translateY(-3px); }


.jar__glass {
  position: relative;
  width: var(--w);
  height: var(--h);
  border: var(--stroke) solid var(--ink);
  border-radius: 9px 9px 21px 21px;
  background: rgba(203, 233, 248, .72);
  overflow: hidden;
  box-shadow: 0 var(--lift) 0 var(--ink);
}
/* etched unit marks */
.jar__glass::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to top,
    transparent 0 calc(var(--unit) - 2px),
    rgba(43, 33, 66, .13) calc(var(--unit) - 2px) var(--unit)
  );
}
/* glass highlight */
.jar__glass::before {
  content: "";
  position: absolute;
  top: 6px;
  left: calc(var(--w) * .1);
  width: calc(var(--w) * .17);
  height: 62%;
  border-radius: 99px;
  background: rgba(255, 255, 255, .72);
  z-index: 0;
  pointer-events: none;
}

.jar__stack {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column-reverse;   /* first cell is the bottom of the jar */
}

.band {
  width: 100%;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-top: 2px solid rgba(43, 33, 66, .22);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .28);
}
/* The letter is how bands are told apart without relying on hue alone.
   Off by default, and switched on by Colour Blind Assist in Settings. */
.band span {
  font-family: "Baloo 2", sans-serif;
  font-size: clamp(9px, calc(var(--jar-h, 122px) * .1), 12px);
  font-weight: 800;
  user-select: none;
  visibility: hidden;
}
body.cb-assist .band span { visibility: visible; }

/* Raised above its neighbours as well as lifted, so it is never behind the
   next jar along or the plank of the row below. */
.jar.is-selected { transform: translateY(-14px) rotate(-4deg); z-index: 5; }
.jar.is-selected .jar__glass { box-shadow: 0 8px 0 var(--ink); }

/* somewhere this jar may legally go */
.jar.is-target .jar__glass {
  animation: beckon 1.05s ease-in-out infinite;
}
@keyframes beckon {
  50% { box-shadow: 0 var(--lift) 0 var(--ink), 0 0 0 5px rgba(53, 196, 107, .5); }
}

/* The hint leaves these on until the next move, so the pair it pointed out
   can still be seen once the shakes have finished. */
.jar.is-hint-from .jar__glass {
  box-shadow: 0 var(--lift) 0 var(--ink), 0 0 0 5px var(--accent);
}
.jar.is-hint-to .jar__glass {
  box-shadow: 0 var(--lift) 0 var(--ink), 0 0 0 5px var(--grass);
}

.jar.is-blocked { animation: nope .34s ease; }
@keyframes nope {
  25% { transform: translateX(-6px) rotate(-3deg); }
  75% { transform: translateX(6px) rotate(3deg); }
}

.shelf .jar__glass { --h: var(--jar-h, 122px); --w: calc(var(--h) * .54); }
.jar--main .jar__glass {
  --h: calc(var(--jar-h, 122px) * 1.64);
  --w: calc(var(--h) * .62);
  border-radius: 11px 11px 26px 26px;
}
.jar--main.is-won .jar__glass { animation: cheer .5s ease .05s 2; }
@keyframes cheer {
  30% { transform: scale(1.07, .94); }
  60% { transform: scale(.97, 1.05); }
}

/* ───────── the shelf ───────── */

.shelf-wrap { margin-top: 8px; padding-bottom: 28px; }
.shelf {
  --shelf-gap: 14px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-end;
  column-gap: var(--shelf-gap);
  row-gap: 46px;          /* a wrapped row needs clearance for its own plank */
  /* Head-room for the lift a picked-up jar does. The shelf can scroll on a
     short window, and without this the top row is clipped mid-lift. */
  padding: 20px 8px 0;
}

/* Eight jars to a row at most, however wide the window is.

   The shelf wraps on available width, which is right up to a point and wrong
   past it: a sixteen-jar board put all sixteen in one line on a landscape
   iPad — small, cramped, with the lower half of the screen empty — and split
   12 and 4 on a desktop. An even pair of rows reads better than either.

   Done with a hard break every eighth jar rather than a max-width on the
   shelf. A width cap has to be expressed in jar widths, and jar width is the
   very thing fitBoard() is bisecting, so the cap moved under the search and
   it settled somewhere absurd — eleven rows of two on a phone. A break
   element is a fixed fact about the markup: it forces a wrap at eight and is
   invisible to the sizing. Narrower windows still wrap sooner on their own. */
.shelf__break { display: none; }
.shelf--capped .shelf__break {
  display: block;
  flex-basis: 100%;
  height: 0;
  margin: 0;
}

.slot { position: relative; }

/* Each jar carries its own length of plank, reaching into the gaps on both
   sides so neighbours join into one continuous shelf. Doing it per jar rather
   than as a single bar underneath means a shelf that wraps to a second row
   gets a plank on both rows instead of leaving the top row in mid-air. */
.shelf .slot::after {
  content: "";
  position: absolute;
  left: calc(var(--shelf-gap) / -2 - 1px);
  right: calc(var(--shelf-gap) / -2 - 1px);
  bottom: -22px;
  height: 20px;
  background: linear-gradient(180deg, var(--wood) 0 55%, var(--wood-edge) 55%);
  border-top: var(--stroke) solid var(--ink);
  border-bottom: var(--stroke) solid var(--ink);
  box-shadow: 0 6px 0 var(--wood-under);
}
/* cap the open ends of the run — for a shelf that wraps, JS also tags every
   row's first and last slot so each row's plank is capped in the middle too. */
.shelf .slot:first-child::after,
.shelf .slot--row-start::after {
  border-left: var(--stroke) solid var(--ink);
  border-radius: 8px 0 0 8px;
}
.shelf .slot:last-child::after,
.shelf .slot--row-end::after {
  border-right: var(--stroke) solid var(--ink);
  border-radius: 0 8px 8px 0;
}

/* ───────── toolbar ───────── */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 30px;
}

.status {
  text-align: center;
  margin: 18px auto 0;
  max-width: 52ch;
  /* Room for two lines always, so a long hint appearing cannot push the
     shelf around or reintroduce a scrollbar. */
  min-height: 2.7em;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink-soft);
}
.status.is-good { color: #1f8f4d; }
.status.is-warn { color: var(--accent-deep); }

.keys {
  text-align: center;
  margin: 12px 0 0;
  font-size: 12.5px;
  color: rgba(43, 33, 66, .5);
}
.keys b { color: var(--ink); font-weight: 800; }

/* ───────── overlays ───────── */

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(43, 33, 66, .5);
  display: grid;
  place-items: center;
  /* Keep the modal off the notch, home indicator and rotated-side sensors. */
  padding:
    calc(20px + env(safe-area-inset-top))
    calc(20px + env(safe-area-inset-right))
    calc(20px + env(safe-area-inset-bottom))
    calc(20px + env(safe-area-inset-left));
  z-index: 30;
  animation: fade .18s ease both;
}
.overlay[hidden] { display: none; }
@keyframes fade { from { opacity: 0; } }

.modal {
  background: var(--paper);
  border: var(--stroke) solid var(--ink);
  border-radius: 22px;
  padding: 28px;
  max-width: 390px;
  width: 100%;
  /* Fit within the overlay content area; scroll if the copy is taller than
     the phone \u2014 how-to on a short window used to be clipped. */
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  text-align: center;
  box-shadow: 0 8px 0 var(--ink);
  animation: pop .34s cubic-bezier(.2, .9, .3, 1.5) both;
}
.modal--wide { max-width: 540px; text-align: left; }
@keyframes pop { from { opacity: 0; transform: scale(.86) translateY(14px); } }

.modal h2 {
  margin: 0 0 6px;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 30px;
  line-height: 1.05;
}
.modal__line { color: var(--ink-soft); font-size: 15px; margin: 12px 0 0; }
.modal__swatch {
  width: 74px; height: 74px;
  margin: 0 auto 16px;
  border-radius: 20px;
  border: var(--stroke) solid var(--ink);
  box-shadow: 0 5px 0 var(--ink);
}
.modal__actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  margin-top: 22px;
  flex-wrap: wrap;
}
/* Two choices of equal weight, one above the other: side by side they read as
   a question and its dismissal, which buying and restoring are not. */
.modal__actions--stack { flex-direction: column; align-items: stretch; }

/* Says what just happened, under the buttons that did it. Empty until then, so
   it takes no room and cannot be read as a warning that was already there. */
.modal__note {
  margin: 14px 0 0;
  min-height: 0;
  font-size: 14px;
  color: var(--ink-soft);
}
.modal__note:empty { margin: 0; }

.stars { font-size: 34px; letter-spacing: 6px; color: var(--gold); margin: 6px 0; }
.stars .off { color: rgba(43, 33, 66, .16); }
.stars span.win { display: inline-block; animation: star-pop .42s cubic-bezier(.2, .9, .3, 1.8) both; }
.stars span.win:nth-child(2) { animation-delay: .13s; }
.stars span.win:nth-child(3) { animation-delay: .26s; }
@keyframes star-pop { from { opacity: 0; transform: scale(0) rotate(-50deg); } }

.rules { margin: 16px 0 0; padding-left: 22px; color: var(--ink-soft); font-size: 15px; }
.rules li { margin-bottom: 10px; }
.rules b { color: var(--ink); }

/* Confetti falls in front of the win card, not behind the scrim — it is the
   celebration, so it should not arrive dimmed. Never interactive. */
.confetti {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 40;
}

/* ───────── narrow screens ───────── */

@media (max-width: 760px) {
  .app {
    padding:
      calc(14px + env(safe-area-inset-top))
      calc(12px + env(safe-area-inset-right))
      calc(14px + env(safe-area-inset-bottom))
      calc(12px + env(safe-area-inset-left));
  }
  .board { gap: 18px; }
  .goal { width: 100%; max-width: 330px; }
  .shelf { --shelf-gap: 9px; row-gap: 36px; }

  .toolbar { gap: 7px; }
  .toolbar .btn { padding: 9px 11px; font-size: 15px; }
  .goal { padding: 12px; }
  #screen-levels .level-grid { gap: 7px; }
  .tile__no { font-size: 17px; }
  .choice { padding: 14px; gap: 12px; }
  .choice__name { font-size: 22px; }
  .subhead { gap: 8px; margin-bottom: 16px; }
  .subhead__balance { flex-basis: 0; }
  .subhead__title h2 { font-size: 22px; }
  .swatch { width: 38px; height: 38px; flex-basis: 38px; border-radius: 11px; }
  .topbar { padding: 9px 11px; gap: 8px; }
  .topbar__title h2 { font-size: 17px; }
  /* keep "Normal · seed 905063537" on one line so the bar stays short */
  .topbar__title span { font-size: 11px; }
  .stat { min-width: 46px; padding: 4px 6px; }
  .keys { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Wider screens \u2014 iPad landscape, desktop \u2014 have room for the menu to
   read like a title screen rather than a compact phone list. */
@media (min-width: 761px) {
  #screen-home .masthead { margin: 8px 0 22px; }
  .masthead h1 { font-size: clamp(56px, 8vw, 96px); }
  .masthead__tag { font-size: 19px; max-width: 36ch; }
  .choices { max-width: 720px; gap: 18px; }
  .choice { padding: 22px 26px; gap: 20px; }
  .choice__name { font-size: 30px; }
  .choice__sub { font-size: 17px; }
  .choice__jar,
  .choice__shelf,
  .choice__mix,
  .choice__cal { flex: 0 0 60px; width: 60px; height: 82px; }
  .choice__cal { gap: 8px; padding: 25px 11px 12px; }
  .choice__cal-top { height: 17px; }
  .choice__mix i { width: 34px; height: 34px; }
  .choice__mix i:nth-child(1) { top: 10px; }
  .choice__mix i:nth-child(2) { top: 10px; }
  .choice__mix i:nth-child(3) { left: 13px; top: 34px; }
  .choice__go { font-size: 38px; }
}

/* Keyboard shortcuts don't apply inside a native shell, so drop that line
   entirely. Set by app.js when window.Capacitor is present. */
body.is-native .keys { display: none; }

/* The game selector sits above the settings row and reads as its own choice,
   so the two rows are not mistaken for one long list of difficulties. */
.difficulty--modes {
  margin-bottom: 10px;
  padding-bottom: 12px;
  border-bottom: 2px dashed rgba(43, 33, 66, .18);
}

/* ───────── press and hold to erase ───────── */

.hold {
  position: relative;
  display: block;
  width: 100%;
  margin: 4px 0 2px;
  padding: 16px 18px;
  border: var(--stroke) solid var(--ink);
  border-radius: 16px;
  background: var(--paper-warm);
  box-shadow: 0 5px 0 var(--ink);
  font: inherit;
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 19px;
  color: var(--ink);
  cursor: pointer;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;          /* holding must not scroll the page instead */
}
.hold:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.hold:active { transform: translateY(3px); box-shadow: 0 2px 0 var(--ink); }

/* The fill is the whole point: it says how much longer, and it is driven from
   script rather than a transition so that letting go stops it dead. */
.hold__fill {
  position: absolute;
  inset: 0;
  transform-origin: left center;
  transform: scaleX(var(--held, 0));
  background: linear-gradient(90deg, #ff8fb1, var(--danger));
  border-radius: 0;
}
.hold__label { position: relative; }
.hold.is-done .hold__fill { background: var(--grass); }

/* ───────── settings modal ───────── */

.settings-list {
  display: grid;
  gap: 12px;
  margin: 20px 0 0;
  text-align: left;
}
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  border: 2px solid var(--ink);
  border-radius: 12px;
  background: var(--paper-warm);
}
.settings-row > span {
  font-family: "Baloo 2", sans-serif;
  font-weight: 800;
  font-size: 17px;
  color: var(--ink);
}

/* Marked out from the rows that merely toggle something, so nobody reaches for
   it by accident. It has to come after .settings-row, whose border shorthand
   would otherwise reset the colour set here. */
.settings-row--danger { border-color: var(--danger); }
.settings-row--danger > span { color: var(--danger); }
.settings-row .btn {
  padding: 8px 16px;
  min-width: 72px;
  font-size: 15px;
}
.settings-row .btn[aria-pressed="true"] {
  background: var(--gold);
}

/* Home menu wants to stay in view without scrolling, so let it shrink on
   short windows rather than push the footer off screen. */
#screen-home.is-active {
  justify-content: flex-start;
  overflow-y: auto;
  overscroll-behavior: contain;
}
#screen-home .masthead { margin: 4px 0 18px; }
/* `flex: 0 1 auto` alone does not shrink a flex item below its content, so on
   a short window the cards kept their full height and pushed the footer off
   the bottom of a screen that cannot scroll — How to play, Settings and
   Remove Ads were unreachable on an iPhone SE.

   The scrolling goes on the screen rather than on the card list, and that is
   the whole point of the rule below. Scrolling the list itself works, but a
   scroll container clips to its padding box, and these cards are drawn with a
   6px hard shadow sitting outside theirs — so the bottom card lost its shadow
   and every card lost the outer half of its focus ring. Putting the scroll on
   the screen leaves the cards in the middle of the scrolled content, nowhere
   near a clipping edge. */
#screen-home .choices { flex: 0 0 auto; }
#screen-home .menu-foot {
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 14px;
  /* The footer is the last thing in a screen that scrolls, so its bottom edge
     is a clipping edge. These buttons carry a hard shadow --lift below their
     border box, and a focus ring 5px outside it, so both were being shaved
     off. Same trap the cards fell into; the room has to be asked for. */
  padding-bottom: calc(var(--lift) + 2px);
}
/* Landscape has width to spare and height it cannot spare, so the menu goes
   two across instead of four down. This is what makes the four modes fit an
   iPad held sideways: in Safari the toolbar takes about a hundred points off
   an already short window, and a single column put the fourth card and the
   whole footer below the fold.

   Keyed to orientation rather than to a height threshold on purpose. A
   threshold would flip the layout between two devices that feel identical, or
   on the same device when a subtitle happens to wrap — where "sideways means
   two across" is a rule someone can predict. min-width keeps phones in
   landscape out of it, where two columns would be two cramped ones. */
@media (min-width: 761px) and (orientation: landscape) {
  #screen-home .choices {
    grid-template-columns: 1fr 1fr;
    max-width: 940px;
    /* Two rows leave real space below on a tall landscape window. The footer
       is already pinned to the bottom by its own auto margin, so giving the
       cards auto margins too shares the slack above and below them instead of
       pooling it all in one gap under the grid. */
    margin-top: auto;
    margin-bottom: auto;
  }
}

/* A phone held sideways is the extreme of the same problem: 390 points of
   height against a menu that wants 486. Two columns alone do not close that,
   so the title screen gives up the parts that are decoration — the three jars
   and the tagline — and keeps the name and the four modes, which are what the
   screen is for. Above this height there is room for all of it, and the
   shortest iPad landscape window is comfortably clear of the threshold. */
@media (orientation: landscape) and (max-height: 500px) {
  #screen-home .masthead { margin: 0 0 10px; }
  #screen-home .masthead__mark { display: none; }
  #screen-home .masthead__tag { display: none; }
  #screen-home .masthead h1 { font-size: clamp(28px, 5vw, 40px); }
  #screen-home .choices { grid-template-columns: 1fr 1fr; max-width: 940px; gap: 10px; }
  #screen-home .choice { padding: 12px 18px; gap: 14px; }
  /* Past 760px wide the tablet type sizes apply, and on a phone lying down
     they are what stops the four fitting. Wind them back here rather than
     moving that breakpoint, which the tablet layout wants where it is. */
  #screen-home .choice__name { font-size: 20px; }
  #screen-home .choice__sub { font-size: 14px; }
  #screen-home .choice__jar,
  #screen-home .choice__shelf,
  #screen-home .choice__mix,
  #screen-home .choice__cal { flex: 0 0 40px; width: 40px; height: 54px; }
  #screen-home .choice__cal { gap: 5px; padding: 16px 7px 8px; }
  #screen-home .choice__cal-top { height: 11px; }
  #screen-home .choice__mix i { width: 22px; height: 22px; }
  #screen-home .choice__mix i:nth-child(1),
  #screen-home .choice__mix i:nth-child(2) { top: 5px; }
  #screen-home .choice__mix i:nth-child(3) { left: 9px; top: 23px; }
  #screen-home .menu-foot { padding-top: 8px; }
}

/* The title-screen masthead assumes a tall window. With four cards on the
   menu a 1040x860 desktop no longer has room for both, and the fourth card
   ended up cut in half behind the footer. Rather than shrink the title
   everywhere — it is the thing that makes the wide layout read like a title
   screen — trade it back only on windows too short to hold both. */
@media (min-width: 761px) and (max-height: 920px) {
  #screen-home .masthead { margin: 2px 0 12px; }
  #screen-home .masthead h1 { font-size: clamp(44px, 5.4vw, 64px); }
  #screen-home .masthead__mark { padding-top: 16px; margin-bottom: 8px; }
  #screen-home .masthead__tag { font-size: 17px; margin-top: 6px; }
  #screen-home .choices { gap: 14px; }
  #screen-home .choice { padding: 16px 26px; }
}

/* The random card grew a jar stepper, and on a 320px phone that pushed its
   last line past the bottom of a screen that could not scroll — the same trap
   the home menu fell into. Let the screen scroll rather than clip. */
#screen-random.is-active {
  justify-content: flex-start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ───────── consent ─────────
   A bar at the foot rather than a dialog over the board: the question is not
   urgent enough to stop somebody playing, and the game opens straight into a
   level. It sits above the safe-area inset so it clears the gesture bar. */
.consent {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  padding: 14px 16px calc(14px + env(safe-area-inset-bottom));
  background: var(--paper);
  border-top: var(--stroke) solid var(--ink);
  box-shadow: 0 -6px 18px rgba(43, 33, 66, .16);
}
.consent[hidden] { display: none; }
.consent__text {
  margin: 0 0 10px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink-soft);
  max-width: 46ch;
}
.consent__actions { display: flex; gap: 10px; flex-wrap: wrap; }
.consent__actions .btn { flex: 1 1 auto; }
@media (min-width: 620px) {
  .consent {
    left: 50%;
    right: auto;
    bottom: 18px;
    transform: translateX(-50%);
    width: min(560px, calc(100vw - 36px));
    border: var(--stroke) solid var(--ink);
    border-radius: 16px;
  }
}
