/* Clase generica, sin uso hasta 2026-07-11: peticion del cliente de
   trasladar el vector horizontal que Figma pone justo debajo de
   "Servicios" en Home (nodo 1:737, linea de ancho completo bajo el
   titulo, antes de que empiece el contenido) - usada en index.html/
   en/index.html entre el <h2> y .service-showcase__list. --color-border
   coincide con el mismo tono ya usado para separadores horizontales de
   ancho completo en este componente (ver .service-link-row border-top,
   mismo rol visual). */
.divider {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: 0;
}

/* ---------- Project entry ----------
   En el Figma, los 4 proyectos del Home usan la misma proporcion de
   columnas y la misma imagen ~retrato (texto ~410px / imagen ~770-790px,
   imagen con altura mayor que el ancho, ratio ~0.9). No hay un proyecto
   "destacado" mas grande que el resto: las 4 filas son visualmente
   equivalentes. */
.project-entry {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.9fr);
  gap: var(--space-xl);
  align-items: center;
  padding-block: var(--space-2xl);
  border-top: 1px solid var(--color-border);
}

.project-entry:first-of-type { border-top: none; }

/* HOME-only (#home-proyectos, index.html). En Figma (Vector 1 dentro de
   cada Proyecto_1: nodos 1:776/1:781/1:786/1:791) la linea separadora vive
   pegada al titulo -11px de separacion, ancho = columna de texto (406px)-,
   no como border-top de toda la fila. Los 4 proyectos la tienen, incluido
   el primero. Se ancla al propio <h3> para que el ancho quede acotado a la
   columna de texto y la separacion de 11px no varie por el align-items:
   center del grid. proyectos.html no se toca (fuera de alcance, usa las
   reglas base de arriba sin cambios). Ver current-section-repair.md. */
/* Ajuste de padding-top de Proyectos (hereda 96px de .section, que tambien
   usa el teaser de Servicios mas abajo - no se toca esa regla compartida). */
#home-proyectos {
  padding-top: 100px;
}

/* Historial 2026-06-29: hubo una ronda de ajustes de espaciado solo para
   tablet (padding-top/bottom de #home-proyectos reducidos 70%, margin-top
   del titulo a 0, padding-top del primer proyecto a 0) pensados para el
   layout EN FLUJO NORMAL (fallback sin pin) que tenia tablet en ese
   momento. El mismo dia, el usuario pidio que Proyectos tuviera el mismo
   efecto de panel fijo/crossfade que en escritorio tambien en tablet (ver
   CR-03 mas abajo) - con el panel de vuelta, esos ajustes de flujo normal
   ya no aplican (el panel no usa esos paddings/margins de la misma forma)
   y se han retirado: tablet hereda exactamente el espaciado de escritorio
   para Proyectos, sin overrides propios. */
#home-proyectos .project-entry {
  border-top: none;
  padding-block: 24px;
  margin-top: 0;
}

#home-proyectos .project-entry__title {
  border-top: 1px solid var(--color-text-muted);
  margin-top: 115px;
  padding-top: 11px;
}

/* Valores exactos de Figma (1:772/1:778/1:783/1:788): 32px, peso Regular
   (no 600), ancho de caja 397px (necesario para que "Abu Dhabi" parta en
   2 lineas igual que en Figma). Escopado a #home-proyectos: no toca el
   token --text-heading compartido ni la regla base que usa proyectos.html. */
#home-proyectos .project-entry__title {
  font-size: 32px;
  font-weight: normal;
  line-height: 1.1;
  max-width: 397px;
}

/* Cambio de marca 2026-06-26: Figma (1:773/etc.) ahora usa 20px para la
   categoria de cada ficha (antes 32px, igual que el titulo). */
#home-proyectos .project-entry__category {
  font-size: 20px;
  margin-top: 0;
}

/* Descripcion: ancho exacto 410px en las 4 fichas (Figma). Cambio de
   marca 2026-06-26: Figma unifico el font-size a 16px en las 4 fichas
   (antes 14px solo en Wanderlust, 1:774 - inconsistencia real que el
   propio cliente corrigio) - ya no hace falta el override por ficha, el
   16px de --text-body (ver tokens.css) cubre las 4. */
/* Peticion cliente 2026-07-26 (1a): subir el texto 2px - con
   margin-top:auto empujando el bloque al fondo de la fila, aumentar el
   padding-bottom (que reserva el hueco entre el texto y el borde
   inferior real) sube el texto sin mover el borde inferior de la fila
   ni el resto del layout.
   Peticion cliente 2026-07-26 (2a, mismo dia): ademas, subir 2px el
   font-size - de 16px (--text-body, token compartido con el resto del
   sitio) a 18px, explicito y escopado solo a esta ficha de Home para no
   afectar el resto de usos de --text-body.
   Peticion del usuario 2026-07-30: ya no anclada al fondo de la fila -
   margin-top pasa de auto a 0 en escritorio (>1279px), igual que ya se
   hizo en tablet (861-1279px, ver @media mas abajo). Solo afecta a
   escritorio: movil (<=860px) mantiene su propio override de 20px. */
#home-proyectos .project-entry__description {
  max-width: 410px;
  margin-top: 0;
  padding-bottom: 102px;
  font-size: 18px;
}

/* En Figma la descripcion queda anclada al borde inferior de cada fila
   (bottom de su caja = bottom del frame del proyecto, exacto en 3/4 casos),
   no pegada justo debajo de la categoria con un margen fijo. Se logra
   estirando la fila (en vez de centrarla) y convirtiendo la columna de
   texto en flex-column: titulo+categoria quedan arriba en su flujo normal,
   y "margin-top:auto" en la descripcion (arriba) empuja solo a ella hacia
   el fondo. */
#home-proyectos .project-entry {
  align-items: stretch;
}

#home-proyectos .project-entry > div:first-child {
  display: flex;
  flex-direction: column;
}

.project-entry__title {
  font-family: var(--font-body);
  font-size: var(--text-heading);
  font-weight: 600;
}

.project-entry__category {
  font-family: var(--font-body);
  font-size: var(--text-heading);
  color: var(--color-text-muted);
  margin-top: 0.25rem;
}

.project-entry__description {
  margin-top: var(--space-lg);
  color: var(--color-text-soft);
  font-size: var(--text-body);
  line-height: 1.7;
  max-width: 48ch;
}

.project-entry__media {
  border-radius: 4px;
  overflow: hidden;
  aspect-ratio: 9 / 10;
}

.project-entry__media a {
  display: block;
  width: 100%;
  height: 100%;
}

.project-entry__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- Proyectos: scroll showcase (CR-03, peticion de cliente 2026-06-25) ----------
   Peticion del cliente (ver client-feedback/projects-scroll-showcase-spec.md):
   mientras se hace scroll en Proyectos, el titulo "Proyectos" queda fijo;
   la columna de imagenes se desplaza de forma continua (sube/baja); la
   columna de texto NO se desplaza, solo aparece/desaparece (crossfade)
   mostrando el texto del proyecto que corresponde a la imagen visible en
   ese momento. Referencia citada: koto.com, seccion "Our Work".

   Misma tecnica que CR-02 (scroll pin + variables CSS actualizadas por
   JS, sin librerias externas), pero con progreso CONTINUO de 0 a N-1
   (N=numero de proyectos, --project-track-progress) en vez de 0-1, para
   que las imagenes se vean desplazarse de forma fluida entre posiciones,
   no a saltos.

   IMPORTANTE: no se duplican ni renombran .project-entry/.project-entry__title/
   etc. - son los MISMOS elementos ya afinados a pixel fidelity (ver
   comentarios de arriba). Este bloque solo AÑADE el comportamiento de
   scroll por encima, y el fallback (media query al final) neutraliza esas
   reglas para volver exactamente al layout apilado de siempre. */
.project-showcase {
  position: relative;
  height: calc(4 * 100vh);
}

.project-showcase__pin {
  position: sticky;
  top: var(--header-height);
  height: calc(100vh - var(--header-height));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* margin-left:100px pedido por el cliente 2026-07-10, solo version web
   (escritorio/tablet) - el fallback movil/reduced-motion de mas abajo lo
   resetea a 0. */
.project-showcase__list {
  position: relative;
  flex: 1;
  min-height: 0;
  margin-left: 100px;
}

/* Cada .project-entry pasa de "fila apilada" a "panel superpuesto a
   pantalla completa dentro del pin" - por eso se anula el padding-block
   de 24px (#home-proyectos .project-entry), que solo tenia sentido
   cuando los proyectos estaban uno debajo de otro en flujo normal.
   align-items:stretch y la columna de texto en flex-column (reglas de
   mas arriba) siguen aplicando igual - el anclaje de la descripcion al
   fondo de su columna no cambia.
   2026-06-28 (peticion del usuario): la linea sobre el titulo (Figma
   1:776/1:781/1:786/1:791) SI debe verse tambien en este modo panel -
   se habia anulado por error de criterio al construir este efecto
   (border-top:none/margin-top:0/padding-top:0), pero el fallback movil
   de mas abajo ya la restauraba correctamente. Se igualan ambos modos:
   mismos valores que la version apilada/movil (border-top 1px,
   margin-top 115px, padding-top 11px). */
#home-proyectos .project-showcase__list .project-entry {
  position: absolute;
  inset: 0;
  padding-block: 0;
  /* Los 4 paneles ocupan el mismo inset:0 y se cruzan por opacidad -
     sin esto, el ultimo del DOM (data-project-index="3") queda siempre
     por encima en el orden de pintado y se come los clics de los 3
     paneles anteriores, aunque no sean el activo visible. Solo el panel
     .is-active debe recibir clics (titulo e imagen enlazan a su
     proyecto). */
  pointer-events: none;
  /* Sin esto, la unica fila del grid (implicita, "auto") se dimensiona
     por el contenido mas alto - incluida .project-entry__media, que es
     precisamente el elemento al que luego queremos ponerle un
     max-height:100% (peticion cliente 2026-07-10, tamano de Figma
     767x868). Esto crea una referencia circular: el porcentaje de
     max-height se resuelve contra el alto de la fila, pero la fila mide
     lo que mida su contenido mas alto (868px), asi que el limite nunca
     hace nada. grid-template-rows:100% fuerza la fila a ocupar
     exactamente el alto de .project-entry (ya fijo via inset:0),
     independiente del contenido - ahi si funciona el max-height del
     hijo. */
  grid-template-rows: 100%;
}

/* margin-top:115px vivia antes aqui (empuja el titulo desde el borde
   superior del panel) - re-auditado 2026-07-20 al montar la persiana de
   titulo+categoria: con el titulo ahora DENTRO de la ventana recortada
   (.project-entry__heading-window, mas abajo), ese margin-top empujaba
   el texto fuera de la ventana (ventana con alto fijo desde arriba,
   texto desplazado 115px hacia abajo dentro de ella = recortado por
   completo, invisible - verificado con Playwright). Se traslada el
   margin-top a la propia ventana (afecta a la posicion de la ventana
   dentro del panel, no al contenido que se desliza dentro de ella);
   border-top/padding-top se quedan aqui, son parte del contenido que
   SI se desliza (la linea va pegada al titulo, no al borde del panel). */
#home-proyectos .project-showcase__list .project-entry__title {
  border-top: 1px solid var(--color-text-muted);
  /* 0, no omitido: la regla base #home-proyectos .project-entry__title
     (mas arriba, especificidad menor pero declara margin-top:115px) NO
     se cancela por si sola con solo quitar la propiedad aqui - hay que
     pisarla explicitamente o el titulo vuelve a heredar el margin-top
     que ya causo el bug de invisibilidad (ver nota en
     .project-entry__heading-window). */
  margin-top: 0;
  padding-top: 11px;
}

#home-proyectos .project-showcase__list .project-entry__heading-window {
  margin-top: 115px;
}

/* Peticion cliente 2026-07-20: titulo+categoria dejan de hacer crossfade
   (opacidad) y pasan a "persiana" - se desliza hacia arriba al entrar/
   salir haciendo scroll hacia abajo, y hacia abajo al hacer scroll hacia
   arriba. Envueltos en 2 niveles (nuevo HTML en index.html/en/index.html):
   - .project-entry__heading-window: la "ventana" fija (overflow:hidden,
     alto fijo medido por JS en --project-heading-h, mismo patron que
     --row-title-h en Servicios) - lo unico que no se mueve.
   - .project-entry__heading: el contenido que SI se desplaza dentro de
     esa ventana.

   Primer intento (2026-07-20): transform continuo atado a
   --project-track-progress/--item-index, igual mecanismo que
   .project-entry__media. Descartado 2026-07-21 (peticion explicita del
   cliente tras verlo: "no quiero que se vea el texto de dos proyectos a
   la vez") - con un transform continuo ligado al scroll, CUALQUIER
   punto intermedio del scroll (el usuario puede parar de hacer scroll
   en cualquier momento, no solo cuando un proyecto esta 100% activo)
   deja los 2 titulos parcialmente visibles y legibles a la vez dentro
   de la misma ventana - verificado con Playwright, se veian claramente
   2 titulos simultaneos al parar el scroll a medio camino. Ademas tenia
   un bug propio (translateY con % es relativo al alto del PROPIO
   elemento, no al de la ventana - un titulo de 1 sola linea no se
   desplazaba lo suficiente para salir del todo de la ventana cuando
   Abu Dhabi, de 2 lineas, era el activo).

   Solucion actual: en vez de un transform continuo, cada titulo solo
   tiene 3 posiciones discretas segun su relacion con el proyecto activo
   (is-active/is-past, ver main.js) - SIEMPRE fuera de la ventana salvo
   el activo, nunca "a medias". El cambio entre posiciones anima con
   transition (0.4s, mismo tiempo que el crossfade de
   .project-entry__description), asi que sigue viendose como una
   persiana deslizante al cambiar de proyecto, pero sin quedar nunca
   parada a medio camino con 2 titulos visibles: parado en cualquier
   punto del scroll, o esta el titulo activo centrado en la ventana, o
   no se ve ninguno todavia/ya paso (segun se este a medio camino entre
   2 proyectos o no). */
