/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-family: var(--font-heading);
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.btn:hover { text-decoration: none; }

.btn-primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
}
.btn-primary:hover { background: var(--color-primary-dark); }

.btn-outline {
  background: transparent;
  border-color: var(--color-border);
  color: var(--color-text);
}
.btn-outline:hover { border-color: var(--color-primary); color: var(--color-primary); }

.btn-danger {
  background: transparent;
  border-color: var(--color-error);
  color: var(--color-error);
}
.btn-danger:hover { background: var(--color-error); color: #fff; }

.btn-pill { border-radius: var(--radius-pill); }
.btn-sm { padding: 0.45rem 1rem; font-size: 0.85rem; }
.btn:disabled { opacity: 0.6; cursor: not-allowed; }

/* Site header */
.site-header {
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  top: 0;
  background: #fff;
  z-index: 100;
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 1rem;
  padding-bottom: 1rem;
  gap: 1rem;
}
.site-logo {
  font-family: var(--font-heading);
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--color-text);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.site-logo .logo-mark { color: var(--color-primary); }
.site-nav {
  display: flex;
  gap: 1.75rem;
  align-items: center;
  margin-left: auto;
}
.site-nav a {
  color: var(--color-text);
  font-weight: 600;
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.site-nav a:hover { color: var(--color-primary); text-decoration: none; }
.header-cta { margin-left: 0.5rem; text-transform: none !important; letter-spacing: 0; }
/* `.site-nav a` (0,1,1) outranks `.btn-primary` (0,1,0), so without these the
   header's Contact Us button takes the nav link colour and sits dark text on a
   dark brand colour. Restated at higher specificity, including hover, which
   would otherwise paint the label in the same colour as the button behind it. */
.site-nav .header-cta,
.site-nav .header-cta:hover { color: var(--color-on-primary); }
.nav-toggle {
  display: none;
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: var(--color-text);
}

@media (max-width: 860px) {
  .site-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border-bottom: 1px solid var(--color-border);
    flex-direction: column;
    gap: 0;
    padding: 0.5rem 0;
  }
  .site-nav.open { display: flex; }
  .site-nav a { padding: 0.85rem 1.25rem; width: 100%; }
  .header-cta { margin: 0.5rem 1.25rem; }
  .nav-toggle { display: block; }
}

/* Hero with search */
.hero {
  position: relative;
  color: #fff;
  min-height: 70vh;
  display: flex;
  align-items: center;
  text-align: center;
  background: var(--color-dark);
  background-size: cover;
  /* Anchored to the bottom, not centered: the hero is much wider than it is
     tall, so `cover` crops the image vertically, and a centered crop cuts the
     bottom off. Property photos put the building along the lower edge and sky
     above, so keeping the bottom in frame keeps the subject. */
  background-position: center bottom;
}
/* No colour cast over the photo. Kept as a very light neutral scrim rather
   than removed outright, because the hero text is white and the top of a
   property photo is usually bright sky. Neutral black, not the navy tint it
   used to be, so the image keeps its own colours. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.28), rgba(0, 0, 0, 0.42));
}
.hero .container { position: relative; }
.hero-content { max-width: 760px; margin: 0 auto; padding: 5rem 0; }
.hero h1 { color: #fff; }
.hero p { color: rgba(231, 234, 242, 0.9); font-size: 1.1rem; max-width: 40rem; margin: 0 auto 2rem; }

/* Search bar */
/* Hero search. Every field is optional, so they read as independent narrowing
   controls rather than one query that has to be filled in. */
.search-panel {
  background: #fff;
  border-radius: var(--radius-md);
  padding: 1.25rem;
  box-shadow: 0 20px 50px rgba(0,0,0,0.25);
  max-width: 940px;
  margin: 0 auto;
  text-align: left;
}
/* Column counts are explicit rather than auto-fit so the seven fields divide
   evenly at every width and never leave a half-empty last row:
     2 cols - loc / type+status / beds+baths / parking+keyword
     3 cols - loc+type / status+beds+baths / parking+keyword
     4 cols - loc+type+status / beds+baths+parking+keyword
   Location spans 2 throughout; keyword only needs the extra width at 3. */
.search-fields {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.7rem 0.9rem;
}
.search-field-wide { grid-column: span 2; }

@media (min-width: 560px) {
  .search-fields { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 560px) and (max-width: 899px) {
  .search-field-keyword { grid-column: span 2; }
}
@media (min-width: 900px) {
  .search-fields { grid-template-columns: repeat(4, 1fr); }
}

/* Autocomplete (location fields). Replaces the native <datalist> popup, which
   the browser draws outside the page and no site CSS can style. */
.ac-wrap { position: relative; width: 100%; min-width: 0; }
/* .filter-bar sizes its direct input children with flex, and the wrapper is
   now that child, so it has to carry the same sizing. */
.filter-bar .ac-wrap { flex: 1 1 150px; }
/* The wrapper is not a flex container, so the flex sizing on .filter-bar input
   no longer does anything and the input would fall back to its intrinsic
   ~20-character width. */
.ac-wrap input { width: 100%; }
.ac-menu {
  position: absolute;
  top: calc(100% + 0.25rem);
  left: 0;
  right: 0;
  z-index: 50; /* over hero content, under the sticky header at 100 */
  max-height: 15rem;
  overflow-y: auto;
  padding: 0.25rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card-hover);
  text-align: left;
}
.ac-menu[hidden] { display: none; }
.ac-option {
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  font-size: 0.88rem;
  color: var(--color-text);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ac-option:hover, .ac-option.is-active { background: var(--color-primary-light); }
.ac-option mark { background: none; color: var(--color-primary); font-weight: 700; }

.search-field { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
/* Required: the display:flex above sits at the same specificity as the
   browser's own [hidden] { display: none }, and author rules win that tie, so
   without this a field set to hidden in JS stays on screen. Same trap the
   .range-slider[hidden] rule below already guards against. */
.search-field[hidden] { display: none; }
.search-field label,
.search-field .search-field-label {
  font-family: var(--font-heading);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.search-field input,
.search-field select {
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font-size: 0.95rem;
  font-family: inherit;
  color: var(--color-text);
  min-width: 0;
}
.search-field input:focus,
.search-field select:focus { outline: 2px solid var(--color-primary); outline-offset: 0; }

/* Range fields (price, floor area). Named generically rather than .price-*
   because the same markup drives more than one of them.

   Full width below 900px, since neither 2 nor 3 columns splits evenly for two
   of these. At 4 columns they sit side by side instead: stacking two
   full-width rows made the panel noticeably taller than the rest of the form
   warranted. */
.search-field-range { grid-column: 1 / -1; }
@media (min-width: 900px) {
  .search-field-range { grid-column: span 2; }
}

.range-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.range-readout { font-size: 0.8rem; color: var(--color-text-muted); }

.range-inputs { display: flex; align-items: center; gap: 0.4rem; }
/* Tighter than the standard .search-field input: these sit under a slider that
   already communicates the range, so they do not need equal visual weight. */
.range-inputs input { flex: 1 1 0; padding: 0.45rem 0.6rem; font-size: 0.88rem; }
.range-dash { color: var(--color-text-muted); }
/* Spinners are noise at these magnitudes and shrink the usable field. */
.range-inputs input::-webkit-outer-spin-button,
.range-inputs input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.range-inputs input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* Two range inputs stacked on one track. The inputs themselves ignore pointer
   events so a click always lands on whichever thumb is under the cursor. */
.range-slider { position: relative; height: 1.5rem; margin-top: 0.1rem; }
.range-slider[hidden] { display: none; }
.range-slider-track,
.range-slider-fill {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: 4px;
  border-radius: 2px;
  pointer-events: none;
}
.range-slider-track { left: 0; right: 0; background: var(--color-border); }
.range-slider-fill { background: var(--color-primary); }

.range-slider input[type="range"] {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  background: none;
  border: none;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}
.range-slider input[type="range"]:focus { outline: none; }
.range-slider input[type="range"]::-webkit-slider-runnable-track { background: none; border: none; }
.range-slider input[type="range"]::-moz-range-track { background: none; border: none; }

.range-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  pointer-events: auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-bg);
  border: 2px solid var(--color-primary);
  box-shadow: 0 1px 4px rgba(31, 39, 51, 0.25);
  cursor: grab;
}
.range-slider input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-bg);
  border: 2px solid var(--color-primary);
  box-shadow: 0 1px 4px rgba(31, 39, 51, 0.25);
  cursor: grab;
}
.range-slider input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.range-slider input[type="range"]:focus-visible::-moz-range-thumb { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.search-actions {
  display: flex;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 1.1rem;
}
.search-actions .btn { flex: 0 0 auto; }

@media (max-width: 559px) {
  .search-panel { padding: 1rem; }
  .search-actions { flex-direction: column; }
  .search-actions .btn { width: 100%; }
}

/* Section headers (centered) */
.section-header {
  text-align: center;
  max-width: 640px;
  margin: 0 auto 2.75rem;
}
.section-header h2 { margin-bottom: 0.4rem; }
.section-header h2::after {
  content: "";
  display: block;
  width: 56px;
  height: 3px;
  background: var(--color-primary);
  border-radius: 3px;
  margin: 0.75rem auto 0;
}
.section-header p { margin: 0.9rem 0 0; }

/* Dark section variant */
.section-dark { background: var(--color-dark); color: var(--color-dark-text); }
.section-dark h1, .section-dark h2, .section-dark h3 { color: var(--color-dark-text); }
.section-dark p { color: var(--color-dark-muted); }

/* Filter bar (listings page) */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 1.1rem 1.25rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  margin-bottom: 2.5rem;
}
.filter-bar select,
.filter-bar input {
  flex: 1 1 150px;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-family: inherit;
  background: var(--color-bg);
  color: var(--color-text);
}
.filter-bar select:focus,
.filter-bar input:focus { outline: 2px solid var(--color-primary); outline-offset: 0; }

