/* ============================================================
   Residence detail pages (klccinvestor.com/<slug>.html) - shared styles
   for all property-specific pages, starting with binastra-cochrane.html.
   Linked after styles.css: reuses that file's tokens (cream/ink/gray
   grayscale ramp, radius/duration/easing custom properties) and base
   classes (.container/.section/.section-heading/.btn) rather than redefining
   them. Only the components genuinely new to a property detail page live
   here, written once so all future property pages reuse the same file.
   ============================================================ */

/* -------------------- Hero (Ken Burns) --------------------
   A slow, continuous zoom on the hero photo - simple CSS @keyframes
   rather than JS, since it never needs to start/stop/sync with scroll
   (unlike the index.html hero video, which script.js explicitly pauses
   under reduced-motion - here we just skip the animation instead). */
.residence-hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: clip;
}

.residence-hero__photo {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.residence-hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: residence-ken-burns 24s ease-in-out infinite alternate;
}

@keyframes residence-ken-burns {
  from { transform: scale(1); }
  to { transform: scale(1.1); }
}

.residence-hero__photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(10, 10, 10, 0.65), rgba(10, 10, 10, 0.05) 55%);
}

.residence-hero__content {
  position: relative;
  padding-block: clamp(4rem, 3rem + 4vw, 7rem) clamp(3rem, 2.5rem + 3vw, 5rem);
  color: var(--white);
}

.residence-hero__title {
  font-size: clamp(2.75rem, 2.1rem + 4vw, 5.5rem);
  color: var(--white);
}

.residence-hero__tagline {
  margin-top: 1rem;
  font-size: clamp(1.1rem, 1rem + 0.6vw, 1.4rem);
  color: var(--gray-300);
  max-width: 32ch;
}

.residence-hero__actions {
  margin-top: clamp(2rem, 1.5rem + 2vw, 3rem);
}

@media (prefers-reduced-motion: reduce) {
  .residence-hero__photo img {
    animation: none;
  }
}

/* Top-aligned variant: for a hero photo whose empty/negative space (sky,
   not building) sits in the UPPER portion of the frame - the base
   flex-end alignment assumes the opposite (Binastra Cochrane's facade
   shot), so this is a separate modifier rather than a change to the base
   class, keeping that page's hero untouched. */
.residence-hero--top {
  align-items: flex-start;
}

.residence-hero--top .residence-hero__content {
  padding-block: clamp(7rem, 6rem + 4vw, 10rem) clamp(3rem, 2.5rem + 3vw, 5rem);
}

.residence-hero--top .residence-hero__photo::after {
  background: linear-gradient(180deg, rgba(10, 10, 10, 0.55), rgba(10, 10, 10, 0) 45%);
}

/* -------------------- Address + connectivity badge -------------------- */
.residence-badge-row {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.residence-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-full);
  font-size: 0.85rem;
  color: var(--gray-600);
}

.residence-badge strong {
  color: var(--ink);
  font-weight: 600;
}

/* -------------------- Video embed -------------------- */
.residence-video {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: clip;
  background: var(--black-elevated);
}

.residence-video iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* -------------------- Alternating showcase spread -------------------- */
.residence-showcase__item {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  align-items: center;
}

.residence-showcase__item + .residence-showcase__item {
  margin-top: clamp(3rem, 2.5rem + 3vw, 5rem);
}

.residence-showcase__photo {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  overflow: clip;
}

/* Full-bleed infographics (location/facility breakdown maps) are wide
   source images (2400x1350, 16:9) with load-bearing text near the edges -
   the base 4:3 crop via object-fit:cover was cutting that text off. This
   matches the source aspect ratio exactly so nothing gets cropped. */
.residence-showcase__photo--infographic {
  aspect-ratio: 16 / 9;
}

.residence-showcase__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.residence-showcase__copy h3 {
  font-size: clamp(1.6rem, 1.4rem + 1vw, 2.2rem);
}

.residence-showcase__copy p {
  margin-top: 0.75rem;
  color: var(--gray-500);
  line-height: 1.7;
  max-width: 42ch;
}

@media (min-width: 800px) {
  .residence-showcase__item {
    grid-template-columns: 3fr 2fr;
    gap: 3rem;
  }

  .residence-showcase__item--reverse {
    direction: rtl;
  }

  .residence-showcase__item--reverse > * {
    direction: ltr;
  }
}

/* -------------------- Generic photo grid (facilities / landmarks) --------------------
   Same visual treatment as .agent-card__photo's hover-zoom, generalized
   for any equal-weight image tile on this page. Vision/Interiors uses the
   dedicated .residence-vision spread below instead - a distinct, larger,
   asymmetric treatment for that section. */
.residence-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
}

/* Fewer, bigger tiles than the default 240px minimum - for a small feature
   set (e.g. 4 developer track-record photos) where the default column
   count would otherwise pack them in small and tight. */
.residence-grid--feature {
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}

.residence-tile {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  overflow: clip;
  background: var(--black-elevated);
}

.residence-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out-quart);
}

.residence-tile:hover img {
  transform: scale(1.06);
}

/* Overlay caption, matching the gradient-scrim/white-uppercase convention
   used on every other photo tile on these pages (.residence-vision__caption,
   .residence-facilities__photo figcaption, .connectivity-photo__frame
   figcaption) - this was the one remaining plain-gray-text-below-image
   treatment left on the page. */
