/*
  Visual language follows the project design system.
  References: Horizontal Clip Path, Parallax Image Layers, Canvas Grid Mouse
  Effect, Images Reveal, Atmospheric Depth Gallery, Parallax Floating and
  Variable Font Cursor Proximity. See CREDITS.md.
*/

@font-face {
  font-family: "Instrument Sans Variable";
  src: url("./assets/fonts/instrument-sans-latin-wght-normal.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Instrument Sans Variable";
  src: url("./assets/fonts/instrument-sans-latin-wght-italic.woff2") format("woff2");
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Instrument Serif";
  src: url("./assets/fonts/instrument-serif-400-normal.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Instrument Serif";
  src: url("./assets/fonts/instrument-serif-400-italic.woff2") format("woff2");
  font-style: italic;
  font-weight: 400;
  font-display: swap;
}

:root {
  --paper: #ece7dd;
  --paper-light: #f5f1e9;
  --ink: #171813;
  --ink-soft: #2b2e27;
  --lichen: #79806e;
  --moss: #3e493b;
  --wine: #4b2925;
  --sand: #c9bda9;
  --line-dark: rgba(23, 24, 19, 0.22);
  --line-light: rgba(245, 241, 233, 0.26);
  --serif: "Instrument Serif", Georgia, serif;
  --sans: "Instrument Sans Variable", "Helvetica Neue", sans-serif;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* {
  box-sizing: border-box;
}

/* <picture> wrappers (AVIF/WebP responsive sources — see render.js and
   scripts/optimize-images.mjs) stay layout-invisible, so every existing
   `... img { height: 100%; object-fit: ...; }` rule across the site keeps
   sizing the selected <img> exactly as it did as a bare element. */
picture {
  display: contents;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  overflow-x: hidden;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  font-variation-settings: "wght" 430;
}

img {
  display: block;
  width: 100%;
}

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

button,
a {
  -webkit-tap-highlight-color: transparent;
}

button {
  color: inherit;
  font: inherit;
}

::selection {
  background: var(--moss);
  color: var(--paper-light);
}

:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

.skip-link {
  position: fixed;
  z-index: 1000;
  top: 12px;
  left: 12px;
  padding: 8px 16px;
  background: var(--paper-light);
  color: var(--ink);
  transform: translateY(-160%);
  transition: transform 150ms var(--ease);
}

.skip-link:focus {
  transform: translateY(0);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.topbar {
  position: fixed;
  z-index: 100;
  top: 0;
  left: 0;
  display: grid;
  width: 100%;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 20px 32px;
  color: #fff;
  mix-blend-mode: difference;
  font-size: 12px;
  font-variation-settings: "wght" 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.topbar-name {
  justify-self: start;
}

.topbar nav {
  display: flex;
  gap: 32px;
}

.topbar nav a {
  position: relative;
}

.topbar nav a::after,
.footer-bottom a::after,
.footer-contact a::after,
.footer-legal-link::after {
  position: absolute;
  right: 0;
  bottom: -4px;
  left: 0;
  height: 1px;
  content: "";
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 250ms var(--ease);
}

.topbar nav a:hover::after,
.topbar nav a:focus-visible::after,
.footer-bottom a:hover::after,
.footer-bottom a:focus-visible::after,
.footer-contact a:hover::after,
.footer-contact a:focus-visible::after,
.footer-legal-link:hover::after,
.footer-legal-link:focus-visible::after {
  transform: scaleX(1);
  transform-origin: left;
}

.topbar-place {
  justify-self: end;
}

/* Hero — composition unchanged; the duplicate text layer is clipped to the photo. */
.prologue {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  background: var(--paper);
}

.prologue-rule {
  position: absolute;
  z-index: 1;
  background: var(--line-dark);
}

.prologue-rule--one {
  top: 0;
  bottom: 0;
  left: 30%;
  width: 1px;
}

.prologue-rule--two {
  right: 0;
  bottom: 20%;
  left: 0;
  height: 1px;
}

.prologue-kicker {
  position: absolute;
  z-index: 4;
  top: 104px;
  left: 32px;
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.prologue-discipline {
  position: absolute;
  z-index: 4;
  top: 152px;
  left: 32px;
  max-width: 260px;
  margin: 0;
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 400;
  line-height: 1.12;
}

/* Mobile only (see media query below): a duplicate of the discipline
   heading, clipped to exactly the hero photo's footprint and colored
   white, sitting on top of the real (dark) one — so wherever the text
   crosses onto the image it reads white, and everywhere else the
   original dark text underneath still shows through. Same technique as
   .prologue-contrast-mask below, just for this element specifically. */
.prologue-discipline-mask {
  display: none;
}

.prologue-image-primary {
  position: absolute;
  z-index: 0;
  top: 0;
  right: 0;
  width: 70%;
  height: 80%;
  overflow: hidden;
  clip-path: polygon(10% 0, 100% 0, 100% 100%, 0 88%);
  background: var(--ink);
}

.prologue-image-primary::after {
  position: absolute;
  inset: 0;
  content: "";
  background: linear-gradient(90deg, rgba(20, 23, 18, 0.38), rgba(20, 23, 18, 0.04) 62%);
}

.prologue-image-primary img {
  height: 100%;
  object-fit: cover;
  filter: saturate(0.72) contrast(1.05);
  transform: scale(1.04);
  will-change: transform;
}

.prologue-image-layer {
  position: absolute;
  z-index: 2;
  top: 0;
  right: 0;
  width: 70%;
  height: 80%;
  overflow: hidden;
  clip-path: polygon(30% 28%, 78% 22%, 87% 91%, 22% 94%);
  pointer-events: none;
  will-change: transform;
}

.prologue-image-layer img {
  height: 100%;
  object-fit: cover;
  filter: saturate(0.9) contrast(1.08);
  transform: scale(1.03);
}

.prologue-axis {
  position: absolute;
  z-index: 3;
  margin: 0;
  line-height: 0.78;
}

.prologue-axis--matter {
  top: 39%;
  left: 7%;
  color: transparent;
  font-size: clamp(72px, 10vw, 160px);
  font-variation-settings: "wght" 640;
  letter-spacing: -0.07em;
  -webkit-text-stroke: 1px var(--ink);
}

.prologue-axis--light {
  right: 5%;
  bottom: 11%;
  color: var(--ink);
  font-family: var(--serif);
  font-size: clamp(80px, 13vw, 200px);
  font-style: italic;
  letter-spacing: -0.07em;
}

.prologue-axis--use {
  right: 32px;
  top: 34%;
  color: var(--paper-light);
  font-size: 12px;
  font-variation-settings: "wght" 660;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
}

.prologue-contrast-mask {
  position: absolute;
  z-index: 3;
  inset: 0;
  overflow: hidden;
  clip-path: polygon(37% 0, 100% 0, 100% 80%, 30% 70.4%);
  pointer-events: none;
}

.prologue-axis--contrast.prologue-axis--matter {
  color: transparent;
  -webkit-text-stroke-color: var(--paper-light);
}

.prologue-axis--contrast.prologue-axis--light {
  color: var(--paper-light);
}

.prologue-manifesto {
  position: absolute;
  z-index: 4;
  bottom: 40px;
  left: 32px;
  display: grid;
  width: min(58%, 760px);
  grid-template-columns: 120px 1fr;
  gap: 32px;
  align-items: start;
}

.prologue-manifesto span {
  padding-top: 8px;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.prologue-manifesto p {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(20px, 2.4vw, 36px);
  line-height: 1.16;
}

.section-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line-dark);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.section-heading p,
.section-heading span {
  margin: 0;
}

.project-index {
  position: relative;
  min-height: 100svh;
  padding: 128px 32px 96px;
  background: var(--paper-light);
}

.index-intro {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  padding: 48px 0 72px;
}

.index-count {
  font-family: var(--serif);
  font-size: clamp(120px, 19vw, 280px);
  line-height: 0.72;
  letter-spacing: -0.08em;
}

.index-intro p {
  justify-self: end;
  width: min(100%, 480px);
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(24px, 3vw, 48px);
  line-height: 1.05;
}

.index-list {
  position: relative;
  margin: 0;
  padding: 0;
  list-style: none;
}

.index-list li {
  border-top: 1px solid var(--line-dark);
}

.index-list li:last-child {
  border-bottom: 1px solid var(--line-dark);
}

.index-list a {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 72px minmax(260px, 1fr) minmax(220px, 0.7fr);
  align-items: baseline;
  gap: 24px;
  padding: 20px 0;
}

.index-number {
  font-size: 12px;
  letter-spacing: 0.12em;
}

.index-title {
  font-family: var(--serif);
  font-size: clamp(32px, 4vw, 64px);
  line-height: 1;
  letter-spacing: -0.035em;
}

.index-meta {
  justify-self: end;
  font-size: 12px;
  letter-spacing: 0.04em;
}

.index-preview {
  position: fixed;
  z-index: 20;
  top: 50%;
  left: 55%;
  width: min(28vw, 400px);
  height: min(42vh, 480px);
  margin: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -42%) rotate(-3deg) scale(0.94);
  transition: opacity 250ms var(--ease), transform 400ms var(--ease);
}

.index-preview img {
  height: 100%;
  object-fit: cover;
}

.index-list a:hover .index-preview,
.index-list a:focus-visible .index-preview {
  opacity: 1;
  transform: translate(-50%, -50%) rotate(0) scale(1);
}

/* Four-project horizontal clip sequence. */
.projects-scroll {
  position: relative;
  height: 520svh;
  background: var(--ink);
}

.projects-pin {
  position: sticky;
  z-index: 1;
  top: 0;
  height: 100svh;
  overflow: hidden;
  background: var(--ink);
}

.project-panel {
  position: absolute;
  inset: 0;
  height: 100%;
  overflow: hidden;
  will-change: clip-path;
}

.project-panel:nth-child(1) { z-index: 1; }
.project-panel:nth-child(2) { z-index: 2; clip-path: inset(0 100% 0 0); }
.project-panel:nth-child(3) { z-index: 3; clip-path: inset(0 100% 0 0); }
.project-panel:nth-child(4) { z-index: 4; clip-path: inset(0 100% 0 0); }
/* Headroom for a 5th/6th primary project — still finite, not generated
   from data (CSS can't read csv/projects.csv). Adding a 7th needs one more
   :nth-child(7) rule following the same pattern. */
.project-panel:nth-child(5) { z-index: 5; clip-path: inset(0 100% 0 0); }
.project-panel:nth-child(6) { z-index: 6; clip-path: inset(0 100% 0 0); }

.panel-copy {
  position: absolute;
  z-index: 10;
  top: 96px;
  left: 32px;
  width: min(38vw, 540px);
}

.panel-copy--light,
.panel-copy--nike {
  color: var(--paper-light);
}

.panel-number {
  display: block;
  margin-bottom: 48px;
  font-size: 12px;
  letter-spacing: 0.16em;
}

.panel-type {
  margin: 0 0 24px;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.panel-copy h2 {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(72px, 10vw, 152px);
  font-weight: 400;
  line-height: 0.72;
  letter-spacing: -0.07em;
}

.panel-copy h2 em {
  font-weight: 400;
}

.panel-copy h2 small {
  font-family: var(--sans);
  font-size: 0.22em;
  font-weight: 400;
  letter-spacing: 0.02em;
  vertical-align: 0.36em;
}

.panel-description {
  width: min(100%, 440px);
  margin: 48px 0 0;
  font-size: 16px;
  line-height: 1.65;
}

.panel-foot {
  position: absolute;
  z-index: 12;
  right: 32px;
  bottom: 28px;
  left: 32px;
  display: flex;
  justify-content: flex-end;
  gap: 32px;
  padding-top: 12px;
  border-top: 1px solid var(--line-dark);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.panel-foot--light {
  border-color: var(--line-light);
  color: var(--paper-light);
}

/* The caption treatment is a frosted-glass/backdrop-blur reveal. */
.project-media {
  position: relative;
  margin: 0;
  overflow: hidden;
}

.media-caption {
  position: absolute;
  z-index: 8;
  right: 0;
  bottom: 0;
  left: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px;
  align-items: baseline;
  padding: 16px;
  background: rgba(14, 15, 12, 0.78);
  color: #fff;
  opacity: 0;
  transform: translateY(100%);
  transition: opacity 250ms var(--ease), transform 400ms var(--ease);
  pointer-events: none;
}

.media-caption--dark {
  background: rgba(245, 241, 233, 0.85);
  color: var(--ink);
}

.media-caption span {
  font-size: 12px;
  letter-spacing: 0.12em;
}

.media-caption strong {
  font-size: 12px;
  font-variation-settings: "wght" 560;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.project-media:hover .media-caption,
.depth-card:hover .media-caption,
.depth-card:focus-visible .media-caption {
  opacity: 1;
  transform: translateY(0);
}

/* Caption reveal is :hover-only, not :focus-within — a lightbox close
   returns focus to whichever photo opened it (real accessibility, not
   optional), and tying the caption to focus meant that photo's caption
   stayed stuck open afterward. Keyboard users still get a visible focus
   ring here instead. */
.project-media:focus-visible {
  outline: 2px solid var(--paper-light);
  outline-offset: -2px;
}

/* Cabane: the drawings sit on opaque paper plates instead of blending into the forest. */
.project-panel--cabane {
  background: #11150f;
}

.cabane-backdrop {
  position: absolute;
  inset: 0;
}

.cabane-backdrop::after {
  position: absolute;
  inset: 0;
  content: "";
  background: linear-gradient(90deg, rgba(10, 13, 9, 0.94) 0%, rgba(10, 13, 9, 0.55) 37%, rgba(10, 13, 9, 0.14) 100%);
}

.cabane-backdrop img {
  height: 100%;
  object-fit: cover;
  filter: saturate(0.65) contrast(1.1);
}

.cabane-media-board {
  position: absolute;
  z-index: 5;
  top: 11%;
  right: 3%;
  width: 55%;
  height: 76%;
}

.cabane-media {
  position: absolute;
  background: #e9e3d8;
  box-shadow: 0 16px 44px rgba(5, 7, 4, 0.25);
}

.cabane-media img {
  height: 100%;
  object-fit: cover;
}

.cabane-media--exterior {
  z-index: 2;
  top: 5%;
  left: 0;
  width: 57%;
  height: 56%;
  padding: 8px;
  transform: rotate(-2deg);
}

.cabane-media--exterior img,
.cabane-media--plan img {
  object-fit: contain;
  background: #e9e3d8;
}

.cabane-media--interior {
  z-index: 4;
  top: 0;
  right: 0;
  width: 42%;
  height: 42%;
  transform: rotate(1.5deg);
}

.cabane-media--bedroom {
  z-index: 5;
  right: 2%;
  bottom: 2%;
  width: 43%;
  height: 39%;
  transform: rotate(-1deg);
}

.cabane-media--plan {
  z-index: 3;
  bottom: 0;
  left: 8%;
  width: 38%;
  height: 34%;
  padding: 8px;
  transform: rotate(2deg);
}

/* Nike: the canvas is a removable tiled veil over a project patchwork. */
.project-panel--nike {
  background: #0d0e0c;
  color: var(--paper-light);
}

.nike-canvas-wrap {
  position: absolute;
  top: 0;
  right: 0;
  width: 64%;
  height: 100%;
  overflow: hidden;
  background: #1a1b17;
}

/* The mosaic's source image is never shown directly — only the canvas
   draws it. Reveal gaps show .nike-canvas-wrap's own flat background,
   not another photo, so the hover effect stays legible (see script.js's
   initReverseMosaic). */
.nike-mosaic-source {
  display: none;
}

.nike-canvas {
  position: absolute;
  z-index: 3;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.nike-canvas-wrap::after {
  position: absolute;
  z-index: 5;
  inset: 0;
  content: "";
  pointer-events: none;
  background: linear-gradient(90deg, #0d0e0c 0%, transparent 36%);
}

.nike-poster {
  position: absolute;
  z-index: 7;
  right: 7%;
  bottom: 9%;
  width: min(19vw, 280px);
  max-height: 66%;
  object-fit: contain;
  transform: rotate(3deg);
  box-shadow: 0 20px 64px rgba(0, 0, 0, 0.36);
}

.nike-lines {
  position: absolute;
  z-index: 6;
  right: -3%;
  top: 8%;
  width: 55%;
  height: 18%;
  transform: rotate(-7deg);
}

.nike-lines i {
  position: absolute;
  right: 0;
  width: 100%;
  height: 4px;
  transform-origin: right;
}

.nike-lines i:nth-child(1) { top: 0; background: #e95196; transform: scaleX(0.9); }
.nike-lines i:nth-child(2) { top: 20px; background: #f4cf3f; transform: scaleX(0.76); }
.nike-lines i:nth-child(3) { top: 40px; background: #6ccdd2; transform: scaleX(0.98); }
.nike-lines i:nth-child(4) { top: 60px; background: #f1a179; transform: scaleX(0.68); }
.nike-lines i:nth-child(5) { top: 80px; background: #5271a8; transform: scaleX(0.84); }

/* Véranda: cards arrive through scroll, then resolve to the original fan. */
.project-panel--veranda {
  background: var(--paper-light);
}

.panel-copy--veranda {
  top: 88px;
  width: min(36vw, 520px);
}

.veranda-notation {
  position: absolute;
  right: 32px;
  top: 88px;
  font-size: 12px;
  letter-spacing: 0.16em;
}

.photo-stack {
  position: absolute;
  top: 13%;
  right: 4%;
  width: 56%;
  height: 70%;
}

.photo-card {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(46vw, 700px);
  height: min(50vh, 520px);
  padding: 12px;
  background: #fff;
  box-shadow: 0 20px 60px rgba(51, 42, 31, 0.12);
  opacity: 0;
  transform: translate(-50%, 20%) rotate(0deg) scale(0.94);
  will-change: transform, opacity;
}

/* The fanned photos overlap almost entirely (near-identical data-x/data-y
   with only rotation setting them apart), so a middle card in DOM order
   can end up with only a thin rotated-corner sliver actually exposed —
   bring whichever one the cursor is currently over (or has keyboard
   focus) to the front so every photo, not just the first/last in DOM
   order, is reachable and its full caption/hover state is visible. */
.photo-card:hover,
.photo-card:focus-visible {
  z-index: 10;
}

.photo-card img {
  height: 100%;
  object-fit: contain;
  background: #fff;
}

/* Cave remains compositionally identical; only captions have been added. */
.project-panel--cave {
  background: #2b201d;
  color: var(--paper-light);
}

.cave-gallery {
  position: absolute;
  inset: 0 0 0 38%;
  overflow: hidden;
}

.cave-gallery::after {
  position: absolute;
  z-index: 5;
  inset: 0;
  content: "";
  background: linear-gradient(90deg, #2b201d 0%, transparent 28%);
  pointer-events: none;
}

.cave-image {
  position: absolute;
}

.cave-image img {
  height: 100%;
  object-fit: cover;
  filter: saturate(0.72) sepia(0.08);
}

.cave-image--main {
  top: 0;
  right: 0;
  width: 86%;
  height: 72%;
}

.cave-image--detail {
  right: 7%;
  bottom: 8%;
  z-index: 3;
  width: 43%;
  height: 38%;
  border: 8px solid #2b201d;
}

.cave-image--plan {
  left: 8%;
  bottom: 8%;
  z-index: 3;
  width: 28%;
  height: 32%;
  border: 8px solid #2b201d;
}

.cave-image--plan img {
  object-fit: contain;
  background: #f0e9df;
}

/* Stage 2 (see script.js's initStageCrossfade): a deliberately different
   composition from main/detail/plan above, not a re-skin of the same
   three shapes — anchored left instead of right, one accent top-right
   instead of two along the bottom. */
.cave-image--exhibition {
  top: 0;
  left: 0;
  width: 64%;
  height: 100%;
}

.cave-image--overview {
  top: 6%;
  right: 4%;
  z-index: 3;
  width: 30%;
  height: 36%;
  border: 8px solid #2b201d;
}

.panel-copy--cave {
  width: min(34vw, 480px);
}

/* Stage crossfade base state (script.js's initStageCrossfade sets the
   actual opacity continuously as the user scrolls through the panel —
   this is just the sensible starting point before that first runs, and
   the no-JS fallback). */
[data-stage="2"] {
  opacity: 0;
  pointer-events: none;
}

/* Atmospheric depth gallery: one scene at a time, with surrounding studies. */
.archive {
  position: relative;
  overflow: hidden;
  padding: 128px 32px 104px;
  background: #171813;
  color: var(--paper-light);
  transition: background-color 800ms var(--ease);
}

.archive[data-mood="1"] { background: #2d2922; }
.archive[data-mood="2"] { background: #282722; }
.archive[data-mood="3"] { background: #1c2420; }
.archive[data-mood="4"] { background: #30251f; }
/* Headroom for a 6th archive card; a 7th falls back to the base
   background (still fine, just loses the per-card mood tint) until one
   more rule is added here. */
.archive[data-mood="5"] { background: #23201c; }

.section-heading--archive {
  border-color: var(--line-light);
}

.archive-intro {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding: 56px 0 40px;
}

.archive-intro p {
  width: min(70%, 900px);
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(48px, 7vw, 104px);
  line-height: 0.9;
  letter-spacing: -0.05em;
}

.archive-intro span {
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.depth-gallery {
  position: relative;
  width: 100%;
  height: min(82svh, 840px);
  min-height: 620px;
  overflow: hidden;
  perspective: 1400px;
  isolation: isolate;
}

.depth-gallery::before {
  position: absolute;
  z-index: -1;
  top: 50%;
  left: 50%;
  width: min(68vw, 980px);
  height: min(68vw, 980px);
  content: "";
  border: 1px solid rgba(245, 241, 233, 0.12);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}

.depth-card {
  --float-x: 0px;
  --float-y: 0px;
  --idle-x: 0px;
  --idle-y: 0px;
  position: absolute;
  z-index: 1;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  opacity: 0.54;
  translate: calc(var(--float-x) + var(--idle-x)) calc(var(--float-y) + var(--idle-y));
  transition: top 600ms var(--ease), right 600ms var(--ease), bottom 600ms var(--ease), left 600ms var(--ease), width 600ms var(--ease), height 600ms var(--ease), opacity 400ms var(--ease), filter 400ms var(--ease), transform 600ms var(--ease), translate 250ms var(--ease);
}

.depth-card figure {
  position: relative;
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--ink-soft);
  box-shadow: 0 24px 80px rgba(7, 8, 6, 0.28);
}

.depth-card img {
  height: 100%;
  object-fit: cover;
  filter: saturate(0.72);
  transition: filter 400ms var(--ease), transform 400ms var(--ease);
}

.depth-card:hover img,
.depth-card:focus-visible img,
.depth-card[data-position="0"] img {
  filter: saturate(1);
}

.depth-card[data-position="0"] {
  z-index: 8;
  top: 4%;
  left: 50%;
  width: min(35vw, 520px);
  height: 68%;
  opacity: 1;
  transform: translateX(-50%) translateZ(120px) rotate(0deg);
}

.depth-card[data-position="-1"] {
  z-index: 5;
  top: 16%;
  left: 5%;
  width: min(21vw, 300px);
  height: 39%;
  transform: translateZ(-80px) rotate(-4deg);
}

.depth-card[data-position="1"] {
  z-index: 5;
  top: 13%;
  left: 74%;
  width: min(21vw, 300px);
  height: 40%;
  transform: translateZ(-60px) rotate(4deg);
}

.depth-card[data-position="-2"] {
  z-index: 3;
  top: 61%;
  left: 18%;
  width: min(17vw, 240px);
  height: 31%;
  transform: translateZ(-180px) rotate(3deg);
}

.depth-card[data-position="2"] {
  z-index: 3;
  top: 62%;
  left: 66%;
  width: min(17vw, 240px);
  height: 32%;
  transform: translateZ(-168px) rotate(-3deg);
}

/* Headroom for a 6th archive card: initDepthGallery()'s coverflow math
   (script.js) puts any card past ±2 on the negative side first, so a 6th
   card needs this one extra slot. A 7th needs data-position="-4" added
   the same way, and the formula itself stops being symmetric past 6 —
   worth revisiting the position formula rather than chasing it with more
   rules if the archive regularly grows past 6. */
.depth-card[data-position="-3"] {
  z-index: 2;
  top: 70%;
  left: 3%;
  width: min(13vw, 190px);
  height: 25%;
  transform: translateZ(-220px) rotate(5deg);
}

.depth-card:not([data-position="0"]):hover,
.depth-card:not([data-position="0"]):focus-visible {
  opacity: 0.9;
}

/* Focus mode (second click on the already-centered card): it stays
   exactly where it is — data-position doesn't change — everything else
   in the carousel just recedes. .depth-card already transitions filter
   and opacity (see the base rule above), so this doesn't need its own
   transition declaration. */
.depth-gallery.is-focused .depth-card:not([data-position="0"]) {
  opacity: 0.16;
  filter: blur(10px);
}

.depth-gallery.is-focused::before {
  opacity: 0.25;
  filter: blur(6px);
  transition: opacity 500ms var(--ease), filter 500ms var(--ease);
}

.depth-related {
  position: absolute;
  z-index: 9;
  inset: 0;
  pointer-events: none;
}

.depth-related img {
  position: absolute;
  z-index: 1;
  width: 148px;
  height: 192px;
  object-fit: cover;
  border: 4px solid var(--paper-light);
  box-shadow: 0 12px 32px rgba(5, 7, 4, 0.35);
  opacity: 0;
  pointer-events: auto;
  cursor: pointer;
  transform: translate(-50%, -50%) scale(0.7);
  transition: opacity 400ms var(--ease), transform 400ms var(--ease), z-index 0s;
}

.depth-related img:hover,
.depth-related img:focus-visible {
  z-index: 2;
  transform: translate(-50%, -50%) scale(1.06);
}

.depth-related.is-visible img {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.depth-gallery-label {
  position: absolute;
  z-index: 12;
  bottom: 2%;
  left: 50%;
  width: min(38vw, 560px);
  padding-top: 12px;
  border-top: 1px solid var(--line-light);
  transform: translateX(-50%);
  text-align: center;
}

.depth-gallery-label span,
.depth-gallery-label p {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.depth-gallery-label h3 {
  margin: 8px 0 4px;
  font-family: var(--serif);
  font-size: clamp(32px, 4vw, 56px);
  font-weight: 400;
  line-height: 1;
}

.depth-gallery-label p {
  margin: 0;
  color: var(--sand);
}

.depth-gallery-hint {
  position: absolute;
  right: 0;
  bottom: 2%;
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.about {
  padding: 128px 32px 96px;
  background: var(--paper);
}

.about-title {
  display: flex;
  flex-direction: column;
  padding: 88px 0 96px;
  line-height: 0.72;
}

.about-title-proximity {
  display: flex;
  align-self: flex-start;
  font-size: clamp(80px, 15vw, 232px);
  font-variation-settings: "wght" 470;
  letter-spacing: -0.08em;
}

.about-title-proximity > span {
  display: inline-block;
  font-variation-settings: "wght" 470;
  transition: font-variation-settings 150ms var(--ease), transform 150ms var(--ease);
}

.about-title-serif {
  align-self: flex-end;
  font-family: var(--serif);
  font-size: clamp(64px, 11vw, 168px);
  font-style: italic;
  letter-spacing: -0.06em;
}

.about-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8vw;
  padding-top: 32px;
  border-top: 1px solid var(--line-dark);
}

.about-lead-col {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.about-portrait {
  width: min(220px, 60%);
  margin: 0;
}

.about-portrait img {
  width: 100%;
  height: auto;
  filter: grayscale(1) contrast(1.05);
}

.about-lead {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(28px, 3.2vw, 48px);
  line-height: 1.12;
}

.about-copy {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
}

.about-copy p {
  margin: 0;
  font-size: 16px;
  line-height: 1.7;
}

.about blockquote {
  width: min(80%, 1100px);
  margin: 160px auto 64px;
  font-family: var(--serif);
  font-size: clamp(44px, 7vw, 104px);
  font-style: italic;
  line-height: 0.95;
  letter-spacing: -0.045em;
  text-align: center;
}

.site-footer {
  padding: 96px 32px 28px;
  background: var(--moss);
  color: var(--paper-light);
}

.footer-name {
  display: block;
  font-family: var(--serif);
  font-size: clamp(88px, 15vw, 224px);
  font-style: italic;
  line-height: 0.72;
  letter-spacing: -0.07em;
}

.site-footer > p {
  margin: 72px 0 32px;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.footer-contact {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 32px;
  margin: 0 0 56px;
  font-size: 14px;
  letter-spacing: 0.02em;
}

.footer-contact a {
  position: relative;
}

.footer-bottom {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--line-light);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.footer-legal-link {
  display: block;
  width: max-content;
  margin: 24px auto 0;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.6;
}

.footer-bottom a,
.footer-legal-link {
  position: relative;
}

/* Click-to-close-up: click any project photo (not backgrounds, not the
   archive carousel — that has its own second-click focus mode above) to
   see it centered and larger, with the rest of the page dimmed and
   blurred behind it. See script.js's initLightbox. */
.is-closeupable {
  cursor: pointer;
}

.lightbox {
  position: fixed;
  z-index: 100;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px;
  background: rgba(10, 13, 9, 0.72);
  backdrop-filter: blur(28px) saturate(0.8);
  -webkit-backdrop-filter: blur(28px) saturate(0.8);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 300ms var(--ease), visibility 0s linear 300ms;
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 300ms var(--ease);
}

body.lightbox-lock {
  overflow: hidden;
}

.lightbox-close {
  position: absolute;
  top: 24px;
  right: 24px;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(245, 241, 233, 0.3);
  border-radius: 50%;
  background: transparent;
  color: var(--paper-light);
  font-size: 16px;
  cursor: pointer;
  transition: border-color 200ms var(--ease), transform 200ms var(--ease);
}

.lightbox-close:hover,
.lightbox-close:focus-visible {
  border-color: var(--paper-light);
  transform: scale(1.06);
}

.lightbox-figure {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: min(90vw, 1200px);
  max-height: 90vh;
  margin: 0;
  transform: scale(0.96);
  transition: transform 300ms var(--ease);
}

.lightbox.is-open .lightbox-figure {
  transform: scale(1);
}

.lightbox-figure img {
  max-width: 100%;
  max-height: 78vh;
  object-fit: contain;
  box-shadow: 0 40px 120px rgba(0, 0, 0, 0.5);
}

.lightbox-figure figcaption {
  display: flex;
  gap: 12px;
  align-items: baseline;
  margin-top: 20px;
  color: var(--paper-light);
}

.lightbox-figure figcaption span {
  font-size: 12px;
  letter-spacing: 0.12em;
}

.lightbox-figure figcaption strong {
  font-size: 14px;
  font-variation-settings: "wght" 560;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.legal-page {
  min-height: 100svh;
  padding: 96px 32px;
  background: var(--paper-light);
}

.legal-page > * {
  width: min(100%, 800px);
  margin-right: auto;
  margin-left: auto;
}

.legal-page h1 {
  margin: 48px auto;
  font-family: var(--serif);
  font-size: clamp(64px, 10vw, 128px);
  font-weight: 400;
  line-height: 0.8;
  letter-spacing: -0.06em;
}

.legal-page h2 {
  margin: 56px auto 16px;
  font-family: var(--serif);
  font-size: 36px;
  font-weight: 400;
}

.legal-page p,
.legal-page li {
  font-size: 16px;
  line-height: 1.65;
}

.legal-warning {
  padding: 20px;
  border: 1px solid #8b5e48;
  background: rgba(139, 94, 72, 0.08);
}

.legal-back {
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

@media (max-width: 1120px) and (min-width: 900px) {
  .cabane-media-board {
    right: 2%;
    width: 53%;
  }

  .panel-copy {
    width: 40vw;
  }

  .panel-description {
    margin-top: 32px;
  }
}

@media (width < 900px) {
  .topbar {
    grid-template-columns: 1fr auto;
    padding: 16px;
  }

  .topbar nav {
    gap: 16px;
  }

  .topbar nav a:first-child,
  .topbar-place {
    display: none;
  }

  .prologue {
    min-height: 112svh;
  }

  .prologue-rule--one { left: 20%; }
  .prologue-rule--two { bottom: 24%; }
  .prologue-kicker { top: 80px; left: 16px; }

  .prologue-discipline {
    top: 112px;
    left: 16px;
    max-width: 220px;
    font-size: 20px;
  }

  /* Same rect as .prologue-image-primary/.prologue-image-layer below
     (top:20%, width:88% from the right edge i.e. left:12%, height:57%)
     so the white duplicate is clipped to exactly where the photo sits. */
  .prologue-discipline-mask {
    display: block;
    position: absolute;
    z-index: 5;
    inset: 0;
    overflow: hidden;
    clip-path: inset(20% 0 23% 12%);
    pointer-events: none;
  }

  .prologue-discipline--contrast {
    margin: 0;
    color: var(--paper-light);
  }

  .prologue-image-primary,
  .prologue-image-layer {
    top: 20%;
    width: 88%;
    height: 57%;
  }

  .prologue-axis--matter { top: 40%; left: 1%; font-size: 19vw; }
  .prologue-axis--light { right: 4%; bottom: 20%; font-size: 25vw; }
  .prologue-axis--use { right: 12px; top: 38%; }

  .prologue-contrast-mask {
    clip-path: polygon(20.8% 20%, 100% 20%, 100% 77%, 12% 70.16%);
  }

  .prologue-manifesto {
    bottom: 32px;
    left: 16px;
    width: calc(100% - 32px);
    grid-template-columns: 80px 1fr;
    gap: 16px;
  }

  .prologue-manifesto p { font-size: 20px; }

  .project-index {
    min-height: auto;
    padding: 96px 16px 72px;
  }

  .index-intro {
    grid-template-columns: 1fr;
    gap: 48px;
    padding: 48px 0;
  }

  .index-count { font-size: 144px; }
  .index-intro p { justify-self: start; }

  .index-list a {
    grid-template-columns: 40px 1fr;
    gap: 8px 16px;
  }

  .index-meta {
    grid-column: 2;
    justify-self: start;
  }

  .index-preview { display: none; }

  .projects-scroll {
    height: auto;
  }

  .projects-pin {
    position: relative;
    height: auto;
    overflow: visible;
  }

  .project-panel,
  .project-panel:nth-child(n) {
    position: relative;
    inset: auto;
    min-height: 112svh;
    clip-path: none;
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 700ms var(--ease), transform 700ms var(--ease);
  }

  .project-panel.is-revealed {
    opacity: 1;
    transform: translateY(0);
  }

  .panel-copy {
    top: 72px;
    left: 16px;
    width: calc(100% - 32px);
  }

  .panel-number { margin-bottom: 24px; }
  .panel-copy h2 { font-size: clamp(64px, 22vw, 112px); }

  .panel-description {
    max-width: 520px;
    margin-top: 32px;
    font-size: 16px;
  }

  .panel-foot {
    right: 16px;
    bottom: 20px;
    left: 16px;
    justify-content: space-between;
    gap: 8px;
  }

  .panel-foot span:nth-child(3) { display: none; }

  /* Taller than before (was 142svh) with the media board pushed further
     down (was top:47%) — the description paragraph is long enough that
     it was running into the board's top edge; this gives it clear room
     regardless of exactly how many lines it wraps to on a given phone. */
  .project-panel--cabane { min-height: 176svh; }

  .cabane-backdrop::after {
    background: linear-gradient(180deg, rgba(10, 13, 9, 0.92) 0%, rgba(10, 13, 9, 0.45) 48%, rgba(10, 13, 9, 0.78) 100%);
  }

  .cabane-media-board {
    top: 60%;
    right: 16px;
    left: 16px;
    width: auto;
    height: 37%;
  }

  .cabane-media--exterior { width: 58%; height: 54%; }
  .cabane-media--interior { width: 40%; height: 42%; }
  .cabane-media--bedroom { width: 42%; height: 38%; }
  .cabane-media--plan { width: 40%; height: 34%; }

  /* Same reasoning as cabane above — more height, media pushed down, so
     the description text has clear room above it. */
  .project-panel--nike { min-height: 150svh; }

  .nike-canvas-wrap {
    top: 52%;
    width: 100%;
    height: 45%;
  }

  .nike-canvas-wrap::after {
    background: linear-gradient(180deg, #0d0e0c 0%, transparent 32%);
  }

  .nike-poster { right: 6%; bottom: 7%; width: 32%; }
  .nike-lines { top: 32%; width: 90%; }

  /* Tall on purpose: the photo-stack's scroll-driven reveal (script.js)
     computes its progress from how far this panel has actually scrolled
     through the viewport, so giving it 3x+ the room is what makes each
     photo linger — a taller min-height, not a tweak to the JS formula
     (which would just mean the last photos never fully arrive). */
  .project-panel--veranda { min-height: 400svh; }

  /* Sticky, not absolute: the title/intro stay in view for as long as
     the (very tall) panel is being scrolled through, i.e. until the
     photo sequence below has fully played out — then it releases
     naturally once the panel itself scrolls past, moving on to cave. */
  .panel-copy--veranda {
    position: sticky;
    top: 64px;
  }

  .photo-stack {
    top: 14%;
    right: 0;
    width: 100%;
    /* Ends well short of the panel's own bottom edge (66% + 14% top =
       80%) so there's a real settled hold after the last photo arrives —
       the véranda background stays in view until then — before scrolling
       far enough to actually leave the panel for cave. */
    height: 66%;
  }

  .photo-card {
    width: 76vw;
    height: 38vh;
    padding: 8px;
    transition: opacity 600ms var(--ease), transform 700ms var(--ease);
  }

  .veranda-notation { display: none; }

  /* Same reasoning as cabane/nike above. */
  .project-panel--cave { min-height: 140svh; }
  .cave-gallery { inset: 52% 0 0 0; }

  .cave-gallery::after {
    background: linear-gradient(180deg, #2b201d 0%, transparent 24%);
  }

  .cave-image--main { width: 100%; height: 100%; }

  /* Desktop pins these to bottom:8%, which on the mobile gallery box left
     the whole middle empty and both overlays crowding the very bottom.
     Spread them through the middle of the gallery region instead. */
  .cave-image--detail { top: 24%; right: 4%; bottom: auto; width: 44%; height: 34%; }
  .cave-image--plan { top: 48%; left: 4%; bottom: auto; width: 32%; height: 28%; }

  .archive { padding: 96px 16px 72px; }

  .archive-intro {
    align-items: flex-start;
    flex-direction: column;
    gap: 32px;
  }

  .archive-intro p { width: 100%; }

  /* No coverflow on mobile — a vertical zigzag list instead, each card a
     plain single-tap close-up (script.js branches the click handler on
     !desktop.matches). The [data-position] rules above still apply on
     desktop only; here every card resets to static, normal-flow, full
     opacity regardless of its data-position value (same specificity,
     later in the cascade wins — see initDepthGallery's comment). */
  .depth-gallery {
    display: flex;
    flex-direction: column;
    height: auto;
    min-height: 0;
    overflow: visible;
    gap: 40px;
    padding: 8px 0 32px;
  }

  .depth-gallery::before { display: none; }

  .depth-card[data-position] {
    position: static;
    top: auto;
    right: auto;
    bottom: auto;
    left: auto;
    z-index: auto;
    width: 74vw;
    max-width: 420px;
    height: auto;
    aspect-ratio: 4 / 5;
    opacity: 1;
    transform: none;
  }

  .depth-card:nth-child(odd) { align-self: flex-start; rotate: -2deg; }
  .depth-card:nth-child(even) { align-self: flex-end; rotate: 2deg; }
  .depth-card:nth-child(3n) { align-self: center; rotate: -1deg; }

  .depth-gallery-label,
  .depth-gallery-hint {
    display: none;
  }

  .about { padding: 96px 16px 72px; }
  .about-title { padding: 72px 0; }

  .about-grid {
    grid-template-columns: 1fr;
    gap: 48px;
  }

  .about-copy { grid-template-columns: 1fr; }

  .about blockquote {
    width: 100%;
    margin-top: 112px;
  }

  .site-footer { padding: 80px 16px 24px; }
  .site-footer > p { margin: 56px 0 28px; }
  .footer-contact { margin-bottom: 40px; }

  .footer-bottom {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .footer-bottom a { width: max-content; }
  .legal-page { padding: 72px 16px; }

  .lightbox {
    padding: 16px;
  }

  .lightbox-close {
    top: 12px;
    right: 12px;
  }

  .lightbox-figure img {
    max-height: 68vh;
  }
}

@media (hover: none) {
  /* No hover on touch — rather than force the caption permanently on
     (which meant a permanent dark bar over every photo), drop it
     entirely. The same number/label still shows in the lightbox once a
     photo is tapped open. */
  .media-caption {
    display: none;
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }

  .projects-scroll { height: auto; }

  .projects-pin {
    position: relative;
    height: auto;
    overflow: visible;
  }

  .project-panel,
  .project-panel:nth-child(n) {
    position: relative;
    min-height: 100svh;
    clip-path: none !important;
  }

  .prologue-image-primary img,
  .prologue-image-layer {
    transform: none !important;
  }

  .photo-card {
    opacity: 1 !important;
  }

  .depth-card {
    translate: 0 0 !important;
  }
}
