@font-face {
  font-family: "Labil Grotesk";
  src: url("/assets/fonts/LabilGrotesk-Regular.woff2") format("woff2"),
       url("/assets/fonts/LabilGrotesk-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Brand colours — names match Figma Design System (node 940:1243) one-for-one. */
  --night: #0d1b1e;
  --orange: #ff795c;
  --blush: #efccc4;
  --olive: #567c30;
  --forest: #293e14;
  --white: #fff5f5;

  --row-pad: 24px;
  --section-gap: 80px;
  --shell-width: min(80%, 1440px);
  --bio-max: 720px;
  --separator: color-mix(in srgb, var(--night) 5%, transparent);

  /* 12-col row spans — matches Figma (node 401:1113). Title 516px @ 1048px
     content = 6 cols; middle + end each take 1fr of the remaining = 3 cols. */
  --col-title: 6;
  --col-middle: 3;
  --col-end: 3;

  /* Header pill — Bg #fff5f5 at 20%, blur(10) + saturate(180%), 5% Night edge. */
  --pill-bg: rgba(255, 245, 245, 0.2);
  --pill-filter: blur(10px) saturate(180%);
  --pill-edge: inset 0 0 0 1px var(--separator);

  /* Theme tokens — day mode defaults. Overridden by [data-theme="night"] and
     by [data-theme="system"] @ prefers-color-scheme: dark below. */
  --bg: var(--white);
  --fg: var(--night);
}

[data-theme="night"] {
  --bg: var(--night);
  --fg: var(--white);
  --separator: color-mix(in srgb, var(--white) 8%, transparent);
  --pill-bg: rgba(13, 27, 30, 0.5);
}

@media (prefers-color-scheme: dark) {
  [data-theme="system"] {
    --bg: var(--night);
    --fg: var(--white);
    --separator: color-mix(in srgb, var(--white) 8%, transparent);
    --pill-bg: rgba(13, 27, 30, 0.5);
  }
}

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

html,
body {
  margin: 0;
  padding: 0;
}

/* Anchor the root to the theme bg so there's no seam between html and body
   (or overscroll / scrollbar gutter) above the page content. */
html {
  background: var(--bg);
}

body {
  background: var(--bg);
  color: var(--fg);
  transition: background-color 240ms ease, color 240ms ease;
  font-family: "Labil Grotesk", system-ui, sans-serif;
  font-size: 16px;
  line-height: 1.3;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Strip the browser-default 2px inset border on iframe (Vimeo / YouTube embeds)
   so they sit seamlessly inside their media slots. */
iframe {
  border: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

/* Paragraph text gets a more generous 150% line-height across the site
   (bio, intro, detail body, kirbytext output). Non-paragraph text stays at
   the tighter body default. */
p {
  line-height: 1.5;
}

img {
  display: block;
  max-width: 100%;
}

.page {
  position: relative;
  min-height: 100vh;
  padding-top: 144px;
  padding-bottom: 120px;
}

/* Progressive blur ramp under the header pills. Six stacked layers — each
   has a stronger backdrop-filter than the one below it AND a mask that
   shows it only over its allotted slice of the height. Effect: content
   scrolling under the top edge gets a smooth blur-ramp (heavier near the
   pills, fully clear near the page body) rather than a single hard band. */
.top-blur {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 110px; /* ends just past the bottom of the pills */
  z-index: 40;
  pointer-events: none;
}

.top-blur__layer {
  position: absolute;
  inset: 0;
}

/* Every layer fades to transparent before the bottom of the strip so the
   bio text directly below isn't softened. Heavier blurs cover a smaller
   slice of the height, ramping out faster. */
.top-blur__layer[data-blur="1"] {
  -webkit-backdrop-filter: blur(0.5px);
  backdrop-filter: blur(0.5px);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 60%, transparent 100%);
  mask-image: linear-gradient(to bottom, black 0%, black 60%, transparent 100%);
}

.top-blur__layer[data-blur="2"] {
  -webkit-backdrop-filter: blur(1px);
  backdrop-filter: blur(1px);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 95%);
  mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 95%);
}

.top-blur__layer[data-blur="3"] {
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 40%, transparent 85%);
  mask-image: linear-gradient(to bottom, black 0%, black 40%, transparent 85%);
}