.residence-tile figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 1.5rem 1rem 0.85rem;
  background: linear-gradient(0deg, rgba(10, 10, 10, 0.7), rgba(10, 10, 10, 0));
  color: var(--white);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* -------------------- Track-record grid (developer credentials) --------
   Mirrors .residence-vision's hero+filmstrip grammar (same 800px
   breakpoint, same aspect-ratio swap) rather than .residence-grid--
   feature's equal tiles - the newest/largest-GDV project (last in source
   order) is promoted to a tall hero tile instead of the first, since it's
   the one the stat block above already singles out by name. */
.residence-grid--track-record {
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 800px) {
  .residence-grid--track-record {
    grid-template-columns: 1fr 1.5fr;
    grid-template-rows: repeat(3, 1fr);
    grid-template-areas:
      "t1 hero"
      "t2 hero"
      "t3 hero";
    gap: 1.25rem;
  }

  .residence-grid--track-record .residence-tile:nth-of-type(1) { grid-area: t1; }
  .residence-grid--track-record .residence-tile:nth-of-type(2) { grid-area: t2; }
  .residence-grid--track-record .residence-tile:nth-of-type(3) { grid-area: t3; }

  .residence-grid--track-record .residence-tile:nth-of-type(4) {
    grid-area: hero;
    aspect-ratio: auto;
    height: 100%;
  }

  .residence-grid--track-record .residence-tile:not(:nth-of-type(4)) {
    aspect-ratio: 16 / 11;
  }
}

/* Structured caption (year / name / metric, stacked) instead of one
   middot-joined line - restyles how figcaption's own inherited position/
   background/uppercase (set above) get distributed across three lines. */
.residence-tile__caption {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.residence-tile__caption-year {
  color: var(--gray-300);
}

.residence-tile__caption-name {
  font-size: 0.85rem;
}

.residence-tile__caption-meta {
  font-weight: 400;
  color: rgba(255, 255, 255, 0.75);
}

.residence-tile--flagship .residence-tile__caption {
  padding-top: 2.5rem;
}

.residence-tile--flagship .residence-tile__caption-name {
  font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.4rem);
  font-weight: 600;
}

/* -------------------- Vision spread (hero + filmstrip) --------------------
   Distinct from .residence-grid: one dominant portrait hero (facade) plus
   a subordinate strip of three interiors, instead of four equal tiles.
   Mobile: hero on top, strip below. Desktop: strip becomes a stacked
   column beside a full-height hero. Captions are gradient-scrim overlays,
   reusing .residence-hero__photo's overlay technique at tile scale. */
.residence-vision {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "hero hero hero"
    "t1   t2   t3";
  gap: 0.85rem;
}

.residence-vision__hero,
.residence-vision__tile {
  position: relative;
  margin: 0;
  border-radius: var(--radius-md);
  overflow: clip;
  background: var(--black-elevated);
}

.residence-vision__hero { grid-area: hero; aspect-ratio: 4 / 5; }
.residence-vision__tile:nth-of-type(2) { grid-area: t1; }
.residence-vision__tile:nth-of-type(3) { grid-area: t2; }
.residence-vision__tile:nth-of-type(4) { grid-area: t3; }
.residence-vision__tile { aspect-ratio: 1 / 1; }

.residence-vision__hero img,
.residence-vision__tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out-quart);
}

.residence-vision__hero:hover img,
.residence-vision__tile:hover img {
  transform: scale(1.06);
}

.residence-vision__caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 1.5rem 1rem 0.85rem;
  background: linear-gradient(0deg, rgba(10, 10, 10, 0.6), rgba(10, 10, 10, 0));
  color: var(--white);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

@media (min-width: 800px) {
  .residence-vision {
    grid-template-columns: 1.5fr 1fr;
    grid-template-rows: repeat(3, 1fr);
    grid-template-areas:
      "hero t1"
      "hero t2"
      "hero t3";
    gap: 1.25rem;
  }

  .residence-vision__hero { aspect-ratio: auto; height: 100%; }
  .residence-vision__tile { aspect-ratio: 16 / 11; }
}

/* -------------------- Facilities tiered index --------------------
   A third distinct language from .residence-grid (equal tiles, used for
   the landmark thumbnails) and .residence-vision (hero+filmstrip, overlay
   captions): two repeating asymmetric bands keyed to the Podium/Sky
   framing, headed by a typographic index numeral. Captions sit below the
   photo, plain text, not an overlay. */
.residence-facilities {
  display: flex;
  flex-direction: column;
  gap: clamp(2.5rem, 2rem + 2vw, 4rem);
}

.residence-facilities__tier-head {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 1.25rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid var(--gray-300);
}

.residence-facilities__tier-index {
  font-size: clamp(1.75rem, 1.4rem + 1.2vw, 2.5rem);
  font-weight: 600;
  line-height: 1;
  /* var(--gray-300) reads at 1.3:1 here - that token is reserved for text on
     dark/photo surfaces (hero eyebrow, tile captions); this numeral sits on
     the plain cream section background instead, so it needs the on-light
     scale's darkest step, not the on-dark one. */
  color: var(--gray-500);
}

.residence-facilities__tier-label {
  font-size: clamp(1.25rem, 1.05rem + 0.8vw, 1.6rem);
}

.residence-facilities__tier-meta {
  margin-left: auto;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray-500);
}

.residence-facilities__row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.residence-facilities__photo {
  position: relative;
  min-width: 0;
  margin: 0;
  border-radius: var(--radius-md);
  overflow: clip;
  background: var(--black-elevated);
}

.residence-facilities__photo--wide {
  grid-column: 1 / -1;
  aspect-ratio: 16 / 10;
}

.residence-facilities__photo:not(.residence-facilities__photo--wide) {
  aspect-ratio: 4 / 5;
}

