/* Sky Map — styled against The Dojo App's live typography settings and design
 * roles: Work Sans (800) headings, Roboto body, Lato reserved for small
 * "eyebrow" labels — plus the teal/gold roles over a charcoal raised material.
 * Dark theme only here — a night-sky app has no reason to ever go light. */

:root {
  /* ---- Fonts (per The Dojo App's Typography settings) ---- */
  --font-heading: 'Work Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-body: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-eyebrow: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* ---- Layer 1: ramps (raw hue, from dojo-palette.css) ---- */
  --teal-300: #6FE3D6;
  --teal-500: #109F93;
  --teal-600: #0B746B;
  --gold-500: #D9A441;
  --danger-500: #E0575D;
  --neutral-400: #9DABA9;
  --neutral-900: #171C1C;

  /* ---- Layer 2: roles (dark theme values) ---- */
  --surface-page: #0F171D;
  --surface-raised: linear-gradient(180deg, #323B44 0%, #232B33 40%, #1A2128 100%);
  --surface-sunken: var(--neutral-900);
  --surface-overlay: rgba(6, 12, 15, 0.93);
  --text-primary: #F2F6F5;
  --text-secondary: #9AA6A4;
  --text-muted: var(--neutral-400);
  --text-disabled: #758A89;
  --accent: var(--teal-500);
  --accent-text: var(--teal-300);
  --accent-quiet: var(--teal-600);
  --accent-wash: rgba(16, 159, 147, 0.12);
  --on-accent: #04241F;
  --gold: var(--gold-500);
  --gold-wash: rgba(217, 164, 65, 0.07);
  --danger: var(--danger-500);
  --border: #1B2B33;
  --border-strong: #3A434B;
  --border-lip: #0E1216;
  --border-raised: #3A434B;
  --shadow-raised: 0 1px 0 rgba(255,255,255,.06) inset, 0 3px 4px rgba(0,0,0,.45);

  /* ---- Motion: one shared easing curve + two durations for every
     screen/panel switch and popup in the app (see showPanel()/hidePanel()
     in app.js). cubic-bezier(.4,0,.2,1) is Material Design's "standard"
     curve — quick to start, gentle landing — the same shape iOS's default
     UIView animation curve approximates too, which is why it reads as
     generically "modern phone UI" rather than any one platform's. ---- */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-base: 220ms;  /* panels: guide list, settings, info card, permission help */
  --motion-fast: 160ms;  /* small popups: toast, zoom pill, compass widget */
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: #000;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-weight: 400;
  overflow: hidden;
  overscroll-behavior: none;
}

/* Titles and headings read as light-weight, uppercase display type
   throughout the app (deliberately the opposite of a normal bold-heading
   convention) — every h1-h4 gets this by default; specific headings only
   override size/color/spacing below, not weight or case, so a future new
   heading picks up the same look automatically without needing its own
   rule. */
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 300;
  text-transform: uppercase;
}

.hidden { display: none !important; }

/* ---------------------------------------------------------------------
   Motion: fade+micro-slide open/close for panels and popups, driven by
   showPanel()/hidePanel() in app.js. .hidden itself is untouched above —
   these are separate opt-in classes scoped to specific elements below,
   so nothing else that already uses .hidden (buttons, list rows, badges)
   is affected. The sequence per element: showPanel() removes .hidden,
   then briefly applies .panel-enter (opacity 0 + offset) before letting
   it transition back to the element's normal state; hidePanel() applies
   .panel-exit (same 0/offset look) and only adds .hidden back once the
   transition has actually finished, so the fade-out has time to play
   instead of being cut off by an instant display:none.
   --------------------------------------------------------------------- */
#guide-panel, #settings-panel, #info-panel, #permission-help, #icon-guide {
  transition: opacity var(--motion-base) var(--ease-standard),
              transform var(--motion-base) var(--ease-standard);
}
#guide-panel.panel-enter, #guide-panel.panel-exit,
#settings-panel.panel-enter, #settings-panel.panel-exit,
#permission-help.panel-enter, #permission-help.panel-exit,
#icon-guide.panel-enter, #icon-guide.panel-exit {
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
}
/* Info panel reads as a "card" being presented, not a sheet sliding up —
   a touch of scale alongside the slide sells that a little better. */
#info-panel.panel-enter, #info-panel.panel-exit {
  opacity: 0;
  transform: translateY(10px) scale(0.97);
  pointer-events: none;
}

/* Small popups — quicker and lighter than the panels above, since they're
   incidental status (a toast, the research-mode compass) rather than a
   screen the user is actively navigating into. */
#mode-toast, #compass-widget {
  transition: opacity var(--motion-fast) var(--ease-standard),
              transform var(--motion-fast) var(--ease-standard);
}
#mode-toast.panel-enter, #mode-toast.panel-exit,
#compass-widget.panel-enter, #compass-widget.panel-exit {
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
}

/* ---------- Viewer frame ----------
   A slim white edge around the entire screen, on top of everything else,
   for that "you're looking through a viewfinder" feel. See the HTML
   comment on #viewer-frame for why it lives as its own top-level layer
   rather than a border on any one screen. */
#viewer-frame {
  position: fixed;
  inset: 0;
  z-index: 200;
  border: 3px solid #fff;
  pointer-events: none;
}

/* ---------- Start screen ---------- */
.overlay-screen {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: var(--surface-page);
  z-index: 50;
  padding: 24px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  transition: opacity var(--motion-base) var(--ease-standard);
}

/* Boot crossfade: the start screen fades out while #app fades in on top
   of it (see the JS right after bootApp() computes `stages` — it adds
   .fade-exit here and .app-visible on #app in the same tick), instead of
   the old instant classList swap. #start-screen keeps rendering (still
   display:flex, .hidden only gets added after the fade finishes) so the
   two overlap briefly rather than jump-cutting. */
.overlay-screen.fade-exit { opacity: 0; pointer-events: none; }

#app {
  transition: opacity var(--motion-base) var(--ease-standard);
  opacity: 0; /* only matters for the split-second between .hidden being removed and .app-visible being added right after — see bootApp() */
}
#app.app-visible { opacity: 1; }

/* Looping background video behind the start card — a self-hosted,
   muted/autoplaying <video> (a licensed stock clip bundled in media/). A
   dark scrim on top keeps the start-card text readable and dims the video
   by roughly 30%. Both layers are pointer-events: none so they never
   intercept taps meant for the buttons above them. */
.start-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
  background: #000;
}

.start-bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: none;
  pointer-events: none;
}

/* The rotating remote clip sits on top of the locally-bundled base clip,
   invisible until it's actually loaded and playing (see startBgRotation()
   in app.js) — a slow network never shows a flash of black or a broken
   frame, just a delayed crossfade. */
.start-bg-rotator {
  z-index: 1;
  opacity: 0;
  transition: opacity 1.4s ease;
}
.start-bg-rotator.visible { opacity: 1; }

.start-bg-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(11, 15, 20, 0.3);
  pointer-events: none;
}

.start-card {
  position: relative;
  z-index: 2;
  max-width: 420px;
  width: 100%;
  text-align: center;
  margin: auto 0;
  padding-bottom: 24px;
}

.start-card h1 {
  font-size: 56px;
  letter-spacing: 1px;
  text-align: center;
  margin-bottom: 12px;
  color: var(--text-primary);
}

.start-card p {
  color: var(--text-secondary);
  line-height: 1.5;
  margin: 8px 0;
}

.start-card .lead {
  font-size: 32px; /* 2× the default 16px body copy */
  line-height: 1.35;
}

.start-card .hint {
  font-size: 13px;
  color: var(--text-muted);
  font-weight: 300;
}

/* ---- Buttons — dojo-buttons.css idiom: charcoal raised material, lifts to
   teal on hover, 7px radius, body font bold with letter-spacing. All three
   start-screen actions (Start, Constellation Guide, Permission trouble)
   share this one base so they read as a single deliberate button stack,
   ranked primary → secondary → tertiary by visual weight rather than by
   underlined-link vs. real-button, which is what they were before. ---- */
.cta-btn {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 14px 28px;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  text-align: center;
  border-radius: 7px;
  cursor: pointer;
}

.cta-btn-primary {
  margin-top: 20px;
  color: var(--text-primary);
  background: var(--surface-raised);
  border: 1px solid var(--border-raised);
  box-shadow: var(--shadow-raised);
}

.cta-btn-primary:hover {
  background: var(--teal-300);
  color: var(--on-accent);
  border-color: rgba(16, 159, 147, 0.5);
}

.cta-btn-primary:active {
  transform: translateY(1px);
  box-shadow: 0 1px 2px rgba(0,0,0,.5) inset;
}

.cta-btn-primary:disabled {
  opacity: 0.6;
  cursor: default;
}

