/* =============================================================================
   PHOTOLOG DIASHOW — the click-through carousel
   =============================================================================
   The second shape an image group can take: one photo at a time, prev/next
   buttons, and a caption that changes with the slide. Rendered by
   portfolio/_photolog_carousel_media.html, driven by photolog_carousel.js.

   Loaded by BOTH project shells (classic + journal) next to photolog_group.css,
   and for the same reason: Journal does not load project.css or Bootstrap, so
   anything a Diashow needs has to live in a file of its own rather than in
   either layout's stylesheet.

   THE ONE STRUCTURAL RULE: a slide is never `display: none`. All slides sit
   side by side in a flex track that is moved by `translateX`, and the viewport
   clips it. Two things depend on that and both break silently if someone
   "simplifies" it to toggling display:

     - `loading="lazy"` images inside a display:none subtree may never start
       loading, so the second slide would arrive blank on click.
     - lightbox.js collects `.gallery-image` once, in DOM order. Laid-out slides
       keep that list complete and correctly ordered, so opening any slide and
       walking the whole Diashow with the lightbox arrows just works.
   ========================================================================== */

.pf-carousel {
  --pf-carousel-btn: 2.75rem;      /* ≥44px hit target */
  /* ONE SHAPE FOR EVERY SLIDE — the same promise photolog_group.css makes for
     the Collage row ("justified by height"), and for the same reason: it is the
     only way to show a portrait and a landscape as equals.

     A frame that followed the current slide would reflow the page under the
     reader's cursor on every click. So the shape is fixed, and it comes from
     the group's WIDEST photo (models.py → frame_aspect_ratio), set inline. That
     is what keeps the frame the same size as an ordinary photo on the same
     page: at full column width the widest slide fills it exactly, instead of
     being shrunk to some invented rem height. Anything narrower is matted.

     ⚠️ There is deliberately NO max-height here. `aspect-ratio` plus a
     `max-height` does not letterbox — the browser shrinks the WIDTH to keep
     the ratio, and the Diashow came out narrower than the photographs around
     it (939px beside 1280px neighbours). The frame is bounded on the inline
     axis instead, by --pf-carousel-maxw: the largest slide's natural width,
     which is the very cap a lone photograph gets. */
  --pf-carousel-ar: 1.5;
  margin: 0;
  max-width: var(--pf-carousel-maxw, none);
  /* Centred like a lone photograph; the side-aligned layouts override below. */
  margin-inline: auto;
}

.pf-carousel__viewport {
  position: relative;
  overflow: hidden;
  aspect-ratio: var(--pf-carousel-ar);
  /* A fixed frame holding photographs of different shapes always has margins
     around some of them. Naming the frame as a surface is what turns those from
     a gap into a mount — and it is what the prev/next buttons stand on when the
     slide is narrower than the frame. Journal already does exactly this behind
     its figures (--jr-surface-dim); an alpha rather than a token so the same
     rule holds on all three layouts and on either theme. */
  background: rgba(0, 0, 0, 0.045);
}

.pf-carousel__track {
  display: flex;
  /* Load-bearing: without an explicit height the track is auto, the slides'
     `height: 100%` resolves against auto (i.e. to auto as well), and every
     photo falls back to its natural height and overflows the frame. */
  height: 100%;
  /* JS sets `transform: translateX(-N * 100%)`; nothing else moves the track. */
  transition: transform 0.35s ease;
  will-change: transform;
}

/* Slides stay exactly one viewport wide whatever their photo's shape — the
   translateX arithmetic in photolog_carousel.js counts on it. The photograph
   inside is what varies in width. */
