/* =====================================================================
   SHIROL — 360° tour stylesheet

   Sections:
     01  tokens
     02  reset and base
     03  the level machine
     04  brand lockup
     05  level 1 — hero
     06  level 2 — chooser
     07  level 3 — viewer chrome
     08  the point rail
     09  the facts panel
     10  the compass and loading veil
     11  plot layer (SVG)
     12  call-out layer (HTML)
     13  fluid scale notes
     14  landscape phones — HEIGHT breakpoints
     15  motion and reduced motion
   ===================================================================== */

/* ---------------------------------------------------------------------
   01  TOKENS
   Aliases at the bottom are how you change a whole palette in one commit
   without touching 400 rules.
   --------------------------------------------------------------------- */
:root {
  --slate:      #2B3338;
  --slate-soft: #3C474D;
  --sage:       #8A9A90;
  --brass:      #C8A96A;
  --ink:        #232A2E;
  --muted:      #79828a;
  --line:       rgba(43, 51, 56, 0.13);
  --paper:      #ffffff;
  --mist:       #EEF1F0;

  /* one material — every floating element uses these three, nothing else */
  --glass:      rgba(20, 26, 29, 0.44);
  --glass-line: rgba(255, 255, 255, 0.16);
  --glass-shadow: 0 18px 44px rgba(0, 0, 0, 0.34);

  --serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --sans:  "Jost", "Segoe UI", Helvetica, Arial, sans-serif;

  --head-h: 74px;
  --ease:  cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ---------------------------------------------------------------------
   02  RESET AND BASE
   --------------------------------------------------------------------- */
* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%; width: 100%;
  overflow: hidden;
  /* no rubber-band on a phone: a bounce during a drag reads as the tour
     stuttering rather than the page scrolling */
  overscroll-behavior: none;
  touch-action: none;
  background: var(--slate);
  font-family: var(--sans);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }
img { max-width: 100%; display: block; }

/* The hidden attribute LOSES to display:flex. A [hidden] element with a
   flex display rule stays on screen. This one line is not optional. */
[hidden] { display: none !important; }

::selection { background: var(--brass); color: #fff; }

/* ---------------------------------------------------------------------
   03  THE LEVEL MACHINE

   Every level stays laid out at inset:0 even while invisible. That is
   why #panorama ALWAYS has a real width and height, and why the WebGL
   viewer can be constructed before level 3 has ever been shown. Build
   this with display:none and you get a viewer that renders at 0x0.
   --------------------------------------------------------------------- */
.level {
  position: fixed;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease), visibility 0.7s;
  z-index: 1;
}
.level--active {
  opacity: 1;
  visibility: visible;
  transform: none;
  z-index: 2;
}

/* ---------------------------------------------------------------------
   04  BRAND LOCKUP
   --------------------------------------------------------------------- */
.brand { display: flex; flex-direction: column; line-height: 1; user-select: none; }
.brand__top  { font-size: 10px; letter-spacing: 0.34em; color: var(--muted); margin-bottom: 6px; }
.brand__main {
  font-family: var(--serif);
  font-size: clamp(19px, 1.45vw, 30px);
  font-weight: 400;
  letter-spacing: 0.20em;
  color: var(--slate);
}
.brand__sub {
  font-size: clamp(8px, 0.52vw, 10px);
  letter-spacing: 0.42em;
  color: var(--sage);
  margin-top: 7px;
}
.brand__logo { width: clamp(112px, 8.4vw, 206px); height: auto; }
.brand--light .brand__main { color: #fff; }
.brand--light .brand__sub  { color: rgba(255,255,255,0.62); }
.brand--light .brand__top  { color: rgba(255,255,255,0.5); }

.site-head {
  position: absolute; top: 0; left: 0; right: 0;
  height: var(--head-h);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 clamp(18px, 3.4vw, 60px);
  z-index: 6;
}
/* level 1 is a centred composition, so the wordmark sits on the same
   axis as everything under it rather than in a corner */
#level1 .site-head { justify-content: center; }
#level1 .brand { align-items: center; text-align: center; }
.site-head .brand__main { color: #fff; }
.site-head .brand__sub  { color: rgba(255,255,255,0.6); }
.site-head--dark .brand__main { color: var(--slate); }
.site-head--dark .brand__sub  { color: var(--sage); }

.lnk-back {
  font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--muted); padding: 10px 2px; position: relative;
}
.lnk-back::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 6px;
  height: 1px; background: var(--sage); transition: right 0.4s var(--ease);
}
.lnk-back:hover::after { right: 0; }

