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

:root {
  --bg: #0e0f13;
  --fg: #f2f2f4;
  --muted: #8a8c96;
  /* Tamaño único para TODOS los botones de arriba (Valentina, audio, día/noche, projects) —
     un intermedio entre el grande que tenía Valentina y el chico que tenían los demás. */
  --nav-btn-size: 58px;
}

html, body { width: 100%; height: 100%; overflow: hidden; background: var(--bg); }

body {
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
  color: var(--fg);
  -webkit-font-smoothing: antialiased;
}

#app { position: fixed; inset: 0; }

/* touch-action:none — sin esto, dos taps seguidos (nuestra selección + apertura táctil, ver
   main.js) pueden interpretarse como double-tap-to-zoom del navegador en vez de dos taps
   normales, además de sumar el delay de ~300ms que algunos navegadores le meten al click. */
#scene { display: block; width: 100%; height: 100%; touch-action: none; }

/* "Valentina": mismo botón de vidrio que Projects, arriba a la izquierda, pero grande
   (el tamaño que tenía como título antes de ser botón). Selector por id: tiene que ganarle
   a ".side-btn--pill" sin importar el orden en que aparezcan las reglas en el archivo. */
#btn-valentina.intro-btn {
  position: fixed;
  top: 30px;
  left: 36px;
  z-index: 8;
  height: var(--nav-btn-size);
  padding: 0 30px;
  font-size: 22px;
  /* "bloom" simulado en CSS (no pasa por el pipeline de postprocessing 3D, es DOM): un halo
     alrededor del vidrio + un resplandor sobre el texto, para que el nombre destaque. */
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.28);
  box-shadow: 0 0 20px 2px rgba(255, 255, 255, 0.25), 0 0 55px 10px rgba(255, 255, 255, 0.15);
  text-shadow: 0 0 14px rgba(255, 255, 255, 0.85), 0 0 34px rgba(255, 255, 255, 0.5);
}

/* Etiqueta que sigue al objeto en hover */
.label {
  position: absolute;
  z-index: 6;
  /* padding en "em": encoge junto con el font-size que ajusta main.js según el tamaño
     en pantalla de cada esfera, para que siga viéndose proporcionado ya achicado */
  padding: 0.54em 1.08em;
  border-radius: 999px;
  /* Fondo oscuro (no blanco) a propósito: la etiqueta flota justo encima del material de la
     esfera, que en algunos proyectos (ej. Superlocal) es muy claro/saturado — con un fondo
     blanco traslúcido ese brillo se filtraba a través del blur y el texto (también claro)
     quedaba casi ilegible. Oscureciendo el fondo el contraste queda garantizado sin importar
     qué haya detrás. */
  background: rgba(8, 9, 13, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-size: 13px;
  font-weight: 300;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.9);
  transition: opacity 0.18s ease, transform 0.18s ease, font-size 0.1s ease;
}
.label.visible {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* Botones de navegación, en fila arriba a la derecha */
.side-nav {
  position: fixed;
  top: 30px;
  right: 36px;
  z-index: 8;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 14px;
}
.side-btn {
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Un poco más opaco que un "vidrio" puro a propósito: en versiones viejas de iOS Safari
     (ej. 16.2) backdrop-filter puede no desenfocar bien lo que hay atrás (sobre todo el
     canvas WebGL de la escena), y con solo 8% de opacidad blanca el texto/ícono se volvía
     ilegible ahí — este piso de opacidad mantiene el contraste con o sin blur real. */
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--fg);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}
.side-btn:hover { background: rgba(255, 255, 255, 0.22); transform: scale(1.05); }
.side-btn.active,
.side-btn[aria-expanded="true"] {
  background: rgba(255, 255, 255, 0.2);
  border-color: rgba(255, 255, 255, 0.3);
  color: #fff;
}

/* Audio: círculo con ícono */
.side-btn--circle {
  width: var(--nav-btn-size);
  height: var(--nav-btn-size);
  border-radius: 50%;
}
.side-btn--circle svg { width: 24px; height: 24px; }

