/* Meandr Tier 0 — a calm, inviting wayfinding UI for a voice/text local tour guide.
   Priorities: large readable text (outdoor mobile use), high contrast, big touch targets,
   minimal chrome. No framework, no build step.

   Brand idea — "meandr": wandering, getting pleasantly lost, serendipitous discovery.
   Direction "DUSK WANDER": a twilight-INDIGO primary (calm, trustworthy — the evening sky
   you wander a new city under) with a warm sunset-CORAL accent (the spark of discovery, the
   place you arrive at) on soft dusk-haze neutrals. The signature motif is the meandering
   ROUTE — a winding line toward a destination — not a compass. See DESIGN-SYSTEM.md.

   Authored with the visual-designer method: hierarchy first, 60-30-10 colour (haze 60 /
   indigo 30 / coral 10), 8-point spacing, a 1.25 modular type scale, WCAG 2.1 AA throughout. */

:root {
  /* ============================================================================
     CONVERSATION LAYER — legacy alias names kept (app.js + the rules below reference
     them) but re-pointed at the Meandr brand tokens, so the whole Tier 0 app speaks
     ONE visual language. The guide view stays large-text, high-contrast and calm.
     ============================================================================ */
  --bg: var(--haze);                   /* soft dusk-haze field                     */
  --surface: var(--surface-1);         /* white                                    */
  --ink: var(--ink-strong);            /* near-black ink (15:1 on haze)            */
  --ink-soft: var(--ink-soft-tok);     /* secondary text (9.3:1 on white)          */
  --me-bg: var(--me-bubble);           /* visitor's words: warm coral mist         */
  --me-ink: var(--me-bubble-ink);      /* ≥14:1 on coral mist                      */
  --them-bg: var(--guide-bubble);      /* the guide's voice: soft indigo mist      */
  --them-ink: var(--guide-bubble-ink); /* ≥14:1 on indigo mist                     */
  --accent: var(--primary);            /* INDIGO primary — actions, listening dot  */
  --accent-ink: var(--on-primary);     /* white (6.3:1 on accent)                  */
  --accent-strong: var(--primary-strong);
  --warn: var(--notice);               /* wayfinding/help notice (5.0:1 on white)  */
  --warn-bg: #fdf0db;                  /* soft amber notice fill                    */
  --line: var(--line-soft);            /* decorative hairline (dividers)           */
  --line-strong: var(--line-control);  /* visible control borders (3.7:1)          */
  --focus: var(--focus-ring);          /* INDIGO focus (7.9:1 on white)            */

  /* ROOT SIZE — 16px base (the browser default, 100%) multiplied by the user's font-scale.
     Everything below is sized in rem, so this ONE variable scales the whole app together: chat
     bubbles, POI cards, controls, headers. The previous base was 22px, which made the UI read
     "zoomed-in"; 16px is the sane modern default with the modular scale below carrying hierarchy.
     --font-scale (default 1) is driven live by the Preferences text-size slider, persisted to
     localStorage (mhyra.tier0.fontScale), and applied pre-paint by an inline script in layout.tsx
     so there is no flash of the wrong size on reload. */
  --font-scale: 1;
  font-size: calc(100% * var(--font-scale));   /* 100% = 16px → 1rem = 16px × scale */

  /* ============================================================================
     MEANDR DESIGN SYSTEM — shared brand tokens (whole Tier 0 web app). Single source
     of truth for BOTH surfaces: the admin/config screens (sign-in gate + settings)
     AND the visitor conversation view, whose alias names above re-point here.
     All pairings verified for WCAG 2.1 AA — ratios noted per token.
     ============================================================================ */

  /* --- Brand: INDIGO tonal scale (the twilight sky over a new city) --- */
  --indigo-50:  #eef0fe;   /* tints, guide bubble, halo wash               */
  --indigo-100: #e0e3fc;   /* soft fills, focus glow                       */
  --indigo-200: #c4c9f8;   /* borders on tinted surfaces                   */
  --indigo-300: #9aa2f0;   /* decorative only (2.5:1 — not for text)       */
  --indigo-400: #6f78ea;   /* decorative / large non-text (3.3:1)          */
  --indigo-500: #5b54e0;   /* large text / UI on white    (4.9:1)          */
  --indigo-600: #4f46e5;   /* PRIMARY — buttons, brand    (6.29:1)         */
  --indigo-700: #4338ca;   /* primary hover, links, focus (7.91:1)         */
  --indigo-800: #312e81;   /* headings on light           (11.4:1)         */
  --indigo-900: #1e1b4b;   /* deepest ink / shadow tint   (15.8:1)         */

  /* --- Accent: CORAL tonal scale (the sunset, the destination you reach) --- */
  --coral-100: #ffe9e3;   /* soft warm fills, visitor bubble               */
  --coral-200: #ffc9bb;   /* tinted borders                                */
  --coral-300: #ff9f86;   /* decorative / large non-text (2.4:1)           */
  --coral-400: #fb7a59;   /* pin / icon accent, large    (3.0:1)           */
  --coral-500: #e64a32;   /* large text / icon on white  (3.9:1)           */
  --coral-600: #cc3a2f;   /* accent text/icon on white   (5.01:1)          */
  --coral-700: #a82d24;   /* accent hover                (6.9:1)           */

  /* --- Semantic roles (admin / shared) --- */
  --primary:        var(--indigo-600);
  --primary-strong: var(--indigo-700);
  --primary-soft:   var(--indigo-100);
  --primary-tint:   var(--indigo-50);
  --on-primary:     #ffffff;            /* 6.29:1 on --primary             */
  --brand-heading:  var(--indigo-800);
  --brand-link:     var(--indigo-700);
  --accent-brand:   var(--coral-600);   /* 10% warm accent (pins, badges)  */
  --accent-brand-strong: var(--coral-700);

  /* --- Dusk-haze neutrals --- */
  --haze:         #f3f3fb;              /* shared soft dusk-haze field      */
  --surface-1:    #ffffff;
  --surface-2:    #f8f8fe;              /* faint cool white                 */
  --ink-strong:   #1c1b2e;             /* near-black ink   (15.8:1)         */
  --ink-soft-tok: #45455c;             /* secondary text   (9.35:1)         */
  --ink-muted:    #59596f;             /* hints on white   (6.84:1)         */
  --line-soft:    #e4e4f0;             /* hairline (card edge, decorative)  */
  --line-control: #82839c;             /* input borders   (3.69:1 — passes 1.4.11) */

  /* --- Semantic status (all AA on white; each hue distinct from brand) --- */
  --ok:     #1f7a52;   /* green  5.3:1  */
  --notice: #8a5a12;   /* amber  5.0:1 — caution, NOT the coral accent */
  --danger: #b3261e;   /* red    6.5:1  */

  /* --- Typography ---
     Pairing: a confident humanist SANS for the wordmark, headings, body & UI (modern,
     friendly, app-native, zero network cost via the system stack), with an editorial
     SERIF reserved ONLY for place names on POI cards — so a recommended place reads with
     a distinct, atlas-like character. Contrast through weight & scale, not decoration. */
  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-brand: var(--font-ui);   /* wordmark + headings: same sans, set heavy & tight */
  --font-place: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  /* Modular type scale — 1.25 (major third), base --fs-md = 1rem = 16px. Hierarchy comes from
     this scale + weight, never from ad-hoc pixel sizes. xs/sm for meta & hints, md for body &
     chat, lg for emphasis/inputs, xl for the wordmark, 2xl/3xl for display headings. */
  --fs-xs: 0.75rem;  --fs-sm: 0.875rem; --fs-md: 1rem;
  --fs-lg: 1.25rem;  --fs-xl: 1.563rem; --fs-2xl: 1.953rem; --fs-3xl: 2.441rem;
  --lh-tight: 1.2;   --lh-body: 1.5;    --lh-relaxed: 1.6;
  --fw-regular: 400; --fw-medium: 600; --fw-bold: 700; --fw-black: 800;
  --tracking-brand: -0.01em;       /* tight tracking gives the sans wordmark its identity */

  /* --- Spacing scale (8-point grid; 4px sub-grid for fine offsets) --- */
  --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem;
  --space-4: 1rem;    --space-5: 1.5rem; --space-6: 2rem;

  /* --- Radius (soft, rounded — winding rather than sharp) --- */
  --radius-sm: 10px; --radius-md: 14px; --radius-lg: 18px;
  --radius-xl: 24px; --radius-pill: 999px;

  /* --- Elevation (soft, indigo-tinted — rgba of --indigo-900 = 30,27,75) --- */
  --elev-1: 0 1px 2px rgba(30,27,75,0.06), 0 2px 6px rgba(30,27,75,0.06);
  --elev-2: 0 4px 14px rgba(30,27,75,0.10);
  --elev-3: 0 14px 40px rgba(30,27,75,0.18);

  /* --- Focus + motion --- */
  --focus-ring: var(--indigo-700);     /* 7.91:1 — well above 3:1         */
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --dur: 160ms;

  /* ============================================================================
     CONVERSATION-VIEW TUNING — the visitor view is the SAME Meandr brand as the
     config surface, tuned for at-a-glance use on a phone outdoors: bigger type,
     generous spacing, gentle motion, high contrast, low clutter. All pairings AA.
     The two voices split across the brand: the GUIDE speaks in calm indigo, YOU
     answer in warm coral.
     ============================================================================ */
  --guide-bubble:        var(--indigo-50);   /* the guide's voice — soft indigo mist */
  --guide-bubble-ink:    var(--ink-strong);  /* ≥14:1 on indigo mist               */
  --me-bubble:           var(--coral-100);   /* visitor's words — warm coral mist   */
  --me-bubble-ink:       var(--ink-strong);  /* ≥14:1 on coral mist                 */
  --companion-accent:        var(--indigo-600);/* listening dot, primary actions    */
  --companion-accent-strong: var(--indigo-700);/* hover / active                    */
  --companion-ink-soft:  var(--ink-soft-tok);
  /* Indigo glow used by the ambient listening pulse (rgba of --indigo-600). */
  --companion-pulse: 79, 70, 229;

  /* --- Admin alias names (kept for the config surface rules below) --- */
  --admin-bg:        var(--haze);
  --admin-surface:   var(--surface-1);
  --admin-surface-2: var(--surface-2);
  --admin-ink:       var(--ink-strong);
  --admin-ink-soft:  var(--ink-soft-tok);
  --admin-ink-muted: var(--ink-muted);
  --admin-line:      var(--line-soft);
  --admin-line-2:    var(--line-control);
}