.residence-facilities__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out-quart);
}

.residence-facilities__photo:hover img {
  transform: scale(1.06);
}

/* Overlay caption, matching the gradient-scrim/white-uppercase convention
   already used on .residence-vision__caption and .connectivity-photo__frame
   figcaption - consistent across every photo tile on this page instead of
   the plain gray below-image label this used to have. */
.residence-facilities__photo figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 1.5rem 1rem 0.85rem;
  background: linear-gradient(0deg, rgba(10, 10, 10, 0.6), rgba(10, 10, 10, 0));
  color: var(--white);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

@media (min-width: 700px) {
  .residence-facilities__row {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr);
  }

  .residence-facilities__photo--wide {
    grid-column: auto;
    aspect-ratio: 4 / 3;
  }

  .residence-facilities__photo:not(.residence-facilities__photo--wide) {
    aspect-ratio: 3 / 4;
  }
}

/* Sky tier has only 2 photos (both genuinely elevated) rather than 3, so
   it gets its own equal-weight landscape pair instead of forcing a
   wide-lead + portrait composition built for 3 - avoids an empty grid
   cell and a needlessly cropped portrait of what's actually a wide shot. */
.residence-facilities__row.residence-facilities__row--pair {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.residence-facilities__row--pair .residence-facilities__photo {
  aspect-ratio: 4 / 3;
}

/* Podium tier has 4 photos rather than 3 (the gymnasium reads as a
   ground-level/podium space, not an elevated one, so it moved here from
   Sky) - the base row's single-row "1.6fr lead beside 2 tiles" composition
   only fits exactly 3 items, so this modifier makes the lead a full-width
   banner on its own row with the remaining 3 tiles evenly filling the row
   below it, instead of a 4th tile auto-placing alone into a stretched,
   oddly-tall cell with two empty columns beside it. */
@media (min-width: 700px) {
  .residence-facilities__row.residence-facilities__row--lead4 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .residence-facilities__row--lead4 .residence-facilities__photo--wide {
    grid-column: 1 / -1;
    aspect-ratio: 21 / 9;
  }

  .residence-facilities__row--lead4 .residence-facilities__photo:not(.residence-facilities__photo--wide) {
    aspect-ratio: 3 / 4;
  }
}

/* -------------------- 6 Reasons: scroll lock --------------------
   Same tall-trigger + position:sticky-stage pattern as .connectivity-
   trigger/.connectivity-stage: pins the section for a fixed scroll
   distance so it can't be flicked past unseen, and residence.js gates the
   closing thesis card's reveal to late in that range instead of as soon
   as it's in view (which pinning would otherwise make almost immediate).
   Only disabled below 900px, matching the master/detail layout's own
   breakpoint - below that there's no hover panel to protect, just a plain
   accordion list, so pinning would add friction without payoff. */
.highlights-trigger {
  position: relative;
  height: 220vh;
}

.highlights-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(2rem, 1.5rem + 2vw, 3rem);
}

@media (max-width: 899px), (prefers-reduced-motion: reduce) {
  .highlights-trigger {
    height: auto;
  }

  .highlights-stage {
    position: static;
    height: auto;
  }
}

/* -------------------- Investment highlights: master/detail --------------------
   Compact left list of headers ("collapsed on each other") drives a single
   big-type detail card on the right. Hover swaps it on desktop; click/tap
   swaps it AND expands the same row inline as a FAQ-style accordion on
   mobile - one state (aria-pressed + data-open, set together in
   residence.js) drives both, so there's no separate mobile/desktop state
   to keep in sync. */
.residence-highlights {
  display: block;
}

.residence-highlight-list {
  border-top: 1px solid var(--gray-300);
  max-width: 40rem;
}

.residence-highlight-item {
  border-bottom: 1px solid var(--gray-300);
}

.residence-highlight-item__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: 1rem;
  padding-left: 0.75rem;
  margin-left: -0.75rem;
  background: none;
  border: 0;
  border-left: 2px solid transparent;
  font: inherit;
  color: var(--gray-500);
  text-align: left;
  cursor: pointer;
  transition: color var(--duration-base) var(--ease-out-quart), border-color var(--duration-base) var(--ease-out-quart);
}

.residence-highlight-item__icon-wrap {
  position: relative;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--gray-300);
  overflow: clip;
  transition: border-color var(--duration-base) var(--ease-out-quart);
}

.residence-highlight-item__icon-wrap::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ink);
  transform: scale(0);
  transition: transform var(--duration-base) var(--ease-out-quart);
}

.residence-highlight-item__icon {
  position: relative;
  z-index: 1;
  width: 1.1rem;
  height: 1.1rem;
  color: var(--ink);
  transition: color var(--duration-base) var(--ease-out-quart);
}

.residence-highlight-item__title {
  flex: 1;
  font-size: 1.05rem;
}

.residence-highlight-item__caret {
  flex-shrink: 0;
  font-size: 1.1rem;
  color: var(--gray-400);
  transition: transform var(--duration-base) var(--ease-out-quart);
}

.residence-highlight-item[data-open='true'] .residence-highlight-item__caret {
  transform: rotate(45deg);
}

.residence-highlight-item__trigger[aria-pressed='true'] {
  color: var(--ink);
  border-left-color: var(--ink);
}

.residence-highlight-item__trigger[aria-pressed='true'] .residence-highlight-item__icon-wrap {
  border-color: var(--ink);
}

.residence-highlight-item__trigger[aria-pressed='true'] .residence-highlight-item__icon-wrap::before {
  transform: scale(1);
}