/* Día/noche: sol por defecto, luna cuando está activo — con una pequeña rotación al cambiar */
#btn-daynight .icon-moon { display: none; }
#btn-daynight.active .icon-sun { display: none; }
#btn-daynight.active .icon-moon { display: block; }
#btn-daynight svg {
  transition: transform 0.4s ease, opacity 0.3s ease;
}

/* Onda de audio: quieta por defecto, se mueve puntito por puntito (con desfasaje) solo
   mientras el audio está activo — simula una onda viajando en vez de un simple parpadeo */
.side-btn--circle svg circle {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.25s ease;
}
#btn-audio.active svg circle { animation: audio-wave-bob 0.9s ease-in-out infinite; }
#btn-audio.active svg circle:nth-child(1) { animation-delay: 0s; }
#btn-audio.active svg circle:nth-child(2) { animation-delay: 0.09s; }
#btn-audio.active svg circle:nth-child(3) { animation-delay: 0.18s; }
#btn-audio.active svg circle:nth-child(4) { animation-delay: 0.27s; }
#btn-audio.active svg circle:nth-child(5) { animation-delay: 0.36s; }
#btn-audio.active svg circle:nth-child(6) { animation-delay: 0.45s; }
#btn-audio.active svg circle:nth-child(7) { animation-delay: 0.54s; }
#btn-audio.active svg circle:nth-child(8) { animation-delay: 0.63s; }
#btn-audio.active svg circle:nth-child(9) { animation-delay: 0.72s; }
@keyframes audio-wave-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}

/* About: óvalo alargado con texto, misma tipografía que "Valentina" */
.side-btn--pill {
  height: var(--nav-btn-size);
  padding: 0 22px;
  border-radius: 999px;
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-size: 14px;
  font-weight: 300;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Projects: el botón y su desplegable son UN solo rectángulo redondeado — el vidrio
   (fondo/borde/blur) vive acá, en el contenedor, para que no haya costura entre las dos
   partes. El radio es la mitad de --nav-btn-size, así da una píldora perfecta cuando
   está cerrado (dropdown en max-height:0) y un rectángulo redondeado normal al abrirse. */
.nav-item {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: calc(var(--nav-btn-size) / 2);
  background: rgba(255, 255, 255, 0.16); /* ver comentario de opacidad en .side-btn */
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  overflow: hidden;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.nav-item.open {
  background: rgba(255, 255, 255, 0.2);
  border-color: rgba(255, 255, 255, 0.3);
}

/* El botón en sí queda transparente: el vidrio ya lo pinta .nav-item por detrás */
.side-btn--label {
  /* -2px: .nav-item ya suma su propio borde de 1px arriba y abajo alrededor de este botón */
  height: calc(var(--nav-btn-size) - 2px);
  padding: 0 20px 0 22px;
  gap: 10px;
  background: transparent;
  border: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-size: 14px;
  font-weight: 300;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  white-space: nowrap;
  flex-shrink: 0;
}
.side-btn--label:hover { background: rgba(255, 255, 255, 0.06); transform: none; }
/* Tres puntitos verticales: da la pista de que es un menú desplegable */
.menu-dots { width: 4px; height: 16px; flex-shrink: 0; }
#btn-projects.side-btn--label[aria-expanded="true"] {
  background: transparent;
  border-color: transparent;
  color: var(--fg);
}

/* Lista de proyectos: crece hacia abajo dentro del mismo rectángulo (sin fondo/borde
   propios, sin separación del botón) */
.dropdown {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 0;
  padding: 0 8px;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.28s ease, opacity 0.2s ease, padding 0.28s ease;
}
.dropdown.open {
  /* Bastante más que el contenido actual (8 items ≈ 286px) a propósito: crece con
     max-height fijo para la transición animada, así que hay que dejar margen para nuevos
     proyectos sin tener que volver a ajustar este número cada vez. */
  max-height: 400px;
  padding: 0 8px 8px;
  opacity: 1;
}
.dropdown a {
  display: block;
  padding: 9px 12px;
  border-radius: 12px;
  color: var(--fg);
  text-decoration: none;
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 13px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  text-align: center;
  transition: background 0.15s ease;
}
.dropdown a:hover { background: rgba(255, 255, 255, 0.1); }

/* Panel "About": modal centrado */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: 9;
  background: rgba(6, 7, 9, 0.6);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0.25s;
}
.backdrop.open { opacity: 1; visibility: visible; }

