﻿/* =====================================================================
   VALENTINA ZALCH — ARCHITECTURE
   Public site stylesheet
   ===================================================================== */

/* The font is loaded with a <link> in each page's <head> rather than an
   @import here, because an @import blocks rendering until the CSS is parsed
   and only then starts fetching the font. */

:root {
  --bg:            #f3f2ef;
  --bg-white:      #ffffff;
  --bg-soft:       #f7f6f3;
  --ink:           #1b1b19;
  --ink-soft:      #3a3a37;
  --muted:         #74726c;
  --muted-light:   #9a9892;
  --line:          #dedcd6;
  --line-strong:   #c4c2bb;
  --accent:        #1b1b19;

  --font: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --wrap: 1360px;
  --gut: clamp(20px, 4vw, 56px);

  /* ---- Spacing scale -------------------------------------------------
     Every margin and padding on the site comes from this list. Nothing is
     nudged by an arbitrary number, which is what keeps the rhythm even. */
  --sp-1:  4px;
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-5:  22px;
  --sp-6:  30px;
  --sp-7:  clamp(34px, 3.4vw, 46px);
  --sp-8:  clamp(44px, 4.6vw, 64px);

  /* Vertical padding inside a band, and the air between major sections. */
  --section-y:     clamp(48px, 5.6vw, 84px);
  --section-y-sm:  clamp(34px, 3.8vw, 56px);

  /* ---- Type ---------------------------------------------------------- */
  --label-size: 10.5px;
  --label-track: 0.16em;
  --measure: 64ch;        /* comfortable reading width for body copy */
  --measure-tight: 46ch;  /* short intros and standfirsts */

  /* ---- Motion --------------------------------------------------------
     Three speeds only: a touch for small state changes, a beat for
     movement, and a slow drift for images and reveals. */
  --ease:      cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
  --t-fast:    .25s;
  --t-med:     .45s;
  --t-slow:    .9s;

  --ratio-card: 4 / 3;    /* one ratio for every project image on the site */
  --zoom: 1.04;           /* one hover zoom everywhere */

  /* The arrow used beside menu items, drawn once and tinted by colour. */
  --nav-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 10'%3E%3Cpath d='M0 5h18M14 1l4 4-4 4' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img { 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, h6, p, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

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

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

/* ---------- Shared primitives ---------- */
/* One container rule for the whole site. Every band's inner element uses it,
   so a heading in the hero sits on the same vertical line as a card in the
   footer. Changing --wrap or --gut moves the entire site together. */
.wrap,
.hero__inner,
.split__inner,
.about-band__inner,
.timeline-section__inner,
.approach-band__inner,
.site-footer__inner {
  max-width: var(--wrap);
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--gut);
  padding-right: var(--gut);
}

.label {
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.4;
}
.label--muted { color: var(--muted); }

/* Large type is set tighter than small type: the bigger the size, the less
   leading and letter-spacing it needs to look even. */
.display {
  font-weight: 300;
  letter-spacing: -0.018em;
  line-height: 1.12;
  color: var(--ink);
  text-wrap: balance;
}
/* Page titles, ~12% smaller than before. Same face, weight and tracking —
   only the size changes, so visitors reach the work sooner. */
.display--xl { font-size: clamp(30px, 4.05vw, 53px); line-height: 1.1; letter-spacing: -0.022em; }
.display--lg { font-size: clamp(28px, 3.4vw, 44px); line-height: 1.12; }
.display--md { font-size: clamp(23px, 2.3vw, 31px); line-height: 1.22; letter-spacing: -0.014em; }

.body-text {
  color: var(--ink-soft);
  font-size: 15px;
  line-height: 1.8;
  max-width: var(--measure);
}
.body-text p + p { margin-top: 1.05em; }
.muted { color: var(--muted); }

.hr { height: 1px; background: var(--line); border: 0; }

/* Rhythm helpers. These exist so spacing lives in the stylesheet and comes
   from the scale, instead of being written inline on individual elements. */
.mt-4 { margin-top: var(--sp-4); }
.mt-5 { margin-top: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); }
.mt-7 { margin-top: var(--sp-7); }
.mb-4 { margin-bottom: var(--sp-4); }
.mb-5 { margin-bottom: var(--sp-5); }
.measure-tight { max-width: var(--measure-tight); }

/* Arrow link — underlined caps label with a trailing arrow */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--ink);
  transition: gap var(--t-med) var(--ease);
}
.arrow-link:hover { gap: 18px; }
.arrow-link .arw { transition: transform var(--t-med) var(--ease); }
.arrow-link:hover .arw { transform: translateX(3px); }

/* The "View all" links. Given a little more presence than a plain label —
   heavier weight, full ink colour and a rule that draws itself in on hover —
   while staying a quiet caps label rather than becoming a button. */
.plain-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink);
  padding-bottom: 7px;
  transition: gap var(--t-med) var(--ease), color var(--t-fast) var(--ease);
}
.plain-link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--line-strong);
  transform: scaleX(1);
  transform-origin: left;
  transition: background var(--t-med) var(--ease), transform var(--t-med) var(--ease-out);
}
.plain-link:hover::after { background: var(--ink); }
.plain-link:hover { gap: 16px; }
.plain-link .arw { transition: transform var(--t-med) var(--ease); }
.plain-link:hover .arw { transform: translateX(3px); }
.plain-link.muted { color: var(--muted); }
.plain-link.muted:hover { color: var(--ink); }

/* Image frame with graceful empty state */
.frame {
  position: relative;
  overflow: hidden;
  background: #e6e4df;
}
.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* From the middle unless a picture names its own point. A card is a
     crop, and the middle of a photograph is not always the subject of it -
     a bridge can sit off to one side and be lost. --focus lets each one
     say where to hold. */
  object-position: var(--focus, center);
  /* Both inside the 350-500ms band: quick enough to feel responsive,
     slow enough to read as a drift rather than a snap. */
  transition: transform var(--t-med) var(--ease-out),
              filter var(--t-med) var(--ease);
  will-change: transform;
}
.frame.is-empty::after {
  content: attr(data-monogram);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: clamp(22px, 3vw, 40px);
  font-weight: 300;
  letter-spacing: 0.22em;
  color: #b9b6ae;
  background:
    repeating-linear-gradient(135deg, rgba(255,255,255,.5) 0 2px, transparent 2px 11px),
    linear-gradient(160deg, #eceae5, #ddd9d2);
}
.frame.is-empty img,
.frame.is-empty picture { display: none; }
.frame picture { display: contents; }

.ratio-16-10 { aspect-ratio: 16 / 10; }
.ratio-4-3   { aspect-ratio: var(--ratio-card); }
.ratio-3-2   { aspect-ratio: 3 / 2; }
.ratio-1-1   { aspect-ratio: 1 / 1; }
.ratio-5-4   { aspect-ratio: 5 / 4; }
.ratio-3-4   { aspect-ratio: 3 / 4; }

/* One ratio for every picture of a project, anywhere on the site: cards,
   home tiles, galleries, interiors, drawings and prev/next thumbnails. */
.card__media .frame,
.gallery-grid .frame,
.drawing .frame,
.pn__thumb .frame,
.detail-hero .frame { aspect-ratio: var(--ratio-card); }

/* =====================================================================
   HEADER
   ===================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 90;
  /* Plain colour first: color-mix is only understood by Safari 16.2 and
     later, and without a fallback an older iPhone gets no header
     background at all and the page scrolls underneath the logo. */
  background: rgba(243, 242, 239, .92);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease), background .3s var(--ease);
}
.site-header.is-stuck { border-bottom-color: var(--line); }

.site-header__inner {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: var(--sp-5) var(--gut);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
}
.site-header__right {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3vw, 44px);
}

.brand { line-height: 1.25; }
.brand__name {
  font-size: clamp(15px, 1.5vw, 20px);
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}
.brand__suffix {
  display: block;
  margin-top: 4px;
  font-size: 9.5px;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted);
}

.site-nav { display: flex; align-items: center; gap: clamp(18px, 2.4vw, 38px); }
.site-nav a {
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  padding-bottom: 4px;
  border-bottom: 1px solid transparent;
  transition: border-color .25s var(--ease), color .25s var(--ease);
  color: var(--ink-soft);
}
.site-nav a:hover { color: var(--ink); border-bottom-color: var(--line-strong); }
.site-nav a.is-active { color: var(--ink); border-bottom-color: var(--ink); }

.nav-toggle {
  width: 26px; height: 18px;
  display: flex; flex-direction: column; justify-content: space-between;
  flex: 0 0 auto;
}
.nav-toggle span {
  display: block; height: 1.4px; width: 100%;
  background: var(--ink);
  transition: transform .35s var(--ease), opacity .25s var(--ease);
}
body.menu-open .nav-toggle span:nth-child(1) { transform: translateY(8.3px) rotate(45deg); }
body.menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.menu-open .nav-toggle span:nth-child(3) { transform: translateY(-8.3px) rotate(-45deg); }

/* The header has to sit ABOVE the open menu, or the close button is
   underneath the overlay and cannot be clicked at all. */
body.menu-open .site-header { z-index: 97; background: transparent; border-bottom-color: transparent; }

/* Dimmed backdrop behind the menu. Clicking it closes. */
.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 94;
  background: rgba(20, 20, 18, .34);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s var(--ease), visibility .4s;
}
body.menu-open .nav-scrim { opacity: 1; visibility: visible; }

/* Slide-out panel */
.nav-panel {
  position: fixed;
  inset: 0;
  z-index: 95;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 var(--gut);
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s var(--ease), visibility .4s;
}
body.menu-open .nav-panel { opacity: 1; visibility: visible; }
.nav-panel ol { max-width: var(--wrap); margin: 0 auto; width: 100%; }
.nav-panel li { border-top: 1px solid var(--line); }
.nav-panel li:last-child { border-bottom: 1px solid var(--line); }
.nav-panel a {
  display: flex;
  align-items: baseline;
  gap: 20px;
  padding: clamp(14px, 2.4vw, 26px) 0;
  font-size: clamp(26px, 5vw, 52px);
  font-weight: 300;
  /* Title case, set as typed. The header capitalises its own links, so the
     menu reads as the quieter, more editorial of the two. */
  letter-spacing: -0.01em;
  transition: padding-left .35s var(--ease), opacity var(--t-fast) var(--ease);
}
.nav-panel a:hover { padding-left: 16px; }
.nav-panel a .idx {
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: var(--muted);
  font-weight: 500;
}
.nav-panel__foot {
  max-width: var(--wrap);
  margin: 42px auto 0;
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  /* Without this the flex row stretches the circles into ovals. */
  align-items: center;
  gap: 10px;
}
/* Small, quiet marks. They sit under the navigation and should stay there:
   noticeable when looked for, never competing with the page links. */
.nav-panel__foot a {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--muted);
  padding: 0;
  transition: color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease-out);
}
.nav-panel__foot a svg { width: 19px; height: 19px; display: block; flex: 0 0 auto; }
.nav-panel__foot a:hover {
  color: var(--ink);
  border-color: var(--line-strong);
  transform: translateY(-2px);
  padding-left: 0;
}

/* =====================================================================
   DESKTOP MENU
   On a wide screen the main links are already on show in the header, so
   taking over the whole window to repeat them is heavy-handed and loses
   the visitor's place. It becomes a panel at the side instead: the page
   stays visible behind it, and the menu reads as a drawer you opened
   rather than a page you navigated to.
   ===================================================================== */
@media (min-width: 901px) {
  .nav-panel {
    left: auto;
    right: 0;
    width: min(430px, 40vw);
    padding: 0 clamp(34px, 3vw, 52px);
    justify-content: center;
    border-left: 1px solid var(--line);
    box-shadow: -40px 0 80px -40px rgba(0, 0, 0, .22);
    transform: translateX(28px);
    transition: opacity .42s var(--ease-out),
                visibility .42s,
                transform .42s var(--ease-out);
  }
  body.menu-open .nav-panel { transform: none; }

  .nav-panel ol { max-width: none; }
  /* Scoped to the list, so the social icons below keep their own shape. */
  .nav-panel ol a {
    font-size: 26px;
    padding: 17px 0;
    gap: 16px;
  }
  .nav-panel ol a .idx { font-size: 9.5px; }

  .nav-panel__foot {
    margin-top: 40px;
    max-width: none;
    gap: 20px;
  }
}