/* Sits out of the 1 1 150px flow so it keeps its natural button width. The
   explicit [hidden] rule is needed because .btn sets display: inline-flex,
   which would otherwise win over the browser default for the attribute. */
.filter-bar .filter-clear { flex: 0 0 auto; }
.filter-bar .filter-clear[hidden] { display: none; }

.results-count {
  margin: -1.5rem 0 1.5rem;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}
.results-count:empty { display: none; }

/* Centered call to action under a section's grid. */
.section-cta { text-align: center; margin-top: 2.5rem; }

/* Listings grid */
.listings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1.75rem;
}

/* Auto-scrolling single-row carousel (Featured Properties). Pauses on hover and
   keyboard focus, and can be swiped on touch. Switched on by initCardMarquee()
   in home.js, and only when the cards actually overflow the row. */
.card-marquee {
  overflow-x: auto;
  overflow-y: hidden;
  /* Room for the card shadow and the hover lift, which overflow-y would clip. */
  padding: 0.5rem 0 1.5rem;
  scrollbar-width: none; /* the row is swipeable, so the bar is just noise */
  -webkit-overflow-scrolling: touch;
}
.card-marquee::-webkit-scrollbar { display: none; }
.card-marquee:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 4px; }

.card-marquee-track {
  --card-gap: 1.75rem; /* drives both card spacing and the loop distance */
  display: flex;
  gap: var(--card-gap);
  width: max-content;
}