/* Bug encontrado 2026-07-25 (al investigar por que el titulo de algunos
   proyectos se recortaba - reportado primero como "108 Wanderlust" en
   tablet): esta ventana es hija de un flex-column (div:first-child de
   .project-entry, alto:100% estirado a la fila completa) sin
   flex-shrink:0 - por defecto flex-shrink:1, asi que si el contenido
   total del flex-column (esta ventana + .project-entry__description) no
   cabe en el alto fijo de la fila, flexbox la ENCOGE en vez de respetar
   el alto exacto medido por JS (--project-heading-h, pensado para
   contener el titulo/categoria completos para el efecto "persiana" de
   mas arriba). El grado de encogido variaba por proyecto segun cuanto
   ocupara su propia descripcion (verificado con Playwright: 51px/148px/
   0px/51px de alto real en los 4 proyectos de Home, en vez de 148px en
   los 4). Con flex-shrink:0 la ventana nunca se encoge; si el contenido
   total sigue sin caber, es .project-entry__description quien se sale
   del alto de la fila (el propio panel raiz ya recorta ese sobrante con
   overflow:hidden - mucho menos visible/grave que recortar titulo o
   categoria). */
.project-showcase__list .project-entry__heading-window {
  overflow: hidden;
  height: var(--project-heading-h, auto);
  flex-shrink: 0;
}

.project-showcase__list .project-entry__heading {
  /* Por defecto (ni activo ni ya pasado): debajo de la ventana, aun no
     le toca aparecer - scroll hacia abajo lo hace subir hasta 0. */
  transform: translateY(var(--project-heading-h, 0px));
  transition: transform 0.4s ease;
  will-change: transform;
}

.project-showcase__list .project-entry.is-active .project-entry__heading {
  transform: translateY(0);
}

.project-showcase__list .project-entry.is-past .project-entry__heading {
  /* Ya se paso este proyecto (scroll hacia abajo lo dejo atras, encima
     de la ventana) - scroll hacia arriba lo hace bajar de vuelta a 0. */
  transform: translateY(calc(-1 * var(--project-heading-h, 0px)));
}

/* La descripcion (fuera de la ventana/persiana) sigue con el mismo
   crossfade de opacidad de siempre - JS anade/quita .is-active en el
   .project-entry segun cual sea el proyecto activo (ver main.js, CR-03). */
.project-showcase__list .project-entry__description {
  opacity: 0;
  transition: opacity 0.4s ease;
}

#home-proyectos .project-showcase__list .project-entry.is-active {
  pointer-events: auto;
}

.project-showcase__list .project-entry.is-active .project-entry__description {
  opacity: 1;
}

/* Columna de imagen: se anade el movimiento (translateY) y la misma
   posicion en el grid de .project-entry (sin position:absolute propio;
   el unico position:absolute de este sistema es el del .project-entry
   padre, que ya agrupa los 4 paneles).
   --item-index es la posicion estatica de cada proyecto (0,1,2,3);
   --project-track-progress es el progreso continuo que pone JS. Con
   item-index=0 y progress=0 -> translateY:0% (en su sitio). Con
   item-index=1 y progress=0 -> translateY: una altura propia + 15px
   hacia abajo. A medida que progress avanza, cada imagen se desplaza
   hacia arriba en multiplos de (su propia altura + 15px) - el +15px
   (peticion cliente 2026-07-10: espacio entre las imagenes de cada
   proyecto) crea un hueco fijo de 15px entre una imagen y la siguiente
   mientras se cruzan durante el scroll, en vez de tocarse borde con
   borde. Funciona igual en las 4 porque las filas miden exactamente lo
   mismo (ver nota de mas abajo). */
.project-showcase__list .project-entry__media {
  /* Peticion cliente 2026-07-10: usar el tamano exacto especificado en
     Figma Dev Mode para la imagen del carrusel (767x868). Probado tal
     cual (width/height fijos) primero: se desbordaba por debajo del
     panel en viewports bajos (~900px de alto, portatiles tipicos) porque
     868px supera el alto real disponible de la fila (~711px en ese
     viewport) - el panel recorta el sobrante (overflow:hidden) y la
     imagen quedaba cortada por la barra "Ver todos los proyectos".
     Corregido con max-height:100%: 868px sigue siendo el alto PREFERIDO
     (se respeta tal cual en pantallas suficientemente altas), pero si la
     fila disponible es mas baja, max-height la encoge. width:auto (en
     vez de 767px fijo) es necesario para que aspect-ratio pueda derivar
     el ancho a partir del alto ya encogido - con width fijo Y height
     fijos a la vez, aspect-ratio se ignora por completo (spec: solo
     actua si al menos una de las dos dimensiones es auto), y el ancho se
     quedaba en 767px aunque el alto se hubiera encogido, deformando la
     proporcion. */
  /* Arreglo 2026-07-25 (reportado por el usuario: en tablet "los textos de
     los proyectos de la Home no se adaptan al ancho" - confirmado con
     Playwright a 1024px: la imagen invadia la columna de texto). Causa:
     height:868px fijo (el alto exacto de Figma) + max-height:100% SOLO
     limitaba el alto por si la fila quedaba mas baja que 868px (caso ya
     corregido 2026-07-10 para portatiles bajos) - pero nunca limitaba el
     ANCHO derivado via aspect-ratio, que en un viewport estrecho pero
     ALTO (tablet en vertical, ~1024x900) sigue siendo enorme (~620-700px)
     aunque la columna de texto solo tenga sitio para ~260px. width/height
     "auto" + max-width/max-height (cada uno con su propio techo de Figma
     via min(), ademas del limite real de la fila/columna en 100%) es el
     patron CSS estandar para que el navegador elija el mas restrictivo de
     los 2 ejes manteniendo el aspect-ratio - en escritorio ancho y alto
     (sitio de sobra en ambos ejes) el resultado es identico a antes
     (767x868, el techo de Figma, verificado con Playwright a 1920x1080);
     en tablet, el ancho pasa a ser el eje limitante y el alto se deriva
     proporcionalmente, sin invadir la columna de texto (verificado con
     Playwright a 1024x900); el caso de portatil bajo ya corregido
     (1440x760) tambien se reverifico sin cambios de comportamiento. */
  width: auto;
  height: auto;
  max-width: min(767px, 100%);
  max-height: min(868px, 100%);
  aspect-ratio: 767 / 868;
  /* Historial 2026-07-10: sin justify-self, un ancho definido (via
     aspect-ratio) menor que el de la columna de grid se alinea por
     defecto al INICIO (stretch se comporta como start cuando el tamano
     ya es definido) - deja un hueco vacio a la derecha, dentro de la
     propia columna, de tamano completo/variable. El cliente pidio luego
     que el boton "Ver todos los proyectos" compartiera ese margen, pero
     reducido un 20% (el hueco completo quedaba demasiado grande) - para
     que la imagen tenga EXACTAMENTE el mismo margen que el boton (no
     solo uno parecido), se ancla con justify-self:end (sin hueco propio)
     y se reintroduce el mismo margen ya reducido via
     --project-media-right-gap (main.js lo mide y expone; ver esa nota
     para el porque no se puede medir la posicion actual de la imagen,
     solo su ancho, o se crearia un bucle inestable). */
  justify-self: end;
  margin-right: var(--project-media-right-gap, 0px);
  transform: translateY(calc((var(--item-index, 0) - var(--project-track-progress, 0)) * (100% + 15px)));
  will-change: transform;
}

.project-showcase__list .project-entry__media img {
  object-fit: cover;
}

.project-showcase__list .project-entry[data-project-index="0"] .project-entry__media { --item-index: 0; }
.project-showcase__list .project-entry[data-project-index="1"] .project-entry__media { --item-index: 1; }
.project-showcase__list .project-entry[data-project-index="2"] .project-entry__media { --item-index: 2; }
.project-showcase__list .project-entry[data-project-index="3"] .project-entry__media { --item-index: 3; }

/* Bug reportado por el cliente 2026-07-28 (portado desde el prototipo tras
   su aprobacion, auditoria responsive): en resoluciones de portatil
   (pantalla mas baja que un monitor de escritorio externo, tipicamente
   768-900px de alto) la descripcion de cada proyecto se cortaba a mitad
   de frase. El panel es de alto fijo (100vh menos el header,
   overflow:hidden - efecto pedido explicitamente por el cliente tambien
   en tablet, ver "Historial 2026-06-29" mas arriba), y
   titulo+categoria+descripcion no siempre caben en esa altura cuando el
   viewport es mas bajo que un monitor de escritorio tipico. Confirmado
   con Playwright a 1366x768 (portatil comun): la descripcion de "Parque
   Warner Madrid" se cortaba 245px por debajo del borde del panel, a
   mitad de la palabra "Nuestra". Se reduce el espacio decorativo fijo
   (separacion entre el borde superior del panel y el titulo, hueco
   reservado bajo la descripcion) y el interlineado para recuperar altura,
   SIN tocar el tamano de fuente de 18px (pedido explicito del cliente el
   2026-07-26).

   Ajustado 2026-07-29 tras desplegar en test.foozgeneration.com: los
   valores calibrados contra el prototipo estatico (margin-top:24px,
   padding-bottom:16px, line-height:1.4) daban overflow 0 exacto ahi, sin
   ningun margen de seguridad - en el tema WordPress real, con las fuentes
   cargadas via @font-face del propio tema (metricas ligeramente distintas
   a como las interpretaba el prototipo), "Parque Warner Madrid" seguia
   cortandose ~37px a 1366x768 pese al fix. Endurecido a valores con
   margen real (verificado con Playwright directamente contra el sitio
   desplegado, no solo el prototipo): overflow 0 con margen en
   1280x768/1366x768/1440x900. Por debajo de eso (ej. 1280x700, ventana no
   maximizada) puede volver a rozar el limite - no cubierto, fuera del
   rango de portatiles reales que se pidio soportar. */
@media (max-height: 900px) {
  #home-proyectos .project-showcase__list .project-entry__heading-window {
    margin-top: 4px;
  }

  #home-proyectos .project-entry__description {
    padding-bottom: 0px;
    line-height: 1.2;
  }
}

/* Mismo bug, causa adicional en este rango de ancho: la columna de texto
   (proporcion 1:1.9 con la imagen, pensada para escritorio ancho) se
   estrecha por debajo de lo que Figma asume (410px), asi que el mismo
   parrafo envuelve en muchas mas lineas y necesita mas altura de la que
   el ajuste de arriba por si solo puede recuperar - confirmado con
   Playwright a 1024x768: incluso con el espaciado ya reducido, "Parque
   Warner Madrid" seguia cortandose ~245px (columna de texto medida en
   ~222px reales). Se le da mas proporcion al texto (imagen mas pequeña)
   solo en este rango de ancho - decision confirmada con el usuario
   2026-07-28 (prioriza que el texto nunca se corte sobre mantener el
   tamano de imagen de escritorio en tablet). Escritorio (>=1280px)
   mantiene la proporcion e imagen exactas de Figma, sin cambios.
   updateProjectMediaGap en main.js ya no asume esta proporcion (lee
   getComputedStyle().gridTemplateColumns en tiempo real) - no hace falta
   tocar el JS al cambiar este ratio. */
@media (min-width: 861px) and (max-width: 1279px) {
  #home-proyectos .project-entry {
    grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  }
}

/* Fallback documentado (mobile real <=860px y prefers-reduced-motion): se
   desactiva el pin/scrub por completo y se vuelve al layout en flujo
   normal - mismas reglas de #home-proyectos de mas arriba, sin pin, sin
   crossfade, sin transform. El JS de main.js tampoco ejecuta el calculo
   de progreso en estos casos.

   Historial 2026-06-29: este umbral estuvo ampliado a <=1279px (tablet
   incluida) por el mismo motivo que CR-02 - el scroll-jacking se nota
   brusco en tactil. El mismo dia, el usuario pidio explicitamente lo
   contrario: quiere el mismo efecto de panel fijo/crossfade en tablet que
   en escritorio. Revertido a <=860px (solo movil real) - en tablet,
   Proyectos vuelve a comportarse exactamente igual que en escritorio, sin
   overrides propios (ver nota de mas arriba sobre los ajustes de
   espaciado retirados). */