/* =====================================================================
   HERO (home)
   ===================================================================== */
.hero {
  position: relative;
  background: linear-gradient(180deg, #eeece7 0%, #e9e7e1 100%);
  overflow: hidden;
}
/* Same container as .split__inner, .about-band__inner and the footer, so the
   whole page shares one grid. */
.hero__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(280px, 0.78fr) 1.4fr;
  align-items: stretch;
  min-height: min(65vh, 578px);
}
.hero__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(48px, 6.5vw, 96px) clamp(28px, 3.6vw, 58px) clamp(48px, 5.5vw, 84px) 0;
  position: relative;
  z-index: 2;
}
.hero__eyebrow { margin-bottom: clamp(20px, 2.6vw, 34px); color: var(--ink-soft); }
.hero__title {
  font-size: clamp(30px, 3.9vw, 52px);
  font-weight: 300;
  line-height: 1.18;
  letter-spacing: -0.02em;
  max-width: 15ch;
}
.hero__cta { margin-top: clamp(28px, 3.4vw, 46px); align-self: flex-start; }

.hero__media { position: relative; overflow: hidden; background: #ddd9d2; }
.hero__media .frame { position: absolute; inset: 0; }
/* The picture no longer runs under the text, so it gets a clean edge like
   the cards below rather than a fade. */

.hero__counter {
  position: absolute;
  left: var(--gut);
  bottom: clamp(28px, 4vw, 52px);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  color: var(--muted);
}
.hero__counter i { display: block; width: 1px; height: 26px; background: var(--line-strong); }

/* =====================================================================
   HOME — two column preview (Projects / Practice)
   ===================================================================== */
/* Two bands, each carrying its own ground colour edge to edge. The shift
   is deliberately slight — just enough to read as a new chapter without
   drawing attention to the change itself. */
.split { background: var(--bg-white); }
.split__band--projects { background: var(--bg-white); }
.split__band--practice { background: var(--bg-soft); }

/* Full width each, stacked, so the pictures are as large as the page allows. */
.split__block {
  padding-top: var(--section-y);
  padding-bottom: var(--section-y);
}

.split__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
/* The section name leads; the work titles beneath it are set smaller, so
   the eye moves from chapter to piece rather than competing. */
.split__title {
  margin-bottom: 10px;
  font-size: 13px;
  letter-spacing: 0.2em;
  font-weight: 600;
  color: var(--ink);
}
.split__intro { font-size: 13.5px; color: var(--muted); }

.mini-grid {
  margin-top: clamp(18px, 2.2vw, 30px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(14px, 1.7vw, 26px);
}
.mini-card { display: block; }
.mini-card .frame { margin-bottom: var(--sp-4); aspect-ratio: var(--ratio-card); overflow: hidden; }
.mini-card:hover .frame img { transform: scale(var(--zoom)); filter: brightness(1.04); }
.mini-card__title {
  font-size: clamp(15px, 1.35vw, 19px);
  font-weight: 400;
  letter-spacing: -0.008em;
  transition: transform var(--t-med) var(--ease-out);
}
.mini-card:hover .mini-card__title { transform: translateY(-3px); }
.mini-card__foot {
  margin-top: 6px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.mini-card__meta {
  font-size: 12.5px;
  color: var(--muted);
  opacity: .78;
  transition: opacity var(--t-med) var(--ease);
}
.mini-card:hover .mini-card__meta { opacity: 1; }
.mini-card__arrow { color: var(--muted); transition: transform var(--t-med) var(--ease-out), color var(--t-med) var(--ease); }
.mini-card:hover .mini-card__arrow { transform: translateX(7px); color: var(--ink); }
.mini-card__year {
  font-size: 12.5px; color: var(--muted); margin-top: 3px;
  opacity: .78;
  transition: opacity var(--t-med) var(--ease);
}
.mini-card:hover .mini-card__year { opacity: 1; }

/* =====================================================================
   HOME — about band
   ===================================================================== */
.about-band { background: var(--bg-soft); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
/* Deliberately compact: this band supports the work above rather than
   competing with it. */
/* The label is a grid child in its own right so a phone can put it above
   the portrait. Here it is placed back at the head of the text column. */
.about-band__inner {
  padding-top: var(--section-y-sm);
  padding-bottom: var(--section-y-sm);
  display: grid;
  grid-template-columns: 0.72fr 1.05fr 0.92fr;
  gap: clamp(22px, 3vw, 46px);
  align-items: start;
}
.about-band__eyebrow { grid-column: 2; grid-row: 1; margin-bottom: 15px; }
.about-band__media   { grid-column: 1; grid-row: 1 / span 2; }
.about-band__copy    { grid-column: 2; grid-row: 2; }
.about-band .values  { grid-column: 3; grid-row: 1 / span 2; }

.about-band__copy { display: flex; flex-direction: column; }
.about-band__media .frame { aspect-ratio: 1 / 1; }
.about-band__heading {
  margin-bottom: 14px;
  max-width: 18ch;
  font-size: clamp(19px, 1.85vw, 26px);
}
.about-band__body { max-width: 42ch; font-size: 13.5px; line-height: 1.7; }
.about-band__cta { margin-top: 18px; align-self: flex-start; }

.values { display: grid; gap: clamp(13px, 1.6vw, 19px); padding-left: clamp(0px, 1.6vw, 24px); border-left: 1px solid var(--line); }
.value { display: grid; grid-template-columns: 24px 1fr; gap: 13px; align-items: start; }
.value__icon { color: var(--ink); opacity: .82; }
.value__icon svg { width: 21px; height: 21px; display: block; }
.value__title { font-size: 10px; letter-spacing: .15em; text-transform: uppercase; font-weight: 500; margin-bottom: 4px; }
.value__text { font-size: 12px; color: var(--muted); line-height: 1.55; }

/* =====================================================================
   HOME — timeline
   ===================================================================== */
.timeline-section { background: var(--bg-white); }
.timeline-section__inner { padding-top: var(--section-y-sm); padding-bottom: var(--section-y-sm); }
.timeline-section__head {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  margin-bottom: clamp(16px, 2vw, 24px); flex-wrap: wrap;
}

.timeline {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--line-strong);
}
.timeline__item { position: relative; padding: 19px clamp(14px, 2vw, 30px) 2px 0; }
.timeline__item + .timeline__item { border-left: 1px solid var(--line); padding-left: clamp(14px, 2vw, 30px); }
.timeline__item::before {
  content: '';
  position: absolute;
  top: -3.5px;
  left: 0;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ink);
}
.timeline__item + .timeline__item::before { left: clamp(14px, 2vw, 30px); transform: translateX(-50%); left: 0; }
.timeline__period { font-size: 10px; letter-spacing: .12em; color: var(--muted); margin-bottom: 7px; }
.timeline__role { font-size: 13.5px; font-weight: 500; margin-bottom: 2px; }
.timeline__company { font-size: 12.5px; color: var(--ink-soft); margin-bottom: 7px; }
.timeline__desc { font-size: 11.5px; color: var(--muted); line-height: 1.55; max-width: 32ch; }
.timeline__more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 10px;
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ink);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--line-strong);
  transition: gap var(--t-med) var(--ease), border-color var(--t-med) var(--ease);
}
.timeline__more:hover { gap: 13px; border-bottom-color: var(--ink); }
.timeline__more .arw { width: 15px; }

/* =====================================================================
   PAGE HEADERS
   ===================================================================== */
/* A more compact hero: less air above the title and a tighter gap down to
   the supporting line, so the content starts higher up the page. */
.page-head { padding-top: var(--section-y-sm); padding-bottom: var(--sp-6); }
.page-head__eyebrow { margin-bottom: var(--sp-5); }
.page-head__title { max-width: 19ch; margin-bottom: var(--sp-4); }
.page-head__intro {
  max-width: var(--measure-tight);
  font-size: 14px;
  color: var(--muted);
  line-height: 1.75;
}

.filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: clamp(16px, 2.4vw, 34px);
  padding-bottom: var(--sp-7);
}
.filter {
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--muted);
  padding-bottom: 5px;
  border-bottom: 1px solid transparent;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.filter:hover { color: var(--ink); }
.filter.is-active { color: var(--ink); border-bottom-color: var(--ink); }

/* =====================================================================
   PROJECT / EXPERIENCE GRID
   ===================================================================== */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(18px, 2.2vw, 30px);
  padding-bottom: var(--section-y);
}

/* Group headings on the Projects page */
.project-group__title {
  margin-bottom: var(--sp-7);
  padding-top: var(--section-y-sm);
  border-top: 1px solid var(--line-strong);
}
.project-group:first-child .project-group__title {
  padding-top: 0;
  border-top: 0;
}
.project-group .card-grid { padding-bottom: var(--section-y-sm); }
.project-group:last-child .card-grid { padding-bottom: var(--section-y); }
.card {
  display: flex;
  flex-direction: column;
  background: var(--bg-white);
  border: 1px solid var(--line);
  transition: transform var(--t-med) var(--ease-out),
              box-shadow var(--t-med) var(--ease-out),
              border-color var(--t-med) var(--ease);
}
.card:hover {
  transform: translateY(-3px);
  box-shadow: 0 20px 46px -28px rgba(0,0,0,.28);
  border-color: var(--line-strong);
}
.card__media { position: relative; overflow: hidden; }
.card:hover .frame img { transform: scale(var(--zoom)); filter: brightness(1.04); }
.card__num {
  position: absolute;
  top: 14px; left: 14px;
  z-index: 2;
  background: var(--bg-white);
  color: var(--ink);
  font-size: 10.5px;
  letter-spacing: .1em;
  font-weight: 500;
  padding: 5px 9px;
}
.card__body {
  padding: 18px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  flex: 1;
}
/* Hover: the picture lifts and brightens a touch, the title rises, the
   category comes up to full strength and the arrow slides. Four small
   moves on one curve, so it reads as a single gesture. */
.card__title {
  font-size: 17px; font-weight: 400; letter-spacing: -0.012em;
  transition: transform var(--t-med) var(--ease-out);
}
.card:hover .card__title { transform: translateY(-3px); }
.card__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: auto;
}
.card__meta {
  font-size: 12.5px;
  color: var(--muted);
  opacity: .78;
  transition: opacity var(--t-med) var(--ease);
}
.card:hover .card__meta { opacity: 1; }
.card__arrow { color: var(--muted); transition: transform var(--t-med) var(--ease-out), color var(--t-med) var(--ease); }
.card:hover .card__arrow { transform: translateX(7px); color: var(--ink); }

.empty-note {
  padding: 60px 0 90px;
  color: var(--muted);
  font-size: 13.5px;
}

/* =====================================================================
   APPROACH BAND (projects page bottom)
   ===================================================================== */
.approach-band { background: var(--bg-soft); border-top: 1px solid var(--line); }
.approach-band__inner {
  padding-top: var(--section-y);
  padding-bottom: var(--section-y);
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: clamp(28px, 4vw, 64px);
}
.approach-band__right { border-left: 1px solid var(--line); padding-left: clamp(20px, 3vw, 48px); }
.approach-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 2.6vw, 34px); margin-top: 22px; }

/* =====================================================================
   PROJECT DETAIL
   ===================================================================== */
.detail-top { padding-top: var(--section-y-sm); padding-bottom: var(--section-y); }
.back-link { display: inline-flex; align-items: center; gap: 12px; font-size: var(--label-size); letter-spacing: var(--label-track); text-transform: uppercase; font-weight: 500; margin-bottom: var(--sp-7); transition: gap var(--t-med) var(--ease); }
.back-link:hover { gap: 18px; }

