html, body {
  margin: 0;
  height: 100%;
  font-family: system-ui, sans-serif;
  background: #111;
}

#app {
  display: flex;
  /* 100vh on mobile is the *largest* possible viewport (browser chrome
     hidden), not the actually-visible one -- with the address bar showing
     (the normal state), content sized to 100vh runs off the bottom of the
     screen. 100dvh tracks the real visible area; this was why the PSV
     navbar (time/play/zoom/fullscreen) was invisible on mobile. Keep the
     100vh line as a fallback for older browsers without dvh support. */
  height: 100vh;
  height: 100dvh;
}

#player {
  width: 60%;
  height: 100%;
  position: relative;
  background: #000;
}

#viewer {
  width: 100%;
  height: 100%;
}

#map-container {
  width: 40%;
  height: 100%;
  position: relative;
  border-left: 2px solid #333;
}

#map {
  width: 100%;
  height: 100%;
  background: #ddd;
}

/* Phones in portrait: stack video-over-map (60/40) instead of squeezing
   both side by side into a narrow width. (The play-pill-hiding rule for
   this same breakpoint lives at the end of the file, not here -- it has to
   come AFTER .play-pill-overlay's base display:flex further down, or the
   later base rule wins the cascade at equal specificity regardless of the
   media query. Learned that the hard way; see the note down there.) */
@media (orientation: portrait) {
  #app {
    flex-direction: column;
  }
  #player {
    width: 100%;
    height: 60%;
  }
  #map-container {
    width: 100%;
    height: 40%;
    border-left: none;
    border-top: 2px solid #333;
  }
}

/* ── Map's own bottom bar -- overlays the map (doesn't shrink it), same
   pattern as PSV's own navbar overlaying the video, so the two visually
   read as one continuous bar spanning the whole window. Grid with a
   matching spacer on the right keeps Recenter genuinely centered
   regardless of the fullscreen button's width on the left. ── */
#map-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 40px;
  z-index: 700;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  background: rgba(0, 0, 0, .45);
  padding: 0 10px;
}

/* No circle background -- matches PSV's own (borderless) fullscreen icon on
   the video side, just with a vertical divider baked into the glyph so the
   two icons read as "same family, different scope" rather than unrelated. */
#combined-fullscreen-btn {
  grid-column: 1;
  /* PSV's own fullscreen button renders its icon at 20px inside the navbar
     -- match that exactly so the two fullscreen icons are visually the
     same size, not just "close." */
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: #fff;
  opacity: .85;
  cursor: pointer;
}

#combined-fullscreen-btn:hover {
  opacity: 1;
}

/* Flexbox let the SVG's own width shrink well below its declared 20px
   (rendered 8x20 in testing) despite width/height attributes on the <svg>
   itself -- forcing the size here, off the flex item, is what actually
   sticks. flex-shrink:0 stops the button itself from being squeezed by its
   grid track too. */
#combined-fullscreen-btn {
  flex-shrink: 0;
}

#combined-fullscreen-btn svg {
  display: block;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.map-bar-spacer {
  grid-column: 3;
  width: 28px;
}

#recenter-btn {
  grid-column: 2;
  justify-self: center;
  padding: 8px 16px;
  background: #fff;
  border: none;
  border-radius: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s ease;
}

#recenter-btn.visible {
  opacity: 1;
  pointer-events: auto;
}

#satellite-btn {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 1000;
  padding: 6px 14px;
  background: #fff;
  border: none;
  border-radius: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}

#satellite-btn.active {
  background: #2b7de9;
  color: #fff;
}

/* Drone marker: static-size position dot + FOV cone showing the live
   viewport-orientation. Leaflet divIcons are screen-space overlays, so this
   is already a fixed pixel size regardless of map zoom — no extra work
   needed for that part of the request. Rotation pivots at (0,0), which is
   the SVG viewBox center = the marker's iconAnchor, i.e. the exact lat/lng
   point = the tip of the cone. */
.drone-marker svg {
  overflow: visible;
}

.drone-cone {
  fill: #2b7de9;
  fill-opacity: .55;
  transform-origin: 0px 0px;
  transition: transform 80ms linear;
}

.drone-dot {
  fill: #e63946;
  stroke: #fff;
  stroke-width: 2px;
}

/* Invisible wide line stacked on the visible flight-path purely to widen
   the click/tap hit area -- the visible line stays thin (weight 3), but a
   3px-wide target is genuinely hard to hit precisely with a mouse and
   nearly impossible with a fingertip. See pathHitTarget in app.js. */
.flight-path-hit {
  cursor: pointer;
}

/* ── Calibration tools (dev-only; toggle with Ctrl+Shift+Q, no visible
   button -- not meant for client-facing use) ── */
#calib-panel {
  position: fixed;
  top: 46px;
  left: 12px;
  z-index: 2000;
  margin: 0;
  padding: 10px 12px;
  background: rgba(0, 0, 0, .8);
  color: #0f0;
  font: 12px/1.5 ui-monospace, monospace;
  border-radius: 4px;
  white-space: pre;
  display: none;
}

#calib-panel.visible {
  display: block;
}

