/* ==========================================================================
   BACHELOR TRAIL — mobile-first 8-bit UI

   HOW THIS STYLESHEET THINKS, so a later change does not fight it.

   1. TWO TYPEFACES, TWO JOBS. The bitmap faces (fonts.css) are the
      *interface* — titles, HUD, buttons, labels, chips, any number the game
      is asserting. Prose stays a normal mono face, because this game has
      paragraphs and a bitmap face at paragraph length on a phone is genuinely
      hard to read. The rule: if you would read it aloud as a sentence, it is
      `--font`. If it is a label or a number, it is `--font-px`.

      Both bitmap faces sit on an 8px grid. Use the `--px-*` sizes. A pixel
      font at 15px or 0.95rem gets resampled, goes soft, and you have paid
      18KB for nothing.

   2. DEPTH IS DRAWN, NEVER BLURRED. Every shadow in here has a blur radius of
      0. A blurred shadow is the most out-of-place thing you can put next to
      pixel art — it is the one rule that, broken anywhere, makes the whole
      screen read as a webpage again. Same for corners: everything is square.
      `--drop` and `--drop-sm` are the only shadows; use them.

   3. GLOW NEVER GOES ON A GLYPH YOU HAVE TO READ. `--glow-*` is a 6px blur,
      and 6px of blur around a 16px bitmap character is a halo wider than the
      strokes — it undoes the crispness that is the entire point of using a
      bitmap face. This shipped wrong once: the HUD's supply numbers and the
      morale figure were glowing, and Kyle's words for the result were
      "brutal to try and read". They were the most important numbers on
      screen and they were the least legible.

      So: `--glow-*` as a BOX-shadow on containers (a full meter, the belt
      strip, the arrival banner, a selected card) — yes. As a TEXT-shadow,
      only on hero display type that is meant to be emitting rather than
      read: `.logo-main`, `.logo-sub`, `.arrive-name`, and the trail wagon
      marker, which is a symbol rather than a word.

      What reads well on bitmap type instead is `0 2px 0 var(--shadow)` — a
      hard offset with no blur, which lifts the glyph off the panel without
      softening a single edge. Colour alone already carries warn/bad; the
      cell border and background carry it too.

   4. MOTION IS OPT-OUT. All DECORATIVE motion lives inside
      `@media (prefers-reduced-motion: no-preference)`, so the reduced-motion
      build is the static one by construction rather than by remembering to
      add an override. Put new animation there.

      The two deliberate exceptions are the hover/press transitions on
      `button` and `.card-pick`. Those are direct-manipulation feedback — the
      button travelling 4px down under your thumb is the affordance, not
      decoration — and reduced-motion is about vestibular triggers, not about
      removing the response to your own touch. Anything ambient, anything that
      moves without being touched, goes in the block.
   ========================================================================== */

:root {
  /* ---------------------------------------------------------- palette ----
     PICO-8-ish, deepened. `--bg` went darker than the panels specifically so
     panels read as objects sitting ON something rather than as slightly
     different rectangles of the same surface. The gap between --bg and
     --panel is what the whole depth system rests on; closing it flattens
     every screen at once. */
  --bg:        #0a0a18;
  --bg-2:      #12122a;
  --panel:     #1f1f42;
  --panel-2:   #2b2b57;
  --panel-3:   #34346b;
  --ink:       #fff1e8;
  --ink-dim:   #9a9ad0;
  --ink-dimmer:#6f6faa;

  --amber:     #ffec27;
  --red:       #ff3b6b;
  --blue:      #29adff;
  --green:     #00e436;
  --pink:      #ff77a8;
  --orange:    #ffa300;

  --line:      #3d3d7a;
  --line-hi:   #5555a0;

  /* The hard shadow every raised surface drops. Near-black rather than pure
     black so it reads as shadow on the indigo ground, not as a hole. */
  --shadow:    #05050e;
  --drop:      0 4px 0 0 var(--shadow);
  --drop-sm:   0 2px 0 0 var(--shadow);
  /* The light line along a surface's top edge. Sells "this is raised" more
     cheaply than anything else on the page. */
  --lift:      inset 0 2px 0 0 rgba(255,255,255,0.07);

  --glow-amber: 0 0 6px rgba(255,236,39,0.55);
  --glow-green: 0 0 6px rgba(0,228,54,0.5);
  --glow-red:   0 0 6px rgba(255,59,107,0.5);
  --glow-blue:  0 0 6px rgba(41,173,255,0.5);

  /* --------------------------------------------------------- typography --
     `--font-px` is the interface. `--font-ps` is the logo and screen titles
     only — it is an 8x8 square face and anything longer than a few words in
     it wraps to nothing. `--font` is prose. */
  --font-px: 'Silkscreen', ui-monospace, Menlo, monospace;
  --font-ps: 'Press Start 2P', 'Silkscreen', ui-monospace, monospace;
  --font: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Courier New", monospace;

  /* Safe bitmap sizes — multiples of the 8px design grid. Anything else
     resamples. --px-12 is the one concession: off-grid, but Silkscreen
     survives it and 8px is genuinely too small for a chip you have to read.

     --px-8 is now unused, and that is the finding, not an oversight: every
     label set at it — the card axis, IF YOU PLAY AS HIM, YOU HAVE REACHED,
     the statline rail, the crew strip — turned out to be unreadable on an
     actual phone. --px-12 is the floor for anything a player has to read.
     Kept as a token only for genuinely decorative type. */
  --px-8:  8px;
  --px-12: 12px;
  --px-16: 16px;
  --px-24: 24px;
  --px-32: 32px;

  --edge: 2px;

  /* ------------------------------------------------------ safe areas ----
     The viewport is `viewport-fit=cover` and the iOS status bar is
     `black-translucent`, which means the page is drawn edge to edge — under
     the Dynamic Island, under the home indicator, under the landscape notch.
     That is what we want (the vignette and scanlines should reach the glass),
     but it means every inset has to be paid back in padding by whatever holds
     READABLE content. It was not, and the first line of every screen hid
     behind the island once the game was installed to the home screen.

     Fall back to 0 rather than to a guessed 44px: on a phone with no cutout,
     and in a normal browser tab where the toolbar already covers the bar,
     `env()` correctly reports 0 and a hardcoded guess would leave a dead
     stripe at the top. Anything `position: fixed` near an edge needs these
     too — the padding on #app cannot reach it. */
  --safe-t: env(safe-area-inset-top,    0px);
  --safe-r: env(safe-area-inset-right,  0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left,   0px);
}

/* `manipulation` drops double-tap-to-zoom (and the ~300ms wait iOS spends
   deciding whether your second tap was one). Without it, working a + / -
   stepper or the scrounge grid at speed reads as a zoom gesture, because
   tapping the same 33px target twice quickly is literally what a double-tap
   is. Panning and pinch-zoom are untouched, so the page can still be zoomed
   deliberately — this is not `user-scalable=no`. */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent;
    touch-action: manipulation; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  overscroll-behavior: none;
}

/* The ground. A faint grid plus a vignette, so the dark area around the
   560px column is not a dead flat field — it reads as a screen the UI is
   sitting on. Fixed, so it does not scroll with the content. */