.residence-highlight-item__trigger[aria-pressed='true'] .residence-highlight-item__icon {
  color: var(--cream);
}

/* Mobile: inline accordion (same grid-rows trick as .faq-item__answer) */
.residence-highlight-item__answer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 500ms var(--ease-out-quart);
}

.residence-highlight-item[data-open='true'] .residence-highlight-item__answer {
  grid-template-rows: 1fr;
}

.residence-highlight-item__answer-inner {
  overflow: hidden;
  min-height: 0;
}

.residence-highlight-item__answer p {
  padding: 0 0 1.25rem 3.25rem;
  color: var(--gray-500);
  font-size: 0.95rem;
  line-height: 1.6;
}

/* Desktop: side-by-side master/detail; hover drives the swap (residence.js) */
.residence-highlight-detail {
  display: none;
}

@media (min-width: 900px) {
  .residence-highlights {
    display: grid;
    grid-template-columns: 5fr 7fr;
    gap: 3rem;
    align-items: start;
  }

  .residence-highlight-item__answer {
    display: none;
  }

  .residence-highlight-item__caret {
    display: none;
  }

  .residence-highlight-detail {
    display: grid;
    position: sticky;
    top: 8rem;
  }

  .residence-highlight-detail__panel {
    grid-area: 1 / 1;
    align-self: start;
    padding: clamp(2rem, 1.5rem + 2vw, 2.75rem);
    border-radius: var(--radius-md);
    border: 1px solid var(--gray-300);
    background: var(--white);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duration-base) var(--ease-out-quart);
  }

  .residence-highlight-detail__panel.is-active {
    opacity: 1;
    pointer-events: auto;
    animation: residence-highlight-reveal var(--duration-slow) var(--ease-out-quart);
  }

  /* A genuine reveal for the card itself (replacing the removed per-
     character heading bounce) - re-triggers on every hover/click switch
     automatically since it's scoped to .is-active, same as the unit-type
     tab panels' own cross-fade. */
  @keyframes residence-highlight-reveal {
    from {
      opacity: 0;
      transform: translateY(14px) scale(0.98);
    }
    to {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
  }

  .residence-highlight-detail__index {
    display: block;
    margin-bottom: 0.75rem;
    color: var(--gray-400);
    font-size: 0.85rem;
    letter-spacing: 0.05em;
  }

  .residence-highlight-detail__panel h3 {
    margin: 0 0 1rem;
    font-size: 1.75rem;
  }

  .residence-highlight-detail__panel p {
    margin: 0;
    color: var(--gray-600);
    font-size: 1.15rem;
    line-height: 1.7;
  }
}

/* -------------------- Closing thesis: dark reveal card --------------------
   The one deliberate break from this page's all-cream palette: inverted to
   --black-elevated so the bookend statement reads as genuinely distinct.
   Reveal is a clip-path wipe (not the char-by-char heading reveal, not the
   generic [data-reveal-item] fade) - driven by its own IntersectionObserver
   in residence.js, mirroring styles.css's [data-phase-reveal] precedent.
   The clip-path lives on __inner, not on this outer element: an element
   clipped to zero width reports as NOT intersecting to IntersectionObserver
   regardless of its layout position, which would make the reveal that's
   supposed to un-clip it never fire in the first place. Observing this
   unclipped outer box breaks that circular dependency. */
.residence-thesis-card {
  position: relative;
  margin-top: clamp(2.5rem, 2rem + 2vw, 4rem);
  max-width: 52rem;
}

.residence-thesis-card__inner {
  padding: clamp(2rem, 1.5rem + 2vw, 3rem);
  border-radius: var(--radius-md);
  background: var(--black-elevated);
  color: var(--white);
  overflow: clip;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 900ms var(--ease-out-quart);
}

.residence-thesis-card.is-revealed .residence-thesis-card__inner {
  clip-path: inset(0 0 0 0);
}

.residence-thesis-card__mark {
  display: block;
  font-size: 3.5rem;
  line-height: 1;
  margin-bottom: 0.5rem;
  /* This card sits on --black-elevated, not cream - it needs the on-dark
     token (also used by the hero eyebrow/tagline), not the on-light one.
     Was var(--gray-500), which the token darkening above would have dropped
     to ~2.5:1 here; var(--gray-300) is the correct on-dark step and lands
     around 11:1 against this background. */
  color: var(--gray-300);
}

.residence-thesis-card p {
  margin: 0;
  max-width: 56ch;
  color: var(--white);
  font-size: 1.15rem;
  line-height: 1.7;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 600ms var(--ease-out-quart) 450ms, transform 600ms var(--ease-out-quart) 450ms;
}

