/* ==========================================================================
   Interactive floor plan viewer — layout D.

   Composes tokens only. See _planning/floorplan-viewer-spec.md.

     floor pills  -> bottom centre, the control switched most often
     view tools   -> left rail, floating over the drawing
     panels       -> right rail: furniture, measure, exteriors, print
     selection    -> rotate / flip / delete travel WITH the piece, so the hand
                     stays where the eye is

   Spoken in the site's own vocabulary — pill chips, Literata, brand blue —
   rather than the borrowed icon cards of the builder viewers.
   ========================================================================== */

/* GeneratePress styles bare <button> with its own padding (theme.css documents
   this trap). Icon-only buttons that set width/height but never reset it end up
   inflated into lopsided pills with the glyph pushed off centre — which is what
   happened to the panel's close X. Reset once here; every button below opts back
   into the padding it wants.

   NOT line-height. `.fp button` is (0,1,1) and beats every `.fp__thing` restore
   at (0,1,0), so zeroing it here collapsed the label text in the furniture
   library into an overlapping heap. Icon-only buttons zero it themselves. */
.fp button {
  box-sizing: border-box;
  padding: 0;
  font-family: var(--hdc-font-ui);
}

/* Nothing in here is text to be read or copied — it is a tool. Without this,
   dragging the plan or a piece paints a selection across every room label the
   pointer crosses. */
.fp,
.fp * {
  -webkit-user-select: none;
  user-select: none;
}

.fp {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20rem;
  /* The HEIGHT lives on the row, not on the stage. With it on the stage the
     panel column was free to grow to its content — 22 furniture items — which
     stretched the whole component and meant the library never scrolled, it
     just got taller. Bounding the row bounds both columns. */
  grid-template-rows: 38rem;
  background: var(--hdc-surface-warm);
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-md);
  overflow: hidden;
}
@media (max-width: 60rem) {
  .fp { grid-template-columns: minmax(0, 1fr); }
}

/* ---- stage --------------------------------------------------------------- */

.fp__stage {
  position: relative;
  height: 100%;
  background: var(--hdc-surface);
  overflow: hidden;
  /* ⚠️ NOTHING INSIDE THE STAGE MAY EVER PAINT OUTSIDE IT.
     overflow:hidden alone does not guarantee that: a position:fixed descendant
     escapes it entirely, and a furniture piece was once seen floating over the
     whole page after the mobile viewer was dismissed — following the screen,
     which is what fixed positioning looks like. It could not be reproduced from
     the URL it happened on, so rather than guess at the cause this removes the
     possibility: `contain: paint` makes this element a containing block for
     fixed descendants AND clips to its own box, so however a child ends up
     positioned, it stays in the viewer. */
  contain: paint;
  touch-action: none;
  cursor: grab;
}
.fp__stage.is-panning { cursor: grabbing; }
.fp:fullscreen { border-radius: 0; grid-template-rows: 100vh; }

.fp__canvas { position: absolute; top: 0; left: 0; transform-origin: 0 0; }
.fp__canvas svg { display: block; width: 100%; height: auto; }

.fp-overlay { position: absolute; inset: 0; pointer-events: none; }

/* ---- title --------------------------------------------------------------- */

.fp__title {
  position: absolute;
  top: var(--hdc-space-5);
  left: var(--hdc-space-6);
  z-index: 3;
  padding: var(--hdc-space-2) var(--hdc-space-4);
  margin: calc(var(--hdc-space-2) * -1) calc(var(--hdc-space-4) * -1);
  border-radius: var(--hdc-radius-md);
  /* Not a solid card: the plate should disappear against an empty stage and
     only assert itself once drawing passes underneath. */
  background: color-mix(in srgb, var(--hdc-surface) 82%, transparent);
  backdrop-filter: blur(2px);
  pointer-events: none;
}
.fp__title span {
  display: block;
  font-size: var(--hdc-fs-200);
  font-weight: var(--hdc-fw-semi);
  letter-spacing: var(--hdc-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--hdc-color-primary);
}
.fp__title b {
  display: block;
  font-family: var(--hdc-font-display);
  font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-500);
  color: var(--hdc-color-heading);
}

/* ---- left rail ----------------------------------------------------------- */

/* Labelled, not icon-only. "Flip" and "Reset" are not guessable from a glyph,
   and an unlabelled control a buyer will not press may as well not exist. The
   plan is portrait so this margin is dead space anyway — it costs nothing to
   spend it on words. */
.fp__rail {
  position: absolute;
  left: var(--hdc-space-5);
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: var(--hdc-space-1);
  padding: var(--hdc-space-2);
  background: var(--hdc-surface-warm);
  border: 1px solid var(--hdc-line-strong);
  border-radius: var(--hdc-radius-md);
}
.fp__rail button {
  --_bg: transparent;
  --_fg: var(--hdc-color-heading);
  line-height: var(--hdc-lh-snug);
  width: 4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: var(--hdc-space-2) var(--hdc-space-1);
  border: 0;
  border-radius: var(--hdc-radius-sm);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  line-height: var(--hdc-lh-snug);
  text-align: center;
  cursor: pointer;
}
.fp__rail button:hover { --_bg: var(--hdc-surface); --_fg: var(--hdc-color-primary); }
.fp__rail button.is-on { --_bg: var(--hdc-color-primary); --_fg: var(--hdc-color-on-dark); }
.fp__rail svg { width: 1.375rem; height: 1.375rem; }  /* 22 */
.fp__rail hr { width: auto; border: 0; border-top: 1px solid var(--hdc-line);
               margin: var(--hdc-space-1) var(--hdc-space-2); }

