/* ============================================================
   518 Official — EVENTS page layout
   css/pages/events.css?v=4 — loads AFTER tokens.css + site.css.
   Owns events.html layout ONLY. Never restyles the shell, never
   overrides tokens, never declares :root. All classes are page
   local (ev- prefix, plus .spec / .next-slot family shared with
   the event-detail dialect).

   Palette law (RIZZ): VOLTAGE COBALT is the only chrome accent —
   fills/strokes via --rizz / --rizz-line, copy via --rizz-text
   only. No amber here (commerce-only token).

   FLYER LAW (binding): a flyer is NEVER cover-cropped — cropping
   one destroys the information it exists to carry. Every flyer
   surface in this sheet is object-fit: CONTAIN, and the card
   widths below are derived from each flyer's true aspect so
   "contain" letterboxes by ~nothing. Only the coverage-photo
   strip (.ev-shot) uses object-fit: cover, and every tile there
   carries its measured face-safe object-position.

   Transform ownership (motion.js contract) — one owner per node:
     .ev-artifact        GSAP  (data-reveal)
     .ev-artifact-tilt   CSS   (static tilt + hover lift)
     .ev-ghost           GSAP  (data-parallax)
     .ev-carousel-track  events-carousel.js (rAF conveyor)
   Nothing in this file transforms a node a script drives.

   TOC:
   01 Hero            05 Archive carousel + cards + lightbox
   02 Ticker band     06 From the room (film + coverage strip)
   03 Shared pieces   07 Next date (held slot)
   04 Permanent-page  08 Venue band + atmosphere slot
      link            09 Small-screen floor
   ============================================================ */

/* ---------- 01 Hero ---------- */
.ev-hero {
  padding-top: clamp(0.5rem, 3vw, 2.75rem);
  padding-bottom: clamp(3rem, 8vw, 6rem);
}

.ev-hero .eyebrow { margin-bottom: clamp(1.75rem, 5vw, 3rem); }

/* Hero overline — Space Grotesk so it reads as a marquee card,
   not a second headline. */
.ev-kicker {
  font-family: var(--font-logo);
  font-size: clamp(1.35rem, 4.2vw, 2.2rem);
  line-height: 1;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ivory-60);
  margin-bottom: clamp(0.5rem, 1.6vw, 0.9rem);
}

/* Archivo Black runs wide — ceiling tuned under --fs-hero so the
   single word holds the 360px floor without wrapping. */
.ev-hero-title {
  font-size: clamp(2rem, 5.4vw, 4rem);
  line-height: 0.95;
  letter-spacing: -0.012em;
  text-wrap: initial;              /* any <br> owns the break, not the browser */
  max-width: 16ch;
}

/* Terminal period — the one cobalt mark in the type. Decorative only
   (aria-hidden); --rizz-line still clears AA-large at display scale. */
.dot { color: var(--rizz-line); }

.ev-hero .lead {
  margin-top: clamp(1.5rem, 3.5vw, 2.25rem);
  max-width: 46ch;
}

/* Editorial contents rail */
.ev-index { margin-top: clamp(2.75rem, 8vw, 5rem); }

.ev-index a {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: 56px;                 /* comfortable tap target */
  padding: 0.55rem 0.15rem;
  border-top: 1px solid var(--line);
  font-family: var(--font-label);
  font-weight: 500;
  font-size: clamp(0.95rem, 1.6vw, 1.1rem);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--ivory-80);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease), border-color var(--dur-2) var(--ease);
}

.ev-index a:last-child { border-bottom: 1px solid var(--line); }

.ev-index-num {
  font-family: var(--font-logo);
  font-size: 1.4rem;
  line-height: 1;
  color: var(--ivory-60);
  min-width: 2ch;
  flex: none;
  transition: color var(--dur-1) var(--ease);
}

.ev-index-label { min-width: 0; }

.ev-index-arrow {
  margin-left: auto;
  flex: none;
  color: var(--smoke);
  transition: color var(--dur-1) var(--ease), transform var(--dur-2) var(--ease);
}

.ev-index a:hover { color: var(--ivory); border-top-color: var(--line-strong); }
.ev-index a:hover .ev-index-num { color: var(--ivory-80); }
.ev-index a:hover .ev-index-arrow { color: var(--rizz-hot); transform: translateX(7px); }

/* ---------- 02 Ticker band (full-bleed) ---------- */
.ev-ticker-band {
  border-block: 1px solid var(--line);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--rizz) 10%, transparent), transparent 70%),
    var(--bg-2);
  padding-block: clamp(1.1rem, 2.6vw, 1.6rem);
}

.ev-ticker { padding-inline: var(--gutter); }

/* No-JS floor: a centered, wrapping wall of brand facts. motion.js may
   swap .ev-ticker-track to a nowrap loop track (.mq-on / .mq-track). */