@media (prefers-reduced-motion: reduce), (max-width: 860px) {
  .project-showcase { height: auto; }
  .project-showcase__pin {
    position: relative;
    top: auto;
    height: auto;
    overflow: visible;
  }
  .project-showcase__list { flex: none; min-height: 0; margin-left: 0; }
  /* Bug encontrado 2026-07-13 (auditoria de version movil): grid-template-rows:100%
     (regla base de escritorio, linea ~232 mas arriba - fuerza la unica
     fila explicita a ocupar el 100% de .project-entry para que el
     max-height:100% de la imagen funcione dentro del panel de altura
     fija) no se reseteaba aqui. En movil .project-entry vuelve a
     alto automatico (inset:auto), asi que ese 100% se resolvia contra
     un alto indefinido - la fila implicita donde cae la imagen (2a fila,
     fuera de la unica pista explicita) colapsaba a 0, y con ella la
     imagen entera. grid-template-rows:none restaura el dimensionado
     automatico normal (cada fila mide su propio contenido). */
  /* Bug encontrado 2026-07-24 (portado desde prototype/css/components.css,
     reportado por el cliente: "no funcionan los hipervinculos de los
     proyectos que estan en el home, son 4"): la regla base de escritorio
     (mas arriba, #home-proyectos .project-showcase__list .project-entry)
     pone pointer-events:none y solo lo libera vía .is-active (clase que
     pone el JS del pin/scroll-progress). Ese JS no se ejecuta en
     movil/reduced-motion (ver nota de mas arriba), asi que ningun
     .project-entry llega a tener .is-active nunca - los 4 enlaces
     (titulo e imagen de cada proyecto) quedaban permanentemente
     inaccesibles al toque. Se restaura aqui junto con el resto de
     propiedades que ya reseteaba este mismo bloque. */
  #home-proyectos .project-showcase__list .project-entry {
    position: relative;
    inset: auto;
    padding-block: 24px;
    grid-template-rows: none;
    pointer-events: auto;
  }
  /* Bug encontrado 2026-07-13 (auditoria de version movil): en el HTML el
     bloque de texto (titulo/categoria/descripcion) va ANTES que
     .project-entry__media - en escritorio no se nota porque son 2
     columnas lado a lado (el orden del DOM no afecta el orden visual),
     pero al apilarse en 1 sola columna en movil (ver @media generico mas
     abajo) el orden del DOM SI pasa a ser el orden visual, e invertia el
     de Figma (imagen primero, texto despues). order:-1 en la imagen la
     adelanta visualmente sin tocar el HTML. */
  #home-proyectos .project-showcase__list .project-entry__media {
    order: -1;
  }
  #home-proyectos .project-showcase__list .project-entry__title {
    border-top: 1px solid var(--color-text-muted);
    /* 40px (antes 115px, valor pensado para el modo panel de escritorio
       donde el titulo no va debajo de la imagen sino en su propia
       columna): gap real medido en Figma (Home Mobile, nodos 165:90/165:91
       y equivalentes) entre el borde inferior de la imagen y esta linea
       divisoria, ~40-49px segun el proyecto - nunca antes visible en
       movil real porque .project-entry__media medía 0 de alto (ver bug
       de mas arriba, ya corregido).
       Peticion cliente 2026-07-27: reducir a la mitad (40px -> 20px) el
       hueco entre la imagen y el texto de cada proyecto, solo en movil. */
    margin-top: 20px;
    padding-top: 11px;
  }
  .project-showcase__list .project-entry__description { opacity: 1; }
  /* Peticion del usuario 2026-07-25: mismo espaciado que en el rango
     861-1279px de mas abajo, extendido aqui porque tablets reales en
     vertical (iPad Mini/Air/Pro 11, Galaxy Tab S7: ~744-834px de ancho)
     caen en este breakpoint (<=860px), no en el de 861px+ - se habian
     calibrado los arreglos de tablet anteriores asumiendo ~1024px como
     ancho tipico, sin contrastar con anchos reales de dispositivo. */
  #home-proyectos .project-entry__description {
    margin-top: 20px;
  }
  /* Persiana de titulo+categoria (regla base de escritorio, mas arriba):
     sin pin ni progreso continuo en movil/reduced-motion, se vuelve a
     flujo normal - ventana sin recortar y sin desplazamiento.
     Bug encontrado 2026-07-27 (al revisar la peticion de reducir a la
     mitad el hueco entre imagen y texto): el margin-top:115px de la
     regla base de escritorio (mas arriba, pensado unicamente para
     separar la "ventana" fija de persiana del borde superior del panel
     absoluto de escritorio - ver su propio comentario extenso) no se
     reseteaba aqui igual que overflow/height. En movil el panel ya no
     es absoluto y la ventana ya no desliza nada (sin sentido mantenerlo
     "separado" del resto) - ese margin se sumaba sin motivo al hueco
     real entre imagen y texto (medido con Playwright: 159px en vez de
     los ~44px que corresponden). Se resetea aqui a 0 igual que el resto
     de propiedades de este bloque.
     La regla de escritorio que se resetea lleva el prefijo
     #home-proyectos (mayor especificidad, ID + 2 clases) - hay que
     igualarlo aqui o esta regla pierde el empate de especificidad pese
     a venir despues en el archivo (verificado con Playwright: sin el
     prefijo, margin-top seguia calculando 115px pese a este reset). */
  #home-proyectos .project-showcase__list .project-entry__heading-window {
    overflow: visible;
    height: auto;
    margin-top: 0;
  }
  .project-showcase__list .project-entry__heading {
    transform: none;
  }
  /* Aqui .project-entry vuelve a flujo normal (position:relative, alto
     automatico segun contenido) - la fila YA NO tiene una altura externa
     fija que darle a la imagen (a diferencia del modo panel/showcase de
     escritorio), asi que height:100% no serviria (se resuelve como auto
     contra un contenedor de alto indefinido, y la imagen perderia su
     proporcion consistente entre las 4 fichas). Se restaura
     aspect-ratio:9/10 (igual que la regla base compartida con
     proyectos.html) para que el alto vuelva a derivarse del ancho, como
     antes de la correccion de mas arriba (esa correccion es especifica
     del modo panel de escritorio, con altura de fila fija). */
  /* Bug encontrado 2026-07-13 (auditoria de version movil): justify-self:end
     y margin-right:var(--project-media-right-gap) (regla base, pensada
     para el hueco fijo junto al boton en el modo panel de escritorio) no
     se reseteaban aqui. Con .project-entry en 1 sola columna en movil
     (ver @media mas abajo en este archivo, "grid-template-columns:
     minmax(0,1fr)" generico), un grid item con justify-self:end no se
     estira a la anchura de esa columna - se dimensiona por su propio
     contenido, y como ni el div ni su <img> tienen un ancho propio
     (width:auto en la regla base), colapsaba a 0x0 y las 4 imagenes de
     Proyectos desaparecian por completo en movil. */
  .project-showcase__list .project-entry__media {
    align-self: stretch;
    justify-self: stretch;
    width: 100%;
    margin-right: 0;
    aspect-ratio: 9 / 10;
    height: auto;
    /* max-height:100% (regla base) resuelve a 0 aqui: la fila
       (.project-entry) tiene alto automatico segun su contenido, y ese
       contenido incluye esta misma imagen - un porcentaje de altura
       contra un contenedor sin alto definido colapsa a 0 en vez de
       "sin limite". Sin sentido en movil de todas formas (esa regla
       existia solo para no desbordar la fila de ALTURA FIJA del panel
       de escritorio). */
    max-height: none;
    transform: none;
  }

  /* Peticion cliente 2026-07-13: "Proyectos" debe quedar fijo (pegado bajo
     el header) mientras se hace scroll por las 4 fichas apiladas, hasta
     que se pase la ultima - no es el mismo mecanismo que el pin de
     escritorio (ahi TODO el bloque, titulo+imagen, se queda fijo y hace
     crossfade; aqui en movil la lista de fichas SI se desplaza con el
     scroll, solo el titulo se queda). position:sticky en el propio <h2>
     (no en .project-showcase__pin, que se dejo en position:relative/alto
     automatico mas arriba para que las fichas fluyan con su alto real) -
     su contenedor de referencia para el sticky sigue siendo ese mismo pin
     (h2 + lista, sin el boton "Ver todos los proyectos", que es hermano
     FUERA de .project-showcase en el HTML): el titulo se suelta justo
     cuando el alto de ese contenedor se agota, es decir, justo al pasar
     la ultima ficha (DEVCON VI) - ningun calculo extra en JS, es el
     comportamiento nativo de sticky contra un contenedor de alto mayor
     que el propio elemento.
     z-index+background: sin esto, al quedar "pegado", el contenido de
     las fichas que sigue moviendose por debajo (position:relative, sin
     z-index propio) pintaria POR ENCIMA del titulo (orden del DOM, ambos
     en el mismo contexto de apilamiento) en vez de detras. */
  #home-proyectos .project-showcase__pin > .section__heading {
    position: sticky;
    top: var(--header-height);
    z-index: 2;
    background: var(--color-bg);
  }
}

/* ---------- Botones ---------- */
/* Peticion cliente 2026-07-10: el boton ya no se ancha para igualar la
   columna de imagen (formula anterior: calc((100% - 64px) * 1.9/2.9),
   pensada para cuando la imagen SI llenaba toda esa columna) - ahora que
   la imagen tiene un ancho propio via aspect-ratio (767x868, mas
   estrecha que la columna), el boton se ajusta a su propio texto con un
   margen pequeno (padding) via width:fit-content.
   El margen derecho de la imagen (hueco entre su borde derecho y el
   borde derecho del contenedor, ver nota en .project-entry__media) es
   dinamico - depende del alto de imagen resultante, que varia segun el
   alto real de pantalla. Para que el boton tenga "el mismo margen"
   (peticion cliente 2026-07-10) sin hardcodear un valor que solo
   coincidiria a una altura de pantalla concreta, main.js mide ese hueco
   en tiempo real (misma logica que ya mide --project-track-progress) y
   lo expone como --project-media-right-gap en :root; margin-right lo
   usa directamente, con 0 como fallback si JS no llega a ejecutarse
   (movil/reduced-motion, donde el boton ya es width:100% de todas
   formas). margin-left:auto sigue empujando el boton hacia la derecha,
   dejando ese hueco como margin-right explicito antes del borde. */
/* font-size literal: 20px exacto en Figma (1:800, cambio de marca
   2026-06-26 - antes var(--text-body-lg), 16-18px). Solo se usa en
   index.html, seguro hardcodear sin tocar el token compartido (body,
   input/textarea).
   Estilo re-verificado 2026-07-10 contra Figma Dev Mode (nodo 1:798-1:800):
   el boton NO es una barra solida - fondo rgba(94,94,94,0) (el mismo
   --color-button-bg pero con alpha 0, es decir transparente) y borde
   1px solid blanco (--color-text). Antes usaba --color-button-bg como
   fondo solido - corregido a contorno. Sin estado hover definido en
   Figma (mockup estatico): se rellena con --color-button-bg al pasar el
   raton/foco, para mantener affordance sin inventar un color nuevo. */
.btn-bar {
  display: flex;
  align-items: center;
  width: fit-content;
  margin-left: auto;
  margin-right: var(--project-media-right-gap, 0px);
  margin-top: 20px;
  padding: var(--space-sm) var(--space-md);
  background: transparent;
  border: 1px solid var(--color-text);
  font-size: 20px;
  transition: background-color var(--duration-fast) var(--ease-standard);
}

@media (max-width: 860px) {
  .btn-bar { width: 100%; }
}

.btn-bar:hover, .btn-bar:focus-visible { background-color: var(--color-button-bg); }

/* ---------- Servicios: layout de pagina + filas de categoria ---------- */
/* Peticion del cliente (2026-06-26): "Servicios" (titulo de pagina) se
   queda fijo SIEMPRE, en su propia columna a la izquierda, mientras se
   hace scroll por las 3 filas. Antes "Servicios" vivia en una seccion
   .page-hero separada, suelta del resto - ahora es hermano de las filas
   dentro de un mismo grid de 2 columnas (mismo patron ya usado en
   .projects-layout para "Proyectos"), para que su contenedor de sticky
   abarque toda la altura de la pagina, no solo una franja inicial.
   279px = el mismo ancho que antes tenia la columna 1 (vacia/margen) de
   .service-row - se traslada aqui para que "Estrategia" etc. sigan
   empezando exactamente en la misma posicion x que antes (ver nota de
   alineacion mas abajo, en .service-row). */
.services-layout {
  display: grid;
  grid-template-columns: 279px minmax(0, 1fr);
  padding-top: calc(var(--header-height) + var(--space-2xl));
  padding-bottom: var(--space-xl);
}