/* ---- floor pills --------------------------------------------------------- */

.fp__floors {
  position: absolute;
  left: 50%;
  bottom: var(--hdc-space-5);
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  gap: var(--hdc-space-1);
  padding: var(--hdc-space-1);
  background: var(--hdc-surface);
  border: 1px solid var(--hdc-line-strong);
  border-radius: var(--hdc-radius-pill);
  box-shadow: var(--hdc-shadow-2);
}
.fp__floors button {
  --_bg: transparent;
  --_fg: var(--hdc-color-heading);
  font-family: var(--hdc-font-ui);
  line-height: var(--hdc-lh-snug);
  font-size: var(--hdc-fs-300);
  font-weight: var(--hdc-fw-medium);
  padding: var(--hdc-space-2) var(--hdc-space-5);
  border: 0;
  border-radius: var(--hdc-radius-pill);
  background: var(--_bg);
  color: var(--_fg);
  white-space: nowrap;
  cursor: pointer;
}
.fp__floors button:hover { --_fg: var(--hdc-color-primary); }
.fp__floors button.is-on {
  --_bg: var(--hdc-color-primary);
  --_fg: var(--hdc-color-on-dark);
  font-weight: var(--hdc-fw-semi);
}

/* ---- readout ------------------------------------------------------------- */

.fp__readout {
  position: absolute;
  left: 50%;
  top: var(--hdc-space-5);
  transform: translateX(-50%);
  z-index: 3;
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200);
  color: var(--hdc-color-on-dark);
  background: var(--hdc-surface-ink);
  border-radius: var(--hdc-radius-pill);
  padding: var(--hdc-space-2) var(--hdc-space-5);
}
.fp__readout:empty { display: none; }

/* ---- right panel --------------------------------------------------------- */

.fp__panel {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  border-left: 1px solid var(--hdc-line);
  min-height: 0;
  background: var(--hdc-surface-warm);
}
@media (max-width: 60rem) { .fp__panel { display: none; } }

.fp__panel-rail {
  border-right: 1px solid var(--hdc-line);
  padding: var(--hdc-space-3) 0;
  display: flex;
  flex-direction: column;
  gap: var(--hdc-space-1);
  background: var(--hdc-surface-warm);
}
.fp__panel-rail button {
  --_bg: transparent;
  --_fg: var(--hdc-color-muted);
  line-height: var(--hdc-lh-snug);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--hdc-space-1);
  padding: var(--hdc-space-3) 0;
  border: 0;
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  line-height: var(--hdc-lh-snug);
  cursor: pointer;
}
.fp__panel-rail svg { width: 1.625rem; height: 1.625rem; }  /* 26 */
.fp__panel-rail button:hover { --_fg: var(--hdc-color-primary); }
/* The active mark is a shape around the WHOLE control — icon and label together —
   rather than a rule down the panel edge. An edge line reads as a border of the
   panel; a shape reads as the state of the thing you pressed. */
.fp__panel-rail .fp__panel-icon { display: grid; place-items: center; }
.fp__panel-rail button {
  margin: 0 var(--hdc-space-2);
  border-radius: var(--hdc-radius-md);
}
.fp__panel-rail button:hover { --_bg: var(--hdc-surface); --_fg: var(--hdc-color-primary); }
/* Wash, not a solid fill. tokens.css records that a wash failed for CHIPS,
   where nothing but the fill says which one is on — but this tab has the open
   panel beside it saying the same thing, so the mark can be quiet without
   becoming ambiguous. The floor pills, which have no such backup, stay solid. */
.fp__panel-rail button.is-on {
  --_bg: var(--hdc-color-primary-wash);
  --_fg: var(--hdc-color-primary);
}

.fp__pane { display: flex; flex-direction: column; min-height: 0; }
.fp__pane[hidden] { display: none; }

.fp__pane-hd { padding: var(--hdc-space-4) var(--hdc-space-4) var(--hdc-space-4); }
/* Pane type is a ladder: title 17-18, instruction 15, buttons 13, footnote 12.
   It used to run 16 / 10 / 10 / 13, which put the disclaimer above the
   instruction it disclaims. --hdc-fs-050 is the micro status pill size and does
   not belong on anything anyone has to read. */
.fp__pane-hd h4 {
  font-family: var(--hdc-font-display);
  font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-500);
  color: var(--hdc-color-heading);
  margin: 0;
}
.fp__pane-hd p {
  margin: var(--hdc-space-2) 0 0;
  font-size: var(--hdc-fs-300);
  line-height: var(--hdc-lh-snug);
  color: var(--hdc-color-muted);
}

.fp__cats { display: flex; flex-wrap: wrap; gap: var(--hdc-space-2);
            padding: 0 var(--hdc-space-4) var(--hdc-space-3); }
.fp__cat {
  --_bg: var(--hdc-surface);
  line-height: var(--hdc-lh-snug);
  /* bright chip on the warm panel — the contrast is what makes it read as a control */
  --_fg: var(--hdc-color-heading);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  padding: 3px var(--hdc-space-3);
  border: 1px solid var(--hdc-line-strong);
  border-radius: var(--hdc-radius-pill);
  background: var(--_bg);
  color: var(--_fg);
  cursor: pointer;
}
.fp__cat.is-on { --_bg: var(--hdc-color-primary); --_fg: var(--hdc-color-on-dark);
                 border-color: var(--hdc-color-primary); }

