/* ============================================================
   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;
}

/* Rotator vertical dentro de pill (estilo marquee Rayo).
   El JS (ka-hero-words.js) anima translateY y el width de la pill. */
.hero-01-title__item.ka-hero-pill{
  --ka-pill-gap: 0.55em; /* aire entre palabras en el track */
  opacity: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: 1.08em;
  /* ~XR: evita FOUC al ancho de Design/Testing antes de que JS mida */
  width: 2.4em;
  margin-inline: 0.12em;
  padding: 0 0.42em;
  background-color: rgba(0, 0, 0, 0.39);
  border: 7px solid rgba(255, 255, 255, 0.28);
  box-shadow: none;
  border-radius: 999px;
  color: #fff;
  vertical-align: middle;
  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: 1em;
  overflow: hidden;
  width: 100%;
}
.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 1em;
  height: 1em;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-style: normal;
  color: #fff;
  white-space: nowrap;
  padding: 0 0.04em;
  box-sizing: border-box;
  /* Offset vertical del glifo dentro del slot */
  transform: translateY(-2px);
}
.ka-hero-pill__track > span.is-vr{
  /* VR ya se ve bien; el resto sube 2px */
  transform: translateY(0);
}
.ka-hero-pill__track > span.is-descender{
  /* Design, Testing — un poco más arriba por descenders */
  transform: translateY(calc(-0.1em - 2px));
}

/* 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.02em;
  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 "IA", 5,74em. Si se dimensiona mirando "IA",
      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;
  }

  /* Pill más alta: con border-box el borde se come la altura y
     las palabras (XR, Design…) se recortaban. Borde a 3px.
     Un poco más de aire a la izquierda para separarla de «love». */
  .hero-01-title__item.ka-hero-pill{
    height: 1.32em;
    margin-left: 0.28em;
    padding: 0 0.38em;
    border-width: 3px;
  }
}

/* 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. */
.bg-accent .tag-outline-opposite{
  color: var(--t-opp-bright);
  border-color: var(--st-opp-bright);
}
/* Y el cuerpo de la tarjeta igual. Rayo ya lo pedía con `t-opposite`; era la
   regla de arriba la que lo volvía blanco. El titular se queda en claro. */
.bg-accent p.t-opposite{ color: var(--t-opp-bright); }

/* 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: el logotipo de ENGAGE queda a la izquierda y la avatar a la
   derecha, así que un recorte centrado los partiría los dos. Se encuadra
   sobre la avatar, que es vertical y aguanta bien el formato. El logotipo se
   pierde, pero la tarjeta ya lleva «ENGAGE XR» escrito justo debajo. */
.preview-image-1{
  background-image: url('../img/engage-athina-ai-avatar.jpg');
  /* Pegada al borde derecho, que es donde está la avatar.
     La tarjeta enseña el 41% del ancho en escritorio y solo el 34% en el
     teléfono. La avatar ocupa del 70% al 97%, así que para que quepa entera
     en la ventana más estrecha el encuadre tiene que estar como mínimo al
     96%. Al 84% se le comía el hombro en móvil. Al 100% entra con margen en
     los dos, a costa del logotipo, que se corta: es lo que pidió Celia. */
  background-position: 100% 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: en verde solo al pasar por encima, para que no compita
   con la dirección postal que tiene al lado. */
.footer-blocks__card a[href^="mailto:"]{
  transition: color .18s ease;
}
.no-touch .footer-blocks__card a[href^="mailto:"]:hover{
  color: var(--ka-link);
}

/* 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;
}
.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{
  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{
  width: 0.86em;
  height: 0.86em;
  flex-shrink: 0;
}
.footer-blocks__column:nth-child(2) .mxd-point-subtitle > a,
.footer-blocks__column:nth-child(2) .mxd-point-subtitle > span{
  white-space: nowrap;
}

@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{
  --ka-shape: min(8.4rem, 21vw);
  --ka-union: min(1.6rem, 4vw);
  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;
    --ka-union: 9.6rem;
  }
}

.ka-process__step{ display: flex; }

.ka-process__shape{
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--ka-shape);
  height: var(--ka-shape);
  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{
  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; }

/* ---------------------------------------------------------------------------
   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{
  max-width: 46ch;
  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.
   --------------------------------------------------------------------------- */
@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;
  }
  .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; }
}

.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: #39ff88;
  color: #0a0710;              /* 15.07:1 sobre el verde */
  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 *{ 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; }
}

/* 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 What is KA, en dos pesos
   ---------------------------------------------------------------------------
   La primera línea es el planteamiento y va pequeña; la segunda es la
   conclusión y se lleva el tamaño de titular. Se marca en el HTML con
   `.ka-titular__entrada`, no partiendo el h1 en dos, para que siga siendo
   una sola frase de cara a un buscador o a un lector de pantalla.
   --------------------------------------------------------------------------- */
.ka-titular__entrada{
  display: block;
  margin-bottom: 1.6rem;
  font-size: 2.2rem;
  line-height: 1.35;
  letter-spacing: 0;
  font-weight: var(--fw-regular);
  color: var(--t-muted);
}
@media only screen and (min-width: 768px){
  .ka-titular__entrada{ font-size: 3rem; }
}

/* ---------------------------------------------------------------------------
   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'); }
