/* H2H public storefront — the Citizen Journal editorial palette (translated from the prototype's
   apps/public design system). Paper + wheat + CJ-blue; Newsreader serif for headings/addresses,
   JetBrains Mono for labels. The signature move: the agent is co-equal with the photo. */

/* Self-hosted fonts (was render-blocking Google Fonts). These are variable woff2, so ONE file per
   family covers its whole weight range. font-display:swap keeps text visible immediately (fallback,
   then swap). Relative url() so ManifestStaticFilesStorage rewrites them to the hashed names. */
@font-face { font-family: 'Manrope'; font-style: normal; font-weight: 400 800; font-display: swap; src: url("fonts/manrope.938c6e8019b6.woff2") format("woff2"); }
@font-face { font-family: 'Newsreader'; font-style: normal; font-weight: 400 600; font-display: swap; src: url("fonts/newsreader.3b69d5758c8b.woff2") format("woff2"); }
@font-face { font-family: 'Newsreader'; font-style: italic; font-weight: 400 600; font-display: swap; src: url("fonts/newsreader-italic.9d012ba0701a.woff2") format("woff2"); }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400 600; font-display: swap; src: url("fonts/jetbrainsmono.b636a65da4f0.woff2") format("woff2"); }

:root {
  --cj-blue: #2C39A0;
  --cj-blue-dark: #1F2878;
  --cj-blue-soft: #E8EAF5;
  --cj-wheat: #F0B82E;
  --cj-wheat-deep: #C99012;
  --cj-wheat-soft: #FCF1D3;

  --paper: #FAF6EE;
  --paper-2: #F4EEDF;
  --paper-line: #E5DCC4;
  --ink: #14181F;
  --ink-2: #3D4350;
  /* --ink-3 is the site-wide "muted" small-text colour (eyebrows, labels, card city/specs, footer).
     Darkened from #6F7480 to clear WCAG AA 4.5:1 on the lightest backgrounds it sits on (paper, paper-2,
     and the blue-soft agent row) — the old value was a 3.9–4.3:1 near-miss. */
  --ink-3: #5C5F69;
  --ink-4: #A2A6AD;

  /* Status-badge text colours, darkened to clear AA 4.5:1 on their light pill backgrounds (the old
     #1F8A5B/#C97F0E were 3.69:1 / 2.82:1 — sub-AA on the most common public badges). */
  --status-active: #0F6B45;
  --status-active-bg: #DFF1E6;
  --status-pending: #8A5800;
  --status-pending-bg: #FCEFD3;
  --status-sold: #6F4CB8;
  --status-sold-bg: #ECE4F8;

  --radius: 10px;
  --font-sans:  'Manrope', ui-sans-serif, system-ui, sans-serif;
  --font-serif: 'Newsreader', Georgia, serif;
  --font-mono:  'JetBrains Mono', ui-monospace, Menlo, monospace;
}

/* Notice pages — the custom 404 and the off-market "no longer listed" page. Centered message with a
   prominent filled CTA (the plain link read as invisible). */
.notice { max-width: 640px; margin: 4rem auto; padding: 2.5rem 1.25rem; text-align: center; }
.notice h1 { font-family: var(--font-serif); font-size: clamp(1.7rem, 4vw, 2.4rem); margin: 0 0 .6rem; color: var(--ink); }
.notice p { color: var(--ink-2); margin: .4rem 0; font-family: var(--font-sans); }
.notice .button { margin-top: 1.4rem; }
.button { display: inline-block; padding: .8rem 1.6rem; background: var(--cj-blue); color: #fff;
  text-decoration: none; border-radius: var(--radius); font-family: var(--font-sans); font-weight: 600; }
.button:hover { background: var(--cj-blue-dark); }

/* Legal / prose pages (privacy policy). A single readable measure of body text on paper. */
.legal { max-width: 72ch; margin: 3rem auto; padding: 0 1.25rem; color: var(--ink-2); line-height: 1.7; }
.legal h1 { font-family: var(--font-serif); font-size: clamp(1.9rem, 4vw, 2.6rem); color: var(--ink); margin: 0 0 .4rem; }
.legal h2 { font-family: var(--font-serif); font-size: 1.4rem; color: var(--ink); margin: 2.2rem 0 .6rem; }
.legal h3 { font-size: 1.05rem; color: var(--ink); margin: 1.4rem 0 .4rem; }
.legal p, .legal li { font-family: var(--font-sans); }
.legal ul { padding-left: 1.2rem; margin: .6rem 0; }
.legal li { margin: .35rem 0; }
.legal a { color: var(--cj-blue); font-weight: 600; }
.legal a:hover { text-decoration: underline; }
.legal-meta { font-family: var(--font-mono); font-size: 12px; letter-spacing: .04em; color: var(--ink-3); margin-top: 0; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }

/* Skip-to-content link (WCAG 2.4.1 Bypass Blocks): the first focusable element in <body>, hidden
   off-screen until it receives keyboard focus, then revealed top-left to jump past the header. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 1000; }
.skip-link:focus {
  left: 8px; top: 8px; width: auto; height: auto; padding: 8px 14px;
  background: var(--paper); color: var(--ink); border: 1px solid var(--paper-line);
  border-radius: 6px; font-family: var(--font-sans); font-weight: 600; font-size: 13px;
}
main:focus { outline: none; }  /* programmatic focus target (tabindex=-1) — no focus ring on click */

