/* Photo archive: a numbered grid of contained thumbnails, and a fullscreen viewer that shows one photo
   at a time on black, framed in a thin white border. The red dot turns it into a darkroom (see the end). */

:root {
  --cols: 6;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --font: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --room: #0d0303;      /* the darkroom under its safelight */
  --safelight: #d2321d;
}
@media (max-width: 1199px) { :root { --cols: 5; } }
@media (max-width: 999px) { :root { --cols: 4; } }
@media (max-width: 767px) { :root { --cols: 3; } }

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
html { background: #fff; color: #000; color-scheme: light; -webkit-text-size-adjust: 100%; }
body {
  min-height: 100vh;
  background: #fff;
  font: 500 12px/1 var(--font);
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: inherit; text-decoration: none; }
img { -webkit-user-drag: none; user-select: none; }
:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }
.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ---------------------------------------------------------------- grid
   Each cell is a square 1/cols of the page width. The photo is fitted, uncropped, inside a centred
   square of 2/3 the cell, with its number below. */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  padding: calc(100% / var(--cols) / 12) 0 calc(100% / var(--cols));
}
.cell { display: block; aspect-ratio: 1; padding: 16.6667% 16.6667% 0; }
.cell:focus-visible { outline: none; }
.cell:focus-visible .box { outline: 1px solid #000; outline-offset: 6px; }
.box { position: relative; display: block; aspect-ratio: 1; transition: transform 0.7s var(--ease); }
.box img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.8s var(--ease);
}
.box img.is-loaded { opacity: 1; }
.box .neg { display: none; }
@media (hover: hover) and (pointer: fine) { .box:hover { transform: scale(0.95); } }
.num { display: block; padding-top: 8.125%; text-align: center; font-variant-numeric: tabular-nums; }
.empty { grid-column: 1 / -1; padding: 30vh 24px 0; text-align: center; line-height: 1.5; }

@media (max-width: 599px) {
  .grid { grid-template-columns: minmax(0, 1fr); gap: 40px; padding: 40px; }
  .cell { position: relative; aspect-ratio: var(--ar); padding: 0; }
  .box { position: absolute; inset: 0; aspect-ratio: auto; }
  .num { display: none; }
}

/* ---------------------------------------------------------------- viewer
   The viewer is a size container, so each layer can be fitted to the visible area from its photo's
   aspect ratio (--ar, set by app.js) and the white frame drawn around exactly the photo. */
.viewer {
  --gap: clamp(12px, 3vmin, 36px);  /* black around the frame */
  --frame: clamp(4px, 0.8vmin, 10px);
  position: fixed;
  inset: 0;
  z-index: 10;
  container-type: size;
  background: #000;
  cursor: zoom-out;
  opacity: 0;
  visibility: hidden;
  touch-action: pinch-zoom;
  transition: opacity 0.5s var(--ease), visibility 0s linear 0.5s;
}
.viewer.is-open { opacity: 1; visibility: visible; transition: opacity 0.5s var(--ease); }
.viewer__layer {
  position: absolute;
  top: 50%;
  left: 50%;
  box-sizing: content-box;
  width: min(100cqw - 2 * (var(--gap) + var(--frame)), (100cqh - 2 * (var(--gap) + var(--frame))) * var(--ar, 1.5));
  aspect-ratio: var(--ar, 1.5);
  border: var(--frame) solid #fff;
  background: #fff;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease);
}
.viewer:not(.is-open) .viewer__layer { transition: none; }
.viewer__layer.is-shown { opacity: 1; pointer-events: auto; }
.viewer__layer.is-top { z-index: 1; }
.viewer__layer img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.viewer__layer .neg { display: none; }
.viewer[data-side="prev"] .viewer__layer { cursor: w-resize; }
.viewer[data-side="next"] .viewer__layer { cursor: e-resize; }
body.has-viewer { overflow: hidden; }

/* ---------------------------------------------------------------- darkroom
   html.is-dark: the room is lit only by a red safelight and every photo shows as its colour negative
   (filter #neg in index.html). The enlarger light - the mouse, or on touch screens a band across the
   middle that the photos scroll through - masks the negatives away so the positives show where it falls.
   app.js sets the light's page position (--bx/--by on .grid) and each box's (--ox/--oy). A photo opened
   in the dark develops in its frame: developer washes the negative away. */
