/* ═══════════════════════════════════════════════════════════
   Work index + project detail pages
   Inherits the palette and type from styles.css
   ═══════════════════════════════════════════════════════════ */

/* ── Breadcrumbs ─────────────────────────────────────────── */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 2rem;
  font-size: 0.75rem;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}
.crumbs a {
  color: var(--text-dim);
  text-decoration: none;
  transition: var(--transition);
}
.crumbs a:hover { color: var(--accent); }
.crumbs span { color: var(--text-muted); }
.crumb-current { color: var(--text); font-weight: 600; }

/* ── Shared page headers ─────────────────────────────────── */
.work-header,
.proj-header {
  padding: 7rem 0 3rem;
  border-bottom: 1px solid var(--border);
  position: relative;
  overflow: hidden;
}
.work-header::before,
.proj-header::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 15% 40%, var(--accent-glow) 0%, transparent 55%);
  pointer-events: none;
}
.work-header > .container,
.proj-header > .container { position: relative; }

.work-title,
.proj-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-stretch: var(--display-stretch);
  text-transform: uppercase;
  font-size: clamp(2.5rem, 7vw, 5rem);
  line-height: 1;
  letter-spacing: -0.01em;
}
.work-sub,
.proj-summary {
  max-width: 620px;
  margin-top: 1rem;
  color: var(--text-dim);
  font-size: 1.05rem;
  line-height: 1.6;
}

/* ── Work index: context tabs ────────────────────────────────
   The primary split, so these read heavier than the division toggle
   and the category filters below them. Underline rather than pill:
   a tab bar should look like it changes what you are looking at,
   not like one more filter chip. */
.work-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 2rem;
  border-bottom: 1px solid var(--border);
}
.work-tab {
  position: relative;
  padding: 0.7rem 1.1rem;
  bottom: -1px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.3px;
  cursor: pointer;
  transition: var(--transition);
}
.work-tab:hover { color: var(--text); }
.work-tab.active {
  color: var(--text);
  border-bottom-color: var(--accent);
}
.work-tab .filter-count {
  margin-left: 0.4rem;
  color: var(--text-muted);
  font-size: 0.75rem;
  font-weight: 500;
}
.work-tab.active .filter-count { color: var(--accent); }

@media (max-width: 600px) {
  .work-tab { padding: 0.6rem 0.75rem; font-size: 0.82rem; }
}

/* ── Work index: search ──────────────────────────────────── */
.work-search {
  width: 100%;
  max-width: 460px;
  margin-top: 2rem;
  padding: 0.85rem 1.25rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 100px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.9rem;
  transition: var(--transition);
}
.work-search::placeholder { color: var(--text-muted); }
.work-search:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.work-header .gallery-filters { margin-top: 1.5rem; margin-bottom: 0; }

.work-count {
  margin-top: 1.25rem;
  color: var(--text-muted);
  font-size: 0.75rem;
  letter-spacing: 2px;
  text-transform: uppercase;
}

/* ── Work index: grid ────────────────────────────────────── */
/* Same double-class trap as .proj-main - the grid was reaching the
   viewport edge with no gutter. Max-width is left at .container's 1400px
   here, because a browsable index benefits from the extra columns. */
.work-main { padding: 3rem 2rem 6rem; }

.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1.75rem;
}

.work-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: var(--transition);
}
/* Cards keep their chrome, and deliberately so. This is the inverse of
   the rule applied to artwork: a card is a target, not a piece of work,
   and the border plus lift is what tells you it can be clicked. */
.work-card:hover {
  border-color: var(--accent);
  transform: translateY(-4px);
  box-shadow: 0 12px 32px var(--shadow);
}

/* ── Home page: bento ───────────────────────────────────────
   Only the home page. It is a highlights reel, so letting the stronger
   work claim more room gives the grid a rhythm; the /work/ index stays
   uniform because a dense filterable record is easier to scan when
   every row is the same height.

   Four fixed columns rather than auto-fill, because a span of 2 needs a
   known column count to land predictably. Declared after .work-grid at
   equal specificity, so source order decides - no !important. */