/* ---------------------------------------------------------------------
   05  LEVEL 1 — HERO
   --------------------------------------------------------------------- */
#hero-media {
  position: absolute; inset: 0;
  background-size: cover; background-position: center 58%;
  transform: scale(1.04);
  animation: heroDrift 30s ease-in-out infinite alternate;
  /* The grade is BAKED INTO THE FILE, not applied here. A CSS filter on
     a full-screen background forces the compositor to re-filter it on
     every animation frame, which is a real cost on a phone. */
}
@keyframes heroDrift {
  from { transform: scale(1.04) translate3d(0, 0, 0); }
  to   { transform: scale(1.10) translate3d(-1.2%, -0.8%, 0); }
}
/* Centred layout, so the scrim is centred too: a vignette that darkens
   the middle enough to hold type, and the edges enough to hold the
   frame, without flattening the picture into a grey sheet. */
#hero-overlay {
  position: absolute; inset: 0;
  background:
    radial-gradient(74% 62% at 50% 52%, rgba(14,19,22,0.62) 0%, rgba(14,19,22,0.30) 46%, rgba(14,19,22,0) 78%),
    linear-gradient(to bottom, rgba(14,19,22,0.5) 0%, rgba(14,19,22,0) 26%, rgba(14,19,22,0) 66%, rgba(14,19,22,0.6) 100%);
}
.hero-grain {
  position: absolute; inset: 0; opacity: 0.16; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* Centred, and centred properly: the block is centred in the viewport
   by grid rather than by absolute positioning plus a translate, so the
   button and the rule stay on the same optical axis as the wordmark at
   every screen size and nothing drifts when the type reflows. */
.hero__body {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center;
  text-align: center;
  padding: var(--head-h) clamp(20px, 6vw, 80px) clamp(40px, 8vh, 96px);
  max-width: none;
  color: #fff;
  z-index: 5;
}
.hero__eyebrow {
  margin: 0 0 clamp(12px, 1.6vh, 22px);
  font-size: clamp(9px, 0.62vw, 12px);
  letter-spacing: 0.46em;
  color: rgba(255,255,255,0.72);
  animation: rise 1s var(--ease) 0.15s both;
}
.hero__title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(52px, 8.4vw, 158px);
  line-height: 0.92;
  letter-spacing: 0.012em;
  animation: rise 1.1s var(--ease) 0.28s both;
}
.hero__rule {
  width: clamp(46px, 5vw, 90px); height: 1px;
  background: var(--brass);
  margin: clamp(16px, 2.6vh, 34px) auto clamp(14px, 2.2vh, 26px);
  transform-origin: center;             /* centred layout: wipe from the middle */
  animation: wipe 1.1s var(--ease) 0.62s both;
}
.hero__desc {
  margin: 0 0 clamp(24px, 4vh, 46px);
  font-size: clamp(13px, 1.02vw, 19px);
  font-weight: 300;
  line-height: 1.72;
  color: rgba(255,255,255,0.82);
  max-width: 34em; margin-left: auto; margin-right: auto;
  animation: rise 1s var(--ease) 0.5s both;
}
@keyframes rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
@keyframes wipe { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.btn-enter {
  display: inline-flex; align-items: center; gap: 16px;
  padding: clamp(13px, 1.5vh, 19px) clamp(24px, 2.4vw, 40px);
  border: 1px solid rgba(255,255,255,0.34);
  border-radius: 999px;
  color: #fff;
  font-size: clamp(10px, 0.66vw, 12.5px);
  letter-spacing: 0.28em; text-transform: uppercase;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px);
  transition: background 0.45s var(--ease), border-color 0.45s var(--ease),
              transform 0.45s var(--ease);
  animation: rise 1s var(--ease) 0.72s both;
}
.btn-enter:hover { background: rgba(255,255,255,0.15); border-color: rgba(255,255,255,0.6); transform: translateY(-2px); }
.btn-enter__ic { display: block; width: 19px; height: 19px; transition: transform 0.45s var(--ease); }
.btn-enter__ic svg { width: 100%; height: 100%; }
.btn-enter:hover .btn-enter__ic { transform: translateX(5px); }