.detail-grid {
  display: grid;
  grid-template-columns: minmax(260px, 0.62fr) 1.38fr;
  gap: clamp(28px, 4.4vw, 68px);
  align-items: start;
}
.detail-num { font-size: 11px; letter-spacing: .12em; color: var(--muted); margin-bottom: 16px; }
.detail-title { margin-bottom: 12px; }
.detail-cat { margin-bottom: clamp(20px, 2.6vw, 30px); color: var(--ink-soft); }
.detail-desc { max-width: 40ch; margin-bottom: clamp(26px, 3.4vw, 42px); }

.meta-list { display: grid; gap: 14px; margin-bottom: clamp(26px, 3.4vw, 42px); }
.meta-row { display: grid; grid-template-columns: 108px 1fr; gap: 14px; align-items: start; }
.meta-row dt { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); padding-top: 2px; }
.meta-row dd { margin: 0; font-size: 13.5px; color: var(--ink-soft); }

.credit-note { font-size: 12.5px; color: var(--muted); font-style: italic; line-height: 1.65; max-width: 34ch; margin-bottom: var(--sp-7); }
.credit-note--page { max-width: var(--measure); padding-bottom: var(--section-y); margin-bottom: 0; }

/* Optional outward links (firm website, project page) */
.detail-cat a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 1px;
  transition: border-color .25s var(--ease);
}
.detail-cat a:hover { border-bottom-color: var(--ink); }

.detail-links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: clamp(24px, 3vw, 36px);
}
.ext-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line-strong);
  transition: border-color .25s var(--ease), gap .25s var(--ease);
}
.ext-link:hover { border-bottom-color: var(--ink); gap: 12px; }

.share { border-top: 1px solid var(--line); padding-top: 20px; }
.share__label { margin-bottom: 14px; }
.share__links { display: flex; gap: 18px; }
.share__links a { color: var(--muted); transition: color .25s var(--ease), transform .25s var(--ease); }
.share__links a:hover { color: var(--ink); transform: translateY(-2px); }
.share__links svg { width: 17px; height: 17px; display: block; }

.detail-hero .frame { aspect-ratio: 4 / 3; }

/* Tabbed content */
.tabs-section { padding: var(--section-y) 0; border-top: 1px solid var(--line); }
.tabs-grid { display: grid; grid-template-columns: minmax(180px, 0.36fr) 1.64fr; gap: clamp(26px, 4vw, 58px); align-items: start; }
.tabs-nav { display: flex; flex-direction: column; gap: 18px; }
.tab-btn {
  text-align: left;
  font-size: var(--label-size);
  letter-spacing: var(--label-track);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--muted);
  padding-bottom: 5px;
  border-bottom: 1px solid transparent;
  align-self: flex-start;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.tab-btn:hover { color: var(--ink); }
.tab-btn.is-active { color: var(--ink); border-bottom-color: var(--ink); }
.tab-panels { border-left: 1px solid var(--line); padding-left: clamp(20px, 3.2vw, 52px); min-height: 150px; }
.tab-panel { display: none; }
.tab-panel.is-active { display: block; animation: fadeUp .45s var(--ease); }
.tab-panel__title { margin-bottom: 20px; }
.tab-panel__body { max-width: 62ch; }

@keyframes fadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* Galleries */
.gallery-section { padding-bottom: var(--section-y); }
.gallery-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(10px, 1.2vw, 16px); }
.gallery-grid--3 { grid-template-columns: repeat(3, 1fr); }
.gallery-grid button { display: block; width: 100%; padding: 0; }
.gallery-grid .frame { aspect-ratio: 4 / 3; }
.gallery-grid button:hover .frame img { transform: scale(var(--zoom)); }

.drawings-section { padding-bottom: var(--section-y); }
.drawings-section__label { margin-bottom: var(--sp-5); }
.drawings-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(12px, 1.6vw, 24px); }
.drawing .frame { aspect-ratio: 4 / 3; background: #f6f5f2; }
.drawing__caption { margin-top: 12px; font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }

/* PDF drawings */
.drawing__link { position: relative; display: block; overflow: hidden; }
.drawing__link:hover .frame img { transform: scale(var(--zoom)); }
.drawing__badge {
  position: absolute;
  top: 10px; right: 10px;
  background: var(--ink);
  color: var(--bg);
  font-size: 9px;
  letter-spacing: .12em;
  font-weight: 500;
  padding: 4px 7px;
}
.drawing__pdf {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--muted);
  background: #f6f5f2;
  border: 1px solid var(--line);
  transition: color .3s var(--ease), border-color .3s var(--ease);
}
.drawing__pdf svg { width: 30px; height: 30px; }
.drawing__pdf span { font-size: 9.5px; letter-spacing: .16em; }
.drawing__link:hover .drawing__pdf { color: var(--ink); border-color: var(--line-strong); }

/* Prev / next */
.prevnext { border-top: 1px solid var(--line); padding: var(--section-y-sm) 0 var(--section-y); }
.prevnext__inner { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 4vw, 60px); }
.pn { display: flex; flex-direction: column; gap: 14px; }
.pn--next { align-items: flex-end; text-align: right; }
.pn__label { color: var(--muted); }
.pn__link { display: flex; align-items: center; gap: 16px; }
.pn--next .pn__link { flex-direction: row-reverse; }
.pn__thumb { width: 78px; flex: 0 0 78px; }
.pn__thumb .frame { aspect-ratio: 4 / 3; }
.pn__title { font-size: 14.5px; font-weight: 400; }
.pn__sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
.pn__arrow { margin-top: 10px; color: var(--muted); transition: transform .3s var(--ease), color .3s var(--ease); }
.pn:hover .pn__arrow { color: var(--ink); }
.pn--prev:hover .pn__arrow { transform: translateX(-5px); }
.pn--next:hover .pn__arrow { transform: translateX(5px); }
.pn.is-disabled { opacity: .32; pointer-events: none; }

/* =====================================================================
   ABOUT / CV / CONTACT pages
   ===================================================================== */
.about-page { padding-bottom: var(--section-y); }
.about-page__grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(30px, 4.6vw, 72px);
  align-items: start;
}
.about-page__media .frame { aspect-ratio: 3 / 4; }
.about-page__intro { font-size: 17px; line-height: 1.6; font-weight: 300; color: var(--ink); margin-bottom: 26px; max-width: 48ch; }

.chips { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 18px; }
.chip {
  border: 1px solid var(--line);
  background: var(--bg-white);
  padding: 7px 13px;
  font-size: 12px;
  color: var(--ink-soft);
}

.cv-section { padding-bottom: var(--section-y); }
.cv-section__label { margin-bottom: var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line-strong); }
.cv-row {
  display: grid;
  grid-template-columns: 170px 1fr;
  gap: clamp(18px, 3vw, 48px);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--line);
}
.cv-row__period { font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); padding-top: 3px; }
.cv-row__role { font-size: 16px; font-weight: 400; margin-bottom: 3px; }
.cv-row__company { font-size: 13px; color: var(--ink-soft); margin-bottom: 8px; }
.cv-row__desc { font-size: 13px; color: var(--muted); max-width: 60ch; }

.contact-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(30px, 4.6vw, 72px);
  padding-bottom: var(--section-y);
  align-items: start;
}
.contact-block { border-top: 1px solid var(--line-strong); padding-top: 20px; margin-bottom: 30px; }
.contact-block__label { margin-bottom: 16px; }
.contact-big {
  font-size: clamp(18px, 2.1vw, 26px);
  font-weight: 300;
  letter-spacing: -0.01em;
  display: inline-block;
  border-bottom: 1px solid var(--line);
  padding-bottom: 4px;
  transition: border-color .25s var(--ease);
}
.contact-big:hover { border-bottom-color: var(--ink); }
.contact-lines { display: grid; gap: 8px; font-size: 14px; color: var(--ink-soft); }
.social-list { display: grid; gap: 12px; }
.social-list a {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  font-size: 14px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--line);
  transition: padding-left .3s var(--ease);
}
.social-list a:hover { padding-left: 8px; }
.social-list .arw { color: var(--muted); }

/* =====================================================================
   FOOTER
   ===================================================================== */
.site-footer { background: var(--bg-soft); border-top: 1px solid var(--line); }
.site-footer__inner {
  padding-top: var(--section-y-sm);
  padding-bottom: var(--section-y-sm);
  display: grid;
  grid-template-columns: 1.35fr 0.85fr 0.7fr 1fr;
  gap: clamp(24px, 3vw, 48px);
}
.site-footer__col + .site-footer__col { border-left: 1px solid var(--line); padding-left: clamp(18px, 2.6vw, 40px); }
.site-footer__label { margin-bottom: 18px; color: var(--muted); }
.site-footer__cta-title {
  font-size: clamp(15px, 1.5vw, 18px);
  font-weight: 400;
  line-height: 1.5;
  max-width: 22ch;
  margin-bottom: 22px;
}
.site-footer__lines { display: grid; gap: 9px; font-size: 13px; color: var(--ink-soft); }
.site-footer__lines a:hover { border-bottom: 1px solid var(--line-strong); }
.site-footer__socials { display: grid; gap: 9px; font-size: 13px; color: var(--ink-soft); }
.site-footer__socials a { transition: padding-left .28s var(--ease); }
.site-footer__socials a:hover { padding-left: 6px; }
.site-footer__brand { text-align: right; display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-start; }
.site-footer__mono {
  font-size: clamp(30px, 3.6vw, 46px);
  font-weight: 300;
  letter-spacing: 0.05em;
  line-height: 1;
  margin-bottom: 16px;
}
.site-footer__fine { font-size: 11.5px; color: var(--muted); line-height: 1.7; }

/* =====================================================================
   LIGHTBOX
   ===================================================================== */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(20,20,18,.94);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(20px, 5vw, 70px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s var(--ease), visibility .3s;
}
.lightbox.is-open { opacity: 1; visibility: visible; }
.lightbox img { max-width: 100%; max-height: 88vh; object-fit: contain; }
.lightbox__close, .lightbox__nav {
  position: absolute;
  color: #f3f2ef;
  width: 46px; height: 46px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(243,242,239,.28);
  transition: background .25s var(--ease);
}
.lightbox__close:hover, .lightbox__nav:hover { background: rgba(243,242,239,.14); }
.lightbox__close { top: 22px; right: 22px; }
.lightbox__nav--prev { left: 22px; top: 50%; transform: translateY(-50%); }
.lightbox__nav--next { right: 22px; top: 50%; transform: translateY(-50%); }
.lightbox__count { position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%); color: rgba(243,242,239,.7); font-size: 11px; letter-spacing: .14em; }

/* =====================================================================
   REVEAL ANIMATION
   ===================================================================== */
/* =====================================================================
   PAGE TRANSITIONS
   The page fades out before following an internal link, and the incoming
   page introduces itself through the section reveals below.

   Deliberately one-directional: an entrance animation would have to start
   the body at zero opacity, and anything that stops it finishing — a
   backgrounded tab, a stalled frame, a script error — leaves the whole
   site invisible. Fading out only can never fail that way, because the
   page's resting state is always fully visible.
   ===================================================================== */
body.is-leaving {
  /* Was opacity: 0, and that is what stopped the monogram ever being
     seen on the way out. The screen is a child of the body, so fading
     the body faded the screen along with it - a parent's opacity
     composites everything inside it as one group, and no opacity set on
     the child can climb back out of that.

     The monogram covers the page now, which is a better transition than
     dimming it anyway. The rule is left in place, doing nothing, because
     a page still cached from before will go on adding this class. */
  opacity: 1;
}

/* A short, slow rise. Small distance and a long ease read as considered;
   a big jump reads as a effect. */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--t-slow) var(--ease-out),
              transform var(--t-slow) var(--ease-out);
}
.reveal.is-in { opacity: 1; transform: none; }

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

/* =====================================================================
   RESPONSIVE
   ===================================================================== */
