/* ==========================================================================
   Community single — page-specific styles only.

   Cards, section bands, section headers, buttons, status pills, spec lists and
   empty states all come from assets/components.css. This file is only the
   pieces unique to the community page: hero, breadcrumb, sub-nav, facts strip,
   overview + lead rail, site plan, plan explorer, location, visit, financing,
   FAQ and the closing CTA.

   Tokens only — no raw hex, no magic px. Built against wf-community.html.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1 · Hero  (the fixed transparent header overlays the top; content bottom-left)
   -------------------------------------------------------------------------- */
.community-hero {
  position: relative; display: flex; align-items: flex-end;
  /* Taller now that it carries motion — a video needs room to read as one.
     The vh figure still wins on short laptop screens; the px cap stops a tall
     desktop monitor from pushing the whole page below the fold. */
  min-height: var(--hdc-hero-h); overflow: hidden; background: var(--hdc-surface-ink);
}
.community-hero__media { position: absolute; inset: 0; }
.community-hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Sits over the photograph, revealed only once it is genuinely playing — a
   stalled load then shows the photo rather than a black rectangle. */
.community-hero__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  display: block; opacity: 0; transition: opacity var(--hdc-dur-slow) var(--hdc-ease);
}
.community-hero__video.is-on { opacity: 1; }
.community-hero__media--empty { aspect-ratio: auto; }
/* Same three-layer scrim as the homepage hero (.hero--bplus .hero__media::after):
   a TOP scrim so the transparent header's utilbar links stay legible over bright
   sky, the bottom lift for the title, and a left wash for the copy. Kept in sync
   with chrome.css (the old hero-transparent layer) — if that changes, this follows. */
.community-hero::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(to bottom, color-mix(in srgb, var(--hdc-surface-ink) 55%, transparent) 0%, transparent 22%),
    linear-gradient(to top, color-mix(in srgb, var(--hdc-surface-ink) 82%, transparent) 0%, color-mix(in srgb, var(--hdc-surface-ink) 34%, transparent) 38%, color-mix(in srgb, var(--hdc-surface-ink) 6%, transparent) 70%),
    linear-gradient(to right, color-mix(in srgb, var(--hdc-surface-ink) 34%, transparent), transparent 60%);
}
/* Title bottom-left — the homepage hero's .hero__bottom pattern, so the two
   heroes sit on the same grammar. The flex row went with the weather pill: one
   child left, so the block just sits at the bottom of the hero. */
.community-hero__inner {
  position: relative; z-index: 1; text-align: left;
  padding-block: var(--hdc-space-9) var(--hdc-space-8);
}
.community-hero__lead { min-width: 0; }
/* Full-strength white, NOT --hdc-color-on-dark-muted. That token is tuned for the
   flat ink band (#C9C4BB on #2A2825 ≈ 8.5:1, fine there) — over a photograph it
   has far less to push against and the eyebrow drops out. Same call already made
   for the utilbar links in chrome.css (the old hero-transparent layer). */
/* fs-300, not the fs-200 every other eyebrow uses. The 13px eyebrow token is
   sized against 32–44px section H2s; this one sits under a ~56px hero H1 and a
   six-word phrase, so it reads undersized at the shared value. A hero-local
   step up — the token itself stays 13px for the ~10 section eyebrows using it. */
/* Now .hero-eyebrow in components.css — the custom-collection hero wanted the same
   treatment, so the declarations moved there rather than being copied. This class
   is kept as the template's hook and carries nothing of its own. */
.community-hero h1 { color: var(--hdc-color-on-dark); font-size: var(--hdc-fs-850); margin: var(--hdc-space-3) 0 0; }
.community-hero__tagline { color: var(--hdc-color-on-dark); font-size: var(--hdc-fs-500); margin: var(--hdc-space-4) 0 0; max-width: 46ch; }

/* Every in-page target has to clear BOTH the fixed header and the sticky sub-nav
   that parks under it, or the jump lands with the heading hidden behind them. */
.community [id] { scroll-margin-top: var(--hdc-anchor-top); }

/* Sticky sub-nav → promoted to components.css as .subnav (the floor-plan single
   needs the same bar). Nothing community-specific remained. */

/* --------------------------------------------------------------------------
   Overview — facts strip + copy + lead rail
   -------------------------------------------------------------------------- */
/* Summary — a price line, then one icon spec row. Replaces the six-column facts
   strip: three of its six items had icons and three didn't, which read as broken.
   Now every fact in the row carries one, and price leads on its own line. */
/* Sits inside the left column, between the community wordmark and the copy, so
   the facts read as part of the story rather than a banner bolted above it.
   No rules — the spacing does the separating. */
.community-summary { margin: var(--hdc-space-6) 0 var(--hdc-space-7); }

/* Address, linked to directions. Sits under the specs as the last fact. */
.community-addr {
  margin: var(--hdc-space-5) 0 0;
  font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-500);
  color: var(--hdc-color-text);
}
.community-addr__lab { color: var(--hdc-color-heading); }
.community-addr a {
  color: var(--hdc-color-heading); text-decoration: underline; text-underline-offset: 2px;
}
.community-addr a:hover { color: var(--hdc-color-primary); }
/* Label and number share one line, one size, one font — the number takes the
   accent through color alone, not weight or scale. */
/* fs-550 → 20px, one step above the 18px spec row and address beneath it. */
.community-price {
  margin: 0; line-height: 1.2;
  font-family: var(--hdc-font-display); font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-550); color: var(--hdc-color-heading);
}
.community-price b { font-weight: inherit; color: inherit; }
.community-price.is-unset { color: var(--hdc-color-muted); }

.community-specs { margin-top: var(--hdc-space-5); font-size: var(--hdc-fs-500);
  gap: var(--hdc-space-3) var(--hdc-space-6); }
.community-specs .spec-ico { width: 19px; height: 19px; }

/* Rail at 380px left the paired name fields at 149px each, which read as broken.
   440px takes them to ~179px. The body column loses 60px it wasn't using — its
   copy is capped at 62ch regardless. */
.community-overview__grid { display: grid; grid-template-columns: 1.6fr 440px; gap: var(--hdc-space-9); align-items: start; }
/* Direct children ONLY. As a descendant selector this is (0,2,1) and it captured
   every <p> nested in the column — including the summary's price line and address,
   forcing both back to 18px and silently overriding their own sizes. It is meant
   for the community's prose, which sits directly in this column. */
/* Paragraph rhythm on the token scale — this was falling back to the browser's
   1em default (27px), the only spacing value in the section not from tokens. */
.community-overview__body > p { font-size: var(--hdc-fs-500); line-height: var(--hdc-lh-body);
  max-width: 62ch; margin: 0 0 var(--hdc-space-5); }
.community-overview__body > p:last-child { margin-bottom: 0; }
/* Community wordmark used as the About heading (an <img> inside the h2). */
.community-logo { margin: 0; line-height: 0; }
/* min() rather than a breakpoint: width:100% inside a ~350px mobile column meant
   the wordmark rendered at its full 340px cap and ate the screen. 60vw keeps it
   proportional on phones (≈234px at 390px) while desktop still hits the 340 cap. */
.community-logo img { max-width: min(340px, 60vw); width: 100%; height: auto; display: block; }

.community-rail { position: sticky; top: var(--hdc-sticky-top); display: flex; flex-direction: column; gap: var(--hdc-space-4); }