/* === Header === */
.site-head {
  background: var(--paper);
  border-bottom: 3px solid var(--cj-wheat);
}
.site-head .inner {
  max-width: 1320px;
  margin: 0 auto;
  padding: 16px 24px;
  display: flex;
  align-items: center;
  gap: 24px;
}
.site-head .brand {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 22px;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.site-head .brand em { color: var(--cj-blue); font-style: italic; }
.site-head .nav { display: flex; gap: 14px; margin-left: auto; font-size: 13px; font-weight: 600; }
.site-head .nav a { color: var(--ink-2); padding: 6px 10px; border-radius: 6px; }
.site-head .nav a:hover { color: var(--ink); background: var(--paper-2); }
.site-head .nav a.active { color: var(--cj-blue); }

/* === Page === */
.page {
  max-width: 1320px;
  width: 100%;
  margin: 0 auto;
  padding: 32px 24px 80px;
  flex: 1;
}
.ed-eyebrow {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.ed-h1 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 36px;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 6px 0 0;
  /* Balance multi-line headings so a short tail (e.g. ", KS") never orphans onto its own line —
     matters now that the h1 sits in the narrower city-top left column. Progressive: older engines
     fall back to normal wrapping. */
  text-wrap: balance;
}
.ed-h1 em { color: var(--cj-blue); font-style: italic; }
.ed-sub { color: var(--ink-3); margin-top: 8px; max-width: 60ch; text-wrap: pretty; }

/* === Filter bar === */
.filter-bar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin: 24px 0;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--paper-line);
}
.chip {
  appearance: none;
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit;
  font-weight: 600; font-size: 12px;
  padding: 7px 12px;
  min-height: 24px;  /* WCAG 2.2 Target Size (2.5.8) — the smaller `.filters .chip` variant would otherwise be ~22px tall */
  border: 1px solid var(--paper-line);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
}
.chip:hover { border-color: var(--ink-3); color: var(--ink); }
.chip.active { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.chip.active::before {
  content: "";
  width: 6px; height: 6px; border-radius: 1px;
  background: var(--cj-wheat);
  display: inline-block;
}

/* Filter chip rows (beds / baths / price / type / status) — sit under the city bar; one labelled
   row per group, chips slightly smaller than the city chips so the whole block reads as one control. */
.filters { display: flex; flex-direction: column; gap: 8px; margin: 0 0 24px; }
.filter-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.filter-label {
  flex: none; min-width: 52px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
/* Chips live in their own flex track so they can wrap on desktop but switch to a single
   horizontally-scrollable line on mobile (see the max-width:600px block). flex-basis:0 (not auto) is
   load-bearing: with an `auto` basis the row's wrap algorithm measures the track at its full content
   width and bumps a wide group (Price, Agency) onto its own line below the label. A 0 basis makes the
   track's hypothetical size 0 so it always sits beside the label; min-width:0 lets overflow-x scroll. */
.filter-chips { display: flex; flex: 1 1 0; min-width: 0; flex-wrap: wrap; align-items: center; gap: 8px; }
.filters .chip { font-size: 11px; padding: 5px 11px; }
/* Brokerage / agent / school-district SEARCH row — three text inputs in one GET form (was: an agency
   chip row plus two native <select>s, none of which survive an MLS-wide inventory). Styled to read as
   one more control in the filter block; it submits via the button, so no JS is needed. filter-search.js
   appends .search-suggest to a .search-field and it anchors there — hence position:relative. */
.search-filter { gap: 10px 14px; }
.search-field { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.search-input {
  font: inherit; font-size: 11px; font-weight: 600;
  padding: 5px 11px; width: 150px; max-width: 100%;
  border: 1px solid var(--paper-line); border-radius: 999px;
  background: transparent; color: var(--ink);
}
/* --ink-3 (not --ink-4) so the placeholder clears WCAG AA against paper, like every other muted text. */
.search-input::placeholder { color: var(--ink-3); font-weight: 500; }
.search-input:hover { border-color: var(--ink-3); }
.search-apply { cursor: pointer; }
.search-suggest {
  position: absolute; z-index: 30; left: 0; top: calc(100% + 4px); min-width: 100%; width: max-content;
  max-width: 320px; margin: 0; padding: 4px; list-style: none;
  background: #fff; border: 1px solid var(--paper-line); border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(20, 24, 31, .12); max-height: 264px; overflow-y: auto;
}
.search-opt {
  padding: 8px 10px; border-radius: 6px; cursor: pointer;
  font-family: var(--font-sans); font-size: 13px; font-weight: 600; color: var(--ink-2);
}
.search-opt.active, .search-opt:hover { background: var(--cj-blue-soft); color: var(--cj-blue); }
.search-opt-sub {
  display: block; margin-top: 1px;
  font-family: var(--font-mono); font-size: 10px; font-weight: 400; color: var(--ink-3);
}
.filters-clear {
  display: inline-flex; align-items: center; gap: 6px;
  align-self: flex-start; margin-top: 6px;
  padding: 6px 14px; border: 1px solid var(--cj-blue); border-radius: 999px;
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--cj-blue); cursor: pointer;
}
.filters-clear:hover { background: var(--cj-blue); color: #fff; }
@media (max-width: 600px) {
  /* Phones: keep each filter group on ONE line — the label stays put on the left and the chips
     become a horizontally-swipeable track (was: chips wrapped onto 2-3 rows, eating vertical space).
     touch-action is left at its DEFAULT on purpose: `pan-x` made horizontal scroll instant but fully
     blocked vertical PAGE scroll across the whole 6-row filter block, trapping the user (can't scroll
     down by dragging from a strip). There is no touch-action value that gives both instant-horizontal
     AND vertical-page scroll — that needs the browser's runtime gesture disambiguation, which the
     default provides. The laggy "tap to activate" feel was really the scroll-snap settle, which is now
     removed, so the default disambiguates responsively while keeping vertical page scroll alive.
     `overscroll-behavior-x: contain` stops a horizontal fling chaining out to the page. No
     `-webkit-overflow-scrolling` (a no-op on Android; iOS 13+ has native momentum). */
  .filter-label { min-width: 44px; }
  .filter-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;               /* Firefox */
  }
  .filter-chips::-webkit-scrollbar { display: none; }   /* WebKit / Blink */
  .filter-chips .chip { flex: 0 0 auto; }
  /* The search row is the one filter row that must NOT become a horizontal track: a text field you
     have to scroll to reach is unusable. Each field takes a full line instead, label above box. */
  .search-field { flex: 1 1 100%; }
  .search-input { flex: 1 1 auto; width: auto; min-width: 0; }
  .search-suggest { max-width: none; width: auto; right: 0; }
}

/* === Brokerage A-Z (/brokerages/) === */
.brokerage-list { list-style: none; margin: 24px 0 0; padding: 0; }
.brokerage-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  padding: 12px 2px; border-bottom: 1px solid var(--paper-line);
}
.brokerage-name { font-family: var(--font-serif); font-size: 1.15rem; color: var(--ink); }
.brokerage-where { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3); }
/* Pushed right on desktop; on a phone it simply wraps under the name with the rest. */
.brokerage-count { margin-left: auto; font-size: .85rem; color: var(--ink-3); }

