/* ============================================================
   HOUSE CIRCUIT — EVENT PHOTOS
   Crowd and booth photos placed around the site (home + contact).
   Files are pre-graded to one brightness / saturation by
   tools/optimize_photos.py; .ph adds one on-screen treatment so
   every room, from red basements to daylight patios, reads as the
   same set: a light signal-green colour wash, scanlines and a
   vignette. Framed photos add HUD corner ticks and a mono tag, and
   brighten to full colour on hover.
============================================================ */
.ph {
  --ph-b: 0.95;      /* brightness */
  --ph-tint: 0.12;   /* green wash */
  position: relative;
  overflow: hidden;
  margin: 0;
  background: var(--ink-2);
  isolation: isolate;
}
.ph picture,
.ph img {
  display: block;
  width: 100%;
  height: 100%;
}
.ph img {
  object-fit: cover;
  filter: saturate(0.88) contrast(1.06) brightness(var(--ph-b));
  transition: filter 0.7s var(--ease-out), transform 1.4s var(--ease-out);
}
.ph::before,
.ph::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.ph::before {
  z-index: 1;
  background: var(--signal);
  mix-blend-mode: color;
  opacity: var(--ph-tint);
  transition: opacity 0.7s var(--ease-out);
}
.ph::after {
  z-index: 2;
  background:
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0 1px, transparent 1px 3px),
    radial-gradient(120% 95% at 50% 45%, transparent 55%, rgba(5, 7, 6, 0.6) 100%);
}

/* Framed photos: HUD corner ticks (some add a tag), full colour on hover. */
.mission-photo,
.reel-frame,
.join-tile { border: 1px solid var(--line-2); }
.mission-photo::after,
.reel-frame::after,
.join-tile::after {
  --tick: linear-gradient(var(--signal), var(--signal));
  background:
    var(--tick) left 6px top 6px / 14px 1px no-repeat,
    var(--tick) left 6px top 6px / 1px 14px no-repeat,
    var(--tick) right 6px top 6px / 14px 1px no-repeat,
    var(--tick) right 6px top 6px / 1px 14px no-repeat,
    var(--tick) left 6px bottom 6px / 14px 1px no-repeat,
    var(--tick) left 6px bottom 6px / 1px 14px no-repeat,
    var(--tick) right 6px bottom 6px / 14px 1px no-repeat,
    var(--tick) right 6px bottom 6px / 1px 14px no-repeat,
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.2) 0 1px, transparent 1px 3px),
    radial-gradient(120% 95% at 50% 45%, transparent 55%, rgba(5, 7, 6, 0.6) 100%);
}
@media (hover: hover) {
  .reel-frame:hover,
  .join-tile:hover { --ph-b: 1.02; --ph-tint: 0.03; }
  .reel-frame:hover img,
  .join-tile:hover img { transform: scale(1.05); }
}
.ph-tag {
  position: absolute;
  z-index: 3;
  left: 26px;     /* clear of the corner ticks */
  bottom: 9px;
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--signal);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9);
}

/* ------------------------------------------------------------
   MISSION: three booth shots beside the statement. As each one's
   reveal fires it wipes in, then the room lights up (dim and green
   → full) and the shot drifts into place, alongside the words.
------------------------------------------------------------ */
.mission-copy { display: grid; gap: clamp(20px, 3.4vh, 36px); }
.mission-photos { display: none; }
.mission-photo { --ph-b: 0.42; --ph-tint: 0.4; }
.mission-photo.is-in { --ph-b: 0.98; --ph-tint: 0.14; }
.mission-photo img { transform: scale(1.1) translate3d(0, calc(var(--drift, 24px) * 0.5), 0); }
.mission-photo.is-in img { transform: scale(1.06); }
.motion .mission-photo img {
  transition:
    filter 1.6s ease calc(var(--d, 0s) + 0.5s),
    transform 2.6s var(--ease-out) var(--d, 0s);
}
.motion .mission-photo::before { transition: opacity 1.6s ease calc(var(--d, 0s) + 0.5s); }

