/* ==========================================================================
   IAN IACONO — SOUND PORTFOLIO
   Hoja de estilos unica para todo el sitio.

   COMO ESTA ORGANIZADO ESTE ARCHIVO
   01. Sistema de diseno (los "tokens": colores, tamanos, espacios)
   02. Reset y base
   03. Tipografia
   04. Layout y utilidades
   05. Navegacion + control global de sonido
   06. Paginas (el router muestra una a la vez)
   07. Hero / reel
   08. Tarjetas de proyecto (tiles): la grilla de la home
   09. Selector Sound / Audiovisual
   10. Pagina de proyecto
   10b. Formulario de contacto
   11. Botones y links
   12. Reproductor de video (controles compartidos)
   13. Animaciones de entrada
   14. Responsive
   15. Pie de pagina
   ========================================================================== */


/* ==========================================================================
   01. SISTEMA DE DISENO
   Si queres cambiar los colores del sitio, cambialos SOLO aca.
   Todo lo demas los usa automaticamente.
   ========================================================================== */

:root {
  /* --- Colores ------------------------------------------------------------
     El sitio es oscuro siempre, a proposito: la marca es oscura. El negro
     tiene un tinte violeta muy sutil, y el violeta es el unico acento
     (textos de marca, foco, roles, y el control de volumen cuando esta
     activo). El resto del color lo ponen las propias portadas y videos.    */
  --c-bg:            #0a0910;   /* fondo general, casi negro con tinte violeta */
  --c-bg-sunken:     #060509;   /* fondo mas hundido                         */
  --c-bg-raised:     #131019;   /* superficies elevadas (tarjetas)           */
  --c-bg-hover:      #1a1524;   /* superficie con el mouse encima            */

  --c-text:          #f4f3f7;   /* texto principal   — contraste 17.9:1      */
  --c-text-muted:    #a9a6b4;   /* texto secundario  — contraste  8.6:1      */
  --c-text-faint:    #7d7a8a;   /* texto terciario   — contraste  4.9:1      */

  --c-accent:        #8b7cf6;   /* violeta: el acento principal              */
  --c-accent-soft:   #b3a8fa;   /* violeta claro, para hover                 */
  --c-accent-deep:   #531e9e;   /* violeta profundo: sliders (linea de tiempo del reel, volumen) */
  --c-bloom-violet:  #1f1433;   /* violeta muy oscuro y sutil: solo el degrade de Contact/footer */

  --c-line:          rgba(255,255,255,.10);  /* lineas finas                 */
  --c-line-strong:   rgba(255,255,255,.24);  /* lineas con enfasis           */

  /* --- Tipografia --------------------------------------------------------- */
  --font: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --font-tabs: "Alexandria", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;   /* solo para el selector Sound/Audiovisual */
  --font-name: "Unbounded", "Arial Black", sans-serif;   /* solo para el nombre del header */

  /* Escala tipografica fluida: crece sola entre celular y desktop */
  --fs-2xs:  0.6875rem;                          /* 11px — etiquetas         */
  --fs-xs:   0.8125rem;                          /* 13px                     */
  --fs-sm:   0.9375rem;                          /* 15px                     */
  --fs-base: 1.0625rem;                          /* 17px — cuerpo de texto   */
  --fs-lg:   clamp(1.125rem, 0.4vw + 1.05rem, 1.3125rem);
  --fs-xl:   clamp(1.375rem, 1vw + 1.15rem, 1.75rem);
  --fs-2xl:  clamp(1.75rem, 2vw + 1.25rem, 2.5rem);
  --fs-3xl:  clamp(2.25rem, 4.5vw + 1rem, 4rem);
  --fs-4xl:  clamp(2.75rem, 8vw + 0.5rem, 6.5rem);   /* titulo gigante       */

  --lh-tight:  1.05;
  --lh-snug:   1.25;
  --lh-normal: 1.65;   /* cuerpo de texto: comodo para leer                  */

  --ls-tight:  -0.03em;
  --ls-normal: -0.01em;
  --ls-wide:    0.14em;   /* para etiquetas en MAYUSCULA                     */

  /* Medida de linea: entre 60 y 75 caracteres, como pide la buena tipografia */
  --measure: 68ch;

  /* --- Escala de espaciado (base 4px) ------------------------------------- */
  --sp-1:  0.25rem;   /*   4px */
  --sp-2:  0.5rem;    /*   8px */
  --sp-3:  0.75rem;   /*  12px */
  --sp-4:  1rem;      /*  16px */
  --sp-5:  1.5rem;    /*  24px */
  --sp-6:  2rem;      /*  32px */
  --sp-7:  3rem;      /*  48px */
  --sp-8:  4rem;      /*  64px */
  --sp-9:  6rem;      /*  96px */
  --sp-10: 8rem;      /* 128px */
  --sp-11: 12rem;     /* 192px */

  /* --- Radios de esquina --------------------------------------------------- */
  --r-xs:   2px;
  --r-sm:   4px;
  --r-md:   8px;
  --r-lg:  14px;
  --r-full: 999px;

  /* --- Sombras ------------------------------------------------------------- */
  --sh-1: 0 1px 2px rgba(0,0,0,.5);
  --sh-2: 0 10px 34px rgba(0,0,0,.55);
  --sh-3: 0 40px 90px -24px rgba(0,0,0,.85);

  /* --- Movimiento ---------------------------------------------------------- */
  --ease:      cubic-bezier(.2,.65,.25,1);
  --dur-fast:  150ms;
  --dur:       200ms;
  --dur-slow:  250ms;

  /* --- Layout -------------------------------------------------------------- */
  --maxw:    94rem;   /* 1504px — ancho maximo del contenido */
  --maxw-sm: 54rem;
  --gutter:  clamp(1.25rem, 4vw, 3.5rem);
  --nav-h:   4.5rem;
  /* Arriba de todo del home la barra es mas alta (nombre + subtitulo +
     links), no --nav-h -- el reel deja estos otros margenes libres para
     no quedar pegado a ese texto. Arriba hace falta mucho mas que abajo
     (ahi abajo no hay nada de que despegarse), asi el reel puede ser mas
     grande Y separarse mas del nombre a la vez -- antes, con el mismo
     margen arriba y abajo, agrandar el reel comia el espacio libre de
     los dos lados por igual. */
  /* Ancho real del reel. Vive aca y no suelto adentro de .hero__frame
     porque la barra de arriba se alinea con sus bordes (ver ".nav" en
     "3. BARRA DE ARRIBA"): si el numero estuviera escrito dos veces,
     tarde o temprano uno de los dos cambia y la barra deja de coincidir.
     Depende del ALTO de la ventana, no solo del ancho: el reel es 16/9 y
     lo primero que se le acaba es el alto. */
  --hero-top-clearance:    8.75rem;
  --hero-bottom-clearance:  0.75rem;
  --reel-w: min(94vw, calc((100svh - var(--hero-top-clearance) - var(--hero-bottom-clearance)) * 16 / 9));

  /* Area minima clickeable (accesibilidad) */
  --tap: 44px;

  color-scheme: dark;
}


/* ==========================================================================
   02. RESET Y BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + var(--sp-4));
  /* Reserva el lugar de la barra de scroll siempre, haya o no: si no,
     el header (position:fixed, centrado) se corre unos pixeles cada vez
     que se pasa de una pagina larga (con scroll) a una corta (sin scroll),
     por ejemplo de la home a un proyecto. */
  scrollbar-gutter: stable;
}

body {
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: var(--lh-normal);
  letter-spacing: var(--ls-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100dvh;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
}

img, picture, video, iframe, svg {
  display: block;
  max-width: 100%;
}

img { height: auto; }

button, input, select, textarea { font: inherit; color: inherit; }

ul, ol { list-style: none; }

/* El atributo hidden tiene que ganar siempre, aunque el elemento tenga una
   clase que le ponga su propio "display" (como .grid, que usa display:grid).
   Sin esta regla, esa clase le gana al hidden del navegador y el bloque
   sigue viendose. */
[hidden] { display: none !important; }

/* Foco visible y consistente en TODO lo que se puede tocar con el teclado */
:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--c-accent); color: #0a0910; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* ==========================================================================
   03. TIPOGRAFIA
   ========================================================================== */

