/* ============================================================
   Listing pages.

   Tokens come from assets/token.css, which partials.js links ahead
   of this file and index.html links too — one source of truth for
   both. The shared primitives below are still transcribed from
   index.html so generated pages sit in the same visual language
   without making the homepage depend on this build. Near-black and
   off-white carry the page; John L. Scott green shows up only on call
   buttons. Status badges are the one place other hues appear, and
   they are carrying information, not decoration.
============================================================ */

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

body {
  font-family: 'Inter', sans-serif;
  font-weight: 400;
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--ink);
  background: var(--cream);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

.container { max-width: var(--container); margin: 0 auto; padding: 0 var(--space-4); }
.section { padding: var(--section-y) 0; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Type ──
   The scale is deliberately wide: the listing headline and the stat
   labels are a factor of seven apart. Everything in the middle is
   quiet, because the photographs are what the page is selling.

   Only two faces, both already in the system. With the typefaces
   fixed, the personality has to come from size, spacing and weight,
   so those are pushed hard rather than split down the middle. */
h1, h2, h3 {
  font-family: 'Montserrat', sans-serif;
  letter-spacing: -0.02em;
  color: var(--ink);
}
h1 { font-weight: 700; font-size: clamp(32px, 5vw, 52px); line-height: 1.15; }
h2 { font-weight: 700; font-size: clamp(26px, 3.4vw, 38px); line-height: 1.2; }
h3 { font-weight: 600; font-size: clamp(19px, 2vw, 22px); line-height: 1.3; }
.on-dark { color: var(--white); }

/* Section headings are signposts, not statements. They name what
   follows and then get out of the way, which is why they sit far
   closer to the body text than to the headline. */
.section-title {
  font-size: var(--text-heading);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.eyebrow {
  display: block;
  font-family: 'Inter', sans-serif;
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: var(--space-2);
}
.eyebrow.on-dark { color: rgba(255, 255, 255, 0.5); }

.lede {
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink-soft);
  max-width: 560px;
}
.lede.on-dark { color: rgba(255, 255, 255, 0.75); }

/* ── Buttons ── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: var(--space-3) 30px;
  border-radius: var(--radius);
  border: none;
  cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-weight: 500;
  font-size: var(--text-base);
  line-height: 1;
  white-space: nowrap;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.btn-call { background: var(--accent); color: var(--white); }
.btn-call:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(15, 99, 66, 0.35); }
.btn-lg { padding: 18px 34px; font-size: 17px; }
.btn-sm { padding: 10px 18px; font-size: var(--text-sm); }
.btn-full { width: 100%; }

@media (max-width: 420px) {
  .btn-lg { padding: var(--space-3) 20px; font-size: 15px; white-space: normal; text-align: center; }
}

/* ── Topbar ──
   Same lockup and crop behavior as the homepage. The Listings link
   is the only addition, and it is the first thing to go on narrow
   screens so the call button never loses room. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--white);
  border-bottom: 1px solid var(--line);
}
.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-top: 14px;
  padding-bottom: 14px;
}
.topbar-logo-wrap { display: block; height: 52px; flex-shrink: 0; }
.topbar-logo-img { height: 100%; width: auto; max-width: none; }
/* Call button only, at every width. Listings live in the footer. */
.topbar-nav { display: flex; align-items: center; gap: 22px; flex-shrink: 0; }
@media (max-width: 560px) {
  .topbar-logo-wrap { width: 108px; overflow: hidden; height: 34px; }
  .topbar-nav { gap: 14px; }
}

/* ── Status badges ──
   The only multi-hue system on the site. Each color is load-bearing,
   so the label always ships alongside it rather than color alone. */
.badge {
  display: inline-block;
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--white);
  background: var(--slate);
  border-radius: var(--radius-pill);
  padding: 6px 13px;
  line-height: 1;
  white-space: nowrap;
}
.badge-coming-soon { background: #0981DB; }
.badge-active { background: var(--accent); }
.badge-pending { background: #B5801A; }
.badge-sold { background: #55565A; }

/* ── Section rhythm ──
   The detail page has no section rhythm to speak of any more: the
   portal layout is one mosaic and one two-column block. This padding
   is for the seller band and the listings index, which are still
   ordinary sections. */
.section { padding: var(--space-7) 0; }
@media (max-width: 820px) {
  .section { padding: var(--space-6) 0; }
}

/* ── Photo mosaic ──
   The top of the page, and the thing the reader came for. Inside the
   container rather than full-bleed, because a portal frames its
   photos as content rather than as a banner: the address and price
   need to be reachable without scrolling past a full viewport of
   photograph. */
.mosaic-section { padding-top: var(--space-4); }
.mosaic {
  position: relative;
  display: grid;
  grid-template-columns: 60fr 40fr;
  grid-template-rows: repeat(2, 1fr);
  gap: var(--space-2);
}
.mosaic-lead {
  grid-column: 1;
  grid-row: 1 / 3;
  /* The lead's own ratio sets the height of both rows, so the right
     column follows it rather than needing a height of its own. */
  aspect-ratio: 4 / 3;
}
.mosaic-a { grid-column: 2; grid-row: 1; }
.mosaic-b { grid-column: 2; grid-row: 2; }

/* Two photos: the right tile takes the full height beside the lead.
   One photo: a single wide image, no second column at all. */
.mosaic[data-count="2"] .mosaic-a { grid-row: 1 / 3; }
.mosaic[data-count="1"] { grid-template-columns: 1fr; grid-template-rows: auto; }
.mosaic[data-count="1"] .mosaic-lead { grid-row: 1; aspect-ratio: 16 / 9; }

.mosaic-tile {
  display: block;
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--cream);
  cursor: zoom-in;
  width: 100%;
  height: 100%;
  min-height: 0;
}
.mosaic-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}
.mosaic-tile:hover img { transform: scale(1.02); }

/* Badge and overlay are positioned siblings of the tiles rather than
   children, so neither nests a control inside a control. */
.mosaic-badge {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  pointer-events: none;
}
.mosaic-all {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 10px var(--space-3);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  color: var(--ink);
  font-family: 'Inter', sans-serif;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.mosaic-all:hover { background: var(--ink); border-color: var(--ink); color: var(--white); }

/* ── Two-column body ── */
.listing-body-section { padding: var(--space-6) 0 var(--space-7); }
.listing-body {
  display: grid;
  grid-template-columns: 62fr 34fr;
  gap: var(--space-6);
  /* start, not stretch: a stretched grid item is full-height and
     sticky has nothing to travel within. */
  align-items: start;
}

/* ── Facts column ── */
.status-line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
/* Takes its color from the badge classes, so a Pending listing gets
   the amber dot and a Sold one the grey without a second color map. */
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}

.listing-address {
  font-size: clamp(24px, 2.6vw, 30px);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin-top: var(--space-2);
}
.listing-locality {
  font-size: 17px;
  font-weight: 400;
  color: var(--ink-soft);
  margin-top: var(--space-1);
}
.listing-price {
  font-family: 'Montserrat', sans-serif;
  font-size: clamp(30px, 3.4vw, 40px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin-top: var(--space-4);
}
/* The original ask on a sold listing: the sold number is the
   headline, this is the footnote that proves it. */
.price-strike {
  font-family: 'Inter', sans-serif;
  font-size: var(--text-base);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink-mute);
  text-decoration: line-through;
  margin-left: var(--space-3);
}

.fact-row {
  display: flex;
  flex-wrap: wrap;
  /* Row and column gaps set separately: the wide column gap that
     separates facts on one line is far too much air between them when
     the row wraps onto a second line on a phone. */
  gap: var(--space-4) var(--space-6);
  margin-top: var(--space-4);
}
.fact { display: flex; flex-direction: column; gap: 2px; }
.fact-num {
  font-family: 'Montserrat', sans-serif;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
}
.fact-label {
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.listing-rule {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-5) 0;
}

/* ── Description ── */
.listing-copy { max-width: 68ch; }
.listing-copy p {
  font-size: 17px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin-top: var(--space-3);
}
.listing-copy p:first-child { margin-top: 0; }
/* The hand-written Airtable headline. The portal layout has no slot
   for a marketing title, so it leads the description instead of being
   dropped. */
.listing-lead {
  font-weight: 500;
  color: var(--ink) !important;
}
.section-title + .listing-copy { margin-top: var(--space-3); }

/* ── Video ── */
.listing-video { margin-top: var(--space-6); }
.video-frame {
  margin-top: var(--space-3);
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--charcoal);
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ── Ask card ──
   The only call to action in the page body. Quiet on purpose: a
   hairline border and white, no lifted shadow. The green button
   inside it is the loud part. */
.listing-aside { position: sticky; top: 105px; }
.ask-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
}
.ask-title {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}
.ask-line {
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin-top: var(--space-2);
}
.ask-card .btn { margin-top: var(--space-4); }
.ask-agent {
  font-size: 13px;
  color: var(--ink-mute);
  margin-top: var(--space-3);
  text-align: center;
}
.ask-sep { margin: 0 6px; }