.residence-thesis-card.is-revealed p {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .residence-thesis-card__inner {
    clip-path: inset(0 0 0 0);
    transition: none;
  }

  .residence-thesis-card p {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* -------------------- Unit-type tab switcher --------------------
   A click-driven exploration hook (not scroll-driven): 4 pill buttons
   toggle which plan panel is visible via a simple opacity cross-fade,
   using this file's own tokens rather than introducing new timing. */
.residence-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: clamp(2rem, 1.5rem + 2vw, 3rem);
}

/* Pill variant of the site's .btn sweep-fill convention (see styles.css
   "Buttons" block): same ghost-outline/fill-sweep/text-swap mechanic as
   every other button on this page, just radius-full and smaller. The
   active tab stays permanently in the "filled" end-state instead of only
   on hover, so the selected type reads as selected at rest. */
.residence-tab {
  padding: 0.6rem 1.4rem;
  border-radius: var(--radius-full);
  border-color: var(--gray-300);
  font-size: 0.9rem;
  color: var(--gray-600);
}

.residence-tab[aria-pressed='true'] {
  color: var(--btn-contrast);
  border-color: var(--ink);
}

.residence-tab[aria-pressed='true'] .btn__fill {
  transform: scaleX(1);
}

.residence-tab-panels {
  position: relative;
}

.residence-tab-panel {
  display: none;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  align-items: center;
}

.residence-tab-panel.is-active {
  display: grid;
  animation: residence-tab-fade var(--duration-base) var(--ease-out-quart);
}

@keyframes residence-tab-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.residence-tab-panel__photo {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  overflow: clip;
}

/* WOLO Mont Kiara's Penthouse Collection plan is portrait (an L-shaped
   layout extending down to a separate master suite), unlike every other
   tab's landscape-ish render this shared 4:3 box assumes - cover-cropping
   it into a landscape box cut the master bedroom and bathroom off
   entirely. Opt-in modifier so Binastra Cochrane's own tab photos (which
   the plain 4:3 default still fits) are untouched. */
.residence-tab-panel__photo--tall {
  aspect-ratio: 1666 / 1888;
}

/* Golden Crown Residence's 5 floor-plan crops (A1/A2/B1B2/B3/C) came from a single shared sales
   poster and, unlike every prior page's renders, have wildly different aspect ratios from EACH
   OTHER (0.73 to 2.3) since each unit's own drawn shape differs (a 1-bedroom vs. a dual-key plan
   with a dressing room). No single aspect-ratio modifier makes `cover` crop all 5 correctly -
   fixing one type's crop would only break another's differently. Unlike lifestyle photography,
   cropping a floor plan is actively misleading (it can cut off an entire room), so `contain` is
   the correct fix here, not a better-guessed crop box. The background color matches the crops'
   own uniform dark-brown fill (sampled directly from the source images), so the box's true
   aspect-ratio becomes invisible - there's no visible letterbox seam regardless of how much
   padding `contain` adds around a given plan. */
.residence-tab-panel__photo--plan {
  background: #5a4d47;
  /* Square rather than the default 4:3 landscape - a meaningfully taller box at the same column
     width, since 4 of these 5 crops (A1/A2/B1B2/B3) now measure close to 1:1 after re-cropping;
     Type C (a wide dual-key plan, ~2.7:1) still shows in full via `contain`, just with visible
     letterbox padding above/below - unavoidable for a plan this much wider than the other 4. */
  aspect-ratio: 1 / 1;
}

.residence-tab-panel__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Must come AFTER the base `.residence-tab-panel__photo img` rule above - both have identical
   specificity (one class + one element), so source order decides the winner. This modifier was
   first added BEFORE the base rule by mistake, which meant `cover` always won the cascade and
   silently cropped every plan regardless of this rule's presence - the box never actually grew
   or shrank, `object-fit: contain` was simply never applied. */
.residence-tab-panel__photo--plan img {
  object-fit: contain;
}

.residence-tab-panel__specs {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.residence-tab-panel__specs li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--gray-300);
  color: var(--gray-600);
}

.residence-tab-panel__specs strong {
  color: var(--ink);
}

@media (min-width: 800px) {
  .residence-tab-panel {
    grid-template-columns: 3fr 2fr;
    gap: 3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .residence-tab-panel.is-active {
    animation: none;
  }
}

/* -------------------- Connectivity scroll-scrub --------------------
   Same structural pattern as .properties-trigger/.service-trigger in
   styles.css: a tall trigger wrapper holds a position:sticky stage:
   residence.js scrubs an SVG line's stroke-dashoffset and each stop
   label's opacity as the wrapper scrolls through the viewport. Falls
   back to a static stacked list below 768px and under reduced-motion,
   matching the Properties section's own fallback convention exactly. */
.connectivity-trigger {
  position: relative;
  height: 400vh;
}

.connectivity-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(4rem, 3rem + 4vw, 6rem);
}

.connectivity-line {
  /* Extra top margin, on top of the flex gap above: the stop labels sit
     ABOVE the svg line itself (translateY(-140%) - see .connectivity-stop
     below), so the line needs real clearance above it or its labels
     collide with the heading. */
  margin-top: 2rem;
  position: relative;
  width: 100%;
  max-width: 64rem;
  margin-inline: auto;
}

.connectivity-line svg {
  width: 100%;
  height: auto;
  display: block;
}

/* Rail-bed texture, not a flat progress bar: a resting track (ties +
   parallel rails, reads as "unbuilt") gets overlaid by a thick solid ink
   pill as the user scrolls (reads as "traveled"). Same single-path
   stroke-dashoffset mechanism as before, just a different metaphor -
   grayscale throughout, no color introduced, matching this page's one
   other deliberate departure (the closing thesis card) being a tonal
   inversion rather than a chromatic one. */