/* Peticion usuario 2026-07-13: separar .services-layout__rows (columna 2,
   donde viven las 3 .service-row) de la columna de "Servicios" con ~100px
   de aire adicional a la izquierda. clamp(), no un 100px fijo: el valor
   se calcula dinamicamente segun el ancho de viewport (8vw), con 100px
   como techo (pantallas de escritorio normales/anchas) y 24px como piso
   (mismo valor que --space-md, el margen reducido que ya usa el resto del
   sitio por debajo de 860px) - asi no hace falta un breakpoint propio ni
   queda un hueco fijo desproporcionado en moviles estrechos. */
.services-layout__rows {
  padding-left: clamp(24px, 8vw, 100px);
}

/* Correccion 2026-06-26 (señalada por el usuario): al hacer scroll hacia
   abajo, los puntos de cada bloque de servicio (columna de cuerpo) seguian
   subiendo visibles por detras del hueco bajo el header y de "Servicios"
   hasta desaparecer solo al llegar al header (z-index:100) - el cliente
   quiere que desaparezcan antes, justo al llegar a la altura de la linea
   de "Servicios", no que se vean pasar por ahi. `position:fixed` (no
   sticky - un pseudo-elemento de un grid container pasaria a ser el a su
   vez un grid-item, descolocando las 2 columnas) crea una franja opaca de
   ancho completo, pegada al viewport, justo debajo del header y hasta la
   misma altura en la que se fijan "Estrategia"/el titulo de cada bloque
   (header + space-2xl + 87px, ver nota en .service-row > div:first-child)
   - cualquier contenido que suba por detras queda oculto ahi, antes de
   llegar a esa altura, no en el borde del header.

   Corregido otra vez el mismo dia (señalado por el usuario: el titulo de
   fila y el de bloque TAMBIEN se veian pasar por encima de esta franja
   al soltarse su propio sticky y subir en scroll normal hacia el header -
   tenian z-index mayor que la franja, asi que se dibujaban por encima en
   vez de ocultarse). z-index 3: por debajo de "Servicios" (4, que SI debe
   verse siempre, su propia caja vive permanentemente dentro de esta misma
   franja) pero por encima del titulo de fila (2) y de bloque (1), que asi
   quedan ocultos al pasar por aqui en cuanto se sueltan - sin afectar a
   cuando estan correctamente fijados mas abajo (260px desde arriba), fuera
   del alcance de esta franja. */
.services-layout::before {
  content: "";
  position: fixed;
  top: var(--header-height);
  left: 0;
  right: 0;
  height: calc(var(--space-2xl) + 87px);
  background: var(--color-bg);
  z-index: 3;
  pointer-events: none;
}

/* Correccion 2026-06-26 (señalada por el usuario): al hacer scroll, la
   columna del medio (titulo de fila) se veia pasar por encima de
   "Servicios". Sin `background`, el contenedor sticky de "Servicios" es
   transparente - cualquier contenido que pase por detras durante el
   scroll se ve a traves de el. `z-index` mas alto que el resto de niveles
   sticky de la pagina (titulo de fila, titulo de bloque) asegura que,
   aunque algo llegara a coincidir en pantalla, "Servicios" se dibuje
   siempre por encima, nunca al reves. */
/* Peticion cliente 2026-07-16: la imagen compartida (.services-media) debe
   quedar anclada abajo a la izquierda del carrusel, no justo debajo de
   "Servicios" como antes. Este contenedor sticky pasa a ocupar todo el
   alto visible del viewport bajo el header (mismo alto que su `top`
   necesita para llegar hasta el borde inferior real de la pantalla) y a
   ser flex-column: "Servicios" se queda arriba (flujo normal) y
   .services-media se ancla abajo con margin-top:auto (mismo truco que
   .service-entry__media en el carrusel de Home). Solo escritorio/tablet:
   en movil este contenedor vuelve a position:static + alto automatico (ver
   @media max-width:860px mas abajo) porque .services-media esta oculta ahi
   (regla ya existente) - sin ese reset, este alto de pantalla completa
   dejaria un hueco vacio enorme debajo de "Servicios" en movil. */
.services-layout__title {
  align-self: start;
  position: sticky;
  top: calc(var(--header-height) + var(--space-2xl));
  height: calc(100vh - var(--header-height) - var(--space-2xl) * 2);
  display: flex;
  flex-direction: column;
  z-index: 4;
  background: var(--color-bg);
}

/* Peticion cliente 2026-07-14 (correccion sobre el primer intento): el
   componente de imagen del carrusel de Servicios de Home (imagen de un
   proyecto por servicio) NO va en la columna del medio (titulo de cada
   fila, "Estrategia"/"Sistemas de Diseño"/"Experiencias y Ejecución"),
   va debajo de "Servicios", en su misma columna sticky de la izquierda
   - una sola imagen compartida para toda la pagina (no 3 independientes,
   una por fila) que va cambiando segun el servicio y el proyecto que
   esten activos en cada momento del scroll (ver JS en main.js,
   updateServiceRowMedia). Estructura en 2 niveles, mismo patron que el
   carrusel de Home pero aplicado aqui: .services-media (caja de tamano
   fijo, position:relative) contiene un .services-media__group por
   servicio (position:absolute+inset:0, crossfade por opacidad segun cual
   de los 3 servicios este activo - equivalente a .service-entry en
   Home), y cada grupo contiene sus 3 imagenes con el MISMO crossfade
   interno que .service-entry__media/img de Home (name distinto,
   .services-media__group img, para no interferir con esas reglas -
   contextos distintos, aqui no hay flex-column que centrar). */
.services-media {
  position: relative;
  margin-top: auto;
  width: 100%;
  max-width: 320px;
  aspect-ratio: 4 / 3;
}

/* Auditoria responsive 2026-07-28 (portado desde el prototipo tras su
   aprobacion): 2 problemas reales de adaptacion encontrados en esta
   imagen.

   1) Se ocultaba (display:none) para cualquier ancho <=860px, el mismo
      umbral que "es un movil". Pero un iPad vertical mide 768px de ancho
      y caia en esa regla igual que un telefono - el mismo dispositivo
      mostraba la imagen en horizontal (1024px, bloque de tablet) y la
      ocultaba por completo en vertical. Bajado a <=600px (por encima del
      ancho de cualquier telefono real en horizontal o vertical, por
      debajo de cualquier tablet real) para que tablet vertical y
      horizontal se comporten igual - deja de ocultarse solo por rotar el
      dispositivo. El layout que la envuelve (.services-layout__title en
      flujo normal, no sticky) ya es identico en todo el rango 601-1279px,
      asi que no hace falta ningun ajuste adicional de posicion.

   2) A la inversa, un telefono grande en horizontal (ej. 932x430, ancho
      por encima de 860px) heredaba el tamano de escritorio/tablet
      (320x240 fijo) en una pantalla de apenas 430px de alto - la imagen
      por si sola ocupaba mas de la mitad del viewport antes de llegar a
      ningun contenido. Se limita por ALTO de viewport (no de ancho, que
      ya es grande en ese caso) para que telefonos en horizontal reciban
      una imagen proporcional a su pantalla, no la misma que un monitor de
      escritorio. */
@media (max-width: 600px) {
  .services-media {
    display: none;
  }
}

@media (max-height: 500px) {
  .services-media {
    max-width: 180px;
  }
}

.services-media__group {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.4s ease;
}

.services-media__group.is-active {
  opacity: 1;
}

.services-media__group img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.4s ease;
}

.services-media__group img.is-active {
  opacity: 1;
}

/* Figma (frame Servicios 1:138): el diseñador piensa la pagina en 3
   columnas verticales reales -- col1 vacia/margen (alineada con "Servicios",
   x=98), col2 titulo+tagline de cada fila (x=351 en las 3 filas, sin
   excepcion) y col3 cuerpo/descripciones (x=640 en las 3 filas). Esa
   columna 1 (279px) ahora la ocupa .services-layout (arriba) en vez de
   estar vacia dentro de .service-row - por eso aqui quedan solo 2
   columnas (289px titulo de fila, 1fr cuerpo) en vez de 3; el resultado
   visual es identico, la columna vacia simplemente cambio de "dueño".
   El gap literal de Figma entre col2 y col3 es 0 (col3 empieza en x=640,
   justo donde acaba col2: 351+289=640, sin hueco). 2026-06-28 (peticion
   del usuario): se separan a proposito mas de lo que marca Figma - usa
   --space-xl (64px) del sistema de tokens en vez de un valor suelto.
   2026-07-13 (peticion del usuario): la columna de titulo de fila
   ("Estrategia"/"Sistemas de Diseño"/"Experiencias y Ejecución") ocupa
   mas espacio horizontal (289px -> 420px) - la lista de items sigue
   mostrandose exactamente igual (una sola columna apilada, sin
   reagrupar), solo cambia el ancho reservado para esta columna. */
.service-row {
  display: grid;
  grid-template-columns: 420px minmax(0, 1fr);
  gap: 0 var(--space-xl);
}

/* Figma (frame Servicios 1:138, confirmado con captura de pantalla
   2026-06-24): NO hay ninguna linea entre filas (ni border-top ni vector) -
   "Diseño Editorial" (fin de fila 1) y "Sistemas de Diseño" (inicio de
   fila 2) no tienen ningun separador visual, solo espacio en blanco.
   Se elimina el border-top que no tenia respaldo en Figma, y el
   padding-block:var(--space-2xl) (96px arriba+abajo = 192px entre filas)
   se sustituye por el espacio real medido en las coordenadas de Figma:
   fila1 termina en y=1311 (max de 1:197/1:198), fila2 empieza en y=1340
   (1:228) -> 29px; fila2 termina en y=2325 (1:227), fila3 empieza en
   y=2367 (1:225) -> 42px. Son 2 valores distintos porque no es un padding
   uniforme, son posiciones absolutas en Figma (no hay Auto Layout aqui). */
.service-row:nth-of-type(2) { margin-top: 29px; }
.service-row:nth-of-type(3) { margin-top: 42px; }

/* Los 2 <div> hijos de .service-row (titulo+tagline, cuerpo) ocupan las
   2 columnas de .service-row (289px / 1fr) - la columna del margen real
   ya no esta aqui, ahora es .services-layout quien la reserva (ver
   arriba), por eso ya no hace falta saltarse una columna 1 vacia. */
.service-row > div:first-child { grid-column: 1; }

/* Peticion del cliente (2026-06-26): la columna de titulo de cada fila de
   Servicios ("Estrategia", "Sistemas de Diseño", "Experiencias y
   Ejecución") se queda fija al hacer scroll mientras la columna de cuerpo
   (a la derecha) se desplaza. `align-self:start` evita que el grid
   estire la caja a la altura de toda la fila; al estar contenida dentro
   de cada `.service-row` (mucho mas alto que el titulo), el sticky se
   suelta solo al llegar al final de esa fila, justo antes de que empiece
   la siguiente - no hace falta JS para limitar el rango.

   Corregido 2 veces el mismo dia, señalado por el usuario: este titulo
   se veia pasar por encima de "Servicios". La 1a correccion (background+
   z-index) no era suficiente porque el problema real no era de capas, era
   de POSICION: con el mismo `top` que "Servicios", "Estrategia" se fijaba
   a la misma altura que el TEXTO "Servicios", por encima de la linea
   decorativa que hay debajo de el (.page-hero__title--md::after, 339px de
   ancho, que llega justo hasta la "a" de "Estrategia" - por eso esta
   columna SI cae dentro de su alcance horizontal). El `top` ahora suma
   70px (distancia real en Figma entre el borde superior del texto
   "Servicios" y esa linea, y=233-163) + 1px (la propia linea) + 16px de
   aire, para que "Estrategia" se fije SIEMPRE por debajo de la linea, no
   a la misma altura que "Servicios". El titulo de bloque de servicio
   (mas adentro, fuera del alcance de esa linea) se alinea con este mismo
   valor en vez de con "Servicios" directamente. */
.service-row > div:first-child {
  align-self: start;
  position: sticky;
  top: calc(var(--header-height) + var(--space-2xl) + 87px);
  z-index: 2;
  background: var(--color-bg);
}

/* Peticion cliente 2026-07-16: al pasar de una categoria a otra
   (Estrategia/Sistemas de Diseño/Experiencia y Ejecución), el titulo de
   fila NO debe verse deslizarse hacia arriba al soltar su sticky - solo el
   de la categoria activa debe estar visible, apareciendo directamente en
   vez de a traves del scroll normal. Solo este titulo cambia asi: la
   columna de cuerpo (.service-row > div:last-child, con sus
   .service-group y el sticky anidado de cada uno) se queda exactamente
   igual, sin tocar (peticion explicita del cliente). display:none/block
   (sin transition) saca el bloque del todo mientras esta inactivo - por
   eso no se ve "pasar" scrolleando, solo aparece cuando su clase is-active
   se activa. El indice activo ya lo calculaba updateServiceRowMedia en
   main.js para la imagen compartida (.services-media__group) - se
   reutiliza el mismo calculo para alternar tambien esta clase, sin logica
   nueva de deteccion de scroll.
   Solo desktop/tablet (>860px): en movil este titulo sigue el patron
   original de lista apilada con sticky por fila (ver @media
   max-width:860px mas abajo), sin tocar - el cliente no ha pedido cambiar
   el comportamiento movil, solo el "carrusel" de escritorio. */