body::before {
  content: "";
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(61,61,122,0.30) 0%, transparent 60%),
    repeating-linear-gradient(to right,  rgba(61,61,122,0.10) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(to bottom, rgba(61,61,122,0.10) 0 1px, transparent 1px 32px);
}

/* Scanlines, over everything. */
body::after {
  content: "";
  position: fixed; inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom, rgba(255,255,255,0.035) 0 1px, transparent 1px 3px);
  opacity: 0.7;
  z-index: 9999;
}
@media (prefers-reduced-motion: reduce) { body::after { display: none; } }

#app {
  position: relative;
  z-index: 1;
  max-width: 560px;
  margin: 0 auto;
  padding:
    calc(12px + var(--safe-t))
    calc(12px + var(--safe-r))
    calc(48px + var(--safe-b))
    calc(12px + var(--safe-l));
  min-height: 100vh;
}

/* ---------------------------------------------------------------- type -- */
/* Screen titles are the square face. They are short by design — if a new
   screen needs an h1 longer than about 16 characters, shorten the title
   rather than the font size, or it wraps into a wall. */
h1 {
  font-family: var(--font-ps);
  font-size: var(--px-16);
  line-height: 1.5;
  letter-spacing: 0;
  color: var(--amber);
  text-shadow: 0 2px 0 var(--shadow);
  margin: 4px 0 14px;
}
h2, h3 {
  font-family: var(--font-px);
  font-weight: 700;
  letter-spacing: 1px;
  margin: 0 0 8px;
}
h2 { font-size: var(--px-24); color: var(--amber); text-shadow: 0 2px 0 var(--shadow); }
h3 { font-size: var(--px-16); color: var(--ink); }

p  { margin: 0 0 10px; }
.dim   { color: var(--ink-dim); }
.amber { color: var(--amber); }
.red   { color: var(--red); }
.small { font-size: 0.82rem; }
.center { text-align: center; }

/* A dashed rule reads as dotted-line-on-a-form. A hard bar with a highlight
   under it reads as a divider in a game menu. */
.mono-rule {
  border: 0; height: var(--edge);
  background: var(--line);
  box-shadow: 0 2px 0 0 rgba(255,255,255,0.04);
  margin: 18px 0;
}

/* -------------------------------------------------------------- panels -- */
/* The base object. Square, 2px edge, lit along the top, dropping a hard
   shadow. Everything else in this file is a variation on it. */
.panel {
  position: relative;
  background: var(--panel);
  border: var(--edge) solid var(--line);
  border-radius: 0;
  padding: 13px;
  margin-bottom: 14px;
  box-shadow: var(--lift), var(--drop);
}
.panel-tight { padding: 9px 11px; }

/* Alarm state. The red is loud enough on its own; it also loses the top
   highlight, so a panel in trouble reads as *unlit* next to its neighbours. */
.panel.alarm {
  border-color: var(--red);
  background: #33132a;
  box-shadow: var(--drop), 0 0 14px rgba(255,59,107,0.28);
}
.panel.alarm h3 { color: var(--red); text-shadow: 0 2px 0 var(--shadow); }

/* ------------------------------------------------------------- buttons -- */
/* A button is a physical object: it stands 4px off the page and, when
   pressed, travels exactly that 4px down onto its own shadow. The travel and
   the shadow offset must stay the same number or it appears to sink through
   the page. */
button, .btn {
  display: block;
  position: relative;
  width: 100%;
  margin: 0 0 12px;
  padding: 12px 14px;
  min-height: 50px;
  background: var(--panel-2);
  color: var(--ink);
  border: var(--edge) solid var(--line-hi);
  border-radius: 0;
  font-family: var(--font-px);
  font-weight: 700;
  font-size: var(--px-16);
  letter-spacing: 1px;
  text-align: left;
  cursor: pointer;
  box-shadow: var(--lift), var(--drop);
  /* Fast repeat taps otherwise start selecting the label text and raise the
     iOS selection callout mid-purchase. */
  -webkit-user-select: none; user-select: none;
  transition: background 0.08s steps(2), border-color 0.08s steps(2),
              transform 0.06s steps(1), box-shadow 0.06s steps(1);
}
button:hover:not(:disabled) { background: var(--panel-3); border-color: var(--blue); }
button:active:not(:disabled) {
  transform: translateY(4px);
  box-shadow: var(--lift), 0 0 0 0 var(--shadow);
  background: var(--panel-3);
}
button:disabled { opacity: 0.35; cursor: not-allowed; box-shadow: none; transform: translateY(4px); }

/* "Already done here" is not the same state as "you cannot do that", and the
   0.35 dim above says the second. It also takes the label below readable on a
   phone, so you could not tell at a glance WHICH thing you had already done —
   which is the actual question you are asking the screen.

   A done action stays legible, sits flat and sunken like a pressed key, and
   carries a green rail plus a DONE tag. The tag is set in the pixel face
   rather than a check glyph on purpose: the bitmap fonts are subset-embedded
   in fonts.css and U+2713 is not in them, so a checkmark would render as
   tofu on exactly the phones this is meant to fix. */
button.done:disabled {
  opacity: 1;
  cursor: default;
  color: var(--ink-dim);
  background: #16162e;
  border-color: var(--line);
  border-left: 3px solid var(--green);
  padding-right: 62px;
}
button.done:disabled .lbl-sub { color: var(--ink-dimmer); }
button.done:disabled::after {
  content: "DONE";
  position: absolute; top: 50%; right: 12px; transform: translateY(-50%);
  font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 1px;
  color: var(--bg); background: var(--green);
  padding: 3px 5px; box-shadow: var(--drop-sm);
}
button:focus-visible { outline: var(--edge) solid var(--blue); outline-offset: 2px; }

/* The sub-label is the one place inside a button that is prose. */
button .lbl-sub {
  display: block; font-family: var(--font); font-weight: 400;
  color: var(--ink-dim); font-size: 0.76rem; letter-spacing: 0;
  margin-top: 5px; line-height: 1.35;
}

