/* ============================================================
   HEADED NORTH — Direction D3 · Immersive Dark
   Deep ink navy, full-bleed cinematic photography, cream text.
   Manrope 200/300 display · 400 body. Mustard = micro-accents.
   ============================================================ */

:root {
  /* ---- Official brand palette, from Headed North_Logo Book.pdf ----
     #1f3650 navy · #99524e brick · #f6c669 mustard · #aec0ce blue-grey
     #65615e warm grey · #d3a19e blush · #f6ddb9 sand · #dde6eb pale blue
     #f4f0ed off-white                                                    */
  --brand-navy: #1f3650;
  --brand-brick: #99524e;
  --brand-mustard: #f6c669;
  --brand-bluegrey: #aec0ce;
  --brand-grey: #65615e;
  --brand-blush: #d3a19e;
  --brand-sand: #f6ddb9;
  --brand-paleblue: #dde6eb;
  --brand-offwhite: #f4f0ed;

  /* Light direction — white base, beige accents, brand navy type */
  --ink: #FFFFFF;               /* base surface */
  --ink-raised: #f4f0ed;        /* raised panels — brand off-white */
  --ink-deep: #ece4d9;          /* footer, a shade deeper for hierarchy */
  --cream: #1f3650;             /* primary type — brand navy */
  --cream-dim: rgba(31, 54, 80, 0.78);
  --cream-faint: rgba(31, 54, 80, 0.52);
  --slate: #65615e;             /* secondary type — brand warm grey */
  --mustard: #8f6413;           /* Brand mustard darkened until it clears WCAG AA as text.
                                   #a97516 was an earlier attempt at the same thing but only
                                   reached 4.01:1 on white and 3.53:1 on cream, short of the
                                   4.5:1 that 11px overlines need. This is 5.25:1 and 4.63:1.
                                   Used as text in ~36 rules, so it has to pass on both bands.
                                   --mustard-bright below stays for type over dark photos. */
  --brick: #99524e;             /* brand brick */
  --hairline: rgba(31, 54, 80, 0.13);
  --text-strong: #1f3650;
  --text-mute: rgba(31, 54, 80, 0.62);
  --ink-on-cream: rgba(31, 54, 80, 0.62);
  --ink-on-cream-strong: rgba(31, 54, 80, 0.85);
  /* Always-light type, for use over photography and brick fills */
  --on-dark: #f4f0ed;
  --on-dark-dim: rgba(244, 240, 237, 0.86);
  --on-dark-faint: rgba(244, 240, 237, 0.62);
  --mustard-bright: #f6c669;    /* full brand mustard, over dark photography */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --wrap: 1240px;
  --gutter: clamp(1.25rem, 5vw, 4rem);
}

/* ---------- Reset ---------- */

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

html {
  scroll-behavior: smooth;
  overflow-x: clip;
}

body {
  background: var(--ink);
  color: var(--cream);
  font-family: "Manrope", "Helvetica Neue", Arial, system-ui, sans-serif;
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

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

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; }

::selection { background: var(--mustard); color: var(--ink); }

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Consistent warm dark grade on every photograph */
.graded {
  filter: brightness(0.9) saturate(0.94) sepia(0.07) contrast(1.04);
}

/* ---------- Typography ---------- */

.overline {
  display: block;
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--slate);
}

.overline--accent { color: var(--mustard); }

.display {
  font-weight: 200;
  line-height: 1.12;
  letter-spacing: -0.005em;
}

h1.display { font-size: clamp(2.6rem, 6.5vw, 5.2rem); }
h2.display { font-size: clamp(2rem, 4.4vw, 3.4rem); }
h3.display { font-size: clamp(1.5rem, 3vw, 2.2rem); font-weight: 300; }

.lede {
  font-weight: 300;
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  line-height: 1.65;
  color: var(--cream-dim);
}

.body-copy {
  color: var(--cream-dim);
  max-width: 62ch;
}

/* Studio blurb — what the practice actually covers */
.disciplines {
  margin-top: 1.8rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.disciplines span {
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brand-navy);
  border: 1px solid var(--hairline);
  padding: 0.45rem 0.9rem;
}

/* Studio blurb — closing statement and the small factual line beneath it */
.creed {
  margin-top: 1.5rem;
  font-size: 1.02rem;
  font-weight: 500;
  color: var(--brand-navy);
  letter-spacing: 0.01em;
}

.kicker {
  margin-top: 1.8rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.86rem;
  color: var(--slate);
  max-width: 52ch;
}

.rule {
  width: 56px;
  height: 1px;
  background: var(--mustard);
  border: 0;
  margin: 0;
}

/* Visually hidden, still read by screen readers and search engines */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 300;
  background: var(--mustard);
  color: var(--ink);
  padding: 0.6rem 1.2rem;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
}
.skip:focus { left: 0; }

/* ---------- Header ---------- */

.site-head {
  position: relative;
  z-index: 120;
  background: var(--ink);
  box-shadow: 0 1px 0 rgba(22, 41, 63, 0.07), 0 12px 24px -20px rgba(0, 0, 0, 0.28);
  padding-block: clamp(1.2rem, 2.2vw, 1.6rem);
}

/* Two-tier header: brand + CTA on top row, nav centered on its own row below */
.head-row-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  max-width: 1560px;
  padding-bottom: 1.1rem;
}

.head-row-bottom {
  max-width: 1560px;
  border-top: 1px solid var(--hairline);
  padding-top: 0.95rem;
  display: flex;
  justify-content: center;
}

.brand,
.brand-h {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-shrink: 0;
}

.head-top-right {
  display: flex;
  align-items: center;
  gap: 1.3rem;
}

.brand-emblem { height: 42px; width: auto; display: block; }

.brand-word { display: flex; flex-direction: column; line-height: 1.2; }

/* Wordmark — brand face is Helvetica Neue Black Condensed (licensed, not a webfont);
   Anton is the closest freely-servable match to the logo lettering. */
.brand-name {
  font-family: "HelveticaNeueLTStd-BlkCn", "Helvetica Neue Condensed",
               "HelveticaNeue-BlackCond", Anton, Impact, sans-serif;
  font-weight: 400;
  font-size: 1.18rem;
  letter-spacing: 0.055em;
  color: var(--text-strong);
  white-space: nowrap;
}

.brand-sub {
  font-weight: 500;
  font-size: 0.6rem;
  letter-spacing: 0.4em;
  color: var(--text-mute);
  text-transform: lowercase;
  margin-top: 0.18rem;
}

.btn-contact {
  display: inline-block;
  background: var(--brick);
  color: var(--on-dark);
  padding: 0.75rem 1.6rem;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: background 0.3s var(--ease);
}

.btn-contact:hover { background: #7A332D; }

.site-nav {
  justify-self: start;
  display: flex;
  align-items: center;
  gap: clamp(1.2rem, 2.2vw, 2.2rem);
}

.site-nav a {
  position: relative;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-on-cream);
  padding-block: 0.4rem;
  transition: color 0.35s var(--ease);
}

.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--brick);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.4s var(--ease);
}

.site-nav a:hover { color: var(--ink); }
.site-nav a:hover::after { transform: scaleX(1); }

.site-nav a.is-active { color: var(--brick); }
.site-nav a.is-active::after { transform: scaleX(1); }

/* Hamburger */
.nav-toggle {
  display: none;
  position: relative;
  z-index: 220;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid rgba(22, 41, 63, 0.2);
  cursor: pointer;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
}

.nav-toggle span {
  display: block;
  width: 20px;
  height: 1px;
  /* --ink is #FFFFFF in this palette, so these bars were white on a white
     header - an empty box. The button was also pushed off-screen, so the
     invisible icon went unnoticed. Match the brand text instead. */
  background: var(--cream);
  transition: transform 0.35s var(--ease), opacity 0.35s var(--ease);
}

body.nav-open .nav-toggle span:first-child { transform: translateY(4px) rotate(45deg); }
body.nav-open .nav-toggle span:last-child { transform: translateY(-4px) rotate(-45deg); }

body.nav-open .nav-toggle { border-color: var(--hairline); }

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

  .site-nav {
    position: fixed;
    inset: 0;
    /* The base rule sets justify-self:start for the desktop header row. On a
       fixed box, any box-alignment value other than normal/stretch makes it
       size to fit-content instead of honouring inset:0 - which collapsed this
       full-screen overlay to 126px, the width of the word PROJECTS, and made
       the tracked-out links look broken. Reset it here. */
    justify-self: stretch;
    z-index: 200;
    flex-direction: column;
    justify-content: center;
    gap: 2.2rem;
    background: var(--ink);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.45s var(--ease), visibility 0s 0.45s;
  }

  body.nav-open .site-nav {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.45s var(--ease);
  }

  .site-nav a {
    font-size: 1rem;
    letter-spacing: 0.34em;
  }

  /* Touch devices have no hover, so tapping a link in the overlay gave no
     feedback at all - only the current page showed its brick underline.
     Mirror the hover state onto :active so a tap lights the link up. */
  .site-nav a:active { color: var(--ink); }
  .site-nav a:active::after { transform: scaleX(1); }

  body.nav-open { overflow: hidden; }
}

/* The header row is brand + (Talk to us + hamburger). Below about 450px those
   add up to 409px against 335px of usable width, and the flex row pushes the
   LAST item out - so the hamburger sat at x=385 in a 375px viewport and the
   menu could not be opened at all. The CTA is the redundant one on a phone:
   Contact is in the overlay and the footer carries its own Talk to us. */
@media (max-width: 560px) {
  .head-row-top .btn-contact { display: none; }
}

/* ---------- Full-bleed cinematic bands ---------- */

.slide {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 82vh;
  isolation: isolate;
  overflow: hidden;
}

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

.slide-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg,
      rgba(22, 41, 63, 0.62) 0%,
      rgba(22, 41, 63, 0.18) 34%,
      rgba(22, 41, 63, 0.28) 62%,
      rgba(18, 35, 58, 0.82) 100%);
}

.slide-content {
  position: relative;
  width: 100%;
  padding-block: clamp(3rem, 8vh, 5.5rem);
}

/* Photo bands keep a dark scrim, so their type stays light regardless of page palette */
.slide-content,
.slide-content .display { color: var(--on-dark); }
.slide-content .overline { margin-bottom: 1.2rem; color: var(--on-dark-faint); }
.slide-content .display { max-width: 16ch; }
.slide-content p {
  max-width: 46ch;
  margin-top: 1.4rem;
  color: var(--on-dark-dim);
}

.slide-content .btn-ghost {
  border-color: var(--on-dark-dim);
  color: var(--on-dark);
}

.slide-content .btn-ghost:hover,
.slide-content .btn-ghost:focus-visible {
  border-color: var(--mustard-bright);
  color: var(--mustard-bright);
}

.slide-content .link-arrow { color: var(--on-dark); }

a.slide:hover .slide-content .link-arrow,
a.slide:focus-visible .slide-content .link-arrow,
.slide-content .link-arrow:hover { color: var(--mustard-bright); }

.slide-content .case-meta,
.slide--next .overline { color: var(--mustard-bright); }

/* Hero — fills the viewport below the header, so its caption bar stays in view.
   --head-h is measured and set by the inline script on each page. */
.slide--hero {
  min-height: calc(100vh - var(--head-h, 150px));
  min-height: calc(100svh - var(--head-h, 150px));
}

