/*
 * project.css — Project page layout.
 *
 * Two stacked sections inside .project-shell (gallery on top, description
 * below, both position: fixed). The description slides up over the gallery
 * via a snap transition. Static elements (header logo, Get in touch link,
 * info row, back arrow, toast) sit above both sections and remain visible
 * across the snap.
 *
 * BUILD_SPEC.md §5.2 (Project page), §2 (Captions, Snap transition,
 * Line reveal, Get in touch behavior).
 */

.project-page {
  --page-pad-x: 40px;
  --page-pad-y: 20px;
  --chrome-mask-top: 100px;
  --chrome-mask-bottom: 100px;
  --cursor-color: var(--project-highlight);
  background-color: var(--color-bg-light);
  color: var(--color-fg-light);
  overflow: hidden; /* sections are fixed; we manage scroll ourselves */
}

/* Chrome masks. Pseudo-element overlays at the top and bottom of the
 * viewport that hide any text scrolled in from the description section.
 * Z-index sits between content sections (1–2) and static elements (70+),
 * so static elements remain visible on top of the mask but scrolling
 * description text disappears behind it. Background matches the page so
 * the mask is invisible against the section's white surface — only the
 * masking effect on scrolling content is visible. */
.project-page::before,
.project-page::after {
  content: '';
  position: fixed;
  left: 0;
  right: 0;
  background-color: var(--color-bg-light);
  z-index: 50;
  pointer-events: none;
}

.project-page::before {
  top: 0;
  height: var(--chrome-mask-top);
}

.project-page::after {
  bottom: 0;
  height: var(--chrome-mask-bottom);
}

/* ---------- Project shell + section layering ---------- */

.project-shell {
  position: fixed;
  inset: 0;
}

.section-gallery,
.section-description {
  position: absolute;
  inset: 0;
  background-color: var(--color-bg-light);
  color: var(--color-fg-light);
}

/* Default landing: description is the base layer (z-content, no
 * transform). Gallery is the overlay (z 2) parked off-screen below
 * via translateY(100%) and slides up to cover the description when
 * the user snaps forward.
 *
 * v1.8 reversed the section order: the description used to be the
 * overlay and the gallery the base. The snap class moved with it
 * (.show-description -> .show-gallery). */
.section-description { z-index: var(--z-content); }
.section-gallery {
  z-index: 2;
  transform: translateY(100%);
  transition: transform var(--dur-sweep) var(--ease-sweep);
}

.project-shell.show-gallery .section-gallery {
  transform: translateY(0);
}

/* When the gallery overlay is showing, take the description out of
 * the pointer-event hit-test. Chrome (and Safari in some versions)
 * doesn't refresh its hover-state cache when an element slides under
 * a stationary cursor via CSS transform — text underneath can keep
 * receiving events even when visually covered. pointer-events: none
 * skips the description in hit-testing so wheel events and clicks
 * route to the gallery (and the window-level wheel listener) the way
 * they should from the moment the snap begins. */
.project-shell.show-gallery .section-description {
  pointer-events: none;
}

/* ---------- Site header (project: logo only, no Contact link) ---------- */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--page-pad-y) var(--page-pad-x);
  color: var(--color-fg-light);
  pointer-events: none;
  transition: opacity 0.6s ease;
}

/* SVG logomark — see home.css for the rationale (including the
 * align-self: flex-start that keeps the logo's vertical position
 * identical across all three pages). Background-color uses
 * --project-highlight (set per-project by project.js, falls back to
 * --color-accent) so the color is consistent across both sections.
 * The transition fires once on page load as JS sets the project color,
 * giving a subtle fade-in from the default blue. */
.site-logo {
  pointer-events: auto;
  display: inline-block;
  align-self: flex-start;
  margin-top: 6px;
  height: 32px;
  aspect-ratio: 1651.25 / 597.98;
  background-color: var(--project-highlight);
  mask-image: url('/assets/morro-logo.svg');
  -webkit-mask-image: url('/assets/morro-logo.svg');
  mask-size: contain;
  -webkit-mask-size: contain;
  mask-repeat: no-repeat;
  -webkit-mask-repeat: no-repeat;
  mask-position: left center;
  -webkit-mask-position: left center;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-highlight) ease;
}