.ev-ticker-track {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  column-gap: clamp(1rem, 2.4vw, 2.1rem);
  row-gap: var(--sp-2);
}

.ev-ticker-track li {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-label);
  font-weight: 600;
  font-size: clamp(0.9rem, 1.9vw, 1.15rem);
  line-height: 1.3;
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--ivory-60);
  white-space: nowrap;
}

.ev-ticker-track li + li::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  margin-right: clamp(1rem, 2.4vw, 2.1rem);
  background: var(--rizz);
  transform: rotate(45deg);
}

/* ---------- 03 Shared page pieces ---------- */
.ev-display { font-size: var(--fs-display); text-wrap: initial; letter-spacing: -0.006em; }

.ev-head { margin-bottom: clamp(2.25rem, 5vw, 3.5rem); }

.ev-head .sect-lead { max-width: 46ch; }

/* Framed artifact — nested nodes so GSAP and CSS never fight
   over the same transform (see the ownership note at the top). */
.ev-artifact { display: block; }

.ev-artifact-tilt {
  --tilt: -1.1deg;
  transform: rotate(var(--tilt));
  transition: transform var(--dur-2) var(--ease);
}

.ev-artifact:hover .ev-artifact-tilt { transform: rotate(0deg) translateY(-6px); }

/* Fact spec sheet — hairline label / value ledger */
.spec { border-top: 1px solid var(--line); }

.spec-row {
  display: grid;
  grid-template-columns: minmax(6rem, 8.5rem) 1fr;
  gap: var(--sp-3) var(--sp-4);
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--line);
}

.spec--tight .spec-row { padding-block: 0.62rem; }

.spec dt {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--smoke);
  padding-top: 0.28em;
}

.spec dd {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--ivory-80);
}

/* Honesty / provenance microcopy */
.ev-note {
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--smoke);
  max-width: 48ch;
  padding-left: var(--sp-4);
  border-left: 2px solid var(--line-rizz);
}

.ev-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

.ev-cta-row--center { justify-content: center; }

/* Chapter ghost numeral — decorative, parallax-driven, never read.
   Sits fully inside the section so it never reads as a clipped shape;
   phone hides it to keep the small screen clean. */
.ev-ghost { display: none; }

@media (min-width: 768px) {
  .ev-ghost {
    display: block;
    position: absolute;
    top: clamp(1.5rem, 4vw, 3.5rem);
    right: var(--gutter);
    z-index: var(--z-bg);
    font-family: var(--font-logo);
    font-size: clamp(9rem, 17vw, 15rem);
    line-height: 0.8;
    letter-spacing: 0.01em;
    color: color-mix(in srgb, var(--ivory) 3.8%, transparent);
    pointer-events: none;
    user-select: none;
  }
}

/* ---------- 04 Permanent-page link ---------- */
.ev-arch { margin-top: clamp(1.75rem, 4vw, 2.5rem); }

/* Whole panel is the tap target (house Click Protocol) — it IS the link. */
.ev-arch-link {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: 64px;
  padding: clamp(1.1rem, 2.6vw, 1.6rem) clamp(1.25rem, 3vw, 2rem);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--rizz);
  border-radius: var(--r-sm);
  text-decoration: none;
  transition: border-color var(--dur-2) var(--ease),
              background var(--dur-2) var(--ease),
              transform var(--dur-2) var(--ease),
              box-shadow var(--dur-2) var(--ease);
}

.ev-arch-link:hover,
.ev-arch-link:focus-visible {
  background: var(--panel-2);
  border-color: var(--line-rizz);
  border-left-color: var(--rizz-hot);
  transform: translateY(-3px);
  box-shadow: var(--glow-rizz-soft);
}
.ev-arch-link:focus-visible { outline: 2px solid var(--rizz-hot); outline-offset: 3px; }

.ev-arch-kicker {
  flex: none;
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
  color: var(--rizz-text);
}

.ev-arch-title {
  min-width: 0;
  font-family: var(--font-label);
  font-weight: 500;
  font-size: clamp(1rem, 2vw, 1.2rem);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--ivory);
  line-height: 1.35;
}

.ev-arch-arrow {
  margin-left: auto;
  flex: none;
  font-size: 1.3rem;
  color: var(--smoke);
  transition: color var(--dur-1) var(--ease), transform var(--dur-2) var(--ease);
}

.ev-arch-link:hover .ev-arch-arrow { color: var(--rizz-hot); transform: translateX(7px); }

