/* =========================================================
   LISTED ARCHITECTURE — page stylesheet, V2
   ---------------------------------------------------------
   Built on the V2 tokens in assets/css/ds/. Nothing here
   names a colour; everything resolves through the system.

   THE RULE THAT SHAPES EVERYTHING ELSE:

     There is no single default page background. A page is a
     sequence of contrasting surfaces; the change from one to
     the next creates rhythm, hierarchy and emotion.

   So this file has no body background. Sections carry their
   own surface, and a page that forgets to is visibly wrong
   rather than quietly beige.

   Three voices, each with one job:
     Graphie creates desire        — display only, 28px floor
     Neue Haas communicates        — headings, body, UI
     Tekton adds editorial         — annotation, rarely
   A section usually needs no more than two of the three.
   ========================================================= */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  /* No background. Sections own their surface. */
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-weight: var(--fw-regular);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
figure { margin: 0; }

::selection { background: var(--gold-400); color: var(--navy-900); }

:focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}
.on-dark :focus-visible, .section--dark :focus-visible, .section--deep :focus-visible {
  outline-color: var(--focus-ring-inverse);
}

/* ---------- the page grows with the screen ----------

   THE BUG: --content-max is 1440px, so on anything wider the whole
   site sat in a 1440 column with dead margin either side — a laptop
   layout pinned to the middle of a 27-inch monitor.

   The system's 1440 is a sensible DEFAULT, not a ceiling. Above it the
   page keeps growing, but sub-linearly (86vw, capped at 2200) so an
   ultra-wide screen gets a bigger page rather than an absurd one. The
   margins grow with it.

   Reading measure is deliberately NOT part of this: a paragraph stays
   at --measure however wide the screen, because a 200-character line
   is unreadable at any resolution. Wide screens buy bigger IMAGES,
   wider GRIDS and larger DISPLAY TYPE — not longer lines. */
:root {
  --content-fluid: clamp(1200px, 94vw, 2400px);
  --margin-fluid:  clamp(20px, 2.6vw, 72px);
}

.wrap {
  width: 100%;
  max-width: var(--content-fluid);
  margin-inline: auto;
  padding-inline: var(--margin-fluid);
}

@media (max-width: 1200px) {
  :root { --content-fluid: 100%; }
}

@media (max-width: 720px) {
  :root { --margin-fluid: var(--page-margin-mobile); }
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* =========================================================
   SURFACES

   Each one has a job. See the system's surface table.
   ========================================================= */

.section { padding-block: var(--space-9); }      /* quieter sections */
.section--peak { padding-block: var(--space-10); } /* visual peaks */

.section--light   { background: var(--surface-light); }
.section--white   { background: var(--surface-white); }
.section--mineral { background: var(--surface-mineral); }

.section--dark,
.section--deep {
  color: var(--text-inverse);
}
.section--dark { background: var(--surface-dark); }
.section--deep { background: var(--surface-deep); }

.section--dark .meta,
.section--deep .meta,
.section--dark .body-l,
.section--deep .body-l { color: var(--text-inverse-muted); }

.section--dark .eyebrow,
.section--deep .eyebrow { color: var(--gold-500); }

.section--dark .title-block,
.section--deep .title-block { border-color: var(--line-dark); }
.section--dark .title-block > div,
.section--deep .title-block > div { border-color: var(--line-dark); }

/* ---------- transitions ----------

   Broad and gradual — at least 320px tall — never a compressed
   strip. A gradient section changes the emotional temperature
   without a hard edge. */

/* A transition is a breath between two grounds, not a section. At
   38vh it was half a screen of empty gradient and the page felt like
   it had stalled; the eye reads the change of ground long before the
   gradient has finished making it. */
.transition {
  min-height: calc(var(--transition-min-h) * .55);
  height: 20vh;
  max-height: var(--transition-min-h);
}
.transition--down { background: var(--gradient-light-to-dark); }
.transition--up   { background: var(--gradient-dark-to-light); }
.surface-depth    { background: var(--gradient-navy-depth); }

/* =========================================================
   TYPE
   ========================================================= */

h1, h2, h3 { margin: 0; color: inherit; }

/* ---- Graphie: display only. 28px is the floor. ---- */

.display-xl, .display-l, .h1-display, .title-display {
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  color: inherit;
  text-wrap: balance;
}

/* The upper bound of each clamp is the TOP of the system's stated
   range, not its nominal size — so a wide screen gets the 120px hero
   the system allows for, instead of stopping at the laptop value. */
.display-xl {
  font-size: clamp(3.25rem, 1.1rem + 5.6vw, 7.5rem);   /* 52 → 120 */
  line-height: var(--lh-display-xl);
  letter-spacing: var(--ls-display);
}
.display-l {
  font-size: clamp(2.5rem, 1.4rem + 3.1vw, 4.5rem);    /* 40 → 72 */
  line-height: var(--lh-display-l);
  letter-spacing: var(--ls-display);
}
.h1-display {
  font-size: clamp(2rem, 1.4rem + 1.9vw, 3.5rem);      /* 32 → 56 */
  line-height: var(--lh-h1);
  letter-spacing: var(--ls-display);
}
.title-display {
  font-size: var(--fs-title-display);                   /* 28 — the floor */
  line-height: var(--lh-title);
  letter-spacing: var(--ls-title);
}

/* Graphie display may be uppercase or title case, to suit the
   composition. Caps is a choice here, not a rule. */
.caps { text-transform: uppercase; }

/* ---- Neue Haas: headings ---- */

.h1, .h2, .h3 {
  font-family: var(--font-sans-display);
  font-weight: var(--fw-medium);
  color: inherit;
}
.h1 { font-size: clamp(2rem, 1.4rem + 1.9vw, 3.5rem); line-height: var(--lh-h1-sans); letter-spacing: var(--ls-h1); }
.h2 { font-size: clamp(1.625rem, 1.25rem + 1.3vw, 2.5rem); line-height: var(--lh-h2); letter-spacing: var(--ls-h2); }
.h3 { font-size: clamp(1.25rem, 1.1rem + 0.6vw, 1.75rem); line-height: var(--lh-h3); }

/* ---- Neue Haas: text ---- */

.body-l {
  margin: 0 0 var(--space-5);
  max-width: var(--measure);
  font-size: clamp(1.125rem, 1rem + 0.25vw, 1.375rem);   /* 18 → 22 */
  line-height: var(--lh-body-l);
  color: var(--text-secondary);
}

.body {
  margin: 0 0 var(--space-5);
  max-width: var(--measure);
  font-size: clamp(1rem, 0.95rem + 0.15vw, 1.125rem);    /* 16 → 18 */
  line-height: var(--lh-body);
  color: var(--text-secondary);
}

.body:last-child, .body-l:last-child { margin-bottom: 0; }

.section--dark .body, .section--deep .body,
.section--dark .body-l, .section--deep .body-l { color: var(--text-inverse-muted); }

.meta {
  font-size: var(--fs-meta);
  line-height: var(--lh-meta);
  color: var(--text-muted);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  line-height: 1.3;
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* The gold rule that opens a section marker. */
.eyebrow::before {
  content: "";
  width: var(--space-6);
  height: 1px;
  background: var(--line-accent);
  flex: none;
}
.eyebrow--bare::before { display: none; }

.price {
  font-size: var(--fs-price);
  font-weight: var(--fw-medium);
  line-height: var(--lh-h3);
  color: inherit;
}

/* ---- Tekton: the annotation layer. Rarely. ---- */

.annotation {
  font-family: var(--font-annotation);
  font-size: var(--fs-annotation);
  line-height: var(--lh-annotation);
  letter-spacing: var(--ls-annotation);
  color: inherit;
}

/* ---- Plex Mono: anything measured ---- */

.data {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  line-height: var(--lh-data);
  letter-spacing: var(--ls-data);
}

/* =========================================================
   NAVIGATION
   ========================================================= */

.nav {
  position: sticky; top: 0; z-index: 60;
  height: var(--nav-h);
  display: flex; align-items: center;
  background: var(--surface-dark);
  color: var(--text-inverse);
}

.nav__inner {
  position: relative;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-7);
  width: 100%;
}

.nav__links { display: flex; align-items: center; gap: var(--space-6); }

/* THE BUG: this rule was deleted by an edit that rebuilt the toggle
   around it, so the links fell back to the browser default — and
   V1's base.css was still underlining every anchor, which made the
   whole menu look selected.

   The gold underline marks the CURRENT page and nothing else. Hover
   lifts the ink instead: an underline that follows the cursor claims
   "you are here" about whatever is under the mouse. */
.nav__link {
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-inverse-muted);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid transparent;
  transition: color var(--duration-fast) var(--ease-architectural);
}

.nav__link:hover { color: var(--text-inverse); }

.nav__link[aria-current="page"] {
  color: var(--text-inverse);
  border-bottom-color: var(--line-accent);
}

/* ---------- the toggle ----------

   THE BUG, TWICE OVER.

   First there was no mobile navigation at all: the link row overflowed
   off the side of the screen and the site was unnavigable below about
   900px.

   The fix for that introduced a worse one. I wrapped the links in a
   <details> so the disclosure would work without script — but a CLOSED
   <details> hides its children with content-visibility on an internal
   slot, which `display: flex` cannot override. So the links vanished on
   desktop as well, and the site lost its menu entirely.

   A checkbox and a label do the same job correctly: the links are a
   real sibling, so they are simply visible at desktop widths and the
   toggle only governs them below the breakpoint. No script, so it
   cannot break when JavaScript fails. */

.nav__check { position: absolute; opacity: 0; pointer-events: none; }

.nav__toggle {
  display: none;
  width: var(--control-h); height: var(--control-h);
  align-items: center; justify-content: center;
  border: 1px solid var(--line-dark);
  background: transparent;
  color: var(--text-inverse);
  cursor: pointer;
}

.nav__bars { display: block; width: 18px; height: 1px; background: currentColor; position: relative; }
.nav__bars::before,
.nav__bars::after {
  content: ""; position: absolute; left: 0;
  width: 18px; height: 1px; background: currentColor;
  transition: transform var(--duration-fast) var(--ease-architectural);
}
.nav__bars::before { top: -6px; }
.nav__bars::after  { top: 6px; }