@media (min-width: 1024px) and (min-height: 600px) {
  .mission-grid {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: clamp(40px, 5vw, 96px);
  }
  .mission-photos {
    display: block;
    position: relative;
    height: min(60svh, 33vw, 600px);   /* leaves the statement its width */
    aspect-ratio: 0.8;
  }
  .mission-photo {
    position: absolute;
    height: 62%;
    aspect-ratio: 2 / 3;
  }
  .mission-photo .ph-tag { top: 9px; bottom: auto; }   /* the overlap hides bottoms */
  .mission-photo:nth-child(1) { left: 0; top: 0; --drift: 30px; }
  .mission-photo:nth-child(2) { left: 24%; top: 19%; z-index: 1; --drift: -22px; }
  .mission-photo:nth-child(3) { right: 0; top: 38%; z-index: 2; --drift: 40px; }
}
/* Tall tablets / phones: a row of three under the pillars. */
@media (max-width: 1023px) and (min-height: 820px) {
  .mission-photos {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    max-width: 560px;
    margin-top: clamp(4px, 1.5vh, 16px);
  }
  .mission-photo { aspect-ratio: 3 / 4; }
}
@media (max-width: 599px) {
  .mission-photo .ph-tag { display: none; }
}

/* ------------------------------------------------------------
   THE NAME: each entry has its photo behind it, fading in
   from the right (the booth for "house", the floor for "circuit").
------------------------------------------------------------ */
.entry { position: relative; overflow: hidden; isolation: isolate; }
.entry > :not(.entry-photo) { position: relative; z-index: 1; }
.entry-photo {
  --ph-b: 0.62;
  position: absolute;
  z-index: 0;
  inset: 0 0 0 32%;
  opacity: 0.8;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 55%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 55%);
}
.entry-head dfn,
.entry-def { text-shadow: 0 1px 14px rgba(5, 7, 6, 0.85); }