@media (max-width: 1080px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
  .gallery-grid, .drawings-grid { grid-template-columns: repeat(2, 1fr); }
  .about-band__inner { grid-template-columns: 0.85fr 1.15fr; }
  .about-band__media { grid-column: 1 / -1; order: -1; }
  .about-band__media .frame { aspect-ratio: 16 / 9; }
  .timeline { grid-template-columns: repeat(2, 1fr); }
  .timeline__item:nth-child(odd) { border-left: 0; padding-left: 0; }
  .timeline__item:nth-child(n+3) { border-top: 1px solid var(--line); margin-top: 4px; }
  .site-footer__inner { grid-template-columns: 1fr 1fr; }
  .site-footer__col:nth-child(3) { border-left: 0; padding-left: 0; }
  .site-footer__col:nth-child(n+3) { border-top: 1px solid var(--line); padding-top: 28px; }
  .site-footer__brand { align-items: flex-start; text-align: left; }
}

@media (max-width: 900px) {
  .site-nav { display: none; }
  .hero__inner { grid-template-columns: 1fr; min-height: 0; padding: 0; }
  .hero__text { padding: clamp(40px, 8vw, 70px) var(--gut) clamp(26px, 5vw, 34px); }
  /* Same reasoning as the phone rules further down: once the hero stacks,
     the absolutely-positioned picture has no sibling to give the row a
     height, so the shape moves onto the picture itself. */
  .hero__media { order: -1; align-self: start; aspect-ratio: auto; height: auto; }
  .hero__media .frame {
    position: relative;
    inset: auto;
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
  }

  /* Stacked, the counter would sit on top of the hero button, so it
     becomes a normal row underneath the text instead. */
  .hero__counter {
    position: static;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 0 var(--gut) clamp(30px, 7vw, 46px);
  }
  .hero__counter i { width: 26px; height: 1px; }
  .hero__title { max-width: 18ch; }


  .detail-grid { grid-template-columns: 1fr; }
  .detail-hero { order: -1; }
  .tabs-grid { grid-template-columns: 1fr; }
  .tabs-nav { flex-direction: row; flex-wrap: wrap; gap: 16px 24px; }
  .tab-panels { border-left: 0; padding-left: 0; border-top: 1px solid var(--line); padding-top: 24px; }

  .approach-band__inner { grid-template-columns: 1fr; }
  .approach-band__right { border-left: 0; padding-left: 0; border-top: 1px solid var(--line); padding-top: 26px; }

  .about-page__grid, .contact-grid { grid-template-columns: 1fr; }
  .about-page__media .frame { aspect-ratio: 4 / 3; }

  .values { border-left: 0; padding-left: 0; grid-template-columns: 1fr 1fr; }
}

@media (max-width: 640px) {
  body { font-size: 14.5px; }
  .card-grid { grid-template-columns: 1fr; }
  /* One across on a phone: the pictures are the point, and nothing is hidden. */
  .mini-grid { grid-template-columns: 1fr; }
  .gallery-grid, .drawings-grid { grid-template-columns: 1fr 1fr; }
  .timeline { grid-template-columns: 1fr; }
  .timeline__item { border-left: 0 !important; padding-left: 0 !important; }
  .timeline__item + .timeline__item { border-top: 1px solid var(--line); }
  .values { grid-template-columns: 1fr; }
  .meta-row { grid-template-columns: 92px 1fr; gap: 10px; }
  .cv-row { grid-template-columns: 1fr; gap: 8px; }
  .site-footer__inner { grid-template-columns: 1fr; }
  .site-footer__col + .site-footer__col { border-left: 0; padding-left: 0; border-top: 1px solid var(--line); padding-top: 24px; }
  .prevnext__inner { grid-template-columns: 1fr; gap: 28px; }
  .pn--next { align-items: flex-start; text-align: left; }
  .pn--next .pn__link { flex-direction: row; }
  .approach-grid { grid-template-columns: 1fr; }
  .filters { justify-content: flex-start; }
  .lightbox__nav { width: 40px; height: 40px; }
}


/* =====================================================================
   =====================================================================
   MOBILE
   ---------------------------------------------------------------------
   All of this sits inside media queries, so the desktop site is
   untouched by every rule below.

   The phone layout is not the desktop one squeezed down. It is set like
   a printed monograph: one work per spread, pictures large and
   uninterrupted, type quiet, and a lot of air between things.
   Photography comes first and everything else gets out of its way.

   PART 1 - the scale, and the header
   ===================================================================== */

@media (max-width: 768px) {

  /* One set of numbers drives the whole phone layout, exactly as the
     desktop tokens do. Change --gut or --section-y and everything moves
     together, which is what keeps the grid honest. */
  :root {
    --gut: 24px;
    --section-y: 80px;
    --section-y-sm: 56px;
    --radius: 12px;

    --sp-5: 20px;
    --sp-6: 24px;
    --sp-7: 32px;
    --sp-8: 48px;

    --measure: 40ch;
    --measure-tight: 34ch;

    /* Touch, not hover: shorter and firmer than the desktop timings. */
    --t-fast: .2s;
    --t-med:  .3s;
    --t-slow: .6s;
  }

  body {
    font-size: 17px;
    line-height: 1.6;
    -webkit-text-size-adjust: 100%;   /* iOS must not resize text in landscape */
  }

  /* Notch, Dynamic Island and home indicator. */
  .site-header__inner { padding-top: calc(14px + env(safe-area-inset-top)); }
  .site-footer__inner { padding-bottom: calc(var(--section-y-sm) + env(safe-area-inset-bottom)); }

  /* Pictures become objects on the page rather than panels of a grid. */
  .frame { border-radius: var(--radius); }

  /* ---- Type ---- */
  .display--xl { font-size: clamp(34px, 10.5vw, 44px); line-height: 1.12; letter-spacing: -0.02em; }
  .display--lg { font-size: clamp(27px, 8vw, 34px); line-height: 1.18; }
  .display--md { font-size: clamp(21px, 6vw, 26px); line-height: 1.24; }
  .body-text   { font-size: 16px; line-height: 1.68; }
  .label       { font-size: 11px; letter-spacing: .15em; }

  /* Nothing tappable is smaller than a fingertip. */
  .arrow-link, .plain-link, .back-link, .filter, .tab-btn,
  .social-list a, .ext-link, .contact-big {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }

  /* ---- Header: small, fixed, and tighter once you scroll ---- */
  .site-header {
    background: rgba(243, 242, 239, .95);
    background: color-mix(in srgb, var(--bg) 94%, transparent);
  }
  .site-header__inner {
    padding-bottom: 14px;
    transition: padding var(--t-med) var(--ease);
  }
  .site-header.is-stuck .site-header__inner {
    padding-top: calc(9px + env(safe-area-inset-top));
    padding-bottom: 9px;
  }
  .brand__name   { font-size: 14px; letter-spacing: .13em; }
  .brand__suffix { font-size: 8.5px; letter-spacing: .26em; margin-top: 3px; }

  .nav-toggle {
    width: 44px;
    height: 44px;
    padding: 13px 9px;
    margin-right: -9px;     /* keeps the icon optically on the margin */
  }

  /* ---- Menu ----
     The same warm ground as the rest of the site, so opening it reads as
     the page making room rather than a different interface arriving. It
     fades and rises a little, which is enough movement to feel deliberate
     without announcing itself. */
  .nav-scrim { display: none; }          /* nothing behind it to dim */

  .nav-panel {
    background: var(--bg);
    justify-content: flex-start;
    padding-left: var(--gut);
    padding-right: var(--gut);
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    transform: translateY(10px);
    transition: opacity .34s var(--ease-out),
                visibility .34s,
                transform .34s var(--ease-out);
  }
  body.menu-open .nav-panel { transform: none; }

  .nav-panel ol { margin-top: calc(92px + env(safe-area-inset-top)); }
  .nav-panel li,
  .nav-panel li:last-child { border-color: var(--line); }

  /* Scoped to the navigation list. Written as ".nav-panel a" it also caught
     the social icons underneath and stretched them into ovals. */
  .nav-panel ol a {
    color: var(--ink);
    font-size: 27px;
    padding: 21px 0;
    min-height: 66px;
    align-items: center;
    justify-content: space-between;
  }
  .nav-panel ol a:hover { padding-left: 0; }
  .nav-panel ol a:active { opacity: .45; }
  .nav-panel ol a .idx { display: none; }

  /* A quiet arrow that steps across when tapped. */
  /* Written as longhand. Safari does not accept a size after a slash in
     the -webkit-mask shorthand, so the whole declaration is thrown away
     and the arrow becomes a solid block. */
  .nav-panel ol a::after {
    content: '';
    width: 18px;
    height: 8px;
    flex: 0 0 18px;
    background: currentColor;
    opacity: .3;
    -webkit-mask-image: var(--nav-arrow);
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-position: center;
    -webkit-mask-size: contain;
    mask-image: var(--nav-arrow);
    mask-repeat: no-repeat;
    mask-position: center;
    mask-size: contain;
    transition: transform var(--t-med) var(--ease-out), opacity var(--t-med) var(--ease);
  }
  .nav-panel ol a:active::after { transform: translateX(5px); opacity: .75; }

  .nav-panel__foot {
    margin-top: auto;
    margin-bottom: 34px;
    gap: 12px;
  }
}

/* =====================================================================
   MOBILE - PART 2
   The homepage: hero, the work, about, timeline, footer.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- Hero ----
     About a screenful: a large picture, then the line, then one link.
     Nothing else. The image runs to both edges, because on a phone a
     margin around the opening photograph only makes it look smaller. */
  .hero__inner { padding: 0; }

  /* The picture is lifted out of the flow by "position: absolute", so it
     adds no height of its own. Across the desktop layout that is fine:
     the text column sits beside it and props the row open. Stacked on a
     phone there is nothing beside it, so the row has nothing to measure
     and WebKit gives it no height at all — the opening image simply is
     not there on an iPhone.

     The shape is therefore carried by the picture itself, back in normal
     flow, and the band takes its height from that. Nothing depends on a
     sibling any more. */
  .hero__media {
    order: -1;
    align-self: start;
    aspect-ratio: auto;
    height: auto;
    margin: 0;
  }
  .hero__media .frame {
    position: relative;
    inset: auto;
    width: 100%;
    height: auto;
    border-radius: 0;
    aspect-ratio: 6 / 5;
    aspect-ratio: var(--hero-mobile-ratio, 6 / 5);
  }

  .hero__text {
    padding: 32px var(--gut) 0;
    justify-content: flex-start;
  }
  .hero__eyebrow { margin-bottom: 18px; }
  .hero__title {
    font-size: clamp(28px, 8.4vw, 35px);
    line-height: 1.2;
    max-width: 16ch;
  }
  .hero__cta { margin-top: 26px; }

  /* Decoration is the first thing to go on a small screen. */
  .hero__counter { display: none; }

  .hero { padding-bottom: var(--section-y-sm); }

  /* ---- The work ----
     One project per screenful. No grid, no columns, nothing to compare
     against — you meet each building on its own. */
  .split__block {
    padding-top: var(--section-y);
    padding-bottom: var(--section-y);
  }
  .split__block + .split__block { border-top: 0; }

  .split__head {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }
  .split__title { margin-bottom: 4px; }
  .split__intro { font-size: 15px; }
  .split__head .plain-link { margin-top: 12px; }

  .mini-grid {
    grid-template-columns: 1fr;
    gap: var(--section-y-sm);
    margin-top: var(--sp-7);
  }
  .mini-card .frame {
    aspect-ratio: var(--ratio-card);
    margin-bottom: 22px;
  }
  .mini-card__title {
    font-size: 27px;
    font-weight: 300;
    letter-spacing: -0.015em;
    line-height: 1.2;
  }
  .mini-card__meta,
  .mini-card__year { font-size: 14px; opacity: 1; }
  .mini-card__foot { margin-top: 10px; min-height: 24px; }
  .mini-card__arrow { color: var(--ink); }

  /* ---- About ----
     Portrait first, then the words, then the values as cards you take
     one at a time. */
  .about-band__inner {
    grid-template-columns: 1fr;
    gap: var(--sp-7);
    padding-top: var(--section-y);
    padding-bottom: var(--section-y);
  }
  .about-band__media { order: -1; grid-column: auto; }
  .about-band__media .frame { aspect-ratio: 4 / 5; }
  .about-band__heading { font-size: 25px; max-width: 20ch; margin-bottom: 16px; }
  .about-band__body { font-size: 16px; max-width: none; }
  .about-band__cta { margin-top: 24px; }

  .values {
    grid-template-columns: 1fr;
    gap: 12px;
    padding-left: 0;
    border-left: 0;
  }
  .value {
    grid-template-columns: 26px 1fr;
    gap: 16px;
    background: var(--bg-white);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 20px;
  }
  .value__icon svg { width: 24px; height: 24px; }
  .value__title { font-size: 11px; margin-bottom: 7px; }
  .value__text { font-size: 14.5px; line-height: 1.6; }

  /* ---- Timeline ----
     Upright, so it reads as a run of years rather than a table. */
  .timeline-section__inner {
    padding-top: var(--section-y);
    padding-bottom: var(--section-y);
  }
  .timeline-section__head { margin-bottom: var(--sp-7); }

  .timeline { display: block; border-top: 0; }
  .timeline__item,
  .timeline__item + .timeline__item {
    position: relative;
    border: 0;
    border-left: 1px solid var(--line) !important;
    margin: 0;
    padding: 0 0 34px 26px !important;
  }
  .timeline__item:last-child { padding-bottom: 4px !important; }
  .timeline__item::before,
  .timeline__item + .timeline__item::before {
    left: -5px;
    top: 3px;
    transform: none;
    width: 9px;
    height: 9px;
    background: var(--bg-white);
    border: 1px solid var(--ink);
  }
  .timeline__period  { font-size: 11px; margin-bottom: 9px; }
  .timeline__role    { font-size: 17px; font-weight: 400; margin-bottom: 3px; }
  .timeline__company { font-size: 14.5px; margin-bottom: 9px; }
  .timeline__desc    { font-size: 14.5px; line-height: 1.6; max-width: none; }

  /* ---- Footer ---- */
  .site-footer__inner {
    grid-template-columns: 1fr;
    gap: var(--sp-7);
    padding-top: var(--section-y-sm);
  }
  .site-footer__col + .site-footer__col {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--line);
    padding-top: var(--sp-6);
  }
  .site-footer__cta-title { font-size: 19px; max-width: 20ch; }
  .site-footer__lines,
  .site-footer__socials { font-size: 15.5px; gap: 2px; }
  .site-footer__lines a,
  .site-footer__socials a {
    min-height: 44px;
    display: flex;
    align-items: center;
  }
  .site-footer__brand { align-items: flex-start; text-align: left; }
}

