:root {
  --bg: #ffffff;
  --fg: #000000;
  --muted: #767676;
  --line: #000000;
  --accent: #2b3bff;
  --font-serif: "Times New Roman", Times, "Liberation Serif", serif;
  /* Sans = reading / information layer on work pages (statement, body,
     process text, captions, credits). Neue Haas Grotesk is licensed and
     not yet self-hosted — until its web-font files are added via
     @font-face, this falls back to Helvetica Neue, which is a very close
     stand-in for NHG. */
  --font-sans: "Neue Haas Grotesk Text Pro", "Neue Haas Grotesk Text", "NHaasGroteskTXPro", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --content-width: 80%;
  --content-max-width: 1760px;
  /* viewport-edge margin for the full-bleed plate grid + the opening
     text blocks that align to it (work pages) */
  --edge-margin: 16px;
  --ambient-size: clamp(368px, 39vw, 736px);
  --ambient-parallax: 0px;
}

* {
  box-sizing: border-box;
}

html, body {
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-serif);
}

a {
  color: inherit;
  text-decoration: none;
  transition: color 0.15s ease;
}

a:hover {
  color: var(--accent);
}

/* ---------- Page transition ---------- */
/* Fade-in on load, applied only to .page-fade-in (Archive, About,
   Contact) — arriving at one of these three, whether from the
   homepage or from one another, gets a brief fade instead of content
   popping in instantly. The homepage itself never carries this class,
   so navigating back to it is always instant, as intended. */

@keyframes page-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.page-fade-in {
  animation: page-fade-in 300ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .page-fade-in {
    animation: none;
  }
}

/* ---------- Homepage -> work page transition ----------
   A very light two-phase fade, opacity only, no movement.
   Phase 1 (here): leaving the homepage. script.js adds body.is-leaving
   on a click to a works/ link and delays the navigation ~160ms. The
   ambient circle goes first and a little faster; the header, list and
   footer follow a beat behind.
   Phase 2: the work page fades its hero in on arrival (further down,
   ".work-hero-photo" / work-page ".site-header"). */
@media (prefers-reduced-motion: no-preference) {
  .site-header,
  .page-content,
  .site-footer {
    transition: opacity 140ms ease-out 10ms;
  }

  body.is-leaving .site-header,
  body.is-leaving .page-content,
  body.is-leaving .site-footer {
    opacity: 0;
  }

  /* scoped so it overrides .ambient-media's own 0.6s expand-fade only
     while leaving — circle leads: no delay, shorter duration */
  body.is-leaving .ambient-media,
  body.is-leaving .ambient-ring {
    opacity: 0;
    transition: opacity 90ms ease-out;
  }
}

/* ---------- Ambient background media layer ---------- */
/* Fixed, out of document flow, does not affect layout or scroll.
   z-index: -1 pins both layers behind all normal-flow content
   (header, intro, work list) regardless of DOM order — a positioned
   element with no z-index actually paints ABOVE static in-flow
   siblings by default, so without this the video/ring would sit in
   front of and clip through text wherever they overlap.
   --ambient-parallax is written by script.js on scroll (a fraction of
   scroll distance) so the media + ring drift slightly instead of
   staying perfectly glued to the viewport centre; see .ambient-ring
   below for the matching half. */

.ambient-media {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, calc(-50% + var(--ambient-parallax)));
  width: var(--ambient-size);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.6s ease;
  z-index: -1;
}

.ambient-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

body.has-expanded-work .ambient-media {
  opacity: 0;
}

/* Ring sits on the same footprint as .ambient-media — a separate
   element only so it stays a true circle (.ambient-media itself stays
   a plain square box so it can clip the video with overflow:hidden;
   rounding it directly would crop the footage into a circle, which
   isn't wanted). It's independent of the video's own baked-in opening
   fade — full opacity from first paint, no fade-in of its own — but
   otherwise tracks .ambient-media exactly, fading out together with
   it on work-expand. */

