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

:root {
  --bg:     #000;
  --fg:     rgba(255,255,255,0.92);
  --fg-mid: rgba(255,255,255,0.5);
  --fg-dim: rgba(255,255,255,0.22);
  --border: rgba(255,255,255,0.09);
  --font:   'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif;
  --ease:   cubic-bezier(0.16, 1, 0.3, 1);
  --hh:     68px;
}

html {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 14px;
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  scroll-behavior: smooth;
}

body { min-height: 100vh; overflow-x: hidden; }

/* ── PAGE TRANSITION OVERLAY ─────────────────────────────────────── */

.page-transition {
  position: fixed;
  inset: 0;
  background: #000;
  z-index: 9000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease;
}
.page-transition.active {
  opacity: 1;
  pointer-events: all;
}
/* Boot cover (set in the <head> before first paint) — keep the overlay up until JS
   establishes the initial view, so a reload never flashes the hero / main page. */
html.booting #pageTransition { opacity: 1; pointer-events: auto; }

/* ── CURSOR LABEL ────────────────────────────────────────────────── */

.cursor-label {
  position: fixed;
  top: 0; left: 0;
  z-index: 8999;
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font);
  font-size: var(--cursor-size, 11px);
  font-weight: 400;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg);
  white-space: nowrap;
  padding: 7px 14px;
  border: 1px solid rgba(255,255,255,0.28);
  background: rgba(0,0,0,0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.18s ease;
  will-change: transform;
  transform: translate(-300px, -300px); /* off-screen until first move */
}
.cursor-label.visible { opacity: 1; }

/* text-only style — no background/border */
.cursor-label.text-only {
  border-color: transparent;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  text-shadow: 0 1px 5px rgba(0,0,0,0.95), 0 0 12px rgba(0,0,0,0.7);
  padding: 0;
}

/* small square before the text */
.cursor-sq {
  display: block;
  width: 5px;
  height: 5px;
  background: currentColor;
  flex-shrink: 0;
}

/* ── SITE HEADER ─────────────────────────────────────────────────── */

.site-header {
  position: fixed;
  inset: 0 0 auto;
  height: var(--hh);
  /* Above the page-transition overlay (9000) so it never blinks during
     enter/exit transitions — it simply stays put while content swaps below. */
  z-index: 9100;
  display: flex;
  align-items: center;
  justify-content: flex-start; /* nav position controlled per-class below */
  padding: 0 36px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease, background 0.4s ease, border-color 0.4s ease;
}

/* ── Solid header (default): dark blurred panel ── */
.site-header.visible {
  opacity: 1;
  pointer-events: all;
}
.site-header.visible.style-solid {
  background: rgba(0,0,0,0.88);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

/* ── Floating header: nav words only, no panel ── */
.site-header.visible.style-floating {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: none;
}
/* Add text-shadow so links stay readable over any background */
.site-header.style-floating .nav-link,
.site-header.style-floating .back-btn,
.site-header.style-floating .header-logo {
  text-shadow: 0 1px 6px rgba(0,0,0,0.9), 0 0 20px rgba(0,0,0,0.5);
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.8));
}

.header-start { display: flex; align-items: center; gap: 18px; }

/* Language switch — fixed in the bottom-left corner */
.lang-switch {
  position: fixed;
  bottom: 22px; left: 24px;
  z-index: 195;
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11px; letter-spacing: 0.1em;
  padding: 6px 12px;
  border: 1px solid rgba(255,255,255,0.12);
  background: rgba(0,0,0,0.4);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border-radius: 999px;
}
.lang-btn {
  background: none; border: none; cursor: pointer;
  font-family: var(--font); font-size: 11px; letter-spacing: 0.1em;
  color: var(--fg-dim); padding: 0; transition: color 0.2s;
}
.lang-btn:hover { color: var(--fg); }
.lang-btn.active { color: var(--fg); font-weight: 500; }
.lang-sep { color: var(--fg-dim); }

@media (max-width: 768px) {
  .lang-switch { bottom: 16px; left: 16px; }
}

.header-logo-link { display: flex; align-items: center; margin-left: 12px; text-decoration: none; color: inherit; -webkit-text-decoration: none; }
.header-logo-link:hover, .header-logo-link:visited { text-decoration: none; color: inherit; }
.header-logo { height: var(--header-logo-h, 26px); width: auto; display: block; mix-blend-mode: screen; }

/* Text wordmark alternative to the logo image — treated as a graphic
   (no spellcheck squiggle, no text selection highlight). */
.header-wordmark {
  font-family: var(--header-font, var(--font));
  font-size: var(--header-brand-size, 16px);
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--header-color, var(--fg));
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none;
  user-select: none;
  -webkit-user-select: none;
}
body.brand-text  .header-logo { display: none; }
body.brand-logo  .header-wordmark { display: none; }

/* Transparent-PNG mode: show the logo as-is (no blend). Toggled in admin. */
body.logo-transparent .header-logo,
body.logo-transparent .hero-logo { mix-blend-mode: normal; }

/* Return / back button */
.back-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font);
  font-size: 11px; /* fixed — independent of the header nav font size */
  font-weight: 400;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--return-color, #e8e8e8);
  background: none;
  border: 1px solid rgba(255,255,255,0.32);
  border-radius: 6px;
  cursor: pointer;
  padding: 7px 16px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, color 0.2s;
  white-space: nowrap;
}
.back-btn:hover { color: #fff; }

.header-nav {
  display: flex; align-items: center; gap: var(--header-gap, 30px);
  transform: translateX(var(--nav-offset, 0px)); /* fine nudge */
}

/* Brand stays on its side; nav block position is set by these classes */
.site-header.nav-right  .header-nav { margin-left: auto; }
.site-header.nav-left   .header-nav { margin-left: 28px; }
.site-header.nav-center .header-nav {
  position: absolute; left: 50%;
  transform: translateX(calc(-50% + var(--nav-offset, 0px)));
}
/* Spread: nav fills the remaining width and distributes buttons with EQUAL gaps */
.site-header.nav-spread .header-nav {
  flex: 1; margin-left: 44px; gap: 0;
  justify-content: space-between;
}

.nav-link {
  font-size: var(--header-font-size, 11px); /* global; per-item inline style overrides */
  font-weight: 400;
  letter-spacing: var(--header-letter-spacing, 0.1em);
  text-transform: uppercase;
  color: var(--header-color, rgba(255,255,255,0.55));
  text-decoration: none;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--header-font, var(--font));
  padding: 0;
  transition: opacity 0.2s, color 0.2s;
  white-space: nowrap;
  opacity: 0.8;
}
.nav-link:hover { opacity: 1; }

