/* =========================================================
   MIXTAPE — retro band-management game
   Design system based on Project Rockstar reference screenshots
   ========================================================= */

@import url('https://fonts.googleapis.com/css2?family=Bungee&family=VT323&family=Verdana&display=swap');

:root {
  --gold-bg: #f5b731;
  --gold-bg-dark: #e8a716;
  --purple: #3d1f6e;
  --purple-dark: #2a1450;
  --orange: #e8730b;
  --orange-bright: #ff8c1a;
  --red-header: #d9451e;
  --panel-bg: #ffffff;
  --panel-border: #8a5a00;
  --text-dark: #2a1a00;
  --link-blue: #1a4fa0;
  --green-money: #1c7a2e;
  --shadow: rgba(0, 0, 0, 0.35);

  /* v19.37: promoted out of ~150 raw hex repeats across the sheet so
     dark mode (see [data-theme="dark"] near the end of this file) can
     override the handful of tokens that actually carry most of the
     game's light/dark weight, instead of hand-editing hundreds of
     individual rules. --cream is the pale panel-body tint almost every
     card/box uses; --text-secondary/--text-tertiary are the two muted
     caption colors used everywhere for sub-labels and fine print;
     --dash-border is the tan dashed divider between list rows. */
  --cream: #fff8e6;
  --text-secondary: #6a5a30;
  --text-tertiary: #8a7a50;
  --dash-border: #e0d0a0;

  /* v19.39: dark mode toggle. --page-bg/--panel-bg/--text-dark are the
     three tokens that actually decide whether the whole page reads as
     light or dark; --gold-bg deliberately stays untouched by the
     [data-theme="dark"] override below even though it's visually similar
     to --page-bg today, because --gold-bg does double duty everywhere
     else as a bright accent color (nav text, button fills, badges) on
     TOP of dark headers — redefining it for dark mode would have broken
     every one of those accents, not just the page background. --page-bg
     is a dedicated, decoupled token that starts at the same value as
     --gold-bg but only the page background, so dark mode can repaint the
     background without touching any accent that happens to reuse gold. */
  --page-bg: #f5b731;

  /* v19.42: Josh — "dark mode looks like poop and it's hard to read
     anything." Root cause: --purple-dark and --green-money were both
     designed as TEXT colors assuming they'd always sit on the light
     --panel-bg (white) — a huge number of headings, names, and money
     figures across the whole game use one or the other. Once --panel-bg
     went dark, those same dark-purple/dark-green colors became nearly
     invisible against it — the actual bug, not "dark mode is bad."
     --heading-text and --money-positive are decoupled copies (NOT the
     same variable as --purple-dark/--green-money, which are still used
     as-is for backgrounds, borders, gradients, and decorative shadows
     that have nothing to do with this) so dark mode can brighten just
     the on-panel text roles without touching any of those other jobs. */
  --heading-text: #2a1450;
  --money-positive: #1c7a2e;

  --font-display: 'Bungee', 'Arial Black', sans-serif;
  --font-mono: 'VT323', monospace;
  --font-body: Verdana, Geneva, Arial, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--page-bg);
  font-family: var(--font-body);
  color: var(--text-dark);
}

body {
  background-image:
    repeating-linear-gradient(45deg, rgba(255,255,255,0.05) 0 2px, transparent 2px 40px);
}

/* v19.43: tutorial scroll lock — see lockTutorialScroll()/
   unlockTutorialScroll() in app.js. The body itself gets pinned via inline
   position:fixed at lock time; this just also stops the outer html element
   from scrolling, which plain overflow:hidden on body alone can miss on
   some mobile browsers. */
html.tutorial-scroll-locked {
  overflow: hidden;
}

/* ---------- CRT scanline overlay ---------- */
.crt-overlay {
  pointer-events: none;
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.09) 0px,
    rgba(0, 0, 0, 0.09) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
  animation: flicker 6s infinite;
}
@keyframes flicker {
  0%, 96%, 100% { opacity: 1; }
  97% { opacity: 0.92; }
  98% { opacity: 1; }
}

/* ---------- Screen management ---------- */
.screen {
  display: none;
  min-height: 100vh;
  width: 100%;
}
.screen.active { display: block; }

/* =========================================================
   Shared header banner
   ========================================================= */
