/* Beneyam Hassen — one stylesheet, hand written, no framework.
   Rules that are not negotiable:
     · images are contained, never cropped to fill
     · images are large
     · nothing competes with a photograph on screen          */

:root {
  /* ── Beneyam's palette ───────────────────────────────────────────────────
     Brown is the main colour, and the way to make a colour "main" without it
     shouting is to make it the INK — it is then on every page, in every
     sentence, and never announces itself. #2B1700 measures 14.8:1 on the cream,
     so it carries body text comfortably.

     Rust is the single accent: links, plate numbers, the cursor. 6.9:1, and it
     separates cleanly from the brown so a link never looks like an accident.

     Teal is deliberately unused. It measures 8.7:1 — a fine colour — but rust
     and teal are within 1.26:1 of each other, meaning they read as the same
     weight. Two accents of equal weight look like a flag, not a decision.
     Held in reserve; see the note at the bottom of this block.               */

  --paper:     #F7F4EC;   /* warm white. the old #F9ECE5 had red 13 points above
                             green, which is what read as pink. here they are 3
                             apart and the warmth comes from dropping blue, so it
                             leans yellow — paper, not blush. */
  --ink:       #160F07;   /* your brown, darker — 17.3:1 */
  --ink-soft:  #625D55;   /* captions — 5.9:1 */
  --ink-faint: #767168;   /* labels, dates — 4.6:1. the natural tint landed at
                             3.5:1 and failed, so this is darkened on purpose. */
  --rule:      #DEDBD3;
  --accent:    #9C2113;   /* rust — 7.2:1 */
  --accent-bright: #B8402F;
  --lightbox:  #0E0A06;
  color-scheme: light;

  /* ── one column for the whole site ───────────────────────────────────────
     --col is the page column. Every structural block on every page — heading,
     dateline, list, plate list, further reading, onward, quote — begins and
     ends on these two lines. Before this there were three different widths in
     play, so a heading started in one place and the list beneath it started in
     another, and nothing on the page agreed with anything else.

     --measure is narrower, and is used ONLY to stop lines of body text getting
     too long to read. It constrains the paragraph, not the block, so prose
     still starts on the column's left edge — a shared edge, a shorter line.  */
  --col:     46em;
  --measure: 34em;
  --sheet-w: 1180px;   /* the contact sheet is wider; heading and grid share it */
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --sans:  ui-sans-serif, -apple-system, "Helvetica Neue", Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 19px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration-color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--accent); }

/* masthead — quiet. the work is the site, not the nav */
.masthead {
  display: flex; justify-content: space-between; align-items: baseline; gap: 2rem;
  padding: 1.6rem 6vw;
  font-family: var(--sans); font-size: .74rem;
  letter-spacing: .11em; text-transform: uppercase;
}
.masthead a { text-decoration: none; }
.masthead nav { display: flex; gap: 1.5rem; color: var(--ink-soft); }
.masthead nav a.on { color: var(--accent); }
.masthead .name { font-weight: 600; letter-spacing: .14em; }

/* opening frame — arrives before the title */
.opening { padding: 2vh 6vw 0; display: flex; justify-content: center; }
.opening img { display: block; max-height: 84vh; max-width: 100%; width: auto; height: auto; cursor: zoom-in; }

/* The Work page opens larger than any other page — near enough the full height
   of the screen, so the first thing a stranger meets is a photograph and not a
   layout. It has to be scrolled past, which is the point. */
.home-opening img { max-height: 92vh; }

/* ── the rotating opening ────────────────────────────────────────────────────
   Every frame is in the page; only opacity changes. The rotation gets one box,
   and every frame fits inside it.

   The first version left frame one in normal flow and stacked the rest on top
   of it, which meant the box was whatever shape the first photograph happened
   to be — a landscape lead made a short box that squashed every portrait after
   it, and on a phone, where the opening runs full-bleed with no height cap, the
   box was whatever that one file dictated. Now the height is the page's
   decision, not the first photograph's, and `contain` means nothing is ever
   cropped to fill it. */
.rotator {
  position: relative;
  height: 92vh; width: 100%;
}
.rotator .frame {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  transition: opacity 1.1s ease;
}
.rotator .frame:not(.on) { opacity: 0; pointer-events: none; }
.rotator .frame picture { display: block; width: 100%; height: 100%; }
.rotator img {
  width: 100%; height: 100%;
  max-width: 100%; max-height: 100%;
  object-fit: contain; object-position: center;
  cursor: pointer;                       /* click advances, it does not zoom */
}

/* A full-bleed invisible button over the photograph, so a click anywhere moves
   on and a keyboard can reach it. It carries no visual weight at all. */
.rot-next {
  position: absolute; inset: 0; width: 100%; height: 100%;
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer; z-index: 2;
}
.rot-next:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }

/* Motion off: no rotation, no fade. The first photograph stays and the button
   still works, so the other frames remain reachable by choice. */
@media (prefers-reduced-motion: reduce) {
  .rotator .frame { transition: none; }
}