@font-face {
  font-family: "Space Grotesk";
  src: url("../assets/fonts/spacegrotesk-latin.woff2") format("woff2");
  font-weight: 300 700;          /* una sola fuente cubre todos los pesos */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: "Space Grotesk";
  src: url("../assets/fonts/spacegrotesk-latin-ext.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                 U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Alexandria: solo se usa en el selector Sound/Audiovisual (.portfolio-tab) */
@font-face {
  font-family: "Alexandria";
  src: url("../assets/fonts/alexandria-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: "Alexandria";
  src: url("../assets/fonts/alexandria-latin-ext.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                 U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Unbounded Black: solo para el nombre grande del header (.nav__brand-name)
   y el pie de pagina -- una tipografia distinta a proposito, para que
   funcione como firma/logotipo en vez de mezclarse con el resto. Eleccion
   provisoria, todavia en revision. */
@font-face {
  font-family: "Unbounded";
  src: url("../assets/fonts/unbounded-black-latin.woff2") format("woff2");
  font-weight: 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
                 U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
                 U+FFFD;
}
@font-face {
  font-family: "Unbounded";
  src: url("../assets/fonts/unbounded-black-latin-ext.woff2") format("woff2");
  font-weight: 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
                 U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
                 U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
                 U+2113, U+2C60-2C7F, U+A720-A7FF;
}

h1, h2, h3, h4 {
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}

p { text-wrap: pretty; }

/* Etiqueta chiquita en mayuscula: "ESP", "SOUND DESIGN", etc. */
.label {
  font-size: var(--fs-2xs);
  font-weight: 500;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-text-faint);
  line-height: 1;
}

/* Parrafos de lectura: medida controlada entre 60 y 75 caracteres */
.prose {
  max-width: var(--measure);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--c-text-muted);
}


/* ==========================================================================
   04. LAYOUT Y UTILIDADES
   ========================================================================== */

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Solo la usa Contact (ver index.html) -- valor propio, no var(--maxw-sm),
   para agrandarla sin afectar al pie de pagina (.site-footer__inner), que
   si usa esa variable compartida directamente. */
.wrap--narrow { max-width: 68rem; }

/* Linea divisoria fina */
.rule {
  height: 1px;
  background: var(--c-line);
  border: 0;
}

.stack > * + * { margin-top: var(--sp-4); }


/* ==========================================================================
   05. NAVEGACION + CONTROL GLOBAL DE SONIDO
   Barra fija arriba: el nombre a la izquierda, el volumen y los links a la
   derecha.
   ========================================================================== */

/* La barra tiene 2 estados. Arriba de todo del home: transparente, con el
   nombre y el subtitulo grandes y centrados (marca), sin links ni volumen
   -- es una bienvenida, no todavia una barra de navegacion. Al scrollear
   (o en cualquier otra pagina, donde no hay "arriba del todo" que valga):
   se encoge, se oscurece, y muestra los links + el volumen -- ese cambio
   lo maneja .is-stuck (ver "3. BARRA DE ARRIBA" en js/main.js). */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  min-height: var(--nav-h);
  /* Un toque mas que --sp-7 (3rem): con eso solo, el nombre quedaba
     pegado contra el borde de arriba de todo. .nav es "fixed" (no
     empuja al reel, que se posiciona solo con --hero-top-clearance),
     asi que bajar este valor sube el nombre Y de paso separa mas el
     nombre del reel, las dos cosas a la vez. */
  padding-block: 2.75rem;
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: padding-block var(--dur-slow) var(--ease),
              background var(--dur-slow) var(--ease),
              border-color var(--dur-slow) var(--ease);
}

.nav.is-stuck {
  padding-block: 0;
  background: rgba(10,9,16,.86);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border-bottom-color: var(--c-line);
}

.nav__inner {
  position: relative;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* "Ian Iacono" a la izquierda de la barra, link de vuelta al reel:
   aparece siempre que la barra esta en su version chica/"is-stuck"
   (paginas de proyecto siempre, o el home despues de scrollear -- ver
   ".is-stuck" mas abajo y "3. BARRA DE ARRIBA" en js/main.js) y solo
   en escritorio. Misma tipografia/peso que .nav__brand-name (la firma
   del home) pero a un tamano que entra comodo en la barra chica -- no
   el clamp gigante pensado para flotar solo sobre el reel.
   margin-right:auto la empuja a la izquierda del todo sin tocar el
   justify-content compartido de .nav__inner (que .nav__brand no usa,
   por ser position:absolute).
   Aparece/desaparece con opacity+visibility, no con display -- display
   no se puede animar (cambia de golpe, sin fundido posible), y esto
   tiene que entrar y salir con un fade suave igual que .nav__brand
   (mismo patron: siempre ocupa su lugar en el flex, pointer-events la
   saca de la navegacion por teclado/mouse mientras esta invisible). */
.nav__project-brand {
  font-family: var(--font-name);
  /* Arriba a la izquierda, antes del boton y el slider de sonido, y link
     de vuelta al reel.

     Arriba de todo del home esta invisible, y ahi no puede ocupar lugar:
     si lo ocupara, el volumen no arrancaria pegado al borde izquierdo
     del reel, que es donde tiene que estar. Con max-width en 0 (mas
     overflow:hidden y el nowrap de mas abajo) no mide nada hasta que
     hace falta. Al achicarse la barra recupera su ancho y corre al
     volumen a su derecha -- animado y no de un salto, que es para lo que
     esta el max-width en la transicion.

     El hueco hasta el volumen aparece con el nombre, y por eso es un
     margen suyo y no un "gap" de la fila: un gap separa igual aunque el
     nombre mida cero, y esos 24px alcanzaban para despegar al volumen
     del borde del reel. Padding tampoco servia: con box-sizing:border-box
     una caja nunca queda mas angosta que su propio padding, asi que
     seguia midiendo esos 24px con max-width en 0. */
  max-width: 0;
  overflow: hidden;
  margin-right: 0;
  font-size: var(--fs-xl);
  font-weight: 900;
  color: var(--c-text);
  white-space: nowrap;
  text-decoration: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease), visibility 0s linear var(--dur-slow),
              max-width var(--dur-slow) var(--ease), margin-right var(--dur-slow) var(--ease),
              color var(--dur) var(--ease);
}
.nav__project-brand:hover { color: var(--c-accent-soft); }
.nav.is-stuck .nav__project-brand {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  max-width: 20rem;
  margin-right: var(--sp-5);
  transition: opacity var(--dur-slow) var(--ease), visibility 0s linear 0s,
              max-width var(--dur-slow) var(--ease), margin-right var(--dur-slow) var(--ease),
              color var(--dur) var(--ease);
}
/* En celular/tablet no existe: el equivalente ahi es .hero__welcome. */
@media (max-width: 900px) {
  .nav__project-brand { display: none; }
}

.nav__brand {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-slow) var(--ease), visibility 0s linear 0s;
}
.nav.is-stuck .nav__brand {
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-slow) var(--ease), visibility 0s linear var(--dur-slow);
}

.nav__brand-name {
  font-family: var(--font-name);
  /* Un clamp propio, no --fs-4xl: ese tope (6.5rem) choca contra los
     links a la derecha en pantallas medianas (~1366px). */
  font-size: clamp(2.25rem, 5vw + 1rem, 4.5rem);
  font-weight: 900;
  letter-spacing: 0;
  line-height: 1;
  color: var(--c-text);
  white-space: nowrap;
}

.nav__brand-role {
  margin-top: var(--sp-1);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-text-muted);
  white-space: nowrap;
}

/* Links + volumen: se ven siempre, arriba de todo del home (junto a la
   marca grande) y tambien al scrollear (ahi la marca ya se desvanecio). */
.nav__right {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

@media (min-width: 901px) {
  .audio-control--nav { margin-right: auto; }

  /* ARRIBA DE TODO DEL HOME, LA BARRA SE ALINEA CON EL REEL.
     El reel no ocupa todo el ancho: es 16/9 y esta centrado, asi que sus
     bordes caen adentro de los margenes de la pagina -- y la barra, que
     iba por esos margenes, quedaba por afuera del video. En una ventana
     de 1920 el volumen llegaba a estar 208px a la izquierda del borde
     del reel. Ahora los costados de la barra valen exactamente lo que le
     sobra al reel a cada lado, asi que el volumen empieza donde empieza
     el video y los links terminan donde termina.

     Vale en los dos estados de la barra, no solo arriba de todo: al
     scrollear la barra se achica pero nada se corre de lugar. Antes,
     al achicarse, volvia a los margenes de la pagina y el volumen se
     iba 218px a la derecha de un saque. */
  .nav {
    padding-inline: max(0px, calc((100% - var(--reel-w)) / 2));
  }
  /* Sin esto el volumen arrancaria un gutter mas adentro: el margen
     propio de .nav__inner, que aca no hace falta. */
  .nav__inner { padding-inline: 0; }
  /* Los links se corren su propio padding hacia afuera: lo que se ve
     alineado con el borde del video es la LETRA de "Contact", no la caja
     que la rodea. Del lado del volumen no hace falta -- ahi el borde de
     la caja es el circulo del boton, que si se ve. */
  .nav__right { margin-right: calc(var(--sp-3) * -1); }
}

/* Arriba de todo del home conviven tres cosas en la misma fila: el volumen
   a la izquierda, el nombre grande centrado y los links a la derecha. El
   nombre mide casi 500px y esta centrado en la PANTALLA (no en el hueco que
   dejan los otros dos), asi que por debajo de ~1360px se le encimaban -- se
   llego a ver el boton de volumen pisando el apellido. Debajo de ese ancho
   se hace lo que el sitio ya hacia en celular: arriba de todo se ve solo el
   nombre centrado, y el volumen y los links aparecen recien cuando la barra
   se achica al scrollear (que es justo cuando el nombre grande se va).
   El control de volumen se nombra aparte de .nav__right porque en escritorio
   vive afuera, a la izquierda, al lado del nombre chico. */
@media (max-width: 1359px) {
  .nav__right,
  .audio-control--nav {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 450ms ease-in-out, visibility 0s linear 450ms;
  }
  .nav.is-stuck .nav__right,
  .nav.is-stuck .audio-control--nav {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 450ms ease-in-out, visibility 0s linear 0s;
  }
}

.nav__links {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* .hero__welcome (el equivalente movil de .nav__brand, dentro de .hero
   en vez de dentro de la barra fija) esta definido en "07. HERO / REEL",
   junto al resto de .hero -- tiene mas sentido ahi que aca. */

.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  /* Antes eran mas chicos y livianos que .nav__brand-role (el subtitulo
     de arriba) -- se leian como una nota al pie en vez de una barra de
     navegacion. Mas grandes y mas pesados que el subtitulo, para que
     destaquen como corresponde a links reales -- pero sin pasarse: un
     salto mas grande (a --fs-sm) hacia mas ancho el bloque de la derecha
     y se metia contra el nombre grande centrado en pantallas medianas
     (~1366px, ver el comentario de .nav__brand-name mas abajo). */
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-text-muted);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: var(--sp-3);
  right: var(--sp-3);
  bottom: calc(50% - 0.9em);
  height: 1px;
  background: var(--c-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.nav__link:hover { color: var(--c-text); }
.nav__link:hover::after { transform: scaleX(1); }
/* Seccion actual al scrollear (ver el IntersectionObserver en main.js) */
.nav__link.is-active { color: var(--c-text); }
.nav__link.is-active::after { transform: scaleX(1); }

/* --- Control global de sonido ---------------------------------------------
   El unico control de audio de todo el sitio. En reposo es blanco/gris
   (neutro); a medida que subis el volumen se va poniendo violeta y le
   crece un brillo alrededor. Por eso el nivel se guarda dos veces en el
   HTML (--level en % y --level-num en numero, para poder hacer cuentas
   con calc() y color-mix()). */
.audio-control {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.audio-control__toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--tap) + 6px);
  height: calc(var(--tap) + 6px);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--r-full);
  background: rgba(0,0,0,.3);
  color: var(--c-text);
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
/* El boton del header (el de la barra compacta/.nav__right, con la barra
   de volumen al lado) un poco mas chico que el tamano base de arriba --
   la copia de .nav__brand (la de la bienvenida movil) se queda con el
   tamano original. */
.audio-control--nav .audio-control__toggle {
  width: calc(var(--tap) - 2px);
  height: calc(var(--tap) - 2px);
}
/* Escopado a "hover:hover" a proposito: en celular/tablet, tocar el boton
   dispara :hover igual que un mouse y el navegador lo deja "pegado" hasta
   el proximo toque en otro lado -- sin este escope, volver a mutear se
   veia mal (se quedaba con los colores de este estado en vez de pasar a
   los de aria-pressed="false" de mas abajo). Con esto :hover solo corre
   en dispositivos que de verdad tienen mouse. */
@media (hover: hover) and (pointer: fine) {
  .audio-control__toggle:hover {
    background: var(--c-text);
    border-color: var(--c-text);
    color: #0a0910;
  }
}
.audio-control__toggle svg { width: 16px; height: 16px; pointer-events: none; }
.audio-control__toggle .icon-on  { display: none; }
.audio-control__toggle[aria-pressed="true"] .icon-on   { display: block; }
.audio-control__toggle[aria-pressed="true"] .icon-off  { display: none; }
/* Con sonido: boton blanco solido, icono oscuro -- se ve "activo/prendido". */
.audio-control__toggle[aria-pressed="true"] {
  background: var(--c-text);
  border-color: var(--c-text);
  color: #0a0910;
  box-shadow: 0 0 16px -2px color-mix(in srgb, var(--c-accent) 55%, transparent);
  /* Instantaneo: el icono (sound-off -> sound-on) ya cambia de una, sin
     transicion posible (es un display:none/block). Si el color tardaba
     200ms en alcanzarlo, se veia como dos pasos separados -- primero
     cambia el icono, recien despues el fondo termina de ponerse blanco.
     Con 0s los dos cambian juntos, en el mismo instante. */
  transition-duration: 0s;
}

/* Silenciado: negro con el borde violeta, y ademas titila (el glow crece
   y se apaga) y vibra fuerte y seguido, invitando a activar el sonido --
   se detiene solo al prenderlo (aria-pressed pasa a "true", lo pinta
   audio.js) o si alguien lo esta enfocando o pasandole el mouse por
   arriba, para no competir con esa interaccion real. */