* { box-sizing: border-box; }

/* Author-level hide: beats any layout `display` set on flex elements so the `hidden` attribute
   reliably hides controls (e.g. the composer mic before capability is confirmed, signed-out auth buttons). */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--fs-md);     /* 1rem = 16px × --font-scale — the body default */
  line-height: var(--lh-body);
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: -999px; top: 0;
  background: var(--accent); color: var(--accent-ink);
  padding: 0.6rem 1rem; z-index: 10; border-radius: 0 0 8px 0;
}
.skip:focus { left: 0; }

/* ---- Top bar ---- */
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 0.9rem 1.2rem;
  background: var(--surface); border-bottom: 2px solid var(--line);
}
/* Brand: the Meandr route mark beside the wordmark. Decorative (aria-hidden) —
   it carries the wayfinding feel, not information. */
.topbar-brand { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.brand-mark { width: 40px; height: 40px; flex: 0 0 auto; display: block; }
.title { margin: 0; font-family: var(--font-brand); font-size: var(--fs-xl); font-weight: var(--fw-black);
  letter-spacing: var(--tracking-brand); color: var(--brand-heading);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Brand text column: the Meandyr wordmark with the guide's configurable name as a quiet secondary
   line (#companion-name). The wordmark is always the primary label; the guide name never replaces it. */
.brand-text { display: flex; flex-direction: column; min-width: 0; line-height: var(--lh-tight); }
.brand-guide { font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--ink-soft);
  letter-spacing: 0.02em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.brand-guide:empty { display: none; }

.icon-btn {
  font-size: var(--fs-sm); padding: 0.7rem 1.1rem;
  min-height: 56px; border: 2px solid var(--line-strong); border-radius: var(--radius-md);
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.icon-btn:hover { background: var(--primary-tint); border-color: var(--primary); color: var(--primary-strong); }
.icon-btn:focus-visible { outline: 4px solid var(--focus); outline-offset: 3px; }

/* ---- Conversation stage ---- */
.stage {
  flex: 1; width: 100%; max-width: 820px; margin: 0 auto;
  padding: 1.2rem; display: flex; flex-direction: column; gap: 1rem;
}

.transcript {
  flex: 1; min-height: 40vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1rem;
  padding: 0.4rem;
}

.hint { color: var(--ink-soft); font-size: var(--fs-md); text-align: center; margin: auto;
  max-width: 36ch; line-height: var(--lh-body); }

.bubble {
  max-width: 90%; padding: 1rem 1.2rem; border-radius: var(--radius-lg);
  font-size: var(--fs-md); line-height: var(--lh-body);
  white-space: pre-wrap; word-wrap: break-word;
  box-shadow: var(--elev-1);
}
/* Speaker label: explicit soft-ink color (not opacity) so it stays AA on both
   the indigo-mist and coral-mist bubble fills. */
.bubble .who { display: block; font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--companion-ink-soft); margin-bottom: 0.25rem; }

.from-them { align-self: flex-start; background: var(--them-bg); color: var(--them-ink);
  border-bottom-left-radius: var(--radius-sm); }
.from-me { align-self: flex-end; background: var(--me-bg); color: var(--me-ink);
  border-bottom-right-radius: var(--radius-sm); }

/* Safety/help notice — a calm, distinct AMBER card (semantic --notice, neither the indigo
   brand nor the coral accent), so a caution can never be mistaken for a normal reply. */
.bubble.safety {
  align-self: stretch; max-width: 100%;
  background: var(--warn-bg); color: var(--warn);
  border: 2px solid var(--warn); border-radius: var(--radius-md);
  box-shadow: none;
}
.bubble.safety .who { color: var(--warn); }

/* ---- POI / recommendation card ----
   Component style for a place the guide points you to ("off the beaten path near you").
   Available for server/markup-rendered recommendations; the warm CORAL pin marks the
   destination so a place visually reads differently from the guide's prose. Anatomy:
   title (serif place name), meta row (coral marker), body (body ink). Left coral rule =
   the wayfinding marker; the round ◆ marker echoes the route's destination dot. */
.poi-card {
  align-self: flex-start; max-width: 90%;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent-brand);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-1);
  padding: var(--space-4) var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.poi-card .poi-title {
  margin: 0; font-family: var(--font-place); font-size: var(--fs-lg);
  line-height: var(--lh-tight); font-weight: var(--fw-bold); color: var(--brand-heading);
}
.poi-card .poi-meta {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--accent-brand);
}
.poi-card .poi-meta::before { content: "●"; font-size: 0.7em; line-height: 1; }
.poi-card .poi-body { margin: 0; font-size: var(--fs-md); line-height: var(--lh-body); color: var(--ink); }

/* ---- Open in Maps / Directions control ----
   A real-tap link the guide attaches to a reply (or a POI/recommendation) that opens the device's
   DEFAULT map app with WALKING directions to the place. CORAL = the destination you're routing to,
   visually distinct from the indigo primary actions. The link IS the user gesture (no auto-open).
   The wrapping row forces it onto its own line below the message text. */
.directions-row { margin-top: var(--space-3); }
.directions-btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 44px; padding: 0.5rem 1.1rem;
  background: var(--accent-brand); color: #fff;
  border: 2px solid transparent; border-radius: var(--radius-pill);
  font-size: var(--fs-sm); font-weight: var(--fw-bold);
  text-decoration: none; cursor: pointer;
  box-shadow: var(--elev-1);
  transition: background var(--dur) var(--ease);
}
.directions-btn:hover { background: var(--accent-brand-strong); }
.directions-btn:focus-visible { outline: 4px solid var(--focus); outline-offset: 3px; }
.directions-btn .directions-pin { font-size: 0.95em; line-height: 1; transform: rotate(-45deg); }

