/* ==========================================================================
   EL JICHI - styles.css
   Nueve escenas. El sitio tiene que verse terminado sin JavaScript: todo lo
   que sigue deja los elementos en su estado final y el neon encendido fijo.
   main.js solo agrega movimiento (y la clase html.is-animated, o html.is-calm
   con movimiento reducido: ver la seccion 18).
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 1rem);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, ol, dl, dd, figure, blockquote {
  margin: 0;
}

ul, ol {
  padding: 0;
  list-style: none;
}

img, video, svg {
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
}

strong {
  font-weight: 500;
  color: var(--ink);
}

:focus-visible {
  outline: 2px solid var(--neon);
  outline-offset: 3px;
}

/* Al seguir un ancla, main.js lleva el foco a la escena de destino para el
   lector de pantalla: ese foco no dibuja contorno (rodeaba una escena
   entera de 100 svh con la caja de 2 px). */
main :is(section, article)[tabindex="-1"]:focus-visible {
  outline: 0;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: 0.75rem;
  z-index: 200;
  padding: 0.75rem 1rem;
  background: var(--neon);
  color: var(--bg);
  font-weight: 500;
  transform: translateY(calc(-100% - 1rem));
  transition: transform var(--t-base) var(--ease-out);
}

.skip-link:focus {
  transform: none;
}

.shell {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.data {
  font-family: var(--font-mono);
  font-size: var(--fs-data);
  letter-spacing: var(--tracking-mono);
  font-feature-settings: "tnum" 1;
}

/* --------------------------------------------------------------------------
   2. Sistema de neon
   La serpiente y el rotulo son las formas RELLENAS del letrero real
   (assets/svg/serpiente-neon-fiel-transparente.svg y rotulo-el-jichi.svg),
   definidas una sola vez en .neon-defs y referenciadas con <use>. El tubo es
   una forma de ancho variable, no un trazo; el nucleo es la misma forma
   metida tres unidades, al 45 % como en el letrero. La linea central del tubo
   (#js-rev, de neon-paths-v2.json, bloque "letrero") no se pinta nunca: es la
   mascara de revelado con la que el preloader y el hover de la barra dibujan
   el tubo, asi el estado final es identico al letrero.
   .neon-layer solo fija los remates de trazo: el color y el relleno los
   ponen las figuras (.neon-snake, .neon-sign). La serpiente de la escena
   de areas tiene sus propias clases en 9b.
   -------------------------------------------------------------------------- */

.neon-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.neon-snake,
.neon-sign {
  display: block;
  overflow: visible;
}

.neon-layer {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Las figuras fieles: relleno, no trazo. */
.neon-snake .neon-layer,
.neon-sign .neon-layer {
  stroke: none;
}

.neon-snake .neon-layer-tube,
.neon-sign .neon-layer-tube {
  fill: var(--neon);
}

.neon-snake .neon-layer-core,
.neon-sign .neon-layer-core {
  fill: var(--neon-core);
  opacity: 0.45;
}

/* La piel: relleno oscuro de cabeza y cuello (degradado vertical medido en
   el letrero), boca, fosas, pupilas, ojos y lengua. Todo en tokens para que
   el modo del letrero los tine a la vez que el tubo. */
.neon-hfill {
  fill: url("#js-hfill-grad");
}

.js-fill-a { stop-color: var(--neon-fill-a); }
.js-fill-b { stop-color: var(--neon-fill-b); }

.neon-snake .neon-ink {
  fill: var(--neon-ink);
  stroke: none;
}

.neon-snake .neon-boca-1 {
  opacity: 0.55;
}

.neon-snake .neon-eyes {
  fill: var(--neon-core);
  stroke: none;
}

.neon-snake .neon-tongue {
  fill: var(--tongue);
  stroke: none;
}

.neon-snake .neon-tongue-core {
  fill: var(--tongue-core);
  stroke: none;
}

/* La mascara de revelado: linea central del tubo, grosor medido (11.8) por
   el factor que cubre el tubo entero (1.16). En reposo esta dibujada.
   El blanco de aca (y el #fff/#000 de las mascaras js-mt y js-mn del HTML)
   es luminancia de mascara, no un color que se pinte: es la unica excepcion
   a la regla de no usar blanco ni negro puros. */
.neon-rev {
  fill: none;
  stroke: #fff;
  stroke-width: 13.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1000;
  stroke-dashoffset: 0;
}

/* --------------------------------------------------------------------------
   2b. El halo: una imagen, no un desenfoque en vivo
   El brillo era una capa de trazo ancho con filter: blur() que
   ademas respiraba. En iOS eso se recalcula en cada cuadro del scroll. Ahora
   el halo viene horneado en assets/img/neon-halo-*-v2.webp (resvg, desde las
   formas fieles del letrero y con sus dos desenfoques) y lo unico que se
   anima es su opacidad. El sufijo -v2 es por la cache inmutable de /assets/img.
   Los cuatro colores estan apilados y hacen crossfade con el modo del letrero.
   -------------------------------------------------------------------------- */

.neon-figure {
  position: relative;
  display: block;
  isolation: isolate;
}

.neon-figure > .neon-snake,
.neon-figure > .neon-sign {
  position: relative;
  z-index: 1;
  width: 100%;
}

.neon-halo {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* Los halos se rasterizaron (resvg) desde las mismas formas fieles, con 60
   unidades de aire alrededor del viewBox para que el desenfoque no quede
   cortado; por eso la imagen es mas grande que la caja del SVG:
   serpiente (785+120)/785 x (707+120)/707, rotulo (736+120)/736 x (173+120)/173.
   La caja del halo se agranda hasta la imagen (inset negativo) y la imagen se
   pinta a 100 %: antes se escalaba dentro de la caja del SVG y el borde de la
   caja recortaba el desenfoque, que a esa altura todavia tiene un 20 % de
   alfa, y detras del rotulo se veia un rectangulo mas claro con cantos
   duros. Los porcentajes del inset salen del aire de 60 unidades a cada lado:
   rotulo 60/173 = 34.7 % arriba y abajo, 60/736 = 8.15 % a los lados;
   serpiente 60/707 = 8.5 % y 60/785 = 7.65 %. */
.neon-halo > i {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  background-size: 100% 100%;
  transition: opacity var(--t-slow) var(--ease-out);
}

.neon-halo-snake {
  inset: -8.5% -7.65%;
}

.neon-halo-sign {
  inset: -34.7% -8.15%;
}

/* De entrada solo se baja el verde (17 KB). Los otros tres colores se piden
   recien cuando el visitante toca el letrero: el selector :root[data-neon-mode]
   entra con cualquier modo, asi que a partir del primer toque los cuatro estan
   disponibles y el crossfade funciona en los dos sentidos. */
.neon-halo-snake > i[data-halo="verde"] { background-image: url("../img/neon-halo-serpiente-verde-v2.webp"); }
.neon-halo-sign  > i[data-halo="verde"] { background-image: url("../img/neon-halo-rotulo-verde-v2.webp"); }

:root[data-neon-mode] .neon-halo-snake > i[data-halo="rojo"]    { background-image: url("../img/neon-halo-serpiente-rojo-v2.webp"); }
:root[data-neon-mode] .neon-halo-snake > i[data-halo="azul"]    { background-image: url("../img/neon-halo-serpiente-azul-v2.webp"); }
:root[data-neon-mode] .neon-halo-snake > i[data-halo="violeta"] { background-image: url("../img/neon-halo-serpiente-violeta-v2.webp"); }

:root[data-neon-mode] .neon-halo-sign > i[data-halo="rojo"]    { background-image: url("../img/neon-halo-rotulo-rojo-v2.webp"); }
:root[data-neon-mode] .neon-halo-sign > i[data-halo="azul"]    { background-image: url("../img/neon-halo-rotulo-azul-v2.webp"); }
:root[data-neon-mode] .neon-halo-sign > i[data-halo="violeta"] { background-image: url("../img/neon-halo-rotulo-violeta-v2.webp"); }

/* el modo manda cual de los cuatro esta encendido */
:root .neon-halo > i[data-halo="verde"] { opacity: 0.5; }

:root[data-neon-mode="rojo"] .neon-halo > i[data-halo="verde"],
:root[data-neon-mode="azul"] .neon-halo > i[data-halo="verde"],
:root[data-neon-mode="violeta"] .neon-halo > i[data-halo="verde"] { opacity: 0; }

:root[data-neon-mode="rojo"] .neon-halo > i[data-halo="rojo"],
:root[data-neon-mode="azul"] .neon-halo > i[data-halo="azul"],
:root[data-neon-mode="violeta"] .neon-halo > i[data-halo="violeta"] { opacity: 0.5; }

/* respiracion permanente del letrero del hero y del cierre: solo opacidad */
@keyframes neon-breathe {
  0%, 100% { opacity: 0.82; }
  50%      { opacity: 1.13; }
}

.fig-hero .neon-halo,
.fig-sign .neon-halo,
.fig-foot .neon-halo,
.fig-menu .neon-halo {
  animation: neon-breathe 4s var(--ease-in-out) infinite;
}

/* tamanos de cada figura (antes vivian en el SVG) */
.fig-hero { width: 100%; }
.fig-sign { width: 78%; }

/* --------------------------------------------------------------------------
   3. Placas de foto: el tratamiento que iguala todas las imagenes
   -------------------------------------------------------------------------- */

.plate {
  position: relative;
  overflow: hidden;
  background: var(--stage);
  isolation: isolate;
}

.plate > picture,
.plate > img,
.plate > video {
  display: block;
  width: 100%;
  height: 100%;
}

/* El tratamiento es uno solo y se aplica fuera del navegador: el grado
   (desaturacion, contraste, brillo y el verde en las sombras) viene
   horneado en assets/img/graded/, con la misma receta para todas las fotos. Aca no queda ni un filter ni un mix-blend-mode: eso es lo
   que hacia tironear el scroll del iPhone al cambiar de direccion.
   La exposicion por placa tambien se hornea; lo que sigue en el CSS es solo
   una veladura opcional en negro plano, que es barata.
   La veladura inferior (::before) se queda: es un gradiente, no una mezcla. */
.plate {
  --photo-veil: 0;
}

.plate picture > img,
.plate > img,
.plate > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Bajar una foto un punto se hace con negro encima, no con brightness. */
.plate::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--bg);
  opacity: var(--photo-veil);
  pointer-events: none;
  /* vira en 600 ms con el modo del letrero, como el neon y los acentos:
     era lo unico que saltaba de golpe al tocar el letrero */
  transition: opacity var(--t-slow) var(--ease-out),
              background-color var(--t-slow) var(--ease-out);
}

html.is-booting .plate::after {
  transition: none;
}

.plate::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(180deg, transparent 55%, var(--bg) 100%);
}