.cta-btn-secondary {
  color: var(--accent-text);
  background: var(--accent-wash);
  border: 1px solid rgba(16, 159, 147, 0.35);
}

.cta-btn-secondary:hover {
  background: var(--teal-300);
  color: var(--on-accent);
  border-color: rgba(16, 159, 147, 0.6);
}

.cta-btn-tertiary {
  padding: 10px 20px;
  font-size: 12px;
  letter-spacing: .5px;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
}

.cta-btn-tertiary:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.4);
}

.error-text {
  color: var(--danger);
  font-size: 13px;
  min-height: 18px;
}

.privacy-note {
  margin-top: 16px;
  padding: 12px 14px;
  background: var(--accent-wash);
  border: 1px solid rgba(16, 159, 147, 0.3);
  border-radius: 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-secondary);
  text-align: left;
}

/* ---- Permission help — .key-card idiom: raised surface, 12px radius ---- */
#permission-help {
  margin-top: 12px;
  text-align: left;
  background: var(--surface-raised);
  border: 1px solid var(--border-raised);
  border-radius: 12px;
  box-shadow: var(--shadow-raised);
  padding: 16px 18px;
}

.help-block {
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.help-block:last-of-type {
  border-bottom: none;
}

.help-block h3 {
  margin: 0 0 6px;
  font-size: 14px;
  color: var(--text-primary);
}

.help-why {
  font-size: 12.5px;
  color: var(--text-muted);
  font-weight: 300;
  margin: 0 0 8px;
}

.help-os {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 10px 0 4px;
  line-height: 1.5;
}

.help-os strong {
  color: var(--accent-text);
  font-family: var(--font-eyebrow);
  font-weight: 700;
  letter-spacing: .4px;
}

#permission-help ol {
  margin: 4px 0 8px;
  padding-left: 20px;
  font-size: 12.5px;
  color: var(--text-secondary);
  line-height: 1.6;
}

#permission-help li {
  margin-bottom: 2px;
}

#permission-help li strong { color: var(--accent-text); }

.help-hint {
  font-size: 12px;
  color: var(--text-muted);
  font-weight: 300;
  margin-top: 10px;
  margin-bottom: 0;
}

.help-hint-top {
  margin-top: 0;
  margin-bottom: 10px;
}

/* ---- Live vs Research mode switch ----
   Two large buttons rather than the smaller platform-switch style below —
   this is a bigger decision (changes what permissions get asked for and
   how the whole view is driven), so it gets more visual weight. */
.mode-switch {
  display: flex;
  gap: 10px;
  margin: 14px 0;
}

.mode-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 12px 8px;
  font-family: var(--font-body);
  color: var(--text-secondary);
  /* Same raised-card material as .cta-btn-primary (Start Sky Map) —
     gradient + border + drop shadow, rather than the flatter "sunken"
     look these used before, so the two mode choices read as solid,
     tappable buttons at the same visual weight as the CTAs below them. */
  background: var(--surface-raised);
  border: 1px solid var(--border-raised);
  box-shadow: var(--shadow-raised);
  border-radius: 10px;
  cursor: pointer;
}

.mode-btn-icon {
  font-size: 20px;
  line-height: 1.2;
}

.mode-btn-sub {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-muted);
  text-align: center;
}

.mode-btn.active {
  color: var(--on-accent);
  background: var(--teal-300);
  border-color: rgba(16, 159, 147, 0.5);
}
.mode-btn.active .mode-btn-sub { color: var(--on-accent); opacity: 0.85; }

.mode-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ---- Platform switch — auto-picked, manually overridable ---- */
.platform-switch {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.platform-btn {
  flex: 1;
  padding: 8px 6px;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  cursor: pointer;
}

.platform-btn.active {
  color: var(--on-accent);
  background: var(--teal-300);
  border-color: rgba(16, 159, 147, 0.5);
}

/* ---- Real-photo permission walkthrough ----
   Built from actual device screenshots (not illustrated) supplied by the
   app owner. Each step blurs/dims the full screenshot and cuts a sharp,
   rounded "spotlight" window over just the row that needs a tap, via two
   stacked <img> copies of the same screenshot: one blurred base, one sharp
   copy clipped with clip-path to the target row. Steps flagged `scroll`
   pan in from further up the *same* image (there's no separate "top of
   list" screenshot) to suggest the scroll motion needed to reach that row. */
.photo-walk {
  margin: 0 auto 18px;
  max-width: 300px;
}

.photo-walk-viewport {
  position: relative;
  width: 100%;
  height: 340px;
  margin: 0 auto;
  border-radius: 20px;
  overflow: hidden;
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-raised);
}

.pw-frame {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s ease;
}
.pw-frame.active {
  opacity: 1;
  visibility: visible;
}

.pw-img-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.pw-img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: auto;
  display: block;
  will-change: transform;
}

.pw-img-blurred {
  filter: blur(5px) brightness(0.55);
  transform: translateY(0);
}

.pw-img-sharp {
  filter: none;
}

.pw-tap-dot {
  position: absolute;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  background: rgba(217, 164, 65, 0.35);
  border: 2px solid rgba(217, 164, 65, 0.9);
  opacity: 0;
  pointer-events: none;
  animation: pw-tap-pulse 1.4s ease-out infinite;
}

@keyframes pw-tap-pulse {
  0% { transform: scale(0.85); box-shadow: 0 0 0 0 rgba(217, 164, 65, 0.5); }
  70% { transform: scale(1); box-shadow: 0 0 0 12px rgba(217, 164, 65, 0); }
  100% { transform: scale(0.85); box-shadow: 0 0 0 0 rgba(217, 164, 65, 0); }
}

.pw-caption {
  margin: 10px 0 6px;
  text-align: center;
  font-size: 12.5px;
  color: var(--text-secondary);
}

.pw-progress {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-bottom: 4px;
}
.pw-progress span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border-strong);
  transition: background 0.25s ease;
}
.pw-progress span.active {
  background: var(--teal-500);
}

@media (prefers-reduced-motion: reduce) {
  .pw-img { transition: none !important; }
  .pw-tap-dot { animation: none; opacity: 1; }
}

/* ---- Supplementary video links ----
   Tapping one of these pops a real YouTube embed open right underneath,
   in place, instead of leaving the app — dismiss with the same button or
   the ✕ on the popup. */
.help-video-link {
  margin: 10px 0 0;
  font-size: 13px;
  text-align: center;
}
.help-video-link-secondary {
  color: var(--text-muted);
  text-align: left;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
}
.video-link-note {
  color: inherit;
}

.video-link-btn {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--teal-500);
  cursor: pointer;
  text-decoration: none;
}
.video-link-btn:hover,
.video-link-btn:focus-visible {
  text-decoration: underline;
}
.video-link-btn.active {
  color: var(--text-primary);
}

.video-popup {
  position: relative;
  margin: 10px auto 0;
  max-width: 300px;
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow-raised);
}
.video-popup[data-aspect="portrait"] { aspect-ratio: 9 / 16; }
.video-popup[data-aspect="landscape"] { aspect-ratio: 16 / 9; max-width: 100%; }

/* The step-video-mount is the SAME video-popup box, just always-on (no
   close button, nothing to dismiss — it IS the step, not an aside). Give
   it a little more breathing room since it's the main event now. */
.step-video-mount.video-popup {
  margin: 14px auto 4px;
  max-width: 320px;
}
.step-video-mount:empty { display: none; } /* no video mapped for this OS yet */

/* ---- "Prefer written steps?" fallback ----
   Collapsed by default so the video is the only thing on screen at
   first — this is where the old numbered instructions/copy-paste hints
   live now, for anyone who'd rather read than watch. */
.step-fallback {
  margin-top: 16px;
  text-align: left;
}
.step-fallback summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  color: var(--text-muted);
  text-align: center;
  list-style: none;
  padding: 8px 0;
}
.step-fallback summary::-webkit-details-marker { display: none; }
.step-fallback summary::before { content: '▸ '; }
.step-fallback[open] summary::before { content: '▾ '; }
.step-fallback[open] summary { color: var(--text-secondary); }

.help-block-note {
  text-align: center;
  font-size: 14px;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px;
  margin: 12px 0;
}

.video-popup iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.video-popup-close {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 1;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

/* ---- Step-by-step permission wizard progress ----
   Only the current permission's help-block is shown at a time; this is
   the "Step X of Y" readout + jump-to-step dots sitting above it. */
.wizard-progress {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 4px 0 16px;
  flex-wrap: wrap;
}

.wizard-progress-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
}

.wizard-progress-dots {
  display: flex;
  gap: 6px;
}