.status { min-height: 1.6rem; color: var(--ink-soft); font-size: var(--fs-sm); text-align: center; }
.status.error { color: var(--danger); font-weight: 700; }

/* ---- Composer (the always-available text box) ---- */
.composer { display: flex; gap: 0.7rem; align-items: stretch; flex-wrap: wrap; }

.text-input {
  flex: 1 1 60%; min-width: 12ch;
  font-size: var(--fs-md); padding: 1rem 1.1rem;
  border: 3px solid var(--line-strong); border-radius: var(--radius-md);
  background: var(--surface); color: var(--ink);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.text-input::placeholder { color: var(--admin-ink-muted); }
.text-input:focus { outline: 4px solid var(--focus); outline-offset: 2px;
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

/* Composer input sits cleanly — no resting outer ring/pill (meridian's composer convention). The
   3px border is kept transparent (so sizing is unchanged and the AA focus ring + indigo border still
   appear on focus); a soft surface fill keeps the field discoverable on the dusk-haze stage. */
.composer .text-input { border-color: transparent; background: var(--surface-2); }

.btn {
  font-size: var(--fs-md); font-weight: 700; cursor: pointer;
  min-height: 64px; padding: 0.8rem 1.4rem;
  border-radius: var(--radius-md); border: 3px solid transparent;
}
.btn:focus-visible { outline: 4px solid var(--focus); outline-offset: 3px; }

.btn-send { background: var(--accent); color: var(--accent-ink); }
.btn-send:not([disabled]):hover { background: var(--accent-strong); }

/* Composer mic (voice INPUT / dictation): a compact, square indigo-outlined icon button next to Send.
   Tap-to-talk records ONE utterance and drops the transcript into the text box for review/edit. It
   fills indigo + pulses while recording, and shows a spinner while the /v1/stt request is in flight.
   Distinct from the per-message read-aloud (TTS output) button (.msg-tts). */
.btn-mic-input {
  flex: 0 0 auto; min-width: 64px;
  display: inline-flex; align-items: center; justify-content: center;
  position: relative;
  background: var(--surface); color: var(--accent); border-color: var(--accent);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn-mic-input:not([disabled]):hover { background: var(--primary-tint); }
.btn-mic-input .mic-glyph { display: block; }
.btn-mic-input.recording {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
  animation: mic-pulse 1.4s var(--ease) infinite;
}
.btn-mic-input.recording:not([disabled]):hover { background: var(--accent-strong); }
/* Transcribing: hide the glyph, show a spinner ring (button is disabled meanwhile). */
.btn-mic-input.busy .mic-glyph { visibility: hidden; }
.btn-mic-input.busy::after {
  content: ""; position: absolute; width: 22px; height: 22px;
  border: 3px solid var(--primary-soft); border-top-color: var(--accent);
  border-radius: 50%; animation: mic-spin 0.7s linear infinite;
}
@keyframes mic-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--companion-pulse), 0.5); }
  70%  { box-shadow: 0 0 0 12px rgba(var(--companion-pulse), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--companion-pulse), 0); }
}
@keyframes mic-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .btn-mic-input.recording { animation: none !important; }
}

