/* ==========================================================================
   H20 Property Services — page composition
   --------------------------------------------------------------------------
   Where components.css defines the vocabulary, this file defines the pages:
   the hero, the apartment card, browse and filters, the detail page, the
   tenant/owner/about/contact bands, and the smaller shared page furniture.

   Section modifiers (`.hero--*`, `.band--*`) are additive. Nothing here reaches
   into a component's internals; pages compose via the classes in components.css.
   ========================================================================== */

/* ============================== PAGE FURNITURE =========================== */

.page-head {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 62ch;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.page-head h1 {
  font-size: var(--step-5);
}

/* An interior-page header band: shallower than a hero, with the breadcrumb. */
.page-band {
  position: relative;
  padding-block: clamp(2rem, 4vw, 3.25rem) clamp(1.5rem, 3vw, 2.5rem);
  background: var(--bg-sunk);
  border-bottom: 1px solid var(--line);
}

.page-band--deep {
  background: var(--grad-deep);
  border-bottom-color: transparent;
}

.page-band .breadcrumbs {
  margin-bottom: var(--sp-5);
}

.page-band--deep .breadcrumbs,
.page-band--deep .breadcrumbs a {
  color: var(--brand);
}

.page-band--deep h1 {
  color: var(--ink);
}

.page-band--deep .lede {
  color: var(--ink-2);
}

/* ============================== HERO ==================================== */

/* Paper, not a dark field. A muted headline on white is the calmest thing a
   landing page can do, and it is what everyone already knows how to read. */
.hero {
  padding-block: clamp(3rem, 6vw, 5.5rem) clamp(2.5rem, 5vw, 4.5rem);
  border-bottom: 1px solid var(--line);
}

.hero__grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: start;
}

@media (min-width: 1000px) {
  .hero__grid {
    grid-template-columns: 1.05fr 0.95fr;
  }
}

.hero__copy {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  max-width: 36rem;
}

.hero h1 {
  font-size: var(--step-6);
  font-weight: var(--weight-light);
  color: var(--ink);
}

/* One phrase carries the accent colour and nothing else — no gradient
   underline, no italic, no highlight block. */
.hero h1 em {
  font-style: normal;
  color: var(--brand);
}

@media (max-width: 560px) {
  .hero h1 {
    font-size: clamp(2rem, 8.4vw, 2.9rem);
  }
}

.hero__lede {
  font-size: var(--step-1);
  line-height: var(--leading-relaxed);
  color: var(--ink-2);
  max-width: 42ch;
}

/* The two hero actions stack and share one width at every size. Side by side
   they do not fit the copy column, and a wrapped row leaves the second button
   floating at its own width, which reads as a mistake rather than a choice. */
.hero__actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-3);
  /* Sized so the large CTA's own padding still leaves room for
     "BROWSE APARTMENTS" on one line. At 21rem the content box was ~268px
     against a ~273px label, and the button silently wrapped into two lines
     on desktop while fitting on a 390px phone. */
  max-width: 22rem;
}

/* Balanced wrapping is right for prose-length buttons elsewhere; here it
   would break a two-word call to action across two lines. */
.hero__actions .btn {
  white-space: nowrap;
}

/* The trust strip under the hero actions. Small facts, plainly set. */
.hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  font-size: var(--step--1);
  color: var(--ink-2);
}

.hero__facts li {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.hero__facts svg {
  width: 1.1em;
  height: 1.1em;
  flex: none;
  color: var(--brand);
}

/* ---------------------------------------------------- hero search widget
   The primary conversion surface: pick a size and a price, land on a filtered
   browse page. It is a real GET form, so it works with JavaScript off. */

.search-panel {
  position: relative;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  color: var(--ink);
}

.search-panel__title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: var(--weight-semibold);
  color: var(--ink);
}

.search-panel__title svg {
  width: 1.15em;
  height: 1.15em;
  color: var(--brand);
}

.search-panel__grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: 1fr;
}

@media (min-width: 560px) {
  .search-panel__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-3);
  }
}

.search-panel .field__label {
  font-size: var(--step--2);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-3);
}

.search-panel .btn {
  width: 100%;
  margin-top: var(--sp-4);
}

/* The floating stat card over the hero media. */
.hero__media {
  position: relative;
}

.hero__figure {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  background: var(--surface-2);
}