.top-blur__layer[data-blur="4"] {
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 30%, transparent 75%);
  mask-image: linear-gradient(to bottom, black 0%, black 30%, transparent 75%);
}

.top-blur__layer[data-blur="5"] {
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 22%, transparent 60%);
  mask-image: linear-gradient(to bottom, black 0%, black 22%, transparent 60%);
}

.top-blur__layer[data-blur="6"] {
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 14%, transparent 45%);
  mask-image: linear-gradient(to bottom, black 0%, black 14%, transparent 45%);
}

/* Header pills cluster — main pill is the fixed anchor, dead-centred via a
   3-track grid (1fr auto 1fr). Back pill lives in the left cell anchored to
   its right edge; theme pill in the right cell anchored to its left edge.
   Back pill expands leftward on hover without nudging the main pill. */
.header-pills {
  position: fixed;
  top: 40px;
  left: 0;
  right: 0;
  z-index: 50;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  pointer-events: none;
}

.header-pills > *                   { pointer-events: auto; }
.header-pills > .header-back-pill   { grid-column: 1; justify-self: end;    margin-right: 8px; }
.header-pills > .header-pill        { grid-column: 2; justify-self: center; }
.header-pills > .header-theme-pill  { grid-column: 3; justify-self: start;  margin-left: 8px; }

.header-pill {
  background: var(--pill-bg);
  backdrop-filter: var(--pill-filter);
  -webkit-backdrop-filter: var(--pill-filter);
  border-radius: 32px;
  padding: 8px;
  /* Hairline inset edge so the pill keeps its shape on plain white backgrounds
     where the backdrop-filter has nothing to blur against. */
  box-shadow: var(--pill-edge);
}

.header-back-pill {
  background: var(--pill-bg);
  backdrop-filter: var(--pill-filter);
  -webkit-backdrop-filter: var(--pill-filter);
  border-radius: 32px;
  padding: 8px;
  text-decoration: none;
  color: var(--fg);
  box-shadow: var(--pill-edge);
  position: relative;
  display: flex;
  height: 64px;
  box-sizing: border-box;
  transition: padding 320ms cubic-bezier(0.4, 0, 0.2, 1);
}

.header-back-pill:hover,
.header-back-pill:focus-visible,
.header-back-pill:active {
  padding: 4px;
}

/* Back pill default: compact circle (icon only). On hover/focus/active the
   outer's padding shrinks 8 → 4 and the inner widens to reveal the label.
   The inner container IS the visible "blob" — it carries the bg + radius +
   overflow-clip, so the bg geometry tracks layout 1:1 (no separate ::before
   chasing the pill bounds). */
.header-back-pill__inner {
  display: flex;
  align-items: center;
  align-self: stretch;
  background: color-mix(in srgb, var(--fg) 10%, transparent);
  border-radius: 48px;
  overflow: hidden;
  position: relative;
  z-index: 1;
}

