/*
 * cs-gallery — every layout, no JavaScript.
 *
 * The whole file is ~3KB. Competitors ship 40–120KB of JS to do the two
 * layouts that flexbox already does, and pay for it twice: once in transfer,
 * once in the layout shift that happens when the script finally measures.
 */

.cs-g { --gap: 0.5rem; --row: 15rem; --cols: 3; --cell: 1/1; }
@media (min-width: 46rem) { .cs-g { --gap: 0.75rem; } }

.cs-g__i { margin: 0; position: relative; }
.cs-g__i img { display: block; width: 100%; height: auto; background: #f0f0f1; }

/* The button is a transparent wrapper: it must not look like a control, and
 * it must still BE one for a keyboard. */
.cs-g__open {
  display: block; width: 100%; padding: 0; border: 0; background: none;
  cursor: zoom-in; border-radius: inherit;
}
.cs-g__open:focus-visible { outline: 3px solid currentColor; outline-offset: 3px; }

.cs-g__cap {
  font-size: 0.8125rem; line-height: 1.4; opacity: 0.72;
  margin-top: 0.35rem;
}

/* ---- JUSTIFIED -----------------------------------------------------------
 * flex-basis is the item's natural width at the target row height, and
 * flex-grow is its aspect ratio, so leftover width is shared in proportion to
 * shape. That IS a justified row, computed by the browser's own layout
 * engine, before any image has loaded.
 */
.cs-g__rows { display: flex; flex-wrap: wrap; gap: var(--gap); }
.cs-g__rows > .cs-g__i {
  flex-grow: var(--ar);
  flex-basis: calc(var(--ar) * var(--row));
  /* Never let one panorama become an entire row on a phone. */
  max-width: 100%;
}
.cs-g__rows > .cs-g__i img { height: auto; }
/* On a phone a justified row is one photograph wide anyway, and the
 * arithmetic left each one at its own natural width — a ragged column of
 * pictures that each stopped short of the edge by a different amount. At
 * this width every photograph gets the full column. */
@media (max-width: 34rem) { .cs-g__rows > .cs-g__i { flex-basis: 100%; } }

/* The last row has fewer items and would otherwise stretch them across the
 * full width, which reads as a bug. Capping growth on the trailing items
 * leaves them at their natural size. */
.cs-g__rows::after {
  content: ""; flex-grow: 999999; flex-basis: 0; min-width: 0;
}

/* ---- MASONRY — real columns, balanced server-side ---------------------- */
.cs-g__cols { display: grid; grid-template-columns: repeat(var(--cols), 1fr); gap: var(--gap); align-items: start; }
.cs-g__col { display: grid; gap: var(--gap); align-content: start; }
@media (max-width: 60rem) { .cs-g__cols { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 34rem) { .cs-g__cols { grid-template-columns: 1fr; } }

/* ---- GRID — uniform, cropped ------------------------------------------ */
.cs-g__grid { display: grid; grid-template-columns: repeat(var(--cols), 1fr); gap: var(--gap); }
.cs-g__grid > .cs-g__i img { aspect-ratio: var(--cell); object-fit: cover; }
@media (max-width: 60rem) { .cs-g__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 34rem) { .cs-g__grid { grid-template-columns: 1fr; } }

/* ---- FILMSTRIP — scroll-snap, no carousel script ---------------------- */
.cs-g__strip {
  display: flex; gap: var(--gap); overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: 0.5rem; scrollbar-width: thin;
}
.cs-g__strip > .cs-g__i { flex: 0 0 auto; scroll-snap-align: start; }
.cs-g__strip > .cs-g__i img { height: var(--row); width: auto; }
.cs-g__strip:focus-visible { outline: 3px solid currentColor; outline-offset: 4px; }

/* ---- COMP CARD ---------------------------------------------------------
 * 5.5 x 8.5 is the industry sheet. The hero is the front; the range strip is
 * the back. Proportions hold on screen so what you see is what prints.
 */
.cs-g__cc { display: grid; gap: var(--gap); }
@media (min-width: 46rem) { .cs-g__cc { grid-template-columns: 5fr 4fr; align-items: start; } }
.cs-g__cc-hero img { aspect-ratio: 5.5 / 8.5; object-fit: cover; }
.cs-g__cc-range { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap); }
.cs-g__cc-range img { aspect-ratio: 3 / 4; object-fit: cover; }

/* ---- LIGHTBOX ----------------------------------------------------------
 * A <dialog>. The browser gives the focus trap, Escape, the top layer and the
 * backdrop for free — all four of the things the accessibility audits list as
 * the most common gallery failures.
 */
.cs-lb { border: 0; padding: 0; max-width: 100vw; max-height: 100vh; width: 100%; height: 100%; background: #000; color: #fff; }
.cs-lb::backdrop { background: rgba(0,0,0,0.92); }
.cs-lb__fig { margin: 0; height: 100%; display: grid; grid-template-rows: 1fr auto; }
.cs-lb__fig img { width: 100%; height: 100%; object-fit: contain; min-height: 0; }
.cs-lb__cap { padding: 0.75rem 1rem 1.25rem; text-align: center; font-size: 0.875rem; }
.cs-lb__count { opacity: 0.6; margin-left: 0.75em; }

/* WCAG 2.2 · 2.5.8 Target Size (Minimum) is 24x24 CSS px. These are 44,
 * which is the touch target size and costs nothing here. */
.cs-lb button {
  position: absolute; background: rgba(0,0,0,0.55); color: #fff; border: 1px solid rgba(255,255,255,0.35);
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer; font-size: 20px; line-height: 1;
  display: grid; place-items: center;
}
.cs-lb button:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
.cs-lb__x { top: 1rem; right: 1rem; }
.cs-lb__prev { left: 1rem; top: 50%; transform: translateY(-50%); }
.cs-lb__next { right: 1rem; top: 50%; transform: translateY(-50%); }

@media (prefers-reduced-motion: reduce) {
  .cs-lb, .cs-lb * { transition: none !important; animation: none !important; }
}

/* ---- PRINT — the comp card is a printable artifact ---------------------- */
@media print {
  .cs-g__open { cursor: default; }
  .cs-lb { display: none !important; }
  .cs-g__cc { grid-template-columns: 5fr 4fr; page-break-inside: avoid; }
  .cs-g__i { break-inside: avoid; }
}
