:root {
  --art-cloud: #F6E7B8;
  --art-cream: #F3E6C4;
  --art-gold: #E3B04B;
  --art-ink: #3B2A3F;
  --art-night: #4A3F66;
  --art-night-light: #6B5E8C;
  --art-olive: #B9B566;
  --art-peach: #EBA77C;
  --art-peach-dark: #D98B5C;
  --art-rose: #D9837F;
  --art-sage: #9DB58A;
  --art-sage-dark: #6F8F66;
  --art-sand: #E8CF98;
  --art-shadow: #5B4A63;
  --art-sky: #E9C46A;
  --art-sky-light: #F2DC98;
  --art-slate: #6E8494;
  --art-snow: #F7F1E3;
  --art-stone: #C9B79C;
  --art-teal: #79ADA4;
  --art-teal-dark: #4F8580;
  --art-wood: #A9774E;
  --backdrop-ceiling: #D9D4C7;
  --backdrop-cooler: #7E8C93;
  --backdrop-cooler-glow: #E8F2F2;
  --backdrop-counter: #9D8F78;
  --backdrop-counter-edge: #6F6352;
  --backdrop-door: #B9CED2;
  --backdrop-floor: #BDB4A2;
  --backdrop-floor-far: #A99F8C;
  --backdrop-light: #FFFDF4;
  --backdrop-pack-1: #C97B5A;
  --backdrop-pack-2: #7FA07A;
  --backdrop-pack-3: #D8BE6A;
  --backdrop-pack-4: #7C8FB5;
  --backdrop-pack-5: #B57C9E;
  --backdrop-pack-6: #E2D7C0;
  --backdrop-shelf: #8C8272;
  --backdrop-shelf-edge: #6F6759;
  --backdrop-wall: #CFC6B2;
  --backdrop-window: #C9DCE0;
  --backdrop-window-frame: #77705F;
  --belt: #8F8370;
  --belt-mark: #6B6457;
  --cup: #FBF8F1;
  --field: #56B4E9;
  --field-ink: #3E4A89;
  --flavors-cherry-dim: #AF9682;
  --flavors-cherry-fill: #D55E00;
  --flavors-cola-dim: #767574;
  --flavors-cola-fill: #2B2A28;
  --flavors-lemon-dim: #C1BFA4;
  --flavors-lemon-fill: #F0E442;
  --flavors-raspberry-dim: #A9B9C2;
  --flavors-raspberry-fill: #56B4E9;
  --focus: #3E4A89;
  --judge-bottom: #E69F00;
  --judge-edge: #2B2A28;
  --judge-glow: #56B4E9;
  --judge-top: #FFF3B0;
  --lid: #6B6457;
  --line: #2B2A28;
  --machine: #C9BEA6;
  --muted: #6B6457;
  --panel: #E4DBC6;
  --paper: #EFE8D8;
  --pattern-ink: #2B2A28;
  --pattern-ink-on-dark: #EFE8D8;
}
/* Colors are variables written from data/palette.json by package-web.sh;
   this file names none of its own (crates/content/tests/look.rs). */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; background: var(--paper); color: var(--line); }
body { font: 16px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; }
#app { max-width: min(1900px, 100%); margin: 0 auto; padding: 16px; }
#masthead h1 { margin: 0 0 8px; font-size: 1.6rem; font-weight: 650; letter-spacing: 0.01em; }
#status, #loading-error { margin: 8px 0; }
#foot { display: flex; justify-content: space-between; gap: 16px; margin-top: 24px; color: var(--muted); font-size: 0.9rem; }
#foot a { color: var(--focus); }

