/* International Executive Rentals — our own stylesheet.
 *
 * This replaces the 528 KB Squarespace stylesheet the clone used to serve. Every rule
 * here keys off a class this project's own templates emit; there is no `sqs-` selector,
 * no `data-*` selector, and no theme flag to fight. Colours and measures are the ones
 * the live site actually renders — its `proxima-nova` never loads, so what visitors see
 * is the Times New Roman fallback, and that is what this file reproduces without
 * downloading a font.
 */

/* ------------------------------------------------------------------ tokens */
:root {
  --ink: #1f2526;
  --ink-soft: rgba(31, 37, 38, 0.75);
  /* 0.55 measured 3.62:1 on white, which fails AA for body-size text. 0.65 is 4.91:1. */
  --ink-faint: rgba(31, 37, 38, 0.65);
  --teal: #2b6173;
  --teal-dark: #234f5e;
  --button: #272727;
  --paper: #ffffff;
  --rule: #d9d9d4;
  --wash: #f4f4f1;

  --measure: 1020px;   /* the live site's #page width */
  --prose-measure: 700px;
  --gap: 24px;

  --serif: "Times New Roman", Times, Georgia, serif;
  --ui: "Times New Roman", Times, serif;
}

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

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

body {
  margin: 0;
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper);
}

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

a { color: var(--ink-soft); text-decoration: none; }
a:hover { color: var(--ink); }

h1, h2, h3, h4 {
  font-weight: 400;
  line-height: 1.2;
  margin: 0 0 0.5em;
  /* These headings are set in capitals, so a long one breaks into two lines with one or two words
     stranded on the second. Balance gives the lines back. */
  text-wrap: balance;
}
h1 { font-size: 30px; text-transform: uppercase; }
h2 { font-size: 26px; text-transform: uppercase; }
h3 { font-size: 20px; text-transform: uppercase; }
h4 { font-size: 18px; }

p, ul, ol { margin: 0 0 1em; }

