/* ===== FONTS =====
   Self-hosted from R2 rather than fetched from Google. Three reasons, in order
   of how much they cost: a third-party font host is a second DNS lookup, a
   second TLS handshake and a dependency whose downtime is invisible to us; the
   files are served with max-age=31536000, immutable, which Google caps far
   lower; and nothing about a visitor is announced to a third party.

   These are the same subsetted WOFF2 files Google serves - same unicode-range
   splits, so a page still downloads only the latin range it needs - copied to
   c21-branding/fonts/ and served from our own domain. Regenerate them with the
   manifest rather than editing by hand: the file names encode style, weight
   and subset, and a mismatch here fails silently as a fallback face.
   ================= */
@font-face {
  font-family: 'Allura';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://library.c21masters.com/c21-branding/fonts/allura/allura-normal-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Allura';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://library.c21masters.com/c21-branding/fonts/allura/allura-normal-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(https://library.c21masters.com/c21-branding/fonts/inter/inter-normal-300-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(https://library.c21masters.com/c21-branding/fonts/inter/inter-normal-300-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(https://library.c21masters.com/c21-branding/fonts/outfit/outfit-normal-300-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(https://library.c21masters.com/c21-branding/fonts/outfit/outfit-normal-300-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* =====================================================================
   CLASSIC CLEAN — Century 21 Masters agent template
   Mirror of https://library.c21masters.com/global-css/classic-clean.css
   --------------------------------------------------------------------
   neutral ground, Outfit over Inter.

   Derived from "Classic Luxury Clean". One design in three themes: classic-light,
   classic-clean and classic-dark share partials/classic/ and
   global-js/classic.js, and differ only in this file.

   Dropped from the source, because nothing renders them any more:
   STATS, FOOTER. The .svc rules went with the services page.
   Fonts are loaded by head-meta.njk, not here.
   ===================================================================== */

/* ---- Design tokens ------------------------------------------------- */
:root {
  /* surfaces */
  --white:      #FFFFFF;
  --paper:      #F5F7F9;   /* cool off-white — alternating sections */
  --mist:       #E9EEF3;   /* deeper light — hovers, fills */
  --navy:       #112A45;   /* brand navy */
  --navy-2:     #173655;   /* raised navy */
  --navy-deep:  #0B1E33;   /* darkest — footer */

  /* text */
  --ink:        #1A2A3A;   /* primary text */
  --slate:      #516374;   /* muted text on light */
  --slate-dim:  #8090A0;   /* faint text on light */

  /* accent */
  --gold:       #B89150;   /* warm metallic gold */
  --gold-soft:  #D0AE72;   /* gold on dark grounds */
  /* Was #98762F, which is 4.22:1 on white - just under the 4.5:1 its own note
     asks for, and it carries eight small-text usages plus the CTA hover fill,
     where a white label on it was also 4.22:1. #8A6A2A is 5.02:1 both ways. */
  --gold-deep:  #8A6A2A;   /* gold for small text on white */

  /* lines */
  --line:        #E2E7EC;  /* hairline on light */
  --line-strong: #D0D8E0;
  --line-onnavy: rgba(255,255,255,0.14);
  --line-gold:   rgba(184,145,80,0.45);

  /* type */
  --display: "Outfit", "Segoe UI", system-ui, sans-serif;
  --sans:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --script:  "Allura", cursive;

  /* fluid scale */
  /* Was clamp(2.7rem, 1.1rem + 6.6vw, 7.4rem): 96px at 1440, 118px at the
     ceiling. This theme was already the restrained one of the three and it
     stays that way - 76px at 1440 against the other two at 87. */
  --fs-hero:    clamp(2.3rem, 1.15rem + 4vw, 5.25rem);
  --fs-display: clamp(2.2rem, 1.3rem + 3.6vw, 4.8rem);
  --fs-h2:      clamp(1.9rem, 1.3rem + 2.2vw, 3.2rem);
  --fs-h3:      clamp(1.4rem, 1.1rem + 1.1vw, 1.95rem);
  --fs-lead:    clamp(1.05rem, 0.97rem + 0.4vw, 1.25rem);
  --fs-body:    clamp(1rem, 0.97rem + 0.16vw, 1.06rem);
  --fs-label:   0.72rem;

  /* layout */
  --rail: 0px;
  --pad-x: clamp(1.4rem, 5vw, 6rem);
  --maxw: 1280px;
  --nav-h: 86px;

  --radius:    14px;
  --radius-sm: 6px;
  --shadow:    0 26px 60px -34px rgba(17,42,69,0.42);
  --shadow-sm: 0 14px 34px -22px rgba(17,42,69,0.34);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-slow: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---- Reset --------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-h);
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--sans);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.72;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; background: none; border: none; }
ul { list-style: none; }

::selection { background: var(--gold); color: var(--navy); }

/* A focus ring needs 3:1 against what it sits on: --gold is the decorative
   gold and reads 2.91:1 on white, --gold-deep is 5.02:1. */
/* The nav sits over the agent's hero photo, so no single ring colour can be
   relied on: --gold-deep measured 1.30:1 against one of them. The white halo
   outside the ring means whichever tone the photo swallows, the other shows -
   the same two-tone trick Chromium's default ring uses. The photo differs on
   all 582 sites, so a fixed colour cannot win on its own. */
:focus-visible {
  outline: 2px solid var(--gold-deep);
  outline-offset: 3px;
  box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.92);
}
iframe:focus { outline: 2px solid var(--gold-deep); outline-offset: 2px; }

/* ---- Typography helpers ------------------------------------------- */
.display {
  font-family: var(--display);
  font-weight: 500;
  line-height: 1.04;
  letter-spacing: -0.018em;
  color: var(--navy);
}
.display em { font-style: normal; font-weight: 300; color: var(--gold-deep); }

.eyebrow {
  font-family: var(--sans);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-deep);
  display: inline-flex;
  align-items: center;
  gap: 0.8rem;
}
.eyebrow.is-centered { justify-content: center; }

.lead { font-size: var(--fs-lead); color: var(--slate); font-weight: 400; max-width: 46ch; }

/* ---- Layout primitives -------------------------------------------- */
.shell { padding-left: var(--rail); }
.wrap  { width: 100%; max-width: var(--maxw); margin: 0 auto; padding-inline: var(--pad-x); }

.block { padding-block: clamp(4.5rem, 9vw, 8.5rem); }
.block--tight { padding-block: clamp(3rem, 6vw, 5.5rem); }

.section-ivory { background: var(--paper); color: var(--ink); }
.section-ivory .lead { color: var(--slate); }

/* The architectural rail is retired in this theme. */
.rail { display: none; }

/* ---- Dark (navy) grounds keep light text + light hairlines --------- */
.hero, .footer {
  color: rgba(255,255,255,0.86);
  --slate: rgba(255,255,255,0.74);
  --slate-dim: rgba(255,255,255,0.5);
  --line: var(--line-onnavy);
  --line-strong: rgba(255,255,255,0.22);
}
.hero .display, .footer .display { color: #fff; }
.hero .display em, .cta-banner .display em { color: var(--gold-soft); }
.hero .eyebrow, .footer .eyebrow { color: var(--gold-soft); }

/* ---- Navigation ---------------------------------------------------- */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: var(--nav-h);
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: var(--pad-x);
  transition: background 0.45s var(--ease), height 0.45s var(--ease),
              box-shadow 0.45s var(--ease), backdrop-filter 0.45s var(--ease);
}
.nav.is-stuck {
  height: 70px;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  box-shadow: 0 1px 0 var(--line), 0 14px 40px -28px rgba(17,42,69,0.45);
}

