/* ================================================================
   COMPONENT: LIGHTBOX — every screen on a case page opens at full size
   ================================================================
   WHY IT EXISTS. The stepper stands whole phones in a square well, which
   renders a 393pt-wide interface at ~41% of its design size — legible as an
   OBJECT, not as an interface. That is the right call for the rail (a phone
   on a dark ground reads as a phone) but it costs the thing a case page is
   for: showing the work. `admin-test.pindo.info` in the merchant screens'
   URL bar is the chapter's whole product argument, and at 41% it is a smear.
   This is what pays that back — one click and the capture is at full size.

   IT IS NOT A GALLERY. No next/previous, no thumbnails, no counter, no
   slideshow. One picture, opened and closed. A gallery would make the page's
   pictures a collection to browse; they are evidence attached to a sentence,
   and the reader should come straight back to the sentence.

   THE TRIGGER IS A REAL BUTTON. `js/modules/lightbox.js` wraps each qualifying
   picture in one at init rather than putting `role="button"` on the image:
   `DESIGN.md → Focus` — anything clickable is an `<a>` or a `<button>`, which
   is the rule the Experience accordion broke by being a `<div>`. Wrapping in
   JS keeps 23 figures of markup clean; the button inherits the picture's box
   so nothing moves.
   ================================================================ */

/* ── The trigger ───────────────────────────────────────────────── */
/* A pass-through box: no chrome of its own, inherits the layout slot the
   picture used to occupy. `display: block` + `width: 100%` is what makes it
   transparent to the mat's grid — it becomes the grid item and the picture
   fills it exactly as before. */
.case-zoom {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  /* The custom cursor replaces the system one (DESIGN.md → Cursor), so this
     is `default`, not `pointer` — `data-cursor="viewing"` is what signals
     that it opens. */
  cursor: default;
}

/* IN THE STEPPER THE BUTTON TAKES THE PICTURE'S PLACEMENT. The rail's phone is
   absolutely positioned inside the well; the wrapper has to become that box,
   or it would collapse to a zero-height static block and there would be
   nothing to click. The picture then simply fills it. Kept in step with
   `course-stepper.css` — the two rules describe one box. */
/* NO `aspect-ratio` HERE — the picture states it. An earlier pass hardcoded
   393/852 on the button, which is only two of the six captures; the other four
   are 1260/2736 and were being stretched 0.2% to fit a ratio that was not
   theirs. Height is the constraint, the image's own proportions set the width,
   and the button shrink-wraps it. */
.course-stepper--rail .course-stepper__shot .case-zoom {
  position: absolute;
  top: var(--sp-5);
  left: 50%;
  transform: translateX(-50%);
  height: calc(100% - var(--sp-10));
  width: auto;
  line-height: 0;   /* or the inline box adds a few px under the picture */
}

.course-stepper--rail .course-stepper__shot .case-zoom img {
  position: static;
  transform: none;
  height: 100%;
  width: auto;
}

/* ── Scroll lock ───────────────────────────────────────────────── */
/* Lenis is stopped by the module, which covers the smooth path; this covers
   the native one (reduced motion, or a CDN miss, where Lenis never ran).
   `overflow: hidden` on the root only — NOT a `position: fixed` body, which
   would lose the scroll position and re-run every ScrollTrigger measurement
   on close. */
html.lightbox-open {
  overflow: hidden;
}

/* ── The overlay ───────────────────────────────────────────────── */
/* THE GROUND IS INK, NOT THE TEAL `--surface-overlay`. Teal is this site's
   labelling colour and reads as decoration; behind a screenshot it would tint
   the work, which is the exact thing `DESIGN.md → Color` forbids ("do not tint
   the work"). Warm ink at 88% is a darkroom, not a colour. */
/* THE CLOSE BUTTON'S ROW IS RESERVED, NOT SHARED. The picture is centred in
   this box and a wide capture can reach the full padded width, so on a short
   window its top-right corner arrives underneath a control floating in the
   viewport corner — they were clearing each other by 48px on a 1440x760
   laptop, and had collided outright before the caption got its own height
   budget. Incidental clearance is not clearance. The top padding is therefore
   derived from the control itself — inset, button, inset again — so no image
   can ever reach the row it sits in, and the two stay apart if the button's
   size or inset ever changes. */
