/* ============================================================================
   SHOWCASE  —  the "bilder." photo carousel
   Fixed-height page: header / title / carousel / caption, no page scroll.
   ========================================================================== */

.page-showcase {
  --content-max: 1620px;

  /* One dial for the whole carousel, same as the --scale in your draft.
     2.5 is the 750px card you had. The arrow size and the gap follow it. */
  --card-scale: 2.5;
  --card-target: calc(300px * var(--card-scale));

  /* Vertical space the header and caption need. Only kicks in on short
     screens: the card shrinks instead of being clipped by overflow:hidden. */
  --card-reserved: 28rem;

  --card-size: min(var(--card-target), calc(100vh - var(--card-reserved)), 92vw);
  --card-gap: calc(2rem * var(--card-scale) / 1.5);
  --caption-size: calc(1rem * var(--card-scale) / 1.3);

  /* Nav buttons are a fixed size rather than scaling with the cards — they
     are controls, not content, and want to stay the same at any card size. */
  --nav-size: 3.6rem;
  --nav-glyph: 2rem;

  /* --- Vertical rhythm, top to bottom. All vmin so the spacing keeps its
     proportions as the window changes, like the rest of the page. --------- */

  /* Title block down to the photos. Was `auto`, which pushed the photos to
     the bottom and pooled the leftover height up here; a fixed value keeps
     them near the title and lets the slack settle below the caption. */
  --titles-gap: 2vmin;
  /* Breathing room above the cards, so a tilted corner has somewhere to go. */
  --tilt-room: 5vmin;
  /* Bottom of the photos down to the caption. Lower = tighter. */
  --caption-gap: 3.3vmin;

  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding-block: 4vmin 3vmin;
}

.page-showcase .top-bar,
.showcase__titles,
.caption {
  flex: 0 0 auto;
}

.showcase__titles {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4vmin;
  padding-top: 3vmin;
}

.showcase__title {
  font-size: clamp(2.5rem, 9vmin, 7rem);
}

.showcase__counter {
  letter-spacing: 0.16em;
  padding-bottom: 0.4rem;
}

/* Labels the counter rather than the caption. The header never reflows, so
   the word holds still while the number beside it changes — which is the
   whole reason it lives up here instead of down in the typed line. */
.showcase__counter-label {
  color: var(--accent-soft);
  margin-right: 0.9rem;
}

/* --- Carousel ------------------------------------------------------------ */

/* Sizes to its content rather than absorbing the leftover height.
   With flex-grow the carousel would swallow any space freed below the cards
   and hand half of it straight back to the gap, so --caption-gap would do
   roughly half of what it says. */
.carousel {
  flex: 0 0 auto;
  margin-top: var(--titles-gap);
  position: relative;
  display: flex;
  align-items: center;
}

/* Fades the strip out against the background at both edges. */
.carousel__fade {
  position: absolute;
  top: 0;
  height: 100%;
  width: calc(var(--card-size) / 10);
  z-index: 5;
  pointer-events: none;
}

/* Offset by the gutter so the fade lines up with the scrolling strip
   rather than the wrapper's outer edge. */
.carousel__fade--left {
  left: var(--gutter);
  background: linear-gradient(to right, var(--bg) 0%, transparent 100%);
}

.carousel__fade--right {
  right: var(--gutter);
  background: linear-gradient(to left, var(--bg) 0%, transparent 100%);
}

/* Frosted translucent squares, floating over the neighbouring photo.
   Deliberately not .btn-ghost — these sit on images, not on background,
   so they need a fill to stay legible rather than an outline. */
.carousel__nav {
  position: absolute;
  /* Measured to the middle of the photo, not the middle of the container.
     The track's padding is asymmetric now (tilt room above, caption gap
     below), so top:50% would sit noticeably high. */
  top: calc(var(--tilt-room) + var(--card-size) / 2);
  transform: translateY(-50%);
  z-index: 10;
  display: grid;
  place-items: center;
  width: var(--nav-size);
  height: var(--nav-size);
  padding: 0;
  border: none;
  background: rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(4px);
  color: var(--text);
  font-size: var(--nav-glyph);
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s ease, opacity 0.2s ease;
}

.carousel__nav:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.4);
}

.carousel__nav:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.carousel__nav--prev { left: calc(var(--gutter) + 2.5rem); }
.carousel__nav--next { right: calc(var(--gutter) + 2.5rem); }

/* No hover on touch, so the arrows are dead weight there. */
@media (pointer: coarse) {
  .carousel__nav { display: none; }
}