/* ------------------------------------------------------------
   TICKETS: the floor behind the Posh panel.
------------------------------------------------------------ */
.stub { overflow: hidden; isolation: isolate; }
.stub-main { position: relative; z-index: 1; }
.stub-photo {
  --ph-b: 0.58;
  position: absolute;
  z-index: 0;
  inset: 0;
  opacity: 0.85;
  -webkit-mask-image: linear-gradient(90deg, transparent 8%, #000 72%);
          mask-image: linear-gradient(90deg, transparent 8%, #000 72%);
}
@media (max-width: 959px) {
  .stub-photo {
    --ph-b: 0.45;
    -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 85%);
            mask-image: linear-gradient(180deg, #000 0%, transparent 85%);
  }
}
.stub-title,
.stub-copy { text-shadow: 0 1px 16px rgba(5, 7, 6, 0.9); }

/* ------------------------------------------------------------
   THE REEL — crowd photos drifting past like a film strip between
   the venues and tickets (tilted a touch, sprocket holes top and
   bottom). It never stops: hovering a frame only brightens it.
------------------------------------------------------------ */
.reel {
  position: relative;
  z-index: 1;
  overflow: hidden;
  padding-block: clamp(24px, 5vh, 56px) clamp(48px, 10vh, 112px);   /* transparent: the board runs under the reel */
}
.reel-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  max-width: var(--maxw);
  margin: 0 auto clamp(14px, 2.4vh, 24px);
  padding-inline: var(--gutter);
}
.reel-bar .label { color: var(--text-2); }
.reel-link { transition: color var(--t-fast); }
.reel-link:hover { color: var(--signal); }
.reel-window {
  transform: rotate(-1.6deg);
  margin-inline: -3vw;
  /* Film gate: frames fade in and out at the edges (and clear the stop map). */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
}
@media (min-width: 1100px) {
  .reel-window {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, transparent 60px, #000 220px, #000 90%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, transparent 60px, #000 220px, #000 90%, transparent 100%);
  }
}
.reel-track {
  --frame-h: clamp(200px, 32svh, 420px);   /* big enough to really see the room */
  display: flex;
  width: max-content;
  padding-block: 16px;
  background:
    repeating-linear-gradient(90deg, var(--line-2) 0 10px, transparent 10px 22px) left top 4px / 100% 6px no-repeat,
    repeating-linear-gradient(90deg, var(--line-2) 0 10px, transparent 10px 22px) left bottom 4px / 100% 6px no-repeat;
}
.reel-frame {
  --ph-b: 1;
  --ph-tint: 0.08;
  flex: none;
  height: var(--frame-h);
  aspect-ratio: 3 / 2;
  margin-right: 12px;
}
@media (max-width: 699px) {
  .reel-track { --frame-h: clamp(180px, 30svh, 280px); }   /* about one frame per screen width */
}
/* It slides by a transform set each frame (photo strips, site.js), not
   a CSS animation: see the venues band below. */
.motion [data-marquee] { will-change: transform; }
/* No motion: a still strip you can scroll sideways. */
html:not(.motion) .reel-window { transform: none; margin-inline: 0; overflow-x: auto; }
html:not(.motion) [data-loop-copy] { display: none; }   /* the second pass only serves the loop */
@media (min-width: 1100px) {
  .reel-bar { padding-left: max(var(--gutter), 88px); }
}
@media (min-width: 1800px) and (min-height: 1100px) {
  .reel-bar { padding-left: max(var(--gutter), 100px); }
  .reel-track { --frame-h: clamp(200px, 32svh, 520px); }
}

/* ------------------------------------------------------------
   PHOTO BANDS — full-bleed photos between the home stops, nearly a
   screen tall, so the rooms are really seen. A lighter treatment than
   the framed shots (a faint wash, fine scanlines), soft fades into the
   board above and below, and a big caption. Reveal (one way): the
   frame opens like a letterbox and the photo settles. Phones: a
   portrait photo fills the screen (ph.art). The venues band is a
   gallery sliding across the screen.
------------------------------------------------------------ */
.band {
  position: relative;
  z-index: 1;
  height: clamp(420px, 82svh, 980px);
  margin: 0;
  overflow: hidden;
  isolation: isolate;
}
.band-photo {
  --ph-b: 1;
  --ph-tint: 0.07;
  position: absolute;
  inset: 0;
}
.band-photo img { object-position: 50% 45%; }
.band--late .band-photo img { object-position: 38% 35%; }
.band--floor .band-photo img { object-position: 50% 30%; }   /* the hands, over the DJ */
.band-photo::after,
.tile::after {
  background:
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.12) 0 1px, transparent 1px 3px),
    radial-gradient(130% 100% at 50% 45%, transparent 60%, rgba(5, 7, 6, 0.45) 100%);
}
/* Fades into the board above and below, and a floor for the caption. */
.band::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(180deg, var(--ink) 0%, rgba(5, 7, 6, 0) 14%, rgba(5, 7, 6, 0) 50%, rgba(5, 7, 6, 0.8) 86%, var(--ink) 100%);
}
.band-caption {
  position: absolute;
  z-index: 3;
  left: 0;
  right: 0;
  bottom: clamp(32px, 8svh, 88px);
  display: grid;
  justify-items: start;
  gap: clamp(10px, 1.6vh, 18px);
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  pointer-events: none;   /* swipes and clicks go through to the photos */
}
.band-caption .label { color: var(--text); text-shadow: 0 1px 12px rgba(0, 0, 0, 0.8); }
.band-quote {
  margin: 0;
  font-family: var(--f-display);
  font-weight: 900;
  font-size: clamp(40px, min(8vw, 11svh), 150px);
  line-height: 0.9;
  text-transform: uppercase;
  color: var(--text);
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.6);
}
@media (min-width: 1100px) {
  .band-caption { padding-left: max(var(--gutter), 88px); }   /* clear of the stop map */
}

