/* ==========================================================================
   STONEGRAPH — Design System
   Palette sampled directly from the Stonegraph mark.
   Type pairs Roman inscriptional capitals (letters made to be carved)
   with a machine mono (the CNC half of the craft).
   ========================================================================== */

/* ---------- 1. TOKENS ---------- */
/* Every colour below is sampled from, or extrapolated from, the Stonegraph
   mark itself — a warm taupe monochrome running #30291F to #7F796D.
   The site is deliberately near-monochrome: on a page selling carved stone,
   restraint reads as expensive. Warmth comes from the bronze, used sparingly. */
:root {
  /* Stone ramp — the brand */
  --stone-950:  #14110C;
  --stone-900:  #1E1A14;
  --stone-800:  #2A251D;
  --stone-700:  #3A342B;
  --stone-600:  #4A4237;
  --stone-500:  #5E574A;
  --stone-450:  #625E53;   /* the mark's own mid tone */
  --stone-400:  #7F796D;   /* the mark's lightest */
  --stone-300:  #A9A294;
  --stone-200:  #CFC8B8;
  --stone-150:  #DCD5C6;
  --stone-100:  #E9E3D5;
  --stone-050:  #F5F1E8;

  /* Semantic aliases */
  --basalt:      var(--stone-900);
  --basalt-2:    var(--stone-800);
  --basalt-3:    var(--stone-700);
  --sand:        var(--stone-100);
  --sand-2:      var(--stone-150);
  --sand-3:      var(--stone-200);
  --marble:      var(--stone-050);
  --marble-2:    #FBF9F3;

  /* Accent — a single warm bronze, drawn from the mark's warmest pixels */
  --accent:      #6F5A3C;   /* 4.6:1 on sand */
  --accent-2:    #8A7150;
  --accent-soft: rgba(111, 90, 60, 0.10);
  --accent-dark: #C2AA7E;   /* 7.7:1 on basalt */

  /* Back-compat aliases (older rules still reference these) */
  --teal:        var(--accent);
  --teal-2:      var(--accent-2);
  --teal-soft:   var(--accent-soft);
  --gold:        #8A7A5E;   /* hairlines and marks only */
  --gold-ink:    var(--accent);
  --gold-2:      var(--accent-dark);

  /* Text */
  --ink:         #241F18;
  --ink-soft:    #4A4237;   /* 7.7:1 */
  --ink-mute:    #5E574A;   /* 5.6:1 */
  --on-dark:     #EFEADC;   /* 14.4:1 */
  --on-dark-mute:#B4AD9C;   /* 7.8:1 */

  /* Lines */
  --rule:        rgba(36, 31, 24, 0.15);
  --rule-soft:   rgba(36, 31, 24, 0.08);
  --rule-dark:   rgba(239, 234, 220, 0.15);

  /* Type */
  --f-display: "Marcellus", "Cormorant Garamond", Georgia, serif;
  --f-body:    "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --f-mono:    "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Rhythm */
  --gut: 24px;
  --band-y: clamp(58px, 6.4vw, 106px);
  --wrap: 1400px;

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ---------- 2. RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* No `scroll-behavior: smooth` here — see the note in components.css. On the
   root element it also governs the scroll restoration the browser performs
   when a page is re-entered, so returning to a page meant watching it animate
   from the top down to wherever you had been. Code that wants a smooth scroll
   asks for it per call. */
html { -webkit-text-size-adjust: 100%; }

/* The root viewport owns horizontal containment. Keeping `hidden` on `html`
   is intentional: Android Chrome can still expose a clipped body's visual
   overflow during a real left/right touch gesture, even when scrollWidth is
   mathematically equal to clientWidth. `overscroll-behavior-x: none` also
   stops that gesture chaining into browser/page rubber-banding.

   The body uses `clip` where available so it never becomes a second scroll
   container. Older browsers simply ignore that declaration; the root rule is
   the compatibility boundary and continues to hold the canvas still. */
html {
  max-width: 100%;
  overflow-x: hidden;
  overscroll-behavior-x: none;
}
body {
  max-width: 100%;
  overflow-x: clip;
  overscroll-behavior-x: none;
}
body {
  margin: 0;
  background: var(--sand);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: 17px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
img, svg, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1, h2, h3, h4, h5 { margin: 0; font-weight: 400; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

::selection { background: var(--teal); color: var(--marble); }

:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---------- 3. TYPE SCALE ---------- */

/* Inscriptional display — never below 26px, never bold. Discipline is the point. */
.display {
  font-family: var(--f-display);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.018em;
  text-wrap: balance;
}
/* Deliberately restrained, but no longer whispering. Earlier sizes topped out at
   4.1rem for a page headline and 0.65rem for labels, which read as faint on a
   14" laptop and disappeared on a phone. Every step is bumped a notch and the
   mobile floor is raised: a hero headline never falls below 2.4rem now, and
   every mono label stays at 12px or above so coordinates and specs stay legible. */
.d-xxl { font-size: clamp(2.45rem, 5.6vw, 4.6rem); line-height: 1.05; }
.d-xl  { font-size: clamp(2.05rem, 3.8vw, 3.05rem); line-height: 1.1; }
.d-lg  { font-size: clamp(1.7rem, 2.7vw, 2.3rem);  line-height: 1.14; }
.d-md  { font-size: clamp(1.42rem, 2vw, 1.75rem);  line-height: 1.2; }
.d-sm  { font-size: clamp(1.25rem, 1.5vw, 1.45rem); line-height: 1.25; letter-spacing: -0.005em; }

/* Machine mono — labels, coordinates, specs, years. Lifted off 0.66rem so the
   numbers underneath the hero actually read at a glance on every screen. */
.mono {
  font-family: var(--f-mono);
  font-size: 0.78rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
}

.eyebrow {
  font-family: var(--f-mono);
  font-size: 0.76rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--teal);
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 22px;
}
.eyebrow::before {
  content: "";
  width: 26px; height: 1px;
  background: var(--gold);
  flex: none;
}
.eyebrow--dark { color: var(--gold-2); }
.eyebrow--plain::before { display: none; }

.lede {
  font-size: clamp(1.05rem, 1.2vw, 1.2rem);
  line-height: 1.65;
  color: var(--ink-soft);
  text-wrap: pretty;
}
.body { color: var(--ink-soft); line-height: 1.75; text-wrap: pretty; }
.body + .body { margin-top: 1.1em; }
.body--dark { color: var(--on-dark-mute); }

.measure    { max-width: 62ch; }
.measure-sm { max-width: 48ch; }

/* ---------- 4. LAYOUT ---------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(20px, 4.5vw, 68px);
}
.wrap--narrow { max-width: 1040px; }

.band {
  padding-block: var(--band-y);
  position: relative;
}

/* ==========================================================================
   OFF-SCREEN SECTIONS ARE RENDERED — the page is one continuous document
   Collections is around 13,000px tall and holds 66 photographs and some 900
   SVG nodes, and the temptation on a page that long is to stop the browser
   rendering the parts nobody is looking at. That was tried here twice, and
   the note below records why it is not being tried a third time.
   ========================================================================== */
/* Both rules used to carry `content-visibility: auto`. They are gone, and
   this is the single change that fixes the page getting stuck under a scroll.

   `content-visibility: auto` lets the browser skip an off-screen section
   entirely, and the skipped section is then laid out at whatever
   `contain-intrinsic-size` claims it is. A later patch in components.css set
   that hint to `auto 0`, so every section the reader was not currently
   looking at measured zero. The homepage reported 2,331px tall against a real
   8,205px, and Collections a fraction of its 13,000. What that does under a
   finger or a wheel is exactly what was reported: the document is short, so a
   flick ends almost immediately; then each band the scroll reaches inflates
   from nothing to its true height, shoves everything below it down, and the
   scroll position re-anchors — you push, the page moves, the page pushes
   back, and you are still looking at the same content. It also broke every
   in-page anchor (`collections.html#murals` and the rest), because the
   browser cannot scroll to a target that has no box.

   The hint could have been repaired instead of removing the feature. It is
   not worth it here: the saving is a scroll-cost optimisation on pages that
   scroll acceptably without it, and the failure mode is the whole site
   feeling broken. `contain: style` on .band (components.css) still gives
   most of the style-invalidation win at none of this risk. */
.band--tight { padding-block: clamp(52px, 6vw, 92px); }
.band--dark  { background: var(--basalt); color: var(--on-dark); }
.band--dark .body { color: var(--on-dark-mute); }
.band--marble{ background: var(--marble); }
.band--sand2 { background: var(--sand-2); }

.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gut);
}