/* "Cascarón" fijo: define tamaño/vidrio/posición, pero NO scrollea — así el botón de cerrar
   y el riel de la scrollbar (hijos directos de .panel) quedan siempre visibles, sin importar
   cuánto scrolleaste el contenido de adentro. Lo que sí scrollea es .panel-scroll. */
.panel {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 10;
  width: min(720px, 92vw);
  min-height: 320px;
  /* dvh (no solo vh) — en mobile Safari "vh" se calcula contra el viewport SIN la barra de
     direcciones (más alto que lo que realmente se ve), así que un panel centrado con
     max-height:76vh podía terminar más alto que el área visible real y quedar cortado arriba
     (sin espacio antes del título). dvh sigue el viewport visible de verdad; vh queda de
     fallback para navegadores que no lo soporten. */
  max-height: 76vh;
  max-height: 76dvh;
  display: flex;
  flex-direction: column;
  border-radius: 20px;
  /* mismo vidrio que los botones de navegación (ver comentario de opacidad en .side-btn) */
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.45);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -50%) scale(0.96);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
}
.panel.open {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(1);
}

/* Contenido scrolleable de adentro — acá vivían antes el padding/gap/overflow que tenía
   .panel directamente. flex:1 1 auto + min-height:0 es lo que le permite scrollear en vez de
   estirar el panel entero (el min-height:0 es el truco clásico de flexbox para esto). */
.panel-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  text-align: center;
  padding: 44px 40px;
  /* la scrollbar nativa del navegador queda oculta del todo — se reemplaza por
     .panel-scrollbar (ver más abajo), una barra 100% propia con control total sobre la
     forma (sin flechas, con las puntas que queramos, separada de las esquinas). */
  scrollbar-width: none;
}
.panel-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* Scrollbar propia: una tira vertical pegada al borde derecho del panel (por fuera del
   padding del contenido), con el "riel" separado de las esquinas redondeadas y la barrita
   con las puntas redondeadas. JS (nav.js, attachCustomScrollbar) calcula alto/posición y la
   arrastra. Se oculta sola si el contenido entra sin necesidad de scroll. */
.panel-scrollbar {
  position: absolute;
  top: 22px;
  bottom: 22px;
  right: 6px;
  width: 6px;
  border-radius: 999px;
  pointer-events: none;
}
.panel-scrollbar-thumb {
  position: absolute;
  left: 0;
  width: 100%;
  /* Hilo de puntitos (mismo lenguaje que la onda de audio y la "X" de cerrar) en vez de una
     barra sólida: un gradiente radial repetido en vertical — se adapta solo a cualquier alto
     del thumb, sin tocar el JS. Sólido/opaco (no semitransparente) a propósito: con el fondo
     animado detrás (esferas con glow moviéndose), una barra muy translúcida deja que ESE
     brillo se note más que el movimiento real de la barra. */
  background-image: radial-gradient(circle, rgba(230, 232, 238, 0.9) 2.2px, transparent 2.3px);
  background-size: 100% 10px;
  background-repeat: repeat-y;
  background-position: top center;
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.35);
  pointer-events: auto;
  cursor: pointer;
  transition: background-image 0.15s ease;
}
.panel-scrollbar-thumb:hover,
.panel-scrollbar-thumb.dragging {
  background-image: radial-gradient(circle, rgba(255, 255, 255, 1) 2.2px, transparent 2.3px);
}

/* "Bloom" del panel About: es DOM, no pasa por el pipeline de postprocessing 3D — mismo
   truco que el de "Valentina" (halo con box-shadow + resplandor en el texto). Selector por
   id para que no se filtre a los paneles de proyecto, que comparten la clase .panel. */