/* The venues wall: blocks of photos packed three rows high, sliding
   right to left (two identical passes; site.js moves the track one pass
   along every 90s, never pausing for the pointer, only off screen). It
   reads as parts on the circuit board: the band is transparent, so the
   board's traces run through the gaps; every frame is a component with
   a thin signal outline, solder pads at its corners and a silkscreen
   reference (U big, J wide, Q tall, R small). Modules (.mod) hold whole
   frames, so each pass ends square and the loop is seamless. A frame is
   1.5 rows wide (3:2; 4:5 on phones, so more fit across); big = 2x2,
   wide = 2x1, tall = 1x2.
   Kept light for phones: no colour wash or filter per photo (scanlines
   only), phone-sized sources, and no CSS animation (phones keep an
   animated layer's whole path painted). */
.band--slides {
  --wall-g: 10px;
  --wall-ratio: 1.5;   /* a frame's width : height (phones: 0.8, so more frames fit across) */
  --wall-row: calc((clamp(420px, 82svh, 980px) - 2 * var(--wall-g)) / 3);
  --wall-col: calc(var(--wall-row) * var(--wall-ratio));
}
.band-window { position: absolute; inset: 0; }
.band-track {
  display: flex;
  width: max-content;
  height: 100%;
}
.band-pass {
  display: flex;
  gap: var(--wall-g);
  height: 100%;
  margin: 0;
  padding: 0 var(--wall-g) 0 0;
  list-style: none;
}
.mod {
  display: grid;
  gap: var(--wall-g);
  height: 100%;
  grid-template-rows: repeat(3, minmax(0, 1fr));
  grid-template-columns: var(--wall-col);
}
.mod--bw,
.mod--wb,
.mod--wsw { grid-template-columns: repeat(2, var(--wall-col)); }
.mod--bw { grid-template-areas: "a a" "a a" "b b"; }
.mod--wb { grid-template-areas: "a a" "b b" "b b"; }
.mod--wsw { grid-template-areas: "a a" "b c" "d d"; }
.mod--ts { grid-template-areas: "a" "a" "b"; }
.mod--st { grid-template-areas: "a" "b" "b"; }
.mod > :nth-child(1) { grid-area: a; }
.mod > :nth-child(2) { grid-area: b; }
.mod > :nth-child(3) { grid-area: c; }
.mod > :nth-child(4) { grid-area: d; }
.mod--sss > * { grid-area: auto; }

.tile {
  --ph-b: 1;
  --ph-tint: 0.07;
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: visible;
  border: 1px solid rgba(57, 255, 127, 0.3);
  background: #000;
}
.tile picture,
.tile img { display: block; width: 100%; height: 100%; object-fit: cover; }
.tile img { filter: none; transition: none; }
.tile::before {
  /* Solder pads on the four corners. */
  content: '';
  position: absolute;
  inset: -4px;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(var(--signal), var(--signal)) left top / 7px 7px no-repeat,
    linear-gradient(var(--signal), var(--signal)) right top / 7px 7px no-repeat,
    linear-gradient(var(--signal), var(--signal)) left bottom / 7px 7px no-repeat,
    linear-gradient(var(--signal), var(--signal)) right bottom / 7px 7px no-repeat;
  mix-blend-mode: normal;   /* not the .ph colour wash: no per-frame blending while the wall slides */
  opacity: 0.8;
  transition: none;
}
.tile-ref {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 3;
  padding: 2px 6px;
  border: 1px solid rgba(57, 255, 127, 0.35);
  background: rgba(5, 7, 6, 0.72);
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  line-height: 1.4;
  color: var(--signal);
}
/* No motion: one pass you can scroll sideways. */
html:not(.motion) .band-window { overflow-x: auto; overflow-y: hidden; }
html:not(.motion) .band-pass[aria-hidden] { display: none; }

/* Reveal: the letterbox opens and the photo settles (once). */
.motion [data-reveal="band"] {
  opacity: 1;
  transform: none;
  clip-path: inset(14% 0 14% 0);
  transition: clip-path 1.4s var(--ease-out) var(--d, 0s);
}
.motion [data-reveal="band"].is-in { clip-path: inset(0 0 0 0); }
.motion [data-reveal="band"] img {
  transform: scale(1.14);
  transition: transform 2.4s var(--ease-out) var(--d, 0s), filter 0.7s var(--ease-out);
}
.motion [data-reveal="band"].is-in img { transform: scale(1.02); }