/* Lead-capture form → promoted to components.css (shared with the plan and
   move-in-ready singles). Nothing community-specific remains. */

/* --------------------------------------------------------------------------
   Promotion — a CARD, not a band
   -------------------------------------------------------------------------- */
/* Full CONTENT width, not full bleed: it lines up with every other section's
   container rather than running edge to edge, so it reads as a card between two
   sections instead of a page-wide band that outranks the community. */
.promo-wrap { padding-block: 0; }
.promo { position: relative; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--hdc-space-4); text-align: center;
  /* Generous top padding predates the icon it used to clear, and is kept: the
     card reads as cramped without it, and the ribbon needs room in the corner. */
  padding: var(--hdc-space-9) var(--hdc-space-7) var(--hdc-space-8); border-radius: var(--hdc-radius-md);
  background: var(--hdc-surface-deep); color: var(--hdc-color-on-dark);
  /* clips the ribbon to the rounded corner */ overflow: hidden; }
/* The kicker as a roof-pitched tab, centerd on the card's axis — one device where
   there were previously two (a corner ribbon saying the same thing, plus this).

   Title case at fs-600 in the display face, NOT small tracked caps: this is part
   of the offer, not an eyebrow labeling it.

   The pitch is SHALLOW. A 45° notch reads as a downward arrow rather than a roof,
   which is what the first attempt got wrong. Bottom padding is set so the text
   clears the pitch that eats the lower 30% of the box, and the card's own top
   padding (space-9) clears the tab. */
.promo__label { position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  padding: var(--hdc-space-3) var(--hdc-space-6) var(--hdc-space-6);
  background: var(--hdc-color-primary-pale); color: var(--hdc-surface-deep);
  font-family: var(--hdc-font-display); font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-600); letter-spacing: normal; text-transform: none;
  line-height: var(--hdc-lh-snug); white-space: nowrap;
  clip-path: polygon(0 0, 100% 0, 100% 70%, 50% 100%, 0 70%); }
/* On a narrow screen a nowrap tab would overflow the card, so let it shrink. */
@media (max-width: 560px) {
  .promo__label { max-width: calc(100% - var(--hdc-space-6)); white-space: normal;
    padding-inline: var(--hdc-space-5); font-size: var(--hdc-fs-500); }
}
/* The offer is the point of the card, so it outranks everything on it.
   lh-snug not lh-tight: 1.1 is fine for a single line but cramps badly once the
   figure wraps, which is exactly what it does on a phone. */
.promo__head { margin: 0; font-family: var(--hdc-font-display); font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-800); line-height: var(--hdc-lh-snug); color: inherit; text-align: center;
  text-wrap: balance; }
.promo__star { font-size: 0.6em; vertical-align: super; opacity: .8; }
/* Fine print sits at the FOOT of the page, not in the card — the disclaimer runs
   longer than the offer it qualifies and would swamp it. */
.disclaimer { padding-block: var(--hdc-space-7) var(--hdc-space-8); }
/* Mark to the LEFT of the fine print, the pair centerd on the page together. The
   mark is BLACK artwork on a light section, so it needs no filter here — the
   footer inverts its copy because that footer is dark. */
.disclaimer .container { display: flex; align-items: center;
  justify-content: center; gap: var(--hdc-space-5); }
.disclaimer__eho { flex: none; margin: 0; width: auto; height: var(--hdc-space-7); }
/* margin: 0, NOT margin-inline:auto. Auto margins on a flex item absorb all the
   free space, which shoved the mark out to the container edge and left a gap the
   width of the section. justify-content on the row does the centering now. */
.disclaimer__t { margin: 0; max-width: 78ch;
  text-align: center; font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200); line-height: var(--hdc-lh-body); color: var(--hdc-color-muted); }

/* Site plan → extracted to assets/siteplan.css, because the floor-plan single
   renders the same partial (filtered to one plan) and this sheet does not load
   there. */

/* --------------------------------------------------------------------------
   Floor-plan explorer
   -------------------------------------------------------------------------- */
/* space-7 (48px), not space-6. The gap above these chips is 48px, so at 32px the
   TIGHTEST gap in the stack sat exactly where the biggest change happens —
   controls handing off to content. Rhythm should not compress downward. */
/* Chip, .plan-tabs/.plan-tab, .plan-drawing, .plan-video*, .plan-panel__media
   → promoted to components.css, so the plan and move-in-ready singles compose the
   same viewer instead of each growing a private copy. What stays here is only
   this page's COMPOSITION of them. */

/* The drawing takes the room; the model list rides beside it.
   Text chips reading "Model A" / "Model B" used to sit above all of this. They
   said nothing about the houses, so choosing meant clicking each in turn and
   holding three sets of numbers in your head — and with every chip identical the
   whole section read as line drawings and grey text, which is what the client
   objected to. The cards put a façade, a price and the specs on every option at
   once. */
/* One container, like the reference. The explorer used to bleed straight into
   the page, so its two halves — a drawing and a list — read as unrelated things
   that happened to be adjacent. A single surface says they are one instrument. */
.plan-explorer {
  /* One height for the whole instrument. Declared here because custom properties
     inherit downward only — set on .plan-panel__media it was invisible to the
     views column and the card rail, which are its siblings, so their min-height
     and max-height silently resolved to nothing. */
  --plan-view-h: clamp(26rem, 58vh, 40rem);
  background: var(--hdc-surface);
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-md);
  /* NO PADDING, and overflow hidden — exactly how .siteplan builds its panel.
     Padding here would float the model list inside the box with a margin on
     three sides; the list is supposed to BE one side of the box. Children carry
     their own padding instead, and the radius clips whatever reaches a corner. */
  overflow: hidden;
}

.plan-explorer__stage {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: 0;
  align-items: stretch;
}
/* All the container's padding lives here now, so the list beside it can run to
   the edges. */
.plan-explorer__views {
  padding: clamp(var(--hdc-space-4), 2vw, var(--hdc-space-6));
}
.plan-panel { display: block; }
.plan-panel[hidden] { display: none; }

/* Capped and scrollable so a five-plan community does not push the drawing off
   the screen. Heartwood has five; Silver Oaks has three and never scrolls. */
/* Rows, not a stack of little cards. This is the SAME list the site plan viewer
   already uses — .siteplan-row — because it is the same object: a list of homes
   you pick one from, next to a picture of the one you picked. Three different
   answers to that on three pages is what "inconsistent" means, so this borrows
   the answer that exists.

   Divided by a hairline rather than boxed individually: nine rounded cards in a
   column is nine borders competing with the container that already holds them. */