@media (min-width: 1000px) {
  .hero__figure {
    aspect-ratio: 5 / 6;
  }
}

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

/* With no photo yet the figure becomes a drawn elevation, not a grey box. */
.hero__figure--drawn {
  display: grid;
  place-items: center;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink-4);
}

.hero__figure--drawn svg {
  width: 58%;
  max-width: 20rem;
}

.hero__stat {
  position: absolute;
  left: clamp(-0.5rem, -1vw, 0rem);
  bottom: clamp(1rem, 3vw, 2rem);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-lg);
  color: var(--ink);
}

.hero__stat b {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: var(--weight-semibold);
  line-height: 1;
}

.hero__stat span {
  font-size: var(--step--2);
  color: var(--ink-3);
}

/* ============================== BANDS =================================== */

/* A band is a full-width section with its own surface. */
.band {
  position: relative;
  padding-block: var(--section-y);
}

.band--sunk {
  background: var(--bg-sunk);
}

/* Exactly one dark band is allowed per page, and it is flat. */
.band--deep {
  background: var(--ink-950);
  color: var(--ink-inverse);
}

.band--surface {
  background: var(--surface);
  border-block: 1px solid var(--line);
}

.band__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.band__head > div {
  max-width: 58ch;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.band__head h2 {
  font-size: var(--step-4);
}

.band--deep .band__head h2 {
  color: var(--ink);
}

.band--deep .band__head p {
  color: var(--ink-2);
}

/* ======================= WHY / FEATURE CARDS ============================= */

.feature {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: clamp(1.4rem, 3vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  transition: var(--transition-lift), border-color var(--dur-2) var(--ease);
}

.feature:hover {
  transform: translateY(-3px);
  border-color: var(--brand-line);
  box-shadow: var(--sh-md);
}

.feature__icon {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  color: var(--brand);
  background: var(--brand-soft);
  border-radius: var(--r-md);
}

.feature__icon svg {
  width: 1.5rem;
  height: 1.5rem;
}

.feature h3 {
  font-family: var(--font-display);
  font-size: var(--step-2);
}

.feature p {
  color: var(--ink-2);
  line-height: var(--leading-relaxed);
}

.feature__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: auto;
  padding-top: var(--sp-2);
}

.band--deep .feature {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.12);
}

.band--deep .feature:hover {
  border-color: var(--brand-line);
  background: rgba(255, 255, 255, 0.09);
}

/* ============================ APARTMENT CARD ============================ */

.lcard {
  display: flex;
  flex-direction: column;
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--sh-xs);
  transition: border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}

/* The card answers the pointer. The first pass removed both the lift and the
   image move, which left a grid that felt inert — interacting with it gave
   back nothing at all. */
.lcard:hover {
  transform: translateY(-2px);
  border-color: var(--line-3);
  box-shadow: var(--sh-md);
}

.lcard[hidden] {
  display: none;
}

.lcard__media {
  position: relative;
  display: block;
  background: var(--surface-2);
  text-decoration: none;
}

.lcard__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-3) var(--ease);
}

.lcard:hover .lcard__img {
  transform: scale(1.03);
}

/* The no-photo placeholder: a plain surface with a drawn plan on it. No
   blueprint hatching and no diagonal pattern — an honest empty box that says
   why it is empty.
   The bottom padding reserves the corner the price plate occupies, so the
   drawing and its label never collide with it. */
.lcard__ph {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) calc(var(--sp-5) + 2.6rem);
  /* ink-4 on surface-2 measured ~1.9:1 — the plan drawing was effectively
     invisible, which is why full grids of photo-less cards read as blank
     paper. The ink is the drawing; the surface only has to separate the media
     block from the card body, and paper-on-mist-50 did not do that. */
  color: var(--ink-3);
  background: var(--surface-3);
}

.lcard__ph svg {
  width: min(62%, 11rem);
}

.lcard__ph-label {
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-3);
}