.lightbox {
  --lb-close-size: 44px;    /* WCAG touch target */
  --lb-close-inset: var(--sp-6);

  position: fixed;
  inset: 0;
  z-index: 300;   /* over the navbar (90) and the chapter nav */
  display: grid;
  place-items: center;
  padding:
    calc(var(--lb-close-inset) * 2 + var(--lb-close-size))
    var(--sp-10)
    var(--sp-10);
  /* Safety net only — the height budget below is meant to prevent this, but a
     very long alt on a short window should scroll rather than clip. */
  overflow: auto;
  background: color-mix(in srgb, var(--ink-900) 88%, transparent);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity    var(--dur-normal) var(--ease-out),
    visibility 0s linear var(--dur-normal);
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
  transition:
    opacity    var(--dur-normal) var(--ease-out),
    visibility 0s;
}

/* The picture. `max-height` leaves the caption room; the width cap is the
   capture's own natural size, so a 393px export is never upscaled into a
   blur — it opens at 1:1 and stops. That is the honest ceiling: the lightbox
   shows what the asset actually holds. */
/* THE CAPTION IS PART OF THE HEIGHT BUDGET. `100vh - padding` alone gave the
   picture the whole viewport and pushed the caption off the bottom edge — the
   figure measured 920 in a 900 window. The reserve is 5.5rem: two lines of
   `--type-body2` at 1.5 plus the figure's gap, which is what the longest alt
   on the four pages needs. `dvh` so a mobile browser's collapsing toolbar does
   not re-clip it mid-scroll. */
.lightbox__img {
  display: block;
  max-width: min(100%, var(--lightbox-natural, 100%));
  max-height: calc(100dvh - var(--lb-close-inset) * 2 - var(--lb-close-size) - var(--sp-10) - 5.5rem);
  width: auto;
  height: auto;
  /* Fallback only. `lightbox.js` copies the thumbnail's own corner onto this
     element on open, so the opened picture keeps the shape it was clicked in —
     the stepper's phone arrives with its `13% / 6%` device corner rather than
     a flat one a quarter the size. This value is what shows if a picture has
     no radius of its own to inherit. */
  border-radius: var(--radius-md, 12px);
  /* The one place a resting shadow is allowed: this is not a surface on the
     sheet, it is an object lifted off it. DESIGN.md → Shadow. */
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
  transform: scale(0.96);
  transition: transform var(--dur-normal) var(--ease-out);
}

.lightbox.is-open .lightbox__img {
  transform: scale(1);
}

.lightbox__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  max-width: 100%;
}

/* Caption on ink — the alt text's own sentence, so the picture keeps its
   description when it is the only thing on screen. */
.lightbox__caption {
  max-width: 60ch;
  text-align: center;
  font-size: var(--type-body2-size);
  font-weight: var(--weight-book);
  line-height: 1.5;
  color: color-mix(in srgb, var(--paper) 72%, transparent);   /* 7.9:1 on ink */
}

/* ── Close ─────────────────────────────────────────────────────── */
/* Top-right, away from the picture. It is a real button and the FIRST focus
   stop when the dialog opens, so a keyboard user's first Tab is not a hunt. */
.lightbox__close {
  position: absolute;
  top: var(--lb-close-inset);
  right: var(--lb-close-inset);
  display: grid;
  place-items: center;
  width: var(--lb-close-size);
  height: var(--lb-close-size);
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--paper) 12%, transparent);
  color: var(--paper);
  cursor: default;
  transition: background-color var(--dur-fast) var(--ease-out);
}

.lightbox__close:hover {
  background: color-mix(in srgb, var(--paper) 22%, transparent);
}

.lightbox__close svg {
  width: 18px;
  height: 18px;
}

/* Focus lands on ink here, so the ring flips to the light token — the same
   pairing the footer's ink block uses. `outline`, never `box-shadow`. */
.lightbox__close:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-on-dark);
  outline-offset: var(--focus-ring-offset);
}

/* ── Motion off ────────────────────────────────────────────────── */
/* No scale, no fade — it appears and it goes. The dialog still works
   identically; only the choreography is dropped. */
@media (prefers-reduced-motion: reduce) {
  .lightbox,
  .lightbox__img,
  .lightbox__close {
    transition: none;
  }
  .lightbox__img,
  .lightbox.is-open .lightbox__img {
    transform: none;
  }
}

@media (max-width: 768px) {
  /* Only the inset moves. The reserved top row still derives from it, so the
     picture keeps clearing the control without restating the arithmetic. */
  .lightbox {
    --lb-close-inset: var(--sp-4);
    padding-inline: var(--sp-4);
    padding-bottom: var(--sp-4);
  }
  /* One more line of caption reserve — the 60ch measure wraps further at 343px. */
  .lightbox__img {
    max-height: calc(100dvh - var(--lb-close-inset) * 2 - var(--lb-close-size) - var(--sp-4) - 7rem);
  }
}
