@charset "UTF-8";
/* ==========================================================================
   Vista Lofts — 1627 Washington Avenue, Saint Louis
   Design tokens sampled from media/brand/ and the logo SVGs.
   ========================================================================== */

/* --------------------------------------------------------------- 1. Tokens */
:root {
  /* Brand
     ------------------------------------------------------------------------
     The sage ramp is a surface family: every one of these carries cream text
     and the gold accent, so each is dark enough to do that at AA. The hue
     (188°) and saturation (14%) are the brand's — only the value moved. The
     original #88a2a6 sat at 2.6:1 under cream and 1.9:1 under gold, which is
     roughly the contrast of a watermark; the masthead nav and the feature band
     were both unreadable by the numbers and visibly washed out on a laptop in
     daylight. */
  --sage:        #4a6063;   /* masthead, feature band — cream 6.5:1, gold 4.8:1 */
  --sage-deep:   #415558;   /* the masthead once it sticks */
  --sage-dark:   #39494c;   /* the nav drawer */
  /* Sage as a foreground, not a ground: the spec glyphs on white cards. Held at
     the ramp's old darkest value, which is what those icons have always been. */
  --sage-ink:    #4f6669;
  /* Sage carried most of the way to the page background: a tint that reads as
     the brand without competing with the photography it sits beside. */
  --sage-pale:   #dde5e2;
  /* The dark ground the neighbourhood section stands on. Taupe rather than a
     new hue, so the page has one dark family, not two. */
  --taupe-deep:  #382f2a;
  --cream:       #fffcf5;
  --cream-warm:  #f6efe3;
  --gold:        #f9d58c;
  --gold-deep:   #e8bd68;
  --brown:       #683414;
  --brown-soft:  #8a5432;
  --sand:        #ddc1a7;
  --taupe:       #5b4f47;
  --taupe-dark:  #453b35;
  --sky:         #c9d8eb;

  /* Applied roles */
  --ink:         #33291f;
  /* Secondary copy — captions, fine print, fact labels. Nudged down from
     #6a5c50, which cleared AA on cream but only just, and did it at 12–14px in
     a light weight where the ratio flatters the result. This clears AAA (7:1)
     on both cream grounds, and the two are close enough in hue that nothing
     reads as a different colour. */
  --ink-soft:    #5d5045;
  --rule:        rgba(104, 52, 20, .16);
  --rule-light:  rgba(255, 255, 255, .22);

  /* Type */
  --display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --sans:    "Jost", "Avenir Next", "Segoe UI", system-ui, sans-serif;

  /* Fluid type scale.
     Each value is the exact linear solution between two endpoints — the first
     size at a 360px viewport, the second at 1440px — clamped either side. Sizes
     were chosen by measuring rendered line counts, not by eye: on a phone the
     eyebrow holds to two lines, the page title to two, and body copy to three,
     with the characters spread evenly across them. Because the interpolation is
     continuous there is no jump at any breakpoint and nothing overshoots its
     box at an in-between width. */
  --fs-eyebrow: clamp(0.656rem, 0.629rem + 0.12vw,  0.738rem);   /* 10.5 -> 11.8px */
  --fs-body:    clamp(1rem,     0.979rem + 0.09vw,  1.063rem);   /* 16   -> 17px   */
  --fs-lede:    clamp(1rem,     0.933rem + 0.30vw,  1.2rem);     /* 16   -> 19.2px */
  --fs-h1:      clamp(2.25rem,  1.667rem + 2.59vw,  4rem);       /* 36   -> 64px   */
  --fs-h2:      clamp(1.7rem,   1.317rem + 1.70vw,  2.85rem);    /* 27.2 -> 45.6px */
  --fs-h2-sm:   clamp(1.5rem,   1.233rem + 1.19vw,  2.3rem);     /* 24   -> 36.8px */
  --fs-price:   clamp(1.5rem,   1.417rem + 0.37vw,  1.75rem);    /* 24   -> 28px   */

  /* Metrics */
  --shell: 1240px;
  --gut: clamp(1.25rem, 4vw, 3.5rem);
  --band: clamp(4rem, 9vw, 7.5rem);
  --radius: 2px;

  --shadow-sm: 0 1px 2px rgba(69, 59, 53, .08), 0 4px 14px rgba(69, 59, 53, .07);
  --shadow-md: 0 12px 40px rgba(69, 59, 53, .16);

  --ease: cubic-bezier(.22, .61, .36, 1);
}

/* Increase Contrast on macOS and iOS, and the equivalent on Windows, land here.
   Someone who has asked the OS for more contrast has told us the defaults do
   not work for them, and the honest answer is to stop distinguishing secondary
   copy by lightening it and to make the hairline rules actually visible. Three
   token overrides, so every component follows without knowing this exists. */
@media (prefers-contrast: more) {
  :root {
    --ink-soft:   var(--ink);
    --rule:       rgba(104, 52, 20, .42);
    --rule-light: rgba(255, 255, 255, .5);
  }
}

/* ----------------------------------------------------------- 2. Foundation */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }
/* Smooth scrolling only for anchor jumps — the skip link and the plan page's
   "see the homes". `scroll-behavior: smooth` on the root governs wheel and
   trackpad scrolling too, and on a 9,000px page that fights the reader the
   whole way down and eases them back from the end instead of settling on it.
   Scoped with :has(:target) so a fragment gets the animation and a scroll wheel
   does not; browsers without :has simply jump, which is what a skip link
   should do anyway. */
@media (prefers-reduced-motion: no-preference) {
  html:has(:target) { scroll-behavior: smooth; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-body);
  /* Regular, not Light. Jost at 300 measures fine against WCAG — body copy on
     cream was already 13.9:1 — but the ratio is computed from the colour, not
     from how much of it lands on the page, and at 300 the stems are thin enough
     that paragraphs read faint regardless of what the maths says. 400 is the
     same typeface with the same proportions; nothing else about the page moves.
     The two display weights that were deliberately Light (the drawer's nav, set
     in Fraunces) still say so themselves. */
  font-weight: 400;
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
body.is-locked { overflow: hidden; }

img, svg, video { max-width: 100%; display: block; }
img { height: auto; }

a { color: inherit; }
a:focus-visible, button:focus-visible, [tabindex]:focus-visible, summary:focus-visible {
  outline: 2px solid var(--brown);
  outline-offset: 3px;
}
.masthead a:focus-visible, .topbar a:focus-visible, .site-footer a:focus-visible,
.hero a:focus-visible, .promo a:focus-visible {
  outline-color: var(--gold);
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -.015em;
  margin: 0;
  font-variation-settings: "SOFT" 30, "WONK" 1;
  /* Even out the line lengths of a wrapped headline instead of filling each
     line greedily and leaving a stub on the last one. */
  text-wrap: balance;
}
/* A three-line headline at 1.1 reads cramped on a phone; give it more air. */
@media (max-width: 640px) {
  h1, h2, h3, h4 { line-height: 1.18; }
}

/* Avoid single-word last lines in body copy. */
p, li, figcaption { text-wrap: pretty; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--brown); color: var(--cream);
  padding: .8rem 1.4rem; font: 500 .85rem/1 var(--sans); letter-spacing: .1em; text-transform: uppercase;
}
.skip-link:focus { left: 0; }

.shell { width: min(100% - (2 * var(--gut)), var(--shell)); margin-inline: auto; }
.shell--wide { width: min(100% - (2 * var(--gut)), 1480px); margin-inline: auto; }

.eyebrow {
  font-family: var(--sans);
  font-size: var(--fs-eyebrow); font-weight: 500;
  /* Tracking scales too: .26em on a 360px screen turned a 38-character kicker
     into two badly-broken lines. */
  letter-spacing: clamp(.15em, .09em + .28vw, .26em); text-transform: uppercase;
  color: var(--brown-soft);
  margin: 0 0 1.1rem;
}
.eyebrow::after {
  content: ""; display: block; width: 2.5rem; height: 1px;
  background: currentColor; opacity: .5; margin-top: .9rem;
}
.eyebrow--center { text-align: center; }
.eyebrow--center::after { margin-inline: auto; }
.eyebrow--light { color: var(--gold); }

.lede { font-size: var(--fs-lede); color: var(--ink-soft); text-wrap: balance; }
.measure { max-width: 62ch; }

/* ------------------------------------------------------------- 3. Buttons */
.btn {
  --btn-bg: var(--brown);
  --btn-fg: var(--cream);
  --btn-bd: var(--brown);
  display: inline-flex; align-items: center; justify-content: center; gap: .6em;
  padding: .95em 2.1em;
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bd); border-radius: var(--radius);
  font: 500 .78rem/1 var(--sans); letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none; cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease), transform .25s var(--ease);
}
.btn:hover { transform: translateY(-1px); --btn-bg: var(--brown-soft); --btn-bd: var(--brown-soft); }
.btn--gold  { --btn-bg: var(--gold); --btn-fg: var(--brown); --btn-bd: var(--gold); }
.btn--gold:hover { --btn-bg: var(--gold-deep); --btn-bd: var(--gold-deep); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--cream); --btn-bd: rgba(255,255,255,.55); }
.btn--ghost:hover { --btn-bg: var(--cream); --btn-fg: var(--brown); --btn-bd: var(--cream); }
.btn--outline { --btn-bg: transparent; --btn-fg: var(--brown); --btn-bd: var(--rule); }
.btn--outline:hover { --btn-bg: var(--brown); --btn-fg: var(--cream); --btn-bd: var(--brown); }
.btn--sm { padding: .75em 1.5em; font-size: .72rem; }
/* Fully rounded, cream on dark: the single onward step at the foot of a dark
   section, where a square brown button would disappear into the ground. */
.btn--pill {
  --btn-bg: var(--cream); --btn-fg: var(--ink); --btn-bd: var(--cream);
  border-radius: 100px; padding: 1.05em 2.4em;
}
.btn--pill:hover { --btn-bg: var(--gold); --btn-bd: var(--gold); --btn-fg: var(--brown); }
.btn--pill svg { width: 17px; height: 17px; }

/* -------------------------------------------------------------- 4. Topbar */
.topbar {
  background: var(--taupe-dark); color: var(--cream-warm);
  font-size: .78rem; letter-spacing: .1em;
}
.topbar__in {
  display: flex; align-items: center; justify-content: flex-end; gap: clamp(1rem, 4vw, 2.5rem);
  min-height: 40px; padding-block: .4rem;
}
.topbar a { text-decoration: none; }
.topbar a:hover { color: var(--gold); }
.topbar__call { color: var(--gold); font-weight: 500; text-transform: uppercase; }
.topbar__call strong { font-weight: 500; }
.topbar__portal { text-transform: uppercase; font-weight: 400; }
@media (max-width: 640px) { .topbar__portal { display: none; } }

/* ------------------------------------------------------------ 5. Masthead */
.masthead {
  position: sticky; top: 0; z-index: 60;
  background: var(--sage);
  border-bottom: 1px solid rgba(255,255,255,.18);
  transition: background .3s var(--ease), box-shadow .3s var(--ease);
}
.masthead.is-stuck { background: var(--sage-deep); box-shadow: var(--shadow-md); }
.masthead__in {
  display: flex; align-items: center; gap: clamp(1rem, 3vw, 2.5rem);
  min-height: 84px; padding-block: .85rem;
}
.masthead__brand { flex: 0 0 auto; margin-right: auto; display: flex; }
/* Size by height, not width: the 1004x630 wordmark was driving a 110-124px
   masthead — roughly double Ventana's, eating 13% of a tablet viewport. */