/* The focus ring belongs on the control the keyboard is actually on. */
.nav__check:focus-visible + .nav__toggle {
  outline: var(--focus-width) solid var(--focus-ring-inverse);
  outline-offset: var(--focus-offset);
}

@media (max-width: 900px) {
  .nav__toggle { display: inline-flex; }

  .nav__links {
    display: none;
    position: absolute;
    top: var(--nav-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch;
    gap: 0;
    padding: var(--space-3) var(--margin-fluid) var(--space-6);
    background: var(--surface-dark);
    border-top: 1px solid var(--line-dark);
  }

  .nav__check:checked ~ .nav__links { display: flex; }

  .nav__check:checked + .nav__toggle .nav__bars { background: transparent; }
  .nav__check:checked + .nav__toggle .nav__bars::before { transform: translateY(6px) rotate(45deg); }
  .nav__check:checked + .nav__toggle .nav__bars::after  { transform: translateY(-6px) rotate(-45deg); }

  .nav__links .nav__link {
    padding-block: var(--space-4);
    border-bottom: 1px solid var(--line-dark);
  }
  .nav__links .nav__link[aria-current="page"] { color: var(--gold-500); }
}

/* ---------- the lockup ----------

   The system specifies it as TYPE, not as a raster: "LISTED|ARCHITECTURE
   in Graphie — LISTED semibold, ARCHITECTURE regular, a hairline
   between — beside the gold dimension-mark bracket."

   Colourway C ONLY: gold bracket, gold LISTED, ivory ARCHITECTURE on
   navy and over imagery. On light surfaces ARCHITECTURE switches to
   navy; the bracket and LISTED stay gold. Never the all-gold or
   all-ivory versions, never other colours, never recolour the bracket.

   A previous version used a "[" character as the bracket. The real
   mark is a drafting dimension mark — extension line with 45 degree
   ticks — and it ships as mark.svg. */

/* Spacing is in EM, not in spacing tokens: the lockup is a piece of
   typography, so its gaps have to scale with the type rather than
   stepping to the next 8px when the logo grows. Both gaps were set
   from the spacing scale and were visibly too loose — the bracket
   floated away from the L, and the rule sat in a pool of air between
   the D and the A. */
.logo {
  display: inline-flex; align-items: center; gap: 0.34em;
  font-family: var(--font-display);
  font-size: clamp(1rem, .9rem + .3vw, 1.3125rem);
  letter-spacing: var(--ls-logo);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  line-height: 1;
}

.logo__mark {
  height: 1.75em;
  width: auto;
  flex: none;
}

/* The gap either side of the rule: D | A. Reduced 30% from 0.3em.
   The gap at the dimension mark is set on .logo and is deliberately
   larger — the mark needs room to read as a measurement, the rule
   does not. */
.logo__words { display: inline-flex; align-items: center; gap: 0.21em; }
.logo__listed { color: var(--gold-500); font-weight: var(--fw-semibold); }
.logo__rule   { width: 1px; height: 1em; background: var(--gold-500); opacity: .55; }
.logo__arch   { color: var(--text-inverse); font-weight: var(--fw-regular); }

.logo--on-light .logo__arch { color: var(--navy-900); }

/* =========================================================
   BUTTONS

   Contrast rule: light section → dark CTA; dark section →
   light CTA. A primary action must never disappear into its
   section. One primary per view. Gold reinforces — the arrow,
   an underline, a hover. Never a gold fill.
   ========================================================= */

.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }

/* A primary action must never disappear into its section, and it
   should feel like a way through rather than a label in a box. Three
   things give it presence without decoration: real height and
   generous inline padding, a fill that WIPES in on the drafting
   easing, and an arrow that travels. Gold reinforces the arrow —
   never the fill. */

.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-4);
  min-height: var(--control-h);
  padding: 0 var(--space-7);
  border: 1px solid transparent;
  border-radius: var(--radius-none);
  font-family: var(--font-sans);
  font-size: var(--fs-button);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  transition: color var(--duration-base) var(--ease-architectural),
              border-color var(--duration-base) var(--ease-architectural);
}

/* the fill, wiping in from the left */
.btn::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duration-base) var(--ease-architectural);
}
.btn:hover::before { transform: scaleX(1); }

.btn__arrow {
  transition: transform var(--duration-base) var(--ease-architectural);
  font-size: 1.15em;
  line-height: 1;
}
.btn:hover .btn__arrow { transform: translateX(5px); }

.btn:active { transform: translateY(1px); }

/* primary — on a light section */
.btn--primary {
  background: var(--action-primary);
  color: var(--action-primary-text);
  border-color: var(--action-primary);
}
.btn--primary::before { background: var(--action-primary-hover); }
.btn--primary .btn__arrow { color: var(--gold-400); }

/* inverse — on a dark section or over imagery */
.btn--inverse {
  background: var(--action-inverse);
  color: var(--action-inverse-text);
  border-color: var(--action-inverse);
}
.btn--inverse::before { background: var(--action-inverse-hover); }

/* secondary — outline, filling on hover */
.btn--outline {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--line-control);
}
.btn--outline::before { background: var(--action-primary); }
.btn--outline:hover { color: var(--action-primary-text); border-color: var(--action-primary); }

.section--dark .btn--outline,
.section--deep .btn--outline,
.plan .btn--outline,
.hero .btn--outline {
  color: var(--text-inverse);
  border-color: var(--line-dark);
}
.section--dark .btn--outline::before,
.section--deep .btn--outline::before,
.plan .btn--outline::before,
.hero .btn--outline::before { background: var(--action-inverse); }
.section--dark .btn--outline:hover,
.section--deep .btn--outline:hover,
.plan .btn--outline:hover,
.hero .btn--outline:hover {
  color: var(--action-inverse-text);
  border-color: var(--action-inverse);
}

/* tertiary — a text link with an arrow and a gold underline */
.link {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-button);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  text-decoration: none;
  color: inherit;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line-accent);
  transition: border-color var(--duration-fast) var(--ease-architectural);
}
.link:hover { border-bottom-color: var(--gold-400); }

/* =========================================================
   STATUS CHIPS

   Compact, strongly legible, minimally rounded, no shadows,
   visually subordinate to the property.
   ========================================================= */

.chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-chip);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-chip);
  text-transform: uppercase;
  white-space: nowrap;
}

.chip::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: var(--radius-full);
  background: currentColor;
  flex: none;
}

.chip--available   { background: var(--status-available-soft);   color: var(--status-available-ink); }
.chip--under-offer { background: var(--status-under-offer-soft); color: var(--status-under-offer-ink); }
.chip--sold        { background: var(--status-sold-soft);        color: var(--status-sold-ink); }

/* The Suggested Edit chip is FILLED vermilion with a navy label —
   white on vermilion is too weak. Its power is rarity. */
.chip--edit {
  background: var(--status-edit);
  color: var(--status-edit-text);
}
.chip--edit::before { display: none; }

/* =========================================================
   HERO — full-screen architectural photograph
   ========================================================= */

.hero {
  position: relative;
  min-height: min(88vh, 860px);
  display: grid;
  align-items: end;
  background: var(--surface-deep);
  color: var(--text-inverse);
  isolation: isolate;
}

.hero__img {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%;
  object-fit: cover;
}

/* Text over images uses gradient-image. It reveals the image,
   never obscures it. */
.hero__veil {
  position: absolute; inset: 0; z-index: -1;
  background: var(--gradient-image);
}

.hero__inner { padding-block: var(--space-9); }

.hero .eyebrow { color: var(--text-inverse); }
.hero .eyebrow::before { background: var(--gold-500); }

.hero__title { margin: var(--space-6) 0 0; max-width: 14ch; }

.hero__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--space-6);
  margin-top: var(--space-7);
}

.hero__meta {
  font-size: var(--fs-body-l);
  color: var(--text-inverse);
  margin: 0;
}

/* =========================================================
   TITLE BLOCK — the facts, as a drawing carries them
   ========================================================= */

.title-block {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--line-light);
  border-left: 1px solid var(--line-light);
}

@media (max-width: 720px) { .title-block { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.title-block > div {
  padding: var(--space-5);
  border-right: 1px solid var(--line-light);
  border-bottom: 1px solid var(--line-light);
  min-width: 0;
}

.title-block dt {
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}

.section--dark .title-block dt,
.section--deep .title-block dt { color: var(--text-inverse-muted); }

.title-block dd {
  margin: 0;
  font-family: var(--font-sans-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-regular);
  line-height: var(--lh-h3);
}

/* =========================================================
   FEATURED — image left, navy panel right
   ========================================================= */

.featured {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  background: var(--surface-dark);
  color: var(--text-inverse);
}

@media (max-width: 900px) { .featured { grid-template-columns: minmax(0, 1fr); } }

.featured__media { position: relative; overflow: hidden; }
.featured__media img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4 / 3; }

.featured__panel {
  padding: var(--space-8);
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--space-5);
}

.featured__panel .eyebrow { color: var(--gold-500); }

/* =========================================================
   GALLERY
   ========================================================= */

.gallery { display: grid; gap: var(--gutter); }

/* On a wide screen a pair becomes a trio rather than two enormous
   photographs. The grid gains columns; it does not just stretch. */
@media (min-width: 1800px) {
  .gallery__pair { grid-template-columns: repeat(3, 1fr); }
}
.gallery__plate img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.gallery__pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gutter); }
.gallery__pair img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }

@media (max-width: 720px) { .gallery__pair { grid-template-columns: 1fr; } }

/* A caption names the photograph above it — the site writing in the
   margin of a picture, which is Tekton's one role. It was mono,
   which reads as data: "SCHREIBER HOUSE — Hampstead, London" is not
   data, it is us saying what you are looking at. */
.caption {
  margin: var(--space-3) 0 0;
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  line-height: 1.4;
  color: var(--text-secondary);
}
.section--dark .caption,
.section--deep .caption { color: var(--text-inverse-muted); }

/* =========================================================
   ANNOTATION — Tekton, with a leader line into the image

   The rarest face. It should feel discovered, not repeated.
   ========================================================= */

.markup {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
  gap: var(--space-8);
  align-items: center;
}

@media (max-width: 900px) { .markup { grid-template-columns: minmax(0, 1fr); } }

.markup__media { position: relative; }
.markup__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

.markup__note {
  position: absolute;
  right: 6%; bottom: 18%;
  max-width: 18ch;
  padding: var(--space-3) var(--space-4);
  background: var(--scrim);
  color: var(--text-inverse);
  text-align: left;
}

