/* ==========================================================================
   Site plan — interactive lot map + inventory panel.

   Its own sheet because it is no longer one page's furniture: the community
   single renders it whole, and the floor-plan single renders the same partial
   filtered to one plan (open homesites plus that plan's standing homes). It is
   enqueued wherever template-parts/siteplan.php is used.

   Tokens only. Markup in template-parts/siteplan.php, logic in inc/siteplan.php.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Site plan — interactive lot map + inventory panel

   The SVG is anchored to __frame, which is sized BY THE IMAGE. Anchoring it to
   the grid item instead lets that column stretch to match the taller panel, and
   the polygons stretch vertically with it — invisible at the width you build at.
   -------------------------------------------------------------------------- */
.siteplan {
  display: grid; grid-template-columns: 1fr 420px; gap: var(--hdc-space-6);
  /* ⚠️ A grid item's default min-width is AUTO, not 0 — it refuses to shrink below
     its content. Harmless while the panel held a wrapping column, but the phone
     rail's 80 cards are one unbreakable line, so the panel grew to 8358px on a
     390px screen. The page did not visibly overflow (an ancestor clipped it), yet
     every measurement the rail depends on was wrong: 27 cards fitted the "viewport",
     so snapping had nothing to snap and scrollIntoView never scrolled. */
  align-items: start;
  border: 1px solid var(--hdc-line); border-radius: var(--hdc-radius-md);
  /* Warm INSIDE the border only. The section around it is --white, so the box is
     the thing that reads as a panel — the heading above stays on the white. */
  overflow: hidden; background: var(--hdc-surface-warm);
}
/* touch-action: pan-y so a one-finger drag still scrolls the PAGE at rest — the
   drawing is most of a phone screen, and a map you cannot scroll past is a trap.
   Without any touch-action at all the browser claimed every touch as scroll and
   fired pointercancel, which is why neither drag nor pinch did anything.

   ⚠️ BUT pan-y DOES NOT EXCLUDE PINCH-ZOOM. Safari treats a two-finger pinch as
   page zoom regardless, so the plan scaled under the fingers AND the whole page
   zoomed with it. The `is-pinching` class below is added by siteplan.js the
   instant a second finger lands and removed when it lifts, which hands the
   gesture to the plan for exactly as long as two fingers are down. A static rule
   cannot do this: pan-y alone leaks the pinch, and `none` alone costs the
   one-finger page scroll the drawing needs at rest. */
.siteplan__frame { position: relative; line-height: 0; overflow: hidden; touch-action: pan-y; }
/* Zoomed in there IS somewhere to pan to, so the drag belongs to the plan. */
.siteplan__frame.is-grab,
.siteplan__frame.is-pinching { touch-action: none; }
/* The stage is the moving part: drawing and polygons scale together, because the
   lot shapes are percent-of-image and anything that moved one without the other
   would slide them apart. transform-origin at the corner keeps the arithmetic in
   siteplan.js simple — translate then scale, no compensation term. */
.siteplan__stage { transform-origin: 0 0; will-change: transform; }
.siteplan__stage.is-eased { transition: transform .35s var(--hdc-ease, ease); }
.siteplan__frame img { width: 100%; height: auto; display: block; }
.siteplan__svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.siteplan__frame.is-grab { cursor: grab; }
.siteplan__frame.is-grabbing { cursor: grabbing; }

/* DESKTOP HEIGHT CAP. The drawings are tall portraits, so at a 900px-wide column
   the plan ran past the fold and the section could not be seen at once — you had
   to scroll to find the bottom of a map you were trying to read as a whole.
   Capping the frame's WIDTH at (height x ratio) leaves the IMAGE as the sizer, so
   the percent-of-image polygons stay aligned; a max-height on the frame would
   letterbox the image inside it and slide every lot shape off its parcel. */
/* Mirrors .fp__rail — same three controls, same shape, same place. A drawing you
   can zoom should offer it identically wherever it appears, and the viewer's rail
   is on the LEFT, so this one is too. */