.header-back-pill__icon {
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  margin-right: 0;
  transition: margin-right 320ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* On expand, pull the icon 4px into the label's column so the chevron and
   label read as a tighter group — matches the construction in Figma. */
.header-back-pill:hover .header-back-pill__icon,
.header-back-pill:focus-visible .header-back-pill__icon,
.header-back-pill:active .header-back-pill__icon {
  margin-right: -4px;
}

.header-back-pill__chev {
  width: 16px;
  height: 16px;
}

.header-back-pill__label {
  font-size: 16px;
  line-height: 1.3;
  max-width: 0;
  opacity: 0;
  white-space: nowrap;
  padding-right: 0;
  transform: translateX(16px);
  transition:
    max-width     320ms cubic-bezier(0.4, 0, 0.2, 1),
    opacity       320ms cubic-bezier(0.4, 0, 0.2, 1),
    transform     320ms cubic-bezier(0.4, 0, 0.2, 1),
    padding-right 320ms cubic-bezier(0.4, 0, 0.2, 1);
}

.header-back-pill:hover .header-back-pill__label,
.header-back-pill:focus-visible .header-back-pill__label,
.header-back-pill:active .header-back-pill__label {
  max-width: 200px;
  opacity: 1;
  transform: translateX(0);
  padding-right: 16px;
}

/* Theme toggle pill — same surface as the back pill, cycles day → night → system.
   All three glyphs are stacked on the same grid cell; the one matching the
   current [data-theme] on <html> fades in over 240ms. */
.header-theme-pill {
  background: var(--pill-bg);
  backdrop-filter: var(--pill-filter);
  -webkit-backdrop-filter: var(--pill-filter);
  border: 0;
  border-radius: 32px;
  padding: 8px;
  cursor: pointer;
  color: var(--fg);
  box-shadow: var(--pill-edge);
  display: inline-flex;
  font: inherit;
  position: relative;
  overflow: hidden;
}

/* Theme pill keeps the ::before bg blob — it has no label so there's no inner
   container to carry the bg. Expands to ~60% silhouette on hover. */
.header-theme-pill::before {
  content: '';
  position: absolute;
  top: 8px;
  left: 8px;
  width: 48px;
  height: 48px;
  background: var(--separator);
  border-radius: 48px;
  z-index: 0;
  transition:
    top          240ms cubic-bezier(0.4, 0, 0.2, 1),
    left         240ms cubic-bezier(0.4, 0, 0.2, 1),
    width        240ms cubic-bezier(0.4, 0, 0.2, 1),
    height       240ms cubic-bezier(0.4, 0, 0.2, 1),
    border-radius 240ms cubic-bezier(0.4, 0, 0.2, 1);
}

.header-theme-pill:hover::before,
.header-theme-pill:focus-visible::before {
  top: 3.2px;
  left: 3.2px;
  width: calc(60% + 19.2px);
  height: 57.6px;
  border-radius: 38.4px;
}

.header-theme-pill__icon {
  width: 48px;
  height: 48px;
  border-radius: 48px;
  /* Background now lives on .header-theme-pill::before so it can animate
     to fill the pill on hover. */
  display: grid;
  place-items: center;
  flex-shrink: 0;
  overflow: hidden;
  position: relative;
  z-index: 1;
}

.header-theme-pill__glyph {
  grid-area: 1 / 1;
  width: 16px;
  height: 16px;
  opacity: 0;
  transform: rotate(-90deg) scale(0.85);
  transition: opacity 240ms ease-out, transform 240ms cubic-bezier(0.4, 0, 0.2, 1);
}

[data-theme="day"]    .header-theme-pill__glyph[data-state="day"],
[data-theme="night"]  .header-theme-pill__glyph[data-state="night"],
[data-theme="system"] .header-theme-pill__glyph[data-state="system"] {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

/* Hover preview — show the NEXT state in the cycle (day → night → system → day)
   by rotating the active glyph out and the next-in-line glyph in. */
[data-theme="day"]    .header-theme-pill:hover .header-theme-pill__glyph[data-state="day"],
[data-theme="night"]  .header-theme-pill:hover .header-theme-pill__glyph[data-state="night"],
[data-theme="system"] .header-theme-pill:hover .header-theme-pill__glyph[data-state="system"] {
  opacity: 0;
  transform: rotate(90deg) scale(0.85);
}

[data-theme="day"]    .header-theme-pill:hover .header-theme-pill__glyph[data-state="night"],
[data-theme="night"]  .header-theme-pill:hover .header-theme-pill__glyph[data-state="system"],
[data-theme="system"] .header-theme-pill:hover .header-theme-pill__glyph[data-state="day"] {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

.header-pill__inner {
  display: flex;
  align-items: center;
  gap: 12px;
}

.header-pill__avatar {
  width: 48px;
  height: 48px;
  border-radius: 48px;
  /* Same translucent wash as the theme + back pill icons (--separator).
     Theme-aware: 5% Night in day mode, 8% White in night mode. Glyph
     colour follows body's --fg via the inlined SVG's currentColor. */
  background: var(--separator);
  overflow: hidden;
  flex-shrink: 0;
}

.header-pill__avatar img,
.header-pill__avatar svg {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}

.header-pill__label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-right: 12px;
}

.header-pill__name {
  font-size: 16px;
  line-height: 1.3;
}

.header-pill__role {
  font-size: 16px;
  line-height: 1.3;
  opacity: 0.6;
}

/* Page content shell */
.shell {
  width: var(--shell-width);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--section-gap);
}

/* Bio */
.bio {
  display: flex;
  justify-content: center;
}

.bio__body {
  width: 100%;
  max-width: var(--bio-max);
  font-size: 16px;
  line-height: 1.3;
  color: var(--fg);
}

.bio__body p {
  margin: 0 0 16px;
  text-wrap: pretty;
}

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

/* Index */
.index {
  display: flex;
  flex-direction: column;
  gap: var(--section-gap);
  align-items: center;
}

.index-section {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.index-section__head {
  display: flex;
  align-items: center;
  height: 64px;
  padding: 0 var(--row-pad);
}

.index-section__title {
  font-size: 24px;
  line-height: 1.3;
  color: var(--fg);
  margin: 0;
  font-weight: 400;
  text-wrap: balance;
}

/* Sub-section heading — used when an index section groups its rows by kind
   (currently Elsewhere → Talks / Features / Awards / Publications).
   The label sits inside a small Night chip with rounded corners, separated
   from the previous group above and the first row below by generous space. */
.subsection__title {
  margin: 0;
  padding: 40px var(--row-pad) 28px;
  font-weight: 400;
  line-height: 1;
}

.subsection__pill {
  display: inline-block;
  background: var(--separator);
  color: var(--fg);
  font-size: 12px;
  letter-spacing: 0;
  padding: 7px 8px;
  border-radius: 4px;
}

/* Rows */
.row {
  display: flex;
  align-items: center;
  padding: var(--row-pad);
  border-top: 1px solid var(--separator);
  background: transparent;
  transition: background-color 120ms ease, opacity 120ms ease;
  text-decoration: none;
  color: inherit;
}

.row__content {
  /* True 12-col grid. minmax(0, 1fr) locks every column to an exact 1/12 share
     of the available width — without it, nowrap content (e.g. "March 2022")
     forces individual cols wider than their fr-share and breaks the grid.
     Year and arrow are anchored to the right edges of cols 11 / 12 so they
     align with the overlay regardless of content; year text overflows
     leftward into the gap rather than clipping. width:100% so the grid
     fills the row — .row is a flex container, otherwise this would shrink
     to its content's intrinsic width. */
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-items: center;
  gap: 16px;
  width: 100%;
  min-width: 0;
  opacity: 1;
  /* Fast snap when brightening back up. The dim fade-out below overrides this. */
  transition: opacity 100ms ease;
}

.row__content > :nth-child(1) { grid-column: 1 / span var(--col-title); }
.row__content > :nth-child(2) { grid-column: span var(--col-middle); }
.row__end                     { grid-column: span var(--col-end) / -1; }

.row__end {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-width: 0;
}

.row__date {
  font-size: 16px;
  line-height: 1.3;
  white-space: nowrap;
}

/* Link affordance icon — inline with the title text, scaled to ~cap-height
   so it reads as a typographic adornment, not a separate UI chip. On
   desktop it reveals from 0-width on row hover; mobile pins it open. */
.row__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: -0.05em;
  width: 0;
  height: 0.75em;
  margin-left: 0;
  opacity: 0;
  overflow: hidden;
  transition:
    width        240ms cubic-bezier(0.4, 0, 0.2, 1),
    margin-left  240ms cubic-bezier(0.4, 0, 0.2, 1),
    opacity      180ms ease-out 60ms;
}

.row:hover .row__icon,
.row:focus-visible .row__icon {
  width: 0.75em;
  margin-left: 0.35em;
  opacity: 1;
}

/* Outbound rows are link-first — pin the icon visible regardless of hover. */
.row--outbound .row__icon {
  width: 0.75em;
  margin-left: 0.35em;
  opacity: 1;
}

.row__icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.row__cell {
  min-width: 0;
  font-size: 16px;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Inherit color from .row so the per-row hover foreground cascades down. */
}

/* Title is the primary content — let it wrap to multiple lines if a name is
   long, rather than truncating it. text-wrap: balance keeps multi-line
   titles evenly distributed instead of one long + one stub line. */
.row__cell:first-child {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  text-wrap: balance;
}

/* Hover — any row that has a preview tile (linked or not) lights up.
   Row inline style sets --row-hover from the thumbnail's dominant colour
   (or an explicit override). --row-hover-fg flips text to a readable
   colour against dark backgrounds. Both fall back gracefully. */
.row[data-preview]:hover {
  background: var(--row-hover, var(--orange));
  color: var(--row-hover-fg, var(--night));
}

/* When one row is being hovered, the others slowly fade so the active row pops */
body.is-hovering-row .row__content {
  opacity: 0.3;
  transition: opacity 360ms ease-out;
}

/* The actively hovered row stays bright with a fast snap */
body.is-hovering-row .row[data-preview]:hover .row__content {
  opacity: 1;
  transition: opacity 100ms ease;
}

/* Outbound rows (e.g. "View full history at LinkedIn" at the end of
   Experience) sit faded by default to read as secondary, full opacity
   on hover or keyboard focus. */
.row--outbound .row__content {
  opacity: 0.3;
}
.row--outbound:hover .row__content,
.row--outbound:focus-visible .row__content {
  opacity: 1;
}

/* Custom cursor + preview */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 100;
  display: none;
  transform: translate(-50%, -50%);
}