/* Los dos videos tambien vienen horneados: la misma receta de graded/ pasada
   por un LUT 3D con ffmpeg. Antes el filtro del navegador reproducia solo
   saturate/contrast/brightness y se saltaba la mezcla verde de las sombras,
   asi que el hero se veia magenta y el loop de Records rojo mientras todas
   las fotos eran verde-teal, que es justo la homogeneidad que pidio el
   cliente. Ahora el poster y el video, que son el mismo cuadro, miden lo
   mismo, y no queda ni un filter sobre un elemento a pantalla completa. */

/* El modo del letrero tine las fotos solo con puntero fino. En el celular el
   modo cambia el neon y los acentos, y las fotos se quedan como estan.
   Antes el tinte era un hue-rotate sobre cada foto a sangre y sobre el VIDEO
   del hero a pantalla completa: un filtro vivo sobre elementos grandes, que
   ademas se mueven con scrub, asi que el compositor levantaba una superficie
   de 1440x900 por cada uno en cada cuadro (medido: en rojo el doble de long
   tasks que en verde). Ahora la foto no lleva filtro: la veladura que ya
   existe (.plate::after, solo opacidad) se tine con el color del modo y sube
   a un minimo de 0.3, y la pagina entera vira junta como pide la direccion
   de arte. El unico hue-rotate que queda es el del recorte chico de las
   columnas, que no es una foto a sangre. */
@media (pointer: fine) {
  :root[data-neon-mode="rojo"]    { --neon-hue: 260deg; }
  :root[data-neon-mode="azul"]    { --neon-hue: 100deg; }
  :root[data-neon-mode="violeta"] { --neon-hue: 150deg; }

  :root[data-neon-mode] .plate::after {
    background: color-mix(in oklab, var(--neon) 40%, var(--bg));
    opacity: max(var(--photo-veil), 0.34);
  }

  :root[data-neon-mode] .ledger-cut img {
    filter: hue-rotate(var(--neon-hue, 0deg));
  }
}

/* --------------------------------------------------------------------------
   4. Titulares y textos de escena
   -------------------------------------------------------------------------- */

.scene-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-h2);
  line-height: var(--lh-display);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
  text-wrap: balance;
}

.scene-title span {
  display: block;
}

/* El corte de las dos lineas se decide a mano en los dos anchos: en mobile
   "Tres formas" y "de usarla." bajan una debajo de otra; desde 48em vuelven
   a la misma linea. */
@media (min-width: 48em) {
  .scene-title .t-part {
    display: inline;
  }

  /* Espacio duro: un espacio normal en content se colapsa y las dos partes
     quedan pegadas ("TRES FORMASDE USARLA."). Ademas asi no se separan. */
  .scene-title .t-join::before {
    content: "\0000a0";
  }
}

.scene-lead {
  margin-top: var(--sp-5);
  max-width: var(--measure);
  font-size: var(--fs-lead);
  color: var(--ink-2);
  text-wrap: pretty;
}

.sub-title {
  margin-top: var(--sp-7);
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--ink-3);
}

.scene-head {
  padding-block: var(--scene-pad) var(--sp-7);
}

.quote {
  margin-top: var(--sp-6);
  max-width: 46ch;
  font-size: 1.25rem;
  line-height: 1.45;
  color: var(--ink-2);
}

.quote-brand {
  font-size: clamp(1.25rem, 2.4vw, 1.75rem);
  max-width: 34ch;
  color: var(--ink);
}

/* listas separadas por hairline, nunca por guiones */
.rule-list li {
  padding-block: 0.7rem;
  border-top: 1px solid var(--line);
  color: var(--ink-2);
  text-wrap: pretty;
  transition: color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out);
}

.rule-list li:hover {
  color: var(--ink);
  border-top-color: var(--line-hot);
}

.rule-list-wide li {
  padding-block: 0.95rem;
  font-size: var(--fs-lead);
}

/* --------------------------------------------------------------------------
   5. Botones y enlaces
   -------------------------------------------------------------------------- */

.btn {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  min-height: var(--tap);
  padding: 0.7rem 1.35rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-weight: 500;
  line-height: 1.25;
  overflow: hidden;
  isolation: isolate;
  transition: color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out),
              transform 100ms var(--ease-out);
}

.btn > * {
  position: relative;
  z-index: 1;
}

/* z-index NEGATIVO, no 0. El relleno es un descendiente posicionado: a
   z-index 0 se pinta despues del contenido en linea del propio boton y tapa
   las etiquetas que son texto suelto (todas menos las de .btn-stack, que van
   en spans). El isolation:isolate de .btn lo mantiene dentro del boton. */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--neon);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-base) var(--ease-out);
}

.btn:active {
  transform: scale(0.985);
}

.btn-primary {
  border-color: var(--line-hot);
  color: var(--neon);
}

.btn-primary:hover,
.btn-primary:focus-visible {
  color: var(--bg);
}

.btn-primary:hover::before,
.btn-primary:focus-visible::before {
  transform: scaleX(1);
}

.btn-line {
  border-color: var(--line);
  color: var(--ink);
}

.btn-line:hover,
.btn-line:focus-visible {
  border-color: var(--line-hot);
  color: var(--neon);
}

.btn-quiet {
  padding-inline: 0;
  border: 0;
  color: var(--ink);
}

.btn-quiet::before {
  top: auto;
  bottom: 0.35rem;
  height: 1px;
  background: var(--neon);
  transform: scaleX(0);
  transform-origin: center;
}

.btn-quiet:hover::before,
.btn-quiet:focus-visible::before {
  transform: scaleX(1);
}

.btn-quiet:hover,
.btn-quiet:focus-visible {
  color: var(--neon);
}

/* Sin puntero que lo encienda, el boton silencioso se leia como una linea de
   texto suelta cuando cae debajo del principal: en tactil lleva la hairline
   dibujada en reposo, como .link-line, para que se lea como accion. */
@media (hover: none) {
  .btn-quiet::before {
    transform: scaleX(1);
    background: var(--line-hot);
  }
}

.btn-stack .btn-main {
  display: block;
}

.btn-stack .btn-sub {
  display: block;
  margin-top: 0.2rem;
  color: var(--ink-3);
  transition: color var(--t-base) var(--ease-out);
}

.btn-primary:hover .btn-sub,
.btn-primary:focus-visible .btn-sub,
.btn-line:hover .btn-sub,
.btn-line:focus-visible .btn-sub {
  color: inherit;
}

/* Las transiciones de color, border-color y text-decoration-color de esta
   hoja son repintado de elementos chicos (botones, hairlines, enlaces): la
   excepcion aceptada a la lista de transform, opacity, clip-path y
   stroke-dashoffset. Nada de top, background-size ni box-shadow animados. */

/* El padding vertical lleva el enlace a los 44px de toque. La hairline va
   dibujada en reposo (en el celular no hay hover, y sin ella "Como llegar",
   los correos e Instagram se leian como texto plano) y solo cambia de
   color al pasar o tocar. */
.link-line {
  position: relative;
  display: inline-block;
  padding-block: 0.5rem;
  margin-block: -0.4rem;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.28em;
  text-decoration-color: var(--line-hot);
  transition: color var(--t-base) var(--ease-out),
              text-decoration-color var(--t-base) var(--ease-out);
}

.link-line:hover,
.link-line:focus-visible,
.link-line:active {
  color: var(--neon);
  text-decoration-color: var(--neon);
}

/* --------------------------------------------------------------------------
   6. Navegacion
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  inset: 0 0 auto;
  z-index: var(--z-nav);
  min-height: var(--nav-h);
  transition: background-color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out);
  border-bottom: 1px solid transparent;
}

/* Fondo solido, sin backdrop-filter en ningun puntero: era el unico filter
   del sitio y vivia justo sobre el elemento que se recompone en cada cuadro
   del scroll, encima de la escena fijada. Sobre este fondo casi negro el
   desenfoque no se distinguia. Opaco del todo: con un 8 % de aire se leian
   en fantasma los titulares, el neon y los botones que pasaban por debajo,
   y el cliente pidio la barra solida. */
.nav.is-stuck {
  background: var(--bg);
  border-bottom-color: var(--line);
}

.nav-inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: var(--nav-h);
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.nav-mark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding-block: 0.7rem;
  margin-right: auto;
}

.fig-nav {
  width: 26px;
  flex: 0 0 26px;
}

/* A 26 px el tubo del letrero (12 unidades de 785) queda por debajo del
   pixel: la serpiente mini lo engorda con un contorno del mismo color, sin
   la mascara de la cola, que a ese tamano no se ve. El redibujado al pasar
   por la marca lo hace main.js sobre .rev-nav (stroke-dashoffset), con la
   mascara de revelado mas ancha para cubrir el tubo engordado. */
.snake-nav .neon-layer-tube .neon-body {
  stroke: var(--neon);
  stroke-width: 8;
  stroke-linejoin: round;
}

.snake-nav .neon-layer-core {
  opacity: 0.3;
}

.rev-nav {
  stroke-width: 24;
}

.nav-mark-name {
  font-weight: 500;
  letter-spacing: 0.01em;
  font-size: 0.9375rem;
  white-space: nowrap;
}

.nav-links {
  display: none;
  gap: var(--sp-5);
}

.nav-links a {
  position: relative;
  color: var(--ink-2);
  font-weight: 500;
  font-size: 0.9375rem;
  padding-block: 0.65rem;
  transition: color var(--t-base) var(--ease-out);
}

/* El subrayado se queda pegado al texto aunque el enlace crezca hasta los
   44px de toque. */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.25rem;
  height: 1px;
  background: var(--neon);
  transform: scaleX(0);
  transition: transform var(--t-base) var(--ease-out);
}

.nav-links a:hover,
.nav-links a:focus-visible {
  color: var(--ink);
}

.nav-links a:active {
  color: var(--neon);
}

.nav-links a:hover::after,
.nav-links a:focus-visible::after {
  transform: scaleX(1);
}

.nav-cta {
  display: none;
  padding-block: 0.5rem;
  min-height: var(--tap);
}

.nav-menu-btn {
  font-weight: 500;
  font-size: 0.9375rem;
  padding: 0.6rem 0;
  min-height: var(--tap);
  min-width: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  border-bottom: 1px solid var(--line-hot);
  transition: color var(--t-base) var(--ease-out),
              border-color var(--t-base) var(--ease-out);
}

.nav-menu-btn:hover,
.nav-menu-btn:focus-visible,
.nav-menu-btn:active {
  color: var(--neon);
  border-bottom-color: var(--neon);
}

@media (min-width: 64em) {
  .nav-links,
  .nav-cta {
    display: inline-flex;
  }

  .nav-menu-btn {
    display: none;
  }
}

/* capa de menu en mobile, abierta por :target (anda sin JavaScript) */
.menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  background: var(--bg);
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--t-base) var(--ease-out),
              visibility 0s linear var(--t-base);
}

.menu:target {
  visibility: visible;
  opacity: 1;
  transition: opacity var(--t-base) var(--ease-out);
}