.plan-explorer__models {
  /* THE DRAWING SETS THE HEIGHT, NOT THE LIST. height:0 with min-height:100% is
     the trick that matters here: in a stretch grid a tall list would grow the
     ROW, so five plans at Heartwood would have stretched the box and left a
     void beside the façade. At height:0 the list contributes nothing to the
     row, then fills whatever the drawing column decided — and scrolls when its
     own content is longer. Three plans never scroll; five do. */
  height: 0;
  min-height: 100%;
  overflow-y: auto;
  border-left: 1px solid var(--hdc-line);
  /* TOP-ALIGNED. A three-plan community leaves the column taller than its cards,
     and centring them was tried first — it made the empty space read as margin
     rather than as a hole, but it also floated the list away from the plan title
     and tabs it belongs beside. A list starts at the top. The space below it is
     the price of a drawing that can use its height, and it is the cheaper cost. */
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.plan-card {
  text-decoration: none;
  color: inherit;
  display: grid;
  /* 150px, not 116. Three plans beside a ~570px drawing left 142px of nothing
     underneath, and the render is the best asset on the card — so the space goes
     into the picture rather than into padding. A FIXED size, deliberately: an
     earlier attempt let the cards stretch to fill, which closed the gap but made
     them resize every time you switched tabs, and a list that changes shape
     under you is worse than a gap at the bottom of it. */
  grid-template-columns: 165px 1fr;
  gap: var(--hdc-space-4);
  align-items: center;
  width: 100%;
  padding: var(--hdc-space-5);
  text-align: left;
  border: 0;
  border-bottom: 1px solid var(--hdc-line);
  cursor: pointer;
  /* --_bg/--_fg, never background/color: GeneratePress paints content buttons on
     :hover and :focus at (0,1,1) and beats a single-class rule. theme.css routes
     every content button through these props for that reason. */
  --_bg: transparent;
  --_fg: var(--hdc-color-text);
  background: var(--_bg);
  color: var(--_fg);
}
/* The last card KEEPS its rule. Dropping it was right when the list always filled
   the column and the container's own border finished the stack — but the list is
   top-aligned now, so on the floor-plan tab a three-plan community ends in open
   surface and the final card had no bottom edge at all: it read as unfinished
   rather than as the last one. At full height the rule lands on the container's
   border and simply is not seen. */
/* Pressed state everywhere; hover and selection only where selection MEANS
   something. Above 1000px the cards drive the panel beside them, so .is-on marks
   the plan you are looking at. Below it they are a scrolling rail of LINKS to the
   plan pages — nothing is open, so a permanently tinted first card read as stuck
   rather than as chosen, which is exactly how it looked on a phone. */
.plan-card:active { --_bg: var(--hdc-surface-tint); }

@media (min-width: 1001px) {
  .plan-card:hover,
  .plan-card.is-on { --_bg: var(--hdc-surface-tint); }
}

.plan-card__art {
  display: block;
  border-radius: var(--hdc-radius-sm);
  overflow: hidden;
  background: var(--hdc-surface-tint);
  line-height: 0;
}
.plan-card__art img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.plan-card__art .media-empty { width: 100%; aspect-ratio: 4 / 3; }

.plan-card__body { display: block; min-width: 0; }
.plan-card__cta { display: none; }
/* The plan NAME is the headline here, not the price: on the site plan you are
   scanning addresses and prices, here you are choosing between three houses by
   name. Same face, same size as that list's lead. */
.plan-card__name {
  display: block;
  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;
  margin: 2px 0 4px;
}
/* The shared .home-card__price, only smaller. Display face and heading colour,
   because that is what a price looks like on the search page, on the homepage's
   featured homes and in the site plan panel. It was blue UI type here and blue
   nowhere else. It sits UNDER the name rather than over it: on the site plan the
   price is the headline because you are scanning lots, here you are choosing
   between three named houses. Same treatment, different rank. */
.plan-card .home-card__price {
  display: block;
  font-size: var(--hdc-fs-500);
  margin: 2px 0 0;
}
/* Icons off, middot on — identical to the site plan panel. At this width the
   icons repeat what the words already say and cost a line. */
.plan-card .spec-list {
  margin-top: 6px;
  font-size: var(--hdc-fs-300);
  gap: 2px var(--hdc-space-1);
}
.plan-card .spec-ico { display: none; }
.plan-card .spec-list li { gap: var(--hdc-space-1); white-space: nowrap; }
.plan-card .spec-list li:not(:last-child)::after {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: var(--hdc-radius-pill);
  background: var(--hdc-line-strong);
  vertical-align: middle;
}

/* THE SAME CONTROL AS THE FLOOR PILLS IN THE FLOOR PLAN VIEWER, on purpose.
   "Which one of these am I looking at" is one question, and the site should not
   answer it two different ways on two pages — a visitor who has used the viewer
   on the model page already knows what this is. It floats over the image rather
   than sitting above it, which also gives the row of space back to the picture.

   Mirrors .fp__floors in floorplan-viewer.css; if that changes, change both. */
.plan-subtabs {
  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);
}
.plan-subtab {
  /* --_bg/--_fg, never background/color directly: GeneratePress paints content
     buttons on :hover and :focus at (0,1,1) and would beat a single-class rule.
     theme.css routes every content button through these props for that reason. */
  --_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);
  cursor: pointer;
  /* A pill that wraps stops being a pill. "Six-unit building" broke over two
     lines as soon as the media column narrowed. */
  white-space: nowrap;
}
.plan-subtab:hover { --_fg: var(--hdc-color-primary); }
.plan-subtab.is-on {
  --_bg: var(--hdc-color-primary);
  --_fg: var(--hdc-color-on-dark);
  font-weight: var(--hdc-fw-semi);
}

/* Mirrors .fp__title in floorplan-viewer.css — eyebrow over name — because it is
   the same statement in the same situation: this is the thing you are looking at.
   Without it, choosing a card swapped the picture and nothing named the result;
   on two townhome façades that differ by two units, that reads as nothing having
   happened. */
.plan-panel__title { margin-bottom: var(--hdc-space-4); }
.plan-panel__title span {
  display: block;
  font-family: var(--hdc-font-ui);
  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);
}
.plan-panel__title b {
  display: block;
  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.15;
}

/* Every view is the same height, and that height leaves the buttons on screen.
   The drawing scales down inside the frame rather than the frame growing. */
/* No min-height here on purpose. Reserving the drawing's height would leave
   ~200px of visible emptiness under the façade — which is the view almost
   everyone sees — to avoid one reflow for the few who switch tabs. The gap is
   paid on every visit; the reflow is paid on a click that was already a
   deliberate change of view. */
.plan-panel__media .plan-drawing { position: relative; }
.plan-panel__media .plan-drawing .elev[hidden],
.plan-panel__media .plan-drawing .plan-figure[hidden] { display: none; }
/* ⚠️ ANY display: rule on a pane beats the UA's [hidden] { display: none },
   which is only (0,1,0). A `display:flex` added at the mobile breakpoint left
   the Exteriors pane on screen under whichever view you switched to — click
   Video tour and you got the video AND the façade below it. This guard is
   (0,2,1) so it outranks the component's own selectors, and it is why the rule
   exists rather than being fixed at the one call site. Same trap is documented
   in siteplan.css. */
.plan-panel__media .plan-drawing[hidden] { display: none; }
.plan-panel__media .plan-drawing figure { margin: 0; }

/* ONE FRAME, NOT THREE. There was the page, then the explorer container, then a
   matte around the picture, then the picture — four nested rectangles for one
   photograph, which reads as packaging rather than presentation.

   A photograph has its own edges and needs no matte, so the façade fills the
   container it is already inside, and crops to the frame rather than
   letterboxing — these renders are wide, and fitting one by its height wastes
   the column. Only the DRAWINGS keep a matte: a line drawing has no edge of its
   own and would float. */
/* The tour is a picture too — same bleed, same frame. It was sitting at its own
   16:9 inside the pane while the façade filled the box, so switching to it
   looked like the viewer had shrunk. */
