:root {
  --sky: #87b6d9;
  --ink: #1c2733;
  --panel: rgba(255, 255, 255, 0.92);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  color: var(--ink);
}

/* ---------- hub page ---------- */
body.hub {
  background: linear-gradient(180deg, #dcecf6 0%, #cfe6dd 100%);
  min-height: 100%;
  overflow-y: auto;
}

.hub-header {
  max-width: 900px;
  margin: 0 auto;
  padding: 56px 24px 16px;
}
.hub-header h1 { margin: 0; font-size: 34px; letter-spacing: -0.5px; }
.hub-header p { margin: 8px 0 0; opacity: 0.7; }

.scene-list {
  max-width: 900px;
  margin: 0 auto;
  padding: 24px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 20px;
}

.scene-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  background: #fff;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 6px 20px rgba(30, 50, 70, 0.12);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.scene-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 30px rgba(30, 50, 70, 0.2);
}
.scene-thumb {
  height: 130px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 48px;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(135deg, var(--sky), #6f9bbf);
}
.scene-card-body { padding: 16px 18px 20px; }
.scene-card-body h2 { margin: 0 0 6px; font-size: 18px; }
.scene-card-body p { margin: 0; font-size: 13px; opacity: 0.7; line-height: 1.45; }

/* A 2D scene's card. The badge says the scene opens on its own animated page
   rather than in the 3D viewer, so the change of medium is not a surprise. */
.scene-thumb { position: relative; }
.scene-tag {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.32);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.scene-card.is-2d .scene-thumb {
  background: linear-gradient(135deg, #b8674f, #4a6f97);
}

/* ---------- viewer page ---------- */
body.viewer {
  overflow: hidden;
  background: var(--sky);
}
#viewer-canvas { display: block; width: 100%; height: 100%; }

/* On-screen controls and HUD text are chrome, not content: a drag or a
   long-press should never select or highlight them (mobile especially). */
body.viewer .back-link,
body.viewer .overlay,
.freemove-ui,
.freemove-hint,
.joystick-base,
.joystick-knob,
.vertical-controls,
.transport,
.music-toggle,
button {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.back-link {
  position: fixed;
  top: 16px;
  left: 16px;
  z-index: 15;
  color: #fff;
  text-decoration: none;
  font-size: 13px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

.music-toggle {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 15;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  background: var(--panel);
  color: var(--ink);
  font-size: 16px;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
}
.music-toggle:hover { background: #fff; }
.music-toggle[hidden] { display: none; }

.overlay {
  position: fixed;
  top: 40px;
  left: 16px;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  pointer-events: none;
  z-index: 10;
}
.overlay h1 { font-size: 18px; font-weight: 600; margin: 0 0 4px; }
.overlay p { font-size: 13px; margin: 0; opacity: 0.9; }

.locus-panel {
  position: fixed;
  right: 16px;
  bottom: 80px;
  max-width: 320px;
  background: var(--panel);
  border-radius: 12px;
  padding: 14px 32px 14px 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.4s ease, transform 0.4s ease, bottom 0.2s ease;
  z-index: 16;
}
.locus-panel.visible { opacity: 1; transform: none; }
.locus-progress { font-size: 11px; letter-spacing: 1px; opacity: 0.55; }
.locus-panel h2 { margin: 4px 0 6px; font-size: 17px; }
.locus-panel p { margin: 0; font-size: 13px; line-height: 1.5; opacity: 0.8; white-space: pre-line; }
.locus-link {
  display: block;
  margin-top: 10px;
  font-size: 13px;
  font-weight: 600;
  color: #2563eb;
  text-decoration: none;
}
.locus-link:hover { text-decoration: underline; }
/* `display: block` above outranks the plain `hidden` attribute LocusOverlay
   sets, so without this the "Go to scene" link showed on every locus in every
   scene, pointing at the placeholder href="#". Only loci that declare a `link`
   should offer one. */
.locus-link[hidden] { display: none; }
.locus-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 22px;
  height: 22px;
  line-height: 20px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  font-size: 18px;
  opacity: 0.5;
  cursor: pointer;
  padding: 0;
}
.locus-close:hover { opacity: 1; background: rgba(0, 0, 0, 0.08); }

/* Structural wrappers for the mobile sheet. Inert on desktop -- no margins, no
   constraints -- so the panel renders exactly as it did before. */
.locus-head, .locus-body { display: block; }
.locus-grabber { display: none; }

/* The panel is faded out with opacity, not removed, so without this it went on
   swallowing taps over its whole footprint while invisible. Harmless on
   desktop; on a phone it would have blocked the bottom half of the scene. */
.locus-panel { pointer-events: none; }
.locus-panel.visible { pointer-events: auto; }
/* On phones `.locus-panel` sets its own `display: flex` (the bottom sheet),
   which beats the UA `[hidden]` rule -- so Viewer setting `hidden` on it for
   free move left a full-width, pointer-events: auto sheet sitting over the
   bottom quarter of the screen, swallowing every drag-to-look that started
   there. Same fix as `.transport[hidden]`; the attribute selector outranks the
   media-query rule, so one line covers both layouts. */
.locus-panel[hidden] { display: none; }

/* Free Move's up/down buttons sit where the tour bottom-right panel
   normally does, so lift it clear of them while free-moving. */
.free-mode .locus-panel { bottom: 220px; }

.transport {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
  z-index: 12;
}
.transport button {
  border: 0;
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 13px;
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
}
.transport button:hover { background: #fff; }
.tour-controls {
  display: flex;
  gap: 8px;
}
.tour-controls[hidden] { display: none; }
/* `.transport` sets its own display, which beats the UA `[hidden]` rule, so
   restate it — a scene with no loci (glacier-deck) hides the whole bar,
   Free Move toggle included, because free move is then the only mode. */
.transport[hidden] { display: none; }
.transport button.active {
  background: var(--ink);
  color: #fff;
}
.transport button.active:hover { background: #2c3b4d; }

.freemove-hint {
  position: fixed;
  top: 40px;
  left: 50%;
  transform: translateX(-50%);
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  font-size: 13px;
  pointer-events: none;
  z-index: 10;
  text-align: center;
  max-width: 90vw;
}

.joystick-base {
  position: fixed;
  left: 24px;
  bottom: 84px;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.25);
  border: 2px solid rgba(255, 255, 255, 0.5);
  touch-action: none;
  z-index: 14;
}
.joystick-knob {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  border-radius: 50%;
  background: var(--panel);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  transition: transform 0.05s linear;
}

.vertical-controls {
  position: fixed;
  right: 24px;
  bottom: 84px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 14;
}
.vertical-controls button {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 0;
  background: var(--panel);
  color: var(--ink);
  font-size: 22px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  touch-action: none;
  cursor: pointer;
}
.vertical-controls button:hover { background: #fff; }

.loading {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sky);
  color: #fff;
  font-size: 15px;
  z-index: 20;
  transition: opacity 0.4s ease;
}
.loading.hidden { opacity: 0; pointer-events: none; }

.error-box {
  position: fixed;
  top: 16px;
  right: 16px;
  max-width: 320px;
  background: rgba(180, 30, 30, 0.92);
  color: #fff;
  padding: 12px 14px;
  border-radius: 8px;
  font-size: 13px;
  display: none;
  z-index: 30;
}

/* ---------- small screens: the locus panel becomes a bottom sheet ----------

   This is the first media query in the file; there was no existing mobile
   breakpoint to match. 640px keeps phones (360-430 CSS px) on the sheet and
   leaves tablets and up on the desktop layout untouched.

   What was wrong: .locus-panel is `position: fixed; right: 16px; bottom: 80px`
   with no max-height, so it grows UPWARD from its bottom edge. On a ~390px
   phone a long locus -- the-white-house stop 4 is ~1,900 characters -- wraps to
   roughly 1,700px of text. The panel ran clean off the top of the viewport, so
   the counter, the <h2> title and the absolutely-positioned close button all
   sat above y=0 with no way to reach them, the first readable line started
   mid-sentence under the URL bar, and the panel covered both the back link and
   the scene itself. Capping the height and scrolling the body inside the panel
   fixes all of it. */
@media (max-width: 640px) {
  .locus-panel {
    left: 0;
    right: 0;
    bottom: 0;
    max-width: none;
    display: flex;
    flex-direction: column;
    /* dvh follows the collapsing browser chrome; vh is the fallback for
       engines without it. Order matters -- the later one wins where supported. */
    max-height: 45vh;
    max-height: 45dvh;
    border-radius: 16px 16px 0 0;
    padding: 6px 16px 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.24);
    /* Slide the whole sheet off the bottom edge when closed. Both states are
       restated here: the base `.locus-panel.visible { transform: none }` above
       is more specific than a bare `.locus-panel` in this block, so a hidden
       transform declared on its own would lose to it and the sheet would never
       come to rest flush with the bottom. */
    transform: translateY(100%);
  }
  .locus-panel.visible { transform: none; }
  /* .free-mode lifts the desktop panel clear of the up/down buttons. The sheet
     is already docked at the bottom, so cancel that or it floats mid-screen.
     Needs the same two-class specificity as the rule it overrides. */
  .free-mode .locus-panel { bottom: 0; }

  .locus-grabber {
    display: block;
    flex: none;
    width: 36px;
    height: 4px;
    margin: 2px auto 8px;
    border-radius: 2px;
    background: currentColor;
    opacity: 0.22;
  }

  .locus-head {
    flex: none;
    padding-right: 34px; /* clear of the close button */
  }
  .locus-panel h2 { margin: 2px 0 8px; font-size: 18px; }

  .locus-body {
    flex: 1 1 auto;
    min-height: 0;        /* without this a flex child refuses to shrink and
                             overflow-y never engages */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain; /* don't chain the scroll to the page behind */
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  }
  .locus-panel p { font-size: 14px; line-height: 1.55; opacity: 0.85; }

  .locus-close {
    top: 10px;
    right: 10px;
    width: 34px;
    height: 34px;
    line-height: 32px;
    font-size: 22px;
    opacity: 0.65;
    background: rgba(0, 0, 0, 0.06);
  }

  /* Everything anchored to the bottom of the screen has to ride above the
     sheet. --locus-sheet-h is written by LocusOverlay from the panel's real
     measured height, and is 0px whenever the panel is closed -- so a two-line
     locus doesn't shove the controls as far up as a twenty-line one, and
     nothing moves at all when the sheet is dismissed. */
  .transport { bottom: calc(var(--locus-sheet-h, 0px) + 20px); }
  .joystick-base,
  .vertical-controls { bottom: calc(var(--locus-sheet-h, 0px) + 84px); }
}
