/* ================================================================
   dorothy philip  portfolio
   Helvetica Light (300) + Helvetica Regular (400) throughout
   ================================================================ */

:root {
  --ink: #111;
  --grey: #767676;
  --line: #e3e3e3;
  --bg: #fff;
  --ease: cubic-bezier(.76, 0, .24, 1);
  --pad: clamp(20px, 4vw, 48px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

#work, #about { scroll-margin-top: 64px; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: 'Helvetica Neue', HelveticaNeue, Helvetica, Arial, sans-serif;
  font-weight: 300;
  font-size: 15px;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
}

::selection { background: #111; color: #fff; }

a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }

.regular { font-weight: 400; }

/* ── LOADER (52 countdown, kept from yydph.52) ─────────────── */
#loader {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  overflow: hidden;
}
.loader-count {
  font-weight: 300;
  font-size: clamp(80px, 18vw, 200px);
  letter-spacing: -0.04em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  min-width: 3ch;
  text-align: center;
}

/* Content slides in as loader slides away.
   Scoped to .js (set by an inline script in <head>) so the page is never
   left invisible if the stylesheet loads but the scripts don't. */
/* Only hide the content on a page that is actually about to run the
   countdown. The head script decides that before first paint, so pages
   without a loader (gallery, the project pages) never blank out and then
   flash back in once script.js reaches the bottom of the body. */
.js.has-loader main, .js.has-loader footer { opacity: 0; transform: translateY(24px); }
.js.has-loader body.revealed main, .js.has-loader body.revealed footer {
  opacity: 1;
  transform: none;
  transition: opacity .7s ease .1s, transform .7s var(--ease) .1s;
}
.js body.no-loader main, .js body.no-loader footer { opacity: 1; transform: none; }

/* ── TOPBAR ─────────────────────────────────────────────────── */
#topbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 600;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 18px var(--pad);
  background: rgba(255,255,255,.92);
  backdrop-filter: blur(8px);
}
#topbar .brand { font-weight: 400; letter-spacing: .02em; }
#topbar nav { display: flex; gap: 28px; align-items: baseline; }
#topbar nav a {
  color: var(--ink);
  font-weight: 300;
  transition: color .2s;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}