.menu-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: calc(var(--nav-h) + var(--sp-5)) var(--gutter) var(--sp-6);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.menu-links {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.menu-links a {
  display: block;
  padding-block: 0.35rem;
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 11vw, 4rem);
  line-height: 1.02;
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
  transition: color var(--t-base) var(--ease-out);
}

.menu-links a:hover,
.menu-links a:focus-visible,
.menu-links a:active {
  color: var(--neon);
}

/* "Cerrar" es el cierre deliberado de la capa, no un resto: hairline propia
   arriba, tinta plena y aire por debajo para que no se pise con el tubo de la
   serpiente de fondo. */
.menu-close {
  display: block;
  margin-top: auto;
  padding-block: 0.85rem 0.35rem;
  border-top: 1px solid var(--line);
  min-height: var(--tap);
  font-weight: 500;
  color: var(--ink);
  transition: color var(--t-base) var(--ease-out);
}

.menu-close:hover,
.menu-close:focus-visible,
.menu-close:active {
  color: var(--neon);
}

/* La serpiente respira al fondo de la capa, entera en pantalla: con
   right y bottom en -16 % solo asomaba un trozo de cabeza por la esquina y
   se leia como un recorte accidental. Debajo de "Cerrar" queda aire. */
.fig-menu {
  position: fixed;
  right: -6%;
  bottom: 9%;
  width: 90%;
  z-index: -1;
  opacity: 0.22;
}

/* Con la capa abierta la barra sube por encima: la serpiente mini y el
   nombre siguen a la vista (antes quedaban tapados y arriba sobraban 130 px
   vacios). El enlace "Menu" se esconde porque la barra queda inerte y el
   cierre deliberado es el "Cerrar" de abajo. */
html.menu-open .nav {
  z-index: calc(var(--z-menu) + 1);
}

html.menu-open .nav-menu-btn {
  visibility: hidden;
}

@media (min-width: 64em) {
  .menu {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   7. Preloader (oculto por defecto: solo existe con html.is-loading)
   -------------------------------------------------------------------------- */

.preloader {
  position: fixed;
  inset: 0;
  z-index: var(--z-pre);
  display: none;
  background: var(--bg);
}

html.is-loading .preloader {
  display: block;
}

/* Bloqueo del scroll mientras corre la intro. Con puntero grueso (barra
   superpuesta, sin ancho) alcanza overflow: hidden. Con puntero fino la
   barra clasica de Windows mide 15 px y esconderla cambia el ancho del
   documento: el FLIP de la serpiente se calculaba a 1440 y, al soltar
   is-loading, la barra volvia y el hero entero (video, titular y letrero)
   se recomponia 15 px en el mismo cuadro en que aterrizaba. Chrome no
   reserva el hueco con scrollbar-gutter cuando la vista esta en hidden,
   asi que ahi la barra se queda (overflow-y: scroll) y el gesto lo bloquea
   main.js (lockScroll: rueda, teclas, tacto y Lenis). Lo mismo para el
   menu abierto. */
html.is-loading,
html.menu-open {
  overflow: hidden;
}

@media (pointer: fine) {
  html.is-loading,
  html.menu-open {
    overflow: hidden scroll;
  }
}

.preloader-veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(80% 60% at 50% 45%, var(--neon-glow), transparent 70%);
  opacity: 0.08;
}

/* --------------------------------------------------------------------------
   8. Hero: el cuarto a oscuras
   -------------------------------------------------------------------------- */

/* svh, no dvh: en el celular dvh cambia cada vez que la barra del navegador
   entra o sale (justo al invertir el scroll) y el hero crecia o encogia
   50-100 px, con toda la pagina de abajo saltando y la geometria de la
   serpiente vertical desfasada hasta el proximo refresh. La linea anterior es
   el fallback para navegadores sin unidades nuevas. */
.hero {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto;
  min-height: 100vh;
  min-height: 100svh;
  padding: calc(var(--nav-h) + var(--sp-2)) var(--gutter) var(--sp-4);
  overflow: hidden;
}

.hero-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.plate-hero {
  --photo-veil: 0;
}

/* A 375px el poster entra centrado y el letrero de neon real que hay en la
   pared del estudio cae justo al lado del rotulo SVG: se leen dos "EL JICHI"
   pegados, como un logo duplicado.
   El encuadre se corre todo a la derecha (el vertical no hace nada: a este
   ancho la foto entra justa de alto), lo que deja al DJ del microfono de
   ancla y empuja el letrero de pared contra el borde. Los 60px que todavia
   asoman se apagan con una caida de luz por el canto izquierdo, que es lo que
   hace una pared a oscuras. */
@media (max-width: 47.99em) {
  .plate > .hero-poster {
    object-position: 100% 50%;
  }
}

/* Mientras el rotulo va centrado arriba (por debajo de 64em) el letrero de
   pared sigue en el cuadro aunque el encuadre no se pueda correr mas: la
   caida de luz por el canto izquierdo lo apaga y de paso limpia el fondo
   detras del titular. */
@media (max-width: 63.99em) {
  .hero-vignette::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
      var(--bg) 0%,
      var(--bg) 16%,
      color-mix(in oklab, var(--bg) 62%, transparent) 26%,
      transparent 42%);
  }
}

/* Gana .plate > img/video (0-1-1) si no se sube la especificidad: con la
   clase sola el poster quedaba apilado fuera de la placa. Y el video aparece
   solo cuando main.js le engancha las fuentes (a partir de 48em con puntero
   fino, tambien con movimiento reducido: es contenido, no un efecto): asi
   el celular no se baja 1,1 MB de webm y sin JavaScript queda el poster,
   que es el mismo cuadro. */
.plate > .hero-video {
  display: none;
}

.plate > .hero-poster {
  display: block;
}

html.has-video .plate > .hero-video {
  display: block;
}

html.has-video .plate > .hero-poster {
  display: none;
}

.hero-vignette {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background:
    radial-gradient(135% 105% at 50% 42%, transparent 0%, var(--bg) 100%),
    linear-gradient(180deg, var(--bg) 0%, transparent 26%, transparent 58%, var(--bg) 100%);
}

.hero-grid {
  position: relative;
  z-index: 2;
  display: grid;
  gap: var(--sp-5);
  align-content: center;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
}

.hero-mark {
  display: grid;
  justify-items: center;
  gap: var(--sp-4);
  margin: 0;
  font-weight: 400;
}

.hero-neon {
  display: grid;
  justify-items: center;
  gap: var(--sp-4);
  width: min(58vw, 20rem);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  transition: transform 100ms var(--ease-out);
}

/* El letrero responde como todo lo demas que se toca: presion corta y, con
   puntero, el halo del color activo sube un punto. Sin filtro: solo la
   opacidad del halo horneado. En modo calmo queda solo el halo, sin escala. */
.hero-neon:active {
  transform: scale(0.985);
}

html.is-calm .hero-neon:active {
  transform: none;
}

:root:not([data-neon-mode]) .hero-neon:hover .neon-halo > i[data-halo="verde"],
:root:not([data-neon-mode]) .hero-neon:focus-visible .neon-halo > i[data-halo="verde"],
:root[data-neon-mode="rojo"] .hero-neon:hover .neon-halo > i[data-halo="rojo"],
:root[data-neon-mode="rojo"] .hero-neon:focus-visible .neon-halo > i[data-halo="rojo"],
:root[data-neon-mode="azul"] .hero-neon:hover .neon-halo > i[data-halo="azul"],
:root[data-neon-mode="azul"] .hero-neon:focus-visible .neon-halo > i[data-halo="azul"],
:root[data-neon-mode="violeta"] .hero-neon:hover .neon-halo > i[data-halo="violeta"],
:root[data-neon-mode="violeta"] .hero-neon:focus-visible .neon-halo > i[data-halo="violeta"] {
  opacity: 0.64;
}


.hero-hint {
  margin-top: -0.4rem;
  font-size: 0.8rem;
  line-height: 1.2;
  color: var(--ink-3);
  opacity: 0;
}

/* line-height 1.1 y no 1: Anton dibuja la tilde de las mayusculas por
   encima del em box y las de GRABA y HACE se pegaban a la linea de arriba. */
.hero-claim {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  line-height: 1.1;
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
}

.hero-claim span {
  display: block;
}

.hero-lead {
  margin-top: var(--sp-4);
  max-width: 44ch;
  color: var(--ink-2);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-5);
  margin-top: var(--sp-5);
}

.hero-foot {
  position: relative;
  z-index: 2;
  padding-bottom: var(--sp-2);
  display: flex;
  align-items: flex-end;
  gap: var(--sp-5);
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-top: var(--sp-5);
  font-size: var(--fs-small);
  color: var(--ink-3);
}

/* "Baja" y la flecha van en una fila, con la linea base de la palabra a la
   altura de la direccion: apilados, la palabra quedaba 50 px por encima del
   pie y se leian dos elementos sueltos. */
.hero-scroll {
  flex: none;
  display: inline-grid;
  grid-auto-flow: column;
  align-items: end;
  gap: 0.6rem;
  margin-left: auto;
  min-height: var(--tap);
  color: var(--ink-3);
  transition: color var(--t-base) var(--ease-out);
}

.hero-scroll:hover,
.hero-scroll:focus-visible,
.hero-scroll:active {
  color: var(--neon);
}

.arrow-down {
  display: block;
  flex: none;
  width: 12px;
  height: 30px;
  stroke: currentColor;
  stroke-width: 1;
  fill: none;
}

/* Pausar el video de fondo (el del hero y el loop de Records): un enlace
   de texto de 44 px que solo existe cuando el video esta enganchado. En
   el hero se apoya en la linea base de la direccion; en la placa de
   Records va abajo a la izquierda, por encima de la veladura. */
.video-pause {
  display: none;
  align-items: flex-end;
  min-height: var(--tap);
  padding: 0 0.15rem;
  border: 0;
  background: none;
  font-weight: 500;
  font-size: var(--fs-small);
  color: var(--ink-3);
  cursor: pointer;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.28em;
  text-decoration-color: var(--line);
  transition: color var(--t-base) var(--ease-out),
              text-decoration-color var(--t-base) var(--ease-out);
}

html.has-video .video-pause {
  display: inline-flex;
}

.video-pause:hover,
.video-pause:focus-visible,
.video-pause:active {
  color: var(--neon);
  text-decoration-color: var(--neon);
}

.video-pause-plate {
  position: absolute;
  left: var(--sp-4);
  bottom: var(--sp-3);
  z-index: 4;
  color: var(--ink-2);
}

@media (min-width: 64em) {
  .arrow-down {
    height: 44px;
  }
}

@keyframes stem-trace {
  0%   { stroke-dashoffset: 36; }
  55%  { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: -36; }
}

.arrow-stem {
  stroke-dasharray: 36;
  animation: stem-trace 3.2s var(--ease-in-out) infinite;
}

/* En un portatil de 13 pulgadas con la barra del navegador quedan entre 700 y
   800px de alto: sin esto el pie del hero (la direccion y el "Baja") cae
   debajo del pliegue y la primera pantalla deja de cerrar como una escena.
   Medido a 1440x760, que es donde el hero se iba a 809px. */
