/* ==========================================================================
   andreuinglada.com V3 · CSS base
   Reset, estilos base y utilidades. Los valores (color, tipografía, espacios,
   radios) viven en tokens.css, que se carga antes que este archivo.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fuentes (OFL, autoalojadas, subset latino; ver /assets/fonts/LICENCIAS.md)
   Se generan con scripts/fuentes.mjs desde los .ttf de _masters/fuentes/.
   La display se precarga en el <head> de las páginas con texto display en la primera pantalla:
   <link rel="preload" href="/assets/fonts/eb-garamond-400-latin.woff2" as="font" type="font/woff2" crossorigin>
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "EB Garamond";
  src: url("/assets/fonts/eb-garamond-400-latin.woff2?v=97257827c8") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+013F-0140, 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: "EB Garamond";
  src: url("/assets/fonts/eb-garamond-italic-400-latin.woff2?v=b15b037806") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+013F-0140, 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;
}

/* Fuentes de reserva ajustadas (fase 21): mientras cargan Garamond e Inter,
   Times New Roman y Arial ocupan lo mismo (ancho medio, alto de línea), y el
   texto no salta al cambiar. Medidas con fontTools sobre los .ttf. */
@font-face {
  font-family: "EB Garamond Fallback";
  src: local("Times New Roman"), local("TimesNewRomanPSMT");
  size-adjust: 94.27%;
  ascent-override: 106.82%;
  descent-override: 31.61%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("ArialMT");
  size-adjust: 105.24%;
  ascent-override: 92.06%;
  descent-override: 22.92%;
  line-gap-override: 0%;
}

/* Ficha NFC de Contacto: SemiBold solo con las letras de «Andreu Inglada».
   Familia aparte para que ningún otro texto la use. Solo se descarga en Contacto. */
@font-face {
  font-family: "EB Garamond Ficha";
  src: url("/assets/fonts/eb-garamond-600-ficha.woff2?v=c96e0ba3b8") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap; /* precargada en Contacto: llega antes de pintar la ficha */
}

@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-400-latin.woff2?v=152296ac84") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+013F-0140, 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: "Inter";
  src: url("/assets/fonts/inter-500-latin.woff2?v=66b69b2f42") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+013F-0140, 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;
}

:root {
  color-scheme: light;
}

/* --------------------------------------------------------------------------
   Reset mínimo
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent; /* sin el recuadro gris de Safari al tocar */
}

/* Los contornos de foco van con :focus-visible (solo con teclado). Además,
   si lo último fue un toque o un clic (modules/input-mode.js), ninguno: Safari
   a veces los enseña cuando un script mueve el foco tras un toque. */
html[data-input="pointer"] :focus-visible {
  outline: none !important;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  /* El fondo puede cambiar por secciones (data-page-color, page-color.js):
     --page-mix va de 0 (papel) a 1 (grafito); el fundido lo hace el JS */
  background: color-mix(in srgb, var(--color-graphite) calc(var(--page-mix, 0) * 100%), var(--color-paper));
  color: var(--color-ink);
  font-family: var(--font-text);
  font-weight: var(--weight-text);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol {
  margin: 0;
}

/* Solo existen los pesos 400/500: los títulos heredan y el énfasis usa 500 */
h1, h2, h3, h4 {
  font-weight: inherit;
}

strong, b {
  font-weight: var(--weight-text-strong);
}

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

a {
  color: inherit;
}

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

:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Tipografía display (EB Garamond): frases protagonistas, menú y títulos.
   Sin negritas ni cursivas falsas: la familia solo tiene 400 y cursiva 400.
   -------------------------------------------------------------------------- */

.display {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  font-synthesis: none;
}

/* Cursiva para detalles dentro de una frase display */
.display em,
.display i {
  font-style: italic;
}

/* --------------------------------------------------------------------------
   Marcas de contenido (ver CLAUDE.md)
   -------------------------------------------------------------------------- */

/* [data-pending]: material que aún no existe (marcado real en <template>);
   no se muestra. [data-provisional] solo marca un texto provisional para
   localizarlo: se ve como cualquier otro. */
[data-pending] {
  display: none !important;
}

/* Entrada «crece al cargar» ([data-grow-in], grow-in.js), como el showreel de
   Collins: espera al 80 % hasta que arranca la página. Misma red de seguridad
   que [data-reveal]: si el JS falla, a los 3 s se ve entero. */
.js [data-grow-in]:not(.is-entered) {
  transform: scale(0.8);
  animation: grow-in-fallback 0s 3s forwards;
}

@keyframes grow-in-fallback {
  to { transform: none; }
}

/* --------------------------------------------------------------------------
   Elementos que entran animados
   Con JS se ocultan hasta que su módulo los anima (y les pone .is-revealed).
   Red de seguridad: si el JS falla tras poner .js, a los 3 s se muestran igual.
   Cuando el módulo toma el control (.is-reveal-ready), el estado oculto lo
   lleva él y la red de seguridad deja de aplicarse: si no, lo que está más
   abajo de la primera pantalla aparecería a los 3 s sin haber llegado a él.
   Sin JS (.no-js) no se ocultan nunca.
   -------------------------------------------------------------------------- */

.js [data-reveal]:not(.is-revealed, .is-reveal-ready) {
  opacity: 0;
  animation: reveal-fallback 0s 3s forwards;
}

@keyframes reveal-fallback {
  to { opacity: 1; }
}

/* Máscara de cada trozo de un revelado de texto (motion/reveal-text.js).
   Recorta el trozo mientras sube. Se ensancha en vertical con padding y lo
   compensa con margen negativo, para no cortar los descendentes (g, p, j),
   los acentos ni lo que sobresale de la cursiva de la Garamond (con
   interlineado 1) sin mover el texto. */
.reveal-mask {
  overflow: clip;
  padding: 0.12em 0.08em 0.2em;
  margin: -0.12em -0.08em -0.2em;
}

/* El texto partido por líneas no cambia de alto. Las máscaras son bloques
   con margen negativo y, en un bloque normal, esos márgenes se funden con
   los del párrafo y entre sí (solo cuenta el mayor), así que lo de debajo
   bajaba unos píxeles al partirse (lo notaba la transición tarjeta → case
   study: la imagen principal se capturaba 7 px más arriba de donde
   acababa). flow-root deja dentro los márgenes de la primera y la última
   máscara, y entre dos máscaras el margen de arriba de la segunda ya es la
   suma de los dos (-0,2em - 0,12em). */
[data-reveal="lines"] {
  display: flow-root;
}

.reveal-mask + .reveal-mask {
  margin-top: -0.32em;
}

/* --------------------------------------------------------------------------
   Componentes de interfaz (propuestos en /lab/estilos/, fase 2; en la web
   desde la fase 6). Se adaptan al fondo: toman el color del texto
   (currentColor) y el botón principal usa --btn-bg / --btn-fg, que un
   contexto oscuro puede invertir (ver .site-menu.is-overlay).
   .is-hover / .is-focus solo sirven para enseñar el estado en el lab.
   -------------------------------------------------------------------------- */

/* Enlace de texto */
.link {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: text-underline-offset var(--duration-fast) var(--ease-out), text-decoration-thickness var(--duration-fast) var(--ease-out);
}

.link:hover,
.link.is-hover {
  text-decoration-thickness: 2px;
  text-underline-offset: 0.35em;
}

/* Botón píldora */
.btn {
  --btn-bg: var(--color-ink);
  --btn-fg: var(--color-paper);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-xs) var(--space-m);
  border: 0;
  border-radius: var(--radius-pill);
  font-size: var(--text-small);
  font-weight: var(--weight-text-strong);
  text-decoration: none;
  cursor: pointer;
  transition: scale var(--spring-snappy-duration) var(--spring-snappy), background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}

/* Principal: relleno (escala con `scale` para no pisar el transform del magnetismo) */
.btn--primary {
  background: var(--btn-bg);
  color: var(--btn-fg);
}

.btn--primary:hover,
.btn--primary.is-hover {
  background: color-mix(in srgb, var(--btn-bg) 86%, var(--btn-fg));
  scale: 1.04;
}

/* Secundario: contorno que se rellena */
.btn--secondary {
  background: transparent;
  color: var(--btn-bg);
  box-shadow: inset 0 0 0 1px var(--btn-bg);
}

.btn--secondary:hover,
.btn--secondary.is-hover {
  background: var(--btn-bg);
  color: var(--btn-fg);
}

/* Foco visible común */
.link:focus-visible,
.link.is-focus,
.btn:focus-visible,
.btn.is-focus,
.status:focus-visible,
.status.is-focus {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

.btn:focus-visible,
.btn.is-focus,
.status:focus-visible,
.status.is-focus {
  border-radius: var(--radius-pill);
}

.btn--primary:focus-visible,
.btn--primary.is-focus {
  outline-color: var(--btn-bg);
}

/* Indicador «● Disponible para trabajar» */
.status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-s);
  border-radius: var(--radius-pill);
  font-size: var(--text-small);
  color: inherit;
  text-decoration: none;
  background: color-mix(in srgb, currentColor 7%, transparent);
  transition: background-color var(--duration-fast) var(--ease-out);
}

a.status:hover,
.status.is-hover {
  background: color-mix(in srgb, currentColor 13%, transparent);
}

.status__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--color-accent);
  flex: none;
}

/* --------------------------------------------------------------------------
   Header (partials/header.html)
   Como Collins: nombre a la izquierda y «Menú» a la derecha, en todas las
   pantallas. Fondo siempre transparente: solo cambia el color del texto
   según la sección que tiene debajo (modules/header.js):
     data-header-theme="light"  texto tinta (por defecto)
     data-header-theme="dark"   texto papel (secciones en tinta o color de proyecto)
   Sin JS, el tema sale del <body data-header-theme> de la página.
   -------------------------------------------------------------------------- */

.site-header {
  --header-fg: var(--color-ink);
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-m);
  /* Alineado con la columna de contenido (ver .container) */
  padding: var(--space-s) var(--page-inline);
  color: var(--header-fg);
  /* Excepción aprobada a «solo transform y opacity»: el color del texto se
     anima (200 ms). Son dos enlaces cortos: el coste es despreciable. */
  transition: color var(--duration-fast) var(--ease-out);
  /* La franja transparente no bloquea los clics de lo que hay debajo */
  pointer-events: none;
}

body[data-header-theme="dark"] .site-header,
.site-header[data-header-theme="dark"] {
  --header-fg: var(--color-paper);
}

/* Con JS, manda el tema que pone el módulo (aunque el body diga otra cosa) */
.site-header[data-header-theme="light"] {
  --header-fg: var(--color-ink);
}

.site-header__home,
.site-header__menu,
.site-header__lang {
  pointer-events: auto;
}

/* Fondo del header: papel translúcido con desenfoque cuando la página no está
   arriba del todo (.is-scrolled en <html>, del script del <head>); arriba del
   todo, transparente. Sobre secciones oscuras, tinta translúcida (sigue el
   tema del header). Sin líneas ni sombras: la banda se distingue por el
   desenfoque. Con el menú abierto no hace falta (el menú ya es tinta). */
.site-header::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgb(247 246 242 / 0.75);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  opacity: 0;
  transition:
    opacity var(--duration-base) var(--ease-out),
    background-color var(--duration-fast) var(--ease-out);
}

.is-scrolled .site-header::before {
  opacity: 1;
}

body[data-header-theme="dark"] .site-header::before,
.site-header[data-header-theme="dark"]::before {
  background: rgb(15 15 15 / 0.75);
}

.site-header[data-header-theme="light"]::before {
  background: rgb(247 246 242 / 0.75);
}

/* Mientras el fondo de la página cambia (page-color.js), el header cambia de
   color con el mismo fundido */
.is-page-toning .site-header,
.is-page-toning .site-header::before {
  transition-duration: var(--duration-page-tone);
  transition-timing-function: var(--ease-in-out);
}

html.menu-open .site-header::before {
  opacity: 0;
}

/* Mientras dura una transición entre páginas el header lleva nombre propio y
   el desenfoque no funciona (main.css, Transiciones; Chrome ni siquiera pinta
   el fondo): sin backdrop-filter y casi opaco, para que lo de debajo no se
   transparente de golpe */
