/* =========================================================
   CAPITAL DJs — Prototipo v2 · css/motion.css
   Estilos del sistema de movimiento (js/motion.js). API en el brief §6.4.

   Regla de oro: NADA se oculta si el JS no lo pidió. Los estados
   "antes de animar" viven bajo html.js-motion, que motion.js pone
   siempre que GSAP + ScrollTrigger cargaron y no hay reduced-motion
   (llegue cuando llegue: ya no hay regla de "llegó tarde"), y bajo
   html.js-soft, el modo suave de movimiento reducido (solo opacidad).
   Lo que ya se veía cuando motion.js arrancó llega con .is-in puesto.
   Las dos excepciones las pone el <head> antes del primer pintado y
   siempre tienen salida (motion.js o DOMContentLoaded): html.cdj-espera
   (la portada espera a motion.js) y html.cdj-llegada (la cortina sigue
   puesta al llegar desde otra página del sitio).
   ========================================================= */

/* ---------- Lenis (CSS recomendado de lenis@1.3.4) ---------- */
html.lenis, html.lenis body { height: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }

/* ---------- cortina de llegada (1-oct-2026, tarde) ----------
   Solo al llegar desde otra página del sitio: la cortina de salida (motion.js, leave) tapó la
   pantalla y deja una marca en sessionStorage; el <head> de la página nueva pone html.cdj-llegada
   antes del primer pintado y esta cortina (igual a la real en reposo: negro y el logo al centro)
   sigue puesta. motion.js la cambia por la real y la levanta. SIN reloj (la de antes se quitaba
   sola a los 2.5 s y, si motion.js llegaba después, la página quedaba sin animación): si motion.js
   no llega, el <head> quita la clase en DOMContentLoaded. */
html.cdj-llegada::after {
  content: "";
  position: fixed; inset: 0; z-index: 9998;
  pointer-events: none;
  background: #0b0b0b url("/img/logo-cdjs-blanco.svg") no-repeat center / min(240px, 56vw) auto;
}

/* ---------- la portada que espera a motion.js (1-oct-2026, tarde) ----------
   En las páginas que no son la home, el <head> pone html.cdj-espera antes del primer pintado y lo
   marcado con data-entrada (el nombre y la foto del DJ, la cabecera del aviso, la 404) no se ve
   hasta que motion.js lo anima, llegue cuando llegue (en un celular con datos, segundos después
   del primer pintado). motion.js quita la clase en cdj:ready, en el mismo cuadro en que arrancan
   las entradas; sin GSAP ni modo suave, al correr; si motion.js no llega, el <head> la quita en
   DOMContentLoaded. Oculta lo mismo que html.js-motion, más el odómetro (si no, se vería su cifra
   y luego el "00" del que rueda; con movimiento reducido no rueda, así que no se oculta). */
html.cdj-espera [data-entrada] [data-reveal],
html.cdj-espera [data-entrada] [data-split],
html.cdj-espera [data-entrada] [data-reveal-group] > :not(.nota) { visibility: hidden; }
@media (prefers-reduced-motion: no-preference) {
  html.cdj-espera [data-entrada] [data-odometer] { visibility: hidden; }
}

/* Con movimiento, el scroll nativo va sin suavizado aunque falte Lenis: con scroll-behavior: smooth
   (styles.css, html:not(.lenis)) el scrollTo(0) de ScrollTrigger.refresh() es suave, mide a medio
   camino y las Líneas se quedan recortadas. Las anclas siguen suaves (motion.js, scrollToTarget). */
html.js-motion:not(.lenis) { scroll-behavior: auto; }