#about-panel {
  /* Más grande que el resto de los paneles chicos (los de proyecto ya son anchos aparte) —
     con la bio + software + contacto necesita más aire. */
  width: min(920px, 94vw);
  max-height: 94vh; /* fallback — ver comentario de dvh en .panel */
  max-height: 94dvh;
  box-shadow:
    0 30px 80px rgba(0, 0, 0, 0.45),
    0 0 30px 4px rgba(255, 255, 255, 0.18),
    0 0 80px 16px rgba(255, 255, 255, 0.12);
}
#about-panel h2 {
  text-shadow: 0 0 14px rgba(255, 255, 255, 0.75), 0 0 34px rgba(255, 255, 255, 0.4);
}

.panel h2 {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 26px;
  text-transform: uppercase;
  margin-bottom: 22px;
}
.panel p {
  color: var(--fg);
  font-family: "Lexend Deca", "Inter", sans-serif;
  font-weight: 300;
  font-size: 16px;
  line-height: 1.6;
  text-align: justify;
  text-align-last: center;
  width: 100%;
}

/* "Software I use" — globitos de vidrio, mismo lenguaje visual que el resto del sitio */
.software-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: 44px;
}
.software-heading {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.software-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  list-style: none;
}
.software-list li {
  /* Solo el borde de vidrio, sin relleno — a propósito distinto de los botones de contacto
     de abajo: estos son etiquetas informativas, no algo clickeable. */
  padding: 8px 20px;
  border-radius: 999px;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.18);
  font-family: "Lexend Deca", "Inter", sans-serif;
  font-weight: 300;
  font-size: 14px;
  color: var(--fg);
}

/* Contacto: mail/LinkedIn/teléfono, al fondo del panel About — íconos de puntitos (mismo
   lenguaje visual que el resto del sitio) en vez de texto. Círculo compacto en reposo; al
   mostrar "Copied" el texto lo estira horizontalmente (min-width, no width fija). */
.contact-section {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 100px;
}
.contact-item {
  appearance: none;
  -webkit-appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 48px;
  height: 48px;
  padding: 0 16px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16); /* ver comentario de opacidad en .side-btn */
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--fg);
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}
.contact-item svg { width: 22px; height: 22px; flex-shrink: 0; }
.contact-item:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.28);
  transform: scale(1.05);
}
.contact-item.copied {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.35);
}
.panel-close {
  appearance: none;
  -webkit-appearance: none;
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.06);
  color: var(--fg);
  cursor: pointer;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.panel-close svg { width: 14px; height: 14px; }
.panel-close:hover { background: rgba(255, 255, 255, 0.14); }

/* ------------------------------------------------------------------ *
 *  PANELES DE PROYECTO
 *  Se abren superpuestos a la escena 3D (pasto + esferas), igual que
 *  "About" — mismo backdrop, más anchos porque tienen más contenido
 *  (video, texto, ficha técnica, galería).
 * ------------------------------------------------------------------ */
.project-panel {
  width: min(1360px, 96vw);
  max-height: 88vh;
}
.project-panel .panel-scroll {
  align-items: stretch;
  justify-content: flex-start;
  text-align: left;
  gap: 28px;
}
/* El wrapper interno es un flex column con alto limitado (scrollea si no entra todo) — por
   default flexbox ACHICA los hijos para que "entren" antes de dejarlos desbordar, lo cual
   rompía el aspect-ratio del video (se veía comprimido a una tira finita). flex-shrink:0 en
   las secciones de contenido real evita eso: si no entran, scrollea, pero no se deforman. */
.project-panel .panel-scroll > * { flex-shrink: 0; }
.project-panel .project-desc { text-align: left; text-align-last: left; width: auto; }
/* Necesita esta especificidad para ganarle a ".panel h2" (que le pone margin-bottom:22px,
   pensado para el panel About) — acá el espaciado lo da SOLO el gap de .panel-scroll. */
.project-panel .project-title { margin-bottom: 0; }