/* The leader line: a hairline from the note to the thing it means. */
.markup__note::before {
  content: "";
  position: absolute;
  left: -22%; top: 50%;
  width: 20%; height: 1px;
  background: var(--text-inverse);
  opacity: .8;
}

/* Both of these sit ON a photograph that carries no wash — the
   markup image never had one — in near-white ink. Over a dark
   interior they read; over a white render they disappear entirely,
   which is the fault the other captions were just fixed for and
   this pair was missed in.

   A plate rather than a shadow, for the same reason as everywhere
   else: a shadow cannot be measured against the worst photograph in
   the catalogue and a plate can. */
.markup__caption {
  position: absolute; left: var(--space-5); bottom: var(--space-5);
  padding: var(--space-2) var(--space-4);
  background: var(--scrim);
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  line-height: 1.3;
  color: var(--text-inverse);
}

.markup__body .h2 { margin-block: var(--space-4) var(--space-5); }

/* The edit's own verdict, in Tekton, in the signal colour. */
.markup__verdict {
  font-family: var(--font-annotation);
  font-size: var(--fs-annotation);
  letter-spacing: var(--ls-annotation);
  color: var(--status-edit);
  margin: var(--space-5) 0 0;
}

/* =========================================================
   DISCOVERY RAIL — every property leads somewhere
   ========================================================= */

.rail__chips {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin-top: var(--space-5);
}

.rail__chip {
  display: inline-flex; align-items: baseline; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-white);
  border: 1px solid var(--line-light);
  border-radius: var(--radius-sm);
  font-size: var(--fs-meta);
  text-decoration: none;
  color: var(--text-primary);
  transition: border-color var(--duration-fast) var(--ease-architectural);
}
.rail__chip:hover { border-color: var(--action-primary); }
.rail__chip .data { color: var(--text-muted); }

.rail__chip--edit::before {
  content: "";
  width: 7px; height: 7px;
  background: var(--status-edit);
  align-self: center;
  flex: none;
}

/* =========================================================
   ENQUIRIES

   THE BUG: the broker's name was set as a centred Graphie
   display line — 64px of someone else's brand, and the
   largest thing on a page about OUR house. Billing an agent
   above the architecture is exactly the estate-agent move
   this site exists not to make.

   The section is now a quiet two-column block: the action on
   the left, the credit inside a sentence on the right. The
   broker is named properly, at body size, in medium weight.
   ========================================================= */

.enquiry {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--space-8);
  align-items: start;
}

@media (max-width: 900px) { .enquiry { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); } }

/* =========================================================
   FOOTER
   ========================================================= */

.foot { padding-block: var(--space-9) var(--space-7); }
.foot__grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) repeat(2, minmax(0, 3fr));
  gap: var(--space-8);
}
@media (max-width: 900px) { .foot__grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-6); } }

.foot__list { margin: var(--space-5) 0 0; padding: 0; list-style: none; }
.foot__list li { margin-bottom: var(--space-3); }
.foot__list a { color: var(--text-inverse); text-decoration: none; font-size: var(--fs-body); }
.foot__list a:hover { color: var(--gold-400); }

.foot__base {
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line-dark);
}

/* =========================================================
   MOTION — alive, not moving for the sake of moving
   ========================================================= */

.reveal {
  opacity: 0;
  transform: translateY(var(--rise));
  transition: opacity var(--duration-slow) var(--ease-reveal),
              transform var(--duration-slow) var(--ease-reveal);
}
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
  .reveal { opacity: 1; transform: none; }
}

/* =========================================================
   THE INFORMATION A BROKER HAS AND WE DID NOT

   A listing page has to carry what the representing
   brokerage carries, or it is a worse reference than the
   page it points at. From the reference set: a property
   summary, a gallery, a DETAILS tab of grouped specification
   lists, a downloadable floorplan PDF, and the address on a
   map.

   The important architectural decision is WHERE it goes.

   The editorial voice bans equipment — no ranges, no
   warranties, no security systems. That ban is right for the
   PROSE and wrong for the page: a buyer legitimately wants
   to know there is a Sub-Zero and a tankless heater. So the
   two live apart. The essay stays architecture; the spec
   lists are structured data in their own section, set as a
   drawing schedule rather than written out in sentences.

   Both exist. Neither contaminates the other.
   ========================================================= */

/* ---------- the section rail ----------
   Sticky, under the masthead, so a long page stays navigable. */

.railnav {
  position: sticky;
  top: var(--nav-h);
  z-index: 50;
  background: var(--surface-light);
  border-bottom: 1px solid var(--line-light);
}

.railnav__inner {
  display: flex;
  gap: var(--space-7);
  overflow-x: auto;
  scrollbar-width: none;
}
.railnav__inner::-webkit-scrollbar { display: none; }

.railnav__link {
  flex: none;
  padding-block: var(--space-5);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  transition: color var(--duration-fast) var(--ease-architectural),
              border-color var(--duration-fast) var(--ease-architectural);
}
.railnav__link:hover { color: var(--text-primary); }
.railnav__link[aria-current="true"] {
  color: var(--text-primary);
  border-bottom-color: var(--line-accent);
}

/* ---------- specification schedule ----------

   Grouped lists, set like a schedule on a drawing: a heading
   rule, then items on hairlines. Mono for anything measured.
   Deliberately NOT prose. */

.schedule {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-8) var(--space-7);
  margin-top: var(--space-7);
}

.schedule__group > h3 {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--text-primary);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-eyebrow);
  text-transform: uppercase;
  font-family: var(--font-sans);
}

.schedule__list { margin: 0; padding: 0; list-style: none; }

.schedule__list li {
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--line-light);
  font-size: var(--fs-meta);
  line-height: var(--lh-meta);
  color: var(--text-secondary);
}

.section--dark .schedule__group > h3,
.section--deep .schedule__group > h3 { border-bottom-color: var(--line-dark); }
.section--dark .schedule__list li,
.section--deep .schedule__list li { border-bottom-color: var(--line-dark); }

/* ---------- plans ----------
   A floorplan is a drawing, so it sits on the blueprint ground
   rather than on paper, and it is downloadable. */

.plan {
  background: var(--gradient-navy-depth);
  padding: var(--space-8);
}
/* The plan sits on a white sheet inside the blueprint ground.

   It was screen-blended onto the navy, which only works for a drawing
   that is white lines on black. Almost every floorplan a brokerage
   publishes is the opposite — black lines on white — and screen blend
   turns that into a blank white rectangle. A plan nobody can read is
   worse than a plan without the flourish. */
.plan img {
  width: 100%; height: auto; display: block;
  background: var(--surface-white);
  padding: var(--space-5);
}

/* The plan panel is a DARK SURFACE, whatever section it sits in. The
   download button inherited the light-section treatment — navy on
   navy — and was invisible. Any control placed on a dark panel has to
   be told that the panel is dark. */
.plan, .plan .data { color: var(--text-inverse); }

.plan__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--space-5);
  margin-top: var(--space-6);
}

/* ---------- map ----------
   The address, the enclave, and what is nearby. Architecture is
   never only the building. */

.map {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--space-8);
  align-items: center;
}
@media (max-width: 900px) { .map { grid-template-columns: minmax(0, 1fr); } }

.map__frame {
  aspect-ratio: 16 / 10;
  background: var(--surface-mineral);
  border: 1px solid var(--line-light);
  position: relative;
  overflow: hidden;
}

.map__pin {
  position: absolute; left: 50%; top: 50%;
  width: 10px; height: 10px;
  margin: -5px 0 0 -5px;
  background: var(--status-edit);
  border-radius: var(--radius-full);
  box-shadow: 0 0 0 6px rgba(228, 87, 46, .2);
}

.map__list { margin: var(--space-5) 0 0; padding: 0; list-style: none; }
.map__list li {
  display: flex; justify-content: space-between; gap: var(--space-5);
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--line-light);
  font-size: var(--fs-meta);
}
.map__list .data { color: var(--text-muted); }

/* =========================================================
   COMMENTARY

   THE PROBLEM: all the writing pooled in one place at the
   top, so an entry opened with a wall of prose and then ran
   out of words entirely.

   The writing is now spread down the page in short passages,
   each beside the photographs it describes — the opening,
   what to look for after the gallery, the detail beside the
   plans, and who made it. A reader meets a few paragraphs at
   a time, in the place where they mean something.

   The shape is deliberately the same each time — marker and
   heading left, prose right — so the page has a spine the
   eye can follow between the full-bleed sections.
   ========================================================= */

.commentary {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: var(--space-7) var(--space-8);
  align-items: start;
}

@media (max-width: 900px) {
  .commentary { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
}

/* On a wide screen the prose column must not run to the full width —
   the measure is what keeps it readable, not the container. */
.commentary > div:last-child > .body,
.commentary > div:last-child > .body-l { max-width: var(--measure); }

/* =========================================================
   FULL-SCREEN SLIDER

   Every photograph, at the size the house deserves, in one
   place — between "what to look for" and who made it. The
   reader has been told what to look at; this is where they
   look at it.

   Built on scroll-snap, so it works with no JavaScript at
   all: touch swipe, trackpad, shift-scroll and keyboard all
   drive it natively. The arrows are an enhancement layered
   on top — if the script fails they are simply not shown,
   and the slider still works.
   ========================================================= */

.slider {
  position: relative;
  background: var(--surface-deep);
  color: var(--text-inverse);
}

.slider__track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.slider__track::-webkit-scrollbar { display: none; }

.slider__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  position: relative;
  height: min(92vh, 1000px);
}

.slider__slide img,
.slider__shot {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* NO WASH HERE AT ALL.

   This is the one place on the site whose entire purpose is the
   photograph: full screen, one at a time, after the reader has been
   told what to look for. Shading it to make two small labels legible
   is the wrong trade — a photographer's work dimmed across its whole
   area so that "03 / 11" can sit on top of it.

   The caption and the counter carry their own plates instead, which
   cover a few square centimetres each and leave the picture alone.

   There is no .slider__veil rule because there is no veil element:
   leaving one in place, hidden, is how it comes back. */

.slider__caption {
  position: absolute;
  left: var(--margin-fluid);
  bottom: var(--space-7);
  display: flex; align-items: baseline; gap: var(--space-4);
  padding: var(--space-2) var(--space-4);
  /* Its own cover, now that the photograph has none. */
  background: var(--scrim);
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  line-height: 1.3;
  color: var(--text-inverse);
}

/* The index stays mono. It is a position in a sequence, not a
   remark — the same reason the counter in the other corner is mono
   while the words beside it are not. */
.slider__index {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--gold-400);
}