.hero__scroll {
  position: absolute; bottom: 26px; left: 50%; z-index: 5;
  width: 1px; height: 52px; background: rgba(255,255,255,0.2); overflow: hidden;
}
.hero__scroll span {
  display: block; width: 100%; height: 40%; background: var(--brass);
  animation: scrollTick 2.4s var(--ease) infinite;
}
@keyframes scrollTick { 0% { transform: translateY(-100%); } 100% { transform: translateY(250%); } }

/* ---------------------------------------------------------------------
   06  LEVEL 2 — CHOOSER
   --------------------------------------------------------------------- */
#level2 { background: var(--mist); }
.chooser {
  position: absolute; inset: var(--head-h) 0 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0 clamp(18px, 4vw, 70px) clamp(20px, 4vh, 50px);
}
.chooser__sub {
  margin: 0 0 clamp(22px, 4vh, 48px);
  font-family: var(--serif);
  font-size: clamp(21px, 2.1vw, 40px);
  font-weight: 300; letter-spacing: 0.03em; color: var(--slate);
}
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: clamp(14px, 1.6vw, 28px);
  width: min(1180px, 100%);
}
.card {
  position: relative; text-align: left; overflow: hidden;
  border-radius: 3px; background: var(--paper);
  box-shadow: 0 2px 8px rgba(35,42,46,0.06);
  transition: transform 0.55s var(--ease), box-shadow 0.55s var(--ease);
}
.card:hover { transform: translateY(-6px); box-shadow: 0 26px 54px rgba(35,42,46,0.17); }
.card__media {
  height: clamp(140px, 24vh, 260px);
  background-size: cover; background-position: center;
  transition: transform 0.9s var(--ease);
}
.card:hover .card__media { transform: scale(1.06); }
.card__body { padding: clamp(16px, 1.7vw, 30px); }
.card__ic { display: block; width: 26px; height: 26px; color: var(--sage); margin-bottom: 12px; }
.card__ic svg { width: 100%; height: 100%; }
.card__name {
  margin: 0 0 8px; font-family: var(--serif); font-weight: 400;
  font-size: clamp(20px, 1.55vw, 30px); letter-spacing: 0.12em; color: var(--slate);
}
.card__desc {
  margin: 0 0 16px; font-size: clamp(12px, 0.76vw, 14.5px); font-weight: 300;
  line-height: 1.68; color: var(--muted);
}
.card__go {
  font-size: 10.5px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--brass);
}

/* ---------------------------------------------------------------------
   07  LEVEL 3 — VIEWER CHROME
   --------------------------------------------------------------------- */
#level3 { background: #101518; }
#panorama { position: absolute; inset: 0; }
#panorama .pnlm-container { background: #101518 !important; }

/* The header sits DIRECTLY on the panorama with no plate at all —
   a blurred strip across the top of a landscape photograph looks like a
   mistake. This is the one place the glass material is not used. */
/* TOP LEFT — back arrow, then the wordmark. Nothing else lives up here
   except the compass, which is pinned top right. */
.viewer__head {
  position: absolute; top: 0; left: 0; right: 0; height: var(--head-h);
  display: flex; align-items: center; justify-content: flex-start;
  gap: clamp(10px, 1.2vw, 18px); padding: 0 clamp(14px, 3vw, 46px);
  z-index: 8; pointer-events: none;
}
.tool--back { flex: none; }
.viewer__head > * { pointer-events: auto; }
.viewer__vig {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(10,14,16,0.5) 0%, rgba(10,14,16,0) 22%),
    linear-gradient(to top,    rgba(10,14,16,0.42) 0%, rgba(10,14,16,0) 26%);
}