#topbar nav a:hover { color: var(--ink); }
#topbar nav a.active { color: var(--ink); border-bottom-color: var(--ink); }
#clock {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-left: 18px;
  display: none;
}
@media (min-width: 720px) { #clock { display: inline; } }

/* ── HERO (Figma wireframe layout) ──────────────────────────── */
.hero {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 var(--pad);
  position: relative;
}
.hero h1 {
  font-weight: 300;
  font-size: clamp(44px, 8.5vw, 110px);
  line-height: 1.06;
  letter-spacing: -0.03em;
  max-width: 14ch;
}
.hero .sub {
  margin-top: 32px;
  color: var(--grey);
  max-width: 52ch;
  line-height: 1.65;
}
.scroll-cue {
  position: absolute;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 300;
  color: var(--grey);
}
@keyframes nudge { 0%,100% { transform: translateY(0);} 50% { transform: translateY(6px);} }
.scroll-cue span:last-child { animation: nudge 2s ease-in-out infinite; }

/* ── CURRENTLY EXPLORING (Pentagram-style fill-in) ──────────── */
.exploring {
  padding: clamp(80px, 14vh, 160px) var(--pad);
  border-top: 1px solid var(--line);
}
.exploring .line {
  font-weight: 300;
  font-size: clamp(30px, 5.4vw, 72px);
  letter-spacing: -0.025em;
  line-height: 1.18;
  max-width: 1200px;
}
.exploring .slot {
  display: inline-block;
  position: relative;
  border-bottom: 2px solid var(--ink);
  min-width: 4ch;
  white-space: nowrap;
  vertical-align: bottom;
  overflow: hidden;
}
.exploring .slot .word {
  display: inline-block;
  animation: wordIn .5s var(--ease);
}
@keyframes wordIn {
  from { transform: translateY(105%); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.exploring .tags {
  margin-top: 40px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 12px;
}
.exploring .tags button {
  font: inherit;
  font-size: 13px;
  font-weight: 300;
  font-family: inherit;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: 100px;
  background: none;
  color: var(--grey);
  cursor: pointer;
  transition: all .25s;
}
.exploring .tags button:hover,
.exploring .tags button.on {
  border-color: var(--ink);
  color: var(--ink);
}

/* ── SELECTED WORK  full-screen thumbnails, hover titles ───── */
.work { padding: 60px 0 40px; }
.section-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 24px var(--pad);
}
.section-head h2 {
  font-size: 14px;
  font-weight: 300;
  color: var(--grey);
}
.section-head .range { color: var(--grey); font-size: 13px; font-weight: 300; }

.project-card {
  display: block;
  margin: 0 var(--pad) 48px;
}
.project-card .thumb {
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  height: 42vh;
  min-height: 320px;
  background: #f2f2f2;
}
.project-card .thumb img,
.project-card .thumb .ph {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .8s var(--ease);
}
.project-card:hover .thumb img,
.project-card:hover .thumb .ph { transform: scale(1.03); }

/* wide artwork that shouldn't be cropped  letterbox it against its own ground */
.project-card.fit-contain .thumb { background: #000; }
.project-card.fit-contain .thumb img { object-fit: contain; }
.project-card.fit-contain.light .thumb { background: #f6f6f6; }
/* drawings on a pale ground need the overlay scrim to stay readable */
.project-card.fit-contain.light .overlay { background: linear-gradient(180deg, rgba(255,255,255,.1), rgba(0,0,0,.42)); }

/* "more work" link out to the gallery, under the project cards */
.more-work {
  display: inline-flex;
  align-items: baseline;
  gap: 14px;
  /* .about is capped at 1400px and centred, so match its left edge
     rather than the project cards', which sit at the viewport padding */
  margin: 10px var(--pad) 56px;
  margin-left: max(var(--pad), calc((100% - 1400px) / 2 + var(--pad)));
  color: var(--ink);
  font-weight: 300;
  font-size: clamp(28px, 4.2vw, 56px);
  letter-spacing: -0.025em;
  line-height: 1.1;
}
.more-work .arw {
  font-size: 0.52em;
  color: var(--grey);
  transition: transform .35s var(--ease), color .25s;
}
.more-work:hover .arw {
  color: var(--ink);
  transform: translate(6px, -6px);
}

/* title/subtitle live on the thumbnail, revealed on hover (Figma) */
.project-card .overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: clamp(24px, 4vw, 48px);
  background: linear-gradient(to top, rgba(0,0,0,.55) 0%, rgba(0,0,0,.18) 45%, rgba(0,0,0,0) 70%);
  color: #fff;
  opacity: 0;
  transition: opacity .45s ease;
}
.project-card:hover .overlay,
.project-card:focus-visible .overlay { opacity: 1; }
.project-card .overlay h3 {
  font-weight: 400;
  font-size: clamp(22px, 2.6vw, 38px);
  letter-spacing: -0.02em;
  line-height: 1.05;
  transform: translateY(14px);
  transition: transform .5s var(--ease);
}
.project-card .overlay .q {
  font-weight: 300;
  font-style: italic;
  font-size: clamp(14px, 1.3vw, 18px);
  margin-top: 10px;
  max-width: 56ch;
  color: rgba(255,255,255,.92);
  transform: translateY(14px);
  transition: transform .5s var(--ease) .05s;
}
.project-card .overlay .cat {
  font-weight: 300;
  font-size: 13px;
  margin-bottom: 12px;
  color: rgba(255,255,255,.75);
  transform: translateY(14px);
  transition: transform .5s var(--ease) .1s;
}
.project-card:hover .overlay h3,
.project-card:hover .overlay .q,
.project-card:hover .overlay .cat { transform: none; }
@media (hover: none) {
  .project-card .overlay { opacity: 1; }
  .project-card .overlay h3, .project-card .overlay .q, .project-card .overlay .cat { transform: none; }
}

/* image placeholder blocks */
.ph {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #b9b9b9;
  font-size: 12px;
  font-weight: 300;
  text-align: center;
  padding: 12px;
  background:
    repeating-linear-gradient(45deg, #f7f7f7 0 14px, #f0f0f0 14px 28px);
}

/* ── CAROUSEL (graphic design) ─────────────────────────────── */
.carousel-section { padding: 40px 0 90px; border-top: 1px solid var(--line); }
.carousel-wrap { position: relative; }
.carousel {
  display: flex;
  gap: 18px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 8px var(--pad) 20px;
  scrollbar-width: none;
}
.carousel::-webkit-scrollbar { display: none; }
.carousel .slide {
  flex: 0 0 auto;
  width: min(420px, 74vw);
  scroll-snap-align: start;
}
.carousel .slide .frame {
  aspect-ratio: 4 / 5;
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  background: #f2f2f2;
}
.carousel .slide .frame .ph { position: absolute; inset: 0; }
.carousel .slide p { margin-top: 10px; color: var(--grey); font-size: 13px; font-weight: 300; }
.car-btns {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  padding: 14px var(--pad) 0;
}
.car-btns button {
  width: 42px; height: 42px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: none;
  font-size: 16px;
  cursor: pointer;
  color: var(--ink);
  transition: all .25s;
}
.car-btns button:hover { border-color: var(--ink); }

/* ── ABOUT SECTION (end of the scroll, Figma layout) ────────── */
.about { padding: clamp(80px, 12vh, 140px) var(--pad) 60px; border-top: 1px solid var(--line); max-width: 1400px; margin: 0 auto; }
.about-row {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: clamp(20px, 4vw, 70px);
  padding: 44px 0;
  border-top: 1px solid var(--line);
}
.about-row > .label {
  font-size: 13px;
  font-weight: 300;
  color: var(--grey);
  padding-top: 6px;
}
@media (max-width: 720px) { .about-row { grid-template-columns: 1fr; gap: 16px; } }

.about .statement {
  font-size: clamp(26px, 3.6vw, 46px);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1.3;
  max-width: 26ch;
}
.about .bio p { line-height: 1.8; color: #333; max-width: 66ch; margin-bottom: 16px; }
.about .bio .quiet { color: var(--grey); font-style: italic; }

/* resume rows  from the yydph.52 index page */
.cv-item {
  display: grid;
  /* the year column is a fixed width, not auto: each .cv-item is its own
     grid, so an auto column sized to its own text ("2026" vs "2026-" vs
     blank) and the skills column started at a different x on every row */
  grid-template-columns: 34px minmax(140px, 1.1fr) minmax(140px, 1fr) 2fr 62px;
  gap: 16px;
  align-items: baseline;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}
.cv-item:last-child { border-bottom: none; }
.cv-item .num { color: #c9c9c9; font-variant-numeric: tabular-nums; }
.cv-item .org { font-weight: 400; }
.cv-item .role { color: #333; }
.cv-item .tags { color: var(--grey); font-size: 13px; }
.cv-item .when { color: var(--grey); font-size: 13px; white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 860px) {
  .cv-item { grid-template-columns: 34px 1fr 62px; }
  .cv-item .role { grid-column: 2; }
  .cv-item .tags { display: none; }
}

.about .now-listening { color: var(--grey); font-size: 13.5px; line-height: 1.7; max-width: 500px; }
.contact-links { display: flex; flex-direction: column; gap: 12px; }
.contact-links a { display: inline-flex; gap: 10px; align-items: center; color: #333; transition: color .2s; width: fit-content; }
.contact-links a:hover { color: var(--ink); }
.contact-links svg { width: 18px; height: 18px; }

/* ── FOOTER ─────────────────────────────────────────────────── */
footer {
  border-top: 1px solid var(--line);
  padding: 56px var(--pad) 40px;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  flex-wrap: wrap;
}
footer .big {
  font-size: clamp(26px, 4vw, 44px);
  font-weight: 300;
  letter-spacing: -0.02em;
}
footer .links { display: flex; gap: 20px; align-items: center; }
footer .links a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--grey);
  font-weight: 300;
  transition: color .2s;
}
footer .links a:hover { color: var(--ink); }
footer .links svg { width: 18px; height: 18px; }
footer .fine { width: 100%; color: var(--grey); font-size: 12px; font-weight: 300; margin-top: 26px; }

/* ── SCROLL REVEAL ──────────────────────────────────────────── */
/* also .js-scoped  without scripts every section must stay visible */
.js .reveal { opacity: 0; transform: translateY(30px); transition: opacity .7s ease, transform .7s var(--ease); }
.js .reveal.in { opacity: 1; transform: none; }

/* ================================================================
   PROJECT PAGE
   ================================================================ */
.proj { padding: 120px var(--pad) 0; max-width: 1500px; margin: 0 auto; }
.proj-head h1 {
  font-weight: 300;
  font-size: clamp(44px, 7.5vw, 96px);
  letter-spacing: -0.03em;
  line-height: 1.02;
}
.proj-head .meta-row {
  display: flex;
  gap: clamp(24px, 6vw, 90px);
  flex-wrap: wrap;
  margin: 36px 0 0;
  padding: 22px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.proj-head .meta-row div span {
  display: block;
  font-size: 12px;
  font-weight: 300;
  color: var(--grey);
  margin-bottom: 6px;
}

/* blurb left / hero right (Reika Oh layout) */
.proj-intro {
  display: grid;
  grid-template-columns: minmax(260px, 440px) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 80px);
  margin: 64px 0 90px;
  align-items: start;
}
.proj-intro .blurb { position: sticky; top: 110px; }
.proj-intro .blurb p { line-height: 1.75; color: #333; margin-bottom: 18px; }
.proj-intro .blurb .lede { font-size: 18px; color: var(--ink); }
.proj-intro .hero-img {
  aspect-ratio: 4 / 3;
  border-radius: 14px;
  overflow: hidden;
  position: relative;
  background: #f2f2f2;
}
.proj-intro .hero-img .ph { position: absolute; inset: 0; }
@media (max-width: 860px) {
  .proj-intro { grid-template-columns: 1fr; }
  .proj-intro .blurb { position: static; }
}

/* process images */
.process { margin: 0 0 100px; }
.process h2, .whatif h2, .viz-section > h2, .more h2 {
  font-size: 14px;
  font-weight: 300;
  color: var(--grey);
  margin-bottom: 36px;
}
.process .grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr));
  gap: 36px 24px;
}
.process figure.wide { grid-column: 1 / -1; }
.process .frame {
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  aspect-ratio: 3 / 2;
  background: #f2f2f2;
}
.process figure.wide .frame { aspect-ratio: 21 / 9; }
.process .frame .ph { position: absolute; inset: 0; }
.process figcaption {
  margin-top: 12px;
  color: var(--grey);
  font-size: 13px;
  line-height: 1.6;
  max-width: 72ch;
}
.process figcaption .n { font-weight: 400; color: var(--ink); margin-right: 8px; }

/* full-bleed strip (plan diagrams)  matches the artwork exactly, no crop */
.process figure.strip .frame {
  aspect-ratio: 2600 / 814;
  background: #000;
}
.process figure.strip figcaption { max-width: none; margin-top: 16px; }
/* program panels sit on white and keep their own proportions */
.process figure.program .frame {
  aspect-ratio: 2005 / 1253;
  background: #fff;
  border: 1px solid var(--line);
}
.process figure.program .frame img { object-fit: contain; }

/* ── FEATURES  image + text rows, caption under the image ───────── */
.features { margin: 0 0 100px; }
.features > h2 {
  font-size: 14px;
  font-weight: 300;
  color: var(--grey);
  margin-bottom: 40px;
}
/* one bar across the page: textures 2/5, perforated steel 1/5, script 2/5 */
.feature-bar-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.55fr) minmax(0, 2.45fr);
  gap: clamp(18px, 2.6vw, 36px);
  align-items: start;
  margin: clamp(16px, 3vh, 32px) 0 clamp(56px, 9vh, 96px);
}
.feature-bar-row figure { margin: 0; }
.feature-bar-row .frame {
  position: relative;
  height: clamp(240px, 34vh, 380px);
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.feature-bar-row .frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 12px;
  box-sizing: border-box;
  display: block;
}
/* the perforation reads as a solid rectangle  fill the frame, no white */
.feature-bar-row figure.perf .frame img {
  object-fit: cover;
  padding: 0;
}
.feature-bar-row figcaption {
  margin-top: 12px;
  color: var(--grey);
  font-size: 13px;
  font-weight: 300;
}
.feature-bar-row figcaption { line-height: 1.7; max-width: none; }
.feature-note {
  margin: 0 0 clamp(56px, 9vh, 96px);
  color: #333;
  line-height: 1.8;
  max-width: none;
}
/* numbering across the three features */
.feature-bar-row figcaption .n,
.feature figcaption .n {
  font-weight: 400;
  color: var(--ink);
  margin-right: 10px;
}
@media (max-width: 700px) {
  .feature-bar-row { grid-template-columns: 1fr; row-gap: 26px; }
}

/* one row across the page: pods → text → structure → text */
.feature-row {
  display: grid;
  grid-template-columns: minmax(0, 0.5fr) minmax(0, 0.7fr) minmax(0, 0.95fr) minmax(0, 0.7fr);
  gap: clamp(20px, 3vw, 48px);
  align-items: start;
}
/* .feature is a semantic wrapper only  its children join the row directly */
.feature { display: contents; }
.feature figure { margin: 0; }
/* fixed frame height + contain: artwork stays centred whatever its proportions */
.feature .frame {
  position: relative;
  height: clamp(320px, 46vh, 520px);
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.feature .frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 10px;
  box-sizing: border-box;
  object-fit: contain;
  display: block;
}
.feature figcaption {
  margin-top: 12px;
  color: var(--grey);
  font-size: 13px;
  font-weight: 300;
}
/* text starts level with the top of the image, not centred against it */
.feature .copy { align-self: start; }
.feature .copy p { line-height: 1.8; color: #333; max-width: none; margin-top: -0.3em; }
@media (max-width: 1100px) {
  .feature-row { grid-template-columns: minmax(0, 0.6fr) minmax(0, 1fr); row-gap: 48px; }
}
@media (max-width: 700px) {
  .feature-row { grid-template-columns: 1fr; row-gap: 28px; }
  .feature .frame { height: clamp(280px, 44vh, 420px); }
}

/* real images fill their frames */
.proj-intro .hero-img img,
.process .frame img,
.carousel .slide .frame img,
.project-card .thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ── SITE ANALYSIS  landscape swipe carousel inside a project page ── */
/* heading sits on the same line as the arrows */
.site-analysis .sa-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 26px;
}
.site-analysis .sa-head h2 { margin-bottom: 0; }
.site-analysis .car-btns { padding: 0; }
.site-analysis .carousel { padding: 0 0 20px; gap: 24px; }
/* drawings stay a comfortable size; the strip still runs off the right edge */
.site-analysis .carousel .slide { width: min(880px, calc(100% - 56px)); }
.site-analysis .carousel .slide .frame {
  aspect-ratio: 1600 / 1035;
  border-radius: 12px;
}
.site-analysis .carousel .slide p .n { font-weight: 400; color: var(--ink); margin-right: 8px; }
/* gallery: every slide shares one height and takes its own width from the
   image, so wide sections and portrait model shots both sit flush with no
   letterboxing and nothing gets cropped */
.gallery .carousel { gap: 16px; }
.gallery .carousel .slide {
  width: auto;
  flex: 0 0 auto;
}
.gallery .carousel .slide .frame {
  height: clamp(220px, 32vh, 380px);
  width: auto;
  aspect-ratio: auto;
  display: inline-block;
  background: #111;
  border-radius: 12px;
  overflow: hidden;
}
.gallery .carousel .slide .frame img {
  position: static;
  inset: auto;
  height: 100%;
  width: auto;
  max-width: none;
  object-fit: contain;
  display: block;
}
.gallery .carousel .slide p { max-width: 40ch; }
.gallery .carousel .slide p .n { font-weight: 400; color: var(--ink); margin-right: 8px; }

.site-note {
  margin: 40px 0 0;
  max-width: none;
  line-height: 1.85;
  color: #333;
}
@media (max-width: 860px) {
  .site-analysis .carousel .slide { width: calc(100% - 56px); }
}

/* what if */
.whatif {
  margin: 0 0 100px;
  padding: clamp(48px, 8vh, 90px) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
/* question and text on the left, the 14th street chart alongside it */
.whatif-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}
.whatif .statement {
  font-size: clamp(24px, 3vw, 40px);
  font-weight: 300;
  font-style: italic;
  letter-spacing: -0.02em;
  line-height: 1.25;
  max-width: 20ch;
}
.whatif .follow { margin-top: 28px; color: var(--grey); max-width: none; line-height: 1.7; }
@media (max-width: 1000px) {
  .whatif-row { grid-template-columns: 1fr; row-gap: 44px; }
}

/* additional content */
.more { margin-bottom: 110px; }
.more .cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 40px;
}
.more .cols h3 { font-weight: 400; font-size: 15px; margin-bottom: 12px; }
.more .cols p { color: #333; line-height: 1.75; }

/* stub project pages */
.stub-note {
  margin: 60px 0 90px;
  padding: clamp(40px, 7vh, 80px);
  border: 1px dashed var(--line);
  border-radius: 14px;
  text-align: center;
  color: var(--grey);
  line-height: 1.8;
}

/* ── FILL-IN SLOTS ───────────────────────────────────────────────
   <p class="fill" data-hint="what goes here"></p>
   The hint only shows while the element is empty. Type anything
   between the tags and it styles like normal body copy.
   When a page is finished you can delete the class + data-hint. */
.fill:empty::before {
  content: attr(data-hint);
  color: #b9b9b9;
  font-style: normal;
}
.fill:empty {
  display: inline-block;
  min-width: 6ch;
  border-bottom: 1px dashed #d6d6d6;
  padding-bottom: 2px;
}
p.fill:empty { display: block; }
.fill:empty:hover { border-bottom-color: var(--ink); }
.fill:empty:hover::before { color: var(--grey); }
/* meta-row spans are styled as labels  a fill slot there is a value, not a label */
.proj-head .meta-row div span.fill {
  display: inline-block;
  font-size: 14px;
  font-weight: 300;
  color: var(--ink);
  margin-bottom: 0;
}
@media print { .fill:empty { display: none; } }

/* ================================================================
   DATA VIZ  forecast + history only
   ================================================================ */
.viz-section { margin: 0; }
.viz-title {
  font-size: 14px;
  font-weight: 300;
  color: var(--grey);
  margin-bottom: 18px;
}
.viz-shell { border: 1px solid var(--line); border-radius: 16px; overflow: hidden; max-width: none; }
.viz-tabs { display: flex; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.viz-tabs button {
  font-family: inherit;
  font-weight: 300;
  font-size: 13px;
  padding: 16px 26px;
  border: none;
  border-right: 1px solid var(--line);
  background: none;
  color: var(--grey);
  cursor: pointer;
  transition: all .25s;
}
.viz-tabs button:hover { color: var(--ink); }
.viz-tabs button.on { color: #fff; background: var(--ink); }
.viz-pane { display: none; padding: clamp(24px, 4vw, 44px); }
.viz-pane.on { display: block; animation: fadeUp .5s var(--ease); }
@keyframes fadeUp { from { opacity: 0; transform: translateY(14px);} to { opacity:1; transform:none; } }

.viz-pane .pane-head { margin-bottom: 26px; max-width: 70ch; }
.viz-pane .pane-head h3 { font-weight: 400; font-size: 16px; margin-bottom: 8px; }
.viz-pane .pane-head p { color: var(--grey); line-height: 1.65; font-size: 13.5px; }

.viz-svg { width: 100%; height: auto; max-width: none; }
.viz-svg text { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; }
.viz-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 24px;
  margin-top: 18px;
  font-size: 12px;
  font-weight: 300;
  color: var(--grey);
}
.viz-legend span { display: inline-flex; align-items: center; gap: 8px; }
.viz-legend i {
  width: 24px;
  height: 0;
  border-top-width: 2px;
  display: inline-block;
}
.viz-note { margin-top: 18px; color: var(--grey); font-size: 12px; line-height: 1.6; }

#vizTip {
  position: fixed;
  z-index: 900;
  pointer-events: none;
  background: var(--ink);
  color: #fff;
  font-size: 12px;
  line-height: 1.5;
  padding: 8px 12px;
  border-radius: 8px;
  opacity: 0;
  transform: translate(-50%, -120%);
  transition: opacity .15s;
  white-space: nowrap;
}
#vizTip.on { opacity: 1; }

/* history timeline */
.timeline { border-left: 1px solid var(--line); margin-left: 6px; }
.timeline .ev { position: relative; padding: 0 0 34px 28px; }
.timeline .ev::before {
  content: '';
  position: absolute;
  left: -4.5px; top: 6px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--ink);
}
.timeline .ev .when { font-size: 12px; font-weight: 300; color: var(--grey); }
.timeline .ev h4 { font-weight: 400; margin: 6px 0; }
.timeline .ev p { color: var(--grey); line-height: 1.65; font-size: 13.5px; max-width: 58ch; }

/* ================================================================
   UNSCRIPTED  plan oblique, environmental pods, kit of parts
   ================================================================ */
/* tall plan oblique beside its text */
.oblique-row {
  display: grid;
  grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: start;
  margin-bottom: clamp(44px, 6vh, 72px);
}
.oblique-row figure { margin: 0; }
.oblique-row .frame {
  position: relative;
  aspect-ratio: 759 / 2000;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.oblique-row .frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 10px;
  box-sizing: border-box;
  display: block;
}
.oblique-row figcaption,
.pods .pod-row figcaption {
  margin-top: 12px;
  color: var(--grey);
  font-size: 13px;
  font-weight: 300;
  line-height: 1.6;
}
.oblique-row figcaption .n { font-weight: 400; color: var(--ink); margin-right: 6px; }
.oblique-row .copy p { line-height: 1.8; color: #333; margin-bottom: 16px; }

/* intro: text + catalogue on the left, plan oblique on the right */
.proj-intro.un-intro { grid-template-columns: minmax(0, 1fr) minmax(0, 460px); }
.proj-intro.un-intro .blurb { position: static; }
.proj-intro.un-intro .oblique { margin: 0; }
.proj-intro.un-intro .oblique .frame {
  position: relative;
  aspect-ratio: 875 / 1800;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.proj-intro.un-intro .oblique .frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.proj-intro.un-intro .oblique figcaption {
  margin-top: 10px;
  color: var(--grey);
  font-size: 12.5px;
  font-weight: 300;
}
.proj-intro.un-intro .oblique figcaption .n { color: var(--ink); margin-right: 5px; }

/* catalogue of parts, sitting under the intro copy, caption above */
.kit-cat { margin: clamp(40px, 6vh, 68px) 0 0; }
.kit-cat figcaption {
  margin: 0 0 10px;
  color: var(--grey);
  font-size: 12.5px;
  font-weight: 300;
}
.kit-cat .frame {
  position: relative;
  aspect-ratio: 2600 / 1014;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.kit-cat .frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 12px;
  box-sizing: border-box;
  display: block;
}
/* kit of parts tabs, directly below the catalogue */
.kit-shell { max-width: none; margin-top: 18px; }
.kit-shell .viz-pane { padding: clamp(16px, 2vw, 24px); }
/* only the active pane becomes a grid, so the rest stay display:none */
.kit-shell .viz-pane.on {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(16px, 2.2vw, 30px);
  align-items: start;
}
.kit-shell .viz-tabs button { padding: 12px 16px; font-size: 12.5px; }
.kit-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.kit-grid figure { margin: 0; }
.kit-grid .frame {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.kit-grid.mounds .frame { aspect-ratio: 4 / 3; }
.kit-grid .frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 8px;
  box-sizing: border-box;
  display: block;
}
.kit-copy h4 { font-weight: 400; font-size: 14px; margin-bottom: 10px; }
.kit-copy p { color: #333; line-height: 1.7; font-size: 13.5px; }
.eq {
  margin-top: 12px;
  font-size: 12.5px;
  color: var(--ink);
  line-height: 1.9;
  padding: 10px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.eq-note { margin-top: 10px; color: var(--grey) !important; font-size: 12.5px; }
.eq-note em { font-style: italic; }

/* ── ENVIRONMENTAL PODS ──────────────────────────────────────────
   axon takes a quarter and spans the block; the remaining three
   quarters is two rows  icon + diagram on top, section below. */
.pods .pods-intro {
  color: #333;
  line-height: 1.75;
  max-width: 80ch;
  margin: -18px 0 clamp(28px, 4vh, 44px);
}
/* The right column's frames carry explicit aspect-ratios, so its height is
   always definite. The left column stretches to match it: the axon frame
   grows to fill whatever is left after the caption and lead text, which
   keeps the bottom edges aligned with no trailing white block. */
.pod {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.8fr);
  column-gap: clamp(14px, 1.4vw, 20px);
  row-gap: 14px;
  align-items: stretch;
  margin-bottom: clamp(56px, 7.5vh, 92px);
}
.pod:last-child { margin-bottom: 0; }
.pod-title {
  grid-column: 1 / -1;
  font-size: 14px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--line);
}
.pod-title .n { color: var(--grey); margin-right: 8px; }
.pod figure { margin: 0; }
.pod .frame {
  position: relative;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #fff;
  overflow: hidden;
}
.pod .frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 12px;               /* the white border space */
  box-sizing: border-box;
  display: block;
}

/* left: axon at its own proportions, caption and explanation under it */
.pod-left {
  grid-column: 1;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pod-left .ax .frame { aspect-ratio: 1027 / 1500; }
.pod-left .lead {
  color: #333;
  font-size: 13px;
  line-height: 1.65;
}

/* right: icon + data diagram on top, section perspective below */
.pod-right {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.6vw, 22px);
}
/* the diagram frame sets the height; the icon stretches to match it exactly,
   so the square is never left floating against white */
.pod-meta { margin: 0; }
/* The diagram is 2000x417 (4.796:1). Solving for an icon whose side equals
   the diagram's rendered height gives side = (row width - gap) / 5.796,
   so the square always lines up exactly with the diagram beside it. */
.icon-row {
  display: grid;
  grid-template-columns: calc((100% - 16px) / 5.796) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}
.icon-row .pod-icon {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  display: block;
  object-fit: cover;
}
.icon-row .frame { aspect-ratio: 2000 / 417; }
/* the perspective spans the whole right column and stays close to its own
   proportions, so it reads as the dominant drawing in the block */
.pod-right .sec .frame { aspect-ratio: 3 / 2; }
.pods figcaption {
  flex: 0 0 auto;
  margin-top: 7px;
  color: var(--grey);
  font-size: 12px;
  font-weight: 300;
  line-height: 1.5;
}

.whatif-copy.narrow .statement { max-width: 22ch; }
.whatif-copy.narrow .follow { max-width: 72ch; }

@media (max-width: 900px) {
  .proj-intro.un-intro { grid-template-columns: 1fr; }
  .kit-shell .viz-pane.on { grid-template-columns: 1fr; row-gap: 18px; }
  .pod { grid-template-columns: 1fr; row-gap: 16px; }
  .pod-left, .pod-right { grid-column: 1; }
  .pod-left .ax .frame { aspect-ratio: 1027 / 1500; flex: 0 0 auto; }
  .pod-right .sec .frame { aspect-ratio: 3 / 2; }
}

/* ================================================================
   INDEX PAGE, justified rows
   ================================================================ */
.gallery-page .proj-head { margin-bottom: clamp(40px, 6vh, 72px); }
.index-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  padding-bottom: clamp(60px, 9vh, 100px);
}
.index-grid figure { margin: 0; }
.index-grid .frame {
  position: relative;
  height: var(--fh, 380px);
  border-radius: 10px;
  overflow: hidden;
  background: #f2f2f2;
  cursor: zoom-in;
}
.index-grid .frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;      /* exact-fit rows, so this never actually crops */
  display: block;
  transition: transform .8s var(--ease);
}
.index-grid figure:hover .frame img { transform: scale(1.02); }
.index-empty {
  color: var(--grey);
  font-size: 14px;
  padding: 40px 0;
}

