/* ============================================================
   EPIC VIEW — design system
   ink black / blood red / cream. Brutal contrast, no warm grays.
   ============================================================ */

:root {
  --ink: #060606;
  /* E17 ⑦: #0b0b0c sobre #060606 eran 5 niveles de luminancia — un escalón
     que no se veía. Ferrari salta #181818 → #303030 para sus tarjetas. */
  --ink-2: #121212;
  --red: #e0091d;
  --red-deep: #8f0413;
  --cream: #f4efe6;
  --font-display: 'Anton', Impact, sans-serif;
  --font-wordmark: 'Clash Display', 'Bricolage Grotesque', 'Anton', Impact, sans-serif;
  /* E17 ⑥: una sola voz de texto. Bricolage ya era el cuerpo de los pilares
     (E7H); ahora lo es de todo — nav, tarjetas, botones, footer, módulos —
     y Space Grotesk deja de pedirse (tres archivos menos). */
  --font-body: 'Bricolage Grotesque', 'Space Grotesk', 'Segoe UI', sans-serif;
  /* E17 ② — escalera de tracking. Las etiquetas iban de .22 a .42em (4–6 px
     entre letras a 10 px), la lectura "HUD de videojuego" señalada en E7F.
     Referencia Ferrari: CTA 1,4 px (~.10em), nav .65 px. Dos pasos y un
     tercero ANCHO reservado al subtítulo del hero, donde sí es identidad. */
  --track-label: .14em;
  --track-btn: .16em;
  --track-wide: .42em;
  /* E17 ⑤ — espaciado de secciones de flujo en px con tope, no en vh puro:
     16vh eran 115 px en un monitor de 720 y 173 en uno de 1080. */
  --sp-md: clamp(32px, 5vh, 56px);
  --sp-lg: clamp(48px, 8vh, 88px);
  --sp-xl: clamp(64px, 12vh, 128px);
}

/* the EPIC VIEW wordmark: Clash Display — geometric with sharp cuts,
   aggressive but refined (Monument Extended / Aeonik are paid licenses) */
.hero-title, .pre-brand, .brand, .foot-brand {
  font-family: var(--font-wordmark);
  font-weight: 600;
  letter-spacing: .02em;
}

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

html { scroll-behavior: auto; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* El ANCLAJE DE SCROLL de Chrome está activo por defecto: cuando algo por
   encima del viewport cambia de alto, el navegador corrige `scrollTop` solo
   para dejar quieto un elemento "ancla". Con los pines de ScrollTrigger eso
   se pelea con el scroll —los spacers se recalculan— y el efecto es que la
   página salta hacia arriba a mitad de gesto y hay que volver a scrollear.
   Pega sobre todo en la ÚLTIMA sección pineada, que es la que acumula más
   altura de spacers por encima. Aquí manda el scroll, no la heurística. */
html, body { overflow-anchor: none; }

body {
  background: var(--ink);
  color: var(--cream);
  font-family: var(--font-body);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* custom scrollbar — the default one breaks the immersion */
html { scrollbar-width: thin; scrollbar-color: var(--red) #0a0a0a; }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #0a0a0a; }
::-webkit-scrollbar-thumb { background: var(--red); }
::-webkit-scrollbar-thumb:hover { background: #ff2338; }

::selection { background: var(--red); color: var(--ink); }

a { color: inherit; text-decoration: none; }
button { font-family: inherit; background: none; border: none; color: inherit; }

@media (pointer: fine) {
  body, a, button, .card { cursor: none; }
}

/* magnetic anchors need a box for gsap transforms; media queries can still hide them */
a[data-magnetic] { display: inline-block; }

/* ============ PRELOADER → PORTADA ============
   E7F 3.1: REDISEÑO. Era lo primero que ve cualquier visitante y se leía como
   interfaz de videojuego. Tres causas concretas, las tres eliminadas:

   · el RECUADRO de filete fino alrededor de ENTRAR (dos pseudo-elementos con
     bordes rojos formando corchetes). Un botón dibujado con un marco es
     lenguaje de menú, no de marca. Lo sustituye un filete bajo la palabra —
     el mismo recurso que ya usa el sitio bajo cada antetítulo.
   · la SOMBRA DESPLAZADA del wordmark (-4px rojo / +4px crema): una aberración
     cromática de estética retro-glitch. Fuera. Ahora que Clash Display sí
     carga en esta página (E7F 2.1), la letra se sostiene sola.
   · el PUNTO ROJO LATIENDO. Fuera (ver index.html).

   Lo que queda es una portada: wordmark, un filete, la acción, y una nota al
   pie. Cuatro elementos, mucho aire, jerarquía sin discusión. */
#preloader {
  position: fixed; inset: 0; z-index: 1000;
  background: var(--ink);
  display: flex; align-items: center; justify-content: center;
  transition: opacity .6s ease .15s, visibility .6s ease .15s;
}
#preloader.done { opacity: 0; visibility: hidden; pointer-events: none; }
.pre-inner { width: min(560px, 82vw); text-align: center; }
.pre-brand {
  font-family: var(--font-wordmark);
  font-weight: 600;
  font-size: clamp(3rem, 9vw, 6rem);
  /* E7F C1: .01em → .02em, el mismo del wordmark en nav, footer y carta roja. */
  line-height: .9; letter-spacing: .02em;
  color: var(--cream);
  margin-bottom: clamp(34px, 5.5vh, 60px);
}
/* La barra de carga ya no desaparece al terminar: se queda como FILETE, que es
   lo que separa el wordmark de la acción y sostiene la composición. Antes se
   desvanecía y dejaba un hueco, y el botón lo tapaba subiendo con un
   `margin-top: -3.2em` — un parche que ya no hace falta. */
.pre-bar {
  width: min(210px, 42vw);
  height: 1px;
  margin: 0 auto clamp(18px, 2.6vh, 26px);
  background: rgba(244, 239, 230, .14);
  overflow: hidden;
  transition: width .8s cubic-bezier(.16, 1, .3, 1);
}
#pre-fill {
  display: block; height: 100%; width: 100%;
  background: var(--red);
  transform: scaleX(0); transform-origin: left;
}
.pre-status {
  /* E17 ①②: 9,9 px → 11 px, tracking .38 → .18em, alfa .38 → .5 */
  font-size: .69rem; letter-spacing: .18em;
  color: rgba(244, 239, 230, .5);
  padding-left: .18em;
  transition: opacity .45s ease;
}
#pre-pct { color: var(--red); }
/* al terminar la carga solo se va el TEXTO de estado; el filete se queda y se
   recoge un poco, que es el gesto de "listo" */
/* E7G 1: al terminar la carga el texto de estado no solo se apaga — COLAPSA.
   Antes se quedaba ocupando su renglón invisible entre el filete y ENTRAR, y
   ese hueco fantasma (82 px medidos) se leía como un error de maquetación.
   Ahora el aire entre filete y palabra queda en ~46 px, del mismo orden que
   los 49 px entre wordmark y filete: se lee como ritmo, no como fallo. */
#preloader.ready .pre-status { opacity: 0; height: 0; overflow: hidden; }
/* E8b: la barra de carga ya NO se queda como filete. Quedaban dos líneas
   rojas —la barra a 65 px por encima de ENTRAR y el subrayado de la propia
   palabra— y la de arriba se leía como un resto, no como composición (lo
   señaló el cliente). Al terminar la carga la barra se recoge y desaparece;
   el único filete es el que sostiene a ENTRAR. */
#preloader.ready .pre-bar { width: 0; opacity: 0; margin-bottom: 0; transition: width .6s cubic-bezier(.16, 1, .3, 1), opacity .45s ease .1s, margin .6s ease; }
#preloader.ready #enter-btn { margin-top: clamp(4px, .8vh, 10px); }

#enter-btn {
  position: relative;
  margin: clamp(16px, 2.4vh, 26px) auto 0;
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(14px, 2vh, 20px);
  padding: 4px 10px;
  background: none; border: 0;
  opacity: 0; pointer-events: none;
  transition: opacity .6s ease .25s;
}
#preloader.ready #enter-btn { opacity: 1; pointer-events: auto; }
.enter-label {
  position: relative;
  font-family: var(--font-body);
  /* 0.52em era el tracking más abierto de todo el sitio y hacía que la palabra
     se deshiciera en letras sueltas. 0.38em es el valor que ya usan el HUD y
     los antetítulos: la misma voz, no una excepción. */
  font-size: 1.02rem; font-weight: 500; letter-spacing: .38em;
  color: var(--cream); padding-left: .38em;
  padding-bottom: 12px;
}
/* EL FILETE. Sustituye al recuadro: nace del ancho del texto, no lo encierra.
   Al pasar el ratón crece hacia los lados y se enciende — un gesto, no un
   estado de botón. */
.enter-label::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: rgba(224, 9, 29, .55);
  transition: background .35s ease, left .45s cubic-bezier(.16, 1, .3, 1), right .45s cubic-bezier(.16, 1, .3, 1);
}
#enter-btn:hover .enter-label::after,
#enter-btn:focus-visible .enter-label::after {
  left: -14px; right: -14px; background: var(--red);
}
#enter-btn:focus-visible { outline: none; }
#enter-btn small {
  font-size: .69rem; letter-spacing: .18em;
  color: rgba(244, 239, 230, .42);
  padding-left: .18em;
}

/* ============ NAV ============ */
#nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 500;
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px clamp(18px, 3vw, 44px);
  background: linear-gradient(180deg, rgba(6,6,6,.85), transparent);
}
.brand {
  font-family: var(--font-wordmark);
  font-weight: 600;
  font-size: 1.35rem; letter-spacing: .02em; color: var(--cream);
}
.brand sup { color: var(--red); font-size: .6em; }
.nav-links { display: flex; align-items: center; gap: clamp(16px, 2.6vw, 40px); }
.nav-links a {
  font-size: .72rem; font-weight: 600; letter-spacing: var(--track-label);
  color: rgba(244, 239, 230, .8);
  transition: color .2s;
}
.nav-links a:hover { color: var(--red); }

/* sound toggle: a small secret — the dock carries it from center-bottom
   to the top-right corner as the page starts moving */
#sound-dock {
  position: fixed; left: 0; top: 0; z-index: 600;
  will-change: transform;
  display: flex; align-items: center; gap: 9px;
}
.dock-label {
  font-size: .69rem; letter-spacing: var(--track-btn);
  color: rgba(244, 239, 230, .55);
  pointer-events: none;
}
#sound-toggle {
  position: relative;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  opacity: .85;
  transition: opacity .3s;
}
#sound-toggle:hover { opacity: 1; }
#sound-toggle::before {
  content: '';
  position: absolute; inset: 6px;
  border: 1px solid rgba(224, 9, 29, .35);
  border-radius: 50%;
  transition: border-color .3s, box-shadow .3s;
}
#sound-toggle:hover::before { border-color: var(--red); }
#sound-toggle.on::before {
  border-color: rgba(224, 9, 29, .75);
  box-shadow: 0 0 12px 1px rgba(224, 9, 29, .22);
}
/* micro-pulse until first interaction */
#sound-toggle:not(.used)::before { animation: stpulse 2.6s ease-in-out infinite; }
@keyframes stpulse {
  0%, 100% { transform: scale(1); opacity: .5; box-shadow: 0 0 0 0 rgba(224, 9, 29, 0); }
  50% { transform: scale(1.16); opacity: 1; box-shadow: 0 0 14px 2px rgba(224, 9, 29, .28); }
}
.st-bars { display: inline-flex; align-items: flex-end; gap: 2.5px; height: 12px; }
.st-bars i {
  width: 2px; height: 3px; background: var(--red); display: block;
  opacity: .8;
  animation: stbar .9s ease-in-out infinite;
  animation-play-state: paused;
}
.st-bars i:nth-child(2) { animation-delay: .15s; }
.st-bars i:nth-child(3) { animation-delay: .3s; }
.st-bars i:nth-child(4) { animation-delay: .45s; }
#sound-toggle.on .st-bars i { animation-play-state: running; }
@keyframes stbar { 0%, 100% { height: 3px; } 50% { height: 12px; } }

/* ============ PINNED SECTIONS ============ */
/* E8b — LA BANDA NEGRA DEL PIE Y LOS TAMAÑOS QUE NO CABÍAN, MEDIDOS EN LAS
   CAPTURAS DEL CLIENTE. En su iPhone la sección pineada medía 657 px pero la
   unidad vh valía 7,41 px (741): los cuerpos en vh crecían para un viewport de
   741 dentro de una caja de 657 (el párrafo del pilar 03 se metía en el
   módulo) y, con la barra del navegador recogida, quedaban 84 px de nada
   debajo de cada sección ("hay un espacio negro abajo siempre").

   La causa era la cadena de alturas de abajo: en un Safari sin lvh gana
   `calc(var(--vh) * 100)`, y --vh se medía con innerHeight AL CARGAR — con
   la barra desplegada, el viewport CHICO. Justo lo contrario de lo que v27
   decidió (viewport grande, la barra se absorbe con el recorte del video).

   Corrección: el respaldo es 100vh a secas. En Safari iOS —con o sin lvh— y
   en Chrome Android, 100vh ES el viewport grande, el mismo que lvh, así que
   la sección y las unidades vh de su interior vuelven a hablar del mismo
   alto. --vh deja de escribirse (main.js / corp.js). */
.pin-sec {
  position: relative; overflow: hidden;
  /* v27: lvh (LARGE viewport) — identical to vh on desktop. dvh regressed on
     phones: ignoreMobileResize freezes the pinned height at the bar-visible
     measure, so once the URL bar collapsed a dead band opened at the bottom.
     lvh sizes the section to the barless viewport: full-bleed after the first
     scroll, and while the bar shows the cover-fit crop absorbs the overflow. */
  height: 100vh;
  height: 100lvh;
  background: var(--ink);
}
.seq-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: block;
}
.seq-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
}
/* native master playback during the release (full codec framerate) */
.core-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
}
/* hero overlay layers: real dust footage + generated grains + glitch reveal */
.dust-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 2; pointer-events: none;
  mix-blend-mode: screen; /* only the particles read, never the clip's black */
  opacity: .9; /* the user's edit already bakes the right level in */
  will-change: transform;
}
.dust {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 2; pointer-events: none;
}
.glitch-reveal {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 3; pointer-events: none;
}
/* offscreen but decoding — display:none would stall drawImage frames */
#glitch-src {
  position: fixed; width: 2px; height: 2px;
  opacity: 0.01; pointer-events: none; z-index: -1;
}
.sec-veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, rgba(6,6,6,.6) 0%, rgba(6,6,6,0) 28%, rgba(6,6,6,0) 62%, rgba(6,6,6,.78) 100%);
}
/* peek strip: proportional preview of what comes below the pinned section */
.peek {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  border-top: 1px solid rgba(224, 9, 29, .55);
  background: rgba(6, 6, 6, .82);
  padding: 13px clamp(20px, 6vw, 90px);
  translate: 0 100%;
  opacity: 0;
  pointer-events: none;
}
.peek span {
  font-size: .66rem; letter-spacing: .34em;
  color: var(--red);
  white-space: nowrap;
}

/* hero: deeper blacks — lighter center wash, much stronger edge vignette */
#hero .sec-veil {
  background:
    radial-gradient(ellipse 115% 85% at 50% 46%, rgba(6,6,6,0) 38%, rgba(6,6,6,.35) 68%, rgba(6,6,6,.82) 100%),
    linear-gradient(180deg, rgba(6,6,6,.42) 0%, rgba(6,6,6,0) 24%, rgba(6,6,6,0) 66%, rgba(6,6,6,.85) 100%);
}

