/* ==========================================================================
   Atelier Versailles — design system

   Reference: arbony.com (client's final choice, superseding thehudsonco.com).
   Measured on that site: Roboto 300 throughout, no serif anywhere; full-bleed
   background-cover sections rather than inset images; white letterspaced
   statements overlaid on photography; black label boxes on image tiles; a
   light footer; a transparent header sitting over the hero.

   ONE CONSTRAINT SHAPES EVERYTHING BELOW. Arbony's imagery is 2560px+ (their
   files carry WordPress's -scaled suffix, which is only applied above that
   threshold). Every master here is 800px. Full-bleed at 1440 would be a 1.8x
   upscale on the most prominent image on the site, so the full-bleed treatment
   is applied where it survives and held back where it would not — noted at
   each point. This resolves itself the day better photography arrives.
   ========================================================================== */

:root {
  /* Ink & paper */
  --ink:        #141210;
  --ink-soft:   #443e39;
  --muted:      #756d65;   /* 5.08:1 on white, 4.63:1 on --paper-warm — AA on both */
  --paper:      #ffffff;
  --paper-warm: #f7f4ef;
  --paper-deep: #efeae2;
  --rule:       #ddd7cf;
  --rule-soft:  #ebe6df;
  /* Form controls need 3:1 against their surroundings (WCAG 1.4.11). The
     hairline --rule managed 1.43:1, which made a field almost invisible. */
  --field-fill:   #f2eee7;
  --field-border: #908678;   /* 3.58:1 on white, 3.10:1 on the fill */
  --black:      #0d0d0d;

  /* Type */
  /* The build brief specifies serif headings ("Image-led, generous whitespace,
     serif headings"). Headings, the wordmark, banner titles and the display
     lines are EB Garamond; running text and every small letterspaced label
     (navigation, filters, the black label boxes, the footer) stay in Roboto,
     which is what the arbony.com reference contributes. */
  --sans:  'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --serif: 'EB Garamond', 'Iowan Old Style', Georgia, 'Times New Roman', serif;

  --track-wide: .18em;   /* letterspaced caps */
  --track-mid:  .12em;

  /* Rhythm */
  --gutter: clamp(20px, 5vw, 64px);

  /* Three content widths, and only three. Every element on the site resolves to
     one of them, so a heading, the prose under it and the component under that
     share one left edge. There were nine before this: 1180, 900, 860, 780, 760,
     736, 720, 68ch and 38rem, several of them nested inside each other. */
  --wrap:        1180px;   /* full content — grids, gallery, footer */
  --wrap-mid:     900px;   /* feature media — figures, slider, contents */
  --measure:      38rem;   /* prose, ~74 characters at 16px */
  --wrap-narrow: var(--measure);

  /* One vertical rhythm, used for every gap on the site. Adjacent plain
     sections collapse (see .section + .section below) so the gap between two
     of them is --section-y, not the sum of two paddings. Before this the home
     page ran 68 / 160 / 184 / 184px between consecutive sections. */
  --section-y:   clamp(56px, 6.5vw, 96px);
  --section-y-s: clamp(34px, 4vw, 56px);
  /* The two gaps inside a section: between a block of prose and a component
     (a figure, the timber strip, the specification list), and between two
     paragraphs. With --section-y that is the whole vertical scale — three
     numbers. The editor body previously ran six: 154, 130, 114, 85, 76 and
     44px, none of which matched the 94px the page's own sections sat apart. */
  --block-y:     clamp(30px, 3.8vw, 54px);
  --flow-y:      1.15em;
  /* Every page opens with the same gap below the header or the banner. */
  --page-top:    var(--section-y);
  --stack:  clamp(20px, 2.4vw, 32px);

  --speed: .45s cubic-bezier(.22,.61,.36,1);
}

/* --------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 300;
  font-size: 16px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, picture, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; }
button { font: inherit; color: inherit; }

/* Visible focus for keyboard users only */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
/* On the dark footer the ink outline is 1.04:1 — invisible. WCAG 2.4.7 and
   1.4.11 both bite here, across 13 focusable links. */
.site-footer :focus-visible { outline-color: #fff; }
.screen-reader-text {
  position: absolute !important; width: 1px; height: 1px;
  overflow: hidden; clip: rect(1px,1px,1px,1px); white-space: nowrap;
}
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--ink); color: #fff; padding: 12px 20px;
  font: 500 12px/1 var(--sans); letter-spacing: var(--track-mid); text-transform: uppercase;
}
.skip-link:focus { left: 0; }

/* --------------------------------------------------------- typography */
h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 400;
  color: var(--ink);
  margin: 0;
  line-height: 1.14;
  letter-spacing: 0;
}
/* The reference keeps type quiet and lets photography carry the page. The
   whole ladder moves together — shrinking h1 alone would leave h2 larger. */
h1 { font-size: clamp(30px, 3.6vw, 46px); }
h2 { font-size: clamp(25px, 2.6vw, 34px); }
h3 { font-size: clamp(19px, 1.7vw, 23px); }
p  { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

.standfirst {
  font-family: var(--sans);
  font-style: normal;
  letter-spacing: .04em;
  font-size: clamp(15px, 1.4vw, 18px);
  line-height: 1.6;
  color: var(--ink-soft);
}
.lede  { font-size: clamp(17px, 1.35vw, 19px); line-height: 1.68; color: var(--ink-soft); }
.prose { color: var(--ink-soft); max-width: var(--measure); }
.prose p + p { margin-top: 0; }

/* --------------------------------------------------------- layout */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: calc(var(--wrap-narrow) + var(--gutter) * 2); }
.section { padding-block: var(--section-y); }
.section--tight { padding-block: var(--section-y-s); }
.section--warm  { background: var(--paper-warm); }

/* Two plain sections in a row each paint their own padding into the gap between
   them, so the visitor sees twice the rhythm. The second drops its top padding
   and the gap is always --section-y. Sections that paint a background keep
   theirs, because there the space has to sit inside the painted band. */
.section + .section:not(.section--warm):not(.cta) { padding-top: 0; }
/* Utilities, so a template never reaches for an inline style. There were
   eleven of them across six templates, each carrying its own clamp() — the
   single largest source of the inconsistent spacing. */
.text-center { text-align: center; }
/* A text block held to the prose measure inside a full-width section, so the
   image components below it can still use the whole content width. */
.measure-center { max-width: var(--measure); margin-inline: auto; }
.text-center .hero__note { margin-inline: auto; }
/* The opening block of a section, above the body it introduces. */
.section__intro { margin-bottom: var(--section-y-s); }
/* A heading standing on its own above a component, outside a .section-head. */
.section__subhead { margin-bottom: clamp(22px, 2.8vw, 34px); }
.rule { border: 0; border-top: 1px solid var(--rule); margin: 0; }
.rule--soft { border-top-color: var(--rule-soft); }

.page-title {
  font-family: var(--serif);
  font-size: clamp(17px, 1.7vw, 21px);
  font-weight: 400;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--ink);
}

/* Section heading with the hairline rule above it, Hudson-style */
.section-head { margin-bottom: clamp(28px, 3.6vw, 48px); }
.section-head--center { text-align: center; }
.section-head h1 { margin-bottom: 14px; }
.section-head h2 { margin-bottom: 14px; }
.section-head .page-title { margin-bottom: 12px; }

/* --------------------------------------------------------- header */
.site-header {
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.site-header__inner {
  display: flex; flex-direction: column; align-items: center;
  padding-block: 18px 0;
}
.wordmark {
  display: inline-block;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(19px, 2.2vw, 25px);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  text-align: center;
}
.wordmark small {
  display: block;
  font-family: var(--sans);
  font-size: 9.5px;
  letter-spacing: var(--track-wide);
  color: var(--muted);
  margin-top: 5px;
}

.nav-toggle {
  display: none;
  position: absolute; right: var(--gutter); top: 26px;
  background: none; border: 0; cursor: pointer; padding: 8px;
  font: 500 11px/1 var(--sans); letter-spacing: var(--track-mid); text-transform: uppercase;
}

/* The nav row carries no rule of its own: the header's own full-width
   border-bottom is the only line, as in the reference. Two rules of different
   widths (one inside .wrap, one on the header) was the mismatch. */
.nav-row {
  width: 100%;
  margin-top: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.main-nav { }
.main-nav ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: clamp(18px, 3vw, 42px);
}
.main-nav a {
  display: block; padding: 12px 0;
  font: 500 11.5px/1 var(--sans);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink);
  position: relative;
}
.main-nav a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 8px;
  height: 1px; background: var(--ink);
  transform: scaleX(0); transform-origin: center;
  transition: transform var(--speed);
}
.main-nav a:hover::after,
.main-nav .current-menu-item > a::after,
.main-nav .current_page_item > a::after { transform: scaleX(1); }

@media (max-width: 820px) {
  .site-header__inner { position: relative; padding-bottom: 0; }
  /* Gated on .js: without it the toggle would paint as a dead control while
     .main-nav stayed hidden, leaving no navigation at all on a phone. */
  .js .nav-toggle { display: inline-flex; align-items: center; min-height: 44px; top: 17px; }
  .nav-toggle[hidden] { display: none; }
}
@media (max-width: 400px) {
  /* Shrink the sub-line rather than padding the wordmark off-centre: the
     letterspaced 9.5px sub-line is what actually sets the wordmark's width. */
  .wordmark { font-size: 15px; letter-spacing: var(--track-mid); }
  .wordmark small { font-size: 8.5px; letter-spacing: var(--track-mid); }
  .nav-toggle { font-size: 10px; padding: 8px 0; }
}
@media (max-width: 820px) {
  .main-nav { width: 100%; }
  .main-nav ul { flex-direction: column; align-items: center; gap: 0; }
  .main-nav li { width: 100%; text-align: center; border-bottom: 1px solid var(--rule-soft); }
  .main-nav a { padding: 16px 0; }
  .main-nav a::after { display: none; }
}

/* --------------------------------------------------------- site search */
.site-search {
  position: absolute;
  right: 0;
  display: flex;
  align-items: center;
  gap: 4px;
}
.site-search__field {
  width: 150px;
  padding: 6px 9px;
  font: 400 12.5px/1.4 var(--sans);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 0;
  -webkit-appearance: none; appearance: none;
  transition: width var(--speed), opacity var(--speed), border-color var(--speed);
}
.site-search__field:focus { border-color: var(--ink); outline: none; }

/* With JS the field collapses behind the icon, as the reference does. Without
   JS it stays visible, so the icon is never a control that does nothing. */