.btn-ghost { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.btn-ghost:hover { background: var(--primary-tint); border-color: var(--primary); }

.btn[disabled] { opacity: 0.5; cursor: not-allowed; }

/* Per-message read-aloud (TTS OUTPUT) — a small round play/stop button appended to each assistant
   bubble. Idle: a play triangle on a soft indigo tint. Playing: a stop/pause glyph on filled indigo.
   Tap to play THAT reply via /v1/tts (browser-voice fallback); tap again to stop. On-demand only —
   replies are never auto-spoken unless the auto-play preference is on. Mirrors meridian's SectionPlayButton. */
.msg-tts {
  display: flex; align-items: center; justify-content: center;   /* block-level → sits on its own line */
  width: 34px; height: 34px; margin-top: 0.6rem; padding: 0;
  border: none; border-radius: var(--radius-pill); cursor: pointer;
  background: var(--primary-soft); color: var(--accent);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.msg-tts:hover { background: var(--indigo-200); }
.msg-tts:focus-visible { outline: 4px solid var(--focus); outline-offset: 3px; }
.msg-tts .tts-stop { display: none; }
.msg-tts.playing { background: var(--accent); color: var(--accent-ink); animation: mic-pulse 1.4s var(--ease) infinite; }
.msg-tts.playing:hover { background: var(--accent-strong); }
.msg-tts.playing .tts-play { display: none; }
.msg-tts.playing .tts-stop { display: inline; }
@media (prefers-reduced-motion: reduce) { .msg-tts.playing { animation: none !important; } }

/* ---- Settings dialog (admin/config management surface) ---- */
.settings {
  border: none; border-radius: var(--radius-xl); padding: 0; max-width: 560px; width: 92%;
  background: var(--admin-surface); color: var(--admin-ink); box-shadow: var(--elev-3);
}
.settings::backdrop { background: rgba(30, 27, 75, 0.45); }
.settings-form { padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-2); }

/* Header: the route mark beside the title to carry the brand into the panel. */
.settings-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.settings-head .logo-mark { width: 40px; height: 40px; }
.settings-form h2 {
  margin: 0; font-family: var(--font-brand); font-size: var(--fs-xl); line-height: var(--lh-tight);
  font-weight: var(--fw-black); letter-spacing: var(--tracking-brand); color: var(--brand-heading);
}
.settings-form label {
  font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--admin-ink);
  margin-top: var(--space-4);
}
.field-hint {
  margin: var(--space-1) 0 0; font-size: var(--fs-sm); font-weight: var(--fw-regular);
  color: var(--admin-ink-muted); line-height: var(--lh-body);
}
.settings-form input, .settings-form select {
  font-size: var(--fs-lg); padding: 0.75rem 0.85rem; margin-top: var(--space-2);
  border: 2px solid var(--admin-line-2); border-radius: var(--radius-md);
  background: var(--admin-surface); color: var(--admin-ink);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.settings-form input:focus, .settings-form select:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-soft);
}
.settings-actions { display: flex; gap: var(--space-3); margin-top: var(--space-6); }
.settings-actions .btn { flex: 1; }