/* ============ HERO ============ */
/* hero outro: cinematic red card that covers the video's last frames —
   small, strong, clean typography (no heavy borders or generic shadows) */
.hero-outro {
  position: absolute; inset: 0; z-index: 5;
  background: var(--red);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; /* driven by scroll progress */
  pointer-events: none;
}
.outro-inner { text-align: center; padding: 0 6vw; }
.hero-title {
  font-size: clamp(2.4rem, 6.6vw, 6rem);
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}
.hero-title .ltr {
  display: inline-block;
  opacity: 0;
  will-change: opacity;
}
.hero-title .ltr.on { opacity: 1; }
.hero-title .ltr.flick { animation: glitchflick-soft 90ms steps(2, end) 1; }
@keyframes glitchflick-soft {
  0% { transform: translate(-2px, 1px); opacity: .55; clip-path: inset(10% 0 28% 0); }
  50% { transform: translate(2px, -1px); opacity: .85; clip-path: inset(46% 0 6% 0); }
  100% { transform: none; opacity: 1; clip-path: none; }
}
.letterbox {
  position: absolute; left: 0; right: 0; height: 12vh;
  background: #000; z-index: 6; pointer-events: none;
  transform: scaleY(0);
}
.lb-top { top: 0; transform-origin: top; }
.lb-bottom { bottom: 0; transform-origin: bottom; }
@keyframes glitchflick {
  0% {
    text-shadow: -7px 0 var(--red), 7px 0 rgba(244,239,230,.4);
    transform: translate(-4px, 2px) skewX(-8deg);
    clip-path: inset(12% 0 31% 0);
  }
  50% {
    text-shadow: 6px 0 var(--red), -6px 0 rgba(244,239,230,.35);
    transform: translate(3px, -2px) skewX(6deg);
    clip-path: inset(52% 0 8% 0);
  }
  100% { text-shadow: none; transform: none; clip-path: none; }
}
.hero-sub {
  margin-top: 2.6vh;
  font-family: var(--font-wordmark);
  font-weight: 300; /* extra-light against the bold title = elegant contrast */
  font-size: clamp(.72rem, 1.15vw, .98rem);
  letter-spacing: var(--track-wide); /* E17 ②: el único momento ancho del sitio */
  text-transform: uppercase;
  color: rgba(6, 6, 6, .82);
  opacity: 0; /* driven proportionally by scroll progress */
}
.hero-sub em { font-style: normal; color: var(--cream); }

/* HUD scroll indicator: precision sci-fi piece in the negative space,
   right side — section markers + progress stroke framed by brackets */
#hud {
  /* E7G 3: más adentro y algo mayor. A 1440 pasa de 75 a 101 px del borde. */
  position: fixed; right: clamp(46px, 7vw, 118px); top: 50%;
  translate: 0 -50%;
  z-index: 520;
  display: flex; flex-direction: column; align-items: center; gap: 19px;
  padding: 12px 17px; /* generous grab area for drag-to-scroll */
  pointer-events: auto;
  user-select: none; -webkit-user-select: none;
  touch-action: none;
  cursor: none; /* the custom red ring stays in charge */
}
/* final HUD form (confirmed by sketch): plain line above — SCROLL — arrow
   below. One opacity pulse flows top line → through the word → down to the
   arrow tip, as a single continuous run (3.4s cycle). */
.hud-line {
  position: relative;
  width: 3.4px; height: min(238px, 27vh); /* equal to the arrow below */
  background-image:
    linear-gradient(180deg, transparent, var(--red), transparent),
    linear-gradient(rgba(224, 9, 29, .18), rgba(224, 9, 29, .18));
  background-size: 100% 36%, 100% 100%;
  background-repeat: no-repeat, no-repeat;
  animation: hudchase-top 3.4s linear infinite;
}
@keyframes hudchase-top {
  0% { background-position: 0 -65%, 0 0; }
  45% { background-position: 0 165%, 0 0; }
  100% { background-position: 0 165%, 0 0; }
}
.hud-tag {
  font-size: .95rem; letter-spacing: .36em;
  color: rgba(224, 9, 29, .5);
  white-space: nowrap;
  padding-left: .36em; /* visually recenters the tracked text */
  animation: hudglow 3.4s linear infinite;
}
@keyframes hudglow {
  0%, 42% { color: rgba(224, 9, 29, .5); text-shadow: none; }
  48% { color: rgba(224, 9, 29, 1); text-shadow: 0 0 12px rgba(224, 9, 29, .55); }
  56%, 100% { color: rgba(224, 9, 29, .5); text-shadow: none; }
}
.hud-arrow {
  position: relative;
  width: 3.4px; height: min(238px, 27vh);
  background-image:
    linear-gradient(180deg, transparent, var(--red), transparent),
    linear-gradient(rgba(224, 9, 29, .18), rgba(224, 9, 29, .18));
  background-size: 100% 36%, 100% 100%;
  background-repeat: no-repeat, no-repeat;
  animation: hudchase-bottom 3.4s linear infinite;
}
@keyframes hudchase-bottom {
  0%, 50% { background-position: 0 -65%, 0 0; }
  100% { background-position: 0 165%, 0 0; }
}
.hud-arrow::after {
  content: '';
  position: absolute; bottom: 0; left: 50%;
  width: 9px; height: 9px; margin-left: -5.5px;
  border-right: 2px solid rgba(224, 9, 29, .8);
  border-bottom: 2px solid rgba(224, 9, 29, .8);
  transform: rotate(45deg);
}
@media (max-width: 700px) { #hud { display: none; } }

/* ============ FORK — cuchillas SOBRE la carta roja del hero ============
   Full-height wedges crossing the frame from the sides while EPIC VIEW
   stays visible in red between them. */
.fork-panel {
  /* v25: blades extend 7vw BEYOND the viewport edge, so the hover invasion
     compresses the red center instead of detaching from the side and
     exposing a red gap behind */
  position: absolute; top: 0; bottom: 0; width: calc(36% + 7vw);
  z-index: 7; /* above the red card (5) and letterbox (6) */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1.8vh;
  cursor: pointer;
  will-change: transform, translate;
  /* hover invasion rides the `translate` channel — JS owns `transform` */
  transition: translate .5s cubic-bezier(.16, 1, .3, 1);
}
/* E4 ①: el pie de las cuchillas cortaba en seco contra el negro de la
   sección siguiente — sobre todo la de CORPORATIVO, que es papel claro.
   No se enmascara a transparente (dejaría ver la carta roja de detrás):
   se funde hacia --ink, el mismo negro del letterbox y del manifiesto, así
   que las cuchillas se hunden en la oscuridad en vez de terminar en filo.
   El clip-path recorta también este ::after, así que sigue la cuña. */
.fork-panel::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 30%;
  pointer-events: none;
  /* E5 ①: el negro de destino sale de --fade-ink, no de --ink. Medido: el
     letterbox del hero y #manifiesto son #000 puro, mientras --ink es
     #060606. La cuchilla aterrizaba 6 niveles POR ENCIMA del negro contra el
     que de verdad termina, y ese escalón se leía como línea de corte — en el
     pie contra el manifiesto y a lo largo de la diagonal contra el letterbox.
     Solo la cuchilla ARTISTA redefine la variable; la CORPORATIVA cae al
     valor de reserva de var() y renderiza exactamente igual que antes. */
  background: linear-gradient(to bottom,
    rgba(var(--fade-ink, 6, 6, 6), 0) 0%,
    rgba(var(--fade-ink, 6, 6, 6), .55) 52%,
    rgba(var(--fade-ink, 6, 6, 6), .88) 80%,
    rgb(var(--fade-ink, 6, 6, 6)) 100%);
}
/* considering an option = that blade claims more of the red center */
.fork-artist:hover { translate: 4.5vw 0; }
.fork-corp:hover { translate: -4.5vw 0; }
.fork-artist {
  left: -7vw; /* anchored past the edge — the outer side can never peel off */
  background: #0d0d0e;
  clip-path: polygon(0 0, 100% 0, 76.5% 100%, 0 100%);
  transform: translateX(-108%);
  padding-right: 8%;
  padding-left: 7vw; /* recenters the label over the visible portion */
  --fade-ink: 0, 0, 0; /* E5 ① — ver .fork-panel::after */
}
/* ============ E5 ② — EL SANGRADO ROJO DEL LADO ARTISTA ============
   Los dos lados no pueden transicionar igual. La cuchilla CORPORATIVA cae de
   #f7f5f0 a negro: ~247 niveles de recorrido, y por eso su fundido se ve.
   La ARTISTA cae de #0d0d0e a negro: 13 niveles. Ningún degradado se percibe
   en ese rango — no es un bug, es física.

   Así que en vez de imitar un fundido imposible, este lado transiciona con su
   propio color: la luz de la carta roja se derrama por el pie de la cuchilla
   negra y se disuelve. Rojo de marca, apenas insinuado.

   Dos decisiones que sostienen el efecto:
   · No lleva máscara propia. ::after —el fundido a negro— pinta DESPUÉS y es
     el que lo apaga, así que el rojo muere justo donde la cuchilla llega a
     negro puro. El borde inferior sigue aterrizando en #000 y el punto ① no
     se rompe.
   · El foco del radial va pegado al canto interior, que es por donde asoma la
     carta: el clip-path recorta el núcleo y lo que queda es el halo. Se lee
     como luz que viene de más allá del filo, no como una mancha. */
.fork-artist::before {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 58%;
  pointer-events: none;
  mix-blend-mode: screen; /* es luz: solo puede sumar, nunca ensuciar el negro */
  background: radial-gradient(76% 64% at 90% 66%,
    rgba(224, 9, 29, .30) 0%,
    rgba(224, 9, 29, .13) 36%,
    rgba(224, 9, 29, .035) 64%,
    transparent 84%);
}
.fork-corp {
  right: -7vw;
  background: #f7f5f0;
  clip-path: polygon(23.5% 0, 100% 0, 100% 100%, 0 100%);
  transform: translateX(108%);
  padding-left: 8%;
  padding-right: 7vw;
}
/* ============ E6 — LA CURVA DEL FUNDIDO CORPORATIVO ============
   El fundido de esta cuchilla NO era de dos paradas: ya tenía cuatro
   (0 / .55 / .88 / 1). El problema estaba en otro sitio, y se ve midiendo
   en L* —luminancia perceptual— en vez de en niveles RGB:

     · La pendiente arrancaba de golpe. En el píxel de entrada de la banda
       pasaba de 0 a 0,395 L* por píxel. Eso es una discontinuidad de pendiente, y
       el ojo la lee como una línea (banda de Mach) aunque el degradado sea
       continuo. Era, con diferencia, el defecto dominante.
     · Y no era uniforme: 0,43 → 0,57 → 0,30 L* por píxel según el tramo. El
       escalón metía un segundo quiebre de −0,245 en el 80%.

   Sobre el lado ARTISTA la misma curva no se nota porque su recorrido
   entero son 7 L*; aquí son 97. La forma era la misma, la escala no.

   La corrección es una curva de derivada trapezoidal: la pendiente sube
   desde CERO durante el primer 22% de la banda, se mantiene constante en el
   centro y baja a cero en el último 20%. Nueve paradas, colocadas más juntas
   donde la curvatura es mayor (la entrada y la salida) y más separadas en el
   tramo recto — repartirlas por igual desperdicia paradas en la zona que no
   las necesita.

   La banda pasa de 30% a 44% de la cuchilla: con extremos suavizados el
   centro se empina, y alargar es lo que devuelve el pico a su sitio. A 44%
   la banda arranca por debajo de .fork-sub, que es el límite útil.
   (Medido a 1280×720 — pico 0,643 → 0,433 L* por píxel; quiebre 0,397 → 0,142.)

   Aterriza en #000 y no en --ink por lo mismo que la cuchilla ARTISTA: el
   letterbox y el manifiesto son negro puro, y quedarse en #060606 dejaba una
   línea de 6 niveles en el pie y a lo largo de la diagonal — el punto ④.
   Aquí va literal, no por --fade-ink, para no tocar la regla compartida. */
