/* ==========================================================================
   Home Dynamics — Design Tokens (canonical)
   Single source of truth for every visual value. No raw hex or magic px
   anywhere else — components reference these vars only. (Rebuild rule #1.)
   Brand blue #0079C1 extracted from the logo SVG (HomeDynamics_Logo.svg).
   ========================================================================== */

/* --------------------------------------------------------------------------
   Brand webfonts — SELF-HOSTED variable WOFF2 (assets/fonts/).

   Self-hosted, not fonts.googleapis.com: no third-party font call, so no visitor
   IP or UA reaches Google before consent (CIPA — see rebuild-plan.md §10).

   ⚠️ SUBSET TO LATIN, and this is where the weight was. The previous
   literata-variable.woff2 was the ALL-SUBSETS build — cyrillic, cyrillic-ext,
   greek, greek-ext, vietnamese, latin-ext and latin in one 383KB file, preloaded
   on every page of a US English site. The latin subset is 107KB. Figtree went
   27KB -> 19KB the same way.

   latin-ext is declared SEPARATELY with its own unicode-range, so the browser
   fetches those 87KB/9KB only if a page actually contains a character that needs
   them. An English page never pays for it; a name like "Łukasz" still renders in
   the right face instead of falling back mid-word.

   ⚠️ These are the VARIABLE files (Literata 200-900 with opsz 7-72, Figtree
   300-900), not static instances. The design pass chose Literata precisely
   because it is variable where Prata had one weight — grabbing a static cut here
   would silently undo that. If these are ever re-fetched, use the css2 API with a
   RANGE in the query (…wght@200..900), which is what makes Google serve the
   variable file.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Literata";
  src: url("fonts/literata-latin.woff2") format("woff2");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;   /* latin */
}
@font-face {
  font-family: "Literata";
  src: url("fonts/literata-latin-ext.woff2") format("woff2");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;   /* latin-ext */
}
@font-face {
  font-family: "Figtree";
  src: url("fonts/figtree-latin.woff2") format("woff2");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;   /* latin */
}
@font-face {
  font-family: "Figtree";
  src: url("fonts/figtree-latin-ext.woff2") format("woff2");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;   /* latin-ext */
}
@font-face {
  font-family: "Figtree";
  src: url("fonts/figtree-italic-latin.woff2") format("woff2");
  font-weight: 300 900;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;   /* latin */
}
@font-face {
  font-family: "Figtree";
  src: url("fonts/figtree-italic-latin-ext.woff2") format("woff2");
  font-weight: 300 900;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;   /* latin-ext */
}