/* ------------------------------------------------------------------ access */
/* Hidden until focused, then pinned where a keyboard user can see it. */
.skip-link {
  position: absolute;
  left: 8px;
  top: -100px;
  z-index: 200;
  padding: 10px 16px;
  background: var(--ink);
  color: #fff;
  font-size: 16px;
  text-transform: uppercase;
}
.skip-link:focus { top: 8px; color: #fff; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 3px solid var(--teal);
  outline-offset: 2px;
}
.site-header :focus-visible,
.site-footer :focus-visible { outline-color: #fff; }

/* ------------------------------------------------------------------ header */
.site-header {
  background: var(--teal);
  padding: 0 20px;
  /* The dropdown and the mobile menu are positioned against the header box, so it has to be a
     containing block, and it has to sit above the page so a panel can float over the banner. */
  position: relative;
  z-index: 100;
}
.header-inner {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 20px 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
/* The wordmark keeps its 260 px — the size the picture was drawn at — and it borrows the room
   for it from the bar rather than from the menu: the bar's one line is 260 px of wordmark, a
   16 px gap and the menu's widest line, 801 px measured on a page the property family holds,
   its dropdown title bold and its caret drawn — 1,077 px in all, which the bar stops being able to lay out inside
   the 1,020 px measure and so takes the window's full width instead at desktop widths. Shrink
   the wordmark instead and it stops being a wordmark: 1,020 px of measure leaves 203 px for
   it beside any menu that still reads as six words rather than a wall. */
.site-logo img { max-width: 260px; }

/* The brand block is the wordmark alone. It used to carry a line of small capitals under the
   picture naming the family of bedroom listings in the bar — a caption, because at the time the
   four listings stood in the bar themselves and nothing in the menu named what they were. The
   bar names the family itself now: San Francisco Properties stands in the menu as the dropdown
   the listings open under, and a second city's family gets its own dropdown beside it. A caption
   repeating one city's name under the logo on every page would say the doubled thing and name
   the wrong one the day the second family arrives, so the picture stands alone. */
.site-brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* The nav is not a positioning context: the opened menu panel anchors to the header, so
   opening it does not grow this box. The auto margin holds it at the right-hand end of
   whatever row it lands on, signed in or out. */
.site-nav { position: static; margin-left: auto; }

/* The way into the back office, printed only for a reader who is signed in — View.EditURL,
 * internal/site/render.go — and drawn as a button rather than as a link: the same box the Menu
 * toggle draws, filled white, so it reads as the one thing on the teal bar that does something
 * rather than names a page. Its letters are teal-dark on white, close to 9:1, and on hover it is
 * the fill that leaves, so the button keeps the shape it had.
 *
 * It sits in the bar's own flow, at the right-hand end of the wordmark's row: wordmark and menu
 * take the bar's one line, and wordmark, menu and button together sum to 1,261 px — wider than
 * the bar holds at ordinary windows, a 1,280 px one included — so the auto margin keeps the
 * button in the corner beside the wordmark's opposite end and the menu takes the row under
 * them, still on one line; at a window wide enough for all three they simply share the line.
 * The signed-in bar is then the signed-out bar with one button added to a row it already
 * had. Below the desktop breakpoint the corner belongs to the Menu toggle, so the button sits
 * beside the toggle instead, and the toggle never moves. */
.header-edit {
  font-family: var(--ui);
  font-size: 14px;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--teal-dark);
  background: #fff;
  border: 1px solid rgba(255, 255, 255, 0.5);
  padding: 10px 18px;
  margin-left: auto;
}
.header-edit:hover { color: #fff; background: transparent; border-color: #fff; }

.nav-toggle {
  display: none;
  font-family: var(--ui);
  font-size: 14px;
  text-transform: uppercase;
  color: #fff;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.5);
  padding: 10px 18px;
  cursor: pointer;
}

/* The menu is two lists inside one box — the home link and the property families, each printed
   as a dropdown, in the first list, the site's pages and the two ways out in the second — because
   a wrapping flex list sizes itself to a single unbroken line: as one list the menu is too wide
   to sit beside the wordmark at all, and it would wrap wherever a line broke. As two lists the
   box holds one line whole — 260 px of wordmark, a 16 px gap and an 801 px menu, 1,077 px
   across a bar that takes the window's full width at desktop widths — and a window too narrow for that
   line does not break the box across two rows, it folds the box whole behind the Menu toggle,
   where the panel prints it as two lists too. The box is
   what the Menu toggle opens and closes, which is why it is one focusable unit and one
   aria-controls target.

   The 13 px between labels is what keeps that one line affordable: 723 px of
   ink across the six labels, plus the gaps, plus what the bold current marker and each box's
   own edge add, sums to the 801 px above at 13 px — at 16 px it would be 816 px, and the fold
   breakpoint would have to rise 15 px to match, hiding the menu from windows that could have
   shown it. The two kinds of page are divided by
   a hairline only inside the opened panel, where the panel is no longer one flat list but a
   list with families printed open in it; on one desktop line there is no row to divide, and the
   single gap runs through both lists unbroken. */
.nav-rows { display: none; }

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 13px;
  width: max-content;
}

.nav-list a,
.nav-folder summary {
  font-family: var(--ui);
  font-size: 14px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
  background: none;
  padding: 6px 0;
  cursor: pointer;
  transition: color 0.15s ease;
  /* The line-height is spelled, not left normal, so every label's line box is this height
     and no label's box can grow past it on its own — which is what keeps the one line's
     items aligned when the row centers them. (The family title's disclosure mark is drawn
     for the same reason — see the caret's own comment below.) */
  line-height: 1.2;
}
/* The links go inline-block so they carry their padding as a box, the way the family's
   summary already does: an inline label's own box does not stretch past its glyphs — its
   padding hangs off the line, and the line puts the glyphs wherever its baseline decides,
   not where the box's padding top is. That is the whole of a two-pixel mystery: the
   summary's letters begin under its padding top, the inline links' began where the
   baseline happened to land inside a taller line, two pixels lower. As boxes of their
   own, aligned to the top of the line rather than to its baseline — the line's strut
   still reaches a pixel below any box's own descent — both compute the same height,
   the same distance from the top, and their letters land on one line. */
.nav-list a { display: inline-block; vertical-align: top; }
/* The reserved rule this list used to carry under every item is gone, and the menu now marks
   itself the way the page's own category list does: the current page's item is the only bold
   word in the menu and sits at full white, while hover brightens letters alone. Before either
   marker, "current" was 0.85 white against 1.0 white — the same difference hover makes. */
.nav-list a:hover,
.nav-folder summary:hover {
  color: #fff;
}
.nav-list a[aria-current="page"] {
  color: #fff;
  font-weight: 700;
}

/* A property family — San Francisco Properties, and whatever city comes next — sits in the bar
   as a dropdown: its title is the bar's item, and the listings it holds open under it. The
   family, not the listing, is the unit the bar names, so each city keeps its own door and its
   own list. The disclosure is native <details>, so the dropdown works with no JavaScript at
   all — with it, site.js opens it on mouse arrival and closes it on departure — and the
   open panel hangs 12 px below the title, which is the gap site.js's closing delay covers.
   The listing links inside it keep the nav-cat class — the same hook the bar's tests read — and
   the one that names the page being read goes bold white like any other current link.

   Opening a family drops a panel *below* the bar instead of pushing the bar taller: the panel
   leaves the flex row (position: absolute), so the header keeps the exact height it had closed
   and the other menu items stay put. It is a shade darker than the bar so it reads as a menu
   attached to its title, and it floats over the page rather than displacing it. The family that
   holds the page being read is marked the way a current link is — its title goes bold white,
   the one difference the menu makes. */
.nav-folder > details { position: relative; }
.nav-folder > details > summary { list-style: none; }
.nav-folder > details > summary::-webkit-details-marker { display: none; }
/* The disclosure mark is drawn, not typed. A ▾ glyph would have to come from a fallback
   font — Times has none — and a glyph whose box the fallback chooses is a glyph whose box
   stretches the summary's line: measured, the title's letters sat two pixels above the
   neighbours it shares the row with, and the line's height belonged to a character the
   stylesheet never picked. A zero-by-zero inline-block with borders for a caret is the
   same mark with a box the stylesheet *did* pick, and borders paint in currentColor like
   the letters do, so hover and the bold current title take the mark with them. It hangs
   off the middle line, the height a lowercase e's, and when the family opens the two
   solid-pixel borders trade places and it points up. */
.nav-folder > details > summary::after {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 6px;
  vertical-align: middle;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
}
.nav-folder > details[open] > summary::after {
  border-top: 0;
  border-bottom: 5px solid currentColor;
  vertical-align: 2px;
}
.nav-folder.is-active > details > summary {
  color: #fff;
  font-weight: 700;
}
.nav-sublist {
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-sublist a { display: block; padding: 6px 0; }
.nav-folder > details[open] > .nav-sublist {
  position: absolute;
  top: calc(100% + 12px);
  left: 0;
  z-index: 120;
  min-width: 232px;
  margin: 0;
  padding: 10px 18px 12px;
  background: var(--teal-dark);
  box-shadow: 0 14px 30px rgba(18, 22, 23, 0.30);
}
.nav-folder > details[open] > .nav-sublist a { padding: 8px 0; }

@media (min-width: 1117px) {
  /* The breakpoint is arithmetic, not taste, and it is the bar's one-line threshold itself:
     the widest one line is 1,077 px — 260 px of wordmark, a 16 px gap and the 801 px menu,
     its title bold and its caret drawn on a page the family holds — inside a bar padded
     20 px each side, which a window pays for from 1,117 px, its 40 px of padding and the
     line's own fractions included. A window that cannot pay does not get its menu stacked under the wordmark —
     a two-row bar reads as a defect at exactly the widths a reader resizes at — it gets
     the Menu toggle, the fold breakpoint below spelled to this same threshold. From here up
     the bar stops wearing the measure — the page's prose keeps its 1,020 px column, but the
     bar takes the window's whole width, the way the original site's did — and the box lays
     its two lists out side by side. The two lists meet at the same 13 px the items keep
     inside them, so the split reads as a seam in the markup, not a gap in the bar. */
  .header-inner { max-width: none; }
  .nav-rows {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 13px;
  }
}

@media (max-width: 1116px) {
  .nav-toggle { display: block; }

  /* Beside the Menu toggle, not in the corner the toggle has always held. The button's own box
     shrinks before the bar's does, which is what keeps the pair on the wordmark's row down to a
     tablet's width; at a phone's width the pair takes the row below it instead. */
  .header-edit { padding: 8px 14px; }

  /* Inside the panel a row is no longer a row: both lists lay out as plain columns, each
     family printed open — its listings already hanging indented under its title, one tap
     from the toggle. A divider still divides the two lists here, because the panel is no
     longer one flat list to run your eye down: it has a shape, and the hairline is where
     the pages a visitor came for stop and the site's own pages begin. */
  .nav-list { display: block; width: auto; }

  /* The open menu is a panel that hangs *below* the bar, anchored to the header, so tapping
     Menu never stretches the bar down the page. It scrolls on its own if the list is taller
     than the screen, and it is tall enough, before scrolling, to hold the whole menu open:
     one row per page plus the family titles, at tap-target heights, come to a little over
     470 px. The family's dropdown inside it is a plain accordion rather than a second
     floating panel — there is nothing to float above. */
  .nav-rows.is-open {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    position: absolute;
    top: calc(100% + 12px);
    right: 20px;
    z-index: 120;
    width: min(340px, calc(100vw - 40px));
    max-height: min(80vh, 560px);
    overflow-y: auto;
    padding: 10px 20px 18px;
    background: var(--teal-dark);
    box-shadow: 0 16px 34px rgba(18, 22, 23, 0.34);
  }
  .nav-rows.is-open .nav-list + .nav-list {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.18);
  }

  /* Inside the panel, a link's padding is a finger's target, not a cursor's aim: 6 px under a
     14 px line reads as both a cramped list and a small mark to hit. The panel's links take
     11 px above and below — with the line box, a strip about 39 px tall, near the 44 px the
     guidelines reach for and comfortably above what a row of 6 px strips was — and they go
     block, because padding is only a target when it belongs to a box: an inline link's strip
     of paint is the text, and a thumb aiming beside the word hits the panel, not the link.
     The listings inside a family take 9 px rather than 11: they are the row under the title,
     and a step down in air is what says they belong to it. */
  .nav-rows.is-open .nav-list a,
  .nav-rows.is-open .nav-folder summary { display: block; padding: 11px 0; }
  .nav-rows.is-open .nav-folder details[open] .nav-sublist a { padding: 9px 0; }

  /* Inside the panel an opened family stops floating: the panel already floats, and a second
     one inside it would float over its own list. The listings hang under their title with an
     indent and a little air of their own, so it is visible which door each one belongs to —
     which is the whole point of a family once a second city's door stands above this one.
     The title still answers a tap, to fold its list away; site.js is what opens every family
     when the panel is the menu, so no listing ever waits behind a closed title. */
  .nav-folder > details[open] > .nav-sublist {
    position: static;
    min-width: 0;
    margin: 0;
    padding: 2px 0 8px 20px;
    background: none;
    box-shadow: none;
  }
}

/* ------------------------------------------------------------------ flash */
.flash {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 16px 32px;
  font-size: 16px;
}
.flash-ok { background: #eaf3ef; color: #1d4b34; }
.flash-error { background: #f8e9e7; color: #7a1f16; }

/* ------------------------------------------------------------------ gate */
/* The page in front of the one menu item that leaves the site (internal/site/gate.go). It is
 * deliberately short and plain: its whole job is to be read before the click, so nothing on it
 * competes with the address it names. The column is the prose measure, not the 1020px page
 * measure — a departure notice you have to scan across two columns is a notice you skip. */
.gate { max-width: var(--prose-measure); }
.gate h1 { margin-bottom: 20px; }
.gate-lead { font-size: 20px; line-height: 1.55; }
.gate-facts { margin: 26px 0 32px; padding-left: 1.25em; }
.gate-facts li { margin-bottom: 0.55em; }
.gate-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; margin: 0 0 26px; }
.gate-back { color: var(--teal); border-bottom: 1px solid var(--rule); }
.gate-back:hover { color: var(--ink); border-bottom-color: var(--teal); }
/* The full destination, printed. This is the one page on the site where reading exactly where you
 * are going is the point of the page, so it is set as content rather than hidden in a title. */
.gate-url {
  font-size: 15px;
  color: var(--ink-faint);
  overflow-wrap: break-word;
}

/* ------------------------------------------------------------------ banner */
/* Every banner — a property's hero, a page's, and the home page's photographs — spans the
 * viewport edge to edge: the property hero and a page's sit outside the content column in the
 * markup, and a home section stopped carrying a measure so its banners could too. The box is
 * reserved by aspect-ratio, which is what keeps the page from jumping when the file
 * arrives. */
.banner { margin: 0; overflow: hidden; }
.banner-img {
  width: 100%;
  aspect-ratio: 21 / 9;
  min-height: 240px;
  max-height: 560px;
  object-fit: cover;
}
.banner-caption {
  max-width: var(--prose-measure);
  margin: 0 auto;
  padding: 24px 32px 0;
}

/* The property page prints its name and headline over the hero photograph itself. Whatever the
   photograph is, the words go at the bottom left of the picture in white, and what makes them
   readable on a bright sky, a pale façade, or a dark interior is not luck: a gradient wash rises
   from the bottom edge and reaches full darkness exactly where the words stand, so the contrast
   under them never falls below about 5:1 no matter what the image shows. The shadow is a second
   layer of insurance against the brightest possible pixels. */
.property-banner { position: relative; }
/* The desktop floor under the property hero, and the reason it is not a rounder number.

   A phone gets 4:3 (see the 559 px rule below), which at 559 px of window paints a picture
   419.25 px tall. On the far side of that breakpoint the 21:9 band asks for 240 px at 560 px of
   window, and until now the only floor under this hero was the 240 px floor it shares with every
   other banner — so one more pixel of width took 179 px off the picture. It collapsed as the reader
   dragged the window wider, which reads as a defect even though each side of the breakpoint is well
   sized for its own case. 420 px makes that boundary flat instead: the far side of the breakpoint is
   a fraction of a pixel taller than the near side rather than 179 px shorter, so nothing drops.

   The floor only ever binds in the middle of the desktop range. 21:9 out-talls 420 px on its own at
   980 px of window (420 × 21/9), so above that the ratio does the work exactly as before, and the
   560 px ceiling still stops it at 1307 px. Between 560 px and 980 px the hero sits at 420 px, which
   is the height a window that width was already showing one pixel narrower — a narrow desktop window
   keeps a tall picture instead of being handed a strip of one. Measured through the breakpoint, the
   hero now runs 280 px → 4:3 → 420 px → 21:9 → 560 px as the window widens, and never comes back
   down.

   Only the property hero changes. The home and page banners keep the 240 px floor: they carry no
   words of their own, so a shallow band on a narrow window costs them nothing. */
.property-banner .banner-img { min-height: 420px; }
.property-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(17, 20, 21, 0.82) 0%,
    rgba(17, 20, 21, 0.55) 30%,
    rgba(17, 20, 21, 0) 65%);
  pointer-events: none;
}
.banner-overlay {
  /* Above the wash: the pseudo-element is the figure's last child and would otherwise paint
     over the caption's own box. */
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: flex-end;
}
.banner-overlay-inner {
  width: 100%;
  /* The same column the header and the page body are pinned to, so the words begin exactly
     where the prose below begins. */
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 32px 40px;
}
.banner-eyebrow {
  margin: 0 0 10px;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
}
.property-banner .banner-headline {
  margin: 0;
  max-width: 24em;
  color: #fff;
  font-size: 40px;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.5);
}
@media (max-width: 899px) {
  .property-banner .banner-headline { font-size: 32px; }
}