/* ---------- 05 Archive carousel + cards + lightbox (the centerpiece) ----------
   Base state (no class) is a NATIVE horizontal-scroll row: it works with
   zero JS AND is the reduced-motion floor (static, scrollable, tappable).
   events-carousel.js adds .is-live to switch to the rAF conveyor (drag +
   flick + tap-to-open). The cards carry .flyer-card for the shared frame
   chrome but NOT [data-tilt], so motion.js never grabs them — the track
   transform is owned solely by events-carousel.js.

   Slide widths are driven ENTIRELY by CSS (fixed --fh face height x a
   per-card aspect), never by intrinsic image size, so the conveyor's
   measure() pass is correct whether or not the flyer images have decoded
   yet — which is why the card images can stay loading="lazy". */

.ev-carousel {
  position: relative;
  margin-top: clamp(1.75rem, 4vw, 3rem);
  --fh: clamp(300px, 40vh, 440px);          /* card face height — equal across cards */
}

/* Edge fades as pointer-events:none overlays rather than a mask on the
   scroll container — a CSS mask on the transformed conveyor triggers a
   Chrome compositing bug where only the first card paints. */
.ev-carousel::before,
.ev-carousel::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: clamp(1.5rem, 4vw, 3.25rem);
  z-index: 2;
  pointer-events: none;
}
.ev-carousel::before { left: 0; background: linear-gradient(90deg, var(--bg), transparent); }
.ev-carousel::after  { right: 0; background: linear-gradient(270deg, var(--bg), transparent); }

.ev-carousel-viewport {
  overflow-x: auto;
  overflow-y: hidden;
  /* overflow-y can't be visible on a scroll container, so give the cards
     real vertical breathing room — the --shadow-2 glow plus the -6px hover
     lift clear before the viewport edge clips. */
  padding-block: clamp(1.75rem, 3.2vw, 3rem);
  padding-inline: var(--gutter);
  scrollbar-width: thin;
  scrollbar-color: var(--rizz) transparent;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}

.ev-carousel-viewport::-webkit-scrollbar { height: 6px; }
.ev-carousel-viewport::-webkit-scrollbar-thumb { background: var(--rizz); border-radius: var(--r-pill); }
.ev-carousel-viewport:focus-visible { outline: 2px solid var(--rizz-hot); outline-offset: -4px; }

/* Live conveyor: JS drives translateX, so kill native scroll + snap. */
.ev-carousel.is-live .ev-carousel-viewport {
  overflow: hidden;
  scroll-snap-type: none;
  touch-action: pan-y;             /* JS owns horizontal; page keeps vertical */
  cursor: grab;
}
.ev-carousel.is-live .ev-carousel-viewport.is-grab { cursor: grabbing; }

.ev-carousel-track {
  display: flex;
  align-items: stretch;
  gap: clamp(1rem, 2.4vw, 2rem);
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
}
/* NOTE: no `will-change: transform` here — on this masked/transformed
   conveyor it promotes a GPU layer that makes Chrome drop the paint of
   every card except the clipping-boundary one. translate3d in the JS
   already gives the track its own layer for smooth animation. */

.ev-slide {
  flex: 0 0 auto;
  display: flex;
  scroll-snap-align: center;
}

/* Card artifact button — whole card opens the detail lightbox. */
.ev-flyer {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  margin: 0;
  padding: var(--sp-4);
  text-align: left;
  appearance: none;
  -webkit-appearance: none;
  background: var(--flyer-ground);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-2);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: transform var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease),
              box-shadow var(--dur-2) var(--ease);
}

/* Card width = fixed face height x the card's aspect. For a real flyer the
   aspect is the FLYER'S OWN (so object-fit: contain letterboxes by ~0 and
   the poster is never cropped); the typographic card borrows --port.
     --p23  800 x 1200  (flyer-518-jul4)   = 0.6667
     --port 848 x 1200  (flyer-518-may16)  = 0.7067 ≈ 0.7072
   --land / --sq stay defined for future flyers in those formats. */
.ev-flyer--land { width: calc(var(--fh) * 1.4144 + 2 * var(--sp-4)); }
.ev-flyer--port { width: calc(var(--fh) * 0.7072 + 2 * var(--sp-4)); }
.ev-flyer--p23  { width: calc(var(--fh) * 0.6667 + 2 * var(--sp-4)); }
.ev-flyer--sq   { width: calc(var(--fh) * 1 + 2 * var(--sp-4)); }

/* Aspect classes for the archive flyer rows — each ratio is that flyer's
   own MEASURED pixel ratio (widths/heights recorded in
   data/flyer-archive.json and verified against the files by
   tools/build-archive.mjs), so object-fit: contain letterboxes by ~0:
     --a563   788x1400  (both Jamrock uprights)   = 0.5629
     --a714   999x1400  (SHOW THE WORLD)          = 0.7136
     --a750  1050x1400  (KNUCKLES BRIM)           = 0.7500
     --a807  1127x1396  (VINTAGE 96)              = 0.8073
   933x1400 / 932x1400 (0.6664 / 0.6657) ride --p23 (0.6667): the widest
   mismatch is 0.001 x --fh = 0.3px letterbox at the 300px row face —
   sub-pixel. 989x1400 / 991x1400 (0.7064 / 0.7079) ride --port (0.7072)
   the same way, and 1254x1254 is exactly --sq. */