/* ================================================================
   MIST LAB
   ================================================================ */
.site-link { margin-top: 22px; font-size: 13.5px; }
.site-link a {
  color: var(--grey);
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  transition: color .2s, border-color .2s;
}
.site-link a:hover { color: var(--ink); border-bottom-color: var(--ink); }
/* \FE0E is the text-presentation selector: without it iOS draws U+2197
   as the colour emoji arrow */
.site-link a::after { content: ' \2197\FE0E'; font-size: .85em; }

/* A justified row. The three source images have genuinely different
   proportions, so equal columns either cropped them or left dead ground
   above and below. Instead each figure grows in proportion to its own
   aspect ratio, which lands every image at the same height, spans the
   full width, and crops nothing. */
.ml-row {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(18px, 2.4vw, 30px);
  align-items: flex-start;
}
.ml-row figure {
  flex: var(--r, 1) 1 0;
  min-width: 200px;
}
.ml-row figure { margin: 0; }
/* The aerial is a tall portrait plate. Left to its own proportions it ran
   well past the bottom of the text and turned the intro into a long
   scroll. The text column widens to take back roughly half the row, which
   shortens the copy and so shortens the drawing with it, and the height is
   capped to the viewport so the whole intro lands on one screen. */
.proj-intro.ml-intro {
  /* text takes the larger share so the plan sits smaller and off to the
     side. The plan then keeps its own proportions rather than being
     stretched to the text's height, so nothing is cropped. */
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
  align-items: start;
}
.proj-intro.ml-intro .blurb { position: static; }
.proj-intro.ml-intro .hero-img {
  aspect-ratio: 1800 / 2094;
  height: auto;
  background: none;
}