.has-page-transition .site-header::before,
.has-page-transition .site-header[data-header-theme="light"]::before {
  background: rgb(247 246 242 / 0.95);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.has-page-transition body[data-header-theme="dark"] .site-header::before,
.has-page-transition .site-header[data-header-theme="dark"]::before {
  background: rgb(15 15 15 / 0.95);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Sin backdrop-filter: casi opaco, para que el texto se lea igual */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header::before,
  .site-header[data-header-theme="light"]::before {
    background: rgb(247 246 242 / 0.95);
  }

  body[data-header-theme="dark"] .site-header::before,
  .site-header[data-header-theme="dark"]::before {
    background: rgb(15 15 15 / 0.95);
  }
}

/* Selector de idioma del header, junto al icono: Inter pequeña, el activo con
   el color del header (tinta, o papel sobre oscuro) y los demás apagados */
.site-header__lang {
  margin-left: auto;
  gap: var(--space-2xs);
}

.site-header .site-header__lang a {
  color: inherit;
  opacity: 0.62; /* AA: ~5:1 sobre papel y ~7:1 sobre tinta (con 0,5 no llegaba) */
  transition: opacity var(--duration-fast) var(--ease-out);
}

.site-header .site-header__lang a:hover,
.site-header .site-header__lang a[aria-current] {
  color: inherit;
  opacity: 1;
}

.site-header__home {
  font-weight: var(--weight-text-strong);
  text-decoration: none;
  white-space: nowrap; /* en una línea también a 320 px, con el selector de idioma */
}

/* Entrada al cargar (modules/header-motion.js): con JS, nombre e icono
   esperan ocultos a 'page:ready'. Misma red de seguridad que [data-reveal]:
   si el JS falla, aparecen a los 3 s. Si la página llega con una transición
   entre páginas (fase 19), ya se ven: el header no se mueve en ella. */
.js:not(.arrived-by-transition) .site-header__home:not(.is-revealed),
.js:not(.arrived-by-transition) .site-header__lang:not(.is-revealed),
.js:not(.arrived-by-transition) .site-header__menu:not(.is-revealed) {
  opacity: 0;
  animation: reveal-fallback 0s 3s forwards;
}

/* Sin puntero fino (o sin JS) no hay ola: el hover del nombre es un subrayado.
   Con ratón, la ola de letras lo sustituye (el subrayado se cortaría en las
   letras partidas). */
.site-header__home:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .js .site-header__home:hover {
    text-decoration: none;
  }
}

/* Icono de menú: dos rayas de 1,5 px en un área de pulsación de 44 × 44 px.
   Las rayas se pegan al borde derecho para que el icono quede alineado con
   la columna; cada raya es un elemento propio para poder girarla hasta la ✕
   (fase 5), con el origen de giro en su centro. */
.site-header__menu {
  --bar-width: 1.5rem;
  --bar-gap: 0.4375rem; /* 7 px entre rayas */
  position: relative;
  display: block;
  width: 44px;
  height: 44px;
  color: inherit;
}

.site-header__bar {
  position: absolute;
  top: 50%;
  right: 0;
  width: var(--bar-width);
  height: 1.5px;
  margin-top: -0.75px;
  border-radius: 1px;
  background: currentColor;
  transform-origin: center;
}

.site-header__bar:first-child {
  translate: 0 calc(var(--bar-gap) / -2);
}

.site-header__bar:last-child {
  translate: 0 calc(var(--bar-gap) / 2);
}

/* Enlace para saltar al contenido: invisible hasta que llega el foco del teclado */
.skip-link {
  position: fixed;
  top: var(--space-s);
  left: var(--gutter);
  z-index: 101;
  padding: var(--space-2xs) var(--space-s);
  border-radius: var(--radius-pill);
  background: var(--color-ink);
  color: var(--color-paper);
  font-size: var(--text-small);
  font-weight: var(--weight-text-strong);
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus-visible {
  transform: none;
}

/* --------------------------------------------------------------------------
   Menú (partials/menu.html, modules/menu.js)
   Sin JS: sección al final de la página, sobre papel.
   Con JS (.is-overlay): capa a pantalla completa sobre tinta, con la
   composición de Collins: enlaces grandes y una fila de acciones a la
   izquierda, destacados en filas a la derecha y la parte de abajo libre.
   -------------------------------------------------------------------------- */

.site-menu {
  padding: var(--space-3xl) var(--page-inline);
}

.site-menu__inner {
  display: grid;
  gap: var(--space-2xl) var(--space-xl);
  align-content: start;
}

/* Los títulos solo orientan a los lectores de pantalla (Collins no los muestra) */
.site-menu__title,
.site-menu__label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.site-menu__main {
  display: grid;
  gap: var(--space-xl);
  align-content: start;
}

.site-menu__list {
  display: grid;
  padding: 0;
  list-style: none;
}

/* Como en Collins: el enlace de la página actual va encendido y los demás
   apagados. Al pasar el ratón, se enciende el que tiene debajo y se apagan
   los demás (también el de la página actual). Siempre la misma letra: nada
   de cursiva ni de cambios de texto, solo opacidad.
   El apagado va en el <li>: el <a> lleva opacidad en línea mientras entra
   (revealText). */
.site-menu__list > li {
  transition: opacity var(--duration-base) var(--ease-out);
}

.site-menu__list:has([aria-current="page"]) > li:not(:has([aria-current="page"])) {
  opacity: 0.35;
}

@media (hover: hover) and (pointer: fine) {
  .site-menu__list:hover > li:hover {
    opacity: 1;
  }

  .site-menu__list:hover > li:not(:hover) {
    opacity: 0.35;
  }
}

/* Con teclado, lo mismo con el foco visible (no con el foco que el menú
   pone en el primer enlace al abrirse con el ratón) */
.site-menu__list:has(:focus-visible) > li:has(:focus-visible) {
  opacity: 1;
}

.site-menu__list:has(:focus-visible) > li:not(:has(:focus-visible)) {
  opacity: 0.35;
}

/* Bloque del ancho de su texto, no inline-block: mientras se revela, el texto
   va partido en una máscara con overflow: clip y WebKit (Safari) le cambiaba
   la línea base al enlace; la fila crecía 6,5 px y, al acabar, se encogía */
.site-menu__link {
  display: block;
  width: fit-content;
  font-size: var(--text-menu);
  line-height: 1.3; /* interlineado amplio, como Collins */
  text-decoration: none;
}

/* Fila de acciones: «Pedir mi CV» y enlaces de texto */
.site-menu__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s) var(--space-l);
}

.site-menu__action {
  color: inherit;
  font-weight: var(--weight-text-strong);
  text-decoration: none;
}

.site-menu__action:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}

/* Con ratón el correo sale dentro del botón: el enlace suelto sobra */
@media (hover: hover) and (pointer: fine) {
  .site-menu__email {
    display: none;
  }
}

/* Botón que cambia de texto al pasar el ratón (o con el foco del teclado).
   Las dos etiquetas ocupan la misma celda; sin JS el botón tiene el ancho de
   la más larga. Con JS (menu.js) mide las dos y el ancho pasa de una a otra
   con el muelle soft. Excepción a «solo transform y opacity»: el ancho de
   este botón (aprobada por Andreu en la fase 6). */
.btn--swap {
  display: inline-grid;
  /* Una sola columna del ancho del botón: cada etiqueta se centra en ella
     aunque sea más ancha (la que sobra queda oculta) */
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  overflow: hidden;
  width: var(--swap-width, auto);
  transition: width var(--spring-soft-duration) var(--spring-soft), scale var(--spring-soft-duration) var(--spring-soft), background-color var(--duration-fast) var(--ease-out);
}

.btn--swap > span {
  grid-area: 1 / 1;
  white-space: nowrap;
  transition: translate var(--spring-soft-duration) var(--spring-soft), opacity var(--duration-fast) var(--ease-out);
}

/* En táctil (y sin JS) solo se ve la etiqueta. El correo espera a la
   izquierda, a media altura, y entra hacia el centro con un fundido; la
   etiqueta sale hacia la derecha */
.btn__alt {
  translate: -40% 0;
  opacity: 0;
}

@media (hover: hover) and (pointer: fine) {
  .btn--swap:is(:hover, :focus-visible) {
    width: var(--swap-width-alt, auto);
  }

  .btn--swap:is(:hover, :focus-visible) .btn__label {
    translate: 40% 0;
    opacity: 0;
  }

  .btn--swap:is(:hover, :focus-visible) .btn__alt {
    translate: 0 0;
    opacity: 1;
  }

  /* Este botón no crece al pasar el ratón: ya cambia de ancho */
  .btn--swap.btn--primary:hover {
    scale: 1;
  }
}

/* Destacados en filas, como las «Story» de Collins: miniatura cuadrada,
   qué es y nombre, y una flecha a la derecha */
.site-menu__featured {
  display: grid;
  align-content: start;
}

.menu-cards {
  display: grid;
  gap: var(--space-s);
  padding: 0;
  list-style: none;
}

/* Cada destacado, en un recuadro sutil (algo más claro que la tinta) que se
   aclara al pasar el ratón, con la misma suavidad que los títulos. Todo el
   recuadro es el enlace. */
.menu-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-m);
  align-items: center;
  padding: var(--space-s);
  padding-right: var(--space-m);
  border-radius: var(--radius-l);
  background: rgb(247 246 242 / 0.05);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--duration-base) var(--ease-out);
}

.menu-card:hover {
  background: rgb(247 246 242 / 0.1);
}

.menu-card__media {
  display: block;
  overflow: hidden;
  width: clamp(5rem, 4rem + 3vw, 7rem);
  aspect-ratio: 1;
  border-radius: var(--radius-m);
  background: var(--card-color);
  transition: scale var(--spring-soft-duration) var(--spring-soft);
}

.menu-card__text {
  display: grid;
  gap: var(--space-3xs);
}

/* Fila superior: qué es y, a la derecha, la flecha */
.menu-card__top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-s);
}

.menu-card__kind {
  font-size: var(--text-small);
  color: var(--color-muted);
}

.menu-card__name {
  font-weight: var(--weight-text-strong);
  line-height: 1.3;
}

.menu-card__arrow {
  flex: none;
  width: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: translate var(--spring-soft-duration) var(--spring-soft);
}

.menu-card:hover .menu-card__media {
  scale: 1.03;
}

.menu-card:hover .menu-card__arrow {
  translate: 0.25rem 0;
}

/* «Otros trabajos →»: al pie de los destacados, como los enlaces secundarios */
.site-menu__other {
  display: inline-flex;
  align-items: center;
  justify-self: start;
  gap: var(--space-2xs);
  margin-top: var(--space-m);
}

.site-menu__other:hover .menu-card__arrow {
  translate: 0.25rem 0;
}

.menu-card:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

/* --- Capa a pantalla completa (con JS) ----------------------------------- */

.site-menu.is-overlay {
  /* Sobre tinta: gris secundario y botón invertidos */
  --color-muted: var(--color-muted-on-ink);
  position: fixed;
  inset: 0;
  z-index: 99; /* bajo el header, que sigue visible con la ✕ */
  overflow-x: clip; /* las filas de destacados entran desde fuera por la derecha */
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Arriba, como Collins: algo más de un quinto de la pantalla, y nunca
     menos que el header (2 × --space-s + 44 px del icono) más aire. Abajo,
     el mismo margen. */
  /* Centrado en vertical (sin quedar debajo del header): arriba, el header
     y aire; abajo, el mismo margen */
  display: grid;
  align-content: safe center;
  padding: calc(var(--space-s) * 2 + 44px + var(--space-l)) var(--page-inline);
  background: var(--color-ink);
  color: var(--color-paper);
  /* Tinta también por encima del panel: si al abrirse el muelle lo baja un
     poco de más, lo que asoma arriba es tinta y no una franja de la página */
  box-shadow: 0 -50vh 0 0 var(--color-ink);
}

.site-menu.is-overlay[hidden] {
  display: none;
}

/* Al abrirse, el foco va al menú entero (tabindex="-1"), no a un enlace */
.site-menu:focus {
  outline: none;
}

/* En la capa, todo algo más grande: títulos, fila de acciones y destacados
   (cabe sin scroll en 1280 × 720) */
.site-menu.is-overlay .site-menu__link {
  font-size: clamp(2.75rem, 2.083rem + 2.963vw, 4.75rem); /* 44 → 76 px */
}

.site-menu.is-overlay .site-menu__actions {
  font-size: 1.125rem;
}

/* La miniatura también depende del alto: en 1280 × 720 todo cabe sin scroll */
.site-menu.is-overlay .menu-card__media {
  width: min(clamp(5.5rem, 4.25rem + 3.5vw, 8rem), 12vh);
  width: min(clamp(5.5rem, 4.25rem + 3.5vw, 8rem), 12svh); /* alto pequeño: no cambia con las barras del navegador */
}

