/* The Ultimate Cookbook — white paper, black type, nothing else.
 *
 * Every colour in the app comes from the tokens below and from nowhere else,
 * so a theme is one block of overrides at the bottom of this file. If you find
 * yourself writing a literal colour anywhere between here and there, it is a
 * missing token: add it to all the themes rather than hardcoding it once.
 */

/* The default theme, Historic Kitchen, lives here rather than in a block of
   its own: it is what the app wears before anyone has chosen anything, and
   every other theme overrides these. Its photograph is monochrome, so this
   is still very close to the white page and black type the brief asks for. */
:root {
  --ink: #2b2620;            /* body text, rules, the solid blocks */
  --paper: #f0ece4;          /* the page behind everything */
  --faint: #6a6055;          /* de-emphasis only; must stay AA on --paper */
  --rule: #2b2620;           /* hard borders */
  --hair: rgba(43, 38, 32, 0.18);   /* light dividers */
  --wash: #e5e0d6;           /* hover fill */
  --accent: #7a6248;         /* the one non-neutral: streak, celebration */
  --accent-ink: #ffffff;     /* text placed on --accent */
  --confetti: #2b2620 #7a6248 #a89880 #4f4335;   /* celebration palette */
  --backdrop: 0.13;          /* how strongly the point cloud prints, 0 to 1 */
  --art: url("../backgrounds/historic.jpg");     /* the theme's own kitchen */
  --veil: 0.86;              /* how much --paper is washed over it, 0 to 1 */

  --type: "Courier New", Courier, "Nimbus Mono PS", "Liberation Mono", monospace;
  --gap: 14px;
  --max: 760px;
}

* { box-sizing: border-box; }

/* The page colour lives on the root, not on the body, so the backdrop canvas
   can sit between the two. */
html {
  -webkit-text-size-adjust: 100%;
  background: var(--paper);
}

body {
  margin: 0;
  padding: 0 var(--gap) calc(48px + env(safe-area-inset-bottom));
  background: transparent;
  color: var(--ink);
  font-family: var(--type);
  font-size: 15px;
  line-height: 1.55;
  max-width: var(--max);
  margin-inline: auto;
  -webkit-tap-highlight-color: transparent;
}

/* ── the backdrop ─────────────────────────────────────────────────────
   Two layers, and never both at once.

   #themebg is the theme's photograph, washed over with that theme's own
   --paper at --veil so body text keeps its contrast on top of it. A
   photograph at full strength would make the page unreadable, which is the
   whole reason the veil is a token each theme sets for itself: the dark
   kitchens need far less of it than the pale ones.

   #backdrop is the point cloud, drawn by js/backdrop.js. It shows only when
   the photograph is off, so turning art off leaves a moving room rather than
   a flat colour. If WebGL is missing the canvas stays empty and the page is
   exactly as it was. */

#themebg, #backdrop {
  position: fixed;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

#themebg {
  z-index: -2;
  background-image: var(--art, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* The wash, in two parts, both flat colours so the contrast is something
   that can be reasoned about rather than guessed at.

   ::before is the whole-viewport veil. ::after is a second, narrower band
   over the column the words actually occupy — because a photograph is not
   evenly bright, and the failure mode is a caption landing on the one pale
   patch in the picture. The margins keep the thinner veil, which is where
   most of the photograph is anyway and where nothing has to be read. */
#themebg::before,
#themebg::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--paper);
}

#themebg::before {
  left: 0;
  right: 0;
  opacity: var(--veil, 0.88);
}

#themebg::after {
  left: 50%;
  width: min(100%, calc(var(--max) + 8vw));
  transform: translateX(-50%);
  opacity: var(--veil-column, 0.5);
  /* Feathered, so the band never reads as a panel with an edge. The black
     here is not a colour anything is painted in — a mask only reads alpha,
     and black means "keep this part". It is exempt from the no-literals
     rule for the same reason: there is no theme for it to follow. */
  -webkit-mask-image: linear-gradient(to right, transparent, black 14%, black 86%, transparent);
  mask-image: linear-gradient(to right, transparent, black 14%, black 86%, transparent);
}

#backdrop { z-index: -1; }

/* Colour only: no photograph, and the point cloud takes the room back. */
:root[data-art="off"] #themebg { display: none; }

/* The photograph is the room. Two kitchens at once is one too many. */
:root[data-art="on"] #backdrop { display: none; }

button, select, input {
  font: inherit;
  color: inherit;
}

/* ── masthead ─────────────────────────────────────────────────────── */

.masthead {
  padding: calc(26px + env(safe-area-inset-top)) 0 14px;
  text-align: center;
  border-bottom: 3px double var(--rule);
}