.brand { display: flex; align-items: center; }

/* ---- Brand logo lockup (CENTURY 21 MASTERS / luxury) -------------- */
.logo {
  --ls: 1rem;
  display: inline-flex; flex-direction: column; align-items: center;
  line-height: 1; color: var(--navy);
}
.logo__word {
  font-family: var(--display); font-weight: 500;
  font-size: var(--ls); letter-spacing: 0.14em; text-transform: uppercase;
  white-space: nowrap;
}
.logo__masters { font-weight: 700; }
.logo__sub {
  font-family: var(--script); font-weight: 400;
  font-size: calc(var(--ls) * 2.1); line-height: 0.62;
  letter-spacing: 0; text-transform: none;
  margin-top: calc(var(--ls) * 0.12); color: var(--gold);
}
.logo--nav { --ls: 0.76rem; }
.logo--footer { --ls: clamp(1.2rem, 2.4vw, 1.7rem); margin-bottom: 0.2rem; color: #fff; }
.logo--footer .logo__sub { color: var(--gold-soft); }
.logo--pre { --ls: clamp(1.4rem, 4.5vw, 2.5rem); }

.nav__menu { display: flex; align-items: center; gap: clamp(1rem, 1.9vw, 2.3rem); }
.nav__link {
  position: relative;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--slate);
  padding-block: 0.4rem;
  transition: color 0.35s var(--ease);
}
.nav__link::after {
  content: "";
  position: absolute; left: 0; bottom: 0;
  width: 100%; height: 2px; border-radius: 2px;
  background: var(--gold);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.45s var(--ease);
}
.nav__link:hover,
.nav__link[aria-current="page"] { color: var(--navy); }
.nav__link:hover::after,
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

.nav__cta { display: inline-flex; }

.nav__toggle {
  display: none;
  /* 44px is the touch target, not the drawn size: the bars stay 26px wide and
     6px apart, and the extra is padding around them. Matches the rest of the
     Gold set. */
  width: 44px; height: 44px;
  flex-direction: column; justify-content: center; gap: 6px; align-items: flex-end;
}
.nav__toggle span {
  display: block; height: 2px; width: 26px; border-radius: 2px;
  background: var(--navy);
  transition: transform 0.4s var(--ease), opacity 0.3s var(--ease), width 0.4s var(--ease), background 0.3s var(--ease);
}

/* Homepage: the transparent nav sits over the hero photo, and the top of that
   photo is whatever the agent uploaded — a bright sky as often as a dark
   facade. The light-ground colours above (navy lockup, gold script, slate
   links) disappeared into the pale ones, so everything in the bar goes plain
   white while the bar is transparent. Once stuck (white bar) the navy-on-white
   defaults take over again. Matches classic-light.
   :has(.hero) rather than a class on <body>: base.njk emits a bare <body> for
   all 18 templates, so the body.home this block used to be keyed on never
   matched and the whole inversion was dead. The hero is the thing that makes
   the treatment necessary and only the homepage has one, so key on it directly
   and keep the fix inside this stylesheet.
   :not(.is-nav-open) is load-bearing here, not tidiness: this template's burger
   overlay is a LIGHT ground, so white links and a white lockup over it would be
   invisible. The rules below restore navy, but only if these never fire. */
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__link,
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__link:hover,
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__link[aria-current="page"] { color: #fff; }
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__link::after { background: #fff; }
/* The row's own backdrop while the bar is transparent.
   The links go #fff here (rule above) over whatever the hero photograph
   happens to be, and the hero's own scrim is at its thinnest at the top of the
   frame - 0.45 alpha - because it was built to lift the headline further down.
   A pale sky therefore left white text on near-white: measured on the demo,
   the nav row read at about 1.1:1.
   This gradient is 0.62 at the top and gone by the bottom of the bar, so the
   nav always has something to sit on and the hero is untouched below it. It is
   scoped to the transparent state; the solid bar has a real ground of its own.
   z-index: -1 keeps it behind the links and the mark: .nav is position:fixed
   with a z-index, so it is the stacking context this resolves inside. */
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open)::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: calc(100% + 2.5rem);
  background: linear-gradient(to bottom, rgba(11, 10, 8, 0.62) 0%, rgba(11, 10, 8, 0.56) 62%, rgba(11, 10, 8, 0) 100%);
  pointer-events: none;
  z-index: -1;
}

body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__toggle span { background: #fff; }
/* .logo__sub carries its own gold, so inheriting from .logo is not enough. */
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .logo,
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .logo .logo__sub { color: #fff; }

/* Mobile menu open: bar floats above a light overlay */
.nav.is-nav-open .nav__toggle span,
.nav.is-nav-open .logo { color: var(--navy); }
.nav.is-nav-open .nav__toggle span { background: var(--navy); }

/* ---- Buttons ------------------------------------------------------- */
.btn {
  position: relative;
  display: inline-flex; align-items: center; gap: 0.65rem;
  font-family: var(--sans);
  font-size: 0.74rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: #fff; background: var(--navy);
  padding: 1rem 1.85rem; border: 1px solid var(--navy);
  border-radius: var(--radius-sm);
  transition: background 0.4s var(--ease), color 0.4s var(--ease),
              border-color 0.4s var(--ease), transform 0.4s var(--ease), box-shadow 0.4s var(--ease);
}
.btn:hover { background: var(--navy-2); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.btn .btn__icon { width: 15px; height: 15px; transition: transform 0.4s var(--ease); }
.btn:hover .btn__icon { transform: translate(3px, -3px); }

/* gold primary — the booking CTA */
.btn--solid, .nav__cta {
  background: var(--gold); border-color: var(--gold); color: var(--navy);
}
.btn--solid:hover, .nav__cta:hover {
  background: var(--gold-deep); border-color: var(--gold-deep); color: #fff;
}

.btn--ghost { background: transparent; color: var(--navy); border-color: var(--line-strong); }
.btn--ghost:hover { background: var(--navy); border-color: var(--navy); color: #fff; }

/* solid-gold buttons placed on dark grounds */
.hero .btn, .cta-banner .btn {
  background: var(--gold); border-color: var(--gold); color: var(--navy);
}
.hero .btn:hover, .cta-banner .btn:hover { background: #fff; border-color: #fff; color: var(--navy); }

/* text link with sliding underline */
.tlink {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-size: 0.74rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--gold-deep);
  padding-bottom: 4px;
  position: relative;
}
.tlink::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 2px; border-radius: 2px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.5s var(--ease);
}
.tlink:hover::after { transform: scaleX(1); }
.tlink svg { width: 14px; height: 14px; transition: transform 0.5s var(--ease); }
.tlink:hover svg { transform: translateX(4px); }

/* =====================================================================
   HERO
   ===================================================================== */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  /* The copy sits on the centre of the photograph rather than on its floor.
     min-height rather than height is what makes that safe: a headline long
     enough to fill the frame grows the section instead of being centred out
     of the top of it and clipped by the overflow rule below. */
  align-items: center;
  overflow: hidden;

  /* The band at the foot of the hero that belongs to the scroll indicator.
     The indicator is taken out of flow so it can centre on the section (see
     .hero__scroll), and .hero__inner reserves the same band as bottom
     padding - which is what keeps the centred copy off it without either box
     knowing the other's height. The line is a variable for the same reason:
     .hero__scroll i draws it, and the reservation must not be able to drift
     from the thing it is reserving for. */
  --hero-scroll-line: 46px;
  --hero-scroll-gap: clamp(1.25rem, 3.5vh, 2.5rem);
  --hero-scroll-band: calc(var(--hero-scroll-line) + var(--hero-scroll-gap));
}
.hero__media { position: absolute; inset: 0; z-index: -2; overflow: hidden; }
.hero__media img { width: 100%; height: 116%; object-fit: cover; object-position: center; will-change: transform; }
.hero__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(8,22,38,0.93) 0%, rgba(8,22,38,0.80) 30%, rgba(8,22,38,0.31) 64%, rgba(8,22,38,0.55) 100%);
  z-index: 1;
}
.hero__inner {
  width: 100%;
  /* .hero centres this box, so these two paddings are what the centring is
     measured against: they hold the copy clear of the fixed bar above it and
     of the scroll indicator below it, and they come out within a few pixels
     of each other, which is what puts the copy on the centre line rather
     than a little under it.

     The 2rem above the bar goes fluid on a short viewport. At 480px tall the
     fixed value spent 118px of 480 on padding before the eyebrow, which put
     the last line of the paragraph 5px under the fold on classic-clean. 3vh
     reaches the 2rem cap at 667px tall, so nothing changes on a normal phone
     or above - only the short end gets the room back. */
  padding-top: calc(var(--nav-h) + clamp(0.75rem, 3vh, 2rem));
  padding-bottom: calc(var(--hero-scroll-band) + clamp(0.75rem, 3vh, 2rem));
}
.hero__title {
  font-size: var(--fs-hero);
  /* Out from 17ch with the size coming down - see the note on --fs-hero. */
  max-width: 21ch;
  /* On .hero__title, not .display, which other headings share. */
  line-height: 1.08;
  margin-block: 1.5rem 2.2rem;
  font-weight: 400;   /* The headline is agent copy, so its longest word is not known at design
     time. A word with no break opportunity has to break rather than run out
     of the hero, which clips it. Same guard as timeless and aurelia. */
  overflow-wrap: break-word;
}
/* Paragraph, then button, down the same left edge. The two were a
   space-between row, which put the button at the far right of the hero on its
   own: it read as a third element rather than as the paragraph's action, and
   it left the empty shelf under the copy that the QA screenshot marks.

   align-items is flex-start rather than the column default - stretch would
   size the button to the width of the hero. The gap is .hero__title's bottom
   margin, so eyebrow, headline, paragraph and button keep one rhythm. The
   phone-width .btn rule still takes the button full width, as it does for
   every standalone CTA on the site. */