@media (min-width: 861px) {
  .service-row__title-block {
    display: none;
  }

  .service-row__title-block.is-active {
    display: block;
  }
}
/* Figma (1:198/1:197, 1:228/1:227, 1:225/1:224, las 3 filas con el mismo
   patron exacto): la columna de cuerpo NO empieza a la misma altura que la
   columna de titulo - empieza 32px mas abajo (titulo y=283/1340/2367,
   linea decorativa del primer grupo y=315/1372/2399 -> offset +32px en
   los 3 casos). Sin este margen, la grid alineaba ambas columnas al mismo
   borde superior y la linea quedaba mas alta de lo que marca Figma.

   2026-07-13: se probo repartir los .service-group en 2 columnas para
   llenar el hueco a la derecha, pero el usuario pidio revertirlo - los
   items de cada bloque de servicio deben seguir mostrandose exactamente
   como antes (lista apilada de grupos, uno debajo de otro). El ancho se
   soluciona ensanchando las columnas (ver .service-row y los tamanos de
   fuente de mas abajo), no cambiando como se agrupa el contenido. */
.service-row > div:last-child { grid-column: 2; margin-top: 32px; }

/* Figma (Servicios, nodos 1:198/1:228/1:225): titulo de fila 48px exacto,
   Georgia Regular, blanco, line-height normal. Re-auditado a fondo
   2026-06-24 (mismo patron que SRV-002 en el titulo de pagina):
   - font-weight: <h2> tiene bold por defecto del navegador (base.css solo
     resetea margin) y Figma pide Regular - no se habia corregido aqui.
   - line-height: heredaba 1.5 de body (Figma pide "normal", ~1.15-1.2
     para Georgia) - con titulos de 2 lineas ("Sistemas"/"de Diseño",
     "Experiencias"/"y Ejecución") esto separaba las 2 lineas mucho mas de
     lo que marca Figma (72px de alto de linea en vez de ~55px).
   El token --text-display-lg (recalibrado en HT-01, llega a 72px exacto a
   1280px) no sirve aqui porque su target es 72px, no 48px; el color soft
   (gris) tampoco coincide con Figma (blanco). Compartido por las 3 filas
   de Servicios (mismo componente repetido, no contenido distinto). */
.service-row__title {
  font-family: var(--font-display);
  font-size: 48px;
  font-weight: normal;
  line-height: normal;
  color: var(--color-text);
}

/* Figma (mismos nodos): tagline 16px exacto, color #5e5e5e
   (--color-text-muted) - el gris soft actual no coincide. line-height
   normal añadido 2026-06-24 (heredaba 1.5 de body; font-weight no hacia
   falta - los <p> ya son normal por defecto, a diferencia de los <h2>). */
.service-row__tagline {
  margin-top: var(--space-sm);
  color: var(--color-text-muted);
  font-size: 16px;
  line-height: normal;
}

/* Figma (nodos 1:197/1:227/1:224): entre el ultimo item de un grupo y el
   titulo del siguiente hay 2 lineas en blanco (una a 16px sans, otra a
   32px sans -ambas con leading-normal, no Georgia-) en vez de 1 sola.
   Gap inferido ~58px (19px + 38px de interlineado), no 40px (--space-lg).
   Valor inferido a partir de lineas en blanco del texto exportado, no de
   una propiedad "gap" literal (Figma no usa Auto Layout aqui) - pendiente
   de confirmacion visual. */
.service-group + .service-group { margin-top: 58px; }

/* Figma (vectores 1:188/1:191/1:194, confirmados visualmente vía
   captura de pantalla del frame Servicios): linea fina sobre el primer
   grupo de cada fila, ancho fijo 495px, ~33px por encima del texto
   (consistente en las 3 filas, no varia). Color inferido del mismo
   convenio de hairlines ya usado en este proyecto (var(--color-text-muted)
   - ver HOME-PROJECTS-LINE-01 y la linea bajo "Servicios", SRV-004); los
   vectores se exportaron como imagen plana sin color explicito disponible
   vía MCP - pendiente de confirmacion visual del tono exacto. */
.service-row > div:last-child .service-group:first-child {
  position: relative;
  padding-top: 33px;
}

/* Bug encontrado 2026-07-28 (portado desde el prototipo tras su
   aprobacion, auditoria responsive): 495px es fijo independientemente del
   ancho real de la columna de cuerpo, que se estrecha por debajo de 495px
   en portatiles (~1366px de viewport, columna ~288px) - la linea se
   salia de su contenedor y provocaba scroll horizontal en toda la pagina
   (confirmado con Playwright: scrollWidth 979px vs 772px de .service-row
   a 1366x768). min() mantiene los 495px exactos de Figma en pantallas
   anchas (columna >=495px) y la limita al 100% de la columna cuando esta
   es mas estrecha, sin overflow. */
.service-row > div:last-child .service-group:first-child::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: min(495px, 100%);
  height: 1px;
  background: var(--color-text-muted);
}

/* Figma (nodos 1:197/1:227/1:224): titulo de grupo Georgia Regular (no
   bold), 32px exacto, blanco, line-height normal. Aunque --text-heading ya
   llega a 32px exacto a 1280px tras la recalibracion de HT-01, se
   hardcodea igualmente aqui porque el target SI coincide con el token
   compartido (no hace falta un escalon distinto como en
   .service-row__title) - hardcodear evita que un cambio futuro del token
   global desalinee Servicios sin darse cuenta. La familia sans y el peso
   600 anteriores no coincidian con Figma (Georgia + Regular) y eso si era
   un error real, no de clamp. line-height:normal añadido 2026-06-24
   (heredaba 1.5 de body). */
/* 2026-07-13 (peticion del usuario): +4px sobre el tamano exacto de
   Figma (32px) para que la columna de cuerpo ocupe mas espacio, sin
   cambiar el orden/agrupacion de la informacion (sigue siendo 1 sola
   columna apilada, ver nota de mas arriba en
   `.service-row > div:last-child`). */
.service-group__title { font-size: 36px; font-family: var(--font-display); font-weight: normal; line-height: normal; }

/* Peticion del cliente (2026-06-26): dentro de la columna de cuerpo, el
   titulo de cada bloque de servicio (ej. "Estrategia de Marca") se queda
   fijo arriba mientras sus propios puntos (`.service-group__items`) pasan
   por debajo; al terminar esos puntos se suelta y el titulo del siguiente
   bloque pasa a fijarse, y asi sucesivamente. Funciona con CSS puro (sin
   JS): cada `.service-group` es su propio contenedor de bloque, así que
   el sticky queda contenido dentro de sus limites y se suelta justo al
   llegar al final de ese grupo. `background`+`z-index` evitan que el
   texto de los puntos se vea pasar visualmente por detras/encima del
   titulo fijo mientras hace scroll. Mismo `top` que el titulo de fila
   (no el de "Servicios" directamente - corregido 2026-06-26, ver nota en
   `.service-row > div:first-child`) para que ambos queden alineados a la
   misma altura, por debajo de la linea decorativa de "Servicios". */
.service-group__title {
  position: sticky;
  top: calc(var(--header-height) + var(--space-2xl) + 87px);
  z-index: 1;
  background: var(--color-bg);
}
/* Figma: descripcion de grupo 16px exacto, blanco, line-height normal -
   el color soft (gris) actual no coincide (Figma no aplica gris a la
   descripcion, solo a los items de la lista). margin-top eliminado
   2026-06-24: en Figma el titulo y la descripcion son lineas adyacentes
   sin parrafo en blanco entre ellas (a diferencia del hueco antes del
   primer item, que si tiene 1 linea en blanco) - el unico espacio es el
   propio interlineado, no un margen extra. */
/* 2026-07-13 (peticion del usuario): +2px sobre el tamano exacto de
   Figma (16px), mismo motivo que .service-group__title de mas arriba. */
.service-group__desc { color: var(--color-text); font-size: 18px; line-height: normal; }

/* Figma: cada item ocupa su propia linea, sin excepcion (lista vertical
   simple, no texto que fluye en chips). display:flex + flex-wrap:wrap
   (eliminado 2026-06-24) permitia que items cortos como "Tono de voz" o
   "Packaging" compartieran linea con el siguiente, en vez de quedar cada
   uno en su propia linea como en Figma. */
.service-group__items {
  margin-top: var(--space-sm);
  display: flex;
  flex-direction: column;
}

/* Figma: items de lista 16px exacto, color #5e5e5e (--color-text-muted),
   line-height normal - el blanco y los 14px de antes no coincidian (color
   y tamano invertidos respecto a la descripcion del grupo); line-height
   añadido 2026-06-24 (heredaba 1.5 de body). */
/* Figma (1:197/1:227/1:224, confirmado con captura de pantalla 2026-06-24):
   los items son texto plano, sin guion/bullet/marcador delante de ninguno.
   Eliminados position:relative + padding-left:1rem (existian solo para
   posicionar el guion) y la regla ::before con content:"—" (no tiene
   respaldo en Figma - se hizo visible en todas las lineas al arreglar
   SRV-021, antes quedaba disimulado por el layout de flex-wrap roto). */
/* 2026-07-13 (peticion del usuario): +2px sobre el tamano exacto de
   Figma (16px), mismo motivo que .service-group__title/__desc de mas
   arriba. */
.service-group__items li {
  color: var(--color-text-muted);
  font-size: 18px;
  line-height: normal;
}

/* ---------- Carrusel de Servicios en Home ---------- */
/* Peticion cliente 2026-07-10: mismo mecanismo que el carrusel de
   Proyectos (.project-showcase/.project-entry, ver mas arriba) - pin
   fijo con "Servicios" siempre visible mientras 3 slides (Estrategia /
   Sistemas de Diseño / Experiencia y Ejecución) hacen crossfade segun el
   scroll. Deliberadamente NO se reutilizan las clases de Proyectos
   (.project-showcase, .project-entry, etc.) ni su bloque de JS: son
   nombres y logica totalmente independientes (.service-showcase,
   .service-entry, updateServiceProgress en main.js) para que un cambio
   futuro en cualquiera de los 2 carruseles no pueda afectar al otro por
   accidente - peticion explicita del cliente de no afectar nada mas.
   Solo se replica el PATRON (sticky pin + track de N*100vh + crossfade
   por scroll), no las clases en si.
   A diferencia de Proyectos (que tiene una columna de imagen con
   translateY continuo), Servicios no tiene imagen - cada slide es solo
   texto, así que basta un crossfade de opacidad por indice activo (sin
   variable de progreso continuo tipo --project-track-progress, que solo
   hacia falta para animar la imagen). */
.service-showcase {
  position: relative;
  height: calc(3 * 100vh);
}

/* Peticion cliente 2026-07-14 (v1): reducir el alto de este pin - a
   calc(100vh-header) quedaba mucho espacio vacio por debajo de la barra
   de progreso (hasta ~236px en un viewport de 900px de alto), porque
   .service-showcase__list solo ocupaba el 50% de esa altura y nada mas
   rellenaba el resto. Primer intento (75vh + lista al 65%) SE RECORTABA
   en viewports bajos (~768px): al ser la lista un % del pin y el pin un
   % del viewport, en pantallas bajas ambos se encogen aunque el TEXTO no
   se encoja con ellos. Segundo intento: pin a height:auto + lista a un
   alto fijo (450px) - sin recortes, pero ya NO se calcula en funcion del
   viewport (siempre el mismo alto, sin importar la pantalla).

   Peticion cliente 2026-07-14 (v2): "sigue habiendo espacio perdido,
   reducir unos 100px - las dimensiones tienen que calcularse en funcion
   del dispositivo". Se combinan las dos ideas con max(): un SUELO fijo
   (589px = la suma EXACTA medida de titulo(72)+su margen(24)+divisor(1)+
   su margen(18)+lista minima(430)+margen superior de la barra(18)+barra
   (2)+margen inferior(24), con los margenes ya recortados, ver mas
   abajo) y una parte basada en 68vh que solo entra en juego (y hace
   crecer el pin) en viewports altos donde 68vh-header supera ese suelo
   (~980px de alto en adelante) - asi vuelve a depender del dispositivo
   (mas alto en monitores grandes) sin arriesgar recortes en portatiles
   normales, donde el suelo manda y el resultado es ~76px mas bajo que el
   intento anterior (665px -> 589px).
   Bug 2026-07-14 (corregido aqui): el primer calculo de este suelo
   (571px) estaba mal sumado - 18px por debajo de lo que realmente
   necesitan los hijos. Con el pin mas bajo que su contenido, flexbox
   encogia lo unico sin min-height que lo protegiera (la barra de
   progreso, min-content practicamente 0) para absorber esos 18px,
   dejandola con 0px de alto - invisible, aunque seguia en el DOM. Ademas
   de corregir el numero, .service-showcase__progress (ver mas abajo)
   gana flex-shrink:0 como cinturon de seguridad, para que este tipo de
   fallo no pueda volver a hacerla desaparecer en silencio. */
