/* =============================================================
   PORTAFOLIO INMERSIVO — hoja de estilos única
   1. Tokens · 2. Reset · 3. Utilidades · 4. Botones
   5. Barra lateral + menú · 6. Home · 7. Secciones
   8. Responsive · 9. Reduced-motion
   ============================================================= */

/* =============================================================
   0. TIPOGRAFÍAS
   Las tres se sirven desde el propio sitio, en woff2 y recortadas al
   juego latino + acentos del español. Nada de peticiones a Google: el
   sitio arranca igual sin conexión externa y no hay salto de fuente.
   IMPORTANTE: se conserva U+00AD (guion blando). Es el carácter con el
   que están marcados los cortes de palabra de las fichas; si el recorte
   se lo comiera, la partición dejaría de funcionar.
   ============================================================= */
@font-face {
  font-family: "Europa Grotesk SH";
  src: url("assets/fonts/europa-grotesk-bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Sloop Script Pro";
  src: url("assets/fonts/sloop-script-pro.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face { font-family: "Montserrat"; src: url("assets/fonts/montserrat-300.woff2") format("woff2"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("assets/fonts/montserrat-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("assets/fonts/montserrat-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("assets/fonts/montserrat-500-italic.woff2") format("woff2"); font-weight: 500; font-style: italic; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("assets/fonts/montserrat-600.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Montserrat"; src: url("assets/fonts/montserrat-700.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }

/* =============================================================
   1. TOKENS
   ============================================================= */
:root {
  /* Paleta vinotinto, tomada del SVG «Home demo-burg» */
  --burgundy:  #5B0F18;   /* principal: texto, botones, acciones */
  --cream:     #F8F1E7;   /* claro */
  --espresso:  #1E1E1E;   /* oscuro */
  --rose:      #D8A7A7;   /* complementario claro */
  --wine:      #7A1F2B;   /* complementario: hover y firma */

  --grad-a:      #5B0F18;   /* inicio del degradado del fondo */
  --grad-b:      #F8F1E7;   /* centro */
  --grad-c:      #D8A7A7;   /* final */
  --card:        #EBD5D3;   /* tinte de los recuadros de cristal */

  --bg:        #FAF6F0;
  --bg-deep:   #F2E8DE;
  --surface:   #ffffff;
  --ink:       #1E1E1E;
  --ink-soft:  #4A3A3C;
  --ink-mute:  #8A7476;
  --line:      rgba(30, 30, 30, .12);
  --line-soft: rgba(30, 30, 30, .07);

  /* El degradado del hero, con el ángulo del diseño (24° bajo la horizontal) */
  --grad-hero: linear-gradient(114deg, var(--grad-a) 0%, var(--grad-b) 49%, var(--grad-c) 100%);
  /* bg-light.svg: crema → rose dust. El ángulo sale de su matriz (337°).
     La variante espejo (23°) se alterna entre bloques para que dos
     seguidos no se vean calcados. */
  --grad-light:     linear-gradient(337deg, #F9F0E7 0%, #D39997 100%);
  --grad-light-alt: linear-gradient( 23deg, #F9F0E7 0%, #D39997 100%);
  --grad:      linear-gradient(114deg, var(--grad-a) 0%, #A85260 55%, var(--grad-c) 100%);
  --grad-text: linear-gradient(115deg, #7A1F2B 0%, #5B0F18 55%, #B07079 100%);
  --grad-pill: linear-gradient(115deg, #5B0F18 0%, #7A1F2B 100%);

  /* Titulares en Europa Grotesk, cursivas de titular en Sloop Script,
     texto corrido en Montserrat Medium. La firma del apellido no usa
     tipografía: es un trazado vectorial del diseño. */
  /* REGLA DE MARCA: Europa Grotesk siempre en versales. Todas las reglas
     que usan --display llevan text-transform: uppercase. */
  --display: "Europa Grotesk SH", "Montserrat", system-ui, sans-serif;
  --script:  "Sloop Script Pro", "Georgia", serif;
  --serif:   "Sloop Script Pro", Georgia, serif;
  --sans:    "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono:    "Montserrat", ui-monospace, monospace;

  /* Acento del recorrido: el vino sobre la crema de las fichas, y el
     vinotinto principal para la ficha activa, que va más profunda. */
  --accent:      #7A1F2B;
  --accent-deep: #5B0F18;
  --about-ink:      #1E1E1E;
  --about-ink-soft: #3A2F30;
  --about-ink-mute: #453538;
  --about-top: clamp(1.5rem, 5.5vh, 5.5rem);

  --wrap:    1240px;
  --gutter:  clamp(1.25rem, 4vw, 3rem);
  --side-w:  0px;                       /* espacio reservado a la izquierda */
  --sect-y:  clamp(5rem, 12vh, 9rem);
  --radius:  20px;
  --radius-s: 12px;

  --ease-out:    cubic-bezier(.16, 1, .3, 1);
  --ease-soft:   cubic-bezier(.25, .46, .45, .94);
  --ease-bounce: cubic-bezier(.34, 1.56, .64, 1);

  /* Progreso del hero: 0 = póster a pantalla completa · 1 = todo en el lateral */
  --p: 0;
  /* Nº de letras del logotipo, lo calcula el JS para ajustar el tamaño */
  --mark-len: 9;
}

@property --p { syntax: "<number>"; inherits: true; initial-value: 0; }
/* 0 = el panel está sobre el degradado · 1 = sobre una sección de fondo
   claro. Lo escribe el JS y sirve para virar el logotipo y los textos. */
@property --light { syntax: "<number>"; inherits: true; initial-value: 0; }

/* =============================================================
   2. RESET & BASE
   ============================================================= */
*, *::before, *::after { box-sizing: border-box; margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 2;
  overflow-x: clip;
  scroll-behavior: smooth;
}

body {
  font-family: var(--sans);
  font-weight: 500;              /* Montserrat Medium */
  font-size: 16px;
  line-height: 1.62;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

img, svg, video, iframe { display: block; max-width: 100%; }
img { height: auto; }
button { font: inherit; color: inherit; cursor: pointer; border: 0; background: none; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }
p { text-wrap: pretty; }
h1, h2, h3, h4 { text-wrap: balance; line-height: 1.02; letter-spacing: -.02em; font-weight: 400; }

::selection { background: var(--burgundy); color: #fff; }
:focus-visible { outline: 2px solid var(--burgundy); outline-offset: 3px; border-radius: 6px; }

/* =============================================================
   3. UTILIDADES
   ============================================================= */
.skip-link {
  position: fixed; top: -100px; left: 1rem; z-index: 9999;
  padding: .65rem 1.1rem; background: var(--ink); color: #fff;
  border-radius: 10px; font-weight: 500; font-size: .9rem;
}
.skip-link:focus { top: 1rem; }

/* Texto que solo leen los buscadores y los lectores de pantalla.
   Nada de `display: none` ni `visibility: hidden`: eso sí lo descartan
   los dos. Se saca de pantalla recortándolo, que es la técnica que
   respetan ambos. */
.solo-lectores {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

.wrap { width: min(100% - (var(--gutter) * 2), var(--wrap)); margin-inline: auto; }

.kicker {
  font-family: var(--mono); font-size: .68rem; font-weight: 500;
  letter-spacing: .22em; text-transform: uppercase; color: var(--ink-mute);
}

.reveal { opacity: 0; transform: translateY(28px); transition: opacity .9s var(--ease-out), transform .9s var(--ease-out); }
.reveal.is-in { opacity: 1; transform: none; }
.reveal[data-split] { opacity: 1; transform: none; }

/* Regla de marca: nunca dos familias tipográficas en la misma línea */
.shead__title span, .poster__claim span { display: block; }

/* =============================================================
   4. BOTONES
   ============================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .95rem 1.9rem; border-radius: 999px;
  font-size: .92rem; font-weight: 500;
  transition: transform .5s var(--ease-out), box-shadow .5s var(--ease-out), background-position .8s var(--ease-out);
}
.btn--solid {
  color: #fff; background: var(--grad-pill); background-size: 200% 100%; background-position: 0% 50%;
  box-shadow: 0 10px 28px -12px rgba(91, 15, 24, .65);
}
.btn--solid:hover { background-position: 100% 50%; transform: translateY(-3px); box-shadow: 0 18px 40px -14px rgba(91, 15, 24, .75); }
.btn--ghost { color: var(--ink); border: 1px solid var(--line); background: rgba(255,255,255,.55); }
.btn--ghost:hover { border-color: var(--burgundy); background: #fff; transform: translateY(-3px); }
.btn--full { width: 100%; }

/* =============================================================
   5. BARRA LATERAL + MENÚ
   ============================================================= */
.side {
  position: fixed; z-index: 60;
  left: clamp(.75rem, 1.6vw, 1.4rem);
  top: clamp(.75rem, 1.6vw, 1.4rem);
  width: 232px;
  display: flex; flex-direction: column; gap: .68rem;
  pointer-events: none;
}
.side > * { pointer-events: auto; }

/* Huecos vacíos: reservan el sitio al que viajan el nombre y la ficha del
   hero. No se ven nunca; el JS los mide para saber dónde aterrizar.
   Las proporciones salen del SVG de Sobre Mí: el lockup mide 238 × 100 y
   la ficha 64 × 71 sobre una mesa de 1920. */
.side__slot { position: relative; pointer-events: none; }
.side__slot--mark  { width: 100%; aspect-ratio: 238 / 100; }
/* Las dos tarjetas que se quedan: «5+ años» estrecha y los roles ancha.
   Medidas explícitas para que las dos midan exactamente lo mismo de alto.
   Con `aspect-ratio` dentro de un flex el hueco salía de cero, por eso
   rejilla con columnas y alto fijos. */
.side__row {
  /* Sube 12 px para que el lazo de la «O» y la cola de la «g» rocen el
     borde superior de las tarjetas, como en el diseño. */
  margin-top: -15px;
  display: grid;
  grid-template-columns: 79px 1fr;
  grid-auto-rows: 88px;
  gap: .5rem;
}
.side__slot--badge,
.side__slot--roles { width: 100%; height: 100%; }


/* ---- El menú: mismo elemento en las dos posiciones ---- */
.side__nav {
  display: flex; flex-direction: column;
  /* Cada píldora mide lo que mide su texto. Si se estiraran al ancho
     del panel, en la fila horizontal del hero se pisarían entre sí. */
  align-items: flex-start;
  gap: calc(.1rem + (.14rem * var(--p)));
  margin-block: .15rem;
}
.nav__item {
  position: relative;
  display: flex; align-items: center;
  gap: calc(.55rem * var(--p));
  padding: .42rem calc(.5rem + (.6rem * var(--p))) .42rem calc(.3rem + (.45rem * var(--p)));
  /* El menú va en Montserrat Medium, no en Europa. */
  font-family: var(--sans); font-weight: 500;
  font-size: .8rem; letter-spacing: .04em; text-transform: uppercase;
  white-space: nowrap; color: var(--burgundy);
  border-radius: 999px;
  transform-origin: left center;
  /* El JS escribe --dx / --dy / --s con la posición de partida en el hero.
     Traslación 2D y sin `will-change`: con `translate3d` + capa de GPU el
     texto pierde el suavizado subpíxel y «Contacto» se veía más difuso y
     más saturado que «Sobre mí». El JS redondea a píxeles enteros. */
  transform: translate(var(--dx, 0px), var(--dy, 0px)) scale(var(--s, 1));
  transition: color .4s var(--ease-out);
}
.nav__pill {
  position: absolute; inset: 0; border-radius: 999px;
  background: rgba(255,255,255,.66);
  border: 1px solid var(--line-soft);
  box-shadow: 0 6px 18px -12px rgba(30,30,30,.4);
  opacity: 0; transform: scale(.6); transform-origin: left center;
  transition: opacity .45s var(--ease-out), transform .5s var(--ease-bounce), background .4s var(--ease-out), border-color .4s var(--ease-out);
}
.side.is-open .nav__pill { opacity: 1; transform: scale(1); }

.nav__icon {
  position: relative; flex: 0 0 auto; overflow: hidden;
  width: calc(1.05em * var(--p)); height: 1.05em;
  opacity: 0; transform: scale(.3);
  transition: opacity .4s var(--ease-out) .05s, transform .45s var(--ease-bounce) .05s;
}
.nav__icon svg { width: 1.05em; height: 100%; }
.side.is-open .nav__icon { opacity: 1; transform: scale(1); }
.nav__text { position: relative; }

/* «Contacto» es el botón del hero: viaja hasta el panel y allí se vuelve
   píldora. Por eso su fondo ya está visible desde el principio. */
.nav__item--cta {
  color: #fff;
  justify-content: center;
  /* En el hero es exactamente el mismo botón que «Sobre mí» (mismas medidas
     que tenía el botón del hero); al acoplarse recupera el de las demás
     píldoras. --cta-dock-h lo escribe el JS midiendo un ítem normal. */
  font-size: calc(clamp(.7rem, 1.11vw, 1.15rem) * (1 - var(--p)) + .8rem * var(--p));
  min-width: calc(10.16vw * (1 - var(--p)));
  height: calc(max(7vh, 44px) * (1 - var(--p)) + var(--cta-dock-h, 29px) * var(--p));
  padding-block: 0;
  padding-inline: calc(1.4vw * (1 - var(--p)) + .9rem * var(--p));
}
.nav__item--cta .nav__pill {
  opacity: 1; transform: scale(1);
  /* De esquina de botón a píldora redonda a medida que viaja */
  border-radius: calc(.55vw * (1 - var(--p)) + 200px * var(--p));
  background: var(--grad-pill); background-size: 200% 100%; background-position: 0% 50%;
  /* Sin borde. El borde de 1 px del resto de píldoras metía el degradado en
     la caja de padding, que es 1,6 px más estrecha, y el mismo degradado
     salía con otro tono que el de «Sobre mí». El filete del panel lateral
     se hace con una sombra interior, que no toca la geometría. */
  border: 0;
  box-shadow: 0 10px 28px -12px rgba(91, 15, 24, .65);
  transition: opacity .45s var(--ease-out), transform .5s var(--ease-bounce),
              background .4s var(--ease-out), border-color .4s var(--ease-out),
              background-position .8s var(--ease-out), box-shadow .5s var(--ease-out);
}
.nav__item--cta::after { content: none; }

/* Hover idéntico al de `.btn--solid`: el degradado se desplaza y el botón
   sube 3 px. Como el JS conduce el `transform` del ítem con el scroll, el
   levantamiento va en la píldora y el texto, no en el ítem. */
.side:not(.is-open) .nav__item--cta .nav__text { transition: transform .5s var(--ease-out); }
.side:not(.is-open) .nav__item--cta:hover .nav__pill {
  background-position: 100% 50%;
  transform: scale(1) translateY(-3px);
  box-shadow: 0 18px 40px -14px rgba(91, 15, 24, .75);
}
.side:not(.is-open) .nav__item--cta:hover .nav__text { transform: translateY(-3px); }
.side.is-open .nav__item--cta { color: var(--burgundy); }
.side.is-open .nav__item--cta .nav__pill {
  background: rgba(255, 255, 255, .66);
  box-shadow: inset 0 0 0 1px var(--line-soft), 0 6px 18px -12px rgba(30, 30, 30, .4);
}


/* Separador «|» del diseño. Solo se ve con el menú en el hero. */
.nav__item::after {
  content: "|";
  /* --sep-x lo calcula el JS: la mitad del hueco, para que quede centrado */
  position: absolute; right: calc(-1 * var(--sep-x, .62em) / var(--s, 1)); top: 50%;
  /* --sep-o lo escribe el JS: 1 en el home, 0 en cuanto se da el primer
     golpe de rueda. Se van antes que nada, apagándose y subiendo un poco. */
  transform: translateY(calc(-50% - 9px * (1 - var(--sep-o, 1))));
  opacity: var(--sep-o, 1);
  color: var(--burgundy);
  font-weight: 400;
  pointer-events: none;
}
/* El 3º es el último de la fila del hero (Contacto va aparte como botón),
   así que ninguno de los dos lleva separador. */
.nav__item:nth-child(3)::after,
.nav__item:last-child::after { content: none; }

/* La entrada seleccionada, en Montserrat Medium Italic */
.nav__item.is-active { color: var(--wine); font-style: italic; }
.side.is-open .nav__item.is-active { color: #fff; }
.side.is-open .nav__item.is-active .nav__pill {
  background: var(--grad-pill); border-color: transparent;
  box-shadow: 0 10px 26px -12px rgba(91, 15, 24, .7);
}
.side.is-open .nav__item:not(.is-active):hover .nav__pill { background: #fff; border-color: var(--burgundy); }
/* «Contacto» no tiene borde, así que su filete de hover también es sombra */
.side.is-open .nav__item--cta:not(.is-active):hover .nav__pill {
  box-shadow: inset 0 0 0 1px var(--burgundy), 0 6px 18px -12px rgba(30, 30, 30, .4);
}

/* =============================================================
   6. HOME
   Todas las medidas salen del SVG del diseño (mesa 1920 × 869).
   Se expresan en % del escenario para que escalen con la pantalla.
   ============================================================= */
/* 200vh = 100vh de recorrido para el morfeo, el doble que antes: la
   transición al «Sobre mí» pasa de ~3 a ~6 golpes de rueda. */
.hero { position: relative; height: 200vh; }

/* Cuatro capas con orden explícito. El diseño pide
   degradado → SYLETH → retrato → botones, y el nombre tiene que quedar
   por detrás del retrato aunque después viaje al panel lateral. */
/* Las tres capas pegajosas ocupan el mismo sitio, no una debajo de otra:
   el margen negativo les quita la altura que aportan al flujo. */
.hero__layer { position: sticky; top: 0; height: 100vh; height: 100svh; }
.hero__bg, .hero__shot { margin-bottom: -100vh; margin-bottom: -100svh; }
/* Sin fondo propio: el degradado es una sola capa fija detrás de toda la
   página (ver «FONDO ÚNICO»). Si cada sección pintaba el suyo, en la unión
   entre el home y «Sobre mí» se veía la costura. */
.hero__bg    { z-index: 1; overflow: hidden; }
.hero__shot  { z-index: 4; overflow: hidden; pointer-events: none; }
.hero__front { z-index: 6; overflow: hidden; }

/* Esta va FIJA, no pegajosa: el lockup y la ficha siguen en pantalla
   cuando el hero ya se fue, porque en las demás secciones son el panel. */
.hero__mark {
  position: fixed; inset: 0; height: auto;
  z-index: 3; pointer-events: none;
}
/* Igual de fija, pero por delante del retrato: la tarjeta de roles va
   sobre el brazo, no por detrás. */
.hero__cards {
  position: fixed; inset: 0; height: auto;
  z-index: 5; pointer-events: none;
}
.hero__mark > *, .hero__cards > * { pointer-events: auto; }

/* ---- FONDO ÚNICO ----
   Una sola capa fija con el degradado, el brillo y el grano, detrás de
   toda la página. El home y «Sobre mí» comparten exactamente el mismo
   pixel de fondo, así que no hay línea de corte donde uno acaba y el
   otro empieza. Las secciones de abajo llevan fondo propio opaco y la
   tapan sin más. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(38% 46% at 28% 34%, rgba(255,255,255,.5), transparent 70%),
    var(--grad-hero);
}
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1;
  pointer-events: none; opacity: .13;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* El .grain del hero se queda sin trabajo: el grano ahora es global */
.grain { display: none; }

/* ---- Las dos capas del póster se encogen juntas ---- */
.poster { position: absolute; inset: 0; }
.poster > * { position: absolute; }

/* Piezas que viajan al panel lateral: se transforman por su cuenta.
   El origen arriba-izquierda hace que al escalar quede fijo ese vértice,
   que es lo que permite interpolar posición y tamaño con una sola cuenta. */
[data-morph] {
  position: absolute;
  transform-origin: top left;
  will-change: transform;
}

/* ---- El nombre: «SYLETH» + «Ortega» encima ----
   El JS calcula --mark-fs midiendo el texto real. Todo lo demás va en em,
   así el conjunto se mueve como una sola pieza con cualquier nombre. */
.mark {
  left: 8.9%; top: 9.9%;   /* posición en el hero; el JS la interpola hacia el panel */
  font-size: var(--mark-fs, 14.3vw);
  line-height: 1;
}
.mark__name {
  display: flex;
  font-family: var(--display); font-weight: 900;
  font-size: 1em; line-height: .86;
  letter-spacing: -.015em;
  /* Blanco sobre el degradado, espresso al pasar sobre el fondo claro */
  color: color-mix(in oklab, var(--espresso) calc(var(--light) * 100%), #fff);
  /* La sombra del diseño: #1E1E1E al 6 %, desplazada a 315° y casi sin
     radio. Muy leve a propósito: da cuerpo sin ensuciar el blanco. */
  text-shadow: .045em .045em .05em rgba(30, 30, 30, .11);   /* espresso */
  text-transform: uppercase;
}
/* Orden de capas del logotipo: la firma pasa POR DETRÁS de la «S» y la
   «Y», y por delante de «LETH». Así el lazo de la «O», al dibujarse,
   se mete detrás de las dos primeras letras.
      SY      z-index 3
      Ortega  z-index 2
      LETH    z-index 1
   Los `span` son elementos flex, así que aceptan z-index sin más, y el
   contexto de apilado lo pone `.mark` (tiene transform). */
.mark__name span { display: block; position: relative; z-index: 1; }
.mark__name span:nth-child(1),
.mark__name span:nth-child(2) { z-index: 3; }
/* La firma. La tipografía es lo que se ve; el trazo dibujado a mano solo
   hace de máscara para revelarla. Tamaño y posición los calcula el JS
   midiendo, para que encaje con la caja de «SYLETH». */
.sign {
  position: absolute; left: 0; top: 0; z-index: 2;
  width: 40vw; height: auto;
  overflow: visible;
  /* Sin el tope global de `img, svg { max-width: 100% }`.
     El encaje de la firma se hace midiendo: se estira el SVG a 1000 px,
     se mide cuánto ocupa la tinta y de ahí sale la escala. Con el tope
     puesto, esa sonda no llegaba a 1000 sino al ancho del contenedor, la
     regla de tres salía mal y la firma acababa un 27 % más grande que
     «SYLETH». En escritorio no se notaba porque su contenedor es ancho y
     el tope nunca llegaba a morder. */
  max-width: none;
}
/* La firma viene de Affinity: necesita evenodd para que los
   contraformas de las letras salgan huecas y no rellenas. */
/* La firma cambia de color con el scroll: vinotinto mientras está sobre la
   parte clara del hero, rosa cuando aterriza en el panel, que vive sobre el
   vinotinto oscuro. Va atada a --p, así que es continua, suave y funciona
   igual bajando que subiendo. El `fill` de antes queda de respaldo para
   navegadores sin color-mix. */
.sign__art { fill: var(--wine); fill-rule: evenodd; }
@supports (color: color-mix(in oklab, red, blue)) {
  .sign__art {
    fill: color-mix(in oklab, var(--burgundy) calc(var(--light) * 100%),
            color-mix(in oklab, var(--rose) calc(var(--p) * 100%), var(--wine)));
  }
}

/* ---- El retrato ---- */
/* Margen del diseño: el recorte tiene aire a los lados, así que la silueta
   entra entera. Solo apoya en el borde inferior, que es el pie de pantalla. */
.shot {
  position: absolute; z-index: 2;
  right: 2.55%; bottom: 0;
  height: 96.9%;
  filter: blur(var(--shot-blur, 0px));
  opacity: var(--shot-o, 1);
  will-change: filter, opacity;
}
.shot__in { height: 100%; }
.shot__img { height: 100%; width: auto; object-fit: contain; object-position: bottom center; }

/* ---- Recuadros de cristal ---- */
.glass {
  /* Respaldo para navegadores sin backdrop-filter: ahí sí hace falta
     algo de cuerpo, porque no van a poder difuminar nada. */
  background: rgba(216, 167, 167, .30);
  border: 1px solid rgba(255, 255, 255, .42);
  border-radius: clamp(10px, 1.1vw, 22px);
  box-shadow:
    0 1.2vw 3vw -1.4vw rgba(91, 15, 24, .26),
    inset 0 1px 0 rgba(255, 255, 255, .6);
}
@supports (backdrop-filter: blur(10px)) {
  .glass {
    /* Casi nada de relleno: lo que da el cuerpo es el desenfoque.
       Se tiene que ver lo de atrás, solo que difuso. */
    background: rgba(255, 255, 255, .10);
    backdrop-filter: blur(16px) saturate(145%);
    -webkit-backdrop-filter: blur(16px) saturate(145%);
  }
  /* Las dos iguales, rose dust al 30 %: el color del archivo pero sin
     tapar tanto lo que hay detrás. */
  .badge, .roles { background: rgba(216, 167, 167, .30); }
}

/* Ficha «5+ años de experiencia» — arriba a la izquierda, como el diseño */
.badge {
  left: 4.4%; top: 38.3%;
  /* Proporción fija (la del diseño). Si el alto dependiera de la ventana,
     la ficha cambiaría de forma y al escalarla no encajaría en su hueco. */
  width: 9.5%; min-width: 132px; aspect-ratio: 64 / 71;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  padding: 0 .8vw;
  text-align: center;
}
.badge strong {
  font-family: var(--display); font-weight: 900;
  font-size: clamp(2.4rem, 3.9vw, 4.4rem); line-height: 1;
  color: var(--burgundy);
  text-transform: uppercase;
}
.badge span {
  margin-top: .5em;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(.62rem, .95vw, 1rem); line-height: 1.25;
  letter-spacing: .01em; text-transform: uppercase;
  color: color-mix(in oklab, var(--espresso) calc(var(--light) * 100%), #fff);
  text-shadow: 0 1px 2px rgba(30, 30, 30, .10), 0 3px 18px rgba(30, 30, 30, .16);
}

/* Lista de habilidades */
/* Lista de habilidades — centro-derecha. Su borde inferior queda a la
   misma altura que el de los botones (86.8% + 7% = 93.8%). */

/* «Portafolio» */
/* Tarjeta de roles. Su proporción es la misma que la del hueco del panel
   (117 × 70), así que al escalarla por el ancho el alto encaja solo. */
.roles {
  left: 47.5%; top: 64%;
  width: 19%; min-width: 250px;
  aspect-ratio: 117 / 70;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  gap: .5em; padding: 0 1.2vw;
  text-align: center;
  font-family: var(--display); font-weight: 700;
  font-size: clamp(.62rem, 1.12vw, 1.24rem);
  letter-spacing: .01em; text-transform: uppercase;
  /* Vinotinto en el hero, donde la tarjeta cae sobre la mitad clara del
     degradado, y blanco al aterrizar en el panel, que está sobre la parte
     oscura. La mezcla la conduce --p, igual que la firma. */
  color: var(--burgundy);
}
@supports (color: color-mix(in oklab, red, blue)) {
  .roles {
    color: color-mix(in oklab, var(--espresso) calc(var(--light) * 100%),
             color-mix(in oklab, #fff calc(var(--p) * 100%), var(--burgundy)));
  }
}
.roles li {
  line-height: 1.15; white-space: nowrap;
  text-shadow: 0 1px 2px color-mix(in oklab, rgba(30, 30, 30, .5) calc(var(--p) * 100%), rgba(248, 241, 231, .55));
}

.word {
  left: 6.35%; top: 74.5%;
  font-family: var(--display); font-weight: 900;
  font-size: clamp(2rem, 5.25vw, 6.3rem); line-height: 1;
  letter-spacing: -.01em; text-transform: uppercase;
  color: var(--burgundy);
}


/* ---- INTRO: la entrada escalonada al cargar ----
   Se esconde solo si hay JS (la clase .js la pone un script del <head>). */
.js .mark__name span, .js .sign,
.js .badge, .js .roles, .js .word,
.js .shot__in, .js .side__nav .nav__item {
  opacity: 0;
  /* Red de seguridad: si el JS falla y nunca llega .is-lit, a los 4 s
     aparece todo igual. Las reglas .is-lit de abajo la reemplazan.
     Nombre propio (`safetyIn`, no `fadeIn`) a propósito: si coincidiera con
     el de la regla .is-lit, el navegador no reiniciaría la animación al
     encenderse la intro, solo le cambiaría los tiempos, y el elemento
     entraría a destiempo.
     Y `forwards`, no `both`: con `both` el fotograma inicial (opacidad 0)
     mandaría durante los 4 s de espera y taparía la firma, que se enciende
     por JS. */
  animation: safetyIn .01s linear 4s forwards;
}
/* Excepción: estas dos son las únicas que llevan `opacity` en línea escrita
   por el morfeo desde el primer frame. Con `forwards` esa opacidad manda
   durante la espera y se quedaban visibles hasta que llegaba .is-lit (que
   espera a las tipografías, y en un Ctrl+R con revalidación eso se va a más
   de un segundo). Con `both` manda el fotograma inicial: invisibles. */
.js .word { animation: safetyIn .01s linear 4s both; }

.is-lit .mark__name span { animation: markIn .9s var(--ease-out) forwards; }
.is-lit .mark__name span:nth-child(1) { animation-delay: .05s }
.is-lit .mark__name span:nth-child(2) { animation-delay: .11s }
.is-lit .mark__name span:nth-child(3) { animation-delay: .17s }
.is-lit .mark__name span:nth-child(4) { animation-delay: .23s }
.is-lit .mark__name span:nth-child(5) { animation-delay: .29s }
.is-lit .mark__name span:nth-child(6) { animation-delay: .35s }
.is-lit .mark__name span:nth-child(7) { animation-delay: .41s }
.is-lit .mark__name span:nth-child(8) { animation-delay: .47s }
.is-lit .mark__name span:nth-child(n+9) { animation-delay: .53s }

.is-lit .shot__in              { animation: shotIn 1.1s var(--ease-out) .95s forwards; }
/* Segunda tanda: fichas, «Portafolio», menú y los dos botones entran juntos,
   en una ventana de apenas 0,36 s. */
.is-lit .side__nav .nav__item  { animation: fadeIn .8s var(--ease-out) both; }
.is-lit .side__nav .nav__item:nth-child(1) { animation-delay: 1.40s }
.is-lit .side__nav .nav__item:nth-child(2) { animation-delay: 1.46s }
.is-lit .side__nav .nav__item:nth-child(3) { animation-delay: 1.52s }
/* «Contacto» es el gemelo de «Sobre mí»: entra con él, no al final. */
.is-lit .side__nav .nav__item:nth-child(4) { animation-delay: 1.34s }
.is-lit .badge       { animation: softIn .85s var(--ease-out) 1.28s both; }
.is-lit .roles       { animation: softIn .85s var(--ease-out) 1.34s both; }
.is-lit .word        { animation: softIn .85s var(--ease-out) 1.28s both; }

@keyframes markIn   { from { opacity: 0; transform: translateY(18%); } to { opacity: 1; transform: none; } }
@keyframes shotIn   { from { opacity: 0; filter: blur(24px); transform: scale(1.05); } to { opacity: 1; filter: blur(0); transform: none; } }
@keyframes softIn   { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes fadeIn   { from { opacity: 0; } to { opacity: 1; } }
@keyframes safetyIn { from { opacity: 0; } to { opacity: 1; } }

/* =============================================================
   7. SECCIONES
   ============================================================= */
/* z-index 2: por encima del fondo del hero (1) para poder subir mientras
   este todavía está pegado, y por debajo del nombre (3), que se queda. */
.section {
  position: relative; z-index: 2;
  padding-block: var(--sect-y); padding-left: var(--side-w);
  scroll-margin-top: 10px;
}
/* Sin fondo propio: hereda el del `body`, que es el mismo del home. Así el
   panel lateral sigue viviendo sobre la parte violeta (que es lo que hace
   legible el nombre en blanco y las tarjetas de cristal) y no aparece
   ninguna costura donde acaba el home. */
.section--about {
  /* Se solapa con el final del hero: así empieza a asomar a mitad del
     recorrido, mientras el morfeo todavía se está resolviendo. Al alargar
     el hero a 200vh este solape sube de 25vh a 50vh para mantener la
     proporción. */
  margin-top: -50vh;
  /* Al pulsar «Sobre mí» hay que aterrizar donde la presentación termina
     de subir (el golpe 8 de rueda), no en el borde de la sección: ese
     borde queda 16,7vh más abajo y el título ya se fue de pantalla.
     150vh (borde) − 133,3vh (golpe 8) = 16,7vh. */
  scroll-margin-top: 16.7vh;
  padding-top: clamp(2.5rem, 6vh, 4.5rem);
}
.section--work    { background: var(--cream); }
.section--skills  { background: var(--bg); }
.section--clients { background: var(--grad-light); overflow: hidden; }
/* Sin fondo propio, igual que «Sobre mí»: deja ver la capa fija del
   `body`, que es EL MISMO degradado del home, no una copia. Así el cierre
   de la página vuelve al color con el que abre, y como la capa es única no
   hay costura ni un segundo degradado que cuadrar. */
.section--contact { background: none; overflow: hidden; }

.shead { max-width: 46rem; margin-bottom: clamp(3rem, 7vh, 5.5rem); }
.shead--wide { max-width: none; }
.shead--centro { max-width: none; text-align: center; margin-bottom: clamp(2.5rem, 7vh, 5rem); }

.shead--centro .shead__l1 { font-size: clamp(2.4rem, 5.2vw, 4.6rem); }
.shead--centro .shead__l2 { font-size: clamp(3.2rem, 7vw, 6.2rem); }
/* «¿Tienes una idea?» en blanco. Va sobre el degradado del home, así que
   lleva la misma sombra tenue que el nombre del hero: espresso a muy poca
   opacidad y con radio, para que le dé cuerpo sobre la parte clara sin
   ensuciar el blanco. Solo en contacto: el mismo `.shead__l1` compone
   «IDEAS LLEVADAS» sobre crema, y ahí tiene que seguir en espresso. */
.section--contact .shead__l1 {
  color: #fff;
  text-shadow: 0 1px 2px rgba(30, 30, 30, .18),
               0 4px 22px rgba(30, 30, 30, .28);
}
/* ---- LOCKUP DE TITULAR ----
   Regla de marca, igual en todas las secciones: arriba Europa en
   versales, abajo Sloop. Nunca las dos en la misma línea. El solape va en
   `em`, así que se mantiene proporcional a cualquier tamaño. */
.shead__title { margin-top: 0; line-height: .95; }
.shead__l1 { font-size: clamp(2rem, 4.4vw, 3.9rem); }
.shead__l2 { font-size: clamp(2.7rem, 5.95vw, 5.27rem); }
.shead__lede { margin-top: 1.6rem; font-size: clamp(1rem, 1.5vw, 1.15rem); color: var(--ink-soft); max-width: 36rem; }

/* ---- SOBRE MÍ: presentación + recorrido ----
   Dos columnas que no se tocan: la izquierda es texto suelto sobre el
   degradado (nada de tarjeta) y la derecha es todo el recorrido. */
/* El recorrido ocupa el ancho entero. La presentación no le quita carril:
   flota arriba a la izquierda y se va con el scroll, así que las únicas
   fichas que tienen que esquivarla son las primeras, y para eso el
   recorrido arranca por el carril derecho. */
.about {
  position: relative; z-index: 1;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* La columna izquierda es solo el carril; quien se queda pegado es el
   bloque de dentro, y su contenedor es esta columna, así que se suelta
   solo cuando el recorrido termina. */
.about__col {
  position: absolute; z-index: 1;
  top: 0; left: var(--gutter);
  width: min(40%, 30rem);
}

/* La ruta cubre las dos columnas y vive por debajo de todo */
.about__path {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  overflow: visible; pointer-events: none;
}
/* No se queda pegado: sube hasta su sitio con el scroll y desde ahí se va
   con la página, dejando la columna libre para el recorrido. La entrada la
   conduce el JS (--about-y / --about-o). */
.about__intro {
  position: relative;
  padding-block: 0 clamp(3rem, 8vh, 6rem);
  transform: translateY(var(--about-y, 0px));
  opacity: var(--about-o, 1);
}

/* Dos renglones, dos tipografías: nunca en la misma línea.
   Arriba el grotesco en versales, abajo el script. El script se solapa
   un poco hacia arriba y se sangra a la derecha, como en el diseño. */
.about__title { margin-top: 0; line-height: .95; }
.about__title span { display: block; }
.about__title-a, .shead__l1 {
  display: block;
  font-family: var(--display); font-weight: 700;
  letter-spacing: normal;          /* el interletrado propio de Europa */
  text-transform: uppercase;
  color: var(--ink);
}
.about__title-a { font-size: clamp(2.5rem, 4.9vw, 4.5rem); }
.about__title-b, .shead__l2 {
  display: block;
  font-family: var(--script); font-weight: 700; font-style: normal;
  line-height: 1.1;
  letter-spacing: 0;
  /* Medido: con -.18em quedaban 11 px de aire entre el pie de las
     versales y el alto de la «d» y la «l». Con -.52em se solapan unos
     12 px: los astiles entran en las versales, como en la maqueta. */
  /* El solape va en `em`, o sea proporcional al propio script. */
  margin-top: -.45em; margin-left: .04em;
  color: var(--burgundy);
}
.about__title-b { font-size: clamp(3.37rem, 6.6vw, 6.07rem); }

/* Justificados con partición de palabras: sin guiones, un texto justificado
   a este ancho abre ríos de espacio entre palabras. */
.about__lede, .about__note {
  text-align: justify;
  text-justify: inter-word;
  /* Justificado pero sin partir palabras */
  hyphens: none;
  -webkit-hyphens: none;
  overflow-wrap: normal;
}
.about__lede {
  margin-top: 1.6rem;
  max-width: 28rem;                 /* ~9 palabras por línea */
  font-size: clamp(.98rem, 1.15vw, 1.12rem);
  line-height: 1.65;
  color: var(--about-ink-soft, #3A2F30);
}
.about__note {
  margin-top: 1.3rem;
  font-style: italic;              /* Montserrat Medium Italic */
  max-width: 26rem;
  font-size: clamp(.84rem, .95vw, .95rem);
  line-height: 1.6;
  color: var(--about-ink-mute, #5C4B4D);
}

/* ---- El recorrido ---- */
.journey { position: relative; z-index: 1; padding-block: clamp(1rem, 4vh, 3rem) clamp(4rem, 12vh, 9rem); }
.journey__list { position: relative; z-index: 1; list-style: none; padding: 0; margin: 0; }
/* Las paradas del recorrido: una por ficha, en la esquina por la que la
   línea sale hacia la siguiente. Se encienden cuando el trazo llega. */
.journey__stop {
  fill: var(--accent, #7A1F2B);
  stroke: #F8F1E7; stroke-width: 2.5;
  opacity: 0; transform: scale(.4); transform-box: fill-box; transform-origin: center;
  transition: opacity .35s var(--ease-out), transform .45s var(--ease-bounce);
}
.journey__stop.is-on { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
  .journey__stop { opacity: 1; transform: none; transition: none; }
}
/* El color lo pone el degradado del SVG (vinotinto → vino → vinotinto).
   El glow es un `drop-shadow`, no un `box-shadow`: sobre un trazo hay que
   ir por el filtro, y así el resplandor sigue la curva en vez de la caja. */
.journey__line {
  stroke-width: 2.6;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: .95;
  /* El glow tiene que ser MÁS CLARO que el fondo; en vinotinto oscuro se
     leía como sombra. Halo cálido pegado al trazo y otro más abierto. */
  filter: drop-shadow(0 0 3px rgba(255, 246, 242, .95))
          drop-shadow(0 0 10px rgba(255, 232, 228, .7))
          drop-shadow(0 0 22px rgba(216, 167, 167, .5));
}

/* Cada ficha ocupa ~74 % del carril y se va alternando. El `--nudge`
   mete una variación de ±18 px para que el recorrido no quede simétrico. */
/* Tres carriles sobre el ancho completo: derecha, centro e izquierda. La
   secuencia baja en cascada (der → centro → izq) y vuelve a empezar, que
   es lo que le da al recorrido su diagonal. */
/* Tres carriles que NO se pisan de ancho: 32 % cada uno, pegados a la
   izquierda, al centro y a la derecha. Al no solaparse horizontalmente,
   cada ficha puede subir y meterse en la banda de la anterior, y la
   cascada queda apretada en vez de dejar huecos en diagonal. */
.jcard {
  --w: clamp(20rem, 32.6%, 28rem);
  position: relative;
  width: var(--w);
}
/* Todas se meten igual en la banda de la anterior. Como la secuencia es
   una serpentina (cada ficha se mueve un solo carril), todos los pasos
   miden lo mismo y el ritmo no se rompe en ningún punto. */
.jcard + .jcard { margin-top: calc(-1 * clamp(2rem, 6vh, 4.5rem)); }
.jcard[data-lane="right"]  { margin-left: auto; }
.jcard[data-lane="center"] { margin-inline: auto; }
.jcard[data-lane="left"]   { margin-right: auto; }

.jcard__in {
  /* Mismo cristal que las tarjetas del home: respaldo con cuerpo para
     navegadores sin backdrop-filter, y casi sin relleno cuando sí lo hay. */
  background: rgba(248, 241, 231, .62);      /* respaldo sin backdrop-filter */
  border: 1px solid rgba(255, 255, 255, .5);
  border-radius: 24px;
  padding: clamp(1.15rem, 1.4vw, 1.7rem);
  box-shadow: 0 24px 50px -34px rgba(30, 30, 30, .45),
              inset 0 1px 0 rgba(255, 255, 255, .55);
  /* Sin `transform` ni `opacity` en la transición: los conduce el scroll
     con --near, que ya viene suavizado. Meter aquí una transición encima
     solo añadiría retardo. Solo se transicionan los cambios de estado. */
  transition: box-shadow .5s var(--ease-out),
              border-color .5s var(--ease-out), background .5s var(--ease-out);
}

@supports (backdrop-filter: blur(10px)) {
  /* Cristal al 30 %: lo que da cuerpo es el desenfoque, no el relleno.
     Se tiene que ver la línea del recorrido por detrás, difusa. */
  .jcard__in {
    background: rgba(248, 241, 231, .30);
    backdrop-filter: blur(20px) saturate(155%);
    -webkit-backdrop-filter: blur(20px) saturate(155%);
  }
}

.jcard__mark {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  margin-bottom: clamp(1.1rem, 2vh, 1.7rem);
  font-family: var(--display);
  color: var(--accent, #7A1F2B);
  transition: color .5s var(--ease-out);
  text-transform: uppercase;
}
.jcard__mark strong {
  font-weight: 800; font-size: clamp(2.6rem, 4.4vw, 4rem);
  line-height: .85; letter-spacing: -.04em;
}
.jcard__mark span {
  font-weight: 700; font-size: clamp(.64rem, .82vw, .8rem);
  letter-spacing: .1em; text-transform: uppercase;
  opacity: .82;
}
/* Los rótulos largos («años», «años de experiencia») se apilan bajo la cifra */
.jcard__mark--long { flex-direction: column; align-items: flex-start; gap: .35rem; }

.jcard__title {
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.02rem, 1.45vw, 1.32rem);
  line-height: 1.25; letter-spacing: .005em;
  color: #1E1E1E;
  margin-bottom: .85rem;
  text-transform: uppercase;
}
.jcard__text {
  font-size: clamp(.82rem, .945vw, .88rem);
  line-height: 1.75;
  color: #4A3A3C;
  /* Sin tope de medida: justificado, cuanto más ancha la línea, menos se
     abren los espacios. Manda el padding de la ficha. */
  text-align: justify;
  text-justify: inter-word;
  /* Nunca se parten palabras, en ningún sitio del sitio. */
  hyphens: none;
  -webkit-hyphens: none;
}
/* Cortes de línea puestos a mano en cuatro fichas, donde el justificado
   abría demasiado los espacios. Solo valen si la columna da de sí: por
   debajo de 1320 px se ocultan y el texto vuelve a romper solo. */
.jcard__text br { display: none; }
@media (min-width: 1320px) { .jcard__text br { display: inline; } }
.jcard__coda {
  margin-top: 1.4rem; padding-top: 1.2rem;
  border-top: 1px solid rgba(30, 30, 30, .12);
  font-family: var(--display); font-weight: 700;
  font-size: clamp(.82rem, .95vw, .94rem);
  line-height: 1.45; letter-spacing: -.01em;
  color: var(--accent, #7A1F2B);
  text-align: justify; text-justify: inter-word; hyphens: none; -webkit-hyphens: none;
  text-transform: uppercase;
}

/* ---- LA FICHA RESPIRA CON EL SCROLL ----
   --near lo escribe el JS: 0 lejos del centro de la pantalla, 1 encima.
   No es un disparo de una vez, es continuo: la ficha se levanta, gana
   cuerpo y sombra al acercarse, y los pierde al alejarse. */
.jcard__in {
  transform: translateY(calc(34px * (1 - var(--near, 1))))
             scale(calc(.94 + .06 * var(--near, 1)));
  opacity: calc(.42 + .58 * var(--near, 1));
}

/* La que cae en el centro del viewport. Ya no toca ni el `transform` ni
   el cuerpo del cristal: de eso se encarga --near. Solo gana filete y
   sombra, para que se note sin dejar de ser cristal. */
.jcard.is-active .jcard__in {
  background: rgba(253, 249, 243, .38);
  border-color: rgba(255, 255, 255, .78);
  box-shadow: 0 34px 62px -32px rgba(30, 30, 30, .5),
              inset 0 1px 0 rgba(255, 255, 255, .7);
}
.jcard.is-active .jcard__mark { color: var(--accent-deep, #5B0F18); }

/* ---- CONTACTO: tres vías directas, sin formulario ----
   Píldoras de cristal, el mismo lenguaje que el panel del home. */
.vias {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(.9rem, 2.4vw, 2.4rem);
  list-style: none; padding: 0; margin: 0;
}
.via {
  display: inline-flex; align-items: center; gap: .7rem;
  padding: .85rem 1.6rem; border-radius: 999px;
  background: rgba(255, 255, 255, .5);
  border: 1px solid rgba(255, 255, 255, .6);
  box-shadow: 0 14px 34px -22px rgba(30, 30, 30, .5),
              inset 0 1px 0 rgba(255, 255, 255, .6);
  font-family: var(--sans); font-weight: 600;
  font-size: clamp(.92rem, 1.15vw, 1.1rem);
  color: var(--burgundy);
  transition: transform .5s var(--ease-out), box-shadow .5s var(--ease-out),
              background .4s var(--ease-out), color .4s var(--ease-out);
}
@supports (backdrop-filter: blur(10px)) {
  .via { background: rgba(255, 255, 255, .24);
         backdrop-filter: blur(16px) saturate(150%);
         -webkit-backdrop-filter: blur(16px) saturate(150%); }
}
.via__ico { flex: 0 0 auto; display: grid; place-items: center; }
.via__ico svg { width: 1.35em; height: 1.35em; }
.via:hover {
  transform: translateY(-3px);
  background: var(--burgundy); border-color: var(--burgundy); color: var(--cream);
  box-shadow: 0 20px 42px -20px rgba(91, 15, 24, .6);
}

/* ---- CLIENTES DEL PORTAFOLIO ----
   Cada marca es una secuencia ANCLADA: mientras dura, la página no
   avanza. El scroll conduce dos cosas a la vez, con --q de 0 a 1:
   el logotipo sube y se encoge hasta ponerse en línea con el rol, y los
   teléfonos entran desde la derecha escalonados. Al llegar a 1, el pin
   se suelta y el scroll continúa hacia la siguiente marca. */
.client { position: relative; height: 175vh; }
/* Fondo de borde a borde. --sangra lo escribe el JS: es lo que hay desde
   el borde izquierdo del bloque hasta el de la pantalla. Un `100vw`
   centrado no vale, porque la sección lleva el hueco del panel lateral y
   el bloque no está centrado en la pantalla. */
.client::before {
  content: ""; position: absolute; z-index: -1;
  top: 0; bottom: 0;
  left: var(--sangra, 0px); width: 100vw;
  background: var(--cream);
}
.client:nth-of-type(even)::before { background: var(--grad-light); }
.client__pin {
  position: sticky; top: 0;
  height: 100vh; height: 100svh;
  display: flex; flex-direction: column;
  padding-block: clamp(1.2rem, 4vh, 3rem) clamp(1.5rem, 5vh, 3.5rem);
}

/* La cabecera manda: ocupa lo que necesita y los teléfonos se reparten
   el resto. Así no puede taparla nunca. */
/* La cabecera de la marca, en tres renglones y centrada:
      logotipo + nombre + arroba
      estadísticas
      rol
   Antes iba todo en una fila pegada a la izquierda, y ahí el nombre de la
   marca quedaba a un palmo del logotipo de ella, que vive fijo en esa
   misma esquina. Centrada, cada uno tiene su sitio.
   La cabecera entera se anima como una pieza (--oy / --os): sube desde
   abajo y crece. Que viajara solo el logotipo ya no vale, porque al
   apilarse los renglones se montaba sobre los de abajo. */
.client__head {
  position: relative; z-index: 3;
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  gap: clamp(.5rem, 1vw, .9rem) clamp(.7rem, 1.4vw, 1.3rem);
  flex-wrap: wrap; text-align: center;
  margin-bottom: clamp(1rem, 3vh, 2rem);
  transform: translateY(var(--oy, 0px)) scale(var(--os, 1));
  transform-origin: center bottom;
}
/* Cada una ocupa su propio renglón: con base 100 % el flex las obliga a
   saltar de línea, sin necesidad de más envoltorios en el HTML.
   OJO: `flex-basis` se mide sobre el eje principal. Solo vale mientras la
   cabecera sea una FILA; en cuanto se apila en columna (el bloque propio y
   todo el móvil) ese 100 % pasaría a ser del alto. Por eso se anula. */
.client__stats, .client__role { flex-basis: 100%; }
.client__head--centro .client__stats,
.client__head--centro .client__role { flex-basis: auto; }
/* Logotipo y nombre, juntos y como una sola pieza */
.client__mark {
  display: flex; align-items: center; justify-content: center;
  gap: clamp(.6rem, 1.1vw, 1rem);
  transform-origin: center;
  opacity: var(--mo, 1);
}
.client__logo { width: clamp(2.6rem, 3.4vw, 3.4rem); height: auto; flex: 0 0 auto; }
.client__name {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.5rem, 2.2vw, 2.1rem); letter-spacing: normal;
  line-height: 1; color: var(--ink); white-space: nowrap;
}
/* La arroba y el rol solo aparecen cuando el logotipo ya subió */
.client__at, .client__role { opacity: var(--q, 1); transition: opacity .2s linear; }
.client__at {
  font-family: var(--sans); font-weight: 500; font-size: .86rem;
  letter-spacing: .01em; color: var(--ink);
  padding: .38em 1em; border-radius: 999px;
  border: 1px solid rgba(30, 30, 30, .22);
  transition: background .35s var(--ease-out), color .35s var(--ease-out),
              border-color .35s var(--ease-out), opacity .2s linear;
}
.client__at:hover { background: var(--burgundy); border-color: var(--burgundy); color: var(--cream); }
.client__role {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(.72rem, .92vw, .92rem); letter-spacing: .04em;
  color: var(--burgundy);
}

/* ---- LOS CUATRO TELÉFONOS ----
   Se dimensionan por la ALTURA que queda libre, no por el ancho: así
   nunca desbordan la pantalla ni se comen la cabecera. */
.phones {
  flex: 1 1 auto; min-height: 0;
  /* Centrada y con aire entre teléfonos. La fila entera se desplaza como
     UN BLOQUE: así los cuatro conservan siempre su separación mientras
     entran, en vez de amontonarse por viajar distancias distintas. */
  display: flex; align-items: center; justify-content: center;
  gap: clamp(1.5rem, 3.9vw, 4.2rem);
  list-style: none; padding: 0; margin: 0;
  transform: translateX(var(--rowx, 0px));
}
.phone {
  position: relative;
  /* Casi todo el alto que les queda libre. El límite ya no es el ancho:
     la fila puede ser más ancha que el hueco que deja el panel, y el
     primer teléfono se sale por la izquierda y pasa por detrás del menú.
     Lo único que se respeta es que el ÚLTIMO se vea entero. */
  height: 85%; aspect-ratio: 9 / 19.5; width: auto; flex: 0 0 auto;
}
/* El mismo teléfono, tumbado. Conserva la escala del aparato: su alto es
   lo que mide de ancho uno vertical, así que se ve como el mismo móvil
   girado y no como otro más grande. */
.phone--h {
  height: calc(85% * 9 / 19.5);
  aspect-ratio: 19.5 / 9;
  perspective: 1400px; perspective-origin: 50% 45%;
  /* Sin transformación propia: quien se mueve es la fila entera. */
}
.phone__stage { position: absolute; inset: 0; transform-style: preserve-3d; }

.phone__body {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform: rotateY(calc(var(--tilt, -14deg) * (1 - var(--hov, 0))))
             rotateX(calc(3deg * (1 - var(--hov, 0))))
             translateZ(calc(18px * var(--hov, 0)));
  transition: transform .75s var(--ease-out);
}

/* Extrusión: el JS apila varias lonjas entre -GROSOR y +GROSOR. Es lo que
   le da canto de verdad, con las esquinas redondeadas incluidas, en vez
   de un plano inclinado. */
.phone__slice {
  position: absolute; inset: 0;
  border-radius: 13% / 6%;
  background: linear-gradient(100deg, #6b5a5c 0%, #cbbcbb 22%, #8d7a7c 48%, #d6cac9 72%, #6f5e60 100%);
  backface-visibility: hidden;
}
.phone__face {
  position: absolute; inset: 0;
  transform: translateZ(var(--grosor, 7px));
  border-radius: 13% / 6%;
  padding: 3.2%;
  background: linear-gradient(150deg, #2a2426, #161214 60%, #241e20);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
}
.phone__screen {
  position: relative; width: 100%; height: 100%;
  border-radius: 10% / 4.6%; overflow: hidden; background: #100c0e;
}
.phone__poster, .phone__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.phone__video { opacity: 0; transition: opacity .35s var(--ease-out); }
.phone.is-playing .phone__video { opacity: 1; }
.phone__island {
  position: absolute; left: 50%; top: 4.4%;
  width: 30%; height: 3%;
  transform: translateX(-50%) translateZ(calc(var(--grosor, 7px) + .5px));
  border-radius: 999px; background: #0b0809;
}
/* Tumbado, la isla se va al canto izquierdo */
.phone--h .phone__island {
  left: 2.4%; top: 50%; width: 1.6%; height: 30%;
  transform: translateY(-50%) translateZ(calc(var(--grosor, 7px) + .5px));
}
.phone--h .phone__face,
.phone--h .phone__slice { border-radius: 6% / 13%; }
.phone--h .phone__screen { border-radius: 4.6% / 10%; }
/* ---- BLOQUE PROPIO: ella como talento ----
   Sin logotipo de marca, sin arroba y sin estadísticas: las cuentas donde
   sale no son suyas, y poner números de terceros ahí confundiría. Lleva
   el mismo lockup de dos renglones que los titulares de sección. */
/* Bloque propio: titular al centro y más grande */
.client__head--centro {
  flex-direction: column; align-items: center; text-align: center;
  gap: .4rem;
  /* Sin relleno arriba. Lo llevaba de cuando esta cabecera era la única
     que subía y crecía, y necesitaba sitio para el recorrido. Ahora suben
     todas igual, así que esos 54 px solo le estaban robando banda a los
     teléfonos: por eso salían más chicos que los de las demás marcas. */
  padding-top: 0;
  /* En este bloque el titular NO viaja de lado: sube desde abajo y crece.
     Se transforma la cabecera ENTERA, título y línea de apoyo juntos, para
     que no se monten entre ellos. El origen abajo hace que al crecer
     empuje hacia arriba, no hacia los dos lados. */
  transform: translateY(var(--oy, 0px)) scale(var(--os, 1));
  transform-origin: center bottom;
}
.client--own .client__mark { transform: none; opacity: 1; }
.client__head--centro .client__role { margin-top: .2rem; }
.client__lock { line-height: .95; text-align: center; }
.client__lock .shead__l1 { font-size: clamp(2.9rem, 5.2vw, 4.9rem); }
.client__lock .shead__l2 { font-size: clamp(3.9rem, 7vw, 6.6rem); }
/* Cinco en fila: un pelo más chicos y con menos hueco para que entren */
/* Mismo 85 % que las demás marcas. Lo único distinto es el hueco: son
   cinco en fila y con la separación de los otros la fila se iría a 1.450
   px, así que aquí va más apretado. */
.client--own .phone  { height: 85%; }
.client--own .phones { gap: clamp(.8rem, 2vw, 2.2rem); }

/* Con uno tumbado la fila se ensancha: se aprieta un poco el hueco */
.phones:has(.phone--h) { gap: clamp(1rem, 2.4vw, 2.6rem); }

/* ---- ESTADÍSTICAS DE LA CUENTA ----
   Entran al final, cuando los teléfonos ya se colocaron. */
/* Su propio renglón dentro de la cabecera, centrado */
.client__stats {
  display: flex; justify-content: center;
  gap: clamp(1.6rem, 3.6vw, 3.4rem);
  margin: 0;
  opacity: var(--st, 1);
  transform: translateY(calc(14px * (1 - var(--st, 1))));
}
.client__stats > div { text-align: center; }
.client__stats dd {
  margin: 0;
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.3rem, 2vw, 1.9rem); line-height: 1;
  letter-spacing: normal; color: var(--ink);
}
.client__stats dt {
  margin-top: .35em;
  font-family: var(--sans); font-weight: 500;
  font-size: clamp(.62rem, .78vw, .76rem);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--burgundy);
}

.phone__shadow {
  position: absolute; left: 8%; right: 8%; bottom: -3%; height: 7%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(30, 30, 30, .42), transparent 75%);
  filter: blur(8px);
  transform: translateX(calc(var(--tilt-x, 0px) * (1 - var(--hov, 0)))) scaleX(calc(1 - .12 * var(--hov, 0)));
  transition: transform .75s var(--ease-out), opacity .75s var(--ease-out);
  opacity: calc(.6 + .4 * var(--hov, 0));
  pointer-events: none;
}

.phone__sound {
  position: absolute; right: 8%; bottom: 4%; z-index: 4;
  width: 2rem; height: 2rem; display: grid; place-items: center;
  border-radius: 999px; color: #fff;
  background: rgba(30, 30, 30, .55);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  opacity: 0; transform: scale(.7); pointer-events: none;
  transition: opacity .3s var(--ease-out), transform .35s var(--ease-bounce);
}
.phone__sound svg { width: 1rem; height: 1rem; }
.phone__sound svg[hidden] { display: none; }
.phone.is-playing .phone__sound { opacity: 1; transform: none; pointer-events: auto; }
.phone__sound:hover { background: var(--burgundy); }
@media (hover: none) { .phone__sound { opacity: 1; transform: none; pointer-events: auto; } }

/* ---- Habilidades ---- */
.bigtext {
  font-family: var(--serif); font-size: clamp(1.6rem, 4.2vw, 3.4rem); font-weight: 300;
  line-height: 1.18; letter-spacing: -.015em; max-width: 24ch; margin-bottom: clamp(3.5rem, 8vh, 6rem);
}
.split-word { display: inline-block; opacity: .13; transform: translateY(.16em); transition: opacity .5s var(--ease-out), transform .6s var(--ease-out); }
.split-word.is-in { opacity: 1; transform: none; }

.skills { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--line-soft); border: 1px solid var(--line-soft); border-radius: var(--radius); overflow: hidden; }
.skill { padding: clamp(1.6rem, 3vw, 2.4rem); background: var(--bg); transition: background .6s var(--ease-out); }
.skill:hover { background: #fff; }
.skill__num { font-family: var(--mono); font-size: .68rem; letter-spacing: .14em; color: var(--ink-mute); }
.skill__title { margin: .8rem 0 .7rem; font-size: 1.3rem; font-weight: 500; letter-spacing: -.02em; }
.skill__desc { font-size: .93rem; color: var(--ink-soft); }
.skill__tools { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: 1.1rem; }
.skill__tools li { font-family: var(--mono); font-size: .66rem; color: var(--ink-soft); padding: .22rem .55rem; border-radius: 6px; background: var(--bg-deep); }
.skill:hover .skill__tools li { background: #EBD5D3; }

/* ---- Clientes ---- */
.marquee { overflow: hidden; padding-block: clamp(1.5rem, 4vh, 3rem); border-block: 1px solid var(--line-soft); margin-bottom: clamp(3rem, 7vh, 5rem); }
.marquee__track {
  display: flex; align-items: center; gap: 2.2rem; width: max-content;
  font-family: var(--serif); font-size: clamp(1.6rem, 3.4vw, 2.8rem); font-weight: 300; font-style: italic;
  color: var(--ink-mute); animation: marquee 38s linear infinite;
}
.marquee__track span:nth-child(even) { color: var(--burgundy); font-style: normal; }
@keyframes marquee { to { transform: translateX(-50%); } }

.quotes {
  display: flex; gap: 1.5rem; overflow-x: auto; overflow-y: visible; padding-bottom: 1.5rem;
  scroll-snap-type: x mandatory; scrollbar-width: thin; scrollbar-color: var(--burgundy) transparent;
}
.quotes::-webkit-scrollbar { height: 6px; }
.quotes::-webkit-scrollbar-track { background: var(--line-soft); border-radius: 99px; }
.quotes::-webkit-scrollbar-thumb { background: var(--burgundy); border-radius: 99px; }
.quote {
  flex: 0 0 min(100%, 27rem); scroll-snap-align: start;
  padding: clamp(1.6rem, 3vw, 2.4rem); border-radius: var(--radius);
  background: rgba(255,255,255,.62); border: 1px solid var(--line-soft);
  box-shadow: 0 24px 50px -42px rgba(30,30,30,.5);
  display: flex; flex-direction: column; justify-content: space-between; gap: 1.6rem;
}
@supports (backdrop-filter: blur(10px)) { .quote { background: rgba(255,255,255,.5); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); } }
.quote blockquote p { font-family: var(--serif); font-size: 1.3rem; line-height: 1.4; }
.quote blockquote p::before { content: "“"; color: var(--burgundy); }
.quote blockquote p::after  { content: "”"; color: var(--burgundy); }
.quote figcaption { display: flex; align-items: center; gap: .8rem; }
.quote__av { width: 40px; height: 40px; border-radius: 50%; flex: 0 0 auto; background: var(--grad); }
.quote__av[data-av="b"] { background: linear-gradient(140deg, #D8A7A7, var(--grad-c)); }
.quote__av[data-av="c"] { background: linear-gradient(220deg, var(--burgundy), #EBD5D3); }
.quote__av[data-av="d"] { background: linear-gradient(30deg,  var(--burgundy), #D8A7A7); }
.quote__who { display: flex; flex-direction: column; font-size: .82rem; color: var(--ink-mute); }
.quote__who strong { font-weight: 500; font-size: .92rem; color: var(--ink); }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1.5rem; margin-top: clamp(3rem, 7vh, 5rem); }
.stat strong {
  display: block; font-family: var(--serif); font-size: clamp(2.6rem, 5vw, 4rem); font-weight: 300; line-height: 1;
  background: var(--grad-text); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.stat span { display: block; margin-top: .5rem; font-size: .85rem; color: var(--ink-soft); }

/* ---- Contacto ---- */
.section--contact .wrap { position: relative; z-index: 2; }
.contact { display: grid; grid-template-columns: 1fr; gap: clamp(2.5rem, 5vw, 4rem); }
.form { display: flex; flex-direction: column; gap: 1.1rem; }
.field { display: flex; flex-direction: column; gap: .45rem; }
.field label { font-family: var(--mono); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-mute); }
.field input, .field select, .field textarea {
  font: inherit; font-size: .95rem; color: var(--ink); padding: .85rem 1.05rem;
  border-radius: var(--radius-s); border: 1px solid var(--line); background: rgba(255,255,255,.7);
  transition: border-color .4s var(--ease-out), background .4s var(--ease-out), box-shadow .4s var(--ease-out); width: 100%;
}
.field textarea { resize: vertical; min-height: 7rem; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-mute); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--burgundy); background: #fff; box-shadow: 0 0 0 4px rgba(216,167,167,.22);
}
.field.has-error input, .field.has-error textarea { border-color: #7A1F2B; }
.form__status { min-height: 1.3rem; font-size: .85rem; color: var(--burgundy); }

.contact__side { display: flex; flex-direction: column; gap: 2.2rem; }
.contact__block { display: flex; flex-direction: column; gap: .6rem; }
.contact__big {
  font-family: var(--serif); font-size: clamp(1.4rem, 2.6vw, 2rem); width: fit-content;
  background-image: linear-gradient(currentColor, currentColor); background-size: 0% 1px;
  background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size .6s var(--ease-out), color .4s var(--ease-out);
}
.contact__big:hover { background-size: 100% 1px; color: var(--burgundy); }
.contact__social { display: flex; flex-wrap: wrap; gap: .5rem; }
.contact__social a {
  display: inline-block; padding: .45rem .95rem; border-radius: 999px; font-size: .85rem;
  border: 1px solid var(--line); background: rgba(255,255,255,.6);
  transition: transform .5s var(--ease-out), border-color .4s var(--ease-out), background .4s var(--ease-out);
}
.contact__social a:hover { transform: translateY(-3px); border-color: var(--burgundy); background: #fff; }
.contact__text { font-size: .93rem; color: var(--ink-soft); max-width: 30rem; }

/* ---- Pie ---- */
/* ---- PIE ----
   Fondo vinotinto y el mismo logotipo del home, pero quieto y pequeño:
   «SYLETH» con la firma encima, del tamaño que tenía el nombre suelto. */
.footer {
  padding-block: 2.6rem;
  /* Sin el hueco del panel: el pie está al final de la página, donde el
     panel ya no estorba, y así su fila se reparte sobre la pantalla
     entera en vez de sobre el trozo que queda a la derecha del menú. */
  background: var(--burgundy);
  color: var(--cream);
}
.footer__in {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 1.2rem;
}
/* El lockup: la firma se superpone al nombre igual que en el home, pero
   aquí la proporción va fija (no la calcula el JS) porque no se anima. */
.footer__mark { position: relative; display: inline-block; line-height: 0; }
.footer__name {
  display: block;
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: 1.5rem; line-height: .86; letter-spacing: -.015em;
  color: var(--cream);
}
.footer__sign {
  position: absolute; left: 0; top: 0;
  width: 100%; height: auto;
  pointer-events: none;
}
.footer__meta {
  font-family: var(--sans); font-weight: 500;
  font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--rose);
}
.footer__top {
  font-family: var(--sans); font-weight: 500; font-size: .85rem;
  color: var(--rose); transition: color .4s var(--ease-out);
}
.footer__top:hover { color: var(--cream); }

/* =============================================================
   7 bis. CENTRADO REAL, EL DE LA PANTALLA
   Las secciones llevan `padding-left: var(--side-w)` para dejarle sitio al
   panel de la izquierda. Eso corre el centro del contenido media anchura
   del panel hacia la derecha, y lo que va centrado quedaba descuadrado: no
   estaba en el centro de la PANTALLA, sino en el centro del hueco que
   queda entre el menú y el borde derecho.

   El margen izquierdo negativo devuelve esa mitad. Se compensa con el
   ANCHO ENTERO del panel, no con la mitad, porque al ensanchar una caja
   por la izquierda su centro solo se mueve la mitad de lo que crece.
   En vertical `--side-w` vale 0 y la cuenta se anula sola.

   Va al final a propósito: `.vias` trae `margin: 0` en su propia regla, y
   con la misma especificidad manda la última.

   La fila de teléfonos entra aquí también, pero con un tope: si al
   centrarla el primero se metiera por debajo del panel (pasa en monitores
   de menos de ~1400 px), el JS la empuja lo justo para librarlo. Centrada
   siempre que quepa; nunca tapada.
   ============================================================= */
.shead--centro,
.vias,
.phones,
.client__head { margin-left: calc(var(--side-w) * -1); }

/* =============================================================
   8. RESPONSIVE
   ============================================================= */

/* --- MÓVIL Y TABLET (hasta 1023 px) -------------------------------
   Aquí no hay morfeo. El home es un póster de una sola pantalla, con
   el orden del diseño (PORTAFOLIO · SYLETH · Ortega · las dos fichas ·
   el retrato) y el menú vive en una barra de cristal pegada abajo.

   Las cuatro capas del escritorio existen para intercalar el retrato
   entre el nombre y las tarjetas. En vertical estorban, así que se
   disuelven con `display: contents`: sus hijos pasan a ser celdas de
   la rejilla del hero y se pueden reordenar como en la maqueta. */
@media (max-width: 1023px) {

  /* Alto de la barra de abajo. Lo usan el hero (para que el retrato apoye
     EN la barra y no por debajo), el panel de cada cliente y el `body`. */
  :root { --barra: calc(3.3rem + env(safe-area-inset-bottom, 0px)); }

  /* ============ HOME: el póster ============ */
  .hero {
    height: auto; min-height: 100svh;
    display: grid;
    grid-template-columns: 1fr 1.42fr;   /* la ficha de años, más estrecha */
    /* Cuatro renglones: rótulo, nombre, las dos fichas y el retrato. El
       último se lleva lo que sobre, así el retrato siempre apoya en el
       pie de pantalla y la barra de cristal se le monta encima, como en
       la maqueta, sin dejar una franja de degradado suelta. */
    grid-template-rows: auto auto auto 1fr;
    justify-items: center;
    column-gap: clamp(.55rem, 2.6vw, 1rem);
    /* El relleno de abajo es justo lo que mide la barra: así el retrato
       APOYA en ella en vez de meterse por debajo. */
    padding: clamp(1.9rem, 6vh, 3.2rem) var(--gutter) var(--barra);
    /* Sin fondo propio: el degradado es la capa fija de `body`, la misma
       que en escritorio. Si el hero pintara el suyo, en la unión con
       «Sobre mí» volvería a verse la costura. */
  }
  .hero__layer, .hero__mark, .hero__cards, .hero__front, .poster {
    display: contents;
  }
  .hero__bg { display: none; }
  /* En escritorio llevan margen negativo para que las capas pegajosas se
     apilen unas sobre otras. Dentro de la rejilla sobra. */
  .hero__bg, .hero__shot { margin-bottom: 0; }

  [data-morph] {
    position: relative; left: auto; right: auto; top: auto; bottom: auto;
    transform: none !important;
  }
  .poster { opacity: 1 !important; }
  .poster > * { position: relative; left: auto; right: auto; top: auto; bottom: auto; }

  /* El orden de la maqueta */
  .word  { order: 1; grid-column: 1 / -1; }
  .mark  { order: 2; grid-column: 1 / -1; }
  .badge { order: 3; grid-column: 1; }
  .roles { order: 4; grid-column: 2; }
  .shot  { order: 5; grid-column: 1 / -1; }

  /* ---- «PORTAFOLIO» ---- */
  .word {
    margin: 0 0 .18em;
    /* Con tope por ALTO además de por ancho: en una pantalla ancha y baja
       (un móvil tumbado, una ventana estrecha de escritorio) el bloque de
       arriba crecía con el ancho y le robaba el sitio al retrato. */
    font-size: min(clamp(1.9rem, 10.6vw, 3.4rem), 6.4vh);
    letter-spacing: -.005em;
    text-align: center;
    /* Crema sobre el vinotinto, con la misma sombra tenue del nombre */
    color: var(--cream);
    text-shadow: .03em .03em .06em rgba(30, 30, 30, .18);
  }

  /* ---- «SYLETH» + la firma ---- */
  .mark {
    font-size: min(19.4vw, 13vh);
    width: 100%;
    text-align: center;
    margin-bottom: clamp(1.1rem, 3.4vh, 2rem);
  }
  .mark__name {
    justify-content: center;
    color: var(--cream);
  }
  /* La firma vuelve al flujo: se centra bajo el nombre y monta sobre él
     lo mismo que en escritorio, pero en `em`, o sea proporcional. */
  .sign {
    position: relative; left: auto; top: auto;
    display: block; width: 74%;
    margin: -.42em auto 0;
    /* Sin `opacity: 1 !important`. Aquí la firma también se ESCRIBE, y esa
       declaración la dejaba visible entera antes de que el trazo empezara:
       se veía puesta, desaparecía y se dibujaba. La enciende el JS, igual
       que en escritorio, con la red de seguridad de los 4 s por detrás. */
  }
  /* En vertical no hay morfeo (--p queda clavado en 1), así que los
     colores que en escritorio conduce el scroll se fijan a mano. La firma
     cae sobre la banda clara del degradado: vinotinto, como el diseño. */
  .sign__art { fill: var(--burgundy) !important; }

  /* ---- Las dos fichas de cristal, hombro con hombro ---- */
  .badge, .roles {
    width: 100%; min-width: 0; max-width: none;
    height: 100%; aspect-ratio: auto;
    margin: 0;
    border-radius: 16px;
  }
  .badge {
    flex-direction: column; justify-content: center; align-items: center;
    gap: 0;
    padding: clamp(.7rem, 2.6vw, 1.1rem) .6rem;
    text-align: center;
  }
  .badge strong { font-size: min(clamp(1.9rem, 9vw, 3rem), 5.4vh); }
  .badge span {
    margin-top: .45em;
    font-size: clamp(.56rem, 2.8vw, .74rem);
    line-height: 1.3;
    color: var(--cream) !important;
  }
  .roles {
    display: flex; flex-direction: column;
    justify-content: center; align-items: center;
    gap: .5em;
    padding: clamp(.7rem, 2.6vw, 1.1rem) .5rem;
    font-size: clamp(.56rem, 2.9vw, .82rem);
    color: var(--burgundy) !important;
  }
  .roles li { text-shadow: 0 1px 2px rgba(248, 241, 231, .55); }

  /* ---- El retrato: a sangre, apoyado en el pie de pantalla ---- */
  .shot {
    position: relative; right: auto; bottom: auto;
    /* A sangre por los dos lados: se estira a su columna y los márgenes
       negativos se comen justo el `padding-inline` del hero. Con `100vw`
       se pasaba de largo en cuanto había barra de desplazamiento. */
    width: auto; max-width: none;
    margin: clamp(.9rem, 3vh, 1.8rem) calc(-1 * var(--gutter)) 0;
    justify-self: stretch; align-self: stretch;
    /* Sin alto propio: se lleva el renglón `1fr`, o sea lo que sobre de la
       pantalla. Ponerle un mínimo lo hacía desbordar por abajo en las
       pantallas cortas y asomaba por encima de «Sobre mí». */
    height: auto; min-height: 0;
    /* Solo mide la banda; quien pinta es `.shot__in`, que va fijo. Sin
       `filter` ni `will-change`: cualquiera de los dos convertiría a
       `.shot` en bloque contenedor y el hijo fijo dejaría de serlo. */
    filter: none; will-change: auto; opacity: 1;
  }
  /* EL RETRATO SE QUEDA CLAVADO.
     La página sube por detrás y él no se mueve: solo se va desenfocando y
     apagando (--shot-blur / --shot-o, que escribe `initFotoMovil`) hasta
     desaparecer. Así no se ve el corte de la silueta al irse el home.
     --foto-top y --foto-alto los mide el JS: son la banda que la rejilla
     del hero le reserva, y un elemento fijo necesita esas coordenadas en
     píxeles de pantalla. */
  .shot__in {
    position: fixed; z-index: -1;
    left: 0; right: 0;
    top: var(--foto-top, 45svh);
    /* Hasta el borde de la pantalla, NO hasta donde empieza la barra. La
       silueta está recortada a la altura de la cadera, y si su base
       aterriza justo encima de la barra se ve el corte. Bajándola hasta
       el borde, ese último tramo queda por detrás del cristal de la barra
       y el corte no se lee. Y de paso vale para cualquier área segura:
       ya no depende de acertar el alto exacto de la barra. */
    bottom: 0; height: auto;
    pointer-events: none;
  }
  /* Con movimiento reducido el JS no escribe el desenfoque, así que una
     foto fija se quedaría flotando para siempre sobre «Sobre mí». Ahí
     vuelve a su celda y se va con la página, como cualquier imagen. */
  @media (prefers-reduced-motion: reduce) {
    .shot__in { position: absolute; left: 0; right: 0; top: 0; height: 100%; }
    .shot__img { filter: none; opacity: 1; }
  }
  .shot__img {
    width: 100%; height: 100%;
    /* `contain` y apoyada abajo. Con `cover` la foto se escala por el
       ANCHO, y como es muy vertical (1023 × 1376) en cuanto la pantalla es
       ancha y baja el recorte se comía todo menos la cara. Así entra
       completa siempre y su base cae exactamente sobre la barra. */
    object-fit: contain; object-position: 50% 100%;
    margin-inline: 0;
    /* EL DESENFOQUE VA AQUÍ, en la imagen, NO en `.shot__in`.
       `.shot__in` es el elemento que anima la intro (`shotIn`), y esa
       animación termina en `filter: blur(0)` con `fill-mode: forwards`.
       Una animación gana a TODO, incluido el estilo en línea, así que
       dejaba el filtro clavado en cero para siempre y la foto no se
       difuminaba nunca por mucho que el scroll escribiera --shot-blur. */
    filter: blur(var(--shot-blur, 0px));
    opacity: var(--shot-o, 1);
  }

  /* ============ LA BARRA DE ABAJO ============
     El mismo cristal de las fichas: rose dust al 30 % y desenfoque. Lo
     que da cuerpo es el `backdrop-filter`, no el relleno. */
  .side {
    left: 0; right: 0; top: auto; bottom: 0; width: auto;
    flex-direction: row; align-items: center; justify-content: center;
    gap: .2rem;
    padding: .55rem .7rem calc(.55rem + env(safe-area-inset-bottom, 0px));
    background: rgba(216, 167, 167, .58);          /* respaldo sin blur */
    border-top: 1px solid rgba(255, 255, 255, .5);
    box-shadow: 0 -10px 30px -22px rgba(30, 30, 30, .55);
    overflow-x: auto; scrollbar-width: none;
  }
  .side::-webkit-scrollbar { display: none; }
  @supports (backdrop-filter: blur(10px)) {
    .side {
      background: rgba(216, 167, 167, .30);
      backdrop-filter: blur(20px) saturate(155%);
      -webkit-backdrop-filter: blur(20px) saturate(155%);
    }
  }
  .side__slot, .side__row { display: none; }
  .side__nav { flex-direction: row; align-items: center; gap: .12rem; margin: 0; }
  .nav__item {
    transform: none !important; opacity: 1 !important; animation: none !important;
    gap: 0; padding: .48rem .72rem;
    font-size: clamp(.6rem, 3vw, .74rem); letter-spacing: .05em;
    color: var(--burgundy); flex: 0 0 auto;
    /* En escritorio estas dos las conduce el scroll; aquí no hay scroll
       que las conduzca y hay que devolverlas a su valor plano. */
    min-width: 0; height: auto;
  }
  .nav__item::after { display: none; }
  .nav__icon { display: none; }
  .nav__pill { opacity: 0; transform: none; }
  /* La sección en la que estás: Montserrat Medium Italic, sin píldora,
     como el resto del sitio. */
  .nav__item.is-active { color: var(--burgundy); font-style: italic; }
  /* «Contacto» sí es botón, igual que en el diseño */
  .nav__item--cta { color: var(--cream); padding-inline: .95rem; }
  .nav__item--cta .nav__pill {
    opacity: 1; border-radius: 999px;
    background: var(--grad-pill);
    box-shadow: 0 8px 20px -12px rgba(91, 15, 24, .8);
  }
  .nav__item--cta.is-active { color: var(--cream); }
  /* Hueco para la barra: lo que mide + el borde seguro del teléfono */
  body { padding-bottom: var(--barra); }

  /* ---- CRISTAL MÁS BARATO DE CALCULAR ----
     `backdrop-filter` es de lo más caro que hay en un teléfono: obliga a
     volver a muestrear y desenfocar lo que hay detrás en cada fotograma, y
     aquí hay diez fichas del recorrido más la barra fija, que además se
     recalcula siempre porque el contenido pasa por detrás.
     A la mitad de radio el efecto se lee igual (todo mide la mitad en una
     pantalla de teléfono) y cuesta bastante menos. */
  @supports (backdrop-filter: blur(10px)) {
    .jcard__in { backdrop-filter: blur(10px) saturate(150%);
                 -webkit-backdrop-filter: blur(10px) saturate(150%); }
    .side { backdrop-filter: blur(12px) saturate(150%);
            -webkit-backdrop-filter: blur(12px) saturate(150%); }
    .glass { backdrop-filter: blur(9px) saturate(140%);
             -webkit-backdrop-filter: blur(9px) saturate(140%); }
    .via { backdrop-filter: blur(9px) saturate(140%);
           -webkit-backdrop-filter: blur(9px) saturate(140%); }
  }

  /* ============ SOBRE MÍ ============ */
  .section { padding-block: clamp(3.2rem, 9vh, 5rem); }
  /* En escritorio «Sobre mí» monta 50vh sobre el hero porque el hero mide
     200vh y el morfeo se resuelve en ese solape. Aquí el hero es una
     pantalla normal: ese margen negativo se comía media sección. */
  .section--about { margin-top: 0; scroll-margin-top: 0; padding-top: clamp(2.6rem, 7vh, 4rem); }

  .about__col { position: static; width: auto; margin-bottom: clamp(2.2rem, 6vh, 3.2rem); }
  .about__intro { position: static; transform: none; opacity: 1; padding-bottom: 0; }
  .about__lede, .about__note { max-width: 34rem; }

  /* ---- TODOS LOS TITULARES AL CENTRO ----
     En vertical la página es una sola columna, así que los titulares
     alineados a la izquierda quedaban descolgados. Los párrafos siguen
     justificados: centrar un texto justificado no tiene sentido, porque
     el justificado ya llena la línea de lado a lado. */
  .about__title, .shead { text-align: center; }
  .shead { max-width: none; margin-inline: auto; }
  .shead__lede { margin-inline: auto; text-align: center; }
  .about__title-b { margin-left: 0; }

  /* La línea del recorrido, en rose dust.
     El degradado de escritorio va de vinotinto a vino, y aquí la recta
     cae sobre el carril izquierdo, que es justo la esquina más oscura del
     fondo: vinotinto sobre vinotinto no se veía. El resplandor también se
     invierte, más cerrado y cálido. */
  .journey__line {
    stroke: var(--rose);
    stroke-width: 2.4;
    filter: drop-shadow(0 0 4px rgba(248, 241, 231, .5))
            drop-shadow(0 0 12px rgba(216, 167, 167, .45));
  }
  .journey__stop { fill: var(--cream); stroke: var(--burgundy); stroke-width: 2; }

  /* Las fichas se apilan a todo el ancho, con la recta a su izquierda.
     Hay que ANULAR el margen negativo de escritorio: allí las fichas se
     meten en la banda de la anterior porque están en carriles distintos;
     aquí, a todo el ancho, ese solape las pegaba unas encima de otras. */
  .journey { padding-left: 2.1rem; padding-block: 0 clamp(2.5rem, 7vh, 4rem); }
  .jcard { width: 100%; min-width: 0; }
  .jcard + .jcard { margin-top: clamp(1.1rem, 3.2vh, 1.9rem); }
  .jcard[data-lane] { margin-inline: 0; transform: none; }
  .js .jcard[data-lane] .jcard__in { --from-x: 0px; }
  .jcard__in {
    border-radius: 20px; transform: none; opacity: 1;
    /* El relleno de escritorio va en vw y aquí se quedaba en el mínimo:
       el texto salía casi pegado al filete del cristal. */
    padding: 1.35rem 1.25rem;
  }
  .jcard__mark { margin-bottom: .9rem; }
  .jcard__mark strong { font-size: clamp(2.1rem, 9vw, 2.8rem); }
  .jcard__title { font-size: clamp(.98rem, 4.1vw, 1.16rem); margin-bottom: .7rem; }
  .jcard__text { font-size: clamp(.82rem, 3.5vw, .9rem); line-height: 1.7; }

  /* ============ PROYECTOS ============ */
  .shead { margin-bottom: clamp(2.2rem, 6vh, 3.4rem); }
  .client { height: 165vh; }
  .client__pin {
    /* La barra de abajo tapa el pie del panel: se le reserva su alto para
       que ni los teléfonos ni las estadísticas queden por debajo. */
    padding-block: clamp(1rem, 3.5vh, 2rem) calc(var(--barra) + .4rem);
  }
  /* La cabecera de cada marca, centrada y en tres renglones: logotipo con
     nombre, la arroba y el rol. Y se anima ENTERA, como la de «Frente a
     la cámara»: sube desde abajo y crece. Antes viajaba solo el logotipo
     y, al apilarse los renglones, se montaba sobre la arroba y el rol. */
  .client__head {
    flex-direction: column; align-items: center; text-align: center;
    gap: .5rem;
    margin-bottom: clamp(.9rem, 3vh, 1.6rem);
  }
  /* En columna el eje principal es el alto: un `flex-basis: 100%` haría
     que cada renglón pidiera la pantalla entera. */
  .client__stats, .client__role { flex-basis: auto; }
  .client__mark { transform-origin: center; justify-content: center; }
  .client__name { white-space: normal; }
  .client__stats {
    justify-content: center;
    gap: clamp(1.1rem, 6vw, 2.2rem);
    margin-bottom: clamp(.7rem, 2.4vh, 1.4rem);
  }
  .client__stats > div { text-align: center; }
  .client__stats dd { font-size: clamp(1.1rem, 5.2vw, 1.5rem); }
  .client__at { font-size: .78rem; }
  /* Los teléfonos: la fila sigue siendo más ancha que la pantalla, así que
     el JS la deja apoyada en el borde DERECHO. El primero se sale por la
     izquierda; el último se ve entero, que es lo que importa. */
  .phones { gap: clamp(.8rem, 3.4vw, 1.4rem); justify-content: flex-start; }
  /* Ocupan casi toda la banda libre: en vertical sobra mucho alto y se
     quedaban chicos, con el vídeo ilegible. */
  .phone { height: 80%; }
  /* El tumbado tiene que seguir siendo el MISMO aparato girado: su alto es
     lo que mide de ancho uno vertical. Sin esta línea, la regla de arriba
     le daba un 80 % de alto con proporción 19,5/9 y salía un teléfono de
     casi mil píxeles de ancho. */
  .phone--h { height: calc(80% * 9 / 19.5); }
  /* Este bloque tiene cinco y la cabecera de dos renglones, así que su
     banda es más corta. */
  .client--own .phone { height: 72%; }

  /* ============ CONTACTO ============ */
  .shead--centro { margin-bottom: clamp(2.6rem, 7.5vh, 3.6rem); }
  /* «¿Tienes una idea?» no cabe de una línea con el tamaño de escritorio:
     partía en dos y la Sloop de abajo, que monta -.45em, se metía en el
     segundo renglón de la Europa. */
  .shead--centro .shead__l1 { font-size: clamp(1.75rem, 8.3vw, 4.6rem); }
  .shead--centro .shead__l2 { font-size: clamp(2.5rem, 11.8vw, 6.2rem); }
  /* Las tres vías, una debajo de otra y a todo el ancho. `.via` es
     inline-flex, así que `align-items: stretch` en la lista no la
     estiraba: hay que darle el ancho a la píldora. */
  .vias { flex-direction: column; align-items: stretch; gap: .75rem; }
  .vias li { display: flex; }
  .via { flex: 1 1 auto; justify-content: center; padding-inline: 1.1rem; }

  /* ============ PIE ============ */
  .footer { padding-block: 2.2rem; }
  .footer__in {
    flex-direction: column; align-items: center;
    justify-content: center; text-align: center; gap: 1.1rem;
  }
}

@media (min-width: 540px) {
  .reels  { grid-template-columns: repeat(2, 1fr); }
  .skills { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  :root { --side-w: 232px; }
  .reels  { grid-template-columns: repeat(3, 1fr); }
  .skills { grid-template-columns: repeat(3, 1fr); }
  .contact { grid-template-columns: 1.15fr .85fr; }
}

@media (min-width: 1280px) { .quote { flex-basis: 24rem; } }
@media (min-width: 1600px) { :root { --side-w: 186px; } }

/* =============================================================
   9. REDUCED MOTION — solo lo intrusivo
   ============================================================= */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .marquee__track { animation: none; }
  .hero__bg::before { animation: none; }
  /* El recorrido se muestra entero y quieto: fichas puestas, línea
     completa (el `stroke-dasharray` lo desactiva el JS) y sin parallax. */
  .jcard__in { opacity: 1 !important; transform: none !important; transition: none; }
  .about__intro { transform: none; opacity: 1; }
}