/* ---------- cortina de transición (el preloader de la home lo reemplazó la entrada del logo, css/hero.css) ---------- */
.cdj-curtain {
  position: fixed; inset: 0; z-index: 9999;
  display: grid; place-items: center;
  background: #0b0b0b; color: #fff;
  will-change: transform;
}
.cdj-curtain.is-off { visibility: hidden; transform: translateY(-101%); }
.cdj-curtain__in { position: relative; width: min(240px, 56vw); }
.cdj-curtain__logo { width: 100%; height: auto; }
.cdj-curtain .cdj-eq { position: absolute; left: 50%; top: calc(100% + 26px); transform: translateX(-50%); }

/* ---------- ecualizador (cortina y "Sonando:") ---------- */
.cdj-eq { display: inline-flex; align-items: flex-end; gap: 3px; height: 16px; flex: none; }
.cdj-eq i { display: block; width: 3px; height: 100%; background: currentColor; transform-origin: bottom; animation: cdj-eq 1s ease-in-out infinite; }
.cdj-eq i:nth-child(1) { animation-duration: .9s; animation-delay: -.2s; }
.cdj-eq i:nth-child(2) { animation-duration: 1.15s; animation-delay: -.5s; }
.cdj-eq i:nth-child(3) { animation-duration: .8s; animation-delay: -.1s; }
.cdj-eq i:nth-child(4) { animation-duration: 1.05s; animation-delay: -.7s; }
.cdj-eq i:nth-child(5) { animation-duration: .95s; animation-delay: -.35s; }
@keyframes cdj-eq { 0%, 100% { transform: scaleY(.25); } 50% { transform: scaleY(1); } }

/* ---------- estados previos a animar (solo con html.js-motion) ---------- */
html.js-motion [data-reveal]:not(.is-in) { opacity: 0; }
html.js-motion [data-reveal="clip"]:not(.is-in) { opacity: 1; clip-path: inset(100% 0 0 0); }
html.js-motion [data-reveal-group]:not(.is-in) > :not(.nota) { opacity: 0; }
html.js-motion [data-split]:not(.is-split) { visibility: hidden; }

/* modo suave (prefers-reduced-motion): lo que aún no entra está transparente; motion.js le pone
   .is-in y un fundido de opacidad de 0.6 s (Web Animations) al entrar al viewport. Nada se mueve. */
html.js-soft [data-reveal]:not(.is-in),
html.js-soft [data-split]:not(.is-in),
html.js-soft [data-reveal-group]:not(.is-in) > :not(.nota) { opacity: 0; }

/* máscaras de SplitText: un poco de aire para acentos y descendentes */
.sl-mask { padding-block: .1em; margin-block: -.1em; }
.sc, .sw { will-change: transform; }

/* ---------- temas por sección animados en el body ---------- */
html.js-themes [data-section-theme]:not(.is-opaque) {
  --bg: inherit; --fg: inherit; --fg-2: inherit; --fg-3: inherit; --rule: inherit; --logo-invert: inherit;
  background-color: transparent;
}

/* ---------- marquee ---------- */
/* sin JS (o con reduced-motion): los elementos se acomodan centrados, sin desbordar */
[data-marquee] { display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0 .2em; max-width: 100%; }
[data-marquee]:not(.is-marquee) > * { white-space: normal; text-align: center; }
[data-marquee].is-marquee {
  flex-wrap: nowrap; justify-content: flex-start; gap: 0;
  overflow: hidden; touch-action: pan-y;
  user-select: none; -webkit-user-select: none;
}
[data-marquee][data-marquee-draggable].is-marquee { cursor: grab; }
[data-marquee].is-dragging { cursor: grabbing; }
/* con mouse (5-oct-2026): cursor normal, sin manita ni texto. Arrastrar queda de extra, sin anunciarlo
   (la manita cerrada solo sale mientras se arrastra); la tira frena al pasar encima (motion.js) */
@media (hover: hover) and (pointer: fine) {
  [data-marquee][data-marquee-draggable].is-marquee { cursor: default; }
  [data-marquee][data-marquee-draggable].is-marquee.is-dragging { cursor: grabbing; }
}
.marquee__track { display: flex; flex: none; will-change: transform; }
.marquee__group { display: flex; flex: none; align-items: baseline; }
.marquee__group > * { flex: none; white-space: nowrap; }

