/* Coelacanth by Ben Whitmore, a free revival of Centaur (SIL Open Font License, fonts/coelacanth/LICENSE.md).
   The Display cut is drawn for large sizes; every serif use on the site is large. */
@font-face {
  font-family: "Coelacanth Display";
  src: url("../fonts/coelacanth/CoelacanthDisplay.otf") format("opentype");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Coelacanth Display";
  src: url("../fonts/coelacanth/CoelacanthIt.otf") format("opentype");
  font-weight: 400; font-style: italic; font-display: swap;
}

:root {
  --ground: #efefec;
  --ink: #151514;
  --soft: #62625e;
  --faint: #9c9c96;
  --line: #dadad5;
  --led: #e2402f;
  --current: #dba9a2;            /* the dusty pink Manuela sent */

  --serif: "Coelacanth Display", "Centaur", "Times New Roman", serif;
  --sans: "Hanken Grotesk", ui-sans-serif, system-ui, sans-serif;
  --mono: "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --gutter: clamp(16px, 3vw, 36px);
  --aside: 380px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #121211;
    --ink: #e9e8e3;
    --soft: #a2a19b;
    --faint: #6a6a65;
    --line: #2a2a28;
    --led: #ff5b45;
    --current: #e0b0a9;
  }
}
:root[data-theme="dark"] {
  --ground: #121211;
  --ink: #e9e8e3;
  --soft: #a2a19b;
  --faint: #6a6a65;
  --line: #2a2a28;
  --led: #ff5b45;
  --current: #e0b0a9;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  padding-inline: var(--gutter);
}

a { color: inherit; }
a:focus-visible { outline: 2px solid var(--led); outline-offset: 3px; }
img { max-width: 100%; display: block; }

/* ---------- Arriving on a page ---------- */
/* Everything clears from a blur. Only the project title waves while it clears — an SVG
   turbulence that stirs and dies down, like smoke. The index, the text and the photos
   stay still and simply come into focus. The stirring animates itself (SMIL, in the
   filters build.py writes into each page); the blur is the CSS below.
   .enter-quick is the shorter version used after the first page of a visit. */
.gallery a, .preview {
  animation: blur-in 400ms cubic-bezier(0.16, 0.7, 0.3, 1) both;
}
.site-header, .project-text, .next-project,
.site-footer, .about-grid, .cv {
  animation: blur-in 400ms cubic-bezier(0.16, 0.7, 0.3, 1) 70ms both;
}
.project-title {
  animation: wave-in 500ms cubic-bezier(0.16, 0.7, 0.3, 1) 70ms both;
}

/* The project index never animates: it's the one fixed thing on the page, like the
   header, so it stays put while the work beside it changes. (The header animates once,
   when the site first opens, then stays put too.) */
.enter-quick .site-header { animation: none; }

.enter-quick .gallery a, .enter-quick .preview,
.enter-quick .project-text, .enter-quick .next-project,
.enter-quick .site-footer, .enter-quick .about-grid, .enter-quick .cv {
  animation-name: blur-in-quick;
  animation-duration: 280ms;
  animation-delay: 0ms;
}
.enter-quick .project-title {
  animation-name: wave-in-quick;
  animation-duration: 320ms;
  animation-delay: 0ms;
}