/* ── PROJECT MODE ────────────────────────────────────────────────── */
/*
   Layout in project mode (DOM order: detail → carousel):
   [Detail section — project info, scrollable]
   [Carousel section — 100vh, stays visible below detail]
   Hero and contact/footer are hidden.
   Clicking a carousel item scrolls UP to the detail above it.
*/

body.project-mode .hero,
body.project-mode .contact-section,
body.project-mode .footer           { display: none !important; }

/* carousel stays visible in project mode */
body.project-mode .carousel-section { display: block !important; }
body.project-mode .detail-section   { display: block !important; }

body.project-mode .site-header {
  opacity: 1 !important;
  pointer-events: all !important;
}
body.project-mode .back-btn {
  opacity: 1 !important;
  pointer-events: all !important;
}

/* Carousel is frozen in project mode — hide controls, lock cursor */
body.project-mode .carousel-arrow,
body.project-mode .carousel-dots,
body.project-mode .carousel-progress { display: none !important; }

/* Frozen carousel is inert in project mode — not clickable */
body.project-mode .carousel-item { cursor: default !important; }

/* ── HERO ────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: pointer;
}

.hero-clickable { position: absolute; inset: 0; z-index: 5; }

.hero-video-wrap { position: absolute; inset: 0; }
.hero-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; pointer-events: none;
}
.hero-video-iframe {
  position: absolute; inset: 0;
  overflow: hidden; pointer-events: none;
}
.hero-video-iframe iframe {
  position: absolute; top: 50%; left: 50%;
  width: 177.78vh; height: 100vh;
  min-width: 100vw; min-height: 56.25vw;
  transform: translate(-50%, -50%);
  border: none; pointer-events: none;
}

/* Hero fallback still (from the master project) — shown if the hero video can't
   autoplay or errors. Above the video, below the dim overlay + logo. */
.hero-fallback {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; pointer-events: none;
  opacity: 0; transition: opacity 0.5s ease;
}
.hero-fallback.show { opacity: 1; }
/* Battery saver (mobile): cover ANY hero media — incl. YouTube/Vimeo iframes — with the still. */
body.autoplay-blocked .hero-fallback { opacity: 1; }

.hero-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0, var(--hero-dim, 0.35));
  z-index: 1;
}

.hero-logo-wrap {
  position: relative; z-index: 2;
  pointer-events: none;
}

/* Width driven by --hero-logo-w (set by JS from admin's logo size); falls back
   to a fluid clamp. Capped to a share of the viewport on mobile (see below). */
.hero-logo {
  width: var(--hero-logo-w, clamp(100px, 26vw, 500px));
  height: auto;
  display: block;
  mix-blend-mode: screen;
}

.hero-scroll-hint {
  position: absolute; bottom: 36px; left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  pointer-events: none;
}

.scroll-label {
  font-size: 11px; font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--fg); text-align: center; min-height: 14px;
  text-shadow: 0 1px 8px rgba(0,0,0,0.7), 0 0 16px rgba(0,0,0,0.45);
  transition: opacity 0.4s ease;
}
.scroll-label.swap { opacity: 0; }

.scroll-line {
  display: block; width: 1px; height: 48px;
  background: rgba(255,255,255,0.32);
  transform-origin: top;
  animation: tickDown 2.2s ease infinite;
}
@keyframes tickDown {
  0%   { transform: scaleY(0); opacity: 0; }
  20%  { opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}

/* ── CAROUSEL ────────────────────────────────────────────────────── */

.carousel-section {
  position: relative;
  width: 100%; height: 100vh;
  overflow: hidden; background: #000;
}
.carousel-track { position: absolute; inset: 0; }

/* One-time mobile "swipe hint": nudge the track sideways to reveal a sliver of
   the next project, then settle back with a soft bounce. Duration is set via
   --peek-dur (from admin). The next slide is staged off-screen by JS. */
@keyframes peekBounce {
  0%   { transform: translateX(0); }
  20%  { transform: translateX(-9%); }
  45%  { transform: translateX(-6%); }
  100% { transform: translateX(0); }
}
.carousel-track.peek {
  animation: peekBounce var(--peek-dur, 1.1s) cubic-bezier(0.34, 1.15, 0.4, 1) 1;
}

.carousel-item {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  background: #000;            /* any sub-pixel gap shows black, never the slide behind */
  opacity: 0;
  transition: opacity 0.85s ease;
  cursor: pointer;
  user-select: none;
  /* Only the visible (active) slide receives clicks — otherwise the stacked
     invisible slides intercept the click and open the wrong project. */
  pointer-events: none;
}
.carousel-item.active { opacity: 1; pointer-events: auto; }

/* Instant slide swap (no crossfade) — used when jumping between projects so the
   previous slide doesn't linger/flash as the wrong image during the transition. */
.carousel-track.instant .carousel-item { transition: none !important; }

/* Media backgrounds — overscanned 1px top/bottom so a sub-pixel rounding gap can never
   reveal a strip of the slide stacked behind during a crossfade. */
.ci-bg-video, .ci-bg-img {
  position: absolute; left: 0; right: 0; top: -1px;
  width: 100%; height: calc(100% + 2px);
  object-fit: cover; pointer-events: none;
}

/* Per-slide fallback still image. Hidden by default (video shows); revealed when the
   video is blocked (battery saver) or, with the off-screen option, while the carousel
   isn't full-screen. Sits above the media, below the dim + centered overlays. */
.ci-fallback {
  position: absolute; left: 0; right: 0; top: -1px;
  width: 100%; height: calc(100% + 2px);
  object-fit: cover; pointer-events: none;
  opacity: 0; z-index: 2;
  /* Cross-dissolve between the still and the (still-playing) video so returning to the
     carousel softly blends the image into the moving video instead of a hard cut. The
     swap is gated to when the carousel is ≤12% visible, so going to Contact it's mostly
     off-screen; coming back, this fade reveals the live video smoothly. */
  transition: opacity 0.55s ease;
}
/* On a device that can't autoplay (battery saver, decided once at load), EVERY slide shows
   its still — including YouTube/Vimeo iframe slides. Cover ALL slides (not just the active
   one) INSTANTLY so a crossfade swipe blends image→image and never flashes the video
   underneath. */
body.autoplay-blocked .ci-fallback { opacity: 1; transition: none; }
/* Off-screen option:
   - Inside a PROJECT page: cover the carousel video with the still (both scopes).
   - Whole-site scope (fallback-site) ALSO covers it on the Contact page.
   It cross-dissolves back to the live video as the carousel returns. Deliberately keyed to
   project-mode / contact-active (not raw visibility), so the hero→carousel reveal always
   shows the video, never the still. */
body.offscreen-fallback.project-mode.carousel-hidden .ci-fallback,
body.offscreen-fallback.fallback-site.contact-active .ci-fallback { opacity: 1; }

.ci-bg-iframe {
  position: absolute; inset: 0;
  overflow: hidden; pointer-events: none;
}
.ci-bg-iframe iframe {
  position: absolute; top: 50%; left: 50%;
  width: 177.78vh; height: 100vh;
  min-width: 100vw; min-height: 56.25vw;
  transform: translate(-50%, -50%);
  border: none; pointer-events: none;
}

.ci-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0, var(--carousel-dim, 0.18));
  pointer-events: none;
}

