/* gallery-wall — one image wall in four variants: grid (uniform cells),
 * masonry and scatter (column fill), and justified (rows at a target height).
 * All four ride the shared .sk-cell-grid container. */

/* A cell that IS an anchor: block posture, no underline, ambient ink. The focus ring is the
 * kit's floor, which every focusable thing gets. */
.sk-gallery-wall-cell {
  display: block;
  position: relative;
  text-decoration: none;
  color: inherit;
}

/* Gap gutter: on the fixed --kit-space-* scale, not Density (the kit scales
 * vertical rhythm with Density but keeps gutters fixed). `none` = flush mosaic. */
.sk-gallery-wall-cells {
  --sk-wall-gap: var(--kit-space-4);
  column-gap: var(--sk-wall-gap);
}
.sk-gallery-wall[data-gap="none"] .sk-gallery-wall-cells {
  --sk-wall-gap: 0px;
}
.sk-gallery-wall[data-gap="tight"] .sk-gallery-wall-cells {
  --sk-wall-gap: var(--kit-space-2);
}
.sk-gallery-wall[data-gap="airy"] .sk-gallery-wall-cells {
  --sk-wall-gap: var(--kit-space-5);
}

/* The variants are NAMED so one added later takes the floor only when meant to. */
.sk-gallery-wall:is([data-variant="grid"], [data-variant="masonry"]):not(
    [data-caption-placement="inside"]:not([data-captions=""])
  )
  .sk-gallery-wall-cells {
  --sk-columns-min: 2;
}

/* FIXED, never palette-derived: it holds the overlay inks up on any photo. */
.sk-gallery-wall {
  --sk-wall-scrim: rgb(14, 12, 10);
}

/* The cell's link posture is its own, above; its focus ring is the kit's
 * floor, which every focusable thing gets. */
.sk-gallery-wall-media {
  position: relative;
  width: 100%;
  overflow: hidden;
  background: var(--sk-surface);
  /* Boundary rule: gapped cells are bounded tiles, so they round; gap=none is
   * one filling field, kept square (rounding would slit every seam). */
  border-radius: var(--sk-media-radius);
}
/* On the wall, not the media, and not scatter, whose own gutter keeps its
 * tiles apart at gap:none. */