/* ---------- odómetro ---------- */
.odo { display: inline-flex; align-items: flex-end; line-height: 1; white-space: nowrap; }
.odo__d { display: inline-block; height: 1.08em; overflow: hidden; text-align: center; }
.odo__strip { display: block; will-change: transform; }
.odo__strip > span { display: block; height: 1.08em; line-height: 1.08em; }
/* las cifras viven en atributos (no en el texto del DOM): ver initOdometer */
.odo__strip::before { content: attr(data-d); display: block; white-space: pre; line-height: 1.08em; }
.odo__fix { display: inline-block; height: 1.08em; line-height: 1.08em; }
.odo__fix::before { content: attr(data-t); }

/* ---------- transición "Línea": data-transicion="linea" (js/motion.js) ----------
   Sin JS (o con reduced-motion) esto ya es el estado final: el filete a todo lo ancho y la
   etiqueta a la derecha, como folio de la sección que entra. Con JS, motion.js dibuja el
   filete, abre la sección desde él (clip-path) y pone .ln__under con el color de la que sale. */
/* --ln-u: distancia del borde de la sección que entra al filete (el aire del folio por arriba;
   se suma al padding de abajo de la que sale) · --ln-gap: del filete al contenido de la que entra.
   Los dos crecen con --sec-y (styles.css): el folio queda con aire propio, no apretado. */
:root { --ln-u: clamp(72px, 15vh, 150px); --ln-gap: calc(var(--sec-y, 160px) * .8); }
@supports (height: 1svh) { :root { --ln-u: clamp(72px, 15svh, 150px); } }
[data-transicion="linea"] {
  position: relative; display: block;
  height: 0; /* no ocupa lugar: todo cuelga sobre la parte de arriba de la sección que entra */
  pointer-events: none;
  /* el filete y la etiqueta se mezclan (difference) con lo que tienen debajo: nada de
     transform, opacity < 1 ni isolation aquí ni entre ellos y la sección que entra */
}
/* la sección que entra va encima de la capa con el color de la que sale… */
[data-transicion="linea"] + * { position: relative; z-index: 1; }
/* …y deja aire arriba para el folio (gana a los paddings de styles.css y dj.css; el mapa lo
   repite en map.css porque su propio padding es más específico) */
[data-transicion="linea"] + [data-section-theme] { padding-top: calc(var(--ln-u) + var(--ln-gap)); }

/* el filete: 1 px, a todo lo ancho. Blanco + difference = el color del texto de lo que hay debajo */
.ln__rule {
  position: absolute; left: 0; right: 0; top: var(--ln-u); z-index: 2;
  display: block; height: 1px;
  background: #fff;
  mix-blend-mode: difference;
  transform-origin: 0 50%;
}
/* la etiqueta que viaja sobre el filete (Geist Mono). En reposo, a la derecha, alineada con la retícula */
.ln__label {
  position: absolute; z-index: 2;
  top: calc(var(--ln-u) - 1em - 12px);
  right: max(var(--gutter, 28px), calc((100% - var(--max, 1240px)) / 2 + var(--gutter, 28px)));
  margin: 0;
  font: 400 11px/1 var(--mono, "Geist Mono", ui-monospace, monospace);
  letter-spacing: .16em; text-transform: uppercase;
  white-space: nowrap;
  color: #fff;
  mix-blend-mode: difference;
}
.ln__label .lc { text-transform: none; }
/* solo con JS: el color de la sección que sale, detrás de la que entra
   (la que entra está recortada con clip-path hasta que se abre desde el filete) */
.ln__under {
  position: absolute; left: 0; right: 0; top: -2px; z-index: 0; /* -2px: se mete bajo A, sin rendija */
  display: block;
}
/* las notas amarillas del prototipo de la sección que entra van debajo del folio, no encima
   (en ≤860 px ya van en el flujo, después del aire del folio) */