/* The positioning context for the rail: full column width, so the rail's left
   edge is fixed while the drawing inside it is centred and varies in width. */
.siteplan__pane { position: relative; display: flex; justify-content: center; }

.siteplan__zoom {
  position: absolute;
  left: var(--hdc-space-4);
  /* Vertically centred, not bottom-anchored: the drawing is tall and portrait, so
     a bottom-left rail sat a long way from the parcels people are reaching for. */
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  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-md);
  box-shadow: var(--hdc-shadow-2);
}
/* ⚠️ GeneratePress pads content buttons and colours them on hover; without these
   the icons sit off-centre in boxes bigger than they were asked for. Same reset
   .fp button carries. */
/* Mirrors .fp__rail button exactly: icon over a label, 4rem wide, same paint. */
.siteplan__zoom button {
  --_bg: transparent;
  --_fg: var(--hdc-color-heading);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  width: 4rem;
  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;
}
.siteplan__zoom button:hover { --_bg: var(--hdc-surface-warm); --_fg: var(--hdc-color-primary); }
.siteplan__zoom svg { width: 1.375rem; height: 1.375rem; display: block; }
/* Lots that are neither an open homesite nor a home of the plan being viewed.
   Drawn, not removed: a site plan missing three-quarters of its lots stops
   reading as a map of a place, and the highlighted ones only mean something
   against the ones that are not.

   Desaturated, NOT faded. opacity would let the artwork show through, and the
   artwork has its own lot numbers baked into it — so a translucent polygon put a
   printed number underneath our rendered one. A filter preserves alpha, so the
   fill stays fully opaque and keeps covering the plate.

   Its own class rather than reusing .is-dim: that one is toggled by the chip
   filters in siteplan.js and is a transient state, where this is permanent. */
.siteplan__svg .lot.is-muted {
  filter: saturate(0.08) brightness(1.18); pointer-events: none; }
/* The NUMBER does not go quiet with its lot. Quieting it was the wrong instinct:
   --hdc-color-muted is a mid grey on a now-grey parcel, so the numbers vanished
   entirely. Every lot number stays white — the polygon carries the status, the
   number only has to say which lot it is, and on a map you read the neighbours to
   find the one you want. */

/* Status is PAINTED from data, never read from the artwork — that is the point.
   The artwork has lot numbers printed on it and we draw our own on top, so a
   translucent fill shows both. Every state must stay fully opaque — hover and
   dim are expressed with filter, never opacity. */
.lot { cursor: pointer; stroke: var(--hdc-surface); stroke-width: .18;
  transition: filter var(--hdc-dur) var(--hdc-ease); }
.lot.is-ready     { fill: var(--hdc-color-primary); }
.lot.is-available { fill: var(--hdc-siteplan-available); }
.lot.is-pending   { fill: var(--hdc-siteplan-pending); }
.lot.is-sold      { fill: var(--hdc-siteplan-sold); }
.lot.is-model     { fill: var(--hdc-siteplan-model); }
.lot.is-soon      { fill: var(--hdc-siteplan-soon); }
.lot:hover, .lot.is-on { filter: brightness(1.15); }
.lot.is-dim { filter: saturate(.18) brightness(1.1); }
.lot-num { font-family: var(--hdc-font-ui); font-size: 1.5px; font-weight: var(--hdc-fw-semi);
  fill: var(--hdc-color-on-dark); text-anchor: middle; dominant-baseline: middle;
  pointer-events: none; }

.siteplan__panel { display: flex; flex-direction: column; min-width: 0;
  border-left: 1px solid var(--hdc-line); }
.siteplan__head { padding: var(--hdc-space-5); border-bottom: 1px solid var(--hdc-line); }
.siteplan__count { margin: 0 0 var(--hdc-space-4); font-family: var(--hdc-font-display);
  font-size: var(--hdc-fs-550); color: var(--hdc-color-heading); }
.siteplan__chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--hdc-space-2); }