.js .site-search__field {
  width: 0; padding-inline: 0; opacity: 0;
  border-color: transparent; pointer-events: none;
  /* pointer-events alone still leaves it focusable by keyboard. visibility
     takes it out of the tab order too, and is animatable via the delay. */
  visibility: hidden;
  transition: width var(--speed), opacity var(--speed), border-color var(--speed),
              visibility 0s linear var(--speed);
}
.js .site-search.is-open .site-search__field {
  width: 168px; padding-inline: 9px; opacity: 1;
  border-color: var(--rule); pointer-events: auto;
  visibility: visible;
  transition: width var(--speed), opacity var(--speed), border-color var(--speed);
}

.site-search__button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  padding: 0; background: none; border: 0; cursor: pointer;
  color: var(--muted);
  transition: color var(--speed);
}
.site-search__button:hover { color: var(--ink); }
.site-search__button svg { width: 15px; height: 15px; fill: currentColor; }

/* Above the mobile breakpoint but below ~900px the nav is wide enough that the
   absolutely-positioned search overlaps its last link. Give it its own row. */
@media (min-width: 821px) and (max-width: 900px) {
  .nav-row { flex-direction: column; gap: 6px; }
  .site-search { position: static; justify-content: center; padding-bottom: 10px; }
  .js .site-search__field { visibility: visible; }
}

@media (max-width: 820px) {
  /* On a phone the search sits in the opened menu rather than crowding the
     wordmark, which already shares that row with the Menu button. */
  .site-search { position: static; width: 100%; justify-content: center; padding: 14px 0; }
  .js .site-search__field { width: 60%; padding-inline: 9px; opacity: 1; border-color: var(--rule); pointer-events: auto; visibility: visible; }
  .nav-row { flex-direction: column; margin-top: 0; }
  .js .nav-row { display: none; }
  .js .nav-row.is-open { display: flex; }
}

/* --------------------------------------------------------- figures */
figure { margin: 0; }
.figure img { width: 100%; }
.figure figcaption {
  font: 400 12px/1.6 var(--sans);
  letter-spacing: .04em;
  color: var(--muted);
  padding-top: 12px;
}
/* Source photography is 800px square; never scale a frame past what it can carry. */
.figure--inset { max-width: var(--wrap-mid); margin-inline: auto; }

.ratio { position: relative; overflow: hidden; background: var(--paper-deep); }
.ratio > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ratio--square { aspect-ratio: 1 / 1; }
.ratio--portrait { aspect-ratio: 4 / 5; }
.ratio--landscape { aspect-ratio: 3 / 2; }
.ratio--hero { aspect-ratio: 16 / 9; }
@media (max-width: 700px) { .ratio--hero { aspect-ratio: 4 / 3; } }

/* --------------------------------------------------------- hero */
.hero { padding-block: clamp(44px, 6vw, 80px) 0; }
/* Photograph first: the reference starts its hero flush under the header, so
   the first thing on the page is the work rather than empty paper. */
/* Whatever a template opens with, it opens with the same gap. */
main > .section:first-child,
main > .hero:first-child,
main > article.section:first-child { padding-top: var(--page-top); }

/* Except the home page, whose photograph runs flush under the header as the
   reference does. Needs to out-specify the rule above — .hero--full on its own
   is (0,1,0) and loses to `main > .hero:first-child` at (0,2,1), which left
   84px of white above the hero with the white wordmark sitting on it. */
main > .hero--media-first:first-child,
main > .hero--full:first-child { padding-top: 0; }
.hero__head { text-align: center; max-width: var(--measure); margin: 0 auto clamp(34px, 4.4vw, 54px); }
/* Words beneath the photograph, as the reference does on its home page. */
.hero__head--below { margin: clamp(26px, 3.2vw, 42px) auto 0; }
.hero__head--below .hero__note { margin-inline: auto; max-width: 52ch; }
/* Deliberately NOT full content width. The masters are 800px, so the 1052px
   content column would be a 1.3x upscale; 900px holds it to 1.12x, and the
   inset reads as intentional against the reference's own margins. */
.hero__head h1 { margin-bottom: 18px; }
.hero__copy h1 { margin-bottom: 18px; }
.hero__note {
  margin-top: 20px;
  font: 400 14px/1.6 var(--sans);
  color: var(--muted);
  max-width: var(--measure);
}
.hero__head .page-title { margin-bottom: 14px; }
.hero__media { max-width: var(--wrap-mid); margin-inline: auto; }

/* Split hero: keeps the 800px source at a size it can actually fill */
.hero--split .hero__grid {
  display: grid; gap: clamp(28px, 5vw, 72px);
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  align-items: center;
}
/* As a grid item the centring margin would cancel stretch alignment, and the
   .ratio box has no intrinsic width to fall back on — it would collapse. */
.hero--split .hero__media { max-width: none; margin-inline: 0; }
@media (max-width: 900px) {
  .hero--split .hero__grid { grid-template-columns: 1fr; }
  /* justify-self, not auto margins — same collapse trap as above. */
  .hero--split .hero__media { max-width: 520px; width: 100%; justify-self: center; order: 2; }
  /* Words first on a phone: a wordmark above a photograph told the visitor
     nothing, and the headline was landing on or below the fold. */
  .hero--split .hero__copy { order: 1; margin-bottom: 6px; }
}

/* --------------------------------------------------------- page banner
   Arbony's inner-page pattern: a short full-bleed band carrying the page title
   in white letterspaced caps. Measured on theirs at 1440x400.

   Ours runs shorter (max 340px). A 1440-wide band from an 800px master is
   already a 1.8x upscale; keeping it shallow means less of the frame is
   stretched and the crop stays in the sharpest part of the image. */
.page-banner {
  position: relative;
  height: clamp(200px, 30vh, 340px);
  overflow: hidden;
  background: var(--ink);
}
.page-banner__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.page-banner::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(16,14,12,.58) 0%, rgba(16,14,12,.30) 55%, rgba(16,14,12,.44) 100%);
}
.page-banner__title {
  position: absolute; inset: 0; z-index: 2;
  display: grid; place-items: center;
  padding-inline: var(--gutter);
  margin: 0;
  color: #fff;
  font-family: var(--serif);
  font-size: clamp(18px, 2.1vw, 27px);
  font-weight: 400;
  letter-spacing: .16em;
  text-transform: uppercase;
  text-align: center;
  text-shadow: 0 1px 20px rgba(0,0,0,.36);
}
/* The banner is the page opening, so it cancels the standard opening gap. */
main > .page-banner:first-child { padding-top: 0; }

/* Whatever follows the banner opens with the full rhythm, whichever section
   variant it happens to be. Panel Options and Gallery render their standfirst
   section only when the page has an excerpt; without one, the next section
   along became the page opening and brought its own smaller padding with it,
   so deleting an excerpt in the editor would quietly change how that page
   opens. Pinned here instead of left to depend on source order. */
main > .page-banner + .section,
main > .page-banner + article.section { padding-top: var(--section-y); }

/* Section titles carry a short centred rule beneath, as the reference does. */
.section-head--center .page-title,
.section-head--center h2 { position: relative; padding-bottom: 16px; }
.section-head--center .page-title::after,
.section-head--center h2::after {
  content: ''; position: absolute; left: 50%; bottom: 0;
  width: 42px; height: 1px; margin-left: -21px;
  background: var(--rule);
}

/* --------------------------------------------------------- full-bleed hero
   Arbony's model: the photograph fills the viewport, the header sits over it,
   and a single letterspaced statement is centred on top.

   The scrim is not decoration. Our masters are 800px against Arbony's 2560px+,
   so a full-bleed frame upscales ~1.8x at 1440. A low-opacity dark wash both
   makes white type legible and takes the edge off that softness. Remove it the
   day higher-resolution photography lands. */
.hero--full { padding: 0; }
.hero--full .wrap { max-width: none; padding-inline: 0; position: relative; }

.hero--full .slider { max-width: none; }
.hero--full .slider__track { max-height: none; height: min(84vh, 780px); }
.hero--full .slider__slide { max-height: none; height: 100%; }
.hero--full .slider__slide .ratio { aspect-ratio: auto; height: 100%; }
.hero--full .slider__slide .ratio::after {
  content: ''; position: absolute; inset: 0;
  /* Bottom band only, for the slide caption. The top of the frame used to carry
     a 0.62 wash so a white header could sit on it; the header is no longer over
     the slideshow, so the photograph is left alone there. The statement and the
     controls carry their own backing instead of relying on this. */
  background: linear-gradient(180deg,
    rgba(16,14,12,0) 0%,
    rgba(16,14,12,0) 55%,
    rgba(16,14,12,.46) 100%);
  pointer-events: none;
}

/* The statement sits over the photograph, as on the reference. */
.hero__statement {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding-inline: var(--gutter);
  text-align: center;
  pointer-events: none;
  z-index: 2;
}
.hero__statement p,
.hero__statement .hero__title {
  margin: 0;
  color: #fff;
  font-family: var(--serif);
  font-size: clamp(17px, 1.9vw, 25px);
  font-weight: 400;
  letter-spacing: .14em;
  text-transform: uppercase;
  line-height: 1.6;
  /* Its own backing, in the site's black-label language. Measured on the four
     slides with the old wash, white type here ran 10.67, 3.00, 2.61 and 1.52:1
     — three of four failed. 0.85 of ink computes to 5.1:1 even over a pure
     white pixel, so the photograph behind it no longer decides legibility. */
  background: rgba(16, 14, 12, .85);
  padding: .85em 1.5em;
  max-width: calc(22ch + 3em);
}

