/* ==========================================================================
   CUTOUT KINGDOM — comic-book design system
   Built to match the logo: dark brick wall, blue crest, orange→gold gradient
   lettering with a heavy black outline, halftone dots, comic action bursts.
   Self-hosted fonts only. Zero external requests.
   ========================================================================== */

/* ---------- Fonts ---------- */
@font-face {
  font-family: 'Luckiest Guy';
  src: url('../fonts/luckiestguy-latin.woff2') format('woff2');
  font-weight: 400; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Luckiest Guy';
  src: url('../fonts/luckiestguy-latin-ext.woff2') format('woff2');
  font-weight: 400; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Bangers';
  src: url('../fonts/bangers-latin.woff2') format('woff2');
  font-weight: 400; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bangers';
  src: url('../fonts/bangers-latin-ext.woff2') format('woff2');
  font-weight: 400; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Nunito';
  src: url('../fonts/nunito-latin.woff2') format('woff2');
  font-weight: 400 900; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito';
  src: url('../fonts/nunito-latin-ext.woff2') format('woff2');
  font-weight: 400 900; font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- Tokens ---------- */
:root {
  /* straight off the logo */
  --ink: #0d1420;          /* the outline black */
  --wall: #16202e;         /* brick wall */
  --wall-2: #1e2a3c;       /* mortar */
  --wall-3: #26344a;
  --blue: #2d55a8;         /* crest blue */
  --blue-d: #1b3a7a;
  --blue-l: #4a7ad4;
  --orange: #f26522;
  --gold: #ffc20e;
  --gold-l: #ffd85c;
  --red: #e0342a;
  --red-d: #b8241c;        /* deep enough for white text to clear AA */
  --cream: #fff4e0;
  --cream-2: #ffe7c2;
  --white: #ffffff;
  --steel: #b9c6d6;

  --grad-title: linear-gradient(180deg, #ffe07a 0%, var(--gold) 30%, var(--orange) 68%, var(--red) 100%);
  --grad-crest: linear-gradient(160deg, var(--blue-l), var(--blue) 45%, var(--blue-d));
  /* ⚠ Ends at a mid orange, NOT gold. White sits on this in buttons, the
     CTA panel and the tier chips; against the old gold stop that was 1.6:1.
     Every stop here clears 4.5:1 with white. */
  --grad-fire: linear-gradient(115deg, var(--red-d), #c53f18 55%, #cd4419);

  --edge: 4px;
  --r-sm: 8px;
  --r: 14px;
  --r-lg: 22px;
  --r-pill: 999px;

  --shadow-panel: 7px 7px 0 var(--ink);
  --shadow-panel-sm: 4px 4px 0 var(--ink);
  --shadow-deep: 0 20px 46px -18px rgba(0, 0, 0, 0.8);

  --wrap: 1220px;
  --gut: clamp(1rem, 4vw, 2.5rem);

  --f-display: 'Luckiest Guy', 'Impact', system-ui, sans-serif;
  --f-action: 'Bangers', 'Luckiest Guy', system-ui, sans-serif;
  --f-body: 'Nunito', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
  --nav-h: 72px;

  /* halftone + brick, reused all over */
  --halftone: radial-gradient(circle, rgba(0, 0, 0, 0.26) 1.4px, transparent 1.5px);
  --halftone-light: radial-gradient(circle, rgba(255, 255, 255, 0.13) 1.4px, transparent 1.5px);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: var(--f-body);
  font-weight: 700;
  color: var(--ink);
  background: var(--cream);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg, canvas { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; }
a { color: inherit; }

h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: 0.01em;
}
h1 { font-size: clamp(2.5rem, 6.6vw, 5rem); }
h2 { font-size: clamp(2rem, 4.6vw, 3.3rem); }
h3 { font-size: clamp(1.2rem, 2.2vw, 1.6rem); }

:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; border-radius: 4px; }

.wrap { width: min(100% - var(--gut) * 2, var(--wrap)); margin-inline: auto; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: 8px; top: -60px; z-index: 999;
  background: var(--gold); color: var(--ink); padding: 0.7rem 1.1rem;
  border: 3px solid var(--ink); border-radius: var(--r-sm);
  font-family: var(--f-display); transition: top 0.2s;
}
.skip:focus { top: 8px; }

/* ==========================================================================
   Comic title treatment — gradient fill, hard black outline, drop shadow.

   ⚠ The outline MUST come from chained drop-shadow filters, not text-shadow.
   With -webkit-background-clip:text the gradient is painted as the element's
   BACKGROUND, and text-shadow paints ABOVE the background — so a text-shadow
   outline covers the gradient completely and the heading renders solid black.
   drop-shadow() renders behind the whole element, so the fill survives.
   ========================================================================== */
.comic {
  font-family: var(--f-display);
  background: var(--grad-title);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  filter:
    drop-shadow(3px 0 0 var(--ink)) drop-shadow(-3px 0 0 var(--ink))
    drop-shadow(0 3px 0 var(--ink)) drop-shadow(0 -3px 0 var(--ink))
    drop-shadow(0 7px 0 rgba(0, 0, 0, 0.45));
}
.comic--sm {
  filter:
    drop-shadow(2px 0 0 var(--ink)) drop-shadow(-2px 0 0 var(--ink))
    drop-shadow(0 2px 0 var(--ink)) drop-shadow(0 -2px 0 var(--ink))
    drop-shadow(0 5px 0 rgba(0, 0, 0, 0.4));
}
/* On cream backgrounds the outline alone carries it. */
.comic--ink {
  background: none; color: var(--ink);
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, 0.16));
}

/* Action word — the "POW!" treatment */
.action {
  font-family: var(--f-action);
  letter-spacing: 0.03em;
  color: var(--gold);
  text-shadow: 2px 2px 0 var(--ink), -2px 2px 0 var(--ink), 2px -2px 0 var(--ink),
    -2px -2px 0 var(--ink), 0 6px 0 rgba(0, 0, 0, 0.35);
  transform: rotate(-3deg);
  display: inline-block;
}

/* ---------- Surfaces ---------- */
.brickwall {
  background-color: var(--wall);
  background-image:
    var(--halftone-light),
    repeating-linear-gradient(90deg, transparent 0 88px, var(--wall-2) 88px 92px),
    repeating-linear-gradient(0deg, transparent 0 42px, var(--wall-2) 42px 46px);
  background-size: 9px 9px, auto, auto;
}
.brickwall::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse at 50% 40%, transparent 35%, rgba(0, 0, 0, 0.55));
}

/* ---------- Buttons ---------- */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: var(--f-display); font-weight: 400; font-size: 1.02rem;
  letter-spacing: 0.02em;
  padding: 0.8rem 1.6rem;
  border: var(--edge) solid var(--ink);
  border-radius: var(--r-pill);
  background: var(--white);
  /* Explicit: buttons sit on dark sections too, and inheriting the cream
     text colour there renders white-on-white. */
  color: var(--ink);
  box-shadow: var(--shadow-panel-sm);
  text-decoration: none;
  transition: transform 0.14s var(--ease-pop), box-shadow 0.14s var(--ease-pop);
  will-change: transform;
}
.btn:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--ink); }
.btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: var(--shadow-panel-sm); }

.btn--primary { background: var(--grad-fire); color: var(--white); text-shadow: 0 2px 0 rgba(0, 0, 0, 0.4); }
.btn--cyan { background: var(--blue); color: var(--white); text-shadow: 0 2px 0 rgba(0, 0, 0, 0.35); }
.btn--yellow { background: var(--gold); }
.btn--lime { background: var(--gold-l); }
.btn--ink { background: var(--ink); color: var(--cream); }
.btn--ghost { background: transparent; box-shadow: none; color: inherit; }
.btn--ghost:hover { background: var(--white); color: var(--ink); box-shadow: var(--shadow-panel-sm); }
.btn--lg { font-size: 1.2rem; padding: 1rem 2.1rem; }
.btn--sm { font-size: 0.86rem; padding: 0.48rem 1rem; border-width: 3px; box-shadow: 3px 3px 0 var(--ink); }
.btn--block { width: 100%; }

/* ---------- Comic panel (the signature surface) ---------- */
.sticker {
  position: relative;
  background: var(--white);
  border: var(--edge) solid var(--ink);
  border-radius: var(--r);
  box-shadow: var(--shadow-panel);
}

/* ---------- Nav ---------- */
.nav {
  position: sticky; top: 0; z-index: 60;
  background: var(--ink);
  border-bottom: var(--edge) solid var(--ink);
  color: var(--cream);
  box-shadow: 0 4px 0 rgba(0, 0, 0, 0.35);
}
.nav__in { display: flex; align-items: center; gap: 1rem; height: var(--nav-h); }
.brand {
  display: flex; align-items: center; gap: 0.6rem;
  font-family: var(--f-display); font-size: 1.4rem;
  text-decoration: none; color: var(--cream); margin-right: auto;
  white-space: nowrap; letter-spacing: 0.02em;
}
.brand__mark { width: 44px; height: 44px; flex: none; }
.brand__mark svg { width: 100%; height: 100%; }
.brand__k { color: var(--gold); }
.brand__txt {
  background: var(--grad-title); -webkit-background-clip: text; background-clip: text;
  color: transparent;
  /* drop-shadow, not text-shadow — see the .comic note above */
  filter:
    drop-shadow(1.5px 0 0 var(--ink)) drop-shadow(-1.5px 0 0 var(--ink))
    drop-shadow(0 1.5px 0 var(--ink)) drop-shadow(0 -1.5px 0 var(--ink));
}

.nav__links { display: flex; align-items: center; gap: 0.3rem; }
.nav__link {
  font-family: var(--f-display); font-size: 1rem; text-decoration: none;
  padding: 0.45rem 0.85rem; border-radius: var(--r-pill);
  border: 3px solid transparent;
  transition: background 0.15s, border-color 0.15s, transform 0.15s var(--ease-pop);
}
.nav__link:hover { background: rgba(255, 255, 255, 0.12); transform: translateY(-2px); }
/* ink on orange (5.6:1); white on orange was only 3.15:1 */
.nav__link[aria-current='page'] { background: var(--orange); color: var(--ink); border-color: var(--ink); }

.nav__pts {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-family: var(--f-display); font-size: 0.92rem;
  background: var(--gold); color: var(--ink);
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.25rem 0.75rem;
}
.nav__burger { display: none; font-size: 1.6rem; padding: 0.4rem 0.6rem; color: var(--cream); }

@media (max-width: 960px) {
  .nav__burger { display: block; }
  .nav__links {
    position: absolute; inset: var(--nav-h) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--ink); padding: 0.5rem var(--gut) 1.2rem;
    border-bottom: var(--edge) solid var(--wall-3);
    display: none;
  }
  .nav__links.is-open { display: flex; }
  .nav__link { padding: 0.85rem 0.5rem; border-radius: var(--r-sm); }
}

/* ---------- Hero ---------- */
.hero {
  position: relative; overflow: hidden;
  color: var(--cream);
  padding: clamp(2.6rem, 7vw, 5rem) 0 clamp(3.4rem, 8vw, 6rem);
  isolation: isolate;
}
.hero__grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 980px) { .hero__grid { grid-template-columns: 1.02fr 0.98fr; } }

/* radiating comic burst behind the hero */
.hero__rays {
  position: absolute; inset: -40% -20%; z-index: -1; pointer-events: none;
  background: repeating-conic-gradient(from 0deg at 50% 45%,
    rgba(255, 194, 14, 0.14) 0deg 7deg, transparent 7deg 14deg);
  animation: spin 90s linear infinite;
}
@keyframes spin { to { rotate: 360deg; } }
@media (prefers-reduced-motion: reduce) { .hero__rays { animation: none; } }

.eyebrow {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--f-action); font-size: 1rem; letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--red-d); color: var(--white);
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.3rem 1rem; box-shadow: 3px 3px 0 var(--ink);
  rotate: -2deg;
}
.hero h1 { margin: 1.1rem 0 0.9rem; }
.hero__lede { font-size: clamp(1.05rem, 2vw, 1.28rem); max-width: 46ch; color: var(--cream); opacity: 0.94; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: 1.9rem; }
.hero__trust { display: flex; flex-wrap: wrap; gap: 1.4rem; margin-top: 2.1rem; font-size: 0.9rem; }
.hero__trust b {
  font-family: var(--f-display); font-size: 1.6rem; display: block; color: var(--gold);
  text-shadow: 2px 2px 0 var(--ink);
}

/* ==========================================================================
   ADMIN — orders, categories and charts
   ========================================================================== */
.tablewrap { overflow-x: auto; }          /* wide tables scroll, the page never does */
.atable { width: 100%; border-collapse: collapse; min-width: 620px; }
.atable td {
  padding: 0.7rem 0.6rem; border-bottom: 2px solid rgba(13, 20, 32, 0.12);
  vertical-align: middle;
}
.atable tr:last-child td { border-bottom: 0; }

/* Order stage pills. Each tone is a filled chip with ink text — coloured text
   on cream could not clear AA at this size, so the colour carries the meaning
   and the text stays black on top of it. */
