/* Vending Machine puzzle — ported from the standalone prototype.
   Every selector is scoped under .vending-app so the prototype's generic
   class names (.card, .grid, .controls, .legend, .rule, .status, .slot, .toast)
   and its palette never leak into the rest of Puzzmallow (or collide with Bulma).
   The prototype's own :root / body theming is rehomed onto .vending-app. */

.vending-app {
  /* --- day konbini (light) --- */
  --bg: #fff3e3;
  --dot: rgba(255,150,120,0.16);
  --burst: rgba(255,209,62,0.28);
  --ink: #2b2444;
  --muted: #86799f;
  --card: #ffffff;
  --card-line: #ffe0cd;
  --card-shadow: 0 16px 30px -20px rgba(120,60,40,0.5);

  /* the machine — a red jihanki, same day or night */
  --chassis-1: #ff5d6b;
  --chassis-2: #ef2f4c;
  --chassis-edge: #c81f39;
  --trim: #fffdf8;
  --trim-line: #ffd0c4;
  --glass: #141a2e;
  --glass-2: #0c1120;
  --glass-edge: #05070f;
  --bay: #1b2338;
  --bay-line: #2c3550;
  --on-dark: #fdf1e7;
  --muted-dark: #8f9bc4;

  /* pop accents */
  --accent: #ffca28;     /* sunny yellow */
  --accent-ink: #5a3d00;
  --pop-blue: #2ec5ff;
  --pop-pink: #ff6faa;
  --good: #22c07a;
  --bad: #ff4d63;
  --pending: #b9b0c9;

  /* packaging brand colours (poppy, all distinct) */
  --funco: #ff9130;   /* Sunco — warm amber, shifted yellower to read clearly apart from Tasty's pink */
  --fresh: #12bfa4;
  --allgood: #5b7cff;
  --tasty: #ff5aa8;

  --vfont-display: "Arial Rounded MT Bold", "Hiragino Maru Gothic ProN", "Baloo 2", "Quicksand", system-ui, sans-serif;
  --vfont-jp: "Hiragino Maru Gothic ProN", "Yu Gothic", "YuGothic", "Meiryo", "Hiragino Kaku Gothic ProN", sans-serif;
  --vfont-body: "Hiragino Maru Gothic ProN", system-ui, -apple-system, "Segoe UI", "Yu Gothic", Roboto, sans-serif;
  --vfont-mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace;

  color: var(--ink);
  font-family: var(--vfont-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  background-color: var(--bg);
  background-image: radial-gradient(var(--dot) 1.6px, transparent 1.7px);
  background-size: 18px 18px;
  /* full-bleed dotted background: fills the whole content column, square edges */
  border-radius: 0;
  padding: clamp(16px, 2.8vw, 26px) clamp(12px, 2vw, 18px);
}
@media (prefers-color-scheme: dark) {
  .vending-app {
    --bg: #0c0f1f;
    --dot: rgba(120,150,255,0.10);
    --burst: rgba(255,90,120,0.22);
    --ink: #f3ecff;
    --muted: #9c93c2;
    --card: #171a2e;
    --card-line: #2a2f4a;
    --card-shadow: 0 18px 34px -20px rgba(0,0,0,0.7);
    --pending: #59607a;
  }
}
:root[data-theme="light"] .vending-app {
  --bg: #fff3e3; --dot: rgba(255,150,120,0.16); --burst: rgba(255,209,62,0.28);
  --ink: #2b2444; --muted: #86799f; --card: #ffffff; --card-line: #ffe0cd;
  --card-shadow: 0 16px 30px -20px rgba(120,60,40,0.5); --pending: #b9b0c9;
}
:root[data-theme="dark"] .vending-app {
  --bg: #0c0f1f; --dot: rgba(120,150,255,0.10); --burst: rgba(255,90,120,0.22);
  --ink: #f3ecff; --muted: #9c93c2; --card: #171a2e; --card-line: #2a2f4a;
  --card-shadow: 0 18px 34px -20px rgba(0,0,0,0.7); --pending: #59607a;
}

.vending-app * { box-sizing: border-box; }

/* Jihanki only: drop the shared puzzle-container top padding (Bulma .pt-2 ≈ 8px)
   so the dotted vending background starts flush under the header. */
.puzzle-container:has(> .vending-app) { padding-top: 0 !important; }

/* Container-query reference box at the container's width. .vending-stack is wider
   than this on desktop and deliberately overflows it (that's how the
   Restock/Manifest card comes to sit outside .puzzle-container), so nothing here
   may clip: inline-size containment doesn't paint-clip, so the overflow still
   shows. Its only job is to give the left column a 100cqw to measure against. */
.vending-app .vending-bounds { container-type: inline-size; }

/* ---- Vertical stack for all devices: machine, then crate, then manifest ----
   Capped and centred so the 4x4 grid gets most of the width without becoming
   absurdly tall on wide desktops. */
.vending-app .vending-stack {
  display: flex; flex-direction: column;
  gap: clamp(12px, 2.2vw, 20px);
  max-width: 640px; margin: 0 auto;
  text-align: left;
}

/* ---- Machine ---- : a single thin red frame around the glass, so nearly all
   the width goes to the 4x4 grid (no cream inner cabinet, no marquee plate). */
.vending-app .machine {
  position: relative;
  background: linear-gradient(158deg, var(--chassis-1), var(--chassis-2));
  border: 1.5px solid var(--chassis-edge);
  /* red chassis frame ~a third of its former thickness (was 2px border +
     clamp(7,1.3vw,11px) padding) so almost all the width goes to the glass. */
  border-radius: 15px; padding: clamp(2px, 0.45vw, 4px);
  box-shadow: 0 16px 32px -22px rgba(200,30,55,0.6), inset 0 2px 0 rgba(255,255,255,0.28);
  display: flex; flex-direction: column; gap: 8px;
}
.vending-app .machine::before {
  content: ""; position: absolute; inset: -30px -30px auto -30px; height: 160px; z-index: -1;
  background: radial-gradient(closest-side, var(--burst), transparent 72%);
  filter: blur(4px);
}
/* glass display */
.vending-app .glass {
  position: relative; background: linear-gradient(170deg, var(--glass), var(--glass-2));
  /* black inset band (holds the row/column numbers) halved: was 22/7/7/19. */
  border-radius: 11px; padding: 14px 7px 7px 13px;
  border: 2px solid var(--glass-edge);
  box-shadow: inset 0 3px 26px rgba(0,0,0,0.7);
  overflow: hidden;
}
.vending-app .glass::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, rgba(255,255,255,0.10) 0%, transparent 30%, transparent 72%, rgba(255,255,255,0.05) 100%);
}
.vending-app .grid { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
/* faint interior lighting from the top */
.vending-app .glass::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 40%; pointer-events: none; z-index: 1;
  background: linear-gradient(180deg, rgba(180,210,255,0.10), transparent);
}
/* coloured outline around each of the five 2x2 sections */
.vending-app .regions {
  /* z-index 1: the section outlines sit above the empty bays but BEHIND placed
     products (.prod is z-index 2) and notes (.marks is z-index 3). */
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 1fr); gap: 6px;
}
.vending-app .region { border: 2px solid; border-radius: 10px; margin: 1px; transition: opacity .25s ease; }
.vending-app .region.reg-tl { border-color: #2ec5ff; }
.vending-app .region.reg-tr { border-color: #ff6faa; }
.vending-app .region.reg-bl { border-color: #ffca28; }
.vending-app .region.reg-br { border-color: #22c07a; }
/* once every bay in a section is stocked, its outline has done its job — fade it. */
.vending-app .region.filled { opacity: 0; }
.vending-app .colnums { position: absolute; top: 4px; left: 13px; right: 7px; display: grid; grid-template-columns: repeat(4,1fr); gap: 6px; }
.vending-app .colnums span { text-align: center; font-family: var(--vfont-mono); font-size: 9px; color: var(--muted-dark); }
.vending-app .rownums { position: absolute; top: 14px; bottom: 7px; left: 3px; width: 9px; display: grid; grid-template-rows: repeat(4,1fr); gap: 6px; }
.vending-app .rownums span { display: flex; align-items: center; justify-content: center; font-family: var(--vfont-mono); font-size: 9px; color: var(--muted-dark); }

.vending-app .bay {
  position: relative; aspect-ratio: 1 / 1; border: 2px solid var(--bay-line);
  background: var(--bay); border-radius: 12px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease, transform .12s ease;
}
.vending-app .bay:hover { border-color: color-mix(in srgb, var(--accent) 65%, var(--bay-line)); }
/* selecting a crate product no longer outlines the empty bays (.droptarget is a no-op). */
.vending-app .bay:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.vending-app .bay .coil { width: 46%; height: 3px; border-radius: 3px; background: repeating-linear-gradient(90deg, #333c58 0 4px, transparent 4px 7px); opacity: .7; }
.vending-app .bay.given { cursor: default; }

/* Pencil marks (notes overlay) on empty bays: 2 rows of 4 (types, brands).
   Price is fixed on the bay, so it has no note. The top strip is left clear for
   the .bprice tag. Each mark cycles grey → crossed → highlighted. */
.vending-app .bay .marks {
  position: absolute; inset: 14px 12px 8px 12px; z-index: 3;
  display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: min-content;
  align-content: center; row-gap: 10px; column-gap: 2px;
}
.vending-app .mk {
  position: relative; display: flex; align-items: center; justify-content: center;
  border-radius: 4px; cursor: pointer; user-select: none; line-height: 1;
  font-size: 11px; color: var(--on-dark);
  opacity: 1;                                        /* notes are full-colour in every state */
  padding: 6px 3px; box-sizing: border-box;          /* taller hit/ring box — a rectangle around the icon */
  transition: filter .1s ease, background .1s ease;
}
.vending-app .mk:hover { background: rgba(255,255,255,.12); }
.vending-app .mk .sw { width: 60%; height: auto; aspect-ratio: 1 / 1; border-radius: 3px; box-shadow: inset 0 1px 0 rgba(255,255,255,.4); }
/* highlighted: this candidate is "on" — full colour, ringed */
.vending-app .mk.on { opacity: 1; filter: none; background: rgba(255,255,255,.14); box-shadow: inset 0 0 0 1.5px var(--accent); }
/* crossed out: eliminated — marked only by the red slash, no dimming */
.vending-app .mk.cross { opacity: 1; }
.vending-app .mk.cross::after {
  content: ""; position: absolute; inset: 12%; pointer-events: none;
  background: linear-gradient(45deg, transparent 42%, var(--bad) 42%, var(--bad) 58%, transparent 58%);
}
.vending-app .bay.given:hover { border-color: var(--bay-line); }
/* pre-placed (given) products: no yellow ring — they just read as normal tiles that can't be moved. */
.vending-app .prod.pinned { cursor: default; box-shadow: inset 0 2px 0 rgba(255,255,255,0.32), inset 0 0 0 2px rgba(255,255,255,0.14), 0 3px 6px rgba(0,0,0,0.4); animation: none; }

/* product tile — brand label on top, emoji centred. Price is on the bay, not the
   product. */
.vending-app .prod {
  --b: var(--muted);
  width: 100%; height: 100%; border: none; cursor: pointer;
  border-radius: 11px; padding: 6px;
  background: linear-gradient(165deg, color-mix(in srgb, var(--b) 82%, white 18%), var(--b));
  color: #fff; display: grid; grid-template-rows: auto 1fr; gap: 3px;
  position: relative; z-index: 2; overflow: hidden; text-align: left;
  box-shadow: inset 0 2px 0 rgba(255,255,255,0.32), inset 0 0 0 2px rgba(255,255,255,0.14), 0 3px 6px rgba(0,0,0,0.4);
  transition: transform .13s ease, box-shadow .13s ease, opacity .13s ease;
  animation: vending-drop .2s ease;
}
@keyframes vending-drop { from { transform: translateY(-8px) scale(.9); opacity: 0; } to { transform: none; opacity: 1; } }
.vending-app .prod .pb { display: block; height: 12px; }
.vending-app .prod .pe { font-size: clamp(15px, 3vw, 20px); line-height: 1; align-self: center; justify-self: center; filter: drop-shadow(0 2px 2px rgba(0,0,0,.35)); }
.vending-app .prod:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Brand wordmark icon — an SVG rendered as a CSS mask so it can be recoloured:
   black on the product tile, brand-coloured in notes and the manifest. */
.vending-app .bicon {
  display: inline-block; width: 1em; height: 1em; background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}
.vending-app .prod .pb .bicon { display: block; width: 100%; height: 100%; -webkit-mask-position: left center; mask-position: left center; }
.vending-app .mk-brand .bicon { width: 81%; height: auto; aspect-ratio: 1 / 1; }
.vending-app .clues .bicon.inline { width: 1.2em; height: 1.2em; vertical-align: -0.3em; margin: 0 1px; }

/* fixed bay price tag — the slot's printed price, shown top-right whether the bay
   is empty or filled (sits above the product tile and the notes overlay). */
.vending-app .bay .bprice {
  position: absolute; top: 3px; right: 4px; z-index: 4; pointer-events: none;
  font-family: var(--vfont-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .01em;
  padding: 1px 5px; border-radius: 999px;
  background: var(--accent); color: #3a2800; border: 1.5px solid #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
}

/* Product icons are SVGs, served as <img> and — on the tiles vending.js owns —
   swapped for inline <svg> by vending-eyes.js so the pupils can track the pointer.
   These rules must therefore hold for both element types. Sizing the tile icon in
   `em` keeps the existing .pe font-size rules (base + the desktop machine
   enlargement/offset) in charge. */
.vending-app .picon { display: block; object-fit: contain; user-select: none; -webkit-user-drag: none; pointer-events: none; }
.vending-app .pe .picon { width: 1.15em; height: 1.15em; }
.vending-app .mk-type .picon { width: 81%; height: auto; aspect-ratio: 1 / 1; }
.vending-app .legend .picon { width: 15px; height: 15px; }

/* Mobile: the tiles are small, so the type icon sat centred in a tall 1fr row,
   leaving a wide gap under the brand wordmark. Pack the brand + type together
   and centre the pair, and halve the tile's padding (6→3) and gap (3→1.5), so
   the overall spacing between them is roughly halved. */
@media (max-width: 899px) {
  .vending-app .prod { grid-template-rows: auto auto; align-content: center; gap: 1.5px; padding: 3px; }
}

/* Tabbed card: the Restock crate and the Manifest share one card + footprint */
.vending-app .vtabs {
  display: flex; gap: 6px; margin-bottom: 13px;
  background: color-mix(in srgb, var(--card-line) 45%, transparent);
  padding: 4px; border-radius: 13px;
}
.vending-app .vtab {
  flex: 1; font-family: var(--vfont-display); font-weight: 800; font-size: 13px;
  padding: 8px 10px; border: none; border-radius: 10px; cursor: pointer;
  background: transparent; color: var(--muted);
  display: flex; align-items: center; justify-content: center; gap: 6px;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.vending-app .vtab .jp { font-family: var(--vfont-jp); font-size: 11px; color: var(--pop-pink); }
.vending-app .vtab.active { background: var(--card); color: var(--ink); box-shadow: 0 2px 6px -2px rgba(0,0,0,.3); }
.vending-app .vtab:focus-visible { outline: 3px solid var(--pop-blue); outline-offset: 2px; }
/* both panels occupy the same grid cell, so the card height stays constant when switching */
.vending-app .vtab-panels { display: grid; }
.vending-app .vtab-panel { grid-area: 1 / 1; align-self: start; }
.vending-app .vtab-panel:not(.active) { visibility: hidden; }

.vending-app .tray {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px;
  background: linear-gradient(180deg, #1a2136, #10131f); padding: 9px; border-radius: 12px; border: 2px solid var(--glass-edge);
  box-shadow: inset 0 3px 14px rgba(0,0,0,0.55);
}
.vending-app .slot { aspect-ratio: 1/1; border-radius: 11px; }
.vending-app .slot.empty { border: 2px dashed #3a4361; background: repeating-linear-gradient(135deg, transparent 0 6px, rgba(255,255,255,0.03) 6px 12px); }
.vending-app .prod.selected { transform: translateY(-4px) scale(1.04); box-shadow: 0 0 0 3px var(--accent), 0 0 18px color-mix(in srgb, var(--accent) 60%, transparent), inset 0 2px 0 rgba(255,255,255,.3); }
.vending-app .prod.done { opacity: .32; }

/* ---- Guide / manifest cards ---- */
.vending-app .v-card { background: var(--card); border: 2px solid var(--card-line); border-radius: 20px; padding: 17px 17px 15px; box-shadow: var(--card-shadow); }
/* the tabbed card stacks its parts, so .v-stats can be CSS-ordered to the top
   (desktop) or the bottom (mobile) of the card without moving in the markup. */
.vending-app .tabcard { display: flex; flex-direction: column; }
.vending-app .v-card h2 { margin: 0 0 3px; font-family: var(--vfont-display); font-weight: 800; font-size: 15px; letter-spacing: 0.01em; display: flex; align-items: center; gap: 8px; }
.vending-app .v-card h2 .jp { font-family: var(--vfont-jp); font-size: 12px; font-weight: 700; color: var(--pop-pink); }
.vending-app .v-card .sub { margin: 0 0 13px; font-size: 12.5px; color: var(--muted); }

.vending-app ol.clues { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; counter-reset: c; }
.vending-app ol.clues li {
  display: grid; grid-template-columns: 22px 14px 1fr; gap: 9px; align-items: start;
  padding: 8px 7px; border-radius: 12px; font-size: 13.5px; transition: background .15s ease;
}
.vending-app ol.clues li::before { counter-increment: c; content: counter(c); font-family: var(--vfont-display); font-weight: 800; font-size: 12px; color: #fff; background: var(--pop-blue); width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
.vending-app ol.clues li.ok { background: color-mix(in srgb, var(--good) 13%, transparent); }
.vending-app ol.clues li.ok::before { background: var(--good); }
.vending-app ol.clues li.bad { background: color-mix(in srgb, var(--bad) 14%, transparent); }
.vending-app ol.clues li.bad::before { background: var(--bad); }
.vending-app ol.clues li.bad .txt { color: var(--bad); }
.vending-app .status { width: 11px; height: 11px; border-radius: 50%; margin-top: 4px; background: var(--pending); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pending) 20%, transparent); }
.vending-app li.ok .status { background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 24%, transparent); }
.vending-app li.bad .status { background: var(--bad); box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 24%, transparent); }

/* the pinned, always-on brand-latin rule sits first, tinted to read as fixed */
.vending-app ol.clues li.latin { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.vending-app ol.clues li.latin::before { background: var(--accent); color: var(--accent-ink); }

/* Hint: the rule you can use right now to place at least one product. */
.vending-app ol.clues li.hint {
  box-shadow: 0 0 0 2px var(--pop-blue), 0 0 14px -2px color-mix(in srgb, var(--pop-blue) 70%, transparent);
  animation: vending-hint-pulse 0.9s ease-in-out 2;
}
@keyframes vending-hint-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--pop-blue), 0 0 14px -2px color-mix(in srgb, var(--pop-blue) 60%, transparent); }
  50%      { box-shadow: 0 0 0 3px var(--pop-blue), 0 0 22px 0 color-mix(in srgb, var(--pop-blue) 85%, transparent); }
}

/* ---- Bottom controls: toolbar (New/Reset/Solve) + prototype devbar ---- */
.vending-app .v-toolbar { display: flex; gap: 10px; }

/* Hint callout under the toolbar (stage-1 prompt / stage-2 explanation). */
.vending-app .v-hint {
  display: none; font-size: 13.5px; line-height: 1.45; color: var(--ink);
  background: color-mix(in srgb, var(--pop-blue) 12%, var(--card));
  border: 2px solid color-mix(in srgb, var(--pop-blue) 55%, var(--card-line));
  border-radius: 14px; padding: 11px 14px;
}
.vending-app .v-hint.show { display: block; }

/* The bay a revealed hint points at. */
.vending-app .bay.hint-cell {
  border-color: var(--pop-blue);
  box-shadow: inset 0 0 0 3px var(--pop-blue), 0 0 16px -2px color-mix(in srgb, var(--pop-blue) 75%, transparent);
  animation: vending-hint-pulse 0.9s ease-in-out 2;
}
.vending-app .v-devbar {
  display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; justify-content: center;
  padding: 10px 14px; border: 2px dashed var(--card-line); border-radius: 16px;
}
.vending-app .v-dev-group { display: flex; align-items: center; gap: 9px; }
.vending-app .v-dev-label { font-family: var(--vfont-display); font-weight: 800; font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.vending-app .v-dev-slider { accent-color: var(--pop-blue); cursor: pointer; }
/* Stock mode (classic / fruit / veg) — swaps the four product types. */
.vending-app .v-dev-select {
  font-family: var(--vfont-display); font-weight: 800; font-size: 12px; color: var(--ink);
  background: var(--card); border: 2px solid var(--card-line); border-radius: 9px;
  padding: 4px 8px; cursor: pointer;
}
.vending-app .v-dev-select:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Notes on/off — a small pill switch to the right of the difficulty slider. */
.vending-app .v-dev-toggle { cursor: pointer; }
.vending-app .v-dev-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.vending-app .v-switch {
  /* inline-block, not inline: outside a flex parent the track would otherwise
     collapse and the switch would read as a stray blob rather than a toggle. */
  display: inline-block;
  position: relative; width: 38px; height: 22px; border-radius: 999px; flex: none;
  background: color-mix(in srgb, var(--muted) 45%, transparent);
  transition: background .15s ease;
}
.vending-app .v-switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.35); transition: transform .15s ease;
}
.vending-app .v-dev-toggle input:checked + .v-switch { background: var(--pop-blue); }
.vending-app .v-dev-toggle input:checked + .v-switch::after { transform: translateX(16px); }
.vending-app .v-dev-toggle input:focus-visible + .v-switch { outline: 3px solid var(--pop-blue); outline-offset: 2px; }

.vending-app button.v-btn {
  flex: 1; font-family: var(--vfont-display); font-size: 14px; font-weight: 800; cursor: pointer;
  padding: 11px 12px; border-radius: 14px; border: 2px solid var(--card-line); background: var(--card); color: var(--ink);
  box-shadow: 0 4px 0 -1px var(--card-line); transition: transform .09s ease, box-shadow .09s ease, filter .15s ease;
}
.vending-app button.v-btn:hover:not(:disabled) { filter: brightness(1.03); }
.vending-app button.v-btn:active:not(:disabled) { transform: translateY(3px); box-shadow: 0 1px 0 -1px var(--card-line); }
.vending-app button.v-btn:disabled { opacity: .4; cursor: default; }
.vending-app button.v-btn.primary { background: var(--accent); border-color: #e0a800; color: #4a3200; box-shadow: 0 4px 0 -1px #d09c00; }
.vending-app button.v-btn.primary:active { box-shadow: 0 1px 0 -1px #d09c00; }
.vending-app button.v-btn:focus-visible { outline: 3px solid var(--pop-blue); outline-offset: 2px; }

/* ---- Stats bar: the timer pill and a jump to the instructions ----
   Lives inside the Restock/Manifest card; ordered to the card's top on desktop
   and its bottom on mobile (see the media queries at the foot of this file).
   Undo/Reset sit with Hint/Solve/New in .v-toolbar, under the machine. */
.vending-app .v-stats {
  display: flex; align-items: center; justify-content: space-between; gap: 8px 10px; flex-wrap: wrap;
  order: 3; margin-top: 13px; padding-top: 11px; border-top: 2px solid var(--card-line);
}
/* The whole pill is the switch's label, so the time and the icon toggle it too. */
.vending-app .v-stat-timer {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 4px 5px 4px 11px; border-radius: 999px;
  border: 2px solid var(--card-line); background: color-mix(in srgb, var(--card-line) 22%, var(--card));
}
.vending-app .v-stat-timer input { position: absolute; opacity: 0; width: 0; height: 0; }
.vending-app .v-stat-timer .fa-stopwatch { font-size: 12px; color: var(--muted); }
.vending-app .v-time {
  font-family: var(--vfont-mono); font-weight: 700; font-size: 14px; letter-spacing: .02em;
  color: var(--ink); min-width: 43px; text-align: center;
}
/* A ringed track + solid knob, so it reads as a switch against the card. */
.vending-app .v-stat-timer .v-switch {
  width: 34px; height: 20px;
  background: color-mix(in srgb, var(--muted) 22%, transparent);
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--muted) 50%, transparent);
}
.vending-app .v-stat-timer .v-switch::after {
  top: 3px; left: 3px; width: 14px; height: 14px;
  background: color-mix(in srgb, var(--muted) 80%, var(--card));
}
.vending-app .v-stat-timer input:checked + .v-switch {
  background: var(--pop-blue); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--pop-blue) 65%, #003348);
}
.vending-app .v-stat-timer input:checked + .v-switch::after { background: #fff; transform: translateX(14px); }
.vending-app .v-stat-timer input:focus-visible + .v-switch { outline: 3px solid var(--pop-blue); outline-offset: 3px; }

.vending-app .v-stat-help {
  flex: none; width: 33px; height: 33px; border-radius: 50%; cursor: pointer;
  font-family: var(--vfont-display); font-weight: 800; font-size: 14px; line-height: 1;
  border: 2px solid var(--card-line); background: color-mix(in srgb, var(--card-line) 22%, var(--card));
  color: var(--pop-blue); transition: filter .15s ease;
}
.vending-app .v-stat-help:hover { filter: brightness(1.06); }
.vending-app .v-stat-help:focus-visible { outline: 3px solid var(--pop-blue); outline-offset: 2px; }
/* label kept for screen readers only — the switch reads as a plain toggle */
.vending-app .v-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---- Win card: shown as a modal on solving, then inline once dismissed ---- */
.vending-app .v-win-overlay {
  position: fixed; inset: 0; z-index: 140; background: rgba(12, 8, 24, 0.55);
  backdrop-filter: blur(2px); animation: vending-win-fade .3s ease;
}
.vending-app .v-win-modal {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 150;
  width: min(440px, calc(100vw - 28px)); max-height: calc(100vh - 32px); overflow-y: auto;
  animation: vending-win-in .35s cubic-bezier(.2,.9,.3,1.15);
}
.vending-app .v-win {
  background: var(--card); border: 3px solid var(--good); border-radius: 20px;
  padding: 15px 15px 0; box-shadow: 0 24px 50px -18px rgba(0,0,0,.55); overflow: hidden;
}
.vending-app .v-win.inline { border-width: 2px; box-shadow: var(--card-shadow); }

.vending-app .v-win-head { display: flex; align-items: center; gap: 11px; }
.vending-app .v-win-prod { flex: none; width: 52px; height: 52px; display: block; }
.vending-app .v-win-prod .prod { width: 100%; height: 100%; cursor: default; }
.vending-app .v-win-prod .prod .pe { font-size: 26px; }
.vending-app .v-win-prod .prod .pb { height: 13px; }
.vending-app .v-win-msg {
  flex: 1; margin: 0; text-align: center; font-family: var(--vfont-display); font-weight: 800;
  font-size: 16px; color: var(--ink);
}
.vending-app .v-win-btn {
  flex: none; display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-family: var(--vfont-display); font-weight: 800; font-size: 12.5px;
  padding: 7px 12px; border-radius: 11px; border: 2px solid #1a9c60;
  background: var(--good); color: #04220f;
  transition: filter .15s ease, transform .09s ease;
}
.vending-app .v-win-btn:hover { filter: brightness(1.06); }
.vending-app .v-win-btn:active { transform: translateY(1px); }
.vending-app .v-win-btn.icon { padding: 7px 10px; background: var(--card); border-color: var(--card-line); color: var(--muted); }
.vending-app .v-win-btn:focus-visible { outline: 3px solid var(--pop-blue); outline-offset: 2px; }
.vending-app .v-win-divider { border: none; border-top: 2px solid var(--card-line); margin: 13px 0 11px; }
.vending-app .v-win-sub {
  margin: 0 0 9px; text-align: center; font-family: var(--vfont-display); font-weight: 800;
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
}
.vending-app .v-win-games { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.vending-app .v-win-games a {
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
  font-family: var(--vfont-display); font-weight: 800; font-size: 12.5px; color: var(--ink);
  padding: 6px 11px; border-radius: 11px; border: 2px solid var(--card-line);
  background: color-mix(in srgb, var(--card-line) 22%, var(--card));
  transition: filter .15s ease, transform .09s ease;
}
.vending-app .v-win-games a:hover { filter: brightness(1.06); color: var(--ink); }
.vending-app .v-win-games a:active { transform: translateY(1px); }
.vending-app .v-win-games img { width: 14px; height: 17px; object-fit: contain; }
.vending-app .v-win-games img.tint { filter: invert(68%) sepia(89%) saturate(2278%) hue-rotate(157deg) brightness(104%) contrast(101%); }
.vending-app .v-win-games .ico { font-size: 13px; line-height: 1; }
.vending-app .v-win-games .new {
  background: linear-gradient(135deg, #ff6b6b, #ff5252); color: #fff;
  padding: 1px 5px; border-radius: 9px; font-size: 9px; letter-spacing: .05em;
}
/* member pitch — a full-bleed gold footer on the card, hidden for members */
.vending-app .v-win-join {
  display: flex; align-items: center; gap: 10px; text-decoration: none; color: #3a2800;
  background: var(--accent); border-top: 2px solid #e0a800;
  margin: 13px -15px 0; padding: 10px 15px; font-weight: 700; font-size: 13px;
}
.vending-app .v-win-join span { text-decoration: underline; }
.vending-app .v-win-join:hover { filter: brightness(1.06); color: #3a2800; }
.vending-app .v-win-join-icon { width: 26px; height: 26px; object-fit: contain; flex: none; }
/* no member pitch → the card still needs a bottom edge */
.vending-app .v-win-games:last-child { margin-bottom: 15px; }

@keyframes vending-win-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes vending-win-in {
  from { transform: translate(-50%, -50%) translateY(-18px) scale(.94); opacity: 0; }
  to   { transform: translate(-50%, -50%); opacity: 1; }
}

.vending-app .legend { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; font-size: 12.5px; }
.vending-app .legend .grp { display: flex; flex-direction: column; gap: 6px; }
.vending-app .legend .lh { font-family: var(--vfont-display); font-weight: 800; font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.vending-app .legend .row { display: flex; align-items: center; gap: 7px; }
.vending-app .swatch { width: 14px; height: 14px; border-radius: 5px; flex: none; box-shadow: inset 0 1px 0 rgba(255,255,255,.4); }
.vending-app .shp { font-family: var(--vfont-mono); font-weight: 700; font-size: 10.5px; padding: 2px 7px; border-radius: 999px; background: var(--accent); color: #4a3200; border: 1.5px solid #fff; }

/* win banner */
.vending-app .toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(160%);
  background: var(--good); color: #04220f; font-family: var(--vfont-display); font-weight: 800; padding: 14px 24px; border-radius: 16px;
  border: 3px solid #fff; box-shadow: 0 18px 40px -12px rgba(0,0,0,0.5); display: flex; gap: 10px; align-items: center;
  transition: transform .45s cubic-bezier(.2,.9,.3,1.3); z-index: 60; font-size: 15px;
}
.vending-app .toast .jp { font-family: var(--vfont-jp); }
.vending-app .toast.show { transform: translateX(-50%) translateY(0); }

/* Hint reveal toast (2nd Hint press): product tile + reason + dismiss. Blue,
   roomy and left-aligned so the explanation reads naturally, unlike the terse
   green win banner it inherits from. */
.vending-app .toast.hint-toast {
  background: color-mix(in srgb, var(--pop-blue) 14%, var(--card));
  color: var(--ink); border-color: var(--pop-blue);
  align-items: center; gap: 13px; padding: 13px 16px;
  max-width: min(440px, calc(100vw - 28px)); text-align: left;
}
.vending-app .hint-toast .ht-item { flex: none; width: 50px; height: 50px; display: block; }
.vending-app .hint-toast .ht-item .prod { width: 100%; height: 100%; cursor: default; animation: none; }
.vending-app .hint-toast .ht-text {
  flex: 1; font-family: var(--vfont-body); font-weight: 600; font-size: 13.5px; line-height: 1.4;
}
.vending-app .hint-toast .ht-close {
  flex: none; align-self: flex-start; background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: 15px; line-height: 1; padding: 2px; border-radius: 6px;
}
.vending-app .hint-toast .ht-close:hover { color: var(--ink); }
.vending-app .hint-toast .ht-close:focus-visible { outline: 2px solid var(--pop-blue); outline-offset: 2px; }
.vending-app .machine.win .glass { box-shadow: inset 0 3px 26px rgba(0,0,0,0.5), 0 0 0 3px var(--good), 0 0 40px -6px color-mix(in srgb, var(--good) 65%, transparent); }

@media (prefers-reduced-motion: reduce) { .vending-app * { transition: none !important; animation: none !important; } }

/* ---- Desktop: same tabbed, stacked design as mobile, just scaled down.
   The machine and the tabbed card both fill the stack width, so capping the
   stack scales them together (~64% of the mobile size: 0.75 x 0.85). ---- */
/* ---- Desktop: fill the full width of the puzzle-container with a two-column
   layout — the machine (and the controls beneath it) grow to fill the left
   column, and the Restock/Manifest card occupies a fixed column on the right.
   The stack is given an explicit width so the container expands to it instead
   of hugging the old narrow machine. The Restock/Manifest sidebar column is
   flexible so it compresses (and its contents wrap) as the viewport narrows
   toward 1024px. Below 1024px the mobile vertical stack (with the tabbed
   Restock/Manifest card) takes over. */
@media (min-width: 1024px) {
  .vending-app .vending-stack {
    max-width: none;
    width: min(900px, 92vw);
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(232px, 26vw, 320px);
    column-gap: clamp(16px, 2vw, 28px);
    row-gap: clamp(12px, 1.6vw, 18px);
    align-items: start;
  }
  /* The left column is capped at the container's width (100cqw of .vending-bounds)
     rather than at its grid track, so as the viewport narrows the machine shrinks
     instead of crossing the container edge. The stack itself keeps its full width,
     so the Restock/Manifest card stays put outside the container. */
  .vending-app .machine,
  .vending-app .v-toolbar,
  .vending-app .v-hint,
  .vending-app .v-devbar,
  .vending-app .v-win.inline { grid-column: 1; max-width: 100cqw; }
  /* Span exactly the four left-column rows (machine, toolbar, hint, devbar) so the
     sidebar can grow alongside them. Over-spanning (span 99) materialises 99 row
     tracks, and the row-gap between every empty pair left ~1400px of dead space
     under the puzzle. */
  .vending-app .tabcard { grid-column: 2; grid-row: 1 / span 4; align-self: start; }

  /* No tabs on desktop: Restock is shown above Manifest, both always visible,
     so the panels sit in normal flow instead of overlapping in one grid cell. */
  .vending-app .tabcard .vtabs { display: none; }
  .vending-app .tabcard .vtab-panels { display: block; }
  .vending-app .tabcard .vtab-panel { grid-area: auto; }
  .vending-app .tabcard .vtab-panel:not(.active) { visibility: visible; }
  .vending-app .tabcard #tab-manifest { margin-top: 15px; }

  /* stats bar sits at the TOP of the card on desktop */
  .vending-app .v-stats {
    order: -1; margin-top: 0; margin-bottom: 13px;
    padding-top: 0; padding-bottom: 11px;
    border-top: none; border-bottom: 2px solid var(--card-line);
  }

  /* The machine fills the left column, so its bays are large: scale up the
     product emoji, nudge it up ~10px, and enlarge the brand label and bay price.
     Scoped to the machine grid (.grid) so the restock crate is unaffected. */
  .vending-app .grid .prod .pe { font-size: clamp(42px, 5vw, 62px); transform: translateY(-5px); }
  .vending-app .grid .prod .pb { height: 22px; }   /* larger brand wordmark on the big machine bays */
  .vending-app .grid .bay .bprice { font-size: 11.5px; padding: 2px 7px; } /* bay price tag, enlarged for the big machine bays */

  /* Restock crate: keep the 4-wide grid capped at the small tile size, but let the
     tiles shrink below it (minmax min 0) so the crate compresses as the sidebar
     narrows toward 1024px instead of overflowing. */
  .vending-app .tray { grid-template-columns: repeat(4, minmax(0, 54px)); justify-content: center; }
}

/* ---- Mobile (< 1024px): single-column tabbed layout. Enlarge the icons for
   touch/legibility on smaller screens. ---- */
@media (max-width: 1023px) {
  /* Tighter outer spacing (and a wider cap) so the machine grid and tiles can be
     larger on phones/tablets. */
  .vending-app .vending-stack { gap: clamp(8px, 1.5vw, 12px); max-width: 820px; }
  .vending-app .v-card { padding: 12px 12px 11px; }
  /* five buttons abreast (Undo/Reset/Hint/Solve/New) need tighter metrics here */
  .vending-app .v-toolbar { gap: 6px; }
  .vending-app button.v-btn { padding: 11px 4px; font-size: 12.5px; }
  /* The machine grid stays full width; the Restock crate is what reflows to save
     vertical space — 5 tiles per row instead of 4, so its tiles are smaller and it
     takes fewer/shorter rows, leaving the grid + crate visible together. */
  .vending-app .tray { padding: 6px; gap: 5px; grid-template-columns: repeat(5, 1fr); }
  .vending-app ol.clues li { padding: 6px 6px; }

  /* Product icons enlarged in both the machine grid and the restock crate. */
  .vending-app .prod .pe { font-size: clamp(26px, 5.4vw, 36px); }
  /* Note icons enlarged; reclaim the side margins/padding and tighten the row gap.
     Top inset raised so the top row clears the price tag. The rows are centred, so a
     smaller row-gap drops the type (top) row down closer to the brand (bottom) row. */
  .vending-app .bay .marks { inset: 19px 5px 8px 5px; row-gap: 1px; }
  .vending-app .mk { padding: 6px 1px; }
  .vending-app .mk-type .picon,
  .vending-app .mk-brand .bicon { width: 97%; }
}