/* min-height:0 is what actually lets a flex child scroll — without it the item
   refuses to shrink below its content and the overflow never engages. */
.fp__lib {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--hdc-space-3) var(--hdc-space-4);
  scrollbar-width: thin;
  scrollbar-color: var(--hdc-line-strong) transparent;
}
.fp__lib::-webkit-scrollbar { width: 10px; }
.fp__lib::-webkit-scrollbar-track { background: transparent; }
.fp__lib::-webkit-scrollbar-thumb {
  background: var(--hdc-line-strong);
  border: 3px solid var(--hdc-surface-warm);
  border-radius: var(--hdc-radius-pill);
}
.fp__lib::-webkit-scrollbar-thumb:hover { background: var(--hdc-color-muted); }
.fp__group { border-bottom: 1px solid var(--hdc-line); }
.fp__group:last-of-type { border-bottom: 0; }
.fp__group > summary {
  line-height: var(--hdc-lh-snug);
  display: flex;
  align-items: center;
  gap: var(--hdc-space-2);
  padding: var(--hdc-space-3) var(--hdc-space-3);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200);
  font-weight: var(--hdc-fw-semi);
  color: var(--hdc-color-heading);
  cursor: pointer;
  list-style: none;
}
.fp__group > summary::-webkit-details-marker { display: none; }
/* Own chevron so it can sit on the left and rotate — the native marker cannot
   be positioned or animated. */
.fp__group > summary::before {
  content: "";
  width: .4rem;
  height: .4rem;
  border-right: 1.5px solid var(--hdc-color-muted);
  border-bottom: 1.5px solid var(--hdc-color-muted);
  transform: rotate(-45deg);
  transition: transform var(--hdc-dur) var(--hdc-ease);
}
.fp__group[open] > summary::before { transform: rotate(45deg); }
.fp__group > summary:hover { color: var(--hdc-color-primary); }
.fp__group-n {
  margin-left: auto;
  font-size: var(--hdc-fs-050);
  font-weight: var(--hdc-fw-regular);
  color: var(--hdc-color-muted);
}
/* Two up: the list ran to 1,150px in one column, which is a lot of scrolling to
   find a chair. Items become tiles — art over name — because a horizontal row
   cannot survive halving its width. */
.fp__group-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--hdc-space-2);
  padding: 0 var(--hdc-space-1) var(--hdc-space-3);
}

.fp__lib-item {
  --_bg: transparent;
  --_fg: var(--hdc-color-text);
  line-height: var(--hdc-lh-snug);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--hdc-space-1);
  width: 100%;
  padding: var(--hdc-space-2);
  border: 1px solid transparent;
  border-radius: var(--hdc-radius-sm);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  line-height: var(--hdc-lh-snug);
  text-align: center;
  cursor: pointer;
}
.fp__lib-item:hover {
  --_bg: var(--hdc-surface);
  --_fg: var(--hdc-color-primary);
  border-color: var(--hdc-color-primary);
}
.fp__lib-art { width: 100%; height: 2.6rem; flex: none; }
.fp__lib-art svg { width: 100%; height: 100%; }
.fp__lib-nm { font-weight: var(--hdc-fw-medium); }
.fp__lib-sz { color: var(--hdc-color-muted); }

.fp__empty { padding: var(--hdc-space-3) var(--hdc-space-4); font-size: var(--hdc-fs-100);
             line-height: var(--hdc-lh-body); color: var(--hdc-color-muted); }

/* ---- placed furniture ---------------------------------------------------- */

.fp__items { position: absolute; inset: 0; }
.fp-piece {
  position: absolute;
  transform: translate(-50%, -50%);
  cursor: move;
  touch-action: none;
}
/* Only the artwork turns. The outer box stays axis-aligned so the controls
   hanging off it stay upright and stay put. */
.fp-piece__art { width: 100%; height: 100%; transform-origin: 50% 50%; }
.fp-piece svg { display: block; width: 100%; height: 100%; pointer-events: none; }
.fp-piece.is-selected .fp-piece__art {
  outline: calc(2px / var(--fp-scale, 1)) solid var(--hdc-color-primary);
}

/* Controls ride with the piece. Counter-scaled so they stay a constant size on
   screen however far the plan is zoomed. */
.fp-piece__ctl {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translate(-50%, calc(-8px / var(--fp-scale, 1)))
             scale(calc(1 / var(--fp-scale, 1)));
  transform-origin: bottom center;
  display: flex;
  gap: 2px;
  padding: 3px;
  /* Ink, not brand blue. This pill floats ON the drawing, so it has to read as
     tool chrome rather than as one more blue thing competing with the rail, the
     active tab and the floor pills. Dark also holds contrast over both the white
     plan and the tinted furniture fills, which a light blue would not. */
  background: var(--hdc-surface-ink);
  border-radius: var(--hdc-radius-pill);
  white-space: nowrap;
}
.fp-piece__ctl button {
  line-height: 0;
  --_bg: transparent;
  --_fg: var(--hdc-color-on-dark);
  /* Sized for a fingertip mid-drag, not for a mouse at rest — these sit on top
     of the piece being moved and get hit while the hand is already busy. */
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--hdc-radius-pill);
  background: var(--_bg);
  color: var(--_fg);
  cursor: pointer;
}
.fp-piece__ctl button:hover { --_bg: rgba(255, 255, 255, .18); }
.fp-piece__ctl svg {
  width: 1.15rem;
  height: 1.15rem;
  pointer-events: none;
  /* fp-rotate is stroke-drawn, fp-delete and fp-flip-piece are filled. The
     icons declare currentColor for whichever they use, so colour comes from
     the button either way and neither needs its own rule. */
  color: var(--_fg);
}
.fp-piece__ctl [data-fp-piece-act="flip"] svg { transform: scale(1.25); }