.pf-carousel__slide {
  flex: 0 0 100%;
  max-width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pf-carousel .pf-carousel__img {
  display: block;
  width: 100%;
  height: 100%;
  /* `contain` is the whole point: the frame is fixed, so the photo fits itself
     into it. Nothing is ever cropped — a Diashow shows whole photographs. */
  object-fit: contain;
  /* styles.css sets a global `img { padding: 0 1.5pt }` on classic sites, and
     the classic presets cap images at their natural width; both fight a slide
     that must fill its frame exactly. */
  padding: 0;
  margin: 0;
  max-width: none;
  /* lightbox.js binds to .gallery-image — a slide opens like any other photo. */
  cursor: zoom-in;
}

/* --- controls -------------------------------------------------------------
   Overlaid on the photo rather than placed beside it: the Diashow sits in three
   different layouts (simple, essay-01, journal) whose column widths have
   nothing in common, and only the photo's own box is the same in all three.
   ------------------------------------------------------------------------- */
.pf-carousel__btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: var(--pf-carousel-btn);
  height: var(--pf-carousel-btn);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.2s ease, background-color 0.2s ease;
}

.pf-carousel__btn:hover,
.pf-carousel__btn:focus-visible {
  opacity: 1;
  background: rgba(0, 0, 0, 0.7);
}

.pf-carousel__btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.pf-carousel__btn--prev { left: 0.75rem; }
.pf-carousel__btn--next { right: 0.75rem; }

@media (prefers-reduced-motion: reduce) {
  .pf-carousel__track {
    transition: none;
  }
}

/* Phones: the buttons keep their full size — this is where they are actually
   thumbed, and the house rule grows touch targets rather than shrinking them.
   The frame is what gives way instead, pulling them closer to its edges so they
   cover less of a photograph that now has far less width to spare. */
@media (max-width: 30rem) {
  .pf-carousel__btn--prev { left: 0.375rem; }
  .pf-carousel__btn--next { right: 0.375rem; }
}


/* --- captions --------------------------------------------------------------
   Rendered by the LAYOUT, in the layout's own caption slot (see the note in
   _photolog_carousel_media.html); this file only governs which one shows.
   `display: contents` lets each per-slide wrapper vanish as a box so its
   title/caption/dateline stay direct children of the layout's caption
   element and inherit its flex or block layout untouched. */
.pf-carousel__cap { display: contents; }
.pf-carousel__caps { display: contents; }

/* Beats the `display: contents` above — [attr] outranks a bare class. */
.pf-carousel__cap[hidden],
[data-pf-carousel-captions][hidden] { display: none; }

/* Right-aligned figures put the photograph's right edge on the column edge;
   the Diashow frame follows, mirroring the essay's own image rule. */
.pf-essay__figure--right .pf-carousel {
  margin-left: auto;
  margin-right: 0;
}

/* --- a Diashow as a CELL of a Collage --- */
/* The flex row owns the width here (the cell carries flex-grow and --ar from
   the frame's aspect ratio), so the standalone frame's natural-width cap has
   to come off — otherwise a Diashow of small photographs would sit matted in
   a cell the row already sized for it.

   Captions need no rule: a nested Diashow is not a top-level block, so no
   layout emits a data-pf-carousel-captions container for it, and
   photolog_carousel.js finds none. Inside a Collage the Collage speaks. */
.pf-group-item--carousel { position: relative; }
.pf-group-item--carousel .pf-carousel {
  max-width: none;
  width: 100%;
  height: 100%;
  margin: 0;
  /* A control sized for a column-wide frame dominates a cell a third as wide. */
  --pf-carousel-btn: 2rem;
}
/* The frame is a cell now, not a column: it has no mat around the photograph,
   so the viewport's surface tint would only show as a seam. */
.pf-group-item--carousel .pf-carousel__viewport {
  height: 100%;
  background: none;
}
/* `contain`, like everywhere else in the Diashow — "nothing is ever cropped"
   is the component's founding rule (see the base __img comment), and the
   Collage row around this cell is built on the same promise. This rule was
   `cover` until 2026-08-29, and the collision was invisible until a mixed
   Diashow met a real portrait: the frame takes the WIDEST member's shape
   (models.py → frame_aspect_ratio, a rationale written for matting), and
   cover in that frame trimmed the head off every portrait slide (M3 finding).

   What replaces the crop is the __fill twin below, not a mat — the seam
   objection above still stands. */