.fork-corp::after {
  height: 44%;
  background: linear-gradient(to bottom,
    rgba(0, 0, 0, 0)    0%,
    rgba(0, 0, 0, .02)  9%,
    rgba(0, 0, 0, .08) 16%,
    rgba(0, 0, 0, .28) 31%,
    rgba(0, 0, 0, .53) 50%,
    rgba(0, 0, 0, .74) 68%,
    rgba(0, 0, 0, .90) 83%,
    rgba(0, 0, 0, .98) 92%,
    rgb(0, 0, 0)      100%);
}
.fork-label {
  font-family: var(--font-wordmark);
  font-weight: 600;
  font-size: clamp(1.5rem, 2.9vw, 2.8rem);
  letter-spacing: .05em;
  line-height: 1;
  opacity: 0; /* scroll-driven */
  transition: letter-spacing .35s cubic-bezier(.16, 1, .3, 1), color .25s, text-shadow .25s;
}
.fork-artist .fork-label { color: var(--red); }
.fork-corp .fork-label { color: #0b0b0c; }
.fork-sub {
  font-size: .69rem; letter-spacing: var(--track-btn); font-weight: 400;
  opacity: 0; /* scroll-driven */
  text-align: center;
}
.fork-artist .fork-sub { color: rgba(244, 239, 230, .55); }
.fork-corp .fork-sub { color: rgba(11, 11, 12, .5); }
.fork-panel:hover .fork-label { letter-spacing: .1em; }
.fork-corp:hover .fork-label { color: var(--red); }
.fork-artist:hover .fork-label { text-shadow: 0 0 20px rgba(224, 9, 29, .55); }
@media (max-width: 900px) {
  .fork-panel { width: calc(44% + 7vw); }
  .fork-label { font-size: clamp(1.1rem, 4.6vw, 1.8rem); }
  .fork-sub { display: none; }
}

/* ============ E8 ③④ — FORK LATERAL TAMBIÉN EN VERTICAL ============ */
/* Aquí vivía el "fork apilado" (E4 ⑦): en teléfono las cuchillas entraban por
   ARRIBA y ABAJO. El cliente lo descartó — la lógica es la de escritorio, por
   los COSTADOS, en cualquier pantalla. Lo que E4 ⑦ medía sigue siendo cierto
   (a 390 px dos cuchillas de 44% dejan 46 px de centro para un wordmark de
   344), así que la composición vertical se resuelve de otro modo:

   · las cuchillas son más ESTRECHAS (30% visible cada una) → el centro rojo
     queda en 40% del ancho, 156 px;
   · el wordmark se parte en dos líneas ("EPIC" / "VIEW") y cabe en ese
     centro: la palabra más ancha, VIEW, mide ~113 px a este cuerpo;
   · la etiqueta se escribe EN VERTICAL (writing-mode), porque la cuchilla
     es una columna de 117 px de ancho y 664–844 de alto: "CORPORATIVO" en
     horizontal no cabe a ningún cuerpo legible; en vertical, a 7vw (27 px),
     mide 242 px de alto y 27 de ancho y se lee como un lomo de libro — que
     es exactamente la forma de la pieza. Medido a 390×664: "VIEW" ocupa
     105 px ya escalado (125 sin escalar) dentro de un centro de 156.

   ④ Los degradados de pie (::after) y el sangrado rojo (::before) ya NO se
   apagan: con las cuchillas a los lados vuelven a tener sentido y aquí se
   recalibran para la columna vertical. En una pieza tan alta la banda de
   fundido se recorta (30% → 22%, 44% → 32%): la altura absoluta queda en el
   mismo orden que en escritorio (150–215 px contra 216–317) y la cuchilla no
   parece "medio disuelta". El sangrado rojo se centra en el canto interior,
   que en la columna queda mucho más cerca del eje. */
@media (max-width: 700px) {
  .fork-panel {
    width: calc(30% + 7vw);
    gap: 0;
    /* E8 ③: el arrastre horizontal con el dedo lo lee JS; la vertical sigue
       siendo scroll del navegador. `pan-y` es exactamente ese reparto. */
    touch-action: pan-y;
  }
  .fork-artist { padding-left: 7vw; padding-right: 0; }
  .fork-corp { padding-right: 7vw; padding-left: 0; }
  /* la invasión por hover es un gesto de ratón; con el dedo la cuchilla se
     despliega arrastrando (JS) y un :hover pegajoso tras el toque la movería
     sola. Se anula. */
  .fork-artist:hover, .fork-corp:hover { translate: 0 0; }
  .fork-label {
    writing-mode: vertical-rl; /* las dos se leen de arriba abajo, como dos lomos */
    font-size: clamp(1.5rem, 7vw, 2.2rem);
    letter-spacing: .12em;
    line-height: 1;
  }
  .fork-panel:hover .fork-label { letter-spacing: .12em; }
  .fork-sub { display: none; }
  /* ④ degradados recalibrados para la columna */
  .fork-panel::after { height: 22%; }
  .fork-corp::after { height: 32%; }
  .fork-artist::before {
    height: 46%;
    background: radial-gradient(70% 58% at 84% 70%,
      rgba(224, 9, 29, .30) 0%,
      rgba(224, 9, 29, .13) 36%,
      rgba(224, 9, 29, .035) 64%,
      transparent 84%);
  }
  /* el wordmark en dos líneas dentro del centro rojo: el espacio entre las
     dos palabras (un span .sp con &nbsp;, ver splitLetters) se vuelve salto */
  .hero-title { font-size: clamp(2.2rem, 11.5vw, 3.4rem); line-height: .96; }
  .hero-title .sp { display: block; height: 0; }
  .outro-inner { padding: 0 4vw; }
  /* E8 ③: asas invisibles en los bordes para arrastrar una cuchilla que está
     fuera de cuadro. Solo existen mientras la carta roja domina (html.carta-roja)
     y las activa JS; van DEBAJO de las cuchillas en el orden del DOM, así que
     cuando la cuchilla ya está desplegada encima, el toque cae en la cuchilla. */
  .fork-grip {
    position: absolute; top: 0; bottom: 0; z-index: 7;
    width: max(56px, 14vw);
    touch-action: pan-y;
    pointer-events: none;
  }
  html.carta-roja .fork-grip { pointer-events: auto; }
  .fork-grip-a { left: 0; }
  .fork-grip-c { right: 0; }
}
@media (min-width: 701px) { .fork-grip { display: none; } }

/* (E4 ⑤⑥ — portafolio y cierre en vertical: al FINAL del archivo, porque
   sus reglas base viven más abajo y a igual especificidad gana la última) */

/* full-page white fade for the ARTISTA → CORPORATIVO handoff
   (fade-to-solid, same device as the hero's red-card climax) */
#page-slider {
  position: fixed; inset: 0; z-index: 1200;
  background: #f7f5f0;
  opacity: 0;
  pointer-events: none;
}

/* ============ MANIFIESTO ============
   Full-screen typographic beat. No image, no glitch — the rhythm cut IS
   the effect. Absolute black, two staggered lines. */
.manif-sec {
  background: #000;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  /* E7G 4: 4,5vh -> 1,6vh. Las dos líneas se juntan para que se lean como un
     solo golpe: la primera monta la premisa y la segunda la remata. */
  gap: 1.6vh;
  text-align: center;
  padding: 0 7vw;
}
.manif-l1 {
  font-family: var(--font-wordmark);
  font-weight: 300;
  /* E7G 4: más chica, para que el salto de escala a la línea 2 sea el efecto.
     Antes 1,9rem contra 4,6 = 2,4x. Ahora 1,3 contra 4,6 = 3,5x. */
  font-size: clamp(.8rem, 1.62vw, 1.3rem);
  letter-spacing: .2em;
  color: rgba(244, 239, 230, .4);
  opacity: 0; /* scroll-driven */
}
.manif-l2 {
  font-family: var(--font-wordmark);
  font-weight: 600;
  font-size: clamp(1.9rem, 5vw, 4.6rem);
  letter-spacing: .02em;
  line-height: 1.05;
  color: var(--cream);
  opacity: 0; /* scroll-driven */
}
/* E4 — "ARTISTAS" destacada. El resto de la frase no se toca. */
.manif-l2 em {
  font-style: normal; color: var(--red);
  /* glow permanente y tenue mientras la frase está en pantalla */
  text-shadow: 0 0 18px rgba(224, 9, 29, .35);
}
/* mismo keyframe que el reveal de "EPIC VIEW" (glitchflick-soft, ver arriba):
   se reutiliza tal cual. Aquí las letras NO llevan el opacity:0 de
   `.hero-title .ltr` — la línea ya tiene su reveal y el destello es un extra. */
.manif-l2 em .ltr { display: inline-block; }
.manif-l2 em .ltr.flick { animation: glitchflick-soft 90ms steps(2, end) 1; }
/* Pulso único al completarse la palabra. Lo dispara main.js 300ms DESPUÉS
   del flicker (100ms de destello + 200ms de respiro), para que se lean como
   dos momentos y no como uno solo.
   1500ms y no 900: a 900 con ease-out el pico llegaba casi instantáneo y se
   confundía con el destello. Ahora sube y baja de forma perceptible, con el
   pico al 45% y una curva simétrica.
   Arranca y termina en el valor de reposo — antes salía de 6px, por debajo
   del reposo, y eso metía un bajón antes de encender. La intensidad del
   pico NO cambia. */
.manif-l2 em.glow-in { animation: manif-glow 1500ms cubic-bezier(.4, 0, .3, 1) 1; }
@keyframes manif-glow {
  0%   { text-shadow: 0 0 18px rgba(224, 9, 29, .35); }
  45%  { text-shadow: 0 0 34px rgba(224, 9, 29, .95), 0 0 72px rgba(224, 9, 29, .45); }
  100% { text-shadow: 0 0 18px rgba(224, 9, 29, .35); }
}

/* ============ MARQUEE ============ */
.marquee {
  position: relative; overflow: hidden;
  padding: 4.2vh 0;
  border-top: 1px solid rgba(244, 239, 230, .1);
  border-bottom: 1px solid rgba(244, 239, 230, .1);
  background: var(--ink);
}
.marquee-track {
  display: inline-flex; white-space: nowrap;
  will-change: transform;
  transform: skewX(-4deg);
}
.mq-item {
  display: inline-block;
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 6.4vw, 6.2rem);
  line-height: 1;
  color: var(--red);
  padding: 0 .45em 0 0;
  transition: color .18s, transform .18s cubic-bezier(.16, 1, .3, 1);
}
.mq-item:hover {
  color: var(--cream);
  text-shadow: -5px 0 var(--red), 5px 0 rgba(224, 9, 29, .5);
  transform: skewX(-9deg) scaleY(1.06);
}
.mq-item .dot { color: rgba(244, 239, 230, .35); padding-left: .45em; }
.glitch-flick { animation: glitchflick 120ms steps(2, end) 1; }

/* ============ PILARES ============ */
.sec-kicker {
  position: absolute; top: 13vh; left: clamp(20px, 6vw, 90px); z-index: 3;
  font-size: .7rem; letter-spacing: .4em; color: var(--red);
}
.pillars-wrap { position: absolute; inset: 0; z-index: 3; }
/* E4 ②: el párrafo se perdía contra el video. Un scrim diagonal —no un
   recuadro— oscurece el flanco izquierdo, que es donde vive el texto, y se
   desvanece antes de la mitad para no matar la imagen. Va detrás del
   contenido del wrap pero por encima del canvas del video (que es hermano
   con z-index menor), así que sirve a los tres pilares por igual. */
.pillars-wrap::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  background: linear-gradient(102deg,
    rgba(6, 6, 6, .88) 0%,
    rgba(6, 6, 6, .74) 30%,
    rgba(6, 6, 6, .34) 58%,
    rgba(6, 6, 6, 0) 84%);
}
.pillar {
  position: absolute; top: 50%; left: clamp(20px, 6vw, 90px);
  transform: translateY(-42%);
  max-width: min(880px, 82vw);
  opacity: 0; visibility: hidden;
  pointer-events: none;
}
.pillar.active {
  opacity: 1; visibility: visible;
  animation: pillarin .55s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes pillarin {
  from { opacity: 0; transform: translateY(-34%) skewY(2.5deg); }
  to   { opacity: 1; transform: translateY(-42%) skewY(0); }
}
.pillar-num {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 2.2vw, 1.8rem);
  color: transparent;
  -webkit-text-stroke: 1px var(--red);
  display: block; margin-bottom: 1.6vh;
}
.pillar-title {
  font-family: var(--font-display);
  /* E7F 1.2: Anton solo existe en 400. Estos son <h2>/<h3>, y la hoja del
     navegador les impone bold=700: el navegador lo resolvía ENGORDANDO el
     contorno por trazo. Medido: +19,9% de tinta con 0 px de ancho extra —
     bordes empastados y contraformas cerradas, justo a 158 px. Declararlo en
     400 no mueve NADA de sitio y devuelve la letra dibujada. */
  font-weight: 400;
  font-size: clamp(2.6rem, 7.4vw, 7.6rem);
  /* E7G 5: .92 -> 1.16. No es un número de gusto: medido con las métricas de
     tinta reales de Anton a 106,6 px, la tilde de "DIRECCIÓN" sube 26 px por
     encima de la altura de mayúscula (ascendente 119 px contra 93 sin tilde).
     Para que no toque la línea de arriba hace falta un interlineado de al menos
     119 + 1 = 120 px, o sea 1,126. Con .92 (98 px) faltaban 22 px y por eso
     chocaban. 1,16 deja 4 px de aire real por encima del acento. */
  line-height: 1.16; letter-spacing: .005em;
  color: var(--cream);
  margin-bottom: 2.6vh;
}
.pillar-body {
  /* E7H 4: fuera Space Grotesk 300. Es una grotesca neutra de rasgo técnico y
     al peso 300 se lee genérica y sin voz — señalada tres veces. Entra
     BRICOLAGE GROTESQUE, que ya vive en esta página (hasta la ronda anterior
     cargaba el wordmark) así que no suma ni un archivo nuevo. Es una variable
     200–800 pensada como híbrido texto/display: tiene irregularidades
     deliberadas —la g, la a, la y— que le dan carácter editorial sin perder
     legibilidad en párrafo. Y sube a peso 400: sobre video, el 300 en claro
     adelgaza por sangrado óptico. */
  font-family: 'Bricolage Grotesque', var(--font-body);
  font-size: clamp(.95rem, 1.32vw, 1.15rem);
  font-weight: 400; line-height: 1.5;
  /* E4 ②: .82 → .92 y una sombra corta. El scrim hace el trabajo grueso;
     esto asegura los frames más claros del video sin engordar la tipografía. */
  color: rgba(244, 239, 230, .92);
  text-shadow: 0 1px 12px rgba(6, 6, 6, .8);
  /* E7H 5: 52ch -> 40ch. Ésta es la palanca real para acercar el módulo: la
     tinta del párrafo terminaba en x=715 y el módulo no podía bajar de ahí.
     Con una medida más corta el texto cierra en ~600 y el módulo entra en el
     recorrido natural de lectura. De paso 40ch es mejor medida que 52. */
  max-width: 40ch;
}

/* ============ TRABAJO ============ */
.work-wrap {
  position: absolute; inset: 0; z-index: 3;
  padding: 11vh clamp(20px, 5vw, 80px) 7vh;
  display: flex; flex-direction: column; justify-content: space-between;
}
.work-title {
  font-family: var(--font-display);
  /* E7F 1.2: Anton solo existe en 400. Estos son <h2>/<h3>, y la hoja del
     navegador les impone bold=700: el navegador lo resolvía ENGORDANDO el
     contorno por trazo. Medido: +19,9% de tinta con 0 px de ancho extra —
     bordes empastados y contraformas cerradas, justo a 158 px. Declararlo en
     400 no mueve NADA de sitio y devuelve la letra dibujada. */
  font-weight: 400;
  /* E17 ⑨: 11rem → 8.6rem (141 → 110 px a 1280). PORTAFOLIO es un rótulo de
     sección; era mayor que el titular del cierre, que es el mensaje. */
  font-size: clamp(3.4rem, 8.6vw, 8.6rem);
  line-height: .9;
  color: var(--cream);
  opacity: 0; /* proportional scroll reveal */
}
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(14px, 2vw, 32px);
}
.card {
  display: block; position: relative;
  opacity: 0; /* proportional scroll reveal */
}
.card-media {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  border: 1px solid rgba(244, 239, 230, .16);
  background: var(--ink-2);
}
.card-canvas { width: 100%; height: 100%; display: block; }
.card-tex {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; mix-blend-mode: screen;
  transition: opacity .25s; pointer-events: none;
}
.card:hover .card-tex { opacity: .32; }
.card-flash {
  position: absolute; inset: 0; background: var(--red);
  opacity: 0; pointer-events: none; mix-blend-mode: hard-light;
}
.card:hover .card-flash { animation: redflash .42s ease-out 1; }
@keyframes redflash {
  0% { opacity: .5; } 18% { opacity: .12; } 32% { opacity: .3; } 100% { opacity: 0; }
}
.card:hover .card-media { border-color: var(--red); }
.card-meta { padding-top: 16px; }
.card-meta h3 {
  font-family: var(--font-display);
  /* E7F 1.2: Anton solo existe en 400. Estos son <h2>/<h3>, y la hoja del
     navegador les impone bold=700: el navegador lo resolvía ENGORDANDO el
     contorno por trazo. Medido: +19,9% de tinta con 0 px de ancho extra —
     bordes empastados y contraformas cerradas, justo a 158 px. Declararlo en
     400 no mueve NADA de sitio y devuelve la letra dibujada. */
  font-weight: 400;
  font-size: clamp(1.15rem, 1.7vw, 1.7rem);
  letter-spacing: .02em; color: var(--cream);
  margin-bottom: 6px;
}
.card-meta p {
  /* E7F C3: 300 -> 400. A ~13 px en crema sobre negro el 300 pierde trazo por
     sangrado óptico: el texto claro se expande sobre el fondo oscuro y adelgaza
     el palo. (Corrección a mi propia auditoría: dije "por debajo de 10 px" y no
     existe ninguna etiqueta de 300 tan pequeña; la más chica es de 13,2 px.) */
  font-size: .875rem; font-weight: 400; /* E17 ⑩: 13,4 → 14 px, alfa .62 → .7 */
  color: rgba(244, 239, 230, .7);
  margin-bottom: 10px; max-width: 34ch;
}
.card-cta {
  font-size: .69rem; letter-spacing: var(--track-label); color: var(--red);
}

