/* ============================================================
   LYNDHURST PARK · THE VIRTUAL SUITE — HUD & overlays
   Warm glass over a warm room: parchment text, sage & brass,
   Fraunces headings, Karla UI, Caveat for the hand notes.
   ============================================================ */

/* 28 Sep 2026: Fraunces and Karla are VARIABLE fonts — Fraunces-400/-600 are
   the same file byte for byte, and so are Karla-400/-500/-700 — so one
   @font-face per family with its weight range replaces five downloads with two
   (−111 KB, −3 requests). The old per-weight files stay on disk; to go back,
   restore the per-weight rules (Fraunces-600, Karla-500, Karla-700). */
@font-face { font-family: 'Fraunces'; src: url('../assets/fonts/Fraunces-400.woff2') format('woff2'); font-weight: 100 900; font-display: swap; }
@font-face { font-family: 'Fraunces'; src: url('../assets/fonts/Fraunces-400-italic.woff2') format('woff2'); font-weight: 100 900; font-style: italic; font-display: swap; }
@font-face { font-family: 'Karla'; src: url('../assets/fonts/Karla-400.woff2') format('woff2'); font-weight: 400 800; font-display: swap; }
@font-face { font-family: 'Space Mono'; src: url('../assets/fonts/SpaceMono-400.woff2') format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Caveat'; src: url('../assets/fonts/Caveat-600.woff2') format('woff2'); font-weight: 400 700; font-display: swap; }

:root {
  --ink: #12120e;
  --glass: rgba(18, 20, 16, .72);
  --glass-2: rgba(24, 26, 20, .88);
  --paper: #f6efdf;
  --paper-dim: #d4c9b0;
  --paper-faint: #a0947c;
  --gold: #e0bc78;
  --gold-hi: #f6e2b0;
  --sage: #8fad84;
  --terra: #c8845f;
  --line: rgba(246, 239, 223, .14);
  --line-strong: rgba(246, 239, 223, .28);
  --ease: cubic-bezier(.22, .8, .24, 1);
  --ease-slow: cubic-bezier(.16, 1, .3, 1);
  --disp: 'Fraunces', Georgia, serif;
  --ui: 'Karla', system-ui, sans-serif;
  --mono: 'Space Mono', monospace;
  /* HUD stacking clearance (dock + home indicator) */
  --dock-h: 72px;
  /* --disc-h: the AI disclaimer's band along the very bottom (measured live by
     main.js). Everything that sat on the bottom edge sits on it instead. */
  --disc-h: calc(22px + var(--sa-b));
  --chrome-bottom: calc(var(--dock-h) + var(--disc-h));   /* the band already includes the home-indicator inset */

  /* ---- iOS safe areas, in one place so every rule agrees ----
     Landscape on a Dynamic Island phone puts the whole inset (~59px) on ONE
     side and nothing on the other. Padding each side by its own inset shunts
     centred content sideways and leaves a dead band beside the island, so
     anything centred uses the symmetric --sa-x gutter instead. Backgrounds
     always run edge to edge underneath — only content is inset. */
  --sa-t: env(safe-area-inset-top, 0px);
  --sa-r: env(safe-area-inset-right, 0px);
  --sa-b: env(safe-area-inset-bottom, 0px);
  --sa-l: env(safe-area-inset-left, 0px);
  --sa-x: max(var(--sa-l), var(--sa-r));
  /* Centre line of the usable area. Anything pinned with left:50% would sit
     centred on the display, which pushes it under the island in landscape. */
  --sa-mid: calc(50% + (var(--sa-l) - var(--sa-r)) / 2);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
/* The landing page's ground is the media wall green, not the room's near-black.
   It sits on <html>, not <body>, so it is what the browser paints outside the
   page box — the rubber-band area on iOS and the flash before the gradient
   arrives — while body keeps --ink for the suite itself. Same value as the
   middle stop of the #intro gradient, measured off the lit media wall. */
html { background: #1e281e; }
html, body { height: 100%; overflow: hidden; }
body {
  background: var(--ink);
  color: var(--paper);
  font-family: var(--ui);
  font-size: 15.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 3px; }

#scene { position: fixed; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
#labelLayer { position: fixed; inset: 0; pointer-events: none; z-index: 5; }

/* hotspot labels projected from 3D */
.hs-label {
  position: absolute; transform: translate(-50%, -130%);
  font: 700 10.5px var(--ui); letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink); background: var(--gold);
  padding: 5px 11px; border-radius: 99px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
  opacity: 0; transition: opacity .25s;
  white-space: nowrap;
}
.hs-label.on { opacity: 1; }

/* ── intro ── */
#intro {
  position: fixed; inset: 0; z-index: 100;
  /* flex + margin:auto on the card, NOT place-items:center — grid/flex centring
     clips the top of an over-tall child and makes it unreachable. Portrait
     phones fell in that gap: the old scroll fallback only covered landscape
     and short viewports, so the quality picker pushed Step inside off-screen. */
  display: flex; overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  padding:
    max(12px, var(--sa-t))
    max(16px, var(--sa-r))
    calc(16px + var(--disc-h))
    max(16px, var(--sa-l));
  /* The ground is the media wall, not black. NOT the paint value from
     CFG.paints.feature (#39473a) — that is the albedo the material is authored
     with, and the lit wall renders far darker than it. Measured off the wall
     itself: the green pixels in the media zone sit at #1a2319-#1e281e, median
     #1e281e, and that is what the visitor actually sees. The two stops either
     side are the same hue a shade up and down: depth, not a second colour. */
  background:
    radial-gradient(ellipse 80% 60% at 50% 35%, rgba(224, 188, 120, .14), transparent 55%),
    radial-gradient(ellipse 70% 50% at 20% 80%, rgba(143, 173, 132, .1), transparent 50%),
    linear-gradient(165deg, #222d21 0%, #1a2419 55%, #1e281e 100%);
  backdrop-filter: blur(6px);
  transition: opacity .9s var(--ease), visibility .9s;
}
#intro.done { opacity: 0; visibility: hidden; }
.intro-card {
  text-align: center; max-width: 560px; padding: 40px 28px;
  margin: auto;              /* centres when it fits, scrolls cleanly when it does not */
  width: 100%;
  transition: opacity .28s ease;
}
/* Polish r3 (live QA R2-m5): held back while the page streams in on a slow line,
   so it never paints half-built and then jumps (index.html adds .intro-hold in
   the head and removes it once the card and its fonts are in, ≤ ~1.5 s). The
   animation is the safety net: shown after 2.5 s even if that script never runs. */
html.intro-hold .intro-card {
  visibility: hidden; opacity: 0;
  animation: introShowAnyway 0s linear 2.5s forwards;
}
@keyframes introShowAnyway { to { visibility: visible; opacity: 1; } }
/* The gradient behind still runs edge to edge; only the content is inset, so
   the island side loses nothing that was ever visible. */
.intro-mark {
  width: min(220px, 58vw); height: auto; display: block;
  margin: 0 auto 18px;
  filter: drop-shadow(0 8px 18px rgba(160, 176, 16, .28));
}
.intro-kicker { font: 700 11px var(--mono); letter-spacing: .34em; text-transform: uppercase; color: var(--paper-faint); margin-bottom: 16px; }
#intro h1 { font: 400 clamp(38px, 6vw, 58px)/1.04 var(--disp); margin-bottom: 8px; text-shadow: 0 2px 40px rgba(0,0,0,.35); }
#intro h1 em { font-style: italic; color: var(--gold); }
/* 5 Oct 2026 (client): "Open 24/7" — a third line under "the virtual suite", smaller,
   in gold, set like the kicker above the heading (Space Mono capitals, tracked). It is
   sized off the heading (em), so every breakpoint below that resizes the h1 resizes it. */
#intro h1 .intro-open {
  display: block;              /* its own line box: the h1's 1.04 line height would leave a gap */
  margin-top: .5em;
  padding-bottom: .35em;       /* a little air above the paragraph (margins would collapse) */
  text-indent: .3em;           /* balances the last letter's tracking, so the line centres */
  font: 700 max(12px, .3em)/1.2 var(--mono);
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--gold);
  text-shadow: 0 1px 14px rgba(0,0,0,.35);
}
.intro-sub { color: var(--paper-dim); margin: 14px auto 26px; max-width: 46ch; }
.intro-tiny { margin-top: 18px; font-size: 11.5px; color: var(--paper-faint); letter-spacing: .06em; }

/* Landscape / short viewports — keep Step inside on-screen and tappable */
@media (max-height: 520px), ((orientation: landscape) and (max-width: 950px)) {
  #intro {
    display: flex;
    overflow-x: hidden;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding:
      max(8px, var(--sa-t))
      max(16px, var(--sa-r))
      calc(12px + var(--disc-h))
      max(16px, var(--sa-l));
  }
  .intro-card {
    max-width: min(560px, 100%);
    margin: auto;
    padding: 8px 12px 16px;
  }
  .intro-mark {
    width: min(110px, 22vw);
    margin: 0 auto 6px;
  }
  .intro-kicker {
    margin-bottom: 4px;
    font-size: 9px;
    letter-spacing: .22em;
  }
  #intro h1 {
    font-size: clamp(22px, 6.5vh, 34px);
    line-height: 1.05;
    margin-bottom: 2px;
  }
  .intro-sub {
    margin: 4px auto 12px;
    font-size: 13px;
    line-height: 1.35;
    max-width: 54ch;
  }
  #intro .btn-gold {
    min-height: 44px;
    padding: 11px 22px;
  }
  .intro-tiny {
    margin-top: 8px;
    font-size: 10px;
  }
}

/* ── buttons ── */
.btn-gold, .btn-ghost {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 26px; border-radius: 999px;
  font: 700 12px var(--ui); letter-spacing: .18em; text-transform: uppercase;
  text-decoration: none;
  transition: transform .3s var(--ease), box-shadow .3s, background-position .5s;
}
.btn-gold {
  color: #1a1408;
  background: linear-gradient(105deg, #7a5c2e, #e0bc78 42%, #f6e2b0 52%, #e0bc78 62%, #7a5c2e);
  background-size: 220% 100%; background-position: 15% 0;
  box-shadow: 0 14px 36px -12px rgba(224, 188, 120, .65);
}
.btn-gold:hover { transform: translateY(-2px); background-position: 85% 0; }
/* "Step inside" while the host photo is still decoding — reads as busy, not broken */
.btn-gold.is-preparing,
.btn-gold[disabled] {
  cursor: progress;
  opacity: .62;
  box-shadow: 0 10px 26px -14px rgba(224, 188, 120, .45);
}
.btn-gold.is-preparing:hover,
.btn-gold[disabled]:hover { transform: none; background-position: 15% 0; }
/* shader-link progress from main.js (data-prep="40%"), shown only while preparing */
.btn-gold.is-preparing[data-prep]::before {
  content: attr(data-prep); font-size: .78em; letter-spacing: .04em; opacity: .85; flex: none;
}
.btn-gold.is-preparing::after {
  content: ''; width: 12px; height: 12px; flex: none;
  border: 2px solid rgba(26, 20, 8, .3); border-top-color: #1a1408; border-radius: 50%;
  animation: btnSpin .8s linear infinite;
}
@keyframes btnSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn-gold.is-preparing::after { animation: none; } }
.btn-ghost { border: 1.4px solid var(--line-strong); color: var(--paper); }
.btn-ghost:hover { border-color: var(--gold); color: var(--gold-hi); transform: translateY(-2px); }
.btn-wide { width: 100%; }

/* ── HUD ── */
.hud-brand {
  position: fixed; top: max(12px, var(--sa-t)); left: max(12px, var(--sa-l)); z-index: 40;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px; border-radius: 99px;
  background: var(--glass); backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  font: 600 13px var(--disp); letter-spacing: .04em;
}
.hud-brand em { font-style: italic; color: var(--gold); }
.hud-brand img { width: 22px; height: 22px; display: block; object-fit: contain; }
.hud-brand svg { width: 20px; color: var(--gold); }
/* Masterplan focus: hide brand so it doesn’t sit under the focus bar */
body.focus-mode .hud-brand { opacity: 0; pointer-events: none; }

.lbl-short { display: none; }
.lbl-full { display: inline; }

.dock-wrap {
  position: fixed;
  bottom: calc(10px + var(--disc-h));
  left: var(--sa-mid); transform: translateX(-50%); z-index: 40;
  max-width: calc(100vw - 16px - var(--sa-l) - var(--sa-r));
  display: flex; align-items: stretch;
}
.dock {
  display: flex; gap: 2px; padding: 6px;
  background: var(--glass); backdrop-filter: blur(14px);
  border: 1px solid var(--line); border-radius: 18px;
  box-shadow: 0 20px 50px -20px rgba(0, 0, 0, .7);
  max-width: 100%;
  overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scroll-padding-inline: 8px;
  flex: 1 1 auto; min-width: 0;
}
.dock::-webkit-scrollbar { display: none; }
.dock button {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  min-width: 74px; padding: 9px 10px 8px; border-radius: 13px;
  font: 700 9.5px var(--ui); letter-spacing: .12em; text-transform: uppercase;
  color: var(--paper-dim); transition: all .25s; white-space: nowrap;
}
/* display:flex above beats the UA stylesheet's [hidden]{display:none}, so a
   withdrawn destination stayed on the dock with the attribute set. */