/* No drawn box and no empty ground: each frame takes its own image's
   proportions, so the image defines the box instead of being letterboxed
   inside a fixed one. */
.ml-row .frame {
  position: relative;
  /* .process .frame imposes a 3/2 box and clips to it; these images have
     their own proportions, so that box has to be released explicitly.
     overflow stays hidden so the rounded corners actually clip the image,
     and since the frame now takes the image's height it crops nothing. */
  aspect-ratio: auto;
  overflow: hidden;
  border-radius: 12px;
  background: none;
}
.ml-row .frame img {
  position: static;
  width: 100%;
  height: auto;
  object-fit: fill;
  display: block;
}
/* Two of these plates carry a thin white page margin baked into the jpg
   itself, left over from the pdf they were lifted out of. Properly this
   wants re-cropping the file; a small scale pushes the margin outside the
   frame, which clips it, at the cost of about 2% of the artwork's edge. */
.ml-row .trim img { transform: scale(1.05); transform-origin: center; }

@media (max-width: 860px) {
  .proj-intro.ml-intro { grid-template-columns: 1fr; }
  .proj-intro.ml-intro .hero-img { max-height: 60vh; }
}
.ml-row figcaption {
  margin-top: 10px;
  color: var(--grey);
  font-size: 12.5px;
  font-weight: 300;
  line-height: 1.55;
}
.ml-row figcaption .n { color: var(--ink); margin-right: 8px; }