.ev-flyer--a563 { width: calc(var(--fh) * 0.5629 + 2 * var(--sp-4)); }
.ev-flyer--a714 { width: calc(var(--fh) * 0.7136 + 2 * var(--sp-4)); }
.ev-flyer--a750 { width: calc(var(--fh) * 0.7500 + 2 * var(--sp-4)); }
.ev-flyer--a807 { width: calc(var(--fh) * 0.8073 + 2 * var(--sp-4)); }

/* ---------- 05a The double flyer wall (two counter-moving rows) ----------
   Two .ev-carousel--row instances stacked: the top row drifts rtl, the
   bottom ltr, breathing in anti-phase (phases 0 / 0.5) — the data-* motion
   contract lives in the js/events-carousel.js v6 header. Face height drops
   from the single-row clamp(300px, 40vh, 440px) so two rows read as one
   wall. MEASURED in-browser 2026-08-20: at 1280x800 --fh resolves to 240px
   and a full row (viewport padding + face + two-line meta + its own nav)
   is 503-524px, so the pair plus labels runs ≈ 1.1k px of section — one
   comfortable scroll. At 360x740 --fh is 222px (30vh), a --p23 card is
   ~174px wide and a row is 490px; at 768x1024 the 300px ceiling engages
   (rows 525-547px). No width from 360 to 1440 shows page-level horizontal
   overflow — document.scrollWidth == innerWidth at every step. */
.ev-carousel--row { --fh: clamp(200px, 30vh, 300px); margin-top: clamp(0.4rem, 1.2vw, 0.75rem); }
.ev-carousel--row .ev-carousel-viewport { padding-block: clamp(1.1rem, 2vw, 1.6rem); }
.ev-carousel--row .ev-carousel-nav { padding-top: 0; }
.ev-carousel--row .ev-car-btn { width: 40px; height: 40px; font-size: 1.35rem; }

/* Row captions — the stretch of the timeline each row carries, stated
   where sighted users can read it (each viewport's aria-label carries the
   same for assistive tech). The arrow glyph points the way that row's
   content actually travels: top row rtl (larr), bottom row ltr (rarr). */
.ev-row-label { margin-top: clamp(1.75rem, 3.5vw, 2.5rem); margin-bottom: 0; }
.ev-row-label .ev-row-dir { color: var(--rizz); }

.ev-flyer--featured { box-shadow: var(--shadow-2), var(--glow-rizz-soft); }

.ev-flyer:hover,
.ev-flyer:focus-visible {
  transform: translateY(-6px);
  border-color: var(--line-rizz);
  /* Deeper cast on the lift so the elevation reads. Raw neutral rgba
     matches the tokens.css shadow-1/-2 convention (a page-local shadow-3). */
  box-shadow: 0 24px 62px rgba(0, 0, 0, 0.6);
}
.ev-flyer--featured:hover,
.ev-flyer--featured:focus-visible {
  box-shadow: 0 24px 62px rgba(0, 0, 0, 0.6), var(--glow-rizz-soft);
}
.ev-flyer:focus-visible { outline: 2px solid var(--rizz-hot); outline-offset: 3px; }
.ev-carousel-viewport.is-grab .ev-flyer:hover { transform: none; }   /* no lift mid-drag */

/* --- Card face — either a REAL flyer (.ev-card--art) or, where no flyer
       still exists on disk, a typographic panel (.ev-card--type). A card
       never borrows another night's artwork. --- */
.ev-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: var(--fh);
  width: 100%;
  padding: clamp(0.9rem, 2vw, 1.25rem);
  overflow: hidden;
  border-radius: var(--r-xs);
  border: 1px solid var(--line);
  background: var(--bg);
}

.ev-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
}

/* PAST flag on a typographic card face — a status, not a disclaimer, so it
   takes a solid hairline rather than the dashed "reserved/held" language. */
.ev-card-past {
  display: inline-flex;
  padding: 0.28rem 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
  color: var(--ivory-60);
}

.ev-card-no {
  font-family: var(--font-logo);
  font-weight: 500;
  font-size: 1.1rem;
  letter-spacing: 0.14em;
  color: var(--rizz-text);
}

.ev-card-title {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 4.5vw, 2.6rem);
  line-height: 1.02;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--ivory);
}

.ev-card-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-2);
}

.ev-card-date {
  display: inline-flex;
  padding: 0.34rem 0.7rem;
  background: var(--rizz);              /* ivory on --rizz = 5.73:1 ✓ */
  color: var(--ivory);
  border-radius: var(--r-xs);
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
}