.plan-panel__media .plan-drawing--elev,
.plan-panel__media .plan-drawing[data-plan-pane="video"] {
  border-radius: var(--hdc-radius-md);
  overflow: hidden;
  background: var(--hdc-surface-tint);
}
.plan-panel__media .plan-drawing[data-plan-pane="video"] .plan-video {
  border-radius: 0;
  background: none;
}
/* Matches the façade's shape rather than keeping a 16:9 of its own, so the pane
   is the same size whichever of the two you are looking at. */
.plan-panel__media .plan-drawing[data-plan-pane="video"] .plan-video img {
  aspect-ratio: 1024 / 467;
}
/* Full width at the render's OWN aspect. Forcing these into a tall frame with
   object-fit:cover zoomed a 2.2:1 building until its ends were outside the box —
   a crop that hides the very thing the picture is of. */
.plan-panel__media .plan-drawing--elev img {
  width: 100%; height: auto; max-height: none; border-radius: 0; object-fit: contain;
}
.plan-panel__media .plan-drawing[data-plan-pane="video"] img { border-radius: 0; }

.plan-panel__media .plan-drawing:not([data-plan-pane="video"]):not(.plan-drawing--elev) {
  background: var(--hdc-surface-paper);
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-md);
  padding: clamp(var(--hdc-space-4), 2vw, var(--hdc-space-5));
}
/* ⚠️ EXCLUDES THE VIDEO FACADE. This is for PLAN DRAWINGS — line art that must
   not be cropped — and the video pane is neither --elev nor --floors, so it was
   being caught too. The poster is 864x369 (2.341) in a box forced to 1024/467
   (2.193): object-fit:contain letterboxed it, leaving 10px of pane background
   above and below. That bottom bar is the "white space" under the picture, and
   the top bar is why the corner looked sharp — the ROUNDING was on the bar and
   the photo kept its own square corners inside it. A poster is a photograph and
   should fill its frame; the 6% crop is not visible. */
.plan-panel__media .plan-drawing:not(.plan-drawing--elev):not(.plan-drawing--floors):not([data-plan-pane="video"]) img {
  max-height: var(--plan-view-h);
  width: auto;
  margin-inline: auto;
  object-fit: contain;
  border-radius: 0;
}
/* Floors sit in a row, sharing the width. auto-fit rather than a fixed count so
   a one-floor plan gets the whole frame instead of half of it and a gap. */
.plan-panel__media .plan-drawing--floors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: clamp(var(--hdc-space-4), 2vw, var(--hdc-space-6));
  align-items: end;
}
.plan-drawing--floors .plan-figure { margin: 0; min-width: 0; }
/* Sized to the height the box ALREADY has, which is the correction to an earlier
   call. The old cap (28vh, 252px at a 900px window) was set when the model list
   was a stack of small cards and a taller drawing left "430px of nothing" beside
   it. That list is now full rows — photo, plan name, price — and needs 588px at
   Silver Oaks and 863px at Heartwood, so it, not the drawing, sets the box: the
   drawing column was carrying ~250px of unused height on this tab.

   The cost of the old cap was worse than idle space. These images are
   width: 100% / height: auto / object-fit: contain, so a max-height shorter than
   the natural height does not crop — it LETTERBOXES. A 0.42 townhome strip in a
   317x252 box rendered about 106px wide: the drawing was not merely short, it was
   a sixth of the column it sat in.

   56vh lands the pane at ~587px against a 588px box at 1440x900 — the drawing
   takes the room the list was already claiming, and the box does not grow. */
.plan-panel__media .plan-drawing--floors img {
  width: 100%; height: auto; object-fit: contain;
  max-height: clamp(22rem, 56vh, 40rem);
}
.plan-drawing--floors figcaption {
  margin-top: var(--hdc-space-3);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-200);
  color: var(--hdc-color-muted);
  text-align: center;
}

/* Room under a DRAWING for the floating pills. Over a façade they sit on the
   picture, which is what floating was meant to be. */
.plan-panel__media .plan-drawing:not(.plan-drawing--elev):has(.plan-subtabs) {
  padding-bottom: 4.5rem;
}
.plan-panel__actions { display: flex; flex-wrap: wrap; gap: var(--hdc-space-4); margin-top: var(--hdc-space-5); }

/* --------------------------------------------------------------------------
   Location
   -------------------------------------------------------------------------- */
/* ONE carousel, then the places. A row in `area_places` becomes a slide only
   when it has a photo, so a community that gains a pool gains a slide and
   nothing else changes. */
.area-car { position: relative; }
/* overscroll-behavior-x: contain — a swipe that reaches the end of the track stops
   there instead of handing the scroll to the page. Without it the whole document
   slides sideways (and on iOS triggers the back-navigation gesture), which reads
   as the carousel overscrolling even though its snap geometry is correct. */
/* PROXIMITY, not mandatory — this is the fix for "it overscrolls then jumps back".
   The snap geometry is correct (measured at 390px: points at 0/285/571/782, both
   ends reachable), so the jump was never a misplaced snap position. It is what
   `mandatory` is defined to do: a flick that ends BETWEEN two points must be
   corrected onto one, and that correction is visible as a bounce-back. Proximity
   snaps when the gesture already ends near a point and leaves it alone otherwise.
   The arrows are unaffected — they animate scrollLeft directly with snap lifted.

   overscroll-behavior-x: none, not contain. `contain` stops the scroll chaining to
   the page but still permits the element's own rubber-band at its boundaries,
   which is the same bounce by another route. */
.area-car__track { display: flex; gap: var(--hdc-space-5); overflow-x: auto;
  overscroll-behavior-x: none;
  scroll-snap-type: x proximity; scrollbar-width: none; padding-bottom: var(--hdc-space-2); }
.area-car__track::-webkit-scrollbar { display: none; }
/* NO surface, border or radius on these, deliberately — they are a photo with a
   caption under it, not a card. Given the site's card treatment they grew a
   near-white block behind the caption, which on the tint section reads as a
   pasted-on label rather than as part of the page. The colour difference in this
   section comes from the SECTION (see #area, the page's only section--tint), not
   from the slides. */
.area-car__slide { flex: 0 0 calc((100% - var(--hdc-space-5) * 2) / 3); margin: 0; scroll-snap-align: start; }
/* The LAST slide snaps to the end, not the start, or the track's end is not a snap
   point at all: with start-alignment the final snap position sits further right
   than max scrollLeft, so `mandatory` drags the track back the moment a touch
   scroll releases at the end. That is the jump — the flick reaches the end and
   then rubber-bands to the previous slide. It also left the next arrow permanently
   enabled, because scrollLeft could never reach scrollWidth - clientWidth. */
.area-car__slide:last-child { scroll-snap-align: end; }
.area-car__slide img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  border-radius: var(--hdc-radius-md); margin-bottom: var(--hdc-space-4); background: var(--hdc-surface-tint); }
.area-car__cat { display: block; font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-100);
  letter-spacing: var(--hdc-tracking-ui); text-transform: uppercase; font-weight: var(--hdc-fw-semi);
  color: var(--hdc-color-primary); margin-bottom: 4px; }
.area-car__slide 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); margin-bottom: 4px; }
.area-car__meta { display: block; font-size: var(--hdc-fs-300); color: var(--hdc-color-muted); }
.area-car__nav { display: flex; align-items: center; justify-content: space-between; margin-top: var(--hdc-space-5); }

/* The section's closing CTA. It had NO rule at all, so it sat hard against the
   carousel nav above it — measured 0px — and read as part of the controls rather
   than as the end of the section. space-7 separates the two without floating it
   off on its own. */