.site-menu.is-overlay .menu-card__name {
  font-size: var(--text-lead);
}

.site-menu.is-overlay .menu-card__kind {
  font-size: var(--text-body);
}

.site-menu.is-overlay .btn {
  --btn-bg: var(--color-paper);
  --btn-fg: var(--color-ink);
}

/* Como en Collins, el contenido del menú queda más hacia dentro que el
   header: una columna centrada de 64rem como máximo (en pantallas grandes,
   unos 450 px de margen a cada lado). En móvil y tablet, los márgenes normales. */
.site-menu.is-overlay .site-menu__inner {
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
}

@media (min-width: 768px) {
  .site-menu__inner {
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 23rem);
  }

  .site-menu.is-overlay .site-menu__inner {
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 28rem);
  }
}

/* Cuadrado de «Otros trabajos»: solo en móvil */
.menu-cards__other {
  display: none;
}

/* Menú en móvil: todo en una pantalla. Los destacados son una fila de cuatro
   cuadrados iguales (los tres proyectos y «Otros trabajos»), sin texto: el
   nombre va en su aria-label. El enlace «Otros trabajos →» sobra. En
   escritorio, nada de esto cambia. */
@media (max-width: 767px) {
  /* Arriba, no centrado: en Safari el alto de la capa cambia mientras se
     abre (la barra de abajo aparece al bloquear el scroll de la página) y,
     centrado, todo se recolocaba al acabar */
  .site-menu.is-overlay {
    align-content: start;
  }

  .site-menu.is-overlay .site-menu__actions {
    font-size: var(--text-body);
  }

  .site-menu .menu-cards {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-xs);
  }

  .site-menu .menu-cards__other {
    display: block;
  }

  .site-menu .menu-card {
    display: block;
    padding: 0;
    border-radius: var(--radius-m);
    background: none;
  }

  .site-menu .menu-card .menu-card__media {
    width: 100%;
    aspect-ratio: 1; /* reservado: no cambia al cargar la imagen */
    transition: opacity var(--duration-fast) var(--ease-out);
  }

  /* Estado sutil al tocar */
  .site-menu .menu-card:active .menu-card__media {
    opacity: 0.7;
  }

  .site-menu .menu-card__text,
  .site-menu .site-menu__other {
    display: none;
  }
}

/* Lenis (modules/smooth-scroll.js): reglas de su lenis.css */
html.lenis,
html.lenis body {
  height: auto;
}

.lenis:not(.lenis-autoToggle).lenis-stopped {
  overflow: clip;
}

.lenis [data-lenis-prevent],
.lenis [data-lenis-prevent-wheel],
.lenis [data-lenis-prevent-touch],
.lenis [data-lenis-prevent-vertical],
.lenis [data-lenis-prevent-horizontal] {
  overscroll-behavior: contain;
}

.lenis.lenis-smooth iframe {
  pointer-events: none;
}

/* La galería solo contiene el scroll en horizontal: en vertical, el gesto
   tiene que seguir bajando la página */
.lenis .gallery {
  overscroll-behavior-y: auto;
}

/* Con el menú abierto: la página no se mueve por detrás y el header pasa a
   papel. La raíz se pinta de tinta para que el hueco reservado de la barra de
   scroll (scrollbar-gutter) no quede como una franja clara. */
html {
  scrollbar-gutter: stable;
}

html.menu-open {
  overflow: hidden;
  background: var(--color-ink);
}

html.menu-open .site-header {
  --header-fg: var(--color-paper);
}

/* --------------------------------------------------------------------------
   Galería de proyectos (partials/gallery.html, modules/gallery.js)
   Como Collins: scroll horizontal nativo, finito, que empieza alineado con
   la columna de contenido y se sale por la derecha. Tarjetas del mismo ancho
   y alturas distintas (entre 3:4 y 2:3), alineadas por abajo, con 16 px de
   hueco. Dedo, trackpad y teclado son nativos; con el ratón se arrastra
   (gallery.js). Sin JS funciona igual, salvo el arrastre con el ratón.
   -------------------------------------------------------------------------- */

.gallery {
  --card-width: clamp(14rem, 8rem + 17vw, 25rem);
  /* Nunca más ancha que su contenedor (en una rejilla o un flex se estiraría
     hasta el ancho de la tira) */
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  /* Solo scroll horizontal: si la galería pudiera desplazarse aunque fuera
     2 px en vertical, el navegador le daría a ella el gesto vertical del
     dedo y la página no bajaría */
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-padding-inline: var(--page-inline);
  /* Sin barra visible, como Collins */
  scrollbar-width: none;
}

.gallery::-webkit-scrollbar {
  display: none;
}

.gallery__track {
  display: flex;
  align-items: flex-end; /* alineadas por abajo */
  gap: var(--space-s);
  width: max-content;
  margin: 0;
  /* Arriba, aire para la inclinación al pasar el ratón (la parte de arriba
     de la tarjeta crece un poco) y para el contorno del foco; abajo, el sitio
     del nombre y la frase de cada tarjeta (van colocados bajo el color, fuera
     del flujo, para que nada se mueva al aparecer) */
  padding: var(--space-xl) var(--page-inline) 6.5rem;
  list-style: none;
}

.gallery__item {
  flex: none;
}

/* Proporciones que varían de una tarjeta a la siguiente (entre 3:4 y 2:3),
   alineadas por abajo. La portada se adapta a cada una (object-fit: cover) */
.gallery__item:nth-child(5n + 1) { --ratio: 2 / 3; }
.gallery__item:nth-child(5n + 2) { --ratio: 3 / 4; }
.gallery__item:nth-child(5n + 3) { --ratio: 7 / 10; }
.gallery__item:nth-child(5n + 4) { --ratio: 2 / 3; }
.gallery__item:nth-child(5n + 5) { --ratio: 5 / 7; }

/* Arrastre con el ratón (gallery.js): mano abierta y cerrada, y sin
   seleccionar texto ni arrastrar enlaces o imágenes */
.gallery.is-draggable {
  cursor: grab;
}

.gallery.is-grabbing {
  cursor: grabbing;
}

.gallery.is-draggable .gallery__track {
  user-select: none;
  -webkit-user-select: none;
}

.gallery.is-draggable a,
.gallery.is-draggable img {
  -webkit-user-drag: none;
}

.gallery.is-grabbing a {
  cursor: grabbing;
}

/* Hasta la entrada «carta que se levanta», las tarjetas esperan ocultas.
   Misma red de seguridad que [data-reveal]: si el JS falla, aparecen a los 3 s. */
.js .gallery:not(.is-entered) .gallery-card {
  opacity: 0;
  animation: reveal-fallback 0s 3s forwards;
}

.gallery-card {
  position: relative;
  display: block;
  width: var(--card-width);
  perspective: 1500px; /* para la inclinación al pasar el ratón, como Collins */
  color: inherit;
  text-decoration: none;
  /* La entrada gira y escala desde abajo, como una carta que se levanta */
  transform-origin: 50% 100%;
}

/* Placeholder: color del proyecto + nombre. La portada real irá aquí. */
.gallery-card__media {
  display: grid;
  place-items: center;
  aspect-ratio: var(--ratio, 3 / 4);
  border-radius: var(--radius-l);
  background: var(--card-color);
  color: var(--card-text, var(--project-text));
  font-size: var(--text-lead);
  transform-origin: 50% 100%;
  transition: color var(--duration-fast) var(--ease-out); /* el nombre, al acabar una transición entre páginas */
}

/* Al pasar el ratón, la tarjeta se inclina 10° desde su base, como Collins
   (misma inclinación pongas el ratón donde lo pongas; 0,8 s con una curva
   que frena mucho al final) */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .gallery-card__media {
    transition: transform 0.8s cubic-bezier(0.19, 1, 0.22, 1), color var(--duration-fast) var(--ease-out);
  }

  .gallery-card:hover .gallery-card__media {
    transform: rotateX(-10deg);
  }
}

/* Con portada (.has-image): la imagen llena la tarjeta (sea cual sea su
   proporción) y se recorta con su encuadre por proyecto: --cover-position en
   el style de la tarjeta (object-position; por defecto, centrada) */
.gallery-card__media:has(picture),
.menu-card__media {
  position: relative;
  overflow: hidden;
}

/* La imagen, sobre toda la tarjeta: un height: 100% no se resuelve dentro de
   una caja medida con aspect-ratio (la imagen se saldría por abajo y el
   encuadre quedaría arriba) */
.gallery-card__media picture,
.menu-card__media picture {
  position: absolute;
  inset: 0;
}

.gallery-card__media picture,
.gallery-card__media img,
.menu-card__media picture,
.menu-card__media img {
  display: block;
  width: 100%;
  height: 100%;
}

.gallery-card__media img,
.menu-card__media img {
  object-fit: cover;
  object-position: var(--cover-position, 50% 50%);
}

/* Eurodrive (marfil): un borde muy sutil para que no se pierda en el papel.
   Va en una capa encima: un box-shadow interior quedaría debajo de la
   portada. (Team3Fitness lo lleva en papel sobre el grafito: --border-card,
   más abajo.) */
.gallery-card--light .gallery-card__media,
.gallery-card--dark .gallery-card__media {
  position: relative;
}

.gallery-card--light .gallery-card__media::after,
.gallery-card--dark .gallery-card__media::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--border-card, rgb(15 15 15 / 0.09));
  pointer-events: none;
}

.gallery-card--dark {
  --border-card: transparent;
}

.gallery-card:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
  border-radius: var(--radius-l);
}

/* Nombre y frase bajo la tarjeta, en el sitio reservado por la tira */
.gallery-card__caption {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  display: grid;
  gap: var(--space-3xs);
  padding-top: var(--space-xs);
}

.gallery-card__name {
  font-weight: var(--weight-text-strong);
}

.gallery-card__phrase {
  font-size: var(--text-small);
  color: var(--color-muted);
}

/* Con ratón: aparecen al pasar por encima (o con el foco del teclado), y
   nunca mientras la tira se mueve (.is-moving, gallery.js). En táctil y sin
   JS están siempre a la vista. */
@media (hover: hover) and (pointer: fine) {
  .gallery-card__caption {
    opacity: 0;
    translate: 0 -0.25rem;
    transition: opacity var(--duration-fast) var(--ease-out), translate var(--spring-soft-duration) var(--spring-soft);
  }

  .gallery:not(.is-moving) .gallery-card:is(:hover, :focus-visible) .gallery-card__caption {
    opacity: 1;
    translate: 0 0;
  }
}

/* --------------------------------------------------------------------------
   Home, como Collins (CLAUDE.md): encabezado, showreel, galería de proyectos,
   segunda imagen y «Otros trabajos» (fase 9) y footer (fase 10).
   -------------------------------------------------------------------------- */

/* Color de página por secciones (page-color.js), como Collins al llegar a
   sus case studies: cuando empiezan las tarjetas de la sección de proyectos
   de la home, el fondo de toda la página pasa al grafito con un fundido
   corto. Dentro de ella, con el mismo fundido (--page-mix), el texto pasa de
   tinta a papel, el gris secundario se aclara y el botón principal se
   invierte (papel con texto en tinta). AA en los dos estados. */
[data-page-color] {
  --mix: calc(var(--page-mix, 0) * 100%);
  color: color-mix(in srgb, var(--color-paper) var(--mix), var(--color-ink));
  --color-muted: color-mix(in srgb, var(--color-muted-on-graphite) var(--mix), var(--color-muted-on-paper));
}

/* Team3Fitness (casi negra): sobre el grafito, el mismo borde sutil que
   Eurodrive sobre el papel, en papel; aparece con el fundido */
[data-page-color] .gallery-card--dark {
  --border-card: rgb(247 246 242 / calc(var(--page-mix, 0) * 0.09));
}

/* El botón define sus colores en sí mismo: se mezclan aquí */
[data-page-color] .btn--primary {
  --btn-bg: color-mix(in srgb, var(--color-paper) var(--mix), var(--color-ink));
  --btn-fg: color-mix(in srgb, var(--color-ink) var(--mix), var(--color-paper));
}

/* Encabezado de sección, como los de Collins: etiqueta pequeña, frase en
   Garamond y botón, alineados con la columna */
.section-head {
  display: grid;
  justify-items: start;
  gap: var(--space-s);
  padding-inline: var(--page-inline);
}

.section-head__label {
  font-size: var(--text-body);
  font-weight: var(--weight-text);
  color: var(--color-muted);
}