/* Fila de reels de Instagram embebidos — estirada al mismo ancho que el resto del
   contenido (título/descripción/ficha técnica), no centrada con aire a los costados. Con 1 o
   2 reels (ej. Superlocal, R&D) no hay de qué "estirar" — se centran con un gap más chico en
   vez de quedar cada uno pegado a un extremo. */
.project-reels {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
}
.project-reels:not(:has(.reel-embed:nth-child(3))) {
  justify-content: center;
  gap: 24px;
}
/* 3 videos de YouTube/Vimeo en la misma fila (ej. Lab): .vimeo-embed viene pensado para 1-2
   videos grandes (449px de alto) — con 3 en fila eso no entra en el ancho del panel y se
   apilan uno debajo del otro. Achicarlos solo cuando hay un tercero en la misma fila — a
   232px de alto, los tres (16:9) más los gaps ocupan practicamente el ancho completo del
   panel, llegando a los mismos bordes que el texto de arriba/abajo. La fila también matchea
   la regla de arriba (":not(:has(.reel-embed:nth-child(3)))", pensada para reels de Instagram)
   porque no tiene ningún .reel-embed — hay que pisar su gap/justify-content acá para que los
   tres videos entren en una sola fila en vez de que el tercero salte a una línea nueva. */
.project-reels:has(.vimeo-embed:nth-child(3)) {
  gap: 12px;
  justify-content: space-between;
}
.project-reels:has(.vimeo-embed:nth-child(3)) .vimeo-embed {
  height: 232px;
}
.reel-embed {
  /* Instagram fuerza su propio ancho mínimo (~326px) vía inline style en el iframe, sin
     importar el ancho del contenedor — para agrandar el recorte sin perder la ventana
     lograda (mismo recorte, solo más grande), se escala el iframe entero un factor "--zoom"
     desde su esquina superior izquierda (transform-origin: 0 0) y el top/left se escalan
     igual (--crop-top/--crop-left), así el crop queda idéntico, solo más grande. Si se
     quiere agrandar más, subir --zoom (y ajustar --crop-top/--crop-left en la misma
     proporción) en vez de tocar el iframe de abajo directamente.
     "Recorte" experimental: tapa el header/pie/bordes blancos de Instagram corriendo el
     iframe con top/left negativos — es a ojo sobre el layout actual de Instagram; si ellos
     lo cambian, hay que recalibrar (o sacar el recorte y volver al embed completo). */
  --zoom: 1.15;
  --crop-top: -75px;
  --crop-left: -5px;
  flex: 0 0 auto;
  width: 366px;
  height: 449px;
  overflow: hidden;
  border-radius: 12px;
  position: relative;
}
/* Un solo reel: 30% más grande que el tamaño de fila (misma ventana de recorte, solo más
   grande — --zoom/--crop-top/--crop-left escalan por el mismo factor que width/height). */
.reel-embed:only-child {
  --zoom: 1.495;
  --crop-top: -97.5px;
  --crop-left: -6.5px;
  width: 476px;
  height: 584px;
  max-width: 100%;
}
.reel-embed iframe {
  position: absolute;
  top: var(--crop-top);
  left: var(--crop-left);
  transform: scale(var(--zoom));
  transform-origin: 0 0;
  border-radius: 12px;
}
/* En mobile el ancho de fila (366px, o 476px para :only-child) no entra en el contenido del
   panel (~92vw - 80px de padding, bastante menos que eso) — sin achicar, el reel se salía del
   panel. Mismo truco de siempre: width/height/--zoom/--crop-* escalan todos por el mismo
   factor (acá 250/366 ≈ 0.683), así el recorte queda idéntico, solo más chico. Un solo tamaño
   para ambos casos (no solo :only-child) — en mobile no hay margen para diferenciarlos como en
   desktop. max-width:100% queda de red de seguridad en pantallas todavía más angostas. */
@media (max-width: 700px) {
  .reel-embed,
  .reel-embed:only-child {
    --zoom: 0.786;
    --crop-top: -51px;
    --crop-left: -3.4px;
    width: 250px;
    height: 307px;
    max-width: 100%;
  }
}