.bento { grid-template-columns: repeat(4, 1fr); }
.bento .bento-lg { grid-column: span 2; }
/* Wider crop, not a taller tile. Spanning rows would need fixed row
   heights, and every card carries a different length of text under the
   image, so the rows would either clip or leave gaps. */
.bento .bento-lg .work-card-img { aspect-ratio: 16 / 10; }

@media (max-width: 1100px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .bento { grid-template-columns: 1fr; }
  /* One column: a span of 2 would overflow the grid. */
  .bento .bento-lg { grid-column: span 1; }
  .bento .bento-lg .work-card-img { aspect-ratio: 4 / 3; }
}

.work-card-img {
  aspect-ratio: 4 / 3;
  background: var(--bg);
  overflow: hidden;
}
.work-card-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.5s ease;
}
.work-card:hover .work-card-img img { transform: scale(1.05); }

/* A mark is the wrong shape for a 4:3 crop - a wide wordmark loses both
   ends to object-fit: cover. Contain it instead, on the white it was
   drawn against, with room at the edges so it reads as a logo and not a
   detail shot. */
.work-card-img.is-mark {
  background: #fff;
}
.work-card-img.is-mark img {
  object-fit: contain;
  padding: 2rem 2.25rem;
  box-sizing: border-box;
}
/* The hover zoom would push a contained mark back under the crop. */
.work-card:hover .work-card-img.is-mark img { transform: none; }

.work-card-noimg {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-muted);
  font-size: 0.8rem;
  letter-spacing: 2px;
  text-transform: uppercase;
}

.work-card-info {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 1.35rem 1.5rem 1.5rem;
  flex: 1;
}
.work-card-client {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--accent);
}
.work-card-title {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.3;
}
.work-card-summary {
  font-size: 0.875rem;
  color: var(--text-dim);
  line-height: 1.55;
}
.work-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: auto;
  padding-top: 0.9rem;
  color: var(--text-muted);
  font-size: 0.7rem;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}

.work-empty {
  padding: 4rem 0;
  text-align: center;
  color: var(--text-dim);
}

/* ── Project detail ──────────────────────────────────────── */
.proj-client {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.75rem;
}

.proj-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.75rem;
}
.proj-meta-item {
  padding: 0.4rem 1rem;
  border: 1px solid var(--border);
  border-radius: 100px;
  color: var(--text-dim);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}

/* This element carries BOTH .container and .proj-main, so the shorthand
   below was silently cancelling .container's `0 2rem` and letting content
   run to the viewport edge - invisible on a wide desktop, but on a phone
   the copy touched both sides of the screen. */
.proj-main { padding: 3.5rem 2rem 6rem; }

/* One measure for the whole write-up. Copy and figures share it, so a
   project page reads as a single column rather than text at reading
   width with images sprawling to the full container behind it. Change
   this one value to widen or narrow the whole page. */
:root {
  --measure: 680px;
  /* The widest a figure is allowed to run, matching the is-wide tier and
     SHAPE_CAP in build.py. */
  --column: 1180px;
  /* .container is border-box, so its max-width has to carry its own
     2rem side padding for the content box to land on --column. */
  --page-max: calc(var(--column) + 4rem);
}

/* A project page is one centred column, not a left-pinned block inside a
   1400px shell. Previously the copy sat at 680px hard against the left
   edge of a near-full-width container, so on a 1440px display roughly
   half the screen was empty and the first thing a reader saw was dead
   space. Centring the column turns that into symmetric gutters.

   Header and main share the value deliberately: narrowing only one would
   leave the project title out of alignment with the copy beneath it.

   Note the selectors are not symmetrical, because the markup is not:
   the header wraps a child .container, while main IS the container
   (`<main class="container proj-main">`). A `.proj-main > .container`
   rule matches nothing at all. */