/* Hero headline runs narrative-length — give it more room than interior pages */
.slide--hero .slide-content .display { max-width: 17ch; }

@media (min-width: 640px) {
  .slide--hero .slide-content .display { max-width: 15ch; }
}

/* Hero photo carousel — crossfading full-bleed stack */
.hero-carousel { position: absolute; inset: 0; z-index: -2; }

.hero-carousel .slide-img {
  opacity: 0;
  transition: opacity 1.8s var(--ease);
}

.hero-carousel .slide-img.is-active { opacity: 1; }

/* Focal points — the hero fills the full viewport, so on very wide/short
   screens the crop cuts top-bottom, and on tall phone screens it cuts
   left-right. Each image is anchored on its actual subject so neither
   extreme slices through it. */
.hero-carousel .slide-img[src*="hero-aerosky-lanterns"] { object-position: 38% 42%; }
.hero-carousel .slide-img[src*="hero-bokpara-exterior"] { object-position: 46% 55%; }
.hero-carousel .slide-img[src*="hero-windsong-cafe"] { object-position: 50% 55%; } /* seating sits just below centre */
.hero-carousel .slide-img[src*="hero-aerosky-planes"] { object-position: 42% 40%; }
.hero-carousel .slide-img[src*="hero-royal-highness"] { object-position: 50% 55%; } /* signage and desk are centred */
.hero-carousel .slide-img[src*="hero-bokpara-office"] { object-position: 48% 40%; }
/* Legends needs no focal value: its 9:16 portrait crop is cut on the propeller,
   and the landscape file is close enough to the band's aspect that centring it
   loses nothing. */

/* Below 680px each slide swaps to a purpose-cut 9:16 portrait file via <picture>.
   Those crops are already composed on their subject, so the focal values above -
   which exist to rescue a LANDSCAPE file from a portrait band - would now push a
   correct composition off centre. The [src*=] selectors still match, because src
   keeps pointing at the landscape file even when currentSrc is the portrait one,
   so this has to repeat their specificity to win. */
@media (max-width: 680px) {
  .hero-carousel .slide-img[src*="hero-"] { object-position: 50% 50%; }
}

/* ---------- Hero drag navigation ----------
   The carousel crossfades, so there is no track to slide. During a drag we
   translate the active slide and bring its neighbour in behind the pointer,
   then either commit to it or snap back on release. */
.slide--hero { touch-action: pan-y; }
.slide--hero.has-cue { cursor: none; }
.slide--hero.has-cue .hero-bar, .slide--hero.has-cue .hero-bar * { cursor: auto; }

.hero-carousel .slide-img {
  will-change: transform, opacity;
  -webkit-user-drag: none;
  -webkit-user-select: none;
  user-select: none;
}

/* Held only while a drag is actually in progress */
.slide--hero.is-locked, .slide--hero.is-locked * { -webkit-user-select: none; user-select: none; }
.hero-carousel.is-dragging .slide-img { transition: none; }
.hero-carousel.is-settling .slide-img { transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }

/* The pill that replaces the cursor over the hero */
.hero-drag-cue {
  position: fixed;
  top: 0; left: 0;
  z-index: 7;
  pointer-events: none;
  width: 78px; height: 78px;
  margin: -39px 0 0 -39px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.5);
  background: rgba(255, 255, 255, 0.10);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: #fff;
  font-size: 0.58rem; letter-spacing: 0.2em; text-transform: uppercase;
  opacity: 0; transform: scale(0.55);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease), background 0.3s var(--ease);
}
.hero-drag-cue.is-on { opacity: 1; transform: scale(1); }
.hero-drag-cue.is-grabbing { transform: scale(0.84); background: rgba(255, 255, 255, 0.22); }

/* Touch devices get the native swipe and no cursor pill */
@media (hover: none) {
  .hero-drag-cue { display: none; }
  .slide--hero.has-cue { cursor: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-drag-cue { transition: opacity 0.3s linear; }
}

/* Replaced by .slide-scrim on 2026-09-04. The old .hero-veil covered only the bottom
   46% because it held nothing but the caption; the hero now carries the h1, so it uses
   the full-height scrim that contact.html already uses. */

/* Hero headline and standfirst. No .reveal here: this is the first thing the page has
   to say, so it must be legible on the first frame rather than fading in. */
.hero-title {
  font-size: clamp(2.1rem, 8.2vw, 4.6rem);
  text-wrap: balance;
}

/* The headline sits on the carousel drag surface, so a horizontal swipe across it must
   move the reel rather than start a text selection. Same treatment .slide-img already has. */
.hero-title,
p.hero-sub {
  -webkit-user-select: none;
  user-select: none;
}

p.hero-sub {
  margin-top: 0.9rem;
  max-width: 44ch;
  font-style: italic;
  font-weight: 300;
  font-size: clamp(0.95rem, 3.6vw, 1.2rem);
  line-height: 1.55;
  color: var(--on-dark-dim);
}

.hero-bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding-bottom: clamp(1.6rem, 3.4vw, 2.8rem);
  display: flex;
  /* The headline holds the bottom-left of the hero, so the caption and the dashes
     anchor right rather than reading as two more lines of the same column. */
  flex-direction: column;
  align-items: flex-end;
  gap: 1.1rem;
}

/* The caption fades with the photograph rather than swapping on the first frame — see
   capSwap() in index.html, which does the text swap at the midpoint of the image fade.
   0.32s suits both crossfades: half of the 1.8s idle fade, and half of the 0.7s one that
   runs when a drag settles. */
.hero-cap { display: block; max-width: 30ch; text-align: right; transition: opacity 0.32s var(--ease); }
.hero-cap.is-swapping { opacity: 0; }

.hero-cap-name {
  display: block;
  font-size: clamp(1.05rem, 1.7vw, 1.45rem);
  font-weight: 300;
  line-height: 1.25;
  color: #fff;
  transition: color 0.3s var(--ease);
}

.hero-cap-meta {
  display: block;
  margin-top: 0.4rem;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}

.hero-cap:hover .hero-cap-name { color: var(--brand-mustard); }

.hero-ctrl {
  display: flex;
  align-items: center;
  gap: clamp(1.2rem, 2.6vw, 2.2rem);
  flex-shrink: 0;
}

/* Dashes double as a progress track — the active one fills as the slide plays */
.hero-dashes { display: flex; gap: 0.5rem; align-items: center; }

.hero-dashes button {
  width: clamp(22px, 2.4vw, 36px);
  height: 3px;
  padding: 0;
  border: 0;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.32);
  overflow: hidden;
  transition: background 0.3s var(--ease);
}

.hero-dashes button:hover { background-color: rgba(255, 255, 255, 0.7); }

.hero-dashes button i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--brand-mustard);
}

.hero-dashes button.is-on i { animation: heroDashFill 5s linear forwards; }

@keyframes heroDashFill { from { width: 0; } to { width: 100%; } }

@media (max-width: 680px) {
  /* On a phone the band splits left and right across one row: the dashes anchor the
     left edge and the caption the right, so the pair reads against the headline
     instead of stacking underneath it. */
  .hero-bar {
    flex-direction: row-reverse;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.9rem;
  }

  .hero-cap { flex: 1 1 auto; min-width: 0; }
  /* The rail is hidden on a phone. It earns its place on a desktop, where the active
     bar fills as the slide plays and doubles as a progress track - but on a phone a
     swipe already advances the reel, and seven bars sat competing with the caption for
     the same corner. Hidden rather than removed: the buttons leave the tab order with
     the rail, so nothing is announced that cannot be reached. */
  .hero-ctrl { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-dashes button.is-on i { animation: none; width: 100%; }
}

/* Retuned 2026-09-04 when the h1 moved into the hero. The middle stops carry the
   headline, which sits between roughly 48% and 70% of the band, and has to stay legible
   on the pale daylight slides (Bokpara Office) as well as the dark interiors. */
.slide--hero .slide-scrim {
  background:
    linear-gradient(180deg,
      rgba(22, 41, 63, 0.6) 0%,
      rgba(22, 41, 63, 0.14) 30%,
      rgba(22, 41, 63, 0.46) 58%,
      rgba(18, 35, 58, 0.88) 100%);
}

/* Clears the caption band, which the hero now sits above rather than owning alone.
   --hero-bar-h is the band's measured height, written by the inline script on the page,
   because the band grows and shrinks with the caption. The fallback is the tallest the
   band gets without JS. */
.slide--hero .slide-content {
  padding-bottom: calc(var(--hero-bar-h, 7rem) + clamp(1.5rem, 4vh, 3rem));
}

/* Shorter interior-page hero band */
.slide--page {
  min-height: 58vh;
  min-height: 58svh;
}

.slide--page .display { font-size: clamp(2.3rem, 5vw, 4.2rem); }

/* Image-less page opener — header now sits in normal flow above it */
.page-head { padding-top: clamp(3rem, 7vh, 4.5rem); }

/* Whole-band links (featured projects, next-project) */
a.slide { display: flex; }

a.slide .slide-img {
  transition: transform 1.6s var(--ease);
}

a.slide:hover .slide-img,
a.slide:focus-visible .slide-img {
  transform: scale(1.035);
}

/* ---------- Buttons & text links ---------- */

.btn-ghost {
  display: inline-block;
  margin-top: 2.2rem;
  padding: 1rem 2.4rem;
  border: 1px solid var(--cream-dim);
  color: var(--cream);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  background: transparent;
  cursor: pointer;
  transition: border-color 0.4s var(--ease), color 0.4s var(--ease), background 0.4s var(--ease);
}

.btn-ghost:hover,
.btn-ghost:focus-visible {
  border-color: var(--mustard);
  color: var(--mustard);
}

/* 28px tall before. The padding takes it to 44 and the margin gives back what the
   padding added, so it sits exactly where it did. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding-block: 8px;
  margin-top: 1.5rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--cream);
  transition: color 0.35s var(--ease);
}

.link-arrow::after {
  content: "\2192";
  font-size: 1rem;
  letter-spacing: 0;
  transition: transform 0.4s var(--ease);
}

.link-arrow:hover,
a:hover > .link-arrow,
a:focus-visible > .link-arrow {
  color: var(--mustard);
}

.link-arrow:hover::after,
a:hover > .link-arrow::after,
a:focus-visible > .link-arrow::after {
  transform: translateX(6px);
}

/* ---------- Quiet navy interludes ---------- */

.interlude {
  padding-block: clamp(5.5rem, 13vh, 9.5rem);
}

.interlude--raised { background: var(--ink-raised); }

.interlude .overline { margin-bottom: 1.4rem; }

.interlude-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
}

.interlude-grid .rule { margin-top: 1.8rem; }
.sec-head .rule { margin-top: 1.8rem; }
.faq-split-head .rule { margin-top: 1.8rem; }
.stage-grid-head .rule { margin-top: 1.8rem; }
.cta-panel .rule { margin-top: 1.4rem; margin-bottom: 1.8rem; }

/* ---------- Home: selected work (cards + register) ---------- */


.sec-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  margin-bottom: clamp(2rem, 4vw, 3.2rem);
}

.pcards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(1.6rem, 3.5vw, 3rem) clamp(1.6rem, 3vw, 2.6rem);
}

/* Drawings sit inside a soft field rather than being cropped to fill it */
.pcard-media {
  background: var(--brand-offwhite);
  border: 1px solid var(--hairline);
  aspect-ratio: 3 / 2;   /* suits the landscape sections and axonometrics */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 2.2vw, 1.8rem);
  overflow: hidden;
}