/* ============ FINAL ============ */
#final { touch-action: pan-y; } /* horizontal drag = video scrub, vertical = page scroll */
#final.scrubbing { user-select: none; }
.final-wrap {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 6vh; text-align: center; padding: 0 5vw;
}
.final-title {
  font-family: var(--font-display);
  /* E7F 1.2: Anton solo existe en 400. Estos son <h2>/<h3>, y la hoja del
     navegador les impone bold=700: el navegador lo resolvía ENGORDANDO el
     contorno por trazo. Medido: +19,9% de tinta con 0 px de ancho extra —
     bordes empastados y contraformas cerradas, justo a 158 px. Declararlo en
     400 no mueve NADA de sitio y devuelve la letra dibujada. */
  font-weight: 400;
  font-size: clamp(2.6rem, 8.4vw, 8.6rem);
  line-height: .94;
  color: var(--cream);
  opacity: 0; /* proportional scroll reveal */
}
.final-title .accent { color: var(--red); }
.final-ctas {
  display: flex; gap: clamp(14px, 2vw, 28px); flex-wrap: wrap; justify-content: center;
  opacity: 0; /* proportional scroll reveal */
}
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  /* E17 ③: caja de 63 px con letra de 13 px → 14 px en ~52 px (Ferrari: 48 px,
     14/700). Tracking .24 → .16em. */
  padding: 18px clamp(26px, 3.2vw, 46px);
  font-size: .875rem; font-weight: 700; letter-spacing: var(--track-btn);
  transition: transform .25s cubic-bezier(.16, 1, .3, 1), scale .08s ease-out;
}
/* E17 ③: estado de PULSACIÓN (Apple: scale .95 en todo botón). Va en `scale`,
   no en transform, porque el imán (gsap) y los reveals escriben transform en
   línea; el !important es para ganarle al `scale: none` que gsap deja. */
.btn:active { scale: .97 !important; }
.btn span { position: relative; z-index: 2; }
.btn-primary { background: var(--red); color: var(--ink); }
.btn-primary:hover { transform: translateY(-3px) skewX(-2deg); }
.btn-tex {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; mix-blend-mode: multiply;
  transition: opacity .25s; pointer-events: none; z-index: 1;
}
.btn-primary:hover .btn-tex { opacity: .55; }
.btn-ghost {
  border: 1px solid rgba(244, 239, 230, .35); color: var(--cream);
  transition: border-color .25s, color .25s, transform .25s cubic-bezier(.16, 1, .3, 1);
}
.btn-ghost:hover { border-color: var(--red); color: var(--red); transform: translateY(-3px); }
/* E11 ④: "VER EL REEL" late en rojo cada 4 s — un pulso de borde y halo,
   suave, que dice "esto se puede pulsar" sin gritar. */
#reel-open { animation: reel-latido 4s cubic-bezier(.4, 0, .3, 1) infinite; }
@keyframes reel-latido {
  0%, 100% { border-color: rgba(244, 239, 230, .35); box-shadow: 0 0 0 0 rgba(224, 9, 29, 0); }
  14%  { border-color: var(--red); box-shadow: 0 0 18px 2px rgba(224, 9, 29, .5); }
  34%  { border-color: rgba(244, 239, 230, .35); box-shadow: 0 0 0 0 rgba(224, 9, 29, 0); }
}

/* ============ FOOTER ============ */
#footer {
  position: relative; background: var(--ink);
  border-top: 1px solid rgba(244, 239, 230, .1);
  padding: var(--sp-lg) clamp(20px, 5vw, 80px) var(--sp-md); /* E17 ⑤ */
}
/* E11 ⑤: en columna — el wordmark arriba, INSTAGRAM y CONTACTO debajo, todo
   sobre el mismo margen izquierdo. En escritorio y en teléfono. */
.foot-top {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 26px;
  margin-bottom: var(--sp-lg);
}
/* solid fill + subtle static RGB split — the transparent-fill outline over a
   display face rendered as an illegible tangle of overlapping contours */
.foot-brand {
  font-family: var(--font-wordmark);
  font-weight: 600;
  font-size: clamp(3.6rem, 12vw, 12rem);
  line-height: .82;
  color: rgba(244, 239, 230, .88);
  /* E17 ⑧: fuera el desdoblamiento RGB (2 px rojo / −2 px azul), la misma
     aberración retro-glitch que E7F retiró de la portada. A 154 px la letra
     se sostiene sola. */
}
.foot-links { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.foot-links a {
  font-size: .8rem; letter-spacing: var(--track-label);
  color: rgba(244, 239, 230, .8);
  transition: color .2s;
}
.foot-links a:hover { color: var(--red); }
.foot-bottom {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px;
  font-size: .69rem; letter-spacing: var(--track-label); /* E17 ①: 9,9 → 11 px */
  color: rgba(244, 239, 230, .55);
}

/* ============ FX OVERLAYS ============ */
#fx-scan {
  position: fixed; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 800;
  opacity: 0; pointer-events: none; mix-blend-mode: screen;
}
#grain {
  position: fixed; inset: 0; width: 100%; height: 100%;
  /* E2B: .05 → .12. El cliente no percibía el grano en pantalla real.
     E2C: la opacidad final es esta BASE por el multiplicador de sección, que
     escribe initGrain (ver GRAIN_SECTION en main.js). A falta de la variable
     el grano queda en su nivel global, así que degrada bien. */
  z-index: 900; opacity: calc(.12 * var(--grain-mul, 1)); pointer-events: none;
  mix-blend-mode: overlay; /* keeps pure blacks black — no gray wash */
}
/* E2 B.1: la capa que faltaba. `overlay` multiplica contra el fondo, así que
   sobre negro puro el grano desaparecía y las sombras quedaban planas.
   `screen` hace lo contrario: pasa entero sobre negro y se apaga hacia el
   blanco. Nivel muy por debajo del de #grain — la jerarquía se mantiene
   (máximo en luces), solo deja de haber zonas muertas. Sube el negro
   ~1.4/255: lo justo para que respire, no un velo gris. */
/* E2B: .028 → .075. Sobre negro puro `screen` deja pasar el grano entero, así
   que esto sube el negro ~9/255 (de #060606 a ~#0f0f0f de media) y le da
   amplitud visible. Es el precio de que las sombras dejen de estar muertas:
   el cine tampoco aplasta los negros a cero. */