/* ---- App shell + OPTIONAL traveller sign-in (Clerk) ---- */
/* #app-root holds the whole guide UI; it stays a flex column so the stage layout is unchanged. The
   guide is NEVER hidden behind sign-in — signing in only unlocks the saved profile + itinerary. */
#app-root { flex: 1; display: flex; flex-direction: column; min-height: 0; }

.topbar-actions { display: flex; align-items: center; gap: 0.8rem; }
.user-button { display: inline-flex; align-items: center; min-height: 40px; }
.user-button:empty { display: none; }

/* Auth chrome shows ONLY when Clerk is configured (html.clerk-on). "Sign in" when signed out;
   "My trip" + the Clerk user button once signed in. Default-hidden so the live site (Clerk
   unconfigured) shows no auth controls at all. */
.auth-signin-btn, .auth-account-btn { display: none; }
html.clerk-on.signed-out .auth-signin-btn { display: inline-flex; }
html.clerk-on.signed-in .auth-account-btn { display: inline-flex; }

/* The sign-in + account modals reuse the .settings <dialog> shell; these center the brand header. */
.signin-modal .settings-form, .signin-form { text-align: center; }
.signin-head { display: flex; flex-direction: column; align-items: center; margin-bottom: var(--space-4); }
.signin-mount { display: flex; justify-content: center; min-height: 3rem; }

/* ENTRY GATE — the full-screen Clerk sign-in shown to unauthenticated visitors on load (page.tsx
   <SignedOut>). It overlays the always-present guide; signing in (or "keep exploring") reveals it. */
.entry-gate {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-5);
  background: var(--surface, #fff);
  background: color-mix(in srgb, var(--surface, #1b1726) 86%, transparent);
  backdrop-filter: blur(8px);
  overflow-y: auto;
}
.entry-card {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  width: 100%; max-width: 26rem; margin: auto; text-align: center;
}
.entry-card .auth-title { margin: var(--space-2) 0 0; }
.entry-card .auth-sub { margin: 0; color: var(--ink-muted); max-width: 24rem; }
.entry-signin { display: flex; justify-content: center; width: 100%; margin: var(--space-2) 0; }
.entry-skip { margin-top: var(--space-1); }
.account-subhead {
  margin: var(--space-6) 0 var(--space-2); font-family: var(--font-brand);
  font-size: var(--fs-lg); color: var(--brand-heading); font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-brand);
  border-top: 1px solid var(--line); padding-top: var(--space-4);
}

/* Saved itinerary list inside the "My trip" modal. */
.itin-list { list-style: none; margin: var(--space-2) 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.itin-empty { color: var(--ink-muted); font-size: var(--fs-sm); }
.itin-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-md); padding: var(--space-2) var(--space-3);
}
.itin-label { font-size: var(--fs-sm); color: var(--ink); }
.itin-remove { flex: 0 0 auto; min-height: 36px; padding: 0 var(--space-3); font-size: var(--fs-xs); }
.itin-add { display: flex; gap: var(--space-2); margin-top: var(--space-3); }
.itin-add input { flex: 1; }