/* The duplicate row exists only to make the loop seamless, so it stays hidden
   until the row is actually animating. Targeted by class rather than
   :last-child, since stop() removes this row and would otherwise leave the
   real row matching :last-child and getting hidden too. */
.card-marquee-track > .card-marquee-duplicate { display: none; }
.card-marquee.is-scrolling .card-marquee-track > .card-marquee-duplicate { display: flex; }

.listings-row { display: flex; gap: var(--card-gap, 1.75rem); }
/* Cards need a fixed width in a flex row. Capping at 82vw leaves a peek of the
   next card on a phone, which is what makes it read as a carousel. */
.listings-row > .listing-card { flex: 0 0 min(320px, 82vw); }

.card-marquee.is-scrolling .card-marquee-track {
  animation: card-marquee-scroll linear infinite;
}
/* Parking the animation hands scrolling back to the container, so the two never
   fight over the same axis. */
.card-marquee.is-paused .card-marquee-track { animation-play-state: paused; }

/* The track is (row + gap + row), so one full row is 50% of the track plus half
   the gap. A plain -50% stops half a gap short and jumps on every loop. */
@keyframes card-marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(calc(-50% - var(--card-gap) / 2)); }
}

@media (prefers-reduced-motion: reduce) {
  .card-marquee.is-scrolling .card-marquee-track { animation: none; }
  .card-marquee.is-scrolling .card-marquee-track > .card-marquee-duplicate { display: none; }
}

/* Prev/next controls, added by addMarqueeArrows() in home.js. They float over
   the row's edges rather than taking up layout space of their own. */
.card-marquee-wrap { position: relative; }

.card-marquee-arrow {
  position: absolute;
  top: 42%;
  transform: translateY(-50%);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-text);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  transition: box-shadow 0.2s ease, transform 0.2s ease, color 0.2s ease;
}
.card-marquee-arrow:hover { color: var(--color-primary); box-shadow: var(--shadow-card-hover); }
.card-marquee-arrow:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.card-marquee-arrow-prev { left: -0.5rem; }
.card-marquee-arrow-next { right: -0.5rem; }

@media (max-width: 640px) {
  /* No room to float outside the row on narrow screens, so sit them just
     inside the edges instead of overlapping the container's own padding. */
  .card-marquee-arrow-prev { left: 0.5rem; }
  .card-marquee-arrow-next { right: 0.5rem; }
}