.section-head__title {
  max-width: 14em;
  margin-bottom: var(--space-xs);
  font-size: clamp(1.75rem, 1.5rem + 1.111vw, 2.25rem); /* 28 → 36 px (Collins: 36 px) */
  line-height: 1.05;
  text-wrap: balance;
}

/* Sin margen abajo: «Otros trabajos» llega justo hasta el footer, como Collins */
.home {
  padding-bottom: 0;
}

/* Encabezado, como Collins: la frase, la línea de rol y la fila de proyectos
   centradas en la primera pantalla, con mucho aire; el borde de arriba del
   showreel asoma al final de la pantalla */
.hero {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--space-l);
  min-height: 92vh;
  min-height: 92svh;
  padding: calc(var(--space-s) * 2 + 44px) var(--page-inline) var(--space-xl); /* arriba, el header */
  text-align: center;
}

/* Con la firma (cambio temporal mientras no hay showreel): la firma, grande
   y centrada, y el texto debajo ocupan juntos la primera pantalla. La firma
   mide lo que deja el alto de la pantalla menos el texto (su proporción es
   1384 × 630), sin pasar de 52 rem ni de la columna. */
.hero--signature {
  min-height: 100vh;
  min-height: 100svh;
  padding-bottom: var(--space-2xl);
}

.hero__signature {
  width: min(52rem, 100%, (100svh - 25rem) * 2.2);
  margin-bottom: var(--space-s);
  color: var(--color-ink);
}

.hero__signature svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* Con JS, escondida hasta que hero-signature.js la dibuja (.is-drawing); si
   el JS falla, a los 3 s se ve entera. Sin JS o con movimiento reducido,
   entera desde el principio. */
@media (prefers-reduced-motion: no-preference) {
  .js .hero__signature:not(.is-drawing) {
    opacity: 0;
    animation: reveal-fallback 0s 3s forwards;
  }
}

/* En móvil, la firma más pequeña, centrada y con aire a los lados; y sin la
   lista de proyectos, para que el encabezado respire (en escritorio, igual) */
@media (max-width: 767px) {
  .hero__signature {
    width: min(100% - 3rem, 20rem);
    margin-bottom: 0;
  }

  .hero__projects {
    display: none;
  }
}

/* Más pequeña que las demás frases protagonistas: en escritorio cabe holgada
   en una línea (56 px a 1440 px, también con un 30 % más de texto); en
   móvil, en dos equilibradas. Tracking algo más cerrado que la display. */
.hero__title {
  font-size: var(--text-title);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-xs) var(--space-m);
  font-size: var(--text-small);
  color: var(--color-muted);
}

.hero__meta .status {
  color: var(--color-ink);
}

/* Los cinco proyectos, pequeños y discretos (como la fila de premios de
   Collins), separados por «·» y enlazados a su case study */
.hero__projects ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-small);
  color: var(--color-muted);
}

/* El punto va detrás de cada nombre (no delante): si la fila se parte en
   móvil, la segunda línea no empieza con un punto suelto */
.hero__projects li:not(:last-child)::after {
  content: '·';
  content: '·' / ''; /* sin leerlo en voz alta */
  margin-inline: var(--space-xs);
}

.hero__projects a {
  color: inherit;
  text-decoration: underline transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: color var(--duration-fast) var(--ease-out), text-decoration-color var(--duration-fast) var(--ease-out);
}

.hero__projects a:hover,
.hero__projects a:focus-visible {
  color: var(--color-ink);
  text-decoration-color: currentColor;
}

/* Showreel, como Collins: centrado, en 16:9 y con las esquinas redondeadas;
   nunca a todo el ancho. Su caja mide el 70 % de la ventana (en móvil, la
   columna) y con JS empieza escalado a --showreel-start (el 50 % de la
   ventana; en móvil, el 70 % de la columna) desde su borde de arriba, que
   asoma al final de la primera pantalla; showreel.js lo hace crecer con el
   scroll. Sin JS y con movimiento reducido, a su tamaño máximo. */
.showreel[hidden] {
  display: none;
}

.showreel {
  --showreel-start: 0.7143; /* 50 % / 70 % (igual que FEEL en showreel.js) */
  margin-bottom: var(--space-4xl);
  padding-inline: var(--page-inline);
}

.showreel__frame {
  width: min(70vw, 100%);
  margin-inline: auto;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  clip-path: inset(0% 0% 0% round var(--radius-l));
  transform-origin: 50% 0;
}

.js .showreel__frame {
  transform: scale(var(--showreel-start));
}

@media (max-width: 767px) {
  .showreel {
    --showreel-start: 0.7;
  }

  .showreel__frame {
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .showreel__frame {
    transform: none;
  }
}

.showreel__placeholder,
.showreel__video {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.showreel__placeholder {
  background: var(--color-showreel);
  color: var(--color-ink);
  font-size: var(--text-title);
}

/* «Otros trabajos», como «Arts & Culture» de Collins: una imagen de lado a
   lado y de toda la altura, con el texto encima abajo a la izquierda (alineado
   con la columna) y las barras de progreso debajo. Entra bajando, con un
   fundido recortada a la columna, y se abre hasta los bordes (stories.js).
   Las imágenes y los textos cambian con fundido por CSS al mover
   .is-current; el JS solo mueve las clases y las barras. Sin JS: la primera imagen y los cuatro textos, uno debajo de otro. */
/* La sección no se mueve (header.js y stories.js miden su posición); el
   recorte y el desplazamiento van en la capa de dentro, .stories__frame. */
.stories {
  display: grid;
  min-height: 100vh;
  min-height: 100svh;
  margin-top: var(--space-2xl);
  color: var(--color-paper);
}

.stories__frame {
  position: relative;
  display: grid;
  overflow: hidden;
  background: var(--color-ink);
}

.stories__images {
  position: absolute;
  inset: 0;
}

/* Collins: la imagen nueva aparece encima con fundido mientras se asienta
   (de 1,1 a su tamaño); la anterior se queda quieta debajo hasta que la tapa */
.stories__image {
  position: absolute;
  inset: 0;
  background: var(--story-color);
  opacity: 0;
  scale: 1.1;
  transition: opacity var(--duration-slower) var(--ease-out), scale 1.6s var(--ease-out);
}

.stories__image.is-previous {
  z-index: 1;
  opacity: 1;
  scale: 1;
  transition: none;
}

.stories__image.is-current {
  z-index: 2;
  opacity: 1;
  scale: 1;
}

/* Velo de tinta que sube desde abajo, bajo el texto (blanco): con las fotos
   claras (el cartel, el hormigón) el texto no se leía. Fuerte donde va el
   texto y transparente arriba; por encima de las imágenes y por debajo de las
   zonas para pulsar y del texto. */
.stories__images::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(to top, rgb(15 15 15 / 0.75) 0%, rgb(15 15 15 / 0.58) 35%, rgb(15 15 15 / 0.22) 60%, transparent 80%);
}

.stories__image picture,
.stories__image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Encuadre de cada diapositiva (--story-position en el style de la imagen) y,
   en móvil, el suyo si lo tiene (--story-position-mobile): los rótulos usan en
   móvil una foto vertical con el nombre de la empresa a la izquierda */
.stories__image img {
  object-position: var(--story-position, 50% 50%);
}

@media (max-width: 767px) {
  .stories__image img {
    object-position: var(--story-position-mobile, var(--story-position, 50% 50%));
  }
}

/* Zonas para pulsar la imagen: un tercio atrás, dos tercios adelante */
.stories__controls {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  grid-template-columns: 1fr 2fr;
}

.stories__control {
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.no-js .stories__controls {
  display: none;
}

/* El texto deja pasar los clics a la imagen; el botón y las barras, no */
.stories__shell {
  position: relative;
  z-index: 4;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--space-xl);
  padding: var(--space-4xl) var(--page-inline) var(--space-m);
}

.stories__content {
  display: grid;
  justify-items: start;
  gap: var(--space-s);
  max-width: 34rem;
}

/* Cabecera de «Otros trabajos», encima de la imagen, como la de Proyectos */
.stories-head {
  margin-top: var(--space-3xl);
}

.stories-head + .stories {
  margin-top: var(--space-l);
}

/* Los textos ocupan la misma celda: la caja mide lo que el más largo */
.stories__texts {
  display: grid;
  margin-bottom: var(--space-xs);
}

.stories__text {
  display: grid;
  gap: var(--space-xs);
  grid-area: 1 / 1;
  align-content: start; /* pegado a la etiqueta, como Collins */
}

.stories__title {
  font-size: var(--text-title);
  line-height: var(--leading-title);
  text-wrap: balance;
}

.stories__body {
  max-width: 28rem;
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* Con JS: solo se ve el texto actual. Al cambiar, el que se va desaparece
   rápido y el nuevo sube un poco al aparecer (muelle soft). */
.js .stories__text {
  transition: opacity var(--duration-base) var(--ease-out) var(--duration-fast), translate var(--spring-soft-duration) var(--spring-soft) var(--duration-fast), visibility 0s;
}

.js .stories__text:not(.is-current) {
  visibility: hidden;
  opacity: 0;
  translate: 0 0.75rem;
  transition: opacity var(--duration-fast) var(--ease-in), translate 0s var(--duration-fast), visibility 0s var(--duration-fast);
}

.stories__cta,
.stories__nav {
  pointer-events: auto;
}

.stories .btn {
  --btn-bg: var(--color-paper);
  --btn-fg: var(--color-ink);
}

/* Barras de progreso: cuatro rayas de 4 px con los extremos redondeados; la
   actual se va llenando (stories.js, con clip-path para que el relleno también
   sea redondeado), las anteriores están llenas. Área de pulsación de 44 px. */
.stories__nav {
  display: flex;
  gap: var(--space-2xs);
}

.stories__progress {
  position: relative;
  width: 4rem;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.stories__progress::before,
.stories__bar {
  position: absolute;
  inset: calc(50% - 2px) 0 auto;
  height: 4px;
  border-radius: var(--radius-pill);
}

.stories__progress::before {
  content: "";
  background: color-mix(in srgb, var(--color-paper) 30%, transparent);
}

.stories__bar {
  background: var(--color-paper);
  clip-path: inset(0% 100% 0% 0% round 4px);
}

.stories__progress.is-done .stories__bar {
  clip-path: none;
}

.stories__progress:focus-visible {
  outline: 2px solid var(--color-paper);
  outline-offset: 2px;
  border-radius: var(--radius-s);
}

/* Sin JS: los textos uno debajo de otro y sin barras */
.no-js .stories__texts {
  gap: var(--space-l);
}

.no-js .stories__text {
  grid-area: auto;
}

.no-js .stories__nav {
  display: none;
}

/* --------------------------------------------------------------------------
   Sobre mí (fases 11–13): página clara y con prioridad al texto
   -------------------------------------------------------------------------- */

/* Encabezado a dos columnas: texto (2/3) y foto vertical 3:4 (1/3), alineada
   arriba con el texto. En móvil, el texto primero y la foto debajo, a todo el
   ancho y con altura moderada. */
.about-hero {
  display: grid;
  gap: var(--space-2xl);
  padding: calc(var(--space-s) * 2 + 44px + var(--space-3xl)) var(--page-inline) 0; /* el header y aire */
}

.about-hero__text {
  display: grid;
  align-content: start;
  justify-items: start;
}

.about-hero__title {
  max-width: 12em;
  margin-top: var(--space-s);
  font-size: var(--text-hero);
  line-height: var(--leading-display);
  text-wrap: balance;
}

.about-hero__label {
  margin-top: var(--space-3xl);
  font-size: var(--text-body);
  font-weight: var(--weight-text);
  color: var(--color-muted); /* como todas las etiquetas de sección */
}

.about-hero__intro + .about-hero__intro {
  margin-top: var(--space-s);
}

/* «Quién soy»: un párrafo largo, en Inter de lectura */
.about-hero__intro {
  max-width: 38rem;
  margin-top: var(--space-m);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.about-hero__photo {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-l);
}

.about-hero__photo picture {
  display: block;
  height: 100%;
}

/* En móvil (4:3) la foto se recorta por arriba y por abajo: encuadrada
   arriba, del pelo a la barbilla */
.about-hero__photo img {
  object-position: 50% 20%;
}

@media (min-width: 768px) {
  .about-hero {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    align-items: start;
    gap: var(--space-3xl);
  }

  .about-hero__photo {
    aspect-ratio: 3 / 4;
  }

  .about-hero__photo img {
    object-position: 50% 50%;
  }
}

.about-hero__placeholder,
.about-hero__photo img {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.about-hero__placeholder {
  background: var(--color-about-photo);
  color: var(--color-muted);
  font-size: var(--text-title);
}

/* Secciones de texto («Cómo trabajo», «Experiencia», «Formación»): etiqueta a
   la izquierda y texto a la derecha, como «Overview» de Collins (en móvil, la
   etiqueta encima) */
.about-section {
  display: grid;
  gap: var(--space-m);
  padding: var(--space-4xl) var(--page-inline) 0;
}

.about-section__label {
  font-size: var(--text-body);
  font-weight: var(--weight-text);
  color: var(--color-muted); /* como todas las etiquetas de sección */
}

.about-section__body {
  display: grid;
  gap: var(--space-l);
}

.about-section__text {
  max-width: 21em;
  font-size: clamp(1.75rem, 1.5rem + 1.111vw, 2.25rem); /* 28 → 36 px (Collins: 36 px) */
  line-height: 1.05;
}

/* Dos columnas desde 1024 px (fase 21): entre 768 y 1024, el texto en Garamond
   se quedaba en una columna de tres o cuatro palabras por línea */
@media (min-width: 1024px) {
  .about-section {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    padding-inline: calc(var(--page-inline) + var(--space-3xl));
  }
}

/* Filas de experiencia y formación: título en Garamond, el dato en gris y la
   explicación debajo; sin líneas divisorias, separadas por aire */
.about-list {
  display: grid;
  gap: var(--space-xl);
  margin: 0;
  padding: 0;
  list-style: none;
}

.about-item {
  display: grid;
  gap: var(--space-2xs);
}

.about-item__title {
  font-size: clamp(1.5rem, 1.333rem + 0.741vw, 2rem); /* 24 → 32 px */
  line-height: var(--leading-title);
}

.about-item__meta {
  font-size: var(--text-small);
  color: var(--color-muted);
}

.about-item__text {
  max-width: 34rem;
  margin-top: var(--space-3xs);
}

/* Filas con imagen ([data-row-preview], row-preview.js). Por defecto (táctil,
   movimiento reducido y sin JS) la miniatura va dentro de la fila, a la
   derecha. Con ratón (html.has-row-preview) la miniatura se oculta: la imagen
   flotante sigue al cursor, la fila activa se adelanta y las demás se apagan. */
/* Sin ratón (táctil, y sin JS), la miniatura va encima de la fila y el texto
   ocupa todo el ancho; con ratón la sustituye la imagen que sigue al cursor */
.about-item--preview {
  grid-template-columns: minmax(0, 1fr);
}

.about-item__thumb {
  display: grid;
  place-items: center;
  order: -1;
  margin-bottom: var(--space-2xs);
  width: 4.5rem;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  padding: var(--space-3xs);
  border-radius: var(--radius-s);
  background: var(--thumb-color);
  color: var(--color-paper);
  text-align: center;
}

.about-item__thumb-name {
  font-size: 0.8125rem;
  line-height: 1.1;
}

.about-item__thumb img,
.row-preview__layer img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Con la imagen real, sin el margen del placeholder y a sangre */
.about-item__thumb:has(picture),
.row-preview__layer:has(picture) {
  padding: 0;
}

.about-item__thumb picture,
.row-preview__layer picture {
  display: block;
  width: 100%;
  height: 100%;
}

.has-row-preview .about-item--preview {
  grid-template-columns: minmax(0, 1fr);
}

.has-row-preview .about-item__thumb {
  display: none;
}

.has-row-preview [data-row-preview] > li {
  transition: opacity var(--duration-base) var(--ease-out), translate var(--spring-soft-duration) var(--spring-soft);
}

.has-row-preview [data-row-preview].has-active > li:not(.is-active) {
  opacity: 0.35;
}

.has-row-preview [data-row-preview] > li.is-active {
  translate: 0.75rem 0;
}

/* Imagen flotante: pequeña (160 px, 4:5) y arriba a la derecha del cursor,
   con su esquina inferior izquierda a 12 px de la punta: nunca tapa la fila
   que se está leyendo. El origen del giro y de la escala es esa esquina. */
.row-preview {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 40;
  width: 10rem;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius-s);
  translate: 12px calc(-100% - 12px);
  transform-origin: 0 100%;
  opacity: 0;
  scale: 0.85;
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-out), scale var(--spring-soft-duration) var(--spring-soft);
}

.row-preview.is-visible {
  opacity: 1;
  scale: 1;
}

.row-preview__layer {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-s);
  background: var(--thumb-color);
  color: var(--color-paper);
  text-align: center;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}

.row-preview__layer.is-active {
  opacity: 1;
}

.row-preview__layer .about-item__thumb-name {
  font-size: 1rem;
}

/* Otras experiencias (more.js): un botón de texto pequeño y gris y, debajo,
   filas compactas de una línea, también en gris. La altura del panel la anima
   el JS (sin createLayout); cerrado, el panel lleva hidden. Sin JS, abiertas
   y sin botón. */
.about-more {
  margin-top: var(--space-l);
}

.about-more__toggle {
  padding: var(--space-3xs) 0;
  border: 0;
  background: none;
  color: var(--color-muted);
  font: inherit;
  font-size: var(--text-small);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out);
}