/* ---------- arrows ----------
   Hidden until the script marks the slider as enhanced. */

.slider__nav {
  position: absolute; top: 50%;
  transform: translateY(-50%);
  width: var(--control-h); height: var(--control-h);
  display: none;
  align-items: center; justify-content: center;
  border: 1px solid var(--line-dark);
  background: var(--scrim);
  color: var(--text-inverse);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-architectural),
              border-color var(--duration-fast) var(--ease-architectural);
}
.slider__nav:hover { background: var(--action-inverse); color: var(--action-inverse-text); border-color: var(--action-inverse); }
.slider__nav--prev { left: var(--margin-fluid); }
.slider__nav--next { right: var(--margin-fluid); }

.slider[data-enhanced] .slider__nav { display: inline-flex; }

/* ---------- counter ---------- */

/* Same corner, same problem: the wash clears the lower right, so the
   counter is on its own there. Mono, because it is a count — the
   Tekton role is for the site's own words, and "03 / 11" is not a
   remark, it is data. */
.slider__count {
  position: absolute;
  right: var(--margin-fluid);
  bottom: var(--space-7);
  padding: var(--space-2) var(--space-4);
  background: var(--scrim);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-inverse);
}

@media (max-width: 720px) {
  .slider__slide { height: min(70vh, 620px); }
  .slider__nav { display: none !important; }
}

/* =========================================================
   PLACEHOLDERS (preview only)

   A flat gradient standing in for a photograph reads as an
   empty panel, which is how a full-screen hero came to look
   like a missing image. A placeholder has to announce that
   it is one.
   ========================================================= */

.shot { position: relative; background: var(--surface-mineral); }
.shot::after {
  content: attr(data-shot);
  position: absolute; left: var(--space-5); bottom: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-inverse);
  opacity: .75;
}


/* =========================================================
   ══ PART TWO — THE REST OF THE SITE ══

   Everything above was built for one page, the listing, and
   iterated on until it was right. What follows carries the
   same system across the catalogue, the home page, brokers,
   about, submit and the Edit, so that one stylesheet and one
   set of tokens serve the whole site.

   The ordering principle is the same as part one: surfaces
   first, then the repeating objects, then the page-specific
   shapes. Nothing here introduces a colour, a size or a
   duration that is not already a token.
   ========================================================= */


/* ---------------------------------------------------------
   SKIP LINK

   Visible only when focused. It has to sit above the fixed
   masthead, or it lands behind it and a keyboard user
   watching for it sees nothing happen.
   --------------------------------------------------------- */

.skip {
  position: fixed; top: var(--space-3); left: var(--space-3);
  z-index: 100;
  transform: translateY(calc(-100% - var(--space-5)));
  padding: var(--space-3) var(--space-5);
  background: var(--surface-dark); color: var(--text-inverse);
  font-family: var(--font-sans); font-size: var(--fs-meta);
  transition: transform var(--duration-base) var(--ease-architectural);
}
.skip:focus { transform: translateY(0); }


/* ---------------------------------------------------------
   THE LISTING CARD

   The system is explicit that a card is a sales object and
   that a grid of them should read "curated, not
   database-generated". Two decisions follow from that.

   First, the information order is fixed and the same on
   every card — location, title, architect and year, then the
   measures. A card that reorders itself according to what
   data happens to exist is exactly the database look, and it
   also makes the grid impossible to scan: the eye learns one
   position for the price and then has to hunt for it.

   Second, a missing field leaves its line out rather than
   collapsing the ones below it. The lines are laid out from
   the bottom of the body, so the measures sit on the same
   baseline across a row whatever is above them.
   --------------------------------------------------------- */

.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-7) var(--space-6);
  margin-top: var(--space-6);
  /* EVERY ROW THE SAME HEIGHT — AND THE HEIGHT IS CHOSEN, NOT
     INHERITED FROM WHICHEVER CARD HAPPENS TO BE TALLEST.

     `grid-auto-rows: 1fr` makes the rows equal, which is right, but
     it equalises them UPWARD to the tallest card on the page. One
     house with a long name and an editorial note then dragged every
     photograph on the page taller, and a photograph that is taller
     than it is wide is a portrait crop of a building that is almost
     always the other way round.

     So the row is stated: a photograph of a chosen depth, plus the
     space four lines of text need. The media fills whatever is left,
     so it is exactly that depth on an ordinary card and a little
     deeper on a wide one, whose text reads across instead of
     stacking.

     minmax rather than a flat height: a row grows if its content
     genuinely needs more, because clipping someone's house name to
     protect a grid is the wrong trade. In practice nothing does —
     the note is held to two lines below. */
  --card-media: clamp(200px, 18vw, 320px);
  --card-text: 16rem;
  grid-auto-rows: minmax(calc(var(--card-media) + var(--card-text)), auto);
  align-items: stretch;
}

/* Below three columns there is no leftover column to pair a
   promotion with, and the text has to wrap harder, so the row needs
   a little more room for it — measured, a two-line note at this
   width pushed one row 68px past its neighbours. */
@media (max-width: 1100px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); --card-text: 19rem; }
}
@media (max-width: 680px) {
  .grid { grid-template-columns: 1fr; gap: var(--space-7); --card-text: 17rem; }
}

.card { position: relative; }

.card__link {
  display: grid;
  /* The photograph takes whatever height is left after the text. That
     is what lets a one-column card and a two-column card finish on the
     same line without either one stretching its type. */
  grid-template-rows: minmax(0, 1fr) auto;
  height: 100%;
  color: inherit;
}

/* A card with no photograph has nothing to absorb the slack, so its
   text sits at the top rather than floating in the middle of an empty
   cell. */
.card__link:not(:has(.card__media)) { grid-template-rows: auto; align-content: start; }

.card__media {
  position: relative;
  overflow: hidden;
  min-height: 0;
  background: var(--surface-mineral);

  /* NO ASPECT RATIO HERE, DELIBERATELY.

     An aspect-ratio makes the box's height definite, and a definite
     height cannot stretch. Two attempts went round this loop:

       · ratio on the media — the photographs kept their shape and
         the slack appeared as a band of white between the picture
         and the text, inside every card
       · no ratio and only a min-height — the tallest card on the
         page set the row, and every photograph became a portrait
         crop of a building that is almost always wider than it is
         tall

     Only one thing can decide the height, and it has to be the ROW,
     because the row is what makes the cards equal. So the media
     stretches to fill whatever is left after the text, and its SHAPE
     is controlled by the one number below.

     At three columns on a 1440 screen that is roughly 260 × 405 —
     landscape, which is what a house is. A wide card's photograph is
     the same height across twice the width, which is the panorama.

     MEASURED, not guessed. With `height: 100%` here the height
     resolved to auto, so each photograph fell back to its own
     intrinsic ratio — and the rows came out 875, 539, 759 and 435,
     because a 4:3 image across two columns is 631px tall and dragged
     its whole row with it. */
  height: var(--card-media);
}
.card__media img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform var(--duration-slow) var(--ease-architectural);
}
.card__link:hover .card__media img { transform: scale(1.03); }

@media (prefers-reduced-motion: reduce) {
  .card__media img { transition: none; }
  .card__link:hover .card__media img { transform: none; }
}



/* The Edit flag. On V1 this was vermilion at full strength and
   shouted louder than the house; it is now the status chip it
   always should have been, and it sits ON the photograph so it
   does not take a line of the body. */
.card__flag {
  position: absolute; top: var(--space-4); left: var(--space-4);
  z-index: 2;
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-deep); color: var(--text-inverse);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-chip);
  letter-spacing: var(--ls-chip); text-transform: uppercase;
}
.card__flag::before {
  content: ''; width: 6px; height: 6px; flex: none;
  background: var(--status-edit);
}
.card__flag--bare { position: static; margin-bottom: var(--space-4); }

.card__body {
  display: flex; flex-direction: column;
  gap: var(--space-2);
  padding-top: var(--space-5);
}

/* Location, as an eyebrow — the system's card format opens with it. */
.card__place {
  order: 1;
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase;
  color: var(--text-secondary);
}

/* The name. Graphie, so the one word on the card that is meant to
   be WANTED is the one set in the face that does desire — and
   never below its 28px floor. */
.card__name {
  order: 2;
  margin: 0;
  font-family: var(--font-display); font-weight: 400;
  font-size: var(--fs-title-display);
  line-height: 1.15;
  letter-spacing: var(--ls-title);
  text-transform: uppercase;
  color: var(--text-primary);
}

.card__architect {
  order: 3;
  font-family: var(--font-sans);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--text-secondary);
}

/* The measures, mono, because they are data and the eye should be
   able to compare them down a column. */
.card__measure {
  order: 4;
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-primary);
}

/* Our own commentary, when there is any. Tekton — the same
   editorial-intervention role it has on the listing page. */
.card__note {
  order: 5;
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line-light);
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--text-secondary);
  max-width: 44ch;
  /* Two lines. The note is the one part of a card whose length
     nobody controls at writing time, and a four-line note on one
     house pushes its row taller than every other row on the page.
     The full note is on the listing itself. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card__foot {
  order: 6;
  margin-top: var(--space-4);
  display: flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-button);
  letter-spacing: var(--ls-button); text-transform: uppercase;
  color: var(--text-primary);
}
.card__foot .arw {
  transition: transform var(--duration-base) var(--ease-architectural);
}
.card__link:hover .card__foot .arw { transform: translateX(var(--space-2)); }

/* Cards on a dark band. */
.section--dark .card__name,
.section--deep .card__name { color: var(--text-inverse); }
.section--dark .card__place, .section--deep .card__place,
.section--dark .card__architect, .section--deep .card__architect,
.section--dark .card__note, .section--deep .card__note {
  color: var(--text-inverse-muted);
}
.section--dark .card__measure, .section--deep .card__measure,
.section--dark .card__foot, .section--deep .card__foot {
  color: var(--text-inverse);
}
.section--dark .card__note, .section--deep .card__note {
  border-top-color: var(--line-dark);
}


