/* ===== Fonts ===== */
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-Thin.woff2") format("woff2");
  font-weight: 100;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-ExtraLight.woff2") format("woff2");
  font-weight: 200;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-Light.woff2") format("woff2");
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-Medium.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-ExtraBold.woff2") format("woff2");
  font-weight: 800;
  font-display: swap;
}
@font-face {
  font-family: "Nohemi";
  src: url("../assets/fonts/nohemi/Nohemi-Black.woff2") format("woff2");
  font-weight: 900;
  font-display: swap;
}

/* ===== Design tokens (from Figma: primitive + semantic variables) ===== */
:root {
  /* primitive colors */
  --neutral-10: #ffffff;
  --neutral-20: #f5f5f5;
  --neutral-30: #ededed;
  --neutral-40: #e0e0e0;
  --neutral-50: #c2c2c2;
  --neutral-60: #9e9e9e;
  --neutral-70: #757575;
  --neutral-80: #616161;
  --neutral-90: #404040;
  --neutral-100: #0a0a0a;

  --primary-main: #0030bf;
  --primary-hover: #00207f;
  --primary-pressed: #001247;
  --primary-surface: #e6ebfa;
  --primary-border: #ced7f2;
  --primary-focused: #d1dcf5;

  /* semantic colors */
  --color-bg: var(--neutral-10);
  --color-paragraph: var(--neutral-70);
  --color-text: var(--neutral-70);
  --color-headline: var(--primary-main);
  --color-subheadline: var(--neutral-100);
  --color-stroke: var(--neutral-30);

  --color-card-title: var(--neutral-100);
  --color-card-subtitle: var(--neutral-70);
  --color-card-title-hover: var(--primary-main);

  --color-navbar-bg: var(--neutral-10);
  --color-navbar-text: var(--neutral-100);

  --color-link-default: var(--neutral-70);
  --color-link-hover: var(--primary-main);
  --color-link-pressed: var(--primary-main);

  --color-button-bg: var(--neutral-10);
  --color-button-stroke: var(--neutral-30);
  --color-button-text: var(--neutral-70);
  --color-button-text-pressed: var(--neutral-100);

  --color-list-content-idle-closed: var(--neutral-100);
  --color-list-content-tab-idle-closed: var(--primary-main);
  --color-list-content-idle-expand: var(--neutral-60);
  --color-list-content-active-expand: var(--neutral-100);

  /* spacing scale */
  --space-4: 4px;
  --space-6: 6px;
  --space-8: 8px;
  --space-12: 12px;
  --space-16: 16px;
  --space-20: 20px;
  --space-24: 24px;
  --space-32: 32px;
  --space-40: 40px;
  --space-48: 48px;
  --space-56: 56px;
  --space-64: 64px;
  --space-80: 80px;
  --space-100: 100px;
  --space-300: 300px;

  /* typography */
  --font-heading: "Nohemi", "Geist", -apple-system, sans-serif;
  --font-body: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* container gutter — overridden per breakpoint below */
  --gutter: var(--space-40);
}

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

/* Smooth-scrolls native anchor jumps (content-nav links, any #id href) at
   every breakpoint — this is what the browser uses for `<a href="#...">`
   navigation, so it covers desktop, tablet and mobile with one rule and
   no JS. Reduced-motion override lives with the other transition resets
   below. */
html {
  scroll-behavior: smooth;
}

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

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

body {
  background: var(--color-bg);
  font-family: var(--font-body);
  color: var(--color-text);
}

/* ===== Page transition (quiet fade) ===== */
@keyframes page-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

body {
  opacity: 0;
  animation: page-fade-in 220ms 60ms ease-out forwards;
}

body.is-leaving {
  transition: opacity 160ms ease-in;
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  body {
    opacity: 1;
    animation: none;
  }

  body.is-leaving {
    transition: none;
  }

  .content-nav,
  .content-nav__label,
  .content-nav__list,
  .content-nav__list li,
  .content-nav__link {
    transition: none !important;
  }
}

/* ===== Type styles ===== */
.title {
  font-family: var(--font-heading);
  font-size: 18px;
  font-weight: 500;
  line-height: 27px;
  letter-spacing: 0.7px;
  color: var(--color-headline);
}

.paragraph {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  letter-spacing: 0.7px;
  color: var(--color-paragraph);
}

.link {
  text-decoration: underline;
  color: var(--color-link-default);
}

.link:hover {
  color: var(--color-link-hover);
}

.link:active {
  color: var(--color-link-pressed);
}