:root {
  /* ---- Color: brand ---- */
  --hdc-color-primary:       #0079C1; /* bright logo blue — buttons, links, accents */
  --hdc-color-primary-deep:  #1F5FAC; /* deeper blue — headings emphasis, hover */

  /* Derived from primary — never hand-picked. These replaced --hdc-color-accent-soft,
     which was a hardcoded #ACCBE7 duplicating primary-line almost exactly. */
  --hdc-color-primary-wash:  color-mix(in srgb, var(--hdc-color-primary) 12%, white); /* chip/badge fill  ≈ #E0EFF8 */
  --hdc-color-primary-line:  color-mix(in srgb, var(--hdc-color-primary) 30%, white); /* chip/badge border ≈ #B2D7EC */
  --hdc-color-primary-pale:  color-mix(in srgb, var(--hdc-color-primary) 45%, white); /* pale fill ON dark surfaces — promo ribbon ≈ #8CC3E3, 8.4:1 on --hdc-surface-deep */

  /* ---- Color: status / feedback ---- */
  --hdc-color-success:       #1A7A3F; /* "now open" / available — darkened from #23A455 to clear WCAG AA (4.5:1) on every warm surface; the old value was 2.7–3.1 and is used for 10px status labels */
  --hdc-color-selling:       #975E13; /* "now selling" — warm amber, sparing. Darkened from #C77D1A for the same reason */

  /* ---- Color: text ---- */
  /* #2A2825 is declared ONCE, on --hdc-surface-ink below. color-heading aliases it so
     the two can diverge later (soften headings without touching dark bands) by simply
     giving this its own literal. Two names, one value, on purpose — not a duplicate. */
  --hdc-color-heading:       var(--hdc-surface-ink);
  --hdc-color-text:          #57544F; /* body — warm grey */
  --hdc-color-muted:         #736F68; /* captions, meta — warm, kept dark for legibility */
  /* #FDFCFA declared ONCE, on --hdc-surface below. Same reasoning as color-heading. */
  --hdc-color-on-dark:       var(--hdc-surface);
  --hdc-color-on-dark-muted: #C9C4BB; /* muted text on dark — warm light grey (was a cool blue #DCE6F0) */

  /* ---- Color: surfaces ---- */
  --hdc-surface:             #FDFCFA; /* default card/raised surface — soft near-white, NOT stark #fff */
  --hdc-surface-warm:        #F7F5F1; /* base section bg (warm off-white) */
  /* True white, and the ONE place the system wants it. Plan drawings are line
     art on paper: the warm off-white above tints the paper and the drawing
     stops reading as a drawing. Not a general-purpose white — if a card or a
     panel needs a light surface it takes --hdc-surface, which is what that
     token's own comment is warning about. */
  --hdc-surface-paper:       #FFFFFF;

  /* ---- Color: the area map ----
     Google Maps takes literal colour strings and cannot read var(), so
     area-map.js resolves these to literals once at load — the same trick
     fyh-map.js already uses for its pins. They live HERE rather than as eleven
     hexes buried in a script, so the map's palette is editable with the rest of
     the system and cannot quietly drift away from it.

     Warm neutrals on purpose: the map should sit in the page rather than punch a
     bright rectangle through it. Three of these are just the site's own surfaces;
     the rest are map-only shades with no counterpart elsewhere, which is why they
     are named for what they colour rather than for a place in a scale. */
  --hdc-map-land:            var(--hdc-surface-warm);  /* base geometry */
  --hdc-map-road:            var(--hdc-surface);       /* road fill, and label halo */
  --hdc-map-built:           #F2EFEA;  /* man-made landscape */
  --hdc-map-park:            #E7EDE4;  /* parkland — the one green */
  --hdc-map-highway:         #EDE9E2;
  --hdc-map-highway-edge:    #E0DBD2;
  --hdc-map-border:          #DED9D0;  /* administrative boundaries */
  --hdc-map-water:           #DCE7EF;
  --hdc-map-label:           #8C877F;  /* place labels */
  --hdc-map-label-road:      #A9A49B;  /* road numbers — a step back from place labels */
  --hdc-map-label-water:     #9FB4C4;
  --hdc-surface-tint:        #EDF2F7; /* the ONE alternating-section background. Absorbed --hdc-surface-blue (rendered nowhere) and --hdc-surface-warm-deep (only rendered behind an opaque photo) */
  --hdc-surface-ink:         #2A2825; /* deep band bg — the only dark surface. Absorbed --hdc-surface-charcoal, which held the same value for the same job */
  /* Deep indigo. The ONLY hue in the system not derived from the logo blue, and
     chosen deliberately: HSL 220° / 50% / 20.5%. Everything derived from #0079C1
     darkened to a navy stayed visibly cyan-leaning; this is far enough round to
     read as its own color without going violet.

     Judged on the page, never in a swatch. Two effects push it violet: the warm
     off-white surround (#F7F5F1), and AREA — 232° looked right on the promo card
     alone and read purple again once the FAQ panel doubled how much of it is on
     screen. More of a hue reads as more of that hue. Landed at 220° with both
     blocks in place. 238° and 232° were both too purple in situ. Ceiling is ~256°, where the pale ribbon stops matching and a SECOND
     new token becomes necessary. */
  --hdc-surface-deep:        #1A2A4E;

  /* Site-plan lot statuses. Move-in ready reuses the brand primary.
     The rest were originally all muted so a map of 38 parcels read as a plan and
     not a color chart — right instinct, wrong distribution: SALIENCE WAS
     INVERTED. Sold carried a strong dark green and the model a red, while
     "available" — the only status anyone is shopping for — was #878F6F, a drab
     olive on a green aerial plate, so it camouflaged against the lawn.
     Now the spend goes where the intent is: available is a true green, sold
     recedes to neutral because it is context rather than inventory, and the model
     home softens to a muted red. Judged on the plate, not in a swatch. */
  /* Area-map place categories. One pin colour per category, warm-leaning so a
     map of seven of them still reads as this site rather than as a chart. Kept
     here rather than in the map's JS because the LEGEND uses the same values —
     hdc_place_colors() in inc/community-data.php mirrors this list, and that
     function is what feeds both the swatches and the pins. */
  /* ⚠️ THESE FOLLOW THE ARTWORK. The designer coloured the drawn pin set herself
     and kept it inside the brand; these are read off those files by
     bin/install-pins.py, and hdc_place_colors() in inc/community-data.php carries
     the same values for PHP. A legend swatch that disagrees with its pin is worse
     than no legend, so all three move together or none of them do. */
  --hdc-place-community: #0079C1; /* the community itself — the emblem pin */
  --hdc-place-commute:   #1A2A4E; /* roads and airports — deep navy */
  --hdc-place-education: #47A1D9; /* schools — mid blue */
  --hdc-place-shopping:  #8CC3E3; /* shops and restaurants — light blue */
  --hdc-place-health:    #6D6863; /* health and services — warm grey */
  --hdc-place-outdoors:  #484541; /* parks — dark warm grey */
  --hdc-place-fun:       #1A3F88; /* theme parks and entertainment — indigo */
  /* The fallback pin, for a place whose category is empty or since-renamed.
     hdc_place_colors() has always returned this value; it was the one of the
     eight with no token to match, so anything styling it in CSS had nothing to
     reach for. */
  --hdc-place-days_out:  #8C877F;

  --hdc-siteplan-available: #63B04A; /* a fairway green — brighter/cleaner than the darker map grass, reads as "go" without the old green vanishing or an alarm color */
  /* Under contract — warmer and lighter than sold, so the two read as different
     states at a glance rather than as one grey. */
  --hdc-siteplan-pending:   #A98E62;
  --hdc-siteplan-sold:      #7E7C77;
  --hdc-siteplan-model:     #A3443D; /* brick red. Was #8C5A5A, a dusty red that read brown beside sold grey; client asked for red (10/2). Kept dark for the white lot numbers */
  --hdc-siteplan-soon:      #6098A5;

  /* ---- Color: lines ---- */
  --hdc-line:                #E7E4DF; /* hairline on warm surfaces */
  --hdc-line-strong:         #D3CEC6;

  /* ---- Typography ---- */
  --hdc-font-body:    "Figtree", system-ui, sans-serif;
  --hdc-font-ui:      "Figtree", system-ui, sans-serif;
  /* Literata, replacing Prata. Prata shipped ONE weight and no italic, which is
     why heading hierarchy had only size and color to work with; Literata is
     variable 200–900 with true italics. It also survives at text sizes, where
     Prata (a Didone) did not — 11 of Prata's 18 uses were below 24px and bought
     nothing. Serif fallbacks, not sans: a sans fallback reflows completely. */
  --hdc-font-display: Literata, Georgia, "Times New Roman", serif;

  /* Type scale — base 16px, Major Third (1.25). Heading steps are fluid
     (mobile → desktop) so they shrink on small screens automatically. */
  --hdc-fs-050: 0.625rem;  /* 10 — micro status pill (was raw 0.62rem in two badges) */
  --hdc-fs-100: 0.75rem;   /* 12 — legal / micro */
  --hdc-fs-200: 0.8125rem; /* 13 — eyebrow (caps) */
  --hdc-fs-300: 0.9375rem; /* 15 — meta / specs / captions */
  --hdc-fs-400: 1rem;      /* 16 — body / UI / buttons */
  /* The body pair — fluid is fine, but the FLOORS must not collide.
     fs-500 was clamp(1rem …), i.e. a floor identical to fs-400's fixed 1rem, so
     on mobile both resolved to 16px and the two stopped being different things.
     Floor raised to 1.0625rem so the step survives at every width.
       fs-400 = long / detail text        16
       fs-500 = important body text       17 → 18  */
  --hdc-fs-500: clamp(1.0625rem, 1rem + 0.3vw, 1.125rem);   /* 17 → 18 — important body */
  --hdc-fs-550: clamp(1.125rem, 1.05rem + 0.35vw, 1.25rem); /* 18 → 20 — quiet stat lead */
  --hdc-fs-600: clamp(1.375rem, 1.2rem + 0.6vw, 1.5rem);    /* 22 → 24 — small heading */
  --hdc-fs-700: clamp(1.625rem, 1.4rem + 1vw, 2rem);        /* 26 → 32 — H3 / card name */
  --hdc-fs-800: clamp(2rem, 1.5rem + 2.2vw, 2.75rem);       /* 32 → 44 — H2 / section */
  --hdc-fs-850: clamp(2.25rem, 1.6rem + 2.4vw, 3.5rem); /* hero h1 — ~3.5rem desktop */
  --hdc-fs-900: clamp(2.75rem, 1.6rem + 4.6vw, 4.5rem); /* hero display */

  --hdc-lh-tight: 1.1;
  --hdc-lh-snug:  1.25;
  --hdc-lh-body:  1.65;

  --hdc-fw-regular: 400;
  --hdc-fw-medium:  500;
  --hdc-fw-semi:    600;

  --hdc-tracking-eyebrow: 0.14em;
  --hdc-tracking-ui:      0.12em; /* nav, buttons, labels — uppercase */
  --hdc-tracking-wide:    0.22em; /* fine hairline labels / wordmark feel */

  /* ---- Spacing scale (4-based) ---- */
  --hdc-space-1: 0.25rem; /* 4  */
  --hdc-space-2: 0.5rem;  /* 8  */
  --hdc-space-3: 0.75rem; /* 12 */
  --hdc-space-4: 1rem;    /* 16 */
  --hdc-space-5: 1.5rem;  /* 24 */
  --hdc-space-6: 2rem;    /* 32 */
  --hdc-space-7: 3rem;    /* 48 */
  --hdc-space-8: 4rem;    /* 64 */
  --hdc-space-9: 6rem;    /* 96 — section rhythm */

  /* ---- Radii ---- */
  /* Softened one step. 8px on a ~300px panel is 2.7% of its height, against 13.6%
     for 6px on a 44px button — same scale serving elements that differ tenfold in
     size, which is why the containers read sharp next to the controls. */
  --hdc-radius-xs: 4px;   /* tight status pills only (was raw 4px in two badges) */
  --hdc-radius-sm: 8px;   /* buttons, inputs */
  --hdc-radius-md: 12px;  /* cards, photos, media, tags */
  --hdc-radius-lg: 16px;  /* large panels — search dropdown, mega-menu */
  --hdc-radius-pill: 999px;

  /* ---- Breakpoints ----
     Published so SCRIPTS stop hand-copying the numbers. Six matchMedia() calls
     carried four different literals, and the only thing holding them to the
     stylesheets was a comment saying "must match the 720px breakpoint in
     siteplan.css" — which is documentation, not enforcement.

     ⚠️ THE STYLESHEETS STILL WRITE THE LITERALS, and must. A custom property
     cannot be used in a media query condition: `@media (max-width: var(--x))` is
     invalid and silently never matches, which fails in the worst possible way.
     So this is a one-way single source — CSS states the number, these tokens
     republish it, and the scripts read it here instead of guessing.

     They are named for the JOB, not for a size, because "mobile" already meant
     three different widths in this codebase. */
  --hdc-bp-phone:   720px;   /* siteplan rail; hero video gives up */
  --hdc-bp-results: 760px;   /* Find Your Home list/map swap */
  --hdc-bp-nav:     60rem;   /* the header's phone menu — rem on purpose, so it
                                follows the reader's text size */
  --hdc-bp-map:     1000px;  /* area map + site plan go narrow */

  /* ---- Shadows ---- */
  /* The close mark as a CSS mask, for the one control we cannot render in PHP:
     the Complianz banner's close (see assets/chrome.css). Must stay identical to
     hdc_close_icon() in inc/chrome.php, which is the producer for every other × on
     the site. Stroke is white because a mask reads alpha, not colour — the element's
     background-color supplies the real one. */
  --hdc-close-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E");

  --hdc-shadow-1: 0 1px 2px rgba(42, 40, 37, 0.06), 0 1px 3px rgba(42, 40, 37, 0.08);
  --hdc-shadow-2: 0 6px 20px rgba(42, 40, 37, 0.10);
  --hdc-shadow-3: 0 18px 48px rgba(42, 40, 37, 0.18);
  /* For large SURFACES rather than cards. A shadow's read depends on its spread
     relative to the element, the same way radius does: shadow-1 and -2 are tuned
     for ~300px cards and collapse to a smudge under a 1200px panel. Wider blur,
     lower opacity — it lifts the panel without implying it is a control. */
  --hdc-shadow-panel: 0 8px 28px rgba(42, 40, 37, 0.055);

  /* ---- Layout ---- */
  --hdc-header-h: 108px; /* solid site-header height — for sticky offsets under the fixed header */
  /* The sticky sub-nav parks UNDER the fixed header, so page chrome is the two
     stacked. Every pin was offsetting against the header alone and sliding behind
     the bar; the two lead rails were worse, using a raw 6rem (96px) that pinned
     behind the HEADER. One value, derived, so a change to either bar moves every
     pinned element with it. */
  --hdc-subnav-h: 80px;
  --hdc-sticky-top: calc(var(--hdc-header-h) + var(--hdc-subnav-h) + var(--hdc-space-5));
  /* Jump targets need the same clearance plus a little air, or a heading lands
     flush against the bar it just scrolled under. */
  --hdc-anchor-top: calc(var(--hdc-header-h) + var(--hdc-subnav-h) + var(--hdc-space-7));
  /* Photo-hero height. One value for every page in the photo-hero family
     (community, floor plan), so they open at the same scale — the plan page was
     declaring its own shorter min(62vh, 560px) and read as a lesser page. The vh
     figure wins on short laptop screens; the px cap stops a tall monitor pushing
     the whole page below the fold. */
  --hdc-hero-h: min(80vh, 720px);
  /* One card in a sideways phone rail — the floor plan cards on a community page
     and the move-in-ready homes lists. The same width in both, so the two rails a
     visitor scrolls on one page read as one control. */
  --hdc-rail-card-w: 13.5rem;
  /* Overridden for phones at the foot of this file — see the media query there.
     80vh is a desktop figure: it assumes the hero is the only thing competing for
     the fold. On a phone the header is ~144px tall above it and 80vh of what is
     left is the entire screen. */
  /* THE content measure. One value, used everywhere, so every section aligns to
     the same grid. Was 1560 via a raw literal in home-v2.css while this token sat
     unused. --hdc-container-wide (1440) and --hdc-container-max (1560) are gone —
     with everything on one measure they had no referents left. */
  --hdc-container: 1320px;
  --hdc-gutter: clamp(1.25rem, 0.5rem + 3vw, 3rem);

  /* SAFE AREA. Live only because the viewport meta carries viewport-fit=cover
     (see hdc_meta_viewport() in functions.php) — without it every one of these
     resolves to 0 and the insets below are inert. With cover on, the document
     runs edge to edge UNDER the notch and the home indicator, so anything pinned
     to a viewport edge has to inset itself or it is partly unreadable and partly
     untappable on a modern phone.

     Tokens rather than bare env(), so the fallback is declared once: the second
     argument matters on browsers that support env() but expose no inset, where a
     bare env(safe-area-inset-top) with no fallback is an invalid value that voids
     the whole declaration. */
  --hdc-safe-t: env(safe-area-inset-top, 0px);
  --hdc-safe-r: env(safe-area-inset-right, 0px);
  --hdc-safe-b: env(safe-area-inset-bottom, 0px);
  --hdc-safe-l: env(safe-area-inset-left, 0px);
  /* The distance from a viewport edge to the container's content edge. What a
     full-bleed band pads by on one side so that side lines up with the container
     while the band itself still runs edge to edge. Collapses to the gutter once
     the viewport is narrower than the measure. */
  --hdc-container-inset: max(var(--hdc-gutter), calc((100vw - var(--hdc-container)) / 2 + var(--hdc-gutter)));

  /* ---- Motion ----
     Three durations, chosen by WHAT IS MOVING rather than by taste:

       fast   a pointer is waiting on it. Hover and press feedback must land
              inside the time someone perceives as "instant"; at --hdc-dur a
              link colour lags visibly behind the cursor.
       base   the element itself is moving or resizing and the eye should be
              able to follow it — panels, drawers, accordions.
       slow   something is crossing over something else, or arriving from
              offscreen. At --hdc-dur a hero video snapping in over its own
              poster reads as a flicker.

     One easing for all three. A single decelerating curve is what makes motion
     from different components read as one system; a second curve is a decision
     that has to be justified per use, and none here needs it. */
  --hdc-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --hdc-dur-fast: 0.18s;
  --hdc-dur: 0.4s;
  --hdc-dur-slow: 0.7s;

  /* How far a revealing element travels. Deliberately small — this is a rise,
     not an entrance, and anything past ~16px starts to read as a slide. */
  --hdc-rise: 14px;
}