.ev-card-venue {
  font-family: var(--font-label);
  font-weight: 500;
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ivory-60);
  line-height: 1.35;
}

/* Flyer face — the real poster, edge to edge inside the archival frame.
   CONTAIN, never cover: the card's width is cut from this flyer's own
   aspect, so the poster fills the face without a pixel of crop. */
.ev-card--art {
  padding: 0;
  background: var(--bg);
}

.ev-card--art picture { display: block; width: 100%; height: 100%; }

/* Overrides site.css's generic .flyer-card img rule (width:100% + inner
   hairline) — inside the face the frame chrome belongs to .ev-card. */
.ev-card--art img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  border: 0;
  border-radius: 0;
}

/* Typographic face — used ONLY where no flyer still exists on disk.
   Reads as a set index card, not as a poster, so it can never be mistaken
   for the night's real artwork. */
.ev-card--type {
  background:
    radial-gradient(120% 70% at 100% 0%, var(--rizz-haze), transparent 60%),
    linear-gradient(200deg, color-mix(in srgb, var(--rizz-deep) 42%, transparent), transparent 55%),
    var(--bg);
}

/* Flyer strapline set as type, e.g. MUSIC · VIBES · ENERGY */
.ev-card-line {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
  color: var(--ivory-60);
}

/* Meta ledger below the face (reuses .flyer-meta / .flyer-tag from site.css)
   CINDY 2026-08-17 — ONE MEANING PER TREATMENT. This badge was dashed while
   .ev-card-past above carries the identical "Past" status on a SOLID hairline,
   for the reason stated there: dashed is this page's reserved/held language and
   the only thing genuinely held open is .next-slot-inner. A dashed frame around
   "Past · Jul 4, 2026" rhymed the finished archive with the TBA slot. Solid
   now, so dashed means exactly one thing on the page. */
.ev-flyer-badge {
  display: inline-flex;
  align-self: flex-start;
  padding: 0.3rem 0.62rem;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ivory-60);
  border-radius: var(--r-xs);
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
}

.ev-flyer-sub { font-size: var(--fs-small); color: var(--smoke); }

/* Prev / next controls */
.ev-carousel-nav {
  position: relative;
  z-index: 3;                        /* above the edge-fade overlays */
  display: flex;
  gap: var(--sp-3);
  justify-content: flex-end;
  padding: var(--sp-4) var(--gutter) 0;
}
.ev-car-btn {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--panel);
  color: var(--ivory);
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease),
              background var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease);
}
.ev-car-btn:hover { border-color: var(--rizz); background: var(--panel-2); }
.ev-car-btn:focus-visible { outline: 2px solid var(--rizz-hot); outline-offset: 2px; }
.ev-car-btn:active { transform: translateY(1px); }
/* .ev-car-btn sets display:flex, which would override the UA [hidden] rule —
   restore it so the JS-revealed pause/play control stays hidden until live. */
.ev-car-btn[hidden] { display: none; }

.ev-flyers-note {
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
  font-size: var(--fs-small);
  line-height: 1.65;
  color: var(--smoke);
  max-width: 62ch;
}

/* ---------- 05b Lightbox ----------
   The full-size flyer is authored INSIDE each slide's [data-flyer-detail]
   block, not in a shared [data-lightbox-img] pane. events-carousel.js clones
   that block wholesale, so a card with no flyer on file simply arrives with
   no image — it can never inherit the previously opened card's artwork. */
.ev-lightbox {
  position: fixed;
  inset: 0;
  z-index: 900;                    /* above the 800 header */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 2.5rem);
}
.ev-lightbox[hidden] { display: none; }

body.lb-open { overflow: hidden; }

.ev-lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5, 5, 6, 0.86);      /* neutral scrim — allowed literal */
  -webkit-backdrop-filter: blur(4px);
          backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease);
}
.ev-lightbox.is-open .ev-lightbox-backdrop { opacity: 1; }