#screen { margin: 12px 0; }
.menu { display: grid; gap: 12px; max-width: 640px; }
.menu .item p { margin: 4px 0 0; color: var(--muted); }
button {
  font: inherit; font-weight: 600; color: var(--paper); background: var(--focus);
  border: 0; border-radius: 6px; padding: 8px 14px; cursor: pointer;
}
button:focus-visible { outline: 3px solid var(--line); outline-offset: 2px; }
button.quiet { color: var(--line); background: var(--panel); }
.controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.controls p { margin: 0; }
#stage { display: block; width: 100%; height: auto; background: var(--paper); border: 1px solid var(--panel); border-radius: 6px; }
#hud { display: flex; flex-wrap: wrap; gap: 4px 24px; margin: 8px 0; }
#hud p { margin: 0; }
#hud ul { display: flex; flex-wrap: wrap; gap: 4px 20px; margin: 0; padding: 0; list-style: none; }
#notice { margin-top: 12px; }
h2 { font-size: 1.3rem; margin: 8px 0 12px; }
h3 { font-size: 1.05rem; margin: 18px 0 6px; }
h4 { font-size: 0.95rem; margin: 12px 0 4px; color: var(--muted); }
.path { display: grid; gap: 8px; padding-left: 1.6em; max-width: 720px; }
.path li p { margin: 2px 0 0; color: var(--muted); }
.path li.locked .name { color: var(--muted); font-weight: 600; }
.path button.passed::after { content: " \2713"; }
.try { font-style: italic; }
.spouts { display: flex; flex-wrap: wrap; gap: 4px 20px; padding: 0; list-style: none; }
.cups { padding-left: 1.2em; }
.verdict { font-weight: 650; }
.hint { color: var(--muted); font-size: 0.9rem; }
.bindings { list-style: none; padding: 0; display: grid; gap: 6px; }
.bindings button { min-width: 4em; }
button.waiting { outline: 3px dashed var(--focus); }
.switch { font-weight: 600; }
#hud-live { display: flex; flex-wrap: wrap; gap: 4px 24px; width: 100%; font-weight: 600; }
.key { font-weight: 700; }
section.how p { max-width: 720px; margin: 0; }

/* The judgement word, after Dance Dance Revolution's: heavy, italic, leaning
   forward, a gradient fill inside a thick dark edge, popping in and fading.
   System faces only, because nothing loads from off the site. */
#stage-wrap { position: relative; }
#groove { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
#judge {
  position: absolute; left: 0; right: 0; top: 18%;
  text-align: center; pointer-events: none;
  font-family: "Arial Black", "Helvetica Neue", Impact, "Franklin Gothic Heavy", system-ui, sans-serif;
  font-weight: 900; font-style: italic; letter-spacing: 0.06em;
  font-size: clamp(40px, 9vw, 112px); line-height: 1;
  background: linear-gradient(180deg, var(--judge-top) 0%, var(--judge-top) 35%, var(--judge-bottom) 62%, var(--judge-top) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-text-stroke: 3px var(--judge-edge);
  filter: drop-shadow(0 0 10px var(--judge-glow)) drop-shadow(4px 5px 0 var(--judge-edge));
  transform: skewX(-12deg);
  animation: judge-pop 1.2s cubic-bezier(.2, 1.6, .4, 1) both;
}
#judge.judge-excellent, #judge.judge-groovy { font-size: clamp(44px, 10.5vw, 132px); }
#judge.judge-miss, #judge.judge-darn { font-size: clamp(34px, 7vw, 88px); filter: drop-shadow(4px 5px 0 var(--judge-edge)); }
@keyframes judge-pop {
  0%   { opacity: 0; transform: skewX(-12deg) scale(0.3, 1.8); }
  14%  { opacity: 1; transform: skewX(-12deg) scale(1.25, 0.85); }
  26%  { transform: skewX(-12deg) scale(0.95, 1.05); }
  36%  { transform: skewX(-12deg) scale(1); }
  78%  { opacity: 1; transform: skewX(-12deg) scale(1) translateY(0); }
  100% { opacity: 0; transform: skewX(-12deg) scale(1.08) translateY(-18px); }
}
@media (prefers-reduced-motion: reduce) {
  #judge { animation: judge-fade 1.2s linear both; }
  @keyframes judge-fade { 0%, 80% { opacity: 1; } 100% { opacity: 0; } }
}
#sound-volume { vertical-align: middle; width: 12em; }