/* ==========================================================================
   Responsive token overrides

   A token that changes with the viewport belongs HERE, redefined once, not
   overridden per component. --hdc-hero-h is read by .community-hero and
   .model-hero — the community, floor-plan and move-in-ready singles — and a
   per-sheet override would have to be written three times and kept in step.
   ========================================================================== */
@media (max-width: 760px) {
  :root {
    /*
     * Measured, not guessed. At 430×900 the original min(80vh, 720px) resolved to
     * 720, and on the listing single the header sits ABOVE the hero rather than
     * over it — so the hero ran 144→864 of a 900px screen and the first real
     * content started at 976, below the fold. The page opened as a photograph and
     * nothing else, with no signal there was anything under it.
     *
     * The rule this satisfies: the hero must end high enough that the visitor can
     * see there is a page beneath it. Worst case is the listing single, where the
     * header is stacked above rather than overlaid.
     *
     * NOTE ON THE CAP — it is not decoration. The previous pair was
     * min(56vh, 420px), and on every current phone 56vh exceeded 420, so the CAP
     * was the value and the vh never applied: 420px flat on a 932pt screen and on
     * an 844. Raising the vh alone changes nothing; the cap has to move with it.
     *
     * At 65vh / 560px the hero resolves to 560 / 549 / 434 on a 16 Pro Max /
     * 15 / SE, against 420 / 420 / 374 before. On the listing single (header
     * stacked, ~144px) that puts the hero's bottom at 76% / 82% / 87% of the
     * screen — the SE is the tight one, leaving ~89px of the next section
     * showing. Still a visible signal, but that is the floor; do not raise this
     * further without re-measuring on a short phone.
     *
     * min-height, not height, so a long address wrapping to three lines still
     * grows the hero instead of being clipped.
     */
    --hdc-hero-h: min(65vh, 560px);
  }
}
