/* ============================================================
   KA STUDIO — capa de tema sobre la plantilla Rayo
   ============================================================
   Se carga DESPUÉS de main.css. No se edita main.css nunca:
   Rayo resuelve todo su color a través de variables declaradas
   por duplicado (--x--light / --x--dark), así que basta con
   redefinir esos pares aquí para teñir las 19 páginas.

   Orden de carga en cada HTML:
     plugins.css  →  main.css  →  ka-theme.css
   ============================================================ */

:root{

  /* ----------------------------------------------------------
     MODO OSCURO — la identidad KA de siempre
     ---------------------------------------------------------- */
  --base--dark: #0a0710;
  --base-rgb--dark: 10, 7, 16;
  --base-tint--dark: #120c1e;
  --base-shade--dark: #050308;
  --base-opp--dark: #f4f5f6;
  --base-opp-tint--dark: #ffffff;

  /* Acento = magenta KA. Se usa sobre todo como SUPERFICIE con
     texto claro encima (4.60:1, AA). Como color de texto solo
     da 4.35:1, así que para enlaces pequeños va --ka-link. */
  --accent--dark: #b026ff;
  --accent-rgb--dark: 176, 38, 255;
  --additional--dark: #39ff88;
  --additional-rgb--dark: 57, 255, 136;

  --neutral-transparent--dark: rgba(10, 7, 16, 0.3);

  --st-bright--dark: #f4f5f6;
  --st-medium--dark: #7d7091;
  --st-muted--dark: #241a33;
  --st-opp-bright--dark: #0a0710;

  --t-bright--dark: #f4f5f6;          /* 18.31:1  AAA */
  --t-medium--dark: #cfc4de;          /* 11.99:1  AAA */
  --t-muted--dark: #b7abc9;           /*  9.21:1  AAA */
  --t-muted-extra--dark: #7d7091;     /*  4.38:1  AA grande */
  --t-opp-bright--dark: #0a0710;
  --t-opp-medium--dark: #241a33;
  --t-opp-muted--dark: #6a30a8;

  /* Pesos: Funnel Display aguanta bien el peso alto en oscuro */
  --fw-regular--dark: 300;
  --fw-medium--dark: 500;
  --fw-semibold--dark: 600;
  --fw-bold--dark: 700;
  --fw-regular-opp--dark: 400;
  --fw-medium-opp--dark: 600;


  /* ----------------------------------------------------------
     MODO CLARO — diseño nuevo, la web nunca lo ha tenido
     ----------------------------------------------------------
     Dos colores de la paleta no sobreviven tal cual sobre fondo
     claro y se sustituyen:

       · #b026ff magenta  →  4.22:1, insuficiente como texto.
         Se usa #9315d6, el mismo magenta oscurecido: 5.81:1 (AA)
         y 6.33:1 con blanco encima. Misma lectura de marca.

       · #39ff88 verde  →  1.22:1, invisible. En claro queda
         relegado a superficie con texto oscuro encima (15:1),
         nunca como color de texto.

       · #E0D1E9, el lila de los eyebrows  →  1.2:1. Cada modo
         tiene el suyo (ver --ka-eyebrow más abajo).
     ---------------------------------------------------------- */
  --base--light: #F7F4FA;             /* blanco con un punto de lila */
  --base-rgb--light: 247, 244, 250;
  --base-tint--light: #ffffff;
  --base-shade--light: #e6e0ee;
  --base-opp--light: #0a0710;
  --base-opp-tint--light: #000000;

  --accent--light: #9315d6;
  --accent-rgb--light: 147, 21, 214;
  --additional--light: #39ff88;       /* solo superficie */
  --additional-rgb--light: 57, 255, 136;

  --neutral-transparent--light: rgba(255, 255, 255, 0.3);

  --st-bright--light: #17101f;
  --st-medium--light: #9084a0;
  --st-muted--light: #e6e0ee;
  --st-opp-bright--light: #ffffff;

  --t-bright--light: #17101f;         /* 17.05:1  AAA */
  --t-medium--light: #453a52;         /*  9.73:1  AAA */
  --t-muted--light: #5e5270;          /*  6.61:1  AA  */
  --t-muted-extra--light: #9084a0;    /*  3.22:1  AA grande */
  --t-opp-bright--light: #f4f5f6;
  --t-opp-medium--light: #cfc4de;
  --t-opp-muted--light: #b7abc9;

  --fw-regular--light: 400;
  --fw-medium--light: 600;
  --fw-semibold--light: 700;
  --fw-bold--light: 800;
  --fw-regular-opp--light: 300;
  --fw-medium-opp--light: 500;
}


/* ------------------------------------------------------------
   Tokens propios de KA que Rayo no tiene.
   Van en los mismos selectores que usa la plantilla para
   resolver el modo, para que cambien con el conmutador.
   ------------------------------------------------------------ */
:root{
  --ka-eyebrow: #E0D1E9;   /* lila claro — 13.8:1 sobre el fondo oscuro */
  --ka-link: #39ff88;      /* verde — 15.07:1, para enlaces pequeños */
}
@media (prefers-color-scheme: light){
  :root{
    --ka-eyebrow: #6a30a8; /*  7.41:1 sobre #F7F4FA */
    --ka-link: #9315d6;    /*  5.81:1 */
  }
}
[color-scheme=light]{
  --ka-eyebrow: #6a30a8;
  --ka-link: #9315d6;
}
[color-scheme=dark]{
  --ka-eyebrow: #E0D1E9;
  --ka-link: #39ff88;
}


/* ------------------------------------------------------------
   Máscara XR — lo único que se rescata del diseño anterior.
   El visor de VR que recorta la imagen de cabecera de cada
   página de caso. Venía de css/styles.css:883 del sitio viejo.
   ------------------------------------------------------------ */
