/* ============================================================================
   Iteration 54 — "Hybrid" (client-directed synthesis, 2026-07-17)
   Baseline: iteration 35 "Blue Hour" (mono ink/white/grey system) with
     1) HOME HERO   ← iteration 45's GSAP-pinned two-stage knockout sequence
        (intro title stage → "415 / Leasing Agent" outline→photo-fill wordmark).
        CSS-sticky pin (not ScrollTrigger pin) so Lenis never judders it.
        Static by default; scroll.js adds html.has-motion only after GSAP+
        ScrollTrigger are live, so any JS failure degrades to a readable
        one-screen hero. Reduced motion / no-JS never see the runway.
     2) CHEVRON STRIP ← 45's `.chevrons` arrow collage (4 chevron-clipped
        photos), drift + scale-on-entry wired in scroll.js.
     3) LSCROLL ROWS ← 46's horizontally scrollable listing rows (scroll-snap
        track + prev/next arrows), restyled to the Blue Hour card chrome.
     4) TYPE        ← 47's system: Newsreader display / Lexend body /
        Playfair Display for the wordmark + hero knockout (tokens swapped in
        styles.css; the tweaks below fix what the metrics change breaks).
   ========================================================================== */

/* ---- 4) Type-system fixups (Newsreader/Lexend metrics vs Compass faces) --- */
/* Playfair Display for the logo/wordmark treatments */
.brand__name b,
.footer-brand b { font-family: var(--ff-logo); font-weight: 600; letter-spacing: 0; }
/* Lexend runs wider than CompassSans — ease the tracked UI labels a touch */
.textlink { letter-spacing: .03em; }
.eyebrow { letter-spacing: .2em; }
/* Newsreader's x-height is smaller than CompassSerif's — give dense serif
   moments a hair more line room */
.tcard__quote { line-height: 1.48; }

/* ============================================================================
   1) HOME HERO — 45's pinned knockout sequence in Blue Hour clothes.
   Markup: .hero54 > .hero54__pin > (.hero54__bg, .hero54__stage--intro,
   .hero54__stage--mark > .knockout). Default = static single screen; motion
   mode (html.has-motion, added by scroll.js) stretches the section into a
   scroll runway and lets the sticky pin + scrubbed timeline play.
   ========================================================================== */
:root { --pin-h: 180vh; } /* 45's runway, shortened again 2026-09-21 (Inna: "three scrolls … annoys people") — about one screen of scroll now */

.hero54 { position: relative; background: var(--paper); }
html.has-motion .hero54 { height: var(--pin-h); }

.hero54__pin {
  position: relative;
  min-height: 100svh;
  overflow: clip;
  display: grid;
}
html.has-motion .hero54__pin {
  position: sticky; top: 0;
  height: 100vh; height: 100svh;
  min-height: 0;
}

.hero54__bg { position: absolute; inset: 0; z-index: 0; }
/* Framed off the TOP of the plate, not the bottom, so the skyline drops below
   the headline and the display line reads entirely against sky (Inna,
   2026-08-17: "the photo needs to come down a little bit"). */
.hero54__bg img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 0%;
  transform-origin: 50% 100%;
}
/* sky-forward framing: light wash keeps the ink title legible up top */
.hero54__bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(244,244,245,.5), rgba(244,244,245,0) 52%);
}

.hero54__stage {
  grid-area: 1 / 1; position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  padding: 0 var(--s-6);
}
/* Seat the display line just under the header. The plate only has ~20px of
   vertical slack under object-fit:cover, so clearing the skyline is the
   headline's job, not the photograph's (Inna, 2026-08-17: the title should be
   "all written on the sky"). */
.hero54__stage--intro {
  justify-content: flex-start;
  padding-top: max(12vh, calc(var(--header-h) + var(--s-6)));
}