/* The context chips — "The Cedar", "Lot 22". A different AXIS from the status
   chips beside them, so a rule divides the two rather than letting them read as
   five statuses in a row. Color and state come from .chip and its --_bg/--_fg
   props, untouched: this is spacing and an affordance, not a repaint. (Re-pinning
   a chip's colors is what caused the three-state bug — see theme.css.)

   The rule belongs to the FIRST status chip, not to the context chips, and only
   when a context chip is actually on screen. There can be nought, one or two of
   those — the page's own and one a map click created, either of which may be
   hidden — so anything keyed to adjacency drew a stray line before "All" in one
   arrangement or suppressed a wanted one in another. The general sibling
   combinator asks the only question that matters: is there a visible context chip
   to the left of this? */
[data-filter="all"] { position: relative; }
.chip--ctx:not([hidden]) ~ [data-filter="all"] { margin-left: var(--hdc-space-4); }
.chip--ctx:not([hidden]) ~ [data-filter="all"]::before {
  content: ""; position: absolute; left: calc(var(--hdc-space-3) * -1);
  top: 50%; transform: translateY(-50%); width: 1px; height: 22px; background: var(--hdc-line-strong); }
.chip__x { margin-left: var(--hdc-space-2); font-size: 1.15em; line-height: 1; opacity: .75; }
/* Nothing to clear when it is already cleared — the × would promise the opposite
   of what a click does. */
.chip--ctx:not(.is-on) .chip__x { display: none; }

/* A class carrying `display` beats the browser's [hidden] rule — the exact bug the
   filter chips hit below. Both keys and chips are restated where the display is. */
.siteplan__key[hidden] { display: none; }
/* The single-lot view sets [hidden] on this, and a class carrying `display: flex`
   beats the browser's own [hidden] rule — the chips rendered anyway, offering to
   filter a map already filtered to one parcel. Restated here, where the display
   that broke it is declared. */
.siteplan__chips[hidden] { display: none; }
.siteplan__list { overflow-y: auto; max-height: 620px; min-width: 0; }
.siteplan__more {
  display: none; width: 100%; cursor: pointer;
  padding: var(--hdc-space-4); border: 0; border-top: 1px solid var(--hdc-line);
  background: var(--hdc-surface); color: var(--hdc-color-primary);
  font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-400); font-weight: var(--hdc-fw-semi);
}
.siteplan__more:hover { background: var(--hdc-surface-warm); }

/* The row is an <a> already, so actions (also links) can't nest inside it — this
   wrapper is what actually owns the hairline and the expanded-actions tint. */
.siteplan-item { border-bottom: 1px solid var(--hdc-line); }
/* Set by JS when the mobile clamp is on. NOT nth-child: that counts DOM position
   and cannot see which rows the filter has hidden, so filtering while clamped
   silently dropped matching lots. */
.siteplan-item.is-clamped-out { display: none; }
/* A class-based display would beat the UA's [hidden]; the filter relies on it. */
.siteplan-item[hidden] { display: none; }
.siteplan-item .siteplan-row { border-bottom: 0; }

/* Same grammar as .home-card: media left, body right, lot number as quiet meta. */
.siteplan-row { display: grid; grid-template-columns: 116px 1fr; gap: var(--hdc-space-4);
  align-items: center; padding: var(--hdc-space-4) var(--hdc-space-5);
  border-bottom: 1px solid var(--hdc-line); text-decoration: none; }
/* A class-based display beats the UA's [hidden] rule, so the filter's row.hidden
   would set the attribute and change nothing on screen. */
.siteplan-row[hidden] { display: none; }
/* One tint for all three: pointing at a row, highlighting it from the map, and
   having chosen it. They were warm / warm / tint, which spent two colours on a
   distinction nobody reads — the row you are pointing at and the row you picked
   are the same row a moment apart. Matches the plan explorer's list. */
.siteplan-row:hover,
.siteplan-row.is-on,
.siteplan-row.is-selected { background: var(--hdc-surface-tint); }

/* The row for the page you are already on. Rendered as a <div>, so it has no link
   affordance to take away — this only stops the hover tint promising one. */