.pcard-media img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  transition: transform 1.2s var(--ease);
  mix-blend-mode: multiply;   /* drops the drawing's white paper into the field */
}

.pcard:hover .pcard-media img { transform: scale(1.03); }

.pcard-body { padding-top: 1.1rem; }

.pcard h3 {
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  font-weight: 300;
  color: var(--brand-navy);
  transition: color 0.3s var(--ease);
}

.pcard:hover h3 { color: var(--brand-brick); }

.pcard .pmeta,
.prowi .pmeta {
  display: block;
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--slate);
}

.pcard .pmeta { margin-top: 0.45rem; }
.pcard p { margin-top: 0.8rem; font-size: 0.92rem; color: var(--cream-dim); max-width: 48ch; }

/* Register label — small caps with a rule running off to the right */
.reg-label {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  margin: clamp(3rem, 6vw, 4.5rem) 0 0.4rem;
}

.reg-label span {
  font-size: 0.68rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--slate);
  white-space: nowrap;
}

.reg-label::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }

.plist { border-top: 1px solid var(--hairline); }

.prowi {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 1.6rem;
  gap: clamp(0.9rem, 2.2vw, 1.8rem);
  align-items: baseline;
  padding-block: 1.05rem;
  border-bottom: 1px solid var(--hairline);
  transition: background 0.35s var(--ease), padding-left 0.35s var(--ease);
}

.prowi:hover { background: var(--ink-raised); padding-left: 0.7rem; }


.prowi h3 {
  font-size: clamp(1.02rem, 1.5vw, 1.2rem);
  font-weight: 300;
  color: var(--brand-navy);
  transition: color 0.3s var(--ease);
}

.prowi .pmeta { white-space: nowrap; }

.prowi-go {
  justify-self: end;
  color: var(--cream-faint);
  transition: color 0.3s var(--ease), transform 0.35s var(--ease);
}

.prowi:hover .prowi-go { color: var(--mustard); transform: translateX(4px); }
.prowi:hover h3 { color: var(--brand-brick); }

@media (max-width: 820px) {
  .pcards { grid-template-columns: 1fr; }
  .prowi { grid-template-columns: minmax(0, 1fr); }
  .prowi .pmeta, .prowi-go { display: none; }
}

/* ---------- Projects listing ---------- */

.teaser {
  border: 1px solid var(--hairline);
  padding: clamp(2rem, 5vw, 3.5rem);
  max-width: 720px;
}

.teaser p { color: var(--slate); margin-top: 0.9rem; }

/* ---------- Case studies ---------- */

.case-meta {
  display: block;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--mustard);
  margin-bottom: 1.2rem;
}

.materials-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem 0.7rem;
}

.materials-list li {
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  color: var(--cream-dim);
  border: 1px solid var(--hairline);
  padding: 0.5rem 1.1rem;
}

/* Next-project band */
.slide--next { min-height: 52vh; }
.slide--next .overline { color: var(--mustard); }

/* Closing contact band — photo + floating action card */
.cta-band { min-height: 68vh; align-items: center; }

.cta-scrim {
  background: linear-gradient(100deg,
    rgba(15, 25, 38, 0.55) 0%,
    rgba(15, 25, 38, 0.1) 60%);
}

.cta-card-wrap { width: 100%; }

.cta-panel {
  background: rgba(244, 240, 237, 0.97);
  max-width: 29rem;
  padding: clamp(2rem, 4vw, 2.6rem);
  box-shadow: 0 20px 60px rgba(15, 25, 38, 0.25);
}

.cta-panel .overline { color: var(--mustard); margin-bottom: 0.9rem; }

.cta-panel .display {
  font-size: clamp(1.4rem, 2.4vw, 1.7rem);
  line-height: 1.18;
  margin: 0 0 1.6rem;
  color: var(--cream);
}

.cta-size-label { font-size: 0.78rem; color: var(--slate); margin-bottom: 0.8rem; }

.cta-chips { display: flex; flex-direction: column; gap: 0.6rem; }

.cta-chip {
  text-align: left;
  padding: 0.85rem 1rem;
  border: 1px solid var(--hairline);
  background: transparent;
  font-family: inherit;
  font-size: 0.9rem;
  color: var(--cream-dim);
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.8rem;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.cta-chip span { font-size: 0.68rem; color: var(--slate); text-align: right; }

.cta-chip:hover { border-color: var(--cream-dim); }

.cta-chip.is-picked { background: var(--cream); color: var(--on-dark); border-color: var(--cream); }
.cta-chip.is-picked span { color: var(--on-dark-dim); }

.cta-chips.has-picked { margin-bottom: 1.4rem; }

.cta-reveal {
  display: none;
  border-top: 1px solid var(--hairline);
  padding-top: 1.3rem;
}

.cta-reveal.is-open { display: block; }

.cta-reveal p { font-size: 0.88rem; color: var(--slate); margin: 0 0 1.2rem; line-height: 1.55; }

.cta-btn {
  display: block;
  text-align: center;
  padding: 1rem;
  background: var(--cream);
  color: var(--on-dark);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  margin-bottom: 1rem;
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
}

.cta-btn:hover { background: var(--brand-navy); }

.cta-or {
  text-align: center;
  font-size: 0.68rem;
  color: var(--slate);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 0.9rem;
}

.cta-phones { display: flex; flex-direction: column; gap: 0.5rem; }

.cta-phones a {
  text-align: center;
  font-size: 0.9rem;
  color: var(--cream);
  border: 1px solid var(--hairline);
  padding: 0.65rem;
  transition: border-color 0.3s var(--ease);
}

.cta-phones a:hover { border-color: var(--cream-dim); }

@media (max-width: 640px) {
  .cta-band { min-height: 0; padding-block: clamp(3rem, 10vh, 4rem); }
  .cta-panel { max-width: none; }
}

/* ---------- Value-add cards ---------- */

.value-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--hairline);
  margin-top: 3rem;
}

.value-card {
  background: var(--ink);
  padding: clamp(1.6rem, 3vw, 2.2rem);
}

.interlude--raised .value-card { background: var(--ink-raised); }

.value-card h3 { font-size: 1.05rem; font-weight: 300; margin-bottom: 0.8rem; }
.value-card p { color: var(--slate); font-size: 0.88rem; line-height: 1.7; }

@media (max-width: 920px) {
  .value-cards { grid-template-columns: 1fr; }
}

/* ---------- Home: what we do ----------

   Same hairline-gap idiom as .value-cards, but two columns instead of three.
   Four cards in a 3-column grid leaves one stranded on its own row; 2x2 fills
   both rows and gives the longer service copy a sensible measure. */

.svc-cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--hairline);
  margin-top: 2.6rem;
}

.svc-card {
  background: var(--ink);
  padding: clamp(1.7rem, 3.2vw, 2.4rem);
}

/* The section sits on a raised band, so the cards take the band's colour and the
   1px grid gap is what separates them. Mirrors .interlude--raised .value-card. */
.interlude--raised .svc-card { background: var(--ink-raised); }

.svc-card h3 {
  font-size: 1.08rem;
  font-weight: 400;
  line-height: 1.35;
  margin-bottom: 0.85rem;
  max-width: 26ch;
}

.svc-card p {
  color: var(--slate);
  font-size: 0.88rem;
  line-height: 1.75;
}

/* Anchors are colour: inherit / no underline globally, so links inside prose need
   this or they read as plain text. Underline rather than a colour swap: these sit
   mid-paragraph and a coloured word would fight the overline above. */
.svc-proof {
  margin-top: 1.15rem;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--text-mute);
}

.svc-proof a {
  color: var(--cream);
  text-decoration: underline;
  text-decoration-color: var(--hairline);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  transition: color 0.3s var(--ease), text-decoration-color 0.3s var(--ease);
}

.svc-proof a:hover,
.svc-proof a:focus-visible {
  color: var(--mustard);
  text-decoration-color: var(--mustard);
}

.svc-all { display: inline-block; }

@media (max-width: 920px) {
  .svc-cards { grid-template-columns: 1fr; }
}

/* ---------- Careers ---------- */

.careers-note {
  margin-top: 3rem;
  color: var(--cream-dim);
  max-width: 58ch;
}

.careers-note a { color: var(--cream); border-bottom: 1px solid var(--mustard); transition: color 0.3s var(--ease); }
.careers-note a:hover { color: var(--mustard); }

/* ---------- Contact ---------- */

.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(2.5rem, 7vw, 7rem);
  align-items: start;
}

/* ---------- Enquiry invitation ----------
   The enquiry itself is a Tally form opened in a modal, so this panel is all a
   visitor sees until they choose to start. That is the point of it: the page
   stays entirely ours, and nothing off-brand appears on load. Raised ground
   rather than a border, so it reads as an invitation and not as a form field. */
.invite {
  background: var(--ink-raised);
  padding: clamp(2rem, 5vw, 3.2rem);
  display: grid;
  align-content: center;
  gap: 1.1rem;
  min-height: 26rem;
}
.invite h2 {
  font-size: clamp(1.4rem, 3vw, 2rem);
  font-weight: 300;
  line-height: 1.25;
  margin: 0;
}
.invite p { color: var(--slate); font-size: 0.95rem; margin: 0; max-width: 34ch; }
.invite .btn-ghost { margin-top: 0.6rem; justify-self: start; }

@media (max-width: 680px) {
  .invite { min-height: 0; }
}

.contact-details { display: grid; gap: 1.9rem; }

.contact-details .overline { margin-bottom: 0.4rem; }

.contact-details a,
.contact-details span.val {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  font-weight: 300;
  color: var(--cream);
}

.contact-details a { transition: color 0.3s var(--ease); }
.contact-details a:hover { color: var(--mustard); }

/* The enquiry form is a Tally modal, not markup on this page. The house-styled
   native form that used to live here (.enquiry, .field and friends) was removed
   on 4 Sep 2026 — recover it from git if a first-party form ever replaces Tally. */

/* ---------- FAQ (split panel, photo right) ---------- */

.faq-split {
  display: grid;
  /* The image column is the wider one: the panel is full-height to match the
     accordion, so a 50/50 split made it far too narrow and sliced the
     landscape photographs down to a third of their width. */
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(1.6rem, 4.5vw, 4rem);
  align-items: stretch;   /* photo tracks the accordion column height */
}

.faq-split-left {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(2.5rem, 6vh, 5rem);
}

.faq-split-head .display { max-width: 20ch; }

/* Fills the grid row so its bottom edge lands on the accordion's, whichever
   question is open. Height comes from the left column, not a fixed ratio. */
.faq-split-image {
  position: relative;
  overflow: hidden;
  width: 100%;
  height: 100%;
  min-height: clamp(22rem, 44vh, 32rem);
}

.faq-split-image img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Each photo is anchored on its own subject so the crop never
     slices through the vanishing point of the shot. */
  object-position: center;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity 0.8s ease, transform 1.4s ease;
}

.faq-split-image img.is-active { opacity: 1; transform: scale(1); }

/* Focal points, per photograph.
   In a portrait panel only ONE axis ever does anything: a landscape source is
   already full-height, so only X moves it, and a portrait source is already
   full-width, so only Y does. The inert value is left at 50% for clarity. */

/* Landscape source — X only */
.faq-split-image img[data-i="2"] { object-position: 56% 50%; } /* the balloon sculpture sits right of centre; this frames it whole with air either side */