.service-showcase__pin {
  position: sticky;
  top: var(--header-height);
  height: max(589px, calc(68vh - var(--header-height)));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Peticion cliente 2026-07-14: margenes recortados (de 40px a 24px) para
   ganar los ~100px pedidos sin tocar el tamano de texto - "Servicios"
   (.section__heading, regla compartida con Proyectos, --space-lg=40px)
   se escopa aqui solo para #home-servicios, igual que ya se hacia con
   .divider un poco mas abajo, sin tocar Proyectos. */
#home-servicios .section__heading {
  margin-bottom: 24px;
}

/* Peticion cliente 2026-07-11: espacio entre el vector (.divider, ver mas
   arriba) y el contenido que viene despues (.service-showcase__list).
   Escopado a #home-servicios en vez de modificar la regla generica
   .divider (compartida potencialmente con otros usos futuros de esa
   clase, que no deberian heredar este espacio especifico de Servicios).
   Reducido de 30px a 18px el 2026-07-14 (ver nota extensa en
   .service-showcase__pin) para recuperar altura sin recortar contenido. */
#home-servicios .divider {
  margin-bottom: 18px;
}

/* Peticion cliente 2026-07-11: reducir el alto de esta lista - antes
   flex:1 (crecia para ocupar todo el espacio restante del pin despues de
   "Servicios"/divider/barra de progreso).
   Peticion cliente 2026-07-14: flex:1 1 auto + min-height:430px (en vez
   del alto fijo de 450px del intento anterior) - vuelve a crecer con el
   espacio disponible del pin (que ahora SI depende del viewport, ver
   .service-showcase__pin), pero sin bajar nunca de 430px, que sigue
   dando margen sobre los 422px reales de "Sistemas de Diseño" (el slide
   con mas sub-items, medido con los 4 items en flujo normal sin
   comprimir) - la combinacion (pin con suelo 571px = header+430+footer,
   lista flex+min-height) garantiza que nunca se recorta, en cualquier
   alto de viewport, y crece en pantallas altas en vez de quedarse fija. */
/* margin-left:100px pedido por el usuario 2026-07-13: mismo margen
   izquierdo que usa el carrusel de Proyectos (.project-showcase__list,
   ver nota de mas arriba), solo version web (escritorio/tablet) - el
   fallback movil/reduced-motion de mas abajo lo resetea a 0 igual que
   hace el de Proyectos. */
.service-showcase__list {
  position: relative;
  flex: 1 1 auto;
  min-height: 430px;
  margin-left: 100px;
}

/* Peticion cliente 2026-07-11: indicar el avance del scroll dentro del
   carrusel de Servicios (3 slides). Se elige una barra de progreso fina
   y continua en vez de puntos discretos (uno por slide): la peticion
   habla de "el avance que hacemos con el scroll", no de "en que slide
   estamos" - una barra que se llena de 0 a 100% representa mejor un
   avance continuo que 3 puntos que solo tienen 3 estados posibles.
   Aislado del resto del sitio: clases y variable CSS exclusivas de este
   carrusel (--service-scroll-progress, ver main.js), no relacionadas con
   --project-track-progress de Proyectos (esa es para animar la imagen,
   esta es solo para el indicador visual).

   Rediseñada 2026-07-28 (peticion cliente, portado desde el prototipo tras
   su aprobacion): la version horizontal (linea fina bajo el contenido, se
   llenaba de izquierda a derecha) "no termina de funcionar" segun el
   cliente - sustituida por una barra VERTICAL en el lado izquierdo del
   carrusel. Movida en el HTML (ver template-parts/home/servicios-carousel.php)
   de hermana de .service-showcase__list a hija suya (primer elemento,
   antes de los 3 .service-entry) para poder anclarla con
   position:absolute al lado izquierdo de esa lista (ya es
   position:relative) sin depender de un alto medido por JS - simplemente
   estira top:0/bottom:0 al alto real de la lista, sea cual sea. Colocada
   dentro del hueco de margin-left:100px de .service-showcase__list (mismo
   margen que usa el carrusel de Proyectos), no debajo del texto.
   aria-hidden en el HTML (ya existente): es un indicador puramente
   visual, redundante con el propio scroll para un lector de pantalla. */
.service-showcase__progress {
  position: absolute;
  left: -52px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--color-border);
  overflow: hidden;
}

.service-showcase__progress-bar {
  width: 100%;
  height: calc(var(--service-scroll-progress, 0) * 100%);
  background: var(--color-text);
}

/* Cada .service-entry pasa de "fila apilada" a "panel superpuesto a
   pantalla completa dentro del pin", igual que .project-entry en
   Proyectos - por eso se anula el border-top/padding-block de
   .service-link-row (pensados para la lista apilada, ver mas abajo), y
   se cruza por opacidad segun la clase .is-active que pone main.js.

   Bug encontrado 2026-07-28 (portado desde el prototipo tras su
   aprobacion, a raiz del reporte de cliente sobre el hover en
   "Experiencia y Ejecucion"): a diferencia de .project-entry en
   Proyectos, aqui faltaba aislar los 3 paneles con pointer-events - los 3
   .service-entry ocupan el mismo inset:0 simultaneamente y, sin
   pointer-events:none en los inactivos, el ultimo del DOM
   (data-service-index="2", "Experiencia y Ejecucion") quedaba SIEMPRE
   encima en el orden de pintado y se comia el hover/clic de los otros 2,
   aunque no fueran el panel visible en ese momento. Esto explicaba 2
   sintomas: (a) el titulo de "Experiencia y Ejecucion" era el UNICO de
   los 3 donde el hover podia verse (los otros 2 tenian su hover
   silenciosamente interceptado por el panel invisible encima, sin efecto
   visible - por eso el cliente solo reporto ese bloque); (b) mas grave,
   cada .service-link-row es un <a href> a una seccion distinta de
   servicios.html - sin este aislamiento, hacer clic en "Estrategia" o
   "Sistemas de Diseño" mientras se mostraban en pantalla en realidad
   activaba el enlace de "Experiencia y Ejecucion" (el que estaba encima),
   llevando siempre a la misma seccion sin importar cual se hubiera
   clicado. Mismo patron ya usado en Proyectos (ver nota identica en este
   archivo sobre #home-proyectos .project-showcase__list .project-entry). */
#home-servicios .service-showcase__list .service-entry {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

#home-servicios .service-showcase__list .service-entry.is-active {
  opacity: 1;
  pointer-events: auto;
}

#home-servicios .service-showcase__list .service-link-row {
  border-top: none;
  padding-block: 0;
  height: 100%;
}

/* Peticion cliente 2026-07-10: cada apartado debe incluir el contenido
   real que el diseñador definio en Figma (nodo 1:687, "Home") para esta
   seccion - 11 textos titulo+descripcion (194:631, 198:721-732) que no
   estaban agrupados dentro del frame "Servicios" (1:734) en Figma, pero
   se posicionan junto a el (x=726-728, misma altura que cada fila) y
   coinciden EXACTAMENTE con el titulo/.service-group__desc ya existentes
   en servicios.html - se copia ese mismo texto para no duplicar contenido
   con variaciones. display:block -> grid (2 columnas: titulo de fila /
   items) SOLO afecta a esta clase, exclusiva de index.html/en/index.html
   (no se usa en servicios.html, que tiene su propio .service-row/
   .service-group sin relacion). border-top/padding-block (fila apilada)
   se anulan dentro del carrusel de escritorio (regla de arriba) y se
   restauran en el fallback movil/reduced-motion (mas abajo), donde
   vuelve a ser una lista apilada normal. */
.service-link-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-xl);
  /* Peticion cliente 2026-07-14 (2a): stretch en vez de start - ambas
     columnas (col1, .service-link-row__items con la descripcion) pasan a
     ocupar la altura completa de la fila, requisito para poder centrar su
     contenido en esa altura (ver .service-link-row__items mas abajo). Con
     align-items:start cada columna medía solo su propio contenido y sus
     centros no coincidian entre si. */
  align-items: stretch;
  padding-block: var(--space-lg);
  border-top: 1px solid var(--color-border);
}

/* Sin :first-of-type aqui (retirado 2026-07-10): desde que cada fila vive
   dentro de su propio .service-entry (para el crossfade del carrusel),
   ":first-of-type" ya no identifica "la primera fila real" - cada
   .service-link-row es hijo unico (por tanto "first-of-type") de un
   .service-entry distinto, asi que matchearia las 3, no solo la primera.
   El caso "sin borde en la primera fila" ahora se resuelve de forma
   explicita en el fallback movil/reduced-motion, con
   .service-entry:first-child .service-link-row (ver mas abajo) - en el
   modo carrusel de escritorio el borde ya se anula siempre (ver regla
   #home-servicios .service-showcase__list .service-link-row de mas
   arriba), asi que ahi no hace falta ningun caso especial. */

/* font-size literal: re-verificado 2026-07-10 contra Figma Dev Mode
   (teaser de Servicios en Home, nodos 1:736/1:738/1:740) - 48px exacto,
   no 72px como decia el comentario anterior (desactualizado). Color
   #989898 (--color-text-soft) ya coincidia. Solo se usa en index.html,
   seguro hardcodear. */
.service-link-row__title {
  font-family: var(--font-display);
  font-size: 48px;
  color: var(--color-text-soft);
  transition: color var(--duration-fast) var(--ease-standard);
}

/* Peticion cliente 2026-07-28 (portado desde el prototipo tras su
   aprobacion): el titulo cambiaba a blanco con el hover - "ese
   comportamiento no es el esperado". Se quita el :hover (el efecto solo
   se veia de forma consistente en "Experiencia y Ejecucion" por un bug de
   capas ya corregido arriba - #home-servicios .service-showcase__list
   .service-entry - que hacia que ese panel, ultimo del DOM, se comiera el
   hover/clic de los otros 2 sin importar cual estuviera activo; con las
   capas ya aisladas el efecto se hubiera visto igual en los 3, pero el
   cliente no lo quiere en ninguno). Se conserva :focus-visible (no
   reportado, accesibilidad de teclado: quien navega con Tab necesita ver
   donde esta el foco). */
.service-link-row:focus-visible .service-link-row__title { color: var(--color-text); }

/* Peticion usuario 2026-07-13: 20px de interlineado entre cada item
   (titulo+descripcion) apilado dentro de esta columna - antes sin gap
   propio (ver historial Figma en el comentario retirado), la separacion
   salia solo del line-height natural de las 2 lineas de cada item. */
.service-link-row__items {
  display: flex;
  flex-direction: column;
  gap: 20px;
  /* Peticion cliente 2026-07-14 (2a): centra el bloque de items en la
     altura completa de la columna (ahora stretch, ver .service-link-row). */
  justify-content: center;
}

/* Figma: titulo de item Lora Regular 32px, color #989898 (--color-text-soft,
   igual que el titulo de fila) - a diferencia de .service-group__title en
   servicios.html (blanco): esta es una version condensada/teaser del
   mismo contenido, con jerarquia visual mas discreta. Clase nueva y
   exclusiva de este teaser para no alterar .service-group__title (usada
   en servicios.html). */
.service-link-row__item-title {
  font-family: var(--font-display);
  font-weight: normal;
  font-size: 32px;
  line-height: normal;
  color: var(--color-text-soft);
}

/* Figma: descripcion de item Libre Franklin Regular 16px, mismo color
   #989898 que el titulo (a diferencia de .service-group__desc en
   servicios.html, que es blanco) - clase nueva, no reutiliza esa. */
.service-link-row__item-desc {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: normal;
  color: var(--color-text-soft);
  margin-top: 0;
}

/* Peticion cliente 2026-07-14: nuevo componente a la izquierda de cada
   slide del carrusel de Servicios (solo escritorio/tablet), inspirado en
   koto.com (div "bottom-0 left-0 w-full md:absolute md:max-w-sidebar-wide"
   de su carrusel de estudios) - una imagen anclada a la esquina inferior
   izquierda del slide. Se traslada el PATRON (imagen anclada abajo a la
   izquierda, ancho acotado) sin copiar el aspecto de koto (esquinas
   redondeadas): el resto del sitio no usa border-radius en ninguna
   imagen, mantenerlo sin redondear es consistente con esa convencion ya
   establecida.

   Correccion 2026-07-14 sobre el primer intento: la imagen se habia
   colocado compartiendo celda con el titulo via grid-column/grid-row en
   .service-link-row (grid de 2 columnas, titulo/items) - eso descuadraba
   el carrusel entero, porque .service-link-row usa align-items:start
   (los hijos NO se estiran a la altura de la fila, solo se alinean
   arriba): al compartir celda el titulo y la imagen quedaban los dos
   pegados arriba (la imagen ENCIMA del titulo, no debajo) en vez de
   quedar cada uno en su sitio, y el intento de forzar la imagen abajo
   con align-self:end no tenia una celda con alto resuelto contra la que
   anclarse de forma fiable.
   Solucion: .service-link-row vuelve a tener exactamente 2 hijos directos
   (como antes de este cambio) - .service-link-row__col1 (titulo + imagen)
   y .service-link-row__items (sin tocar) - así las 2 columnas del grid
   siguen siendo las mismas de siempre. Dentro de la primera columna,
   .service-link-row__col1 es un flex column con height:100% (resuelve
   bien: el grid contenedor SI tiene alto definido via su propio
   height:100%, y con un unico row track sin dimensionar explicitamente
   ese alto se reparte completo a la fila por defecto - align-items:start
   solo afecta a cada ITEM dentro de su celda, no al alto de la celda en
   si) - el titulo queda arriba (flujo normal) y la imagen se ancla abajo
   con margin-top:auto, sin salir nunca de la propia columna 1. Imagenes
   elegidas por afinidad tematica con cada pilar (mismos assets ya usados
   en el teaser de Proyectos de Home, sin descargar nada nuevo): Estrategia
   -> 108 Wanderlust (rebranding), Sistemas de Diseño -> Parque Warner
   Madrid (sistema de identidad extenso), Experiencia y Ejecución -> DEVCON
   VI (diseño de evento). */