/* ===== Navbar ===== */
.navbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--color-navbar-bg);
  border-bottom: 1px solid var(--color-stroke);
  transition: transform 1s ease;
}

.navbar--transparent {
  background: transparent;
  border-bottom: none;
}

.navbar--hidden {
  transform: translateY(-100%);
}

.navbar__inner {
  padding: var(--space-16) var(--space-40);
}

.navbar__title {
  font-family: var(--font-body);
  font-size: 20px;
  font-weight: 500;
  line-height: 30px;
  letter-spacing: 1px;
  color: var(--color-navbar-text);
}

a.navbar__title:hover {
  color: var(--color-headline);
}

/* ===== Layout container ===== */
/* .container is home (index.html) only — works.html uses its own
   .works-container instead, so these home-specific spacing overrides
   below are scoped under .container and never touch works.html. */
.container {
  max-width: 1440px;
  margin: 0 auto;
  padding: var(--space-80) var(--gutter);
}

@media (max-width: 767px) {
  .container {
    padding-bottom: var(--space-64);
  }
}

/* ===== Headline / about ===== */
.headline {
  margin-bottom: var(--space-64);
}

.container .headline {
  margin-bottom: var(--space-40);
}

.about {
  max-width: 840px;
}

.about__text {
  margin-top: var(--space-16);
  display: flex;
  flex-direction: column;
  gap: var(--space-24);
}

.container .about__text {
  margin-top: var(--space-24);
  gap: var(--space-16);
}

/* ===== Selected works ===== */
.selected-works__title {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 500;
  line-height: 24px;
  letter-spacing: 0.7px;
  color: var(--color-text);
  margin-bottom: var(--space-32);
}

.container .selected-works__title {
  margin-bottom: var(--space-24);
}

.works {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-24);
  max-width: 840px;
  margin-bottom: var(--space-32);
}

.container .works {
  margin-bottom: var(--space-24);
}

@media (min-width: 768px) {
  .works {
    grid-template-columns: repeat(2, 1fr);
  }
}

.work-card__thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--neutral-30);
  border-radius: 4px;
}

.work-card__thumb--placeholder {
  display: block;
}

/* ===== All Works page ===== */
.works-container {
  max-width: 1440px;
  margin: 0 auto;
  padding: var(--space-48) var(--space-40) var(--space-80);
}

.works--all {
  grid-template-columns: 1fr;
  max-width: 1360px;
}

@media (min-width: 768px) {
  .works--all {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1280px) {
  .works--all {
    grid-template-columns: repeat(3, 1fr);
  }
}

.work-card__title {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  letter-spacing: 0.7px;
  color: var(--color-card-title);
  margin-top: var(--space-16);
}

.work-card:hover .work-card__title {
  color: var(--color-card-title-hover);
}

.work-card__subtitle {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  letter-spacing: 0.7px;
  color: var(--color-card-subtitle);
}

/* ===== Button ===== */
.button {
  display: inline-block;
  background: var(--color-button-bg);
  border: 1px solid var(--color-button-stroke);
  border-radius: 8px;
  padding: var(--space-8) var(--space-16);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 24px;
  letter-spacing: 0.7px;
  color: var(--color-button-text);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.button:hover {
  border-color: var(--color-button-stroke);
  color: var(--color-button-text);
  box-shadow: 0 10px 10px -4px rgba(0, 0, 0, 0.1);
}

.button:active {
  border-color: transparent;
  color: var(--color-button-text-pressed);
}

.selected-works__cta {
  display: flex;
  justify-content: center;
  max-width: 840px;
}

/* ===== Footer ===== */
.footer-main {
  background: var(--color-bg);
  border-top: 1px solid var(--color-stroke);
  padding: var(--space-24) var(--gutter);
}

.footer-main__inner {
  max-width: 840px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
}

/* ===== Detail page (project case study) ===== */
/* Full viewport width on purpose — unlike .container (used on
   index/works, which has no visible background or border), each
   .detail-section needs its bg + border-bottom to reach the actual
   screen edges. Capping .detail-page itself at 1440px left a plain
   white gap beyond that width with no section chrome in it at all,
   which read as the border "stopping in the middle" on wide screens.
   Content is instead capped per-section via padding (see
   .detail-section's desktop rule below). */
.detail-page {
  display: flex;
  flex-direction: column;
}

.detail-section {
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-stroke);
  padding: var(--space-64) var(--gutter);
  display: flex;
  flex-direction: column;
  /* 40px at mobile, 56px at tablet+ (see the tablet breakpoint below) —
     confirmed by re-measuring Booksmart/Bookdrop's mobile frames after
     the Figma spacing cleanup; previously flat 56px at every width. */
  gap: var(--space-40);
}

.detail-section--tight {
  padding-top: var(--space-48);
}

.detail-section--outro {
  border-bottom: none;
  padding-top: var(--space-48);
}

/* Images-only "gallery" variant (Kasatmata, ZNTRAL): no per-image
   heading/paragraph, so the item-to-item gap is tighter than the usual
   56px block gap: 16px mobile, 24px tablet+ (Kasatmata's desktop gap
   was re-measured at 24px too after the Figma spacing cleanup — it's
   no longer 40px, so this is one flat value from tablet up).
   Compounded as .detail-section.detail-section--gallery so it reliably
   outranks the plain .detail-section breakpoint rules below on
   specificity, regardless of source order. */
.detail-section.detail-section--gallery {
  gap: var(--space-16);
}

@media (min-width: 768px) {
  .detail-section.detail-section--gallery {
    gap: var(--space-24);
  }
}

.detail-subgroup {
  display: flex;
  flex-direction: column;
  gap: var(--space-40);
}

.detail-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

.detail-hero__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.detail-hero__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
}