.siteplan-row.is-self { cursor: default; background: var(--hdc-surface-tint); }
.siteplan-row.is-self:hover { background: var(--hdc-surface-tint); }
.siteplan-row__here { font-size: var(--hdc-fs-100); font-weight: var(--hdc-fw-semi);
  letter-spacing: var(--hdc-tracking-ui); text-transform: uppercase;
  color: var(--hdc-color-primary); }

.siteplan-row__actions {
  display: flex; flex-wrap: wrap; gap: var(--hdc-space-2);
  padding: 0 var(--hdc-space-5) var(--hdc-space-4);
  background: var(--hdc-surface-tint);
}
/* A class-based display beats the UA's [hidden] rule, so without this the actions
   would stay on screen while reporting as hidden. */
.siteplan-row__actions[hidden] { display: none; }

/* Compact on purpose: the panel is 420px and .btn is sized for page-level CTAs. */
.siteplan-act {
  font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-400); font-weight: var(--hdc-fw-semi);
  padding: var(--hdc-space-2) var(--hdc-space-4); border-radius: var(--hdc-radius-sm);
  border: 1px solid var(--hdc-line-strong); color: var(--hdc-color-heading);
  text-decoration: none; white-space: nowrap;
  transition: background var(--hdc-dur) var(--hdc-ease), color var(--hdc-dur) var(--hdc-ease), border-color var(--hdc-dur) var(--hdc-ease);
}
.siteplan-act:hover { border-color: var(--hdc-color-primary); color: var(--hdc-color-primary); }
.siteplan-act--go { background: var(--hdc-color-primary); border-color: var(--hdc-color-primary);
  color: var(--hdc-color-on-dark); }
.siteplan-act--go:hover { background: var(--hdc-color-primary-deep);
  border-color: var(--hdc-color-primary-deep); color: var(--hdc-color-on-dark); }
.siteplan-row__media { border-radius: var(--hdc-radius-sm); overflow: hidden;
  background: var(--hdc-surface-tint); line-height: 0; display: block; }
.siteplan-row__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }

/* ---- the empty lot ------------------------------------------------------
   A house that is not there yet. It replaced a dashed outline with a question
   mark in it, which is the visual language of a BROKEN IMAGE — and the row
   already says "Available homesite" in words right beside it.

   The art is a straight-on silhouette of the Acacia, solid: an unbuilt lot has
   not chosen any windows or doors, so none are drawn. It is applied as a mask
   rather than placed as an image, so the colour lives here in the stylesheet
   with every other colour, and the one file works anywhere.

   Slate, deliberately not the brand blue: blue means "live" everywhere else on
   this site, and an empty lot is the one thing that is not. */
.homesite {
  display: grid; place-items: center; position: relative;
  width: 100%; aspect-ratio: 4 / 3;
  background: var(--hdc-surface-tint);
}
/* No ground strip. It was there to sit the house on something, and all it did
   was give the tile two background colours — at 116px that reads as a rendering
   fault, not as ground. One flat tint. */
/* 96%, not 88%. The house is 3.8:1 and the tile is 4:3, so width is the only
   lever there is — at 88% it sat as a small band with margin on every side and
   read as a smudge at 116px. */
.homesite::before {
  content: ""; width: 96%; aspect-ratio: 824 / 218;
  background: color-mix(in srgb, var(--hdc-surface-ink) 36%, var(--hdc-surface-tint));
  -webkit-mask: url(img/homesite-house.png) center / contain no-repeat;
  mask: url(img/homesite-house.png) center / contain no-repeat;
}
/* ⚠️ line-height HAS TO COME BACK HERE. .siteplan-row__media sets line-height: 0 so
   an inline <img> does not reserve baseline space under it — correct for an image,
   and inherited by this TEXT placeholder, where it collapsed "Photography needed"
   onto a single zero-height line and printed both words on top of each other.
   Invisible until the BuildTopia import created the first six homes with no
   photography of their own. */