/* Controls move onto the photograph. */
.hero--full .slider__controls {
  /* A pill sized to its contents, centred. Measured before: arrows (75% white)
     2.57:1 and dots (80% white) 2.67:1 on the brightest slide, against the 3:1
     WCAG 1.4.11 needs for controls. Solid white on 0.7 of ink, over the bottom
     wash, computes to more than 4.5:1 even over a pure white pixel. */
  position: absolute; left: 50%; right: auto; bottom: 22px;
  transform: translateX(-50%);
  width: max-content;
  margin: 0; padding: 0 14px; z-index: 3;
  background: rgba(16, 14, 12, .7);
}
.hero--full .slider__arrow { color: #fff; }
.hero--full .slider__arrow:hover { color: #fff; opacity: .8; }
.hero--full .slider__dot { border-color: #fff; }
.hero--full .slider__dot.is-active { background: #fff; border-color: #fff; }
/* The caption is a timber name, so it wears the site's black label box, flush
   in the corner as on the tiles. As bare white text over the wash it measured
   3.16:1 against the brightest pixel under it on the Renaissance slide. */
.hero--full .slider__caption {
  z-index: 3;
  left: 0; bottom: 0;
  padding: 10px 16px;
  background: var(--ink);
  text-shadow: none;
}

/* The header is solid on every page, including the home page, and the hero
   slideshow begins beneath it. It previously ran transparent over the slides,
   which put the wordmark and navigation on moving photography and forced a
   heavy scrim across the top of every frame to keep them legible. */

@media (max-width: 700px) {
  .hero--full .slider__track { height: min(74vh, 560px); }
  .hero__statement p { letter-spacing: .12em; }
  /* The caption sits bottom-left and the control pill bottom-centre; at 375 they
     overlapped by 4px on every slide even with the caption lifted 62px. Move
     the label to the top corner, where nothing else sits. */
  .hero--full .slider__caption { bottom: auto; top: 0; }
}

/* --------------------------------------------------------- hero slider
   3:2 at up to 900px, matching the reference's frame proportion while staying
   inside what an 800px master can carry. */
.slider { max-width: var(--wrap-mid); margin-inline: auto; }
/* On a short window a 600px-tall hero fills the whole viewport and the visitor
   sees nothing but floor — no headline, no sense there is a page below it.
   Cap it against viewport height so the words are always partly in view. */
.slider__track {
  position: relative;
  max-height: 58vh;
  overflow: hidden;
}
/* The cap belongs on the track only. Putting max-height on the .ratio box makes
   it shrink its WIDTH to preserve the 3:2 ratio — the hero went from 900px to
   682px and stopped being centred. Clipping the track instead keeps the frame
   900px wide and simply crops its height. */
/* The slide is clipped to the same height so the caption, which sits at its
   bottom edge, lands on the visible edge rather than being cropped away with
   the rest of the frame. No aspect-ratio here, so the width is unaffected. */
.slider__slide { max-height: 58vh; overflow: hidden; }
/* Crop from the top, not the bottom: these are floors, and a top-anchored clip
   on a short window left ceiling and chandelier in frame with no parquet. */
@media (max-height: 899px) {
  .slider__slide .ratio > img { object-position: 50% 72%; }
}

/* Tall windows get the full 3:2 frame; the cap only bites on short ones. */
@media (min-height: 900px) {
  .slider__track, .slider__slide { max-height: none; }
}
.slider__slide {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity 1.1s ease, visibility 0s linear 1.1s;
}
.slider__slide.is-active {
  position: relative;
  opacity: 1; visibility: visible;
  transition: opacity 1.1s ease;
}
/* Without JS only the first slide exists in flow, so the hero is simply static. */
.slider__slide:first-child { position: relative; opacity: 1; visibility: visible; }
.js .slider__slide:first-child:not(.is-active) { position: absolute; opacity: 0; visibility: hidden; }

.slider__caption {
  position: absolute; left: 0; bottom: 0;
  padding: 20px 22px;
  font: 500 10.5px/1 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  color: #fff;
  text-shadow: 0 1px 14px rgba(0,0,0,.55);
  pointer-events: none;
}

.slider__controls {
  display: flex; align-items: center; justify-content: center;
  gap: 18px;
  margin-top: 18px;
}
/* [hidden] works through the UA rule [hidden]{display:none}, which ANY author
   display declaration beats regardless of specificity. Without this the
   controls paint as focusable buttons that do nothing when JS has not run.
   Same guard as .nav-toggle[hidden]. */
.slider__controls[hidden] { display: none; }
.slider__arrow {
  background: none; border: 0; cursor: pointer;
  padding: 6px 10px; min-height: 44px;
  font-size: 15px; color: var(--muted); line-height: 1;
  transition: color var(--speed);
}
.slider__arrow:hover { color: var(--ink); }
.slider__dots { display: flex; align-items: center; gap: 9px; }
.slider__dot {
  width: 7px; height: 7px; padding: 0;
  border: 1px solid var(--muted); border-radius: 50%;
  background: none; cursor: pointer;
  transition: background-color var(--speed), border-color var(--speed);
}
.slider__dot.is-active { background: var(--ink); border-color: var(--ink); }
/* The dot itself is 7px; the hit area is not. */
.slider__dot { position: relative; }
.slider__dot::after { content: ''; position: absolute; inset: -16px; }

@media (prefers-reduced-motion: reduce) {
  .slider__slide { transition: none; }
}

/* --------------------------------------------------------- signposts */
.signposts {
  display: grid; gap: clamp(24px, 3.4vw, 46px);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.signpost { text-decoration: none; display: block; group: signpost; }
.signpost .ratio { margin-bottom: 16px; }
/* 4:5 made the pair 1069px tall on its own. 1:1 still shows the room and keeps
   the page moving. */
.signpost .ratio--portrait { aspect-ratio: 1 / 1; }
/* No scale transform: the first pass removed these from the gallery and the
   reference has none anywhere. The rule underline is the hover affordance. */
.signpost__label { transition: border-color var(--speed); }
.signpost:hover .signpost__label { border-top-color: var(--muted); }
.signpost { position: relative; }
.signpost__media { position: relative; display: block; }
.signpost .ratio { margin-bottom: 0; }
/* Black label box laid over the photograph, as the reference does on its
   product tiles. */
.signpost__label {
  position: absolute;
  left: 0; bottom: 0;
  z-index: 2;
  display: flex; align-items: baseline; gap: 12px;
  padding: 13px 22px;
  background: var(--ink);
  transition: background-color var(--speed);
}
.signpost:hover .signpost__label { background: #000; }
.signpost__label h3 {
  font-family: var(--sans);
  font-size: clamp(12px, 1.2vw, 14px);
  font-weight: 300;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #fff;
}
.signpost__label span { display: none; }
.signpost p { margin-top: 16px; color: var(--ink-soft); font-size: 15px; }

/* --------------------------------------------------------- editorial blocks */
.block { padding-block: var(--section-y); border-top: 1px solid var(--rule-soft); }
.block:first-of-type { border-top: 0; }
.block__grid {
  display: grid; gap: clamp(28px, 5vw, 76px);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
}
.block__grid--text-first > .block__media { order: 2; }
@media (max-width: 880px) {
  .block__grid { grid-template-columns: 1fr; }
  .block__grid--text-first > .block__media { order: 0; }
}
.block__text h2 { margin-bottom: clamp(18px, 2vw, 26px); }

/* --------------------------------------------------------- timber strip */
/* Antique / Reclaimed / Engineered / Renaissance are specification options,
   not patterns: one pattern shown in four timbers. */
.timber-strip {
  display: grid; gap: clamp(16px, 2.2vw, 28px);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-top: clamp(30px, 4vw, 48px);
}
/* Stays two-up even on a phone: the strip exists to be compared across, and
   one tile at a time defeats that. */
@media (max-width: 900px) { .timber-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 460px) {
  .timber-strip { gap: 12px; }
  .timber h3 { font-size: 14px; }
  .timber p { font-size: 12.5px; }
}
.timber { text-decoration: none; display: block; }
.timber .ratio { margin-bottom: 14px; }
.timber h3 { transition: border-color var(--speed); }
.timber:hover h3 { border-top-color: var(--ink); }
.timber { position: relative; }
.timber__media { position: relative; display: block; }
.timber .ratio { margin-bottom: 0; }
.timber h3 {
  position: absolute; left: 0; bottom: 0; z-index: 2;
  margin: 0; padding: 10px 16px;
  background: var(--ink); color: #fff;
  font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase;
  font-family: var(--sans); font-weight: 300;
  transition: background-color var(--speed);
}
.timber:hover h3 { background: #000; }
.timber p { padding-top: 16px; }
.timber p { font-size: 14px; line-height: 1.6; color: var(--muted); margin-top: 8px; }

/* --------------------------------------------------------- facts band */
.facts {
  display: grid; gap: clamp(24px, 3.4vw, 48px);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin: 0;
}
.facts__item { border-top: 1px solid var(--ink); padding-top: 16px; }
.facts__item dt {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(19px, 1.8vw, 23px);
  color: var(--ink);
  margin-bottom: 10px;
}
.facts__item dd {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--ink-soft);
}

/* --------------------------------------------------------- spec list */
.spec {
  display: grid; gap: 0 clamp(24px, 4vw, 56px);
  /* Explicit column counts that divide four evenly. repeat(auto-fit,
     minmax(240px, 1fr)) could never reach four across — that needs
     4x240 + 3x56 = 1128px against a widest content box of 1052px — so at every
     desktop width the list broke 3 + 1, with the closing rule under the first
     column only and two empty cells beside "Lead time". */
  grid-template-columns: minmax(0, 1fr);
  border-top: 1px solid var(--rule);
  margin-top: clamp(28px, 3.6vw, 44px);
}
@media (min-width: 560px) {
  .spec { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.spec__item { padding: 22px 0; border-bottom: 1px solid var(--rule-soft); }
.spec__item dt {
  font: 500 10.5px/1 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted); margin-bottom: 9px;
}
.spec__item dd {
  margin: 0; font-family: var(--serif);
  font-size: clamp(17px, 1.5vw, 20px); line-height: 1.45; color: var(--ink);
}

/* --------------------------------------------------------- other designs row */
/* Each design is a repeatable block so any one can be split onto its own
   page later without reworking the markup. */
/* auto-fill, not auto-fit: with only one or two designs set, auto-fit would
   collapse the empty tracks and stretch a tile far past its 800px master. */
.designs {
  display: grid; gap: clamp(18px, 2.6vw, 32px);
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  margin-top: clamp(26px, 3.4vw, 40px);
}
.design .ratio { margin-bottom: 13px; }
.design h3 {
  font-family: var(--sans); font-weight: 500; font-size: 13px;
  letter-spacing: var(--track-mid); text-transform: uppercase;
  border-top: 1px solid var(--rule); padding-top: 11px;
}

/* --------------------------------------------------------- panel drawings */
/* One line drawing per panel size, named and measured beneath. The drawings are
   transparent line art: they sit on the page itself, not on the tint a
   photograph loads over, and are never cropped. Rows are centred, so a short
   last row reads as deliberate rather than as a gap. */
.drawings {
  --per-row: 2;
  --drawings-gap: clamp(18px, 2.6vw, 32px);
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(28px, 3.4vw, 44px) var(--drawings-gap);
}
@media (min-width: 561px) { .drawings { --per-row: 3; } }
@media (min-width: 901px) {
  .drawings--4 { --per-row: 4; }
  .drawings--5 { --per-row: 5; }
}
.drawing {
  flex: 0 0 calc((100% - (var(--per-row) - 1) * var(--drawings-gap)) / var(--per-row));
  margin: 0;
}
.drawing .ratio { background: none; border-radius: 0; }
.drawing .ratio > img { object-fit: contain; }
/* The drawing is a button: it opens the drawing large, in the gallery's viewer. */
.drawing__open {
  display: block; width: 100%;
  margin: 0 0 14px; padding: 0;
  border: 0; background: none;
  cursor: zoom-in;
}
.drawing__open img { transition: opacity var(--speed); }
.drawing__open:hover img { opacity: .7; }
.drawing__open:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.drawing figcaption { border-top: 1px solid var(--rule); padding-top: 11px; text-align: center; }
.drawing__name {
  display: block;
  font: 500 12px/1.45 var(--sans);
  letter-spacing: var(--track-mid); text-transform: uppercase;
  color: var(--ink);
}
.drawing__size {
  display: block; margin-top: 4px;
  font-size: 14px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------- gallery
   Redesigned. Three changes, all of them about consistency with the rest of
   the site:

   1. Every tile is the same size. A lead tile spanning two of three columns
      made the first row a different scale from the four rows beneath it, and
      it forced a second `sizes` value that JS had to rewrite after every
      filter — a moving part whose only job was to support the irregularity.
   2. Every tile carries the black label box the signposts and the timber strip
      already use. Captions were hidden outright, so on a page of 45 floors
      nothing on screen said which timber any of them was, on a site whose
      whole proposition is that you can see the timber.
   3. The filter bar is a band with the active timber marked as a black box,
      matching those labels, rather than a row of bare text under a hairline. */

.filters {
  /* Sticky: with 45 images the bar scrolled away after the first row and the
     visitor had to return to the top to change timber. */
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 8px clamp(4px, .8vw, 10px);
  /* Translucent rather than solid: tiles passing under the bar stay faintly
     visible, so it reads as a layer over the gallery instead of a lid on it.
     The fallback below keeps it opaque where backdrop-filter is unsupported. */
  background: var(--paper);
  border-block: 1px solid var(--rule);
  padding-block: 13px;
  margin-bottom: clamp(26px, 3.4vw, 44px);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .filters {
    background: rgba(255, 255, 255, .9);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
  }
}
.filter {
  background: none; border: 1px solid transparent; cursor: pointer;
  padding: 9px 17px;
  font: 400 11px/1 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted);
  transition: color var(--speed), background-color var(--speed), border-color var(--speed);
}
.filter:hover { color: var(--ink); border-color: var(--rule); }
/* Transition lists across this stylesheet name background-COLOR rather than the
   `background` shorthand. Both are valid — a shorthand in transition-property
   expands to its longhands — but naming the colour is precise about what is
   actually animating, and avoids transitioning background-position and
   background-size along with it. */
/* The selected timber is marked the way every other label on this site is
   marked — a black box. The 1px underline it replaces was a second, quieter
   visual language saying the same thing. */
.filter[aria-pressed="true"] {
  background: var(--ink); color: #fff; border-color: var(--ink);
}

/* How many floors are in view, which the bar alone never said. Mirrors the
   screen-reader live region so both audiences get the same count. */
.gallery-count {
  margin: 0 0 clamp(18px, 2.2vw, 26px);
  text-align: center;
  font: 400 11px/1 var(--sans);
  letter-spacing: var(--track-mid); text-transform: uppercase;
  color: var(--muted);
}

/* Three up. A 332px tile from an 800px master stays sharp; a wider one would
   not, which is the same constraint that caps every other frame on the site. */
.gallery-grid {
  display: grid;
  gap: clamp(12px, 1.5vw, 20px);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: start;
}
@media (max-width: 860px) {
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* One up below 520px: a 162px tile shows a specifier nothing, and the whole
   proposition of this site is that you can see the timber. */
@media (max-width: 520px) {
  .gallery-grid { grid-template-columns: 1fr; gap: 14px; }
}

.gallery-item { margin: 0; position: relative; }
.gallery-item.is-hidden { display: none; }
.gallery-item .ratio { background: var(--paper-deep); }

/* The photograph is a button: it opens the floor's details. */
.gallery-item__open {
  display: block; width: 100%;
  margin: 0; padding: 0;
  border: 0; background: none;
  cursor: zoom-in;
  text-align: inherit;
}
.gallery-item__open img { transition: opacity var(--speed); }
.gallery-item__open:hover img { opacity: .9; }
.gallery-item__open:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* What each floor is, under its photograph: timber, room, then where and when
   once recorded. The timber used to appear only on hover, and nothing else at
   all, so the gallery read as bare pictures. */
.gallery-item figcaption { margin-top: 12px; }
.gallery-item__timber {
  display: block;
  font: 400 10.5px/1.4 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted);
}
.gallery-item__title {
  display: block;
  margin-top: 3px;
  font-family: var(--serif);
  font-size: calc(clamp(17px, 1.4vw, 19px) * var(--heading-scale, 1));
  line-height: 1.35;
  color: var(--ink);
}
.gallery-item__meta { display: block; margin-top: 2px; font-size: 13px; color: var(--muted); }
.gallery-empty {
  text-align: center; padding: clamp(40px, 7vw, 80px) 0; color: var(--muted);
  font-family: var(--serif); font-size: 18px;
  letter-spacing: .04em;
}

/* --------------------------------------------------------- search results */
/* --block-y, not --section-y: these are entries in a list, not sections of a
   page, and 94px between two short title-and-excerpt blocks read as a fault. */
.search-result { margin-bottom: var(--block-y); }
.search-result:last-of-type { margin-bottom: 0; }
.search-result h2 a { text-decoration: none; }

/* --------------------------------------------------------- contents */
.contents {
  border-block: 1px solid var(--rule);
  padding-block: 18px;
  max-width: var(--wrap-mid);
  margin-inline: auto;
}
.contents ul {
  list-style: none; margin: 0; padding: 0;
  /* Centred, because it wraps onto two lines at every width below about 1200px
     and a ragged left-aligned second line under a centred standfirst reads as a
     mistake rather than a choice. */
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 10px clamp(16px, 2.4vw, 30px);
}
.contents a {
  font: 500 11px/1.5 var(--sans);
  letter-spacing: var(--track-mid);
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color var(--speed), border-color var(--speed);
}
.contents a:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* --------------------------------------------------------- CTA */
.cta { background: var(--paper-warm); text-align: center; }
.cta h2 { margin-bottom: 18px; }
.cta .prose { margin-inline: auto; }
.btn {
  display: inline-block;
  margin-top: clamp(24px, 3vw, 34px);
  padding: 16px 40px;
  background: var(--ink); color: #fff;
  font: 500 11px/1 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  text-decoration: none; border: 1px solid var(--ink); cursor: pointer;
  transition: background-color var(--speed), color var(--speed);
}
.btn:hover { background: transparent; color: var(--ink); }
.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { background: var(--ink); color: #fff; }

/* --------------------------------------------------------- form
   Follows the reference's quote form: sentence-case label above a softly
   filled field, small grey helper text below where it earns its place.
   Selectors cover Contact Form 7's own control classes as well as the
   .field wrappers the form template supplies. */
.enquiry { max-width: var(--wrap-mid); }
.field { margin-bottom: 20px; }
.field label {
  display: block;
  font: 400 13px/1.4 var(--sans);
  letter-spacing: .01em;
  color: var(--ink-soft);
  margin-bottom: 7px;
}
.field .req { color: var(--muted); }
.field-help {
  display: block;
  font: 400 12px/1.5 var(--sans);
  color: var(--muted);
  margin-top: 6px;
}
/* Only four of twelve fields are needed. Saying which is kinder than leaving a
   busy specifier to infer it from the asterisks. */
.field .opt {
  font-size: 11.5px;
  font-weight: 400;
  color: var(--muted);
  letter-spacing: 0;
  text-transform: none;
}
.form-lead {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ink-soft);
  margin-bottom: clamp(22px, 2.6vw, 30px);
  max-width: var(--measure);
}
.enquiry input[type="file"] {
  padding: 11px 13px;
  font-size: 13.5px;
  background: var(--paper);
}
.form-note strong { color: var(--ink); font-weight: 500; }
.field input, .field textarea, .field select,
.enquiry input:not([type="submit"]), .enquiry textarea, .enquiry select {
  width: 100%; padding: 12px 14px;
  font: 400 15px/1.5 var(--sans); color: var(--ink);
  background: var(--field-fill); border: 1px solid var(--field-border);
  border-radius: 0; -webkit-appearance: none; appearance: none;
  transition: border-color var(--speed), background-color var(--speed);
}
.field select, .enquiry select {
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 19px) 52%, calc(100% - 14px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 38px;
}
.field input:focus, .field textarea:focus, .field select:focus,
.enquiry input:focus, .enquiry textarea:focus, .enquiry select:focus {
  border-color: var(--ink); background: var(--paper); outline: none;
}
.field textarea, .enquiry textarea { min-height: 150px; resize: vertical; }
.field--row { display: grid; gap: 22px; grid-template-columns: 1fr 1fr; }
@media (max-width: 620px) { .field--row { grid-template-columns: 1fr; } }
/* Honeypot: hidden from people, visible to bots that fill every input */
.field--trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-note { font-size: 13px; color: var(--muted); margin-top: 18px; }

/* ---- Contact Form 7 output ---- */
.enquiry .wpcf7-form-control-wrap { display: block; }
.enquiry .wpcf7-not-valid { border-color: #9d3b2f; }
.enquiry .wpcf7-not-valid-tip {
  display: block; color: #9d3b2f;
  font: 400 13px/1.4 var(--sans); margin-top: 6px;
}
.enquiry .wpcf7-response-output {
  margin: 0 0 26px; padding: 15px 19px;
  border: 1px solid var(--rule); background: var(--paper-warm);
  font: 400 15px/1.5 var(--sans); color: var(--ink);
}
.enquiry .wpcf7-form.invalid .wpcf7-response-output,
.enquiry .wpcf7-form.spam .wpcf7-response-output,
.enquiry .wpcf7-form.failed .wpcf7-response-output {
  border-color: #9d3b2f; color: #9d3b2f; background: var(--paper);
}
.enquiry .wpcf7-form.sent .wpcf7-response-output { border-color: var(--ink); }
.enquiry .wpcf7-spinner { margin: 0 0 0 14px; }
.enquiry input[type="submit"] {
  width: auto; margin-top: clamp(12px, 1.6vw, 20px);
  padding: 15px 40px;
  background: var(--ink); color: #fff;
  font: 500 11px/1 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  border: 1px solid var(--ink); cursor: pointer;
  transition: background-color var(--speed), color var(--speed);
}
.enquiry input[type="submit"]:hover { background: transparent; color: var(--ink); }
.form-message { padding: 16px 20px; margin-bottom: 26px; border: 1px solid var(--rule); font-size: 15px; }
.form-message--ok  { border-color: var(--ink); background: var(--paper-warm); }
.form-message--err { border-color: #9d3b2f; color: #9d3b2f; }
.field-error { display: block; color: #9d3b2f; font-size: 13px; margin-top: 7px; }

/* --------------------------------------------------------- contact columns */
.contact-grid {
  display: grid; gap: clamp(32px, 5vw, 72px);
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  align-items: start;
}
@media (max-width: 880px) { .contact-grid { grid-template-columns: 1fr; } }
.contact-grid { margin-top: var(--section-y-s); }
.contact-grid + .rule, .rule + .contact-grid { margin-top: var(--section-y-s); }
/* The form runs roughly twice the height of the panel beside it, leaving a dead
   right column. Sticking the panel keeps the email address and the lead time in
   view for the whole length of the form. */
@media (min-width: 881px) {
  .contact-grid > aside {
    position: sticky;
    top: clamp(16px, 3vw, 34px);
  }
}

.contact-detail dt {
  font: 500 10.5px/1 var(--sans);
  letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--muted); margin-bottom: 8px;
}
.contact-detail dd { margin: 0 0 24px; font-size: 15px; color: var(--ink-soft); }
.contact-detail a { text-decoration: none; border-bottom: 1px solid var(--rule); }
.contact-detail a:hover { border-bottom-color: var(--ink); }
/* Two addresses sit one under the other, each underlined to its own length. */
.contact-detail__email { display: block; width: fit-content; max-width: 100%; overflow-wrap: anywhere; }
.contact-detail__email + .contact-detail__email { margin-top: 6px; }

/* --------------------------------------------------------- notice (awaiting content) */
.notice {
  border: 1px dashed var(--rule);
  background: var(--paper-warm);
  padding: clamp(20px, 3vw, 32px);
  font-size: 14.5px; color: var(--muted);
}
.notice strong { color: var(--ink-soft); font-weight: 500; }

/* --------------------------------------------------------- footer */
/* Dark, per the client, and per the footer reference supplied with the brief.
   The dark ground needs its own two text tones: --muted is 5.08:1 on white but
   only 3.68:1 here, which fails AA for body text. --footer-text is 7.26:1. */
.site-footer {
  --footer-text: #a8a09a;
  --footer-rule: rgba(255, 255, 255, .16);
  background: var(--ink);
  color: var(--footer-text);
  border-top: 0;
}
.site-footer a { text-decoration: none; }
.footer-cols {
  display: grid; gap: clamp(26px, 3.4vw, 44px);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  padding-block: var(--section-y) var(--section-y-s);
}
.footer-col h2 {
  font-family: var(--sans); font-weight: 400; font-size: 11px;
  letter-spacing: var(--track-wide); text-transform: uppercase; color: #fff;
  padding-bottom: 13px; margin-bottom: 18px;
  border-bottom: 1px solid var(--footer-rule);
}
.footer-col p, .footer-col li {
  font-size: 13.5px; line-height: 1.62; color: var(--footer-text);
}
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li + li { margin-top: 7px; }
.footer-col a { color: var(--footer-text); transition: color var(--speed); }
.footer-col a:hover { color: #fff; }
.footer-col .group + .group { margin-top: 20px; }
.footer-col > p + ul { margin-top: 10px; }

/* Editor-only guidance on the front end, so it can never read as public copy. */
.notice--admin { border-style: solid; border-color: #2271b1; background: #f0f6fc; margin-top: 14px; }

.footer-base {
  border-top: 1px solid var(--footer-rule);
  padding-block: 26px;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 16px;
}
.footer-base p, .footer-base a {
  font: 300 11.5px/1.5 var(--sans);
  letter-spacing: .05em; color: var(--footer-text); margin: 0;
}
.footer-base a:hover { color: #fff; }
.footer-base nav { display: flex; flex-wrap: wrap; gap: 20px; }
.footer-social {
  display: inline-flex; align-items: center; gap: 9px;
  font: 300 11px/1 var(--sans); letter-spacing: var(--track-wide); text-transform: uppercase;
  color: var(--footer-text);
}
.footer-social:hover { color: #fff; }
.footer-social svg { width: 17px; height: 17px; fill: currentColor; }

/* --------------------------------------------------------- reveal on scroll */
/* Hidden ONLY once JS has confirmed it is running and can reveal it again.
   Hiding unconditionally in CSS means any script failure blanks the page. */
.js .reveal { opacity: 0; transform: translateY(14px); transition: opacity .9s ease, transform .9s cubic-bezier(.22,.61,.36,1); }
.js .reveal.is-in { opacity: 1; transform: none; }
/* Already on screen at load: show it, do not animate it. */
.js .reveal--instant { transition: none; }
@media (prefers-reduced-motion: reduce) { .js .reveal { opacity: 1; transform: none; } }

/* --------------------------------------------------------- print */
@media print {
  /* .reveal is opacity:0 until IntersectionObserver fires, and it never fires
     for content below the fold when printing — so the printed page lost its
     entire body. On Panel Options that was every section. */
  .js .reveal, .js .reveal.is-in { opacity: 1 !important; transform: none !important; }

  .site-header, .btn { display: none; }
  body { color: #000; }

  /* A printed sheet with no company name and no way to make contact is not a
     document a specifier can act on. Keep the footer's base bar only. */
  .site-footer { display: block; background: none; color: #000; border-top: 1px solid #000; }
  .footer-cols { display: none; }
  .footer-base { border: 0; padding-block: 12px; }
  .footer-base p, .footer-base a { color: #000; }
  .print-contact { display: block !important; font: 400 11px/1.5 var(--sans); }
  /* On paper the filter bar cannot be used, but the caption is the only thing
     identifying a floor — so the screen/print trade-off inverts. */
  .filters, .gallery-count, .floor-detail { display: none; }
  .gallery-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .gallery-item figcaption {
    position: static; width: auto; height: auto; clip: auto; white-space: normal;
    display: flex; justify-content: space-between; gap: 10px;
    padding-top: 8px; font: 400 10px/1.4 var(--sans); color: #000;
  }
  .gallery-item figcaption b {
    font-weight: 600; letter-spacing: .08em; text-transform: uppercase; font-size: 9px;
  }
}

/* --------------------------------------------------------- page body
   Editor output laid on a two-track grid: prose sits in a fixed measured
   column so every element shares one left edge (a `ch` measure would not —
   it scales with each element's own font size), while components break out
   to the full content width. */
.page-body {
  display: grid;
  grid-template-columns:
    [full-start] minmax(0, 1fr)
    [content-start] min(var(--measure), 100%) [content-end]
    minmax(0, 1fr) [full-end];
  color: var(--ink-soft);
}
.page-body > * { grid-column: content; max-width: none; margin-inline: 0; }

/* EVERY vertical gap in here is owned by the element BELOW it, and nothing
   sets a bottom margin. Grid items never collapse margins, so spacing declared
   on both sides is added together: a figure's 58px bottom margin met an h2's
   96px top margin and 48px top padding and produced a 202px hole, while the
   page's own sections sat 94px apart. Measured before this change, the gaps
   running down Panel Options were 154, 130, 114, 85, 76 and 44px.

   Owning each gap from one side makes it arithmetic anyone can follow, and it
   behaves identically in .page-body--plain, which is a block container where
   margins WOULD collapse — with no bottom margins, collapsing is a no-op. */
.page-body > * { margin-block: 0; }
.page-body > * + * { margin-top: var(--block-y); }
.page-body > p + p,
.page-body > p + ul,
.page-body > p + ol,
.page-body > ul + p,
.page-body > ol + p { margin-top: var(--flow-y); }

/* A new section of the page. The rule sits at 60% of the section gap, so there
   is more air above it than below and it binds to the heading it introduces. */
.page-body > h2 {
  margin-top: calc(var(--section-y) * .6);
  padding-top: calc(var(--section-y) * .4);
  border-top: 1px solid var(--rule-soft);
  color: var(--ink);
  text-wrap: balance;
}
/* The first heading needs no rule above it — the banner already separates it. */
.page-body > h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.page-body > h3 { color: var(--ink); text-wrap: balance; }

/* The gap below a heading belongs to the heading's own first line of content. */
.page-body > h2 + * { margin-top: clamp(18px, 2vw, 26px); }
.page-body > h3 + * { margin-top: clamp(12px, 1.4vw, 18px); }

.page-body .lede-line {
  font-family: var(--serif);
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.5;
  color: var(--ink);
  font-style: italic;
}

/* Components run to the full content width. */
.page-body > .timber-strip,
.page-body > .spec,
.page-body > .designs,
.page-body > .figure { grid-column: full; }
/* A single figure is given real scale but capped near the source's own 800px,
   so photography is generous without being upscaled into softness.
   justify-self, NOT margin-inline:auto — an auto margin on a grid item cancels
   stretch alignment, and the only child with intrinsic width is the caption, so
   each figure collapsed to the width of its own caption text (measured 247px,
   248px, 322px and 429px in a 922px column). Same trap as the split hero. */
.page-body > .figure--inset {
  grid-column: full;
  width: 100%;
  max-width: var(--wrap-mid);
  justify-self: center;
  margin-inline: 0;
}

.page-body ul, .page-body ol { padding-left: 1.2em; }
.page-body li { margin-bottom: .4em; }
/* Links in prose only — a .btn carries its own colour and would be painted
   ink-on-ink by this rule. */
.page-body a:not(.btn):not(.signpost):not(.timber) {
  color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--rule);
}
.page-body a:not(.btn):not(.signpost):not(.timber):hover { border-bottom-color: var(--ink); }
/* After the column split a quotation sits inside the text column. */
.page-body > blockquote,
.split__body > blockquote {
  padding-left: clamp(18px, 2.4vw, 30px);
  border-left: 1px solid var(--rule);
  font-family: var(--serif);
  font-size: clamp(19px, 1.9vw, 23px);
  font-style: italic;
  color: var(--ink);
}

/* The CTA is a band across the viewport, so it escapes the page wrapper. It
   closes the page, so it takes the full section gap rather than the block gap. */
.page-body > .cta {
  grid-column: full;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  margin-top: var(--section-y);
}

/* A centred prose page (About, legal, Contact) keeps the same measure but
   needs no breakout tracks. */
/* ~70 characters. 46rem set roughly 90, which is past comfortable reading. */
.page-body--plain { display: block; max-width: var(--measure); margin-inline: auto; }
.section--prose { padding-block: var(--section-y); }
.page-body--plain > * { max-width: none; }

/* --------------------------------------------------------- logo and icons */
.site-logo { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.site-logo .custom-logo-link { display: block; line-height: 0; }
.site-logo .custom-logo { width: auto; max-width: min(320px, 70vw); max-height: 72px; }
.site-logo__sub {
  font: 400 9.5px/1 var(--sans); letter-spacing: var(--track-wide);
  text-transform: uppercase; color: var(--muted);
}
.av-icon { width: 1.15em; height: 1.15em; vertical-align: -.2em; margin-right: .5em; flex: none; }
.footer-disclaimer {
  margin: 0; padding-block: 0 clamp(18px, 2.4vw, 26px);
  font: 300 12px/1.6 var(--sans); color: var(--footer-text); max-width: 72ch;
}

/* ==========================================================================
   ONE CONTAINER WIDTH, HEADING ABOVE, TEXT FULL WIDTH  (client decision)

   Every section uses the same 1180px container and content fills it; no
   element carries a narrower max-width of its own. Page content is still
   grouped server-side (inc/layout.php) into a row per section — the heading
   with its opening line, then its text — which keeps the rhythm between
   sections, but the two now stack. The client chose this over heading and text
   side by side, knowing lines run long at desktop widths.

   Single editorial figures take the full width too, so an 800px master is
   enlarged to fill the container on a wide screen.
   ========================================================================== */

.wrap--narrow { max-width: var(--wrap); }
.page-body,
.page-body--plain { display: block; max-width: none; margin-inline: 0; }
.measure-center,
.prose,
.hero__note,
.form-lead,
.contents,
.enquiry { max-width: none; }

.split { display: block; }
.split__head + .split__body { margin-top: clamp(16px, 2vw, 24px); }

/* A new section keeps the rhythm the single-column body used: a hairline at
   60% of the section gap, the heading 40% below it. */
.page-body > .split + .split:not(.split--cont),
.page-body > .split__full + .split:not(.split--cont) {
  margin-top: calc(var(--section-y) * .6);
  padding-top: calc(var(--section-y) * .4);
  border-top: 1px solid var(--rule-soft);
}
.page-body > .split:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.page-body > .split--cont,
.page-body > .split__full { margin-top: var(--block-y); }
.page-body > .split__full > * { margin-top: 0; }

.split__head > * { margin: 0; }
.split__head > * + * { margin-top: clamp(14px, 1.6vw, 20px); }
.page-body .split__head h2 { margin: 0; padding: 0; border: 0; color: var(--ink); text-wrap: balance; }
.split__head .standfirst { text-align: left; }
.split__head .hero__note { margin-inline: 0; }

.split__body > * { margin: 0; }
.split__body > * + * { margin-top: var(--flow-y); }
.split__body > h3 { margin-top: var(--block-y); color: var(--ink); }
.split__body > h3:first-child { margin-top: 0; }
.split__body > h3 + * { margin-top: clamp(10px, 1.2vw, 16px); }
.split__body > .figure { margin-top: var(--block-y); }
.split__body > .figure:first-child { margin-top: 0; }
.split__body > .figure + * { margin-top: var(--block-y); }
.split__body .figure--inset { max-width: none; width: 100%; margin-inline: 0; }

.split__body table { width: 100%; border-collapse: collapse; font-size: 14px; line-height: 1.5; }
.split__body th,
.split__body td { text-align: left; vertical-align: top; padding: 10px 14px 10px 0; border-bottom: 1px solid var(--rule-soft); }
.split__body th { font: 500 10.5px/1.4 var(--sans); letter-spacing: var(--track-mid); text-transform: uppercase; color: var(--muted); }

@media (max-width: 880px) {
}
@media (max-width: 600px) {
  .split__body table { display: block; overflow-x: auto; }
}

/* ==========================================================================
   FOOTER — closer to the Hudson reference the client supplied: white text on
   black, with a hairline above AND below each column heading.
   ========================================================================== */
.footer-col h2 { border-top: 1px solid var(--footer-rule); padding-top: 13px; }
.footer-col p,
.footer-col li,
.footer-col a { color: #fff; }
.footer-col a:hover { color: var(--footer-text); }
.footer-legal {
  flex-basis: 100%;
  margin: 0;
  font: 300 11px/1.6 var(--sans);
  letter-spacing: .04em;
  color: var(--footer-text);
}

/* ==========================================================================
   ICONS beside labels (chosen in each page's content box)
   ========================================================================== */
.facts__item dt,
.contact-detail dt { display: flex; align-items: center; }
.facts__item dt .av-icon { width: 22px; height: 22px; color: var(--muted); }
.contact-detail dt .av-icon { width: 16px; height: 16px; color: var(--muted); }

/* ==========================================================================
   DESIGN OPTIONS (Appearance → Customize → Site content)

   Each hook below reads a variable the Customizer can override. Every fallback
   is the design as built, so with nothing changed these rules render exactly
   what the rules above them did.
   ========================================================================== */

/* Typography */
body { font-size: var(--base-size, 16px); }
/* Text set larger than the body scales with it (--base-scale is the chosen
   size over 16px, printed alongside --base-size). */
.lede { font-size: calc(clamp(17px, 1.35vw, 19px) * var(--base-scale, 1)); }
.standfirst { font-size: calc(clamp(15px, 1.4vw, 18px) * var(--base-scale, 1)); }
h1, h2, h3, h4 { font-weight: var(--heading-weight, 400); }
h1 { font-size: calc(clamp(30px, 3.6vw, 46px) * var(--heading-scale, 1)); }
h2 { font-size: calc(clamp(25px, 2.6vw, 34px) * var(--heading-scale, 1)); }
h3 { font-size: calc(clamp(19px, 1.7vw, 23px) * var(--heading-scale, 1)); }
/* Page titles are headings too: the banner titles, the home statement and the
   letterspaced section titles take the same weight and scale. */
.page-banner__title,
.hero__statement .hero__title,
.page-title { font-weight: var(--heading-weight, 400); }
.page-banner__title { font-size: calc(clamp(18px, 2.1vw, 27px) * var(--heading-scale, 1)); }
.hero__statement .hero__title { font-size: calc(clamp(17px, 1.9vw, 25px) * var(--heading-scale, 1)); }
.page-title { font-size: calc(clamp(17px, 1.7vw, 21px) * var(--heading-scale, 1)); }

/* Buttons */
.btn,
.enquiry input[type="submit"] {
  background: var(--btn-bg, #141210);
  border-color: var(--btn-bg, #141210);
  color: var(--btn-text, #fff);
  border-radius: var(--radius-btn, 0);
}
.btn:hover,
.enquiry input[type="submit"]:hover { background: transparent; color: var(--btn-bg, #141210); }
.btn--ghost { background: transparent; color: var(--btn-bg, #141210); }
.btn--ghost:hover { background: var(--btn-bg, #141210); color: var(--btn-text, #fff); }

/* Label boxes, plates and pills on photographs. The hero plate and the slide
   controls keep their measured translucency (85% and 70%), mixed from the
   chosen colour. */
.signpost__label,
.timber h3,
.hero--full .slider__caption,
.filter[aria-pressed="true"] { background-color: var(--label-bg, #141210); }
.filter[aria-pressed="true"] { border-color: var(--label-bg, #141210); }
/* Only where color-mix() exists: elsewhere the solid and rgba rules earlier in
   this file stay in force, instead of these declarations dropping to transparent. */
@supports (background-color: color-mix(in srgb, red 50%, transparent)) {
  .signpost:hover .signpost__label,
  .timber:hover h3 { background-color: color-mix(in srgb, var(--label-bg, #000) 86%, #000); }
  .hero__statement p,
  .hero__statement .hero__title { background-color: color-mix(in srgb, var(--label-bg, #100e0c) 85%, transparent); }
  .hero--full .slider__controls { background-color: color-mix(in srgb, var(--label-bg, #100e0c) 70%, transparent); }
}

/* Footer */
.site-footer { background: var(--footer-bg, #141210); }

/* Corners */
.ratio,
.signpost__media,
.timber__media { border-radius: var(--radius-img, 0); }
.signpost__media,
.timber__media { overflow: hidden; }
/* The home slideshow runs edge to edge; rounded corners there would read as a
   gap at the sides of the window. */
.hero--full .ratio { border-radius: 0; }
.field input,
.field textarea,
.field select,
.enquiry input:not([type="submit"]),
.enquiry textarea,
.enquiry select,
.site-search__field { border-radius: var(--radius-field, 0); }
.signpost__label,
.timber h3,
.filter,
.hero__statement p,
.hero__statement .hero__title,
.hero--full .slider__controls,
.hero--full .slider__caption { border-radius: var(--radius-label, 0); }

/* Printing: the footer ground option must not print a dark band, and the
   disclaimer's quiet footer tone must not print grey. */
@media print {
  .site-footer { background: none; }
  .footer-disclaimer { color: #000; }
}

/* --------------------------------------------------------- pattern drawing
   One panel, its parts numbered, a legend beside it; stacked on a phone. */
.pattern-diagram {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(24px, 4.5vw, 72px);
  align-items: center;
  margin: 0;
}
.pattern-diagram__art svg { display: block; width: 100%; height: auto; max-height: 540px; }
.pattern-diagram__frame { fill: var(--paper-warm, #f7f4ef); stroke: var(--ink, #141210); stroke-width: 1.5; }
.pattern-diagram__field { fill: var(--paper-deep, #efeae2); }
.pattern-diagram__strip { fill: var(--paper, #fff); stroke: var(--ink, #141210); stroke-width: 1.1; }
.pattern-diagram__edge { fill: none; stroke: var(--ink, #141210); stroke-width: 1.5; }
.pattern-diagram__dimension { fill: none; stroke: var(--muted, #756d65); stroke-width: 1.2; }
.pattern-diagram__marker circle { fill: var(--ink, #141210); }
.pattern-diagram__marker text { fill: #fff; font: 500 13px/1 var(--sans); text-anchor: middle; }
.pattern-diagram__title {
  margin: 0 0 20px;
  font-family: var(--serif);
  font-size: calc(clamp(19px, 1.8vw, 24px) * var(--heading-scale, 1));
  color: var(--ink);
}
.page-body .pattern-diagram__legend ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 16px; }
.page-body .pattern-diagram__legend li { display: flex; align-items: center; gap: 14px; margin: 0; color: var(--ink-soft); }
.pattern-diagram__num {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--ink); color: #fff;
  font: 500 12px/1 var(--sans);
}
@media (max-width: 700px) {
  .pattern-diagram { grid-template-columns: minmax(0, 1fr); }
  .pattern-diagram__art { max-width: 420px; }
}

/* ==========================================================================
   UI/UX PASS — tuned toward the Hudson reference, on the Arbony look
   ========================================================================== */

/* Section headings in the page body: small letterspaced capitals in the serif,
   as Hudson sets its section titles and as this site's own section titles
   ("Where to begin") already are. The opening line beneath carries the voice,
   so a long run of stacked sections reads calmly instead of as a wall of large
   headings. */
.page-body .split__head h2 {
  font-size: calc(clamp(17px, 1.5vw, 20px) * var(--heading-scale, 1));
  letter-spacing: .16em;
  text-transform: uppercase;
  line-height: 1.45;
}
.page-body .split__head h2 + .lede-line { margin-top: clamp(10px, 1.2vw, 14px); }
/* Subheadings stay sentence case, sized so they never outweigh the section's
   own heading. */
.page-body .split__body > h3 { font-size: calc(clamp(17px, 1.45vw, 20px) * var(--heading-scale, 1)); }

/* Full-width lines are long at desktop sizes (the client's choice): a little
   more leading keeps the eye on the line. */
@media (min-width: 1024px) {
  .split__body p,
  .split__body li { line-height: 1.85; }
}

/* A portrait photograph across the full width stood about 1,400px tall and
   filled the screen twice over. It keeps a size that fits on one screen,
   centred in the column. */
.split__body .figure--inset:has(.ratio--portrait) { max-width: min(100%, 640px); margin-inline: auto; }

/* On a phone the in-page contents ran to six centred lines before any content.
   It becomes one row that scrolls sideways. */
@media (max-width: 700px) {
  .contents ul {
    display: flex;
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 0 22px;
    overflow-x: auto;
    padding-bottom: 6px;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .contents ul::-webkit-scrollbar { display: none; }
  .contents li { flex: none; }
}

/* The standfirst is the page's opening statement. Once heading and text
   stacked it read as one more line of body copy, so it is set in the serif, a
   step up — the same voice as the opening line under each section heading. */
.standfirst {
  font-family: var(--serif);
  letter-spacing: 0;
  font-size: calc(clamp(19px, 1.9vw, 24px) * var(--base-scale, 1));
  line-height: 1.45;
  color: var(--ink);
}

/* --------------------------------------------------------- home: sections beside photographs
   The opening three sections of the approved copy, each beside a large
   photograph, alternating sides, so the home page leads with photography rather
   than three full-width runs of text. Stacked, photograph first, on a phone. */
.features { display: grid; gap: var(--section-y); }
.feature {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(32px, 6vw, 96px);
  align-items: center;
}
.feature--reverse { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); }
.feature--reverse .feature__media { order: 2; }
.feature--text { display: block; }
.feature__media { margin: 0; }
.feature__media figcaption { margin-top: 12px; font: 400 12px/1.5 var(--sans); color: var(--muted); }
.feature__text > * { margin: 0; }
.feature__text > * + * { margin-top: var(--flow-y); }
.feature__text .standfirst + * { margin-top: clamp(16px, 2vw, 24px); }
.page-body .feature__text h2 {
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--ink);
  font-size: calc(clamp(17px, 1.5vw, 20px) * var(--heading-scale, 1));
  letter-spacing: .16em;
  text-transform: uppercase;
  line-height: 1.45;
  text-wrap: balance;
}
.feature__text h2 + .lede-line { margin-top: clamp(10px, 1.2vw, 14px); }
.feature__text h2 + p,
.feature__text .lede-line + p { margin-top: clamp(16px, 2vw, 24px); }
@media (max-width: 860px) {
  .feature,
  .feature--reverse { grid-template-columns: minmax(0, 1fr); gap: clamp(20px, 5vw, 32px); }
  .feature--reverse .feature__media { order: 0; }
}

/* ------------------------------------------------------ photograph viewer
   The dialog a gallery photograph opens: the photograph alone, as large as the
   screen allows, on a dark ground, between previous and next. A click on the
   dark space, the close button or Escape closes it. On a phone the arrows sit
   under the photograph, and a swipe moves through them. */
html:has(.floor-detail[open]) { overflow: hidden; }
.floor-detail {
  width: 100vw;
  height: 100dvh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: #11100e;
  color: #fff;
  overflow: hidden;
}
.floor-detail[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.floor-detail::backdrop { background: rgba(17, 16, 14, .6); }
.floor-detail__bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: clamp(8px, 1.4vw, 16px) clamp(8px, 1.6vw, 20px) 0 clamp(18px, 2.6vw, 34px);
}
.floor-detail__count {
  margin: 0;
  font: 400 11px/1 var(--sans);
  letter-spacing: var(--track-mid); text-transform: uppercase;
  color: rgba(255, 255, 255, .72);
}
.floor-detail__stage {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(8px, 2vw, 28px);
  min-height: 0;
  padding: clamp(8px, 1.6vw, 20px) clamp(10px, 2.2vw, 30px) clamp(20px, 3vw, 40px);
}
.floor-detail__body {
  display: flex; align-items: center; justify-content: center;
  height: 100%; min-height: 0;
}
.floor-detail__img {
  display: block;
  width: auto; height: auto;
  max-width: 100%;
  max-height: calc(100dvh - clamp(96px, 12vw, 136px));
  object-fit: contain;
  border-radius: var(--radius-img, 0);
}
/* A drawing is dark line art on nothing: in the viewer it lies on a sheet of
   paper, so its lines read against the dark ground. */
.floor-detail__img--drawing {
  box-sizing: border-box;
  padding: clamp(14px, 3vw, 44px);
  background: #fff;
}
.floor-detail__close,
.floor-detail__step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 50%;
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition: background-color var(--speed), border-color var(--speed);
}
.floor-detail__close { border-color: transparent; font: 300 32px/1 var(--sans); }
.floor-detail__step svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.4; }
.floor-detail__close:hover,
.floor-detail__step:hover:not(:disabled) { background: rgba(255, 255, 255, .12); border-color: rgba(255, 255, 255, .65); }
.floor-detail__step:disabled { opacity: .3; cursor: default; }
.floor-detail__close:focus-visible,
.floor-detail__step:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
@media (max-width: 700px) {
  .floor-detail__stage {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
    gap: 14px 0;
    padding: 8px 0 max(18px, env(safe-area-inset-bottom));
  }
  .floor-detail__body { grid-column: 1 / -1; grid-row: 1; }
  .floor-detail__img { max-height: calc(100dvh - 150px); }
  .floor-detail__step--prev { grid-column: 1; grid-row: 2; justify-self: start; margin-left: 18px; }
  .floor-detail__step--next { grid-column: 2; grid-row: 2; justify-self: end; margin-right: 18px; }
}

/* ------------------------------------------------------------ photo collage
   Two to six photographs from one section of About, a legal page or a new page,
   the first given the most room. A 12-column grid gives each count its own
   arrangement rather than a row of equal tiles, and every photograph fills its
   cell, so square, portrait and landscape sources sit flush together. */
.figure--collage { width: 100%; }
.page-body > .figure--collage { grid-column: full; }
.collage {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: clamp(150px, 17vw, 250px);
  gap: clamp(8px, 1.1vw, 16px);
}
.collage > .ratio { aspect-ratio: auto; height: 100%; border-radius: var(--radius-img, 0); }
.collage--2 > :nth-child(1) { grid-column: span 7; grid-row: span 2; }
.collage--2 > :nth-child(2) { grid-column: span 5; grid-row: span 2; }
.collage--3 > :nth-child(1) { grid-column: span 7; grid-row: span 2; }
.collage--3 > :nth-child(n+2) { grid-column: span 5; }
.collage--4 > :nth-child(1) { grid-column: span 6; grid-row: span 2; }
.collage--4 > :nth-child(2) { grid-column: span 6; }
.collage--4 > :nth-child(n+3) { grid-column: span 3; }
.collage--5 > :nth-child(1) { grid-column: span 6; grid-row: span 2; }
.collage--5 > :nth-child(n+2) { grid-column: span 3; }
.collage--6 > :nth-child(1) { grid-column: span 6; grid-row: span 2; }
.collage--6 > :nth-child(2) { grid-column: span 6; }
.collage--6 > :nth-child(3),
.collage--6 > :nth-child(4) { grid-column: span 3; }
.collage--6 > :nth-child(n+5) { grid-column: span 6; }

/* On a phone: the first photograph across the width, the rest in pairs, and an
   odd one out across the width as well. */
@media (max-width: 700px) {
  .collage { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 42vw; }
  .figure--collage .collage > * { grid-column: auto; grid-row: auto; }
  .figure--collage .collage > :first-child { grid-column: 1 / -1; grid-row: span 2; }
  .figure--collage .collage > :last-child:nth-child(even) { grid-column: 1 / -1; }
}

/* ============================================================ design review pass
   14 September: the page-by-page review's confirmed findings, applied together
   (README → History). Later rules here override earlier ones in the file. */

/* ---- Pinned header: navigation and search stay in reach on long pages. On a
   wide screen the wordmark scrolls away; on a phone the whole bar stays. The two
   measurements come from main.js. */
.js .site-header { position: sticky; top: calc(-1 * var(--header-scroll, 96px)); z-index: 40; }
@media (max-width: 820px) {
  .js .site-header { top: 0; }
  .js .nav-row.is-open { max-height: calc(100dvh - var(--header-pinned, 64px)); overflow-y: auto; }
}
.filters { top: var(--header-pinned, 37px); }
@media (min-width: 881px) { .contact-grid > aside { top: calc(var(--header-pinned, 37px) + clamp(16px, 3vw, 34px)); } }
html { scroll-padding-top: calc(var(--header-pinned, 37px) + 24px); }

/* ---- Header search field */
.site-search__field { border-color: var(--field-border); }
.js .site-search.is-open .site-search__field { border-color: var(--field-border); }
.js .site-search .site-search__field:focus { border-color: var(--ink); }
.site-search__field:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
@media (max-width: 820px) {
  .site-search { width: 100%; justify-content: flex-start; gap: 0; padding: 16px 0 20px; }
  .js .site-search__field { flex: 1 1 auto; width: auto; min-height: 44px; padding-inline: 12px; font-size: 16px; border-color: var(--field-border); }
  .site-search__button { flex: none; width: 44px; height: 44px; border: 1px solid var(--field-border); border-left: 0; color: var(--ink); }
  .field input, .field textarea, .field select,
  .enquiry input:not([type="submit"]):not([type="file"]), .enquiry textarea, .enquiry select { font-size: 16px; }
}

/* ---- Page banner: more of the photograph, and firmer backing for the title. */
.page-banner { height: clamp(240px, 40vh, 420px); }
.page-banner::after { background: radial-gradient(70% 60% at 50% 50%, rgba(16,14,12,.30), rgba(16,14,12,0) 100%), linear-gradient(180deg, rgba(16,14,12,.50) 0%, rgba(16,14,12,.30) 55%, rgba(16,14,12,.44) 100%); }

/* ---- Enquiry links land on the form with its heading in view. */
#enquiry { scroll-margin-top: clamp(72px, 7vw, 96px); }

/* ---- Image link tiles (Home, 404, no results, Panel Options timbers) */
.signpost__label h3::after, .timber h3::after { content: "\2192"; content: "\2192" / ""; display: inline-block; margin-left: .7em; transition: transform var(--speed); }
.signpost:hover .signpost__label h3, .signpost:focus-visible .signpost__label h3, .timber:hover h3, .timber:focus-visible h3 { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
.signpost:hover h3::after, .signpost:focus-visible h3::after, .timber:hover h3::after, .timber:focus-visible h3::after { transform: translateX(3px); }
.signpost .ratio img, .timber .ratio img { transition: opacity var(--speed); }
.signpost:hover .ratio img, .timber:hover .ratio img { opacity: .9; }
.timber p { padding-top: 0; margin-top: 12px; }
@media (max-width: 900px) { .timber-strip { row-gap: clamp(28px, 6vw, 40px); } }

/* ---- Search results, no results and page not found */
.search-form { display: flex; align-items: stretch; max-width: 34rem; margin: 0 auto clamp(28px, 3.6vw, 48px); border-bottom: 1px solid var(--field-border); }
.search-form:focus-within { border-bottom-color: var(--ink); }
.search-form input[type="search"] { flex: 1; min-width: 0; min-height: 48px; padding: 0 4px; border: 0; border-radius: var(--radius-field, 0); background: transparent; -webkit-appearance: none; appearance: none; font: 400 calc(clamp(17px, 1.6vw, 20px) * var(--base-scale, 1))/1.3 var(--serif); color: var(--ink); }
.search-form button { min-height: 48px; padding: 0 20px; border: 0; background: none; color: var(--ink); font: 500 11px/1 var(--sans); letter-spacing: var(--track-wide); text-transform: uppercase; cursor: pointer; }
.search-results__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px 32px; padding-bottom: clamp(18px, 2vw, 24px); border-bottom: 1px solid var(--rule); }
.search-results__head .standfirst { margin: 0; text-align: left; }
.search-results__head .search-form { flex: 0 1 26rem; margin: 0; }
.search-result + .search-result { padding-top: var(--block-y); border-top: 1px solid var(--rule-soft); }
.search-result mark { background: none; color: var(--ink); font-weight: 500; box-shadow: inset 0 -1px 0 currentColor; }
.cta .wrap > .btn:first-child { margin-top: 0; }

/* ---- Contents list on the legal pages */
.page-body .contents--list { max-width: none; margin-inline: 0; padding-block: clamp(22px, 2.6vw, 34px); border-block: 1px solid var(--rule-soft); }
.page-body > .split__full:first-child { margin-top: 0; }
.page-body > .split__full:has(> .contents--list) + .split { border-top: 0; padding-top: 0; margin-top: calc(var(--section-y) * .4); }
.contents__label { margin: 0 0 14px; font: 500 11px/1.5 var(--sans); letter-spacing: var(--track-wide); text-transform: uppercase; color: var(--muted); }
.page-body .contents--list ol { list-style: none; margin: 0; padding: 0; columns: 3 15rem; column-gap: clamp(24px, 4vw, 56px); counter-reset: toc; }
.page-body .contents--list li { margin: 0; break-inside: avoid; counter-increment: toc; }
.page-body .contents--list a:not(.btn):not(.signpost):not(.timber) { display: flex; gap: .9em; padding-block: 6px; font: 300 15px/1.45 var(--sans); letter-spacing: 0; text-transform: none; color: var(--ink-soft); text-decoration: none; border-bottom: 0; }
.page-body .contents--list a:not(.btn):not(.signpost):not(.timber):hover { color: var(--ink); text-decoration: underline; text-underline-offset: .24em; }
.page-body .contents--list a::before { content: counter(toc, decimal-leading-zero); flex: none; font-size: 11px; line-height: 2; letter-spacing: var(--track-mid); color: var(--muted); }
@media (max-width: 700px) {
  .page-body .contents--list ol { columns: 1; }
  .page-body .contents--list a:not(.btn):not(.signpost):not(.timber) { align-items: center; min-height: 44px; padding-block: 8px; }
}

/* ---- Home */
.hero__statement .hero__title { font-size: calc(clamp(20px, 2.3vw, 32px) * var(--heading-scale, 1)); max-width: min(100%, 20em); padding: .7em 1.4em; text-wrap: balance; }
@media (max-width: 700px) { .hero__statement .hero__title { font-size: calc(19px * var(--heading-scale, 1)); letter-spacing: .1em; padding: .7em 1em; } }
@media (min-width: 1100px) {
  .feature { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: clamp(40px, 5vw, 72px); }
  .feature--reverse { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
}

/* ---- Panel Options */
@media (max-width: 700px) {
  .contents { padding-block: 6px; }
  .contents ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px; justify-content: stretch; overflow: visible; padding-bottom: 0; }
  .contents li { flex: initial; min-width: 0; }
  .contents a { display: flex; align-items: center; min-height: 44px; padding-block: 6px; line-height: 1.35; border-bottom: 0; }
}
.spec__item:last-child { border-bottom: 0; }
@media (min-width: 560px) {
  .spec__item:nth-child(odd):nth-last-child(-n+2), .spec__item:nth-child(odd):nth-last-child(-n+2) ~ .spec__item { border-bottom: 0; }
}
@media (min-width: 701px) {
  .pattern-diagram { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); }
  .pattern-diagram__art svg { max-height: 640px; }
}

/* ---- Gallery */
.gallery-grid { row-gap: clamp(36px, 3.6vw, 56px); }
.gallery-item figcaption { margin-top: 14px; }
@media (max-width: 520px) { .gallery-grid { row-gap: 32px; } }
@media (max-width: 620px) {
  .filters { flex-wrap: nowrap; justify-content: flex-start; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; margin-inline: calc(var(--gutter) * -1); padding: 4px var(--gutter); }
  .filters::-webkit-scrollbar { display: none; }
  .filter { flex: 0 0 auto; min-height: 44px; padding: 0 14px; }
}
.gallery-item__open { scroll-margin-top: 72px; }
/* The whole tile opens the photograph, caption included. */
.gallery-item__open::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.gallery-item:hover .gallery-item__title { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }

/* ---- Contact */
.enquiry .wpcf7 form .wpcf7-response-output { margin: 18px 0 0; padding: 15px 19px; border: 1px solid var(--rule); background: var(--paper-warm); color: var(--ink); }
.enquiry .wpcf7 form.invalid .wpcf7-response-output, .enquiry .wpcf7 form.unaccepted .wpcf7-response-output, .enquiry .wpcf7 form.spam .wpcf7-response-output, .enquiry .wpcf7 form.failed .wpcf7-response-output, .enquiry .wpcf7 form.aborted .wpcf7-response-output, .enquiry .wpcf7 form.payment-required .wpcf7-response-output { border-color: #9d3b2f; color: #9d3b2f; background: var(--paper); }
.enquiry .wpcf7 form.sent .wpcf7-response-output { border-color: var(--ink); background: var(--paper-warm); color: var(--ink); }
.enquiry input:not([type="submit"]):focus-visible, .enquiry textarea:focus-visible, .enquiry select:focus-visible { box-shadow: inset 0 0 0 1px var(--ink); }
.enquiry .field-group { min-width: 0; margin: clamp(28px, 3.4vw, 40px) 0 0; padding: clamp(22px, 2.6vw, 30px) 0 0; border: 0; border-top: 1px solid var(--rule-soft); }
.enquiry .field-group > legend { float: left; width: 100%; margin: 0 0 18px; padding: 0; font: 500 10.5px/1.4 var(--sans); letter-spacing: var(--track-wide); text-transform: uppercase; color: var(--muted); }
.enquiry .field-group > legend + * { clear: both; }
.contact-detail--reach { display: none; }
@media (max-width: 880px) {
  .contact-detail--reach { display: block; margin: 0 0 var(--section-y-s); padding-bottom: 2px; border-bottom: 1px solid var(--rule-soft); }
  .contact-grid > aside .contact-detail__reach { display: none; }
}
.contact-note { margin: 4px 0 0; padding-top: 20px; border-top: 1px solid var(--rule-soft); font-size: 15px; line-height: 1.7; color: var(--ink-soft); }
.field--row { gap: 20px 22px; margin-bottom: 20px; }
.field--row > .field { margin-bottom: 0; }
.enquiry input[type="file"] { padding: 6px 7px; font-size: 13.5px; color: var(--ink-soft); }
.enquiry input[type="file"]::file-selector-button { height: 34px; margin-right: 14px; padding: 0 16px; border: 1px solid var(--btn-bg, #141210); border-radius: var(--radius-btn, 0); background: var(--paper); color: var(--btn-bg, #141210); font: 500 10.5px/1 var(--sans); letter-spacing: var(--track-wide); text-transform: uppercase; cursor: pointer; transition: background-color var(--speed), color var(--speed); }
.enquiry input[type="file"]::file-selector-button:hover { background: var(--btn-bg, #141210); color: var(--btn-text, #fff); }

/* ---- Footer */
.footer-col ul + .group { margin-top: 22px; }
.footer-col__label { display: block; margin-bottom: 4px; font: 400 10.5px/1.4 var(--sans); letter-spacing: var(--track-wide); text-transform: uppercase; color: var(--footer-text); }
.footer-social svg { width: 17px; height: 17px; margin-right: 0; fill: none; stroke: currentColor; }
.footer-social svg circle:last-child { fill: currentColor; }
@media (max-width: 560px) {
  .footer-col:nth-child(2) ul, .footer-col:nth-child(3) ul { column-count: 2; column-gap: 20px; }
  /* Only the two-up link lists get the taller rows; the Enquiries column keeps
     its own spacing, so the footer is not made longer on a phone. */
  .footer-col:nth-child(2) li, .footer-col:nth-child(3) li { break-inside: avoid; }
  .footer-col:nth-child(2) li + li, .footer-col:nth-child(3) li + li { margin-top: 0; }
  .footer-col:nth-child(2) li a, .footer-col:nth-child(3) li a { display: inline-block; padding-block: 11px; }
}

/* ---- Links, wrapping, small type */
.page-body a:not(.btn):not(.signpost):not(.timber) { border-bottom: 0; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .24em; text-decoration-color: var(--field-border); }
.page-body a:not(.btn):not(.signpost):not(.timber):hover { text-decoration-color: var(--ink); }
.search-result > h2 a:not(.btn):not(.signpost):not(.timber) { text-decoration-color: transparent; }
.search-result > h2 a:not(.btn):not(.signpost):not(.timber):hover { text-decoration-color: var(--ink); }
.standfirst, .page-body .lede-line { text-wrap: balance; }
.split__body p, .split__body li { text-wrap: pretty; }
.feature__text > p:not([class]) { text-wrap: pretty; }
.cta .prose { text-wrap: balance; }
.wordmark small { font-size: 10.5px; }
@media (max-width: 400px) { .wordmark small { font-size: 9.5px; letter-spacing: var(--track-mid); } }
@media (max-width: 820px) {
  .main-nav .current-menu-item > a, .main-nav .current_page_item > a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 6px; }
}
@media (max-width: 400px) {
  .js .nav-toggle { padding: 8px 12px; margin-right: -12px; }
}

/* ---- Print: the title in black, without the photograph band. */
@media print {
  main > .page-banner { height: auto; overflow: visible; background: none; }
  main > .page-banner .page-banner__img, main > .page-banner::after { display: none; }
  main > .page-banner .page-banner__title { position: static; display: block; padding: 0 var(--gutter) 12pt; color: #000; text-shadow: none; text-align: left; }
  main > .page-banner + .section, main > .page-banner + article.section { padding-top: 0; }
}

/* The drawings viewer names the drawing beside its count, so the line may wrap. */
.floor-detail--drawings .floor-detail__count { line-height: 1.5; padding-right: 12px; }