.about-more__toggle:hover {
  color: var(--color-ink);
}

.about-more__toggle:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}

.about-more__toggle[aria-expanded="true"] .about-more__open,
.about-more__toggle[aria-expanded="false"] .about-more__close {
  display: none;
}

.no-js .about-more__toggle {
  display: none;
}

.about-more__panel {
  overflow: hidden;
}

.about-more__list {
  display: grid;
  gap: var(--space-2xs);
  margin: 0;
  padding: var(--space-s) 0 0;
  list-style: none;
  font-size: var(--text-small);
  color: var(--color-muted);
}

.about-more__name {
  font-weight: var(--weight-text-strong);
}

/* Herramientas e idiomas: tarjetas fijas a todo el ancho de la sección, en
   2 × 2 (en una fila de 4, Diseño y Web necesitarían tres líneas de
   píldoras) y apiladas en móvil. Las píldoras se ven siempre. */
.about-section--tools .about-section__body {
  grid-column: 1 / -1;
}

.tool-cards {
  display: grid;
  gap: var(--space-s);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 768px) {
  .tool-cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.tool-card {
  display: grid;
  align-content: start;
  gap: var(--space-l);
  padding: var(--space-m);
  border-radius: var(--radius-l);
  background: color-mix(in srgb, var(--color-ink) 4%, var(--color-paper));
}

.tool-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-s);
}

.tool-card__icon {
  flex: none;
  width: 3rem;
  height: 3rem;
  overflow: visible;
}

.tool-card__title {
  font-size: clamp(1.375rem, 1.292rem + 0.37vw, 1.625rem); /* 22 → 26 px */
  line-height: var(--leading-title);
}

.tool-card__pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.tool-pill {
  padding: 0.375rem 0.875rem;
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-ink) 22%, transparent);
  font-size: var(--text-small);
  white-space: nowrap;
  transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}

/* Al pasar el ratón, la píldora se vuelve negra */
.tool-pill:hover {
  background: var(--color-ink);
  color: var(--color-paper);
}

/* Cierre: frase grande y los dos botones, centrados */
.about-close {
  display: grid;
  justify-items: center;
  gap: var(--space-xl);
  padding: var(--space-4xl) var(--page-inline);
  text-align: center;
}

.about-close__title {
  font-size: var(--text-hero);
  line-height: var(--leading-display);
}

.about-close__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-s);
}

/* Palabras que se iluminan al bajar (scroll-words.js) */
.scroll-word {
  display: inline;
}

/* --------------------------------------------------------------------------
   Contacto (fase 14): texto a la izquierda y la ficha NFC a la derecha (en
   móvil, debajo). Misma entrada del encabezado que Sobre mí.
   -------------------------------------------------------------------------- */

/* Rejilla: a la izquierda la intro (etiqueta, frase, texto y estado) y debajo
   los datos; a la derecha, la ficha, centrada respecto a la intro. En móvil,
   intro, datos y ficha. */
.contact-hero {
  display: grid;
  grid-template-areas: "intro" "details" "coin";
  gap: var(--space-3xl);
  padding: calc(var(--space-s) * 2 + 44px + var(--space-3xl)) var(--page-inline) var(--space-4xl);
}

@media (min-width: 768px) {
  .contact-hero {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    grid-template-areas: "intro coin" "details .";
    column-gap: var(--space-3xl);
    row-gap: 0;
  }
}

.contact-hero__intro {
  grid-area: intro;
  display: grid;
  justify-items: start;
}

.contact-hero__details {
  grid-area: details;
  display: grid;
  justify-items: start;
  min-width: 0;
  container-type: inline-size; /* para el tamaño del correo (cqi) */
}

.contact-coin {
  grid-area: coin;
  align-self: center;
}

/* Primer párrafo en Garamond (más pequeño que una frase protagonista: es un
   párrafo) y el segundo en Inter de lectura */
.contact-hero__title {
  max-width: 22em;
  margin-top: var(--space-s);
  font-size: clamp(1.75rem, 1.5rem + 1.111vw, 2.5rem); /* 28 → 40 px */
  line-height: var(--leading-title);
  text-wrap: pretty;
}

.contact-hero__text {
  max-width: 34rem;
  margin-top: var(--space-m);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.contact-hero__status {
  margin-top: var(--space-l);
}

/* El correo, protagonista: grande y en Garamond, con «Copiar» al lado. Su
   ancho se fija al del correo (copy.js) para que «Copiado ✓» no mueva el botón. */
.contact-email {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s) var(--space-m);
  margin-top: var(--space-3xl);
}

/* El correo en una línea: el tamaño sale del ancho de la columna (9cqi:
   la dirección mide unos 10,5 em), entre 24 y 52 px; nunca corta ni desborda */
.contact-email__link {
  display: inline-block;
  font-size: clamp(1.5rem, 9cqi, 3.25rem);
  line-height: 1.1;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
  overflow-wrap: anywhere;
}

.contact-email__link:hover {
  text-decoration-thickness: 2px;
}

.contact-email__link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
  border-radius: 2px;
}

.no-js .contact-email__copy,
.no-js .contact-phone__action {
  display: none;
}

/* Teléfono, junto al correo: el número en Garamond, más pequeño que el
   correo, con «WhatsApp» al lado */
.contact-phone {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s) var(--space-m);
  margin-top: var(--space-l);
}

.contact-phone__number {
  font-size: clamp(1.375rem, 1.2rem + 0.8vw, 1.875rem); /* 22 → 30 px */
  line-height: 1.1;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

.contact-phone__number[href]:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.contact-phone__number:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
  border-radius: 2px;
}

/* Correo y teléfono sin JS (contact-info.js): el HTML los lleva al revés y
   aquí se les da la vuelta, para que se lean sin estar tal cual en el código */
.contact-fallback {
  direction: rtl;
  unicode-bidi: bidi-override;
}

/* Datos: etiqueta pequeña en gris y el dato */
.contact-rows {
  display: grid;
  gap: var(--space-m);
  width: 100%;
  margin: var(--space-2xl) 0 0;
}

.contact-row {
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr);
  align-items: center;
  gap: var(--space-s);
}

.contact-row__label {
  font-size: var(--text-small);
  color: var(--color-muted);
}

.contact-row__value {
  margin: 0;
  overflow-wrap: anywhere; /* por si acaso: el de LinkedIn ya corta tras la barra (<wbr>) */
}

/* Ficha NFC en CSS 3D (portada de la V2, deseo 8). Capas, cada una con su
   transform para que no se pisen:
     .coin         <button>, perspectiva
       .coin__tilt   inclinación hacia el ratón (coin.js)
         .coin__spin   arrastre y volteo (coin.js)
           .coin__body   pose base, caras, canto de 18 discos y lámina
   La pose base es también el estado sin JS y con movimiento reducido.
   Grafito mate con un brillo satinado; el canto, un punto más claro. */
.contact-coin {
  display: grid;
  justify-items: center;
  gap: var(--space-m);
}

.coin {
  --coin-size: clamp(14rem, 10rem + 10vw, 20rem);
  --coin-depth: calc(var(--coin-size) * 0.06);
  --coin-face: #141413;
  --coin-edge: #3a3a37;
  position: relative;
  perspective: 1400px;
  width: var(--coin-size);
  aspect-ratio: 1;
  /* Hueco para la sombra del suelo, antes de la pista */
  margin-bottom: calc(var(--coin-size) * 0.14);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: grab;
  touch-action: pan-y; /* el giro con el dedo es horizontal: el scroll vertical sigue */
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

.coin:active {
  cursor: grabbing;
}

.coin:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 8px;
}

