:root {
  /* Colours */
  --bg: #FFFFFF;
  --fg: #212529;
  --muted: color-mix(in srgb, var(--fg) 62%, transparent);

  /* Image styling */
  --img-radius: 4px;
  --placeholder-bg: #BBBBBB; /* grey each photo fades in from/over while loading */

  /* How much each photo "zooms" up as it fades in — read by assets/js/main.js,
     not used directly in CSS. 0 = no zoom, photo appears at full size.
     0.02 means it starts 2% smaller and grows to full size as it fades in. */
  --zoom-scale: 0;

  /* Length of each step in the photo transition (fade the old photo out,
     fade the grey placeholder in, fade the new photo in over it) — read by
     assets/js/main.js. A plain number of seconds, no unit (e.g. 0.25, not
     0.25s), since it's consumed in JS rather than by CSS directly. */
  --transition-step: 0.25;

  /* Caption — fixed to the bottom of the viewport, centred, lined up
     with the counter below */
  --title-size: 0.5rem;

  /* Wordmark — fixed to the top of the viewport, centred. Text for now;
     due to become an SVG logo, at which point --wordmark-size stops
     applying (size it via the SVG's own width/height instead). */
  --wordmark-size: 0.85rem;

  /* "Next"/"Previous" cursor replacement shown over the photo — see
     #nav-hint and assets/js/main.js. */
  --nav-hint-size: 0.5rem;

  /* Shared edge inset — how far the header, caption, counter and info
     toggle sit from the viewport's own edges. --edge-y is redefined for
     mobile below. Reused as #info-drawer's own padding too, so its copy
     lines up with the header rather than using a separate figure — which
     is also why --edge-y is in vh rather than a bare %: as a top/bottom
     *offset* on a fixed element the two are identical, but CSS always
     resolves padding percentages against the containing block's width
     (even on the top/bottom sides), so a bare % here would have made the
     drawer's vertical padding silently disagree with the header's actual
     top offset. --edge-x doesn't need the same treatment: left/right
     offsets and left/right padding both resolve against width anyway. */
  --edge-x: 5%;
  --edge-y: 4vh;

  /* Info drawer (contact/copyright) and its overlay — see #info-toggle,
     #overlay and #info-drawer, and assets/js/main.js for the open/close
     behaviour. */
  --drawer-bg: #212529;
  --drawer-fg: #FFFFFF;
  --drawer-width: min(360px, 86vw);
  --overlay-blur: 12px;

  /* The frame's own footprint — redefined for mobile below. Both .frame
     and .stack size from these directly, rather than from each other's
     computed box, so the photo can't end up wider than its own content.
     84vmin (rather than a plain 90) is deliberate: on the frame's
     constrained axis that leaves an 8% margin on both top and bottom —
     equal, and enough for the header/footer text to clear the photo on
     both edges rather than just the bottom. */
  --frame-w: 84vmin;
  --frame-h: 84vmin;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
}

/* The nav area (assets/js/main.js) spans the whole of #stage — i.e. the
   whole viewport — rather than just the photo frame, so scrolling,
   swiping, clicking and the next/previous hover label all work anywhere
   on the page, not only when the pointer is over the image itself. */
#stage {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none;
  cursor: pointer;
}

/* Native cursor is swapped out for #nav-hint's text label while hovering
   (assets/js/main.js adds this class) — desktop/mouse only. */
#stage.is-hovering {
  cursor: none;
}

/* Desktop: a 1:1 frame whose side equals the shorter viewport dimension,
   giving a 5% margin on every side (90vmin = 100vmin minus 5% top/bottom
   or left/right, whichever is the constrained axis). */
.frame {
  position: relative;
  width: var(--frame-w);
  height: var(--frame-h);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Sized directly off the frame's own dimensions (not a percentage of an
   ancestor whose own size depends on this element) — that's what keeps
   the photo's box exactly matched to the photo, so it centers cleanly
   instead of sitting in a wider, left-aligned box. */
.stack {
  position: relative;
  max-width: var(--frame-w);
  max-height: var(--frame-h);
  display: grid;
  grid-template-columns: 100%;
  grid-template-rows: 100%;
  overflow: hidden; /* belt-and-braces: clip rather than distort if anything
                        still tries to overflow this box */
}

.stack > .placeholder,
.stack > picture {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
}

.stack > .placeholder {
  background: var(--placeholder-bg);
  border-radius: var(--img-radius);
}

.stack > picture {
  display: block;
  opacity: 0; /* GSAP (assets/js/main.js) animates this wrapper's opacity for
                 the crossfade, so the starting state belongs here now, not
                 on the <img> inside it */
  will-change: opacity, transform;
  pointer-events: none;
}