@keyframes blur-in {
  from { filter: blur(18px); opacity: 0; }
  55%  { opacity: 1; }
  to   { filter: blur(0px); opacity: 1; }
}
@keyframes blur-in-quick {
  from { filter: blur(10px); opacity: 0; }
  55%  { opacity: 1; }
  to   { filter: blur(0px); opacity: 1; }
}
@keyframes wave-in {
  from { filter: url(#smoke) blur(6px); opacity: 0.35; }
  to   { filter: url(#smoke) blur(0px); opacity: 1; }
}
@keyframes wave-in-quick {
  from { filter: url(#smoke-quick) blur(4px); opacity: 0.45; }
  to   { filter: url(#smoke-quick) blur(0px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .gallery a, .preview, .site-header, .project-title, .project-text,
  .next-project, .site-footer, .about-grid, .cv { animation: none; }
}

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

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding-block: 20px 28px;
}
.site-name {
  font-family: var(--serif);
  font-size: 1.65rem;
  line-height: 1;
  text-decoration: none;
  letter-spacing: -0.005em;
}
.site-nav {
  display: flex;
  gap: 22px;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.site-nav a { text-decoration: none; color: var(--soft); }
.site-nav a:hover, .site-nav a[aria-current="page"] { color: var(--ink); }

/* On phones and tablets the header is a bar that stays at the top of the screen while the
   work scrolls under it. It reaches into the page's side gutters so nothing shows past it,
   and sits above the index (10000), which is itself above the cursor. */
@media (max-width: 900px) {
  .site-header {
    position: sticky;
    top: 0;
    z-index: 10001;
    background: var(--ground);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    border-bottom: 1px solid var(--line);
  }
}

/* On a phone the name has to share that bar with the nav, so it stays on one line
   and both sides come down a size to fit. */
@media (max-width: 560px) {
  .site-header { gap: 10px; padding-block: 14px 14px; }
  .site-name { font-size: 1.4rem; white-space: nowrap; }
  .site-nav { gap: 14px; font-size: 0.68rem; letter-spacing: 0.06em; }
  .index-label { padding-top: 22px; }
}

/* Narrower than an iPhone 12 mini (e.g. the SE), where CONTACT would be cut off. */
@media (max-width: 359px) {
  .site-name { font-size: 1.18rem; }
  .site-nav { gap: 11px; font-size: 0.6rem; }
}

/* ---------- Two-column frame: work + index ---------- */

.frame {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
}

.index-aside {
  position: sticky;
  top: 20px;
  /* Above the cursor (9999) so the titles stay in front of the photo it shows. */
  z-index: 10000;
}

@media (max-width: 900px) {
  .frame { grid-template-columns: minmax(0, 1fr); }
  .index-aside { position: static; }
  .frame--home .index-aside { order: -1; }
}

.project-video {
  display: block;
  width: auto;
  max-width: 100%;
  max-height: 78vh;
  margin: 0 auto 44px;
  background: #000;
}

/* ---------- Project index ---------- */

.index-label {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding-bottom: 10px;
  border-bottom: 1px solid var(--ink);
  display: flex;
  justify-content: space-between;
}

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

.index li { border-bottom: 1px solid var(--line); transition: border-color 160ms ease; }

/* The grey rules sit in the same layer as the titles, so they can't pass behind the
   cursor's photo — they fade out instead while it is showing. */
.has-preview .index li { border-color: transparent; }

.index a,
.index .pending {
  display: grid;
  grid-template-columns: 2.2rem minmax(0, 1fr) auto;
  column-gap: 10px;
  align-items: baseline;
  padding-block: 9px;
  text-decoration: none;
}

.index .num {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
  position: relative;
}

.index .title {
  font-size: 0.86rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.index .meta {
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--soft);
  text-align: right;
  white-space: nowrap;
}

.index .medium { display: block; }
.index .year { display: block; color: var(--faint); font-variant-numeric: tabular-nums; }

.index a:hover .title { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

.index .pending { color: var(--faint); cursor: default; }
.index .pending .title, .index .pending .meta, .index .pending .year { color: var(--faint); }

/* The current project is marked by a flat dusty-pink circle. */
.index a[aria-current="page"] .num::before {
  content: "";
  position: absolute;
  left: -15px;
  top: 50%;
  width: 8px;
  height: 8px;
  margin-top: -4px;
  border-radius: 50%;
  background: var(--current);
}

.index a[aria-current="page"] .num { color: var(--ink); }

/* The cover beside each row. It stands in for the cursor preview, so it appears only
   where there is no cursor — a phone or a tablet — and never on a narrow desktop window,
   where the cursor still does the previewing. */
.index .row-shot { display: none; }
:root { --shot: 0px; --shot-gap: 0px; --shot-show: none; }
@media (hover: none) { :root { --shot: 60px; --shot-gap: 74px; --shot-show: block; } }
@media (pointer: coarse) { :root { --shot: 60px; --shot-gap: 74px; --shot-show: block; } }

/* Phones and narrow screens: one column, with the cover circle beside every project. */
@media (max-width: 900px) {
  .index a {
    position: relative;
    grid-template-columns: 2rem minmax(0, 1fr);
    row-gap: 2px;
    align-content: center;
    /* Room for the circle at the right, and height for it. */
    padding-right: var(--shot-gap);
    min-height: var(--shot);
  }
  .index a .meta {
    grid-column: 2;
    text-align: left;
    /* A long medium wraps rather than running under the circle. */
    white-space: normal;
  }
  /* Out of the grid flow, so it can't push the title and the medium apart. */
  .index a .row-shot {
    display: var(--shot-show);
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    width: var(--shot);
    height: var(--shot);
    border-radius: 50%;
    overflow: hidden;
    background: var(--line);
  }
  .index a .row-shot img { width: 100%; height: 100%; object-fit: cover; }
  .index .pending { grid-template-columns: 2rem minmax(0, 1fr); }
  .index .pending .meta { grid-column: 2; text-align: left; }
  /* The year sits on its own line under the medium, as it does beside the work on a
     wide screen — .medium and .year are block by default, so nothing to undo here. */
}

/* ---------- Negative cursor ---------- */
/* A circle follows the mouse and inverts whatever is under it. Added by js/cursor.js,
   which only runs for a real pointer, so touch screens are untouched. */
@media (hover: hover) and (pointer: fine) {
  .has-neg-cursor body, .has-neg-cursor a, .has-neg-cursor button,
  .has-neg-cursor input, .has-neg-cursor summary { cursor: none; }

  .neg-cursor {
    --neg-size: 64px;
    position: fixed;
    top: 0;
    left: 0;
    width: var(--neg-size);
    height: var(--neg-size);
    margin: calc(var(--neg-size) / -2) 0 0 calc(var(--neg-size) / -2);
    border-radius: 50%;
    background: #fff;
    mix-blend-mode: difference;
    pointer-events: none;
    z-index: 9999;
    opacity: 0;
    will-change: transform;
    transition: width 0.18s ease, height 0.18s ease, margin 0.18s ease, opacity 0.18s ease;
  }
  .neg-cursor.is-on { opacity: 1; }
  .neg-cursor.is-over-link { --neg-size: 92px; }
  /* Over a project in the index the circle becomes a window onto its photographs. */
  .neg-cursor.is-preview {
    --neg-size: 64px;                 /* the same size as the plain cursor */
    mix-blend-mode: normal;
    background-color: transparent;
    background-size: cover;
    background-position: center;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.22);
  }
  .neg-cursor.is-down { --neg-size: 48px; }
}

/* ---------- Homepage preview ---------- */

.preview { margin: 0; }
.preview-img {
  width: 100%;
  height: min(78vh, 900px);
  object-fit: contain;
  object-position: left top;
  transition: opacity 180ms ease;
}
.preview-img.is-swapping { opacity: 0.25; }
.preview figcaption {
  margin-top: 12px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--soft);
}

@media (max-width: 900px) {
  .preview-img { height: auto; max-height: 70vh; object-position: center top; }
}

/* ---------- Project page ---------- */

.project-head { display: flex; flex-direction: column; gap: 14px; margin-bottom: 36px; }

.project-title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.8rem, 7vw, 5.6rem);
  line-height: 0.92;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0;
  text-wrap: balance;
}

/* Wall-label: medium, venue, year */
.wall-label {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 18px;
  row-gap: 4px;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--soft);
  max-width: 62ch;
}
.wall-label dt { color: var(--faint); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.66rem; padding-top: 2px; }
.wall-label dd { margin: 0; }
.wall-label dd a { text-decoration: none; border-bottom: 1px solid var(--soft); }
.wall-label dd a:hover { color: var(--led); border-color: var(--led); }

.project-quote {
  margin: 0.4em 0 0;
  padding-left: 18px;
  border-left: 1px solid var(--ink);
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.2rem;
  line-height: 1.45;
}
.project-quote p { margin: 0; }
.project-quote cite {
  display: block;
  margin-top: 10px;
  font-family: var(--mono);
  font-style: normal;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--soft);
}

.project-text {
  max-width: 62ch;
  font-size: 1.02rem;
  line-height: 1.65;
  display: flex;
  flex-direction: column;
  gap: 1em;
  margin-bottom: 44px;
}
.project-text p { margin: 0; }
.project-text .lede {
  font-family: var(--serif);
  font-size: 1.55rem;
  line-height: 1.3;
}

.trailer {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-decoration: none;
  border-bottom: 1px solid var(--ink);
  width: fit-content;
}
.trailer:hover { color: var(--led); border-color: var(--led); }

.gallery {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.gallery a { display: block; background: var(--line); }
.gallery img { width: 100%; height: 100%; object-fit: cover; }
.gallery .p img { aspect-ratio: 4 / 5; }
.gallery .l img { aspect-ratio: 3 / 2; }
.gallery .full { grid-column: 1 / -1; }
.gallery .full img { aspect-ratio: auto; height: auto; }
.gallery .solo { grid-column: 1 / -1; width: calc(50% - 6px); }
.gallery .solo img { aspect-ratio: auto; height: auto; }

@media (max-width: 640px) {
  .gallery { grid-template-columns: 1fr; }
  .gallery .solo { width: 100%; }
  .gallery .p img, .gallery .l img { aspect-ratio: auto; height: auto; }
}

.next-project {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  margin-top: 56px;
  padding-top: 16px;
  border-top: 1px solid var(--ink);
  text-decoration: none;
}
.next-project .label {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}
.next-project .name {
  font-family: var(--serif);
  font-size: clamp(1.8rem, 4vw, 2.8rem);
  line-height: 1;
  text-transform: uppercase;
}
.next-project:hover .name { color: var(--led); }

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

.about-grid {
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 48px);
  align-items: start;
}
.about-portrait { width: 100%; height: auto; }
.about-portrait-caption {
  margin-top: 8px;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--faint);
}
.about-intro {
  font-family: var(--serif);
  font-size: clamp(1.6rem, 3vw, 2.1rem);
  line-height: 1.2;
  margin: 0 0 22px;
  max-width: 30ch;
  text-wrap: balance;
}
.about-bio { max-width: 62ch; display: flex; flex-direction: column; gap: 1em; line-height: 1.65; }
.about-bio p { margin: 0; }

@media (max-width: 640px) {
  .about-grid { grid-template-columns: minmax(0, 1fr); }
  .about-portrait { max-width: 260px; }
}

.cv {
  margin-top: 64px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 40px clamp(24px, 4vw, 56px);
}
@media (max-width: 640px) { .cv { grid-template-columns: minmax(0, 1fr); } }
.cv-col { display: flex; flex-direction: column; gap: 40px; min-width: 0; }

.cv h2 {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--ink);
}
.cv ol { list-style: none; margin: 0; padding: 0; }
.cv li {
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr);
  gap: 10px;
  padding-block: 7px;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
  line-height: 1.45;
}
.cv .yr { font-family: var(--mono); font-size: 0.74rem; color: var(--soft); font-variant-numeric: tabular-nums; padding-top: 2px; }
.cv em { font-style: italic; }
.cv .venue { color: var(--soft); }