/* === City landing page (+ detail breadcrumb) === */
.crumbs {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em;
  color: var(--ink-3); margin-bottom: 18px;
}
.crumbs a { color: var(--ink-3); }
.crumbs a:hover { color: var(--ink); }
.crumbs .sep { margin: 0 7px; color: var(--paper-line); }
.crumbs .here { color: var(--ink-2); }

.city-stats {
  display: flex; flex-wrap: wrap; gap: 10px 22px; margin: 16px 0 4px;
  font-family: var(--font-mono); font-size: 12px; color: var(--ink-2);
}
.city-stats strong { color: var(--ink); }

.city-intro { margin: 18px 0 4px; max-width: 65ch; color: var(--ink-2); line-height: 1.6; }
.city-intro p { margin: 0 0 10px; }

/* Agent landing-page profile header (advertiser_homes.html for an agent): headshot + brokerage + license,
   then bio + contact CTAs. Business info only. */
.agent-profile { display: flex; align-items: center; gap: 14px; margin: 14px 0 6px; }
.agent-profile-photo { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; flex: none; border: 1px solid var(--paper-line); }
.agent-profile-org { font-weight: 600; color: var(--ink); }
.agent-profile-org a { color: var(--cj-blue); }
.agent-profile-creds { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); margin-top: 3px; }
.agent-profile-cta { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 4px; }
.agent-profile-cta a { display: inline-flex; align-items: center; min-height: 24px; padding: 8px 14px; border-radius: 8px; font-weight: 600; font-size: 13px; border: 1px solid var(--paper-line); }
.agent-profile-cta .call { background: var(--cj-blue); border-color: var(--cj-blue); color: #fff; }
.agent-profile-cta .call:hover { background: var(--cj-blue-dark); }
.agent-profile-cta .email { color: var(--cj-blue); }
.agent-profile-cta .email:hover { background: var(--paper-2); }

/* City landing: intro text + map side-by-side when there's a map (fills the space the narrow intro
   column used to leave empty). Without a map it stays a single readable column. */
.city-top.has-map {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: 32px;
  align-items: stretch;
  margin-top: 4px;
}
.city-top.has-map .city-intro { max-width: none; }
.city-top-map { display: flex; min-height: 380px; }
.city-top-map .browse-map { flex: 1; height: auto; min-height: 380px; margin: 0; }
/* Agency page reuses city-top but has NO filter-bar/filters below it (they sit in the left column), so
   the grid would butt right against the side-by-side map (whose bottom margin is zeroed above). Restore
   the home-page 28px map-to-grid gap — desktop only; mobile keeps the stacked map's own bottom margin. */
@media (min-width: 821px) { .advertiser-top.has-map, .home-top.has-map { margin-bottom: 28px; } }
@media (max-width: 820px) {
  .city-top.has-map { display: block; }
  .city-top-map { display: block; margin: 16px 0 24px; }
  .city-top-map .browse-map { min-height: 0; height: min(55vh, 420px); }
}

.city-faq { margin-top: 40px; border-top: 1px solid var(--paper-line); padding-top: 24px; }
.city-faq h2 { margin-bottom: 16px; }
.faq-item { margin-bottom: 18px; max-width: 70ch; }
.faq-q { font-size: 15px; margin-bottom: 4px; }
.faq-a { color: var(--ink-2); line-height: 1.6; }
.faq-a p { margin: 0 0 8px; }

.city-link { color: var(--cj-blue); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
.city-link:hover { text-decoration-thickness: 2px; }

/* === Grid === */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}
/* Off-screen cards skip style/layout/paint until they near the viewport. On a phone that is 23 of the
   24 (the grid starts under the map), and laying them all out up front was the 64–117 ms first-layout
   task the lab saw before the map (and a ~0.45 s later mobile LCP). The HTML is untouched, so crawlers,
   find-in-page and screen readers still get every card.
   contain-intrinsic-size is the placeholder height a never-rendered card takes, so the scrollbar and
   the rows below don't jump as cards come into view; `auto` swaps in each card's real height once it
   has rendered. A card is ~0.6 × its width (the 5:3 photo) + ~178px (agent strip + body + footer), and
   its width tracks the viewport through the grid's column count (320px min tracks, 20px gaps, 24px
   page gutters, 1320px max page), so the placeholder follows the same formula per column count —
   measured on the live homepage: 3 columns 370–425px (capped once .page hits 1320px), 2 columns
   374–465px, 1 column 370px at 360 wide, 397px at 412. The column breaks fall at ~708px and ~1048px
   (not the 600/720/820px breakpoints). The values are the card's CONTENT box (2px under its bordered
   height); checked against every width from 360 to 1600px, the grid's placeholder height lands within
   0.7% of the rendered one. The rent grid shares the rule; its cards are the same shape, and `auto`
   corrects any difference after first paint. */
.grid > .card { content-visibility: auto; contain-intrinsic-size: auto min(calc(20vw + 158px), 423px); }
@media (max-width: 1047px) { .grid > .card { contain-intrinsic-size: auto calc(30vw + 156px); } }
@media (max-width: 707px) { .grid > .card { contain-intrinsic-size: auto calc(60vw + 147px); } }

/* === Card === */
.card {
  background: var(--paper);
  border: 1px solid var(--paper-line);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform .2s, border-color .2s, box-shadow .2s;
  color: inherit;
}
.card:hover {
  border-color: var(--ink-3);
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 24, 31, 0.06);
}
.card .photo {
  aspect-ratio: 5/3;
  background: var(--paper-2);
  position: relative;
  overflow: hidden;
}
.card .photo .status { position: absolute; top: 12px; left: 12px; z-index: 1; }

