/* ============================================================
   PARRANDA MECÁNICA · design system
   A lava-black room. The community is the subject; the machine
   hums underneath it, drawn in cold white hairlines, with amber
   used only where something should catch the light.
   ============================================================ */

:root {
  /* lava black: not a true black. It keeps a warm volcanic cast
     that the amber sits inside instead of on top of. */
  --bg: #22201a;
  /* the page ink: a cold white, deliberately not the amber. Type
     is white; amber is a spark, not a coat of paint. */
  --ink: #e6eced;
  /* long-form copy: the same white, stepped back so paragraphs
     don't compete with display type and labels. */
  --body: rgba(230, 236, 237, 0.74);
  --mute: rgba(230, 236, 237, 0.5);
  --hair: rgba(230, 236, 237, 0.16);
  /* hairline strokes for the technical drawings (gear, turntable) */
  --line: rgba(230, 236, 237, 0.85);
  --line-soft: rgba(230, 236, 237, 0.38);
  --font: "Inter Tight", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: "IBM Plex Mono", "SF Mono", Menlo, Consolas, monospace;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* the brand amber: "Mecánica", the spark in the A, buttons,
     hovers. Slightly deeper than the old #f99d1c so it reads as
     ember rather than highlighter against the lava. */
  --accent: #e08a15;
  --accent-hi: #f5a52f;
  /* the second brand color: the single hottest point on the page */
  --brand2: #d2410a;
}

/* Per-artist palettes, reduced to a background wash only. The site
   runs on one brand palette — names and links stay white, the amber
   is reserved for the wordmark and single accents — and each artist
   keeps a faint field of their own colour behind the type, enough to
   tell the chapters apart without breaking the palette. Each one
   dissolves straight to transparent, which over lava black reads as
   a glow rather than a stain. */