.coin__defs {
  position: absolute;
  overflow: hidden;
}

.coin__tilt,
.coin__spin,
.coin__body {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* Pose base: ligeramente inclinada para que se vea el canto */
.coin__body {
  transform: rotateX(10deg) rotateY(-22deg);
}

/* Caras */
.coin__face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 50%;
  background: var(--coin-face);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

/* Satinado: un brillo muy suave arriba a la izquierda y algo de sombra abajo */
.coin__face::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background:
    radial-gradient(120% 90% at 28% 18%, rgb(255 255 255 / 0.07), transparent 55%),
    radial-gradient(90% 90% at 80% 95%, rgb(0 0 0 / 0.25), transparent 60%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.05);
  pointer-events: none;
}

.coin__face--front {
  transform: translateZ(calc(var(--coin-depth) / 2 + 0.5px));
}

.coin__face--back {
  transform: rotateY(180deg) translateZ(calc(var(--coin-depth) / 2 + 0.5px));
}

.coin__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Texto e icono grabados en hueco, en vector (un filtro SVG se emborrona
   dentro del 3D). Tres capas de la misma forma (<use>):
     __lip   luz fina justo por debajo del borde, fuera de la ranura
     __wall  la pared de arriba de la ranura, en sombra (papel oscurecido)
     __ink   el fondo en papel, algo más abajo: tapa la pared salvo arriba
   __wall y __ink van recortadas a la forma (clipPath). */
.coin__name {
  font-family: "EB Garamond Ficha", var(--font-display);
  font-weight: 600;
  font-size: 29px; /* en unidades del viewBox (200): ~50 % del diámetro de ancho */
  letter-spacing: 0.01em;
  text-anchor: middle;
}

.coin__lip {
  fill: var(--color-paper);
  opacity: 0.14;
}

.coin__wall {
  fill: color-mix(in srgb, var(--color-paper) 45%, #0f0f0f);
}

.coin__ink {
  fill: var(--color-paper);
}

/* Canto: discos apilados en Z, más oscuros hacia el centro del grosor
   (--t y --s vienen en el HTML) */
.coin__edge {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

.coin__edge i {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: color-mix(in srgb, var(--coin-edge), #000 var(--s, 0%));
  transform: translateZ(calc(var(--coin-depth) * (var(--t) - 0.5)));
}

/* Lámina de canto: un rectángulo perpendicular que da cuerpo cuando la ficha
   queda de perfil (los discos, vistos de lado, no tienen grosor) */
.coin__rim {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: var(--coin-depth);
  margin-left: calc(var(--coin-depth) / -2);
  border-radius: calc(var(--coin-depth) / 2);
  background: linear-gradient(90deg, var(--coin-edge), color-mix(in srgb, var(--coin-edge), #000 20%), var(--coin-edge));
  transform: rotateY(90deg);
}

/* Sombra en el suelo: coin.js la estrecha cuando la ficha se pone de perfil
   y la desplaza con la inclinación */
.coin__shadow {
  position: absolute;
  left: 15%;
  right: 15%;
  top: 104%;
  height: 12%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(15 15 15 / 0.24), rgb(15 15 15 / 0.08) 60%, transparent);
  pointer-events: none;
}

.contact-coin__hint {
  font-size: var(--text-small);
  color: var(--color-muted);
}

/* --------------------------------------------------------------------------
   Case studies (fase 16, plantilla con Daycup), como las páginas de proyecto
   de Collins. Página clara; --case-color (en <main>) pinta los placeholders.
   Todo con prefijo case- para reutilizarlo en los demás proyectos.

   Rejilla única de 12 columnas (.case-12) dentro de la columna de la página:
   el mismo borde izquierdo y derecho para etiquetas, textos, imágenes, la
   tira de bocetos y el siguiente proyecto.
     - etiquetas: columnas 1–3 · textos: 5–11
     - piezas: imagen de 5 columnas (1–5 u 8–12) y texto de 6 (7–12 o 1–6),
       en espejo; el texto empieza arriba, a la altura de la imagen
     - anchas: 1–12, siempre junto a un bloque de texto
   En móvil, una sola columna.
   -------------------------------------------------------------------------- */

.case-12 {
  --caption-measure: 34ch; /* unos 45 caracteres de Inter por línea */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-m);
  padding-inline: var(--page-inline);
}

@media (min-width: 768px) {
  .case-12 {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--space-m);
    row-gap: var(--space-l);
  }

  .case-col-label { grid-column: 1 / 4; }
  .case-col-text { grid-column: 5 / 12; }
  .case-col-full { grid-column: 1 / -1; }
  .case-col-media-left { grid-column: 1 / 6; }
  .case-col-text-right { grid-column: 7 / 13; }
  .case-col-text-left { grid-column: 1 / 7; }
  .case-col-media-right { grid-column: 8 / 13; }
  .case-col-text-narrow { grid-column: 1 / 5; }
  .case-col-group { grid-column: 6 / 13; }

  /* Pie de una imagen a ancho completo (.case-piece--wide): alineado con el
     borde izquierdo de la imagen, con líneas de ~45 caracteres (no en la
     columna del medio, donde quedaba descolgado) */
  .case-piece--wide > .case-col-text {
    grid-column: 1 / -1;
    max-width: var(--caption-measure);
  }

  /* Puntos en tres columnas iguales del ancho de la imagen (Team3Fitness) */
  .case-features {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--space-m);
  }

  /* Un pie largo (el Naming de Daycup) ocupa todo el ancho de la imagen */
  .case-piece--wide > .case-caption--full,
  .case-caption--full .case-body {
    max-width: none;
  }
}

/* Ritmo vertical: un solo espacio grande entre secciones */
.case > * + * {
  margin-top: var(--space-3xl);
}

.case-head {
  align-items: start;
  padding-top: calc(var(--space-s) * 2 + 44px + var(--space-3xl)); /* el header y aire */
}

.case-head + .case-hero {
  margin-top: var(--space-2xl);
}

/* Etiquetas de sección: pequeñas, en gris y siempre a la izquierda */
.case-label {
  margin: 0;
  font-size: var(--text-body);
  font-weight: var(--weight-text);
  color: var(--color-muted);
}

.case-head .case-label {
  padding-top: 0.6em; /* a la altura del nombre */
}

.case-head__title {
  font-size: var(--text-hero);
  line-height: var(--leading-display);
}

.case-head__phrase {
  max-width: 16em;
  margin-top: var(--space-s);
  font-size: clamp(1.5rem, 1.333rem + 0.741vw, 2rem); /* 24 → 32 px */
  line-height: var(--leading-title);
  text-wrap: balance;
}

.case-stack {
  display: grid;
  gap: var(--space-m);
  align-content: start;
}

/* Puntos bajo una imagen ancha (Team3Fitness): en móvil, apilados, con
   48 px entre bloque y bloque (como pies separados) y 20 px entre título y
   texto; en escritorio, en columnas (más abajo) */
.case-features {
  display: grid;
  gap: var(--space-2xl);
}

.case-feature {
  display: grid;
  gap: 1.25rem;
  align-content: start;
}

/* Texto principal de un bloque, en Garamond (como la historia de Collins) */
.case-lead {
  font-size: clamp(1.5rem, 1.333rem + 0.741vw, 2rem); /* 24 → 32 px */
  line-height: 1.15;
}

.case-body {
  max-width: 40rem;
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* Placeholder de imagen: color del proyecto + nombre, en la proporción
   prevista (--ratio). La imagen o el vídeo real ocupan el mismo sitio. */
.case-media {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: var(--ratio, 16 / 9);
  overflow: hidden;
  border-radius: var(--radius-l);
  background: var(--case-color);
  color: var(--case-text, var(--color-paper));
}

.case-media__name {
  font-size: var(--text-lead);
  opacity: 0.9;
}

/* Proyecto claro (Eurodrive, marfil sobre papel): el mismo borde sutil que su
   tarjeta de la galería, para que los huecos de imagen se lean como bloques */
.case--light .case-media {
  box-shadow: inset 0 0 0 1px rgb(15 15 15 / 0.09);
}

.case-media__name--hero {
  font-size: var(--text-hero);
}

.case-media img,
.case-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Vertical (4:5) de pieza: como mucho ~70vh de alto. Si se estrecha por eso,
   se pega a su borde exterior para no salirse de la rejilla. */
.case-media--portrait {
  width: min(100%, 70vh * 4 / 5);
}

/* Cuadrada (1:1) de pieza, como la vertical: como mucho ~70vh de alto */
.case-media--square {
  width: min(100%, 70vh);
}

.case-col-media-right.case-media--portrait,
.case-col-media-right.case-media--square {
  justify-self: end;
}

/* Captura de móvil (9:16) dentro de un grupo: como mucho ~70vh de alto,
   centrada en su mitad del grupo */
.case-media--phone {
  width: min(100%, 70vh * 9 / 16);
  justify-self: center;
}

/* En móvil, primero la historia (la intro) y después los datos (la ficha) */
@media (max-width: 767px) {
  .case-facts + .case-col-text {
    order: -1;
  }
}

/* Ficha */
.case-facts {
  display: grid;
  align-content: start;
  gap: var(--space-m);
  margin: 0;
}

.case-facts__row {
  display: grid;
  gap: var(--space-3xs);
}

.case-facts dt {
  font-size: var(--text-small);
  font-weight: var(--weight-text-strong);
}

.case-facts dd {
  margin: 0;
  font-size: var(--text-small);
  color: var(--color-muted);
}

.case-facts__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-m);
}

.case-facts__links .link {
  color: var(--color-ink);
}

/* Proceso: el texto en la rejilla y la tira de bocetos debajo, empezando en
   la columna 1 y saliéndose por la derecha */
.case-sketches {
  --card-width: clamp(16rem, 10rem + 22vw, 32rem);
  margin-top: var(--space-s);
}

.case-sketches .gallery__item {
  --ratio: 4 / 3;
}

/* A diferencia de la galería de la home, alineados por arriba: los pies tienen
   distinta altura y, alineados por abajo, las imágenes quedaban descuadradas */
.case-sketches .gallery__track {
  align-items: flex-start;
}

.gallery--plain .gallery__track {
  padding-bottom: var(--space-s);
}

.case-sketch {
  margin: 0;
}

.case-sketch .gallery-card__media {
  color: var(--case-text, var(--color-paper));
}

/* Pie de cada boceto: su título en negrita y el texto, en Inter */
.case-sketch__caption {
  margin-top: var(--space-s);
  font-size: var(--text-small);
  line-height: var(--leading-body);
}

.case-sketch__caption strong {
  font-weight: var(--weight-text-strong);
}

/* La solución, por piezas */
.case-solution {
  display: grid;
  gap: var(--space-3xl);
}

.case-solution > .case-12:first-child {
  margin-bottom: calc(var(--space-l) - var(--space-3xl)); /* la etiqueta, cerca de la primera pieza */
}

.case-piece {
  align-items: start;
}

/* En móvil, la imagen siempre antes que su texto (en escritorio van en espejo) */
@media (max-width: 767px) {
  .case-piece > .case-media,
  .case-piece > .case-group {
    order: -1;
  }
}

.case-piece__text {
  display: grid;
  gap: var(--space-s);
  align-content: start;
}

/* Título de cada pieza (y de cada punto en las soluciones numeradas): un nivel
   por debajo de la etiqueta de sección (Inter gris), en Garamond y en tinta.
   Siempre en la columna de texto, junto a su párrafo: la columna izquierda es
   solo para las etiquetas de sección. */
/* Entre el título de una pieza y su texto, 20 px en todos los pies (como los
   puntos de Team3Fitness); entre párrafos, lo de siempre */
.case-stack > .case-piece__label + * {
  margin-top: calc(1.25rem - var(--space-m));
}

.case-piece__text > .case-piece__label + * {
  margin-top: calc(1.25rem - var(--space-s));
}

.case-piece__label {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lead);
  font-weight: var(--weight-display, 400);
  line-height: var(--leading-title);
  color: var(--color-ink);
}

.case-piece__body {
  max-width: 36rem;
  line-height: var(--leading-body);
}

/* Grupo de imágenes: una ancha arriba y dos verticales debajo */
.case-group {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-m);
}

.case-group__wide {
  grid-column: 1 / -1;
}

/* Grupo de tres verticales en fila (fotos de producto) */
.case-group--three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Una ancha arriba y una sola vertical debajo, centrada (del ancho de media
   columna del grupo, como si fuera una de dos) */
