/* --------------------------------------------------------------------------
   The Gallery page

   Attached to the two gal-* bands rather than a page slug, so the bands work
   wherever they are placed. Nothing here is measured against a deck canvas —
   this page is not a slide conversion — and no colour below is a new hex: ink,
   ground and rule all come from site.css.
   -------------------------------------------------------------------------- */

.gal-band {
  background-image: var(--section-bg, none);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  padding-block: clamp(2rem, 5vw, 4.5rem);
}

.gal-band__title {
  margin: 0 0 clamp(1.25rem, 3vw, 2.25rem);
  font-size: clamp(1.4rem, 2.6vw, 2.2rem);
  line-height: 1.2;
  font-weight: 700;
  color: var(--c-ink);
}

/* Set as the page's own title (the h1), the heading takes .display-title's
   face, step and leading from site.css, so "Gallery" reads as every other
   page title does. A band heading set at h2 keeps the smaller style above. */
h1.gal-band__title {
  font-family: var(--font-display);
  font-size: var(--step-display);
  line-height: 1.05;
}

.gal-grid {
  list-style: none;
  margin: 0;
  padding: 0;
}

.gal-tile {
  margin: 0;
}

.gal-tile__link {
  position: relative;
  display: block;
  cursor: pointer;
  background: var(--c-rule, #d9d2ca);
}

/* --------------------------------------------------------------------------
   The video band — an even row of stills
   -------------------------------------------------------------------------- */

/* The rule closing this band, as the design has it. On .page rather than on
   the band so it stops at the container edges instead of running the full
   bleed, and a border rather than an <hr> because it is a boundary between two
   bands and not a thing in the document. It follows the band, so a page with
   no photographs below still ends on it — which is the same line the design
   draws under the stills either way. */
.gal-videos .page {
  padding-bottom: clamp(2rem, 5vw, 4.5rem);
  border-bottom: 1px solid #000;
}

/* 415 x 415, as asked. The track is minmax(min(100%, 415px), 415px), which is
   exactly 415 wherever there is room for it and the full column width where
   there is not — so the figure is honoured on every screen it fits on and a
   phone gets one tile edge to edge instead of 415px of horizontal scroll.
   auto-fill wraps the row itself, so the number across is a consequence of the
   container rather than a count that has to be maintained per breakpoint. */
/* 415 is a ceiling, not a demand — which is the whole fix.
   `repeat(auto-fill, 415px)` broke the row: three tiles plus two gaps is
   1301px against a 1300px container, one pixel too many, so the third wrapped
   and left most of a row empty — which is what the screenshot showed.
   minmax(0, 415px) asks for 415 and accepts less, so the three sit across at
   414.67 and nothing wraps for the sake of a pixel.

   Three explicit columns rather than auto-fill, because the count is a design
   decision here and not a consequence of arithmetic: a fourth video belongs on
   a second row at the same size, which auto-fill would instead have squeezed
   into one row of four. */
.gal-videos .gal-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 415px));
  justify-content: start;
  gap: clamp(0.75rem, 2vw, 1.75rem);
}

/* Square, to match. The stills are covered into it, so a run of differently
   shaped thumbnails still makes an even grid; the whole picture is never lost,
   because the dialog shows the video itself. */
.gal-videos .gal-tile__link {
  aspect-ratio: 1;
  overflow: hidden;
}