.wizard-progress-dots button {
  width: 9px;
  height: 9px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--border-strong);
  cursor: pointer;
  transition: width 0.2s ease, border-radius 0.2s ease, background 0.2s ease;
}
.wizard-progress-dots button.done {
  background: var(--teal-300);
}
.wizard-progress-dots button.active {
  background: var(--teal-500);
  width: 22px;
  border-radius: 5px;
}

.help-wizard-done {
  text-align: center;
}

/* ---- Per-permission head row + live status badge ---- */
.help-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}

.help-head h3 { margin: 0; }

.perm-status {
  flex-shrink: 0;
  font-family: var(--font-eyebrow);
  font-size: 10.5px;
  font-weight: 900;
  letter-spacing: .3px;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 3px 9px;
}

.perm-status.perm-granted { color: var(--on-accent); background: var(--teal-300); border-color: transparent; }
.perm-status.perm-denied { color: var(--text-primary); background: var(--danger); border-color: transparent; }
.perm-status.perm-prompt { color: var(--text-secondary); }

/* ---- Wizard steps — numbered, one action per card ---- */
.wizard-steps {
  margin: 8px 0;
}

.wizard-step {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 8px 0;
}

.wizard-step + .wizard-step {
  border-top: 1px dashed var(--border);
}

.step-num {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent-quiet);
  color: var(--text-primary);
  font-family: var(--font-eyebrow);
  font-size: 11px;
  font-weight: 900;
  display: flex;
  align-items: center;
  justify-content: center;
}

.step-body { flex: 1; min-width: 0; }

.step-body p {
  margin: 0 0 6px;
  font-size: 12.5px;
  color: var(--text-secondary);
  line-height: 1.55;
}

.step-body p:last-child { margin-bottom: 0; }

.step-body strong { color: var(--accent-text); }

.copy-btn {
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 700;
  color: var(--accent-text);
  background: var(--accent-wash);
  border: 1px solid rgba(16, 159, 147, 0.3);
  border-radius: 7px;
  padding: 5px 10px;
  cursor: pointer;
}

.copy-hint {
  margin: 6px 0 0 !important;
  font-size: 11px !important;
  font-style: italic;
  color: var(--text-muted) !important;
}

/* ---- Retry ---- */
.retry-btn {
  display: block;
  width: 100%;
  margin-top: 10px;
  padding: 10px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
  background: var(--surface-raised);
  border: 1px solid var(--border-raised);
  border-radius: 7px;
  box-shadow: var(--shadow-raised);
  cursor: pointer;
}

.retry-btn:hover { background: var(--teal-300); color: var(--on-accent); border-color: rgba(16, 159, 147, 0.5); }
.retry-btn:disabled { opacity: 0.6; cursor: default; }

.retry-feedback {
  font-size: 12px;
  margin: 6px 0 0;
  min-height: 16px;
  color: var(--text-muted);
}

.retry-feedback.retry-ok { color: var(--teal-300); }
.retry-feedback.retry-bad { color: var(--danger); }

/* ---- Flash highlight when a failed permission auto-scrolls into view ---- */
@keyframes help-flash {
  0%, 100% { background: transparent; }
  25%, 75% { background: var(--accent-wash); }
}

.help-flash {
  animation: help-flash 1.6s ease-in-out;
  border-radius: 8px;
}

/* ---------- App ---------- */
#app {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: #000;
}

#camera-feed {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

#sky-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  touch-action: none;
}

#top-bar {
  position: absolute;
  top: env(safe-area-inset-top, 12px);
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  z-index: 10;
  pointer-events: none;
}

/* ---- .key-bar idiom: raised surface, 7px radius, for compact chrome ---- */
#status-pill {
  pointer-events: auto;
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 6px 14px;
  font-size: 12px;
  font-family: var(--font-body);
  color: var(--text-secondary);
}

.top-bar-actions {
  display: flex;
  gap: 8px;
}

#mode-toggle-btn, #settings-btn, #guide-btn-app, #fullscreen-btn, #grid-btn, #icon-guide-btn {
  pointer-events: auto;
  width: 36px;
  height: 36px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  color: var(--text-primary);
  font-size: 16px;
  cursor: pointer;
}

#mode-toggle-btn:hover:not(:disabled), #settings-btn:hover, #guide-btn-app:hover, #fullscreen-btn:hover, #grid-btn:hover, #icon-guide-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

#mode-toggle-btn.active, #settings-btn.active, #grid-btn.active {
  color: var(--accent-text);
  background: rgba(16, 159, 147, 0.18);
  border-color: rgba(16, 159, 147, 0.65);
}

#mode-toggle-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Brief status feedback for the mode-toggle button above — in-progress
   or failed mode-switch messages. Sits just under the top bar so it
   never covers the sky itself; success simply clears it (see
   showModeToast('') in app.js), so most of the time it's invisible. */
#mode-toast {
  position: absolute;
  top: calc(env(safe-area-inset-top, 12px) + 56px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  max-width: min(86vw, 360px);
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 8px 14px;
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-secondary);
  text-align: center;
  pointer-events: none;
}

/* ---- Research-mode compass widget ----
   Only shown in Research mode (no camera, no real compass) — the needle
   rotates in app.js's loop() to always point toward state.forward, so
   this is the one visual cue for "which way am I currently looking."
   Styled after a real chrome-cased pocket compass — a matte black dial,
   a polished/brushed chrome bezel, white-on-black tick marks and cardinal
   letters, and the classic red-tip/white-tail needle color coding — per
   an explicit reference photo, rather than the brass/gold language used
   everywhere else in the app (buttons, the horizon compass wheel). That's
   a deliberate one-widget exception, not an oversight — see the build
   notes for the call to unify or keep them distinct. Entirely
   pointer-events:none (nothing here is interactive) so it never competes
   with dragging the sky underneath.

   Anchored to the bottom-right corner (rather than upper-right, where it
   used to sit right in the middle of where objects are actually being
   viewed) and scaled to exactly half its original 112px dial via a plain
   `transform: scale(.5)` — simplest way to shrink every layered piece
   (dial, ticks, bezel, needle, hub, readout) uniformly and in lockstep,
   with zero risk of any one of them drifting out of proportion the way
   re-deriving ~15 separate measurements by hand would risk.
   `transform-origin: bottom right` keeps it hugging that same corner as
   it shrinks, instead of shrinking toward its own center and leaving a
   gap. Still just a glance-and-go "which way am I facing" cue, not a
   precision instrument — see the needle-specific contrast/glow boost
   below, which exists specifically so that cue doesn't get lost now that
   the whole widget reads smaller and sits out of the way. */
#compass-widget {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom, 16px) + 14px);
  right: 14px;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;
  transform: scale(0.5);
  transform-origin: bottom right;
}

/* Lanyard loop — a small chrome ring peeking above the case, the way a
   real pocket compass's carry-loop does in the reference photo. Purely
   decorative (an open ring via a transparent-fill circle with a gradient
   border) but it's what tips the widget from "a dial" to "an object." */
#compass-loop {
  width: 13px;
  height: 9px;
  margin-bottom: -5px;
  border-radius: 50% / 60%;
  border: 3px solid;
  border-image: linear-gradient(135deg, #f6f8fa, #7b8087 55%, #4d5156) 1;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.5);
}

/* The dial: a matte-black face (radial-gradient, darkest at the rim like
   a domed lens), two tiers of hairline tick marks — a fine one every 10°
   and a bolder one every 45° lining up with the cardinal/intercardinal
   letters (both via repeating-conic-gradient, cheap to render, no need
   for 24+ separate tick divs) — a brushed-chrome bezel (::after, a
   conic-gradient sweeping through several grays and near-whites to fake
   polished-metal reflections, plus a fine knurled ridge texture layered
   on top of it), and a glass "glare" highlight (::before, offset toward
   the upper-left) that sells the domed-glass feel over the dial itself. */
#compass-ring {
  position: relative;
  width: 112px;
  height: 112px;
  border-radius: 50%;
  background:
    repeating-conic-gradient(rgba(232, 236, 240, 0.4) 0deg 0.6deg, transparent 0.6deg 10deg),
    repeating-conic-gradient(rgba(255, 255, 255, 0.55) 0deg 1deg, transparent 1deg 45deg),
    radial-gradient(circle at 50% 50%, #1c1c1f 0%, #101012 65%, #030303 100%);
  border: 2px solid #9aa0a6;
  box-shadow:
    0 8px 20px rgba(0, 0, 0, 0.55),
    0 0 0 5px rgba(210, 214, 218, 0.10),
    inset 0 2px 5px rgba(255, 255, 255, 0.12),
    inset 0 -10px 18px rgba(0, 0, 0, 0.6);
}
#compass-ring::before {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 24%, rgba(255, 255, 255, 0.30), rgba(255, 255, 255, 0) 42%);
  pointer-events: none;
}
#compass-ring::after {
  content: '';
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  background:
    repeating-conic-gradient(rgba(20, 20, 22, 0.45) 0deg 1deg, rgba(255, 255, 255, 0.2) 1deg 2deg),
    conic-gradient(from 220deg, #4a4d52, #eef1f4 25%, #9a9fa6 45%, #f6f8fa 65%, #6a6e74 85%, #d4d8dc 100%);
  z-index: -1;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.6), inset 0 0 0 2px rgba(0, 0, 0, 0.35);
}