.hero54__title {
  font-size: clamp(36px, 5.8vw, 86px);
  max-width: 13.5em;
  letter-spacing: -.03em;
  line-height: 1.04;
  color: var(--ink);
  text-wrap: balance;
}
/* light sheen sweeping across the display line — motion mode only */
.sheen { color: var(--ink); }
html.has-motion .sheen {
  background: linear-gradient(100deg,
    var(--ink) 0 34%, #6f7c86 46%, #b8c4cc 50%, #6f7c86 54%, var(--ink) 66% 100%);
  background-size: 300% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  animation: sheen54 7s ease-in-out infinite;
}
@keyframes sheen54 {
  0%, 20% { background-position: 100% 0; }
  60%, 100% { background-position: 0% 0; }
}

.hero54__actions {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  margin-top: var(--s-7); justify-content: center;
}
/* frosted paper ground so the ghost CTA stays legible over the bright city */
.hero54__actions .btn--ghost {
  border-color: transparent;
  background: rgba(255,255,255,.82);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.hero54__actions .btn--ghost:hover { background: #fff; color: var(--ink); border-color: transparent; }

/* knockout stage — hidden until motion mode; GSAP then owns its opacity */
.hero54__stage--mark { display: none; }
html.has-motion .hero54__stage--mark {
  display: flex; justify-content: center;
  pointer-events: none;
  opacity: 0; visibility: hidden; /* pre-GSAP safe state; inline styles take over */
}

/* Two rows: "Leasing Agent" on top, 415 beneath (Inna, 2026-08-17). Both 415
   layers share row 2 so the photo-fill still knocks through the outline. */
.knockout { position: relative; display: grid; place-items: center; }
.knockout__outline,
.knockout__fill {
  grid-area: 2 / 1;
  font-family: var(--ff-logo);
  font-size: clamp(160px, 34vw, 520px);
  font-weight: 700; letter-spacing: -.02em; line-height: 1;
  /* Playfair leaves ~.36em of leading above the figures, so line-height:1 alone
     drops "415" a long way from the tag. Pull the row back up in the 415's own
     em so the lockup stays tight at every clamp step. */
  margin-top: -.3em;
}
.knockout__outline {
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(255, 255, 255, .9);
}
.knockout__fill {
  background-image: url("../img/momentum/sky-city.webp");
  background-size: cover; background-position: 50% 72%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.knockout__tag {
  grid-area: 1 / 1;
  position: relative; display: grid; place-items: center;
  font-family: var(--ff-logo);
  font-size: clamp(30px, 6vw, 92px);
  font-weight: 500; letter-spacing: -.01em; line-height: 1.05;
}
.knockout__tag-outline, .knockout__tag-fill { grid-area: 1 / 1; }
.knockout__tag-outline { color: transparent; -webkit-text-stroke: 1px rgba(255,255,255,.9); }
.knockout__tag-fill {
  background-image: url("../img/momentum/sky-city.webp");
  background-size: cover; background-position: 50% 62%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

/* ============================================================================
   2) CHEVRON STRIP — 45's arrow collage, seated between the tenants band and
   the Find/Lease tiles. Static = centered, fully visible; scroll.js drifts
   the track ±14vw and scales the chevrons in on entry (motion mode only).
   ========================================================================== */
.chevrons {
  position: relative;
  overflow: clip;
  /* the grey band above dissolves into paper BEHIND the arrows — no hard seam
     now that the tenants section between them is gone (2026-09-17) */
  background: linear-gradient(180deg, var(--bg-alt) 0%, var(--paper) 62%);
  padding: clamp(var(--s-8), 9vw, var(--s-10)) 0;
}
/* a soft ink halo under the strip so the arrows sit IN the page, not on it */
.chevrons::before {
  content: "";
  position: absolute; inset: 16% 6% 4%; z-index: 0;
  pointer-events: none;
  background: radial-gradient(50% 58% at 50% 56%, rgba(17, 17, 20, .08), rgba(17, 17, 20, 0) 72%);
}
.chevrons__track {
  position: relative; z-index: 1;
  display: flex; gap: clamp(16px, 2.6vw, 34px);
  width: max-content; margin-inline: auto;
}
.chevron {
  margin: 0; flex: none;
  width: clamp(170px, 23vw, 372px);
  aspect-ratio: .82;
  clip-path: polygon(0 0, 62% 0, 100% 50%, 62% 100%, 0 100%, 38% 50%);
  overflow: hidden;
  background: var(--bg-alt);
}
.chevron img { width: 100%; height: 100%; object-fit: cover; }

/* ============================================================================
   3) LSCROLL ROWS — 46's horizontal listing rows (For Lease / Recently
   leased) on the homepage: native scroll-snap track, hidden scrollbar,
   prev/next hairline arrow buttons. Cards are 35's .listing markup,
   width-clamped for the track. NO aggregate leased count anywhere.
   ========================================================================== */
.listings-scroller { min-width: 0; }
.lscroll-track {
  display: flex;
  gap: var(--s-5);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  padding-bottom: 4px;
}
.lscroll-track::-webkit-scrollbar { display: none; }
.lscroll-track:empty { display: none; }
/* Card width is a share of the TRACK, not the viewport: sizing off vw meant the
   card count never divided evenly into the container and the third card was
   always sliced. Three whole cards, two gaps, exactly 100%. */
.lscroll-track .listing {
  flex: none;
  width: calc((100% - 2 * var(--s-5)) / 3);
  scroll-snap-align: start;
}
.lscroll-nav { display: flex; justify-content: flex-end; gap: 12px; margin-top: var(--s-4); }
.lscroll-nav[hidden] { display: none; }
.lscroll-btn {
  position: relative;
  width: 48px; aspect-ratio: 1;
  background: #fff;
  border: 1.5px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color .25s var(--ease), opacity .25s var(--ease);
}
.lscroll-btn::after {
  content: "";
  position: absolute; inset: 0;
  margin: auto;
  width: 45%; aspect-ratio: 1;
  background: var(--ink);
  -webkit-mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M3 10h12m-5-5.5L15.5 10 10 15.5" fill="none" stroke="black" stroke-width="2"/></svg>') 50% / contain no-repeat;
  mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M3 10h12m-5-5.5L15.5 10 10 15.5" fill="none" stroke="black" stroke-width="2"/></svg>') 50% / contain no-repeat;
}
.lscroll-btn.prev::after { transform: rotate(180deg); }
.lscroll-btn:hover:not(:disabled) { border-color: var(--ink); }
.lscroll-btn:disabled { opacity: .35; cursor: default; }

.listings-subhead { margin: var(--s-7) 0 var(--s-5); }
.listings-subhead h3 { font-size: clamp(1.25rem, 1.1rem + 0.8vw, 1.7rem); }

@media (max-width: 1040px) {
  /* three cards would be ~220px here — drop to two whole ones, still no sliver */
  .lscroll-track .listing { width: calc((100% - var(--s-5)) / 2); }
  .lscroll-nav { justify-content: flex-start; }
}

@media (max-width: 640px) {
  /* ONE WHOLE CARD per screen — no sliver of the next (Matthew, 2026-08-17:
     "so it looks cleaner"). The card fills the track and the 24px gap parks
     the next one fully offscreen; mandatory snapping means a swipe always
     lands on a whole card rather than resting mid-sliver. The arrows already
     step by card width + gap (listings.js `step()`), so they stay in step. */
  .lscroll-track { scroll-snap-type: x mandatory; }
  .lscroll-track .listing { width: 100%; }
}

@media (max-width: 640px) {
  /* Chevron strip: the arrows are sized so the TRACK overhangs the viewport,
     which is what lets the drift run on a phone (Matthew, 2026-08-17). Four at
     32.5vw plus three 8px gaps ≈ 130vw, against 100vw of viewport + 24vw of
     travel (±12vw in scroll.js) — so no bare paper reaches frame at either
     extreme. .chevrons has `overflow: clip`, so the overhang never becomes
     page-level horizontal scroll. Change either number and the other must
     follow: track width ≥ 100vw + 2 × drift. */
  .chevrons__track {
    gap: 8px;
    /* `margin-inline: auto` centres the track on desktop, but auto margins
       resolve to ZERO once the box is WIDER than its container — so at this
       width the 130vw track hung off the right and the drift slid 47px of bare
       paper into the left edge. Centre the overhang explicitly:
       (100vw − 130vw) / 2 = −15vw. */
    margin-left: -15vw;
    margin-right: 0;
  }
  .chevron { width: calc(32.5vw - 6px); }

  /* Review ticker, phone build (Matthew, 2026-08-17: "you shouldn't see the big
     desktop version of them passing by"). The whole card is re-proportioned,
     not just narrowed: type, stars, byline and padding all step down together,
     which also collapses the row height — cards stretch to the tallest in the
     row, so shrinking the quote is what stops one long review from inflating
     every card beside it. */
  .rticker__group { gap: var(--s-3); padding: 0 var(--s-3); }
  .tcard {
    width: min(272px, 66vw);
    padding: var(--s-4);
    gap: var(--s-2);
    border-radius: calc(var(--radius) * .8);
  }
  .tcard__quote { font-size: 0.88rem; line-height: 1.38; }
  .tcard__stars { font-size: 0.72rem; letter-spacing: 2.5px; }
  .tcard__name { font-size: 0.58rem; letter-spacing: .12em; padding-top: var(--s-2); gap: 7px; }
  .tcard__brand { width: 14px; height: 14px; }
  .tcard__go { top: 9px; right: 9px; width: 16px; height: 16px; }
  .tcard__go svg { width: 13px; height: 13px; }
}

/* ---- home nav: monochrome + transparent over the hero (client, 2026-07-17) --
   35's top state is white-on-dark-glass because its heroes are dark photographs;
   54's sky hero is light, so the blended state is ink-on-transparent instead.
   scroll.js (hero54 in its hero selector) flips .is-scrolled at the hero's
   bottom edge — the standard glass bar and the brand-color phone/social chips
   fade back in there on the header's existing .35s transitions. Menu-open and
   scrolled states are excluded so their ink-on-white rules keep winning. */
.nav__contact { transition: filter .35s var(--ease); }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) {
  background: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border-bottom-color: transparent;
}
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .brand,
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav__link { color: var(--ink); }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .brand__mark { filter: none; }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .brand__name small { color: var(--muted); }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav-cta--ghost { color: var(--ink); border-color: var(--line-2); }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav-cta--ghost:hover { background: var(--ink); color: #fff; border-color: var(--ink); }
/* Hamburger: bars only, no boxed outline (Matthew, 2026-08-17). styles.css
   frames it in a 1px rounded square; killing the WIDTH here beats every
   border-color override downstream, and the 46×46 button keeps its full tap
   area because only the frame goes, not the box. */
.nav-toggle { border: 0; }

body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav-toggle { border-color: var(--line-2); }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav-toggle__bars,
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav-toggle__bars::before,
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav-toggle__bars::after { background: var(--ink); }
body[data-page="home"]:not(.nav-open) .site-header:not(.is-scrolled) .nav__contact { filter: grayscale(1); }

/* ---- motion safety ------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .sheen, html.has-motion .sheen { animation: none; color: var(--ink); background: none; }
  .lscroll-btn { transition: none; }
}

/* ============================================================================
   FOLLOW ME — four equal tiles (Inna, 2026-08-17: "add Substack … make them
   all somewhat the same size"). Levels iteration-30's deliberate Instagram
   bump back down so IG / YouTube / LinkedIn / Substack read as one set.
   ========================================================================== */
.follow__btn { flex: 0 1 250px; justify-content: flex-start; }
.follow__btn--ig {
  padding: var(--s-4) var(--s-6);
  border-width: 2px;
  border-color: transparent;          /* the gradient ring (iter-35) owns the frame */
}
.follow__btn--ig .follow__icon svg { width: 44px; height: 44px; }
.follow__btn--ig .follow__btn-label b { font-size: 1.15rem; }
.follow__btn--ig .follow__btn-label small { font-size: .85rem; }

/* Substack — brand orange, same aura/glow language as the other three */
.follow__btn--sub {
  --aura: linear-gradient(135deg, #FFA366 0%, #FF6719 55%, #C74C00 100%);
  --glow-rest:  0 8px 26px -13px rgba(255, 103, 25, .42);
  --glow-hover: 0 15px 40px -10px rgba(255, 103, 25, .62);
}

/* ============================================================================
   LIGHTBOX GRID OVERVIEW — "View all N photos" opens a grid of EVERY photo,
   each one clickable into the large single view (Matthew, 2026-08-17). Ported
   from iteration 70 "Clearsky" and re-tokenised for 54's ink/white system.
   The !important is load-bearing: .apt-lb__stage and .apt-lb__nav are
   display:flex in iteration-35-apt.css, which otherwise beats [hidden].
   ========================================================================== */
.apt-lb__stage[hidden], .apt-lb__nav[hidden] { display: none !important; }
/* The single view's scrim is 95% opaque, which reads as depth around one photo.
   Behind a full-screen grid that same 5% lets the white header ghost through
   and looks like a rendering fault — so the grid gets a solid ground. */
.apt-lb[data-mode="grid"] { background: #0a0a0d; }
.apt-lb__grid {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: 84px clamp(16px, 4vw, 48px) 40px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  align-content: start;
  max-width: 1280px;
  margin: 0 auto;
}
.apt-lb__grid[hidden] { display: none; }
.apt-lb__thumb {
  appearance: none;
  border: 0;
  padding: 5px;
  background: #fff;
  border-radius: 4px;
  cursor: pointer;
  transition: transform .18s var(--ease);
}
.apt-lb__thumb:hover { transform: scale(1.025); }
.apt-lb__thumb:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }
.apt-lb__thumb img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}
/* back to the grid from the single view — mirrors the .apt-viewall pill */
.apt-lb__gridbtn {
  position: absolute;
  top: 20px; left: 20px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 18px;
  background: #fff;
  color: var(--ink);
  border: 0;
  border-radius: 999px;
  font-weight: 600;
  font-size: .82rem;
  letter-spacing: .06em;
  cursor: pointer;
  transition: transform .18s var(--ease);
}
.apt-lb__gridbtn:hover { transform: scale(1.04); }
.apt-lb__gridbtn[hidden] { display: none; }
@media (max-width: 640px) {
  .apt-lb__grid { grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 72px 12px 32px; }
  .apt-lb__gridbtn { top: 12px; left: 12px; }
}
@media (prefers-reduced-motion: reduce) {
  .apt-lb__thumb:hover, .apt-lb__gridbtn:hover { transform: none; }
}

/* ============================================================================
   MOBILE PASS (Matthew, 2026-08-17) — phones, not shrunk desktop.
   Two classes of fix, both found by `node iterations/tests/mobile-audit.mjs`
   (BASE=http://localhost:8136 PAGES=...):

     1) MICRO-TYPE. The tracked uppercase labels are set 9–11px, which is a
        desktop reading distance. Nothing on a phone goes below ~11.5px, and
        the tracking eases off so the bigger size doesn't force new wraps.
     2) THUMB TARGETS. Footer/contact links were 21–25px tall. They now carry
        a ≥40px hit area WITHOUT growing visually, via padding on the box.

   Not touched, deliberately: the header social glyphs read as 30px but
   iteration-18.css already gives them a 44px thumb area via `.social::after
   { inset: -7px }`. The audit measures the <a> box and can't see that, so it
   reports them every run — a known false positive, not a regression.
   ========================================================================== */
@media (max-width: 640px) {
  /* ---- 1) micro-type ---------------------------------------------------- */
  .brand__name small { font-size: .72rem; letter-spacing: .1em; }
  .listing__tag { font-size: .72rem; letter-spacing: .12em; padding: 6px 10px; }
  .listing__price small { font-size: .76rem; }
  .listing__meta { font-size: .76rem; letter-spacing: .07em; }
  .footer-col h4 { font-size: .78rem; letter-spacing: .14em; }
  .footer-brand__text small { font-size: .78rem; letter-spacing: .1em; }
  .profile__card dt { font-size: .76rem; letter-spacing: .12em; }
  .footer-bottom .powered a { font-size: .78rem; }
  .cap-card small { font-size: .74rem; letter-spacing: .12em; }

  /* ---- 2) thumb targets ------------------------------------------------- */
  /* Every one of these is a link on its own line, so a block box with vertical
     padding buys the 44px without shifting the text or the layout. */
  .footer-contact address a,
  .profile__card dd a,
  .contact-list a,
  .textlink,
  .footer-bottom .powered a {
    display: inline-block;
    padding-block: 11px;
  }
  .brand { min-height: 44px; }
}

/* ---- footer marks, centred on a phone (Matthew, 2026-08-17) ---------------
   iteration-22.css stacks .footer-brand into a column at ≤560px with
   `align-items: flex-start`, which left-parks the 116px round mark in a
   326px column and reads as misaligned. Centre the two LOGO lockups — the
   affiliation mark and the social row — and leave the running text ranged
   left, where a paragraph belongs. */
@media (max-width: 640px) {
  .footer-brand { align-items: center; }
  .footer-affiliation { margin-inline: auto; }
  .footer-social { justify-content: center; }
  /* The LeasingAgent415 wordmark centres with them, and the tagline + blurb
     come along so the block reads as ONE centred unit — a centred mark over a
     left-ranged wordmark over centred icons looked like a mistake. */
  .footer-brand__text { text-align: center; }
}

/* ============================================================================
   DESKTOP-PARITY TABLET HEADER (Matthew, 2026-08-31; phones carved out
   2026-09-21, see the PHONE HEADER block below) — no hamburger from 641px
   up: the COMPLETE desktop bar (brand, links, For Lease / Leased pills,
   call button, socials) stays ONE horizontal line, fixed and visible at all
   times, exactly as on desktop — proportionally scaled down to fit the
   screen via `zoom` on the bar's inner row. The knockout hero runway and the
   stock section stacking are untouched.
   ========================================================================== */
@media (max-width: 1040px) and (min-width: 641px) {
  .nav-toggle { display: none; }
  .site-header__inner { min-height: 0; padding-block: 12px; }
  /* undo styles.css's drawer rules — full desktop nav layout at every width */
  .nav {
    position: static; inset: auto; transform: none;
    background: transparent; border: 0; box-shadow: none;
    display: flex; flex-direction: row; align-items: center;
    gap: var(--s-5);
    margin: 0 0 0 auto; padding: 0;
  }
  .nav__list { flex-direction: row; align-items: center; gap: var(--s-5); padding: 0; }
  .nav__link { padding: 6px 0; border-bottom: 1.5px solid transparent; color: rgba(255,255,255,.92); font-size: 0.82rem; white-space: nowrap; }
  .nav__cta { flex-direction: row; gap: var(--s-2); padding: 0; }
  .nav-cta { min-height: 40px; padding: 0 18px; font-size: 0.78rem; white-space: nowrap; }
  .nav-cta--ghost { color: #fff; border-color: rgba(255,255,255,.55); }
  .nav__contact { display: contents; }
  .nav__call { width: 42px; height: 42px; margin: 0; }
  .nav__call svg { width: 20px; height: 20px; }
  .site-header__social { gap: var(--s-4); padding: 0; }
  .site-header__social .social { width: 22px; height: 22px; position: relative; }
  .site-header__social .social::after { content: ""; position: absolute; inset: -11px; }
  .site-header__social .social--instagram { width: 26px; height: 26px; }
  .brand__name b { white-space: nowrap; }

  /* one line: scale the whole desktop row to the viewport (the bar lays out
     at ~1200px and zooms down). Stepped because `zoom` takes a plain number. */
  .site-header__inner { zoom: 0.80; }
}
@media (max-width: 860px) and (min-width: 641px) { .site-header__inner { zoom: 0.66; } }
@media (max-width: 700px) and (min-width: 641px) { .site-header__inner { zoom: 0.53; } }
/* The scaled bar is shorter than the 76px token the heroes and the residence stage reserve
   for it; the token follows the bar (68px layout row × zoom, rounded up — iteration 122). */
@media (max-width: 1040px) and (min-width: 641px) { :root { --header-h: 56px; } }
@media (max-width: 860px) and (min-width: 641px)  { :root { --header-h: 46px; } }
@media (max-width: 700px) and (min-width: 641px)  { :root { --header-h: 38px; } }

/* ============================================================================
   PHONE HEADER (Matthew, 2026-09-21: "go back to the hamburger thing for
   mobile view") — below 641px the zoomed desktop bar was a 0.30× smear, so
   styles.css's drawer is back: brand + hamburger in the bar, the links, the
   For Lease / Leased pills, the call button and the socials in a white
   drawer that slides down under it (partials.js wireNav()).
   ========================================================================== */
@media (max-width: 640px) {
  .nav-toggle { display: inline-flex; }
  .nav { max-height: calc(100svh - var(--header-h)); overflow-y: auto; }
  .nav__cta { flex-direction: row; gap: var(--s-3); padding: var(--s-4) var(--s-6) 0; }
  .nav__cta .nav-cta { flex: 1; justify-content: center; }
  .nav__link { font-size: 1rem; }
}

/* ============================================================================
   RESIDENCE PAGE ON A PHONE (Matthew, 2026-09-21: "make sure the view all
   photos button works and looks dynamically placed") — iteration-35-apt.css
   makes .apt-collage static when the stage stacks (≤960px), which handed the
   absolutely-positioned arrows and the "View all photos" pill to the
   document: they pinned ~420px down, over the facts and the feature list.
   The photo box owns them again (iteration 122's fix, ported).
   ========================================================================== */
@media (max-width: 960px) {
  .apt-collage { position: relative; top: auto; }   /* top: auto — the desktop sticky top would shift a relative box */
  .apt-viewall { min-height: 44px; }                /* thumb target */
  .apt-back { display: inline-block; padding-block: 12px; }
}

/* ============================================================================
   HOMEPAGE CARD ROWS — LINE UP (Inna, 2026-09-16: "can we make them all line
   up?"). Titles wrap to one or two lines, so the address, price and meta
   strip sat at a different height on every card. Reserve two title lines and
   one address line, and pin the meta strip to the card's bottom edge — the
   flex track already stretches every card to the tallest one. Scope: the
   lscroll rows only; the for-lease / leased grids keep 35's spotlight card.
   ========================================================================== */
.lscroll-track .listing__body { flex: 1 1 auto; }
/* (reel titles: the sitewide one-line rule below applies here too, 2026-09-21) */
.lscroll-track .listing__addr {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lscroll-track .listing__price { margin-bottom: var(--s-3); }
.lscroll-track .listing__meta { margin-top: auto; }

/* Homepage "My listings" row with nothing available (2026-09-17): one quiet
   line between hairlines, in place of the card track. */
.listings-empty-note {
  margin: 0;
  padding: var(--s-6) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  max-width: 64ch;
  font-size: 1.02rem;
  line-height: 1.6;
  color: var(--ink-soft);
}
.listings-empty-note a { color: var(--ink); border-bottom: 1px solid var(--line-2); }
.listings-empty-note a:hover { border-color: var(--ink); }
/* The no-listings lines are editable wording slots that sit in the markup and
   are only toggled by listings.js — [hidden] has to win over any display rule. */
.listings-empty-note[hidden], [data-listings-empty][hidden] { display: none !important; }

/* Facebook follow button (replaced TikTok, Inna 2026-09-21): Facebook-blue ring
   and underglow — same ring/glow convention as the other networks in iteration-35.css. */
.follow__btn--fb {
  --aura: linear-gradient(135deg, #1877F2, #0D5FCE);
  --glow-rest:  0 14px 28px -16px rgba(24, 119, 242, .55);
  --glow-hover: 0 22px 42px -16px rgba(24, 119, 242, .72);
}

/* Follow me — ONE line on desktop (Matthew, 2026-09-17): the five buttons share
   the container width; below 1180px they wrap as before. */
@media (min-width: 1180px) {
  .follow__logos { flex-wrap: nowrap; gap: var(--s-4); }
  .follow__btn { flex: 1 1 0; min-width: 0; padding-inline: clamp(14px, 1.4vw, 26px); justify-content: center; }
  .follow__btn-label b { white-space: nowrap; }
  .follow__btn--ig .follow__btn-label b { font-size: 1.25rem; }
}

/* 2026-09-21 (Matthew: "only see the words"): the word-reveal masks clip the
   heading's 40px text-shadow into a faint box behind every word over the sky.
   Masked headings get no shadow. */
.page-hero h1[data-animate-heading], .hero h1[data-animate-heading], [data-animate-heading] { text-shadow: none; }  /* outranks styles.css .page-hero h1 */

/* ==========================================================================
   Even listing cards + a quicker, smaller hover (Inna, 2026-09-21 texts + video)
   Title and address each reserve two lines (and clamp there), so the price
   and specs rows sit at the same height on every card in a row instead of
   riding up and down with how the text wraps. The hover lift/zoom stays,
   shorter and faster, so a hovered card no longer reads as "higher".
   ========================================================================== */
/* Street on line one, "San Francisco, CA 94xxx" on line two — always (listings.js splits it). */
.listing__addr { line-height: 1.45; min-height: calc(2 * 1.45em); }
.listing__addr span { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Titles on ONE line wherever cards sit side by side; listings.js fitTitles() shrinks long
   ones to fit. The row keeps the design height so a shrunken title never moves the price
   line. Stacked phone cards (≤640px) wrap instead. */
.listing__title { min-height: calc(1.18 * 1.3rem); }
@media (min-width: 641px) {
  .listing__title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* No dark scrim behind the bar on ANY hero: it read as a dark glow across the top of
   every hero image, and Matthew wants the bar as clean as possible over them
   (2026-09-21). The frosted .is-scrolled bar is untouched. */
.site-header::before { display: none; }

/* Header lockup follows the hero like the nav links do — ink on the light home sky, white over dark
   heroes (Matthew, 2026-09-21, after a uniform-black lockup failed contrast on the About film). */

/* About film hero: untouched — its wash and title shadow are the originals from styles.css (Matthew, 2026-09-21: "make it what it was originally with the new clean top bar"). */
#listings-app .listing { transition-duration: .18s; }
#listings-app .listing:hover { transform: translateY(-2px); box-shadow: 0 16px 28px -22px rgba(0,0,0,.35); }
@media (prefers-reduced-motion: reduce) { #listings-app .listing:hover { transform: none; } }


/* ==========================================================================
   Listing-card hover styles — picked in Inna's editor ("Card hover"), stored as
   edits.hover, applied as <html data-card-hover="…"> by site-edit.js. "lift" is
   what shipped on 2026-09-21 (the rules above); the rest are alternatives she
   and Matthew can flip between. Tilt/glare need listings.js for the pointer.
   ========================================================================== */
/* none: the card just sits there */
html[data-card-hover="none"] :is(#listings-app, .lscroll-track, [data-limit]) .listing:hover { transform: none; box-shadow: none; border-color: var(--line); }
/* photo zoom: the photo (a background image) rides on a pseudo layer that scales; the card stays put */
html[data-card-hover="zoom"] .listing__media { overflow: hidden; }
html[data-card-hover="zoom"] .listing__media::after { content: ""; position: absolute; inset: 0; background: inherit; transition: transform .6s var(--ease); }
html[data-card-hover="zoom"] .listing:hover .listing__media::after { transform: scale(1.06); }
html[data-card-hover="zoom"] :is(#listings-app, .lscroll-track, [data-limit]) .listing:hover { transform: none; box-shadow: none; }
/* reveal label: an ink veil rises from the bottom of the photo with a "View residence" line */
html[data-card-hover="reveal"] .listing__media { overflow: hidden; }
html[data-card-hover="reveal"] .listing__media::after { content: "View residence \2192"; position: absolute; left: 0; right: 0; bottom: 0; padding: 44px 16px 14px; color: #fff; font: 600 .7rem/1 var(--ff-normal); letter-spacing: .14em; text-transform: uppercase; background: linear-gradient(180deg, rgba(17,17,20,0), rgba(17,17,20,.78)); transform: translateY(100%); transition: transform .35s var(--ease); }
html[data-card-hover="reveal"] .listing:hover .listing__media::after { transform: none; }
html[data-card-hover="reveal"] :is(#listings-app, .lscroll-track, [data-limit]) .listing:hover { transform: none; box-shadow: none; }
/* monochrome: the photo drops to black & white while the card is under the cursor */
html[data-card-hover="mono"] .listing__media { transition: filter .5s var(--ease); }
html[data-card-hover="mono"] .listing:hover .listing__media { filter: grayscale(1); }
html[data-card-hover="mono"] :is(#listings-app, .lscroll-track, [data-limit]) .listing:hover { transform: none; box-shadow: none; }
/* tilt / glare: the card leans toward the cursor (listings.js sets --rx/--ry/--mx/--my) */
html[data-card-hover="tilt"] :is(#listings-app, .lscroll-track, [data-limit]) .listing:hover,
html[data-card-hover="glare"] :is(#listings-app, .lscroll-track, [data-limit]) .listing:hover { transform: perspective(700px) rotateY(var(--rx, 0deg)) rotateX(var(--ry, 0deg)); transition-duration: .2s; transition-timing-function: linear; box-shadow: 0 24px 40px -28px rgba(0,0,0,.4); }
html[data-card-hover="glare"] .listing { position: relative; }
html[data-card-hover="glare"] .listing::after { content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none; mix-blend-mode: soft-light; opacity: 0; transition: opacity .25s ease; background: radial-gradient(farthest-corner circle at var(--mx, 50%) var(--my, 50%), rgba(255,255,255,.85) 10%, rgba(255,255,255,.6) 20%, rgba(255,255,255,0) 90%); }
html[data-card-hover="glare"] .listing:hover::after { opacity: .7; }
@media (prefers-reduced-motion: reduce) {
  html[data-card-hover] .listing:hover, html[data-card-hover] .listing:hover .listing__media::after { transform: none !important; }
}

/* ==========================================================================
   HOMEPAGE — CONTACT FORM + TIGHTER RHYTHM (Inna, 2026-09-27 videos)
   1) "Why Work with Me" now ends in the landlord intake form (same markup as
      rent-your-space.html), set as a white card under her lead line.
   2) "Too much empty space between these parts … it's huge": the home
      sections were 115px top AND bottom (≈230px of paper between blocks at
      1440px) and the chevron strip another 130px each side. Home only — the
      inner pages keep their rhythm.
   ========================================================================== */
.manifesto__form {
  width: 100%;
  max-width: 46rem;
  margin-top: clamp(var(--s-6), 4.5vh, var(--s-7));
  text-align: left;
}
body[data-page="home"] .manifesto { min-height: 0; }
/* The form section sits right above "In the press" (Inna, 2026-09-27: "people
   have to scroll too far to connect") — between the white listings and the
   white press section, so its grey band still alternates cleanly. */

body[data-page="home"] main > .section { padding-block: clamp(var(--s-7), 5vw, 76px); }
body[data-page="home"] .chevrons { padding-block: clamp(var(--s-6), 4vw, var(--s-8)); }

/* ==========================================================================
   PHONES — keep the intake forms inside the screen (2026-09-27, Inna: "this
   gets cut off"). The reCAPTCHA widget is a fixed 304px; its min-content width
   was stretching the "Why Work with Me" grid (and the Landlords contact grid)
   past the viewport, so the heading, lead line and card ran off the right
   edge. Let those tracks shrink, slim the card padding on phones, and
   lead.js scales the widget down on screens too narrow for 304px.
   ========================================================================== */
.manifesto { grid-template-columns: minmax(0, 1fr); }
.manifesto__inner, .contact-layout > *, .form-card { min-width: 0; }
.form-card { max-width: 100%; }
@media (max-width: 480px) {
  .form-card { padding: var(--s-5) var(--s-4); }
}

/* Review ratings (Yelp / Zillow / Google) on phones (Matthew, 2026-09-27:
   "make this straight"): the one-line row wrapped into three lines whose
   leftover "·" separators pushed each line off-centre. Stack them as one
   neat column — a shared left edge, the block centred — with no separators. */
@media (max-width: 700px) {
  .tlinks { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3); width: max-content; max-width: 100%; margin-inline: auto; }
  .tlinks__sep { display: none; }
}

/* Whole card opens the residence (Matthew, 2026-09-27) — not just the photo.
   The title link's ::after covers the card; hover modes keep working because
   they key off .listing:hover. Keyboard focus rings the whole card. */
.listing { position: relative; }
.listing__link { color: inherit; }
.listing__link::after { content: ""; position: absolute; inset: 0; z-index: 2; }
.listing__link:focus-visible { outline: none; }
.listing:has(.listing__link:focus-visible) { outline: 2px solid var(--ink); outline-offset: 3px; }