/* title, after the photograph */
.title-block { max-width: var(--col); margin: 5.5rem auto 0; padding: 0 6vw; }
.title-block h1 {
  font-size: clamp(1.9rem, 4.4vw, 2.9rem); line-height: 1.16;
  font-weight: 500; margin: 0 0 1.1rem; letter-spacing: -.012em;
}
.dateline {
  font-family: var(--sans); font-size: .72rem; letter-spacing: .13em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* prose — chunks, never walls */
.prose { max-width: var(--col); margin: 3.2rem auto; padding: 0 6vw; }
.prose p { margin: 0 0 1.35em; max-width: var(--measure); }
.prose p:last-child { margin-bottom: 0; }
.lede { font-size: 1.14em; line-height: 1.55; }

.section-mark {
  max-width: var(--col); margin: 6.5rem auto 3.2rem; padding: 0 6vw;
  font-family: var(--sans); font-size: .72rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint);
  display: flex; align-items: center; gap: 1.1rem;
}
.section-mark::after { content: ""; flex: 1; height: 1px; background: var(--rule); }

/* plates — variable scale is the rhythm */
figure { margin: 0 auto; padding: 0 6vw; display: flex; flex-direction: column; align-items: flex-start; }
figure + figure { margin-top: 5.5rem; }
figure img { display: block; max-width: 100%; width: auto; height: auto; align-self: center; cursor: zoom-in; }

.plate-full   img { max-height: 88vh; }
.plate-large  img { max-height: 72vh; }
.plate-medium img { max-height: 52vh; }
.plate-small  img { max-height: 34vh; }

/* film — for the performance work, where the video is the piece and the
   photographs are documentation of it. sits where a plate would sit. */
figure.film { margin-top: 0; }
figure.film video {
  display: block; width: 100%; max-width: 1400px; max-height: 84vh;
  align-self: center; background: #0d0d0f;
}

figcaption {
  margin-top: .85rem; font-family: var(--sans); font-size: .76rem;
  line-height: 1.5; color: var(--ink-faint); max-width: 30em;
}
figcaption .n { color: var(--accent); margin-right: .5em; font-variant-numeric: tabular-nums; }

/* home index — a list that leads, not a grid that offers */
.home-index { max-width: var(--col); margin: 6rem auto 0; padding: 0 6vw; }
.home-index .entry {
  display: flex; justify-content: space-between; align-items: baseline; gap: 2rem;
  padding: 1.15rem 0; border-bottom: 1px solid var(--rule); text-decoration: none;
}
.home-index .entry:first-child { border-top: 1px solid var(--rule); }
.home-index .t { font-size: 1.15rem; }
.home-index .d {
  font-family: var(--sans); font-size: .72rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-faint); white-space: nowrap;
}
.home-index .entry:hover .t { color: var(--accent); }
.home-index .entry.static { cursor: default; }
.home-index .sub {
  display: block; font-family: var(--sans); font-size: .74rem;
  color: var(--ink-faint); margin-top: .25rem; line-height: 1.45;
}

/* plate list — full information, out of the pictures' way */
.plate-list {
  max-width: var(--col); margin: 8rem auto 0; padding: 3rem 6vw 0;
  border-top: 1px solid var(--rule); font-family: var(--sans);
}
.plate-list h2 {
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 500; margin: 0 0 2rem;
}
.plate-list ol { list-style: none; margin: 0; padding: 0; }
.plate-list li {
  display: grid; grid-template-columns: 2.6rem 1fr; gap: 1rem;
  padding: .85rem 0; border-bottom: 1px solid var(--rule);
  font-size: .82rem; line-height: 1.55; color: var(--ink-soft);
}
.plate-list .num { color: var(--accent); font-variant-numeric: tabular-nums; }
.plate-list .who { color: var(--ink); }
.plate-list .meta { color: var(--ink-faint); }
.note { margin-top: 2rem; font-size: .78rem; color: var(--ink-faint); font-style: italic; }

/* clicking the email copies it too, and says so once, quietly.
   Small enough that it reads as a receipt, not an announcement. */
.copied {
  margin-left: .55rem;
  font-family: var(--sans); font-size: .66rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-faint);
  opacity: 0; transition: opacity .3s ease;
}
.copied.show { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .copied { transition: none; } }

/* ── the letter ──────────────────────────────────────────────────────────────
   One button, and the address written out underneath it. The button is a link,
   not a form control, because there is no form — nothing to submit, nothing to
   go wrong, nothing to maintain. Sized generously for a thumb. */
.join { margin-top: 2.6rem; }
.join-row {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: .7rem 1rem;
}
.join-lbl {
  font-family: var(--sans); font-size: .72rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink-faint);
}
.join-why {
  font-family: var(--sans); font-size: .88rem;
  color: var(--ink); background: transparent;
  border: 0; border-bottom: 1px solid var(--ink-soft);
  padding: .45rem 1.6rem .45rem .1rem;
  border-radius: 0; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  /* the chevron, drawn rather than imported, so it follows the theme */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 8px center, right 3px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.join-why:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.join-why option { color: var(--ink); background: var(--paper); }

.join-btn {
  display: inline-block;
  font-family: var(--sans); font-size: .78rem;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--paper); background: var(--ink);
  padding: .95rem 1.9rem; border-radius: 2px;
  text-decoration: none;
  transition: background .25s ease;
}
.join-btn:hover, .join-btn:focus-visible { background: var(--accent); }
.join-addr {
  font-family: var(--sans); font-size: .78rem;
  color: var(--ink-faint); margin-top: 1rem;
}
.join-addr a { color: var(--ink-soft); }