/* Filas de medios alternando reels (verticales) con videos de Vimeo (16:9) — cada fila es
   su propio ".project-reels" (mismo centrado/gap que ya tenían las filas de reels). */
.project-media-rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.vimeo-embed {
  position: relative;
  flex: 0 0 auto;
  height: 449px;
  aspect-ratio: 16 / 9;
  max-width: 100%;
  overflow: hidden;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.05);
}
.vimeo-embed iframe {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
}

/* Video: helper para el "recorte" que tapa el watermark residual de YouTube incluso con
   controls=0 — el iframe se agranda y se centra dentro de un contenedor con overflow
   oculto. Con Vimeo (controls=0) alcanza con el iframe normal, sin esta clase. */
.project-hero {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 56vh; /* que se note que hay más contenido debajo sin tener que scrollear */
  border-radius: 20px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
}
.project-hero video { object-fit: contain; } /* que se vea el video completo, sin recortar contenido */
.project-hero iframe,
.project-hero video {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
}
/* .crop-yt genérico (no solo .project-hero): tapa el watermark de YouTube agrandando y
   centrando el iframe — se usa también en .vimeo-embed (filas de video en miniatura, ver
   youtubeEmbedHtml en nav.js). */
.crop-yt iframe {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 130%;
  height: 130%;
  transform: translate(-50%, -50%);
}
.project-hero.placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  text-align: center;
  padding: 24px;
}

/* Varias piezas grandes con nombre propio dentro de un mismo proyecto (ej. VFX: tres cortos
   distintos con su propio título, apilados, compartiendo la descripción general de abajo). */
