/* ==========================================================================
   tokens.css — Design tokens (single source of truth)
   --------------------------------------------------------------------------
   This is the ONLY file allowed to contain literal design values (hex colors,
   px/rem sizes, radii, etc.). Component CSS in styles.css — and later the
   WordPress theme — must reference these via var(--token) and never hardcode.

   SOURCE
   Derived from Figma: "WacoSurf-Website" → page "Opening Landing Page" →
   frame "26-WCS-19837 - Opening LP" (node 83:132, 1440 × 8905).

   NOTE ON NAMING
   The Figma file defines NO variables and NO shared color/text styles — every
   value is applied directly to layers. Names below are therefore semantic
   names we are introducing, not mirrored Figma variable names. When variables
   are later added in Figma, mirror THESE names back into Figma (kebab-case)
   so the two systems stay aligned.

   The comp is DESKTOP-ONLY (one 1440px artboard, confirmed with the client).
   No mobile comp exists or is coming, so values marked ASSUMED are our own
   mobile-first decisions to make and own — not something to reconcile later.
   ========================================================================== */

:root {

  /* ======================================================================
     1. COLOR — raw palette
     Measured from the artboard. Nicknames describe the hue so component CSS
     can stay semantic via the role aliases further down.
     ====================================================================== */

  --color-cream:      #ece3d0;  /* dominant: light bg, text on dark, hairlines */
  --color-espresso:   #341e16;  /* primary ink on cream; rules; scrim base     */
  --color-bark:       #1c110d;  /* darkest section background                  */
  --color-slate:      #3f424a;  /* cool dark bg: big section + form panel      */
  --color-gold:       #b17a1d;  /* warm overlay gradient start                 */
  --color-ember:      #2f1b13;  /* warm overlay gradient end (used at 51%)     */
  --color-rust:       #8f4e2c;  /* accent text (e.g. "WACO, TX")               */
  --color-white:      #fefefe;  /* text over imagery/video                     */
  --color-black:      #000000;  /* scrim gradient terminus                     */

  /* Warm grey, used ONLY as the stroke inside the introduction's wave line
     art. It is baked into those SVGs — like the emblem's and wordmark's
     colours — so no CSS rule references it. Listed anyway so the palette is
     complete and nobody re-derives it by eyedropper. Recolouring the waves
     means re-exporting the assets, not editing CSS. */
  --color-driftwood:  #47443d;

  /* Design-annotation grey. Used ONLY for the "Autoplay/Autoloop Video" and
     "Static Image" notes in the comp — these are instructions to us, not UI.
     Do NOT ship this color; do not add it to Elementor. */
  --color-annotation: #8f8f8f;

  /* --- Alpha variants (exact values from the comp) --------------------- */
  --color-espresso-0:   rgba(52, 30, 22, 0);      /* transparent scrim start */
  --color-ember-51:     rgba(47, 27, 19, 0.51);   /* warm overlay end        */


  /* ======================================================================
     2. COLOR — semantic roles
     Component CSS should reference THESE, not the raw palette above.
     ====================================================================== */

  --color-bg:              var(--color-cream);
  --color-bg-dark:         var(--color-bark);
  --color-bg-dark-cool:    var(--color-slate);
  /* Warm counterpart to --color-bg-dark-cool: the espresso ground under the
     rental band, which is the only band that uses espresso as a SURFACE
     rather than as ink. Cream on it measures 12.26:1. */
  --color-bg-dark-warm:    var(--color-espresso);
  /* The ground under the enquiry band — the page's only WHITE band, and the
     one place --color-white is a surface rather than type over imagery. The
     comp's frame carries no fill there at all; white is the client's call,
     confirmed, not a value read off the artboard. */
  --color-bg-white:        var(--color-white);
  --color-surface-form:    var(--color-slate);
  --color-surface-card:    var(--color-cream);
  /* Espresso panel under an arched card's photo, carrying its caption. Cream
     type on it measures 12.26:1 — comfortably past AA. */
  --color-surface-caption: var(--color-espresso);

  --color-ink:             var(--color-espresso);  /* on light  */
  --color-ink-inverse:     var(--color-cream);     /* on dark   */
  --color-ink-accent:      var(--color-rust);
  --color-ink-on-media:    var(--color-white);
  /* The inverse of the role above, and the only place on the page where type
     is set DARK on a photograph: the rental band's headline, which the comp
     puts in espresso over the frame's pale sky. It is a separate role rather
     than --color-ink because --color-ink is rebound to cream by every dark
     band, and this headline sits inside one. */
  --color-ink-on-media-dark: var(--color-espresso);

  --color-border:          var(--color-cream);     /* hairlines on dark     */
  --color-border-inverse:  var(--color-espresso);
  --color-rule:            var(--color-espresso);  /* header divider line   */

  /* ASSUMED — focus ring. Rebound per band by the .theme-* classes in
     styles.css: rust clears 3:1 only on cream, so the dark bands use cream. */
  --color-focus:           var(--color-rust);


  /* ======================================================================
     3. COLOR — gradients & overlays
     The warm overlay sits on hero/feature imagery with a blend mode; the
     scrims fade imagery into the section below so captions stay legible.
     ====================================================================== */

  /* The dark wash shared by the fullscreen menu and the introduction band:
     a radial ellipse anchored just below the top edge, with radii at 96.5%
     of the box in both axes. Percentages keep the falloff identical at any
     size, which is why one token can serve boxes of different dimensions.

     The comp draws the two origins 1.3% apart (1.6% vs 0.3% of their
     respective frame heights) — a by-product of the two frames' differing
     heights rather than a design decision, so they are unified here. */
  --wash-dark-radial: radial-gradient(
    ellipse 96.5% 96.5% at 50% 1%,
    var(--color-espresso) 0%,
    var(--color-bark) 100%
  );

  /* Dither for the wash above. The ramp spans only 24 red / 13 green / 9 blue
     units, so at 8 bits it quantises into flat 20-30px bands. Anywhere the
     ellipse runs nearly flat — the bottom of the introduction, on a wide
     viewport — one of those band edges becomes a straight full-width line in
     empty space, which reads as a stray section divider.

     The tile carries a constant 2% alpha and varies only in luminance, so it
     scatters each quantisation step across its boundary rather than tinting
     anything. Layer it ABOVE the gradient and leave it at its intrinsic
     size; scaling it would defeat the point.

     Not a colour, so it is not in the palette; it belongs to the wash. */
  --wash-dither: url("../assets/icons/wash-dither.svg");

  /* Warm wash over hero + feature media. Apply with the blend/opacity pair.

     CURRENTLY UNREFERENCED, and deliberately kept. Every photograph and
     video shipped so far already carries this treatment baked into the
     export, so applying it again in CSS double-exposed the asset — see
     styles.css section 8, which records what to restore if any of them is
     ever re-exported clean. */
  --overlay-warm:            linear-gradient(180deg, var(--color-gold) 0%, var(--color-ember-51) 100%);
  --overlay-warm-blend:      plus-lighter;
  --overlay-warm-opacity:    0.66;

  /* Bottom-up scrim on arched cards (fade begins ~75% down). */
  --scrim-bottom:            linear-gradient(180deg, var(--color-espresso-0) 74.961%, var(--color-black) 100%);
  --scrim-bottom-opacity:    0.66;

  /* Top-up scrim on the full-bleed band. */
  --scrim-top:               linear-gradient(0deg, var(--color-espresso-0) 64.218%, var(--color-black) 98.664%);
  --scrim-top-opacity:       0.66;

  /* Caption bar behind the "Watch Video" control. */
  --scrim-caption:           linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, var(--color-black) 100%);
  --scrim-caption-opacity:   0.58;


  /* ======================================================================
     4. TYPOGRAPHY — families
     Delivered two different ways — see CLAUDE.md "Font delivery".

     Clash Display  — self-hosted from static/assets/fonts/ (Fontshare).
     Neue Haas      — Adobe Fonts (Typekit) kit tuv2ipf, loaded from the
                      Adobe CDN. The Adobe family name is the lowercase
                      slug below, NOT the Figma layer name "Neue Haas
                      Grotesk Display Pro" — that string matches nothing.
                      Adobe's license forbids self-hosting this family.
     ====================================================================== */

  --font-display: "Clash Display", ui-sans-serif, system-ui, sans-serif;
  --font-body:    "neue-haas-grotesk-display", "Helvetica Neue", Helvetica, Arial, sans-serif;


  /* ======================================================================
     5. TYPOGRAPHY — weights
     The two families map their style names onto CSS weights DIFFERENTLY,
     so the scales are kept separate. Do not mix them.

     Clash Display maps conventionally (Regular 400 / Medium 500 / Bold 700).

     Adobe shifts Neue Haas's numeric style names up one step: the kit
     ships only n5/n6/n7, so "55 Roman" is weight 500 — there is NO 400
     face. Asking for 400 gets a synthesized or fallback render.
     ====================================================================== */

  /* Clash Display — self-hosted */
  --font-weight-display-regular: 400;  /* ClashDisplay-Regular */
  --font-weight-display-medium:  500;  /* ClashDisplay-Medium  */
  --font-weight-display-bold:    700;  /* ClashDisplay-Bold    */

  /* Neue Haas Grotesk Display — Adobe Fonts */
  --font-weight-body-roman:  500;  /* Figma "55 Roman"  → Adobe n5 */
  --font-weight-body-medium: 600;  /* Figma "65 Medium" → Adobe n6 */
  --font-weight-body-bold:   700;  /* Figma "75 Bold"   → Adobe n7 */


  /* ======================================================================
     6. TYPOGRAPHY — size scale
     Exact comp values kept for fidelity. The fractional sizes come from
     scaled artboard groups; round them if a cleaner scale is preferred.
     ====================================================================== */

  --font-size-3xs:  10.64px;   /* menu button label            */
  --font-size-2xs:  11px;      /* small caps label             */
  --font-size-xs:   11.667px;  /* "watch video" control        */
  --font-size-sm:   12px;      /* eyebrow, nav, CTA label      */
  --font-size-base: 14px;      /* body copy                    */
  --font-size-md:   16px;      /* lead paragraph, form label   */
  --font-size-lg:   20px;      /* minor heading                */
  --font-size-xl:   24px;      /* section heading, card caption*/
  --font-size-2xl:  29.647px;  /* display heading              */


  /* ======================================================================
     7. TYPOGRAPHY — line heights (unitless)
     Each ratio reproduces the comp's exact px leading at its paired size,
     while staying fluid if sizes change responsively.
     ====================================================================== */

  --line-height-flat:    1.214;  /* 11.667px → 14.167px */
  --line-height-tight:   1.349;  /* 29.647px → 40px     */
  --line-height-snug:    1.5;    /* 24px → 36px         */
  --line-height-heading: 1.55;   /* 20px → 31px         */
  --line-height-body:    1.714;  /* 14px → 24px         */
  --line-height-relaxed: 1.75;   /* 16px → 28px         */
  --line-height-loose:   1.8125; /* 16px → 29px         */
  --line-height-caps:    2;      /* 10.64px → 21.28px   */
  --line-height-nav:     3;      /* 12px → 36px         */


  /* ======================================================================
     8. TYPOGRAPHY — letter spacing
     The comp's tracking is systematic once normalized against font size:
     0.15em nearly everywhere, 0.2em on small caps, 0.21em on 12px nav.
     Using em keeps tracking correct at any size.
     ====================================================================== */

  --tracking-wide:   0.15em;
  --tracking-wider:  0.2em;
  --tracking-widest: 0.21em;


  /* ======================================================================
     9. TYPOGRAPHY — roles
     Composite sets matching how type is actually used. These map 1:1 onto
     Elementor Global Fonts (family + size + weight + line-height + spacing).
     ====================================================================== */

  /* Display heading — Clash Medium, uppercase */
  --type-display-family:  var(--font-display);
  --type-display-size:    var(--font-size-2xl);
  --type-display-weight:  var(--font-weight-display-medium);
  --type-display-leading: var(--line-height-tight);
  --type-display-track:   var(--tracking-wide);

  /* Section heading — Clash Medium, uppercase */
  --type-heading-family:  var(--font-display);
  --type-heading-size:    var(--font-size-xl);
  --type-heading-weight:  var(--font-weight-display-medium);
  --type-heading-leading: var(--line-height-snug);
  --type-heading-track:   var(--tracking-wide);

  /* Subheading — Clash Medium */
  --type-subheading-family:  var(--font-display);
  --type-subheading-size:    var(--font-size-lg);
  --type-subheading-weight:  var(--font-weight-display-medium);
  --type-subheading-leading: var(--line-height-heading);
  --type-subheading-track:   var(--tracking-wide);

  /* Section eyebrow / rail label — NHG Roman, uppercase */
  --type-eyebrow-family:  var(--font-body);
  --type-eyebrow-size:    var(--font-size-sm);
  --type-eyebrow-weight:  var(--font-weight-body-roman);
  --type-eyebrow-leading: var(--line-height-nav);
  --type-eyebrow-track:   var(--tracking-widest);

  /* Body copy — NHG Roman */
  --type-body-family:  var(--font-body);
  --type-body-size:    var(--font-size-base);
  --type-body-weight:  var(--font-weight-body-roman);
  --type-body-leading: var(--line-height-body);
  --type-body-track:   var(--tracking-wide);

  /* Lead paragraph — NHG Roman */
  --type-lead-family:  var(--font-body);
  --type-lead-size:    var(--font-size-md);
  --type-lead-weight:  var(--font-weight-body-roman);
  --type-lead-leading: var(--line-height-loose);
  --type-lead-track:   var(--tracking-wide);

  /* Card caption — NHG Roman at heading size */
  --type-caption-family:  var(--font-body);
  --type-caption-size:    var(--font-size-xl);
  --type-caption-weight:  var(--font-weight-body-roman);
  --type-caption-leading: var(--line-height-heading);
  --type-caption-track:   var(--tracking-wide);

  /* CTA / button label — NHG Roman, uppercase.
     The comp draws this label in a 36px line box, but that box is the
     CONTROL's height, not leading — --control-height-md reproduces it, and
     the label is centred in it. Applied as leading it is invisible on a
     one-line label and shows up only once a label wraps, as a triple-spaced
     gap that reads as two separate labels rather than one control. Same
     decomposition as --type-menu-*, which splits the comp's 84px line box
     into a 36px text box plus --menu-item-gap.

     At 1.714 a two-line label measures 41px, so a wrapped control still
     lands on the comp's 48px height; three lines grow it, as they should. */
  --type-cta-family:  var(--font-body);
  --type-cta-size:    var(--font-size-sm);
  --type-cta-weight:  var(--font-weight-body-roman);
  --type-cta-leading: var(--line-height-body);
  --type-cta-track:   var(--tracking-widest);

  /* Form field label — NHG Medium */
  --type-label-family:  var(--font-body);
  --type-label-size:    var(--font-size-md);
  --type-label-weight:  var(--font-weight-body-medium);
  --type-label-leading: var(--line-height-snug);
  --type-label-track:   var(--tracking-wide);

  /* Small caps label — NHG Roman, uppercase */
  --type-label-sm-family:  var(--font-body);
  --type-label-sm-size:    var(--font-size-2xs);
  --type-label-sm-weight:  var(--font-weight-body-roman);
  --type-label-sm-leading: var(--line-height-body);
  --type-label-sm-track:   var(--tracking-wide);

  /* Hero badge quote — Clash Regular, uppercase */
  --type-quote-family:  var(--font-display);
  --type-quote-size:    var(--font-size-base);
  --type-quote-weight:  var(--font-weight-display-regular);
  --type-quote-leading: var(--line-height-caps);
  --type-quote-track:   var(--tracking-wider);

  /* Fullscreen menu item — Clash Medium, uppercase. The comp draws these in
     an 84px line box; that pitch is reproduced as this 36px text box plus
     --menu-item-gap, so focus rings hug the word instead of the line box. */
  --type-menu-family:  var(--font-display);
  --type-menu-size:    var(--font-size-xl);
  --type-menu-weight:  var(--font-weight-display-medium);
  --type-menu-leading: var(--line-height-snug);
  --type-menu-track:   var(--tracking-wide);

  /* Media control label ("Watch Video" over a still) — Clash Medium,
     uppercase. One step larger than --type-control-* and a heavier face,
     because it sits over photography rather than on a flat bar. */
  --type-media-control-family:  var(--font-display);
  --type-media-control-size:    var(--font-size-xs);
  --type-media-control-weight:  var(--font-weight-display-medium);
  --type-media-control-leading: var(--line-height-flat);
  --type-media-control-track:   var(--tracking-wider);

  /* Utility control label (menu, watch video) — uppercase */
  --type-control-family:  var(--font-display);
  --type-control-size:    var(--font-size-3xs);
  --type-control-weight:  var(--font-weight-display-regular);
  --type-control-leading: var(--line-height-caps);
  --type-control-track:   var(--tracking-wider);


  /* ======================================================================
     10. SPACING
     The artboard uses absolute positioning (no auto-layout), so no gap or
     padding values could be read directly. This is a 4px-based scale that
     covers the offsets measured in the comp. Numeral = multiple of 4px.
     ====================================================================== */

  --space-0:  0;
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-14: 56px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;
  --space-30: 120px;
  --space-40: 160px;
  --space-50: 200px;


  /* ======================================================================
     11. LAYOUT
     Measured from the 1440px artboard, except where marked ASSUMED.
     ====================================================================== */

  --layout-page-max:      1440px;  /* artboard width                        */
  --layout-content-max:   1138px;  /* section rail: x151 → x1289            */
  --layout-panel-max:     1257px;  /* form container width                  */
  --layout-gutter:        var(--space-6);   /* ASSUMED — our mobile choice  */
  --layout-gutter-lg:     151px;            /* desktop side margin          */

  --layout-header-height: 78px;    /* full header incl. divider             */
  --layout-topbar-height: 71px;    /* cream bar only                        */

  /* The rail at --breakpoint-md, derived rather than measured so it tracks
     the gutter. Bands whose desktop composition STACKS use it as the width
     the stacked version is designed at, and then centre rather than keep
     widening to fill — because a block that grows with the rail past the
     point where growing helps ends up with its content stranded at one edge.

     Promoted here from --rental-stack-max on its second use (the community
     band), per the note in section 15: a value earns its way out of a
     section's namespace on the second instance, not the first. */
  --layout-stack-max:     calc(48rem - var(--space-10) * 2);   /* 688px */

  --control-height-sm:    43px;    /* submit button                         */
  --control-height-md:    48px;    /* outlined CTA                          */
  --control-height-lg:    51px;    /* form input / select                   */
  --rule-width:           1px;     /* header divider                        */

  /* --- Site chrome asset sizes -----------------------------------------
     Intrinsic sizes of the brand assets the header and fullscreen menu
     draw. They were first measured in section 16 (HERO), but that set
     travels with the landing template while header.php and the menu ship
     on every page — so they live up here with the other site-wide layout
     values, as CLAUDE.md's porting note required. */
  --logo-wordmark-width:  181px;
  --logo-wordmark-height: 28px;
  --menu-icon-width:      39px;   /* exported asset is 39×18; the comp had  */
  --menu-icon-height:     18px;   /* 37.5×17 — using the export avoids a    */
                                  /* half-pixel and a squashed aspect ratio */

  /* --- Breakpoints -----------------------------------------------------
     ASSUMED — chosen by us, since the comp is desktop-only (see CLAUDE.md).

     IMPORTANT: @media queries cannot read custom properties, so breakpoint
     values MUST be repeated as literals in media query conditions. That is
     the one unavoidable exception to the no-hardcoded-values rule. These
     tokens exist so the canonical values live here; keep any literal in a
     media query in sync with them and label it with the token name. */

  --breakpoint-sm: 30rem;  /*  480px */
  --breakpoint-md: 48rem;  /*  768px */
  --breakpoint-lg: 64rem;  /* 1024px */
  --breakpoint-xl: 90rem;  /* 1440px — artboard width */


  /* ======================================================================
     12. BORDER RADII
     The arch radii are half the width of the shape they cap; keep them
     paired with their component rather than reused arbitrarily.
     ====================================================================== */

  --radius-none: 0;
  /* Not a measurement — the assertion "this box is a circle", which is why it
     is a percentage where every other radius here is a length. Used by the
     community tagline's separators. */
  --radius-circle: 50%;
  --radius-xs:   4px;         /* inputs, selects, submit button       */
  --radius-md:   25px;        /* form container panel                 */
  --radius-lg:   40px;        /* feature media card                   */
  --radius-pill: 136.715px;   /* hero oval badge                      */

  --radius-arch-sm: 199.5px;  /* top corners, 399px-wide card         */
  --radius-arch-md: 240px;    /* top-left, 480px-wide panel           */
  --radius-arch-lg: 286px;    /* top corners, 497px-wide card         */

  /* A semicircular arch cap at ANY width. Deliberately far larger than any
     card: CSS scales all four radii down uniformly when they overflow their
     edge, so an oversized value always resolves to exactly half the box
     width. The three measured radii above are only correct at the one width
     they were taken from; this one survives a responsive layout, so prefer
     it for new arches. */
  --radius-arch-cap: 999px;


  /* ======================================================================
     13. BORDER WIDTHS
     ====================================================================== */

  --border-width-hairline: 0.5px;    /* outlined CTAs, card outlines      */
  --border-width-thin:     0.814px;  /* hero badge inner outline          */

  /* ASSUMED — the comp draws no focus states. The ring every control on
     the page shares; :focus-visible in styles.css section 3 is the one
     rule that reads these. Its COLOUR travels separately, through
     --color-focus, which each .theme-* class rebinds. */
  --focus-ring-width:  2px;
  --focus-ring-offset: 2px;


  /* ======================================================================
     14. SHADOWS
     None. The comp uses no effect styles or drop shadows anywhere — depth
     comes from the gradient scrims in section 3. Add tokens here only if a
     shadow is genuinely introduced, and never inline the value.
     ====================================================================== */


  /* ======================================================================
     15. SHARED COMPONENTS

     The dividing line in this file: sections 1-14 are the system, 16-20 are
     one section's measurements each, and THIS is the set that more than one
     section uses. A value earns its way in here on the second instance, not
     the first — namespaced until reuse is a fact rather than a guess.

     It matters for the WordPress port. The components built on these tokens
     (.cta, .section-label — styles.css section 6) have to be enqueued site
     wide rather than on the landing template, because Elementor can place a
     CTA on any page. Anything below section 15 can stay with its template;
     anything at or above 1-15 cannot. See CLAUDE.md "Shared components".
     ====================================================================== */

  /* --- Section label ---------------------------------------------------
     A label centred in a hairline that runs the full content rail, each
     half ending in a small dot facing the words:

         [————•]  WHY THE DESPERADO  [•————]

     Measured on the amenities band (Figma 132:125), which leaves 242.5px
     between the dots around a 180px label — ~31px of clearance a side.
     --space-8 reproduces that; the smaller mobile gap keeps a long label
     from crowding the rule off a phone entirely.

     Both the line and the dot are drawn in CSS from the exported vector's
     own geometry rather than shipped as an SVG: the art is a 1px rule plus
     an r=1.333px circle, and stretching an SVG that thin across the rail
     would only soften both.

     The type role is deliberately NOT fixed here — it is applied in the
     markup, because the label's size is the section's decision. The CTA
     below does the opposite, for the opposite reason. */
  --section-label-dot-radius: 1.333px;
  --section-label-gap:        var(--space-4);
  --section-label-gap-md:     var(--space-8);

  /* --- CTA -------------------------------------------------------------
     The rule-bounded control: an uppercase label between two hairlines,
     no fill and no side borders. Height is --control-height-md and the
     rules are --rule-width, both already in section 11; the only geometry
     unique to the control is its inline padding, so that a short viewport
     can't let a long label touch the rule's ends.

     Width is per-instance and stays with the section that places it
     (--intro-cta-width, --amenities-cta-width), fed in through --cta-width.

     Its colours are NOT tokenised here. They are bound by the .cta--light
     and .cta--dark variants in styles.css, deliberately to raw palette
     entries rather than to the .theme-* roles, so a control keeps its
     contrast when Elementor drops it somewhere with no themed ancestor. */
  --cta-pad-inline: var(--space-6);

  /* The standard control width. Five bands set their CTA at 399px —
     amenities, surf, ICON, lines and rental — which is well past this
     section's promote-on-second-use bar; it stayed namespaced longer than
     it should have because each band measured the value independently.
     The per-section tokens remain as aliases so each set still reads in
     its own terms, the same way --rental-stack-max aliases
     --layout-stack-max. */
  --cta-width-standard: 399px;

  /* The width "Get Pricing & Availability" needs, promoted here on its
     second use for the reason above. The introduction measured 289 in the
     comp; the community band measured 263, but for a "Learn More" that has
     since been re-labelled to this same string. At the CTA type scale the
     label sets 221.6px, and --cta-pad-inline takes 24 off each end, so the
     control cannot go below 269.6 without breaking to two lines — which is
     exactly what 263 did.

     It is a MAX-width, so this is headroom rather than a fixed size: the
     control still shrinks to its container on a phone, and wrapping stays
     available there as the safety valve it is meant to be.

     Any band re-labelled to a longer string needs the same check. The
     labels are client-editable through ACF, so this cannot be guaranteed
     from here — it is why the value carries its measurement. */
  --cta-width-pricing: 289px;

  /* --- Dialog close ----------------------------------------------------
     The X on both overlays — the fullscreen menu and the video lightbox.
     See .dialog-close in styles.css section 6. Comp 35.5px, rounded to a
     whole pixel; promoted from the menu's set on its second use. */
  --dialog-close-size: 36px;

  /* Hover dim for controls whose ink cannot shift hue — the dialog close
     buttons and the menu links, where the artwork's colour is baked into
     the SVG or the label is already the brightest thing on the band.
     ASSUMED: the comp specifies no hover states anywhere. */
  --hover-dim-opacity: 0.7;

  /* Video frame ratio, shared by the hero's ambient players and the
     lightbox player. Vimeo serves 16:9; if a differently-cropped video
     ever ships, change it here and every frame follows. */
  --ratio-video: 16 / 9;

  /* --- Brand badge -----------------------------------------------------
     The oval badge device: near-opaque cream fill so the ground tints
     through faintly, plus the inner hairline's insets. Measured on the
     hero badge (Figma 132:63) and promoted here on its second use — the
     404 page sets its status in the same device — per this section's
     promote-on-second-use rule. The hero's own tokens alias these. */
  --badge-fill: color-mix(in srgb, var(--color-cream) 90%, transparent);
  --badge-outline-inset-inline: 7px;
  --badge-outline-inset-block:  9px;


  /* ======================================================================
     16. HERO — component geometry
     Measured from Figma frame 132:3 ("01 - Hero"). Namespaced because these
     are one component's measurements, not system-wide scale steps.

     One comp value is deliberately NOT tokenised here: the 6px gap between
     the header bar and its rule. That is derived:
     --layout-header-height − --layout-topbar-height − --rule-width.
     ====================================================================== */

  /* The comp's 791px media height is not used — the hero fills the viewport
     instead, with a floor derived from the badge so the oval never gets
     cramped on a short window.

     This lives here rather than inline in .hero__media because the
     introduction's connector has to know where the hero's bottom edge is in
     order to reach up to the badge. Two copies of this expression would
     drift apart silently; one token cannot. */
  --hero-media-min-height: max(
    100svh - var(--layout-topbar-height),
    var(--hero-badge-height) + var(--space-12) * 2
  );

  /* Badge — the cream oval over the centre panel. The fill carries its own
     alpha rather than using opacity on the element, which would fade the
     logo and heading inside it too. */
  --hero-badge-width:  234px;
  --hero-badge-height: 437px;
  --hero-badge-fill:   var(--badge-fill);   /* promoted to section 15 on its
                                               second use (the 404 badge);
                                               aliased so this set still
                                               reads in its own terms */

  --hero-badge-outline-inset-inline: var(--badge-outline-inset-inline);  /* promoted with --badge-fill */
  --hero-badge-outline-inset-block:  var(--badge-outline-inset-block);

  --hero-badge-pad-block-start: 73px;
  --hero-badge-pad-block-end:   89px;
  --hero-badge-gap-emblem:      14px;  /* emblem → wordmark; the wordmark →
                                          quote gap is 32px = --space-8    */
  --hero-quote-max-width: 152px;

  /* Emblem intrinsic size, straight from the comp. The wordmark and menu
     icon sizes were first measured here too, but they belong to the site
     chrome — the header and fullscreen menu render on every page — so
     they now live in section 11, the promotion CLAUDE.md's porting note
     called for. Only the emblem is the hero's own. */
  --hero-emblem-width:    72px;
  --hero-emblem-height:   89px;


  /* ======================================================================
     17. MENU — fullscreen overlay (Figma node 101:87)

     The wash is --wash-dark-radial (section 3), shared with the
     introduction band. The 0.93 opacity is applied to a pseudo-element
     rather than the panel, so the nav links stay fully opaque — the same
     reason the hero badge uses a pre-mixed fill.
     ====================================================================== */

  --menu-overlay-gradient: var(--wash-dark-radial);
  --menu-overlay-opacity: 0.93;

  /* 48px. With the 36px text box above it this reproduces the comp's 84px
     pitch between items exactly. */
  --menu-item-gap: var(--space-12);

  --menu-divider-width: 28px;        /* wave rule between items          */
  --menu-divider-ratio: 28.5 / 3.5;  /* the asset's own intrinsic ratio,
                                        so scaling it can't distort it   */

  /* The close button's size lived here until the lightbox reused it —
     it is now --dialog-close-size in section 15. */


  /* ======================================================================
     18. MOTION
     The comp specifies no transitions. These exist so component CSS never
     inlines a duration or easing curve; every one of them is bypassed by
     the prefers-reduced-motion block in styles.css.
     ====================================================================== */

  --motion-duration-fast: 150ms;
  --motion-duration-base: 240ms;
  /* Long enough to read as a dissolve rather than a switch. Used by the
     hero's poster-to-video hand-off, where the whole point is that the eye
     should not catch the moment the still is replaced — a --base-length
     fade there still reads as a cut. */
  --motion-duration-slow: 600ms;
  --motion-ease:          cubic-bezier(0.4, 0, 0.2, 1);




  /* ======================================================================
     19. INTRODUCTION — component geometry (Figma node 132:88)

     Namespaced like the hero and menu sets above: these are one section's
     measurements, not system-wide scale steps. Values that turned out to be
     general — the arch cap radius, the media-control type role, the dark
     wash — live in their proper sections instead and are referenced here.
     ====================================================================== */

  /* --- Rail & rhythm ---------------------------------------------------
     The comp insets this section further than the standard 1138px rail:
     the media card starts at x197 and the copy ends at x1212. Rebinding
     --layout-content-max on the section (see styles.css) makes .container
     produce that narrower rail without a second container class. */
  --intro-content-max: 1015px;  /* x197 → x1212 */
  --intro-text-max:      380px; /* widest copy line in the comp           */
  --intro-cta-width:     var(--cta-width-pricing);  /* 289 — section 15 */

  --intro-pad-block:    var(--space-20);  /* ASSUMED — mobile             */
  --intro-pad-block-lg: var(--space-30);  /* comp: 127px above the card   */

  /* Desktop only: the copy column starts 82px below the media card's top
     in the comp. Rounded onto the 4px scale. */
  --intro-text-offset: var(--space-20);

  /* --- Media card ------------------------------------------------------
     An outlined arch with the photo inset inside it. The gap is 20px at the
     sides and 18px top and bottom, so the two arches are near- but not
     exactly concentric — that asymmetry is in the comp, not a rounding
     error. The arch itself uses --radius-arch-cap (section 12). */
  --intro-card-width: 388px;
  --intro-card-ratio: 388 / 537;

  --intro-frame-inset-inline: var(--space-5);  /* 20px */
  --intro-frame-inset-block:  18px;

  --intro-play-icon-size: 24px;   /* comp 23.333px, rounded to the export */

  /* ASSUMED — no hover states are specified in the comp. */
  --intro-photo-hover-scale: 1.05;
  --intro-icon-hover-scale:  1.15;
  --intro-hover-duration:    480ms;  /* slower than --motion-duration-base:
                                        an image zoom reads as clumsy at
                                        UI-control speed */

  /* --- Connector -------------------------------------------------------
     The hairline that drops out of the hero badge and curls into the
     headline. Its length is derived, never fixed, so it tracks the hero at
     any viewport height.

     The overhang is the distance from the badge's bottom edge to the hero's
     bottom edge. The badge is centred in .hero__media, so that is half the
     leftover height — hence --hero-media-min-height (section 16), which is
     the single definition .hero__media itself also uses. */
  --intro-connector-overhang: calc(
    (var(--hero-media-min-height) - var(--hero-badge-height)) / 2
  );

  /* Where the spiral's centre lands, measured from the top of the band.
     Mobile: in the padding above the media card, since the headline is
     below the card at that width. Desktop: dead on the centre of the
     headline's first line — fully derived, so retyping the heading role
     can't knock it out of alignment. */
  --intro-connector-drop: var(--space-16);
  --intro-connector-drop-lg: calc(
    var(--intro-pad-block-lg)
    + var(--intro-text-offset)
    + var(--type-heading-size) * var(--type-heading-leading) / 2
  );

  --intro-spiral-width:  20.207px;
  --intro-spiral-height: 18.741px;

  /* --- Wave lines ------------------------------------------------------
     The art ships as six separate one-line SVGs rather than one combined
     file, because each line is animated independently — a single image can
     only ever slide as a rigid block, which is exactly what reads as
     mechanical.

     Every line traces an ELLIPTICAL ORBIT rather than a left-right slide.
     That is how water actually moves: a particle in a wave travels in a
     circle, not a line. Each line gets its own period, radius and phase, so
     the six never resynchronise and the band undulates instead of sliding.

     Line 1 is the topmost and most opaque; line 6 the lowest and faintest.
     Radius and scroll-parallax both taper down that index, so the faint
     lines read as further away. Everything below is ASSUMED — the comp
     specifies no motion at all. */

  /* EDGE COVERAGE — the one thing here that must never break.

     Each exported line is cropped to the window where all six paths exist,
     with a couple of units to spare at each end (viewBox x 36.5 → 1498 of
     the original 1526 — the binding constraints are wave 6 on the left and
     wave 3 on the right). Every line therefore spans its whole layer, edge
     to edge, with no endpoint flush to a boundary. With the layer sized off
     the viewport,
     that makes coverage scale-invariant: a line always runs from −bleed to
     100% + bleed, at any width.

     That leaves the bleed with exactly one job — absorbing the orbit's
     travel — so it is DERIVED from the radius rather than set alongside it.
     A full orbit sweeps 2 × radius horizontally, so 4 × radius leaves a
     whole orbit's width of margin at both edges. Widen the orbit and the
     bleed widens with it; the two can no longer drift out of step.

     Do NOT invert this dependency. A bleed chosen independently of the
     orbit is exactly what put line ends on screen above ~1900px: the inset
     it had to cover scaled with the viewport while the bleed itself
     didn't. */
  --intro-wave-radius-x:      var(--space-5);   /* 20px, front line       */
  --intro-wave-radius-x-step: 3px;              /* tapering front to back */
  --intro-wave-radius-y:      10px;
  --intro-wave-radius-y-step: 1.4px;

  --intro-waves-bleed: calc(var(--intro-wave-radius-x) * 4);
  --intro-waves-ratio: 1461.5 / 345;   /* the cropped art's ratio */

  /* Long and mutually prime-ish, so the six orbits stay out of phase for
     minutes rather than seconds. */
  --intro-wave-period:      26s;
  --intro-wave-period-step: 7s;
  --intro-wave-lag:        -6s;   /* negative: staggered from the first frame,
                                     with no dead time on load */

  /* Light moving across water. Kept tight and very slow so it reads as
     depth rather than a flicker. */
  --intro-wave-breath:        37s;
  --intro-wave-breath-step:   5s;
  --intro-wave-breath-lowest: 0.72;

  /* Scroll-linked drift. Also tapers by line, so the band fans open as the
     section travels the viewport — the part that reads as real depth. */
  --intro-waves-parallax:      var(--space-10);   /* 40px, front line */
  --intro-waves-parallax-step: 6px;

  /* --- Video lightbox --------------------------------------------------
     ASSUMED throughout — the comp shows no modal, only the trigger. The
     ratio is the shared --ratio-video (section 15), aliased so this set
     still reads in its own terms. */
  --video-modal-ratio:    var(--ratio-video);
  --video-modal-backdrop: color-mix(in srgb, var(--color-bark) 90%, transparent);


  /* ======================================================================
     20. AMENITIES OVERVIEW — component geometry (Figma node 132:117)

     Namespaced like the hero, menu and introduction sets above: these are
     one section's measurements, not system-wide scale steps. Values that
     turned out to be general — the arch cap radius, the hairline border
     width, the espresso caption surface — live in their proper sections
     and are only referenced from here.

     The band is a flat --color-bg-dark rectangle, so .theme-dark alone
     paints it; there is no wash token, unlike the introduction.
     ====================================================================== */

  /* The band's opening divider is --section-label-* (section 15) — it is a
     repeating device, not this section's property. Same for the control at
     the foot of the band: only its width lives here. */

  /* --- Copy measures ---------------------------------------------------
     Straight from the comp's text-box widths. */
  --amenities-heading-max: 288px;   /* forces the comp's two-line break   */
  --amenities-subhead-max: 380px;
  --amenities-text-max:    523px;
  --amenities-cta-width:   var(--cta-width-standard);   /* the first band
                                       to measure 399 — see section 15    */

  /* --- Rhythm ----------------------------------------------------------
     Comp offsets rounded onto the 4px scale; ties round up. The band opens
     flush at the top because the label rule IS the boundary between this
     section and the one above it — there is no top padding to add. */
  --amenities-pad-block-end:    var(--space-24);  /* ASSUMED — mobile     */
  --amenities-pad-block-end-lg: var(--space-40);  /* comp 156px           */

  --amenities-gap-heading:  var(--space-16);  /* comp 60px                */
  --amenities-gap-subhead:  var(--space-5);   /* comp 18px                */
  --amenities-gap-cards:    var(--space-12);  /* ASSUMED — stacked mobile */
  --amenities-gap-cards-lg: var(--space-0);   /* comp: the side arches
                                                 start level with the
                                                 subhead's box bottom     */
  --amenities-gap-body:     var(--space-16);  /* comp 62px                */
  --amenities-gap-cta:      var(--space-10);  /* comp 42px                */

  /* --- Arched card -----------------------------------------------------
     Three cards; the middle one is wider, taller and dropped down the page.
     Each is a photo above an espresso caption panel, with a cream hairline
     arch inset inside the whole card — note that the outline sits INSIDE
     the media here, the reverse of the introduction's card, where the photo
     is inset inside the outline.

     The comp's inset measures 6px on the side cards and 7px on the middle
     one (and 5/7 and 7/9 top/bottom); one value is used for all four edges
     since the variation is smaller than the hairline is thick. */
  --amenity-card-width:         281px;
  --amenity-card-width-feature: 337px;
  --amenity-outline-inset:      6px;

  --amenity-media-ratio:         281 / 382;
  --amenity-media-ratio-feature: 337 / 477;

  /* ASSUMED — stacked mobile. At the comp ratio a full-width arch is ~605px
     tall on a phone, three quarters of the viewport, so one card is all you
     ever see. Shortening the crop to 4:5 gives back the height while only
     trimming 8% off the photograph — the arch cap is still half the media
     box, which is what makes the shape read. Restored to the comp ratio at
     48rem, where three cards share the row and height is no longer the
     constraint. */
  --amenity-media-ratio-sm: 4 / 5;

  --amenity-row-gap:       var(--space-5);   /* comp 21px between cards   */
  --amenity-stack-gap:     var(--space-12);  /* ASSUMED — stacked mobile  */
  --amenity-feature-offset: var(--space-20); /* comp 84px                 */

  /* The caption panel is 161px tall in both card widths. Padding drives the
     height so it grows with a longer caption, and the min-height holds the
     comp's proportion when the caption is short.

     On a phone the comp's floor is dead weight — the caption already fills
     ~138px there — so the smaller floor simply stops binding and the panel
     sizes to its own text. */
  --amenity-caption-min-height:    var(--space-30);  /* ASSUMED — mobile */
  --amenity-caption-min-height-md: var(--space-40);  /* comp 161px       */
  --amenity-caption-pad-block:  var(--space-10);
  /* 20px is a floor, not the resting value: on both comp card widths the
     measure below wins, which is what reproduces the comp's line breaks. */
  --amenity-caption-pad-inline: var(--space-5);
  --amenity-caption-max:        236px;  /* comp measure on the side cards */
  /* The captions are set in the 16px lead role; the client asked for 14px
     (2026-08), so the section rebinds the size to the body step instead of
     minting a new literal. */
  --amenity-caption-size:       var(--font-size-base);


  /* ======================================================================
     21. SURF EXPERIENCE — component geometry (Figma node 132:158)

     Namespaced like the sets above: one section's measurements, not
     system-wide scale steps. The band is flat --color-bg, so .theme-light
     alone paints it — there is no wash here, as in the amenities band.
     ====================================================================== */

  /* --- Rails -----------------------------------------------------------
     This band runs TWO rails, which is new and is deliberate in the comp:

       the label's rule spans x151 → x1289.5 — the standard 1138px
         --layout-content-max every other band uses;
       the two-column row below it spans x102 → x1347, i.e. 1245px, and so
         hangs ~53px outboard of the rule at each end.

     Only the row rebinds the rail (see styles.css), so the label keeps the
     page's standard one. The comp draws the row 4.5px right of the
     artboard centre — an artefact of an absolutely positioned board, not a
     design decision, so it is centred here.

     Below the width where 1245px + gutters fits, the two rails converge
     and the rule lines up with the copy. */
  --surf-content-max: 1245px;

  /* Copy column. Both text boxes measure exactly 497px in the comp, and
     the photograph is capped here too while the pair is stacked, so the
     two blocks share one edge instead of missing it by 50px. */
  --surf-text-max: 497px;

  /* Heading cap, and the one value here that is NOT the comp's own.
     "PERFECT WAVES" hugs to 306px, so capping at the measured width sits
     exactly on the break point — a hair of font-metric drift either way
     and the heading sets in three lines. The cap clears it instead, while
     staying well under the ~525px the heading needs to set on one line, so
     the comp's break survives from either direction. */
  --surf-heading-max: 380px;

  /* The page's standard control width — --cta-width-standard in section
     15, promoted once five bands had landed on the same 399. The alias
     stays so this section reads in its own terms. */
  --surf-cta-width: var(--cta-width-standard);

  /* --- Row proportions -------------------------------------------------
     The comp's three horizontal measures — copy 497, gap 201, media 547 —
     as grid tracks. In `fr` rather than px on purpose: 497 + 547 already
     needs a 1044px rail before any gap at all, which is wider than the
     944px available at --breakpoint-lg, so fixed columns would overflow
     the moment the row splits. Sharing the rail in the comp's own ratio
     holds the composition at every width and lands on the comp exactly at
     the full 1245px rail.

     The middle track is the gap. It is a track rather than a `column-gap`
     so that it scales with the other two — a fixed gap would eat 21% of
     the rail at 1024px and squeeze both columns. */
  --surf-col-text:  497fr;
  --surf-col-gap:   201fr;
  --surf-col-media: 547fr;

  --surf-media-ratio: 547 / 517;

  /* --- Rhythm ----------------------------------------------------------
     Comp offsets rounded onto the 4px scale, as in the amenities band. */
  --surf-pad-block:          var(--space-20);  /* ASSUMED — mobile        */
  --surf-pad-block-start-lg: var(--space-24);  /* comp 97                 */
  --surf-pad-block-end-lg:   var(--space-30);  /* comp 114 below the card */

  --surf-gap-layout:    var(--space-16);  /* ASSUMED — mobile             */
  --surf-gap-layout-lg: var(--space-24);  /* comp 97, label rule → card   */

  --surf-gap-copy: var(--space-8);   /* comp 29, and again 29            */
  --surf-gap-cta:  var(--space-12);  /* comp 49                          */

  /* Desktop only: the copy column starts 43px below the card's top edge
     in the comp. Rounded onto the 4px scale, as --intro-text-offset is. */
  --surf-text-offset: var(--space-10);

  /* --- Surfboard -------------------------------------------------------
     Line art straddling the card's leading edge: of its 91px width, 63px
     sits out on the cream band and 28px overlaps the photograph, starting
     24px below the card's top.

     Every offset is a FRACTION of the comp's 547 × 517 card rather than a
     px value, because the media column is proportional (see the fr tracks
     above) — a fixed 63px overhang would drift off the card's edge as the
     column narrowed, which is the one thing this composition can't do. */
  --surf-board-ratio:        91 / 307;
  --surf-board-width:        calc(91 / 547 * 100%);
  --surf-board-inset-inline: calc(-63 / 547 * 100%);
  --surf-board-inset-block:  calc(24 / 517 * 100%);


  /* ======================================================================
     22. HOMES & FLOOR PLANS — component geometry (Figma node 132:178)

     The largest band on the page: 2320px in the comp, in three parts —
     a full-bleed banner, the ICON block, and the "choose your line" pair.
     Namespaced in three groups to match, since the three keep their own
     rhythm and only the slate ground is shared.

     No new type roles and no new colours were needed. Every scrim, radius
     and weight here already existed; see the notes on the two places where
     an existing role is reused slightly off its measured leading.
     ====================================================================== */

  /* --- Banner ----------------------------------------------------------
     Full-bleed photograph carrying the section label, with --scrim-top
     over it — the token whose comment already said "full-bleed band",
     written for this section before it was built. */
  /* Three ratios, because holding the comp's letterbox everywhere made the
     band a 136px strip on a phone and a 279px one on a tablet — too short
     to read as a banner at all, which is what it is. ASSUMED, like every
     other small-screen decision here: the comp is desktop-only.

     The band gets deeper as the viewport narrows, which is the opposite of
     what a fixed ratio does and the right way round — a letterbox needs
     width to work. The cost is horizontal crop, and it is small: 4/3 shows
     75% of the file's width, 2/1 shows the full frame less a sliver. */
  --homes-banner-ratio:     4 / 3;         /* ASSUMED — phone  */
  --homes-banner-ratio-md:  2 / 1;         /* ASSUMED — tablet */
  --homes-banner-ratio-lg:  1440 / 524;    /* comp             */

  --homes-banner-label-offset: var(--space-14);  /* comp 59 */

  /* ASSUMED — the comp specifies no motion, as everywhere else on the page.
     Half the travel: the photo drifts from -40 to +40 across the band's
     pass through the viewport.

     The photo's extra height is DERIVED from this rather than set beside
     it — 3x the travel, offset by 1.5x — which is the same dependency the
     introduction's --intro-waves-bleed keeps with its orbit radius, and it
     exists for the same reason. Two edges have to stay covered at both
     ends of the drift; a height chosen independently is exactly what lets
     an edge creep into view later. 3x leaves half a travel of margin at
     each extreme, where 2x would sit flush and rely on rounding.

     Gated at --breakpoint-lg as well as the usual two conditions, because
     the drift and a deep band want opposite things from the same file. The
     slack it travels through only exists while the band is SHALLOWER than
     the photograph's own 16/9 — at the comp's letterbox there is 286px of
     it, at the tablet's 2/1 only 48, and at a phone's 4/3 none at all.
     Below 64rem the band is deep by choice, so there is nothing to drift
     through and the photograph simply sits still. */
  --homes-banner-parallax: var(--space-10);

  /* --- ICON block ------------------------------------------------------
     Arched photograph left, copy right. The card rises OUT of this band
     into the banner above it — the only element on the page that crosses a
     band boundary. */
  --icon-card-ratio: 399 / 633;

  /* How far the card's top sits above the slate ground's top edge. Applied
     as a negative margin at --breakpoint-lg only: stacked, the card is
     ~519px tall against a banner only ~136px tall on a phone, so the same
     27% overlap would carry it clear off the top of the banner. */
  --icon-media-overlap: 170px;

  /* Logo plate on the card. Fractions of the comp's 399 x 633 card, not px,
     because the column is proportional — same reasoning as the surfboard in
     section 21. */
  --icon-logo-width:            calc(180 / 399 * 100%);
  --icon-logo-inset-block-end:  calc(52 / 633 * 100%);
  --icon-logo-ratio:            180 / 94;

  /* Column measures. In fr for the reason given in section 21: 399 + 586
     needs a 985px rail before any gap, wider than the 944px available at
     --breakpoint-lg, so fixed columns would overflow the moment the row
     splits. The comp's three measures share the rail in their own ratio and
     land exactly at the full 1140px row. */
  --icon-col-media: 399fr;
  --icon-col-gap:   155fr;
  --icon-col-text:  586fr;

  /* A fourth, empty track. The comp's row runs x102 → x1242, so it is 1140
     wide inside a rail whose right edge is at 1347 — it does NOT fill the
     rail, it is left-weighted, and the 105px of slack on the right is the
     difference. Carrying it as a track keeps the row left-aligned at every
     width without a second wrapper to hang a max-width on.

     The rail itself is the same 1245px the surf band uses, and for the same
     reason: both rows start at x102 in the comp, so sharing the rail is
     what keeps the ICON card's leading edge aligned with the copy column
     two bands above it. Centring a 1140px rail instead would land the row
     at x150 and break that alignment. */
  --icon-col-slack:    105fr;
  --icon-content-max:  1245px;

  /* The stacked cap for BOTH the card and the copy. The comp's 586px copy
     measure lives in --icon-col-text and applies only once the row splits;
     capping the stack at the card's own width instead is what makes the
     pair share an edge and centre as one column, rather than a 399px card
     floating above a 586px block. It also lands the control exactly: the
     CTA is 399 too, so it fills the column instead of stranding at
     two-thirds of it. */
  --icon-card-width: 399px;
  --icon-lead-max:  497px;   /* "Homes by ICON. Luxury 3D-Printed"    */
  --icon-cta-width: var(--cta-width-standard);   /* 399 — section 15 */

  --icon-gap-stack: var(--space-16);  /* ASSUMED — stacked mobile */

  /* Connector arrow. Anchored to the copy column rather than to the card,
     because it points AT the heading — its length then carries it back over
     the card's edge on its own, exactly as the comp draws it. */
  --icon-arrow-width: calc(187.5 / 586 * 100%);
  --icon-arrow-ratio: 191 / 6;   /* the export's own box, not the comp's
                                    187.5 — the arrowhead overhangs the
                                    line's end, so the art is wider than
                                    the vector Figma measures */
  --icon-arrow-gap:   var(--space-8);   /* comp 31.5, copy edge to arrow */

  --icon-pad-block-start:    var(--space-16);  /* ASSUMED — mobile        */
  --icon-pad-block-start-lg: var(--space-30);  /* comp 114                */

  --icon-gap-lead:   var(--space-4);   /* comp 16                         */
  --icon-gap-claims: var(--space-5);   /* comp 19                         */
  --icon-gap-claim:  var(--space-3);   /* between the two claims — see
                                          .icon__claims in styles.css     */
  --icon-gap-cta:    var(--space-10);  /* comp 39                         */

  /* --- Choose your line ------------------------------------------------
     Back on the page's standard rail, unlike the ICON row above: every
     element in this part is centred on 720 in the comp. */
  --lines-intro-max:  475px;
  --lines-row-max:    1116px;  /* comp x162 → x1278, the two frames + gap */
  --lines-cta-width:  var(--cta-width-standard);   /* 399 — section 15 */

  --lines-pad-block-end:    var(--space-20);  /* ASSUMED — mobile */
  --lines-pad-block-end-lg: var(--space-40);  /* comp 153         */

  --lines-gap-block:    var(--space-24);  /* ASSUMED — mobile             */
  --lines-gap-block-lg: var(--space-40);  /* comp 142 — the loosest round
                                             in this section, and the only
                                             one over 7px out. It is empty
                                             vertical space between two
                                             blocks, so 160 reads as the
                                             same interval; a --space-36
                                             step was not worth adding to
                                             the scale for one value.     */
  --lines-gap-intro: var(--space-8);   /* comp 30                         */
  --lines-gap-cards: var(--space-12);  /* comp 47                         */
  --lines-gap-cta:   var(--space-16);  /* comp 65                         */
  --lines-row-gap:   var(--space-12);  /* comp 50 between the two frames  */
  --lines-stack-gap: var(--space-12);  /* ASSUMED — stacked mobile        */

  /* --- Line card -------------------------------------------------------
     An outlined arch with the photograph inset inside it and the caption
     below, still inside the outline — the introduction's arrangement
     rather than the amenities band's, where the outline sits inside the
     media.

     The comp insets the photo 19px left, 17.1px right and 18px top; one
     value serves all three, since the spread is smaller than the hairline
     is thick. */
  --line-card-width:       533px;
  --line-card-inset:       18px;
  --line-card-media-ratio: 497 / 589;
  --line-card-text-max:    431px;

  --line-card-pad-block-end: var(--space-10);  /* comp 40                 */
  --line-card-gap-kind:      var(--space-8);   /* comp 34, photo → kind   */
  --line-card-gap-body:      var(--space-2);   /* comp 7                  */

  /* The "the / STANDALONES" lockup over the photo's foot. The comp draws
     the two lines in 40px boxes overlapping by 5px, for 75px total; the
     display role's 40px plus --type-caption's 37.2px stacks to 77.2px with
     no negative margin needed, which is why the eyebrow reuses the caption
     role rather than earning one of its own. */
  --line-card-title-inset: var(--space-6);   /* comp 22 above the photo's
                                                bottom edge              */

  /* How far up the media the caption scrim reaches, as a share of the media
     box, anchored at its foot. --scrim-bottom ramps across the last quarter
     of whatever box it is given, so stretching the box past 100% drags the
     ramp's start upward: 100% starts it at 75% of the media, 200% at 50%.

     100% is the comp and is right there — a 589px-tall card puts the
     lockup at 82.8% depth, inside the ramp, and the caption measures
     4.07:1 on the Sets photograph and 5.97:1 on the Standalones.

     It is NOT right on a short card. Two-across on a tablet the media is
     335px while the lockup is a fixed ~77px, so the same caption starts at
     73% — above where the ramp begins at all — and "the" fell to 2.38:1
     over the Sets photograph's pale grass, under even the 3:1 large-text
     threshold. The lockup does not shrink as fast as the box does, so the
     scrim has to reach further to meet it.

     220% rather than the 200% that first cleared the threshold: at 200%
     the Sets caption measured exactly 3.00:1, which is a pass with no
     headroom at all, and these photographs are client-swappable. */
  --line-card-scrim-reach:    220%;   /* ASSUMED — short cards */
  --line-card-scrim-reach-lg: 100%;   /* comp                  */

  /* "STANDALONES" sets 9.07 times its own font size wide, so it stops
     fitting once the media box drops below ~300px. That happens on a
     phone, and the reset's overflow-wrap: break-word then splits it
     mid-word — "STANDALON / ES" — which is worse than either overflowing
     or shrinking.

     The cap holds the word at roughly 90% of the box instead. It is
     measured against the CARD, not the viewport, because the card is what
     constrains it: stacked cards run to --line-card-width on a tablet,
     where a viewport-keyed step would have shrunk the title for no reason.
     9cqi holds it near 82% of the box. 9.9 was the bare fit — it stopped
     the break but left the word running to both edges of a tablet-width
     card, which reads as overflow even when it isn't. Inert at every width
     the comp covers: it only falls under the display size below a ~330px
     media box, which is a two-across tablet card or a phone. */
  --line-card-title-cap: 9cqi;

  /* Holds the comp's 24px until the title itself drops below it, then
     tracks the title down so it can never end up larger than the word it
     sits over.

     The earlier form was a flat 0.81em, which shrank it in lockstep and
     took it to 20.6px on a tablet card. That is a WCAG threshold change,
     not just a size change: under 24px this stops counting as large text
     and the contrast bar it has to clear over the photograph rises from
     3:1 to 4.5:1 — at exactly the card width where the scrim is working
     hardest. Staying at 24px keeps it on the 3:1 side of that line. */
  --line-card-eyebrow-size: min(var(--type-caption-size), 1em);


  /* ======================================================================
     23. RENTAL PROGRAM — component geometry (Figma node 132:224)

     Namespaced like the sets above. The band's ground is espresso, which is
     the page's fourth surface colour — that role lives in section 2 and its
     .theme-dark-warm utility in styles.css section 5, because a theme class
     has to ship site wide. Nothing else here is new: no type role, no
     radius and no colour had to be added.
     ====================================================================== */

  /* --- Rails -----------------------------------------------------------
     THREE rails here, not the surf and homes bands' two, because the
     stacked state needs one of its own.

     --rental-stack-max is the rail at --breakpoint-md, derived rather than
     measured so it tracks the gutter. Stacked, the row stops widening
     there instead of running out to the full rail, and it matters more in
     this band than in any other because BOTH of the panel's proportions
     are keyed to its width: the copy's measure and the arch's radius. Left
     to the rail the panel reached 943px at the split, where its 341px copy
     column sat stranded at the right of a 670px content box and the arch
     had flattened to a quarter of the width — a dark rectangle with a
     corner rounded off, not an arch.

     Holding the tablet rail from 768px up to the split means the stacked
     composition is designed once and then simply centres, which is the same
     thing capping .icon__copy at the card's width does two sections above.
     ASSUMED, like every other small-screen decision in this band.

     The value itself now lives in section 11 as --layout-stack-max, promoted
     there when the community band needed the same rail for the same reason.
     This alias stays so the section still reads in its own terms. */
  --rental-stack-max: var(--layout-stack-max);   /* 688px */

  /* The other two are split as in the surf and homes bands:

       the label's rule spans x151 → x1290 — the standard 1138px
         --layout-content-max;
       the row below it spans x78 → x1351, which is 1273px and the WIDEST
         rail on the page. It hangs outboard of the rule at both ends: 73px
         on the left, 61px on the right.

     The comp draws that row 5.5px left of the artboard centre (78 against
     89) — an artefact of an absolutely positioned board rather than a
     design decision, so it is centred here, exactly as the surf band's row
     is. The cost is that the CTA's rules land 5.5px right of the label
     rule's end instead of flush with it. */
  --rental-content-max: 1273px;   /* x78 → x1351 */

  /* --- Row proportions -------------------------------------------------
     THREE tracks, and the first row on the page whose items OVERLAP: the
     photograph takes tracks 1-2 and the panel tracks 2-3, so the middle
     track is the 241px both of them cover rather than a gap.

     In fr for the reason given in sections 21 and 22: 1034 + 480 needs a
     1514px rail before any overlap at all, wider than the artboard, so
     fixed columns could never resolve. Sharing the rail in the comp's own
     ratio holds the composition at every width and lands on the comp
     exactly at the full 1273px rail. */
  --rental-col-media:   793fr;   /* photograph, clear of the panel  */
  --rental-col-overlap: 241fr;   /* both                            */
  --rental-col-panel:   239fr;   /* panel, clear of the photograph  */

  /* --- Photograph ------------------------------------------------------
     Two ratios, for the same reason --homes-banner-ratio has three: the
     comp's letterbox is 190px tall on a phone, and the headline set on it
     is a fixed ~168px. ASSUMED, like every small-screen decision here.

     4/3 shows 77% of the file's width, so the cost of the deeper frame is a
     small horizontal crop — the same trade the homes banner makes. */
  --rental-media-ratio:    4 / 3;        /* ASSUMED — phone */
  --rental-media-ratio-md: 1034 / 600;   /* comp            */

  /* Desktop only: the photograph's top sits 28px BELOW the panel's, so the
     two shapes are staggered rather than aligned. Rounded onto the 4px
     scale, as --intro-text-offset and --surf-text-offset are. */
  --rental-media-offset: var(--space-8);   /* comp 28 */

  /* --- Headline --------------------------------------------------------
     Set ON the photograph in the comp. The cap forces the comp's three-line
     break ("YOU OWN IT. / WE RUN IT. YOU / SURF.").

     The inline inset is a FRACTION of the comp's 1034px frame rather than
     its 73px, because the media column is proportional — same reasoning as
     the surfboard in section 21 and the ICON logo plate in section 22. The
     block inset lands on the 4px scale as it is, so it stays a length. */
  --rental-heading-max: 283px;
  --rental-heading-inset-inline: calc(73 / 1034 * 100%);
  --rental-heading-inset-block:  var(--space-12);   /* comp 48 */

  /* ASSUMED — stacked only, where the headline sits ABOVE the photograph
     instead of on it. See .rental__heading in styles.css for why. */
  --rental-gap-heading: var(--space-10);

  /* The headline's own height, derived rather than measured: its inset plus
     the comp's three lines of the display role. Retyping the heading or
     changing that role moves everything below with it. */
  --rental-heading-block: calc(
    var(--rental-heading-inset-block)
    + var(--type-display-size) * var(--line-height-tight) * 3
  );

  /* THE FRAME'S FLOOR while the headline is inside it, and the token that
     keeps that headline legible.

     The comp puts the 168px block above in the top 28% of a 600px frame,
     which is the whole basis of its legibility: espresso reads at 7.4:1 on
     the sky and under 2:1 on the roof below it, and there is no scrim. The
     block is a FIXED height, so holding --rental-media-ratio-md instead of
     that fraction pushes the third line onto the roof as soon as the rail
     narrows — at a 1024px viewport the letterbox is 445px deep and the
     block reaches 38% of it, which renders "YOU SURF." on the roof's dark
     leading edge.

     So the frame holds the FRACTION and gives up the ratio: floored at the
     depth that reproduces the comp's 28% exactly, it grows deeper than
     1034/600 below the comp's own rail and pays for it in horizontal crop —
     the same trade the two ratios above make, and it releases on its own at
     the width where the letterbox is 600px deep anyway, so there is no step.

     The 600 / 168 is the comp's own proportion, not a chosen number. If the
     photograph is ever re-cropped with a different sky depth, this is the
     one value to change. */
  --rental-media-min-depth: calc(var(--rental-heading-block) * 600 / 168);

  /* --- Panel -----------------------------------------------------------
     A bark rectangle with ONE arched corner, and the page's first outline
     that is offset OUTBOARD of the shape it traces rather than inset inside
     it (the hero badge and the amenity card both inset theirs).

     THE ARCH IS HALF THE PANEL'S WIDTH, and holding that share rather than
     the comp's 240px is what keeps the shape reading as an arch.

     --radius-arch-md is 240px — the token whose comment already read
     "top-left, 480px-wide panel", written for this panel before it was
     built — and it is exactly half of that 480. But the panel is nowhere
     near 480 wide at most viewports, and an absolute radius drifts badly
     against it: 88% of the panel at 320px, 25% at the width just below the
     row split, 67% just above it. At the low end the corner swallows the
     whole top edge; at the high end it flattens into a rounded rectangle.

     Stacked, the share is therefore held with a container query. cqi is a
     length, so one value gives a CIRCULAR arc — which a percentage cannot:
     border-radius resolves a percentage per axis, so 50% would be an
     ellipse half the panel wide and half its height tall.

     The query container is .rental__layout, which is the panel's own width
     while stacked because the panel fills the rail. It is NOT its width
     once the row splits, so the desktop state rebinds this to
     --radius-arch-md, where the panel is the comp's 480 and 240px IS the
     half. See .rental__panel in styles.css. */
  --rental-panel-arch: 50cqi;

  /* The comp offsets the outline 10px on the inline axis and 11px on the
     block axis; one value serves both, since the difference is smaller than
     twice the hairline it is drawn with — the same call --amenity-outline-
     inset (6 vs 7) and --line-card-inset (19 / 17.1 / 18) make. */
  --rental-panel-outline-offset: 10px;

  /* Inline padding is the comp's 78 / 61 as fractions of its 480px panel,
     so the copy holds its position in the shape as the panel narrows.

     It is NOT used on a phone: 29% of a 342px panel leaves 28 characters to
     a right-aligned line, which would be the least readable block on the
     page. A flat gutter is used there instead — and ONLY there. The switch
     is at --breakpoint-sm rather than --breakpoint-md because the panel
     reaches --rental-stack-max at 736px, so from 480px up it is already a
     wide shape: a flat 24px gutter left the copy sitting 24px off the edge
     of a 552px panel, which read as text pushed against the wall rather
     than as a margin.

     480px is also where the two are closest — 44 characters flat against
     35 proportional. Either side of it the gap widens, so the measure steps
     as little as it can. */
  --rental-panel-pad-inline:          var(--space-6);          /* ASSUMED — phone */
  --rental-panel-pad-inline-start-sm: calc(78 / 480 * 100%);
  --rental-panel-pad-inline-end-sm:   calc(61 / 480 * 100%);

  /* The comp's 133 above and 190 below the copy are the panel's proportion,
     not padding in the usual sense — the CTA sits two thirds of the way down
     a deliberately tall dark shape.

     133 rounds to 120 and 190 to 200: 13px and 10px out, the loosest rounds
     in this section. Both are empty vertical space inside a 690px shape, so
     neither reads as a different interval, and neither was worth adding a
     --space-33 or --space-48 step to the scale for.

     THE 190 DOES NOT SURVIVE STACKING, and the reason is what it is for.
     The comp's panel is portrait, 480 x 690, and that bottom band is what
     carries it 62px PAST the photograph's bottom edge — it is doing work in
     the overlap, not framing the copy. Stacked there is no photograph to
     pass: the panel is landscape, and the same band became a fifth of a
     wide shape with nothing in it, which read as the panel having been left
     unfinished rather than as a base. Matching the top instead balances a
     shape whose top is already occupied by the arch.

     Both ends drop again on a phone, where the copy alone nearly fills the
     panel and either value is dead weight. That step is at --breakpoint-sm
     with the inline padding above, so the panel changes from a phone box to
     the comp's proportions in ONE move rather than two. */
  --rental-panel-pad-block:        var(--space-16);  /* ASSUMED — phone           */
  --rental-panel-pad-block-sm:     var(--space-30);  /* comp 133, matched at both
                                                        ends while stacked        */
  --rental-panel-pad-block-end-lg: var(--space-50);  /* comp 190                  */

  /* Copy measures. The heading keeps the comp's own box, because a tight
     measure is what gives it its three-line break and a heading's measure
     is a design decision rather than a reading one.

     The body and the control do NOT keep theirs. The comp draws them 336
     and 341 inside a 341px content box — i.e. both simply FILL their
     column, and 336 is five pixels of drawing slack, not a measure. Pinned
     at those numbers they stopped filling the moment the panel grew past
     the comp's width, which is the whole stacked range: at the width just
     below the row split the copy sat in 341px of a 670px content box, with
     the other 329 left as bare bark.

     399 is the page's standard control width — --cta-width-standard, which
     the amenities, surf, ICON and lines tokens all alias — and a
     46-character measure at the body size, so it holds a comfortable
     line as the panel widens. It is inert at and above the comp's rail,
     where the content box is 341 and both fill it exactly as drawn. */
  --rental-subhead-max: 253px;   /* forces the comp's three-line break */
  --rental-body-max:    399px;
  --rental-cta-width:   var(--cta-width-standard);

  --rental-gap-body: var(--space-6);   /* comp 25 */
  --rental-gap-cta:  var(--space-8);   /* comp 33 */

  /* --- Rhythm ----------------------------------------------------------
     Comp offsets rounded onto the 4px scale, as in the bands above. Unlike
     the amenities and homes bands this one is NOT flush at the top — the
     comp leaves 91px of espresso above the label rule. */
  --rental-pad-block:          var(--space-20);  /* ASSUMED — mobile     */
  --rental-pad-block-start-lg: var(--space-24);  /* comp 91              */
  --rental-pad-block-end-lg:   var(--space-40);  /* comp 144             */

  --rental-gap-layout:    var(--space-16);  /* ASSUMED — mobile          */
  --rental-gap-layout-lg: var(--space-20);  /* comp 81, rule → panel top */
  --rental-stack-gap:     var(--space-12);  /* ASSUMED — stacked mobile  */


  /* ======================================================================
     24. COMMUNITY & MAP — component geometry (Figma node 132:241)

     A cream band: a centred strapline, then copy left and the masterplan
     right. Nothing new was needed — no colour, no type role, no radius.

     The Figma frame ALSO contains the enquiry band (section 25 below), which
     is built as its own page section: see the note there. This set covers
     only the cream part.
     ====================================================================== */

  /* --- Rails -----------------------------------------------------------
     THREE rails, and the first band where they nest rather than stack:

       the label's rule spans x151 → x1290 — the standard 1138px rail;
       the strapline is capped inside it, centred on the same axis;
       the row spans x151 → x1347.

     THAT ROW IS NOT A CENTRED RAIL, and it is the first on the page that
     cannot be. Its left edge is the standard rail's and its right edge is
     the page's WIDE 1245px rail — x1347 is the same right edge the surf
     photograph and the ICON row already land on, so it is a real alignment
     rather than drift. Centring the 1196px span between them instead would
     sit 29px left of the comp and pull the copy off the label's rule, which
     is the one alignment this band is careful about.

     So the row is stated as two INSETS rather than one rail — see
     .community__layout in styles.css. Below the width where either rail
     binds, both insets fall back to the gutter and the two converge. */
  --community-rail-end: 1245px;   /* x1347 at 1440 — the page's wide rail */

  /* The comp's strapline box is 511px, and its second line measures 512.8 at
     this size and tracking — so the measured width sits 1.8px INSIDE the
     break point and the line wraps. Same trap --surf-heading-max documents,
     and the same fix: clear it rather than sit on it.

     A generous cap costs nothing here because the strapline is centred, so
     the text looks identical at any cap that fits it, and the <br> holds the
     break from the other direction. */
  --community-tagline-max: 560px;

  /* --- Row proportions -------------------------------------------------
     In fr for the reason given in sections 21-23. The middle track is the
     comp's 30px gap between the copy's box and the map — narrow, because
     the copy's own measure is 336 inside a 372 column, so the visible gap
     to the map is 65px rather than 30. */
  --community-col-copy: 372fr;
  --community-col-gap:   30fr;
  --community-col-map:  794fr;

  /* --- Copy measures ---------------------------------------------------
     The heading's cap is the comp's own box and what forces its three-line
     break. It doubles as the copy column's floor at 64rem — see
     .community__layout — because the comp's 31% column share is narrower
     than this below a 1196px rail.

     THE BODY TAKES TWO VALUES, and the stacked one is WIDER. 336 is the
     comp's box, correct inside a 372px column and wrong outside one: below
     64rem the copy has the whole rail and a 336px measure is 38 characters,
     under the comfortable minimum, sitting in a 688px box with 350px of bare
     cream beside it. 497 reads at 57 characters and closes most of that gap.

     It also puts this band in the same relationship the surf band already
     runs — a 380px heading over a 497px body — rather than inventing one. */
  --community-heading-max:  372px;
  --community-text-max:     497px;   /* ASSUMED — stacked */
  --community-text-max-lg:  336px;   /* comp              */
  /* Was 263, measured off the comp's "Learn More". The label is now
     "Get Pricing & Availability", which does not fit that box. */
  --community-cta-width:    var(--cta-width-pricing);  /* 289 — section 15 */

  /* --- Masterplan ------------------------------------------------------
     The ONE image on the page whose crop is load-bearing: every other
     photograph is atmosphere and can be re-cropped per breakpoint, but this
     is a site plan — a fan of 88 plots around the lagoon — and trimming its
     edges removes homes. So it holds one ratio at every width and is never
     deepened for a phone, which is the opposite of the call
     --homes-banner-ratio and --rental-media-ratio make. */
  --community-map-ratio: 794 / 517;

  /* --- Strapline separators --------------------------------------------
     The comp sets "Homes ○ lagoons ○ amenities" with U+25CB WHITE CIRCLE.
     It is drawn in CSS instead, for three reasons:

       NEITHER WEBFONT HAS THE GLYPH. Measured: U+25CB is 8.09px at 14px in
         neue-haas-grotesk-display, Helvetica Neue, Helvetica, sans-serif
         AND Clash Display — five unrelated families reporting one width,
         which is what font substitution looks like. It would render from a
         system fallback, off the page's metric system, and differently on
         Windows than on a Mac.
       It is punctuation, not content, so a screen reader announcing "white
         circle" three times is noise.
       It is a hairline circle, which CSS draws exactly — the same call
         --section-label-dot-radius makes about its rule and dot.

     ASSUMED, both values: a glyph's drawn size cannot be read off the
     artboard, only its advance width. */
  --community-dot-size: 6px;
  --community-dot-gap:  var(--space-2);

  /* --- Rhythm ----------------------------------------------------------
     Comp offsets rounded onto the 4px scale, as in the bands above. */
  --community-pad-block-start: var(--space-20);  /* comp 80              */
  --community-gap-tagline:     var(--space-10);  /* comp 36              */
  --community-gap-layout:      var(--space-14);  /* comp 56              */
  --community-gap-body:        var(--space-5);   /* comp 19              */
  --community-gap-cta:         var(--space-14);  /* comp 52              */
  --community-stack-gap:       var(--space-12);  /* ASSUMED — stacked    */

  /* Desktop only: the copy column starts 45px below the map's top edge in
     the comp, as --surf-text-offset and --icon do. */
  --community-text-offset: var(--space-12);      /* comp 45              */

  /* Cream left visible between the map's bottom edge and the panel's top.
     The band's full bottom padding is this PLUS --enquiry-overlap, derived
     rather than measured so the visible gap survives a change to the
     overlap — see .community in styles.css. */
  --community-gap-enquiry: var(--space-24);      /* comp 101             */


  /* ======================================================================
     25. ENQUIRY FORM — component geometry (Figma node 132:256)

     THE PAGE'S #availability TARGET. Every CTA built so far points at
     href="#availability" and the fullscreen menu's last item is "See
     Availability"; this is what they have all been pointing at, which is
     why it is its own page section with its own h2 rather than a block
     inside the community band.

     That split is also what the comp's own geometry asks for. Frame 07
     carries a cream rectangle over its top 1159px and NO fill below —
     white, confirmed with the client, not read off the artboard — and the
     panel straddles the join: 297px of it on the cream, 259px on the white.
     Two grounds, two sections, one panel bridging them.

     It is the SECOND element on the page to cross a band boundary, after
     the ICON card, and the first to cross a real section boundary. The
     mechanism is the same: an overlap applied as a negative margin.

     Most of this section's geometry was already tokenised before it was
     built — --layout-panel-max (1257), --radius-md (25), --control-height-lg
     (51), --control-height-sm (43), --radius-xs (4), --type-label-sm and
     --color-surface-form were all derived from this form during the first
     pass over the artboard. Only the offsets below are new.
     ====================================================================== */

  /* How far the panel's top sits above the white ground's top edge. A
     literal, like --icon-media-overlap: it is a measured overlap, not a
     step on the spacing scale. Unlike that one it is NOT gated to desktop —
     the panel is taller than the overlap at every width, so the join always
     lands inside the panel.

     THE VALUE THAT MATTERS IS THE FRACTION, NOT THE PIXELS. The comp's 297
     is 53% of its 556px panel, which is what "the panel straddles the join"
     means — cream over the top half, white under the bottom. Because the
     overlap is absolute and the panel's height follows its content, taking
     two fields out of the form drove the join down the panel to 59% and
     left it sitting low. 233 is that same 53% of the 436px the panel now
     stands at.

     Re-deriving it is only needed when the panel's height changes, which
     means when the FORM's field count changes. The community band's bottom
     padding is stated as gap + overlap for exactly this reason, so the
     visible gap above the panel survives the edit untouched. */
  --enquiry-overlap: 233px;

  /* White left below the panel, before the footer band. */
  --enquiry-pad-block-end: var(--space-30);      /* comp 120 */

  /* The panel's rail, fed into .container--wide. Stacked it holds
     --layout-stack-max, because the community row directly above it is capped
     there too and a panel wider than the masterplan it sits under reads as an
     accident rather than a decision. The comp's own 1257px rail returns with
     the column split. */
  --enquiry-rail:    var(--layout-stack-max);
  --enquiry-rail-lg: var(--layout-panel-max);

  /* --- Panel -----------------------------------------------------------
     The rail is --layout-panel-max via .container--wide, both of which were
     written for this panel.

     From --breakpoint-md the padding is the comp's own: 78 / 115 as
     fractions of its 1257 so the two columns hold their place as the panel
     narrows, and 140 / 85 down the block axis.

     THE FIGURE THAT TRANSFERS IS THE SHARE OF THE PANEL, not the pixels.
     140 + 85 is 40% of the comp's 556px panel. Copying the pixels straight
     across only reproduces that while the form is the comp's height, and it
     is not: two fields came out, the panel lost a row, and 160/80 left 48%
     of a 500px panel as padding — half of it empty, weighted to the top,
     with 152px of it stacked above a headline whose column then sagged.

     96/80 puts the share back at the comp's own 40%. It reads as 1.2:1
     rather than the comp's 1.65:1, which is the honest trade: against a
     shorter form the comp's asymmetry is what makes the top look unfinished,
     and the share is what makes the panel feel like the comp's.

     So this pair tracks the form's height. If fields are ever added back,
     scale it up to hold ~40% — and re-derive --enquiry-overlap with it,
     since the straddle is a fraction of the panel too.

     NEITHER SURVIVES A PHONE, and both fail the way the rental panel's did.
     140 above the copy is a fifth of a 1257px panel and a sixth of a 919px
     one, so on a phone it reads as the panel having been left unfinished
     rather than as a margin. And the inline pair is asymmetric by design —
     the comp's field column is offset — which is invisible at 1257 and looks
     like a mistake at 342, where it puts the stacked fields 10px nearer one
     edge than the other. So the phone gets one flat gutter and one modest
     block value.

     THE TWO PAIRS THEN ARRIVE AT DIFFERENT BREAKPOINTS, and that is the
     point rather than an inconsistency. The inline pair is about where the
     two columns sit across the panel, so it lands at 48rem with the form's
     own reflow. The block pair is the air above a row holding copy AND
     fields, so it waits for 64rem, where that row exists. Applied at 48rem
     it sat entirely above the headline in a still-stacked panel — 120px of
     empty panel over one line of type, which is what "too much top margin
     on tablet" was. The flat phone value covers the whole stacked range
     instead. */
  --enquiry-panel-pad-inline: var(--space-6);   /* ASSUMED — phone */
  --enquiry-panel-pad-block:  var(--space-16);  /* ASSUMED — phone */

  --enquiry-panel-pad-inline-start-md: calc(78 / 1257 * 100%);
  --enquiry-panel-pad-inline-end-md:   calc(115 / 1257 * 100%);
  --enquiry-panel-pad-block-start-md:  var(--space-24);  /* holds the comp's 40% share — see above */
  --enquiry-panel-pad-block-end-md:    var(--space-20);  /* comp  85 */

  /* Column measures, in fr as elsewhere: 260 + 660 needs a 920px row before
     any gap, wider than the panel has at --breakpoint-lg once its padding
     is taken off. */
  --enquiry-col-copy:   260fr;
  --enquiry-col-gap:    144fr;
  --enquiry-col-fields: 660fr;

  --enquiry-title-max: 238px;   /* forces the comp's two-line break */

  /* Applied only from 64rem, where the copy sits in its own column. Stacked
     it is uncapped: 260px broke one 51-character sentence over two lines and
     left it as a stub in the corner of a 582px content box, under a form
     spanning the full width. Uncapped it sets as one line and reads as a
     standfirst to the fields rather than a caption beside them. */
  --enquiry-note-max:  260px;   /* comp */

  --enquiry-gap-note:    var(--space-3);   /* comp 11                    */
  --enquiry-gap-copy:    var(--space-16);  /* ASSUMED — stacked          */

  /* NO --enquiry-copy-offset. The comp starts the copy 35px below the first
     field, and that was a measurement of one title length against one field
     count — two lines of heading beside six fields. Both have since changed,
     and as a fixed value it sagged the column inside its own row. styles.css
     §18 centres the copy against the fields instead, which reproduces the
     comp's offset to within a few pixels at the comp's own field count and
     survives a rewrapped heading or a longer HubSpot form. */

  /* --- Fields ----------------------------------------------------------
     Field height, radius and the submit's height are --control-height-lg,
     --radius-xs and --control-height-sm from section 11. */
  --enquiry-field-gap:        var(--space-5);   /* comp 20 */
  --enquiry-field-pad-inline: var(--space-4);   /* comp 17 */
  --enquiry-gap-submit:       var(--space-6);   /* comp 24 */
  --enquiry-submit-width:     208px;

  /* Select chevron. The asset is the comp's own export with its three
     points transposed to point down: Figma carries the 90° rotation as a
     node transform, so the exported path itself still points right. Sized
     at its own 17 x 9.207 box rather than the comp's 16 x 8, because that
     box is the geometry PLUS the 1px round-cap stroke — rendering the
     export at 16 x 8 would scale the stroke down with it. */
  --enquiry-chevron-width:  17px;
  --enquiry-chevron-height: 9.207px;
  --enquiry-chevron-inset:  var(--space-5);     /* comp 19 */

  /* --- Submit ----------------------------------------------------------
     A FILLED control, unlike every other on the page — .cta is rule-bounded
     with no fill, so this is not a variant of it and is namespaced here.

     Its hover is ASSUMED, as .cta's is, and borrows .cta--dark's solution
     for the same reason: the resting state is already the brightest thing
     in the panel, so it cannot get brighter, and the change has to come
     from hue rather than value. Rust fill with a cream label measures
     5.00:1, clear of AA, and stays inside the control's two colours. */
  --enquiry-submit-fill:         var(--color-cream);
  --enquiry-submit-label:        var(--color-espresso);
  --enquiry-submit-fill-hover:   var(--color-rust);
  --enquiry-submit-label-hover:  var(--color-cream);


  /* ======================================================================
     26. FOOTER — component geometry (Figma node 132:277)

     SITE-WIDE, and the only set from 16 up that is. Everything else down
     here is one band's measurements and can be enqueued with the landing
     template; this becomes footer.php and renders on every page, so it has
     to ship alongside sections 1-15 however it is numbered. It sits here
     rather than in section 15 because that section's test is reuse across
     bands, and this is chrome — a different reason for the same treatment.

     It is numbered with the bands because that is where the Figma frame
     sits, and renumbering would break every reference in styles.css.
     CLAUDE.md's "16+ travel with their template" rule names the exception.

     WHAT footer.php DEPENDS ON, and all of it must be enqueued site wide:
       .theme-dark-warm            styles.css section 5
       .site-footer                styles.css section 19
       --footer-*                  this section
       assets/icons/desperado-wordmark-cream.svg

     That asset is a SECOND copy of the wordmark, and deliberately so. The
     mark's colour is baked into the SVG, so a dark ground needs its own
     export rather than a CSS override — the same rule CLAUDE.md states for
     the emblem and the wave art. Every other use of the mark sits on cream,
     so the original stays espresso and this one is that file with its fills
     swapped; the geometry is identical, so the two can never drift apart.

     Nothing else. No type role, no colour and no radius was needed: the band
     is a flat --color-bg-dark-warm rectangle with one centred wordmark, so
     .theme-dark-warm alone paints it.
     ====================================================================== */

  /* The comp draws the lockup at 234 x 36. Only the width is a token: the
     reset gives every image height: auto, so the height comes from the
     export's own 181 x 28 box and the mark cannot be distorted by a wrong
     pair. 234 against 181/28 resolves to 36.2 — the comp's 36.

     Larger than the header's --logo-wordmark-width (181) on purpose: this is
     the sign-off treatment, and the comp sets it bigger at the same 6.5
     ratio. */
  --footer-wordmark-width: 234px;

  /* Comp 57 above the lockup and 121 below it — NOT centred, and that
     asymmetry is reproduced rather than tidied. See the note in index.html:
     the comp leaves that lower band empty, which is where a copyright and
     legal line will land once the client supplies them. */
  --footer-pad-block-start: var(--space-14);   /* comp  57 */
  --footer-pad-block-end:   var(--space-30);   /* comp 121 */


  /* ======================================================================
     27. THANK YOU PAGE — component geometry
     ASSUMED THROUGHOUT — no comp exists for this page (the Figma file is
     the single landing artboard; see CLAUDE.md "Responsive scope"). Every
     value is ours, derived from the landing page's own rhythm: the band
     wash and arch treatment come from the introduction, the CTA width from
     the shared standard, the paddings from the spacing scale.

     In the theme this is template-thankyou.php — a page the client reaches
     from a HubSpot form redirect — so, like the chrome, this set must be
     enqueued site-wide rather than with the landing template.
     ====================================================================== */

  --thanks-pad-block:     var(--space-16);
  --thanks-pad-block-lg:  var(--space-24);

  /* Stacked gap between the copy and the image card, and the column gap
     once the two sit side by side at --breakpoint-lg. */
  --thanks-gap:           var(--space-12);
  --thanks-gap-lg:        var(--space-20);

  /* The copy column's measure and the arch card's width. 480 is the homes
     band's arched-panel width (--radius-arch-md's pairing), reused here so
     the pair sits in the landing page's own proportions. */
  --thanks-copy-max:      480px;
  --thanks-image-max:     440px;

  /* The card crops whatever the client uploads to a stable portrait, so a
     landscape snapshot cannot flatten the arch. 3:4 sits between the
     intro card (2:3) and the amenity cards (~3:4). */
  --thanks-image-ratio:   3 / 4;

  --thanks-cta-width:     var(--cta-width-standard);


  /* ======================================================================
     28. ERROR 404 PAGE — component geometry
     ASSUMED THROUGHOUT — no comp exists; same provenance as section 27.
     The page sets its status inside the brand's oval badge — the hero
     badge's own device (fill and outline shared through section 15), with
     the espresso emblem on the cream ground and the status where the hero
     puts its wordmark and quote.

     In the theme this is 404.php, which WordPress can serve for any URL —
     another site-wide set.
     ====================================================================== */

  --error-pad-block:      var(--space-20);
  --error-pad-block-lg:   var(--space-30);
  --error-gap:            var(--space-8);

  /* The badge. Wider than the hero's 234 — the numerals below are the
     largest type on the site and need the room — with the hero's own
     emblem-to-lockup gap. */
  --error-badge-width:      300px;
  --error-badge-pad-block:  var(--space-16);
  --error-badge-pad-inline: var(--space-6);
  --error-badge-gap:        var(--space-4);

  /* The status numerals: Clash Medium, one step past everything in the
     size scale (the scale is comp-measured, so this one-off stays
     namespaced here rather than joining it). Leading 1 — a numeral
     lockup, not running text. */
  --error-code-size:      56px;
  --error-code-leading:   1;

  /* Centred copy measure — the community tagline's cap, reused for the
     same one-sentence-at-a-time job. */
  --error-copy-max:       511px;

  --error-cta-width:      var(--cta-width-standard);


  /* ======================================================================
     29. INFO PAGES (PROSE) — component geometry
     ASSUMED THROUGHOUT — no comp exists; same provenance as section 27.
     The default treatment for informational pages the client adds — privacy
     policy, terms, and the like: a centred title over a short rule-and-dots
     ornament (the section label's device without words — the header just
     above already says whose site this is), then a single reading column of
     editor-authored HTML.

     In the theme this is page.php — the default page template — so this
     set ships site-wide too.
     ====================================================================== */

  --info-pad-block:       var(--space-16);
  --info-pad-block-lg:    var(--space-24);

  /* The ornament under the title: its length, the gap above it, and the
     gap from it down to the content. */
  --info-rule-width:      var(--space-30);
  --info-title-gap:       var(--space-8);
  --info-content-gap:     var(--space-12);

  /* The reading measure. ch tracks the body face's own glyph width, so the
     column holds ~60 characters at any size — the readability target —
     where a px cap would drift with the type. */
  --prose-max:            60ch;

  /* Vertical rhythm inside editor-authored content: the base flow step,
     the larger break a new heading opens, and the tighter step between
     list items. */
  --prose-flow:           var(--space-6);
  --prose-heading-gap:    var(--space-12);
  --prose-item-gap:       var(--space-2);

  /* List indent and the quote's inset bar. */
  --prose-indent:         var(--space-6);

  /* Editor-placed imagery keeps the form panel's radius — the softest
     shape in the system that isn't an arch, which would be too much for
     inline photos. */
  --prose-media-radius:   var(--radius-md);

}