/* The tree of missions, after Vagrancy's road. */
#tree { position: relative; display: grid; gap: 26px; padding: 4px 0 16px; }
#tree .level { position: relative; z-index: 1; pointer-events: none; }
#tree .tier-label { margin: 0 0 6px; text-align: center; color: var(--muted); font-size: 0.85em; }
#tree .tier { display: flex; flex-wrap: wrap; gap: 10px 14px; justify-content: center; }
#tree .node { pointer-events: auto; width: 168px; padding: 6px 4px; text-align: center; font-size: 0.9rem; }
#tree .node.locked { color: var(--muted); background: var(--panel); border: 1px dashed var(--muted); }
#tree .node.open { box-shadow: 0 0 0 2px var(--focus) inset; }
#tree .node.passed { background: var(--line); color: var(--paper); }
#tree .node.passed::after { content: " \2713"; }
#tree .node.picked { outline: 3px solid var(--judge-bottom); outline-offset: 2px; }
#tree .wires { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; overflow: visible; pointer-events: none; }
#tree .wires path { fill: none; }
#tree .wires path.met { stroke: var(--line); stroke-width: 2; opacity: 0.75; }
#tree .wires path.unmet { stroke: var(--muted); stroke-width: 1.25; stroke-dasharray: 4 4; opacity: 0.6; }
#tree .wires path.hot { stroke: var(--focus); stroke-width: 3.5; opacity: 1; stroke-dasharray: none; }
#mission-card { border: 1px solid var(--panel); border-radius: 8px; padding: 10px 16px; margin-bottom: 16px; background: var(--cup); }
#mission-card h3 { margin-top: 4px; }
.needs { list-style: none; padding: 0; }
.needs .mark { font-weight: 700; display: inline-block; min-width: 7.5em; }
.needs .unmet { color: var(--muted); }
.hud-line { display: inline-flex; gap: 14px; margin-right: 18px; }
@media (max-width: 600px) { #tree .node { width: 112px; font-size: 0.8em; } }

/* The lanes: a column per chapter, a row per number of requirements. */
.view-switch { margin: 0 0 12px; }
#tree.chart { display: grid; gap: 8px 6px; }
#tree.chart .lane { z-index: 0; border-radius: 8px; }
#tree.chart .lane-0 { background: var(--panel); }
#tree.chart .lane-1 { background: transparent; }
#tree.chart .lane-name { z-index: 1; margin: 6px 0 2px; text-align: center; color: var(--line); }
#tree.chart .tier-label { z-index: 1; align-self: center; text-align: right; margin: 0; padding-right: 6px; }
#tree.chart .cell { z-index: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; pointer-events: none; }
#tree.chart .node { width: 100%; max-width: 150px; font-size: 0.82rem; }
@media (max-width: 700px) { #tree.chart .node { font-size: 0.7rem; padding: 4px 2px; } #tree.chart { grid-template-columns: 0 repeat(7, minmax(0, 1fr)) !important; } #tree.chart .tier-label { display: none; } }
#tree.chart .wires { z-index: 1; }
#tree.chart .cell, #tree.chart .lane-name, #tree.chart .tier-label { z-index: 2; }

/* The soundtrack's panel (Sam, 2026-10-06), after Gear Master's, which floats
   over a corner. Here a floating panel covered the lid, so it sits under the
   line in the page's flow, at the right. It folds away; its level row stays
   when it is folded, because whoever hides the player may still want it
   quieter. */
#music {
  margin: 12px 0 0 auto;
  width: min(360px, 100%);
  background: var(--panel); border: 1px solid var(--muted); border-radius: 6px;
  overflow: hidden; font-size: 13px;
}
#music .music-head, #music .music-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 6px 10px;
}
#music .music-head span { font-weight: 700; color: var(--line); }
#music button { padding: 2px 8px; font-size: 12px; }
#music-player { display: block; width: 100%; height: 120px; border: 0; }
#music.folded #music-player { display: none; }
#music-volume { flex: 1; accent-color: var(--focus); }
#music-blocked { margin: 0; padding: 4px 10px; font-size: 12px; color: var(--line); }
#music-level { min-width: 2.5em; text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }

/* --- story mode (Sam, 2026-10-07) -----------------------------------------
   The button, after Vagrancy's arcade mode: skewed, a hard shadow, a bar
   that sweeps under the words. */
.menu .item.featured { margin: 6px 0 22px; }
.menu .item.featured button, #tree .node.next .plaque {
  position: relative; overflow: hidden;
  color: var(--paper); background: var(--line);
  border: 3px solid var(--art-gold); border-radius: 2px;
  box-shadow: 6px 6px 0 var(--art-peach-dark);
  transform: skew(-6deg);
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.menu .item.featured button { font-size: 1.6em; letter-spacing: 0.06em; padding: 14px 34px; }
.menu .item.featured button::after {
  content: ""; position: absolute; left: -10%; bottom: 8px; height: 4px; width: 0;
  background: var(--art-gold); transition: width 260ms ease;
}
.menu .item.featured button:hover, .menu .item.featured button:focus-visible { transform: skew(-6deg) translate(-2px, -2px); box-shadow: 9px 9px 0 var(--art-peach-dark); }
.menu .item.featured button:hover::after, .menu .item.featured button:focus-visible::after { width: 120%; }
.menu .item.featured button:active { transform: skew(-6deg) translate(3px, 3px); box-shadow: 2px 2px 0 var(--art-peach-dark); }
.menu .item.featured p { font-size: 1.05em; }
.premise { max-width: 70ch; color: var(--muted); }
.place { color: var(--muted); font-style: italic; margin-top: -4px; }
.card-store { float: right; width: 200px; margin: 0 0 6px 12px; }