/* Logo + halo. The route mark rests on a faint indigo wash so it reads as a crafted brand
   tile and the edge stays gentle against the card. */
.logo-halo {
  width: 116px; height: 116px; margin: 0 auto var(--space-4);
  display: grid; place-items: center; border-radius: var(--radius-pill);
  background: radial-gradient(closest-side, var(--indigo-50) 0%, rgba(238,240,254,0.55) 62%, rgba(238,240,254,0) 100%);
}
.logo-mark { width: 76px; height: 76px; display: block; }

.auth-title {
  margin: 0 0 var(--space-2); font-family: var(--font-brand); font-size: var(--fs-xl);
  line-height: var(--lh-tight); font-weight: var(--fw-black); color: var(--brand-heading);
  letter-spacing: var(--tracking-brand);
}
.auth-sub { margin: 0 0 var(--space-5); color: var(--admin-ink-soft); font-size: var(--fs-md); }
#sign-in { display: flex; justify-content: center; }

/* Quiet reassurance line — clarifies that sign-in is optional. */
.auth-foot {
  margin: var(--space-5) 0 0; padding-top: var(--space-4);
  border-top: 1px solid var(--admin-line);
  color: var(--admin-ink-muted); font-size: var(--fs-sm); line-height: var(--lh-body);
}
.auth-error { color: var(--danger); font-weight: var(--fw-bold); }

/* Best-effort cosmetic theming for the Clerk-mounted widget (purely visual; the gate
   works regardless of whether these .cl-* hooks match the loaded Clerk version). */
#sign-in .cl-formButtonPrimary { background: var(--primary); }
#sign-in .cl-formButtonPrimary:hover { background: var(--primary-strong); }
#sign-in a, #sign-in .cl-footerActionLink { color: var(--brand-link); }

/* ---- Primary action button (admin surfaces) ---- */
.btn-primary {
  background: var(--primary); color: var(--on-primary); border-color: transparent;
  transition: background var(--dur) var(--ease);
}
.btn-primary:hover { background: var(--primary-strong); }
/* White gap + indigo ring stays visible on both the button and the page. */
.btn-primary:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--admin-surface), 0 0 0 6px var(--focus-ring);
}

/* Respect users who prefer less motion. */
@media (prefers-reduced-motion: reduce) {
  * { scroll-behavior: auto !important; }
  .btn-primary, .settings-form input, .settings-form select,
  .icon-btn, .text-input { transition: none; }
}

/* ---- SHARE LOCATION control ----
   An explicit, opt-in card to share GPS or type a Milan area so the guide knows what's near you.
   Calm indigo brand; the live sharing dot uses the coral accent (the "destination" you've reached).
   All pairings AA: status/note text on the soft card, indigo-filled primary action. */
.location {
  display: flex; flex-direction: column; gap: var(--space-3);
  align-self: stretch; width: 100%;
  background: var(--surface); border: 2px solid var(--line-strong);
  border-radius: var(--radius-lg); box-shadow: var(--elev-1);
  padding: var(--space-3) var(--space-4);
}

/* Status line — ON/OFF + the resolved area name. */
.loc-status { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.loc-status .loc-dot {
  width: 14px; height: 14px; flex: 0 0 auto; border-radius: 50%;
  background: var(--admin-ink-muted); opacity: 0.85;        /* OFF: a muted, clearly-not-live dot */
}
.loc-status .loc-text { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; }
/* ON: a warm coral "you're here" dot that breathes gently; bolder ink for the live state. */
.loc-status[data-sharing="on"] .loc-dot { background: var(--accent-brand); opacity: 1;
  animation: loc-pulse 2s var(--ease) infinite; }
.loc-status[data-sharing="on"] .loc-text { color: var(--ink); }

@keyframes loc-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(204, 58, 47, 0.4); }
  70%  { box-shadow: 0 0 0 10px rgba(204, 58, 47, 0); }
  100% { box-shadow: 0 0 0 0 rgba(204, 58, 47, 0); }
}
@media (prefers-reduced-motion: reduce) { .loc-status .loc-dot { animation: none !important; } }