.lcard__flags {
  position: absolute;
  top: var(--sp-3);
  left: var(--sp-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

/* The price rides on the photo, because price is the first thing scanned. It
   is a flat dark plate with a hard edge — legible over any photograph, and it
   does not blur the picture it sits on. */
.lcard__price {
  position: absolute;
  left: var(--sp-3);
  bottom: var(--sp-3);
  display: inline-flex;
  align-items: baseline;
  gap: 0.1em;
  padding: 0.45em 0.75em;
  color: #fff;
  background: rgba(14, 18, 19, 0.78);
  border-radius: var(--r-sm);
  font-size: var(--step-1);
  line-height: 1;
}

.lcard__price b {
  font-family: var(--font-display);
  font-weight: var(--weight-medium);
}

.lcard__price i {
  font-style: normal;
  font-size: 0.62em;
  opacity: 0.8;
}

/* When there is no photo the price plate would sit on the empty plan; give it
   a paper surface instead so it reads as part of the card. */
.lcard__media:has(.lcard__ph) .lcard__price {
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-2);
}

.lcard__body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  /* More air than the first pass. The cards read as one crammed block because
     every row sat on the same tight rhythm with no grouping. */
  padding: clamp(1.15rem, 2.5vw, 1.5rem);
  flex: 1;
}

/* Where it is first, as a quiet label, so the card has an order the eye can
   follow: where, what, how big, how to get there, what to do. */
.lcard__where {
  display: flex;
  align-items: center;
  gap: 0.4em;
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}

.lcard__where svg {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  color: var(--brand);
}

.lcard__title {
  font-size: var(--step-1);
  line-height: var(--leading-snug);
  margin-top: calc(var(--sp-1) * -1);
}

.lcard__title a {
  color: var(--ink);
  text-decoration: none;
}

/* The whole card is the target. The title's link is stretched over it, so a
   tap anywhere opens the apartment instead of only the words — which is also
   why the media link above is taken out of the tab order. */
.lcard__title a::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}

.lcard__title a:hover {
  color: var(--brand);
}

/* Facts carry an icon each. They used to be one grey run separated by dots,
   which gave the eye nothing to lock onto when comparing cards. */
.lcard__facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3) var(--sp-5);
  margin-top: var(--sp-1);
  font-size: var(--step--1);
  color: var(--ink-2);
}

.lcard__facts li {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
}

.lcard__facts svg {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  color: var(--ink-3);
}

.lcard__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.lcard__transit {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  font-size: var(--step--2);
  color: var(--ink-3);
}

.stamps {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.stamp {
  display: inline-grid;
  place-items: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding-inline: 0.3rem;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: var(--weight-bold);
  line-height: 1;
  color: #fff;
  background: var(--stamp, var(--brand));
  border-radius: 50%;
  box-shadow: var(--sh-xs);
}

.stamp--bus {
  min-width: 2.1rem;
  border-radius: var(--r-pill);
}

.lcard__near {
  font-weight: var(--weight-medium);
}

.lcard__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  /* Pinned to the bottom of the body. Cards in a row stretch to the tallest
     one, and a fixed gap left the shorter card's date and link floating well
     above its neighbours' — the ragged row the user read as "bunched up". */
  margin-top: auto;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

.lcard__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  font-size: var(--step--1);
  font-weight: var(--weight-semibold);
  color: var(--brand);
  /* Painted above the stretched link so it stays visible, but transparent to
     the pointer so the click still lands on the link underneath. */
  position: relative;
  z-index: 2;
  pointer-events: none;
}

.lcard__cta svg {
  width: 1em;
  height: 1em;
  transition: transform var(--dur-2) var(--ease);
}

.lcard:hover .lcard__cta svg {
  transform: translateX(3px);
}

/* The mini card on a detail page. */
.mcard {
  display: flex;
  gap: var(--sp-4);
  align-items: center;
  padding: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  text-decoration: none;
  transition: var(--transition-colors), var(--transition-lift);
}

.mcard:hover {
  border-color: var(--brand-line);
  box-shadow: var(--sh-sm);
  transform: translateY(-2px);
}

.mcard__media {
  flex: none;
  width: 84px;
  height: 84px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
}

.mcard__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.mcard__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.mcard__price {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: var(--weight-semibold);
  color: var(--brand);
  line-height: 1.1;
}

.mcard__price i {
  font-style: normal;
  font-size: 0.62em;
  color: var(--ink-3);
}

.mcard__title {
  font-weight: var(--weight-semibold);
  color: var(--ink);
  line-height: var(--leading-snug);
}

.mcard__where {
  font-size: var(--step--2);
  color: var(--ink-3);
}

/* ======================= RAIL (horizontal scroll) ======================= */