.gal-videos .gal-tile__link img,
.gal-videos .gal-tile__link picture {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* No play mark on the stills, on request: the whole picture is the control.
   Nothing marks it as one, so the pointer cursor and the focus ring on
   .gal-tile__link above are the only affordances left and are kept for that
   reason. */

@media (max-width: 900px) {
  .gal-videos .gal-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  .gal-videos .gal-grid { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------------
   The photograph band — masonry, every picture at its own height
   -------------------------------------------------------------------------- */

/* Columns rather than grid, deliberately. The design packs portraits and
   landscapes together with no common height, and a grid would either crop them
   to a shared ratio or leave ragged gaps. CSS columns flow them at their own
   proportions, which is the whole look — and unlike a scripted masonry it
   needs no JavaScript and no measuring pass after the images load.
   The trade is reading order: columns fill top-to-bottom, then across. */
.gal-images .gal-grid {
  column-count: 4;
  column-gap: clamp(0.5rem, 1.5vw, 1.25rem);
}

.gal-images .gal-grid__cell {
  /* Without this a tile can be split across two columns mid-picture. */
  break-inside: avoid;
  page-break-inside: avoid;
  margin-bottom: clamp(0.5rem, 1.5vw, 1.25rem);
}

.gal-images .gal-tile__link img,
.gal-images .gal-tile__link picture {
  width: 100%;
  height: auto;
  display: block;
}

@media (max-width: 1100px) { .gal-images .gal-grid { column-count: 3; } }
@media (max-width: 760px)  { .gal-images .gal-grid { column-count: 2; } }
@media (max-width: 480px)  { .gal-images .gal-grid { column-count: 1; } }

/* No space above this band: it runs straight on from the rule closing the one
   before it, which is where the separation already comes from. */
.gal-images { padding-top: 0; }

/* The display face across this band — 'Cinzel Decorative', Georgia, serif —
   taken from the token rather than restated, so it follows if site.css ever
   changes the stack. Cinzel Decorative ships at 700 only, which is the weight
   the caption already asks for, so nothing here is synthesised. */
.gal-images {
  font-family: var(--font-display);
}

/* --------------------------------------------------------------------------
   The caption, on hover
   Off the tile and onto the picture: the caption is now an overlay that
   appears when the picture is pointed at, rather than a line of type beneath
   it. .gal-tile is the positioning context — the caption is a sibling of the
   link in the markup, so it is placed against the figure rather than the <a>.
   -------------------------------------------------------------------------- */

.gal-images .gal-tile {
  position: relative;
}

/* The darkening, drawn on the link so it sits over the picture and under the
   words. Painted at zero alpha rather than toggled from none, so the two
   states are one property and the transition has something to move between. */
.gal-images .gal-tile__link::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(20, 17, 15, 0);
  transition: background-color 0.25s ease;
}

.gal-images .gal-tile:hover .gal-tile__link::after,
.gal-images .gal-tile:focus-within .gal-tile__link::after {
  background: rgba(20, 17, 15, 0.62);
}

.gal-images .gal-tile__caption {
  position: absolute;
  inset: 0;
  z-index: 1;
  margin: 0;
  display: grid;
  place-items: center;
  padding: 1rem;
  text-align: center;
  font-size: clamp(0.85rem, 1.2vw, 1.05rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #fff;
  opacity: 0;
  transition: opacity 0.25s ease;
  /* The caption covers the whole tile, so it would otherwise swallow the click
     that opens the lightbox. */
  pointer-events: none;
}

.gal-images .gal-tile:hover .gal-tile__caption,
.gal-images .gal-tile:focus-within .gal-tile__caption {
  opacity: 1;
}

/* focus-within is what makes this reachable from the keyboard: tabbing to the
   tile's link shows the same overlay a pointer does. */

@media (prefers-reduced-motion: reduce) {
  .gal-images .gal-tile__link::after,
  .gal-images .gal-tile__caption { transition: none; }
}

/* Hover is not a thing a touch screen has. Rather than leave the caption
   unreachable there, it is simply shown — the picture is still tappable
   because the overlay does not take pointer events. */
@media (hover: none) {
  .gal-images .gal-tile__link::after { background: rgba(20, 17, 15, 0.45); }
  .gal-images .gal-tile__caption { opacity: 1; }
}

.gal-tile__link:focus-visible {
  outline: 2px solid var(--c-ink);
  outline-offset: 3px;
}

.gal-tile__caption {
  margin: 0.5rem 0 0;
  font-size: clamp(0.8rem, 1.1vw, 0.95rem);
  line-height: 1.35;
  color: var(--c-ink);
}

/* --------------------------------------------------------------------------
   The lightbox
   Same visual language as .video-modal in site.css, deliberately: they are two
   dialogs on one site and should not look like two different ideas.
   -------------------------------------------------------------------------- */

.gal-lightbox[hidden] { display: none; }

.gal-lightbox {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
}

.gal-lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20, 17, 15, 0.88);
}

.gal-lightbox__box {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.5rem 0.75rem;
  width: min(100%, 1200px);
  max-height: 100%;
}

.gal-lightbox__figure {
  grid-column: 2;
  grid-row: 1;
  margin: 0;
  min-height: 0;
  display: grid;
  place-items: center;
}

/* The picture is shown whole. Capping the height against the viewport rather
   than the box is what stops a portrait file pushing the caption off screen. */
.gal-lightbox__figure img {
  max-width: 100%;
  max-height: min(78vh, 100%);
  width: auto;
  height: auto;
  display: block;
  object-fit: contain;
}

.gal-lightbox__caption,
.gal-lightbox__count {
  grid-column: 2;
  margin: 0;
  text-align: center;
  color: #fff;
}

.gal-lightbox__caption { grid-row: 2; font-size: 0.95rem; line-height: 1.4; }
.gal-lightbox__count   { grid-row: 3; font-size: 0.8rem; opacity: 0.7; }

.gal-lightbox__caption[hidden],
.gal-lightbox__count[hidden],
.gal-lightbox__nav[hidden] { display: none; }

.gal-lightbox__nav {
  grid-row: 1;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  font-size: 2rem;
  line-height: 1;
  color: #fff;
  background: rgba(0, 0, 0, 0.4);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.gal-lightbox__nav--prev { grid-column: 1; }
.gal-lightbox__nav--next { grid-column: 3; }

.gal-lightbox__nav:hover { background: rgba(0, 0, 0, 0.7); }

.gal-lightbox__close {
  position: absolute;
  top: -2.6rem;
  right: 0;
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  font-size: 1.7rem;
  line-height: 1;
  color: #fff;
  background: none;
  border: 0;
  cursor: pointer;
  z-index: 1;
}

.gal-lightbox__nav:focus-visible,
.gal-lightbox__close:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* No room above the box on a short screen, and the arrows have to come inside
   the picture once the viewport is this narrow. */
@media (max-height: 560px), (max-width: 700px) {
  .gal-lightbox__close {
    top: 0.25rem;
    right: 0.25rem;
    background: rgba(0, 0, 0, 0.55);
    border-radius: 50%;
  }

  .gal-lightbox__box {
    grid-template-columns: minmax(0, 1fr);
  }

  .gal-lightbox__figure,
  .gal-lightbox__caption,
  .gal-lightbox__count { grid-column: 1; }

  .gal-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    grid-row: auto;
  }

  .gal-lightbox__nav--prev { left: 0.25rem; }
  .gal-lightbox__nav--next { right: 0.25rem; }
}

.has-gal-lightbox,
.has-gal-lightbox body { overflow: hidden; }