.watch-live {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  background: var(--neutral-30);
  border-radius: 34px;
  padding: var(--space-8) var(--space-12);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  line-height: 21px;
  letter-spacing: 0.5px;
  color: var(--neutral-100);
}

/* Text-only block (Figma "section-headline": h2/h3 + paragraph, no
   image — section intros and "Outcome"). 12px gap between title and
   paragraph, confirmed by re-measuring several section-headline and
   Outcome frames in Figma after the spacing cleanup. */
.detail-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}

/* Text-plus-image block (Figma "section-detail": a "headline" h3+p
   pair immediately followed by one or more images/grids). Needs a
   nested wrapper because the title-to-paragraph gap (4px) and the
   text-to-image gap (16px mobile / 24px tablet+) are two different
   values — a flat gap on one flex container can't produce both.
   Compounded with .detail-block for specificity (same reasoning as
   the gallery variant above). */
.detail-block__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Exception: Bookdrop's "Restructuring the Product Architecture" block
   opens its content-section directly (no separate non-image
   section-headline before it) and is meant to read as that section's
   headline despite having an image — so its title-to-paragraph gap
   stays 12px (the section-headline value) instead of the usual 4px. */
.detail-block__text.detail-block__text--section-headline {
  gap: var(--space-12);
}

.detail-block.detail-block--media {
  gap: var(--space-16);
}

@media (min-width: 768px) {
  .detail-block.detail-block--media {
    gap: var(--space-24);
  }
}

.detail-title {
  font-family: var(--font-heading);
  font-size: 18px;
  font-weight: 400;
  line-height: 27px;
  letter-spacing: 0.7px;
  color: var(--neutral-100);
}

.detail-image {
  width: 100%;
  aspect-ratio: 840 / 491;
  object-fit: cover;
  background: var(--neutral-30);
  border-radius: 6px;
}

/* Image grid: stacks as a single column on mobile so images stay
   full-width and readable. Becomes a proper 2-column grid at tablet+
   (works for 2, 3, or 5 images by just adding more <img> children —
   auto-placement fills left-to-right, top-to-bottom). Put a
   .detail-image-grid__text as the FIRST child to reserve the top-left
   cell for a title+paragraph instead of an image (tablet+ only —
   on mobile it just appears above the images like any other block). */
.detail-image-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-24);
}

/* Grid images keep their own natural proportions (no forced crop/stretch)
   — unlike the single full-width .detail-image, which is a fixed 840/491
   crop on purpose. Only applies to real <img> tags: they have an
   intrinsic size to size from. Placeholder <div>s (before the real
   image is uploaded) have nothing to size from and would collapse to
   0 height, so they keep a fallback ratio instead. */
.detail-image-grid img.detail-image {
  aspect-ratio: auto;
  object-fit: contain;
}

.detail-image-grid div.detail-image {
  aspect-ratio: 408 / 492;
}

/* Gallery variant (Kasatmata, ZNTRAL): stays a 2-column grid even on
   mobile (unlike the usual single-column-until-tablet grid), with its
   own tighter gap per breakpoint (16 mobile / 24 tablet+), per Figma.
   Compounded with .detail-image-grid for specificity, same reasoning
   as .detail-section.detail-section--gallery above. */
