/*
  Full-size screen captures: the magnifier on a shot, and the modal it opens.

  WHY THIS EXISTS

  A product screenshot inside a half-height frame is legible as a composition
  and illegible as a screen -- a 1920px capture is drawn at roughly 400px tall
  in the case-study gallery, and every label in it is gone. So the frames stay
  the size the layout wants, and the real capture is one click away. The
  affordance has to be visible before the click, which is what .shot__zoom is:
  hover (or keyboard focus) puts a magnifier over the image so it reads as
  something to open rather than as decoration.

  WHY <dialog>

  The modal is a native <dialog> opened with showModal(), built once by
  js/lightbox.js. That buys the three things a hand-rolled overlay always ends
  up reimplementing badly: the top layer (nothing on the page can paint over
  it, whatever the z-index war looks like), a focus trap with focus returned
  to the shot that opened it, and Escape. ::backdrop is the dim behind it, so
  there is no scrim element either.
*/

/* The magnifier, inlined as a data URI and used as a MASK -- see
   .shot__zoom::after. It lives here rather than in css/variables.css because
   it is not a design decision anything else consults; it is this component's
   one glyph. `#` must stay percent-encoded or the URL ends at the fragment. */
:root {
  --icon-zoom: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cpath d='M15.4 15.4 21 21'/%3E%3Cpath d='M10.5 7.6v5.8M7.6 10.5h5.8'/%3E%3C/svg%3E");
}


/* ---- The shot, and its magnifier ----
   The <button> fills the frame's mat exactly, so its hit area is the picture.
   `display: block` and the zeroed padding/border matter: a button's UA styles
   would otherwise inset the image inside the mat by a couple of pixels and
   make the frame look mis-cut. */
/* Shrink-wraps the image rather than filling the frame: the mat around a
   capture is 8px of frame, not slack the button should be claiming. It
   matters beyond tidiness -- .shot__zoom is `inset: 0`, so a button bigger
   than its picture puts the hover scrim and the magnifier over empty mat. */
.shot {
  display: block;
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: none;
  cursor: zoom-in;
  overflow: hidden;
}

/* The scrim and the magnifier are one element: the span covers the image and
   carries both, so a single opacity transition fades them together and there
   is no frame where one has arrived and the other has not. */
.shot__zoom {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: inherit;
  background: rgba(21, 12, 34, 0.42);
  opacity: 0;
  transition: opacity 220ms cubic-bezier(0.2, 0.7, 0.3, 1);
  pointer-events: none;
}

/* The glass itself. A mask rather than a background image, so the icon takes
   its colour from the token and stays right in both themes. */
.shot__zoom::after {
  content: "";
  width: clamp(28px, 3.4vw, 44px);
  aspect-ratio: 1;
  background: var(--shell-100);
  -webkit-mask: var(--icon-zoom) center / contain no-repeat;
  mask: var(--icon-zoom) center / contain no-repeat;
  transform: scale(0.86);
  transition: transform 220ms cubic-bezier(0.2, 0.7, 0.3, 1);
  filter: drop-shadow(0 2px 10px rgba(21, 12, 34, 0.55));
}

.shot:hover .shot__zoom,
.shot:focus-visible .shot__zoom {
  opacity: 1;
}

.shot:hover .shot__zoom::after,
.shot:focus-visible .shot__zoom::after {
  transform: scale(1);
}

.shot:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}

/* Touch has no hover, and a magnifier that only ever appears on a pointer
   device is an affordance nobody on a phone is told about. There, it sits
   permanently in the corner instead of over the whole picture. */
