/* The reference's layout engine, rebuilt.

   laygridder places every item with two numbers: a span (how many of the 25
   columns it covers) and a push (how many columns of air sit to its left).
   Push is measured from the page margin for the first item in a row and from
   the previous item's right edge for any after it, which is why every two-up
   row in the reference sums to exactly 25 (10+1+14, 12+1+12, 9+1+15 …).

   Columns are computed with calc() rather than CSS grid so the 98px page
   margin stays a fixed pixel value at every width, exactly as the reference
   has it: (100% - 196px) / 25. */

.grid {
  position: relative;
  font-size: 0; /* kills inline-block whitespace, same trick the reference uses */
}

.row {
  position: relative;
  margin-bottom: var(--row-gap);
}
.row.is-tight { margin-bottom: var(--row-gap-tight); }

.col {
  position: relative;
  display: inline-block;
  vertical-align: top;
  z-index: 1;
}
/* the reference's lg-align-middle: two pictures of different heights in one
   row hang off a shared centre line rather than a shared top edge */
.col.is-middle { vertical-align: middle; }

/* a caption row sits tight under its pictures */
.row.is-captions { margin-bottom: var(--row-gap); }
.row.is-tight + .row.is-captions { margin-top: 0; }

/* width: N columns of the 25 */
.col {
  --span: 25;
  --push: 0;
  width: calc((100% - var(--page-margin) * 2) / var(--cols) * var(--span) - var(--col-shave));
}

/* first item in a row carries the page margin; later ones offset from the
   previous item's edge */
.col:first-child {
  margin-left: calc(var(--page-margin)
    + (100% - var(--page-margin) * 2) / var(--cols) * var(--push)
    + var(--col-nudge));
}
.col + .col {
  margin-left: calc((100% - var(--page-margin) * 2) / var(--cols) * var(--push)
    + var(--col-nudge) + var(--gutter));
}

/* full bleed: the one place an item ignores the page margin */
.col.is-bleed { width: 100%; }
.col.is-bleed:first-child { margin-left: 0; }

/* --- media ------------------------------------------------------------- */

/* Aspect is reserved with padding-bottom so nothing shifts as images arrive,
   which is what the reference does on its .lg-placeholder anchors. */
.media {
  position: relative;
  display: block;
  width: 100%;
  overflow: hidden;
  background: var(--paper);
}
.media > img,
.media > video,
.media > iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 0;
}

/* Progressive load: a 10px-wide placeholder blurred under the real file, which
   fades in over it. The reference does this with an SVG feGaussianBlur; a CSS
   filter is the same picture with less plumbing. */
.media > img.lo {
  filter: blur(12px);
  transform: scale(1.06); /* hides the blur's soft edge */
  z-index: 0;
}
.media > img.hi {
  z-index: 1;
  opacity: 0;
  transition: opacity var(--t-mid) var(--ease);
}
.media > img.hi.is-loaded { opacity: 1; }

/* --- captions ------------------------------------------------------------
   Two kinds, both 13-15px. The project title sits under its image; on the work
   index it is always visible, on a project page it fades in on hover. */
.caption {
  display: block;
  font-size: var(--t-medium);
  line-height: 1.1;
  letter-spacing: var(--track);
  color: var(--ink);
  text-decoration: none;
  padding-top: 3px;
}
.caption .disc {
  display: block;
  font-style: italic;
}
.caption .code {
  font-family: var(--font-mono);
  font-size: var(--t-caption);
}

/* Captions show by default. On a project page the grid opts into holding them
   back until the picture is hovered, which is what the reference does
   everywhere but its front page. */
html.can-hover .grid.hover-captions .row.is-captions .col > .caption {
  opacity: 0;
  transition: opacity var(--t-mid) var(--ease);
}
html.can-hover .grid.hover-captions .row.is-captions .col.is-shown > .caption { opacity: 1; }

@media (max-width: 700px) {
  /* one column, everything full width inside a 15px margin */
  .col,
  .col:first-child,
  .col + .col {
    display: block;
    width: calc(100% - var(--page-margin) * 2);
    margin-left: var(--page-margin);
  }
  .col.is-bleed,
  .col.is-bleed:first-child { width: 100%; margin-left: 0; }
  .col + .col { margin-top: 8%; }
  .row.is-captions .col > .caption { opacity: 1; }
  .row.is-captions .col + .col { margin-top: 0; }
}

/* a thumbnail that is a clip: the anchor wraps the player so the whole card
   still navigates, and the iframe must not swallow the click */
.media-link { display: block; position: relative; }
.media-link .media.is-video iframe { pointer-events: none; }

/* A sequence block: every frame stacked, cross-faded by opacity. They must all
   occupy the same box or the cycle would jog the page. */
.media.is-sequence > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