/* phone: the menu and the button each take the full width, stacked */
@media (max-width: 560px) {
  .join-row { flex-direction: column; align-items: flex-start; gap: .5rem; }
  .join-why { width: 100%; }
  .join-btn { width: 100%; text-align: center; }
}

/* ── contact sheet ───────────────────────────────────────────────────────────
   The record layer, shown plainly. Every cell is the same size and every
   photograph sits inside its cell at its own proportions — nothing is cropped
   to make a tidy grid, because cropping to fit would be an edit, and an edit is
   exactly what this layer is not. No captions, no varying scale, no argument.
   A project persuades; a contact sheet only testifies.                       */
/* One column for the whole page. The heading, the dateline, the note and the
   grid all begin and end on the same two lines — previously the text sat in the
   narrow reading measure while the grid ran wider, so the page had two competing
   left edges and nothing lined up with anything. */
.sheet-page .title-block { max-width: var(--sheet-w); }
.sheet {
  display: grid;
  /* A fixed number of columns, not auto-fill. Auto-fill picks a count from the
     available width, so the same sheet is four across on one laptop and five on
     another and the rows never feel deliberate. Fixed counts make every row the
     same, which is the whole point of a contact sheet. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  max-width: var(--sheet-w);
  margin: 3.5rem auto 0;
  padding: 0 6vw;
}
@media (min-width: 1500px) { .sheet { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
@media (max-width: 1000px) { .sheet { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Square cells, filled. The first version held each photograph inside its cell
   at its own proportions — honest, but a portrait frame then forced its row
   taller than the rest and the grid came apart. A contact sheet's whole job is
   to be even, so the cells crop. Nothing is lost: the full frame, uncropped, is
   one click away in the lightbox. */
.sheet .cell {
  aspect-ratio: 1 / 1;
  min-width: 0; min-height: 0;
  overflow: hidden;
  background: var(--rule);
}
.sheet .cell picture { display: block; width: 100%; height: 100%; }
.sheet .cell img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  display: block; cursor: zoom-in;
  transition: opacity .25s ease;
}
.sheet .cell img:hover { opacity: .84; }

.sheet-note {
  font-size: .84rem; color: var(--ink-soft);
  max-width: 34em; margin: 1.1rem 0 0;
}

@media (max-width: 760px) {
  .sheet {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 5px; margin-top: 2.2rem; padding: 0 4vw;
  }
  .sheet-page .title-block { padding: 0 4vw; }
}

/* ── archive rows that lead somewhere ────────────────────────────────────────
   Most of the archive is text, and stays text. An entry that has been worked
   into a sequence or a sheet becomes a link and shows one frame on hover.

   Two rules make it behave. It is a fixed square and the photograph is cropped
   to fill it, so a portrait and a panorama read as the same object rather than
   as two different-shaped scraps. And at 46px it is shorter than the shortest
   row (padding 1.15rem twice plus a 1.15rem line is about 65px), so centred
   vertically it cannot reach the rule above or below.

   Note: no `overflow: hidden` on the row. The accent underline that draws
   itself on hover sits at bottom:-1px, outside the box, and clipping the row
   would erase it.                                                           */
.home-index .entry.lead { position: relative; }

/* The whole row is clickable, but only the title is a link. `.stretch::after`
   spreads that one link across the row, which keeps the markup legal — a note
   link can then sit inside the row without being an anchor inside an anchor.
   The note link is lifted above the overlay so it stays separately clickable. */
.home-index .entry.lead .stretch { text-decoration: none; color: inherit; }
.home-index .entry.lead .stretch::after { content: ""; position: absolute; inset: 0; }
.home-index .entry.lead .notes-on a { position: relative; z-index: 1; }
.home-index .entry.lead:hover .t,
.home-index .entry.lead:focus-within .t { color: var(--accent); }

.home-index .entry.lead .peek {
  position: absolute; right: 5rem; top: 50%;
  width: 46px; height: 46px;
  margin-top: -23px;
  display: block; overflow: hidden;
  background: var(--rule);
  opacity: 0; transition: opacity .24s ease;
  pointer-events: none;
}
.home-index .entry.lead .peek img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  display: block;
}
/* Hover only, deliberately. This also matched :focus-visible, which looked
   right until you clicked a row and pressed back — the browser restores focus
   to the link you left from, so that one thumbnail stayed open for good while
   every other row behaved. Keyboard users still get the accent title and the
   underline, which is signal enough. */
.home-index .entry.lead:hover .peek { opacity: 1; }