/* ---------- Get in touch (top-right) ---------- */

.static-get-in-touch {
  position: fixed;
  top: var(--page-pad-y);
  right: calc(var(--page-pad-x) + 44px + var(--space-3));
  z-index: var(--z-info);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-fg-light);
  font-size: clamp(18px, 1.4vw, 22px);
  font-weight: var(--fw-regular);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  /* Reserve a fixed line-height to keep the dot vertically centered with
   * the label across font scaling. */
  line-height: 1;
  padding: var(--space-2) 0;
  transition: opacity 0.6s ease;
}

.static-get-in-touch .contact-icon {
  display: block;
  width: 24px;
  height: 24px;
  color: var(--project-highlight);
  flex: 0 0 auto;
  transition: color var(--dur-highlight) ease;
}

.static-get-in-touch .label {
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-thickness: 1px;
}

/* ---------- Info row (bottom strip) ---------- */

.static-info-row {
  position: fixed;
  left: var(--page-pad-x);
  right: var(--page-pad-x);
  bottom: var(--page-pad-y);
  z-index: var(--z-info);
  margin: 0;
  display: flex;
  align-items: flex-start;
  gap: clamp(20px, 3vw, 48px);
  color: var(--color-fg-light);
  transition: opacity 0.6s ease;
}

/* LINKS: dl cell, hidden when project.links is empty */
.info-links-cell {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  margin: 0;
}

.info-links-cell[hidden] { display: none; }

.info-links-cell dt {
  font-size: var(--fs-label);
  font-weight: var(--fw-light);
  letter-spacing: var(--ls-label);
  opacity: 0.7;
}

.info-links-cell dd {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-project-info-value);
  font-weight: var(--fw-light);
  line-height: var(--lh-snug);
}

/* Links anchors: inline-flex so the hover arrow aligns with the text */
.info-links-cell dd a {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

/* NEXT PROJECT: dl cell, always populated by JS, pushed to the far right */
.info-next-cell {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  margin: 0;
}

.info-next-cell[hidden] { display: none; }

.info-next-cell dt {
  font-size: var(--fs-label);
  font-weight: var(--fw-light);
  letter-spacing: var(--ls-label);
  opacity: 0.7;
}

.info-next-cell dd {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-project-info-value);
  font-weight: var(--fw-light);
  line-height: var(--lh-snug);
}

.info-next-cell dd a {
  color: inherit;
  text-decoration: none;
}

/* Persistent mobile → arrow; hidden on desktop (hover arrow takes its role) */
.info-next-arrow-mobile {
  display: none;
}

.info-nav-track {
  /* Width is set by JS to match the description text box exactly. flex-shrink: 0
   * prevents the track (and therefore the progress bar) from compressing when
   * LINKS and NEXT PROJECT are present in the same row. */
  flex: 0 0 auto;
  min-width: 0;
}

/* Label row: DESCRIPTION in flow, GALLERY absolute. display: flex eliminates
 * the implicit inline strut that would offset buttons below block-level dt text
 * in the LINKS cell (with the shared align-items: flex-start on the row). */
.info-nav-labels {
  position: relative;
  display: flex;
  align-items: flex-start;
}

/* Bar: position: fixed so its vertical position is completely independent
 * of the flex row height (which varies with LINKS cell font scaling).
 * left/right are set by JS (positionBar()). Only the left edge moves on
 * DESCRIPTION hover — right stays fixed so the bar never reaches the X. */
.info-progress-bar {
  position: fixed;
  bottom: 30px;
  height: 2px;
  background-color: rgba(0, 0, 0, 0.2);
  pointer-events: none;
}

.info-progress-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background-color: var(--project-highlight);
  transition: background-color var(--dur-highlight) ease;
}

/* DESCRIPTION and GALLERY section labels (dt-equivalent). */
.info-section-btn {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-label);
  font-weight: var(--fw-light);
  letter-spacing: var(--ls-label);
  opacity: 0.7;
  white-space: nowrap;
}