.opill {
  display: inline-block; padding: 0.2rem 0.6rem; border-radius: 999px;
  border: 2px solid var(--ink); font-size: 0.78rem; font-weight: 700;
  color: var(--ink); white-space: nowrap;
}
.opill--grey  { background: #d8d2c6; }
.opill--blue  { background: #a9c6f0; }
.opill--amber { background: #f4c66b; }
.opill--cyan  { background: #9fdbe4; }
.opill--lime  { background: #b3e08a; }
.opill--red   { background: #f0a9a3; }

.otl { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.35rem; }

/* Charts */
.chart { width: 100%; height: auto; display: block; }
.chart__grid { stroke: rgba(13, 20, 32, 0.13); stroke-width: 1; }
.chart__ylab, .chart__xlab { font-size: 11px; fill: #4a443c; font-family: var(--f-body); }
.chart__ylab { text-anchor: end; }
.chart__area { fill: rgba(45, 85, 168, 0.16); }
.chart__line { fill: none; stroke: var(--blue, #2d55a8); stroke-width: 3;
  stroke-linejoin: round; stroke-linecap: round; }
.chart__peak { fill: var(--gold); stroke: var(--ink); stroke-width: 2; }

.hbars { display: flex; flex-direction: column; gap: 0.5rem; }
/* The label column gets real room: "Jungle, Tr…" tells you nothing, and these
   sit in a half-width panel where the bar does not need to be long to read. */
.hbar { display: grid; grid-template-columns: minmax(150px, 1.6fr) minmax(60px, 2fr) auto;
  gap: 0.6rem; align-items: center; font-size: 0.86rem; }
.hbar__lab { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hbar__track { background: var(--cream-2); border: 2px solid var(--ink);
  border-radius: 999px; height: 16px; overflow: hidden; }
.hbar__fill { display: block; height: 100%; }
.hbar__fill--grey  { background: #d8d2c6; }
.hbar__fill--blue  { background: #a9c6f0; }
.hbar__fill--amber { background: #f4c66b; }
.hbar__fill--cyan  { background: #9fdbe4; }
.hbar__fill--lime  { background: #b3e08a; }
.hbar__fill--red   { background: #f0a9a3; }
.hbar__val { font-weight: 700; white-space: nowrap; }

@media (max-width: 620px) {
  .hbar { grid-template-columns: 1fr auto; }
  .hbar__track { grid-column: 1 / -1; }
}

/* ==========================================================================
   PRODUCT FORM — sectioned and condensed
   The old form was one long column of equally-weighted fields that ran past
   two screens before the save button. Sections give it a shape you can scan.
   ========================================================================== */
.pform { display: flex; flex-direction: column; gap: 0.9rem; }
.pform .field { margin: 0 0 0.5rem; }
.pform .row { gap: 0.7rem; }
.pform textarea { min-height: 0; }
.pform__sec {
  border: 3px solid var(--ink); border-radius: var(--r-sm);
  padding: 0.8rem 0.9rem; background: #fff;
}

/* Flat blocks — a hairline and a tint instead of a heavy outlined card. The
   form has a lot of groups in it and eight boxed panels read as eight
   competing things rather than one page. */
.pblock {
  border: 2px solid rgba(13, 20, 32, 0.18); border-radius: var(--r-sm);
  padding: 0.7rem 0.8rem; background: #fdfbf6; margin-bottom: 0.2rem;
}
.pblock__head { margin-bottom: 0.55rem; align-items: center; }
.pblock__head b { font-family: var(--f-display); font-size: 0.98rem; }
.pform__foot {
  display: flex; gap: 0.6rem; justify-content: flex-end; flex-wrap: wrap;
  border-top: 2px solid rgba(13, 20, 32, 0.18); padding-top: 0.8rem; margin-top: 0.3rem;
}

/* One size = one row: fields left, photo and controls right */
.srow {
  display: grid; grid-template-columns: 1fr 96px; gap: 0.7rem;
  border: 2px solid rgba(13, 20, 32, 0.18); border-radius: var(--r-sm);
  background: #fff; padding: 0.6rem 0.7rem; margin-bottom: 0.5rem;
}
.srow .field { margin: 0 0 0.4rem; }
.srow .lbl { font-size: 0.72rem; }
.srow textarea { min-height: 0; }
.srow__side { display: flex; flex-direction: column; gap: 0.35rem; align-items: stretch; }
.srow__move { display: flex; gap: 0.2rem; }
.srow__move .btn { flex: 1; padding: 0.12rem 0.2rem; }
@media (max-width: 640px) {
  .srow { grid-template-columns: 1fr; }
  .srow__side { flex-direction: row; align-items: center; flex-wrap: wrap; }
}

/* A size that has sold out on its own */
.selcard.is-gone { opacity: 0.55; cursor: not-allowed; box-shadow: none; }
.selcard.is-gone .selcard__p { color: var(--red-d); font-size: 0.9rem; }
.sel__note {
  margin: 0.45rem 0 0; font-size: 0.86rem; color: #4a443c;
  border-left: 3px solid var(--gold); padding-left: 0.6rem;
}
.pform__h {
  font-family: var(--f-display); font-size: 1rem; margin: 0 0 0.6rem;
  display: flex; align-items: center; gap: 0.4rem;
}
.pform__toggle {
  display: flex; align-items: center; gap: 0.55rem; cursor: pointer;
  font-family: var(--f-body);
}
.pform__toggle input { width: auto; }
.drop--slim { padding: 0.75rem 0.9rem; }

/* Gallery grid with the ordering controls */
.galgrid { display: grid; gap: 0.6rem; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); }
.galgrid > div { position: relative; overflow: hidden; }

/* One small photo per piece of a set */
.setpic {
  width: 46px; height: 46px; flex: 0 0 46px; display: block; overflow: hidden;
  border: 2px solid var(--ink); border-radius: 8px; background: var(--cream-2);
}
.setpic img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Out-of-stock message picker */
.oospick { display: flex; flex-direction: column; gap: 0.3rem; }
.oospick__row {
  display: flex; gap: 0.55rem; align-items: flex-start; cursor: pointer;
  border: 2px solid var(--ink); border-radius: var(--r-sm);
  padding: 0.45rem 0.6rem; background: var(--cream-2); font-size: 0.9rem;
}
.oospick__row input { width: auto; margin-top: 0.15rem; }
.oospick__row:hover { background: #fff; }

/* Buyer-facing out-of-stock notice */
.oos {
  border: 3px solid var(--ink); border-radius: var(--r);
  background: #fff3cd; padding: 0.7rem 0.9rem;
}
.oos b { font-family: var(--f-display); display: block; }
.oos p { margin: 0.2rem 0 0; }

/* ==========================================================================
   SET CONTENTS — the buyer-facing "what is included" list
   ========================================================================== */
.setlist {
  border: 3px solid var(--ink); border-radius: var(--r);
  background: var(--cream-2); padding: 0.8rem 0.9rem;
}
.setlist__head { font-family: var(--f-display); display: block; margin-bottom: 0.5rem; }
.setrow {
  display: flex; gap: 0.7rem; align-items: flex-start; justify-content: space-between;
  padding: 0.5rem 0; border-top: 2px solid rgba(13, 20, 32, 0.12);
}
/* Takes the leftover width and is allowed to shrink. Without min-width:0 a
   flex item refuses to go below its longest word, which is how the name
   column ended up 0px wide while the picture took the whole row. */
.setrow__main { flex: 1 1 auto; min-width: 0; }
.setrow__meta { flex: 0 0 auto; }
.setrow:first-of-type { border-top: 0; }
.setrow__main { flex: 1; min-width: 0; }
.setrow__main b { display: block; font-size: 0.95rem; }
.setrow__main span { font-size: 0.84rem; color: #4a443c; display: block; }
/* Thumbnail of the individual piece, when one has been added */
.setrow__pic {
  /* ⚠ display:block is LOAD BEARING. This is a <span>, so its default is
     inline — and an inline box ignores width and height entirely. It used to
     get away with it by being a direct flex child of .setrow, because flex
     items are blockified automatically. Wrapping it in the buy-alone <a>
     ended that, the 52px was silently dropped, and a 900x2600 photo rendered
     at 328x488 — squashing the piece name to a single letter and breaking the
     link onto one word per line. */
  display: block;
  width: 52px; height: 52px; flex: 0 0 52px; overflow: hidden;
  border: 2px solid var(--ink); border-radius: 8px; background: #fff;
}
.setrow__pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Buy-this-alone link. Underlined as well as coloured — blue on cream is not
   enough on its own for anyone who cannot separate the two. */
.setrow__buy {
  display: inline-block; margin-top: 0.25rem; font-size: 0.82rem;
  font-weight: 700; color: var(--blue); text-decoration: underline;
}
.setrow__meta { display: flex; gap: 0.35rem; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }

/* Admin: one editable line per item in the set */
.setedit {
  display: grid; grid-template-columns: 2rem 1fr auto; gap: 0.6rem;
  align-items: start; padding: 0.7rem; margin-bottom: 0.6rem;
  border: 3px solid var(--ink); border-radius: var(--r-sm); background: #fff;
}
.setedit__no {
  font-family: var(--f-display); font-size: 1.1rem; text-align: center;
  background: var(--gold); border: 2px solid var(--ink); border-radius: 999px;
  line-height: 1.6;
}
.setedit__fields { display: flex; flex-direction: column; gap: 0.45rem; min-width: 0; }
.setedit__ctl { display: flex; flex-direction: column; gap: 0.3rem; }
@media (max-width: 640px) {
  .setedit { grid-template-columns: 2rem 1fr; }
  .setedit__ctl { grid-column: 2; flex-direction: row; }
}

/* Gallery ordering controls */
.galcover {
  position: absolute; top: 4px; left: 4px; z-index: 2;
  background: var(--gold); border: 2px solid var(--ink); border-radius: 999px;
  font-size: 0.66rem; font-weight: 700; padding: 0.05rem 0.4rem;
}
.galmove {
  position: absolute; bottom: 4px; left: 4px; right: 4px;
  display: flex; justify-content: space-between; gap: 0.25rem;
}
.galmove .btn { padding: 0.1rem 0.45rem; }

/* Review diff */
.difftable td { vertical-align: top; }
.diff--old { color: #6b6257; text-decoration: line-through; }
.diff--new { background: #e8f5dd; font-weight: 700; }

/* Clearance */
.clear {
  border: 3px solid var(--ink); border-radius: var(--r);
  background: #fff3cd; padding: 0.7rem 0.9rem;
}
.clear b { font-family: var(--f-display); display: block; }
.clear p { margin: 0.2rem 0 0; }
.price__was { color: #6b6257; font-weight: 400; margin-right: 0.3rem; }
.selcard__p s { color: #6b6257; font-weight: 400; font-size: 0.8em; margin-right: 0.2rem; }
.pill--gold { background: var(--gold); }

/* Milestone ladder on the help page */
.ranktable { display: flex; flex-direction: column; gap: 0.6rem; }
.rankrow {
  display: grid; grid-template-columns: 140px 1fr auto; gap: 0.9rem; align-items: center;
  border: 3px solid var(--ink); border-radius: var(--r); background: #fff;
  padding: 0.7rem 0.9rem; box-shadow: 3px 3px 0 var(--ink);
}
.rankrow__badge { display: flex; align-items: center; gap: 0.4rem; }
.rankrow__badge span { font-size: 1.3rem; }
.rankrow__badge b { font-family: var(--f-display); font-size: 1rem; }
.rankrow__perk { display: block; font-size: 0.98rem; }
.rankrow__main span { color: #4a443c; display: block; margin-top: 0.15rem; }
.rankrow__need { text-align: right; display: flex; flex-direction: column; gap: 0.15rem; align-items: flex-end; }
@media (max-width: 700px) {
  .rankrow { grid-template-columns: 1fr auto; }
  .rankrow__main { grid-column: 1 / -1; }
}

/* Treasury tiers — locked by points vs locked by rank read differently */
.trez { position: relative; }
.trez--points { opacity: 0.72; }
.trez--rank { opacity: 0.92; background: #f3ece0; }
.trez__rank {
  font-size: 0.72rem; font-weight: 700; white-space: nowrap;
  border: 2px solid var(--ink); border-radius: 999px;
  padding: 0.1rem 0.5rem; background: var(--cream-2);
}
.trez__rank.is-locked { background: var(--gold); }
.trez__why {
  background: #fff3cd; border: 2px solid var(--ink); border-radius: var(--r-sm);
  padding: 0.45rem 0.6rem; margin: 0 0 0.6rem;
}

/* Collapsed "More options" in the product form */
.pmore {
  border: 3px solid var(--ink); border-radius: var(--r);
  background: var(--cream-2); margin-bottom: 1rem;
}
.pmore > summary {
  cursor: pointer; padding: 0.7rem 0.9rem; list-style: none;
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
  font-family: var(--f-display);
}
.pmore > summary::-webkit-details-marker { display: none; }
/* a twisty that turns, so the control reads as expandable before you click */
.pmore > summary::before {
  content: '▸'; display: inline-block; transition: rotate 0.15s var(--ease-pop);
}
.pmore[open] > summary::before { rotate: 90deg; }
.pmore[open] > summary { border-bottom: 3px solid var(--ink); }
.pmore > :not(summary) { margin-left: 0.9rem; margin-right: 0.9rem; }
.pmore > :last-child { margin-bottom: 0.9rem; }

/* "Set of N" flag on a shop card. Cyan so it reads as information rather
   than competing with the magenta Fan favourite promo tag. */
.tag--set { background: var(--blue); color: #fff; }

/* A set piece that can be bought on its own. The whole tile reads as
   clickable — picture and name — rather than only the small text link that
   used to be the single way through. */
/* The link is now the flex item where the thumbnail used to be, so it has to
   carry the sizing the thumbnail was relying on. */
.setrow__link { color: inherit; text-decoration: none; display: block; flex: 0 0 auto; }
a.setrow__link { cursor: pointer; }
a.setrow__link:hover span.setrow__pic,
a.setrow__link:focus-visible span.setrow__pic {
  outline: 3px solid var(--blue); outline-offset: 2px;
  transform: translateY(-2px);
}
.setrow__pic { transition: transform 0.15s var(--ease-pop); }
a.setrow__link:hover, a.setrow__link:focus-visible { text-decoration: underline; }
/* a quiet cue that this one is purchasable separately */
.setrow--linked .setrow__main b::after {
  content: ' ↗'; font-size: 0.8em; color: var(--blue);
}

/* Product picker on the buy-alone link field */
.linkpick__pop {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 30;
  max-height: 260px; overflow-y: auto;
  background: var(--white); border: 3px solid var(--ink);
  border-radius: var(--r); box-shadow: 3px 4px 0 var(--ink);
  padding: 0.3rem;
}
.linkpick__opt {
  display: flex; align-items: center; gap: 0.6rem; width: 100%;
  padding: 0.35rem 0.45rem; border: 0; background: transparent;
  border-radius: 8px; cursor: pointer; text-align: left;
}
.linkpick__opt:hover, .linkpick__opt:focus-visible { background: var(--cream-2); }
.linkpick__thumb {
  flex: 0 0 40px; width: 40px; height: 40px; overflow: hidden;
  border: 2px solid var(--ink); border-radius: 7px; background: var(--cream-2);
}
.linkpick__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.linkpick__meta { min-width: 0; display: block; }
.linkpick__meta b { display: block; font-size: 0.86rem; line-height: 1.2; }
.linkpick__meta span { font-size: 0.75rem; color: #4a443c; }
.linkpick__none { margin: 0; padding: 0.5rem; font-size: 0.8rem; color: #4a443c; }

/* "Buyer sees" selector in the items header */
.itemlbl { display: flex; align-items: center; gap: 0.4rem; }
.itemlbl .lbl { margin: 0; font-size: 0.72rem; white-space: nowrap; }
.itemlbl select { max-width: 110px; padding: 0.25rem 0.4rem; font-size: 0.82rem; }

/* The slim banner. The old one announced that every product, price and review
   was invented — true of a spec build, false the moment real products and real
   photographs went in. What remains is narrower (draft policy timeframes), so
   the notice is narrower too rather than being deleted outright. */
.specbar--slim {
  font-size: 0.78rem; padding: 0.35rem 1rem; line-height: 1.35;
  background: var(--ink); color: var(--cream); opacity: 0.92;
}

/* Set maths — admin editor */
.setsum {
  margin-top: 0.7rem; padding: 0.7rem 0.85rem;
  background: var(--cream-2); border: 3px solid var(--ink); border-radius: var(--r);
}
.setsum__row {
  display: flex; justify-content: space-between; gap: 1rem;
  font-size: 0.86rem; padding: 0.15rem 0;
}
.setsum__row--good { color: #1d6b34; border-top: 2px dashed var(--ink); margin-top: 0.25rem; padding-top: 0.4rem; }
.setsum__warn {
  margin-top: 0.4rem; padding: 0.5rem 0.6rem; font-size: 0.82rem; line-height: 1.4;
  background: #fff3cd; border: 3px solid var(--red); border-radius: 10px;
}

/* Set saving — buyer's product page */
.setsave {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.6rem;
  margin-bottom: 0.6rem; padding: 0.5rem 0.7rem;
  background: var(--lime, #6aa84f); color: #fff;
  border: 3px solid var(--ink); border-radius: 10px;
  box-shadow: 2px 3px 0 var(--ink);
}
.setsave b { font-family: var(--f-display); font-size: 1.1rem; }
.setsave span { font-size: 0.8rem; opacity: 0.95; }

/* Storage meter (admin) */
.stbar {
  height: 18px; border: 3px solid var(--ink); border-radius: 999px;
  background: var(--cream-2); overflow: hidden;
}
.stbar > span {
  display: block; height: 100%; width: 0;
  background: var(--lime, #6aa84f);
  transition: width 0.4s var(--ease-pop);
}
.stbar > span.is-warn { background: var(--gold); }
.stbar > span.is-bad { background: var(--red); }

/* Floating prop stage
   ⚠ These cards are LAID OUT BY THE GRID, never absolutely positioned. The
   original layout pinned all three with top/left percentages, which works only while
   every card is the same shape — the moment real photographs went in, three
   different aspect ratios meant the middle card grew downward and buried the
   first one completely. The grid gives each card its own cell, so a card can
   change shape without ever covering a neighbour. */
.stage {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  column-gap: clamp(0.5rem, 1.4vw, 1rem);
  row-gap: clamp(0.6rem, 1.6vw, 1.2rem);
  padding: 0.5rem 0.2rem;
}
.prop {
  border: var(--edge) solid var(--ink);
  border-radius: var(--r);
  background: var(--white);
  box-shadow: var(--shadow-panel), var(--shadow-deep);
  padding: 0.7rem;
  animation: bob 6s ease-in-out infinite;
}
.prop svg { border-radius: 8px; }
/* Real photographs keep their own shape — no cropping. Three different aspect
   ratios is the scrapbook look, not a bug. */
.prop > img { display: block; width: 100%; height: auto; border-radius: 8px; }
/* The overlaps are small, deliberate corner tucks — enough to read as a pinned
   collage, never enough to hide a subject. */
.prop:nth-child(2) {
  grid-column: 1; grid-row: 1;
  animation-delay: -0.4s; rotate: -5deg; z-index: 1;
}
.prop:nth-child(3) {
  grid-column: 2; grid-row: 1 / span 2; align-self: center;
  margin-left: -14%; animation-delay: -2.1s; rotate: 5deg; z-index: 3;
}
.prop:nth-child(4) {
  grid-column: 1; grid-row: 2; width: 92%;
  animation-delay: -3.6s; rotate: 3deg; z-index: 2;
}
@keyframes bob { 0%, 100% { translate: 0 0; } 50% { translate: 0 -18px; } }
@media (prefers-reduced-motion: reduce) { .prop { animation: none; } }

/* Easter egg #1 — party hat on the middle prop */
.egg-hat {
  position: absolute; top: -22px; right: 14%; width: 54px; z-index: 5;
  cursor: pointer; padding: 0; border: 0; rotate: 12deg;
  transition: transform 0.25s var(--ease-pop), filter 0.25s;
}
.egg-hat:hover { transform: translateY(-6px) scale(1.14); filter: drop-shadow(0 0 12px var(--gold)); }
.egg-hat.is-found { animation: spin-out 0.7s var(--ease-pop) forwards; }
@keyframes spin-out { to { transform: translateY(-44px) scale(0.35) rotate(400deg); opacity: 0; } }

/* ---------- Sections ---------- */
.section { padding: clamp(3.2rem, 7vw, 5.5rem) 0; position: relative; }
.section--cream { background: var(--cream); }
.section--tint {
  background: var(--cream-2);
  background-image: var(--halftone);
  background-size: 10px 10px;
}
.section--ink { background: var(--wall); color: var(--cream); position: relative; overflow: hidden; }

.sec-head { max-width: 64ch; margin-bottom: clamp(1.8rem, 4vw, 2.8rem); }
.sec-head p { margin-top: 0.7rem; font-size: 1.06rem; }
.section--cream .sec-head p, .section--tint .sec-head p { color: #45403a; }
.section--ink .sec-head p { color: var(--cream); opacity: 0.85; }
.sec-head--center { margin-inline: auto; text-align: center; }

/* torn-paper style divider */
.wave { display: block; width: 100%; height: 40px; }

/* ---------- Category tiles ---------- */
.cats { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); }
.cat {
  position: relative; overflow: hidden;
  border: var(--edge) solid var(--ink); border-radius: var(--r);
  box-shadow: var(--shadow-panel);
  padding: 1.2rem 1.1rem 1.1rem;
  text-decoration: none; color: var(--ink);
  background: var(--white);
  transition: transform 0.18s var(--ease-pop), box-shadow 0.18s var(--ease-pop);
  min-height: 165px; display: flex; flex-direction: column; justify-content: flex-end;
}
.cat::before {
  content: ''; position: absolute; inset: 0;
  background-image: var(--halftone); background-size: 8px 8px; opacity: 0.5;
}
.cat:hover { transform: translate(-3px, -4px) rotate(-1deg); box-shadow: 10px 11px 0 var(--ink); }
.cat > * { position: relative; }
.cat__ico { font-size: 2rem; margin-bottom: auto; }
.cat h3 { font-size: 1.2rem; }
.cat span { font-size: 0.86rem; color: #4a443c; }
.cat:nth-child(6n + 1) { background: #ffd9b0; }
.cat:nth-child(6n + 2) { background: #b8cef0; }
.cat:nth-child(6n + 3) { background: #ffe59a; }
.cat:nth-child(6n + 4) { background: #ffc0b4; }
.cat:nth-child(6n + 5) { background: #c9e3ff; }
.cat:nth-child(6n + 6) { background: #ffd76e; }

/* ---------- Product grid ---------- */
.grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fill, minmax(255px, 1fr)); }
.card {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--white);
  border: var(--edge) solid var(--ink); border-radius: var(--r);
  box-shadow: var(--shadow-panel);
  transition: transform 0.18s var(--ease-pop), box-shadow 0.18s var(--ease-pop);
}
.card:hover { transform: translate(-3px, -4px) rotate(-0.6deg); box-shadow: 10px 11px 0 var(--ink); }
.card__media {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  border-bottom: var(--edge) solid var(--ink); background: var(--cream-2);
}
.card__media img, .card__media svg { width: 100%; height: 100%; object-fit: cover; }

/* Product detail preview — FIT, never crop.
   The grid tiles above use object-fit:cover so the cards line up, which is the
   right call for a thumbnail. It is the wrong call here: this is the picture
   someone decides on, and cover was cutting the top and bottom off anything
   taller than 16:10 — which is most cutouts, since they are tall by nature.
   contain shows the whole piece and letterboxes the gap. */
/* Two columns once there is room: pictures left, the decision right. Below
   820px it stacks, which is the right order on a phone anyway — you look at
   the thing before you choose a size. */
.pdp { display: grid; gap: clamp(1rem, 2.5vw, 1.6rem); grid-template-columns: 1fr; }
/* 980px, not 820. At 820 the right column came out around 240px wide, which
   wrapped "Add to cart · $92.00" onto two lines and pushed the wishlist
   button below it. Two cramped columns are worse than one comfortable one. */
@media (min-width: 980px) {
  .pdp { grid-template-columns: 1.05fr 1fr; align-items: start; }
  /* The buy row sticks to the bottom of the right column so the price and
     the button stay put while a long set list scrolls past them. */
  .pdp__right { position: sticky; top: 84px; }
}
.pdp__media {
  background: var(--cream-2); border: 3px solid var(--ink);
  border-radius: var(--r); aspect-ratio: 4 / 5; overflow: hidden;
  margin-bottom: 0.7rem;
}
.pdp__media img { object-fit: contain; width: 100%; height: 100%; display: block; }
.pdp__count { margin: -0.5rem 0 0; }

/* ---- Selections: priced cards instead of a dropdown ---------------------
   Borrowed structure, our language: heavy ink outline, gold for the chosen
   one, the hard shadow the rest of the site uses. */
.sel { margin-bottom: 0.9rem; }
.sel__h {
  display: block; font-family: var(--f-action); font-size: 0.8rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--blue);
  margin-bottom: 0.4rem;
}
.sel__grid { display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); }
.sel__chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.selcard {
  display: flex; flex-direction: column; gap: 0.15rem; text-align: left;
  border: 3px solid var(--ink); border-radius: var(--r-sm);
  background: #fff; padding: 0.5rem 0.65rem; cursor: pointer;
  box-shadow: 2px 2px 0 var(--ink);
  transition: transform 0.14s var(--ease-pop), background 0.14s;
}
.selcard:hover { transform: translateY(-2px); }
.selcard__n { font-weight: 700; font-size: 0.9rem; }
.selcard__p { font-family: var(--f-display); color: var(--red-d); font-size: 1.05rem; }
/* Selected state carries colour AND a thicker ring — colour alone is not a
   reliable signal, and these sit next to each other. */
.selcard.is-on {
  background: var(--gold); box-shadow: 2px 2px 0 var(--ink), 0 0 0 3px var(--ink);
}
.selcard.is-on .selcard__p { color: var(--ink); }

/* ---- Item details spec block ---- */
.spec {
  border: 3px solid var(--ink); border-radius: var(--r);
  background: var(--cream-2); padding: 0.7rem 0.9rem;
}
.spec__h {
  display: block; font-family: var(--f-action); font-size: 0.8rem;
  letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 0.35rem;
}
.spec__row {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: 0.28rem 0; font-size: 0.88rem;
  border-top: 2px solid rgba(13, 20, 32, 0.1);
}
.spec__row:first-of-type { border-top: 0; }
.spec__row span { color: #4a443c; }
.spec__row b { text-align: right; }

/* ---- Stock line ---- */
.stockline { display: flex; align-items: center; gap: 0.45rem; font-size: 0.88rem; font-weight: 700; }
.stockline__dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: #4e9a3d; border: 2px solid var(--ink); flex: 0 0 10px;
}
.stockline__dot--out { background: var(--red); }

/* Explicit two-row grid rather than flex-wrap. Wrapping decided the layout
   by accident at every width — sometimes the heart sat beside the button,
   sometimes below it, sometimes the label broke across two lines. This is
   the same shape at every size: quantity and wishlist on top, the button
   full width underneath where it cannot be missed. */
.pdp__buy {
  display: grid; grid-template-columns: 74px auto 1fr; gap: 0.6rem;
  align-items: end; margin-top: 1rem;
  border-top: 3px solid var(--ink); padding-top: 0.9rem;
}
/* Rows pinned explicitly. Auto-placement followed DOM order — and the
   button is written before the wishlist — so the button landed on row 2 and
   pushed the heart down to a row of its own. */
.pdp__buy .field { grid-column: 1; grid-row: 1; margin: 0; }
.pdp__icon { grid-column: 2; grid-row: 1; }
.pdp__add { grid-column: 1 / -1; grid-row: 2; width: 100%; }
.pdp__reassure { margin-top: 0.5rem; text-align: center; }

/* Visually hidden but still reachable by script and assistive tech — the
   selects behind the cards. */
.vh {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* Thumbnail strip. Only rendered when a product actually has more than one
   picture — see openProduct(). */
.pdp__thumbs {
  display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.1rem;
}
.pdp__thumb {
  width: 68px; height: 68px; padding: 0; overflow: hidden; cursor: pointer;
  border: 3px solid var(--ink); border-radius: 10px; background: var(--cream-2);
  opacity: 0.62; transition: opacity 0.18s, transform 0.18s var(--ease-pop);
}
.pdp__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdp__thumb:hover, .pdp__thumb:focus-visible { opacity: 1; transform: translateY(-2px); }
/* The selected one is obvious without relying on opacity alone — that reads as
   "disabled" to some people rather than "not selected". */
.pdp__thumb.is-on {
  opacity: 1; border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--gold);
}
.card__body { padding: 1rem 1.05rem 1.15rem; display: flex; flex-direction: column; gap: 0.5rem; flex: 1; }
.card__cat {
  font-family: var(--f-action); font-size: 0.86rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--blue);
}
.card__title { font-size: 1.15rem; }
.card__desc { font-size: 0.9rem; color: #4a443c; flex: 1; }
.card__foot { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; margin-top: 0.3rem; }
.price { font-family: var(--f-display); font-size: 1.35rem; color: var(--red); }
.price small { font-size: 0.72rem; color: #6b625a; font-family: var(--f-body); }

/* ⚠ Tags STACK — they are not each pinned to the same corner.
   Every tag used to be `position:absolute; top:.7rem; left:.7rem`, which was
   fine while a card could only ever carry one. The moment a product was both
   a Fan favourite AND a set, the two printed directly on top of each other
   and neither could be read. The flow container keeps them in a column and
   costs nothing when there is only one. */
.card__tags {
  position: absolute; top: 0.7rem; left: 0.7rem; z-index: 2;
  display: flex; flex-direction: column; align-items: flex-start; gap: 0.35rem;
  pointer-events: none;
}
.tag {
  position: relative; top: auto; left: auto;
  font-family: var(--f-action); font-size: 0.85rem; letter-spacing: 0.04em;
  background: var(--gold); color: var(--ink);
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.1rem 0.7rem; box-shadow: 2px 2px 0 var(--ink); rotate: -4deg;
}
/* the second tag leans the other way, so a stack reads as two pinned labels
   rather than one thick block */
.card__tags .tag:nth-child(2) { rotate: 3deg; }
.tag--cyan { background: var(--blue); color: #fff; }
.tag--lime { background: var(--gold-l); }
.tag--magenta { background: var(--red); color: #fff; }

.heart {
  position: absolute; top: 0.6rem; right: 0.6rem; z-index: 2;
  width: 40px; height: 40px; display: grid; place-items: center;
  background: var(--white); border: 3px solid var(--ink); border-radius: 50%;
  box-shadow: 2px 2px 0 var(--ink); font-size: 1.1rem; line-height: 1;
  transition: transform 0.15s var(--ease-pop);
}
.heart:hover { transform: scale(1.16) rotate(-8deg); }
.heart.is-on { background: var(--red); color: #fff; }

/* ---------- Mini-game ---------- */
.game { display: grid; gap: 1.5rem; align-items: start; grid-template-columns: 1fr; }
@media (min-width: 920px) { .game { grid-template-columns: 1fr 310px; } }
.game__stage {
  position: relative; aspect-ratio: 16 / 10;
  background: linear-gradient(180deg, #24344c, #0d1420);
  border: var(--edge) solid var(--ink); border-radius: var(--r);
  box-shadow: var(--shadow-panel); overflow: hidden; touch-action: manipulation;
}
.game__stage canvas { width: 100%; height: 100%; }
.game__overlay {
  position: absolute; inset: 0; display: grid; place-items: center; text-align: center;
  background: rgba(13, 20, 32, 0.86); backdrop-filter: blur(3px);
  padding: 1.5rem; color: var(--cream); gap: 0.8rem;
}
.game__overlay[hidden] { display: none; }
.game__hud {
  position: absolute; top: 0.7rem; left: 0.7rem; right: 0.7rem; z-index: 3;
  display: flex; justify-content: space-between; pointer-events: none;
  font-family: var(--f-display); color: var(--cream); font-size: 1rem;
}
.game__hud span {
  background: rgba(13, 20, 32, 0.75); border: 3px solid var(--gold);
  border-radius: var(--r-pill); padding: 0.15rem 0.85rem;
}
.game__side { display: flex; flex-direction: column; gap: 1rem; }

/* ---------- Moat game extras ---------- */
.game__stage--moat { aspect-ratio: 15 / 12; background: #0f2537; }
.game__stage--moat canvas { width: 100%; height: 100%; image-rendering: auto; }

/* ---------- Abduction Patrol ---------- */
.game__stage--alien { aspect-ratio: 20 / 13; background: #0b1020; }
.game__stage--alien canvas { width: 100%; height: 100%; }

.atargets { display: grid; gap: 0.9rem; }
.atargets > div { display: grid; grid-template-columns: 44px 1fr; gap: 0.1rem 0.7rem; align-items: center; }
.atargets b { font-family: var(--f-display); grid-column: 2; align-self: end; }
.atargets .small { grid-column: 2; align-self: start; }
.atarget {
  grid-row: span 2; width: 40px; height: 46px;
  border: 3px solid var(--ink); border-radius: 4px;
  display: block; position: relative;
}
.atarget--bad { background: #7d3fd4; }
.atarget--bad::before {
  content: ''; position: absolute; inset: 40% 32% auto 32%; aspect-ratio: 1;
  background: var(--gold); border: 2px solid var(--ink); border-radius: 50%;
}
.atarget--good { background: #ffd9b0; }
.atarget--good::before {
  content: '♥'; position: absolute; top: -13px; left: 50%; translate: -50% 0;
  color: #6ee86e; font-size: 15px; -webkit-text-stroke: 2px var(--ink);
  paint-order: stroke fill;
}
.atarget--good::after {
  content: ''; position: absolute; inset: auto 26% 26% 26%; height: 8px;
  border: 3px solid var(--ink); border-top: 0;
  border-radius: 0 0 20px 20px;
}

.mopad { display: grid; justify-items: center; gap: 0.4rem; }
.mopad__row { display: flex; gap: 0.4rem; }
.mopad .btn { min-width: 54px; font-size: 1.05rem; padding: 0.45rem 0.7rem; }
@media (hover: hover) and (pointer: fine) {
  /* keyboard players do not need a d-pad taking up space */
  .mopad { display: none; }
}

/* ---------- High score board ---------- */
.board { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.board__row {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.5rem 0.8rem;
  border: 3px solid var(--ink); border-radius: var(--r-sm);
  background: var(--white);
}
.board__row.is-me { background: var(--gold); }
.board__pos {
  font-family: var(--f-display); font-size: 1.05rem; min-width: 1.9rem; text-align: center;
}
.board__who {
  font-family: var(--f-body); font-weight: 800; flex: 1;
  /* names are stored already masked — see CK.maskName */
  letter-spacing: 0.02em;
}
.board__score { font-family: var(--f-display); font-size: 1.1rem; color: var(--red); }
.board__row.is-me .board__score { color: var(--ink); }
.board__score small { font-family: var(--f-body); font-size: 0.7rem; font-weight: 700; opacity: 0.7; }

/* ---------- Silly question pop ---------- */
.quiz-pop {
  position: fixed; left: 14px; bottom: 14px; z-index: 330;
  width: min(340px, calc(100vw - 28px));
  padding: 1rem 1.1rem 1.1rem;
  animation: burst-in 0.4s var(--ease-pop);
}
@media (prefers-reduced-motion: reduce) { .quiz-pop { animation: none; } }
.quiz-pop__x {
  position: absolute; top: 0.35rem; right: 0.5rem;
  width: 28px; height: 28px; border-radius: 50%;
  border: 2px solid var(--ink); background: var(--white);
  font-size: 1.1rem; line-height: 1; display: grid; place-items: center;
}
.quiz-pop__x:hover { background: var(--red); color: #fff; }
.quiz-pop__q {
  font-family: var(--f-display); font-size: 1rem; line-height: 1.25;
  margin-bottom: 0.7rem; padding-right: 1.4rem;
}
.quiz-pop__a { display: flex; flex-direction: column; gap: 0.4rem; }
.quiz-pop__a .btn { justify-content: flex-start; text-align: left; }
@media (max-width: 520px) {
  /* keep clear of the prankster toggle */
  .quiz-pop { bottom: 72px; }
}

/* ---------- The Chronicle ---------- */
.chron { display: flex; flex-direction: column; gap: 0.8rem; }
.chron__item {
  border: 3px solid var(--ink); border-radius: var(--r);
  background: var(--white); overflow: hidden;
  box-shadow: var(--shadow-panel-sm);
}
.chron__item.is-read { background: #fffaf0; }
.chron__head {
  display: flex; align-items: center; gap: 0.8rem; width: 100%;
  padding: 0.9rem 1rem; text-align: left;
}
.chron__head:hover { background: var(--cream-2); }
.chron__ico {
  width: 44px; height: 44px; flex: none; display: grid; place-items: center;
  border: 3px solid var(--ink); border-radius: 50%;
  background: var(--grad-fire); font-size: 1.2rem;
}
.chron__item.is-read .chron__ico { background: var(--gold); }
.chron__t { flex: 1; }
.chron__t b { display: block; font-family: var(--f-display); font-size: 1.05rem; }
.chron__t span { font-size: 0.8rem; color: #4a443c; }
.chron__seal {
  font-family: var(--f-action); font-size: 0.85rem; letter-spacing: 0.04em;
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.1rem 0.7rem; background: var(--cream-2); white-space: nowrap;
}
.chron__item.is-read .chron__seal { background: var(--gold-l); }
.chron__body {
  padding: 0 1rem 1rem calc(44px + 1.8rem);
  font-size: 0.95rem; line-height: 1.65;
  display: grid; gap: 1rem; align-items: start;
  grid-template-columns: 190px 1fr;
}
.chron__body[hidden] { display: none; }
@media (max-width: 680px) {
  .chron__body { grid-template-columns: 1fr; padding-left: 1rem; }
}
/* the little storyboard panel */
.chron__panel {
  border: 3px solid var(--ink); border-radius: var(--r-sm);
  overflow: hidden; background: var(--cream-2);
  box-shadow: 3px 3px 0 var(--ink);
  aspect-ratio: 6 / 5;
}
.chron__text { margin: 0; }

/* lock states */
.chron__item.is-locked { opacity: 0.72; background: #f3ece1; }
.chron__item.is-locked .chron__ico { background: #cfc4b4; filter: grayscale(1); }
.chron__item.is-locked .chron__head { cursor: not-allowed; }
.chron__item.is-locked .chron__head:hover { background: none; }
.chron__item.is-ready { border-color: var(--gold); box-shadow: 5px 5px 0 var(--gold); }
.chron__item.is-ready .chron__seal { background: var(--gold); }
.chron__item.is-ready .chron__ico { animation: keyglow 1.6s ease-in-out infinite; }
@keyframes keyglow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 194, 14, 0.7); }
  50% { box-shadow: 0 0 0 10px rgba(255, 194, 14, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .chron__item.is-ready .chron__ico { animation: none; }
}

/* ---------- Filters / chips ---------- */
.filters { display: flex; flex-wrap: wrap; gap: 0.55rem; margin-bottom: 1.8rem; align-items: center; }
.chip {
  font-family: var(--f-display); font-size: 0.92rem;
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.4rem 1rem; background: var(--white); color: var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  transition: transform 0.15s var(--ease-pop), background 0.15s;
}
.chip:hover { transform: translateY(-2px); }
.chip[aria-pressed='true'] { background: var(--blue); color: var(--white); }
/* The clearance chip reads as a price tag rather than another category —
   it reaches stock that is deliberately off every category shelf, so it
   should not look like one of them. Gold on ink keeps it above 7:1. */
/* Hut roof picker. The swatches use the real templates at real proportions,
   so what you pick is what the trail shows. */
.hutpick { display: flex; flex-wrap: wrap; gap: 0.55rem; }
.hutpick__opt {
  position: relative; width: 104px; padding: 0.3rem 0.3rem 0.35rem; cursor: pointer;
  border: 3px solid var(--ink); border-radius: var(--r);
  background: var(--cream-2); box-shadow: 2px 2px 0 var(--ink);
  transition: transform 0.14s var(--ease-pop);
  display: flex; flex-direction: column; gap: 0.2rem; align-items: center;
}
.hutpick__opt:hover { transform: translateY(-2px); }
.hutpick__opt.is-on { background: var(--gold); box-shadow: 3px 3px 0 var(--ink); }
.hutpick__art { display: block; width: 100%; height: 42px; }
.hutpick__art svg, .hutpick__art img {
  width: 100%; height: 100%; display: block; object-fit: cover; border-radius: 6px;
}
.hutpick__name {
  font-size: 0.72rem; line-height: 1.1; text-align: center; color: var(--ink);
}
.hutpick__opt--own [data-hutclear] {
  position: absolute; top: -8px; right: -8px; padding: 0.05rem 0.4rem;
}

/* Emoji palette in the category editor. Grouped and scrollable rather than
   one long wall — 80 icons in a single block is harder to search than no
   palette at all. */
.emopal {
  max-height: 210px; overflow-y: auto;
  border: 3px solid var(--ink); border-radius: var(--r);
  background: var(--cream-2); padding: 0.5rem 0.6rem;
}
.emopal__grp { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; margin-bottom: 0.45rem; }
.emopal__lbl {
  flex: 0 0 100%; font-family: var(--f-action); font-size: 0.74rem;
  letter-spacing: 0.06em; text-transform: uppercase; color: #6b6257;
}
.emopal__btn {
  width: 38px; height: 38px; font-size: 1.15rem; line-height: 1;
  padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: 9px;
  background: var(--white); box-shadow: 1px 1px 0 rgba(13, 20, 32, 0.25);
  transition: transform 0.12s var(--ease-pop), border-color 0.12s;
}
.emopal__btn:hover, .emopal__btn:focus-visible { transform: translateY(-2px); border-color: var(--blue); }
.emopal__btn.is-on { border-color: var(--ink); background: var(--gold); box-shadow: 2px 2px 0 var(--ink); }

/* A category with nothing in it. Dimmed, not hidden — the quest board above
   lists all six, so hiding one here would make the two disagree. */
.chip--empty { opacity: 0.55; }
.chip--empty .chip__n { background: #6b6257; color: var(--white); }
.chip--sale { background: var(--gold); }
.chip--sale[aria-pressed='true'] { background: var(--red-d); color: var(--white); }
.chip__n {
  display: inline-block; min-width: 1.25em; margin-left: 0.25rem;
  padding: 0 0.3rem; border-radius: var(--r-pill);
  background: var(--ink); color: var(--gold);
  font-size: 0.8em; text-align: center;
}
.chip--sale[aria-pressed='true'] .chip__n { background: var(--white); color: var(--red-d); }

.search {
  display: flex; align-items: center; gap: 0.5rem; margin-left: auto;
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  background: var(--white); padding: 0.3rem 0.9rem; box-shadow: 2px 2px 0 var(--ink);
}
.search input { border: 0; outline: 0; background: none; min-width: 190px; padding: 0.3rem 0; box-shadow: none; }
.search input:focus { box-shadow: none; }

/* ---------- Masonry / portfolio ---------- */
.masonry { columns: 3 285px; column-gap: 1.4rem; }
.masonry > * { break-inside: avoid; margin-bottom: 1.4rem; }
.shot { overflow: hidden; position: relative; }
.shot img, .shot svg { width: 100%; display: block; }
.shot__cap { padding: 0.9rem 1rem 1rem; border-top: var(--edge) solid var(--ink); display: flex; flex-direction: column; gap: 0.25rem; }
.shot__cap b { font-family: var(--f-display); font-size: 1.08rem; }
.shot__cap span { font-size: 0.86rem; color: #4a443c; }

.stars { display: inline-flex; gap: 1px; color: var(--gold); letter-spacing: 1px;
  /* gold on white is 1.6:1 — the glyph needs a full outline to read */
  text-shadow: 1px 1px 0 var(--ink), -1px 1px 0 var(--ink),
    1px -1px 0 var(--ink), -1px -1px 0 var(--ink); }
.stars--dim { color: #cfc4b4; text-shadow: none; }

.rev { padding: 1.2rem 1.25rem 1.3rem; display: flex; flex-direction: column; gap: 0.6rem; }
.rev__head { display: flex; align-items: center; gap: 0.7rem; }
.rev__av {
  width: 46px; height: 46px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-family: var(--f-display);
  border: 3px solid var(--ink); background: var(--grad-fire); color: #fff;
}
.rev__meta { font-size: 0.8rem; color: #4a443c; }
.rev__photo { border: 3px solid var(--ink); border-radius: var(--r-sm); overflow: hidden; }
.rev__photo img { width: 100%; max-height: 300px; object-fit: cover; }

.rate { display: inline-flex; gap: 0.15rem; }
.rate button { font-size: 2rem; line-height: 1; color: #cfc4b4; padding: 0 0.1rem;
  transition: transform 0.15s var(--ease-pop), color 0.15s; }
.rate button:hover { transform: scale(1.25) rotate(-8deg); }
.rate button.is-on { color: var(--gold); text-shadow: 1px 1px 0 var(--ink); }

/* ---------- Forms ---------- */
.field { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 1rem; }
.field > label, .lbl { font-family: var(--f-display); font-size: 0.98rem; }
.field .hint { font-size: 0.8rem; color: #57504a; font-weight: 700; }
.input, input[type='text'], input[type='email'], input[type='password'],
input[type='date'], input[type='number'], input[type='search'], select, textarea {
  font-family: var(--f-body); font-weight: 700;
  border: 3px solid var(--ink); border-radius: var(--r-sm);
  padding: 0.65rem 0.85rem; background: var(--white); width: 100%;
  transition: box-shadow 0.15s;
}
.input:focus, input:focus, select:focus, textarea:focus { outline: 0; box-shadow: 3px 3px 0 var(--ink); }
textarea { min-height: 110px; resize: vertical; }
input[disabled], select[disabled], textarea[disabled] { background: #ece2d4; cursor: not-allowed; }
.row { display: grid; gap: 0 1rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.err { color: var(--red); font-size: 0.88rem; font-weight: 800; }
.ok { color: #15803d; font-size: 0.88rem; font-weight: 800; }

.drop {
  border: var(--edge) dashed var(--ink); border-radius: var(--r);
  padding: 1.6rem 1.2rem; text-align: center; background: var(--white);
  transition: background 0.15s, transform 0.15s; cursor: pointer;
}
.drop:hover, .drop.is-over { background: var(--cream-2); transform: translateY(-2px); }
.drop small { display: block; color: #57504a; margin-top: 0.35rem; font-size: 0.8rem; }
.drop__preview { margin-top: 1rem; }
.drop__preview img { max-height: 220px; margin-inline: auto; border: 3px solid var(--ink); border-radius: var(--r-sm); }

/* ---------- Dashboard ---------- */
.dash { display: grid; gap: 1.4rem; grid-template-columns: 1fr; }
@media (min-width: 1000px) { .dash { grid-template-columns: 300px 1fr; align-items: start; } }
.panel { padding: 1.4rem 1.4rem 1.5rem; }
.panel h3 { margin-bottom: 0.9rem; }

.sidenav { display: flex; flex-direction: column; gap: 0.3rem; }
.sidenav button {
  font-family: var(--f-display); text-align: left; font-size: 1.02rem;
  padding: 0.65rem 0.95rem; border-radius: var(--r-sm);
  border: 3px solid transparent; transition: background 0.15s, border-color 0.15s;
}
.sidenav button:hover { background: var(--cream-2); }
.sidenav button[aria-selected='true'] { background: var(--blue); color: var(--white); border-color: var(--ink); }

.stats { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.stat { padding: 1.05rem 1.1rem; text-align: center; }
.stat b { font-family: var(--f-display); font-size: 2rem; display: block; line-height: 1.1; }
.stat span { font-size: 0.84rem; color: #4a443c; }
.stat:nth-child(4n + 1) { background: #ffd9b0; }
.stat:nth-child(4n + 2) { background: #b8cef0; }
.stat:nth-child(4n + 3) { background: #ffe59a; }
.stat:nth-child(4n + 4) { background: #ffc0b4; }

.bar { height: 18px; border: 3px solid var(--ink); border-radius: var(--r-pill);
  background: var(--white); overflow: hidden; margin: 0.5rem 0 0.35rem; }
.bar i { display: block; height: 100%; width: 0; background: var(--grad-fire); transition: width 0.9s var(--ease-pop); }

.tiers { display: grid; gap: 0.7rem; grid-template-columns: repeat(auto-fit, minmax(145px, 1fr)); }
.tier { padding: 0.85rem; border: 3px solid var(--ink); border-radius: var(--r-sm);
  background: var(--white); text-align: center; opacity: 0.45; }
.tier.is-on { opacity: 1; background: var(--grad-fire); color: #fff; box-shadow: var(--shadow-panel-sm); }
.tier b { font-family: var(--f-display); display: block; }
.tier span { display: block; font-size: 0.78rem; }
.tier span + span { margin-top: 0.25rem; opacity: 0.85; line-height: 1.35; }

.list { display: flex; flex-direction: column; gap: 0.65rem; }
.item { display: flex; gap: 0.9rem; align-items: center; flex-wrap: wrap;
  padding: 0.85rem 1rem; border: 3px solid var(--ink); border-radius: var(--r-sm); background: var(--white); }
.item__main { flex: 1; min-width: 180px; }
.item__main b { font-family: var(--f-display); }
.item__main span { display: block; font-size: 0.84rem; color: #4a443c; }
.pill { font-family: var(--f-display); font-size: 0.8rem;
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.1rem 0.7rem; background: var(--cream-2); white-space: nowrap; }
.pill--lime { background: var(--gold-l); }
.pill--cyan { background: var(--blue); color: #fff; }
.pill--yellow { background: var(--gold); }
.pill--magenta { background: var(--red); color: #fff; }

.copyrow { display: flex; gap: 0.5rem; }
.copyrow input { font-size: 0.85rem; }

.track { display: flex; align-items: center; width: 100%; margin-top: 0.6rem; position: relative; z-index: 0; }
.track__step { flex: 1; text-align: center; position: relative; font-size: 0.72rem; opacity: 0.4; }
.track__step::before { content: ''; display: block; width: 16px; height: 16px; margin: 0 auto 0.3rem;
  border: 3px solid var(--ink); border-radius: 50%; background: var(--white); }
.track__step::after { content: ''; position: absolute; top: 8px; left: -50%; width: 100%; height: 3px;
  background: var(--ink); z-index: -1; }
.track__step:first-child::after { display: none; }
.track__step.is-done { opacity: 1; }
.track__step.is-done::before { background: var(--gold); }

/* ---------- Tables ---------- */
.tablewrap { overflow-x: auto; border: 3px solid var(--ink); border-radius: var(--r-sm); background: var(--white); }
table { border-collapse: collapse; width: 100%; min-width: 640px; font-size: 0.9rem; }
th, td { padding: 0.7rem 0.85rem; text-align: left; border-bottom: 2px solid #e6dac8; }
th { font-family: var(--f-display); background: var(--cream-2); position: sticky; top: 0; }
tbody tr:hover { background: #fffaf0; }
td .btn--sm { margin-right: 0.3rem; }

/* ---------- Modal — bursts in, poofs out ---------- */
.modal {
  position: fixed; inset: 0; z-index: 200; display: none;
  align-items: center; justify-content: center; padding: 1.2rem;
  background: rgba(13, 20, 32, 0.72); backdrop-filter: blur(4px);
}
.modal.is-open { display: flex; animation: fade 0.18s; }
@keyframes fade { from { opacity: 0; } }
.modal__box {
  width: min(100%, 580px); max-height: 90vh; overflow-y: auto;
  background: var(--cream); border: var(--edge) solid var(--ink);
  border-radius: var(--r-lg); box-shadow: var(--shadow-panel), var(--shadow-deep);
  padding: 1.7rem 1.6rem 1.8rem; position: relative;
  animation: burst-in 0.4s var(--ease-pop);
}
.modal__box--wide { width: min(100%, 820px); }
/* The product view carries two columns of its own, so 820 left each side
   around 350px — not enough for a price and a button side by side. */
.modal__box--wide:has(.pdp) { width: min(100%, 1040px); }
@keyframes burst-in {
  0% { transform: scale(0.55) rotate(-6deg); opacity: 0; }
  60% { transform: scale(1.04) rotate(1.5deg); opacity: 1; }
  100% { transform: scale(1) rotate(0); }
}
.modal.is-closing .modal__box { animation: poof-out 0.28s ease-in forwards; }
@keyframes poof-out {
  to { transform: scale(0.7) rotate(4deg); opacity: 0; filter: blur(6px); }
}
@media (prefers-reduced-motion: reduce) {
  .modal__box, .modal.is-closing .modal__box { animation: none; }
  .modal.is-open { animation: none; }
}
.modal__x {
  position: absolute; top: 0.8rem; right: 0.9rem; font-size: 1.6rem; line-height: 1;
  width: 40px; height: 40px; border-radius: 50%; border: 3px solid var(--ink);
  background: var(--white); display: grid; place-items: center;
}
.modal__x:hover { background: var(--red); color: #fff; transform: rotate(90deg); }
.modal__x { transition: transform 0.2s var(--ease-pop), background 0.15s; }
.modal h2 { margin-bottom: 0.4rem; }
.modal__lede { color: #4a443c; margin-bottom: 1.2rem; }

/* ---------- Toast ---------- */
.toasts {
  position: fixed; left: 50%; translate: -50% 0; bottom: 1.2rem; z-index: 300;
  display: flex; flex-direction: column; gap: 0.55rem; align-items: center;
  pointer-events: none; width: min(100% - 2rem, 480px);
}
.toast {
  display: flex; align-items: center; gap: 0.65rem; width: 100%;
  background: var(--ink); color: var(--cream);
  border: 3px solid var(--gold); border-radius: var(--r-pill);
  padding: 0.65rem 1.2rem; box-shadow: var(--shadow-deep);
  font-family: var(--f-display); font-size: 0.98rem;
  animation: toast-in 0.35s var(--ease-pop);
}
.toast--win { background: var(--gold); color: var(--ink); border-color: var(--ink); }
.toast--bad { background: var(--red); color: #fff; border-color: var(--ink); }
@keyframes toast-in { from { transform: translateY(24px) scale(0.9); opacity: 0; } }
.toast.is-out { animation: toast-out 0.3s forwards; }
@keyframes toast-out { to { transform: translateY(14px); opacity: 0; } }

#confetti { position: fixed; inset: 0; z-index: 400; pointer-events: none; }

/* ---------- Footer ---------- */
.foot { background: var(--ink); color: var(--cream); padding: 3.2rem 0 2rem; position: relative; overflow: hidden; }
.foot__grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); margin-bottom: 2.2rem; }
.foot h4 { font-family: var(--f-display); font-size: 1.1rem; margin-bottom: 0.7rem; color: var(--gold); }
.foot a { display: block; text-decoration: none; color: var(--cream); opacity: 0.82; padding: 0.22rem 0; font-size: 0.94rem; }
.foot a:hover { opacity: 1; text-decoration: underline; }
.foot__bot { border-top: 3px solid rgba(255, 244, 224, 0.2); padding-top: 1.3rem;
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; font-size: 0.86rem; opacity: 0.75; }

/* Easter egg #2 — hidden crown in the footer */
.egg-crown {
  position: absolute; right: 4%; bottom: 12%; width: 48px; height: 48px;
  opacity: 0; transition: opacity 0.35s, transform 0.35s var(--ease-pop);
  cursor: pointer; padding: 0; border: 0;
}
.foot:hover .egg-crown { opacity: 0.2; }
.egg-crown:hover { opacity: 1 !important; transform: scale(1.3) rotate(-10deg); }
.egg-crown.is-found { animation: spin-out 0.7s var(--ease-pop) forwards; }

/* ---------- Spec notice ---------- */
.specbar { background: var(--blue-d); color: var(--cream); text-align: center;
  font-size: 0.84rem; padding: 0.5rem var(--gut); line-height: 1.4; }
.specbar b { color: var(--gold); }
.dag { color: var(--red); font-weight: 900; }

/* ---------- Database status strip (admin only) ----------
   Deliberately drab when things are working and loud when they are not. The
   failure it exists for is silent: an edit that saved locally, looked saved,
   and never reached the live site. */
.dbbar { text-align: center; font-size: 0.86rem; line-height: 1.45;
  padding: 0.55rem var(--gut); background: var(--blue-d); color: var(--cream); }
.dbbar b { color: var(--gold); }
.dbbar--warn { background: var(--red); color: #fff; }
.dbbar--warn b { color: #fff; text-decoration: underline; }
.dbbar .btn { margin-inline-start: 0.5rem; vertical-align: baseline; }

/* ---------- Scroll reveal ---------- */
[data-rise] { opacity: 0; transform: translateY(22px); }
[data-rise].is-in { opacity: 1; transform: none;
  transition: opacity 0.6s ease, transform 0.6s var(--ease-pop); }
@media (prefers-reduced-motion: reduce) {
  [data-rise] { opacity: 1; transform: none; transition: none; }
}

/* ==========================================================================
   SMOKE BOMB — the exit transition used everywhere
   ========================================================================== */
.poof { position: fixed; z-index: 380; pointer-events: none; translate: -50% -50%; }
.poof i {
  position: absolute; border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #fff, #cfd8e6 45%, #8c9bb0);
  border: 3px solid var(--ink);
  animation: puff 0.72s var(--ease-pop) forwards;
}
@keyframes puff {
  0% { transform: translate(0, 0) scale(0.2); opacity: 0; }
  25% { opacity: 1; }
  100% { transform: translate(var(--px), var(--py)) scale(1.5); opacity: 0; }
}

/* ==========================================================================
   PRANKSTERS — the little troublemakers
   ========================================================================== */
.prankster {
  position: fixed; z-index: 370; width: 96px; height: 96px;
  pointer-events: auto; cursor: pointer; border: 0; padding: 0;
  translate: -50% -50%;
  filter: drop-shadow(3px 5px 0 rgba(0, 0, 0, 0.45));
  transition: filter 0.15s;
}
.prankster:hover { filter: drop-shadow(0 0 12px var(--gold)) drop-shadow(3px 5px 0 rgba(0, 0, 0, 0.45)); }
.prankster svg { width: 100%; height: 100%; overflow: visible; }
.prankster.is-flipped svg { transform: scaleX(-1); }
/* legs pump while running */
/* gait matched to the slower travel speed — sprinting legs under a
   slow-moving body reads as a glitch */
.prankster.is-running .pk-leg-a { animation: leg-a 0.42s linear infinite; transform-origin: 50% 0; }
.prankster.is-running .pk-leg-b { animation: leg-b 0.42s linear infinite; transform-origin: 50% 0; }
@keyframes leg-a { 0%, 100% { rotate: 22deg; } 50% { rotate: -22deg; } }
@keyframes leg-b { 0%, 100% { rotate: -22deg; } 50% { rotate: 22deg; } }
.prankster.is-running .pk-body { animation: jog 0.42s ease-in-out infinite; }
@keyframes jog { 0%, 100% { translate: 0 0; } 50% { translate: 0 -3px; } }
.prankster .pk-arm { transform-origin: 50% 10%; }
.prankster.is-waving .pk-arm { animation: wave 0.72s ease-in-out infinite; }
@keyframes wave { 0%, 100% { rotate: -12deg; } 50% { rotate: 32deg; } }

/* Doubled over cackling, having just nicked your button.
   Only transform here — .prankster centres itself with the independent
   `translate` property, so re-declaring translate() would shift it twice. */
.prankster.is-laughing { animation: cackle 0.34s ease-in-out infinite; }
@keyframes cackle {
  0%, 100% { transform: rotate(-7deg) scale(1); }
  50% { transform: rotate(7deg) scale(1.07) translateY(-4px); }
}
.prankster.is-laughing .pk-arm { animation: wave 0.3s ease-in-out infinite; }

/* big enough to genuinely block what you were reading */
.prankster--big { width: 190px; height: 190px; z-index: 372; }

/* the stolen button, mid-heist */
.pk-loot {
  position: fixed !important; z-index: 371; margin: 0 !important;
  pointer-events: none; transform-origin: 50% 50%;
}

/* the stolen pointer the prankster runs off with */
.fake-cursor {
  position: fixed; z-index: 372; width: 26px; height: 34px;
  pointer-events: none; translate: -2px -2px;
  filter: drop-shadow(2px 3px 0 rgba(0, 0, 0, 0.5));
}
body.cursor-stolen, body.cursor-stolen * { cursor: none !important; }

/* speech bubble the prankster taunts you with */
.pk-bubble {
  position: fixed; z-index: 371; translate: -50% -100%;
  background: var(--white); color: var(--ink);
  border: 3px solid var(--ink); border-radius: var(--r-pill);
  padding: 0.3rem 0.9rem; font-family: var(--f-action); font-size: 1rem;
  letter-spacing: 0.03em; white-space: nowrap;
  box-shadow: 3px 3px 0 var(--ink);
  animation: burst-in 0.3s var(--ease-pop);
  pointer-events: none;
}
.pk-bubble::after {
  content: ''; position: absolute; left: 50%; bottom: -11px; translate: -50% 0;
  border: 7px solid transparent; border-top-color: var(--ink);
}

/* comic splat a prankster can leave behind */
.pk-splat {
  position: fixed; z-index: 369; translate: -50% -50%; pointer-events: none;
  font-family: var(--f-action); font-size: 3rem; color: var(--gold);
  text-shadow: 3px 3px 0 var(--ink), -3px 3px 0 var(--ink), 3px -3px 0 var(--ink), -3px -3px 0 var(--ink);
  animation: splat 0.9s var(--ease-pop) forwards;
}
@keyframes splat {
  0% { transform: scale(0.2) rotate(-25deg); opacity: 0; }
  30% { transform: scale(1.25) rotate(-8deg); opacity: 1; }
  100% { transform: scale(1) rotate(-8deg); opacity: 0; }
}

/* the "leave me alone" toggle */
.pk-toggle {
  position: fixed; right: 14px; bottom: 14px; z-index: 340;
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--ink); color: var(--gold);
  border: 3px solid var(--gold); font-size: 1.2rem; line-height: 1;
  display: grid; place-items: center;
  box-shadow: var(--shadow-deep);
  transition: transform 0.18s var(--ease-pop);
}
.pk-toggle:hover { transform: scale(1.12) rotate(-8deg); }
.pk-toggle.is-off { color: var(--steel); border-color: var(--steel); }

/* ==========================================================================
   THE CASTLE — account page gatehouse + drawbridge
   ========================================================================== */
.castle {
  position: relative; overflow: hidden;
  padding: clamp(2rem, 5vw, 3.5rem) 0 0;
  color: var(--cream);
  perspective: 1400px;
}
.castle__in { position: relative; width: min(100% - var(--gut) * 2, 860px); margin-inline: auto; }

/* stone gatehouse drawn in CSS so it scales with the container */
.gatehouse { position: relative; }
.gatehouse svg { width: 100%; height: auto; display: block; }

/* The arch opening the bridge sits in.
   These percentages are tied to the gatehouse SVG's viewBox (0 0 800 460):
   opening x 280→520 of 800 = left 35% / width 30%; apex y 170 of 460 = top 37%.
   If the SVG geometry changes, change these to match. */
.gate {
  position: absolute; left: 35%; width: 30%; top: 37%; bottom: 0;
  background: radial-gradient(ellipse at 50% 20%, #1b2635, #05080e 72%);
  border-radius: 50% 50% 0 0 / 34% 34% 0 0;
  overflow: hidden;
  display: grid; place-items: center;
  text-align: center; padding: 12% 7% 6%;
  container-type: inline-size;
}
/* the gate copy has to survive a narrow arch — scale it to the opening */
.gate__inner h1 { font-size: clamp(0.95rem, 3.4cqi, 2.1rem); }
.gate__inner p { font-size: clamp(0.62rem, 1.8cqi, 0.95rem); line-height: 1.45; }
.gate__inner .btn { font-size: clamp(0.6rem, 1.7cqi, 0.95rem); padding: 0.5em 1.1em; }
.gate__inner { position: relative; z-index: 2; }
.gate__inner h1 { font-size: clamp(1.5rem, 3.6vw, 2.4rem); }
.gate__torch {
  position: absolute; width: 14px; height: 14px; border-radius: 50%;
  background: var(--gold); filter: blur(6px);
  animation: flicker 2.4s ease-in-out infinite;
}
@keyframes flicker { 0%, 100% { opacity: 0.55; scale: 1; } 50% { opacity: 1; scale: 1.35; } }

/* THE DRAWBRIDGE
   Physical model, and it matters: the bridge SEALS the arch when closed
   (flat to the wall, facing you, carrying the "halt" sign) and swings DOWN
   TOWARD the viewer to open, foreshortening into a path as it falls.
   So the hinge is at the BOTTOM and closed is rotateX(0) — not the other way
   round, which just renders an invisible edge-on sliver.
   The box is pinned to the same arch geometry as .gate. */
.bridge-wrap {
  position: absolute; left: 35%; width: 30%; top: 37%; bottom: 0;
  perspective: 1100px; perspective-origin: 50% 100%;
  z-index: 4; pointer-events: none;
}
.bridge {
  position: absolute; inset: 0;
  transform-origin: 50% 100%;
  transform: rotateX(0deg);                 /* closed: sealing the gate */
  transition: transform 1.9s cubic-bezier(0.55, 0.03, 0.3, 1.02);
  background:
    repeating-linear-gradient(90deg, #7a4a22 0 26px, #5c3418 26px 31px),
    #6d4020;
  border: var(--edge) solid var(--ink);
  border-radius: 50% 50% 4px 4px / 34% 34% 4px 4px;
  box-shadow: inset 0 0 50px rgba(0, 0, 0, 0.55);
  backface-visibility: hidden;
  display: grid; place-items: center;
  text-align: center; padding: 16% 8% 8%;
  pointer-events: auto;
  container-type: inline-size;
}
/* iron bands across the planks */
.bridge::before, .bridge::after {
  content: ''; position: absolute; left: -1px; right: -1px; height: 11px;
  background: #3a4351; border-block: 3px solid var(--ink); pointer-events: none;
}
.bridge::before { top: 34%; }
.bridge::after { bottom: 18%; }
.bridge.is-open { transform: rotateX(84deg); }
/* once it is down, nothing on its face should be clickable */
.bridge.is-open { pointer-events: none; }

.bridge__face { position: relative; z-index: 2; }
.bridge__face h1 { font-size: clamp(0.95rem, 4cqi, 2.2rem); }
.bridge__face p { font-size: clamp(0.6rem, 1.9cqi, 0.95rem); line-height: 1.4; }
.bridge__face .btn { font-size: clamp(0.58rem, 1.75cqi, 0.92rem); padding: 0.5em 1.05em; }
.bridge.is-open .bridge__face { opacity: 0; transition: opacity 0.4s; }

/* chains — they pay out and go slack as the bridge drops */
.chain {
  position: absolute; top: 30%; width: 6px; height: 26%;
  background: repeating-linear-gradient(180deg, var(--steel) 0 7px, #55606f 7px 11px);
  border: 2px solid var(--ink);
  transform-origin: 50% 0; z-index: 5; pointer-events: none;
  transition: transform 1.9s cubic-bezier(0.55, 0.03, 0.3, 1.02), opacity 0.9s;
}
.chain--l { left: 39%; rotate: 8deg; }
.chain--r { right: 39%; rotate: -8deg; }
.is-open .chain { transform: scaleY(2.4); opacity: 0.55; }

@media (prefers-reduced-motion: reduce) {
  .bridge, .chain { transition: none; }
}

/* what sits behind the gate, revealed once the bridge is down */
.keep {
  opacity: 0; transform: translateY(26px) scale(0.98);
  transition: opacity 0.7s ease 0.9s, transform 0.7s var(--ease-pop) 0.9s;
}
.keep.is-open { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .keep { opacity: 1; transform: none; transition: none; }
}

/* banner across the gatehouse */
.castle__banner {
  position: absolute; top: 4%; left: 50%; translate: -50% 0; z-index: 3;
  font-family: var(--f-action); font-size: clamp(0.9rem, 2vw, 1.3rem);
  letter-spacing: 0.07em; color: var(--gold); text-transform: uppercase;
  text-shadow: 2px 2px 0 var(--ink); white-space: nowrap;
}

/* Sign-in controls live BELOW the castle on narrow screens.
   The arch is only 30% of the width — under ~860px there is physically no
   room for buttons inside it, and they were being clipped, which left no
   way to sign in at all on a phone. The gatehouse stays decorative there. */
.castle__cta { display: none; }
@media (max-width: 860px) {
  .castle__banner {
    position: static; translate: none; white-space: normal;
    display: block; text-align: center; margin-bottom: 0.7rem;
  }
  .bridge__face p, .bridge__face .cluster { display: none; }
  .bridge { padding: 20% 6% 8%; }
  .castle__cta {
    display: flex; justify-content: center; align-items: center;
    flex-wrap: wrap; gap: 0.6rem; padding: 1.4rem var(--gut) 0.2rem;
    text-align: center;
  }
  .castle__cta p { width: 100%; color: var(--cream); font-size: 0.95rem; }
}

/* ---------- Utilities ---------- */
.stack { display: flex; flex-direction: column; gap: 1rem; }
.stack-sm { display: flex; flex-direction: column; gap: 0.5rem; }
.cluster { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }
.between { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; }
.center { text-align: center; }
.mt { margin-top: 1.2rem; }
.mt-lg { margin-top: 2rem; }
.muted { color: #4a443c; }
.section--ink .muted, .hero .muted, .castle .muted { color: var(--cream); opacity: 0.82; }
.small { font-size: 0.88rem; }
.empty { text-align: center; padding: 2.5rem 1rem; color: #6b625a; }
[hidden] { display: none !important; }

/* ---------- Easter egg: the lost camera (showcase) ---------- */
.egg-camera {
  display: inline-block; width: 46px; margin-left: 0.6rem;
  vertical-align: middle; padding: 0; border: 0;
  opacity: 0.16; transition: opacity 0.3s, transform 0.3s var(--ease-pop);
}
.egg-camera:hover { opacity: 1; transform: scale(1.2) rotate(-8deg); }
.egg-camera.is-found { animation: spin-out 0.7s var(--ease-pop) forwards; }

/* ==========================================================================
   ARCADE CAROUSEL (home)
   ========================================================================== */
.carousel { position: relative; }
.carousel__viewport { overflow: hidden; border-radius: var(--r-lg); }
.carousel__track {
  display: flex;
  transition: transform 0.6s var(--ease-pop);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) { .carousel__track { transition: none; } }
.carousel__slide { flex: 0 0 100%; min-width: 0; }

.cslide {
  display: grid; gap: clamp(1.2rem, 3vw, 2.2rem);
  grid-template-columns: 1fr;
  align-items: center;
  background: var(--cream);
  /* Explicit: this is a CREAM card sitting inside .section--ink, which sets
     color:cream. Without this the body copy is cream-on-cream and only the
     elements that set their own colour survive. */
  color: var(--ink);
  border: var(--edge) solid var(--ink);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-panel);
  padding: clamp(1.2rem, 3vw, 2rem);
}
@media (min-width: 820px) { .cslide { grid-template-columns: 320px 1fr; } }

.cslide__art {
  border: var(--edge) solid var(--ink); border-radius: var(--r);
  overflow: hidden; aspect-ratio: 4 / 3; background: var(--cream-2);
  box-shadow: var(--shadow-panel-sm);
}
.cslide__body p { color: #4a443c; }
.cslide__rewards {
  list-style: none; padding: 0; margin: 1rem 0 1.3rem;
  display: flex; flex-direction: column; gap: 0.45rem;
}
.cslide__rewards li {
  display: flex; gap: 0.6rem; align-items: flex-start;
  font-size: 0.95rem;
}
.cslide__rewards li::before {
  content: '⭐'; flex: none; line-height: 1.5;
}
.cslide__rewards b { color: var(--red); font-family: var(--f-display); font-size: 1.05rem; }

.carousel__nav {
  position: absolute; top: 50%; translate: 0 -50%; z-index: 3;
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--gold); color: var(--ink);
  border: var(--edge) solid var(--ink);
  box-shadow: var(--shadow-panel-sm);
  font-family: var(--f-display); font-size: 1.9rem; line-height: 1;
  display: grid; place-items: center; padding-bottom: 5px;
  transition: transform 0.15s var(--ease-pop);
}
.carousel__nav:hover { transform: translateY(-50%) scale(1.12); }
.carousel__nav--prev { left: -14px; }
.carousel__nav--next { right: -14px; }
@media (max-width: 700px) {
  .carousel__nav--prev { left: -6px; }
  .carousel__nav--next { right: -6px; }
  .carousel__nav { width: 40px; height: 40px; font-size: 1.5rem; }
}

.carousel__dots {
  display: flex; justify-content: center; gap: 0.5rem; margin-top: 1.2rem;
}
.carousel__dot {
  width: 14px; height: 14px; border-radius: 50%;
  border: 3px solid var(--cream); background: transparent;
  transition: background 0.2s, transform 0.2s var(--ease-pop);
}
.carousel__dot:hover { transform: scale(1.25); }
.carousel__dot[aria-selected='true'] { background: var(--gold); border-color: var(--ink); }

/* real gameplay screenshots in the carousel */
.cslide__art img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- Hero: the custom-cutout signpost ---------- */
.hero__custom {
  display: flex; align-items: center; gap: 0.9rem;
  margin-top: 1.6rem; padding: 0.85rem 1.1rem;
  max-width: 46ch;
  background: rgba(255, 244, 224, 0.10);
  border: 3px solid var(--gold);
  border-radius: var(--r);
  color: var(--cream); text-decoration: none;
  transition: background 0.18s, transform 0.18s var(--ease-pop);
}
.hero__custom:hover {
  background: rgba(255, 244, 224, 0.18);
  transform: translate(-2px, -2px);
  box-shadow: 5px 5px 0 var(--ink);
}
.hero__custom__ico { font-size: 1.6rem; flex: none; }
.hero__custom__txt { font-size: 0.92rem; line-height: 1.45; }
.hero__custom__txt b {
  display: block; font-family: var(--f-display); font-size: 1.02rem;
  color: var(--gold); font-weight: 400;
}
.hero__custom__go {
  margin-left: auto; flex: none;
  font-family: var(--f-display); font-size: 1.4rem; color: var(--gold);
}
@media (max-width: 560px) {
  .hero__custom__ico { font-size: 1.3rem; }
  .hero__custom__txt { font-size: 0.85rem; }
}

/* On short viewports (a standard laptop is 800px tall) the hero ran long
   enough to push the custom-cutout signpost below the fold — which defeated
   the point of putting it there. Tighten the hero rather than move it. */
@media (min-width: 900px) and (max-height: 920px) {
  .hero { padding-block: 2.2rem 3rem; }
  .hero h1 { font-size: clamp(2.2rem, 4.2vw, 3.4rem); margin: 0.8rem 0 0.7rem; }
  .hero__lede { font-size: 1rem; max-width: 54ch; }
  .hero__cta { margin-top: 1.2rem; gap: 0.6rem; }
  .hero__cta .btn--lg { font-size: 1rem; padding: 0.75rem 1.45rem; }
  .hero__custom { margin-top: 1rem; padding: 0.7rem 1rem; }
  .hero__custom__ico { font-size: 1.35rem; }
  .hero__trust { margin-top: 1.4rem; }
  .hero__trust b { font-size: 1.3rem; }
  /* The collage sizes itself from the photos now, so it is scaled down as a
     whole rather than given a height — on a short laptop screen the hero
     blurb has to stay above the fold. */
  .stage { max-width: 430px; margin-inline: auto; }
}

/* ==========================================================================
   THE QUEST BOARD (home categories)
   A frontier trail: huts along a snaking path up to the kingdom.
   Laid out 3-across so it stays compact — six stacked rows made this the
   tallest section on the page. The landscape is decoration; every readable
   element sits on an opaque card above it.
   ========================================================================== */
.quest {
  position: relative; overflow: hidden;
  padding: clamp(2.2rem, 4.5vw, 3.4rem) 0 clamp(2rem, 4vw, 3rem);
  scroll-margin-top: var(--nav-h);
  isolation: isolate;
}
.quest__scene {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: -2; pointer-events: none;
}
.quest::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(ellipse 62% 30% at 50% 8%, rgba(13, 20, 32, 0.62), transparent 70%),
    linear-gradient(180deg, rgba(13, 20, 32, 0.34), transparent 24%);
}
.quest__in { position: relative; }

/* ---- the kingdom at the summit ---- */
.quest__peak { text-align: center; margin-bottom: clamp(0.8rem, 2vw, 1.4rem); }
.quest__castle {
  width: clamp(104px, 12vw, 150px); height: auto; margin: 0 auto 0.4rem;
  filter: drop-shadow(0 8px 0 rgba(0, 0, 0, 0.3));
}
.quest__peak p {
  color: var(--cream); font-size: 0.98rem; margin-top: 0.4rem;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.65);
}

/* ---- the trail ---- */
.quest__trail { position: relative; }
.quest__path {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
}
.quest__path-base {
  fill: none; stroke: #8a5a2b; stroke-width: 10;
  stroke-linecap: round; vector-effect: non-scaling-stroke; opacity: .85;
}
.quest__path-dash {
  fill: none; stroke: var(--gold); stroke-width: 3;
  stroke-linecap: round; stroke-dasharray: 1 15;
  vector-effect: non-scaling-stroke;
}

.quest__stops {
  position: relative; z-index: 1;
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(1rem, 2.4vw, 1.8rem);
  grid-template-columns: 1fr;
  justify-items: center;
}
/* Snake layout: row 1 runs left→right, row 2 runs right→left, so the path
   can flow continuously through all six. */
@media (min-width: 1000px) {
  /* ⚠ A REPEATING pattern, not six fixed cells.
     This used to be six hardcoded `grid-area` rules, one per category — so
     adding a seventh gave it no placement at all and the trail broke the
     first time the catalogue grew. The 6n formulas below snake left-to-right
     then right-to-left forever, so the path is correct at 6 categories, at
     7, and at 30.
     `dense` is required: an item wanting column 2 after one placed in column
     3 sits BEHIND the auto-placement cursor, and sparse packing would push it
     to a new row instead of filling the gap — which is what turns the snake
     back into a staircase. */
  .quest__stops { grid-template-columns: repeat(3, 1fr); grid-auto-flow: row dense; }
  .quest__stop:nth-child(6n + 1) { grid-column: 1; }
  .quest__stop:nth-child(6n + 2) { grid-column: 2; }
  .quest__stop:nth-child(6n + 3) { grid-column: 3; }
  .quest__stop:nth-child(6n + 4) { grid-column: 3; }
  .quest__stop:nth-child(6n + 5) { grid-column: 2; }
  .quest__stop:nth-child(6n + 6) { grid-column: 1; }
}
@media (min-width: 620px) and (max-width: 999px) {
  /* Same repeating snake, two columns wide: down the right, back down the
     left, every four categories. */
  .quest__stops { grid-template-columns: repeat(2, 1fr); grid-auto-flow: row dense; }
  .quest__stop:nth-child(4n + 1) { grid-column: 1; }
  .quest__stop:nth-child(4n + 2) { grid-column: 2; }
  .quest__stop:nth-child(4n + 3) { grid-column: 2; }
  .quest__stop:nth-child(4n + 4) { grid-column: 1; }
}
@media (max-width: 619px) {
  .quest__path { display: none; }
  .quest__trail::before {
    content: ''; position: absolute; left: 50%; translate: -50% 0; top: 0; bottom: 0; width: 6px;
    background: repeating-linear-gradient(180deg, var(--gold) 0 9px, transparent 9px 22px);
    border-radius: 3px; z-index: 0;
  }
}
/* position:relative so the hover bubble anchors to the stop rather than to
   the section, which would put every bubble in the same place. */
.quest__stop { width: 100%; max-width: 300px; position: relative; }

/* "Tap a hut" — the trail looked decorative, so people scrolled past it to
   the filter chips below without realising the huts were the same thing. */
.quest__how {
  margin: 0.35rem 0 0; font-size: 0.92rem;
  color: var(--cream); opacity: 0.92;
}
.quest__how b { color: var(--gold); }

/* ---- a hut ---- */
.hut {
  position: relative; display: block; text-decoration: none; color: var(--ink);
  padding-top: 62px;                      /* room for the pitched roof */
  transition: transform 0.18s var(--ease-pop);
}
.hut:hover { transform: translateY(-5px); }
.hut:hover .hut__body { box-shadow: 9px 10px 0 var(--ink); }

.hut__roof {
  position: absolute; left: -12px; top: 0; height: 74px;
  z-index: 2; overflow: visible;
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, 0.25));
  /* ⚠ EXPLICIT WIDTH, and it is not optional.
     This used to be left:-12px / right:-12px with width:auto, which does not
     stretch a REPLACED element. An <svg> carrying only a viewBox has an
     intrinsic ASPECT RATIO, so width:auto resolved from the height instead:
     74px tall x the 300:96 ratio = 231px, on a 300px card. Every roof on the
     trail has been 69px too narrow, sitting left of centre with the card
     poking out to its right.
     max-width:none is required too — the global `img, svg { max-width: 100% }`
     on line 125 would otherwise clamp this straight back to the card width
     and eat the overhang. */
  width: calc(100% + 24px); right: auto; max-width: none;
}
/* An uploaded roof. `cover` rather than `contain` so a photo of any shape
   fills the slot instead of letterboxing against the sky, and the ink border
   keeps it looking like part of the board rather than pasted on top.

   ⚠ THE EXPLICIT WIDTH IS REQUIRED — this is why uploaded roofs came out
   narrow while the drawn ones spanned the card. `.hut__roof` positions with
   left:-12px and right:-12px, which stretches an inline <svg> because an SVG
   with only a viewBox has no intrinsic width. An <img> DOES have one, and CSS
   says an absolutely positioned REPLACED element with width:auto uses its
   intrinsic width and ignores the left/right pair. So the photo sized itself
   to the pixels of the file instead of to the hut.
   calc(100% + 24px) restates the same span the drawn roofs get. */
.hut__roof--photo {
  /* Width and max-width now come from .hut__roof — an uploaded picture gets
     exactly the same span as a drawn one, so swapping between them does not
     move the roofline. */
  object-fit: cover; border: var(--edge) solid var(--ink);
  border-radius: var(--r) var(--r) 0 0; background: var(--cream-2);
}

.hut__body {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 0.7rem;
  background: var(--cream);
  border: var(--edge) solid var(--ink);
  border-radius: 0 0 var(--r) var(--r);
  box-shadow: var(--shadow-panel);
  padding: 0.85rem 0.9rem 1.5rem;
  /* timber corner posts */
  background-image:
    linear-gradient(90deg, rgba(109, 64, 32, 0.18) 0 7px, transparent 7px),
    linear-gradient(-90deg, rgba(109, 64, 32, 0.18) 0 7px, transparent 7px);
}
/* a little door, so it reads as a building rather than a tent */
.hut__door {
  position: absolute; left: 50%; translate: -50% 0; bottom: -4px;
  width: 34px; height: 26px;
  background: #6d4020;
  border: 4px solid var(--ink);
  border-bottom: 0;
  border-radius: 17px 17px 0 0;
}
.hut__door::after {
  content: ''; position: absolute; right: 5px; top: 12px;
  width: 4px; height: 4px; border-radius: 50%; background: var(--gold);
}

.hut__ico {
  flex: none; width: 46px; height: 46px; display: grid; place-items: center;
  font-size: 1.45rem;
  background: var(--gold);
  border: 3px solid var(--ink); border-radius: 50%;
  box-shadow: 2px 2px 0 var(--ink);
}
.hut__txt { min-width: 0; }
.hut__txt b {
  /* The hut card is 300px across and the name is the thing being chosen —
     0.98rem left it smaller than the body copy underneath it. */
  display: block; font-family: var(--f-display);
  font-size: clamp(1.15rem, 1.5vw, 1.32rem);
  line-height: 1.1; margin-bottom: 0.15rem;
}

/* ---- the blurb bubble ----
   The card carries the NAME only; the description appears on hover as a
   little speech bubble under the hut. Cards that were three lines of small
   grey text are now one big readable name, which is the thing being chosen. */
/* ON THE ROOF of its own hut — like a signboard nailed to the gable.
   It was below the card first, which the section's overflow:hidden clipped on
   the bottom row. Flipping the bottom row upward fixed the clipping and
   created a worse problem: the row gap is shorter than a bubble, so it
   surfaced under the hut ABOVE and read as that category's description.
   The roof is the one place that is unambiguously THIS hut, always inside the
   section, and identical for every row. */
.hut__tip {
  position: absolute; left: 50%; top: 2px; translate: -50% 0;
  z-index: 6; width: max-content; max-width: 268px;
  padding: 0.5rem 0.75rem;
  font-size: 0.82rem; line-height: 1.3; color: var(--ink); text-align: center;
  background: var(--white);
  border: 3px solid var(--ink); border-radius: var(--r);
  box-shadow: 3px 4px 0 var(--ink);
}
/* the pointer hangs DOWN into the card, so the bubble is tied to the hut it
   is sitting on rather than floating between two of them */
.hut__tip::before {
  content: ''; position: absolute; left: 50%; bottom: -9px; translate: -50% 0;
  width: 13px; height: 13px; background: var(--white); rotate: 45deg;
  border-right: 3px solid var(--ink); border-bottom: 3px solid var(--ink);
}


/* Only hide it where hovering is actually possible. A phone never fires
   hover, so on touch the description simply stays visible under the name —
   the alternative is a description no touch user can ever read. */
@media (hover: hover) and (pointer: fine) {
  .hut__tip {
    opacity: 0; visibility: hidden;
    translate: -50% -6px;
    transition: opacity 0.16s, translate 0.16s var(--ease-pop), visibility 0.16s;
  }

  /* :focus-visible ONLY — never plain :focus.
     A hut is a link, and clicking one leaves it focused, so `:focus` left
     that bubble open permanently while you carried on down the page. That is
     the "one is open on its own" bug. focus-visible matches keyboard focus
     and not mouse clicks, which is exactly the distinction wanted here. */
  .hut:hover .hut__tip,
  .hut:focus-visible .hut__tip {
    opacity: 1; visibility: visible; translate: -50% 0;
  }
  /* Lift the whole stop while its bubble is open, or the hut on the row
     below draws over the top of it. */
  .quest__stop:hover, .quest__stop:focus-within { z-index: 5; }
}
/* TOUCH: the bubble lives on the roof here too, it is simply always open.
   There is no hover on a phone, so there is nothing to reveal it — and the
   earlier fallback (dropping it back inline under the name) meant the quest
   board looked like a different design on mobile. Same signboard, same place,
   just permanently visible.
   The extra padding-top is what it sits in: the drawn roof is 62px and a
   two-line blurb is ~56px, so 88px leaves room without the bubble sliding
   down over the category name. */
@media (hover: none) {
  .hut { padding-top: 88px; }
  .hut__tip {
    opacity: 1; visibility: visible;
    max-width: min(268px, 100%);
  }
}

.hut__marker {
  position: absolute; top: 46px; left: -12px; z-index: 4;
  width: 32px; height: 32px; display: grid; place-items: center;
  font-family: var(--f-display); font-size: 0.92rem;
  background: var(--blue); color: var(--cream);
  border: 3px solid var(--ink); border-radius: 50%;
  box-shadow: 2px 2px 0 var(--ink);
  transform: rotate(-8deg);
}
.hut:hover .hut__marker { background: var(--orange); color: var(--ink); }

/* ==========================================================================
   HELP / FAQ / POLICIES
   ========================================================================== */
.policy-alert {
  border-color: var(--red-d); border-width: 5px;
  box-shadow: 8px 8px 0 var(--red-d);
  background: #fff8f0;
}
.policy-alert p { font-size: 1.02rem; }

.faq { display: flex; flex-direction: column; gap: 0.7rem; }
.faq__item {
  border: var(--edge) solid var(--ink); border-radius: var(--r);
  background: var(--white); overflow: hidden;
  box-shadow: var(--shadow-panel-sm);
}
.faq__item.is-open { box-shadow: var(--shadow-panel); }
.faq__q { margin: 0; font-size: 1rem; }
.faq__q button {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  width: 100%; text-align: left; padding: 1rem 1.1rem;
  font-family: var(--f-display); font-size: 1.04rem; line-height: 1.25;
  color: var(--ink);
}
.faq__q button:hover { background: var(--cream-2); }
.faq__chev {
  flex: none; width: 32px; height: 32px; display: grid; place-items: center;
  border: 3px solid var(--ink); border-radius: 50%;
  background: var(--gold); font-size: 1.2rem; line-height: 1;
  transition: transform 0.2s var(--ease-pop);
}
.faq__item.is-open .faq__chev { transform: rotate(45deg); }
.faq__a {
  padding: 0 1.1rem 1.1rem; font-size: 0.96rem; line-height: 1.7; color: #3f3a34;
  border-top: 2px dashed #e0d6c6; margin-top: -2px; padding-top: 0.9rem;
}
.faq__a[hidden] { display: none; }
.faq__a a { color: var(--blue-d); font-weight: 800; }

.policy { display: grid; gap: 1.1rem; }
.policy__block {
  border-left: 6px solid var(--gold);
  background: var(--white);
  border-radius: var(--r-sm);
  border: 3px solid var(--ink); border-left-width: 8px;
  padding: 1.1rem 1.2rem;
  box-shadow: var(--shadow-panel-sm);
}
.policy__block h3 { font-size: 1.12rem; margin-bottom: 0.5rem; }
.policy__block p { font-size: 0.95rem; line-height: 1.7; color: #3f3a34; }
.policy__block p + p { margin-top: 0.7rem; }
.policy__block a { color: var(--blue-d); font-weight: 800; }

/* the escort marching a caught prankster back */
.prankster.is-marching .pk-arm { animation: none; rotate: -6deg; }
.prankster.is-marching .pk-leg-a { animation-duration: 0.34s; }
.prankster.is-marching .pk-leg-b { animation-duration: 0.34s; }
/* the loot, mid-heist — held aloft, carried, handed back */
.pk-loot { transform-origin: 50% 50%; }

/* ==========================================================================
   ADMIN — catalogue filter bar
   Its own block rather than reusing .row: the filters need to reflow from
   seven across down to two on a phone, and .row is a fixed three-column grid.
   ========================================================================== */
.afilter {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 0.65rem 0.75rem;
  align-items: end;
  padding: 0.9rem 1rem;
  background: var(--cream-2);
  border: 3px solid var(--ink);
  border-radius: var(--r);
}
/* The search box earns more room than a dropdown does. */
.afilter .field:first-child { grid-column: span 2; min-width: 0; }
.afilter .field { margin: 0; }
.afilter label { font-size: 0.82rem; margin-bottom: 0.2rem; }
.afilter input, .afilter select { padding: 0.45rem 0.6rem; font-size: 0.95rem; }

.ftags { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.chip--xs {
  font-size: 0.74rem; padding: 0.12rem 0.55rem; border-width: 2px;
  box-shadow: 1px 1px 0 var(--ink); font-family: var(--f-body); font-weight: 700;
}
.chip.is-on { background: var(--blue); color: var(--white); }

/* Row thumbnails. A fixed box so the rows keep an even height whether the
   product has a photo, a placeholder, or nothing at all. */
.arow__img {
  width: 52px; height: 52px; object-fit: cover; display: block;
  border: 2px solid var(--ink); border-radius: 8px; background: var(--cream-2);
}
.arow__img--none {
  display: grid; place-items: center; color: var(--ink);
  opacity: 0.35; font-weight: 700;
}
.arow__tags { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.3rem; }

@media (max-width: 640px) {
  .afilter { grid-template-columns: 1fr 1fr; }
  .afilter .field:first-child { grid-column: 1 / -1; }
}

/* Admin table rows: compact, one line of actions, and a title column that
   gets the space instead of the buttons. */
.arow__act { display: flex; gap: 0.35rem; flex-wrap: nowrap; justify-content: flex-end; }
.arow__act .btn { padding: 0.3rem 0.7rem; font-size: 0.82rem; white-space: nowrap; }
[data-ptable] td { padding-block: 0.55rem; vertical-align: middle; }
[data-ptable] td:first-child { width: 52px; }
[data-ptable] td:last-child { width: 1%; white-space: nowrap; }

@media (max-width: 900px) {
  /* Below this the three buttons and a title cannot share a line honestly,
     so the actions wrap rather than squeezing the title to nothing. */
  .arow__act { flex-wrap: wrap; justify-content: flex-start; }
}

/* ==========================================================================
   ADMIN — showcase photo assignment
   ========================================================================== */
.sgrid {
  display: grid; gap: 0.9rem;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
.scard {
  margin: 0; border: 3px solid var(--ink); border-radius: var(--r);
  overflow: hidden; background: var(--white); box-shadow: 3px 3px 0 var(--ink);
}
/* A chosen photo is called out, so "which of these have I actually done?"
   is answerable at a glance rather than by reading every pill. */
.scard--picked { border-color: var(--blue); box-shadow: 3px 3px 0 var(--blue); }
.scard__img { aspect-ratio: 4 / 3; overflow: hidden; background: var(--cream-2); }
.scard__img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.scard__body {
  padding: 0.7rem 0.8rem 0.85rem; display: flex; flex-direction: column;
  gap: 0.28rem; align-items: flex-start;
}

/* The picker itself */
.spicker {
  display: grid; gap: 0.6rem;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  max-height: 58vh; overflow-y: auto; padding: 0.2rem;
}
.spick {
  position: relative; padding: 0; cursor: pointer; overflow: hidden;
  aspect-ratio: 4 / 3; background: var(--cream-2);
  border: 3px solid var(--ink); border-radius: 10px;
  transition: transform 0.15s var(--ease-pop);
}
.spick img { width: 100%; height: 100%; object-fit: cover; display: block; }
.spick:hover, .spick:focus-visible { transform: translateY(-3px); }
.spick.is-on { border-color: var(--blue); box-shadow: 0 0 0 3px var(--gold); }
/* Flags a photo already assigned elsewhere. Not blocked — reusing a shot is
   occasionally the right call — but never a silent surprise. */
.spick__used {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: var(--ink); color: var(--white);
  font-size: 0.68rem; font-weight: 700; padding: 0.15rem 0;
  text-transform: uppercase; letter-spacing: 0.04em;
}