.ev-lightbox-panel {
  position: relative;
  z-index: 1;
  width: min(100%, 640px);
  max-height: calc(100vh - 2 * clamp(1rem, 4vw, 2.5rem));
  overflow-y: auto;
  padding: clamp(1.25rem, 3vw, 2rem);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  opacity: 0;
  transform: translateY(12px) scale(0.985);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.ev-lightbox.is-open .ev-lightbox-panel { opacity: 1; transform: none; }

.ev-lightbox-close {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  z-index: 2;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: color-mix(in srgb, #000 45%, var(--panel));
  color: var(--ivory);
  font-size: 1.7rem;
  line-height: 1;
  cursor: pointer;
}
.ev-lightbox-close:hover { border-color: var(--rizz); }
.ev-lightbox-close:focus-visible { outline: 2px solid var(--rizz-hot); outline-offset: 2px; }

.ev-lightbox-body > * + * { margin-top: clamp(1rem, 2.5vw, 1.5rem); }

/* CINDY 2026-08-17 — CENTRED ARTIFACT. The close-control clearance used to be
   a padding-right on the whole .ev-lightbox-body, which inset EVERY child by
   36px on one side only: the flyer figure sat 21px from the left wall and 57px
   from the right inside a 358px dialog, and all nine .spec-row hairlines
   stopped short of the right margin. The control is 44px in the TOP-RIGHT
   corner, so only the first block of the cloned detail can ever collide with
   it — give the clearance to that block alone and the flyer, the ledger rules
   and the CTA row all sit square in the panel. */
.ev-lightbox-detail > :first-child { padding-right: 2.75rem; }

.ev-lb-badge {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
  /* --ivory-60, not --rizz-text: this 13px label sits on the lighter
     --panel lightbox ground where cobalt copy loses AA headroom. */
  color: var(--ivory-60);
}

.ev-lb-title { font-size: var(--fs-h3); margin-top: var(--sp-2); }

.ev-lb-venue { font-size: var(--fs-small); color: var(--smoke); margin-top: var(--sp-2); }

.ev-lb-copy { color: var(--ivory-80); font-size: var(--fs-small); }

/* Full-size flyer inside the panel. CONTAIN + a viewport ceiling so a tall
   poster never pushes its own facts off the bottom of the dialog. */
.ev-lb-fig {
  margin: 0;
  padding: var(--sp-3);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
}

.ev-lb-fig picture { display: block; }

.ev-lb-fig img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 58vh;
  object-fit: contain;
  margin-inline: auto;
  border: 0;
  border-radius: 0;
}

.ev-lb-cap {
  margin-top: var(--sp-3);
  font-size: var(--fs-micro);
  letter-spacing: 0.03em;
  color: var(--ivory-60);           /* 13px — above the smoke-grey floor */
  text-align: center;
}

.ev-lb-note {
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--smoke);
  padding-left: var(--sp-4);
  border-left: 2px solid var(--line-rizz);
}

.ev-lb-link a {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-small);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--rizz-text);
  text-decoration: none;
  border-bottom: 1px solid var(--line-rizz);
  padding-bottom: 0.15em;
}
.ev-lb-link a:hover { color: var(--ivory); border-bottom-color: var(--rizz-hot); }

/* Spacing rhythm inside the cloned detail block */
.ev-lightbox-detail > * + * { margin-top: clamp(0.9rem, 2.2vw, 1.25rem); }

.ev-lightbox-cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

/* Reduced motion: the base native-scroll row IS the experience (JS never
   adds .is-live), plus a hard belt-and-suspenders reset. */
@media (prefers-reduced-motion: reduce) {
  .ev-carousel-track { transform: none !important; }

  /* REDUCED-MOTION SEAT 2026-08-17 — STRANDED FLYER.
     The reset above is correct on a FRESH reduced-motion load: no .is-live,
     so the native-scroll row below is untouched. It was NOT correct on a LIVE
     flip of the OS setting, which motion.js explicitly supports without a
     reload. A conveyor that was already running keeps .is-live — whose
     viewport is `overflow: hidden` because JS owns horizontal movement — and
     events-carousel.js keeps writing the very transform channel the rule
     above pins to none. Net effect measured at 500px client width: the track
     froze at x=0 with `overflow: hidden`, drag dead, and prev/next writing to
     a pinned channel, so 1 of the 3 real flyer cards sat off-canvas with no
     way to reach it. (At 1440px all three happen to fit, which is why this
     hid on desktop.) Hand horizontal movement back to native scroll and drop
     the conveyor's duplicate clone sets, so the frozen track degrades to
     exactly the same reachable 3-card row a fresh reduced-motion load gets.
     Both selectors are no-ops on a fresh load — .is-live and .is-clone only
     ever exist once the conveyor has run. */
  .ev-carousel-track > .is-clone { display: none; }

  .ev-carousel.is-live .ev-carousel-viewport {
    overflow-x: auto;
    scroll-snap-type: x proximity;
    touch-action: auto;
    cursor: auto;
  }
}

/* ---------- 06 From the room — recap film + coverage strip ----------
   The recap clip is a CONTENT video (data/media.json med-recap-518-01,
   has_audio: true): native controls, no autoplay, never a [data-vslot]
   wallpaper. It therefore also sits OUTSIDE .vslot, so site.css's
   reduced-motion `.vslot video { display: none }` can't hide it — a
   user-initiated video stays available at every motion preference. */
.ev-film {
  max-width: 760px;
  margin-inline: auto;
  margin-bottom: clamp(2.25rem, 5vw, 3.25rem);
}

.ev-film-video {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;             /* 1280 x 960 source — reserves the box, no CLS */
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
}

.ev-film-cap {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--smoke);
  text-align: center;
}