.detail-image-grid.detail-image-grid--gallery {
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-16);
}

@media (min-width: 768px) {
  .detail-image-grid.detail-image-grid--gallery {
    gap: var(--space-24);
  }
}

.detail-image-grid__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* ===== Content nav ("List of content") — detail pages =====
   Desktop (>=1280px, hover-driven): default state shows just the label.
   On hover/focus, the label fades out and the section list fades in
   over it. The list is positioned absolute (out of flow) so it never
   affects .content-nav's own box height — that keeps the fixed
   vertical-center anchor (top:50% + translateY(-50%)) stable instead of
   re-centering as the list reveals.

   Mobile & tablet (<1280px, no hover): the label is a real <button> —
   tapping it toggles .content-nav.is-expanded (see initContentNav in
   main.js), which turns the button into the header of a bottom-anchored
   card and reveals the list below it. */
.content-nav {
  position: fixed;
  z-index: 20;
}

.content-nav__label {
  /* button reset — this is a <button> now so it can be tapped without
     a hover state to rely on; strip the browser chrome back down to
     how the plain label text used to look. */
  display: block;
  appearance: none;
  background: none;
  border: none;
  margin: 0;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 400;
  line-height: 21px;
  color: var(--color-list-content-idle-closed);
  white-space: nowrap;
  cursor: pointer;
  transition: opacity 0.15s ease-out;
}

@media (max-width: 1279.98px) {
  .content-nav {
    left: 50%;
    bottom: var(--space-24);
    transform: translateX(-50%);
    /* No explicit width here on purpose — the pill hugs the label's own
       content width (like the Framer reference's "hug" sizing). The
       expanded list gets its own fixed width below, kept from leaking
       into this hug-width via position:absolute (see .content-nav__list). */
    /* Same hide-on-scroll-down / show-on-scroll-up behavior as .navbar
       (identical transition value, on purpose — see initContentNav's
       scroll handler in main.js, which mirrors initNavbarScroll's). */
    transition: transform 1s ease;
  }

  .content-nav.content-nav--hidden {
    transform: translateX(-50%) translateY(150%);
  }

  .content-nav__label {
    display: inline-block;
    width: max-content;
    padding: var(--space-8) var(--space-16);
    background: var(--color-bg);
    border: 1px solid var(--color-stroke);
    border-radius: 8px;
    box-shadow: 0 10px 10px -4px rgba(0, 0, 0, 0.1);
    color: var(--color-list-content-tab-idle-closed);
  }

  /* Expanded state shows the list only — no "List of content" header above
     it (unlike the desktop label, which stays as the closed-state trigger
     text). The trigger button disappears the instant the list opens, and
     comes back once it's collapsed again via outside click or scroll (see
     initContentNav in main.js). */
  .content-nav.is-expanded .content-nav__label {
    display: none;
  }

  .content-nav__list {
    /* Absolute + self-centered (left:50% + translateX(-50%), same trick
       .content-nav uses on itself) so its fixed 280px width never
       contributes to .content-nav's own hug-width sizing — if it stayed
       in normal flow, the browser would size the pill to fit this box
       too (that was the actual bug: the 280px card width was leaking
       into the closed pill's width). Anchoring bottom:0 keeps it growing
       from the same bottom edge as before, independent of the label.

       Open/close itself is transform+opacity only now (no max-height
       animation) — picked after comparing 3 techniques live: this one
       ("cascade pop + soft bounce") vs. a precise grid-rows grow and a
       plain no-bounce pop. Enter overshoots slightly past 100% then
       settles (cubic-bezier(0.34,1.56,0.64,1), a mild spring/back-out
       curve — never more than a slight overshoot per the spring
       guidance), landing with a bit of physical "life". Close stays
       calm/linear and faster (160ms vs 320ms) — the bounce belongs to
       arriving, not leaving (exits are quieter than enters). */
    position: absolute;
    left: 50%;
    bottom: 0;
    width: min(280px, calc(100vw - (var(--space-24) * 2)));
    list-style: none;
    background: var(--color-bg);
    border: 1px solid var(--color-stroke);
    border-radius: 8px;
    transform-origin: bottom center;
    transform: translateX(-50%) translateY(6px) scale(0.9);
    opacity: 0;
    /* Unlike the old max-height:0 approach, this box is always at real
       size (just scaled/translated away), so it still occupies that
       footprint over the label underneath — pointer-events:none while
       closed keeps it from silently eating taps meant for the button. */
    pointer-events: none;
    transition: transform 160ms cubic-bezier(0.2, 0, 0, 1), opacity 140ms ease-in;
  }

  .content-nav.is-expanded .content-nav__list {
    transform: translateX(-50%) translateY(0) scale(1);
    opacity: 1;
    pointer-events: auto;
    transition: transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 200ms ease-out;
  }

  .content-nav__list .content-nav__link {
    padding: var(--space-8) var(--space-20);
    /* Cascading per-item reveal, same 18ms-per-row ladder as the
       desktop hover version, for the same reason: entering rows feel
       staggered/alive, closing doesn't need it (see nth-child rules
       below apply only .is-expanded). */
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 220ms cubic-bezier(0.2, 0, 0, 1), transform 220ms cubic-bezier(0.2, 0, 0, 1);
  }

  .content-nav.is-expanded .content-nav__list .content-nav__link {
    opacity: 1;
    transform: translateY(0);
  }

  .content-nav.is-expanded .content-nav__list li:nth-child(2) .content-nav__link {
    transition-delay: 18ms;
  }
  .content-nav.is-expanded .content-nav__list li:nth-child(3) .content-nav__link {
    transition-delay: 36ms;
  }
  .content-nav.is-expanded .content-nav__list li:nth-child(4) .content-nav__link {
    transition-delay: 54ms;
  }
  .content-nav.is-expanded .content-nav__list li:nth-child(5) .content-nav__link {
    transition-delay: 72ms;
  }
  .content-nav.is-expanded .content-nav__list li:nth-child(6) .content-nav__link {
    transition-delay: 90ms;
  }
}

