/* The tour viewer. Deliberately close to the look Drew is replacing: the
   panorama is the whole page, and everything else is a translucent slab
   floating over it. Nothing here is a framework -- it is one page. */

:root {
  --panel: rgba(38, 40, 44, 0.82);
  --panel-solid: #26282c;
  --ink: #fff;
  --accent: #ff3b30;          /* the you-are-here dot and its view cone */
  --dot: #2f9ae0;             /* every other station */
  --radius: 14px;
  --shadow: 0 6px 28px rgba(0, 0, 0, 0.42);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: #111;
  font: 400 14px/1.4 Roboto, system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

#stage {
  position: fixed;
  inset: 0;
  touch-action: none;
  cursor: grab;
}
#stage.dragging { cursor: grabbing; }
#stage.overhotspot { cursor: pointer; }
#stage canvas { display: block; }

.pill {
  position: fixed;
  padding: 9px 20px;
  border-radius: 999px;
  background: var(--panel);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow);
  font-size: 14px;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

#address {
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  max-width: min(560px, calc(100vw - 32px));
  overflow: hidden;
  text-overflow: ellipsis;
}

#loading {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transition: opacity 0.25s;
}
#loading[hidden] { display: block; opacity: 0; pointer-events: none; }

.panel {
  background: var(--panel);
  backdrop-filter: blur(6px);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

#failed {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 18px 22px;
  max-width: 460px;
  line-height: 1.5;
}
#failed code {
  display: block;
  margin-top: 8px;
  opacity: 0.7;
  font-size: 12px;
  word-break: break-all;
}

/* -------------------------------------------------------------- stairways */

/* A button that belongs to a place in the house rather than to the screen. It
   is a DOM element and not something drawn into the panorama, because the two
   are worth exactly the same amount of immersion -- there is no geometry in the
   scene for a sprite to be occluded by, it is one photograph on a sphere -- and
   this way the lettering is crisp at any distance, it inherits the rest of the
   viewer's look, and it is a real <button> that tabs and reads out loud. The
   only thing viewer.js has to do is keep it over the right pixel. */

#stairnav {
  position: fixed;
  inset: 0;
  overflow: hidden;         /* a marker projected off-screen must not scroll it */
  pointer-events: none;     /* the panorama drags right through the gaps */
}

/* One per spot on the floor. A stacked stairwell -- the same footprint serving
   the flight up and the flight down, which is most houses -- puts two buttons
   on one spot, so they stack here rather than fighting over the same pixel. */
.stairmark {
  position: absolute;
  left: 0;
  top: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}
.stairmark[hidden] { display: none; }

.stairlink {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 16px 8px 9px;
  border: 0;
  border-radius: 999px;
  background: var(--panel);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, transform 0.15s ease-out;
}
.stairlink:hover, .stairlink:focus-visible {
  background: rgba(58, 61, 66, 0.92);
  transform: scale(1.06);
}
.stairlink:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.stairlink .arrow { flex: none; }
.stairlink.down .arrow { transform: rotate(180deg); }

@media (max-width: 560px) {
  .stairlink { font-size: 12px; padding: 7px 13px 7px 8px; gap: 7px; }
}

/* ---------------------------------------------------------------- minimap */

#map {
  position: fixed;
  left: 16px;
  bottom: 16px;
  width: 280px;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
#map[hidden] { display: none; }

#map header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
#map h1 {
  margin: 0;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.15;
  opacity: 0.92;
}

#collapse, #expand {
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: var(--ink);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s;
}
#collapse:hover, #expand:hover { background: rgba(255, 255, 255, 0.26); }

#expand {
  position: fixed;
  left: 16px;
  bottom: 16px;
  width: 44px;
  height: 44px;
  background: var(--panel);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow);
  border-radius: var(--radius);
}
#expand[hidden] { display: none; }

/* The rendered plan and the live overlay share one box and one viewBox, so a
   station dot drawn at plan-mm-through-`frame` lands exactly on its room. */
/* The plan DRAWING sets the height and the overlay is stretched over whatever
   that turns out to be. Anything else drifts: give the box its own height and
   the two layers letterbox their shared viewBox by different amounts, so the
   station dots sit lower than the rooms they mark -- by a distance that changes
   with the height of the window, which is a strange bug to look at.

   A floor plan can be any shape (this house's 2nd floor is twice as tall as it
   is wide) so the height still has to be capped, but the cap is applied to the
   WIDTH -- max-width = the tallest map we want, times the drawing's own aspect.
   Height then follows from the drawing and lands exactly on the cap, with no
   second constraint to disagree with it. --ar comes from the published frame. */