#grain-lo {
  position: fixed; inset: 0; width: 100%; height: 100%;
  /* E2C: misma variable de sección que #grain — las dos capas suben y bajan
     juntas, así la jerarquía luces/sombras se conserva en todo el recorrido */
  z-index: 900; opacity: calc(.075 * var(--grain-mul, 1)); pointer-events: none;
  mix-blend-mode: screen;
  /* E4-fix: `screen` sobre negro deja pasar el grano entero, así que en el
     degradado de pie de la cuchilla ARTISTA el negro no llegaba a asentarse:
     medido, el punto más oscuro pasaba de 6 a 6–16/255 y se leía gris.
     `--grain-cut` abre una ventana sin grano en la banda inferior del
     viewport, que es justo donde vive ese degradado mientras el fork está en
     pantalla. La escribe makeHeroUpdate siguiendo la entrada de las
     cuchillas, y vuelve a 0% al salir del hero.
     Solo se recorta ESTA capa: la de overlay sigue cubriendo la cuchilla
     entera —sobre negro no levanta nada (medido 6,6,6)— así que el grano del
     resto de la pieza y del manifiesto queda intacto. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--grain-cut, 0%)), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - var(--grain-cut, 0%)), transparent 100%);
}

/* ============ CURSOR ============ */
#cursor {
  position: fixed; left: 0; top: 0; z-index: 1001;
  pointer-events: none;
}
.cursor-ring {
  position: absolute; left: 0; top: 0;
  width: 36px; height: 36px;
  border: 1.5px solid var(--red); border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width .22s cubic-bezier(.16,1,.3,1), height .22s cubic-bezier(.16,1,.3,1), background .22s;
}
.cursor-dot {
  position: absolute; left: 0; top: 0;
  width: 4px; height: 4px; background: var(--red); border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width .2s, height .2s, box-shadow .2s;
}
#cursor.hovering .cursor-ring {
  width: 64px; height: 64px;
  background: rgba(224, 9, 29, .12);
}
#cursor.down .cursor-ring { width: 26px; height: 26px; }
/* over the draggable HUD: the ring tightens and the core heats up */
#cursor.on-hud .cursor-ring {
  width: 22px; height: 22px;
  border-color: var(--red);
  background: rgba(224, 9, 29, .12);
}
#cursor.on-hud .cursor-dot { width: 6px; height: 6px; }
/* while dragging: smaller, hotter — organic control */
#cursor.hud-drag .cursor-ring {
  width: 13px; height: 13px;
  border-width: 2px;
  background: rgba(224, 9, 29, .38);
  box-shadow: 0 0 16px rgba(224, 9, 29, .65);
}
#cursor.hud-drag .cursor-dot {
  width: 7px; height: 7px;
  box-shadow: 0 0 10px var(--red);
}
@media (pointer: coarse) { #cursor { display: none; } }

/* ============ E7C — SEÑALES DE QUE LA PÁGINA SIGUE, SOBRE LA CARTA ROJA ====
   Problema observado con usuarios: al llegar a la carta roja, varios daban el
   sitio por terminado. Tres señales se apagan a la vez sobre ese fondo — el
   HUD (rojo sobre rojo), el cursor (anillo rojo sobre rojo) y las cuchillas,
   que todavía no han entrado. `html.carta-roja` la pone y la quita
   makeHeroUpdate mientras la carta domina el cuadro.

   ① EL HUD PASA A NEGRO. Medido sobre el rojo de la carta rgb(224,9,29):
        negro puro  → 4,23:1
        gris medio (grayscale del propio rojo, rgb 56) → 2,36:1
      El cliente pedía probar los dos y quedarse con el mejor: gana el negro,
      con casi el doble de contraste. Y no es un color prestado — el wordmark
      "EPIC VIEW" de la propia carta ya es --ink (#0b0b0c) sobre ese rojo, así
      que el HUD pasa a hablar el idioma de la pantalla en la que está.

      Se hace con `filter` y no reescribiendo colores por una razón concreta:
      los dos bastones pintan su pulso con `background-image` de degradados y
      la palabra late con un `@keyframes` de color. Reescribir todo eso
      duplicaría la animación entera y ni siquiera transicionaría suave
      (los degradados no interpolan de forma fiable). `filter` es UNA
      propiedad, transiciona sola y respeta la coreografía del pulso tal cual.
      No se transiciona `opacity`: esa la escribe el scroll cuadro a cuadro y
      una transición la dejaría arrastrando. */
#hud { transition: filter .5s ease; }
html.carta-roja #hud { filter: grayscale(1) brightness(0); }

/* ② EL CURSOR PASA A GRIS. Blanco descartado por el cliente. Sobre el rojo:
        #d9d9d9 → 3,52:1   (el elegido: se lee claramente y no es blanco)
        #8a8a8a → 1,44:1   (un gris medio desaparecería igual que el rojo)
      El gris claro además sobrevive a la cuchilla ARTISTA (#0d0d0e) si el
      puntero la cruza. Sale de la zona antes de que la cuchilla CORPORATIVA
      —que es papel— llegue a ocupar el cuadro. */
.cursor-ring { transition: width .22s cubic-bezier(.16,1,.3,1), height .22s cubic-bezier(.16,1,.3,1), background .22s, border-color .45s ease; }
.cursor-dot { transition: width .2s, height .2s, box-shadow .2s, background-color .45s ease; }
html.carta-roja .cursor-ring { border-color: #d9d9d9; }
html.carta-roja .cursor-dot { background: #d9d9d9; }
html.carta-roja #cursor.hovering .cursor-ring { background: rgba(217, 217, 217, .14); }
html.carta-roja #cursor.on-hud .cursor-ring { border-color: #d9d9d9; background: rgba(217, 217, 217, .14); }
html.carta-roja #cursor.hud-drag .cursor-ring {
  border-color: #d9d9d9; background: rgba(217, 217, 217, .40);
  box-shadow: 0 0 16px rgba(217, 217, 217, .55);
}
html.carta-roja #cursor.hud-drag .cursor-dot { box-shadow: 0 0 10px #d9d9d9; }

/* ============ RESPONSIVE ============ */
@media (max-width: 900px) {
  .cards { grid-template-columns: 1fr; gap: 12px; }
  .card-meta p { display: none; }
  .card { display: grid; grid-template-columns: 42% 1fr; gap: 14px; align-items: center; }
  .card-meta { padding-top: 0; }
  .work-wrap { justify-content: flex-start; gap: 4vh; padding-top: 13vh; }
  .nav-links a[href="#trabajo"], .nav-links a[href="#final"] { display: none; }
  .hero-title { white-space: normal; }
  .pillar { transform: translateY(-50%); }
  @keyframes pillarin {
    from { opacity: 0; transform: translateY(-42%) skewY(2.5deg); }
    to   { opacity: 1; transform: translateY(-50%) skewY(0); }
  }
}

/* ============ v26: MOBILE SCROLL CUE ============ */
/* The HUD hides under 700px; phones instead get a one-shot "hay más abajo"
   pulse — visible on load, dissolves permanently on the first scroll. */
/* E9 ②: el aviso ya no es "hay más abajo" al pie, sino la INVITACIÓN AL
   CLÍMAX: a la derecha, a la altura natural del pulgar, una flecha hacia
   ARRIBA (el gesto) con el texto "Desliza y suelta hacia arriba". Un desliz
   corto que se suelte completa el recorrido del cubo solo (ver
   initHeroSnap). Se disuelve con el primer scroll, como antes. */
/* E10 ①: la flecha a la derecha (24 px del borde) y el texto CENTRADO en la
   pantalla, los dos sobre el mismo eje horizontal — el centro del gesto. */
/* E12 ①②: la punta de la flecha se queda donde estaba (≈ 21vh + 50 desde el
   pie) y la COLA baja hasta la altura del texto: 120 px de asta. El texto se
   va al pie del bloque, por debajo del cubo, sobre un velo negro muy leve
   (30% con el borde difuminado por sombra, para que no se lea como caja). */
#m-cue {
  position: fixed; left: 0; right: 0; bottom: 8vh; z-index: 550;
  height: 140px;
  display: none;
  pointer-events: none;
  transition: opacity .6s ease;
}
#m-cue .mc-label {
  position: absolute; left: 50%; bottom: 8px; translate: -50% 0;
  font-size: .56rem; font-weight: 500; letter-spacing: .3em; padding: 7px 12px 7px calc(12px + .3em);
  white-space: nowrap;
  color: var(--red);
  background: rgba(6, 6, 6, .30);
  box-shadow: 0 0 16px 8px rgba(6, 6, 6, .30); /* el canto se disuelve: no hay recuadro que leer */
  border-radius: 2px;
}
#m-cue .mc-gesto {
  position: absolute; right: 30px; top: 0;
  display: flex; flex-direction: column; align-items: center;
}
#m-cue .mc-arrow {
  width: 11px; height: 11px;
  border-left: 2px solid var(--red);
  border-top: 2px solid var(--red);
  transform: rotate(45deg);
}
#m-cue .mc-line {
  width: 2px; height: 120px; margin-top: -7px;
  background: linear-gradient(180deg, var(--red) 0%, rgba(224, 9, 29, .55) 55%, transparent 100%);
}
#m-cue .mc-gesto { animation: mcSwipe 1.9s cubic-bezier(.4, 0, .3, 1) infinite; }
#m-cue .mc-label { animation: mcPulse 1.9s ease-in-out infinite; }
@keyframes mcSwipe {
  0% { transform: translateY(12px); opacity: 0; }
  30% { opacity: 1; }
  70% { transform: translateY(-6px); opacity: 1; }
  100% { transform: translateY(-12px); opacity: 0; }
}
#m-cue.gone { opacity: 0; }
@keyframes mcPulse { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
@media (max-width: 700px) { #m-cue { display: block; } }

/* ============ COLOR GRADE — MONITOR DIEGÉTICO (pilar 03) ============ */
/* Vive "dentro" del monitor central de la sala del fondo: marco fino, glow
   rojo coherente con la luz de la escena, perspectiva sutil que se aplana
   al desplegarse. Anclado al viewport de la sección pineada (nunca puede
   quedar bajo el fold, la falla del módulo anterior). */
.grade-monitor {
  position: absolute; z-index: 5;
  /* E7G 7: más adentro. Medido a 1440: la tinta del pilar termina en x=715 y
     el módulo arrancaba en 794, tan pegado al borde que el ojo no llegaba.
     Ahora arranca en ~751, prácticamente sobre el eje central de la pantalla
     (720). Es lo máximo que se puede acercar sin tocar el texto. */
  top: 50%; right: clamp(40px, 15vw, 250px);
  width: min(40vw, 640px);
  transform: translateY(-50%) perspective(1400px) rotateY(-4deg);
  opacity: 0; visibility: hidden; pointer-events: none;
  /* BUG (P2C): visibility was in this list. The CSS spec holds a hidden→
     visible transition AT "hidden" for the entire duration (only flips at
     100%), so the module stayed un-hit-testable for the whole 0.5s fade —
     every click in that window landed on whatever sat behind it. visibility
     now switches instantly; only opacity/transform animate. */
  transition: opacity .5s ease, transform .8s cubic-bezier(.16, 1, .3, 1);
}
.grade-monitor.on { opacity: 1; visibility: visible; pointer-events: auto; }
.grade-monitor.live { transform: translateY(-50%) perspective(1400px) rotateY(0deg); }
.gm-frame {
  background: rgba(8, 8, 9, .92);
  border: 1px solid rgba(244, 239, 230, .16);
  box-shadow: 0 0 0 1px rgba(224, 9, 29, .10), 0 0 38px rgba(224, 9, 29, .12), 0 24px 60px rgba(0, 0, 0, .5);
  padding: 10px;
  transition: box-shadow .6s ease, border-color .6s ease;
}
/* P10: --gm-drag (0..1) lo escribe embers.js en cada frame del arrastre.
   A 0 estos valores son EXACTAMENTE los de antes: en reposo el módulo se ve
   igual que siempre. .gm-react corta la transición de .6s mientras dura el
   gesto — si no, el borde iría medio segundo por detrás de la mano. */
.grade-monitor { --gm-drag: 0; }
.grade-monitor.live .gm-frame {
  border-color: rgba(224, 9, 29, calc(.4 + .45 * var(--gm-drag)));
  box-shadow:
    0 0 0 1px rgba(224, 9, 29, calc(.22 + .55 * var(--gm-drag))),
    0 0 calc(54px + 48px * var(--gm-drag)) rgba(224, 9, 29, calc(.22 + .5 * var(--gm-drag))),
    0 24px 60px rgba(0, 0, 0, .5);
}
.grade-monitor.gm-react .gm-frame { transition: none; }
.gm-stage {
  position: relative;
  aspect-ratio: 2752 / 1536;
  overflow: hidden;
  background: radial-gradient(ellipse at 50% 60%, rgba(224, 9, 29, .07), rgba(6, 6, 6, .25) 72%);
  touch-action: none; /* drag horizontal jamás se convierte en scroll */
  user-select: none; -webkit-user-select: none;
}
.gm-stage img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  pointer-events: none;
  opacity: 0; /* entran con el despliegue */
  transition: opacity .5s ease;
}
.grade-monitor.live .gm-stage img { opacity: 1; }
.gm-divider {
  position: absolute; top: 0; bottom: 0; left: 50%; z-index: 2;
  width: 2px; margin-left: -1px;
  background: var(--red);
  box-shadow: 0 0 12px rgba(224, 9, 29, .5);
  opacity: 0;
  transition: opacity .4s ease .15s;
}
.grade-monitor.live .gm-divider { opacity: 1; }
.gm-handle {
  position: absolute; top: 50%; left: 50%;
  width: 11px; height: 11px;
  translate: -50% -50%;
  rotate: 45deg;
  background: var(--red);
}
.gm-tag {
  position: absolute; z-index: 2; top: 10px;
  font-size: .56rem; letter-spacing: .3em; font-weight: 500;
  color: rgba(244, 239, 230, .85);
  text-shadow: 0 1px 6px rgba(0, 0, 0, .6);
  pointer-events: none;
  opacity: 0;
  transition: opacity .4s ease .3s;
}
.grade-monitor.live .gm-tag { opacity: 1; }
.gm-tag-pre { left: 12px; }
.gm-tag-post { right: 12px; }
/* invitación: botón mínimo centrado + rombo que pulsa */
.gm-start {
  position: absolute; inset: 0; z-index: 3;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 13px;
  color: var(--cream);
  transition: opacity .35s ease;
}
.gm-start-label {
  font-size: .68rem; letter-spacing: .42em; font-weight: 500;
  padding-left: .42em;
  transition: color .25s;
}
.gm-start:hover .gm-start-label { color: var(--red); }
.gm-start-dia {
  width: 8px; height: 8px; rotate: 45deg;
  background: var(--red); display: block;
  animation: gmpulse 2s ease-in-out infinite;
}
@keyframes gmpulse {
  0%, 100% { opacity: .35; box-shadow: 0 0 0 rgba(224, 9, 29, 0); }
  50% { opacity: 1; box-shadow: 0 0 12px rgba(224, 9, 29, .5); }
}
.grade-monitor.live .gm-start { opacity: 0; pointer-events: none; }
/* selectores: proyectos reales, con nombre — claros y fáciles de cliquear */
.gm-selects {
  display: flex; gap: 2px; margin-top: 10px;
  opacity: 0; translate: 0 6px; pointer-events: none;
  transition: opacity .5s ease .35s, translate .5s ease .35s;
}
.grade-monitor.live .gm-selects { opacity: 1; translate: 0 0; pointer-events: auto; }
.gm-select {
  flex: 1; padding: 10px 0 8px;
  font-size: .62rem; letter-spacing: .3em; font-weight: 500;
  color: rgba(244, 239, 230, .4);
  border-bottom: 2px solid rgba(244, 239, 230, .12);
  transition: color .25s, border-color .25s;
}
.gm-select:hover { color: rgba(244, 239, 230, .85); }
.gm-select.on { color: var(--red); border-color: var(--red); }
/* P9: brasas del arrastre. z-index negativo → pintan por DEBAJO del fondo
   opaco de .gm-frame (que es estático), así que nacen en el borde y se ven
   solo al salir: leen como si vinieran de detrás del marco. El inset
   negativo es el margen por el que pueden viajar. Misma mezcla `screen` que
   las motas del hero (.dust-video). */
/* OJO: <canvas> es un elemento REEMPLAZADO — `inset` negativo NO lo estira
   como a un div, se queda en su tamaño intrínseco (300x150) y además
   realimenta el atributo width. Las medidas van explícitas con calc(). */
.gm-embers {
  position: absolute;
  top: -72px; left: -72px;
  width: calc(100% + 144px);
  height: calc(100% + 144px);
  z-index: -1; pointer-events: none;
  mix-blend-mode: screen;
}
@media (max-width: 900px) {
  /* móvil: el monitor baja al pie del pilar 03, centrado y tocable;
     el padding del pilar 03 le despeja el espacio en la composición */
  .pillar[data-i="2"] { padding-bottom: min(44vh, 370px); }
  /* P9: menos sangrado — abajo el módulo queda cerca del borde de #pilares,
     que recorta con overflow:hidden, y de nada sirve emitir hacia una zona
     que no se ve */
  .gm-embers {
    top: -44px; left: -44px;
    width: calc(100% + 88px);
    height: calc(100% + 88px);
  }
  .grade-monitor {
    top: auto; bottom: 3vh; right: 50%;
    width: min(88vw, 430px);
    transform: translateX(50%);
  }
  .grade-monitor.live { transform: translateX(50%); }
}

/* ============ INSTRUMENTO DE STEMS — MÓDULO DEL PILAR 04 ============ */
/* E5: dejó de ser una sección propia a pantalla completa. El instrumento
   ocupaba un viewport entero en mitad del recorrido, así que atravesarlo era
   obligatorio para llegar al contacto — y el audio arrancaba fuerte. Ahora
   toma prestado el contrato del módulo de color grade, que sí funciona:
   contenido dentro del pilar, invisible mientras el pilar no está activo,
   mudo hasta que alguien pulsa INICIAR, y sin capturar nunca el scroll.
   Comparte la ranura con .grade-monitor: 03 y 04 se excluyen, nunca coinciden.
   La chapa (borde + glow rojo) es deliberadamente la misma de .gm-frame —
   los dos módulos del pilar tienen que leerse como la misma pieza de equipo. */
.mono-monitor {
  position: absolute; z-index: 5;
  /* E7G 7: más adentro. Medido a 1440: la tinta del pilar termina en x=715 y
     el módulo arrancaba en 794, tan pegado al borde que el ojo no llegaba.
     Ahora arranca en ~751, prácticamente sobre el eje central de la pantalla
     (720). Es lo máximo que se puede acercar sin tocar el texto. */
  top: 50%; right: clamp(40px, 15vw, 250px);
  width: min(40vw, 640px);
  transform: translateY(-50%);
  opacity: 0; visibility: hidden; pointer-events: none;
  /* visibility fuera de la transición a propósito: ver la nota de
     .grade-monitor — mantenerla en la lista deja el módulo sin hit-testing
     durante todo el fundido, y cada click de esa ventana cae en lo de atrás. */
  transition: opacity .5s ease;
  /* medidas compartidas: la columna de etiquetas y el gap los usan tanto los
     carriles como la zona del cabezal (deben coincidir exactamente) */
  --label-w: clamp(58px, 5.2vw, 82px);
  --lane-gap: clamp(9px, 1.1vw, 15px);
  /* E5B ②: aire interior de la caja. Etiquetas, ondas y cabezal estaban a
     ras del marco. El horizontal es mayor que el vertical a propósito: es el
     lado donde se notaba (el nombre pegado al filo rojo) y el vertical se
     paga en alto de onda, que es más escaso. */
  --box-pad-x: clamp(11px, 1.15vw, 16px);
  --box-pad-y: clamp(7px, .75vw, 10px);
  /* E5B ②: +16px para que el aire interior salga del marco y no del alto de
     onda — el área útil de los carriles queda como estaba. */
  --block-h: clamp(212px, 29vh, 298px);
  --block-h-idle: clamp(104px, 14vh, 138px);
}
.mono-monitor.on { opacity: 1; visibility: visible; pointer-events: auto; }

/* bloque + fader en una sola fila. flex-start y no flex-end: los controles
   cuelgan por debajo del bloque y reservan su sitio siempre (aparecen con
   visibility, no con display), así encender la mezcla no mueve nada. */
.mono-rig {
  display: flex; align-items: flex-start;
  gap: clamp(12px, 1.6vw, 22px);
  width: 100%;
}
.mono-column { flex: 1 1 auto; min-width: 0; }
.mono-block {
  position: relative;
  width: 100%;
  height: var(--block-h-idle); /* ancla el reparto 50/50 de las paredes */
  background: rgba(8, 8, 9, .92);
  border: 1px solid rgba(244, 239, 230, .16);
  box-shadow: 0 0 0 1px rgba(224, 9, 29, .10), 0 0 38px rgba(224, 9, 29, .12), 0 24px 60px rgba(0, 0, 0, .5);
  overflow: hidden; /* las paredes se deslizan ±102%: se contienen aquí */
  /* apertura escénica: crece centrada, así los límites superior e inferior
     se separan hacia arriba y hacia abajo a la vez */
  transition: height .95s cubic-bezier(.16, 1, .3, 1),
              box-shadow .6s ease, border-color .6s ease;
}
.mono-monitor.started .mono-block {
  height: var(--block-h);
  border-color: rgba(224, 9, 29, .4);
  box-shadow: 0 0 0 1px rgba(224, 9, 29, .22), 0 0 54px rgba(224, 9, 29, .22), 0 24px 60px rgba(0, 0, 0, .5);
}

/* ---- paredes: letterbox invertido — cubren hasta INICIAR MEZCLA ---- */
.mono-wall {
  position: absolute; left: 0; right: 0; z-index: 8; height: 50%;
  background: var(--ink);
  /* decoración pura: si el transform de apertura no llegara a aplicarse, una
     pared que capture punteros dejaría el instrumento muerto al click */
  pointer-events: none;
  transition: translate .95s cubic-bezier(.16, 1, .3, 1);
}
.mono-wall-top { top: 0; border-bottom: 1px solid rgba(224, 9, 29, .22); }
.mono-wall-bottom { bottom: 0; border-top: 1px solid rgba(224, 9, 29, .22); }
.mono-block.open .mono-wall-top { translate: 0 -102%; }
.mono-block.open .mono-wall-bottom { translate: 0 102%; }

.mono-start {
  position: absolute; inset: 0; z-index: 9;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 11px;
  color: var(--cream);
  transition: opacity .3s ease;
}
.mono-start:hover .mono-start-label { color: var(--red); }
.mono-start-label {
  font-size: clamp(.62rem, .85vw, .74rem); letter-spacing: .4em; font-weight: 500;
  padding-left: .4em;
  transition: color .25s;
}
.mono-start-dia {
  width: 8px; height: 8px; rotate: 45deg;
  background: var(--red); display: block;
  animation: gmpulse 2s ease-in-out infinite; /* pulso compartido con el módulo de grade */
}
.mono-block.open .mono-start { opacity: 0; pointer-events: none; }

/* ---- instrumento: 5 carriles + cabezal, ocultos detrás de las paredes ---- */
.mono-instrument {
  position: absolute; z-index: 1;
  /* E5B ②: el aire va AQUÍ y no en .mono-block, porque las paredes de
     apertura y el botón de invitación son hermanos de este contenedor y
     tienen que seguir cubriendo el marco entero. Y va como `inset` y no como
     `padding` porque .mono-playhead-zone se posiciona DENTRO de este
     contenedor: insetándolo, el cabezal se mueve con los carriles y no queda
     una segunda medida que mantener a mano en sincronía. */
  inset: var(--box-pad-y) var(--box-pad-x);
  display: flex; flex-direction: column; gap: 3px; /* líneas de fractura */
}
.mono-block:not(.open) .mono-instrument { pointer-events: none; }

/* zona del cabezal: empieza donde empiezan las ondas, así el barrido nunca
   corre por encima de la columna de etiquetas */
.mono-playhead-zone {
  position: absolute; top: 0; bottom: 0; z-index: 6;
  left: calc(var(--label-w) + var(--lane-gap)); right: 0;
  pointer-events: none;
}
.mono-playhead {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 2px; margin-left: -1px;
  background: var(--red);
  box-shadow: 0 0 12px rgba(224, 9, 29, .55);
  cursor: none;
  pointer-events: auto;
}
/* franja invisible ancha alrededor de la línea de 2px — el cabezal se puede
   agarrar sin puntería quirúrgica */
.mono-playhead-grab {
  position: absolute; inset: 0 -16px;
  touch-action: none;
}

.mono-layer {
  position: relative;
  flex: 1 1 0; min-height: 0;
  display: flex; align-items: center; gap: var(--lane-gap);
}
/* columna fija, fuera del área de la forma de onda — se lee de un vistazo.
   E5 ④: ahora es zona activa. align-self:stretch + flex interno para que el
   área clickeable sea la ALTURA ENTERA del carril y no solo la caja del
   texto: la fila completa responde, que es lo que la gente intenta primero. */
.mono-label {
  flex: 0 0 auto;
  width: var(--label-w);
  align-self: stretch;
  display: flex; align-items: center;
  font-size: clamp(.58rem, .75vw, .7rem); letter-spacing: .2em; font-weight: 500;
  color: rgba(244, 239, 230, .78);
  white-space: nowrap;
  cursor: none;
  user-select: none; -webkit-user-select: none;
  touch-action: manipulation;
  transition: color .3s ease;
}
.mono-block.open .mono-label:hover { color: var(--red); }
.mono-layer.is-out .mono-label { color: rgba(244, 239, 230, .32); }
.mono-track {
  position: relative; flex: 1 1 auto; height: 100%;
  overflow: hidden;
}
/* la ranura queda siempre visible detrás — es "el hueco" cuando se retira */
.mono-slot {
  position: absolute; inset: 0;
  background: rgba(244, 239, 230, .03);
  border: 1px dashed rgba(224, 9, 29, .16);
}
.mono-slab {
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(180deg, rgba(20, 18, 19, .96), rgba(10, 9, 10, .98));
  border: 1px solid rgba(224, 9, 29, .3);
  box-shadow: inset 0 0 0 1px rgba(244, 239, 230, .04), 0 0 16px rgba(224, 9, 29, .1);
  cursor: none;
  touch-action: none; /* el arrastre lateral jamás se vuelve scroll */
  user-select: none; -webkit-user-select: none;
  /* easeOutBack: sobrepasa levemente y se asienta — "se traba" como un
     interruptor real, no una animación plana */
  transition: translate .5s cubic-bezier(.34, 1.56, .64, 1),
              opacity .35s ease, box-shadow .35s ease, border-color .35s ease;
}
.mono-slab.no-transition { transition: none; }
.mono-layer.is-out .mono-slab {
  opacity: .26;
  border-color: rgba(244, 239, 230, .1);
  box-shadow: none;
}
.mono-wave {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  pointer-events: none;
}

/* ---- controles: NO existen hasta iniciar (E5 ④) ----
   El usuario de la prueba encontró primero el botón de pausa, lo tocó sin
   nada sonando, no vio ningún efecto y abandonó. Un transporte antes de que
   haya algo que transportar es una promesa falsa. Aparece con `started`, la
   misma bandera que abre las paredes: condición síncrona de un click
   explícito, no un temporizador ni el final de una animación (ver P3H). */
.mono-controls {
  display: flex; align-items: center; gap: 12px;
  margin-top: 12px;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .35s ease;
}
.mono-monitor.started .mono-controls { opacity: 1; visibility: visible; pointer-events: auto; }
/* botón con marco: dos barras = sonando, triángulo = pausado */
.mono-pause {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 8px 15px 8px 11px;
  border: 1px solid rgba(224, 9, 29, .55);
  background: rgba(224, 9, 29, .1);
  cursor: none;
  transition: background .25s, border-color .25s;
}
.mono-pause:hover { background: rgba(224, 9, 29, .2); border-color: var(--red); }
.mp-glyph {
  position: relative; display: inline-flex; align-items: center; gap: 3px;
  width: 12px; height: 13px;
}
.mp-bar {
  width: 3.5px; height: 13px; background: var(--red); display: block;
  transition: opacity .2s;
}
.mp-tri {
  position: absolute; left: 1px; top: 50%; translate: 0 -50%;
  width: 0; height: 0;
  border-left: 11px solid var(--red);
  border-top: 6.5px solid transparent;
  border-bottom: 6.5px solid transparent;
  opacity: 0; transition: opacity .2s;
}
.mono-pause.is-paused .mp-bar { opacity: 0; }
.mono-pause.is-paused .mp-tri { opacity: 1; }
.mono-pause-label {
  font-size: .6rem; letter-spacing: .26em; font-weight: 500;
  color: var(--cream);
}
/* pista de uso: dice qué hacer, y se va sola en cuanto se hace (E5 ④) */
.mono-hint {
  font-size: .58rem; letter-spacing: .24em; font-weight: 500;
  color: var(--red);
  white-space: nowrap;
  animation: monoHint 2.2s ease-in-out infinite;
  transition: opacity .5s ease;
}
@keyframes monoHint { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }
.mono-hint.gone { opacity: 0; animation: none; }

/* ---- fader vertical: consola de estudio, componente propio ---- */
.mono-fader {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  height: var(--block-h-idle);
  opacity: 0; pointer-events: none;
  transition: opacity .4s ease, height .95s cubic-bezier(.16, 1, .3, 1);
}
.mono-monitor.started .mono-fader { height: var(--block-h); }
.mono-fader.on { opacity: 1; pointer-events: auto; }
.mf-rail {
  position: relative;
  width: 24px; flex: 1 1 auto;
  border: 1px solid rgba(244, 239, 230, .12);
  background: rgba(244, 239, 230, .03);
  cursor: none;
  touch-action: none; /* el arrastre vertical no scrollea la página */
  user-select: none; -webkit-user-select: none;
}
/* el riel delgado real, centrado dentro del área de agarre */
.mf-rail::before {
  content: '';
  position: absolute; left: 50%; top: 8px; bottom: 8px;
  width: 2px; margin-left: -1px;
  background: rgba(244, 239, 230, .16);
}
.mf-tick {
  position: absolute; right: calc(100% + 5px);
  width: 6px; height: 1px;
  background: rgba(244, 239, 230, .25);
}
.mf-fill {
  position: absolute; left: 50%; width: 2px; margin-left: -1px;
  bottom: 8px; top: 50%; /* el top lo conduce el fader en JS */
  background: var(--red);
  box-shadow: 0 0 8px rgba(224, 9, 29, .45);
}
/* handle rectangular, como el cuadrito angular de un fader físico */
.mf-handle {
  position: absolute; left: 50%; top: 0;
  width: 28px; height: 13px;
  margin-left: -14px; margin-top: -6.5px;
  background: linear-gradient(180deg, #2a2729, #131213);
  border: 1px solid rgba(224, 9, 29, .55);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .6);
  cursor: none;
  transition: border-color .2s, box-shadow .2s;
}
/* muesca central, el detalle que lo lee como pieza física */
.mf-handle::after {
  content: '';
  position: absolute; left: 4px; right: 4px; top: 50%;
  height: 1px; margin-top: -.5px;
  background: rgba(224, 9, 29, .8);
}
.mf-rail:hover .mf-handle, .mf-handle.dragging {
  border-color: var(--red);
  box-shadow: 0 0 14px rgba(224, 9, 29, .4);
}
.mf-label {
  font-size: .52rem; letter-spacing: .24em; font-weight: 500;
  color: rgba(244, 239, 230, .45);
}

/* ---- remate: pie del módulo ----
   Siempre visible, sin estado ni animación (P3H: cuatro rondas de fallos
   vinieron de atar su visibilidad a que algo se completara — no se reabre).
   A escala de módulo: es un pie, no un titular de sección. */
.mono-outro { margin-top: 14px; }
.mono-outro-line {
  font-family: var(--font-display);
  /* E7F 1.2: Anton solo existe en 400. Estos son <h2>/<h3>, y la hoja del
     navegador les impone bold=700: el navegador lo resolvía ENGORDANDO el
     contorno por trazo. Medido: +19,9% de tinta con 0 px de ancho extra —
     bordes empastados y contraformas cerradas, justo a 158 px. Declararlo en
     400 no mueve NADA de sitio y devuelve la letra dibujada. */
  font-weight: 400;
  font-size: clamp(1rem, 1.55vw, 1.4rem);
  line-height: 1.02; letter-spacing: .005em;
  color: var(--cream);
  margin-bottom: .45em;
}
.mono-outro-sub {
  /* E7F C3: 300 -> 400. A ~13 px en crema sobre negro el 300 pierde trazo por
     sangrado óptico: el texto claro se expande sobre el fondo oscuro y adelgaza
     el palo. (Corrección a mi propia auditoría: dije "por debajo de 10 px" y no
     existe ninguna etiqueta de 300 tan pequeña; la más chica es de 13,2 px.) */
  font-size: clamp(.72rem, .92vw, .84rem);
  font-weight: 400; line-height: 1.45;
  color: rgba(244, 239, 230, .62);
}

@media (max-width: 900px) {
  /* móvil: el módulo baja al pie del pilar 04, centrado y tocable — misma
     maniobra que el monitor de grade en el pilar 03 */
  .pillar[data-i="3"] { padding-bottom: min(50vh, 420px); }
  .mono-monitor {
    top: auto; bottom: 3vh; right: 50%;
    width: min(88vw, 430px);
    transform: translateX(50%);
  }
}

@media (max-width: 700px) {
  .mono-monitor {
    --label-w: 50px; --lane-gap: 8px;
    --box-pad-x: 9px; --box-pad-y: 6px;
    --block-h: clamp(190px, 27vh, 248px);
    --block-h-idle: clamp(96px, 13vh, 122px);
  }
  .mono-rig { gap: 9px; }
  .mono-label { font-size: .52rem; letter-spacing: .12em; }
  .mono-controls { gap: 9px; }
  .mf-rail { width: 30px; } /* área de agarre cómoda para el dedo */
  .mf-tick { display: none; }
  .mono-outro-line { font-size: 1rem; }
  .mono-outro-sub { font-size: .74rem; }
}

@media (max-height: 760px) {
  .mono-monitor {
    --block-h: clamp(184px, 25vh, 244px);
    --block-h-idle: clamp(92px, 12vh, 118px);
  }
  .mono-outro { margin-top: 10px; }
}

/* ============ E4 ⑤⑥ — PORTAFOLIO Y CIERRE EN VERTICAL ============ */
/* Va al final a propósito: `.cards` y `.final-title` se declaran más arriba
   y, a igual especificidad, gana la regla que viene después. */
@media (max-width: 700px) {
  /* ⑤ Las tarjetas se aplastaban a 83px de alto —el grid encogía dentro del
     flex de altura fija— y los videos quedaban en 147px de ancho, con un
     vacío de 379px debajo. Dos columnas y la tercera de par en par: cada
     pieza gana tamaño real y el conjunto llena el encuadre sin desbordar. */
  .work-wrap {
    /* E8 ②: el título arrancaba a 8vh = 53 px a 664 de alto, DEBAJO del nav
       fijo (70 px). El margen superior se fija en px porque el nav mide px. */
    padding: 84px clamp(16px, 5vw, 40px) 6vh;
    /* el aire sobrante se reparte ENTRE título y piezas, no se acumula al
       pie: así se lee como composición y no como un hueco */
    justify-content: space-between;
    gap: 3vh;
  }
  .work-title { font-size: clamp(2.6rem, 13vw, 4.4rem); }
  .cards {
    grid-template-columns: repeat(2, 1fr);
    gap: 2.4vh 14px;
    flex: 0 0 auto; /* sin esto el flex padre lo comprime */
  }
  .cards .card:nth-child(3) { grid-column: 1 / -1; }
  /* La regla de ≤900px vuelve cada tarjeta un grid horizontal (42% media /
     1fr texto, centrado). Ahí el media nunca se estira: su altura la fija el
     bloque de texto y el aspect-ratio le deriva un ancho de 71px. En vertical
     el video manda, así que vuelve a apilarse y ocupa el ancho completo. */
  .card { display: block; grid-template-columns: none; }
  .card-meta { padding-top: 10px; }
  .card-meta h3 { font-size: 1rem; margin-bottom: 4px; }
  .card-cta { font-size: .69rem; letter-spacing: var(--track-label); }

  /* ⑥ El título se pisaba a sí mismo: line-height 39.1px por debajo de un
     font-size de 41.6px hace que las líneas se solapen, y a ese cuerpo la
     primera frase wrapeaba a tres líneas. Cuerpo menor e interlineado > 1. */
  .final-wrap {
    gap: 4vh;
    padding: 0 7vw;
    justify-content: center;
  }
  .final-title {
    font-size: clamp(1.75rem, 8.6vw, 2.5rem);
    line-height: 1.08;
    text-wrap: balance;
  }
  .final-ctas { gap: 12px; width: 100%; }
  .final-ctas .btn {
    width: 100%; padding: 18px 20px;
    font-size: .72rem; letter-spacing: .18em;
  }
}

/* ============ E7E — prefers-reduced-motion ============
   Qué se apaga y qué NO, y por qué.

   Se apaga TODO lo que se mueve solo: los pulsos del HUD, del botón de sonido,
   de la pantalla de entrada, del aviso de scroll en móvil y de los módulos
   interactivos. Son animaciones `infinite`: nadie las pidió, no paran nunca, y
   son exactamente el tipo de movimiento que dispara malestar vestibular.

   NO se toca el movimiento conducido por scroll (el cubo, las cuchillas, los
   pilares, el instrumento). Ese movimiento ES la respuesta al gesto del
   usuario: mientras no arrastre, nada se mueve. Apagarlo no sería respetar la
   preferencia, sería romper el sitio y dejar la página en blanco. La media
   query pide reducir el movimiento no esencial, no eliminar la interfaz.

   Tampoco se tocan los reveals de una sola pasada (glitchflick, pillarin):
   duran menos de 200 ms, van atados al scroll y varios dejan el estado final
   del elemento — apagarlos a ciegas dejaría texto invisible. */
@media (prefers-reduced-motion: reduce) {
  #sound-toggle:not(.used)::before,
  .st-bars i,
  .hud-line,
  .hud-tag,
  .hud-arrow,
  #m-cue .mc-line,
  #m-cue .mc-arrow,
  .gm-start-dia,
  .mono-start-dia,
  .mono-hint,
  #reel-open {
    animation: none !important;
  }
  /* el HUD y el aviso móvil viven de su pulso: sin él quedarían apagados,
     así que se dejan fijos en su estado legible en vez de invisibles */
  .hud-tag { color: rgba(224, 9, 29, .85); }
  .st-bars i { transform: scaleY(.62); }
  /* las transiciones se acortan, no se anulan: a 0 s los cambios de estado
     dan saltos secos, que también es movimiento brusco */
  * { transition-duration: .08s !important; }
}