/* Framed photo: object-fit cover + pan (object-position) + zoom (transform scale, set inline per
   photo via Photo.frame_style). Absolutely positioned so it fills the container's fixed box WITHOUT
   contributing intrinsic height (an in-flow height:100% img inflates aspect-ratio/grid boxes like the
   hero). The container (position:relative; overflow:hidden) sizes itself and clips the scaled image. */
.frame-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }

/* Agent row — co-equal weight with the photo, per the memo. */
.card .agent {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--paper-line);
  background: var(--cj-blue-soft);
}
.card .agent .avatar-wrap { position: relative; flex-shrink: 0; }
.card .agent .avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--paper-2) center/cover no-repeat;
  border: 2px solid var(--paper);
  box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}
/* Monogram fallback — the initial of the name on the line beside it, for a listing whose agent has no
   headshot AND whose brokerage has no logo. That is the normal state across the MLS-wide IDX feed (~590
   listings, ~67 un-onboarded brokerages), where the empty disc was --paper-2 #F4EEDF on the
   --cj-blue-soft #E8EAF5 strip = 1.04:1 and read as an image that failed to load. Same treatment as the
   rent card's landlord monogram (rentals.css) — one monogram, both marketplaces. Letter is #2C39A0 on
   #FAF6EE = 8.86:1, AAA at this size, so axe's color-contrast rule passes even though the glyph is
   aria-hidden (axe gates that rule on visibility, not on exposure to AT). A `.mono` MODIFIER, not the
   bare .avatar, so the two background-image branches above are untouched. */
