/* Palette comes from `.theme-dark` in theme.css. Most components already read
   those tokens; the exceptions are listed further down where colors were
   hardcoded. */

/* Fixed attachment so the wall covers the viewport behind the header too, and
   stays put while slides travel across it. iOS ignores fixed attachment and
   falls back to scrolling, which is fine — it shows the static list there. */
body.portfolio-page {
  background-color: var(--page);
  background-image: url("/assets/project-background-wall.jpg");
  background-size: cover;
  background-position: center center;
  background-attachment: fixed;
  background-repeat: no-repeat;
}

body.portfolio-page .site-header {
  position: fixed !important;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  z-index: 30;
  background: var(--page);
}

.portfolio-sticky-main {
  padding-top: 88px;
}

.portfolio-sticky-section {
  height: 400vh;
  position: relative;
  scroll-margin-top: 88px;
}

.portfolio-sticky-stage {
  position: sticky;
  top: 88px;
  height: calc(100vh - 88px);
  height: calc(100svh - 88px);
  overflow: hidden;
}

.portfolio-sticky-shell {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  align-items: center;
  height: 100%;
  padding-right: 216px;
}

.portfolio-track {
  position: relative;
  height: 100%;
  will-change: transform;
}

.portfolio-copy-track {
  z-index: 2;
  padding-right: 8px;
}

.portfolio-image-stage {
  position: relative;
  height: 100%;
  overflow: hidden;
  padding-left: 18px;
}

.portfolio-copy-panel,
.portfolio-image-panel {
  height: calc(100vh - 88px);
  height: calc(100svh - 88px);
  display: flex;
}

.portfolio-copy-panel {
  flex-direction: column;
  justify-content: center;
  max-width: 31rem;
  /* No top padding: with justify-content centre and no matching bottom padding,
     any value here pushes the copy below the image's centre line. */
  padding-left: 84px;
  padding-right: 32px;
}

.portfolio-count,
.portfolio-kicker,
.portfolio-scroll-note,
.portfolio-progress-item {
  margin: 0;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.portfolio-count {
  display: inline-flex;
  align-items: center;
  gap: 0.22em;
  margin-bottom: 44px;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.1em;
}

.portfolio-count-current {
  color: var(--gold);
}

.portfolio-count-divider,
.portfolio-count-total {
  color: var(--navy);
}

.portfolio-title {
  max-width: 7.2ch;
  margin: 0;
  /* .hero-title sets no color and would inherit --body; the headline needs to
     read brighter than the description. */
  color: var(--title);
  font-size: clamp(76px, 6vw, 96px);
  line-height: 0.98;
  letter-spacing: -0.035em;
}

.portfolio-copy-panel .section-rule {
  width: 58px;
  margin-top: 34px;
  margin-bottom: 28px;
}

.portfolio-kicker {
  margin-bottom: 28px;
  color: var(--fig);
  font-size: 13px;
  letter-spacing: 0.18em;
}

.portfolio-description {
  margin: 0;
  max-width: 20.8rem;
  font-size: 18px;
  line-height: 1.82;
  color: var(--body);
}

.portfolio-link {
  margin-top: 46px;
  color: var(--gold);
  font-size: 15px;
}

.portfolio-scroll-note {
  margin-top: 104px;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.16em;
}

.portfolio-image-track {
  position: relative;
  display: flex;
  flex-direction: column-reverse;
}

/* No overflow: hidden here — it would clip the print shadow. The stage above
   still clips, so neighbouring panels stay out of view. */
.portfolio-image-panel {
  position: relative;
  align-items: center;
  justify-content: center;
  margin: 0;
  flex: 0 0 calc(100vh - 88px);
  flex: 0 0 calc(100svh - 88px);
}

/* One frame for every slide, hung like a framed print. Sizing comes from the
   panel, never from the photo's intrinsic dimensions, so all four cards are
   identical; `cover` crops each photo to fill it rather than letterboxing. The
   portraits (~0.735) sit close to the frame ratio, so the visible crop falls
   mostly on the two squares. Shadow reach stays inside the vertical gap between
   panels so it can't bleed into the neighbouring slide. */
.portfolio-image-panel img {
  display: block;
  width: 90%;
  height: auto;
  /* Sized from panel width with a pinned ratio, so every card is identical and
     the frame can never overflow the panel on tall viewports. 4:5 splits the
     difference between the two portrait sources (~0.735) and the two squares:
     the portraits lose ~8% vertically, the squares ~10% per side. */
  aspect-ratio: 4 / 5;
  object-fit: cover;
  box-shadow:
    0 24px 60px rgba(0, 0, 0, 0.55),
    0 4px 14px rgba(0, 0, 0, 0.38);
}

/* Focal point per photo, since centre-cropping is not right for all four. */
.portfolio-image-panel:nth-child(1) img { object-position: center 42%; }
.portfolio-image-panel:nth-child(2) img { object-position: center 46%; }
.portfolio-image-panel:nth-child(3) img { object-position: center 50%; }
.portfolio-image-panel:nth-child(4) img { object-position: center 46%; }

.portfolio-progress {
  position: absolute;
  top: 50%;
  right: 28px;
  transform: translateY(-50%);
  z-index: 10;
  width: 72px;
  height: 316px;
}

.portfolio-progress-line {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: 1px;
  background: rgba(244, 240, 233, 0.16);
}

.portfolio-progress-items {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
}

.portfolio-progress-item {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding-left: 28px;
  color: rgba(244, 240, 233, 0.44);
  transition: color 220ms ease;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
}

.portfolio-progress-item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(244, 240, 233, 0.32);
  flex: 0 0 auto;
  transform: translateY(-50%);
  transition:
    background-color 220ms ease,
    transform 220ms ease;
}