/* =====================================================================
   MOBILE - PART 3
   Listings, project pages, About, CV, Contact, and touch feedback.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- Page heads ---- */
  .page-head { padding-top: var(--section-y-sm); padding-bottom: var(--sp-7); }
  .page-head__eyebrow { margin-bottom: 16px; }
  .page-head__title { max-width: 15ch; margin-bottom: 18px; }
  .page-head__intro { font-size: 16px; line-height: 1.65; max-width: none; }

  /* Filters run off the edge and scroll, rather than wrapping into a
     block of text that competes with the heading. */
  .filters {
    justify-content: flex-start;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 22px;
    padding-bottom: var(--sp-7);
    margin-left: calc(var(--gut) * -1);
    margin-right: calc(var(--gut) * -1);
    padding-left: var(--gut);
    padding-right: var(--gut);
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .filters::-webkit-scrollbar { display: none; }
  .filter { white-space: nowrap; flex: 0 0 auto; }

  /* ---- Listings ----
     The bordered, lifting card is a desktop device. On a phone the
     picture is the card. */
  .card-grid {
    grid-template-columns: 1fr;
    gap: var(--section-y-sm);
    padding-bottom: var(--section-y);
  }
  .card { background: transparent; border: 0; box-shadow: none; }
  .card:hover { transform: none; box-shadow: none; }
  .card__media { border-radius: var(--radius); overflow: hidden; }
  .card__num {
    top: 12px; left: 12px;
    background: rgba(255,255,255,.92);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    border-radius: 4px;
  }
  .card__body { padding: 20px 0 0; gap: 8px; }
  .card__title {
    font-size: 27px;
    font-weight: 300;
    letter-spacing: -0.015em;
    line-height: 1.2;
  }
  .card__meta { font-size: 14px; opacity: 1; }
  .card__foot { min-height: 24px; }
  .card__arrow { color: var(--ink); }

  .project-group__title {
    font-size: 21px;
    margin-bottom: var(--sp-7);
    padding-top: var(--section-y-sm);
  }
  .project-group .card-grid { padding-bottom: var(--section-y-sm); }

  .approach-band__inner { padding-top: var(--section-y); padding-bottom: var(--section-y); }
  .approach-grid { grid-template-columns: 1fr; gap: 12px; margin-top: var(--sp-6); }
  .approach-band__right { padding-top: var(--sp-7); }
  .approach-grid .value {
    background: var(--bg-white);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 20px;
  }

  /* ---- Project and experience pages ----
     Pictures run to the edges of the screen; the words keep their
     margins. That contrast is what makes a page feel edited. */
  .detail-top { padding-top: var(--sp-6); padding-bottom: var(--section-y-sm); }
  .back-link { margin-bottom: var(--sp-6); }

  .detail-grid { gap: var(--sp-7); }
  .detail-hero {
    margin-left: calc(var(--gut) * -1);
    margin-right: calc(var(--gut) * -1);
  }
  .detail-hero .frame { border-radius: 0; }

  .detail-num { font-size: 11px; margin-bottom: 12px; }
  .detail-title { font-size: clamp(28px, 8.4vw, 35px); line-height: 1.16; margin-bottom: 10px; }
  .detail-cat { margin-bottom: var(--sp-6); }
  .detail-desc { font-size: 16px; max-width: none; margin-bottom: var(--sp-7); }

  /* Facts as a list, not a squeezed two-column table. */
  .meta-list { gap: 0; margin-bottom: var(--sp-7); }
  .meta-row {
    grid-template-columns: 1fr;
    gap: 3px;
    padding: 13px 0;
    border-bottom: 1px solid var(--line);
  }
  .meta-row:first-child { border-top: 1px solid var(--line); }
  .meta-row dt { font-size: 10px; padding-top: 0; }
  .meta-row dd { font-size: 15.5px; }

  .detail-links { gap: 14px; margin-bottom: var(--sp-7); }
  .credit-note { max-width: none; font-size: 13.5px; }
  .share__links { gap: 20px; }
  .share__links a { min-width: 44px; min-height: 44px; display: flex; align-items: center; }
  .share__links svg { width: 19px; height: 19px; }

  /* Tabs scroll sideways instead of wrapping into an untidy block. */
  .tabs-section { padding: var(--section-y-sm) 0; }
  .tabs-grid { gap: var(--sp-6); }
  .tabs-nav {
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 24px;
    margin-left: calc(var(--gut) * -1);
    margin-right: calc(var(--gut) * -1);
    padding: 0 var(--gut) 2px;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .tabs-nav::-webkit-scrollbar { display: none; }
  .tab-btn { white-space: nowrap; flex: 0 0 auto; }
  .tab-panels { padding-top: var(--sp-6); }
  .tab-panel__title { margin-bottom: 14px; }
  .tab-panel__body { font-size: 16px; max-width: none; }

  /* Galleries: one picture at a time, edge to edge. */
  .gallery-section { padding-bottom: var(--section-y-sm); }
  .gallery-grid,
  .gallery-grid--3 {
    grid-template-columns: 1fr;
    gap: 14px;
    margin-left: calc(var(--gut) * -1);
    margin-right: calc(var(--gut) * -1);
  }
  .gallery-grid .frame { border-radius: 0; }

  .drawings-section { padding-bottom: var(--section-y-sm); }
  .drawings-grid { grid-template-columns: 1fr 1fr; gap: 14px; }
  .drawing__caption { margin-top: 10px; }

  .prevnext { padding: var(--sp-7) 0 var(--section-y-sm); }
  .prevnext__inner { gap: var(--sp-7); }
  .pn__thumb { width: 88px; flex-basis: 88px; }
  .pn__title { font-size: 16px; }

  /* ---- About ---- */
  .about-page__grid { gap: var(--sp-8); }
  .about-page__media .frame { aspect-ratio: 4 / 5; }
  .about-page__intro { font-size: 19px; line-height: 1.55; max-width: none; margin-bottom: var(--sp-6); }
  .about-page .body-text { max-width: none; }
  .chips { gap: 8px; margin-top: 16px; }
  .chip { font-size: 13px; padding: 9px 14px; border-radius: 999px; }

  /* ---- CV ---- */
  .cv-section { padding-bottom: var(--section-y-sm); }
  .cv-row { grid-template-columns: 1fr; gap: 6px; padding: var(--sp-5) 0; }
  .cv-row__period  { font-size: 11px; }
  .cv-row__role    { font-size: 18px; }
  .cv-row__company { font-size: 14.5px; }
  .cv-row__desc    { font-size: 14.5px; line-height: 1.6; max-width: none; }

  /* ---- Contact: big, obvious, one-handed ---- */
  .contact-grid { gap: var(--sp-8); padding-bottom: var(--section-y); }
  .contact-block { margin-bottom: var(--sp-6); }
  .contact-big { font-size: 21px; padding: 6px 0 8px; }
  .contact-lines { font-size: 16px; }
  .social-list { gap: 0; }
  .social-list a {
    font-size: 16px;
    padding: 17px 0;
    min-height: 56px;
    border-bottom: 1px solid var(--line);
  }
  .social-list a:first-child { border-top: 1px solid var(--line); }
  .social-list a:hover { padding-left: 0; }

  /* ---- Touch ----
     A phone has no hover, so the feedback comes on the press. Small,
     quick, and over before you notice it. */
  .mini-card:active .frame img,
  .card:active .frame img,
  .gallery-grid button:active .frame img,
  .drawing__link:active .frame img { transform: scale(1.03); }

  .arrow-link:active,
  .plain-link:active,
  .contact-big:active,
  .social-list a:active,
  .nav-panel__foot a:active { transform: scale(.98); }

  .mini-card:active .mini-card__arrow,
  .card:active .card__arrow { transform: translateX(5px); }
  .arrow-link:active .arw,
  .plain-link:active .arw { transform: translateX(4px); }

  /* Hover styling is meaningless on touch and can stick after a tap. */
  .mini-card:hover .mini-card__title,
  .card:hover .card__title { transform: none; }

  .lightbox { padding: 16px; }
  .lightbox__close { top: calc(14px + env(safe-area-inset-top)); right: 14px; }
  .lightbox__nav { width: 44px; height: 44px; }
  .lightbox__count { bottom: calc(16px + env(safe-area-inset-bottom)); }
}

/* Very small phones: hold the layout, ease the type down a step. */
@media (max-width: 360px) {
  :root { --gut: 18px; }
  .hero__title { font-size: 26px; }
  .mini-card__title,
  .card__title { font-size: 24px; }
  .nav-panel a { font-size: 23px; }
}

/* =====================================================================
   MOBILE - PART 4
   Refinements: type hierarchy, the two grounds, the order of the about
   band, and the shortened timeline.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- Hierarchy ----
     The section name is given more weight and the work titles less, so
     the eye reads chapter first and piece second instead of meeting two
     things of equal size. */
  .split__title {
    font-size: 14px;
    letter-spacing: 0.2em;
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 8px;
  }
  .split__intro { font-size: 15px; }

  .mini-card__title,
  .card__title { font-size: 23px; line-height: 1.24; }
  .mini-card__meta,
  .mini-card__year,
  .card__meta { font-size: 13.5px; }

  /* ---- The two grounds ----
     Both bands keep their own colour on a phone as well, so scrolling
     from one to the other still reads as turning a page. */
  .split__band--practice { background: var(--bg-soft); }
  .split__band--practice + .split__band,
  .split__band--projects + .split__band { border-top: 0; }

  /* ---- About: label, portrait, words, values ----
     Reading order on a phone, rather than the desktop arrangement of
     columns folded flat. */
  .about-band__inner {
    display: flex;
    flex-direction: column;
    /* Must be stated. The desktop rule sets align-items: start, which in a
       column means every child shrinks to its content width — and the
       portrait's only content is a frame sized by aspect ratio, with no
       width of its own, so it collapsed to nothing and disappeared. */
    align-items: stretch;
    gap: 0;
  }
  .about-band__eyebrow {
    order: 1;
    margin-bottom: 18px;
  }
  .about-band__media {
    order: 2;
    margin-bottom: var(--sp-7);
  }
  .about-band__copy {
    order: 3;
    margin-bottom: var(--sp-7);
  }
  .about-band .values { order: 4; }

  /* ---- Timeline preview ---- */
  .timeline__desc { font-size: 14.5px; }
  .timeline__more {
    font-size: 10.5px;
    margin-top: 12px;
    min-height: 44px;
    padding-bottom: 0;
    border-bottom: 0;
    gap: 9px;
  }
  .timeline__more::after {
    content: '';
    position: absolute;
  }
  .timeline__more:active { opacity: .55; }

  /* ---- Menu: sized for the hand ---- */
  .nav-panel ol a { font-size: 25px; }
  .nav-panel__foot a {
    border-color: var(--line-strong);
    color: var(--muted);
  }
  .nav-panel__foot a:active {
    color: var(--ink);
    border-color: var(--ink);
    transform: scale(.94);
  }
}