.connectivity-line__bed {
  fill: url(#rail-ties);
}

.connectivity-line__tie {
  stroke: var(--gray-300);
  stroke-width: 2;
}

.connectivity-line__rail {
  stroke: var(--gray-300);
  stroke-width: 2;
  stroke-linecap: round;
}

.connectivity-line__progress {
  fill: none;
  stroke: var(--ink);
  stroke-width: 20;
  stroke-linecap: round;
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
  filter: drop-shadow(0 2px 6px rgba(23, 23, 23, 0.3));
}

/* Leading "train" marker: travels with the progress tip, a live focal
   point rather than a static fill. Position set by residence.js since the
   path is a straight horizontal line (pure arithmetic, no getPointAtLength
   needed). */
.connectivity-line__marker {
  fill: var(--cream);
  stroke: var(--ink);
  stroke-width: 3;
  opacity: 0;
  filter: drop-shadow(0 1px 4px rgba(23, 23, 23, 0.35));
  transition: opacity var(--duration-fast) var(--ease-out-quart);
}

.connectivity-line__marker.is-active {
  opacity: 1;
}

/* Station roundels: ring + core, donut-inverting when lit - like real
   interchange markers rather than a plain dot. Sits in the same SVG
   coordinate space as the line (pixel-aligned, no separate measurement) -
   lit in lockstep with each .connectivity-stop label by residence.js,
   same index, same threshold. */
.connectivity-tick__ring {
  fill: var(--cream);
  stroke: var(--gray-300);
  stroke-width: 3;
  transition: fill var(--duration-base) var(--ease-out-quart), stroke var(--duration-base) var(--ease-out-quart), r var(--duration-base) var(--ease-out-quart);
}

.connectivity-tick__core {
  fill: var(--gray-300);
  transition: fill var(--duration-base) var(--ease-out-quart);
}

.connectivity-tick.is-lit .connectivity-tick__ring {
  /* Grayscale read too close to the ink rail to feel distinct at a
     glance. This is the one deliberate accent color on the whole site -
     a muted brass, used only here, functionally (marking a lit station)
     rather than decoratively - a restrained callback to premium
     real-estate cues without reopening the page's broader no-color rule. */
  --station-accent: #9c7a3c;
  fill: var(--station-accent);
  stroke: var(--station-accent);
  r: 13;
}

.connectivity-tick.is-lit .connectivity-tick__core {
  fill: var(--cream);
}

/* Interchange marker: a rotated square around the roundel, the standard
   transit-map convention for "change lines here" - distinct from every
   other (plain circle) station on the line. Resting state reads as an
   outline; lights up in the same brass accent as the roundel itself once
   the stop is reached. */
.connectivity-tick__interchange-mark {
  fill: none;
  stroke: var(--gray-300);
  stroke-width: 2;
  stroke-dasharray: 4 3;
  transition: stroke var(--duration-base) var(--ease-out-quart);
}

.connectivity-tick--interchange.is-lit .connectivity-tick__interchange-mark {
  stroke: #9c7a3c;
}

.connectivity-stop {
  position: absolute;
  top: 0;
  transform: translate(-50%, -140%);
  text-align: center;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out-quart);
  width: max-content;
  max-width: 9rem;
}

.connectivity-stop.is-lit {
  opacity: 1;
}

.connectivity-stop__value {
  display: block;
  font-weight: 600;
  color: var(--ink);
}

.connectivity-stop__label {
  display: block;
  font-size: 0.8rem;
  color: var(--gray-500);
}

.connectivity-stop--origin .connectivity-stop__value {
  color: var(--ink);
}

.connectivity-stop__interchange {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9c7a3c;
}

/* Photo reveal panel: crossfades to whichever photo-bearing stop is
   currently lit (residence.js toggles .is-active on the matching frame).
   Sits below the line, inside the same sticky stage. */
.connectivity-photo {
  position: relative;
  margin-top: clamp(2.5rem, 2rem + 2vw, 4rem);
  width: min(42rem, 92vw);
  height: clamp(16rem, 34vh, 26rem);
  margin-inline: auto;
}

.connectivity-photo__frame {
  margin: 0;
  position: absolute;
  inset: 0;
  border-radius: var(--radius-md);
  overflow: clip;
  background: var(--black-elevated);
  opacity: 0;
  transform: scale(1.06);
  transition: opacity var(--duration-slow) var(--ease-out-quart),
              transform var(--duration-slow) var(--ease-out-quart);
}

.connectivity-photo__frame.is-active {
  opacity: 1;
  transform: scale(1);
}

.connectivity-photo__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.connectivity-photo__frame figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 1rem 1.25rem 0.75rem;
  background: linear-gradient(0deg, rgba(10, 10, 10, 0.65), rgba(10, 10, 10, 0));
  color: var(--white);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Static fallback list - hidden by default, shown instead of the scrub
   under reduced-motion or below the desktop breakpoint. Photo-bearing
   stops carry an inline thumbnail beside their text info. */
.connectivity-static {
  display: none;
  flex-direction: column;
  gap: 1rem;
  max-width: 32rem;
  margin-inline: auto;
}

.connectivity-static__row {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--gray-300);
}

.connectivity-static__row-info {
  flex: 1;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
}

.connectivity-static__row-info strong {
  color: var(--ink);
}

.connectivity-static__row-info span {
  color: var(--gray-500);
}

/* Interchange row: stacks the line-change badge below the row info
   instead of squeezing it in as a third flex item beside the value/name
   pair, matching the desktop scrub's own stacked-label treatment. */
.connectivity-static__row--interchange {
  flex-direction: column;
  align-items: stretch;
  gap: 0.35rem;
}

.connectivity-static__interchange {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9c7a3c;
}

.connectivity-static__thumb {
  flex-shrink: 0;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--black-elevated);
}

.connectivity-map {
  padding-block: clamp(3rem, 2.5rem + 3vw, 5rem);
}

@media (max-width: 767px), (prefers-reduced-motion: reduce) {
  .connectivity-trigger {
    height: auto;
  }

  .connectivity-stage {
    position: static;
    height: auto;
    padding-block: clamp(2rem, 1.5rem + 2vw, 3rem);
  }

  .connectivity-line {
    display: none;
  }

  .connectivity-static {
    display: flex;
  }
}