.portfolio-progress-item.is-active {
  color: var(--gold);
}

.portfolio-progress-item.is-active::before {
  background: var(--gold);
  transform: translateY(-50%) scale(1.04);
}

.portfolio-mobile-list {
  display: none;
}

/* Native scroll snapping. The section's scroll runway is exactly 300vh —
   400vh minus the stage height (100vh - 88px) minus the stage's own sticky
   offset (88px) — so the header height cancels and each step is a clean
   viewport. Four anchors a viewport apart therefore land on the same scroll
   positions as the track transforms. Absolute so they cost no layout. */
.portfolio-snap-rail {
  position: absolute;
  inset: 0 0 auto 0;
  pointer-events: none;
}

.portfolio-snap-point {
  height: 100vh;
}

@media (min-width: 721px) and (prefers-reduced-motion: no-preference) {
  /* Safe to scope to the root: portfolio.css is only linked from this page, and
     only elements carrying scroll-snap-align act as snap areas. Smooth
     scroll-behavior is overridden because it double-animates against snapping. */
  html {
    scroll-snap-type: y mandatory;
    scroll-behavior: auto;
  }

  /* The section has to start flush at scroll 0 so the first anchor is the
     natural top of the page — with the header's 88px offset here instead,
     mandatory snap yanks the page down 88px on load. The stage's own
     `top: 88px` still clears the fixed header. Mobile and reduced motion keep
     the padding: they render the static list, which has no sticky stage. */
  .portfolio-sticky-main {
    padding-top: 0;
  }

  .portfolio-snap-point {
    scroll-snap-align: start;
  }

  /* Header floats directly on the wall. Only safe here: the sticky stage clips
     its contents, so nothing ever scrolls up behind the header. The static
     list on mobile/reduced-motion does scroll under it and keeps a solid bar. */
  body.portfolio-page .site-header {
    background: transparent;
  }
}

@media (max-width: 1100px) {
  .portfolio-sticky-shell {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0;
    padding-right: 156px;
  }

  .portfolio-title {
    font-size: clamp(60px, 6vw, 80px);
  }

  .portfolio-copy-panel {
    padding-left: 60px;
    max-width: 28rem;
  }

  .portfolio-image-panel img {
    width: 112%;
    height: 90%;
  }
}

/* Reduced motion gets the same static list as mobile — portfolio.js bails on
   either condition, so the sticky sequence would never animate. */
@media (max-width: 720px), (prefers-reduced-motion: reduce) {
  .portfolio-sticky-section,
  .portfolio-progress {
    display: none;
  }

  .portfolio-mobile-list {
    display: grid;
    gap: 36px;
    padding-top: 24px;
    padding-bottom: 40px;
  }

  .portfolio-mobile-card {
    display: grid;
    gap: 18px;
  }

  .portfolio-mobile-copy .page-title {
    margin-bottom: 0;
  }

  .portfolio-mobile-copy .section-rule {
    width: 58px;
    margin-top: 18px;
    margin-bottom: 18px;
  }

  .portfolio-mobile-image {
    margin: 0;
    aspect-ratio: 1 / 1.06;
  }

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