@media (max-height: 52em) {
  .hero {
    padding-top: calc(var(--nav-h) + var(--sp-1));
  }

  .hero-grid {
    gap: var(--sp-4);
  }

  .hero-neon {
    width: min(46vw, 15rem);
  }

  .hero-mark {
    gap: var(--sp-3);
  }

  .hero-lead {
    margin-top: var(--sp-3);
  }

  .hero-actions {
    margin-top: var(--sp-4);
  }

  .hero-foot {
    padding-top: var(--sp-4);
  }
}

@media (min-width: 64em) {
  .hero {
    padding-bottom: var(--sp-6);
  }

  /* En escritorio el letrero real de la pared asoma arriba a la izquierda,
     detras del titular, y se leian dos logos en la misma pantalla: la misma
     caida de luz por ese canto que ya usa el celular. */
  .plate > .hero-video,
  .plate > .hero-poster {
    object-position: 50% 62%;
  }

  .hero-vignette {
    background:
      linear-gradient(135deg, var(--bg) 0%, var(--bg) 9%, color-mix(in oklab, var(--bg) 78%, transparent) 24%, transparent 44%),
      radial-gradient(135% 105% at 50% 42%, transparent 0%, var(--bg) 100%),
      linear-gradient(180deg, var(--bg) 0%, transparent 26%, transparent 58%, var(--bg) 100%);
  }

  .hero-grid {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    align-items: center;
    gap: var(--sp-6);
  }

  .hero-mark {
    grid-column: 8 / 13;
    grid-row: 1;
    justify-items: center;
  }

  .hero-neon {
    width: min(38vw, 29rem);
  }

  .hero-say {
    grid-column: 1 / 8;
    grid-row: 1;
  }
}

/* Entre 48em y 64em el hero todavia va apilado (serpiente arriba, titular
   debajo) pero con el ancho de una tablet: medido a 820x900, el pie se iba
   35px abajo del pliegue. La serpiente cede esos pixeles. */
@media (min-width: 48em) and (max-width: 63.99em) {
  .hero-neon {
    width: min(42vw, 16rem);
  }
}

@media (min-width: 64em) and (max-height: 52em) {
  .hero-neon {
    width: min(30vw, 20rem);
  }
}

/* --------------------------------------------------------------------------
   9. Escena de las tres areas
   -------------------------------------------------------------------------- */

.scene-areas {
  position: relative;
  background: var(--bg-2);
}

.areas-stage {
  position: relative;
}

/* El riel del neon vive DENTRO del track (ver 19): posicionado, para que el
   trazo y las palabras gigantes compartan contexto de apilado. */
.areas-track {
  position: relative;
  display: grid;
}

.area-panel {
  position: relative;
  display: grid;
  align-content: end;
  min-height: 88vh;
  min-height: 88svh;
  padding: var(--sp-8) var(--gutter) var(--sp-9);
  overflow: hidden;
}

.area-photo {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* Cada foto trae su veladura: sin esto, Sounds (patio de noche, camara
   abierta) se leia como otro tratamiento al lado de Studio y Records.
   Studio es la unica foto ambar del sitio (pared naranja, media 40/47/33):
   al pasar de STUDIO a RECORDS en el riel el cambio de temperatura saltaba a
   la vista. Hasta que se re-hornee con la receta verde, su veladura va
   tenida con el verde de la casa y mas alta. */
[data-panel="studio"] .area-photo {
  --photo-veil: 0.2;
}

[data-panel="studio"] .area-photo::after {
  background: color-mix(in oklab, var(--stage-2) 72%, var(--bg));
}

[data-panel="records"] .area-photo {
  --photo-veil: 0.45;
}

/* Sounds (patio de noche) se leia gris y sin materia al lado de Records:
   lleva la misma veladura tenida de verde que Studio, y mas baja. */
[data-panel="sounds"] .area-photo {
  --photo-veil: 0.22;
}

[data-panel="sounds"] .area-photo::after {
  background: color-mix(in oklab, var(--stage-2) 72%, var(--bg));
}

.area-photo::before {
  background: linear-gradient(180deg, var(--bg) 0%, transparent 18%, transparent 38%, var(--bg) 94%);
}

/* El orden de apilado de la escena es foto (0), trazo de neon (1), palabra
   gigante (2) y caja de texto (3). El neon pasa por delante de la foto y por
   detras de la palabra, que es lo que pide la escena: la serpiente
   desaparece detras de la palabra y la enciende al reaparecer. */
.area-word {
  position: relative;
  z-index: 2;
  font-family: var(--font-display);
  font-size: clamp(3.2rem, 22vw, 9rem);
  line-height: 0.82;
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.92;
  margin-bottom: var(--sp-5);
  transition: color var(--t-base) var(--ease-out),
              opacity var(--t-base) var(--ease-out);
}

/* Con movimiento (completo o calmo) la palabra arranca apagada y se
   enciende al pasar el trazo; en modo calmo la enciende la transicion de
   color y opacidad de arriba, sin parpadeo. Sin JavaScript queda como
   estaba: encendida en tinta, que es el estado terminado que ya aprobo el
   cliente. */
html.is-animated .area-word,
html.is-calm .area-word {
  opacity: 0.42;
}

/* Con movimiento completo la opacidad la escribe GSAP: el flicker de dos
   pulsos con que se enciende la palabra (0.55, 1, 0.7, 1 en 260 ms) quedaba
   suavizado por la transicion de 220 ms y se veia como un fundido generico.
   La transicion se queda solo en el color; el apagado lo hace main.js. */
html.is-animated .area-word {
  transition: color var(--t-base) var(--ease-out);
}

html.is-animated .area-word.is-lit,
html.is-calm .area-word.is-lit {
  color: var(--neon);
  opacity: 0.95;
}

@media (min-width: 64em) {
  html.is-animated .area-word.is-lit,
  html.is-calm .area-word.is-lit {
    text-shadow: 0 0 0.08em var(--neon-glow);
  }
}

.area-say {
  position: relative;
  z-index: 3;
  max-width: 34rem;
}

/* Veladura propia detras de la columna de texto: la del gradiente de la foto
   llega floja justo donde arrancan los bullets, y en Studio y Records esa
   zona es pared iluminada. Se funde tambien a los lados (mascara de alfa):
   con el degradado solo vertical los cantos izquierdo y derecho se leian
   como un rectangulo de vidrio sobre la foto. */
.area-say::before {
  content: "";
  position: absolute;
  inset: -2.5rem -3rem -2.5rem -3rem;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent 0%,
    color-mix(in oklab, var(--bg) 72%, transparent) 20%,
    color-mix(in oklab, var(--bg) 86%, transparent) 60%,
    color-mix(in oklab, var(--bg) 86%, transparent) 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, var(--bg) 22%, var(--bg) 78%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, var(--bg) 22%, var(--bg) 78%, transparent 100%);
}

.area-say .rule-list li {
  color: var(--ink);
}

.area-line {
  font-size: var(--fs-lead);
  color: var(--ink);
  margin-bottom: var(--sp-4);
}

.area-say .btn {
  margin-top: var(--sp-5);
}

/* --------------------------------------------------------------------------
   9b. El trazo de neon y la serpiente viva
   La guia serpentea por la escena entera. Lo que la serpiente ya recorrio
   queda encendido como letrero, mas tenue que ella (stroke-dashoffset hasta
   la punta de la cola); lo que falta se ve como tubo apagado. Dos puentes
   cortos detras de la cola suben el brillo del rastro junto a la punta, asi
   la cola se funde con el letrero sin un escalon de luz.
   La serpiente son cuatro paths reales con el mismo d recalculado por cuadro
   (relleno oscuro, halo, tubo y nucleo: cuerpo y cabeza en una sola silueta
   cerrada) y una cara que viaja con la cabeza. El halo no es un blur: es un
   trazo mas ancho con opacidad baja. Nada de filtros ni de mezcla.
   Los grosores (--tw-*) van en unidades del viewBox y main.js los reescribe
   en cada medicion a partir de pixeles, asi el tubo mide lo mismo en un
   telefono apaisado que en uno alto.
   -------------------------------------------------------------------------- */

.areas-trail {
  position: absolute;
  max-width: none;
  pointer-events: none;
  z-index: 1;
  overflow: visible;
}