/* Portrait sources — Y only */
.faq-split-image img[data-i="0"] { object-position: 50% 35%; } /* the sheeted wall and the figure at the table; only spare foreground marble is lost */
.faq-split-image img[data-i="1"] { object-position: 50% 12%; } /* keeps the hanging timber slat ceiling intact and still reaches the team below it */
.faq-split-image img[data-i="3"] { object-position: 50% 10%; } /* mirrored soffit down through the reception desk, losing only spare marble floor */
.faq-split-image img[data-i="4"] { object-position: 50% 45%; } /* unchanged — the jaali panel nearly fills the frame */

.faq-accordion {
  border-top: 1px solid var(--hairline);
  --faq-bullet: 7px;
  --faq-gap: 1.2rem;
}

.faq-acc-row {
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
}

/* The head is a heading wrapping a real button, not a div with a click listener.
   A <button> may not contain an <h3>, but an <h3> may contain a <button> - so the
   heading stays in the outline and the control is genuinely operable. Layout and type
   live on the button, because that is the box the reader focuses and presses. */
.faq-acc-head { margin: 0; }

.faq-acc-head button {
  display: flex;
  align-items: center;
  gap: var(--faq-gap);
  width: 100%;
  padding: 1.3rem 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  font-size: clamp(1rem, 1.6vw, 1.2rem);
  font-weight: 300;
  line-height: 1.4;
  /* Not --cream-faint. That token is rgba(31,54,80,0.52), which lands at 2.99:1 on
     white - fine for decorative metadata, not for five questions a reader is meant to
     read and choose between, often on a phone in daylight. 0.72 measures 5.2:1 and
     still sits clearly behind the open question, which is the full brand navy. The
     token itself is left alone; only this use of it changes. */
  color: rgba(31, 54, 80, 0.72);
  transition: color 0.3s ease;
}

.faq-acc-head button:focus-visible {
  outline: 2px solid var(--brick);
  outline-offset: -2px;
}

/* A plain bullet rather than 01–05: the eight-stage strip immediately below
   is already numbered, and two numbered lists back to back read as one
   sequence continuing. */
.faq-acc-dot {
  width: var(--faq-bullet);
  height: var(--faq-bullet);
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--cream-faint);
  transition: background 0.3s ease, transform 0.3s ease;
}

.faq-acc-row.is-active .faq-acc-dot { background: var(--mustard); transform: scale(1.3); }
.faq-acc-row.is-active .faq-acc-head button { color: var(--cream); }

.faq-acc-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.45s ease;
}

.faq-acc-row.is-active .faq-acc-body { grid-template-rows: 1fr; }

.faq-acc-body-inner { overflow: hidden; min-height: 0; }

/* Reserve the tallest answer's space so switching questions never nudges
   the column height — and with it the photo — by a stray line. */
.faq-acc-row.is-active .faq-acc-body-inner { min-height: calc(5 * 1.7 * 0.92rem + 1.3rem); }

.faq-acc-body-inner p {
  margin: 0;
  padding: 0 0 1.3rem calc(var(--faq-bullet) + var(--faq-gap));
  color: var(--slate);
  font-size: 0.92rem;
  line-height: 1.7;
  max-width: 44ch;
}

@media (max-width: 920px) {
  .faq-split { grid-template-columns: 1fr; gap: 2.5rem; }
  .faq-split-image { aspect-ratio: 16 / 10; height: auto; min-height: 0; order: -1; }

  /* The panel turns landscape here, so the portrait sources swap which axis
     matters and need their own anchors — the desktop values were tuned for a
     tall frame and would leave these showing floor, hoarding and ceiling. */
  .faq-split-image img[data-i="0"] { object-position: 50% 44%; } /* holds the figure and the arch wall in the landscape frame */
  .faq-split-image img[data-i="1"] { object-position: 50% 12%; } /* the slat ceiling feature — the shot's designed element */
  .faq-split-image img[data-i="3"] { object-position: 50% 53%; } /* the reception desk, not the ceiling or the floor */
  .faq-split-image img[data-i="4"] { object-position: 50% 35%; } /* the jaali niches */

  .faq-split-left { gap: 2.2rem; }
}

/* ---------- Process strip (eight stages, drag-navigated) ---------- */
.proc-num {
  font-size: clamp(2.4rem, 4.6vw, 3.7rem); font-weight: 200; line-height: 1;
  color: var(--cream); margin-bottom: clamp(1.2rem, 2.4vw, 2rem);
  font-variant-numeric: lining-nums tabular-nums;
}
.proc-name { font-size: 0.95rem; font-weight: 500; margin: 0 0 0.55rem; color: var(--cream); }
/* The body of all eight process cards, and the only place the studio explains what each
   stage actually involves. 0.84rem is 13.44px, which is below where body text should sit. */
.proc-copy { font-size: 0.94rem; line-height: 1.62; color: var(--slate); margin: 0 0 1.1rem; }

/* Every stage file is pre-normalised to an identical 1200x900 white plate,
   so the frames match exactly and nothing is cropped at render time. */
.proc-shot { margin-top: auto; padding-top: clamp(1.4rem, 3vw, 2.2rem); }
.proc-shot span {
  display: block; aspect-ratio: 4 / 3;
  background: #fff; border: 1px solid var(--hairline); overflow: hidden;
}
.proc-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }

.proc-chips { margin-top: 2.6rem; display: flex; flex-wrap: wrap; gap: 0.6rem; }
.proc-chip,
.pj-chip {
  font-family: inherit; font-size: 0.8rem; cursor: pointer;
  padding: 0.55rem 1rem; border-radius: 999px;
  border: 1px solid var(--hairline); background: transparent;
  color: var(--slate); transition: background 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.proc-chip:hover, .pj-chip:hover { border-color: var(--cream); color: var(--cream); }
.proc-chip.is-on, .pj-chip.is-on { background: var(--brand-navy); border-color: var(--brand-navy); color: var(--on-dark); }
.proc-hint { font-size: 0.78rem; color: var(--slate); margin-top: 0.9rem; }

.proc-strip {
  --proc-pad: clamp(1.2rem, 2.4vw, 2.2rem);
  margin-top: 2.2rem; padding-top: 2.2rem;
  border-top: 1px solid var(--hairline);
  /* Native scrolling, not a hand-rolled drag. The strip used to be overflow:hidden with
     the row moved by a pointer handler, so scrollLeft was inert and seven of the eight
     stages could not be reached without a finger or a mouse. A scroll container is
     keyboard-focusable and arrow-key navigable, screen readers announce it as scrollable,
     and the momentum on iOS is better than a hand-rolled equivalent. Less code, more
     capable. */
  overflow-x: auto;
  overflow-y: hidden;
  /* proximity, not mandatory. Mandatory snapping drags any movement smaller than half a
     card back to where it started, which means an arrow-key press appears to do nothing -
     it would trap the keyboard readers this change exists to serve. Proximity keeps the
     tidy landing on a swipe and lets a small deliberate nudge stand. */
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-x pan-y;
  /* The rail below is the visible position indicator; a scrollbar on top of it is noise. */
  scrollbar-width: none;
}
.proc-strip::-webkit-scrollbar { display: none; }
.proc-strip:focus-visible { outline: 2px solid var(--mustard); outline-offset: 6px; }

.proc-track {
  display: flex;
  gap: clamp(1.2rem, 2.4vw, 2.2rem);
  /* pulled left by one gutter so card 01 still aligns with the container edge
     while keeping the same padding as every other card — that padding is what
     makes all eight plates identical in width */
  margin-left: calc(-1 * var(--proc-pad));
}

.proc-card {
  flex: 0 0 auto;
  width: clamp(228px, 25%, 300px);
  scroll-snap-align: start;
  display: flex; flex-direction: column;
  padding-left: var(--proc-pad);
  border-left: 1px solid var(--hairline);
  transition: opacity 0.35s var(--ease);
}
.proc-card:first-child { border-left: 0; }
.proc-track.is-filtered .proc-card { opacity: 0.2; }
.proc-track.is-filtered .proc-card.is-hit { opacity: 1; }
.proc-card img { -webkit-user-drag: none; user-select: none; }

/* The rail is now eight real buttons, not eight decorative bars. Once scrolling is
   native a mouse wheel scrolls the page rather than the strip, so a mouse-only reader
   needs somewhere to press - and a keyboard reader gets a way to jump rather than
   arrowing through eight cards. The bar stays 2px; the padding makes a 44px target and
   background-clip keeps the paint inside the bar, the same trick the hero dashes use. */
.proc-rail { display: flex; gap: 0.4rem; margin-top: 1.8rem; }
.proc-rail button {
  box-sizing: content-box;
  height: 2px;
  flex: 1 1 auto;
  padding-block: 21px;
  margin-block: -21px;
  border: 0;
  cursor: pointer;
  /* background-color, never the background shorthand, on this and every state below:
     the shorthand resets background-clip to border-box and the thin bar paints as a
     44px block. */
  background-color: var(--hairline);
  background-clip: content-box;
  transition: background 0.4s var(--ease);
}
.proc-rail button.is-on { background-color: var(--cream-dim); }
.proc-rail button:hover { background-color: var(--slate); }


@media (max-width: 900px) { .proc-card { width: 74%; } }

/* ---------- Projects index (card grid + filters) ---------- */
.pj-filters { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: clamp(1.8rem, 3.5vw, 2.6rem); }
.pj-empty { font-size: 0.9rem; color: var(--slate); padding: 2rem 0; }

.pj-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.8rem, 3.2vw, 3rem) clamp(1.4rem, 2.6vw, 2.2rem);
}

.pj-card { display: flex; flex-direction: column; text-decoration: none; }
.pj-card.is-hidden { display: none; }

.pj-media {
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--ink-raised);
  border: 1px solid var(--hairline);
}
.pj-media img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 1.1s var(--ease);
}
.pj-card:hover .pj-media img { transform: scale(1.03); }

.pj-body { padding-top: 1rem; display: flex; flex-direction: column; gap: 0.5rem; }
.pj-body h2 {
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  font-weight: 300; color: var(--cream); margin: 0;
  transition: color 0.3s var(--ease);
}
.pj-card:hover .pj-body h2 { color: var(--brand-brick); }
.pj-type {
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--slate);
}
.pj-line { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

/* Scale and status, the two things the old page never said */
.pj-scale {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cream-dim);
  font-variant-numeric: tabular-nums;
}

.pj-status {
  font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 500;
  display: inline-flex; align-items: center; gap: 0.45rem; white-space: nowrap;
}
.pj-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.pj-status[data-s="built"]        { color: var(--mustard); }
.pj-status[data-s="construction"] { color: var(--slate); }
.pj-status[data-s="concept"]      { color: var(--brand-bluegrey); }

@media (max-width: 900px) { .pj-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .pj-grid { grid-template-columns: 1fr; } }

/* ---------- Clients / testimonials ----------
   Deliberately shorter than a full interlude: this is a trust beat between
   the stages and the CTA, not a destination section. */
.interlude--tight { padding-block: clamp(3.2rem, 7.5vh, 5.4rem); }

.tm-head { margin-bottom: clamp(1.8rem, 3.5vw, 2.6rem); }
.tm-head .rule { margin-top: 1.1rem; }

.tm-quote-block { max-width: 58ch; margin: 0 auto; text-align: center; }