/* Very small phones. */
@media (max-width: 360px) {
  .mini-card__title,
  .card__title { font-size: 21px; }
  .nav-panel ol a { font-size: 22px; }
}

/* =====================================================================
   MOBILE - PART 5
   Final editorial pass: the opening picture joins the grid, the type
   scale is pulled apart so each size has a job, and the vertical rhythm
   is settled.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- The opening picture joins the grid ----
     It was the one thing on the page running to the screen edges, which
     made it read as a banner rather than as the first piece of work. On
     the same margins as everything else, with the same corners, it
     belongs to the page. */
  .hero__media {
    padding-left: var(--gut);
    padding-right: var(--gut);
  }
  .hero__media .frame { border-radius: var(--radius); }
  .hero__text { padding-top: 28px; }

  /* ---- Type scale ----
     Pulled apart so each size has a distinct job: page title, then
     section name, then work title, then prose. Previously the page title
     was loud enough to swallow the first screen, and the section names
     were too quiet to organise anything. */

  /* Page titles: about a fifth smaller, so the content starts sooner
     without the heading losing its authority. */
  .display--xl {
    font-size: clamp(28px, 8.5vw, 36px);
    line-height: 1.15;
    letter-spacing: -0.018em;
  }

  /* Section names: one treatment for all four on the home page, so
     Projects, Professional Work, About Me and Experience read as
     siblings rather than as four unrelated labels. */
  .split__title,
  .about-band__eyebrow,
  .timeline-section__head .label,
  .page-head__eyebrow {
    font-size: 15px;
    letter-spacing: 0.16em;
    font-weight: 600;
    color: var(--ink);
    line-height: 1.35;
  }
  .split__title { margin-bottom: 10px; }
  .about-band__eyebrow { margin-bottom: 20px; }
  .timeline-section__head .label { margin-bottom: 0; }

  /* Work titles: a step down, so the section name leads. */
  .mini-card__title,
  .card__title { font-size: 21px; line-height: 1.26; }

  /* ---- Reading width ----
     Prose set to the full width of the screen reads as a dense block.
     Holding it a little short of the right margin gives the page air and
     makes the line endings less uniform, which is easier on the eye.

     The ch unit measures the width of a zero, which in this typeface is
     considerably wider than average lowercase, so 29ch is roughly 45
     characters of real prose - comfortably inside the readable range,
     and about 30px short of the right margin. */
  .body-text,
  .about-band__body,
  .page-head__intro,
  .detail-desc,
  .tab-panel__body,
  .about-page__intro,
  .split__intro {
    max-width: 29ch;
    line-height: 1.72;
  }
  .timeline__desc,
  .cv-row__desc,
  .value__text { max-width: 27ch; }

  /* ---- Vertical rhythm ----
     One ladder for every section: air above the name, a short gap to the
     line beneath it, a longer one before the work begins, and a clear
     break between pieces. */
  .split__head { gap: 4px; }
  .split__head .plain-link { margin-top: 16px; }
  .mini-grid { margin-top: 40px; gap: 56px; }
  .mini-card .frame { margin-bottom: 20px; }
  .mini-card__foot { margin-top: 8px; }

  .timeline-section__head { margin-bottom: 36px; }

  /* ---- About: portrait and words as one composition ---- */
  .about-band__media { margin-bottom: 20px; }
  .about-band__heading { font-size: 24px; margin-bottom: 14px; }
  .about-band__copy { margin-bottom: 40px; }
  .about-band__cta { margin-top: 22px; }

  /* ---- Read more: the same link as everywhere else ----
     It was a quieter thing than the other calls to action, which made it
     look like an afterthought rather than the way through to the rest. */
  .timeline__more {
    font-size: var(--label-size);
    letter-spacing: var(--label-track);
    font-weight: 500;
    color: var(--ink);
    gap: 12px;
    margin-top: 14px;
    padding-bottom: 5px;
    border-bottom: 1px solid var(--ink);
    min-height: 0;
  }
  .timeline__more:active { transform: scale(.98); }
  .timeline__item { padding-bottom: 40px !important; }

  /* ---- Page heads ---- */
  .page-head { padding-top: var(--section-y-sm); padding-bottom: 36px; }
  .page-head__eyebrow { margin-bottom: 18px; }
  .page-head__title { max-width: 16ch; margin-bottom: 16px; }

  /* ---- Detail pages ---- */
  .detail-title { font-size: clamp(26px, 7.6vw, 32px); line-height: 1.18; }
  .project-group__title { font-size: 20px; }
}

@media (max-width: 360px) {
  .display--xl { font-size: 26px; }
  .mini-card__title,
  .card__title { font-size: 19px; }
  .split__title,
  .about-band__eyebrow,
  .timeline-section__head .label,
  .page-head__eyebrow { font-size: 13.5px; }
}

/* =====================================================================
   MOBILE - PART 6
   Squared corners, a stronger section voice, taller pictures, and more
   air inside each section.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- Corners ----
     Square. A rounded photograph reads as an interface element; a square
     one reads as a plate in a book. It also matches the desktop, so the
     two now share the same treatment rather than diverging on softness. */
  :root { --radius: 0px; }

  .frame,
  .card__media,
  .hero__media .frame,
  .mini-card .frame,
  .value { border-radius: 0; }

  /* Chips stay pill-shaped: they are controls, not pictures. */
  .chip { border-radius: 999px; }

  /* ---- Section voice ----
     Up another fifth. These four names carry the structure of the page,
     so they should be the first thing read in any section — ahead of the
     work titles beneath them, which are larger but far lighter. */
  .split__title,
  .about-band__eyebrow,
  .timeline-section__head .label,
  .page-head__eyebrow {
    font-size: 18px;
    letter-spacing: 0.13em;
    font-weight: 600;
    line-height: 1.3;
  }

  /* Work titles hold their size. The section name leads on weight and
     letter-spacing rather than on scale. */
  .mini-card__title,
  .card__title { font-size: 21px; }

  /* ---- Pictures ----
     Taller than the four-by-three they were, which on a single column
     read as a wide strip. A little more height gives each building room
     to be a picture rather than a banner. */
  .mini-card .frame,
  .card__media .frame { aspect-ratio: 5 / 4; }

  /* ---- Air ----
     A wider ladder between the parts of a section: name, line beneath
     it, the way through, then the work. Each step is now clearly longer
     than the one before, which is what makes a section feel composed
     rather than stacked. */
  .split__title { margin-bottom: 14px; }
  .split__head { gap: 6px; }
  .split__head .plain-link { margin-top: 24px; }
  .mini-grid { margin-top: 52px; gap: 64px; }
  .mini-card .frame { margin-bottom: 22px; }

  .about-band__eyebrow { margin-bottom: 24px; }
  .timeline-section__head { margin-bottom: 40px; }

  /* Section names on interior pages get the same room. */
  .page-head__eyebrow { margin-bottom: 20px; }
  .project-group__title { font-size: 21px; margin-bottom: 36px; }
}

@media (max-width: 360px) {
  .split__title,
  .about-band__eyebrow,
  .timeline-section__head .label,
  .page-head__eyebrow { font-size: 16px; }
  .mini-card__title,
  .card__title { font-size: 19px; }
}

/* =====================================================================
   MOBILE - PART 7
   The last pass: a wider opening picture, a stronger section voice,
   quieter headlines, and more air between chapters.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- Rhythm ----
     More air between chapters. On a phone the sections arrive one after
     another with nothing beside them, so the gap between them is the
     only thing doing the work a margin would do in print. */
  :root { --section-y: 92px; }

  /* ---- Section voice ----
     Up again, to 21px. These five names carry the structure of the site
     and should be the first thing read anywhere they appear. */
  .split__title,
  .about-band__eyebrow,
  .timeline-section__head .label,
  .page-head__eyebrow {
    font-size: 21px;
    letter-spacing: 0.12em;
    font-weight: 600;
    line-height: 1.28;
  }
  .split__title { margin-bottom: 16px; }
  .about-band__eyebrow { margin-bottom: 26px; }
  .page-head__eyebrow { margin-bottom: 22px; }
  .timeline-section__head { margin-bottom: 44px; }

  /* Small capitalised labels get more tracking. At these sizes letters
     need air between them to read as considered rather than cramped. */
  .label,
  .value__title,
  .timeline__period,
  .cv-row__period,
  .meta-row dt,
  .drawing__caption,
  .detail-num,
  .site-footer__label { letter-spacing: 0.2em; }

  /* ---- Headlines ----
     A step quieter. They sat close enough in weight to the photographs
     to compete with them; a little smaller and they introduce the work
     instead. */
  .display--xl { font-size: clamp(26px, 8vw, 34px); }
  .hero__title { font-size: clamp(26px, 7.9vw, 33px); }
  .detail-title { font-size: clamp(25px, 7.2vw, 30px); }

  /* ---- Work titles ----
     Held at 21px but given a little more weight, so a title reads as a
     name rather than as a caption, and more room above it. */
  .mini-card__title,
  .card__title { font-weight: 400; letter-spacing: -0.01em; }
  .mini-card .frame,
  .card__media { margin-bottom: 26px; }
  .card__body { padding-top: 0; }

  /* ---- About ----
     The portrait is given the room of a plate: taller, with clear air
     above and below, so it reads as part of the work rather than as an
     illustration dropped beside the text. */
  .about-band__media {
    margin-top: 4px;
    margin-bottom: 30px;
  }
  .about-band__media .frame { aspect-ratio: 4 / 5; }

  /* Value cards, lighter: a paler rule, more room inside, smaller marks.
     They are a footnote to the portrait, not a row of buttons. */
  .value {
    border-color: var(--line);
    padding: 26px 24px;
    gap: 18px;
    grid-template-columns: 22px 1fr;
  }
  .value__icon svg { width: 20px; height: 20px; }
  .value__title { font-size: 10.5px; margin-bottom: 9px; }
  .value__text { font-size: 14px; line-height: 1.65; }
  .values { gap: 14px; }

  /* ---- Experience ----
     The role is what people scan for, so it is set larger than the
     company and description around it. */
  .timeline__role { font-size: 19px; font-weight: 400; margin-bottom: 5px; }
  .timeline__company { font-size: 14.5px; margin-bottom: 11px; }
  .timeline__item { padding-bottom: 44px !important; }

  /* ---- Interior pages ---- */
  .project-group__title { font-size: 22px; margin-bottom: 38px; }
  .page-head { padding-bottom: 40px; }
}

@media (max-width: 360px) {
  .split__title,
  .about-band__eyebrow,
  .timeline-section__head .label,
  .page-head__eyebrow { font-size: 18px; }
  .display--xl,
  .hero__title { font-size: 25px; }
  .value { padding: 22px 20px; }
}

/* =====================================================================
   MOBILE - PART 8
   The landing screen.

   The first thing anyone sees should be one composition, not the top of
   a list. The hero is therefore given the whole screen: the picture, the
   line above the headline, the headline itself and the way in, with the
   next section beginning only once you scroll.

   The picture is a wide plate rather than a square. A square crop of a
   landscape view throws away half its width and turns a valley into a
   portrait; sixteen by ten keeps the sweep and leaves the type room to
   sit beneath it as part of the same picture rather than as a caption
   pushed off the bottom.
   ===================================================================== */