button.primary {
  background: linear-gradient(180deg, #45356f 0%, #2f2f6e 100%);
  border-color: var(--amber);
  color: var(--amber);
  text-shadow: 0 2px 0 var(--shadow);
  /* The strongest amber glow in the game belongs here, on the thing you press.
     Panels that merely carry belt or total information use an amber spine and
     no glow, so this reads as the only action on the screen. */
  box-shadow: var(--lift), var(--drop), 0 0 20px rgba(255,236,39,0.30);
}
button.primary:hover:not(:disabled) { background: linear-gradient(180deg, #55429a 0%, #3b3b86 100%); }
button.primary:active:not(:disabled) {
  transform: translateY(4px);
  box-shadow: var(--lift), 0 0 0 0 var(--shadow), 0 0 14px rgba(255,236,39,0.18);
}
button.danger { border-color: var(--red); color: var(--red); }
button.ghost  { background: #17172f; border-color: var(--line); color: var(--ink-dim); }
button.ghost:hover:not(:disabled) { color: var(--ink); background: var(--panel); }
button.inline {
  display: inline-block; width: auto; margin-right: 8px;
  padding: 8px 12px; min-height: 0; font-size: var(--px-12);
}

.row { display: flex; gap: 10px; }
.row > * { flex: 1; }

/* ---------------------------------------------------------------- HUD --- */
/* The HUD is the only element that is always true, so it gets the strongest
   frame on the page: a thicker amber-lit top edge marks it as instrumentation
   rather than as another panel of content. */
.hud {
  position: sticky; top: 0; z-index: 10;
  background: linear-gradient(180deg, #262650 0%, var(--panel) 100%);
  border: var(--edge) solid var(--line-hi);
  border-top-width: 3px;
  border-top-color: var(--amber);
  border-radius: 0;
  padding: 8px 10px 9px;
  margin-bottom: 14px;
  box-shadow: var(--drop), 0 0 18px rgba(10,10,24,0.9);
  font-family: var(--font-px);
}

/* Two rows, not three columns. The old single flex row put DAY, mileage and
   the next landmark on one line, and every one of them wrapped mid-word at
   414px — "DAY / 2" over two lines. The landmark is the only variable-length
   item, so it gets its own row and the two fixed-width readouts share the
   one above. */
.hud-top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px;
  font-size: var(--px-12); color: var(--ink-dim); letter-spacing: 1px;
  white-space: nowrap;
}
.hud-top .ht-day { color: var(--ink); font-weight: 700; }
.hud-top .ht-day b { color: var(--amber); font-size: var(--px-16); }
.hud-top .ht-miles b { color: var(--ink); }
/* Keep clear of the fixed mute button. */
@media (max-width: 620px) { .hud-top { padding-right: 46px; } }

/* Two spans, not one string. Truncating the whole line ate the mileage first
   on a 360px phone — the landmark name is long ("Lake Wapello State Park")
   and the distance is the part you actually act on. The name shrinks and
   ellipsises; the distance is fixed and always visible. */
.hud-next {
  display: flex; align-items: baseline; gap: 8px;
  font-size: var(--px-12); color: var(--ink-dim); letter-spacing: 1px;
  margin-top: 4px;
}
.hud-next .hn-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Full --ink, not the dim label colour: this is the number you steer by, and
   it was the dimmest thing in the row. */
.hud-next .hn-dist { flex: 0 0 auto; margin-left: auto; white-space: nowrap;
                     color: var(--ink); }
.hud-next b { color: var(--blue); }

/* Resource readouts. Each is a boxed cell so the row reads as a bank of
   gauges rather than as a line of text. */
/* --px-12, not --px-8. At 8px these labels sat at 3.95:1 and were the hardest
   thing on the screen to read on a phone — under a scanline overlay, an 8px
   bitmap glyph is a 1px stroke competing with a 1-in-3 white line. */
.hud-res {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px;
  margin-top: 8px; font-size: var(--px-12);
}
/* 430px, not the 380px this used to be. Six across leaves each cell about
   51px of usable width at 400px, and the cash cell alone wants ~66px once it
   is showing "$1250" next to a "−$40" delta — it wrapped. Every common phone
   (390, 393, 402, 414, 428) now gets three ~110px cells over two rows
   instead. Costs about 30px of sticky HUD; buys back the horizontal room the
   --px-12 labels and deltas need. */
@media (max-width: 430px) { .hud-res { grid-template-columns: repeat(3, 1fr); row-gap: 6px; } }
.hud-res div {
  text-align: center; padding: 4px 1px 2px;
  background: #12122a; border: 1px solid var(--line);
  letter-spacing: 1px; color: var(--ink-dim);
}
.hud-res .v { display: block; font-weight: 700; font-size: var(--px-16); line-height: 1.25; }
.hud-res .v.ok   { color: var(--ink); }
.hud-res .v.warn { color: var(--amber); }
.hud-res .v.bad  { color: var(--red); }
/* A cell that is actually biting gets its own frame, so it is findable
   without reading any of the numbers. */
.hud-res div:has(.v.bad)  { border-color: var(--red);    background: #2a1020; }
.hud-res div:has(.v.warn) { border-color: var(--orange); background: #2a2210; }

/* The "what just changed" markers. --px-12, not --px-8: at 8px a "−6" was
   the smallest thing on the screen while being the one piece of the HUD that
   is only on screen for a moment, so it had the least time to be read.
   Colour was never the issue here (5.3:1 red, 10.6:1 green) — size was.

   `position: relative` rather than `vertical-align: super` for the raise.
   Superscript alignment grows the line box, which would push the sticky HUD
   taller; a relative offset moves the glyph and costs no height. */
.d {
  font-family: var(--font-px); font-size: var(--px-12); font-weight: 700;
  margin-left: 2px; letter-spacing: 0;
  position: relative; top: -0.3em;
}
.d.up { color: var(--green); }
.d.dn { color: var(--red); }

/* --------------------------------------------------------------- meter -- */
/* Chunky and segmented. The repeating gradient sits over the FILL, not over
   the track, so the bar looks built out of cells while empty space stays
   flat — that asymmetry is what makes it read as a game meter. */
.meter {
  height: 14px;
  background: #0a0a18;
  border: var(--edge) solid var(--line);
  border-radius: 0;
  overflow: hidden;
  box-shadow: inset 0 2px 0 0 rgba(0,0,0,0.45);
}
.meter > i {
  display: block; height: 100%;
  background:
    repeating-linear-gradient(to right, rgba(0,0,0,0.28) 0 1px, transparent 1px 6px),
    linear-gradient(180deg, rgba(255,255,255,0.35) 0 3px, transparent 3px),
    var(--green);
  box-shadow: 0 0 8px rgba(0,228,54,0.45);
}
.meter.warn > i {
  background:
    repeating-linear-gradient(to right, rgba(0,0,0,0.28) 0 1px, transparent 1px 6px),
    linear-gradient(180deg, rgba(255,255,255,0.35) 0 3px, transparent 3px),
    var(--amber);
  box-shadow: 0 0 8px rgba(255,236,39,0.45);
}
.meter.bad > i {
  background:
    repeating-linear-gradient(to right, rgba(0,0,0,0.28) 0 1px, transparent 1px 6px),
    linear-gradient(180deg, rgba(255,255,255,0.35) 0 3px, transparent 3px),
    var(--red);
  box-shadow: 0 0 8px rgba(255,59,107,0.5);
}

/* 14px is the HUD morale bar's size — it is the meter the whole game hangs on
   and it earns the weight. Every other bar is a secondary readout (a man's
   health, progress along one leg) and at full height they shout over the text
   they belong to, so they run thinner. */
.cr-body .meter, .onroad .meter { height: 9px; }

/* -------------------------------------------------------------- trail --- */
.trailbar {
  position: relative; height: 30px; margin: 12px 0 4px;
  border-bottom: var(--edge) solid var(--line);
}
.trailbar .wagon {
  position: absolute; top: 2px; transform: translateX(-50%);
  color: var(--amber); font-size: 0.9rem; text-shadow: var(--glow-amber);
}
.trailbar .mark {
  position: absolute; top: 12px; width: var(--edge); height: 12px;
  background: var(--line-hi);
}

/* The two ends of the trail. These were a space-between flex with no gap, so
   on a 360px phone "The first cabin · 2012" ran straight into "Waverly, Iowa
   · 2026" with no space between them. The gap is the fix; the second label is
   right-aligned so it stays readable once they start wrapping. */
.trail-ends {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 0.74rem; color: var(--ink-dim); line-height: 1.3;
}
.trail-ends span:last-child { text-align: right; }
@media (max-width: 380px) { .trail-ends { font-size: 0.68rem; gap: 8px; } }

/* ---------------------------------------------------------------- crew -- */
.crew { display: grid; gap: 8px; }
.crew-row {
  padding: 9px 10px; background: var(--panel-2);
  border: 1px solid var(--line); border-left: 3px solid var(--line-hi);
  border-radius: 0; font-size: 0.85rem;
  box-shadow: var(--drop-sm);
}
.crew-row.out { opacity: 0.45; border-left-color: var(--red); box-shadow: none; }
.crew-row.out .cr-name { text-decoration: line-through; }
/* `gap` is not optional here: a long name plus a health readout plus a delta
   ("MCKEE (YOU)" / "100 +12 great") ran together with no space between them
   at 390px, and the first thing to disappear was the boundary between the
   name and the number. */
.cr-top { display: flex; justify-content: space-between; align-items: baseline;
          gap: 8px; margin-bottom: 6px; }
.cr-hp { white-space: nowrap; }
.cr-name { font-family: var(--font-px); font-weight: 700; font-size: var(--px-16); letter-spacing: 1px; }
.cr-hp { font-family: var(--font-px); font-size: var(--px-12); color: var(--amber); font-weight: 700; }
.cr-hp .dim { font-weight: 400; color: var(--ink-dimmer); }
.cr-note { font-size: 0.75rem; margin-top: 5px; line-height: 1.4; }
.cr-belt { font-family: var(--font-px); font-size: var(--px-12); margin-top: 5px;
           letter-spacing: 1px; color: var(--ink-dim); }
/* The total is the number you actually scan down the list for, so it outranks
   its own label — matching .cr-name, the other thing you scan for. */
.cr-belt b { color: var(--amber); font-size: var(--px-16); }

/* ---------------------------------------------------------------- cards - */
.card-pick {
  border: var(--edge) solid var(--line);
  border-radius: 0; padding: 11px; margin-bottom: 12px;
  background: var(--panel-2); cursor: pointer;
  box-shadow: var(--lift), var(--drop);
  transition: border-color 0.08s steps(2), background 0.08s steps(2);
}
.card-pick:hover { border-color: var(--line-hi); background: var(--panel-3); }
/* The selected card is lit, not merely outlined — an amber edge alone was
   easy to miss against six identical neighbours on a phone. */
.card-pick.sel {
  border-color: var(--amber);
  background: #34346f;
  box-shadow: var(--lift), var(--drop), 0 0 16px rgba(255,236,39,0.22);
}
.card-pick .nm {
  font-family: var(--font-px); font-weight: 700; font-size: var(--px-24);
  color: var(--amber); letter-spacing: 1px; text-shadow: 0 2px 0 var(--shadow);
}
.card-pick .ti { font-size: 0.78rem; color: var(--ink-dim); }
.card-pick .pf { font-size: 0.78rem; margin-top: 6px; }

/* The identity block sits beside the portrait; `axis` is the headline —
   the one problem this man answers — so it outranks the joke title. */
.pick-id { min-width: 0; }
/* Was --px-8, which is unreadable on a phone — and this is the headline of
   the card, so it was the one line you could not skip. --px-12 with tighter
   tracking; the longest axis is 10 characters (CAMP CRAFT / THE COOLER /
   SCROUNGING), which still clears the portrait at this size. */
.pick-id .axis {
  margin-top: 4px; font-family: var(--font-px); font-size: var(--px-12);
  letter-spacing: 1px; color: var(--blue); font-weight: 700;
}
.card-pick .nm .tag {
  font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 1px;
  color: var(--bg); background: var(--amber); border-radius: 0;
  padding: 2px 5px; vertical-align: 2px; box-shadow: var(--drop-sm);
}
.axis-note { font-size: 0.76rem; color: var(--ink); margin: 8px 0 9px; line-height: 1.4; }

/* One row per fact. The label rail is fixed so BRINGS / HOLDS UP / PACKS
   line up down the whole list and the seven cards can be read as a table. */
.statline { display: grid; grid-template-columns: 74px 1fr; gap: 8px;
            align-items: start; margin-top: 7px; }
.statline .lbl {
  font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 1px;
  color: var(--ink-dimmer); padding-top: 3px;
}
.statline .chips, .roster-tote .chips { display: flex; flex-wrap: wrap; gap: 5px;
                                        align-items: center; }

/* Chips carry the actual numbers. Green is an upside, red is a cost, and a
   chip is never both — if a mod reads ambiguously, the direction in
   MOD_INFO is wrong, not the color.

   The label stays prose (it is a phrase) and only the VALUE is bitmap. That
   split is deliberate: the number is what the eye should catch, and making it
   the only pixel text on the chip does that without relying on color. */
.chip {
  display: inline-flex; align-items: baseline; gap: 5px; flex-wrap: wrap;
  font-size: 0.7rem; line-height: 1.35; padding: 3px 7px; border-radius: 0;
  border: 1px solid var(--line); background: #191934; color: var(--ink);
  box-shadow: var(--drop-sm);
}
.chip b { font-family: var(--font-px); font-weight: 700; font-size: var(--px-12); letter-spacing: 0; }
.chip i { font-style: normal; font-size: 0.62rem; color: var(--ink-dim); }
.chip.up   { border-color: #1c9a48; background: #14301f; }
.chip.up b { color: var(--green); }
.chip.down { border-color: #9a2c50; background: #331522; }
.chip.down b { color: var(--red); }
.chip.kit  { border-color: #9a8524; background: #302a14; color: var(--amber); }
.chip.flat { color: var(--ink-dim); }
.chip.odd  { border-color: var(--pink); color: var(--pink); }

/* Grit has never been visible before. Five pips over the -0.15..0.35 spread. */
.pips { display: inline-flex; gap: 3px; }
.pips span {
  width: 10px; height: 10px; border: 1px solid var(--line);
  border-radius: 0; background: #0d0d20;
}
.pips span.on { background: var(--blue); border-color: var(--blue); box-shadow: var(--glow-blue); }
.pipword { font-size: 0.7rem; color: var(--ink-dim); }

/* Prose is opt-in. Collapsed, all seven fit in one scan; open, one man gets
   the full page he used to get whether you wanted it or not. */
/* Overrides the global 50px tap floor: the whole card is already a tap
   target, so this only has to be reachable, not thumb-sized. */
/* This is the one control on a pick card that is a SENTENCE, so it takes the
   prose face — rule 1 at the top of this file.

   It shipped in Silkscreen at 12px, letterspaced, dim, and all-caps: four
   things working against legibility at once, and it was the first thing Kyle
   flagged. Bitmap type is for labels and numbers. Forty-five characters of
   English is neither, and no amount of size-tuning fixes that — going from
   8px to 12px was treating the symptom.

   Sentence case is deliberate too. It makes this read as a link rather than
   as another uppercase rail label competing with BRINGS / HOLDS UP / PACKS
   directly above it. */
.card-pick .more {
  width: 100%; margin: 10px 0 0; padding: 7px 9px;
  min-height: 0; display: flex; align-items: center;
  font-family: var(--font); font-weight: 400; font-size: 0.78rem;
  letter-spacing: 0; line-height: 1.45;
  background: #191934; border: 1px solid var(--line);
  color: var(--ink-dim); text-align: left; box-shadow: none;
}
.card-pick .more:hover { color: var(--ink); border-color: var(--line-hi); background: #22224a; }
.card-pick .more:active { transform: none; }
.pick-more { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
.pick-more .youbox .statline { margin-top: 6px; }
.pick-more .youbox .statline .lbl { color: var(--amber); opacity: 0.8; }
.scenes { margin-top: 9px; font-size: 0.72rem; color: var(--ink-dim); }
.scenes b { color: var(--blue); }

/* The running total on the crew screen. Three cards cannot show you that
   Seth and Michael are pulling the cooler in opposite directions; this can. */
.roster-tote {
  border: var(--edge) solid var(--line); border-radius: 0;
  padding: 10px 11px; margin-bottom: 12px; background: #191934;
  box-shadow: var(--lift), var(--drop);
}
.roster-tote > b {
  display: block; font-family: var(--font-px); color: var(--amber);
  font-size: var(--px-12); letter-spacing: 2px; margin-bottom: 8px;
}

/* --------------------------------------------------------------- store -- */
/* Two columns only: everything textual on the left, the stepper in a fixed
   right rail so every row's controls line up regardless of text length. */
.store-row { display: grid; grid-template-columns: 1fr 122px; gap: 10px;
             align-items: center; padding: 12px 0;
             border-bottom: 1px solid var(--line); }
.store-row:last-child { border-bottom: 0; }
.si-top { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.si-top strong { font-family: var(--font-px); font-size: var(--px-16); letter-spacing: 1px; color: var(--ink); }
.si-top .amber { font-family: var(--font-px); font-size: var(--px-16); font-weight: 700; text-shadow: 0 2px 0 var(--shadow); }
.si-top .dim { font-size: 0.76rem; }
.si-desc { color: var(--ink-dim); font-size: 0.76rem; line-height: 1.4; margin-top: 4px; }

.qty { display: flex; align-items: center; justify-content: flex-end; gap: 5px; }
.qty button {
  width: 38px; min-height: 38px; padding: 0; text-align: center; margin: 0;
  font-size: var(--px-16); line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.qty span {
  min-width: 32px; text-align: center; font-family: var(--font-px);
  font-size: var(--px-16); color: var(--amber); font-weight: 700;
}

@media (max-width: 380px) {
  .store-row { grid-template-columns: 1fr 108px; }
  .qty button { width: 33px; min-height: 33px; }
  .qty span { min-width: 26px; }
}

/* ---------------------------------------------------------- belt pts --- */
/* The strip that fires the moment points move. It is the loudest number in
   the game, so it gets the loudest treatment on the page: amber edge, amber
   glow, and (see the motion block) a flash on arrival. */
/* Amber is the belt's colour everywhere in the game, so this panel keeps it —
   but only as a spine and a heading, never as a full ring with a glow. A ring
   plus an amber glow is exactly what `button.primary` uses to mean "press
   this", and this panel was wearing a LOUDER version of it (18px/0.22 against
   the button's 14px/0.18) while being the one thing on the screen you cannot
   press. Border and shadow now match an ordinary .panel; the left rule and the
   amber h3 carry the identity. */
.beltfx {
  border: var(--edge) solid var(--line);
  border-left: 5px solid var(--amber);
  background: linear-gradient(180deg, #3a2f10 0%, var(--panel) 100%);
  box-shadow: var(--lift), var(--drop);
}
.beltfx h3 { color: var(--amber); text-shadow: 0 2px 0 var(--shadow); }
.bfx-row { display: grid; grid-template-columns: 1fr auto; gap: 3px 10px;
           align-items: baseline; padding: 6px 0; font-size: 0.85rem;
           border-bottom: 1px solid #3d3d5c; }
.bfx-row:last-of-type { border-bottom: 0; }
.bfx-name { font-family: var(--font-px); font-weight: 700; font-size: var(--px-16); letter-spacing: 1px; }
.bfx-row .amber, .bfx-row .red {
  font-family: var(--font-px); font-weight: 700; font-size: var(--px-16);
}
.bfx-row .dim { grid-column: 1 / -1; font-size: 0.72rem; }

/* The log. Name, swing, and the scene that caused it — the third column is
   the whole point of the screen, so it wraps instead of truncating. */
.blstand { display: grid; gap: 4px; font-size: 0.86rem; }
.bl-st { display: grid; grid-template-columns: 1fr auto; padding: 5px 0;
         border-bottom: 1px solid #2d2d5c; }
.bl-st.out { opacity: 0.5; text-decoration: line-through; }
.bl-row { display: grid; grid-template-columns: auto auto 1fr; gap: 9px;
          align-items: baseline; padding: 6px 0; font-size: 0.8rem;
          border-bottom: 1px solid #2d2d5c; }
.bl-row:last-child { border-bottom: 0; }
.bl-who { font-family: var(--font-px); font-weight: 700; font-size: var(--px-12); letter-spacing: 1px; }
.bl-why { text-align: right; font-size: 0.72rem; min-width: 0; overflow-wrap: anywhere; }

/* Where a point came from. Four states, and the one that matters is THE DICE:
   until this existed there was no way to tell a point you had earned from one
   the game pinned on somebody at random. Set in the pixel face at --px-12 and
   inline, so it reads as a stamp on the line rather than as more sentence. */
.bsrc {
  font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 1px;
  padding: 1px 4px; margin-right: 4px; white-space: nowrap;
  border: 1px solid var(--line-hi); color: var(--ink-dim); background: #16162e;
}
.src-won { border-color: var(--green); color: var(--green); }
.src-you { border-color: var(--amber); color: var(--amber); }
.src-him { border-color: var(--blue);  color: var(--blue); }
/* Deliberately the quiet one. "The dice did this" is information, not an
   achievement, and colouring it like one would read as a reward. */
.src-rng { border-color: var(--line-hi); color: var(--ink-dim); }

/* ------------------------------------------------------- day summary --- */
/* The screen the whole day is reported on, once. See the block comment above
   `screens.daysummary` in ui.js for why it exists.

   The shape of a row is the load-bearing part. Three things want to be on a
   line — what moved, by how much, and what did it — and three columns of
   variable-length text do not fit across 390px; the cause is the one that
   wraps, and truncating the cause would delete the entire point of the
   screen. So it drops to its own grid row underneath, which is the same
   solution `.bfx-row` already uses for the belt strip. */
.dsum {
  text-align: center; margin: 4px 0 14px; padding: 12px 10px;
  background: linear-gradient(180deg, #2a2a58 0%, var(--panel) 100%);
  border: var(--edge) solid var(--line-hi); box-shadow: var(--lift), var(--drop);
}
.dsum-kicker { font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 2px;
               color: var(--ink-dim); }
.dsum-day { font-family: var(--font-ps); font-size: var(--px-24); color: var(--amber);
            margin: 6px 0 4px; text-shadow: 0 3px 0 var(--shadow); }
.dsum-sub { font-size: 0.8rem; color: var(--ink-dim); line-height: 1.35; }
/* A minigame reports on the same screen but does not burn a day, so its header
   is deliberately quieter than the end-of-day one — the big amber DAY number
   reading the same as it did five seconds ago is the point, not a glitch. */
.dsum-same .dsum-day { color: var(--ink); }

.ds-block { border-left: 5px solid var(--line-hi); }
.ds-head { margin-bottom: 8px; }
.ds-head h3 { margin: 0; }
.ds-cause { font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 1px;
            color: var(--amber); margin-top: 5px; line-height: 1.5; }
.ds-text { font-size: 0.88rem; line-height: 1.55; margin: 0 0 10px; }
.ds-nil  { margin: 0; }

.ds-rows { display: grid; gap: 0; }
.ds-row {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  align-items: baseline; padding: 7px 0; border-bottom: 1px solid #2d2d5c;
}
.ds-rows > .ds-row:last-child, .ds-hp .ds-row:last-child { border-bottom: 0; }
.ds-n { font-family: var(--font-px); font-size: var(--px-16); letter-spacing: 1px; }
.ds-row > b {
  font-family: var(--font-px); font-size: var(--px-16); font-weight: 700;
  text-shadow: 0 2px 0 var(--shadow); white-space: nowrap;
}
.ds-row > b.up   { color: var(--green); }
.ds-row > b.dn   { color: var(--red); }
.ds-row > b.flat { color: var(--ink-dim); }
/* The cause. Prose, not bitmap — it is a sentence (rule 1 at the top of this
   file), and it is the only part of the row a player has not seen before. */
.ds-w { grid-column: 1 / -1; font-size: 0.74rem; color: var(--ink-dim); line-height: 1.45; }
.ds-w b { color: var(--ink); font-family: var(--font-px); font-size: var(--px-12); }
.ds-row.gone .ds-n { text-decoration: line-through; }

.ds-hp { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line); }
.ds-sub { font-family: var(--font-px); font-size: var(--px-12); letter-spacing: 1px;
          color: var(--ink-dim); margin-bottom: 4px; }
.ds-sub .ds-w { display: block; margin-top: 4px; }

/* The per-block health one-liner. Wraps rather than scrolls — four names and
   four numbers do not fit on one 390px line and must not clip. */
.ds-hpline { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line);
             display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.ds-hpline .ds-sub { margin: 0; }
.ds-hpbits { font-size: 0.82rem; color: var(--ink-dim); line-height: 1.6; }
.ds-hpbits b { font-family: var(--font-px); font-size: var(--px-12); }
.ds-hpline .ds-w { grid-column: auto; flex-basis: 100%; }
.ds-notes { margin-top: 10px; max-height: none; }
/* Spine, not a ring — same reasoning as .beltfx. This panel sits directly above
   the day's action button, and two amber rectangles stacked on each other made
   the total look as pressable as the button under it. */
.ds-net { border: var(--edge) solid var(--line); border-left: 5px solid var(--amber); }
.ds-net h3 { color: var(--amber); text-shadow: 0 2px 0 var(--shadow); }

/* --------------------------------------------------------------- beans -- */
/* Every other supply is a rate against a clock. Beans are a bill against a
   fixed number, so they get a progress meter rather than a count. */
.beans { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.bn-top { display: flex; justify-content: space-between; align-items: baseline;
          gap: 8px; margin-bottom: 5px; font-family: var(--font-px);
          font-size: var(--px-12); letter-spacing: 1px; }
.bn-l { color: var(--ink-dim); }
.bn-v { white-space: nowrap; color: var(--ink-dim); }
.bn-v b { font-size: var(--px-16); color: var(--amber); text-shadow: 0 2px 0 var(--shadow); }
.beans.short .bn-v b { color: var(--red); }
.bn-note { font-size: 0.74rem; color: var(--ink-dim); margin-top: 5px; line-height: 1.45; }
/* The "/5" in the HUD cell: present, but never louder than the count itself. */
.hud-res .v .of { color: var(--ink-dim); font-size: var(--px-12); }

/* -------------------------------------------------------------- ticker -- */
.ticker { font-size: 0.85rem; color: var(--ink-dim); max-height: 180px; overflow-y: auto; }
.ticker div { padding: 5px 0; border-bottom: 1px solid #2d2d5c; }

/* Day notes that cost you something. Everything in the ticker used to render
   in the same dim grey, so losing a bought item for the rest of the run read
   exactly like a joke about Shane going down a culvert — the biggest single
   swing in a day, filed as subtext. These get pulled out of the flow: full
   ink, a colour-coded rail, and their own ground. See `toned` in engine.js. */
.nt-bad, .nt-gone, .nt-good {
  color: var(--ink); padding-left: 9px; margin: 3px 0;
  border-left: 3px solid var(--line-hi); background: #191934;
}
.ticker .nt-bad, .ticker .nt-gone, .ticker .nt-good { padding: 7px 0 7px 9px; }
.nt-bad  { border-left-color: var(--red);   background: #2a1424; }
.nt-gone { border-left-color: var(--red);   background: #2a1424; font-weight: 700; }
.nt-good { border-left-color: var(--green); background: #14241c; }

/* --------------------------------------------------------------- misc --- */
.tomb {
  border: var(--edge) solid var(--line-hi); border-radius: 44px 44px 0 0;
  padding: 22px 16px; text-align: center; background: var(--panel-2);
  margin: 14px 0; box-shadow: var(--drop);
}
.belt {
  border: 3px solid var(--amber); border-radius: 0; padding: 16px;
  text-align: center; background: linear-gradient(180deg, #4a3c14 0%, #2a2110 100%);
  margin: 14px 0;
  box-shadow: var(--lift), var(--drop), 0 0 24px rgba(255,236,39,0.3);
}
.scorebox { display: grid; grid-template-columns: 1fr auto; gap: 6px; font-size: 0.86rem; }
.scorebox .k { color: var(--ink-dim); }
.scorebox .v { text-align: right; font-family: var(--font-px); font-size: var(--px-16); color: var(--amber); }

/* Final tally shows its arithmetic: label, the math, the points it earned. The
   middle column is what turns a screenshot into an argument at the next trip. */
.tallybox { display: grid; grid-template-columns: auto 1fr auto; gap: 6px 10px; font-size: 0.86rem; }
.tallybox .k { color: var(--ink); }
/* The math wraps rather than truncating: on a 320px phone an ellipsis would eat
   the multiplier, which is the only part of the row that explains anything. */
.tallybox .d {
  color: var(--ink-dim); text-align: right; font-size: 0.72rem; align-self: center;
  min-width: 0; overflow-wrap: anywhere;
  /* `.d` is ALSO the HUD's delta marker, which is bitmap, superscripted and
     glowing. Inside a tally row it is plain prose, so all of that is undone
     here. Removing these lines makes the tally column jump to superscript. */
  font-family: var(--font); vertical-align: baseline; margin-left: 0;
  letter-spacing: 0; text-shadow: none;
}
.tallybox .v { text-align: right; font-family: var(--font-px); font-size: var(--px-16); color: var(--amber); }
.tallybox .v.zero { color: var(--ink-dimmer); }
.tallybox .v.neg { color: var(--red); }
@media (max-width: 380px) {
  .tallybox { font-size: 0.8rem; gap: 5px 8px; }
  .tallybox .d { font-size: 0.68rem; }
  .tallybox .v { font-size: var(--px-12); }
}

/* ------------------------------------------------------------- the logo -- */
/* Was ASCII line-art in a <pre>. It rendered at 0.6rem to make it fit, which
   is smaller than the body copy underneath it — the title of the game was the
   least legible thing on the title screen. It is real type now, so it scales
   with the viewport and stays crisp at any size. */
.logo { text-align: center; margin: 18px 0 22px; user-select: none; }
.logo-main {
  font-family: var(--font-ps);
  font-size: clamp(19px, 7.4vw, 34px);
  line-height: 1.1; color: var(--amber);
  text-shadow:
    0 3px 0 #b8451a, 0 5px 0 var(--shadow),
    0 0 18px rgba(255,236,39,0.45);
  letter-spacing: 0;
}
.logo-sub {
  font-family: var(--font-ps);
  font-size: clamp(10px, 3.6vw, 16px);
  color: var(--blue); text-shadow: 0 2px 0 var(--shadow), var(--glow-blue);
  letter-spacing: 4px; margin-top: 10px;
}
.logo-rule {
  height: var(--edge); background: var(--line-hi); margin: 14px auto 0;
  max-width: 70%; box-shadow: 0 2px 0 0 rgba(255,255,255,0.05);
}
/* ------------------------------------------------------------- sound --- */
.soundbtn {
  position: fixed; z-index: 100;
  top:   calc(10px + var(--safe-t));
  right: calc(10px + var(--safe-r));
  width: 44px; height: 44px; min-height: 44px;
  margin: 0; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel); border: var(--edge) solid var(--line-hi);
  border-radius: 0; opacity: 0.8; box-shadow: var(--drop-sm);
}
.soundbtn:hover { opacity: 1; }
/* 13x9 at scale 3 = 39x27, so it clears the 44px box and its border. */
.sound-ico { display: block; image-rendering: pixelated; image-rendering: crisp-edges; }
@media (max-width: 600px) {
  .soundbtn {
    top:   calc(8px + var(--safe-t));
    right: calc(8px + var(--safe-r));
  }
}

/* --------------------------------------------------------------- guide -- */
.guide { display: grid; gap: 10px; }
/* 96px, not the 74px this rail used to be. The labels in it ("1.3 CASES",
   "1 BUNDLE") are bitmap now and Silkscreen is wider per character than the
   mono face was at the same nominal size — at the old width every two-word
   label wrapped. Measure the longest label before narrowing this again. */
.guide > div { display: grid; grid-template-columns: 96px 1fr; gap: 10px;
               font-size: 0.82rem; align-items: start; }
.guide b { font-family: var(--font-px); font-size: var(--px-12); color: var(--amber);
           font-weight: 700; letter-spacing: 1px; }
.guide span { color: var(--ink-dim); }
/* 380, not 400. The label rail collapses only on genuinely small phones —
   widening this to 400 broke the two-column forecast on a 390px iPhone,
   which is the single most common screen this game will be opened on. */
@media (max-width: 380px) {
  .guide > div { grid-template-columns: 1fr; gap: 2px; }
}

/* ---------------------------------------------------------------- burn -- */
.burn { display: grid; gap: 5px; font-size: 0.78rem; color: var(--ink-dim); }
.burn b { font-family: var(--font-px); font-size: var(--px-12); color: var(--ink); }

/* ------------------------------------------------------------ you-box --- */
.youbox {
  margin-top: 10px; padding: 9px 10px; background: #191934;
  border: 1px solid var(--line); border-left: 3px solid var(--amber);
  border-radius: 0; font-size: 0.78rem; color: var(--ink); line-height: 1.45;
}
.youbox b {
  display: block; font-family: var(--font-px); color: var(--amber);
  font-size: var(--px-12); letter-spacing: 1px; margin-bottom: 5px;
}

/* ---------------------------------------------------------- hud morale -- */
.hud-morale { display: flex; justify-content: space-between; align-items: baseline;
              margin: 8px 0 4px; font-size: var(--px-12); color: var(--ink-dim);
              letter-spacing: 1px; gap: 8px; }
.hud-morale .hml { white-space: nowrap; }
.hud-morale .hml b { color: var(--amber); font-size: var(--px-24); text-shadow: 0 2px 0 var(--shadow); }
.hud-morale .hmt { font-style: normal; text-align: right; color: var(--ink-dim); }
.hud-morale .hmt.red { color: var(--red); }
/* Prose face, so it does not compete with the bitmap labels around it, but it
   still has to clear 4.5:1 — it was 3.40 at 0.62rem, which is where the
   "what is this bar for" answer was going to waste. */
.hud-hint { font-family: var(--font); font-size: 0.7rem; color: var(--ink-dim);
            margin-top: 5px; text-align: right; letter-spacing: 0; }

/* ------------------------------------------------------- arrival state -- */
.arrive {
  border: 3px solid var(--amber); border-radius: 0;
  background: linear-gradient(180deg, #4a3c14 0%, #241d3f 100%);
  padding: 16px 12px; margin-bottom: 14px; text-align: center;
  box-shadow: var(--lift), var(--drop), 0 0 24px rgba(255,236,39,0.28);
}
.arrive-kicker { font-family: var(--font-px); color: var(--ink-dim); font-size: var(--px-12); letter-spacing: 2px; }
.arrive-name {
  font-family: var(--font-ps); color: var(--amber); font-size: clamp(13px, 4.6vw, 19px);
  line-height: 1.35; margin: 10px 0 8px;
  text-shadow: 0 3px 0 var(--shadow), var(--glow-amber);
}
.arrive-sub { font-family: var(--font-px); color: var(--ink-dim); font-size: var(--px-12); letter-spacing: 1px; }

/* ------------------------------------------------------- traveling ----- */
.onroad {
  border: 1px solid var(--line); border-left: 3px solid var(--blue);
  border-radius: 0; padding: 10px 12px; margin-bottom: 12px; background: #1a1a38;
  box-shadow: var(--drop-sm);
}
.onroad-kicker { font-family: var(--font-px); color: var(--ink-dimmer); font-size: var(--px-12); letter-spacing: 2px; }
.onroad-next { font-family: var(--font-px); font-size: var(--px-16); margin-top: 5px; letter-spacing: 1px; }
.onroad-next b { color: var(--blue); text-shadow: 0 2px 0 var(--shadow); }
.onroad-dist { color: var(--ink-dim); font-size: 0.76rem; margin-top: 3px; }

/* Removed with the scene-based rewrite that was reverted (see the note at the
   top of data/years.js): the year timeline (.tlbar/.tl), the crew strip
   (.crewstrip/.cs*), .scenehead, the digest (.dig*) and .title-art. Nothing has
   rendered any of them in a long time. */

/* ------------------------------------------------------------- pixel art -- */
img.portrait, img.cr-face { image-rendering: pixelated; image-rendering: crisp-edges; }
.pick-head { display: flex; align-items: center; gap: 12px; }
.pick-head .nm { line-height: 1.15; }
/* 72x84 and 28x32 are EXACTLY the natural sprite size at scale — see HANDOFF.
   Any other number lands the art on half-pixels and it goes soft. */
img.portrait { width: 72px; height: 84px; flex: 0 0 auto;
               background: #0d0d20; border: var(--edge) solid var(--line);
               border-radius: 0; padding: 2px; box-shadow: var(--drop-sm); }

.crew-row { display: grid; grid-template-columns: 32px 1fr; gap: 10px; align-items: start; }
img.cr-face { width: 28px; height: 32px; background: #0d0d20;
              border: 1px solid var(--line); border-radius: 0; }
.cr-body { min-width: 0; }
.cr-belt { grid-column: 2; }

/* ------------------------------------------------------------- minigames -- */
.mgcanvas { display: block; width: 100%; max-width: 320px; margin: 0 auto 12px;
            image-rendering: pixelated; image-rendering: crisp-edges;
            border: var(--edge) solid var(--line-hi); border-radius: 0; background: #0d0d20;
            box-shadow: var(--drop), 0 0 20px rgba(41,173,255,0.14);
            touch-action: manipulation; }
.mgpad { display: grid; grid-template-columns: 1fr 1.3fr 1fr; gap: 10px; max-width: 320px; margin: 0 auto; }
.mgb { min-height: 64px; font-size: var(--px-24); text-align: center; margin: 0;
       display: flex; align-items: center; justify-content: center;
       touch-action: manipulation; user-select: none; }
.mgb.eat { background: linear-gradient(180deg, #45356f 0%, #2f2f6e 100%);
           border-color: var(--amber); color: var(--amber); font-weight: 700; }
/* touch-action and user-select match .mgb above. They matter most on .hold,
   where a long press would otherwise select the label and raise the iOS
   callout menu mid-drink, but a tap game gains the double-tap-zoom fix too. */
.mgbtn { max-width: 320px; margin: 0 auto; min-height: 64px; text-align: center;
         display: flex; align-items: center; justify-content: center;
         touch-action: manipulation; user-select: none;
         -webkit-touch-callout: none; }
.mgbtn.hold { background: linear-gradient(180deg, #5a1f36 0%, #3a1226 100%);
              border-color: var(--amber); color: var(--amber); font-weight: 700; }
.mgbtn.hold:active { background: linear-gradient(180deg, #7a2a46 0%, #5a1f36 100%); }
.mgpad.two { grid-template-columns: 1fr 1fr; }
/* The swipe pad. touch-action:none is not optional — without it a drag up the
   canvas scrolls the page instead of throwing the bag, and on iOS it also
   triggers the overscroll bounce. */
.mgcanvas { touch-action: none; }
.mgswipe { margin: 8px 0 0; }

/* The pre-game block: START sits in the same column as the canvas and the pads,
   and takes the whole thing away once you are ready. */
#mgready { max-width: 320px; margin: 0 auto 12px; }
#mgready button { min-height: 52px; text-align: center; display: flex;
                  align-items: center; justify-content: center; }
#mgready p { margin: 6px 0 0; }

/* --------------------------------------------------------------- the road -- */
.scenecanvas { display: block; width: 100%; image-rendering: pixelated;
               image-rendering: crisp-edges; border: var(--edge) solid var(--line-hi);
               border-radius: 0; margin-bottom: 12px; background: #0d0d20;
               box-shadow: var(--drop); }

/* The cabin at a landmark is 240x108 where the road is 240x64, so it is
   taller for the same width and wants a little more air under the banner. */
.cabincanvas { margin-top: 10px; }

/* --------------------------------------------------------------- event art -- */
/* Only go two-column when there is actually art — otherwise the text block
   lands in the narrow art column and wraps to one word per line. */
.evpanel { display: block; }
.evpanel.art { display: grid; grid-template-columns: 80px minmax(0, 1fr);
               gap: 12px; align-items: start; }
.evpanel .evbody { min-width: 0; }
canvas.evart { width: 72px; height: 90px; image-rendering: pixelated;
               image-rendering: crisp-edges; background: #0d0d20;
               border: var(--edge) solid var(--line); border-radius: 0;
               box-shadow: var(--drop-sm); }
@media (max-width: 380px) { .evpanel.art { grid-template-columns: 56px minmax(0, 1fr); gap: 8px; }
  canvas.evart { width: 48px; height: 60px; } }

/* Event copy is the longest continuous reading in the game — it is the actual
   writing, where every other panel is a readout — so it gets a size and
   leading bump nothing else needs. Scoped to the first paragraph so the
   trailing `.small dim` notes on the result screen stay secondary to it. */
.evbody > p:first-of-type { font-size: 0.95rem; line-height: 1.6; }

/* ==========================================================================
   MOTION

   Everything below is inside prefers-reduced-motion: no-preference. Nothing
   here may be load-bearing for legibility — it is all confirmation of
   something the static layout already says.

   `steps()` easing rather than a smooth curve on anything short: a 4-frame
   step reads as animation drawn on a sprite sheet, a cubic-bezier reads as
   CSS. That distinction is most of the difference between this feeling 8-bit
   and feeling like a web app with a transition bolted on.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {

  /* Screen entrance. Applied by render() only on an actual screen CHANGE — an
     in-place re-render (tapping a pace, opening a card) must not replay it, or
     the page appears to flinch every time you touch anything. */
  #app.screen-in > *:not(.soundbtn) {
    animation: screenIn 0.22s steps(4) both;
  }
  @keyframes screenIn {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }

  /* Delta markers and belt points punch in. Both appear only in response to
     something the player just did, so replaying on re-render is harmless. */
  .d { animation: popIn 0.24s steps(4) both; }
  @keyframes popIn {
    0%   { opacity: 0; transform: translateY(5px) scale(0.5); }
    60%  { opacity: 1; transform: translateY(0) scale(1.35); }
    100% { opacity: 1; transform: none; }
  }
  /* The tally reuses `.d` as a prose column — it must not pop there. */
  .tallybox .d { animation: none; }

  .beltfx { animation: beltFlash 0.5s steps(6) 1; }
  @keyframes beltFlash {
    0%   { box-shadow: var(--lift), var(--drop), 0 0 0 3px var(--amber), 0 0 40px rgba(255,236,39,0.85); }
    100% { box-shadow: var(--lift), var(--drop), 0 0 0 0 transparent, 0 0 18px rgba(255,236,39,0.22); }
  }

  /* The morale bar tweens between values rather than jumping. render() sets
     the width one frame after insert so there is a previous value to move
     FROM — without that hop the transition has nothing to animate against. */
  .meter > i { transition: width 0.45s steps(12); }

  /* An arrival is the one moment the game should feel like it is shouting. */
  .arrive { animation: arriveIn 0.45s steps(5) both; }
  @keyframes arriveIn {
    0%   { transform: scale(0.9); opacity: 0; }
    70%  { transform: scale(1.03); opacity: 1; }
    100% { transform: none; opacity: 1; }
  }

  /* Trouble should be visible from across the room, so the alarm panel and a
     critical HUD cell breathe. Slow on purpose — a fast blink is an
     accessibility problem and reads as a broken element, not as a warning. */
  .panel.alarm,
  .hud-res div:has(.v.bad) { animation: alarmPulse 1.6s steps(2) infinite alternate; }
  @keyframes alarmPulse {
    from { box-shadow: var(--drop), 0 0 10px rgba(255,59,107,0.20); }
    to   { box-shadow: var(--drop), 0 0 22px rgba(255,59,107,0.50); }
  }

  /* Title screen: the logo drops in once. */
  .logo-main { animation: logoDrop 0.5s steps(6) both; }
  .logo-sub  { animation: logoDrop 0.5s steps(6) 0.12s both; }
  @keyframes logoDrop {
    from { opacity: 0; transform: translateY(-16px); }
    to   { opacity: 1; transform: none; }
  }
}