.ambient-ring {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, calc(-50% + var(--ambient-parallax)));
  width: var(--ambient-size);
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  border: 1px solid var(--accent);
  box-sizing: border-box;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.6s ease;
  z-index: -1;
}

body.has-expanded-work .ambient-ring {
  opacity: 0;
}

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

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 32px 48px;
}

.site-name {
  font-size: 21px;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.site-nav {
  display: flex;
  gap: 44px;
  font-size: 17px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ---------- Page content wrapper ---------- */

.page-content {
  width: var(--content-width);
  max-width: var(--content-max-width);
  margin: 0 auto;
}

/* ---------- Intro ---------- */

.intro {
  margin: 64px 0 120px;
  width: clamp(380px, 30vw, 640px);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.6;
}

/* Invisible float purely so the paragraph's right edge follows a
   curve (wide at the top, narrowing lower down) instead of a straight
   rectangle — echoes the petri-dish circle sitting beside it. Sized
   by eye against that circle's on-screen position, not computed from
   it, since the circle is viewport-fixed and this is in normal flow;
   revisit if the circle's size/position changes materially. */

.intro-shape {
  float: right;
  width: 280px;
  height: 220px;
  shape-outside: circle(220px at 100% 100%);
}

/* ---------- Work list ---------- */

.work-list {
  clear: both;
  padding-bottom: 140px;
}

.work-row-summary {
  display: grid;
  grid-template-columns: 80px 1fr 120px;
  align-items: baseline;
  gap: 24px;
  padding: 40px 0;
  cursor: pointer;
  user-select: none;
  color: var(--fg);
  transition: color 0.15s ease;
}

.work-row-summary:hover {
  color: var(--accent);
}

.work-index,
.work-title,
.work-year {
  font-size: 30px;
  font-weight: 400;
}

.work-year {
  text-align: right;
}

/* ---------- Expanded detail ---------- */

.work-row-detail {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.work-row-detail-inner {
  display: grid;
  grid-template-columns: 40% 1fr;
  gap: 56px;
  padding: 4px 0 56px;
  align-items: stretch;
}

.work-description {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  max-width: 46em;
}

.work-statement {
  margin: 0 0 24px;
  font-size: 21px;
  line-height: 1.7;
  text-align: left;
}

.view-link {
  font-size: 17px;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Homepage-only: plain text link, no underline, always accent-coloured
   rather than colour-on-hover. Scoped to .work-description so the
   shared .view-link styling (underlined, default colour) is untouched
   elsewhere, e.g. the "Full record — Archive →" link on the finalized
   About page. */

.work-description .view-link {
  text-decoration: none;
  color: var(--accent);
}

/* One shared height across every work row's image area — regardless
   of how many photos it holds or what orientation they are — so the
   expanded module's overall height and its VIEW PROJECT baseline stay
   consistent from row to row instead of jumping around with each
   photo's own proportions. Height is fixed (Bactereature's original
   clamp is the reference); each photo's width is whatever its own
   aspect ratio produces at that height — never stretched, never
   cropped, the full frame is always visible. */

.work-images {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  width: 84%;
  height: clamp(220px, 17vw, 340px);
}

.work-image-photo {
  display: block;
  width: auto;
  height: 100%;
  flex-shrink: 0;
}

.placeholder-image {
  position: relative;
  aspect-ratio: 3 / 4;
  border: 1px solid var(--line);
  background-image: linear-gradient(to top right, transparent calc(50% - 0.5px), var(--line) calc(50% - 0.5px), var(--line) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}

.placeholder-image::after {
  content: "Placeholder image";
  position: absolute;
  left: 8px;
  bottom: 8px;
  font-size: 11px;
  background: var(--bg);
  padding: 1px 4px;
}

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

.site-footer {
  margin-top: auto;
  border-top: 0.5px solid var(--line);
  padding: 28px 48px;
}

.site-footer-inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.site-footer-name,
.site-footer-location,
.site-footer-copyright {
  margin: 0;
  font-size: 13px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--fg);
}

/* ---------- Work detail page ---------- */

.work-hero {
  position: relative;
  width: 100%;
  height: 100vh;
  flex-shrink: 0;
  border-bottom: 1px solid var(--line);
  background-image: linear-gradient(to top right, transparent calc(50% - 0.5px), var(--line) calc(50% - 0.5px), var(--line) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}

.work-hero::after {
  content: "Placeholder image";
  position: absolute;
  left: 24px;
  bottom: 24px;
  font-size: 13px;
  background: var(--bg);
  padding: 2px 6px;
}

/* Real hero photo: drop the placeholder cross + label, let the image
   fill the full-bleed frame. Pages still on the template keep the
   placeholder treatment above. */
.work-hero-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Phase 2 of the homepage -> work page transition: on arrival the hero
   fades up from 0, the overlaid header a beat later. Opacity only.
   `both` fill keeps each element at opacity 0 until its turn (no flash
   at full opacity first). */
@media (prefers-reduced-motion: no-preference) {
  @keyframes work-enter-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  .work-hero-photo {
    animation: work-enter-fade 240ms ease-out both;
  }

  body:has(.work-hero) .site-header {
    animation: work-enter-fade 240ms ease-out 40ms both;
  }
}

.work-hero:has(.work-hero-photo) {
  background-image: none;
  border-bottom: none;
}

.work-hero:has(.work-hero-photo)::after {
  display: none;
}

/* Work-detail pages only: the shared header stops being an 88px white
   band above the hero and instead floats over it so the hero reads
   full-bleed from the very top of the page. Scoped via
   body:has(.work-hero) so index/archive/about/contact keep the normal
   in-flow header. Static (not fixed) — it scrolls away with the hero,
   same as elsewhere on the site. Header text colour is decided
   per-page (see below), since it depends on how light/dark that page's
   hero is at the top. */
body:has(.work-hero) {
  position: relative;
}

body:has(.work-hero) .site-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
}

/* Default: reversed-out white, for pages with a dark hero (e.g.
   Halo-Tectonic). Anchors are targeted directly, including
   :link / :visited, because the sitewide `a { color: inherit }`
   doesn't override the UA link colour for visited links
   (index/archive/about/contact are all in history), so inheriting the
   reversed white from .site-header isn't enough. */
body:has(.work-hero) .site-header,
body:has(.work-hero) .site-header a:link,
body:has(.work-hero) .site-header a:visited {
  color: #ffffff;
}

/* Per-page opt-out: pages whose hero is light at the top add
   class="hero-light" on <body> and get the normal black header back
   (still overlaid on the full-bleed hero). e.g. Bactereature. */
body.hero-light:has(.work-hero) .site-header,
body.hero-light:has(.work-hero) .site-header a:link,
body.hero-light:has(.work-hero) .site-header a:visited {
  color: var(--fg);
}

/* Restore the sitewide accent hover on the overlaid header — the
   :link/:visited rules above outrank the generic `a:hover`, so it has
   to be re-stated at this scope (both selectors, so it also wins on
   hero-light pages). */
body:has(.work-hero) .site-header a:hover,
body.hero-light:has(.work-hero) .site-header a:hover {
  color: var(--accent);
}

/* Per-page: portrait / tall hero images that a full-height cover would
   slice through the middle. Show the whole image, full viewport
   height, centred, with the page background filling the sides. Works
   cleanly when the source has a plain light studio background that
   blends into var(--bg). e.g. Blooming Fold. */
body.hero-contain .work-hero {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
}

body.hero-contain .work-hero-photo {
  width: auto;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}

.work-detail {
  /* the hero caption sits just under the (100vh) hero */
  padding-top: 24px;
}

/* Project name — moved out of the body and set as a caption directly
   under the hero image, in the shared caption style. No year. Sits at
   the LEFT, on the plate grid's left edge (same reference as the plate
   captions and the prose label). */
.hero-caption {
  width: calc(100vw - var(--edge-margin) * 2);
  max-width: 1824px;
  margin: 0 0 72px 50%;
  transform: translateX(-50%);
}

/* Two-tier opening hierarchy below the hero caption:
     1. the sans-serif ITALIC core statement (20px, tight leading, wide)
     2. slightly smaller but comfortably readable sans-serif body
        (19px / ~1.48), never bold or italic
   Both blocks break out to the plate grid's width and right-align
   their content to the grid's right edge (same breakout mechanic as
   .work-plates — no overflow). */
.work-title-block {
  margin: 0 0 0 50%;
  width: calc(100vw - var(--edge-margin) * 2);
  max-width: 1824px;
  transform: translateX(-50%);
  display: flex;
  justify-content: flex-end;
}

/* 2. Short statement — sans, italic; the visual peak of the opening.
   Wide measure so it holds ~3–4 lines as a proposition, not body. */
.work-title-statement {
  margin: 0;
  max-width: 720px;
  font-family: var(--font-sans);
  font-size: 17px;
  font-style: italic;
  line-height: 1.4;
  color: var(--fg);
}

/* 3. Main project introduction — sans, regular, near normal reading
   size (a step under the statement); leading kept moderate despite the
   larger size. Breaks out to the plate-grid width; the two columns are
   right-aligned as a group so their RIGHT edge stays locked to the grid
   edge while the group grows leftward. Asymmetric 55/45 — wider left
   column for the longer background narrative, tighter right column for
   the more focused stance — with the gutter held near its old value so
   lines don't get short. */
.work-statement-columns {
  display: grid;
  grid-template-columns: minmax(0, 600px) minmax(0, 490px);
  column-gap: 52px;
  row-gap: 0;
  align-items: start;
  justify-content: end;
  width: calc(100vw - var(--edge-margin) * 2);
  max-width: 1824px;
  /* statement -> two-column: a wide, clear section break */
  margin: 128px 0 0 50%;
  transform: translateX(-50%);
}

.work-statement-columns p,
.work-prose-body p,
.work-credits-body p {
  margin: 0 0 20px;
  font-family: var(--font-sans);
  font-size: 20px;
  line-height: 1.5;
}

.work-statement-columns p:last-child,
.work-prose-body p:last-child {
  margin-bottom: 0;
}

/* Opening statement with a left label — the same label-column idea as
   the prose sections, so the intro reads as its own "Concept" chapter.
   Only engages when the label is present (bactereature); the other work
   pages keep the plain right-locked two-column block above. The body
   wrapper keeps the intro's own 600/490 measure, so only the previously
   empty left margin is filled — the two paragraphs don't reflow. */
.work-statement-columns:has(.work-section-label) {
  grid-template-columns: 1fr auto;
  column-gap: 48px;
}

.work-statement-columns-body {
  display: grid;
  grid-template-columns: minmax(0, 600px) minmax(0, 490px);
  column-gap: 52px;
  align-items: start;
}

/* Credit lines: same face and size as the body, but no paragraph gap
   and tighter leading — each field is just a normal wrapped line, not
   a spaced-out paragraph. */
.work-credits-body p {
  margin-bottom: 0;
  line-height: 1.4;
}

/* ---------- Plate grid (reference: oxman.com/projects/aguahoja) ----------
   A modular column grid for the image bands on a work page. Every row
   (`.plate-row`) is a CSS grid of equal columns — 1-up / 2-up / 3-up
   via `.cols-N`. Two constants govern the whole system regardless of
   what goes in it:
     --plate-gutter   : horizontal gap between columns in a row
     --plate-row-gap  : vertical gap between one row and the next
   Images keep their own aspect ratio (no forced crop); `align-items:
   start` tops-aligns unequal-height images in a row. So a big single
   image and a 3-up strip both sit on the same grid and the same
   vertical rhythm. Broken out of the text measure to a 48px viewport
   margin. Captions sit directly under each image, uppercase + tracked.
   Bactereature uses this; the other work pages still use
   .work-image-full / .work-gallery below. */
.work-plates {
  --plate-gutter: 12px;
  --plate-row-gap: 100px;
  width: calc(100vw - var(--edge-margin) * 2);
  max-width: 1824px;
  /* 50% + translateX(-50%) centres a child wider than .page-content
     on the viewport; margin-top gives columns -> first plate ≈ 120px */
  margin: 120px 0 0 50%;
  transform: translateX(-50%);
}

.plate-row {
  display: grid;
  gap: var(--plate-gutter);
  align-items: start;
}

.plate-row + .plate-row {
  margin-top: var(--plate-row-gap);
}

.plate-row.cols-1 { grid-template-columns: 1fr; }
.plate-row.cols-2 { grid-template-columns: 1fr 1fr; }
.plate-row.cols-3 { grid-template-columns: 1fr 1fr 1fr; }

.plate {
  margin: 0;
}

/* Real images: natural ratio, fill the column width. A silent looping
   <video> (autoplay muted loop playsinline, no controls) is treated the
   same as an image; pointer-events:none keeps it fully inert (no
   right-click / context menu). */
.plate img,
.plate video {
  width: 100%;
  height: auto;
  display: block;
}

.plate video {
  pointer-events: none;
}

/* Per-plate top crop for a media element whose source has dead space at
   the top (e.g. the staining-loop video). Clip ~10% off the top by
   holding a shorter box and anchoring the media to its bottom edge. */
.plate-crop-top img,
.plate-crop-top video {
  aspect-ratio: 1920 / 972;   /* 1080 -> 972 = top 10% removed */
  height: auto;
  object-fit: cover;
  object-position: 50% 100%;
}

/* Placeholder boxes only — give each row type a stand-in ratio so the
   framework is legible before real images land. */
.plate .placeholder-image { width: 100%; aspect-ratio: 3 / 2; }
.plate-row.cols-1 .placeholder-image { aspect-ratio: 16 / 7; }

/* Labeled section — short left label, two-column body on the right with
   an open gutter between (reference layout). Used for BOTH the Process
   section and the closing Credits section, which share this grid, this
   label style and the body text size — Credits is the last narrative
   section, not an appended spec table. Breaks out to the plate-grid
   width so the label sits at the grid's left edge and the body reaches
   its right edge. Sections are separated by vertical space only — no
   rules, no dividers. */
.work-prose-section,
.work-credits {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 48px;
  align-items: start;
  width: calc(100vw - var(--edge-margin) * 2);
  max-width: 1824px;
  margin: 120px 0 120px 50%;
  transform: translateX(-50%);
}

.work-section-label {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.3;
}

.work-prose-body,
.work-credits-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: start;
}

/* Credit lines are ordinary body text — field name and value the same
   weight as the body, running inline. Not a table. (.credit-field is
   kept as a structural hook only, no visual difference.) */

.work-gallery {
  margin: 120px 0 96px;
}

.work-section-image {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}

.work-section-image.two-up {
  grid-template-columns: 1fr 1fr;
}

/* Captions — sans, uppercase, tracked, regular weight; never italic.
   Same style used for the hero caption (project name). Margins are set
   per use (.image-caption sits under its image; .hero-caption spacing
   is defined up with the opening block). */
.image-caption,
.hero-caption {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.04em;
  line-height: 1.3;
  text-transform: uppercase;
  color: var(--fg);
}

.image-caption {
  margin: 10px 0 0;
}

/* ---------- Work-detail: tightened editorial density + wider grid ----------
   Opt-in via <body class="prose-tight">. Bactereature only for now;
   the other five work pages adopt the class later. Goal: smaller,
   tighter, quieter sans text + a wider, more open editorial grid, so
   the images carry the page. Column *proportions* and the shared
   left/right breakout edges are unchanged — every module (full-width
   image, 2-col, 3-col, section text, Credits) still shares one set of
   edges; they just sit wider, with a clearer gutter, and each image
   grows by its own column count. */
.prose-tight {
  --edge-margin: 12px;            /* was 16 — less outer whitespace */

  /* Chapter rhythm. A media band (.work-plates + its caption) and the
     .work-prose-section directly below it are ONE chapter unit. These
     two tokens set every gap on the page — tight inside a unit, open
     between units — so nothing is hand-tuned per chapter. */
  --chapter-inner-gap: 92px;      /* media/caption -> its own section text (was ~100) */
  --chapter-outer-gap: 148px;     /* end of one chapter -> start of the next (was ~120, then 132) */
}

.prose-tight .work-plates {
  --plate-gutter: 20px;           /* was 12 — clearer gap between images */
}

/* One wider ceiling shared by all breakout modules (keep margin-left:
   50% + the translateX(-50%) mechanic from the base rules). */
.prose-tight .hero-caption,
.prose-tight .work-title-block,
.prose-tight .work-statement-columns,
.prose-tight .work-plates,
.prose-tight .work-prose-section,
.prose-tight .work-credits {
  max-width: 2000px;             /* was 1824 */
}

.prose-tight .work-statement-columns p,
.prose-tight .work-prose-body p,
.prose-tight .work-credits-body p {
  font-size: 16px;
  line-height: 1.4;
  margin-bottom: 14px;
}

.prose-tight .work-credits-body p {
  line-height: 1.35;
}

/* Section labels stay one notch above the body: barely larger, but
   heavier. Not enlarged. */
.prose-tight .work-section-label {
  font-size: 17px;
  font-weight: 600;
  line-height: 1.35;
}

/* Vertical spacing. Keep margin-left: 50% + the transform from the base
   rules — only top/bottom move here. */
.prose-tight .work-statement-columns {
  margin-top: 108px;             /* base 128 — title -> intro, not chapter rhythm */
}

.prose-tight .hero-caption {
  margin-bottom: 64px;           /* base 72 */
}

/* Chapter rhythm, driven entirely by the two --chapter-*-gap tokens.
   Trailing margins are zeroed so the NEXT block's leading margin is the
   whole gap (no margin-collapse ambiguity between the two values):
     .work-plates   top = outer  -> a new chapter starts
     .work-prose    top = inner  -> the text belongs to the media above
     .work-credits  top = outer  -> closing block reads as its own unit */
.prose-tight .work-plates {
  margin-top: var(--chapter-outer-gap);
  margin-bottom: 0;
}

.prose-tight .work-prose-section {
  margin-top: var(--chapter-inner-gap);
  margin-bottom: 0;
}

.prose-tight .work-credits {
  margin-top: var(--chapter-outer-gap);
  margin-bottom: 100px;         /* space above the footer — not part of the rhythm */
}

/* Image captions: one to two levels under the body, sentence case,
   regular weight, tight leading, NOT uppercase. (.hero-caption keeps
   the shared caps treatment — this only retargets .image-caption.) */
.prose-tight .image-caption {
  font-size: 13px;
  letter-spacing: 0;
  line-height: 1.35;
  text-transform: none;
}

/* two-column intro -> first image ≈ 120px */
.work-image-full {
  margin: 120px 0 0;
}

.work-image-full .placeholder-image {
  aspect-ratio: 16 / 8;
}

/* ---------- Archive page ---------- */

.archive-content {
  padding: 48px 0 64px;
}

.archive-content.archive-content-narrow {
  max-width: 800px;
}

.archive-title {
  margin: 0 0 12px;
  font-size: 48px;
  font-weight: 400;
  line-height: 1.1;
}

.archive-intro {
  margin: 0 0 72px;
  font-size: 18px;
  line-height: 1.5;
}

/* Editorial two-column archive layout (label column + entries column) */

.archive-editorial {
  width: 64%;
  max-width: 1160px;
  min-width: 640px;
  margin: 0 auto;
  padding: 56px 0 96px;
}

.archive-section {
  margin-top: 96px;
  padding-top: 72px;
  border-top: 0.5px solid #999999;
}

.archive-section:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

.archive-section-grid {
  display: grid;
  grid-template-columns: 21% 1fr;
  gap: 46px;
  align-items: start;
}

.archive-section-label {
  margin: 0;
  font-size: 18px;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--fg);
}

.archive-entries {
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.archive-entry-title {
  margin: 0;
  font-size: 19px;
  line-height: 1.4;
  color: var(--fg);
}

.archive-location {
  margin: 6px 0 0;
  font-size: 15px;
  line-height: 1.4;
  color: #4d4d4d;
}

.archive-date {
  margin: 2px 0 0;
  font-size: 15px;
  line-height: 1.4;
  color: #4d4d4d;
}

/* --- .archive-rows: the horizontal-row section format ---
   Used by #exhibitions and #publications. Each entry is one row:
   title | detail (institution / publication) | year | thumbnail, on a
   shared set of column widths and a shared baseline. Awards /
   Collections keep the three-line serif stack above. No rules, bullets
   or background. */
/* A .archive-rows section runs on its own centred breakout, wider than
   the rest of the Archive column, so the horizontal grid has room. The
   stacked sections stay inside .archive-editorial. */
.archive-rows {
  width: 82vw;
  max-width: 1500px;
  margin-left: 50%;
  transform: translateX(-50%);
}

/* Two-tier type system: the HEADING is the same size as the entries
   (16px) but much heavier — weight alone is the marker, not size. It
   sits ABOVE the list (not in a left-hand column, no
   .archive-section-grid wrapper), left-aligned with the grid below it
   since both are plain full-width children of the section. */
.archive-rows .archive-section-heading {
  margin: 0 0 48px;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--fg);
}

/* Exhibitions rows are tall (86px thumbnail) so they need the 56px gap;
   Publications is text-only and reads better ~30% tighter. */
.archive-rows .archive-entries {
  gap: 56px;
}

#publications .archive-entries {
  gap: 40px;
}

/* Two-zone horizontal structure: the three text columns (title /
   institution&location / year) are one continuous info group clustered
   at the LEFT — each a capped width, so extra row width never spreads
   them out. Title→detail gap is 32px; detail→year is a wider 64px so
   the year reads as separate metadata, not a third text column. All
   slack pools into track 6, the flexible gap right before the
   thumbnail, so the image column sits alone against the right edge
   with a wide void between it and the year. Explicit gap tracks
   (column-gap: 0) because CSS grid can't vary the gap per column. */
.archive-rows .archive-row {
  display: grid;
  grid-template-columns:
    minmax(0, 540px)   /* 1  title (capped, no longer flexes with width) */
    32px               /* 2  gap */
    minmax(0, 250px)   /* 3  institution / location (capped) */
    128px              /* 4  gap (wide — sets the year well apart) */
    46px               /* 5  year */
    minmax(56px, 1fr)  /* 6  isolating gap — absorbs all extra row width */
    120px;             /* 7  thumbnail (fixed, right edge stays aligned) */
  column-gap: 0;
  align-items: baseline;
}

.archive-rows .archive-entry-title { grid-column: 1; }
.archive-rows .archive-location    { grid-column: 3; }
.archive-rows .archive-date        { grid-column: 5; }
.archive-rows .archive-thumb       { grid-column: 7; }

/* Publications & Press has no image column — same left grid (title /
   detail / year all line up with Exhibitions), just the isolating gap
   and thumbnail tracks dropped. `min-height` gives the text-only rows
   a deliberate slot height; kept ~30% under an Exhibitions row (which
   the 86px thumbnail forces) so the section reads tighter. Pitch
   60 + 40 gap = 100px vs Exhibitions' 86 + 56 = 142px. */
#publications .archive-row {
  grid-template-columns:
    minmax(0, 540px)   /* 1  title */
    32px               /* 2  gap */
    minmax(0, 250px)   /* 3  detail (type / medium / pages) */
    128px              /* 4  gap (wide — sets the year well apart) */
    46px;              /* 5  year */
  min-height: 60px;
}