/* The map: a band of region art per row, each fading into the next, and the
   wires and stores over them. */
.map-scroll { overflow-x: auto; border: 1px solid var(--muted); border-radius: 6px; }
#tree.rows { isolation: isolate; gap: 0; padding: 0; width: max-content; min-width: 100%; overflow: hidden; }
#tree.rows .level.band { z-index: auto; padding: 22px 8px 26px; min-height: 170px; }
#tree.rows .level.band::before {
  content: ""; position: absolute; inset: -16px 0; z-index: -1;
  background: var(--region) center bottom / auto 100% repeat-x;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
}
#tree.rows .level.band:first-of-type::before { top: 0; -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent 100%); mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent 100%); }
#tree.rows .region-name {
  position: sticky; left: 10px; z-index: 2; display: inline-block; margin: -12px 0 4px 10px; padding: 1px 8px;
  font-style: italic; font-size: 0.9em; color: var(--line); background: var(--cup); border: 1px solid var(--muted);
}
#tree.rows .tier { position: relative; z-index: 1; gap: 4px 6px; flex-wrap: nowrap; padding: 0 12px; }
#tree.rows .node {
  width: 170px; padding: 0; background: none; border: 0; box-shadow: none; color: var(--line);
  display: flex; flex-direction: column; align-items: center; cursor: pointer;
}
#tree.rows .node .store { width: 160px; height: 120px; display: block; transition: transform 140ms ease, filter 140ms ease; filter: drop-shadow(0 0 3px var(--cup)); }
#tree.rows .node .plaque {
  margin-top: -16px; padding: 1px 7px; font-size: 0.82rem; font-weight: 700; line-height: 1.3;
  background: var(--cup); border: 2px solid var(--focus); border-radius: 2px; max-width: 160px;
}
#tree.rows .node.locked .store { filter: grayscale(1) opacity(0.62); }
#tree.rows .node.locked .plaque { color: var(--muted); border-color: var(--muted); border-style: dashed; }
#tree.rows .node.open .store { transform: translateY(-4px); filter: drop-shadow(0 0 6px var(--art-gold)) drop-shadow(0 4px 0 var(--art-shadow)); }
#tree.rows .node.passed .plaque { background: var(--line); color: var(--paper); border-color: var(--line); }
#tree.rows .node.passed .plaque::after { content: " \2713"; }
#tree.rows .node.next .plaque { padding: 3px 10px; font-size: 0.9rem; background: var(--line); color: var(--paper); border: 3px solid var(--art-gold); }
#tree.rows .node:hover .store, #tree.rows .node:focus-visible .store { transform: translateY(-7px); }
#tree.rows .node.picked { outline: none; }
#tree.rows .node.picked .plaque { outline: 3px solid var(--judge-bottom); outline-offset: 2px; }
#tree.rows .wires path.met { stroke: var(--art-ink); }
@media (max-width: 600px) {
  #tree.rows .node { width: 118px; } #tree.rows .node .store { width: 112px; height: 84px; }
  .card-store { width: 120px; }
}

/* The other way to draw the tour, folded away under the map (Sam,
   2026-10-07, after Vagrancy's chart): muted and small until opened. */
.view-fold { margin-top: 18px; color: var(--muted); font-size: 0.9em; }
.view-fold summary { cursor: pointer; }
.view-fold .controls { margin-top: 8px; }
.view-fold button { font-size: 0.9em; }
/* Focus shows on the store's plaque, not as a box round the whole scene. */
#tree.rows .node:focus { outline: none; }
#tree.rows .node:focus-visible .plaque { outline: 3px solid var(--focus); outline-offset: 2px; }

/* The stores stand out from their region (Sam, 2026-10-07: "some of them
   get really drowned out by the city background"): the band recedes under
   a paper wash with its color softened, and each store is set off by a
   paper halo, as Vagrancy sets off its fight scenes, and drawn larger. */
