/* =============================================================================
   Collabo-Rive 1 — styles
   ============================================================================ */

:root {
  --bg:        #0c0c0f;
  --bg-soft:   #141419;
  --fg:        #f4f4f1;
  --fg-dim:    #8a8a93;
  --fg-faint:  #54545c;
  --accent:    #4fff84;          /* highlight color for the active artist */
  --accent-2:  #ff5a3c;
  --line:      rgba(255,255,255,0.09);
  --stage-bg:  #08080a;

  --maxw:      1200px;
  --pad:       clamp(20px, 5vw, 64px);
  --ease:      cubic-bezier(0.22, 1, 0.36, 1);

  --font-display: "Sora", "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
  --font-body:    "Noto Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

a { color: inherit; }

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85em;
  background: rgba(255,255,255,0.07);
  padding: 0.1em 0.4em;
  border-radius: 4px;
}

/* -----------------------------------------------------------------------------
   Scroll container + snapping
   The whole experience scrolls inside .scroller so the snap behaviour is
   self-contained — important for embedding inside Framer / an iframe.
   --------------------------------------------------------------------------- */
.scroller {
  height: 100vh;
  height: 100dvh;
  overflow-y: scroll;
  overflow-x: hidden;
  scroll-snap-type: y mandatory;    /* fallback snap when JS/Lenis is unavailable */
  scroll-behavior: smooth;
  scrollbar-width: none;            /* Firefox */
}
.scroller::-webkit-scrollbar { width: 0; height: 0; }

/* When our JS smooth-scroll is running it owns the inertia + snapping, so hand
   CSS snap off to it (main.js adds .is-smooth once Lenis is initialised). */
.scroller.is-smooth {
  scroll-snap-type: none;
  scroll-behavior: auto;
}
.scroller.is-smooth .scroller__content { min-height: 100%; }

/* Every full-bleed panel snaps to the viewport. Center, not start — when a
   panel is taller than the viewport (e.g. a caption pushed things down at
   an in-between window size), this splits the overflow evenly off the top
   and bottom edges instead of dumping all of it below the fold. Sections
   that fit the screen look identical either way. */
.hero,
.experience,
.footer {
  scroll-snap-align: center;
  scroll-snap-stop: always;        /* land on each section, "stay briefly" */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  position: relative;
}

/* -----------------------------------------------------------------------------
   Hero
   --------------------------------------------------------------------------- */
.hero {
  background:
    radial-gradient(120% 80% at 50% 0%, var(--bg-soft) 0%, var(--bg) 60%);
  justify-content: center;
  text-align: center;
}
.hero__inner {
  max-width: 880px;
  padding: var(--pad);
}
.hero__eyebrow {
  margin: 0 0 1.2rem;
  font-size: clamp(0.8rem, 1.4vw, 1rem);
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--accent);
}
.hero__title {
  margin: 0 0 1.4rem;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 0.95;
  font-size: clamp(3rem, 12vw, 9rem);
}
.hero__intro {
  margin: 0 auto;
  max-width: 46ch;
  font-size: clamp(1.05rem, 2.2vw, 1.45rem);
  color: var(--fg-dim);
  line-height: 1.55;
}
.hero__scrollcue {
  position: absolute;
  left: 50%;
  bottom: clamp(24px, 5vh, 48px);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  color: var(--fg-faint);
  font-size: 0.7rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
}
.hero__scrollcue-dot {
  animation: scrollcue 1.8s var(--ease) infinite;
}
@keyframes scrollcue {
  0%   { transform: translateY(0);   opacity: 1; }
  70%  { transform: translateY(7px); opacity: 0; }
  100% { transform: translateY(0);   opacity: 0; }
}

/* -----------------------------------------------------------------------------
   Experience section
   --------------------------------------------------------------------------- */
.experience {
  padding: clamp(64px, 10vh, 120px) var(--pad);
}
.experience__layout {
  /* Width is driven by the stage's aspect ratio AND the available height, so
     header + stage + names all share one width and line up edge-to-edge. */
  width: min(100%, calc((82vh - 200px) * var(--ar-num, 1.7778)));
  max-width: var(--maxw);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: clamp(18px, 3vh, 34px);
}

/* Header — group label + themed title, top-left above the stage. */
.experience__header { text-align: left; }
.experience__group {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1rem, 1.8vw, 1.4rem);
  color: var(--fg-dim);
  letter-spacing: 0.01em;
}
.experience__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1;
  font-size: clamp(1.7rem, 4vw, 3rem);
  color: var(--fg);
}
.experience__title::before { content: "\201C"; }
.experience__title::after  { content: "\201D"; }

/* Stage — front & center. Keeps its aspect ratio AND never overflows the
   viewport vertically (width is capped by available height via --ar-num). */
.experience__stage-wrap {
  min-width: 0;
  display: flex;
  justify-content: center;
}
.experience__stage {
  position: relative;
  aspect-ratio: var(--ar, 16 / 9);     /* set per-experience by main.js */
  width: 100%;                         /* layout already caps the width */
  background: var(--stage-bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 30px 80px -40px rgba(0,0,0,0.9);
}
.experience__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* Placeholder shown until a real .riv is supplied (or if load fails). */
.experience__placeholder:not([hidden]) {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  text-align: center;
  padding: 1.5rem;
  background:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,0.02) 0 14px,
      rgba(255,255,255,0.04) 14px 28px);
  color: var(--fg-dim);
}
.experience__placeholder-tag {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 3vw, 2.4rem);
  color: var(--fg);
}
.experience__placeholder-note { font-size: 0.9rem; color: var(--fg-faint); }