.rail {
  position: relative;
}

.rail__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(280px, 1fr);
  gap: clamp(1rem, 2.5vw, 1.5rem);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--sp-4);
  scrollbar-width: thin;
}

.rail__track > * {
  scroll-snap-align: start;
}

@media (min-width: 900px) {
  .rail__track {
    grid-auto-columns: minmax(0, 1fr);
    grid-template-columns: repeat(3, minmax(0, 1fr));
    overflow: visible;
    padding-bottom: 0;
  }
}

/* ============================== FILTERS ================================= */

.filters {
  /* Deliberately not sticky. The panel is ~1850px tall — taller than any
     laptop or phone viewport — so pinning it can only ever show its top
     slice: the last groups, "Clear all" and "Show results" would sit below
     the fold while the pinned panel also covered the results scrolling past
     it on a phone. Scrolling it with the page keeps every control reachable
     and nothing overlapping, which is what a renter of any age expects. */
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
}

.browse-layout {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
}

@media (min-width: 980px) {
  .browse-layout {
    /* 280px was wide enough for the inputs but not for two chips side by side,
       so the filter column could never pair them up. The results grid still
       holds two 390px cards at 1440. */
    grid-template-columns: 320px minmax(0, 1fr);
  }
}

.filters__group {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* The filter column is the one place with dozens of small repeated controls
   (57 chips), and at 35px they sat under the 44px target every other control
   in the site uses. On a phone that is the difference between picking a rent
   bracket and picking its neighbour, so they grow to the standard control
   height. The desktop column stays dense: a mouse is not a thumb. */
@media (max-width: 980px) {
  .filters .chip,
  .filters .btn {
    min-height: var(--control-sm);
  }
}

/* Groups ran together: the gap between two of them was 16px against 8px
   inside one, which is not enough contrast to scan a 57-chip column by. A
   hairline above each group (the same structure the rest of the site uses)
   and the extra padding bind each legend to the chips it labels. */
.filters__group + .filters__group {
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

.filters__legend {
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-3);
}

/* The search field with its icon. */
.search-input {
  position: relative;
}

.search-input svg {
  position: absolute;
  left: 0.85em;
  top: 50%;
  width: 1.05em;
  height: 1.05em;
  transform: translateY(-50%);
  color: var(--ink-4);
  pointer-events: none;
}

.search-input .input {
  padding-left: 2.5em;
}

/* Chip rows for beds, price, features. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.chip {
  padding: 0.42em 0.85em;
  font-size: var(--step--1);
  font-weight: var(--weight-medium);
  color: var(--ink-2);
  /* An outlined chip, not a filled one. A filter column holds a dozen of
     these, and a dozen grey blocks read as noise. */
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: var(--transition-colors);
}

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

/* In the filter column every chip grows to fill its row. Laid out at their
   natural width, two chips never fitted the 238px column, so each one sat
   alone on its own line with half the row left empty — twelve ragged rows
   where six tidy ones belong, which is what made the column look crammed
   and unfinished. Growing them keeps short labels paired and lets a long one
   ("Project-Based Section 8") take the full width on its own line. */
.filters .chip {
  flex: 1 1 auto;
}

.chip[aria-pressed='true'] {
  /* Selected reads the same as a primary button: solid near-black. */
  color: var(--bg);
  background: var(--ink);
  border-color: var(--ink);
  font-weight: var(--weight-medium);
}

.browse-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

.browse-bar__count {
  font-size: var(--step--1);
  color: var(--ink-3);
}

.browse-bar__count b {
  color: var(--ink);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
}

.browse-results {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.25rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
}

/* The active-filter summary above the results. */
.active-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-5);
}

.active-filters:empty {
  display: none;
}

.filter-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.3em 0.5em 0.3em 0.75em;
  font-size: var(--step--2);
  font-weight: var(--weight-semibold);
  color: var(--brand);
  background: var(--brand-soft);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-sm);
}

.filter-pill button {
  display: grid;
  place-items: center;
  width: 1.3em;
  height: 1.3em;
  padding: 0;
  color: inherit;
  background: transparent;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  transition: var(--transition-colors);
}

.filter-pill button:hover {
  background: var(--brand-soft-2);
}

.filter-pill svg {
  width: 0.75em;
  height: 0.75em;
}