.cursor.is-visible {
  display: block;
}

.cursor__preview {
  position: absolute;
  /* Default anchor: bottom-right of cursor. Overridden via the .cursor's
     data-anchor attribute (set in cursor.js) when the preview would clip
     the viewport. */
  top: 12px;
  left: 16px;
  width: 256px;
  height: 256px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--blush);
  box-shadow:
    0 4px 12px rgba(13, 27, 30, 0.05),
    0 32px 96px rgba(13, 27, 30, 0.18);
  display: none;
}

.cursor[data-anchor="bottom-left"] .cursor__preview {
  left: auto;
  right: 16px;
}

.cursor[data-anchor="top-right"] .cursor__preview {
  top: auto;
  bottom: 12px;
}

.cursor[data-anchor="top-left"] .cursor__preview {
  top: auto;
  bottom: 12px;
  left: auto;
  right: 16px;
}

.cursor__preview.is-visible {
  display: block;
}

.cursor__preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Responsive — collapse the column gymnastics on narrow viewports */
/* Row thumb is mobile-only; hidden by default so the desktop cursor preview
   stays the canonical brand-thumb surface. */
.row__thumb {
  display: none;
}

@media (max-width: 900px) {
  :root {
    --shell-width: calc(100% - 32px);
    --section-gap: 56px;
  }

  .page {
    padding-top: 112px;
  }

  .header-pills {
    top: 16px;
  }

  .row__content {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
  }

  .cursor {
    display: none !important;
  }
}