.masthead__brand img { height: clamp(34px, 3.4vw, 46px); width: auto; }

.mainnav { display: flex; gap: clamp(1.1rem, 2.6vw, 2.6rem); }
.mainnav a {
  color: var(--cream); text-decoration: none;
  font-size: .8rem; font-weight: 400; letter-spacing: .18em; text-transform: uppercase;
  padding-block: .5rem; position: relative;
}
.mainnav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: var(--gold);
  transform: scaleX(0); transform-origin: left; transition: transform .3s var(--ease);
}
.mainnav a:hover::after, .mainnav a[aria-current="page"]::after { transform: scaleX(1); }
.mainnav a[aria-current="page"] { color: var(--gold); }

.masthead__cta { display: flex; align-items: center; gap: .9rem; }
.masthead__tel {
  display: grid; place-items: center; width: 40px; height: 40px;
  border: 1px solid rgba(255,255,255,.45); border-radius: 50%; color: var(--cream);
  transition: background .25s var(--ease), color .25s var(--ease);
}
.masthead__tel:hover { background: var(--cream); color: var(--sage-dark); }
.masthead__tel svg { width: 18px; height: 18px; }

.burger {
  display: none; width: 44px; height: 44px; padding: 0;
  background: none; border: 1px solid rgba(255,255,255,.45); border-radius: var(--radius);
  color: var(--cream); cursor: pointer;
}
.burger span { display: block; width: 20px; height: 1.5px; margin: 4px auto; background: currentColor; transition: transform .3s var(--ease), opacity .2s; }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

@media (max-width: 900px) {
  .mainnav { display: none; }
  .burger { display: block; }
  .masthead__in { min-height: 68px; }
}
/* The nav collapses into the drawer at 900, but "Schedule a Tour" is the
   conversion path — keep it until the bar genuinely runs out of room rather
   than dropping it the moment the burger appears. */
@media (max-width: 600px) { .masthead__cta .btn { display: none; } }

/* ---------------------------------------------------------- 6. Nav drawer */
.drawer {
  position: fixed; inset: 0; z-index: 80;
  background: var(--sage-dark); color: var(--cream);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  opacity: 0; visibility: hidden; transition: opacity .35s var(--ease), visibility .35s;
}
.drawer[data-open="true"] { opacity: 1; visibility: visible; }
.drawer__close { position: absolute; top: 1.1rem; right: var(--gut); width: 44px; height: 44px; background: none; border: 0; color: inherit; font-size: 1.9rem; line-height: 1; cursor: pointer; }
.drawer nav { display: grid; gap: .35rem; text-align: center; }
.drawer nav a {
  font-family: var(--display); font-size: clamp(1.9rem, 7vw, 2.9rem); font-weight: 300;
  text-decoration: none; padding: .35rem 1rem;
}
.drawer nav a[aria-current="page"] { color: var(--gold); }
.drawer__meta { margin-top: 2.4rem; text-align: center; font-size: .95rem; }
.drawer__meta a { color: var(--gold); text-decoration: none; }

/* --------------------------------------------------------------- 7. Hero */
.hero { position: relative; display: grid; align-items: end; isolation: isolate; }
.hero__media { position: absolute; inset: 0; z-index: -2; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; }
/* Two scrims: a vertical one to seat the top and bottom of the image, and a
   left-weighted one so the headline keeps contrast over bright window light.

   The horizontal layer runs all the way to 100% and bottoms out at .34 rather
   than fading to .12 by 78%. It used to stop short, which left the right of the
   frame unscrimmed and — because the gradient is angled — put a visible diagonal
   seam where it ended. On the tall home hero the vertical layer covered for it;
   on the shorter .hero--band it read as an overlay that only covered part of
   the photo. */
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(100deg, rgba(40,32,25,.82) 0%, rgba(40,32,25,.55) 45%, rgba(40,32,25,.34) 100%),
    linear-gradient(180deg, rgba(40,32,25,.44) 0%, rgba(40,32,25,.14) 34%, rgba(40,32,25,.70) 100%);
}
.hero__in { padding-block: clamp(5rem, 17vh, 11rem) clamp(3rem, 8vh, 5.5rem); color: var(--cream); }
.hero--tall .hero__in { min-height: min(78vh, 760px); display: flex; flex-direction: column; justify-content: flex-end; }
.hero--band .hero__in { padding-block: clamp(5rem, 16vh, 8rem) clamp(3rem, 8vh, 5rem); }
.hero h1 {
  font-size: var(--fs-h1);
  max-width: 17ch; margin-bottom: 1.4rem;
}
/* balance rather than pretty: a two- or three-line hero paragraph reads far
   better with the lines evened out than with an orphan-free last line. */
.hero p {
  max-width: 58ch; font-size: var(--fs-body);
  color: rgba(255,252,245,.94); text-wrap: balance;
}
.hero__actions { display: flex; flex-wrap: wrap; gap: .9rem; margin-top: 2.2rem; }
/* Sits above the kicker on a detail hero, in the same light-on-photo treatment. */
.hero__crumb { margin: 0 0 1.1rem; font-size: .84rem; letter-spacing: .06em; }
.hero__crumb a { color: rgba(255, 252, 245, .82); text-decoration: none; }
.hero__crumb a:hover { color: var(--gold); }

/* ------------------------------------------------------------ 8. Sections */
.section { padding-block: var(--band); }
.section--tight { padding-block: clamp(3rem, 6vw, 4.5rem); }
/* A filter bar or a breadcrumb is a control, not a new chapter. A full band of
   air above one reads as a gap in the page rather than a break between parts,
   so the section that opens with one keeps its bottom padding and loses most of
   its top. */
.section--flush { padding-block-start: clamp(1.5rem, 3vw, 2.5rem); }
.section--cream { background: var(--cream-warm); }
.section--sage { background: var(--sage); color: var(--cream); }
.section--sage .eyebrow { color: var(--gold); }
.section--sand { background: var(--sand); }
.section h2 { font-size: var(--fs-h2); margin-bottom: 1.2rem; max-width: 20ch; }
.section h2 em { font-style: italic; color: var(--brown-soft); }
.section--sage h2 em { color: var(--gold); }

/* Split: media beside prose */
.split { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--flip .split__media { order: 2; }
  .split--wide-media { grid-template-columns: 1.15fr 1fr; }
}
.split__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; box-shadow: var(--shadow-md); }
.split__body > * + * { margin-top: 1.1rem; }
/* Cap the reading measure. Without this the prose ran 80+ characters per line
   on a wide split column. */
.split__body p { max-width: 62ch; }
.split__body h3 {
  font-family: var(--sans); font-size: .78rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brown);
  margin-top: 2rem;
}
.section--sage .split__body h3 { color: var(--gold); }
.split__body h3 + p { margin-top: .5rem; }

/* Photo strips */
.strip { display: grid; gap: clamp(.5rem, 1.2vw, 1rem); }
.strip--3 { grid-template-columns: repeat(3, 1fr); }
.strip--4 { grid-template-columns: repeat(4, 1fr); }
/* Four across held all the way to 780px, which left 184px thumbnails on a
   tablet. Drop to two while the photos can still read. */
@media (max-width: 1100px) { .strip--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 860px)  { .strip--3 { grid-template-columns: repeat(2, 1fr); } }
.strip figure { margin: 0; overflow: hidden; background: var(--cream-warm); }
.strip img {
  width: 100%; height: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  transition: transform .7s var(--ease);
}
.strip button { display: block; padding: 0; border: 0; background: none; cursor: zoom-in; width: 100%; }
.strip button:hover img { transform: scale(1.045); }
/* Two across is the floor — one photo per row turns the strip into a scroll. */
@media (max-width: 520px) {
  .strip--3, .strip--4 { grid-template-columns: repeat(2, 1fr); gap: .4rem; }
}

/* Spec list — feature enumerations that used to sit as comma runs inside a
   paragraph. Two columns of short items scan far better than a 6-line sentence,
   especially on a phone. */
.spec-list {
  list-style: none; margin: 1.1rem 0 0; padding: 0;
  display: grid; gap: .45rem .5rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  font-size: .96rem;
}
@media (max-width: 359px) { .spec-list { grid-template-columns: 1fr; } }
.spec-list li {
  position: relative; padding-left: 1.1rem; line-height: 1.5;
}
.spec-list li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--gold);
}
.section--cream .spec-list li::before,
.section--plain .spec-list li::before { background: var(--brown-soft); }

/* Chip list — a prose run of proper nouns turned into scannable tags. */
.chip-lead {
  margin: 1.4rem 0 0; font-size: .72rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brown-soft);
}
.chip-lead + .chip-list { margin-top: .6rem; }
.chip-list {
  list-style: none; margin: 1.1rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .4rem;
  font-size: .88rem;
}
.chip-list li {
  padding: .34em .85em;
  border: 1px solid var(--rule); border-radius: 100px;
  background: rgba(255, 255, 255, .5);
  white-space: nowrap;
}
.chip-list li.is-more { border-style: dashed; opacity: .75; }

/* Icon tiles ------------------------------------------------------------- */
/* One component, two grounds: the building's amenities on the sage section and
   the resident portal's four actions on cream. A row of bare rules with a
   heading and a paragraph read as leftover text; a card with a glyph reads as a
   thing.

   Every tile reserves exactly two lines of copy and clamps past that. Without
   it one three-line description dropped its tile's baseline below the three
   beside it, which is the whole reason a row of cards looks untidy. */
.itiles {
  display: grid; gap: clamp(.8rem, 1.8vw, 1.2rem);
  margin-top: clamp(2.2rem, 4.5vw, 3.2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}
.itile {
  display: grid; align-content: start; gap: .5rem;
  padding: clamp(1.3rem, 2.6vw, 1.7rem);
  background: var(--cream-warm); border: 1px solid var(--rule);
  text-decoration: none; color: var(--ink);
}
.itile__icon {
  display: grid; place-items: center; width: 46px; height: 46px; margin-bottom: .45rem;
  color: var(--brown); background: #fff; border: 1px solid var(--rule); border-radius: 50%;
}
.itile__icon svg { width: 22px; height: 22px; }
.itile h3 {
  font-size: 1.12rem; margin: 0; min-height: 2.4em; line-height: 1.2;
  display: flex; align-items: flex-end;
}
.itile p {
  --lines: 2;
  margin: 0; font-size: .92rem; line-height: 1.55; color: var(--ink-soft);
  min-height: calc(var(--lines) * 1.55 * .92rem);
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--lines); line-clamp: var(--lines); overflow: hidden;
}

/* Link variant: the whole tile is the target, with the destination named once
   at the foot of every card so the row reads as four doors into one place. */