.compass-tick {
  position: absolute;
  font-family: var(--font-eyebrow);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .2px;
  color: rgba(232, 236, 240, 0.65);
}
/* Cardinals — bolder and bigger than the intercardinals, all printed
   white like the reference compass's face (no gold pick-out here — that
   accent belongs to the needle instead, see below); N gets a faint
   red-tinted glow, echoing the needle's red tip rather than introducing
   a third color of its own. */
.compass-n, .compass-s, .compass-e, .compass-w {
  font-size: 12px;
  font-weight: 800;
  color: #eef1f4;
}
.compass-n { top: 7px; left: 50%; transform: translateX(-50%); color: #ffffff; text-shadow: 0 0 6px rgba(224, 58, 48, 0.5); }
.compass-s { bottom: 7px; left: 50%; transform: translateX(-50%); }
.compass-e { right: 9px; top: 50%; transform: translateY(-50%); }
.compass-w { left: 9px; top: 50%; transform: translateY(-50%); }
.compass-ne { top: 21px; right: 21px; }
.compass-se { bottom: 21px; right: 21px; }
.compass-sw { bottom: 21px; left: 21px; }
.compass-nw { top: 21px; left: 21px; }

/* Needle — two tapered triangles sharing one pivot: a glowing red tip
   (the half pointing at the current heading) and a white/silver tail
   (counterweight) — the classic red/white magnetic-needle color coding
   from the reference photo, replacing the prior gold/gray needle so
   "which end means which way" still reads unambiguously at a glance.
   Nudged slightly larger (7px/42px -> 8px/45px) and given a brighter,
   wider-spread glow than the rest of the dial's restrained, low-key
   styling — a deliberate, small exception, made specifically because
   this widget now renders at half its old size (see the `transform:
   scale(.5)` on #compass-widget) and the needle is the one piece of it
   that actually answers "which way am I facing." Kept modest on
   purpose: a size bump and a stronger shadow, not a redesign or a new
   color. */
#compass-needle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 45px;
  margin: -45px 0 0 -4px;
  transform-origin: 50% 45px;
  z-index: 1;
}
#compass-needle::before, #compass-needle::after {
  content: '';
  position: absolute;
  left: 0;
  width: 8px;
}
#compass-needle::before {
  top: 0;
  height: 26px;
  background: linear-gradient(to bottom, #ff7a68, #d81f16);
  clip-path: polygon(50% 0%, 100% 100%, 0% 100%);
  filter: drop-shadow(0 0 7px rgba(224, 45, 35, 0.75));
}
#compass-needle::after {
  bottom: 0;
  height: 19px;
  background: linear-gradient(to top, #f4f6f8, #aeb4b9);
  clip-path: polygon(50% 100%, 100% 0%, 0% 0%);
}

/* Pivot hub — a small chrome dome pinning the needle to the dial's
   center, matching the bezel's brushed-metal finish instead of brass. */
#compass-hub {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 11px;
  height: 11px;
  margin: -5.5px 0 0 -5.5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #ffffff, #c7ccd1 55%, #55585d 100%);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.65);
  z-index: 2;
}

/* Readout plaque — heading + altitude sharing one glass panel below the
   dial, rather than altitude standing alone like before, so the two
   numbers that answer "which way, and how high" read as one instrument
   reading rather than two unrelated labels. Border tuned to the same
   cool chrome tone as the bezel above it, rather than the gold wash used
   elsewhere in the app, so the plaque reads as part of the same
   instrument rather than a mismatched label. */
#compass-readout {
  display: flex;
  align-items: baseline;
  gap: 6px;
  background: linear-gradient(180deg, rgba(30, 39, 46, 0.92), rgba(12, 18, 23, 0.92));
  backdrop-filter: blur(8px);
  border: 1px solid rgba(210, 214, 218, 0.35);
  border-radius: 8px;
  padding: 4px 11px;
  box-shadow: var(--shadow-raised);
  white-space: nowrap;
}
#compass-heading {
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: .2px;
}
#compass-readout-sep { color: var(--text-muted); font-size: 11px; }
#compass-alt-readout {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent-text);
}

/* ---- Heading tape — Research mode only, see the HTML comment above
   #heading-tape-wrap for the full design reasoning. A thin, full-width
   ribbon fixed to the very top edge, above even #top-bar, matching the
   reference photo's aircraft-HUD placement. overflow:hidden here is
   what turns the much-wider #heading-tape strip into a clipped window;
   the mask-image fades both edges so ticks don't look chopped off. ---- */
#heading-tape-wrap {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 32px;
  z-index: 13;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(3, 3, 4, 0.94), rgba(3, 3, 4, 0.8));
  border-bottom: 1px solid rgba(210, 214, 218, 0.22);
  -webkit-mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 10%, black 90%, transparent);
}
body.mode-research #heading-tape-wrap { display: block; }

/* The strip itself — far wider than the viewport (three 360° loops'
   worth, built once by buildHeadingTape() in app.js) and translated
   horizontally each frame by updateHeadingTape() to keep the current
   heading centered under the fixed pointer below. */
#heading-tape {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
}

.heading-tape-tick {
  position: absolute;
  bottom: 3px;
  width: 1px;
  height: 5px;
  background: rgba(232, 236, 240, 0.32);
}
.heading-tape-tick.major { height: 8px; background: rgba(232, 236, 240, 0.55); }
.heading-tape-tick.intercardinal { height: 11px; background: rgba(232, 236, 240, 0.7); }
.heading-tape-tick.cardinal { height: 14px; width: 1.5px; background: #ffffff; }

.heading-tape-label {
  position: absolute;
  top: 3px;
  transform: translateX(-50%);
  font-family: var(--font-body);
  font-size: 10.5px;
  font-weight: 700;
  color: rgba(232, 236, 240, 0.75);
  white-space: nowrap;
}
.heading-tape-label.intercardinal { font-size: 11px; font-weight: 800; color: rgba(242, 246, 245, 0.9); }
.heading-tape-label.cardinal { font-size: 12px; font-weight: 800; color: #ffffff; }
/* N picked out in the same red the compass widget's needle tip uses —
   not gold, tying this tape to the widget's chrome/red language rather
   than the horizon wheel's gold one. */
.heading-tape-label.cardinal.n { color: #ff8b7e; text-shadow: 0 0 6px rgba(224, 58, 48, 0.55); }

/* Fixed center marker — "this is where you're currently facing," same
   role the needle's red tip plays on the dial below it. A thin glowing
   line rather than a bold triangle (the reference photo's marker) reads
   as more refined/precision-instrument at this small a scale. */
#heading-tape-pointer {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 2px;
  height: 100%;
  background: linear-gradient(180deg, #ff6b5c, rgba(255, 107, 92, 0.15));
  box-shadow: 0 0 6px rgba(220, 40, 30, 0.6);
}
#heading-tape-pointer::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid #ff6b5c;
}

/* The tape sits ABOVE #top-bar (top:0 vs. #top-bar's own safe-area
   offset), so without this, its 32px height would overlap the top-bar
   icons/status pill by more than half their own height. Pushed down by
   exactly the tape's height, Research-mode only (the tape itself is
   Research-mode-only — see body.mode-research #heading-tape-wrap
   above), and everything else still positioned relative to the bar's
   normal height (#mode-toast — #animate-clock moved down to the
   bottom-center cluster with the hamburger, and #compass-widget moved
   down to the bottom-right corner, so neither needs this anymore)
   shifts down by the same amount so their spacing below the bar stays
   visually consistent instead of suddenly crowding the now-taller
   header. */
body.mode-research #top-bar { top: calc(env(safe-area-inset-top, 12px) + 32px); }
body.mode-research #mode-toast {
  top: calc(env(safe-area-inset-top, 12px) + 56px + 32px);
}

/* Research mode: the canvas itself is the drag surface — grab/grabbing
   affordance so it reads as "click and hold to look around" rather than
   looking unresponsive. Only applied when body carries this class (see
   applyModeToBody in app.js), so Live mode's canvas is untouched. */
body.mode-research #sky-canvas { cursor: grab; }
body.mode-research #sky-canvas.dragging { cursor: grabbing; }

#crosshair {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border: 1.5px solid rgba(255,255,255,0.55);
  border-radius: 50%;
  z-index: 5;
  pointer-events: none;
}

