/* Project detail pages */

body[data-page="portfolio"] {
  /* 14px at a 1280px-wide screen (14 / 1280 = 1.09375vw), scaling linearly
     with viewport width from there so the text-to-screen ratio holds.
     Shared by the header meta line, summary text, headings, links and
     collaborators — defined here (rather than on .project-layout) since
     .project-header is a sibling of .project-layout, not a descendant. */
  --fs-project-text: 1.09375vw;
}

@media (max-width: 900px) {
  body[data-page="portfolio"] {
    /* Fixed size below the breakpoint instead of the fluid vw scale */
    --fs-project-text: 16px;
  }
}

.project-header {
  display: flex;
  align-items: center;
  /* Half the gallery gap — used here as the space between the two arrows
     themselves (the gap before them is overridden below by the auto margin
     that pushes the whole arrow pair to the right edge). */
  gap: calc(var(--space-md) * 0.25);
  padding-top: var(--space-xl);
  padding-bottom: var(--space-lg);
}

.project-header__text {
  /* Sized to content rather than stretched, and pinned to the left edge —
     the arrow pair is pushed to the opposite (right) edge below. */
  flex: none;
  min-width: 0;
}

/* Pushes the prev/next arrow pair to the right edge of the header, leaving
   the title/date/type block flush left. Targets whichever .project-nav
   comes right after the text (the real prev arrow, or its placeholder at
   the start of the sequence) so only one auto margin is needed regardless
   of which is present. */
.project-header__text + .project-nav {
  margin-left: auto;
}

.project-header__title {
  font-size: var(--fs-h1);
  font-weight: 300;
}

.project-header__meta {
  margin-top: var(--space-2xs);
  font-size: var(--fs-project-text);
  opacity: 0.75;
}

/* Carousel-style prev/next between projects (see js/main.js for the
   directional page-transition hint, and css/base.css for the actual
   View Transition animation). Vertically centered against the whole
   title+meta block via align-items:center on .project-header above. */
.project-nav {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  color: var(--color-ink);
  transition: transform var(--duration-base) var(--ease), opacity var(--duration-base) var(--ease);
}

.project-nav svg {
  width: 100%;
  height: 100%;
}

.project-nav:hover,
.project-nav:focus-visible {
  transform: scale(1.2);
}

.project-nav--prev:hover,
.project-nav--prev:focus-visible {
  transform: scale(1.2) translateX(-2px);
}

.project-nav--next:hover,
.project-nav--next:focus-visible {
  transform: scale(1.2) translateX(2px);
}

/* At the start/end of the sequence (Isracard has no prev, Linkstory has no
   next) this invisible same-size placeholder fills that arrow's slot, so
   the other (real) arrow still lands in the same right-edge position it
   would occupy on every other project page. */
.project-nav--placeholder {
  visibility: hidden;
}

.project-layout {
  display: grid;
  /* Half the gap goes to the text column's max-width instead, so the
     column/image spacing tightens while the text itself gets more room. */
  grid-template-columns: minmax(260px, calc(360px + var(--space-2xl) * 0.5)) 1fr;
  gap: calc(var(--space-2xl) * 0.5);
  align-items: start;
  padding-bottom: var(--space-2xl);
}

@media (max-width: 900px) {
  .project-layout {
    grid-template-columns: 1fr;
    gap: var(--space-lg);
  }

  /* Overrides base.css's global `p { max-width: 65ch }` cap, so the text
     fills the column edge-to-edge instead of wrapping early */
  .project-summary p {
    max-width: none;
  }
}

.project-details {
  display: flex;
  flex-direction: column;
  /* Half the spacing between the summary (ending in "The Solution"),
     Links, Software in use, and Collaborators */
  gap: calc(var(--space-lg) * 0.5);
  position: sticky;
  top: calc(72px + var(--space-md));
}

@media (max-width: 900px) {
  .project-details {
    position: static;
  }
}

.project-summary {
  font-size: var(--fs-project-text);
  line-height: var(--lh-normal);
  max-width: none;
}

.project-summary h2 {
  font-size: var(--fs-project-text);
  font-weight: 500;
  text-transform: capitalize;
  margin-top: var(--space-sm);
  margin-bottom: var(--space-2xs);
}

.project-summary h2:first-child {
  margin-top: 0;
}

.project-summary p + h2 {
  margin-top: calc(var(--space-md) * 0.5);
}

@media (max-width: 900px) {
  /* 2x the desktop spacing between a paragraph and the heading that
     follows it (e.g. intro paragraph -> "The Challenge" -> "The Solution") */
  .project-summary p + h2 {
    margin-top: calc(var(--space-md) * 0.5 * 2);
  }
}

.project-summary p {
  margin-bottom: var(--space-2xs);
}

.project-images {
  display: flex;
  flex-direction: column;
  /* Half the previous spacing between gallery segments */
  gap: calc(var(--space-md) * 0.5);
  padding-bottom: var(--space-2xl);
}