.area-foot { margin-top: var(--hdc-space-7); }
/* Desktop shows only Directions — the map is already beside the list — so it
   sits at its natural width rather than being stretched across the row. It keeps
   the ghost treatment there too: on a wide screen it is the only action, and a
   lone primary button at the end of a reading section overstates it. */
.area-foot .area-map__open { display: none; }
.area-car__dots { display: flex; gap: var(--hdc-space-2); }
/* The active dot stretches 7px -> 22px and changes color; without a transition
   that is a hard flip on every step. Radius eases too so it reads as one shape
   growing rather than a dot swapping for a bar. */
/* Matches .hdc-dot (components.css) — the site's one carousel dot. These are <i>
   elements written by JS rather than buttons, so the class cannot simply be swapped
   on; the geometry is mirrored here instead and the stretch-to-bar treatment this
   used to have is gone. Give them the class if that markup is ever touched. */
.area-car__dots i { width: 8px; height: 8px; border-radius: 50%;
  background: var(--hdc-line-strong); display: block;
  transition: background var(--hdc-dur) var(--hdc-ease), transform var(--hdc-dur) var(--hdc-ease); }
.area-car__dots i.is-on { background: var(--hdc-color-primary); transform: scale(1.35); }
.area-car__arrows { display: flex; gap: var(--hdc-space-3); }
/* Paint runs through --_bg/--_fg; theme.css defends every content button from
   GeneratePress in one place, so no per-state pinning is needed here. */
.area-car__arrow {
  --_bg: var(--hdc-surface); --_fg: var(--hdc-color-heading);
  width: 40px; height: 40px; border-radius: var(--hdc-radius-pill);
  border: 1px solid var(--hdc-line-strong); cursor: pointer; font-size: var(--hdc-fs-400); line-height: 1; padding: 0; }
.area-car__arrow:hover { border-color: var(--hdc-color-primary); --_fg: var(--hdc-color-primary); }
.area-car__arrow[disabled] { opacity: .3; cursor: default; }

/* Two INDEPENDENT stacks, split in PHP — not a grid and not CSS columns. A grid
   ties the two sides to a shared row height so an open group strands the one
   beside it; CSS columns reflow groups between columns as you open them. Two
   plain stacks let each side behave on its own. */
/* min(320px, 100%) inside the minmax, NOT a bare 320px. A bare minimum is a hard
   floor: on a 320px phone the content box is ~280px, the track still demanded 320,
   and the grid pushed 36px past the viewport — giving the whole page a sideways
   scroll. min() lets the track collapse to the container when the container is the
   smaller of the two, which is the only case where the floor was wrong. */
/* The list and the map, side by side. The map is the wider half because it is
   the thing you read spatially; the list is scannable at any width. */
.area-grid {
  margin-top: var(--hdc-space-8);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: var(--hdc-space-7);
  align-items: start;
}
/* A community with no coordinates has no map, so the list takes the width back
   rather than sitting in half a page beside nothing. */
.area-grid--nomap { grid-template-columns: 1fr; }
.area-grid--nomap .places__col { columns: 2; column-gap: var(--hdc-space-9); }
.area-grid--nomap .places__grp { break-inside: avoid; }

.places { margin-top: 0; }

/* Sticky so the map stays beside the list while you read down it — the two are
   one instrument, and scrolling the names away from the pins breaks that. */
.area-map {
  position: sticky;
  top: var(--hdc-sticky-top);
  border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-md);
  overflow: hidden;
  background: var(--hdc-surface-warm);
}
.area-map__canvas {
  /* Tall enough to read a neighbourhood, capped so it never owns the screen. */
  height: clamp(24rem, 56vh, 34rem);
  width: 100%;
  background: var(--hdc-surface-warm);
}
/* The swatch on a group heading. A teardrop, not a dot — the same shape as the
   pins it stands for, drawn with a clip-path so it takes its colour from a
   custom property and needs no image per category. This replaced a legend under
   the map: a key there made you learn the colours away from the names, and it
   described all seven categories when the map only ever shows the open ones. */
.places__swatch {
  /* 42px — the size the map draws a HOVERED place pin at. The legend is the one
     place the icon has to be readable rather than merely present: it is where you
     learn what each pin means, and every category label beside it is a display-face
     line, so a small mark read as a bullet point instead of a key. Square, because
     the artwork is drawn on a square box with the teardrop inside it; the old
     swatch was a 10x14 CSS teardrop, so the box changed shape when the art moved
     in. */
  width: 42px;
  height: 42px;
  flex: 0 0 auto;
  display: block;
}
/* ⚠️ THE COMMUNITY SWATCH IS BIGGER TO COMPENSATE FOR ITS HALO. bin/add-pin-halo.py
   scales that pin to 0.88 inside its box to make room for the white outline — the
   box cannot grow, because area-map.js anchors every marker at the bottom-centre of
   it. On the MAP that is invisible: the community pin renders at 58px against the
   places' 34px. In this rail every swatch is the same 42px box, so the inset made
   the subject look smaller than its siblings. 42 / 0.88 puts the artwork back to
   the same drawn size as the rest. */
.places__grp[data-cat="community"] .places__swatch { width: 48px; height: 48px; }

/* The old tinted teardrop, kept for a category with no drawn pin installed —
   better a coloured mark than a gap where the legend key should be. */
.places__swatch--flat {
  width: 10px;
  height: 14px;
  background: var(--_c, var(--hdc-color-primary));
  clip-path: polygon(50% 100%, 0 45%, 0 0, 100% 0, 100% 45%);
  border-radius: 50% 50% 50% 50% / 42% 42% 58% 58%;
}

.places__head {
  margin: var(--hdc-space-8) 0 var(--hdc-space-1);
  font-family: var(--hdc-font-display);
  font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-600);
  color: var(--hdc-color-heading);
}
.places__lede--narrow { display: none; }
.places__lede {
  margin: 0;
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-300);
  color: var(--hdc-color-muted);
}
.area-grid { margin-top: var(--hdc-space-5); }

/* ⚠️ GeneratePress puts its own padding and font on every content <button>, and
   a fixed-size button with content-box sizing then measures bigger than it was
   asked to be — which is why the close X sat off-centre in a circle that was not
   the size it looked. floorplan-viewer.css carries the identical reset as
   `.fp button` for the identical reason; this is the area map's copy of it.
   .btn is untouched: it wants its padding. */
.area-map button {
  box-sizing: border-box;
  font-family: var(--hdc-font-ui);
}
.area-map__close { padding: 0; }

/* Beside the list the map needs no header, no chips and no way in — it is
   already there. All three belong to the phone's full-screen state. */
.area-map__hd,
.area-map__chips,
.area-map__open { display: none; }

/* The consent gate — shown only when area-map.js finds the map is not permitted.
   Sits in the canvas's place, so the section keeps its shape rather than
   collapsing to nothing. */
.area-map__gate {
  position: absolute; inset: 0;
  display: grid; place-content: center; justify-items: center;
  gap: var(--hdc-space-4);
  padding: var(--hdc-space-6);
  text-align: center;
  /* Backdrop and scrim come from .mapgate in components.css, shared with the
     Find Your Home gate. */
  border-radius: var(--hdc-radius-md);
}
/* ⚠️ The same trap siteplan.css documents four times over: this rule carries
   `display: grid`, and ANY author `display` beats the browser's own
   [hidden] { display: none }. Without this the gate stayed on top of the map at
   full size after being "hidden" — pressing Explore the map loaded Google
   perfectly well and the visitor saw no change, because the placeholder was
   still covering it. find-your-home.css has carried the equivalent rule for its
   own gate all along; this one was missing it. */