.itile--link { transition: border-color .25s var(--ease), box-shadow .3s var(--ease), transform .3s var(--ease); }
.itile--link:hover { border-color: var(--brown-soft); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.itile__go {
  display: inline-flex; align-items: center; gap: .4rem; margin-top: .6rem;
  font-size: .72rem; font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
  color: var(--brown);
}
.itile__go svg { width: 15px; height: 15px; transition: transform .25s var(--ease); }
.itile--link:hover .itile__go svg { transform: translateX(3px); }

/* On the sage section the tiles sit on a light ground of their own. */
.section--sage .itile { background: rgba(255, 255, 255, .5); border-color: rgba(255, 255, 255, .55); }
.section--sage .itile h3 { color: var(--ink); }
/* These tiles are a half-white wash over the band, so their ground moved down
   with it. #4a4038 landed at 4.53:1 there — a pass by 0.03. Two steps darker
   holds the same "quieter than the heading" relationship with room to spare. */
.section--sage .itile p { color: #3c332b; }
.section--sage .itile__icon { background: #fff; border-color: rgba(104, 52, 20, .14); }
.section--sage .lede { color: #fdfaf3; }

/* Promo band ------------------------------------------------------------- */
/* A photograph with the offer in a panel over it, rather than a flat band of
   colour. The panel is nearly opaque and hard-edged: the photograph is there to
   be the reason you read the offer, not to be read through. */
.promo {
  position: relative; isolation: isolate;
  color: var(--cream); text-align: center;
  padding-block: clamp(4rem, 10vw, 7.5rem);
}
.promo__media { position: absolute; inset: 0; z-index: -2; background: var(--taupe); }
.promo__media img { width: 100%; height: 100%; object-fit: cover; }
/* The panel carries its own contrast; this only stops the photo's brightest
   corners from fighting the panel edge. */
.promo::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(40,32,25,.42), rgba(40,32,25,.58));
}
.promo__panel {
  max-width: 44rem; margin-inline: auto;
  padding: clamp(2.2rem, 5vw, 3.6rem) clamp(1.5rem, 5vw, 4rem);
  background: rgba(46, 38, 33, .96);
  box-shadow: var(--shadow-md);
}
/* Named rather than `.promo__panel > p`: that selector also matched the flag and
   the terms line, and being one element more specific it silently reset both of
   their margins — which is why the kicker sat almost on top of the heading. */
.promo__flag {
  font: 500 .74rem/1 var(--sans); letter-spacing: .3em; text-transform: uppercase;
  color: var(--gold); margin: 0 0 1.8rem;
}
/* 18ch held this to three lines at every width, desktop included. */
.promo h2 { font-size: var(--fs-h2-sm); margin-inline: auto; max-width: 20ch; }
/* balance rather than pretty, for the reason .lede and the hero paragraph give:
   a two- or three-line intro reads better with its lines evened out than with
   one orphan-free last line. */
.promo__lede { margin: 1.4rem auto 0; max-width: 48ch; color: rgba(255,252,245,.9); text-wrap: balance; }
/* balance, so the two-line case on a phone splits evenly instead of leaving a
   two-word stub under a full line. */
.promo__terms { font-size: .82rem; opacity: .72; margin: 1.6rem auto 0; max-width: 52ch; text-wrap: balance; }
/* A phone number split across a line break stops being a phone number. */
.promo__terms a { color: var(--gold); white-space: nowrap; }
.promo .btn { margin-top: 2rem; }

/* Multi-tier offer ------------------------------------------------------- */
/* Three lease terms as three boxes, then one band that applies to all three.
   The panel widens to carry the row; the single-offer variant above keeps its
   44rem measure, because one paragraph set to 60rem is not a panel, it is a
   line of text with a border round it. */
.promo__panel--tiers { max-width: 60rem; }
/* The wider panel earns the larger heading step — and a longer measure for the
   line under it, which at the single-offer variant's 48ch sat as a narrow column
   adrift in the middle of a 60rem panel. */
.promo__panel--tiers h2 { font-size: var(--fs-h2); max-width: 16ch; }
.promo__panel--tiers .promo__lede { max-width: 56ch; }
/* The fine print is one sentence, and the wide panel has room to keep it on one
   line down to tablet. Below that it wraps and `balance` evens the two lines. */
.promo__panel--tiers .promo__terms { max-width: 86ch; }

.promo__tiers {
  list-style: none; margin: clamp(1.8rem, 4vw, 2.6rem) 0 0; padding: 0;
  display: grid; gap: clamp(.7rem, 1.8vw, 1.1rem);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Gold, because the offer is the one thing on this page that should interrupt
   you. Brown on gold is 7.1:1 — the strongest pairing the palette has. The blue
   foot is the concept's; --sky is where the palette already keeps that blue. */
.promo__tier {
  display: grid; align-content: center; gap: .25rem;
  padding: clamp(1.1rem, 2.6vw, 1.6rem) clamp(.6rem, 1.6vw, 1.2rem);
  background: var(--gold); color: var(--brown);
  border-bottom: 5px solid var(--sky);
  text-align: center;
}
/* Two lines reserved top and bottom, the same levelling the icon tiles do: with
   "Total Rent Credit" wrapping in two boxes and "Gift Card" not wrapping in the
   third, the figures would sit on three different baselines. */
.promo__tier-term,
.promo__tier-label {
  margin: 0; font-weight: 500; line-height: 1.3; text-transform: uppercase;
  min-height: 2.6em; display: grid; align-content: center;
}
.promo__tier-term  { font-size: clamp(.68rem, 1.5vw, .78rem); letter-spacing: .12em; }
.promo__tier-label { font-size: clamp(.66rem, 1.4vw, .76rem); letter-spacing: .1em; }
/* Fraunces, not the concept's heavy sans: every other number on this site —
   rents, bed counts, square footage — is set in the display face, and a blocky
   sans here would read as artwork someone pasted in. The vw coefficient is
   sized so $1800 still clears its column at 641px, the narrowest width that
   holds three across. */
.promo__tier-amount {
  margin: 0; font-family: var(--display); font-weight: 500;
  font-size: clamp(1.9rem, 4.6vw, 3rem); line-height: 1; letter-spacing: -.02em;
}

/* The 72-hour bonus. A bounded band rather than a fourth box: it applies to all
   three terms, so it must not read as a fourth thing to choose between. */
.promo__bonus {
  margin-top: clamp(.9rem, 2vw, 1.3rem);
  padding: clamp(1rem, 2.4vw, 1.4rem) clamp(1.1rem, 3vw, 2rem);
  border: 1px solid rgba(249, 213, 140, .45);
  background: rgba(249, 213, 140, .09);
}
.promo__bonus-lead {
  /* Wide enough to hold the whole sentence on one line at desktop: this is the
     band's headline, and a headline broken in half stops driving. */
  margin: 0 auto; max-width: 60ch;
  font-size: clamp(1rem, 2.1vw, 1.15rem); font-weight: 500; line-height: 1.4;
  color: var(--gold); text-wrap: balance;
}
/* Weight already carries the emphasis; the phrase must not also break in half. */
.promo__bonus-lead strong { font-weight: 500; white-space: nowrap; }
.promo__bonus-note {
  margin: .5rem auto 0; max-width: 52ch;
  font-size: .92rem; line-height: 1.55; color: rgba(255, 252, 245, .86);
  text-wrap: balance;
}

/* Three tall boxes stacked would push the bonus band and the button off a phone
   screen, and an offer you have to scroll to finish reading is half an offer.
   Below the row's floor each box folds to two short lines instead — the term
   over the figure and its label, sharing a baseline. */
@media (max-width: 640px) {
  .promo__tiers { grid-template-columns: 1fr; gap: .6rem; }
  .promo__tier {
    grid-template-columns: auto auto; justify-content: center;
    align-items: baseline; column-gap: .55rem;
    padding: 1rem 1rem 1.05rem;
  }
  .promo__tier-term { grid-column: 1 / -1; }
  .promo__tier-term,
  .promo__tier-label { min-height: 0; display: block; }
  .promo__tier-amount { font-size: 2.1rem; }
}

/* Feature split ---------------------------------------------------------- */
/* A tinted panel of copy beside one full-height photograph, edge to edge. The
   photo column is a real grid track rather than a floated image, so the panel
   and the photograph are exactly the same height at every width and there is no
   crop ratio to guess at. */
.feature-split { display: grid; }
@media (min-width: 1000px) {
  .feature-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; }
}
.feature-split__panel {
  background: var(--sage-pale);
  display: grid; align-content: center;
  padding: var(--band) var(--gut);
}
@media (min-width: 1000px) {
  /* Line the copy up with the page's own left margin rather than the panel's,
     so it sits on the same axis as every other section above it. */
  .feature-split__panel { padding-inline: max(var(--gut), calc((100vw - var(--shell)) / 2)) clamp(2rem, 4vw, 4rem); }
}
.feature-split__body { max-width: 34rem; }
.feature-split__body h2 { font-size: var(--fs-h2); margin-bottom: 1.1rem; max-width: 14ch; }
.feature-split__body > p { max-width: 46ch; color: var(--ink-soft); }
.feature-split__body .btn { margin-top: clamp(1.8rem, 3vw, 2.4rem); }
.feature-split__media { min-height: clamp(20rem, 60vw, 34rem); }
.feature-split__media img { width: 100%; height: 100%; object-fit: cover; }

/* Feature tiles: a thumbnail and a label, two across. The ten-item bullet list
   these replace read as one grey block; a photograph of the actual washer-dryer
   answers "in-unit laundry" faster than the words do. */
.ftiles {
  list-style: none; margin: clamp(1.8rem, 3.5vw, 2.6rem) 0 0; padding: 0;
  display: grid; gap: .6rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 400px) { .ftiles { grid-template-columns: 1fr; } }
.ftile {
  display: flex; align-items: center; gap: .9rem;
  padding: .5rem .9rem .5rem .5rem;
  background: rgba(255, 255, 255, .58);
  border: 1px solid rgba(255, 255, 255, .7);
  transition: background .25s var(--ease);
}
.ftile:hover { background: rgba(255, 255, 255, .85); }
.ftile img { width: 62px; height: 62px; flex: none; object-fit: cover; }
.ftile span {
  font-size: .92rem; font-weight: 500; line-height: 1.3; color: var(--ink);
  text-wrap: balance;
}

/* Find your home — bed-count cards -------------------------------------- */
/* Dark, because it is the page's second big ask and it should not read as
   another light content section. The card is one link: photo above, a solid
   block of facts below, no separate button to miss. */
.bedcta { background: var(--taupe-deep); color: var(--cream); }
.bedcta__intro { text-align: center; max-width: 46rem; margin-inline: auto; }
.bedcta__intro h2 { font-size: var(--fs-h2); margin-inline: auto; max-width: 16ch; }
.bedcta__intro .lede { color: rgba(255, 252, 245, .82); margin-top: 1rem; }

.bedcta__grid {
  display: grid; gap: clamp(1rem, 2.2vw, 1.6rem);
  margin-top: clamp(2.2rem, 4.5vw, 3.4rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
.bedcta__grid:not(.is-ready):empty { min-height: 20rem; }
.bedcta__note { text-align: center; margin: 0; grid-column: 1 / -1; }
.bedcta__note a { color: var(--gold); }

.bedcard { display: flex; }
.bedcard__link {
  display: grid; grid-template-rows: auto 1fr; width: 100%;
  text-decoration: none; color: var(--cream);
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.bedcard__link:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.bedcard__media { display: block; overflow: hidden; background: rgba(255,255,255,.06); }
.bedcard__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.bedcard__body {
  display: grid; gap: .3rem; justify-items: center; align-content: center;
  padding: 1.3rem 1rem 1.5rem; text-align: center;
  background: var(--brown); transition: background .25s var(--ease);
}
.bedcard__link:hover .bedcard__body { background: var(--brown-soft); }
.bedcard__size { font-size: .85rem; color: rgba(255, 252, 245, .78); }
.bedcard__beds {
  font-family: var(--display); font-size: clamp(1.4rem, 2.4vw, 1.85rem); line-height: 1.1;
}
.bedcard__rent { font-size: .95rem; color: rgba(255, 252, 245, .88); }

.bedcta__go { display: flex; justify-content: center; margin-top: clamp(2.2rem, 4.5vw, 3.2rem); }

/* Neighbourhood --------------------------------------------------------- */
/* A mosaic, not another alternating split. The copy column is fixed on the left
   and the six street photographs fill an asymmetric grid beside it, so the
   section reads as one composed block rather than four stacked ones — and it
   does not repeat the photo-beside-prose pattern used twice above it. */
.hood { background: var(--taupe-deep); color: var(--cream); }

.hmos { display: grid; gap: clamp(.5rem, 1vw, .85rem); }
@media (min-width: 1000px) {
  .hmos {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-template-rows: repeat(2, clamp(150px, 15vw, 215px)) clamp(120px, 12vw, 170px);
  }
  .hmos__intro          { grid-column: 1 / 5;  grid-row: 1 / 4; }
  .hmos__cell--lead     { grid-column: 5 / 10; grid-row: 1 / 3; }
  .hmos__cell--tall     { grid-column: 10 / 13; grid-row: 1 / 2; }
  .hmos__cell:nth-of-type(3) { grid-column: 10 / 13; grid-row: 2 / 3; }
  .hmos__cell:nth-of-type(4) { grid-column: 5 / 8;   grid-row: 3 / 4; }
  .hmos__cell:nth-of-type(5) { grid-column: 8 / 11;  grid-row: 3 / 4; }
  .hmos__cell:nth-of-type(6) { grid-column: 11 / 13; grid-row: 3 / 4; }
}
/* Below the mosaic width, two even columns with the streetscape across the top:
   the asymmetry is the point on a desktop and noise on a phone. */
@media (max-width: 999px) {
  .hmos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hmos__intro { grid-column: 1 / -1; margin-bottom: 1rem; }
  .hmos__cell--lead { grid-column: 1 / -1; }
  .hmos__cell img { aspect-ratio: 4 / 3; }
  .hmos__cell--lead img { aspect-ratio: 16 / 9; }
  /* Five photographs in a two-column grid leaves one on a row of its own. The
     streetscape spans the full width and the remaining four pair off; the sixth
     is dropped rather than left hanging. */
  .hmos__cell:nth-of-type(6) { display: none; }
}

.hmos__intro { display: flex; flex-direction: column; align-items: flex-start; padding-right: clamp(1rem, 2.5vw, 2.5rem); }
.hmos__intro h2 { font-size: var(--fs-h2); max-width: 12ch; margin-bottom: 1rem; }
.hmos__lede { color: rgba(255, 252, 245, .8); max-width: 44ch; margin: 0; }

/* The one number worth setting in display type: it is the whole argument for
   the location, and it is measured rather than claimed. */
.hmos__score { display: flex; align-items: baseline; gap: .7rem; margin: auto 0 0; padding-top: 2rem; }
.hmos__score strong {
  font-family: var(--display); font-size: clamp(3rem, 5.5vw, 4.5rem);
  font-weight: 400; line-height: .9; color: var(--gold);
}
.hmos__score span {
  font-size: .74rem; font-weight: 500; letter-spacing: .2em; text-transform: uppercase;
  color: rgba(255, 252, 245, .7);
}
@media (max-width: 999px) { .hmos__score { margin-top: 1.6rem; padding-top: 0; } }

.hmos__partners { display: flex; align-items: center; gap: 1.6rem; margin-top: 1.8rem; opacity: .9; }
.hmos__partners img { height: 30px; width: auto; }
.hmos__partner--fh { height: 46px !important; }

.hmos__cell { position: relative; margin: 0; overflow: hidden; background: rgba(255, 255, 255, .06); }
.hmos__cell button { display: block; width: 100%; height: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.hmos__cell img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--ease); }
.hmos__cell button:hover img { transform: scale(1.04); }

/* The places nearby as an index, not a cloud of pills: a rule between each and
   a heading beside them says "list of destinations" without the tag-cloud look
   that made the old version read as clutter. */
.hmos__index {
  display: grid; gap: .6rem 2.5rem; align-items: start;
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: clamp(1.6rem, 3vw, 2.2rem);
  border-top: 1px solid rgba(255, 255, 255, .16);
}
@media (min-width: 760px) { .hmos__index { grid-template-columns: auto minmax(0, 1fr); } }
.hmos__index h3 {
  font-family: var(--sans); font-size: .72rem; font-weight: 500;
  letter-spacing: .24em; text-transform: uppercase; color: var(--gold); margin: 0;
  padding-top: .35rem;
}
/* Eight destinations, so a fixed four across fills two even rows. auto-fit gave
   five on the first row and three on the second, which reads as a list that ran
   out rather than a set. */
.hmos__index ul {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0 2rem;
}
/* Three columns wherever three fit, two on a phone. Nine divides evenly by three
   but not by two, so in the two-column range the last item spans the row. The
   rule keys off :nth-child(odd) rather than a hard-coded ninth, so it stays
   right if the list gains or loses a place. */
@media (min-width: 560px) { .hmos__index ul { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 559px) {
  .hmos__index ul { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 1.2rem; }
  .hmos__index li:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
.hmos__index li {
  padding: .5rem 0; font-size: .95rem;
  color: rgba(255, 252, 245, .86);
  border-bottom: 1px solid rgba(255, 255, 255, .1);
}

/* Leasing steps ---------------------------------------------------------- */
/* The offset rule is the one flourish on the page: a hairline in the accent
   colour, set off the photograph's top-right, which frames it without putting a
   border on it. Drawn on a pseudo-element so it costs no markup and can be
   dropped below 950px, where an offset frame only steals width. */
.leasing__in { display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
@media (min-width: 950px) {
  .leasing__in { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.leasing__media { position: relative; margin: 0; }
.leasing__media img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; position: relative; z-index: 1; }
@media (max-width: 949px) { .leasing__media img { aspect-ratio: 4 / 3; } }
@media (min-width: 950px) {
  /* Above the photograph, not behind it. Offset up and right by the same 20px
     on every side, so two edges sit outside the image on the page and two run
     across it — a frame, rather than the L you get when the image covers the
     half of it that overlaps. */
  .leasing__media::before {
    content: ""; position: absolute; inset: -20px -20px 20px 20px; z-index: 2;
    border: 1px solid var(--gold-deep); pointer-events: none;
  }
}

.leasing__body h2 { font-size: var(--fs-h2); margin-bottom: 1.1rem; max-width: 12ch; }
.leasing__body > p { max-width: 46ch; color: var(--ink-soft); }

.steps {
  list-style: none; counter-reset: step;
  margin: clamp(1.8rem, 3.5vw, 2.4rem) 0 0; padding: 0;
  display: grid; gap: 1.3rem;
}
.steps li {
  counter-increment: step;
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .2rem 1rem;
}
.steps li::before {
  content: counter(step, decimal-leading-zero);
  grid-row: 1 / span 2;
  font-family: var(--display); font-size: 1.05rem; line-height: 1.35;
  color: var(--brown-soft);
}
.steps h3 {
  font-family: var(--sans); font-size: .78rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brown);
  margin: 0; align-self: center;
}
/* Two lines reserved, so the three steps keep the same rhythm even if one of
   them is re-worded shorter than the others later. */
.steps p {
  margin: 0; font-size: .96rem; line-height: 1.6; color: var(--ink-soft); max-width: 44ch;
  min-height: calc(2 * 1.6 * .96rem);
}
@media (max-width: 520px) { .steps p { min-height: 0; } }

.leasing__actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: clamp(1.8rem, 3.5vw, 2.4rem); }
.leasing__note { margin: 1.1rem 0 0; font-size: .86rem; color: var(--ink-soft); }
.leasing__note a { color: var(--brown); }

/* Quiet call-to-action band */
.cta-band { background: var(--brown); color: var(--cream); text-align: center; padding-block: clamp(3rem, 7vw, 5rem); }
.cta-band h2 { font-size: var(--fs-h2-sm); margin-inline: auto; max-width: 24ch; }
.cta-band p { margin: 1rem auto 0; max-width: 52ch; color: rgba(255,252,245,.82); }
.cta-band .btn { margin-top: 1.9rem; }

/* ------------------------------------------------ 9. Search band (widget) */
.searchband {
  position: relative; isolation: isolate;
  padding-block: clamp(3.5rem, 8vw, 5.5rem);
  color: var(--cream); text-align: center;
}
.searchband__media { position: absolute; inset: 0; z-index: -2; }
.searchband__media img { width: 100%; height: 100%; object-fit: cover; }
.searchband::after { content: ""; position: absolute; inset: 0; z-index: -1; background: rgba(45,36,28,.62); }
.searchband h2 { font-size: clamp(1.55rem, 2.9vw, 2.15rem); margin-bottom: 2rem; }

/* -------------------------------------------------------- 10. Widget host */
.widget-host { min-height: 320px; }
.widget-host:not(.is-ready) { display: grid; place-items: center; }
.widget-fallback {
  text-align: center; padding: clamp(2.5rem, 6vw, 4rem) 1.5rem;
  border: 1px solid var(--rule);
  background: var(--cream-warm);
}
.widget-fallback h2 { font-size: clamp(1.5rem, 3vw, 2.1rem); margin: 0 auto .9rem; max-width: 24ch; }
.widget-fallback p { margin: 0 auto; max-width: 48ch; color: var(--ink-soft); }
.widget-fallback .btn { margin-top: 1.7rem; }

.widget-note {
  margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--rule);
  font-size: .86rem; color: var(--ink-soft); text-align: center;
}
.widget-note a { color: var(--brown); }

/* Breadcrumb back-link above an embedded detail view. */
.crumbs { margin-bottom: 1.6rem; font-size: .84rem; letter-spacing: .06em; }
.crumbs a { color: var(--brown); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }

/* --------------------------------------------------- Home detail (listing) */

.ldp { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; margin-top: clamp(2rem, 4vw, 3rem); }
@media (min-width: 900px) {
  .ldp { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
  /* The plan drawing is its own child rather than the tail of the main column,
     so the two can be ordered independently on a phone. On a desktop it simply
     goes back under the main column. */
  .ldp__main      { grid-column: 1; grid-row: 1; }
  .ldp__aside     { grid-column: 2; grid-row: 1 / span 2; }
  .ldp__planblock { grid-column: 1; grid-row: 2; }
}
/* Reading order on a phone: photographs, then the rent and the two buttons,
   then the home, then what it costs, then the layout. `display: contents` on the
   aside lets its two cards be placed separately in that sequence. */
@media (max-width: 899px) {
  .ldp { display: flex; flex-direction: column; gap: clamp(2rem, 5vw, 2.75rem); }
  .ldp__card--price { order: 1; }
  .ldp__main        { order: 2; }
  .ldp__card--costs { order: 3; }
  .ldp__planblock   { order: 4; }
}

.ldp__main h1 { font-size: clamp(1.75rem, 3vw, 2.4rem); margin-bottom: .6rem; max-width: 22ch; }
.ldp__address { color: var(--ink-soft); margin: 0 0 1.6rem; }
.ldp__prose { max-width: 62ch; }
.ldp__prose p { margin: 0 0 1rem; }
.ldp__prose ul { margin: 0 0 1rem; padding-left: 1.1rem; }
/* Scoped so it outranks `.section h2`, which would otherwise impose the display
   face and a 46px size on what is a small label. */
.ldp .ldp__subhead {
  font-family: var(--sans); font-size: .78rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brown);
  margin: 2.2rem 0 0; max-width: none;
}
.ldp .ldp__aside .ldp__subhead { margin-top: 0; margin-bottom: .9rem; }

/* The plan drawing as a way through to the rest of the layout, not a dead end. */
.ldp__plan {
  display: block; margin-top: 1rem; padding: 1rem;
  background: var(--cream-warm); border: 1px solid var(--rule);
  text-decoration: none; color: var(--brown);
  transition: border-color .2s var(--ease);
}
.ldp__plan:hover { border-color: var(--brown-soft); }
/* Reserve the box: this is lazy and low on the page, so without a ratio it
   collapses to zero height and the section jumps when it arrives. */
.ldp__plan img { width: 100%; max-width: 420px; margin-inline: auto; aspect-ratio: 4 / 3; object-fit: contain; }
.ldp__plan span {
  display: inline-flex; align-items: center; gap: .4rem; margin-top: .8rem;
  font-size: .78rem; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
}
/* Without the drawing there is nothing above the caption to space it away from. */
.ldp__plan--link span { margin-top: 0; }
.ldp__plan svg { width: 15px; height: 15px; }

.ldp__aside { display: grid; gap: 1rem; position: sticky; top: 96px; }
/* `display: contents` so the two cards become flex items of .ldp and can be
   ordered independently of each other — the rent card above the home, the costs
   card below it. Declared here rather than beside the other mobile rules above
   because the `display: grid` on the line before would otherwise win on source
   order. */
@media (max-width: 899px) { .ldp__aside { display: contents; position: static; } }
.ldp__card {
  background: var(--cream-warm); border: 1px solid var(--rule);
  padding: clamp(1.4rem, 3vw, 1.9rem);
}
.ldp__card--quiet { background: #fff; }
.ldp__price {
  font-family: var(--display); font-size: clamp(2rem, 3.4vw, 2.6rem);
  line-height: 1; margin: 0 0 1.4rem; color: var(--brown);
}
.ldp__price span {
  font-family: var(--sans); font-size: .78rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-soft); margin-left: .5rem;
}
.ldp__tmp { margin: -1rem 0 1.3rem; font-size: .8rem; color: var(--ink-soft); }
.ldp__facts { list-style: none; margin: 0 0 1.6rem; padding: 0; }
.ldp__facts li {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .6rem 0; border-top: 1px solid var(--rule); font-size: .95rem;
}
.ldp__facts li span { color: var(--ink-soft); }
.ldp__facts li strong { font-weight: 500; text-align: right; }
.ldp__cta { width: 100%; margin-bottom: .6rem; }
.ldp__contact { margin: 1.1rem 0 0; font-size: .88rem; color: var(--ink-soft); text-align: center; }
.ldp__contact a { color: var(--brown); }
.ldp__finePrint { margin: 1.1rem 0 0; font-size: .78rem; color: var(--ink-soft); }

/* The one figure a prospect is actually adding up. */
.ldp__total {
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline;
  margin: 0; padding: .8rem 0 0; border-top: 2px solid var(--rule);
  font-size: .95rem;
}
.ldp__total span { color: var(--ink-soft); }
.ldp__total strong { font-family: var(--display); font-size: 1.3rem; font-weight: 400; color: var(--brown); }

.ldp__runin {
  font-family: var(--sans); font-size: .72rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brown);
  margin: 1.7rem 0 .2rem;
}
.ldp__card--costs .ldp__facts { margin-bottom: 0; }

/* Leasing information ----------------------------------------------------- */
/* Three columns of figures, each closed by a small note. Two things keep it from
   reading as clutter: the prose is below the numbers rather than above them, so
   every column starts its figures on the same line; and the columns share row
   tracks through subgrid, so the headings, the blocks of figures and the notes
   each sit on one baseline even though Pets has a row the others do not.

   The breakpoint is a container query, not a viewport one: this block lives in a
   column that is full width below 900px and a narrow 1.5fr track above it, so
   viewport width says nothing useful about how much room it actually has. */
/* width:100% is load-bearing, not tidying. `container-type: inline-size` brings
   `contain: inline-size` with it, which tells the browser to size this box
   without looking at its contents — and below 900px, where .ldp is a flex
   column, that left it 0px wide and the whole column vanished. An explicit
   width gives it something to be: the track width as a grid item, the container
   width as a flex item. */
.ldp__main { container-type: inline-size; container-name: ldpmain; width: 100%; min-width: 0; }

.leaseinfo {
  display: grid; gap: clamp(1.4rem, 3vw, 2.2rem);
  grid-template-columns: 1fr; margin-top: 1.2rem;
}
.leaseinfo__col { display: grid; grid-template-rows: auto 1fr auto; min-width: 0; }

@container ldpmain (min-width: 620px) {
  .leaseinfo { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@supports (grid-template-rows: subgrid) {
  @container ldpmain (min-width: 620px) {
    /* row-gap 0 because a subgrid inherits the parent's row gaps, and one here
       lands between the figures and the note in every column — on top of the
       note's own top margin. The columns space themselves; the gap is only
       wanted between the columns, and when they are stacked. */
    .leaseinfo { grid-template-rows: auto auto auto; row-gap: 0; }
    .leaseinfo__col { grid-row: span 3; grid-template-rows: subgrid; }
  }
}

.leaseinfo h3 {
  font-size: 1.05rem; margin: 0 0 .1rem;
  padding-bottom: .55rem; border-bottom: 1px solid var(--rule);
}
.leaseinfo .ldp__facts { margin: 0; align-self: start; }
.leaseinfo .ldp__facts li { padding: .5rem 0; font-size: .88rem; gap: .6rem; }
/* The heading's rule already separates the first row. */
.leaseinfo .ldp__facts li:first-child { border-top: 0; }
.leaseinfo .ldp__facts li span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A price that wraps stops being a price. */
.leaseinfo .ldp__facts li strong { white-space: nowrap; }

.leaseinfo__note {
  margin: .9rem 0 0; padding-top: .7rem; border-top: 1px solid var(--rule);
  font-size: .78rem; line-height: 1.5; color: var(--ink-soft);
}

/* The one figure a prospect is actually adding up. */
.ldp__total {
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline;
  margin: 0; padding: .8rem 0 0; border-top: 2px solid var(--rule);
  font-size: .95rem;
}
.ldp__total span { color: var(--ink-soft); }
.ldp__total strong { font-family: var(--display); font-size: 1.3rem; font-weight: 400; color: var(--brown); }

.ldp__runin {
  font-family: var(--sans); font-size: .72rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--brown);
  margin: 1.7rem 0 .2rem;
}
.ldp__card--costs .ldp__facts { margin-bottom: 0; }

/* ----------------------------------------------------- 11. Portal tiles */
.portal-intro { text-align: center; max-width: 46rem; margin-inline: auto; }
/* `.section h2` caps the heading at 20ch. A block narrower than its parent and
   without auto margins sits left however its text is aligned — which is why the
   centred card that used to be here had a left-hanging heading over centred
   copy. Every centred heading on the site needs this pair. */
.portal-intro h2 { font-size: clamp(1.6rem, 3.2vw, 2.2rem); margin-inline: auto; margin-bottom: .9rem; }
.portal-intro .lede { margin: 0 auto; max-width: 54ch; }

.portal-note {
  margin: clamp(1.6rem, 3vw, 2.2rem) auto 0; max-width: 52ch;
  text-align: center; font-size: .86rem; color: var(--ink-soft);
}
.portal-note a { color: var(--brown); }

/* ------------------------------------------------- 12. Gallery & lightbox */
/* One medium main image with a thumbnail rail: vertical beside the photo from
   900px up, a horizontal scroll-snap strip below that. Used by the unit page
   and by the home page slideshow. */

.gallery { display: grid; gap: .6rem; }
@media (min-width: 900px) {
  .gallery { grid-template-columns: minmax(0, 1fr) 96px; align-items: start; }
}

.gallery__main { position: relative; background: var(--cream-warm); overflow: hidden; }
.gallery__open {
  display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in;
}
.gallery__open img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  animation: gallery-in .35s var(--ease);
}
@media (min-width: 900px) { .gallery__open img { aspect-ratio: 3 / 2; max-height: 62vh; } }
@keyframes gallery-in { from { opacity: .35; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .gallery__open img { animation: none; } }

/* Line art on white: contain it, or the drawing is cropped to 3:2 and the room
   labels go over the edge. */
.gallery__open.is-plan img { object-fit: contain; background: #fff; }

/* Top right, on hover: what you are looking at and what a click will do. Both
   chips are the same 34px height and the same cream fill, so they read as one
   control rather than two decorations that happened to land together. */
.gallery__corner {
  position: absolute; right: .7rem; top: .7rem;
  display: flex; align-items: center; gap: .4rem;
  opacity: 0; transition: opacity .2s var(--ease); pointer-events: none;
}
.gallery__main:hover .gallery__corner,
.gallery__open:focus-visible ~ .gallery__corner { opacity: 1; }
.gallery__count, .gallery__zoom {
  display: inline-flex; align-items: center; justify-content: center;
  margin: 0; height: 34px;
  background: rgba(255, 252, 245, .9); color: var(--ink); border-radius: 100px;
  /* A slide can be a photograph or a floor plan on white. The hairline is what
     keeps a cream chip readable over both. */
  box-shadow: 0 1px 6px rgba(69, 59, 53, .22);
}
.gallery__count { padding-inline: .75rem; font: 400 .78rem/1 var(--sans); letter-spacing: .04em; }
.gallery__zoom { width: 34px; }
.gallery__zoom svg { width: 17px; height: 17px; }

/* The tour, over the bottom-right of the photograph. Always visible rather than
   revealed on hover: it is the only thing on the image that is not navigation,
   and on a touch screen there is no hover to reveal it with. A stacked square on
   a phone, where width is the scarce thing; a wide bar from 700px, where the
   photograph is big enough that a 78px chip disappears into it. */
.gallery__tour {
  position: absolute; right: .7rem; bottom: .7rem; z-index: 3;
  display: grid; justify-items: center; gap: .28rem;
  width: 78px; padding: .6rem .3rem .55rem;
  background: rgba(255, 252, 245, .9); color: var(--brown);
  border: 0; border-radius: var(--radius); cursor: pointer;
  font: 500 .62rem/1.35 var(--sans); letter-spacing: .12em; text-transform: uppercase;
  backdrop-filter: blur(4px); box-shadow: 0 1px 6px rgba(69, 59, 53, .22);
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.gallery__tour:hover { background: var(--cream); transform: translateY(-1px); }
.gallery__tour svg { width: 24px; height: 24px; }
/* Narrow enough that "Video Tour" wraps to two centred lines in the chip. A <br>
   would have to be hidden at the wide size, and hiding it takes the space with
   it — "VideoTour". */
.gallery__tour span { max-width: 4.2em; text-align: center; }
@media (min-width: 700px) {
  .gallery__tour {
    right: 1rem; bottom: 1rem;
    display: flex; align-items: center; justify-content: center; gap: .7rem;
    width: auto; padding: .95rem 1.5rem;
    background: rgba(255, 252, 245, .7);
    font-size: .78rem; letter-spacing: .16em;
    box-shadow: 0 2px 14px rgba(69, 59, 53, .26);
  }
  .gallery__tour:hover { background: rgba(255, 252, 245, .92); }
  .gallery__tour svg { width: 28px; height: 28px; }
  .gallery__tour span { max-width: none; white-space: nowrap; }
}

.gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 40px; height: 40px;
  background: rgba(255, 252, 245, .92); color: var(--ink);
  border: 0; border-radius: 50%; cursor: pointer;
  opacity: 0; transition: opacity .2s var(--ease);
}
.gallery__nav svg { width: 19px; height: 19px; }
.gallery__nav[data-dir="-1"] { left: .7rem; }
.gallery__nav[data-dir="1"] { right: .7rem; }
.gallery__main:hover .gallery__nav, .gallery__nav:focus-visible { opacity: 1; }
@media (hover: none) { .gallery__nav { display: none; } }

/* Thumbnails: a scrolling strip on a phone, a scrolling rail on a desktop. */
.gallery__thumbs {
  display: flex; gap: .45rem; overflow: auto; scroll-snap-type: x proximity;
  scrollbar-width: thin; padding-bottom: .2rem;
}
@media (min-width: 900px) {
  .gallery__thumbs {
    flex-direction: column; scroll-snap-type: y proximity;
    max-height: 62vh; padding-bottom: 0; padding-right: .2rem;
  }
}
.gallery__thumb {
  position: relative; flex: 0 0 84px; padding: 0; border: 0; background: none;
  cursor: pointer; scroll-snap-align: start;
  opacity: .6; transition: opacity .2s var(--ease), outline-color .2s var(--ease);
  outline: 2px solid transparent; outline-offset: -2px;
}
@media (min-width: 900px) { .gallery__thumb { flex: 0 0 auto; } }
.gallery__thumb img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.gallery__thumb:hover { opacity: .85; }
.gallery__thumb[aria-selected="true"] { opacity: 1; outline-color: var(--brown); }
/* The plan thumbnail is line art, so it is contained rather than cropped and
   carries a glyph in the corner. A centred mark like the old play button would
   sit over the middle of the rooms, which is the part you are looking at. */
.gallery__thumb.is-plan img { object-fit: contain; background: #fff; }
.gallery__planmark {
  position: absolute; right: .2rem; bottom: .2rem;
  display: grid; place-items: center; width: 21px; height: 21px;
  color: var(--cream); background: rgba(40, 32, 25, .78); border-radius: var(--radius);
}
.gallery__planmark svg { width: 13px; height: 13px; }

/* Lightbox --------------------------------------------------------------- */
.lightbox {
  width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
  margin: 0; padding: clamp(1rem, 3vw, 2.5rem); border: 0;
  background: rgba(24, 19, 15, .96); color: var(--cream);
  display: grid; grid-template-rows: 1fr auto; gap: 1rem;
  opacity: 0; transition: opacity .25s var(--ease);
}
.lightbox[data-open="true"] { opacity: 1; }
.lightbox::backdrop { background: rgba(24, 19, 15, .96); }
.lightbox:not([open]) { display: none; }

.lightbox__stage {
  margin: 0; display: grid; place-items: center; gap: .8rem;
  grid-template-rows: minmax(0, 1fr) auto; min-height: 0;
}
.lightbox__stage img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain; box-shadow: var(--shadow-md);
}
/* Video mode: one 16:9 frame, no thumbnails, no arrows. */
.lightbox__video {
  width: min(100%, 1400px); aspect-ratio: 16 / 9;
  background: #000; box-shadow: var(--shadow-md);
}
.lightbox__video iframe { width: 100%; height: 100%; border: 0; display: block; }
.lightbox[data-mode="video"] .lightbox__stage { grid-template-rows: auto auto; align-content: center; }

.lightbox__caption {
  margin: 0; max-width: 70ch; text-align: center;
  font-size: .88rem; line-height: 1.5; color: rgba(255, 252, 245, .8);
}

.lightbox__close, .lightbox__nav {
  position: absolute; display: grid; place-items: center;
  background: rgba(255, 252, 245, .12); color: var(--cream);
  border: 0; border-radius: 50%; cursor: pointer;
  transition: background .2s var(--ease);
}
.lightbox__close:hover, .lightbox__nav:hover { background: rgba(255, 252, 245, .26); }
.lightbox__close { top: 1rem; right: 1rem; width: 44px; height: 44px; }
.lightbox__close svg { width: 20px; height: 20px; }
.lightbox__nav { top: 45%; width: 48px; height: 48px; }
.lightbox__nav svg { width: 24px; height: 24px; }
.lightbox__nav[data-dir="-1"] { left: max(1rem, 2vw); }
.lightbox__nav[data-dir="1"] { right: max(1rem, 2vw); }
/* Touch swipes instead; the arrows would only cover the photo. */
@media (hover: none) { .lightbox__nav { display: none; } }

.lightbox__count {
  position: absolute; left: 50%; top: 1.1rem; transform: translateX(-50%); margin: 0;
  font-size: .8rem; letter-spacing: .1em; color: rgba(255, 252, 245, .7);
}

.lightbox__thumbs {
  display: flex; gap: .4rem; overflow-x: auto; justify-content: flex-start;
  padding-bottom: .2rem; scrollbar-width: thin;
}
@media (min-width: 700px) { .lightbox__thumbs { justify-content: center; } }
.lightbox__thumbs button {
  flex: 0 0 74px; padding: 0; border: 0; background: none; cursor: pointer;
  opacity: .5; outline: 2px solid transparent; outline-offset: -2px;
  transition: opacity .2s var(--ease);
}
.lightbox__thumbs button img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.lightbox__thumbs button.is-plan img { object-fit: contain; background: #fff; }
.lightbox__thumbs button:hover { opacity: .8; }
.lightbox__thumbs button[aria-selected="true"] { opacity: 1; outline-color: var(--gold); }

/* ------------------------------------------------------------- 14. Footer */
/* Four columns on one baseline: the brand and three lists that share a heading
   style, an item rhythm and an icon column. What was here before was three
   columns of loose paragraphs at three different lengths, with a 1.5rem display
   phone number in one of them setting a rhythm the other two did not follow. */
.site-footer {
  background: var(--taupe-dark); color: var(--cream-warm);
  padding-block: clamp(3rem, 5.5vw, 4.5rem) 1.75rem;
}
.site-footer a { color: inherit; text-decoration: none; }
.site-footer a:hover { color: var(--gold); }

.footer-cols { display: grid; gap: clamp(2rem, 4vw, 3rem); align-items: start; }
@media (min-width: 620px) { .footer-cols { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .footer-cols { grid-template-columns: 1.5fr 1fr 1.3fr 1.3fr; } }

.footer-brand p {
  margin: 1.3rem 0 0; max-width: 32ch;
  font-size: .93rem; line-height: 1.6; color: rgba(255, 252, 245, .74);
}
/* Sized by height: at width:150px the lockup's own ratio drove a 95px row. */
.footer-mark { height: 58px; width: auto; }

.footer-cols h2 {
  font-family: var(--sans); font-size: .72rem; font-weight: 500;
  letter-spacing: .24em; text-transform: uppercase; color: var(--gold);
  margin: 0 0 1.2rem; max-width: none;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }
.footer-col li { font-size: .95rem; line-height: 1.45; }
/* The email item is a button, not a mailto: — it opens the EliseAI email window.
   It has to read as the links beside it. */
.footer-col button {
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.footer-col button:hover { color: var(--gold); }

/* The icon is a fixed first column, so every label starts on the same axis
   whether or not the row above it wrapped to two lines. */
.footer-list li { display: grid; grid-template-columns: 17px minmax(0, 1fr); gap: .7rem; align-items: start; }
.footer-list svg { width: 17px; height: 17px; margin-top: .15rem; color: var(--gold); opacity: .85; }
/* .62 put this at 5.2:1 in a 13.8px light weight — the faintest text on the
   site. .80 keeps it clearly secondary to the address above it and clears 7:1. */
.footer-hours { margin: 1.1rem 0 0; font-size: .86rem; color: rgba(255, 252, 245, .80); }

.footer-base {
  margin-top: clamp(2.25rem, 4vw, 3rem); padding-top: 1.5rem;
  border-top: 1px solid var(--rule-light);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.25rem;
}
.footer-eho { display: inline-flex; align-items: center; gap: .7rem; }
.footer-eho svg { color: var(--cream-warm); flex: none; }
.footer-eho span { font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; opacity: .72; }
.footer-base p { margin: 0; font-size: .82rem; opacity: .7; }
@media (max-width: 560px) {
  .footer-base { flex-direction: column; align-items: flex-start; gap: 1.1rem; }
}

/* ------------------------------------------------- 15. Listings & filters */
/* The availability grid, the filter bar and the listing card. Built here rather
   than layered over third-party markup, so there is nothing to fight. */

.listings { min-height: 320px; }
.listings__head { margin-bottom: 1.6rem; }

/* Filter bar ------------------------------------------------------------- */
.fbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .7rem .8rem;
  background: var(--cream-warm); border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.fbar__fields { display: flex; flex-wrap: wrap; gap: .5rem; flex: 1 1 auto; }
.fbar__field { position: relative; }

.fbar__toggle {
  display: inline-flex; align-items: center; gap: .5rem;
  min-height: 42px; padding: .5rem .85rem;
  background: #fff; color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--radius);
  font: 400 .85rem/1.2 var(--sans); cursor: pointer;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.fbar__toggle:hover { border-color: var(--brown-soft); }
.fbar__field.is-open .fbar__toggle { border-color: var(--brown); }
.fbar__toggle svg { width: 17px; height: 17px; flex: none; color: var(--brown-soft); }
.fbar__toggle svg:last-child { width: 14px; height: 14px; opacity: .6; transition: transform .2s var(--ease); }
.fbar__field.is-open .fbar__toggle svg:last-child { transform: rotate(180deg); }
.fbar__glyph { font-weight: 500; color: var(--brown-soft); width: 17px; text-align: center; }
.fbar__value { white-space: nowrap; }

.fbar__search {
  min-height: 42px; padding: .5rem .85rem; min-width: 12rem;
  background: #fff; color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--radius);
  font: 400 .9rem/1.2 var(--sans);
}

.fbar__panel {
  position: absolute; z-index: 30; top: calc(100% + .4rem); left: 0;
  min-width: max(100%, 13rem); padding: .8rem;
  background: #fff; border: 1px solid var(--rule); border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
/* A panel opening near the right edge would otherwise push the page sideways. */
.fbar__field:nth-last-child(-n+2) .fbar__panel { left: auto; right: 0; }

.fbar__set { border: 0; margin: 0; padding: 0; display: grid; gap: .15rem; }
.fbar__check {
  display: flex; align-items: center; gap: .55rem;
  padding: .4rem .3rem; font-size: .9rem; cursor: pointer; white-space: nowrap;
}
.fbar__check:hover { color: var(--brown); }
.fbar__check input { accent-color: var(--brown); width: 16px; height: 16px; }
/* Options the current inventory cannot satisfy stay visible but inert, so the
   filter still reads as a complete range rather than a shifting list. */
.fbar__check.is-off { opacity: .38; cursor: not-allowed; }
.fbar__check.is-off:hover { color: inherit; }

.fbar__rows { display: grid; gap: .7rem; }
.fbar__rows label {
  display: grid; gap: .3rem;
  font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}
.fbar__rows input {
  padding: .5rem .6rem; font: 400 .95rem/1.2 var(--sans); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--radius); background: #fff;
}

.fbar__sort { display: flex; align-items: center; gap: .5rem; margin-left: auto; }
.fbar__sort label {
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft);
}
.fbar__sort select {
  min-height: 42px; padding: .5rem 2rem .5rem .8rem;
  font: 400 .85rem/1.2 var(--sans); color: var(--ink);
  background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236a5c50' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right .6rem center / 14px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  -webkit-appearance: none; appearance: none; cursor: pointer;
}
.fbar__sorticon { display: none; }

/* Below the desktop layout the bar becomes an even grid of four: Sort, Beds,
   Price, Move-in. Baths and Pets are hidden rather than dropped — they can still
   arrive in the query string, their pills still show and still clear, and they
   come back on rotation. Five controls in a two-column grid left an orphan on
   the last row, which is what made this look unfinished on a phone.

   `display: contents` on the field wrapper flattens the controls into the bar's
   own grid, which is what lets the sort — a sibling of that wrapper, and last in
   the DOM for the desktop layout — be ordered first here. */
@media (max-width: 899px) {
  .fbar {
    display: grid; gap: .45rem; padding: .6rem;
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  .fbar__fields { display: contents; }
  .fbar__field { min-width: 0; }
  .fbar__field--search { grid-column: 1 / -1; order: -2; }
  /* Scoped to the availability grid: its bar has five controls and needs to
     lose two to make an even four. The home page's search band already renders
     exactly four, and hiding one there would leave it with three. */
  .listings .fbar__field[data-field="baths"],
  .listings .fbar__field[data-field="pets"] { display: none; }

  .fbar__toggle { width: 100%; justify-content: flex-start; padding-inline: .7rem; }
  .fbar__value { overflow: hidden; text-overflow: ellipsis; }
  .fbar__toggle svg:last-child { margin-left: auto; }
  .fbar__search { width: 100%; min-width: 0; }

  /* The sort becomes the first of the four and takes the toggles' shape: the
     glyph in the same place, the label hidden, the native chevron kept. */
  .fbar__sort { order: -1; position: relative; margin-left: 0; width: 100%; }
  .fbar__sort label { display: none; }
  .fbar__sorticon {
    display: grid; place-items: center; position: absolute; left: .7rem; top: 50%;
    transform: translateY(-50%); pointer-events: none; color: var(--brown-soft);
  }
  .fbar__sorticon svg { width: 17px; height: 17px; }
  .fbar__sort select { width: 100%; min-width: 0; padding-left: 2.2rem; font-size: .85rem; }

  /* A panel anchored to a quarter-width control would spill off the screen. */
  .fbar__panel { left: 0; right: auto; min-width: min(15rem, calc(100vw - 2.2rem)); }
  .fbar__field:nth-of-type(even) .fbar__panel { left: auto; right: 0; }
}
@media (max-width: 600px) {
  .fbar { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Filter pills ----------------------------------------------------------- */
.pills { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: .9rem; }
.pills__lead {
  margin: 0; font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft);
}
.pills__list { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; margin: 0; padding: 0; }
.pill {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .7rem; font: 400 .82rem/1.3 var(--sans); color: var(--ink);
  background: var(--sand); border: 1px solid transparent; border-radius: 100px; cursor: pointer;
  transition: background .2s var(--ease);
}
.pill:hover { background: var(--gold); }
.pill__label { color: var(--ink-soft); }
.pill svg { width: 13px; height: 13px; opacity: .7; }
.pills__clear {
  background: none; border: 0; padding: .35rem .2rem; cursor: pointer;
  font: 400 .8rem/1.3 var(--sans); color: var(--brown); text-decoration: underline;
}

/* Count ------------------------------------------------------------------ */
.listings__meta { margin: 1.4rem 0 1rem; }
.listings__count { margin: 0; font-size: .9rem; color: var(--ink-soft); }
.listings__count strong { color: var(--ink); font-weight: 500; }

/* Grid and card ---------------------------------------------------------- */
.listings__grid {
  display: grid; gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
}
.listings__more { height: 1px; }

.ucard {
  display: flex; flex-direction: column;
  background: #fff; border: 1px solid var(--rule); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow-sm);
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.ucard:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.ucard__media { position: relative; background: var(--cream-warm); }
.ucard__strip {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior-x: contain;
}
.ucard__strip::-webkit-scrollbar { display: none; }
.ucard__slide { flex: 0 0 100%; scroll-snap-align: center; display: block; }
.ucard__slide img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }

/* The cover on a layout that is not photographed home by home: the drawing,
   contained on white rather than cropped to 3:2, which would take the room
   labels off the edges. The inset is inside the 3:2 box, not around it — padding
   the slide instead would make this card's media taller than a photographed
   card's and knock the two out of line across a row of the grid. */
.ucard__slide--plan { background: #fff; }
.ucard__slide--plan img { object-fit: contain; padding: .6rem; }

.ucard__empty {
  display: grid; place-items: center; gap: .6rem;
  aspect-ratio: 3 / 2; color: var(--ink-soft); font-size: .85rem;
}
.ucard__empty svg { width: 30px; height: 30px; opacity: .5; }

.ucard__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 34px; height: 34px;
  background: rgba(255, 252, 245, .9); color: var(--ink);
  border: 0; border-radius: 50%; cursor: pointer;
  opacity: 0; transition: opacity .2s var(--ease), background .2s var(--ease);
}
.ucard__nav svg { width: 17px; height: 17px; }
.ucard__nav[data-slide="-1"] { left: .5rem; }
.ucard__nav[data-slide="1"] { right: .5rem; }
.ucard:hover .ucard__nav, .ucard__nav:focus-visible { opacity: 1; }
.ucard__nav:hover { background: var(--cream); }
/* Touch has no hover, and swiping is the natural gesture there — hide the
   arrows rather than parking them permanently over the photo. */
@media (hover: none) { .ucard__nav { display: none; } }

.ucard__count {
  position: absolute; left: .6rem; bottom: .6rem; margin: 0;
  padding: .2rem .5rem; font: 400 .72rem/1.4 var(--sans);
  color: var(--cream); background: rgba(40, 32, 25, .68); border-radius: 100px;
}
.ucard__flag {
  position: absolute; left: 0; top: .8rem; margin: 0;
  padding: .28rem .7rem; font: 500 .68rem/1.4 var(--sans);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--brown); background: var(--gold);
}

/* Media badges ----------------------------------------------------------- */
/* Sit over the bottom-right of a card thumbnail: one plays the tour, one opens
   the plan drawing. Above the card's full-bleed link overlay so a tap opens the
   viewer rather than the home. */
.media-badges {
  position: absolute; right: .6rem; bottom: .6rem; z-index: 3;
  display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .35rem;
}
.media-badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .3rem .6rem .3rem .45rem;
  background: rgba(40, 32, 25, .72); color: var(--cream);
  border: 0; border-radius: 100px; cursor: pointer;
  font: 500 .7rem/1 var(--sans); letter-spacing: .1em; text-transform: uppercase;
  backdrop-filter: blur(4px);
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.media-badge:hover { background: var(--brown); transform: translateY(-1px); }
.media-badge svg { width: 15px; height: 15px; }
/* Below 380px two labels cost more than they earn; the icons still read. */
@media (max-width: 380px) { .media-badge__text { display: none; } .media-badge { padding: .35rem; } }

.ucard__body { display: flex; flex-direction: column; gap: .55rem; padding: 1.2rem 1.3rem 1.3rem; flex: 1; }
.ucard__eyebrow {
  margin: 0; font-size: .68rem; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase; color: var(--brown-soft);
}
/* Plan, home number and rent share one row. The rent used to have a row of its
   own; beside the name it costs nothing and reads as the same fact. */
.ucard__head { display: flex; align-items: flex-start; justify-content: space-between; gap: .8rem; }
.ucard__ident { display: grid; gap: .35rem; min-width: 0; }
.ucard__name { margin: 0; font-size: 1.5rem; }
.ucard__name a { text-decoration: none; }
.ucard__name a:hover { color: var(--brown); }
/* The whole card is clickable, but only the heading link is in the tab order —
   a card with five focusable copies of one destination is hostile to keyboards. */
.ucard__name a::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.ucard { position: relative; }
.ucard__media, .ucard__actions, .ucard__note { position: relative; z-index: 2; }

/* Two lines, always — clamped with an ellipsis when the copy is longer, and
   holding the space when it is shorter. Marketing titles vary from four words
   to twelve; without a reserved box, one long one pushes that card's price,
   specs and availability out of line with every card beside it. */
.ucard__tagline {
  --tagline-lines: 2;
  --tagline-size: .92rem;
  --tagline-leading: 1.5;
  margin: .15rem 0 0; color: var(--ink-soft);
  font-size: var(--tagline-size); line-height: var(--tagline-leading);
  min-height: calc(var(--tagline-lines) * var(--tagline-leading) * var(--tagline-size));
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--tagline-lines); line-clamp: var(--tagline-lines);
  overflow: hidden;
}
.ucard__price { display: grid; justify-items: end; gap: .2rem; margin: 0; flex: none; text-align: right; }
.ucard__amount {
  font-family: var(--display); font-size: var(--fs-price);
  line-height: 1; color: var(--brown);
}
.ucard__per {
  display: inline-flex; align-items: center; gap: .25rem;
  font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft); white-space: nowrap;
}
.ucard__note svg { width: 14px; height: 14px; }

/* One row of equal cells, icon over label, rather than the two-by-two grid of
   labelled rows this used to be: the same four facts in half the height, and
   scannable across a row of cards because every cell is in the same place.
   grid-auto-flow keeps it honest when a commercial listing has only two specs.
   The labels are the short forms — a 72px cell holds "1,450 sqft", not
   "1,450 sq. ft.". */
.ucard__specs {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: .5rem .4rem;
  list-style: none; margin: .55rem 0 0; padding: .85rem 0 0;
  border-top: 1px solid var(--rule);
  font-size: .8rem; color: var(--ink-soft);
}
.ucard__specs li { display: grid; justify-items: center; gap: .35rem; min-width: 0; text-align: center; }
.ucard__specs li span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ucard__specs svg { width: 19px; height: 19px; color: var(--sage-ink); }
@media (max-width: 400px) { .ucard__specs { font-size: .75rem; } }

.ucard__avail {
  display: inline-flex; align-items: center; gap: .38rem;
  margin: .8rem 0 0; padding-top: .8rem; border-top: 1px solid var(--rule);
  width: 100%; font-size: .88rem; color: var(--ink);
}
.ucard__avail svg { width: 16px; height: 16px; color: var(--sage-ink); flex: none; }

.ucard__actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: auto; padding-top: .9rem; }
.ucard__actions .btn { flex: 1 1 auto; }

/* Empty state ------------------------------------------------------------ */
.listings__empty {
  text-align: center; padding: clamp(2.5rem, 6vw, 4rem) 1.5rem;
  border: 1px solid var(--rule); background: var(--cream-warm);
}
/* margin-inline as well as the size: `.section h2` caps this at 20ch, and a
   block narrower than its parent hangs left however its text is aligned. */
.listings__empty h2 { font-size: clamp(1.4rem, 2.8vw, 1.9rem); margin: 0 auto .9rem; max-width: 24ch; }
.listings__empty p { margin: 0 auto; max-width: 48ch; color: var(--ink-soft); }
.listings__empty-actions {
  display: flex; flex-wrap: wrap; gap: .7rem; justify-content: center; margin-top: 1.7rem;
}

/* ------------------------------------------------------- 15b. Floor plans */

/* The line the home page's bed-count cards land under: what was narrowed, and
   the way back out of it. */
.plans__filter {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .9rem;
  margin: 0 0 1.4rem; font-size: .9rem; color: var(--ink-soft);
}
.plans__filter strong { color: var(--ink); font-weight: 500; }
.plans__filter a { color: var(--brown); }

.plans {
  display: grid; gap: clamp(1rem, 2vw, 1.5rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}

.plan {
  display: flex; flex-direction: column;
  background: #fff; border: 1px solid var(--rule); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow-sm);
  transition: box-shadow .3s var(--ease), transform .3s var(--ease);
}
.plan:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

/* The drawings are line art on white, so they are contained rather than cropped
   and sit on a warm ground so the sheet edge reads. */
.plan__drawing { position: relative; background: var(--cream-warm); padding: .6rem; }
.plan__drawing .media-badges { right: 1rem; bottom: 1rem; }
.plan__drawing img { width: 100%; aspect-ratio: 4 / 3; object-fit: contain; }
.plan__noimage { display: grid; place-items: center; aspect-ratio: 4 / 3; color: var(--ink-soft); }
.plan__noimage svg { width: 40px; height: 40px; opacity: .4; }
/* A heading for structure, a badge for appearance — so the `.section h2/h3`
   display-face rules do not turn it into a 46px serif. */
.plan__code {
  position: absolute; left: 0; top: 1rem; margin: 0; max-width: none;
  padding: .3rem .8rem; font: 500 .75rem/1.3 var(--sans);
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--cream); background: var(--brown);
}

.plan__body { display: flex; flex-direction: column; gap: .6rem; padding: 1.1rem 1.3rem 1.3rem; flex: 1; }
.plan__price {
  margin: 0; font-family: var(--display); font-size: var(--fs-price);
  line-height: 1; color: var(--brown);
}
.plan__price span {
  display: inline-block; margin-left: .45rem;
  font-family: var(--sans); font-size: .7rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-soft);
}
.plan__price--ask { color: var(--ink); }

.plan__specs {
  display: flex; flex-wrap: wrap; gap: .35rem .9rem;
  list-style: none; margin: 0; padding: 0; font-size: .87rem; color: var(--ink-soft);
}
.plan__specs li { display: inline-flex; align-items: center; gap: .35rem; }
.plan__specs svg { width: 16px; height: 16px; color: var(--sage-ink); flex: none; }

.plan__stock { margin: 0; font-size: .85rem; color: var(--ink-soft); }
.plan__cta { margin-top: auto; }

/* Plan detail ------------------------------------------------------------ */
/* The same two-column frame the home detail page uses (.ldp), so a layout and a
   home are read the same way: what it is on the left, what it costs and what to
   do next in a sticky card on the right, the homes themselves below both. The
   drawing and four facts used to share one row, which left the right half of a
   desktop page empty and pushed the cards below the fold for nothing. */
/* No top margin: the identity that used to sit above this moved into the page
   hero, so the drawing is now the first thing in its column and has to start on
   the same line as the price card beside it. */
.plan-figure {
  position: relative; margin: 0;
  padding: clamp(.8rem, 2vw, 1.5rem);
  background: var(--cream-warm); border: 1px solid var(--rule);
}
.plan-figure__open { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; }
.plan-figure__open img { width: 100%; aspect-ratio: 4 / 3; object-fit: contain; }

/* On a desktop the drawing and the card are one pair: same top, same bottom.
   The card's content sets the row height and the figure stretches to it, which
   means dropping the drawing's own 4:3 — an intrinsic height would make the
   figure the taller of the two and reopen the gap this closes.

   1.25fr rather than 1.5fr because the drawing is contained: at the card's
   height a 4:3 drawing is only so wide, and a wider left column just pads it
   with more cream. Narrowing the track to roughly the drawing's own width at
   that height is what removes the slack. */
@media (min-width: 900px) {
  .ldp--plan { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); align-items: stretch; }
  .ldp--plan .ldp__main { display: flex; grid-row: auto; }
  /* The listing page spans its aside over two rows to clear the plan drawing
     underneath. This page has no second row, so the span was reaching over an
     empty track *and its 56px gap* — the card came out 56px taller than the
     drawing it is meant to match. */
  .ldp--plan .ldp__aside { grid-row: auto; }
  .ldp--plan .plan-figure { flex: 1; }
  /* The drawing sets the row height and the card stretches to meet it, rather
     than the other way round: a contained 4:3 drawing has an intrinsic height,
     so shrinking it to the card's would mean fighting its own aspect ratio.
     Block layout inside the card, deliberately — a flex column here stops the
     children's margins collapsing and the card overshoots the drawing it is
     supposed to match. */
  .ldp--plan .ldp__aside { position: static; align-self: stretch; }
  .ldp--plan .ldp__aside .ldp__card { height: 100%; }
}
.plan-figure__zoom {
  position: absolute; right: .8rem; top: .8rem;
  display: grid; place-items: center; width: 34px; height: 34px;
  background: rgba(255, 252, 245, .9); color: var(--ink); border-radius: 50%;
  opacity: 0; transition: opacity .2s var(--ease); pointer-events: none;
}
.plan-figure__zoom svg { width: 17px; height: 17px; }
.plan-figure:hover .plan-figure__zoom, .plan-figure__open:focus-visible .plan-figure__zoom { opacity: 1; }
.plan-figure .media-badges { right: clamp(1.3rem, 2.6vw, 2rem); bottom: clamp(1.3rem, 2.6vw, 2rem); }
.plan-figure .plan__noimage { background: #fff; }

/* The drawing and the card now end level, so the rule sits close under both
   rather than clearing whichever ran longer. */
.plan-detail__homes {
  margin-top: clamp(1.4rem, 2.2vw, 1.8rem);
  padding-top: clamp(1.4rem, 2.2vw, 1.8rem);
  border-top: 1px solid var(--rule);
  scroll-margin-top: 96px;
}
.plan-detail__homeshead {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: .3rem 1.5rem; margin-bottom: 1.3rem;
}
.plan-detail__homeshead p { margin: 0; font-size: .9rem; color: var(--ink-soft); }

/* The step out of a layout that did not fit. */
.plan-detail__onward {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .6rem 1.4rem; text-align: center;
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: clamp(1.6rem, 3vw, 2.2rem);
  border-top: 1px solid var(--rule);
}
.plan-detail__onward p { margin: 0; color: var(--ink-soft); }
.plan-detail__onward .btn svg { width: 15px; height: 15px; }

.plan-detail__subhead {
  font-family: var(--sans); font-size: .76rem; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase; color: var(--brown);
  margin: 0 0 1.2rem;
}
.plan-detail__homeshead .plan-detail__subhead { margin: 0; }


/* ------------------------------------------------------ 15c. Search band */
/* The band paints light text over a photo, but the controls themselves sit on
   white, so their colours are pinned rather than inherited. */

.searchband__form { display: grid; gap: 1.1rem; justify-items: center; }
.searchband__form .fbar {
  background: rgba(255, 252, 245, .94); border-color: transparent;
  justify-content: center; max-width: 62rem; width: 100%;
}
.searchband__form .fbar__fields { flex: 0 1 auto; justify-content: center; }
.searchband__form .fbar__sort { display: none; }   /* the grid owns sorting */
.searchband__form .fbar__toggle,
.searchband__form .fbar__value { color: var(--ink); }

.searchband__go { display: grid; gap: .7rem; justify-items: center; }
/* Full cream. This sits on a photograph behind a flat 62% scrim, so its
   contrast is only ever as good as the brightest frame someone swaps in;
   at .82 the current picture left barely any headroom. */
.searchband__all { font-size: .85rem; color: var(--cream); }
.searchband h2 { text-wrap: balance; }

/* --------------------------------------------------------- 15d. Skeletons */

.skeleton-card {
  background: #fff; border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
}
.skeleton-card__body { display: grid; gap: .6rem; padding: 1.2rem 1.3rem 1.6rem; }
.skeleton { background: var(--cream-warm); position: relative; overflow: hidden; }
.skeleton--media { aspect-ratio: 3 / 2; }
.skeleton--plan { aspect-ratio: 4 / 3; }
.skeleton--line { height: .85rem; border-radius: 2px; }
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .75), transparent);
  transform: translateX(-100%);
  animation: skeleton-sweep 1.4s infinite;
}
@keyframes skeleton-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } }

/* --------------------------------------------------------- 16. Utilities */
/* Inline contact links sit at body size, leaving an 18-30px tall hit area on a
   phone. Pad them out without disturbing the desktop rhythm. */
@media (max-width: 640px) {
  .topbar__in a,
  .drawer__meta a,
  .footer-cols a:not(.btn),
  .promo__terms a,
  .cta-band p a { display: inline-block; padding-block: .35rem; }
}

.center { text-align: center; }
.stack > * + * { margin-top: 1.05rem; }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
[hidden] { display: none !important; }

/* The EliseAI widget's mobile launcher renders a 100px in-flow spacer inside its
   shadow root while the launcher it contains is position:fixed. The spacer is
   the last thing in the document, so it added 100px of dead scroll past the
   footer on a phone. Zeroing and clipping the host removes it from both flow and
   scrollable overflow; a fixed descendant is not clipped by an ancestor that has
   no transform, so the launcher itself is untouched. */
me-chat { display: block; height: 0; overflow: hidden; }

/* Scoped to .js so the content is never hidden if scripting is unavailable. */
.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity .8s var(--ease), transform .8s var(--ease); }
.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .js .reveal { opacity: 1; transform: none; } }