/* ≤640px — phones. Header pills collapse to a flex cluster (avatar+name
   centred, theme dot right-edge, back left). Rows split into a text column
   with vertically-stacked title / meta / icon and a square brand thumb on
   the right, so each row carries its colour even without hover. */
@media (max-width: 640px) {
  .top-blur {
    height: 72px;
  }

  .header-pills {
    top: 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 12px;
    grid-template-columns: none;
    gap: 8px;
  }

  .header-pills > .header-back-pill,
  .header-pills > .header-pill,
  .header-pills > .header-theme-pill {
    grid-column: auto;
    justify-self: auto;
    margin: 0;
  }

  /* Drop the role line on mobile — bio carries it anyway. Avatar + name
     stay, the pill becomes a compact identity badge. */
  .header-pill__role {
    display: none;
  }

  .header-pill__inner {
    gap: 8px;
  }

  .header-pill__label {
    line-height: 1;
  }

  .header-pill__name {
    font-size: 14px;
  }

  /* Back pill: don't expand on tap. Phones have no hover; the chevron
     alone is enough of a back affordance. */
  .header-back-pill:active {
    padding: 8px;
  }

  .header-back-pill:active .header-back-pill__icon {
    margin-right: 0;
  }

  .header-back-pill:active .header-back-pill__label {
    max-width: 0;
    opacity: 0;
    padding-right: 0;
    transform: translateX(16px);
  }

  /* Rows extend flush with the bio's left/right edges (shell already
     provides the 16px gutter). Internal row padding goes to 0 horizontal
     so the alignment lines up with the bio body text. */
  .index-section,
  .index-section__head,
  .subsection__title {
    padding-left: 0;
    padding-right: 0;
  }

  .row {
    gap: 12px;
    padding: 12px 0;
  }

  .row__content {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px 8px;
  }

  .row__cell {
    font-size: 15px;
    line-height: 1.3;
  }

  .row__cell:first-child {
    font-size: 17px;
    font-weight: 500;
  }

  .row__end {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
  }

  .row__date {
    font-size: 14px;
    opacity: 0.75;
  }

  /* No hover on touch — pin the inline icon visible for every linked row. */
  .row__icon {
    width: 0.75em;
    margin-left: 0.35em;
    opacity: 1;
  }

  /* Outbound (LinkedIn etc.): collapse the row to one line, no thumb,
     icon already inline with the title text via .row__icon. */
  .row--outbound .row__content {
    flex-direction: row;
  }

  .row--outbound .row__end {
    display: none;
  }

  .row__thumb {
    display: block;
    width: 72px;
    height: 72px;
    flex-shrink: 0;
    border-radius: 8px;
    overflow: hidden;
    background: var(--row-hover, transparent);
  }

  .row__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  /* Touch hover state on rows reads as noise — kill the desktop hover
     background and "dim others" treatment on phones. */
  .row[data-preview]:hover {
    background: transparent;
    color: inherit;
  }

  body.is-hovering-row .row__content {
    opacity: 1;
  }
}