/* GALLERY button: absolute within .info-nav-labels; JS sets left: Xpx on
 * desktop to match the description content's right edge. */
.info-section-btn.is-gallery-btn {
  position: absolute;
  top: 0;
  left: 55%;
}

/* Hover arrows: slightly larger and thinner than the default info-arrow
 * size for external links, since the label font-size is the same but the
 * arrow reads better with a little more weight at this scale. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .info-section-btn .info-arrow-clip { height: 0.9em; }
  .info-section-btn:hover .info-arrow-clip { width: 0.9em; }
  .info-section-btn .info-arrow {
    width: 0.75em;
    height: 0.75em;
    stroke-width: 1.2;
  }

  /* Transition left so the bar's edge eases when DESCRIPTION is hovered.
   * The value is driven by JS (mouseenter/mouseleave on descBtn). */
  .info-progress-bar {
    transition: left 280ms cubic-bezier(.33, 1, .55, 1);
  }
}

/* ---------- Back arrow (bottom-right) ---------- */

.static-back-arrow {
  position: fixed;
  right: var(--page-pad-x);
  top: var(--page-pad-y);
  z-index: var(--z-info);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--color-fg-light);
  text-decoration: none;
  transition: opacity 0.6s ease;
}

.static-back-arrow svg {
  width: 36px;
  height: 36px;
}

/* ---------- Toast ("Email copied") ---------- */

.toast {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: var(--z-toast);
  transform: translate(-50%, -50%);
  padding: var(--space-3) var(--space-5);
  background-color: var(--color-toast-bg);
  color: var(--color-toast-fg);
  font-size: var(--fs-cta);
  font-weight: var(--fw-light);
  letter-spacing: var(--ls-display);
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
  white-space: nowrap;
}

.toast.is-visible { opacity: 1; }
.toast[hidden]    { display: none; }

/* ---------- Gallery section ----------
 * The gallery container fills its section. The track is the JS-driven
 * (desktop) or natively-scrolling (mobile) horizontal row inside it.
 */

.gallery {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  overflow: hidden;
}

.gallery-track {
  display: flex;
  align-items: center;
  gap: 20px;
  /* height is driven by the tallest item (picture + caption); the
   * track is vertically centered inside .gallery via align-items. */
  padding-left: var(--page-pad-x);
  padding-right: var(--page-pad-x);
  flex: 0 0 auto;
  /* No will-change: transform here. The browser auto-promotes the
   * track to a compositor layer while its CSS transition is running,
   * and forcing an always-on layer made wheel-event hit testing flaky
   * (the cursor sitting over the track during inertia caused Chrome
   * to route wheel events to the layer in a way that occasionally
   * stalled until pointer motion). */
}

/* Block-level figure. NOT flex — flex-column on the figure makes the
 * cross-axis stretch resolve oddly when child widths are content-
 * driven (it computes against the inner img's intrinsic pixel width
 * instead of its aspect-scaled width), and that breaks both the
 * gallery layout AND getBoundingClientRect on the figure, which is
 * what fullscreen.js measures to start the expand animation. */
.gallery-item {
  position: relative;
  flex: 0 0 auto;
  width: auto;
  margin: 0;
  cursor: zoom-in;
}


.gallery-item img,
.gallery-item video {
  display: block;
  height: min(70vh, 50vw);
  width: auto;
  -webkit-user-drag: none;
  user-select: none;
}

/* ---------- Caption ----------
 *
 * Positioned absolutely below the image so the figure's box equals
 * the image's box. This is what keeps every image's vertical center
 * aligned with every other image's, regardless of whether each one
 * has a caption — if the caption were in flow, the figure's height
 * would grow for captioned items and the track's center alignment
 * would push captioned images up and uncaptioned ones down.
 *
 * Captions extend BELOW the figure into the gallery's bottom empty
 * space (gallery is 100vh, images cap at 70vh, so ~15vh of room is
 * available below each image). The :empty rule skips rendering when
 * the caption text is blank.
 */