.area-map__gate[hidden] { display: none; }

.area-map__gate-note {
  margin: 0; max-width: 34ch;
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-300);
  color: var(--hdc-color-muted);
}
/* The canvas is empty while gated, so it must not hold height against the gate. */
.area-map.is-gated .area-map__canvas,
.is-gated .area-map__canvas { visibility: hidden; }

.area-map__note {
  margin: 0;
  padding: var(--hdc-space-3) var(--hdc-space-4);
  border-top: 1px solid var(--hdc-line);
  font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-100);
}
/* Google's own info window is a white box with a shadow; this is the content
   inside it, kept in the site's voice rather than left at browser defaults. */
.area-map__pop { font-family: var(--hdc-font-ui); padding: 2px 4px 4px; }
.area-map__pop b {
  display: block;
  font-family: var(--hdc-font-display);
  font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-400);
  color: var(--hdc-color-heading);
}
.area-map__pop span {
  display: block;
  margin-top: 2px;
  font-size: var(--hdc-fs-200);
  color: var(--hdc-color-muted);
}

/* A row that drives the map looks like it does. The rest stay text — most rows
   have no coordinates yet, and a control that does nothing is worse than none. */
.places__row--pin { cursor: pointer; }
.places__row--pin:hover b,
.places__row--pin.is-hot b { color: var(--hdc-color-primary); }
.places__row--pin.is-hot { background: var(--hdc-surface-tint); }
.places__row--pin:focus-visible { outline: 2px solid var(--hdc-color-primary); outline-offset: 2px; }
/* Rules live on the GROUP's top edge, never on the column. A border-top on the
   column plus a border-bottom on the last group met when the two columns stacked
   on mobile and painted a 2px double line at the seam. Top-edge-only means one
   rule between any two adjacent groups at any width; only the very last group in
   the last column closes the list. */
.places__col { }
.places__grp { border-top: 1px solid var(--hdc-line); }
/* The community's own key. Same row as a category, minus the disclosure — there
   is nothing to open, so it takes no arrow and no pointer.
   ⚠️ (0,2,0) on the arrow reset: .places__cat::after is declared LATER in this
   file, and at equal specificity the later rule wins — the chevron came back. */
.places__grp--fixed .places__cat { cursor: default; margin: 0; }
.places__grp--fixed .places__cat::after { content: none; }
.places__grp--fixed:hover > .places__cat { color: inherit; }
/* summary, not a button — native disclosure gives keyboard + AT support free,
   and collapsed rows STAY in the DOM so the place names remain crawlable. */
/* NOT an eyebrow. Eyebrow styling (uppercase, tracked, 11px) is for the small
   label ABOVE a section title — used here it made a control look like a caption
   and gave the category less weight than the place names under it. Display face,
   sentence case, sized above the rows so the hierarchy runs the right way. */
.places__cat { cursor: pointer; list-style: none; display: flex; align-items: center;
  gap: var(--hdc-space-3); padding: var(--hdc-space-4) 0; margin: 0;
  font-family: var(--hdc-font-display); font-weight: var(--hdc-fw-regular);
  font-size: var(--hdc-fs-500); letter-spacing: normal; text-transform: none;
  color: var(--hdc-color-heading); }
.places__cat::-webkit-details-marker { display: none; }
/* Chevron drawn from a border so it needs no icon file and inherits color. */
.places__cat::after { content: ""; width: 7px; height: 7px; margin-left: auto;
  border-right: 1.5px solid var(--hdc-color-muted); border-bottom: 1.5px solid var(--hdc-color-muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--hdc-dur) var(--hdc-ease); }
.places__grp[open] > .places__cat::after { transform: rotate(-135deg) translate(-2px, -2px); }
.places__grp:hover > .places__cat { color: var(--hdc-color-primary); }
.places__count { font-family: var(--hdc-font-ui); font-size: var(--hdc-fs-300);
  font-weight: var(--hdc-fw-regular); color: var(--hdc-color-muted); }
/* Stacked, not name-left/distance-right: the meta line carries type AND
   distance, which is too much to hang off the right edge of a narrow column. */
.places__row { padding: var(--hdc-space-3) 0; border-top: 1px solid var(--hdc-line); }
.places__row:first-of-type { border-top: 0; }
.places__row b { display: block; font-family: var(--hdc-font-ui); font-weight: var(--hdc-fw-medium);
  font-size: var(--hdc-fs-400); color: var(--hdc-color-heading); }
.places__row span { display: block; margin-top: 2px; font-family: var(--hdc-font-ui);
  font-size: var(--hdc-fs-300); color: var(--hdc-color-muted); }
/* On the inner child so it collapses with the group, not on [open] where it
   would snap shut after the animation. */
.places__body > .acc-inner { padding-bottom: var(--hdc-space-4); }
.places__col:last-child .places__grp:last-child { border-bottom: 1px solid var(--hdc-line); }

/* --------------------------------------------------------------------------
   Design features — a summary, not the sheet.

   Picture left, words right. It replaced a stack of accordions holding all
   forty-eight specifications, which was the source document pasted onto a page.
   The list that survives is six items, so it is read rather than skimmed past,
   and the full sheet leaves as a PDF.
   -------------------------------------------------------------------------- */
/* The .dfeat layout (grid, media, highlight list, lede, features-sheet button) now
   lives in components.css — the model single's #design-options section composes from
   the same component, so it was promoted out of this community-only sheet. */

/* --------------------------------------------------------------------------
   Visit — a REQUEST, not a booking. A CTA into the visit page leads, facts beside.
   -------------------------------------------------------------------------- */
