* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  background: var(--surface-tint);
  color: var(--ink);
  font-family: var(--font-serif);
  font-size: var(--text-base);
  line-height: 1.5;
}

#app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-4);
  gap: var(--space-4);
}


#location-name {
  font-family: var(--font-serif);
  font-size: var(--text-2xl);
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.015em;
  line-height: 1.1;
  flex: 1;
}

#audio-indicator {
  font-size: var(--text-base);
  color: var(--ink-disabled);
  transition: color 200ms ease;
}
#audio-indicator.audio-idle { opacity: 0.3; }
#audio-indicator.audio-playing { color: var(--accent); opacity: 1; animation: pulse 1.2s ease-in-out infinite; }

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}



#start-overlay {
  position: fixed;
  inset: 0;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  z-index: 10;
}

#start-btn {
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  padding: var(--space-3) var(--space-6);
  background: var(--accent);
  color: var(--accent-ink);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
}
#start-btn:hover { opacity: 0.92; }

#start-overlay .hint {
  font-family: var(--font-sans);
  color: var(--ink-faint);
  font-size: var(--text-sm);
  max-width: 18rem;
  text-align: center;
}

#start-overlay.hidden { display: none; }

/* Phone mode (?phone=1): larger tap targets */
body.phone .exit-row { font-size: 1.1rem; }
body.phone .examine-card { padding: 1rem; }

/* Map node styles */
.map-node-current  { fill: var(--accent); stroke: var(--accent); }
.map-node-visited  { fill: var(--ink-muted); stroke: var(--ink-muted); }
.map-node-adjacent { fill: var(--surface); stroke: var(--border); }

.map-node-label {
  fill: var(--ink-faint);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
}
.map-node-label-current {
  fill: var(--ink);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
}

/* Phase 24-B: locked-adjacent — visible but not yet reachable */
.map-node-locked_adjacent {
  fill: var(--surface-sunken);
  stroke: var(--ink-faint);
  stroke-width: 1.5;
  stroke-dasharray: 3 2;
  opacity: 0.5;
}

/* Map edge styles */
.map-edge-traveled { stroke: var(--accent); opacity: 0.5; }
.map-edge-known    { stroke: var(--border); }
.map-edge-locked {
  stroke: var(--ink-faint);
  stroke-width: 1.2;
  stroke-dasharray: 4 3;
  opacity: 0.4;
}

/* Cross-region badge */
.map-badge {
  fill: var(--accent);
  font-family: var(--font-sans);
  font-size: 9px;
  font-weight: 600;
}

/* Resume block — appears above the Begin button when a saved session exists */
#resume-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}

#resume-block button {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  cursor: pointer;
  border: 1px solid var(--border);
}

#resume-block .primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

#resume-block .secondary {
  background: var(--surface-raised);
  color: var(--ink);
}

#resume-block .muted {
  font-family: var(--font-sans);
  color: var(--ink-faint);
  font-size: var(--text-xs);
}

/* Phase 19: utility class for visually disabling buttons during in-flight POSTs. */
.dim {
  opacity: 0.55;
  pointer-events: none;
}

/* Phase 19 + 20: slim media player, pinned top */
#player {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
  color: var(--ink);
}
#player[hidden] { display: none; }

#player-section-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
}
#player-section {
  font-family: var(--font-sans);
  font-weight: 500;
  color: var(--ink);
  font-size: var(--text-sm);
  letter-spacing: -0.005em;
}
#player-section-index {
  font-family: var(--font-sans);
  color: var(--ink-faint);
  font-size: var(--text-xs);
}
#player-section-kind {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  margin-right: var(--space-2);
}
#player-section-kind[hidden] { display: none; }

#player-controls-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.player-btn,
.player-btn-primary,
.player-btn-ghost {
  background: transparent;
  border: 1px solid transparent;
  color: var(--ink-muted);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.95rem;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.player-btn:hover { background: var(--accent-soft); color: var(--ink); }

.player-btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
  width: 36px;
  height: 36px;
  font-size: 1rem;
  border-color: var(--accent);
}
.player-btn-primary:hover { background: var(--accent); opacity: 0.92; }

.player-btn-ghost {
  color: var(--ink-faint);
  font-size: var(--text-sm);
}
.player-btn-ghost:hover { background: var(--accent-soft); color: var(--ink); }