/* ------------------------------------------------------------------ layout */
main { display: block; }

.page-body {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 32px;
}

/* The home page is a stack of photographs, each followed by its heading and copy. Every
   section used to carry its own 64px top and bottom padding, so a paragraph and the next
   photograph sat 92px apart while the heading touched the bottom edge of the picture above it.
   The rhythm is now a single value — one fixed gap between sections — the photograph gets a
   little air before the words begin, and the copy is a centred column under the photograph
   instead of a left-pinned block with a wide blank margin on its right.

   The section itself carries no measure. Its photographs run edge to edge across the viewport,
   the full-bleed band a property page's hero has always made and the measure no longer holding
   home's back — the column lives one level down instead, on .section-body, which keeps the
   centred prose and the side padding the section used to own. And the first section's 48 px of
   top padding is gone: above a picture that spans the window that pad reads as a white shelf
   between the bar and the photograph, so the hero sits flush under the header the way the
   property hero does. */
.home-section {
  margin: 0;
  padding: 0;
}
.home-section:last-child { padding-bottom: 56px; }
.home-section + .home-section { margin-top: 56px; }
.home-section .banner { margin-bottom: 28px; }
/* The home photographs take a ceiling 20% lower than the 560 px every banner shares: they are
   the page's scenery, not its subject, and 560 px of band out-talls the words that follow
   them on any ordinary laptop. 448 px = 560 × 4/5; the 21/9 ratio still draws the band on its
   own up to a window of 1,045 px (448 × 21/9, fraction rounded up), and from there the
   ceiling holds. The property hero keeps the shared 560 px — it carries its own words and
   its own arithmetic, and nobody asked it to shrink. */