.case-group__half {
  grid-column: 1 / -1;
  justify-self: center;
  width: min(calc((100% - var(--space-m)) / 2), 70vh * 4 / 5);
}

/* Texto suelto después de las piezas (.case-piece--text): un .case-12 con el
   texto en .case-col-text; no necesita estilos propios */

/* Siguiente proyecto: un bloque grande del color del siguiente. El marfil
   de Eurodrive lleva el mismo borde sutil que su tarjeta de la galería, para
   que se lea como bloque sobre el papel. */
.case > nav:last-child {
  padding-bottom: var(--space-4xl);
}

.case-next {
  transition: color var(--duration-fast) var(--ease-out); /* sus textos, al acabar una transición entre páginas */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-s) var(--space-l);
  padding: var(--space-2xl) var(--space-xl);
  border-radius: var(--radius-l);
  background: var(--next-color);
  color: var(--next-text, var(--project-text));
  text-decoration: none;
}

.case-next--light {
  box-shadow: inset 0 0 0 1px rgb(15 15 15 / 0.09);
}

.case-next__label {
  grid-column: 1 / -1;
  font-size: var(--text-small);
  opacity: 0.75;
}

.case-next__name {
  font-size: clamp(2.5rem, 1.5rem + 4.444vw, 5.5rem); /* 40 → 88 px */
  line-height: var(--leading-display);
}

.case-next__arrow {
  width: clamp(2rem, 1.5rem + 2.222vw, 3.5rem);
  height: auto;
  transition: translate var(--spring-soft-duration) var(--spring-soft);
}

.case-next:hover .case-next__arrow,
.case-next:focus-visible .case-next__arrow {
  translate: 0.5rem 0;
}

.case-next:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 4px;
}

/* En móvil, el nombre en una sola línea y la flecha a su derecha. Un solo
   tamaño para todos: el que deja caber «Fruites Pascual Vidal» (el más largo,
   7,7 em) en 375 px de ancho (32 px). En escritorio, igual. */
@media (max-width: 767px) {
  .case-next {
    align-items: center;
    column-gap: var(--space-s);
    padding-inline: var(--space-m);
  }

  .case-next__name {
    font-size: 2rem;
    white-space: nowrap;
  }

  .case-next__arrow {
    width: 1.75rem;
  }
}

/* --------------------------------------------------------------------------
   Modal del CV (partials/cv-modal.html, cv-modal.js). El <dialog> ocupa toda
   la pantalla (un clic fuera del panel lo cierra) y centra el panel. El fondo
   oscurece con un fundido; el panel sube un poco al entrar (JS).
   -------------------------------------------------------------------------- */

.cv-modal {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: var(--space-m) var(--gutter);
  border: 0;
  background: transparent;
  color: var(--color-ink);
}

.cv-modal[open] {
  display: grid;
  place-items: center;
}

.cv-modal::backdrop {
  background: color-mix(in srgb, var(--color-ink) 55%, transparent);
  animation: cv-backdrop-in var(--duration-base) var(--ease-out);
}

.cv-modal.is-closing::backdrop {
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-in);
}

@keyframes cv-backdrop-in {
  from { opacity: 0; }
}

/* Sin scroll detrás mientras está abierto (Lenis ya se para) */
html.cv-open {
  overflow: hidden;
}

.cv-modal__panel {
  position: relative;
  display: grid;
  gap: var(--space-s);
  width: min(100%, 30rem);
  padding: var(--space-xl);
  border-radius: var(--radius-l);
  background: var(--color-paper);
}

.cv-modal__close {
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  cursor: pointer;
}

/* ✕ con dos rayas de 1,5 px, como el icono del menú */
.cv-modal__close span,
.cv-modal__close span::after {
  display: block;
  width: 1.125rem;
  height: 1.5px;
  background: currentColor;
  rotate: 45deg;
}

.cv-modal__close span::after {
  content: "";
  rotate: 90deg;
}

.cv-modal__close:hover {
  background: color-mix(in srgb, var(--color-ink) 6%, transparent);
}

.cv-modal__close:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.cv-modal__title {
  padding-right: 44px;
  font-size: var(--text-title);
  line-height: var(--leading-title);
}

.cv-modal__text {
  color: var(--color-muted);
}

.cv-form {
  display: grid;
  gap: var(--space-s);
  margin-top: var(--space-s);
}

.field {
  display: grid;
  gap: var(--space-3xs);
}

.field__label {
  font-size: var(--text-small);
  font-weight: var(--weight-text-strong);
}

.field__optional {
  font-weight: var(--weight-text);
  color: var(--color-muted);
}

.field__input {
  width: 100%;
  padding: var(--space-xs) var(--space-s);
  border: 0;
  border-radius: var(--radius-s);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-ink) 22%, transparent);
  background: color-mix(in srgb, var(--color-ink) 3%, var(--color-paper));
  color: inherit;
  font: inherit;
  transition: box-shadow var(--duration-fast) var(--ease-out);
}

.field__input:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--color-ink);
}

.field__input[aria-invalid="true"] {
  box-shadow: inset 0 0 0 2px var(--color-error);
}

/* Honeypot: fuera de la vista y del teclado (no display: none, que algunos
   bots detectan) */
.cv-form__trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.cv-form__captcha:empty {
  display: none;
}

.cv-form__submit {
  justify-self: start;
  margin-top: var(--space-2xs);
}

.cv-form__submit:disabled {
  opacity: 0.5;
  cursor: progress;
}

/* Privacidad: pequeña y en gris, bajo el botón */
.cv-form__privacy {
  margin: 0;
  font-size: var(--text-small);
  color: var(--color-muted);
}

.cv-form__status {
  min-height: 1.5em;
  font-size: var(--text-small);
  color: var(--color-muted);
}

.cv-form__status[data-tone="error"] {
  color: var(--color-error);
}

/* Enviado: solo queda el mensaje, grande */
.cv-form.is-sent > :not(.cv-form__status) {
  display: none;
}

.cv-form.is-sent .cv-form__status {
  font-family: var(--font-display);
  font-size: var(--text-lead);
  color: var(--color-ink);
}

/* --------------------------------------------------------------------------
   Footer (partials/footer.html), como Collins: tinta y del alto de la
   pantalla; enlaces grandes en Garamond centrados y las acciones debajo.
   Abajo, el © con «Disponible» y, a la derecha, la firma (modules/footer.js
   la dibuja); en móvil, la firma debajo, a la izquierda.
   -------------------------------------------------------------------------- */

.site-footer {
  display: grid;
  grid-template-rows: 1fr auto;
  min-height: 100vh;
  min-height: 100svh;
  /* Margen de abajo generoso: la fila de abajo no queda pegada al borde */
  padding: var(--space-4xl) var(--page-inline) var(--space-2xl);
  background: var(--color-ink);
  color: var(--color-paper);
  --color-muted: var(--color-muted-on-ink);
}

.site-footer__main {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--space-xl);
  padding-bottom: var(--space-3xl);
  text-align: center;
}

.site-footer__list {
  display: grid;
  gap: var(--space-2xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Como en Collins y en el menú: al pasar por uno, los demás se apagan */
.site-footer__link {
  display: inline-block;
  font-size: var(--text-menu);
  line-height: 1.15;
  color: inherit;
  text-decoration: none;
  transition: opacity var(--duration-fast) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .site-footer__list:has(.site-footer__link:hover) .site-footer__link:not(:hover) {
    opacity: 0.35;
  }
}

.site-footer__link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
  border-radius: var(--radius-s);
}

.site-footer__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-s) var(--space-l);
}

.site-footer .btn {
  --btn-bg: var(--color-paper);
  --btn-fg: var(--color-ink);
}

.site-footer__action {
  font-size: var(--text-body);
  color: inherit;
  text-decoration: none;
  text-underline-offset: 0.3em;
}

.site-footer__action:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
}

.site-footer__action:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}

/* Abajo: el © con «Disponible» a la izquierda y la firma a la derecha, con
   su borde derecho en el de la columna (el mismo que el icono del menú) y su
   base en la de la fila. En móvil, todo centrado: el ©, «Disponible» y la
   firma debajo. */
.site-footer__bottom {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: var(--space-m);
}

@media (max-width: 767px) {
  .site-footer__bottom {
    flex-direction: column;
    align-items: center;
    gap: var(--space-xl);
    text-align: center;
  }

  .site-footer__meta {
    justify-content: center;
  }
}

.site-footer__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-m);
  font-size: var(--text-small);
  color: var(--color-muted);
}

.site-footer__meta .status {
  color: var(--color-paper);
}

/* Selector de idioma (fase 20), en el footer y al pie del menú, los dos sobre
   tinta: códigos pequeños en gris y el idioma actual en papel. Lo rellena
   scripts/idiomas.mjs (oculto si no hay otro idioma activo). */
.lang-switch {
  display: flex;
  gap: var(--space-xs);
  font-size: var(--text-small);
}

.lang-switch[hidden],
.lang-switch a[hidden] {
  display: none;
}

.lang-switch a {
  color: var(--color-muted-on-ink);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}

.lang-switch a:hover,
.lang-switch a[aria-current] {
  color: var(--color-paper);
}

.lang-switch a:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: var(--radius-s);
}

/* Firma: ancho fijo (de 256 a 384 px; 344 px a 1280) y el alto, el de su
   proporción. En móvil, que quepa holgada. */
.site-footer__signature {
  flex-shrink: 0;
  width: clamp(16rem, 12rem + 12vw, 24rem);
  color: var(--color-paper);
}

@media (max-width: 767px) {
  .site-footer__signature {
    width: min(15rem, 100% - 3rem);
  }
}

.site-footer__signature svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* --------------------------------------------------------------------------
   Aviso de idioma (fase 20, modules/lang-notice.js): una línea discreta abajo
   a la izquierda (en móvil, de lado a lado), en tinta, con el enlace y la ✕.
   Por debajo del menú: con el menú abierto no se ve.
   -------------------------------------------------------------------------- */

.lang-notice {
  position: fixed;
  left: var(--page-inline);
  right: var(--page-inline);
  bottom: var(--space-s);
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs);
  padding: var(--space-2xs) var(--space-2xs) var(--space-2xs) var(--space-s);
  border-radius: 999px;
  background: var(--color-ink);
  color: var(--color-paper);
  font-size: var(--text-small);
  /* Un filo de papel casi invisible: sobre tinta (footer) sigue viéndose la forma */
  box-shadow: inset 0 0 0 1px rgb(247 246 242 / 0.14);
}

@media (min-width: 768px) {
  .lang-notice {
    right: auto;
    bottom: var(--space-m);
  }
}

html.menu-open .lang-notice {
  visibility: hidden;
}

.lang-notice__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: inherit;
  font-weight: var(--weight-text-strong);
  text-decoration: none;
}

.lang-notice__arrow,
.lang-notice__close svg {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.lang-notice__arrow {
  transition: translate var(--spring-soft-duration) var(--spring-soft);
}

.lang-notice__link:hover .lang-notice__arrow {
  translate: 0.25rem 0;
}

.lang-notice__close {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--color-muted-on-ink);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out);
}

.lang-notice__close:hover {
  color: var(--color-paper);
}

.lang-notice a:focus-visible,
.lang-notice__close:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  border-radius: 999px;
}

/* --------------------------------------------------------------------------
   Transiciones entre páginas (fase 19), como Collins: suaves, rápidas y sin
   hacer esperar. View Transitions entre documentos como mejora progresiva:
   cada página sigue siendo un HTML real y, sin soporte, se navega normal.
   El script del <head> (partials/head.html) pone el nombre «proyecto» y
   avisa a main.js de cuándo acaba, para que las entradas empiecen después.
   La activación (@view-transition) también está en ese parcial, en línea:
   aquí, Chrome no la aplica a la página que entra si esta carga un módulo, y
   dentro de @media (prefers-reduced-motion) tampoco. Con movimiento reducido
   no hay transición (cambio directo): la salta el script (skipTransition).
   -------------------------------------------------------------------------- */

/* Por si acaso, con movimiento reducido las animaciones de la transición no
   duran nada */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* General: la página que sale se desvanece (160 ms) y la que entra aparece
   subiendo 16 px (340 ms, desde los 60 ms). Todo acaba hacia los 400 ms. */
::view-transition-old(root) {
  animation: page-out 160ms var(--ease-in) both;
}

::view-transition-new(root) {
  animation: page-in 340ms var(--ease-out) 60ms both;
}

@keyframes page-out {
  to { opacity: 0; }
}

@keyframes page-in {
  from { opacity: 0; transform: translateY(16px); }
}