/* A phone has no hover and no room, so the thumbnail simply does not exist. */
@media (max-width: 760px), (hover: none) {
  .home-index .entry.lead .peek { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .home-index .entry.lead .peek { transition: none; }
}

.further {
  max-width: var(--col); margin: 3.5rem auto 0; padding: 0 6vw;
  font-family: var(--sans); font-size: .82rem; color: var(--ink-soft);
}

footer {
  margin-top: 8rem; padding: 2.5rem 6vw 4rem; border-top: 1px solid var(--rule);
  font-family: var(--sans); font-size: .72rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-faint);
  display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap;
}
footer a { text-decoration: none; }

/* lightbox — still contained, still never cropped */
#lightbox {
  position: fixed; inset: 0; background: #0d0d0f; display: none;
  align-items: center; justify-content: center; padding: 2vh 2vw;
  cursor: zoom-out; z-index: 100;
}
#lightbox.open { display: flex; }
#lightbox img { max-width: 100%; max-height: 96vh; width: auto; height: auto; object-fit: contain; }

@media (max-width: 700px) {
  body { font-size: 17px; }
  .masthead { padding: 1.1rem 6vw; font-size: .68rem; }
  .masthead nav { gap: 1rem; }
  figure + figure { margin-top: 3.5rem; }
  .plate-full img, .plate-large img { max-height: 70vh; }
  .home-index .entry { flex-direction: column; gap: .3rem; }
}

/* desktop-only accent cursor. absent on the phone, which is most visitors */
@media (hover: hover) and (pointer: fine) {
  #dot {
    position: fixed; top: 0; left: 0; width: 26px; height: 26px; margin: -13px 0 0 -13px;
    border-radius: 50%; background: var(--accent);
    opacity: 0;                        /* invisible until it knows where you are */
    pointer-events: none; z-index: 200;
    transition: opacity .22s ease;
  }
  #dot.ready { opacity: .34; }
  body.zooming #dot.ready { opacity: .5; }
  #dot {
  }
  body.zooming #dot { opacity: .5; width: 46px; height: 46px; margin: -23px 0 0 -23px;
                      transition: opacity .16s ease, width .22s ease, height .22s ease, margin .22s ease; }
}
@media not all and (hover: hover) { #dot { display: none; } }



/* pair — two frames at equal height, the comparison IS the argument */
figure.pair .two { display: flex; gap: 1.2rem; align-items: flex-start; justify-content: center; width: 100%; }
figure.pair .two img { max-width: calc(50% - .6rem); width: auto; }
figure.pair.plate-full  .two img { max-height: 76vh; }
figure.pair.plate-large .two img { max-height: 62vh; }
@media (max-width: 700px) {
  figure.pair .two { flex-direction: column; align-items: center; gap: .8rem; }
  figure.pair .two img { max-width: 100%; max-height: 52vh; }
}

/* the reveal — where a project withholds its year until the end */
.reveal {
  margin-top: 2rem; font-size: 1rem; color: var(--ink);
  font-family: var(--serif); letter-spacing: 0;
}


/* ────────────────────────────────────────────────────────────────────────────
   MOTION

   One rule governs all of it: things move on ARRIVAL and DEPARTURE, never
   while you are looking. A photograph that is on screen is completely still.
   Nothing animates in a viewer's peripheral vision while they are reading a
   picture — that is the difference between motion and fidgeting.
   ──────────────────────────────────────────────────────────────────────── */

/* page-to-page cross-fade. no framework, no router — the browser does it.
   silently ignored by browsers that don't support it. */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation: fade-out .28s ease both; }
::view-transition-new(root) { animation: fade-in  .42s ease both; }
@keyframes fade-out { to   { opacity: 0; } }
@keyframes fade-in  { from { opacity: 0; } }

/* the opening frame arrives, once, on load */
.opening img {
  animation: opening-in 1.1s cubic-bezier(.16,.84,.44,1) both;
}
@keyframes opening-in {
  from { opacity: 0; transform: scale(1.012); }
  to   { opacity: 1; transform: none; }
}

/* plates rise into place as you reach them.
   default state is VISIBLE — js adds .will-reveal, so a failed script or a
   blocked file leaves every photograph on the page. */
.will-reveal { opacity: 0; transform: translateY(16px); }
.will-reveal.revealed {
  opacity: 1; transform: none;
  transition: opacity .85s cubic-bezier(.16,.84,.44,1),
              transform .85s cubic-bezier(.16,.84,.44,1);
}
/* the caption follows its picture a beat later */
.will-reveal.revealed figcaption { animation: fade-in .7s .22s ease both; }

/* section marks: the rule draws itself across */
.section-mark::after { transform-origin: left; }
.will-reveal.revealed.section-mark::after { animation: rule-draw 1s .1s cubic-bezier(.16,.84,.44,1) both; }
@keyframes rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* index rows — the accent arrives from the left rather than blinking on */
.home-index .entry { position: relative; transition: color .3s ease; }
.home-index .entry::after {
  content: ""; position: absolute; left: 0; bottom: -1px; height: 1px; width: 100%;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform .45s cubic-bezier(.16,.84,.44,1);
}
.home-index .entry:hover::after { transform: scaleX(1); }
.home-index .t { transition: color .3s ease, transform .45s cubic-bezier(.16,.84,.44,1); }
.home-index .entry:hover .t { transform: translateX(5px); }
.home-index .entry.static::after { display: none; }
.home-index .entry.static:hover .t { transform: none; }