.listing-card {
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  transition: box-shadow 0.2s ease, transform 0.2s ease;
  display: flex;
  flex-direction: column;
}
.listing-card:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-3px); }
.listing-card a.card-link { color: inherit; text-decoration: none; }
.listing-card-media { position: relative; }
.listing-card-img {
  aspect-ratio: 3 / 2;
  width: 100%;
  object-fit: cover;
  background: var(--color-bg-alt);
}
.card-tag {
  position: absolute;
  top: 0.85rem;
  left: 0.85rem;
  background: var(--color-tag);
  color: var(--color-on-primary);
  font-family: var(--font-heading);
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-sm);
  text-transform: capitalize;
}
.listing-card-body { padding: 1.1rem 1.25rem; }
.listing-card-title { margin: 0 0 0.3rem; font-size: 1.1rem; font-weight: 600; }
.listing-card-title a:hover { color: var(--color-primary); }
.listing-card-address {
  color: var(--color-text-muted);
  font-size: 0.88rem;
  margin-bottom: 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.listing-card-specs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.1rem;
  font-size: 0.82rem;
  color: var(--color-text-muted);
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--color-border);
}
.listing-card-specs .spec { display: inline-flex; align-items: center; gap: 0.35rem; }
.listing-card-specs .spec svg { color: var(--color-primary); flex: none; }
.listing-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 0.9rem;
}
.listing-agent { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; font-weight: 600; color: var(--color-text); }
.listing-agent img { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; }
.listing-price { font-family: var(--font-heading); font-weight: 700; font-size: 1.2rem; color: var(--color-primary); }
.listing-price .per { font-size: 0.78rem; font-weight: 500; color: var(--color-text-muted); }

/* A listing offered both ways shows both figures, sale above rent. Right
   aligned so the stack still reads as one price block against the agent name
   on the other side of .listing-card-foot. */
.listing-prices { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.25; }
/* The rent is the secondary figure of the two, so it is a size down. On a
   rent-only listing it is the only line, and looking slightly smaller than a
   sale price is the honest hierarchy anyway. */
.listing-price-rent { font-size: 1rem; }

/* Status badge (top-right of card / detail) */
.badge {
  display: inline-block;
  font-family: var(--font-heading);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-sm);
  color: var(--color-on-primary);
}
.badge-for_sale { background: var(--color-status-for-sale); }
.badge-for_rent { background: var(--color-status-for-rent); }
.badge-sold { background: var(--color-status-sold); }
.badge-rented { background: var(--color-status-rented); }
.listing-card-badge {
  position: absolute;
  top: 0.85rem;
  right: 0.85rem;
  box-shadow: 0 4px 12px rgba(0,0,0,0.18);
}
/* Two badges when a listing is offered both ways. The wrapper carries the
   positioning, so each badge keeps its own pill shape and shadow instead of
   the pair being boxed together. */
.listing-card-badges { display: flex; gap: 0.35rem; box-shadow: none; }
.listing-card-badges .badge { box-shadow: 0 4px 12px rgba(0,0,0,0.18); }

/* Listing detail sidebar: badges, then the price block. */
.detail-badges { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.detail-price {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.6rem;
  color: var(--color-primary);
  line-height: 1.2;
}
.detail-price-group { margin: 0.6rem 0 0.2rem; display: flex; flex-direction: column; gap: 0.5rem; }
/* Only rendered when a listing has both offers, so the two figures cannot be
   mistaken for one another. */
.detail-price-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.detail-price-row .detail-price { font-size: 1.35rem; }
.detail-price-label {
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  font-weight: 600;
}
/* The single-offer case keeps the spacing the old inline style used. */
.sidebar-card > .detail-price { margin: 0.6rem 0 0.2rem; }

/* Cities grid */
.cities-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 180px;
  gap: 1.25rem;
}
.city-tile {
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  color: #fff;
  background-size: cover;
  background-position: center;
  text-decoration: none;
}
.city-tile.feature { grid-row: span 2; grid-column: span 2; }
.city-tile::before {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(26,34,51,0) 40%, rgba(26,34,51,0.8));
}
.city-tile-label { position: relative; padding: 1.1rem 1.25rem; }
.city-tile-name { font-family: var(--font-heading); font-weight: 600; font-size: 1.1rem; }
.city-tile-count { font-size: 0.82rem; opacity: 0.9; }
@media (max-width: 720px) {
  .cities-grid { grid-template-columns: repeat(2, 1fr); }
  .city-tile.feature { grid-column: span 2; }
}