/* Only while the stage is resettling after a pane change — never during a pan,
   where a transition would make the plan lag the cursor. */
.fp__stage.is-settling .fp__canvas { transition: transform .3s var(--hdc-ease, ease); }

/* ---- measure ------------------------------------------------------------- */

.fp.is-measuring .fp__stage { cursor: crosshair; }
.fp-measures { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Counter-scaled so the line stays thin however far you zoom in. */
.fp-measures line {
  stroke: var(--hdc-color-primary);
  stroke-width: calc(2px / var(--fp-scale, 1));
  stroke-dasharray: calc(6px / var(--fp-scale, 1)) calc(4px / var(--fp-scale, 1));
}
.fp-measure__label {
  position: absolute;
  transform: translate(-50%, -50%);
  background: var(--hdc-color-primary);
  color: var(--hdc-color-on-dark);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-050);
  line-height: var(--hdc-lh-snug);
  padding: var(--hdc-space-1) var(--hdc-space-2);
  border-radius: var(--hdc-radius-sm);
  white-space: nowrap;
}
.fp.is-labels-off .fp-overlay { display: none; }

/* ---- furniture fill ------------------------------------------------------
   The stock artwork fills every piece solid white, which makes furniture
   disappear into a white room and — worse — occlude the room labels under it.
   The cut pieces hand those fills to this class instead, so the fill is one
   token rather than 21 files, and furniture reads as the buyer's layer against
   the builder's drawing. */
.fp-fill { fill: var(--fp-piece-fill, rgba(0, 121, 193, 0.12)); }
.fp-piece.is-selected .fp-fill { fill: var(--fp-piece-fill-on, rgba(0, 121, 193, 0.24)); }
/* In the library the tile is small, so it carries a touch more fill to read. */
.fp__lib-art .fp-fill { fill: rgba(0, 121, 193, 0.16); }

.fp__pane-actions { display: flex; flex-wrap: wrap; gap: var(--hdc-space-2);
                    padding: 0 var(--hdc-space-4) var(--hdc-space-3); }
/* 12 above + 12 below = 24 between the controls and the footnote. Was 12 + 24. */
/* `.fp .fp__pane-btn`, not `.fp__pane-btn`: the reset at the top of this file is
   `.fp button`, which is (0,1,1) and beats any bare (0,1,0) class. That is why
   these read as links — the padding below was being thrown away, not missing.
   Same trap that ate the rail labels; the extra `.fp` is what wins it. */
.fp .fp__pane-btn {
  --_bg: var(--hdc-surface);
  --_fg: var(--hdc-color-heading);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200);
  line-height: var(--hdc-lh-snug);
  padding: var(--hdc-space-3) var(--hdc-space-4);
  background: var(--_bg);
  color: var(--_fg);
  border: 1px solid var(--hdc-line-strong);
  border-radius: var(--hdc-radius-pill);
  cursor: pointer;
}
.fp .fp__pane-btn:hover { --_fg: var(--hdc-color-primary); border-color: var(--hdc-color-primary); }
.fp .fp__pane-btn.is-on { --_bg: var(--hdc-color-primary); --_fg: var(--hdc-color-on-dark);
                      border-color: var(--hdc-color-primary); }
.fp__empty b { color: var(--hdc-color-text); }

/* ---- pane close ---------------------------------------------------------- */

.fp__pane-hd { position: relative; padding-right: var(--hdc-space-8); }
.fp__pane-close {
  line-height: 0;
  --_bg: transparent;
  --_fg: var(--hdc-color-muted);
  position: absolute;
  top: var(--hdc-space-3);
  right: var(--hdc-space-3);
  width: 1.75rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--hdc-radius-pill);
  background: var(--_bg);
  color: var(--_fg);
  cursor: pointer;
}
.fp__pane-close:hover { --_bg: var(--hdc-surface); --_fg: var(--hdc-color-primary); }
.fp__pane-close svg { width: .95rem; height: .95rem; }

/* With every pane closed the panel collapses to its rail, handing the width
   back to the drawing. */
.fp.is-pane-closed { grid-template-columns: minmax(0, 1fr) 4.5rem; }
.fp.is-pane-closed .fp__panel { grid-template-columns: 4.5rem; }

/* The disclaimer had no rule of its own and was inheriting body copy from the
   theme, which is why it sat at reading size under a viewer nobody opened to
   read small print. 12 is the legal/micro step. */
.fp__legal {
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  line-height: var(--hdc-lh-snug);
  color: var(--hdc-color-muted);
  margin: var(--hdc-space-3) 0 0;
}
/* Underlined, not just coloured: colour alone is not an affordance, and this is
   the one link in the whole component. */
.fp__legal a {
  color: var(--hdc-color-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.fp__legal a:hover { color: var(--hdc-color-primary-deep); }

.fp__attrib {
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200);
  color: var(--hdc-color-muted);
  margin: var(--hdc-space-4) 0 0;
}

/* ---- panel tab icons ----------------------------------------------------- */

/* The drawn icons carry their own viewBoxes (24, 32, 48, 64), so the element is
   sized here and the box is left alone. */
.fp__panel-icon svg { width: 1.625rem; height: 1.625rem; display: block; }
/* 26 on the right, 22 on the left: the right rail is where the visitor decides
   to DO something — furnish, measure, look at exteriors, print, share — so it
   carries the larger targets. Both were ~19 and read as equal weight. */

/* ---- exteriors pane ------------------------------------------------------ */

.fp__elevs {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--hdc-space-4) var(--hdc-space-4);
  scrollbar-width: thin;
  scrollbar-color: var(--hdc-line-strong) transparent;
}
.fp__elev { margin: 0 0 var(--hdc-space-4); }
.fp__elev img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-sm);
}
.fp__elev figcaption {
  margin-top: var(--hdc-space-2);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  color: var(--hdc-color-muted);
}