/* lightbox: fades up rather than snapping */
#lightbox { opacity: 0; transition: opacity .3s ease; }
#lightbox.open { opacity: 1; }
#lightbox img { transform: scale(.985); transition: transform .42s cubic-bezier(.16,.84,.44,1); }
#lightbox.open img { transform: none; }

.masthead a, footer a, .further a, .prose a { transition: color .25s ease; }

/* honour the system setting. everything above becomes instant, nothing hides. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .will-reveal { opacity: 1 !important; transform: none !important; }
  @view-transition { navigation: none; }
}


/* full bleed on narrow screens — the opening frame and any `full` plate run
   edge to edge on a phone. Side padding is politeness the picture cannot afford
   at that size. Still contained, still never cropped. */
@media (max-width: 700px) {
  .opening { padding: 1.5vh 0 0; }
  .opening img { max-width: 100vw; max-height: none; }
  /* Shorter on a phone: 92vh of photograph with the browser chrome on top
     means the visitor has to scroll twice before reaching a single word. */
  .rotator { height: 62vh; }
  .rotator img { max-height: 100%; }
  figure.plate-full { padding: 0; }
  figure.plate-full img { max-width: 100vw; max-height: none; }
  figure.plate-full figcaption { padding: 0 6vw; }
}


/* ────────────────────────────────────────────────────────────────────────────
   DARK

   Not an inversion. Photographs read differently on a dark surround — they
   appear brighter, contrastier, shadow detail opens up. So:

   · Never pure black. #121110 is near-black with the same warmth as the paper
     tone, because white-hot photographs against #000 halate at the edges.
   · The photographs themselves are NEVER filtered, dimmed or adjusted. The
     surround changes; the work does not.
   · Text is warm off-white, not #fff — pure white on near-black is harsh to
     read at length, and there is a lot to read here.
   · The accent lifts, because ochre that works on paper goes muddy on dark.
   ──────────────────────────────────────────────────────────────────────── */

:root[data-theme="dark"] {
  /* the dark page is your brown, lifted just enough that the grain has a
     surface to sit on rather than disappearing into a void */
  --paper:     #231C15;   /* the new brown, lifted so grain has a surface */
  --ink:       #F7F4EC;
  --ink-soft:  #AFABA3;
  --ink-faint: #89847C;
  --rule:      #413A33;
  --accent:    #C58075;   /* rust lifted — at full strength it is unreadable here */
  --accent-bright: #D6968B;
  --lightbox:  #000;
  color-scheme: dark;
}

#lightbox { background: var(--lightbox); }

/* the toggle — a disc that fills. small, quiet, last in the nav. */
#theme {
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; padding: 0; margin-left: .35rem;
  cursor: pointer; line-height: 0;
  display: inline-flex; align-items: center;
}
#theme .disc {
  display: block; width: 11px; height: 11px; border-radius: 50%;
  border: 1.5px solid var(--ink-soft);
  background: linear-gradient(to right, var(--ink-soft) 0 50%, transparent 50% 100%);
  transition: transform .45s cubic-bezier(.16,.84,.44,1),
              border-color .3s ease, background .3s ease;
}
#theme:hover .disc { border-color: var(--accent); background-image: linear-gradient(to right, var(--accent) 0 50%, transparent 50% 100%); }
:root[data-theme="dark"] #theme .disc { transform: rotate(180deg); }
#theme:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 50%; }

/* colour transition on switch — the surround fades, nothing jumps */
html.theme-shifting,
html.theme-shifting body,
html.theme-shifting .masthead,
html.theme-shifting footer,
html.theme-shifting .plate-list,
html.theme-shifting .section-mark::after,
html.theme-shifting .home-index .entry {
  transition: background-color .32s ease, color .32s ease,
              border-color .32s ease, background .32s ease;
}
@media (prefers-reduced-motion: reduce) {
  html.theme-shifting, html.theme-shifting * { transition: none !important; }
  #theme .disc { transition: none; }
}

/* the portrait on About — smaller than a plate. it is a picture of the person,
   not a piece of the work, and it should not open the page louder than they do. */
.opening.portrait img { max-height: 58vh; }
@media (max-width: 700px) { .opening.portrait { padding: 1.5vh 6vw 0; }
                            .opening.portrait img { max-height: 46vh; max-width: 100%; } }

/* exhibitions & press — a record, not a brag. year in the margin, like a CV. */
.listing { max-width: var(--col); margin: 0 auto 3rem; padding: 0 6vw; }
.listing .row {
  display: grid; grid-template-columns: 5.5rem 1fr; gap: 1.2rem;
  padding: 1rem 0; border-bottom: 1px solid var(--rule);
}
.listing .yr {
  font-family: var(--sans); font-size: .74rem; letter-spacing: .1em;
  color: var(--ink-faint); padding-top: .3rem; font-variant-numeric: tabular-nums;
}
.listing .ttl { display: block; font-size: 1.05rem; }
.listing .where {
  display: block; font-family: var(--sans); font-size: .78rem;
  color: var(--ink-faint); margin-top: .25rem;
}
@media (max-width: 700px) {
  .listing .row { grid-template-columns: 1fr; gap: .3rem; }
  .listing .yr { padding-top: 0; }
}