@media (min-width: 1280px) {
  .content-nav {
    top: 50%;
    left: var(--space-24);
    transform: translateY(-50%);
  }
}

.content-nav__link {
  display: block;
  padding: var(--space-6) 0;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 400;
  line-height: 21px;
  color: var(--color-list-content-idle-expand);
  text-decoration: none;
  transition: color 0.15s ease-out;
}

/* Active section (scroll-spy, see initContentNav in main.js) — the only
   "selected" affordance on mobile/tablet, since there's no hover there. */
.content-nav__link.is-active {
  color: var(--color-list-content-active-expand);
}

@media (min-width: 1280px) {
  .content-nav__list {
    /* Absolute + top:50%/translateY(-50%), same trick as the label's
       parent: top:50% resolves against .content-nav's (constant) height,
       translateY(-50%) resolves against the LIST's OWN height — which
       now changes as each <li> grows (see below). As that happens,
       translateY(-50%) recalculates every frame, so the list's own
       vertical center stays pinned to that same point while its edges
       grow outward — the "expand from the middle" effect, driven by
       genuine reflow instead of a scale() illusion. */
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    /* No gap here on purpose — spacing comes from padding on
       .content-nav__link instead, so the hoverable box of each link
       touches its neighbor with zero dead space between them. A flex
       `gap` would leave a strip with no element in it; the cursor
       crossing that strip while moving down the list drops out of
       .content-nav's hover chain and the list flickers shut mid-way
       instead of closing once, cleanly, when the mouse actually leaves. */
    width: 220px;
    list-style: none;
    pointer-events: none;
  }

  .content-nav:hover .content-nav__label,
  .content-nav:focus-within .content-nav__label {
    opacity: 0;
  }

  /* Each <li> starts collapsed and grows to reveal its link — this is
     what makes the list's total height (and so its centered position)
     animate smoothly frame-by-frame, rather than popping to full size
     in one step. Deliberate exception to "animate transform/opacity
     only": max-height here isn't for a large panel where reflow cost
     would matter, it's a handful of 14px list rows, and no other
     technique reproduces this exact grow-from-center feel (mirrors
     https://muhraufan.com/work/tiket-discover-full.html's section rail,
     which animates height for the same reason). */
  .content-nav__list li {
    max-height: 0;
    overflow: hidden;
    transition: max-height 280ms cubic-bezier(0.2, 0, 0, 1);
  }

  .content-nav:hover .content-nav__list li,
  .content-nav:focus-within .content-nav__list li {
    max-height: 60px;
  }

  .content-nav__link {
    opacity: 0;
    transition: color 0.15s ease-out, opacity 240ms cubic-bezier(0.2, 0, 0, 1);
  }

  .content-nav:hover .content-nav__link,
  .content-nav:focus-within .content-nav__link {
    opacity: 1;
    pointer-events: auto;
  }

  /* Staggered cascade on open — each row starts its fade-in a beat
     after the one above it (same ladder as the reference site: 18ms
     per step). Only the entering direction needs this; closing
     collapses in reverse automatically since these are transitions,
     not keyframes, so a quick mouse-leave doesn't wait for a full
     staggered close before it starts retracting. */
  .content-nav:hover .content-nav__list li:nth-child(2) .content-nav__link,
  .content-nav:focus-within .content-nav__list li:nth-child(2) .content-nav__link {
    transition-delay: 18ms;
  }
  .content-nav:hover .content-nav__list li:nth-child(3) .content-nav__link,
  .content-nav:focus-within .content-nav__list li:nth-child(3) .content-nav__link {
    transition-delay: 36ms;
  }
  .content-nav:hover .content-nav__list li:nth-child(4) .content-nav__link,
  .content-nav:focus-within .content-nav__list li:nth-child(4) .content-nav__link {
    transition-delay: 54ms;
  }
  .content-nav:hover .content-nav__list li:nth-child(5) .content-nav__link,
  .content-nav:focus-within .content-nav__list li:nth-child(5) .content-nav__link {
    transition-delay: 72ms;
  }
  .content-nav:hover .content-nav__list li:nth-child(6) .content-nav__link,
  .content-nav:focus-within .content-nav__list li:nth-child(6) .content-nav__link {
    transition-delay: 90ms;
  }

  .content-nav__link:hover,
  .content-nav__link:focus-visible {
    color: var(--color-list-content-active-expand);
  }
}