.view-switch { display: flex; gap: 6px; }
.view-switch__btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px; border-radius: 999px;
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
  color: rgba(255,255,255,0.72);
  background: var(--glass);
  border: 1px solid var(--glass-line);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  transition: color 0.35s, background 0.35s, border-color 0.35s;
}
.view-switch__btn--on { color: #fff; border-color: rgba(255,255,255,0.42); }
.view-switch__ic { width: 15px; height: 15px; }
.view-switch__ic svg { width: 100%; height: 100%; }

.viewer__tools { display: flex; align-items: center; gap: 7px; }
.tool {
  width: 38px; height: 38px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  color: rgba(255,255,255,0.82);
  background: var(--glass);
  border: 1px solid var(--glass-line);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  font-size: 10.5px; letter-spacing: 0.06em;
  transition: background 0.35s, color 0.35s, transform 0.35s;
}
.tool:hover { background: rgba(20,26,29,0.66); color: #fff; transform: translateY(-1px); }
.tool--on { color: var(--brass); }
.tool svg { width: 17px; height: 17px; }

.hint {
  position: absolute; left: 50%; bottom: clamp(20px, 4vh, 46px);
  transform: translateX(-50%) translateY(8px);
  margin: 0; padding: 8px 18px; border-radius: 999px;
  font-size: 10.5px; letter-spacing: 0.24em; text-transform: uppercase;
  color: rgba(255,255,255,0.82);
  background: var(--glass);
  border: 1px solid var(--glass-line);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  opacity: 0; pointer-events: none; z-index: 9;
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}
.hint--on { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------------------------------------------------------------------
   08  THE POINT RAIL
   --------------------------------------------------------------------- */
/* ---------------------------------------------------------------------
   THE DOCK — bottom left. Tools, then the point rail, then the facts
   panel, stacked upward. Laid out by flow, so nothing has to be measured
   and pinned by JS the way the old floating facts panel was.
   --------------------------------------------------------------------- */
.dock {
  position: absolute; right: clamp(14px, 3vw, 46px);
  bottom: clamp(14px, 3vh, 40px);
  z-index: 7; display: flex; flex-direction: column-reverse;
  align-items: flex-end; gap: 10px;
  pointer-events: none;
}
.dock > * { pointer-events: auto; }

/* tools sit in the OPPOSITE bottom corner from the facts panel, so
   neither ever has to move out of the other's way */
.toolbar {
  position: absolute; left: clamp(14px, 3vw, 46px);
  bottom: clamp(14px, 3vh, 40px);
  z-index: 7; display: flex; gap: 7px;
}

.floors {
  z-index: 7; padding: 14px 14px 12px; border-radius: 4px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-shadow);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
}
.floors__label {
  margin: 0 0 10px; font-size: 9px; letter-spacing: 0.4em;
  color: rgba(255,255,255,0.5);
}
.floors__list { display: flex; flex-direction: column; gap: 2px; }
.floor {
  display: flex; align-items: baseline; gap: 10px;
  padding: 7px 10px; border-radius: 3px; text-align: left;
  color: rgba(255,255,255,0.62);
  transition: color 0.3s, background 0.3s;
}
.floor:hover { color: #fff; background: rgba(255,255,255,0.07); }
.floor--on { color: #fff; background: rgba(255,255,255,0.1); }
.floor__n { font-size: 10px; letter-spacing: 0.1em; color: var(--brass); }
.floor__l { font-size: 11.5px; letter-spacing: 0.14em; text-transform: uppercase; }

/* ---------------------------------------------------------------------
   09  THE FACTS PANEL — positioned by placeFacts() in tour.js, which
   MEASURES the rail's live bounding box. The rail's height depends on
   how many points a destination has, so a hard-coded top would be wrong
   four times out of five.
   --------------------------------------------------------------------- */
.factbox {
  min-width: clamp(196px, 15vw, 288px);
  padding: 15px 18px 16px; border-radius: 4px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-shadow);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
}
.factbox__head {
  margin: 0 0 12px; padding-bottom: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.14);
  font-family: var(--serif); font-size: clamp(14px, 0.95vw, 19px);
  letter-spacing: 0.18em; color: #fff;
}
.factbox__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 11px 16px; }
.factbox__cell { display: flex; flex-direction: column; gap: 4px; }
.factbox__lab {
  font-size: clamp(8.5px, 0.5vw, 10px); letter-spacing: 0.24em;
  text-transform: uppercase; color: rgba(255,255,255,0.5);
}
.factbox__val {
  font-size: clamp(12px, 0.74vw, 17px); font-weight: 400;
  letter-spacing: 0.03em; color: #fff;
}

/* ---------------------------------------------------------------------
   10  COMPASS AND LOADING VEIL
   --------------------------------------------------------------------- */
.compass {
  position: absolute; right: clamp(14px, 3vw, 46px); top: calc(var(--head-h) - 8px);
  z-index: 7; pointer-events: none; text-align: center;
  width: clamp(58px, 4.4vw, 78px);
}
.compass__dial svg { width: 100%; height: auto; display: block;
  filter: drop-shadow(0 6px 16px rgba(0,0,0,0.34)); }
.compass__n { fill: #C8A96A; font-family: var(--sans); font-weight: 600;
  text-anchor: middle; dominant-baseline: middle; letter-spacing: 0.06em; }
.compass__d { fill: rgba(255,255,255,0.66); font-family: var(--sans); font-weight: 500;
  text-anchor: middle; dominant-baseline: middle; letter-spacing: 0.06em; }
.compass__label {
  margin: 5px 0 0; font-size: clamp(9px, 0.62vw, 11.5px);
  letter-spacing: 0.16em; color: rgba(255,255,255,0.82);
  text-shadow: 0 2px 8px rgba(0,0,0,0.6);
}

.pano-loading {
  position: absolute; inset: 0; z-index: 12;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px;
  background: rgba(16,21,24,0.9);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}
.pano-loading__ring {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.16);
  border-top-color: var(--brass);
  animation: spin 0.95s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.pano-loading__txt {
  margin: 0; font-size: 10px; letter-spacing: 0.36em; text-transform: uppercase;
  color: rgba(255,255,255,0.62);
}

/* ---------------------------------------------------------------------
   11  PLOT LAYER (SVG)
   --------------------------------------------------------------------- */
/* While the device is rotating, the viewer's canvas is still the OLD
   size for a few hundred milliseconds while the layout reflows. The
   overlays re-project every frame against the NEW container width, so
   during that window the cards visibly slide across the picture before
   snapping back. Fading them out for the duration is the difference
   between "it rotated" and "something broke". */
body.is-settling .callout-layer,
body.is-settling .plot-layer { opacity: 0; }
.callout-layer, .plot-layer { transition: opacity 0.28s var(--ease); }

.plot-layer {
  position: absolute; inset: 0; z-index: 4;
  pointer-events: none;   /* dragging must still reach Pannellum */
  width: 100%; height: 100%;
}
.plot__poly {
  fill: rgba(200,169,106,0.13);
  stroke: rgba(255,255,255,0.9);
  stroke-width: 1.6;
  stroke-linejoin: round;
  transition: fill 0.3s, stroke 0.3s;
}
.plot__poly--sold     { fill: rgba(214,90,74,0.18);  stroke: rgba(255,206,199,0.9); }
.plot__poly--reserved { fill: rgba(120,150,200,0.16); stroke: rgba(214,228,255,0.9); }
.plot__label {
  fill: #fff; font-family: var(--sans); font-size: 12px;
  letter-spacing: 0.16em; text-transform: uppercase;
  paint-order: stroke; stroke: rgba(12,16,18,0.55); stroke-width: 3px;
}
.plot--on  .plot__poly { fill: rgba(200,169,106,0.3); stroke: var(--brass); stroke-width: 2.2; }
.plot--off { opacity: 0.32; }

/* ---------------------------------------------------------------------
   12  CALL-OUT LAYER (HTML)

   The stem length is published here as a custom property and READ BACK
   by plots.js — never duplicated. It was written in both places once
   and drifted twice.
   --------------------------------------------------------------------- */
.callout-layer {
  position: absolute; inset: 0; z-index: 5;
  pointer-events: none;          /* only the pins re-enable it */
  --co-stem: clamp(26px, 2.42vw, 62px);
}
/* The card sits ABOVE the stem in normal flow, and the whole element is
   translated so its bottom edge lands on the projected point. De-collision
   lengthens the stem — --co-lift — rather than moving the card sideways,
   so nothing else in the arithmetic changes. */
.callout {
  position: absolute; top: 0; left: 0;
  display: flex; flex-direction: column; align-items: flex-start;
  will-change: transform;
}
.callout--below { flex-direction: column-reverse; }

.callout__card {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 15px 9px 9px;
  border-radius: 3px;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-shadow);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
}