/* <picture> wrapper must not break the plate layout */
figure picture, .opening picture { display: contents; }

/* further reading — sits under the plate list, quiet, can hold several links */
.further .lbl {
  display: block; font-size: .72rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: .8rem;
}
.further ul { list-style: none; margin: 0; padding: 0; }
.further li { padding: .4rem 0; }

/* quoted material — indented and marked so a reader can tell it isn't the
   author's own voice. attribution is required, not decorative. */
blockquote.pull {
  max-width: var(--col); margin: 3.2rem auto; padding: 0 6vw;
  border: 0;
}
blockquote.pull p {
  margin: 0 0 .9em; font-size: 1.08em; line-height: 1.55;
  padding-left: 1.1rem; border-left: 2px solid var(--accent);
}
blockquote.pull cite {
  display: block; padding-left: 1.1rem;
  font-family: var(--sans); font-style: normal;
  font-size: .75rem; line-height: 1.5; color: var(--ink-faint);
}

/* onward — where to go when the work runs out. named, not a carousel. */
.onward {
  max-width: var(--col); margin: 6rem auto 0; padding: 3rem 6vw 0;
  border-top: 1px solid var(--rule);
  display: flex; justify-content: space-between; gap: 2rem;
}
.onward a { text-decoration: none; max-width: 20em; }
.onward .n { text-align: right; margin-left: auto; }
.onward .dir {
  display: block; font-family: var(--sans); font-size: .7rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-faint);
  margin-bottom: .45rem;
}
.onward .t { font-size: 1.05rem; transition: color .3s ease; }
.onward a:hover .t { color: var(--accent); }
@media (max-width: 700px) {
  .onward { flex-direction: column; gap: 2rem; }
  .onward .n { text-align: left; margin-left: 0; }
}

/* the lightbox counts, quietly, so you know how far through you are */
#lightbox { flex-direction: column; }
#lb-count {
  position: fixed; bottom: 1.6rem; left: 0; right: 0; text-align: center;
  font-family: var(--sans); font-size: .72rem; letter-spacing: .14em;
  color: rgba(255,255,255,.45); font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* ── getting back ────────────────────────────────────────────────────────────
   The masthead hides as you move forward and returns the moment you scroll up.
   Going deeper into the work, nothing sits over the photographs. The instant you
   reverse — the only time you actually want to leave — the way out is there. */
.masthead {
  position: sticky; top: 0; z-index: 50;
  background: var(--paper);
  transition: transform .3s cubic-bezier(.16,.84,.44,1), box-shadow .3s ease;
}
body.nav-hidden .masthead { transform: translateY(-100%); }
body.nav-floating .masthead { box-shadow: 0 1px 0 var(--rule); }

/* explicit way back at the top of anything you clicked into */
.back {
  display: flex; align-items: baseline; gap: .5rem;
  max-width: var(--col); margin: 2.5rem auto -1.5rem; padding: 0 6vw;
  font-family: var(--sans); font-size: .72rem;
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-faint); text-decoration: none;
  transition: color .25s ease;
}
.back .arr { transition: transform .25s cubic-bezier(.16,.84,.44,1); }
.back:hover { color: var(--accent); }
.back:hover .arr { transform: translateX(-4px); }

@media (prefers-reduced-motion: reduce) {
  .masthead { transition: none; }
  body.nav-hidden .masthead { transform: none; }
}

/* a note naming the work it came from, and the work naming the note */
.notes-on { margin-top: .45rem; }
.notes-on a { color: var(--ink-soft); }
.notes-on a:hover { color: var(--accent); }
.home-index .entry.static:target,
.home-index .entry:target { box-shadow: inset 3px 0 0 var(--accent); padding-left: .9rem; }

/* ── lightbox controls ───────────────────────────────────────────────────────
   Visible, because a keyboard shortcut nobody can see is not a feature.
   Quiet, because the photograph is the point. They dim at the ends of a
   sequence rather than vanishing, so the frame never jumps.               */
#lightbox { position: fixed; }
#lb-prev, #lb-next, #lb-close {
  position: fixed; z-index: 2;
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; cursor: pointer;
  color: rgba(255,255,255,.62);
  font-family: var(--serif); line-height: 1;
  transition: color .2s ease, opacity .2s ease, transform .2s cubic-bezier(.16,.84,.44,1);
}
#lb-prev, #lb-next {
  top: 50%; transform: translateY(-50%);
  font-size: 3.4rem; padding: 1.4rem 1.1rem;
}
#lb-prev { left: max(.5rem, 2vw); }
#lb-next { right: max(.5rem, 2vw); }
#lb-prev:hover:not(:disabled) { color: #fff; transform: translateY(-50%) translateX(-4px); }
#lb-next:hover:not(:disabled) { color: #fff; transform: translateY(-50%) translateX(4px); }
#lb-prev:disabled, #lb-next:disabled { opacity: .18; cursor: default; }