/* Entries are completely uniform — title, institution/location and
   year all share one sans regular size / weight / colour. No size or
   greyscale differentiation between the three; the only hierarchy is
   column width, position and spacing (the grid above). This is what
   keeps the row database-quiet under the one heading. */
.archive-rows .archive-entry-title,
.archive-rows .archive-location,
.archive-rows .archive-date {
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--fg);
}

.archive-rows .archive-location,
.archive-rows .archive-date {
  margin: 0;
}

.archive-rows .archive-date {
  white-space: nowrap;
}

/* Rightmost column: a small archival-reference image — natural ratio,
   never stretched, shared max height. The column keeps its width
   whether or not a row has an image. Until an <img> is dropped in, the
   slot shows the site's placeholder box (thin border + diagonal). */
.archive-rows .archive-thumb {
  align-self: start;
  width: 120px;
  aspect-ratio: 7 / 5;
  border: 1px solid var(--line);
  background-image: linear-gradient(to top right, transparent calc(50% - 0.5px), var(--line) calc(50% - 0.5px), var(--line) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}

.archive-rows .archive-thumb:has(img) {
  aspect-ratio: auto;
  border: none;
  background-image: none;
}

.archive-rows .archive-thumb img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 220px;   /* allow portrait shots real height; landscape sits well under this */
  object-fit: cover;
}

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