/* Optional centered text/image overlay per slide. Sits above the dim overlay
   but stays click-through so the slide itself still opens the project. */
.ci-center {
  position: absolute; inset: 0;
  z-index: 3;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  pointer-events: none;
  text-align: center;
}
.ci-center-text {
  font-size: var(--ci-size, 48px);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: 0.01em;
  max-width: 100%;
  white-space: pre-wrap;
  text-shadow: 0 2px 28px rgba(0,0,0,0.45);
}
/* Width comes from --ci-size (per item); max-width:none lets it grow past the
   slide so large images can fill / overflow the screen. Overflow is clipped by
   the carousel section, keeping the image centered. Capped on mobile below. */
.ci-center-img {
  width: var(--ci-size, 300px);
  max-width: none;
  height: auto;
  display: block;
  filter: drop-shadow(0 2px 24px rgba(0,0,0,0.4));
}

/* ── Carousel controls ───────────────────────────────────────────── */

/* Arrows: no circle, no background — just the icon. Visible by default,
   brighten on hover. Hidden entirely when disabled in admin (body.hide-arrows). */
.carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 12;
  /* Large invisible hit area (tall side strip) so it's clickable from further
     away. Only the icon is visible; the strip itself is transparent. */
  background: none;
  border: none;
  border-radius: 0;
  width: 130px;
  height: 60vh;
  padding: 0;
  color: rgba(255,255,255,0.9);
  cursor: pointer;
  display: flex;
  align-items: center;
  /* Hover-only: hidden until the carousel is hovered */
  opacity: 0;
  transition: opacity 0.5s ease, color 0.2s ease;
  /* Keep arrows readable over any background */
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.7));
}
.carousel-prev { justify-content: flex-start; padding-left: 22px; }
.carousel-next { justify-content: flex-end;   padding-right: 22px; }
/* Position-based reveal: left arrow shows only when the cursor is over the left
   edge zone, right arrow over the right edge zone; in the middle both fade out.
   These zone classes are set by JS on mousemove. */