.siteplan-row__media .media-empty { width: 100%; aspect-ratio: 4 / 3; line-height: 1.3; padding: 0 var(--hdc-space-2); }
/* Two fonts, two sizes. Plan and lot are quiet UI meta; the price is the headline
   in the display face — it is the line a buyer scans for. */
.siteplan-row__meta { display: flex; align-items: center; gap: var(--hdc-space-2);
  font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-300); color: var(--hdc-color-muted); }
.siteplan-row__dot { width: 8px; height: 8px; flex: 0 0 auto;
  border-radius: var(--hdc-radius-pill); background: var(--hdc-siteplan-available); }
.siteplan-row__dot.is-ready { background: var(--hdc-color-primary); }
.siteplan-row__dot.is-available { background: var(--hdc-siteplan-available); }
.siteplan-row__lead { display: block; margin: 2px 0 4px;
  font-family: var(--hdc-font-display); font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-600); color: var(--hdc-color-heading); line-height: 1.1; }
.siteplan-row__sub { display: block; font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-300); color: var(--hdc-color-muted); }
.siteplan-row .spec-list { margin-top: 6px; }
/* Condensed in the panel: the spec icons repeat what the words already say and
   cost a line at this width. The shared .spec-list keeps them everywhere else. */
.siteplan-row .spec-ico { display: none; }
/* With the icons gone the specs run together, so a middot does the separating.
   It hangs off the ::after of every item BUT the last: on ::before, a wrapped
   line opened with a stranded "· 3 car garage". Generated content, so it stays
   out of the markup and out of the accessibility tree. */
/* The dot is a flex child of its <li>, so the li's OWN gap sits before it and the
   list's column-gap sits after. Both must be the same value or the dot reads as
   belonging to the word on its left. */
.siteplan-row .spec-list { gap: 2px var(--hdc-space-1); }
.siteplan-row .spec-list li { gap: var(--hdc-space-1); }
.siteplan-row .spec-list li { white-space: nowrap; }
.siteplan-row .spec-list li:not(:last-child)::after {
  /* A drawn circle, not a bullet glyph. A "\2022" is sized by the font's metrics,
     so it lands differently in Figtree than anywhere else and needs baseline
     nudging to look centerd. This is an exact 5px dot that takes a token color —
     which an inline SVG data-URI could not, since it cannot read CSS variables. */
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: var(--hdc-radius-pill);
  background: var(--hdc-line-strong);
  vertical-align: middle;
}

.siteplan__legend { margin: var(--hdc-space-3) 0 var(--hdc-space-4); display: flex; flex-wrap: wrap; gap: var(--hdc-space-4);
  padding: var(--hdc-space-4) var(--hdc-space-5); border-top: 1px solid var(--hdc-line);
  font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-300); }
.siteplan__key { display: inline-flex; align-items: center; gap: 7px; }
.siteplan__key::before { content: ""; width: 12px; height: 12px; border-radius: 3px; }
.siteplan__key.is-ready::before     { background: var(--hdc-color-primary); }
.siteplan__key.is-available::before { background: var(--hdc-siteplan-available); }
.siteplan__key.is-pending::before   { background: var(--hdc-siteplan-pending); }
.siteplan__key.is-sold::before      { background: var(--hdc-siteplan-sold); }
.siteplan__key.is-model::before     { background: var(--hdc-siteplan-model); }
.siteplan__key.is-soon::before      { background: var(--hdc-siteplan-soon); }
/* Artwork but no geometry: show the plan flat rather than a dead interactive shell. */
.siteplan--flat { display: block; padding: var(--hdc-space-5); }
.siteplan--flat .is-unset { margin: var(--hdc-space-4) 0 0; }

/* ==========================================================================
   DESKTOP: the whole section fits one screen
   The section measured 138vh and only 42% of that was the plan, so the chrome
   pays first — tighter block padding, a shorter list — and what is left goes to
   the drawing. These plans are PORTRAIT, so a landscape screen can have a big
   drawing or a whole section, not both; this is the compromise.

   ⚠️ Must stay AFTER the base .siteplan__list rule. Sitting above it, the 620px
   base max-height won on source order and the panel stayed 834px tall — the
   section barely moved and the drawing had shrunk for nothing.
   ========================================================================== */