:root { --map-max-h: 40vh; }

#stagemap {
  position: relative;
  width: 100%;
  max-width: calc(var(--map-max-h) * var(--ar, 1.6));
  margin: 0 auto;
  background: #fff;
  border-radius: 8px;
  overflow: hidden;
}
#mapsvg svg {
  display: block;
  width: 100%;
  height: auto;
}
/* inset alone: the overlay takes the box the drawing just defined, so the two
   share one geometry by construction rather than by both being told a size. */
#overlay { position: absolute; inset: 0; }

/* Hovering is driven by a class, not by :hover, so that pointing at a ring out
   in the panorama lights the matching dot on the plan as well -- which is the
   half that actually answers "where am I about to go?". */
/* Scaled about its own bounding box, so the whole mark grows in place. Putting
   the transform on the outer group avoids fighting the translate/scale that
   positions the mark -- in SVG those are the same property, and a CSS transform
   on that element would throw the placement away. */
.station {
  cursor: pointer;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.16s ease-out;
}
.station.hovered { transform: scale(1.16); }

/* Invisible until hovered, but still hittable: on a small map the mark is only
   a few pixels across, and this quietly makes it a comfortable target. */
.station .halo {
  opacity: 0;
  transition: opacity 0.16s ease-out;
}
.station.hovered .halo { opacity: 0.34; }

#floorpick { display: block; }
#floors {
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.14) no-repeat right 10px center/12px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23fff' stroke-width='1.8'/%3E%3C/svg%3E");
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  appearance: none;
}
#floors:focus-visible { outline: 2px solid var(--accent); }
#floorpick:has(#floors option:only-child) { display: none; }
#floors option { background: var(--panel-solid); }

/* ------------------------------------------------------------ agent card */

/* Who is selling the house. Everything in here is filled in from the branding
   endpoint at load; the markup is built in viewer.js so that an unbranded
   tour carries no empty scaffolding.

   It sits bottom-right because bottom-left is the floor plan. On a phone
   there is not room for both, so it starts as the chip below and the card is
   something you ask for. */

#agent {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 3;
  width: 264px;
  max-width: calc(100vw - 32px);
  padding: 14px;
  display: grid;
  gap: 11px;
}
#agent[hidden] { display: none; }
#agent.left { right: auto; bottom: auto; left: 16px; top: 16px; }

#agent .note {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0.62;
}

#agent .who {
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
}
#agent .shot {
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  object-fit: cover;
  background: rgba(255, 255, 255, 0.14);
}
#agent .name { font-size: 15px; font-weight: 500; line-height: 1.25; }
#agent .title, #agent .brokerage { font-size: 12px; opacity: 0.7; line-height: 1.35; }
#agent .logo {
  max-width: 100%;
  max-height: 28px;
  object-fit: contain;
  object-position: left center;
}

#agent .links { display: flex; gap: 6px; }
#agent .links a {
  flex: 1;
  padding: 8px 4px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.14);
  color: var(--ink);
  font-size: 12px;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s;
}
#agent .links a:hover { background: rgba(255, 255, 255, 0.26); }
/* The one thing somebody is meant to do next. */
#agent .links a.call { background: var(--accent); }
#agent .links a.call:hover { background: var(--accent); filter: brightness(1.12); }

#agent .fold {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  font-size: 15px;
  line-height: 1;
  opacity: 0.55;
  cursor: pointer;
}
#agent .fold:hover { opacity: 1; background: rgba(255, 255, 255, 0.14); }

/* Folded away: a headshot you can tap. Same corner, so it does not move. */
#agentchip {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 3;
  width: 52px;
  height: 52px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  overflow: hidden;
  background: var(--panel);
  box-shadow: var(--shadow);
  color: var(--ink);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}
#agentchip[hidden] { display: none; }
#agentchip.left { right: auto; bottom: auto; left: 16px; top: 16px; }
#agentchip img { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 560px) {
  #map { width: 190px; left: 12px; bottom: 12px; }
  #address { font-size: 13px; padding: 8px 14px; }
  /* The map is 190px and the card is 264px: on a phone they cannot both have
     a bottom corner, so the card gets the full width and sits above the map
     only while it is open. */
  #agent { right: 12px; bottom: 12px; width: min(264px, calc(100vw - 24px)); }
  #agent.left { left: 12px; top: 12px; }
  #agentchip { right: 12px; bottom: 12px; }
  #agentchip.left { left: 12px; top: 12px; }
}

/* A short window is where the map crowds the tour most, so give it less. */
@media (max-height: 620px) {
  :root { --map-max-h: 32vh; }
}