/* Phase 23-A: SVG media icons — render identically on iOS Safari (no emoji),
   follow currentColor so palette tinting still works. */
.player-icon {
  width: 18px;
  height: 18px;
  fill: currentColor;
  display: block;
}
.player-btn-primary .player-icon { width: 20px; height: 20px; }


.player-time {
  font-family: var(--font-mono);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  min-width: 38px;
  text-align: center;
}

#player-scrub {
  flex: 1;
  position: relative;
  height: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
}
#player-scrub::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 5px;
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
}
#player-scrub-fill {
  position: absolute;
  left: 0;
  height: 5px;
  width: 0%;
  background: var(--accent);
  border-radius: var(--radius-sm);
  pointer-events: none;
}
#player-scrub-ticks {
  position: absolute;
  left: 0;
  right: 0;
  height: 5px;
  pointer-events: none;
}
#player-scrub-ticks .tick {
  position: absolute;
  top: -2px;
  width: 2px;
  height: 9px;
  background: var(--ink-disabled);
}
#player-scrub-ticks .tick.played { background: var(--accent); }

#player-collapsed-bar {
  height: 4px;
  background: var(--surface-sunken);
  cursor: pointer;
  position: relative;
}
#player-collapsed-bar[hidden] { display: none; }
#player-collapsed-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 0%;
  background: var(--accent);
}

/* ===== Phase 27 task 4: hero strip ===== */
/* 16:7 fixed-aspect strip that crops the central horizontal band of
 * the 1024×576 source image (per the Phase 27 composition rule).
 * The strip is the single visual surface for both locations and
 * POIs — Examine taps swap its background-image while the location
 * audio narration plays, then the next state refresh reverts it.
 * Tap opens a full-viewport lightbox of the full 16:9. */
#hero {
  width: 100%;
  aspect-ratio: 16 / 7;
  background-color: var(--surface-sunken);
  background-size: cover;
  background-position: center;
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-4);
  cursor: zoom-in;
  position: relative;
  display: block;
  overflow: hidden;
}
#hero[hidden] { display: none; }
#hero[data-empty="true"] { display: none; }
#hero::after {
  content: "🔍";
  position: absolute;
  right: 8px;
  bottom: 8px;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 50%;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  pointer-events: none;
}
#hero[data-empty="true"]::after { display: none; }
/* The legacy <img id="hero-img"> child is no longer used — the strip
 * paints itself via background-image. Hide it defensively so a stale
 * src can never show through. */
#hero-img { display: none; }

/* ===== Phase 21: title row ===== */
#title-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}
#title-row .title-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--ink-muted);
  font-size: var(--text-lg);
}
#title-row .title-actions button {
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  font-size: inherit;
  padding: var(--space-1) var(--space-2);
}

/* ===== Phase 21: examine cards ===== */
#examine-cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.examine-card {
  display: grid;
  grid-template-columns: 60px 1fr;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  align-items: center;
}
.examine-card:hover { border-color: var(--accent-soft); }
/* Stroll cards are featured — solid accent fill flags this as a
 * different kind of action (a 10–20 min narrated walk, not a brief
 * examine). Same card shape so the row layout stays uniform.
 *
 * Selectors are qualified with .examine-body so they beat the generic
 * .examine-body .examine-verb / .examine-noun rules below (which have
 * the same (0,2,0) specificity but appear later in source order and
 * would otherwise re-paint our text to var(--ink-faint) / var(--ink)).
 *
 * Colors are theme-aware via var(--accent-ink) — defined as the
 * high-contrast color against var(--accent), so this works across
 * both light and dark modes and every regional palette (which all
 * keep --accent and --accent-ink in legible contrast). The verb is
 * a 75/25 blend of accent-ink and the banner color via color-mix,
 * giving a softened "branded" label that's dimmer than the noun
 * without losing legibility. */