.tm-quote {
  font-weight: 200;
  font-size: clamp(1.15rem, 2.2vw, 1.75rem);
  line-height: 1.38;
  letter-spacing: -0.005em;
  color: var(--cream);
  margin: 0;
  quotes: "\201C" "\201D";
}
.tm-quote::before { content: open-quote; color: var(--mustard); }
.tm-quote::after  { content: close-quote; color: var(--mustard); }

.tm-who { font-size: 0.84rem; color: var(--slate); margin: 1.1rem 0 0; }
.tm-who b { color: var(--cream-dim); font-weight: 500; }

.tm-dots { display: flex; gap: 0.4rem; justify-content: center; margin-top: 1.3rem; }
.tm-dots button {
  width: 26px; height: 2px; border: 0; cursor: pointer;
  background: var(--hairline);
  transition: background 0.3s var(--ease);
}

/* A 2px-tall control is not a small target, it is a broken one: a thumb is about
   45px across and lands on the photograph behind it. The thin bar is the right
   design and does not change. What changes is the box around it - the padding
   makes the target, background-clip keeps the paint inside the 26x2 content box,
   and the negative margin hands the padding back to the layout so nothing moves.
   Same treatment on both rails, so they behave alike. */
.tm-dots button,
.hero-dashes button {
  /* content-box on purpose: the site sets border-box globally, under which a 2px
     height with 20px of padding leaves a zero-height content box - and with
     background-clip painting only that box, the bar disappears entirely. Here the
     width and height must mean the bar itself, with the padding added outside it. */
  box-sizing: content-box;
  /* 21, not 20: the bars are 2 and 3px tall, so 20 either side lands at 42 and 43 -
     a pixel or two under the minimum, which is a silly place to stop. */
  padding-block: 21px;
  margin-block: -21px;
  background-clip: content-box;
}
.tm-dots button.is-on { background-color: var(--cream-dim); }
.tm-dots button:hover { background-color: var(--slate); }

/* The marks run as one slow band; the tile of whoever is speaking lights up */
/* One hairline above the row, marks hanging from it on a shared baseline.
   No box per logo: the marks are already framed artwork of their own, and a
   border around each one only added ruling to a page that is full of it. */
/* The logo band runs FIRST, above the quote: the marks are real and the quotes
   are what a reader discounts, so the proof arrives before the claim. The rule
   therefore sits on the bottom edge, still dividing band from quote rather
   than doubling the section rule above it. */
.tm-band {
  margin-bottom: clamp(2.4rem, 5vw, 3.4rem);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: clamp(1.6rem, 3vw, 2.3rem);
  overflow: hidden;
}
.tm-track {
  display: flex;
  align-items: flex-end;
  gap: clamp(2.8rem, 6vw, 5.4rem);
  width: max-content;
  animation: tmScroll 46s linear infinite;
}
.tm-band:hover .tm-track { animation-play-state: paused; }

.tm-tile {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: clamp(58px, 6.5vw, 78px);
}

/* Real client marks. They arrive as JPEGs on white and cream grounds rather
   than transparent, so multiply drops each logo's own paper into the field.
   Greyed back until that client is the one speaking. */
.tm-logo {
  max-height: 100%;
  max-width: clamp(120px, 13vw, 172px);
  width: auto;
  height: auto;
  object-fit: contain;
  mix-blend-mode: multiply;
  filter: grayscale(1);
  opacity: 0.38;
  transition: filter 0.55s var(--ease), opacity 0.55s var(--ease);
}
.tm-tile.is-speaking .tm-logo { filter: grayscale(0); opacity: 1; }

/* Per-mark optical sizing. With no box to sit in, Powow's very wide lettering
   and Windsong's tall narrow mark cannot share one height cap without the row
   reading ragged. */
.tm-tile[data-key="powow"]           .tm-logo { max-height: 62%; }
.tm-tile[data-key="convoy"]          .tm-logo { max-height: 100%; }
.tm-tile[data-key="aerosky"]         .tm-logo { max-height: 96%; }
.tm-tile[data-key="windsong"]        .tm-logo { max-height: 96%; }
.tm-tile[data-key="what-the-ale"]    .tm-logo { max-height: 92%; }
.tm-tile[data-key="little-miracles"] .tm-logo { max-height: 88%; }

.tm-fade { transition: opacity 0.4s var(--ease); }
.tm-fade.is-out { opacity: 0; }

@keyframes tmScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

@media (prefers-reduced-motion: reduce) {
  .tm-track { animation: none; }
}

/* ---------- Footer ---------- */

.site-foot {
  background: var(--ink-deep);
  border-top: 1px solid var(--hairline);
  padding-block: clamp(3.5rem, 8vh, 5.5rem) 6.5rem; /* extra bottom room for switcher */
}

.foot-grid {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 3fr) minmax(0, 3fr);
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: start;
}

.foot-brand .brand-emblem { height: 40px; }

.foot-tag {
  margin-top: 1.4rem;
  font-size: 0.78rem;
  letter-spacing: 0.24em;
  text-transform: lowercase;
  color: var(--slate);
}

/* This is an enquiry button, not a caption. At 0.66rem - 10.56px - in a 135x40 box it
   read as fine print, which is what a call to action styled like metadata becomes. Type
   this small is a deliberate choice across the site and it looks good; it was just being
   applied to the one control in the footer whose job is to convert. */
.foot-cta-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  margin-top: 1.3rem;
  min-height: 44px;
  padding: 0.7rem 1.4rem;
  border: 1px solid var(--cream-dim);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cream);
  transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
}

.foot-cta-btn:hover { border-color: var(--mustard); color: var(--mustard); }

/* Two pages end on a full-width contact band whose button already says "Talk to us"
   and already points at contact.html. The footer's button then repeats both, forty
   pixels below it, which reads as a mistake rather than as a second chance. The
   contact page hides it for a different reason: there the button links to the page
   you are already reading. Everywhere else it is the footer's only contact route
   and stays. Scoped with :has so a page cannot fall out of step with this rule;
   a browser without :has support simply keeps both, which is today's behaviour. */
main:has(.slide--next) ~ .site-foot .foot-cta-btn,
main:has([data-enquiry-open]) ~ .site-foot .foot-cta-btn { display: none; }

/* Not --cream-faint here either. At 0.52 these measured 2.8:1 on the footer's cream,
   and on a phone the footer is where all the navigation and the phone number live, so
   they carry more weight than footer type usually does. 0.78 measures 5.5:1 and stays
   quieter than the links beneath it. The token itself is unchanged. */
.foot-head {
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(31, 54, 80, 0.78);
  margin-bottom: 1.3rem;
}

/* The gap comes out because the padding below replaces it. Five 19px links with 11px
   between them is the arrangement where a thumb lands on the wrong one; padded to 45px
   and stacked flush, they read more open than before and a near miss hits a neighbour
   rather than nothing. */
.foot-nav ul { display: grid; gap: 0; }

/* On a phone these are the phone number, WhatsApp and the way around the site, so they
   carry more weight than footer links usually do - and a mis-tap on a phone number is a
   lost call. Type size is untouched: only the box around it grows to meet the thumb. */
.foot-nav a,
.foot-contact a {
  display: inline-block;
  padding-block: 13px;
  /* "Home" and "Studio" are only 37 and 42px of text. The minimum widens the box
     without widening the column, so the target clears 44 in both directions. */
  min-width: 44px;
  font-size: 0.86rem;
  color: var(--slate);
  transition: color 0.3s var(--ease);
}

.foot-nav a:hover, .foot-contact a:hover { color: var(--mustard); }

.icon-list { display: grid; gap: 0.1rem; }

.icon-list li { display: flex; align-items: center; gap: 0.7rem; }

.foot-icon { width: 15px; height: 15px; flex-shrink: 0; color: var(--cream-faint); }

.foot-legal {
  margin-top: clamp(3rem, 7vh, 4.5rem);
  padding-top: 1.8rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: rgba(31, 54, 80, 0.78);
}

/* ---------- Scroll reveal ----------
   Guarded on html.js, which an inline snippet in <head> sets on every page that
   also carries the reveal bootstrap at the foot of the file. A page without that
   script never hides anything, so a missing observer can no longer leave a
   heading stuck at opacity 0, the way four project pages once were. */

html.js .reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 1s var(--ease), transform 1s var(--ease);
}

html.js .reveal.in { opacity: 1; transform: none; }

.reveal.d1 { transition-delay: 0.12s; }
.reveal.d2 { transition-delay: 0.24s; }
.reveal.d3 { transition-delay: 0.36s; }

/* ---------- Responsive ---------- */

@media (max-width: 920px) {
  .interlude-grid,
  .contact-grid { grid-template-columns: 1fr; }

  .foot-grid { grid-template-columns: 1fr 1fr; gap: 2.5rem 2rem; }
  .foot-brand { grid-column: 1 / -1; }
}

@media (max-width: 680px) {
  .slide { min-height: 72vh; }
  /* Mirrors the base rule at the top of this file: one viewport *below* the header,
     less a 3rem peek of the section underneath so there is a visible cue to scroll.
     A bare 100svh here would push the hero caption below the fold on a phone. */
  .slide--hero { min-height: calc(100vh - var(--head-h, 116px) - 3rem); }
  .slide--hero { min-height: calc(100svh - var(--head-h, 116px) - 3rem); }
  .slide--page { min-height: 52svh; }

  /* Clear the caption band below: the hero carries a headline as well as the bar, and on
     a phone the band is three lines tall for the longest captions and one for the shortest. */
  .slide--hero .slide-content { padding-bottom: calc(var(--hero-bar-h, 9rem) + 2.5rem); }

  .foot-grid { grid-template-columns: 1fr; }

  .brand-emblem { height: 36px; }
  .brand-name { font-size: 0.92rem; }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }

  html.js .reveal { opacity: 1; transform: none; }

  a.slide:hover .slide-img { transform: none; }
}