#crosshair::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 2px;
  height: 2px;
  margin: -1px 0 0 -1px;
  background: rgba(255,255,255,0.9);
  border-radius: 50%;
}

/* ---------- "Animate over time" — Research mode only. Hidden outright
   outside Research (Live mode's whole point is a real, un-sped-up camera
   feed) via the body.mode-research selector below, rather than any JS
   .hidden toggling — appMode already reliably drives that body class (see
   applyModeUI()/enterResearchMode()/enterLiveMode() in app.js).

   Redesigned as a genuine bottom sheet — solid/opaque, not the glassy
   translucent-over-the-sky treatment most floating panels in this app
   use — specifically so the "mess of stars" behind it can't compete for
   attention while picking a date/season/speed. #animate-bar itself is
   just a full-viewport positioning host (pointer-events:none, so it
   never blocks taps on the sky behind it); its two children are
   positioned independently within it:
     - #animate-bar-handle stays fixed in one familiar spot (the same
       spot the old floating card used to occupy) whether the sheet is
       open or closed, and doubles as the sheet's own close (✕) button
       while open — see its explicit z-index, set below, which keeps it
       genuinely clickable above #animate-bar-body rather than merely
       visible underneath it.
     - #animate-bar-body is the sheet itself, pinned to the true bottom
       edge of the screen and sliding up over most of the rest of the UI
       (z-index 12) when open — #guide-status (z-index 16, only visible
       during "Show me" guidance) is the one thing that still floats
       above it.
   Starts collapsed (class="collapsed" in the HTML, matching
   state.animateBarCollapsed's default of true) so the app opens clean;
   every real choice inside the sheet closes it again automatically —
   see closeAnimatePanel()/afterPanelCloses() in app.js.

   The options box (#animate-controls: 300×/500×/800×), the date-jump row
   (#date-jump-row), the season quick-jump grid (#season-quick-row), and
   the toolbar row (#animate-toolbar-row: Star trails + Horizon grid) are
   all width:100% of the shared #animate-bar-body column, so the whole
   assembly reads as one neat, evenly-edged block rather than a stack of
   differently-sized pills.

   #animate-clock is the one exception — it lives OUTSIDE #animate-bar-
   body entirely (a third sibling alongside the handle), floating near
   the top bar instead, specifically so it (and #animate-stop-btn beside
   it) stay visible and usable while playback is running regardless of
   whether the sheet itself is open or slid away — see the HTML comment
   above #animate-bar for the full reasoning. ---- */
#animate-bar {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 12;
  pointer-events: none;
}
body.mode-research #animate-bar { display: block; }

/* Standing handle — fixed in place regardless of the sheet's open/closed
   state (see the comment above), so there's always one predictable way
   in or out without hunting for it. Sized/weighted up from its old
   icon-button treatment (see #grid-btn etc. near the top of this file)
   now that it's the sheet's only entry point, not just an optional
   collapse toggle — sized up a little from those (44px vs. 36px) since
   it's carrying more responsibility than any one of them. Toggles
   between ☰ (closed — tap to open) and ✕ (open — tap to close) via
   textContent in syncAnimateBarCollapse() (app.js), the same familiar
   hamburger/close swap most apps use. */
#animate-bar-handle {
  position: absolute;
  left: 50%;
  /* Bottom, dead-center — same baseline #guide-status uses on the left
     (env()+14px), so it reads as anchored to the screen's own bottom edge
     rather than floating at an arbitrary height. guide-status only
     appears during "Show me" guidance and sits off to the left, so the
     two rarely if ever compete for the same space. */
  bottom: calc(env(safe-area-inset-bottom, 16px) + 14px);
  transform: translateX(-50%);
  /* Explicit z-index, ABOVE #animate-bar-body (which has no z-index of
     its own — plain DOM-order stacking would otherwise paint the sheet
     over the handle, since the body comes later in the markup). Without
     this, the ✕ that's supposed to close the sheet becomes completely
     unclickable the moment the sheet is actually open — the one real
     "just get me out of here" control silently stops working right when
     it's needed, forcing a user to either make a choice they didn't
     want or hunt for some other way out. Fixed here rather than adding a
     second, redundant close control inside the sheet, since this button
     already exists for exactly that purpose (see its ☰/✕ swap in
     syncAnimateBarCollapse(), app.js) — it just needed to stay reachable. */
  z-index: 2;
  pointer-events: auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-raised);
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  color: var(--text-primary);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-raised);
}
#animate-bar-handle:hover { color: var(--accent-text); border-color: rgba(16, 159, 147, 0.5); }
#animate-bar-handle.open {
  color: var(--accent-text);
  background: rgba(16, 159, 147, 0.18);
  border-color: rgba(16, 159, 147, 0.65);
}

/* The sheet itself — solid #surface-page (the same opaque color the app's
   own background uses, not a translucent overlay) so nothing behind it
   shows through while it's open. Slides up from fully off-screen
   (translateY(110%), a hair past 100% so its drop-shadow doesn't peek
   over the bottom edge before the slide starts) with the rounded-top,
   lifted-off-the-screen treatment a native bottom sheet uses. The
   cubic-bezier here is the same "decelerate" curve iOS/Android system
   sheets use — chosen so the motion itself feels immediately familiar
   rather than novel. Duration/easing must stay in sync with
   ANIMATE_PANEL_CLOSE_MS in app.js, which times the sky's own reaction
   (a date jump, a season jump, or starting playback) to land only once
   this transition has actually finished. */
#animate-bar-body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  max-height: 78vh;
  overflow-y: auto;
  background: var(--surface-page);
  border: 1px solid var(--border-raised);
  border-bottom: none;
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  box-shadow: 0 -10px 32px rgba(0, 0, 0, 0.55), 0 1px 0 rgba(255, 255, 255, 0.05) inset;
  padding: 16px 16px calc(env(safe-area-inset-bottom, 16px) + 16px);
  transform: translateY(0);
  transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1);
}

#animate-bar.collapsed #animate-bar-body {
  transform: translateY(110%);
  pointer-events: none;
}

/* The simulated clock — deliberately the most visually prominent
   gold-bordered readout on screen since it's the one piece of
   information that answers "what moment am I looking at right now,"
   which matters the whole time playback is running. Anchored just above
   the standing hamburger handle, in the same bottom-center column, so
   the two read as one connected "sky controls" cluster at the bottom of
   the screen when the sheet is collapsed, rather than living inside
   #animate-bar-body where it would disappear the instant the sheet
   slides away. Sized up substantially (bigger type, a heavier glow, a
   brighter border) so it actually stands out against a busy star field
   instead of blending in as one more small pill. */
#animate-clock {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom, 16px) + 14px + 44px + 12px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 11;
  pointer-events: auto;
  display: flex;
  flex-wrap: nowrap;
  align-items: baseline;
  justify-content: center;
  gap: 9px;
  width: max-content;
  max-width: min(92vw, 360px);
  padding: 11px 14px 11px 18px;
  background: var(--surface-overlay);
  backdrop-filter: blur(8px);
  border: 1.5px solid rgba(217, 164, 65, 0.75);
  border-radius: 12px;
  box-shadow: var(--shadow-raised), 0 0 26px rgba(217, 164, 65, 0.28);
  font-family: var(--font-body);
}
/* That same bottom-center spot is also right where the open sheet's own
   content (the season-jump grid, the Star trails/Horizon grid toolbar
   row) ends up, since the sheet is tall enough to reach past it — so
   without this, the clock would float on top of and block whatever
   sheet content happens to land underneath it the moment the sheet
   opens (exactly what happened before this rule existed). Hidden
   outright while the sheet is open; #animate-bar-handle stays reachable
   through the same open sheet via its own explicit z-index (see that
   rule, above #animate-bar-body in source order), so the clock
   disappearing too, right along with it, is consistent rather than a
   new gap. */
#animate-bar:not(.collapsed) #animate-clock { display: none; }

#animate-clock-elapsed { color: var(--gold); font-weight: 800; font-size: 20px; letter-spacing: .2px; text-shadow: 0 0 10px rgba(217, 164, 65, 0.5); white-space: nowrap; }
#animate-clock-sep { color: var(--text-muted); font-size: 13px; }
#animate-clock-abs { color: var(--text-primary); font-weight: 700; font-size: 15px; white-space: nowrap; }

/* STOP — halts a running speed animation exactly where it is (see
   stopAnim() in app.js). Only shown while state.animState === 'playing'
   (toggled in updateAnimateClock()) — there's nothing to halt while
   merely live or paused. A red accent on purpose: unlike every other
   control in this assembly it's a "make it stop" action, not a "go
   here" choice, so it deliberately reads differently at a glance —
   red/alert rather than gold/informational or teal/selected. Solid,
   not just tinted — meant to be unmissable and easy to hit rather than
   a subtle secondary action, since it's the one thing that stops a
   sped-up sky from continuing to run away from "now" unattended. */