.dock button[hidden] { display: none !important; }
.dock button svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  opacity: .9;
}
.dock button:hover { color: var(--paper); background: rgba(255, 255, 255, .05); }
.dock button.is-on { background: var(--gold); color: #1c1608; }
.dock button.is-on svg { opacity: 1; }

/* Reset + graphics quality (top-right cluster) */
.hud-tools {
  position: fixed;
  top: max(12px, var(--sa-t));
  right: max(12px, var(--sa-r));
  z-index: 41;
  display: inline-flex; align-items: center; gap: 8px;
}
.hud-quality, .hud-reset {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 12px; border-radius: 99px;
  background: var(--glass); backdrop-filter: blur(12px);
  border: 1px solid var(--line);
  font: 700 10px var(--ui); letter-spacing: .16em; text-transform: uppercase;
  color: var(--paper-dim); transition: color .2s, border-color .2s;
  min-width: 40px; min-height: 40px;
}
.hud-quality svg, .hud-reset svg {
  width: 16px; height: 16px; display: block; flex: none;
  stroke: currentColor; fill: none;
}
.hud-quality:hover, .hud-reset:hover { color: var(--paper); border-color: var(--line-strong); }
body.focus-mode .hud-tools { top: max(58px, calc(var(--sa-t) + 46px)); }
.quality-pop {
  position: fixed; top: 56px; right: 16px; z-index: 42;
  width: 230px; padding: 12px;
  background: var(--glass-2); backdrop-filter: blur(16px);
  border: 1px solid var(--line-strong); border-radius: 14px;
  box-shadow: 0 30px 70px -25px rgba(0, 0, 0, .8);
}
/* ---- host chooser: intro tickboxes + settings rows ---- */
/* Host + Quality side by side, the pair centred under the copy. Each card keeps
   its own left-aligned options — centring the labels themselves makes the
   tickboxes look ragged. */
.intro-picks {
  /* auto-fit grid: two columns when the card is wide enough, one when it is
     not, and always flush with the card so the pair reads as centred. */
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
  align-items: stretch;
  margin: 0 0 16px;
}
.host-pick {
  border: 1px solid rgba(243,221,171,.22);
  border-radius: 12px;
  padding: 10px 12px 12px;
  margin: 0;
  display: grid; gap: 6px; text-align: left; align-content: start;
}
.host-pick legend {
  font: 700 10px/1 var(--mono); letter-spacing: .18em;
  text-transform: uppercase; color: var(--gold); padding: 0 6px;
}
.host-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 8px; border-radius: 9px; cursor: pointer;
  transition: background .15s ease;
}
.host-opt:hover { background: rgba(243,221,171,.07); }
.host-opt input { position: absolute; opacity: 0; pointer-events: none; }
.host-tick {
  flex: none; width: 17px; height: 17px; border-radius: 5px;
  border: 1.5px solid rgba(243,221,171,.5); position: relative;
  transition: background .15s ease, border-color .15s ease;
}
.host-opt input:checked + .host-tick { background: var(--gold); border-color: var(--gold); }
.host-opt input:checked + .host-tick::after {
  content: ''; position: absolute; left: 5px; top: 1px;
  width: 4px; height: 9px; border: solid #1c1608;
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.host-opt input:focus-visible + .host-tick { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---- consent before entry ---------------------------------------------
   Reuses .host-tick so the box is the same control the two pickers above
   use. It sits between the pickers and the button because that is the
   reading order of the decision: choose how you want it, agree, go in. */
.intro-consent {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 0 0 16px; padding: 11px 12px;
  border: 1px solid rgba(243,221,171,.22); border-radius: 12px;
  text-align: left; cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.intro-consent:hover { background: rgba(243,221,171,.07); }
.intro-consent input { position: absolute; opacity: 0; pointer-events: none; }
.intro-consent .host-tick { margin-top: 1px; }
.intro-consent input:checked + .host-tick { background: var(--gold); border-color: var(--gold); }
.intro-consent input:checked + .host-tick::after {
  content: ''; position: absolute; left: 5px; top: 1px;
  width: 4px; height: 9px; border: solid #1c1608;
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.intro-consent input:focus-visible + .host-tick { outline: 2px solid var(--gold); outline-offset: 2px; }
.intro-consent-txt { font-size: 12.5px; line-height: 1.45; color: var(--paper-faint); }

/* Held for consent is a different state from held for the host: nothing is
   loading, the visitor simply has not agreed yet. So no progress cursor. */
.btn-gold.is-locked,
.btn-gold.is-locked:hover { cursor: not-allowed; }
.btn-gold.is-locked::after { content: none; }
/* Quality change re-enters the page. While that navigation is in flight the
   fieldset stops taking input — a second click would restart the whole build. */
.host-pick.is-switching,
.host-pick.is-waiting {
  opacity: .55;
  pointer-events: none;
}
.host-pick.is-switching legend::after {
  content: ' · applying';
  color: var(--paper-faint);
}
.host-txt { display: grid; line-height: 1.25; }
.host-txt b { font: 600 13px var(--ui); color: var(--paper); }
.host-txt small { font: 400 11px var(--ui); color: var(--paper-faint); }
/* Narrow phones: one column, still centred */
@media (max-width: 560px) {
  /* one column, and start-aligned so the shorter card is not stretched to
     match the taller one (that left a dead gap under "Virtual host") */
  .intro-picks { grid-template-columns: 1fr; align-items: start; }
  /* Quality's four options go two-up, halving the card's height — that is what
     pushed Step inside below the fold on a portrait phone. */
  #qualityPick { grid-template-columns: 1fr 1fr; column-gap: 4px; }
  #qualityPick legend { grid-column: 1 / -1; }
  .host-opt { padding: 6px 6px; gap: 8px; }
  .host-txt b { font-size: 12.5px; }
  .host-txt small { font-size: 10.5px; }
}
/* Very short portrait (small phones, browser chrome showing): drop the option
   descriptions rather than let the card outgrow the screen. */
@media (max-width: 560px) and (max-height: 720px) {
  .host-txt small { display: none; }
  .intro-sub { display: none; }
}

.host-pop { display: grid; gap: 2px; margin-bottom: 6px; }

.quality-pop p { font: 700 9.5px var(--mono); letter-spacing: .22em; text-transform: uppercase; color: var(--gold); margin: 2px 4px 9px; }
.quality-pop button {
  display: block; width: 100%; text-align: left;
  padding: 9px 11px; border-radius: 9px; margin-top: 2px;
  font: 600 12.5px var(--ui); color: var(--paper-dim); transition: all .2s;
}
.quality-pop button small { display: block; font: 400 10.5px var(--ui); color: var(--paper-faint); margin-top: 1px; }
.quality-pop button:hover { background: rgba(255, 255, 255, .06); color: var(--paper); }
.quality-pop button.is-on { background: rgba(217, 180, 117, .15); color: var(--gold); }
.quality-pop button.is-on small { color: var(--paper-dim); }

.hud-chat-toggle {
  position: fixed;
  left: max(14px, var(--sa-l));
  /* Always clear of the dock (+ scroll hint) */
  bottom: calc(var(--chrome-bottom) + 14px);
  z-index: 38;
  display: none; align-items: center; gap: 9px;
  padding: 11px 18px; border-radius: 99px;
  background: var(--glass); backdrop-filter: blur(12px);
  border: 1px solid var(--line-strong);
  font: 700 12px var(--ui); letter-spacing: .16em; text-transform: uppercase;
}
.hud-chat-toggle .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--sage); animation: pulse 2.2s infinite; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(139,160,129,.55); } 70% { box-shadow: 0 0 0 8px rgba(139,160,129,0); } 100% { box-shadow: 0 0 0 0 rgba(139,160,129,0); } }
body.chat-closed .hud-chat-toggle { display: inline-flex; }
/* Masterplan / overlays: hide floating Jess so it never covers the dock */
body.focus-mode .hud-chat-toggle,
body.card-open .hud-chat-toggle,
body.plots-open .hud-chat-toggle { display: none !important; }

/* Dock edge scroll controls (‹ ›) when more items are off-screen */
.dock-scroll-hint {
  flex: 0 0 auto;
  align-self: center;
  width: 36px; height: 36px;
  min-width: 36px; min-height: 36px;
  border-radius: 50%;
  background: rgba(18, 20, 16, .92);
  border: 1px solid var(--gold);
  color: var(--gold-hi);
  font: 700 22px/1 var(--ui);
  display: none;
  align-items: center; justify-content: center;
  box-shadow: 0 4px 12px rgba(0,0,0,.4);
  z-index: 5;
  padding: 0;
  position: relative;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
}
.dock-scroll-hint--prev { margin-right: 4px; order: -1; }
.dock-scroll-hint--next { margin-left: 4px; }
.dock-scroll-hint.is-visible { display: inline-flex; }
.dock-scroll-hint[hidden] { display: none !important; }

.hint {
  position: fixed; bottom: calc(98px + var(--disc-h)); left: var(--sa-mid); transform: translateX(-50%); z-index: 39;
  font: 500 11px var(--ui); letter-spacing: .2em; text-transform: uppercase;
  color: var(--paper-dim);
  background: rgba(20, 20, 16, .5); border: 1px solid var(--line);
  padding: 8px 18px; border-radius: 99px; backdrop-filter: blur(8px);
  transition: opacity .6s; white-space: nowrap; max-width: 94vw; overflow: hidden; text-overflow: ellipsis;
}
.hint.hide { opacity: 0; pointer-events: none; }

.focus-bar[hidden] { display: none; }
.focus-bar {
  position: fixed;
  top: max(10px, var(--sa-t));
  left: var(--sa-mid); transform: translateX(-50%); z-index: 42;
  display: flex; align-items: center; gap: 18px;
  padding: 8px 8px 8px 22px; border-radius: 99px;
  background: var(--glass-2); backdrop-filter: blur(14px);
  border: 1px solid var(--gold); box-shadow: 0 18px 50px -18px rgba(0,0,0,.8);
  font: 500 12px var(--ui); letter-spacing: .1em; color: var(--gold-hi);
  max-width: calc(100vw - 16px - var(--sa-l) - var(--sa-r));
  box-sizing: border-box;
}
/* One line, centred on the buttons. It used to wrap onto two lines, which
   pushed the text off the buttons' centre line and made the bar lopsided. */
.focus-hint {
  flex: 0 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  line-height: 1;
}
.focus-actions { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; min-width: 0; }
.focus-bar button {
  display: inline-flex; align-items: center; justify-content: center;
  height: 34px; line-height: 1; box-sizing: border-box;
  padding: 0 15px; border-radius: 99px; border: 1px solid var(--line-strong);
  font: 700 10.5px var(--ui); letter-spacing: .14em; text-transform: uppercase; color: var(--paper);
  flex: 0 0 auto; white-space: nowrap;
}
.focus-bar button[aria-pressed="true"] { background: var(--gold); color: #1c1608; border-color: transparent; }
#focusExit { background: var(--paper); color: #1c1608; border-color: transparent; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ── plots panel (schedule table) ── */
.plots-panel[hidden] { display: none; }
.plots-panel {
  position: fixed; right: 18px; top: 72px; bottom: calc(96px + var(--disc-h)); z-index: 46;
  width: min(380px, calc(100vw - 28px));
  display: flex; flex-direction: column;
  background: var(--glass-2); backdrop-filter: blur(18px);
  border: 1px solid var(--gold); border-radius: 16px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .85);
  overflow: hidden;
}
.plots-panel[aria-hidden="false"] { display: flex; }
/* Schedule open: hide the floating plot card — use "Open floorplan" on the table */
body.plots-open .plot-card,
body.plots-open .plot-card.open {
  display: none !important;
  pointer-events: none;
}
.pp-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; padding: 16px 16px 8px; flex-shrink: 0;
}
.pp-kicker {
  font: 700 10px var(--mono); letter-spacing: .2em; text-transform: uppercase;
  color: var(--gold); margin-bottom: 4px;
}
.pp-head h3 { font: 400 22px var(--disp); }
.pp-close {
  width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--line);
  color: var(--paper-dim); font-size: 18px;
}
/* Houses / Flats ticks, in place of the old free-text filter */
.pp-kinds {
  display: flex; gap: 8px; padding: 0 16px 10px; margin: 0;
  border: 0; flex-shrink: 0;
}
.pp-kind {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 14px 8px 11px; border-radius: 99px; cursor: pointer;
  border: 1px solid var(--line-strong); background: rgba(15, 15, 12, .55);
  font: 600 12px var(--ui); letter-spacing: .04em; color: var(--paper-dim);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.pp-kind:hover { background: rgba(243, 221, 171, .07); }
.pp-kind input { position: absolute; opacity: 0; pointer-events: none; }
.pp-kind:has(input:checked) { color: var(--paper); border-color: rgba(243, 221, 171, .45); }
.pp-kind input:checked + .host-tick { background: var(--gold); border-color: var(--gold); }
.pp-kind input:checked + .host-tick::after {
  content: ''; position: absolute; left: 5px; top: 1px;
  width: 4px; height: 9px; border: solid #1c1608;
  border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.pp-kind input:focus-visible + .host-tick { outline: 2px solid var(--gold); outline-offset: 2px; }
.pp-empty {
  padding: 18px 16px; margin: 0; text-align: center;
  font: 400 13px var(--ui); color: var(--paper-faint);
}
.pp-search { display: block; padding: 0 16px 10px; flex-shrink: 0; }
.pp-search input {
  width: 100%; padding: 10px 14px; border-radius: 99px;
  background: rgba(15, 15, 12, .55); border: 1px solid var(--line-strong);
  color: var(--paper); font: 400 16px var(--ui); /* ≥16px avoids iOS zoom on focus */
}
.pp-search input:focus { outline: none; border-color: var(--gold); }
.pp-legend {
  display: flex; gap: 14px; padding: 0 16px 10px; flex-shrink: 0;
  font: 600 10px var(--ui); letter-spacing: .08em; text-transform: uppercase;
  color: var(--paper-faint);
}
.pp-swatch {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  margin-right: 6px; vertical-align: -1px;
}
.pp-swatch.avail { background: #7ec850; }
.pp-swatch.soon { background: #9a9a94; }
.pp-swatch.held { background: #9a9a94; }
.pp-status { font: 600 10px var(--ui); letter-spacing: .04em; text-transform: uppercase; color: var(--paper-faint); }
.pp-price { font: 600 12px var(--ui); color: var(--gold-hi); white-space: nowrap; }
.pp-table tbody tr[data-status="avail"] .pp-status { color: #8fd45e; }
.pp-table tbody tr[data-status="soon"] .pp-status { color: #a8a89e; }
.pp-table tbody tr[data-status="reserved"] .pp-plot::before { background: #f0a53c; }
.pp-table tbody tr[data-status="reserved"] .pp-status { color: #f0a53c; }
.pp-table tbody tr[data-status="sold"] .pp-status { color: #e0735e; }
.pp-table tbody tr[data-status="show"] .pp-status { color: #8dbbe6; }
.pp-table tbody tr[data-status="sold"] .pp-plot::before { background: #d9573f; }
.pp-swatch.reserved { background: #f0a53c; }
.pp-swatch.sold { background: #d9573f; }
.pp-swatch.show { background: #5b9bd5; }
.pp-table tbody tr[data-status="show"] .pp-plot::before { background: #5b9bd5; }
.price-block.is-reserved .pb-full { color: #f0a53c; }
.price-block.is-sold .pb-full { color: #e0735e; }
.pp-table-wrap {
  flex: 1 1 auto;
  min-height: 0; /* allow list to scroll inside the flex panel */
  overflow: auto;
  padding: 0 8px 8px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
.pp-table {
  width: 100%; border-collapse: collapse;
  font: 500 12px var(--ui);
}
.pp-table th {
  position: sticky; top: 0; z-index: 1;
  text-align: left; padding: 8px 8px;
  font: 700 9.5px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  /* CEO review r2 S5: opaque — at .96 the rows scrolling under it still read
     through the header on phones */
  color: var(--paper-faint); background: #12130f;
  border-bottom: 1px solid var(--line);
}
.pp-table td { padding: 9px 8px; border-bottom: 1px solid rgba(255,255,255,.05); color: var(--paper-dim); }
.pp-table tbody tr {
  cursor: pointer; transition: background .15s, color .15s;
}
.pp-table tbody tr:hover { background: rgba(217, 180, 117, .08); }
.pp-table tbody tr.is-on {
  background: rgba(217, 180, 117, .2);
  outline: 1px solid rgba(217, 180, 117, .55);
  color: var(--gold-hi);
}
.pp-table tbody tr.is-on td { color: var(--gold-hi); }
.pp-plot {
  font: 800 13px var(--mono); color: var(--paper);
  display: inline-flex; align-items: center; gap: 7px;
}
.pp-plot::before {
  content: ''; width: 8px; height: 8px; border-radius: 50%;
  background: #7ec850; box-shadow: 0 0 0 1px #1a1a14;
}
.pp-table tbody tr[data-status="soon"] .pp-plot::before,
.pp-table tbody tr[data-status="held"] .pp-plot::before { background: #9a9a94; }
.pp-foot {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  padding: 12px 16px 14px; border-top: 1px solid var(--line);
  flex-shrink: 0;
}
.pp-foot .btn-gold, .pp-foot .btn-ghost { padding: 10px 12px; font-size: 10.5px; }
.pp-foot .btn-gold:disabled, .pp-foot .btn-ghost:disabled { opacity: .35; cursor: not-allowed; }
/* CEO review S5, 28 Sep 2026: "See all prices" where buyers look — the plots
   schedule and the brochure sheet (the mood board already had it) */
.pp-prices { flex-shrink: 0; margin: 0; padding: 9px 16px 2px; border-top: 1px solid var(--line); text-align: center; font: 700 12px var(--ui); letter-spacing: .02em; }
.pp-prices + .pp-foot { border-top: 0; padding-top: 8px; }
.pp-prices a, .sheet-prices a { color: var(--gold-hi, #e8c98a); text-underline-offset: 3px; }
.sheet-prices { margin: -4px 0 16px; text-align: center; font: 700 13px var(--ui); }
/* A viewing is booked only after the affordability check passes on that home,
   so the button is shown locked rather than removed — a button that vanishes
   reads as a fault, one that is visibly waiting reads as a step. */
.tm-ctas .btn-gold:disabled, .tm-ctas .btn-ghost:disabled { opacity: .38; cursor: not-allowed; }
.tm-book-note {
  /* was max-width 42ch, which wrapped the sentence with room to spare */
  margin: 8px 0 0; max-width: none;
  font: 400 11px var(--mono); letter-spacing: .06em; text-transform: uppercase;
  color: var(--paper-faint);
}

/* ── WebGL builder overlay ── */
.webgl-overlay[hidden] { display: none; }
.webgl-overlay {
  position: fixed; inset: 0; z-index: 80;
  display: flex; flex-direction: column;
  background: #12130f;
}
.wgo-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  /* Landscape on a Dynamic Island phone puts the cut-out on one SIDE, so the
     title and the Close button need the horizontal insets, not just the top. */
  padding: calc(10px + var(--sa-t)) calc(16px + var(--sa-r)) 10px calc(16px + var(--sa-l));
  background: rgba(18, 19, 15, .96);
  border-bottom: 1px solid var(--gold);
}
.wgo-title { font: 600 14px var(--ui); letter-spacing: .08em; color: var(--gold-hi); }
.wgo-close-short { display: none; }
.webgl-overlay iframe {
  flex: 1; width: 100%; border: 0; background: #1a1c16;
  /* Avoid iOS Safari iframe bounce fighting the builder canvas */
  overscroll-behavior: none;
}

/* ── Holly chat (left) ── */
.assistant {
  position: fixed; left: 18px; top: 50%; transform: translateY(-50%); z-index: 45;
  width: min(340px, calc(100vw - 28px));
  height: min(560px, calc(100svh - 120px));
  display: flex; flex-direction: column;
  border-radius: 18px; overflow: hidden;
  background: var(--glass-2); backdrop-filter: blur(18px);
  border: 1px solid var(--line-strong);
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .8);
  transition: transform .5s var(--ease-slow), opacity .4s, visibility .5s;
}
body.chat-closed .assistant { transform: translateY(-50%) translateX(-118%); opacity: 0; visibility: hidden; }
.as-head { display: flex; align-items: center; gap: 11px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.as-face { width: 44px; height: 44px; border-radius: 50%; overflow: hidden; background: #efe6d2; flex: none; border: 1.6px solid var(--gold); }
.as-face svg, .as-face img { width: 100%; height: 100%; display: block; object-fit: cover; }
.as-name { font: 600 17px var(--disp); }
.as-role { font: 400 11px var(--ui); letter-spacing: .08em; color: var(--paper-faint); }
.as-role span { color: var(--sage); }
.as-id { flex: 1; }
.as-voice, .as-min {
  width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--line);
  display: grid; place-items: center; color: var(--paper-dim); font-size: 16px;
  transition: all .25s;
}
.as-voice[aria-pressed="true"] { color: var(--gold); border-color: var(--gold); }
.as-voice.is-muted,
.as-voice[aria-pressed="false"] { color: var(--terracotta, #c17a58); border-color: rgba(193, 122, 88, .55); }
.as-voice:hover { color: var(--gold-hi); border-color: var(--gold); }
.as-voice.is-muted:hover { color: var(--terracotta, #c17a58); }
.as-min:hover { color: var(--paper); }
.as-head { flex-shrink: 0; }
.as-log { flex: 1; min-height: 0; overflow-x: hidden; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px; scrollbar-width: thin; min-width: 0; }
.as-chips { flex-shrink: 0; }
.as-input { flex-shrink: 0; }
.msg {
  max-width: 90%; min-width: 0; box-sizing: border-box;
  padding: 10px 13px; border-radius: 13px; font-size: 13.6px; line-height: 1.55;
  overflow-wrap: anywhere; word-break: break-word; white-space: pre-wrap;
  animation: msg-in .4s var(--ease) both;
}
@keyframes msg-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.msg.bot { align-self: flex-start; background: rgba(242, 234, 216, .08); border: 1px solid var(--line); border-bottom-left-radius: 4px; }
.msg.user { align-self: flex-end; background: rgba(217, 180, 117, .16); border: 1px solid rgba(217, 180, 117, .34); border-bottom-right-radius: 4px; color: var(--gold-hi); }
.msg.typing { display: inline-flex; gap: 5px; padding: 13px 15px; }
.msg.typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--paper-dim); animation: blink 1.2s infinite; }
.msg.typing i:nth-child(2) { animation-delay: .18s; } .msg.typing i:nth-child(3) { animation-delay: .36s; }
@keyframes blink { 0%, 70%, 100% { opacity: .3; } 35% { opacity: 1; transform: translateY(-3px); } }
.as-chips { display: flex; gap: 6px; padding: 0 14px 10px; flex-wrap: wrap; }
.chip {
  border: 1px solid var(--line); border-radius: 99px; padding: 6px 12px;
  font: 700 10px var(--ui); letter-spacing: .1em; text-transform: uppercase; color: var(--paper-dim);
  transition: all .25s;
}
.chip:hover { color: var(--gold-hi); border-color: var(--gold); }
.chip.is-on { background: var(--gold); color: #1c1608; border-color: transparent; }
.as-input { display: flex; gap: 7px; padding: 10px 12px; border-top: 1px solid var(--line); align-items: center; }
.as-input input {
  flex: 1; min-width: 0; padding: 10px 14px; border-radius: 99px;
  background: rgba(15, 15, 12, .6); border: 1px solid var(--line-strong); color: var(--paper);
  /* ≥16px: iOS Safari zooms the page on focus below this, which hides the chat sheet */
  font: 400 16px var(--ui);
}
.as-input input:focus { outline: none; border-color: var(--gold); }
.as-mic, .as-send { width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; flex: none; }
.as-mic { border: 1px solid var(--line-strong); color: var(--paper-dim); transition: all .25s; }
.as-mic.listening { color: #e8735a; border-color: #e8735a; animation: pulse 1.5s infinite; }
.as-mic.connecting { color: var(--gold); border-color: var(--gold); animation: pulse 1s infinite; }
.as-mic.live {
  color: #fff; border-color: #e8735a; background: #e8735a;
  animation: livepulse 1.8s infinite;
}
@keyframes livepulse {
  0% { box-shadow: 0 0 0 0 rgba(232, 115, 90, .55); }
  70% { box-shadow: 0 0 0 10px rgba(232, 115, 90, 0); }
  100% { box-shadow: 0 0 0 0 rgba(232, 115, 90, 0); }
}
body.voice-live .as-role #asState { color: #e8927a; }
.as-send { background: var(--gold); color: #1c1608; font-size: 12px; }

/* ── plot card ── */
.plot-card {
  position: fixed; right: 18px; top: 50%; transform: translateY(-50%) translateX(115%); z-index: 44;
  width: min(320px, calc(100vw - 28px));
  background: var(--glass-2); backdrop-filter: blur(16px);
  border: 1px solid var(--gold); border-radius: 16px;
  padding: 22px 22px 18px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .85);
  transition: transform .5s var(--ease-slow);
}
.plot-card.open { transform: translateY(-50%); }
.pc-close { position: absolute; top: 8px; right: 10px; width: 32px; height: 32px; font-size: 18px; color: var(--paper-dim); }
.pc-kicker { font: 700 10px var(--mono); letter-spacing: .22em; text-transform: uppercase; color: var(--gold); margin-bottom: 8px; }
.plot-card h3 { font: 400 22px var(--disp); margin-bottom: 3px; }
.pc-meta { font: 400 11px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--paper-faint); margin-bottom: 10px; }
.pc-strap { font-size: 13.5px; color: var(--paper-dim); margin-bottom: 14px; }
.pc-ctas { display: grid; gap: 8px; }
.pc-ctas .btn-gold, .pc-ctas .btn-ghost { padding: 11px 18px; font-size: 11px; }

/* ── overlays ── */
.overlay { position: fixed; inset: 0; z-index: 60; visibility: hidden; }
.overlay.open { visibility: visible; }
.ov-scrim { position: absolute; inset: 0; background: rgba(8, 8, 6, .6); backdrop-filter: blur(7px); opacity: 0; transition: opacity .4s; }
.overlay.open .ov-scrim { opacity: 1; }
.sheet {
  position: absolute; left: var(--sa-mid); top: 50%;
  width: min(1020px, calc(100vw - 24px - 2 * var(--sa-x)));
  max-height: min(88svh, 820px);
  overflow-y: auto; scrollbar-width: thin;
  background:
    radial-gradient(ellipse 90% 50% at 50% -10%, rgba(224, 188, 120, .08), transparent 55%),
    linear-gradient(180deg, rgba(34, 36, 28, .98), rgba(18, 19, 15, .99));
  border: 1px solid var(--line-strong); border-radius: 20px;
  padding: 26px clamp(18px, 3vw, 34px) 30px;
  box-shadow: 0 70px 150px -50px rgba(0, 0, 0, .95), inset 0 1px 0 rgba(246, 239, 223, .06);
  transform: translate(-50%, -46%) scale(.97); opacity: 0; visibility: hidden;
  transition: transform .5s var(--ease-slow), opacity .4s, visibility .5s;
}
.sheet.on { transform: translate(-50%, -50%); opacity: 1; visibility: visible; }
.sheet-narrow { width: min(560px, calc(100vw - 24px - 2 * var(--sa-x))); }
.sheet-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-bottom: 20px; }
.sheet-kicker { font: 700 10.5px var(--mono); letter-spacing: .26em; text-transform: uppercase; color: var(--gold); margin-bottom: 6px; }
.sheet-head h2 { font: 400 clamp(22px, 3vw, 30px) var(--disp); }
.sheet-close { width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--line); font-size: 19px; color: var(--paper-dim); flex: none; transition: all .25s; }
.sheet-close:hover { color: var(--gold); transform: rotate(90deg); }
.sheet-fine { margin-top: 18px; font-size: 11.5px; color: var(--paper-faint); text-align: center; }
.sheet-copy { color: var(--paper-dim); margin-bottom: 18px; }
.sheet-img { width: 100%; height: auto; border-radius: 12px; margin-bottom: 18px; object-fit: contain; } /* IMG-QUALITY */

/* collection grid */
.type-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.type-card {
  text-align: left; border: 1px solid var(--line); border-radius: 14px; padding: 0;
  background: rgba(242, 234, 216, .03);
  transition: all .3s var(--ease);
  display: flex; flex-direction: column; overflow: hidden;
}
.type-card:hover { border-color: var(--gold); transform: translateY(-4px); }
.type-card .tc-media {
  display: block; width: 100%; padding: 14px 14px 0;
  cursor: pointer; text-align: left; touch-action: manipulation;
}
.type-card .tc-body {
  display: grid; gap: 4px; width: 100%; padding: 10px 14px 14px;
  text-align: left; min-height: 44px;
}
.type-card .elevation { width: 100%; height: auto; display: block; }
.type-card .tc-elev {
  width: 100%; height: 110px; object-fit: contain;
  background: #fff; border-radius: 8px; display: block; pointer-events: none;
}
/* Floorplans are portrait, so at the elevation's 110px they came out about
   70px wide — a grey smudge. Give the plan more height and it reads as a plan. */
.type-card .tc-elev.is-plan { height: 176px; padding: 6px; }
.type-card .el-stroke { stroke: var(--gold); fill: none; stroke-width: 1.4; stroke-linecap: round; stroke-dasharray: none; }
.type-card .el-fill { opacity: 1; }
.type-card h3 { font: 400 18px var(--disp); }
.tc-meta { font: 400 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--paper-faint); }
.tc-cta { font: 700 10px var(--ui); letter-spacing: .2em; text-transform: uppercase; color: var(--gold); margin-top: 4px; }
/* Plot cards carry the model's own green / grey release convention, so the
   grid and the masterplan say the same thing about the same plot. */
.tm-plan-note {
  font: 400 11px var(--ui); color: var(--paper-faint);
  margin: 8px 2px 0; line-height: 1.4;
}
.tc-status { font: 700 10px var(--ui); letter-spacing: .12em; text-transform: uppercase; color: #7ec850; }
.type-card.is-soon .tc-status { color: #9a9a94; }
/* Release price on each plot card — the share price, then the full market
   value, which must always travel with a Shared Ownership share price. */
.tc-price { display: block; font: 700 15px var(--ui); color: var(--paper); margin-top: 2px; }
.tc-price em { font: 400 11px var(--ui); font-style: normal; color: var(--paper-dim); }
.tc-price small { display: block; margin-top: 2px; font: 400 10px var(--mono); letter-spacing: .06em; text-transform: uppercase; color: var(--paper-faint); }
.type-card.is-soon .tc-price { color: var(--paper-dim); }
.type-card.is-soon .tc-elev { opacity: .55; filter: saturate(.35); }
.type-card.is-soon .tc-cta { color: var(--paper-faint); }
.type-card.is-soon:hover .tc-elev { opacity: 1; filter: none; }

/* plans sheet */
.tm-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 26px; }
.plan-toggle { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 99px; padding: 3px; gap: 3px; margin-bottom: 14px; }
.pt-btn { padding: 7px 16px; border-radius: 99px; font: 700 10.5px var(--ui); letter-spacing: .16em; text-transform: uppercase; color: var(--paper-dim); }
.pt-btn.is-on { background: var(--gold); color: #1c1608; }
.plan-stage { position: relative; aspect-ratio: 1/1.02; perspective: 1200px; background: #0e0e0c; border-radius: 12px; overflow: hidden; }
.plan-stage svg { width: 100%; height: 100%; }
/* AD / PNG plans are black linework — need a white plate */
.plan-stage.has-photos { aspect-ratio: 4/3; background: #fff; }
.fp-photo {
  margin: 0; width: 100%; height: 100%;
  display: flex; flex-direction: column; background: #fff;
  cursor: zoom-in; border: 0; padding: 0; color: inherit;
  -webkit-tap-highlight-color: rgba(217, 180, 117, .25);
  touch-action: manipulation;
}
.fp-photo img {
  flex: 1; width: 100%; height: 100%; object-fit: contain;
  display: block; background: #fff; pointer-events: none;
}
.fp-photo figcaption {
  flex: 0 0 auto; padding: 8px 12px;
  font: 700 10px var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--paper-dim); background: rgba(14, 14, 12, .92); text-align: center;
  pointer-events: none;
}
.plan-floors { display: flex; gap: 7px; margin-top: 10px; flex-wrap: wrap; }
.floor-plate { position: absolute; inset: 0; opacity: 0; pointer-events: none; transition: transform 1s var(--ease-slow), opacity .6s; transform-style: preserve-3d; }
.floor-plate.active { opacity: 1; pointer-events: auto; }
.plan-stage.mode-3d .floor-plate {
  opacity: 1;
  transform: rotateX(56deg) rotateZ(-38deg) translateZ(calc((var(--fi) - (var(--fn) - 1) / 2) * 130px)) scale(.64);
  filter: drop-shadow(10px 18px 14px rgba(0, 0, 0, .5));
}
/* Extra headroom so the isometric stack isn’t clipped — keep same box as flat (4/3) */
.plan-stage.mode-3d,
.plan-stage.mode-3d.has-photos {
  aspect-ratio: 4 / 3;
  background: #141410;
}
.plan-stage.mode-3d .fp-photo figcaption { display: none; }
.fp-room { fill: rgba(242, 234, 216, .04); stroke: rgba(242, 234, 216, .8); stroke-width: 1.3; }
.fp-room.wet { fill: rgba(139, 160, 129, .14); }
.fp-label { fill: var(--paper-dim); font-family: var(--ui); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.fp-dim { fill: var(--paper-faint); font-family: var(--mono); }
.fp-outline { fill: none; stroke: var(--gold); stroke-width: 2.2; }
.fp-title { font: 700 11px var(--mono); letter-spacing: .26em; text-transform: uppercase; fill: var(--gold); }
.tm-info { display: grid; gap: 12px; align-content: start; }
.tm-strap { font: 400 15px var(--disp); font-style: italic; color: var(--paper-dim); }
.tm-specs { list-style: none; display: flex; gap: 7px; flex-wrap: wrap; }
.tm-specs[hidden] { display: none; }   /* CEO review r2 S3: no empty chip row */
.tm-specs li { border: 1px solid var(--line); border-radius: 99px; padding: 5px 11px; font: 400 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--paper-dim); }
.tm-features { list-style: none; display: grid; gap: 7px; }
.tm-features li { padding-left: 22px; position: relative; font-size: 13.5px; color: var(--paper-dim); }
.tm-features li::before { content: ''; position: absolute; left: 1px; top: .45em; width: 10px; height: 6px; border-left: 2px solid var(--gold); border-bottom: 2px solid var(--gold); transform: rotate(-45deg); }
.tm-share { border-top: 1px solid var(--line); padding-top: 12px; font-size: 13.5px; }
.tm-share strong { color: var(--gold); }
.tm-guide { display: block; font: 400 11.5px var(--mono); color: var(--paper-faint); margin-top: 3px; }
.tm-ctas { display: grid; gap: 8px; }

/* gallery */
.gal-tabs {
  display: flex; flex-wrap: wrap; gap: 7px;
  padding: 0 0 14px;
}
.gal-tab {
  border: 1px solid var(--line); border-radius: 99px; padding: 8px 16px;
  font: 700 10.5px var(--ui); letter-spacing: .14em; text-transform: uppercase;
  color: var(--paper-dim); background: transparent; cursor: pointer;
}
.gal-tab.is-on { background: var(--gold); color: #1c1608; border-color: transparent; }
/* This was a CSS multi-column masonry (`columns: 3`). It only ever LOOKED
   like a grid because every placeholder was the same 2560x1429 frame, so the
   three columns came out identical heights by accident. The signed-off CGIs
   are not uniform — externals run 1.33 to 1.90, internals 1.68 to 1.83, and
   the local-area photographs are all 3:2 — so the browser balanced 10 items
   4/3/3 and the short column left a dead hole at the bottom right.
   A real grid cannot do that: the tile sets the shape, so every row lines up
   whatever lands in it and the last row is the only ragged one. The frame is
   16:10, the closest single ratio to the set (worst crop ~17%, on one 4:3
   external); the lightbox still opens every image uncropped. chime20260922q */
.gal-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.gal-grid button {
  display: flex; flex-direction: column; width: 100%; height: 100%;
  border-radius: 10px; overflow: hidden; border: 1px solid var(--line);
  cursor: zoom-in; padding: 0; background: #14140f; text-align: left;
}
.gal-grid img {
  width: 100%; aspect-ratio: 16 / 10; height: auto; display: block;
  object-fit: cover; object-position: center;
  transition: transform .6s var(--ease);
}
.gal-grid button:hover img { transform: scale(1.04); }
.gal-cap {
  display: block; flex: 1; padding: 8px 10px 10px;
  font: 400 11.5px var(--ui); color: var(--paper-dim); line-height: 1.35;
  /* two lines' worth, so a one-line caption doesn't make its row short */
  min-height: 49px;
}

/* screening */
.channel-tabs { display: flex; gap: 7px; margin-bottom: 14px; flex-wrap: wrap; }
.ch-tab { border: 1px solid var(--line); border-radius: 99px; padding: 8px 16px; font: 700 10.5px var(--ui); letter-spacing: .14em; text-transform: uppercase; color: var(--paper-dim); }
.ch-tab.is-on { background: var(--gold); color: #1c1608; border-color: transparent; }
.screen-holder { border-radius: 12px; overflow: hidden; border: 1px solid var(--line-strong); background: #000; }
.screen-holder canvas { width: 100%; height: auto; display: block; }

/* Three animations under each media wall heading */
.reel-picks {
  display: flex; gap: 6px; flex-wrap: wrap;
  padding: 0 0 10px;
}
.reel-pick {
  flex: 1 1 auto; min-width: 0;
  padding: 7px 12px; border-radius: 99px;
  border: 1px solid var(--line);
  font: 600 11px var(--ui); letter-spacing: .1em; text-transform: uppercase;
  color: var(--paper-dim); background: rgba(242, 234, 216, .03);
  transition: color .2s, border-color .2s, background .2s;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.reel-pick:hover { color: var(--paper); border-color: var(--line-strong); }
.reel-pick.is-on {
  color: var(--ink); background: var(--gold); border-color: var(--gold);
}
@media (max-width: 560px) {
  .reel-pick { padding: 8px 10px; font-size: 10px; letter-spacing: .06em; min-height: 40px; }
}
.screen-bar { display: flex; gap: 12px; align-items: center; padding-top: 12px; }
#screenPlay { width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--line-strong); color: var(--gold-hi); font-size: 12px; }
#screenScrub { flex: 1; }
input[type="range"] { -webkit-appearance: none; appearance: none; height: 24px; background: transparent; cursor: pointer; }
input[type="range"]::-webkit-slider-runnable-track { height: 3px; border-radius: 3px; background: linear-gradient(90deg, #3a3a30, var(--gold)); }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 15px; height: 15px; border-radius: 50%; margin-top: -6px; background: radial-gradient(circle at 35% 30%, var(--gold-hi), #967640); }
input[type="range"]::-moz-range-track { height: 3px; border-radius: 3px; background: linear-gradient(90deg, #3a3a30, var(--gold)); }
input[type="range"]::-moz-range-thumb { width: 15px; height: 15px; border: 0; border-radius: 50%; background: radial-gradient(circle at 35% 30%, var(--gold-hi), #967640); }

/* materials — CGI sample boards */
.mat-tabs {
  display: flex; flex-wrap: wrap; gap: 7px;
  padding: 0 28px 14px;
}
.mat-tab {
  border: 1px solid var(--line); border-radius: 999px;
  padding: 7px 14px; font: 700 10.5px var(--mono);
  letter-spacing: .08em; text-transform: uppercase; color: var(--paper-dim);
  transition: all .2s;
}
.mat-tab:hover { border-color: var(--line-strong); color: var(--paper); }
.mat-tab.is-on { border-color: var(--gold); color: var(--gold-hi); background: rgba(217, 180, 117, .1); }
/* main nav — the two shared-ownership specifications; reads as the primary
   split, so it carries more weight than the category row in the rail */
.mat-tabs-spec { gap: 9px; }
.mat-tab-spec {
  padding: 9px 22px; font-size: 11.5px; letter-spacing: .12em;
  border-color: var(--line-strong);
}
.mat-tab-spec.is-on {
  background: rgba(217, 180, 117, .16); border-color: var(--gold);
  box-shadow: inset 0 0 0 1px rgba(217, 180, 117, .25);
}
/* secondary nav — sits over the image board in the right-hand rail */
.mat-tabs-cat {
  padding: 0; gap: 6px;
  border-top: 1px solid var(--line); padding-top: 12px;
}
.mat-tab-cat { padding: 6px 11px; font-size: 9.5px; }
.mat-wrap {
  display: grid; grid-template-columns: 1.55fr 1fr; gap: 22px;
  align-items: start; padding: 0 28px 28px;
}
.mat-stage {
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  background: #14140f; min-height: 360px;
}
.mat-hero { margin: 0; display: grid; }
/* IMG-QUALITY (chime15): contain — no squash/crop; revert from assets/_img_quality_bak_chime15 */
.mat-hero img {
  display: block; width: 100%; height: auto; max-height: min(68vh, 640px);
  object-fit: contain; object-position: center; background: #12140f;
  image-orientation: from-image;
}
.mat-thumb img {
  display: block; width: 100%; aspect-ratio: 4/3; object-fit: contain;
  object-position: center; border-radius: 7px; background: #12140f;
  image-orientation: from-image;
}
.mat-hero figcaption {
  display: grid; gap: 4px; padding: 14px 16px;
  border-top: 1px solid var(--line);
}
.mat-hero figcaption strong { font: 400 18px var(--disp); color: var(--paper); }
.mat-hero figcaption span { font-size: 12.5px; color: var(--paper-dim); }
.mat-rail { display: grid; gap: 14px; align-content: start; }
.mat-meta h3 { font: 400 22px var(--disp); margin: 4px 0 8px; }
.mat-meta p { font-size: 13px; color: var(--paper-dim); margin: 0 0 8px; }
.mat-kicker {
  font: 700 10px var(--mono); letter-spacing: .14em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 6px;
}
.mat-note { font-size: 12px !important; color: var(--paper-mute, #8a8474) !important; }
.mat-thumbs {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
  max-height: 42vh; overflow: auto; padding-right: 2px;
}
.mat-thumb {
  display: grid; gap: 6px; text-align: left;
  border: 1px solid var(--line); border-radius: 11px; padding: 6px;
  background: transparent; transition: border-color .2s, background .2s;
}
.mat-thumb span {
  font-size: 11px; color: var(--paper-dim); line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mat-thumb:hover { border-color: var(--line-strong); }
.mat-thumb.is-on { border-color: var(--gold); background: rgba(217, 180, 117, .07); }
.mat-schedule-panel { padding: 22px 24px; color: var(--paper); }
.mat-schedule-panel h3 { font: 400 26px var(--disp); margin: 0 0 10px; }
.mat-palette-block { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }
.mat-palette-block h4 { font: 700 11px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--gold); margin: 0 0 10px; }
.mat-palette-block ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.mat-palette-block li { font-size: 13px; color: var(--paper-dim); }
.mat-palette-block b { color: var(--paper); margin-right: 6px; }
.mat-table {
  width: 100%; border-collapse: collapse; font-size: 12.5px;
}
.mat-table th, .mat-table td {
  border-bottom: 1px solid var(--line); padding: 8px 6px; text-align: left; color: var(--paper-dim);
}
.mat-table th {
  font: 700 9.5px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--gold);
}
.mat-table td b { color: var(--paper); }
.mat-schedule-detail {
  margin: 14px 0 0; padding: 0; list-style: none; display: grid; gap: 10px;
}
.mat-schedule-detail li {
  display: grid; gap: 2px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 10px;
}
.mat-schedule-detail b { color: var(--paper); }
.mat-schedule-detail span { font-size: 12px; color: var(--paper-dim); }

.mat-included {
  padding: 18px 20px 24px; color: var(--paper); overflow: auto; max-height: 100%;
}
.mat-included h3 { font: 400 26px var(--disp); margin: 0 0 8px; }
.mat-finish-group { margin-top: 18px; }
.mat-finish-group header h4 {
  font: 700 11px var(--mono); letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 4px;
}
.mat-finish-group header p {
  margin: 0 0 10px; font-size: 12.5px; color: var(--paper-dim); line-height: 1.45;
}
.mat-finish-group ul { list-style: none; margin: 0; padding: 0; }
.mat-finish-group li {
  display: flex; gap: 12px; align-items: flex-start; justify-content: space-between;
  padding: 10px 0; border-top: 1px solid var(--line);
}
.mat-finish-group li strong { display: block; font: 600 13px var(--ui); color: var(--paper); }
.mat-finish-group li span { display: block; margin-top: 3px; font-size: 12.5px; color: var(--paper-dim); line-height: 1.4; }
.mat-status {
  flex: 0 0 auto; margin-top: 2px;
  font: 700 9px var(--mono); letter-spacing: .12em; text-transform: uppercase;
  padding: 4px 8px; border-radius: 99px; border: 1px solid var(--line);
  color: var(--paper-faint); white-space: nowrap;
}
.mat-status--fixed { color: #b8d4a8; border-color: rgba(184, 212, 168, .35); background: rgba(184, 212, 168, .08); }
.mat-status--employer { color: var(--gold-hi); border-color: rgba(217, 180, 117, .4); background: rgba(217, 180, 117, .1); }
.mat-hero img { object-fit: contain; background: #12140f; } /* IMG-QUALITY chime15 */
.mat-empty { padding: 40px; color: var(--paper-dim); }
.config-ctas { display: grid; gap: 8px; }
.mood-count { background: rgba(217, 180, 117, .2); color: var(--gold-hi); border-radius: 99px; min-width: 20px; height: 20px; display: inline-grid; place-items: center; font-size: 10.5px; padding-inline: 5px; }

@media (max-width: 900px) {
  .mat-wrap { grid-template-columns: 1fr; }
  .mat-thumbs { max-height: none; grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* booking */
.ticket { display: grid; gap: 12px; }
.postcard-line { font: 600 17px var(--disp); font-style: italic; color: var(--paper-dim); }
.date-strip { display: flex; gap: 7px; overflow-x: auto; padding-bottom: 8px; scrollbar-width: thin; -webkit-overflow-scrolling: touch; }
.date-chip { flex: none; width: 58px; padding: 8px 0 9px; text-align: center; border: 1px solid var(--line); border-radius: 11px; display: grid; gap: 1px; color: var(--paper-dim); transition: all .25s; }
.date-chip small { font: 700 8.5px var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.date-chip b { font: 400 19px var(--disp); color: var(--paper); }
.date-chip.closed { opacity: .3; pointer-events: none; }
.date-chip.is-on { border-color: var(--gold); background: rgba(217, 180, 117, .1); }
.slot-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 7px; }
.tm-plan-photos { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 12px; }
.tm-plan-photos figure { margin: 0; border-radius: 10px; overflow: hidden; border: 1px solid var(--line); background: #fff; }
.tm-plan-photos img { display: block; width: 100%; height: auto; background: #fff; }
.tm-plan-photos figcaption { font: 700 9px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--paper-dim); padding: 6px 8px; }
.slot { border: 1px solid var(--line); border-radius: 9px; padding: 8px 2px; text-align: center; font: 400 12px var(--mono); color: var(--paper-dim); transition: all .2s; }
.slot.is-on { background: var(--gold); color: #1c1608; font-weight: 700; border-color: transparent; }
.slot.taken { opacity: .3; pointer-events: none; text-decoration: line-through; }
.ticket-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
.ticket-fields label { font: 700 9.5px var(--ui); letter-spacing: .2em; text-transform: uppercase; color: var(--paper-faint); display: grid; gap: 6px; }
.ticket-fields label .opt { font-weight: 400; text-transform: none; letter-spacing: .04em; }
.ticket-fields input, .ticket-fields select {
  padding: 11px 13px; background: rgba(15, 15, 12, .6); color: var(--paper);
  border: 1px solid var(--line-strong); border-radius: 9px; font: 400 14px var(--ui);
}
/* The dropdown list is painted by the browser, not by this box: it cannot use a
   translucent background, and without an explicit colour-scheme it opens with
   the light UA palette — that is the white flash before the options appear.
   #10110d is rgba(15,15,12,.6) already flattened over the sheet, so the closed
   control looks exactly as it did.
   Scoped to the select on purpose. Moving `color-scheme: dark` up to :root also
   darkens scrollbars and the unstyled checkbox — an improvement, but a visible
   change beyond this fix. */
.ticket-fields select {
  background-color: #10110d;
  color-scheme: dark;
}
.ticket-fields select option,
.ticket-fields select optgroup {
  background-color: #10110d;
  color: var(--paper);
}
.ticket-fields input:focus, .ticket-fields select:focus { outline: none; border-color: var(--gold); }
.ticket-fields input.is-invalid,
.ticket-fields input.is-invalid:focus {
  border-color: #d27a6a;
  box-shadow: 0 0 0 1px rgba(210, 122, 106, .35);
}
.ticket-fields .field-error {
  font: 500 12px var(--ui);
  letter-spacing: .02em;
  text-transform: none;
  color: #e8a090;
  min-height: 1.1em;
}
.ticket-fields .field-error[hidden] { display: none !important; }
.ticket-check { display: flex; gap: 9px; align-items: center; font-size: 12.5px; color: var(--paper-dim); cursor: pointer; }
.ticket-check input { accent-color: var(--gold); width: 15px; height: 15px; }
.ticket-note { font-size: 11.5px; color: var(--paper-faint); text-align: center; }
.ticket-note a { color: var(--gold); }
.ticket.confirmed .postcard-line { color: var(--sage); }

/* steps mini list */
.steps-mini { margin-top: 18px; display: grid; gap: 7px; }
.steps-mini div { display: flex; gap: 10px; align-items: baseline; font-size: 12.5px; color: var(--paper-dim); }
.steps-mini b { font: 700 10px var(--mono); color: var(--gold); }

/* lightbox — above sheets / dock / focus chrome on mobile */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(5, 5, 4, .94);
  display: grid; place-items: center;
  visibility: hidden; opacity: 0;
  pointer-events: none;
  transition: opacity .35s, visibility .35s;
  padding: max(12px, var(--sa-t)) max(12px, var(--sa-x)) max(12px, var(--sa-b));
  -webkit-tap-highlight-color: transparent;
}
.lightbox.open { visibility: visible; opacity: 1; pointer-events: auto; }
.lightbox[hidden] { display: none !important; }
.lightbox.open:not([hidden]) { display: grid; }
body.lightbox-open { overflow: hidden; }
body.lightbox-open .overlay { pointer-events: none; }
.lightbox figure { max-width: calc(92vw - 2 * var(--sa-x)); max-height: 86svh; display: grid; gap: 10px; justify-items: center; }
.lightbox img { max-width: 100%; max-height: 76svh; object-fit: contain; border-radius: 8px; }
.lightbox figcaption { font: 400 12px var(--mono); letter-spacing: .12em; color: var(--paper-dim); text-align: center; }
/* Floorplans: white plate so black PNG linework stays readable */
.lightbox.is-plan { background: rgba(20, 20, 18, .92); }
.lightbox.is-plan figure {
  background: #fff;
  padding: 12px;
  border-radius: 12px;
  max-width: min(calc(96vw - 2 * var(--sa-x)), 1100px);
  max-height: 88svh;
}
.lightbox.is-plan img {
  background: #fff; border-radius: 4px;
  max-height: min(72svh, 76vh);
}
.lightbox.is-plan figcaption { color: #3a3830; }
.lb-close {
  position: absolute;
  top: max(12px, var(--sa-t));
  right: max(12px, var(--sa-r));
  width: 48px; height: 48px;
  border: 1px solid var(--line); border-radius: 50%;
  font-size: 22px; color: var(--paper-dim);
  background: rgba(18, 20, 16, .75); z-index: 2;
}
.lb-prev, .lb-next {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 48px; height: 48px;
  border: 1px solid var(--line); border-radius: 50%;
  font-size: 26px; color: var(--paper-dim);
  background: rgba(18, 20, 16, .75); z-index: 2;
}
.lb-prev { left: max(10px, var(--sa-l)); }
.lb-next { right: max(10px, var(--sa-r)); }

/* mood drawer */
.mood-drawer {
  position: fixed; top: 0; right: 0; bottom: var(--disc-h); width: min(340px, 92vw); z-index: 82;
  background: var(--glass-2); backdrop-filter: blur(18px);
  border-left: 1px solid var(--line-strong);
  transform: translateX(105%); transition: transform .5s var(--ease-slow);
  display: flex; flex-direction: column;
}
.mood-drawer.open { transform: none; }
.mood-drawer header { display: flex; justify-content: space-between; align-items: center; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.mood-drawer h3 { font: 400 19px var(--disp); }
.mood-drawer header button { width: 34px; height: 34px; border: 1px solid var(--line); border-radius: 50%; font-size: 18px; color: var(--paper-dim); }
.mood-list { flex: 1; overflow-y: auto; padding: 16px 18px; display: grid; gap: 10px; align-content: start; }
.mood-item { border: 1px solid var(--line); border-radius: 11px; padding: 12px 13px; display: grid; grid-template-columns: 1fr auto; gap: 3px 8px; align-items: center; }
.mood-item b { font-size: 13.5px; }
.mood-item span { font: 400 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--paper-faint); grid-column: 1; }
.mood-item .mi-dots { grid-row: 1/3; grid-column: 2; display: flex; gap: 4px; }
.mood-item .mi-dots i { width: 13px; height: 13px; border-radius: 50%; background-size: cover; }
.mood-item button { grid-column: 2; justify-self: end; font: 700 9px var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--terra); }
.mood-empty { text-align: center; color: var(--paper-faint); font-size: 12.5px; padding: 24px 8px; border: 1px dashed var(--line); border-radius: 11px; }
.mood-note { padding: 13px 20px 20px; font-size: 11.5px; color: var(--paper-faint); border-top: 1px solid var(--line); }

/* toast */
.toast {
  position: fixed; left: var(--sa-mid); bottom: calc(84px + var(--disc-h)); transform: translate(-50%, 60px); z-index: 99;
  background: var(--glass-2); color: var(--paper); backdrop-filter: blur(12px);
  border: 1px solid var(--gold); border-radius: 99px; padding: 11px 22px;
  font-size: 12.5px; opacity: 0; transition: transform .45s var(--ease-slow), opacity .4s;
  pointer-events: none; max-width: min(90vw, 520px); text-align: center;
}
.toast.show { transform: translate(-50%, 0); opacity: 1; }
/* CEO review r5 (29 Sep 2026): the same line said again while it is still up — a second
   Point-to-move tap that cannot walk — pulses, so every tap visibly gets its answer */
.toast.show.pulse { animation: toastPulse .5s var(--ease); }
@keyframes toastPulse {
  0% { transform: translate(-50%, 0) scale(1); }
  35% { transform: translate(-50%, 0) scale(1.07); border-color: var(--gold-hi); box-shadow: 0 0 0 5px rgba(224, 188, 120, .28); }
  100% { transform: translate(-50%, 0) scale(1); }
}
/* Point to move: a ring answers at the finger (or the pointer) the moment a tap lands —
   gold when it walks, pale with a small shake when it cannot (the hint says why) */
.tap-ripple {
  position: fixed; left: 0; top: 0; width: 56px; height: 56px; margin: -28px 0 0 -28px; z-index: 30;
  border-radius: 50%; border: 3px solid #f2c462; pointer-events: none;
  background: radial-gradient(circle, rgba(242, 196, 98, .32) 0 5px, rgba(242, 196, 98, .1) 6px 100%);
  box-shadow: 0 0 0 1.5px rgba(27, 21, 13, .5), inset 0 0 0 1.5px rgba(27, 21, 13, .4);
  animation: tapRipple .8s cubic-bezier(.2, .7, .3, 1) forwards;
}
.tap-ripple.no { border-color: rgba(255, 250, 240, .9); background: rgba(255, 250, 240, .12); animation-name: tapRippleNo; }
@keyframes tapRipple {
  0% { transform: scale(.35); opacity: 1; }
  55% { transform: scale(1); opacity: .95; }
  100% { transform: scale(1.2); opacity: 0; }
}
@keyframes tapRippleNo {
  0% { transform: scale(.45); opacity: .95; }
  30% { transform: scale(.75) translateX(-4px); }
  55% { transform: scale(.78) translateX(4px); opacity: .8; }
  100% { transform: scale(.8) translateX(0); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .toast.show.pulse { animation: none; }
  .tap-ripple, .tap-ripple.no { animation: tapRippleFade .6s linear forwards; }
  @keyframes tapRippleFade { from { opacity: .9; } to { opacity: 0; } }
}

/* responsive */
@media (max-width: 900px) {
  .tm-grid, .config-wrap { grid-template-columns: 1fr; }
  .type-grid { grid-template-columns: repeat(2, 1fr); }
  .gal-grid { grid-template-columns: repeat(2, 1fr); }
  .assistant { width: min(300px, calc(100vw - 24px)); height: min(460px, calc(100dvh - 150px), calc(100svh - 150px), calc(100vh - 150px)); }
  .hint { display: none; }
}

/*
 * Phone / handset UI — iOS Safari, Android Chrome, Samsung Internet, etc.
 * Width OR touch-primary (coarse + no hover) so large Android phones
 * aren’t left on the desktop HUD.
 */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  :root { --dock-h: 86px; } /* dock + scroll arrows; sheets sit just above menu */

  .type-grid { grid-template-columns: 1fr; }
  .slot-grid { grid-template-columns: repeat(3, 1fr); }
  .tm-plan-photos { grid-template-columns: 1fr; }
  .ticket-fields { grid-template-columns: 1fr; }
  .hint { display: none; }
  .hud-quality span { display: none; }
  .lbl-full { display: none; }
  .lbl-short { display: inline; }

  /* Intro: fit short screens */
  .intro-card { padding: 28px 20px max(28px, var(--sa-b)); max-width: 100%; }
  #intro h1 { font-size: clamp(30px, 9vw, 42px); }
  .intro-sub { font-size: 14px; margin: 10px auto 20px; }
  .btn-gold, .btn-ghost { min-height: 44px; padding: 12px 20px; }

  /* Brand: icon only */
  .hud-brand { padding: 8px 10px; z-index: 43; }
  .hud-brand span { display: none; }

  /* Focus bar — compact gold pill: # · Plots · Turntable · Back */
  .focus-bar {
    gap: 0;
    padding: 5px;
    border-radius: 99px;
    width: auto;
    max-width: calc(100vw - 20px - var(--sa-l) - var(--sa-r));
    z-index: 50;
  }
  .focus-hint { display: none; }
  .focus-actions { width: auto; justify-content: center; gap: 4px; flex-wrap: nowrap; }
  .focus-bar button {
    flex: 0 0 auto;
    min-height: 40px;
    padding: 8px 12px;
    font-size: 10px;
    letter-spacing: .1em;
    text-align: center;
    min-width: 0;
    border-radius: 99px;
  }
  #focusLabels {
    min-width: 40px;
    padding: 8px 12px;
    font-size: 14px;
    letter-spacing: 0;
    font-weight: 800;
  }
  #focusExit { padding: 8px 14px; }

  /* Tools: reset + settings — tuck under focus bar when needed */
  .hud-tools {
    top: max(10px, var(--sa-t));
    right: max(10px, var(--sa-r));
    z-index: 49;
    gap: 6px;
  }
  body.focus-mode .hud-tools { display: none; }
  .hud-quality, .hud-reset { padding: 8px; min-width: 40px; min-height: 40px; }
  .quality-pop {
    top: max(52px, calc(var(--sa-t) + 44px));
    right: 10px;
    left: 10px;
    width: auto;
  }

  /*
   * Jess chip — bottom-LEFT only (not centred).
   * Centred placement sat in the walk/tap zone and kept opening chat
   * when visitors tried to move forward.
   */
  .hud-chat-toggle {
    left: max(10px, var(--sa-l));
    right: auto;
    transform: none;
    bottom: calc(var(--chrome-bottom) + 10px);
    padding: 9px 14px;
    min-height: 40px;
    font-size: 10px;
    letter-spacing: .12em;
    z-index: 38;
    max-width: min(42vw, 140px);
  }

  /*
   * Dock must sit ABOVE the gallery/homes overlay (z=60). Otherwise on mobile
   * the scrim eats taps — The Homes / Materials / etc. look dead while a sheet is open.
   */
  .dock-wrap {
    bottom: calc(8px + var(--disc-h));
    z-index: 72;
  }
  .overlay {
    /* Leave the dock band free so menu stays tappable */
    bottom: var(--chrome-bottom);
    overflow: hidden; /* prevent sheets painting under the dock */
    pointer-events: none; /* only sheets/scrim take taps — never block the dock */
  }
  .overlay.open { pointer-events: none; }
  .overlay .ov-scrim,
  .overlay .sheet.on {
    pointer-events: auto;
  }
  .overlay .ov-scrim { bottom: 0; } /* scrim fills the overlay box only */
  /* Dock ‹ › — comfortable tap target; sit outside the scroll mask */
  .dock-scroll-hint {
    width: 33px;
    height: 33px;
    min-width: 33px;
    min-height: 33px;
    font-size: 22px;
    line-height: 1;
    z-index: 6;
  }
  .dock-scroll-hint::before {
    content: '';
    position: absolute;
    inset: -4px;
  }
  .dock-scroll-hint--prev { margin-right: 6px; }
  .dock-scroll-hint--next { margin-left: 6px; }

  /* Dock — primary chrome; scroll with snap; edge fade hint */
  .dock {
    border-radius: 16px;
    padding: 4px 6px;
    justify-content: flex-start;
    scroll-snap-type: x proximity;
    touch-action: pan-x; /* allow horizontal scroll, keep taps on buttons */
    /* Fade ends: webkit + standard (Android Chrome / Samsung Internet) */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12px, #000 calc(100% - 14px), transparent);
    mask-image: linear-gradient(90deg, transparent, #000 12px, #000 calc(100% - 14px), transparent);
  }
  .dock button {
    min-width: 64px;
    min-height: 56px;
    padding: 8px 8px 7px;
    font-size: 10px;
    letter-spacing: .05em;
    scroll-snap-align: center;
    touch-action: manipulation; /* prefer tap over scroll when finger is on the button */
    position: relative;
    z-index: 2;
  }
  .dock button svg { width: 20px; height: 20px; }

  /* Plot card — short sheet; map stays tappable above */
  .plot-card {
    right: max(10px, var(--sa-r)); left: max(10px, var(--sa-l)); width: auto;
    top: auto;
    bottom: calc(var(--chrome-bottom) + 8px);
    /* fully below the screen edge, band included (reviewer: it peeked 8px under the disclaimer band) */
    transform: translateY(calc(100% + var(--chrome-bottom) + 40px));
    z-index: 52;
    padding: 12px 14px 12px;
    border-radius: 14px;
    max-height: min(34dvh, 34svh, 34vh, 260px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  .plot-card.open { transform: none; }
  .pc-close { width: 40px; height: 40px; top: 4px; right: 4px; }
  .plot-card h3 { font-size: 16px; margin-bottom: 2px; padding-right: 36px; }
  .pc-kicker { font-size: 9px; margin-bottom: 3px; }
  .pc-meta { font-size: 10px; margin-bottom: 5px; }
  .pc-strap {
    font-size: 12px; margin-bottom: 8px;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .pc-ctas { grid-template-columns: 1fr 1fr; gap: 6px; }
  .pc-ctas .btn-gold, .pc-ctas .btn-ghost {
    padding: 11px 8px; font-size: 10px; min-height: 42px; letter-spacing: .1em;
  }
  .pc-ctas #pcBook { grid-column: 1 / -1; }

  /* Jess chat — bottom sheet, not overlapping dock */
  .assistant {
    left: max(10px, var(--sa-l)); right: max(10px, var(--sa-r)); width: auto;
    top: auto;
    bottom: calc(var(--chrome-bottom) + 8px);
    transform: none;
    height: auto;
    max-height: min(46dvh, 46svh, 46vh, 360px);
    z-index: 53;
    border-radius: 16px;
  }
  /* Soft keyboard open: lifted via visualViewport in assistant.js */
  .assistant.kb-open {
    z-index: 85;
    bottom: 8px; /* JS overrides with keyboard overlap */
    max-height: min(70dvh, 520px);
    transition: bottom .2s ease, max-height .2s ease;
  }
  body.chat-kb .dock-wrap,
  body.chat-kb .hud-chat-toggle {
    visibility: hidden;
    pointer-events: none;
  }
  body.chat-closed .assistant { transform: translateY(120%); pointer-events: none; }
  body.card-open .assistant,
  body.plots-open .assistant { visibility: hidden; pointer-events: none; }

  /* Plots schedule — JS sets equal top/bottom gaps from focus bar & dock */
  .plots-panel {
    --plots-inset: 16px;
    --focus-bar-h: 52px;
    left: max(10px, var(--sa-l)); right: max(10px, var(--sa-r)); width: auto;
    top: calc(var(--sa-t) + var(--focus-bar-h) + var(--plots-inset));
    bottom: calc(var(--chrome-bottom) + var(--plots-inset));
    max-height: none;
    height: auto;
    z-index: 52;
    border-radius: 16px;
  }
  .plots-panel.kb-open {
    z-index: 86;
    /* bottom/max-height set by JS from visualViewport */
  }
  body.plots-kb .dock-wrap,
  body.plots-kb .hud-chat-toggle {
    visibility: hidden;
    pointer-events: none;
  }
  .pp-close { width: 40px; height: 40px; }
  .pp-head { padding: 12px 14px 6px; }
  .pp-head h3 { font-size: 20px; }
  .pp-search { padding: 0 12px 8px; }
  .pp-legend { padding: 0 12px 8px; gap: 12px; }
  .pp-table-wrap { padding: 0 6px 6px; }
  .pp-table { font-size: 12.5px; }
  .pp-table td, .pp-table th { padding: 10px 6px; }
  .pp-foot {
    padding: 10px 12px 12px;
    gap: 8px;
  }
  .pp-foot .btn-gold, .pp-foot .btn-ghost { min-height: 44px; }

  /* Sheets / modals — sit close above the dock without a large empty band */
  .sheet, .sheet-narrow {
    width: calc(100vw - 12px - 2 * var(--sa-x));
    top: max(8px, var(--sa-t));
    bottom: 6px;
    max-height: none;
    height: auto;
    left: var(--sa-mid);
    transform: translate(-50%, 12px) scale(.98);
    border-radius: 16px;
    padding: 14px 14px 14px;
    box-sizing: border-box;
  }
  .sheet.on, .sheet-narrow.on { transform: translate(-50%, 0); }
  .sheet-fine {
    margin-top: 12px;
    margin-bottom: 0;
    padding-bottom: 2px;
    line-height: 1.45;
    font-size: 12px;
  }
  .sheet-close { width: 44px; height: 44px; }

  /* Open plans — fit lifted 3D stack on short portrait screens */
  .sheet[data-sheet="type"] .sheet-head { margin-bottom: 10px; }
  .sheet[data-sheet="type"] .tm-grid { gap: 10px; }
  .sheet[data-sheet="type"] .tm-info { gap: 8px; }
  .plan-toggle { margin-bottom: 8px; }
  /* Flat + Lifted 3D share one stage size (max-height alone was shrinking flat width) */
  .plan-stage.has-photos,
  .plan-stage.mode-3d,
  .plan-stage.mode-3d.has-photos {
    width: 100%;
    max-width: 100%;
    aspect-ratio: auto;
    height: min(48dvh, 300px);
    max-height: min(48dvh, 300px);
    min-height: 220px;
  }
  .plan-stage.mode-3d .floor-plate {
    /* Tighter stack + smaller scale so all floors stay inside the stage */
    transform: rotateX(50deg) rotateZ(-30deg)
      translateZ(calc((var(--fi) - (var(--fn) - 1) / 2) * 52px))
      scale(.40);
    filter: drop-shadow(6px 10px 10px rgba(0, 0, 0, .45));
  }
  /* Floor chips: one row for up to three; 29 Sep 2026 — a five-chip pack (Brochure
     plan, Room sizes, Ground, First, Second) squeezed "Brochure plan" to 66px and
     clipped it, so the row now wraps and no chip shrinks below its label. */
  .plan-floors {
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 8px;
  }
  .plan-floors .chip {
    flex: 1 1 auto;
    min-width: calc(33.333% - 4px);
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 8px 4px;
    font-size: 9px;
    letter-spacing: .04em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Specs (m² / beds / house / From £TBA) — denser so they share lines */
  .tm-specs {
    gap: 5px;
  }
  .tm-specs li {
    padding: 4px 8px;
    font-size: 9px;
    letter-spacing: .04em;
  }
  .tm-share {
    font-size: 12.5px;
    line-height: 1.35;
    padding-top: 8px;
    margin: 0;
  }
  .tm-guide {
    display: inline;
    white-space: normal;
  }
  .tm-features { gap: 5px; }
  .tm-features li { font-size: 12.5px; }
  .tm-ctas {
    gap: 6px;
    padding-bottom: 0;
    margin: 0;
  }
  .tm-ctas .btn-gold,
  .tm-ctas .btn-ghost {
    min-height: 42px;
    padding-top: 10px;
    padding-bottom: 10px;
  }
  .type-card { min-height: 0; }
  .type-card:hover { transform: none; }
  .type-card .tc-media { padding: 12px 12px 0; }
  .type-card .tc-body { padding: 10px 12px 14px; min-height: 48px; }
  .type-card .tc-elev { background: #fff; height: 120px; }
  .type-card .tc-elev.is-plan { height: 168px; }

  .lightbox figure { max-width: 96vw; }
  .lightbox img { max-height: 70svh; }
  .gal-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .gal-grid button { min-height: 44px; }

  .wgo-bar {
    padding: 8px 10px;
    padding-top: max(8px, var(--sa-t));
    gap: 8px;
  }
  .wgo-title {
    font-size: 12px;
    letter-spacing: .06em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }
  .wgo-bar .btn-ghost {
    min-height: 36px;
    padding: 7px 12px;
    flex: none;
    font-size: 11px;
  }
  .wgo-close-full { display: none; }
  .wgo-close-short { display: inline; }

  .toast {
    bottom: calc(var(--chrome-bottom) + 12px);
    z-index: 90;
    font-size: 12px;
    padding: 10px 16px;
  }
  /* CEO review S2, 28 Sep 2026: the toast — and the arrival hints it carries
     ("Tap The Homes wall to browse types") — sat right on top of Jess's
     one-line bar, which stands at the same height above the dock. Lift it
     clear of the bar (up to two lines, ~52px); with her chat sheet open, show
     it near the top instead of across the sheet's text box. */
  body.chat-closed:not(.focus-mode):not(.card-open):not(.plots-open) .toast {
    bottom: calc(var(--chrome-bottom) + 76px);
  }
  body:not(.chat-closed):not(.card-open):not(.plots-open):not(.focus-mode) .toast {
    top: calc(max(12px, var(--sa-t)) + 64px);
    bottom: auto;
  }

  /* Hotspot labels: slightly larger on touch */
  .hs-label { font-size: 11px; padding: 7px 12px; }
}

/* Prefer dynamic viewport when supported (Android Chrome / modern Safari) */
@supports (height: 100dvh) {
  @media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
    .plot-card { max-height: min(34dvh, 260px); }
    .assistant { max-height: min(46dvh, 360px); }
    .plots-panel { max-height: none; }
    /* Keep chrome-bottom in the budget — omitting it lets sheets sit under the dock */
    .sheet, .sheet-narrow {
      max-height: none;
      bottom: 6px;
    }
  }
}
/* ============================================================
   Landscape handsets — vertical space is the scarce one
   ------------------------------------------------------------
   The phone rules turn Jess's chat into a bottom sheet, which is right in
   portrait. Turned sideways an iPhone has ~290 CSS px of page height once
   Safari's bars are counted, so that sheet swallowed the screen, clipped its
   own log and input, and sat across the focus bar. It also anchored to a flat
   10px, which put the avatar under the notch.

   Sideways there is width to spare and no height, so the chat goes back to
   being a column at the leading edge — inside the notch inset, above the dock,
   never over the centre chrome.
   ============================================================ */
@media ((hover: none) and (pointer: coarse)) and (orientation: landscape),
       (max-height: 520px) and (pointer: coarse) {
  :root {
    --chat-col: min(320px, calc(58vw - var(--sa-l) - var(--sa-r)));
    --chat-edge: calc(max(10px, var(--sa-l)) + var(--chat-col));
    --side-col: min(340px, calc(52vw - var(--sa-l) - var(--sa-r)));
    --side-edge: calc(100% - max(10px, var(--sa-r)) - var(--side-col));
  }
  .assistant {
    left: max(10px, var(--sa-l));
    right: auto;
    width: var(--chat-col);
    top: max(8px, var(--sa-t));
    bottom: calc(var(--chrome-bottom) + 6px);
    height: auto;
    max-height: none;
    transform: none;
    border-radius: 16px;
  }
  /* slide out sideways, not down — down would cross the dock */
  body.chat-closed .assistant {
    transform: translateX(-118%);
    pointer-events: none;
  }
  .assistant.kb-open {
    top: max(8px, var(--sa-t));
    bottom: 8px;
    max-height: none;
  }
  /* Plot card and accommodation schedule: columns on the trailing edge, so the
     model stays readable between them and the chat. As bottom sheets they had
     ~100px of height to work with here, which is not a schedule — the plot card
     scrolled two lines at a time and the schedule was unusable. */
  .plot-card,
  .plots-panel {
    left: auto;
    right: max(10px, var(--sa-r));
    width: var(--side-col);
    top: max(8px, var(--sa-t));
    bottom: calc(var(--chrome-bottom) + 6px);
    max-height: none;
    height: auto;
    border-radius: 16px;
  }
  .plot-card {
    transform: translateX(118%);
    overflow-y: auto;
    padding: 14px 16px 14px;
  }
  .plot-card.open { transform: none; }
  .plots-panel.kb-open,
  .plot-card.kb-open { bottom: 8px; }

  /* With a side column open there is no room to centre the focus bar on the
     whole screen — it landed across the panel. Centre it in what is left.
     The chat hides itself whenever a right-hand panel opens, so the two cases
     never both apply. */
  body:not(.chat-closed):not(.card-open):not(.plots-open) .focus-bar {
    left: calc((var(--chat-edge) + 100% - var(--sa-r)) / 2);
  }
  body.card-open .focus-bar,
  body.plots-open .focus-bar {
    left: calc((max(10px, var(--sa-l)) + var(--side-edge)) / 2);
  }
  .as-head { padding: 8px 10px; }
  .as-face { width: 34px; height: 34px; }
  .as-name { font-size: 15px; }
  .as-log { padding: 10px; gap: 8px; }
  /* Sideways the column is short. The suggestion chips are a convenience, the
     text box is the only way to type — so the chips go and the input stays.
     They are still there in portrait, where the sheet has room for both. */
  .as-chips { display: none; }
  .as-input { padding: 8px 10px; }
  .as-input input { padding: 9px 12px; }
  .as-mic, .as-send { width: 32px; height: 32px; }

  /* Accommodation schedule: the head, filter, legend and footer were eating
     the panel, leaving one or two rows of the table visible with nowhere to
     scroll. Trim the chrome so the schedule itself gets the height. */
  .plots-panel .pp-legend { display: none; }
  /* "ACCOMMODATION SCHEDULE" already names the panel — the "Plots" heading
     under it is a second title costing 20px of a 130px table. */
  .plots-panel .pp-head h3 { display: none; }
  .pp-head { padding: 6px 10px 2px; align-items: center; }
  .pp-kicker { font-size: 8.5px; letter-spacing: .18em; margin-bottom: 0; }
  .pp-close { width: 26px; height: 26px; font-size: 15px; }
  .pp-search { padding: 0 10px 5px; }
  .pp-search input { padding: 6px 10px; font-size: 11.5px; }
  .pp-table-wrap { padding: 0 6px 6px; }
  .pp-table th { padding: 4px 6px; }
  .pp-table td { padding: 4px 6px; font-size: 12px; line-height: 1.15; }
  /* Six columns in a 340px column wrapped every cell onto two lines, which is
     what made the rows tall enough that only two fitted. Keep every column and
     let the table scroll sideways inside its own box instead — nothing is
     hidden, and the rows come back to a single line. */
  .pp-table { min-width: 100%; width: max-content; }
  .pp-table th, .pp-table td { white-space: nowrap; }
  .pp-table-wrap { overflow: auto; overscroll-behavior: contain; }
  .pp-foot { padding: 5px 10px 7px; gap: 6px; }
  .pp-foot .btn-gold, .pp-foot .btn-ghost { padding: 6px 8px; font-size: 9px; min-height: 32px; }

  /* Sideways there is not enough height for both the dock and a schedule you
     can actually read — the table was down to two rows with nowhere to scroll.
     While the schedule is open the dock steps aside; the panel has its own
     close button, and reading the schedule is the task in hand. */
  body.plots-open .dock-wrap { visibility: hidden; pointer-events: none; }
  body.plots-open .plots-panel { bottom: calc(8px + var(--disc-h)); }
  /* CEO review r2, should-fix 4: in the masterplan close-up the dock covered the
     front of the table (Discovery View 38-42 could not be tapped). It steps aside
     there too; the focus bar's BACK returns to the room and brings it back. */
  body.focus-mode .dock-wrap { visibility: hidden; pointer-events: none; }
  body.focus-mode .plot-card { bottom: calc(8px + var(--disc-h)); }

  /* Same trade for the sheets. On phones the overlay deliberately stops short
     of the dock so the menu stays tappable behind it — worth 93px of an 812px
     portrait page, but 107px of a 292px landscape one, a third of the sheet.
     Every sheet was 168px tall with a 45px header inside it. Sideways the
     sheet takes the full height and the dock steps aside; each sheet has its
     own close button. */
  body.sheet-open .dock-wrap { visibility: hidden; pointer-events: none; }
  .overlay { bottom: 0; }
  .sheet, .sheet-narrow {
    top: max(8px, var(--sa-t));
    bottom: max(8px, var(--sa-b));
    padding: 12px 14px 12px;
  }
  .sheet-head { margin-bottom: 10px; }
  .sheet-head h2 { font-size: 19px; }
  .sheet-kicker { font-size: 9px; margin-bottom: 3px; }
  .sheet-close { width: 34px; height: 34px; }

  /* Sideways there is width, not height. The grids that stack on a portrait
     phone go back to columns, which is what stopped the floorplan being a
     90px sliver under a header. */
  .sheet[data-sheet="type"] .tm-grid { grid-template-columns: 1.15fr 1fr; gap: 14px; }
  .config-wrap { grid-template-columns: 1.1fr 1fr; gap: 14px; }
  .type-grid { grid-template-columns: repeat(3, 1fr); }
  .gal-grid { grid-template-columns: repeat(3, 1fr); }
  .plan-stage { aspect-ratio: auto; height: min(52vh, 210px); }
  .plan-stage.has-photos,
  .plan-stage.mode-3d,
  .plan-stage.mode-3d.has-photos {
    height: min(52vh, 210px);
    max-height: min(52vh, 210px);
    min-height: 120px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001s !important; transition-duration: .15s !important; }
}

/* ---------- affordability checker ---------- */
.af-wrap {
  /* No horizontal padding of its own. The sheet already pads itself, and adding
     more here inset every panel 28px inside the header above it — the kicker and
     title sat proud of the boxes they belong to. */
  padding: 0 0 8px;
  --af-lab-h: 2.6em;    /* two lines of the mono label */
  --af-hint-h: 2.9em;   /* two lines of hint */
}
.af-lead { font-size: 13.5px; color: var(--paper-dim); margin: 0 0 16px; max-width: 62ch; }
.af-fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px 18px; margin-bottom: 18px; align-items: start;
}
/* Three fixed bands — label, input, hint — so every field in a row starts and
   ends on the same line whatever the wording does. Without this a label that
   wraps to two lines drags its own input down and the row falls out of step. */
.af-fields label {
  display: grid; grid-template-rows: var(--af-lab-h) auto var(--af-hint-h); gap: 6px;
  font: 700 10.5px var(--mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--paper-dim);
}
.af-lab {
  display: block; align-self: end;
  /* the asterisk rides with the last word rather than dropping to its own line */
  text-wrap: balance;
}
.af-req { color: var(--gold); margin-left: 2px; }
.af-prefix, .af-suffix { position: relative; display: block; }
.af-prefix::before, .af-suffix::after {
  position: absolute; top: 50%; transform: translateY(-50%);
  font: 400 14px var(--disp); color: var(--paper-mute, #8a8474); pointer-events: none;
}
.af-prefix::before { content: '£'; left: 12px; }
.af-suffix::after { content: '%'; right: 12px; }
.af-fields input {
  width: 100%; box-sizing: border-box; height: 46px;
  border: 1px solid var(--line); border-radius: 10px; background: #14140f;
  padding: 0 12px 0 26px; color: var(--paper); font: 400 15px var(--disp);
}
/* Kill the number spinners — they crowd the % suffix and look nothing like the
   rest of the suite. The fields stay type=number for the mobile keypad. */
.af-fields input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.af-fields input[type="number"]::-webkit-outer-spin-button,
.af-fields input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.af-suffix input { padding: 0 26px 0 12px; }
.af-fields input:focus { outline: none; border-color: var(--gold); }
.af-fields input:read-only { background: #191913; color: var(--paper-dim); border-style: dashed; }
.af-hint { align-self: start; font: 400 11px/1.35 var(--sans, inherit); letter-spacing: 0; text-transform: none; color: var(--paper-mute, #8a8474); }
.af-ctas { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.af-note { font-size: 11.5px; color: var(--paper-mute, #8a8474); margin: 0; max-width: 70ch; }

.af-verdict { border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; margin-bottom: 16px; }
.af-verdict.is-pass { border-color: rgba(143, 173, 132, .5); background: rgba(143, 173, 132, .08); }
.af-verdict.is-fail { border-color: rgba(217, 180, 117, .4); background: rgba(217, 180, 117, .06); }
.af-flag { font: 700 10px var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--gold); margin: 0 0 6px; }
.af-verdict h3 { font: 400 24px var(--disp); margin: 0 0 8px; color: var(--paper); }
.af-verdict p { font-size: 13.5px; color: var(--paper-dim); margin: 0 0 6px; }
.af-soft { font-style: italic; }

.af-break { list-style: none; margin: 0 0 16px; padding: 0; display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.af-break li { display: flex; justify-content: space-between; gap: 16px; background: #14140f; padding: 10px 14px; font-size: 13px; color: var(--paper-dim); }
.af-break b { color: var(--paper); font-weight: 400; font-family: var(--disp); font-size: 15px; }
.af-break .af-total { background: rgba(217, 180, 117, .1); }
.af-break .af-total b { color: var(--gold-hi); }
/* CEO review r2, should-fix 3: the visitor's own deposit and the scheme minimum
   are two rows; a deposit under the minimum is flagged, never replaced */
.af-break .af-dep.is-short b { color: #f0a53c; }
.af-break b { white-space: nowrap; }   /* the label wraps on a phone, never the figure */
.af-break .af-min b { color: var(--paper-dim); }
.af-verdict .af-why { color: var(--paper); }
.af-more { font-size: 13px; color: var(--paper-dim); margin: 0 0 14px; }

.af-alts { margin-bottom: 16px; }
.af-alts h4 { font: 700 10.5px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--gold); margin: 0 0 10px; }
.af-alts p { font-size: 13px; color: var(--paper-dim); margin: 0; }
.af-alts ul { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.af-alts button {
  width: 100%; display: grid; gap: 3px; text-align: left;
  border: 1px solid var(--line); border-radius: 11px; padding: 10px 12px;
  background: transparent; transition: border-color .2s, background .2s;
}
.af-alts button:hover { border-color: var(--gold); background: rgba(217, 180, 117, .07); }
.af-alts button b { font: 400 16px var(--disp); color: var(--paper); }
.af-alts button span { font-size: 11.5px; color: var(--paper-dim); }
.af-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 12px; }
.af-actions .btn-ghost { text-decoration: none; display: inline-flex; align-items: center; }

@media (max-width: 900px) {
  .af-wrap { padding: 0 0 6px; --af-lab-h: auto; --af-hint-h: auto; }
  .af-fields { grid-template-columns: 1fr; }
  .af-lab { align-self: start; }
}

/* Why "Book a viewing" landed on this sheet — Chime's own sentence, shown on the
   sheet itself (the chat may be minimised). See sendToAffordability() in main.js. */
.gate-note {
  margin: 0 0 16px; padding: 12px 16px; border-radius: 12px;
  border: 1px solid rgba(224, 188, 120, .55); background: rgba(224, 188, 120, .12);
  color: var(--paper, #f4efe2); font-size: .95rem; line-height: 1.5;
}
.gate-note[hidden] { display: none; }

/* Booking buttons that wait on an affordability pass are hidden with the
   hidden attribute; the button classes set display, so say it outright. */
#tmBook[hidden], #pcBook[hidden], #visitBook[hidden] { display: none !important; }

/* ============================================================
   Readability pass — Chime, 24 September 2026
   "I find it hard to read the written information with the colour ways we
   have." Two asks, both done here, plus the palette under them:
     1. the overall price big, large and easy to read on every plot
     2. the calculator's input fields white with black text
   ============================================================ */
/* 1 ── price block: full market value headline, share price beneath */
.price-block {
  display: flex; flex-direction: column; gap: 2px;
  background: #0d0d0a; border: 1px solid rgba(224, 188, 120, .55);
  border-radius: 12px; padding: 10px 14px 11px; margin: 6px 0 4px;
  text-align: left;
}
.pb-label {
  font: 700 11px var(--ui); letter-spacing: .14em; text-transform: uppercase;
  color: #f1e6cc;
}
.pb-full {
  font: 700 24px/1.05 var(--ui); color: #fff; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
}
.pb-share { font: 600 14px var(--ui); color: #f1e6cc; }
.pb-share strong { font-weight: 800; font-size: 16px; color: var(--gold-hi); }
.pb-note { font: 400 12px/1.35 var(--ui); color: #e6dcc6; margin-top: 2px; }
.price-block.is-tba .pb-full { color: #f1e6cc; }
.price-block.is-card { margin: 8px 0 2px; }
.price-block.is-card .pb-full { font-size: 22px; }
.price-block.is-sheet { padding: 11px 15px 12px; border-width: 1px; }
.price-block.is-sheet .pb-full { font-size: 26px; }
.price-block.is-sheet .pb-share { font-size: 14.5px; }
.price-block.is-sheet .pb-share strong { font-size: 16.5px; }
.price-block.is-pc .pb-full { font-size: 24px; }
.pc-price { margin: 0 0 10px; }
.pc-price[hidden], .tm-price[hidden] { display: none; }
.tm-price .price-block { margin: 0; }
.type-card.is-soon .price-block { opacity: .92; }
@media (max-width: 720px) {
  .pb-full, .price-block.is-card .pb-full, .price-block.is-pc .pb-full { font-size: 21px; }
  .price-block.is-sheet .pb-full { font-size: 22px; }
  .pb-share { font-size: 13.5px; }
  .pb-share strong { font-size: 15px; }
}

/* 2 ── affordability calculator: white fields, black text */
.af-fields input {
  background: #fff; color: #000; border: 1.5px solid #cfc6b2;
  font: 600 17px var(--ui); caret-color: #000;
}
.af-fields input::placeholder { color: #6b6b6b; }
.af-fields input:focus { border-color: var(--gold); box-shadow: 0 0 0 3px rgba(224, 188, 120, .45); }
/* The plot sets the value and the field is locked — still white and black
   so it reads, with a grey wash and dashed edge to say "not yours to edit". */
.af-fields input:read-only { background: #eeeae1; color: #000; border-style: dashed; border-color: #a99f89; }
.af-prefix::before, .af-suffix::after { color: #000; font: 600 16px var(--ui); z-index: 1; }
.af-fields label { color: #f1e6cc; font-size: 11px; }
.af-hint { color: #e6dcc6; font-size: 12px; }
.af-lead, .af-note { color: #e6dcc6; }
/* keep autofill from repainting the fields yellow/blue */
.af-fields input:-webkit-autofill { -webkit-box-shadow: 0 0 0 40px #fff inset; -webkit-text-fill-color: #000; }

/* booking gate pop-up — above the sheets (overlay is z 60) */
.gate-pop {
  position: fixed; inset: 0; z-index: 95; display: grid; place-items: center;
  background: rgba(6, 6, 4, .55); backdrop-filter: blur(3px);
  padding: 20px calc(16px + var(--sa-x));
  opacity: 0; transition: opacity .2s var(--ease);
}
.gate-pop[hidden] { display: none; }
.gate-pop.show { opacity: 1; }
.gp-card {
  width: min(420px, 100%); text-align: center;
  background: #16160f; border: 1.5px solid var(--gold); border-radius: 18px;
  padding: 26px 24px 22px; box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .9);
  transform: translateY(8px) scale(.98); transition: transform .25s var(--ease-slow);
}
.gate-pop.show .gp-card { transform: none; }
.gp-icon {
  display: inline-grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: var(--gold); color: #1c1608; font: 800 24px var(--ui); margin-bottom: 12px;
}
.gp-msg { font: 600 19px/1.35 var(--ui); color: #fff; margin: 0 0 18px; }
.gp-card .btn-gold { min-width: 200px; padding: 12px 22px; }

/* gallery: Typical Home / Typical Apartment sub-heads (Outside / Inside) */
.gal-sub {
  grid-column: 1 / -1; margin: 6px 2px -2px;
  font: 700 11.5px var(--ui); letter-spacing: .16em; text-transform: uppercase;
  color: var(--gold);
}
.gal-sub:first-child { margin-top: 0; }
.gal-cap { font-size: 12.5px; color: #f1e6cc; }

/* aligned floor plans — one shared board per plot (plan-packs.js PLAN_ALIGN) */
.fp-photo svg.fp-align {
  flex: 1; width: 100%; height: auto; min-height: 0; display: block;
  background: #fff; pointer-events: none; padding: 6px; box-sizing: border-box;
}


/* "AI-generated imagery. For illustrative purposes only." — gallery grid, lightbox, media wall */
.gal-disc { display: block; margin-top: 4px; font: italic 400 11px var(--ui); color: var(--paper-faint); }
.lb-disc { margin: 4px 0 0; text-align: center; font: italic 400 12px var(--ui); color: var(--paper-dim); }
.lb-disc[hidden] { display: none; }
.media-disc { margin: 8px 2px 0; text-align: center; font: italic 400 12px var(--ui); color: var(--paper-dim); }
/* Specification sheet (29 Sep 2026, round b): the CGIs there carry the line too — the same words on every board, so every board is the same height */
.mat-hero figcaption .mat-disc { display: block; font: italic 400 11px/1.3 var(--ui); color: var(--paper-faint); }

/* the show home (not for sale) never offers the affordability check */
#tmAfford[hidden] { display: none !important; }
/* hidden must win over .btn-ghost display (builder only for released plots) */
#tmBuilder[hidden], #pcOpenBuilder[hidden] { display: none !important; }

/* Specification sheet — one size for every picture (client, 24 Sep 2026:
   "when clicking on the internal images make sure the pop up and images are
   not resizing"). The picture sat at its own aspect ratio (1.68 to 1.83) and
   the rail grew with each board's text and thumb count, so the pop-up jumped
   between 635 and 704 px as you clicked. Now the picture is a fixed 16:9 frame
   filled edge to edge, the caption has a fixed two-line height, and on
   desktop the pop-up itself has a fixed height; anything longer scrolls
   inside it. */
.mat-stage { min-height: 0; }
.mat-hero img,
.mat-hero img[src] {
  width: 100%; height: auto; max-height: none;
  aspect-ratio: 16 / 9; object-fit: cover; object-position: center;
}
.mat-hero figcaption { min-height: 84px; align-content: start; }
.mat-hero figcaption strong,
.mat-hero figcaption span {
  display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;
}
.mat-hero figcaption strong { -webkit-line-clamp: 1; }
.mat-hero figcaption span { -webkit-line-clamp: 2; min-height: calc(2 * 1.45em); line-height: 1.45; }
.mat-meta h3 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; min-height: calc(2 * 1.2em); line-height: 1.2; }
.mat-meta p { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; min-height: calc(3 * 1.45em); line-height: 1.45; }
/* the small "Houses · internal finishes" line is one line, not a 3-line block (that was the big gap under it) */
.mat-meta p.mat-kicker { display: block; min-height: 0; -webkit-line-clamp: unset; line-height: 1.3; }
@media (min-width: 901px) {
  .sheet[data-sheet="configurator"] { height: min(88svh, 820px); }
}

/* visitor star rating (js/rating.js) — shares the booking pop-up's shell */
.hud-rate { color: var(--gold); }
.rate-kicker { font: 700 10.5px var(--ui); letter-spacing: .18em; text-transform: uppercase; color: var(--gold); margin: 0 0 6px; }
.rate-title { font: 400 22px/1.25 var(--disp); color: #fff; margin: 0 0 14px; }
.rate-stars { display: flex; justify-content: center; gap: 6px; }
.rate-star {
  width: 46px; height: 46px; padding: 4px; border: 0; background: none; cursor: pointer;
  border-radius: 10px; transition: transform .15s var(--ease);
}
.rate-star svg { width: 100%; height: 100%; display: block; fill: none; stroke: #8f866f; stroke-width: 1.6; stroke-linejoin: round; transition: fill .15s, stroke .15s; }
.rate-star.is-on svg { fill: var(--gold); stroke: var(--gold); }
.rate-star:hover { transform: scale(1.08); }
.rate-star:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.rate-hint { font: 600 13px var(--ui); color: #e6dcc6; margin: 6px 0 12px; min-height: 18px; }
.rate-note {
  width: 100%; box-sizing: border-box; resize: none; margin: 0 0 14px;
  background: #fff; color: #111; border: 1px solid var(--line-strong); border-radius: 10px;
  padding: 10px 12px; font: 400 14px/1.4 var(--ui);
}
.rate-actions { display: grid; gap: 8px; }
.rate-actions .btn-gold:disabled { opacity: .4; cursor: not-allowed; }
@media (max-width: 480px) { .rate-star { width: 42px; height: 42px; } .rate-title { font-size: 20px; } }

/* Settings → Controls → Invert mouse */
.quality-pop .qp-sep { margin-top: 12px; }
.qp-toggle {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 8px 10px; border-radius: 9px; color: var(--paper-dim); font: 600 12px var(--ui);
}
.qp-toggle:hover { background: rgba(255, 255, 255, .06); color: var(--paper); }
.qp-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.qp-switch {
  flex: none; width: 34px; height: 20px; border-radius: 99px; position: relative;
  background: rgba(255, 255, 255, .14); border: 1px solid var(--line-strong); transition: background .2s;
}
.qp-switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--paper); transition: transform .2s var(--ease);
}
.qp-toggle input:checked + .qp-switch { background: var(--gold); border-color: var(--gold); }
.qp-toggle input:checked + .qp-switch::after { transform: translateX(14px); background: #1c1608; }
.qp-toggle input:focus-visible + .qp-switch { outline: 2px solid var(--gold); outline-offset: 2px; }
.qp-text small { display: block; font: 400 10.5px var(--ui); color: var(--paper-faint); margin-top: 1px; }

/* Masterplan view: the reset / rating / quality buttons were pushed down to
   58px so the focus bar could never run into them on a narrow window — which
   left them hanging below the bar on every normal desktop. Where the two
   cannot meet (the bar is ~780px wide and centred, the tools ~180px at the
   right, so from ~1240px up) they sit on the bar's centre line instead:
   bar top 10 + (52 - 40) / 2. Narrower windows and touch keep the old rule. */
@media (min-width: 1240px) and (hover: hover) and (pointer: fine) {
  body.focus-mode .hud-tools { top: calc(max(10px, var(--sa-t)) + 6px); }
}

/* "Email the team" help panel on the affordability result */
.af-mailbox {
  margin: -2px 0 14px; padding: 12px 14px; border: 1px solid var(--line-strong); border-radius: 12px;
  background: rgba(255, 255, 255, .04); color: #e6dcc6; font-size: 13px;
}
.af-mailbox p { margin: 0 0 10px; }
.af-mailbox a:not(.btn-ghost) { color: var(--gold-hi); }
.af-mailbtns { display: flex; flex-wrap: wrap; gap: 8px; }
.af-mailbtns .btn-ghost { text-decoration: none; display: inline-flex; align-items: center; }

/* ============================================================
   No visible scrollbars anywhere in the suite (client, 25 Sep 2026).
   Scrolling still works by wheel, trackpad, touch and keyboard — only the
   bar itself is hidden. !important because several panels set
   scrollbar-width: thin on their own selectors.
   ============================================================ */
* { scrollbar-width: none !important; }
*::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; }

/* Desktop / laptop: the media wall and the materials board fit the sheet
   without scrolling. The sheet is at most min(88svh, 820px) tall; what
   remains after the header, tabs and controls goes to the picture. */
@media (min-width: 901px) {
  .sheet[data-sheet="screening"] .screen-holder { width: fit-content; max-width: 100%; margin-inline: auto; }
  .sheet[data-sheet="screening"] .screen-holder canvas {
    width: auto; max-width: 100%; height: auto;
    max-height: calc(min(88svh, 820px) - 285px);
  }
  /* Materials board (25 Sep, second pass): the pop-up is only as tall as its
     content, and that height never changes between Houses/Flats, categories
     or thumbnails — the picture is a fixed 16:9 frame with a fixed caption,
     and the right-hand column is a fixed height whose thumbnail area takes
     whatever is left (it scrolls inside, without a bar, when a category has
     more boards than fit). */
  .sheet[data-sheet="configurator"] { height: auto; }
  .sheet[data-sheet="configurator"] .mat-rail {
    display: flex; flex-direction: column; gap: 12px;
    height: min(484px, calc(min(88svh, 820px) - 220px));
  }
  /* thumbnails in the renders' own wide shape — no black bands above and
     below, so a whole row always shows in the thumbnail area */
  .sheet[data-sheet="configurator"] .mat-thumb img { aspect-ratio: 16 / 9; }
  .sheet[data-sheet="configurator"] .mat-thumbs { padding-bottom: 2px; }
  .sheet[data-sheet="configurator"] .mat-rail > * { flex: none; }
  .sheet[data-sheet="configurator"] .mat-rail > .mat-thumbs { flex: 1 1 auto; min-height: 0; max-height: none; align-content: start; }
  .sheet[data-sheet="configurator"] .config-ctas { grid-template-columns: 1fr; gap: 8px; }
  .sheet[data-sheet="configurator"] .config-ctas .btn-gold,
  .sheet[data-sheet="configurator"] .config-ctas .btn-ghost {
    height: 44px; padding: 0 18px; white-space: nowrap;
    font-size: 11px; letter-spacing: .14em;
  }
  .sheet[data-sheet="configurator"] .config-ctas .btn-gold { box-shadow: 0 8px 22px -12px rgba(224, 188, 120, .55); }
}

/* ---------- 27 Sep 2026: dock mood board + rating, AI disclaimer, one-line footnote ---------- */
.dock button.dock-extra { position: relative; }
.dock .dock-badge {
  position: absolute; top: 3px; right: 50%; transform: translateX(22px);
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 99px;
  background: var(--gold); color: #1c1608; font: 700 10.5px/18px Karla, system-ui, sans-serif;
  text-align: center; box-shadow: 0 0 0 2px rgba(20, 24, 18, .9);
}
.dock .dock-badge.is-zero { display: none; }   /* reviewer S12: no grey "0" pill */
.hud-mood { display: none; position: relative; }
.hud-mood .dock-badge { position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 99px; background: #f7c948; color: #1c1608; font: 800 10px/17px Karla, system-ui, sans-serif; text-align: center; }
.hud-mood .dock-badge.is-zero { display: none; }
@media (max-width: 699px) { .hud-mood { display: inline-flex; align-items: center; justify-content: center; } }
.dock .dock-badge.bump { animation: badgeBump .5s ease; }
@keyframes badgeBump { 30% { transform: translateX(22px) scale(1.35); } }
.dock button.dock-rate svg .star-fill,
.dock button.dock-rate svg path { fill: #f7c948; stroke: #f7c948; }
.dock button.dock-rate span { color: #f7c948; }
.hud-rate { display: none !important; }            /* the rating lives in the menu and under Jess now */
.as-rate {
  display: flex; align-items: center; gap: 8px; width: calc(100% - 24px); margin: 2px 12px 8px;
  padding: 8px 12px; border-radius: 12px; border: 1px solid rgba(247, 201, 72, .45);
  background: rgba(247, 201, 72, .08); color: var(--paper); font: 500 12.5px/1.3 Karla, system-ui, sans-serif; cursor: pointer;
}
.as-rate b { color: #f7c948; }
.as-rate svg { width: 18px; height: 18px; flex: none; fill: #f7c948; }
.as-rate:hover { background: rgba(247, 201, 72, .16); }
/* Reviewer B1: the line sat UNDER the dock (and under the intro), so nobody
   could read it. It now owns the bottom band — the dock, panels and sheets
   stand on top of it via --disc-h — shows over the intro too, and pads for
   the iPhone home indicator. Long wording from 700px up, short on phones. */
.ai-disclaimer {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 101; margin: 0;
  padding: 4px 14px calc(4px + var(--sa-b));
  font: 400 11px/1.35 Karla, system-ui, sans-serif; color: rgba(245, 240, 228, .8);
  text-align: center; background: rgba(12, 16, 12, .86);
  pointer-events: none;
}
body.sheet-open .ai-disclaimer { z-index: 55; }
.ai-disclaimer .disc-short { display: none; }
@media (max-width: 699px) {
  .ai-disclaimer { font-size: 10.5px; padding: 3px 10px calc(3px + var(--sa-b)); }
  .ai-disclaimer .disc-long { display: none; }
  .ai-disclaimer .disc-short { display: inline; }
}
/* CEO review r2, should-fix 4: sideways (under 500px tall) the long wording ran to
   two lines and, with the dock, took 112 of 390px — the short, one-line wording */
/* 360px phones: the schedule's 25% share column (CEO review r2) left the table
   a few px wider than the panel — slimmer side padding keeps it inside */
@media (max-width: 380px) {
  .pp-table td, .pp-table th { padding-left: 4px; padding-right: 4px; }
}
@media (max-height: 500px) {
  .ai-disclaimer { font-size: 10.5px; padding: 3px 10px calc(3px + var(--sa-b)); }
  .ai-disclaimer .disc-long { display: none; }
  .ai-disclaimer .disc-short { display: inline; }
}
.ai-disclaimer a { color: inherit; text-decoration: underline; pointer-events: auto; }
/* Reviewer B2: never truncate a compliance line. One line where it fits,
   wraps on phones, never under 11px. */
.af-note { white-space: normal; font-size: 12px; line-height: 1.4; }
@media (min-width: 700px) { .af-note { white-space: nowrap; } }
@media (max-width: 699px) { .af-note { font-size: 11px; } }
.intro-consent-txt a { color: var(--gold-hi, #e8c98a); text-decoration: underline; }

/* mood board pack + email (27 Sep 2026) */
.mood-pack { margin: 4px 0 12px; display: grid; gap: 8px; }
.mood-pack-title { margin: 0; font: 700 11px/1 Karla, system-ui, sans-serif; letter-spacing: .14em; text-transform: uppercase; color: var(--gold, #d9b475); }
.mood-pack-item { display: grid; gap: 2px; padding: 10px 12px; border-radius: 12px; border: 1px solid rgba(217, 180, 117, .4); background: rgba(217, 180, 117, .08); color: var(--paper, #f5f0e4); text-decoration: none; }
.mood-pack-item b { font-size: 13.5px; }
.mood-pack-item span { font-size: 12px; opacity: .78; }
.mood-pack-item:hover { background: rgba(217, 180, 117, .16); }
.mood-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0 4px; }
.mood-actions button { flex: 1 1 140px; }
.mood-actions button[aria-busy="true"] { opacity: .7; cursor: progress; }
/* 1 Oct 2026: the note under the email buttons (reference, the email again as a link, copy) */
.mood-mailbox { margin: 2px 18px 10px; font-size: 12.5px; line-height: 1.45; }
.mood-mailbox .af-mailbtns .btn-ghost { flex: 1 1 auto; min-height: 40px; justify-content: center; }
/* a short screen (a phone on its side): the drawer scrolls as one, so the note never pushes the buttons off it */
@media (max-height: 560px) {
  .mood-drawer.open { overflow-y: auto; }
  .mood-drawer.open .mood-list { flex: none; overflow: visible; }
}

/* Row 119: the brochure plan pages lead the 2D plans but are not storeys —
   the lifted 3D stack shows the architect's floors only. */
.plan-stage.mode-3d .floor-plate.is-brochure { display: none; }

/* Booking header split (reviewer S5): short title, Rob's line beneath it */
.bk-sub { margin: 6px 0 0; font-size: 14px; color: var(--paper-dim); }
/* Privacy link outside the consent label (reviewer S6) */
.intro-consent-link { margin: 8px 0 16px; font-size: 12.5px; text-align: center; }
.intro-consent-link a { color: var(--gold-hi, #e8c98a); text-decoration: underline; }

.as-rate[hidden] { display: none !important; }

/* On the intro the disclaimer is part of the page (under Step inside), so it
   never covers the button; the fixed band appears once the visitor is in. */
body:not(.in-suite) .ai-disclaimer { display: none; }
body:not(.in-suite) { --disc-h: 0px; }
.intro-disc { opacity: .92; color: var(--paper-dim, #d9d2c2); }
.intro-disc a { color: inherit; text-decoration: underline; }

/* ============================================================
   28 Sep 2026 — speed + interface pass (report:
   deliverables/qa/suite-speed-ui-2026-09-28.md). Every rule below hangs off a
   body class that index.html ships and main.js removes when assets/config.js
   turns the matching ui.* switch off:
     .ui-sticky-enter  intro fits short screens, "Step inside" stays on screen
     .ui-compact-host  phones: Jess's panel starts as a one-line bar
     .ui-polish        the visible bug fixes + readability pack
   ============================================================ */

/* ---- intro: fit 1280x720 laptops and small phones (live QA m3) ---- */
body.ui-sticky-enter #enterBtn { position: sticky; bottom: 10px; z-index: 3; }
body.ui-sticky-enter #enterBtn:not(.is-locked):not([disabled]) {
  box-shadow: 0 12px 30px rgba(0, 0, 0, .5), 0 14px 36px -12px rgba(224, 188, 120, .7);
}
@media (min-width: 561px) and (max-height: 820px) {
  body.ui-sticky-enter .intro-card { padding: 14px 28px 12px; }
  body.ui-sticky-enter .intro-mark { width: 136px; margin-bottom: 8px; }
  body.ui-sticky-enter .intro-kicker { margin-bottom: 8px; }
  body.ui-sticky-enter #intro h1 { font-size: clamp(30px, 5.6vh, 44px); margin-bottom: 2px; }
  body.ui-sticky-enter .intro-sub { margin: 6px auto 12px; font-size: 14px; line-height: 1.45; }
  body.ui-sticky-enter .intro-picks { margin-bottom: 10px; gap: 10px; }
  body.ui-sticky-enter .host-pick { padding: 6px 10px 8px; gap: 2px; }
  body.ui-sticky-enter .host-opt { padding: 4px 8px; }
  body.ui-sticky-enter .intro-consent { margin-bottom: 4px; padding: 8px 12px; }
  body.ui-sticky-enter .intro-consent-link { margin: 4px 0 10px; }
  body.ui-sticky-enter .intro-tiny { margin-top: 10px; }
}
@media (min-width: 561px) and (max-height: 680px) {
  body.ui-sticky-enter .intro-mark { width: 112px; margin-bottom: 4px; }
  body.ui-sticky-enter #intro h1 { font-size: 30px; line-height: 1.02; }
  body.ui-sticky-enter .intro-sub { font-size: 13px; margin: 4px auto 8px; }
  body.ui-sticky-enter .host-txt small { font-size: 10.5px; }
}
@media (max-width: 560px) {
  body.ui-sticky-enter .intro-card { padding: 12px 14px 8px; }
  body.ui-sticky-enter .intro-mark { width: 124px; margin-bottom: 6px; }
  body.ui-sticky-enter .intro-kicker { margin-bottom: 6px; font-size: 10px; letter-spacing: .28em; }
  body.ui-sticky-enter #intro h1 { font-size: 32px; }
  body.ui-sticky-enter .intro-sub { font-size: 13.5px; line-height: 1.4; margin: 6px auto 12px; }
  body.ui-sticky-enter .intro-picks { gap: 8px; margin-bottom: 10px; }
  body.ui-sticky-enter .host-pick { padding: 6px 8px 8px; gap: 0; }
  body.ui-sticky-enter .host-opt { padding: 5px 6px; }
  body.ui-sticky-enter .intro-consent { margin-bottom: 4px; padding: 8px 10px; }
  body.ui-sticky-enter .intro-consent-link { margin: 4px 0 8px; }
  body.ui-sticky-enter #enterBtn { width: 100%; }
  body.ui-sticky-enter .intro-tiny { margin-top: 10px; }
}
/* small phones: the one-line option descriptions go before the button does */
@media (max-width: 560px) and (max-height: 760px) {
  body.ui-sticky-enter .host-txt small { display: none; }
}
/* Landscape phones (live QA round 2, 28 Sep 2026): at 390 px tall the options, the
   consent tick and the button cannot all fit, and the sticky (still disabled) button
   floated over 4-6 of the host / quality options while the consent row sat below the
   fold. Here it stays in the flow, straight under the consent tick it depends on. */
@media (max-height: 500px) {
  body.ui-sticky-enter #enterBtn { position: static; }
}

/* ---- phones: Jess's panel starts as a one-line bar (ui.phoneCompactHost) ----
   While the panel is closed the "● Jess" chip carries her latest words (main.js
   fills .hud-chat-cap); tapping the bar opens the panel. */
.hud-chat-cap { display: none; }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  body.ui-compact-host.chat-closed .hud-chat-toggle.has-cap {
    max-width: calc(100vw - 28px - var(--sa-l) - var(--sa-r));
    padding: 8px 14px; gap: 10px; border-radius: 16px; text-align: left;
    box-shadow: 0 14px 34px -16px rgba(0, 0, 0, .85);
  }
  body.ui-compact-host .hud-chat-toggle.has-cap .hud-chat-cap {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
    min-width: 0; padding-left: 10px; border-left: 1px solid var(--line-strong);
    font: 500 12.5px/1.32 var(--ui); letter-spacing: 0; text-transform: none; color: var(--paper);
  }
}
/* Polish r3 (live QA R2-m6): held sideways the bar ran the full width (816 px)
   across the front of the masterplan, with only ~190 px of room left between
   the dock and the top. On landscape phones it keeps to the bottom-left corner
   at a readable width — two lines of her words; a tap opens the rest. */
@media ((hover: none) and (pointer: coarse)) and (orientation: landscape),
       (max-height: 520px) and (pointer: coarse) {
  body.ui-compact-host.chat-closed .hud-chat-toggle.has-cap {
    max-width: min(340px, calc(42vw - var(--sa-l)));
  }
}

/* ---- visible bug fixes + readability (ui.polish) ---- */
/* Affordability form: the hints used var(--sans), which is not defined, so
   they inherited the label's Space Mono capitals. Labels in Karla too. */
body.ui-polish .af-hint { font: 400 12px/1.4 var(--ui); color: var(--paper-faint); }
body.ui-polish .af-fields label { font: 700 12.5px/1.3 var(--ui); letter-spacing: .02em; text-transform: none; color: var(--paper); }
/* Mood-board drawer: the pack and the two buttons touched the drawer's left
   edge and were clipped on the right; the buttons wrapped their labels. */
body.ui-polish .mood-pack { margin: 14px 18px 6px; }
body.ui-polish .mood-actions { margin: 12px 18px 10px; flex-direction: column; }
body.ui-polish .mood-actions button { flex: none; width: 100%; min-height: 46px; letter-spacing: .14em; }
/* Information in the readable UI face, sentence case, tabular figures; the
   mono capitals stay for the short kickers. */
body.ui-polish :is(.tc-meta, .pc-meta, .tm-specs li, .tm-guide, .tc-price small, .tm-book-note, .mood-item span) {
  font: 500 12.5px/1.45 var(--ui); letter-spacing: .01em; text-transform: none; font-variant-numeric: tabular-nums;
}
/* Dock: one call to action. "Book a viewing" is outlined in gold (the station
   you are at keeps the solid gold); "Rate us" is calm, not a second gold.
   Polish r3: the label is the client's wording, "Book a viewing", on every
   screen, in the markup (index.html) — it was "Book" plus a desktop-only
   ' a visit' added here. */
@media (min-width: 901px) { body.ui-polish .dock button { font: 700 10.5px var(--ui); letter-spacing: .06em; } }
body.ui-polish .dock button[data-go="desks"]:not(.is-on) { color: var(--gold-hi, #e8c98a); box-shadow: inset 0 0 0 1.4px rgba(224, 188, 120, .75); }
/* the longest label: in the phones' scrolling dock the button would shrink to its
   64 px minimum and the words spill over its neighbours — it keeps its width */
.dock button[data-go="desks"] { flex-shrink: 0; }
body.ui-polish .dock button.dock-rate span { color: var(--paper-dim); }
body.ui-polish .dock button.dock-rate svg path { fill: none; stroke: #f7c948; }
/* the chip's pulse draws the eye a few times, then rests (an endless CSS pulse
   repaints the page every frame, WebGL room or not) */
body.ui-polish .hud-chat-toggle .dot { animation-iteration-count: 4; }
/* 44 px touch targets on phones */
@media (hover: none) and (pointer: coarse) {
  body.ui-polish .as-voice, body.ui-polish .as-min { width: 44px; height: 44px; }
  body.ui-polish .as-chips .chip { min-height: 40px; }
  body.ui-polish .dock-scroll-hint { width: 40px; height: 40px; min-width: 40px; min-height: 40px; }
}

/* ---- 29 Sep 2026: phones — what Jess (or a tap) opens is on screen and scrolls ----
   Client: "on iOS in portrait Jess opens the plots but you can't see them".
   The cause was the chat lifted over the keyboard (.assistant.kb-open, z 85)
   staying up over the sheet (z 60) and the schedule (z 52) after typing to
   her; main.js (settlePhoneChrome) now puts the keyboard and the chat away
   whenever a sheet, the schedule or a plot card opens. Here: each of those
   scrolls inside itself, with iOS momentum, and never drags the page (the
   fixed layers) with it; and the dimmed backdrop blurs on Safari before 18. */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  .overlay .sheet,
  .plots-panel .pp-table-wrap,
  .plot-card { overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
  .ov-scrim { -webkit-backdrop-filter: blur(7px); }
}
/* Portrait: the dock stands 8px above the disclaimer band and the sheets
   stopped 6px short of --chrome-bottom, so a sheet's foot ran 2px under the
   dock's edge. 10px leaves a hairline gap. (Landscape keeps its own rules.) */
@media (max-width: 900px) and (orientation: portrait) and (min-height: 521px),
       (hover: none) and (pointer: coarse) and (orientation: portrait) and (min-height: 521px) {
  .overlay .sheet, .overlay .sheet-narrow { bottom: 10px; }
}

/* =====================================================================
   UI round, 29 Sep 2026 — dock fit, Settings, moving around, feedback
   pop-up, home types, CEO polish. Report: deliverables/qa/ui-round-2026-09-29.md
   ===================================================================== */

/* ---- 1 · The dock fits without scrolling on tablets and phones ----
   Labels shortened in index.html ("Model", "Homes"). On touch / narrow screens
   the dock spans the width and every button takes an equal share; a label
   that does not fit goes onto a second line instead of spilling over its
   neighbours (it used to scroll ~70 px on a 768 px iPad and ~450 px on a
   phone). Narrow portrait phones get two rows of six (the Jess bar and every
   sheet stand on --dock-h, so they move up with it). */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  .dock-wrap { width: min(calc(100vw - 16px - var(--sa-l) - var(--sa-r)), 860px); }
  .dock button {
    min-width: 0; flex: 1 1 0;
    padding: 7px 2px 6px; justify-content: flex-start;
    white-space: normal; text-align: center; line-height: 1.12;
    font-size: 9.5px; letter-spacing: .03em;
  }
  .dock button span { display: block; max-width: 100%; }
  .dock button[data-go="desks"] { flex-shrink: 1; }
}
@media (max-width: 599px) {
  :root { --dock-h: 112px; }
  .dock {
    flex-wrap: wrap; justify-content: center; overflow: visible;
    padding: 4px; gap: 2px;
    -webkit-mask-image: none; mask-image: none;
  }
  .dock button {
    flex: 0 0 calc((100% - 10px) / 6);
    min-height: 48px; padding: 5px 1px 4px; gap: 3px;
    font-size: 9px; letter-spacing: .02em;
  }
  .dock button svg { width: 18px; height: 18px; }
}

/* ---- 2 · "Settings" (gear) and its pop-up ---- */
.hud-quality.hud-settings { letter-spacing: .14em; }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  /* the word shows on touch screens too (it was icon-only) */
  .hud-quality.hud-settings { padding: 8px 13px 8px 11px; }
  .hud-quality.hud-settings span { display: inline; }
}
@media (max-width: 340px) {
  .hud-quality.hud-settings { padding: 8px; }
  .hud-quality.hud-settings span { display: none; }
}
.quality-pop {
  width: min(500px, calc(100vw - 32px));
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; align-items: start;
  max-height: calc(100dvh - 64px - var(--chrome-bottom));
  overflow-y: auto; overscroll-behavior: contain;
}
.quality-pop[hidden] { display: none; }
.qp-col { display: grid; gap: 0; align-content: start; min-width: 0; }
.quality-pop .qp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.quality-pop .qp-now {
  font: 600 10.5px var(--ui); letter-spacing: .02em; text-transform: none;
  color: var(--paper-dim); white-space: nowrap;
}
.qp-move { display: grid; gap: 0; }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  /* over the dock while it is open (it is a short-lived menu), never under it */
  .quality-pop {
    z-index: 80; max-height: calc(100dvh - max(52px, calc(var(--sa-t) + 44px)) - var(--disc-h) - 10px);
    left: auto; right: max(10px, var(--sa-r)); width: min(560px, calc(100vw - 20px - var(--sa-l) - var(--sa-r)));
  }
}
@media (max-width: 599px) {
  .quality-pop { grid-template-columns: 1fr; }
  .qp-col + .qp-col > p:first-child { margin-top: 12px; }
}

/* ---- 3 · Point to move: the floor target is drawn in 3D (navigate.js) ---- */
body.move-point #scene { cursor: crosshair; }

/* ---- 4 · Feedback pop-up (client's mock-up: four questions, prize draw) ---- */
.rate-pop { padding: 16px calc(12px + var(--sa-x)); }
.rate-card {
  width: min(640px, 100%); text-align: left;
  max-height: calc(100dvh - 32px); overflow-y: auto; overscroll-behavior: contain;
  padding: 22px 26px 20px;
}
.rate-card .rate-kicker, .rate-card .rate-title, .rate-done { text-align: center; }
.rate-card .rate-title { font-size: 25px; margin: 0 0 12px; }
.rate-title-sub { white-space: nowrap; }
.rate-qs { display: grid; margin: 0 0 14px; }
.rate-q {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4px 14px;
  padding: 7px 0; border-bottom: 1px solid rgba(243, 221, 171, .1);
}
.rate-q:last-child { border-bottom: 0; }
.rate-q-text { margin: 0; font: italic 500 16px/1.3 var(--disp); color: #fff; }
.rate-q .rate-stars { gap: 4px; }
.rate-q .rate-star { width: 40px; height: 40px; padding: 3px; }
.rate-draw { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: start; margin: 0 0 12px; }
.rate-lab { display: block; margin: 0 0 7px; font: 500 13.5px/1.4 var(--ui); color: #f1e6cc; }
.rate-email {
  display: block; width: 100%; box-sizing: border-box;
  background: #fff; color: #111; border: 1px solid var(--line-strong); border-radius: 10px;
  padding: 10px 12px; font: 400 16px/1.3 var(--ui);           /* 16 px: no zoom on iOS */
}
.rate-email:focus, .rate-card .rate-note:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.rate-email[aria-invalid="true"] { border-color: #e0876a; box-shadow: 0 0 0 2px rgba(224, 135, 106, .35); }
.rate-err { display: block; margin: 6px 0 0; font: 600 12.5px/1.35 var(--ui); color: #f3a68a; }
.rate-err[hidden] { display: none; }
.rate-consent { margin: 7px 0 0; font: 400 11.5px/1.45 var(--ui); color: var(--paper-faint); }
.rate-consent a { color: var(--gold-hi); text-underline-offset: 2px; }
.rate-tin { width: 64px; height: auto; margin-top: 16px; filter: drop-shadow(0 8px 10px rgba(0, 0, 0, .5)); }
.rate-card .rate-note { font-size: 16px; min-height: 76px; resize: vertical; margin-bottom: 10px; }
.rate-card .rate-hint { text-align: center; margin: 0 0 8px; min-height: 0; }
.rate-card .rate-hint:empty { display: none; }
.rate-actions .btn-gold.is-sending { cursor: progress; opacity: .75; }
/* stand clear of the disclaimer band at the foot of the screen */
.rate-pop { padding-bottom: calc(12px + var(--disc-h)); }
.rate-card { max-height: calc(100dvh - 28px - var(--disc-h)); }
/* laptop-height screens (720-820 px): the same form, tighter, so it needs no scrolling */
@media (min-width: 561px) and (max-height: 820px) and (min-height: 521px) {
  .rate-card { padding: 16px 24px 16px; }
  .rate-card .rate-title { font-size: 23px; margin-bottom: 6px; }
  .rate-q { padding: 4px 0; }
  .rate-q .rate-star { width: 36px; height: 36px; }
  .rate-qs { margin-bottom: 10px; }
  .rate-draw { margin-bottom: 8px; }
  .rate-card .rate-note { min-height: 58px; }
  .rate-actions .btn-gold, .rate-actions .btn-ghost { min-height: 40px; padding-top: 9px; padding-bottom: 9px; }
}
@media (max-width: 560px) {
  .rate-card { padding: 18px 16px 16px; border-radius: 16px; }
  .rate-card .rate-title { font-size: 21px; }
  .rate-q { grid-template-columns: 1fr; padding: 6px 0 8px; }
  .rate-q-text { font-size: 15px; }
  .rate-q .rate-stars { justify-content: flex-start; margin-left: -3px; }
  .rate-q .rate-star { width: 38px; height: 38px; }
  .rate-tin { width: 46px; margin-top: 26px; }
}
/* phones held sideways: questions on the left, the draw and comments on the right */
@media (max-height: 520px) and (min-width: 640px) {
  .rate-pop { padding: 6px calc(10px + var(--sa-x)) calc(6px + var(--disc-h)); }
  .rate-card { width: min(860px, 100%); max-height: calc(100dvh - 12px - var(--disc-h)); padding: 14px 18px 14px; }
  .rate-ask { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); column-gap: 22px; align-items: start; }
  .rate-ask > .rate-kicker, .rate-ask > .rate-title { grid-column: 1 / -1; }
  .rate-card .rate-title { font-size: 20px; margin-bottom: 6px; }
  .rate-ask > .rate-qs { grid-column: 1; grid-row: 3 / span 6; margin: 0; }
  .rate-ask > :not(.rate-kicker):not(.rate-title):not(.rate-qs) { grid-column: 2; }
  .rate-q { padding: 4px 0; }
  .rate-q-text { font-size: 14px; }
  .rate-q .rate-star { width: 32px; height: 32px; padding: 2px; }
  .rate-tin { width: 40px; margin-top: 22px; }
  .rate-card .rate-note { min-height: 48px; }
}

/* ---- 5 · Home types: the collection filter, the card line, the schedule ---- */
.type-filter { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin: -6px 0 14px; }
.type-filter:empty { display: none; }
.tf-chip {
  min-height: 34px; padding: 6px 12px; border-radius: 99px;
  border: 1px solid var(--line-strong); background: rgba(15, 15, 12, .55);
  font: 600 12px var(--ui); letter-spacing: .02em; color: var(--paper-dim);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.tf-chip small { margin-left: 3px; font: 700 10.5px var(--ui); color: var(--paper-faint); }
.tf-chip:hover { border-color: rgba(243, 221, 171, .5); color: var(--paper); }
.tf-chip.is-on { background: var(--gold); border-color: var(--gold); color: #1c1608; }
.tf-chip.is-on small { color: rgba(28, 22, 8, .65); }
.tf-chip:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  .type-filter {
    flex-wrap: nowrap; justify-content: flex-start; overflow-x: auto;
    scrollbar-width: none; -webkit-overflow-scrolling: touch; padding-bottom: 2px;
  }
  .type-filter::-webkit-scrollbar { display: none; }
  .tf-chip { flex: none; min-height: 40px; }
}
.type-card[hidden] { display: none !important; }
.type-card h3 .tc-type {
  display: block; margin-top: 3px;
  font: 700 12.5px/1.35 var(--ui); letter-spacing: .01em; color: var(--gold-hi, #f6e2b0);
}
/* the schedule: type filter under the Houses / Apartments ticks, type in two lines */
.pp-kinds { flex-wrap: wrap; row-gap: 8px; }
.pp-type { flex: 1 1 100%; display: block; }
.pp-type select {
  width: 100%; min-height: 36px; padding: 7px 34px 7px 13px; border-radius: 99px;
  border: 1px solid var(--line-strong); color: var(--paper);
  background: rgba(15, 15, 12, .55) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23e0bc78' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 14px center;
  font: 600 12.5px var(--ui); -webkit-appearance: none; appearance: none; cursor: pointer;
}
.pp-type select:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.pp-type select option { background: #1c1c16; color: var(--paper); }
@media (hover: none) and (pointer: coarse) { .pp-type select { font-size: 16px; min-height: 40px; } }
.pp-type-cell b { display: block; font-weight: 600; color: var(--paper); }
.pp-type-cell small { display: block; margin-top: 1px; font-size: 11px; color: var(--paper-faint); }
/* narrow phones: the two-row dock steps aside while the schedule is open (it has its
   own close, and Back is on the bar above), and the Beds column goes — the type
   already says it ("2 Bedroom Apartment", "Pippin · 3 Bedroom House") */
@media (max-width: 599px) {
  body.plots-open .dock-wrap { visibility: hidden; pointer-events: none; }
  .pp-table th:nth-child(4), .pp-table td:nth-child(4) { display: none; }
  .pp-type-cell b { font-size: 13px; }
}

/* ---- 6 · CEO review polish ---- */
/* the quality picker left the first screen: Jess's three options sit side by side */
#qualityPick[hidden] { display: none !important; }
.intro-picks.is-single { grid-template-columns: 1fr; max-width: 600px; margin-left: auto; margin-right: auto; }
@media (min-width: 561px) {
  .intro-picks.is-single #hostPick { grid-template-columns: repeat(auto-fit, minmax(136px, 1fr)); column-gap: 6px; align-items: start; }
}
/* brochure plans on their own cream, not in white pillarbox bars (tablet plot sheet) */
.fp-photo:has(img[src*="/plans/brochure/"]),
.fp-photo img[src*="/plans/brochure/"],
.plan-stage.has-photos:has(img[src*="/plans/brochure/"]),
.type-card .tc-elev.is-plan[src*="/plans/brochure/"] { background: #f3edda; }

/* The Homes cards lead with the home (29 Sep 2026, CEO-review idea): the house
   type's / block's hero CGI fills the tile at the plan's old height, the plan
   stays as an inset in the corner, and the client's CGI line sits on the photo. */
.type-card .tc-hero {
  position: relative; display: block; height: 176px;
  border-radius: 8px; overflow: hidden; background: #14140f; pointer-events: none;
}
.type-card .tc-photo { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; transition: transform .6s var(--ease); }
.type-card:hover .tc-photo { transform: scale(1.04); }
.type-card .tc-plan-inset {
  position: absolute; top: 7px; right: 7px; width: 40%; aspect-ratio: 720 / 509; height: auto;
  object-fit: contain; background: #f3edda; border-radius: 5px; padding: 2px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .45);
}
.type-card .tc-disc {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 8px 5px;
  font: italic 400 9px/1.25 var(--ui); color: rgba(255, 255, 255, .88);
  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .6));
}
.type-card.is-soon .tc-photo { opacity: .7; filter: saturate(.5); }
.type-card.is-soon:hover .tc-photo { opacity: 1; filter: none; }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  .type-card .tc-hero { height: 168px; }
  .type-card:hover .tc-photo { transform: none; }
}

/* =====================================================================
   Live QA round 3 + CEO review r3, 29 Sep 2026 (after release6).
   Report: deliverables/qa/live-qa-r3-2026-09-29.md
   ===================================================================== */

/* ---- M1 · Jess's chat stays usable while a sheet is open ----
   Desktop (a mouse, wider than 900 px): her panel (z 45) sat under the sheet's
   blurred backdrop (z 60), so a click on her text box landed on the backdrop and
   closed the sheet, and her replies were a blur. It now stands above the
   backdrop, readable, and the sheet steps right of it where the two would
   overlap (about 1024-1600 px wide); at 1920 the centred sheet already clears
   it. Her "● Jess" chip (the panel minimised) is above the backdrop too. */
@media (min-width: 901px) and (hover: hover), (min-width: 901px) and (pointer: fine) {
  body.sheet-open .assistant,
  body.sheet-open .hud-chat-toggle { z-index: 61; }
  body.sheet-open:not(.chat-closed) { --chat-r: calc(18px + min(340px, 100vw - 28px) + 14px); }
  body.sheet-open:not(.chat-closed) .overlay .sheet {
    --sheet-w: min(1020px, calc(100vw - var(--chat-r) - 12px - var(--sa-r)));
    width: var(--sheet-w);
    left: max(var(--sa-mid), calc(var(--chat-r) + var(--sheet-w) / 2));
  }
  body.sheet-open:not(.chat-closed) .overlay .sheet-narrow {
    --sheet-w: min(560px, calc(100vw - var(--chat-r) - 12px - var(--sa-r)));
  }
}
/* Phones (the compact host): the Jess bar stays over an open sheet, as a small
   "● Jess" pill at the sheet's foot (her words are in the chat). A tap opens her
   chat as a drawer over the sheet without closing it; a tap back on the sheet
   folds it again (main.js). The sheet keeps room to scroll its last control
   clear of the pill. */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  body.ui-compact-host.sheet-open.chat-closed .hud-chat-toggle {
    display: inline-flex !important;
    z-index: 61;
    bottom: calc(var(--chrome-bottom) + 20px);
    left: max(18px, calc(var(--sa-l) + 12px));
    max-width: none; min-height: 38px; padding: 8px 14px; gap: 8px; border-radius: 99px;
    box-shadow: 0 10px 26px -10px rgba(0, 0, 0, .9);
  }
  body.ui-compact-host.sheet-open .hud-chat-toggle .hud-chat-cap { display: none !important; }
  body.ui-compact-host.sheet-open.chat-closed .overlay .sheet { padding-bottom: 64px; }
  body.ui-compact-host.sheet-open:not(.chat-closed) .assistant { z-index: 62; visibility: visible; pointer-events: auto; }
}
@media ((hover: none) and (pointer: coarse)) and (orientation: landscape),
       (max-height: 520px) and (pointer: coarse) {
  /* sideways the sheet takes the full height and the dock steps aside */
  body.ui-compact-host.sheet-open.chat-closed .hud-chat-toggle { bottom: calc(max(8px, var(--sa-b)) + 12px); }
}

/* ---- M2 · Phones: a plot opened from the Plots list ----
   The sheet stops at the dock's line, but the dock had stepped aside for the list,
   so the band under the sheet showed the list's live footer (a second Open
   floorplan / Open builder). While a sheet is open the list (and a plot card)
   step aside instead and the dock comes back; closing the sheet returns to them. */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  body.sheet-open .plots-panel,
  body.sheet-open .plot-card { visibility: hidden; pointer-events: none; }
}
@media (max-width: 599px) and (orientation: portrait) {
  body.sheet-open.plots-open .dock-wrap { visibility: visible; pointer-events: auto; }
}

/* ---- S3 · Phones: the feedback card's "Not now" was below the fold ----
   Send / Not now stay pinned to the card's foot while the questions scroll, and a
   × in the corner closes it as "Not now" does (rating.js). */
.rate-x { display: none; }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  .rate-card { position: relative; }
  .rate-x {
    display: grid; place-items: center; position: absolute; top: 8px; right: 8px; z-index: 2;
    width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line-strong);
    background: rgba(22, 22, 15, .92); color: var(--paper-dim); font: 400 22px/1 var(--ui);
  }
  .rate-card .rate-title { padding: 0 30px; }
}
@media (max-width: 560px) {
  .rate-card .rate-actions {
    position: sticky; bottom: -16px; z-index: 1;
    margin: 0 -16px -16px; padding: 12px 16px 16px;
    background: linear-gradient(rgba(22, 22, 15, 0), #16160f 14px);
  }
  /* a field scrolled into view (a tap, the keyboard, Tab) stops above the pinned buttons */
  .rate-card { scroll-padding-bottom: 140px; }
}
@media (max-height: 520px) and (min-width: 640px) {
  .rate-card .rate-actions { position: sticky; bottom: -14px; z-index: 1; padding: 8px 0 14px; background: #16160f; }
  .rate-card { scroll-padding-bottom: 120px; }
}

/* ---- S4 · Tablets: the Homes cards ----
   At 768 px one 726 px card per row cut its photo to a 3.3:1 strip under the plan
   inset. Two per row from 600 px on upright screens (three from 1000 px on touch
   screens), the photo at 16:10 with a smaller plan inset. Sideways phones keep
   their three-across rule. */
@media (min-width: 600px) and (min-height: 521px) and (max-width: 900px), (min-width: 600px) and (min-height: 521px) and (hover: none) and (pointer: coarse) {
  .type-grid { grid-template-columns: repeat(2, 1fr); }
  .type-card .tc-plan-inset { width: 32%; }
  .type-card .tc-hero { height: auto; aspect-ratio: 16 / 10; }
}
@media (min-width: 1000px) and (min-height: 521px) and (hover: none) and (pointer: coarse) {
  .type-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---- Desktop 1280x720: Jess's panel (centred, 560 px) ran 22 px over the top of the
   dock's Model / Homes buttons; it now stays clear of the dock band on short screens ---- */
@media (min-width: 901px) and (hover: hover), (min-width: 901px) and (pointer: fine) {
  .assistant { height: min(560px, calc(100svh - 2 * var(--chrome-bottom) - 24px)); }
}

/* =====================================================================
   CEO review r4, 29 Sep 2026 — deliverables/qa/point-to-move-fix-2026-09-29.md
   ===================================================================== */
/* ---- should-fix 2 · she replied while her chat was folded ----
   After Jess acts on a phone the chat folds so the sheet can be seen, and over a
   sheet the pill has no caption. A small gold dot on the "● Jess" pill / bar (or
   the desktop's minimised chip) says there is a reply to read, until the chat is
   opened (main.js sets .has-unread). */
.hud-chat-toggle.has-unread::after {
  content: ''; position: absolute; top: -3px; right: -3px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--gold-hi, #f6e2b0);
  box-shadow: 0 0 0 2px rgba(22, 22, 15, .92), 0 0 10px rgba(246, 226, 176, .55);
  pointer-events: none;
}
/* …and on phones, the drawer she is opened into over a sheet gives her reply the room:
   the suggestion chips and the "Rate your visit" line left 85 px of log on an iPhone
   and 28 px on a 360 x 640 Android (reviewer, should-fix 2). Both are still there in
   the room; Rate us stays in the menu. */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  body.ui-compact-host.sheet-open:not(.chat-closed) .assistant .as-chips,
  body.ui-compact-host.sheet-open:not(.chat-closed) .assistant .as-rate { display: none !important; }
}

/* =====================================================================
   29 Sep 2026 (client) — two hosts, privacy in Settings, no-JS start page
   report: deliverables/qa/round-2026-09-29b/start-seo/report.md
   ===================================================================== */
/* A host that is not on offer (assets/config.js hosts.enabled) ships hidden.
   .host-opt and the pop-up's buttons set their own display, so [hidden] needs help. */
.host-opt[hidden], .quality-pop button[hidden] { display: none !important; }

/* The AI-disclaimer / privacy band along the bottom of the room moves into
   Settings (ui.privacyInSettings, body.ui-privacy-settings; ?privacyset=0 = the
   band). Everything that stood on the band (--disc-h) stands on the phone's
   home-indicator inset alone. The intro is untouched: the band never showed
   there and its --disc-h stays 0. */
body.ui-privacy-settings .ai-disclaimer { display: none; }
body.in-suite.ui-privacy-settings { --disc-h: var(--sa-b, 0px); }
body:not(.ui-privacy-settings) .qp-privacy { display: none; }
.qp-privacy { display: grid; gap: 0; }
.quality-pop p.qp-disc {
  margin: 0 11px 6px; font: 400 11px/1.45 var(--ui); letter-spacing: 0; text-transform: none;
  color: var(--paper-faint);
}
.quality-pop a.qp-link {
  position: relative; display: block; padding: 8px 30px 8px 11px; border-radius: 9px; margin-top: 2px;
  font: 600 12.5px var(--ui); color: var(--paper-dim); text-decoration: none; transition: all .2s;
}
.quality-pop a.qp-link small { display: block; font: 400 10.5px var(--ui); color: var(--paper-faint); margin-top: 1px; }
.quality-pop a.qp-link:hover, .quality-pop a.qp-link:focus-visible { background: rgba(255, 255, 255, .06); color: var(--paper); }
/* opens in a new tab */
.quality-pop a.qp-link::after { content: '↗'; position: absolute; right: 11px; top: 8px; font-weight: 400; color: var(--gold); }

/* The start page for readers without JavaScript (and crawlers that do not run
   it): <noscript> inside the intro card — never drawn when scripts run. */
.intro-noscript { margin: 4px 0 14px; text-align: left; color: var(--paper-dim, #d9d2c2); font-size: 13.5px; line-height: 1.5; }
.intro-noscript h2 { margin: 0 0 6px; font: 600 16px/1.3 var(--disp); color: var(--gold, #d9b475); }
.intro-noscript p { margin: 0 0 8px; }
.intro-noscript ul { margin: 0 0 8px; padding-left: 18px; }
.intro-noscript a { color: var(--gold-hi, #e8c98a); }

/* ---- Round b, 29 Sep 2026 (client RAG): plots, calculator, plot pop-up ---- */
/* The affordability checker's income-cap line, straight under "Simply fill in…"
   (calculator.js; CFG.plots.salaryCapNote, ?capnote=0). */
.af-lead:has(+ .af-cap:not([hidden])) { margin-bottom: 8px; }
.af-cap {
  margin: 0 0 16px; max-width: 62ch; padding: 2px 0 2px 11px; border-left: 2px solid var(--gold);
  font: 600 13.5px/1.45 var(--ui); color: var(--gold-hi, #f6e2b0);
}
.af-cap[hidden] { display: none; }
/* The plot pop-up's CGI · Floor plans · 3D (js/plot-views.js; ?plotviews=classic). */
.plan-floors[hidden], .pv-row[hidden], .pt-btn[hidden] { display: none !important; }
.pv-toggle .pt-btn { padding: 7px 15px; }
.plan-stage.mode-cgi, .plan-stage.mode-cgi.has-photos { background: #0e0e0c; }
.plan-stage.mode-cgi .floor-plate { opacity: 0 !important; pointer-events: none !important; }
.plan-stage:not(.mode-cgi) .cgi-view { display: none; }
.cgi-view { position: absolute; inset: 0; z-index: 2; margin: 0; display: flex; flex-direction: column; background: #0e0e0c; }
.cgi-pic {
  flex: 1 1 auto; min-height: 0; width: 100%; padding: 0; border: 0; display: block;
  background: #0e0e0c; cursor: zoom-in; touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(217, 180, 117, .25);
}
.cgi-pic img { width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none; }
.cgi-cap { flex: 0 0 auto; padding: 7px 12px 8px; background: rgba(14, 14, 12, .92); text-align: center; pointer-events: none; }
.cgi-cap b { display: block; font: 700 10px var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--paper-dim); }
.cgi-cap .cgi-disc { display: block; margin-top: 2px; font: italic 400 11px var(--ui); color: var(--paper-faint); }
.cgi-none { margin: auto; padding: 20px; text-align: center; color: var(--paper-dim); }
/* under the stage: the home's other pictures (CGI) or the builder button (3D) */
.pv-row { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-top: 10px; }
.pv-3d { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
.pv-3d .v3d-open { padding: 11px 20px; }
.v3d-note { font-size: 12px; line-height: 1.4; color: var(--paper-faint); }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  .pv-toggle .pt-btn { padding: 8px 13px; }
  .pv-row { margin-top: 8px; gap: 5px; }
  .pv-row > .chip { flex: 1 1 auto; justify-content: center; }
  .pv-3d { width: 100%; }
  .pv-3d .v3d-open { flex: 1 1 100%; min-height: 44px; }
  .cgi-cap { padding: 5px 10px 6px; }
  .cgi-cap .cgi-disc { font-size: 10px; }
}

/* =====================================================================
   CEO review r10, 29 Sep 2026 (local release10)
   ===================================================================== */
/* ---- S1 + S4 · the plots schedule fits ----
   Desktop: apartment rows wrapped to 3-4 lines ("Pitmaston / Close / 2 Bed /
   Apartment") and "73.4 m²" broke in two. The panel is wider and the home,
   its type, the status and the area each keep to one line, so more plots show.
   Sideways phones, short windows and narrow widths: the 25% share was cut off
   (the table scrolled sideways). Beds (the type already says "2 Bed Apartment")
   and GIA (on every plot sheet) step out, so Plot, Home, Status and the 25% share
   always fit. ?sched=classic (root .sched-classic, main.js) = the old table. */
:root:not(.sched-classic) .pp-table td:nth-child(6) { white-space: nowrap; }
@media (min-width: 1000px) and (min-height: 521px) {
  :root:not(.sched-classic) .plots-panel { width: min(496px, calc(100vw - 28px)); }
  :root:not(.sched-classic) .pp-type-cell b,
  :root:not(.sched-classic) .pp-type-cell small,
  :root:not(.sched-classic) .pp-table td.pp-status,
  :root:not(.sched-classic) .pp-table th { white-space: nowrap; }
}
@media (max-width: 599px), (max-height: 520px) {
  :root:not(.sched-classic) .pp-table th:nth-child(4), :root:not(.sched-classic) .pp-table td:nth-child(4),
  :root:not(.sched-classic) .pp-table th:nth-child(6), :root:not(.sched-classic) .pp-table td:nth-child(6) { display: none; }
  :root:not(.sched-classic) .pp-table { width: 100%; min-width: 0; }
  /* the home's name may wrap if it must; plot, status and price never do */
  :root:not(.sched-classic) .pp-table td.pp-type-cell,
  :root:not(.sched-classic) .pp-type-cell b,
  :root:not(.sched-classic) .pp-type-cell small { white-space: normal; }
  :root:not(.sched-classic) .pp-table td:not(.pp-type-cell) { white-space: nowrap; }
}

/* ---- S2 · phones: the "● Jess" pill no longer covers the sheet ----
   Over an open sheet the pill floated on the sheet's content, and every sheet
   scrolls under it (the price box, "Show home / Not for sale", the Kitchens tab,
   "Explore plot 26 in 3D", the calculator's service charge). The sheet now stops
   a pill's height (+ gaps) higher and the pill sits in that band, under the
   sheet's bottom-left corner — nothing of the sheet is ever behind it. When the
   chat is open (a drawer over the sheet) the sheet takes its full height again.
   ?pill=float (root .pill-float, main.js) = the old floating pill. */
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  :root:not(.pill-float) body.ui-compact-host.sheet-open.chat-closed .overlay .sheet,
  :root:not(.pill-float) body.ui-compact-host.sheet-open.chat-closed .overlay .sheet-narrow {
    bottom: 58px; padding-bottom: 14px;
  }
  :root:not(.pill-float) body.ui-compact-host.sheet-open.chat-closed .hud-chat-toggle {
    bottom: calc(var(--chrome-bottom) + 10px);
  }
}
@media ((hover: none) and (pointer: coarse)) and (orientation: landscape),
       (max-height: 520px) and (pointer: coarse) {
  :root:not(.pill-float) body.ui-compact-host.sheet-open.chat-closed .overlay .sheet,
  :root:not(.pill-float) body.ui-compact-host.sheet-open.chat-closed .overlay .sheet-narrow {
    bottom: calc(max(8px, var(--sa-b)) + 42px); padding-bottom: 12px;
  }
  :root:not(.pill-float) body.ui-compact-host.sheet-open.chat-closed .hud-chat-toggle {
    bottom: max(6px, var(--sa-b)); min-height: 34px; padding: 6px 13px;
  }
}

/* ---- nice-to-have · the chat header: "your suite host · with you" broke as
   "with / you". Her state ("with you", "live — just talk") keeps together. ---- */
.as-role #asState { white-space: nowrap; }

/* =====================================================================
   5 Oct 2026 — Jess · In Suite on phones (deliverables/qa/jess-mobile-audit-2026-10-05)
   assistant.js ui.voiceStates (?vstates=0 = none of this is used)
   ===================================================================== */
/* The mic button while she is live but the microphone is not (refused, busy, missing):
   no longer the red "listening" button — an outline with a slash; a tap asks again. */
.as-mic.live.no-mic { position: relative; background: transparent; color: var(--paper-dim); border-color: var(--line-strong); animation: none; }
.as-mic.live.no-mic::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 22px; height: 2px; border-radius: 2px;
  background: #e8735a; transform: translate(-50%, -50%) rotate(-45deg); pointer-events: none;
}
/* The folded "● Jess" bar says what she is doing: listening (coral), thinking /
   connecting (gold, breathing), speaking (gold), mic off / voice off (hollow). The dot
   only animates opacity (compositor-only — no repaint of the page). */
/* the dot keeps its size when her words fill the bar (it was squeezed to a sliver) */
.hud-chat-toggle .dot { flex: none; }
.hud-vstate { font: 700 9.5px var(--ui); letter-spacing: .12em; text-transform: uppercase; color: var(--paper-dim); white-space: nowrap; }
.hud-vstate:empty { display: none; }
body .hud-chat-toggle[data-vstate="listening"] .dot { background: #e8735a; animation: none; box-shadow: 0 0 0 3px rgba(232, 115, 90, .25); }
body .hud-chat-toggle:is([data-vstate="speaking"], [data-vstate="thinking"], [data-vstate="connecting"], [data-vstate="reconnecting"]) .dot {
  background: var(--gold); box-shadow: none; animation: vstate-breathe 1s ease-in-out infinite;
}
body .hud-chat-toggle[data-vstate="speaking"] .dot { animation-duration: .6s; }
body .hud-chat-toggle:is([data-vstate="nomic"], [data-vstate="ended"], [data-vstate="muted"]) .dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--paper-dim); animation: none; }
.hud-chat-toggle[data-vstate="listening"] .hud-vstate { color: #f0a08c; }
.hud-chat-toggle:is([data-vstate="speaking"], [data-vstate="thinking"]) .hud-vstate { color: var(--gold); }
@keyframes vstate-breathe { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (max-width: 900px), ((hover: none) and (pointer: coarse)) {
  /* the small pill (no caption yet) and the pill over a sheet keep to the dot */
  .hud-chat-toggle:not(.has-cap) .hud-vstate,
  body.ui-compact-host.sheet-open .hud-chat-toggle .hud-vstate { display: none; }
}