/* ---------- Case study: dossier layout ----------
   Sticky fact-and-write-up rail on the left, a constant-width plate sequence
   rolling on the right, and a gallery that hovers over the page. Proportions
   follow the Harbin Opera House reference: rail ~27%, images ~70%.
   Applied to project-aerosky.html on 21 August and now used by all twelve
   case studies; the older .cs-* rules it replaced are deleted. */

  .dos-wrap { padding-block: clamp(2.4rem, 5vw, 4rem) clamp(3rem, 7vw, 5rem); }

  /* Proportions taken from the Harbin Opera House page: the rail is about 27%
     of the measure and the image column about 70%, with the gap making up the
     rest. Our previous split gave the pictures 57%, which ran them noticeably
     small against the reference. */
  .dos {
    display: grid;
    grid-template-columns: minmax(250px, 27%) minmax(0, 1fr);
    gap: clamp(1.4rem, 2.6vw, 2.6rem);
    align-items: start;
  }

  /* The rail carries everything a client asks for. It is taller than a
     viewport, so instead of scrolling internally it scrolls WITH the page
     until its foot reaches the bottom of the screen, and pins there while the
     plates keep rolling. No nested scroll, nothing cut off.

     The mechanism: a sticky element with a NEGATIVE top offset travels up
     with the page until that offset is reached — which, when the offset is
     (viewport - rail height), is exactly the moment its bottom edge meets the
     viewport bottom. The offset depends on the rail's rendered height, so it
     is measured in JS and written to --rail-top. The 4.5rem fallback is what
     applies when the rail is shorter than the screen, or if JS never runs. */
  .dos-rail { position: sticky; top: var(--rail-top, 4.5rem); }
  /* A little air before the writing starts, so the rail does not begin flush
     with the top of the first plate. */
  .dos-rail-inner { padding-right: 0.9rem; padding-top: clamp(0.4rem, 1.4vw, 1.1rem); }

  /* The facts sit under the hero now, not in the rail — the hero already
     names the project and its sector, so repeating either in the rail was
     saying the same thing twice within one screen. */
  .facts-strip {
    padding-block: clamp(1.5rem, 3.4vw, 2.3rem);
    border-bottom: 1px solid var(--hairline);
  }
  .dos-dl--row {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: clamp(1rem, 2.4vw, 2.4rem);
    margin: 0; border-top: 0;
  }
  .dos-dl--row div { padding: 0; border-bottom: 0; }

  .dos-intro { font-size: 0.88rem; line-height: 1.68; color: var(--slate); margin: 0; }

  .dos-dl { margin: 1.3rem 0 0; border-top: 1px solid var(--hairline); }
  .dos-dl div { padding: 0.55rem 0; border-bottom: 1px solid var(--hairline); }
  .dos-dl dt {
    font-size: 0.58rem; letter-spacing: 0.15em; text-transform: uppercase;
    color: var(--text-mute);
  }
  .dos-dl dd { margin: 0.2rem 0 0; font-size: 0.82rem; font-weight: 300; color: var(--cream); }

  .dos-sec { margin-top: clamp(1.5rem, 2.6vw, 2.1rem); }
  .dos-sec h3 {
    font-size: 0.66rem; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--cream); font-weight: 500; margin: 0 0 0.5rem;
  }
  .dos-sec p { font-size: 0.85rem; line-height: 1.68; color: var(--slate); margin: 0; }

  /* Consecutive paragraphs had no space between them at all, so a multi-paragraph
     section read as one unbroken block. It went unseen because those sections were the
     ones hidden behind Read more: on a phone only the single-paragraph brief was ever
     shown, and on a desktop you had to expand the rail to meet it. Expanding by default
     below 901px is what surfaced it. */
  .dos-sec p + p { margin-top: 0.9rem; }

  /* The only prose links in a rail: Windsong and Convoy occupy one building,
     so each names the other. `a` is stripped bare site-wide, so an inline link
     needs its own treatment - mustard plus a rule, following .proc-more's old
     idiom. --mustard, not --mustard-bright: the rail sits on the light ground. */
  .dos-intro a,
  .dos-sec p a {
    color: var(--mustard);
    border-bottom: 1px solid currentColor;
    transition: opacity 0.3s var(--ease);
  }
  .dos-intro a:hover,
  .dos-sec p a:hover { opacity: 0.68; }
  .dos-mat { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
  .dos-mat li {
    font-size: 0.82rem; font-weight: 300; color: var(--slate);
    padding-left: 0.9rem; position: relative;
  }
  .dos-mat li::before { content: "·"; position: absolute; left: 0; color: var(--mustard); }

  /* The long half of the write-up is collapsed, which is how the reference
     keeps a sticky rail shorter than the viewport — hide the text rather than
     offset the column. Expanding it can make the rail taller than the screen
     again, so the toggle re-runs the offset measurement. */
  /* Desktop only now. It was 73x22, under the minimum for the one control that unfolds
     most of the writing. The target grows through an overlay below rather than padding,
     because padding would push the underline away from the words it underlines. */
  .dos-readmore {
    position: relative;
    display: inline-block; margin-top: 1rem; padding: 0;
    background: none; border: 0; cursor: pointer; color: var(--cream);
    font: inherit; font-size: 0.64rem; letter-spacing: 0.18em;
    text-transform: uppercase; font-weight: 500;
    border-bottom: 1px solid var(--cream-dim); padding-bottom: 0.2rem;
    transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
  }
  .dos-readmore:hover { color: var(--mustard); border-color: var(--mustard); }

  /* The invisible half of the target. Nothing here is drawn; it only widens the box the
     pointer has to land in, from 22px tall to 44. */
  .dos-readmore::after {
    content: '';
    position: absolute;
    inset: -11px -8px;
  }

  .dos-gal {
    display: block; width: 100%; margin-top: 1.6rem;
    background: none; border: 1px solid var(--cream-dim); cursor: pointer;
    color: var(--cream); font: inherit;
    font-size: 0.64rem; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 500;
    padding: 0.7rem 0.9rem;
    transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
  }
  .dos-gal:hover { border-color: var(--mustard); color: var(--mustard); }

  /* ---- Hero focal point ----
     The hero is the night aerial. At 16:9 it is the widest photograph in the
     set, so the 2.73:1 band keeps 65% of its height rather than the 55% a 3:2
     shot survives — the least-cropped option available.
     Focus sits at 55% rather than centre so the band runs roughly 19%–84%:
     it takes the city skyline at the top edge, then the lit balloon, the
     Aerosky sign on the parapet and the floor below it, and drops only empty
     night sky and the dark foreground street. */
  .hero-focus { object-position: 50% 55%; }

  /* Convoy's hero is the lobby render at 16:9. The 2.73:1 band keeps 65% of its
     height; biasing to 58% holds the reception desk, the seating and the woven
     pendants, and drops the empty ceiling void above them. */
  .hero-focus-convoy { object-position: 50% 58%; }

  /* Windsong's hero is the built dining room at 2.18:1 - the widest photograph
     in the set, so the 2.73:1 band keeps 80% of its height, more than any other
     option. Focus at 54% holds the seating and the open kitchen counter and
     drops only ceiling. */
  .hero-focus-windsong { object-position: 50% 54%; }

  /* What The Ale's hero is the finished interior at 16:9. Focus at 52% keeps
     the lit brick arches and the banquette, and drops floor. */
  .hero-focus-wta { object-position: 50% 52%; }

  /* Blackburn's hero is the corner render at 3:2, so the 2.73:1 band keeps 55%
     of its height. Focus at 45% holds the roofline and the glazed commercial
     floors rather than sliding down into pavement. */
  .hero-focus-bbr { object-position: 50% 45%; }

  /* Little Miracles' hero is the primary environment at 1.79:1 - the widest
     image in the set, so the band keeps 66% of its height. Focus at 56% holds
     the children and the shelves and drops ceiling. */
  .hero-focus-lm { object-position: 50% 56%; }

  /* Baron's Bar is the only hero in the set that moves X instead of Y. Its
     subject is the backlit lettering, which sits at roughly 18%-38% across the
     frame rather than in the middle. At 16:9 the desktop band crops almost
     nothing vertically, so Y is inert here - but on a phone the band turns
     portrait and keeps only the middle 50% of the width, and a centre crop
     slices the B off the word. Focus at 30% runs the window 15%-65%, which
     holds the whole sign with air either side. */
  .hero-focus-baron { object-position: 30% 50%; }

  /* ---- Measure ----
     Case studies deliberately sit on the SITE measure — the standard .wrap,
     1240px with the 5vw gutter. An earlier version widened the facts strip and
     the dossier body to get 8% larger images, but that left three different
     left edges on every case-study page: header at 64px, body at 31px, footer
     at 77px. Reverted so header, body and footer share one edge.
     The image size on this page comes from the 27/70 rail split, not from the
     outer measure — that split is where the gain actually was. */

  /* ---- Plates ---- */
  .plates { display: flex; flex-direction: column; gap: clamp(1.4rem, 3vw, 2.3rem); }

  .plate { margin: 0; }
  .plate img { display: block; width: 100%; height: auto; border: 1px solid var(--hairline); }
  .plate--doc img { background: #fff; }

  /* No plate number here: the constant-width column already carries the
     order, and a second ordering system competes with it. The number survives
     in the gallery, where an image has no neighbours to place it against. */
  .plate-cap {
    margin-top: 0.5rem; border-top: 1px solid var(--hairline); padding-top: 0.4rem;
  }
  .plate-cap p { margin: 0; font-size: 0.76rem; line-height: 1.5; color: var(--slate); max-width: 78ch; }

  /* Credit line for an image the studio did not take. Sits inline at the end of
     the caption, one notch quieter than the caption itself, so it reads as a
     source note rather than part of the description. Used on Blackburn Rd,
     where the only record of that construction stage is Street View. */
  .img-credit {
    display: inline-block; margin-left: 0.35rem;
    font-size: 0.68rem; letter-spacing: 0.06em;
    color: var(--cream-faint, #9c958a);
    white-space: nowrap;
  }

  /* ---- Plate groups ----
     Most case studies read as one continuous thread and need no headings —
     the caption carries the sequence. A hotel does not: it is documented room
     type by room type, and without a marker the reader cannot tell where the
     standard rooms stop and the deluxe rooms start.
     Same idiom as .reg-label — tracked uppercase Manrope with a hairline
     running out to the right — but set in mustard and scoped to the plate
     column, so it reads as a divider inside the dossier rather than as a
     page-level register mark.
     The margin sits ON TOP of the .plates column gap, which is what opens the
     extra air before each new room type. First heading gets none: the column
     already starts level with the rail, and a top margin there would push the
     images out of alignment with it. */
  .plate-group {
    display: flex; align-items: center; gap: 1rem;
    margin: clamp(1.5rem, 3.2vw, 2.5rem) 0 0;
  }
  .plate-group:first-child { margin-top: 0; }
  .plate-group span {
    font-size: 0.68rem; font-weight: 500; letter-spacing: 0.28em;
    text-transform: uppercase; color: var(--mustard); white-space: nowrap;
  }
  .plate-group::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }


  /* A row of plates. Each gets flex-grow equal to its aspect ratio, so every
     image in the row comes out the SAME HEIGHT and the row fills the column
     edge to edge. Nothing is cropped. Portraits only ever appear in rows —
     a portrait at full column width would be over 1800px tall. */
  .prow { display: flex; gap: clamp(0.5rem, 1vw, 0.9rem); align-items: flex-start; }
  .prow > .plate { flex: var(--ar) 1 0; min-width: 0; }

  /* ---- Gallery ----
     A layer that hovers over the page rather than replacing it: the case study
     stays visible and dimmed behind, so opening the gallery reads as a look
     rather than a departure. One plate at a time, dragged left or right with
     the same gesture the homepage hero uses — commit past 30% of the travel,
     or flick. */
  .gal {
    position: fixed; inset: 0; z-index: 100; display: none;
    background: rgba(20, 30, 44, 0.82);
    backdrop-filter: blur(14px) saturate(0.9);
    /* Was touch-action: none, which suppressed every browser gesture over this subtree
       including pinch-zoom - on the one screen where a client zooms into a joinery
       detail or a drawing sheet. The drag code needs no change: it already binds
       pointercancel, which is exactly what fires when the browser takes the gesture
       over for a pinch, so a two-finger zoom aborts a swipe instead of fighting it. */
    touch-action: pan-y pinch-zoom;
  }
  .gal.is-open { display: grid; grid-template-rows: auto 1fr auto; }

  .gal-bar {
    display: flex; align-items: center; gap: 1rem;
    padding: 0.9rem clamp(1.2rem, 4vw, 3rem);
  }
  .gal-bar .t {
    font-size: 0.66rem; letter-spacing: 0.22em; text-transform: uppercase;
    color: var(--on-dark); font-weight: 500; margin-right: auto;
  }
  .gal-count {
    /* Manrope, not Anton: the house style reserves Anton for large display
       headings and sets all small type in tracked uppercase Manrope. */
    font-size: 0.68rem; letter-spacing: 0.22em; text-transform: uppercase;
    font-weight: 500; color: var(--mustard-bright);
    font-variant-numeric: tabular-nums;
  }
  /* 71x33 before, and the only way out of a full-screen modal. Below the 44px minimum
     it is where phone users press the browser back button instead - which leaves the
     case study entirely, because the gallery is not a history entry. */
  .gal-close {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px;
    background: none; border: 1px solid rgba(244,240,237,0.4); cursor: pointer;
    color: var(--on-dark); font: inherit;
    font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
    padding: 0.5rem 1rem;
    transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
  }
  .gal-close:hover { border-color: var(--mustard-bright); color: var(--mustard-bright); }

  /* The stage holds every slide stacked; only the current one is visible.
     Dragging moves the current slide and brings its neighbour in behind. */
  .gal-stage {
    position: relative; overflow: hidden;
    cursor: grab; touch-action: pinch-zoom;
  }
  .gal.is-dragging .gal-stage { cursor: grabbing; }
  .gal-slide {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    padding: 0 clamp(1.2rem, 5vw, 4rem);
    opacity: 0; pointer-events: none;
    will-change: transform, opacity;
  }
  .gal-slide.is-current { opacity: 1; }
  .gal:not(.is-dragging) .gal-slide {
    transition: transform 0.62s var(--ease), opacity 0.62s var(--ease);
  }
  .gal-slide img {
    display: block; max-width: 100%; max-height: 100%;
    width: auto; height: auto; object-fit: contain;
    user-select: none; -webkit-user-drag: none;
  }
  .gal-slide--doc img { background: #fff; }

  .gal-foot {
    display: flex; align-items: baseline; gap: 0.9rem;
    padding: 0.9rem clamp(1.2rem, 4vw, 3rem) clamp(1.2rem, 3vw, 2rem);
    min-height: 3.4rem;
  }
  .gal-foot .n {
    font-size: 0.68rem; letter-spacing: 0.22em; text-transform: uppercase;
    font-weight: 500; color: var(--mustard-bright);
    font-variant-numeric: tabular-nums; flex: 0 0 auto;
  }
  .gal-foot p { margin: 0; font-size: 0.8rem; line-height: 1.5; color: var(--on-dark-dim); max-width: 70ch; }

  /* Drag affordance, pointer devices only — same language as the hero cue. */
  .gal-cue {
    position: absolute; left: 50%; bottom: clamp(0.8rem, 2vw, 1.4rem);
    transform: translateX(-50%);
    font-size: 0.58rem; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--on-dark-faint); pointer-events: none;
    transition: opacity 0.4s var(--ease);
  }
  .gal.is-dragging .gal-cue { opacity: 0; }
  @media (hover: none) { .gal-cue { content: none; } }

  @media (max-width: 900px) {
    .dos { grid-template-columns: 1fr; gap: 1.8rem; }
    .dos-rail { position: static; top: auto; }
    .dos-rail-inner { padding-right: 0; }
  }
  /* Phones keep the justified row rather than stacking each plate full width.
     Stacking would make the 1:2.4 identity plate ~806px tall on a 375px screen,
     which is the exact problem the row mechanism exists to prevent. Captions
     get narrow in a three-up; that is the accepted cost. */
  @media (max-width: 560px) {
    /* Four fact columns on a phone give each about 72px — "Rooftop pub &
       lounge" in a 72px column. Two columns is the readable minimum. */
    .dos-dl--row { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 1.1rem; }
    .prow { flex-wrap: wrap; }
    /* A three-up of portraits lands at ~107px wide on a 375px screen — a
       contact sheet. Portrait-only rows go two-up there, which roughly doubles
       each image. No flex-grow, so an odd last one stays at half width instead
       of stretching full and towering (a 0.46 portrait at full width would be
       ~725px tall). Mixed rows keep their justified single line: their shared
       height is what stops the portrait in them out-measuring the landscape. */
    .prow--portraits { flex-wrap: wrap; }
    .prow--portraits > .plate { flex: 0 1 calc(50% - 0.2rem); }
    .prow > .plate--breaks { flex: 1 1 100%; }

    /* Mixed rows too, contrary to the note above. Sharing one height does stop the
       portrait out-measuring the landscape, but the arithmetic that achieves it also
       starves the portrait: on Convoy at 375px it lands at 84px wide, and one of the
       starved plates is an annotated drawing. Shared height is a desktop composition
       device; at 335px it buys alignment at the cost of the picture. An equal two-column
       grid gives every plate about 164px and lets the heights differ. */
    .prow:not(.prow--portraits) {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      align-items: start;
    }
    .prow:not(.prow--portraits) > .plate { flex: none; }
    .prow:not(.prow--portraits) > .plate--breaks { grid-column: 1 / -1; }
    .prow { gap: 0.4rem; }
    .prow > .plate .plate-cap { gap: 0.45rem; }
    .prow > .plate .plate-cap p { font-size: 0.68rem; line-height: 1.4; }
  }
/* ---------- End of a case study: where next ----------
   Deliberately almost empty. The block is built from the homepage's register
   components — .reg-label, .plist, .prowi, .pmeta, .link-arrow — so the type,
   colour and hover behaviour are inherited rather than restated. An earlier
   version specified its own Anton headings and a thumbnail; both were wrong
   against the house style, which sets project names in Manrope 300 navy.

   This replaces the full-bleed .slide--next slab on this page: a photographic
   wall after a page of hairlines broke the dossier's flow. The other ten case
   studies still use the slab. */
.cs-end { padding-block: 0 clamp(3rem, 6vw, 4.5rem); }
.cs-end .reg-label { margin-top: clamp(2.6rem, 5vw, 3.6rem); }


/* ============================================================
   STUDIO PAGE  (studio.html)
   Merged from about.html + services.html, 27 August 2026.
   One page answering, in the order a client actually asks:
   who we are, what defines our work, hospitality, the people,
   recognition. A sticky spine keeps a long page reading as one
   argument rather than several pages stacked together.
   The eight stages and the entry points are NOT here - the
   homepage owns both, and restating them was the reason the
   old services page and this one duplicated each other.
   ============================================================ */
/* ---------- Masthead ----------
   No hero photograph. The page opens on the story instead, so the first thing a
   reader meets is the studio talking rather than a picture of somewhere else.
   Needs its own top padding: without a hero pushing it down, the h1 would sit
   under the sticky header. */
.mast { padding-block: clamp(3.5rem, 9vw, 7rem) clamp(2rem, 5vw, 3.5rem); }
.mast h1 { max-width: 18ch; margin: 1rem 0 1.4rem; }
.mast .lede { max-width: 62ch; }
.mast hr.rule { margin-top: clamp(1.8rem, 4vw, 2.6rem); }

/* ---------- The spine ---------- */
.spine-layout {
  display: grid; grid-template-columns: 190px minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem); align-items: start;
}
.spine { position: sticky; top: 104px; padding-block: 0.4rem; }
.spine ol { list-style: none; margin: 0; padding: 0; position: relative; }
.spine ol::before {
  content: ""; position: absolute; left: 4px; top: 0.7rem; bottom: 0.7rem;
  width: 1px; background: var(--hairline);
}
.spine li { position: relative; padding: 0 0 1.5rem 1.6rem; }
.spine li:last-child { padding-bottom: 0; }
.spine li::before {
  content: ""; position: absolute; left: 0; top: 0.42rem;
  width: 9px; height: 9px; border-radius: 50%;
  border: 1px solid var(--hairline); background: var(--paper, #fff);
  transition: border-color 0.35s var(--ease), background 0.35s var(--ease);
}
.spine a {
  display: block; font-size: 0.7rem; font-weight: 500; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--slate); line-height: 1.4;
  transition: color 0.35s var(--ease);
}
.spine a small {
  display: block; font-size: 0.62rem; letter-spacing: 0.06em; text-transform: none;
  color: var(--cream-faint); margin-top: 0.2rem; font-weight: 400;
}
.spine li.is-on::before { border-color: var(--mustard); background: var(--mustard); }
.spine li.is-on a { color: var(--brand-navy); }
.spine a:hover { color: var(--brand-brick); }

.sp { padding-block: clamp(2.6rem, 6vw, 4.5rem); border-top: 1px solid var(--hairline); scroll-margin-top: 100px; }
.sp:first-child { border-top: 0; padding-top: 0; }
.sp-num { display: block; font-size: 0.68rem; font-weight: 500; letter-spacing: 0.24em; text-transform: uppercase; color: var(--mustard); margin-bottom: 0.8rem; }
.sp > h2 { margin-bottom: 1rem; }

@media (max-width: 900px) {
  .spine-layout { grid-template-columns: 1fr; }
  /* On a phone the spine becomes a horizontal scroller pinned under the header,
     so the orientation survives without eating a column. */
  .spine {
    top: 62px; z-index: 40; margin: 0 -5vw 1.5rem; padding: 0.7rem 5vw;
    background: var(--paper, #fff); border-bottom: 1px solid var(--hairline);
    overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  .spine ol { display: flex; gap: 1.3rem; white-space: nowrap; }
  .spine ol::before { display: none; }
  .spine li { padding: 0 0 0 1.1rem; }
  .spine li::before { top: 0.3rem; }
  .spine a small { display: none; }
}

/* ---------- Who: one big frame, copy beside it ---------- */
.who { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 6fr); gap: clamp(1.6rem, 4vw, 3rem); align-items: center; }
.who figure { margin: 0; }
.who img { display: block; width: 100%; aspect-ratio: 4/3; object-fit: cover; border: 1px solid var(--hairline); }
@media (max-width: 760px) { .who { grid-template-columns: 1fr; } }

/* ---------- Section 02: what defines our work ----------
   The six principles are NAMED (ref 2's wording convention) but set in the
   heading treatment from wording option 1 — Manrope 300, larger, title case
   rather than small tracked caps. That is the combination Ritiman picked:
   option 2's wordings, option 1's design.

   (The .values / .value-row rules this page replaced are deleted, along with
   the rest of about.html's stylesheet.) */

/* The thesis block: the collaborative-approach diagram beside the standfirst. */
.thesis {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(1.4rem, 3.5vw, 3rem); align-items: center;
  margin-top: clamp(1.4rem, 3vw, 2.2rem);
}
.thesis figure { margin: 0; }
/* The PNG carries its own transparency, so it takes the page ground rather than
   sitting in a box. No border for the same reason — a frame around a circular
   diagram fights it. */
.thesis img { display: block; width: 100%; height: auto; }
.thesis-cap {
  margin-top: 0.5rem; font-size: 0.66rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--mustard);
}
@media (max-width: 860px) {
  .thesis { grid-template-columns: 1fr; }
  .thesis figure { max-width: 420px; margin-inline: auto; }
}

/* One principle: name + evidence + what it commits us to. */
.vrow-list { list-style: none; margin: clamp(1.8rem, 4vw, 2.6rem) 0 0; padding: 0; border-top: 1px solid var(--hairline); }
.vrow {
  display: grid;
  grid-template-columns: minmax(0, 3.1fr) minmax(0, 4.6fr) minmax(0, 4.3fr);
  gap: clamp(1rem, 2.2vw, 1.9rem);
  align-items: start;
  padding-block: clamp(1.5rem, 3vw, 2.2rem);
  border-bottom: 1px solid var(--hairline);
}
.vrow-lede { display: flex; gap: 0.85rem; align-items: baseline; }
.vrow-n { font-size: 0.68rem; font-weight: 500; letter-spacing: 0.2em; color: var(--mustard); white-space: nowrap; }
.vrow h3 { font-size: clamp(1.05rem, 1.7vw, 1.32rem); font-weight: 300; line-height: 1.28; letter-spacing: 0.01em; }
.vrow figure { margin: 0; }
.vrow figure img { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border: 1px solid var(--hairline); }
/* A drawing or a composed board is contained on white so nothing is cropped —
   the same call the case studies make with .plate--doc. Photographs cover. */
.vrow figure.is-doc img { object-fit: contain; background: #fff; }
.vrow figcaption { margin-top: 0.42rem; font-size: 0.64rem; letter-spacing: 0.13em; text-transform: uppercase; color: var(--mustard); line-height: 1.4; }
.vrow p.vrow-body { font-size: 0.87rem; line-height: 1.72; color: var(--slate); }
@media (max-width: 1080px) {
  .vrow { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  .vrow-lede { grid-column: 1 / -1; }
  .vrow p.vrow-body { align-self: center; }
}
@media (max-width: 700px) { .vrow { grid-template-columns: 1fr; gap: 0.9rem; } }

/* ---------- People ---------- */
.ppl-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1.2rem, 3vw, 2.2rem); }
.ppl img { width: 100%; aspect-ratio: 1; object-fit: cover; border: 1px solid var(--hairline); display: block; }
.ppl h3 { font-size: 1.05rem; font-weight: 400; margin: 0.8rem 0 0.15rem; }
.ppl .ppl-role { display: block; font-size: 0.64rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mustard); margin-bottom: 0.55rem; }
.ppl p { font-size: 0.84rem; line-height: 1.7; color: var(--slate); }
/* Each bio runs to two paragraphs — the professional history, then the personal
   line. The second is set a notch quieter so the card still scans as one block. */