.cv-download {
  display: inline-block;
  margin-top: 36px;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-decoration: none;
  border-bottom: 1px solid var(--ink);
}
.cv-download:hover { color: var(--led); border-color: var(--led); }

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

.site-footer {
  margin-top: 96px;
  padding-block: 18px 28px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px 24px;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--soft);
}
.site-footer a { text-decoration: none; }
.site-footer a:hover { color: var(--ink); text-decoration: underline; }
/* Top-aligned, so the email sits level with "Discover more on Instagram". */
.site-footer .links { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px; }

/* The Instagram link: the words over a small screenshot of her grid, cropped to a circle
   like the covers beside the projects. Used in the footer and again on the About page. */
.ig {
  display: grid;
  justify-items: start;
  gap: 7px;
  line-height: 1.55;
  text-decoration: none;
}
.ig img {
  width: 120px;
  height: 120px;
  border-radius: 50%;
  object-fit: cover;
  transition: opacity 160ms ease;
}
.ig:hover { text-decoration: none; }
.ig:hover span { text-decoration: underline; }
.ig:hover img { opacity: 0.72; }

/* In the About bio column it follows the CV download, in that link's type. */
.about-ig {
  margin-top: 28px;
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
}

/* On a phone the whole footer stacks down the left instead of splitting into two
   columns, which pushed the Instagram line against the right edge. */
@media (max-width: 560px) {
  .site-footer { flex-direction: column; align-items: flex-start; gap: 14px; }
  .site-footer .links { flex-direction: column; align-items: flex-start; gap: 14px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}