/* ---------------------------------------------------------
   A CARD THAT BREAKS THE GRID, AND WHAT SITS BESIDE IT

   "A grid should feel curated, not database-generated." The
   mechanism is that the editor marks a house as wide, and it
   takes two of the three columns.

   The important part is what happens to the third. Stretching
   the house across all three wastes the photograph on empty
   wall; leaving the column blank reads as a fault. So it
   carries a promotion — a Listed Edit feature, a brokerage, a
   partner.

   The wide card is forced to start at column 1. That is what
   keeps the pairing intact: a two-column card dropped into
   the middle of a row would wrap and strand its promotion
   somewhere else entirely.
   --------------------------------------------------------- */

@media (min-width: 1100px) {
  .card--wide {
    grid-column: 1 / span 2;
  }

  /* THE PHOTOGRAPH RUNS THE FULL WIDTH.

     The first attempt put the image in a left-hand column with the
     text beside it. That made the photograph TALLER THAN IT IS WIDE —
     a portrait crop of a house, in the one card on the page that
     exists to show a house broadly. A long low building, which is
     most of them, lost its ends.

     Across both columns the photograph becomes a panorama and the
     text sits under it, reading across the full width. The card is
     still exactly as tall as every other card: the media absorbs the
     difference, as it does everywhere else. */
  .card--wide .card__link {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The wide card's text reads across three columns instead of
     stacking, so it is about a hundred pixels shorter than an
     ordinary card's. That difference goes into the photograph, which
     becomes a deeper panorama — and the content bottoms line up
     exactly, which is the whole point. */
  .card--wide .card__name { font-size: var(--fs-h2); }

  /* The text under a panorama would otherwise run to 500 characters.
     It is held to three columns of its own, which keeps the measure
     readable and leaves the card looking composed rather than
     stretched. */
  .card--wide .card__body {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) auto;
    align-items: end;
    gap: var(--space-4) var(--space-6);
  }
  .card--wide .card__place   { grid-column: 1; grid-row: 1; align-self: end; }
  .card--wide .card__name    { grid-column: 1; grid-row: 2; }
  .card--wide .card__architect { grid-column: 2; grid-row: 1; }
  .card--wide .card__measure { grid-column: 2; grid-row: 2; margin-top: 0; }
  .card--wide .card__note    { grid-column: 1 / -1; grid-row: 3; max-width: 70ch; }
  .card--wide .card__foot    { grid-column: 3; grid-row: 2; margin-top: 0; }
}

/* Below three columns a wide card and its promotion each take a
   full row, one above the other.

   Letting the wide card fall back to an ordinary cell looked
   simpler and left a hole: the promotion, pushed to its own full
   row, freed the cell beside the house and nothing filled it. The
   pairing survives as a vertical one — the house, then the
   promotion that belongs to it. */
@media (max-width: 1099px) {
  .card--wide { grid-column: 1 / -1; }
}


/* ---------------------------------------------------------
   THE PROMOTION SLOT

   It sits in the grid, so it has to read as NOT A HOUSE at a
   glance. Three things do that: a different ground, a rule
   above it rather than a photograph, and — when the placement
   is paid for — a label.

   The label is not a nicety. This site's whole argument is
   that it sells nothing and takes nothing; an advertisement
   that looks like a house chosen on merit would quietly make
   that untrue.
   --------------------------------------------------------- */

.promo {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--space-6);
  background: var(--surface-mineral);
  border-top: 2px solid var(--text-primary);
  color: inherit;
  text-decoration: none;
}

.promo__label {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-chip);
  letter-spacing: var(--ls-chip); text-transform: uppercase;
  color: var(--text-muted);
}

/* The words sit between the label and the call to action rather than
   under the label, so a tall card has no hole in the middle of it. A
   promotion carrying a picture pins them to the top instead and lets
   the picture take the slack. */
.promo__text { display: block; margin-block: auto; }
.promo:has(.promo__media) .promo__text { margin-block: 0; }

.promo__eyebrow {
  display: block;
  margin-top: var(--space-5);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase;
  color: var(--text-secondary);
}

.promo__title {
  margin: var(--space-3) 0 0;
  font-family: var(--font-sans-display); font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--text-primary);
}

.promo__body {
  margin: var(--space-4) 0 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}

.promo__media {
  margin-top: var(--space-5);
  overflow: hidden;
  min-height: 0;
  flex: 1 1 auto;
}
.promo__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.promo__cta {
  margin-top: auto;
  padding-top: var(--space-5);
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-button);
  letter-spacing: var(--ls-button); text-transform: uppercase;
  color: var(--text-primary);
}
.promo__cta .arw {
  transition: transform var(--duration-base) var(--ease-architectural);
}
.promo:hover .promo__cta .arw { transform: translateX(var(--space-2)); }

/* Our own promotions take the editorial ground and the signal mark;
   a paid placement stays neutral, so the two are never confused. */
.promo--edit { background: var(--surface-white); border-top-color: var(--status-edit); }

/* Below the three-column breakpoint there is no leftover column to
   sit in, and a promotion placed as an ordinary cell drifts away
   from the house it was meant to accompany and leaves a hole beside
   it. It takes the full width instead and reads as a band between
   rows of houses — which is unmistakably not a house, and leaves
   nothing dangling. */
@media (max-width: 1099px) {
  .promo {
    grid-column: 1 / -1;
    border-top-width: 1px;
  }
  .promo__text { margin-block: 0; }
}

/* ---------------------------------------------------------
   THE INDEX HERO

   The home page says what the site is and hands over to the
   houses. It is a band, not a full screen: a full-screen
   statement here would put the catalogue below the fold,
   and the catalogue is the point.
   --------------------------------------------------------- */

.hero--index {
  position: relative;
  isolation: isolate;
  min-height: min(78vh, 820px);
  display: grid;
  align-items: end;
  /* The ground behind the photograph, and what shows while it loads
     or if the catalogue has none. A gradient is a better empty state
     than a grey rectangle. */
  background: var(--gradient-navy-depth);
  color: var(--text-inverse);
  padding-block: var(--space-8) var(--space-9);
  overflow: hidden;
}

/* The stage holds the photographs. Two layers are kept so one can
   fade in over the other — swapping a single src produces a blink,
   and a blink in a hero reads as a fault rather than a transition. */
.hero__stage { position: absolute; inset: 0; z-index: -2; }

.hero__stage img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration-section) var(--ease-reveal);
}
.hero__stage img.is-on { opacity: 1; }

.hero--index .hero__veil {
  position: absolute; inset: 0; z-index: -1;
  background: var(--gradient-image);
}

.hero--index .eyebrow { color: var(--text-inverse); }
.hero--index .eyebrow::before { background: var(--gold-500); }

.hero__title--index {
  margin: var(--space-5) 0 0;
  max-width: 20ch;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.5rem, 1.4rem + 3.4vw, 4rem);
  line-height: 1.04;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--text-inverse);
}
.hero__title--index .em { color: var(--gold-500); }

.hero__body {
  margin: var(--space-6) 0 0;
  max-width: var(--measure);
  font-family: var(--font-sans);
  font-size: var(--fs-body-l);
  line-height: var(--lh-body);
  color: var(--text-inverse);
}
.hero--index .btn-row { margin-top: var(--space-7); }

/* The credit: the house and where it is, naming the photograph.

   TEKTON, because this is the site writing in the margin of someone
   else's picture — the same editorial-intervention role the
   annotation and the band caption have. It is a caption, not a
   second headline, and it must never compete with the house it
   names.

   AND IT CARRIES ITS OWN PLATE. The wash is darkest at the upper
   left and clears toward the lower right, which is where this sits:
   measured, the gradient leaves only 11% cover here — 1.41:1 over a
   pale stone elevation, which is no contrast at all. Everything on
   the left of the photograph is covered by the wash and needs
   nothing; this corner is deliberately clear, so the one thing
   standing in it brings its own. */
.hero__credit {
  position: absolute;
  right: var(--margin-fluid);
  bottom: var(--space-6);
  z-index: 1;
  max-width: 40ch;
  text-align: right;
  padding: var(--space-2) var(--space-4);
  background: var(--scrim);
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  line-height: 1.3;
  color: var(--text-inverse);
  text-decoration: none;
  transition: background var(--duration-fast) var(--ease-architectural);
}
.hero__credit:hover { background: var(--surface-deep); }
.hero__credit:empty { display: none; }

@media (max-width: 720px) {
  .hero--index { min-height: min(72vh, 620px); }
  /* On a phone the credit would overlap the call to action, so it
     sits beneath the hero's type rather than on top of it. It keeps
     its plate — the wash is at its lightest down there whatever the
     screen size. */
  .hero__credit {
    position: static;
    display: block;
    text-align: left;
    margin-top: var(--space-5);
    padding-inline: var(--margin-fluid);
  }
}


/* ---------------------------------------------------------
   THE REGIONS STRIP

   The mark used as structure, once per page: extension
   lines, a dimension line, ticks at the ends. It is the one
   place the geometry of the logo is restated at width.

   Two things changed when it moved off the photograph.

   It is a row of LINKS now, not a label. It was a list of
   the places the catalogue covers, which is a fact nobody
   needed stated — as five ways into the catalogue it earns
   its place on the page.

   And the rule is drawn only as wide as the words. It ran
   the full width of the page under a line of text half that
   long, which is not what a dimension does: a drawing
   dimensions the thing it is measuring. `width: fit-content`
   on the inner block makes the rule follow the words at
   every screen size, which is also why the ticks can sit at
   its ends and mean something.
   --------------------------------------------------------- */

.regions {
  background: var(--surface-light);
  padding-block: var(--space-7) var(--space-5);
}

/* fit-content is doing the real work here: everything inside is
   measured by the list, so the rule can never outrun its label. */
.regions__inner {
  position: relative;
  display: inline-block;
  width: fit-content;
  max-width: 100%;
  padding-top: var(--space-5);
}

.regions__rule {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  background: var(--line-light);
}

.regions__end {
  position: absolute;
  top: calc(-1 * var(--space-2));
  width: 1px; height: var(--space-4);
  background: var(--line-accent);
}
.regions__end--start { left: 0; }
.regions__end--finish { right: 0; }

.regions__list {
  display: flex; flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  margin: 0; padding: 0; list-style: none;
}