@media (min-width: 1001px) {
  .section--sitemap { padding-block: var(--hdc-space-6); }
  /* space-7 spent 48px separating one button from the box above it. */
  .section--sitemap .siteplan-foot { margin-top: var(--hdc-space-5); }

  .siteplan {
    /* The height left for the drawing after everything else in the section: the
       block padding, the heading, the PDF button and its margin.

       A flat vh fraction cannot work here — that chrome is a FIXED number of
       pixels, so 74vh fitted a 1080px screen and overflowed an 820px one by 72.
       Measured at 268px on Silver Oaks, then raised to clear Heartwood, whose
       heading and legend run taller; the reserve has to clear the WORST section,
       not the one it was measured on. 21rem fitted every screen exactly but left
       the drawing reading small, so it sits at 19rem — a section that runs a
       little past the fold is the better trade when the drawing IS the section.

       max() keeps the plan usable on a short window rather than letting it
       collapse toward nothing. */
    --_h: max(22rem, calc(100vh - 19rem));
  }
  /* The PANEL STAYS 420px (the base grid), and the capped drawing centres in what
     is left. Sizing the drawing column by --_ar instead let the panel absorb the
     slack, which made the split depend on each plan's shape — Heartwood and Silver
     Oaks stopped looking like the same component. A fixed panel is worth more than
     the empty margins beside a portrait plan. */
  .siteplan__frame { width: min(100%, calc(var(--_h) * var(--_ar, 1.4))); }
  /* Panel chrome above the list is ~214px, so this keeps the panel level with the
     drawing instead of becoming the section's new tallest thing. */
  /* Panel chrome above the list — head, count, legend, chips — measures 214px. */
  .siteplan__list { max-height: min(620px, calc(var(--_h) - 14rem)); }
}

@media (max-width: 1000px) {
  .siteplan { grid-template-columns: minmax(0, 1fr); }
  .siteplan__panel { border-left: 0; border-top: 1px solid var(--hdc-line); }
  .siteplan__list { max-height: none; }
  /* Only the first six rows until asked. nth-child counts .siteplan-item wrappers. */
  .siteplan__more { display: block; }
}

/*
 * PHONE: the list becomes a horizontal rail of cards.
 *
 * Stacked under the drawing, twenty full-width rows push the plan off the top of
 * the screen the moment you start reading them — and the drawing is the point of
 * the section. A rail keeps both on one screen: swipe the homes, tap one, watch
 * the plan zoom to it. Tapping still EXPANDS in place rather than navigating, so
 * a card grows taller than its neighbours; that is fine on a rail, where cards do
 * not have to agree on height the way rows in a column do.
 *
 * Must match the 720px `rail` media query in siteplan.js, which turns the clamp
 * off here — every lot is one swipe away, so there is nothing to withhold.
 */