/* Detail pages */
.page--detail {
  padding-top: 144px;
}

.shell--detail {
  width: var(--shell-width);
  max-width: 1100px;
}

.detail {
  display: flex;
  flex-direction: column;
  gap: 64px;
}

/* Project header group (525:286 in Figma) — title 32px, meta row underneath,
   intro paragraphs below. Internal gaps via explicit margins on children so
   title→meta (20px) and meta→intro (32px) can differ. */
.detail__head {
  display: flex;
  flex-direction: column;
}

.detail__title {
  margin: 0;
  font-size: 32px;
  line-height: 1.3;
  font-weight: 400;
  text-wrap: balance;
}

.detail__meta {
  margin: 20px 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  opacity: 0.7;
  font-size: 16px;
  white-space: nowrap;
}

.detail__meta-item {
  display: inline-block;
}

.detail__meta a {
  color: inherit;
  text-decoration: none;
}

.detail__meta a:hover {
  text-decoration: underline;
}

.detail__intro {
  margin: 32px 0 0;
  max-width: 696px;
  font-size: 16px;
  line-height: 1.5;
}

.detail__intro p {
  margin: 0 0 24px;
  text-wrap: pretty;
}

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

/* Body uses a 2-track grid: a 696px reading column on the LEFT, then 1fr
   slack to the right. Aligns text blocks (h2, p, etc.) flush-left with the
   intro paragraph in .detail__head. Figures still bleed past the grid via
   their own explicit width + margin override. */
.detail__body {
  display: grid;
  grid-template-columns: min(696px, 100%) 1fr;
  font-size: 16px;
  line-height: 1.5;
}

.detail__body > * {
  grid-column: 1;
  margin: 0;
}

.detail__body h2,
.detail__body h3 {
  font-weight: 400;
  letter-spacing: -0.01em;
}

.detail__body h2 { font-size: 24px; line-height: 1.25; }
.detail__body h3 { font-size: 20px; line-height: 1.3; }

/* Rhythm:
   - figure ↔ text block: 64px breathing room either side of an image group
   - figure → figure: 16px (matches the image-pair internal gap)
   - heading → paragraph: 24px (tight, single text-block internals)
   - paragraph → paragraph: 16px (continuous prose)
   - everything else: 32px default */
.detail__body > * + *                   { margin-top: 32px; }
.detail__body > p + p                   { margin-top: 16px; }
.detail__body > figure + figure         { margin-top: 16px; }
.detail__body > h2 + p,
.detail__body > h3 + p                  { margin-top: 24px; }
.detail__body > figure + h2,
.detail__body > figure + h3,
.detail__body > figure + p              { margin-top: 64px; }
.detail__body > h2 + figure,
.detail__body > h3 + figure,
.detail__body > p + figure              { margin-top: 64px; }