/* Coverage strip. This is the ONE cover-cropped surface on the page, so
   every tile carries its measured face-safe object-position below. */
.ev-shots {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Lead frame reads as the establishing shot on narrow screens. */
.ev-shots > li:first-child { grid-column: span 2; }

/* CINDY 2026-08-17 — ASPECT INTEGRITY. There used to be a 3-column rule here
   for 640–979.98px. With the lead still spanning 2 columns but only ONE row,
   it set the first grid row's height from the 2-wide lead (462 x 309 at 768)
   and stretched the single tile beside it to that same row height at 1/3 the
   width: .ev-shot--02 rendered 225 x 309 — a 0.73 PORTRAIT box holding a
   1600x1063 landscape frame, so cover threw away over half the picture, and
   its measured face-safe object-position (77.5% 20.2%, derived for a 3:2 slot)
   dragged the subjects toward the edge. Four siblings stayed 3:2, so one photo
   in five was a different shape. Dropping the rule lets 0–979.98px keep the
   two-column composition below (lead full width, four 3:2 supports in a 2x2)
   — every tile on one aspect, no stretched row, and the supports grow from
   169px to ~343px wide on tablet. The 4-column desktop layout is unchanged. */

/* JUAN 2026-08-17 — HIERARCHY. This was five identical 246x165 tiles at
   1440: a contact sheet, not an edit. On a page whose whole argument is
   "this is what the nights actually look like", the crew's own coverage
   was the smallest visual object in the section — smaller than the caption
   under it. Four columns with a 2x2 lead gives the establishing frame
   634x426 and the supports 311x207, and every tile still lands on the same
   3:2 so the block reads as one composition rather than a ragged collage.
   (CINDY: the lead needs BOTH spans here — dropping grid-row is what
   produced the stretched tile documented above.) */
@media (min-width: 980px) {
  .ev-shots {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--sp-4);
  }
  .ev-shots > li:first-child {
    grid-column: span 2;
    grid-row: span 2;
  }
}

/* ::before sets the 3:2 floor; the picture fills absolutely so a tile that
   grid-stretch makes taller than its neighbours still has no dead gap. */
.ev-shot {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel);
}

.ev-shot::before { content: ""; display: block; padding-top: 66.667%; }

.ev-shot picture { position: absolute; inset: 0; }

.ev-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-3) var(--ease);
}

/* FACE-SAFE CROP LAW — measured focal points (data/media.json `focal`).
   Never place one of these frames in a cover slot without its value.
   event-518-05 is deliberately NOT on this page (see the note in the
   baton): the subjects' hand gesture is wrong for a partnership page. */
.ev-shot--01 img { object-position: 41.3% 15.4%; }
.ev-shot--02 img { object-position: 77.5% 20.2%; }
.ev-shot--03 img { object-position: 39.3% 36.5%; }
.ev-shot--04 img { object-position: 52.1% 24.1%; }
/* ev06 reads 21.7%, not the 42.1% centroid: it was re-derived so the
   SQUARE tile on services.html stops halving a third face. See the note in
   css/pages/services.css. This 3:2 slot crops <0.5% horizontally, so the
   change is invisible here — the registry keeps one focal per asset. */
.ev-shot--06 img { object-position: 21.7% 11.5%; }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .ev-shot:hover img { transform: scale(1.03); }
}

.ev-shots-note { margin-top: clamp(1.5rem, 3.5vw, 2.25rem); }

/* ---------- 07 Next date — honest held slot ---------- */
.ev-next-sect {
  /* position + clip so §03 can carry the parallax ghost numeral —
     the chaptering device stays a system, not a one-off. */
  position: relative;
  overflow: clip;                   /* clip NOT hidden — house law */
}

.ev-next {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: center;
}

@media (min-width: 980px) {
  .ev-next { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
}

.ev-next-body > * + * { margin-top: clamp(1.5rem, 3.5vw, 2.25rem); }

.ev-next-body .lead { max-width: 42ch; }

.ev-next-body .ev-cta-row { margin-top: clamp(1.75rem, 4vw, 2.5rem); }

.ev-steps {
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top: 1px solid var(--line);
  max-width: 42ch;
}

.ev-steps li {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-label);
  font-weight: 500;
  font-size: 1.02rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ivory-80);
}

.ev-steps-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border: 1px solid var(--line-rizz);
  border-radius: 50%;
  font-family: var(--font-logo);
  font-size: 0.95rem;
  line-height: 1;
  color: var(--ivory-60);
}

/* The held frame — artifact language, waiting for Vol. 002's flyer */
.ev-artifact--slot {
  width: 100%;
  max-width: 380px;
  margin-inline: auto;
}

.ev-artifact--slot .ev-artifact-tilt { --tilt: -1.5deg; }