/* Every media block (standalone image, standalone video, or one half of a
   split row) renders at the same height — matching the Isracard project's
   top image (1600x958 natural). Standalone items span the full column, so
   they use that ratio directly; row items are half that width, so their
   ratio is halved too, landing at the same absolute height despite being
   narrower. object-fit crops each item's own content to fit. */
.project-images img,
.video-frame {
  width: 100%;
  /* The HTML width/height attributes (added for CLS) map to a `height`
     presentational hint on <img>. With no explicit `height` here to
     override it, that hint pins the box to the image's raw natural pixel
     height regardless of how much the width gets scaled down — this
     un-pins it so height is driven by aspect-ratio instead. */
  height: auto;
  aspect-ratio: 1600 / 958;
  object-fit: cover;
}

.project-images img {
  border-radius: 0;
}

.video-frame {
  position: relative;
  overflow: hidden;
  border-radius: 0;
  background: var(--color-line);
}

/* The row itself spans the full column width, same as a standalone item —
   giving it the same aspect-ratio makes its established height exactly
   match a standalone image's, with no need to approximate around the
   internal gap between its two children. Each child then just stretches
   to fill that height (rather than deriving its own ratio), so the match
   is exact by construction rather than estimated. */
.project-images__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr;
  /* Matches .project-images's vertical gap between segments */
  gap: calc(var(--space-md) * 0.5);
  /* The gap area reveals whatever this element's own background is —
     transparent, explicitly, so it shows the page behind it rather than
     any opaque fill */
  background: transparent;
  align-items: stretch;
  /* Explicitly decouples this box's own size from its children's content
     size entirely, so the aspect-ratio below always wins — without this,
     a child's intrinsic size (e.g. an <img>'s) can feed back into and
     inflate the grid's computed height past what the ratio alone gives. */
  contain: size layout;
  width: 100%;
  height: auto;
  aspect-ratio: 1600 / 958;
}

/* Grid items get an implicit min-width/min-height: auto, which for an <img>
   resolves toward its natural size — a tall portrait screenshot paired
   with a video here would otherwise blow the whole row out to the image's
   full natural height instead of scaling down with the column width. */
.project-images__row > * {
  min-width: 0;
  min-height: 0;
}

.project-images__row img,
.project-images__row .video-frame {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
}

@media (max-width: 640px) {
  .project-images__row {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    aspect-ratio: auto;
    /* No longer sizing this box from its own aspect-ratio at this
       breakpoint (each stacked child gets its own ratio below instead),
       so let it size from content normally again. */
    contain: none;
    gap: calc(var(--space-md) * 0.5);
  }

  .project-images__row img,
  .project-images__row .video-frame {
    height: auto;
    aspect-ratio: 1600 / 958;
  }
}

/* Vimeo's player letterboxes (contain-fits) the video within the iframe's
   own box by default — it doesn't offer a way to "cover" its container
   from our side, since the player itself is cross-origin content we can't
   style directly. Sizing the iframe to exactly 95% of the container's
   height (top: 2.5% + height: 95% leaves an even 2.5% gap top and
   bottom) and deriving its width from the video's real 16:9 ratio — rather
   than stretching it — keeps the asset's own proportions intact; that
   derived width naturally comes out wider than the container, and gets
   center-cropped horizontally by .video-frame's overflow:hidden. */
.video-frame iframe {
  position: absolute;
  top: 2.5%;
  left: 50%;
  width: auto;
  height: 95%;
  aspect-ratio: 16 / 9;
  /* reset.css caps all iframes at max-width: 100%, which would otherwise
     clamp the derived width back down and defeat the horizontal crop */
  max-width: none;
  transform: translateX(-50%);
  border: 0;
}

/* Vimeo's own UI is hidden entirely (title=0&byline=0&portrait=0&
   controls=0 on the iframe src), so this transparent button sits on top
   of it as the only way to interact with the video — a click anywhere on
   the frame toggles play/pause via js/main.js (initVideoToggle), which
   talks to the player over postMessage. */
.video-frame__toggle {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
}

/* ===========================================================================
   Redesign additions — everything below is new to project-v2.css, layered
   on top of the unchanged rules above. See portfolio/isracard-redesign.html
   for the markup these target.
   =========================================================================== */

/* Reading-progress bar, pinned to the true viewport top edge regardless of
   breakpoint (desktop's own sticky full-width header vs. mobile/tablet's
   floating inset pill have too different a shape to attach this to either
   one directly) — driven by initScrollProgress() in js/main.js. Uses the
   same --gradient-accent as the .link-hover underline (see css/tokens.css
   and css/base.css) rather than introducing a second accent. */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  z-index: 101;
  pointer-events: none;
  overflow: hidden;
}

.scroll-progress__fill {
  width: 100%;
  height: 100%;
  background: var(--gradient-accent);
  transform: scaleX(0);
  transform-origin: left;
}