/* ---- model tour ---------------------------------------------------------
   Mirrors .fp__elevs exactly: same inset, same scroll, and the poster gets the
   border and corner an elevation image has. The two panes sit side by side in
   the rail, so a tour arriving at a different size to a facade would read as a
   different component. */
.fp__tour {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--hdc-space-4) var(--hdc-space-4);
  scrollbar-width: thin;
  scrollbar-color: var(--hdc-line-strong) transparent;
}
.fp__tour .plan-video {
  display: block;
  width: 100%;
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-sm);
  overflow: hidden;
}
/* 16:9 and cropped, NOT the poster's own shape. Letting it run to `auto` was a
   mistake: the fallback poster is the model's featured image, which is portrait,
   and the facade came out 428x1550 — a column of photo down the rail. A tour
   thumbnail is a video still and should be video-shaped whatever it is made of. */
.fp__tour .plan-video img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 16 / 9; object-fit: cover;
}
/* The player replaces the facade, so it has to match the box it replaced. */
.fp__tour .plan-video__frame { border-radius: var(--hdc-radius-sm); }

/* ---- print --------------------------------------------------------------- */

a.fp__pane-btn { text-decoration: none; display: inline-block; }

/* Print the plan, not the page around it. */
@media print {
  .fp__panel,
  .fp__rail,
  .fp__floors,
  .fp__variants,
  .fp__readout,
  .fp-piece__ctl { display: none !important; }
  .fp { grid-template-columns: minmax(0, 1fr); border: 0; }
  .fp__stage { height: auto; aspect-ratio: 3 / 4; }
}

/* ---- wide pane ----------------------------------------------------------
   Exteriors is pictures, and pictures at 13rem are thumbnails nobody can judge
   a facade from. The panel widens only while it is open, then hands the width
   back to the drawing. */
.fp.is-pane-wide { grid-template-columns: minmax(0, 1fr) 34rem; }
.fp.is-pane-wide .fp__elevs { padding-right: var(--hdc-space-5); }
@media (max-width: 70rem) {
  .fp.is-pane-wide { grid-template-columns: minmax(0, 1fr) 24rem; }
}

/* The Print rail item is a link, so it needs the button's layout by hand. */
.fp__panel-rail a {
  --_bg: transparent;
  --_fg: var(--hdc-color-muted);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--hdc-space-1);
  margin: 0 var(--hdc-space-2);
  padding: var(--hdc-space-3) 0;
  border-radius: var(--hdc-radius-md);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
  line-height: var(--hdc-lh-snug);
  text-align: center;
  text-decoration: none;
}
.fp__panel-rail a:hover { --_bg: var(--hdc-surface); --_fg: var(--hdc-color-primary); }

/* ---- drag ghost ---------------------------------------------------------
   Follows the cursor out of the library and across the plan. Sized in rem, not
   plan units: it is a cursor affordance, not a preview of the placed piece. */
.fp-ghost {
  position: fixed;
  z-index: 999;
  width: 3rem;
  height: 3rem;
  margin: -1.5rem 0 0 -1.5rem;
  pointer-events: none;
  opacity: .55;
  color: var(--hdc-color-primary);
}
.fp-ghost.is-over { opacity: .9; }
.fp-ghost svg { width: 100%; height: 100%; }

/* ==========================================================================
   PHONE

   A different shape, not a narrower one. Below 60rem the viewer collapses to a
   teaser in the page; tapping it takes over the screen, where the controls sit
   in a row under a title bar and the panes rise as bottom sheets. Trying to fit
   a portrait plan, two rails and a 20rem panel into 390px produces something
   nobody can use.
   ========================================================================== */

.fp__teaser { display: none; }
.fp__mobile-hd { display: none; }