.examine-card--stroll {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.examine-card--stroll:hover {
  border-color: var(--accent);
  filter: brightness(1.1);
}
.examine-card--stroll .examine-body .examine-verb {
  color: color-mix(in srgb, var(--accent) 25%, var(--accent-ink) 75%);
  font-weight: 700;
  letter-spacing: 0.1em;
}
.examine-card--stroll .examine-body .examine-noun {
  color: var(--accent-ink);
  font-weight: 500;
}
.examine-thumb {
  width: 60px;
  height: 48px;
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
}
.examine-thumb[data-loaded="true"] {
  background-size: cover;
  background-position: center;
}
.examine-body .examine-verb {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}
.examine-body .examine-noun {
  font-family: var(--font-serif);
  font-size: var(--text-md);
  color: var(--ink);
}
/* Phase 28.2: examine-more + lore-link share one horizontal row to
 * reclaim vertical space. The row wraps (controls re-stack) on narrow
 * viewports rather than truncating. */
#examine-footer-row {
  display: flex;
  justify-content: center;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin: var(--space-2) 0;
}
#examine-footer-row:empty { display: none; }
#examine-more {
  background: transparent;
  border: none;
  color: var(--accent);
  cursor: pointer;
  font-size: var(--text-sm);
}
#examine-more[hidden] { display: none; }
#examine-rest {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-3) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
#examine-rest[hidden] { display: none; }

/* ===== Phase 21: lore link ===== */
#lore-link {
  color: var(--ink-tint);
  font-size: var(--text-sm);
  cursor: pointer;
}
#lore-link[hidden] { display: none; }
#lore-link:hover { color: var(--accent); }

/* ===== Phase 21: exits list ===== */
#exits { margin-bottom: var(--space-4); }
#exits .section-label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  margin: 0 0 var(--space-2) 0;
  font-weight: 500;
}
#exits-list {
  list-style: none;
  padding: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
/* Phase 21.2: each Walk-to row gets a card-like resting contrast against
 * the page surface tint (used to only have hover contrast — too flat).
 * Hover deepens the contrast further so the affordance is still clear. */
.exit-row {
  display: grid;
  grid-template-columns: 24px 1fr;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
  /* Phase 28.2: region-coloured left strip — pairs with the filled
   * number badge so the player can read region at a glance. */
  border-left: 4px solid var(--accent);
  padding-left: calc(var(--space-4) - 4px);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
.exit-row:last-child { border-bottom: none; }
.exit-row:hover {
  background: var(--surface-sunken);
  border-bottom-color: var(--accent-soft);
}
.exit-row:hover .exit-label { color: var(--accent); }
.exit-glyph { color: var(--ink-muted); text-align: center; font-weight: 500; }
.exit-label { font-family: var(--font-serif); color: var(--ink); }

/* Phase 26.1: WALK TO grouping + visual distinctions.
 *
 * Group headers (Forward / Back / Floo) render only when the menu has at
 * least one non-Forward row — for plain corridors they don't appear at
 * all. They're small all-caps muted labels, NOT clickable.
 *
 * is_previous rows get a tinted left bar so the player can see at a
 * glance which exit retraces the way they came — no more "↩ from" text
 * prefix doubling up with an arrow glyph.
 *
 * shortcut + RETURN rows get a similar but distinctly-coloured left bar
 * so quick-jump rows are visually grouped without screaming for attention.
 *
 * Floo rows get a warm accent tint on the left bar — the listening tour
 * treats Floo travel as its own register, so the colour cues that.
 */
.exit-group-header {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: var(--space-3) var(--space-4) var(--space-2);
  background: transparent;
  border-bottom: none;
  cursor: default;
}
/* Phase 28.2: the per-row left border is now the universal region
 * strip (.exit-row above). Shortcut / RETURN rows keep a subtle
 * dim so quick-jump rows don't compete for attention. */
.exit-row--shortcut:not(.exit-row--previous) { opacity: 0.92; }

/* ===== Phase 21: map panel (hidden by default) ===== */
#map-panel {
  margin-bottom: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  background: var(--surface-raised);
}
#map-panel[hidden] { display: none; }
/* Phase 24-A.1: taller map gives labels + bezier edges more breathing room. */
#map-panel #map-svg { width: 100%; height: 280px; display: block; }
@media (max-width: 640px) {
  /* Slightly smaller on phones so the player can still see the exits list. */
  #map-panel #map-svg { height: 240px; }
}

/* Phase 24-A.1: hover-highlight a map node when the matching exit-row is hovered. */
.map-node-highlight {
  stroke: var(--accent);
  stroke-width: 3;
  filter: drop-shadow(0 0 4px var(--accent));
}

/* Phase 28.2: region nav (prev / label / next) under the region map.
 * Restyled from default browser button chrome to the app's flat look. */