/* ============ E7G 7b + 8 — LOS MÓDULOS SE ANUNCIAN, Y EL REMATE SE LEE ====

   7b — LLAMADA DE ATENCIÓN. El módulo entraba en silencio: aparecía a la
   derecha y el ojo, que venía leyendo la columna izquierda, nunca llegaba.
   Al activarse ahora da DOS pulsos de glow rojo con un velo de ruido y se
   asienta. No es un efecto nuevo: son exactamente los canales que ya mueve el
   arrastre del módulo de color grade (borde + halo rojo, ver --gm-drag), aquí
   disparados una sola vez. Se apaga solo; después el módulo queda como estaba.

   La animación va en el CONTENEDOR y no en el marco para no pisar la
   transición de box-shadow que el arrastre necesita libre. */
@keyframes modulo-aviso {
  0%   { box-shadow: 0 0 0 0 rgba(224, 9, 29, 0); }
  12%  { box-shadow: 0 0 0 2px rgba(224, 9, 29, .95), 0 0 96px 18px rgba(224, 9, 29, .62); }
  30%  { box-shadow: 0 0 0 1px rgba(224, 9, 29, .28), 0 0 40px 2px rgba(224, 9, 29, .18); }
  46%  { box-shadow: 0 0 0 2px rgba(224, 9, 29, .88), 0 0 88px 14px rgba(224, 9, 29, .55); }
  64%  { box-shadow: 0 0 0 1px rgba(224, 9, 29, .24), 0 0 36px 0 rgba(224, 9, 29, .15); }
  78%  { box-shadow: 0 0 0 1px rgba(224, 9, 29, .62), 0 0 66px 8px rgba(224, 9, 29, .36); }
  100% { box-shadow: 0 0 0 0 rgba(224, 9, 29, 0); }
}
/* el ruido: el mismo grano de la casa, en un velo que sube y baja con el pulso */
@keyframes modulo-ruido {
  0%, 100% { opacity: 0; }
  12% { opacity: .34; }
  30% { opacity: .08; }
  46% { opacity: .30; }
  64% { opacity: .07; }
  78% { opacity: .18; }
}
/* E7H 6: tres pulsos en vez de dos, halo casi el doble de ancho (96 px contra
   62), alfa de .34 a .62 y un aro de 2 px que sí se ve. El aviso era tan sutil
   que no cumplía su único trabajo. */