.ppl p + p { margin-top: 0.7rem; color: var(--cream-faint); }
.ppl p strong { font-weight: 500; color: var(--brand-navy); }
@media (max-width: 760px) { .ppl-row { grid-template-columns: 1fr; } }

/* ---------- Recognition ---------- */
.rec { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: clamp(1.6rem, 4vw, 3rem); align-items: center; margin-top: 1.8rem; }
.rec figure { margin: 0; }
.rec img { display: block; width: 100%; height: auto; border: 1px solid var(--hairline); }
.rec figcaption {
  margin-top: 0.5rem; border-top: 1px solid var(--hairline); padding-top: 0.45rem;
  font-size: 0.72rem; line-height: 1.5; color: var(--slate);
}
.rec-award {
  display: block; font-size: 0.68rem; font-weight: 500; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--mustard); margin-bottom: 0.7rem;
}
@media (max-width: 760px) {
  .rec { grid-template-columns: 1fr; }
  /* The 68% cap here was for the old portrait shot, which stood ~600px tall at full
     phone width and swamped the copy. The award photo is now a 1.36 landscape, so it
     takes the column: capping it would only make the screen behind him unreadable,
     which is the whole reason the picture was changed. */
}



/* ---------- Contact routes in the header and the mobile drawer ---------- */