/* Phones: the portrait photo fills the screen; the venues slide past
   one or two at a time, at full height. */
@media (max-width: 699px) {
  .band { height: clamp(460px, 80svh, 760px); }
  .band-quote { font-size: clamp(40px, 12.5vw, 64px); }
  .band--slides { --wall-g: 8px; --wall-ratio: 0.8; --wall-row: calc((clamp(460px, 80svh, 760px) - 2 * var(--wall-g)) / 3); }
  .tile-ref { top: 6px; left: 6px; font-size: 9px; }
}
@media (min-width: 1800px) and (min-height: 1100px) {
  .band-caption { padding-left: max(var(--gutter), 100px); }
}

/* ------------------------------------------------------------
   JOIN: a small gallery of the floor beside the sign-up. A mosaic
   (one big crowd shot, two tall stage shots, three smaller frames)
   on desktop, a 3×2 grid on tablets and tall phones, one row on
   other phones (and no head row on short ones). The photos are
   pictures, not buttons; "Full gallery" goes on.
------------------------------------------------------------ */
.join-gallery { display: grid; gap: 12px; min-width: 0; }
.join-gallery-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
.join-gallery-head .label { color: var(--text-2); }
.join-mosaic {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.join-cell { min-width: 0; }
.join-tile {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
}
@media (max-width: 959px) and (max-height: 740px),
       (max-width: 699px) and (max-height: 799px) {
  .join-cell:nth-child(n + 4) { display: none; }
}
@media (max-width: 699px) and (max-height: 700px) {
  .join-gallery-head { display: none; }
  .join-tile { aspect-ratio: 16 / 10; }
}
@media (max-width: 359px), (max-width: 699px) and (max-height: 600px) {
  .join-tile { aspect-ratio: 2 / 1; }
}
@media (min-width: 960px) {
  .join-mosaic {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-rows: repeat(3, minmax(0, 1fr));
    grid-template-areas:
      "a a b c"
      "a a b d"
      "e f f d";
    gap: 10px;
    height: clamp(300px, min(54svh, 36vw), 620px);
  }
  .join-cell:nth-child(1) { grid-area: a; }
  .join-cell:nth-child(2) { grid-area: b; }
  .join-cell:nth-child(3) { grid-area: c; }
  .join-cell:nth-child(4) { grid-area: d; }
  .join-cell:nth-child(5) { grid-area: e; }
  .join-cell:nth-child(6) { grid-area: f; }
  .join-tile { height: 100%; aspect-ratio: auto; }
}
@media (min-width: 1800px) and (min-height: 1100px) {
  .join-mosaic { height: clamp(300px, min(54svh, 36vw), 820px); gap: 14px; }
}

/* ------------------------------------------------------------
   CONTACT — the reel under the page heading: the same sliding film
   strip, a little lower, with no head row.
------------------------------------------------------------ */
.contact-reel { margin-top: clamp(20px, 3vh, 40px); padding-block: 0 8px; }
.contact-reel .reel-track { --frame-h: clamp(150px, 24svh, 300px); }

/* Browsers without small-viewport units: the svh sizes in vh. */
@supports not (height: 1svh) {
  .band--slides { --wall-row: calc((clamp(420px, 82vh, 980px) - 2 * var(--wall-g)) / 3); }
  .reel-track { --frame-h: clamp(200px, 32vh, 420px); }
  .contact-reel .reel-track { --frame-h: clamp(150px, 24vh, 300px); }
  .band { height: clamp(420px, 82vh, 980px); }
  @media (min-width: 1024px) and (min-height: 600px) {
    .mission-photos { height: min(60vh, 33vw, 600px); }
  }
  @media (min-width: 960px) {
    .join-mosaic { height: clamp(300px, min(54vh, 36vw), 620px); }
  }
}