.credits-note {
  color: #333;
  line-height: 1.75;
  max-width: 78ch;
  margin: -14px 0 clamp(30px, 4vh, 46px);
}
.credits .cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: clamp(22px, 3vw, 40px);
}
.credits .cols h3 {
  font-weight: 400;
  font-size: 12.5px;
  color: var(--grey);
  margin-bottom: 8px;
}
.credits .cols p { color: var(--ink); line-height: 1.7; font-size: 14px; }

/* ── SECTION SEPARATORS on project pages ─────────────────────────
   one hairline rule between every top-level section, with matching
   space either side of it. Declared late so it wins over the
   per-section margins above. */
.proj > section {
  border-top: 1px solid var(--line);
  border-bottom: none;
  margin: clamp(64px, 9.5vh, 106px) 0 0;
  padding: clamp(64px, 9.5vh, 106px) 0 0;
}
.proj > section:last-child { padding-bottom: clamp(68px, 10vh, 116px); }

/* ── HERO exploring (moved under Hi, I'm Dorothy) ───────────── */
.hero .exploring {
  padding: 0;
  border: none;
  margin-top: 36px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.hero .exploring .line {
  font-size: clamp(20px, 3vw, 38px);
  max-width: none;
}
.hero .exploring .tags { margin-top: 28px; justify-content: center; }

/* ── ABOUT intro (unlabeled, no rules  index-style below) ──── */
.about-intro { padding: 0 0 48px; }
.about-intro .statement {
  font-size: clamp(26px, 3.6vw, 46px);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1.3;
  /* runs about two thirds of the page; the ch cap keeps the line from
     growing past a comfortable measure on very wide screens */
  max-width: min(40ch, 66%);
  margin-bottom: 22px;
}
.about-intro .sub {
  font-size: clamp(16px, 1.5vw, 20px);
  font-weight: 300;
  line-height: 1.65;
  color: #333;
  margin-bottom: 24px;
}
.about-intro p { line-height: 1.8; color: #333; max-width: 66ch; margin-bottom: 14px; }
/* these two sit within the same two thirds as the statement above them */
.about-intro .sub,
.about-intro .quiet { max-width: 66%; }
@media (max-width: 860px) {
  .about-intro .quiet { max-width: 100%; }
}
@media (max-width: 860px) {
  .about-intro .statement,
  .about-intro .sub { max-width: 100%; }
}
.about-intro .quiet { color: var(--grey); font-style: italic; }
.about-intro .now-listening { max-width: 420px; margin-top: 10px; }
.about-intro .listening-label {
  margin-top: 30px;
  color: var(--grey);
  font-size: 13px;
  font-weight: 300;
}

/* cv org links */
.cv-item .org a {
  border-bottom: 1px solid transparent;
  transition: border-color .2s;
}
.cv-item .org a:hover { border-bottom-color: var(--ink); }
.cv-item .org a::after { content: ' \2197\FE0E'; font-size: .8em; color: var(--grey); }

/* ── LIGHTBOX (gallery click-to-expand) ─────────────────────── */
#lightbox {
  position: fixed;
  inset: 0;
  z-index: 1100;
  background: rgba(255,255,255,.97);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s;
}
#lightbox.on { opacity: 1; pointer-events: all; }
#lightbox .lb-content {
  width: min(92vw, 1200px);
  max-height: 86vh;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  overflow: hidden;
}
/* placeholders still need a box; real images size themselves */
#lightbox .lb-content .ph {
  position: relative;
  inset: auto;
  width: 100%;
  aspect-ratio: 4 / 5;
  background: #f2f2f2;
}
#lightbox .lb-content img {
  position: static;
  width: auto;
  max-width: 100%;
  max-height: 86vh;
  object-fit: contain;
}
#lightbox .lb-caption { position: absolute; bottom: 26px; left: 50%; transform: translateX(-50%); color: var(--grey); font-size: 13px; }
#lightbox .lb-close {
  position: absolute;
  top: 20px; right: 26px;
  font: inherit;
  font-size: 20px;
  font-weight: 300;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--ink);
}
/* lightbox prev / next */
#lightbox .lb-prev,
#lightbox .lb-next {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px; height: 46px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(255,255,255,.9);
  font: inherit;
  font-size: 17px;
  font-weight: 300;
  color: var(--ink);
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
  transition: border-color .2s, background .2s;
  z-index: 2;
}
#lightbox.has-nav .lb-prev,
#lightbox.has-nav .lb-next { display: flex; }
#lightbox .lb-prev { left: clamp(14px, 3vw, 44px); }
#lightbox .lb-next { right: clamp(14px, 3vw, 44px); }
#lightbox .lb-prev:hover,
#lightbox .lb-next:hover { border-color: var(--ink); background: #fff; }
#lightbox .lb-count {
  position: absolute;
  bottom: 26px;
  right: clamp(16px, 3vw, 46px);
  color: var(--grey);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.carousel .slide .frame { cursor: zoom-in; }