.masthead h1 {
  margin: 0;
  font-size: clamp(20px, 6.5vw, 30px);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.strap {
  margin: 6px 0 0;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  min-height: 1.2em;
}

/* ── account bar ──────────────────────────────────────────────────── */

/* Sticky, so the level is on screen whichever tab you are in and however far
   down you have scrolled. It is one button: the whole strip opens the account. */
.accountbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  width: 100%;
  padding: 9px 0 8px;
  background: var(--paper);
  border: 0;
  border-bottom: 1px solid var(--rule);
  cursor: pointer;
  text-align: left;
}

.accountbar:hover .ab-name,
.accountbar:focus-visible .ab-name { text-decoration: underline; }

.accountbar:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

.ab-label, .ab-rank, .ab-xp {
  display: block;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}

.ab-name {
  display: block;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 34vw;
}

.ab-who { min-width: 0; }

.ab-level {
  flex: 0 1 220px;
  min-width: 132px;
  text-align: right;
}

.ab-head {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 8px;
}

.ab-lvl {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.ab-lvl strong {
  font-size: 17px;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.ab-rank { font-size: 10px; }

.ab-track {
  display: block;
  height: 7px;
  margin: 4px 0 3px;
  border: 1px solid var(--rule);
  padding: 1px;
}

.ab-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--ink);
  transition: width 0.4s ease;
}

/* Sliding backwards is worth seeing. Hatched fill, same as a recipe you
   cannot cook yet: the bar is telling you something is missing. */
.accountbar.rusting .ab-fill {
  background-image: repeating-linear-gradient(45deg, var(--ink) 0 3px, var(--paper) 3px 6px);
}

.accountbar.rusting .ab-rank::after { content: " · rusting"; }

/* A level change is the one moment this bar earns a flicker of attention. */
.accountbar.bumped { animation: bump 1.1s steps(1) 2; }

@keyframes bump {
  0%, 49% { background: var(--ink); color: var(--paper); }
  50%, 100% { background: var(--paper); color: var(--ink); }
}

.accountbar.bumped .ab-label,
.accountbar.bumped .ab-rank,
.accountbar.bumped .ab-xp { color: inherit; }

.accountbar.bumped .ab-track { border-color: currentColor; }
.accountbar.bumped .ab-fill { background: currentColor; background-image: none; }

@media (prefers-reduced-motion: reduce) {
  .accountbar.bumped { animation: none; }
  .ab-fill { transition: none; }
}

/* ── tabs ─────────────────────────────────────────────────────────── */

.tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--rule);
}

.tab {
  flex: 1;
  padding: 13px 4px;
  background: none;
  border: 0;
  border-right: 1px solid var(--hair);
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 12px;
}

.tab:last-child { border-right: 0; }

.tab[aria-selected="true"] {
  background: var(--ink);
  color: var(--paper);
  font-weight: 700;
}

.view { padding-top: 22px; }

/* ── the randomise button ─────────────────────────────────────────── */

.randomise {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: 100%;
  padding: 26px 12px;
  background: var(--ink);
  color: var(--paper);
  border: 2px solid var(--ink);
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.26em;
  font-size: clamp(15px, 4.6vw, 20px);
  font-weight: 700;
}

.randomise:hover,
.randomise:focus-visible {
  background: var(--paper);
  color: var(--ink);
  outline: none;
}

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

.randomise-mark {
  font-size: 1.1em;
  letter-spacing: 0;
}

.randomise.spinning .randomise-mark {
  animation: spin 0.45s linear infinite;
  display: inline-block;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .randomise.spinning .randomise-mark { animation: none; }
}

/* ── filters ──────────────────────────────────────────────────────── */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 16px;
}

.filters label {
  flex: 1 1 190px;
  display: block;
}

.filter-label {
  display: block;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 3px;
}

select, .search {
  width: 100%;
  padding: 9px 10px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
  font-size: 14px;
  appearance: none;
}

select {
  background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%),
                    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: right 14px center, right 9px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 28px;
}

.search { margin-bottom: 14px; }

select:focus-visible, .search:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 1px;
}

/* ── the dealt card ───────────────────────────────────────────────── */

.dice {
  margin-top: 22px;
  border: 2px solid var(--rule);
  padding: 20px 18px;
  cursor: pointer;
}

.dice:hover { background: var(--wash); }

.dice-name {
  font-size: clamp(19px, 5.6vw, 26px);
  font-weight: 700;
  line-height: 1.2;
  margin: 0 0 8px;
  letter-spacing: 0.01em;
}

.dice-meta {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 16px;
}