@media (max-width: 60rem) {

  /* ---- in the page: a teaser ---- */
  .fp { display: none; }
  .fp__teaser {
    position: relative;
    display: block;
    width: 100%;
    padding: 0;
    background: var(--hdc-surface);
    border: 1px solid var(--hdc-line);
    border-radius: var(--hdc-radius-md);
    overflow: hidden;
    cursor: pointer;
  }
  .fp__teaser-art {
    display: grid;
    place-items: center;
    height: 20rem;
    padding: var(--hdc-space-4);
    overflow: hidden;
    background: var(--hdc-surface);
  }
  /* Whole, not cropped. The reference crops because its plan is landscape; ours
     is portrait 1:2, so a crop lands on an arbitrary corner — a complete plan at
     this height still reads as a floor plan, which is all the teaser owes. */
  .fp__teaser-art svg {
    height: 100%;
    width: auto;
    max-width: 100%;
  }
  .fp__teaser-cta {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: inline-flex;
    align-items: center;
    gap: var(--hdc-space-2);
    padding: var(--hdc-space-3) var(--hdc-space-4);
    white-space: nowrap;
    background: var(--hdc-surface);
    color: var(--hdc-color-primary);
    border: 1px solid var(--hdc-color-primary);
    border-radius: var(--hdc-radius-pill);
    font-family: var(--hdc-font-ui);
    font-size: var(--hdc-fs-300);
    font-weight: var(--hdc-fw-semi);
    box-shadow: var(--hdc-shadow-2);
  }
  .fp__teaser-cta svg { width: 1.05rem; height: 1.05rem; }

  /* ---- taken over ---- */
  body.fp-locked { overflow: hidden; }

  .fp.is-mobile-open {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0;
    z-index: 9999;
    border: 0;
    border-radius: 0;
  }

  .fp.is-mobile-open .fp__mobile-hd {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--hdc-space-4);
    /* Same max() treatment as the tab bar below. Inert today — env() is 0
       without viewport-fit=cover, which this site does not set — but correct if
       that ever changes. */
    padding: max(var(--hdc-space-4), env(safe-area-inset-top))
             max(var(--hdc-space-5), env(safe-area-inset-right))
             var(--hdc-space-4)
             max(var(--hdc-space-5), env(safe-area-inset-left));
    background: var(--hdc-surface-warm);
    border-bottom: 1px solid var(--hdc-line);
  }
  .fp__mobile-hd span {
    display: block;
    font-size: var(--hdc-fs-100);
    font-weight: var(--hdc-fw-semi);
    letter-spacing: var(--hdc-tracking-eyebrow);
    text-transform: uppercase;
    color: var(--hdc-color-primary);
  }
  .fp__mobile-hd b {
    display: block;
    font-family: var(--hdc-font-display);
    font-weight: var(--hdc-fw-regular);
    font-size: var(--hdc-fs-500);
    color: var(--hdc-color-heading);
  }
  /* Carries .hdc-close (components.css) for the shared disc, mark and size. This
     was 2.5rem while the area map next door was 2.25rem — the same control at two
     sizes on two pages. Only the colours are set here now. */
  .fp__mobile-close {
    --_bg: var(--hdc-surface);
    --_fg: var(--hdc-color-heading);
  }

  /* view tools become a row under the title, not a floating rail */
  .fp.is-mobile-open .fp__rail {
    /* relative, not static: z-index only applies to a POSITIONED element, and
       as static the rail's z-index:3 was silently dropped, so the canvas — which
       is absolute — painted over it and the plan's walls ran up through the
       button labels. relative also honours the base rule's top/left, which
       static ignored, so both are reset here. */
    position: relative;
    top: auto;
    left: auto;
    transform: none;
    flex-direction: row;
    justify-content: space-around;
    border: 0;
    border-bottom: 1px solid var(--hdc-line);
    border-radius: 0;
    padding: var(--hdc-space-2) var(--hdc-space-3);
    background: var(--hdc-surface-warm);
  }
  .fp.is-mobile-open .fp__rail hr { display: none; }
  /* Already full screen — the button would promise something it cannot do. */
  .fp.is-mobile-open .fp__rail [data-fp-action="fullscreen"] { display: none; }
  .fp.is-mobile-open .fp__rail button { width: auto; flex: 1 1 0; min-width: 0; }
  .fp.is-mobile-open .fp__rail button span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
  }

  /* The header states the plan now, so the in-stage title is a second copy of
     the same words sitting under the tool row. */
  .fp.is-mobile-open .fp__title { display: none; }

  .fp.is-mobile-open .fp__stage { flex: 1 1 auto; height: auto; min-height: 0; }
  .fp.is-mobile-open .fp__floors { bottom: var(--hdc-space-4); }
  .fp.is-mobile-open .fp__variants { bottom: calc(var(--hdc-space-4) + 2.7rem); }
  /* A sheet covers the bottom of the plan, so the pills under it are unreachable
     anyway — hiding them is honest rather than stacking them on top. */
  .fp.is-mobile-open:not(.is-pane-closed) .fp__floors,
  .fp.is-mobile-open:not(.is-pane-closed) .fp__variants { display: none; }
  .fp.is-mobile-open .fp__legal { display: none; }

  /* ---- panel becomes a tab bar plus a sheet ---- */
  .fp.is-mobile-open .fp__panel {
    display: block;
    border-left: 0;
    border-top: 1px solid var(--hdc-line);
  }
  .fp.is-mobile-open .fp__panel-rail {
    flex-direction: row;
    border-right: 0;
    padding: var(--hdc-space-2) var(--hdc-space-2) calc(var(--hdc-space-2) + env(safe-area-inset-bottom));
  }
  /* min-width:0 is the whole fix: a flex item defaults to min-width:auto and
     refuses to shrink below its label, so four tabs measured 408px inside a
     390px screen and pushed the close button off the right edge. */
  .fp.is-mobile-open .fp__panel-rail button,
  .fp.is-mobile-open .fp__panel-rail a {
    flex: 1 1 0;
    min-width: 0;
    margin: 0;
  }
  .fp.is-mobile-open .fp__panel-rail button span:last-child,
  .fp.is-mobile-open .fp__panel-rail a span:last-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
  }

  /* A sheet, so the plan stays visible above whatever is open. */
  /* Above the floor pills (z 3), which are absolutely positioned in the stage
     and otherwise float over the sheet that is covering them. */
  /* ---- the sheet moves, it does not appear ----
     A panel that snaps into existence reads as a page change; one that rises
     reads as a drawer. Transform and opacity only, both composited, so a phone
     does this on the compositor and never on the main thread.

     `hidden` cannot transition — it is display — so floorplan-viewer.js drops the
     attribute first, lets a frame pass, then adds .is-up; on the way out it waits
     for transitionend before hiding. */
  .fp.is-mobile-open .fp__pane {
    transform: translateY(100%);
    opacity: 0;
    transition: transform var(--hdc-dur) var(--hdc-ease), opacity var(--hdc-dur-fast) var(--hdc-ease);
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 5;
    max-height: 62vh;
    background: var(--hdc-surface);
    border-top: 1px solid var(--hdc-line);
    border-radius: var(--hdc-radius-lg) var(--hdc-radius-lg) 0 0;
    box-shadow: var(--hdc-shadow-3);
    padding-bottom: env(safe-area-inset-bottom);
  }
  /* ---- the sheet's grab bar ----
     A bottom sheet that can be dragged needs to LOOK draggable, and the bar is
     the convention people already know from every app sheet. Drawn rather than
     marked up, because the header is repeated in every pane and this is not
     content — see the pointer handling in floorplan-viewer.js. */
  .fp.is-mobile-open .fp__pane-hd {
    padding-top: var(--hdc-space-6);
    touch-action: none;          /* the drag belongs to the sheet, not the page */
    cursor: grab;
  }
  .fp.is-mobile-open .fp__pane-hd::before {
    content: "";
    position: absolute;
    top: var(--hdc-space-3);
    left: 50%;
    transform: translateX(-50%);
    width: 2.5rem;
    height: 4px;
    border-radius: var(--hdc-radius-pill);
    background: var(--hdc-line-strong);
  }
  .fp.is-mobile-open .fp__pane.is-up { transform: translateY(0); opacity: 1; }
  /* Following a finger has to be instant, so the drag turns the transition off
     and the release turns it back on to carry the spring-back. */
  .fp.is-mobile-open .fp__pane.is-dragging { transition: none; cursor: grabbing; }

  /* Bigger, because a 28px target beside a 4px bar is the thing people miss. */
  .fp.is-mobile-open .fp__pane-close { width: 2.5rem; height: 2.5rem; top: var(--hdc-space-4); }
  .fp.is-mobile-open .fp__pane-close svg { width: 1.25rem; height: 1.25rem; }

  /* No Print on a phone. It opens a brochure PDF in a new tab, which on a phone
     means leaving the viewer for a document nobody is going to print from there —
     and the rail has four other things competing for the same strip. */
  /* ⚠️ (0,2,0). `.fp__panel-rail a` sets display at (0,1,1) and beats a single
     class, so the link stayed visible on a phone. */
  .fp__panel-rail .fp__panel-print { display: none; }

  /* ---- backdrop, for content panes only ----
     Behind the sheet when what is open is something to LOOK at rather than
     something to work with. Furniture and Measure deliberately get none: you are
     reading the plan while you use them, and blurring it would be blurring the
     thing you are aiming at.

     backdrop-filter is not universal, so the tint carries the effect on its own
     where it is unsupported — the sheet still reads as being in front. */
  /* Painted always, at zero opacity, because a pseudo-element that is created by
     a class cannot transition into existence — it would snap. Opacity is the only
     thing that changes. */
  .fp.is-mobile-open::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 4;                       /* under the sheet (5), over the stage */
    background: color-mix(in srgb, var(--hdc-surface-ink) 28%, transparent);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--hdc-dur) var(--hdc-ease);
  }
  .fp.is-mobile-open.is-pane-content::after { opacity: 1; }

  .fp.is-mobile-open.is-pane-wide { grid-template-columns: none; }

  /* one column of tiles reads better than two at this width */
  .fp.is-mobile-open .fp__group-grid { grid-template-columns: 1fr 1fr; }
  .fp.is-mobile-open .fp__lib { max-height: 40vh; }
  .fp.is-mobile-open .fp__elevs { max-height: 40vh; }
}