.regions__list a {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  text-transform: uppercase;
  color: var(--text-secondary);
  text-decoration: none;
  padding-block: var(--space-2);
  border-bottom: 1px solid transparent;
  transition: color var(--duration-fast) var(--ease-architectural),
              border-color var(--duration-fast) var(--ease-architectural);
}
.regions__list a:hover {
  color: var(--text-primary);
  border-bottom-color: var(--line-accent);
}

/* ---------------------------------------------------------
   SHAPES FOR THE EDITORIAL BANDS

   Three of them, deliberately different, because the home
   page's problem was never the colours — it was that every
   band was the same two-column split and the page read flat
   however it was painted.
   --------------------------------------------------------- */

/* A statement at scale, with the argument in two columns under it. */
.split {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-6) var(--space-8);
  margin-top: var(--space-7);
}
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }

.dek {
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-sans);
  font-size: var(--fs-body-l);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.section--dark .dek, .section--deep .dek { color: var(--text-inverse-muted); }

/* Figures, not paragraphs. */
.facts {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--space-7) var(--space-6);
  margin-top: var(--space-7);
}
@media (max-width: 860px) { .facts { grid-template-columns: 1fr; } }

.facts__item {
  padding-top: var(--space-5);
  border-top: 1px solid var(--line-light);
}
.section--dark .facts__item, .section--deep .facts__item {
  border-top-color: var(--line-dark);
}
.facts__figure {
  display: block;
  font-family: var(--font-display); font-weight: 400;
  font-size: var(--fs-h3);
  letter-spacing: var(--ls-title); text-transform: uppercase;
  color: var(--text-primary);
}
.section--dark .facts__figure, .section--deep .facts__figure {
  color: var(--text-inverse);
}
.facts__label {
  display: block; margin-top: var(--space-3);
  max-width: 34ch;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.section--dark .facts__label, .section--deep .facts__label {
  color: var(--text-inverse-muted);
}

/* The argument beside a photograph. */
.with-figure {
  display: grid; grid-template-columns: 1fr 1.15fr;
  gap: var(--space-8);
  align-items: center;
}
@media (max-width: 960px) {
  .with-figure { grid-template-columns: 1fr; gap: var(--space-6); }
}
/* The ratio belongs to the PHOTOGRAPH, not to the figure. With it on
   the figure — plus overflow:hidden — the figcaption was inside the
   ratio box and simply clipped away, so every one of these
   photographs was silently uncredited. */
.with-figure__media { margin: 0; }
.with-figure__media img {
  width: 100%; display: block;
  aspect-ratio: 4 / 3; object-fit: cover;
  background: var(--surface-mineral);
}
.with-figure__media .caption { margin-top: var(--space-3); }

/* A full-bleed photograph used as a section in its own right. */
.band {
  position: relative;
  min-height: clamp(360px, 48vh, 620px);
  background: var(--surface-mineral);
  overflow: hidden;
}
.band img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* A CORNER WASH, not a full one. The band carries a marker and a
   line of text in its lower left and nothing anywhere else, so the
   cover starts in that corner and is gone by the middle of the
   picture. The diagonal wash used here before shaded the whole
   photograph to protect two lines of type in one corner of it. */
.band__veil {
  position: absolute; inset: 0;
  background: var(--gradient-corner);
}
.band__inner {
  position: relative;
  display: flex; flex-direction: column; justify-content: flex-end;
  min-height: inherit;
  padding-block: var(--space-7);
  color: var(--text-inverse);
}
/* The marker over the caption. It inherited --text-muted, which is a
   grey chosen to sit quietly on PAPER and all but disappears on a
   photograph. On the band it is the brightest ink the system has, and
   its tick is gold. */
.band .eyebrow {
  color: var(--text-inverse);
  text-shadow: 0 1px 18px rgba(7, 23, 31, .7);
}
.band .eyebrow::before { background: var(--gold-500); }

.band__caption {
  margin: var(--space-3) 0 0;
  font-family: var(--font-annotation);
  font-size: var(--fs-annotation);
  line-height: 1.35;
  color: var(--text-inverse);
  text-shadow: 0 1px 24px rgba(7, 23, 31, .7);
}

/* The closing statement. Centred, once, at the end of a page. */
.statement {
  background: var(--gradient-navy-depth);
  color: var(--text-inverse);
  padding-block: var(--space-9);
  text-align: center;
}
.statement .eyebrow { color: var(--gold-500); justify-content: center; }
.statement .eyebrow::before { display: none; }
.statement__text {
  margin: var(--space-5) auto 0;
  max-width: 24ch;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.25rem, 1.2rem + 3vw, 4rem);
  line-height: 1.05;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
.statement__sub {
  margin: var(--space-5) auto 0;
  max-width: 54ch;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-inverse-muted);
}
.statement .btn-row { margin-top: var(--space-7); justify-content: center; }


/* ---------------------------------------------------------
   A PAGE HEADER THAT IS NOT A HERO

   Catalogue, brokers, about, submit and the Edit each open
   with a title and a line, on navy, under the masthead. Not
   a photograph — these are index pages, and a full-screen
   photograph on each one would make them all look like the
   same page.
   --------------------------------------------------------- */

.pagehead {
  background: var(--surface-dark);
  color: var(--text-inverse);
  padding-block: var(--space-8);
}
.pagehead .eyebrow { color: var(--gold-500); }
.pagehead .eyebrow::before { background: var(--gold-500); }
.pagehead__title {
  margin: var(--space-5) 0 0;
  max-width: 24ch;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.25rem, 1.3rem + 2.6vw, 3.5rem);
  line-height: 1.06;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: var(--text-inverse);
}
.pagehead__sub {
  margin: var(--space-5) 0 0;
  max-width: var(--measure);
  font-family: var(--font-sans);
  font-size: var(--fs-body-l);
  line-height: var(--lh-body);
  color: var(--text-inverse-muted);
}


/* ---------------------------------------------------------
   FILTERS

   Chips, not a select menu, so the available choices are
   visible rather than hidden behind a click — and so the
   state of the catalogue is legible at a glance.
   --------------------------------------------------------- */

.filters {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}
.filters__label {
  margin-right: var(--space-3);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase;
  color: var(--text-secondary);
}
.filter {
  appearance: none;
  display: inline-flex; align-items: center;
  height: var(--control-h);
  padding-inline: var(--space-4);
  border: 1px solid var(--line-control);
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-button);
  letter-spacing: var(--ls-button); text-transform: uppercase;
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-architectural),
              color var(--duration-base) var(--ease-architectural),
              border-color var(--duration-base) var(--ease-architectural);
}
.filter:hover { border-color: var(--text-primary); }
.filter[aria-pressed="true"] {
  background: var(--surface-dark);
  border-color: var(--surface-dark);
  color: var(--text-inverse);
}
.filters__count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-secondary);
}
.empty {
  margin-top: var(--space-7);
  padding: var(--space-7);
  border: 1px solid var(--line-light);
  font-family: var(--font-sans);
  font-size: var(--fs-body-l);
  color: var(--text-secondary);
}


/* ---------------------------------------------------------
   THE BROKERS LIST

   A row per firm, not a card — a brokerage is a credit, and
   when it was a card it competed with the houses. The
   editor's note on the earlier version was exact: the broker
   name was too big and took away from the site.
   --------------------------------------------------------- */

.brokers { margin-top: var(--space-6); border-top: 1px solid var(--line-light); }
/* The ROW is a container, not a grid. It used to carry the columns
   itself, and when the whole row became a link the link landed in
   the first column — a third of the width — with the name wrapping
   to four lines and two thirds of the row empty. The columns belong
   to whichever element spans the row. */
.broker {
  padding-block: var(--space-6);
  border-bottom: 1px solid var(--line-light);
}
.broker__name {
  margin: 0;
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-body-l);
  line-height: 1.3;
  color: var(--text-primary);
}
.broker__where {
  display: block; margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-secondary);
}
.broker__note {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.broker__count {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-secondary);
  white-space: nowrap;
}


/* ---------------------------------------------------------
   PROSE

   About and the Edit carry real paragraphs. They get one
   measure and one rhythm, and they never run full width —
   the earlier version's complaint about "reams of text" was
   partly a line-length problem.
   --------------------------------------------------------- */

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: var(--space-5); }
.prose p {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body-l);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.prose h2 {
  margin: var(--space-8) 0 0;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--text-primary);
}
.prose h2:first-child { margin-top: 0; }
.section--dark .prose p, .section--deep .prose p { color: var(--text-inverse-muted); }
.section--dark .prose h2, .section--deep .prose h2 { color: var(--text-inverse); }

.lede {
  max-width: 34ch;
  font-family: var(--font-display); font-weight: 500;
  font-size: var(--fs-h2);
  line-height: 1.2;
  color: var(--text-primary);
}
.section--dark .lede, .section--deep .lede { color: var(--text-inverse); }

/* A numbered sequence — how a submission is handled, what the
   Edit involves. Mono numerals, because they are a sequence and
   not a heading. */
.steps { margin-top: var(--space-7); counter-reset: step; }
.steps__item {
  position: relative;
  padding: var(--space-5) 0 var(--space-5) var(--space-8);
  border-top: 1px solid var(--line-light);
  counter-increment: step;
}
.steps__item:last-child { border-bottom: 1px solid var(--line-light); }
.steps__item::before {
  content: counter(step, decimal-leading-zero);
  position: absolute; left: 0; top: var(--space-5);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--gold-ink);
}
.steps__head {
  margin: 0;
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-body-l);
  color: var(--text-primary);
}
.steps__body {
  margin: var(--space-3) 0 0;
  max-width: var(--measure);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}


/* ---------------------------------------------------------
   THE SUBMISSION FORM

   One control height across the site, which is the token's
   whole reason for existing. Labels above, never inside —
   a placeholder used as a label disappears the moment
   someone starts typing, exactly when they need it.
   --------------------------------------------------------- */

.form { margin-top: var(--space-7); max-width: 720px; }
.form__row { display: grid; gap: var(--space-5); }
@media (min-width: 720px) { .form__row--two { grid-template-columns: 1fr 1fr; } }
.form__row + .form__row { margin-top: var(--space-5); }

.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field > label {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase;
  color: var(--text-secondary);
}
.field input,
.field textarea,
.field select {
  width: 100%;
  min-height: var(--control-h);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-control);
  border-radius: 0;
  background: var(--surface-white);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