.hero__foot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2.2rem;
}
/* The paragraph sits on the photograph. .hero already remaps --slate to
   rgba(255,255,255,0.74) for it, which is close but thin over a bright frame;
   0.88 clears 4.5:1 against the worst of this scrim and still reads below the
   headline's #fff. The scrim is left alone. */
.hero__sub {
  max-width: 42ch;
  color: rgba(255, 255, 255, 0.88);
  line-height: 1.7;
}
/* Bottom centre of the hero, and out of flow to get there.

   In flow it was the second item of .hero's flex row: it sat at the bottom
   right beside the copy, and it took its width out of the copy's measure -
   flex:none and width:auto were there to limit that, and the phone-width rule
   in the responsive block stacked the row to undo it. Positioning it removes
   the cause, so all three of those are gone with it.

   .hero__inner reserves --hero-scroll-band under the copy, which is what
   keeps the two apart at every width - do not put this back in flow without
   taking that reservation out. */
.hero__scroll {
  position: absolute;
  left: 0; right: 0;
  bottom: var(--hero-scroll-gap);
  /* .wrap caps this box at --maxw and centres it with auto margins, which is
     under-defined for an absolutely positioned box spanning left to right and
     is resolved differently by different engines. So the cap comes off and
     the label is centred by justify-content instead; .wrap's padding-inline
     still keeps it off the edge of a phone. */
  max-width: none;
  display: flex; align-items: center; justify-content: center; gap: 0.8rem;
  font-size: 0.62rem; font-weight: 500; letter-spacing: 0.28em; text-transform: uppercase;
  color: rgba(255,255,255,0.7);
}
.hero__scroll i {
  display: block; width: 2px; height: var(--hero-scroll-line); border-radius: 2px;
  background: linear-gradient(var(--gold), transparent);
  animation: drop 2.4s var(--ease) infinite;
}
@keyframes drop {
  0% { transform: scaleY(0); transform-origin: top; }
  40% { transform: scaleY(1); transform-origin: top; }
  60% { transform: scaleY(1); transform-origin: bottom; }
  100% { transform: scaleY(0); transform-origin: bottom; }
}

/* page header (interior pages) */
.phead { position: relative; padding-top: calc(var(--nav-h) + clamp(3rem, 9vw, 6.5rem)); }
.phead__index { display: none; }
.phead__title { font-size: var(--fs-display); max-width: 18ch; position: relative; z-index: 1; }

/* =====================================================================
   INTRO SPLIT
   ===================================================================== */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 6vw, 6.5rem); align-items: center; }
.split--portrait { grid-template-columns: 0.85fr 1fr; }
.split__media { position: relative; overflow: hidden; border-radius: var(--radius); box-shadow: var(--shadow); }
.split__media img { width: 100%; aspect-ratio: 4/5; object-fit: cover; transition: transform 1.4s var(--ease-slow); }
.split__media:hover img { transform: scale(1.04); }
.split__media--tall img { aspect-ratio: 3/4; }
.split__title { font-size: var(--fs-h2); margin-block: 1.3rem 1.5rem; max-width: 18ch; }
.split__body p + p { margin-top: 1.1rem; }
.split__body { color: var(--slate); }
.split__cta { margin-top: 2.2rem; }

/* corner mark on media */
.framed::before, .framed::after {
  content: ""; position: absolute; width: 26px; height: 26px; z-index: 2;
  border-color: var(--gold); border-style: solid; border-width: 0;
}
.framed::before { top: 16px; left: 16px; border-top-width: 2px; border-left-width: 2px; }
.framed::after  { bottom: 16px; right: 16px; border-bottom-width: 2px; border-right-width: 2px; }

/* =====================================================================
   FEATURED COLLECTION — property cards
   ===================================================================== */
.collection-head {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: 1.5rem;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.collection-head h2 { font-size: var(--fs-h2); max-width: 16ch; margin-top: 1rem; }

.estates { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.6rem, 2.6vw, 2.4rem); }
.estate {
  display: flex; flex-direction: column;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease), border-color 0.5s var(--ease);
}
.section-ivory .estate { background: var(--white); }
.estate:hover { transform: translateY(-6px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.estate__media { position: relative; overflow: hidden; }
.estate__media img { width: 100%; aspect-ratio: 4/3; object-fit: cover; transition: transform 1.4s var(--ease-slow); }
.estate:hover .estate__media img { transform: scale(1.06); }
.estate__no {
  position: absolute; top: 1rem; left: 1rem; z-index: 2;
  width: 38px; height: 38px; display: grid; place-items: center;
  font-family: var(--display); font-size: 0.95rem; font-weight: 600; color: var(--navy);
  background: rgba(255,255,255,0.9); border-radius: 50%;
}
.estate > div:last-child { display: flex; flex-direction: column; flex: 1; padding: clamp(1.5rem, 2.4vw, 2rem); }
.estate__loc { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-deep); }
.estate__name { font-family: var(--display); font-weight: 500; font-size: var(--fs-h3); line-height: 1.1; margin-block: 0.7rem 0.9rem; color: var(--navy); }
.estate__desc { color: var(--slate); font-size: 0.97rem; }
.estate__spec {
  display: flex; flex-wrap: wrap; gap: 1rem 1.4rem;
  margin-block: 1.4rem; padding-top: 1.3rem;
  border-top: 1px solid var(--line);
}
.estate__spec li { display: flex; align-items: center; gap: 0.5rem; font-size: 0.82rem; color: var(--slate); }
.estate__spec svg { width: 17px; height: 17px; color: var(--gold-deep); stroke-width: 1.5; }
.estate__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-top: auto; padding-top: 1.2rem; border-top: 1px solid var(--line);
}
.estate__price { font-family: var(--display); font-weight: 600; font-size: 1.3rem; color: var(--navy); }