.audio-control__toggle[aria-pressed="false"] {
  background: #0a0910;
  border-color: var(--c-accent);
  /* Explicito (no solo heredado) para que vuelva bien a este color aunque
     el navegador haya dejado :hover "pegado" de un toque anterior. */
  color: var(--c-text);
  /* Mismo motivo que en aria-pressed="true": que el swap de vuelta a
     negro/violeta sea instantaneo, a la par del icono. */
  transition-duration: 0s;
  animation: toggle-attention 1.4s ease-in-out infinite;
}
.audio-control__toggle[aria-pressed="false"]:hover,
.audio-control__toggle[aria-pressed="false"]:focus-visible {
  animation-play-state: paused;
}
@keyframes toggle-attention {
  0%, 100% {
    box-shadow: 0 0 0 0 transparent;
    transform: translateX(0);
  }
  2%  { transform: translateX(-3px); }
  4%  { transform: translateX(3px); }
  6%  { transform: translateX(-3px); }
  8%  { transform: translateX(3px); }
  10% { transform: translateX(-2px); }
  12% { transform: translateX(2px); }
  14% {
    transform: translateX(0);
    box-shadow: 0 0 18px 3px color-mix(in srgb, var(--c-accent) 65%, transparent);
  }
  40% {
    box-shadow: 0 0 0 0 transparent;
  }
}
@media (prefers-reduced-motion: reduce) {
  .audio-control__toggle[aria-pressed="false"] { animation: none; }
}

/* El color de la barra y el circulo van de blanco/gris (nivel 0) a
   violeta (nivel 100): color-mix() con --level-num hace la mezcla en
   vivo, sin pasos ni JavaScript extra. */
.audio-control__level {
  display: none;
  -webkit-appearance: none;
  appearance: none;
  width: 140px;
  height: var(--tap);
  background: transparent;
  cursor: pointer;
}
/* El tramo vacio va "hundido" (sombra interna, como un canal tallado);
   el tramo lleno va "de relieve" (brillo arriba), como en un fader fisico
   de consola. */
.audio-control__level::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--r-full);
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--c-accent) var(--level, 80%), #d8d5e0) 0 var(--level, 80%),
    rgba(255,255,255,.14) var(--level, 80%) 100%
  );
  box-shadow: inset 0 1px 3px rgba(0,0,0,.65),
              inset 0 -1px 0 rgba(255,255,255,.05),
              0 0 calc(var(--level-num, 0) * 0.25px) color-mix(in srgb, var(--c-accent) 70%, transparent),
              0 0 calc(var(--level-num, 0) * 0.5px) color-mix(in srgb, var(--c-accent-deep) 55%, transparent);
  transition: box-shadow 120ms linear;
}
.audio-control__level::-moz-range-track {
  height: 6px;
  border-radius: var(--r-full);
  background: rgba(255,255,255,.14);
  box-shadow: inset 0 1px 3px rgba(0,0,0,.65), inset 0 -1px 0 rgba(255,255,255,.05);
}
.audio-control__level::-moz-range-progress {
  height: 6px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-accent) var(--level, 80%), #d8d5e0);
}
.audio-control__level::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px; height: 16px;
  margin-top: -5px;
  border: 0;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-accent) var(--level, 80%), var(--c-text));
  box-shadow: var(--sh-1),
              inset 0 1px 1px rgba(255,255,255,.55),
              0 0 calc(var(--level-num, 0) * 0.3px) color-mix(in srgb, var(--c-accent-soft) 85%, transparent),
              0 0 calc(var(--level-num, 0) * 0.6px) color-mix(in srgb, var(--c-accent-deep) 60%, transparent);
  transition: transform 150ms var(--ease), box-shadow 120ms linear, background 120ms linear;
}
.audio-control__level::-moz-range-thumb {
  width: 16px; height: 16px;
  border: 0;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-accent) var(--level, 80%), var(--c-text));
  transition: transform 150ms var(--ease), background 120ms linear;
}
.audio-control__level:hover::-webkit-slider-thumb,
.audio-control__level:focus-visible::-webkit-slider-thumb { transform: scale(1.25); }
.audio-control__level:active::-webkit-slider-thumb { transform: scale(1.5); }
.audio-control__level:hover::-moz-range-thumb,
.audio-control__level:focus-visible::-moz-range-thumb { transform: scale(1.25); }

/* Mientras el sitio esta silenciado, la bolita "respira" achicandose y
   agrandandose de a poco, como invitando a subir el volumen. */
@keyframes volume-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.35); }
}
.audio-control__level.is-muted::-webkit-slider-thumb { animation: volume-pulse 1.6s ease-in-out infinite; }
.audio-control__level.is-muted::-moz-range-thumb { animation: volume-pulse 1.6s ease-in-out infinite; }
.audio-control__level.is-muted:hover::-webkit-slider-thumb,
.audio-control__level.is-muted:active::-webkit-slider-thumb { animation-play-state: paused; }

@media (min-width: 640px) {
  .audio-control__level { display: block; }
}


/* ==========================================================================
   06. PAGINAS
   El sitio original de Carrd mostraba una seccion por vez, cambiando el # de
   la direccion. Se mantiene igual: cada <section class="page"> es una pagina.
   ========================================================================== */

.page { display: block; }

/* Mini animacion de entrada al cambiar de pagina (ver js/main.js, funcion
   "show"): fade + leve deslizamiento hacia arriba. La misma clase se usa
   yendo hacia un proyecto y volviendo -- así el "Back" se siente igual
   de vivo que entrar a un proyecto, en vez de aparecer de golpe. */
@keyframes page-enter {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.page.page-enter { animation: page-enter 360ms var(--ease); }

/* Espacio para que el contenido no quede tapado por la barra fija */
.page__body {
  padding-top: calc(var(--nav-h) + var(--sp-8));
  padding-bottom: var(--sp-10);
}

/* Cuando el bloque viene justo despues del reel o del carrusel, la barra
   fija ya no lo tapa, asi que hace falta menos aire arriba. */
.page__body--after-hero { padding-top: var(--sp-9); position: relative; }

/* Encabezado de pagina interna: etiqueta + titulo grande */
.page__head { margin-bottom: var(--sp-8); text-align: center; }


/* ==========================================================================
   07. HERO / REEL
   El video principal, con una copia borrosa de fondo llenando la pantalla.
   Al final se disuelve en el fondo de la pagina en vez de cortar en seco.
   ========================================================================== */

.hero {
  position: relative;
  width: 100%;
  min-height: 100svh;
  display: flex;
  /* flex-start (no center): el padding-top de abajo es quien decide la
     distancia al nombre, no un reparto automatico y parejo arriba/abajo. */
  align-items: flex-start;
  justify-content: center;
  padding-top: var(--hero-top-clearance);
  padding-bottom: var(--hero-bottom-clearance);
  background: var(--c-bg);
  overflow: hidden;
  isolation: isolate;
}

/* Capa de fondo: el mismo video, escalado y desenfocado */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
  background: #000;
  /* Mismo tipo de difuminado alpha que .hero__frame (ver mas abajo), pero
     solo en el borde de abajo: el fondo borroso se desvanece hacia
     transparente (revela el fondo solido de la pagina detras, del mismo
     color, ver "background" arriba) en vez de cortar en seco. En
     porcentaje, no en px, para que la misma proporcion funcione en pc y
     en celular sin reglas aparte. Arranca antes que antes (55%, no 62%)
     para que se note claramente -- el degrade de color de .hero__bg::after
     ya no le pisa el efecto (ver ese comentario). */
  --hero-bg-fade-start: 55%;
  -webkit-mask-image: linear-gradient(to bottom, #000 var(--hero-bg-fade-start), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 var(--hero-bg-fade-start), transparent 100%);
}
.hero__bg video,
.hero__bg img {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 128%;
  height: 128%;
  /* translateZ(0) fuerza una capa propia en la GPU para este elemento:
     sin esto, el navegador tiene que recalcular el blur sobre un video
     grande en cada cuadro sin aceleracion, y la reproduccion se ve
     entrecortada en vez de fluida. */
  transform: translate(-50%, -50%) translateZ(0);
  object-fit: cover;
  filter: blur(44px) brightness(.42) saturate(1.5);
  will-change: filter;
}

/* Vineta: oscurece los bordes para que el video central resalte, mas un
   leve oscurecido arriba (para que el nombre/header se lean bien
   encima). El desvanecido del borde de ABAJO ya no va aca -- lo hace el
   mask-image de .hero__bg (mas arriba en este archivo): un alpha real
   (transparencia de verdad, revela el fondo solido detras) en vez de
   una mezcla de color -- los dos efectos pisandose se veian casi
   invisibles juntos, por eso no se notaba ningun difuminado ahi. */
.hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 50% 45%, transparent 30%, rgba(0,0,0,.55) 100%),
    linear-gradient(to bottom, rgba(0,0,0,.5), transparent 22%);
}

.hero__frame {
  position: relative;
  z-index: 1;
  width: var(--reel-w);
  aspect-ratio: 16 / 9;
  border-radius: 0;
  overflow: hidden;
  background: #000;
  /* Los bordes de arriba y abajo se disuelven en vez de cortar de golpe —
     nada de video/sombra/marco duro justo ahi, se desvanecen hacia la
     pagina. Arriba separa el reel del nombre/header; abajo, de la
     grilla de proyectos. Los costados se dejan tal cual.
     OJO: esta distancia (--hero-fade) todavia esta en revision -- 22px
     se sintio muy brusco, 72px comia demasiado video. Ver el
     comparativo que se le mando al cliente antes de tocar este numero
     de nuevo a ciegas. */
  --hero-fade: 28px;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 var(--hero-fade), #000 calc(100% - var(--hero-fade)), transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 var(--hero-fade), #000 calc(100% - var(--hero-fade)), transparent);
}
/* Pantalla completa: llena toda la pantalla, sin marco ni difuminado —
   esas son decisiones de diseno para cuando esta encajado en la pagina,
   no tienen sentido a pantalla completa. */
.hero__frame:fullscreen,
.hero__frame:-webkit-full-screen {
  width: 100vw;
  height: 100vh;
  aspect-ratio: auto;
  border-radius: 0;
  box-shadow: none;
  -webkit-mask-image: none;
          mask-image: none;
}
/* A pantalla completa la pantalla casi nunca es 16:9 (en celular es al
   reves, mas alta que ancha): con "cover" el video se agranda tanto que
   recorta los costados casi enteros. "contain" respeta la proporcion
   real del video y deja franjas negras arriba/abajo o a los costados en
   vez de recortar — como cualquier reproductor de video de verdad. */
.hero__frame:fullscreen .hero__video,
.hero__frame:-webkit-full-screen .hero__video {
  object-fit: contain;
  background: #000;
}
.hero__frame [data-hero-main] {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  object-fit: cover;
}

/* Reemplaza el poster fijo: mientras el video no junto suficientes datos
   para arrancar, esto es lo unico que se ve (el video de atras es negro
   hasta entonces) -- visible de entrada por CSS, sin esperar al JS. El JS
   solo le agrega esta clase cuando el video ya puede arrancar. */
.player__loading {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  transition: opacity var(--dur-slow) var(--ease);
}
.player__loading.is-hidden { opacity: 0; pointer-events: none; }
.player__loading svg {
  width: 34px;
  height: 34px;
  color: var(--c-accent-soft);
  animation: spin 900ms linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .player__loading svg { animation: none; }
}