.dj-arfro    { --field: radial-gradient(closest-side, #a06bff 0%, transparent 76%); }
.dj-annita   { --field: radial-gradient(closest-side, #3fc1c9 0%, transparent 76%); }
.dj-zagato   { --field: radial-gradient(closest-side, #93a2b5 0%, transparent 76%); }
.dj-naya     { --field: radial-gradient(closest-side, #ff8f7a 0%, transparent 76%); }
.dj-ladyl    { --field: radial-gradient(closest-side, #34c77f 0%, transparent 76%); }
.dj-azafran  { --field: radial-gradient(closest-side, #d8b45c 0%, transparent 76%); }

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  background: var(--bg);
  scrollbar-width: thin;
  scrollbar-color: rgba(230, 236, 237, 0.45) transparent;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  font-weight: 380;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

::selection { background: var(--accent); color: var(--bg); }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* language switching: html[data-lang] decides which copy exists */
html[data-lang="en"] .es { display: none !important; }
html[data-lang="es"] .en { display: none !important; }

/* smooth-scroll wrapper (JS moves it, body keeps the height) */
.pm-smooth body, body.pm-smooth-on { overflow-y: hidden; }
#pm-content.is-fixed {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  will-change: transform;
}

/* Grain: a quiet mechanical texture. The noise tile is bright, so
   over the lava black it screens up into a fine ash rather than
   multiplying down into nothing. */
.grain {
  position: fixed;
  inset: -50%;
  width: 200%; height: 200%;
  pointer-events: none;
  z-index: 60;
  opacity: 0.055;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------
   cursor: a 7px amber point instead of the system arrow. It sits
   above everything, never takes a hit, and is only ever created on
   fine pointers (see main.js) — .pm-cursor-on is what hides the
   native cursor, so a device without one keeps its own.
   ------------------------------------------------------------ */
.pm-cursor-on,
.pm-cursor-on * { cursor: none !important; }

.pm-cursor {
  position: fixed;
  top: 0; left: 0;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent-hi);
  pointer-events: none;
  z-index: 999;
  opacity: 0;
  will-change: transform;
  transition: opacity 0.25s var(--ease), width 0.28s var(--ease),
              height 0.28s var(--ease), background 0.28s var(--ease);
}
.pm-cursor.is-on { opacity: 1; }

/* the wake: sits just under the ball, never over the header */
.pm-cursor-trail {
  position: fixed;
  top: 0; left: 0;
  pointer-events: none;
  z-index: 998;
}

/* over a link the point opens up into a ring: same 7px core idea,
   just enough growth to read as "this is clickable" */
.pm-cursor.is-hot { width: 22px; height: 22px; background: rgba(245, 165, 47, 0.32); }
.pm-cursor.is-down { width: 5px; height: 5px; background: var(--ink); }

/* a pointer that can't be seen is worse than the system one, so any
   viewer who asked for reduced motion keeps the native arrow */
@media (prefers-reduced-motion: reduce) {
  .pm-cursor { transition: none; }
}

/* ------------------------------------------------------------
   header: a technical ruler. Mono type, indexed entries,
   a scrim instead of a bar so the lava stays unbroken.
   ------------------------------------------------------------ */
.pm-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(20px, 4vw, 60px);
  padding: 18px clamp(20px, 4vw, 56px) 16px;
  color: var(--ink);
}

.pm-header::before {
  content: "";
  position: absolute;
  inset: 0 0 -70%;
  background: linear-gradient(to bottom, rgba(34, 32, 26, 0.96), rgba(34, 32, 26, 0.72) 40%, transparent);
  pointer-events: none;
  z-index: -1;
}

.pm-mark {
  display: flex;
  align-items: center;
  gap: 14px;
}

.pm-mark .gear-slot {
  width: 36px; height: 36px;
  display: block;
  flex: none;
}

.pm-home { display: block; line-height: 0; }

.pm-wordmark {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--ink);
}

/* the section marker: plain text now, matching the home page's own
   wordmark exactly. No pill, no fill, same color, same weight. */
.pm-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3em;
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--ink);
  transition: opacity 0.4s var(--ease);
}
.pm-badge:hover { opacity: 0.7; }

.pm-nav {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: clamp(22px, 4vw, 58px);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.pm-nav a {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  opacity: 0.72;
  transition: opacity 0.4s var(--ease);
}
.pm-nav a:hover { opacity: 1; }
/* the section you are already in */
.pm-nav a.is-current {
  opacity: 1;
  border-bottom: 1px solid var(--hair);
  padding-bottom: 3px;
}
.pm-nav a sup {
  font-size: 8px;
  color: var(--mute);
  letter-spacing: 0;
}

.lang-switch {
  display: flex;
  gap: 2px;
  font-family: var(--mono);
  border: 1px solid var(--hair);
  border-radius: 2px;
  padding: 2px;
}
.lang-switch button {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.45;
  padding: 4px 8px;
  border-radius: 1px;
  transition: opacity 0.4s var(--ease), background 0.4s var(--ease);
}
.lang-switch button.active {
  opacity: 1;
  background: rgba(224, 138, 21, 0.2);
  color: var(--accent-hi);
}
.lang-switch span { display: none; }

/* The nav used to be hidden on phones, which was survivable when it
   only jumped between anchors on one long page. It is now the only
   route between Community and Booking, so it has to stay reachable —
   it shrinks instead of disappearing, and the index numbers are the
   first thing to go. */
@media (max-width: 720px) {
  .pm-header {
    grid-template-columns: auto auto;
    grid-template-areas: "mark lang" "nav nav";
    row-gap: 12px;
    justify-content: space-between;
  }
  .pm-mark { grid-area: mark; }
  .lang-switch { grid-area: lang; }
  .pm-nav {
    grid-area: nav;
    justify-content: flex-start;
    gap: 20px;
    font-size: 10px;
  }
  .pm-nav a sup { display: none; }
}

/* ------------------------------------------------------------
   the gear, in three-quarter view: front face, extruded back,
   hub pushed toward the viewer. Hairline strokes only.
   ------------------------------------------------------------ */
.gear3d { display: block; width: 100%; height: 100%; overflow: visible; }
.gear3d * { vector-effect: non-scaling-stroke; }
.gear3d .g-back {
  fill: rgba(230, 236, 237, 0.2);
  stroke: rgba(230, 236, 237, 0.6);
  stroke-width: 1;
  stroke-linejoin: round;
}
.gear3d .g-front {
  fill: var(--bg);
  stroke: currentColor;
  stroke-width: 1;
  stroke-linejoin: round;
}
.gear3d .g-hubbase,
.gear3d .g-side {
  fill: none;
  stroke: rgba(230, 236, 237, 0.6);
  stroke-width: 1;
}
.gear3d .g-hubtop {
  fill: var(--bg);
  stroke: currentColor;
  stroke-width: 1;
}
/* the bore is the one amber detail in the mark: a lit centre, so
   the gear reads as part of the brand and not as a stray diagram */
.gear3d .g-bore {
  fill: var(--bg);
  stroke: var(--accent);
  stroke-width: 1;
}

/* ------------------------------------------------------------
   type scale
   ------------------------------------------------------------ */
.label {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mute);
}

.display {
  font-weight: 620;
  font-size: clamp(3.4rem, 12vw, 11.5rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  text-transform: uppercase;
}

.display-md {
  font-weight: 560;
  font-size: clamp(2rem, 5.5vw, 4.6rem);
  line-height: 1.02;
  letter-spacing: -0.025em;
}

/* the "A" of Mecánica: the real glyph, upright and normal, with the
   crossbar masked away leaving a clean gap. */
.oa { position: relative; }
.oa-glyph {
  position: relative;
  display: inline-block;
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 53%, transparent 53%, transparent 71%, black 71%, black 100%);
          mask-image: linear-gradient(to bottom, black 0%, black 53%, transparent 53%, transparent 71%, black 71%, black 100%);
}
/* a quiet spark IN the gap left by the crossbar, on the letter and
   not above or below it. Anchored to .oa-glyph, NOT the outer .oa:
   .oa shares a line box with its "Mec"/"nica" siblings so its box is
   taller than the glyph's; .oa-glyph is inline-block, so its height
   reliably IS its own 0.92em line-height, matching the mask's
   coordinate space exactly. */
.oa-glyph::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0.57em;
  width: 0.09em;
  height: 0.09em;
  background: var(--brand2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* ------------------------------------------------------------
   hero
   ------------------------------------------------------------ */
.hero {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0 clamp(20px, 4vw, 56px) clamp(28px, 5vh, 60px);
  position: relative;
  overflow: hidden;
}
.hero-haze {
  position: absolute;
  top: -20%; right: -15%;
  width: 70vw; height: 70vw;
  background: radial-gradient(closest-side, rgba(224, 138, 21, 0.13), transparent 70%);
  border-radius: 50%;
  pointer-events: none;
}

/* ------------------------------------------------------------
   hero-exploded: an exploded-view line drawing of a turntable
   mechanism. Every part sits at its assembled position by default;
   JS nudges each .ex-part apart on arrival and pulls it back
   together as the page scrolls — the machine assembles itself as
   you commit to scrolling further in.
   ------------------------------------------------------------ */
.hero-exploded {
  position: absolute;
  top: 47%; left: 50%;
  width: min(132vw, 172svh);
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  opacity: 0.3;
  pointer-events: none;
}
.exploded-tt { display: block; width: 100%; height: 100%; overflow: visible; }
.exploded-tt * { vector-effect: non-scaling-stroke; }

/* every solid surface is background-filled, so a part in front
   genuinely hides the one behind it — that occlusion is what
   makes the projection read as three-dimensional. */
.exploded-tt .ex-face {
  fill: var(--bg);
  stroke: var(--line);
  stroke-width: 1;
  stroke-linejoin: round;
}
.exploded-tt .ex-line {
  fill: none;
  stroke: var(--line);
  stroke-width: 1;
}
.exploded-tt .ex-line-soft {
  fill: none;
  stroke: var(--line-soft);
  stroke-width: 1;
}
.exploded-tt .ex-tick { stroke: rgba(230, 236, 237, 0.55); stroke-width: 1; }
.exploded-tt .ex-strobe { fill: rgba(230, 236, 237, 0.65); }
.exploded-tt .ex-fill {
  fill: var(--bg);
  stroke: var(--line);
  stroke-width: 1;
}
.exploded-tt .ex-tube {
  fill: none;
  stroke: var(--line);
  stroke-width: 2.2;
  stroke-linecap: round;
}
.exploded-tt .ex-dot { fill: var(--brand2); }
/* the assembly axis stays put while the parts travel along it */
.exploded-tt .ex-axis {
  fill: none;
  stroke: rgba(230, 236, 237, 0.24);
  stroke-width: 1;
  stroke-dasharray: 2 7;
}
.exploded-tt .ex-lead { stroke: rgba(230, 236, 237, 0.34); stroke-width: 1; }
.exploded-tt .ex-num {
  fill: rgba(230, 236, 237, 0.5);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
}

@media (max-width: 900px) {
  .hero-exploded { width: min(146vw, 112svh); opacity: 0.22; }
}

/* On a phone the turntable is the full-bleed background of the first
   screen: top edge to bottom edge, with the title sitting on top of
   it. The sizing is indirect because the drawing does not fill its
   own viewBox — the assembled machine occupies roughly the middle
   46% of the square box's height — so to make the machine itself as
   tall as the screen the box has to be a bit over twice that. The
   box is square, so it also becomes far wider than the viewport and
   the hero's overflow:hidden crops the sides, which is what makes it
   read as a background rather than an illustration.

   --ex-shift then lifts the box so the machine's own centre lands on
   the centre of the screen rather than the box's centre; main.js
   composes it into the parallax transform it writes every frame. */
@media (max-width: 720px) {
  /* A full-height hero left the bottom third of the first screen
     empty, because the title is the only thing in it. The hero is cut
     to roughly two thirds so the manifesto is already on screen when
     the page opens, and the drawing is resized to span that shorter
     hero rather than the whole viewport. */
  /* flex-end, not centre: with the title centred, the leftover height
     of the hero was split above and below it, and the half underneath
     opened a dead gap between the title and the manifesto. Pushing
     the title to the foot of the hero gives all that room to the
     drawing above and puts the copy straight under the heading. */
  .hero-machine {
    min-height: 64svh;
    justify-content: flex-end;
    padding-bottom: clamp(14px, 2.5vh, 26px);
  }
  .hero-machine .hero-exploded {
    --ex-shift: -3svh;
    top: 50%;
    width: 101svh;
    opacity: 0.2;
  }
  /* the manifesto is the rest of that first screen, so its opening
     gap has to read as a gap, not as a full section break */
  .hero-machine + .manifesto {
    padding-top: clamp(16px, 2.5vh, 28px);
  }
  .hero-machine + .manifesto .label {
    margin-bottom: clamp(14px, 2.2vh, 24px);
  }
}

.hero-title {
  position: relative;
  color: var(--ink);
}

/* The wordmark is split down the middle: PARRANDA is the cold white
   of the page, MECÁNICA is the amber. Targeting the second .line
   rather than adding a class keeps every page's markup untouched —
   only the homepage h1 has two lines, so nothing else is affected. */
.hero-title .line:nth-child(2) > span { color: var(--accent); }

/* mask-reveal: the slide-up-from-below-the-mask effect, shared by
   the homepage wordmark and every DJ page's name so they arrive the
   same way. */
.mask-reveal .line { display: block; overflow: hidden; }
/* direct child only, so a nested .oa accent span is left alone */
.mask-reveal .line > span {
  display: block;
  transform: translateY(110%);
  transition: transform 1.4s var(--ease);
  white-space: nowrap;
}
.mask-reveal.in .line > span { transform: translateY(0); }
.mask-reveal .line:nth-child(2) > span { transition-delay: 0.12s; }

/* the portrait fades in as a soft unit, no shift, right behind
   the name's own reveal */
.photo-fade { opacity: 0; transition: opacity 1.3s var(--ease); }
.photo-fade.in { opacity: 1; }

.hero-foot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: clamp(20px, 4vh, 44px);
  gap: 16px;
  flex-wrap: wrap;
}

.hero-foot .label { max-width: 340px; line-height: 1.7; }

/* ------------------------------------------------------------
   manifesto: words surface as you scroll
   ------------------------------------------------------------ */
.manifesto {
  padding: clamp(120px, 22vh, 260px) clamp(20px, 4vw, 56px);
  max-width: 1180px;
}

.manifesto .label { display: block; margin-bottom: clamp(28px, 5vh, 56px); }

.manifesto p {
  font-size: clamp(1.7rem, 4.2vw, 3.6rem);
  font-weight: 480;
  line-height: 1.22;
  letter-spacing: -0.02em;
}
/* margin-bottom rather than p + p: the EN and ES copies are
   siblings and only one is ever displayed, so a sibling selector
   would give the first visible paragraph of one language a top gap
   the other language never gets. */
.manifesto p { margin-bottom: 0.62em; }
.manifesto p:last-child { margin-bottom: 0; }

.manifesto .w {
  display: inline-block;
  opacity: 0.14;
  transition: opacity 0.5s linear;
  will-change: opacity;
}

/* ------------------------------------------------------------
   roster: each artist is a chapter, the container adopts them
   ------------------------------------------------------------ */
.roster-head {
  padding: 0 clamp(20px, 4vw, 56px) clamp(40px, 8vh, 90px);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
}

.chapter {
  position: relative;
  min-height: 96svh;
  display: flex;
  align-items: center;
  overflow: hidden;
  border-top: 1px solid var(--hair);
}

.chapter a.chapter-link {
  display: block;
  width: 100%;
  padding: clamp(70px, 12vh, 130px) clamp(20px, 4vw, 56px);
  position: relative;
}

.chapter .field {
  position: absolute;
  top: 50%; left: 58%;
  width: min(88vw, 95svh);
  height: min(88vw, 95svh);
  transform: translate(-50%, -50%) scale(0.75);
  background: var(--field);
  opacity: 0;
  filter: blur(28px);
  border-radius: 50%;
  pointer-events: none;
  will-change: transform, opacity;
}

.chapter .idx {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.24em;
  color: var(--mute);
  display: block;
  margin-bottom: clamp(14px, 2.5vh, 26px);
}

/* Artist names stay white: the amber is reserved for the wordmark
   and for single accents, so eight amber headlines in a row would
   spend the whole colour in one scroll. */
.chapter .name {
  position: relative;
  color: var(--ink);
  transition: letter-spacing 0.9s var(--ease);
}
.chapter:hover .name { letter-spacing: -0.015em; }

.chapter .cta {
  font-family: var(--mono);
  display: inline-block;
  margin-top: clamp(20px, 3.5vh, 36px);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--body);
  opacity: 0.6;
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.chapter:hover .cta { opacity: 1; transform: translateX(6px); }

/* ------------------------------------------------------------
   community home
   ------------------------------------------------------------ */
/* the payoff: four short lines, each arriving on its own. It now
   lives inside the hero's own flex column, directly above the
   title — horizontal padding comes from .hero itself, and this is
   the exact 50px the title sits below it. */
.payoff {
  margin-bottom: 50px;
}
.payoff p {
  font-size: clamp(1.5rem, 4vw, 3.2rem);
  font-weight: 540;
  line-height: 1.2;
  letter-spacing: -0.025em;
  max-width: 1100px;
}
.payoff .ln { display: block; }
.payoff .ln + .ln { margin-top: 0.1em; }
/* each line trails the one above it into view */
.payoff .ln:nth-child(2) { transition-delay: 0.08s; }
.payoff .ln:nth-child(3) { transition-delay: 0.16s; }
.payoff .ln:nth-child(4) { transition-delay: 0.24s; }

.pillars {
  border-top: 1px solid var(--hair);
  padding: clamp(90px, 16vh, 180px) clamp(20px, 4vw, 56px);
}
.pillars .label { display: block; margin-bottom: clamp(40px, 7vh, 80px); }

.pillar-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(30px, 5vw, 70px);
}

.pillar { border-top: 1px solid var(--hair); padding-top: 22px; }
.pillar .n {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.24em;
  color: var(--mute);
  display: block;
  margin-bottom: 16px;
}
.pillar h3 {
  font-size: clamp(1.5rem, 2.6vw, 2.2rem);
  font-weight: 580;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin-bottom: 14px;
}
.pillar p {
  font-size: 1rem;
  line-height: 1.65;
  color: var(--body);
}

/* upcoming nights */
.events {
  border-top: 1px solid var(--hair);
  padding: clamp(90px, 16vh, 180px) clamp(20px, 4vw, 56px);
}
.events .label { display: block; margin-bottom: clamp(34px, 6vh, 60px); }

.event-list { border-top: 1px solid var(--hair); }
.event-list li {
  list-style: none;
  display: grid;
  grid-template-columns: minmax(90px, auto) 1fr auto;
  align-items: baseline;
  gap: clamp(16px, 3vw, 40px);
  padding: clamp(22px, 3.5vh, 34px) 0;
  border-bottom: 1px solid var(--hair);
  transition: padding-left 0.5s var(--ease);
}
.event-list li:hover { padding-left: 10px; }
.event-list .when {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
}
.event-list .what {
  font-size: clamp(1.2rem, 2.2vw, 1.8rem);
  font-weight: 540;
  letter-spacing: -0.015em;
}
.event-list .where {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
  text-align: right;
}
@media (max-width: 700px) {
  .event-list li { grid-template-columns: 1fr; gap: 8px; }
  .event-list .where { text-align: left; }
}

/* join / belonging */
.join {
  border-top: 1px solid var(--hair);
  padding: clamp(110px, 20vh, 220px) clamp(20px, 4vw, 56px);
}
.join .display-md { max-width: 900px; margin-top: 28px; }
.join-body {
  margin-top: clamp(30px, 5vh, 50px);
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.65;
  color: var(--body);
  max-width: 620px;
}
.join-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: clamp(36px, 6vh, 60px);
}
.btn {
  font-family: var(--mono);
  display: inline-block;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 15px 26px;
  border-radius: 3px;
  border: 1px solid var(--ink);
  transition: background 0.4s var(--ease), color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.btn-solid { background: var(--accent); border-color: var(--accent); color: var(--bg); }
.btn-solid:hover { background: var(--accent-hi); border-color: var(--accent-hi); }
.btn-ghost { color: var(--ink); }
.btn-ghost:hover { background: var(--ink); color: var(--bg); }

/* ------------------------------------------------------------
   about
   ------------------------------------------------------------ */
.about {
  border-top: 1px solid var(--hair);
  padding: clamp(110px, 20vh, 220px) clamp(20px, 4vw, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: clamp(30px, 6vw, 90px);
}
@media (max-width: 820px) { .about { grid-template-columns: 1fr; } }

.about-body p {
  font-size: clamp(1.05rem, 1.6vw, 1.35rem);
  line-height: 1.65;
  color: var(--body);
  max-width: 620px;
}
.about-body p + p { margin-top: 1.4em; }

.about-list {
  margin-top: clamp(36px, 6vh, 64px);
  border-top: 1px solid var(--hair);
}
.about-list li {
  font-family: var(--mono);
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 0;
  border-bottom: 1px solid var(--hair);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.about-list li span:last-child { color: var(--mute); text-align: right; }

/* ------------------------------------------------------------
   contact / footer
   ------------------------------------------------------------ */
.contact {
  border-top: 1px solid var(--hair);
  padding: clamp(110px, 20vh, 220px) clamp(20px, 4vw, 56px) clamp(50px, 8vh, 90px);
  position: relative;
  overflow: hidden;
}

.contact .display-md { max-width: 900px; }

.contact-mail {
  display: inline-block;
  margin-top: clamp(30px, 6vh, 60px);
  font-size: clamp(1.3rem, 3.4vw, 2.6rem);
  font-weight: 540;
  letter-spacing: -0.02em;
  border-bottom: 1px solid var(--hair);
  padding-bottom: 8px;
  transition: border-color 0.5s var(--ease), color 0.5s var(--ease);
}
.contact-mail:hover { border-color: var(--accent); color: var(--accent-hi); }

.pm-footer {
  font-family: var(--mono);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: clamp(80px, 14vh, 160px);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mute);
}

/* ------------------------------------------------------------
   artist page
   ------------------------------------------------------------ */
.dj-hero {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0 clamp(20px, 4vw, 56px) clamp(30px, 6vh, 64px);
  position: relative;
  overflow: hidden;
}

/* portrait: a clear grayscale cutout with only a whisper of the
   artist's color, fading into the lava before it meets an edge */
.dj-hero .portrait {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.dj-hero .portrait img {
  position: absolute;
  top: 0; right: 0;
  height: 100%;
  width: 66%;
  object-fit: cover;
  object-position: center top;
  /* over lava black a full-brightness cutout reads as a lit rectangle
     stuck on the page; pulling it down lets it sit inside the dark */
  filter: grayscale(1) contrast(1.06) brightness(0.8);
  -webkit-mask-image: linear-gradient(to left, black 55%, transparent 99%);
          mask-image: linear-gradient(to left, black 55%, transparent 99%);
  opacity: 1;
}
/* Ricky Zagato's photo is full-body, decks included — anchor lower
   than the default "top" so more of the console shows. Specificity
   note: ".dj-zagato-portrait img" alone (0,1,1) loses to the base
   rule ".dj-hero .portrait img" (0,2,1) — it never actually applied.
   Compounding the class onto .portrait itself, under .dj-hero,
   brings it to (0,3,1) so it genuinely wins. */
.dj-hero .portrait.dj-zagato-portrait img { object-position: center 34%; }

/* Annita's photo has generous blank wall above her head, so on a
   short/wide (landscape) hero box the top-anchored cover crop shows
   mostly that empty wall and barely her face. Anchoring lower keeps
   her face and shoulders in frame at any viewport shape. */
.dj-hero .portrait.dj-annita-portrait img { object-position: center 42%; }

/* Naya Binghi has generous headroom above the subject in the source
   photo. On a narrow/tall viewport the portrait box is narrower than
   it is tall, so "cover" crops vertically off the default "top" and
   still keeps the face in frame — but on a wide desktop window the
   box widens faster than it grows tall, the crop math flips, and the
   same "top" instead shows nearly the whole frame including all that
   empty headroom, leaving the face small and low. Pulling the
   position down only matters once that flip happens, so it's scoped
   to wide viewports. */
@media (min-width: 900px) {
  .dj-hero .portrait.dj-naya-portrait img { object-position: center 40%; }
}

/* Over cream this was an amber colour-blend, which was the only way
   to keep the cutout from reading as a grey rectangle. Over lava
   black the amber blend turns every portrait sepia and fights the
   artist's own colour field behind it, so the photo now stays
   neutral and the veil is simply the page colour, laid on heaviest
   at the bottom where the name has to sit on top of it. */
.dj-hero .portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, var(--bg) 2%, rgba(34, 32, 26, 0.55) 34%, transparent 72%);
  pointer-events: none;
}

/* the color field: back over black it lights the hero rather than
   tinting it, so it carries a fuller value. */
.dj-hero .field {
  position: absolute;
  top: 58%; left: 40%;
  width: min(80vw, 90svh);
  height: min(80vw, 90svh);
  transform: translate(-50%, -50%);
  background: var(--field);
  filter: blur(34px);
  opacity: 0.5;
  border-radius: 50%;
  pointer-events: none;
  will-change: transform;
}

.dj-hero .display {
  position: relative;
  color: var(--ink);
}


.dj-section {
  border-top: 1px solid var(--hair);
  padding: clamp(90px, 16vh, 180px) clamp(20px, 4vw, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: clamp(30px, 6vw, 90px);
}
@media (max-width: 820px) { .dj-section { grid-template-columns: 1fr; } }

.dj-section .body p {
  font-size: clamp(1.05rem, 1.6vw, 1.35rem);
  line-height: 1.65;
  color: var(--body);
  max-width: 640px;
}
.dj-section .body p + p { margin-top: 1.4em; }

/* ------------------------------------------------------------
   the mix player: sits in the left rail of the bio, under the
   BIOGRAPHY label. Built out of the parts the rest of the page
   already uses — a hairline, mono numerals, one amber spark for
   the played portion. No chrome, no gradients, no shadow.
   The transport is always drawn. Until a mix is actually on the
   server the player sits dormant: stepped back, not operable, but
   present — so the page never has a hole where a player belongs.
   ------------------------------------------------------------ */
.mixplayer {
  margin-top: clamp(30px, 5vh, 52px);
  padding-top: 24px;
  border-top: 1px solid var(--hair);
  max-width: 320px;
  transition: opacity 0.9s var(--ease);
}
.mixplayer.is-idle { opacity: 0.42; }
.mixplayer.is-idle .mp-btn,
.mixplayer.is-idle .mp-track { cursor: default; }
.mixplayer.is-idle .mp-btn:hover {
  border-color: var(--hair);
  color: var(--ink);
}

.mp-btn {
  width: 42px; height: 42px;
  display: grid;
  place-items: center;
  border: 1px solid var(--hair);
  border-radius: 50%;
  color: var(--ink);
  transition: border-color 0.45s var(--ease), color 0.45s var(--ease);
}
.mp-btn:hover { border-color: var(--accent); color: var(--accent); }
.mp-btn svg { width: 12px; height: 12px; display: block; fill: currentColor; }
/* one glyph at a time, swapped by the playing state */
.mp-i-pause { display: none; }
.mixplayer.is-playing .mp-i-play { display: none; }
.mixplayer.is-playing .mp-i-pause { display: block; }

.mp-body { margin-top: 20px; }

.mp-cap {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mute);
}

/* a 1px rule is the right weight but a hopeless click target, so the
   hit area is padded out invisibly above and below it */
.mp-track {
  position: relative;
  height: 1px;
  margin-top: 14px;
  background: var(--hair);
  cursor: pointer;
}
.mp-track::before {
  content: "";
  position: absolute;
  inset: -12px 0;
}
.mp-fill {
  position: absolute;
  left: 0; top: 0;
  height: 100%;
  width: 0;
  background: var(--accent);
}
.mp-dot {
  position: absolute;
  top: 50%; left: 0;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
  transform: translate(-50%, -50%) scale(0);
  transition: transform 0.4s var(--ease);
}
.mixplayer.is-ready .mp-dot { transform: translate(-50%, -50%) scale(1); }

.mp-time {
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--mute);
  font-variant-numeric: tabular-nums;
}

.sound-words {
  font-size: clamp(1.9rem, 4.5vw, 3.8rem);
  font-weight: 540;
  line-height: 1.15;
  letter-spacing: -0.02em;
}
.sound-words em {
  font-style: normal;
  color: var(--accent);
}

.set-list { border-top: 1px solid var(--hair); }
.set-list li {
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  padding: clamp(18px, 3vh, 28px) 0;
  border-bottom: 1px solid var(--hair);
  transition: padding-left 0.5s var(--ease);
}
.set-list li:hover { padding-left: 10px; }
.set-list .venue {
  font-size: clamp(1.1rem, 2vw, 1.6rem);
  font-weight: 520;
  letter-spacing: -0.01em;
}
.set-list .where {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mute);
  text-align: right;
}

.dj-book {
  border-top: 1px solid var(--hair);
  padding: clamp(100px, 18vh, 200px) clamp(20px, 4vw, 56px) clamp(50px, 8vh, 90px);
}
.dj-book .contact-mail { color: var(--accent); border-color: transparent; }
.dj-book .contact-mail:hover { border-color: var(--accent); }

.back-roster {
  font-family: var(--mono);
  display: inline-block;
  margin-top: clamp(60px, 10vh, 110px);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--mute);
  transition: color 0.4s var(--ease);
}
.back-roster:hover { color: var(--accent); }

/* ------------------------------------------------------------
   phones
   Everything above is fluid already, so this block is not a second
   layout — it only fixes the things fluid type and clamp() can't:
   controls that were too small to hit, rows that assumed two
   columns of room, and long unbreakable strings.
   ------------------------------------------------------------ */
@media (max-width: 720px) {
  /* The nav and the language toggle were sized for a mouse: 10px
     mono with 4px of padding is roughly a 26px target, well under
     the ~44px a thumb needs. The type stays small — it is meant to
     read as a technical ruler — and the target grows around it. */
  .pm-nav a {
    padding: 9px 0;
    margin: -9px 0;
  }
  .lang-switch button { padding: 9px 12px; }

  /* two labels at opposite ends of one line is a desktop idea; on a
     phone the right-hand one wraps into a ragged second line under
     itself, so they stack cleanly instead */
  .roster-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  /* same reason: a label and its value pushed apart by
     justify-content leaves almost no room for either */
  .about-list li {
    flex-direction: column;
    gap: 6px;
  }
  .about-list li span:last-child { text-align: left; }

  .set-list li {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }
  .set-list .where { text-align: left; }

  /* the address is one unbreakable token wider than a small phone;
     without this it forces a horizontal scrollbar on the whole page */
  .contact-mail {
    max-width: 100%;
    overflow-wrap: anywhere;
    line-height: 1.25;
  }

  /* the chapters are full-height panels, which on a short phone
     screen leaves the name floating in a lot of nothing */
  .chapter { min-height: 78svh; }

  /* On desktop the portrait is a 66%-wide panel on the right with the
     name beside it. A phone has no "beside", so the photo takes the
     full width and fades out at the top instead of the left — the
     name then sits under the face rather than across it, and the
     lava veil in ::after already gives it a bed to sit on. */
  .dj-hero .portrait img {
    width: 100%;
    object-position: center 18%;
    /* fades at BOTH ends: cropping the height instead would leave a
       hard horizontal edge across the page where the photo stops */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 24%, black 56%, transparent 90%);
            mask-image: linear-gradient(to bottom, transparent 0%, black 24%, black 56%, transparent 90%);
  }

  /* the payoff and the title have to share one screen with a
     two-row header, so the gap between them tightens */
  .payoff { margin-bottom: 34px; }

  /* full-width buttons: two 26px-padded pills side by side wrap
     into an uneven stack otherwise */
  .join-actions { gap: 12px; }
  .btn {
    flex: 1 1 100%;
    text-align: center;
    padding: 16px 20px;
  }
}

/* very narrow phones (SE-class): the display type is the only thing
   that still doesn't fit, because .mask-reveal keeps every line on
   one line by design */
@media (max-width: 380px) {
  .display { font-size: clamp(2.7rem, 13vw, 3.4rem); }
}

/* ------------------------------------------------------------
   reveals
   ------------------------------------------------------------ */
.reveal {
  opacity: 0;
  transform: translateY(34px);
  transition: opacity 1.1s var(--ease), transform 1.1s var(--ease);
}
.reveal.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .manifesto .w { opacity: 1 !important; }
  .reveal { opacity: 1; transform: none; }
  .mask-reveal .line span { transform: none; }
  .photo-fade { opacity: 1; }
}