/* -------------------- Developer credentials -------------------- */
.residence-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2rem;
  padding-top: clamp(1.5rem, 1rem + 1.5vw, 2.5rem);
  border-top: 1px solid var(--gray-300);
}

.residence-stat-value {
  display: flex;
  align-items: baseline;
  gap: 0.15em;
  font-size: clamp(2.25rem, 1.8rem + 2vw, 3.5rem);
  font-weight: 600;
}

.residence-stat-value__prefix {
  font-size: 0.5em;
  font-weight: 500;
  color: var(--gray-500);
}

.residence-stat-label {
  margin-top: 0.5rem;
  color: var(--gray-500);
  font-size: 0.9rem;
}

.residence-partners {
  margin-top: clamp(2rem, 1.5rem + 2vw, 3rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1.5rem, 1rem + 2vw, 3rem);
}

.residence-partners img {
  height: 2.25rem;
  width: auto;
  max-width: 9rem;
  object-fit: contain;
  filter: grayscale(1);
  opacity: 0.7;
}

/* -------------------- Prominent partner lockup --------------------
   KL360-specific upgrade from the plain floating-logo row above: that
   base .residence-partners/img rule stays untouched (Binastra Cochrane's
   partner row keeps rendering exactly as before) - this only applies
   where the --prominent modifier is explicitly added. */
.residence-partners__eyebrow {
  margin-bottom: 1rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-500);
}

.residence-partners--prominent {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}

.residence-partner-card {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 6.5rem;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-md);
  background: var(--white);
  transition: border-color var(--duration-base) var(--ease-out-quart);
}

.residence-partner-card:hover {
  border-color: var(--gray-400);
}

/* Compound selector wins over the base .residence-partners img rule
   regardless of source order. */
.residence-partners.residence-partners--prominent img {
  height: 3.25rem;
  max-width: 11rem;
  filter: grayscale(1);
  opacity: 0.75;
  transition: filter var(--duration-slow) var(--ease-out-quart), opacity var(--duration-slow) var(--ease-out-quart);
}

.residence-partner-card:hover img {
  filter: grayscale(0);
  opacity: 1;
}

@media (min-width: 700px) {
  .residence-partners--prominent {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 700px) {
  .residence-stats {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* A property page whose sourced developer facts only support 3 stats
   (e.g. no confirmed founding year) overrides the 4-column desktop
   default - otherwise a 3-item grid leaves an empty 4th track and skews
   left, the same category of "item count doesn't match the template's
   assumption" problem the facilities grid modifiers solve for photos. */
@media (min-width: 700px) {
  .residence-stats.residence-stats--3 {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* -------------------- Unit-type filter grid --------------------
   Filter-first alternative to the tab switcher above, for property pages
   with too many layout types for one-button-per-type tabs to scale (e.g.
   20 types). Reuses .residence-tabs/.residence-tab for the pill filter
   rows and .residence-grid/.residence-tab-panel__specs for the card grid
   and spec list verbatim - only the card shell itself is new. */
.residence-unit-filters {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* Caps how wide a card can grow when a filter combination leaves only 1-2
   matches - .residence-grid's inherited minmax(240px, 1fr) would otherwise
   let a lone remaining card stretch to the full row width, blowing the
   floor-plan image up far past its source resolution. */
.residence-unit-grid {
  grid-template-columns: repeat(auto-fit, minmax(240px, 340px));
  /* Filtering hides cards and shrinks this grid's height while it sits
     above the user's current scroll position - browser scroll anchoring
     then "helpfully" scrolls the page up to compensate, jumping the view
     backward into the previous section. Opt this element out so clicking
     a filter pill never moves the viewport. */
  overflow-anchor: none;
}

.residence-unit-card {
  margin: 0;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-md);
  overflow: clip;
  background: var(--white);
}

.residence-unit-card__photo {
  aspect-ratio: 4 / 3;
  overflow: clip;
  background: var(--black-elevated);
}

.residence-unit-card__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out-quart);
}

.residence-unit-card:hover .residence-unit-card__photo img {
  transform: scale(1.06);
}

.residence-unit-card__body {
  padding: 1.25rem 1.25rem 1.5rem;
}

.residence-unit-card__body h3 {
  font-size: 1.1rem;
}

.residence-unit-card__body .residence-tab-panel__specs {
  margin-top: 1rem;
}

.residence-unit-grid__empty {
  grid-column: 1 / -1;
  padding: 3rem 1rem;
  text-align: center;
  color: var(--gray-500);
}

/* Pavilion Square: its floor-plan renders were cropped to an exact 1:1
   square (unlike KL360's ~16:9 renders sharing the default 4:3 photo box
   above), so the default ratio crops real plan content off the top/bottom -
   opt-in override matching the source exactly, zero cropping either way. */
.residence-unit-card__photo--square {
  aspect-ratio: 1 / 1;
}

/* Armani Hallson: its 8 floor-plan renders range from a 0.35 to a 0.8
   width/height ratio (a 406 sq ft studio vs. a 1,182 sq ft twin-suite
   unit aren't remotely the same shape), so no single fixed aspect-ratio
   avoids cropping real plan content the way it did for KL360/Pavilion
   Square's more uniform renders. Same "never crop a floor plan" fix as
   the lightbox/tab-panel plan variants: object-fit:contain on a neutral
   ground. Must come after the base `.residence-unit-card__photo img`
   rule above - both have identical specificity (one class + one
   element), so source order decides the winner. */
.residence-unit-card__photo--plan {
  aspect-ratio: 3 / 4;
  background: var(--cream-soft);
}

.residence-unit-card__photo--plan img {
  object-fit: contain;
}

/* Pavilion Square: a few Car Park values ("1 Bay or 1 Tandem Bay") wrap to
   two lines while most stay on one, making cards in different rows land at
   different heights - reserve two lines' worth on every spec row so all
   cards in the grid match regardless of which row they're in. */
.residence-unit-grid--tight-specs .residence-tab-panel__specs li {
  min-height: 2.7em;
}

/* Pavilion Square: a couple of Car Park values run long enough ("1 Tandem
   Bay or 2 Single Bay") to squeeze the label into wrapping too - pin the
   label to one line and let only the value wrap, so the label never moves. */
.residence-unit-grid--tight-specs .residence-tab-panel__specs li span {
  flex-shrink: 0;
}

.residence-unit-grid--tight-specs .residence-tab-panel__specs li strong {
  text-align: right;
}

/* Clickable unit cards: cursor + a subtle lift on hover signal "click to
   enlarge" without adding a visible button chrome to every one of the 20
   cards - tabindex/role are set by residence.js so the markup above stays
   plain <article> elements. */
.residence-unit-card {
  cursor: pointer;
  transition: box-shadow var(--duration-base) var(--ease-out-quart);
}

.residence-unit-card:hover,
.residence-unit-card:focus-visible {
  box-shadow: 0 12px 30px rgba(23, 23, 23, 0.12);
}

/* -------------------- Unit layout lightbox --------------------
   Shared overlay/panel chrome for two distinct triggers: clicking any unit
   card (populated dynamically from that card's own photo/title/specs) and
   the Penthouse & Corner Suites button (static private-inquiry content).
   Same open/close mechanics for both, driven by residence.js. */
.residence-lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-out-quart);
}

.residence-lightbox.is-open {
  opacity: 1;
  pointer-events: auto;
}

.residence-lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 10, 10, 0.72);
}