.home-section .banner-img { max-height: 448px; }
.home-section .section-body { max-width: var(--prose-measure); margin: 0 auto; padding: 0 32px; }

.folder-nav {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 32px 32px 0;
}
/* The title used to be 15 px at 400 weight in faint ink — fine print sitting above the very links
   it names, so the whole list read like a footnote. It is a signpost, not a whisper: 18 px, bold,
   in the page's full ink — the same ink the links now carry, since a title lighter than its own
   list reads as a caption again. */
.folder-nav-title {
  font-size: 18px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 14px;
}
/* One 26 px gap for both axes read as a set of separate blocks once the list wrapped on a phone:
   the vertical gap matched the horizontal one and looked like a paragraph break between rows of
   one list. The row gap is tighter now, so a wrapped row reads as the next item in the same list,
   not a new band. */
.folder-nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px 26px; }
/* At 14 px and 400 weight the links were smaller than the page's own body copy (18 px) — the one
   row a visitor uses to choose a bedroom count was the weakest type on the page. 16 px and the
   page's full ink put them at the body's weight class without shouting past the 18 px title over
   them; the weight stays regular so the one bold label in the list can mean something. */
.folder-nav a {
  display: inline-block;
  font-size: 16px;
  font-weight: 400;
  text-transform: uppercase;
  color: var(--ink);
  transition: color 0.15s ease;
}
/* The underline that used to answer "which of these am I looking at?" is gone. The current page
   is now the only bold label in the list, in the site's own teal — weight and color together,
   against regular dark labels — while hovering darkens toward the same teal family. Two different
   marks, and neither of them a line. */
.folder-nav a:hover { color: var(--teal-dark); }
.folder-nav .is-active a { color: var(--teal); font-weight: 700; }
/* The heading used to start a full 64 px of page-body below the list — more air than the heading
   itself keeps over the cards under it, so the list floated over an empty band and looked like it
   belonged to no one. 32 px ties the list to the page it is on: the nav names the collection, the
   heading opens it. */
.folder-nav + .page-body { padding-top: 32px; }

/* A property page no longer carries the sibling list — the listing it belongs to is one link
   away, and the link says so with an arrow pointing back up the page. It borrows the folder
   nav's measure, label size, and colors, so it sits exactly where that list used to sit and
   reads as the same kind of thing: navigation, not prose. */
.property-back {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 24px 32px 0;
}
.property-back-link {
  display: inline-block;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
  transition: color 0.15s ease;
}
.property-back-link:hover { color: var(--teal-dark); }
.property-back-arrow { padding-right: 6px; }

/* When the page has a hero, the way back rides the photograph — centered across its top edge,
   the side of the picture opposite the headline, and drawn as a button rather than a line of
   nav text: over a picture there is no paper margin to hold a link, so the control carries
   its own surface. A translucent dark pill with a backdrop blur and a hairline light border
   stays legible over a bright sky or a dark interior — the same argument the headline's wash
   makes at the bottom of the picture, made per control instead of per page. */
.property-back-hero {
  position: absolute;
  top: 24px;
  /* An absolutely positioned box shrinks to its contents, so pulling the box back half its own
     width from the picture's midpoint centers the pill itself, not just its left edge. */
  left: 50%;
  transform: translateX(-50%);
  max-width: none;
  margin: 0;
  padding: 0;
  /* Above the gradient wash (the figure's ::after) *and* above the headline's figcaption:
     that caption layer is absolutely positioned over the whole picture, and being later in
     the tree it would otherwise sit on the button and eat its clicks. One and the other
     never overlap — the button rides the top edge, the caption stands at the foot — so
     both can keep their z-indexes. */
  z-index: 2;
}
.property-back-hero .property-back-link {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 10px 16px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  border-radius: 999px;
  background: rgba(17, 20, 21, 0.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.35);
  font-size: 13px;
  /* The plain link went bold with the folder nav it borrows; over a photograph the pill keeps its
     regular weight — the fill and the hairline already carry its presence. */
  font-weight: 400;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.95);
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.property-back-hero .property-back-link:hover {
  color: #fff;
  background: rgba(17, 20, 21, 0.7);
  border-color: rgba(255, 255, 255, 0.75);
}
.property-back-hero .property-back-arrow { font-size: 15px; }
/* The teal focus ring would vanish against a dark photograph, so over the hero it goes white,
   as it does in the header and footer. */