.stack > picture > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: var(--img-radius);
  -webkit-user-drag: none;
}

#nav-hint {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  font-size: var(--nav-hint-size);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #BBBBBB;
  /* Inverts against whatever photo content is under it, same technique
     as the wordmark — this is mix-blend-mode, not filter (see the note
     on #wordmark below). Keeps the label legible over any photo tone. */
  mix-blend-mode: difference;
  opacity: 0; /* shown by JS on mousemove, hidden on mouseleave */
  pointer-events: none;
  white-space: nowrap;
  z-index: 200; /* above the wordmark (100) and the photo stack */
}

#site-header {
  position: fixed;
  top: var(--edge-y); /* matches the caption's own offset from its edge */
  left: var(--edge-x);
  right: var(--edge-x);
  z-index: 100; /* above every other layer except the info drawer/overlay
                    stack (150+) and the photo stack, which tops out at
                    z-index 2 */
  display: flex;
  justify-content: center;
  pointer-events: none;
}

#wordmark {
  font-size: var(--wordmark-size);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #212529;
  /* Inverts against whatever is behind it (photo or bare background)
     rather than sitting at a fixed colour — this is a blend mode, not a
     filter: CSS's `filter` property has no "difference" value, only
     mix-blend-mode does, so that's what's applied here to get the effect
     you're after. */
  mix-blend-mode: difference;
}

/* Info toggle, its dimming/blurring overlay, and the slide-out drawer
   itself — opened/closed by assets/js/main.js. Stacking, top to bottom:
   #info-toggle (300) always reachable, even once the drawer's open;
   #info-drawer (250) slides out above the overlay; #overlay (150) sits
   above the photo stack and wordmark but below both of those. */
#info-toggle {
  position: fixed;
  top: var(--edge-y);
  right: var(--edge-x);
  z-index: 300;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0;
  border: none; 
  border-radius: 50%;
  background: none;
  font: inherit;
  /*font-style: italic;*/
  font-weight: 600;
  font-size: 0.8rem;
  line-height: 1;
  color: #CCCCCC;
  /* Same difference-blend trick as the wordmark, so the toggle stays
     legible whatever photo is behind it. */
  mix-blend-mode: difference;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* A 50% white wash over the photo, with the photo itself blurred behind
   it. That's `backdrop-filter`, not `filter` — `filter` would blur the
   overlay's own (empty) contents, `backdrop-filter` blurs whatever is
   showing through it, which is the effect wanted here. */
#overlay {
  position: fixed;
  inset: 0;
  z-index: 150;
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: blur(var(--overlay-blur));
  -webkit-backdrop-filter: blur(var(--overlay-blur));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

#overlay.is-visible {
  opacity: 1;
  pointer-events: auto; /* clicking the wash closes the drawer */
}

#info-drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 250;
  width: var(--drawer-width);
  max-width: 100vw;
  height: 100%;
  background: var(--drawer-bg);
  color: var(--drawer-fg);
  text-align: left;
  /* Same figures as the header's own top/left/right offset, applied here
     as padding, so the copy's top and left edges line up with it. */
  padding: var(--edge-y) var(--edge-x);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform 0.4s ease;
}

#info-drawer.is-open {
  transform: translateX(0);
}

#info-drawer h2 {
  margin: 0 0 0.5em;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

#info-drawer p {
  margin: 0 0 1.5em;
  font-size: 0.8rem;
  line-height: 1.5;
}

#info-drawer a {
  color: inherit;
}

figcaption#caption {
  position: fixed;
  left: var(--edge-x);
  right: var(--edge-x);
  bottom: var(--edge-y); /* matches .progress, so the two line up */
  text-align: center;
  pointer-events: none;
}

#caption-title {
  margin: 0;
  font-size: var(--title-size);
  font-weight: 500;
  letter-spacing: 0.02em;
}

#caption-desc {
  margin: 0.3em 0 0;
  font-size: calc(var(--title-size) * 0.82);
  color: var(--muted);
}

.progress {
  position: fixed;
  right: var(--edge-x);
  bottom: var(--edge-y);
  font-size: 0.75rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Mobile: fill as much of the viewport as possible rather than a fixed
   square, while still preserving the photo's original aspect ratio. */
@media (max-width: 767px) {
  :root {
    --frame-w: 97vw;
    --frame-h: 88vh; /* equal ~6% margin top and bottom once centred —
                         the minimum needed to clear the header/footer
                         text, applied evenly on both edges */
    --edge-y: 3vh; /* header/caption/counter/toggle all move with this */
  }
}