/* Section head — asymmetric, never centred, never a text/image split */
.sec-head { margin-bottom: clamp(38px, 5vw, 66px); }
.sec-head__title { grid-column: 1 / span 7; }
.sec-head__aside {
  grid-column: 9 / span 4;
  align-self: end;
  padding-bottom: 6px;
}

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

/* ---------- 5. NAVIGATION ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 90;
  padding-block: 16px;
  transition: transform .5s var(--ease), background-color .35s, backdrop-filter .35s, border-color .35s;
  border-bottom: 1px solid transparent;
  /* No `will-change: transform` here. It promotes this header to a
     containing block for `position: fixed` descendants, and the phone menu
     panel is exactly that — `position: fixed; inset: 0` nested inside. With
     the hint on, "cover the viewport" resolved against the 77px header
     instead, and the menu opened as a 128px sliver with every link clipped
     off below it. See the matching note in components.css. */
}
.nav--stuck {
  background: rgba(233, 227, 213, 0.975);
  border-bottom-color: var(--rule-soft);
}
/* Drawn up out of the way, on the .5s ease the header already carries.
   102% rather than 100%: the bar has a bottom hairline and, once stuck, a
   soft shadow under it, and at exactly 100% the last pixel of both stayed
   on screen as a thin line across the top of the page. */
.nav--hidden { transform: translateY(-102%); }

/* Except while the phone menu is open — then the bar IS the menu, and taking
   it off screen would take the panel with it. Belt and braces: site.js also
   refuses to hide it in that state. */
.nav--open.nav--hidden { transform: none; }

.nav__inner { display: flex; align-items: center; justify-content: space-between; gap: 32px; }

.brand { display: flex; align-items: baseline; gap: 11px; }
.brand__mark {
  font-family: var(--f-display);
  font-size: 1.42rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1;
}
.brand__sub {
  font-family: var(--f-mono);
  font-size: 0.53rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-bottom: 1px;
}

.nav__links { display: flex; align-items: center; gap: clamp(18px, 2.3vw, 34px); }
.nav__link {
  font-family: var(--f-mono);
  font-size: 0.63rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  position: relative;
  padding-block: 6px;
  transition: color .3s;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .45s var(--ease);
}
.nav__link:hover { color: var(--teal); }
.nav__link:hover::after { transform: scaleX(1); transform-origin: left; }
.nav__link[aria-current="page"] { color: var(--teal); }
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

.nav__toggle {
  display: none;
  width: 44px; height: 44px;
  align-items: center; justify-content: center;
  /* A faint sand-colored chip behind the lines so the hamburger reads
     cleanly over the hero photograph at the top of the page, where the
     nav background is still transparent. Without it the 1px lines
     disappeared into the busy marble texture behind them. */
  background: rgba(245, 241, 232, 0.72);
  border: 1px solid var(--rule-soft);
  border-radius: 2px;
  -webkit-tap-highlight-color: transparent;
}
.nav__toggle span {
  display: block; width: 22px; height: 2px; background: var(--ink);
  position: relative; transition: background .3s;
  border-radius: 1px;
}
.nav__toggle span::before, .nav__toggle span::after {
  content: ""; position: absolute; left: 0; width: 22px; height: 2px; background: var(--ink);
  transition: transform .4s var(--ease);
  border-radius: 1px;
}
.nav__toggle span::before { top: -7px; }
.nav__toggle span::after  { top: 7px; }
.nav--open .nav__toggle span { background: transparent; }
.nav--open .nav__toggle span::before { transform: translateY(7px) rotate(45deg); }
.nav--open .nav__toggle span::after  { transform: translateY(-7px) rotate(-45deg); }

/* ---------- 6. BUTTONS ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: var(--f-mono);
  font-size: 0.66rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 15px 26px;
  border: 1px solid var(--ink);
  color: var(--ink);
  position: relative;
  overflow: hidden;
  transition: color .45s var(--ease), border-color .45s;
  isolation: isolate;
}
.btn::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--teal);
  transform: translateY(101%);
  transition: transform .55s var(--ease);
  z-index: -1;
}
.btn:hover { color: var(--marble); border-color: var(--teal); }
.btn:hover::before { transform: translateY(0); }
.btn__arrow { transition: transform .45s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }

.btn--solid { background: var(--teal); border-color: var(--teal); color: var(--marble); }
.btn--solid::before { background: var(--basalt); }
.btn--solid:hover { color: var(--marble); border-color: var(--basalt); }

.btn--ghost-dark { border-color: var(--rule-dark); color: var(--on-dark); }
.btn--ghost-dark::before { background: var(--marble); }
.btn--ghost-dark:hover { color: var(--basalt); border-color: var(--marble); }

/* Text link with drawn underline */
.tlink {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--f-mono);
  font-size: 0.64rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--teal);
  padding-bottom: 4px;
  position: relative;
}
.tlink::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: right;
  transition: transform .5s var(--ease);
}
.tlink:hover::after { transform: scaleX(1); transform-origin: left; }
.tlink svg { transition: transform .45s var(--ease); }
.tlink:hover svg { transform: translateX(4px); }
.tlink--dark { color: var(--gold-2); }