#crosshair {
  position: absolute;
  inset: 0;
  display: none;
  pointer-events: none;
  z-index: 500;
}

#crosshair.visible {
  display: block;
}

#crosshair div {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .6);
}

#crosshair div::before,
#crosshair div::after {
  content: '';
  position: absolute;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .6);
}

#crosshair div::before {
  top: 50%;
  left: -8px;
  right: -8px;
  height: 1px;
  margin-top: -0.5px;
}

#crosshair div::after {
  left: 50%;
  top: -8px;
  bottom: -8px;
  width: 1px;
  margin-left: -0.5px;
}

/* ── "This is interactive" cues ── */

#badge-360 {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 600;
  padding: 5px 14px;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .02em;
  border-radius: 20px;
  pointer-events: none;
  backdrop-filter: blur(2px);
  white-space: nowrap;
  max-width: calc(100% - 24px);
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 420px) {
  #badge-360 {
    font-size: 11px;
    padding: 4px 10px;
  }
}

/* Custom compass -- replaces PSV's own CompassPlugin, which showed the
   viewer's RAW uncalibrated yaw with no way to feed it a corrected value.
   This one is driven by the exact same computedBearing() that rotates the
   map's cone (see updateOrientationCone() in app.js), so the two can never
   disagree with each other again. */
#viewport-compass {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 600;
  cursor: grab;
  /* Stops the browser treating a touch-drag on the dial as a page-scroll
     gesture -- without this, dragging on mobile fights the browser instead
     of rotating the view. */
  touch-action: none;
}

#viewport-compass.dragging {
  cursor: grabbing;
}

.compass-ring {
  fill: rgba(0, 0, 0, .45);
  stroke: rgba(255, 255, 255, .35);
  stroke-width: 2;
}

.compass-n-label {
  fill: #fff;
  font-size: 11px;
  font-weight: 700;
  pointer-events: none;
}

#compass-needle {
  fill: rgba(255, 255, 255, .8);
  transform-origin: 0px 0px;
  transition: transform 80ms linear;
  pointer-events: none;
}

#drag-hint {
  position: absolute;
  inset: 0;
  z-index: 900;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: rgba(0, 0, 0, .25);
  pointer-events: none;
  opacity: 0;
  transition: opacity .4s ease;
}

#drag-hint.visible {
  display: flex;
}

#drag-hint.shown {
  opacity: 1;
}

.drag-hint-icon {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 30px;
  color: rgba(255, 255, 255, .85);
}

.drag-hint-icon .chevron {
  opacity: .6;
}

.drag-hint-icon .pointer {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .9);
  box-shadow: 0 0 0 6px rgba(255, 255, 255, .18);
  animation: drag-hint-slide 1.6s ease-in-out infinite;
}

.drag-hint-text {
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .6);
}

@keyframes drag-hint-slide {
  0%, 100% { transform: translateX(-22px); }
  50% { transform: translateX(22px); }
}

/* Hide Photo Sphere Viewer's own big centered play/pause button -- it sits
   dead center of the frame, which is exactly where someone would want to
   screenshot, and gets in the way. Our own controls (below) replace it. */
.psv-video-bigbutton {
  display: none !important;
}

/* Pushes the swivel button, and everything after it in the navbar
   (fullscreen, lat/long), to the right edge of the bar -- see the
   `className` comment on the swivel button definition in app.js for why. */
.psv-push-right {
  margin-left: auto;
}

/* ── Swivel + lat/long: real PSV navbar buttons (passed as custom button
   `content` elements in app.js). Explicit flex/height centers each
   vertically within the navbar row regardless of its own font-size. ── */
.psv-swivel-icon,
.psv-latlong {
  display: flex;
  align-items: center;
  height: 100%;
}

.psv-swivel-icon {
  font-size: 16px;
}

.psv-latlong {
  padding: 0 8px;
  white-space: nowrap;
  /* Deliberately no font-size/color override -- inherits PSV's own navbar
     text styling directly, so it matches the time display exactly without
     needing to guess PSV's actual values. */
}

/* ── Play pill: NOT a navbar item -- a separate element appended as a
   direct child of #viewer (see app.js), absolutely centered so it stays
   centered regardless of how wide the navbar's left/right button groups
   are, and still visible in PSV's native fullscreen since it's inside
   #viewer rather than a sibling of it. Sized to fit inside PSV's ~40px
   navbar band without clipping (28px tall, 6px clearance top/bottom). ── */
.play-pill-overlay {
  position: absolute;
  left: 50%;
  bottom: 6px;
  transform: translateX(-50%);
  z-index: 1000;
  height: 28px;
  padding: 0 14px;
  display: flex;
  align-items: center;
  background: #fff;
  color: #111;
  border: none;
  border-radius: 14px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

@media (max-width: 480px) {
  .psv-latlong {
    padding: 0 4px;
  }
}

/* These two MUST come after .play-pill-overlay and .psv-latlong's base
   rules above (source order, not just file position) -- see the comment
   on the portrait layout media query near the top of this file for why. */
@media (orientation: portrait) {
  .play-pill-overlay {
    display: none;
  }
}

@media (hover: none) and (pointer: coarse) {
  .psv-latlong {
    display: none;
  }
}