/* .visit / .visit__* moved to components.css — the model and move-in-ready
   singles render the same two-column visit block now, and this sheet does not
   load on those pages. */

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1000px) {
  .community-overview__grid { grid-template-columns: 1fr; gap: var(--hdc-space-7); }
  .community-rail { position: static; }

  /* THE MAP IS BEHIND A BUTTON, NOT BESIDE THE LIST.
     390px cannot hold a control and a usable map at once. Inline, the map was
     either too small to read or so tall the list fell off the screen, and making
     it sticky to solve that put a pinned panel under the header on every scroll.
     Full screen is where both finally fit — and it is the pattern the floor plan
     viewer already uses on a phone, so it is a move visitors have met. */
  .area-grid { display: block; }
  .area-map-stick { display: contents; }
  .area-map { display: none; }
  /* Two equal columns, map first and filled. This section is about where things
     are, so the map is what it is offering; Directions is a departure from the
     page and takes the ghost. */
  .area-foot {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--hdc-space-3);
  }
  .area-foot .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--hdc-space-2);
    width: 100%;
    padding-inline: var(--hdc-space-3);
    text-align: center;
  }
  .area-foot .area-map__open { display: inline-flex; }
  .area-map__open svg { width: 1rem; height: 1rem; flex: 0 0 auto; }

  .is-map-open .area-map {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0;
    z-index: 9999;
    border: 0;
    border-radius: 0;
    background: var(--hdc-surface);
  }
  .area-map__hd {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--hdc-space-4);
    /* max(), so the inset only ever ADDS room. Note this is currently inert:
       env(safe-area-inset-*) resolves to 0 without viewport-fit=cover in the
       viewport meta, and this site does not set it — which means iOS insets the
       whole viewport itself and a fixed panel never reaches the camera. Written
       correctly anyway so that if cover is ever switched on, this panel is
       already right rather than newly broken. */
    padding: max(var(--hdc-space-3), env(safe-area-inset-top))
             max(var(--hdc-space-4), env(safe-area-inset-right))
             var(--hdc-space-3)
             max(var(--hdc-space-4), env(safe-area-inset-left));
    background: var(--hdc-surface-warm);
    border-bottom: 1px solid var(--hdc-line);
  }
  .area-map__hd span {
    display: block;
    font-family: var(--hdc-font-ui);
    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);
  }
  .area-map__hd b {
    display: block;
    font-family: var(--hdc-font-display);
    font-weight: var(--hdc-fw-regular);
    font-size: var(--hdc-fs-550);
    color: var(--hdc-color-heading);
  }
  /* Carries .hdc-close (components.css); the hairline is this surface's own —
     it sits on a photograph and needs the edge. */
  .area-map__close {
    --_bg: var(--hdc-surface);
    --_fg: var(--hdc-color-heading);
    border: 1px solid var(--hdc-line-strong);
  }

  /* One row, scrolled sideways. An "All" chip so the default is everything
     rather than whichever category happened to be first. */
  /* NO pin on the accordion rows here. The map is display:none until the takeover
     opens, so a 42px legend key on this list refers to nothing on screen — and the
     takeover's own chips already carry the same six pins, which is where the map
     actually is. Scoped to .places so the chips keep theirs. */
  .places .places__swatch { display: none; }

  /* 20px, not the legend's 42. A chip is a FILTER, not a key — the map is right
     beside it with the real pins on it — and at 42 each chip stood 60px tall and
     the row took 100px off an 800px screen, which is the map's height being spent
     on its own legend. The icon survives the shrink; the colour alone would not,
     since the palette is four blues and two greys. */
  .area-map__chips .places__swatch { width: 20px; height: 20px; }

  .area-map__chips {
    display: flex;
    gap: var(--hdc-space-2);
    overflow-x: auto;
    /* One line that scrolls sideways, rather than wrapping to two. */
    flex-wrap: nowrap;
    /* Tighter block padding once the chips got short: the row's own padding was
       most of what was left of its height, and every pixel here is map. */
    padding: var(--hdc-space-2) var(--hdc-space-4);
    background: var(--hdc-surface-warm);
    border-bottom: 1px solid var(--hdc-line);
  }
  .area-map__chip {
    --_bg: var(--hdc-surface);
    --_fg: var(--hdc-color-text);
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: var(--hdc-space-2);
    padding: var(--hdc-space-2) var(--hdc-space-3);
    border: 1px solid var(--hdc-line-strong);
    border-radius: var(--hdc-radius-pill);
    background: var(--_bg);
    color: var(--_fg);
    font-family: var(--hdc-font-ui);
    font-size: var(--hdc-fs-200);
    white-space: nowrap;
    cursor: pointer;
  }
  .area-map__chip.is-on {
    --_bg: var(--hdc-color-primary);
    --_fg: var(--hdc-color-on-dark);
    border-color: var(--hdc-color-primary);
    font-weight: var(--hdc-fw-semi);
  }
  /* The swatch is redundant once the chip itself is the category colour. */
  .area-map__chip.is-on .places__swatch { display: none; }

  .is-map-open .area-map__canvas { flex: 1 1 auto; height: auto; min-height: 0; }
  .is-map-open .area-map { padding-bottom: env(safe-area-inset-bottom); }
  .is-map-open .area-map__note { display: none; }

  .area-grid--nomap .places__col { columns: 1; }
  .places__head { margin-top: var(--hdc-space-7); }
  /* Opening a group is not a map control here — the map is behind a button. */
  .places__lede--wide { display: none; }
  .places__lede--narrow { display: block; }
  /* ON A PHONE THIS IS A LIST, NOT A VIEWER.
     The viewer stacked four control strips — view tabs, façade pills, two
     buttons and the scroller — around a picture that ended up 245px tall. Every
     strip was defensible; together they left no room for the thing they
     controlled. So the drawing column goes away entirely and the cards do the
     one job the section has on a phone: help you pick a plan to open.

     Nothing is lost, because each plan HAS a page and that page is where it is
     studied. The cards are real links; plan-viewer.js only intercepts them when
     a panel is on screen to switch, which here there is not. */
  .plan-explorer__views { display: none; }
  .plan-explorer { border: 0; border-radius: 0; background: none; overflow: visible; }
  .plan-explorer__stage { display: block; }

  .plan-explorer__models {
    display: flex;
    /* ⚠️ ROW, restated. The desktop rule turned this into a flex COLUMN so a short
       model list could be aligned within a tall box; here the same element is the
       horizontal card rail, and inheriting `column` stacked the cards vertically
       inside a container that only scrolls sideways — the rail stopped working
       entirely. A base rule that sets flex-direction has to be answered wherever
       the same element flexes the other way. */
    flex-direction: row;
    height: auto; min-height: 0;
    border: 1px solid var(--hdc-line);
    border-radius: var(--hdc-radius-md);
    background: var(--hdc-surface);
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
  }
  .plan-card {
    grid-template-columns: 1fr;
    width: var(--hdc-rail-card-w);
    flex: 0 0 auto;
    align-items: start;
    gap: var(--hdc-space-3);
    padding: var(--hdc-space-4);
    border-bottom: 0;
    border-right: 1px solid var(--hdc-line);
    scroll-snap-align: start;
  }
  .plan-card:last-child { border-right: 0; }
  .plan-card__art img { aspect-ratio: 4 / 3; }
  .plan-card__name { font-size: var(--hdc-fs-500); }
  .plan-card .home-card__price { font-size: var(--hdc-fs-400); }

  /* The card navigates here, so it says so — the same underlined text link
     .home-card uses on the search page rather than a button, because a button
     inside a card that is already a link is a second target for one destination
     and the card is the bigger one. */
  .plan-card__cta {
    display: block;
    margin-top: var(--hdc-space-3);
    font-family: var(--hdc-font-ui);
    font-size: var(--hdc-fs-300);
    font-weight: var(--hdc-fw-semi);
    color: var(--hdc-color-primary);
    text-decoration: underline;
    text-underline-offset: 3px;
  }
  .plan-card__cta svg { vertical-align: -2px; }
  /* Unpin. Stacked, the details column sits ABOVE the drawing, so pinning it
     holds the specs on screen while the plan scrolls away underneath — the exact
     inverse of what the pin is for. */
  /* Icon stays corner-pinned on a phone too — it just moves in and shrinks so the
     first line of text still clears its bottom edge. */
  /* Top padding has to clear the corner disc: at this width the text runs the
     full card and was rendering UNDERNEATH the icon. 24px offset + 48px disc =
     72px, so the text starts at 96px. */
  .promo { padding: var(--hdc-space-9) var(--hdc-space-6) var(--hdc-space-7); }
  .promo__ico { top: var(--hdc-space-5); left: var(--hdc-space-5);
    width: var(--hdc-space-7); height: var(--hdc-space-7); }
  .promo__ico svg { width: 26px; height: 26px; }
  .area-car__slide { flex-basis: 78%; }
  /* UNIFORM SNAP. Fourth look at this, and the first three all tuned the same
     mechanism (mandatory → proximity, start → end alignment, contain → none).
     Turning snap off entirely did stop the bounce, but a free track has nothing to
     settle into and just feels loose.

     The actual defect was never snap — it was the LAST STEP. Slides align `start`
     but the final one aligned `end`, so the closing step was 211px against a 285px
     stride: every swipe to the end felt unlike every other swipe. End-aligning was
     itself a workaround, because with start-alignment the last slide's snap
     position sits past max scrollLeft and the track gets dragged back.

     Trailing padding removes the reason for the workaround. With
     (track − slide) of empty space after the last slide, it CAN start-align, so every
     point is one uniform stride apart and the final one is genuinely reachable.
     Nothing sits beyond the end, so there is nothing to correct back to. */
  /* MANDATORY here, and it is safe now in a way it was not before.
     Mandatory was rejected earlier because it bounced back — but that bounce had
     one specific cause: the last slide's snap position sat PAST max scrollLeft, so
     releasing at the end left mandatory with a point it could not reach and it
     dragged the track backwards. The spacer below removes that; every point is now
     reachable and one uniform stride apart, so mandatory has nothing to correct to.

     Proximity is the wrong choice on touch anyway: a drag that ends between two
     points is only corrected if it happens to stop NEAR one, so the track parks
     mid-slide. On a real phone a flick usually carries near a point and hides it;
     with no momentum — a trackpad, or Chrome device mode, which is where this was
     reported — it happens constantly. Mandatory always lands on a slide. */
  /* NO scroll-behavior: smooth — the arrow code animates scrollLeft by hand
     precisely because native smooth does not land on this element, and mandatory
     snap cancels it outright. Adding it back would fight the glide. */
  .area-car__track { scroll-snap-type: x mandatory; }
  .area-car__slide, .area-car__slide:last-child { scroll-snap-align: start; }
  /* A SPACER, not padding-inline-end. Percentage padding on this flex container is
     circular: it shrinks the content box that the slides' own 78% flex-basis
     resolves against, so the slides would quietly narrow. A flex pseudo-item adds
     the same trailing room without touching the content box.
     Width is (100% − 78%) minus one gap, which lands max scrollLeft exactly on the
     last slide's start position — reachable, with no dead space past it. */
  .area-car__track::after { content: ""; flex: 0 0 calc(22% - var(--hdc-space-5)); }
}
/* Sub-nav mobile behavior and the fixed bottom action bar → promoted to
   components.css with .subnav. They are the same component's small-screen half,
   including the reduced-motion rule. */