.proj-header > .container,
.proj-main {
  max-width: var(--page-max);
}

.proj-body {
  max-width: var(--measure);
  margin-bottom: 3.5rem;
}
.proj-body p {
  color: var(--text-dim);
  font-size: 1.05rem;
  line-height: 1.8;
  margin-bottom: 1.5rem;
}
.proj-body p:first-child {
  color: var(--text);
  font-size: 1.15rem;
}
.proj-body h2 {
  font-family: var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: 1.5rem;
  letter-spacing: 0.5px;
  margin: 2.5rem 0 1rem;
}
.proj-body ul {
  margin: 0 0 1.5rem 1.25rem;
  color: var(--text-dim);
  line-height: 1.8;
}
.proj-body li { margin-bottom: 0.5rem; }

.proj-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 2rem;
}
.proj-tag {
  padding: 0.3rem 0.8rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-muted);
  font-size: 0.7rem;
  letter-spacing: 1px;
  text-transform: uppercase;
}

/* ── Division toggle ─────────────────────────────────────── */
/* Primary split between Business and Arts & Entertainment. Sits above
   the category filters and is visually heavier than them. */
.division-toggle {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.3rem;
  margin: 0 0 1.75rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  flex-wrap: wrap;
}
.div-btn {
  padding: 0.55rem 1.4rem;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: var(--transition);
  white-space: nowrap;
}
.div-btn:hover { color: var(--text); }
.div-btn.active {
  background: var(--accent);
  color: var(--bg);
}
.div-btn.active .filter-count { color: inherit; opacity: 0.75; }

/* ── Work-type badges ────────────────────────────────────── */
/* Student, competition and concept work is always labelled. These are
   generated by build.py, never hand-written, so they cannot drift. */
.proj-badge {
  display: inline-block;
  margin-bottom: 0.85rem;
  padding: 0.35rem 0.85rem;
  border: 1px solid var(--accent-dim);
  border-radius: 999px;
  /* Was a 3% white wash, which is a dark-mode assumption: on the light
     theme's off-white surface it rendered as nothing and the badge lost
     its fill entirely. --surface-2 is a step away from the canvas in
     either direction. */
  background: var(--surface-2);
  color: var(--accent);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.proj-outcome {
  margin: 0.75rem 0 0;
  color: var(--accent);
  font-weight: 600;
}

/* Stated plainly, above the write-up - not hidden in the footer. */
.proj-disclaimer {
  margin: 0 0 2rem;
  padding: 0.9rem 1.2rem;
  border-left: 3px solid var(--accent-dim);
  background: var(--bg-card);
  border-radius: 0 var(--radius) var(--radius) 0;
  color: var(--text-dim);
  font-size: 0.9rem;
  line-height: 1.6;
}

/* Pull quote - for the strategic line a project actually hangs on. */
.proj-body blockquote {
  margin: 2.25rem 0;
  padding: 0.25rem 0 0.25rem 1.5rem;
  border-left: 3px solid var(--accent);
  color: var(--text);
  font-size: 1.35rem;
  font-style: italic;
  font-weight: 500;
  line-height: 1.45;
}
@media (max-width: 600px) {
  .proj-body blockquote { font-size: 1.15rem; }
}

/* Client testimonial - closes the write-up. Given real weight, because
   an attributed client verdict is the hardest thing in a portfolio to
   manufacture and the easiest for a reader to trust. */
.proj-testimonial {
  margin: 2.5rem 0 0;
  padding: 1.6rem 1.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-card);
}
.proj-testimonial blockquote {
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--text);
  font-size: 1.15rem;
  font-style: italic;
  line-height: 1.55;
}
.proj-testimonial blockquote::before { content: "\201C"; }
.proj-testimonial blockquote::after  { content: "\201D"; }
.proj-testimonial figcaption {
  margin-top: 0.9rem;
  color: var(--text-muted);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.proj-testimonial figcaption::before { content: "\2014\00a0"; }

/* Credits lead the write-up on directed work: who ran it, who built it. */
.proj-credits {
  margin: 0 0 2rem;
  padding: 1.1rem 1.4rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--bg-card);
}
.proj-credit-row + .proj-credit-row {
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--border);
}
.proj-credits-label {
  display: block;
  margin-bottom: 0.25rem;
  color: var(--text-muted);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.proj-credit-name {
  color: var(--text-dim);
  font-size: 0.9rem;
  line-height: 1.5;
}
/* The directing role is the point of the block, so it carries the weight. */
.proj-credit-row.is-lead .proj-credits-label { color: var(--accent); }
.proj-credit-row.is-lead .proj-credit-name {
  color: var(--text);
  font-size: 1rem;
  font-weight: 600;
}

.work-card-img { position: relative; }
.work-card-badge {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.78);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  backdrop-filter: blur(4px);
}