.pf-group-item--carousel .pf-carousel__slide {
  position: relative;
  overflow: hidden;      /* clips the fill's scale-up */
}
.pf-group-item--carousel .pf-carousel__img {
  object-fit: contain;
  position: relative;    /* paints above the absolutely-positioned fill */
}
/* The stage: the slide's own image, blurred and dimmed, covering the cell
   edge-to-edge. Portrait-on-a-stage — the idiom of every player and story
   format, so it reads as deliberate where a background tint reads as a gap.
   Exactly fitting slides hide it completely. Scaled up so the blur's soft
   edge bleeds past the clip instead of showing as a halo. */
.pf-carousel__fill {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: blur(18px) saturate(0.85) brightness(0.82);
  transform: scale(1.12);
  pointer-events: none;  /* clicks belong to the slide (lightbox) */
}
/* ...and pulled in tight to the cell's edges. */
.pf-group-item--carousel .pf-carousel__btn--prev { left: 0.4rem; }
.pf-group-item--carousel .pf-carousel__btn--next { right: 0.4rem; }

/* A standalone Diashow is a column-wide frame and can carry its controls
   openly. In a cell they are two discs sitting on a photograph a third that
   size — so here they wait for the pointer, and the stack mark above is what
   holds the meaning at rest. Same split as the download button: the SIGN
   stays, the ACTIONS arrive on approach.

   Guarded by (hover: hover): on a touch screen nothing ever "approaches", and
   hiding the only way to reach slide 2 behind a hover state would strand it. */
@media (hover: hover) {
  .pf-group-item--carousel .pf-carousel__btn {
    opacity: 0;
    transition: opacity 0.2s ease, background-color 0.2s ease;
  }
  .pf-group-item--carousel:hover .pf-carousel__btn,
  .pf-group-item--carousel:focus-within .pf-carousel__btn {
    opacity: 0.85;
  }
  .pf-group-item--carousel .pf-carousel__btn:hover,
  .pf-group-item--carousel .pf-carousel__btn:focus-visible {
    opacity: 1;
  }
}
@media (hover: hover) and (prefers-reduced-motion: reduce) {
  .pf-group-item--carousel .pf-carousel__btn { transition: none; }
}

/* --- the stack mark ---------------------------------------------------------
   What tells a visitor that this cell in the row is a set rather than a single
   photograph. Emitted only for a nested Diashow (see the template).

   Unlike the download button — an ACTION, so it waits for hover — this is a
   SIGN, and a sign that only appears once you already found the thing is not a
   sign. So it sits at rest, quiet: the same dark disc the other overlays use,
   at a lower alpha, coming to full strength with the rest of the cell's chrome
   on hover.

   Top LEFT: photolog_download.css owns top right (0.6rem), and the two would
   sit on top of each other on a project with downloads on. */
.pf-carousel__stack {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  padding: 0.25rem 0.45rem;
  border-radius: 999px;
  /* Quietness is carried by the disc's own alpha, NOT by `opacity` on top of
     it: the two compound, and 0.82 x 0.6 lands the composite near #7f7f7f,
     where white 11px text is ~3.9:1 — under the 4.5:1 floor. At 0.64 alone
     the worst case (a white sky behind it) composites to ~#676767 and white
     reads 5.5:1. Anything darker in the photograph only helps. */
  background: rgba(17, 17, 17, 0.64);
  color: #fff;
  font-size: 0.7rem;
  line-height: 1;
  /* The count is the point; keep it from shifting as slides change width. */
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  transition: background-color 0.2s ease;
}
.pf-group-item--carousel:hover .pf-carousel__stack,
.pf-group-item--carousel:focus-within .pf-carousel__stack {
  background: rgba(0, 0, 0, 0.82);
}
.pf-carousel__stack svg { display: block; }
.pf-carousel__stack-n { font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .pf-carousel__stack { transition: none; }
}