.card .agent .avatar.mono {
  display: flex; align-items: center; justify-content: center;
  background: var(--paper); color: var(--cj-blue);
  font-family: var(--font-serif); font-weight: 600; font-size: 17px; line-height: 1;
}
.card .agent .org-badge {
  position: absolute; right: -4px; bottom: -4px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--paper) center/contain no-repeat;
  border: 1.5px solid var(--paper);
  box-shadow: 0 1px 2px rgba(0,0,0,0.18);
}
.card .agent .name {
  font-weight: 700; font-size: 13px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card .agent .org {
  font-size: 11px; color: var(--ink-3);
  font-family: var(--font-mono); letter-spacing: 0.04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card .body { padding: 14px 16px 16px; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.card .addr {
  font-family: var(--font-serif);
  font-weight: 500; font-size: 18px; line-height: 1.25;
  letter-spacing: -0.01em; color: var(--ink);
}
.card .city {
  font-family: var(--font-mono);
  font-size: 10px; letter-spacing: 0.1em; margin-top: 3px;
  text-transform: uppercase; color: var(--ink-3);
}
/* Price + key facts share one footer row. Fixed height + no-wrap keeps the divider line, card tops,
   and bottoms aligned across every card in a grid row at ALL widths — content can never reflow it
   (specs ellipsis as a last resort; never triggers at the grid's 320px min column). */
.card .meta {
  display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: auto; padding-top: 11px; border-top: 1px solid var(--paper-line);
  height: 42px;
}
.card .price {
  font-weight: 800; font-size: 20px;
  font-variant-numeric: tabular-nums; line-height: 1;
  white-space: nowrap; flex-shrink: 0;
}
/* "Price on request" isn't a number — smaller/muted so real prices stay the anchor and it never
   crowds a long property-type pill on the same line. */
.card .price.on-request { font-weight: 600; font-size: 14px; color: var(--ink-2); }
/* Distinct class (NOT .specs) so the detail page's boxed .specs rule can't bleed border/padding here. */
.card .facts {
  display: flex; flex-wrap: nowrap; align-items: center; gap: 8px;
  font-size: 13.5px; line-height: 1; color: var(--ink-2);
  white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  justify-content: flex-end;
}
.card .facts span { white-space: nowrap; }
.card .facts strong { color: var(--ink); font-weight: 700; }
.card .ptype {
  align-self: center; flex-shrink: 0; white-space: nowrap;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--cj-blue); background: var(--cj-blue-soft);
  padding: 3px 9px; border-radius: 999px; line-height: 1.25;
}

/* === Status badge === */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 700; font-size: 10.5px;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 4px 10px 4px 8px;
  border-radius: 999px; line-height: 1.1;
}
.badge .dot { width: 6px; height: 6px; border-radius: 50%; }
.badge.active  { background: var(--status-active-bg);  color: var(--status-active); }
.badge.active  .dot { background: var(--status-active); }
.badge.pending { background: var(--status-pending-bg); color: var(--status-pending); }
.badge.pending .dot { background: var(--status-pending); }
.badge.sold    { background: var(--status-sold-bg);    color: var(--status-sold); }
.badge.sold    .dot { background: var(--status-sold); }
/* Freshness pills (no dot): "New" (recent list date) + "Price reduced" (gated by SHOW_PRICE_REDUCED).
   AA-contrast text on a light pill, matching the status-badge treatment. */
.badge.new     { background: #E3ECFB; color: #1B4DA6; }
.badge.reduced { background: #FCE4E4; color: #B0221F; }
/* The status container can now hold the status pill plus the New / Price-reduced pills — lay them out. */
.card .photo .status, .detail-hero-main .status-overlay { display: flex; flex-wrap: wrap; gap: 6px; }
.detail-hero-main .status-overlay { max-width: calc(100% - 32px); }
/* Open-house pill — FILLED (solid), so it reads as the primary time-sensitive cue, distinct from the
   outline status/New pills. */
.badge.oh     { background: var(--status-active); color: #fff; }
.badge.oh .dot { background: #fff; }
/* Detail-page "Open houses" block: one row per upcoming event, a calendar date chip + time + remarks. */
.open-houses { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.open-houses li { display: flex; gap: 16px; align-items: center; padding: 14px 16px;
                  background: var(--paper); border: 1px solid var(--paper-line); border-radius: var(--radius); }
.oh-date { flex: none; width: 64px; text-align: center; border: 1px solid var(--paper-line);
           border-radius: 8px; padding: 8px 4px; background: #fff; }
.oh-dow { display: block; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.1em;
          text-transform: uppercase; color: var(--status-active); font-weight: 700; }
.oh-dom { display: block; font-family: var(--font-serif); font-size: 20px; color: var(--ink); margin-top: 2px; }
.oh-time { font-family: var(--font-serif); font-size: 18px; color: var(--ink); }
.oh-remarks { font-family: var(--font-sans); font-size: 13.5px; color: var(--ink-2); margin-top: 3px; }

.empty-state { text-align: center; padding: 60px 24px; color: var(--ink-3); }

.pagination { display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 28px; }
.page-status { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; color: var(--ink-3); }
.page-link {
  padding: 8px 16px; border: 1px solid var(--cj-blue); border-radius: 999px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  color: var(--cj-blue); text-decoration: none;
}
.page-link:hover { background: var(--cj-blue); color: #fff; }

/* === Detail page === */
.back { display: inline-block; font-size: 13px; color: var(--ink-3); margin-bottom: 18px; }
.back:hover { color: var(--ink); }

.detail-hero {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 12px;
}
/* The main image defines the hero height (4/3); the side column STRETCHES to that height and splits
   it evenly between its thumbs, so the columns always bottom-align at any width (no gap-induced
   drift). Thumbs come out ~4/3, matching the main, gallery, and the portal cropper. */
.detail-hero-main {
  aspect-ratio: 4/3;
  background: var(--paper-2);
  border-radius: var(--radius);
  position: relative; overflow: hidden;
}
.detail-hero-main .status-overlay { position: absolute; top: 16px; left: 16px; z-index: 1; }
.detail-hero-side {
  display: grid;
  grid-auto-rows: 1fr;  /* 1 thumb → full height; 2 thumbs → two equal halves */
  gap: 12px;
  min-height: 0;
}
.detail-hero-thumb {
  background: var(--paper-2);
  border-radius: var(--radius);
  position: relative; overflow: hidden;
  min-height: 0;
}
@media (max-width: 720px) {
  .detail-hero { grid-template-columns: 1fr; }
  .detail-hero-side { display: none; }
}

/* Agent block — the differentiator. Sits ABOVE specs. */
.agent-block {
  display: flex; gap: 20px; align-items: center;
  margin: 24px 0; padding: 20px;
  background: var(--cj-blue); color: white;
  border-radius: var(--radius);
}
.agent-block-photo {
  width: 96px; height: 96px; border-radius: 50%;
  background: var(--paper-2) center/cover no-repeat;
  border: 3px solid white; flex-shrink: 0;
}
/* The 96px monogram — same pair as the card's, so the two surfaces read as one system. On the dark
   --cj-blue block the --paper disc is the white circle the 3px border already implies (8.86:1), and the
   blue letter on it is 8.86:1 — large text, which needs only 3:1. Matches .agent-block-orgmark's
   white-plate treatment below. 44px keeps the card's 17/36 letter-to-disc ratio at 96px. */
.agent-block-photo.mono {
  display: flex; align-items: center; justify-content: center;
  background: var(--paper); color: var(--cj-blue);
  font-family: var(--font-serif); font-weight: 600; font-size: 44px; line-height: 1;
}
.agent-block-body { flex: 1; }
.agent-block-name {
  font-family: var(--font-serif);
  font-size: 24px; font-weight: 500; letter-spacing: -0.01em;
}
.agent-block-tag { opacity: 0.85; font-size: 14px; margin-top: 2px; }
/* License/designation line. Sits on the DARK blue card, so it CANNOT reuse --ink-3 (a
   light-background muted grey — it was 1.50:1 here, illegible). Use the same white-with-opacity as the
   sibling name/tag/org lines: rgba(255,255,255,0.85) = ~7.35:1 on --cj-blue (WCAG AA+AAA). */
.agent-block-creds { color: rgba(255,255,255,0.85); font-size: 12.5px; letter-spacing: 0.02em; margin-top: 2px; }
/* Agent/agency name links through to that agent's / agency's filtered browse. Inherits the heading
   colour (so the block reads as one), but carries a PERSISTENT underline so it's discoverable as a
   link without relying on hover or colour alone (WCAG 1.4.1) — on the blue block the text is the same
   white as the heading, so hover-only gave no affordance. Hover just thickens the rule. */
.agent-link { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
.agent-link:hover { text-decoration-thickness: 2px; }
.agent-block-org { margin-top: 8px; font-size: 13px; opacity: 0.9; display: flex; align-items: center; gap: 10px; }
.agent-block-orgmark {
  width: 44px; height: 44px;
  background: white center/contain no-repeat;
  border-radius: 6px; padding: 4px; flex-shrink: 0;
}
.agent-block-cta { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.agent-block-cta .btn {
  display: inline-flex; padding: 8px 14px; border-radius: 6px;
  font-weight: 600; font-size: 13px; cursor: pointer;
}
.agent-block-cta .btn.primary { background: white; color: var(--cj-blue); }
.agent-block-cta .btn.ghost {
  background: rgba(255,255,255,0.16); color: white;
  border: 1px solid rgba(255,255,255,0.3);
}
@media (max-width: 720px) {
  .agent-block { flex-direction: column; text-align: center; }
  .agent-block-cta { justify-content: center; }
}

.detail-head {
  margin-top: 24px;
  display: flex; justify-content: space-between; align-items: flex-end;
  padding-bottom: 20px; border-bottom: 1px solid var(--paper-line);
  gap: 20px; flex-wrap: wrap;
}
.detail-head h1 { margin: 6px 0 0; }
.detail-price {
  font-weight: 800; font-size: 32px;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
.ppsf { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em; margin-top: 4px; }
.muted { color: var(--ink-3); }

/* === Share control === */
/* A quiet outline pill (cloned from .page-link) + a calm "paper" dropdown — deliberately NOT a row of
   loud brand-colored pills. Monochrome ink glyphs; brand colour is earned on the Copy row + on hover. */
.share { position: relative; display: inline-block; margin-top: 14px; text-align: left; }
.share-trigger {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border: 1px solid var(--cj-blue); border-radius: 999px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--cj-blue); background: transparent; user-select: none;
}
.share-trigger::-webkit-details-marker { display: none; }
.share-trigger:hover, .share[open] .share-trigger { background: var(--cj-blue); color: #fff; }
.share-trigger:focus-visible { outline: 2px solid var(--cj-blue); outline-offset: 2px; }
.share-caret { transition: transform 0.15s ease; }
.share[open] .share-caret { transform: rotate(180deg); }
.share-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 5; min-width: 210px; padding: 4px;
  background: var(--paper); border: 1px solid var(--paper-line); border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(20, 24, 31, 0.10); display: flex; flex-direction: column;
}
.share:not([open]) .share-menu { display: none; }
.share-row {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 6px;
  color: var(--ink); font-family: var(--font-sans); font-size: 13px; text-decoration: none;
}
.share-row svg { flex: 0 0 16px; color: var(--ink-3); }
.share-row:hover, .share-row:focus-visible { background: var(--cj-blue-soft); color: var(--cj-blue); outline: none; }
.share-row:hover svg, .share-row:focus-visible svg { color: var(--cj-blue); }
.share-copy { color: var(--cj-blue); border-bottom: 1px solid var(--paper-line); border-radius: 6px 6px 0 0; margin-bottom: 2px; }
.share-copy svg { color: var(--cj-blue); }
.share-copy.is-copied, .share-copy.is-copied svg { color: var(--status-active); }
/* JS hides the dropdown on capable devices — the trigger becomes a one-tap native OS share sheet. */
.share--native .share-menu { display: none; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@media (prefers-reduced-motion: reduce) {
  /* Honor the OS "reduce motion" preference across every non-essential animation/transition. */
  .share-caret { transition: none; }
  .card { transition: none; }
  .card:hover { transform: none; }
  .map-spinner { animation: none; }
  html { scroll-behavior: auto; }
}

/* Consistent, high-contrast keyboard focus ring for interactive elements that don't define their own
   (chips, links, buttons, form controls, pagination). :focus-visible keeps it keyboard-only (no ring on
   mouse click). The lightbox/share controls set their own more-specific rings, which still win. */
a:focus-visible, button:focus-visible, summary:focus-visible, .chip:focus-visible,
.button:focus-visible, .page-link:focus-visible, .city-link:focus-visible,
select:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--cj-blue);
  outline-offset: 2px;
  border-radius: 3px;
}

.specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 18px; margin: 24px 0; padding: 20px;
  background: var(--paper); border: 1px solid var(--paper-line);
  border-radius: var(--radius);
}
.specs .l {
  font-family: var(--font-mono);
  font-size: 10.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
.specs .v {
  font-family: var(--font-serif);
  font-size: 22px; font-weight: 500; letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums; margin-top: 4px;
}
.detail-section { margin: 32px 0; }
.detail-section h2 {
  font-family: var(--font-mono);
  font-size: 10.5px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-3);
  margin: 0 0 14px; padding-bottom: 8px;
  border-bottom: 1px solid var(--paper-line);
}
.description {
  font-family: var(--font-serif);
  font-size: 17px; line-height: 1.6; color: var(--ink-2);
}
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
}
.photo-grid .frame-cell {
  aspect-ratio: 4/3; overflow: hidden; position: relative;
  border-radius: 6px; background: var(--paper-2);
}
.detail-hero-main[data-lightbox], .detail-hero-thumb[data-lightbox], .photo-grid [data-lightbox] {
  cursor: zoom-in;
}
/* lightbox.js promotes triggers to role=button/tabindex=0 — give keyboard users a visible focus ring. */
[data-lightbox]:focus-visible { outline: 2px solid var(--cj-blue); outline-offset: 2px; }

/* === Video tour (click-to-load facade + Facebook link-out card) === */
.video-embed {
  /* display:block so aspect-ratio/width/overflow apply on the Facebook link-out <a> too (an <a> is
     inline by default, which collapses the box — the YouTube/Vimeo facade is a <div> so it was fine). */
  display: block;
  position: relative; aspect-ratio: 16/9; width: 100%;
  border-radius: 8px; overflow: hidden; background: var(--ink) center/cover no-repeat;
  cursor: pointer;
}
.video-play {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  border: 0; cursor: pointer; color: #fff;
  background: rgba(20, 24, 31, 0.34);
  font-family: var(--font-sans); font-weight: 600; font-size: 14px;
}
.video-play svg {
  width: 64px; height: 64px; padding: 16px; border-radius: 999px;
  background: rgba(20, 24, 31, 0.62); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
}
.video-play:hover svg { background: var(--cj-blue, #1a5fb4); }
.video-embed:focus-within { outline: 2px solid var(--cj-blue); outline-offset: 2px; }
/* Absolutely fill the 16:9 box (which is overflow:hidden) so the player — Facebook's plugin especially,
   and any vertical video — is always clipped to the frame and can never blow out to full-page height
   (height:100% against an aspect-ratio box doesn't always resolve; inset:0 on the positioned parent
   does). A portrait video letterboxes inside the frame rather than overflowing. */
.video-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
/* Facebook link-out card: same poster+play-overlay as the inline facade, but a real anchor (opens the
   tour on Facebook in a new tab) — FB's inline plugin can't size portrait video / behave on mobile. */
.video-linkout { text-decoration: none; }

/* === Documents + links === */
.doc-list, .link-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.doc-list a, .link-list a {
  display: inline-flex; align-items: center; gap: 9px;
  color: var(--ink); font-family: var(--font-sans); text-decoration: none;
}
.doc-list a { padding: 10px 14px; border: 1px solid var(--paper-line); border-radius: var(--radius); background: var(--paper-2); }
.doc-list a:hover, .link-list a:hover { color: var(--cj-blue); }
.doc-list a:hover { border-color: var(--ink-3); }
.doc-ext { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; color: var(--ink-3); }
.link-list a { color: var(--cj-blue); }
.link-list svg { opacity: .65; }

/* === Lightbox === */
.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(20, 24, 31, 0.92);
  display: flex; align-items: center; justify-content: center;
}
.lightbox[hidden] { display: none; }
.lightbox .lb-img {
  max-width: 92vw; max-height: 88vh;
  object-fit: contain; border-radius: 6px;
  box-shadow: 0 10px 50px rgba(0, 0, 0, 0.5);
}
.lightbox .lb-btn {
  position: absolute;
  background: rgba(255, 255, 255, 0.12); color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.25); border-radius: 8px;
  cursor: pointer; font-size: 30px; line-height: 1;
  width: 48px; height: 48px;
  display: flex; align-items: center; justify-content: center;
}
.lightbox .lb-btn:hover { background: rgba(255, 255, 255, 0.22); }
.lightbox .lb-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox .lb-close { top: 20px; right: 20px; }
.lightbox .lb-prev { left: 20px; top: 50%; transform: translateY(-50%); }
.lightbox .lb-next { right: 20px; top: 50%; transform: translateY(-50%); }
.lightbox .lb-count {
  position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.85);
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em;
}
@media (max-width: 720px) {
  .lightbox .lb-prev { left: 8px; } .lightbox .lb-next { right: 8px; }
}
.detail-attribution {
  /* The IDX/MLS attribution is compliance-required text, so it must be readable: --ink-2 (9.2:1 on
     paper), not the old --ink-4 (2.27:1, a clear AA failure on legally-required copy). */
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: 0.04em; color: var(--ink-2); margin-top: 32px;
}

/* === Map (MapLibre + OpenFreeMap) === */
.listing-map {
  height: 380px;
  border-radius: var(--radius);
  border: 1px solid var(--paper-line);
  overflow: hidden;
  background: var(--paper-2);
}
/* Tone MapLibre's chrome toward the editorial palette */
.listing-map .maplibregl-ctrl-attrib {
  font-family: var(--font-mono); font-size: 9.5px; background: rgba(250, 246, 238, 0.85);
}
.listing-map .maplibregl-ctrl-attrib a { color: var(--ink-3); }
.map-basemap-btn {
  /* MapLibre's `.maplibregl-ctrl-group button` forces width:29px / height:29px / padding:0 at higher
     specificity AND loads after this file, so width/height/padding all need !important to override —
     otherwise the label is clipped (width) and cramped (padding). */
  display: block; width: auto !important; height: auto !important; padding: 11px 18px !important;
  white-space: nowrap;
  background: #fff; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-2);
}
.map-basemap-btn:hover { background: var(--paper-2); color: var(--ink); }

/* Aerial season toggle (summer NAIP <-> spring NG911). Same chrome as the basemap button but visually
   subordinate (smaller, lighter); it sits in its own ctrl-group just below and only shows in satellite
   view. Same !important overrides needed — MapLibre's `.maplibregl-ctrl-group button` forces 29px. */
.map-season-btn {
  display: block; width: auto !important; height: auto !important; padding: 9px 18px !important;
  white-space: nowrap;
  background: #fff; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3);
}
.map-season-btn:hover { background: var(--paper-2); color: var(--ink); }

/* Tile-loading indicator — a small corner badge (NOT a full-map overlay), shown while imagery
   streams in (esp. the dynamic 30cm Kansas tiles). Driven by MapLibre's own tile-load events, so it
   appears on the Satellite toggle and when zooming in pulls new tiles, then hides once settled.
   Bottom-left keeps it clear of the toggle (top-left), nav (top-right) and attribution (bottom-right). */
.listing-map, .browse-map { position: relative; }
.map-loading {
  position: absolute; left: 9px; bottom: 9px; z-index: 2; display: none;
  align-items: center; gap: 7px; padding: 5px 10px; border-radius: 999px;
  background: rgba(250, 246, 238, 0.92); border: 1px solid var(--paper-line);
  box-shadow: 0 1px 5px rgba(20, 24, 31, 0.14); pointer-events: none;
}
.map-loading.is-on { display: flex; }
.map-loading-label {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-2);
}
.map-spinner {
  width: 12px; height: 12px; border-radius: 50%; flex: none;
  border: 2px solid rgba(20, 24, 31, 0.2); border-top-color: var(--cj-blue);
  animation: map-spin 0.7s linear infinite;
}
@keyframes map-spin { to { transform: rotate(360deg); } }
/* No-GPU hold (map.js softwareRendered): on a machine that draws WebGL in software the browse map waits
   for the page to settle and says so. A click anywhere on the box starts it at once. */
.browse-map.is-held { cursor: pointer; }
.map-hold {
  position: absolute; inset: 0; margin: auto; z-index: 2;
  width: max-content; height: max-content; max-width: calc(100% - 32px);
  padding: 10px 18px; border-radius: 999px; border: 1px solid var(--paper-line);
  background: rgba(250, 246, 238, 0.95); color: var(--cj-blue);
  font: 600 14px/1.3 var(--font-sans); cursor: pointer;
  box-shadow: 0 1px 5px rgba(20, 24, 31, 0.14);
}
.map-hold:hover { background: #fff; }
.map-hold:disabled { cursor: default; color: var(--ink-3); }

/* Browse: all-listings map — a band between the filter bar and the grid (reserved height so its
   lazy load causes no layout shift). */
.browse-map {
  height: min(60vh, 500px); margin: 16px 0 28px;
  border: 1px solid var(--paper-line); border-radius: var(--radius);
  overflow: hidden; background: var(--paper-2);
}
.browse-map .maplibregl-ctrl-attrib {
  font-family: var(--font-mono); font-size: 9.5px; background: rgba(250, 246, 238, 0.85);
}
.browse-map .maplibregl-ctrl-attrib a { color: var(--ink-3); }
/* Map pin popup (browse) */
.map-popup { display: block; width: 200px; text-decoration: none; color: var(--ink); }
.map-popup-thumb {
  display: block; width: 100%; height: 110px; object-fit: cover;
  border-radius: 6px; background: var(--paper-2);
}
.map-popup-addr { font-family: "Newsreader", Georgia, serif; font-size: 13px; line-height: 1.3; margin-top: 6px; }
.map-popup-price { font-weight: 700; font-size: 13px; margin-top: 2px; color: var(--cj-blue); }
/* Open-house time cue in a pin popup — the same green as the card badge + the green map marker. */
.map-popup-oh { font-family: var(--font-sans); font-weight: 700; font-size: 12px; margin-top: 4px; color: var(--status-active); }
/* Stacked-listings popup (several pins at one spot — same building / identical coords) */
.map-popup-multi { width: 220px; }
.map-popup-multi-head {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 4px;
}
.map-popup-row {
  display: flex; justify-content: space-between; gap: 10px; align-items: baseline;
  padding: 6px 0; border-top: 1px solid var(--paper-line); text-decoration: none; color: var(--ink);
}
.map-popup-row:first-of-type { border-top: 0; }
.map-popup-row-addr { font-family: "Newsreader", Georgia, serif; font-size: 12.5px; line-height: 1.25; }
.map-popup-row-price { font-weight: 700; font-size: 12.5px; color: var(--cj-blue); white-space: nowrap; }

/* === Footer === */
.site-foot { border-top: 1px solid var(--paper-line); background: var(--paper-2); }
.site-foot .inner {
  max-width: 1320px; margin: 0 auto; padding: 24px;
  color: var(--ink-3); font-size: 12.5px;
}
.site-foot .inner p { margin: 0 0 8px; }
.site-foot .inner p:last-child { margin-bottom: 0; }
.site-foot .inner a { color: var(--cj-blue); font-weight: 600; }
.site-foot .inner a:hover { text-decoration: underline; }
/* Decorative "·" separator. aria-hidden (SR-exempt), but bumped --ink-4 (2.11:1) -> --ink-3 (5.50:1)
   so it's visibly legible too — keep everything readable. */
.site-foot .inner p span[aria-hidden] { color: var(--ink-3); margin: 0 6px; }