.gallery-caption {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  width: 100%;
  margin: 0;
  font-size: var(--fs-label);
  font-weight: var(--fw-light);
  letter-spacing: 0;
  line-height: var(--lh-snug);
  text-align: left;
  opacity: 0.6;
  color: var(--color-fg-light);
  pointer-events: none;
}

.gallery-caption:empty { display: none; }

/* ---------- Mobile arrow buttons ---------- */

.gallery-arrow {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: var(--z-info);
  width: 44px;
  height: 44px;
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--color-fg-light);
  background-color: rgba(255, 255, 255, 0.85);
  border-radius: 50%;
  cursor: pointer;
  transition: opacity var(--dur-ui) var(--ease-ui);
}

.gallery-arrow:hover,
.gallery-arrow:focus-visible { opacity: 0.85; }

.gallery-arrow svg { width: 22px; height: 22px; }

.gallery-arrow-prev { left: var(--space-3); }
.gallery-arrow-next { right: var(--space-3); }

/* ---------- Description section ---------- */

.section-description {
  /* Pad below the chrome masks so the initial first line sits clear of
   * the masked zones with a small breathing-room buffer. Padding scales
   * with the same --chrome-mask-* variables, so resizing the mask
   * automatically reshapes the section without per-viewport tweaks. */
  padding-top: calc(var(--chrome-mask-top) + 20px);
  padding-right: var(--page-pad-x);
  padding-bottom: calc(var(--chrome-mask-bottom) + 20px);
  padding-left: var(--page-pad-x);
  overflow-y: auto;
  /* Stop overscroll bounce at the top / bottom — the snap-to-gallery
   * handler used to call e.preventDefault() to block the bounce, but
   * that forced the wheel listener to be passive: false, which in turn
   * forced Chrome to route every wheel event on the page through the
   * throttled main-thread path (passive-ness is per-event, not per-
   * listener). Containing overscroll here lets the wheel listener be
   * passive: true. */
  overscroll-behavior-y: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.section-description::-webkit-scrollbar { width: 0; height: 0; }

.description-content {
  /* Left-anchored block, not centered: matches the legacy live site where
   * the description column sits at the left page padding and the right
   * side stays empty. */
  max-width: clamp(33ch, 58vw, 72ch);
  margin: 0;
  font-size: clamp(24px, 2.8vw, 80px);
  font-weight: var(--fw-light);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-display);
  text-align: justify;
}

.description-content p { margin: 0; }
.description-content p + p { margin-top: 0.9em; }

/* Line reveal markup (.reveal-line-clip / .reveal-line / .reveal-in)
 * lives in base.css so the Project description AND the Contact page
 * can both rely on it. */

/* ---------- Mobile layout ---------- */

@media (max-width: 768px) {
  /* Mobile arrow buttons visible (overrides desktop display: none) */
  .gallery-arrow:not([hidden]) { display: flex; }

  /* Mobile gallery: each image fits the viewport with small horizontal
   * margins. Native horizontal scroll with snap-to-image; touch-action
   * locks horizontal panning to the track so vertical swipes propagate to
   * the snap-transition handler on document.
   *
   * width: 100% is load-bearing — without an explicit width the track's
   * intrinsic size equals its content, so the items don't overflow it and
   * overflow-x: auto becomes a no-op (arrow scrollBy + horizontal swipe
   * both silently do nothing). */
  /* Mobile gallery: each item takes 96vw with equal 2vw margins on
   * both sides and a 2vw gap to the next item — so when an item is
   * snapped at center, its left and right margins are identical and
   * no part of the neighbour bleeds into the viewport. */
  .gallery-track {
    width: 100%;
    height: auto;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
    touch-action: pan-x;
    transform: none !important; /* desktop JS-driven transform is disabled */
    padding-left: 2vw;
    padding-right: 2vw;
    gap: 2vw;
  }
  .gallery-track::-webkit-scrollbar { width: 0; height: 0; }

  /* Each item shrinks to the rendered image's width (capped at 96vw).
   * Previously the item was a fixed 96vw box and the image inside used
   * object-fit: contain — for portrait images the image content was
   * centered inside the 96vw box, but the caption (100% of box) sat
   * anchored to the item's left edge, visibly left of the image
   * itself. Letting the figure shrink to the image keeps the caption
   * left-edge aligned with the image left-edge, and as a side benefit
   * the fullscreen expand animation now measures the image's actual
   * bounds instead of the wider padded box. */
  .gallery-item {
    width: fit-content;
    max-width: 96vw;
    height: auto;
    scroll-snap-align: center;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
  }


  .gallery-item img,
  .gallery-item video {
    display: block;
    width: auto;
    height: auto;
    max-width: 96vw;
    max-height: 65vh;
  }

  /* Mobile gallery: captions back to in-flow. The mobile track has
   * overflow-y: hidden (required for the horizontal scroll-snap to
   * work cleanly), which clips desktop's absolute-positioned caption
   * below the figure. Since mobile shows one image at a time via
   * snap-to-center, the figure-alignment trade-off the desktop
   * absolute caption was solving isn't visible here — adjacent items
   * aren't side-by-side, so per-item caption-height variance is fine. */
  .gallery-caption {
    position: static;
    top: auto;
    width: 100%;
    margin: var(--space-2) 0 0;
  }
}