.property-back-hero :focus-visible { outline-color: #fff; }
/* The two halves of a phone's hero: a picture at a ratio a phone can hold, and a smaller control
   on it.

   The wide band cannot survive at a phone's width. At 390 px the 21:9 ratio asks for 167 px of
   height, and the 420 px desktop floor above is what the page actually paints — a photograph very
   nearly square on a window 390 px wide, with the caption competing for what little of the band is
   left. Below this breakpoint the hero takes a ratio a phone can hold, 4:3 with 280 px under it, and
   the object-fit: cover that has been on the image all along fills the box by cropping the sides
   instead of by letterboxing or stretching. The crop is the cost: a 16:9 photograph centred into a
   4:3 box gives up about a third of its own width, which is what showing a phone a photograph
   rather than a strip of one — and one that is not a square either — buys. Only the property hero
   changes; the home and page banners keep the wide band.

   The way back rides that picture, and shrinks to match it. Sized for the desktop band, the
   pill took a third of the strip's height and half its width: more of the photograph spent saying
   where to go than showing what is there. At caption scale — 12px against the 25px headline at the
   picture's foot — and on a picture with real height to it, it reads as a label on the photograph
   rather than a banner across it. The letter size was never what made the button wide, though:
   twenty-one uppercase letters are. Here the word that names what kind of list it is drops out,
   and the button says "Back to List" — no smaller a thing to tap, and a third fewer letters across
   the picture.
   Nothing is lost to anyone listening: the <nav> around it is already labelled "Property list", and
   that label is what an announcement reads first. The rule is not scoped to the hero, so the plain
   line of nav printed when a property has no hero picture loses the word at the same width, and the
   two spellings of the same link stay in step. */
@media (max-width: 559px) {
  .property-banner .banner-img { aspect-ratio: 4 / 3; min-height: 280px; }
  /* Centered, so only the inset from the top edge changes: the button sits closer to the edge of a
     phone's picture than of a desktop's, because the picture itself is that much shorter. */
  .property-back-hero { top: 8px; }
  .property-back-hero .property-back-link { padding: 8px 12px; font-size: 12px; letter-spacing: 0.05em; }
  .property-back-hero .property-back-arrow { font-size: 14px; }
  .property-back-scope { display: none; }
}

/* ------------------------------------------------------------------ grid */
.grid { display: block; }
.grid-row {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
  margin-bottom: var(--gap);
}
.grid-col { min-width: 0; }
.span-1 { grid-column: span 1; }
.span-2 { grid-column: span 2; }
.span-3 { grid-column: span 3; }
.span-4 { grid-column: span 4; }
.span-5 { grid-column: span 5; }
.span-6 { grid-column: span 6; }
.span-7 { grid-column: span 7; }
.span-8 { grid-column: span 8; }
.span-9 { grid-column: span 9; }
.span-10 { grid-column: span 10; }
.span-11 { grid-column: span 11; }
.span-12 { grid-column: span 12; }

@media (max-width: 899px) {
  .grid-col { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------------ blocks */
.block { margin-bottom: var(--gap); }
.block > h2 { margin-top: 0; }
/* A block following prose had no gap at all before it: the block carries only a bottom margin,
   and the prose's last paragraph has its own margin-bottom zeroed, so the section's heading
   landed flush against the last line of description — Photos sitting hard under the enquiry
   paragraph on the property page. A block after prose takes the same gap the blocks give each
   other; between two blocks the margins collapse, so nothing there grows. */
.prose + .block { margin-top: var(--gap); }
/* A section already has its own padding under the last thing in it. The block margin and the
   row margin stacked on top of that padding, so the end of a section carried a second gap
   before the next one began. */
.grid-row:last-child, .block:last-child { margin-bottom: 0; }

.prose { max-width: var(--prose-measure); }
/* The measure is a reading distance, not a position. It sits flush left in its column, and at the
   site's own 1,020px measure a twelve-column block is 956px wide, so the 700px measure leaves 256px
   of the column over — all of it on the right. That stays invisible until a block asks to be
   centred, because then the centring happens on the measure's axis, 128px left of the column's.
   The one block on this site asks: the licence number /real-estate-sales prints under its
   call-to-action. Its 130px of ink landed at x 447–577 on a 1,280px screen — under the gutter
   between the second photograph and the third — while the banner, the header and the column itself
   all centre on x 640. The drift is (column − measure) / 2: nil up to 764px wide, 68px at 900, and
   the full 128px from 1,020 up, since a narrower column narrows the measure along with it. A block
   marked Wide — see Block.Wide — gets its column back, so the centring lands where it was written. */
.block-wide .prose { max-width: none; }
.prose > :last-child { margin-bottom: 0; }
.prose ul, .prose ol { padding-left: 1.3em; }
.prose li { margin-bottom: 0.4em; }
.prose blockquote {
  margin: 0 0 1em;
  padding: 0 0 0 18px;
  border-left: 3px solid var(--rule);
  color: var(--ink-soft);
}
.prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2em 0; }
/* A property's prose is Markdown now, and Markdown has two shapes this page had no reason to draw
   before: a heading somebody wrote inside the text, and a piece of code. The heading sits under the
   page's own — internal/markdown will not print an h1 in prose at all — one step below the section
   title it lives inside. Code gets the monospace face and the same wash the rest of the site uses
   for something set apart. */
.prose h2, .prose h3, .prose h4 { margin: 1.6em 0 0.5em; }
.prose h2 { font-size: 20px; }
.prose h3 { font-size: 17px; }
/* The crawl wrote a standfirst under the page title as an <h3> straight after the <h1>, which skips
   a level. Those two are <h2>s now (internal/pages/real_estate_sales.go, terms_of_service.go), and
   this pulls one back to the 17px it was drawn at, so a standfirst stays a step under the section
   headings beneath it instead of matching them. */
.prose h2.subhead { font-size: 17px; }
.prose code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92em;
  background: var(--wash);
  padding: 2px 5px;
}
.prose pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 16px;
  background: var(--wash);
  padding: 12px 14px;
  margin: 0 0 1em;
  overflow-x: auto;
}
.prose pre code { background: none; padding: 0; }