#tree.rows .level.band::before { opacity: 0.58; filter: saturate(0.75); }
#tree.rows .node { width: 186px; }
#tree.rows .node .store {
  width: 176px; height: 132px;
  filter: drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 5px var(--cup));
}
#tree.rows .node.open .store {
  filter: drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 7px var(--art-gold)) drop-shadow(0 4px 0 var(--art-shadow));
}
#tree.rows .node.locked .store {
  filter: grayscale(1) opacity(0.75) drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 4px var(--cup));
}
@media (max-width: 600px) { #tree.rows .node { width: 124px; } #tree.rows .node .store { width: 118px; height: 88px; } }

/* Each store's name is in its art; the mission's name pops up when its
   scene is picked (Sam, 2026-10-07), in a slot kept under every scene so
   that nothing moves and nothing is covered when it appears. */
#tree.rows .node { position: relative; }
#tree.rows .node .slot { display: flex; justify-content: center; align-items: flex-start; height: 2.9em; width: 100%; margin-top: 2px; }
#tree.rows .node .popname {
  visibility: hidden; opacity: 0; max-width: 172px; padding: 3px 9px;
  font-size: 0.84rem; font-weight: 700; line-height: 1.25;
  color: var(--paper); background: var(--line); border: 2px solid var(--art-gold); border-radius: 2px;
  box-shadow: 3px 3px 0 var(--art-peach-dark);
  transform: skew(-6deg) scale(0.6); transition: transform 160ms cubic-bezier(.2, 1.6, .4, 1), opacity 120ms ease;
}
#tree.rows .node.picked .popname { visibility: visible; opacity: 1; transform: skew(-6deg) scale(1); }
#tree.rows .node.picked .store { transform: translateY(-6px); }
/* Passed: a stamp on the scene. Next: a slow gold pulse. */
#tree.rows .node.passed::after {
  content: "\2713"; position: absolute; top: 4px; right: 10px; width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 14px;
  color: var(--paper); background: var(--line); border: 2px solid var(--art-gold);
}
#tree.rows .node.next .store { animation: next-pulse 1.8s ease-in-out infinite; }
@keyframes next-pulse {
  0%, 100% { filter: drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 6px var(--art-gold)); }
  50% { filter: drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 1.5px var(--cup)) drop-shadow(0 0 14px var(--art-gold)); }
}
@media (prefers-reduced-motion: reduce) { #tree.rows .node.next .store { animation: none; } #tree.rows .node .popname { transition: none; } }
#tree.rows .node:focus-visible .store { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 10px; }

/* --- touch screens (Sam, 2026-10-10: the game on the iPad) ----------------
   A baseline any of these tools can take: no double-tap zoom or tap flash,
   no selection or callout over the game, the canvas keeps every touch for
   itself, and targets are at least a fingertip on a coarse pointer. */
html { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
#stage-wrap, .belt-buttons, #hud, #tree .node { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
#stage { touch-action: none; }
.belt-buttons { display: none; gap: 14px; margin: 10px 0; }
.belt-button { flex: 1; min-height: 64px; font-size: 1.1em; }
@media (pointer: coarse) {
  .belt-buttons { display: flex; }
  button, .switch { min-height: 44px; }
  .switch { display: flex; align-items: center; gap: 8px; }
  input[type="radio"], input[type="checkbox"] { width: 24px; height: 24px; }
  /* Key names and the Enter key mean nothing without a keyboard. */
  #hud-belt-keys, .hint, .hud-keys, #hud .spouts strong { display: none; }
}
/* Started from the home screen: keep clear of the notch and the corners. */
body[data-standalone="1"] #app {
  padding: env(safe-area-inset-top) max(16px, env(safe-area-inset-right)) env(safe-area-inset-bottom) max(16px, env(safe-area-inset-left));
}
/* Fullscreen: the line fits the screen's height, with room for its
   controls. */
body[data-fullscreen="1"], body[data-standalone="1"] { overflow-x: hidden; }
body[data-fullscreen="1"] #masthead, body[data-fullscreen="1"] #foot { display: none; }
body[data-fullscreen="1"] #stage { width: auto; max-width: 100%; max-height: calc(100dvh - 12rem); margin: 0 auto; display: block; }
body[data-fullscreen="1"] #stage-wrap { width: fit-content; max-width: 100%; margin: 0 auto; }

/* A run on a touch screen (2026-10-10): the whole line in view, spouts to
   gauges, with the live score above it and the belt buttons below, in the
   browser's own frame as well as fullscreen. On an iPad in landscape the
   page around it would otherwise push the spouts off the top. */
@media (pointer: coarse) {
  body[data-running="1"] #masthead, body[data-running="1"] #foot, body[data-running="1"] #music { display: none; }
  body[data-running="1"] #app { padding-top: 6px; }
  body[data-running="1"] #stage-wrap { width: fit-content; max-width: 100%; margin: 0 auto; }
  body[data-running="1"] #stage { width: auto; max-width: 100%; max-height: calc(100dvh - 11rem); }
  body[data-running="1"] .belt-button { min-height: 56px; }
}