@media (max-width: 768px) {

  /* svh is the height with the browser's own bars showing - the smallest
     the screen ever is. Sizing to that means the composition is whole on
     first load, when those bars are always visible, rather than being
     cut off and only resolving after a scroll. vh first for anything too
     old to know the newer unit. */
  /* The header is 73px on a phone. Subtracting it is what makes the hero
     end exactly on the fold rather than a little past it, so the section
     below starts clean instead of showing a sliver of itself. */
  .hero {
    --header-h: 73px;

    /* The mount the picture sits in: clamp(20px, 4svh, 40px), written out
       above the plate and again below it. The two must stay identical -
       the plate had ended up with 6px above it and 41px below, which is
       what made it look pinned to the header rather than placed.

       It is written out rather than held in a custom property because the
       value carries svh. A browser without svh drops a whole declaration
       and falls back to the one before it, but it will happily store the
       same value in a variable and only fail where the variable is used -
       and that failure zeroes the padding instead of falling back.

       It scales with the screen so the mount stays in proportion to the
       picture rather than thinning to a hairline on a large phone. */
    min-height: calc(100vh - var(--header-h));
    min-height: calc(100svh - var(--header-h) - env(safe-area-inset-top));
    padding-bottom: 0;
    display: flex;
  }
  .hero__inner {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  /* Stretch, not start. As a column flex item the plate is sized across by
     align-self, and left to shrink to fit it takes its width from whatever
     height the ceiling below allows - so trimming the height would narrow
     the picture too and walk it off the 24px grid the text stands on. */
  .hero__media {
    flex: 0 0 auto;
    align-self: stretch;

    /* No border around the picture. This element carries #ddd9d2, darker
       than the page, and any padding on it shows as a frame - which is
       what the visible band was. On a phone the picture sits straight on
       the page instead, so the tone is dropped here. The frame inside
       keeps its own paler tone, so there is still something to look at
       while the photograph loads.

       Without a border it is the spacing that has to hold the balance:
       this measure is repeated as .hero__text's padding-top, so the gap
       above the picture and the gap below it are one and the same. The
       sides stay on the gutter, the same edge the type stands on. */
    background: none;
    padding: 28px var(--gut) 0;
    padding: clamp(20px, 4svh, 40px) var(--gut) 0;
  }

  /* Deep enough to stand in the picture rather than look through a
     letterbox, and the ceiling is what stops it going further. A fixed
     ratio on a short phone would swallow the screen and push the heading
     below the fold, so the plate is held to a share of the height.

     That share is measured against svh - the height with the browser's own
     bars showing - because on a phone those bars are always there on first
     load. It is the binding constraint in Safari far more often than the
     ratio is, so it is the number that decides how deep the picture
     actually looks in the hand.

     The two together settle the proportion by themselves: a tall phone has
     the room and holds the full six by five, a short one is trimmed back
     towards four by three. Nothing is stretched - the picture is set to
     cover, so a trimmed plate loses a band of sky and a band of paving,
     which are the parts worth losing. */
  .hero__media .frame {
    max-height: 42vh;
    max-height: 42svh;
  }

  /* The bridge sits a third of the way down the crop. Holding the frame
     there means it stays the subject at every height rather than drifting
     out of the top as the plate is trimmed. */
  .hero__media .frame img { object-position: 50% 38%; }

  /* The type sits directly beneath the plate and stays with it. Whatever
     height is left over falls below the whole group, where it reads as
     the foot of a page rather than as a gap in the middle of one. */
  .hero__text {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    /* The top value must match .hero__media's padding-top. With no border
       to read the balance from, these two gaps are the balance. */
    padding: 28px var(--gut) 32px;
    padding: clamp(20px, 4svh, 40px) var(--gut) clamp(16px, 4.2svh, 42px);
  }

  /* A full screen given to one picture leaves ground beneath the type.
     Left bare it reads as a mistake; a thin rule at the foot of it, fading
     downward, claims the space and says the page continues. It takes the
     leftover height as its own top margin, so it always sits on the fold
     however tall the phone is. */
  .hero__inner::after {
    content: '';
    flex: 0 0 auto;
    width: 1px;
    height: 46px;
    margin: auto auto 30px;
    background: linear-gradient(to bottom, transparent, var(--ink, #1a1a1a));
    opacity: 0.28;
  }
  /* One rhythm above and below the headline.

     Equal margins do not give equal gaps here. A margin is measured from
     the edge of a line box, and the ink sits at a different depth inside
     each of these three: the label is small and tightly led, the headline
     carries a descender below its baseline and half its leading above the
     caps, and the link is small again. Setting both to the same number
     left the space under the headline 22-28px larger than the space over
     it - which is exactly what could be seen.

     Measured against the glyphs rather than the boxes, the correction is a
     constant and not a ratio: the label needs about 8.5px more margin than
     the link for the two gaps to read the same. So both are drawn from one
     value, 4.4svh, one half-offset either side of it. The total is what it
     was, so the composition keeps its height and only the balance changes.
     The bounds are offset by the same 8.5px, so the pair stays matched
     even where the clamps take over. */
  .hero__eyebrow {
    margin-bottom: 24px;
    margin-bottom: clamp(24px, calc(4.4svh + 4.25px), 46px);
    letter-spacing: 0.2em;
  }
  /* Two lines, not three. The headline was breaking after "Designing"
     because the measure was capped at 15ch - about 306px on a 390 screen,
     while the column it sits in is 342px. "Designing experiences" needs
     308px: it was losing by two pixels to a limit, not to the type size.

     So the cap goes and the headline takes the column, which is the same
     edge the picture and the label already stand on. Nothing else changes -
     the size, the leading and the tracking are all as they were, and the
     break falls where it should on its own rather than being forced. On a
     narrower phone than any sold today it simply wraps to three again,
     which is the right way to fail. */
  .hero__title {
    font-size: clamp(27px, 8.2vw, 34px);
    line-height: 1.22;
    letter-spacing: -0.016em;
    max-width: none;
  }
  /* The other half of that pair - see the note above the label. */
  .hero__cta {
    margin-top: 15.5px;
    margin-top: clamp(15.5px, calc(4.4svh - 4.25px), 37.5px);
  }

  /* The spacing above scales with the screen, so a short phone tightens on
     its own and no longer needs a breakpoint to rescue it. What it does
     still need is the rule to step aside: there is no ground left under
     the type for it to claim. */
  @media (max-height: 700px) {
    .hero__inner::after { display: none; }
  }

  /* Below this the headline is three fixed lines against very little
     height, and a share of the screen is no longer a fair measure - the
     picture would push the way in onto the fold itself. It gives up a
     little so the type keeps its footing. */
  @media (max-height: 620px) {
    .hero__media .frame {
      max-height: 36vh;
      max-height: 36svh;
    }
  }

  /* The next section starts with a full measure of air above it, so the
     break between the landing screen and the work is unmistakable. */
  .split__band--projects .split__block { padding-top: var(--section-y); }
}

/* =====================================================================
   PROJECT HERO CAROUSEL

   The opening picture on a project page, swiped through.

   It is a scroll container that snaps, not a slider driven by script.
   That matters: the momentum, the resistance at either end and the way a
   vertical scroll passes straight through to the page are the browser's
   own, and no amount of touch handling reproduces them faithfully. It
   also means the thing still works with the script disabled - it just
   scrolls without keeping count.
   ===================================================================== */

.carousel { position: relative; }

.carousel__track {
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  /* A flick should advance by one picture, not skate past four. */
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.carousel__track::-webkit-scrollbar { display: none; }
.carousel__track:focus { outline: none; }
.carousel__track:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.carousel__slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

/* The picture keeps whatever shape .detail-hero .frame gives it, so the
   opening picture is exactly the size it was before and every slide is
   the same. Nothing is stretched - the frames crop as they always have. */

/* Bottom left, because the signature sits bottom right and the two should
   not argue. Same size and tracking as the counter in the lightbox. */
.carousel__count {
  position: absolute;
  left: 18px;
  bottom: 16px;
  font-size: 11px;
  letter-spacing: .14em;
  color: rgba(243, 242, 239, .92);
  text-shadow: 0 1px 8px rgba(0, 0, 0, .55);
  pointer-events: none;
}

/* The arrows are a hint, not a control. On a phone the thumb does the
   work and these only say that it can be done, so they are drawn as
   quietly as possible: the same thin stroke as the arrow on "Back to
   projects", in the paper tone, with no circle, no plate and no fill of
   any kind. A soft shadow is the only thing behind them, because they
   have to stay legible over a bright sky as well as a dark facade.

   The target around them is larger than the mark, so a thumb can find
   them, but the padding is transparent - nothing is drawn there. */
.carousel__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  /* The padding is the target, not the mark. Nothing is drawn in it, but
     it gives a thumb something to land on. */
  padding: 16px;
  border: 0;
  background: none;
  color: #ffffff;
  cursor: pointer;
  opacity: .36;
  filter: drop-shadow(0 1px 5px rgba(0, 0, 0, .55));
  transition: opacity var(--t-med) var(--ease), transform var(--t-med) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.carousel__arrow--prev { left: 10px; }
.carousel__arrow--next { right: 10px; }
.carousel__arrow svg { width: 24px; height: 12px; display: block; }

/* The right arrow is the left one turned round, so there is exactly one
   glyph on the site and the two cannot drift apart. */
.carousel__arrow--next svg { transform: scaleX(-1); }

.carousel__arrow:focus-visible { opacity: 1; outline: none; }

/* They are always faintly there. Hiding them until the pointer arrives
   would make them useless as a hint - the whole point is to say, before
   anyone has touched anything, that this picture moves. They only firm up
   when the pointer is over the picture, and step aside a little when it
   reaches them. */
@media (hover: hover) and (pointer: fine) {
  .carousel:hover .carousel__arrow { opacity: .7; }
  .carousel__arrow:hover { opacity: 1; }
  .carousel__arrow--prev:hover { transform: translate(-3px, -50%); }
  .carousel__arrow--next:hover { transform: translate(3px, -50%); }
}

@media (max-width: 768px) {
  /* The hero runs edge to edge on a phone, so the counter comes in to the
     same gutter the type stands on. */
  .carousel__count { left: var(--gut); bottom: 14px; }
}

/* A picture that leads somewhere. It behaves like the project cards do - a
   slow, slight push in on hover - so that it reads as something to click
   without needing to be told. Used by the opening picture and by the
   portrait on the home page. */
.picture-link { display: block; }
.picture-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

@media (hover: hover) and (pointer: fine) {
  .picture-link:hover .frame img { transform: scale(var(--zoom)); }
}

/* The hero carousel sits inside the page gutter rather than running edge
   to edge, so its counter comes in from the picture's own corner instead
   of the page's. */
.carousel--hero .carousel__count { left: 14px; bottom: 14px; }

@media (max-width: 768px) {
  .carousel--hero .carousel__count { left: 12px; bottom: 12px; }
}

/* On a wide screen the hero picture is not sized by its own proportions:
   the grid row gives .hero__media its height and the frame fills it
   absolutely. Putting a carousel in between broke that chain - the track
   and its slides had no height of their own, so the frame resolved
   against nothing and came out 801x0.

   Each link in the chain is told to fill its parent, and the slide is made
   the frame's containing block so the picture fills the slide rather than
   the whole carousel. On a phone none of this binds: there .hero__media is
   auto height and the frame carries its own ratio, so 100% of auto is
   simply auto and the picture sizes itself as before. */
.carousel--hero,
.carousel--hero .carousel__track { height: 100%; }
.carousel--hero .carousel__slide { position: relative; height: 100%; }
.carousel--hero .hero__media-link { height: 100%; }

/* =====================================================================
   MOBILE - PART 9
   The work seen two at a time, and the reading pages given their width.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- Listings, two to a row ----
     One project to a screen showed the work but not the body of it: you
     had to scroll to learn there was more. Two to a row lets someone take
     in the range first and choose second, which is the order people
     actually look in.

     The pictures do the work, so the cards keep only what a glance needs -
     the picture, the name, and what kind of thing it is. The row of
     arrows and numbers over the top belonged to a full width card and
     would be clutter at this size. */
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
    /* Wide down the page, narrow across it: the gap between two pictures
       side by side should read as a seam, and the gap between rows as a
       break. Equal gaps make a mesh, which is the Pinterest look. */
    gap: 40px 14px;
    padding-bottom: var(--section-y);
  }

  /* A little upright. A square reads as a thumbnail; taller than it is
     wide reads as a plate, and it is what architecture photographs are
     usually cropped to in print. */
  .card__media .frame { aspect-ratio: 4 / 5; }

  .card__body { padding: 12px 0 0; gap: 4px; }

  /* Two to a row halves the space, so the name comes down to suit it. Two
     lines at most, so that a long title cannot push the row below it out
     of step with its neighbour. */
  .card__title {
    font-size: 15.5px;
    line-height: 1.3;
    letter-spacing: -0.005em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* The category set as a label rather than as prose, which is how every
     other small line on the site is set. */
  .card__meta {
    font-size: 10px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--muted);
    opacity: 1;
    line-height: 1.5;
  }
  .card__foot { min-height: 0; margin-top: 0; }

  /* Every practice card carries the same firm, so at this width it is
     repetition taking room from the role, which is what tells one card
     from the next. The full line is still there on the page itself. */
  .card__firm,
  .card__sep { display: none; }

  /* Both belong to the wide card: an arrow that says "this row is a link"
     when the row is the width of the page, and a number plate that has
     room to sit over a large picture without crowding it. */
  .card__arrow { display: none; }
  .card__num { display: none; }

  /* ---- The reading pages ----
     The paragraphs were held to 27 and 29 characters. That was set when
     the concern was lines running too long, but against a 342px column it
     left a third of the width empty and broke the text into a narrow
     ribbon down the left.

     The column is the measure now. Between the two 24px gutters it comes
     to about 45 characters at this size, which is inside what is
     comfortable to read and uses the page as a page. */
  .page-head__intro,
  .about-page__intro,
  .cv-row__desc,
  .timeline__desc,
  .value__text { max-width: none; }

  /* Breaks lines by looking at the paragraph rather than one line at a
     time, so the right edge is less ragged and no paragraph ends on a
     single word. Ignored where it is not supported, which costs nothing. */
  .about-page__intro,
  .cv-row__desc,
  .timeline__desc,
  .body-text p { text-wrap: pretty; }

  /* ---- The CV, read down ----
     Four things in a row - when, what, where, and what it involved - and
     they were spaced almost alike, so the eye had to read them to tell
     them apart. Now the gaps say it: a clear step from the date to the
     job, the company held close under its title, and a wider gap before
     the description, which is the part you skip when you are scanning.
     Each entry is given more room to breathe than the lines within it. */
  .cv-row { gap: 12px; padding: 26px 0; }
  .cv-row__period  { padding-top: 0; }
  .cv-row__role    { margin-bottom: 4px; line-height: 1.3; }
  .cv-row__company { margin-bottom: 12px; }
  .cv-row__desc    { line-height: 1.68; }
}

/* Two columns on the narrowest phones would leave each picture about
   130px across, which is a thumbnail rather than a plate. Below this the
   list goes back to one, where there is at least enough width to see a
   building in it. */
@media (max-width: 359px) {
  .card-grid { grid-template-columns: 1fr; gap: 34px; }
  .card__media .frame { aspect-ratio: 5 / 4; }
  .card__title { font-size: 19px; -webkit-line-clamp: 3; }
}

/* =====================================================================
   MOBILE - PART 10
   Letting the second row show, and the pictures arriving quietly.
   ===================================================================== */

@media (max-width: 768px) {

  /* ---- The grid should look like a grid on arrival ----
     With one row filling the screen the page reads as though it holds two
     projects. The row below has to break the fold - not by much, forty
     pixels of the next pictures is enough - so that the work is obviously
     a body of work and scrolling is the obvious thing to do.

     Nothing is added to say so. The height is taken back in small amounts
     from the places that were merely generous: the air above the page
     title, the gap under the filters, the gap between the section name and
     the grid, and the gap between rows. Only the listings are touched, so
     the reading pages keep the openings they were given. */
  body[data-page="projects"] .page-head,
  body[data-page="practice"] .page-head {
    padding-top: 40px;
    padding-bottom: 26px;
  }
  body[data-page="projects"] .filter-bar { padding-bottom: 24px; }
  .project-group__title { margin-bottom: 20px; }
  .card-grid { gap: 28px 14px; }

  /* Seven by eight rather than four by five. Still upright, still a plate
     rather than a thumbnail, and eighteen pixels shorter - which is the
     last of what was needed. The crop is unchanged, so nothing is squeezed
     and no detail is lost; the frame simply asks for a little less. */
  .card__media .frame { aspect-ratio: 7 / 8; }
  .card__body { padding-top: 10px; }
}

/* =====================================================================
   PICTURES ARRIVING

   A frame already carries a warm grey, so nothing is ever blank white
   while a picture loads - the composition of the page is there before
   the photographs are. What was missing was the arrival itself: a
   picture appeared in one frame, which on a slow connection reads as a
   series of small jolts down the page.

   It fades up instead, over a quarter of a second. The starting state is
   set by script rather than in the stylesheet on purpose: if it were set
   here, a picture already in the browser's cache - or any picture at all
   should the script fail to run - would sit at zero opacity with nothing
   to bring it back. Written this way the resting state of a picture is
   always visible, and the fade is only ever added to one that is known
   to be still coming.
   ===================================================================== */
.frame img.is-arriving {
  opacity: 0;
}
.frame img.is-here {
  opacity: 1;
  transition: opacity .28s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  .frame img.is-arriving { opacity: 1; }
  .frame img.is-here { transition: none; }
}

/* =====================================================================
   HOVER IS FOR A POINTER

   :hover is not a state a touch screen really has. Tapping a card leaves
   it hovering until something else is tapped, so the picture stayed
   zoomed and the arrow stayed pushed across long after the finger had
   gone - a hover effect frozen on, which reads as a rendering fault
   rather than as a response.

   The card's own lift was already turned off on a phone; this turns off
   the rest of it. The press feedback is untouched, because :active is a
   state a touch screen genuinely has and ends when the finger lifts.
   ===================================================================== */
@media (hover: none), (pointer: coarse) {
  .card:hover .frame img,
  .mini-card:hover .frame img {
    transform: none;
    filter: none;
  }
  .card:hover .card__arrow,
  .mini-card:hover .mini-card__arrow {
    transform: none;
    color: var(--muted);
  }
  .mini-card:hover .mini-card__title { transform: none; }
}

/* =====================================================================
   MOBILE - PART 11
   The reading column, widened for the last time.
   ===================================================================== */

@media (max-width: 768px) {

  /* Two things were holding the lines short, and only one of them had been
     dealt with. The caps on the About and CV text were lifted; the same
     caps on the project pages and the home page intros were left, so a
     project description still ran at 29 characters inside a column that
     could hold 45. That is where the empty band down the right came from.

     They all go. The column is the measure everywhere now, which is what
     it already was on About and CV, so every long passage on the site
     reads to the same width instead of three different ones. */
  .page-head__intro,
  .detail-desc,
  .tab-panel__body,
  .about-page__intro,
  .split__intro,
  .timeline__desc,
  .cv-row__desc,
  .value__text,
  .body-text,
  .body-text p { max-width: none; }

  /* And the gutter comes in, asked for three times and worth about eight
     pixels a line. Everything on the page is set from this one value -
     the header, the pictures, the grid, the type - so the whole page
     narrows together and nothing steps out of line with anything else. */
  :root { --gut: 20px; }
}

@media (max-width: 360px) {
  :root { --gut: 16px; }
}

/* =====================================================================
   THE WAITING SCREEN

   Shown only when a page is slow to arrive - the script holds it back
   until the fade has already finished, so an ordinary click never sees
   it. The page's own paper, the monogram from the footer, and nothing
   else: a spinner or a bar would announce a delay rather than cover one.

   It sits above the fading page rather than inside it, so it is not
   dragged down to zero by the body's own fade on the way out.
   ===================================================================== */
.leaving-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg, #f3f2ef);
  opacity: 0;
  transition: opacity .16s var(--ease);
  pointer-events: none;
}
.leaving-screen.is-up { opacity: 1; }

.leaving-screen__mark {
  font-size: 34px;
  font-weight: 300;
  letter-spacing: .22em;
  /* The tracking pushes the last letter off centre by half its own space,
     so the whole mark is nudged back by the same amount. */
  text-indent: .22em;
  color: var(--ink);
  line-height: 1;
}

/* The name sits at the foot of the screen the way it does at the foot of
   the page, rather than under the mark - the monogram is meant to be
   alone in the middle. */
.leaving-screen__name {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(56px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
}
.leaving-screen__name span {
  font-size: 12px;
  letter-spacing: .26em;
  color: var(--ink);
}
.leaving-screen__name em {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: .3em;
  color: var(--muted);
}

/* The body fade and this screen would be two things saying the same thing
   at once, and the screen is the better of the two. */
body.is-leaving .leaving-screen { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .leaving-screen { display: none; }
}

/* Drawing groups. The heading is set like the small labels elsewhere so
   the set reads as a drawing set rather than as new sections of the page. */
.drawings-group + .drawings-group { margin-top: clamp(30px, 4vw, 48px); }
.drawings-group__title {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 18px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

@media (max-width: 768px) {
  .drawings-group + .drawings-group { margin-top: 34px; }
  .drawings-group__title { margin-bottom: 16px; }
}

/* =====================================================================
   THE BOOT SCREEN

   Every page here is empty HTML that the script fills in. Between the
   markup arriving and that happening there is a moment with nothing on
   the page at all - the beige of the body and no content - and on a slow
   connection that moment is long enough to look broken. The leaving
   screen never covered it, because that one belongs to the page being
   left behind; this is the page being arrived at.

   So it is written into the markup rather than created by script. It is
   the first thing painted, before a single line of JavaScript has run,
   which is the only way to cover a gap that exists because JavaScript has
   not run yet.

   The failsafe matters more than the animation. An overlay that covers
   the whole site and waits to be taken down by script is a white screen
   of death waiting to happen - one error in the renderer and the site is
   gone. So it also takes itself down after four seconds, with no script
   involved. The script gets there long before that in every normal case;
   the animation is only there for the case where it never gets there at
   all.
   ===================================================================== */
.boot-screen {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg, #f3f2ef);
  animation: boot-give-up .6s var(--ease) 4s forwards;
}

@keyframes boot-give-up {
  to { opacity: 0; visibility: hidden; }
}

.boot-screen__mark {
  font-size: 34px;
  font-weight: 300;
  letter-spacing: .22em;
  text-indent: .22em;
  color: var(--ink, #1b1b19);
  line-height: 1;
  /* A page that is already in the cache is ready almost at once, and a
     mark that flashed on and off would be worse than the gap it covers.
     It fades in over its first fifth of a second instead, so a fast load
     shows a suggestion of it and a slow one shows it properly. */
  opacity: 0;
  animation: boot-mark-in .22s var(--ease) forwards;
}

@keyframes boot-mark-in {
  to { opacity: 1; }
}

.boot-screen__name {
  position: absolute;
  left: 0; right: 0;
  bottom: calc(56px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  opacity: 0;
  animation: boot-mark-in .22s var(--ease) forwards;
}
.boot-screen__name span {
  font-size: 12px;
  letter-spacing: .26em;
  color: var(--ink, #1b1b19);
}
.boot-screen__name em {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: .3em;
  color: var(--muted, #74726c);
}

/* Taken down the moment the page is actually built. */
.boot-screen.is-done {
  animation: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s var(--ease), visibility 0s .3s;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .boot-screen__mark,
  .boot-screen__name { opacity: 1; animation: none; }
  .boot-screen.is-done { transition: none; }
}