/* Artist row — names + handles spread across the bottom, under the stage.
   A fixed-column grid (not flex + space-between) so every row — even an
   uneven last one — stays left-aligned under the columns above it, instead
   of space-between stretching a short row out to the far edges. */
.artists {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* start, not the stretch default — a taller row-mate (its caption open)
     should never stretch/shift a sibling's own name+handle block. */
  align-items: start;
  gap: clamp(14px, 2vh, 22px) clamp(20px, 4vw, 56px);
  transition: padding-bottom 0.3s var(--ease);
}
/* Fallback reservation for the instant before main.js's precise, MEASURED
   padding-bottom (applyRowActive) takes over — captions have no fixed cap,
   so the real amount needed varies per caption; JS sets it as an inline
   style once layout has run, which always wins over this rule. */
.artists:has(.artist__did:not(:empty)) { padding-bottom: 2.6em; }
.artist { min-width: 0; }
.artist__link {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  text-decoration: none;
  text-align: left;
  color: inherit;
  transition: transform 0.3s var(--ease);
  /* Long unbreakable strings (URLs like "anakuster.com", handles like
     "@heisenbergg") have no natural wrap point — without this they
     overflow straight past their own grid column into the next one at
     narrow widths, instead of wrapping. */
  overflow-wrap: break-word;
}
.artist__index { display: none; }       /* no numbering in this layout */
.artist__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1rem, 1.5vw, 1.25rem);
  line-height: 1.1;
  color: var(--fg-dim);
  transition: color 0.3s var(--ease);
}
.artist__handle {
  font-size: clamp(0.82rem, 1.2vw, 0.95rem);
  color: var(--fg-faint);
  transition: color 0.3s var(--ease);
}

/* Hover (pointer devices only) */
@media (hover: hover) {
  .artist__link:hover .artist__name { color: var(--fg); }
}

/* ACTIVE artist — driven by the Rive state-machine number input. */
.artist.is-active .artist__name   { color: var(--accent); }
.artist.is-active .artist__handle { color: var(--accent); }

/* Credit line — used by group-shot experiences (e.g. autoCycle) to say what
   that artist contributed. Empty when unused, so it takes no space.
   No fixed cap — captions grow to whatever their real content needs. A
   plain height transition can't animate TO "auto" (browsers can't
   interpolate toward "however tall the content ends up being", so it just
   snaps) — animating grid-template-rows 0fr→1fr instead is the standard
   trick to get a smooth reveal to an unknown/auto height. .artist__did-box
   is the animated grid track; .artist__did is the inner content, clipped
   until its row opens up.
   Reserved space is controlled per ROW (.row-active, set in JS on every
   artist that shares a row with the active one — see applyRowActive in
   main.js), not per item. That's what lets a visitor move between two
   names in the same row with zero dip: both already reserve the space, so
   only their text opacity crossfades. */
.artist__did-box {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.3s var(--ease), opacity 0.3s var(--ease);
}
.artist.row-active .artist__did-box { grid-template-rows: 1fr; }
.artist.is-active .artist__did-box { opacity: 1; }
.artist__did {
  display: block;
  min-height: 0;
  overflow: hidden;
  font-size: clamp(0.8rem, 1.1vw, 0.9rem);
  line-height: 1.3;
  color: var(--fg-faint);
}

/* -----------------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------------- */
.footer {
  flex-direction: column;
  justify-content: center;
  text-align: center;
  gap: 0.6rem;
  color: var(--fg-dim);
  background: var(--bg-soft);
}
.footer__mark {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.4rem, 4vw, 2.2rem);
  color: var(--fg);
}
.footer__by { margin: 0; font-size: 0.9rem; color: var(--fg-faint); }
.footer__by a { color: var(--accent); text-decoration: none; }

/* -----------------------------------------------------------------------------
   Side rail (progress dots)
   --------------------------------------------------------------------------- */
.rail {
  position: fixed;
  top: 50%;
  right: clamp(14px, 2vw, 28px);
  transform: translateY(-50%);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.rail__dot {
  --size: 9px;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background: var(--fg-faint);
  border: 1px solid transparent;
  transition: background 0.3s var(--ease), transform 0.3s var(--ease);
  position: relative;
}
.rail__dot.is-current {
  background: var(--accent);
  transform: scale(1.35);
}
.rail__dot::after {
  content: attr(data-label);
  position: absolute;
  right: calc(100% + 12px);
  top: 50%;
  transform: translateY(-50%) translateX(6px);
  white-space: nowrap;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--fg-dim);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
@media (hover: hover) {
  .rail__dot:hover::after { opacity: 1; transform: translateY(-50%) translateX(0); }
}

/* -----------------------------------------------------------------------------
   Responsive — stack the stage above the credits on narrow screens.
   --------------------------------------------------------------------------- */
@media (max-width: 860px) {
  .scroller { scroll-snap-type: y proximity; } /* gentler on touch */

  .experience { padding: clamp(48px, 9vh, 80px) var(--pad); }
  .experience__layout {
    width: 100%;                 /* on phones, height isn't the constraint */
    gap: clamp(18px, 4vh, 30px);
  }
  .experience__stage { width: 100%; }
  .artists { gap: 18px 16px; }
  .rail { display: none; }
}

@media (max-width: 520px) {
  .experience__stage { border-radius: 8px; }
  .artists { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Respect reduced-motion preferences. */
@media (prefers-reduced-motion: reduce) {
  .scroller { scroll-behavior: auto; scroll-snap-type: none; }
  .hero__scrollcue-dot { animation: none; }
  * { transition-duration: 0.001ms !important; }
}