/* ==========================================================================
   PANTALLA DE CARGA INICIAL
   .site-blur envuelve nav + main + footer entero (ver build.py) para
   poder desenfocarlo todo junto. Arranca desenfocado por CSS puro, sin
   esperar al JS (asi nunca hay un flash de la pagina nitida antes de
   que el blur entre) -- el JS (PRELOADER en js/player.js) solo va
   subiendo --load-ratio de 0 a 1 a medida que el reel y el video de
   cada tarjeta quedan listos, lo que hace bajar el blur de a poco, y al
   final agrega is-ready para sacarlo del todo. El <noscript> en
   build.py cubre el caso sin JS: si no, quedaria borrosa para siempre. */
.site-blur {
  --load-ratio: 0;
  filter: blur(calc((1 - var(--load-ratio)) * 22px));
  transition: filter .5s var(--ease);
}
.site-blur.is-ready { filter: none; }

.preloader {
  position: fixed;
  inset: 0;
  z-index: 600;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-5);
  padding: var(--sp-6);
  text-align: center;
  pointer-events: none;
  opacity: 1;
  transition: opacity .5s var(--ease);
}
.preloader.is-hidden { opacity: 0; }

.preloader__msg {
  margin: 0;
  max-width: 22ch;
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-text-muted);
  text-shadow: 0 2px 20px rgba(6,5,9,.9), 0 0 40px rgba(6,5,9,.6);
}

.preloader__dots { display: flex; gap: var(--sp-2); }
.preloader__dots span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--c-accent-soft);
  box-shadow: 0 0 12px rgba(139,124,246,.55);
  animation: preloader-bounce 1.1s ease-in-out infinite;
}
.preloader__dots span:nth-child(2) { animation-delay: .15s; }
.preloader__dots span:nth-child(3) { animation-delay: .3s; }

@keyframes preloader-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: .45; }
  30% { transform: translateY(-6px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .site-blur { filter: none !important; }
  .preloader { display: none !important; }
}

/* Etiqueta chica debajo del rol de un proyecto, para casos como "todavia
   no se estreno" -- ver project__badge en build.py (project_page). */
.project__badge {
  display: inline-block;
  /* El inline-block de arriba alcanzaba hasta que .project__info paso a ser
     un flex en columna (para poder mandar los botones Back/Next al fondo,
     ver "10. PAGINAS DE PROYECTO"): en un flex los hijos se estiran al ancho
     completo por defecto, asi que el cartel se convertia en una barra de
     punta a punta en vez de rodear solo su texto. Esto lo achica de nuevo a
     lo que mide su contenido, sin tocar a los demas hijos -- si en cambio se
     le pusiera align-items al contenedor, tambien se encogerian el titulo y
     los parrafos, y el texto dejaria de cortarse a lo ancho de la columna. */
  align-self: flex-start;
  margin: var(--sp-2) 0 0;
  padding: 0.3em 0.85em;
  border: 1px solid var(--c-accent);
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-accent) 14%, transparent);
  color: var(--c-accent-soft);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}

/* Bienvenida movil: nombre + subtitulo + links + boton de sonido. Es el
   equivalente de .nav__brand (arriba, en "05. NAVEGACION"), pero vive
   ADENTRO de .hero -- que es position:relative, no fixed -- en vez de
   adentro de la barra de arriba. Por eso se va con el scroll como
   cualquier otro contenido del reel, en vez de quedar flotando encima
   mientras se scrollea. Solo existe en celular/tablet: en desktop se usa
   .nav__brand (la version de la barra fija) en su lugar. Reutiliza las
   clases .nav__brand-name / .nav__brand-role para el texto -- mismo
   estilo, un solo lugar donde tocar la tipografia si hace falta. */
.hero__welcome { display: none; }
@media (max-width: 900px) {
  .hero__welcome {
    display: block;
    position: absolute;
    top: 0;
    left: 50%;
    width: 100%;
    padding-top: var(--sp-4);
    transform: translateX(-50%);
    text-align: center;
    z-index: 2;
  }
  .hero__welcome-links {
    /* Con el cuarto link (About) los cuatro dejaron de entrar. Un margen
       automatico centra la CAJA, no lo que sobresale de ella: la caja
       quedaba centrada y los links se desbordaban todos para el mismo
       lado, asi que la fila terminaba 19px a la derecha del nombre y del
       subtitulo (medido a 390px de ancho: la fila necesita 324px y solo
       tenia 286). La unica forma de que quede centrada de verdad es que
       entre; por eso ahora usa todo el ancho disponible y las medidas de
       abajo se achican solas con la pantalla. */
    width: fit-content;
    max-width: 100%;
    margin: var(--sp-3) auto 0;
    flex-wrap: nowrap;
  }
  .hero__welcome-links .nav__link {
    padding-inline: clamp(0.125rem, 1.95vw, var(--sp-2));
    font-size: clamp(0.5625rem, 2.75vw, var(--fs-2xs));
  }
  /* El "top" y el "height" de este wrap los calcula JS (ver "7. CENTRAR
     EL BOTON..." en main.js), no CSS: .hero centra el marco del reel
     verticalmente (align-items:center, mas abajo en este archivo) para
     repartir el aire libre arriba/abajo, asi que el hueco real entre el
     borde de abajo de los links y el borde de arriba del video cambia
     segun el alto de pantalla -- no hay forma de calcularlo con un
     numero fijo en CSS. Position:absolute lo saca del flujo de
     .hero__welcome (que si no, seguiria empujando para abajo con su
     tamano normal) para que JS lo pueda ubicar a mano en ese hueco. */
  .hero__welcome-audio-wrap {
    position: absolute;
    left: 0;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  /* Un poco mas grande que el tamano base de .audio-control__toggle
     (calc(var(--tap) + 6px), 50px). */
  .hero__welcome-audio {
    width: calc(var(--tap) + 12px);
    height: calc(var(--tap) + 12px);
  }
}

/* "See Projects": solo tiene sentido en celular, donde no se ve la grilla
   de entrada. En desktop la grilla ya asoma debajo del reel, asi que se
   oculta (ver seccion 17, responsive). */
.hero__scroll {
  position: absolute;
  bottom: var(--sp-5);
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  color: var(--c-text-muted);
  text-decoration: none;
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  transition: color var(--dur) var(--ease);
}
.hero__scroll:hover { color: var(--c-text); }
.hero__scroll svg { width: 14px; height: 14px; }


/* ==========================================================================
   08. TARJETAS DE PROYECTO (TILES)
   Todas las tarjetas de una grilla comparten el mismo tamano (16:9): la
   imagen se recorta con object-fit para llenar el recuadro entero, sin
   bordes borrosos de relleno. Al pasar el mouse o enfocar con el teclado,
   el video del proyecto reemplaza la portada y arranca, con un resplandor
   blanco suave — a proposito no se usa el fondo con video (la "aura") aca,
   para que el brillo de una tarjeta no se meta con las de al lado.
   ========================================================================== */

/* Ya no hay dos grillas superpuestas: es una sola con los diez proyectos,
   y el selector de arriba filtra sus tarjetas (ver "4. SELECTOR DE
   CATEGORIA" en js/main.js). Las que no corresponden se esconden con
   [hidden]; el fundido de las que quedan lo hace .tile de aca abajo. */
.grid {
  display: grid;
  /* Tres por fila: tres filas para Sound (siete proyectos) y una para
     Audiovisual (tres) -- la forma de la tarjeta no cambia en ninguno
     de los dos casos. */
  grid-template-columns: repeat(3, 1fr);
  align-items: start;
  gap: var(--sp-8) var(--sp-6);
}

.tile { position: relative; }

.tile__link {
  position: relative;
  display: block;
  isolation: isolate;
  text-decoration: none;
  color: inherit;
  border-radius: var(--r-md);
}

.tile__media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--c-bg-raised);
  border: 1px solid var(--c-line);
  transition: transform 340ms var(--ease),
              border-color 340ms var(--ease),
              box-shadow 340ms var(--ease);
}

.tile__poster,
.tile__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.tile__poster {
  transition: opacity 300ms var(--ease), filter 340ms var(--ease);
  filter: saturate(.86) brightness(.92);
}
.tile__video { opacity: 0; transition: opacity 320ms var(--ease); }

/* Mientras la portada titila (ver "TARJETA CON TITILEO + SPOTIFY" en
   js/player.js) el brillo lo va cambiando el JavaScript de un salto al
   otro. La transicion de 340ms que tiene .tile__poster suavizaria justo
   eso: cada destello llegaria tarde y desdibujado, y en vez de un
   parpadeo se veria un latido lento. */
.tile__link.is-flickering .tile__poster { transition: none; }


/* El video queda oculto hasta que realmente arranca a reproducirse: asi
   nunca se ve un cuadro negro mientras carga. */
.tile__link.is-playing .tile__video  { opacity: 1; }
.tile__link.is-playing .tile__poster { opacity: 0; }

/* Resplandor blanco, contenido dentro del propio recuadro (no se agranda
   hacia afuera), para que nunca toque a la tarjeta de al lado. */
.tile__link:hover .tile__media,
.tile__link:focus-visible .tile__media {
  transform: translateY(-4px);
  border-color: rgba(255,255,255,.5);
  box-shadow: 0 0 0 1px rgba(255,255,255,.12),
              0 18px 40px -12px rgba(0,0,0,.6),
              0 0 32px -4px rgba(255,255,255,.22);
}
.tile__link:hover .tile__poster,
.tile__link:focus-visible .tile__poster { filter: saturate(1) brightness(1); }

.tile__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-4);
  min-height: var(--tap);
}

.tile__title {
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: var(--ls-normal);
  line-height: var(--lh-snug);
  color: var(--c-text);
  transition: color var(--dur) var(--ease);
}
.tile__link:hover .tile__title,
.tile__link:focus-visible .tile__title { color: var(--c-accent); }

.tile__role {
  margin-top: var(--sp-1);
  font-size: var(--fs-xs);
  color: var(--c-text-faint);
  transition: color var(--dur) var(--ease);
}
.tile__link:hover .tile__role,
.tile__link:focus-visible .tile__role { color: var(--c-accent); }

.tile__arrow {
  flex: none;
  width: 18px; height: 18px;
  color: var(--c-text-faint);
  transform: translateX(-4px);
  opacity: 0;
  transition: transform var(--dur-slow) var(--ease),
              opacity var(--dur-slow) var(--ease),
              color var(--dur) var(--ease);
}
.tile__link:hover .tile__arrow,
.tile__link:focus-visible .tile__arrow {
  opacity: 1;
  transform: translateX(0);
  color: var(--c-accent);
}


/* ==========================================================================
   09. SELECTOR SOUND / AUDIOVISUAL
   Dos botones: alternan que grilla se ve, sin cambiar de pagina.
   ========================================================================== */

/* Fila que junta el titulo "Selected Works" con el selector de pestanas. */
.portfolio-tabs-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-5);
  margin-bottom: var(--sp-8);
}

.portfolio-tabs-row__title {
  font-size: var(--fs-2xl);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
  line-height: 1;
  color: var(--c-text);
}

.portfolio-tabs {
  position: relative;
  display: inline-flex;
  gap: var(--sp-1);
  padding: var(--sp-1);
  background: var(--c-bg-raised);
  border: 1px solid var(--c-line);
  border-radius: var(--r-full);
}