/* Image bleed: figures break out of the .shell--detail container (max 1100px)
   and fill the viewport up to a 1920px cap, with a 20px inset on each side
   so they never reach the browser edge. Centred on the viewport. */
.detail__body > figure {
  grid-column: 1 / -1;
  width: min(100vw - 40px, 1920px);
  max-width: none;
  margin-left: calc(50% - min(50vw - 20px, 960px));
  justify-self: stretch;
}

.detail__body > figure img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
}

.detail__body figcaption {
  font-size: 14px;
  opacity: 0.6;
  margin-top: 12px;
}

/* Image pair: 2-up grid inside the full-bleed figure. */
.block-image-pair__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.block-image-pair__img {
  width: 100%;
  aspect-ratio: 16 / 9;
  display: block;
  object-fit: cover;
  border: 0;
  border-radius: 8px;
}

.block-image-pair__placeholder {
  background: var(--separator);
  aspect-ratio: 4 / 3;
  border-radius: 8px;
}

/* Image trio: 3-up equal grid. */
.block-image-trio__grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 16px;
}

.block-image-trio__img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.block-image-trio__placeholder {
  background: var(--separator);
  aspect-ratio: 16 / 9;
  border-radius: 8px;
}

/* Image stack-pair: 50/50 split, one column with 2 stacked images, the other
   with 1 tall image. Stack column drives the row height (two 16:9 images +
   16px gap); tall column stretches to match. Orientation just controls DOM
   order, so the grid template stays symmetric. */
.block-image-stack-pair__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: stretch;
}

.block-image-stack-pair__stack {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.block-image-stack-pair__tall {
  display: block;
}

.block-image-stack-pair__stack .block-image-stack-pair__img {
  aspect-ratio: 16 / 9;
  width: 100%;
  display: block;
  object-fit: cover;
  border-radius: 8px;
}

.block-image-stack-pair__tall .block-image-stack-pair__img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  border-radius: 8px;
}

.block-image-stack-pair__placeholder {
  background: var(--separator);
  aspect-ratio: 16 / 9;
  border-radius: 8px;
}

.block-image-stack-pair__tall .block-image-stack-pair__placeholder {
  aspect-ratio: auto;
  height: 100%;
}

/* Image stack-triple: 66/33 split with 3 stacked images opposite a tall one.
   Grid template flips so tall always takes the 2fr (~66%) track. */
.block-image-stack-triple__grid {
  display: grid;
  gap: 16px;
  align-items: stretch;
}

.block-image-stack-triple--tall-left  .block-image-stack-triple__grid { grid-template-columns: 2fr 1fr; }
.block-image-stack-triple--tall-right .block-image-stack-triple__grid { grid-template-columns: 1fr 2fr; }

.block-image-stack-triple__stack {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.block-image-stack-triple__tall {
  display: block;
}

.block-image-stack-triple__stack .block-image-stack-triple__img {
  aspect-ratio: 16 / 9;
  width: 100%;
  display: block;
  object-fit: cover;
  border-radius: 8px;
}

.block-image-stack-triple__tall .block-image-stack-triple__img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  border-radius: 8px;
}

.block-image-stack-triple__placeholder {
  background: var(--separator);
  aspect-ratio: 16 / 9;
  border-radius: 8px;
}

.block-image-stack-triple__tall .block-image-stack-triple__placeholder {
  aspect-ratio: auto;
  height: 100%;
}

.detail__body--empty {
  opacity: 0.4;
  font-style: italic;
}

/* 12-col debug overlay. Aligned with row content width (shell - row pad),
   centred, fixed full viewport height. Toggle via `data-grid-overlay="on"`
   on <body> (driven by a Dialkit boolean). */
.grid-overlay {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--shell-width) - (var(--row-pad) * 2));
  pointer-events: none;
  z-index: 99;
  display: none;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 32px;
}

body[data-grid-overlay="on"] .grid-overlay {
  display: grid;
}

.grid-overlay__col {
  background: color-mix(in srgb, var(--orange) 8%, transparent);
  outline: 1px dashed color-mix(in srgb, var(--orange) 28%, transparent);
}

@media (max-width: 900px) {
  .detail__title {
    font-size: 28px;
  }
}
