:root {
  --photo-gap: 4px;   /* spacing between photos, same in the row and every grid */
  --paper: #fbfbf9;
  --blue: #1f3ba6;
  --blue-mute: color-mix(in srgb, var(--blue) 58%, var(--paper));
  --blue-rule: color-mix(in srgb, var(--blue) 24%, var(--paper));
  --serif: "Instrument Serif", "Times New Roman", serif;
  --sans: "Archivo", system-ui, sans-serif;
  --gutter: clamp(16px, 3vw, 44px);
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--blue);
  font: 400 16px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
::selection { background: var(--blue); color: var(--paper); }
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
h1, h2, h3 { font-family: var(--serif); font-weight: 400; margin: 0; letter-spacing: -0.015em; }
p { margin: 0; }

/* small wide uppercase labels, like the Simplicité references */
.lab, .top, .chip, .pill, .btn {
  font-family: var(--sans);
  font-stretch: 125%;
  font-weight: 600;
  font-size: 11.5px;
  line-height: 1.3;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.lab.mute { color: var(--blue-mute); }

.wrap { padding-inline: var(--gutter); }

/* ---------- top bar ---------- */
.top {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(8px);
  padding: 0 var(--gutter);
}
.top-in {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px;
  border-top: 1.5px solid var(--blue);
  margin-top: 14px; padding: 16px 0;
}
.top nav { display: flex; gap: clamp(16px, 3.6vw, 52px); }
.top .right { justify-self: end; }
.top a:hover { opacity: .6; }
@media (max-width: 720px) {
  .top-in { grid-template-columns: 1fr auto; }
  .top .right { display: none; }
  .top nav .hide-sm { display: none; }
  .top { font-stretch: 100%; letter-spacing: .06em; }
  .top nav { gap: 14px; }
  .top a { white-space: nowrap; }
}

/* ---------- hero: 3D gallery ---------- */
.hero {
  position: relative;
  height: calc(100svh - 64px);
  min-height: 520px;
  overflow: hidden;
}
/* photos fly in front of the title and cover it as they pass; fades in once the first batch has loaded */
.hero canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; z-index: 2; opacity: 0; transition: opacity .9s ease; }
.hero canvas.on { opacity: 1; }
.hero-title {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  text-align: center; pointer-events: none;
  /* sits in the upper part of the hero; photos mostly gather around the centre */
  padding: clamp(28px, 8vh, 96px) var(--gutter) 0;
}
.hero-title h1 {
  font-size: clamp(72px, 15.5vw, 280px);
  line-height: .8;
  letter-spacing: -0.03em;
}
.hero-title .lab { margin-top: 22px; }
.hero-title .lab + .lab { margin-top: 6px; }
.hero-foot {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: 18px; z-index: 3;
  display: flex; justify-content: space-between; gap: 16px;
}
.hero-foot a { text-align: right; }   /* wraps on phones; keep both lines on the right margin */
.hero-foot a:hover { opacity: .6; }
/* shown only if WebGL is unavailable */
.hero-fallback { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 8px; opacity: .35; }
.hero-fallback img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- sections ---------- */
section.block { padding-block: clamp(80px, 11vw, 160px) 0; }
.head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 24px; flex-wrap: wrap;
  border-top: 1.5px solid var(--blue);
  padding-top: clamp(28px, 3.4vw, 52px); margin-bottom: clamp(28px, 4vw, 56px);
}
.head h2 { font-size: clamp(60px, 10vw, 168px); line-height: .82; }
.head p { color: var(--blue-mute); max-width: 38ch; font-size: 15px; }
/* beside the title on wide screens, so end flush with the right margin; below it on phones, so stay left */
@media (min-width: 761px) { .head p { text-align: right; } }

/* ---------- archive ---------- */
.filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 22px; }
.chip { padding: 9px 14px 8px; border: 1.5px solid var(--blue); background: none; color: var(--blue); cursor: pointer; }
.chip:hover { background: var(--blue-rule); }
.chip[aria-pressed="true"] { background: var(--blue); color: var(--paper); }
.chip .n { opacity: .55; margin-left: 5px; }