/* any frame the lightbox has claimed */
.zoomable { cursor: zoom-in; }
.zoomable:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 3px;
}
#lightbox .lb-caption {
  max-width: min(92vw, 900px);
  text-align: center;
  line-height: 1.6;
}

/* ================================================================
   NEON PINK HOVER
   Anything clickable goes neon on hover. This block sits last on
   purpose: several components set their own hover colour earlier in
   the file, and these need to win without raising specificity.
   ================================================================ */
:root {
  --neon: #ff2d9b;
  --neon-soft: #ff6ad5;
}

/* Deliberately narrow. Neon belongs to the site chrome (topbar, footer)
   and to the home page. Inside a project article the artwork is the
   subject, so links, tabs, carousel arrows, lightbox controls and image
   outlines there keep their original ink treatment. */

#topbar nav a:hover,
#topbar .brand:hover { color: var(--neon); }

footer .links a:hover { color: var(--neon); }
/* the footer and contact svgs are drawn in currentColor, so they follow along */

/* home page only */
.more-work:hover .label,
.more-work:hover .arw { color: var(--neon); }

.cv-item .org a:hover { color: var(--neon); border-bottom-color: var(--neon); }
.cv-item .org a:hover::after { color: var(--neon); }

.contact-links a:hover { color: var(--neon); }
.exploring .tags button:hover { border-color: var(--neon); color: var(--neon); }