#display-controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
#display-controls button {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1;
  padding: var(--space-1) var(--space-2);
  background: var(--surface-raised);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}
#display-controls button:hover {
  background: var(--surface-sunken);
  border-color: var(--accent-soft);
}
#region-label {
  font-family: var(--font-serif);
  font-size: var(--text-sm);
  color: var(--ink);
}

/* ===== Phase 21: footer two-column ===== */
#footer {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
#footer button {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--ink-muted);
  cursor: pointer;
}
#footer button:hover { border-color: var(--accent-soft); color: var(--ink); }

/* ===== Phase 21: settings drawer ===== */
#settings-drawer {
  position: fixed;
  top: 0;
  right: 0;
  width: 320px;
  max-width: 85vw;
  height: 100vh;
  background: var(--surface-raised);
  border-left: 1px solid var(--border);
  padding: var(--space-4);
  z-index: 100;
  overflow-y: auto;
  transform: translateX(0);
  transition: transform 200ms ease;
}
#settings-drawer[hidden] {
  display: block !important;
  transform: translateX(100%);
  pointer-events: none;
}
#drawer-overlay {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.4);
  z-index: 99;
}
#drawer-overlay[hidden] { display: none; }
.drawer-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-4);
}
.drawer-header h2 {
  margin: 0;
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  font-weight: 400;
}
#drawer-close {
  background: transparent;
  border: none;
  font-size: var(--text-lg);
  cursor: pointer;
  color: var(--ink-muted);
}
.setting { margin-bottom: var(--space-4); }
.setting-label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  margin: 0 0 var(--space-1) 0;
  font-weight: 500;
}
.setting-help {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  margin: 0 0 var(--space-2) 0;
  line-height: 1.4;
}
.segmented {
  display: flex;
  gap: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.segmented button {
  flex: 1;
  background: transparent;
  border: none;
  padding: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--ink-muted);
  cursor: pointer;
}
.segmented button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
}

/* Phase 27 task 3: walk-to badge palette. Each color maps to a modality
 * the backend assigns; the frontend never invents a color, just looks
 * up the class. Map node badges use the same tokens so list ↔ map
 * share visual vocabulary. */
:root {
  --exit-forward: #4a6e3e;
  --exit-back:    #8a8c80;
  --exit-floo:    #b8742a;
  --exit-portkey: #6b5e9c;
  --exit-book:    #7f6a4a;
  --exit-locked:  #a3a3a3;
}

.exit-num {
  width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-family: ui-sans-serif, system-ui, sans-serif;
  font-weight: 600; font-size: 12px;
  position: relative;
  flex-shrink: 0;
}
/* Phase 28.2: the badge fills solid with the destination region's
 * colour — strong --accent for visited destinations, lighter
 * --accent-soft for unvisited. The fill darkness carries the visited
 * state; the number is a high-contrast neutral. The .palette-<region>
 * class on this element supplies --accent / --accent-soft. */
.exit-num { background: var(--accent); color: var(--accent-ink); }
.exit-num.exit-num--unvisited { background: var(--accent-soft); color: var(--ink); }

/* Phase 27 task 3: walk-to grid changes from glyph+label to badge+label.
 * The 28px column matches the badge size + small breathing room. */
.exit-row {
  grid-template-columns: 28px 1fr;
}

/* ===== Phase 27 task 4: lightbox ===== */
.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 16px;
}
.lightbox[hidden] { display: none; }
.lightbox img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  border-radius: 4px;
}
.lightbox-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.95);
  border: none;
  font-size: 22px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ===== Phase 27 task 4: examine cards — drop the thumb slot =====
 * The hero strip is now the single visual surface for POIs, so the
 * per-card thumbnail is redundant. Override the two-column grid set
 * earlier in this file so cards become text-only and tighter. */
.examine-card {
  grid-template-columns: 1fr;
}
.examine-card .examine-thumb,
.examine-card .card-thumb {
  display: none;
}

/* Phase 27 task 5: app-chrome pill — fixed top-right floating bar
 * holding ⚙ Settings + ☀ Theme. Out of the location header so they
 * read as app chrome, not location buttons. The ▶ audio-state
 * indicator stays inside the audio chip (where it belongs as a
 * playback-state indicator, not chrome). */