.service-link-row__col1 {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* ---------- Reveal-on-scroll de Servicios en Home (solo escritorio/tablet) ---------- */
/* Peticion cliente 2026-07-30 (portado desde el prototipo): sustituye el
   pin+crossfade+barra de progreso vertical (bloque de arriba, "Carrusel
   de Servicios en Home") por un efecto de parallax vertical - cada uno
   de los 3 servicios se revela de forma progresiva (fade + deslizamiento
   hacia arriba) segun entra en el viewport, sin scroll-jacking (el pin
   deja de fijarse con position:sticky) y sin la linea de progreso
   vertical, que el cliente pidio quitar explicitamente. Peticion escopada
   a "version desktop" - el carrusel de movil real (<=860px, ver @media
   (max-width:860px) e isMobileViewport en main.js mas abajo/arriba) NO se
   toca, sigue con el mismo pin+crossfade+barra que ya tenia.
   Los 3 .service-entry pasan de "paneles superpuestos" (position:absolute
   inset:0, ver regla base mas arriba) a "filas apiladas en flujo normal"
   - mismo patron visual que el fallback de prefers-reduced-motion de
   justo aqui debajo (border-top+padding-block en .service-link-row, sin
   borde en la primera fila), con el que comparte selectores para no
   duplicar layout - la unica diferencia es que aqui la opacidad/posicion
   de cada fila arrancan en 0/translateY(56px) y pasan a 1/translateY(0)
   con una clase .is-revealed que anade main.js via IntersectionObserver
   segun cada fila entra en el viewport (ver "Reveal-on-scroll de
   Servicios en Home" en main.js), en vez de opacity:1 fijo.
   Este bloque debe ir ANTES (en el CSS) que el fallback de
   prefers-reduced-motion de justo abajo: mismos selectores/especificidad
   en ambos, así que si un usuario de escritorio tiene reduced-motion
   activado necesitamos que gane ESE bloque (opacity:1 sin transicion),
   no este - el orden en la cascada resuelve ese empate a favor del que
   aparece despues en el archivo.

   Peticion cliente 2026-07-30 (2a, mismo dia): ademas de lo anterior,
   "Servicios" tiene que quedarse fijo arriba mientras la lista se oculta
   detras al hacer scroll - ver #home-servicios .service-showcase__title
   mas abajo (nuevo wrapper del <h2>+<div class="divider">, sticky con
   z-index+background). Esa regla vive solo en este @media (no se aplica
   a movil real, isMobileViewport en main.js) pero SI se aplica con
   prefers-reduced-motion activado (sticky no es una animacion) - lo unico
   que desactiva el fallback de reduced-motion de mas abajo es el
   fade+deslizamiento de cada .service-entry (opacity/transform), no la
   posicion fija del titulo. */
@media (min-width: 861px) {
  .service-showcase {
    height: auto;
  }

  .service-showcase__pin {
    position: relative;
    top: auto;
    height: auto;
    overflow: visible;
  }

  /* Peticion cliente 2026-07-30 (2a, sobre este mismo cambio): "Servicios"
     tiene que quedarse fijo arriba mientras el resto de la seccion se
     oculta por debajo al hacer scroll - no basta con que deje de
     moverse, tiene que TAPAR el contenido que pasa detras (igual patron
     ya usado en el sitio para este mismo efecto: #home-proyectos
     .project-showcase__pin > .section__heading en movil, mas arriba, y
     .services-layout__title en la pagina Servicios) en vez de quedar
     transparente. .service-showcase__title (nuevo wrapper en el HTML,
     hermano de .service-showcase__list dentro del pin, agrupa el <h2> y
     el <div class="divider">) es lo que se fija, no todo el pin - el pin
     ya esta en flujo normal (regla de arriba), asi que el propio alto de
     .service-showcase__list determina cuanto scroll dura el efecto de
     forma nativa, sin ningun calculo de distancia en JS.
     z-index+background: sin esto el titulo se queda pegado pero
     transparente, y el contenido de la lista (position:relative, mismo
     nivel de apilamiento) se dibujaria por encima en vez de detras. */
  #home-servicios .service-showcase__title {
    position: sticky;
    top: var(--header-height);
    z-index: 2;
    background: var(--color-bg);
  }

  .service-showcase__progress {
    display: none;
  }

  .service-showcase__list {
    min-height: 0;
  }

  #home-servicios .service-showcase__list .service-entry {
    position: relative;
    inset: auto;
    pointer-events: auto;
    opacity: 0;
    transform: translateY(56px);
    transition: opacity 0.9s cubic-bezier(0.16, 1, 0.3, 1), transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
  }

  #home-servicios .service-showcase__list .service-entry.is-revealed {
    opacity: 1;
    transform: translateY(0);
  }

  #home-servicios .service-showcase__list .service-link-row {
    border-top: 1px solid var(--color-border);
    padding-block: var(--space-lg);
    height: auto;
  }

  /* Peticion cliente 2026-07-30: quitar la linea separadora que queda
     ENCIMA de "Estrategia" (la primera fila) - las demas (encima de
     "Sistemas de Diseño" y "Experiencia y Ejecucion") se mantienen.
     Bug real encontrado al implementar esto: :first-child (usado aqui
     hasta ahora) nunca llegaba a matchear ningun .service-entry, porque
     el hijo REAL mas temprano de .service-showcase__list es
     .service-showcase__progress (la barra de progreso, ver arriba en
     este mismo @media - display:none aqui pero sigue presente en el DOM,
     :first-child no distingue por display) - "Estrategia" (el primer
     .service-entry) es en realidad el SEGUNDO hijo del contenedor, nunca
     el primero, asi que el border-top:none de mas abajo jamas se aplicaba
     y las 3 filas mostraban su linea superior, incluida la de encima de
     "Estrategia". :first-of-type SI funciona aqui: cuenta solo entre
     hermanos del MISMO tag (<article>), ignorando el <div> de la barra de
     progreso que lo precede - selecciona igual el primer .service-entry
     real ("Estrategia") sin ese falso negativo.
     Nota aparte, no contradice el comentario de mas arriba ("Sin
     :first-of-type aqui... service-link-row.first-of-type matchearia las
     3"): ese caso aplicaba :first-of-type a .service-link-row (hijo unico
     de cada .service-entry, por tanto SIEMPRE first-of-type de las 3)
     - aqui se aplica a .service-entry mismo, que si tiene hermanos reales
     del mismo tag para distinguir. */
  #home-servicios .service-showcase__list .service-entry:first-of-type .service-link-row {
    border-top: none;
  }
}

/* Fallback documentado (solo prefers-reduced-motion, CUALQUIER ancho): se
   desactiva el pin/scrub del carrusel de Servicios y vuelve al layout en
   flujo normal (lista apilada de las 3 categorias, con sus sub-items
   completos, sin crossfade). El JS de main.js tampoco calcula el indice
   activo en este caso (guard !prefersReducedMotion en main.js).

   Peticion cliente 2026-07-13: a diferencia de este caso (accesibilidad,
   cualquier ancho), el carrusel de Servicios en MOVIL REAL (<=860px) debe
   comportarse igual que en escritorio/tablet - mismo pin+crossfade+barra
   de progreso, solo con estilos distintos (ver el @media (max-width:860px)
   de mas abajo, que ya NO desactiva el pin, solo ajusta margen y oculta
   los sub-items). Por eso este bloque ya no incluye "(max-width:860px)"
   en la media query (antes lo hacia, ver historial mas abajo).

   Bug encontrado 2026-07-13 (auditoria de version movil): este bloque
   vivia ANTES de las reglas base de arriba (.service-showcase,
   #home-servicios .service-showcase__list .service-entry, etc.) - con
   igual especificidad en ambos lados, ganaba la regla que aparece
   DESPUES en el CSS sin importar la media query, asi que en movil real
   se aplicaban las reglas de escritorio (pin sticky, opacity:0,
   position:absolute) en vez de este fallback, dejando la lista de
   Servicios invisible. Movido a este punto (justo despues de las reglas
   base que sobreescribe) para que la cascada resuelva correctamente. */
@media (prefers-reduced-motion: reduce) {
  .service-showcase { height: auto; }
  .service-showcase__pin {
    position: relative;
    top: auto;
    height: auto;
    overflow: visible;
  }
  .service-showcase__list { flex: none; min-height: 0; margin-left: 0; }
  /* pointer-events:auto restaurado 2026-07-28 (portado desde el prototipo
     tras su aprobacion): mismo bug ya conocido y corregido en Proyectos
     (ver #home-proyectos .project-showcase__list .project-entry mas
     arriba, "no funcionan los hipervinculos") - la regla base pone
     pointer-events:none y solo lo libera via .is-active, pero ese JS no
     corre con reduced-motion, asi que sin este reset los 3 enlaces de
     Servicios quedarian permanentemente inaccesibles al tacto/clic para
     quien tenga esta preferencia activada.
     transform:none añadido 2026-07-30 (portado desde el prototipo): el
     nuevo reveal-on-scroll de escritorio/tablet (ver @media
     (min-width:861px) mas arriba) arranca cada .service-entry en
     translateY(56px) antes de revelarse - sin este reset, un usuario de
     escritorio con reduced-motion activado (JS no añade .is-revealed en
     ese caso, ver guard !prefersReducedMotion en main.js) veria las 3
     filas permanentemente desplazadas 56px hacia abajo aunque opacity:1
     las de por visibles. */
  #home-servicios .service-showcase__list .service-entry {
    position: relative;
    inset: auto;
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }
  #home-servicios .service-showcase__list .service-link-row {
    border-top: 1px solid var(--color-border);
    padding-block: var(--space-lg);
    height: auto;
  }
  /* :first-of-type (2026-07-30, mismo motivo que el bloque de reveal-on-
     scroll de mas arriba): :first-child nunca matcheaba ningun
     .service-entry porque .service-showcase__progress lo precede como
     hijo real del contenedor - la linea de encima de "Estrategia" no se
     quitaba. */
  #home-servicios .service-showcase__list .service-entry:first-of-type .service-link-row {
    border-top: none;
  }
  /* Sin scroll-jacking aqui (lista apilada normal), el concepto de
     "avance dentro del carrusel" no aplica - main.js tampoco actualiza
     --service-scroll-progress en este modo (mismo guard que el resto del
     carrusel), asi que la barra se queda fija en 0% si no se oculta. */
  .service-showcase__progress { display: none; }
}

/* Peticion cliente 2026-07-13: SOLO estilos para movil real (<=860px) -
   el pin/scroll/barra de progreso permanecen activos (igual
   comportamiento de scroll que escritorio/tablet, ver JS en main.js).

   Correccion sobre el intento anterior (flex:1 1 auto + overflow-y:auto
   en .service-link-row): con las 3 categorias comprimidas dentro de un
   pin de ALTO FIJO (calc(100vh - header), pensado para el viewport alto
   de escritorio), la que mas sub-items tiene ("Sistemas de Diseño", 4
   items) seguia sin caber entera y necesitaba scroll interno - el
   cliente pide expresamente que NO se corte ningun texto, ni con scroll
   interno: cada slide debe medir lo que necesite su propio contenido.
   Eso es incompatible con el modelo de escritorio (los 3 paneles
   superpuestos via position:absolute+inset:0, todos del mismo alto fijo,
   crossfade por opacidad) - aqui en movil el panel INACTIVO se saca del
   flujo por completo (display:none, no solo opacity:0) y el ACTIVO vuelve
   a flujo normal (position:relative, alto automatico segun su contenido).
   El pin (sticky) ya no fuerza una altura fija - crece o se encoge con
   cada categoria activa. Se pierde el crossfade (fundido) de escritorio
   al cambiar de categoria en movil (cambio instantaneo en su lugar) -
   aceptado: el cliente prioriza ver el texto completo sobre conservar esa
   transicion. */