/* --------------------------------------------------------------------------
   Available homes — QMI grid
   -------------------------------------------------------------------------- */
/* No drop shadow on this page. .home-card is ONE component shared with the
   homepage featured grid (featured-homes.php), and that page is approved — so
   this is scoped here rather than pulled out of components.css. The border and
   the hover lift still carry the card. */
.community .home-card { box-shadow: none; }

/* --------------------------------------------------------------------------
   Schedule-a-visit page form slot (single-hdc_community-visit.php). The day-chip
   row .visit-days is shared with the model/QMI CTA, so it lives in components.css.
   -------------------------------------------------------------------------- */
/* The form panel — the SAME treatment as .ct-formwrap (/contact/) and .lead-form
   (community, plan and listing pages): surface, one hairline, radius-md, space-6.
   Deliberately not a new look; this page was the odd one out, sitting bare on the
   warm band with no panel at all.

   Centred, because the section head above it is centred — the form used to be
   620px hard against the left container edge under a centred heading. */
/* The column is centred; everything inside it stays left-aligned. */
.visit-col { max-width: 680px; margin-inline: auto; }

.visit-panel { margin-top: var(--hdc-space-7);
  background: var(--hdc-surface); border: 1px solid var(--hdc-line);
  border-radius: var(--hdc-radius-md); padding: var(--hdc-space-6); }

/* Copied from .hdc-form's field labels rather than invented, so the day picker
   reads as the form's first field and not as a heading above it. */
.visit-panel__k { display: block; font-size: var(--hdc-fs-300);
  font-weight: var(--hdc-fw-medium); color: var(--hdc-color-heading);
  margin: 0 0 var(--hdc-space-2); line-height: var(--hdc-lh-snug); }

/* .visit-days is SHARED with the model/QMI visit CTA and carries a space-6 top
   margin for that context. Inside the panel it follows its own label, so the gap
   belongs to the label (space-2), not to the row. Scoped, so the shared component
   is untouched. */
.visit-panel .visit-days { margin-top: 0; }

/* The picker now sits inside the panel, so its spacing is a field gap, matching
   .hdc-form .ff-el-group. */
.visit-form { margin-top: var(--hdc-space-6); }
.visit-form__todo { padding: var(--hdc-space-5); border: 1px dashed var(--hdc-line-strong);
  border-radius: var(--hdc-radius-md); color: var(--hdc-color-muted); }
.visit-form__todo code { font-family: var(--hdc-font-mono, monospace); font-style: normal;
  background: var(--hdc-surface); padding: 1px 5px; border-radius: var(--hdc-radius-xs); }

/* Form chrome is now .hdc-form in components.css — the wrapper carries both
   classes. Only the radio-to-pill treatment below is specific to this form. */

/* Radio → segmented pills (the retired .seg look). The input stays for keyboard/AT. */
.visit-form .ff-el-input--content:has(> .ff-el-form-check) { display: flex; flex-wrap: wrap; gap: var(--hdc-space-3); }
.visit-form .ff-el-form-check { margin: 0; }
.visit-form .ff-el-form-check-label {
  display: inline-flex; align-items: center; cursor: pointer; margin: 0;
  padding: var(--hdc-space-3) var(--hdc-space-4);
  border: 1px solid var(--hdc-line-strong); border-radius: var(--hdc-radius-sm);
  background: var(--hdc-surface); color: var(--hdc-color-heading);
  font-size: var(--hdc-fs-400); line-height: 1;
  transition: border-color var(--hdc-dur) var(--hdc-ease), background var(--hdc-dur) var(--hdc-ease), color var(--hdc-dur) var(--hdc-ease); }
.visit-form .ff-el-form-check-input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
.visit-form .ff-el-form-check:hover .ff-el-form-check-label { border-color: var(--hdc-color-primary); color: var(--hdc-color-primary); }
.visit-form .ff_item_selected .ff-el-form-check-label {
  background: var(--hdc-color-primary-wash); border-color: var(--hdc-color-primary);
  color: var(--hdc-color-primary); font-weight: var(--hdc-fw-medium); }
.visit-form .ff-el-form-check-input:focus-visible + span {
  outline: 2px solid var(--hdc-color-primary); outline-offset: 3px; border-radius: var(--hdc-radius-xs); }

/* Submit button → our button look. Color via --_bg/--_fg (theme.css) so hover holds. */
/* "More dates" chip — a .chip whose visible face is a <span>; a transparent native
   date input covers it, so a click opens the OS calendar (JS also calls showPicker). */
.visit-day-more { position: relative; overflow: hidden; }
.visit-day-more__input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer;
  font: inherit; color: transparent; background: transparent; }

/* Held while the area map is full screen, so the page behind it cannot scroll
   under the fixed panel. Matches the floor plan viewer's lock. */
body.hdc-map-locked { overflow: hidden; }