/* Bolita blanca que desliza detras de la pestana activa */
.portfolio-tabs__thumb {
  position: absolute;
  top: var(--sp-1);
  bottom: var(--sp-1);
  left: 0;
  width: 0;
  border-radius: var(--r-full);
  background: var(--c-text);
  box-shadow: 0 0 14px -2px rgba(255, 255, 255, .55),
              0 0 3px rgba(255, 255, 255, .4);
  transform: translateX(0);
  transition: transform var(--dur) var(--ease), width var(--dur) var(--ease);
  pointer-events: none;
  z-index: 0;
}

.portfolio-tab {
  position: relative;
  z-index: 1;
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  min-height: var(--tap);
  padding-inline: clamp(var(--sp-3), 2vw, var(--sp-5));
  border-radius: var(--r-full);
  font-family: var(--font-tabs);
  font-size: var(--fs-2xs);
  font-weight: 400;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--c-text-faint);
  white-space: nowrap;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.portfolio-tab:hover:not([aria-selected="true"]) { color: var(--c-text); background: var(--c-bg-hover); }
.portfolio-tab[aria-selected="true"] {
  color: #0a0910;
  font-weight: 500;
}


/* ==========================================================================
   10. PAGINA DE PROYECTO
   Dos columnas: el material (video/audio) a la izquierda, el texto a la derecha.
   ========================================================================== */

.project {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: var(--sp-8) var(--sp-9);
  align-items: start;
}

.project__media { display: grid; gap: var(--sp-5); }

.project__credit {
  font-size: var(--fs-xs);
  color: var(--c-text-faint);
  letter-spacing: 0;
  /* Es el pie del video de arriba, asi que va pegado a el y no flotando a
     media distancia. El gap de .project__media (24px) esta pensado para
     separar un video de OTRO -- ahi hace falta ese aire (Red Bull tiene dos
     videos, La Llamada Fatal tres) -- pero aca separa de mas. En vez de
     achicar el gap para todos, este margen negativo se lo descuenta solo a
     este parrafo: 24 de gap menos 16 deja los 8px que se buscan. Hoy solo
     lo usa Lumia. */
  margin-top: calc(var(--sp-2) - var(--sp-5));
}

/* Marco 16:9 para los videos incrustados */
.embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px solid var(--c-line);
  background: #000;
  box-shadow: var(--sh-2);
}
.embed > iframe,
.embed > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Boton "facade": en vez de cargar el iframe pesado de YouTube apenas se
   entra al proyecto (dejaba la caja en blanco varios segundos, sobre todo
   en mobile), se muestra la miniatura del video con un boton de play; el
   iframe real (con autoplay) recien se crea al tocarlo -- ver
   js/embed-lazy.js */
.embed__facade {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.embed__facade-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.embed__facade-play {
  position: relative;
  z-index: 1;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--tap) + 14px);
  height: calc(var(--tap) + 14px);
  border-radius: var(--r-full);
  border: 1px solid var(--c-line-strong);
  background: rgba(0,0,0,.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--c-text);
  box-shadow: 0 0 16px -2px color-mix(in srgb, var(--c-accent) 70%, transparent);
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.embed__facade-play svg { width: 20px; height: 20px; margin-left: 2px; pointer-events: none; }
@media (hover: hover) {
  .embed__facade:hover .embed__facade-play {
    background: var(--c-text);
    border-color: var(--c-text);
    color: #0a0910;
    box-shadow: 0 0 22px -1px color-mix(in srgb, var(--c-accent) 85%, transparent);
  }
}

/* Marco para los reproductores de Spotify (son altos, no 16:9) */
.embed--audio {
  aspect-ratio: auto;
  border: 0;
  box-shadow: none;
  border-radius: var(--r-lg);
  background: transparent;
}
.embed--audio > iframe {
  position: static;
  height: 352px;
  border-radius: var(--r-lg);
}
/* Con mas de 2 reproductores adjuntados (ver project_page en build.py) --
   el alto "compact" oficial de Spotify: sigue mostrando tapa, titulo y
   barra de progreso, solo que en una fila en vez de la tarjeta grande. */
.embed--audio.embed--audio--compact > iframe {
  height: 152px;
}

/* Fila con scroll horizontal para los Reels de Instagram de un proyecto:
   son verticales y bastante altos, así que apilarlos uno debajo del otro
   haría enorme la página del proyecto -- en carrusel ocupan una sola fila
   de alto sin importar cuántos sean.
   min-width:0 en los dos es necesario -- si no, el ancho minimo "automatico"
   de estos items de grid/flex es el de todo el contenido sin achicarse (la
   fila entera de 8 reels), y eso empuja a lo ancho tanto a .project__media
   (el grid de afuera) como a .embed (el video, que comparte esa columna). */
.reels-carousel {
  min-width: 0;
  margin-top: var(--sp-5);
}
.reels-carousel__track {
  display: flex;
  min-width: 0;
  gap: var(--sp-4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}
.reels-carousel__item {
  flex: none;
  width: 280px;
  scroll-snap-align: start;
}
.reels-carousel__item iframe {
  display: block;
  width: 100%;
  height: 520px;
  border: 1px solid var(--c-line);
  border-radius: var(--r-md);
  background: var(--c-bg-raised);
}

.project__title {
  font-size: var(--fs-3xl);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-tight);
  margin-bottom: var(--sp-3);
}

.project__role {
  font-size: var(--fs-lg);
  font-weight: 600;
  line-height: var(--lh-snug);
  color: var(--c-accent);
  letter-spacing: var(--ls-normal);
}

.project__links {
  margin-top: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--c-text-muted);
}

.project__text { margin-top: var(--sp-8); }

/* --- Selector de idioma (solo celular, ver punto 10 en js/main.js) ---
   En escritorio no se ve: ahi estan los dos textos, cada uno con su
   etiqueta. En celular pasa al reves -- se ve el selector y se esconden
   las etiquetas, porque el selector ya dice de que idioma es el texto. */
.lang-switch { display: none; }

@media (max-width: 900px) {
  /* Mismo boton que el selector de proyectos (.portfolio-tabs): una
     pastilla con borde, y el idioma elegido encendido en blanco. La
     diferencia es la rayita en el medio, que es la que ya tenia y separa
     los dos idiomas como lo hacia la etiqueta. Por eso el blanco lo lleva
     puesto cada boton en vez de ser una bolita que se desliza como alla:
     deslizandose tendria que pasar por arriba de la rayita. */
  .lang-switch {
    display: inline-flex;
    align-items: center;
    /* La pastilla mide lo que su contenido y nada mas: adentro de una
       grilla o de una columna flex, un hijo se estira al ancho de la
       columna por defecto y quedaba una pastilla larguisima. */
    width: fit-content;
    max-width: 100%;
    justify-self: start;
    align-self: flex-start;
    gap: var(--sp-1);
    padding: var(--sp-1);
    background: var(--c-bg-raised);
    border: 1px solid var(--c-line);
    border-radius: var(--r-full);
    /* Toda la pastilla es el control (ver punto 10 en js/main.js), asi
       que toda la pastilla se ve apretable. */
    cursor: pointer;
    /* La misma distancia arriba que abajo: abajo la pone este margen, y
       arriba el margen del bloque que la contiene (ver .project__text y
       .about__cols en "14. RESPONSIVE"). */
    margin-bottom: var(--sp-4);
  }
  /* La etiqueta suelta sobra: lo que dice ya lo dice el selector. */
  .lang-switch + .lang .lang__label,
  .lang-switch ~ .lang .lang__label { display: none; }

  .lang-switch__btn {
    appearance: none;
    border: 0;
    background: transparent;
    cursor: pointer;
    min-height: var(--tap);
    padding-inline: clamp(var(--sp-3), 2vw, var(--sp-5));
    border-radius: var(--r-full);
    font-family: var(--font-tabs);
    font-size: var(--fs-2xs);
    font-weight: 400;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--c-text-faint);
    white-space: nowrap;
    transition: color var(--dur) var(--ease), background var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
  }
  .lang-switch__btn[aria-pressed="true"] {
    background: var(--c-text);
    color: #0a0910;
    font-weight: 500;
    box-shadow: 0 0 14px -2px rgba(255, 255, 255, .55),
                0 0 3px rgba(255, 255, 255, .4);
  }
  @media (hover: hover) {
    .lang-switch__btn:hover:not([aria-pressed="true"]) {
      color: var(--c-text);
      background: var(--c-bg-hover);
    }
  }
}

/* EL CAMBIO DE IDIOMA (solo celular -- en escritorio se ven los dos).
   No es un corte ni un fundido en el lugar: los dos textos se corren
   para el MISMO lado, el que se va y el que llega, asi se lee como un
   solo movimiento. La direccion la decide el JavaScript (punto 10 en
   js/main.js) con --lang-slide. */
.lang {
  transition: opacity 300ms var(--ease), transform 300ms var(--ease);
}
.lang.is-lang-off {
  opacity: 0;
  transform: translateX(var(--lang-slide, 0px));
}

/* Mientras dura el cambio el bloque toma el alto que va a tener, y lo
   anima. Sin esto, el mismo texto en dos idiomas (que nunca mide igual)
   hace saltar de golpe todo lo que viene abajo -- videos, embeds. El
   overflow es para que el texto que se va no se vea asomando por el
   costado ni por abajo mientras se corre. */
.about__cols.is-lang-sliding,
.project__text.is-lang-sliding {
  position: relative;
  overflow: hidden;
  transition: height 300ms var(--ease);
}

/* Bloque de idioma: la etiqueta ESP/ENG arriba y el parrafo debajo */
.lang + .lang { margin-top: var(--sp-6); }
/* En celular se muestra un texto solo, asi que la separacion entre los dos
   no va: si no, al pasar a ESP el texto se corria 20px y el cambio de
   idioma parecia un salto. Tiene que ir DESPUES de la linea de arriba --
   pesan igual, y entre iguales gana la ultima. */
@media (max-width: 900px) {
  .lang + .lang { margin-top: 0; }
}
.lang__label {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}
.lang__label::after {
  content: "";
  width: var(--sp-6);
  height: 1px;
  background: var(--c-line-strong);
}

/* Back a la izquierda, Next a la derecha (misma altura entre si).
   En celular va justo debajo del texto, con el mismo margen que separa a
   los demas bloques. En escritorio, ademas, todos los proyectos apoyan
   estos botones a la misma altura -- ver mas abajo. */
.project__actions {
  margin-top: var(--sp-8);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

/* En escritorio los botones de TODOS los proyectos quedan a la misma
   altura: la del proyecto con mas texto (hoy Detras Del Puesto), que es
   el que los tiene mas abajo. Antes cada uno los dejaba donde caia su
   propio texto y, pasando de uno a otro con Next, saltaban hasta ~290px.

   La columna de texto recibe un alto minimo comun y se vuelve flexible en
   vertical, asi "margin-top: auto" empuja los botones hasta el fondo de
   ese alto. El margen de arriba pasa a ser padding porque el auto ocupa
   el lugar del margin-top: sin el padding, en el proyecto mas largo (el
   que justo llena el alto minimo, donde el auto vale 0) los botones
   quedarian pegados al ultimo parrafo.

   --project-info-min-h la mide y la escribe js/main.js (punto 9): CSS no
   puede calcularla solo, porque cada proyecto es una seccion que esta en
   display:none mientras se mira otra y ninguna puede leer el alto de las
   demas. Si el JS no llegara a correr, la variable no existe, el alto
   minimo cae en "auto" y queda el comportamiento de antes. */
@media (min-width: 901px) {
  .project__info {
    display: flex;
    flex-direction: column;
    min-height: var(--project-info-min-h, auto);
  }
  .project__actions {
    margin-top: auto;
    padding-top: var(--sp-8);
  }
}


/* ==========================================================================
   10a. SOBRE MI  (#about)
   Va entre Selected Works y Contact, y en escritorio es una "pantalla"
   mas del scroll forzado (ver "14. RESPONSIVE" para el alto y
   "6. SCROLL FORZADO..." en js/main.js para el enganche).
   ========================================================================== */

/* El titulo va alineado a la izquierda, como el de Contact y el de
   Selected Works -- el centrado que trae .page__head por defecto es para
   las paginas de proyecto. */
#about .page__head {
  margin-bottom: var(--sp-6);
  text-align: left;
}
#about .page__head .project__title { margin-bottom: 0; }