/* ── Embedded web pages (Internet Archive captures) ──────── */
/* A live frame of the archived page, rather than a link that throws the
   reader off the site. White background because these are pages from a
   time when every site had one. */
.web-embed {
  max-width: 1100px;
  margin: 0 0 2.5rem;
}
.web-frame {
  position: relative;
  aspect-ratio: 21 / 8;
  border: 1px solid var(--border);
  border-radius: var(--radius, 8px);
  overflow: hidden;
  background: #fff;
}
.web-frame iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 150%;
  height: 150%;
  border: 0;
  transform: scale(0.667);
  transform-origin: top left;
}
.web-frame::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* Appearance comes from the shared media-caption rule further down. */
.web-embed figcaption a {
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
.web-embed figcaption a:hover { text-decoration: underline; }

/* ── YouTube video embeds ───────────────────────────────── */
.video-embed {
  max-width: 760px;
  margin: 0 0 2.5rem;
}
.video-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--border);
  border-radius: var(--radius, 8px);
  overflow: hidden;
  background: #000;
}
.video-frame iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
/* Appearance comes from the shared media-caption rule further down. */
.video-embed figcaption a {
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
.video-embed figcaption a:hover { text-decoration: underline; }

/* ── Event photography slideshow ─────────────────────────── */
/* One frame at a time, so twenty documentary photos never bury the three
   pieces of design work above them. Wider than the copy measure: these
   are photographs, and they earn the extra width the design flats don't. */
.proj-slideshow {
  max-width: 500px;
  margin-bottom: 3.5rem;
}

/* ── Tabbed project sections ─────────────────────────────── */
/* When a project splits into distinct events, a tab bar lets the viewer
   jump between them without scrolling past the entire other section. */
.proj-tabs {
  margin-top: 1rem;
}
.proj-tab-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-bottom: 2px solid var(--border);
  margin-bottom: 2.5rem;
}
.proj-tab-btn {
  padding: 0.75rem 1.5rem;
  border: 0;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-weight: 500;
  font-size: 0.95rem;
  cursor: pointer;
  transition: var(--transition);
}
.proj-tab-btn:hover {
  color: var(--text);
}
.proj-tab-btn.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}
.proj-tab-pane {
  min-height: 0;
}
.proj-tab-pane .proj-body:first-child {
  margin-top: 0;
}
.slide { margin: 0; }
.slide-frame {
  width: 100%;
  aspect-ratio: 7 / 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius, 8px);
  overflow: hidden;
}
.slide-frame img,
.slide-frame a {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}
.slide-frame a { display: flex; align-items: center; justify-content: center; }
/* .slide-cap is styled by the shared media-caption rule further down. */

/* Before the script runs, every slide is visible - a plain vertical run
   of photos. The controls only appear once they can actually do something. */