/* =========================== LISTING DETAIL ============================= */

.detail {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: start;
}

@media (min-width: 1000px) {
  .detail {
    grid-template-columns: minmax(0, 1.65fr) minmax(320px, 1fr);
  }
}

/* Photo mosaic: one lead image plus a 2x2 of the rest. */
.gallery {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(4, 1fr);
  border-radius: var(--r-xl);
  overflow: hidden;
}

.gallery__item {
  position: relative;
  display: block;
  padding: 0;
  border: 0;
  background: var(--surface-2);
  cursor: pointer;
  overflow: hidden;
}

.gallery__item:first-child {
  grid-column: 1 / -1;
  aspect-ratio: 16 / 10;
}

.gallery__item:not(:first-child) {
  grid-column: span 2;
  aspect-ratio: 4 / 3;
}

/* The same drawn placeholder the cards use, but this frame is ~1100px wide
   instead of ~360px. Dropping the padding that reserves the card's price
   plate recentres it, and letting the drawing grow stops a whole listing's
   first impression from being an empty grey rectangle. */
.gallery__item .lcard__ph {
  padding: var(--sp-6);
}

.gallery__item .lcard__ph svg {
  width: min(58%, 24rem);
}

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

  .gallery__item:first-child {
    grid-column: 1 / -1;
  }
}

.gallery__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-4) var(--ease);
}

.gallery__item:hover img {
  transform: scale(1.05);
}

.gallery__more {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-weight: var(--weight-semibold);
  color: #fff;
  background: rgba(14, 18, 19, 0.72);
}

/* The sticky enquiry card beside the details. */
.enquiry {
  position: sticky;
  top: calc(var(--header-h) + var(--sp-4));
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  padding: clamp(1.35rem, 3vw, 1.85rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-lg);
}

.enquiry__price {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
}

.enquiry__price b {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: var(--weight-semibold);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
}

.enquiry__price span {
  font-size: var(--step--1);
  color: var(--ink-3);
}

.detail__head {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.detail__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-6);
  padding: var(--sp-4) 0;
  border-block: 1px solid var(--line);
  font-size: var(--step--1);
  color: var(--ink-2);
}

