/* ═══════════════════════════════════════════════════════════════════════════
   ARCHIVE — a sparse indexed grid, sortable, in the project pages' right lane.

   Follows jakubjakubik.com/work: a sparse grid where each image keeps its own
   aspect ratio instead of being cropped to a tile. That is what makes the
   scales read so differently from one another — and with this set, which runs
   from 0.60 to 6.73 in ratio, the range is wider still. A muted index sits
   above each one.

   The page took the project pages' split when the sort run arrived: the run has
   to stay in reach while 69 images go past it, and `project.css` already has a
   sticky left third that does exactly that. So the grid gave up the full width
   for the right two thirds — and then went three across rather than four or
   five, which is the one change that made the cells bigger than they had ever
   been: 309px at 1440 against the 244px the old full-width five-across gave
   them. Three tracks in two thirds of the page is a wider cell than five in all
   of it. Loaded after project.css.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Grid ────────────────────────────────────────────────────────────────────
   The element is `.proj__media` as well, so project.css hands it the two-thirds
   width and the narrow-width reset and this replaces only its flex flow. */

.grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: start;
  column-gap: var(--gut);
  row-gap: 56px;
  padding-bottom: 14svh;

  /* The way back in after a sort. See js/sort.js for why the swap is a fade in
     place rather than the cells travelling to their new positions. */
  transition: opacity .3s var(--ease-out-quart);
}

/* The way out. Quicker than the way in, so the new arrangement reads as
   arriving rather than the old one as leaving — and long enough to cover the
   reflow the reorder causes. js/sort.js holds the same 130ms; the two have to
   agree. */
.grid.is-swapping{
  opacity: 0;
  transition-duration: .13s;
}

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

/* ─── The sort run ────────────────────────────────────────────────────────────
   Seven categories and a reset, and the only thing in the info column — the
   page's title is in the bar, where it was before this column existed. A single
   file down the column's left edge from 1024 up, a wrapped row above the grid
   below it.

   The shape is the nav's pill and the surface is the 3D & Code page's: a fill a
   shade brighter than the page under the same two-layer lift, which is the one
   shadow on the site. What is different is that the label never hides. There a
   pill rests as a circle and opens into its answer, because the answer is the
   reward for the hover; here the label is the control's name, and a reader
   cannot be asked to hover seven circles to find "Posters".

   Nothing else is added: no counts, no rules, no second colour. These are the
   only controls on a page carrying 69 images, and the images are what the page
   is. The live one wears the ink pill — the same mark the nav puts on the
   current page, so "this one is on" reads the same in both places. */

.sorts{
  margin: 0;                    /* nothing above it in the column, so its top is
                                   the column's top — level with the top of the
                                   grid beside it */
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* From 1024 up the column is a third of the page and the run is the only thing
   in it, so it runs down the left edge as a single file. Flush left: the pills'
   left edges make one line and the rag falls on the right, away from nothing in
   particular — there is no second element in the column for it to collide
   with. */
@media (min-width: 1024px){
  .sorts{
    flex-direction: column;
    align-items: flex-start;
  }
}

.sort{
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  font: inherit;
  cursor: pointer;

  display: block;
  padding: 4px 11px;
  border-radius: 999px;
  white-space: nowrap;
  color: var(--ink);

  /* --bar-ground, not #fff: the token for "a shade brighter than the page",
     which is white on the light theme and #202124 on the dark one. */
  background: var(--bar-ground);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .055),
              0 1px 2px rgba(0, 0, 0, .04);

  transition: background-color .2s var(--ease-out-quart),
              color .2s var(--ease-out-quart),
              box-shadow .2s var(--ease-out-quart);
}

.sort:hover{ color: var(--ink-quiet); }

.sort:focus-visible{
  outline: 1px solid var(--ink);
  outline-offset: 2px;
}

/* Live: the nav's ink pill, and the lift comes off — a filled pill has its own
   edge and does not need one. */