#animate-stop-btn {
  margin-left: 3px;
  font-family: var(--font-eyebrow);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .5px;
  color: #fff5f3;
  background: linear-gradient(180deg, #e2453a, #b81f16);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 22px;
  padding: 7px 15px;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(184, 31, 22, 0.55);
}
#animate-stop-btn:hover { background: linear-gradient(180deg, #f0554a, #c8281e); box-shadow: 0 2px 10px rgba(184, 31, 22, 0.7); }
#animate-stop-btn.hidden { display: none; }

#animate-controls {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  border: 1px solid var(--border-raised);
  border-radius: 9px;
  padding: 10px 8px;
  box-shadow: var(--shadow-raised);
}

/* Swaps to a gold "pick a speed" prompt (.hint, set by syncSpeedRowHint()
   in app.js) while Star trails is waiting on a speed choice — see the
   pulse-hint rules below. white-space goes back to normal in that state
   since the hint sentence is longer than the default label and shouldn't
   risk overflowing a narrow phone width. */
#animate-label {
  font-family: var(--font-eyebrow);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
  transition: color 200ms ease;
}
#animate-label.hint {
  color: var(--gold);
  white-space: normal;
  text-align: center;
}

/* Three mutually-exclusive speeds, one row — 300×/500×/800× —
   forward-only, no reverse/direction toggle, no separate Stop/Reset, and
   no REAL TIME pill (see the HTML comment above #animate-bar for why).
   Exactly one pill is ever .active (see syncAnimateBarUI() in app.js). */
#animate-speed-row {
  display: flex;
  gap: 5px;
  width: 100%;
}

.animate-speed-btn {
  flex: 1;
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .2px;
  text-align: center;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 7px 6px;
  cursor: pointer;
}

.animate-speed-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

.animate-speed-btn.active {
  color: var(--on-accent);
  background: var(--teal-300);
  border-color: rgba(16, 159, 147, 0.5);
}

/* Star trails (#trail-toggle-btn) does nothing visible until something's
   actually playing — rather than closing the sheet on a tap that would
   otherwise look like a dead end, turning trails on while nothing's
   playing keeps the sheet open and pulses these three pills instead: a
   slow, soft, gold breathing glow with a slight stagger between them (not
   a synchronized flash) so it reads as a calm "pick one of these" cue,
   not an alarm. Stops the moment a speed is picked or trails is switched
   off again — see syncSpeedRowHint() in app.js. */
@keyframes speed-row-hint-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(217, 164, 65, 0); border-color: var(--border-strong); }
  50% { box-shadow: 0 0 8px 1px rgba(217, 164, 65, 0.45); border-color: rgba(217, 164, 65, 0.75); }
}
#animate-speed-row.pulse-hint .animate-speed-btn {
  animation: speed-row-hint-pulse 1.8s ease-in-out infinite;
}
#animate-speed-row.pulse-hint .animate-speed-btn:nth-child(2) { animation-delay: 0.15s; }
#animate-speed-row.pulse-hint .animate-speed-btn:nth-child(3) { animation-delay: 0.3s; }

/* ---- Jump to date — an absolute counterpart to the relative play/pause/
   speed controls above (see jumpToDate() in app.js). color-scheme: dark
   makes the native calendar-picker icon/popup render light-on-dark
   instead of the browser's default dark-on-dark (unreadable against this
   app's theme). The GO button is a fixed compact width rather than
   flex:1 like the row below it, since a date field genuinely wants most
   of the row's width and GO doesn't need to match it. ---- */
#date-jump-row {
  display: flex;
  gap: 6px;
  width: 100%;
}

#date-jump-input {
  flex: 1;
  min-width: 0;
  color-scheme: dark;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 6px 8px;
}

#date-jump-btn {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .4px;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 6px 14px;
  cursor: pointer;
}
#date-jump-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

/* ---- Equinox/solstice quick jumps — a 2×2 grid rather than one row of
   four, since "SEP EQUINOX" / "DEC SOLSTICE"-length labels get cramped
   at four-across on a phone-width bar; two rows of two keeps every label
   readable at the same font size the rest of the assembly uses. Same
   sunken/bordered visual language as the toolbar row below it. ---- */
#season-quick-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  width: 100%;
}

.season-btn {
  font-family: var(--font-body);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .3px;
  text-transform: uppercase;
  text-align: center;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 6px 4px;
  cursor: pointer;
}
.season-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

/* ---- Toolbar row — Star trails + Horizon grid, side by side, each
   taking an equal half of the row (flex:1) so together they span exactly
   the same width as #animate-controls above, matching its visual
   language (same surface/border/radius as the speed pills) rather than
   looking like a separate, lesser control. No icon/glyph on either —
   both read purely as their text label. Deliberately separate from
   #grid-btn (the top-bar RA/Dec sky-grid icon) both physically and
   functionally — see the HTML comment above #animate-toolbar-row in
   index.html. ---- */
#animate-toolbar-row {
  display: flex;
  gap: 6px;
  width: 100%;
}

#trail-toggle-btn, #horizon-grid-toggle-btn {
  flex: 1;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .4px;
  text-transform: uppercase;
  text-align: center;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 7px 8px;
  cursor: pointer;
}
#trail-toggle-btn:hover, #horizon-grid-toggle-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

/* Star trails / Horizon grid active — same teal-fill language as the
   speed pills / sky-grid button use for "this is on." */
#trail-toggle-btn.active, #horizon-grid-toggle-btn.active {
  color: var(--on-accent);
  background: var(--teal-300);
  border-color: rgba(16, 159, 147, 0.5);
}


#tap-hint {
  position: absolute;
  top: calc(50% + 34px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 5px 12px;
  font-family: var(--font-body);
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  pointer-events: none;
  opacity: 1;
  transition: opacity .6s var(--ease-standard);
}

#tap-hint.fade-out { opacity: 0; }

/* ---------- Info panel — .key-card idiom ----------
   position:fixed (not absolute) and a top-level DOM sibling of #app/
   #guide-panel/#start-screen — see the HTML comment above it for why: a
   descendant can't out-stack its own ancestor, so this has to sit outside
   #app to be able to layer above the constellation guide too. */
#info-panel {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: env(safe-area-inset-bottom, 16px);
  z-index: 70;
  max-width: 420px;
  max-height: min(62vh, 460px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 auto;
  background: var(--surface-raised);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border-raised);
  border-radius: 12px;
  box-shadow: var(--shadow-raised);
  padding: 18px 20px 20px;
}

#info-close {
  position: absolute;
  top: 10px;
  right: 12px;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 16px;
  cursor: pointer;
  padding: 6px;
}

#info-close:hover { color: var(--accent-text); }

#info-icon { font-size: 26px; margin-bottom: 4px; }

#info-name {
  margin: 0 0 2px;
  font-size: 20px;
  color: var(--text-primary);
}

#info-type {
  color: var(--accent-text);
  font-size: 12px;
  margin-bottom: 12px;
  font-family: var(--font-eyebrow);
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
}

#info-details {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 14px;
  margin: 0;
  font-size: 13px;
}

#info-details dt { color: var(--text-muted); }
#info-details dd { margin: 0; color: var(--text-primary); text-align: right; }

#info-lore {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* ---- Constellation function/energy + expanded depth ---- */
#info-energy {
  margin: 14px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.energy-badge {
  display: inline-block;
  font-family: var(--font-eyebrow);
  font-size: 10px;
  font-weight: 900;
  letter-spacing: .4px;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 20px;
  margin-bottom: 10px;
  background: var(--surface-sunken);
  color: var(--text-muted);
  border: 1px solid var(--border-strong);
}

.energy-badge-astrology { color: var(--on-accent); background: var(--teal-300); border-color: transparent; }
.energy-badge-star-lore { color: var(--on-accent); background: var(--gold); border-color: transparent; }
.energy-badge-interpretive { color: var(--text-secondary); background: var(--surface-sunken); }

.energy-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}

.energy-chip {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary);
  background: var(--accent-wash);
  border: 1px solid rgba(16, 159, 147, 0.25);
  border-radius: 6px;
  padding: 3px 8px;
}

.info-subhead {
  margin: 12px 0 4px;
  font-size: 11.5px;
  font-weight: 500; /* small (11.5px) all-caps eyebrow-style subhead — lightened from 700, but kept a touch heavier than the main-title 300 so it stays legible this small */
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--accent-text);
  font-family: var(--font-eyebrow);
}