.carousel-section.zone-left  .carousel-prev { opacity: 0.85; }
.carousel-section.zone-right .carousel-next { opacity: 0.85; }
.carousel-arrow:hover { opacity: 1; color: #fff; }
/* Temporary reveal (e.g. just after returning to the main page); fades out on its own */
.carousel-section.reveal-arrows .carousel-arrow { opacity: 0.85; }

.carousel-prev { left: 0; }
.carousel-next { right: 0; }

/* Toggle: hide arrows / dots when disabled in admin */
body.hide-arrows .carousel-arrow { display: none !important; }
body.hide-dots   .carousel-dots  { display: none !important; }

/* Edge blur/darkening that appears on the side you're approaching, signalling
   the arrow there is clickable. Driven by the zone-left / zone-right classes. */
.carousel-section::before,
.carousel-section::after {
  content: '';
  position: absolute; top: 0; bottom: 0;
  width: 130px;
  z-index: 9;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.carousel-section::before {
  left: 0;
  background: linear-gradient(to right, rgba(0,0,0,0.55), rgba(0,0,0,0));
  -webkit-mask-image: linear-gradient(to right, #000, transparent);
          mask-image: linear-gradient(to right, #000, transparent);
}
.carousel-section::after {
  right: 0;
  background: linear-gradient(to left, rgba(0,0,0,0.55), rgba(0,0,0,0));
  -webkit-mask-image: linear-gradient(to left, #000, transparent);
          mask-image: linear-gradient(to left, #000, transparent);
}
.carousel-section.zone-left::before  { opacity: 1; }
.carousel-section.zone-right::after  { opacity: 1; }
/* Never show the edge hints while inside a project */
body.project-mode .carousel-section::before,
body.project-mode .carousel-section::after { opacity: 0 !important; }

/* Idle fade: when watching the carousel on the main page without moving, the
   on-screen UI (arrows, dots, header) fades out. Any input brings it back. */
body.ui-idle .carousel-arrow,
body.ui-idle .carousel-dots,
body.ui-idle .site-header {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: opacity 0.6s ease !important;
}
/* On the Contact section (full-screen), fade the carousel dots out (JS pauses autoplay) */
body.contact-active .carousel-dots {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: opacity 0.5s ease !important;
}

/* Dots — show how many slides there are */
/* Dot indicator: a fixed-width viewport showing a few dots; the dot strip
   scrolls behind a fixed, always-highlighted center marker → looks like the
   dots are cycling each time the project changes. */
.carousel-dots {
  position: absolute; bottom: 26px; left: 50%;
  transform: translateX(-50%); z-index: 10;
  width: 44px;            /* shows ~3 dots; the buffer dots fade out at the edges */
  height: 12px;
  overflow: hidden;
  display: flex; align-items: center; justify-content: flex-start;
  cursor: pointer;
  /* fade the dots out at the edges so the current dot reads as centered */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 30%, #000 70%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 30%, #000 70%, transparent);
}
.dot-strip {
  display: flex; gap: 7px; align-items: center;
  will-change: transform;
}
.dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(255,255,255,0.38);
  border: none; padding: 0; flex: 0 0 auto;
  transition: transform 0.25s var(--ease), background 0.25s ease;
}
/* The current project's dot — centered by JS — is enlarged + brightened */
.dot-active {
  background: rgba(255,255,255,0.98);
  transform: scale(1.7);
}

/* Small cycling text above the dots (admin-editable; like the hero scroll hint).
   Colour is set inline by JS; fades on .swap while the word changes. */
.dots-label {
  position: absolute; bottom: 56px; left: 50%;   /* sits above the dots (26px), with clear separation */
  transform: translateX(-50%); z-index: 10;
  font-size: var(--dots-label-size, 11px); font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: #fff; text-align: center; white-space: nowrap;
  text-shadow: 0 1px 8px rgba(0,0,0,0.7), 0 0 16px rgba(0,0,0,0.4);
  pointer-events: none;
  transition: opacity 0.4s ease;
  /* transform (translateX) is set inline by JS to center the MIDDLE character over the
     highlighted dot; this is just the fallback before JS runs. */
}
/* Inner span carries the looping "tap/click" press — scale only, so it keeps playing
   through swipes and never fights the outer element's centering translateX. */
.dots-label-inner {
  display: inline-block;
  animation: dotsLabelClick 2.6s ease-in-out infinite;
}
@keyframes dotsLabelClick {
  0%, 64%, 100% { transform: scale(1);    }
  72%           { transform: scale(0.78); }
  80%           { transform: scale(1);    }
}
.dots-label.swap { opacity: 0; }
.dots-label:empty { display: none; }
body.project-mode .dots-label { display: none !important; }
body.ui-idle .dots-label,
body.contact-active .dots-label {
  opacity: 0 !important;
  transition: opacity 0.6s ease !important;
}

.carousel-progress {
  position: absolute; bottom: 0; left: 0;
  height: 2px; background: rgba(255,255,255,0.55);
  width: 0%; z-index: 10;
}
.carousel-progress.running {
  transition: width 5s linear;
  width: 100%;
}

/* ── DETAIL SECTION ──────────────────────────────────────────────── */

.detail-section { background: var(--bg); min-height: 100vh; }

.detail-fade { opacity: 1; transition: opacity 0.22s ease; }
.detail-fade.out { opacity: 0; }

/* Stage: a clean vertical stack with EQUAL spacing between every part.
   --proj-gap is the single gap used everywhere: header-strip → video → text,
   and (via the stage's bottom padding) text → photo grid. */
.detail-stage {
  --proj-gap: 46px;
  width: 100%; background: #060606;
  display: flex; flex-direction: column;
  align-items: center;
  gap: var(--proj-gap);          /* video ↔ text */
  /* Equal spacing all around: gap above the player (top padding) matches the
     gap below it, and the text ↔ grid gap (bottom padding). */
  padding: var(--proj-gap) 24px;
}
/* The container just centers the media; the media decides the aspect ratio. */
.detail-video-container {
  display: flex; align-items: center; justify-content: center;
  width: 100%;
  margin: 0;
}
/* Direct video files size to their NATIVE aspect ratio, capped to fit the screen */
.detail-video-container video {
  max-width: min(90vw, 1260px);
  max-height: 58vh;
  width: auto; height: auto;
  display: block; background: #111;
}
/* Embeds (YouTube/Vimeo) can't report their ratio cross-origin → keep 16:9.
   Width capped so the 16:9 height never exceeds 58vh (58vh×16/9 ≈ 103vh). */
.detail-video-container iframe {
  width: min(90vw, 1260px, 103vh);
  aspect-ratio: 16 / 9;
  border: none; display: block; background: #111;
}
/* When there's no text (player only), let the video grow to fill more space */
.detail-stage.no-info .detail-video-container video { max-height: 72vh; }
.detail-stage.no-info .detail-video-container iframe,
.detail-stage.no-info .detail-video-container.empty { width: min(92vw, 1500px, 128vh); }

/* Fallback still shown in place of the project player when the video fails / can't
   autoplay — sized like the player, no controls. */
.detail-video-container .detail-fallback-img {
  max-width: min(90vw, 1260px); max-height: 58vh;
  width: auto; height: auto; display: block; background: #111;
}
.detail-stage.no-info .detail-video-container .detail-fallback-img { max-height: 72vh; }

/* No media present → keep a 16:9 placeholder box */
.detail-video-container.empty {
  width: min(90vw, 1260px, 103vh);
  aspect-ratio: 16 / 9;
  background: #111;
}

.detail-info {
  width: 100%;
  max-width: 1000px; margin: 0 auto;
  padding: 0 16px;
}
.detail-title { margin-bottom: 12px; }
.detail-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.detail-client { font-size: 11px; font-weight: 400; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-mid); }
.detail-sep { color: var(--fg-dim); font-size: 11px; }
.detail-type { font-size: 11px; letter-spacing: 0.07em; color: var(--fg-dim); }
/* font-family / size / weight set at runtime via CSS vars from admin Style settings */
.detail-title { font-family: var(--title-font, var(--font)); font-size: var(--title-size, 40px); font-weight: var(--title-weight, 300); letter-spacing: -0.015em; line-height: 1.12; margin-bottom: 22px; }
/* font / size / color / weight overridden at runtime via CSS vars set in script.js */
.detail-description { font-family: var(--desc-font, var(--font)); font-size: var(--desc-size, 14px); line-height: 1.82; color: var(--desc-color, rgba(255,255,255,0.5)); font-weight: var(--desc-weight, 300); }

/* Hide the title (e.g. when showing technical specs) */
.detail-info.hide-title .detail-title { display: none; }

/* ── Technical specs (alternative to the description) ────────────── */
.detail-specs { display: none; }   /* shown only in specs mode */
.detail-info.mode-specs .detail-description { display: none; }
.detail-info.mode-specs .detail-specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 16px 40px;
  text-align: left;
  margin-top: 4px;
}
.detail-specs .spec-row {
  display: flex; flex-direction: column; gap: 4px;
  border-top: 1px solid var(--border);
  padding-top: 10px;
}
.detail-specs dt {
  font-size: 10px; font-weight: 400;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--fg-dim);
}
.detail-specs dd {
  font-family: var(--desc-font, var(--font));
  font-size: var(--desc-size, 14px);
  color: var(--fg);
  font-weight: 400;
}

/* Description toggled off → hide the whole info block (title, meta, description) */
.detail-info.no-desc { display: none; }

/* Width: match the video player, or span the full window */
.detail-info.width-video { max-width: min(99vw, 1920px); }
.detail-info.width-full  { max-width: 96vw; }
/* meta is inline-flex so it follows the info's text-align (left/center/right) */
.detail-info .detail-meta { display: inline-flex; }

.detail-photos {
  /* --photos-top is a single, consistent gap ABOVE the photo grid, applied the same
     way to every project (grid OR free layout) so the spacing is identical across
     projects — independent of each project's content above or its custom arrangement.
     (Free layout is desktop-only; on mobile the grid stacks and resets this.) */
  --photos-top: 60px;
  max-width: 1280px; margin: var(--photos-top) auto 0;
  padding: 0 20px var(--photos-top);   /* bottom gap matches the top gap */
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px;
  position: relative; z-index: 2;
}
.detail-photo {
  aspect-ratio: 16 / 9;            /* default uniform ratio */
  width: 100%; object-fit: cover;
  display: block; background: #0e0e0e;
  cursor: pointer;
  touch-action: manipulation;     /* reliable, no-delay tap on mobile */
}
.detail-photo-empty { aspect-ratio: 16 / 9; background: #0c0c0c; }
/* Photos 6+ live in this wrapper. On desktop it's transparent to layout
   (display:contents) so grid/free layout treat the photos as direct children.
   The peek/expand controls only appear on mobile (see media query). */
.photos-more-wrap { display: contents; }
.photos-fade, .photos-collapse { display: none; }
/* "Native" mode: each photo keeps its own aspect ratio instead of a 16:9 crop */
body.photo-native .detail-photo { aspect-ratio: auto; height: auto; object-fit: contain; }
/* Soft corners (admin option, default) — photogrid photos + About image. Applies on mobile
   AND desktop. The lightbox (.lightbox-img) is intentionally NOT included, so fullscreen photos
   stay sharp. */
body.photos-soft .detail-photo { border-radius: 12px; }
body.photos-soft .about-image  { border-radius: 12px; }

/* Free (per-project, drag/scale) layout — desktop only. The container becomes a
   fixed-aspect canvas; each photo is absolutely positioned (left/top/width/height
   set inline in %). aspect-ratio is set inline by JS. */
.detail-photos.free-layout {
  position: relative;
  display: block;
  grid-template-columns: none;
  gap: 0;
  padding: 0;
  margin: var(--photos-top) auto var(--photos-top);   /* same top & bottom gap as the grid layout */
  /* height is set inline (px) by JS to fit the photos (grows as you add more) */
}
.detail-photos.free-layout .detail-photo {
  position: absolute;
  aspect-ratio: auto;
  /* left / top / width / height set inline (px) by JS */
}

/* ── NEXT PROJECT (end of project page) ──────────────────────────── */

.detail-next {
  position: relative;
  width: 100%;
  height: 70vh;
  overflow: hidden;
  cursor: pointer;
  background: #000;
}
.detail-next-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 0.8s var(--ease), opacity 0.4s ease;
}
.detail-next:hover .detail-next-img { transform: scale(1.05); }

/* Centered carousel overlay (text/image) mirrored on the thumbnail. .ci-center
   inside is absolute/inset:0 and click-through, sitting above the dim overlay. */
.detail-next-center { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
/* Keep an oversized mirrored image from overflowing the (smaller) thumbnail */
.detail-next-center .ci-center-img { max-width: 72%; }

.detail-next-overlay {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 12px;
  background: rgba(0,0,0,0.32);
  pointer-events: none;
  z-index: 4;
}
/* When the project has a centered overlay, move the "Next Project · Title" to the
   bottom (with a gradient) so it doesn't clash with the mirrored overlay. */
.detail-next.has-center .detail-next-overlay {
  justify-content: flex-end;
  padding-bottom: 30px;
  background: linear-gradient(to top, rgba(0,0,0,0.55), rgba(0,0,0,0) 55%);
}
/* Hide the "Next Project" label + title entirely (admin toggle) */
.detail-next.no-label .detail-next-overlay { display: none; }
.detail-next-label {
  font-size: 10px; font-weight: 400;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: rgba(255,255,255,0.6);
}
.detail-next-title {
  font-family: var(--title-font, var(--font));
  font-size: clamp(22px, 3.5vw, 44px);
  font-weight: var(--title-weight, 300);
  letter-spacing: -0.015em;
  color: var(--fg);
  text-align: center;
  padding: 0 24px;
}

/* Cloned next-project card that expands to full screen (keeps its centered
   overlay during the transition). It's purely visual — clicks pass through. */
.next-expand {
  pointer-events: none;
  will-change: top, left, width, height, opacity;
}

/* Fullscreen expanding image used for the next-project transition */
.thumb-expand {
  position: fixed;
  z-index: 8000;            /* below header (9100) so the header stays put */
  background-color: #000;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  will-change: top, left, width, height, opacity;
}

/* ── CONTACT ─────────────────────────────────────────────────────── */

.contact-section { display: grid; grid-template-columns: 1fr 1fr; min-height: 100vh; scroll-margin-top: 60px; }
.contact-image-wrap { position: relative; overflow: hidden; background: #080808; }
/* Stacked cross-fade layers — JS toggles opacity to dissolve between images.
   Overscanned 1px top/bottom so a sub-pixel gap never reveals the layer behind. */
.contact-image {
  position: absolute; left: 0; right: 0; top: -1px;
  width: 100%; height: calc(100% + 2px);
  object-fit: cover; display: block;
  opacity: 1; transition: opacity 1.1s ease;
}
.contact-form-wrap { display: flex; flex-direction: column; justify-content: center; padding: 80px 64px; scroll-margin-top: 72px; }
/* Social media icon links */
.contact-socials { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 30px; }
.contact-socials:empty { display: none; }
.contact-social {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; color: var(--fg-mid);
  transition: color 0.2s, transform 0.2s;
}
.contact-social:hover { color: var(--fg); transform: translateY(-2px); }
.contact-social svg { width: 100%; height: 100%; display: block; }
/* Desktop only: when the footer is showing the social icons, drop the duplicate row
   from the contact page; and shrink the contact section so the whole section AND the
   footer fit on one screen when you jump to Contact from the header. (Footer is always
   hidden on phones, so on mobile the contact page keeps its own social row.) */
@media (min-width: 769px) {
  body.footer-on .contact-socials { display: none; }
  body.footer-on .contact-section { min-height: calc(100vh - 140px); }
}
/* About panel social links (under the text, or under the image if it's below the text) */
.about-socials { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; margin-top: 44px; }
.about-social {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; color: var(--fg-mid);
  transition: color 0.2s, transform 0.2s;
}
.about-social:hover { color: var(--fg); transform: translateY(-2px); }
.about-social svg { width: 100%; height: 100%; display: block; }
.contact-heading { font-size: clamp(26px, 3vw, 42px); font-weight: 300; letter-spacing: -0.015em; margin-bottom: 44px; }
.contact-form { display: flex; flex-direction: column; }
.form-row { border-top: 1px solid var(--border); }
.form-row:last-of-type { border-bottom: 1px solid var(--border); }
.form-row input, .form-row textarea {
  width: 100%; background: transparent; border: none; outline: none;
  color: var(--fg); font-family: var(--font); font-size: 14px;
  font-weight: 300; letter-spacing: 0.02em; padding: 16px 0; resize: none;
}
/* Auto-growing textarea: no scrollbar; height is set by JS to fit the content */
.form-row textarea { overflow: hidden; display: block; line-height: 1.5; }
.form-row input::placeholder, .form-row textarea::placeholder { color: var(--fg-dim); }
/* Required field left empty when Send was clicked — red underline + red placeholder. */
.form-row input.invalid, .form-row textarea.invalid {
  box-shadow: inset 0 -2px 0 #e8736b;
  color: #e8736b;
}
.form-row input.invalid::placeholder, .form-row textarea.invalid::placeholder { color: #e8736b; opacity: 1; }
/* Subtle shake on a missing/invalid field when Send is pressed. */
@keyframes fieldShake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-5px); }
  30% { transform: translateX(4px); }
  45% { transform: translateX(-3px); }
  60% { transform: translateX(3px); }
  75% { transform: translateX(-2px); }
}
.form-row input.shake, .form-row textarea.shake { animation: fieldShake 0.42s ease both; }
.form-submit {
  margin-top: 28px; align-self: flex-start;
  font-family: var(--font); font-size: 11px; font-weight: 400;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--bg); background: var(--fg); border: none; cursor: pointer;
  padding: 13px 36px; transition: opacity 0.2s;
}
.form-submit:hover { opacity: 0.78; }
.form-success { margin-top: 20px; font-size: 13px; letter-spacing: 0.04em; color: var(--fg-mid); display: none; }
.form-success.visible { display: block; }
.form-success.error { color: #e8736b; } /* couldn't send → shown in a warmer tone */

/* ── ABOUT PANEL ─────────────────────────────────────────────────── */

.about-panel {
  position: fixed; top: 0; right: 0;
  width: min(540px, 94vw); height: 100vh;
  background: #080808; border-left: 1px solid var(--border);
  z-index: 9200; /* above the header (9100) so it slides over everything */
  transform: translateX(100%); transition: transform 0.55s var(--ease);
  overflow-y: auto;
}
.about-panel.open { transform: translateX(0); }
/* Lock the page scroll underneath while the About panel is open (until you close it) */
body.about-open { overflow: hidden; }

/* Backdrop — covers everything except the panel; click to close */
.about-backdrop {
  position: fixed; inset: 0;
  z-index: 9190;               /* below the panel (9200), above the header (9100) */
  background: rgba(0,0,0,0.45);
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease;
}
.about-panel.open ~ .about-backdrop { opacity: 1; pointer-events: auto; }
.about-inner { padding: calc(var(--hh) + 28px) 40px 60px; position: relative; }
.about-close {
  position: absolute; top: 20px; right: 28px;
  font-family: var(--font); font-size: 24px; line-height: 1; font-weight: 300;
  color: var(--fg-mid); background: none; border: none; cursor: pointer;
  transition: color 0.2s, transform 0.2s; padding: 4px;
  display: flex; align-items: center; justify-content: center;
}
.about-close:hover { color: var(--fg); transform: scale(1.08); }
.about-image { width: 100%; height: auto; display: block; border-radius: 2px; margin-bottom: 34px; }
.about-image.at-bottom { margin-bottom: 0; margin-top: 34px; }
.about-bio { font-size: 14px; line-height: 1.82; font-weight: 300; color: var(--fg); margin-bottom: 52px; }
.about-representation { display: grid; grid-template-columns: 1fr 1fr; gap: 28px 16px; }
.rep-block { display: flex; flex-direction: column; gap: 5px; }
.rep-label { font-size: 10px; font-weight: 400; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-dim); }
.rep-name { font-size: 13px; font-weight: 400; letter-spacing: 0.03em; color: var(--fg); }

/* ── FOOTER ──────────────────────────────────────────────────────── */

.footer {
  display: flex; align-items: center; justify-content: center;
  padding: 22px 36px; border-top: 1px solid var(--border);
}
.footer-socials { display: flex; align-items: center; gap: 24px; }
.footer-social {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; color: var(--fg-mid);
  transition: color 0.2s, transform 0.2s;
}
.footer-social:hover { color: var(--fg); transform: translateY(-2px); }
.footer-social svg { width: 100%; height: 100%; display: block; }

/* ── ADMIN ACCESS BUTTON (fixed, always accessible) ─────────────── */

.admin-access-btn {
  /* Hidden from the public site — the admin panel is reachable directly at
     /admin.html (which requires a Supabase login). Flip to a non-none display
     to bring the on-page shortcut back. */
  display: none;
  position: fixed;
  bottom: 22px;
  right: 24px;
  z-index: 195;   /* below the transition overlay so it hides during transitions */
  font-family: var(--font);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.18);
  text-decoration: none;
  padding: 6px 10px;
  border: 1px solid rgba(255,255,255,0.08);
  transition: color 0.25s, border-color 0.25s, opacity 0.25s;
  background: transparent;
}
.admin-access-btn:hover {
  color: rgba(255,255,255,0.7);
  border-color: rgba(255,255,255,0.25);
}