@media (max-width: 860px) {
  /* Bug encontrado 2026-07-26 (ver main.js, syncServicePinMobileHeight):
     height:auto a secas hacia que el pin (sticky) cambiara de alto en el
     mismo frame en que cambia el slide activo, empujando de golpe el
     resto de la pagina - mas notorio en la transicion 2->3 (mayor
     diferencia de alto entre slides consecutivos). var() con "auto" como
     fallback: antes de que el JS calcule la altura real (o si esta
     desactivado), se comporta exactamente igual que antes. overflow
     visible explicito (la regla base pensada para escritorio pone
     hidden): con el pin ya fijado al mas alto de los 3 slides no debería
     hacer falta, pero es la misma red de seguridad que ya se aplica en
     servicios.html para garantizar que ningun texto se recorta. */
  .service-showcase__pin {
    height: var(--service-pin-mobile-h, auto);
    overflow: visible;
  }

  /* Peticion cliente 2026-07-13: reducir el espacio entre "Servicios" y
     el primer servicio del carrusel - en movil, con la pantalla mas baja,
     el hueco pensado para escritorio (--space-lg=40px bajo el titulo +
     30px bajo la linea = 71px) se nota excesivo. Escopado a
     #home-servicios (no toca el mismo margin-bottom en "Proyectos", que
     usa la misma clase .section__heading pero no se ha pedido tocar).
     Reducido de nuevo 2026-07-13 (16px+16px, ver mas abajo): con el pin
     ahora a alto automatico (ver mas arriba, "Sistemas de Diseño" ya no
     se recorta), la categoria con mas sub-items sigue sin caber entera
     en 1 sola pantalla en moviles mas bajos (viewport ~667px, iPhone SE y
     similares) - cada px que se libera aqui es un px mas de la
     transicion completa visible sin tener que scrollear dentro del
     propio slide. */
  #home-servicios .section__heading {
    margin-bottom: var(--space-xs);
  }
  #home-servicios .divider {
    margin-bottom: var(--space-xs);
  }

  /* margin-left:100px (regla base, pensada para el ancho generoso de
     escritorio/tablet) no cabe en un viewport movil - el contenido ya
     tiene su propio padding lateral via .container. flex:0 0 50% (regla
     base, tambien pensada para el viewport alto de escritorio) ya no
     aplica: con el pin en alto automatico, la lista debe medir lo que
     mida su contenido, no un porcentaje del pin. */
  .service-showcase__list {
    margin-left: 0;
    flex: none;
    min-height: 0;
  }

  #home-servicios .service-showcase__list .service-entry {
    position: relative;
    inset: auto;
    display: none;
  }

  #home-servicios .service-showcase__list .service-entry.is-active {
    display: block;
  }

  /* Peticion cliente 2026-07-13 (correccion sobre el intento anterior,
     que ocultaba los sub-items por completo): en movil la informacion de
     cada servicio (.service-link-row__items) SI debe verse, pero debajo
     del nombre de la categoria - no al lado, en 2 columnas, como en
     escritorio/tablet (ahi no cabe: cada columna quedaria demasiado
     estrecha). 1 sola columna, el titulo de fila ocupa toda la fila y
     los items pasan a la fila de abajo (grid-auto-flow: row, orden del
     DOM). height:auto (no 100%) + overflow visible: el alto del slide
     activo ya no viene impuesto por el pin, sino por su propio contenido -
     nada que recortar ni que desplazar internamente. */
  #home-servicios .service-showcase__list .service-link-row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
    height: auto;
    overflow: visible;
  }

  /* Peticion cliente 2026-07-13: en movil, .service-link-row__title baja
     de 48px (tamano compartido con escritorio/tablet) a 42px. Reducido de
     nuevo 2026-07-25 (peticion cliente, portado desde el prototipo tras
     su aprobacion) a 30px, en linea con los ajustes de tamano del titulo
     de item justo debajo (ver comentario siguiente). */
  #home-servicios .service-showcase__list .service-link-row__title {
    font-size: 30px;
    /* De inline (span) a block: necesario para que el vector de abajo
       (::after) se dibuje como una linea de ancho completo bajo el
       titulo, no pegado al final del texto. */
    display: block;
  }

  /* Peticion usuario 2026-07-25 (portado desde el prototipo tras su
     aprobacion): el titulo de cada item dentro de una categoria
     ("Estrategia de Marca") mantiene la misma tipografia display (Lora)
     que el titulo de categoria ("Estrategia", regla de arriba), solo que
     mas pequeño - tamano ajustado por el cliente varias veces sobre el
     base de 32px (compartido con escritorio/tablet) hasta fijarse en
     24px. */
  #home-servicios .service-showcase__list .service-link-row__item-title {
    font-size: 24px;
  }

  /* Peticion cliente 2026-07-25 (portado desde el prototipo tras su
     aprobacion): font-size explicito de 14px para la descripcion de cada
     item (base 16px, compartido con escritorio/tablet) en este mismo
     breakpoint. */
  #home-servicios .service-showcase__list .service-link-row__item-desc {
    font-size: 14px;
  }

  /* Peticion cliente 2026-07-13: incluir el vector (misma linea fina que
     ya separa "Servicios" del carrusel, ver .divider) debajo de CADA
     servicio del carrusel - entre el nombre de la categoria y su lista
     de sub-items. Como ::after del titulo (no un <hr> nuevo en el HTML)
     para no duplicar este marcado por breakpoint. */
  #home-servicios .service-showcase__list .service-link-row__title::after {
    content: "";
    display: block;
    margin-top: var(--space-sm);
    height: 1px;
    background: var(--color-border);
  }

  /* .services-media: regla de ocultacion trasladada 2026-07-28 (portado
     desde el prototipo tras su aprobacion) a su propio
     @media (max-width:600px), ver justo debajo de la regla base de
     .services-media mas arriba - antes se ocultaba en todo este bloque
     (<=860px), lo que apagaba la imagen tambien en tablet vertical real
     (768px de ancho, ej. iPad) mientras se veia con normalidad en el
     mismo dispositivo en horizontal (1024px) - un mismo aparato daba 2
     experiencias distintas solo por rotarlo. Ver auditoria responsive de
     esa fecha. */
}


@media (max-width: 860px) {
  /* minmax(0, 1fr), no "1fr" a secas (mismo criterio ya documentado en
     pages.css para .about-page/.contact-page): un 1fr puro no evita que
     la pista crezca para acomodar el max-content de un hijo de ancho fijo
     - aqui la linea decorativa de "Servicios" (.page-hero__title--md::after,
     339px) empujaba .services-layout a 339px+padding, provocando scroll
     horizontal en viewports estrechos tras ampliar el margen lateral a
     100px (2026-07-10, ver client-feedback/home-full-bleed-spec.md). */
  .project-entry,
  .service-row,
  .services-layout,
  .service-link-row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
  }

  /* Sin esto, los hijos de .service-row siguen intentando ocupar las
     columnas 2/3 del grid de escritorio (279px 289px 1fr), que en una
     sola columna no existen - rompería el layout mobile. */
  .service-row > div:first-child,
  .service-row > div:last-child {
    grid-column: 1;
  }

  /* En movil "Servicios" (titulo de pagina) sigue apilado en flujo normal -
     su sticky no tiene sentido ahi (vive en su propia columna solo en
     escritorio), se desactiva. El titulo de FILA ("Estrategia"/"Sistemas
     de Diseño"/"Experiencias y Ejecucion") y el titulo de cada BLOQUE
     (.service-group__title) SI mantienen su sticky en movil (peticion del
     cliente 2026-06-29, ampliada el mismo dia: las 2 capas deben quedar
     paradas, apiladas, igual que en escritorio/tablet, para las 3 filas) -
     solo se les ajusta el `top` mas abajo, no se desactivan aqui. */
  .services-layout__title {
    position: static;
    height: auto;
    display: block;
  }

  .services-layout__title { margin-bottom: var(--space-lg); }

  /* El offset de 87px+space-2xl del top de escritorio existe solo para no
     fijarse a la misma altura que la linea decorativa bajo "Servicios"
     (que en movil no esta fija - se desactivo arriba). En movil el titulo
     de fila puede fijarse justo bajo el header, sin ese hueco. */
  .service-row > div:first-child {
    top: var(--header-height);
  }

  /* El titulo de bloque se apila justo debajo de la barra del titulo de
     fila, sin hueco ni solape - --row-title-h es la altura real de esa
     barra (medida por JS en main.js, heredada desde .service-row porque
     .service-group__title es descendiente suyo). No puede ser un numero
     fijo: "Estrategia" es 1 linea pero "Sistemas de Diseño"/"Experiencias
     y Ejecucion" tienen <br> fijo (siempre 2 lineas) y las taglines tienen
     longitudes distintas - la altura real cambia por fila y por ancho. */
  .service-group__title {
    top: calc(var(--header-height) + var(--row-title-h, 0px));
  }

  /* El ULTIMO bloque de cada fila comparte limite de contencion con el
     titulo de fila (el final de la fila ES el final de su ultimo bloque),
     pero al estar mas abajo en pantalla llega antes a ese limite y se
     suelta antes - durante ese tramo de scroll pasa por detras/encima del
     titulo de fila, todavia fijo, y se ve superpuesto. Probado: alargar el
     contenedor del bloque (padding-bottom compensado con margin-bottom
     negativo para no desplazar el resto de la pagina) para igualar ambos
     limites - verificado con Playwright que NO elimina el solape (el
     `top` efectivo de sticky en este motor no sigue el calculo teorico
     padding-box una vez hay margin negativo de por medio). En vez de
     perseguir un ajuste fino poco fiable, el ULTIMO bloque de cada fila
     no se fija - se queda en flujo normal, igual que antes de esta
     ampliacion. El titulo de fila (que es la pieza que pidio el cliente)
     sigue fijo durante toda la fila sin excepcion; los demas bloques
     (no el ultimo) siguen con su sticky "por turnos" normal. */
  .service-row > div:last-child > .service-group:last-child .service-group__title {
    position: static;
  }

  /* La franja opaca de .services-layout::before solo tenia sentido para
     tapar contenido en el hueco header->260px de escritorio (relacionado
     con el offset de arriba) - en movil titulo de fila y de bloque se
     fijan apilados sin hueco que tapar entre ellos ni con el header. */
  .services-layout::before { display: none; }

  /* La linea de 495px fijos (SRV-018) desbordaria en viewports estrechos. */
  .service-row > div:last-child .service-group:first-child::before {
    width: 100%;
  }
}

/* Arreglo 2026-07-25 (reportado por el usuario: "los textos de la version
   tablet no se ajustan al ancho de la tablet" - confirmado con Playwright:
   scroll horizontal en Servicios a 1024px, las columnas fijas 279px+420px
   de mas arriba no caben). Se probo antes estrechar esas 2 columnas con
   clamp() (mismo patron que el hero de Home) - descartado: en cuanto un
   .service-group__title deja de caber en 1 linea a columnas mas
   estrechas, su sticky (top fijo, calibrado para 1 linea) invade
   visualmente el primer renglon de la descripcion de abajo (confirmado
   con Playwright, offsetTop/offsetHeight: sin sticky no hay solape, con
   sticky si). Arreglarlo bien ahi exigiria un offset dinamico nuevo para
   el rango de tablet - en vez de reinventarlo, se reutiliza el fallback
   movil de arriba (ya probado: layout apilado de 1 columna, sticky con
   --row-title-h dinamico, sin franja opaca) extendiendo su rango SOLO
   para las reglas propias de la pagina Servicios
   (.service-row/.services-layout/.service-group__title). No se toca
   .project-entry ni .service-link-row (carrusel de Servicios/Proyectos
   de Home) - esos mantienen deliberadamente el efecto de escritorio en
   tablet por peticion explicita del cliente (ver "Historial 2026-06-29"
   en el bloque de arriba). .services-media (imagen compartida) se deja
   visible (a diferencia de movil, donde se oculta) pero ya no queda
   anclada al fondo del viewport - el truco de altura 100vh de
   .services-layout__title solo tiene sentido con sticky; aqui pasa a
   flujo normal, justo debajo del texto "Servicios". Techo en 1279px:
   1280px es el ancho de referencia (--content-max) donde 279+420+200px
   de padding ya caben con margen. */
@media (min-width: 861px) and (max-width: 1279px) {
  .service-row,
  .services-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
  }

  .service-row > div:first-child,
  .service-row > div:last-child {
    grid-column: 1;
  }

  .services-layout__title {
    position: static;
    height: auto;
    display: block;
    margin-bottom: var(--space-lg);
  }

  .service-row > div:first-child {
    top: var(--header-height);
  }

  .service-group__title {
    top: calc(var(--header-height) + var(--row-title-h, 0px));
  }

  .service-row > div:last-child > .service-group:last-child .service-group__title {
    position: static;
  }

  .services-layout::before { display: none; }

  .service-row > div:last-child .service-group:first-child::before {
    width: 100%;
  }

  /* Peticion del usuario 2026-07-30: volver a anclar la descripcion al
     fondo de la columna en tablet (margin-top:0), igual que escritorio -
     sustituye el ajuste de 2026-07-25 (interlineado fijo de 20px). */
  #home-proyectos .project-entry__description {
    margin-top: 0;
  }
}