/* Las dos versiones, una al lado de la otra. En celular la grilla cae a una
   sola columna sola (auto-fit + un ancho minimo), sin necesidad de un
   media query: cuando no entran dos columnas de 22rem, pasa a una. */
.about__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: var(--sp-7) var(--sp-8);
}
.about__cols .lang + .lang { margin-top: 0; }
.about__cols .prose + .prose { margin-top: var(--sp-5); }



/* ==========================================================================
   10b. FORMULARIO DE CONTACTO
   ========================================================================== */

/* El fondo pasa de negro (arriba, pegado a los proyectos) a un violeta muy
   oscuro y sutil (abajo) — nunca al reves, y el violeta no llega mas
   arriba que esta seccion. Va en la franja de ancho completo (.contact-band)
   y no en el .wrap de adentro (angosto), para que ocupe todo el ancho de
   la pagina en vez de verse como una caja recortada. */
.contact-band {
  background: linear-gradient(to bottom, transparent 0%, transparent 35%, var(--c-bloom-violet) 100%);
}

/* .page__head (el titulo "Contact") comparte clase con el de cada pagina
   de proyecto -- se achica el espacio solo aca, no en esas otras (ahi
   var(--sp-8) se queda igual). Ademas, solo aca, deja de estar centrado:
   el titulo va alineado a la izquierda y los iconos de redes se acomodan
   a su derecha, en la misma fila, centrados verticalmente con el texto. */
#contact .page__head {
  margin-bottom: var(--sp-4);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--sp-4);
  text-align: left;
}
/* .project__title trae margin-bottom (separarlo de .project__role en la
   pagina de un proyecto), pero aca el titulo y los iconos son vecinos en
   la misma fila, no apilados -- ese margen no separaba nada, solo corria
   el texto del titulo hacia arriba del centro real de la fila (el
   align-items:center de arriba centra la caja CON margen, no el texto
   visible). */
#contact .page__head .project__title { margin-bottom: 0; }

/* Mismo caso para el padding-bottom: .page__body trae var(--sp-10),
   pensado para una pagina de proyecto que termina sola. Contact termina
   en el pie de pagina, no en el vacio, asi que le sobraba aire antes del
   pie. */
#contact .page__body { padding-bottom: var(--sp-7); }

.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

.contact-form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-5);
}

.contact-form__field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.contact-form input,
.contact-form textarea {
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-bg-raised);
  border: 1px solid var(--c-line);
  border-radius: var(--r-md);
  color: var(--c-text);
  font: inherit;
  font-size: var(--fs-sm);
  resize: vertical;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.contact-form input::placeholder,
.contact-form textarea::placeholder { color: var(--c-text-faint); }
.contact-form input:focus-visible,
.contact-form textarea:focus-visible {
  outline: none;
  border-color: var(--c-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-accent) 30%, transparent);
}

.contact-form__status {
  margin-top: var(--sp-3);
  min-height: 1.2em;
  font-size: var(--fs-sm);
  text-align: center;
  color: var(--c-accent-soft);
}
.contact-form__status--error { color: var(--c-text); }

/* Ahora vive adentro de #contact .page__head, al lado del titulo (ver
   "10b. FORMULARIO DE CONTACTO" mas arriba) -- ya no necesita su propio
   margin-top ni centrado, los hereda de ese contenedor flex. */
.contact-form__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

.contact-form__social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid var(--c-line-strong);
  border-radius: var(--r-full);
  color: var(--c-text-muted);
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.contact-form__social svg { width: 17px; height: 17px; }
.contact-form__social:hover,
.contact-form__social:focus-visible {
  color: var(--c-text);
  background: var(--c-bg-hover);
  border-color: var(--c-text);
}

/* Boton flotante "volver al reel": aparece solo, con un respiro,
   mientras se esta viendo Contact (ver js/main.js, seccion 8). Oculto
   por defecto -- opacity+visibility, no display, para poder animar la
   entrada/salida y no perder el foco de un tiron. */
.back-to-reel {
  position: fixed;
  right: var(--sp-5);
  bottom: var(--sp-5);
  z-index: 80;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  border-radius: var(--r-full);
  border: 1.5px solid var(--c-text);
  background: rgba(0,0,0,.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--c-text);
  cursor: pointer;
  box-shadow: 0 0 16px -2px color-mix(in srgb, var(--c-accent) 70%, transparent);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity 400ms var(--ease), transform 400ms var(--ease),
              visibility 0s linear 400ms, background var(--dur) var(--ease),
              color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.back-to-reel.is-visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: none;
  transition: opacity 400ms var(--ease), transform 400ms var(--ease),
              visibility 0s linear 0s, background var(--dur) var(--ease),
              color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.back-to-reel svg { width: 20px; height: 20px; pointer-events: none; }

/* Entrada de las flechitas Back/Next de celular (ver "14. RESPONSIVE").
   Mismo movimiento que .back-to-reel de aca arriba -- sube 8px mientras
   aparece -- para que se sientan el mismo elemento. */
@keyframes project-arrows-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@media (hover: hover) {
  .back-to-reel:hover {
    background: var(--c-text);
    color: #0a0910;
    box-shadow: 0 0 22px -1px color-mix(in srgb, var(--c-accent) 85%, transparent);
  }
}


/* ==========================================================================
   11. BOTONES Y LINKS
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0 var(--sp-5);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--r-full);
  background: transparent;
  color: var(--c-text);
  font-size: var(--fs-xs);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.btn:hover {
  background: var(--c-text);
  border-color: var(--c-text);
  color: #0a0910;
}
.btn svg { width: 15px; height: 15px; flex: none; }

.btn--primary {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: #100c1c;
  font-weight: 500;
}
.btn--primary:hover {
  background: var(--c-accent-soft);
  border-color: var(--c-accent-soft);
  color: #100c1c;
}

.btn--ghost { border-color: transparent; color: var(--c-text-muted); }
.btn--ghost:hover { background: var(--c-bg-hover); border-color: var(--c-line); color: var(--c-text); }

.link {
  color: var(--c-accent);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--c-accent) 40%, transparent);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.link:hover { color: var(--c-accent-soft); border-bottom-color: var(--c-accent-soft); }


/* ==========================================================================
   12. REPRODUCTOR DE VIDEO — controles del reel
   Play/pausa es el unico control que arranca o frena la reproduccion; el
   sonido lo maneja siempre el control del header.
   ========================================================================== */

.player__controls {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}
/* ":hover" escopado a "hover:hover" a proposito -- en tactil, tocar el
   video dispara :hover igual que un mouse y el navegador lo deja
   "pegado" hasta el proximo toque en otro lado, lo que hacia que los
   controles se vieran "siempre" en vez de aparecer/ocultarse con el
   toque (ver .is-touch, controlado por JS en player.js). */
@media (hover: hover) {
  .hero__frame:hover .player__controls { opacity: 1; transform: translateY(0); }
}
.hero__frame:focus-within .player__controls,
.player__controls.is-touch {
  opacity: 1;
  transform: translateY(0);
}

.player__playpause,
.player__fullscreen {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--r-full);
  background: rgba(0,0,0,.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--c-text);
  cursor: pointer;
  box-shadow: 0 0 16px -2px color-mix(in srgb, var(--c-accent) 70%, transparent);
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.player__playpause:hover,
.player__fullscreen:hover {
  background: var(--c-text);
  border-color: var(--c-text);
  color: #0a0910;
  box-shadow: 0 0 22px -1px color-mix(in srgb, var(--c-accent) 85%, transparent);
}
.player__playpause svg,
.player__fullscreen svg { width: 15px; height: 15px; pointer-events: none; }
.player__playpause .icon-play { display: none; }
.player__playpause[aria-pressed="false"] .icon-play  { display: block; }
.player__playpause[aria-pressed="false"] .icon-pause { display: none; }

.player__fullscreen .icon-compress { display: none; }
.player__fullscreen[aria-pressed="true"] .icon-expand   { display: none; }
.player__fullscreen[aria-pressed="true"] .icon-compress { display: block; }

/* Linea de tiempo: es un <input type="range"> real, asi anda con el teclado */
.player__scrub {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--tap);
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
/* El recorrido ya andado va de un violeta profundo (arranque) a uno claro
   justo en la bolita — como si el brillo se acumulara a medida que
   avanza, en vez de un violeta plano. */
.player__scrub::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: var(--r-full);
  background: linear-gradient(
    to right,
    var(--c-accent-deep) 0%,
    var(--c-accent-soft) var(--progress, 0%),
    rgba(255,255,255,.28) var(--progress, 0%) 100%
  );
}
.player__scrub::-moz-range-track {
  height: 3px;
  border-radius: var(--r-full);
  background: rgba(255,255,255,.28);
}
.player__scrub::-moz-range-progress {
  height: 3px;
  border-radius: var(--r-full);
  background: linear-gradient(to right, var(--c-accent-deep), var(--c-accent-soft));
}
.player__scrub::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 13px; height: 13px;
  margin-top: -5px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--c-text);
  box-shadow: var(--sh-1),
              0 0 7px 1px color-mix(in srgb, var(--c-accent-soft) 85%, transparent),
              0 0 14px 2px color-mix(in srgb, var(--c-accent-deep) 55%, transparent);
  transform: scale(.72);
  transition: transform var(--dur) var(--ease);
}
.player__scrub::-moz-range-thumb {
  width: 13px; height: 13px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--c-text);
  box-shadow: 0 0 7px 1px color-mix(in srgb, var(--c-accent-soft) 85%, transparent),
              0 0 14px 2px color-mix(in srgb, var(--c-accent-deep) 55%, transparent);
  transform: scale(.72);
  transition: transform var(--dur) var(--ease);
}
.hero__frame:hover .player__scrub::-webkit-slider-thumb,
.player__scrub:focus-visible::-webkit-slider-thumb { transform: scale(1); }
.hero__frame:hover .player__scrub::-moz-range-thumb,
.player__scrub:focus-visible::-moz-range-thumb { transform: scale(1); }

/* En celular no hay ":hover" que agrande la bolita, asi que se queda
   siempre chica (scale .72) -- y como el transform tambien achica la
   zona que el dedo puede tocar para agarrarla, terminaba haciendo falta
   acertarle casi pixel perfecto. Aca se la deja siempre a tamaño real
   (sin el achique) y ademas mas grande que en escritorio, asi el dedo
   tiene margen de sobra alrededor sin tener que caer justo en el centro. */