/* ── RESPONSIVE ──────────────────────────────────────────────────── */

@media (max-width: 768px) {
  :root { --hh: 56px; }

  /* Full-screen sections use the DYNAMIC viewport height so the mobile browser's
     address bar doesn't hide the bottom of the hero/carousel (the 100vh bug).
     Browsers without dvh fall back to the 100vh declared in the base rules. */
  .hero,
  .carousel-section { height: 100dvh; }
  .contact-section,
  .detail-section   { min-height: 100dvh; }
  .about-panel      { height: 100dvh; }

  /* Header / footer: tighter gutters; nav sits closer and never grows too large */
  .site-header { padding-left: 18px; padding-right: 18px; }
  /* Footer is desktop-only — always hidden on phones (it's a social-links bar). */
  .footer { display: none !important; }
  .header-nav { gap: 18px; }
  .nav-link { font-size: min(var(--header-font-size, 11px), 13px); }

  /* Touch: no edge arrows (swipe instead), no pointer label */
  .carousel-arrow { display: none; }
  .cursor-label { display: none; }

  /* Keep the header (and brand) reachable on mobile even when idle — the
     auto-fade still hides the dots, but the header stays put and tappable. */
  body.ui-idle .site-header { opacity: 1 !important; pointer-events: auto !important; }

  /* Dots + their label don't fade on idle on mobile (always visible on the carousel),
     but they DO fade out on the Contact page (body.contact-active), like desktop. */
  body.ui-idle .carousel-dots,
  body.ui-idle .dots-label {
    opacity: 1 !important; pointer-events: auto !important;
  }

  /* Mobile arrows: shown on arrival/return to the carousel; fade while interacting and
     reappear after 3s (toggled by JS body classes). !important so the shared idle-fade
     rule (body.ui-idle .carousel-arrow{opacity:0!important}) can't keep them hidden. */
  body.show-mobile-arrows .carousel-arrow {
    display: flex; opacity: 0.85 !important;
    pointer-events: none !important; /* visual hint ONLY on mobile — navigation is by swipe */
    transition: opacity 0.6s ease;
  }
  body.show-mobile-arrows.mobile-arrows-faded .carousel-arrow { opacity: 0 !important; }

  /* Brand on mobile is ALWAYS the logo image (never the text wordmark). */
  body.brand-text .header-logo    { display: block !important; }
  body.brand-text .header-wordmark { display: none !important; }

  /* Manual photo-grid collapse (up-arrow) — shown only while the grid is expanded. */
  .photos-collapse {
    display: none; width: 100%; border: 0; cursor: pointer;
    background: none; color: var(--fg); padding: 14px 0 6px;
    align-items: center; justify-content: center;
  }
  .detail-photos.photos-expanded .photos-collapse { display: flex; }
  .photos-chevron-up { width: 30px; height: 30px; display: block; }

  /* Jumping to Contact leaves a thin strip of the carousel (the section right above it,
     since the project detail is hidden on the main page) behind the header — so it reads
     like the project-page header backdrop. Smaller = less carousel showing. */
  .contact-section { scroll-margin-top: 48px; }

  /* No mobile swipe-hint bounce on the carousel. */
  .carousel-track.peek { animation: none !important; }

  /* Center overlay: cap so a large desktop size can't overflow the phone */
  .ci-center { padding: 18px; }
  .ci-center-text { font-size: min(var(--ci-size, 48px), 13vw); }
  .ci-center-img  { width: min(var(--ci-size, 300px), 86vw); max-width: 86vw; }

  /* Hero logo: never wider than ~62% of the screen */
  .hero-logo { width: min(var(--hero-logo-w, 260px), 62vw); }

  /* Project page: smaller inner gap + gutters, capped title, shorter media.
     Equal 72px top & bottom padding so the player isn't cramped under the header
     and has matching breathing room below (after the description when it's shown).
     The video↔description gap stays the small --proj-gap. */
  .detail-stage { --proj-gap: 44px; padding: 72px 16px; }
  .detail-video-container video { max-height: 52vh; }
  .detail-stage.no-info .detail-video-container video { max-height: 64vh; }
  .detail-info { padding: 0 14px; }
  .detail-title { font-size: min(var(--title-size, 40px), 9vw); margin-bottom: 16px; }
  /* Free (desktop) layout reverts to the normal single-column stack on mobile */
  .detail-photos.free-layout {
    position: static; height: auto !important; aspect-ratio: auto !important;
    display: grid; gap: 26px; margin: 0; padding: 0 16px 56px;
  }
  .detail-photos.free-layout .detail-photo {
    position: static !important; left: auto !important; top: auto !important;
    width: 100% !important; height: auto !important; aspect-ratio: 16 / 9;
  }

  /* Photos 6+ collapse into a clipped wrapper that reveals only the START of the
     6th photo under a gradient + down-arrow. Tapping expands it downward; it
     auto-collapses after a period of no input (JS). Avoids a long scroll. */
  .photos-more-wrap {
    display: grid; grid-template-columns: 1fr; gap: 26px;
    position: relative; overflow: hidden;
    max-height: 120px;                 /* peek height of the 3rd photo (collapsed) */
    /* expanded max-height is set inline by JS to the REAL content height, so expand and
       collapse animate symmetrically (collapse = the expand played in reverse). */
    transition: max-height 0.55s cubic-bezier(0.4, 0, 0.2, 1);
  }
  /* Gradient + centered down-arrow sitting over the peek; fades out when expanded and
     fades back in as it collapses (instead of popping via display). */
  .photos-fade {
    display: flex; align-items: flex-end; justify-content: center;
    position: absolute; left: 0; right: 0; bottom: 0; height: 120px;
    padding-bottom: 12px; border: 0; cursor: pointer;
    background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.55) 55%, var(--bg) 100%);
    color: var(--fg);
    transition: opacity 0.45s ease;
  }
  .detail-photos.photos-expanded .photos-fade { opacity: 0; pointer-events: none; }
  .photos-chevron { width: 30px; height: 30px; display: block; animation: photoChevBob 1.8s ease-in-out infinite; }
  @keyframes photoChevBob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

  /* Photo grid → ONE vertical column: big, full-width photos stacked top-to-bottom */
  .detail-photos {
    display: grid;
    grid-template-columns: 1fr;
    gap: 26px;
    margin-top: 0;
    padding: 0 16px 56px;
  }
  .detail-next { height: 56vh; }

  /* Contact: stack image over the form */
  /* Tighter so the image + form (with the 2-row auto-grow message), the Send button
     AND the social icons all fit on screen when you open Contact (nothing clipped). */
  .contact-section { grid-template-columns: 1fr; min-height: 0; }
  .contact-image-wrap { height: 34vh; }   /* taller image → less carousel showing above it */
  .contact-form-wrap { padding: 18px 20px 34px; scroll-margin-top: 60px; }
  .contact-heading { margin-bottom: 14px; font-size: 25px; }
  .form-submit { margin-top: 16px; }
  /* Center the social icons so the fixed language / admin buttons (bottom corners)
     don't sit on top of them; tight top gap so the row isn't pushed off-screen. */
  .contact-socials { justify-content: center; margin-top: 14px; }
  /* iOS auto-zooms the page when focusing an input under 16px — keep them at 16px so
     filling the form (or any text box) never zooms in. */
  .form-row input, .form-row textarea { font-size: 16px; }
  /* About social icons are ALWAYS centered on mobile (the left/center/right setting is
     desktop-only). !important beats the inline justify-content JS sets from the setting. */
  .about-socials { justify-content: center !important; }

  /* About panel: tighter padding */
  .about-inner { padding: calc(var(--hh) + 24px) 24px 48px; }
  .about-bio { margin-bottom: 36px; }
}