.ka-xr-mask{
  aspect-ratio: 1264 / 620;
  overflow: hidden;
  -webkit-mask-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%201264%20620%22%3E%3Cpath%20d%3D%22M1033.44%204H230.25C104.16%204%201%20107.16%201%20233.25V257.62C1%20454.66%20162.21%20615.87%20359.25%20615.87H904.42C1101.46%20615.87%201262.67%20454.66%201262.67%20257.62V233.25C1262.67%20107.16%201159.51%204%201033.42%204H1033.44Z%22%20fill%3D%22%23000%22/%3E%3C/svg%3E');
          mask-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%201264%20620%22%3E%3Cpath%20d%3D%22M1033.44%204H230.25C104.16%204%201%20107.16%201%20233.25V257.62C1%20454.66%20162.21%20615.87%20359.25%20615.87H904.42C1101.46%20615.87%201262.67%20454.66%201262.67%20257.62V233.25C1262.67%20107.16%201159.51%204%201033.42%204H1033.44Z%22%20fill%3D%22%23000%22/%3E%3C/svg%3E');
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-position: center;   mask-position: center;
}
.ka-xr-mask img,
.ka-xr-mask video{
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* Mentorship (y cualquier portada con grupo a sangre): las curvas del
   visor dejan a la gente a medias en los extremos. Un zoom corto
   (scale > 1) rellena la máscara y saca del encuadre esos cortes. */
.ka-xr-mask--safe{
  background: var(--base);
}
.ka-xr-mask--safe img{
  object-fit: cover;
  object-position: center 45%;
  scale: 1.2;
}
/* Misma foto en el listado de Success stories: la tarjeta es alta y
   `cover` se come los lados. Mismo zoom que la portada del caso. */
.ka-stack-img--zoom{
  overflow: hidden;
}
.ka-stack-img--zoom img{
  object-position: center 45%;
  scale: 1.2;
}


/* ------------------------------------------------------------
   Ajustes de componente
   ------------------------------------------------------------ */

/* Enlaces de texto con flecha (el patrón "Privacy Policy →").
   Rayo los deja en --t-muted; en KA llevan el color de enlace. */
.btn-line-xsmall.ka-link,
.btn-line-small.ka-link,
.btn-line-medium.ka-link{ color: var(--ka-link); }

/* Eyebrows / antetítulos */
.ka-eyebrow{
  color: var(--ka-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 1.4rem;
  font-weight: var(--fw-medium);
}

/* El verde nunca como texto en modo claro: si algún componente
   de Rayo lo hereda, se fuerza el token seguro. */
[color-scheme=light] .ka-link{ color: var(--ka-link); }


/* ============================================================
   HEADER LOGO
   Imagotipo completo (marca + wordmark) en SVG. Ya no usamos
   .mxd-logo__text — el texto va dentro del archivo.
   El SVG es claro; en modo claro lo pasamos a tinta oscura.
   ============================================================ */
.mxd-logo{
  gap: 0;
}
.mxd-logo__image{
  width: 12rem;
  height: auto;
  display: block;
}
@media only screen and (min-width: 768px){
  .mxd-logo__image{
    width: 14.5rem;
  }
}
[color-scheme=light] .mxd-logo__image{
  filter: brightness(0);
}

/* ============================================================
   HERO 01 — logo Three.js + pill
   ============================================================ */

/* Contenedor del hero: el canvas vive aquí (no dentro de .mxd-hero-01)
   para cubrir también el padding-top bajo el header */
.mxd-hero-section.mxd-hero-fullheight{
  position: relative;
  isolation: isolate;
}

/* Logo 3D: llena toda la sección, detrás de texto y header */
.ka-hero-logo{
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.6s ease;
}
.ka-hero-logo.is-ready{
  /* Transparencia del lienzo (no del material 3D) + blend para que el
     fondo negro del bloom no tape el hero */
  opacity: 0.55;
  mix-blend-mode: screen;
}
@media only screen and (max-width: 767px){
  .ka-hero-logo.is-ready{
    opacity: 0.42;
  }
}
/* Modo claro: screen se come el brillo; usamos multiply suave + más opacidad */
[color-scheme=light] .ka-hero-logo.is-ready{
  opacity: 0.4;
  mix-blend-mode: multiply;
}
.ka-hero-logo.is-hidden{
  display: none;
}
.ka-hero-logo__canvas{
  display: block;
  width: 100%;
  height: 100%;
}
/* Degradado inferior fuera del blend del canvas: suaviza el corte
   del logo 3D contra el fondo (screen/multiply no sirven aquí). */
.mxd-hero-section.mxd-hero-fullheight::after{
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: min(28vh, 220px);
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(var(--base-rgb), 0) 0%,
    rgba(var(--base-rgb), 0.55) 45%,
    rgba(var(--base-rgb), 1) 100%
  );
}
/* Contenido del hero por encima del canvas y del degradado */
.mxd-hero-01{
  position: relative;
  z-index: 2;
}
.mxd-hero-01__wrap{
  position: relative;
  z-index: 1;
}

.mxd-hero-01 .hero-01-title{
  position: relative;
  z-index: 3;
}

/* Header fijo: logo + Contact reaparecen al hacer scroll up.
   Con el menú al 50%, Contact us / theme siguen visibles: si el
   header baja a z-index 10 (Rayo), quedan bajo el overlay y dejan
   de responder. Lo mantenemos por encima de .mxd-nav__wrap (100).
   Importante: NO poner transform en reposo — si no, el ::before
   fixed del blur se ancla al ancho del header (no al viewport). */
.mxd-header{
  position: fixed;
  z-index: 200;
  isolation: isolate;
  /* Anula el scaleY(1) de Rayo: cualquier transform hace que el
     ::before fixed se limite al ancho del header. */
  transform: none;
  transition:
    transform var(--_animspeed-medium) var(--_animbezier),
    opacity var(--_animspeed-medium) var(--_animbezier);
}
.mxd-header.menu-is-visible{
  z-index: 110;
}
/* Con el menú abierto (o cerrándose), Contact us se oculta.
   menu-contact-hidden dura hasta onReverseComplete — si no,
   el botón parpadea encima del panel al empezar el cierre. */
.mxd-header.menu-is-visible .mxd-header__controls,
.mxd-header.menu-contact-hidden .mxd-header__controls{
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
/* En <1200px el panel es a ancho completo: el header (z-index 110)
   quedaría encima de la caja del menú. Mismo criterio que Contact us.
   En desktop el menú es solo el 50% derecho y el logo no se solapa. */
@media only screen and (max-width: 1199px){
  .mxd-header.menu-is-visible .mxd-header__logo,
  .mxd-header.menu-contact-hidden .mxd-header__logo{
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
}
/* Rayo: .mxd-nav__contain { pointer-events: none } y el hijo
   hamburger { auto }. El PADRE .mxd-nav__wrap seguía en auto y,
   al ser fixed a ancho completo, se tragaba los clics de Contact
   us cuando el header quedaba debajo (p. ej. tras Escape). */
.mxd-nav__wrap{
  pointer-events: none;
}
.mxd-nav__hamburger,
.mxd-menu__wrapper{
  pointer-events: auto;
}
/* La barra superior entera se oculta al bajar y vuelve al subir: el header
   (logo + Contact) y también el hamburger. Antes el hamburger se quedaba fijo
   y era lo único que flotaba sobre el contenido al hacer scroll.

   El hamburger se mueve con `header-is-hidden`, la clase que app.js pone en
   <html>, y no con la del header, para que los dos salgan y entren a la vez.
   Con el menú abierto app.js quita esa clase, así que la X nunca desaparece.

   El desplazamiento va en rem y no en porcentaje porque los dos elementos
   miden distinto: en % cada uno viajaría a su ritmo y se notaría el desfase. */
.mxd-header.is-hidden{
  transform: translateY(-14rem);
  opacity: 0;
  pointer-events: none;
}
.mxd-nav__contain{
  transition:
    transform var(--_animspeed-medium) var(--_animbezier),
    opacity var(--_animspeed-medium) var(--_animbezier);
}
html.header-is-hidden .mxd-nav__contain{
  transform: translateY(-14rem);
  opacity: 0;
  pointer-events: none;
}
/* Velo al scroll up: capa PROPIA bajo el hamburger (100) y el header (200).
   Si fuera ::before del header, isolation+z-index 200 lo pintaría encima del menú. */
html.has-header-backdrop::before{
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 14rem;
  z-index: 90;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(var(--base-rgb), 0.78) 0%,
    rgba(var(--base-rgb), 0.45) 55%,
    rgba(var(--base-rgb), 0) 100%
  );
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
          backdrop-filter: blur(14px) saturate(1.15);
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 60%, transparent 100%);
  transition: opacity var(--_animspeed-medium) var(--_animbezier);
}
@media only screen and (min-width: 768px){
  html.has-header-backdrop::before{
    height: 16rem;
  }
}
html.has-header-backdrop.header-is-hidden::before{
  opacity: 0;
}

/* Switch light/dark desactivado: .mxd-color-switcher { display:flex }
   pisa el [hidden] del UA (mismo bug que cookies). */
#color-switcher,
.mxd-color-switcher{
  display: none !important;
}

/* Temporal: ocultar bloque inferior del hero (CTA / socials / vídeo) */
.mxd-hero-01__bottom{
  display: none !important;
}

/* Rayo usa <em> → cursiva; KA quiere la misma familia sin italic */
.mxd-hero-01 .hero-01-title,
.mxd-hero-01 .hero-01-title__item,
.mxd-hero-01 .ka-hero-pill__track > span{
  font-style: normal;
}

/* ---------------------------------------------------------------------------
   Tracking de los titulares de todo el sitio
   ---------------------------------------------------------------------------
   Un pelo de separación entre letras en TODOS los titulares (h1 a h6), no
   solo en el hero: los pesos medios de Funnel Display se cierran mucho a
   tamaño grande y el titular gana aire sin tocar el cuerpo de texto, que se
   queda como está.

   Un solo mando para el sitio entero: --ka-track. El hero lo hereda de aquí
   y lo usa además para compensar el hueco final dentro de la pastilla.

   Comprobado que ningún titular cambia de número de líneas ni desborda en
   ninguna página, ni en escritorio ni a 320px.
   --------------------------------------------------------------------------- */
:root{
  --ka-track: 0.02em;
}
h1, h2, h3, h4, h5, h6{
  letter-spacing: var(--ka-track);
}

/* El tracking hace más anchos los titulares, y varios iban pegados al borde
   de su columna: «Product context» y «Main challenges» en las cinco páginas
   de caso, «Recent articles» en la home, las fichas de Success stories, un
   capítulo de YBVR y el titular del artículo de blog. Todos ganaban una
   línea.

   En vez de dejar esos sin tracking —serían los únicos del sitio— o de ir
   parcheando uno a uno, el h2 baja de 7 a 6,6rem en todo el sitio. Es un 6%
   que a este cuerpo no se ve, mantiene una sola medida de h2 y devuelve el
   aire con margen. h1 y h3 solo lo necesitaban en un sitio cada uno, así que
   ahí sí va puntual.

   «Main challenges» ya se partía por debajo de 1465px de ventana; con este
   ajuste aguanta en una línea desde 1439px, así que además de arreglarlo,
   mejora. */
@media only screen and (min-width: 768px){
  h2{
    font-size: 6.6rem;
  }
}
@media only screen and (min-width: 1200px) and (max-width: 1599px){
  /* Misma razón en los titulares de página («Notes from inside the headset»,
     el del artículo de blog): a 10rem se salían por 8px con el tracking.
     Solo hace falta en esta franja: por debajo hay sitio y por encima esos
     titulares ya venían en dos líneas de antes. El h1 del hero no se entera,
     tiene su propio tamaño en .hero-01-title. */
  h1{
    font-size: 9.5rem;
  }
}
.ka-chapter__title{               /* capítulos de las páginas de caso */
  font-size: clamp(2.2rem, 2vw, 2.9rem);
}

/* ---------------------------------------------------------------------------
   Tracking del titular
   ---------------------------------------------------------------------------
   Un pelo de separación entre letras en las tres piezas del hero: «We love»,
   la palabra de la pastilla y la línea de abajo. Un solo mando, --ka-track.

   El tracking añade el hueco DETRÁS de cada letra, también de la última. En
   «We love» ese hueco sobrante empujaría la pastilla hacia la derecha, justo
   lo contrario de lo que se busca, así que se descuenta con un margen
   negativo del mismo tamaño. (Dentro de la pastilla se compensa por delante;
   está explicado en su bloque.)

   Comprobado que no rompe el móvil: con este tracking, la fila 1 admite
   hasta 10,5vw a 320px y se está usando 9,2vw.
   --------------------------------------------------------------------------- */
.mxd-hero-01 .hero-01-title{
  /* Correccion optica de la primera linea: el texto sube 2px DENTRO de la
     pastilla, que no se mueve. Con el anillo tan grande, la linea se leia
     baja aunque estuviera centrada por metricas. Se aplica igual a «We love»
     y a la palabra de dentro para que sigan cuadradas entre si. */
  --ka-subir: 2px;
}
.mxd-hero-01 .hero-01-title__item,
.mxd-hero-01 .ka-hero-pill__track > span{
  letter-spacing: var(--ka-track);
}
.mxd-hero-01 .hero-01-title__row:first-child > .hero-01-title__item:not(.ka-hero-pill){
  margin-right: calc(-1 * var(--ka-track));
  /* transform, no margen: sube sin mover el hueco que ocupa en la fila */
  transform: translateY(calc(-1 * var(--ka-subir)));
}

/* ---------------------------------------------------------------------------
   Rotator vertical dentro de la pastilla (estilo marquee Rayo)
   ---------------------------------------------------------------------------
   El JS (ka-hero-words.js) anima el translateY del track y el width de la
   pastilla. Aquí se resuelve la parte tipográfica, que antes iba a ojo: cada
   palabra se centraba en un hueco de 1em y luego se corregía con un
   translateY distinto por palabra (-2px, 0, -0.1em-2px según llevara
   descendentes). Por eso la línea base bailaba al rotar.

   Ahora la línea base es la misma para todas las palabras, y sale sola de
   las métricas de Funnel Display, sin corregir nada a mano:

     ascendente de la fuente  1,000em      caja alta (H)   0,675em
     descendente de la fuente 0,250em      descendente (g) 0,210em
     ascendente real (l)      0,700em

   Con `line-height: 1` en una caja de 1em, el navegador reparte el
   interlineado sobrante —(1 − 1,25)/2— y deja la línea base a 0,875em del
   borde superior del hueco. Ese número no depende de la palabra, solo de la
   fuente: por eso «XR» y «Design» se apoyan exactamente en la misma línea.

   El hueco mide 1,1em para que quepa la descendente (0,875 + 0,210 = 1,085)
   sin que el overflow del viewport la corte.

   ALTO Y CENTRADO ÓPTICO DE LA PASTILLA
   El ojo no centra por la caja em, centra por la mancha de texto: de la caja
   alta a la línea base. Así que la pastilla se construye desde ahí: la banda
   de caja alta (0,675em) más el mismo aire por arriba y por abajo
   (--ka-aire). Sea la palabra en versales o con descendentes, la pastilla
   mide igual y se ve igual de centrada.

   --ka-aire es el único número de gusto de todo esto. 0,32em deja 0,11em
   entre la descendente y el borde inferior; por debajo de 0,29em la «g» de
   Design empieza a rozarlo. Sobre ese centrado va luego --ka-subir, la
   corrección óptica de 2px que levanta el texto sin mover el anillo.

   --ka-shift: la fila del titular es flex con align-items:center y la
   pastilla es más alta que la línea de texto, así que manda ella y el texto
   queda centrado dentro. Eso deja la palabra 0,0375em por encima de la base
   de «We love»; el shift la baja. Al ir todo en em, vale a cualquier tamaño.
   --------------------------------------------------------------------------- */
.hero-01-title__item.ka-hero-pill{
  --ka-pill-gap: 0.55em; /* aire entre palabras en el track */
  --ka-cap: 0.675em;     /* caja alta de Funnel Display */
  --ka-base-hueco: 0.875em; /* línea base dentro del hueco (line-height:1) */
  --ka-slot: 1.1em;      /* alto del hueco: base + descendente + holgura */
  --ka-aire: 0.32em;     /* aire óptico sobre la caja alta y bajo la base */
  --ka-borde: 7px;
  --ka-shift: 0.0375em;  /* cuadra la base de la palabra con la de «We love» */
  opacity: 1;
  display: inline-flex;
  align-items: flex-start; /* el hueco se ancla arriba, no centrado */
  justify-content: center;
  box-sizing: border-box;
  height: calc(var(--ka-cap) + 2 * var(--ka-aire));
  /* ~XR: evita FOUC al ancho de Design/Testing antes de que JS mida */
  width: 2.4em;
  margin-inline: 0.02em 0.12em; /* pegada a «love» por la izquierda */
  padding: 0 0.42em;
  background-color: rgba(0, 0, 0, 0.39);
  border: var(--ka-borde) solid rgba(255, 255, 255, 0.28);
  box-shadow: none;
  border-radius: 999px;
  color: #fff;
  vertical-align: middle;
  transform: translateY(var(--ka-shift));
  transition: width 0.55s cubic-bezier(0.7, 0, 0.3, 1);
  will-change: width;
}
.hero-01-title__item.ka-hero-pill:not(.is-ready){
  transition: none;
}
.ka-hero-pill__viewport{
  display: block;
  height: var(--ka-slot);
  overflow: hidden;
  width: 100%;
  /* Baja el hueco hasta que su línea base caiga a --ka-aire + caja alta del
     borde superior de la pastilla. Se resta el borde porque el contenido
     empieza ya por dentro de él. */
  margin-top: calc(var(--ka-cap) + var(--ka-aire) - var(--ka-base-hueco) - var(--ka-borde) - var(--ka-subir));
}
.ka-hero-pill__track{
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center; /* intrinsic width por palabra (stretch rompía el measure) */
  gap: var(--ka-pill-gap);
  transition: transform 0.55s cubic-bezier(0.7, 0, 0.3, 1);
  will-change: transform;
}
.ka-hero-pill__track > span{
  flex: 0 0 var(--ka-slot);
  height: var(--ka-slot);
  display: block;
  line-height: 1;   /* de aquí sale la línea base común */
  text-align: center;
  font-style: normal;
  color: #fff;
  white-space: nowrap;
  box-sizing: border-box;
  /* El tracking mete un hueco DETRÁS de la última letra, así que la palabra
     se iría a la izquierda del centro. El mismo hueco por delante lo cuadra. */
  padding: 0 0.04em 0 calc(0.04em + var(--ka-track));
}

/* Segunda línea del hero: "and we know how to make it right".
   ------------------------------------------------------------
   OJO CON LA CASCADA: el tamaño de esta línea NO se decide aquí.
   Lo fijan las tres media queries de más abajo, que cubren todos
   los anchos y tapan cualquier font-size que se ponga en esta
   regla base:

       ≤767px   → bloque móvil (clamp)
       ≥768px   → 0.5em
       ≥1200px  → 0.6em

   Aquí solo van las propiedades que ninguna de ellas pisa. */
.hero-01-title__item.ka-hero-line-long{
  letter-spacing: 0.01em;
  line-height: 1.1;
}
/* Cuadrado del titular: un poco más pequeño que Rayo */
@media only screen and (min-width: 768px){
  .hero-01-title__item.title-item-image svg{
    width: 4rem;
    height: 4rem;
  }
}
@media only screen and (min-width: 1200px){
  .hero-01-title__item.title-item-image svg{
    width: 6.4rem;
    height: 6.4rem;
  }
}
@media only screen and (min-width: 1600px){
  .hero-01-title__item.title-item-image svg{
    width: 7rem;
    height: 7rem;
  }
}
/* Banda estrecha 768–849px: aquí el h1 de Rayo salta a 8rem (80px)
   pero el padding del contenedor ya es de 6rem por lado, así que
   "We love Testing" se sale por 19px. Se sujeta con vw hasta que
   la pantalla da de sí. */
@media only screen and (min-width: 768px) and (max-width: 849px){
  .mxd-hero-01 .hero-01-title,
  .mxd-hero-01 .hero-01-title__item{
    font-size: min(9.3vw, 8rem);
  }
}

@media only screen and (min-width: 768px){
  .hero-01-title__item.ka-hero-line-long{
    font-size: 0.5em;
  }
}
@media only screen and (min-width: 1200px){
  /* 0.6em por decisión de Celia */
  .hero-01-title__item.ka-hero-line-long{
    font-size: 0.6em;
  }
}
/* ------------------------------------------------------------
   HERO EN MÓVIL (≤767px)
   ------------------------------------------------------------
   Objetivo: "We love [pill]" en una sola línea horizontal, y
   justo debajo "and we know how to make it right" también en
   una línea, más pequeña.

   Dos cosas que había que resolver:

   1. Rayo pone .hero-01-title__row en horizontal SOLO a partir
      de 768px (main.css:7701). Por debajo es columna, así que
      "We love" y la pill se apilaban. Se fuerza row aquí.

   2. El ancho lo manda la palabra más larga del rotador, no la
      que se vea en cada momento. Con "Testing" la fila ocupa
      8,26em; con "AI", 5,74em. Si se dimensiona mirando "AI",
      al rotar a "Testing" se sale de la pantalla.

   Medido contra el ancho útil (viewport − 3rem de padding):
   el h1 no puede pasar de ~9,7vw en ningún móvil. Se usa 9,2vw
   para dejar margen sobre la estimación tipográfica.

   Si algún día se añade una palabra más larga que "Testing" al
   rotador, hay que rebajar ese 9,2vw.
   ------------------------------------------------------------ */
@media only screen and (max-width: 767px){

  /* Fila 1 en horizontal, sin partirse */
  .mxd-hero-01 .hero-01-title__row{
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    gap: 0;
  }

  /* Cuerpo del titular, atado al peor caso del rotador */
  .mxd-hero-01 .hero-01-title,
  .mxd-hero-01 .hero-01-title__item{
    font-size: clamp(2.2rem, 9.2vw, 6rem);
    white-space: nowrap;
  }

  /* Línea 2: una sola línea, claramente menor que la de arriba */
  .hero-01-title__item.ka-hero-line-long{
    font-size: clamp(1.5rem, 4.6vw, 2.6rem);
    white-space: nowrap;
    text-align: center;
  }

  /* El alto ya lo calcula la pastilla sola (caja alta + aire), así que aquí
     solo se toca lo que no escala: el borde, que en px se comería la caja a
     este tamaño. Al cambiar --ka-borde, el hueco se recoloca solo. */
  .hero-01-title__item.ka-hero-pill{
    --ka-borde: 3px;
    margin-inline: 0.18em 0.28em;
    padding: 0 0.38em;
  }
}

/* Home — "About me": el manifiesto ("I'm Celia...") con un pelín menos de
   tamaño que el de Rayo, para que se equilibre mejor con el resto de la
   sección. Solo se usa aquí (.mxd-manifest sin más contexto no aparece en
   ninguna otra página). */
.ka-top-hero .mxd-manifest{
  line-height: 1.5;
}
@media only screen and (min-width: 768px){
  .ka-top-hero .mxd-manifest{
    font-size: 4rem;
  }
}

/* Menos margen derecho SOLO en esta sección (no el .mxd-container global,
   que se comparte con el resto del sitio), para que el bloque de texto
   respire un poco más hacia el borde. */
@media only screen and (min-width: 768px){
  .ka-top-hero .mxd-container.grid-container{
    padding-right: 3rem;
  }
}
@media only screen and (min-width: 1600px){
  .ka-top-hero .mxd-container.grid-container{
    padding-right: 6rem;
  }
}

/* Loader: oculto hasta que JS lo active tras 3 s de espera (ver app.js). */
.loader{
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.25s ease, visibility 0.25s ease;
}
.loader.ka-loader-active{
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.loader.loaded{
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}


/* ============================================================
   COOKIE CONSENT (RGPD)
   ------------------------------------------------------------
   Portado del sitio anterior. Rayo no trae banner de cookies y
   es obligatorio. Las variables del sistema viejo se han
   mapeado a las de Rayo, así que ahora responde al conmutador
   de modo claro/oscuro como el resto de la web.

   El bug de [hidden] sigue documentado: .cookie-modal y
   .cookie-trigger necesitan la regla display:none explícita,
   porque su propio display (flex/inline-flex) anula el hidden
   nativo y bloquean clics en toda la página aunque estén
   "ocultos".
   ============================================================ */

:root{
  --ka-container: 1440px;
  --ka-gutter: 2.4rem;
}

/* Cookie consent — banner + preferences modal.
   RGPD: aceptar y rechazar tienen el mismo peso visual;
   nada de terceros se activa hasta que la persona elige. */
.cookie-banner{
  position:fixed;
  left:0; right:0; bottom:0;
  z-index:9998;
  background:rgba(18,12,30,0.96);
  backdrop-filter:blur(12px);
  border-top:1px solid var(--st-muted);
  transform:translateY(100%);
  transition:transform .35s cubic-bezier(.16,1,.3,1);
  padding-bottom:env(safe-area-inset-bottom, 0);
}
.cookie-banner.is-visible{transform:translateY(0);}
.cookie-banner__inner{
  max-width:var(--ka-container);
  margin:0 auto;
  padding:24px var(--ka-gutter);
  display:flex;
  align-items:center;
  gap:24px;
}
.cookie-banner__icon{width:56px; height:56px; object-fit:contain; flex-shrink:0;}
.cookie-banner__body{flex:1; min-width:200px;}
.cookie-banner__body p{font-size:14px; line-height:1.55; color:var(--t-muted);}
.cookie-banner__body a{
  color: var(--ka-link);
  background: none; /* anula el subrayado-gradient gris de Rayo (`p a`) */
  text-decoration: underline;
  text-decoration-color: var(--ka-link);
  text-underline-offset: 2px;
}
.no-touch .cookie-banner__body a:hover{
  color: var(--t-bright);
  text-decoration-color: var(--t-bright);
  background-size: unset;
}
.cookie-banner__actions{display:flex; align-items:center; gap:12px; flex-wrap:wrap; flex-shrink:0;}
.cookie-banner__actions .btn, .cookie-modal__actions .btn{
  font-size:14px;
  line-height:1;
  padding:12px 22px;
  gap:6px;
  white-space:nowrap;
}
@media(max-width:760px){
  .cookie-banner__inner{flex-direction:column; align-items:flex-start; padding:20px var(--ka-gutter);}
  .cookie-banner__icon{width:44px; height:44px;}
  .cookie-banner__actions{width:100%;}
  .cookie-banner__actions .btn{flex:1; justify-content:center;}
}

/* El scroll vive AQUÍ, no en el panel.
   La galleta tiene que asomar por la esquina superior izquierda, y eso es
   imposible mientras el panel sea el elemento que se desplaza: `overflow-y`
   recorta todo lo que se salga. Moviendo el desplazamiento al contenedor, el
   panel puede desbordar libremente y además la X se puede quedar pegada
   arriba con `position: sticky`, que dentro de un `absolute` no funcionaría.

   `align-items: flex-start` + `margin: auto` en el panel: así queda centrado
   cuando cabe y no se le corta la cabeza cuando no cabe, que es lo que pasa
   con `align-items: center` en un contenedor con scroll. */
.cookie-modal{
  position:fixed; inset:0;
  z-index:10000;
  display:flex;
  align-items:flex-start;
  justify-content:center;
  padding:24px;
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
}
.cookie-modal[hidden]{display:none;}
/* Fijo, no absoluto: dentro de un contenedor con scroll, `inset:0` sobre
   `absolute` solo cubriría la primera pantalla. */
.cookie-modal__overlay{
  position:fixed; inset:0;
  background:rgba(10,7,16,0.72);
  backdrop-filter:blur(6px);
  opacity:0;
  transition:opacity .3s ease;
}
.cookie-modal.is-visible .cookie-modal__overlay{opacity:1;}
.cookie-modal__panel{
  position:relative;
  width:100%; max-width:520px;
  margin:auto;
  background:var(--base-tint);
  border:1px solid var(--st-muted);
  border-radius:0 32px 0 32px;
  /* Más aire arriba: la galleta ya no ocupa sitio en el flujo y el título
     se subiría hasta debajo de ella. */
  padding:88px 40px 40px 40px;
  transform:translateY(16px) scale(0.97);
  opacity:0;
  transition:transform .35s cubic-bezier(.16,1,.3,1), opacity .3s ease;
}
.cookie-modal.is-visible .cookie-modal__panel{transform:translateY(0) scale(1); opacity:1;}
/* La X se queda arriba mientras se baja por el panel, igual que la barra
   superior del sitio. Va flotada a la derecha en vez de en bloque para que la
   galleta pueda subir a su misma línea; y lleva fondo propio porque, al
   quedarse pegada, el texto le pasaría por debajo. */
.cookie-modal__close{
  position:sticky; top:0;
  float:right;
  z-index:2;
  /* Arriba del todo, en la esquina: el margen negativo la saca del relleno
     superior del panel (88px) y la deja a 20px de sus dos bordes. */
  margin:-68px -20px 0 0;
  width:36px; height:36px;
  border-radius:50%;
  border:none;
  background:var(--base-tint);
  color:var(--t-muted);
  font-size:16px;
  cursor:pointer;
  transition:background .15s ease, color .15s ease;
}
.cookie-modal__close:hover{background:var(--st-muted); color:var(--t-bright);}
/* La galleta, grande y asomando por la esquina superior izquierda. Esa
   esquina es la única del panel sin redondear (el radio es 0 32px 0 32px),
   así que es donde el desbordamiento se lee como una decisión y no como un
   descuadre. z-index por encima de la X, como pidió Celia.

   Va POSICIONADA, no en el flujo con márgenes negativos. Con la X flotada a
   la derecha, el navegador aplica la regla de que un bloque reemplazado no
   puede solaparse con un flotante y recoloca la imagen al inicio del área de
   contenido, descartando su margen izquierdo negativo: el margen superior sí
   surtía efecto y el izquierdo no. Al sacarla del flujo deja de competir con
   el flotante, y el hueco que ocupaba se lo devuelve el padding del panel. */
.cookie-modal__icon{
  position:absolute;
  top:-40px; left:-40px;
  z-index:3;
  display:block;
  width:112px; height:112px;
  object-fit:contain;
  margin:0;
}
@media only screen and (max-width: 560px){
  /* Menos vuelo: en un teléfono el panel casi toca los bordes y la galleta
     se saldría de la pantalla. */
  .cookie-modal__icon{
    top:-24px; left:-20px;
    width:84px; height:84px;
  }
}
.cookie-modal__panel h2{
  font-family:var(--_font-accent);
  font-weight:800;
  font-size:24px;
  margin-bottom:12px;
}
.cookie-modal__intro{font-size:14px; line-height:1.55; color:var(--t-muted); margin-bottom:24px;}
/* Cada categoría es una fila: texto a la izquierda, interruptor a la derecha
   y CENTRADO respecto al bloque entero, no pegado al nombre. Antes el
   interruptor vivía en la misma línea que el título y quedaba arriba del
   todo, descolgado de las dos líneas de texto que tenía debajo. */
.cookie-cat{
  display:flex;
  align-items:center;
  gap:20px;
  border-top:1px solid var(--st-muted);
  padding:18px 0;
}
.cookie-cat:first-of-type{border-top:none; padding-top:0;}
.cookie-cat__text{flex:1 1 auto; min-width:0;}
.cookie-cat__name{display:block; margin-bottom:6px; font-family:var(--_font-accent); font-weight:700; font-size:15px; color:var(--t-bright); cursor:pointer;}
.cookie-cat p{font-size:13px; line-height:1.55; color:var(--t-muted-extra);}
.cookie-toggle{
  position:relative;
  width:40px; height:24px;
  border-radius:999px;
  background:var(--st-muted);
  border:none;
  padding:0;
  cursor:pointer;
  flex-shrink:0;
  transition:background .2s ease;
}
.cookie-toggle__knob{
  position:absolute; top:3px; left:3px;
  width:18px; height:18px;
  border-radius:50%;
  background:var(--t-bright);
  transition:transform .2s ease;
}
.cookie-toggle.is-on{background:var(--ka-link);}
.cookie-toggle.is-on .cookie-toggle__knob{transform:translateX(16px);}
.cookie-toggle.is-locked{opacity:0.55; cursor:default;}
.cookie-modal__actions{
  display:flex; flex-wrap:wrap; gap:12px;
  margin-top:28px;
}
.cookie-modal__actions .btn{flex:1; justify-content:center; min-width:140px;}
@media(max-width:480px){
  .cookie-modal__panel{padding:28px;}
  .cookie-modal__actions{flex-direction:column;}
  .cookie-modal__actions .btn{width:100%;}
}

.cookie-modal__legal{
  display:flex; flex-wrap:wrap; gap:8px;
  margin-top:20px;
  padding-top:20px;
  border-top:1px solid var(--st-muted);
  font-size:12.5px;
  color:var(--t-muted-extra);
}
.cookie-modal__legal a{
  color:var(--t-muted-extra);
  border-bottom:1px solid rgba(183,171,201,0.35);
  transition:color .15s ease, border-color .15s ease;
}
.cookie-modal__legal a:hover{color:var(--t-bright); border-color:var(--t-bright);}
/* El punto separador es un elemento aparte, no un ::before del enlace.
   Dentro del <a>, el `border-bottom` del propio enlace le pasaba por debajo
   y el subrayado salía corrido de una palabra a la siguiente, punto incluido.
   Un pseudoelemento no puede escaparse de la línea de su padre. */
.cookie-modal__sep{color:var(--t-muted-extra); border-bottom:none;}

html.cookie-modal-open,
body.cookie-modal-open{
  overflow:hidden;
  overscroll-behavior:none;
}
/* Lenis + overflow:hidden no bastan solos; stop() en cookie-consent.js */

.cookie-trigger{
  position:fixed;
  left:24px;
  bottom:24px;
  bottom:max(24px, env(safe-area-inset-bottom, 24px));
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:999px;
  background:var(--base-tint);
  border:1px solid var(--st-muted);
  padding:10px;
  cursor:pointer;
  z-index:9997;
  box-shadow:4px 4px 24px rgba(0,0,0,0.35);
  transition:transform .15s ease, border-color .15s ease;
}
.cookie-trigger[hidden]{display:none;}
.cookie-trigger:hover{transform:scale(1.04); border-color:var(--accent);}
.cookie-trigger img{width:32px; height:32px; object-fit:contain; flex-shrink:0;}

@media(prefers-reduced-motion: reduce){
  .cookie-banner, .cookie-modal__overlay, .cookie-modal__panel, .cookie-toggle, .cookie-toggle__knob, .cookie-trigger{transition:none;}
}


/* ============================================================
   CORRECCIÓN DE CONTRASTE EN SUPERFICIES DE COLOR
   ------------------------------------------------------------
   Rayo resuelve el texto de sus botones de color con --t-bright
   y --t-opp-bright, que cambian con el modo. Con SU paleta
   funciona; con la de KA no, porque nuestro verde y nuestro
   magenta son mucho más claros que su lila y su lima.

   Medido:
     .btn-additional (verde #39ff88) + --t-bright
        · en oscuro  #f4f5f6 sobre #39ff88  =  1.33:1   ilegible
        · en claro   #17101f sobre #39ff88  = 15.07:1   bien
     .btn-accent (magenta) + --t-opp-bright
        · en oscuro  #0a0710 sobre #b026ff  =  4.35:1   solo AA grande

   El verde y el magenta son claros en los DOS modos, así que su
   texto no debe depender del modo: se fija aquí y se acabó.
   ============================================================ */

/* Verde: siempre tinta oscura encima. 15.07:1 en ambos modos. */
.btn-additional,
.btn-additional:hover,
.bg-additional,
.bg-additional h1, .bg-additional h2, .bg-additional h3,
.bg-additional p,  .bg-additional span{
  color: #0a0710;
}
.btn-additional i{ color: #0a0710; }

/* Magenta: siempre texto claro encima.
   4.60:1 en oscuro (#b026ff) y 6.33:1 en claro (#9315d6). Los dos AA. */
.btn-accent,
.btn-accent:hover,
.bg-accent,
.bg-accent h1, .bg-accent h2, .bg-accent h3,
.bg-accent p,  .bg-accent span{
  color: #ffffff;
}
.btn-accent i{ color: #ffffff; }

/* Excepción: las etiquetas de contorno sobre magenta van enteras en tinta
   oscura —borde y texto del mismo color—, por decisión de Celia.
   Rayo ya las pintaba así (`--t-opp-bright`); lo que las volvía blancas era
   la regla de arriba, que pinta de blanco TODO lo que va sobre `.bg-accent`
   para garantizar el contraste de los botones.

   Medido: #0a0710 sobre #b026ff da 4.35:1, un pelo por debajo del 4.5:1 que
   pide AA para texto pequeño. Se asume a sabiendas: son etiquetas
   decorativas y lo que dicen está repetido en el cuerpo de la tarjeta.
   En modo claro, sobre #9315d6, bajaría a 3.16:1 — si algún día se activa el
   conmutador, esto hay que revisarlo. Anotado en ESTADO.md. */
/* REVISADO: vuelven a BLANCO PURO. La tinta oscura da 4,04:1 sobre el
   magenta y el blanco 4,60:1, así que ya era la peor de las dos opciones;
   pero además, sobre un fondo tan saturado, el texto oscuro se lee mucho
   peor de lo que dice su ratio, y así lo reportaron los primeros visitantes.
   Es la regla que ya estaba escrita en el proyecto: sobre verde, tinta
   oscura; sobre magenta, texto blanco. */
.bg-accent .tag-outline-opposite{
  color: #ffffff;
  border-color: #ffffff;
}
.bg-accent p.t-opposite{ color: #ffffff; }

/* Tarjeta magenta de stats: .opposite / p.t-bright.opposite ganan a
   las reglas de arriba y pintan el copy casi negro (--t-opp-bright). */
.bg-accent .mxd-counter__number,
.bg-accent .mxd-counter__descr,
.bg-accent .mxd-stats-number.opposite,
.bg-accent p.t-bright.opposite,
.bg-accent .opposite,
.bg-accent .btn-muted,
.bg-accent .btn-muted .btn-caption,
.bg-accent .btn-muted i{
  color: #ffffff;
}

/* ============================================================
   STATS CARDS — pinwheel abajo-izquierda + hover zoom/rotate
   ============================================================ */
.mxd-stats-cards__image.mxd-stats-cards-image-1{
  top: auto;
  left: -2rem;
  right: auto;
  bottom: -2rem;
  padding: 0;
}
@media only screen and (min-width: 768px){
  .mxd-stats-cards__image.mxd-stats-cards-image-1{
    width: 420px;
    left: -4rem;
    right: auto;
    bottom: -4rem;
  }
}
@media only screen and (min-width: 1200px){
  .mxd-stats-cards__image.mxd-stats-cards-image-1{
    width: 300px;
    left: -3rem;
    right: auto;
    bottom: -3rem;
  }
}
@media only screen and (min-width: 1600px){
  .mxd-stats-cards__image.mxd-stats-cards-image-1{
    width: 420px;
    left: -4rem;
    right: auto;
    bottom: -4rem;
  }
}

.mxd-stats-cards__inner{
  transform-origin: center center;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.mxd-stats-cards__image{
  transform-origin: center center;
  transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.no-touch .mxd-stats-cards__inner:hover{
  transform: scale(1.03);
}
.no-touch .mxd-stats-cards__inner:hover .mxd-stats-cards__image{
  transform: scale(1.12);
}
@media (prefers-reduced-motion: reduce){
  .mxd-stats-cards__inner,
  .mxd-stats-cards__image{
    transition: none;
  }
  .no-touch .mxd-stats-cards__inner:hover,
  .no-touch .mxd-stats-cards__inner:hover .mxd-stats-cards__image{
    transform: none;
  }
}


/* ============================================================
   FONDOS DE DEMO QUE APUNTABAN A UN SERVICIO EXTERNO
   ------------------------------------------------------------
   main.css trae 33 reglas con background-image apuntando a
   dummyimage.com. Siete de ellas están en uso en el sitio —entre
   otras .mxd-promo__bg, el fondo del bloque de llamada a la acción
   que aparece en casi todas las páginas— así que la web dependía
   de un servicio de terceros para pintarse.

   Aquí se redirigen todas al marcador local. Cuando lleguen las
   imágenes reales de los proyectos, esto es lo que hay que ir
   sustituyendo una a una.
   ============================================================ */
.about-image-1,
.divider-image-10,
.divider-image-2,
.divider-image-3,
.divider-image-4,
.divider-image-5,
.divider-image-6,
.divider-image-7,
.divider-image-8,
.divider-image-9,
.headline-img-01::before,
.headline-img-02::before,
.headline-img-03::after,
.headline-img-04::before,
.headline-img-05::before,
.headline-img-06::before,
.headline-img-07::before,
.image-values-1,
.image-values-2,
.preview-image-4,
.preview-image-5,
.preview-image-6,
.preview-image-7,
.prj-details-img-01,
.prj-details-img-02{
  background-image: url('../img/siri__bb6p4jhfelki_large_2x.jpg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* ---------------------------------------------------------------------------
   Avatares circulares — Rayo deja height:auto
   ---------------------------------------------------------------------------
   .mxd-avatars__item mide 8rem × 8rem con overflow:hidden, pero su <img>
   lleva `height: auto` en main.css. Con una foto cuadrada da igual; con una
   apaisada la imagen sólo ocupa una franja horizontal y queda flotando en
   medio del círculo, con hueco arriba y abajo. Se fuerza a rellenar.
   --------------------------------------------------------------------------- */
.mxd-avatars__item img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* ---------------------------------------------------------------------------
   Fondos reales
   ---------------------------------------------------------------------------
   Rayo declara estas clases con una URL de dummyimage. Arriba quedan todas
   apuntando al marcador; aquí se les da su imagen definitiva. El orden importa:
   este bloque va después, así que gana.
   --------------------------------------------------------------------------- */


/* proyecto destacado 1 — ENGAGE XR */
/* La tarjeta es vertical (0,73 en escritorio, 0,60 en el teléfono) y la foto
   es apaisada, así que siempre se recorta por los lados: se ve una franja
   vertical del centro y se pierden los dos extremos.

   OJO AL CAMBIAR ESTA FOTO. El encuadre estaba antes al 100% (pegado al
   borde derecho) porque en la imagen anterior la avatar estaba a la derecha
   y el logotipo de ENGAGE a la izquierda. La foto nueva tiene a la avatar
   justo en el centro, de modo que aquel 100% pasó a enseñar solo las islas
   flotantes del fondo: la mujer desaparecía de la tarjeta. Ahora va
   centrada, que es donde está ella. */
.preview-image-1{
  background-image: url('../img/engage-athina-ai-avatar.jpg');
  background-position: 50% center;
}

/* proyecto destacado 2 — YBVR */
.preview-image-2{ background-image: url('../img/ybvr-xtadium-feel-the-game.jpg'); }

/* proyecto destacado 3 — Oisoi / PaintingVR
   ---------------------------------------------------------------------------
   Aquí va una versión VERTICAL del key art, no la original. Esta tarjeta es
   la única de todo el sitio que enseña la imagen en retrato: 0,73 en
   escritorio y 0,60 en el teléfono. El key art es 16:9 y el logotipo amarillo
   ocupa el 53,8% de su ancho, mientras que un recorte a 0,60 solo deja ver el
   33,7%: el logo salía partido siempre, y no había `background-position` que
   lo arreglara.

   `paintingvr-key-art-vertical.jpg` es el mismo arte sobre un lienzo más alto,
   con el fondo azul noche extendido por arriba y por abajo y el logo centrado.
   Lo genera `_build/reencuadre_vertical.py`.

   Los demás sitios donde aparece PaintingVR son apaisados (2,04 / 1,38 / 1,35)
   y ahí el logo ya se veía entero: siguen con la imagen original. */
.preview-image-3{ background-image: url('../img/paintingvr-key-art-vertical.jpg'); }

/* tarjeta de blog 1 */
/* Las tres tarjetas del blog enseñan la MISMA foto partida en tres, como un
   carrusel de redes. No hay tres archivos: la imagen se escala al 300% del
   ancho de la tarjeta y cada una la coloca en un extremo.

   El truco está en cómo funciona `background-position` en porcentaje: no
   desplaza «el 50% del ancho», sino que reparte la diferencia entre imagen y
   contenedor. Con la imagen midiendo justo el triple, 0% deja ver el primer
   tercio, 50% el de en medio y 100% el último. Exactos, sin recortes ni
   cálculos con las canaladuras.

   OJO: estas tres tarjetas NO llevan `parallax-img-small`. Ukiyo escala y
   desplaza cada elemento por su cuenta según el scroll, así que las tres se
   movían de forma independiente y la foto dejaba de leerse como una sola.
   Si alguien devuelve esa clase, vuelven a salir las tres iguales. */
.blog-preview-image-1,
.blog-preview-image-2,
.blog-preview-image-3{
  background-image: url('../img/blog-designers-roles.jpg');
  background-size: 300% auto;
  background-repeat: no-repeat;
}
.blog-preview-image-1{ background-position:   0% center; }
.blog-preview-image-2{ background-position:  50% center; }
.blog-preview-image-3{ background-position: 100% center; }

/* En el teléfono, «Recent articles» enseña solo el primero. Los tres van a
   una columna y en pantalla pequeña se convierten en tres pantallas enteras
   de scroll antes de llegar al final de la home; con «All articles» justo
   debajo, los otros dos están a un toque.
   El combinador `~` coge todo `.mxd-blog-preview__item` precedido por otro,
   o sea del segundo en adelante, sin depender de cuántos haya.
   Solo en la home: en la página de blog se ven todos. */
@media only screen and (max-width: 767px){
  body[data-page="index.html"] .mxd-blog-preview__item ~ .mxd-blog-preview__item{
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   Artículo: las dos tarjetas comparadas
   ---------------------------------------------------------------------------
   Llevaban `padding-3`, que NO EXISTE en ninguna hoja de la plantilla: ni en
   plugins.css ni en main.css. Y `.mxd-services-cards__inner` tampoco declara
   relleno propio, así que el texto quedaba pegado a los cuatro cantos de la
   caja. Se le da el mismo aire que a las escenas de What is KA.
   --------------------------------------------------------------------------- */
.mxd-article .mxd-services-cards__inner{
  padding: 3.2rem 2.8rem;
}
@media only screen and (min-width: 768px){
  .mxd-article .mxd-services-cards__inner{ padding: 4.4rem 4rem; }
}
.mxd-article .mxd-services-cards__inner h3{
  margin-bottom: 1.6rem;
}
/* Los párrafos de dentro iban pegados uno detrás de otro, sin aire: la
   entradilla y el «What they handle» se leían como un solo bloque. */
.mxd-article .mxd-services-cards__inner p + p{
  margin-top: 2rem;
}
/* Las dos tarjetas van una junto a otra: en escritorio necesitan calle en
   medio, que la rejilla de Rayo (`g-0`) no les da. */
@media only screen and (min-width: 768px){
  .mxd-article .row.g-0 > .col-md-6:first-child .mxd-services-cards__inner{
    margin-right: 1.2rem;
  }
  .mxd-article .row.g-0 > .col-md-6:last-child .mxd-services-cards__inner{
    margin-left: 1.2rem;
  }
}
@media only screen and (max-width: 767px){
  .mxd-article .row.g-0 > .col-md-6 + .col-md-6 .mxd-services-cards__inner{
    margin-top: 2.4rem;
  }
}

/* ---------------------------------------------------------------------------
   Artículo: las citas
   ---------------------------------------------------------------------------
   Rayo las resuelve con dos filetes de un píxel, arriba y abajo, y nada más:
   la cita no se distingue del cuerpo del texto salvo por dos rayas.

   Aquí se traduce el lenguaje de What is KA: superficie tintada con el radio
   grande, mucho aire, y una figura enorme y muy tenue detrás — allí son el
   glifo egipcio y el kanji, aquí la propia comilla. Mismo lila
   (rgba(198,120,255)) y mismo papel: textura de fondo, nunca competencia.
   --------------------------------------------------------------------------- */
.mxd-article__block.block-quote{
  padding: 0;
  border: 0;
  margin: 5.6rem 0;
}
.mxd-article__block.block-quote blockquote{
  position: relative;
  overflow: hidden;
  margin: 0;
  padding: 4rem 2.8rem;
  border-radius: var(--_radius-l);
  background: var(--base-tint);
}
@media only screen and (min-width: 768px){
  .mxd-article__block.block-quote blockquote{ padding: 5.6rem 5.6rem 5.6rem 12rem; }
}
/* La comilla, de figura. `line-height: 1` y el desplazamiento negativo la
   suben hasta que su hombro queda a la altura de la primera línea del texto:
   una comilla tiene casi todo su dibujo en la mitad superior de la caja. */
.mxd-article__block.block-quote blockquote::before{
  content: '\201C';
  position: absolute;
  top: -2.4rem;
  left: 1.2rem;
  font-family: var(--_font-accent);
  font-size: 16rem;
  line-height: 1;
  color: rgba(198, 120, 255, 0.20);
  pointer-events: none;
}
@media only screen and (min-width: 768px){
  .mxd-article__block.block-quote blockquote::before{
    top: -1.6rem;
    left: 3.2rem;
    font-size: 20rem;
  }
}
.mxd-article__block.block-quote blockquote p{
  position: relative;
  z-index: 1;
  margin: 0;
  max-width: 52ch;
  font-family: var(--_font-accent);
  font-size: 2rem;
  line-height: 1.45;
  color: var(--t-bright);
}
@media only screen and (min-width: 768px){
  .mxd-article__block.block-quote blockquote p{ font-size: 2.6rem; }
}
.mxd-article__block.block-quote blockquote em{
  color: var(--ka-link);
  font-style: normal;
  font-weight: var(--fw-semibold);
}

/* ---------------------------------------------------------------------------
   Enlaces en verde: los legales del pie, los «Back» y el correo
   ---------------------------------------------------------------------------
   Se usa `--ka-link`, no el verde escrito a pelo. Ese token es verde sobre
   fondo oscuro y magenta en modo claro, porque el #39FF88 sobre blanco da
   1,22:1 y sencillamente no se ve. Es el mismo criterio que siguen todos los
   enlaces del sitio.

   La flecha va incluida: es parte del enlace, y dejarla del color anterior
   partía la pieza en dos.
   --------------------------------------------------------------------------- */
.footer-blocks__links .btn,
.footer-blocks__links .btn .btn-caption,
.footer-blocks__links .btn i,
.ka-back-link .btn,
.ka-back-link .btn .btn-caption,
.ka-back-link .btn i,
.ka-paginacion .btn,
.ka-paginacion .btn .btn-caption,
.ka-paginacion .btn i{
  color: var(--ka-link);
}
/* Rayo dibuja el subrayado de estos botones con un borde inferior. */
.footer-blocks__links .btn,
.ka-back-link .btn,
.ka-paginacion .btn{
  border-color: var(--ka-link);
}
.no-touch .footer-blocks__links .btn:hover,
.no-touch .footer-blocks__links .btn:hover .btn-caption,
.no-touch .footer-blocks__links .btn:hover i,
.no-touch .ka-back-link .btn:hover,
.no-touch .ka-back-link .btn:hover .btn-caption,
.no-touch .ka-back-link .btn:hover i,
.no-touch .ka-paginacion .btn:hover,
.no-touch .ka-paginacion .btn:hover .btn-caption,
.no-touch .ka-paginacion .btn:hover i{
  color: var(--t-bright);
  border-color: var(--t-bright);
}

/* El correo del pie: hover (color + flecha) vive en .ka-footer-mail. */

/* banda de llamada a la acción, en todas las páginas */
.mxd-promo__bg{ background-image: url('../img/celia-headset-controllers.jpg'); }

/* Vídeo en línea dentro de las piezas de proyecto. Se comporta como la
   imagen a la que sustituye: ancho completo y alto proporcional. */
.ka-video{
  display: block;
  width: 100%;
  height: auto;
  border-radius: inherit;
}

/* Vídeo que rellena su contenedor, como hace la imagen del divisor. */
.ka-video-cover{
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------------------------------------------------------------------------
   Vídeo incrustado de YouTube
   ---------------------------------------------------------------------------
   Se usa youtube-nocookie.com a propósito: no deja cookies de seguimiento
   mientras nadie pulsa play, que es lo que permite tenerlo antes de que el
   visitante acepte el banner. Si algún día se cambia al dominio normal, hay
   que engancharlo al evento ka:consent-updated.
   --------------------------------------------------------------------------- */
.ka-embed{
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--base-tint);
}
.ka-embed iframe{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Figura ancha con pie */
.ka-figure{ margin: 0; }
.ka-figure img{ display: block; width: 100%; height: auto; }
.ka-figure figcaption{ margin-top: 1.6rem; }

/* ---------------------------------------------------------------------------
   Texto pendiente de escribir
   ---------------------------------------------------------------------------
   Los pies que Celia tiene que rellenar. Se ven a propósito: si fueran
   invisibles se publicarían sin darse cuenta. Cada uno lleva además un
   comentario RELLENO en el HTML, así verify.py señala la página entera.
   Al escribir el texto definitivo se quita la clase y el comentario.
   --------------------------------------------------------------------------- */
.ka-por-rellenar{
  display: block;
  margin-top: 1.6rem;
  padding: 1.2rem 1.6rem;
  border: 1px dashed var(--accent);
  border-radius: 8px;
  font-style: italic;
}

/* ===========================================================================
   Maquetación de las páginas de caso
   ===========================================================================
   Referencia: Orionix y Monks. Lo que ambas hacen y aquí se copia:

     · capítulos numerados, con el número grande y separado del título;
     · el texto en una columna estrecha —64 caracteres— porque una línea
       larga en pantalla ancha no hay quien la lea;
     · las imágenes y vídeos siempre en el MISMO marco, con proporción fija,
       para que los bordes casen entre filas. Es lo que hacía que la versión
       anterior se viera desordenada: cada pieza traía su propio alto.

   Todo esto vive fuera del grid de Bootstrap de Rayo a propósito. Pelearse
   con col-xl-6 / col-xl-5 para conseguir columnas iguales daba filas
   descuadradas; con CSS grid son tres líneas.
   =========================================================================== */

.ka-case{
  display: flex;
  flex-direction: column;
  gap: 8rem;
}
@media only screen and (min-width: 992px){
  .ka-case{ gap: 12rem; }
}

/* Enlace de vuelta arriba a la izquierda, el mismo de las páginas de caso.
   Se usa también en las tres páginas legales, a las que se llega desde el pie
   y de las que antes no había forma de salir sin el menú. */
.ka-back-link{ margin-bottom: 2.4rem; }

/* ── Cabecera centrada ─────────────────────────────────────────────────── */
/* Rayo deja 14rem por debajo de la cabecera de página. En las que llevan una
   imagen grande justo después —los casos y Success stories— es demasiado y
   todo el contenido queda muy caído. Se reduce a la mitad. Se aplica con una
   clase, no tocando el valor general, para no alterar las legales ni el blog. */
.mxd-section-inner-headline.padding-default.ka-headline-tight{ padding-bottom: 7rem; }
/* Nombre del cliente y frase, centrados; debajo va la portada con máscara y
   después la ficha de datos. Las distancias son las de Rayo —3.5rem entre
   titular y párrafo, 5.5rem antes de las etiquetas— para que el ritmo sea el
   mismo que en el resto del sitio y ningún título quede pegado a su texto. */
.ka-case-head{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Aire igual arriba y abajo. Se queda corto a propósito: la sección ya
     trae `padding-default`, así que un valor grande aquí se suma al de fuera
     y deja media pantalla vacía debajo de las etiquetas. */
  padding-block: 3rem;
}
@media only screen and (min-width: 992px){
  .ka-case-head{ padding-block: 4rem; }
}
.ka-case-head__title{ margin: 0; }
.ka-case-head__claim{
  margin-top: 2.4rem;
  max-width: 56ch;
}
.ka-case-head__tags{
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
  margin-top: 3.2rem;
}

/* ── Ficha de datos, debajo de la portada ──────────────────────────────── */
/* Sin línea encima: cortaba la portada con máscara justo por debajo y partía
   la página en dos. La separación la da el espacio, no un filete — y la
   sección de la portada no tiene relleno inferior, así que el aire tiene que
   salir de aquí o la ficha queda pegada a la imagen. */
.ka-case-facts{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: 3.2rem 4rem;
  margin-top: 6rem;
}
@media only screen and (min-width: 992px){
  .ka-case-facts{ margin-top: 9rem; }
}
.ka-case-facts__name{
  margin-bottom: 1.2rem;
  color: var(--t-medium);
}
.ka-case-facts__value{ margin: 0; }

/* ── Capítulo ──────────────────────────────────────────────────────────── */
.ka-chapter{
  display: grid;
  grid-template-columns: 1fr;
  gap: 3.2rem;
}
.ka-chapter__head{
  display: flex;
  align-items: baseline;
  gap: 1.6rem;
}
.ka-chapter__num{
  font-size: 1.4rem;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--ka-eyebrow);
  flex-shrink: 0;
}
.ka-chapter__kicker{
  display: block;
  margin-bottom: .6rem;
  font-size: 1.3rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--t-muted);
}
/* El título del capítulo vive en una columna de 34rem. Con el tamaño de h3
   de Rayo, una palabra como «Conceptualization» mide más que la columna
   entera y se sale por encima del cuerpo de texto. Se le da un tamaño propio
   que escala con la ventana, y `break-word` como red por si algún día llega
   un término aún más largo. */
.ka-chapter__title{
  margin: 0;
  font-size: clamp(2.2rem, 2vw, 3rem);
  line-height: 1.15;
  overflow-wrap: break-word;
}
.ka-chapter__body > p{ margin-bottom: 1.6rem; }
.ka-chapter__body > p:last-child{ margin-bottom: 0; }

@media only screen and (min-width: 1200px){
  .ka-chapter{
    grid-template-columns: minmax(28rem, 34rem) minmax(0, 1fr);
    column-gap: 8rem;
    row-gap: 4rem;
  }
  .ka-chapter__head{ grid-column: 1; flex-direction: column; align-items: flex-start; gap: 1.2rem; }
  .ka-chapter__body{ grid-column: 2; max-width: 64ch; }
  .ka-chapter__media{ grid-column: 1 / -1; }
  /* Una pieza vertical suelta se queda en la columna del texto, no a todo
     el ancho: así su borde izquierdo coincide con el del párrafo de arriba
     en vez de flotar sola en medio de la fila. */
  .ka-chapter__media--inline{ grid-column: 2; }

  /* Variante a tres columnas: número y título · media estrecha · texto.
     Para capítulos cortos con una pieza vertical, donde ponerla debajo deja
     una franja de hueco a su derecha. La columna del vídeo es deliberadamente
     estrecha: un 9:16 ancho se convierte en una torre altísima.

     Los tres llevan `grid-row: 1` explícito porque en el HTML el texto va
     antes que la media; sin la fila fijada, la colocación automática de grid
     —que solo avanza— mandaría el vídeo a una segunda fila. */
  .ka-chapter--aside{
    grid-template-columns: minmax(18rem, 24rem) minmax(14rem, 20rem) minmax(0, 1fr);
    column-gap: 5rem;
  }
  .ka-chapter--aside .ka-chapter__head { grid-column: 1; grid-row: 1; }
  .ka-chapter--aside .ka-chapter__media{ grid-column: 2; grid-row: 1; }
  .ka-chapter--aside .ka-chapter__body { grid-column: 3; grid-row: 1; max-width: 56ch; }
  .ka-chapter--aside .ka-media--vertical .ka-media__frame{ max-width: none; }

  /* La misma idea para una pieza apaisada: sigue siendo pequeña y al lado del
     texto, pero con más columna, porque un 16:9 en 20rem se queda en nada. */
  .ka-chapter--aside-wide{
    grid-template-columns: minmax(16rem, 20rem) minmax(26rem, 40rem) minmax(0, 1fr);
    column-gap: 5rem;
  }
  .ka-chapter--aside-wide .ka-chapter__head { grid-column: 1; grid-row: 1; }
  .ka-chapter--aside-wide .ka-chapter__media{ grid-column: 2; grid-row: 1; }
  .ka-chapter--aside-wide .ka-chapter__body { grid-column: 3; grid-row: 1; max-width: 56ch; }

  /* ── Una sola retícula para toda la página ──────────────────────────────
     Medido en las cinco páginas de caso, el texto caía en cuatro márgenes
     izquierdos distintos: 60 los títulos de «Product context» y «Main
     challenges», 45 los de los capítulos, 556 el cuerpo de los primeros y
     465, 585 o 745 el de los capítulos según la variante. De ahí el efecto
     de escalones al bajar por la página.

     Los dos primeros bloques usan la rejilla de Bootstrap de Rayo (cinco
     doceavos para el título, seis para el cuerpo, con 15px de canaladura).
     Los capítulos viven fuera de esa rejilla a propósito —con `col-xl-6` las
     filas salían descuadradas— así que aquí se reproduce su geometría a
     mano: mismas fracciones, misma canaladura, mismos bordes.

     Los medios se quedan a todo el ancho, alineados con la imagen de
     cabecera, que es su sitio: son las piezas grandes de la página. */
  .ka-chapter,
  .ka-chapter--aside,
  .ka-chapter--aside-wide{
    grid-template-columns: 5fr 6fr 1fr;
    column-gap: 0;
  }
  .ka-chapter .ka-chapter__head,
  .ka-chapter--aside .ka-chapter__head,
  .ka-chapter--aside-wide .ka-chapter__head{
    grid-column: 1; grid-row: 1;
    padding-left: 1.5rem;
  }
  .ka-chapter .ka-chapter__body,
  .ka-chapter--aside .ka-chapter__body,
  .ka-chapter--aside-wide .ka-chapter__body{
    grid-column: 2; grid-row: 1;
    padding-left: 1.5rem;
    max-width: none;
  }
  .ka-chapter .ka-chapter__media,
  .ka-chapter--aside .ka-chapter__media,
  .ka-chapter--aside-wide .ka-chapter__media{
    grid-column: 1 / -1;
    grid-row: auto;
  }
  /* Excepción: una pieza vertical SUELTA —un short— no va a todo el
     ancho (se convertiría en una torre). Se centra en la fila, con un
     tope de ancho, debajo del texto. */
  .ka-chapter__media--inline,
  .ka-chapter__media:has(.ka-media-grid--1 > .ka-media--vertical){
    grid-column: 1 / -1;
    padding-inline: 1.5rem;
  }
  .ka-chapter__media--inline .ka-media-grid--1,
  .ka-chapter__media:has(.ka-media-grid--1 > .ka-media--vertical) .ka-media-grid--1{
    justify-items: center;
  }
  .ka-chapter__media--inline .ka-media--vertical,
  .ka-chapter__media:has(.ka-media-grid--1 > .ka-media--vertical) .ka-media{
    width: min(100%, 28rem);
  }
  .ka-chapter__media--inline .ka-media__frame,
  .ka-chapter__media:has(.ka-media-grid--1 > .ka-media--vertical) .ka-media__frame{
    max-width: none;
    margin-inline: auto;
  }
  .ka-chapter__media--inline .ka-media figcaption,
  .ka-chapter__media:has(.ka-media-grid--1 > .ka-media--vertical) .ka-media figcaption{
    text-align: center;
    margin-inline: auto;
  }
}

/* ── Marco de imagen y vídeo ───────────────────────────────────────────── */
.ka-media{ margin: 0; }
.ka-media__frame{
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 16px;
  overflow: hidden;
  background: var(--base-tint);
}
.ka-media__frame img,
.ka-media__frame video{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Tableros de diseño y hojas de referencia: recortarlos pierde justo la
   información que se quiere enseñar, así que se muestran enteros. */
.ka-media--contain .ka-media__frame img,
.ka-media--contain .ka-media__frame video{
  object-fit: contain;
  object-position: center;   /* explícito: una apaisada muy alargada tiene que
                                quedar centrada en el marco, no arrimada */
}
.ka-media--wide  .ka-media__frame{ aspect-ratio: 21 / 9; }
.ka-media--square .ka-media__frame{ aspect-ratio: 1 / 1; }
/* Pieza suelta que no debe ir a todo el ancho (p. ej. un tablero de
   wireframes): se centra y se tapa el ancho. */
.ka-media-grid--1 > .ka-media--capped{
  width: min(100%, 90rem);
  max-width: 72%;
  justify-self: center;
}
.ka-media--capped figcaption{
  text-align: center;
  margin-inline: auto;
}
/* Sin marco fijo: la imagen manda su propio alto. Para las panorámicas de
   verdad —la del MX Ink mide 3,15:1— donde un 16:9 obliga a elegir entre
   recortar la mitad de la foto o dejar dos franjas vacías. Solo tiene sentido
   si la pieza va sola en su fila, que es lo que hace `banda` en el generador. */
.ka-media--natural .ka-media__frame{
  aspect-ratio: auto;
  background: none;
}
.ka-media--natural .ka-media__frame img,
.ka-media--natural .ka-media__frame video{
  position: static;
  width: 100%;
  height: auto;
  object-fit: fill;
}
/* Un short de YouTube es vertical: si se mete en el marco 16/9 sale una
   columna de vídeo entre dos franjas negras. Se le da su proporción y se
   centra dentro de la celda. */
.ka-media--vertical .ka-media__frame{
  aspect-ratio: 9 / 16;
  max-width: 36rem;
  margin-inline: auto;
}
/* Foto vertical metida en una fila de 16:9 junto a otras piezas horizontales
   (p. ej. los vídeos de Playformanx): mantiene el marco panorámico para no
   romper la fila, pero mueve el recorte hacia abajo, que es donde está lo
   importante de la imagen (la escena del cine VR con los avatares). */
.ka-crop-focus-low .ka-media__frame img{
  object-position: center 90%;
}
.ka-media__frame iframe{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.ka-media figcaption{
  margin-top: 1.2rem;
  max-width: 60ch;
}

/* ── Rejilla de medios ─────────────────────────────────────────────────── */
.ka-media-grid{
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.4rem;
}
@media only screen and (min-width: 768px){
  .ka-media-grid--2{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ka-media-grid--3{ grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* El contexto y los retos usan ahora los bloques propios de Rayo
   (.mxd-project__block), que ya traen bien las distancias. La clase
   .ka-case-intro se retiró: si aparece en algún HTML, es que quedó viejo. */

/* ── Tarjeta de logo, para los proyectos que solo tienen marca ─────────── */
.ka-logo-card{
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 9;
  padding: 4rem;
  border-radius: 16px;
  background: var(--base-tint);
}
.ka-logo-card img{
  max-width: 60%;
  max-height: 60%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* ---------------------------------------------------------------------------
   Cabecera de página interior
   ---------------------------------------------------------------------------
   Todas las subpáginas arrancan igual: enlace de vuelta arriba a la izquierda
   y, debajo y en vertical, antetítulo → título → subtítulo. Antes el
   antetítulo iba en una columna y el título en otra, así que quedaban en la
   misma línea; eso era lo que rompía la homogeneidad con las de caso.
   --------------------------------------------------------------------------- */
.ka-page-head{
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.ka-page-head .mxd-point-subtitle{ margin-bottom: 2.4rem; }
.ka-page-head .inner-headline__title{ margin: 0; }
.ka-page-head .inner-headline__paragraph{ max-width: 68ch; }

/* ---------------------------------------------------------------------------
   Lo que venía justo después de la cabecera
   ---------------------------------------------------------------------------
   Reducir el relleno de la cabecera no bastaba, porque el hueco no salía solo
   de ahí. Lo que había debajo aportaba lo suyo:

     · en Success stories, `.stack-offset` es un separador VACÍO de 10,6rem
       que Rayo pone antes de la pila de tarjetas;
     · en las legales y en las demás, el primer `.mxd-project__block` trae
       12,3rem de margen superior.

   Sumado a la cabecera daban casi veinte rem de aire. Se recorta solo en la
   sección que va inmediatamente después de una cabecera apretada, así que el
   resto del sitio conserva el ritmo de la plantilla.
   --------------------------------------------------------------------------- */
.ka-headline-tight + .mxd-section .stack-offset{ height: 3rem; }
.ka-headline-tight + .mxd-section.mxd-project .mxd-project__block{ margin-top: 0; }

/* =====================================================
   FOOTER — más compacto, tipografía menor, columnas al ras
   ============================================================
   La «K» gigante aportaba el aire superior; al quitarla quedó
   pegado y, además, Rayo fija las columnas a 650px con el menú
   a 4.4rem — demasiado para KA. Aquí se compacta todo.
   ============================================================ */
.mxd-footer{
  padding-top: calc((5rem + 90px) / 4);
  padding-bottom: 8rem;
}
@media only screen and (min-width: 768px){
  .mxd-footer{
    padding-top: calc((6rem + 90px) / 4);
    padding-bottom: 10rem;
  }
}

.mxd-footer__footer-blocks{
  gap: 1.6rem;
}

.footer-blocks__column{
  gap: 1.6rem;
}
/* En desktop: columnas a la misma altura; las dos cajas del
   centro crecen y se comen el hueco negro entre correo y dirección. */
/* Correo y dirección: misma escala en todos los anchos.
   container + cqi encogen en columnas estrechas (desktop);
   vw cubre el móvil, donde la tarjeta es casi a ancho completo
   y cqi casi no baja. nowrap + <br> = dirección en 2 filas. */
.footer-blocks__column:nth-child(2) .footer-blocks__card{
  container-type: inline-size;
}
.footer-blocks__column:nth-child(2) .mxd-point-subtitle{
  /* flex-start + margen: el cuadrado va con la PRIMERA línea (dirección
     de dos renglones). Rayo trae align-items:center y prefijos -webkit-
     box-align que hay que pisar también. */
  -webkit-box-align: start;
  -ms-flex-align: start;
  align-items: flex-start;
  font-size: clamp(1.15rem, min(6.5cqi, 4.6vw), 2.2rem);
  gap: 0.45em;
  max-width: 100%;
}
.footer-blocks__column:nth-child(2) .mxd-point-subtitle svg{
  /* 0,86em del caja SVG; el rect visible va centrado en el viewBox.
     Interlineado del subtítulo = 1,2 → centrar la caja en la 1ª línea. */
  width: 0.86em;
  height: 0.86em;
  flex-shrink: 0;
  margin-top: calc((1.2em - 0.86em) / 2);
}
.footer-blocks__column:nth-child(2) .mxd-point-subtitle > a,
.footer-blocks__column:nth-child(2) .mxd-point-subtitle > span{
  white-space: nowrap;
}

/* Correo del pie: flecha como los legales, en em para ir con el texto.
   Rayo pone `.mxd-point-subtitle:hover a { color: t-medium }` — hay que
   ganar esa especificidad para que el hover sea verde (texto + cuadrado). */
.ka-footer-mail{
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  color: inherit;
  text-decoration: none;
  transition: color .18s ease;
}
.ka-footer-mail .btn-caption,
.ka-footer-mail i{
  display: inline-flex;
  position: relative;
  color: inherit;
  transition: color .18s ease;
}
.ka-footer-mail i{
  /* Misma caja óptica que el texto (legales: 1,6rem sobre 1,4rem ≈ 1,14em). */
  font-size: 1.05em;
  line-height: 1;
  overflow: hidden;
  /* Oculta en reposo; aparece al hover (el slide-right anima el glifo). */
  max-width: 0;
  opacity: 0;
  transition: max-width .25s ease, opacity .2s ease, color .18s ease;
}
.footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle svg{
  transition: fill .18s ease;
}
/* Hover en la fila del correo: cuadrado, texto y flecha en verde. */
.no-touch .footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle:hover svg{
  fill: var(--ka-link);
}
.no-touch .footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle:hover .ka-footer-mail,
.no-touch .footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle:hover .ka-footer-mail .btn-caption,
.no-touch .footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle:hover .ka-footer-mail i{
  color: var(--ka-link);
}
.no-touch .footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle:hover .ka-footer-mail .btn-caption{
  text-decoration: underline;
  text-decoration-color: var(--ka-link);
  text-underline-offset: 0.25em;
  text-decoration-thickness: 1px;
}
.no-touch .footer-blocks__card:has(.ka-footer-mail) .mxd-point-subtitle:hover .ka-footer-mail i{
  max-width: 1.2em;
  opacity: 1;
}

@media only screen and (min-width: 1200px){
  .mxd-footer__footer-blocks{
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: stretch;
  }
  .footer-blocks__column{
    height: 100%;
    min-height: 0;
  }
  .footer-blocks__column:nth-child(2){
    display: flex;
    flex-direction: column;
    gap: 1.6rem;
  }
  .footer-blocks__column:nth-child(2) .footer-blocks__card{
    flex: 1 1 0;
    min-height: 0;
    justify-content: center;
  }
}

.footer-blocks__card{
  padding: 2.4rem;
  gap: 2rem;
}
@media only screen and (min-width: 768px){
  .footer-blocks__card{
    padding: 3rem;
  }
}

.footer-blocks__block{
  gap: 1.6rem;
}

/* Menú del pie: cuerpo más pequeño (antes 3 / 4.4rem).
   overflow:hidden + altura de una línea: btn-anim duplica el
   texto para el hover; si se ve el overflow, salen dos filas. */
.footer-nav{
  gap: 0.6rem;
}
.footer-nav__link{
  font-size: 2.2rem;
  line-height: 1.15;
}
.footer-nav__link .btn-caption{
  height: 1.15em;
  line-height: 1.15;
  overflow: hidden;
}
@media only screen and (min-width: 768px){
  .footer-nav__link{
    font-size: 2.8rem;
  }
}

/* «Ecosystem» al mismo tamaño que el menú */
.footer-blocks__title-l{
  font-size: 2.2rem;
}
@media only screen and (min-width: 768px){
  .footer-blocks__title-l{
    font-size: 2.8rem;
  }
}

/* Legales: una sola línea, sin repartir el ancho a lo bestia */
.footer-blocks__links{
  gap: 0.8rem 1.6rem;
  flex-wrap: wrap;
}
@media only screen and (min-width: 768px){
  .footer-blocks__links{
    justify-content: flex-start;
  }
}
.footer-blocks__links .btn-line-xsmall,
.footer-blocks__links .btn-line-xsmall .btn-caption{
  white-space: nowrap;
  flex-shrink: 0;
}

/* ============================================================
   MARQUEE — tiles con caption (fondo magenta + texto)
   ============================================================ */
.marquee__item.has-caption{
  background-color: rgba(var(--accent-rgb), 0.5);
}
.marquee__item.has-caption p{
  color: rgba(255, 255, 255, 0.5);
}

/* ============================================================
   MENÚ PRINCIPAL — solo enlaces, panel al 50% en desktop
   ============================================================
   Sin caption ni bloque derecho (foto / promo). En ≥1200px el
   panel ocupa la mitad derecha; en responsive sigue a ancho
   completo como Rayo, pero sin la columna de la foto.
   ============================================================ */
.mxd-menu__right{
  display: none !important;
}
/* Opciones más abajo: separar Home del hamburger / X. */
.mxd-menu__inner{
  padding-top: 13rem;
}
@media only screen and (min-width: 1200px){
  .mxd-menu__contain{
    width: 50%;
    max-width: 50%;
  }
  .mxd-menu__inner{
    padding-top: 0;
  }
  .mxd-menu__left{
    width: 100%;
    padding-top: 12rem;
  }
  /* La franja inferior del menú ya no debe cruzar la pantalla:
     queda dentro del panel del 50%. */
  .mxd-menu__data{
    width: 100%;
  }
}
@media only screen and (min-width: 1600px){
  .mxd-menu__left{
    padding-top: 14rem;
  }
}

/* ---------------------------------------------------------------------------
   Diagrama del proceso — «As easy as 1, 2, 3»
   ---------------------------------------------------------------------------
   Rescatado del sitio anterior (`.process` en reference/old-site). Se conserva
   tal cual estaba —rombo, cuadrado y círculo, borde claro que pasa a color al
   pasar por encima— pero con la tipografía y los tokens de ahora, así que
   funciona igual en modo claro que en oscuro.

   El color del hover es `--ka-link`: verde sobre fondo oscuro y magenta en
   claro, donde el verde daría 1,2:1 y no se vería. Es el mismo criterio que
   siguen los enlaces del resto del sitio.
   --------------------------------------------------------------------------- */
.ka-process__head{
  text-align: center;
  margin-bottom: 9.6rem;
}
@media only screen and (min-width: 768px){
  .ka-process__head{ margin-bottom: 12rem; }
}
.ka-process__head .ka-eyebrow{ margin-bottom: 1.2rem; }
.ka-process__head h2{ margin: 0; }

/* En móvil las tres formas van también en una sola línea, encogidas. Antes
   caían en columna y el diagrama ocupaba tres pantallas para decir algo que
   se entiende de un vistazo si se ve entero.

   El tamaño no es un número fijo sino `min(8.4rem, 21vw)`: la fila mide
   3,414 veces la forma —el rombo girado ocupa √2 de su lado— más los dos
   enlaces, así que atarlo al ancho de la pantalla es lo único que garantiza
   que quepa igual en un iPhone SE que en un Pro Max. */
.ka-process__track{
  /* En móvil la fila ocupa TODO el ancho disponible, no un tamaño fijo.
     La fila mide 3,4142 veces la forma —el rombo girado ocupa √2 de su lado,
     y su margen compensa justo ese desbordamiento— más los dos enlaces. Así
     que despejando: forma = (ancho − 2 enlaces) / 3,4142. Al ir en %, el
     cálculo lo hace el navegador contra el ancho real del contenedor, así que
     el diagrama llena el hueco en cualquier teléfono.

     Va en vw y no en %, aunque el % sería más exacto: un porcentaje en el
     ancho de la forma se mide contra `.ka-process__step`, que es su padre
     directo y se encoge al contenido, no contra la fila. El resultado eran
     formas de 14px. Con 25vw la fila ocupa 356 de los 360px disponibles en
     un móvil normal, que era el objetivo. */
  --ka-union: min(8vw, 9.6rem);
  --ka-shape: min(22vw, 15rem);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 0;
}
@media only screen and (min-width: 768px){
  .ka-process__track{
    --ka-shape: 15rem;
    /* El enlace también se encoge en pantallas intermedias. Con los 9,6rem
       fijos, entre 768 y 991px la fila medía 704px dentro de un hueco de 678
       y se salía por la derecha. */
    --ka-union: min(9.6rem, 8vw);
  }
}

.ka-process__step{ display: flex; }

.ka-process__shape{
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--ka-shape);
  /* Cuadradas por proporción, no por altura declarada: `--ka-shape` es un
     porcentaje en móvil, y un % en `height` se mediría contra el alto del
     contenedor —que es automático— en vez de contra su ancho. */
  aspect-ratio: 1;
  height: auto;
  padding: 0.5rem;
  text-align: center;
  font-family: var(--_font-accent);
  font-weight: var(--fw-medium);
  font-size: min(1.15rem, 2.9vw);
  line-height: 1.25;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--t-bright);
  text-decoration: none;
  border: 2px solid var(--st-medium);
  transition: border-color .2s ease, color .2s ease;
}
@media only screen and (min-width: 768px){
  .ka-process__shape{
    padding: 1.6rem;
    font-size: 1.6rem;
    line-height: 1.3;
    letter-spacing: 0.06em;
  }
}
.ka-process__shape:hover{
  border-color: var(--ka-link);
  color: var(--ka-link);
}
/* El rombo se gira entero y el texto se desgira, para que se lea recto.
   `rotate` no cambia la caja: sigue midiendo 15rem, pero el rombo ocupa
   15·√2 ≈ 21,2rem de ancho visual. Sin este margen, la línea de puntos
   arranca por debajo de la punta del rombo en vez de tocarla.
   15·(√2−1)/2 ≈ 3,1rem a cada lado. */
.ka-process__shape--diamond{
  transform: rotate(45deg);
  margin-inline: calc(var(--ka-shape) * 0.2071);
}
.ka-process__shape--diamond span{ display: block; transform: rotate(-45deg); }
.ka-process__shape--square{ border-radius: 6px; }
.ka-process__shape--circle{ border-radius: 50%; }

/* Unión de puntos con el rombo pequeño en medio */
.ka-process__link{
  position: relative;
  width: var(--ka-union);
  height: 1px;
  flex: 0 0 auto;
  background: repeating-linear-gradient(90deg,
              var(--st-medium) 0 6px, transparent 6px 12px);
}
.ka-process__link::before{
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  background: var(--ka-link);
  transform: translate(-50%, -50%) rotate(45deg);
}
@media only screen and (min-width: 768px){
  .ka-process__link::before{
    width: 10px;
    height: 10px;
  }
}

/* ---------------------------------------------------------------------------
   Separación entre bloques de la home
   ---------------------------------------------------------------------------
   Rayo nombra sus rellenos por lo que venía DESPUÉS —`padding-pre-stack`,
   `padding-grid-pre-mtext`, `padding-mtext-pre-pinned`— con valores que van
   de 5,6 a 18,3rem. En cuanto se reordena la página esos nombres mienten y
   los saltos salen desiguales.

   Aquí hay solo dos medidas:
     · ka-gap-l  separación normal entre dos bloques distintos
     · ka-gap-s  cuando lo de abajo es continuación de lo de arriba
                 (el diagrama y sus tarjetas, el marquee y los logos)
   --------------------------------------------------------------------------- */
.mxd-section.ka-gap-l{ padding-bottom: 13rem; }
.mxd-section.ka-gap-m{ padding-bottom:  9rem; }
.mxd-section.ka-gap-s{ padding-bottom:  4rem; }
@media only screen and (min-width: 1200px){
  .mxd-section.ka-gap-l{ padding-bottom: 18.3rem; }
  .mxd-section.ka-gap-m{ padding-bottom: 11rem; }
  .mxd-section.ka-gap-s{ padding-bottom:  5rem; }
}

/* Formulario de contacto — labels + checkbox (Rayo solo usa placeholders;
   los input genéricos romperían el checkbox si no se excluyen). */
.ka-form-field label{
  display: block;
  margin-bottom: 0.4rem;
  font-family: var(--_font-accent);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--t-muted);
}
.contact-form textarea{
  height: 10rem;
}
@media only screen and (min-width: 768px){
  .contact-form textarea{
    height: 12rem;
  }
}

/* Más aire entre Name/Email, Subject y Message. Rayo separa sus celdas 30px,
   que para una rejilla de tarjetas está bien pero para un formulario deja los
   campos amontonados: cada campo son dos elementos (etiqueta y línea) y sin
   distancia entre grupos cuesta ver dónde acaba uno y empieza el siguiente. */
.ka-contact-form .mxd-grid-item{
  margin-top: 3.6rem;
}
@media only screen and (min-width: 768px){
  .ka-contact-form .mxd-grid-item{ margin-top: 4.8rem; }
}

/* Los textos de ejemplo dentro de los campos, apagados hasta casi fundirse
   con el fondo, y fuera en cuanto el campo recibe el foco. Al salir sin
   escribir nada vuelven solos: no hace falta JavaScript, basta con que la
   regla cuelgue de `:focus`. Y en cuanto hay texto escrito, el navegador ya
   oculta el ejemplo por su cuenta.

   Esto SOLO es aceptable porque cada campo tiene su <label> visible encima.
   Un ejemplo apagado no es información: si desapareciera la etiqueta, el
   formulario dejaría de ser usable para mucha gente.

   El color se mezcla con el fondo en vez de escribirse a mano, así vale
   igual en claro que en oscuro sin declararlo dos veces. */
.contact-form input::placeholder,
.contact-form textarea::placeholder{
  color: var(--t-muted-extra);
  color: color-mix(in srgb, var(--t-muted-extra) 62%, var(--base));
  opacity: 1;                       /* Firefox se la baja al 54% por su cuenta */
  transition: color .18s ease;
}
.contact-form input:focus::placeholder,
.contact-form textarea:focus::placeholder{
  color: transparent;
}
/* Lo que escribe la persona, en blanco puro: así se despega del ejemplo
   apagado y se ve de un vistazo qué está relleno y qué no.

   La lista es larga a propósito. Rayo pinta el texto de los campos desde
   `form input:required:valid` y `form input:focus:required:invalid`, que
   pesan más que un simple `.contact-form input` y ganaban la partida. Hay que
   repetir esos mismos estados con la clase delante para pasarles por encima. */
.ka-contact-form .contact-form input,
.ka-contact-form .contact-form textarea,
.ka-contact-form .contact-form input:required:valid,
.ka-contact-form .contact-form textarea:required:valid,
.ka-contact-form .contact-form input:focus:required:invalid,
.ka-contact-form .contact-form textarea:focus:required:invalid{
  color: #ffffff;
}
/* Enlaces en legales (y bloques de proyecto): mismo verde que
   Privacy Policy del formulario de contacto. */
.mxd-project__paragraph a{
  color: var(--ka-link);
  background: none;
  text-decoration: underline;
  text-decoration-color: var(--ka-link);
  text-underline-offset: 0.15em;
}
.no-touch .mxd-project__paragraph a:hover{
  color: var(--t-bright);
  text-decoration-color: var(--t-bright);
  background-size: unset;
}
.ka-form-check{
  display: flex;
  align-items: flex-start;
  gap: 1.2rem;
  margin: 0.8rem 0 0.4rem;
  cursor: pointer;
  font-family: var(--_font-accent);
  font-size: 1.6rem;
  line-height: 1.5;
  color: var(--t-medium);
}
.ka-form-check a{
  color: var(--ka-link);
  background: none;
  text-decoration: underline;
  text-decoration-color: var(--ka-link);
  text-underline-offset: 0.15em;
}
.no-touch .ka-form-check a:hover{
  color: var(--t-bright);
  text-decoration-color: var(--t-bright);
  background-size: unset;
}
.ka-form-check input[type="checkbox"]{
  flex: 0 0 auto;
  width: 1.8rem;
  height: 1.8rem;
  margin: 0.2rem 0 0;
  padding: 0;
  border: 2px solid var(--st-medium);
  border-radius: 0.3rem;
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  accent-color: var(--accent);
}
/* ---------------------------------------------------------------------------
   Contact: la columna de la izquierda, en caja
   ---------------------------------------------------------------------------
   Mismo tratamiento que las tarjetas comparadas del artículo: superficie
   tintada, radio grande y el mismo relleno. Así el texto deja de flotar
   suelto sobre el fondo frente a un formulario que sí tiene estructura.

   Los interlineados van sueltos y no heredados: la frase grande pide poco
   aire entre líneas para leerse como un bloque, y el párrafo de debajo pide
   bastante más para no apelmazarse. Rayo les daba el mismo a las dos.
   --------------------------------------------------------------------------- */
.ka-contact-intro{
  background: var(--base-tint);
  border-radius: var(--_radius-l);
  padding: 3.2rem 2.8rem;
}
@media only screen and (min-width: 768px){
  .ka-contact-intro{ padding: 4.4rem 4rem; }
}
.ka-contact-intro p.t-large{
  line-height: 1.32;
  margin-bottom: 2.8rem;
}
.ka-contact-intro p:not(.t-large){
  line-height: 1.7;
}

/* «Get in touch» a la misma vertical que Name, Subject y Message.
   El título vive fuera de la rejilla del formulario, así que le faltaba la
   canaladura que Bootstrap le da a cada columna: quedaba 15px a la izquierda
   de los campos.

   No sirve pedirla con `var(--bs-gutter-x)`: el título está dentro de una
   fila con `gx-0`, que pone esa variable a cero, y la hereda. Los 1,5rem van
   escritos porque son los de la rejilla de dentro del formulario, que es
   otra y sí los tiene. */
.ka-form-title{
  padding-left: 1.5rem;
}

/* El texto bajaba un píxel respecto al centro de la casilla: la caja mide
   1,8rem y la primera línea del texto 2,4rem, y sus centros no coincidían. */
.ka-form-check > span{
  position: relative;
  top: -1px;
}

/* Marcado en verde. La palomita va en TINTA OSCURA, no en blanco: blanco
   sobre #39ff88 da 1,35:1 y desaparece. Es la misma regla que ya aplica el
   botón grande de llamada a la acción — sobre verde, tinta oscura; sobre
   magenta, texto blanco. */
.ka-form-check input[type="checkbox"]:checked{
  border-color: #39ff88;
  background-color: #39ff88;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230a0710' stroke-width='2.8'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");
  background-size: 1.1rem 1.1rem;
  background-position: center;
  background-repeat: no-repeat;
}
.ka-form-check input[type="checkbox"]:focus-visible{
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Contact form — contenedor + honeypot */
.ka-contact-form.form-container{
  position: relative;
  min-height: 12rem;
}
/* El aviso ocupa todo el hueco del formulario y va centrado.
   Rayo (.form__reply.centered) usa top/left 50% + translate(-50%,-50%)
   y max-width:400px; si solo tocábamos top:0 el mensaje subía al borde.
   Aquí se anula ese centrado y se rellena el contenedor con flex. */
.ka-contact-form .form__reply{
  inset: 0;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  max-width: none;
  transform: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
}
.ka-contact-form .form__reply.is-visible{
  pointer-events: auto;
}
.ka-contact-form .form__reply .reply__text{
  max-width: 42ch;
}
/* Rayo esconde el formulario solo con `opacity: 0`. Sigue estando ahí:
   se puede tabular hasta sus campos, pulsar Enviar sin verlo y los lectores
   de pantalla lo leen entero por debajo del aviso. `visibility` lo saca de
   en medio sin quitarle el sitio, que es lo que sostiene la altura de la
   caja mientras se ve el mensaje. */
.ka-contact-form .form.is-hidden{
  visibility: hidden;
  pointer-events: none;
}
/* El asterisco de campo obligatorio. `aria-hidden` en el HTML porque el
   `required` del propio campo ya se lo dice a los lectores de pantalla; si no,
   lo cantarían dos veces. */
.ka-req{
  color: var(--ka-link);
  font-weight: 400;
}
.ka-contact-form .form__reply a{
  color: var(--ka-link);
  /* Rayo pinta el subrayado de `p a` con un gradient gris (--t-medium);
     hay que quitarlo o el texto queda verde y la raya gris. */
  background: none;
  text-decoration: underline;
  text-decoration-color: var(--ka-link);
  text-underline-offset: 0.15em;
}
.no-touch .ka-contact-form .form__reply a:hover{
  color: var(--t-bright);
  text-decoration-color: var(--t-bright);
  background-size: unset;
}
.ka-contact-submit{
  display: flex;
  justify-content: flex-end;
}
.ka-hp{
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* El bloque que va justo detrás del hero necesita aire por arriba: el hero no
   trae relleno inferior y «The studio» le quedaba pegado. Es para lo que Rayo
   tiene `padding-hero-01`, que estaba en el bloque que antes iba ahí. */
.mxd-section.ka-top-hero{ padding-top: 9rem; }
@media only screen and (min-width: 1200px){
  .mxd-section.ka-top-hero{ padding-top: 12rem; }
}

/* ---------------------------------------------------------------------------
   Logos de clientes — carrusel del sitio anterior
   ---------------------------------------------------------------------------
   Recuperado tal cual de `.clients` en reference/old-site.

   La pista lleva la lista de logos DOS veces seguidas y se anima de
   translateX(0) a translateX(-50%): eso mueve exactamente un juego completo,
   así que el punto de empalme no se ve. Las máscaras de los bordes hacen que
   entren y salgan fundidos en vez de aparecer de golpe.

   Se para al pasar el ratón por encima; con JS también se puede
   arrastrar (click/touch). Sin motion si el sistema lo pide.
   --------------------------------------------------------------------------- */
.ka-client-marquee{
  /* A SANGRE: el carrusel se sale del contenedor y llega de canto a canto de
     la pantalla, para que el desvanecido ocurra en los bordes reales y no a
     la altura de los márgenes de la web.

     Se hace con márgenes negativos y `width: auto`, NO con `width: 100vw`:
     `vw` incluye el ancho de la barra de desplazamiento, así que en Windows
     —donde la barra ocupa sitio— habría dado unos píxeles de scroll
     horizontal en toda la home. Con márgenes negativos el elemento crece
     hasta el ancho real disponible, sin contar la barra. */
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  overflow: hidden;
  cursor: grab;
  touch-action: pan-y;
  -webkit-user-select: none;
          user-select: none;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.ka-client-marquee.is-dragging{
  cursor: grabbing;
}
.ka-client-track{
  display: flex;
  width: max-content;
  gap: 1.6rem;
  will-change: transform;
  animation: ka-client-scroll 34s linear infinite;
}
.ka-client-marquee:hover .ka-client-track{ animation-play-state: paused; }
/* Con JS: el transform lo mueve el script (auto + drag) */
.ka-client-marquee.is-js .ka-client-track{
  animation: none;
}
.ka-client-card{
  flex: none;
  display: block;
  height: 10rem;
  width: auto;
  border-radius: 16px;
  pointer-events: none; /* el drag lo captura el marquee */
}
@keyframes ka-client-scroll{
  from{ transform: translateX(0); }
  to  { transform: translateX(-50%); }
}
@media only screen and (max-width: 760px){
  .ka-client-card{ height: 7.6rem; }
}
@media (prefers-reduced-motion: reduce){
  .ka-client-track{ animation: none; }
}

/* El antetítulo «Our clients», encima del carrusel de logos. */
.ka-clients{ margin-top: 3.2rem; }

/* ---------------------------------------------------------------------------
   Fotos «About me» — early adopter
   ---------------------------------------------------------------------------
   Tres fotos en fila junto al texto del manifiesto: dispositivos probados
   antes de estar a la venta aquí (Meta Ray-Ban Display, Apple Vision Pro).
   En móvil, la primera ocupa el ancho completo y las otras dos se reparten
   la fila siguiente, para no dejar una imagen minúscula de tres en fila.
   --------------------------------------------------------------------------- */
.ka-about-photos{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.2rem;
  margin: 2rem 0;
}
.ka-about-photos img{
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: 16px;
}
@media only screen and (max-width: 760px){
  .ka-about-photos{ grid-template-columns: 1fr 1fr; }
  .ka-about-photos img:first-child{ grid-column: 1 / -1; aspect-ratio: 16 / 9; }
}

/* ---------------------------------------------------------------------------
   Banda de contacto — «Get in touch with Celia»
   ---------------------------------------------------------------------------
   Rayo la pinta como tarjeta CLARA con la foto pegada a la derecha, y ahí se
   veía el corte: la foto de Celia está sobre fondo negro y el borde entre la
   tarjeta y la imagen quedaba a la vista.

   Ahora la banda entera es un degradado de gris a la izquierda a negro a la
   derecha, y la foto se funde con él por un desvanecido en su borde
   izquierdo. Al ser siempre oscura, el texto y el botón van fijos en claro:
   no pueden depender del modo, porque el fondo ya no cambia.
   --------------------------------------------------------------------------- */
.mxd-promo__inner{
  /* El redondeo lo ponía `anim-zoom-out-container` al terminar su recorrido
     —animaba el border-radius de 200px hasta --_radius-l—, así que al quitar
     esa animación la caja se quedó con las esquinas rectas. Ahora es fijo.
     El borde del ::after lo hereda, así que sigue el contorno. */
  border-radius: var(--_radius-l);
  background-color: transparent;
  /* Termina en NEGRO PURO, no en #0a0710. La foto de Celia está recortada
     sobre negro absoluto, y esa diferencia de dos tonos era justo la costura
     vertical que se veía entre la mitad izquierda y la derecha. */
  background-image: linear-gradient(90deg, #120c1e 0%, #000000 60%, #000000 100%);
}

/* El borde va en un pseudoelemento por encima de todo —foto incluida—, no en
   el fondo. Antes era una sombra interior del contenedor y la imagen, que se
   sale 4rem por arriba, se lo comía en su mitad. */
.mxd-promo__inner::after{
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  /* Un pelo, no un marco: 1px al 10% de blanco. Lo justo para despegar la
     caja del fondo de la página sin que se vea el borde. */
  box-shadow: inset 0 0 0 1px rgba(244, 245, 246, 0.10);
}

/* La foto: más pequeña, dentro de la caja, y con un desvanecido MUY corto en
   su canto izquierdo. Los 12% del fundido caen sobre el fondo negro de la
   propia foto, no sobre las manos, así que no se pierde nada de la imagen y
   la unión con el degradado desaparece. */
.mxd-promo__bg{
  top: 0;
  height: 100%;
  width: 46%;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 12%, #000 100%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 12%, #000 100%);
}
@media only screen and (min-width: 1200px){
  .mxd-promo__bg{ width: 38%; }
}

/* El titular, al tamaño de un h3 (3,6rem, y 5rem a partir de 768px). Rayo lo
   trata como titular de portada y lo llevaba a 4,4 / 8 / 10rem, que en una
   caja ya compacta se comía la mitad del bloque. Pasó por el tamaño de h2
   —el de «Recent articles»— y seguía siendo demasiado: la banda es un
   remate, no una portada. h3 es el escalón que ya usamos en las tarjetas.
   El salto de línea va en el HTML: «Get in touch / with Celia». */
.mxd-promo__caption{ font-size: 3.6rem; }
@media only screen and (min-width: 768px){
  .mxd-promo__caption{ font-size: 5rem; }
}
@media only screen and (min-width: 1400px){
  .mxd-promo__caption{ font-size: 5rem; }
}

/* El módulo era enorme: Rayo le da hasta 20rem de margen al contenido. */
.mxd-promo__content{ margin: 7rem 0 8rem 0; }
@media only screen and (min-width: 1200px){
  .mxd-promo__content{ margin: 9rem 0 10rem 0; }
}
/* El hueco es el mismo entre los tres hijos, así que se aprieta el general
   —titular con el cuerpo— y se le devuelve al botón lo que pierde, para que
   quede a la distancia de antes. */
.mxd-promo__content{ gap: 1.6rem; }
.mxd-promo__controls{ margin-top: 1.6rem; }

/* Texto y botón siempre en claro. 18.31:1 sobre el extremo negro y 9.6:1
   sobre el gris del arranque, así que se lee en toda la banda. */
.mxd-promo__title,
.mxd-promo__caption,
.mxd-promo__caption .char,
.mxd-promo__caption .word,
.ka-promo__text{ color: #f4f5f6; }
.ka-promo__text{
  /* Dos topes, y manda el menor. Los 46ch son la medida cómoda de lectura y
     valen de sobra en escritorio. El 48% es el que salva los anchos
     intermedios: ahí la caja de 46ch mide 455px, la foto empieza en el 392 y
     el texto se metía 23px por debajo de ella. Medido a 816px de ventana. */
  max-width: min(46ch, 48%);
  margin-top: 2.4rem;
}
.ka-promo__text p + p{ margin-top: 1.2rem; }

/* ---------------------------------------------------------------------------
   La banda de contacto en móvil: apilada
   ---------------------------------------------------------------------------
   En escritorio el texto va a la izquierda y la foto a la derecha. En un
   teléfono no hay dos columnas, así que se apila: titular, texto, foto y
   botón, en ese orden.

   La foto es HERMANA de `.mxd-promo__content`, no hija, así que por orden de
   documento siempre quedaría antes o después del bloque entero — nunca entre
   el texto y el botón. `display: contents` disuelve ese contenedor: sus tres
   hijos pasan a ser hermanos de la foto y ya se pueden ordenar los cuatro.

   El degradado gira 90°: claro arriba, negro abajo. Y la foto se funde por
   sus dos cantos horizontales, no por el izquierdo, para que el corte de
   abajo no se vea justo encima del botón, que va pegado a ella.

   HASTA 767px, NO HASTA 991. Esto valía para un teléfono y era un desastre
   en tableta. La foto de Celia es vertical (1200×1800) y se muestra con
   `contain` en una caja de 34rem de alto, así que sale siempre a 227px de
   ancho pase lo que pase: en una pantalla de 390 ocupa el 58% de la banda y
   se ve bien, pero en una de 916 ocupa el 27% y queda una figura diminuta
   flotando en un socavón negro de 790px de alto. Medido.

   Desde 768 ya caben dos columnas, así que se deja actuar a la maqueta de
   escritorio —texto a la izquierda, foto a la derecha al 46%—, que es
   proporcional y se adapta sola. La banda pasa de 790px de alto a 472.
   --------------------------------------------------------------------------- */
@media only screen and (max-width: 767px){
  .mxd-promo__inner{
    padding-left: 2.4rem;
    padding-right: 2.4rem;
    /* Llega a NEGRO PURO en el 45%, justo antes de donde arranca la foto.
       La foto de Celia está recortada sobre negro absoluto: si en ese punto
       el fondo todavía es morado, se ve una costura horizontal por mucho que
       la imagen se desvanezca por arriba. Es el mismo problema que en
       escritorio, pero en el otro eje. */
    background-image: linear-gradient(180deg,
      #2a1c3d 0%, #17102a 26%, #000000 45%, #000000 100%);
  }
  .mxd-promo__content{ display: contents; }
  .mxd-promo__title{ order: 1; margin-top: 5.6rem; }
  .ka-promo__text{ order: 2; margin-top: 2rem; }
  .mxd-promo__bg{
    order: 3;
    position: relative;
    top: auto;
    right: auto;
    width: auto;
    height: 34rem;
    /* A sangre: se come el padding lateral y llega de canto a canto. */
    margin: 3.2rem -2.4rem 0 -2.4rem;
    /* CONTAIN, no cover.
       Con `cover` cada foto se recortaba de una forma distinta según dónde
       cayera Celia en el encuadre: en unas páginas salía la cabeza flotando
       en un hueco negro y en otras se le comía las manos. `contain` enseña la
       foto ENTERA siempre, sea cual sea, y no deja bandas visibles a los
       lados porque las cinco fotos están recortadas sobre negro absoluto y el
       degradado de la banda ya ha llegado a negro a esa altura.
       Anclada abajo, para que quede pegada al botón. */
    background-size: contain;
    background-position: center bottom;
    /* El desvanecido se queda solo arriba, donde la foto entra en el
       degradado. Abajo ya no hace falta: el canto inferior de la imagen es
       negro sobre negro y no se ve. */
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 100%);
            mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 100%);
  }
  .mxd-promo__controls{
    order: 4;
    margin-top: 0;
    margin-bottom: 5.6rem;
  }
}

.ka-promo__cta,
.ka-promo__cta:hover,
.ka-promo__cta .btn-caption,
.ka-promo__cta i{
  color: #f4f5f6;
  border-color: #f4f5f6;
}

/* ---------------------------------------------------------------------------
   Tarjetas de servicio en móvil: darle medida a la imagen
   ---------------------------------------------------------------------------
   Rayo NO le pone ninguna medida a `.service-img` por debajo de 992px: daba
   por hecho que la versión pequeña (`service-img-s`) sería un archivo ya
   diminuto. Las nuestras no lo son —1000×810, 873×788 y un vídeo de 1280×720—
   así que se dibujaban a tamaño real dentro de una tarjeta de unos 315px de
   ancho. Su contenedor crecía hasta 810px de alto dentro de una tarjeta de
   600px con `overflow:hidden` y `justify-content:space-between`, y empujaba
   fuera el título y el texto. El efecto en el teléfono era que las tarjetas
   parecían haber desaparecido y solo se veía la última, Solution, con su
   vídeo gigante tapando a las otras dos.

   Aquí la imagen pasa a ser una franja de alto fijo apoyada en el borde
   inferior de la tarjeta, que es donde Rayo la quiere (de ahí su
   `padding-bottom: 0`).

   Y, medido en un teléfono de 390px, el apilado NO CABE. La tarjeta morada
   («In-depth analysis») necesita 663px de alto para su contenido y en una
   pantalla de esas solo hay 570 disponibles: el efecto obliga a que las tres
   tarjetas midan lo mismo y quepan de una vez en la ventana, y con este texto
   eso es imposible sin recortarlo. No es cuestión de ajustar números.

   Así que por debajo de 992px el apilado se desactiva —también en app.js, que
   es quien ancla el bloque— y las tres tarjetas quedan como una lista vertical
   normal, cada una con el alto que su contenido pida. En escritorio el efecto
   sigue igual. */
@media only screen and (max-width: 991px){
  .services-stack{
    height: auto;
    min-height: 0;
  }
  /* Ojo al selector: `.services-stack .stack-item`, NO `.stack-item` a secas.
     Escrito sin acotar, esta regla también desapilaba las tarjetas de Success
     stories —que comparten clase— mientras su contenedor `.projects-stack`
     seguía midiendo 600px de alto. Las cinco tarjetas se salían por abajo y
     la banda de «Get in touch with Celia» aparecía montada encima de la
     última. */
  .services-stack .stack-item{
    position: relative;
    height: auto;
    padding-bottom: 2.4rem;
  }
  .mxd-services-stack__inner{
    height: auto;
  }
  /* El hueco y el tirón hacia arriba eran para el bloque anclado; sin él
     sobran los dos y dejaban un vacío en medio de la página. */
  .stack-wrapper.in-content-stack{
    margin-top: 0;
  }
  .stack-wrapper.in-content-stack .stack-offset{
    height: 0;
  }
  .mxd-services-stack__inner .services-stack__image{
    -ms-flex: 0 0 auto;
        flex: 0 0 auto;
    height: 22rem;
    margin-top: 2.8rem;
    -moz-border-radius: var(--_radius-m) var(--_radius-m) 0 0;
         border-radius: var(--_radius-m) var(--_radius-m) 0 0;
  }
  .services-stack__image .service-img{
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    object-fit: cover;
    object-position: center top;
  }
  /* «In-depth analysis»: no es una fotografía, es un recorte del casco con
     fondo transparente. Recortarlo por los bordes le quita las correas, que
     es justo lo que se reconoce. `contain` lo deja entero dentro de la franja. */
  .services-stack__image .service-img.ka-media-contain{
    object-fit: contain;
    object-position: center;
  }
  /* «Solution»: el vídeo es 16:9 y la franja era casi cuadrada, así que
     `cover` se llevaba por delante el 40% de los lados y solo quedaba un
     fragmento sin contexto. Dándole a la franja la proporción del vídeo no
     hay nada que recortar y se ve el plano completo. */
  .services-stack__image.ka-media-16-9{
    height: auto;
    aspect-ratio: 16 / 9;
  }
  .services-stack__image.ka-media-16-9 .service-img{
    object-position: center;
  }
}

/* ---------------------------------------------------------------------------
   Tarjetas de servicio: la imagen se recorta por abajo, nunca por arriba
   ---------------------------------------------------------------------------
   Rayo la coloca con `bottom: -5%` y `height: 110%`, así que recortaba un 5%
   por arriba y otro 5% por abajo. Anclándola arriba, el 10% que sobra se va
   entero por la parte inferior y ninguna de las tres queda descabezada.
   --------------------------------------------------------------------------- */
@media only screen and (min-width: 992px){
  .services-stack__image .service-img{
    top: 24px;         /* el aire mínimo por arriba que pidió Celia */
    bottom: auto;
    right: 5rem;       /* el mismo margen que la tarjeta tiene a la izquierda */
    height: auto;      /* Rayo la estiraba al 110% de la tarjeta */
    max-height: calc(100% - 24px);
    max-width: 42%;
  }
  /* Free review: más grande y apoyada en el borde inferior de la tarjeta. */
  .services-stack__image .service-img.ka-img-bottom{
    top: auto;
    bottom: 0;
    max-height: calc(100% - 24px);
    max-width: 54%;
  }
  /* El vídeo llena la tarjeta ENTERA: se ancla a los cuatro lados y se
     recorta por donde haga falta. Con `object-fit: cover` manda el ancho, así
     que lo que sobre se va por abajo, que es lo que pidió Celia. */
  .services-stack__image video.service-img{
    position: absolute;
    inset: 0;
    top: 0; right: 0; bottom: 0; left: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    object-fit: cover;
    object-position: center top;
  }
}

/* ---------------------------------------------------------------------------
   Tarjetas de blog: zoom al pasar por encima, y cursor propio
   ---------------------------------------------------------------------------
   El velo con el icono del ojo que traía Rayo está fuera del HTML. En su
   lugar la foto hace un zoom corto y el puntero se convierte en un círculo
   verde que dice «Read article» (js/ka-cursor.js).

   El zoom usa la propiedad `scale`, NO `transform`. Es a propósito: la imagen
   la mueve Ukiyo con un `transform` en línea para el efecto parallax, y un
   `transform: scale()` aquí se lo comería. `scale` es una propiedad aparte y
   los dos conviven.
   --------------------------------------------------------------------------- */
.mxd-blog-preview__image{
  scale: 1;
  transition: scale .6s cubic-bezier(.22,.61,.36,1);
}
.mxd-blog-preview__media:hover .mxd-blog-preview__image{ scale: 1.06; }

@media (prefers-reduced-motion: reduce){
  .mxd-blog-preview__image{ transition: none; }
  .mxd-blog-preview__media:hover .mxd-blog-preview__image{ scale: 1; }
}

/* Cristal esmerilado, no disco verde.
   ---------------------------------------------------------------------------
   El tinte oscuro NO es decorativo: sin él las letras blancas desaparecen
   sobre las fotos claras. Se midió el parche de 11rem más luminoso de las
   cinco fotos de proyecto y el peor es el key art de PaintingVR (el logotipo
   amarillo), con luminancia 0,76. Contraste del blanco encima según el tinte:

       45% → 3,96:1   no llega
       50% → 4,62:1   justo
       55% → 5,41:1   ← el elegido, con margen
       60% → 6,38:1

   Si algún día entra una foto todavía más clara hay que volver a medir.
   El desenfoque es un extra: donde `backdrop-filter` no exista, el tinte solo
   ya garantiza la lectura. */
.ka-cursor{
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 11rem;
  height: 11rem;
  border-radius: 50%;
  background: rgba(10, 7, 16, .55);
  border: 1px solid rgba(255, 255, 255, .85);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
          backdrop-filter: blur(14px) saturate(1.1);
  color: #fff;
  font-family: var(--_font-accent);
  font-size: 1.4rem;
  font-weight: var(--fw-medium);
  text-align: center;
  pointer-events: none;        /* que nunca se coma un clic */
  opacity: 0;
  /* Solo fade: el scale + la posición a (0,0) hasta el primer paint
     hacía que el círculo «volara» desde una esquina. */
  transition: opacity .15s ease;
  will-change: transform, opacity;
}
.ka-cursor.is-visible{
  opacity: 1;
}
/* El puntero nativo se esconde solo mientras se está sobre la tarjeta. */
body.ka-cursor-activo .mxd-blog-preview__media,
body.ka-cursor-activo .mxd-blog-preview__media *,
body.ka-cursor-activo .mxd-project-item__preview,
body.ka-cursor-activo .mxd-project-item__preview *,
body.ka-cursor-activo .mxd-projects-stack__inner,
body.ka-cursor-activo .mxd-projects-stack__inner *{ cursor: none; }

/* Las tres tarjetas de Success stories de la home: mismo zoom que
   los artículos (`scale`), no `background-size`. Pasar de `cover` a
   `112%` no se interpola y el navegador saltaba a enseñar casi toda
   la foto. El recorte lo hace el padre (overflow + radio). */
.mxd-project-item__media{
  overflow: hidden;
  border-radius: var(--_radius-l);
}
.mxd-project-item__preview{
  background-size: cover;
  scale: 1;
  transition: scale .5s var(--_animbezier);
}
.no-touch .mxd-project-item:hover .mxd-project-item__preview,
.no-touch .mxd-project-item__preview:hover{
  scale: 1.06;
}
@media (prefers-reduced-motion: reduce){
  .mxd-project-item__preview{ transition: none; }
  .no-touch .mxd-project-item:hover .mxd-project-item__preview{ scale: 1; }
}


/* ---------------------------------------------------------------------------
   Tarjetas de proyecto: los tags aparecen al pasar por encima
   ---------------------------------------------------------------------------
   En reposo se ve la foto limpia; al pasar el cursor entran los tags sobre un
   degradado oscuro que los hace legibles. Vale para las dos páginas: las tres
   tarjetas de la home (.mxd-project-item) y las cinco de All success stories
   (.mxd-projects-stack).

   TODO ESTO CUELGA DE `.no-touch`, Y NO ES UN DETALLE. En un teléfono no hay
   ratón que pasar por encima, así que si los tags se escondieran ahí también
   no habría forma humana de verlos: la información desaparecería del sitio
   para la mitad de las visitas. Con la clase delante, el móvil y la tableta
   se quedan como estaban, con los tags siempre a la vista.

   El degradado va en un `::after` propio y no en el elemento de la foto,
   porque la foto hace un zoom con `scale` al pasar por encima y arrastraría
   el degradado con ella, estirándolo.
   --------------------------------------------------------------------------- */

/* -- home ------------------------------------------------------------------ */
.no-touch .mxd-project-item__media::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to top,
              rgba(4, 2, 8, .72) 0%,
              rgba(4, 2, 8, .34) 32%,
              rgba(4, 2, 8, 0)   62%);
  opacity: 0;
  transition: opacity .45s var(--_animbezier);
}
.no-touch .mxd-project-item:hover .mxd-project-item__media::after{ opacity: 1; }

.no-touch .mxd-project-item__tags{
  z-index: 2;                  /* por encima del degradado */
  opacity: 0;
  /* Suben un poco al entrar. 8px es suficiente para que se note el gesto sin
     que parezca que la tarjeta se descuadra. */
  transform: translateY(8px);
  transition: opacity .35s var(--_animbezier), transform .35s var(--_animbezier);
}
.no-touch .mxd-project-item:hover .mxd-project-item__tags{
  opacity: 1;
  transform: translateY(0);
}

/* -- All success stories --------------------------------------------------- */
.no-touch .mxd-projects-stack__inner::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to top,
              rgba(4, 2, 8, .72) 0%,
              rgba(4, 2, 8, .34) 32%,
              rgba(4, 2, 8, 0)   62%);
  opacity: 0;
  transition: opacity .45s var(--_animbezier);
}
.no-touch .mxd-projects-stack__inner:hover::after{ opacity: 1; }

/* El título ya estaba en `position: relative`; le falta el z-index para no
   quedarse por debajo del degradado que acabamos de meter. */
.no-touch .mxd-projects-stack__title{ z-index: 2; }

.no-touch .mxd-projects-stack__tags{
  z-index: 2;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .35s var(--_animbezier), transform .35s var(--_animbezier);
}
.no-touch .mxd-projects-stack__inner:hover .mxd-projects-stack__tags{
  opacity: 1;
  transform: translateY(0);
}

/* Con movimiento reducido se sigue viendo todo: solo se quita el recorrido. */
@media (prefers-reduced-motion: reduce){
  .no-touch .mxd-project-item__media::after,
  .no-touch .mxd-project-item__tags,
  .no-touch .mxd-projects-stack__inner::after,
  .no-touch .mxd-projects-stack__tags{ transition: none; transform: none; }
}


/* ---------------------------------------------------------------------------
   All success stories: los tags, abajo a la derecha
   ---------------------------------------------------------------------------
   Rayo apila la tarjeta en columna con `space-between`, lo que deja los tags
   arriba y el título abajo. Celia los quiere juntos en la fila de abajo, el
   título a la izquierda y los tags a la derecha.

   Se hace con una rejilla de dos columnas en vez de tocar el orden del HTML,
   porque el marcado lo escupe `build.py` desde `_pages/` y conviene que el
   orden de lectura siga siendo tags → título (que es como lo anuncia el
   lector de pantalla y como se apila en el teléfono).

   La foto no entra en la rejilla: está en `position: absolute`, o sea fuera
   del flujo, y sigue tapando la tarjeta entera igual que antes.
   --------------------------------------------------------------------------- */
/* Desde 1024 y no desde 768, medido. Entre esos dos anchos los tags se llevan
   su ancho natural (unos 330px) y al título le quedaban 185: «ENGAGE XR» se
   salía de su columna y «Mentorship, Talks & Community» caía en cuatro líneas.
   Por debajo de 1024 se conserva la pila de Rayo, con los tags arriba. */
@media (min-width: 1024px){
  .mxd-projects-stack__inner.justify-between{
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: 1fr auto;
    align-items: end;
    /* `column-gap` generoso: si un título largo y los tags se tocaran, la
       tarjeta se leería como un solo bloque de texto. */
    column-gap: 4rem;
  }
  .mxd-projects-stack__inner.justify-between > .mxd-projects-stack__title{
    grid-column: 1;
    grid-row: 2;
  }
  .mxd-projects-stack__inner.justify-between > .mxd-projects-stack__tags{
    grid-column: 2;
    grid-row: 2;
    justify-content: flex-end;
    /* `justify-self: end` y NO `max-width`. Con un `max-width: 40%` puesto
       aquí el porcentaje se resuelve contra la columna, que a su vez se está
       midiendo por el contenido: la caja se quedaba en 129px, los tres tags
       caían uno debajo de otro y el bloque se pegaba a la izquierda de su
       propia columna, a 244px del borde. Medido en el navegador. */
    justify-self: end;
    /* Los tags se alinean por su última línea con el subtítulo. Si el título
       envuelve a tres líneas, siguen abajo y no flotando a media altura. */
    align-self: end;
  }
}


/* ---------------------------------------------------------------------------
   La pila de Success stories tiene que caber en la ventana
   ---------------------------------------------------------------------------
   El bloque se ancla con `start: "top top"`, así que lo que no quepa de alto
   se queda fuera por abajo y no hay forma de verlo: no se puede desplazar
   dentro de un elemento anclado.

   Rayo da tres alturas fijas (75vh de base, 600px desde 992 y 760px desde
   1600) más un `min-height: 600px`. Todas ignoran lo alta que sea la ventana,
   así que en una de 600px —un portátil pequeño con el navegador a media
   pantalla, o un móvil apaisado— el bloque pedía 630px contando el separador
   y los últimos 30px se quedaban fuera para siempre.

   Se conservan las tres alturas de Rayo, pero con un techo: nunca más de la
   ventana menos 4rem, que es lo que ocupa el separador con algo de aire. En
   pantallas normales no cambia nada; en las bajas cede.

   El techo va en `height` y no en `max-height` a propósito: las tarjetas son
   `height: 100%` de este contenedor, y un porcentaje se resuelve contra la
   altura declarada, no contra la recortada por `max-height`. Con `max-height`
   el contenedor encogería y las tarjetas seguirían midiendo 600px, saliéndose
   por abajo. */
.projects-stack{
  height: min(75vh, calc(100vh - 4rem));
  min-height: 0;
}
@media only screen and (min-width: 992px){
  .projects-stack{ height: min(600px, calc(100vh - 4rem)); }
}
@media only screen and (min-width: 1600px){
  .projects-stack{ height: min(760px, calc(100vh - 4rem)); }
}


/* En Contact sobra el botón «Contact us» de la barra superior: ya estás ahí.
   Se oculta con `visibility`, no con `display: none`. Quitándolo del flujo,
   la barra se recolocaba y el logotipo daba un salto al cambiar de página
   respecto al resto del sitio. Así conserva su hueco exacto y nada se mueve.
   `visibility: hidden` además lo saca del recorrido del tabulador, así que
   tampoco queda un botón invisible al que llegar con el teclado. */
body[data-page="contact.html"] .mxd-header__controls .btn{
  visibility: hidden;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce){
  .ka-cursor{ transition: none; }
}

/* ---------------------------------------------------------------------------
   Barra de desplazamiento
   ---------------------------------------------------------------------------
   Discreta y con forma de píldora, como la de macOS: ancha pero corta, con
   aire alrededor, y se desvanece cuando se deja de mover. Vale para la página
   y para el panel de cookies, que tiene su propio desplazamiento.

   El truco del `border: 4px solid transparent` con `background-clip:
   content-box` es lo que da ese aspecto flotante: el canal mide 14px pero el
   pulgar solo pinta los 6 de dentro, así que no toca el borde de la ventana.

   El azul-pizarra está elegido para verse sobre el fondo casi negro sin
   competir con el magenta ni con el verde de la marca. `js/ka-scrollbar.js`
   pone y quita la clase `ka-scrolling` en <html>.
   --------------------------------------------------------------------------- */
::-webkit-scrollbar{
  width: 14px;
  height: 14px;
}
::-webkit-scrollbar-track{
  background: transparent;
}
::-webkit-scrollbar-thumb{
  background-color: transparent;
  background-clip: content-box;
  border: 4px solid transparent;
  border-radius: 999px;
  transition: background-color .35s ease;
}
html.ka-scrolling ::-webkit-scrollbar-thumb{
  background-color: rgba(122, 132, 178, 0.55);
}
html.ka-scrolling ::-webkit-scrollbar-thumb:hover{
  background-color: rgba(150, 160, 205, 0.8);
}
::-webkit-scrollbar-corner{ background: transparent; }

/* Firefox no tiene pseudoelementos de barra: se queda con una versión fina y
   fija, del mismo color. No se desvanece, pero tampoco molesta. */
html{
  scrollbar-width: thin;
  scrollbar-color: rgba(122, 132, 178, 0.45) transparent;
}
.cookie-modal__panel{
  scrollbar-width: thin;
  scrollbar-color: rgba(122, 132, 178, 0.45) transparent;
}

/* ---------------------------------------------------------------------------
   What is KA — dos escenas ilustradas
   ---------------------------------------------------------------------------
   Los tres SVG (jeroglíficos de fondo, glifo del Ka y kanji 化) son siluetas
   de un solo color. En vez de meterlos con <img> —que obligaría a fijar el
   color dentro del archivo— se usan como MÁSCARA sobre un div: el color lo
   pone el CSS y sale de la paleta de KA, así que cambia con el modo.

   El hilo verde de puntos con el círculo viene de la referencia que pasó
   Celia: cruza la escena a la altura del glifo y lo señala.
   --------------------------------------------------------------------------- */
/* Las dos escenas pesan lo mismo en el relato, así que van a la par. */
.ka-escenas__titulo{ margin-bottom: 2.4rem; }

.ka-escenas{
  display: grid;
  grid-template-columns: 1fr;
  gap: 3.2rem;
}
@media only screen and (min-width: 992px){
  .ka-escenas{ grid-template-columns: 1fr 1fr; }
}

.ka-escena{
  position: relative;
  overflow: hidden;
  border-radius: var(--_radius-l);
  background: var(--base-tint);
  padding: 5rem 2.4rem 4rem;
  display: flex;
  flex-direction: column;
}
@media only screen and (min-width: 768px){
  .ka-escena{ padding: 6rem 4rem 5rem; }
}

/* Fondo: los jeroglíficos, muy tenues, como textura */
/* Los dos fondos son patrones GENERADOS (ver los SVG), no ilustraciones.
   Cada cultura con su gramática, que es lo que los hace reconocibles:

     Japón  → seigaiha, la retícula de olas concéntricas.
     Egipto → cenefa de bandas apiladas: cuentas, galones, cadena de óvalos
              y una fila de lotos. El friso egipcio se organiza en franjas,
              no en retícula; probar con escamas no sonaba a Egipto.

   Lo que sí comparten: color, grosor de línea y densidad. Van muy tenues,
   son textura y no deben competir con el glifo.

   `jeroglificos.svg` y `plumas-rishi.svg` siguen en la carpeta, sin usar. */
.ka-escena__fondo{
  position: absolute;
  inset: 0;
  background-color: rgba(198, 120, 255, 1);
  -webkit-mask-repeat: repeat;          mask-repeat: repeat;
  -webkit-mask-position: center;        mask-position: center;
}
.ka-escena--egipto .ka-escena__fondo{
  opacity: .05;
  -webkit-mask-size: 42rem auto;        mask-size: 42rem auto;
  -webkit-mask-image: url('../img/what-is-ka/cenefa-egipcia.svg');
          mask-image: url('../img/what-is-ka/cenefa-egipcia.svg');
}
/* Japón: seigaiha, el patrón de olas. Generado como SVG (ver el archivo) y
   repetido en mosaico, del mismo modo que los jeroglíficos en la otra. */
.ka-escena--japon .ka-escena__fondo{
  opacity: .045;
  -webkit-mask-size: 46rem auto;        mask-size: 46rem auto;
  -webkit-mask-image: url('../img/what-is-ka/olas-seigaiha.svg');
          mask-image: url('../img/what-is-ka/olas-seigaiha.svg');
}
/* Y un halo por debajo del patrón, para que el kanji no flote en plano. */
.ka-escena--japon::before{
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(58% 58% at 50% 42%, rgba(176, 38, 255, .09), transparent 72%);
}

/* La figura principal */
.ka-escena__figura{
  position: relative;
  z-index: 1;
  height: 24rem;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-position: center;   mask-position: center;
}
@media only screen and (min-width: 768px){
  .ka-escena__figura{ height: 32rem; }
}
/* El glifo egipcio, un tono por encima del fondo — como en la referencia,
   donde la silueta se lee sin gritar. */
.ka-escena--egipto .ka-escena__figura{
  background-color: rgba(198, 120, 255, .92);   /* por delante del patrón */
  filter: drop-shadow(0 0 4rem rgba(10, 7, 16, .9));
  -webkit-mask-image: url('../img/what-is-ka/ka-glifo-egipcio.svg');
          mask-image: url('../img/what-is-ka/ka-glifo-egipcio.svg');
}
/* El kanji, del mismo color que el glifo egipcio: son la misma idea contada
   dos veces, y tienen que leerse como pareja. */
.ka-escena--japon .ka-escena__figura{
  background-color: rgba(198, 120, 255, .92);
  filter: drop-shadow(0 0 4rem rgba(10, 7, 16, .9));
  -webkit-mask-image: url('../img/what-is-ka/kanji-ka.svg');
          mask-image: url('../img/what-is-ka/kanji-ka.svg');
}

/* Pie de la escena */
.ka-escena__pie{
  position: relative;
  z-index: 1;
  margin-top: auto;      /* el texto se apoya abajo, así las dos cajas casan */
  padding-top: 4rem;
  text-align: center;
}
.ka-escena__pie .ka-eyebrow{ margin-bottom: 1.6rem; }
.ka-escena__texto{
  max-width: 62ch;
  margin: 0 auto;
  font-size: 1.8rem;
  line-height: 1.5;
  color: var(--t-medium);
}
.ka-escena__texto strong{
  color: var(--ka-link);
  font-weight: var(--fw-semibold);
}

/* Remate */
.ka-escena__cierre{
  margin-top: 6rem;
  text-align: center;
}
.ka-escena__cierre .ka-eyebrow{ margin-bottom: 1.6rem; }
.ka-escena__cierre-texto{
  max-width: 64ch;
  margin: 0 auto;
  font-size: 2.4rem;
  line-height: 1.4;
  color: var(--t-bright);
}
.ka-escena__cierre-texto strong{
  color: var(--ka-link);
  font-weight: var(--fw-semibold);
}

/* ---------------------------------------------------------------------------
   Titular de About me, en una sola frase
   ---------------------------------------------------------------------------
   «If it needs a boring manual,» y «I haven't finished designing it.» son
   una única frase partida en dos líneas por diseño. Iba en dos pesos (la
   primera línea pequeña y muted, a modo de planteamiento), pero se ve
   igual de grande que el resto de titulares del sitio: aquí hereda tamaño,
   peso y color del h1, y solo aporta el salto de línea entre las dos.
   Se marca con `.ka-titular__entrada` en vez de partir el h1 en dos
   elementos para que siga siendo una sola frase de cara a un buscador o
   un lector de pantalla.
   --------------------------------------------------------------------------- */
.ka-titular__entrada{
  display: block;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
}

/* ---------------------------------------------------------------------------
   La foto de la banda de contacto, distinta en cada página
   ---------------------------------------------------------------------------
   La banda es un partial compartido, así que el HTML es idéntico en las trece
   páginas. Lo que cambia es la foto, y se decide aquí a partir del atributo
   `data-page` que build.py pone en el <body>. La home mantiene la de siempre;
   el resto rotan entre las otras tres de «Celia photos».
   --------------------------------------------------------------------------- */
body[data-page="404.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-controllers-front.jpg'); }
body[data-page="blog-article.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-on.jpg'); }
body[data-page="blog-standard.html"] .mxd-promo__bg{ background-image: url('../img/celia-tools-tablet.jpg'); }
body[data-page="case-concepts-research.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-controllers-front.jpg'); }
body[data-page="case-engage-xr.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-on.jpg'); }
body[data-page="case-mentorship-community.html"] .mxd-promo__bg{ background-image: url('../img/celia-tools-tablet.jpg'); }
body[data-page="case-oisoi-studio.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-controllers-front.jpg'); }
body[data-page="case-ybvr.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-on.jpg'); }
body[data-page="faq.html"] .mxd-promo__bg{ background-image: url('../img/celia-tools-tablet.jpg'); }
body[data-page="pricing.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-controllers-front.jpg'); }
body[data-page="success-stories.html"] .mxd-promo__bg{ background-image: url('../img/celia-headset-on.jpg'); }
body[data-page="what-is-ka.html"] .mxd-promo__bg{ background-image: url('../img/celia-tools-tablet.jpg'); }

/* ---------------------------------------------------------------------------
   Ajustes de puntos de corte — auditoría de 13 páginas en 7 anchos
   ---------------------------------------------------------------------------
   Los cuatro problemas que salieron al medir, en orden de gravedad.
   --------------------------------------------------------------------------- */

/* 1. El vídeo del proyecto sacaba SCROLL HORIZONTAL en la página de ENGAGE,
      de 21px en un móvil a 74px en escritorio. La culpa es de
      `anim-uni-scale-in`: la animación de entrada de Rayo arranca con el
      elemento escalado por encima de 1, y un `transform` no respeta el ancho
      del contenedor. El `overflow-hidden` que ya llevaba está en el propio
      embed, y un elemento no se recorta a sí mismo: tiene que recortarlo su
      sección. Era la única página del sitio con desbordamiento. */
.mxd-section:has(> .mxd-container > .mxd-block > .ka-embed){
  overflow: hidden;
}

/* 2. En la banda de contacto, entre 768 y 991px el texto se metía DEBAJO de
      la foto de Celia: 39px de solape a 768 y 4px a 834. El diseño apilado
      que hicimos para el teléfono cortaba en 767, y a partir de ahí entraba
      el de dos columnas sin que hubiera sitio para dos columnas. La solución
      es que el apilado llegue hasta donde sí caben. Ver «La banda de contacto
      en móvil», que ahora cubre hasta 991px. */

/* 3. Los tres artículos de la home se veían enteros de 768 a 1199px, uno por
      fila y a todo el ancho: 678px de tarjeta en tablet y 1090px en un
      portátil de 1180. Como son el mismo artículo repetido, ver uno basta
      hasta que la rejilla de tres columnas entra en 1200. */
@media only screen and (max-width: 1199px){
  body[data-page="index.html"] .mxd-blog-preview__item ~ .mxd-blog-preview__item{
    display: none;
  }
}

/* 4. Las tarjetas de Success stories cambiaban de forma en ese mismo tramo:
      0,72 en móvil y 0,73 en escritorio, pero 1,08 entre 768 y 1199 porque
      Rayo les fija 600px de alto. El encuadre de las fotos está pensado para
      vertical, así que en esa franja salía otro recorte. Se le devuelve la
      proporción de los otros dos tramos. */
@media only screen and (min-width: 768px) and (max-width: 1199px){
  /* Con la proporción devuelta pero a todo el ancho, la tarjeta se iba a
     1060x1472 en un portátil de 1180: correcta de forma y absurda de tamaño.
     Se le pone el mismo tope de ancho que tiene en escritorio. */
  .mxd-project-item{
    max-width: 52rem;
  }
  .mxd-project-item__preview{
    height: auto;
    aspect-ratio: 0.72;
  }
}

/* ---------------------------------------------------------------------------
   Los botones animados no se parten a mitad de palabra
   ---------------------------------------------------------------------------
   `btn-anim` es el botón cuyo texto sube al pasar por encima. Para animarlo,
   app.js parte el rótulo en un <span> POR LETRA (`.btn-anim__letter`), cada
   uno `inline-block`.

   Y ahí está el problema: para el navegador esas letras ya no forman una
   palabra, son cajas sueltas, así que puede cortar la línea entre dos
   cualesquiera. Si el botón se queda estrecho, «project» se parte por donde
   sea y salta a una segunda línea. Como `.btn-anim .btn-caption` mide
   exactamente 3,3rem de alto con `overflow: hidden` —para que la letra
   sobrante quede oculta mientras se desliza— esa segunda línea desaparece.
   Resultado: se lee «projec» y la «t» no está en ninguna parte.

   Con `nowrap` la línea no se rompe nunca y el botón crece a lo ancho, que es
   lo que debe hacer. Los rótulos del sitio son cortos; el más largo, «All
   success stories», cabe de sobra incluso a 320px.
   --------------------------------------------------------------------------- */
.btn-anim .btn-caption{
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Ritmo dentro de las tarjetas de servicio
   ---------------------------------------------------------------------------
   Rayo separa por igual el título de las etiquetas (3,4rem) y las etiquetas
   del texto (3,2rem), y con dos huecos casi idénticos las tres piezas se leen
   como una lista suelta. Con el título despegado y las etiquetas pegadas a su
   párrafo, se entiende que las etiquetas pertenecen a lo que viene debajo.
   --------------------------------------------------------------------------- */
.mxd-services-stack__inner .mxd-services-stack__title{
  margin-bottom: 5.6rem;
}
.mxd-services-stack__inner .mxd-services-cards__tags{
  margin-bottom: 2.4rem;
}

/* ---------------------------------------------------------------------------
   Interlineado del cuerpo de texto
   ---------------------------------------------------------------------------
   Rayo pone 1,6 en todos los párrafos. A 1,8rem de tamaño eso son 28,8px de
   línea, que en columnas estrechas —las tarjetas, la columna del caso— deja
   los renglones flotando y el bloque se deshace. 1,5 los mantiene juntos sin
   apelmazarlos.

   No se toca ni titulares ni etiquetas: solo el cuerpo. Y se deja fuera el
   texto pequeño del modal de cookies, que ya tiene el suyo medido aparte.
   --------------------------------------------------------------------------- */
p{
  line-height: 1.5;
}
.cookie-banner__body p,
.cookie-modal__intro,
.cookie-cat p{
  line-height: 1.55;
}

/* En pantallas anchas el título va partido a propósito, para que las tres
   tarjetas empiecen a la misma altura. En estrechas sobra: la caja ya es
   angosta y el texto se parte solo donde le toca. */
@media only screen and (max-width: 991px){
  .ka-br-ancho{ display: none; }
}

/* Más aire por arriba y el título más cerca de sus etiquetas. El bloque
   quedaba pegado al canto superior y con un vacío enorme en medio. */
@media only screen and (max-width: 991px){
  .mxd-services-stack__inner{
    padding-top: 4.6rem;
  }
  .mxd-services-stack__inner .mxd-services-stack__title{
    margin-bottom: 3.2rem;
  }
}

/* El vídeo de «Solution» va a sangre: se come el relleno lateral de la
   tarjeta y llega de canto a canto. Es un plano general dentro de una cabina
   de avión, y encajonado entre márgenes no se leía qué era. Los cantos los
   redondea la propia tarjeta, que ya recorta con `overflow: hidden`. */
@media only screen and (max-width: 991px){
  .mxd-services-stack__inner .services-stack__image.ka-media-16-9{
    margin-left: -3rem;
    margin-right: -3rem;
    border-radius: 0;
  }
}
@media only screen and (min-width: 768px) and (max-width: 991px){
  .mxd-services-stack__inner .services-stack__image.ka-media-16-9{
    margin-left: -5rem;
    margin-right: -5rem;
  }
}

/* ---------------------------------------------------------------------------
   Las fases de «How we work»
   ---------------------------------------------------------------------------
   El cuadradito no se dibuja con un `list-style`: es el mismo marcador que ya
   usan los antetítulos del sitio («The studio», «Our clients»), así que se
   pinta igual y hereda su color. Así no hay dos cuadrados distintos.
   --------------------------------------------------------------------------- */
.ka-fases{
  list-style: none;
  margin: 0;
  padding: 0;
}
.ka-fases li{
  position: relative;
  padding-left: 2.2rem;
  margin-bottom: 1.2rem;
  font-size: 1.8rem;
  line-height: 1.5;
  color: var(--t-muted);
}
.ka-fases li::before{
  content: '';
  position: absolute;
  left: 0;
  /* Centrado con la PRIMERA línea, no con el bloque: media línea hacia
     abajo (0,75em con interlineado 1,5) menos medio cuadrado. Antes iba a
     0,85em a ojo y quedaba por debajo del texto. */
  top: calc(0.75em - 0.3rem);
  width: 0.6rem;
  height: 0.6rem;
  /* Mismo tono neutro que las líneas separadoras, no el verde de marca.
     Las líneas van al 9%, pero un cuadrado de 8px a esa opacidad
     desaparece: al ser una pieza tan pequeña necesita más carga para leerse
     como lo mismo. 30% es lo que iguala su peso visual. */
  background: var(--t-bright);
}
.ka-fases strong{
  color: var(--t-bright);
  font-weight: var(--fw-semibold);
}

/* Menos aire entre el titular de la tarjeta y sus etiquetas, y la imagen
   más cerca del párrafo. En escritorio el hueco del título eran 3,4rem y
   el bloque quedaba desarmado. */
.mxd-services-stack__inner .mxd-services-stack__title{
  margin-bottom: 2.4rem;
}
@media only screen and (max-width: 991px){
  .mxd-services-stack__inner .mxd-services-stack__title{
    margin-bottom: 2rem;
  }
  .mxd-services-stack__inner .services-stack__image{
    margin-top: 1.2rem;
  }
}

/* ---------------------------------------------------------------------------
   Las líneas separadoras de «How we work»
   ---------------------------------------------------------------------------
   Rayo las pinta con `--st-bright` y 2px de grosor: un filete casi blanco que
   pesa más que los propios titulares de cada bloque y ordena la sección a
   gritos. Se quedan en 1px y en un tono a un paso del fondo: se nota que hay
   una separación, pero hay que buscarla.

   Va en rgba y no en un token porque ninguno de los que hay es tan tenue;
   los dos valores están medidos contra el fondo de cada modo.
   --------------------------------------------------------------------------- */
.mxd-approach-list__border{
  height: 1px;
  background-color: rgba(244, 245, 246, 0.09);
}
[color-scheme=light] .mxd-approach-list__border{
  background-color: rgba(23, 16, 31, 0.10);
}

/* Un punto menos de aire entre el titular del bloque y su cuerpo. */
.mxd-approach-list .mxd-approach-list__title{
  margin-bottom: 1.4rem;
}

/* ---------------------------------------------------------------------------
   Enlaces legales del pie
   ---------------------------------------------------------------------------
   En reposo, blancos y sin raya. En hover, verdes y subrayados.

   El subrayado va en el RÓTULO, no en el botón. Rayo lo dibuja con un
   `border-bottom` del `.btn`, y ese borde cruza por debajo de todo lo que hay
   dentro, flecha incluida. Con `text-decoration` sobre el `.btn-caption` la
   raya se queda en las palabras y la flecha se libra.
   --------------------------------------------------------------------------- */
.footer-blocks__links .btn,
.footer-blocks__links .btn .btn-caption,
.footer-blocks__links .btn i{
  color: var(--t-bright);
}
.footer-blocks__links .btn{
  border-color: transparent;
}
.no-touch .footer-blocks__links .btn:hover,
.no-touch .footer-blocks__links .btn:hover .btn-caption,
.no-touch .footer-blocks__links .btn:hover i{
  color: var(--ka-link);
  border-color: transparent;
}
.no-touch .footer-blocks__links .btn:hover .btn-caption{
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-thickness: 1px;
}

/* Redes del pie: icono de marca (Phosphor) + nombre + flecha, sin borde. */
.footer-socials{
  align-items: flex-start;
  gap: 1rem;
}
.footer-socials__item{
  margin-bottom: 0;
}
.ka-footer-social,
.ka-footer-social:hover{
  background: transparent;
  border-color: transparent;
  color: var(--t-bright);
  padding-left: 0;
  padding-right: 0;
  justify-content: flex-start;
}
.ka-footer-social .btn-caption,
.ka-footer-social i{
  color: inherit;
}
.no-touch .ka-footer-social:hover,
.no-touch .ka-footer-social:hover .btn-caption,
.no-touch .ka-footer-social:hover i{
  color: var(--ka-link);
  border-color: transparent;
  background: transparent;
}
/* Solo la flecha externa se anima; el logo de marca se queda quieto. */
.no-touch .ka-footer-social.slide-right-up:hover > i:first-of-type::before{
  animation: none;
}


/* ---------------------------------------------------------------------------
   Menú desplegable — las cuatro cosas, juntas
   ---------------------------------------------------------------------------
   1. Tamaño progresivo: Rayo salta de 3,6 a 5,6rem de golpe en 1200px.
   2. Pie a los lados, no centrado: Rayo lo centra por debajo de 1200.
   3. Pie pegado abajo: `margin-top: auto` dentro de la columna del panel.
   4. Velo al hacer scroll, en un pseudoelemento ANCLADO SOBRE el pie
      (`bottom: 100%`), que por construcción no llega al borde del panel y no
      puede tocar sus esquinas redondeadas.

   Lo que rompió escritorio en los intentos anteriores fue añadir además
   `align-items: baseline` y `gap`, que peleaban con la fila que Rayo ya monta
   a partir de 1200. Aquí no se tocan.
   --------------------------------------------------------------------------- */
.main-menu__link,
.main-menu__link .btn-caption{
  font-size: clamp(3.6rem, 1.6rem + 3.2vw, 5.6rem);
  height: 1.17em;
  line-height: 1.17em;
}
/* ABSOLUTO Y PEGADO ABAJO, en todos los anchos.
   Rayo ya lo hace así a partir de 1200px (`position: absolute; bottom: 4rem`)
   y en flujo por debajo. Esa diferencia era la raíz de todo: un elemento
   absoluto IGNORA `margin-top: auto`, así que todos mis intentos de empujarlo
   al fondo no hacían nada en escritorio, y el contenido del menú le pasaba
   por debajo y se le montaba encima.

   Unificado: absoluto siempre. Y para que la lista no pase por debajo, se le
   reserva sitio con relleno inferior — es la pareja obligatoria de un pie
   fijo sobre una lista que hace scroll. */
.mxd-menu__data{
  position: absolute;
  left: 0;
  right: 0;
  bottom: 4rem;
  flex-direction: row;
  justify-content: space-between;
  text-align: left;
}
@media only screen and (min-width: 1600px){
  .mxd-menu__data{ bottom: 6rem; }
}
.mxd-menu__data::before{
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  /* Más alto y opaco antes: con 7rem y el 92% a mitad de recorrido, el texto
     que pasaba por debajo seguía leyéndose cruzando el pie. Ahora es opaco
     del todo en la primera cuarta parte, así que nada llega a tocarlo. */
  height: 11rem;
  margin-bottom: -1px;
  pointer-events: none;
  background: linear-gradient(to top,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 255, 255, 1) 25%,
    rgba(255, 255, 255, 0.85) 55%,
    rgba(255, 255, 255, 0) 100%);
}
[color-scheme=light] .mxd-menu__data::before{
  background: linear-gradient(to top,
    rgba(10, 7, 16, 1) 0%,
    rgba(10, 7, 16, 0.92) 35%,
    rgba(10, 7, 16, 0) 100%);
}

/* ---------------------------------------------------------------------------
   Los enlaces del menú no se recortan nunca
   ---------------------------------------------------------------------------
   Rayo pone `overflow: hidden` en `.btn-anim .btn-caption` —lo necesita para
   la animación de las letras al pasar por encima— y su caja es un bloque, así
   que la hereda de su padre. Si por lo que sea sale más estrecha que el
   texto, lo corta en seco: de ahí «Success sto».

   `width: max-content` obliga a la caja a medir lo que mide el texto, así que
   ya no hay nada que recortar. No toca la animación.
   --------------------------------------------------------------------------- */
.main-menu__link .btn-caption{
  width: max-content;
}

/* El pie, abajo del todo. `.mxd-menu__inner` ya es columna en Rayo; se
   repite para que `margin-top: auto` tenga contra qué empujar en todos los
   anchos y quede mucho aire blanco bajo «Contact us». */
.mxd-menu__inner{
  display: flex;
  flex-direction: column;
  /* SIN posicionar. Es el elemento que hace scroll, y mientras estuviera
     `relative` el pie absoluto se anclaba a ÉL: en un contenedor con scroll,
     `bottom: 4rem` significa «4rem desde el final de todo el contenido
     desplazable», no desde el borde visible. De ahí que el pie flotara a
     media altura y que el degradado cayera encima de «Contact us» sin haber
     hecho scroll. Quitándole la posición, el pie se ancla a
     `.mxd-menu__contain`, que es el panel y no se mueve. */
  position: static;
}

/* En los tamaños intermedios el panel no se come la pantalla: se sigue viendo
   el hero por detrás, como en escritorio. */
@media only screen and (min-width: 700px) and (max-width: 1199px){
  .mxd-menu__contain{
    max-width: 58rem;
    margin-left: auto;
  }
}

/* El pie del menú NO se encoge y siempre tiene aire por encima.
   En una columna con altura fija, cuando el contenido no cabe el navegador
   encoge los elementos flexibles para hacer sitio: el pie se quedaba en cero
   de alto y el texto del menú se le montaba encima. Es lo que pasaba al abrir
   el submenú en una pantalla poco alta.
   `flex-shrink: 0` lo deja intocable, y el relleno inferior de la lista
   garantiza el espacio en blanco bajo «Contact us» en cualquier caso. */
.mxd-menu__left{
  /* 22rem = el pie (a 4rem del fondo) + su alto + el velo de 7rem + aire.
     Con menos, «Contact us» quedaba justo bajo el degradado y se veía
     apagado, como si estuviera desactivado. */
  padding-bottom: 22rem;
}
@media only screen and (min-width: 1200px){
  .mxd-menu__left{
    padding-bottom: 18rem;
  }
}

/* VELO SUPERIOR: RETIRADO.
   Se intentó dos veces y las dos se vio flotando sobre el hero. La razón es
   la misma que atraviesa todo este componente: la tarjeta blanca del menú la
   dibuja GSAP con una animación Flip, y un pseudoelemento de CSS se pinta
   desde el primer instante, sin esperar a que el panel exista. El de abajo sí
   funciona porque se apoya en `.mxd-menu__data`, que es DOM real dentro del
   panel; arriba no hay nada donde apoyarse.

   Para retomarlo haría falta condicionarlo al estado abierto del menú (la
   clase `menu-is-visible` que app.js pone en el header), no al CSS a secas.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   Testimonios: el bloque de autor arranca siempre a la misma altura
   ---------------------------------------------------------------------------
   La tarjeta es una columna con `justify-content: space-between`: el texto
   arriba, el autor abajo. Como la tarjeta mide lo mismo en todas las fichas
   (600/640/700px según ancho) y el bloque de autor se pega al fondo, la
   altura de ese bloque decide dónde empieza el nombre. Con roles de una sola
   línea el nombre bajaba; con el de Eduardo —que ocupa dos— subía. Al pasar
   de una ficha a otra el nombre daba un salto.

   La referencia es la ficha alta, la de dos líneas de rol. Reservando ese
   alto en TODOS los roles, el bloque mide siempre igual y los nombres quedan
   alineados; a quien tenga el rol corto le sobra aire por debajo, que es lo
   que se busca. `3.2em` son dos líneas del propio párrafo (1.6 de interlínea
   x 2), así que la reserva sigue al tipo si cambia de tamaño.

   Solo de 768px hacia arriba: en móvil la tarjeta crece con su contenido, no
   hay nada que alinear y la reserva solo añadiría hueco muerto.

   El nombre, en una línea: es un nombre propio y partirlo entre apellidos se
   lee mal.

   Entre 768 y 1399px la tarjeta se queda en 460px y el hueco útil del nombre
   son 380px. Ahí el más largo —«Helena Llorente De Carli»— mide 390px a
   3,4rem y se saldría, así que en esa franja el nombre baja a 3,2rem: 367px,
   con holgura. Se baja en TODAS las fichas, no solo en la larga, para que dos
   tarjetas seguidas no cambien de tamaño de letra. De 1400px en adelante hay
   sitio de sobra y se queda en 3,4rem.
   --------------------------------------------------------------------------- */
@media only screen and (min-width: 768px){
  .mxd-testimonials-card__position{
    min-height: 3.2em;
  }
  .mxd-testimonials-card__name{
    white-space: nowrap;
  }
}
@media only screen and (min-width: 768px) and (max-width: 1399px){
  .mxd-testimonials-card__name{
    font-size: 3.2rem;
  }
}

/* ---------------------------------------------------------------------------
   Testimonios: la cita, más cerca de la foto
   ---------------------------------------------------------------------------
   Rayo separa la foto del cuerpo del testimonio con 4,9rem. Con las fotos
   redondas y grandes de las fichas, ese hueco leía como un corte: la cara por
   un lado y el texto por otro, en vez de una persona hablando. 4,1rem —ocho
   píxeles menos— los vuelve a juntar sin apretarlos.

   De paso, esos ocho píxeles vuelven al presupuesto de alto de la tarjeta,
   que en la franja de 768 a 1399px va justo.
   --------------------------------------------------------------------------- */
.mxd-testimonials-card__content{
  gap: 4.1rem;
}

/* ============================================================
   PÁGINA ABOUT (portfolio personal)
   Dos columnas: retrato a la izquierda, biografía a la derecha.
   Sin grid propio: reutiliza las columnas de Bootstrap de Rayo.
   ============================================================ */
.ka-about-photo img{
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-m, 1.6rem);
}
.ka-about-text{
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
}
@media only screen and (min-width: 1200px){
  .ka-about-text{
    padding-right: 4rem;
  }
}
.ka-about-text > p{
  margin: 0;
}
.ka-about-text .mxd-manifest__controls{
  margin-top: 1.2rem;
}

/* Miniaturas debajo de la foto grande de «About me»: mismo grid de
   ka-about-photos, pero más compactas y con hueco respecto a la foto. */
.ka-about-photos--small{
  margin-top: 1.2rem;
  gap: 0.8rem;
}
.ka-about-photos--small img{
  aspect-ratio: 1 / 1;
  border-radius: 10px;
}

/* La miniatura de las Meta Ray-Ban Display centra por defecto (50% 50%) y
   con una foto tan vertical eso corta la frente/ojos. Ancla arriba para que
   se vea la cara entera. Solo esta miniatura la necesita. */
.ka-about-photos--small img.ka-crop-top{
  object-position: center top;
}

/* Fila de "juguetes de la infancia" dentro del texto de About me: muy
   pequeñas y con su propio pie, a modo de polaroid, justo junto al
   párrafo que las menciona (Tamagotchi, Tetris, NO-NO...). No usan el
   marco de ka-media —eso es para las piezas de los casos— porque aquí
   el punto es que sean chiquititas y con un aire distinto, de recuerdo. */
.ka-about-toys{
  display: flex;
  flex-wrap: wrap;
  gap: 1.2rem;
  margin: 0.4rem 0 1rem;
}
.ka-about-toys figure{
  margin: 0;
  width: 6.8rem;
}
.ka-about-toys img{
  width: 6.8rem;
  height: 6.8rem;
  object-fit: cover;
  border-radius: 8px;
  display: block;
}
/* El pantallazo del Spectrum es pixel art de verdad (256x192 originales):
   escalarlo con suavizado lo deja borroso. Que se note que son píxeles. */
.ka-about-toys img.ka-toy-pixelated{
  object-fit: contain;
  background: #000;
  image-rendering: pixelated;
}
/* `.t-small`/`.t-muted` en main.css solo aplican a `p.t-small` — un
   <figcaption> no es <p>, así que aquí no heredan nada y salían al tamaño de
   párrafo normal. En un recuadro de 5,6rem eso desborda con cualquier
   palabra suelta (Tamagotchi, Paperboy) y se monta encima de la siguiente
   figura. Tamaño y color explícitos, más rotura de palabra por si acaso. */
.ka-about-toys figcaption{
  margin-top: 0.4rem;
  text-align: center;
  font-size: 1.2rem;
  line-height: 1.3;
  color: var(--t-muted);
  overflow-wrap: break-word;
  word-break: break-word;
  hyphens: auto;
}

/* Dos CTA en la misma fila (Get in touch + Download my CV): las únicas
   páginas que usan .mxd-manifest__controls son home y About me, así que no
   hace falta una clase nueva para no afectar a otras. */
.mxd-manifest__controls{
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem;
}

/* Botón secundario "Download my CV": mismo btn-outline pero con el verde/
   magenta seguro de --ka-link en vez de --st-bright/--t-bright (el verde de
   marca puro no se puede usar como texto en modo claro, ver cabecera del
   archivo). */
.btn-outline-cv{
  border-color: var(--ka-link);
  color: var(--ka-link);
}


/* ── Biografía en línea de tiempo (About me) ─────────────────────────────
   Sustituye al antiguo layout de dos columnas (texto + foto lateral).
   Reutiliza .ka-eyebrow y .ka-media/.ka-media--natural/.ka-media__frame
   (mismos que en las páginas de caso) para que la tipografía y el trato
   de imagen sean coherentes con el resto del sitio; lo nuevo es solo el
   espinazo vertical con los puntos, que ka-chapter no tiene. */
.ka-bio-timeline{
  position: relative;
  padding-top: .4rem;
}
.ka-bio-timeline::before{
  content: '';
  position: absolute;
  left: .9rem;
  top: .6rem;
  bottom: 3.2rem;
  width: 2px;
  background: var(--t-muted-extra);
  opacity: .35;
}
.ka-bio-node{
  position: relative;
  padding-left: 3.6rem;
  margin-bottom: 5.6rem;
}
.ka-bio-node:last-child{ margin-bottom: 0; }
.ka-bio-dot{
  position: absolute;
  left: 0;
  top: .4rem;
  width: 1.8rem;
  height: 1.8rem;
  border-radius: 50%;
  background: var(--base);
  border: 3px solid var(--accent);
}
/* Alterna violeta/verde a lo largo de la línea, como en el resto del sitio
   (--accent y --ka-link son los dos colores de marca ya usados en Rayo/KA). */
.ka-bio-node--alt .ka-bio-dot{ border-color: var(--ka-link); }
.ka-bio-node .ka-eyebrow{ display: block; margin-bottom: .8rem; }
.ka-bio-node h2{
  margin: 0 0 1.6rem;
  font-size: clamp(2.2rem, 2vw, 2.8rem);
  line-height: 1.15;
  overflow-wrap: break-word;
}
/* Los párrafos del cuerpo, sin tope, se extendían al ancho completo de la
   rejilla (más de 100 caracteres por línea) y eran incómodos de leer.
   Se limita a una medida de columna clásica, como el resto de textos
   largos del sitio. */
.ka-bio-node .ka-chapter__body{
  max-width: 60ch;
}
/* Fotos del timeline: aspecto natural (sin recorte forzado), porque las
   fuentes reales mezclan apaisadas y verticales y un marco fijo recortaría
   mal alguna de ellas. `--portrait` limita el ancho de las verticales para
   que no se disparen de alto en el móvil. */
.ka-bio-photo{
  margin-top: 2rem;
  max-width: 46rem;
}
.ka-bio-photo .ka-media__frame{ margin-inline: 0; }
.ka-bio-photo--portrait{ max-width: 26rem; }
.ka-bio-photo figcaption{
  margin-top: 1rem;
  font-size: 1.3rem;
  color: var(--t-muted);
}
.ka-bio-video{
  margin-top: 2rem;
  max-width: 46rem;
}
.ka-bio-video .ka-media__frame{ margin-inline: 0; }
/* Caja de cierre del último nodo (Right now): reutiliza el mismo panel
   tintado que .ka-contact-intro en contact.html, para que la despedida
   de about.html y la de contact.html se sientan de la misma familia. */
.ka-bio-cta-card{
  margin-top: 2.4rem;
  max-width: 48rem;
}
.ka-bio-cta-card p:last-of-type{ margin-bottom: 0; }
.ka-bio-cta-card .mxd-manifest__controls{ margin-top: 2rem; }

@media only screen and (max-width: 767px){
  .ka-bio-node{ padding-left: 2.8rem; margin-bottom: 4.4rem; }
  .ka-bio-timeline::before{ left: .7rem; }
  .ka-bio-dot{ width: 1.4rem; height: 1.4rem; top: .3rem; }
}