/* E8b: en el módulo 04 el pulso y el ruido van en .mono-rig (el instrumento
   con su fader), NO en .mono-monitor: el contenedor incluye el remate, y el
   anillo rojo lo encerraba en la misma caja — "un bloque", justo lo que se
   quiere evitar. */
.grade-monitor.on, .mono-monitor.on .mono-rig { animation: modulo-aviso 3s cubic-bezier(.4, 0, .3, 1) 1; }
.mono-rig { position: relative; }
.grade-monitor::after, .mono-rig::after {
  content: '';
  position: absolute; inset: 0; z-index: 6;
  pointer-events: none; opacity: 0;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='r' color-interpolation-filters='sRGB'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url(%23r)'/></svg>");
  background-size: 140px 140px;
}
.grade-monitor.on::after, .mono-monitor.on .mono-rig::after { animation: modulo-ruido 3s cubic-bezier(.4, 0, .3, 1) 1; }

/* 8 — EL REMATE DEL PILAR 04 NECESITA FONDO.
   "ESTO LO HICIMOS NOSOTROS" colgaba directamente sobre el video, y en los
   fotogramas rojos desaparecía. Recibe el MISMO tratamiento de panel que
   .mono-block, que es la caja de al lado: mismo fondo, mismo borde, misma
   sombra roja. No es un recuadro nuevo, es la misma chapa. */
.mono-outro {
  /* E8b: tercera versión, y la definitiva por diseño: NO es un panel. El
     remate es un PIE DE FOTO — dos líneas centradas bajo el instrumento— y lo
     que garantiza su lectura sobre el video es un velo negro DIFUMINADO
     detrás del texto (un ::before con blur), sin cantos, sin fondo plano,
     sin máscara con forma. El anterior, aun degradado, seguía leyéndose como
     "un bloque duro" (cliente). Un velo desenfocado no tiene borde que leer. */
  position: relative;
  margin-top: 22px;
  padding: 10px 18px 8px;
  text-align: center;
  background: none;
  -webkit-mask-image: none; mask-image: none;
}
.mono-outro::before {
  content: '';
  position: absolute; inset: -4px 4%; z-index: -1;
  border-radius: 40%;
  background: rgba(6, 6, 7, .82);
  filter: blur(18px);
  pointer-events: none;
}
.mono-outro-line { margin-left: auto; margin-right: auto; }
.mono-outro-sub { margin-left: auto; margin-right: auto; max-width: 42ch; }

@media (prefers-reduced-motion: reduce) {
  .grade-monitor.on, .mono-monitor.on .mono-rig,
  .grade-monitor.on::after, .mono-monitor.on .mono-rig::after { animation: none !important; }
}

/* ============ E7G 10 — VISOR DEL REEL ============
   El botón mandaba a YouTube: sacaba al visitante del sitio justo en el
   momento en que había decidido mirar el trabajo. Ahora el reel se ve aquí,
   en un visor propio: negro pleno, el video y nada más. Sin cromo. */
#reel-modal {
  position: fixed; inset: 0; z-index: 1300;
  display: grid; place-items: center;
  opacity: 0; transition: opacity .45s ease;
}
#reel-modal[hidden] { display: none; }
#reel-modal.abierto { opacity: 1; }
.reel-backdrop {
  position: absolute; inset: 0;
  background: rgba(4, 4, 5, .93);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.reel-box {
  position: relative;
  width: min(880px, 78vw);
  /* el reel es 16:9 nativo; la caja lo respeta para que no aparezcan bandas */
  aspect-ratio: 16 / 9;
  max-height: 74vh;
  background: #000;
  border: 1px solid rgba(244, 239, 230, .14);
  box-shadow: 0 0 0 1px rgba(224, 9, 29, .12), 0 0 70px rgba(224, 9, 29, .10), 0 40px 100px rgba(0, 0, 0, .7);
  scale: .97; opacity: 0;
  transition: scale .5s cubic-bezier(.16, 1, .3, 1), opacity .4s ease;
}
#reel-modal.abierto .reel-box { scale: 1; opacity: 1; }
#reel-player { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; }
.reel-close {
  position: absolute; top: -14px; right: -14px; z-index: 2;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  font-family: var(--font-wordmark);
  font-size: 1.5rem; font-weight: 300; line-height: 1;
  color: var(--cream);
  background: var(--ink);
  border: 1px solid rgba(244, 239, 230, .22);
  border-radius: 50%;
  cursor: pointer;
  transition: background .25s, color .25s, border-color .25s, rotate .35s cubic-bezier(.16, 1, .3, 1);
}
.reel-close:hover, .reel-close:focus-visible {
  background: var(--red); color: var(--ink); border-color: var(--red);
  rotate: 90deg; outline: none;
}
.reel-tag {
  position: absolute; left: 0; bottom: -26px;
  font-size: .69rem; letter-spacing: var(--track-btn);
  color: rgba(244, 239, 230, .5);
  padding-left: var(--track-btn);
}
@media (max-width: 700px) {
  .reel-box { width: 94vw; }
  .reel-close { top: -12px; right: -6px; width: 40px; height: 40px; }
}

/* ============ E7H 1 — DENTRO DEL VISOR SIEMPRE HAY CURSOR ============
   BUG CRÍTICO. El sitio esconde el cursor nativo (`cursor: none` en body,
   a, button y .card) y lo sustituye por el anillo rojo #cursor, que vive en
   z-index 1001. El visor del reel se abre en 1300: el anillo quedaba pintado
   POR DEBAJO y el nativo seguía oculto. Resultado: ni anillo ni flecha —
   imposible dar play, pausa o cerrar. Quedabas encerrado.

   La corrección no pelea por el apilamiento, que es frágil: DENTRO del visor
   manda el cursor NATIVO y el anillo se retira. Un elemento con controles de
   video nativos necesita la flecha del sistema de todas formas, y la flecha
   del sistema no puede quedar por debajo de nada. */
html.reel-abierto #cursor { display: none; }
#reel-modal, #reel-modal * { cursor: default; }
#reel-modal video { cursor: default; }
#reel-modal .reel-backdrop { cursor: pointer; } /* el ID de arriba le ganaba por especificidad */
#reel-modal .reel-close { cursor: pointer; }

/* ============ E7H 7 → E8 ⑪ — EL GRANO SE APARTA DEL MÓDULO DE COLORIZACIÓN ==
   Las imágenes sin gradear son S-Log3: planas y muy claras. La capa #grain va
   en `overlay`, que es justo donde más pega —en medios y luces—, así que el
   grano se comía el detalle en el único sitio donde el usuario está mirando
   color.

   E8 ⑪ — POR QUÉ LA VERSIÓN ANTERIOR NO BASTÓ. E7H abría una ELIPSE con
   radios de 0,62×ancho y 0,68×alto y un degradado que a medio radio ya dejaba
   pasar el 8% y al 82% el 55%. Medido sobre el rectángulo del módulo: el
   centro quedaba limpio, pero en el borde horizontal de la imagen (81% del
   radio) pasaba la MITAD del grano y las cuatro esquinas caían FUERA de la
   elipse, con el grano entero. El hueco solo servía en el centro; el cliente
   veía grano en la mayor parte de la imagen. Y solo se enmascaraba #grain;
   #grain-lo (screen) sumaba su ruido intacto en los medios.

   AHORA: un RECTÁNGULO —el del propio marco del módulo, medido en píxeles—
   con el borde desvanecido 28 px HACIA FUERA del marco, en las DOS capas de
   grano. Dentro del marco el nivel es uniforme: pasa el 12% del grano
   (--gmask-k = .88 recortado), que conserva la textura de la casa sin tapar
   el detalle. Es el mismo recurso que --grain-cut (enmascarar la capa, no
   bajar su opacidad global), con la forma que hacía falta.

   Mecanismo: tres capas de máscara. La de abajo es una banda VERTICAL
   (opaca entre y0 e y1, desvanecida fuera), la del medio una banda
   HORIZONTAL con la opacidad --gmask-k: su INTERSECCIÓN es el rectángulo
   con bordes suaves. La de arriba es la máscara base de la capa (llena en
   #grain; la banda de --grain-cut en #grain-lo) y le RESTA ese rectángulo.
   En #grain-lo se resta y no se hace xor a propósito: donde --grain-cut ya
   deja la capa a cero, xor volvería a ENCENDERLA. Las variables las escribe
   initGrade (grade.js) con el rect real del marco; con los valores por
   defecto el rectángulo es de ancho cero y la máscara no hace nada. */
#grain, #grain-lo {
  --gmask-x0: -1px; --gmask-x1: -1px;
  --gmask-y0: -1px; --gmask-y1: -1px;
  --gmask-f: 28px;  /* desvanecido hacia fuera del marco */
  --gmask-k: .88;   /* cuánto grano se quita dentro (queda el 12%) */
}
#grain {
  -webkit-mask-image:
    linear-gradient(#000 0 0),
    linear-gradient(to right,
      transparent calc(var(--gmask-x0) - var(--gmask-f)),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x0),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x1),
      transparent calc(var(--gmask-x1) + var(--gmask-f))),
    linear-gradient(to bottom,
      transparent calc(var(--gmask-y0) - var(--gmask-f)),
      #000 var(--gmask-y0), #000 var(--gmask-y1),
      transparent calc(var(--gmask-y1) + var(--gmask-f)));
  -webkit-mask-composite: source-out, source-in, source-over;
  mask-image:
    linear-gradient(#000 0 0),
    linear-gradient(to right,
      transparent calc(var(--gmask-x0) - var(--gmask-f)),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x0),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x1),
      transparent calc(var(--gmask-x1) + var(--gmask-f))),
    linear-gradient(to bottom,
      transparent calc(var(--gmask-y0) - var(--gmask-f)),
      #000 var(--gmask-y0), #000 var(--gmask-y1),
      transparent calc(var(--gmask-y1) + var(--gmask-f)));
  mask-composite: subtract, intersect, add;
}
#grain-lo {
  -webkit-mask-image:
    linear-gradient(to bottom, #000 calc(100% - var(--grain-cut, 0%)), transparent 100%),
    linear-gradient(to right,
      transparent calc(var(--gmask-x0) - var(--gmask-f)),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x0),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x1),
      transparent calc(var(--gmask-x1) + var(--gmask-f))),
    linear-gradient(to bottom,
      transparent calc(var(--gmask-y0) - var(--gmask-f)),
      #000 var(--gmask-y0), #000 var(--gmask-y1),
      transparent calc(var(--gmask-y1) + var(--gmask-f)));
  -webkit-mask-composite: source-out, source-in, source-over;
  mask-image:
    linear-gradient(to bottom, #000 calc(100% - var(--grain-cut, 0%)), transparent 100%),
    linear-gradient(to right,
      transparent calc(var(--gmask-x0) - var(--gmask-f)),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x0),
      rgba(0, 0, 0, var(--gmask-k)) var(--gmask-x1),
      transparent calc(var(--gmask-x1) + var(--gmask-f))),
    linear-gradient(to bottom,
      transparent calc(var(--gmask-y0) - var(--gmask-f)),
      #000 var(--gmask-y0), #000 var(--gmask-y1),
      transparent calc(var(--gmask-y1) + var(--gmask-f)));
  mask-composite: subtract, intersect, add;
}

/* ============ E8 ⑤ — GRANO MÁS FINO Y MÁS BAJO EN TELÉFONO ============
   El tamaño de partícula lo resuelve initGrain (canvas a densidad de
   dispositivo, bloque de 1 px). Aquí va la INTENSIDAD: las dos capas bajan
   al 55% de su nivel. La jerarquía luces/sombras entre #grain y #grain-lo se
   conserva porque el factor es el mismo para las dos, y los multiplicadores
   por sección (--grain-mul, ver GRAIN_SECTION) siguen operando encima. */
@media (max-width: 700px) {
  #grain { opacity: calc(.12 * .55 * var(--grain-mul, 1)); }
  /* E8b: la capa de sombras (screen) se retira en teléfono. Cada capa a
     pantalla completa con mix-blend-mode obliga al compositor a fundir toda
     la página por cuadro; en un iPhone dos de ellas son de lo más caro que
     hay en el sitio y el cliente sentía el scroll "a pocos fps". Queda la
     capa principal (overlay), que es la que se ve. */
  #grain-lo { display: none; }
}

/* ============ E8 ②⑥⑦ — PILARES EN VERTICAL: RECOMPOSICIÓN ============
   Va al FINAL del archivo a propósito: pisa las reglas de ≤900 de más arriba
   (pilar centrado a 50%, módulos al pie con padding reservado en el pilar).

   ② EL PROBLEMA, medido en el teléfono del cliente (390×664): el pilar iba
   centrado verticalmente con un padding-bottom de 292 px reservado para el
   módulo. Ese padding forma parte de la caja, así que centrar la caja
   empujaba el TEXTO hacia arriba: el título arrancaba en y≈5, debajo del nav
   (0–70), y el antetítulo "LO QUE HACEMOS" (a 13vh = 86) caía sobre la
   segunda línea del título. Tres capas de texto encimadas.

   LA COMPOSICIÓN NUEVA — una columna, anclada por los dos extremos:
   · arriba, una fila de cabecera: "LO QUE HACEMOS" a la izquierda y el
     número del pilar a la derecha, en el mismo renglón, debajo del nav y del
     botón de sonido (que en teléfono se recoge dentro de la fila del nav,
     ver initSoundDock);
   · el texto entra desde la cabecera hacia abajo, CENTRADO (⑥) — todos los
     pilares comparten el mismo `top`, así el título no salta de sitio al
     pasar de uno a otro;
   · el módulo (⑦) se ancla al PIE, con altura CONSTANTE (el instrumento ya
     no crece al iniciarse: en vertical no hay dónde crecer, y la zona baja
     es además la del pulgar). Entre texto y módulo queda el aire que sobre,
     nunca menos de ~18 px en el peor caso (pilar 04 a 664 de alto).
   Los cuerpos van en vh dentro de un clamp: a 664 de alto se leen a 32/13,6
   px; a 844 a 41/15,2. El presupuesto vertical lo fija el teléfono más
   bajo, no el más alto. */