.residence-lightbox__panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 44rem;
  max-height: 88vh;
  overflow-y: auto;
  background: var(--white);
  border-radius: var(--radius-md);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  transform: scale(0.96) translateY(12px);
  opacity: 0;
  transition: transform var(--duration-base) var(--ease-out-quart), opacity var(--duration-base) var(--ease-out-quart);
}

.residence-lightbox.is-open .residence-lightbox__panel {
  transform: scale(1) translateY(0);
  opacity: 1;
}

.residence-lightbox__close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 2;
  width: 2.25rem;
  height: 2.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  border: 1px solid var(--gray-300);
  background: var(--white);
  color: var(--ink);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--duration-base) var(--ease-out-quart);
}

.residence-lightbox__close:hover {
  border-color: var(--ink);
}

/* Floor plans are technical drawings, not photography - object-fit:contain
   on a light neutral ground (not the usual --black-elevated photo tile)
   so the full plan is always visible, never cropped, regardless of a given
   render's exact aspect ratio. */
.residence-lightbox__photo--plan {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: clip;
  background: var(--cream-soft);
}

.residence-lightbox__photo--plan img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.residence-lightbox__body {
  margin-top: 1.5rem;
}

.residence-lightbox__body h3 {
  font-size: 1.5rem;
}

.residence-lightbox__body .residence-tab-panel__specs {
  margin-top: 1.25rem;
}

/* Floor-plan renders carry small baked-in room labels (raster text inside
   the image itself, not styleable) - the only lever for legibility is
   displaying the image as close to its native resolution (1400px wide
   source) as the viewport allows, so this variant gets a much wider cap
   than the default panel. */
.residence-lightbox__panel--plan {
  max-width: min(95vw, 78rem);
}

/* Penthouse variant: no photo, just a short message + CTA - a simpler,
   narrower panel than the layout lightbox. */
.residence-lightbox__panel--simple {
  max-width: 32rem;
  text-align: center;
}

/* Full-image variant (facilities/location maps): minimal chrome so the
   image itself gets maximum room - no caption body, no fixed aspect-ratio
   on the image (each source graphic keeps its own natural proportions). */
.residence-lightbox__panel--image {
  max-width: min(95vw, 72rem);
  padding: 0.75rem;
}

.residence-lightbox__panel--image img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 86vh;
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: var(--cream-soft);
}

/* Hover/focus affordance for the two clickable infographic images
   (facilities breakdown, location map) - a plain gray zoom-in glyph over a
   slight darkening, not a new color, so it reads as "click to enlarge"
   without competing with the page's own accent usage. */
.residence-showcase__photo--infographic {
  position: relative;
  cursor: pointer;
}

.residence-showcase__photo--infographic::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(10, 10, 10, 0);
  transition: background var(--duration-base) var(--ease-out-quart);
}

.residence-showcase__photo--infographic:hover::after,
.residence-showcase__photo--infographic:focus-visible::after {
  background: rgba(10, 10, 10, 0.15);
}

.residence-lightbox__panel--simple h3 {
  font-size: 1.5rem;
  margin: 0 0 1rem;
}

.residence-lightbox__panel--simple p {
  margin: 0 0 1.75rem;
  color: var(--gray-600);
  line-height: 1.7;
}

.residence-lightbox__panel--simple .btn {
  display: inline-flex;
}

/* Body scroll lock while any lightbox is open (residence.js toggles this
   on <html>). */
html.has-lightbox-open {
  overflow: hidden;
}

/* -------------------- Final CTA -------------------- */
.residence-cta {
  text-align: center;
}

.residence-cta .section-heading {
  margin-inline: auto;
  text-align: center;
}