/* =====================================================================
   CTA banner — navy panel
   ===================================================================== */
.cta-banner {
  text-align: center; position: relative; overflow: hidden;
  margin-bottom: 50px;
  background: var(--navy);
  color: rgba(255,255,255,0.84);
  border-radius: var(--radius);
  padding: clamp(3rem, 7vw, 6rem) clamp(1.5rem, 5vw, 4rem);
  background-image: radial-gradient(120% 120% at 50% -20%, rgba(184,145,80,0.18), transparent 60%);
}
.cta-banner .eyebrow { color: var(--gold-soft); }
.cta-banner .display { color: #fff; }
.cta-banner .lead { color: rgba(255,255,255,0.78); }
.cta-banner__big { font-size: var(--fs-display); max-width: 20ch; margin: 1.4rem auto 2.4rem; }
.cta-banner .lead { margin-inline: auto; margin-bottom: 0; }

/* =====================================================================
   TESTIMONIALS
   ===================================================================== */
.quotes { display: grid; gap: clamp(1.6rem, 3vw, 2.2rem); }
.quotes--row { grid-template-columns: repeat(3, 1fr); }
.quote {
  padding: clamp(2rem, 3vw, 2.6rem);
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--radius);
  display: flex; flex-direction: column; gap: 1.3rem;
  transition: border-color 0.5s var(--ease), transform 0.5s var(--ease), box-shadow 0.5s var(--ease);
}
.section-ivory .quote { background: var(--white); }
.quote:hover { border-color: var(--line-strong); transform: translateY(-5px); box-shadow: var(--shadow); }
.quote__mark { font-family: var(--display); font-weight: 700; font-size: 3.2rem; line-height: 0.4; color: var(--gold); height: 1.3rem; }
.quote__text { font-family: var(--display); font-size: 1.22rem; line-height: 1.5; font-weight: 400; color: var(--ink); }
.quote__by { margin-top: auto; font-size: 0.68rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--slate); }

/* =====================================================================
   SERVICES
   ===================================================================== */
.services-list { display: grid; gap: clamp(1rem, 2vw, 1.4rem); }
.service {
  position: relative;
  display: grid;
  grid-template-columns: clamp(3rem, 6vw, 6rem) 1fr auto;
  gap: clamp(1.4rem, 4vw, 3.5rem);
  align-items: center;
  padding: clamp(1.8rem, 3vw, 2.6rem) clamp(1.6rem, 3vw, 2.6rem);
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--radius);
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease), border-color 0.5s var(--ease);
}
.service:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.service__no {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(1.6rem, 2.4vw, 2.2rem); line-height: 1;
  color: var(--gold);
}
.service__sub { display: block; font-family: var(--sans); font-size: 0.66rem; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: var(--gold-deep); margin-bottom: 0.7rem; }
.service__name { font-family: var(--display); font-weight: 500; font-size: var(--fs-h3); line-height: 1.08; max-width: 18ch; color: var(--navy); }
.service__desc { color: var(--slate); max-width: 60ch; margin-top: 0.9rem; }
.service__link {
  align-self: center; justify-self: end; flex: none;
  width: 52px; height: 52px; display: grid; place-items: center;
  border: 1px solid var(--line-strong); border-radius: 50%;
  color: var(--navy);
  transition: background 0.5s var(--ease), color 0.5s var(--ease), border-color 0.5s var(--ease), transform 0.5s var(--ease);
}
.service:hover .service__link { background: var(--gold); border-color: var(--gold); color: var(--navy); transform: translate(3px, -3px); }
.service__link svg { width: 19px; height: 19px; stroke-width: 1.5; }

/* feature cards */
/* Two columns, not three. Both pages that render this grid — buyers and
   sellers — carry exactly four cards, so a three-column row left the fourth
   stranded on a row of its own. 2x2 holds at every width down to the
   single-column breakpoint. */
.feature-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(1rem, 2vw, 1.6rem); }
.feature {
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: clamp(2rem, 3vw, 2.6rem);
  display: flex; flex-direction: column; gap: 1rem;
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease), border-color 0.5s var(--ease);
}
.feature:hover { transform: translateY(-5px); box-shadow: var(--shadow); border-color: var(--line-strong); }
.feature__icon {
  width: 52px; height: 52px; display: grid; place-items: center;
  background: var(--mist); border-radius: 12px; margin-bottom: 0.4rem;
}
.feature__icon svg { width: 25px; height: 25px; color: var(--gold-deep); stroke-width: 1.4; }
.feature h4 { font-family: var(--display); font-weight: 500; font-size: 1.4rem; color: var(--navy); }
.feature p { color: var(--slate); font-size: 0.96rem; }

/* =====================================================================
   TIMELINE
   ===================================================================== */
.timeline { display: grid; }
.tl-item {
  --tl-dot: 13px;
  display: grid;
  grid-template-columns: clamp(4rem, 6vw, 5.5rem) 30px minmax(0, 1fr);
  column-gap: clamp(1.4rem, 2.6vw, 2.4rem);
  align-items: stretch;
  padding-block: clamp(1.6rem, 2.8vw, 2.3rem);
}
.tl-year { font-family: var(--display); font-weight: 600; font-size: clamp(1.4rem, 2vw, 1.85rem); line-height: 1.15; color: var(--gold-deep); }
.tl-node { position: relative; }
.tl-node::before {
  content: ""; position: absolute; z-index: 1; top: 6px; left: 50%;
  transform: translateX(-50%);
  width: var(--tl-dot); height: var(--tl-dot); border-radius: 50%;
  background: var(--gold); box-shadow: 0 0 0 5px rgba(184,145,80,0.16);
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease);
}
.tl-node::after {
  content: ""; position: absolute; left: 50%; transform: translateX(-50%);
  top: 0; bottom: 0; width: 2px; background: var(--line-strong);
}
.tl-item:first-child .tl-node::after { top: calc(6px + var(--tl-dot) / 2); }
.tl-item:last-child .tl-node::after { bottom: auto; height: calc(6px + var(--tl-dot) / 2); }
.tl-item:hover .tl-node::before { transform: translateX(-50%) scale(1.25); box-shadow: 0 0 0 7px rgba(184,145,80,0.14); }
.tl-title { font-family: var(--display); font-weight: 500; font-size: clamp(1.3rem, 1.8vw, 1.6rem); line-height: 1.12; margin-bottom: 0.5rem; color: var(--navy); }
.tl-body { color: var(--slate); max-width: 60ch; line-height: 1.65; }