.sort.is-on{
  background: var(--ink);
  color: var(--on-ink);
  box-shadow: none;
}
.sort.is-on:hover{ color: var(--on-ink); }


/* ─── Where the sorted run ends ───────────────────────────────────────────────
   Nothing is filtered out, so the grid needs to say where the matches stop and
   the rest of the collection carries on. A hairline across the full width with
   the label sitting on it — the row it makes is the divider. */

.grid__rule{
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--gut);
  margin: 6px 0 -14px;         /* the 56px row gap is already generous; this
                                  pulls the line back towards the run it closes
                                  so it belongs to it rather than floating */
  color: var(--ink-quiet);
}
.grid__rule::after{
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: currentColor;
  opacity: .28;
}
.grid__rule[hidden]{ display: none; }

/* ─── Cell ────────────────────────────────────────────────────────────────── */

.cell{
  margin: 0;
  min-width: 0;                /* grid children need this, or a wide image
                                  pushes its column past the track */
}

/* The caption leads now — date and title, top left of the cell. Set at the
   site's single 13px, the same size the project pages give their copy, so the
   index reads continuously with the rest of the site; --ink-quiet is what
   keeps it a label against 69 images rather than competing with them. */
.cell__label{
  display: block;
  margin-bottom: 10px;
  font-size: var(--fs);
  color: var(--ink-quiet);
  text-wrap: pretty;
}

/* Full track width, and still each image's own height — nothing is cropped. */
.cell__media{
  /* it is a <button>, so the UA border, padding and background have to go —
     without this reset every image sits in a grey inset frame */
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;

  display: block;
  width: 100%;
  background: var(--media-ground);
  cursor: zoom-in;
}

.cell__media img{
  width: 100%;
  height: auto;
  display: block;
  transition: opacity .3s var(--ease-out-quart);
}
.cell__media:hover img,
.cell__media:focus-visible img{ opacity: .78; }

/* ─── Narrow ──────────────────────────────────────────────────────────────── */

/* Below 1024 project.css unstacks the split: the info column runs the full
   width with the run under it, and the grid takes the whole page. Still three
   across — the count does not change here, the cells simply grow into the third
   the sticky column gives back. */
@media (max-width: 1023px){
  .grid{ row-gap: 44px; }

  /* project.css drops 12svh under an unstacked info column, which is right for
     a project's intro standing clear of its images. The sort run is not an
     intro — it is the grid's control, and 97px of ground between a button and
     the thing it rearranges reads as two unrelated blocks. */
  .arch .proj__info{ padding-bottom: 5svh; }

  .sorts{ gap: 8px; }
}

@media (max-width: 639px){
  .grid{ grid-template-columns: repeat(2, 1fr); row-gap: 36px; }

  /* A 21px pill is a fine click and a poor tap. The label does not change size
     — the padding does, which is what takes the run to a 39px target. */
  .sort{ padding: 12px 14px; }
}

/* ─── Zoom ────────────────────────────────────────────────────────────────────
   A native <dialog>, so the browser handles the top layer, Escape, and the
   focus trap rather than us reimplementing them. */

.zoom{
  border: 0;
  padding: 0;
  max-width: 100vw;
  max-height: 100vh;
  width: 100vw;
  height: 100vh;
  background: var(--ground);
  color: var(--ink);
}
.zoom::backdrop{ background: var(--ground); }

.zoom__inner{
  width: 100%;
  height: 100%;
  padding: var(--bar) var(--gut) var(--gut);
  display: flex;
  flex-direction: column;
  gap: var(--gut);
  cursor: zoom-out;
}

.zoom__img{
  flex: 1 1 auto;
  min-height: 0;
  object-fit: contain;
  width: 100%;
  display: block;
}

.zoom__bar{
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  gap: var(--gut);
}
.zoom__cap{ color: var(--ink-quiet); }

.zoom__close{
  appearance: none;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  padding: 0;
  cursor: pointer;
}
.zoom__close:hover{ color: var(--ink-quiet); }