/* ── Small phones ── */
@media (max-width: 560px) {
  /* Representation stacks to a single column */
  .about-representation { grid-template-columns: 1fr; }
  /* Slightly tighter video↔text gap on the smallest phones */
  .detail-stage { --proj-gap: 38px; }
}

/* ── LIGHTBOX (full-size photo viewer) ───────────────────────────── */
.lightbox {
  position: fixed; inset: 0; z-index: 9500;
  background: rgba(0,0,0,0.92);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 0.25s ease;
  cursor: default;
}
.lightbox.open { opacity: 1; pointer-events: all; }
/* Lock the page behind the open lightbox so swiping between photos can't scroll it */
body.lightbox-open { overflow: hidden; }

/* 3-cell sliding track: each cell is one viewport wide; the small cell padding
   is the only gap you see between adjacent photos while swiping. */
.lightbox-track {
  position: absolute; top: 0; left: 0; height: 100%;
  display: flex; will-change: transform;
  /* Base = centered on the middle cell. Each cell is exactly 100vw, so one cell =
     100vw — explicit units, no dependency on a computed %. JS only adds the drag
     offset vars (separate translate fns → never a calc with `+ -`). */
  transform: translateX(-100vw) translateX(var(--lb-x, 0px)) translateY(var(--lb-y, 0px)) scale(var(--lb-s, 1));
}
.lightbox-cell {
  flex: 0 0 100vw; width: 100vw;
  display: flex; align-items: center; justify-content: center;
  padding: 0 8px;
}
.lightbox-img {
  max-width: 96vw; max-height: 94vh;
  width: auto; height: auto;
  object-fit: contain;
  box-shadow: 0 10px 60px rgba(0,0,0,0.6);
}
.lightbox-close {
  position: fixed; top: 22px; right: 26px;
  width: 40px; height: 40px;
  border: none; background: none;          /* floating ✕ — no circle */
  color: #fff; font-size: 26px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  text-shadow: 0 1px 8px rgba(0,0,0,0.7);  /* stays visible over light photos */
  transition: opacity 0.2s, transform 0.2s;
}
.lightbox-close:hover { background: none; opacity: 0.65; transform: scale(1.08); }