/* Action row — compact controls (smaller than the big composer buttons). */
.loc-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.loc-actions .btn { min-height: 52px; font-size: var(--fs-sm); padding: 0.6rem 1.2rem; }
/* Primary share action: indigo-filled, like Send. */
.btn-loc { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.btn-loc:not([disabled]):hover { background: var(--accent-strong); }

/* Manual fallback form: input + Set on one row, hint below. */
.loc-manual { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: stretch; }
.loc-manual .text-input { flex: 1 1 60%; min-width: 12ch; font-size: var(--fs-md); padding: 0.7rem 0.9rem; }
.loc-manual .btn-send { min-height: 52px; font-size: var(--fs-sm); padding: 0.6rem 1.2rem; }
.loc-manual .field-hint { flex-basis: 100%; margin: 0; color: var(--ink-muted); }

/* Note line — friendly denial / nearest-spot message. Neutral by default, danger on error. */
.loc-note { margin: 0; font-size: var(--fs-sm); color: var(--ink-soft); line-height: var(--lh-body); }
.loc-note.error { color: var(--danger); font-weight: var(--fw-bold); }

@media (max-width: 520px) {
  .loc-actions .btn { flex: 1 1 100%; }
  .loc-manual .text-input { flex-basis: 100%; }
  .loc-manual .btn-send { flex: 1 1 100%; }
}

/* Larger phones / small screens: stack the composer. (Text size is controlled by the
   Preferences slider via --font-scale now, not a per-breakpoint root override.) */
@media (max-width: 520px) {
  .text-input { flex-basis: 100%; }
  .btn-send { flex: 1; }
}

/* ---- Admin ⇄ Visitor mode gate ----
   VISITOR mode is the default and unlocked; admin controls are hidden until a correct PIN unlocks
   admin for this session only. The mode button itself is the gate and stays visible/keyboard-reachable
   so an operator can unlock; everything marked .admin-only is revealed only in admin mode. */
.admin-only { display: none; }
body.admin-mode .admin-only { display: inline-flex; }

/* In admin mode the gate reads as an active, lit control (so the operator can see they're in admin
   and exit). AA: --on-primary on --primary is 6.29:1. */
body.admin-mode .mode-btn {
  background: var(--primary); color: var(--on-primary); border-color: var(--primary);
}
body.admin-mode .mode-btn:hover { background: var(--primary-strong); border-color: var(--primary-strong); }

/* ---- Setup wizard + PIN modal ---- (reuse the .settings dialog shell + brand tokens) */
.settings textarea {
  font-size: var(--fs-lg); padding: 0.75rem 0.85rem; margin-top: var(--space-2);
  border: 2px solid var(--admin-line-2); border-radius: var(--radius-md);
  background: var(--admin-surface); color: var(--admin-ink);
  font-family: inherit; line-height: var(--lh-body); resize: vertical; width: 100%;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.settings textarea:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft);
}
.setup-progress { margin-top: 0; font-weight: var(--fw-medium); color: var(--admin-ink-soft); }
.setup-step { display: flex; flex-direction: column; }
.setup-intake-lead { margin-top: 0; }

/* Guided intake conversation (setup): a compact chat inside the wizard. Reuses the
   conversation bubble styles but sized for the dialog. */
.intake-log {
  margin-top: var(--space-3);
  max-height: 38vh; min-height: 8rem; overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-3);
  background: var(--admin-surface-2); border: 1px solid var(--admin-line); border-radius: var(--radius-md);
}
.intake-log .bubble { max-width: 100%; font-size: var(--fs-md); padding: 0.7rem 0.9rem; box-shadow: none; }
.intake-log:empty { display: none; }
.intake-input-row { display: flex; gap: var(--space-2); align-items: stretch; margin-top: var(--space-3); }
.intake-input-row .text-input { flex: 1 1 auto; font-size: var(--fs-md); margin-top: 0; }
.intake-send { flex: 0 0 auto; min-height: 0; }
/* Errors/alerts in setup + PIN: semantic danger (6.5:1 on white), never brand indigo/coral. */
.setup-err { color: var(--danger); font-weight: var(--fw-bold); }
.pin-modal { max-width: 420px; }

@media (prefers-reduced-motion: reduce) {
  .settings textarea { transition: none; }
}

/* ============================================================================
   PREFERENCES PANEL — one discoverable home (opened by the gear in the header) for the
   user-controllable settings. It reuses the .settings <dialog> shell + brand tokens. The
   font-size slider lives here; the conversation/voice/location/profile controls are surfaced
   as sections that delegate to the SAME existing logic in app.js (no duplicated behaviour).
   The guide is public, so Preferences is available to everyone, signed in or not.
   ============================================================================ */
.prefs-modal { max-width: 560px; }
.prefs-section { display: flex; flex-direction: column; gap: var(--space-2); }
.prefs-section + .prefs-section { margin-top: var(--space-4); }

/* Section heading — reuses the account-subhead rule (route-line under the brand). */
.prefs-subhead {
  margin: var(--space-5) 0 var(--space-1); font-family: var(--font-brand);
  font-size: var(--fs-lg); color: var(--brand-heading); font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-brand);
  border-top: 1px solid var(--line); padding-top: var(--space-4);
}
.prefs-section:first-of-type .prefs-subhead { border-top: none; padding-top: 0; margin-top: 0; }

/* ---- Text-size slider ---- */
.font-scale-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.font-scale-value {
  font-variant-numeric: tabular-nums; font-weight: var(--fw-bold);
  font-size: var(--fs-md); color: var(--primary-strong);
  min-width: 4ch; text-align: right;
}
.font-scale-row { display: flex; align-items: center; gap: var(--space-3); }
.font-scale-row .fs-small { font-size: var(--fs-xs); color: var(--ink-muted); font-weight: var(--fw-bold); }
.font-scale-row .fs-large { font-size: var(--fs-lg); color: var(--ink-muted); font-weight: var(--fw-bold); }
input[type="range"].font-scale {
  flex: 1; min-width: 0; height: 44px;          /* 44px hit target */
  accent-color: var(--primary);                  /* indigo track/thumb (modern browsers) */
  cursor: pointer; background: transparent;
}
input[type="range"].font-scale:focus-visible { outline: 4px solid var(--focus); outline-offset: 3px; }
/* Live preview line — scales with the slider because it is rem-based like the rest of the app. */
.font-scale-sample {
  margin: var(--space-2) 0 0; padding: var(--space-3) var(--space-4);
  background: var(--guide-bubble); color: var(--guide-bubble-ink);
  border-radius: var(--radius-md); font-size: var(--fs-md); line-height: var(--lh-body);
}
.font-scale-reset { align-self: flex-start; min-height: 44px; margin-top: var(--space-1); }