@media (hover: none) {
  .player__scrub::-webkit-slider-thumb {
    width: 22px; height: 22px;
    margin-top: -9.5px;
    transform: scale(1);
  }
  .player__scrub::-moz-range-thumb {
    width: 22px; height: 22px;
    transform: scale(1);
  }
}

.player__time {
  flex: none;
  font-size: var(--fs-2xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  color: var(--c-text-muted);
}


/* ==========================================================================
   13. ANIMACIONES DE ENTRADA
   Aparecen suavemente al hacer scroll. Se apagan solas si la persona pidio
   "menos movimiento" en la configuracion de su sistema.
   ========================================================================== */

.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  will-change: opacity, transform;
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
  will-change: auto;
}
/* Escalona la entrada de los elementos de una grilla */
.reveal[data-delay="1"] { transition-delay:  70ms; }
.reveal[data-delay="2"] { transition-delay: 140ms; }
.reveal[data-delay="3"] { transition-delay: 210ms; }
.reveal[data-delay="4"] { transition-delay: 280ms; }

/* Si no hay JavaScript, todo se ve igual: nunca queda contenido invisible */
.no-js .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
}


/* ==========================================================================
   14. RESPONSIVE
   Se diseno primero para celular; aca se ajusta lo que hace falta.
   ========================================================================== */

/* --- Tablet y abajo (hasta 900px) --- */
@media (max-width: 900px) {
  /* En mobile, los bloques ya aparecen enteros -- nada de fade-in al
     scrollear hasta ellos. Esto es puramente visual (la animacion de
     ".reveal" no retrasa ninguna descarga real: el HTML de cada
     bloque ya esta en la pagina desde el principio, solo se anima su
     opacity/transform), asi que apagarla aca no cuesta nada de
     rendimiento -- es al reves de un lazy-load. */
  .reveal { opacity: 1; transform: none; }

  /* Deja lugar arriba de .hero para ".hero__welcome" (nombre + subtitulo
     + links + boton de sonido, ver "07. HERO / REEL") -- ese bloque
     ahora vive DENTRO del reel, no en la barra fija (ver mas abajo por
     que). Un poco mas alto que antes (13rem, no 11rem): ese bloque
     completo (nombre + subtitulo + links + boton, apilados) no entraba
     mas en el espacio viejo. */
  :root { --hero-top-clearance: 13rem; }

  /* .nav__brand (la version de escritorio, adentro de la barra fija) NO
     se usa en este ancho. .nav es position:fixed, asi que nombre,
     subtitulo, links y boton quedaban flotando sobre el reel al
     scrollear en vez de irse con la pagina -- eso es lo que se pedia
     corregir. La version movil (.hero__welcome) vive dentro de .hero,
     que es position:relative (no fixed): se va del scroll como
     cualquier otro contenido del reel. Con .nav__brand afuera de esto,
     .nav vuelve a ser una barra chica siempre (nunca necesita ser
     grande) -- se apoya en su min-height de base (var(--nav-h)) para
     el alto. */
  .nav__brand { display: none; }
  .nav, .nav.is-stuck { padding-block: var(--sp-3); }

  /* En desktop el reel se ancla arriba (align-items:flex-start en
     .hero) porque ahi el "top clearance" ya calcula justo cuanto
     hace falta para separarlo del header. Pero en mobile/tablet el
     .hero ocupa toda la pantalla (100svh) y el marco es mas chico
     (menos ancho disponible = menos alto, por el aspect-ratio), asi
     que anclado arriba deja un espacio vacio enorme abajo antes del
     "See Projects" -- y el margen de arriba, fijo, se siente escaso
     en comparacion. Centrado verticalmente reparte ese aire entre
     arriba y abajo, en vez de amontonarlo todo abajo. */
  .hero { align-items: center; }

  /* .page__body--after-hero (la clase de la que #projects hereda su
     padding-top) esta pensada para cuando la barra fija ya no tapa nada
     -- pero en este ancho, al tocar "Projects" en el nav (o al scrollear
     hasta ahi), quedaba MUCHO aire antes del titulo "Selected Works".
     Achicado especificamente para celular/tablet.
     El padding-top no es tan chico como parece: hace falta ese colchon
     extra para que "Selected Works" nunca asome en la pantalla del
     reel -- .hero mide 100svh (el alto "chico" de la pantalla, cuando
     la barra del navegador esta desplegada), pero muchos celulares
     arrancan la carga con esa barra ya colapsada, con lo cual el alto
     real visible es mayor al 100svh calculado y el contenido de
     despues del reel queda asomando un poco. Este aire de mas absorbe
     esa diferencia. El padding-bottom, en cambio, se achica: heredaba
     el de .page__body (pensado para paginas de proyecto, mucho mas
     alto) y dejaba demasiada distancia entre la ultima tarjeta y
     Contact. Ligeramente mas grande que el primer achique: quedaba
     un poco demasiado pegado. */
  #projects { padding-top: var(--sp-9); padding-bottom: var(--sp-6); }

  /* Una sola columna: las tarjetas van apiladas una debajo de la otra,
     sin compartir fila. */
  .grid { grid-template-columns: 1fr; gap: var(--sp-7) var(--sp-5); }

  /* En celular se lee primero de que va el proyecto y despues se mira el
     material: titulo, subtitulo, "Upcoming release" si lo hay, los textos
     ESP/ENG, y recien al final los videos y embeds. En el HTML el bloque
     multimedia viene primero (en escritorio es la columna izquierda, ver
     "10. PAGINAS DE PROYECTO"), asi que aca se invierte con "order" en vez
     de tocar el orden real del documento -- moverlo en el HTML le cambiaria
     el lado en escritorio. */
  .project__info  { order: 1; }
  .project__media { order: 2; }

  /* Los botones Back/Next dejan de ser dos botones con texto al final del
     texto y pasan a ser dos flechitas flotantes, iguales a la de "volver
     al reel" del inicio (mismo tamano, mismo borde, mismo fondo y a la
     misma altura), una a cada lado de la pantalla. Son los MISMOS enlaces
     de siempre, solo con otra pinta: asi no hay un segundo juego de
     direcciones que mantener en sincronia (ver el comentario del orden de
     los proyectos en index.html).

     Al vivir dentro de la seccion del proyecto, desaparecen solas al salir
     de el: el router esconde la seccion entera (display:none) y con ella se
     va cualquier position:fixed de adentro.

     El contenedor no recibe toques (pointer-events:none) porque ocupa todo
     el ancho de la pantalla; solo las dos flechitas lo hacen. */
  .project__actions {
    position: fixed;
    left: var(--sp-5);
    right: var(--sp-5);
    bottom: var(--sp-5);
    z-index: 80;
    margin-top: 0;
    padding-top: 0;
    pointer-events: none;
    /* Aparecen recien a los 0.4s de entrar al proyecto. "both" es lo que
       las mantiene invisibles DURANTE esa espera: sin eso se verian de
       entrada y el retraso no serviria de nada. La animacion se reinicia
       sola en cada proyecto, porque cada seccion pasa de display:none a
       visible al mostrarse. */
    animation: project-arrows-in var(--dur-slow) var(--ease) 400ms both;
  }
  .project__actions .btn {
    pointer-events: auto;
    width: var(--tap);
    height: var(--tap);
    min-height: 0;
    padding: 0;
    justify-content: center;
    border-radius: var(--r-full);
    border: 1.5px solid var(--c-text);
    background: rgba(0,0,0,.55);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: var(--c-text);
    box-shadow: 0 0 16px -2px color-mix(in srgb, var(--c-accent) 70%, transparent);
  }
  /* Las palabras "Back" y "Next" sobran: quedan dos flechas y se entiende
     solo. El texto sigue en el HTML para quien navegue con lector de
     pantalla. */
  .project__actions .btn span {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .project__actions .btn svg { width: 20px; height: 20px; }

  .project {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-7);
  }
  /* En escritorio el texto y el subtitulo estan en columnas distintas del
     mismo bloque, asi que ese margen de arriba (var(--sp-8)) hace de aire
     entre columnas y aca no venia al caso -- estaba en cero. Pero ahora que
     en celular todo va apilado (titulo, subtitulo, badge y despues los
     textos, ver "order" mas arriba), quedaban pegados sin ninguna
     separacion. Se le da el mismo aire que separa al parrafo en espanol del
     que esta en ingles (.lang + .lang), asi el ritmo vertical del bloque es
     uno solo. */
  /* Lo mismo que el margen de abajo del selector de idioma, para que
     quede a la misma distancia del subtitulo violeta que del texto. */
  .project__text { margin-top: var(--sp-4); }
  /* En About el selector arranca pegado al titulo y al texto por igual:
     la fila de la grilla no separa (el idioma que no se muestra no esta),
     y el titulo se acerca. */
  #about .about__cols { row-gap: 0; }
  #about .page__head { margin-bottom: var(--sp-4); }

  .nav__right {
    /* Mas chico que en desktop: con el tamano de link mas grande que le
       pusimos a .nav__link en general, esta fila ya no entraba en una
       pantalla angosta y se salia por el costado. Aca adentro, en esta
       barra compacta especificamente, vuelve a un tamano chico --
       .hero__welcome-links (la bienvenida, arriba de todo) no se toca,
       se queda grande. */
    gap: var(--sp-2);
  }
  .nav__right .nav__link {
    padding-inline: var(--sp-2);
    font-size: var(--fs-2xs);
  }
  .nav__right .nav__links { gap: var(--sp-1); }
  .audio-control--nav .audio-control__toggle {
    width: calc(var(--tap) - 8px);
    height: calc(var(--tap) - 8px);
  }
  /* .nav__brand esta oculto en este ancho (no ocupa lugar), asi que
     .nav__right (boton de volumen + links) queda como el unico
     contenido real de la barra -- centrado en vez de pegado a la
     derecha. */
  .nav__inner { justify-content: center; }

  /* .page__body trae un padding-top pensado para paginas que arrancan
     solas, justo debajo de la barra fija (cada proyecto). Contact no es
     una de esas -- viene justo despues de la grilla de proyectos, en el
     mismo scroll continuo -- asi que ese mismo padding quedaba como
     demasiada separacion entre las dos secciones. */
  #contact .page__body { padding-top: var(--sp-6); }
}

/* --- Celular (hasta 640px) --- */
@media (max-width: 640px) {
  :root { --nav-h: 4rem; }

  /* "Sound Designer // Multimedia Post Production" no entra en una linea
     en un celular angosto -- se corta contra los bordes. Se lo deja
     envolver en 2 lineas en vez de forzarlo a una sola. .nav__brand-role
     ya no necesita su propio max-width (.nav__brand esta oculto en este
     ancho, ver "14. RESPONSIVE" arriba) pero .hero__welcome, que es
     quien lo reemplaza aca, si. */
  .nav__brand-role { white-space: normal; }
  .hero__welcome { max-width: calc(100vw - var(--sp-8)); }

  .grid { grid-template-columns: 1fr; gap: var(--sp-6) var(--sp-4); }

  /* El marco ocupa todo el ancho, pegado al video, pero ahora si mantiene
     el mismo difuminado de arriba/abajo que en desktop (--hero-fade,
     definido en la regla base de .hero__frame) en vez de sacarlo. */
  .hero__frame {
    width: 100%;
    border-radius: 0;
  }
  .hero { min-height: 100svh; }

  .player__controls { gap: var(--sp-3); padding: var(--sp-4) var(--sp-3) var(--sp-3); }
  .player__time { display: none; }   /* no entra: se prioriza la barra */

  .embed--audio > iframe { height: 232px; }

  .page__body { padding-top: calc(var(--nav-h) + var(--sp-6)); }
  .portfolio-tabs { width: 100%; }
  /* min-width:0 + white-space:normal -- si no, "Audiovisual Portfolio"
     (una sola palabra larga entre las dos, y de por si en una sola linea
     -- ver white-space:nowrap de la regla base) le pone un piso al boton
     mas ancho que lo que el flex-shrink de arriba deja disponible, y se
     sale de la pantalla en vez de partirse en dos lineas. */
  .portfolio-tab {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: var(--sp-2);
    text-align: center;
    white-space: normal;
  }

  .contact-form__row { grid-template-columns: 1fr; }
}