/* El header no se mueve: queda fijo encima y solo funde su contenido (por
   si cambia de color o el icono pasa de ✕ a las dos rayas). El nombre solo
   lo lleva mientras dura una transición (.has-page-transition, del script del
   <head>): un elemento con view-transition-name corta el backdrop-filter de
   su fondo y el desenfoque dejaría de verse. */
.has-page-transition .site-header {
  view-transition-name: site-header;
}

::view-transition-group(site-header) {
  animation: none;
}

/* El header de salida (con su fondo, si lo tenía) se va al mismo ritmo que la
   página que sale, para que el fondo no desaparezca antes que el contenido que
   tapaba; el de llegada está entero desde el principio (no parpadea) */
::view-transition-old(site-header) {
  animation: page-out 160ms var(--ease-in) both;
}

::view-transition-new(site-header) {
  animation: none;
}

/* Menú abierto: al navegar desde él, se desvanece por encima mientras la
   página nueva entra debajo (se cierra dentro de la propia transición). */
html.menu-open .site-menu {
  view-transition-name: site-menu;
}

::view-transition-old(site-menu) {
  animation: page-out 150ms var(--ease-in) both;
}

/* Tarjeta → case study (y al volver, al revés): la tarjeta pulsada se
   convierte en la imagen principal. Recortada con el radio de las imágenes
   y sin deformarse (las dos vistas cubren la caja mientras cambia).
   Sin imagen (el bloque «Siguiente proyecto», con su nombre escrito): la
   vista de salida se desvanece en 120 ms al empezar, para que el texto no se
   estire, y la de llegada se ve entera desde el principio pero sin nombre
   (.is-vt-target, abajo), que aparece al acabar. */
::view-transition-group(proyecto) {
  overflow: clip;
  border-radius: var(--radius-l);
  animation-duration: 420ms;
  animation-timing-function: var(--ease-out);
}

::view-transition-old(proyecto),
::view-transition-new(proyecto) {
  height: 100%;
  object-fit: cover;
  object-position: var(--vt-framing, 50% 50%); /* el encuadre de la tarjeta (partials/head.html) */
}

::view-transition-old(proyecto) {
  animation: page-out 120ms var(--ease-in) both;
}

::view-transition-new(proyecto) {
  animation: none;
}

/* Con imagen (view-transition-class «imagen», del script del <head>): la
   tarjeta y la cabecera llevan la misma imagen 16:9, la tarjeta recortada y
   la cabecera entera. No se funden (se verían los dos encuadres a la vez):
   se ve la captura de la cabecera, que tiene la imagen entera y, recortada
   con object-fit en la caja que cambia, empieza (o acaba) en el recorte de
   la tarjeta y se abre hasta la imagen completa.
   - Ida: la de llegada (la cabecera) encima desde el principio; la de la
     tarjeta, debajo (se ve si la cabecera aún está cargando: no lleva fondo,
     ver abajo).
   - Vuelta (.has-project-return): la de salida (la cabecera) hasta el final;
     la de llegada (la tarjeta, ya recortada) no se ve: al acabar, la tarjeta
     real enseña el mismo recorte.
   Fusión normal: con la de por defecto (plus-lighter), dos vistas opacas se
   sumarían y aclararían la imagen. */
::view-transition-old(proyecto.imagen),
::view-transition-new(proyecto.imagen) {
  animation: none;
  mix-blend-mode: normal;
}

html.has-project-return::view-transition-new(proyecto.imagen) {
  opacity: 0;
}

/* La cabecera con portada no lleva el color del proyecto de fondo: mientras
   carga la imagen, en la transición se ve la de la tarjeta, no un bloque de
   color */
.case-hero .case-media:has(picture) {
  background: none;
}

/* El nombre del placeholder de llegada, oculto mientras la caja cambia; al
   acabar (partials/head.html quita la clase) aparece con un fundido corto
   (el de la tarjeta, en .gallery-card__media) */
.case-media__name {
  transition: color var(--duration-fast) var(--ease-out);
}

.gallery-card__media.is-vt-target,
.is-vt-target .case-media__name,
.case-next.is-vt-target {
  color: transparent;
  transition: none; /* se esconde al momento: si no, se ve estirándose en la captura */
}

/* Al llegar convertida desde la tarjeta, la imagen ya está a su tamaño: no
   espera al 80 % para crecer (grow-in.js tampoco la anima). Con html delante
   para ganar a «.js [data-grow-in]:not(.is-entered)» (si no, la captura de
   llegada salía al 80 % y la imagen saltaba al 100 % al acabar). */
html.has-project-transition [data-grow-in]:not(.is-entered),
.has-project-transition [data-grow-in] {
  transform: none;
  animation: none;
}

/* --------------------------------------------------------------------------
   Movimiento reducido: sin transiciones ni animaciones CSS largas.
   (Las animaciones de anime.js se adaptan desde cada módulo con scope.matches.)
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   Utilidades
   -------------------------------------------------------------------------- */

/* Columna de contenido: ancho máximo --container centrado y, como mínimo, el
   margen lateral --gutter (en móvil manda el gutter). --page-inline es el
   margen lateral resultante, para piezas a todo el ancho que alinean su
   contenido con la columna (header, menú…): se usa en padding-inline, donde
   el 100 % es el ancho de la propia pieza (sin la barra de scroll). */
:root {
  --page-inline: max(var(--gutter), (100% - var(--container)) / 2);
}

.container {
  width: 100%;
  max-width: calc(var(--container) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.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;
}

/* --------------------------------------------------------------------------
   404 (fase 18), sobria como Collins: una sola pantalla, sin scroll. Arriba,
   la etiqueta, la frase y los enlaces; abajo, la zona de «4 0 4», que ocupa
   el alto que sobra. Los números se lanzan dentro de ella (not-found.js) y
   la zona recorta lo que asome: nunca tapan la frase ni los enlaces.
   -------------------------------------------------------------------------- */

.not-found {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: var(--space-l);
  height: 100vh;
  height: 100dvh;
  padding: calc(var(--space-s) * 2 + 44px + var(--space-xl)) var(--page-inline) var(--space-m);
}

.not-found__head {
  display: grid;
  gap: var(--space-l);
}

@media (min-width: 768px) {
  .not-found__head {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    align-items: end;
  }
}

.not-found__text {
  display: grid;
  justify-items: start;
}

.not-found__title {
  max-width: 12em;
  margin-top: var(--space-s);
  font-size: var(--text-hero);
  line-height: var(--leading-display);
  text-wrap: balance;
}

.not-found__lead {
  max-width: 28em;
  margin-top: var(--space-s);
  color: var(--color-muted);
}

/* Los tres enlaces, grandes en Garamond como los del menú y el footer */
.not-found__list {
  display: grid;
  justify-items: start;
  margin: 0;
  padding: 0;
  list-style: none;
}

.not-found__link {
  display: inline-block;
  font-size: var(--text-title);
  line-height: 1.15;
  color: inherit;
  text-decoration: none;
  transition: opacity var(--duration-fast) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .not-found__list:has(.not-found__link:hover) .not-found__link:not(:hover) {
    opacity: 0.35;
  }
}

.not-found__link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
  border-radius: var(--radius-s);
}

/* Zona de los números: todo el alto que sobra. «4 0 4» va junto, centrado y
   abajo. El tamaño sale del lado que mande (ancho o alto de la zona, cqi /
   cqb): «404» mide ~1,6 em de ancho y su caja, 0,8 em de alto, así que ocupa
   como mucho el 65 % del alto y deja aire encima y a los lados para lanzarlo. */
.not-found__field {
  --digit-size: min(62cqi, 82cqb);
  container-type: size;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  min-height: 0;
  overflow: clip;
  user-select: none;
  -webkit-user-select: none;
}

.not-found__digit {
  display: block;
  padding-inline: 0.03em; /* el trazo de la Garamond se sale un poco de su ancho */
  font-size: var(--digit-size);
}

.not-found__glyph {
  display: block;
  line-height: 0.8; /* la caja, ceñida a las cifras */
  font-variant-numeric: lining-nums; /* cifras de caja alta (la Garamond trae las elzevirianas) */
}

/* Con ratón (not-found.js), la mano indica que se pueden coger */
.not-found__field.is-draggable .not-found__digit {
  cursor: grab;
}

/* Hasta que suben, los números esperan ocultos. Misma red de seguridad que
   [data-reveal]: si el JS falla, a los 3 s se ven. */
.js .not-found__field:not(.is-entered) .not-found__glyph {
  opacity: 0;
  animation: reveal-fallback 0s 3s forwards;
}

/* --------------------------------------------------------------------------
   Trabajo (/trabajo/), como la página «Work» de Collins: etiqueta y frase,
   los cinco proyectos en dos columnas desplazadas (la de la derecha empieza
   más abajo) y «Otros trabajos» (#otros-trabajos). Las tarjetas son las de
   la galería de la home (.gallery-card--work), a todo el ancho de su columna.
   -------------------------------------------------------------------------- */
.work {
  padding-block: calc(var(--space-s) * 2 + 44px + var(--space-3xl)) var(--space-4xl);
}

.work-grid,
.work-pieces {
  display: grid;
  gap: var(--space-2xl) var(--space-xl);
  margin: var(--space-2xl) 0 0;
  padding: 0 var(--page-inline);
  list-style: none;
}

@media (min-width: 768px) {
  .work-grid,
  .work-pieces {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
  }

  /* Columna derecha desplazada, como Collins */
  .work-grid__item:nth-child(even) {
    margin-top: var(--space-4xl);
  }
}

.gallery-card--work {
  width: 100%;
}

/* El nombre y la frase debajo, en el flujo (en la tira de la home van en un
   hueco reservado) */
.gallery-card--work .gallery-card__caption {
  position: static;
}

/* Con ratón, el nombre y la frase aparecen al pasar por encima, como en la
   tira de la home (su hueco ya está reservado: nada se mueve) */
@media (hover: hover) and (pointer: fine) {
  .gallery-card--work:is(:hover, :focus-visible) .gallery-card__caption {
    opacity: 1;
    translate: 0 0;
  }
}

.work-other {
  margin-top: var(--space-4xl);
  /* Al llegar por el ancla, la etiqueta no queda debajo del header */
  scroll-margin-top: calc(var(--space-s) * 2 + 44px + var(--space-l));
}

/* Piezas sueltas: imagen (pendiente: su color), título en Garamond y texto */
.work-piece__media {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--radius-l);
  background: var(--piece-color);
}

.work-piece__media picture,
.work-piece__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Pieza con varias imágenes ([data-piece-slider], piece-slider.js): apiladas
   en la misma caja (no cambia de alto) y flechas con el indicador dentro, abajo
   a la derecha. Sin JS, la primera y sin flechas. */
.piece-slider {
  position: relative;
  touch-action: pan-y; /* el dedo en horizontal es del carrusel; en vertical, del sistema */
}

.piece-slider__slides,
.piece-slider__slides > picture {
  display: block;
  width: 100%;
  height: 100%;
}

.piece-slider__slides {
  display: grid;
}

.piece-slider__slides > picture {
  grid-area: 1 / 1;
}

.piece-slider__controls {
  position: absolute;
  right: var(--space-s);
  bottom: var(--space-s);
  display: none;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--space-3xs);
  border-radius: 999px;
  background: rgb(15 15 15 / 0.55);
  color: var(--color-paper);
  font-size: var(--text-small);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.piece-slider.is-ready .piece-slider__controls {
  display: flex;
}

.piece-slider__count {
  min-width: 3.2em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.piece-slider__btn {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-out);
}

.piece-slider__btn:hover {
  background: rgb(247 246 242 / 0.15);
}

.piece-slider__btn:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 1px;
}

.piece-slider__btn svg {
  width: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.work-piece__title {
  margin-top: var(--space-s);
  font-size: var(--text-lead);
  line-height: var(--leading-title);
}

.work-piece__text {
  max-width: 34rem;
  margin-top: var(--space-2xs);
  color: var(--color-muted);
}

/* --------------------------------------------------------------------------
   Páginas mínimas de la fase 1 (home vacía, 404, índice del lab).
   Se sustituirán por el diseño real en sus fases.
   -------------------------------------------------------------------------- */

.page-min {
  display: grid;
  align-content: center;
  gap: var(--space-s);
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--space-m) var(--gutter);
  max-width: 60rem;
  margin-inline: auto;
}

.page-min__title {
  font-size: var(--text-title);
  line-height: var(--leading-title);
}

.page-min__text {
  color: var(--color-muted);
}