.callout__pic { width: 42px; height: 42px; border-radius: 2px; overflow: hidden; flex: none; }
.callout__pic img { width: 100%; height: 100%; object-fit: cover; }
.callout__ic {
  width: 34px; height: 34px; flex: none; border-radius: 2px;
  display: flex; align-items: center; justify-content: center;
  color: var(--brass);
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.1);
}
.callout__ic svg { width: 18px; height: 18px; }
.callout__txt { min-width: 0; }
.callout__title {
  margin: 0; font-size: 12.5px; font-weight: 400; letter-spacing: 0.1em;
  text-transform: uppercase; color: #fff; white-space: nowrap;
}
.callout__meta { margin: 4px 0 0; display: flex; gap: 8px; }
.callout__meta span {
  font-size: 10px; letter-spacing: 0.1em; color: rgba(255,255,255,0.78);
  padding: 2px 7px; border-radius: 999px; background: rgba(255,255,255,0.09);
  white-space: nowrap;
}
.callout__note {
  margin: 5px 0 0; font-size: 10px; letter-spacing: 0.06em;
  color: rgba(255,255,255,0.5); white-space: nowrap;
}

/* The DOT is the thing that must sit exactly on the projected point;
   the card hangs off the stem above it. DOT_X (26px) and DOT_R (7px)
   are mirrored in plots.js. */