.field textarea { min-height: calc(var(--control-h) * 3); resize: vertical; }
.field input:focus-visible,
.field textarea:focus-visible,
.field select:focus-visible {
  outline: 2px solid var(--text-primary); outline-offset: 2px;
  border-color: var(--text-primary);
}
.field__hint {
  font-family: var(--font-sans);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--text-muted);
}
.form .btn-row { margin-top: var(--space-7); }


/* ---------------------------------------------------------
   THE MASTHEAD STAYS

   This was a reported fault and it is worth a note. The
   masthead is fixed, the section rail is sticky, and the two
   are not the same mechanism: an earlier version made the
   masthead static so the rail could sit at the top, and the
   masthead then scrolled away. The rail's offset is derived
   from --nav-h so the two can never disagree.
   --------------------------------------------------------- */

main { display: block; }

/* The masthead is sticky, not fixed, so nothing below it needs to
   reserve space for it. This was asserted the other way round once
   and cost a masthead's worth of empty navy on every page. */
.nav + main > :first-child { margin-top: 0; }


/* ---------------------------------------------------------
   FORM FEEDBACK

   One line under a form, hidden until there is something to
   say. It is a live region so a screen reader hears the
   result; a visual-only confirmation means a blind user
   cannot tell whether the form went anywhere.
   --------------------------------------------------------- */

.form-status {
  margin-top: var(--space-5);
  font-family: var(--font-sans);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--text-secondary);
}
.form-status.is-ok  { color: var(--status-available-ink); }
.form-status.is-err { color: var(--status-sold); }
.section--dark .form-status,
.section--deep .form-status { color: var(--text-inverse-muted); }


/* ---------------------------------------------------------
   ANCHORS LAND BELOW THE MASTHEAD

   Every in-page link on the listing page — the whole section
   rail — would otherwise scroll its heading to y=0, which is
   underneath the sticky masthead and, on the listing page,
   underneath the sticky rail as well. The section would look
   as though it had been skipped.
   --------------------------------------------------------- */

[id] { scroll-margin-top: calc(var(--nav-h) + var(--space-5)); }

/* On a page carrying the rail as well, clear both. */
.railnav ~ * [id],
#listing-root [id] {
  scroll-margin-top: calc(var(--nav-h) * 2 + var(--space-5));
}


/* ---------------------------------------------------------
   THE SAME COMPONENTS, ON A DARK BAND

   Each of these is painted with the primary ink, and each of
   them would therefore be navy on navy the first time it is
   placed on an inverse section. That is the exact failure
   that made an h1 invisible, one component down, and it is
   the kind of thing nobody finds until the day the section
   changes colour.
   --------------------------------------------------------- */

.section--dark .railnav__link,
.section--deep .railnav__link,
.section--dark .broker__name,
.section--deep .broker__name,
.section--dark .steps__head,
.section--deep .steps__head { color: var(--text-inverse); }

.section--dark .broker__note,
.section--deep .broker__note,
.section--dark .broker__where,
.section--deep .broker__where,
.section--dark .steps__body,
.section--deep .steps__body,
.section--dark .broker__count,
.section--deep .broker__count { color: var(--text-inverse-muted); }

.section--dark .broker,
.section--deep .broker,
.section--dark .brokers,
.section--deep .brokers,
.section--dark .steps__item,
.section--deep .steps__item { border-color: var(--line-dark); }

/* A chip on navy keeps its own light card ground rather than
   inverting — it is a card in miniature, and inverting it would
   make it disappear into the band. */
.section--dark .rail__chip,
.section--deep .rail__chip {
  background: var(--surface-white);
  color: var(--text-primary);
  border-color: var(--line-light);
}

/* Form fields keep their white ground on a dark band; what has to
   change is the label above them. */
.section--dark .field > label,
.section--deep .field > label { color: var(--text-inverse-muted); }
.section--dark .field__hint,
.section--deep .field__hint { color: var(--text-inverse-muted); }


/* ---------------------------------------------------------
   ARCHITECTS

   Two shapes, because an architect appears on this site in
   two quite different states.

   A PROFILE CARD, for the ones who have been written up: a
   photograph, the name, where and when, and a paragraph.

   A ROW IN A LIST, for everyone else — the architects named
   on houses in the catalogue but not yet written about. Most
   of them, most of the time. They are not second-class; they
   are simply a different thing, and dressing an unwritten
   name up as a profile card with an empty paragraph under it
   would be worse than listing it plainly.
   --------------------------------------------------------- */

.architects {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-7) var(--space-6);
  margin-top: var(--space-6);
  align-items: stretch;
}
@media (max-width: 1100px) { .architects { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 680px)  { .architects { grid-template-columns: 1fr; } }

/* The card itself. It had no rule of its own at all — every part of
   it was styled and the container was not, which the class-coverage
   check caught. */
.architect { position: relative; }

.architect__link {
  display: flex; flex-direction: column;
  height: 100%;
  color: inherit;
}

/* A portrait where there is one, a building where there is not —
   either way the same depth, so a row of profiles lines up. */
.architect__media {
  overflow: hidden;
  background: var(--surface-mineral);
  height: clamp(200px, 16vw, 280px);
}
.architect__media img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform var(--duration-slow) var(--ease-architectural);
}
.architect__link:hover .architect__media img { transform: scale(1.03); }
@media (prefers-reduced-motion: reduce) {
  .architect__media img { transition: none; }
  .architect__link:hover .architect__media img { transform: none; }
}

.architect__body {
  display: flex; flex-direction: column;
  gap: var(--space-3);
  padding-top: var(--space-5);
  flex: 1 1 auto;
}

.architect__name {
  margin: 0;
  font-family: var(--font-sans-display); font-weight: 500;
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--text-primary);
}

/* Place and years, in mono — they are data, and a reader scanning a
   row of profiles for a period or a country is scanning a column. */
.architect__meta {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-secondary);
}

.architect__intro {
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}

.architect__foot {
  margin-top: auto;
  padding-top: var(--space-5);
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-button);
  letter-spacing: var(--ls-button); text-transform: uppercase;
  color: var(--text-primary);
}
.architect__foot .arw {
  transition: transform var(--duration-base) var(--ease-architectural);
}
.architect__link:hover .architect__foot .arw { transform: translateX(var(--space-2)); }


/* ---------------------------------------------------------
   A LIST OF NAMES

   Used for every architect with houses in the catalogue. The
   count on the right is the useful column — it is what tells
   someone whether following the name is worth it — so it is
   set in mono and aligned, to be read down rather than
   across.
   --------------------------------------------------------- */

.namelist {
  margin: var(--space-7) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line-light);
}
.namelist__row { border-bottom: 1px solid var(--line-light); }

.namelist__link {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 2fr) auto;
  gap: var(--space-4) var(--space-6);
  align-items: baseline;
  padding-block: var(--space-5);
  color: inherit;
  transition: background var(--duration-fast) var(--ease-architectural);
}
@media (max-width: 720px) {
  .namelist__link { grid-template-columns: 1fr; gap: var(--space-2); }
}

.namelist__name {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-body-l);
  color: var(--text-primary);
}
.namelist__meta {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-secondary);
}
.namelist__count {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-primary);
  white-space: nowrap;
}
.namelist__count .arw {
  display: inline-block;
  transition: transform var(--duration-base) var(--ease-architectural);
}
.namelist__link:hover .namelist__count .arw { transform: translateX(var(--space-2)); }


/* ---------------------------------------------------------
   SELECTED WORKS

   Buildings that are not for sale and never will be. This is
   the part that makes a profile a reference rather than an
   advertisement, so it is set as a schedule — name, place and
   year, a note where there is one — and not as cards.
   --------------------------------------------------------- */

.works {
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--text-primary);
}
.works__row {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr) minmax(0, 4fr);
  gap: var(--space-3) var(--space-6);
  align-items: baseline;
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--line-light);
}
@media (max-width: 860px) {
  .works__row { grid-template-columns: 1fr; gap: var(--space-2); }
}
.works__name {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.works__where {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-secondary);
}
.works__note {
  font-family: var(--font-sans);
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--text-secondary);
}

/* A credit on a full-bleed photograph, in the corner the wash
   clears — so it carries its own plate, like the hero credit. */
.band__credit {
  position: absolute;
  right: var(--margin-fluid);
  bottom: var(--space-5);
  padding: var(--space-2) var(--space-4);
  background: var(--scrim);
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  color: var(--text-inverse);
}


/* ---------------------------------------------------------
   THE ARCHITECT COMPONENTS ON A DARK BAND

   Each of these is painted with the primary ink, so each of
   them would be navy on navy the first time it is placed on
   an inverse section — the same failure that made an h1
   invisible, three components down. They are on light
   grounds today; this is what stops that being load-bearing.
   --------------------------------------------------------- */

.section--dark .architect__name,
.section--deep .architect__name,
.section--dark .architect__foot,
.section--deep .architect__foot,
.section--dark .namelist__name,
.section--deep .namelist__name,
.section--dark .namelist__count,
.section--deep .namelist__count,
.section--dark .works__name,
.section--deep .works__name { color: var(--text-inverse); }

.section--dark .architect__meta,
.section--deep .architect__meta,
.section--dark .architect__intro,
.section--deep .architect__intro,
.section--dark .namelist__meta,
.section--deep .namelist__meta,
.section--dark .works__where,
.section--deep .works__where,
.section--dark .works__note,
.section--deep .works__note { color: var(--text-inverse-muted); }

.section--dark .namelist,
.section--deep .namelist,
.section--dark .namelist__row,
.section--deep .namelist__row,
.section--dark .works__row,
.section--deep .works__row { border-color: var(--line-dark); }

.section--dark .works,
.section--deep .works { border-top-color: var(--text-inverse); }


/* =========================================================
   ══ THE BROKER STOREFRONT ══

   "Listed Architecture owns the structure. The brokerage
   owns the atmosphere."

   The brokerage's colour arrives as --broker-primary, set
   inline on the elements that may use it, with
   --broker-ink alongside it — the ink computed server-side
   from that colour, because a brand yellow with our inverse
   ink on it is unreadable and nobody pasting a hex is
   thinking about that.

   WHERE THE COLOUR MAY GO: the hero ground, rules, hover
   states, selected backgrounds, one accent mark.

   WHERE IT MAY NOT: body copy, the masthead, the property
   cards, any control a reader uses to navigate OUR site.
   Those are Listed Architecture's, and a visitor has to be
   able to tell whose site they are on.

   Every rule below falls back to the system when a
   brokerage has no theme, so an unthemed storefront is a
   Listed Architecture page rather than a broken one.
   ========================================================= */