/* ---- scroll gate --------------------------------------------------------
   Until the viewer is clicked it does not take the wheel, so the page scrolls
   past it normally. The prompt says so, and disappears on the first click. */
.fp__gate {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--hdc-surface) 55%, transparent);
  pointer-events: none;
  opacity: 1;
  transition: opacity var(--hdc-dur) var(--hdc-ease);
}
.fp.is-active .fp__gate { opacity: 0; }

.fp__gate span {
  display: inline-flex;
  align-items: center;
  gap: var(--hdc-space-2);
  padding: var(--hdc-space-3) var(--hdc-space-5);
  background: var(--hdc-surface);
  border: 1px solid var(--hdc-line-strong);
  border-radius: var(--hdc-radius-pill);
  box-shadow: var(--hdc-shadow-2);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-300);
  font-weight: var(--hdc-fw-semi);
  color: var(--hdc-color-heading);
}

/* The gate is a hint, not a shield — it never swallows the click that dismisses
   it, so one click both activates the viewer and does what you meant. */
.fp:not(.is-active) .fp__stage { cursor: pointer; }

@media (max-width: 60rem) { .fp__gate { display: none; } }

/* ---- "Bar stool added" ----------------------------------------------------
   Confirmation for a tap whose result is hidden behind the sheet that received
   it. Above the sheet (JS sets `bottom` from the sheet's HEIGHT, which a
   transform does not move), centred, and out of the way of pointers.

   JS sets opacity to show and hide it. ⚠️ NO TRANSITION — see the long note in
   announce(); every attempt at one left the toast pinned at its opening frame,
   invisible, because the element cannot be reliably painted before its first
   message. */