@media (max-width: 700px) {
  /* E8b: la cabecera baja a 100 px (el cliente veía el texto "pegado muy
     arriba"). Con la altura de sección corregida (100vh = viewport grande,
     ver .pin-sec) sobra el aire: medido a 390×741, el pilar 03 termina en
     ~424 y el módulo arranca en 461. En teléfonos bajos (≤700 de alto) se
     vuelve al reparto ajustado de 84 px, ver la media query de abajo. */
  .pillars-wrap { --pw-top: 100px; } /* fila de cabecera */
  .pillars-wrap::before {
    /* el scrim ya no es diagonal (el texto no vive en un flanco) sino
       vertical: oscurece la mitad superior, donde está el texto, y suelta
       la imagen hacia el pie, donde está el módulo */
    background: linear-gradient(180deg,
      rgba(6, 6, 6, .86) 0%,
      rgba(6, 6, 6, .74) 42%,
      rgba(6, 6, 6, .30) 66%,
      rgba(6, 6, 6, 0) 86%);
  }
  .sec-kicker {
    top: var(--pw-top); left: 24px;
    font-size: .64rem; letter-spacing: .36em;
    line-height: 1;
  }
  .pillar {
    top: calc(var(--pw-top) + 28px); left: 24px; right: 24px;
    max-width: none;
    transform: none;
    text-align: center;
    padding-bottom: 0 !important; /* el sitio del módulo ya no se reserva aquí */
  }
  @keyframes pillarin {
    from { opacity: 0; transform: translateY(10px) skewY(2.5deg); }
    to   { opacity: 1; transform: none; }
  }
  .pillar-num {
    /* a la fila de cabecera, a la derecha: mismo renglón que el antetítulo */
    position: absolute; right: 0; top: -34px;
    margin: 0;
    font-size: 1.1rem; line-height: 1;
  }
  /* E9: sobre --sec-h (el alto REAL de la sección, lo escribe initSecH) y no
     sobre vh — en Brave iOS las dos medidas no coincidían y el párrafo del
     pilar 03 se metía bajo el módulo. Con --sec-h ausente cae a 100vh. */
  .pillar-title {
    font-size: clamp(2rem, calc(var(--sec-h, 100vh) * .049), 2.6rem);
    line-height: 1.16;
    margin-bottom: calc(var(--sec-h, 100vh) * .016);
    text-wrap: balance;
  }
  .pillar-body {
    font-size: clamp(.84rem, calc(var(--sec-h, 100vh) * .0205), .95rem);
    line-height: 1.45;
    max-width: none;
    margin: 0 auto;
  }
  /* ajuste medido (initSecH): si aun así el párrafo roza el módulo, −7% */
  .pillar.compacto .pillar-body { font-size: clamp(.78rem, calc(var(--sec-h, 100vh) * .019), .89rem); line-height: 1.4; }
  /* el aviso de "hay más abajo" al pie del pin, más bajo: comparte el pie con
     los módulos y cada píxel cuenta */
  #pilares .peek { padding: 9px 20px; line-height: 1; }
  #pilares .peek span { display: inline-block; font-size: .58rem; letter-spacing: .28em; line-height: 1.2; }

  /* ⑦ los módulos: al pie, centrados, altura constante */
  .grade-monitor, .mono-monitor {
    top: auto; bottom: 36px; right: 50%;
    width: min(88vw, 430px);
    transform: translateX(50%);
  }
  .grade-monitor.live { transform: translateX(50%); }
  .mono-monitor {
    /* el bloque nace ya a su altura final: la apertura de las paredes sigue
       ocurriendo, pero dentro de la misma caja. Así el módulo no crece hacia
       el texto al pulsar INICIAR y el reparto vertical es determinista. */
    --block-h: clamp(150px, calc(var(--sec-h, 100vh) * .22), 190px);
    --block-h-idle: var(--block-h);
    --label-w: 46px; --lane-gap: 8px;
    --box-pad-x: 9px; --box-pad-y: 6px;
  }
  .mono-monitor.started .mono-block { height: var(--block-h); }
  .mono-controls { margin-top: 10px; justify-content: center; }
  /* E8b: el fader se retira en teléfono. El volumen ahí lo regulan los
     botones físicos, y su columna (30 px a la derecha) descentraba el
     instrumento respecto del remate: "unas cosas centradas, otras pegadas a
     la izquierda". Sin él, bloque, controles y pie comparten el mismo eje. */
  .mono-fader { display: none; }
  .mono-outro { margin-top: 12px; padding: 8px 10px 6px; }
  .mono-outro-line { font-size: .92rem; margin-bottom: .3em; }
  .mono-outro-sub { font-size: .7rem; line-height: 1.35; }
}
/* (la cabecera a 84 px en secciones bajas la escribe initSecH en --pw-top,
   medida sobre la sección y no sobre el viewport) */

/* ============ E9 ④ — MODAL DEL CORREO ============
   "Empezar un proyecto" y CONTACTO abrían el cliente de correo (mailto:),
   que en escritorio suele lanzar una app que nadie configuró y en teléfono
   saca del sitio. Ahora abren un visor propio, con el idioma del visor del
   reel: negro, filete con acento rojo, el correo grande y centrado, y una
   acción de copiar. El correo sigue siendo un enlace mailto DENTRO del
   modal: quien sí quiere abrir su correo, puede. Va en z-index 1000, por
   DEBAJO del cursor custom (1001): aquí el anillo rojo sigue mandando. */
#mail-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center;
  opacity: 0; transition: opacity .35s ease;
}
#mail-modal[hidden] { display: none; }
#mail-modal.abierto { opacity: 1; }
.mail-backdrop {
  position: absolute; inset: 0;
  background: rgba(4, 4, 5, .9);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.mail-box {
  position: relative;
  width: min(560px, 88vw);
  padding: clamp(34px, 5vw, 56px) clamp(22px, 4vw, 48px) clamp(30px, 4vw, 44px);
  text-align: center;
  background: var(--ink);
  border: 1px solid rgba(244, 239, 230, .14);
  box-shadow: 0 0 0 1px rgba(224, 9, 29, .18), 0 0 70px rgba(224, 9, 29, .12), 0 40px 100px rgba(0, 0, 0, .7);
  scale: .97; opacity: 0;
  transition: scale .45s cubic-bezier(.16, 1, .3, 1), opacity .35s ease;
}
#mail-modal.abierto .mail-box { scale: 1; opacity: 1; }
/* el acento: un filete rojo arriba, el recurso de la casa (antetítulos, ENTRAR) */
.mail-box::before {
  content: '';
  position: absolute; top: 0; left: 50%; translate: -50% 0;
  width: 56px; height: 2px; background: var(--red);
}
.mail-kicker {
  font-size: .69rem; letter-spacing: .18em; padding-left: .18em;
  color: var(--red);
  margin-bottom: clamp(16px, 2.4vw, 26px);
}
.mail-address {
  display: inline-block;
  font-family: var(--font-wordmark);
  font-weight: 600;
  font-size: clamp(1.25rem, 4.2vw, 2.3rem);
  letter-spacing: .01em; line-height: 1.1;
  color: var(--cream);
  word-break: break-all;
  border-bottom: 1px solid rgba(224, 9, 29, .45);
  padding-bottom: 6px;
  transition: color .25s, border-color .25s;
}
.mail-address:hover { color: var(--red); border-color: var(--red); }
.mail-copy {
  display: inline-flex; align-items: center; gap: 10px;
  margin-top: clamp(22px, 3vw, 32px);
  padding: 12px 20px;
  font-size: .69rem; font-weight: 500; letter-spacing: var(--track-btn); padding-left: calc(20px + var(--track-btn));
  color: rgba(244, 239, 230, .75);
  border: 1px solid rgba(244, 239, 230, .22);
  transition: color .25s, border-color .25s;
}
.mail-copy:hover, .mail-copy.listo { color: var(--red); border-color: var(--red); }
.mail-close {
  position: absolute; top: -14px; right: -14px; z-index: 2;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  font-family: var(--font-wordmark);
  font-size: 1.5rem; font-weight: 300; line-height: 1;
  color: var(--cream);
  background: var(--ink);
  border: 1px solid rgba(224, 9, 29, .6);
  border-radius: 50%;
  transition: background .25s, color .25s, border-color .25s, rotate .35s cubic-bezier(.16, 1, .3, 1);
}
.mail-close:hover, .mail-close:focus-visible {
  background: var(--red); color: var(--ink); border-color: var(--red);
  rotate: 90deg; outline: none;
}
@media (max-width: 700px) {
  .mail-close { top: -12px; right: -6px; width: 40px; height: 40px; }
}

/* ============ E10 ⑥ — FOOTER EN TELÉFONO: TODO A LA IZQUIERDA ============ */
@media (max-width: 700px) {
  .foot-top { gap: 22px; margin-bottom: 6vh; }
}

/* ============ E10 ③④ — MÓDULOS EN TELÉFONO: PÍLDORA, EXPANSIÓN Y CIERRE ====
   ③ Hasta que se pulsa, el módulo de color grade era una caja negra del tamaño
   del comparador entero — ocupaba casi la mitad de la pantalla para mostrar
   dos palabras. Ahora en reposo es una PÍLDORA del tamaño del texto (INICIAR
   COLORIZACIÓN + rombo, 232×54) y al pulsarla crece hasta el comparador
   completo: anchura y altura transicionan (por eso la altura del escenario va
   en calc() explícito en vez de aspect-ratio, que no se puede animar). Se
   ancla al pie, así que crece hacia arriba, hacia el hueco que el reparto ya
   le reserva.
   ④ Mientras un módulo está abierto (grade "live", instrumento "started") el
   pilar se queda fijo aunque se scrollee (makePillarsUpdate, solo teléfono):
   el usuario lo cerró él o no se cierra. El × es ese cierre. */
/* E11 ③: la píldora se RETIRA del comparador (03) — el cliente lo quiere
   grande, como estaba — y pasa al instrumento (04): "INICIAR MEZCLA" nace
   como píldora de 232×54 y al pulsar crece a la consola completa (ancho y
   alto transicionan; controles y remate se despliegan después). Anclado al
   pie, crece hacia arriba, hacia el hueco que el reparto ya le reserva. */
@media (max-width: 700px) {
  .mono-monitor {
    width: min(88vw, 430px);
    transition: opacity .5s ease, width .55s cubic-bezier(.16, 1, .3, 1);
    --block-h-idle: 54px;
    /* E12 ③: anclado ARRIBA, no al pie. Cerrado, la píldora quedaba sola al
       fondo de la pantalla con un hueco enorme entre el párrafo y ella; ahora
       nace justo debajo del texto (55% del alto de sección: 408 a 741, 365 a
       664) y al abrirse crece hacia ABAJO, que es donde está el sitio libre. */
    top: calc(var(--sec-h, 100vh) * .55); bottom: auto;
  }
  .mono-monitor:not(.started) { width: 232px; }
  .mono-start { gap: 9px; }
  .mono-start-label { font-size: .62rem; letter-spacing: .36em; padding-left: .36em; }
  .mono-monitor:not(.started) .mono-wall { display: none; } /* la píldora no lleva paredes */
  /* E12 ③: los carriles (etiquetas LEAD/DRUMS… y ranuras) asomaban a través de
     la píldora de 54 px; no existen hasta abrir */
  .mono-monitor:not(.started) .mono-instrument { display: none; }
  .mono-controls, .mono-outro {
    max-height: 0; overflow: hidden; margin-top: 0;
    transition: max-height .5s ease .3s, margin-top .5s ease .3s, padding .5s ease .3s, opacity .35s ease .45s;
  }
  .mono-outro { padding-top: 0; padding-bottom: 0; opacity: 0; } /* plegado del todo: ni el relleno ni el velo asoman bajo la píldora */
  .mono-monitor.started .mono-controls { max-height: 60px; margin-top: 10px; }
  .mono-monitor.started .mono-outro { max-height: 140px; margin-top: 12px; padding-top: 8px; padding-bottom: 6px; opacity: 1; overflow: visible; } /* el velo difuminado del pie no se recorta */

  .mod-close {
    position: absolute; top: -13px; right: -8px; z-index: 12;
    width: 38px; height: 38px;
    display: none; place-items: center;
    font-family: var(--font-wordmark);
    font-size: 1.35rem; font-weight: 300; line-height: 1;
    color: var(--cream);
    background: var(--ink);
    border: 1px solid rgba(224, 9, 29, .65);
    border-radius: 50%;
    cursor: pointer;
  }
  .grade-monitor.live .gm-close, .mono-monitor.started .mono-close { display: grid; }
}
@media (min-width: 701px) { .mod-close { display: none; } }

/* ============ E12 ④ — ESCRITORIO, PILAR 04: SIN HUECOS Y CENTRADO ============
   Cerrado, el módulo reservaba sitio para dos cosas que no existían todavía:
   la fila de controles (46 px vacíos bajo la consola) y la columna del fader
   (24 px + hueco a la derecha, que además descentraba la consola respecto del
   remate). El cliente lo marcó: "quitar" el hueco, "centrar" el texto.
   · la fila de controles no ocupa nada hasta iniciar (max-height, como en
     teléfono);
   · el fader no existe hasta iniciar (display), y cuando aparece, el remate
     se corre lo que mide su columna para seguir centrado con la CONSOLA. */
@media (min-width: 701px) {
  .mono-controls { max-height: 0; margin-top: 0; overflow: hidden; transition: max-height .5s ease .3s, margin-top .5s ease .3s, opacity .35s ease .45s; }
  .mono-monitor.started .mono-controls { max-height: 60px; margin-top: 12px; }
  .mono-fader { display: none; }
  .mono-monitor.started .mono-fader { display: flex; }
  .mono-monitor.started .mono-outro { margin-right: calc(24px + clamp(12px, 1.6vw, 22px)); }
}

/* ============ E16 ① — SIN BOTÓN DE PLAY DE iOS EN LOS VIDEOS DE FONDO ====
   WebKit pinta un "play" gigante sobre cualquier <video> inline que esté
   pausado y sin `controls` — y nuestros videos de fondo se pausan a
   propósito (el cristal en el corte, el cubo tras la carta) o porque el Modo
   de Bajo Consumo les niega el autoplay. El glifo es un pseudo-elemento de
   la interfaz nativa: se apaga aquí. Los videos CON controles (el visor del
   reel) conservan su interfaz. */
video:not([controls])::-webkit-media-controls-start-playback-button { display: none !important; -webkit-appearance: none; }
video:not([controls])::-webkit-media-controls { display: none !important; }

/* ============ E17 ④ — FOCO DE TECLADO, UNA SOLA REGLA ============
   Antes solo tres controles tenían :focus-visible; el resto caía en el
   anillo azul del navegador, ajeno a la paleta. Un filete rojo separado del
   elemento (el único acento) para todo lo que se pueda enfocar. Los
   controles con su propio foco (portada, cierre del reel, del correo) siguen
   mandando por especificidad. */
:where(a, button, [tabindex]):focus-visible { outline: 1px solid var(--red); outline-offset: 4px; }