.block-image .card { margin: 0; }
.card-caption {
  margin-top: 10px;
  font-size: 16px;
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ button */
.button {
  display: inline-block;
  padding: 14px 34px;
  font-family: var(--ui);
  font-size: 14px;
  text-transform: uppercase;
  color: #fff;
  background: var(--button);
  border: 0;
  cursor: pointer;
}
.button:hover { color: #fff; background: #000; }
.block-button { margin: 0 0 var(--gap); }

/* ------------------------------------------------------------------ gallery */
/* A grid row is as tall as the tallest thing in it, and these pictures arrive at whatever
   ratio the photographer shot them in. On /property-141 that is 29 tiles in three ratios —
   1.50, 0.85, 0.67 — so a row holding one portrait stood 258px tall while the two landscape
   pictures beside it rendered 115px, and 1,207px of the 2,118px section — 57% of it — was
   blank space beside a short picture. The grid was not broken; it was doing exactly what it
   was told, which was to size each row to its tallest item.

   So the tile is fixed, not the row: every picture is drawn into the same 3 / 2 rectangle,
   which is the ratio 21 of those 29 already have and the ratio the listing cards already use,
   so the crop only ever touches the minority. The crop is the thumbnail's problem alone — the
   anchor's href is the full picture, and the viewer shows all of it. */
.gallery {
  list-style: none;
  margin: 0 0 var(--gap);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.gallery-item { margin: 0; }
.gallery-link { display: block; }
.gallery-img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

/* Two tiles to a row below the stacking point, and square tiles once the row is phone
   width. A half-width 3 / 2 tile on a phone is 161 px across and 107 px tall: the crop
   takes off the top and bottom of a picture that was already cut to the same 3 / 2
   ratio, so the shorter tile is simply less of the photograph. Square keeps the full
   161 px of it, and at that width a square band is still a band.

   The row count is worked out rather than fixed, because the width at which two tiles
   become absurd varies: 50% - 7px is exactly half the row less its share of the 12 px
   gap, so two columns always fit while the row is wide enough, and a third is
   arithmetically impossible below 899 px, where the desktop's own three stand. Under
   that the 150 px floor is what ends two-a-row — a 340 px phone has 300 px of body
   left after the page padding, which cannot hold two 150 px tiles and the gap, so it
   draws one tile across the whole width instead. A 360 px phone has 320 px, and two fit.

   The square waits for the phone's own boundary, the 559 px the hero already uses. The
   tiles are what decide it, not the window: two stay to a row the whole way down, since
   50% - 7px is always half the row, so at 699 px a tile is still 324 px across — and a
   square that wide stands 324 px tall: taller than the picture reads well at, and enough
   to push the enquiry form off the screen. There the 3 / 2 band carries on. At 559 px the
   pair is 253 px apiece, at 375 px it is 161 px, and there the short square band is what
   keeps the photograph. */
@media (max-width: 899px) {
  .gallery {
    /* The plain two first: a browser that cannot parse max() inside a track size drops
       the second line and keeps this one, which is two tiles to a row at every width the
       150 px floor would have granted them anyway — it only fails to narrow to one tile
       on the smallest phones, where two small tiles still read as a grid of photographs. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-columns: repeat(auto-fill, minmax(max(150px, 50% - 7px), 1fr));
  }
}
@media (max-width: 559px) {
  .gallery-img { aspect-ratio: 1 / 1; }
}

/* ------------------------------------------------------------------- tour */
/* The 3D tour frame. Matterport hands out its embed at 853×480, which is a 16:9-ish
   rectangle; the frame takes the full column and keeps that ratio, so the box it will
   fill is on the page before the tour is — the same rule the map frame follows, and
   the reason a lazy frame does not shove the form down when it lands. */
.tour { border: 1px solid var(--rule); }
.tour-frame { display: block; width: 100%; aspect-ratio: 853 / 480; border: 0; }

/* ------------------------------------------------------------------ cards */
.cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--gap);
}
.card { margin: 0; }
.card-link { display: block; color: inherit; }
.card-img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.card-title {
  display: block;
  margin-top: 10px;
  font-size: 16px;
  text-transform: uppercase;
  color: var(--ink);
}
.card-link:hover .card-title { text-decoration: underline; }
.card-blurb { margin: 6px 0 0; font-size: 17px; color: var(--ink-soft); }

/* ------------------------------------------------------------------ map */
/* The frame's height is stated here rather than left to the frame: an iframe that is
   given none arrives at 150 px and grows when its document lands, and that growth is the
   layout shift the rest of the stylesheet works to avoid. */
.map { border: 1px solid var(--rule); }
.map-frame { display: block; width: 100%; height: 340px; border: 0; }
.map-address { font-style: normal; margin-top: 10px; font-size: 16px; color: var(--ink-soft); }
.map-address strong { display: block; color: var(--ink); }

/* ------------------------------------------------------------------ form */
.form-fields { list-style: none; margin: 0; padding: 0; }
.form-field { margin-bottom: 18px; }
.form-label, .form-field label {
  display: block;
  margin-bottom: 6px;
  font-size: 14px;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.required { color: #7a1f16; }
.form-field input, .form-field textarea {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 18px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
}
.form-field textarea { min-height: 120px; }
.form-field input:focus, .form-field textarea:focus { border-color: var(--teal); }
.form-field input:invalid:not(:placeholder-shown) { border-color: #a33; }
.field-help { margin: 6px 0 0; font-size: 15px; color: var(--ink-faint); }
.form-submit { margin: 0; }

/* The honeypot: an input no visitor is ever shown, which a script that fills in every field it finds
   will fill in. It is moved off the screen rather than hidden with display:none, because a script that
   looks at the HTML it was handed should still find a field called website sitting in the form — that
   is the whole test — while display:none would teach one which inputs to skip. It is not the
   .visually-hidden helper above: that class hides a thing from the eye and announces it to a screen
   reader, and this one is hidden from both. */
.form-hp {
  position: absolute;
  top: -9999px;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ------------------------------------------------------------------ property */
.property-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  /* The gutter down the middle of a property page was never the 40px gap. The sidebar ended on
     track 4 and the prose began on track 6, so the empty track — 43px of it at the full column
     width — sat between them with a 40px gap paid either side, and the space a reader actually
     saw measured 123px: wider than the gap between the sidebar's own sections, and enough to
     read as two pages set side by side rather than one listing. The two columns now share the
     track that used to be left empty, so the gap is the gutter and nothing else. 60px is half
     what was measured, and the width the gutter gives up goes to the prose column, which grows
     from 541px to 617px. The row gap stays at 40px here: below the 899px stacking point both
     columns are unpacked with display:contents, and there that gap becomes the vertical rhythm
     between the stacked sections — where it is halved, and the sections' own bottom margins
     with it. */
  column-gap: 60px;
  row-gap: 40px;
}
/* The sidebar leads the document, and these two column lines are what put it on the left with
   the prose beside it. No row is pinned any more. Grid's sparse packing only ever moves its
   cursor forward, and because the left-most item now comes first the cursor is still on the
   first row when the prose is placed: it opens at track 5, ahead of a cursor left at track 1, so
   it joins the same row. Measured at 1280px of window, the sidebar at 20,20 and the prose at
   20,453. The markup the other way round is the case that needed the pin — the prose parked the
   cursor at track 5, the sidebar's track 1 read as a step backwards, and the packing dropped it
   to a row of its own below the description it was meant to sit beside. */
.property-main { grid-column: 5 / 13; min-width: 0; }
.property-side { grid-column: 1 / 5; min-width: 0; }

@media (max-width: 899px) {
  /* Below the stacking point both columns are unpacked with display:contents so their
     sections become items of .property-grid and can be placed among them — each still
     needs the full width the column itself took, since the wide rules no longer reach
     them.
     The painted order follows what someone on a phone asks next: the picture of the
     property is behind them, so the map leads, then where it sits (the neighbourhood),
     then what it offers (the features), then the property description, and only then
     the photos, the tour and the enquiry form. That is now very near the order the
     markup is written in, because the sidebar leads the document; the description, the
     photos, the tour and the form already follow the features on their own. One move is
     left, so one rule is left: the heading pair sits in the prose column, past all of
     that, and has to be lifted back above the map. */
  /* The twelve tracks go quiet here, because the sixty-pixel gutter between them does not:
     with everything spanning 1 / -1 the column gap buys nothing, yet a twelve-column grid
     still owes eleven gaps of it, and 11 × 60 is 660 px — a floor under the whole page. The
     grid is then 660 px wide no matter how narrow the window goes, which is why below about
     700 px the property page stopped shrinking and grew a horizontal scrollbar: measured, the
     document's scrollWidth froze at 680 (660 plus the body's own 20 + 20) at 650, 500 and
     380 px of window. Zeroing the gap drops the floor, and every span still reads, since
     1 / -1 is simply the whole of each remaining track. The row gap halves to 20 px, and each
     stacked section's own 24 px bottom margin halves to 12 px with it — across grid items
     margins do not collapse, so the air above a stacked heading was the row gap plus the
     section above: 40 px over the subheadline, 64 px after a section. That was too much on a
     phone; halving both components leaves 20 px and 32 px, half of each. The gallery carries
     24 px of its own under the last tile, which stacks the same way, so it halves too — the
     88 px of air above the heading under the photos drops to 44 px. */
  .property-grid { column-gap: 0; row-gap: 20px; }

  .property-main, .property-side { display: contents; }
  .property-main > *, .property-side > * { grid-column: 1 / -1; min-width: 0; margin-bottom: 12px; }
  .property-grid .gallery { margin-bottom: 12px; }

  /* Negative, not zero: an order of 0 would tie with the sidebar's sections, and on a tie grid
     paints in document order — which now puts the map first, so the heading would land under it.
     -1 is the smallest deviation that clears them. Measured at 700px of window, the tops come out
     20 / 88 / 155 / 223 / 290 / 358 / 471 / 539 px: heading, map, neighbourhood, features,
     description, photos, tour, form. */
  .property-main > .property-title,
  .property-main > .property-subheadline { order: -1; }
}

.property-title { margin-bottom: 24px; }
.property-subheadline { margin: 0 0 6px; font-size: 20px; color: var(--ink-soft); }
/* On a page whose headline rides the hero, the subheadline is the first thing in the body and
   leads the prose on its own — it needs the room the withdrawn header used to carry. */
.property-main > .property-subheadline:first-child { margin: 0 0 20px; }
/* On a phone these two lines are the pair lifted above the map, and their bottom margins are cut
   here rather than in the stacking block above, because the :first-child rule just over — same
   width, later position — would otherwise out-rank the cut. Those margins exist to lead the prose
   in the wide layout, where the description follows at once. On a phone the map, the neighbourhood
   and the features come between them, so the margin is left holding nothing: the 40 px row gap plus
   the subheadline's own 20 was 60 px of air above the *Location* heading, which read as two
   unrelated sections rather than a lead-in and its map. With the margin gone the pair sits 20 px —
   the halved row gap alone — above the heading. */
@media (max-width: 899px) {
  .property-main > .property-title,
  .property-main > .property-subheadline,
  .property-main > .property-subheadline:first-child { margin-bottom: 0; }
}
.property-neighbourhood, .property-features { margin-bottom: var(--gap); }
.property-features ul { list-style: none; margin: 0; padding: 0; }
.property-features li {
  padding: 8px 0;
  font-size: 17px;
  border-bottom: 1px solid var(--rule);
}

/* The way to edit the page, when a reader is signed in, leaves the bar here and rides the lower
   right corner of the window instead: a property page is long — hero, gallery, form, sidebar — and
   a button parked in the bar is off the screen long before the enquiry form is. `.layout-property`
   is the body class layoutClass names for this kind of page (internal/site/render.go), so only this
   page moves the button, and the anchor itself is untouched: the bar simply loses the row the button
   used to take.

   Out of the bar the button can no longer borrow the bar's skin: white on white paper is barely a
   box, so the fill and the letters trade places — white on teal-dark, the pair the header and the
   menu panel are drawn with, 8.9:1 — and the panel's own shadow is borrowed to lift it off whatever
   photograph or paragraph sits under it.

   The header wraps the button in a stacking context of its own at z-index 100, so the 110 below is a
   position inside the header's stack, not the page's: it outranks the bar's own parts, stays under
   the open menu panel's 120 — a panel that has taken the corner should keep it — and the whole
   header stack, 100, sits under the lightbox's 300 and over the measure's 2. The 20 px is the
   gutter the bar and the page body already keep, and env() holds it clear of a phone's home bar. */
.layout-property .header-edit {
  position: fixed;
  right: 20px;
  bottom: calc(20px + env(safe-area-inset-bottom));
  z-index: 110;
  /* The bar's auto margin would hold the button at its static position instead of the right edge
     once the box is out of flow, so it is zeroed rather than left to answer the flex row. */
  margin-left: 0;
  color: #fff;
  background: var(--teal-dark);
  border-color: var(--teal-dark);
  box-shadow: 0 16px 34px rgba(18, 22, 23, 0.34);
}
/* The same answer to the pointer as in the bar, inverted: the letters keep their colour and the
   fill lightens to the bar's own teal, 6.9:1 on white — the button never loses the shape and
   contrast that make it read over whatever it happens to float on. */
.layout-property .header-edit:hover {
  color: #fff;
  background: var(--teal);
  border-color: var(--teal);
}

/* ------------------------------------------------------------------ testimonials */
.testimonials { max-width: var(--prose-measure); margin: 0 auto; }
/* The other two home sections take their heading from crawled markup that centres it; this one
   is ours, and left-aligned in the middle of three centred headings it read as a mistake. */
.home-section .testimonials > h2 { text-align: center; }
.testimonial {
  margin: 0 0 28px;
  padding: 0 0 0 18px;
  border-left: 3px solid var(--teal);
}
.testimonial p { margin: 0 0 8px; }
/* Links in crawled copy sat in a paragraph with nothing but colour to tell them apart. */
.prose a { text-decoration: underline; text-underline-offset: 2px; }

.testimonial cite { font-size: 15px; text-transform: uppercase; color: var(--ink-faint); }

/* ------------------------------------------------------------------ lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The side padding is the arrows' lane: the picture is never drawn underneath them. */
  padding: 64px 96px 56px;
  /* A horizontal drag inside the viewer belongs to the viewer, so the browser does not try
     to pan the page behind it; vertical travel is left alone, and a phone can still scroll
     a caption or a short landscape screen. */
  touch-action: pan-y;
  background: rgba(18, 22, 23, 0.94);
}
.lightbox[hidden] { display: none; }
.lightbox :focus-visible { outline-color: #fff; }

.lightbox-stage {
  margin: 0;
  min-width: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
/* The picture's own box, and the only thing that ever slides inside it. A swipe drags the
   picture on show and a copy of the one coming in, edge to edge, so the next photograph
   arrives from the side it belongs on instead of materialising behind the old one. */
.lightbox-lane {
  position: relative;
  min-width: 0;
  width: 100%;
  display: flex;
  justify-content: center;
}
/* 180px is the viewer's own padding, the caption line, and the gap between them. */
.lightbox-image, .lightbox-ghost { max-width: 100%; max-height: calc(100vh - 180px); }
/* The second picture of the pair: absolutely placed so the lane never grows and nothing else
   on the screen moves, kept out of the accessibility tree because a swipe is one photograph
   at a time to anyone reading it, and hidden until site.js has somewhere to put it. */
.lightbox-ghost {
  position: absolute;
  left: 50%;
  top: 50%;
  visibility: hidden;
}
.lightbox-lane.has-ghost .lightbox-ghost { visibility: visible; }
/* This is a swipe let go: the snap back to where the picture was, or the slide that carries
   the pair the rest of the way across. During the drag itself site.js switches it off with
   .is-dragging — a picture that lags its finger is not being dragged. */
.lightbox-image, .lightbox-ghost { transition: transform 180ms ease-out; }
.lightbox-lane.is-dragging .lightbox-image,
.lightbox-lane.is-dragging .lightbox-ghost { transition: none; }
.lightbox-meta {
  max-width: 900px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 16px;
  text-align: center;
}
.lightbox-caption { font-size: 16px; color: rgba(255, 255, 255, 0.8); }
.lightbox-count {
  font-size: 14px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
}

/* The three controls share one skin, so the viewer reads as one instrument. The fill is
   translucent because the arrows sit on top of the picture, not beside it: without it the
   label is just white letters over whatever the photograph happens to show there. */
.lightbox-close, .lightbox-prev, .lightbox-next {
  position: absolute;
  /* A swipe brings the next photograph in under the thumb, and it slides through the arrows'
     lane on its way to the centre: the controls are what a reader aims at, so they stay on
     top of a picture passing underneath them. */
  z-index: 1;
  padding: 10px 18px;
  font-family: var(--ui);
  font-size: 14px;
  text-transform: uppercase;
  color: #fff;
  background: rgba(18, 22, 23, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.6);
  cursor: pointer;
}
.lightbox-close:hover, .lightbox-prev:hover, .lightbox-next:hover {
  background: rgba(18, 22, 23, 0.85);
  color: #fff;
}
.lightbox-close { top: 16px; right: 16px; }
.lightbox-prev, .lightbox-next {
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
}
.lightbox-prev { left: 16px; }
.lightbox-next { right: 16px; }
.lightbox-arrow { font-size: 18px; line-height: 1; }
/* Out of pictures: the arrow stays put and goes quiet, which is what says the gallery ends
   here. aria-disabled rather than disabled keeps it in the accessibility tree — announced as
   dimmed, not vanished — and keeps the browser from stealing focus mid-walk. */
.lightbox-prev[aria-disabled="true"], .lightbox-next[aria-disabled="true"] {
  opacity: 0.35;
  border-color: rgba(255, 255, 255, 0.3);
  background: rgba(18, 22, 23, 0.55);
  cursor: default;
}
/* One picture is not a walk, so the arrows have nothing to say. */
.lightbox.is-single .lightbox-prev,
.lightbox.is-single .lightbox-next { display: none; }

@media (max-width: 899px) {
  .lightbox { padding: 60px 64px 52px; }
  .lightbox-image, .lightbox-ghost { max-height: calc(100vh - 172px); }
  .lightbox-prev, .lightbox-next { padding: 10px 12px; }
  .lightbox-prev { left: 10px; }
  .lightbox-next { right: 10px; }
}
/* Below this the word is the arrow's job, so the arrow keeps the lane to itself. */
@media (max-width: 559px) {
  .lightbox { padding: 56px 14px 48px; }
  .lightbox-image, .lightbox-ghost { max-height: calc(100vh - 168px); }
  .lightbox-prev, .lightbox-next { padding: 8px 10px; gap: 0; }
  .lightbox-nav-label { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
}

/* ------------------------------------------------------------------ footer */
/* Redesigned for reading. What the crawl left behind was one row of five links at 12px with 3px
   of letter-spacing — capitals stretched wide enough to be the slowest text on the page — with
   the address beside it in a column so narrow that one address took four lines, and the email
   printed twice. Three columns now, each with its own measure: the contact block, the policy
   pages, and the footer's own control. Body text is 16px, the same size the page is written in;
   only the labels are capitals. */
.site-footer {
  background: var(--teal);
  color: rgba(255, 255, 255, 0.8);
  /* The header is the same teal and the page above it ends in white, so without an edge of its
     own the footer reads as more of the same block the address was buried in. */
  border-top: 1px solid rgba(255, 255, 255, 0.18);
}
.footer-inner {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 40px 32px 32px;
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) auto;
  gap: 32px 48px;
  align-items: start;
}
.footer-name {
  margin: 0 0 14px;
  font-size: 21px;
  text-transform: uppercase;
  color: #fff;
  /* The name is long enough to break, and one word stranded on the second line is what the
     footer used to end with. Balance gives the two lines back. */
  text-wrap: balance;
}
.footer-info { font-style: normal; font-size: 18px; line-height: 1.7; }
.footer-info > * { display: block; }
.footer-address { color: #fff; }
.footer-info a { color: rgba(255, 255, 255, 0.85); }
.footer-info a:hover { color: #fff; text-decoration: underline; }

.footer-heading {
  margin: 0 0 12px;
  font-size: 15px;
  text-transform: uppercase;
  /* 0.75 is 4.68:1 against the footer's #2b6173. 0.65 — the token the body uses for faint text
     on white — is 3.95:1 here, which fails AA at this size. */
  color: rgba(255, 255, 255, 0.75);
}
/* One link per line, at the size the page is written in, instead of five capitals in a row that
   wrapped as soon as the row ran out of room. */
.footer-links ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.footer-links a { font-size: 18px; color: rgba(255, 255, 255, 0.85); }
.footer-links a:hover { color: #fff; text-decoration: underline; }

/* The footer's own control, kept out of the policy list it used to share a row with. It sits at
   the foot of the columns rather than the head, where a reader reaches for it. */
.footer-top { align-self: end; }
.footer-top a {
  display: inline-block;
  padding: 10px 18px;
  border: 1px solid rgba(255, 255, 255, 0.5);
  font-size: 15px;
  text-transform: uppercase;
  color: #fff;
}
.footer-top a:hover { color: #fff; background: rgba(255, 255, 255, 0.12); border-color: #fff; }

/* The copyright is the last thing on the page and the least important, so it gets its own rule
   and its own line rather than sitting under the columns as a fourth paragraph. */
.footer-base {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 32px 26px;
}
.footer-copyright {
  margin: 0;
  padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  font-size: 16px;
  color: rgba(255, 255, 255, 0.75);
}

@media (max-width: 899px) {
  .footer-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* Two columns now, and the control stays under the list it belongs with rather than dropping
     into the empty lane beside the contact block. */
  .footer-top { grid-column: 2; }
}
@media (max-width: 699px) {
  .footer-inner { grid-template-columns: 1fr; gap: 28px; padding: 36px 20px 32px; }
  /* One column now, so the control goes back to it: a second column asked for in a one-column
     grid is a column the grid has to invent, and the footer stacks in two again. */
  .footer-top { grid-column: 1; }
  .footer-base { padding: 0 20px 24px; }
}

/* ------------------------------------------------------------------ narrow */
@media (max-width: 559px) {
  .page-body { padding: 20px 20px; }
  .home-section .section-body { padding: 0 20px; }
  .home-section:last-child { padding-bottom: 40px; }
  .home-section + .home-section { margin-top: 40px; }
  .home-section .banner { margin-bottom: 20px; }
  .folder-nav { padding: 20px 20px 0; }
  /* At 16 px bold with 26 px between labels, three families fit on a phone row and the fourth
     drops alone beneath it. 16 px between labels is where a small phone fits them more evenly,
     and the wrapped rows sit close enough to read as one list. */
  .folder-nav ul { gap: 10px 16px; }
  .folder-nav + .page-body { padding-top: 24px; }
  .property-back { padding: 20px 20px 0; }
  .banner-overlay-inner { padding: 0 20px 24px; }
  .property-banner .banner-headline { font-size: 25px; }
  .banner-eyebrow { font-size: 13px; }
  h1 { font-size: 25px; }
  h2 { font-size: 22px; }
  /* On a phone the bar's one row is the wordmark and the Menu toggle, and a 390 px window
     gives them 350 px between them: 260 + 16 + the toggle's ~98 do not fit, 200 + 16 + the
     toggle do. The wordmark gives the width back at this end so the toggle keeps its place
     on the wordmark's own line all the way down. */
  .site-logo img { max-width: 200px; }
}

/* ------------------------------------------------------------------ motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