@media (min-width: 861px) {
  [data-transicion="linea"] + [data-section-theme] > .nota { top: calc(var(--ln-u) + 16px); }
}
@media (max-width: 640px) {
  .ln__label { font-size: 10px; letter-spacing: .14em; }
}

/* ---------- vinilo (CSS puro; motion.js lo hace girar con el scroll) ---------- */
[data-vinyl], .vinyl {
  position: relative; display: block;
  aspect-ratio: 1; border-radius: 50%;
  background:
    radial-gradient(circle, transparent 0 1.6%, #fff 1.7% 16%, #0b0b0b 16.4% 17.4%, transparent 17.6%),
    conic-gradient(from 20deg, rgba(255,255,255,0) 0deg, rgba(255,255,255,.16) 30deg, rgba(255,255,255,0) 70deg, rgba(255,255,255,0) 180deg, rgba(255,255,255,.12) 215deg, rgba(255,255,255,0) 250deg),
    repeating-radial-gradient(circle, #111 0 1px, #1b1b1b 1.5px 3px),
    #0b0b0b;
  box-shadow: 0 0 0 1px rgba(255,255,255,.06), 0 14px 30px rgba(0,0,0,.25);
  container-type: inline-size;
}
/* lo impreso en la etiqueta: así se nota que gira */
[data-vinyl]::after, .vinyl::after {
  content: "CAPITAL DJs";
  position: absolute; left: 50%; top: 41.5%;
  transform: translateX(-50%);
  font: 600 2.2cqw/1 var(--sans); letter-spacing: .12em;
  color: #0b0b0b; white-space: nowrap;
}
[data-vinyl]::before, .vinyl::before {
  content: ""; position: absolute; left: 41%; right: 41%; top: 56%; height: 1px; background: #0b0b0b; opacity: .6;
}

/* ---------- cursor custom (solo puntero fino) ---------- */
.cdj-cursor { position: fixed; left: 0; top: 0; z-index: 9000; pointer-events: none; width: 0; height: 0; }
.cdj-cursor__dot {
  position: absolute; left: -46px; top: -46px; width: 92px; height: 92px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--fg); color: var(--bg);
  font: 600 10.5px/1 var(--sans); letter-spacing: .22em; text-transform: uppercase; text-indent: .22em;
  transform: scale(0); transition: transform .45s var(--ease-out);
}
.cdj-cursor.is-on .cdj-cursor__dot { transform: scale(1); }
html.has-cursor [data-cursor], html.has-cursor [data-cursor] * { cursor: none; }

/* ---------- botones con texto que rueda ---------- */
.roll { position: relative; display: inline-flex; overflow: hidden; vertical-align: top; line-height: 1.25; padding-block: .06em; margin-block: -.06em; }
.roll__row { display: inline-block; white-space: nowrap; }
.roll__row--b { position: absolute; left: 0; top: 100%; }
.roll__c { display: inline-block; white-space: pre; transition: transform .6s var(--ease-out); transition-delay: calc(var(--i, 0) * 13ms); }
[data-btn-roll]:hover .roll__c,
[data-btn-roll]:focus-visible .roll__c,
[data-roll-host]:hover [data-btn-roll] .roll__c { transform: translateY(-100%); }

/* ---------- reduced motion: nada se mueve (los fundidos del modo suave son de opacidad) ---------- */
@media (prefers-reduced-motion: reduce) {
  .cdj-eq i, .roll__c { animation: none !important; transition: none !important; }
}
@media print {
  .cdj-curtain, .cdj-cursor, html.cdj-llegada::after { display: none !important; }
  html.cdj-espera [data-entrada] * { visibility: visible !important; }
  html.js-motion [data-reveal], html.js-soft [data-reveal], html.js-soft [data-split],
  html.js-motion [data-reveal-group] > *, html.js-soft [data-reveal-group] > * { opacity: 1 !important; }
}