/* ── Mobile call bar ──
   Shown only on phones, and only between the mosaic and the footer.
   The script toggles [hidden]; this rule is what makes that stick. */
.callbar { display: none; }
.callbar[hidden] { display: none !important; }

@media (max-width: 900px) {
  /* One photo on a phone. The other tiles are a desktop luxury: at
     this width they would be postage stamps, and the "view all"
     overlay is the way into the rest anyway. */
  /* Edge to edge, the way the portal apps do it. Negative margins
     exactly cancel the container's side padding, which spans the
     viewport without the horizontal-scrollbar risk that a 100vw
     width carries. Only the mosaic breaks out — everything below it
     keeps the container's padding. */
  .mosaic-section { padding-top: 0; }
  .mosaic {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    margin-left: calc(-1 * var(--space-4));
    margin-right: calc(-1 * var(--space-4));
  }
  /* 3:2 is the shape, 45vh is the floor. On a narrow phone 3:2 alone
     would be shorter than the 4:3 it replaces, which is the opposite
     of the point; on a wide phone the ratio takes over. */
  .mosaic-lead {
    grid-column: 1;
    grid-row: 1;
    aspect-ratio: 3 / 2;
    min-height: 45vh;
    border-radius: 0;
  }
  .mosaic-a, .mosaic-b { display: none; }

  .listing-body { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
  .fact-row { gap: var(--space-4) var(--space-5); }
  /* Sticky is meaningless in a single column — the card would pin
     itself over the content it follows. */
  .listing-aside { position: static; }
  .listing-body-section { padding: var(--space-5) 0 var(--space-6); }

  .callbar {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    padding: 12px var(--space-4);
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--white);
    border-top: 1px solid var(--line);
  }
  .callbar .btn { padding: var(--space-3) var(--space-4); }
}