.callout__stem {
  width: 1px; flex: none;
  height: calc(var(--co-stem) + var(--co-lift, 0px));
  margin-left: 26px;
  background: linear-gradient(to bottom, rgba(255,255,255,0.14), rgba(255,255,255,0.8));
  transition: height 0.35s var(--ease);
}
.callout--below .callout__stem {
  background: linear-gradient(to top, rgba(255,255,255,0.14), rgba(255,255,255,0.8));
}
/* The dot, and the two rings that make it read as a live marker rather
   than a sticker. Two rings on the same 2.8s loop with the second offset
   by half, so there is always one expanding — one ring alone reads as a
   stutter. Rings are pseudo-elements on the dot, so the 26px DOT_X maths
   in plots.js is untouched. */
.callout__dot {
  position: absolute; left: 26px; bottom: 0;
  width: 13px; height: 13px; margin-left: -6.5px; margin-bottom: -6.5px;
  border-radius: 50%;
  background: var(--brass);
  box-shadow: 0 2px 8px rgba(0,0,0,0.45),
              0 0 0 1px rgba(20,25,28,0.18),
              0 0 14px rgba(200,169,106,0.75);
}
.callout__dot::before,
.callout__dot::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 100%; height: 100%; border-radius: 50%;
  border: 1.5px solid rgba(200,169,106,0.9);
  transform: translate(-50%,-50%) scale(1);
  animation: coPulse 2.8s cubic-bezier(.16,.62,.3,1) infinite;
  pointer-events: none;
}
.callout__dot::after { animation-delay: 1.4s; }

@keyframes coPulse {
  0%   { transform: translate(-50%,-50%) scale(1);   opacity: 0.9; }
  62%  { opacity: 0; }
  100% { transform: translate(-50%,-50%) scale(3.4); opacity: 0; }
}
.callout--below .callout__dot { top: 0; bottom: auto; margin-top: -6.5px; }
.callout--flip { align-items: flex-end; }
.callout--flip .callout__stem { margin-left: 0; margin-right: 26px; }
.callout--flip .callout__dot  { left: auto; right: 26px; margin-left: 0; margin-right: -6.5px; }
.callout--flip .callout__card { flex-direction: row-reverse; padding: 9px 9px 9px 15px; }
.callout--flip .callout__txt  { text-align: right; }
.callout--flip .callout__meta { justify-content: flex-end; }

/* ---------------------------------------------------------------------
   13  FLUID SCALE
   clamp(min, preferred, max) everywhere — a floor so nothing gets
   illegible, a viewport-relative middle so it tracks the screen, a
   ceiling so a 4K monitor does not get comic-book UI. Media-query steps
   would make either the 2560px monitor or the 1366px laptop look wrong.
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   14  LANDSCAPE PHONES — HEIGHT BREAKPOINTS

   A phone in landscape is about 844 x 390. Every @media (max-width:700px)
   rule DOES NOT FIRE. It is a wide screen — a wide screen with 390px of
   height. If a rule is about FITTING THINGS IN, key it on height.
   Width breakpoints are for layout direction, not for size.
   --------------------------------------------------------------------- */