.list { list-style: none; margin: 0; padding: 0; border-top: 1.5px solid var(--blue); }
.list li { border-bottom: 1px solid var(--blue-rule); }
.list li[hidden] { display: none; }
.row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 16px; padding: clamp(14px, 1.6vw, 22px) 4px; }
.row .t { font: 400 clamp(30px, 3.6vw, 56px)/1 var(--serif); letter-spacing: -0.01em; transition: transform .3s; }
.row:hover .t, .row:focus-visible .t { transform: translateX(12px); }
.row .tags { display: flex; gap: 6px; }
.pill { padding: 7px 11px 6px; border: 1.5px solid var(--blue); white-space: nowrap; font-size: 10.5px; }
.row:hover .pill { background: var(--blue); color: var(--paper); }
.row .thumb { display: none; }
@media (hover: none), (max-width: 760px) {
  .row { grid-template-columns: 84px 1fr; }
  /* fill exactly the height of the title + tags beside it; contain:size stops the image's own ratio from setting the row height */
  .row .thumb { display: block; width: 84px; height: 100%; align-self: stretch; object-fit: cover; contain: size; grid-row: span 2; }
  .row .tags { grid-column: 2; }
  .row .tags .pill:first-child { display: none; }
}

/* cursor preview */
.preview {
  position: fixed; left: 0; top: 0; z-index: 60;
  width: clamp(260px, 26vw, 420px); aspect-ratio: 16/9;
  pointer-events: none; overflow: hidden;
  background: var(--blue) center/cover no-repeat;
  opacity: 0; scale: .9;
  transition: opacity .2s, scale .25s;
}
.preview.on { opacity: 1; scale: 1; }
.preview .fc { position: absolute; left: 8px; bottom: 6px; color: #fff; font-size: 11px; letter-spacing: .05em; font-variant-numeric: tabular-nums; text-shadow: 0 1px 2px #0008; }
@media (hover: none) { .preview { display: none; } }

/* ---------- photographs ---------- */
.strip { margin-inline: calc(-1 * var(--gutter)); overflow: hidden; }
.strip-track { display: flex; gap: var(--photo-gap); width: max-content; animation: drift 140s linear infinite; }
/* hold still on hover, keyboard focus, and while a photo is open full size */
.strip:hover .strip-track, .strip:focus-within .strip-track, body:has(dialog.lb[open]) .strip-track { animation-play-state: paused; }
.strip button { all: unset; cursor: zoom-in; display: block; flex: none; }
.strip button:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }
.strip img { height: clamp(280px, 38vw, 540px); width: auto; transition: opacity .2s; }
.strip:hover img { opacity: .55; }
.strip:hover button:hover img { opacity: 1; }
/* track holds two copies; shift by one copy plus half a gap */
@keyframes drift { to { transform: translateX(calc(-50% - var(--photo-gap) / 2)); } }
@media (prefers-reduced-motion: reduce) { .strip { overflow-x: auto; } }
.board-foot { margin-top: clamp(28px, 4vw, 56px); display: flex; justify-content: flex-end; }
.btn { display: inline-flex; align-items: center; gap: 10px; background: var(--blue); color: var(--paper); padding: 15px 22px 14px; }
.btn:hover { background: color-mix(in srgb, var(--blue) 82%, #000); }
@media (max-width: 760px) {
  .board-foot .btn { width: 100%; justify-content: center; }
}

/* ---------- about ---------- */
.about { display: grid; grid-template-columns: 4fr 8fr; gap: var(--gutter); align-items: start; }
.portrait { position: sticky; top: 90px; margin: 0; }
.portrait figcaption { color: var(--blue-mute); padding-top: 10px; }
.bio { font: 400 clamp(28px, 3vw, 46px)/1.1 var(--serif); max-width: 26ch; margin-bottom: clamp(32px, 5vw, 72px); }
.cv { display: grid; grid-template-columns: 10rem 1fr; border-top: 1.5px solid var(--blue); margin: 0; }
.cv dt, .cv dd { margin: 0; padding: 16px 0; border-bottom: 1px solid var(--blue-rule); }
.cv dt { padding-top: 19px; }
.cv dd p + p { margin-top: 10px; }
.cv dd small { display: block; color: var(--blue-mute); font-size: 14px; }
.cv strong { font-weight: 600; }
.inline-list { display: flex; flex-wrap: wrap; gap: 6px 22px; }
@media (max-width: 860px) {
  .about { grid-template-columns: 1fr; }
  .portrait { position: static; max-width: 340px; }
  .cv { grid-template-columns: 1fr; }
  .cv dt { border-bottom: 0; padding-bottom: 0; }
}

/* ---------- footer: the one solid blue band, same on every page ---------- */
/* arrows: one drawn shape, rotated per direction, so they match the label type */
.arr { width: 1.15em; height: 1.15em; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: square; vertical-align: -0.24em; margin-left: .5em; }
.arr.ne { transform: rotate(-45deg); }
.arr.up { transform: rotate(-90deg); }
.arr.down { transform: rotate(90deg); }
.arr.left { transform: rotate(180deg); margin: 0 .5em 0 0; }
.btn .arr { margin-left: 0; }   /* the button already spaces its children */
/* line icons drawn in the text colour */
.ico { width: 1.25em; height: 1.25em; fill: none; stroke: currentColor; stroke-width: 1.6; vertical-align: -0.28em; margin-right: .5em; }
.ico .dot { fill: currentColor; stroke: none; }

.contact {
  background: var(--blue); color: var(--paper);
  margin-top: clamp(80px, 11vw, 160px);
  padding-block: clamp(56px, 8vw, 112px) 28px;
}
.contact ::selection { background: var(--paper); color: var(--blue); }
.contact :focus-visible { outline-color: var(--paper); }
/* big "Say hi" on the left, contacts as a ruled list on the right, bottoms aligned */
.contact-main { display: grid; grid-template-columns: 1fr minmax(0, 600px); gap: 40px var(--gutter); align-items: end; }
.contact h2 { font-size: clamp(96px, 15vw, 260px); line-height: .78; letter-spacing: -0.035em; }
.contact-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid color-mix(in srgb, var(--paper) 40%, transparent); }
.contact-list a {
  display: grid; grid-template-columns: 16.5rem 1fr auto; align-items: baseline; gap: 6px 16px;
  padding: 16px 0; border-bottom: 1px solid color-mix(in srgb, var(--paper) 40%, transparent);
  transition: opacity .2s;
}
.contact-list a:hover { opacity: .7; }
.contact-list .lab { opacity: .7; }
.contact-list .v { font-size: 17px; overflow-wrap: anywhere; }
.contact-list .arr { margin-left: 0; }
.contact-foot { display: flex; justify-content: flex-end; margin-top: clamp(48px, 7vw, 96px); }
.contact-foot a:hover { opacity: .7; }
@media (max-width: 860px) { .contact-main { grid-template-columns: 1fr; } }
@media (max-width: 560px) {
  .contact-list a { grid-template-columns: 1fr auto; }
  .contact-list .lab { grid-column: 1 / -1; }
}

/* ---------- lightbox ---------- */
dialog.lb { border: 0; padding: 0; background: transparent; max-width: 94vw; max-height: 94vh; overflow: visible; color: var(--blue); }
dialog.lb::backdrop { background: color-mix(in srgb, var(--paper) 97%, transparent); animation: lb-fade .45s ease; transition: opacity .6s ease; }
dialog.lb.leaving::backdrop { opacity: 0; }
/* while the waving copy flies, the real image and controls wait */
dialog.lb.flying img, dialog.lb.flying .bar { opacity: 0; }
dialog.lb .bar { transition: opacity .3s; }
@keyframes lb-fade { from { opacity: 0; } }
dialog.lb img { max-width: 94vw; max-height: 86vh; width: auto; height: auto; margin: auto; }
dialog.lb .bar { display: flex; justify-content: space-between; gap: 16px; padding-top: 12px; }
dialog.lb button { all: unset; cursor: pointer; }
dialog.lb button:hover { opacity: .6; }
dialog.lb button:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

/* ---------- work page ---------- */
.w-head { padding-top: clamp(24px, 4vw, 56px); }
/* the huge title's glyph box rises above its line and would swallow clicks; keep the link on top */
.back { position: relative; z-index: 1; display: inline-block; padding: 8px 0; }
.back:hover { opacity: .6; }
.w-title { font-size: clamp(64px, 13vw, 220px); line-height: .82; letter-spacing: -0.03em; margin: 22px 0 clamp(24px, 3vw, 44px); }
.w-meta { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1.5px solid var(--blue); margin: 0; }
.w-meta div { padding: 12px 16px 14px 0; border-bottom: 1px solid var(--blue-rule); }
.w-meta dd { margin: 6px 0 0; }
.w-summary { font: 400 clamp(28px, 3vw, 46px)/1.1 var(--serif); max-width: 30ch; margin: clamp(32px, 5vw, 72px) 0; }
.w-cover { aspect-ratio: 16/9; width: 100%; object-fit: cover; }
.w-label { border-top: 1.5px solid var(--blue); padding-top: 12px; margin: clamp(48px, 7vw, 100px) 0 20px; }
/* clips: wide ones share a row in pairs, tall (phone-shot) ones sit side by side */
.clips { display: flex; flex-wrap: wrap; gap: var(--gutter); align-items: flex-start; }
.clip { margin: 0; flex: 1 1 100%; }
.clips:has(.wide ~ .wide) .clip.wide { flex: 0 1 calc(50% - var(--gutter) / 2); min-width: min(100%, 420px); }   /* no growing, so an odd last clip stays the same size */
.clip.tall { flex: 0 1 300px; }
.clip iframe { display: block; width: 100%; height: auto; border: 0; background: var(--blue); }
.clip figcaption { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.clip figcaption a:hover { opacity: .6; }
@media (max-width: 760px) { .clip.tall { flex: 1 1 calc(50% - var(--gutter)); } }
.stills { columns: 3 280px; column-gap: var(--photo-gap); }
.stills button { all: unset; display: block; cursor: zoom-in; margin-bottom: var(--photo-gap); break-inside: avoid; }
.stills button:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
.stills img { width: 100%; height: auto; }   /* keep each photo's shape; the width/height attributes only reserve space */
.sheet { width: 100%; }
.next { display: block; border-top: 1.5px solid var(--blue); margin-top: clamp(72px, 10vw, 150px); padding: 14px 0 clamp(40px, 6vw, 80px); }
.next b { display: block; font: 400 clamp(56px, 10vw, 170px)/.85 var(--serif); letter-spacing: -0.03em; margin-top: 10px; }
.next b { transition: opacity .2s; }
.next:hover b { opacity: .7; }
@media (max-width: 760px) { .w-meta { grid-template-columns: 1fr 1fr; } }

/* ---------- motion: reveal on scroll ----------
   Only active when <html> has .anim (set in <head> unless reduced motion is on),
   so without JS or with reduced motion everything is simply visible.
   site.js adds .in as each element enters the viewport, then .done once it has settled.
   Only opacity / transform / clip-path animate, which phones handle on the GPU. */
:root { --ease: cubic-bezier(.2, .7, .1, 1); --rise: 28px; }
@media (max-width: 760px) { :root { --rise: 16px; } }

.anim :is(.head h2, .head p, .filters, .list li, .board-foot, .bio, .cv dt, .cv dd,
          .contact h2, .contact-list li, .contact-foot, .back, .w-title, .w-meta > div,
          .w-summary, .w-label, .clips > *, .next):not(.done) {
  transition: opacity .8s var(--ease), transform .9s var(--ease);
  transition-delay: calc(var(--d, 0) * 70ms);
}
.anim :is(.head h2, .head p, .filters, .list li, .board-foot, .bio, .cv dt, .cv dd,
          .contact h2, .contact-list li, .contact-foot, .back, .w-title, .w-meta > div,
          .w-summary, .w-label, .clips > *, .next):not(.in) {
  opacity: 0; transform: translateY(var(--rise));
}
.anim :is(.strip, .stills button, .sheet):not(.done) { transition: opacity 1s var(--ease); transition-delay: calc(var(--d, 0) * 70ms); }
.anim :is(.strip, .stills button, .sheet):not(.in) { opacity: 0; }
/* images unveil from the bottom with a slight settle */
.anim :is(.portrait img, .w-cover):not(.done) { transition: clip-path 1.2s var(--ease), transform 1.4s var(--ease); }
.anim :is(.portrait img, .w-cover):not(.in) { clip-path: inset(100% 0 0 0); transform: scale(1.06); }
/* section rule draws left to right */
.anim .head { position: relative; border-top-color: transparent; }
.anim .head::before {
  content: ""; position: absolute; left: 0; right: 0; top: -1.5px; height: 1.5px;
  background: var(--blue); transform-origin: left; transform: scaleX(0);
  transition: transform 1.1s var(--ease);
}
.anim .head.in::before { transform: none; }
/* archive rows re-enter when a filter is picked */
.anim .list li.pop { animation: rise-in .55s var(--ease) both; animation-delay: calc(var(--d, 0) * 40ms); }

/* first load: header, wordmark, subtitle, then hero labels, in step with the gallery fading in */
.anim .top { animation: fade-in .8s ease both; }
.anim .hero-title > * { animation: rise-in 1s var(--ease) both .05s; }
.anim .hero-title .lab { animation-delay: .3s; }
.anim .hero-title .lab + .lab { animation-delay: .4s; }
.anim .hero-foot { animation: fade-in 1s ease both .7s; }
@keyframes rise-in { from { opacity: 0; transform: translateY(var(--rise)); } }
@keyframes fade-in { from { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