.fp__toast {
  position: fixed;
  left: 50%;                      /* replaced per-show: centred on the visible plan */
  z-index: 6;                     /* over the sheet (5) and its backdrop (4) */
  transform: translate(-50%, 6px);
  opacity: 0;
  pointer-events: none;
  padding: var(--hdc-space-2) var(--hdc-space-4);
  border-radius: var(--hdc-radius-pill);
  /* THE ACTIVE-TAB TREATMENT, which this viewer already uses two rules away: see
     .fp__panel-rail button.is-on above — primary-wash behind brand-blue text. The
     search's active filter pill ("Single-family x") is the same pairing, so this
     is the look the site uses for "this is on" wherever the mark has something
     beside it doing the explaining.

     Not the solid .chip.is-on fill: that is reserved for controls with nothing
     else to say which is selected, like the floor pills.

     ⚠️ 3.97:1, where AA wants 4.5 for text this size, and brand blue cannot reach
     it on any blue tint — measured, even a 4% wash tops out at 4.43. It is
     consistent with the two patterns above, it is transient, and it announces
     itself over aria-live, so nothing depends on reading it. Recorded so the next
     person does not measure it again. */
  background: var(--hdc-color-primary-wash);
  color: var(--hdc-color-primary);
  border: 1px solid var(--hdc-color-primary-line);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-300);
  font-weight: var(--hdc-fw-medium);        /* as .fp__panel-rail button.is-on */
  white-space: nowrap;
  box-shadow: var(--hdc-shadow-2);
}

/* The furniture hint names the input that is actually available at this width —
   see the note in inc/floorplan-viewer.php. */
.fp__hint--narrow { display: none; }
@media (max-width: 60rem) {
  .fp__hint--wide { display: none; }
  .fp__hint--narrow { display: block; }
}

/* ---- reduced motion ------------------------------------------------------
   Nothing here. base.css already ends with a global

       @media (prefers-reduced-motion: reduce) { * { transition: none !important;
                                                     animation: none !important; } }

   and those are the SHORTHANDS, so they reset transition-property and
   animation-name to none for every element on the page. This file used to
   re-declare the kill as `animation-duration: 0.01ms !important` and friends on
   `.fp *`. That selector is more specific, so it did win the duration — but with
   the name already forced to none there was no animation left to shorten, which
   is why removing it changes nothing.

   The block also carried `scroll-behavior: auto !important`, guarding against a
   `scroll-behavior: smooth` that does not exist anywhere in the theme:
   community.css:1143 says so explicitly — "NO scroll-behavior: smooth, the arrow
   code animates scrollLeft by hand" — and ui-glide.js checks the reduced-motion
   query itself and jumps straight to the destination.

   The remaining !important in this file is the @media print block, which is a
   real specificity fight and stays. */

/* ---- unit type (corner / interior) ---------------------------------------
   A second, INDEPENDENT choice above the floor pills: which unit in the row you
   are looking at. Only townhome plans drawn more than once have it, so the
   control is hidden unless the plan carries real variants.

   Deliberately quieter than the floor pills — same shape, no fill on the active
   item, just weight and the brand colour. The floor is the thing being switched
   most often, and two identical pill groups stacked on top of each other made it
   ambiguous which one was the primary control. */
.fp__variants {
  position: absolute;
  left: 50%;
  bottom: calc(var(--hdc-space-5) + 2.9rem);
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  gap: var(--hdc-space-1);
  padding: var(--hdc-space-1);
  background: var(--hdc-surface);
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-pill);
  box-shadow: var(--hdc-shadow-1);
}
/* A class carrying `display` beats the UA's [hidden] rule — the trap this theme
   has hit five times. See hidden-attribute notes in siteplan.css. */
.fp__variants[hidden] { display: none; }

.fp__variants button {
  --_bg: transparent;
  --_fg: var(--hdc-color-muted);
  font-family: var(--hdc-font-ui);
  line-height: var(--hdc-lh-snug);
  font-size: var(--hdc-fs-200);
  font-weight: var(--hdc-fw-medium);
  letter-spacing: var(--hdc-tracking-ui);
  padding: var(--hdc-space-1) var(--hdc-space-4);
  border: 0;
  border-radius: var(--hdc-radius-pill);
  background: var(--_bg);
  color: var(--_fg);
  white-space: nowrap;
  cursor: pointer;
}
.fp__variants button:hover { --_fg: var(--hdc-color-primary); }
.fp__variants button.is-on {
  --_bg: var(--hdc-color-primary-wash);
  --_fg: var(--hdc-color-primary);
  font-weight: var(--hdc-fw-semi);
}

/* The floor pills hide themselves on a single-floor plan; keep the same rule here
   so nothing leaves an empty pill container floating over the drawing. */
.fp__floors[hidden] { display: none; }

/* One named unit type is a statement, not a choice — Model B is drawn as the
   interior unit, and the corner drawing differs only in one wall's treatment.
   So the same slot becomes a caption: no border, no shadow, nothing that looks
   pressable. */
.fp__variants--static {
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0;
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200);
  font-weight: var(--hdc-fw-medium);
  letter-spacing: var(--hdc-tracking-ui);
  color: var(--hdc-color-muted);
  pointer-events: none;
}

/* A raster floor plan, standing in for an SVG one. It fills the canvas exactly
   as the inline <svg> does, so every coordinate conversion above it is unchanged
   — the canvas is pinned to the drawing's own pixel width by renderFloor().

   image-rendering stays default (smooth): these are line drawings photographed
   at high resolution, and crisp-edges makes the diagonal walls and hatching
   shimmer under the viewer's transform. */
.fp__raster {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}