.next-slot {
  padding: var(--sp-4);
  /* Reserved slot reads as identical material to the board cards:
     the shared --flyer-ground token drives both. */
  background: var(--flyer-ground);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  /* A whisper of cobalt bloom so the reserved slot reads as held-open
     and charged, not a dead dashed frame. Single accent. */
  box-shadow: var(--shadow-2), var(--glow-rizz-soft);
}

.next-slot-inner {
  aspect-ratio: 3 / 4;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-xs);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-5);
  text-align: center;
}

.next-slot-bar {
  width: 28px;
  height: 2px;
  background: var(--rizz);
}

.next-slot-word {
  font-family: var(--font-logo);
  font-size: clamp(2.6rem, 7vw, 4rem);   /* "002" runs wider than "Next" */
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ivory);
}

.next-slot-label {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: var(--ls-eyebrow);
  color: var(--smoke);
}

/* ---------- 08 Venue band + atmosphere slot ----------
   Page-local cobalt haze layered as a background on the band itself
   (site.css `.haze::before` paints at z-index -1, which a `.sect--band`
   background would cover). The ivory lift settles back to --bg-2 by 64%
   so the footer handoff stays seamless. Background only — .sect--band's
   hairlines are preserved. */
.ev-venue {
  position: relative;
  overflow: clip;                   /* carries the §04 parallax ghost */
  background:
    radial-gradient(72% 62% at 50% 0%, var(--rizz-haze), transparent 72%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--ivory) 5%, transparent),
      color-mix(in srgb, var(--ivory) 1.5%, transparent) 64%,
      transparent),
    var(--bg-2);
}

.ev-venue .sect-head { margin-bottom: clamp(2rem, 4vw, 2.75rem); }

.ev-venue .sect-lead { max-width: 52ch; }

/* Framed atmosphere clip — poster-first [data-vslot]; honest caption.
   Silent, client-owned club b-roll (data/media.json med-club-vertical-01,
   has_audio: false) — the only kind of clip allowed in a decorative slot.
   The source is 720 x 1280, so the frame is 9:16: the clip is shown in its
   own shape rather than cover-cropped into a landscape band. Its `focal` is
   50% 50% (crop-safe), which matching aspect ratios make moot. */
.ev-atmo {
  margin-bottom: clamp(2.25rem, 5vw, 3.25rem);
}

.ev-atmo-slot {
  width: 100%;
  max-width: 300px;
  margin-inline: auto;
  aspect-ratio: 9 / 16;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
}

.ev-atmo-note {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--smoke);
}

.ev-venue-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* Three stats must share one row inside the --w-narrow container:
     3 × 13rem + 2 × ~3.5rem ≈ 46rem < 51.25rem — no accidental wrap. */
  gap: var(--sp-7) clamp(2rem, 5vw, 3.5rem);
  margin-top: clamp(3rem, 7vw, 5rem);
  padding-top: clamp(2.5rem, 6vw, 4rem);
  border-top: 1px solid var(--line);
}

.ev-venue-stats .stat {
  text-align: left;
  align-items: flex-start;
  max-width: 13rem;
}

/* One step down from --fs-stat keeps the three stat blocks on a shared
   baseline inside the narrow container without wrapping. */
.ev-venue-stats .stat-num { font-size: clamp(2rem, 4.5vw, 3.4rem); }

.ev-venue-sign {
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
  font-family: var(--font-label);
  font-weight: 500;
  font-size: clamp(1rem, 2vw, 1.2rem);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  color: var(--ivory-60);
}

/* ---------- 09 Small-screen floor (360) ---------- */
@media (max-width: 559.98px) {
  .ev-venue-stats { flex-direction: column; align-items: center; gap: var(--sp-6); }
  .ev-venue-stats .stat { text-align: center; align-items: center; max-width: 22rem; }
  .ev-arch-link { flex-wrap: wrap; row-gap: var(--sp-2); }
  .ev-arch-kicker { flex-basis: 100%; }
  /* CINDY 2026-08-17 — TIGHT FIT. flex-wrap breaks lines from each item's
     HYPOTHETICAL size (flex-basis: auto = max-content) before any shrinking
     happens, so the long title claimed the whole second row and pushed the
     arrow onto a third one: a 152px-tall panel with a dead right column, x3.
     Basis 0 lets the title share its row with the arrow and then grow into
     the leftover — panel drops to ~108px and the arrow lands where it reads
     as this row's affordance. */
  .ev-arch-title { flex: 1 1 0; }
}

@media (max-width: 420px) {
  .spec-row { grid-template-columns: 1fr; gap: 0.25rem; }
  .spec dt { padding-top: 0; }
  .ev-index a { gap: var(--sp-4); }
  .ev-steps li { font-size: 0.94rem; letter-spacing: 0.06em; }
  .ev-note { padding-left: var(--sp-3); }
}