/* Lightbox nav arrows — large invisible hit strip on each side so you can click
   from far away; only the icon is visible. (Close button keeps its circle.) */
.lightbox-nav {
  position: fixed; top: 50%; transform: translateY(-50%);
  width: 200px; height: 80vh;
  border: none;
  background: none;
  color: #fff; font-size: 44px; line-height: 1; cursor: pointer;
  display: flex; align-items: center;
  transition: color 0.2s;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.7));
}
.lightbox-prev { left: 0; justify-content: flex-start; padding-left: 26px; }
.lightbox-next { right: 0; justify-content: flex-end;   padding-right: 26px; }
.lightbox-nav:hover { filter: drop-shadow(0 1px 6px rgba(0,0,0,0.9)) brightness(1.2); }
.lightbox-count {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
  font-size: 11px; letter-spacing: 0.12em; color: rgba(255,255,255,0.6);
}

/* Mobile lightbox: shrink the side hit-strips so they don't blanket the whole
   image (they overlapped on small screens), keep the close button reachable. */
@media (max-width: 768px) {
  /* Arrows are desktop-only — on mobile you swipe between photos instead */
  .lightbox-nav { display: none !important; }
  .lightbox-img { max-width: 94vw; max-height: 84vh; }
  .lightbox-close { top: 14px; right: 14px; }
}