.carousel__track {
  display: flex;
  align-items: center;
  gap: var(--card-gap);
  width: 100%;
  overflow-x: auto;
  overflow-y: visible;
  scroll-snap-type: x mandatory;
  scroll-snap-stop: always;
  scroll-behavior: smooth;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  padding-block: var(--tilt-room) var(--caption-gap);

  /* Horizontal padding is set from JS so the first and last card can centre. */

  scrollbar-width: none;
  -ms-overflow-style: none;
}

.carousel__track::-webkit-scrollbar {
  display: none;
}

.carousel__track.is-dragging,
.carousel__track.is-dragging .card {
  cursor: grabbing;
}

/* Both of these fight a drag, for different reasons.

   scroll-behavior: every scrollLeft we write would be animated, so the strip
   lags behind the mouse instead of tracking it.

   scroll-snap-type: `mandatory` means the container must sit on a snap point
   whenever it is not actively scrolling — and a scrollLeft assignment counts
   as finished the moment it happens. So the browser re-snapped after every
   single mousemove, which is why dragging jumped a whole card at a time
   instead of following the cursor. Native trackpad swipes are exempt because
   the browser knows the gesture is still in progress.

   Stays off through the release animation too, otherwise snap yanks the
   strip into place instantly and the easing is never seen. */
.carousel__track.is-unsnapped {
  scroll-snap-type: none;
  scroll-behavior: auto;
}

/* --- Cards --------------------------------------------------------------- */

.slide {
  flex: 0 0 auto;
  perspective: 1200px;
  scroll-snap-align: center;
}

.card {
  position: relative;
  width: var(--card-size);
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  transform-style: preserve-3d;
  transition: transform 0.2s ease;
  cursor: zoom-in;
  margin: 0;
}

.card__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}

/* "view" chip, bottom right, fades in on hover. */
.card__badge {
  position: absolute;
  inset: auto 0.9rem 0.9rem auto;
  z-index: 3;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  color: var(--text-overlay);
  background: rgba(10, 10, 10, 0.45);
  backdrop-filter: blur(3px);
  border: 1px solid var(--line-strong);
  padding: 0.4rem 0.7rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s ease;
}

/* Radial highlight that tracks the cursor. JS sets the background. */
.card__shine {
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  pointer-events: none;
  mix-blend-mode: screen;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.25s ease;
}

.card.is-hovered .card__badge,
.card.is-hovered .card__shine {
  opacity: 1;
}

.card.is-hovered .card__shine {
  visibility: visible;
}

/* --- Caption ------------------------------------------------------------- */

/* Centred under the middle card, which is itself centred in the strip.
   Holds only the text and the cursor, so everything in here is supposed to
   move as the line is typed.

   gap is 0 on purpose: a flex `gap` applies between EVERY child, so any
   value here wedges itself between the text and the cursor and leaves the
   cursor floating a space-width past the last character. */
.caption {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 0;
  min-height: calc(var(--caption-size) * 1.6);
  margin-bottom: 3.5vmin;
}

.caption__label {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent-soft);
}

.caption__text {
  font-family: var(--font-caption);
  font-size: var(--caption-size);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
}

/* The cursor sets its own font-size on purpose. It is a sibling of
   .caption__text, not a child, so bare `em` here would resolve against the
   parent's 1rem and render a tiny block no matter how large the caption is. */
.caption__cursor {
  font-size: var(--caption-size);
  display: inline-block;
  width: 0.55em;
  height: 1em;
  background: var(--text);
  transform: translateY(0.12em);
  animation: caption-blink 1.5s linear infinite;
}

/* Solid while characters are appearing, blinking once the line settles. */
.caption.is-typing .caption__cursor {
  animation: none;
  opacity: 1;
}

@keyframes caption-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* --- Lightbox ------------------------------------------------------------ */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
  cursor: zoom-out;
  background: rgba(8, 8, 8, 0);
  backdrop-filter: blur(0);
  transition: background 0.4s ease, backdrop-filter 0.4s ease;
}

.lightbox.is-open {
  display: flex;
}

.lightbox.is-visible {
  background: rgba(8, 8, 8, 0.94);
  backdrop-filter: blur(8px);
}

.lightbox__image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  display: block;
  border: 1px solid rgba(255, 255, 255, 0.15);
  box-shadow: var(--shadow-lightbox);
  transform-origin: top left;
  will-change: transform, opacity;
}

.lightbox__caption {
  position: absolute;
  left: var(--gutter);
  bottom: 3.5vmin;
  display: flex;
  align-items: baseline;
  gap: 1.2rem;
  opacity: 0;
  transition: opacity 0.35s ease 0.1s;
}

.lightbox.is-visible .lightbox__caption {
  opacity: 1;
}

.lightbox__hint {
  position: absolute;
  right: var(--gutter);
  bottom: 3.5vmin;
  pointer-events: none;
}