@media (prefers-reduced-motion: no-preference) {
  .scroll-progress__fill {
    transition: transform 80ms linear;
  }
}

/* Quick-facts strip — a fast "who/what/when" scan point for a reviewer who
   isn't going to read the full case study, sitting between the header and
   the hero image. Generalizes .meta-block's existing label treatment
   (fs-small, 70% opacity) rather than introducing a new one. */
.project-quickfacts {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--space-2xl);
  row-gap: var(--space-md);
  padding-bottom: var(--space-lg);
  margin-bottom: var(--space-lg);
  border-bottom: 1px solid var(--color-line);
}

.project-quickfacts__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.project-quickfacts__label {
  font-size: var(--fs-small);
  opacity: 0.7;
}

.project-quickfacts__value {
  font-size: var(--fs-project-text);
}

@media (max-width: 900px) {
  .project-quickfacts {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-lg);
  }
}

/* Shared TODO treatment for placeholder copy (quick-facts values, Results
   paragraph) — reads as visibly unfinished in-browser rather than as
   shipped, fabricated case-study claims. */
.is-placeholder {
  opacity: 0.55;
  text-decoration: underline dashed;
  text-underline-offset: 3px;
}

/* Numbered section index — same tabular-nums treatment as
   .project-list__index on the home page's project index (css/pages/home.css),
   the clearest available visual callback tying this page back to the rest
   of the site. */
.project-summary__index {
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  margin-right: var(--space-2xs);
}

/* Hero image — the case study's opening visual, promoted out of the
   gallery and given a large, confident presentation directly under the
   quick-facts strip rather than competing for space in the two-column
   sticky layout below it. Same aspect-ratio as the rest of the gallery
   (no new crop to reconcile), just bigger and first. */
.project-hero {
  width: 100%;
  aspect-ratio: 1600 / 958;
  overflow: hidden;
  margin-bottom: var(--space-2xl);
}

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

@media (prefers-reduced-motion: no-preference) {
  .project-hero img {
    animation: project-hero-in 900ms var(--ease) both;
  }
}

@keyframes project-hero-in {
  from {
    opacity: 0;
    transform: scale(1.04);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@media (max-width: 900px) {
  .project-hero {
    margin-bottom: var(--space-lg);
  }
}

/* Next-project band — the case study's closing beat. A full band rather
   than the small header arrow alone, so leaving this page toward another
   project is as considered a moment as arriving on it. Thumbnail reuses
   the home grid's .tilt-wrap/.tilt-image tilt-on-hover exactly (see
   .project-card__image-wrap in css/components.css) rather than a new
   hover treatment. */
.project-next {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  padding-block: var(--space-2xl);
  border-top: 1px solid var(--color-line);
  color: var(--color-ink);
}

.project-next__thumb {
  flex: none;
  width: clamp(120px, 18vw, 220px);
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

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

.project-next__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-width: 0;
}

.project-next__label {
  font-size: var(--fs-small);
  opacity: 0.7;
}

.project-next__title {
  font-size: var(--fs-h1);
  font-weight: 300;
}

/* Lives inside .project-next__title's own text (see the markup) rather
   than alongside it, specifically so mobile (below) can let it flow as
   an ordinary piece of inline content — sitting right after the title on
   its last line if there's room, wrapping down on its own like a
   trailing word would if there isn't, exactly the way plain text wraps.
   That's not achievable with this as a separate flex item: flexbox only
   wraps whole items to a new row as a unit, never mid-line based on
   remaining space the way inline text does. Desktop pins it to the far
   right of the whole row instead, absolutely positioned against
   .project-next above (the nearest positioned ancestor) rather than via
   flex — a flex item's margin-left: auto trick doesn't apply once this
   is nested inside a plain text span instead of being .project-next's
   own direct child. */
.project-next__arrow {
  position: absolute;
  top: 50%;
  right: 0;
  width: 32px;
  height: 32px;
  transform: translateY(-50%);
  transition: transform var(--duration-base) var(--ease);
}

.project-next:hover .project-next__arrow,
.project-next:focus-visible .project-next__arrow {
  transform: translateY(-50%) translateX(6px);
}

@media (max-width: 640px) {
  .project-next {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-md);
  }

  .project-next__thumb {
    width: 100%;
    aspect-ratio: 1600 / 958;
  }

  /* Back into normal inline flow (see the comment on the base rule above)
     — sized in em so it scales with the title's own fluid font-size
     rather than staying a fixed 32px regardless of how large the text
     around it is. */
  .project-next__arrow {
    position: static;
    display: inline-block;
    vertical-align: middle;
    width: 0.8em;
    height: 0.8em;
    margin-left: 0.35em;
    transform: none;
  }

  .project-next:hover .project-next__arrow,
  .project-next:focus-visible .project-next__arrow {
    transform: translateX(6px);
  }
}