.sk-gallery-wall[data-gap="none"]:not([data-variant="scatter"]) {
  --sk-media-radius: 0;
}
.sk-gallery-wall-media .sk-art {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* GRID: uniform grid tracks off the dial, cropped to the section aspect (or
 * `none` = natural proportions). The :not([data-aspect="none"]) guard is what
 * lets `none` fall through to natural cell heights. */
.sk-gallery-wall[data-variant="grid"] .sk-gallery-wall-cells {
  /* display:grid + the repeat(--sk-active-columns) tracks come from .sk-cell-grid;
   * we only restate the gap on the kit's --sk-wall-gap scale. */
  gap: var(--sk-wall-gap);
}
.sk-gallery-wall[data-variant="grid"]:not([data-aspect="none"]) .sk-gallery-wall-media {
  aspect-ratio: var(--sk-wall-aspect, 1 / 1);
}
/* Cover-fill the crop: beat the auto-aspect height:auto image rule (0,3,0). */
.sk-gallery-wall[data-variant="grid"]:not([data-aspect="none"]) .sk-gallery-wall-media .sk-art {
  height: 100%;
}

/* Copy below stretches the cell, so a caption that wraps further would move its
 * own picture. */
.sk-gallery-wall[data-variant="grid"] .sk-gallery-wall-cells {
  align-items: var(--sk-valign, start);
}
.sk-gallery-wall[data-valign="top"] {
  --sk-valign: start;
}
.sk-gallery-wall[data-valign="center"] {
  --sk-valign: center;
}
.sk-gallery-wall[data-valign="bottom"] {
  --sk-valign: end;
}
/* The wall's ratio comes from the shared ASPECT TOKEN TABLE (--sk-aspect,
 * resolved on this root, where the section's data-aspect rides). */
.sk-gallery-wall[data-aspect] {
  --sk-wall-aspect: var(--sk-aspect);
}

.sk-gallery-wall[data-variant="grid"]:not(
    [data-caption-placement="inside"],
    [data-captions=""]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-cells {
  align-items: stretch;
}
.sk-gallery-wall[data-variant="grid"]:not(
    [data-caption-placement="inside"],
    [data-captions=""]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-cell {
  /* One band only where the media share a baseline, which is bottom; elsewhere
   * the cell stacks so the copy follows its own image. Never layered. */
  row-gap: var(--kit-space-2);
}
.sk-gallery-wall[data-variant="grid"]:not(
    [data-caption-placement="inside"],
    [data-captions=""],
    [data-valign="bottom"]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-cell {
  display: flex;
  flex-direction: column;
  justify-content: var(--sk-valign, start);
}
.sk-gallery-wall[data-variant="grid"][data-valign="bottom"]:not(
    [data-caption-placement="inside"],
    [data-captions=""]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-cell {
  grid-row: span 2;
  display: grid;
  grid-template-rows: subgrid;
}
.sk-gallery-wall[data-variant="grid"]:not(
    [data-caption-placement="inside"],
    [data-captions=""]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-frame {
  /* Never for an inside caption, which needs the frame as a containing block. */
  display: contents;
}
.sk-gallery-wall[data-variant="grid"]:not(
    [data-caption-placement="inside"],
    [data-captions=""],
    [data-valign="bottom"]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-media {
  flex: 0 0 auto;
}
.sk-gallery-wall[data-variant="grid"][data-valign="bottom"]:not(
    [data-caption-placement="inside"],
    [data-captions=""]
  ):has(.sk-gallery-wall-copy)
  .sk-gallery-wall-media {
  align-self: var(--sk-valign, end);
}

/* Real layout fills every column in reading order: native CSS masonry where the
 * browser has it, else the script's 1px-track span pass. */

/* Turn the generic masonry utility ON for these two variants (gallery-wall.js reads the
 * flag; grid/justified leave it unset and keep their own layout). */
.sk-gallery-wall[data-variant="masonry"] .sk-gallery-wall-cells,
.sk-gallery-wall[data-variant="scatter"] .sk-gallery-wall-cells {
  --sk-masonry: 1;
}

/* JS-on grid-fill geometry. Only masonry + scatter get the marker; each feeds
 * its own gutter via --sk-flow-gap. */
.sk-gallery-wall-cells[data-sk-masonry-js] {
  display: grid;
  /* dense back-fills the gap a wider (data-grid span) cell leaves when it can't
   * fit the columns free at that spot — trades strict reading order for a tight
   * mosaic pack. Harmless without a span (uniform cells never gap). */
  grid-auto-flow: dense;
  grid-template-columns: repeat(var(--sk-active-columns), minmax(0, 1fr));
  grid-auto-rows: 1px;
  column-gap: var(--sk-flow-gap, var(--sk-wall-gap));
  row-gap: 0;
}
/* align-self:start lets the script measure the real image height before it spans;
 * the leftover span rows are the vertical gutter. */
.sk-gallery-wall-cells[data-sk-masonry-js] .sk-gallery-wall-cell {
  display: block;
  width: auto;
  margin: 0;
  align-self: start;
  break-inside: auto;
}

/* Native CSS masonry: correct reading order, no JS. Keyed on the masonry
 * VARIANT, not the script's marker, so it applies with scripts off. */
@supports (grid-template-rows: masonry) {
  .sk-gallery-wall[data-variant="masonry"] .sk-gallery-wall-cells,
  .sk-gallery-wall-cells[data-sk-masonry-js] {
    grid-template-rows: masonry;
    grid-auto-rows: auto;
    row-gap: var(--sk-flow-gap, var(--sk-wall-gap));
  }
}
@supports (display: grid-lanes) {
  .sk-gallery-wall[data-variant="masonry"] .sk-gallery-wall-cells,
  .sk-gallery-wall-cells[data-sk-masonry-js] {
    display: grid-lanes;
    grid-auto-rows: auto;
    row-gap: var(--sk-flow-gap, var(--sk-wall-gap));
  }
}

/* SCATTER — masonry's fill engine, plus an inset and a stagger. Its Spacing
 * dial scales gutter, rhythm and inset together. */
.sk-gallery-wall[data-variant="scatter"] .sk-gallery-wall-cells {
  --sk-scatter-gutter: var(--kit-space-4);
  --sk-scatter-drop: var(--kit-space-5);
  --sk-scatter-inset: 76%;
  column-gap: var(--sk-scatter-gutter);
}
.sk-gallery-wall[data-variant="scatter"][data-spacing="tight"] .sk-gallery-wall-cells {
  --sk-scatter-gutter: var(--kit-space-2);
  --sk-scatter-drop: var(--kit-space-4);
  --sk-scatter-inset: 90%;
}
.sk-gallery-wall[data-variant="scatter"][data-spacing="airy"] .sk-gallery-wall-cells {
  --sk-scatter-gutter: var(--kit-space-6);
  --sk-scatter-drop: var(--kit-space-6);
  --sk-scatter-inset: 62%;
}

/* Scatter's inset + stagger is a JS-on effect (below); with scripts off it
 * degrades to the plain .sk-cell-grid uniform grid, same as the others. */

/* Scatter on the grid engine. X (gutter) and Y are decoupled: Y rides
 * `row-gap` as a channel the script reads, zeroes, and bakes into the 1px-track
 * spans (native masonry uses it directly). The generous Y gives the visual-only
 * transform offset room to be pronounced without overlapping the item below. */
.sk-gallery-wall[data-variant="scatter"] .sk-gallery-wall-cells[data-sk-masonry-js] {
  --sk-flow-gap: var(--sk-scatter-gutter);
  row-gap: var(--sk-scatter-drop);
  /* A TRANSFORM moves paint without growing the grid, so a dropped cell in the
   * LAST row would paint past the section box. */
  padding-bottom: calc(var(--sk-scatter-drop) * 0.8);
}
.sk-gallery-wall[data-variant="scatter"]
  .sk-gallery-wall-cells[data-sk-masonry-js]
  .sk-gallery-wall-cell {
  width: var(--sk-scatter-inset);
  margin: 0 auto;
}
/* The stagger rides the FRAME: the reveal framework owns the cell's own
 * transform, and two on one element overwrite rather than compose. */
.sk-gallery-wall[data-variant="scatter"]
  .sk-gallery-wall-cells[data-sk-masonry-js]
  .sk-gallery-wall-cell:nth-child(3n)
  .sk-gallery-wall-frame {
  transform: translateY(calc(var(--sk-scatter-drop) * 0.55));
}
.sk-gallery-wall[data-variant="scatter"]
  .sk-gallery-wall-cells[data-sk-masonry-js]
  .sk-gallery-wall-cell:nth-child(4n)
  .sk-gallery-wall-frame {
  transform: translateY(calc(var(--sk-scatter-drop) * 0.8));
}
.sk-gallery-wall[data-variant="scatter"]
  .sk-gallery-wall-cells[data-sk-masonry-js]
  .sk-gallery-wall-cell:nth-child(5n)
  .sk-gallery-wall-frame {
  transform: translateY(calc(var(--sk-scatter-drop) * 0.3));
}

/* Masonry + scatter render every image at its natural ratio: the script's
 * masonry pass and native CSS masonry both flow items by their real
 * measured height, so there's no per-item crop to declare. */
.sk-gallery-wall[data-variant="masonry"] .sk-gallery-wall-media .sk-art,
.sk-gallery-wall[data-variant="scatter"] .sk-gallery-wall-media .sk-art {
  height: auto;
  display: block;
}

/* JUSTIFIED — full-width rows of natural-aspect images at a target height,
 * reading order preserved. Flexbox cannot pack one: no grow under-fills, grow
 * balloons a lone item, so the script sizes each row. */
.sk-gallery-wall[data-variant="justified"] .sk-gallery-wall-cells {
  --sk-justified: 1;
  --sk-row-height: 260px;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sk-wall-gap);
}
.sk-gallery-wall[data-variant="justified"][data-row-height="short"] .sk-gallery-wall-cells {
  --sk-row-height: 180px;
}
.sk-gallery-wall[data-variant="justified"][data-row-height="tall"] .sk-gallery-wall-cells {
  --sk-row-height: 360px;
}
.sk-gallery-wall[data-variant="justified"] .sk-gallery-wall-cell {
  /* Fallback (no JS): a uniform 3:2 target-height width, no grow so a lone
   * image never balloons, shrink lets an over-wide row fit. */
  flex: 0 1 calc(1.5 * var(--sk-row-height));
  width: auto;
  /* min-width:auto is min-content: a long word can beat the basis the script set
   * and wrap the row. */
  min-width: 0;
  margin: 0;
}
.sk-gallery-wall[data-variant="justified"] .sk-gallery-wall-media {
  aspect-ratio: 3 / 2;
  height: auto;
}
.sk-gallery-wall[data-variant="justified"] .sk-gallery-wall-media .sk-art {
  height: 100%;
}
/* JS-on: the script sets the row's shared height on the cell and this hands it to
 * the MEDIA, so a caption has room and the flex line stretches the row. */
.sk-gallery-wall-cells[data-sk-justified-js] .sk-gallery-wall-media {
  aspect-ratio: auto;
  height: var(--sk-justified-h, var(--sk-row-height));
}

.sk-gallery-wall-copy {
  display: flex;
  flex-direction: column;
  gap: var(--kit-space-1);
  /* min-width:auto is min-content here, and overflow-wrap does not lower it. */
  min-width: 0;
  color: var(--sk-ink);
}
.sk-gallery-wall:not([data-variant="grid"], [data-caption-placement="inside"])
  .sk-gallery-wall-copy {
  padding-top: var(--kit-space-2);
}
.sk-gallery-wall[data-variant="grid"][data-gap="none"]:not([data-caption-placement="inside"])
  .sk-gallery-wall-copy {
  padding-bottom: var(--kit-space-2);
}
.sk-gallery-wall[data-align="center"] .sk-gallery-wall-copy {
  text-align: center;
}
.sk-gallery-wall[data-align="right"] .sk-gallery-wall-copy {
  text-align: right;
}
.sk-gallery-wall-caption {
  margin: 0;
  overflow-wrap: break-word;
  color: var(--sk-ink-muted);
}
.sk-gallery-wall-title {
  margin: 0;
  hyphens: auto;
  overflow-wrap: break-word;
}
/* Justified only: its rows are sized from the pictures, so a long title is the
 * one thing that can stretch one. */
.sk-gallery-wall[data-variant="justified"]:not([data-caption-placement="inside"])
  .sk-gallery-wall-title {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A flush mosaic has no wall gutter, so the copy carries its own air. */
.sk-gallery-wall[data-gap="none"]:not([data-variant="scatter"], [data-caption-placement="inside"])
  .sk-gallery-wall-copy {
  padding-inline: var(--kit-space-2);
}
.sk-gallery-wall[data-gap="none"]:not(
    [data-variant="grid"],
    [data-variant="scatter"],
    [data-caption-placement="inside"]
  )
  .sk-gallery-wall-copy {
  padding-block: var(--kit-space-2);
}
.sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-frame {
  position: relative;
  /* The frame clips the copy's rise, on the picture's own corner. */
  border-radius: var(--sk-media-radius);
  overflow: clip;
  --sk-focus-ring-offset: -2px;
}
.sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-copy {
  position: absolute;
  inset: auto 0 0 0;
  /* Sides yield with the tile copy; even here grows the bar over the picture. */
  padding: var(--kit-space-3) min(calc(var(--sk-gutter) * var(--sk-air)), 14%);
  --sk-ink: var(--sk-overlay-ink);
  --sk-ink-on-dark: 1;
  --sk-ink-muted: var(--sk-overlay-ink-muted);
  --sk-selection-bg: var(--sk-selection-page-bg);
  --sk-selection-ink: var(--sk-selection-page-ink);
  /* A stacking context, or the copy's negative-z scrim paints behind the image. */
  z-index: 1;
  max-height: 100%;
}
/* Above the trigger, so it must not take hits. Presence, not `="true"`. */
.sk-gallery-wall[data-lightbox]:not([data-lightbox=""])[data-caption-placement="inside"]
  .sk-gallery-wall-cell:not(a)
  .sk-gallery-wall-copy {
  pointer-events: none;
}
/* Sized from the COPY so the ramp tracks the words; the frame clips the
 * overspill, so the copy must not clip its own. */
.sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-copy::before {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 160%;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to top,
    color-mix(in oklab, var(--sk-wall-scrim) 72%, transparent) 0%,
    color-mix(in oklab, var(--sk-wall-scrim) 64%, transparent) 55%,
    transparent 100%
  );
}
.sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-title {
  /* overflow:hidden drops a flex item's automatic minimum size to zero, so
   * without this the bounded box collapses the title and keeps the caption. */
  flex-shrink: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-caption {
  /* A shorter tile is cut by the BOX, not the clamp, so nothing ellipsises. */
  flex-shrink: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.sk-gallery-wall[data-captions=""] .sk-gallery-wall-copy {
  display: none;
}

/* The PROJECT WALL face's feed states (repeaters/sk-gallery-wall-project.html
 * + -empty.html). The pending atom is base/parts.css's; this sizes its plate
 * to a picture. A wired-but-empty feed speaks once, across every column; the
 * justified pass sizes cells from their pictures and would size this one too
 * (gallery-wall.js skips it by class). */
.sk-gallery-wall-skeleton {
  aspect-ratio: 4 / 3;
}
.sk-gallery-wall-empty {
  grid-column: 1 / -1;
  flex: 1 1 100%;
  padding-block: var(--kit-space-4);
  color: var(--sk-ink-muted);
}
/* visibleWhen is display-only: a staged value keeps riding the template, so
 * every rule here carries the placement or the knob bites where it is hidden. */
@media (hover: hover) {
  .sk-gallery-wall[data-caption-placement="inside"][data-caption-reveal="hover"]
    .sk-gallery-wall-copy {
    /* Inherited channels, so print resets them from the wall root. */
    opacity: var(--sk-wall-copy-rest, 0);
    transform: translateY(var(--sk-wall-copy-lift, 6px));
    transition:
      opacity 0.35s ease,
      transform 0.35s var(--sk-ease-out);
  }
  .sk-gallery-wall[data-caption-placement="inside"][data-caption-reveal="hover"]
    .sk-gallery-wall-cell:hover
    .sk-gallery-wall-copy,
  /* Focus lands ON the cell for a link, INSIDE it for a lightbox trigger. */
  .sk-gallery-wall[data-caption-placement="inside"][data-caption-reveal="hover"]
    .sk-gallery-wall-cell:is(:focus-visible, :has(:focus-visible))
    .sk-gallery-wall-copy {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .sk-gallery-wall[data-caption-placement="inside"][data-caption-reveal="hover"]
    .sk-gallery-wall-copy {
    transition: none;
  }
}

@media (forced-colors: active) {
  .sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-copy::before {
    display: none;
  }
  /* The scrim is gone here, so a hover wall would have nothing to reveal. */
  .sk-gallery-wall {
    --sk-wall-copy-rest: 1;
    --sk-wall-copy-lift: 0px;
  }
  .sk-gallery-wall[data-caption-placement="inside"] .sk-gallery-wall-copy {
    background: Canvas;
    color: CanvasText;
  }
}

/* NO rule here re-flows copy where the script wrote spans, measured on screen:
 * an added line overflows one already written. */
@media print {
  .sk-gallery-wall[data-caption-placement="inside"]
    .sk-gallery-wall-cells:not([data-sk-masonry-spans])
    .sk-gallery-wall-copy {
    position: static;
    max-height: none;
    color: inherit;
    --sk-ink-muted: inherit;
    padding: var(--kit-space-2) 0 0;
  }
  .sk-gallery-wall[data-caption-placement="inside"]
    .sk-gallery-wall-cells:not([data-sk-masonry-spans])
    :is(.sk-gallery-wall-title, .sk-gallery-wall-caption) {
    display: block;
    overflow: visible;
  }
  .sk-gallery-wall[data-caption-placement="inside"]
    .sk-gallery-wall-cells:not([data-sk-masonry-spans])
    .sk-gallery-wall-copy::before {
    display: none;
  }
  /* Dropped copy sits flush in the frame's corner, which a radius would clip. */
  .sk-gallery-wall[data-caption-placement="inside"]
    .sk-gallery-wall-cells:not([data-sk-masonry-spans])
    .sk-gallery-wall-frame {
    overflow: visible;
    border-radius: 0;
  }
  .sk-gallery-wall {
    --sk-wall-copy-rest: 1;
    --sk-wall-copy-lift: 0px;
  }
  .sk-gallery-wall:not([data-caption-placement="inside"])
    .sk-gallery-wall-cells:not([data-sk-masonry-spans])
    .sk-gallery-wall-title {
    display: block;
    overflow: visible;
  }
  /* Print paints no background, so the ink would land on a bare photograph. */
  .sk-gallery-wall[data-caption-placement="inside"]
    .sk-gallery-wall-cells[data-sk-masonry-spans]
    .sk-gallery-wall-copy::before {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }
  /* (hover: hover) matches under print, so the reveal would run here. Carries
   * the reveal because the rule it neutralizes is (0,4,0). */
  .sk-gallery-wall[data-caption-placement="inside"][data-caption-reveal="hover"]
    .sk-gallery-wall-copy {
    transition: none;
  }
}