/* ── Lightbox ── */
body.lightbox-open { overflow: hidden; }
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(23, 23, 23, 0.94);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: var(--space-4);
}
.lightbox[hidden] { display: none; }
.lightbox-stage {
  max-width: min(1200px, 92vw);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.lightbox-stage img {
  max-width: 100%;
  max-height: 80vh;
  width: auto;
  object-fit: contain;
  border-radius: 6px;
}
/* Only the current slide exists as far as the desktop viewer is
   concerned. Keyed on the role rather than on which element it is,
   because the script rotates the roles between the three slides as a
   reader swipes. The script also leaves the off-screen slides' src
   empty at desktop widths, so they are not downloaded either. */
.lightbox-stage img[data-role="prev"],
.lightbox-stage img[data-role="next"],
.lightbox-stage img[data-role="none"] { display: none; }
.lightbox-count { font-size: 13px; color: rgba(255, 255, 255, 0.65); letter-spacing: 0.06em; }
.lightbox-prev,
.lightbox-next,
.lightbox-close {
  background: rgba(255, 255, 255, 0.1);
  border: none;
  color: var(--white);
  cursor: pointer;
  border-radius: var(--radius-pill);
  line-height: 1;
  flex-shrink: 0;
  transition: background 0.15s ease;
}
.lightbox-prev,
.lightbox-next { width: 52px; height: 52px; font-size: 32px; }
.lightbox-close {
  position: absolute;
  top: 18px;
  right: 20px;
  width: 44px;
  height: 44px;
  font-size: 28px;
}
.lightbox-prev:hover,
.lightbox-next:hover,
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }
/* ── Lightbox on a phone ──
   Full-screen, the way the portal apps do it. The desktop layout above
   is untouched.

   The arrows stop being flex children stealing width from the photo
   and move onto the vertical edges instead. Keyboard and click
   handling are unchanged and still drive the desktop. */