.detail__facts li {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.detail__facts svg {
  width: 1.15em;
  height: 1.15em;
  color: var(--brand);
  flex: none;
}

.spec-list {
  display: grid;
  gap: var(--sp-3) var(--sp-6);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.spec {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
  font-size: var(--step--1);
}

.spec dt {
  color: var(--ink-3);
}

.spec dd {
  margin: 0;
  font-weight: var(--weight-semibold);
  color: var(--ink);
  text-align: right;
}

.amenity-list {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.amenity-list li {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--step--1);
  color: var(--ink-2);
}

.amenity-list svg {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  color: var(--brand);
}

/* Transit rows on the detail page: a stamp, the stop, and the distance. */
.transit-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.transit-list li {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}

.transit-list li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.transit-list__stop {
  flex: 1;
  min-width: 0;
  font-size: var(--step--1);
  color: var(--ink-2);
}

.transit-list__dist {
  font-size: var(--step--2);
  color: var(--ink-3);
  white-space: nowrap;
}

/* The map, with its honest fallback. */
.map {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--line);
}

.map__canvas {
  position: absolute;
  inset: 0;
}

.map__fallback {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: var(--sp-3);
  padding: var(--sp-6);
  text-align: center;
  font-size: var(--step--1);
  color: var(--ink-3);
}

/* ======================= TENANT / OWNER / CONTACT ======================= */

/* A numbered step, used by the apply and owner-process sections. */
.steps {
  counter-reset: step;
  display: grid;
  gap: clamp(1.25rem, 3vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}

.step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  /* The marker sits *beside* the first line, not above it. It used to be
     absolutely positioned over the heading with less top padding than its own
     height, so the heading ran straight into the bottom of the number and the
     whole list read as a number with text crammed under it. */
  padding-left: 2.75rem;
  min-height: 2rem;
}

.step::before {
  counter-increment: step;
  content: counter(step);
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: var(--weight-medium);
  line-height: 1;
  color: var(--bg);
  background: var(--ink);
  border-radius: 50%;
}

.step h3 {
  font-family: var(--font-display);
  font-size: var(--step-2);
}

.step p {
  color: var(--ink-2);
  line-height: var(--leading-relaxed);
}

/* A split card: copy on one side, a checklist on the other. */
.info-split {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: center;
}

@media (min-width: 900px) {
  .info-split {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .info-split--reverse > *:first-child {
    order: 2;
  }
}

.tick-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.tick-list li {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  line-height: var(--leading-normal);
  color: var(--ink-2);
}

.tick-list svg {
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  padding: 0.2rem;
  color: var(--brand);
  background: var(--brand-soft);
  border-radius: 50%;
}

.tick-list strong {
  display: block;
  color: var(--ink);
}

/* The quick-contact tiles on the contact page and the home page. */
.contact-tiles {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

.tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: clamp(1.35rem, 3vw, 1.85rem);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  text-decoration: none;
  color: inherit;
  transition: var(--transition-lift), border-color var(--dur-2) var(--ease);
}

.tile:hover {
  transform: translateY(-3px);
  border-color: var(--brand-line);
  box-shadow: var(--sh-md);
  color: inherit;
}

.tile__icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  color: var(--brand);
  background: var(--brand-soft);
  border-radius: var(--r-md);
}

.tile__icon svg {
  width: 1.4rem;
  height: 1.4rem;
}

.tile b {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: var(--weight-semibold);
}

.tile span {
  font-size: var(--step--1);
  color: var(--ink-3);
}

.tile__value {
  font-weight: var(--weight-semibold);
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}

/* The neighbourhood list on the home page. */
.hood-list {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
}

.hood {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  text-decoration: none;
  color: var(--ink);
  font-weight: var(--weight-medium);
  transition: var(--transition-colors);
}

.hood:hover {
  color: var(--brand);
  border-color: var(--brand-line);
  background: var(--brand-soft);
}

.hood span {
  font-size: var(--step--2);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* ============================ FORMS / CTA =============================== */

.form-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  padding: clamp(1.35rem, 3vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
}

.form-card > header {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.form-card > header h2 {
  font-size: var(--step-3);
}

/* A big closing call to action. */
/* The closing prompt is a light band, not a dark one. Only the footer is
   black, so the end of a page reads as one authored ending rather than two
   heavy blocks stacked together. */
.cta-band {
  padding-block: clamp(3rem, 6vw, 5rem);
  background: var(--bg-sunk);
  border-top: 1px solid var(--line);
}

.cta-band__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
}

.cta-band h2 {
  font-size: var(--step-4);
  color: var(--ink);
  max-width: 22ch;
}

.cta-band p {
  color: var(--ink-2);
  margin-top: var(--sp-3);
  max-width: 46ch;
}

/* ============================ LIGHTBOX ================================= */

/* The photo viewer on a detail page. Opens over everything; arrow keys and
   Escape are wired in js/listing.js. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: rgba(14, 18, 19, 0.96);
}

.lightbox[hidden] {
  display: none;
}

.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  color: #fff;
}

.lightbox__count {
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.72);
}

.lightbox__stage {
  display: grid;
  place-items: center;
  padding: 0 var(--sp-5);
  min-height: 0;
}

.lightbox__stage img {
  max-width: min(1200px, 100%);
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--r-md);
}

.lightbox__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  padding: var(--sp-5);
}

.lightbox__btn {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  cursor: pointer;
  transition: var(--transition-colors);
}

.lightbox__btn:hover {
  background: rgba(255, 255, 255, 0.22);
}

.lightbox__btn svg {
  width: 1.15rem;
  height: 1.15rem;
}

.lightbox__btn[disabled] {
  opacity: 0.35;
  cursor: not-allowed;
}

/* ============================ MISC ===================================== */

/* The bar that shows an apartment's status on the detail page. */
.status-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--step--1);
  background: var(--brand-soft);
  border: 1px solid var(--brand-line);
  border-radius: var(--r-md);
  color: var(--ink-2);
}

.status-bar svg {
  width: 1.15em;
  height: 1.15em;
  color: var(--brand);
  flex: none;
}

/* Two-up facts under a section heading. */
.fact-pair {
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
}

/* The "no results" state inside the browse grid spans the full row. */
.browse-results > .empty {
  grid-column: 1 / -1;
}

/* Print a listing page cleanly. */
@media print {
  .enquiry,
  .filters,
  .gallery__more {
    display: none !important;
  }
}