.dice-open {
  margin: 16px 0 0;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border-top: 1px solid var(--hair);
  padding-top: 10px;
}

.empty {
  margin-top: 24px;
  padding: 18px;
  border: 1px dashed var(--rule);
  text-align: center;
  font-size: 14px;
}

/* ── readiness ────────────────────────────────────────────────────── */

.gauge {
  display: flex;
  align-items: center;
  gap: 10px;
}

.gauge-bar {
  flex: 1;
  height: 14px;
  border: 1px solid var(--rule);
  padding: 2px;
}

.gauge-fill {
  height: 100%;
  background: var(--ink);
  transition: width 0.35s ease;
}

.gauge-fill.striped {
  background-image: repeating-linear-gradient(45deg, var(--ink) 0 3px, var(--paper) 3px 6px);
}

.gauge-pct {
  font-weight: 700;
  font-size: 14px;
  min-width: 4.2ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.verdict {
  margin: 12px 0 0;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.verdict.go {
  display: inline-block;
  background: var(--ink);
  color: var(--paper);
  padding: 4px 10px;
  font-weight: 700;
}

/* ── kitchen ──────────────────────────────────────────────────────── */

.stockbar {
  border: 2px solid var(--rule);
  padding: 12px 14px;
  margin-bottom: 14px;
}

.stockbar-line { font-size: 14px; }
.stockbar-line strong { font-size: 20px; }
.stockbar-sub { color: var(--faint); font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; }

.kitchen-actions {
  display: flex;
  gap: 10px;
  margin-bottom: 14px;
}

.btn {
  flex: 1;
  padding: 11px 8px;
  background: var(--paper);
  border: 1px solid var(--rule);
  cursor: pointer;
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.btn:hover { background: var(--ink); color: var(--paper); }

.zones {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 18px;
}

.zone {
  flex: 1 1 auto;
  padding: 9px 12px;
  background: var(--paper);
  border: 1px solid var(--rule);
  cursor: pointer;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

.zone[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  font-weight: 700;
}

.zone .zone-n { opacity: 0.7; font-size: 10px; }

.group { margin-bottom: 22px; }

.group h3 {
  margin: 0 0 10px;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 5px;
  font-weight: 700;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.chip {
  padding: 8px 11px;
  background: var(--paper);
  color: var(--ink);
  border: 1px dashed var(--rule);
  cursor: pointer;
  font-size: 13px;
  line-height: 1.2;
  text-align: left;
}

.chip::before {
  content: "\2610\00a0\00a0";     /* empty ballot box */
}

.chip[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-style: solid;
}

.chip[aria-pressed="true"]::before {
  content: "\2611\00a0\00a0";     /* ticked ballot box */
}

.chip:hover { border-style: solid; }

.zone-note {
  font-size: 12px;
  color: var(--faint);
  margin: -6px 0 16px;
}

/* ── index listing ────────────────────────────────────────────────── */

.tally {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 10px;
}

.listing {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--rule);
}

.entry {
  display: block;
  width: 100%;
  text-align: left;
  padding: 14px 2px;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hair);
  cursor: pointer;
}

.entry:hover { background: var(--wash); }

.entry-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}

.entry-name { font-weight: 700; font-size: 15px; }

.entry-time {
  font-size: 12px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.entry-meta {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 2px 0 8px;
}

/* ── recipe sheet ─────────────────────────────────────────────────── */

.sheet {
  position: fixed;
  inset: 0;
  background: var(--paper);
  overflow-y: auto;
  z-index: 20;
  overscroll-behavior: contain;
}

.sheet-inner {
  max-width: var(--max);
  margin-inline: auto;
  padding: calc(20px + env(safe-area-inset-top)) var(--gap) calc(60px + env(safe-area-inset-bottom));
}

.sheet-close {
  position: sticky;
  top: 0;
  float: right;
  width: 42px;
  height: 42px;
  background: var(--paper);
  border: 1px solid var(--rule);
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  z-index: 2;
}

.sheet-close:hover { background: var(--ink); color: var(--paper); }

.r-title {
  margin: 0 52px 4px 0;
  font-size: clamp(21px, 6.2vw, 30px);
  line-height: 1.15;
  letter-spacing: 0.01em;
}

.r-meta {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 18px;
}

.r-section {
  margin-top: 26px;
  padding-top: 14px;
  border-top: 1px solid var(--rule);
}

.r-section > h3 {
  margin: 0 0 12px;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.checklist { list-style: none; margin: 0; padding: 0; }

.checklist li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--hair);
}

.checklist li:last-child { border-bottom: 0; }

.tickbox {
  flex: none;
  width: 26px;
  height: 26px;
  margin-top: 1px;
  background: var(--paper);
  border: 1px solid var(--rule);
  cursor: pointer;
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  padding: 0;
}

.tickbox.on { background: var(--ink); color: var(--paper); }
.tickbox:hover { outline: 2px solid var(--ink); outline-offset: 1px; }

.item-name { font-weight: 700; }
.item-qty { color: var(--faint); }
.item-sub { font-size: 12px; color: var(--faint); display: block; }

/* Held items read solid black; missing ones fade back. Deliberately not a
   strikethrough — struck-out text reads as "done", which is the opposite. */
.missing .item-name { font-weight: 400; color: var(--faint); }
.missing .item-qty { color: var(--faint); }
.optional .item-name { font-weight: 400; }
.tag-optional {
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--hair);
  padding: 0 4px;
  color: var(--faint);
}

.steps { margin: 0; padding-left: 1.4em; }
.steps li { margin-bottom: 14px; }

.r-note {
  margin-top: 22px;
  border-left: 3px solid var(--rule);
  padding: 4px 0 4px 14px;
  font-size: 14px;
}

.r-note strong {
  display: block;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 3px;
}

.shortfall {
  margin: 12px 0 0;
  font-size: 13px;
}

/* ── start / finish ───────────────────────────────────────────────── */

/* The block at the top of a recipe: press Start when you begin, Finish when
   it is on the plate. Everything the app knows about how long the dish
   really takes comes from these two presses. */
.cookblock {
  margin: 18px 0 0;
  border: 2px solid var(--rule);
  padding: 14px;
}

.cookblock-acts {
  display: flex;
  gap: 8px;
}

.bigbtn {
  flex: 1;
  padding: 15px 10px;
  background: var(--ink);
  color: var(--paper);
  border: 2px solid var(--ink);
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.bigbtn:hover, .bigbtn:focus-visible {
  background: var(--paper);
  color: var(--ink);
  outline: none;
}

.bigbtn.ghost {
  flex: 0 1 auto;
  background: var(--paper);
  color: var(--ink);
  font-weight: 400;
  letter-spacing: 0.13em;
}

.bigbtn.ghost:hover { background: var(--ink); color: var(--paper); }

.bigbtn[disabled] {
  background: var(--paper);
  color: var(--faint);
  border-style: dashed;
  border-color: var(--faint);
  cursor: not-allowed;
}

.cookblock-note {
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--faint);
}

.cookblock-note strong { color: var(--ink); }

.cookblock-live {
  margin: 0 0 12px;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.cookblock-live .running {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  display: block;
}

/* The true-time line: the book's claim, and what it has actually taken you. */
.truetime {
  margin: 12px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--hair);
  font-size: 13px;
}

.truetime .over { font-weight: 700; }

/* ── the bar for a cook in progress ───────────────────────────────── */

/* Fixed to the bottom: you started this dish on the recipe sheet, but you
   will be anywhere in the app by the time it is plated. */
.cookbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  background: var(--ink);
  color: var(--paper);
  border-top: 2px solid var(--ink);
  padding-bottom: env(safe-area-inset-bottom);
}