#info-energy-text, #info-depth-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* ---------- Settings panel — .key-card idiom ---------- */
/* Anchored a fixed distance below the top bar (rather than vertically
   centered via top:50%) so its top edge can never land on top of the
   cog/grid/guide buttons and block them — on real devices with browser
   chrome eating into the viewport height, a 50%-centered panel could
   overlap that row. The offset accounts for the top bar's own height
   (safe-area-inset-top + ~10px padding + 36px buttons + ~10px padding). */
#settings-panel {
  position: absolute;
  left: 12px;
  right: 12px;
  top: calc(env(safe-area-inset-top, 12px) + 74px);
  z-index: 25;
  max-width: 480px;
  margin: 0 auto;
  background: var(--surface-raised);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border-raised);
  border-radius: 12px;
  box-shadow: var(--shadow-raised);
  padding: 20px;
  max-height: calc(100vh - env(safe-area-inset-top, 12px) - 74px - env(safe-area-inset-bottom, 16px) - 16px);
  overflow-y: auto;
}

/* Flex row with a same-width spacer on the left balancing the close
   button on the right, so the "SETTINGS" heading stays truly centered on
   narrow screens instead of the absolutely-positioned button risking an
   overlap with the (large, 54px) heading text. */
#settings-panel-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

#settings-panel h2 {
  flex: 1;
  min-width: 0;
  margin: 0;
  /* Was a flat 54px — now the smaller of 54px or whatever actually fits
     the row once the close button (and its matching left spacer) take
     their 42px+8px each out of the available width, so "SETTINGS" never
     runs into the button on narrow phones. 7.2vw keeps it scaling with
     viewport width instead of just clamping to one fixed fallback size. */
  font-size: min(54px, 7.2vw);
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-primary);
  white-space: nowrap;
}

.settings-header-spacer, #settings-close-btn {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
}

/* Large, sunken close button — mirrors tapping the cog again (see
   closeSettingsPanel() in app.js), just a more discoverable exit once
   you're already inside the panel. The inset shadow (same recipe as the
   app's other "pressed in" surfaces) is what reads as "sunken" rather
   than a normal raised button. */
#settings-close-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-size: 19px;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0,0,0,.55) inset, 0 1px 0 rgba(255,255,255,.04);
  cursor: pointer;
}
#settings-close-btn:hover { color: var(--accent-text); border-color: rgba(16, 159, 147, 0.5); }
#settings-close-btn:active { box-shadow: 0 1px 4px rgba(0,0,0,.7) inset; }

.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  color: var(--text-primary);
}

.setting-hint {
  font-size: 12px;
  color: var(--text-muted);
  font-weight: 300;
  margin: -4px 0 4px;
  line-height: 1.4;
}

/* ---- Daytime mode's Auto/Manual override — same pill language as
   .guide-sort-btn, just a single button whose label names the action
   tapping it takes ("Set manually" while Auto is driving the checkbox,
   "Resume auto" once a person has taken over — see updateDaytimeAutoUI()
   in app.js). ---- */
.daytime-auto-row {
  display: flex;
  justify-content: flex-end;
  margin: -6px 0 4px;
}

.daytime-auto-btn {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 5px 12px;
  cursor: pointer;
}

.daytime-auto-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

.daytime-auto-btn.active {
  color: var(--accent-text);
  background: var(--accent-wash);
  border-color: rgba(16, 159, 147, 0.3);
}

.stepper {
  display: flex;
  align-items: center;
  gap: 10px;
}

.stepper button {
  width: 30px;
  height: 30px;
  border-radius: 7px;
  border: 1px solid var(--border-strong);
  background: var(--surface-sunken);
  color: var(--text-primary);
  font-size: 16px;
  cursor: pointer;
}

.stepper button:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

.stepper span { min-width: 40px; text-align: center; color: var(--text-secondary); }

input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
}

input[type="range"] {
  width: 110px;
  accent-color: var(--accent);
}

#daytime-opacity-row.disabled {
  opacity: 0.4;
  pointer-events: none;
}

/* ---------- Constellation guide ----------
   A top-level overlay (see the HTML comment on #guide-panel) reachable from
   either the start screen or the main app, listing all 88 constellations
   with no camera/location/motion permission required to browse it. */
#guide-panel {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: flex;
  flex-direction: column;
  background: var(--surface-page);
  padding: 24px;
  /* Extra clearance below the safe area (and below #guide-close, which
     sits in that same top strip) before the title — the whole panel used
     to start right at the top edge with everything stacked tight
     underneath, so there was nowhere for the eye to land first. This
     gives the title room to breathe before anything else competes for
     attention. */
  padding-top: calc(env(safe-area-inset-top, 24px) + 32px);
  padding-bottom: env(safe-area-inset-bottom, 24px);
}

/* Was previously unstyled — a bare <button> defaults to the browser's own
   gray/white button chrome, which showed up as a stray white bar across
   the top of the panel. Matches #info-close/#settings-close's look. */
#guide-close {
  position: absolute;
  top: calc(env(safe-area-inset-top, 24px) + 10px);
  right: 20px;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 16px;
  cursor: pointer;
  padding: 6px;
}

#guide-close:hover { color: var(--accent-text); }

#guide-panel h2 {
  /* Centered and width-matched to the tab strip/search/list below it
     rather than sitting flush left at full panel width — one aligned
     column reads as considered; a title on its own measure with
     differently-aligned rows underneath doesn't. The generous
     bottom margin is deliberate: it's what pushes the Constellations/
     Stars switcher further down the page, giving the title its own
     moment before the next decision point appears. */
  margin: 0 auto 28px;
  max-width: 480px;
  width: 100%;
  font-size: 20px;
  text-align: center;
  color: var(--text-primary);
}

#guide-panel .setting-hint {
  margin: 0 auto 20px;
  max-width: 480px;
  width: 100%;
  font-size: 12.5px;
}

/* ---- Tab strip (Constellations / Stars) — a two-up segmented control,
   same visual language as .guide-sort-btn but full-width/flex so each tab
   reads as a peer of the other rather than a small toggle. ---- */
.guide-tab-row {
  display: flex;
  gap: 8px;
  margin: 0 auto 22px;
  max-width: 480px;
  width: 100%;
}

.guide-tab-btn {
  flex: 1;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 9px 10px;
  cursor: pointer;
}

.guide-tab-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

.guide-tab-btn.active {
  color: var(--on-accent);
  background: var(--teal-300);
  border-color: rgba(16, 159, 147, 0.5);
}

#guide-search {
  display: block;
  width: 100%;
  max-width: 480px;
  padding: 12px 16px;
  margin: 0 auto 18px;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--text-primary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 7px;
}

#guide-search::placeholder { color: var(--text-muted); }

/* ---- Sort toggle (By importance / A-Z) — a small segmented control,
   same pill language as .guide-item-badge but interactive. ---- */
.guide-sort-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 auto 18px;
  max-width: 480px;
  width: 100%;
}

.guide-sort-label {
  font-family: var(--font-eyebrow);
  font-size: 10.5px;
  font-weight: 900;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--text-muted);
}

.guide-sort-btn {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 5px 12px;
  cursor: pointer;
}

.guide-sort-btn:hover {
  color: var(--accent-text);
  border-color: rgba(16, 159, 147, 0.5);
}

.guide-sort-btn.active {
  color: var(--on-accent);
  background: var(--teal-300);
  border-color: rgba(16, 159, 147, 0.5);
}

/* ---- Importance-mode section headers (Zodiac / Star Lore / Other) ---- */
.guide-section-header {
  padding: 14px 4px 6px;
  font-family: var(--font-eyebrow);
  font-size: 11px;
  font-weight: 900;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--accent-text);
  border-bottom: none !important; /* override the #guide-list li default divider — a header shouldn't look like a row */
}

.guide-section-header:first-child { padding-top: 0; }

#guide-list, #guide-star-list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1;
  max-width: 480px;
  width: 100%;
  margin: 0 auto;
}

#guide-list li, #guide-star-list li {
  display: flex;
  align-items: center;
  gap: 6px;
  border-bottom: 1px solid var(--border);
}

.guide-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 1;
  min-width: 0;
  padding: 13px 4px;
  border: none;
  background: none;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 14.5px;
  text-align: left;
  cursor: pointer;
}

.guide-item:hover { color: var(--accent-text); }

.guide-item-badge {
  flex-shrink: 0;
  font-family: var(--font-eyebrow);
  font-size: 9.5px;
  font-weight: 900;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 3px 9px;
  margin-left: 10px;
}

/* ---- "Show me" — per-row button that jumps to the AR view and starts
   live directional guidance toward that constellation ---- */