.project-pieces {
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.project-piece {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.piece-title {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 15px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg);
}

.project-title {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 40px;
  text-transform: uppercase;
  letter-spacing: -0.01em;
}

/* Título + botón "Visit site" debajo (proyectos con externalUrl, ej. Flor/Phos) — solo existe
   este wrapper cuando hay CTA (ver ensureProjectPanel en nav.js), así que no afecta el título
   de los proyectos sin uno. */
.project-title-block {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}
.project-title-block .project-title { margin-bottom: 0; }

.project-desc {
  font-family: "Lexend Deca", "Inter", sans-serif;
  font-weight: 300;
  font-size: 16px;
  line-height: 1.75;
  color: var(--fg);
  max-width: 68ch;
  white-space: pre-line; /* respeta los \n\n entre párrafos de la descripción */
}

/* Antes era una grilla de columnas de ancho IGUAL — con un valor corto (ej. "2026") eso deja
   un montón de aire vacío antes de la columna siguiente, mientras que valores largos quedan
   pegados al límite. Con flex, cada caja (título + texto) ocupa solo lo que su contenido
   necesita, y el espacio entre cajas queda parejo de verdad (gap real + lo que sobra se
   reparte con space-between) en vez de parecerlo solo en las columnas. */
.project-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 24px;
  padding: 28px 32px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.project-meta > div { max-width: 280px; }
@media (max-width: 700px) {
  .project-meta { flex-direction: column; align-items: stretch; }
  .project-meta > div { max-width: none; }
}
.project-meta dt {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.project-meta dd {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 15px;
}

/* Secciones largas de "detrás de escena" (ej. "Main challenges", "Technical process",
   "Result"), debajo de la ficha técnica — cada una es su propio hijo directo de
   .panel-scroll, así el gap:28px de siempre las separa igual que al resto de las secciones. */
.process-section {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.process-heading {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 22px;
  letter-spacing: -0.005em;
}

/* Respiro extra entre el video de un proyecto y el título del siguiente (ej. Lab, donde se
   encadenan varios proyectos sueltos uno debajo del otro) — arriba del gap:28px de siempre
   entre hijos de .panel-scroll, para que se note más el corte entre uno y otro. */
.project-hero + .process-section,
.project-hero + .project-title,
.project-hero + .project-title-block,
.project-media-rows + .process-section,
.project-media-rows + .project-title {
  margin-top: 24px;
}

/* Más chico que el resto de las píldoras (58px, pensado para la barra de nav) — acá es un
   link secundario debajo del título, no hace falta que compita en tamaño con él. */
.project-cta {
  text-decoration: none;
  align-self: flex-start;
  height: 40px;
  padding: 0 18px;
  font-size: 12px;
  gap: 8px;
}
.project-cta .cta-arrow { width: 13px; height: 13px; flex-shrink: 0; }

/* Loader */
.loader {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: #4d56b0;
  transition: opacity 0.5s ease;
}
.loader.hidden { opacity: 0; pointer-events: none; }
.loader-title {
  font-family: "Lexend Peta", "Inter", sans-serif;
  font-weight: 300;
  font-size: 22px;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--fg);
}
/* Línea de puntitos (mismo lenguaje visual que el resto del sitio) en vez de una barra
   sólida — un gradiente radial repetido de fondo (tenue, todos los puntitos) más una copia
   idéntica adentro de "span" (brillante), que se revela de izquierda a derecha con su ancho
   — así se ven los puntitos "prendiéndose" a medida que carga, en vez de una barra rellenando. */
.loader-bar {
  width: 200px;
  height: 10px;
  background-image: radial-gradient(circle, rgba(255, 255, 255, 0.18) 2px, transparent 2.2px);
  background-size: 14px 10px;
  background-repeat: repeat-x;
  overflow: hidden;
}
.loader-bar span {
  display: block;
  height: 100%;
  width: 0%;
  background-image: radial-gradient(circle, rgba(255, 255, 255, 1) 2px, transparent 2.2px);
  background-size: 14px 10px;
  background-repeat: repeat-x;
  transition: width 0.25s ease;
}

/* Errores visibles en pantalla (para no depender de F12) */
#debug {
  display: none;
  max-width: 640px;
  max-height: 40vh;
  overflow: auto;
  margin-top: 8px;
  padding: 10px 14px;
  border-radius: 8px;
  background: rgba(255, 70, 70, 0.1);
  border: 1px solid rgba(255, 70, 70, 0.35);
  color: #ff8a8a;
  font-family: "Consolas", monospace;
  font-size: 12px;
  white-space: pre-wrap;
  text-align: left;
}
#debug.visible { display: block; }

/* Mobile: los botones de arriba (Valentina + fila de audio/día-noche/projects) al 65% de
   tamaño. Un solo scale() en el contenedor en vez de tocar cada font-size/svg por separado —
   escala todo (texto, íconos, padding) proporcional de una — anclado a la esquina donde ya
   estaban pegados, para que no se corran de lugar al achicarse. Mismo breakpoint (700px) que
   ya se usa para servir los videos "mobile" más livianos. */
@media (max-width: 700px) {
  /* La mitad de separación con el borde que en desktop (36px → 18px): Valentina pegada al
     límite izquierdo, Projects al derecho. */
  #btn-valentina.intro-btn {
    left: 18px;
    transform: scale(0.65);
    transform-origin: top left;
  }
  .side-nav { right: 18px; }
  .nav-item {
    transform: scale(0.65);
    transform-origin: top right;
  }
  /* Audio y Día/Noche: columna vertical pegada al lateral izquierdo, debajo de Valentina, al
     mismo 65% (mismo alto final que Valentina/Projects) en vez de ir apretados arriba a la
     derecha con Projects (ahí quedaban superpuestos/intercalados entre sí). Siguen siendo
     hijos de .side-nav en el HTML (mismo botón, mismo listener de nav.js) pero .side-nav ya
     no tiene su propio transform, así que este position:fixed + scale propio queda relativo
     al viewport de verdad, no al rectángulo de .side-nav. top/left fijos (no translate%)
     porque transform-origin:top left ancla la esquina escalada exactamente ahí, igual que
     Valentina/Projects arriba. */
  #btn-audio,
  #btn-daynight {
    position: fixed;
    left: 18px;
    transform-origin: top left;
    transform: scale(0.65);
    z-index: 8;
  }
  #btn-audio { top: 82px; }
  #btn-daynight { top: 134px; }
}
