/* =========================================================================
   Emily M. — REALTOR® | Real Estate Website
   =========================================================================
   Palette supplied by Emily, 2026-09: charcoal, hot pink, ivory, soft gold,
   teal. Century 21's own branding is black/white/gold; Emily asked for
   something "more me and a little more colorful," and the teal comes straight
   out of the background of her headshot.

   THE ONE COLOR RULE THAT EXPLAINS THE TOKEN PAIRS BELOW
   -----------------------------------------------------
   Brand pink (#ff4f87) and brand teal (#28c7c7) are text-safe on charcoal —
   4.9:1 and 7.4:1 — and NOT text-safe on ivory or white, where they measure
   2.8:1 and 2.1:1. Both fail AA badly on a light ground.

   So each bright color has a darkened partner, and which one you reach for is
   decided by the surface, not by taste:

     · on DARK grounds  → --pink and --teal are used directly, for text too.
     · on LIGHT grounds → fills, rules and rings stay bright; TEXT switches to
       --pink-ink / --teal-deep / --gold-deep.

   This is the thing that looks fine in a flat mockup and fails in a browser:
   a pink eyebrow on ivory reads beautifully as a picture and is unreadable as
   type. The mockup's pink and teal small-caps therefore render here in the
   -ink/-deep partners. They are a few percent darker and do not read as a
   second color.
   ========================================================================= */

:root {
  /* Ink */
  --ink: #252526;          /* charcoal — headings, nav, body strong */
  --ink-deep: #1a1a1b;     /* deepest charcoal */
  --body: #4a4a4d;         /* running body copy */
  --muted: #6f6f73;        /* labels, secondary */

  /* Brand pink */
  --pink: #ff4f87;         /* fills, underlines, rings — and text on dark */
  --pink-ink: #c9265f;     /* pink as small text on light; small pink pills */

  /* Brand teal */
  --teal: #28c7c7;         /* rules, rings, marks — and text on dark */
  --teal-soft: #a8e6e6;    /* listings band ground */
  --teal-wash: #e4f7f7;    /* faintest teal, for tinted chips */
  --teal-deep: #0f7d7d;    /* teal as text on WHITE or IVORY (4.6:1) */
  /* A third teal, and it earns its place. --teal-deep was chosen against
     ivory; the listings band's ground is --teal-soft, which is far darker
     than ivory, and the same text there measures only 3.6:1. Ground decides
     the token — so text sitting ON the teal band uses this one (5.1:1). */
  --teal-on-band: #0a6363;

  /* Soft gold */
  --gold: #beaf87;         /* hairlines, rings, REALTOR® on dark */
  --gold-deep: #7a6a3f;    /* gold as small text on light (4.8:1 on ivory) */

  /* Grounds */
  --ivory: #f8f6f2;
  --paper: #ffffff;
  --paper-warm: #fdfcfa;   /* EXACTLY the logo PNG's flat background — see
                              .brand-lockup. Do not drift from this value. */
  --line: #e4e0d8;

  --shadow: 0 18px 50px rgba(37, 37, 38, 0.13);
  --shadow-lift: 0 26px 60px rgba(37, 37, 38, 0.16);
  --radius: 14px;
  --max: 1140px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Author `display` rules can otherwise outrank the browser's default
   [hidden] styling — force it to always win. */
[hidden] { display: none !important; }

/* Available to assistive tech, invisible on screen. Used for the /homeinfo
   field labels, which the design shows as placeholders only — the label still
   has to exist, it just isn't painted. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Hidden until keyboard-focused, so mouse/touch users never see it. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--ink);
  color: #fff;
  padding: 12px 20px;
  border-radius: 0 0 8px 0;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

html { scroll-behavior: smooth; }

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color: var(--body);
  background: var(--paper);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  font-optical-sizing: auto;
  /* Several bands below bleed past the container with pseudo-elements and
     absolutely positioned ornament. None of them should be able to start a
     horizontal scroll on a phone. */
  overflow-x: hidden;
}

/* =========================================================================
   TYPE: TRACKING AND LEADING ARE FUNCTIONS OF SIZE
   =========================================================================
   Letterforms do not scale linearly to the eye. The same spacing that looks
   correct at 16px looks visibly loose and disconnected at 60px, because the
   gaps between letters grow with the letters while the eye's tolerance for
   them does not. Display text therefore needs NEGATIVE tracking, and it needs
   more of it the larger it gets. Small text needs the opposite — a touch of
   positive tracking to stay legible, which is why the .eyebrow and
   .section-eyebrow rules below carry +0.2em and are left alone.

   Leading moves inversely for the same reason: a 4rem headline at 1.65 line
   spacing falls apart into separate stripes, and a 1.2rem heading at 1.12 has
   its descenders nearly touching the line beneath.

   The display face is now Playfair Display rather than Cormorant Garamond —
   Emily's mockups are set in a high-contrast serif with much heavier stems.
   Playfair runs larger on the body than Cormorant at the same nominal size,
   so the clamp() maxima below are set against that, not carried over.
   ========================================================================= */
h1, h2, h3 {
  font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
  line-height: 1.12;
  color: var(--ink);
  font-weight: 600;
}

/* Small headings: the ones that were being squeezed by the shared 1.12. */
.about-card h3,
.connect-title { line-height: 1.22; }

a { color: inherit; text-decoration: none; }

.container { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 24px; }

/* The script that ties back to the logo's wordmark. Used for the footer
   lockup and the two handwritten accent lines. Style Script is the closest
   Google Font to the signature in Emily's logo file; if a real webfont of the
   actual logotype ever arrives, this is the only rule that changes. */
.script { font-family: 'Style Script', 'Segoe Script', cursive; font-weight: 400; }

/* =========================================================================
   PRESS FEEDBACK
   =========================================================================
   Two rules govern every interactive element on this site.

   1. FEEDBACK BELONGS ON THE PRESS, NOT THE RELEASE.
      A phone has no cursor and no hover, so before this section existed the
      entire site was silent under a thumb: you tapped "Let's Talk" and
      absolutely nothing happened until the page moved. That gap is where a
      person taps a second time because they assume the first one missed.
      Every :active rule below fires the instant the finger lands.

   2. HOVER MUST BE FENCED OFF FROM TOUCH.
      A tap on iOS Safari triggers :hover and LEAVES IT APPLIED until you tap
      something else. Every hover-lift on this site therefore used to stick —
      you would tap a button and it stayed floating 2px in the air afterwards,
      looking stuck. Hover effects that move an element live inside
      @media (hover: hover), so pointer devices get the lift and touch devices
      get the press instead.

   The scale values are small on purpose (0.97, not 0.9). This is a
   confirmation that the tap registered, not an animation to be admired.
   ========================================================================= */

/* ---------- Buttons ----------
   WHY THE BIG PINK BUTTONS CARRY BIG LABELS
   Brand pink against white measures 3.1:1. That clears WCAG AA for LARGE text
   (which starts at 18.7px bold) and fails it for anything smaller. So the
   primary pills get a 1.2rem/700 label — which is what Emily's mockup draws
   anyway — and the one genuinely small pink control on the site, the nav
   pill, is filled with --pink-ink instead (5.4:1). Keeping brand pink there
   would have meant shipping a button whose own label fails at the size it is
   actually set. */
.btn {
  display: inline-block;
  padding: 15px 30px;
  border-radius: 50px;
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  cursor: pointer;
  border: 1.5px solid transparent;
  transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease,
              color 0.2s ease, border-color 0.2s ease;
}
.btn-primary {
  background: var(--pink);
  color: #fff;
  font-size: 1.2rem;      /* 19.2px — see the note above. Not decorative. */
  font-weight: 700;
  padding: 15px 34px;
  box-shadow: 0 12px 26px rgba(255, 79, 135, 0.32);
}
/* The arrow the mockup puts inside every pink pill. A span rather than a
   character in the label so it can be nudged on hover without moving text. */
.btn-arrow { display: inline-block; margin-left: 8px; transition: transform 0.2s ease; }