.areas-trail path {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.trail-off {
  stroke: var(--line);
  stroke-width: var(--tw-off);
}

.trail-halo {
  stroke: var(--neon);
  stroke-width: var(--tw-halo);
  opacity: var(--op-trail-halo);
}

.trail-tube {
  stroke: var(--neon);
  stroke-width: var(--tw-tube);
  opacity: 0.55;
}

.trail-core {
  stroke: var(--neon-core);
  stroke-width: var(--tw-core);
  opacity: 0.5;
}

/* Los puentes existen solo con movimiento completo: main.js les escribe el
   tramo (dasharray) y lo corre con la cola. */
.trail-bridge {
  display: none;
  stroke: var(--neon);
  stroke-width: var(--tw-tube);
}

html.is-animated .trail-bridge {
  display: inline;
}

.trail-bridge-near {
  opacity: 0.3;
}

.trail-bridge-far {
  opacity: 0.14;
}

/* Sin JavaScript, y en modo calmo, el trazo esta entero y encendido: en
   calmo la serpiente queda quieta a mitad de escena con el letrero completo.
   Con movimiento se dibuja detras de la cola. */
/* pathLength vale 1000, no 1: GSAP redondea los valores en px, asi que con
   una longitud normalizada a 1 el offset solo podia valer 0 o 1 y el trazo
   aparecia de golpe en vez de dibujarse. */
html.is-animated .trail-halo,
html.is-animated .trail-tube,
html.is-animated .trail-core {
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
}

.areas-trail-wide {
  display: none;
  --tw-off: 2.8;
  --tw-halo: 9.5;
  --tw-tube: 3.1;
  --tw-core: 0.85;
  --op-halo: 0.3;
  --op-trail-halo: 0.18;
  --op-head-halo: 0;
}

/* El camino vertical baja pegado al borde derecho; su viewBox es veinte veces
   mas alto que ancho, asi que con height:100% la escena manda el largo y el
   ancho sale solo (unos 107px a 375). En el celular no hay halo en el cuerpo
   ni en el rastro: solo una luz suave alrededor de la cabeza (un degradado
   radial como relleno de la elipse, sin trazo: el trazo de 13 unidades se
   veia como un disco de borde duro pegado detras de la cabeza). */
.areas-trail-tall {
  top: 0;
  right: 0;
  height: 100%;
  width: auto;
  --tw-off: 4.2;
  --tw-halo: 14;
  --tw-tube: 5.4;
  --tw-core: 1.5;
  --op-halo: 0;
  --op-trail-halo: 0;
  --op-head-halo: 1;
}

.areas-trail-tall .snake-head-halo {
  fill: url(#head-glow-tall);
  stroke: none;
}

.head-glow-in {
  stop-color: var(--neon);
  stop-opacity: 0.34;
}

.head-glow-mid {
  stop-color: var(--neon);
  stop-opacity: 0.1;
}

.head-glow-out {
  stop-color: var(--neon);
  stop-opacity: 0;
}

/* La serpiente: relleno con la tinta del letrero (la misma piel oscura de la
   serpiente del hero, que vira con el modo), tubo a plena luz y nucleo. Solo
   se ve con JavaScript: sin el, el trazo entero encendido ya cuenta la
   escena. */
.snake {
  opacity: 0;
}

html.is-animated .snake,
html.is-calm .snake {
  opacity: 1;
}

.areas-trail .snake-fill {
  fill: var(--neon-ink);
  stroke: none;
}

.snake-halo {
  stroke: var(--neon);
  stroke-width: var(--tw-halo);
  opacity: var(--op-halo);
}

.snake-tube {
  stroke: var(--neon);
  stroke-width: var(--tw-tube);
}

.snake-core {
  stroke: var(--neon-core);
  stroke-width: var(--tw-core);
  opacity: 0.85;
}

.snake-head-halo {
  fill: none;
  stroke: var(--neon);
  stroke-width: var(--tw-halo);
  opacity: var(--op-head-halo);
}

.areas-trail-tall .snake-halo,
.areas-trail-tall .tongue-halo,
.areas-trail-wide .snake-head-halo {
  display: none;
}

/* La cara: ojos claros con pupila de tinta, la ceja inclinada de la marca
   (un recorte del color del relleno que enoja el ojo), dos fosas y la
   lengua bifida roja con su nucleo claro. */
.snake-eye {
  fill: var(--neon-core);
  stroke: none;
}

.snake-pupil {
  fill: var(--neon-ink);
  stroke: none;
}

/* Con la misma especificidad que ".areas-trail path" (que apaga el fill de
   todos los trazos) la ceja no se pintaba nunca: va anclada al svg. */
.areas-trail .snake-brow {
  fill: var(--neon-ink);
  stroke: none;
}

.snake-nostril {
  fill: var(--neon);
  stroke: none;
  opacity: 0.8;
}

.snake-tongue {
  opacity: 0;
}

/* El color va en cada trazo y no en el descendiente generico: una regla
   sobre .snake-tongue path pesa mas que .tongue-core y le pisaba el nucleo
   claro. */
.snake-tongue path {
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tongue-halo {
  stroke: var(--tongue);
  stroke-width: var(--tw-halo);
  opacity: var(--op-halo);
}

.tongue-tube {
  stroke: var(--tongue);
  stroke-width: calc(var(--tw-tube) * 0.72);
}

.tongue-core {
  stroke: var(--tongue-core);
  stroke-width: var(--tw-core);
  opacity: 0.85;
}

/* Modo calmo: serpiente quieta, sin lengua. */
html.is-calm .snake-tongue {
  display: none;
}

@media (min-width: 64em) {
  .area-panel {
    min-height: 100vh;
    min-height: 100svh;
    padding: var(--sp-9) var(--gutter) var(--sp-10);
    grid-template-columns: repeat(12, minmax(0, 1fr));
    align-content: end;
  }

  .area-word {
    grid-column: 1 / 8;
    grid-row: 1;
    font-size: clamp(5rem, 13vw, 12rem);
    align-self: end;
    margin-bottom: 0;
  }

  /* La caja arranca una columna despues de donde termina la palabra: asi
     queda aire de verdad entre las dos, y sigue habiendo margen mientras
     Anton no llego y el fallback dibuja la palabra mas ancha. */
  .area-say {
    grid-column: 9 / 13;
    grid-row: 1;
    align-self: end;
    padding-bottom: 1rem;
  }
}

/* El pin con scrub queda limitado a (min-width: 1024px) and (pointer: fine).
   Una tablet tactil ancha se comia el riel horizontal de tres fotos a sangre,
   que es justo el gasto que no se paga en tactil. Ahi se queda el camino
   vertical, la unica animacion atada al scroll que se concede en tactil. Las
   dos consultas son complementarias: ningun ancho se queda sin trazo. */
@media (min-width: 64em) and (pointer: fine) {
  /* El cambio de camino cuelga de html.is-animated, igual que el riel
     horizontal: sin JavaScript, o en modo calmo, los tres paneles se quedan
     apilados (la misma disposicion vertical del celular, con el camino
     vertical dibujandose con el scroll), asi que el zigzag ancho no
     describe esa escena. Antes
     estas dos declaraciones estaban sueltas y en ese estado se apagaba el
     camino vertical y se pintaba una onda de 285px cruzando el medio de la
     foto de RECORDS, con los otros dos paneles sin ningun trazo. */
  html.is-animated .areas-trail-tall {
    display: none;
  }

  /* Uniforme, sin preserveAspectRatio="none": un tubo de neon no puede
     cambiar de grosor segun por donde pase. El alto sale del ancho (5:1) y
     lo que sobra lo recorta el escenario. */
  html.is-animated .areas-trail-wide {
    display: block;
    left: 0;
    top: 50%;
    width: 100%;
    height: auto;
    transform: translateY(-50%);
  }

  /* con JavaScript la escena se fija y los tres paneles corren en horizontal.
     Se mide en porcentaje del escenario, no en vw: vw incluye la barra de
     scroll, asi que a cada panel se le recortaba esa franja a la derecha
     durante todo el scrub y el tercer panel no llegaba a verse entero. */
  /* clip, no hidden: un contenedor con overflow hidden se puede desplazar
     por foco (tabular a un boton de RECORDS corria el escenario 1440 px y
     STUDIO no volvia a aparecer). Con clip el recorte es solo visual y el
     foco lo lleva main.js al panel con el scroll de la escena. */
  html.is-animated .areas-stage {
    overflow: hidden;
    overflow: clip;
  }

  html.is-animated .areas-track {
    display: flex;
    width: 300%;
  }

  html.is-animated .area-panel {
    flex: 0 0 33.3333%;
    width: auto;
  }
}

/* --------------------------------------------------------------------------
   10. Clases
   -------------------------------------------------------------------------- */

.clases {
  position: relative;
  display: grid;
  gap: var(--sp-7);
  padding-block: var(--scene-pad);
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.clases-photo {
  aspect-ratio: 4 / 5;
  --photo-veil: 0.28;
}

/* Las placas enmarcadas (con margen alrededor: esta, la de Records y las
   dos del ledger de Sounds) no funden el pie: ese degradado a --bg es para
   las placas a sangre, y sobre una caja con bordes duros arriba y a los
   lados dejaba el de abajo disuelto, tres tipos de borde en la misma
   escena. Las fotos del patio ya vienen horneadas con el cielo apagado. */
.clases-photo::before,
.records-media::before,
.ledger-main::before,
.ledger-drop::before {
  background: none;
}

.ficha {
  margin-top: var(--sp-7);
}

.ficha-row {
  display: grid;
  gap: 0.15rem;
  padding-block: 0.8rem;
  border-top: 1px solid var(--line);
  transition: border-color var(--t-base) var(--ease-out);
}

@media (min-width: 48em) {
  .ficha-row {
    grid-template-columns: 8.5rem minmax(0, 1fr);
    gap: var(--sp-4);
  }
}

.ficha-row:hover {
  border-top-color: var(--line-hot);
}

.ficha-row dt {
  color: var(--ink-3);
  font-size: var(--fs-small);
}

.ficha-row dd {
  color: var(--ink);
}

.temario {
  margin-top: var(--sp-7);
}

.temario li {
  display: grid;
  gap: 0.15rem;
  padding-block: 0.9rem;
  border-top: 1px solid var(--line);
  transition: border-color var(--t-base) var(--ease-out);
}

.temario li:hover {
  border-top-color: var(--line-hot);
}

.temario-tema {
  font-weight: 500;
  color: var(--ink);
}

.temario-dice {
  color: var(--ink-2);
  font-size: var(--fs-small);
  text-wrap: pretty;
}

.clases-actions {
  margin-top: var(--sp-6);
}

/* La placa sangra el margen izquierdo de verdad: la escena arranca en el
   borde de la ventana y solo el lado derecho respeta la reticula (el margen
   de centrado mas la canaleta). Antes quedaba a 48 px, ni sangrada ni
   alineada a los 112 px del resto. */
@media (min-width: 64em) {
  .clases {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--sp-8);
    align-items: start;
    max-width: none;
    margin-inline: 0;
    padding-inline: 0 calc(max(0px, (100% - var(--maxw)) / 2) + var(--gutter));
  }

  .clases-photo {
    grid-column: 1 / 6;
    aspect-ratio: 4 / 5;
    position: sticky;
    top: calc(var(--nav-h) + var(--sp-5));
  }

  .clases-say {
    grid-column: 7 / 13;
  }

  .temario li {
    grid-template-columns: 14rem minmax(0, 1fr);
    gap: var(--sp-4);
    align-items: baseline;
  }
}

/* --------------------------------------------------------------------------
   11. Configurador: elegi tu setup
   -------------------------------------------------------------------------- */

.setup {
  background: var(--bg-2);
  padding-bottom: var(--scene-pad);
}

.setup-body {
  display: grid;
  gap: var(--sp-6);
}

/* Un tablero, no una tarjeta: sin fondo propio (el marco de neon iba
   inscripto dentro de un rectangulo mas claro y se leia como tarjeta con
   borde); solo el foco radial y el marco sobre el fondo de la escena. */
.setup-stage {
  position: relative;
  display: grid;
  align-items: center;
  min-height: 11rem;
  padding: var(--sp-5) var(--sp-4);
}

/* El foco del tablero es un elemento propio que se DESPLAZA, no un gradiente
   que se vuelve a calcular. Antes se animaba la custom property --spot-x
   dentro del background, y eso repinta el tablero entero en cada cuadro: la
   regla de la casa deja animar solo transform, opacity, clip-path y
   stroke-dashoffset. Ahora el gradiente se pinta una sola vez y lo que cambia
   es un translateX. */
.setup-spot {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0.14;
  pointer-events: none;
}

.setup-spot > i {
  position: absolute;
  inset: 0;
  display: block;
  background: radial-gradient(42% 60% at 50% 45%, var(--neon-glow), transparent 72%);
  transform: translateX(var(--spot-shift, 0%));
  transition: transform var(--t-slow) var(--ease-out);
}

/* Con movimiento lo mueve GSAP cuadro a cuadro: si la transicion del CSS
   sigue viva, vuelve a suavizar cada escritura y el foco se arrastra. */
html.is-animated .setup-spot > i {
  transition: none;
}

/* El conjunto se apoya en una linea de base comun: las unidades crecen
   hacia arriba y el pie no se mueve. La altura queda reservada (min-height)
   para que cambiar de decks no mueva la seccion. */
.rig {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(0.4rem, 1.8vw, 1.1rem);
  flex-wrap: nowrap;
  width: 100%;
  min-height: min(13.5rem, 42vw);
  padding-bottom: 0.9rem;
}

/* Cada unidad reparte el ancho en proporcion a su medida real (mm de
   frente): XDJ-700 306, XDJ-1000MK2 305 pero mas profundo, mixers 320. A
   375px cuatro decks mas el mixer entran achicandose, sin desbordar. */
.rig-unit {
  --u: 306;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  flex: var(--u) 1 0;
  min-width: 0;
}

.rig-player {
  max-width: 9.25rem;
}

/* El XDJ-1000MK2 es el aparato mas grande de los dos (mismo frente, 30 %
   mas de fondo): se muestra un 8 % mas ancho para que la diferencia se lea.
   Sale del flyer, asi que nunca pasa de su resolucion real. */
.setup:has(#cfg-player-1000:checked) .rig-player {
  --u: 332;
  max-width: 10rem;
}

.rig-mixer {
  --u: 320;
  max-width: 9.75rem;
}

/* Los dos reproductores y los dos mixers viven apilados en la misma celda:
   la altura de la celda es la del mas alto de los dos, asi cambiar de
   modelo es un crossfade entre dos fotos reales y no mueve nada. */
.rig-shot {
  position: relative;
  display: grid;
  width: 100%;
  transform-origin: 50% 100%;
}

.rig-shot .gear {
  grid-area: 1 / 1;
  align-self: end;
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
  transition: opacity 350ms var(--ease-out);
}

/* Sombra de contacto: una elipse fija bajo cada pieza, pintada una vez.
   Nada de blur ni de filter; el pie oscuro es lo que la apoya en el tablero. */
.rig-shot::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.55rem;
  width: 112%;
  height: 1.1rem;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%,
    color-mix(in oklab, var(--bg) 92%, transparent) 0%,
    color-mix(in oklab, var(--bg) 55%, transparent) 45%,
    transparent 100%);
  pointer-events: none;
}

/* Un charco de luz leve, mas ancho, sobre la sombra: ata la pieza al neon. */
.rig-shot::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -0.9rem;
  width: 140%;
  height: 1.6rem;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, var(--neon-glow) 0%, transparent 100%);
  opacity: 0.22;
  pointer-events: none;
}