.banner {
  background: linear-gradient(120deg, var(--purple) 0%, var(--purple) 40%, var(--purple-dark) 60%, var(--purple) 100%);
  background-image:
    repeating-linear-gradient(115deg, var(--orange) 0 14px, transparent 14px 90px),
    linear-gradient(120deg, var(--purple) 0%, var(--purple-dark) 100%);
  border-bottom: 4px solid var(--gold-bg-dark);
  padding: 10px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-shadow: 0 3px 8px var(--shadow);
}
.banner-logo {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--gold-bg);
  text-shadow: 2px 2px 0 var(--purple-dark), -1px -1px 0 #fff2;
  letter-spacing: 1px;
}
.banner-logo span { color: #fff; }
.banner-meta {
  font-family: var(--font-mono);
  color: var(--gold-bg);
  font-size: 18px;
}

/* =========================================================
   Landing page
   ========================================================= */
.landing {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: radial-gradient(ellipse at center 35%, #3a1a4d 0%, #1a0a26 55%, #0a0512 100%);
  overflow: hidden;
  text-align: center;
  padding: 20px;
}

.landing-title {
  font-family: var(--font-display);
  font-size: clamp(32px, 7vw, 56px);
  color: var(--gold-bg);
  text-shadow: 3px 3px 0 var(--purple-dark), 0 0 24px rgba(255, 180, 40, 0.5);
  margin: 10px 0 0;
  letter-spacing: 2px;
}
/* v16.13: start screen now shows just the MIXTAPE wordmark, no hero
   illustration — give it more presence on its own since it's the only
   visual on the page now. */
.landing-title-solo {
  font-size: clamp(48px, 12vw, 84px);
  margin: 0;
}
.landing-sub {
  color: #d8c8ff;
  font-family: var(--font-mono);
  font-size: 20px;
  margin-top: 4px;
  letter-spacing: 3px;
}

.enter-btn {
  margin-top: 26px;
  font-family: var(--font-display);
  font-size: 22px;
  color: #fff;
  background: linear-gradient(180deg, var(--orange-bright) 0%, var(--orange) 55%, #b85a08 100%);
  border: 3px solid #fff5;
  border-radius: 10px;
  padding: 14px 46px;
  cursor: pointer;
  box-shadow: 0 6px 0 #8a4405, 0 10px 18px var(--shadow);
  text-shadow: 1px 1px 0 #0004;
  transition: transform 0.08s ease;
}
.enter-btn:hover { filter: brightness(1.08); }
.enter-btn:active {
  transform: translateY(4px);
  box-shadow: 0 2px 0 #8a4405, 0 4px 10px var(--shadow);
}

.tagline {
  margin-top: 14px;
  font-family: var(--font-mono);
  font-size: 24px;
  color: var(--gold-bg);
}

.visitor-counter {
  margin-top: 30px;
  font-family: var(--font-mono);
  font-size: 15px;
  color: #9a8ab0;
  border: 1px solid #4a3a5e;
  padding: 4px 10px;
  border-radius: 3px;
  background: #150a1e;
}

.dialup-msg {
  font-family: var(--font-mono);
  color: #7fffb0;
  font-size: 15px;
  min-height: 18px;
  margin-top: 8px;
}

/* =========================================================
   Panels (shared by creation + hub)
   ========================================================= */
.page-wrap {
  max-width: 980px;
  margin: 0 auto;
  padding: 22px 16px 60px;
}

.panel {
  background: var(--panel-bg);
  border: 3px solid var(--panel-border);
  border-radius: 6px;
  box-shadow: 3px 4px 0 var(--shadow);
  margin-bottom: 18px;
  overflow: hidden;
}
.panel-header {
  background: linear-gradient(180deg, var(--red-header) 0%, #b8330f 100%);
  color: #fff;
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 1px;
  padding: 8px 12px;
  text-shadow: 1px 1px 0 #0006;
}
.panel-body { padding: 14px 16px; }

/* =========================================================
   v19.24: "Your Studio Deal" box — Josh: "redesign that totally. Make
   it look different than other boxes so it stands out." Deliberately
   NOT a .panel — a warm gold "real contract" look (double border, a
   torn/deckle top edge, a wax-seal corner emoji) instead of the
   standard cream box + red header every other panel uses, so this one
   reads as "a real deal with a real other player" the instant you see
   it, not just another stat box. Same shape (header + body) in both
   its signed and unsigned/pitch states — see .studio-deal-box-signed /
   .studio-deal-box-pitch for the two accent-color variants and
   renderStudioSignedPanel() in app.js for what fills the body.
   ========================================================= */
.studio-deal-box {
  position: relative;
  background: linear-gradient(180deg, #fff8e1 0%, #fbecc0 100%);
  border: 3px solid var(--panel-border);
  border-radius: 6px;
  box-shadow: 3px 4px 0 var(--shadow), inset 0 0 0 3px #fffbe8;
  margin-bottom: 18px;
  overflow: hidden;
}
.studio-deal-box::before {
  /* the "deckle edge" torn-paper strip along the top, purely decorative */
  content: '';
  display: block;
  height: 6px;
  background: repeating-linear-gradient(-45deg, var(--panel-border) 0, var(--panel-border) 4px, transparent 4px, transparent 8px);
  opacity: 0.35;
}
.studio-deal-box-header {
  background: linear-gradient(180deg, #6b3fa0 0%, #4a2a75 100%);
  color: #fff;
  font-family: var(--font-display);
  font-size: 14px;
  letter-spacing: 1px;
  padding: 10px 14px;
  text-shadow: 1px 1px 0 #0006;
  display: flex;
  align-items: center;
  gap: 8px;
}
.studio-deal-box-signed .studio-deal-box-header { background: linear-gradient(180deg, #1c7a2e 0%, #145a21 100%); }
.studio-deal-box-body { padding: 14px 16px; }
.studio-deal-intro { font-size: 13px; color: var(--text-secondary); margin: 0 0 8px; }
.studio-deal-pitch-body { font-size: 13px; color: #4a3a1a; line-height: 1.5; background: #fff3d0; border: 2px dashed #c99a2e; border-radius: 6px; padding: 10px 12px; margin: 0 0 12px; }
.studio-deal-terms .value em { font-style: normal; font-size: 11px; color: var(--text-tertiary); display: block; }
.studio-deal-note { font-size: 12px; color: var(--text-secondary); margin: 10px 0; }
.studio-deal-request-explainer {
  font-size: 12px;
  color: #4a3a1a;
  background: #eaf6ec;
  border: 2px solid #1c7a2e;
  border-radius: 6px;
  padding: 8px 10px;
  margin: 10px 0;
}
.studio-deal-empty { font-size: 13px; color: var(--text-tertiary); font-style: italic; }
/* v19.31: live-updating "you'll be messaging X" note under the studio
   picker dropdown, so it's clear which studio owner a message is going
   to before hitting the button. */
.studio-deal-picked-note { font-size: 12px; color: #4a3a1a; font-style: italic; margin: 6px 0 0; min-height: 14px; }

.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
}

/* =========================================================
   Character creation
   ========================================================= */
.creation-title {
  font-family: var(--font-display);
  color: var(--heading-text);
  font-size: 26px;
  text-align: center;
  margin: 18px 0;
  text-shadow: 1px 1px 0 var(--gold-bg-dark);
}

.field-row { margin-bottom: 14px; }
.field-row label {
  display: block;
  font-weight: bold;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
  color: var(--heading-text);
}
.field-row input[type="text"],
.field-row input[type="number"],
.field-row select {
  width: 100%;
  padding: 8px 10px;
  border: 2px solid var(--panel-border);
  border-radius: 4px;
  font-family: var(--font-body);
  font-size: 15px;
  background: #fffdf5;
}

.choice-row { display: flex; gap: 10px; flex-wrap: wrap; }
.choice-btn {
  flex: 1 1 140px;
  padding: 10px 12px;
  border: 2px solid var(--panel-border);
  border-radius: 6px;
  background: var(--cream);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: bold;
  font-size: 14px;
  text-align: center;
  transition: all 0.1s ease;
}
.choice-btn:hover { background: #ffe9b0; }
.choice-btn.selected {
  background: var(--orange);
  color: #fff;
  border-color: var(--orange-bright);
  box-shadow: 0 0 0 2px var(--orange-bright) inset;
}
.choice-sub { display: block; font-weight: normal; font-size: 11px; margin-top: 2px; opacity: 0.85; }

/* v16.6: per-role hiring requirement chips (0/1, 1/1) for the first-band
   and sign-a-new-band screens */
.role-req-bar { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.role-req-chip {
  display: inline-block;
  padding: 5px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: bold;
  border: 2px solid var(--panel-border);
  background: #fff2cf;
  color: var(--text-secondary);
}
.role-req-chip.role-req-missing { background: #ffe1d6; border-color: #d9451e; color: #a3341c; }
.role-req-chip.role-req-ok { background: #e2f2d9; border-color: #4a7a3d; color: #2f5426; }

/* v16.6: instrument tabs for browsing a hire pool while building a band */
.instrument-tabs-row { margin: 8px 0 12px; }
.instrument-tabs-row .instrument-tab-btn {
  flex: 0 1 auto;
  padding: 7px 14px;
  font-size: 13px;
}

.backstory-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}
.backstory-card {
  border: 2px solid var(--panel-border);
  border-radius: 8px;
  background: var(--cream);
  padding: 14px;
  cursor: pointer;
  transition: all 0.12s ease;
}
.backstory-card:hover { background: #ffedbc; transform: translateY(-2px); }
.backstory-card.selected {
  background: #fff1cf;
  border-color: var(--orange);
  box-shadow: 0 0 0 3px var(--orange-bright) inset;
}
.backstory-card h3 {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--heading-text);
  margin: 0 0 8px;
}
.backstory-card p {
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
  color: #4a3a20;
}

/* v19.31: "Hire Another Member" is optional once a roster already meets
   its role requirements and has nobody pending fire — deemphasize it
   (smaller, lower-opacity) so it stops competing with the primary
   "Hire New Member" prompt a fresh/understaffed roster still needs. */
.roster-hire-optional { font-size: 12px; padding: 6px 12px; opacity: 0.75; }
.btn-primary, .btn-secondary, .btn-danger {
  font-family: var(--font-display);
  font-size: 15px;
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 12px 28px;
  cursor: pointer;
  box-shadow: 0 4px 0 rgba(0,0,0,0.3);
  transition: transform 0.08s ease;
}
.btn-primary { background: linear-gradient(180deg, var(--orange-bright), var(--orange)); }
.btn-secondary { background: linear-gradient(180deg, #7a5aa8, var(--purple)); }
.btn-danger { background: linear-gradient(180deg, #d9451e, #a52d10); }
.btn-primary:active, .btn-secondary:active, .btn-danger:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(0,0,0,0.3); }
.btn-danger:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }
.btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.form-actions { text-align: center; margin-top: 10px; }

/* =========================================================
   Hub dashboard
   ========================================================= */
.avatar-circle {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: linear-gradient(160deg, var(--orange-bright), var(--purple));
  color: #fff;
  font-family: var(--font-display);
  font-size: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 3px solid var(--panel-border);
  float: left;
  margin-right: 12px;
  overflow: hidden;
}
.avatar-circle-sm {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid var(--panel-border);
  overflow: hidden;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: middle;
  margin-right: 6px;
}
.avatar-svg { display: block; }
.stat-list { list-style: none; margin: 0; padding: 0; font-size: 14px; }
.stat-list li { padding: 3px 0; border-bottom: 1px dashed var(--dash-border); }
.stat-list li:last-child { border-bottom: none; }
.stat-list .label { color: var(--text-secondary); }
.stat-list .value { font-weight: bold; float: right; }
.stat-list .value.money { color: var(--money-positive); }
.stat-list li.clickable-stat { cursor: pointer; }
.stat-list li.clickable-stat:hover { background: #fff1cf; }
.stat-list li.clickable-stat .value { color: var(--link-blue); text-decoration: underline dotted; }

.ladder-list { text-align: left; margin-top: 10px; max-height: 340px; overflow-y: auto; }
.ladder-row { display: flex; gap: 10px; align-items: flex-start; padding: 8px 4px; border-bottom: 1px dashed var(--dash-border); }
.ladder-row:last-child { border-bottom: none; }
.ladder-row.locked { opacity: 0.55; }
.ladder-row.current { background: #fff1cf; border-radius: 6px; }
.ladder-icon { font-size: 18px; width: 22px; text-align: center; flex-shrink: 0; }
.ladder-row.achieved .ladder-icon { color: var(--money-positive); }
.ladder-row.current .ladder-icon { color: var(--orange); }
.ladder-label { font-family: var(--font-display); font-size: 13px; color: var(--heading-text); }
.ladder-you { font-family: var(--font-body); font-size: 9px; background: var(--orange); color: #fff; padding: 1px 6px; border-radius: 8px; margin-left: 4px; }
.ladder-sub { font-size: 11px; color: var(--text-secondary); margin-top: 2px; }

.hours-bar-outer {
  background: #efe0b8;
  border: 2px solid var(--panel-border);
  border-radius: 20px;
  height: 20px;
  overflow: hidden;
  margin-top: 6px;
}
.hours-bar-inner {
  height: 100%;
  background: linear-gradient(90deg, #ffd166, var(--orange));
  transition: width 0.25s ease;
}
.hours-text { font-family: var(--font-mono); font-size: 15px; text-align: center; margin-top: 2px; }

.action-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-top: 6px;
}
.action-btn {
  font-family: var(--font-display);
  font-size: 13px;
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 16px 8px;
  cursor: pointer;
  text-align: center;
  box-shadow: 0 4px 0 rgba(0,0,0,0.3);
  transition: transform 0.08s ease;
  line-height: 1.5;
}
.action-btn .cost { display: block; font-family: var(--font-mono); font-size: 13px; margin-top: 4px; opacity: 0.9; }
.action-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(0,0,0,0.3); }
.action-btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none; }
.action-train { background: linear-gradient(180deg, #4fd1c5, #1f9e91); }
.action-record { background: linear-gradient(180deg, #f77f8e, #d9451e); }
.action-write { background: linear-gradient(180deg, #7a5aa8, var(--purple)); }
.action-advertise { background: linear-gradient(180deg, #f7c948, var(--orange)); }
.action-sleep { background: linear-gradient(180deg, #7a5aa8, var(--purple)); }

.catalog-list { list-style: none; margin: 0; padding: 0; }
.catalog-list li {
  display: flex;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 13px;
}
.catalog-empty { font-size: 13px; color: var(--text-tertiary); font-style: italic; }

/* ---- Pixel Magazine icon (v19.37 dashboard shortcut) ---- */
/* Josh — "make a magazine button on the dashboard, with a little
   magazine 8bit." Pure-CSS pixel art (single zero-size element, every
   "pixel" drawn as a box-shadow offset) rather than an image file — same
   blocky retro-8-bit look as everything else in the game, no asset to
   ship. 8 wide x 10 tall grid at 2px/pixel: a purple-outlined magazine
   with a red masthead band and two gold text-line accents. */
.pixel-magazine-icon {
  display: inline-block;
  /* v19.41 fix: Josh — "the magazine tile is still broken on dashboard"
     (screenshot showed a smeared rectangle blob, not the crisp icon).
     The v19.38 "fix" here was wrong: box-shadow always paints a copy of
     the ELEMENT'S OWN box shape at each offset (when spread is 0, as
     every entry below is), so widening this element itself to 16x20px
     made every one of the ~80 shadow entries paint a full 16x20
     rectangle at each offset instead of a crisp 2x2 "pixel" — all of
     them overlapping into exactly the smeared mess in the screenshot.
     The real fix: keep THIS element sized to match the drawn art
     (16x20) so flexbox/vertical-align reserve the right space for it —
     that part of the v19.38 reasoning was correct — but move the actual
     box-shadow painting onto a ::before pseudo-element sized back down
     to the original 2x2 "pixel" unit and pinned to this element's
     top-left corner via position:absolute, so the shadows paint crisp
     2x2 squares again without affecting this element's own layout box. */
  position: relative;
  width: 16px;
  height: 20px;
  margin-right: 6px;
  vertical-align: middle;
}
.pixel-magazine-icon::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 2px;
  height: 2px;
  box-shadow:
    0px 0px 0 0 var(--purple-dark),
    2px 0px 0 0 var(--purple-dark),
    4px 0px 0 0 var(--purple-dark),
    6px 0px 0 0 var(--purple-dark),
    8px 0px 0 0 var(--purple-dark),
    10px 0px 0 0 var(--purple-dark),
    12px 0px 0 0 var(--purple-dark),
    14px 0px 0 0 var(--purple-dark),
    0px 2px 0 0 var(--purple-dark),
    2px 2px 0 0 var(--red-header),
    4px 2px 0 0 var(--red-header),
    6px 2px 0 0 var(--red-header),
    8px 2px 0 0 var(--red-header),
    10px 2px 0 0 var(--red-header),
    12px 2px 0 0 var(--red-header),
    14px 2px 0 0 var(--purple-dark),
    0px 4px 0 0 var(--purple-dark),
    2px 4px 0 0 var(--red-header),
    4px 4px 0 0 var(--red-header),
    6px 4px 0 0 var(--red-header),
    8px 4px 0 0 var(--red-header),
    10px 4px 0 0 var(--red-header),
    12px 4px 0 0 var(--red-header),
    14px 4px 0 0 var(--purple-dark),
    0px 6px 0 0 var(--purple-dark),
    2px 6px 0 0 var(--cream),
    4px 6px 0 0 var(--cream),
    6px 6px 0 0 var(--cream),
    8px 6px 0 0 var(--cream),
    10px 6px 0 0 var(--cream),
    12px 6px 0 0 var(--cream),
    14px 6px 0 0 var(--purple-dark),
    0px 8px 0 0 var(--purple-dark),
    2px 8px 0 0 var(--cream),
    4px 8px 0 0 var(--gold-bg),
    6px 8px 0 0 var(--gold-bg),
    8px 8px 0 0 var(--gold-bg),
    10px 8px 0 0 var(--cream),
    12px 8px 0 0 var(--cream),
    14px 8px 0 0 var(--purple-dark),
    0px 10px 0 0 var(--purple-dark),
    2px 10px 0 0 var(--cream),
    4px 10px 0 0 var(--cream),
    6px 10px 0 0 var(--cream),
    8px 10px 0 0 var(--cream),
    10px 10px 0 0 var(--cream),
    12px 10px 0 0 var(--cream),
    14px 10px 0 0 var(--purple-dark),
    0px 12px 0 0 var(--purple-dark),
    2px 12px 0 0 var(--cream),
    4px 12px 0 0 var(--gold-bg),
    6px 12px 0 0 var(--gold-bg),
    8px 12px 0 0 var(--cream),
    10px 12px 0 0 var(--cream),
    12px 12px 0 0 var(--cream),
    14px 12px 0 0 var(--purple-dark),
    0px 14px 0 0 var(--purple-dark),
    2px 14px 0 0 var(--cream),
    4px 14px 0 0 var(--cream),
    6px 14px 0 0 var(--cream),
    8px 14px 0 0 var(--cream),
    10px 14px 0 0 var(--cream),
    12px 14px 0 0 var(--cream),
    14px 14px 0 0 var(--purple-dark),
    0px 16px 0 0 var(--purple-dark),
    2px 16px 0 0 var(--cream),
    4px 16px 0 0 var(--cream),
    6px 16px 0 0 var(--cream),
    8px 16px 0 0 var(--cream),
    10px 16px 0 0 var(--cream),
    12px 16px 0 0 var(--cream),
    14px 16px 0 0 var(--purple-dark),
    0px 18px 0 0 var(--purple-dark),
    2px 18px 0 0 var(--purple-dark),
    4px 18px 0 0 var(--purple-dark),
    6px 18px 0 0 var(--purple-dark),
    8px 18px 0 0 var(--purple-dark),
    10px 18px 0 0 var(--purple-dark),
    12px 18px 0 0 var(--purple-dark),
    14px 18px 0 0 var(--purple-dark);
}
.dash-magazine-btn { display: inline-flex; align-items: center; }

/* ---- Cover art (v16.5) ---- */
.cover-art-thumb {
  display: inline-grid;
  vertical-align: middle;
  margin-right: 6px;
  border: 1px solid var(--panel-border);
  background: #fff;
}
.cover-art-thumb div { width: 100%; height: 100%; }
/* v19.21: clickable wrapper around a cover-art-thumb on chart rows, opens
   the song art profile modal (attribution + like button). */
.cover-art-thumb-btn {
  display: inline-block;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  vertical-align: middle;
  line-height: 0;
}
.cover-art-thumb-btn:hover .cover-art-thumb { border-color: var(--accent, #c99a2e); }
.cover-art-note { font-size: 12px; color: var(--text-secondary); margin-top: -6px; }
.cover-art-editor { display: flex; flex-direction: column; align-items: center; gap: 10px; margin: 10px 0; }
.cover-art-grid {
  display: grid;
  width: 224px;
  height: 224px;
  border: 2px solid var(--panel-border);
  background: #fff;
}
.cover-art-cell { border: 1px solid #eee0c0; cursor: pointer; }
.cover-art-palette { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; max-width: 260px; }
.cover-art-swatch {
  width: 24px;
  height: 24px;
  border-radius: 4px;
  border: 2px solid transparent;
  cursor: pointer;
  padding: 0;
}
.cover-art-swatch.selected { border-color: var(--purple-dark); }
.cover-art-btn { font-size: 11px; padding: 3px 8px; }

.charts-placeholder {
  text-align: center;
  padding: 18px 10px;
  font-family: var(--font-mono);
  font-size: 15px;
  color: var(--text-tertiary);
  background: repeating-linear-gradient(45deg, #fffdf5 0 8px, #fff6dd 8px 16px);
  border: 2px dashed var(--panel-border);
  border-radius: 6px;
}

/* ---------- Top 10 chart ---------- */
.chart-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 260px;
  overflow-y: auto;
}
.chart-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 13px;
}
.chart-list li:last-child { border-bottom: none; }
.chart-rank {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--text-tertiary);
  width: 28px;
  flex-shrink: 0;
}
.chart-name { flex: 1; }
.chart-fame {
  font-family: var(--font-mono);
  font-weight: bold;
  color: var(--heading-text);
}
.chart-row-player {
  background: #fff1cf;
  border-radius: 4px;
  font-weight: bold;
  padding: 4px 4px;
}
.chart-row-player .chart-rank { color: var(--orange); }
.chart-note {
  font-size: 11px;
  color: var(--text-tertiary);
  margin: 8px 0 0;
  font-style: italic;
}

/* ---------- v16.15: Weekly Chart recap panel ---------- */
.weekly-chart-panel { margin-bottom: 14px; }
.weekly-chart-last {
  font-size: 13px;
  margin: 0 0 8px;
  line-height: 1.5;
}
.weekly-chart-history {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  max-height: 200px;
  overflow-y: auto;
}
.weekly-chart-history li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 12px;
}
.weekly-chart-history li:last-child { border-bottom: none; }

/* ---------- Activity / time system ---------- */
.time-toggle {
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--heading-text);
  background: var(--gold-bg);
  border: 2px solid var(--purple-dark);
  border-radius: 12px;
  padding: 2px 10px;
  cursor: pointer;
}
.time-toggle:hover { background: #fff2c8; }

.activity-box {
  margin-top: 10px;
  border: 2px dashed var(--panel-border);
  border-radius: 6px;
  padding: 10px;
  text-align: center;
}
.activity-idle-note {
  font-size: 12px;
  color: var(--text-tertiary);
  font-style: italic;
  margin: 0 0 10px;
}
.activity-idle .dash-shortcut-row { justify-content: center; }
.activity-idle .dash-shortcut-row button { flex: 0 1 auto; min-width: 120px; font-size: 12px; padding: 6px 12px; }
.activity-label {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--heading-text);
}
.activity-countdown {
  font-family: var(--font-mono);
  font-size: 26px;
  color: var(--orange);
}

.coming-soon-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.coming-soon-list li {
  padding: 6px 0;
  font-size: 13px;
  border-bottom: 1px dashed #d9c68a;
}
.coming-soon-list li:last-child { border-bottom: none; }

/* ---------- Chart tabs (also: Store Items/Garage, Finance's 4 sub-tabs,
   Inbox's Inbox/Forum, Charts' own type/view toggles — every screen with
   sub-tabs shares this one class). v16.6: Josh flagged these as "lost" —
   bumped size, weight, and contrast on BOTH the active and inactive state
   (inactive tabs used to be a near-white cream that barely read as a
   button at all against the page background), not just made the active
   one stand out more. */
.chart-tabs { display: flex; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.chart-tab {
  font-family: var(--font-body);
  font-weight: bold;
  font-size: 14px;
  padding: 9px 18px;
  border: 3px solid var(--orange);
  border-radius: 16px;
  background: var(--gold-bg);
  cursor: pointer;
  color: var(--heading-text);
  box-shadow: 0 2px 0 var(--panel-border);
}
.chart-tab.active { background: var(--orange); color: #fff; border-color: var(--purple-dark); box-shadow: 0 2px 0 var(--purple-dark); }

/* ---------- v16.13: Charts screen cleanup ----------
   Josh: "Clean up chart ui, is confusing and not user friendly." — the
   Charts screen used to stack THREE rows of visually-identical big pill
   buttons (chart type, Overall/Genre, and the Multiplayer data-source
   toggle) with no labels distinguishing what each row actually does.
   #chartTypeTabs (Bands/Songs/World) keeps the normal .chart-tab look —
   that's the real primary navigation. Below it, .chart-subcontrols puts
   the Overall/Genre filter and the data-source toggle side by side in
   one smaller, clearly-labeled row instead of two more full-width pill
   rows, so the screen doesn't read as "four rows of tabs" at a glance. */
.chart-subcontrols {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dashed var(--dash-border);
}
.chart-tabs-label {
  font-size: 10px;
  font-weight: bold;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  align-self: center;
  margin-right: 2px;
}
#chartTabs.chart-filter-tabs {
  margin-top: 0;
  gap: 6px;
}
#chartTabs.chart-filter-tabs .chart-tab {
  font-size: 12px;
  padding: 5px 12px;
  border-width: 2px;
  border-radius: 12px;
  box-shadow: none;
}
/* v19.16: .chart-mp-toggle/.chart-mp-toggle-row (the old real-players
   data-source toggle) removed along with the rest of the standalone
   World tab in v19.13 — the button and its markup are long gone from
   index.html, this was just the dead CSS rule left behind. Cleaned up
   here while already touching this section. */

/* v19.16: highlights whichever genre filter tab matches the player's
   own band's genre (class + the ★ suffix are both applied in
   renderCharts()) — a subtle cue only, every genre stays equally
   clickable regardless of the player's own. Layers under .active
   rather than replacing it, so "this is your genre" and "this is the
   one currently selected" both stay visible if they're the same tab. */
#chartTabs.chart-filter-tabs .chart-tab-own-genre { border-color: var(--orange); }
#chartTabs.chart-filter-tabs .chart-tab-own-genre.active { box-shadow: 0 0 0 2px var(--purple-dark) inset; }

/* v19.16: the "···" separator row between the top 10 and the small
   window of rows around the player's own rank (chartTopAndNeighborhood()
   in app.js) — shown only when there's a real gap between rank 10 and
   wherever the player actually sits. */
.chart-list-gap {
  justify-content: center;
  color: #b0a070;
  font-weight: bold;
  letter-spacing: 2px;
}

/* ---------- Band roster ---------- */
.roster-list { list-style: none; margin: 0; padding: 0; }
.roster-list li {
  padding: 6px 0;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.roster-list li:last-child { border-bottom: none; }
.roster-instrument { color: var(--text-tertiary); font-size: 11px; }
.roster-top-row { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 4px; width: 100%; }
.roster-contract-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--text-secondary);
}
.roster-contract-row.contract-expiring { color: var(--red-header); font-weight: bold; }
.wage-badge { font-family: var(--font-mono); color: var(--money-positive); }
.roster-renew-btn, .roster-fire-btn { font-size: 11px; padding: 3px 10px; }
.fire-pending-badge { font-size: 11px; color: var(--red-header); font-weight: bold; font-style: italic; }

/* ---------- Member tabs (training screen) ---------- */
.member-tabs {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.member-tab {
  font-family: var(--font-body);
  font-weight: bold;
  font-size: 13px;
  padding: 7px 16px;
  border: 2px solid var(--panel-border);
  border-radius: 16px;
  background: var(--cream);
  cursor: pointer;
  color: var(--heading-text);
}
.member-tab.active { background: var(--purple); color: #fff; border-color: var(--purple-dark); }

/* ---------- Training screen ---------- */
.skill-card .panel-body { text-align: center; }
/* v19.27: Josh — a raw number like "32" reads as noise to a new player who
   has no idea what scale it's on. The TITLE is now the hero readout
   (skillDisplayTitle always returns a real word, even before the first
   real checkpoint at 25 — see SKILL_ZERO_TITLE), the old big orange number
   is demoted to a small parenthetical caption underneath for anyone who
   still wants the exact figure. */
.skill-title-hero {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1.15;
  color: var(--orange);
  text-shadow: 1px 1px 0 var(--purple-dark);
  padding: 0 4px;
}
.skill-value-caption {
  font-size: 11px;
  color: #9a8a5a;
  margin: 2px 0 6px;
}
.skill-desc {
  font-size: 12px;
  color: var(--text-secondary);
  min-height: 32px;
  margin: 6px 0 12px;
}
.skill-title-progress {
  font-size: 11px;
  color: #8a7a4a;
  margin-bottom: 4px;
}
.skill-title-maxed { color: #b8860b; font-weight: 700; }
.skill-train-btn { width: 100%; }
/* v19.27, redone v19.31: cosmetic tier frames — Josh picked "cosmetic
   reward" for what a title tier gets you beyond the quiet +2% stat bonus.
   The first pass (a 1px inset shadow, a border-color barely different from
   the game's own default gold/brown panel border) was too subtle to
   actually notice — Josh: "I didn't even notice the trim in the
   screenshot... make it more noticable." This version goes much louder: a
   real thick, saturated border (not a close cousin of the theme's own
   brown), a visible background color wash, a stronger glow, AND a corner
   ribbon that names the tier outright (bronze/silver/gold/platinum/
   diamond) so there's no ambiguity even at a glance. Tier 0 (no title yet)
   stays the ordinary panel look — nothing to show off yet. */
.skill-card { position: relative; }
.skill-tier-1 { border: 4px solid #cd7f32; box-shadow: 0 0 0 2px #cd7f32 inset, 0 0 10px rgba(205,127,50,0.5); }
.skill-tier-1 .panel-body { background: linear-gradient(160deg, rgba(205,127,50,0.22), transparent 65%); }
.skill-tier-2 { border: 4px solid #b8c2cc; box-shadow: 0 0 0 2px #b8c2cc inset, 0 0 12px rgba(184,194,204,0.6); }
.skill-tier-2 .panel-body { background: linear-gradient(160deg, rgba(184,194,204,0.28), transparent 65%); }
.skill-tier-3 { border: 4px solid #ffcc33; box-shadow: 0 0 0 2px #ffcc33 inset, 0 0 14px rgba(255,204,51,0.65); }
.skill-tier-3 .panel-body { background: linear-gradient(160deg, rgba(255,204,51,0.25), transparent 65%); }
.skill-tier-4 { border: 4px solid #a259ff; box-shadow: 0 0 0 2px #a259ff inset, 0 0 16px rgba(162,89,255,0.65); }
.skill-tier-4 .panel-body { background: linear-gradient(160deg, rgba(162,89,255,0.22), transparent 65%); }
.skill-tier-5 {
  border: 4px solid #2ee6d6;
  box-shadow: 0 0 0 2px #2ee6d6 inset, 0 0 20px rgba(46,230,214,0.75);
}
.skill-tier-5 .panel-body { background: linear-gradient(160deg, rgba(46,230,214,0.20), rgba(162,89,255,0.16) 70%); }
.skill-tier-ribbon {
  position: absolute;
  top: 8px;
  right: -30px;
  width: 120px;
  transform: rotate(40deg);
  text-align: center;
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.04em;
  padding: 3px 0;
  color: #fff;
  text-shadow: 1px 1px 0 rgba(0,0,0,0.35);
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
  z-index: 2;
  pointer-events: none;
}
.skill-tier-1 .skill-tier-ribbon { background: linear-gradient(180deg, #e0995a, #a8611f); }
.skill-tier-2 .skill-tier-ribbon { background: linear-gradient(180deg, #dbe3e9, #93a0aa); }
.skill-tier-3 .skill-tier-ribbon { background: linear-gradient(180deg, #ffe27a, #d19c1a); }
.skill-tier-4 .skill-tier-ribbon { background: linear-gradient(180deg, #c896ff, #7a2ee0); }
.skill-tier-5 .skill-tier-ribbon { background: linear-gradient(90deg, #2ee6d6, #a259ff); }
/* v19.21: the "?" mechanical-explanation button on each skill card. */
.skill-help-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid currentColor;
  background: transparent;
  color: inherit;
  font-size: 11px;
  font-weight: bold;
  line-height: 1;
  cursor: pointer;
  vertical-align: middle;
  margin-left: 4px;
  padding: 0;
}
.skill-mech {
  font-size: 12px;
  color: var(--text-secondary);
  text-align: left;
  background: rgba(0,0,0,0.12);
  border-radius: 8px;
  padding: 8px 10px;
  margin: -4px 0 10px;
}

/* ---------- Toast (AIM-style popup) ---------- */
/* v19.9: root-caused "the Run Ad Campaign button does nothing when
   clicked" (and, by the same mechanism, any button tapped right after
   an action resolves) — toasts have no click behavior of their own, but
   sat at z-index 10000, ABOVE the tutorial spotlight overlay (9000) and
   everything else on the page, with no pointer-events override, so they
   defaulted to catching clicks/taps like any other element. On mobile,
   .toast-wrap goes full-width (`left/right: 8px`) right along the bottom
   of the screen, exactly where a toast like "Release Started" lands
   right before the tutorial highlights Run an Ad Campaign — a tap there
   within the toast's ~4.2s lifetime hit the toast and silently did
   nothing, instead of reaching the real button underneath. Confirmed via
   a Playwright repro that walked the tutorial through Train/Write/
   Record/Release and inspected the Advertise step's DOM: the button
   itself was enabled/visible/wired correctly the whole time — nothing
   in app.js needed to change. pointer-events: none lets every click/tap
   pass straight through the toast layer to whatever's actually
   underneath; toasts were never clickable/dismissable in the first
   place (they just auto-remove after 4.2s), so this changes nothing
   about how they look or behave, only that they stop blocking taps. */
.toast-wrap {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 10000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.toast {
  background: var(--cream);
  border: 2px solid var(--panel-border);
  border-radius: 6px;
  box-shadow: 3px 3px 0 var(--shadow);
  padding: 10px 14px;
  font-size: 13px;
  max-width: 260px;
  animation: toast-in 0.25s ease;
}
.toast strong { display: block; color: var(--heading-text); font-family: var(--font-display); font-size: 11px; margin-bottom: 3px; }
@keyframes toast-in {
  from { transform: translateX(40px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

/* ---------- Persistent top nav ---------- */
.topnav {
  background: linear-gradient(120deg, var(--purple) 0%, var(--purple) 40%, var(--purple-dark) 60%, var(--purple) 100%);
  background-image:
    repeating-linear-gradient(115deg, var(--orange) 0 14px, transparent 14px 90px),
    linear-gradient(120deg, var(--purple) 0%, var(--purple-dark) 100%);
  border-bottom: 4px solid var(--gold-bg-dark);
  padding: 10px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  box-shadow: 0 3px 8px var(--shadow);
  position: sticky;
  top: 0;
  z-index: 500;
}
.topnav-logo {
  font-family: var(--font-display);
  font-size: 24px;
  color: var(--gold-bg);
  text-shadow: 2px 2px 0 var(--purple-dark);
}
.topnav-logo span { color: #fff; }
.topnav-tabs { display: flex; gap: 3px; flex-wrap: wrap; }
.topnav-tab {
  font-family: var(--font-body);
  font-weight: bold;
  font-size: 12px;
  color: var(--gold-bg);
  background: rgba(0,0,0,0.2);
  border: 2px solid transparent;
  border-radius: 6px;
  padding: 5px 9px;
  cursor: pointer;
  white-space: nowrap;
}
.topnav-tab:hover { background: rgba(0,0,0,0.35); }
.topnav-tab.active {
  background: var(--orange);
  color: #fff;
  border-color: var(--orange-bright);
}
/* v19.3: .nav-tab-badge generalizes the old .inbox-badge look (same red
   pill) to two new top-nav badges — Store and Finance — see the "pill
   tab a player would never find" fix in the v19.3 design-notes section.
   Kept the .inbox-badge name/id in place rather than renaming it, so
   this is additive, not a rename. */
.inbox-badge, .nav-tab-badge {
  background: var(--red-header);
  color: #fff;
  border-radius: 10px;
  font-size: 10px;
  padding: 1px 6px;
  margin-left: 3px;
}
/* v19.4: Josh — "make them blink red if something is needed there,"
   on the new Store/Finance nav badges (v19.3). A steady badge is easy
   to tune out on a screen you're not currently looking at; a blink
   catches the eye out of the corner of it, which is the whole point
   of a "something needs you, and you'd never think to look" signal.
   Opacity pulse rather than a color swap — reads as "blinking" without
   fighting the badge's own red. Respects reduced-motion. */
@keyframes navBadgeBlink { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
.nav-tab-badge-blink { animation: navBadgeBlink 1s step-start infinite; }
@media (prefers-reduced-motion: reduce) {
  .nav-tab-badge-blink { animation: none; }
}
.topnav-right { display: flex; align-items: center; gap: 10px; }
/* v19.32: header band-switcher dropdown, adopted from the original
   Project Rockstar's "managing: [Band ▼]" selector — Josh's own pick. */
.topnav-band-switcher-wrap { display: inline-flex; align-items: center; gap: 5px; }
.topnav-band-switcher-label { font-size: 11px; color: #d8c8a0; text-transform: uppercase; letter-spacing: 0.03em; }
.topnav-band-switcher {
  font-family: var(--font-mono);
  font-size: 13px;
  color: #fff;
  background: rgba(0,0,0,0.3);
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: 6px;
  padding: 3px 6px;
  max-width: 140px;
}
.topnav-money {
  font-family: var(--font-mono);
  font-size: 18px;
  color: #b8ffb0;
  background: rgba(0,0,0,0.25);
  padding: 3px 10px;
  border-radius: 10px;
}

/* ---------- Modal ---------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 10, 5, 0.6);
  z-index: 20000;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* v19.34 real bug fix — actionConfirmModal (the "this will take X, go
   ahead?" pre-action dialog from confirmLongAction()) is opened WHILE
   another modal (adModal/writeModal/recordModal/releaseModal) is still
   open underneath it. Both share .modal-overlay's z-index (20000), so
   which one visually wins was pure DOM order — actionConfirmModal
   happens to sit earlier in index.html than adModal, so adModal was
   painting on top, making the confirm dialog invisible AND unclickable
   (Josh: "Run ad campaign is STILL broken... nothing happens" — this is
   why: the confirm dialog was there the whole time, just hidden behind
   the still-open Ad modal). Give it its own higher stacking layer so it
   always wins regardless of DOM order or which modal opened it.  */
#actionConfirmModal.modal-overlay {
  z-index: 20001;
}
.modal-box {
  background: var(--panel-bg);
  border: 3px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 4px 5px 0 var(--shadow);
  padding: 22px 24px;
  max-width: 360px;
  /* v16.1: some callers override max-width up to 560px inline for wider
     content (the ad-campaign modal, the setlist builder). width:92vw here
     — combined with whatever max-width is in effect — means the box is
     always whichever is SMALLER: 92% of the viewport or that max-width.
     On a phone that guarantees it never forces horizontal scroll; on
     desktop it's still capped at the caller's intended max-width exactly
     as before. */
  width: 92vw;
  max-height: 88vh;
  overflow-y: auto;
  text-align: center;
}
.modal-box h3 {
  font-family: var(--font-display);
  color: var(--heading-text);
  margin-top: 0;
}
.modal-box p { font-size: 13px; color: #4a3a20; }
.modal-actions { display: flex; gap: 10px; justify-content: center; margin-top: 14px; }

/* ---------- Hire pool ---------- */
.hire-pool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}
.hire-card {
  background: var(--panel-bg);
  border: 3px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 3px 4px 0 var(--shadow);
  padding: 14px;
  text-align: center;
}
.hire-name { font-family: var(--font-display); font-size: 15px; color: var(--heading-text); }
.hire-instrument { font-size: 12px; color: var(--text-secondary); margin: 3px 0 6px; }
.hire-cost { font-family: var(--font-mono); font-size: 18px; color: var(--money-positive); margin-bottom: 2px; }
.hire-wage { font-family: var(--font-mono); font-size: 12px; color: var(--text-secondary); margin-bottom: 8px; }
.hire-toggle-btn, .hire-btn { width: 100%; margin-top: 6px; font-size: 13px; padding: 8px 10px; }
.hire-card-stats { margin-top: 10px; text-align: left; }
.hire-stat-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  margin-bottom: 4px;
}
.hire-stat-row span:first-child { width: 78px; flex-shrink: 0; color: var(--text-secondary); }
.hire-stat-row span:last-child { width: 18px; flex-shrink: 0; text-align: right; font-weight: bold; }
.hire-stat-bar-outer {
  flex: 1;
  height: 8px;
  background: #efe0b8;
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  overflow: hidden;
}
.hire-stat-bar-inner { height: 100%; background: linear-gradient(90deg, #ffd166, var(--orange)); }

/* ---------- Pick your first band ---------- */
.pick-band-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
}
.pick-band-card {
  background: var(--panel-bg);
  border: 3px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 3px 4px 0 var(--shadow);
  padding: 14px;
  text-align: left;
}
.pick-band-name { font-family: var(--font-display); font-size: 16px; color: var(--heading-text); }
.pick-band-genre {
  display: inline-block;
  font-size: 10px;
  font-weight: bold;
  text-transform: uppercase;
  color: #fff;
  background: var(--orange);
  border-radius: 10px;
  padding: 2px 8px;
  margin: 4px 0 8px;
}
.pick-band-blurb { font-size: 12px; color: var(--text-secondary); margin: 0 0 8px; font-style: italic; }
.pick-band-members { list-style: none; margin: 0 0 10px; padding: 0; font-size: 12px; }
.pick-band-members li { padding: 2px 0; border-bottom: 1px dashed var(--dash-border); }
.pick-band-members li:last-child { border-bottom: none; }
.pick-band-btn { width: 100%; font-size: 13px; }

/* ---------- Inbox ---------- */
/* v19.37: real two-pane mail layout — list on the left, the selected
   message's full detail on the right (see index.html's .inbox-mail-layout
   for the structure, renderInbox()/initInbox() in app.js for the JS).
   Desktop-first on purpose: both panes are simply always visible side by
   side above the mobile breakpoint below, no JS involved at all. Below
   that breakpoint the layout collapses to one column and only one pane
   shows at a time — the list by default, the detail pane once a message
   is tapped (via the .inbox-mail-layout.inbox-detail-open toggle) — the
   same show-one-view-at-a-time pattern the Band screen's grid/detail
   already uses on mobile, so a returning player already knows the shape. */
.inbox-mail-layout { display: flex; gap: 16px; align-items: flex-start; }
.inbox-list-pane { width: 300px; flex-shrink: 0; }
.inbox-list-pane .panel-body { max-height: 65vh; overflow-y: auto; }
.inbox-detail-pane { flex: 1; min-width: 0; }
.inbox-detail-pane .panel-header { display: flex; align-items: center; gap: 8px; }
/* v19.38: Josh — "on mobile, make the 'back to inbox' button a little
   more clear, different color perhaps." Was plain white text sitting
   directly on the same red panel-header gradient as everything else
   around it, easy to miss as a tappable control. Now a real gold pill
   badge (the game's other primary-action color, distinct from the header
   itself) with dark text, so it reads as a button rather than a label. */
.inbox-mobile-back-btn {
  display: none;
  background: var(--gold-bg);
  border: 2px solid var(--panel-border);
  border-radius: 20px;
  color: var(--text-dark);
  font-family: var(--font-display);
  font-size: 12px;
  cursor: pointer;
  padding: 4px 12px;
  text-shadow: none;
  box-shadow: 1px 1px 0 #0004;
}
.inbox-mobile-back-btn:active { background: var(--gold-bg-dark); }
.inbox-list { list-style: none; margin: 0; padding: 0; }
.inbox-item {
  border-bottom: 1px dashed var(--dash-border);
  padding: 10px 4px;
  cursor: pointer;
}
.inbox-item:last-child { border-bottom: none; }
.inbox-item.inbox-item-selected { background: #fff3cf; margin: 0 -4px; padding: 10px 8px; border-radius: 4px; }
.inbox-subject { font-weight: bold; font-size: 14px; color: var(--heading-text); }
.inbox-item.unread .inbox-subject { color: var(--red-header); }
.inbox-body {
  margin-top: 8px;
  font-size: 13px;
  background: var(--cream);
  border: 1px dashed var(--panel-border);
  border-radius: 4px;
  padding: 8px 10px;
}
.inbox-song-block { padding: 4px 0; border-bottom: 1px dashed var(--dash-border); }
.inbox-song-block:last-child { border-bottom: none; }

@media (max-width: 640px) {
  .inbox-mail-layout { display: block; }
  .inbox-list-pane { width: 100%; }
  .inbox-list-pane .panel-body { max-height: none; overflow-y: visible; }
  .inbox-detail-pane { display: none; margin-top: 0; }
  .inbox-mail-layout.inbox-detail-open .inbox-list-pane { display: none; }
  .inbox-mail-layout.inbox-detail-open .inbox-detail-pane { display: block; }
  .inbox-mobile-back-btn { display: inline-block; }
}

/* ---------- Fan timeline (Dashboard) ---------- */
.fan-count-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.fan-count-number { font-family: var(--font-display); font-size: 30px; color: var(--heading-text); }
.fan-count-suffix { font-size: 13px; color: var(--text-secondary); }
.fan-rank-badge.clickable-stat { cursor: pointer; }
.fan-rank-badge.clickable-stat:hover { filter: brightness(1.15); }
.fan-rank-badge {
  margin-left: auto;
  background: var(--purple);
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  padding: 4px 10px;
  border-radius: 12px;
  text-transform: uppercase;
}
.fan-timeline-track {
  position: relative;
  height: 14px;
  background: #efe0b8;
  border: 2px solid var(--panel-border);
  border-radius: 10px;
  overflow: visible;
}
.fan-timeline-fill {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  border-radius: 8px;
  background: linear-gradient(90deg, #ffd166, var(--orange), var(--red-header));
  transition: width 0.3s ease;
}
.fan-timeline-marker {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  font-size: 18px;
  filter: drop-shadow(1px 1px 0 rgba(0,0,0,0.4));
  transition: left 0.3s ease;
}
.fan-timeline-ticks {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-tertiary);
  margin-top: 4px;
}
.fan-timeline-note { font-size: 11px; color: var(--text-tertiary); font-style: italic; margin: 8px 0 0; }
.streak-box {
  margin-top: 8px;
  border-radius: 6px;
  padding: 5px 8px;
  background: var(--cream);
}
/* v16.13: was a boxed multi-line callout (title + explainer paragraph +
   pip row + payout line, each its own block) — Josh: "takes up too much
   room." Collapsed to one compact line: title, pips, next-payout, all
   inline, wrapping only on very narrow screens. The old explainer
   sentence is dropped (the pips are self-explanatory once you've seen
   one), the dashed border is dropped so it reads as a status line, not
   a callout box. */
.streak-box-compact {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 11px;
}
/* v19.3 briefly gave the streak box its own lead panel with a bigger
   .streak-box-lead variant; v19.4 recombined it back into YOUR JOURNEY
   per Josh ("combine daily streak and fan timeline like we originally
   had"), so that variant is gone again — back to just the one compact
   class from v16.13. */
.streak-title { font-size: 12px; color: var(--heading-text); font-weight: bold; white-space: nowrap; }
.streak-pips { display: flex; gap: 2px; }
.streak-pip { font-size: 11px; opacity: 0.25; filter: grayscale(1); }
.streak-pip.filled { opacity: 1; filter: none; }
.streak-next { font-size: 11px; color: var(--money-positive); font-weight: bold; }
/* v19.19: streak-granted free action (Josh's idea — "Streak should give
   you a one free action for that day") */
.streak-free-action {
  display: inline-block;
  font-size: 11px;
  font-weight: bold;
  color: #6a4a00;
  background: #ffd76a;
  border-radius: 6px;
  padding: 3px 8px;
  margin-top: 4px;
}
.day-today { color: var(--money-positive); font-weight: bold; }

/* ---------- Written songs (Dashboard) ---------- */
.written-list { list-style: none; margin: 0; padding: 0; }
.written-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 13px;
}
.written-list li:last-child { border-bottom: none; }
.written-item-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.record-btn, .release-btn { font-size: 12px; padding: 6px 10px; flex-shrink: 0; }
.btn-discard {
  background: #fff;
  border: 2px solid var(--red-header);
  color: var(--red-header);
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 1;
  width: 30px;
  height: 30px;
  border-radius: 6px;
  cursor: pointer;
  flex-shrink: 0;
}
.btn-discard:hover { background: var(--red-header); color: #fff; }
.btn-discard:active { transform: translateY(1px); }

/* ---------- Band catalog & sales ---------- */
.band-catalog-list { list-style: none; margin: 0; padding: 0; }
.band-catalog-item { padding: 8px 0; border-bottom: 1px dashed var(--dash-border); }
.band-catalog-item:last-child { border-bottom: none; }
.band-catalog-title { font-weight: bold; font-size: 13px; margin-bottom: 3px; }
.band-catalog-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 12px;
  color: var(--text-secondary);
  font-family: var(--font-mono);
}
.act-meta-list { list-style: none; margin: 10px 0 0; padding: 0; font-size: 13px; }
.act-meta-list li { padding: 3px 0; border-bottom: 1px dashed var(--dash-border); }
.act-meta-list li:last-child { border-bottom: none; }
.act-meta-list .label { color: var(--text-secondary); }
.act-meta-list .value { font-weight: bold; float: right; }

/* ---------- Advertise campaign modal ---------- */
/* Wider minimum column (was 160px) so a 4-card grid in a 620px modal lands
   on 2-per-row instead of 3-per-row-plus-one-lonely-card — fewer wrapped
   lines per card, and a much less awkward last row. */
.ad-campaign-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px; margin-top: 10px; }
.ad-campaign-card {
  background: #fffdf5;
  border: 2px solid var(--panel-border);
  border-radius: 8px;
  padding: 12px;
  text-align: left;
  display: flex;
  flex-direction: column;
}
.ad-campaign-card h4 { margin: 0 22px 4px 0; font-family: var(--font-display); font-size: 14px; color: var(--heading-text); }
/* A card's description can run from one line ("the reliable default") to
   a full paragraph (a Blitz-tier option explaining the real tradeoff) —
   rather than letting the long ones balloon the whole card (and every
   other card in its row, if they're height-matched), every card's
   description sits in a fixed-height, internally-scrolling box. Every
   card in the grid ends up the same height either way; text that fits
   just doesn't scroll. */
.ad-campaign-card p {
  font-size: 11px;
  color: var(--text-secondary);
  margin: 0 0 8px;
  height: 98px;
  overflow-y: auto;
  padding-right: 6px;
  scrollbar-width: thin;
  scrollbar-color: var(--gold-bg-dark) #f0e6c8;
  /* Text that runs past the box fades out at the bottom edge instead of
     just getting hard-clipped — a visible "there's more, scroll for it"
     cue that works whether or not anyone notices the scrollbar itself.
     Harmless no-op when the text is short enough to fit: the fade lands
     on empty space below the last line. */
  -webkit-mask-image: linear-gradient(to bottom, black 82%, transparent 100%);
  mask-image: linear-gradient(to bottom, black 82%, transparent 100%);
}
.ad-campaign-card p::-webkit-scrollbar { width: 7px; }
.ad-campaign-card p::-webkit-scrollbar-track { background: #f0e6c8; border-radius: 3px; }
.ad-campaign-card p::-webkit-scrollbar-thumb { background: var(--gold-bg-dark); border-radius: 3px; }
/* v19.22: quality-vs-viral tradeoff meter on each recording-method card */
.record-method-stats { font-size: 12px; color: var(--text-secondary); margin: 4px 0 8px; }
.record-method-stats > div { display: flex; justify-content: space-between; gap: 6px; }
.record-method-stat-label { font-weight: bold; }
.ad-campaign-card .ad-cost { font-family: var(--font-mono); font-size: 13px; color: var(--money-positive); margin-bottom: 8px; }
.ad-campaign-card button { width: 100%; font-size: 12px; padding: 8px; margin-top: auto; }
/* v19.30: the Train modal's plain-language value comparison — Josh wanted
   someone to look at Masterclass's $400 price tag and immediately think
   "that's worth it" without doing any math themselves. Not the ordinary
   dark flavor-text color — a little louder, so it reads as the actual
   answer to "why would I pick this one," not another description line. */
.train-value-note {
  font-size: 12px;
  color: #5c3d05;
  background: rgba(240,183,58,0.18);
  border-left: 3px solid #c98a12;
  border-radius: 4px;
  padding: 6px 8px;
  margin: 2px 0 8px;
}
.train-value-baseline {
  color: var(--text-secondary);
  background: rgba(0,0,0,0.06);
  border-left-color: #9a8a5a;
  font-style: italic;
}

/* ---------- Bank ---------- */
.bank-warning {
  background: #ffe2df;
  border: 2px solid var(--red-header);
  color: #7a1a10;
  font-size: 12px;
  padding: 8px 10px;
  border-radius: 6px;
  margin: 0 0 10px;
}
.bank-input-row { display: flex; gap: 8px; align-items: center; }
.bank-input-row input {
  flex: 1;
  padding: 8px 10px;
  border: 2px solid var(--panel-border);
  border-radius: 4px;
  font-family: var(--font-mono);
}
.bank-input-row button { flex-shrink: 0; }

/* v19.6: "something broke, tell Josh" — same pill look as the online
   counter beside it, kept small/icon-led so it doesn't compete with the
   nav-tab row above it for attention on mobile. */
.topnav-feedback-btn {
  font-family: var(--font-mono);
  font-size: 12px;
  color: #ffe8c0;
  background: rgba(0,0,0,0.25);
  padding: 3px 10px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  white-space: nowrap;
}
.topnav-feedback-btn:hover { background: rgba(0,0,0,0.4); }

/* ---------- Top nav: online counter ---------- */
.topnav-online-wrap { position: relative; }
.topnav-online {
  font-family: var(--font-mono);
  font-size: 12px;
  color: #d8ffe0;
  background: rgba(0,0,0,0.25);
  padding: 3px 10px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
}
.topnav-online:hover { background: rgba(0,0,0,0.4); }

/* v16.6: Josh — "when you click where it shows how many are playing, drop
   down the players names that are on." */
.online-players-dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 6px;
  min-width: 180px;
  max-width: 260px;
  max-height: 260px;
  overflow-y: auto;
  background: var(--cream);
  border: 2px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 3px 4px 0 var(--shadow);
  z-index: 600;
  padding: 8px 0;
}
.online-players-dropdown-title {
  font-family: var(--font-body);
  font-weight: bold;
  font-size: 11px;
  text-transform: uppercase;
  color: var(--text-secondary);
  padding: 0 12px 6px;
  border-bottom: 1px solid var(--panel-border);
  margin-bottom: 4px;
}
.online-players-list { list-style: none; margin: 0; padding: 0; }
.online-players-list li {
  padding: 5px 12px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--text-dark);
}
.online-players-list li:before { content: "\1F3B8\0020"; }

/* ---------- No-band onboarding banner ---------- */
.no-band-banner { border-color: var(--orange); box-shadow: 3px 4px 0 var(--shadow), 0 0 0 3px var(--orange-bright) inset; }
.no-band-banner-body { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.no-band-icon { font-size: 34px; flex-shrink: 0; }
.no-band-text { flex: 1 1 220px; }
.no-band-text h3 { margin: 0 0 4px; font-family: var(--font-display); font-size: 15px; color: var(--heading-text); }
.no-band-text p { margin: 0; font-size: 12px; color: var(--text-secondary); }

/* ---------- v19.19: Getting Started checklist (Holden's idea) ---------- */
.checklist-dismiss-btn {
  float: right;
  background: none;
  border: none;
  color: #fff;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.85;
}
.checklist-dismiss-btn:hover { opacity: 1; }
.getting-started-list { list-style: none; margin: 0; padding: 0; }
.getting-started-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  font-size: 13px;
  cursor: pointer;
  border-radius: 6px;
}
.getting-started-item:hover { background: rgba(0,0,0,0.06); }
.getting-started-check { font-size: 14px; opacity: 0.6; width: 18px; text-align: center; flex-shrink: 0; }
.getting-started-done { opacity: 0.55; cursor: default; }
.getting-started-done:hover { background: none; }
.getting-started-done .getting-started-check { color: var(--money-positive); opacity: 1; }
.getting-started-done .getting-started-label { text-decoration: line-through; }

/* ---------- Written songs: demo potential ---------- */
.written-list li.written-item { display: block; padding: 10px 0; }
.written-item-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.song-potential {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
  font-size: 11px;
  color: var(--text-secondary);
}
.song-potential .gut-feeling em { color: var(--heading-text); font-style: normal; font-weight: bold; }
.song-potential .est-quality { font-family: var(--font-mono); }
.focus-group-btn { font-size: 11px; padding: 4px 10px; }
.song-opinions { margin-top: 6px; }
.song-opinion {
  font-size: 11px;
  font-style: italic;
  color: #4a3a20;
  background: var(--cream);
  border-left: 3px solid var(--orange);
  padding: 3px 8px;
  margin-top: 3px;
}
.song-opinion em { font-style: normal; color: var(--text-tertiary); }

/* ---------- Band catalog: reach + profit breakdown ---------- */
.song-status-badge {
  font-size: 10px;
  font-weight: normal;
  color: var(--purple);
  background: #efe0f5;
  border-radius: 10px;
  padding: 1px 8px;
  margin-left: 6px;
  text-transform: uppercase;
}
.reach-row { display: flex; align-items: center; gap: 8px; margin: 4px 0 6px; }
.reach-label { font-size: 11px; color: var(--text-secondary); width: 150px; flex-shrink: 0; }
.reach-bar-outer {
  flex: 1;
  height: 8px;
  background: #efe0b8;
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  overflow: hidden;
}
.reach-bar-inner { height: 100%; background: linear-gradient(90deg, #ffd166, var(--orange), var(--red-header)); }
.profit-pos { color: var(--money-positive); font-weight: bold; }
.profit-neg { color: var(--red-header); font-weight: bold; }

/* ---------- Mail-style decision modal (reused for the first-decision
   email and any future in-world "you've got mail" moment) ---------- */
.mail-window {
  background: var(--panel-bg);
  border: 2px solid #444;
  border-radius: 4px;
  box-shadow: 4px 6px 0 var(--shadow);
  max-width: 480px;
  width: 92vw;
  /* v16.1: a long email body (the welcome email's action/time paragraph,
     a multi-song Welcome Back summary) plus a short phone viewport used
     to have no way to scroll — the window would just get clipped by the
     fixed overlay, hiding the close/decision button below the fold. Now
     the whole card scrolls together once it's taller than the viewport. */
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.mail-titlebar {
  background: linear-gradient(90deg, var(--purple), var(--purple-dark));
  color: #fff;
  font-family: var(--font-display);
  font-size: 12px;
  padding: 6px 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0;
}
.mail-close { cursor: pointer; padding: 0 4px; }
.mail-meta { padding: 10px 14px 0; font-size: 12px; color: #4a3a20; flex-shrink: 0; }
.mail-body { padding: 10px 14px; font-size: 13px; line-height: 1.5; color: #2a1a00; text-align: left; overflow-y: auto; min-height: 0; }
.mail-body p { margin: 0 0 8px; }
.mail-actions { padding: 0 14px 14px; text-align: right; flex-shrink: 0; }

/* v19.34: Josh — "make the email a little smaller on screen after band
   creation, like it used to be. Show a little of the dashbaord around
   edges." Reverses the v16.9 full-screen treatment above (kept as
   history in this comment) back to a bounded window, now dressed up as
   an old CRT monitor per Josh's "pops up on a old crt monitor" ask. */
#welcomeEmailModal .mail-window {
  max-width: 520px;
}
#welcomeEmailModal .mail-titlebar { font-size: 15px; padding: 14px 18px; }
#welcomeEmailModal .mail-meta { padding: 14px 20px 0; font-size: 13px; }
#welcomeEmailModal .mail-body {
  padding: 14px 20px;
  font-size: 14px;
  line-height: 1.55;
}
#welcomeEmailModal .mail-actions { padding: 14px 20px 20px; text-align: center; }
#welcomeEmailModal .mail-actions .btn-primary { font-size: 15px; padding: 12px 28px; }
/* The CRT bezel: a thick dark curved frame plus a faint scanline overlay.
   Kept subtle enough that the actual email is still easy to read. */
.welcome-crt-frame {
  position: relative;
  border: 10px solid #1a1a1a !important;
  border-radius: 22px !important;
  box-shadow:
    0 0 0 3px #3a3a3a,
    0 0 24px rgba(0,0,0,0.6),
    inset 0 0 40px rgba(0,0,0,0.35) !important;
  overflow: hidden;
}
.welcome-crt-scanlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0,0,0,0.08) 0px,
    rgba(0,0,0,0.08) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
  z-index: 5;
}
@media (max-width: 640px) {
  .welcome-crt-frame { border-width: 6px !important; border-radius: 14px !important; }
}

/* ---------- Dial-up intro (v19.34) ----------
   Josh: "a little retro intro of modem and dial up going on." A brief,
   skippable green-on-black terminal screen that plays once, right before
   the welcome email — see playDialUpIntro() in app.js. */
.dialup-overlay {
  position: fixed;
  inset: 0;
  background: #000;
  z-index: 21000;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.dialup-screen {
  width: min(560px, 90vw);
  background: #041604;
  border: 10px solid #1a1a1a;
  border-radius: 22px;
  box-shadow: 0 0 0 3px #3a3a3a, 0 0 40px rgba(0, 255, 100, 0.15), inset 0 0 40px rgba(0,0,0,0.5);
  padding: 24px 22px;
  min-height: 220px;
}
.dialup-text {
  font-family: var(--font-mono);
  color: #4dff6b;
  font-size: 14px;
  line-height: 1.7;
  text-shadow: 0 0 6px rgba(77,255,107,0.65);
  white-space: pre-wrap;
  margin: 0;
}
.dialup-text::after {
  content: '\2588';
  animation: dialupBlink 0.9s steps(1) infinite;
}
@keyframes dialupBlink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}
.dialup-skip {
  position: absolute;
  bottom: 24px;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: #7a8a7a;
  margin: 0;
}
@media (max-width: 640px) {
  .dialup-screen { padding: 16px 14px; min-height: 180px; }
  .dialup-text { font-size: 12px; }
}

footer.game-footer {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--text-secondary);
  padding: 10px;
}

/* Studio panel (Manager tab) */
.studio-buy-desc {
  font-size: 13px;
  color: #4a3a20;
  margin-bottom: 8px;
  line-height: 1.4;
}
.studio-benefits {
  margin: 0 0 12px;
  padding-left: 20px;
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.6;
}
.studio-buy-btn {
  width: 100%;
}
.studio-buy-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.studio-buy-locked {
  text-align: center;
  font-size: 11px;
  color: #9a3a20;
  margin-top: 6px;
}
.studio-owned {
  background: #eafbe0;
  border: 2px solid #4a8a2a;
  border-radius: 6px;
  padding: 10px 12px;
}
.studio-owned-name {
  font-family: var(--font-display);
  font-size: 15px;
  color: #2a6a1a;
  margin-bottom: 4px;
}
.studio-owned-sub {
  font-size: 12px;
  color: #3a5a2a;
  line-height: 1.4;
}

/* Art Market (v16.16) — reuses .studio-owned/.studio-buy-*/.roster-list/
   .workplace-signed-row for everything except the browse grid itself,
   which needs its own layout (a business's listings as a wrapping row of
   thumbnail cards rather than a plain list). */
.art-business-group {
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  padding: 10px;
  margin-bottom: 12px;
  background: #fffdf5;
}
.art-business-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 14px;
  margin-bottom: 8px;
}
.art-listing-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.art-listing-card {
  width: 108px;
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  padding: 6px;
  background: #fff;
  text-align: center;
}
.art-listing-card .cover-art-thumb {
  display: grid;
  width: 96px !important;
  height: 96px !important;
  margin: 0 auto 4px;
}
.art-listing-title {
  font-size: 11px;
  font-weight: bold;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.art-listing-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  margin-top: 4px;
  font-size: 11px;
}
.cover-art-listing-fields {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 10px;
}
.cover-art-listing-fields input {
  width: 100%;
}

/* Finance tab */
.finance-entity-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* v19.37: Josh — "from chart to band profile, it's rough. clean it up."
   Real bug, not just polish: .finance-entity-list's <li> children (the
   label/value span pairs used by the band profile's Songs list, the
   manager profile's band list, and the workplace profile's upgrade list
   — all three reuse this exact same markup) had NO layout rule of their
   own at all, only the unused .finance-entity-row class actually had
   spacing. Two inline spans with nothing separating them just ran
   together on one line ("Diddly Squat714,231 lifetime sales"). Same
   flex-column shape as .roster-list li above, so label/value stack onto
   their own lines with real spacing and a dashed divider between rows,
   consistent with every other profile-style list in the game. */
.finance-entity-list li {
  padding: 6px 0;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.finance-entity-list li:last-child { border-bottom: none; }
.finance-entity-list li .label { font-weight: bold; }
.finance-entity-list li .value { color: var(--text-secondary); }
.finance-entity-row {
  padding: 8px 0;
  border-bottom: 1px dashed var(--dash-border);
}
.finance-entity-row:last-child { border-bottom: none; }
.finance-entity-title {
  font-weight: bold;
  font-size: 13px;
  margin-bottom: 3px;
}
.finance-entity-sub {
  font-weight: normal;
  font-size: 11px;
  color: var(--text-tertiary);
  margin-left: 6px;
}
.finance-entity-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 12px;
  color: var(--text-secondary);
  font-family: var(--font-mono);
}
.finance-empty {
  font-size: 13px;
  color: var(--text-tertiary);
  font-style: italic;
  list-style: none;
}

/* ---------- Band member traits ---------- */
/* v19.34 real bug fix — Josh: "the trait (far left) is way too big and the
   boxes look disorganized." Root cause: on the Train screen this row lives
   INSIDE #skillCards, a CSS grid (.panel-grid) with no align-items set, so
   the default grid stretch behavior was blowing this tiny trait-chip row
   up to the full height of a skill card in that same grid row — a mostly-
   empty giant box, not a rendering choice. grid-column spans it across
   every column as its own compact banner row instead, and align-self stops
   it from stretching vertically. Harmless no-op everywhere memberTraitsHtml
   is used OUTSIDE a grid (e.g. the hire-card lists) since those properties
   only do anything inside a grid container. */
.member-traits-row { display: flex; flex-wrap: wrap; gap: 5px; margin: 6px 0 10px; grid-column: 1 / -1; align-self: start; }
.trait-chip {
  display: inline-block;
  font-size: 11px;
  background: #fff1cf;
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  padding: 2px 8px;
  color: #4a3a20;
  cursor: default;
}
.skill-diminish-note {
  display: block;
  font-size: 11px;
  color: var(--red-header);
  font-style: italic;
  margin: 4px 0;
}

/* ---------- Milestones (Dashboard strip) ---------- */
.milestone-strip { display: flex; flex-wrap: wrap; gap: 8px; }
.milestone-chip {
  display: inline-block;
  font-size: 12px;
  background: linear-gradient(180deg, #fff1cf, #ffe0a3);
  border: 2px solid var(--panel-border);
  border-radius: 8px;
  padding: 5px 10px;
  color: #4a3a20;
  font-weight: bold;
}
.milestone-empty { font-size: 12px; color: var(--text-tertiary); font-style: italic; }

/* ---------- Dashboard "at a glance" ---------- */
.dash-quickstats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}
.dash-quickstat {
  background: var(--cream);
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  padding: 8px 10px;
  text-align: center;
}
.dash-quickstat-label {
  display: block;
  font-size: 11px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.dash-quickstat-value {
  display: block;
  font-family: var(--font-mono);
  font-size: 18px;
  color: var(--heading-text);
  margin-top: 2px;
}
.dash-shortcut-row { display: flex; gap: 10px; flex-wrap: wrap; }
.dash-shortcut-row button { flex: 1; min-width: 140px; }

/* v19.21: At a Glance redesign — four compact widgets replacing the old
   Cash/Rating/Pipeline row + Latest Singles list. */
.glance-widget { margin-bottom: 14px; }
.glance-widget:last-child { margin-bottom: 0; }
.glance-widget-title {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.03em;
  color: var(--heading-text);
  margin: 0 0 6px;
}
.glance-move-btn {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--cream);
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--heading-text);
  cursor: pointer;
}
.glance-move-btn:hover { background: #fff2cf; }
.glance-money-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13px;
  font-family: var(--font-mono);
}
.glance-money-in { color: var(--money-positive); }
.glance-money-out { color: #d9451e; }
.glance-waiting-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.glance-waiting-btn {
  display: block;
  width: 100%;
  text-align: left;
  background: rgba(217, 69, 30, 0.08);
  border: 1px solid rgba(217, 69, 30, 0.3);
  border-radius: 6px;
  padding: 7px 10px;
  font-size: 12px;
  color: var(--heading-text);
  cursor: pointer;
}
.glance-waiting-btn:hover { background: rgba(217, 69, 30, 0.16); }
/* v19.31: "This Week's Top Cover Art" — the 4th At a Glance widget,
   replacing the old "Waiting On You" list. */
.glance-top-cover-row { display: flex; align-items: center; gap: 10px; }
.glance-top-cover-info { min-width: 0; flex: 1; }
.glance-top-cover-song {
  font-size: 13px;
  font-weight: bold;
  color: var(--heading-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.glance-top-cover-credit { font-size: 11px; color: var(--text-secondary); margin: 2px 0; }
.glance-top-cover-likes { font-size: 12px; font-weight: bold; color: #c98a12; }

/* ---------- Magazine tab (v19.33) ---------- */
/* v19.37: Josh — "on the magazine tab, give it a unique color, not the
   yellow bg, but keep it with color theme and on brand." First pass used
   purple (the game's other brand color). v19.38: Josh — "give the
   magazine tab a newspaper look. Like the newspaper grey and black."
   Superseded that with real newsprint styling instead — light grey
   paper background, black masthead-style headers/rules, a serif-ish
   uppercase title with a real double-rule underline like a newspaper
   nameplate. --news-paper/--news-ink/--news-mid are scoped to this
   screen only (and anything nested under it, like the Manager's Corner
   panel below) rather than promoted to :root, since nothing else in the
   game uses a black-and-grey palette. */
#screen-magazine {
  --news-paper: #e4e2da;
  --news-ink: #1c1c1c;
  --news-mid: #6b6a63;
  background: var(--news-paper);
}
#screen-magazine .magazine-title {
  color: var(--news-ink);
  text-shadow: none;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 1px;
  padding-bottom: 8px;
  border-bottom: 4px double var(--news-ink);
}
#screen-magazine .magazine-subtitle {
  text-align: center;
  margin-top: 8px;
  color: var(--news-mid);
  font-size: 13px;
  font-style: italic;
}
#screen-magazine .panel-header {
  background: var(--news-ink);
  color: #f0efe8;
}
#screen-magazine .panel {
  background: #f5f4ee;
  border-color: var(--news-ink);
}
.magazine-changelog-list, .magazine-interview-list { list-style: none; margin: 0; padding: 0; }
.magazine-changelog-item, .magazine-interview-item {
  padding: 10px 0;
  border-bottom: 1px solid rgba(0,0,0,0.15);
}
.magazine-changelog-item:last-child, .magazine-interview-item:last-child { border-bottom: none; }
.magazine-changelog-subject { font-family: var(--font-display); font-size: 15px; color: #1c1c1c; }
.magazine-changelog-date { font-size: 11px; color: var(--text-tertiary); margin: 2px 0 6px; }
.magazine-changelog-body { font-size: 13px; color: #3a3a36; margin: 0; white-space: pre-wrap; }
.magazine-interview-header { font-family: var(--font-display); font-size: 15px; color: #1c1c1c; margin-bottom: 6px; }
.magazine-interview-manager { font-size: 12px; color: var(--text-tertiary); font-weight: normal; }
.magazine-interview-q { font-size: 13px; color: #3a3a36; margin: 4px 0; }

/* v19.38: Josh — "move manager chat to the magazine tab and call it
   something like the managers corner - give it a jazzy look." Moved
   wholesale from the Dashboard (see index.html — same #chatMessageList/
   #chatInput/#chatSendBtn IDs, so none of app.js's chat JS needed to
   change, it just now lives under a different screen). "Jazzy" reads
   here as a small warm accent against the otherwise strict grey/black
   newsprint — a burgundy-and-gold "late night jazz club" trim, like a
   colorful classified-ads column in an otherwise black-and-white paper,
   rather than breaking the newspaper look wholesale. */
.managers-corner-panel .panel-header {
  background: linear-gradient(180deg, #5c1a2e 0%, #3a0f1c 100%) !important;
  color: #f0d9a8 !important;
  font-style: italic;
  letter-spacing: 0.5px;
}
.managers-corner-panel { border-color: #5c1a2e; }
.managers-corner-panel .chat-message-list { border-color: #5c1a2e33; }

/* ---------- Manager Leaderboard (v19.32) ---------- */
.leaderboard-list { list-style: none; margin: 0; padding: 0; max-height: 360px; overflow-y: auto; }
.leaderboard-row {
  display: grid;
  grid-template-columns: 34px 1fr auto;
  grid-template-areas: "rank name score" "rank breakdown breakdown";
  align-items: center;
  gap: 2px 8px;
  padding: 6px 4px;
  border-bottom: 1px solid rgba(0,0,0,0.08);
}
.leaderboard-row:last-child { border-bottom: none; }
.leaderboard-row-me { background: #fff3d0; border-radius: 6px; }
.leaderboard-rank { grid-area: rank; font-family: var(--font-mono); font-weight: bold; color: var(--panel-border); }
.leaderboard-name { grid-area: name; font-size: 13px; font-weight: bold; color: var(--heading-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.leaderboard-score { grid-area: score; font-size: 12px; font-weight: bold; color: #2a6a1a; white-space: nowrap; }
.leaderboard-breakdown { grid-area: breakdown; font-size: 11px; color: var(--text-secondary); }

/* ---------- Studio tab ---------- */
.studio-subheader {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--heading-text);
  margin: 18px 0 8px;
  padding-top: 12px;
  border-top: 1px dashed var(--dash-border);
}
.studio-subheader:first-of-type { border-top: none; padding-top: 0; }

/* ---------- Multi-band grid + detail (Band tab) ---------- */
.band-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}
.band-card {
  position: relative;
  background: var(--panel-bg);
  border: 3px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 3px 4px 0 var(--shadow);
  padding: 16px 14px;
  cursor: pointer;
  transition: transform 0.1s;
}
.band-card:hover { transform: translateY(-2px); }
.band-card-name { font-family: var(--font-display); font-size: 15px; color: var(--heading-text); margin-bottom: 4px; }
.band-card-genre {
  display: inline-block;
  font-size: 10px;
  font-weight: bold;
  text-transform: uppercase;
  color: #fff;
  background: var(--orange);
  border-radius: 10px;
  padding: 2px 8px;
  margin-bottom: 8px;
}
.band-card-members { font-size: 12px; color: var(--text-secondary); }
.band-card-active-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 9px;
  font-weight: bold;
  color: #2a6a1a;
  background: #eafbe0;
  border: 1px solid #4a8a2a;
  border-radius: 8px;
  padding: 2px 6px;
}
.band-card-sign {
  border-style: dashed;
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.band-card-sign .band-card-name { color: var(--orange); }
.band-card-locked { cursor: not-allowed; opacity: 0.65; }
/* v16: World screen cards reuse .band-card's look but aren't clickable —
   they're just a read-only view into another real player's band. */
.world-band-card { cursor: default; }
.world-band-card:hover { transform: none; }
.world-song-list { margin-top: 8px; }
.world-song-list li { font-size: 12px; }

/* v16.1: Dashboard crew chat */
.chat-message-list {
  list-style: none;
  margin: 0 0 10px;
  padding: 8px;
  max-height: 220px;
  overflow-y: auto;
  background: #fffaf0;
  border: 2px solid var(--panel-border);
  border-radius: 6px;
}
.chat-message-list li { padding: 3px 0; font-size: 13px; line-height: 1.4; }
.chat-message-sender { font-weight: bold; color: var(--heading-text); }
.chat-message-time { font-size: 10px; color: var(--text-tertiary); margin-left: 4px; }
.chat-message-empty { font-size: 12px; color: var(--text-tertiary); font-style: italic; }
/* v19.1: report/block controls, shown on hover-equivalent (always visible
   but small/muted) rather than a real hover state, since this needs to
   work on mobile touch same as everywhere else in this game. */
.chat-message-mod-actions { float: right; }
.chat-mod-btn {
  background: none; border: none; cursor: pointer; padding: 0 2px;
  font-size: 12px; color: #b8a878; line-height: 1;
}
.chat-mod-btn:hover { color: var(--heading-text); }
.chat-input-row { display: flex; gap: 8px; }
.chat-input-row input { flex: 1; }
/* v14: bands now work in parallel, so each card needs its own honest
   busy/idle readout — not just which one you happen to be viewing. */
.band-card-status-badge {
  display: inline-block;
  margin-top: 8px;
  font-size: 10px;
  font-weight: bold;
  border-radius: 8px;
  padding: 2px 8px;
}
.band-card-status-idle { color: #2a6a1a; background: #eafbe0; border: 1px solid #4a8a2a; }
.band-card-status-busy { color: var(--panel-border); background: #fff2d0; border: 1px solid var(--gold-bg-dark); }

/* ---------- Dashboard: other bands' status ---------- */
.other-bands-activity {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--panel-border);
}
.other-band-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  padding: 4px 0;
}
.other-band-name { color: var(--heading-text); font-weight: bold; }
.other-band-status { font-size: 11px; }
.other-band-idle { color: #2a6a1a; }
.other-band-busy { color: var(--panel-border); }

/* Store Pagination (v19.33, retired v19.37 — replaced by the sectioned
   single-scroll layout above; see .store-category-header). */

/* ---------- Lottery Ticket (v19.32) ---------- */
.lottery-panel .panel-header { background: linear-gradient(180deg, #d19c1a 0%, #a8761a 100%); }
.lottery-controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.lottery-status { font-size: 12px; color: var(--text-secondary); font-style: italic; }

/* ---------- General Store ---------- */
/* v19.37: sectioned single-scroll layout replacing the old numbered
   pagination — see the index.html comment above #storeGrid for why. Each
   category is its own light-touch header + grid, so scrolling further
   IS the "next page," nothing's ever hidden behind a click. */
.store-category { margin-bottom: 20px; }
.store-category:last-child { margin-bottom: 0; }
.store-category-header {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.5px;
  color: var(--heading-text);
  margin: 0 0 8px;
  padding-bottom: 6px;
  border-bottom: 2px dashed var(--dash-border);
}
.store-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
}
.store-item-card {
  background: var(--panel-bg);
  border: 3px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 3px 4px 0 var(--shadow);
  padding: 10px;
  text-align: center;
}
.store-item-card.store-item-owned { background: var(--cream); }
.store-item-emoji { font-size: 24px; margin-bottom: 3px; }
.store-item-name { font-family: var(--font-display); font-size: 12px; color: var(--heading-text); margin-bottom: 5px; }
.store-item-desc { font-size: 11px; color: var(--text-secondary); margin: 0 0 6px; line-height: 1.35; }
.store-item-buff {
  font-size: 11px;
  font-weight: bold;
  color: #2a6a1a;
  background: #eafbe0;
  border: 1px dashed #4a8a2a;
  border-radius: 4px;
  padding: 3px 6px;
  margin-bottom: 8px;
}
.store-item-footer { display: flex; align-items: center; justify-content: space-between; gap: 6px; flex-wrap: wrap; }
.store-item-price { font-family: var(--font-mono); font-size: 14px; color: var(--money-positive); }
.store-item-footer button { flex-shrink: 0; font-size: 11px; padding: 6px 8px; }
/* v19.30: a skill-tier-gated store item, before it's earned — dimmed and
   flagged with a real lock icon + the exact title still needed, so it
   reads as a real future goal, not just an item you can't afford yet. */
.store-item-card.store-item-locked { opacity: 0.62; filter: grayscale(0.3); }
.store-item-locked-note {
  font-size: 11px;
  font-weight: bold;
  color: #7a3a1a;
  background: #ffe9d6;
  border: 1px dashed #c98a3a;
  border-radius: 4px;
  padding: 3px 6px;
  margin-bottom: 8px;
}

/* ---------- First-decision email options ---------- */
.first-decision-btn { font-size: 13px; line-height: 1.4; }

/* ---------- UI pass: bigger, clearer text throughout ----------
   The original small print (11-12px labels, tight line-height) read fine
   on a mockup but was a strain in actual play. Nudging the whole body
   copy up a size and loosening line-height, then re-bumping the specific
   classes that were still stuck at 10-11px, without touching layout or
   the display/mono headline type (that's the retro identity — stays put). */
body { font-size: 15px; line-height: 1.5; }
.panel-body { font-size: 14px; line-height: 1.5; }
.stat-list { font-size: 15px; }
.hire-stat-row { font-size: 12px; }
.hire-instrument, .pick-band-blurb, .band-card-members, .store-item-desc,
.finance-entity-stats, .finance-entity-sub, .fan-timeline-note, .streak-explainer,
.dash-quickstat-label, .roster-instrument, .chart-note, .inbox-body,
.act-meta-list .label, .act-meta-list .value {
  font-size: 13px;
}
.pick-band-members, .ad-campaign-card p, .studio-buy-desc, .studio-benefits,
.studio-owned-sub, .skill-desc, .song-opinion, .est-quality, .gut-feeling {
  font-size: 13px;
  line-height: 1.5;
}
.trait-chip, .milestone-chip { font-size: 12px; }
.ad-campaign-card .ad-cost { font-size: 14px; }
.band-catalog-stats span, .reach-label { font-size: 12px; }
.written-item-top em, .band-catalog-title em { font-size: 12px; }

/* ---------- v13: Studio song pipeline ---------- */
.pipeline-legend {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
  margin: 0 0 14px;
  padding-bottom: 12px;
  border-bottom: 2px dashed var(--dash-border);
}
.pipeline-stage { margin-bottom: 18px; }
.pipeline-stage:last-child { margin-bottom: 0; }
.pipeline-stage-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--heading-text);
  margin-bottom: 8px;
}
/* v19.20: "show in spot 1, 2, or 3 that there is a song there being
   written or recorded..." — Josh */
.pipeline-in-progress {
  background: #fff3d6;
  border: 2px solid #e8b84b;
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 12px;
  color: #6a4a00;
  margin-bottom: 8px;
}
.pipeline-stage-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--purple);
  color: #fff;
  font-size: 12px;
  flex-shrink: 0;
}
.pipeline-stage-sub { font-family: var(--font-body); font-size: 11px; color: var(--text-tertiary); font-weight: normal; }

/* The "Write a New Song" button used to be sized like a normal in-panel
   button and got lost above a long list — Josh: "make write a new song
   bigger, it looks weird in the UI." Made it a full-width, tall, high-
   contrast call to action since it's the entry point to the whole loop. */
.studio-write-btn {
  display: block;
  width: 100%;
  padding: 16px;
  font-family: var(--font-display);
  font-size: 16px;
  letter-spacing: 0.5px;
  color: #fff;
  background: linear-gradient(180deg, var(--orange-bright), var(--orange));
  border: 3px solid var(--panel-border);
  border-radius: 8px;
  box-shadow: 0 3px 0 var(--shadow);
  cursor: pointer;
}
.studio-write-btn:hover { filter: brightness(1.05); }
.studio-write-btn:active { transform: translateY(3px); box-shadow: 0 0 0 rgba(0,0,0,0.3); }
.studio-write-btn:disabled { opacity: 0.5; cursor: default; transform: none; box-shadow: 0 3px 0 var(--shadow); }

/* ---------- v16.6/v16.13: little retro 8-bit studio room (owned Store items) ----------
   Josh (v16.6): "under the write a song option, have a little retro 8bit
   studio where if you buy an item in the store, it shows up in that
   studio."
   Josh (v16.13): "the 8bit retro studio item display isn't what I
   wanted. I want a little recording studio replica made in 8bit retro
   style (like you see the wall, a lamp, the mixing console, and mic)
   and then when you buy an item, it goes displayed on that screen...
   But it at the top of the screen." — replaced the old 3x3 grid of
   emoji chips with an actual drawn room (see renderPixelStudioRoom() /
   PIXEL_STUDIO_SLOTS / PIXEL_ITEM_SPRITES in app.js) built as one
   inline SVG: fixed room fixtures (wall, window, hanging lamp, shelf,
   mixing console, mic on a stand) always visible, plus a small pixel
   sprite per owned Store item appearing in its own fixed spot in the
   scene the moment it's bought. Moved to its own panel at the very top
   of the Studio screen, above Song Pipeline. */
.pixel-studio-caption { font-size: 12px; color: var(--text-secondary); margin: 0 0 8px; text-align: center; }
.pixel-studio-room {
  /* v19.2: Josh — "make the visual studio smaller so you don't have to
     scroll as much." Was stretching to the full panel width (up to
     980px on desktop, still 300-400px+ on mobile), which at its fixed
     320x190 aspect ratio ate a lot of vertical space above the Song
     Pipeline panel. Capped and centered instead of full-bleed.
     v19.4: Josh — "make the studio visual a little bigger though" —
     260px read as too small once he saw it live. Bumped up; still well
     short of the old full-bleed size that caused the scrolling complaint. */
  margin: 0 auto;
  max-width: 340px;
  border: 4px solid var(--purple);
  border-radius: 4px;
  overflow: hidden;
  background: #2a1450;
  box-shadow: inset 0 0 0 4px #20101c;
}
.pixel-studio-svg { display: block; width: 100%; height: auto; }
.pixel-item-sprite { cursor: default; }

/* ---------- v13: risk badges (Write/Record/Release/Train method cards) ---------- */
.ad-campaign-card { position: relative; }
.risk-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.5px;
  padding: 2px 7px;
  border-radius: 4px;
  font-weight: bold;
}
.risk-badge-safe { background: #d9f2dc; color: var(--money-positive); border: 1px solid var(--green-money); }
.risk-badge-moderate { background: #fff0d0; color: var(--orange); border: 1px solid var(--orange); }
.risk-badge-blitz { background: #ffdcd6; color: var(--red-header); border: 1px solid var(--red-header); }

/* ---------- v13: nav-bar activity indicator ---------- */
.topnav-activity {
  font-family: var(--font-mono);
  font-size: 12px;
  color: #d8ffe0;
  background: rgba(0,0,0,0.25);
  padding: 3px 10px;
  border-radius: 10px;
  cursor: pointer;
  white-space: nowrap;
}
.topnav-activity:hover { background: rgba(0,0,0,0.4); }
.topnav-activity.nav-activity-busy { color: #ffe8b0; }

/* ---------- v13: Gigs screen ---------- */
.gig-setlist-picker { list-style: none; margin: 0; padding: 0; max-height: 320px; overflow-y: auto; }
.gig-setlist-item {
  padding: 6px 0;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 13px;
}
.gig-setlist-item:last-child { border-bottom: none; }
.gig-setlist-item label { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.gig-setlist-item input[type="checkbox"] { width: 16px; height: 16px; flex-shrink: 0; }
.gig-setlist-item-capped label { opacity: 0.45; cursor: not-allowed; }
.gig-setlist-count { font-size: 12px; color: var(--text-secondary); font-family: var(--font-mono); margin: 8px 0 0; }
.saved-setlist-list { list-style: none; margin: 0; padding: 0; }
.saved-setlist-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 8px 6px;
  border-bottom: 1px dashed var(--dash-border);
  font-size: 12px;
  cursor: pointer;
  border-radius: 5px;
}
.saved-setlist-item:hover { background: var(--cream); }
.saved-setlist-item.saved-setlist-item-capped { opacity: 0.5; cursor: not-allowed; }
.saved-setlist-item.saved-setlist-item-capped:hover { background: none; }
.saved-setlist-item.saved-setlist-selected { background: #fff1cf; border: 1px solid var(--orange); }
.saved-setlist-item:last-child { border-bottom: none; }
.saved-setlist-main { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.saved-setlist-name { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.saved-setlist-count { color: var(--text-tertiary); font-size: 11px; }
.saved-setlist-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.saved-setlist-actions .btn-secondary { padding: 4px 10px; font-size: 11px; }
/* v15: fan rating on a played setlist — hover the stars for why. */
.setlist-rating { color: var(--orange); letter-spacing: 1px; font-size: 13px; cursor: help; }
.setlist-rating-none { color: #a0906a; font-size: 11px; font-style: italic; }
.gig-venue-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.gig-venue-card { display: flex; flex-direction: column; }
/* v19.20: EARLY/MID/LATE GAME grouping (see GIG_VENUES' `stage` field).
   #gigVenueGrid switches from being the grid itself to just a plain
   vertical stack of stage sections, each of which is its own grid using
   the exact same column layout the cards always had. */
#gigVenueGrid.gig-venue-grid { display: block; }
.gig-stage-header {
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 1px;
  color: var(--heading-text);
  margin: 18px 0 8px;
  opacity: 0.85;
}
.gig-stage-header:first-child { margin-top: 0; }
.gig-stage-group {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px;
}
.gig-venue-meta { font-size: 11px; color: var(--text-secondary); margin-bottom: 6px; }
.gig-venue-vehicle { font-size: 11px; color: var(--panel-border); background: #fff2d0; border-radius: 5px; padding: 3px 6px; margin-bottom: 6px; }
.gig-venue-locked { opacity: 0.6; background: #f2ecd8; }
/* v15: song chart — rival "single" entries get a muted band-name suffix */
.chart-band-name { color: var(--text-tertiary); font-weight: normal; font-size: 11px; }
/* v16.12: "signed to X" links — Band tab, Manager screen, Charts — all
   opening the Studio/Venue Profile modal */
.workplace-profile-link, .chart-signed-link, .chart-band-link, .chart-manager-link {
  color: var(--link-blue);
  text-decoration: underline;
  cursor: pointer;
}
.chart-signed-link { font-size: 11px; margin-left: 4px; }
/* v19.2: manager links sit inside the muted .chart-band-name suffix
   (e.g. "managed by <a>Josh</a>") — inherit that font-size instead of
   the default, so they don't look larger than the text around them. */
.chart-band-name .chart-manager-link { font-size: inherit; }
/* World tab band cards (.band-card-name/.band-card-members) already
   have their own deliberate color/font treatment (purple display font,
   muted meta text) — the generic blue-underline link style would clash,
   so these two spots keep their own look and just gain an underline +
   pointer to read as clickable. */
.band-card-name .chart-band-link, .band-card-members .chart-manager-link {
  color: inherit;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.wp-profile-body .scout-loading, .wp-profile-body .scout-empty { padding: 10px 0 0; }
.gig-venue-locked-note {
  font-size: 12px;
  color: var(--red-header);
  font-weight: bold;
  text-align: center;
  padding: 8px 0 0;
}

/* =========================================================
   v16.1 — mobile pass
   No @media rules existed anywhere in this file before this pass — the
   whole game was effectively desktop-only. This block tightens things up
   for phone-width viewports without touching the desktop layout above
   (everything here only kicks in under the breakpoint). Two things got
   fixed unconditionally, not just under a phone breakpoint, since they
   were real bugs on any small window: .modal-box now has width:92vw so
   an inline max-width override (the ad-campaign modal, the setlist
   builder) can never force horizontal scroll, and .mail-window now
   scrolls its body internally with the titlebar/close button always
   pinned in view (see those rules above).
   ========================================================= */
@media (max-width: 640px) {
  .page-wrap { padding: 14px 10px 48px; }
  .banner { padding: 8px 12px; }
  .banner-logo { font-size: 22px; }
  .banner-meta { font-size: 14px; }

  /* Persistent nav: three rows is fine on a phone as long as nothing in
     any row gets clipped — shrink everything a notch and let it wrap. */
  .topnav { padding: 8px 10px; gap: 6px; }
  .topnav-logo { font-size: 19px; }
  .topnav-tabs { gap: 2px; }
  .topnav-tab { font-size: 11px; padding: 5px 7px; }
  /* v16.8: on a 320px-wide phone (iPhone SE and similar), Idle-status +
     online-count + money together were wider than the row and had nowhere
     to wrap (.topnav-right had no flex-wrap), so the money pill spilled
     off the right edge of the screen and dragged the WHOLE PAGE into
     horizontal scroll on every single screen. flex-wrap lets it drop to a
     second line instead of overflowing. */
  .topnav-right { width: 100%; justify-content: space-between; gap: 6px; flex-wrap: wrap; row-gap: 6px; }
  .topnav-activity, .topnav-online, .topnav-feedback-btn { font-size: 11px; padding: 3px 7px; }
  .topnav-money { font-size: 15px; padding: 3px 8px; }
  /* v19.42: Josh — "Adding it also messed up the nav bar on mobile,
     re organize the nav bar so it is sleek and crisp." The dark-mode
     toggle got added at its full desktop size (15px icon, 5px/9px
     padding) without ever getting folded into this mobile shrink pass —
     next to the 11px/3px-7px pills around it, it stuck out and threw
     the whole row's rhythm off. Sized down to match its neighbors. */
  .dark-mode-toggle-btn { font-size: 13px; padding: 3px 7px; }

  /* Email popups — middle-of-screen. Already width:92vw and internally
     scrolling (see .mail-window above); just tighten the padding/type a
     touch so more of a long email fits without scrolling at all. */
  .mail-titlebar { font-size: 11px; padding: 6px 8px; }
  .mail-meta { padding: 8px 10px 0; font-size: 11px; }
  .mail-body { padding: 8px 10px; font-size: 12.5px; }
  .mail-actions { padding: 0 10px 10px; }
  .mail-actions .btn-primary, .mail-actions .btn-secondary { width: 100%; }
  #firstDecisionOptions .first-decision-btn { display: block; width: 100%; margin-bottom: 6px; }

  .modal-box { padding: 16px 14px; }

  /* Toasts — bottom-RIGHT popups. A 260px box anchored to the right edge
     is easy to miss (or thumb-block) on a phone, and stacked toasts eat a
     lot of a short viewport. Full-width bar centered at the bottom reads
     much better with one hand. */
  .toast-wrap {
    left: 8px;
    right: 8px;
    bottom: 8px;
    align-items: stretch;
  }
  .toast { max-width: none; font-size: 12.5px; }

  /* Grids already collapse to one column on their own (auto-fit/minmax) —
     just tighten the gaps so more fits above the fold. */
  .store-grid, .hire-pool-grid, .pick-band-grid, .band-grid,
  .ad-campaign-grid, .gig-venue-grid { gap: 10px; }
  .store-item-card { padding: 8px; }

  .reach-label { width: 96px; font-size: 10px; }

  /* Finance/Charts/Store sub-tab rows on phones. v16.6: these used to
     shrink down to 11px/5px-8px to dodge a scroll (Finance has 4 of
     them) — that's exactly what made them "lost" on mobile per Josh's
     report. Now they wrap onto a second row instead of shrinking below a
     legible, tappable size; still real spacing so a thumb doesn't
     mis-tap the wrong one. */
  .chart-tab, .store-sub-tab, .inbox-sub-tab, .finance-sub-tab {
    font-size: 12.5px;
    padding: 7px 12px;
  }

  .dash-shortcut-row button { min-width: 0; flex-basis: 45%; }
}

/* ============ v16.7: Studio & Venue signing economy ============ */
.btn-small { padding: 6px 12px; font-size: 12px; box-shadow: 0 2px 0 rgba(0,0,0,0.3); }
.btn-small:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(0,0,0,0.3); }

.workplace-econ-row {
  display: flex;
  gap: 10px;
  margin: 14px 0 10px;
  flex-wrap: wrap;
}
.workplace-econ-stat {
  flex: 1 1 140px;
  background: rgba(0,0,0,0.18);
  border-radius: 8px;
  padding: 8px 12px;
}
.workplace-econ-stat .label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.7; }
.workplace-econ-stat .value { display: block; font-family: var(--font-display); font-size: 16px; margin-top: 2px; }

.workplace-deposit-row { display: flex; gap: 8px; margin-bottom: 14px; }
.workplace-deposit-input {
  flex: 1;
  min-width: 0;
  border-radius: 8px;
  border: 2px solid rgba(0,0,0,0.25);
  padding: 8px 10px;
  font-family: var(--font-body);
  font-size: 14px;
}

.workplace-upgrade-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
.workplace-upgrade-card {
  background: rgba(0,0,0,0.18);
  border-radius: 10px;
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.workplace-upgrade-card.workplace-upgrade-owned { outline: 2px solid var(--green, #4caf7d); }
.workplace-upgrade-emoji { font-size: 22px; }
.workplace-upgrade-name { font-family: var(--font-display); font-size: 13px; }
.workplace-upgrade-desc { font-size: 11px; opacity: 0.8; margin: 0; flex: 1; }
.workplace-upgrade-footer { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-top: 4px; }
.workplace-upgrade-price { font-family: var(--font-display); font-size: 12px; }

.workplace-scout-btn { display: block; width: 100%; margin-bottom: 12px; }

.workplace-signed-list { margin-top: 4px; }
.workplace-signed-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid rgba(0,0,0,0.15);
  font-size: 13px;
}
.workplace-signed-row:last-child { border-bottom: none; }
.workplace-signed-owner { opacity: 0.7; font-size: 11px; }
.workplace-signed-empty { opacity: 0.7; font-size: 13px; padding: 6px 0; }
.workplace-offer-actions { display: flex; gap: 6px; flex-shrink: 0; }

.contract-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: rgba(0,0,0,0.18);
  border-radius: 8px;
  padding: 8px 12px;
  margin-top: 10px;
  font-size: 13px;
}

.scout-agent-list {
  max-height: 260px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0;
}
.scout-agent-card {
  background: rgba(0,0,0,0.18);
  border-radius: 8px;
  padding: 10px 12px;
  cursor: pointer;
  border: 2px solid transparent;
  text-align: left;
}
.scout-agent-card.scout-agent-selected { border-color: var(--orange-bright); }
.scout-agent-name { font-family: var(--font-display); font-size: 14px; }
.scout-agent-genre { font-size: 11px; opacity: 0.75; margin-left: 6px; }
.scout-agent-meta { font-size: 12px; opacity: 0.8; margin-top: 2px; }
.scout-loading, .scout-empty { opacity: 0.8; font-size: 13px; text-align: center; padding: 20px 0; }

/* v19.20: mailbox — the Studio tab's "signed to" box (message/request
   buttons) and the two compose modals (manager message, studio request). */
.studio-signed-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
#managerMessageModal textarea, #studioRequestModal textarea {
  font-family: inherit;
  font-size: 13px;
  border-radius: 8px;
  border: 2px solid rgba(0,0,0,0.2);
  padding: 8px 10px;
  margin-top: 8px;
  resize: vertical;
}
.thread-message-list {
  max-height: 320px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0;
  padding-right: 4px;
}
.thread-message {
  background: rgba(0,0,0,0.15);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 13px;
}
.thread-message-mine { background: rgba(0,0,0,0.28); }
.thread-message-meta { font-size: 11px; opacity: 0.7; margin-bottom: 3px; }
.thread-message-body { white-space: pre-wrap; word-break: break-word; }
#messageThreadModal textarea { font-family: inherit; font-size: 13px; border-radius: 8px; border: 2px solid rgba(0,0,0,0.2); padding: 8px 10px; resize: vertical; }
#studioRequestModal select {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 8px;
  border-radius: 8px;
  border: 2px solid rgba(0,0,0,0.2);
  font-family: inherit;
  font-size: 13px;
}

@media (max-width: 640px) {
  .workplace-upgrade-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .workplace-signed-row, .contract-info-row { flex-direction: column; align-items: flex-start; }

  /* v19.19: "On mobile deposit box for studio too small" — the amount
     input was squeezed to almost nothing sharing one row with two full-
     text buttons on a narrow screen. Stack the input on its own full-width
     row above the buttons instead of fighting them for horizontal space. */
  .workplace-deposit-row { flex-wrap: wrap; }
  .workplace-deposit-input { flex-basis: 100%; }
  .workplace-deposit-row button { flex: 1; }

  /* v16.8: at 140px-wide cards, a price label + "Need Treasury"/"Install"
     button side-by-side had nowhere to go — the button was overflowing
     past its own card into the next column. Stack them and let the button
     take the full card width instead of fighting for horizontal space. */
  .workplace-upgrade-footer { flex-direction: column; align-items: stretch; gap: 6px; }
  .workplace-upgrade-footer .btn-small { width: 100%; white-space: normal; }
  .workplace-upgrade-price { text-align: center; }
}

/* ---------- v16.13: first-run tutorial spotlight overlay ----------
   Josh: "create a tutorial when you start a new account... make it to
   where the UI blocks everything out and highlights things as it
   teaches you what to do." Four dim panels form a rectangular "hole"
   around whatever's being taught (a real button/panel already on the
   page, untouched) instead of a fake screenshot of it — see
   renderTutorialOverlay() in app.js for how the four panels get sized.
   z-index sits BELOW .modal-overlay (20000) on purpose: the moment a
   tutorial step's target opens a real modal (the Write/Train/Release
   method picker, etc.), that modal should sit on top of the dimming and
   be fully usable, not fight the tutorial card for space. */
.tutorial-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  /* The overlay container itself spans the full viewport (inset:0), but
     must NOT intercept clicks over the "hole" — only its four dim panels
     and the card (each opts back in via pointer-events:auto below)
     should ever block a click. Without this, the container's own
     default pointer-events:auto would swallow every click across the
     whole screen regardless of where the dim panels actually are. */
  pointer-events: none;
}
.tutorial-dim {
  position: fixed;
  background: rgba(10, 5, 20, 0.72);
  pointer-events: auto;
}
.tutorial-spotlight-ring {
  position: fixed;
  border: 3px solid var(--orange-bright);
  border-radius: 8px;
  box-shadow: 0 0 0 3px rgba(255, 140, 26, 0.35), 0 0 18px rgba(255, 140, 26, 0.55);
  pointer-events: none;
  animation: tutorialPulse 1.6s ease-in-out infinite;
}
@keyframes tutorialPulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(255, 140, 26, 0.35), 0 0 18px rgba(255, 140, 26, 0.55); }
  50% { box-shadow: 0 0 0 6px rgba(255, 140, 26, 0.2), 0 0 26px rgba(255, 140, 26, 0.75); }
}
/* v19.34 mobile tutorial fix — a real, visible cue for when the
   highlighted target has scrolled out of view (see renderTutorialOverlay()
   in app.js for the show/hide logic). */
.tutorial-scroll-cue {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: 10px;
  background: var(--orange-bright);
  color: #2a1a05;
  font-family: var(--font-mono);
  font-weight: bold;
  font-size: 13px;
  padding: 6px 14px;
  border-radius: 20px;
  box-shadow: 0 2px 6px rgba(0,0,0,0.4);
  z-index: 9002;
  pointer-events: none;
  animation: tutorialCuePulse 1.1s ease-in-out infinite;
}
.tutorial-scroll-cue-down { top: auto; bottom: 10px; }
@keyframes tutorialCuePulse {
  0%, 100% { opacity: 0.85; }
  50% { opacity: 1; transform: translateX(-50%) scale(1.06); }
}
.tutorial-card {
  position: fixed;
  left: 50%;
  width: min(360px, 90vw);
  background: var(--panel-bg);
  border: 3px solid var(--purple-dark);
  border-radius: 10px;
  box-shadow: 4px 6px 0 var(--shadow);
  padding: 16px 18px;
  z-index: 9001;
  pointer-events: auto;
  /* v19.45: safety net for a long step body (e.g. Train) on a short phone
     viewport — the card scrolls internally instead of ever being cut off
     top or bottom. */
  max-height: calc(100vh - 20px);
  overflow-y: auto;
}
.tutorial-card-step {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--orange);
  font-weight: bold;
  letter-spacing: 0.5px;
  margin-bottom: 2px;
}
.tutorial-card-title {
  font-family: var(--font-display);
  font-size: 16px;
  color: var(--heading-text);
  margin: 0 0 8px;
}
.tutorial-card-body { font-size: 13px; color: var(--text-dark); line-height: 1.45; margin: 0; }
.tutorial-card-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}
.tutorial-card-actions .btn-secondary { font-size: 11px; padding: 6px 10px; }
.tutorial-card-actions .btn-primary { font-size: 13px; padding: 8px 16px; }

/* ---------- Dark mode (v19.39, readability fix v19.42) ---------- */
/* Josh: "Make a dark mode toggle." Applies via a `data-theme="dark"`
   attribute on <html> (see index.html's early inline script + app.js's
   setTheme()/initThemeToggle()), overriding just the handful of tokens
   that actually carry the page's light/dark weight: --page-bg (the body
   background), --panel-bg (every card), --text-dark (body/panel text),
   the four v19.37-promoted tokens (--cream/--text-secondary/
   --text-tertiary/--dash-border), --panel-border, and — new in v19.42 —
   --heading-text/--money-positive (see their comment up in :root).
   Deliberately NOT a full re-skin — a number of components still use a
   raw hardcoded light color for a highlight/badge background here and
   there (e.g. a couple of `#fff3d0`-style tints) rather than one of
   these tokens, and those won't flip with the toggle. That's a known,
   accepted partial-coverage gap given this project's no-git-repo/no-undo
   posture around broad sweeping changes across a 3000+ line stylesheet —
   the core reading experience (page background, every panel, all body
   text, every heading/name/label, money figures) goes dark and stays
   readable; a handful of small decorative surfaces don't flip yet.
   --gold-bg is deliberately untouched here even though it looks similar
   to the old light --page-bg, because --gold-bg does double duty as a
   bright accent color (nav text, button fills, badges) on top of dark
   headers elsewhere in the game — redefining it for dark mode would have
   repainted every one of those accents along with the background.

   v19.42: Josh caught the real bug in the first pass — "dark mode looks
   like poop and it's hard to read anything." --purple-dark and
   --green-money hadn't been touched, and they're used as TEXT color in
   38 and 11 places respectively (headings, band/song/manager names,
   money figures) — all of it assuming a white panel underneath. Once
   --panel-bg went dark those same colors were nearly invisible. Fixed
   by decoupling those specific text roles into --heading-text/
   --money-positive (see :root) and brightening just those two here. */
:root[data-theme="dark"] {
  --page-bg: #1a140a;
  --panel-bg: #2b2216;
  --text-dark: #f0e6cc;
  --cream: #362c1a;
  --text-secondary: #cbb98a;
  --text-tertiary: #a89968;
  --dash-border: #4a3f28;
  --panel-border: #b8863c;
  --heading-text: #cbadf0;
  --money-positive: #58d37e;
}
/* v19.42: Josh — the toggle "looked weird" sitting in the nav as a
   bordered gold circle while every other nav-right pill (Feedback,
   Idle/Activity, online count, money) shares the same plain
   rgba(0,0,0,0.25) pill style. Matched that family instead of standing
   out as its own distinct control — same background/radius as
   .topnav-feedback-btn, sized for one emoji glyph instead of a text
   label. Lives outside the [data-theme="dark"] block since it should
   look identical in both themes, not just dark. */
.dark-mode-toggle-btn {
  font-family: var(--font-mono);
  font-size: 15px;
  line-height: 1;
  color: #ffe8c0;
  background: rgba(0,0,0,0.25);
  border: none;
  border-radius: 10px;
  padding: 3px 9px;
  cursor: pointer;
}
.dark-mode-toggle-btn:hover { background: rgba(0,0,0,0.4); }
.dark-mode-toggle-btn:active { transform: translateY(1px); }