/* ---- Auto-play / location rows inside Preferences ---- */
.prefs-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.prefs-hint { margin: 0; font-size: var(--fs-sm); color: var(--admin-ink-muted); line-height: var(--lh-body); }
.prefs-loc-status { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--ink-soft); }
.prefs-section select {
  font-size: var(--fs-md); padding: 0.7rem 0.85rem;
  border: 2px solid var(--admin-line-2); border-radius: var(--radius-md);
  background: var(--admin-surface); color: var(--admin-ink); width: 100%;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.prefs-section select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.prefs-actions-inline { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.prefs-actions-inline .btn { min-height: 48px; font-size: var(--fs-sm); }

@media (prefers-reduced-motion: reduce) {
  .prefs-section select { transition: none; }
}

/* ============================================================================
   PRICING / PLANS PAGE (/pricing) — the tiering + upgrade CTA surface, carried over
   from meridian's billing page structure (a grid of plan cards with a highlighted
   middle plan, a per-tier feature list, and one primary CTA per card) and re-skinned
   in the Dusk Wander system. Reuses the brand tokens — no new colours. The guide stays
   free; this page only surfaces the optional paid tiers.
   ============================================================================ */
.pricing-stage { gap: var(--space-5); padding-bottom: var(--space-6); overflow-y: auto; }

.pricing-intro { text-align: center; max-width: 44rem; margin: var(--space-5) auto 0; }
.pricing-title {
  margin: 0; font-family: var(--font-brand); font-size: var(--fs-2xl); line-height: var(--lh-tight);
  font-weight: var(--fw-black); letter-spacing: var(--tracking-brand); color: var(--brand-heading);
}
.pricing-sub { margin: var(--space-3) 0 0; font-size: var(--fs-lg); color: var(--ink-soft); line-height: var(--lh-body); }

/* The card grid — three equal columns on desktop, stacking on narrow screens. */
.pricing-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4);
  width: 100%; max-width: 60rem; margin: 0 auto; align-items: stretch;
}
@media (max-width: 860px) { .pricing-grid { grid-template-columns: 1fr; max-width: 28rem; } }

.plan-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--surface); border: 2px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--elev-1);
  padding: var(--space-5);
}
/* The recommended middle plan — accent ring + lift, mirroring meridian's highlighted tier. */
.plan-card-featured {
  border-color: var(--primary); box-shadow: var(--elev-2);
}
.plan-badge {
  position: absolute; top: calc(-1 * var(--space-3)); left: 50%; transform: translateX(-50%);
  background: var(--accent-brand); color: #fff;
  font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: 0.04em;
  padding: 0.3rem 0.8rem; border-radius: var(--radius-pill); white-space: nowrap;
  box-shadow: var(--elev-1);
}

.plan-name {
  margin: 0; font-family: var(--font-brand); font-size: var(--fs-xl);
  font-weight: var(--fw-bold); letter-spacing: var(--tracking-brand); color: var(--brand-heading);
}
.plan-price { margin: var(--space-2) 0 0; display: flex; align-items: baseline; gap: 0.2rem; }
.plan-amount { font-size: var(--fs-3xl); font-weight: var(--fw-black); color: var(--ink-strong); line-height: 1; }
.plan-period { font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--ink-muted); }
.plan-hook { margin: var(--space-3) 0 0; font-size: var(--fs-sm); color: var(--ink-soft); line-height: var(--lh-body); min-height: 2.6em; }

.plan-features { list-style: none; margin: var(--space-4) 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); flex: 1; }
.plan-feature { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--fs-md); color: var(--ink-strong); line-height: var(--lh-body); }
.plan-check {
  flex: 0 0 auto; margin-top: 0.1rem; width: 1.4rem; height: 1.4rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-soft); color: var(--primary-strong);
  border-radius: var(--radius-pill); font-size: var(--fs-sm); font-weight: var(--fw-bold);
}

/* CTA per card — reuses .btn / .btn-primary / .btn-ghost; full width, capped height. */
.plan-cta { width: 100%; min-height: 56px; margin-top: auto; }

.pricing-notice {
  max-width: 60rem; margin: 0 auto; text-align: center;
  background: var(--primary-tint); color: var(--primary-strong);
  border: 1px solid var(--primary-soft); border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4); font-size: var(--fs-md); font-weight: var(--fw-medium);
}
.pricing-foot { max-width: 44rem; margin: 0 auto var(--space-5); text-align: center; font-size: var(--fs-sm); color: var(--ink-muted); }