.cookbar-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: var(--max);
  margin-inline: auto;
  padding: 9px var(--gap);
}

.cookbar-what { flex: 1 1 auto; min-width: 0; }

.cookbar-label {
  display: block;
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.75;
}

.cookbar-name {
  display: block;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cookbar-clock {
  flex: none;
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

.cookbar-acts {
  flex: none;
  display: flex;
  gap: 6px;
}

.cookbar .btn {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--paper);
  padding: 9px 10px;
}

.cookbar .btn:hover { background: var(--paper); color: var(--ink); }

.cookbar .cookbar-finish {
  background: var(--paper);
  color: var(--ink);
  font-weight: 700;
}

.cookbar .cookbar-finish:hover { background: var(--ink); color: var(--paper); }

/* Nothing should sit under the bar while it is up. */
body.cooking { padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
body.cooking .sheet-inner { padding-bottom: calc(110px + env(safe-area-inset-bottom)); }

@media (max-width: 420px) {
  .cookbar-clock { font-size: 17px; }
  .cookbar .btn { font-size: 10px; letter-spacing: 0.08em; }
}

/* ── account sheet ────────────────────────────────────────────────── */

.a-level {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0 52px 2px 0;
}

.a-level .a-no {
  font-size: clamp(34px, 12vw, 52px);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.a-level .a-rank {
  font-size: clamp(15px, 4.4vw, 20px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.a-name-field {
  display: flex;
  gap: 8px;
  align-items: flex-end;
}

.a-name-field label { flex: 1; }

.a-name-field .btn { flex: 0 0 auto; padding: 10px 14px; }

.figures {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--hair);
  border: 1px solid var(--rule);
  margin-top: 4px;
}

.figure {
  background: var(--paper);
  padding: 11px 12px;
}

.figure b {
  display: block;
  font-size: 21px;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.figure span {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.rules { margin: 0; padding: 0; list-style: none; font-size: 13px; }

.rules li {
  display: flex;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--hair);
}

.rules li:last-child { border-bottom: 0; }

.rules .swing {
  flex: none;
  min-width: 5.5ch;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.history { list-style: none; margin: 0; padding: 0; }

.history li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--hair);
  font-size: 13px;
}

.history .h-name { flex: 1; font-weight: 700; }
.history .h-when { font-size: 11px; color: var(--faint); display: block; font-weight: 400; letter-spacing: 0.06em; text-transform: uppercase; }
.history .h-time { font-variant-numeric: tabular-nums; white-space: nowrap; }
.history .h-xp { min-width: 5ch; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.history .gave-up .h-name { font-weight: 400; color: var(--faint); }

.ladder { list-style: none; margin: 0; padding: 0; font-size: 13px; }

.ladder li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
  border-bottom: 1px solid var(--hair);
  color: var(--faint);
}

.ladder li.here {
  color: var(--ink);
  font-weight: 700;
  border-bottom-color: var(--rule);
}

.ladder .l-at { font-variant-numeric: tabular-nums; }

/* ── the streak ───────────────────────────────────────────────────── */

.ab-streak {
  display: block;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 42vw;
}

.ab-streak.live { color: var(--accent); font-weight: 700; }
.ab-streak.risk { color: var(--ink); }
.ab-streak.risk::after { content: " ·"; }

/* Weeks, drawn as a row of boxes: filled if you cooked, hatched if a rescue
   covered it, empty if it was missed. Twelve of them is a season. */
.weekstrip {
  display: flex;
  gap: 3px;
  margin: 10px 0 4px;
  align-items: flex-end;
}

.weekstrip span {
  flex: 1 1 0;
  height: 26px;
  border: 1px solid var(--rule);
  background: var(--paper);
}

.weekstrip .w-cooked { background: var(--ink); }

/* Ringed, not just coloured: in the paper theme the accent *is* the ink, so
   an earned week has to be told apart by shape rather than by hue. */
.weekstrip .w-earned {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--paper);
}
.weekstrip .w-rescued {
  background-image: repeating-linear-gradient(45deg, var(--ink) 0 3px, var(--paper) 3px 6px);
}
.weekstrip .w-missed { border-style: dashed; border-color: var(--faint); }
.weekstrip .w-open { border-style: dotted; }

.weekstrip-key {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

.streak-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

.streak-no {
  font-size: clamp(30px, 10vw, 44px);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.streak-word {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.rescues {
  display: flex;
  gap: 5px;
  align-items: center;
  margin-top: 10px;
  font-size: 12px;
}

.rescue-pip {
  display: inline-block;
  width: 15px;
  height: 15px;
  border: 1px solid var(--rule);
  background: var(--paper);
}

.rescue-pip.held { background: var(--accent); border-color: var(--accent); }

.thisweek {
  margin: 12px 0 0;
  padding: 11px 12px;
  border: 1px solid var(--rule);
  font-size: 13px;
}

.thisweek.risk { border-width: 2px; }
.thisweek strong { font-weight: 700; }

/* ── the teaching layer ───────────────────────────────────────────── */

.coach {
  margin-top: 22px;
  border: 1px solid var(--rule);
  padding: 13px 14px;
}

.coach + .coach { margin-top: 12px; }

.coach > h3 {
  margin: 0 0 10px;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 700;
}

.coach-lead {
  margin: 0 0 10px;
  font-size: 12px;
  color: var(--faint);
}

.words { list-style: none; margin: 0; padding: 0; }

.words li {
  padding: 7px 0;
  border-bottom: 1px solid var(--hair);
  font-size: 13px;
}

.words li:last-child { border-bottom: 0; }
.words dfn { font-style: normal; font-weight: 700; display: block; }

.safety { list-style: none; margin: 0; padding: 0; font-size: 13px; }

.safety li {
  display: flex;
  gap: 9px;
  padding: 6px 0;
  border-bottom: 1px solid var(--hair);
}

.safety li:last-child { border-bottom: 0; }
.safety li::before { content: "!"; font-weight: 700; flex: none; width: 1.2ch; }

.jobs { list-style: none; margin: 0; padding: 0; font-size: 13px; }

.jobs li {
  display: flex;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--hair);
}

.jobs li:last-child { border-bottom: 0; }

.jobs .job-step {
  flex: none;
  min-width: 6.5ch;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding-top: 2px;
}

.jobs .job-care { display: block; font-size: 12px; color: var(--faint); }

.gap-figures {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13px;
}

.gap-flag {
  border: 1px solid var(--rule);
  padding: 3px 8px;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* ── onboarding ───────────────────────────────────────────────────── */

.onboarding {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--paper);
  color: var(--ink);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.ob-inner {
  min-height: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding: calc(28px + env(safe-area-inset-top)) var(--gap) calc(24px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
}

.ob-body { flex: 1; }

.ob-eyebrow {
  margin: 0 0 10px;
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--faint);
}

.ob-title {
  margin: 0 0 14px;
  font-size: clamp(22px, 7vw, 32px);
  line-height: 1.15;
  letter-spacing: 0.01em;
}

.ob-title:focus { outline: none; }

.ob-lead { margin: 0 0 16px; font-size: 14px; }

.ob-points { margin: 0 0 16px; padding-left: 1.2em; font-size: 14px; }
.ob-points li { margin-bottom: 8px; }

.ob-cards {
  display: grid;
  gap: 10px;
  grid-template-columns: 1fr;
}

.ob-cards-theme { grid-template-columns: repeat(2, 1fr); }

.ob-card {
  display: block;
  width: 100%;
  text-align: left;
  padding: 13px 14px;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  cursor: pointer;
}

.ob-card:hover { background: var(--wash); }

.ob-card[aria-pressed="true"] {
  border-width: 3px;
  padding: 11px 12px;
}

.ob-card[aria-pressed="true"] .ob-card-name::after {
  content: " \2713";
}

.ob-card-name {
  display: block;
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 3px;
}

.ob-card-who {
  display: block;
  font-size: 12px;
  color: var(--faint);
  line-height: 1.45;
}

.ob-card-gets {
  margin: 9px 0 0;
  padding-left: 1.1em;
  font-size: 12px;
}

.ob-card-gets li { margin-bottom: 3px; }

/* The palette preview on each theme card: page, accent and ink, in roughly
   the proportion the app uses them. The colours are the whole of what the
   card has to say, so this is finished — it is not holding space for
   anything else.

   A theme that later gets a background image overrides background-image in
   its own .ob-swatch[data-swatch="…"] rule at the foot of this file; the
   bands below are the fallback whenever there is no image. */
.ob-swatch {
  display: block;
  height: 82px;
  margin-bottom: 9px;
  border: 1px solid var(--rule);
  overflow: hidden;
  background-color: var(--sw-paper, var(--paper));
  background-image: var(--sw-bands);
  background-size: cover;
  background-position: center;

  /* Declared here, on the card, and deliberately not on :root. A custom
     property resolves its own var() references on the element that declares
     it — so hoisting this to :root would bake in whichever theme is
     currently applied and paint all six cards the same. Declared here it is
     computed per card, against that card's own --sw-* values. */
  --sw-bands: linear-gradient(to right,
    var(--sw-paper, var(--paper)) 0 40%,
    var(--sw-accent, var(--accent)) 40% 60%,
    var(--sw-ink, var(--ink)) 60% 100%);
}

/* With art on, the card wears its own photograph over the bands. The bands
   stay underneath, so an image that is slow, missing or broken leaves the
   palette showing rather than a hole. */
:root[data-art="on"] .ob-swatch {
  background-image: var(--sw-art, none), var(--sw-bands);
}

/* The art switch sits under the cards it governs, separated from them so it
   reads as a setting rather than as a seventh theme. */
.ob-art {
  margin-top: 4px;
  border-top: 1px solid var(--hair);
  padding-top: 14px;
}

.ob-chips { display: flex; gap: 8px; margin-bottom: 20px; }

.ob-chip {
  flex: 1;
  padding: 14px 6px;
  background: var(--paper);
  border: 1px solid var(--rule);
  cursor: pointer;
  font-size: 16px;
  font-weight: 700;
}

.ob-chip[aria-pressed="true"] { background: var(--ink); color: var(--paper); }

.ob-note {
  border: 1px solid var(--rule);
  padding: 14px;
  font-size: 13px;
}

.ob-note h3 {
  margin: 0 0 10px;
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.ob-note p { margin: 10px 0 0; color: var(--faint); }
.ob-note .ob-points { font-size: 13px; margin-bottom: 0; }

.ob-field { display: block; margin-bottom: 18px; }

.ob-toggles { display: grid; gap: 8px; }

.ob-toggle {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  width: 100%;
  text-align: left;
  padding: 11px 12px;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  cursor: pointer;
  font-size: 13px;
}

.ob-toggle-box {
  flex: none;
  width: 22px;
  height: 22px;
  border: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}

.ob-toggle[aria-pressed="true"] .ob-toggle-box { background: var(--ink); color: var(--paper); }

.ob-toggle-note { display: block; font-size: 12px; color: var(--faint); margin-top: 2px; }

.ob-foot {
  position: sticky;
  bottom: 0;
  background: var(--paper);
  padding-top: 16px;
  margin-top: 22px;
  border-top: 1px solid var(--rule);
}

.ob-dots { display: flex; gap: 5px; justify-content: center; margin-bottom: 12px; }

.ob-dot {
  width: 7px;
  height: 7px;
  border: 1px solid var(--rule);
  background: var(--paper);
}

.ob-dot.on { background: var(--ink); }

.ob-nav { display: flex; gap: 8px; align-items: stretch; }
.ob-nav .btn { flex: 0 0 auto; padding: 14px 16px; }
.ob-nav .bigbtn { flex: 1; }

@media (min-width: 620px) {
  .ob-cards { grid-template-columns: repeat(2, 1fr); }
  .ob-cards-theme { grid-template-columns: repeat(3, 1fr); }
}

/* ── celebration ──────────────────────────────────────────────────── */

.confetti {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0;
}

.confetti.on { opacity: 1; }

/* What reduced motion gets instead: one still pulse, no travel. */
.celebration-flash {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  border: 6px solid var(--accent);
  animation: hold 0.9s ease-out forwards;
}

@keyframes hold {
  0% { opacity: 1; }
  100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .celebration-flash { animation: none; opacity: 0.9; }
}

/* ── colophon ─────────────────────────────────────────────────────── */

.colophon {
  margin-top: 44px;
  padding-top: 14px;
  border-top: 3px double var(--rule);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: center;
}

.colophon p { margin: 4px 0; }

@media (min-width: 620px) {
  body { font-size: 16px; }
  .chip { font-size: 14px; }
}

/* ══════════════════════════════════════════════════════════════════
   THEMES

   Every palette below this line is what the app actually ships. The
   picker presents them as finished, so treat an edit here as a visible
   change rather than as filling in a blank.

   To redress a theme, edit only its block here:

     --ink        body text, rules, the solid blocks
     --paper      the page behind everything
     --faint      de-emphasised text — keep it AA on --paper (4.5:1)
     --rule       hard borders
     --hair       light dividers
     --wash       hover fill
     --accent     the one non-neutral: streak pips, celebration frame
     --accent-ink text placed on --accent
     --confetti   space-separated colours the celebration throws
     --type       typeface stack
     --sw-*       the three bands of the onboarding preview swatch

   A background image for a theme goes in backgrounds/ and is named after
   the theme id — see backgrounds/README.md for the contract. Nothing else
   in the app needs to change.

   tools/test-themes.js checks every block below for contrast and for a
   complete set of tokens. Run it after editing.
   ══════════════════════════════════════════════════════════════════ */
/* Modern Kitchen — sage cupboards, warm wood, a window over the sink.
   Cream page, dark walnut ink, the cupboard green as the accent. */
[data-theme="modern"] {
  --ink: #40342a;
  --paper: #f7f0e2;
  --faint: #6f6154;
  --rule: #40342a;
  --hair: rgba(64, 52, 42, 0.2);
  --wash: #ece3d1;
  --accent: #5c6f47;
  --accent-ink: #ffffff;
  --backdrop: 0.14;
  --confetti: #5c6f47 #b5693c #40342a #8a7a52;
  --art: url("../backgrounds/modern.jpg");
  --veil: 0.87;
  --type: "Courier New", Courier, "Nimbus Mono PS", "Liberation Mono", monospace;
}

/* Eastern Kitchen — blush and mint, drawn by hand.
   Pale blush page, slate ink, the red fridge as the accent. */
[data-theme="eastern"] {
  --ink: #3a4450;
  --paper: #fbeae6;
  --faint: #5e6570;
  --rule: #3a4450;
  --hair: rgba(58, 68, 80, 0.2);
  --wash: #f5dcd6;
  --accent: #c9413f;
  --accent-ink: #ffffff;
  --backdrop: 0.12;
  --confetti: #c9413f #8fbfb0 #3a4450 #d98a96;
  --art: url("../backgrounds/eastern.jpg");
  --veil: 0.88;
  --type: "Courier New", Courier, "Nimbus Mono PS", "Liberation Mono", monospace;
}

/* Green Kitchen — a kitchen inside a greenhouse.
   Pale glasshouse light, deep leaf ink, the grow-lamp pink as the accent. */
[data-theme="green"] {
  --ink: #243b28;
  --paper: #eef3e9;
  --faint: #5a6b58;
  --rule: #243b28;
  --hair: rgba(36, 59, 40, 0.2);
  --wash: #e0e9d9;
  --accent: #b3517f;
  --accent-ink: #ffffff;
  --backdrop: 0.13;
  --confetti: #b3517f #4f7a43 #243b28 #7fa06a;
  --art: url("../backgrounds/green.jpg");
  --veil: 0.87;
  --type: "Courier New", Courier, "Nimbus Mono PS", "Liberation Mono", monospace;
}

/* Cyber Kitchen — neon on wet concrete.
   Deep violet ground, pale lavender type, magenta signage as the accent. */
[data-theme="cyber"] {
  --ink: #ecdcff;
  --paper: #150f20;
  --faint: #9a86b8;
  --rule: #ff3fd0;
  --hair: rgba(255, 63, 208, 0.28);
  --wash: #211a30;
  --accent: #ff3fd0;
  --accent-ink: #150f20;
  --backdrop: 0.20;
  --confetti: #ff3fd0 #37e6ff #ecdcff #b07cff;
  --art: url("../backgrounds/cyber.jpg");
  --veil: 0.78;
  --type: "Courier New", Courier, "Nimbus Mono PS", monospace;
}

/* Night Kitchen — the same city, later and colder.
   Near-black blue, pale cyan type, the fridge light as the accent. */
[data-theme="night"] {
  --ink: #dbf3f8;
  --paper: #0a1014;
  --faint: #7d9aa4;
  --rule: #35d6e6;
  --hair: rgba(53, 214, 230, 0.26);
  --wash: #131c22;
  --accent: #35d6e6;
  --accent-ink: #0a1014;
  --backdrop: 0.20;
  --confetti: #35d6e6 #dbf3f8 #6fe3c8 #2aa6d8;
  --art: url("../backgrounds/night.jpg");
  --veil: 0.76;
  --type: "Courier New", Courier, "Nimbus Mono PS", monospace;
}

/* Themes that turn the page dark need their form controls told; the arrow on
   a select is drawn with --ink, but the widget itself is not. */
[data-theme="cyber"],
[data-theme="night"] {
  color-scheme: dark;
}

/* ── theme previews ───────────────────────────────────────────────────
   The swatch on each theme card has to show THAT theme, not the one
   currently applied, so these are keyed on the card rather than on the
   document. Three values per theme: page, accent, ink — the same three
   as the block above it. tools/test-themes.js checks they match, so a
   palette edited above without editing here fails the suite rather than
   quietly showing the wrong preview.

   Each card also carries its theme's own photograph, so the picker shows
   what you are actually choosing. With art turned off the image is dropped
   and the three colours show through on their own — the card previews the
   setting as well as the theme.
   ──────────────────────────────────────────────────────────────────── */

.ob-swatch[data-swatch="historic"] { --sw-paper: #f0ece4; --sw-accent: #7a6248; --sw-ink: #2b2620; --sw-art: url("../backgrounds/historic.jpg"); }
.ob-swatch[data-swatch="modern"]   { --sw-paper: #f7f0e2; --sw-accent: #5c6f47; --sw-ink: #40342a; --sw-art: url("../backgrounds/modern.jpg"); }
.ob-swatch[data-swatch="eastern"]  { --sw-paper: #fbeae6; --sw-accent: #c9413f; --sw-ink: #3a4450; --sw-art: url("../backgrounds/eastern.jpg"); }
.ob-swatch[data-swatch="green"]    { --sw-paper: #eef3e9; --sw-accent: #b3517f; --sw-ink: #243b28; --sw-art: url("../backgrounds/green.jpg"); }
.ob-swatch[data-swatch="cyber"]    { --sw-paper: #150f20; --sw-accent: #ff3fd0; --sw-ink: #ecdcff; --sw-art: url("../backgrounds/cyber.jpg"); }
.ob-swatch[data-swatch="night"]    { --sw-paper: #0a1014; --sw-accent: #35d6e6; --sw-ink: #dbf3f8; --sw-art: url("../backgrounds/night.jpg"); }

/* The photograph on the card. Layered over the bands rather than replacing
   them, so a slow or missing image degrades to the palette instead of to a
   blank rectangle. Kept separate from the rules above because those are
   parsed by the theme suite, which wants three colours and nothing else. */