#lb-close {
  top: 1rem; right: max(.5rem, 2vw);
  font-size: 2rem; padding: .6rem .8rem;
}
#lb-close:hover { color: #fff; }

#lb-prev:focus-visible, #lb-next:focus-visible, #lb-close:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px;
}

/* on a phone the gesture is the swipe, so the arrows shrink out of the way */
@media (max-width: 700px) {
  #lb-prev, #lb-next { font-size: 2.2rem; padding: 1rem .5rem; }
}
@media (prefers-reduced-motion: reduce) {
  #lb-prev, #lb-next, #lb-close { transition: none; }
}

/* ── grain ───────────────────────────────────────────────────────────────────
   A tooth on the paper, so the page and the photograph share a surface instead
   of a picture floating on glass.

   Three rules:
   · It sits BEHIND everything (z-index: -1). It never overlays a photograph.
     Your images already carry your grain; adding more on top would be altering
     the work.
   · It is fixed, not scrolling. Paper does not move under the ink.
   · It is very light. Turn --grain up if you want more; past about .06 it stops
     reading as a surface and starts reading as an effect.

   The noise is a 160px SVG tile repeated — one filter pass on a small square,
   then tiled, rather than a filter running across the whole viewport.        */

:root { --grain: .065; }
/* I originally set dark LOWER on the theory that grain reads stronger against
   black. Wrong in practice: on a near-black page the noise disappears and the
   photographs float in a void. Dark needs MORE, not less. */
:root[data-theme="dark"] { --grain: .105; }

body::before {
  content: "";
  position: fixed;
  inset: -50%;                 /* overscan, so nothing shows at the edges */
  z-index: -1;
  pointer-events: none;
  opacity: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-repeat: repeat;
}

/* anyone who has asked their system for higher contrast gets a clean surface */
@media (prefers-contrast: more) {
  body::before { display: none; }
}

/* ── the self-portraits on About ─────────────────────────────────────────────
   A row, not a single frame, because it is a series — you read them as
   variations on one setup, which is the point of the work. Smaller than a
   plate: these are pictures OF the person, and they should not open the page
   louder than the writing does.                                            */
.portraits {
  display: flex; gap: 1rem; align-items: flex-end; justify-content: center;
  max-width: 62em; margin: 2vh auto 0; padding: 0 6vw;
}
.portraits img { display: block; width: auto; height: auto; max-height: 40vh; max-width: 100%; cursor: zoom-in; }
.portraits.n1 img { max-height: 56vh; }
@media (max-width: 760px) {
  .portraits { flex-direction: column; align-items: center; gap: .8rem; }
  .portraits img { max-height: 40vh; }
}

/* ════════════════════════════════════════════════════════════════════════════
   TWO SITES, NOT ONE SITE AT TWO SIZES

   The contexts genuinely differ, so the layouts should:

   PHONE — one hand, one column, no hover, probably late, probably dark.
     The photograph takes the whole width. Nothing sits beside it because there
     is no beside. Everything is vertical and the pictures are the interface.

   DESKTOP — 55–66% of the screen was empty beside the writing. That space is
     not "clean", it is unused. The captions move into it: set in the left
     margin, aligned to the top of the frame, the way a wall label sits beside
     a print rather than underneath it. The photograph is uninterrupted, the
     information is peripheral, and nothing is pushed down the page.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── PHONE ─────────────────────────────────────────────────────────────────── */
@media (max-width: 760px) {
  figure { padding: 0 3vw; }                 /* was 6vw — give the picture the width */
  .plate-large img  { max-height: none; max-width: 100%; }
  .plate-medium img { max-height: 46vh; }
  .plate-small img  { max-height: 30vh; }

  figcaption { padding: 0 1vw; font-size: .74rem; }

  .masthead nav { gap: .85rem; font-size: .64rem; }
  .masthead .name { letter-spacing: .1em; }

  .title-block { margin-top: 3rem; }
  .section-mark { margin: 4rem auto 2.2rem; }

  /* thumb-sized targets, not mouse-sized */
  .home-index .entry { padding: 1.35rem 0; }
  .plate-list li { padding: 1rem 0; }
}

/* ── DESKTOP ───────────────────────────────────────────────────────────────── */
@media (min-width: 1100px) {
  figure {
    display: grid;
    grid-template-columns: 1fr minmax(0, 78vw) 1fr;
    align-items: start;
    padding: 0 3vw;
    column-gap: 1.6rem;
  }
  figure img, figure .two, figure video { grid-column: 2; justify-self: center; }

  /* the caption lives in the left margin, level with the top of the frame */
  figure figcaption {
    grid-column: 1; grid-row: 1;
    margin-top: .35rem;
    text-align: right;
    justify-self: end;
    max-width: 15em;
    padding: 0;
  }
  figure figcaption .n { display: block; margin: 0 0 .3rem; }

  /* if the margin is too tight to read in, fall back to underneath */
  @media (max-width: 1320px) {
    figure figcaption { max-width: 11em; font-size: .72rem; }
  }

  .title-block h1 { font-size: clamp(2.2rem, 3.4vw, 3.4rem); }
  .plate-list { max-width: 52em; }
}