:root { --r0: 70px; --r1: 150px; }    /* the light: all positive within r0 of its centre, all negative beyond r1 */
html.is-band { --r0: 11vh; --r1: 21vh; }
html.is-dark { background: var(--room); color: #b9a5a0; color-scheme: dark; }
html.is-dark body { background: transparent; }
html.is-dark .num { color: #a8321f; }
html.is-dark .cell:focus-visible .box { outline-color: #a8321f; }

.room, .beam { position: fixed; z-index: -1; pointer-events: none; opacity: 0; }
.room { inset: 0; background: radial-gradient(110% 65% at 50% -8%, rgba(200, 30, 12, 0.32), transparent 70%); }
.beam {
  left: 0;
  top: 0;
  width: calc(var(--r1) * 2.8);
  height: calc(var(--r1) * 2.8);
  margin: calc(var(--r1) * -1.4) 0 0 calc(var(--r1) * -1.4);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 228, 196, 0.17), rgba(255, 228, 196, 0.05) 60%, transparent);
  will-change: transform;
}
html.is-band .beam {
  top: 50%;
  right: 0;
  width: auto;
  height: calc(var(--r1) * 2.6);
  margin: calc(var(--r1) * -1.3) 0 0;
  border-radius: 0;
  background: linear-gradient(transparent, rgba(255, 228, 196, 0.1) 30% 70%, transparent);
  transform: none !important;
}
html.is-dark .room,
html.is-dark .beam { opacity: 1; }

.grid { --bx: -9999px; --by: -9999px; }
html.is-dark .box .neg {
  display: block;
  filter: url(#neg);
  -webkit-mask-image: radial-gradient(circle at calc(var(--bx) - var(--ox, 0px)) calc(var(--by) - var(--oy, 0px)), transparent var(--r0), #000 var(--r1));
  mask-image: radial-gradient(circle at calc(var(--bx) - var(--ox, 0px)) calc(var(--by) - var(--oy, 0px)), transparent var(--r0), #000 var(--r1));
}
html.is-band .box .neg {
  -webkit-mask-image: linear-gradient(#000 calc(var(--by) - var(--oy, 0px) - var(--r1)), transparent calc(var(--by) - var(--oy, 0px) - var(--r0)),
    transparent calc(var(--by) - var(--oy, 0px) + var(--r0)), #000 calc(var(--by) - var(--oy, 0px) + var(--r1)));
  mask-image: linear-gradient(#000 calc(var(--by) - var(--oy, 0px) - var(--r1)), transparent calc(var(--by) - var(--oy, 0px) - var(--r0)),
    transparent calc(var(--by) - var(--oy, 0px) + var(--r0)), #000 calc(var(--by) - var(--oy, 0px) + var(--r1)));
}

html.is-dark .viewer { background: radial-gradient(110% 70% at 50% -10%, rgba(170, 24, 10, 0.4), transparent 70%), var(--room); }
html.is-dark .viewer__layer .neg {
  display: block;
  filter: url(#neg);
  -webkit-mask: linear-gradient(to bottom right, transparent 45%, #000 55%) 0 0 / 300% 300% no-repeat;
  mask: linear-gradient(to bottom right, transparent 45%, #000 55%) 0 0 / 300% 300% no-repeat;
}
/* The mask is three times the photo's size; sliding it from 84% to 16% carries its soft diagonal edge
   from just past the bottom-right corner to just past the top-left, so the whole wash is visible. */
html.is-dark .viewer__layer.is-developing .neg { animation: develop 2.8s cubic-bezier(0.4, 0, 0.3, 1) both; }
@keyframes develop {
  0%, 18% { -webkit-mask-position: 84% 84%; mask-position: 84% 84%; }
  100% { -webkit-mask-position: 16% 16%; mask-position: 16% 16%; }
}

/* the switch, top right: a small red dot that glows once the lights are off */
.safelight {
  position: fixed;
  top: 8px;
  right: 8px;
  z-index: 5;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
}
.safelight::before {
  content: "";
  position: absolute;
  inset: 11px;
  border-radius: 50%;
  background: var(--safelight);
  transition: transform 0.4s var(--ease), background-color 0.6s var(--ease), box-shadow 0.6s var(--ease);
}
@media (hover: hover) { .safelight:hover::before { transform: scale(1.4); } }
.safelight:focus-visible { outline: 1px solid var(--safelight); outline-offset: 0; }
html.is-dark .safelight::before {
  background: #ff4a2a;
  box-shadow: 0 0 6px 1px rgba(255, 60, 30, 0.9), 0 0 36px 10px rgba(255, 40, 12, 0.28);
}

/* lights off: black, then the safelight flickers on. Lights on: a fluorescent tube's white flicker. */
.curtain { position: fixed; inset: 0; z-index: 50; pointer-events: none; visibility: hidden; background: #000; }
.curtain.is-off { visibility: visible; animation: lights-off 1.5s linear both; }
.curtain.is-on { visibility: visible; background: #fff; animation: lights-on 1.1s linear both; }
@keyframes lights-off {
  0% { opacity: 0; }
  18%, 46% { opacity: 1; }
  50% { opacity: 0.55; }
  53% { opacity: 0.95; }
  58% { opacity: 0.35; }
  63% { opacity: 0.85; }
  100% { opacity: 0; }
}
@keyframes lights-on {
  0% { opacity: 0; }
  8% { opacity: 0.85; }
  12% { opacity: 0.1; }
  20% { opacity: 0.95; }
  24% { opacity: 0.3; }
  30% { opacity: 1; }
  100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; }
  html.is-dark .viewer__layer.is-developing .neg { animation: none; }
}