.app-chrome-pill {
  position: fixed;
  top: 12px;
  right: 12px;
  background: rgba(255, 255, 255, 0.92);
  border-radius: 100px;
  padding: 4px 6px;
  display: flex;
  gap: 4px;
  align-items: center;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
  z-index: 50;
  backdrop-filter: blur(8px);
}
.app-chrome-pill button {
  width: 36px;
  height: 36px;
  border: none;
  background: transparent;
  font-size: 18px;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
}
.app-chrome-pill button:hover,
.app-chrome-pill button:focus {
  background: rgba(0, 0, 0, 0.06);
  outline: none;
}

/* Push the page content down on narrow viewports so the pill doesn't
 * occlude the audio chip when it scrolls under. The 50px buys clearance
 * for the 36px button + 12px top offset + a few px breathing room. */
@media (max-width: 500px) {
  #app {
    padding-top: 50px;
  }
}

/* Phase 27 task 7 (A): tighten H1 location title on narrow mobile.
 * Desktop default ~28px serif (set on #location-name); reduce to 22px
 * on viewports <500px wide so the title doesn't dominate the limited
 * above-fold real estate alongside the new hero strip and audio chip. */
@media (max-width: 500px) {
  #location-name,
  h1.location-title,
  .location-title {
    font-size: 22px;
    line-height: 1.2;
  }
}

/* Phase 27 task 7 (B): sticky audio chip — pinned at top of the
 * viewport while the player scrolls down past it. The chip has an
 * opaque background already (--surface-raised), so content scrolling
 * behind it doesn't bleed through. z-index sits BELOW the chrome pill
 * (which is z:50 from Task 5) so the pill stays on top. */
#player {
  position: sticky;
  top: 0;
  z-index: 40;
}

/* Phase 27 task 9: Dev tools section inside the settings drawer.
 * Hidden by default; revealed only when devMode === "on". Lists the
 * region hubs returned by /api/region-hubs grouped by category, so a
 * dev can warp anywhere without narrating through floo/portkey chains.
 * Multi-hub regions expand to a nested row of sub-buttons (one per
 * hub) so we never collapse distinct anchors under a region label. */
.dev-tools-section {
  border-top: 1px dashed var(--border, #444);
  margin-top: 16px;
  padding-top: 12px;
}
.region-warp-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 8px;
}
.region-warp-category {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.region-warp-category > h4 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.7;
  margin: 0 0 2px 0;
}
/* Phase 27.1 redesign: flat list of region-warp buttons (no multi-hub
 * expand pattern). Each .region-warp-row-btn is one tap target that
 * warps directly to a single hub location. */