/* Why choose / split feature */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3rem;
  align-items: center;
}
.split img { border-radius: var(--radius-md); width: 100%; object-fit: cover; box-shadow: var(--shadow-card); }
@media (max-width: 760px) { .split { grid-template-columns: 1fr; } }

/* Partners logo row */
.partners-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 2.5rem 3.5rem;
}
.partners-row .partner {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.15rem;
  color: #aab1bf;
  filter: grayscale(1);
  opacity: 0.75;
  transition: opacity 0.15s ease;
}
.partners-row .partner:hover { opacity: 1; }
.partners-row img { max-height: 40px; filter: grayscale(1); opacity: 0.7; }

/* Photo gallery (detail page) */
.gallery { display: grid; gap: 0.5rem; }
.gallery-main img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border-radius: var(--radius-md); }
.gallery-thumbs { display: flex; gap: 0.5rem; overflow-x: auto; padding-top: 0.5rem; }
.gallery-thumbs img {
  width: 92px; height: 68px; object-fit: cover; border-radius: var(--radius-sm);
  cursor: pointer; opacity: 0.6; flex-shrink: 0; border: 2px solid transparent;
}
.gallery-thumbs img.active { opacity: 1; border-color: var(--color-primary); }
.gallery-caption { font-size: 0.88rem; color: var(--color-text-muted); margin: 0.6rem 0 0; font-style: italic; }

/* Location map on the listing detail page */
.listing-map {
  height: 300px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: 0.6rem;
  background: var(--color-bg-alt);
}
/* Leaflet ships a high z-index stack; keep it below the sticky site header. */
.listing-map .leaflet-pane,
.listing-map .leaflet-control { z-index: 1; }
.listing-map-note { font-size: 0.85rem; margin-bottom: 1.5rem; }

/* "Financing available" callout on the listing detail sidebar */
.financing-note {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 0.9rem;
  margin-bottom: 1.25rem;
  border-radius: var(--radius-sm);
  background: #f2faf7;
  border: 1px solid #bfe6d8;
  color: var(--color-success);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.9rem;
}
.financing-note svg { flex: none; margin-top: 2px; }
.financing-note small {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin-top: 0.15rem;
}

/* Feature tags (Home/Neighborhood Features, Nearby Establishments) */
.feature-tags { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.5rem; }

/* Nearby establishments, grouped by the categories the broker set up on the
   edit page (School, Grocery, and so on) rather than one undifferentiated run
   of pills. */
.nearby-groups { margin-bottom: 1.5rem; }
.nearby-group + .nearby-group { margin-top: 0.9rem; }
.nearby-group-name {
  display: block;
  font-family: var(--font-heading);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 0.45rem;
}
/* The group already carries the bottom spacing, so the pill row inside it
   should not add its own on top. */
.nearby-group .feature-tags { margin-bottom: 0; }
.feature-tag {
  display: inline-flex;
  align-items: center;
  background: var(--color-primary-light);
  color: var(--color-primary-dark);
  font-size: 0.85rem;
  font-weight: 500;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill);
}

/* Listing detail layout */
.listing-detail { display: grid; grid-template-columns: 1.4fr 1fr; gap: 3rem; align-items: start; }
@media (max-width: 860px) { .listing-detail { grid-template-columns: 1fr; } }
.listing-detail-facts {
  display: flex; gap: 2rem; flex-wrap: wrap; margin: 1.5rem 0; padding: 1.25rem 0;
  border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
}
.fact { text-align: left; }
.fact-value { font-family: var(--font-heading); font-size: 1.35rem; font-weight: 700; }
.fact-label { font-size: 0.78rem; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
.sidebar-card {
  border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 1.75rem;
  box-shadow: var(--shadow-card); position: sticky; top: 100px;
}

/* Forms */
.form-field { margin-bottom: 1rem; }
.form-field label { display: block; font-family: var(--font-heading); font-weight: 600; font-size: 0.88rem; margin-bottom: 0.4rem; }
.form-field input, .form-field textarea, .form-field select {
  width: 100%; padding: 0.75rem 0.9rem; border: 1px solid var(--color-border);
  border-radius: var(--radius-sm); font-size: 1rem; font-family: inherit; background: var(--color-bg); color: var(--color-text);
}
.form-field input:focus, .form-field textarea:focus, .form-field select:focus { outline: 2px solid var(--color-primary); outline-offset: 0; border-color: transparent; }
.form-field textarea { min-height: 110px; resize: vertical; }
.form-message { font-size: 0.9rem; margin-top: 0.75rem; }
.form-message.success { color: var(--color-success); }
.form-message.error { color: var(--color-error); }

/* About / agent */
.agent-block { display: grid; grid-template-columns: 260px 1fr; gap: 3rem; align-items: center; }
.agent-block img { border-radius: var(--radius-md); aspect-ratio: 1; object-fit: cover; box-shadow: var(--shadow-card); }

/* Track-record stats, the third column. Visually echoes the admin stat cards,
   but redefined here because admin.css is not loaded on public pages; the
   shared tokens keep the two consistent. */
.agent-stats {
  display: grid;
  /* Always two across, so four stats read as a 2x2 block rather than a tall
     single column beside the bio. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.9rem;
  align-content: center;
}
/* Required: the display above would otherwise beat the browser's [hidden]
   rule, same trap as the marquee duplicate row further up this file. */
.agent-stats[hidden] { display: none; }

.agent-stat {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 1rem 1.15rem;
  box-shadow: var(--shadow-card);
  position: relative;
  overflow: hidden;
}
.agent-stat::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--color-primary);
  opacity: 0.9;
}
.agent-stat-icon { color: var(--color-primary); display: block; margin-bottom: 0.35rem; }
.agent-stat-value {
  display: block;
  font-family: var(--font-heading);
  font-size: 1.9rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--color-text);
  /* Fixed-width digits so the card does not jiggle while counting up. */
  font-variant-numeric: tabular-nums;
}
.agent-stat-label {
  display: block;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-top: 0.2rem;
}