/* project card titles stay white on their photo, no neon */

/* ================================================================
   FAKE FLIP BOOK (dog's tail intro)
   A stack of pages with the spine at the left edge. The top page swings
   away on a timer and drops to the back, so it reads as a loop rather
   than a slideshow.
   ================================================================ */
/* the book sits on black, so the pages read as lit paper */
/* The card is a wide band and the page is portrait, so only a narrow
   slice shows. Sit it low enough to lead with the elephant's head and
   catch the tip of the petal above it. */
.project-card.dt-card .thumb img { object-position: center 30%; }

.flip-stage {
  background: #0c0c0c;
  border-radius: 14px;
  padding: clamp(40px, 6vw, 90px) clamp(20px, 4vw, 60px);
  display: flex;
  justify-content: center;
}

.flipbook {
  position: relative;
  aspect-ratio: 3 / 2;          /* the open spread; shut, it shows half */
  width: 100%;
  max-width: 700px;
  margin: 0 auto;
  align-self: start;
  perspective: 2400px;
  cursor: pointer;
}
/* Shut, only the right half carries a sheet, so the whole book slides
   over to sit centred on the cover. It slides back as it opens, and on
   again when every leaf has landed on the left. */
.fb-book {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform .8s var(--ease);
}
.flipbook[data-state="shut"] .fb-book { transform: translateX(-25%); }
.flipbook[data-state="open"] .fb-book { transform: translateX(0); }
.flipbook[data-state="done"] .fb-book { transform: translateX(25%); }