.contact-row {
  display: flex;
  gap: 16px;
  margin-bottom: 16px;
  font-size: 19px;
}

.contact-label {
  min-width: 180px;
  color: var(--fg);
}

.contact-value {
  color: var(--fg);
}

/* ---------- About page ---------- */

.about-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 72px;
  align-items: start;
}

.about-lead {
  margin: 0;
  font-size: 21px;
  line-height: 1.7;
}

.about-section {
  margin: 0 0 56px;
}

.about-secondary .view-link {
  display: inline-block;
  margin-top: 40px;
}

.about-section-label {
  margin: 0 0 16px;
}

.about-section p {
  margin: 0 0 20px;
  font-size: 19px;
  line-height: 1.7;
}

.about-section p:last-child {
  margin-bottom: 0;
}

.about-focus-list p {
  margin: 0 0 8px;
  font-size: 17px;
  line-height: 1.5;
}

.about-focus-list p:nth-child(2) {
  margin-bottom: 28px;
}

.about-focus-list p:last-child {
  margin-bottom: 0;
}

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

@media (max-width: 900px) {
  :root {
    --content-width: 88%;
  }

  .intro {
    width: 100%;
    max-width: none;
    margin: 96px 0 80px;
  }

  .work-row-summary {
    grid-template-columns: 48px 1fr 90px;
    padding: 28px 0;
  }

  .work-index,
  .work-title,
  .work-year {
    font-size: 20px;
  }

  .work-row-detail-inner {
    grid-template-columns: 1fr;
  }

  .work-statement-columns,
  .work-statement-columns:has(.work-section-label),
  .work-statement-columns-body,
  .work-section-image.two-up,
  .work-prose-body,
  .work-credits-body {
    grid-template-columns: 1fr;
  }

  .archive-title {
    font-size: 32px;
  }

  .archive-editorial {
    width: 90%;
    min-width: 0;
  }

  .archive-section-grid {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .archive-rows {
    width: auto;
    max-width: none;
    margin-left: 0;
    transform: none;
  }

  .archive-rows .archive-row,
  #publications .archive-row {
    grid-template-columns: 1fr;
    row-gap: 4px;
    min-height: 0;
  }

  .archive-rows .archive-entry-title,
  .archive-rows .archive-location,
  .archive-rows .archive-date,
  .archive-rows .archive-thumb {
    grid-column: auto;
  }

  .contact-row {
    flex-direction: column;
    gap: 2px;
  }

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

  .contact-label {
    min-width: 0;
  }

  .site-footer {
    padding: 28px 24px;
  }
}