@media (max-width: 900px) {
  .lightbox {
    padding: 0;
    gap: 0;
    /* Opaque, not 94%: at full-screen there is nothing behind worth
       showing through, and the bleed made the photo look washed. */
    background: #111;
  }
  .lightbox-prev,
  .lightbox-next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    font-size: 28px;
  }
  .lightbox-prev { left: var(--space-2); }
  .lightbox-next { right: var(--space-2); }
  /* ── Edge peek ──
     The current photo is inset far enough for a sliver of each
     neighbour to show at the screen edges. That sliver is the whole
     affordance: the viewport visibly holds more than one image, so
     swiping is the obvious thing to try and no hint text is needed.

     Three images, never more, whatever the length of the set — the
     script recycles them around the current index.

     Geometry, all derived from one number:
       slide  = 100vw - 44px   (14px peek + 8px gap, both sides)
       step   = slide + gap    = 100vw - 36px
     The script reads the slide width off the DOM rather than
     recomputing it, so these stay in sync. */
  .lightbox-stage {
    position: relative;
    max-width: 100%;
    max-height: 100%;
    width: 100%;
    height: 100%;
    display: block;
    gap: 0;
    /* Explicit, and load-bearing. The stage has to be positioned to be
       the containing block for the slides, and at this width it is the
       whole screen. It also comes after the controls in the DOM, so at
       z-index auto it painted over them and swallowed every tap —
       including the close button, which left the viewer with no way
       out on a phone. Desktop never hit this because the stage is
       static there. */
    z-index: 0;
  }
  .lightbox-close,
  .lightbox-prev,
  .lightbox-next { z-index: 2; }
  .lightbox-stage img {
    position: absolute;
    top: 50%;
    left: 50%;
    width: calc(100vw - 44px);
    max-width: none;
    height: auto;
    /* Room left for the close button and the counter so neither ever
       sits on the photo. */
    max-height: calc(100vh - 140px);
    border-radius: 0;
    /* transform only. Animating left/margin/width would relayout every
       frame; a composited transform does not touch layout at all. */
    transform: translate(-50%, -50%) translateX(calc(var(--x, 0px) + var(--drag, 0px)));
    transition: transform 0.24s ease;
    will-change: transform;
  }
  /* While a thumb is down the photos follow it one-to-one; the
     transition would otherwise lag the finger. */
  .lightbox-stage.is-dragging img { transition: none; }

  /* Position by role, so the roles can rotate between the elements
     without anything moving on screen. */
  .lightbox-stage img[data-role="prev"],
  .lightbox-stage img[data-role="next"] { display: block; }
  .lightbox-stage img[data-role="cur"] { --x: 0px; }
  .lightbox-stage img[data-role="prev"] { --x: calc(-100vw + 36px); }
  .lightbox-stage img[data-role="next"] { --x: calc(100vw - 36px); }
  .lightbox-close {
    top: calc(10px + env(safe-area-inset-top, 0px));
    right: 10px;
    width: 48px;
    height: 48px;
    font-size: 30px;
    background: rgba(255, 255, 255, 0.16);
  }
  .lightbox-count {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(18px + env(safe-area-inset-bottom, 0px));
    text-align: center;
    font-size: var(--text-sm);
  }
}