@media (hover: none) {
  .shot__zoom {
    opacity: 1;
    inset: auto 8px 8px auto;
    width: 34px;
    height: 34px;
    border-radius: 999px;
    background: rgba(21, 12, 34, 0.62);
  }

  .shot__zoom::after {
    width: 18px;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shot__zoom,
  .shot__zoom::after {
    transition: none;
  }
}


/* ---- The modal ----
   Sized against the VIEWPORT, not the image: a 1920px capture on a 1440px
   screen has to come down, and one on a 4K screen must not be blown up past
   its pixels. `max-width`/`max-height` on the image do the first; the frame
   shrink-wraps whatever that leaves. */
.lightbox {
  width: min(96vw, 1800px);
  max-width: none;
  max-height: 94vh;
  margin: auto;
  padding: 0;
  border: 0;
  background: none;
  overflow: visible;
}

.lightbox::backdrop {
  background: rgba(10, 8, 18, 0.82);
  backdrop-filter: blur(6px);
}

/* The same mat as .case__shot, so the picture arrives in the frame it left.

   `min-width: 0` is load-bearing, not tidiness. The frame is a flex item, a
   flex item's `min-width` is `auto`, and on the narrow layout its content is
   an image drawn at 1920px -- so without this the frame grows to the picture
   and takes the dialog, the caption and the close button off the side of a
   phone with it. The dialog's own width is the size; the frame fits in it. */
.lightbox__frame {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  max-width: 100%;
  padding: 10px;
  border-radius: 20px;
  border: 1px solid var(--frame-line);
  background: linear-gradient(180deg, var(--frame-top), var(--frame-bottom));
  box-shadow: var(--frame-shadow);
}

/* The window. It shrink-wraps the fitted image, and once the reader zooms in
   it is what the picture moves behind -- so it clips, and it owns the touch
   gestures rather than letting the phone pan the page under the modal. */
.lightbox__viewport {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  min-width: 0;
  max-width: 100%;
  touch-action: none;
  cursor: default;
}

.lightbox__viewport.is-zoomed {
  cursor: grab;
}

.lightbox__viewport.is-zoomed:active {
  cursor: grabbing;
}

/* What actually moves. */
.lightbox__stage {
  position: relative;
  display: block;
  transform-origin: center center;
  will-change: transform;
}

.lightbox__image {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  /* 94vh of dialog, less the mat, the caption row and the gap. */
  max-height: calc(94vh - 6.5rem);
  margin: 0 auto;
  border-radius: 12px;
  background: var(--plum-880);
}

/* Tall pictures (js/lightbox.js, `data-lightbox-tall`): drawn at a readable
   width and scrolled, rather than squeezed to the screen's height. The
   viewport becomes the scroller and hands vertical drags back to the
   browser, since pinch-zoom is off in this mode. */
.lightbox--tall .lightbox__viewport {
  max-height: calc(94vh - 6.5rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  touch-action: pan-y;
}

/* Its own width, capped at 900px -- a 1472px design sheet comes down to a
   readable column, and a 375px phone screenshot stays at its real size
   rather than being blown up 2.4x into a blur. */
.lightbox--tall .lightbox__image {
  width: auto;
  max-width: min(100%, 900px);
  max-height: none;
}

.lightbox__caption {
  margin: 0;
  padding: 0 var(--space-2) var(--space-1);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  color: var(--card-text-muted);
  text-align: center;
}

/* Empty caption: the row would otherwise hold open a line of blank mat. */
.lightbox__caption:empty {
  display: none;
}

/* A picture that is fitted rather than full size gives the reader no sign
   that there is more to see in it. Only on touch, where the gesture is the
   one being advertised; a mouse gets the same zoom on ctrl+wheel and does not
   need telling. */
@media (hover: none) {
  .lightbox__caption::after {
    content: " Pinch or double-tap to zoom.";
    color: var(--card-text-accent);
  }
}

/* Sits on the frame's corner rather than inside the picture, so it never
   covers part of the capture the reader opened it to see. */
.lightbox__close {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--btn-border);
  border-radius: 999px;
  background: var(--btn-bg);
  color: var(--btn-text);
  font: 500 1.25rem/1 var(--font-body);
  cursor: pointer;
  box-shadow: var(--btn-shadow);
  transition: background-color 220ms ease, box-shadow 220ms ease;
}

.lightbox__close:hover {
  background: var(--btn-bg-hover);
  box-shadow: var(--btn-shadow-hover);
}

.lightbox__close:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}

/* The close button is positioned against this, and the dialog itself is a
   flex box only so the frame centres in it. */
.lightbox__inner {
  position: relative;
  display: flex;
  justify-content: center;
}

@media (max-width: 1023.98px) {
  .lightbox {
    width: 94vw;
  }

  .lightbox__close {
    top: -10px;
    right: -10px;
    width: 38px;
    height: 38px;
  }
}