@media (max-width: 600px) {
  /* Mobile static-element positions match the legacy live site. */
  .static-get-in-touch {
    top: var(--page-pad-y);
    left: var(--page-pad-x);
    right: auto;
    font-size: clamp(18px, 4.8vw, 20px);
  }
  .static-get-in-touch .contact-icon { width: 17px; height: 17px; }

  .static-back-arrow {
    width: 32px;
    height: 32px;
  }
  .static-back-arrow svg { width: 28px; height: 28px; }

  /* Hide the page logo on mobile to keep the chrome minimal — the back
   * arrow already handles "return to Home" on small screens. */
  .site-header { display: none; }

  /* Mobile info row: back-arrow is at top-right so bar extends fully.
   * right: 16px aligns the NEXT PROJECT arrow's left edge with the X
   * button's left edge (X: right 40px, width 32px → left edge 72px from
   * right; arrow: right 16px, width 56px → left edge 72px from right). */
  .static-info-row {
    left: 20px;
    right: 16px;
    gap: clamp(24px, 5vw, 40px);
  }
  /* Mobile: nav-track fills available row space; no JS-set width. */
  .info-nav-track { flex: 1 1 auto; }

  /* Mobile: GALLERY returns to normal flow — just a gap after DESCRIPTION.
   * Absolute positioning and JS measurement are desktop-only. */
  .info-nav-labels {
    gap: clamp(48px, 10vw, 64px);
  }
  .info-section-btn.is-gallery-btn {
    position: static;
    left: auto;
  }
  .info-links-cell dt {
    font-size: clamp(11px, 2.8vw, 13px);
  }
  .info-links-cell dd {
    font-size: clamp(15px, 3.5vw, 20px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .info-section-btn {
    font-size: clamp(11px, 2.8vw, 13px);
  }

  /* Mobile: hide LINKS, show NEXT PROJECT as a bare → arrow icon.
   * Cancel the desktop margin-left: auto on NEXT PROJECT — the nav-track
   * uses flex: 1 1 auto here, so auto margins are not needed and would
   * prevent the track from growing. */
  .info-links-cell { display: none !important; }
  .info-next-cell { margin-left: 0 !important; }

  .info-next-cell dt,
  .info-next-name { display: none; }

  .info-next-cell dd { margin: 0; }

  .info-next-arrow-mobile {
    display: block;
    width: 56px;
    height: 40px;
    stroke-width: 1;
  }

  .description-content {
    text-align: left;
    font-size: 28px;
    line-height: 1.13;
  }
  /* Mobile keeps the same mask-aware padding from the default rule,
   * so no override is needed here. */
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .section-description       { transition: none; }
  .reveal-line               { transition: none; }
  .gallery-track             { scroll-behavior: auto; }
}