/* Solid pill on a colored ground — "View All Listings" on the teal band. */
.btn-outline {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--paper);
  font-weight: 600;
  box-shadow: 0 8px 20px rgba(37, 37, 38, 0.10);
}
/* Outline pill on a light ground — the quieter alternative action. */
.btn-quiet { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-ghost { border-color: rgba(255, 255, 255, 0.6); color: #fff; }
.btn-block { display: block; width: 100%; text-align: center; }

@media (hover: hover) {
  .btn-primary:hover { background: var(--pink-ink); transform: translateY(-2px); }
  .btn-primary:hover .btn-arrow { transform: translateX(4px); }
  .btn-outline:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
  .btn-quiet:hover { background: var(--ink); color: #fff; }
  .btn-ghost:hover { background: rgba(255, 255, 255, 0.12); }
}

/* Press states last, so they outrank the hover rules above on a device that
   has both (a laptop with a touchscreen presses and hovers at once). The
   shorter transition is deliberate — going down should feel immediate, coming
   back up can afford to be gentler. */
.btn:active { transform: scale(0.97); transition-duration: 0.08s; }
.btn-primary:active { background: var(--pink-ink); box-shadow: 0 4px 12px rgba(255, 79, 135, 0.3); }
.btn-outline:active { box-shadow: 0 4px 12px rgba(37, 37, 38, 0.14); }
.btn-quiet:active { background: var(--ink); color: #fff; }
.btn-ghost:active { background: rgba(255, 255, 255, 0.18); }
.btn:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 3px; }

/* ---------- Header / Nav ----------
   OPAQUE, AND FOR A CONCRETE REASON.
   This header used to be a translucent frosted layer. It is now solid
   --paper-warm, because the logo lockup is a raster with no alpha channel and
   a flat #fdfcfa background baked into it. Matching the header ground to that
   exact value is what makes the image's own rectangle invisible — no blend
   mode, no compositing trick, nothing to go wrong on a browser that handles
   mix-blend-mode differently. It also matches the mockup, which draws a solid
   header rather than a frosted one.

   If a transparent PNG or an SVG of the lockup ever arrives, the header is
   free to go translucent again; until then --paper-warm is load-bearing. */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--paper-warm);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
/* The 1px bottom border used to be drawn at all times, including at the top
   of the page where there is nothing beneath the header to separate it from.
   A rule with nothing on the other side of it is just a line. The edge fades
   in only once content has scrolled under the chrome — main.js adds
   .is-scrolled — so the separation appears exactly when it starts carrying
   information. */
.site-header { --logo-h: 84px; }
.site-header.is-scrolled {
  --logo-h: 58px;            /* shrinks once the page moves, so it doesn't eat a phone screen */
  border-bottom-color: var(--line);
  box-shadow: 0 1px 12px rgba(37, 37, 38, 0.06);
}
.nav { display: flex; align-items: center; justify-content: space-between; gap: 20px; min-height: calc(var(--logo-h) + 16px); transition: min-height 0.2s ease; }

/* The logo lockup: script wordmark + REALTOR® + gold rule + pink tagline +
   teal underline, all in one supplied image. Sized by height so the intrinsic
   3:1 ratio does the rest. */
.brand { display: flex; align-items: center; flex: 0 0 auto; }
/* Never above 120px: the file is 240px tall, so more goes soft on 2x screens. */
.brand-lockup { display: block; height: var(--logo-h); width: auto; transition: height 0.2s ease; }

.nav-links { display: flex; align-items: center; gap: 30px; list-style: none; }
.nav-links a { font-size: 0.94rem; font-weight: 500; color: var(--ink); transition: color 0.2s; }
/* --pink-ink, not --pink: this pill's label sits at 0.95rem, well under the
   18.7px where white-on-brand-pink becomes legal. See the .btn note. */
.nav-cta {
  background: var(--pink-ink); color: #fff !important;
  padding: 11px 24px; border-radius: 50px;
  font-weight: 600;
  transition: background 0.2s ease, transform 0.1s ease-out;
}
@media (hover: hover) {
  .nav-links a:hover { color: var(--pink-ink); }
  .nav-cta:hover { background: #ac1e51; }
}
.nav-cta:active { background: #ac1e51; transform: scale(0.97); }
.nav-links a:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 4px; border-radius: 4px; }

.nav-toggle {
  display: none; flex-direction: column; gap: 5px;
  background: none; border: none; cursor: pointer; padding: 8px;
  transition: transform 0.1s ease-out;
}
.nav-toggle:active { transform: scale(0.92); }
.nav-toggle span {
  width: 24px; height: 2px; background: var(--ink); border-radius: 2px;
  transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.2s ease;
}
/* The control shows its own state by becoming the thing it now does. It had
   aria-expanded all along and told screen readers exactly this — sighted
   users were the ones getting no answer. Styling straight off that attribute
   means there is one source of truth for "open" instead of a class that can
   drift out of sync with it. */
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------- The handwritten accent ----------
   "Let's find what's next together." — set in the wordmark's script with a
   drawn pink swoosh beneath it. Decorative in tone but not in content, so it
   is real text, not an image: it scales, it is selectable, and it is read out
   like anything else. */
.script-accent { position: relative; display: inline-block; margin-top: 28px; }
.script-accent-text {
  display: inline-block;
  font-family: 'Style Script', 'Segoe Script', cursive;
  font-size: clamp(1.5rem, 3.4vw, 2rem);
  line-height: 1.25;
  color: var(--ink);
  transform: rotate(-4deg);
  transform-origin: left center;
}
.script-swoosh {
  display: block;
  width: clamp(120px, 22vw, 190px);
  height: auto;
  margin-top: 2px;
  margin-left: 20%;
  color: var(--pink);
}

/* ---------- The curved band divider ----------
   Emily's mockups separate the header from the hero, and the hero from the
   form band, with a soft curve rather than a straight edge. One SVG, flipped
   with scaleY where the curve needs to go the other way. It is ornament, so
   it never takes a pointer event. */
.wave { display: block; width: 100%; height: auto; pointer-events: none; }
.wave-flip { transform: scaleY(-1); }
/* Sits over the top of the band it opens, filled with the colour of the band
   ABOVE it — so it reads as the header spilling down into the hero rather
   than as a shape drawn on the hero. height:auto against a preserveAspect-
   Ratio="none" viewBox means it scales with the page: 56px at desktop width,
   ~15px on a phone, which is the right amount of curve at both. */
.hero-wave { position: absolute; top: 0; left: 0; width: 100%; z-index: 3; }

/* ---------- Hero ---------- */
/* The mockup's hero ground: window light top-centre, a soft green blur low-left
   where the porch planting sits. Gradients only, no asset. A real full-bleed
   porch photo from Emily's photographer would replace this. */
.hero {
  position: relative; overflow: hidden;
  background:
    radial-gradient(42% 55% at 0% 100%, rgba(143, 188, 150, 0.30), transparent 70%),
    radial-gradient(38% 60% at 48% 0%, #fff, transparent 72%),
    var(--ivory);
}
.hero-grid {
  display: grid; grid-template-columns: 1.02fr 0.98fr;
  gap: 40px; align-items: center;
  padding: 60px 0 72px;
}
.hero-copy { position: relative; z-index: 2; }
.eyebrow {
  color: var(--pink-ink); letter-spacing: 0.22em; font-size: 0.74rem;
  font-weight: 600; text-transform: uppercase; margin-bottom: 18px;
}
/* The largest text on the site, so it takes the most negative tracking and
   the tightest leading. */
.hero h1 {
  font-size: clamp(2.5rem, 5.6vw, 4rem);
  line-height: 1.04;
  letter-spacing: -0.022em;
  margin-bottom: 22px;
  text-wrap: balance;
}
.hero-lead { color: var(--body); font-size: 1.1rem; max-width: 30em; margin-bottom: 30px; }

/* ---------- Hero Photo ----------
   Emily's headshot already has the teal she asked for behind her, which is
   where the whole palette came from. The teal panel behind the frame extends
   that ground out to the edge of the page so the photo reads as part of the
   band rather than a cut-out dropped onto it. */
.hero-photo { position: relative; display: flex; justify-content: center; align-items: flex-end; }
.hero-photo::before {
  content: "";
  position: absolute;
  top: 6%; bottom: 0; left: 8%;
  width: 200vw;                     /* runs off the right edge of the page */
  background: linear-gradient(160deg, var(--teal-soft), var(--teal));
  border-radius: 260px 0 0 0;
  z-index: 0;
}
.photo-frame {
  position: relative; z-index: 1;
  width: 100%; max-width: 400px;
  aspect-ratio: 4 / 5;
  border-radius: 220px 220px 20px 20px;
  overflow: hidden;
  background: linear-gradient(160deg, var(--teal), var(--teal-deep));
  box-shadow: var(--shadow-lift);
}
.photo-frame img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }
/* If images/emily.jpg is ever missing, the page falls back to a gold monogram
   rather than a broken-image icon. Documented behaviour — keep it. */
.photo-initials {
  position: absolute; inset: 0;
  display: none; align-items: center; justify-content: center;
  font-family: 'Playfair Display', serif; font-size: 7rem; color: #fff; font-weight: 700;
}
.photo-frame.photo-fallback { background: linear-gradient(160deg, var(--gold), var(--gold-deep)); }
.photo-frame.photo-fallback .photo-initials { display: flex; }

/* ---------- Value strip ----------
   Four columns on ivory, divided by hairlines. The icons are stroked SVG for
   the same reason the emoji came out of the service cards: an emoji is drawn
   by the operating system in its own house style, so Apple picks the colours
   on an iPhone and Google picks them on Android, and it becomes the one place
   on the page where the palette is decided by somebody else. */
.pillars { background: var(--ivory); }
.pillars-grid { display: grid; grid-template-columns: repeat(4, 1fr); }
.pillar {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 4px;
  padding: 40px 22px 44px;
  border-left: 1px solid var(--line);
  transition: transform 0.15s ease-out;
}
.pillar:first-child { border-left: none; }
.pillar-ring {
  width: 76px; height: 76px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 14px;
  transition: transform 0.2s ease;
}
/* The four rings carry the whole palette across one row, which is the fastest
   way to say "this brand has more than two colours in it." */
.ring-teal { background: var(--teal-soft); }
.ring-gold { background: var(--gold); }
.ring-pink { background: #ffd0de; }
.ring-ivory { background: #eee7d8; }
.pillar-icon {
  width: 34px; height: 34px;
  fill: none; stroke: var(--ink); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.pillar-label {
  font-size: 0.8rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--ink);
  line-height: 1.35;
}
.pillar-body { font-size: 0.92rem; color: var(--body); max-width: 22ch; }
@media (hover: hover) {
  a.pillar:hover .pillar-ring { transform: scale(1.06); }
  a.pillar:hover .pillar-label { color: var(--pink-ink); }
}
a.pillar:active { transform: scale(0.98); }
a.pillar:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: -3px; }

/* ---------- Sections ---------- */
.section { padding: 84px 0; scroll-margin-top: 100px; }
.section-tight { padding: 56px 0; }
.section-alt { background: var(--ivory); }
.section-eyebrow {
  color: var(--pink-ink); letter-spacing: 0.2em; font-size: 0.74rem;
  font-weight: 600; text-transform: uppercase; margin-bottom: 12px;
}
.section-eyebrow-teal { color: var(--teal-deep); }
.section-title {
  font-size: clamp(2rem, 4.2vw, 2.9rem);
  line-height: 1.10;
  letter-spacing: -0.018em;
  margin-bottom: 18px;
  text-wrap: balance;
}
.section-lead { color: var(--body); font-size: 1.05rem; max-width: 40em; margin-bottom: 36px; }

/* ---------- About ---------- */
.about-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: 48px; align-items: start; margin-top: 18px; }
.about-text p { margin-bottom: 18px; color: var(--body); font-size: 1.05rem; }
.about-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 32px;
  box-shadow: var(--shadow);
}
.about-card h3 { font-size: 1.5rem; margin-bottom: 18px; }
.fact-list { list-style: none; margin-bottom: 26px; }
.fact-list li { display: flex; flex-direction: column; padding: 12px 0; border-bottom: 1px solid var(--line); }
.fact-list li:last-child { border-bottom: none; }
.fact-list span { font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin-bottom: 2px; }
.fact-list strong { color: var(--ink); font-weight: 600; }

/* ---------- Buy / Sell / Build ----------
   These were one "Services" section holding three cards. They are now three
   anchored sections, because the navigation Emily designed says Buy, Sell and
   Build — and a nav item has to land somewhere real. Same copy, given room. */
.service { scroll-margin-top: 100px; }
.service-grid { display: grid; grid-template-columns: 0.92fr 1.08fr; gap: 48px; align-items: center; }
.service-flip .service-art { order: -1; }
.service-step {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--gold-deep);
  margin-bottom: 10px;
}
.service h2 { font-size: clamp(1.9rem, 3.6vw, 2.5rem); letter-spacing: -0.015em; margin-bottom: 14px; }
.service p { color: var(--body); font-size: 1.06rem; }
.service-rule { width: 56px; height: 3px; background: var(--teal); border-radius: 2px; margin-top: 22px; }
.service-art {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
  color: var(--ink);
}
.service-art svg { width: 30%; height: auto; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; }
/* These are the same 24-unit icons used at 34px in the value strip, drawn here
   at around 170px. Scaling the artwork scales the stroke with it, so a 1.1
   stroke that reads as a hairline in the strip arrives as a 10px slab down
   here — which is what it did before this rule. non-scaling-stroke takes the
   line weight out of the coordinate system entirely: 2px on every screen, at
   every size, so one icon set can serve both places. */
.service-art svg :is(path, circle, rect) {
  vector-effect: non-scaling-stroke;
  stroke-width: 2;
}
.art-teal { background: var(--teal-wash); }
.art-gold { background: #f1ece0; }
.art-pink { background: #ffeaf1; }

/* ---------- Listings band ----------
   Full-bleed teal, as drawn. The container inside keeps its normal max-width;
   only the colour reaches the window edges. */
.listings-band {
  position: relative;
  background: var(--teal-soft);
  padding: 72px 0 80px;
  overflow: hidden;
  scroll-margin-top: 100px;
}
.listings-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 28px; flex-wrap: wrap; margin-bottom: 30px;
  position: relative; z-index: 1;
}
.listings-head .section-eyebrow { color: var(--teal-on-band); }
.listings-head .section-title { margin-bottom: 8px; }
.listings-head .section-lead { margin-bottom: 0; color: #2c5757; }

/* ---------- Listing cards ----------
   WHAT EMILY'S MOCKUP DRAWS THAT IS NOT HERE, AND WHY
   Her card carries "Midlothian | $489,900" under the address. This one does
   not, and that is deliberate rather than an omission.

   The site does not send prices to the browser at all — /api/listings returns
   slug, address, city, image, gallery and detailPath, and nothing else. The
   price is the thing someone trades their contact details for at /homeinfo,
   which is the entire reason that form exists; putting it back on the card
   would hand it to every visitor and leave the form with nothing to offer.
   See the header comment in js/listings.js.

   So the card is address, city, and one link out to /homeinfo. The mockup's
   price line is the one element of her design this deliberately does not
   reproduce. Everything else about the card — the type, the elevation, the
   press behaviour — follows it exactly. */
.listings-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px;
  position: relative; z-index: 1;
}
.listing-card {
  background: var(--paper);
  border-radius: 12px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 10px 30px rgba(31, 90, 90, 0.14);
  transition: box-shadow 0.2s ease, transform 0.12s ease-out;
}
/* One device, not three. This card used to carry a border AND a shadow AND a
   6px hover lift; the photograph is the product, so the frame stays quiet and
   only the elevation responds. */
@media (hover: hover) {
  .listing-card:hover { box-shadow: 0 18px 44px rgba(31, 90, 90, 0.22); }
}
/* The whole card is a link target, so it acknowledges the press itself. The
   scale is deliberately smaller than a button's — this is a large surface,
   and the same 0.97 on a 380px card is a much bigger visual move than it is
   on a 140px button. */
.listing-card:active { transform: scale(0.99); }
.listing-media {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  background: linear-gradient(160deg, var(--teal-deep), var(--ink));
  overflow: hidden;
}
.listing-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.listing-media.no-img::after {
  content: "Photo coming soon";
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255, 255, 255, 0.7); font-size: 0.85rem; letter-spacing: 0.04em;
}
.listing-body { padding: 18px 20px 20px; display: flex; flex-direction: column; flex: 1; }
/* Address first and largest — the mockup's hierarchy, minus its price line. */
.listing-address {
  display: block;
  font-size: 1.12rem; font-weight: 700; color: var(--ink);
  line-height: 1.3;
  transition: color 0.2s ease;
}
.listing-city { margin-top: 4px; color: var(--body); font-size: 0.95rem; }
@media (hover: hover) {
  .listing-address:hover { color: var(--pink-ink); }
}
/* Not a duplicate of the card link: the card opens this home's photos, while
   this goes to /homeinfo to have the actual details emailed over. Deliberately
   quiet — a secondary action, not a second headline.
   margin-top:auto pins it to the bottom of the card so a one-line address and
   a two-line one still end level across a row. */
.listing-link {
  margin-top: auto; padding-top: 14px;
  font-weight: 500; color: var(--muted);
  font-size: 0.87rem; align-self: flex-start;
  transition: color 0.2s ease;
}
.listing-link:hover { color: var(--pink-ink); }
.listings-empty { color: var(--body); font-size: 1.05rem; }
.listings-more-wrap { display: flex; justify-content: center; margin-top: 34px; position: relative; z-index: 1; }
/* On the homepage this control sits up beside the heading rather than under
   the grid, which is where Emily's design puts it. Same button, same id, same
   aria-expanded contract that js/listings.js drives — only the position
   moves, so the centring and top margin come off. It keeps the under-the-grid
   placement anywhere else it is used. */
.listings-head .listings-more-wrap { margin-top: 0; flex: 0 0 auto; }
/* Below the wrap point the heading block and the button stack, and a button
   the full width of the band reads as a section footer rather than a control
   attached to that heading. */
@media (max-width: 700px) {
  .listings-head { gap: 18px; }
  .listings-head .listings-more-wrap { width: 100%; justify-content: flex-start; }
}

/* ---------- The door into /homeinfo ----------
   /homeinfo is built for someone standing in a driveway with a flyer, but it
   cannot only be reachable by typing the URL. This is the version of that door
   for someone browsing at their desk, and it sits under the listings grid
   because that is where the intent already is.

   It is a rule and a line of text, not a banner. Everything loud in this band
   is already spoken for. The teal edge on the left is the whole ornament. */
.homeinfo-prompt {
  display: flex; align-items: center; gap: 28px; flex-wrap: wrap;
  margin-top: 40px; padding: 24px 28px;
  background: var(--paper);
  border-radius: var(--radius);
  border-left: 4px solid var(--teal);
  box-shadow: 0 8px 24px rgba(31, 90, 90, 0.12);
  position: relative; z-index: 1;
}
.homeinfo-prompt-text { flex: 1 1 320px; }
.homeinfo-prompt-title {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: 1.35rem; font-weight: 600; color: var(--ink); line-height: 1.25;
}
.homeinfo-prompt-body { margin-top: 6px; color: var(--body); font-size: 0.95rem; max-width: 56ch; }
.homeinfo-prompt-btn { flex: 0 0 auto; }

/* Below the wrap point the button is the only thing left on its row, and a
   pill floating at the left edge of a full-width block looks dropped rather
   than placed. Let it own the row. */
@media (max-width: 560px) {
  .homeinfo-prompt { padding: 22px 20px; gap: 20px; }
  .homeinfo-prompt-btn { flex: 1 1 100%; text-align: center; }
}

/* ---------- Areas ----------
   Emily's mockup drops the service-area list. It is kept, compressed to a
   single strip: these are the six place names someone types next to "realtor",
   and they are the cheapest local-search signal the site has. */
.area-list { display: flex; flex-wrap: wrap; gap: 12px; list-style: none; }
.area-list li {
  background: var(--teal-wash);
  border: 1px solid #cdeaea;
  padding: 10px 22px;
  border-radius: 50px;
  font-weight: 500;
  font-size: 0.95rem;
  color: #175959;
}

/* ---------- Connect band ----------
   Split: a photo on the left, the invitation on the right. Until Emily sends
   the lifestyle photograph her mockup uses, the left half falls back to a
   teal panel carrying the same three words her framed print does. The <img>
   slot is already wired — dropping a file in is a filename change. */
.connect { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; }
.connect-media {
  position: relative;
  min-height: 440px;
  background: linear-gradient(155deg, var(--teal-soft), var(--teal));
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.connect-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Charcoal, not white — and measured, not guessed. This panel is a gradient
   running from --teal-soft to --teal, and white script on the LIGHT end of it
   comes out at 1.4:1: technically decorative, visibly washed out. Charcoal
   holds 11:1 at the light end and 7.4:1 at the dark one, so it is legible
   across the whole sweep. It also matches Emily's design, where the framed
   print carries dark script rather than reversed. */
.connect-words {
  position: relative;
  font-family: 'Style Script', 'Segoe Script', cursive;
  font-size: clamp(2rem, 4vw, 3rem);
  line-height: 1.35;
  color: var(--ink);
  text-align: center;
}
.connect-copy {
  position: relative;
  background: var(--ivory);
  padding: 76px 60px;
  display: flex; flex-direction: column; justify-content: center;
  overflow: hidden;
}
/* Faint gold line art in the corner, as in the mockup. */
.connect-leaf {
  position: absolute; right: -34px; bottom: -24px;
  width: 270px; color: var(--gold); opacity: 0.34; pointer-events: none;
}
.connect-leaf svg { width: 100%; height: auto; fill: none; stroke: currentColor; stroke-width: 1.2; }
.connect-copy > * { position: relative; z-index: 1; }
.connect-title { font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.018em; margin-bottom: 14px; }
.connect-lead { color: var(--body); font-size: 1.05rem; max-width: 34em; margin-bottom: 28px; }
.connect-cta { align-self: flex-start; }

/* ---------- Contact ---------- */
.section-contact { background: var(--ink); color: #fff; }
.section-contact .section-title { color: #fff; }
/* On charcoal the brand pink and teal are text-safe (4.9:1 and 7.4:1), so the
   eyebrows here use the bright originals rather than the -ink partners. This
   is the surface the rule at the top of this file is talking about. */
.section-contact .section-eyebrow { color: var(--pink); }
.section-contact .section-lead { color: rgba(255, 255, 255, 0.82); }
.contact-grid { display: grid; grid-template-columns: 1fr 1.1fr; gap: 56px; align-items: start; }
.contact-details { list-style: none; margin-top: 28px; }
.contact-details li { padding: 12px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.14); color: rgba(255,255,255,0.92); }
.cd-label { display: block; font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--teal); margin-bottom: 2px; }
.contact-details a { color: #fff; }
.contact-details a:hover { color: var(--teal); }

.contact-form {
  background: var(--paper);
  border-radius: var(--radius);
  padding: 36px;
  box-shadow: var(--shadow);
}
.form-row { margin-bottom: 18px; display: flex; flex-direction: column; }
.form-row label { font-size: 0.82rem; font-weight: 600; color: var(--ink); margin-bottom: 7px; letter-spacing: 0.02em; }
.optional { color: var(--muted); font-weight: 400; }
/* 1rem (16px) is a hard floor, not a style choice: iOS Safari zooms the whole
   viewport whenever a focused input is under 16px, which shoves the layout
   sideways mid-typing and hides the Send button. This form was 0.98rem and did
   exactly that — see the same note in the /homeinfo section below. */
.contact-form input,
.contact-form select,
.contact-form textarea {
  font-family: inherit; font-size: 1rem;
  /* An input's default `size` gives it an intrinsic min-width of roughly 190px
     that it will not shrink below unless told to. Under a grid track that
     minimum propagates upward and widens the track past the viewport — which
     is what made this page scroll sideways at 320-375px. They stretch to the
     row width anyway, so nothing about the look depends on the floor. */
  min-width: 0;
  width: 100%;
  min-height: 48px;
  padding: 13px 15px;
  border: 1.5px solid var(--line);
  border-radius: 10px;
  background: var(--paper-warm);
  color: var(--ink);
  transition: border 0.2s, box-shadow 0.2s;
}
.contact-form input:focus-visible,
.contact-form select:focus-visible,
.contact-form textarea:focus-visible {
  outline: none; border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(40, 199, 199, 0.24);
}
.contact-form textarea { resize: vertical; }
.form-note { text-align: center; color: var(--muted); font-size: 0.8rem; margin-top: 14px; }

/* Brief highlight when a customer clicks a listing and lands on the form */
.contact-form.form-flash { animation: formFlash 1.6s ease; }
@keyframes formFlash {
  0%   { box-shadow: 0 0 0 0 rgba(40, 199, 199, 0.0); }
  25%  { box-shadow: 0 0 0 5px rgba(40, 199, 199, 0.6); }
  100% { box-shadow: var(--shadow); }
}

/* ---------- Listing Detail Page ---------- */
.back-link {
  display: inline-block; margin-bottom: 24px;
  color: var(--muted); font-weight: 500; font-size: 0.9rem;
  transition: color 0.2s ease, transform 0.1s ease-out;
}
@media (hover: hover) { .back-link:hover { color: var(--ink); } }
.back-link:active { color: var(--ink); transform: scale(0.97); }

/* ---- Listing hero: cover photo beside the facts ----
   The facts used to sit BELOW the whole gallery, which on a 29-photo listing
   put the price 2,543px down the page. Cover and facts now share one row. */
.listing-hero { display: grid; grid-template-columns: 1.3fr 1fr; gap: 36px; align-items: start; margin-bottom: 20px; }
.listing-cover { position: relative; }
/* The sentence standing where the price used to be. It is the page's actual
   offer — "ask and Emily emails it" — so it reads as copy, not as an apology
   for a missing field. */
.listing-detail-lead {
  color: var(--body);
  font-size: 1.05rem;
  max-width: 34em;
  margin-top: 14px;
}
.listing-ask-btn { margin-top: 26px; }

/* "View all N photos" — the recognised affordance for a large gallery, and it
   opens the lightbox that was already built. */
.listing-gallery-all {
  position: absolute; right: 14px; bottom: 14px;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(6px);
  color: var(--ink);
  border: none; border-radius: 50px;
  padding: 10px 18px;
  font-family: inherit; font-size: 0.84rem; font-weight: 600;
  cursor: pointer;
  transition: background 0.2s, transform 0.1s ease-out;
}
@media (hover: hover) { .listing-gallery-all:hover { background: #fff; } }
.listing-gallery-all:active { background: #fff; transform: scale(0.96); }
.listing-gallery-all:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 2px; }

.listing-detail-gallery { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 36px; }
.listing-detail-photo {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;
  background: linear-gradient(160deg, var(--teal-deep), var(--ink));
  cursor: pointer;
}
.listing-detail-photo.is-cover { aspect-ratio: 4 / 3; cursor: pointer; }

/* "+24" on the last thumbnail, so the count of hidden photos is visible
   without opening anything. */
.listing-thumb-more {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(37, 37, 38, 0.62);
  color: #fff;
  font-family: 'Playfair Display', serif;
  font-size: 1.9rem; font-weight: 600;
  pointer-events: none;
}
.listing-detail-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.listing-detail-photo.no-img::after {
  content: "Photo coming soon";
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255, 255, 255, 0.7); font-size: 0.85rem; letter-spacing: 0.04em;
}

/* ---------- Lightbox ----------
   The photos live on one long horizontal strip inside a clipping viewport,
   and the strip is moved with a single transform. See the header comment in
   js/listing-detail.js for why it is built this way rather than swapping one
   image's src. Nothing here animates anything but transform and opacity. */
.lightbox { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; }
.lightbox-backdrop {
  position: absolute; inset: 0;
  /* Blurred, not merely dark. Dimming alone flattens the page behind into a
     grey rectangle; blurring keeps it recognisable as the page you will
     return to, which is the difference between "a layer opened on top" and
     "you left". */
  background: rgba(26, 26, 27, 0.84);
  backdrop-filter: blur(24px) saturate(120%);
}
.lightbox-viewport {
  position: relative; z-index: 1;
  width: 100%; height: 86vh; overflow: hidden;
  /* pinch-zoom, not none: the browser must stop claiming horizontal pans
     (otherwise its own swipe-to-navigate fights the gesture), but pinching
     to zoom into a photo is exactly what someone looking at a kitchen wants
     and there is no reason to take it away. */
  touch-action: pinch-zoom;
  cursor: grab;
  /* A drag across a photo would otherwise start a text/image selection. */
  user-select: none;
  -webkit-user-select: none;
}
.lightbox-viewport:active { cursor: grabbing; }
.lightbox-track {
  display: flex; height: 100%;
  /* The transform is written every frame during a drag; tell the compositor
     up front so the first frame isn't the one that pays for the layer. */
  will-change: transform;
}
.lightbox-slide {
  position: relative; flex: 0 0 100%; height: 100%;
  display: flex; align-items: center; justify-content: center; padding: 0 12px;
}
.lightbox-slide img {
  max-width: min(90vw, 1100px);
  max-height: 86vh;
  object-fit: contain;
  display: block;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  -webkit-user-drag: none;
}
.lightbox-slide.is-broken img { visibility: hidden; }
.lightbox-slide.is-broken::after {
  content: "Photo unavailable";
  position: absolute; inset: 20% 15%;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255, 255, 255, 0.75); font-size: 0.9rem; letter-spacing: 0.04em;
  background: linear-gradient(160deg, var(--teal-deep), var(--ink));
  border-radius: var(--radius);
}
.lightbox-counter {
  position: absolute; left: 0; right: 0; bottom: 24px; z-index: 2;
  text-align: center; color: rgba(255, 255, 255, 0.8);
  font-size: 0.82rem; letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.lightbox-close {
  position: absolute; top: 20px; right: 24px; z-index: 2;
  background: none; border: none; color: #fff; font-size: 2rem; line-height: 1;
  cursor: pointer; padding: 8px;
  transition: color 0.2s ease, transform 0.1s ease-out;
}
.lightbox-arrow {
  position: absolute; top: 50%; margin-top: -24px; z-index: 2;
  width: 48px; height: 48px; border-radius: 50%;
  border: none; cursor: pointer;
  background: rgba(255, 255, 255, 0.12); color: #fff;
  font-size: 1.8rem; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.2s ease, opacity 0.2s ease, transform 0.1s ease-out;
}
/* At the first/last photo the arrow stays put and dims, rather than
   disappearing and making the other one jump. */
.lightbox-arrow:disabled { opacity: 0.28; cursor: default; }
@media (hover: hover) {
  .lightbox-close:hover { color: var(--teal); }
  .lightbox-arrow:hover:not(:disabled) { background: rgba(255, 255, 255, 0.24); }
}
.lightbox-close:active { color: var(--teal); transform: scale(0.88); }
.lightbox-arrow:active:not(:disabled) { background: rgba(255, 255, 255, 0.3); transform: scale(0.9); }
.lightbox-prev { left: 20px; }
.lightbox-next { right: 20px; }
.lightbox-close:focus-visible,
.lightbox-arrow:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
body.lightbox-open { overflow: hidden; }

/* =========================================================================
   FOOTER
   =========================================================================
   Charcoal, with the real lockup PNG on a small plate.

   The supplied logo file is a 24-bit PNG with no alpha, a flat #fdfcfa ground
   baked in, and charcoal script, so it cannot sit directly on a dark ground:
   multiply darkens, screen wrecks the gold and the pink, and keying the
   background out by luminance would eat the pink. The plate is
   var(--paper-warm), exactly that ground, so the image rectangle disappears
   the same way it does in the header. This replaces an earlier live-text
   rebuild that Emily rejected: same idea, wrong font and layout.

   If her designer can supply a transparent or reversed (white) SVG/PNG of the
   lockup, drop it straight onto the charcoal and delete the plate.
   ========================================================================= */
.site-footer { background: var(--ink); color: rgba(255, 255, 255, 0.76); padding: 56px 0 40px; }
.footer-inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 44px;
  align-items: center;
}

.footer-lockup { display: inline-block; background: var(--paper-warm); border-radius: 12px; padding: 8px 16px; }
.footer-lockup img { display: block; height: 72px; width: auto; }

.footer-contact { list-style: none; display: flex; flex-direction: column; gap: 12px; }
.footer-contact li { display: flex; align-items: center; gap: 12px; font-size: 0.92rem; }
.footer-contact a { color: rgba(255, 255, 255, 0.88); transition: color 0.2s ease; }
.footer-contact a:hover { color: var(--teal); }
.footer-icon {
  flex: 0 0 auto; width: 19px; height: 19px;
  fill: none; stroke: var(--teal); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}

.footer-right { display: flex; flex-direction: column; align-items: flex-end; gap: 22px; }
.footer-social { display: flex; gap: 12px; list-style: none; }
.footer-social a {
  width: 38px; height: 38px; border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.28);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  transition: border-color 0.2s ease, background 0.2s ease, transform 0.1s ease-out;
}
.footer-social svg { width: 18px; height: 18px; fill: currentColor; }
@media (hover: hover) {
  .footer-social a:hover { border-color: var(--teal); background: rgba(40, 199, 199, 0.16); }
}
.footer-social a:active { transform: scale(0.92); }

.footer-c21 { text-align: right; }
.footer-c21-name { font-size: 1.28rem; font-weight: 700; letter-spacing: 0.02em; color: #fff; }
.footer-c21-sub { font-size: 0.74rem; color: rgba(255, 255, 255, 0.62); }

.footer-legal {
  margin-top: 40px; padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  font-size: 0.78rem; color: rgba(255, 255, 255, 0.55);
}

/* ---------- Responsive ---------- */
@media (max-width: 1000px) {
  .pillars-grid { grid-template-columns: repeat(2, 1fr); }
  /* With two columns the third and fourth cells start a new row, so the
     divider has to move: it belongs between the pair and above the second
     row, not on every cell after the first. */
  .pillar { border-left: none; border-top: 1px solid var(--line); }
  .pillar:nth-child(odd) { border-right: 1px solid var(--line); }
  .pillar:nth-child(-n+2) { border-top: none; }
  .footer-inner { grid-template-columns: 1fr 1fr; gap: 32px; align-items: start; }
  .footer-right { grid-column: 1 / -1; align-items: flex-start; }
  .footer-c21 { text-align: left; }
}

@media (max-width: 860px) {
  .nav-toggle { display: flex; }
  /* The menu used to animate max-height from 0 to a hard-coded 360px. The
     panel is nowhere near 360px tall, so the easing curve was pacing itself
     against a height the menu never reaches: it appeared to arrive early and
     then sit there, and the number would have quietly broken the day Emily
     wanted a sixth nav item.
     --nav-h is measured from the real panel by main.js and re-measured on
     resize, so the curve now runs to the height the menu actually has. The
     fallback below only matters for the one frame before that script runs.
     max-height is a layout property, but this element is position:absolute,
     so nothing in the document reflows behind it — and the slide is carried
     by transform/opacity, which stay on the compositor. */
  .nav-links {
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0;
    background: var(--paper-warm);
    border-bottom: 1px solid transparent;
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    transform: translateY(-8px);
    transition: max-height 0.3s cubic-bezier(0.32, 0.72, 0, 1),
                transform 0.3s cubic-bezier(0.32, 0.72, 0, 1),
                opacity 0.2s ease,
                border-color 0.2s ease;
  }
  .nav-links.open {
    max-height: var(--nav-h, 460px);
    opacity: 1;
    transform: translateY(0);
    border-bottom-color: var(--line);
  }
  .nav-links li { width: 100%; text-align: center; }
  .nav-links li a { display: block; padding: 16px; border-top: 1px solid var(--line); }
  .nav-links li a:active { background: rgba(37, 37, 38, 0.06); }
  .nav-cta { border-radius: 0 !important; }

  /* minmax(0, 1fr) rather than a bare 1fr: `1fr` is minmax(auto, 1fr), and
     that `auto` floor is what let an input's intrinsic width widen the contact
     column past the screen. Zeroing the floor means no single wide descendant
     can push a column past the viewport again. */
  .hero-grid,
  .about-grid,
  .service-grid,
  .listings-grid,
  .connect,
  .contact-grid { grid-template-columns: minmax(0, 1fr); }

  .hero-grid { padding: 36px 0 0; gap: 30px; }
  .hero-photo { order: -1; }
  /* The angled teal panel is a desktop composition. On a phone it becomes a
     plain band behind the photo, because a 260px corner radius on a 340px
     screen is not a corner, it is the whole shape. */
  .hero-photo::before { left: -12vw; width: 130vw; top: 16%; border-radius: 0; }
  .photo-frame { max-width: 300px; }
  .script-accent { margin-bottom: 44px; }

  .service-grid { gap: 26px; }
  .service-art,
  .service-flip .service-art { order: -1; }
  .service-art svg { width: 30%; }
  .connect-media { min-height: 280px; }
  .connect-copy { padding: 52px 24px; }

  .listing-hero { grid-template-columns: 1fr; gap: 24px; }
  .listing-detail-gallery { grid-template-columns: repeat(4, 1fr); gap: 8px; }
  /* 36px of padding on each side of a 320px screen leaves the fields barely
     wider than half the phone. */
  .contact-form { padding: 24px 18px; }
  .listing-ask-btn { display: block; text-align: center; }
  /* margin-top must track half the height — the arrows are centred with a
     negative margin rather than translateY(-50%) so that `transform` stays
     free for the press state. */
  .lightbox-arrow { width: 40px; height: 40px; margin-top: -20px; font-size: 1.4rem; }
  .lightbox-prev { left: 8px; }
  .lightbox-next { right: 8px; }
  .lightbox-close { top: 12px; right: 12px; }

  .footer-inner { grid-template-columns: 1fr; gap: 30px; }
  .footer-right { align-items: flex-start; }
  .site-header { --logo-h: 60px; }
  .site-header.is-scrolled { --logo-h: 46px; }
}

/* ---------- Tablet / large phone ----------
   The site had exactly one site-wide breakpoint, so everything dropped straight
   from three columns to one — ten full-width listing cards is a very long
   scroll to reach the contact form. Two columns holds this range comfortably. */
@media (min-width: 561px) and (max-width: 860px) {
  .listings-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .listing-detail-gallery { grid-template-columns: repeat(2, 1fr); }
  .pillars-grid { grid-template-columns: 1fr; }
  .pillar,
  .pillar:nth-child(odd) {
    border-right: none; border-left: none;
    border-top: 1px solid var(--line);
    padding: 30px 20px;
  }
  .pillar:first-child { border-top: none; }
  .listings-band { padding: 52px 0 60px; }
  .section { padding: 60px 0; }
  .site-header { --logo-h: 52px; }
  .site-header.is-scrolled { --logo-h: 40px; }
  .footer-lockup img { height: 56px; }
  .btn-primary { font-size: 1.1rem; padding: 14px 26px; }
}

/* =========================================================================
   ACCESSIBILITY PREFERENCES
   =========================================================================
   Three independent settings, none of which mean "turn everything off".

   WHAT WAS WRONG HERE ONCE
   This block used to be the usual sledgehammer — `animation-duration: 0.01ms
   !important` on `*, *::before, *::after`. Two problems.

   First, it broke the submit spinner. Down in the /homeinfo section there is a
   rule that slows .hi-spinner to a calm 1.6s rotation for exactly this
   audience. It has no !important, so the universal rule beat it and the
   spinner ran a full rotation every hundredth of a millisecond: a static
   smear. That is the one moment in the entire site where a visitor has tapped
   Send and is waiting to find out whether their lead went through, and the
   only thing telling them the page is still working was switched off for
   people who asked for less motion.

   Second, it is the wrong reading of the setting. Reduced motion is a request
   to avoid VESTIBULAR motion — things flying across the screen, parallax,
   spinning, zooming. It is not a request to remove feedback. Fading, colour
   changes and the contact form's landing highlight all still convey what
   happened without moving anything through space, so they stay.

   MAINTENANCE NOTE: the lists below name selectors one at a time. Any new
   interactive element has to be added here too, or it keeps its squeeze for
   people who explicitly asked for no motion.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* Kill movement through space, keep everything else. Transitions still run,
     but only over properties that change an element in place — nothing is
     left able to interpolate a transform.

     The !important is load-bearing, and is not the same gesture as the rule
     this replaced. `*` is the weakest selector there is, so without it this
     loses to every class in the file and is a comment rather than a rule.
     What separates it from the old blanket is what it protects: that one
     flattened every duration to 0.01ms and took the feedback down along with
     the motion, the submit spinner included. This one restricts WHICH
     properties may move, and deliberately leaves opacity and colour free to
     go on explaining what just happened. */
  *, *::before, *::after {
    transition-property: opacity, color, background-color, border-color, box-shadow, visibility !important;
    animation-play-state: paused !important;
  }

  /* Press feedback becomes a colour shift rather than a squeeze: the tap is
     still confirmed, nothing scales. */
  .btn:active,
  .btn-primary:active,
  .nav-cta:active,
  .nav-toggle:active,
  a.pillar:active,
  .listing-card:active,
  .listing-gallery-all:active,
  .back-link:active,
  .lightbox-close:active,
  .lightbox-arrow:active,
  .footer-social a:active,
  .hi-submit:active,
  .hi-next-btn:active,
  .hi-choice input:active + span,
  .hi-consent input:active + .hi-check { transform: none; }

  .btn-primary:hover,
  .btn-primary:hover .btn-arrow,
  .btn-outline:hover,
  a.pillar:hover .pillar-ring,
  .hi-submit:hover:not(:disabled),
  .hi-next-btn:hover { transform: none; }

  /* The script accent's tilt is a static transform, not motion — it never
     animates, so reduced motion has no quarrel with it and it stays. */

  /* The hamburger still becomes an X — it just arrives instead of rotating. */
  .nav-toggle span { transition-duration: 0.01ms; }
  .nav-links { transition: opacity 0.2s ease; transform: none; }

  /* The one animation that must keep running. Slower, so it reads as a
     steady pulse rather than a spin, but running: it is the only signal that
     a lead is in flight. `animation` (not animation-duration) so it also
     overrides the paused state set above. */
  .hi-spinner { animation: hiSpin 1.6s linear infinite !important; }

  /* Keeps the "you landed here" highlight, which is comprehension, not
     decoration — it is the only thing explaining why the page jumped to a
     form. It only ever changed box-shadow; nothing moves. */
  .contact-form.form-flash { animation: formFlash 2s ease !important; }
}

/* ---------- Reduced transparency ----------
   Frosted glass is a legibility problem for some people. The header no longer
   uses backdrop-filter at all (see the note on .site-header), so this list is
   shorter than it used to be — but every remaining frosted surface goes solid,
   and none of them lose their boundary, because a translucent surface that
   becomes opaque without gaining an edge just becomes an unexplained white
   rectangle. */
@media (prefers-reduced-transparency: reduce) {
  .listing-gallery-all {
    background: #fff;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: 1px solid var(--line);
  }
  .lightbox-backdrop {
    background: var(--ink-deep);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* ---------- Increased contrast ----------
   Near-solid surfaces with a defined border, and the muted greys pulled up.
   The ornamental line art comes off entirely: it sits behind text, and behind
   text is the last place it belongs for someone who asked for more contrast. */
@media (prefers-contrast: more) {
  :root {
    --muted: #4a4a4d;
    --body: #333335;
    --line: #b5ada0;
    --teal-soft: #7fd8d8;
  }
  .site-header { border-bottom: 1px solid var(--ink); }
  /* NOT `.hi-choice span` — the Yes/No control is now a radio mark plus a text
     label, both spans, so that selector would draw a box around the word
     "Yes". The marks are handled by the .hi-radio/.hi-check rule in the
     /homeinfo contrast block at the end of this file, which sets border-COLOR
     and so keeps their 1.8px weight. */
  .listing-card,
  .about-card,
  .hi-card,
  .hi-input,
  .hi-next-btn { border: 1px solid var(--ink); }
  .section-contact .section-lead { color: #fff; }
  .listings-head .section-lead { color: var(--ink); }
  .connect-leaf { display: none; }
  .footer-social a { border-color: #fff; }
  .footer-legal, .footer-c21-sub { color: rgba(255, 255, 255, 0.85); }
  .lightbox-backdrop { background: #000; backdrop-filter: none; -webkit-backdrop-filter: none; }
  .lightbox-arrow { background: rgba(255, 255, 255, 0.28); border: 1px solid #fff; }
  .lightbox-arrow:disabled { opacity: 0.5; }
}

/* =========================================================================
   HOME INFO  —  /homeinfo  QR lead-capture page
   =========================================================================
   Everything below is scoped to .hi-* classes and is only used by
   homeinfo.html. No rule above this line is modified, so the homepage and the
   listing pages render exactly as they did before.

   Mobile is the design target, not an afterthought: the visitor is standing
   outside a house holding a phone in one hand. That drives three decisions
   you will see repeated below —
     1. inputs are 1rem (16px). Anything smaller makes iOS Safari zoom the
        page on focus, which shoves the layout sideways mid-typing.
        .contact-form used to be 0.98rem and did exactly that; it now shares
        this floor.
     2. every interactive target is at least 48px tall.
     3. the two Yes/No questions answer on the press, not the release.
   ========================================================================= */

/* ---------- Page frame ----------
   The card sits on a teal band and overlaps the hero above it, as drawn. The
   overlap is done with a negative margin on the card rather than absolute
   positioning, so the band still grows to contain it when the form gets
   taller — which it does the moment a validation message appears. */
.hi-page { background: var(--ivory); }

/* This header does not stick, so it never has content passing beneath it —
   the scroll edge main.js toggles site-wide would be a line drawn against
   nothing. Opted out explicitly rather than by making the script special-case
   a page. */
.hi-header { position: static; }
.hi-header.is-scrolled { box-shadow: none; border-bottom-color: var(--line); }
.hi-header-inner { display: flex; align-items: center; min-height: calc(var(--logo-h) + 16px); }

.hi-hero { position: relative; background: var(--ivory); overflow: hidden; }
.hi-hero-grid {
  display: grid; grid-template-columns: 1.05fr 0.95fr;
  gap: 36px; align-items: center;
  padding: 52px 0 60px;
}
.hi-hero-copy { position: relative; z-index: 2; }
.hi-hero-art {
  position: relative;
  aspect-ratio: 5 / 4;
  border-radius: var(--radius);
  background: linear-gradient(158deg, var(--teal-soft), var(--teal));
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-shadow: var(--shadow);
}
/* A drawn front door, standing in for the photograph in Emily's mockup. It is
   ornament with no informational content, so it is aria-hidden in the markup
   and never announced. */
.hi-hero-art svg { width: 62%; height: auto; fill: none; stroke: var(--paper); stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; opacity: 0.9; }

.hi-band { background: var(--teal); }
.hi-main { padding: 0 0 60px; }
.hi-container { max-width: 700px; }

.hi-title {
  font-size: clamp(2rem, 6vw, 3.1rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  margin-bottom: 14px;
  text-wrap: balance;
}
.hi-lead {
  color: var(--body);
  font-size: 1.05rem;
  line-height: 1.6;
  max-width: 30em;
}

/* ---------- Card ---------- */
.hi-card {
  background: var(--paper);
  border-radius: 18px;
  padding: 30px 28px 28px;
  box-shadow: 0 24px 60px rgba(15, 125, 125, 0.25);
  margin-top: -46px;      /* the overlap onto the hero above */
  /* Positioned, and it has to be. .hi-hero above is position:relative, and a
     positioned element paints above a non-positioned one no matter which came
     first in the document — so without this the hero's ivory ground covers
     the top 46px of the card, which is exactly where the step indicator is. */
  position: relative;
  z-index: 1;
}

/* ---------- Step indicator ----------
   1 Home · 2 Your Info · 3 Get Details.

   This is NOT decoration painted on a one-page form. form.js marks a step
   complete off the same validation that guards the submit, so the numbers
   describe what the person has actually finished: choosing a home fills step
   one, a valid name/email/mobile fills step two, and a successful send fills
   step three. A progress indicator that does not track progress is worse than
   no indicator at all — it is a claim about state that can be false.

   aria-hidden, because the form's own headings and error messages already
   convey the same thing in the reading order; a screen reader hearing
   "1 Home 2 Your Info 3 Get Details" before the first field would just be
   listening to the layout. */
.hi-steps {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 26px;
}
.hi-step { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; }
.hi-step-num {
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: #e2e0da; color: var(--muted);
  font-size: 0.86rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background 0.25s ease, color 0.25s ease;
}
.hi-step-label {
  font-size: 0.92rem; font-weight: 600; color: var(--muted);
  transition: color 0.25s ease;
  white-space: nowrap;
}
.hi-step-line { flex: 1 1 auto; height: 1px; background: var(--line); min-width: 12px; }
.hi-step.is-active .hi-step-num { background: var(--teal-deep); color: #fff; }
.hi-step.is-active .hi-step-label { color: var(--ink); }
.hi-step.is-done .hi-step-num { background: var(--teal); color: #fff; }
.hi-step.is-done .hi-step-label { color: var(--ink); }

.hi-fieldset { border: 0; padding: 0; margin: 0 0 26px 0; }
.hi-fieldset:last-of-type { margin-bottom: 18px; }

.hi-legend {
  /* Rendered as a heading rather than a browser legend so it lines up with the
     card padding — legends position unpredictably across browsers. */
  display: block;
  width: 100%;
  padding: 0 0 14px 0;
  font-size: 1.06rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.35;
  font-family: 'Inter', sans-serif;
}

.hi-field { margin-bottom: 14px; }
.hi-field:last-child { margin-bottom: 0; }

.hi-label {
  display: block;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 8px;
  letter-spacing: 0.01em;
  line-height: 1.4;
}

.hi-hint { font-size: 0.82rem; color: var(--muted); margin-top: 6px; line-height: 1.45; }

/* ---------- Inputs ----------
   The mockup labels these fields with placeholder text only. The <label>
   elements are still in the markup, moved off-screen with .sr-only — a
   placeholder is not a label: it vanishes the moment someone starts typing,
   and several screen readers never announce it at all. This way the design
   is as drawn and the field still has a name. */
.hi-input {
  width: 100%;
  font-family: inherit;
  font-size: 1rem;          /* 16px — see note at the top of this section */
  min-height: 54px;
  padding: 15px 16px;
  border: 1.5px solid var(--line);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  transition: border-color 0.2s, box-shadow 0.2s;
}
.hi-input::placeholder { color: #9a978f; }
.hi-input:focus-visible {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(40, 199, 199, 0.26);
}
.hi-input.is-invalid { border-color: #b3261e; background: #fffafa; }
.hi-input.is-invalid:focus-visible { box-shadow: 0 0 0 3px rgba(179, 38, 30, 0.16); }

/* Native arrow varies wildly between platforms; draw our own so the select
   matches the text inputs. appearance:none needs the -webkit- prefix on iOS. */
.hi-select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 44px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='14'%20height='9'%20viewBox='0%200%2014%209'%3E%3Cpath%20fill='%236f6f73'%20d='M1.4%200%207%205.6%2012.6%200%2014%201.4%207%208.4%200%201.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  background-size: 13px 8px;
}

/* ---------- Validation messages ---------- */
.hi-error {
  display: flex;
  gap: 6px;
  margin-top: 7px;
  font-size: 0.85rem;
  font-weight: 500;
  color: #b3261e;
  line-height: 1.45;
}
.hi-error::before { content: "!"; font-weight: 700; }

.hi-alert {
  margin: 0 0 16px 0;
  padding: 13px 15px;
  border-radius: 10px;
  background: #fdecea;
  border: 1px solid #f3c2bd;
  color: #8c1d18;
  font-size: 0.92rem;
  line-height: 1.5;
}

/* ---------- Property confirmation preview ----------
   Photo + address ONLY. Price, beds, baths, sqft and the description are
   deliberately absent: that information is what the form is gating. Do not
   add spec rows here. */
.hi-preview {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
  padding: 12px;
  background: var(--teal-wash);
  border: 1px solid #cdeaea;
  border-radius: 12px;
}
.hi-preview-media {
  flex: 0 0 92px;
  width: 92px;
  height: 72px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--line);
  position: relative;
}
.hi-preview-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hi-preview-media.no-img::after {
  content: "Photo coming soon";
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  font-size: 0.6rem; color: var(--muted); padding: 4px;
}
.hi-preview-text { min-width: 0; }
.hi-preview-address { font-weight: 700; color: var(--ink); font-size: 0.98rem; line-height: 1.35; }
.hi-preview-city { color: #2c5757; font-size: 0.86rem; margin-top: 2px; }

/* ---------- The two qualifying questions ----------
   Emily's mockup puts them side by side, divided by a vertical rule, with
   radio dots rather than the filled pills this page used to carry.

   The dots are drawn, the rule is drawn — but the TAP TARGET is not the dot.
   The <label> wraps its input, so the whole 48px row answers the question. A
   14px circle is a fine thing to look at and a bad thing to hit with a thumb
   while standing in someone's driveway, which is the entire operating
   context of this page. */
.hi-questions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 28px;
}
.hi-questions .hi-field + .hi-field {
  border-left: 1px solid var(--line);
  padding-left: 28px;
}
.hi-choices { display: flex; flex-direction: column; gap: 2px; }

.hi-choice {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  min-height: 48px;
  cursor: pointer;
}
.hi-choice input {
  /* Visually hidden but still focusable and still announced by screen
     readers — display:none would remove it from the tab order entirely. */
  position: absolute;
  opacity: 0;
  width: 100%; height: 100%;
  margin: 0;
  cursor: pointer;
}
.hi-radio {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 1.8px solid #bdb9b0;
  background: var(--paper);
  display: flex; align-items: center; justify-content: center;
  transition: border-color 0.15s, background 0.15s, transform 0.15s ease-out;
}
.hi-radio::after {
  content: "";
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--paper);
  transform: scale(0);
  transition: transform 0.15s cubic-bezier(0.32, 0.72, 0, 1);
}
.hi-choice-label { font-size: 1rem; font-weight: 500; color: var(--ink); }
.hi-choice input:checked + .hi-radio { border-color: var(--teal-deep); background: var(--teal-deep); }
.hi-choice input:checked + .hi-radio::after { transform: scale(1); }
/* The control answers on the way down, not on the way up. `change` fires on
   release, so without this there is a full tap's worth of silence between
   deciding and seeing the answer register. */
.hi-choice input:active + .hi-radio { border-color: var(--teal-deep); transform: scale(0.9); transition-duration: 0.08s; }
.hi-choice input:focus-visible + .hi-radio { box-shadow: 0 0 0 3px rgba(40, 199, 199, 0.34); }
.hi-field.is-invalid .hi-radio { border-color: #d2827c; }

/* ---------- Consent ----------
   The TCPA agreement. It gates the submit button, it is validated again on
   the server, and the server stamps its own timestamp when it is accepted —
   a consent record the browser could edit is not a consent record.

   Styled as a genuine checkbox rather than a pill: this is a legal agreement,
   and it should look like the thing people already know how to read. */
.hi-consent {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 22px 0 6px;
  cursor: pointer;
  position: relative;
}
.hi-consent input {
  position: absolute;
  opacity: 0;
  width: 24px; height: 24px;
  margin: 0;
  cursor: pointer;
}
.hi-check {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  margin-top: 1px;
  border: 1.8px solid #bdb9b0;
  border-radius: 5px;
  background: var(--paper);
  display: flex; align-items: center; justify-content: center;
  transition: border-color 0.15s, background 0.15s, transform 0.15s ease-out;
}
.hi-check svg {
  width: 15px; height: 15px;
  fill: none; stroke: #fff; stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.15s ease, transform 0.15s cubic-bezier(0.32, 0.72, 0, 1);
}
.hi-consent input:checked + .hi-check { border-color: var(--teal-deep); background: var(--teal-deep); }
.hi-consent input:checked + .hi-check svg { opacity: 1; transform: scale(1); }
.hi-consent input:active + .hi-check { transform: scale(0.92); transition-duration: 0.08s; }
.hi-consent input:focus-visible + .hi-check { box-shadow: 0 0 0 3px rgba(40, 199, 199, 0.34); }
.hi-consent-text { font-size: 0.86rem; line-height: 1.5; color: var(--body); }
.hi-consent.is-invalid .hi-check { border-color: #b3261e; }

/* ---------- Honeypot ----------
   Off-screen rather than display:none — some bots skip hidden fields, and this
   one is meant to be filled in. */
.hi-hp {
  position: absolute;
  left: -9999px;
  top: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- Submit ---------- */
.hi-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  min-height: 60px;
  padding: 16px 28px;
  border: none;
  border-radius: 50px;
  background: var(--pink);
  color: #fff;
  font-family: inherit;
  /* 1.15rem/700 is 18.4px bold — right at the WCAG "large text" threshold,
     which is the size white-on-brand-pink needs in order to pass AA at its
     3.1:1. See the .btn-primary note far above; same reasoning, same colour. */
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 12px 26px rgba(255, 79, 135, 0.34);
  transition: background 0.2s, transform 0.15s, opacity 0.2s, box-shadow 0.2s;
}
@media (hover: hover) {
  .hi-submit:hover:not(:disabled) { background: var(--pink-ink); transform: translateY(-2px); }
}
/* This is the button the whole page exists for, pressed by someone standing
   outside a house on a phone. It gets the clearest press state on the site. */
.hi-submit:active:not(:disabled) {
  background: var(--pink-ink);
  transform: scale(0.98);
  box-shadow: 0 4px 12px rgba(255, 79, 135, 0.3);
  transition-duration: 0.08s;
}
.hi-submit:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 3px; }
.hi-submit:disabled { opacity: 0.72; cursor: progress; transform: none; box-shadow: none; }

.hi-spinner {
  width: 18px; height: 18px;
  border: 2.5px solid rgba(255, 255, 255, 0.45);
  border-top-color: #fff;
  border-radius: 50%;
  animation: hiSpin 0.7s linear infinite;
}
@keyframes hiSpin { to { transform: rotate(360deg); } }

.hi-note { text-align: center; color: var(--muted); font-size: 0.8rem; line-height: 1.5; margin-top: 14px; }

/* ---------- Success / next steps ---------- */
.hi-success {
  background: var(--paper);
  border-radius: 18px;
  padding: 34px 26px 28px;
  box-shadow: 0 24px 60px rgba(15, 125, 125, 0.25);
  margin-top: -46px;
  text-align: center;
  position: relative;   /* same stacking reason as .hi-card */
  z-index: 1;
}
.hi-success-mark {
  width: 64px; height: 64px;
  margin: 0 auto 16px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--teal-deep);
  color: #fff;
  font-size: 2rem;
  line-height: 1;
}
.hi-success .hi-lead { margin: 0 auto 26px; }
#hi-success-title:focus-visible { outline: none; }

.hi-next-heading {
  font-size: 1.2rem;
  /* The shared h1/h2/h3 leading of 1.12 is set for display sizes; at 1.2rem
     it pinches the descenders. Leading loosens as size comes down. */
  line-height: 1.3;
  color: var(--ink);
  margin-bottom: 16px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.hi-next { display: flex; flex-direction: column; gap: 12px; text-align: left; }

.hi-next-btn {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 68px;
  padding: 14px 18px;
  border: 1.5px solid var(--line);
  border-radius: 12px;
  background: var(--paper-warm);
  transition: border-color 0.15s, background 0.15s, transform 0.15s;
}
@media (hover: hover) {
  .hi-next-btn:hover { border-color: var(--teal); background: #fff; transform: translateY(-2px); }
}
.hi-next-btn:active { border-color: var(--teal); background: #fff; transform: scale(0.985); transition-duration: 0.08s; }
.hi-next-btn:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 2px; }
/* Stroked, not filled, so the icons sit at the same visual weight as the
   label text beside them instead of blocking in as three dark shapes. */
.hi-next-icon {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  fill: none;
  stroke: var(--teal-deep);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.hi-next-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hi-next-text strong { font-size: 1rem; font-weight: 600; color: var(--ink); }
.hi-next-text small { font-size: 0.83rem; color: var(--muted); line-height: 1.4; overflow-wrap: anywhere; }

/* ---------- Narrow phones ----------
   A dedicated breakpoint, because the site's other breakpoints are 1000/860px
   and this page has to hold together down to 320px. Scoped entirely to .hi-*
   so nothing else on the site is affected. */
@media (max-width: 860px) {
  .hi-hero-grid { grid-template-columns: minmax(0, 1fr); padding: 34px 0 46px; gap: 26px; }
  .hi-hero-art { order: -1; aspect-ratio: 16 / 9; }
  .hi-hero-art svg { width: 34%; }
}

@media (max-width: 560px) {
  .hi-container { padding: 0 16px; }
  .hi-card, .hi-success { padding: 22px 17px 20px; border-radius: 14px; margin-top: -34px; }
  .hi-main { padding-bottom: 44px; }
  .hi-legend { font-size: 1rem; }

  /* Side by side, the two questions get about 130px of column each on a
     narrow phone, which is not enough for "Are you currently working with a
     buyer's agent?" to be anything but a stack of two-word lines. They go one
     above the other, and the divider becomes horizontal. */
  .hi-questions { grid-template-columns: 1fr; gap: 0; }
  .hi-questions .hi-field + .hi-field {
    border-left: none;
    padding-left: 0;
    border-top: 1px solid var(--line);
    padding-top: 16px;
    margin-top: 4px;
  }

  /* The step labels are the first thing to go: the numbered circles alone
     still say "three steps, you are on the first" once there is no room for
     the words beside them. The active step keeps its label. */
  .hi-steps { gap: 8px; margin-bottom: 22px; }
  .hi-step-label { display: none; }
  .hi-step.is-active .hi-step-label { display: block; font-size: 0.86rem; }

  /* At 320px a 92px thumbnail plus a long address overflows the row. */
  .hi-preview-media { flex-basis: 74px; width: 74px; height: 60px; }
  .hi-preview-address { font-size: 0.92rem; }

  .hi-next-btn { padding: 13px 14px; gap: 11px; }
  .hi-next-icon { width: 22px; height: 22px; }
  .hi-submit { font-size: 1.08rem; }
}

/* The reduced-motion handling for this page (including the spinner, which
   must keep turning) lives with the rest of it in the ACCESSIBILITY
   PREFERENCES block above, so there is one place to look rather than two that
   have to be kept in agreement — the previous split is precisely how the
   spinner ended up frozen: the override lived here, the rule that beat it
   lived up there, and nothing connected them. */

@media (prefers-contrast: more) {
  .hi-page { background: #fff; }
  .hi-error, .hi-alert { color: #7a1810; }
  .hi-input.is-invalid { border-color: #7a1810; }
  .hi-radio, .hi-check { border-color: var(--ink); }
  .hi-step-num { background: #cfccc4; color: var(--ink); }
}

/* =========================================================================
   LISTING STATUS RIBBON  (.listing-ribbon)
   =========================================================================
   A diagonal corner ribbon on a card photo or the detail cover, set by hand in
   /admin (Coming Soon, New Listing, Under Contract, Pending, Sold). Real text,
   not aria-hidden, so it is announced with the address.

   The fills are the BRIGHT brand colours and the text on them is charcoal:
   --ink on --pink is 4.9:1 and --ink on --teal is 7.4:1. White on those fills
   fails (3.1:1 and 2.1:1) at ribbon size, so do not "fix" it to white.
   Needs a position:relative, overflow:hidden parent — .listing-media and
   .listing-detail-photo both are.
   ========================================================================= */
.listing-ribbon {
  position: absolute; z-index: 2;
  /* The band's centre line sits 116px along each edge from the corner, which
     leaves ~139px of full-height band: room for "UNDER CONTRACT" (~116px). Pull
     it closer to the corner and that label gets clipped. */
  top: 45px; left: -47px; width: 210px;
  padding: 6px 0;
  transform: rotate(-45deg);
  text-align: center;
  font-size: 0.7rem; font-weight: 700; line-height: 1.2;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink);
  box-shadow: 0 2px 8px rgba(37, 37, 38, 0.28);
  pointer-events: none;
}
.ribbon-pink { background: var(--pink); }
.ribbon-teal { background: var(--teal); }

/* =========================================================================
   /ADMIN  (.ad-*)
   =========================================================================
   Scoped entirely to .ad-*, appended like the .hi-* block. A phone is a
   first-class device here: her assistant will use one.
   ========================================================================= */
.ad-page { background: var(--ivory); }
.ad-page [hidden] { display: none !important; }
.ad-page .site-header { --logo-h: 56px; position: static; }
.ad-main { padding-top: 36px; padding-bottom: 72px; max-width: 880px; }
.ad-main h1 { font-size: clamp(1.7rem, 4vw, 2.3rem); margin-bottom: 10px; }
.ad-main h2 { font-size: 1.35rem; margin: 40px 0 8px; }
.ad-lead { color: var(--body); margin-bottom: 24px; }
.ad-logout { padding: 9px 20px; }

.ad-card {
  max-width: 420px; margin: 24px auto 0;
  background: var(--paper); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 34px 30px;
}
.ad-label { display: block; font-weight: 600; margin-bottom: 8px; }
.ad-input {
  width: 100%; min-height: 48px; padding: 11px 14px;
  font: inherit; color: var(--ink);
  background: #fff; border: 1.5px solid #8a8a8e; border-radius: 10px;
}
.ad-input:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 2px; }
.ad-error { color: #8a1a12; min-height: 1.6em; margin: 10px 0 6px; font-size: 0.92rem; }
.ad-login-btn { width: 100%; text-align: center; margin-top: 6px; }

.ad-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 22px; margin-bottom: 18px; }
.ad-tools .ad-input { flex: 1 1 260px; width: auto; }
.ad-check { display: flex; align-items: center; gap: 9px; min-height: 44px; cursor: pointer; }
.ad-check input { width: 20px; height: 20px; accent-color: var(--teal-deep); }
.ad-count { color: var(--muted); font-size: 0.9rem; }

.ad-list { list-style: none; display: grid; gap: 12px; }
.ad-row {
  display: grid; grid-template-columns: 96px minmax(0, 1fr) auto;
  gap: 8px 18px; align-items: center;
  background: var(--paper); border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px;
}
.ad-row.is-on { border-left-color: var(--teal-deep); }
.ad-thumb {
  width: 96px; height: 72px; border-radius: 8px; overflow: hidden;
  background: linear-gradient(160deg, var(--teal-deep), var(--ink));
}
.ad-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ad-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ad-address { font-size: 1.02rem; line-height: 1.3; overflow-wrap: anywhere; }
.ad-meta, .ad-feed { font-size: 0.86rem; color: var(--muted); }
.ad-controls { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }

/* The switch is a real checkbox (role=switch), drawn as a track and thumb.
   Off track #8a8a8e is 3.4:1 on white, which a control boundary needs. */
.ad-switch { position: relative; display: flex; align-items: center; gap: 10px; min-height: 36px; cursor: pointer; font-weight: 600; font-size: 0.92rem; }
.ad-switch input { position: absolute; opacity: 0; width: 46px; height: 26px; margin: 0; cursor: pointer; }
.ad-switch-ui {
  position: relative; flex: 0 0 46px; width: 46px; height: 26px; border-radius: 26px;
  background: #8a8a8e; transition: background 0.18s ease;
}
.ad-switch-ui::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px;
  border-radius: 50%; background: #fff; transition: transform 0.18s ease;
}
.ad-switch input:checked + .ad-switch-ui { background: var(--teal-deep); }
.ad-switch input:checked + .ad-switch-ui::after { transform: translateX(20px); }
.ad-switch input:focus-visible + .ad-switch-ui { outline: 3px solid var(--teal-deep); outline-offset: 2px; }
.ad-switch input:disabled + .ad-switch-ui { opacity: 0.55; }

.ad-banner-label { display: flex; align-items: center; gap: 10px; font-weight: 600; font-size: 0.92rem; }
.ad-banner {
  min-height: 44px; padding: 8px 12px; font: inherit; font-weight: 400; color: var(--ink);
  background: #fff; border: 1.5px solid #8a8a8e; border-radius: 10px;
}
.ad-banner:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 2px; }

.ad-msg { grid-column: 2 / -1; font-size: 0.86rem; color: var(--teal-deep); min-height: 1.2em; margin: 0; }
.ad-msg.is-error { color: #8a1a12; font-weight: 600; }
.ad-warn {
  grid-column: 1 / -1; margin: 0; padding: 9px 12px; border-radius: 8px;
  background: #fff4d6; border: 1px solid #e3b341; color: #6b4400; font-size: 0.9rem;
}
.ad-row-missing { grid-template-columns: minmax(0, 1fr); }

@media (max-width: 640px) {
  .ad-card { padding: 26px 20px; }
  .ad-row { grid-template-columns: 76px minmax(0, 1fr); padding: 12px; }
  .ad-thumb { width: 76px; height: 60px; }
  .ad-controls, .ad-msg, .ad-warn { grid-column: 1 / -1; }
  .ad-banner-label { width: 100%; }
  .ad-banner { flex: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .ad-switch-ui, .ad-switch-ui::after { transition: none; }
}
@media print { .listing-ribbon { box-shadow: none; border: 1px solid var(--ink); } }