/* affiliations row */
.affil { display: flex; flex-wrap: wrap; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.affil span {
  font-family: var(--display); font-weight: 500; font-size: clamp(1.1rem, 2.2vw, 1.7rem);
  color: var(--slate-dim); letter-spacing: 0.01em;
  transition: color 0.5s var(--ease);
}
.affil span:hover { color: var(--gold-deep); }

/* =====================================================================
   CONTACT
   ===================================================================== */
.contact-grid { display: grid; grid-template-columns: 1.2fr 0.8fr; gap: clamp(2.5rem, 6vw, 5.5rem); }
.field { position: relative; margin-bottom: 2.2rem; }
.field input, .field textarea {
  width: 100%;
  background: var(--white);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-family: var(--sans); font-weight: 400; font-size: 1rem;
  padding: 1.2rem 1rem 0.6rem;
  transition: border-color 0.4s var(--ease), box-shadow 0.4s var(--ease);
}
.field textarea { resize: vertical; min-height: 130px; }
.field label {
  position: absolute; left: 1rem; top: 1rem;
  font-size: 0.78rem; letter-spacing: 0.06em; color: var(--slate-dim);
  pointer-events: none;
  transition: transform 0.35s var(--ease), color 0.35s var(--ease), font-size 0.35s var(--ease);
}
.field input:focus, .field textarea:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 3px rgba(184,145,80,0.14); }
.field input:focus + label,
.field textarea:focus + label,
.field input:not(:placeholder-shown) + label,
.field textarea:not(:placeholder-shown) + label {
  transform: translateY(-0.85rem);
  font-size: 0.62rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--gold-deep);
}
.field input::placeholder, .field textarea::placeholder { color: transparent; }

.form-note { font-size: 0.78rem; color: var(--slate-dim); margin-top: -0.6rem; margin-bottom: 1.6rem; }
.form-status {
  margin-top: 1.3rem; font-size: 0.82rem; letter-spacing: 0.04em;
  color: var(--gold-deep); opacity: 0; transform: translateY(6px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.form-status.is-shown { opacity: 1; transform: translateY(0); }

.contact-detail { display: flex; flex-direction: column; gap: 2rem; }
.cd-item { display: flex; flex-direction: column; gap: 0.35rem; }
.cd-item .label { font-size: 0.66rem; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: var(--gold-deep); }
.cd-item a, .cd-item p { font-family: var(--display); font-weight: 500; font-size: 1.3rem; color: var(--navy); }
.cd-item small { color: var(--slate); font-family: var(--sans); }

.map-frame {
  position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius);
  margin-top: clamp(3rem, 6vw, 5rem);
}
.map-frame iframe {
  width: 100%; height: clamp(300px, 42vw, 460px);
  border: 0; display: block;
  filter: grayscale(0.3) contrast(1.02);
  transition: filter 0.8s var(--ease);
}
.map-frame:hover iframe { filter: grayscale(0); }
.map-frame__tag {
  position: absolute; left: 1.3rem; bottom: 1.3rem; z-index: 2;
  background: var(--navy); border-radius: var(--radius-sm);
  padding: 0.7rem 1.2rem; font-size: 0.64rem; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: var(--gold-soft);
  pointer-events: none; box-shadow: var(--shadow-sm);
}

/* =====================================================================
   BUYER / SELLER GUIDE — two columns
   ===================================================================== */
.guide { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.4rem, 3vw, 2.2rem); }
.guide__col {
  display: flex; flex-direction: column;
  padding: clamp(2rem, 4vw, 3.2rem);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--white);
  transition: border-color 0.5s var(--ease), transform 0.5s var(--ease), box-shadow 0.5s var(--ease);
}
.guide__col:hover { border-color: var(--line-strong); transform: translateY(-6px); box-shadow: var(--shadow); }
.guide__no { font-size: 0.7rem; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-deep); }
.guide__title { font-family: var(--display); font-weight: 500; font-size: var(--fs-h3); margin-top: 0.6rem; color: var(--navy); }
.guide__lead { color: var(--slate); margin-top: 0.9rem; max-width: 38ch; }
.guide__list {
  counter-reset: g; margin-top: 1.7rem; padding-top: 1.7rem;
  border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 1rem;
}
.guide__list li { display: flex; gap: 1rem; align-items: baseline; color: var(--ink); font-size: 0.97rem; line-height: 1.5; }
.guide__list li::before {
  counter-increment: g; content: counter(g, decimal-leading-zero);
  font-family: var(--display); font-weight: 600; font-size: 0.92rem; color: var(--gold-deep);
  min-width: 1.8em; flex: none;
}
.guide__cta { margin-top: auto; padding-top: 2rem; }

/* =====================================================================
   LISTINGS SYNDICATED TO — partner logo tiles
   --------------------------------------------------------------------
   The real SVGs from c21-branding/syndicated-logos/ in R2, each on its own
   white square tile, monochrome until pointed at: five tiles per row on a
   desktop, four on a tablet, two on a phone, and a short last row centres
   itself. Not a marquee - nothing here scrolls or animates on its own.

   Four things here are not free to change:

   - The tile is white because that is what makes the logos correct, not
     because it is a card. Five of the ten files are raster images inside an
     SVG wrapper, and three of those (Robb Report, Mansion Global, duPont
     Registry) sit on an opaque #FFF plate. The logo multiplies onto the
     tile, and multiply leaves white exactly as it finds it: on a white tile
     the plate vanishes and every brand colour renders true. Any other tile
     colour brings the plates back as pale boxes.

   - isolation on the tile is what that blend needs. mix-blend-mode reaches
     the backdrop of its nearest stacking context, so without it the logo
     blends with whatever is painted below the list - the page canvas, and
     the .reveal fade mid-animation, which is where the plates used to
     flash. Isolating makes the tile's own white the entire backdrop, so
     nothing underneath it matters any more. It is also why this list no
     longer repaints the body background the way it had to before.

   - The rest state may darken and desaturate but must never darken white.
     grayscale plus a little contrast is the whole of it, and each mark
     lands on its own luminance: eight of the ten fall between #000 and
     #6C6C6C, so the wall reads as dark grey. A brightness step would take
     them lower and take the white with them - both the plates and the
     white inside realtor.com's house and the ListHub wordmark - because to
     a filter white and ink are the same channel.

   - The two size limits work together and both are relative to the tile, so
     a tile of any width shows the same composition. max-width keeps a wide
     wordmark inside the tile. max-height is the optical cap height
     (--syn-cap, a share of the tile inner height) divided by that file's ink
     fraction (--syn-fill, set per item in the partial), which is what puts
     every mark on the same optical size - cap them all to one box instead and
     Properstar, duPont Registry and Mansion Global read at half the size of
     their neighbours. Re-measure --syn-fill when a file is replaced; the
     number belongs to the file, not to the partner.

     Equal ink boxes only mean equal cap height while a mark is one line of
     type. A lockup that stacks an icon over its wordmark spends that same box
     on two rows and the gap between them, so its wordmark comes out around
     half the size of its neighbours - CREtech, measured, was 23% of the tile
     wide against a 38-71% band. Those files carry --syn-lift, a multiplier on
     the cap. Unlike fill it is a judgement rather than a measurement, so the
     partial records how each one was arrived at.

     The widest marks (realtor.com, JamesEdition, Properstar) end up limited
     by width rather than by cap height, which is what a square tile does to a
     7:1 wordmark and is preferable to letting it run out of the tile.
   ===================================================================== */