/* ===== Footer detail (project pages) ===== */
.footer-detail {
  background: var(--color-bg);
  border-top: 1px solid var(--color-stroke);
  padding-top: var(--space-40);
}

.footer-detail__email {
  max-width: 840px;
  margin: 0 auto;
  padding: 0 var(--gutter);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-8);
}

.footer-detail__label {
  color: var(--color-paragraph);
}

.link--large {
  font-size: 18px;
  line-height: 27px;
}

.footer-detail__bar {
  /* Fixed padding/margin here, NOT the --gutter scale used by the rest
     of the page (300/100/40) — re-measured against Figma after the
     spacing cleanup: horizontal padding is 20px mobile / 40px tablet+,
     and the gap above the bar is 24px mobile / 32px tablet+. */
  margin-top: var(--space-24);
  max-width: 840px;
  margin-left: auto;
  margin-right: auto;
  padding: var(--space-24) var(--space-20);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-24);
}

.footer-detail__links {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-32);
  flex-wrap: wrap;
}

/* Tablet and up: links + copyright become one horizontal row */
@media (min-width: 768px) {
  .footer-detail__bar {
    flex-direction: row;
    justify-content: center;
    gap: var(--space-32);
    margin-top: var(--space-32);
    padding-left: var(--space-40);
    padding-right: var(--space-40);
  }

  .footer-detail__links {
    display: contents;
  }
}

/* ===== Responsive breakpoints (matches Figma frames: 390 / 810 / 1440) ===== */

/* Tablet and up */
@media (min-width: 768px) {
  :root {
    --gutter: var(--space-100);
  }

  .detail-section {
    padding-bottom: var(--space-80);
    gap: var(--space-56);
  }

  .detail-section:not(.detail-section--tight):not(.detail-section--outro) {
    padding-top: var(--space-80);
  }

  .detail-image-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* Unequal 2-column split (~500:316 in Figma) instead of the usual
     even 50/50 grid — used where one image is meant to read as the
     "main" shot and the other as a supporting detail. Percentages,
     not fr units: fr tracks were computing as equal-width here
     (placeholder <div> grid items with no intrinsic content size
     seem to break fr's free-space distribution), while percentages
     size correctly. */
  .detail-image-grid--wide-narrow {
    grid-template-columns: 61.27% 38.73%;
  }
}

/* Desktop */
@media (min-width: 1280px) {
  :root {
    --gutter: var(--space-300);
  }

  /* Figma's desktop frame is exactly 1440px (300px gutter + 840px
     content + 300px gutter). .detail-page has no max-width anymore
     (so section backgrounds/borders reach the real screen edges on
     wider monitors), so the 840px content column is instead capped
     here directly: padding grows past 300px once the viewport clears
     1440px, keeping content centered at a fixed 840px instead of
     stretching wider and wider. */
  .detail-section {
    padding-left: max(var(--gutter), calc((100% - 840px) / 2));
    padding-right: max(var(--gutter), calc((100% - 840px) / 2));
  }
}