/* On a phone the header used to scroll away after 116px and never come back, and the
   drawer held four links and nothing else. Between the top of the homepage and the
   footer there was no way to reach the studio at all, across 8,700px. */

.nav-contact { display: none; }

@media (max-width: 920px) {
  .nav-contact {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.4rem;
    margin-top: 1rem;
    padding-top: 2.2rem;
    border-top: 1px solid var(--hairline);
    width: min(20rem, 72vw);
  }

  /* Scoped through .site-nav so these beat the tracked-out uppercase nav-link rules.
     These are contact details, not navigation, and should not read as more menu items. */
  .site-nav .nav-contact-link {
    font-size: 1rem;
    font-weight: 400;
    letter-spacing: 0.01em;
    text-transform: none;
    color: var(--text-strong);
    padding-block: 10px;
  }

  .site-nav .nav-contact-link::after { display: none; }
  .site-nav .nav-contact-link:active { color: var(--brick); }

  /* Styled standalone rather than by borrowing .btn-contact. The nav script makes
     every .btn-contact on the page inert while the menu is open, so a drawer button
     wearing that class disabled itself the moment the drawer it lives in appeared. */
  .site-nav .nav-contact-cta {
    display: inline-block;
    background: var(--brick);
    color: var(--on-dark);
    padding: 0.95rem 2.4rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    white-space: nowrap;
    transition: background 0.3s var(--ease);
  }

  .site-nav .nav-contact-cta::after { display: none; }
  .site-nav .nav-contact-cta:active { background: #7A332D; }

  /* The drawer has to survive a short phone with the contact block in it. */
  .site-nav { overflow-y: auto; padding-block: 5rem 2.5rem; }
}

/* WhatsApp, beside the enquiry button on desktop and beside the menu on a phone.
   Sized to the 44px minimum target rather than to the 20px glyph inside it. */
.btn-wa {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  border: 1px solid rgba(22, 41, 63, 0.2);
  color: var(--text-strong);
  flex-shrink: 0;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}

.btn-wa:hover,
.btn-wa:focus-visible { color: var(--brick); border-color: var(--brick); }

/* ---------- The header follows the reader down a phone ---------- */

@media (max-width: 920px) {
  /* Fixed rather than sticky, with the body reserving the height it would have taken
     in flow. A sticky header that shrinks changes the page height under the reader and
     the text jumps; this way collapsing costs nothing below it. */
  body { padding-top: var(--head-h, 116px); }

  .site-head {
    position: fixed;
    top: 0; left: 0; right: 0;
    transition: padding-block 0.25s var(--ease);
  }

  /* 56px exactly: the 44px minimum tap target of the menu button, which is the tallest
     thing in the row, plus 6px of air above and below it. */
  body.head-small .site-head { padding-block: 6px; }
  body.head-small .head-row-top { padding-bottom: 0; }

  /* Collapse the nav row to nothing WITHOUT display:none. The full-screen menu overlay
     is a child of this row, so hiding it stops the menu opening at all. On a phone the
     overlay is position:fixed and out of flow, so zeroing the border and the padding is
     enough to take the row to zero height. */
  body.head-small .head-row-bottom { border-top-width: 0; padding-top: 0; }

  body.head-small .brand-emblem { height: 30px; }
  body.head-small .brand-sub { display: none; }
  body.head-small .brand-name { font-size: 0.98rem; }
}

/* ---------- One-row header above the drawer breakpoint ---------- */

/* The nav used to sit centred on a second tier below the brand, which cost the header
   162px on a desktop screen. On one row it costs about 95px, and that difference is a
   band of photograph the reader sees before scrolling. Below 921px nothing changes: the
   nav is a full-screen overlay there and the lower row still carries the hairline. */
@media (min-width: 921px) {
  /* Three columns with equal outer tracks, so the nav sits on the centre line of the
     page rather than centred in whatever space the brand leaves over. Safe as a grid
     only above this breakpoint: below it the nav is position:fixed and would stop being
     a grid item at all. */
  .head-row-top {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1.5rem;
    padding-bottom: 0;
  }

  .head-row-top .brand-h { justify-self: start; }
  .head-row-top .site-nav { justify-self: center; }
  .head-row-top .head-top-right { justify-self: end; }
  .head-row-bottom { display: none; }

  /* WhatsApp alone. Two buttons side by side read as competing calls to action, and
     Contact is already one of the four links beside it. */
  .head-top-right .btn-contact { display: none; }
}


/* ---------- WebP with a JPEG fallback ---------- */

/* Every content image is now a <picture> serving WebP, with the original JPEG as the
   <img> fallback. display:contents takes the wrapper out of layout entirely, so every
   rule already written against `img` keeps applying exactly as it did when the images
   were bare — .plate img, .gallery figure img, .slide-img and the rest. */
picture { display: contents; }

  /* ---- Gallery: getting out, and getting on ----
     Advancing used to be drag-only, so reaching plate 28 of 31 took 27 successful
     drags and most readers gave up around plate five - taking the construction
     sequence and the drawing set with them. */
  .gal-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(20, 30, 44, 0.55);
    border: 1px solid rgba(244, 240, 237, 0.28);
    border-radius: 50%;
    color: var(--on-dark);
    cursor: pointer;
    /* .gal-slide sets pointer-events:none and these sit in the same stage. */
    pointer-events: auto;
    z-index: 3;
    transition: border-color 0.3s var(--ease), background 0.3s var(--ease);
  }

  .gal-nav:hover,
  .gal-nav:focus-visible { background: rgba(20, 30, 44, 0.8); border-color: var(--mustard-bright); }
  .gal-nav--prev { left: clamp(0.4rem, 1.5vw, 1.2rem); }
  .gal-nav--next { right: clamp(0.4rem, 1.5vw, 1.2rem); }
  .gal.is-dragging .gal-nav { opacity: 0; pointer-events: none; }

/* The footer's enquiry button goes full width on a phone, where the footer is the end of
   a nine-screen page and a 135px button in a 335px column reads as an afterthought. */
@media (max-width: 680px) {
  .foot-cta-btn { width: 100%; }
}

/* ---------- Legal pages ----------
   Long-form prose with no photography to carry it, so the measure does the work:
   68ch, a clear step down from h2 to h3, and lists that get their markers back
   after the global `list-style: none` reset. Used by privacy.html; anything else
   of the same shape can reuse .legal without new rules. */

.legal { padding-block: clamp(2.4rem, 6vw, 3.6rem) clamp(4rem, 10vw, 7rem); }

.legal-block { max-width: 68ch; }
.legal-block + .legal-block { margin-top: clamp(2.8rem, 6vw, 4rem); }

.legal-block h2 {
  font-size: clamp(1.3rem, 2.5vw, 1.7rem);
  font-weight: 300;
  line-height: 1.25;
  letter-spacing: -0.005em;
  margin-bottom: 1rem;
}

.legal-block h3 {
  font-size: 1rem;
  font-weight: 500;
  margin: 1.9rem 0 0.5rem;
}

.legal-block p,
.legal-block li { color: var(--cream-dim); }
.legal-block p + p { margin-top: 1.1rem; }

.legal-block ul {
  list-style: disc;
  margin: 1rem 0 0;
  padding-left: 1.2rem;
}

.legal-block li { margin-bottom: 0.7rem; }
.legal-block li::marker { color: var(--mustard); }

.legal-block a {
  color: var(--cream);
  border-bottom: 1px solid var(--mustard);
  transition: color 0.3s var(--ease);
}

.legal-block a:hover { color: var(--mustard); }

/* Effective date. Sits under the masthead rule, quieter than body copy. */
.legal-date {
  font-size: 0.86rem;
  letter-spacing: 0.02em;
  color: var(--cream-faint);
  margin-top: 1.4rem;
}

/* The privacy link in the footer's legal line. The global `a { color: inherit }` would
   render it as plain text, so it gets a hairline underline and the footer's hover. */
.foot-legal a {
  color: var(--cream);
  border-bottom: 1px solid var(--hairline);
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}

.foot-legal a:hover { color: var(--mustard); border-bottom-color: var(--mustard); }