.trust-section { text-align: center; }
.trust {
  --syn-gap: clamp(0.75rem, 1.5vw, 1.1rem);
  /* The optical cap height every mark is normalised to, as a share of the
     tile's inner height rather than of the viewport - so the composition
     inside a tile is the same on a phone as it is on a desktop. */
  --syn-cap: 19%;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--syn-gap);
  max-width: 880px;
  margin: clamp(2rem, 4vw, 3rem) auto 0;
  list-style: none;
}
/* Column count. A basis of (100/n)% less one gap fits exactly n tiles per row
   and can never fit n+1, whatever width the list ends up with, so the count
   holds without measuring anything. These two overrides belong next to the
   rule they change rather than in the responsive block at the end of the
   file: the count is the layout here, not a tweak to it. max-width is a
   ceiling on the tile, not a width: without it a 4-column tablet row makes
   tiles half again as large as the 5-column desktop ones, since each track is
   a share of the row. A cap narrows a tile, so it is the one change here that
   could in principle fit an extra one on a line: at 168px it cannot, at any
   width, for any of the three counts. Re-check that if you raise it. */
.trust__item { flex: 0 0 calc(20% - var(--syn-gap)); max-width: 168px; }
@media (max-width: 880px) { .trust__item { flex-basis: calc(25% - var(--syn-gap)); } }
@media (max-width: 540px) { .trust__item { flex-basis: calc(50% - var(--syn-gap)); } }
.trust__link {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1;
  /* Percentage padding resolves against the tile's own width, so the frame
     around the logo stays the same share of the tile at every breakpoint. */
  padding: 14%;
  /* The card idiom of this theme, at tile scale: .guide__col's surface and
     hairline on the smaller of the two radii. The page ground is white too,
     so the border is what draws the tile - see the note above for why the
     fill itself is not a style choice. */
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  isolation: isolate;
  transition: border-color 0.45s var(--ease), transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}
.trust__logo {
  display: block;
  width: auto; height: auto;
  max-width: 100%;
  max-height: calc(var(--syn-cap) * var(--syn-lift, 1) / var(--syn-fill, 1));
  filter: grayscale(1) contrast(1.06);
  mix-blend-mode: multiply;
  transition: filter 0.45s var(--ease);
}
/* Brand colour on hover and on keyboard focus, so the reveal is not
   pointer-only. Both filter lists name the same functions in the same order,
   which is what lets the colour ease in rather than snap. The focus ring is
   the global :focus-visible rule - it sits 3px outside the tile with a 5px
   halo, which is why --syn-gap does not go below 0.75rem. */
.trust__link:hover,
.trust__link:focus-visible {
  border-color: var(--line-strong);
  transform: translateY(-4px);
}
/* The lift shadow is on hover alone. It is the same property the global
   :focus-visible rule uses for its light halo, and a tile is more specific
   than that rule, so naming it here as well would quietly drop the halo from
   the keyboard ring. */
.trust__link:hover { box-shadow: var(--shadow-sm); }
.trust__link:hover .trust__logo,
.trust__link:focus-visible .trust__logo { filter: grayscale(0) contrast(1); }

/* Agent credential line (About bio) */
.agent-id { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem 1rem; margin-top: 1.4rem; }
.agent-id__name { font-family: var(--display); font-weight: 600; font-size: 1.5rem; line-height: 1; color: var(--navy); }
.agent-id__role { font-size: 0.72rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--slate); }
.agent-id__dre {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--gold-deep);
  border: 1px solid var(--line-gold); border-radius: 100px;
  padding: 0.36rem 0.85rem;
}

/* =====================================================================
   SCROLL REVEAL
   ===================================================================== */