.proj-slideshow:not(.is-live) .slide + .slide { margin-top: 1.5rem; }
.proj-slideshow:not(.is-live) .slide-controls,
.proj-slideshow:not(.is-live) .slide-dots { display: none; }
.proj-slideshow.is-live .slide { display: none; }
.proj-slideshow.is-live .slide.is-on { display: block; }

.slide-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 1.25rem;
}
.slide-btn {
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-card);
  color: var(--text);
  font-size: 1.1rem;
  cursor: pointer;
  transition: var(--transition);
}
.slide-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.slide-count {
  color: var(--text-muted);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  font-variant-numeric: tabular-nums;
}
.slide-now { color: var(--text); font-weight: 600; }

.slide-dots {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 1rem;
}
.slide-dot {
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--border);
  cursor: pointer;
  transition: var(--transition);
}
.slide-dot:hover { background: var(--text-muted); }
.slide-dot.is-on { background: var(--accent); }

/* ── Logo grid (tiled marks) ────────────────────────────── */
/* A compact grid for collections of small marks — promotion logos, badge
   sets, icon families. Each tile is a white card with the logo centred
   via object-fit: contain and a small caption below. */
.proj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1.25rem;
  max-width: 760px;
  margin-bottom: 3rem;
}
.proj-grid-wide {
  grid-template-columns: repeat(3, 1fr);
  max-width: 1100px;
}
.proj-grid-wide .grid-tile-img {
  aspect-ratio: 7 / 5;
}
.proj-grid-med {
  grid-template-columns: repeat(2, 1fr);
  max-width: 700px;
}
.proj-grid-med .grid-tile-img {
  aspect-ratio: 7 / 10;
}
.proj-grid-large {
  grid-template-columns: repeat(3, 1fr);
  max-width: 1100px;
}
.proj-grid-large .grid-tile-img {
  aspect-ratio: 1 / 1;
}
.proj-grid-large .grid-tile-img img {
  max-width: 92%;
  max-height: 92%;
}
.proj-grid-large .grid-cap {
  font-size: 0.8rem;
}
.proj-grid-xl {
  grid-template-columns: repeat(2, 1fr);
  max-width: 1100px;
}
.proj-grid-xl .grid-tile-img {
  aspect-ratio: 16 / 9;
}
.proj-grid-xl .grid-tile-img img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.proj-grid-xl .grid-cap {
  font-size: 0.85rem;
}
.proj-grid-feature {
  grid-template-columns: 1fr;
  max-width: 800px;
}
.proj-grid-feature .grid-tile-img {
  aspect-ratio: auto;
  height: auto;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 0;
}
.proj-grid-feature .grid-tile-img img {
  max-width: 100%;
  max-height: none;
  width: 100%;
  height: auto;
  object-fit: contain;
}
.proj-grid-feature .grid-cap {
  font-size: 0.9rem;
  text-align: center;
  margin-top: 0.5rem;
}
.grid-tile {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.grid-tile-img {
  width: 100%;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 1px solid var(--border, rgba(0,0,0,0.08));
  border-radius: var(--radius, 8px);
  overflow: hidden;
}
.grid-tile-img img {
  max-width: 88%;
  max-height: 88%;
  width: auto;
  height: auto;
  object-fit: contain;
}
/* Grid tiles are centred, so their captions are too - a left rule here
   would fight the tile it sits under. Sized up from 0.72rem, which was
   small enough to read as a footnote nobody was meant to bother with. */
.grid-cap {
  margin-top: 0.6rem;
  text-align: center;
  color: var(--text-2);
  font-size: 0.78rem;
  line-height: 1.45;
}

/* ── Project images ──────────────────────────────────────── */
.proj-gallery {
  display: flex;
  flex-direction: column;
  /* Wide enough that a caption is unmistakably closer to the image above
     it than to the next figure below. With the old 2.5rem the two gaps
     were near enough to equal that captions read as floating between
     images rather than belonging to one. */
  gap: 4rem;
  /* Wide enough for a landscape spread to break out past the copy measure.
     Text and images are different problems: 760px is a comfortable line
     length, but a two-page spread rendered 760px wide gives each page ~380px,
     which is too small to read the type or judge the layout. Each figure is
     still capped individually by its own tier and native resolution, so this
     is a ceiling rather than a width. */
  max-width: var(--column);
}
/* Every inline [[image]] in a write-up is emitted as its OWN gallery
   holding a single figure, so `gap` above never applies between two
   consecutive images - and nothing else spaced them either. The result
   was a caption sitting 0.9rem under its own image and flush against the
   next one, so it read as labelling the image below it rather than the
   one it belongs to. Matches the intra-gallery gap. */
.proj-gallery + .proj-gallery { margin-top: 4rem; }

/* Copy and figures alternate down the page, so a gallery that is
   followed by more write-up needs the same breathing room the body
   blocks give each other. */
.proj-gallery + .proj-body { margin-top: 3.5rem; }
.proj-body-close { margin-top: 3.5rem; }

/* Display width is driven by the image's real proportions. build.py hands
   each figure its native pixel width as --native-w, and the tiers below
   take min(native, tier) - so nothing is ever scaled up past its true
   resolution, and nothing runs wider than its treatment allows. */
.proj-figure {
  /* Left aligned, not centred. Body copy runs to a 680px measure from
     the left edge, and a centred figure below it breaks the alignment
     and reads as unrelated to the paragraph that introduces it. */
  margin: 0;
  /* No card around the work. Two reasons, and the first is a defect:
     border-radius with overflow:hidden was clipping the corners off
     every image, and on a cover or a poster the corners are part of
     the composition. The second is judgement - a bordered rounded
     panel puts a rectangle around a rectangle and competes with the
     piece it is supposed to present. The artwork provides its own
     edge. */
  background: none;
  border: 0;
  border-radius: 0;
  transition: var(--transition);
}
/* Tier caps. These mirror SHAPE_CAP in build.py, which uses the same numbers
   to write each image's `sizes` attribute - if they drift apart the browser
   downloads the wrong candidate. Composed against --native-w rather than
   overriding it, so no tier needs !important to win. */

/* Landscape work - spreads, layouts, campaign pieces. Allowed to break out
   past the copy measure, because this is the work itself and it should be
   large enough to actually read. */
.proj-figure.is-wide { max-width: min(var(--native-w, 100%), 960px); }
/* Posters, flyers, vertical layouts - cap so they don't dominate. */
.proj-figure.is-tall { max-width: min(var(--native-w, 100%), 640px); }
/* Landscape pieces that shouldn't run the full column - motion samples,
   secondary artwork. Between a mark and a full-bleed layout. */
.proj-figure.is-medium { max-width: min(var(--native-w, 100%), 560px); }
/* Logos, marks, small assets - full width would just blow them up. A mark
   is read as a whole shape, not examined, so it wants to sit clearly
   inside the measure rather than push against it. */
.proj-figure.is-small { max-width: min(var(--native-w, 100%), 400px); }
/* The frame is gone, but these figures still open a lightbox, so the
   affordance has to come from somewhere. zoom-in states the action
   literally, and the hover shadow reads as liftable without painting a
   permanent box. */
.proj-figure a {
  display: block;
  cursor: zoom-in;
}
.proj-figure a:hover img { box-shadow: 0 12px 34px var(--shadow); }
.proj-figure img { transition: box-shadow var(--transition); }
/* Without the border there is nothing for a focus ring to sit against,
   so it gets explicit offset rather than inheriting the global style. */
.proj-figure a:focus-visible {
  outline: 2px solid var(--action);
  outline-offset: 4px;
}

/* Marks and wordmarks are usually supplied on white and sit awkwardly
   when they run straight to the edge. Matte them instead. Applied only
   where the build detected a white background, so full-bleed artwork
   doesn't get a border it was never designed to have. The padding goes
   on the link rather than the figure, which keeps the caption outside
   the white and on the page surface where it belongs. */
.proj-figure.has-matte a {
  background: #fff;
  padding: 2.25rem 2.5rem;
  /* The one place a border is still correct: white matte on the light
     theme's off-white surface has no edge of its own, and the mark
     would appear to float in nothing. Radius is safe here because the
     padding already insets the artwork from the corners. */
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.proj-figure.has-matte a:hover { border-color: var(--action); }
.proj-figure.has-matte a:hover img { box-shadow: none; }
@media (max-width: 600px) {
  .proj-figure.has-matte a { padding: 1.5rem 1.75rem; }
}
.proj-figure img,
.proj-figure video {
  width: 100%;
  height: auto;
  display: block;
}
/* Motion pieces sit on black so letterboxing reads as intentional. */
.proj-figure video { background: #000; }
/* ── Media captions ─────────────────────────────────────────
   One rule for every caption that describes a piece of media. It is a
   single declaration on purpose: these had drifted into five separate
   definitions across three font sizes and two colours, which is why
   captions looked unrelated to each other from page to page.

   A caption is a note about the work, not a label riveted to it - but
   removing the figure panel in the chrome pass left it as bare text
   adrift in whitespace, reading as stray body copy. The rule on the left
   is what fixes that: it marks the text as a deliberate annotation, and
   being a vertical edge it points back up at the media it describes.

   Capped at the reading measure so a caption under a 1180px spread never
   runs to an uncomfortable line length.

   Testimonial figcaptions are deliberately NOT included - those are
   attributions, not descriptions, and they carry their own em-dash and
   uppercase treatment. */
.proj-figure figcaption,
.slide-cap,
.web-embed figcaption,
.video-embed figcaption {
  margin-top: 0.9rem;
  padding-left: 0.9rem;
  border-left: 2px solid var(--border-strong);
  max-width: var(--measure);
  color: var(--text-2);
  font-size: 0.82rem;
  line-height: 1.55;
}

/* ── Project footer nav ──────────────────────────────────── */
.proj-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 4rem;
  padding-top: 2.5rem;
  border-top: 1px solid var(--border);
}
.proj-nav-btn {
  padding: 0.7rem 1.6rem;
  border: 1px solid var(--border);
  border-radius: 100px;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  transition: var(--transition);
}
.proj-nav-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  transform: translateY(-2px);
}
.proj-nav-cta {
  border-color: var(--accent);
  color: var(--accent);
}
.proj-nav-cta:hover {
  background: var(--accent);
  color: var(--bg);
  box-shadow: 0 0 20px var(--accent-glow);
}

/* ── Responsive ──────────────────────────────────────────── */
@media (max-width: 640px) {
  .work-header,
  .proj-header { padding: 5rem 0 2.5rem; }

  .work-grid { grid-template-columns: 1fr; gap: 1.25rem; }
  .work-main { padding: 2rem 1.25rem 4rem; }
  .proj-main { padding: 2.5rem 1.25rem 4rem; }

  .proj-gallery { gap: 1.5rem; }
  .proj-gallery + .proj-gallery { margin-top: 2.5rem; }
  .proj-body p { font-size: 1rem; }
  .proj-nav-btn { flex: 1; text-align: center; }
}

/* ── Lightbox ────────────────────────────────────────────── */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.92);
  padding: 2rem;
}
.lightbox[hidden] { display: none; }
.lightbox img {
  max-width: 90vw;
  max-height: 85vh;
  object-fit: contain;
  border-radius: 4px;
}
.lightbox-close {
  position: absolute;
  top: 1rem;
  right: 1.5rem;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s;
}
.lightbox-close:hover {
  background: rgba(255, 255, 255, 0.25);
}

/* Ensure lightbox trigger links don't break layouts */
a.lightbox-trigger {
  display: block;
  color: inherit;
  text-decoration: none;
}
.grid-tile-img a.lightbox-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
