/* ══════════════════════════════════════════════════════════════════
   photo-slide.css — one photograph at a time.

   The frame is 3:2 in every frame Sandy drew, and the photograph
   inside it is HEIGHT-locked. That is the whole rule, and it is what
   keeps the page from jumping: page to a portrait and it comes out
   narrower, not taller, so nothing below it moves.

   The 3:2 is also why the pane numbers look like a coincidence and
   aren't — at 1280 the frame is 573x382, and a 3:2 landscape at 382
   tall is 573 wide, exactly the pane. Same at 1728 (1021x681). So
   locking the height gives "landscape fills the pane" for free
   instead of it being a second rule.

   Two modes, and they differ in which axis is the constraint:

     pane      the frame takes the pane's WIDTH and 3:2 sets its
               height (52:1715 573x382, 52:2107 1021x681)
     expanded  the frame takes whatever HEIGHT is left and the
               photograph is centred in it (52:1909: 799.5x533 inside
               a 1224-wide row, so 212.25 of background either side)

   Expanded is sized by flexbox rather than by subtracting a stack of
   constants from 100dvh. The constants would have to include the
   description's height, and that is 1 line on one collection and 3 on
   another — the number would be right for whichever page it was
   measured on and wrong for the next. Filling the leftover height
   instead is exact on both, and stays exact when the text changes.
══════════════════════════════════════════════════════════════════ */

/* 22 between the photograph and its controls, in every frame — the
   pane's own 44 rhythm is the gap BETWEEN blocks; this is inside one. */
.photo-slide{
  display:flex; flex-direction:column;
  gap:22px; width:100%;
}

.slide-frame{
  position:relative;
  width:100%;
  aspect-ratio:3 / 2;
}

/* Only the current slide is in flow. Every other one stays in the DOM
   (js/photo-layouts.js explains why it is not one <img> with a
   swapped src) but has no box at all — which is what
   js/photo-progressive.js's `unprintable` looks for, so the pane's
   print chain steps past them instead of waiting on something it can
   never fill. */
.slide-img{ display:none; }

/* The current slide fills the frame's BOX and object-fit does the
   rest. That one rule is the whole height-lock: contained inside a
   3:2 box, a 3:2 photograph fills it exactly, a portrait comes out
   the frame's full height and narrower (centred, background either
   side), and a panorama wider than 3:2 is the single case that gives
   up height instead of overflowing — the clamp, for free.

   The obvious spelling of the lock — height:100%, width:auto,
   max-width:100% — does NOT work, and fails in a way that hides
   itself: a percentage height has nothing definite to resolve
   against when the parent's own height came from aspect-ratio, so
   the image falls back to its natural size and stretches the frame
   to fit. Every photograph in the collection this was first built
   against is 3:2, and max-width:100% clamped those to the pane width
   with the right height following from their own ratio — so it
   looked exactly like the design until a portrait was put through
   it and the frame grew to 573x600.

   Absolutely positioned rather than a flex child because inset:0 is
   edge-anchored: it needs no percentage resolution at all, so it is
   immune to the same trap in expanded view, where the frame's height
   comes from the flex chain rather than from a ratio. */
.slide-img.is-current{
  display:block;
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:contain;
  cursor:pointer;                 /* click opens the loupe */
}

.slide-img:focus-visible{
  outline:1px solid var(--fg);
  outline-offset:2px;
}

/* [prev] ... dots ... [next]. space-between would centre the dots on
   its own while both buttons are 6 characters of a monospace face,
   but the dots carry flex:1 and centre within their own share
   instead — that stays true if either label ever changes length. */
.slide-control{
  display:flex; align-items:center; justify-content:space-between;
  gap:22px; width:100%;
}

.slide-btn{ flex:none; }

/* 8.096px on a 12.096px pitch = a 4px gap (52:1883). Wraps, because
   Sandy's mobile frame (52:1776) shows two rows of dots — which is
   also why the control row has no fixed height. */
.slide-dots{
  flex:1;
  display:flex; flex-wrap:wrap;
  gap:4px;
  align-items:center; justify-content:center;
}

/* The glyphs are Doto's bullet, flattened, with the four centre dots
   deleted for the hollow one — exported from Figma as
   img/dot-empty.svg and img/dot-filled.svg.

   Drawn as a MASK rather than an <img>: both files carry a hardcoded
   fill of #070909 from the export, which is invisible against the
   terminal theme's own background. Masking keeps the exported vector
   exactly as it is and takes the colour from --fg, so the dots
   follow the theme the way every other mark on the page does. */
.slide-dot{
  width:8.096px; height:8.096px;
  flex:none;
  background:var(--fg);
  -webkit-mask:url('/img/dot-empty.svg') no-repeat center / contain;
          mask:url('/img/dot-empty.svg') no-repeat center / contain;
}

.slide-dot.is-current{
  -webkit-mask-image:url('/img/dot-filled.svg');
          mask-image:url('/img/dot-filled.svg');
}

/* ── expanded ─────────────────────────────────────────────────────
   js/photo-expand.js puts .is-expanded on .stage. The chain below
   hands the leftover pane height down to the frame: .pane-inner is
   already a flex column (css/base.css), so each link only has to
   grow and be allowed to shrink past its content (min-height:0,
   without which a flex item floors at its content size and the
   photograph would push the controls off-screen).

   .is-slide is set by js/pages.js only when this layout is the one
   in use — the grid and linear pages must keep their natural height
   and their scroll. */
.stage.is-expanded .pane-blocks.is-slide{ flex:1 1 auto; min-height:0; }

.stage.is-expanded .pane-blocks.is-slide .photo-list-mount{
  flex:1 1 auto; min-height:0;
  display:flex; flex-direction:column;
}

.stage.is-expanded .photo-slide{ flex:1 1 auto; min-height:0; }

/* aspect-ratio is dropped here, not overridden with another ratio:
   the height now comes from the flex chain above and the width is
   the full row, with the photograph centring inside it. */
.stage.is-expanded .slide-frame{
  flex:1 1 auto; min-height:0;
  aspect-ratio:auto;
}