/* ---------------------------------------------------------
   THE FRAME

   Directly under our masthead, above the brokerage's own
   hero. It is never conditional and never subtle enough to
   miss.

   This is a trust requirement before it is a design one.
   Someone who believes they are on the brokerage's own site
   may enquire thinking they are talking to them; and the
   offer we are making — we built this for you — only reads
   if it is obvious that we built it.
   --------------------------------------------------------- */

.bframe {
  background: var(--surface-deep);
  color: var(--text-inverse-muted);
  border-bottom: 1px solid var(--line-dark);
}
.bframe__inner {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: var(--space-4);
  padding-block: var(--space-3);
}
.bframe__label,
.bframe__mark {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-chip);
  letter-spacing: var(--ls-chip);
  text-transform: uppercase;
}
.bframe__mark { color: var(--gold-500); }


/* ---------------------------------------------------------
   THE BRANDED HERO
   --------------------------------------------------------- */

/* The height comes from the PHOTOGRAPH, not from the element. A
   brokerage with no imagery yet was getting 62vh of empty navy with
   its name at the bottom — the same screen-of-nothing the home page
   hero was fixed for, and on a page whose whole job is to look worth
   claiming it is the worst possible first impression.

   With a photograph it is a hero. Without one it is a masthead, and
   sized by what is in it. */
.bhero {
  position: relative;
  isolation: isolate;
  display: grid; align-items: end;
  padding-block: var(--space-8);
  /* The system ground, until a brokerage supplies one. */
  background: var(--surface-dark);
  color: var(--text-inverse);
  overflow: hidden;
}

/* Their colour, when there is one. --broker-ink is computed from it
   server-side, so this can never be white-on-yellow. */
.bhero--themed {
  background: var(--broker-primary);
  color: var(--broker-ink);
}

.bhero--shot { min-height: min(62vh, 640px); }

.bhero__img {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover;
}

/* With a photograph behind it the ground becomes a wash OF their
   colour rather than a flat field of it — their brand still reads,
   and the photograph is still a photograph. */
.bhero__veil {
  position: absolute; inset: 0; z-index: -1;
  background: var(--gradient-corner);
}
.bhero--themed .bhero__veil {
  background:
    linear-gradient(to top right,
      color-mix(in srgb, var(--broker-primary) 92%, transparent) 0%,
      color-mix(in srgb, var(--broker-primary) 64%, transparent) 26%,
      color-mix(in srgb, var(--broker-primary) 22%, transparent) 52%,
      transparent 72%);
}

.bhero__inner { position: relative; }

/* THEIR LOGO IS THE MASTHEAD OF THIS PAGE. It leads, because the
   page is supposed to feel like theirs — within our frame, which is
   sitting directly above it. */
.bhero__logo {
  display: block;
  max-width: min(340px, 62vw);
  max-height: 96px;
  width: auto; height: auto;
  margin-bottom: var(--space-6);
}

.bhero__name {
  margin: 0;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.25rem, 1.3rem + 2.6vw, 3.5rem);
  line-height: 1.06;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  color: inherit;
}

.bhero__meta {
  display: flex; flex-wrap: wrap;
  gap: var(--space-3) var(--space-4);
  margin: var(--space-5) 0 0;
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: inherit;
  opacity: .9;
}
/* Separators, so three facts set in one face at one size do not read
   as a single run-on string. */
.bhero__meta > span + span::before {
  content: '·';
  margin-right: var(--space-4);
  opacity: .55;
}

.bhero__links {
  display: flex; flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  margin: var(--space-6) 0 0;
}
.bhero__links a {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-button);
  letter-spacing: var(--ls-button); text-transform: uppercase;
  color: inherit;
  text-decoration: none;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid currentColor;
  opacity: .75;
  transition: opacity var(--duration-fast) var(--ease-architectural);
}
.bhero__links a:hover { opacity: 1; }


/* ---------------------------------------------------------
   WHERE THEY ARE, AND WHERE THEY WORK

   Routinely different — a Brussels firm covering the
   Benelux — so the page says both rather than inferring one
   from the other.
   --------------------------------------------------------- */

.bwhere {
  margin: var(--space-6) 0 0;
  display: grid; gap: var(--space-4);
}
.bwhere dt {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--ls-eyebrow); text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}
.bwhere dd {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: var(--text-primary);
}


/* ---------------------------------------------------------
   WHAT THEY ARE KNOWN FOR

   The answer to "what would make someone recognise a
   property as belonging in this brokerage's portfolio" —
   which is the thing that gives a profile editorial value
   rather than being a directory entry with a logo on it.
   --------------------------------------------------------- */

.btags {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin: var(--space-5) 0 0; padding: 0; list-style: none;
}
.btags li {
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--broker-secondary, var(--line-light));
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-chip);
  letter-spacing: var(--ls-chip); text-transform: uppercase;
  color: var(--text-secondary);
}


/* ---------------------------------------------------------
   EXPLORE THEIR WORLD

   Deliberately not buried in a footer. Sending a reader to
   the brokerage is the point of the page, not a leak from
   it — a brokerage that sees traffic arrive from here is a
   brokerage that claims its profile.
   --------------------------------------------------------- */

.bworld {
  margin-top: var(--space-7);
  border-top: 1px solid var(--line-dark);
}
.bworld__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto;
  gap: var(--space-4) var(--space-6);
  align-items: baseline;
  padding-block: var(--space-5);
  border-bottom: 1px solid var(--line-dark);
  color: var(--text-inverse);
  text-decoration: none;
}
@media (max-width: 720px) {
  .bworld__link { grid-template-columns: 1fr; gap: var(--space-2); }
}
.bworld__what {
  font-family: var(--font-sans); font-weight: 600;
  font-size: var(--fs-body-l);
}
.bworld__where {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  color: var(--text-inverse-muted);
}
.bworld__link .arw {
  transition: transform var(--duration-base) var(--ease-architectural);
}
.bworld__link:hover .arw { transform: translateX(var(--space-2)); }

/* On a themed section the hover takes their colour. This is a HOVER
   STATE, which is explicitly where a brokerage colour belongs. */
.bsection--themed .bworld__link:hover { color: var(--broker-primary); }
.bsection--themed .bworld__link:hover .bworld__where { color: var(--broker-primary); }


/* ---------------------------------------------------------
   THE DIRECTORY ROW

   It links to the storefront now rather than to a filtered
   catalogue. The weight is unchanged: a brokerage is a
   credit, and when these were cards they competed with the
   architecture.
   --------------------------------------------------------- */

.broker__link {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto;
  gap: var(--space-5) var(--space-6);
  align-items: baseline;
  color: inherit;
}
@media (max-width: 860px) {
  .broker__link { grid-template-columns: 1fr; gap: var(--space-3); }
}
.broker__id { display: block; }
.broker__link .broker__name { display: block; }
.broker__link .arw {
  display: inline-block;
  transition: transform var(--duration-base) var(--ease-architectural);
}
.broker__link:hover .arw { transform: translateX(var(--space-2)); }

/* The storefront's structured facts on a dark band. */
.section--dark .bwhere dd,
.section--deep .bwhere dd { color: var(--text-inverse); }
.section--dark .bwhere dt,
.section--deep .bwhere dt { color: var(--text-inverse-muted); }

/* =========================================================
   ORIGINAL DRAWINGS
   ---------------------------------------------------------
   A PLAN IS NOT A PHOTOGRAPH, and the gallery patterns above
   would ruin one. They use object-fit: cover, because a
   photograph of a house survives being cropped to a shape —
   the subject is in the middle and the edges are context.

   A plan is the opposite. The edges ARE the drawing: the
   title block, the scale bar, the north point, the sheet
   number, the pencil note in the margin. Crop a section
   drawing to 16/9 and you have removed the part that proves
   it is a section drawing.

   So these are CONTAINED, never cropped, on a light ground
   because that is what they were drawn on — a blueprint or a
   dyeline on our navy reads as a photograph of a piece of
   paper lying in a dark room. Each sits in its own frame at
   whatever proportion it actually is, and the grid lets them
   differ, because a long section and a square plan are not
   the same shape and pretending otherwise is how a sheet
   gets trimmed.

   The caption carries the credit. These come from archives
   and estates, and naming the holder is both ordinary
   practice and the single thing most likely to turn a
   complaint into a correction.
   ========================================================= */

.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: var(--gutter);
  margin-top: var(--space-7);
  align-items: start;
}

.plan {
  background: var(--surface-white);
  /* --line-light, not --rule-soft. The latter is the ADMIN's name for
     this and does not exist out here; an undefined var() invalidates
     the whole declaration, so the frame would simply not have been
     drawn. The admin stylesheet is now audited for the same mistake. */
  border: var(--border-hairline) solid var(--line-light);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Contained, and given room to be tall or wide as it happens to be.
   max-height stops one enormous sheet from pushing everything else
   off the screen; it is a ceiling, not a shape. */
.plan__sheet {
  display: block;
  width: 100%;
  height: auto;
  max-height: 70vh;
  object-fit: contain;
  background: var(--surface-light);
}

.plan__caption {
  font-family: var(--font-annotation);
  font-size: var(--fs-meta);
  line-height: 1.45;
  color: var(--text-secondary);
}

/* The holder, set smaller and quieter than the caption — it is a
   matter of record rather than something to read. */
.plan__credit {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ---------- the portrait ----------

   Beside the opening prose rather than across the full width.
   A photograph of a person at 100vw is a magazine cover; this
   is a reference entry, and the face belongs next to the
   sentence that says who they were. */

.who {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
}

@media (min-width: 860px) {
  .who { grid-template-columns: 22rem minmax(0, 1fr); }
}

/* The heading and the prose share a column here, unlike the
   commentary blocks where the heading has one of its own. Without a
   gap the h2 sits straight on top of the first line and reads as a
   label attached to it rather than a heading over a passage. */
.who > div > .h2 { margin-bottom: var(--space-5); }

.who__portrait { margin: 0; }

.who__portrait img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--surface-mineral);
}

.who__credit {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--ls-data);
  text-transform: uppercase;
  color: var(--text-muted);
}