@media (max-height: 620px) {              /* short window / small laptop */
  :root { --head-h: 62px; }
  .factbox__grid { gap: 9px 13px; }
  .callout-layer { --co-stem: clamp(24px, 3.4vh, 42px); }
}
@media (max-height: 500px) {              /* phone, landscape */
  :root { --head-h: 54px; }
  .hero__body { padding: var(--head-h) 6vw 20px; }
  .hero__desc { margin-bottom: 20px; line-height: 1.55; }
  .hero__scroll { display: none; }
  .chooser { padding-bottom: 16px; }
  .cards { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 10px; }
  .card__media { height: 96px; }
  .card__body  { padding: 13px; }
  .floors { padding: 10px 10px 8px; }
  .floor  { padding: 5px 8px; }
  /* The facts panel becomes a STRIP, not a slab, and placeFacts() drops
     it to the bottom-left — on a 390px-tall screen a stacked panel eats
     half the land and sits exactly where the call-outs cluster. */
  .factbox { min-width: 0; padding: 8px 14px 9px; }
  .factbox__head { display: none; }
  .factbox__grid { grid-auto-flow: column; grid-template-columns: none; gap: 0 18px; }
  .factbox__lab  { font-size: 7.5px; letter-spacing: 0.18em; }
  .factbox__val  { font-size: 11px; white-space: nowrap; }
  /* compass stays top right, just smaller — it must not collide with the
     dock, which owns the whole bottom-left corner on a phone */
  .compass { width: 46px; top: calc(var(--head-h) - 4px); right: 12px; }
  .compass__label { margin-top: 3px; font-size: 8.5px; letter-spacing: 0.1em; }
  .dock { right: 10px; bottom: 10px; gap: 7px; }
  .toolbar { left: 10px; bottom: 10px; gap: 5px; }
  .callout__card  { padding: 6px 11px 6px 6px; gap: 8px; }
  .callout__title { font-size: 10.5px; }
  .callout__note  { display: none; }
  .callout__ic    { width: 26px; height: 26px; }
  .callout__ic svg{ width: 14px; height: 14px; }
  .callout__pic   { width: 30px; height: 30px; }
  .callout__meta  { margin-top: 3px; }
  .tool { width: 33px; height: 33px; }
  .hint { bottom: 12px; font-size: 9.5px; }
}
@media (max-height: 450px) {
  /* Pull the pulse rings in. Four call-outs each throwing a 3.4x ring
     fills the whole sky on a 390px-tall screen. */
  .callout__dot::before,
  .callout__dot::after { animation-name: coPulseSm; }
}
@keyframes coPulseSm {
  0%   { transform: translate(-50%,-50%) scale(1);   opacity: 0.85; }
  62%  { opacity: 0; }
  100% { transform: translate(-50%,-50%) scale(2.4); opacity: 0; }
}

@media (max-height: 400px) {              /* small phone, landscape */
  .hero__title { font-size: clamp(38px, 11vw, 62px); }
  .compass { width: 40px; }
  .hero__rule  { margin: 10px 0 9px; }
  .callout__meta span { padding: 1px 5px; font-size: 9px; }
}
/* width breakpoints: layout DIRECTION only */
@media (max-width: 620px) {
  .cards { grid-template-columns: 1fr; }
  .view-switch__btn span:last-child { display: none; }
  .view-switch__btn { padding: 9px 11px; }
}

/* ---------------------------------------------------------------------
   15  MOTION
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  #hero-media { animation: none; transform: scale(1.02); }
}


/* ---------------------------------------------------------------------
   16  PORTRAIT — ROTATE THE DEVICE

   The tour is landscape-only. In portrait a phone gives the panorama a
   letterbox strip and leaves the call-outs nowhere to sit, so rather
   than degrade the layout it asks for the rotation.

   Deliberately pure CSS. An orientation listener would be a second
   source of truth that can disagree with the media query — and
   orientationchange fires before the reflow finishes, so it would
   flicker. The query IS the state.
   --------------------------------------------------------------------- */
/* Always in the DOM and always laid out, so the change of orientation
   crossfades rather than snapping. display:none cannot be transitioned —
   opacity and visibility can. */
.rotate-note {
  position: fixed; inset: 0; z-index: 9998;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center; gap: 20px; padding: 32px;
  background: var(--slate); color: #fff;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.32s var(--ease), visibility 0.32s;
}

@media (max-width: 900px) and (orientation: portrait) {
  .rotate-note {
    opacity: 1; visibility: visible; pointer-events: auto;
  }
  .rotate-note__icon {
    width: 66px; height: 66px; color: var(--brass);
    animation: rotate-hint 2.2s ease-in-out infinite;
  }
  .rotate-note__title {
    margin: 0; font-family: var(--serif);
    font-size: 26px; font-weight: 400; letter-spacing: 0.06em;
  }
  .rotate-note__sub {
    font-family: var(--sans); font-size: 13px; letter-spacing: 0.16em;
    opacity: 0.72; max-width: 320px; line-height: 1.6;
  }
}
@keyframes rotate-hint {
  0%, 40%   { transform: rotate(0deg); }
  60%, 100% { transform: rotate(-90deg); }
}