/* ---------- 7. STONE PLATE (image slot) ---------- */
/* Procedural stone surface. Drop a photo in via <img> and it covers the texture. */
.plate {
  position: relative;
  overflow: hidden;
  background:
    linear-gradient(148deg, #C4BCA9 0%, #ADA491 38%, #8B8371 72%, #625B4C 100%);
  isolation: isolate;
}
.plate::before {
  /* fine grain */
  content: "";
  position: absolute; inset: -20%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url(%23g)' opacity='0.42'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
  opacity: .9;
  pointer-events: none;
}
.plate::after {
  /* coarse veining + light rake */
  content: "";
  position: absolute; inset: -30%;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='700' height='700'%3E%3Cfilter id='v'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.008 0.035' numOctaves='5' seed='7'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='700' height='700' filter='url(%23v)' opacity='0.55'/%3E%3C/svg%3E");
  mix-blend-mode: soft-light;
  opacity: .85;
  pointer-events: none;
}
.plate > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 3; }

/* stone variants so plates don't all look identical */
.plate--marble   { background: linear-gradient(150deg, #F1EDE3 0%, #DFD9C9 45%, #C3BBA8 100%); }
.plate--sandstone{ background: linear-gradient(150deg, #C9BCA0 0%, #A99C81 50%, #7D7259 100%); }
.plate--basalt   { background: linear-gradient(150deg, #4A443A 0%, #322D25 55%, #1E1A14 100%); }
.plate--jade     { background: linear-gradient(150deg, #8C8878 0%, #666254 55%, #423E34 100%); }

/* caption sitting inside the plate */
.plate__tag {
  position: absolute;
  left: 16px; bottom: 14px;
  z-index: 4;
  font-family: var(--f-mono);
  font-size: 0.56rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.72);
  text-shadow: 0 1px 6px rgba(0,0,0,0.45);
  display: flex; align-items: center; gap: 8px;
}
.plate__tag::before {
  content: ""; width: 5px; height: 5px; border: 1px solid currentColor; transform: rotate(45deg);
}
.plate__corner {
  position: absolute; z-index: 4; width: 15px; height: 15px;
  border: 1px solid rgba(255,255,255,0.28);
  top: 14px; right: 14px; border-left: 0; border-bottom: 0;
}

/* Stretch to the row set by a sibling. Never combine with an .ar-* class:
   aspect-ratio would win and compute width from height, blowing past the column. */
.plate--fill { height: 100%; width: 100%; min-height: 220px; }
.stretch { display: flex; }
.stretch > .plate { flex: 1; }

/* aspect helpers */
.ar-32  { aspect-ratio: 3 / 2; }
.ar-11  { aspect-ratio: 1 / 1; }
.ar-45  { aspect-ratio: 4 / 5; }
.ar-34  { aspect-ratio: 3 / 4; }
.ar-169 { aspect-ratio: 16 / 9; }
.ar-219 { aspect-ratio: 21 / 9; }

/* ---------- 8. THE SPINE (signature: a CNC toolpath down the page) ---------- */
.spine {
  position: fixed;
  left: clamp(14px, 2vw, 28px);
  top: 0; bottom: 0;
  width: 24px;
  z-index: 80;
  pointer-events: none;
  display: flex;
  justify-content: center;
}
.spine__track {
  position: absolute;
  top: 120px; bottom: 120px;
  width: 1px;
  background: rgba(20,18,15,0.13);
}
/* Progress and head are driven by transform, not by `height` and `top`.
   Writing those two properties on every scroll frame put a layout and a paint
   on the main thread sixty times a second for the whole length of every
   scroll — a fixed 1px hairline is cheap to paint, but the invalidation is
   not. scaleY and translate3d are handled by the compositor, so the scroll
   handler now writes two values that cost nothing to apply. */
.spine__fill {
  position: absolute;
  top: 120px; bottom: 120px;
  width: 1px;
  background: var(--teal);
  transform: scaleY(0);
  transform-origin: top center;
  will-change: transform;
  transition: transform .12s linear;
}
.spine__head {
  position: absolute;
  top: 120px;
  will-change: transform;
  width: 7px; height: 7px;
  border: 1px solid var(--teal);
  background: var(--sand);
  transform: translate3d(0, -50%, 0) rotate(45deg);
  transition: transform .12s linear;
}
.spine__label {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: center;
  transform: translate(-50%, -50%) rotate(-90deg);
  white-space: nowrap;
  font-family: var(--f-mono);
  font-size: 0.53rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-mute);
  transition: opacity .4s;
}
body.on-dark-spine .spine__track { background: rgba(239,231,218,0.16); }
body.on-dark-spine .spine__head  { background: var(--basalt); border-color: var(--gold-2); }
body.on-dark-spine .spine__fill  { background: var(--gold-2); }
body.on-dark-spine .spine__label { color: var(--on-dark-mute); }

/* ---------- 9. SCROLL REVEAL ----------
   The reveal rules themselves live in components.css (FIRST PAINT block),
   gated on `html.js-reveal`. By default the page is visible — only when JS
   runs and confirms it can drive the reveal does it apply the hidden state.
   This stops the page ever arriving blank on a slow connection. */
.reveal { opacity: 1; transform: none; }
.reveal-line { transform: none; }
/* The mask that lets a line rise into place.
   Its clip box is exactly one line-height tall, but the display serif's ink
   runs taller than the line box — caps sit above it, descenders hang below.
   Left tight, `overflow:hidden` shaved roughly 4px off the top of every
   capital and 3px off every g, p and y on the site. So the clip box is padded
   out past the ink and the same amount is pulled back off the margin: the
   letters arrive whole and the vertical rhythm is unchanged.

   The padding was 0.2em / 0.18em, which clipped the tall caps of Marcellus at
   larger display sizes — the tops of T, L and I disappeared, so the heading
   read as cut in half on the page head and on the hero. The padding is opened
   up to clear the ink at every size, including the new larger display scale. */
.rise-mask {
  overflow: hidden;
  display: block;
  /* Widened from .42em/.34em. That was a tight clearance for a display serif
     at 74px — the ascenders on a capital and the tail of a 'y' both run close
     to the edge of it, and a mask that clips ink is indistinguishable from a
     mask that is merely mid-reveal, so it is not a thing anyone notices until
     it is on a screen in front of them. The extra room costs nothing: the
     negative margin cancels it, so the heading occupies exactly the same
     space on the page as before. */
  padding-block: 0.54em 0.44em;
  margin-block: -0.54em -0.44em;
}
.rise-mask > span { display: block; transform: none; }

/* ---------- 10. ART LAYER (hand-drawn SVG placement) ---------- */
.art {
  position: absolute;
  pointer-events: none;
  z-index: 0;
  line-height: 0;
}
.art svg { width: 100%; height: 100%; overflow: visible; }
/* Premium feel: strong warm bronze/gold tint + saturation boost so the line
   art (drawn in neutral greys) reads as rich bronze and the peacock's teals
   and Rajasthan terracottas are vibrant. */
/* The tint sits on the <svg>, not on the wrapper around it. On the wrapper it
   was an ancestor filter over a child that animates its own opacity forever,
   which means the browser cannot keep the filtered result: it re-runs the
   whole chain over the plate on every frame of the breathe loop, and again on
   every frame of any stroke or rotation inside. On the svg itself the tint and
   the opacity belong to the same composited layer and are applied together,
   once, at composite time. Same colour, a fraction of the work. */
.art > svg, .hero__art > svg, .phead__art > svg { filter: sepia(0.6) saturate(1.8) contrast(1.12) brightness(1.08); }

/* The peacock is the one drawing whose own colour is the point. The heavy
   sepia pass collapses its teal, green and gold into a single bronze, which
   is why it read flat. It keeps a much lighter wash so the iridescence
   survives while still sitting in the warm palette. */
.art[data-art="peacock"] > svg,
.art[data-art="peacock-grove"] > svg {
  filter: sepia(0.16) saturate(1.4) contrast(1.05) brightness(1.03);
}
/* Weight of a drawing that sits BEHIND words.
   These began at .88–.98, which put a fully saturated peacock under a
   paragraph on the portfolio page with 95% of that paragraph on top of it.
   Dropping everything to .16–.30 fixed that and created the opposite fault:
   the hand-drawn plates went so faint they read as smudges, and a half-visible
   line drawing looks like an animation that gave up half way.

   The two are not the same problem. The peacock is dense and saturated; the
   plates are bronze hairlines on sand and carry almost no ink. So they are
   weighted separately — the line work comes back up to where it reads, and
   only the peacock stays quiet. */
.art--soft   { opacity: .8; }
.art--medium { opacity: .88; }
.art--strong { opacity: .94; }

/* The one drawing with real mass in it. */
.art[data-art="peacock"] { opacity: .8; }

/* A drawing that has a band to itself, with no copy over it, and so is free
   to be the thing you are looking at. */
.art--plate  { opacity: 1; }
.art--full   { opacity: 1; }

/* keep content above art */
.layer { position: relative; z-index: 2; }

/* ---------- 11. FOOTER ----------
   Rebuilt to the proportions and colour structure of the AKSUTRA footer, with
   Stonegraph's own words, accent and marks. What was taken and what was
   deliberately not:

   TAKEN — the ground and the ramp.
     A single near-black (#141413) rather than the warm basalt used elsewhere,
     and one off-white (#F0EEE6) stated once and then varied only by opacity:
     70% for the address, 60% for links, 55% for the standfirst, 45% for the
     division line, 40% for column heads and the legal row, 10% for the rule.
     That ramp is why the reference footer reads as quiet and expensive — every
     level of the hierarchy is the same colour at a different strength, so
     nothing competes.

   TAKEN — the measure and the rhythm.
     A 1152px measure, 64px of air above and below, a twelve-column grid, and a
     legal bar set off by a hairline with 32px above and 24px below it. The old
     footer used a five-track `fr` grid and asymmetric padding (78px top, 48px
     bottom), which is why it read heavier at the top than the bottom.

   TAKEN — the underline that retracts.
     Every link carries a full-width rule that withdraws to the right on hover,
     drawn as a background gradient so it animates on the compositor and never
     shifts the text. The old links moved 5px to the right instead, which nudges
     a whole column when a cursor crosses it.

   NOT TAKEN — the accent.
     The reference uses a terracotta (#D97757) for its status dot and hovers.
     Against Stonegraph's bronze that reads as a second brand arguing with the
     first, so the accent stays --accent-dark and only the structure is
     borrowed.

   NOT TAKEN — the square social buttons.
     Those are now discs, which is the reference's shape and the better one at
     36px: a circle at that size reads as a control, a square reads as a
     cropped photograph.
   ------------------------------------------------------------------------- */

.foot {
  /* One ground for the whole closing page. Stated as a token so the ramp below
     can be written against it rather than repeating the hex eleven times.

     WARMED, and built from the ramp rather than from a new colour. It was a
     flat `#141413` — a near-neutral black that belongs to no part of this
     palette, and read as a hole cut in the bottom of a sand-coloured site.

     The ground is now the stone ramp's own dark end, run as a gradient the
     same way the studio plates are: --stone-700 at the top where the footer
     meets the page, down through --stone-800, to --stone-950 at the very
     bottom. Warm brown, not black, and every stop is a token — so if the ramp
     is ever retuned the footer moves with it instead of being left behind.

     Over that goes the same warm radial the plates carry. It is what stops a
     large flat dark area reading as a rectangle of paint: there is a light in
     it, off-centre and high, and the corners fall away from it. */
  --foot-ink: 245, 241, 232;          /* #F5F1E8 — the ramp's own lightest */
  /* THE MUTED ALPHAS BELOW WERE LIFTED WITH THE GROUND, and they had to be.
     A semi-transparent ink composites against whatever is behind it, so
     warming the footer from #141413 up to the stone ramp's dark end made every
     faded line LESS legible, not more: the ink got lighter but the ground got
     lighter faster. Measured against --stone-700, the lightest stop and so the
     worst case, the footer headings at 0.4 came out at 3.14:1 — under the 4.5
     that small text needs. 0.548 is where that line sits, so the text alphas
     were moved to 0.62 / 0.66 / 0.70 / 0.74 / 0.80 and every one of them now
     clears it. Hairlines and separators were left alone; a rule is not text. */
  background:
    radial-gradient(ellipse 90% 70% at 22% 0%, rgba(194, 170, 126, .10), transparent 62%),
    linear-gradient(168deg,
      var(--stone-700) 0%,
      var(--stone-800) 42%,
      var(--stone-900) 74%,
      var(--stone-950) 100%);
  color: rgb(var(--foot-ink));
  position: relative;
  /* The hairline that separates the footer from the band above it. Now gold at
     a low strength rather than white: on a warm ground a white hairline is a
     cold line, and this is the same rule the mounts and the plates draw. */
  border-top: 1px solid rgba(194, 170, 126, 0.20);
}

/* The measure. 1152px is the reference's, and it is a better one for a footer
   than the page's own wrap: link columns set narrower than the body copy above
   them read as a colophon rather than as more page. */
.foot > .wrap {
  max-width: 1152px;
  margin-inline: auto;
  padding-inline: clamp(24px, 4vw, 40px);
  /* Was clamp(44px, 5vw, 64px). The reference's 64px sits under a footer whose
     tallest column is a single status line; this one carries an address, so
     the same padding around a taller band read as slack rather than as air. */
  padding-block: clamp(32px, 3.4vw, 44px);
}

.foot__grid {
  display: grid;
  /* Twelve columns: brand 4, three link columns of 2, status 2. The old grid
     was five `fr` tracks, which let a long link stretch its own column and
     pull the others out of true. Fixed columns cannot drift. */
  grid-template-columns: repeat(12, minmax(0, 1fr));
  /* Row and column gaps set separately. Horizontal air is what keeps four link
     lists from reading as one block, so it stays; vertical air between rows is
     only doing anything on a phone, where the columns wrap. */
  column-gap: clamp(24px, 2.6vw, 40px);
  row-gap: clamp(24px, 2.4vw, 28px);
}
/* 3 / 2 / 2 / 2 / 3. The Workshop column holds a six-part postal address and
   was in the same 2-track slot as a list of four links — at ~143px it wrapped
   onto six lines and set the height of every other column. A third track lets
   it wrap onto four and costs the brand column nothing it was using: its
   standfirst is capped at 34ch and was never filling 4 tracks either. */
.foot__brand { grid-column: span 3; }
.foot__col { grid-column: span 2; }
.foot__col--status { grid-column: span 3; }

/* ---- the brand column ---- */
.foot__brand .brand { margin-bottom: 0; }
.foot__brand .brand__mark {
  font-size: 18px !important;
  font-weight: 500;
  letter-spacing: -0.025em;
}
.foot__tagline {
  margin-top: 16px;
  font-size: 14px;
  line-height: 1.6;
  color: rgba(var(--foot-ink), 0.7);
  max-width: 34ch;
}
.foot__division {
  margin-top: 8px;
  font-size: 13px;
  line-height: 1.6;
  color: rgba(var(--foot-ink), 0.66);
}
.foot__mail {
  display: inline-block;
  margin-top: 18px;
  font-family: var(--f-mono);
  font-size: 13px;
  letter-spacing: 0;
  text-transform: none;
  color: rgba(var(--foot-ink), 0.8);
  transition: color .3s;
}
.foot__mail:hover { color: rgb(var(--foot-ink)); }

/* Discs, not squares. */
.foot__social { display: flex; align-items: center; gap: 10px; margin-top: 16px; }
.foot__social a {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 999px;
  border: 1px solid rgba(var(--foot-ink), 0.15);
  background: rgba(var(--foot-ink), 0.03);
  color: rgba(var(--foot-ink), 0.8);
  transition: color .3s, border-color .3s, background .3s;
}
.foot__social a:hover {
  color: var(--accent-dark);
  border-color: rgba(194, 170, 126, 0.6);
  background: rgba(194, 170, 126, 0.1);
}
.foot__social svg { width: 16px; height: 16px; }

/* ---- the link columns ----
   The heads were bronze, which made five small labels the loudest thing in the
   footer and put them ahead of the links they describe. Dimmed into the ramp
   they do what a head should: mark the column and get out of the way. */
.foot__head {
  margin-bottom: 14px;
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: rgba(var(--foot-ink), 0.62);
}
.foot__list li { margin-bottom: 8px; }
.foot__list a {
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(var(--foot-ink), 0.74);
  transition: color .3s;
}
.foot__list a:hover { color: rgb(var(--foot-ink)); padding-left: 0; }

/* ---- the workshop's status ---- */
.foot__status {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px;
  font-size: 13px;
  color: rgba(var(--foot-ink), 0.74);
}
/* A round dot with a ring going out of it. The old one was a rotated square,
   which at 6px reads as a speck of dirt rather than a signal. */
.foot__pulse { position: relative; width: 6px; height: 6px; flex: none; }
.foot__pulse::before, .foot__pulse::after {
  content: ""; position: absolute; inset: 0;
  border-radius: 999px;
  background: var(--accent-dark);
  transform: none;
}
.foot__pulse::before { animation: foot-ping 2.6s ease-out infinite; }
@keyframes foot-ping {
  0%   { opacity: .7; transform: scale(1); }
  70%, 100% { opacity: 0; transform: scale(3.4); }
}
.foot__hours {
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: none;
  color: rgba(var(--foot-ink), 0.66);
  line-height: 1.7;
}

/* ---- the legal bar ---- */
.foot__bottom {
  margin-top: 24px;
  padding-top: 20px;
  padding-bottom: 0;
  border-top: 1px solid rgba(var(--foot-ink), 0.1);
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px; flex-wrap: wrap;
}
/* A flex item will not shrink below its own content width unless it is told it
   may. Without this the copyright line kept its full width and overhung the
   column rather than wrapping inside it. */
.foot__bottom > * { min-width: 0; }
.foot__bottom p { overflow-wrap: anywhere; }
.foot__bottom p, .foot__legal a, .foot__legal span {
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(var(--foot-ink), 0.62);
}
.foot__legal { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; min-width: 0; }
.foot__legal > * { min-width: 0; }
.foot__legal a { transition: color .3s; }
.foot__legal a:hover { color: rgba(var(--foot-ink), 0.8); }
.foot__legal > span:not(.mono) { color: rgba(var(--foot-ink), 0.2); }

/* ---- the underline that withdraws ----
   Drawn as a background gradient rather than border-bottom or text-decoration,
   for two reasons: it can be animated (a border cannot be transitioned to a
   partial width), and it composites, so a column of forty links costs nothing
   as the cursor crosses them. It retracts to the RIGHT — the origin flips with
   the size — so the rule reads as being drawn back rather than shrinking. */
.foot__list a,
.foot__mail,
.foot__legal a {
  background-image: linear-gradient(currentColor, currentColor);
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 100% 1px;
  transition: background-size .4s ease, color .3s;
  padding-bottom: 1px;
}
.foot__list a:hover,
.foot__mail:hover,
.foot__legal a:hover {
  background-size: 0 1px;
  background-position: 100% 100%;
}
@media (prefers-reduced-motion: reduce) and (prefers-reduced-motion: no-preference) { /* OFF by the motion policy — see motion-always.css */
  .foot__list a, .foot__mail, .foot__legal a { transition: color .3s; }
}

/* ---- narrower screens ----
   Twelve columns hold to a tablet, then fold: the brand takes the full width
   and the link columns pair up. The status column joins them rather than
   spanning, so three short columns do not become one long one. */
@media (max-width: 900px) {
  .foot__brand { grid-column: span 12; }
  .foot__col, .foot__col--status { grid-column: span 4; }
}
@media (max-width: 620px) {
  .foot__col { grid-column: span 6; }

  /* A flourish, not information, and the least readable type on the site. On a
     327px column it takes a row of its own and wraps onto two lines. It stays
     in the laptop footer, where there is width for it beside the links. */
  .foot__legal .mono { display: none; }
  .foot__legal { gap: 12px; }
  /* Full width, so the address is not squeezed into half a row beside a link
     list. Its ORDER is deliberately not set here — the rule further down that
     lifts it up beneath the brand stands, because the orphaned third column it
     was blamed for happens at any position: three columns in a two-up grid
     always leave one alone. */
  .foot__col--status { grid-column: span 12; }
  .foot__bottom { flex-direction: column; align-items: flex-start; gap: 14px; }
}

/* The 1080 and 700 breakpoints that used to reshape .foot__grid are gone: the
   footer block above carries its own, against a twelve-column grid rather than
   the old `fr` tracks. Two sets would have fought at exactly the widths this
   was meant to fix. */


/* ---------- 12. UTILITIES ---------- */
.stack-6  > * + * { margin-top: 6px; }
.stack-12 > * + * { margin-top: 12px; }
.stack-20 > * + * { margin-top: 20px; }
.stack-28 > * + * { margin-top: 28px; }
.stack-40 > * + * { margin-top: 40px; }
.mt-a { margin-top: auto; }
.tc-teal { color: var(--teal); }
.tc-gold { color: var(--gold-ink); }
/* AND THE SAME TWO, ON A DARK GROUND.
   --gold-ink is --accent, #6F5A3C, which the palette above documents as
   "4.6:1 on sand" — it is the value for a light ground and it is the only one
   these two utilities offered. On basalt the same colour measures 2.6:1, and
   .tc-gold is used on the dark bands: the material line under "Architectural
   elements" on Collections is one of them. The pale value, --gold-2, is
   already defined for exactly this ("7.7:1 on basalt"); it just had to be
   reachable from the same class, or every dark band needed its author to
   remember to type .tc-goldlt instead.

   Written as context rather than as a second class so it cannot be forgotten
   again, and so a band that changes from light to dark takes its accent with
   it. */
.band--dark .tc-gold,
.band--dark .tc-teal,
.foot .tc-gold,
.foot .tc-teal,
.panel .tc-gold,
.panel .tc-teal,
.cn .tc-gold,
.cn .tc-teal,
.proc .tc-gold,
.proc .tc-teal { color: var(--gold-2); }
.tc-mute { color: var(--ink-mute); }
.tc-goldlt { color: var(--gold-2); }
.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 {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--teal); color: var(--marble); padding: 12px 20px;
}
.skip:focus { left: 12px; top: 12px; }

/* ---------- 13. RESPONSIVE ---------- */
@media (max-width: 1180px) {
  .spine { display: none; }
}

/* ---------- MOBILE NAVIGATION ----------
   The breakpoint was 980px, which let iPad-landscape (1024px) and large
   phone-landscape (932px+) viewports fall through to the desktop nav. With
   five links plus a "Get in touch" button, the desktop row only just fits
   at ~981px, so anything between 981–1100px ended up cramped — the brand
   on the left took its full 200px, the row of links took the rest, and
   the last one or two links (Journal, Get in touch) ended up clipped off
   the right edge of the screen, reachable only by horizontal swipe.

   The breakpoint now sits at 1200px so every tablet and every phone in
   landscape gets the panel. The panel itself is also a real full-screen
   overlay now: previously it was `inset: 0 0 auto 0`, which meant the
   panel sized to its own content height and the page bled through below
   it, reading as a half-finished menu. It now covers the whole viewport,
   scrolls if it ever overflows, and uses a more legible link size. */
@media (max-width: 1200px) {
  .nav__links {
    position: fixed;
    inset: 0;                       /* full-screen overlay */
    background: var(--sand);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 96px clamp(20px, 6vw, 32px) 32px;
    transform: translateX(-100%);   /* slide in from the left, not the top */
    transition: transform .5s var(--ease);
    overflow-y: auto;
    z-index: -1;
    display: flex;
  }
  .nav--open .nav__links { transform: none; }
  .nav__link {
    font-size: 1.1rem;
    letter-spacing: 0.12em;
    padding: 18px 4px;
    width: 100%;
    border-bottom: 1px solid var(--rule-soft);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
  }
  .nav__link[aria-current="page"] { color: var(--teal); }
  /* Small arrow cue on the right of each link, so the row reads as tappable
     the way a native iOS list does, instead of as plain text. Uses ::after
     because the link is a flex row — ::after is the last flex child and
     margin-left:auto pushes it to the right edge, leaving the label on the
     left. ::before would have put the chevron before the label. This
     replaces the desktop underline that ::after usually draws. */
  .nav__link::after {
    content: "";
    width: 8px; height: 8px;
    border-right: 1px solid currentColor;
    border-top: 1px solid currentColor;
    transform: rotate(45deg);
    opacity: .35;
    flex: none;
    margin-left: auto;
    background: none;
    position: static;
    inset: auto;
    transition: none;
  }
  .nav__toggle { display: flex; }

  /* The CTA button inside the panel gets full width and a more generous
     tap target, instead of the desktop pill squeezed at the row's end. */
  .btn--nav {
    width: 100%;
    margin: 28px 0 0;
    justify-content: center;
    padding: 18px;
    font-size: 0.78rem;
  }

  .sec-head__title, .sec-head__aside { grid-column: 1 / -1; }
  .sec-head__aside { padding-top: 22px; }
}
@media (max-width: 620px) {
  :root { --gut: 16px; }
}

/* ==========================================================================
   MOBILE PARITY — premium feel on a phone, identical to desktop.
   Earlier rules quietly dropped: hero film, ambient mandala rotations, plate
   breathes, hover lifts, .piece hover captions, .pillar hover wash and the
   portfolio film poster on narrow screens. None of these are expensive on a
   modern phone (they are bounded animations or compositor-only transforms),
   and stripping them made the mobile site read as the cheap version of the
   desktop rather than the same one. The guards that actually matter — pause
   on offscreen, IntersectionObserver gating — already keep cost down, and
   the heavier CSS filters are gated behind `prefers-reduced-motion` higher up.

   The phone keeps the artwork breathing, the cards lifting, the captions
   sliding in, and the reel running, exactly as on a laptop.
   ========================================================================== */
@media (max-width: 760px) {
  /* Card hover lift is restored on touch via :active, so a tap answers the
     same way a hover does on desktop. */
  .card:active { transform: translateY(-4px); transition: transform .35s var(--ease); }
  .work:active .work__media .plate { transform: scale(1.02); transition: transform .5s var(--ease); }
  .piece:active { transform: translateY(-2px); }
  /* Spine continues to run on phones, since it is now compositor-only and
     was already hidden on < 1180px. Mobile only — we re-enable nothing else
     here, because nothing else was disabled for narrow screens. */
}
@media (hover: none) and (pointer: coarse) {
  /* Touch devices keep the lift and the gradient veil, on tap instead of hover.
     On no-hover pointers the captions and zoom affordance are already on
     (see components.css) — here we restore the depth cues the laptop gets. */
  .piece { box-shadow: 0 14px 32px -22px rgba(36,31,24,.32); }
  .card { box-shadow: 0 12px 30px -22px rgba(36,31,24,.28); }
  .pcard { box-shadow: 0 14px 34px -24px rgba(36,31,24,.3); }
}

/* ---------- 13b. VERTICAL RHYTHM ON A PHONE ----------
   The band padding is a clamp whose lower bound, 58px, is already what every
   phone gets — 6.4vw only overtakes it at about 900px wide. So a phone paid
   the full 58px above and below all nine bands: something like 1,000px of
   deliberate emptiness, roughly a screen and a half of scrolling that contains
   nothing, on the device where scrolling costs the most.

   Whitespace is doing real work on a page selling carved stone and it is not
   being removed — it is being scaled to the screen it is on, which is what the
   clamp was trying to do and could not, because a viewport-relative middle
   term cannot reach below its own floor. */
@media (max-width: 760px) {
  :root { --band-y: 44px; }
  .band--tight { padding-block: 34px; }
}
@media (max-width: 420px) {
  :root { --band-y: 38px; }
  .band--tight { padding-block: 30px; }
}

/* ---------- 14. REDUCED MOTION ----------
   Less motion, not no motion.

   This block used to flatten everything: every transition cut to 0.001ms and
   every reveal forced to its finished state. The intent was right and the
   result was that the site had no animation whatsoever on a very large share
   of phones — Android turns this preference on under Battery Saver and under
   Accessibility → Remove animations, so people hit it constantly without ever
   having made a decision about websites.

   What the preference is actually for is movement. Travel across the screen,
   parallax and scaling are what provoke vestibular symptoms; a change in
   opacity does not, and the WCAG guidance on this is about motion rather than
   about change. So the rise, the slide and the scale are removed here — and
   the fade is kept, a little quicker than standard. The page still arrives in
   sequence rather than all at once, and nobody is made unwell by it. */
@media (prefers-reduced-motion: reduce) and (prefers-reduced-motion: no-preference) { /* OFF by the motion policy — see motion-always.css */
  html { scroll-behavior: auto; }

  /* No blanket animation kill.
     This was a universal selector setting animation-duration to 0.001ms, which
     is the usual advice and is far too broad for this site: it stopped every
     drawing in the art engine — strokes drawing themselves, petals opening,
     feathers swaying — on any phone with Battery Saver switched on. None of
     that motion travels across the screen or scales the viewport, which is
     what the preference exists to prevent; it is ornament moving a degree or
     two inside its own box.

     What is still suppressed below is page-level movement: blocks rising into
     place, rules drawing across, photographs scaling back to true. */

  /* Movement is removed from the reveals; the fade survives. */
  html.js-reveal .reveal {
    transform: none !important;
    transition: opacity .38s var(--ease-soft) !important;
    transition-delay: calc(var(--rd, 0s) * 0.25) !important;
  }
  html.js-reveal .reveal.is-in { opacity: 1 !important; }

  /* A line that draws itself across is travel, so it simply fades in place. */
  html.js-reveal .reveal-line {
    transform: none !important;
    opacity: 0;
    transition: opacity .38s var(--ease-soft) !important;
  }
  html.js-reveal .reveal-line.is-in { opacity: 1; }

  /* The masked headline rises; without the rise it fades as one block. */
  html.js-reveal .rise-mask > span {
    transform: none !important;
    opacity: 0;
    transition: opacity .42s var(--ease-soft) !important;
    transition-delay: calc(var(--rd, 0s) * 0.25) !important;
  }
  html.js-reveal .rise-mask.is-in > span { opacity: 1; }

  /* Photographs used to settle by scaling back to true, which is movement.
     The entrance is a rise and a fade now (a scale on a fitted picture is a
     crop — see fix 11), so the rise is what is suppressed here. The opacity
     is forced to 1 rather than left to the reveal: if the observer never adds
     .is-in, a reader with motion reduced would otherwise be looking at a
     photograph that is permanently invisible rather than merely unanimated. */
  html.js-reveal .reveal .plate img,
  html.js-reveal .reveal.is-in .plate img { transform: none !important; opacity: 1 !important; }
}

/* ==========================================================================
   THE FOOTER ON A PHONE — A CLOSE, NOT A SECOND PAGE
   --------------------------------------------------------------------------
   Measured at 390px: 1,100px of footer. Where it went:

     brand block          469px   (of which the address is 204)
     Studio, folded        47px
     Collections, folded   47px
     Enquiries, folded     47px
     Workshop            169px   — the one column that never folds
     bottom bar          161px   — a copyright line wrapping to two, and a
                                   row of legal links wrapping to three

   None of that is content, it is spacing decided on a 1400px canvas and never
   revisited for a 390px one: mono set at line-height 2, a status block with
   fourteen pixels under it, and letter-spacing wide enough to wrap three short
   links onto three separate lines. The copy is untouched; only the air is.
   ========================================================================== */
@media (max-width: 760px) {
  /* The workshop column. It carries the studio's status and hours, so unlike
     the link columns it stays open — which is right, and is exactly why its
     internal spacing has to be tighter rather than looser. */
  .foot__col--status { padding-top: 14px; }
  .foot__status { margin-bottom: 9px; font-size: .82rem; }
  .foot__hours { line-height: 1.62; font-size: .6rem; letter-spacing: .12em; }
  .foot__hours + .foot__hours { margin-top: 7px; }

  /* The bottom bar. Two short statements do not need a third of a screen. */
  .foot__bottom { gap: 7px; }
  .foot__bottom p,
  .foot__legal a,
  .foot__legal span {
    font-size: .55rem;
    letter-spacing: .11em;
    line-height: 1.55;
  }
  .foot__legal { gap: 9px; }

  /* The brand block's two paragraphs are set for a wide column. */
  .foot__tagline { line-height: 1.6; }
  .foot__division { margin-top: 8px; line-height: 1.55; }
}

/* The last of the footer's repetition.
   The workshop column ends with "Jaipur, Rajasthan / India" — which the
   address block a few centimetres above it now states in full, with a map
   link. Two addresses in one footer is the same fault as three phone numbers.
   The status and the opening hours are unique to this column and stay. */
@media (max-width: 760px) {
  .foot__col--status .foot__hours + .foot__hours { display: none; }
  .foot__col--status { padding-top: 12px; }
  /* Three short links do not need three lines. */
  .foot__legal { gap: 8px 10px; }
  /* Only the links and the middot separators are held on one line. The studio
     line is a span in this same row and is fifty characters long; included in
     the old blanket rule it could not wrap, so it ran 134px off the right of
     a 375px screen and was cut off. */
  .foot__legal a,
  .foot__legal > span:not(.mono) { white-space: nowrap; }
  .foot__legal .mono { white-space: normal; }
}

/* Email and Instagram share a line.
   Once the phone and WhatsApp icons moved into the address block as real
   buttons, the social row was left holding a single 36px square sitting on its
   own line under the email — which reads as something that used to have
   company rather than as a considered mark. On a phone the two sit together:
   the address is the block, and these are the footnote to it. */
@media (max-width: 760px) {
  .foot__brand .foot__mail {
    display: inline-block;
    vertical-align: middle;
    margin-top: 14px;
  }
  .foot__brand .foot__social {
    display: inline-flex;
    vertical-align: middle;
    margin-top: 14px;
    margin-left: 14px;
  }
  .foot__brand .foot__social a { width: 32px; height: 32px; }
}

/* ==========================================================================
   THE FOOTER ON A LAPTOP
   --------------------------------------------------------------------------
   Measured at 1440x900: 660px of footer against a 900px viewport — three
   quarters of the screen given to the close of the page. 162px of that is
   padding alone (78 above the grid, 48 below it, 36 around the bottom bar),
   set by clamps that reach their maximum on any screen wider than about
   1400px and were never checked against what they add up to.

   The grid's row height is content — the brand column carries the mark, the
   tagline, the division line, the address and the contact marks, and every
   other column stretches to match it. That is left alone. What comes down is
   the air around it, and the address block's own internal spacing, which was
   tuned for a narrow phone column and is looser than it needs to be when the
   block has a third of a wide screen to sit in.
   ========================================================================== */
@media (min-width: 761px) {

  /* The address block, at desktop proportions. */
  .fadr { margin-top: 14px; }
  .fadr__k { margin-bottom: 8px; }
  .fadr__studio { margin-bottom: 5px; }
  .fadr__line { line-height: 1.5; }
  .fadr__go { margin-top: 8px; }
  .fadr__acts { margin-top: 12px; }
  .fadr__act { min-height: 40px; padding: 8px 14px; }

  /* Email and the social mark share a line here too — on a wide screen the
     brand column is the tallest thing in the grid, and every line saved in it
     is a line saved from all five. */
  .foot__brand .foot__mail {
    display: inline-block; vertical-align: middle; margin-top: 14px;
  }
  .foot__brand .foot__social {
    display: inline-flex; vertical-align: middle; margin-top: 14px; margin-left: 14px;
  }
  .foot__brand .foot__social a { width: 32px; height: 32px; }
}

/* The Workshop column now carries the address, so on a phone — where every
   column becomes a row — it is brought up directly beneath the brand rather
   than sitting below three folded link lists. The address is the thing a
   visitor scrolls to the bottom looking for; it should not be last. */
@media (max-width: 620px) {
  .foot__brand { order: -2; }
  .foot__col--status { order: -1; }
  /* Its hours read as a continuation of the address block above them. */
  .foot__col--status .foot__hours { margin-top: 12px; }
}
/* The location line the address block now states in full, in every layout. */
.foot__col--status .foot__hours + .foot__hours { display: none; }

/* The Workshop column earns more width now that it carries the address.
   The grid was 2.4fr 1fr 1.15fr 1fr 1.15fr — proportions set when that last
   column held three short lines. With the address in it, 190px wraps the
   studio name across two lines and forces the call and WhatsApp buttons to
   stack. Taking a little from the brand column and giving it to this one lets
   the name sit on one line and the two buttons sit side by side, and evens out
   the column heights instead of trading a tall left for a tall right. */
@media (min-width: 1000px) {
  .foot__col--status .fadr__act { white-space: nowrap; }
}

/* ==========================================================================
   FOOTER NAVIGATION ON A PHONE — ALL OF IT, VISIBLE
   --------------------------------------------------------------------------
   The three link columns used to collapse behind their headings, which hid
   thirteen links — Our story, Portfolio, Journal and every collection — until
   somebody guessed to tap a chevron.

   Two columns per list is what makes showing them affordable: Studio's four
   links become two rows, Collections' five become three, Enquiries' four
   become two. Thirteen links in roughly the height the three closed headings
   occupied, with nothing hidden and nothing to tap.
   ========================================================================== */
@media (max-width: 620px) {
  /* The heading is a label again, not a control. */
  .foot__col .foot__head {
    cursor: default;
    margin-bottom: 10px;
    font-size: .55rem;
    letter-spacing: .2em;
  }
  /* The chevron the fold used to need. */
  .foot__col .foot__head::after { display: none !important; }

  .foot__col .foot__list {
    display: grid !important;
    /* ONE column, not two.
       The note above is right that two columns per list is what makes showing
       thirteen links affordable — but it was written assuming each .foot__col
       is the full width of the phone. It is not: the column itself is `span 6`,
       so half. Two lists inside half a screen is four links across a 375px
       footer, and each one lands in a 68px box. Measured: "Start a commission"
       needs 75px and "Architectural elements" needs 81px, so both overflowed
       their own column and printed over the link beside them.
       Halved once, at the column, is enough. Each link now has 152px. */
    grid-template-columns: 1fr;
    gap: 2px 14px;
    height: auto !important;
    opacity: 1 !important;
    overflow: visible !important;
  }
  .foot__col .foot__list li { margin-bottom: 0; }
  .foot__col .foot__list a {
    display: block;
    padding: 7px 0;
    font-size: .84rem;
    line-height: 1.35;
  }
  /* A tapped link should not shunt sideways in a two-column grid. */
  .foot__list a:hover { padding-left: 0; }

  /* The columns sit closer together now that each is only a few rows tall. */
  .foot__col { padding-top: 14px; }
}

/* ========================================================================== 
   MOBILE FOOTER COMPLETENESS
   --------------------------------------------------------------------------
   The direction-aware header transform above intentionally remains active on
   phones and tablets too; site.js protects it while the menu is open. The
   footer's groups use the full measure below so no orphaned half-row reads as
   missing content.
   ========================================================================== */

@media (max-width: 620px) {
  .foot__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: 16px;
  }
  .foot__brand,
  .foot__col,
  .foot__col--status { grid-column: 1 / -1; }
  .foot__col .foot__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(18px, 7vw, 34px);
  }
}


/* ==========================================================================
   THE ENTRY
   --------------------------------------------------------------------------
   One flat veil in the page's own colour, lifted once the fonts have settled.
   Without it the opening is three visible events — a flash of fallback type, a
   reflow as three families load, then the reveals firing. With it there is one,
   and the page appears already composed.

   `is-entering` is added by script and never ships in the markup: a reader
   whose JavaScript does not run must never inherit a veil that only script can
   remove.
   ========================================================================== */
.enter {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--sand);
  pointer-events: none;
  opacity: 0;
}
.is-entering .enter { opacity: 1; }
.is-entered .enter {
  opacity: 0;
  transition: opacity .62s cubic-bezier(.16,1,.3,1);
}

/* The hero is deliberately NOT hidden and faded back in.

   A rise on `.hero__inner` was tried and taken out again. It looked good and
   it put the headline, the standfirst and both buttons behind a transition
   that has to complete — so any browser that drops those frames leaves the
   most important thing on the site at opacity zero. The veil above already
   does the work of making the page arrive composed, and it fails safe: it is
   a separate element that gets removed, so nothing the reader needs is ever
   waiting on an animation to give it back.

   The reveals further down the page are a different case — a section that
   never fades in is below the fold, and site.js carries a deadline that takes
   the whole document out of reveal mode if none of them fire. */

@media (prefers-reduced-motion: reduce) and (prefers-reduced-motion: no-preference) { /* OFF by the motion policy — see motion-always.css */
  .is-entering .hero__inner { opacity: 1; transform: none; }
  .is-entered .enter { transition-duration: .01ms; }
}