.reveal { opacity: 0; transform: translateY(30px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }
.reveal[data-delay="1"] { transition-delay: 0.1s; }
.reveal[data-delay="2"] { transition-delay: 0.2s; }
.reveal[data-delay="3"] { transition-delay: 0.3s; }
.reveal[data-delay="4"] { transition-delay: 0.4s; }

.draw { transform: scaleX(0); transform-origin: left; transition: transform 1.1s var(--ease-slow); }
.draw.is-in { transform: scaleX(1); }

.clip { clip-path: inset(0 0 100% 0); transition: clip-path 1.3s var(--ease-slow); }
.clip.is-in { clip-path: inset(0 0 0% 0); }

[data-stagger].reveal { opacity: 1; transform: none; }
[data-stagger] > * {
  opacity: 0; transform: translateY(14px) scale(0.99);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
[data-stagger].is-in > * { opacity: 1; transform: none; }
[data-stagger].is-in > *:nth-child(1) { transition-delay: 0.05s; }
[data-stagger].is-in > *:nth-child(2) { transition-delay: 0.12s; }
[data-stagger].is-in > *:nth-child(3) { transition-delay: 0.19s; }
[data-stagger].is-in > *:nth-child(4) { transition-delay: 0.26s; }
[data-stagger].is-in > *:nth-child(5) { transition-delay: 0.33s; }
[data-stagger].is-in > *:nth-child(6) { transition-delay: 0.40s; }

/* Fail-safe without JS: never hide content. */
html:not(.js) .reveal,
html:not(.js) .draw,
html:not(.js) .clip,
html:not(.js) [data-stagger] > * { opacity: 1 !important; transform: none !important; clip-path: none !important; }
html:not(.js) .preloader { display: none !important; }

/* =====================================================================
   PRELOADER
   ===================================================================== */
.preloader {
  position: fixed; inset: 0; z-index: 200;
  background: var(--white);
  display: grid; place-items: center;
  transition: opacity 0.8s var(--ease), visibility 0.8s var(--ease);
}
.preloader.is-done { opacity: 0; visibility: hidden; }
.preloader__mark { text-align: center; }
.preloader__mark .w { font-family: var(--display); font-weight: 500; font-size: clamp(2.2rem, 8vw, 4.4rem); letter-spacing: 0.04em; overflow: hidden; color: var(--navy); }
.preloader__mark .w span { display: inline-block; transform: translateY(110%); animation: rise 0.9s var(--ease) forwards; }
.preloader__mark .w span:nth-child(2){ animation-delay: 0.07s; }
.preloader__mark .w span:nth-child(3){ animation-delay: 0.14s; }
.preloader__mark .w span:nth-child(4){ animation-delay: 0.21s; }
@keyframes rise { to { transform: translateY(0); } }
.preloader__mark .logo { animation: logo-in 1s var(--ease) both; }
@keyframes logo-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
.preloader__line { height: 2px; border-radius: 2px; background: var(--gold); margin: 1.4rem auto 0; width: 0; animation: grow 1.1s 0.3s var(--ease) forwards; }
@keyframes grow { to { width: 100%; } }

/* =====================================================================
   RESPONSIVE
   ===================================================================== */
/* Burger overlay — the overflow menu, and the phone menu, and the same thing
   at every width in between. This used to be @media (max-width: 1024px), one
   number standing in for 586 different agent-name lengths since the wordmark
   shares this row. global-js/nav-fit.js keeps as many items inline as the row
   can hold and nests the rest in here. */
.nav-panel {
  position: fixed;
  top: 0; left: 0; width: 100%;
  /* Height follows the contents rather than filling the viewport. It holds
     nine items on a phone and sometimes one on a desktop, and a full-screen
     overlay containing a single word reads as a broken page. */
  height: auto;
  max-height: 100svh;
  /* A drawer from the right rather than a full-screen overlay dropping from
     the top. Width is half the window from 442px up and the whole window at
     441 and below; height fills, which it can now do because the panel holds
     either the entire nav or nothing rather than a single leftover link. */
  top: 0; right: 0; left: auto;
  width: 50%;
  height: 100svh;
  /* The top also clears the close button. The bar keeps its X above this drawer
     (z-index 70 against 65), and on a 480px-tall screen the 4rem floor of the
     figure below put the mark level with it - measured, 1px of overlap at
     320x480 and 3px of clearance at 320x568, which is not a margin.
     max() leaves the drawer's own rhythm alone wherever it is already generous
     enough and lifts only the short end, so nothing changes above ~550px tall. */
  padding-top: max(clamp(4rem, 12vh, 8rem), calc(var(--nav-h) + 0.5rem));
  padding-bottom: clamp(4rem, 12vh, 8rem);
  padding-inline: clamp(1.5rem, 4vw, 2.5rem);
  overflow-y: auto;
  display: flex;
  flex-direction: column; justify-content: flex-start;
  gap: 2.2rem;
  background: var(--white);
  transform: translateX(100%);
  opacity: 0;
  /* visibility carries a delay so it flips instantly on open and only once
     the slide has finished on close; without it the panel disappears on the
     first frame of closing and the animation is never seen. */
  transition: transform 0.45s var(--ease),
              opacity 0.45s var(--ease),
              visibility 0s linear 0.45s;
  z-index: 65;
  /* The overlay sits off-screen rather than hidden, so without this its links
     stay in the tab order while it is closed. */
  visibility: hidden;
}
/* No auto margins here, and that is the point.
   The panel used to centre its contents with justify-content, which put the
   overflow at both ends and made the top half unreachable. Auto margins fixed
   the reachability but kept the drift: the mark landed 32px from the panel top
   on a short screen and 180px on a tall one, and on a tall drawer it left a
   168px void between the mark and the first link - the logo read as stranded
   rather than as the head of the menu.
   So the stack is simply top-aligned: the mark sits at the panel's top padding,
   the links follow one gap below it, and both are the same on every screen. It
   costs nothing in emptiness, because the panels that could look sparse
   (classic, california, timeless) are height:auto and shrink to their content;
   the two side drawers are full-height by design, where a menu that starts at
   the top is the ordinary thing. justify-content: flex-start above is what
   keeps the top scrollable when the content is taller than the screen. */
.nav-panel.is-open { transform: translateY(0); box-shadow: var(--shadow); visibility: visible; }
.nav-panel.is-open {
  transform: translateX(0);
  opacity: 1;
  box-shadow: var(--shadow);
  visibility: visible;
  transition-delay: 0s;
}

/* Phones take the whole window: half of 400px is a column of menu beside a
   column of page nobody can read. */
@media (max-width: 441px) {
  .nav-panel { width: 100%; }
}
.nav-panel .nav__menu { flex-direction: column; justify-content: center; gap: 2.2rem; }
.nav-panel .nav__link { font-size: 1.05rem; color: var(--slate); }
.nav-panel .nav__link:hover,
.nav-panel .nav__link[aria-current="page"] { color: var(--navy); }
.nav-panel .btn { margin-top: 1rem; }

/* The burger appears only when the overlay has something in it. nav-fit.js
   sets this to "false" when every item fits inline; it is absent until the
   script runs, and absent has to mean visible - that is the state a phone
   loads in, with every item still in the overlay. */
.nav__toggle { display: flex; z-index: 70; }
[data-nav-overflow="false"] .nav__toggle { display: none; }
.nav.is-nav-open .nav__toggle span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
.nav.is-nav-open .nav__toggle span:nth-child(2) { opacity: 0; width: 0; }
.nav.is-nav-open .nav__toggle span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* An inline nav link that wraps to a second line is the thing overflow is
   there to prevent, so it must not be an option the row can take instead. */
[data-nav-fit-inline] .nav__link, [data-nav-fit-inline] .nav__cta { white-space: nowrap; }

/* ---- Nav submenus --------------------------------------------------- */
/* Buyers carries Calculator; Sellers carries Home Valuation and Sold. A group
   is ONE element, so nav-fit.js moves it whole and knows nothing about the
   nesting - all that is left is how it reads in each of the two places it can
   land.

   Inline it is an absolutely positioned dropdown, and that is not a styling
   preference: a submenu in flow would make the group as wide as its widest
   child, nav-fit would measure that width, and the group would nest at every
   screen size - so the dropdown would never once be seen.

   It opens on :hover and on :focus-within, and no script owns either. Focus
   reaches the parent link, which is always visible, before it can reach into
   the submenu, so :focus-within is already true by the time the next Tab
   lands inside. That is what lets the closed state use visibility:hidden and
   keep the submenu out of the tab order until it is on screen. */
.nav__group { position: relative; display: flex; align-items: center; }
.nav__group > .nav__link { display: inline-flex; align-items: center; gap: 0.5em; }
.nav__sub { display: flex; flex-direction: column; }

/* Drawn from borders rather than an icon: Lucide is inlined in
   global-js/classic.js and runs after first paint, so an <i data-lucide> here
   would be an empty box in the bar until that script landed. */
.nav__caret {
  width: 0.34em; height: 0.34em; flex: none;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: translateY(-0.1em) rotate(45deg);
  transition: transform 0.4s var(--ease);
}

/* Inline: the dropdown */
[data-nav-fit-inline] .nav__sub {
  position: absolute;
  top: 100%; left: -1.1rem;
  min-width: max-content;
  margin-top: 0.6rem;
  padding-block: 0.5rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease), visibility 0.35s var(--ease);
  z-index: 75;
}
/* The gap between the label and the card is dead ground the pointer has to
   cross, and crossing it would close the menu. This bridges it. */
[data-nav-fit-inline] .nav__sub::before {
  content: ""; position: absolute; left: 0; right: 0; top: -0.6rem; height: 0.6rem;
}
[data-nav-fit-inline] .nav__group:hover .nav__sub,
[data-nav-fit-inline] .nav__group:focus-within .nav__sub {
  opacity: 1; visibility: visible; transform: translateY(0);
}
[data-nav-fit-inline] .nav__group:hover .nav__caret,
[data-nav-fit-inline] .nav__group:focus-within .nav__caret { transform: translateY(0.08em) rotate(-135deg); }
[data-nav-fit-inline] .nav__sub .nav__link { padding: 0.5rem 1.1rem; }
/* The sliding gold rule belongs to the bar. Inside a card it reads as a border
   on the wrong edge. */
[data-nav-fit-inline] .nav__sub .nav__link::after { display: none; }
[data-nav-fit-inline] .nav__sub .nav__link:hover { background: var(--mist); }

/* The homepage inversion above turns EVERY .nav__link in the bar white so it
   reads over the hero photo. The dropdown is inside that bar but stands on a
   white card of its own, so white there is white on white: a menu that opens
   into nothing. Specificity has to beat that rule's (0,5,1) - these are
   (0,6,1) and (0,7,1). */
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__sub .nav__link { color: var(--slate); }
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__sub .nav__link:hover,
body:has(.hero) .nav:not(.is-stuck):not(.is-nav-open) .nav__sub .nav__link[aria-current="page"] { color: var(--navy); }

/* Panel: an indented block, always open. There is no disclosure to tap - the
   panel exists to show what did not fit, so hiding half of it behind a second
   interaction would defeat it. The overlay centres its links, so the submenu
   is centred under a hairline rather than indented behind a left border. */
[data-nav-fit-panel] .nav__group { flex-direction: column; align-items: center; gap: 1.3rem; }
[data-nav-fit-panel] .nav__sub {
  position: relative;
  align-items: center;
  gap: 1.1rem;
  padding-top: 1.3rem;
}
[data-nav-fit-panel] .nav__sub::before {
  content: "";
  position: absolute; top: 0; left: 50%;
  width: 28px; height: 1px;
  transform: translateX(-50%);
  background: var(--line-gold);
}
[data-nav-fit-panel] .nav__sub .nav__link { font-size: 0.9rem; }
[data-nav-fit-panel] .nav__caret { display: none; }

@media (max-width: 1024px) {
  .estates { grid-template-columns: repeat(2, 1fr); }
  .stats { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 880px) {
  .split, .split--portrait { grid-template-columns: 1fr; gap: 2.5rem; }
  .split__media { order: -1; }
  .estates { grid-template-columns: 1fr; }
  .quotes--row { grid-template-columns: 1fr; }
  .feature-grid { grid-template-columns: 1fr; }
  .guide { grid-template-columns: 1fr; }
  .footer__top { grid-template-columns: 1fr; gap: 2.5rem; }
  .contact-grid { grid-template-columns: 1fr; }

  .service { grid-template-columns: auto 1fr; column-gap: 1.2rem; row-gap: 1.2rem; align-items: center; }
  .service__no { grid-column: 1; grid-row: 1; font-size: 1.4rem; }
  .service__link { grid-column: 2; grid-row: 1; justify-self: end; width: 46px; height: 46px; }
  .service__body { grid-column: 1 / -1; grid-row: 2; }
}

@media (max-width: 540px) {
  .stats { grid-template-columns: 1fr; }
  .tl-item { grid-template-columns: 1fr; row-gap: 0.5rem; padding-block: 1.6rem; }
  .tl-node { display: none; }
  .tl-year { font-size: 1.4rem; }
}

/* ---- Reduced motion ----------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal, .clip, .draw,
  [data-stagger] > * { opacity: 1 !important; transform: none !important; clip-path: none !important; }
  .hero__media img { height: 100%; }
}


/* =====================================================================
   ADDED IN CONVERSION — markup the source template did not have
   classic-clean
   ===================================================================== */

/* Ylopo IDX widget. It renders its own cards; this only reserves the space
   and shimmers until it hydrates, so the page does not jump. */
.ylopo-easy-widget { min-height: 420px; position: relative; }
.ylopo-easy-widget.ylopo-loading::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--paper-2) 25%, var(--card) 37%, var(--paper-2) 63%);
  background-size: 400% 100%;
  animation: classic-shimmer 1.4s ease infinite;
}
.ylopo-easy-widget.ylopo-ready::before { display: none; }
@keyframes classic-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* The source styled .feature h4; the converted markup uses h3, which is the
   correct level under the section's h2. */