/* On an actual touch screen the arrows go entirely: swipe is the
   gesture a reader already expects, and dropping them gives the photo
   the full width. Keyed on pointer rather than width on purpose — a
   narrow desktop window is still a mouse, and hiding the arrows there
   would leave nothing to click but the keyboard. */
@media (max-width: 900px) and (pointer: coarse) {
  .lightbox-prev,
  .lightbox-next { display: none; }
}

/* ── Video ── */
.video-section { background: var(--cream); }
.video-frame {
  margin-top: 32px;
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lift);
  background: var(--charcoal);
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ── Seller CTA band ──
   The strategic section. Neighbors browse listings, so every page
   closes on a seller ask, using the same dark treatment the homepage
   reserves for its two most important moments. */
.seller-band { background: var(--charcoal); }
.seller-band-inner { text-align: center; }
.seller-band-inner h2 { max-width: 20ch; margin: 0 auto; }
.seller-band-lede { margin: 18px auto 0; }
.seller-band-actions { margin-top: 32px; }
.seller-band-link {
  display: inline-block;
  margin-top: 20px;
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.6);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.seller-band-link:hover { color: var(--white); }

/* ── Listings index ── */
.listings-head { padding-bottom: var(--section-y); }
.listings-head .lede { margin-top: 14px; }

.filters { margin-top: 34px; display: grid; gap: 12px; }
.chip-row { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.chip-label {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
  margin-right: var(--space-1);
  min-width: 52px;
}
.chip {
  font-family: 'Inter', sans-serif;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.chip:hover { border-color: var(--ink); }
.chip.is-active { background: var(--ink); border-color: var(--ink); color: var(--white); }

.listing-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: var(--space-5);
}
.listing-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.listing-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.listing-card.is-hidden { display: none; }
.listing-card-media { position: relative; aspect-ratio: 4 / 3; background: var(--cream); }
.listing-card-media img { width: 100%; height: 100%; object-fit: cover; }
.listing-card-media .badge { position: absolute; top: 14px; left: 14px; }
.listing-card-body { padding: 22px 22px 26px; display: flex; flex-direction: column; flex: 1; }
.listing-card-body h3 { margin-bottom: 6px; }
.listing-card-address { font-size: var(--text-sm); color: var(--slate); }
.listing-card-price {
  font-family: 'Montserrat', sans-serif;
  font-weight: 700;
  font-size: 22px;
  letter-spacing: -0.02em;
  margin-top: auto;
  padding-top: var(--space-3);
}
.card-price-note {
  font-family: 'Inter', sans-serif;
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
  margin-left: var(--space-2);
}
.listing-card-spec { font-size: var(--text-sm); color: var(--slate); margin-top: var(--space-1); }
.listing-card-spec .dot { color: var(--line); margin: 0 2px; }

.listing-empty { margin-top: var(--space-5); font-size: 17px; color: var(--slate); }

@media (max-width: 980px) { .listing-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .listing-grid { grid-template-columns: 1fr; } }

/* ── Footer ── */
footer { background: var(--cream); padding: 36px 0; border-top: 1px solid var(--line); }
.footer-inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
}
.footer-logo { font-family: 'Montserrat', sans-serif; font-weight: 700; font-size: 15px; color: var(--ink); }
.footer-sep { color: var(--slate); margin: 0 6px; }
footer p { font-size: 13px; color: var(--slate); }
footer a { font-size: var(--text-sm); font-weight: 600; color: var(--ink); }
.footer-links { display: flex; align-items: center; gap: 20px; }
/* Deliberately lighter than the phone number beside it. */
.footer-link { font-weight: 500; color: var(--slate); }
.footer-link:hover { color: var(--ink); }

/* No scroll-reveal on these pages. The rules that used to live here
   hid a section until an IntersectionObserver revealed it; the portal
   layout dropped both the observer and the class, and leaving the
   opacity-0 rule behind would permanently hide anything that ever
   picked the class up again. */