@media (max-width: 720px) {
  /* ⚠️ THE ZOOM RAIL COMES OFF THE DRAWING ON A PHONE. Floating over the plan it
     is 74x186px — on a 333px-wide frame that is a seventh of the picture, and it
     sat over lots 1-8, so eight parcels simply could not be tapped. Nothing said
     so: the tap landed on the panel and nothing happened, which reads as "lots
     are not clickable on mobile" rather than as an obstruction.

     22% of it was dead padding that blocked without being a button, so shrinking
     it would not have been enough either. It becomes a horizontal row UNDER the
     drawing instead, where it blocks nothing and the buttons get bigger. */
  /* ⚠️ THE PANE MUST STACK, or taking the rail out of position:absolute puts it
     INTO the flex row beside the drawing. .siteplan__pane is display:flex, so an
     absolute rail was out of flow; a static one becomes a flex ITEM and takes its
     share of the row — which collapsed the drawing to 75px of a 333px pane and
     left the plan a thumbnail in the corner. Column first, then the rail sits
     under the drawing as intended. */
  .siteplan__pane {
    flex-direction: column;
    align-items: stretch;
  }
  .siteplan__zoom {
    position: static;
    transform: none;
    flex-direction: row;
    justify-content: center;
    width: max-content;
    margin: var(--hdc-space-3) auto 0;
  }
  .siteplan__zoom button { width: 5rem; }

  .siteplan__list {
    display: flex;
    /* NO gap. The rows carry a hover and selected tint, and with a gap between
       them a selected card beside a hovered one showed a strip of panel between
       two fills — where the desktop list, being rows, has them touch. Cards are
       separated by a hairline instead, the same way .plan-card's mobile rail does
       it. */
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    /* NO padding at all. The rail's own inset was visible the moment a card was
       hovered: the tint stopped 16px short of the panel's left edge and 16px
       below its top, so the card read as a tile floating in a box rather than as
       the box's own content. The cards carry their padding themselves. */
    padding: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .siteplan__list::-webkit-scrollbar { display: none; }

  /* NO border and NO background. The outline drew a box tight around content that
     had no padding of its own, so the hover and selected tints ran to the very
     edge of the text — the card looked like a mistake rather than a card. The
     photo and the gaps separate them well enough without a line.
     align-self: stretch, so every card reaches the bottom of the rail instead of
     hanging from the top at whatever height its own text happens to be. */
  .siteplan-item {
    flex: 0 0 76%;
    max-width: 300px;
    scroll-snap-align: center;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    border: 0;
    border-right: 1px solid var(--hdc-line);
    overflow: hidden;
  }
  /* Nothing follows the last card, so a rule after it is a rule to nowhere. */
  .siteplan-item:last-child { border-right: 0; }

  /* The tint belongs to the whole CARD here, not to the row inside it. The row is
     one of two children (actions is the other), so tinting the row left an
     untinted strip under it wherever the actions were open, and the tint could
     never reach the card's full height. Moved up a level, hover fills the card
     top to bottom and edge to edge. */
  .siteplan-row:hover,
  .siteplan-row.is-on,
  .siteplan-row.is-selected,
  .siteplan-row.is-self,
  .siteplan-row.is-self:hover { background: transparent; }
  .siteplan-item:has(.siteplan-row:hover),
  .siteplan-item:has(.siteplan-row.is-on),
  .siteplan-item:has(.siteplan-row.is-selected),
  .siteplan-item:has(.siteplan-row.is-self) { background: var(--hdc-surface-tint); }

  /* Media over body, like .home-card — a 116px thumbnail beside text is a row
     shape, and a row inside a 76%-wide card leaves the name three words wide.
     flex: 1 so the tint fills the card it is in, rather than stopping under the
     last line of text and leaving a bare strip below it. */
  /* Padding on the CARD, not on the words — a full-bleed photo over inset text
     put two different left edges in a card 300px wide, and the eye reads that as
     the text being indented rather than the photo being wide. */
  .siteplan-row {
    grid-template-columns: 1fr;
    gap: var(--hdc-space-3);
    padding: var(--hdc-space-4);
    border-bottom: 0;
    flex: 1;
    align-content: start;
  }
  /* Inset now, so it needs a corner — a hard-edged rectangle floating inside a
     padded card reads as a placeholder. */
  .siteplan-row__media img,
  .siteplan-row__media .media-empty { border-radius: var(--hdc-radius-sm); }
  .siteplan-row__actions { padding: 0 var(--hdc-space-4) var(--hdc-space-4); }

  /* The rail is horizontal, so the clamp is off and its button has nothing to
     reveal. Belt and braces with the JS, which already keeps it hidden here. */
  .siteplan__more { display: none; }
}

.community-meta { font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-300); color: var(--hdc-color-muted); margin: 0; }


/* The printable plan's download, closing the section. Mirrors .area-foot on the
   community page — same offset, same role: a section-ending action, separated from
   the content above so it does not read as part of the inventory panel.
   Secondary on purpose: the interactive map above it is the live, authoritative
   view, and the PDF is a static export that can fall out of date. */
.siteplan-foot { margin-top: var(--hdc-space-7); }