.rig-shot .gear-1000,
.rig-shot .gear-djm {
  opacity: 0;
}

.setup:has(#cfg-player-1000:checked) .rig-shot .gear-700 { opacity: 0; }
.setup:has(#cfg-player-1000:checked) .rig-shot .gear-1000 { opacity: 1; }

.setup:has(#cfg-mixer-djm:checked) .rig-shot .gear-xone { opacity: 0; }
.setup:has(#cfg-mixer-djm:checked) .rig-shot .gear-djm { opacity: 1; }

/* estados del configurador, resueltos en CSS: anda sin JavaScript */
.rig-p3,
.rig-p4 {
  display: none;
}

.setup:has(#cfg-decks-3:checked) .rig-p3,
.setup:has(#cfg-decks-4:checked) .rig-p3,
.setup:has(#cfg-decks-4:checked) .rig-p4 {
  display: flex;
}

/* Con cuatro decks la cabina real va dos, mixer, dos: el tercer reproductor
   pasa a la izquierda del mixer por orden de flex, sin tocar el DOM. El FLIP
   de main.js mide antes y despues del cambio, asi que viaja como los demas. */
.setup:has(#cfg-decks-4:checked) .rig-p3 {
  order: -1;
}

/* --------------------------------------------------------------------------
   11b. El borde de neon del tablero
   Un contorno de tubo alrededor del conjunto que se vuelve a dibujar en cada
   cambio. El halo es un segundo trazo mas ancho con opacidad .35: ni un blur.
   -------------------------------------------------------------------------- */

.rig-frame {
  position: absolute;
  inset: var(--sp-5) var(--sp-2);
  width: auto;
  height: auto;
  pointer-events: none;
  z-index: 2;
  overflow: visible;
  /* la regla global de img/svg lo achicaba al ancho del tablero y cortaba el
     lado derecho del contorno */
  max-width: none;
}

.frame-halo,
.frame-tube {
  fill: none;
  stroke: var(--neon);
  stroke-linejoin: round;
}

.frame-halo {
  stroke-width: 7;
  opacity: 0.35;
}

.frame-tube {
  stroke-width: 2;
}

html.is-animated .frame-halo,
html.is-animated .frame-tube,
html.is-calm .frame-halo,
html.is-calm .frame-tube {
  stroke-dasharray: 1000;
}

/* el foco sigue a la pieza que cambio, tambien sin JavaScript */
.setup:has(#cfg-decks-3:checked) .setup-spot > i {
  --spot-shift: 6%;
}

.setup:has(#cfg-decks-4:checked) .setup-spot > i {
  --spot-shift: 12%;
}

/* segmentos */
.seg {
  margin: 0 0 var(--sp-5);
  padding: 0;
  border: 0;
}

.seg legend {
  padding: 0;
  margin-bottom: 0.6rem;
  font-size: var(--fs-small);
  color: var(--ink-3);
}

/* .seg-input va absoluto: sin un ancestro posicionado se resuelve contra el
   bloque contenedor inicial y el foco de teclado salta la pagina entera. */
.seg-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  width: fit-content;
  max-width: 100%;
}

.seg-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.seg-label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0.5rem 1rem;
  font-weight: 500;
  font-size: 0.9375rem;
  color: var(--ink-2);
  cursor: pointer;
  border-left: 1px solid var(--line);
  transition: background-color var(--t-base) var(--ease-out),
              color var(--t-base) var(--ease-out),
              transform 100ms var(--ease-out);
}

/* presion, como en los botones: en el celular es la unica respuesta antes
   de que el cambio ocurra */
.seg-label:active {
  transform: scale(0.985);
}

html.is-calm .seg-label:active {
  transform: none;
}

.seg-row > .seg-label:first-of-type,
.seg-input:first-child + .seg-label {
  border-left: 0;
}

.seg-label:hover {
  color: var(--ink);
}

.seg-input:checked + .seg-label {
  background: var(--neon);
  color: var(--bg);
}

/* Foco de teclado visible en los dos estados: anillo de neon sobre la
   opcion apagada y anillo oscuro dentro del relleno sobre la encendida. */
.seg-input:focus-visible + .seg-label {
  outline: 2px solid var(--neon);
  outline-offset: -2px;
}

.seg-input:focus-visible:checked + .seg-label {
  outline: 2px solid var(--neon-ink);
  outline-offset: -4px;
}

.setup-readout {
  position: relative;
  margin-top: var(--sp-5);
  /* dos lineas reservadas: el texto mas largo no mueve lo de abajo */
  min-height: calc(2 * var(--lh-body) * 1em);
  font-weight: 500;
  font-size: var(--fs-lead);
  color: var(--ink);
}

/* El fantasma del texto anterior, para que el cambio sea un fundido. Lo
   crea el JS; sin JS no existe. */
.ro-ghost {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}

.ro,
.ro-sep {
  display: none;
}

.ro-sep {
  display: inline;
  color: var(--ink-3);
}

.setup:has(#cfg-mixer-xone:checked) .ro-mixer-xone,
.setup:has(#cfg-mixer-djm:checked) .ro-mixer-djm,
.setup:has(#cfg-decks-2:checked) .ro-decks-2,
.setup:has(#cfg-decks-3:checked) .ro-decks-3,
.setup:has(#cfg-decks-4:checked) .ro-decks-4,
.setup:has(#cfg-player-700:checked) .ro-player-700,
.setup:has(#cfg-player-1000:checked) .ro-player-1000 {
  display: inline;
}

.setup-note {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  color: var(--ink-2);
  text-wrap: pretty;
}

.setup-actions {
  margin-top: var(--sp-5);
}

@media (min-width: 64em) {
  .setup-body {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    column-gap: var(--sp-8);
    align-items: start;
  }

  /* El tablero abraza el marco de neon: con sp-9 arriba y abajo quedaban
     120 px de fondo plano por lado y el bloque se leia como una tarjeta. */
  .setup-stage {
    grid-column: 1;
    grid-row: 1;
    padding: var(--sp-6) var(--sp-5);
  }

  /* En un tablero de 650 px los recortes de 148 px quedaban como stickers
     con el marco vacio arriba y abajo: con dos decks cada pieza sube a
     unos 180 px y llena el alto reservado; con cuatro reparten el ancho. */
  .rig-player {
    max-width: 10.5rem;
  }

  .setup:has(#cfg-player-1000:checked) .rig-player {
    max-width: 11.25rem;
  }

  .rig-mixer {
    max-width: 11.75rem;
  }

  .setup-controls {
    grid-column: 2;
    grid-row: 1;
    align-self: center;
  }
}

/* --------------------------------------------------------------------------
   12. Records
   -------------------------------------------------------------------------- */

.records {
  position: relative;
  display: grid;
  gap: var(--sp-7);
  padding-block: var(--scene-pad);
}

.records-media {
  position: relative;
  aspect-ratio: 4 / 5;
  margin-inline: var(--gutter);
  --photo-veil: 0;
}

.plate > .records-video {
  display: none;
}

.records-say {
  max-width: var(--maxw);
}

.records-house {
  margin-top: var(--sp-6);
  color: var(--ink-2);
}

.records-actions {
  margin-top: var(--sp-6);
}

.sessions {
  margin-top: var(--sp-4);
}

.sessions li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.15rem var(--sp-4);
  padding-block: 0.9rem;
  border-top: 1px solid var(--line);
  transition: border-color var(--t-base) var(--ease-out);
}

.sessions li:hover {
  border-top-color: var(--line-hot);
}

.s-artist {
  font-weight: 500;
  color: var(--ink);
}

.s-format,
.s-genre {
  color: var(--ink-2);
  font-size: var(--fs-small);
}

/* Los unicos enlaces salientes de la escena: el alto de toque sale del
   min-height y el margen negativo lo devuelve, asi la fila no se estira.
   En el celular el enlace se centra sobre las tres lineas (artista, formato,
   genero), no solo sobre las dos primeras. */
.s-link {
  grid-column: 2;
  grid-row: 1 / span 3;
  align-self: start;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  margin-block: -0.6rem;
  color: var(--ink-3);
  font-size: var(--fs-small);
  font-weight: 500;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.28em;
  text-decoration-color: var(--line-hot);
  transition: color var(--t-base) var(--ease-out),
              text-decoration-color var(--t-base) var(--ease-out);
}

.sessions li:hover .s-link,
.s-link:focus-visible,
.s-link:active {
  color: var(--neon);
  text-decoration-color: var(--neon);
}

html.has-video .plate > .records-video {
  display: block;
}

html.has-video .plate > .records-still {
  display: none;
}

@media (min-width: 64em) {
  .records {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    align-items: start;
    max-width: var(--maxw);
    margin-inline: auto;
    padding-inline: var(--gutter);
    gap: var(--sp-8);
  }

  /* Cinco columnas como la placa de Clases, alta (4/5) con el loop
     vertical recortado por arriba y abajo: la columna 9/16 de 365 px dejaba
     170 px vacios hasta el texto y el loop tenido se leia como una mancha. */
  .records-media {
    grid-column: 1 / 6;
    aspect-ratio: 4 / 5;
    margin-inline: 0;
    align-self: start;
  }

  /* el recorte del loop vertical deja entero el letrero de arriba y la
     laptop, y se come el escritorio vacio de abajo */
  .records-media > .records-video {
    object-position: 50% 12%;
  }

  .records-say {
    grid-column: 6 / 13;
    padding-inline: 0;
  }

  .sessions li {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 0.9fr) auto;
    align-items: baseline;
    gap: var(--sp-4);
  }

  .s-link {
    grid-column: 4;
    grid-row: 1;
    align-self: baseline;
  }
}

/* --------------------------------------------------------------------------
   13. Sounds
   -------------------------------------------------------------------------- */

.sounds {
  display: grid;
  gap: var(--sp-7);
  padding-block: var(--scene-pad);
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  background: var(--bg);
}

.sounds-ledger {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

.ledger-main {
  aspect-ratio: 4 / 5;
  --photo-veil: 0.22;
}

/* la misma veladura verde del panel Sounds: la foto del patio sin tinte era
   un bloque gris donde no se distinguia el parlante */
.ledger-main::after,
.ledger-drop::after {
  background: color-mix(in oklab, var(--stage-2) 72%, var(--bg));
}

.ledger-side {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
}

/* El recorte de las columnas ya viene con alfa de verdad: la silueta de los
   dos parlantes, subida lo justo para que se lea sobre el tablero oscuro y
   pasada por la MISMA receta que las fotos. Antes la imagen no tenia un solo
   pixel opaco (era un screen horneado contra --stage puro) y, compuesta sobre
   esta baldosa, que es mas oscura, pintaba el cuadrado entero de verde claro:
   el parche mas brillante de la escena, al lado de dos fotos veladas. */
.ledger-cut {
  position: relative;
  background: color-mix(in oklab, var(--stage) 52%, var(--bg));
  border-radius: var(--radius);
  overflow: hidden;
}

.ledger-cut img {
  display: block;
  width: 100%;
  height: auto;
}


.ledger-drop {
  aspect-ratio: 3 / 4;
  --photo-veil: 0.3;
}

.sounds-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-5);
  margin-top: var(--sp-6);
}

/* A 375 los dos botones no entran en una fila: apilados a proposito, con el
   silencioso debajo y a la izquierda, en vez de caer donde pueda. */
@media (max-width: 47.99em) {
  .sounds-actions {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-3);
  }
}

.sounds .rule-list {
  margin-top: var(--sp-6);
  max-width: 34rem;
}

@media (min-width: 64em) {
  .sounds {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    align-items: start;
    gap: var(--sp-8);
  }

  .sounds-ledger {
    grid-column: 1 / 8;
    grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
    gap: var(--sp-5);
  }

  .ledger-side {
    margin-top: 4rem;
    grid-template-columns: minmax(0, 1fr);
  }

  .sounds-say {
    grid-column: 8 / 13;
    padding-top: var(--sp-7);
  }
}

/* --------------------------------------------------------------------------
   14. Showcase (la unica escena con ambar)
   -------------------------------------------------------------------------- */

.showcase {
  background: var(--bg-2);
  padding-bottom: var(--scene-pad);
}

/* El flyer del evento domina solo: a todo el ancho arriba y los tres de
   clases y cabina en una fila debajo. Antes el cuarto tambien iba a todo el
   ancho y el que dominaba al final era uno de clases. En el celular la fila
   de tres eran miniaturas de 100 px, iguales e ilegibles: van dos a la par y
   el ultimo a todo el ancho. */
.flyers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
}

.flyers .flyer:nth-of-type(4) {
  grid-column: 1 / -1;
}

@media (min-width: 48em) {
  .flyers {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .flyers .flyer:nth-of-type(4) {
    grid-column: auto;
  }
}

.flyer {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--stage);
  transform-style: preserve-3d;
  transition: border-color var(--t-base) var(--ease-out),
              transform 220ms var(--ease-out);
}

.flyer img {
  display: block;
  width: 100%;
  height: auto;
}

.flyer:hover {
  border-color: var(--amber);
  transform: perspective(900px) rotateX(2.5deg) rotateY(-3.5deg);
}

.flyer-hero {
  grid-column: 1 / -1;
}

.flyers-quote {
  grid-column: 1 / -1;
  margin-top: var(--sp-3);
}

.showcase-foot {
  padding-top: var(--sp-7);
}

.showcase-cast {
  margin-top: var(--sp-5);
  color: var(--ink-2);
  max-width: var(--measure);
}

.showcase-actions {
  margin-top: var(--sp-6);
}

@media (min-width: 64em) {
  .flyers {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--sp-5);
    align-items: start;
  }

  /* Las alturas cierran: el flyer grande ocupa seis columnas y dos filas
     (alto 1.25 x 660 = 825 px a 1440) y a su derecha van dos filas de flyers
     de tres columnas (2 x 397 + 24 de gap = 819 px). Antes el cuarto flyer
     era de cinco columnas y el grande terminaba cientos de pixeles antes que
     la segunda fila: un hueco que se leia como grilla sin terminar. La celda
     libre queda en la esquina inferior derecha, como aire, no en el medio. */
  .flyer-hero {
    grid-column: 1 / 7;
    grid-row: 1 / span 2;
  }

  .flyers .flyer:nth-of-type(2) {
    grid-column: 7 / 10;
    grid-row: 1;
  }

  .flyers .flyer:nth-of-type(3) {
    grid-column: 10 / 13;
    grid-row: 1;
  }

  .flyers .flyer:nth-of-type(4) {
    grid-column: 7 / 10;
    grid-row: 2;
  }

  /* la cita ocupa la celda libre, junto al cuarto flyer, en vez de dejar un
     hueco de tres columnas que se leia como grilla sin terminar */
  .flyers-quote {
    grid-column: 10 / 13;
    grid-row: 2;
    align-self: end;
    margin-top: 0;
    max-width: none;
    font-size: clamp(1.125rem, 1.5vw, 1.375rem);
  }

  .showcase-foot {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--sp-6);
    align-items: end;
  }

  .showcase-cast {
    grid-column: 1 / 8;
    margin-top: 0;
  }

  /* tres columnas y sin corte: en dos el boton se partia en dos lineas */
  .showcase-actions {
    grid-column: 10 / 13;
    justify-self: end;
    margin-top: 0;
  }

  .showcase-actions .btn {
    white-space: nowrap;
  }
}

/* --------------------------------------------------------------------------
   15. Reserva
   -------------------------------------------------------------------------- */

.booking {
  background: var(--bg);
  padding-bottom: var(--scene-pad);
}

.booking-body {
  display: grid;
  gap: var(--sp-6);
}

.booking-fallback {
  max-width: 44rem;
  padding: var(--card-pad);
  border: 1px solid var(--line);
  background: var(--stage);
  border-radius: var(--radius-lg);
}

.booking-app:not(:empty) + .booking-fallback {
  display: none;
}

.booking-fallback-title {
  font-weight: 500;
  font-size: var(--fs-lead);
}

.booking-fallback-txt {
  margin-top: var(--sp-4);
  max-width: var(--measure);
  color: var(--ink-2);
}

.booking-fallback-actions {
  display: grid;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}

.booking-fallback-note {
  margin-top: var(--sp-5);
  font-size: var(--fs-small);
  color: var(--ink-3);
}

@media (min-width: 48em) {
  .booking-fallback-actions {
    grid-auto-flow: column;
    justify-content: start;
  }
}

/* --------------------------------------------------------------------------
   16. Encontranos y cierre
   -------------------------------------------------------------------------- */

.contacto {
  position: relative;
  overflow: hidden;
  padding-block: var(--scene-pad);
  background: var(--bg-2);
}

/* A 375 la cabeza de la serpiente de cierre caia justo detras de los tres
   ultimos enlaces: la escena baja un poco mas de pie para que la lista
   termine antes de que empiece la cabeza. */
@media (max-width: 63.99em) {
  .contacto {
    padding-bottom: calc(var(--scene-pad) + 22vw);
  }
}

/* Esta si se sale por el borde inferior, a proposito. A 375px,
   con bottom -30%, entraba solo un arco verde con dos puntos y dejaba de
   leerse como serpiente: sube lo justo para que entren la cabeza y una curva
   del cuerpo, y el recorte de abajo se mantiene. */
.fig-foot {
  position: absolute;
  right: -24%;
  bottom: -8%;
  width: min(86vw, 22rem);
  z-index: 0;
  opacity: 0.22;
  pointer-events: none;
}

.contacto-inner {
  position: relative;
  z-index: 1;
}

.contacto-cols {
  display: grid;
  gap: var(--sp-7);
  margin-top: var(--sp-6);
}

.contacto-col .sub-title {
  margin-top: 0;
}

.contacto-dir {
  margin-top: var(--sp-4);
  font-size: var(--fs-lead);
  max-width: 26rem;
  text-wrap: pretty;
}

.contacto-horario {
  margin-top: var(--sp-4);
  color: var(--ink-2);
}

.contacto-col p + p {
  margin-top: var(--sp-4);
}

.contacto-actions {
  display: grid;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  max-width: 22rem;
}

.contacto-links {
  margin-top: var(--sp-5);
  display: grid;
  gap: 0.6rem;
  color: var(--ink-2);
}

.site-foot {
  padding-block: var(--sp-6);
  border-top: 1px solid var(--line);
  color: var(--ink-3);
  font-size: var(--fs-small);
  background: var(--bg);
}

@media (min-width: 64em) {
  .contacto-cols {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--sp-9);
    max-width: 56rem;
  }

  /* La serpiente ocupa el aire a la derecha de las dos columnas: su borde
     izquierdo queda pasando el boton de eventos y solo la cola se corta por
     abajo. Antes arrancaba en x 800 y la cabeza cruzaba por detras del
     boton, como un solapamiento accidental. */
  .fig-foot {
    right: -8%;
    bottom: -30%;
    width: 40%;
    max-width: 40rem;
  }
}

/* --------------------------------------------------------------------------
   16b. Pagina no encontrada (404.html): mismas hojas, sin nada en linea
   -------------------------------------------------------------------------- */

.notfound {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding-block: var(--scene-pad);
}

.notfound-mark {
  display: block;
  width: 4.5rem;
  height: auto;
  margin-bottom: var(--sp-6);
}

.notfound-actions {
  margin-top: var(--sp-6);
}

/* --------------------------------------------------------------------------
   17. Cursor luz (solo puntero fino, solo con JavaScript)
   -------------------------------------------------------------------------- */

/* La luz es una capa de 520px que se mueve con transform. Antes era un
   pseudoelemento a pantalla completa cuyo gradiente se recalculaba en cada
   cuadro: eso repinta la ventana entera justo encima de la escena fijada.
   Tampoco lleva mix-blend-mode: sobre un fondo casi negro, el screen al 10 %
   daba lo mismo que la composicion normal y era la ultima capa con mezcla
   viva del sitio (regla dura: cero mix-blend-mode). */
.cursor-light {
  position: fixed;
  top: 0;
  left: 0;
  width: 520px;
  height: 520px;
  margin: -260px 0 0 -260px;
  z-index: 70;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(closest-side, var(--neon-glow), transparent 100%);
  transition: opacity var(--t-slow) var(--ease-out);
}

@media (pointer: fine) {
  html.is-animated .cursor-light {
    opacity: 0.1;
  }
}

/* --------------------------------------------------------------------------
   18. Movimiento reducido = modo calmo
   Con prefers-reduced-motion el sitio no se apaga: se quita solo lo
   vestibular y lo que sigue al puntero, y el resto sigue vivo por fundido.
   Antes esta seccion ponia TODAS las transiciones y animaciones en 0.01 ms,
   y el cliente vio el sitio estatico (sin video ni entrada) en una laptop
   con los efectos apagados. Ahora las transiciones de hover, foco y presion
   corren a su duracion normal, y solo se apagan:
     - las animaciones en bucle (la respiracion del halo, que queda encendido
       fijo, y la flecha del hero);
     - el tilt por hover de los flyers (un giro), la luz del cursor y el
       scroll suave del ancla.
   Lo demas lo decide main.js con la clase html.is-calm (que reemplaza a
   html.is-animated en este modo): reveals por opacidad, la serpiente de las
   areas quieta con el letrero entero en disposicion vertical, preloader sin
   vuelo,
   configurador sin FLIP, menu por fundido, video encendido.
   Dos entradas al mismo estado: la consulta del sistema (vale tambien sin
   JavaScript y antes de que cargue) y la clase que pone main.js, que ademas
   cubre ?movimiento=0. html.force-motion (?movimiento=1) anula la consulta.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html:not(.force-motion) {
    scroll-behavior: auto;
  }

  html:not(.force-motion) .fig-hero .neon-halo,
  html:not(.force-motion) .fig-sign .neon-halo,
  html:not(.force-motion) .fig-foot .neon-halo,
  html:not(.force-motion) .fig-menu .neon-halo {
    animation: none;
    opacity: 1;
  }

  html:not(.force-motion) .arrow-stem {
    animation: none;
    stroke-dasharray: none;
  }

  html:not(.force-motion) .flyer:hover {
    transform: none;
  }

  html:not(.force-motion) .cursor-light {
    display: none;
  }
}

html.is-calm {
  scroll-behavior: auto;
}

html.is-calm .fig-hero .neon-halo,
html.is-calm .fig-sign .neon-halo,
html.is-calm .fig-foot .neon-halo,
html.is-calm .fig-menu .neon-halo {
  animation: none;
  opacity: 1;
}

html.is-calm .arrow-stem {
  animation: none;
  stroke-dasharray: none;
}

html.is-calm .flyer:hover {
  transform: none;
}

html.is-calm .cursor-light {
  display: none;
}

/* Preloader calmo: la serpiente del hero se dibuja a si misma en su sitio.
   Mientras carga, la rejilla del hero sube por encima del velo (el texto
   esta en opacidad 0 hasta que termina la intro, asi que solo se ve el
   letrero) y no recibe clics. Al salir de is-loading vuelve a su capa. */
html.is-loading.is-calm .hero-grid {
  z-index: calc(var(--z-pre) + 1);
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   19. motion
   Estados que necesita main.js. Todo lo de aca abajo cuelga de html.has-js,
   de html.is-animated (movimiento completo) o de html.is-calm (modo calmo):
   sin JavaScript nada de esto se aplica y el sitio sigue en su estado final.
   Lo que cuelga solo de is-animated es lo que el modo calmo apaga: riel
   horizontal, serpiente en movimiento, cursor luz, relleno magnetico,
   hairlines que se trazan, perspectiva del configurador y tilt.
   -------------------------------------------------------------------------- */

/* Lenis maneja el scroll: el smooth nativo tiene que salir del camino. */
html.lenis,
html.lenis body {
  height: auto;
}

html.lenis {
  scroll-behavior: auto;
}

/* lenis-stopped no lleva overflow: hidden. Lenis se detiene solo con el
   menu abierto y durante la intro, y esos dos estados ya bloquean el scroll
   (ver html.is-loading); ademas con barra clasica el hidden corria todo el
   ancho 15 px. */

html.lenis [data-lenis-prevent] {
  overscroll-behavior: contain;
}

/* El modo del letrero guardado se aplica sin transicion al cargar. */
html.is-booting {
  transition: none;
}

/* Preloader: el escenario vive sobre el velo para que el fondo se apague y
   la serpiente siga encendida mientras vuela al hero. Solo con movimiento
   completo: en modo calmo no hay escenario (ver 18). */
.preloader-stage {
  position: fixed;
  inset: 0;
  z-index: 101;
  display: grid;
  place-items: center;
  padding: var(--gutter);
  pointer-events: none;
}

.preloader-neon {
  display: grid;
  justify-items: center;
  gap: var(--sp-4);
  width: min(58vw, 20rem);
}

/* El preloader clona las figuras del hero con la clase fig-pre: asi no
   heredan la respiracion del CSS y la linea de tiempo puede escribir la
   opacidad del halo sin pelearse con una animacion. */
.preloader-neon .fig-pre {
  width: 100%;
}

.preloader-neon .fig-pre-sign {
  width: 78%;
}

@media (min-width: 64em) {
  .preloader-neon {
    width: min(38vw, 29rem);
  }
}

/* Menu: con JavaScript manda la clase, no el :target. Asi cierra sin tocar
   el hash y sin pegar un salto de scroll. */
html.has-js .menu {
  visibility: hidden;
  opacity: 0;
}

html.has-js .menu.is-open {
  visibility: visible;
  opacity: 1;
  transition: opacity var(--t-base) var(--ease-out);
}

/* Barra: al pegarse, la serpiente mini se achica un punto. */
html.is-animated .nav-mark {
  transform-origin: left center;
  transition: transform var(--t-base) var(--ease-out);
}

html.is-animated .nav.is-stuck .nav-mark {
  transform: scale(0.92);
}

/* Botones: el relleno entra por el lado por el que entro el puntero. */
html.is-animated .btn:not(.btn-quiet)::before {
  transform-origin: var(--fill-origin, left) center;
}

/* Hairlines que se trazan: con movimiento, la linea pasa del borde a un
   trazo propio que main.js estira de izquierda a derecha. */
html.is-animated [data-reveal="line"] {
  position: relative;
  border-top-color: transparent;
}

html.is-animated [data-reveal="line"]::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: var(--line);
  transform: scaleX(var(--rule, 1));
  transform-origin: left center;
  transition: background-color var(--t-base) var(--ease-out);
}

html.is-animated [data-reveal="line"]:hover::before {
  background: var(--line-hot);
}

/* Escena de las areas: el camino y la serpiente viajan en un riel propio.
   Tambien en modo calmo: ahi el camino vertical se pega al borde derecho con
   un margen negativo y sin este recorte asomaba 18 px fuera del documento. */
html.is-animated .areas-stage,
html.is-calm .areas-stage {
  overflow: hidden;
  overflow: clip;
}

/* El riel lleva el trazo y la serpiente. Va DENTRO de .areas-track y en
   z-index 1: por delante de las fotos (0) y por detras de las palabras
   gigantes (2) y de las cajas de texto (3), que es el orden que pide la
   escena.
   Tiene que ser hijo del track y no hermano: en escritorio GSAP le escribe un
   transform al track, y un elemento transformado abre su propio contexto de
   apilado que se pinta como z-index 0. Desde afuera, cualquier z-index del
   riel quedaba por encima del track ENTERO, palabras y texto incluidos, y el
   tubo cruzaba por delante de RECORDS. Adentro comparten contexto y el orden
   vuelve a ser el declarado. Por eso tampoco lleva ancho propio: hereda el
   300 % del track. */
html.is-animated .areas-rail {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
  /* La capa propia (will-change: transform, para que el d de la silueta y el
     dashoffset del rastro no invaliden la capa de las tres fotos a sangre)
     la escribe main.js solo mientras la escena esta activa: fija en el CSS
     era una capa de 4734x900 viva durante toda la sesion. */
}

/* El camino baja por el margen derecho, despegado cuatro pixeles del borde
   para que el hocico y los ojos no queden cortados en el zigzag. La consulta
   es la misma que usa main.js para la rama vertical, puntero incluido: vale
   en la tablet tactil ancha y en una pantalla ancha sin puntero, que se
   quedan con el camino vertical porque ahi no se arma el pin.
   En vertical la serpiente pasa por delante de la veladura de la caja de
   texto (el riel sube a 4) y la palabra gigante sube a 5 para seguir por
   encima: si no, la cabeza se perdia detras de la veladura durante mas de la
   mitad de cada panel. En escritorio se queda el orden de siempre. */
@media (max-width: 63.99em), (min-width: 64em) and (pointer: coarse), (min-width: 64em) and (pointer: none) {
  html.is-animated .areas-trail-tall,
  html.is-calm .areas-trail-tall {
    right: 4px;
  }

  /* el riel mide lo que mide el camino (unos 120 px), no el escenario: asi
     la capa compuesta es de 120x2200 y no de 375x2200 */
  html.is-animated .areas-rail {
    left: auto;
    right: 0;
    width: 7.5rem;
    z-index: 4;
  }

  html.is-animated .area-word {
    z-index: 5;
  }

  /* El carril: la caja de texto deja libres unos 56px a la derecha, que es
     por donde baja la serpiente (zigzag entre 60 y 100 de la guia). Asi el
     tubo nunca pasa por encima de una letra. */
  html.is-animated .area-say {
    padding-right: 3.5rem;
  }
}

/* En modo calmo no hay riel: el camino vertical cuelga del escenario en
   cualquier ancho, con el mismo orden de apilado y el mismo carril. */
html.is-calm .areas-trail-tall {
  right: 4px;
  z-index: 4;
}

html.is-calm .area-word {
  z-index: 5;
}

html.is-calm .area-say {
  padding-right: 3.5rem;
}

/* Configurador: el swap del mixer necesita profundidad. */
html.is-animated .rig {
  perspective: 900px;
}

/* Flyers: el tilt lo lleva el puntero, no un hover fijo. */
html.is-animated .flyer:hover {
  transform: none;
}

/* Con movimiento, el tilt lo escribe GSAP cuadro a cuadro: si la transicion
   de CSS sigue viva, vuelve a suavizar cada escritura y el flyer se arrastra
   detras del puntero. */
html.is-animated .flyer {
  transition: border-color var(--t-base) var(--ease-out);
}