.region-warp-row-btn {
  text-align: left;
  padding: 6px 10px;
  font: inherit;
  background: var(--surface-raised, #1f1f1f);
  color: var(--ink);
  border: 1px solid var(--border, #444);
  border-radius: 4px;
  cursor: pointer;
  display: block;
  width: 100%;
}
.region-warp-row-btn:hover,
.region-warp-row-btn:focus {
  background: var(--surface-hover, #2a2a2a);
  outline: none;
}

/* Phase 27 task 10: world-map view ----------------------------------- */

#map-panel-header {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 8px;
}

.map-view-switcher {
  display: inline-flex;
  background: var(--surface-sunken, rgba(0,0,0,0.05));
  border-radius: 100px;
  padding: 2px;
}
.map-view-switcher button {
  background: transparent;
  border: none;
  padding: 4px 12px;
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 100px;
  cursor: pointer;
  color: var(--ink-muted, #888);
}
.map-view-switcher button.active {
  background: var(--surface-raised, #fff);
  color: var(--ink, #222);
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}

.world-progress-bar {
  margin-bottom: 12px;
  padding: 8px 12px;
  background: var(--surface-raised, rgba(255,255,255,0.6));
  border-radius: 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: 13px;
}
.world-progress-bar .pct {
  color: var(--accent, #6b8a4a);
  font-weight: 600;
}

.world-map-svg {
  width: 100%;
  height: auto;
  background: var(--surface-sunken, rgba(0,0,0,0.05));
  border-radius: 8px;
  display: block;
}

.region-tile { cursor: pointer; transition: opacity 120ms ease; }
.region-tile:hover { opacity: 0.85; }
/* Phase 27.2: per-region tile coloring. The region's palette-* class
 * (set on the <g.region-tile> wrapper by renderWorldView) provides
 * --accent + --accent-soft via the existing palette token system in
 * palettes.css; the state class (untouched / partial / complete) then
 * picks which token renders. */
.region-tile rect {
  rx: 8; ry: 8;
  fill: var(--accent-soft, #d8e1c6);
  stroke: var(--accent, #6b8a4a);
  stroke-width: 1.5;
  transition: fill 200ms ease, stroke-width 200ms ease;
}
.region-tile.untouched rect {
  fill: var(--surface-raised, #f3f3ee);
  stroke-dasharray: 3 3;
  opacity: 0.6;
}
.region-tile.partial rect {
  fill: var(--accent-soft, #d8e1c6);
  opacity: 0.85;
}
.region-tile.complete rect {
  fill: var(--accent, #aac292);
  stroke-width: 2.5;
}
.region-tile text.label {
  fill: var(--ink, #222);
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14px;
}
.region-tile text.count {
  fill: var(--ink-muted, #888);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10px;
}
.region-tile.untouched text { fill: var(--ink-muted, #888); }


.region-tile .dev-warp-icon { cursor: pointer; }
.region-tile .dev-warp-icon circle {
  fill: rgba(255, 255, 255, 0.95);
  stroke: var(--ink-muted, #888);
  stroke-width: 1;
}
.region-tile .dev-warp-icon text {
  fill: var(--ink, #222);
  font-family: var(--font-sans, system-ui, sans-serif);
}

/* Mobile: world view opens as a full-screen modal layer (the side panel
 * is too cramped for 26 tiles on narrow viewports). */
@media (max-width: 700px) {
  #map-panel.world-modal {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: var(--surface, #fff);
    padding: 16px;
    overflow: auto;
  }
}


/* Phase 28.2: opaque map nodes — visited/unvisited is signalled by
 * fill colour, not opacity. Translucent circles let the connecting
 * edge line bleed through; opaque circles hide the edge behind them.
 * .is-visited / .is-unvisited are set on the node <g> by app.js. */
.map-node circle {
  stroke: var(--accent);
  stroke-width: 2;
}
.map-node.is-visited circle   { fill: var(--accent); }
.map-node.is-unvisited circle { fill: var(--accent-soft); }
.map-node.is-visited text   { fill: var(--accent-ink); font-weight: 600; }
.map-node.is-unvisited text { fill: var(--ink);        font-weight: 600; }


/* ===== Offline-cache install banner =====
 * Visible only while the service worker is fetching the offline bundle
 * (initial install + any later version update). Driven by sw.js
 * postMessage -> app.js. Stays at the top of the viewport, out of the
 * way of the main app, so the user can SEE the install is in progress
 * (the whole problem before this banner was that install was silent
 * and the user couldn't tell whether to close the tunnel yet). */
#install-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  display: flex;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: center;
  border-bottom: 1px solid var(--accent);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  transition: opacity 600ms ease;
}
#install-banner[data-state="done"] {
  background: var(--accent);
  opacity: 0.92;
}
#install-banner[data-state="error"] {
  background: #b03a2e;
  color: #fff;
}
#install-banner-progress {
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
}

/* ===== Settings drawer: diagnostics panel =====
 * Shown at the bottom of the settings drawer. Surfaces APP_VERSION,
 * service-worker state, cache size, and origin in a key/value list.
 * The user can verify on-device (no DevTools) whether the latest
 * build is actually running and whether the offline cache is healthy. */
#diagnostics-body {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  margin-top: var(--space-2);
}
.diagnostics-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 2px 0;
  border-bottom: 1px dashed var(--border);
  word-break: break-all;
}
.diagnostics-row:last-child { border-bottom: none; }
.diagnostics-key {
  color: var(--ink-faint);
  flex-shrink: 0;
}
.diagnostics-val {
  color: var(--ink);
  text-align: right;
  font-weight: 500;
}

.diagnostics-actions {
  margin-top: var(--space-3);
  display: flex;
  justify-content: center;
}
.diagnostics-btn {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-4);
  background: var(--accent);
  color: var(--accent-ink);
  border: 1px solid var(--accent);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.diagnostics-btn:hover { opacity: 0.92; }
.diagnostics-btn:disabled { opacity: 0.5; cursor: default; }