/* Three columns only once stats have actually loaded - .has-stats is added by
   renderAgentStats(), so a failed request keeps the original layout. */
.agent-block.has-stats { grid-template-columns: 220px minmax(0, 1fr) 400px; gap: 2.5rem; }

/* Below this the bio column gets too narrow to read beside a 400px 2x2 block,
   so the stats drop underneath the photo and bio, still two across. */
@media (max-width: 1100px) {
  .agent-block.has-stats { grid-template-columns: 220px minmax(0, 1fr); }
  .agent-stats {
    grid-column: 1 / -1;
    /* Cap the block so the 2x2 keeps card-like proportions instead of two
       very wide banners stretched across the whole section. */
    max-width: 560px;
  }
}

@media (max-width: 640px) {
  .agent-block,
  .agent-block.has-stats { grid-template-columns: 1fr; text-align: center; }
  .agent-block img { margin: 0 auto; max-width: 220px; }
}

/* Testimonials */
.testimonials-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; }
.testimonial-card {
  background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: 1.75rem; box-shadow: var(--shadow-card);
}
.testimonial-quote { margin-bottom: 1.25rem; color: var(--color-text); }
.testimonial-person { display: flex; align-items: center; gap: 0.75rem; }
.testimonial-person img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.testimonial-author { font-family: var(--font-heading); font-weight: 600; font-size: 0.92rem; color: var(--color-text); }
.testimonial-role { font-size: 0.8rem; color: var(--color-text-muted); }

/* CTA band */
.cta-band {
  position: relative;
  background: var(--color-dark);
  background-size: cover;
  background-position: center;
  color: #fff;
  text-align: center;
}
.cta-band::before { content: ""; position: absolute; inset: 0; background: rgba(26,34,51,0.8); }
.cta-band .container { position: relative; }
.cta-band h2 { color: var(--color-dark-text); }
.cta-band p { color: rgba(231,234,242,0.9); max-width: 40rem; margin: 0.5rem auto 1.75rem; }

/* News / blog cards */
.news-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.75rem; }
.news-card {
  background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
  overflow: hidden; box-shadow: var(--shadow-card); display: flex; flex-direction: column;
  transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.news-card:hover { box-shadow: var(--shadow-card-hover); transform: translateY(-3px); }
.news-media { position: relative; }
.news-media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; background: var(--color-bg-alt); }
.news-badge {
  position: absolute; top: 0.85rem; left: 0.85rem; background: var(--color-primary); color: var(--color-on-primary);
  font-family: var(--font-heading); font-size: 0.72rem; font-weight: 600; padding: 0.3rem 0.7rem; border-radius: var(--radius-sm); text-transform: uppercase;
}
.news-body { padding: 1.25rem; display: flex; flex-direction: column; flex: 1; }
.news-title { font-size: 1.05rem; font-weight: 600; margin: 0 0 0.5rem; }
.news-excerpt { font-size: 0.9rem; margin-bottom: 1rem; }
.news-more { margin-top: auto; font-family: var(--font-heading); font-weight: 600; font-size: 0.85rem; color: var(--color-primary); }