.feature h3 {
  font-family: var(--display);
  font-size: 1.45rem;
  font-weight: 400;
}

/* QA build notices. Deliberately visible while the reviews, sold-properties
   and contact-form widgets are being built - an empty band reads as broken
   where a labelled one reads as pending. Remove once all three ship. */
.qa-note {
  display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap;
  max-width: 60ch;
  padding: 1.4rem 1.6rem;
  border: 1px dashed var(--brass);
  border-radius: 2px;
  background: color-mix(in srgb, var(--champagne) 8%, transparent);
}
.qa-note__tag {
  flex: none;
  font-size: 0.62rem; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink);
  background: var(--champagne);
  border-radius: 2px;
  padding: 0.35rem 0.8rem;
}
.qa-note p { font-size: 0.94rem; color: var(--stone); flex: 1 1 22ch; margin: 0; }

@media (max-width: 640px) { .qa-note { flex-direction: column; gap: 0.8rem; } }

/* Mobile CTA width ----------------------------------------------------
   At phone widths a standalone CTA goes full width with its label centred.
   Sized to its own text it leaves a ragged edge against the copy above it,
   and of two stacked buttons the shorter reads as the lesser one purely
   because it is narrower.

   Card actions and widget-injected markup are excluded. Written as a
   max-width override rather than a mobile-first base rule so the desktop
   layout cannot be affected by construction. */
@media (max-width: 640px) {
  .btn { width: 100%; justify-content: center; text-align: center; }
  .community .btn,
  .ylopo-easy-widget .btn { width: auto; }

  /* "Open the full search" is a text link, not a CTA, so it is centred
     without being stretched. fit-content keeps the hover underline tight to
     the label - .tlink::after spans the element, so a full-width link would
     drag a rule across the column. */
  .tlink { display: flex; width: fit-content; margin-inline: auto; }

  /* The hero wants nothing here any more. This block used to stack .hero's
     flex row so the copy got its full measure back from the scroll indicator;
     the indicator is out of flow at every width now, .hero__inner is the only
     item in that row, and there is nothing left to stack. Re-adding a
     flex-direction here would only move the centred copy back to the floor of
     the hero on a phone. */
}

/* Sold page with nothing to show.
   The visitor-facing counterpart to .qa-note: an agent whose Ylopo agent ids
   are not on file yet gets this instead of an empty band. Which of the two
   renders is decided in Nunjucks - the widget has to be absent from the page,
   not hidden here.

   Note this uses --gold/--slate/--line, not the --brass/--champagne/--stone
   that .qa-note above reaches for. This theme never defines those three, so
   those rules silently fall back to inherited values. Do not copy them. */
.sold-empty {
  max-width: 46ch; margin-inline: auto;
  padding: 2rem 1rem;
  text-align: center;
  border-top: 1px solid var(--line, #E2E7EC);
  border-bottom: 1px solid var(--line, #E2E7EC);
}
.sold-empty .sold-empty__title {
  font-family: var(--display, "Segoe UI", system-ui, sans-serif);
  font-size: var(--fs-h3, 1.6rem); font-weight: 400;
  margin: 0 0 0.75rem;
  color: var(--ink, #1A2A3A);
}
.sold-empty p { margin: 0; font-size: var(--fs-body, 1rem); color: var(--slate, #516374); }
.sold-empty a { color: var(--gold, #B89150); text-decoration: underline; text-underline-offset: 0.2em; }
.sold-empty a:hover { color: var(--gold-deep, #8A6A2A); }

/* Sale date on sold cards - OFF unless SHOW_SOLD_DATE is on in
   scripts/feature-flags.mjs, which is what emits data-sold-date on the widget.

   The Ylopo widget puts the date in .gallery-card-b-generate-sort-block, which
   this template's card CSS in the ylopo-grid project hides outright. Overriding
   that !important needs both the flag attribute and this specificity, so the
   rule lives here rather than there - and so the per-template design of the bar,
   when it happens, sits with the rest of this template's tokens.

   :has(.sold-date) is load-bearing, not decoration. For-sale cards reuse the
   same container for "Listed 4 Months Ago", so revealing it unscoped would put
   that strip on every listing card on every home and search page. Only a sold
   card has .sold-date inside.

   Two selectors, not one: the card redesign takes .property-details-wrap out of
   layout with display:none, and on these templates that wrapper holds the sort
   block and nothing else - so the block alone computes to a 0x0 box inside a
   hidden parent. Revealing the wrapper duplicates no content precisely because
   it is otherwise empty; check that still holds before copying this to a new
   template.

   Still wearing the widget's own grey bar styling: designing it per template is
   the work that has to happen before the flag is turned on. */
.ylopo-easy-widget[data-sold-date="true"] .property-details-wrap:has(.sold-date),
.ylopo-easy-widget[data-sold-date="true"] .gallery-card-b-generate-sort-block:has(.sold-date) {
  display: flex !important;
}

/* Backdrop for the nav drawer. Created by the template's script, not by the
   layout, so no markup changes. It exists as much to swallow the click as to
   dim the page: tapping beside an open drawer should dismiss it and nothing
   else, and a dim painted with a shadow on the panel would let that tap fall
   through to a link underneath. Sits one below the drawer. */
.nav-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s var(--ease), visibility 0s linear 0.45s;
  z-index: 64;
}
.nav-backdrop.is-open {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}