/* very wide — let the photographs breathe rather than sprawl */
@media (min-width: 1800px) {
  figure { grid-template-columns: 1fr minmax(0, 1500px) 1fr; }
  .opening img { max-height: 86vh; }
}

/* ── the phone menu ──────────────────────────────────────────────────────────
   Five links and a toggle will not sit in a phone header without becoming
   decoration. So on a phone the header holds the name and one button, and the
   links live in a panel where they can be read at a size worth tapping.    */

#menu { display: none; }
#panel { display: none; }

@media (max-width: 760px) {
  .masthead { align-items: center; }
  .masthead .name {
    white-space: nowrap;            /* it was wrapping onto two lines */
    letter-spacing: .09em;
    font-size: .72rem;
  }
  .masthead nav.wide { display: none; }

  #menu {
    display: flex; flex-direction: column; justify-content: center; gap: 5px;
    width: 34px; height: 34px; margin-right: -6px;
    appearance: none; -webkit-appearance: none;
    background: none; border: 0; padding: 0; cursor: pointer;
  }
  #menu span {
    display: block; height: 1.5px; width: 22px; margin-left: auto;
    background: var(--ink);
    transition: transform .28s cubic-bezier(.16,.84,.44,1), opacity .2s ease;
  }
  body.menu-open #menu span:first-child { transform: translateY(3.25px) rotate(45deg); }
  body.menu-open #menu span:last-child  { transform: translateY(-3.25px) rotate(-45deg); }

  #panel {
    display: flex; position: fixed; inset: 0; z-index: 40;
    flex-direction: column; justify-content: center;
    background: var(--paper);
    padding: 0 8vw;
    opacity: 0; pointer-events: none;
    transition: opacity .26s ease;
  }
  #panel[data-open] { opacity: 1; pointer-events: auto; }
  #panel nav { display: flex; flex-direction: column; gap: 0; }
  #panel nav a {
    font-family: var(--serif); text-transform: none; letter-spacing: 0;
    font-size: 1.9rem; padding: .55rem 0; text-decoration: none;
    border-bottom: 1px solid var(--rule); color: var(--ink);
  }
  #panel nav a.on { color: var(--accent); }
  #panel nav #theme { display: none; }
  .panel-foot { margin-top: 2.5rem; }
  .panel-foot #theme { display: inline-flex; }
  body.menu-open { overflow: hidden; }
}

/* ── the portraits on About, on a phone ──────────────────────────────────────
   Three stacked frames is 120vh of me before a word of writing. As a set —
   one large, two beneath — they read as a series in a single glance.       */
@media (max-width: 760px) {
  .portraits {
    display: grid; grid-template-columns: 1fr 1fr; gap: .6rem;
    padding: 0 3vw; align-items: start;
  }
  .portraits img { max-height: none; width: 100%; }
  .portraits img:first-child { grid-column: 1 / -1; }
}

/* ── overflow guards ─────────────────────────────────────────────────────────
   Grid and flex children default to min-width:auto — they will not shrink
   below the intrinsic width of what is inside them. A 2600px photograph in a
   grid column therefore makes the column 2600px, the document wider than the
   phone, and everything squashes into a band on the left. min-width:0 is the
   fix, and no page should ever be able to scroll sideways.               */

html, body { max-width: 100%; overflow-x: hidden; }
img, picture, video { max-width: 100%; }

/* <picture> is the grid/flex item, not the <img> inside it */
.portraits > picture { display: block; min-width: 0; }
figure > picture     { min-width: 0; }

@media (max-width: 760px) {
  .portraits > picture:first-child { grid-column: 1 / -1; }
  .portraits img { width: 100%; height: auto; max-height: none; }
  /* the base flex rule must not leak through */
  .portraits { display: grid; }
}

@media (min-width: 1100px) {
  figure > picture { grid-column: 2; justify-self: center; }
}

/* ── the monogram ────────────────────────────────────────────────────────────
   Used as a CSS mask rather than a picture, so it takes --ink and follows the
   theme from one file — no second copy for dark mode, no black square on a
   dark page. The name stays as real text beside it: searchable, selectable,
   readable by a screen reader. The mark is decorative, so it is hidden from
   assistive tech rather than announced twice.                             */
.masthead .name { display: inline-flex; align-items: center; gap: .55rem; }
.masthead .name .mark {
  display: block; width: .95rem; height: 1.32rem; flex: none;
  background: var(--ink);
  -webkit-mask: url("/mark.png") center / contain no-repeat;
          mask: url("/mark.png") center / contain no-repeat;
  transition: background .3s ease;
}
.masthead .name:hover .mark { background: var(--accent); }

/* On the inner pages the monogram stands alone, so it carries a little more
   weight than it did sitting next to the name — otherwise it reads as a
   leftover rather than a mark. The accessible name lives on the link. */
.masthead .name.mono { gap: 0; }
.masthead .name.mono .mark { width: 1.14rem; height: 1.58rem; }

@media (max-width: 760px) {
  .masthead .name { gap: .45rem; }
  .masthead .name .mark { width: .82rem; height: 1.14rem; }
  .masthead .name.mono .mark { width: .95rem; height: 1.32rem; }
}