/* --- Pantallas muy angostas (360px y menos) --- */
@media (max-width: 380px) {
  :root { --gutter: 1rem; }
  .nav__right { gap: var(--sp-2); }
  .nav__link { padding-inline: var(--sp-2); }
  .nav__links { gap: 0; }
}

/* --- Pantallas grandes (1440px y mas) --- */
@media (min-width: 1440px) {
  :root { --maxw: 104rem; }
}

/* --- "See Projects" es solo para celular: en desktop la grilla ya se ve
   asomando debajo del reel, asi que el cartel sobra. --- */
@media (min-width: 641px) {
  .hero__scroll { display: none; }
}

/* --- Escritorio (mas de 900px): "Selected Works" entra completa en una
   sola pantalla, centrada verticalmente. El scroll forzado del reel hacia
   esta seccion (con scrollIntoView + un enfriamiento por tiempo para no
   pelearse con la inercia de un trackpad) se hace por JavaScript, ver
   "6. SCROLL FORZADO..." en js/main.js -- ahi esta el historial completo
   de las versiones anteriores de este mecanismo. En celular no se toca
   nada de esto. --- */
@media (min-width: 901px) {
  #projects {
    /* Alto = la franja que se ve de verdad, no la pantalla entera: la
       seccion arranca abajo de la barra fija, asi que un 100svh completo
       le sobra justo el alto de la barra. Ese sobrante inventaba una
       parada de scroll: con Audiovisual (una sola fila, que entra sobrada)
       la seccion seguia siendo 88px mas alta que la pantalla, asi que el
       "piso" de Selected Works quedaba 88px debajo del "techo" y la rueda
       gastaba un tiron en mover la pagina esos 88px sin mostrar nada
       nuevo. Con la franja real, cuando la grilla entra las dos paradas
       son la misma y desaparece esa (ver "6. SCROLL POR SECCIONES" en
       js/main.js). Mismo calculo que #about y #contact aca abajo. */
    min-height: calc(100svh - var(--nav-h) - var(--sp-4));
    display: flex;
    flex-direction: column;
    /* "flex-start", no "center": con "center", si el contenido (titulo +
       pestanas + grilla) es mas bajo que la pantalla, el sobrante se
       reparte MITAD ARRIBA MITAD ABAJO -- eso le devolvia por atras el
       aire que se le habia sacado a padding-top (aca abajo), asi que el
       cambio anterior no se notaba. Con "flex-start" todo el sobrante
       cae SOLO abajo (invisible, no afecta si entra la ultima fila), y
       arriba el contenido arranca pegado al padding-top real, sin nada
       que lo empuje mas abajo. */
    justify-content: flex-start;
    /* Se saca del todo: ver el comentario de arriba. */
    padding-top: 0;
    padding-bottom: var(--sp-4);
    /* El <html> reserva "scroll-padding-top" (altura de la barra fija +
       un respiro de 16px, ver "02. RESET Y BASE") para que el scroll
       nunca deje nada tapado detras de esa barra. Ese respiro de 16px es
       mas de lo que hace falta aca -- se recorta a la mitad (scroll-margin
       SUMA a scroll-padding, asi que un valor negativo lo resta) sin
       tocarlo para el resto del sitio. */
    scroll-margin-top: -8px;
  }
  /* Espacio entre las pestanas Sound/Audiovisual y la grilla: achicado
     un poco mas. */
  #projects .portfolio-tabs-row { margin-bottom: var(--sp-6); }
  /* Espacio ENTRE las dos filas de tarjetas (ej.: entre la fila de
     arriba y la fila de abajo, donde esta "La Llamada Fatal"). */
  #projects .grid { row-gap: var(--sp-4); }
  /* OJO: un intento anterior le puso "max-width: 94%" a esta grilla para
     achicar las tarjetas un poco -- en vez de un achique leve, termino
     achicandolas muchisimo mas de lo esperado (interaccion rara con
     .portfolio-panels, que tambien es un grid). Se saco por completo:
     si "La Llamada Fatal" sigue sin entrar en tu pantalla, avisame el
     alto de tu ventana y lo resolvemos con otro numero, no con este
     mecanismo. */

  /* Sobre mi es una pantalla mas del scroll forzado, asi que ocupa una
     pantalla entera igual que Selected Works: estando parado aca no se ve
     ni la grilla arriba ni el formulario abajo. El contenido va centrado
     verticalmente porque es poco texto -- pegado al techo se veria
     desbalanceado con todo el aire debajo. El scroll-margin-top negativo
     se lleva TODO el respiro de 16px que reserva "scroll-padding-top" (no
     la mitad, como hace #projects): asi el techo de la seccion cae justo
     en el borde de abajo de la barra fija y no queda ni un hilo de
     Selected Works asomando entre las dos. */
  #about {
    /* Alto = la franja que se ve de verdad, no la pantalla entera: la
       seccion arranca abajo de la barra fija, asi que un 100svh completo
       le sobraba justo el alto de la barra y se iba esos px abajo del
       borde inferior. Con el bloque centrado adentro, ese sobrante se
       traducia en el texto apoyado bajo: 309px de aire arriba contra
       125px abajo, en una ventana de 1440x900. Restando el clearance de
       la barra (lo mismo que hace #contact aca abajo por su cuenta), la
       seccion coincide con la franja visible y el centrado es el que se
       ve. */
    min-height: calc(100svh - var(--nav-h) - var(--sp-4));
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-top: 0;
    padding-bottom: 0;
    scroll-margin-top: calc(var(--sp-4) * -1);
  }
  /* El respiro de arriba y abajo lo pone el centrado de la seccion. El
     que trae .page__body (que existe para las paginas de proyecto, donde
     el contenido arranca pegado a la barra) es distinto arriba que abajo
     y correria el bloque unos pixeles hacia abajo. */
  #about .page__body {
    padding-top: 0;
    padding-bottom: 0;
  }

  /* Mismo motivo que #projects arriba, pero aca ademas rompia el scroll
     forzado: sin este alto minimo, Contact + el pie de pagina no
     llegaban a ocupar una pantalla completa, asi que el navegador nunca
     podia scrollear lo suficiente como para dejar a Contact pegado
     arriba del todo -- se seguian viendo las ultimas tarjetas de
     Selected Works asomando arriba, Y el punto de enganche de Contact
     (contactSnap en js/main.js) quedaba mas abajo de lo que el scroll
     llegaba a alcanzar nunca, asi que ese codigo no reconocia "estoy en
     Contact" y, al scrollear para arriba desde ahi, saltaba directo al
     reel salteando Selected Works.
     Un 100svh completo dejaba al pie de pagina fuera de esa pantalla
     (habia que scrollear un poco mas para verlo) -- la idea es que
     "estar en Contact" sea ya el final de la pagina, formulario Y pie
     de pagina juntos en la misma pantalla, no solo el formulario. Por
     eso el alto minimo le resta el alto real del pie (medido por JS,
     ver "6. SCROLL FORZADO..." en js/main.js -- el pie no es hijo de
     #contact, asi que CSS solo no puede restarlo) Y el clearance de la
     barra fija (lo mismo que "scroll-padding-top" mas arriba: sin
     restar esto tambien, el pie quedaba empujado esos mismos px mas
     abajo del final de la pantalla). El resto sobrante (si el
     formulario es mas bajo que eso) queda como aire entre el
     formulario y el pie, no despues del pie. */
  #contact {
    min-height: calc(100svh - var(--footer-h, 66px) - var(--nav-h) - var(--sp-4));
    display: flex;
    flex-direction: column;
    /* El sobrante ya no queda todo abajo (entre el formulario y el pie):
       se reparte arriba y abajo, asi el bloque entero -- titulo, iconos,
       formulario -- queda centrado en la pantalla de Contact en vez de
       apoyado contra el techo. Horizontalmente ya venia centrado, que es
       lo que hace .wrap--narrow. */
    justify-content: center;
  }
  /* Para que ese centrado se VEA centrado hace falta que el aire de
     arriba y el de abajo midan igual. .page__body trae un padding-top
     grande (alto de la barra fija + sp-8) porque una pagina de proyecto
     arranca en scroll 0, tapada por esa barra; Contact no; a esta altura
     ya se llego scrolleando, y el hueco de la barra lo reserva
     "scroll-padding-top" (ver "02. RESET Y BASE"). Ese padding de mas
     empujaba todo el contenido hacia abajo del centro real.

     Los tres clamp() de aca abajo son lo que permite que esta seccion
     ocupe MAS alto sin romper lo de siempre: que el formulario y el pie
     de pagina entren juntos en la misma pantalla. Con medidas fijas, el
     campo nuevo (Subject) mas el textarea agrandado empujaban el pie 93px
     afuera de la pantalla en un monitor de 768px de alto (el clasico
     1366x768). Atandolos a svh, el aire y el textarea crecen en pantallas
     altas -- que es donde sobraba espacio -- y se achican en las bajas,
     donde no. El maximo de cada clamp es lo que se ve en una pantalla
     grande; el minimo, el piso por debajo del cual ya se leeria apretado. */
  #contact .page__body {
    padding-top: clamp(var(--sp-4), 4svh, var(--sp-7));
    padding-bottom: clamp(var(--sp-4), 4svh, var(--sp-7));
  }
  #contact .contact-form { gap: clamp(var(--sp-3), 2.4svh, var(--sp-5)); }
  /* Pisa el alto que da rows="7" en el HTML (que queda igual como valor
     de respaldo, y es el que usa el celular). */
  #contact .contact-form textarea { height: clamp(7.5rem, 20svh, 15rem); }
}

/* --- Impresion: que se pueda imprimir sin desperdiciar tinta --- */
@media print {
  .nav, .player__controls { display: none !important; }
  body { background: #fff; color: #000; }
  .page[hidden] { display: block !important; }
}


/* ==========================================================================
   15. PIE DE PAGINA
   Cierra la pagina repitiendo la marca del header (mismo nombre,
   mismo subtitulo) para que abrir y cerrar el sitio se sientan parte
   del mismo diseno. A diferencia del header -- transparente, flotando
   sobre el reel -- aca no hay video atras: el fondo es negro solido.
   ========================================================================== */

.site-footer {
  border-top: 1px solid var(--c-line);
  padding-block: 1.25rem;
}

.site-footer__inner {
  max-width: var(--maxw-sm);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-4);
  font-size: var(--fs-sm);
  color: var(--c-text-faint);
}

.site-footer__name {
  font-weight: 500;
  color: var(--c-text-muted);
}

@media (max-width: 640px) {
  .site-footer__inner {
    flex-direction: column;
    text-align: center;
  }
}