.guide-show-btn {
  flex-shrink: 0;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  color: var(--accent-text);
  background: var(--accent-wash);
  border: 1px solid rgba(16, 159, 147, 0.3);
  border-radius: 7px;
  padding: 7px 10px;
  cursor: pointer;
  white-space: nowrap;
}

.guide-show-btn:hover {
  background: var(--teal-300);
  color: var(--on-accent);
  border-color: rgba(16, 159, 147, 0.5);
}

/* ---------- Icon guide ----------
   Two sub-views live inside #icon-guide (see the HTML comment on it):
   #icon-tour, a spotlight-style "coach mark" over the real top bar, and
   #icon-list, a plain static legend. Only one is ever un-hidden at a
   time — plain classList toggling between them, no motion, since that's
   a secondary in-place swap rather than a screen-level switch (same
   reasoning as e.g. the daytime-mode settings sub-row elsewhere). The
   outer #icon-guide fades in/out via the shared panel motion rule near
   the top of this file. */
/* Deliberately sits BELOW the real top bar (top: +74px, not +10px) rather
   than in the usual top-right close-button spot — during the guided
   tour, the spotlighted icon can itself be anywhere in that top-right
   corner (including the "?" button that opened this in the first
   place), and a close button competing for the same few pixels would
   overlap it. This clears the whole bar regardless of which icon is
   currently highlighted. */
#icon-guide-close {
  position: fixed;
  top: calc(env(safe-area-inset-top, 12px) + 74px);
  right: 14px;
  z-index: 97;
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  border: 1px solid var(--border);
  border-radius: 7px;
  width: 32px;
  height: 32px;
  color: var(--text-primary);
  font-size: 15px;
  cursor: pointer;
  padding: 0;
}
#icon-guide-close:hover { color: var(--accent-text); border-color: rgba(16, 159, 147, 0.5); }

/* ---- Guided tour: a fixed highlight ring tracks the real icon's own
   getBoundingClientRect() (see positionIconTourHighlight() in app.js),
   using the classic "box-shadow: 0 0 0 9999px" trick to dim the entire
   screen except that one rectangle — no clip-path/mask math needed, and
   it's just as happy animating between positions as any other box-shadow
   transition. #icon-tour itself catches taps anywhere in the dimmed area
   and treats them as "next", so the real (dimmed) icons underneath never
   receive the click. ---- */
#icon-tour {
  position: fixed;
  inset: 0;
  z-index: 90;
  cursor: pointer;
}

#icon-tour-highlight {
  position: fixed;
  border-radius: 10px;
  box-shadow: 0 0 0 9999px rgba(4, 8, 10, 0.84),
              0 0 0 3px var(--accent-text),
              0 0 18px rgba(111, 227, 214, 0.55);
  pointer-events: none;
  transition: top var(--motion-base) var(--ease-standard),
              left var(--motion-base) var(--ease-standard),
              width var(--motion-base) var(--ease-standard),
              height var(--motion-base) var(--ease-standard);
}

#icon-tour-callout {
  position: fixed;
  left: 12px;
  right: 12px;
  top: calc(env(safe-area-inset-top, 12px) + 122px); /* clears both the real top bar and #icon-guide-close above it */
  max-width: 420px;
  margin: 0 auto;
  background: var(--surface-raised);
  border: 1px solid var(--border-raised);
  box-shadow: var(--shadow-raised);
  border-radius: 12px;
  padding: 18px 20px;
  text-align: center;
  cursor: default; /* don't treat interacting with the card itself as a "tap to advance" */
}

#icon-tour-icon {
  font-size: 40px;
  line-height: 1.1;
  margin-bottom: 8px;
}

#icon-tour-callout h3 {
  margin: 0 0 6px;
  font-size: 17px;
  color: var(--text-primary);
}

#icon-tour-callout p {
  margin: 0 0 10px;
  font-size: 13.5px;
  color: var(--text-secondary);
  line-height: 1.5;
}

#icon-tour-step { margin-bottom: 14px; }

.icon-tour-nav {
  display: flex;
  gap: 10px;
}
.icon-tour-nav .cta-btn { flex: 1; width: auto; margin-top: 0; }
#icon-tour-back:disabled { opacity: 0.4; cursor: default; }

.icon-tour-link {
  display: block;
  width: 100%;
  margin-top: 12px;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px;
}
.icon-tour-link:hover { color: var(--accent-text); }

/* ---- Static list view ---- */
#icon-list {
  position: fixed;
  inset: 0;
  z-index: 92;
  background: var(--surface-page);
  padding: 24px;
  padding-top: env(safe-area-inset-top, 24px);
  padding-bottom: env(safe-area-inset-bottom, 24px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

#icon-list h2 { margin: 0 0 14px; font-size: 20px; color: var(--text-primary); }

#icon-list-replay {
  display: block;
  max-width: 480px;
  margin: 0 auto 18px;
}

#icon-list-items {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 480px;
}

.icon-list-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 4px;
  border-bottom: 1px solid var(--border);
}

.icon-list-glyph {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  background: var(--surface-raised);
  border: 1px solid var(--border-raised);
  border-radius: 10px;
}

.icon-list-text h4 { margin: 0 0 2px; font-size: 14.5px; color: var(--text-primary); }
.icon-list-text p { margin: 0; font-size: 12.5px; color: var(--text-secondary); line-height: 1.4; }

/* ---------- "Show me" guide-to navigation HUD ----------
   Lives inside #app (see index.html) as a full-screen, mostly click-through
   overlay: only the status card opts back into pointer events, so the
   arrows never block taps on the sky itself. */
#guide-nav {
  position: absolute;
  inset: 0;
  z-index: 15;
  pointer-events: none;
}

/* ---- Status card — one compact "key-card" tucked in the bottom-left
   corner, deliberately out of the way of both the arrows and whatever's
   actually in view. Transitions in place between "Finding X" and
   "🔒 Locked on X" (see updateGuideNav in app.js) rather than being two
   separate popups fighting for the middle of the screen. ---- */
#guide-status {
  position: absolute;
  left: 14px;
  bottom: calc(env(safe-area-inset-bottom, 16px) + 14px);
  z-index: 16;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(280px, calc(100vw - 28px));
  background: var(--surface-overlay);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(217, 164, 65, 0.45);
  border-radius: 12px;
  box-shadow: var(--shadow-raised);
  padding: 9px 10px 9px 12px;
  pointer-events: auto;
  transition: box-shadow .2s ease, border-color .2s ease;
}

#guide-status-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  background: rgba(217, 164, 65, 0.14);
  border: 1px solid rgba(217, 164, 65, 0.4);
}

#guide-status-body {
  flex: 1;
  min-width: 0;
}

#guide-status-label {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#guide-status-label strong { color: var(--gold); }

#guide-status-hint {
  margin-top: 2px;
  font-family: var(--font-body);
  font-size: 10.5px;
  font-weight: 300;
  line-height: 1.35;
  color: var(--text-muted);
}

#guide-status-close {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  font-size: 12px;
  cursor: pointer;
}

#guide-status-close:hover { color: var(--danger); border-color: var(--danger); }

/* Echoes the canvas overlay's own flash (see drawGuideTarget in app.js)
   so the card reads as part of the same live "locked on" indicator. */
@keyframes guide-status-pulse {
  0%, 100% { box-shadow: var(--shadow-raised); border-color: rgba(217, 164, 65, 0.45); }
  50% { box-shadow: var(--shadow-raised), 0 0 16px rgba(217, 164, 65, 0.5); border-color: rgba(217, 164, 65, 0.85); }
}

#guide-status.locked { animation: guide-status-pulse 1.3s ease-in-out infinite; }

/* ---- Directional arrows — each is a positioned wrapper (so it can sit
   flush against a screen edge) around an inner <span> that gets the pulse
   animation, keeping the two transforms from fighting each other. Only
   the arrows whose direction still needs correcting go .active; the rest
   stay invisible so it's obvious at a glance which way to move. ---- */
.guide-arrow {
  position: absolute;
  opacity: 0;
  transition: opacity .15s ease;
}

.guide-arrow.active { opacity: 1; }

.guide-arrow span {
  display: block;
  font-size: 40px;
  line-height: 1;
  color: var(--gold);
  filter: drop-shadow(0 0 8px rgba(217, 164, 65, 0.65));
}

.guide-arrow.active span {
  animation: guide-arrow-pulse 1s ease-in-out infinite;
}

@keyframes guide-arrow-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.3); }
}

.guide-arrow-up {
  top: calc(env(safe-area-inset-top, 12px) + 130px);
  left: 50%;
  transform: translateX(-50%);
}

.guide-arrow-down {
  bottom: calc(env(safe-area-inset-bottom, 16px) + 64px);
  left: 50%;
  transform: translateX(-50%);
}

.guide-arrow-left {
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
}

.guide-arrow-right {
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
}