/* Contact section */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; }
@media (max-width: 720px) { .contact-grid { grid-template-columns: 1fr; } }
.contact-detail { margin-bottom: 1rem; }
.contact-detail-label { font-family: var(--font-heading); font-weight: 600; font-size: 0.82rem; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
.contact-form-card { background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 1.75rem; box-shadow: var(--shadow-card); }

/* Footer */
.site-footer { background: var(--color-dark); color: var(--color-dark-muted); padding: 3.5rem 0 1.5rem; font-size: 0.9rem; }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.3fr; gap: 2.5rem; margin-bottom: 2.5rem; }
@media (max-width: 760px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .footer-grid { grid-template-columns: 1fr; } }
.footer-col h4 { color: var(--color-dark-text); font-size: 1rem; margin-bottom: 1rem; }
.footer-col a { color: var(--color-dark-muted); display: block; padding: 0.25rem 0; }
.footer-col a:hover { color: var(--color-dark-text); text-decoration: none; }
.footer-brand { font-family: var(--font-heading); font-weight: 700; font-size: 1.25rem; color: var(--color-dark-text); margin-bottom: 0.75rem; }
.footer-social { display: flex; gap: 0.6rem; margin-top: 0.5rem; }
.footer-social a {
  width: 34px; height: 34px; border-radius: 50%; background: rgba(255,255,255,0.1);
  display: inline-flex; align-items: center; justify-content: center; color: var(--color-dark-text); padding: 0;
}
.footer-social a:hover { background: var(--color-primary); }
.newsletter-form { display: flex; gap: 0.5rem; margin-top: 0.75rem; }
.newsletter-form input {
  flex: 1; padding: 0.6rem 0.8rem; border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-sm); background: rgba(255,255,255,0.06); color: #fff; font-family: inherit;
}
.newsletter-form input::placeholder { color: var(--color-dark-muted); }
.footer-bottom { border-top: 1px solid rgba(255,255,255,0.1); padding-top: 1.5rem; text-align: center; font-size: 0.85rem; }

/* Empty / loading states */
.empty-state { text-align: center; padding: 3rem 1rem; color: var(--color-text-muted); grid-column: 1 / -1; }
.section-dark .empty-state { color: var(--color-dark-muted); }
.spinner-text { text-align: center; padding: 2rem; color: var(--color-text-muted); grid-column: 1 / -1; }

/* ---- Contact channels (click to chat) ----------------------------------- */