/* one sheet: two pages, back to back, hinged at the spine */
.fb-leaf {
  position: absolute;
  top: 0;
  right: 0;
  width: 50%;
  height: 100%;
  transform-origin: left center;
  transform-style: preserve-3d;
  transition: transform 1.05s cubic-bezier(.45, 0, .25, 1);
  will-change: transform;
}
.fb-leaf.turned { transform: rotateY(-180deg); }

/* no border: on black a light rule read as a white outline round each
   page. The pages are 3:4 and so is the sheet, so they fill it exactly
   and no ground shows through anyway. */
.fb-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  overflow: hidden;
  background: transparent;
}
.fb-front { border-radius: 0 8px 8px 0; }
.fb-back  { border-radius: 8px 0 0 8px; transform: rotateY(180deg); }

.flipbook img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* the crease only exists while the book is actually open */
.fb-spine {
  position: absolute;
  top: 0; bottom: 0; left: 50%;
  width: 18px;
  margin-left: -9px;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity .5s ease;
  background: linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.08) 45%, rgba(0,0,0,.08) 55%, rgba(0,0,0,0) 100%);
}
.flipbook[data-state="open"] .fb-spine { opacity: 1; }

/* ================================================================
   ZINE PAGE GRID (dog's tail)
   The pages are all the same portrait sheet, so an even grid is the
   honest layout: it reads as a set of spreads rather than a slideshow.
   ================================================================ */
/* copy held narrow so the flip book has room to sit beside it */
.proj-intro.dt-intro {
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.7fr);
  align-items: start;
  gap: clamp(24px, 3.5vw, 56px);
}
.proj-intro.dt-intro .blurb { position: static; }

.zine-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(12px, 1.4vw, 20px);
}
.zine-grid figure { margin: 0; }
.zine-grid .frame {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: 10px;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--line);
}
.zine-grid .frame img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.zine-grid figcaption {
  margin-top: 10px;
  color: var(--grey);
  font-size: 12.5px;
  font-weight: 300;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 860px) {
  .proj-intro.dt-intro { grid-template-columns: 1fr; }
}
@media (max-width: 560px) { .zine-grid { grid-template-columns: repeat(2, 1fr); } }

/* the one link inside a project page that does take neon */
.site-link a:hover { color: var(--neon); border-bottom-color: var(--neon); }

/* selected text is pink everywhere, including the project pages */
::selection { background: var(--neon); color: #fff; }