/* .btn is already inline-flex with gap, so icon + label needs no extra layout. */
.btn-channel { color: #fff; }
.btn-channel:hover { color: #fff; }
.btn-channel svg { flex: none; }

.btn-whatsapp { background: var(--color-whatsapp); }
.btn-whatsapp:hover { background: var(--color-whatsapp-dark); }
.btn-viber { background: var(--color-viber); }
.btn-viber:hover { background: var(--color-viber-dark); }
.btn-messenger { background: var(--color-messenger); }
.btn-messenger:hover { background: var(--color-messenger-dark); }

.channel-buttons { display: flex; flex-direction: column; gap: 0.5rem; }
.channel-buttons .btn { width: 100%; justify-content: flex-start; }
.channel-buttons.row { flex-direction: row; flex-wrap: wrap; }
.channel-buttons.row .btn { width: auto; }

/* Forces exactly one row of equal-width buttons that never wrap, for narrow
   containers like the listing sidebar (~400px) where the default .row wraps
   3 buttons to 2 lines. Smaller padding/font and a centered, truncatable label
   are what let "WhatsApp" still fit its third of the row. */
.channel-buttons.row.compact { flex-wrap: nowrap; }
.channel-buttons.row.compact .btn {
  flex: 1 1 0;
  min-width: 0;
  justify-content: center;
  padding: 0.55rem 0.4rem;
  font-size: 0.82rem;
  gap: 0.35rem;
}
.channel-buttons.row.compact .btn span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.channel-note { font-size: 0.78rem; color: var(--color-text-muted); margin: 0.5rem 0 1.25rem; }

/* Keep the contact block's phone/email links looking like text, not links. */
.contact-detail a { color: inherit; text-decoration: none; }
.contact-detail a:hover { text-decoration: underline; }
.contact-detail .contact-sub { font-size: 0.82rem; color: var(--color-text-muted); }

/* Floating chat button. Public pages only: admin never loads site-chrome.js. */
.channel-fab {
  position: fixed;
  right: 1rem;
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 40; /* above .sidebar-card, which is sticky with no z-index */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
}
.channel-fab-toggle {
  width: 52px; /* clears the 44px minimum touch target */
  height: 52px;
  border-radius: var(--radius-pill);
  border: none;
  background: var(--color-primary);
  color: var(--color-on-primary);
  cursor: pointer;
  box-shadow: var(--shadow-card-hover);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.channel-fab-toggle:hover { background: var(--color-primary-dark); }
.channel-fab-toggle svg { width: 24px; height: 24px; }
.channel-fab-stack { display: flex; flex-direction: column; align-items: flex-end; gap: 0.5rem; }
.channel-fab-stack[hidden] { display: none; }
.channel-fab-stack .btn { box-shadow: var(--shadow-card); white-space: nowrap; }

/* Keep the end of the page clear of the floating button. Without this the FAB
   sits over the right edge of the full-width "Send Inquiry" button once the
   form is scrolled to the bottom on a phone. Applied only when the FAB is
   actually rendered, and to main rather than body so the footer stays flush. */
body.has-channel-fab main { padding-bottom: 76px; }

/* Honeypot. Positioned off-screen rather than display:none, because the naive
   bots this catches skip fields that are obviously hidden. Screen readers are
   covered by aria-hidden plus tabindex="-1" on the input. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   Sell With Us (/sell.html) only.

   The seller-facing page needs three things the rest of the site never asked
   for: a numbered process explainer, a plain-language callout about how the
   broker gets paid, and an FAQ accordion. Everything else on that page reuses
   the existing section, card, and form classes.
   --------------------------------------------------------------------------- */

/* Value cards: why an owner should list with this brokerage. */
.value-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; }
.value-card {
  background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: 1.5rem; box-shadow: var(--shadow-card);
}
.value-card h3 { font-size: 1.02rem; margin: 0 0 0.5rem; }
.value-card p { font-size: 0.92rem; margin: 0; color: var(--color-text-muted); }

/* The four-step "how it works" explainer. */
.process-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1.5rem; }
.step-card {
  position: relative;
  background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: 1.5rem; box-shadow: var(--shadow-card);
}
.step-number {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; margin-bottom: 0.85rem;
  border-radius: 50%; background: var(--color-primary); color: var(--color-on-primary);
  font-family: var(--font-heading); font-weight: 700; font-size: 0.95rem;
}
.step-card h3 { font-size: 1rem; margin: 0 0 0.4rem; }
.step-card p { font-size: 0.9rem; margin: 0; color: var(--color-text-muted); }

/* How the broker gets paid, and what paperwork to bring. Deliberately a calm
   bordered panel rather than an alert colour: this is the block an owner reads
   most carefully, and a warning style would read as a catch. */
.explainer-panel {
  background: var(--color-bg-alt); border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-md); padding: 1.75rem;
}
.explainer-panel h3 { font-size: 1.05rem; margin: 0 0 0.75rem; }
.explainer-panel p { margin: 0 0 0.75rem; }
.explainer-panel p:last-child { margin-bottom: 0; }
.explainer-panel ul { margin: 0.5rem 0 0; padding-left: 1.15rem; }
.explainer-panel li { margin-bottom: 0.45rem; font-size: 0.95rem; }

/* FAQ accordion. Native <details> so the page needs no JavaScript for it. */
.faq-list { max-width: 46rem; margin: 0 auto; }
.faq-item {
  border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  background: var(--color-bg); margin-bottom: 0.75rem;
}
.faq-item summary {
  cursor: pointer; padding: 1rem 1.15rem; list-style: none;
  font-family: var(--font-heading); font-weight: 600; font-size: 0.98rem;
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; font-size: 1.35rem; font-weight: 400; color: var(--color-primary); line-height: 1; }
.faq-item[open] summary::after { content: "\2013"; }
.faq-item summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.faq-answer { padding: 0 1.15rem 1.1rem; font-size: 0.93rem; color: var(--color-text-muted); }

/* Two-up rows inside the sell form, so the small numeric fields do not each
   take a full line on desktop. Collapses on narrow screens. */
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.form-row-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
@media (max-width: 600px) {
  .form-row, .form-row-3 { grid-template-columns: 1fr; gap: 0; }
}

/* Checkbox rows sit inline, unlike the stacked label/input of .form-field. */
.form-check { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 1rem; }
.form-check input { width: auto; margin: 0; }
.form-check label { margin: 0; font-weight: 400; font-size: 0.93rem; }

.field-hint { display: block; font-size: 0.8rem; color: var(--color-text-muted); margin-top: 0.3rem; }

/* Radio group on the sell form. A fieldset for the semantics (the legend names
   the question for screen readers), with the browser's default border and
   padding stripped so it matches the surrounding .form-field boxes. */
.choice-group { border: 0; padding: 0; margin-bottom: 1rem; }
.choice-group legend {
  padding: 0;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.88rem;
  margin-bottom: 0.4rem;
}
.choice-group .form-check { margin-bottom: 0.5rem; }
.choice-group .form-check:last-child { margin-bottom: 0; }
