/**
 * oasys.css — capa propia de Oasys para el sitio publico.
 *
 * Se carga DESPUES de home.css y servicios.css a proposito: lo que hay aqui
 * corrige la plantilla Hidayah, no convive con ella. A medida que la plantilla
 * se vacie, estas reglas dejan de necesitar especificidad y se simplifican.
 *
 * ---------------------------------------------------------------------------
 * LOS TOKENS NO SE INVENTAN AQUI.
 *
 * Son los mismos del ERP, copiados de
 *   OASYS/ERP/static/css/shadcn/theme.css
 * que es la fuente de verdad del design system de la organizacion. Los nombres
 * son los del contrato (10-diseno.md) y NO se renombran: si el ERP cambia un
 * valor, aqui se cambia el mismo nombre y todo lo demas sigue funcionando.
 *
 * La plantilla traia --accent-color / --default-color / --heading-color, que son
 * los nombres de BootstrapMade. Esos quedan como alias al final del archivo
 * mientras dure la migracion; no se usan en reglas nuevas.
 * ---------------------------------------------------------------------------
 */

:root {
  /* ---- Marca (independiente del tema) ---- */
  --royal: #2A51A3;
  --indigo: #2E3192;
  --azure: #2485C7;
  --oasys-teal: #52C3C4;

  /* Las dos direcciones de la rampa, con el motivo escrito en el ERP: 135° para
     superficies grandes donde la diagonal es el eje largo; 100° para pildoras
     anchas y bajas, donde a 135° el ultimo tramo se comprime contra la punta
     derecha y se lee como un corte. */
  --grad:     linear-gradient(135deg, var(--indigo) 0%, var(--azure) 58%, var(--oasys-teal) 100%);
  --grad-btn: linear-gradient(100deg, var(--indigo) 0%, var(--azure) 58%, var(--oasys-teal) 100%);
  --primary-grad: linear-gradient(180deg, #3567D8, #244FB0);
  --brand-solid: #2F5FCA;

  /* Las dos rampas del boton, en token propio porque `.dark-background` las gira
     ENTERAS. Los cuatro valores ya estaban en este archivo —son --primary-grad,
     --brand-solid y --brand-h—, no hay color nuevo. Van literales y no como
     var() anidada a proposito: una propiedad personalizada se sustituye en el
     elemento que la usa, asi que un var(--brand-h) escrito aca resolveria al
     #6F97E0 del tema oscuro dentro del hero y la rampa clara se colaria ahi. */
  --btn-grad:   linear-gradient(180deg, #3567D8, #244FB0);
  --btn-grad-h: linear-gradient(180deg, #2F5FCA, #22448C);
  /* La tinta del boton NO gira con el tema, y esa es la decision. `--on-brand`
     se invierte a casi negro sobre `.dark-background` porque ahi `--brand` se
     aclara; al arrastrar el boton detras de esa inversion la rampa tenia que
     subir hasta #8FB0E8 para sostener el contraste, y #8FB0E8 no es un color de
     marca: es el token de TEXTO del tema oscuro del ERP. El resultado era un
     boton lavanda que no existe en la marca. Con tinta blanca fija la rampa se
     queda en el azul de marca en las 94 paginas y da 5.14:1 arriba y 7.45:1
     abajo, que pasa AA sin ayuda de nadie. */
  --btn-txt: #FFFFFF;

  /* ---- Neutros y superficies ---- */
  --bg: #FAFBFC;
  --panel: #F5F6FA;
  --card: #FFFFFF;
  --hover: #EEF0F5;
  --bd: #E6E8EF;
  --bd-2: #D5DAE4;
  --tx: #14161C;
  --tx2: #565E6E;
  --tx3: #636A78;

  /* ---- Acentos ---- */
  --brand: #2A51A3;
  --brand-h: #22448C;
  --brand-txt: #2A51A3;      /* 7.49:1 sobre --card. El que puede llevar texto */
  --brand-sub: #EAF0FA;
  --brand-sub-bd: #CCDBEF;
  --teal: #3FADAE;           /* el turquesa de marca ya oscurecido por el ERP */

  /* ---- Semanticos ---- */
  --ok: #0D7A4B;   --ok-sub: #E4F6EE;
  --warn: #946012; --warn-sub: #FBF1DE;
  --dg: #C53A31;   --dg-sub: #FCEBE9;
  --on-brand: #FFFFFF;

  /* ---- Sombras ---- */
  --sh-sm: 0 1px 2px rgba(16, 22, 40, .07);
  --sh-md: 0 8px 22px -6px rgba(16, 22, 40, .14);
  --sh-lg: 0 28px 56px -16px rgba(16, 22, 40, .24);

  /* ---- Foco ---- */
  --ring: rgba(42, 81, 163, .3);
  --focus-ring-width: 3px;
  --focus-ring-offset: 2px;

  /* ---- Movimiento. La curva es la unica del sistema. ---- */
  --dur-fast: 150ms;
  --dur-med: 240ms;
  --ease-out: cubic-bezier(0.2, 0, 0, 1);

  /* ---- Forma ---- */
  --radius: 8px;
  --radius-sm: 6px;
  --radius-lg: 14px;

  /* ---- Iconos: tres tamaños, no trece ---- */
  --icon-sm: 16px;
  --icon-md: 24px;
  --icon-lg: 40px;
  /* Minimo de objetivo tactil (WCAG 2.5.8 pide 24; 44 es el criterio AAA
     de 2.5.5 y el que usan iOS y Material). */
  --tap: 44px;

  /* ---- Escala tipografica y de espaciado ----
     Estas dieciocho lineas estaban en home.css:31-48, escritas y correctas, con
     UN uso en todo el repo. Mientras vivieran en la hoja de la plantilla nadie
     las iba a consumir: este archivo se carga despues y es el que manda. En el
     hueco que dejaron, el sitio acumulo 27 tamaños de texto y 29 valores de
     espaciado; 15 y 15.5px conviviendo, y 12, 13, 13.5 y 13.6 tambien. Mudarlas
     aca no cambia un pixel por si solo: habilita que los arreglos siguientes
     sean un token y no otro numero suelto. */
  --text-xs:   0.75rem;    /* 12 — etiquetas, unidades */
  --text-sm:   0.875rem;   /* 14 — texto secundario */
  --text-base: 1rem;       /* 16 — cuerpo, uno solo */
  --text-lg:   1.125rem;   /* 18 — titulo de tarjeta */
  --text-xl:   1.25rem;    /* 20 */
  --text-2xl:  1.5rem;     /* 24 */
  --text-3xl:  1.875rem;   /* 30 */
  --text-4xl:  2.25rem;    /* 36 */
  --text-5xl:  3rem;       /* 48 */

  --space-xs:  0.25rem;    /*  4 */
  --space-sm:  0.5rem;     /*  8 */
  --space-md:  1rem;       /* 16 */
  --space-lg:  1.5rem;     /* 24 */
  --space-xl:  2rem;       /* 32 */
  --space-2xl: 3rem;       /* 48 */
  --space-3xl: 4rem;       /* 64 */
  --space-section: 5rem;   /* 80 */
}

/* ===========================================================================
   Superficie de marca
   ---------------------------------------------------------------------------
   El campo azul del login del portal. Es la UNICA superficie del sitio que usa
   el color de marca como fondo — por eso puede permitirselo. Sobre ella los
   tokens de texto y borde se redefinen; ninguna regla de abajo necesita saber
   si esta en claro o sobre marca, consume los mismos nombres.
   =========================================================================== */

.superficie-marca {
  --tx: #FFFFFF;
  --tx2: #B9CBE8;            /* 8.1:1 sobre el azul mas oscuro del campo */
  --tx3: #8FA6CC;
  --bd: rgba(255, 255, 255, .12);
  --bd-2: rgba(255, 255, 255, .20);
  /* Valores del tema oscuro del ERP, no inventados aqui. */
  --brand-txt: #8FB0E8;
  --teal: #63CCCD;

  position: relative;
  isolation: isolate;
  color: var(--tx2);
  background:
    radial-gradient(78% 62% at 88% 6%, rgba(36, 133, 199, .40) 0%, rgba(36, 133, 199, 0) 62%),
    radial-gradient(70% 60% at 6% 96%, rgba(46, 49, 146, .48) 0%, rgba(46, 49, 146, 0) 64%),
    linear-gradient(158deg, #10275A 0%, #0D1E42 58%, #0B1937 100%);
}

.superficie-marca h1,
.superficie-marca h2,
.superficie-marca h3,
.superficie-marca h4 { color: var(--tx); }

.superficie-marca p { color: var(--tx2); }

/* ===========================================================================
   PUENTE DE TOKENS
   ---------------------------------------------------------------------------
   Esta hoja se carga la ultima en las 94 paginas (base.html:90). Redefiniendo
   aqui los tokens de la plantilla Hidayah y los de Bootstrap, todo el sitio pasa
   a los colores de marca sin tocar una sola plantilla.

   Lo que arregla, medido:
   - 612 usos de las utilidades de Bootstrap (440 `text-primary`, 92
     `btn-outline-primary`, 80 `btn btn-primary`) en 74 archivos. Hoy salen en
     #0d6efd, el azul por defecto de Bootstrap, que no es un color de Oasys:
     home.css solo redefine .btn-primary bajo .about, .testimonial-cta,
     .service-cta y .how-we-work, asi que un `.btn.btn-primary` suelto —lo que
     usan los 68 fragmentos de servicio— se queda con el de Bootstrap.
   - El hover de los enlaces del pie, que hoy EMPEORA el contraste: en reposo dan
     9.77:1 y al pasar el raton caen a 2.71:1, porque `.dark-background`
     redefine --default-color pero no --accent-color.
   - El aro de foco sobre fondo oscuro (home.css:2855), hoy 2.71:1, que incumple
     WCAG 1.4.11.

   Es un puente, no el destino: a medida que la plantilla se vacie, estos alias
   dejan de hacer falta.
   =========================================================================== */

:root {
  --accent-color: var(--brand);
  --accent-color-light: var(--azure);
  --accent-color-dark: var(--indigo);
  --accent-teal: var(--teal);
  --muted-color: var(--tx2);
  --border-color: var(--bd);

  --bs-primary: #2a51a3;
  --bs-primary-rgb: 42, 81, 163;
  --bs-link-color: #2a51a3;
  --bs-link-color-rgb: 42, 81, 163;
  --bs-link-hover-color: #22448C;
}

/* `.text-primary` si consume --bs-primary-rgb, pero los botones NO: en Bootstrap
   5.3 cada variante fija sus propias variables de componente con literales, asi
   que redefinir --bs-primary no los alcanza. Hay que puentear esas. */
.btn-primary {
  --bs-btn-bg: var(--brand);
  --bs-btn-border-color: var(--brand);
  --bs-btn-hover-bg: var(--brand-h);
  --bs-btn-hover-border-color: var(--brand-h);
  --bs-btn-active-bg: var(--brand-h);
  --bs-btn-active-border-color: var(--brand-h);
  --bs-btn-disabled-bg: var(--brand);
  --bs-btn-disabled-border-color: var(--brand);
  --bs-btn-color: var(--on-brand);
  --bs-btn-hover-color: var(--on-brand);
  --bs-btn-active-color: var(--on-brand);
}

.btn-outline-primary {
  --bs-btn-color: var(--brand-txt);
  --bs-btn-border-color: var(--brand);
  --bs-btn-hover-bg: var(--brand);
  --bs-btn-hover-border-color: var(--brand);
  --bs-btn-hover-color: var(--on-brand);
  --bs-btn-active-bg: var(--brand-h);
  --bs-btn-active-border-color: var(--brand-h);
  --bs-btn-active-color: var(--on-brand);
}

/* ===========================================================================
   Superficies oscuras de la plantilla
   ---------------------------------------------------------------------------
   Hidayah ya marca sus secciones oscuras con `.dark-background` (hero, stats,
   pie). Ahi los tokens del contrato tienen que tomar los valores del tema
   oscuro del ERP, o resuelven al tema claro y aparecen fallos como el que tenia
   estadisticas: --brand-txt daba #2A51A3 sobre #060606 = 2.71:1, por debajo del
   minimo de 3:1 para graficos. Con #8FB0E8 son 7.4:1.
   =========================================================================== */

.dark-background {
  --tx: #EDEDEB;
  --tx2: #B4B0A9;
  --tx3: #948F88;
  --brand: #5B86D8;
  --brand-h: #6F97E0;
  --brand-txt: #8FB0E8;
  --teal: #63CCCD;
  --bd: rgba(255, 255, 255, .09);
  --bd-2: rgba(255, 255, 255, .14);

  /* El puente, tambien sobre oscuro. Sin esta linea el hover de los enlaces del
     pie deja el texto en 2.71:1 — menos legible que en reposo — en las 94
     paginas, y el aro de foco es invisible. #8FB0E8 sobre #060606 da 7.4:1. */
  --accent-color: var(--brand-txt);
  --bs-primary: #8FB0E8;
  --bs-primary-rgb: 143, 176, 232;

  /* La tinta que va ENCIMA de --brand tiene que girar con --brand. En claro el
     boton es #2A51A3 y la tinta blanca da 7.49:1; sobre oscuro --brand se aclara
     a #5B86D8 (valor del tema oscuro del ERP, no se toca) y ahi el blanco cae a
     3.59:1 — por debajo de AA. Con la tinta del sistema el par se recompone:
       texto  #14161C sobre #5B86D8 (reposo) = 5.03:1
       texto  #14161C sobre #6F97E0 (hover)  = 6.18:1
       relleno #5B86D8 sobre #060606         = 5.64:1  (1.4.11 pide 3:1)
     Aplica a .btn-oasys y al puente de .btn-primary / .btn-outline-primary por
     igual, que es justo lo que se quiere: son el mismo boton. */
  --on-brand: #14161C;
}

/* ===========================================================================
   Iconos del sistema Corte 45
   =========================================================================== */

.ic {
  width: var(--icon-md);
  height: var(--icon-md);
  display: inline-block;
  vertical-align: middle;
  flex-shrink: 0;
  color: currentColor;
}

.ic-sm { width: var(--icon-sm); height: var(--icon-sm); }
.ic-lg { width: var(--icon-lg); height: var(--icon-lg); }

/* ===========================================================================
   Boton
   ---------------------------------------------------------------------------
   "Solicitar cotizacion" estaba construida SEIS veces en home.css, con seis
   degradados, cinco rellenos, cuatro sombras, dos tipografias y dos animaciones
   infinitas. Los seis bloques ya no existen; esta es la forma unica a la que
   convergen. El orden del selector va de lo generico a lo especifico solo para
   leerse: al estar en una sola regla, la especificidad de cada selector se
   evalua por separado y todos ganan a lo que quede de la plantilla.

     .btn-oasys                                -> las 84 "Solicitar cotizacion"
     .btn-get-started                          -> CTA del hero (3 en home)
     .contact .php-email-form button[type=...] -> envio del formulario

   El puente por contexto —`.about .btn-primary`, `.testimonial-cta .btn-primary`,
   `.service-cta .btn-primary`, `.how-we-work .btn-primary`— ya no existe: las 84
   llamadas a la accion llevan `.btn-oasys` en la plantilla, que era el destino
   que este bloque anunciaba. Se quitan y no se dejan "por si acaso" porque un
   selector que no casa con nada es un comentario que miente: el que lo lea
   despues creera que hay paginas dependiendo de el.

   Las 64 paginas de servicio individual eran las que se quedaban fuera: su
   `btn btn-primary` suelto nunca entro en el puente y salia con la forma cruda
   de Bootstrap. Ver el bloque 60.
   =========================================================================== */

.btn-oasys,
.btn-get-started,
.contact .php-email-form button[type="submit"] {
  display: inline-block;
  /* El degradado del boton, que es el mismo token que usa "Iniciar sesion" en el
     portal del ERP. Al colapsar los seis bloques de home.css en esta regla se
     habia quedado en relleno plano y el degradado solo sobrevivia sobre el campo
     azul: era una perdida de la unificacion, no una decision. Blanco sobre el
     #3567D8 del tope da 5.14:1 y sobre el #244FB0 del pie 7.45:1, o sea que el
     degradado entero pasa AA sin excepcion. */
  background: var(--btn-grad);
  /* Bootstrap y la plantilla dejaban `background-size: 150% 100%` para correr el
     degradado en el hover. Ese truco no se usa aca —el hover cambia la rampa
     entera—, asi que el tamaño vuelve al que le corresponde. */
  background-size: auto;
  color: var(--btn-txt);
  border: 0;
  border-radius: var(--radius);
  padding: 13px 28px;
  font-family: inherit;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.35;
  letter-spacing: normal;
  text-decoration: none;
  /* `<button>` no lleva `.btn` de Bootstrap en el formulario de contacto: sin
     esto el unico boton que envia datos del sitio sale con el cursor de flecha. */
  cursor: pointer;
  box-shadow: none;
  /* Sin transicion, y no por descuido: dos `linear-gradient` no son interpolables
     en CSS, asi que `transition: background` sobre una rampa no anima nada. Dejar
     la declaracion escrita seria un comentario que miente sobre lo que pasa. */
}

.btn-oasys:hover,
.btn-get-started:hover,
.contact .php-email-form button[type="submit"]:hover {
  /* La misma rampa, un escalon mas oscura, armada con dos tokens que ya existen:
     8.94:1 en el pie y 5.82:1 en el tope. Mas contraste que el reposo, que es lo
     que un hover debe hacer. */
  background: var(--btn-grad-h);
  background-position: 0 0;
  transform: none;
  box-shadow: none;
  color: var(--btn-txt);
  animation: none;
}

/* Lo unico que no es forma sino sitio: el envio del formulario ocupa la columna
   entera, como lo hacia antes de la unificacion. */
.contact .php-email-form button[type="submit"] { width: 100%; }

/* ===========================================================================
   Cabecera de pagina — el h1 vuelve a verse
   ---------------------------------------------------------------------------
   home.css:961 sacaba de pantalla el h1 y las migas con `clip: rect(0,0,0,0)` y
   dejaba `.page-title` en 90px de alto fijo. Resultado: NINGUNA pagina del sitio
   mostraba su h1, y las 84 paginas de servicio abrian con una banda negra vacia
   de 90px. El h1 seguia en el DOM —Google lo leia— pero la persona no.

   Se corrige aqui y no en home.css porque `clip` es de la plantilla: cuando
   Hidayah termine de vaciarse, esta regla se queda y aquella desaparece.

   El fondo lo pone `--background-color` (#ffffff), porque en el bloque 58 se le
   quito `.dark-background` a las 20 plantillas que traen `.page-title`: ya no
   es una losa oscura sino el comienzo del contenido. Los tokens resuelven al
   tema claro y de ahi salen los ratios anotados abajo. El alto es el del
   contenido; el relleno superior deja libre la barra de navegacion, que es fija
   —su valor definitivo lo fija `.con-barra` en el bloque 58—.
   =========================================================================== */

.page-title {
  /* 110 de arriba son 90 de barra fija mas 20 de aire; el resto es la caja
     negra propiamente dicha. Ver el bloque 53 para por que baja. */
  padding: 104px 0 26px;
  height: auto;
}

.page-title h1 {
  /* Deshace el ocultamiento visual de home.css. Cada propiedad de aquel bloque
     tiene que volver a su valor inicial o el h1 sigue en una caja de 1x1. */
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip: auto;
  clip-path: none;
  white-space: normal;

  font-size: 36px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.02em;
  color: var(--tx);            /* #14161C sobre #ffffff = 18.08:1 */
}

.page-title .breadcrumbs {
  position: static;
  width: auto;
  height: auto;
  margin: 12px 0 0;
  overflow: visible;
  clip: auto;
  clip-path: none;
  white-space: normal;

  font-size: 14px;
  line-height: 1.5;
  color: var(--tx2);           /* #565E6E sobre #ffffff = 6.52:1 */
}

.page-title .breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0 8px;
  align-items: center;
}

.page-title .breadcrumbs ol li + li::before {
  content: "/";
  margin-right: 8px;
  color: var(--tx3);           /* #636A78 sobre #ffffff = 5.44:1 */
}

.page-title .breadcrumbs ol a {
  color: var(--tx2);
  text-decoration: none;
}

.page-title .breadcrumbs ol a:hover {
  color: var(--tx);
  text-decoration: underline;
}

.page-title .breadcrumbs ol .current {
  color: var(--tx3);
}

@media (max-width: 767px) {
  .page-title { padding: 90px 0 28px; }
  .page-title h1 { font-size: 28px; }
}

/* ===========================================================================
   Estadisticas
   ---------------------------------------------------------------------------
   La plantilla pinta estos iconos con --accent-color #2a51a3 sobre el fondo
   #060606 de `.dark-background`: 2.71:1, por debajo del minimo de 3:1 para
   graficos. --brand-txt del tema oscuro del ERP (#8FB0E8) da 7.4:1.
   Y `line-height: 0` es el mismo defecto que tenia #about.
   =========================================================================== */

/* Icono y cifra en la misma linea; la etiqueta ocupa el ancho completo debajo.
   Sin el `flex: 1 0 100%` del parrafo, los tres bloques compiten por el ancho y
   la etiqueta se parte en tres lineas. */
.stats .stats-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 14px;
}

.stats .stats-item .ic {
  color: var(--brand-txt);
  margin: 0;
}

.stats .stats-item > p {
  flex: 1 0 100%;
  margin: 8px 0 0;
}

/* ===========================================================================
   Vitrina — trabajo real de cliente (sin uso por ahora)
   ---------------------------------------------------------------------------
   Web y app del mismo proyecto, superpuestas. Muestra alcance sin decirlo.
   La proporcion del contenedor esta fijada y cada <img> declara sus pixeles
   reales, asi que no hay salto de layout al cargar — que era justo el defecto
   de la foto que habia antes.
   =========================================================================== */

.vitrina {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  display: block;
}

.vitrina figure { margin: 0; }

.vitrina img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

/* La captura de escritorio, ligeramente girada para que no se lea como un
   rectangulo pegado sobre el fondo. */
.vitrina-web {
  position: absolute;
  top: 20%;
  left: 2%;
  width: 88%;
  transform: rotate(-2.2deg);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--sh-lg);
  border: 1px solid rgba(255, 255, 255, .14);
}

/* El telefono monta sobre la esquina inferior derecha: la superposicion es lo
   que hace legible que son el MISMO proyecto en dos medios. */
.vitrina-app {
  position: absolute;
  right: 0;
  bottom: 6%;
  width: 27%;
  transform: rotate(3.4deg);
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: var(--sh-lg);
  border: 1px solid rgba(255, 255, 255, .18);
  z-index: 2;
}

.vitrina-pie {
  position: absolute;
  left: 2%;
  bottom: 0;
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tx3);
}

.vitrina-cliente {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--teal);
}

@media (max-width: 991px) {
  .vitrina { aspect-ratio: 4 / 3; margin-bottom: 12px; }
  .vitrina-web { top: 12%; width: 84%; }
}

/* ===========================================================================
   Menu movil
   ---------------------------------------------------------------------------
   El boton llevaba la clase `bi bi-list` sobre si mismo y NINGUN contenido: el
   icono era el ::before de la tipografia, y main.js alternaba `bi-list`/`bi-x`.
   Ahora lleva dos <svg> hijos y una clase de estado `nav-abierto`.

   El tamaño tiene que venir de `width`/`height`: `font-size` no dimensiona un
   <svg>, asi que las dos reglas de la plantilla (28px cerrado, 32px abierto)
   quedaron sin efecto. El color si se hereda por `currentColor`, asi que el
   `color: #fff` del estado abierto sigue funcionando.
   =========================================================================== */

.mobile-nav-toggle .ic-menu-abrir { width: 28px; height: 28px; }

.mobile-nav-toggle .ic-menu-cerrar {
  display: none;
  width: 32px;
  height: 32px;
}

.mobile-nav-toggle.nav-abierto .ic-menu-abrir { display: none; }
.mobile-nav-toggle.nav-abierto .ic-menu-cerrar { display: block; }

/* ===========================================================================
   Listas de entregables de las paginas de servicio
   ---------------------------------------------------------------------------
   `.service-details ul i { font-size: 20px }` era selector por etiqueta y dejo
   de aplicar al pasar los 390 checks a <svg>. Sin esto crecen a los 24px de
   `.ic`. 20px conserva la proporcion original contra el texto de 15px.
   =========================================================================== */

.service-details ul .ic {
  width: 20px;
  height: 20px;
}

/* ===========================================================================
   Destello 3D
   ---------------------------------------------------------------------------
   Sin uso en la home por ahora: en #about lo reemplazo trabajo real de cliente,
   que si vende y si aporta al SEO. El componente queda para el hero, donde una
   marca en movimiento es exactamente lo que corresponde.
   =========================================================================== */




.destello {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  /* La proporcion la fija el CSS, no el canvas: si WebGL no arranca y el script
     retira el lienzo, el hueco conserva su alto y no hay salto de layout. */
  display: block;
}

.destello-lienzo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* Sin JS o sin WebGL queda el destello plano, con la misma silueta. */
.destello::before {
  content: "";
  position: absolute;
  inset: 24%;
  background: var(--oasys-teal);
  clip-path: polygon(50% 0%, 54% 46%, 100% 50%, 54% 54%, 50% 100%, 46% 54%, 0% 50%, 46% 46%);
  opacity: .18;
}

@media (max-width: 991px) {
  .destello { aspect-ratio: 4 / 3; }
}

/* ===========================================================================
   Seccion "about"
   ---------------------------------------------------------------------------
   Cuatro defectos de la plantilla corregidos:

   1. home.css:1432 pone `line-height: 0` sobre un glifo de 48px. Eso colapsa la
      caja del icono a altura CERO, y como el `li` es flex con
      `align-items: flex-start`, el glifo desborda ~24px hacia arriba e invade el
      bloque anterior. Los tres iconos quedaban por encima de sus propios titulos.
   2. Los tres glifos de la tipografia de iconos tienen anchos distintos, asi que
      las tres columnas de texto arrancaban en tres posiciones. Ahora el icono va
      en caja de ancho fijo y el texto comparte un borde izquierdo exacto.
   3. El icono pesaba 48px contra un titulo de 18px. Ahora 24px.
   4. El CTA llevaba `.mt-3` (16px) mientras los items se separan 40px entre si.
      Por proximidad se leia como parte del tercer punto. Ahora va tras un filete.
   =========================================================================== */

.about .content ul {
  list-style: none;
  padding: 0;
}

.about .content ul li {
  display: grid;
  grid-template-columns: var(--icon-lg) 1fr auto;
  gap: 0 20px;
  align-items: start;
  margin-top: 0;
  /* 26px arriba y abajo = 52px entre bloques de texto. La version anterior de
     esta seccion separaba 40px y se escaneaba mejor que la de 20px: el ojo
     necesita el hueco para saber donde termina un punto y empieza el otro. */
  padding: 26px 0;
  border-top: 1px solid var(--bd);
}

.about .content ul li:first-child {
  border-top: 0;
  padding-top: 10px;
}

.about .content ul .ic {
  width: var(--icon-lg);
  height: var(--icon-lg);
  margin-top: 2px;
  color: var(--brand-txt);
}

#about .content ul h3 {
  font-size: 19px;
  font-weight: 700;
  margin-bottom: 7px;
  letter-spacing: -.01em;
  color: var(--tx);
}

.about .content ul p {
  font-size: 15.5px;
  /* La descripcion casi siempre cae en dos lineas. Con el interlineado por
     defecto quedan pegadas y el bloque se lee como un parrafo denso. */
  line-height: 1.65;
  color: var(--tx2);
  margin-bottom: 0;
  max-width: 46ch;
}

/* El dato duro va a la derecha, alineado en columna, en tamaño de titular.
   La gente escanea numeros antes que frases: esta columna es la que se lee
   primero de toda la seccion. */
.about .content ul .cifra {
  grid-column: 3;
  grid-row: 1 / span 2;
  align-self: center;
  text-align: right;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  white-space: nowrap;
}

/* La cifra va en blanco, no en turquesa. Sobre el campo azul el turquesa se lava
   y ademas no existe en el login del portal: ahi todo es blanco y azul claro. */
.about .content ul .cifra b {
  display: block;
  /* 22 y no 27: contra el h2 de 32px, 27 daba ratio 1.19 y los dos tamaños se
     leian como el mismo nivel. La escala queda 32 / 22 / 19 / 15.5 / 11.5, con
     cada salto por encima de 1.15, y el h2 recupera el primer lugar. Que la
     cifra se lea primero ya lo consigue la repeticion de las tres, no el
     tamaño. */
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--tx);
}

.about .content ul .cifra span {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--tx3);
}

@media (max-width: 575px) {
  /* En movil la cifra pasa debajo del titulo: tres columnas no caben y
     `white-space: nowrap` desbordaria. */
  .about .content ul li { grid-template-columns: var(--icon-lg) 1fr; }
  .about .content ul .cifra {
    grid-column: 2;
    grid-row: auto;
    text-align: left;
    margin-top: 10px;
  }
  .about .content ul .cifra b { font-size: 20px; }
}

.about .cierre {
  margin-top: 24px;
  padding-top: 22px;
  border-top: 1px solid var(--bd);
}

/* ===========================================================================
   Reglas globales
   ---------------------------------------------------------------------------
   Van aqui y no en cada componente para que sea imposible olvidarlas al añadir
   uno nuevo. El KB mide que portar solo la regla de foco mueve un repo de 0/7
   a 7/7.
   =========================================================================== */

.superficie-marca :focus-visible,
.btn-oasys:focus-visible,
.btn-oasys-sec:focus-visible {
  outline: var(--focus-ring-width) solid var(--teal);
  outline-offset: var(--focus-ring-offset);
}

@media (forced-colors: active) {
  .btn-oasys:focus-visible,
  .btn-oasys-sec:focus-visible { outline: 2px solid CanvasText; }
}


/* ===========================================================================
   MOVIL
   ---------------------------------------------------------------------------
   La mayor parte del trafico entra por telefono y home.css no tiene una sola
   media query que toque el ritmo: sirve la escala de escritorio tal cual. Lo
   que sigue son defectos medidos, no preferencias.
   =========================================================================== */

/* 1. `line-height: 0` sobre un glifo grande colapsa la caja de linea y el icono
   desborda ~20px arriba y abajo. Es el defecto que este archivo ya corrigio
   para #about (ver el bloque de la lista de features); estaba en dos sitios
   mas. En .service-item el icono se dibujaba 20px por encima de su propio
   titulo, en las seis tarjetas. */
.services .service-item .icon i,
.contact .info-item i { line-height: 1; }
.services .service-item .icon { display: flex; align-items: flex-start; }

/* 2. El numero de #stats conservaba un `margin-left: 60px` de cuando el icono
   iba en flujo. Con el flex de este archivo, el numero arrancaba 114px a la
   derecha y su etiqueta en x=0: no compartian eje. */
.stats .stats-item .stat-number {
  margin-left: 0;
  line-height: 1.05;
}

/* 3. El mensaje que explica por que no se envio el formulario estaba en
   #dc3545 sobre #F5F5F5: 4.15:1, por debajo de AA. --dg ya existe. */
.invalid-feedback { color: var(--dg); }

@media (max-width: 991px) {
  /* 4. En #contact la columna de Direccion/Telefonos/Email/Horarios se apilaba
     ANTES del formulario: 696px de datos, casi una pantalla y media, entre el
     CTA que prometia cotizacion y el primer campo donde se pide. */
  #contact > .container > .row > .col-lg-6:first-child { order: 2; }
  #contact > .container > .row > .col-lg-6:last-child  { order: 1; }
}

@media (max-width: 767px) {
  /* 5. Ritmo: 120px entre secciones y 60px mas entre titulo y contenido son
     el 16% y el 24% de una pantalla de 740px. Sumaban cerca de 990px de blanco
     que el visitante recorre a ciegas. */
  section, .section { padding: 40px 0; }
  .section-title { padding-bottom: 32px; }

  /* 6. El ancla de seccion se calibraba contra un header de 90px. Ese header
     ahora mide 64px en movil (ver "CABECERA" al final del archivo) y el
     scroll-margin vive alli, junto al alto del que depende: separarlos era la
     forma segura de que uno cambie y el otro no. */

  /* 7. La tarjeta de servicio dejaba 226px para el texto —unos 30 caracteres
     por linea— porque el icono se llevaba el 21% del ancho util. En columna
     quedan 296px. */
  .services .service-item { flex-direction: column; }
  .services .service-item .icon { margin: 0 0 14px; }

  /* 8. Texto por debajo de 16px en los tres sitios que cuestan dinero: la
     propuesta de valor del hero, la unica señal de "esto lleva a algo" en las
     seis tarjetas, y la descripcion de cada feature. */
  .hero p { font-size: 16px; line-height: 1.5; }
  .services .service-item .description { font-size: 16px; }
  .services .service-item .readmore { font-size: 15px; }
  .about .content ul p { font-size: 16px; }
}

/* El desborde de la franja de clientes en celdas de 156px se arreglaba aqui con
   `flex-direction: column`. La rejilla de cinco celdas ya no existe: la
   marquesina no tiene celdas, desplaza. */

/* 10. El campo del newsletter heredaba los 14px del pie. Por debajo de 16px,
   Safari en iOS amplia la pagina entera al enfocar el campo. Es el mismo
   defecto que ya se corrigio en #contact, sin corregir aca. */
.footer .footer-newsletter .newsletter-form input[type="email"] {
  font-size: 16px;
  padding: 11px 8px;
}

/* 11. Los enlaces del pie median 40x14: el padding vivia en el <li>, no en el
   <a>, asi que no formaba parte del objetivo tactil. */
.footer .footer-links ul li { padding: 0; }
.footer .footer-links ul a {
  display: flex;
  align-items: center;
  min-height: 44px;
  line-height: 1.4;
}

/* 12. La comilla de cierre de los testimonios tiene `line-height: 0` y un
   `top: 10px`: caia ~23px por debajo de la ultima linea, encima del nombre del
   cliente, en las tres tarjetas. */
.testimonials .testimonial-item .quote-icon-left,
.testimonials .testimonial-item .quote-icon-right { line-height: 1; }
.testimonials .testimonial-item .quote-icon-right { top: 4px; }

/* 13. En tactil no hay hover, asi que `.portfolio-info { opacity: 0 }` dejaba
   las ocho capturas mudas —sin nombre, sin cliente— y, peor, `opacity: 0` no
   quita `pointer-events`: quedaban dos enlaces INVISIBLES cerca del centro de
   cada imagen, y uno de ellos abre el sitio del cliente en otra pestaña. Un
   toque al azar en la seccion de prueba social sacaba al visitante del sitio. */
@media (hover: none) {
  .portfolio .portfolio-content .portfolio-info {
    opacity: 1;
    inset: auto 0 0 0;
    padding: 12px 14px;
    background: linear-gradient(0deg, rgba(0, 0, 0, .82), transparent);
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .portfolio .portfolio-content .portfolio-info .preview-link,
  .portfolio .portfolio-content .portfolio-info .details-link {
    position: static;
    width: var(--tap);
    height: var(--tap);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    line-height: 1;
  }
}


/* ===========================================================================
   UN SOLO CUERPO DE TEXTO
   ---------------------------------------------------------------------------
   La misma frase funcional —la descripcion de una cosa— se decia con cuatro
   voces distintas segun la seccion: 14px en los pasos del proceso, 15px en las
   tarjetas de servicio y de categoria, 15.5px en #about, 16px en el resto. Esa
   es la incoherencia que el ojo detecta sin poder nombrarla. Dos tamaños: el
   cuerpo y lo secundario.
   =========================================================================== */

.services .service-item .description,
.service-category-card p,
.about .content ul p,
.how-we-work .node-content p { font-size: var(--text-base); }

.contact .info-item p,
.stats .stats-item p,
.services .service-item .readmore { font-size: var(--text-sm); }


/* ===========================================================================
   UNA SOLA TARJETA
   ---------------------------------------------------------------------------
   Cinco contenedores con la misma funcion —titulo, texto y a veces un enlace—
   y tres formas: radio 12 con sombra y filete de acento arriba, radio 12 con
   borde y sin sombra, y radio 0 sin nada. En una misma pantalla convivian
   tarjetas redondeadas con tarjetas de esquina viva. El usuario no puede
   aprender "esto es una tarjeta" si no hay una tarjeta.

   Gana el borde de la tarjeta de categoria y la sombra suave de la de servicio.
   Se van los filetes de acento de 3px (arriba en .service-item, a la izquierda
   en .info-item): son un tic de la plantilla Hidayah, no existen en el contrato
   del ERP y leen como decoracion, no como estructura.
   =========================================================================== */

.services .service-item,
.service-category-card,
.testimonials .testimonial-item,
.contact .info-item,
.contact .php-email-form {
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: var(--radius-lg);
  box-shadow: var(--sh-sm);
  padding: var(--space-xl);
}

/* Los filetes de acento, fuera. */
.services .service-item { border-top: 1px solid var(--bd); }
.contact .info-item { border-left: 1px solid var(--bd); }

/* Escanear una lista centrada es mas lento, y la consistencia con las demas
   vale mas que la simetria de esta. */
.service-category-card { text-align: left; }


/* ===========================================================================
   UN SOLO HOVER
   ---------------------------------------------------------------------------
   Habia cuatro coreografias distintas mas una animacion infinita en la linea
   del proceso; y el CTA, lo unico que convierte, era deliberadamente estatico.
   Todo se movia menos el boton, asi que lo que se recordaba era la decoracion.
   Una sola respuesta para todo lo que se puede tocar.
   =========================================================================== */

.services .service-item,
.service-category-card,
.testimonials .testimonial-item,
.contact .info-item {
  transition: transform var(--dur-med) var(--ease-out),
              box-shadow var(--dur-med) var(--ease-out);
}

.services .service-item:hover,
.service-category-card:hover,
.testimonials .testimonial-item:hover,
.contact .info-item:hover {
  transform: translateY(-4px);
  box-shadow: var(--sh-md);
}

/* home.css:2150 animaba `letter-spacing` a 1px en hover: el texto CRECE y
   empuja su propio icono. Eso es temblor, no respuesta. */
.service-category-card:hover h3 { letter-spacing: inherit; }

/* La linea del proceso latia con una animacion infinita de 6s. Una animacion
   que no responde a nada no informa nada, y competia por la atencion con la
   unica accion de la seccion. */
.how-we-work .timeline-line { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .services .service-item:hover,
  .service-category-card:hover,
  .testimonials .testimonial-item:hover,
  .contact .info-item:hover { transform: none; }
}


/* ===========================================================================
   PORTAFOLIO — la prueba deja de esconderse
   ---------------------------------------------------------------------------
   Las ocho tarjetas mostraban el LOGOTIPO del cliente y guardaban la captura
   del sistema detras de un click en un lightbox. Un logo no prueba nada: la
   captura si. Ahora la captura es la tarjeta, y el nombre y el tipo de proyecto
   son texto normal debajo —visibles siempre, no dependientes de un hover que en
   tactil no existe—. El logotipo baja al pie como sello.
   =========================================================================== */

.portfolio .portfolio-content {
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  border: 1px solid var(--bd);
  border-bottom: 0;
}

.portfolio .portfolio-content img {
  display: block;
  width: 100%;
  height: auto;
}

.portfolio-pie {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-lg);
  /* Sin altura minima, un pie de dos lineas hacia su tarjeta mas alta que las
     demas y la fila quedaba desigual aunque el layout las alineara arriba. 112
     es 48 de padding mas las dos lineas de descripcion mas holgada. */
  min-height: 112px;
  background: var(--card);
  border: 1px solid var(--bd);
  border-top: 0;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* El logotipo llega en 1024x768 con el sello centrado sobre fondo plano:
   `contain` lo respeta y el recorte cuadrado no le come nada. */
.portfolio-logo {
  width: var(--icon-lg);
  height: var(--icon-lg);
  object-fit: contain;
  border-radius: var(--radius-sm);
  background: var(--panel);
  flex-shrink: 0;
}

.portfolio-pie h3 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.25;
  color: var(--tx);
}

.portfolio-pie p {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.35;
  color: var(--tx2);
}


/* ===========================================================================
   HERO — los indicadores existen y se pueden tocar
   ---------------------------------------------------------------------------
   El `<ol class="carousel-indicators">` estaba VACIO: no se renderizaba nada,
   asi que no habia forma de volver a la primera diapositiva. Ahora que el
   carrusel no avanza solo, son el unico control junto a las flechas, y tienen
   que poder tocarse: la pildora visible mide 28x4, pero el objetivo mide 44.
   =========================================================================== */

.hero .carousel-indicators {
  gap: var(--space-xs);
  margin-bottom: var(--space-lg);
}

.hero .carousel-indicators li { list-style: none; }

.hero .carousel-indicators button {
  width: var(--tap);
  height: var(--tap);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  opacity: 1;
}

.hero .carousel-indicators button span {
  display: block;
  width: 28px;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .38);
  transition: background var(--dur-med) var(--ease-out);
}

.hero .carousel-indicators button.active span,
.hero .carousel-indicators button:hover span { background: #FFFFFF; }

.hero .carousel-indicators button:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: -6px;
  border-radius: var(--radius-sm);
}


/* La tarjeta de portafolio entra en el mismo sistema que las otras cinco: la
   misma sombra en reposo, el mismo hover, la misma respuesta. Antes la imagen
   hacia `scale(1.1)` dentro de su caja mientras el resto del sitio se elevaba
   4px: dos vocabularios de movimiento para la misma clase de elemento. */
.portfolio .portfolio-item {
  transition: transform var(--dur-med) var(--ease-out),
              box-shadow var(--dur-med) var(--ease-out);
}

.portfolio .portfolio-content,
.portfolio-pie { box-shadow: var(--sh-sm); }

.portfolio .portfolio-item:hover {
  transform: translateY(-4px);
}

.portfolio .portfolio-item:hover .portfolio-content,
.portfolio .portfolio-item:hover .portfolio-pie { box-shadow: var(--sh-md); }

.portfolio .portfolio-content:hover img { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .portfolio .portfolio-item:hover { transform: none; }
}

/* Isotope posiciona los items en absoluto y calcula la altura del contenedor.
   El margen negativo que Bootstrap le pone a `.row` para compensar la canaleta
   deja entonces un desfase de 24px arriba que Isotope no reabsorbe, porque los
   hijos ya no estan en flujo. La canaleta la siguen dando los `padding` de las
   columnas, que Isotope si mide. */
.portfolio .isotope-container { margin-top: 0; }

/* Las pestañas de filtro quedaban pegadas a la primera fila de tarjetas. El
   salto de un grupo al siguiente necesita mas hueco que el que hay dentro del
   grupo, o los dos se leen como una sola masa. */
.portfolio .portfolio-filters { margin-bottom: var(--space-2xl); }

@media (max-width: 767px) {
  .portfolio .portfolio-filters { margin-bottom: var(--space-xl); }
  /* En movil el pie no necesita reservar dos lineas: hay ancho de sobra y la
     altura minima solo agrega vacio. */
  .portfolio-pie { min-height: 0; padding: var(--space-md); }
}


/* ===========================================================================
   #about — las cifras son la imagen
   ---------------------------------------------------------------------------
   Esta seccion tuvo cinco imagenes en la columna izquierda y las cinco fallaron
   por la misma razon: ninguna aportaba informacion. La ultima —una pila de
   capturas reales— fallo ademas por fisica: una pagina de escritorio completa
   reducida a 375px de ancho es ilegible, asi que la prueba dejaba de probar.
   Lo que esta seccion tiene para mostrar son sus tres cifras, y estaban a la
   derecha, en cuerpo pequeño, compitiendo con un relleno. Ahora son el elemento
   grafico: una banda de tres, la cifra primero.
   =========================================================================== */

.about .content > h2,
.about .content > p {
  max-width: 46rem;
  margin-inline: auto;
  text-align: center;
}

.about .content > p { margin-bottom: var(--space-3xl); }

.about .content ul.pruebas {
  display: grid;
  gap: var(--space-2xl) var(--space-xl);
  margin: 0;
}

@media (min-width: 768px) {
  .about .content ul.pruebas { grid-template-columns: repeat(3, 1fr); }
}

/* Se van el filete, el padding y la rejilla de tres columnas que servian a la
   version en lista. Aqui cada prueba es un bloque centrado. */
.about .content ul.pruebas li {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-sm);
  padding: 0;
  border-top: 0;
}

/* El orden visual es icono, cifra, frase. En el DOM la cifra va al final para
   que un lector de pantalla escuche primero de que se trata y despues el dato;
   `order` solo mueve lo visual. */
.about .content ul.pruebas .ic { order: 1; margin: 0; }
.about .content ul.pruebas .cifra { order: 2; }
.about .content ul.pruebas li > div { order: 3; }

.about .content ul.pruebas .cifra {
  grid-column: auto;
  grid-row: auto;
  text-align: center;
  margin: 0;
}

.about .content ul.pruebas .cifra b {
  font-size: var(--text-4xl);
  line-height: 1;
}

.about .content ul.pruebas .cifra span { margin-top: var(--space-xs); }

.about .content ul.pruebas p { max-width: 34ch; }

.about .cierre {
  margin-top: var(--space-3xl);
  text-align: center;
}


/* ===========================================================================
   Empresas que confian — el logo del cliente, no un icono de stock
   ---------------------------------------------------------------------------
   La seccion cuyo unico trabajo es dar confianza mostraba `bi-building`,
   `bi-heart-pulse` y `bi-shop`: iconos genericos que no prueban nada y que
   señalan justamente que no se consiguio el logo. Los cinco logos estaban en
   el repo desde el principio, en img/portfolio.
   =========================================================================== */

/* Sello de cliente. La baldosa es 4:3 y no cuadrada, y esa es toda la
   correccion.

   Los ocho archivos son lockups anchos de 1024x768 con el fondo de la marca a
   sangre: azul en Cerrajeria y Onos, naranja en Ghost, negro en Take One. En
   una baldosa cuadrada con `contain`, padding y fondo blanco, la imagen se
   dibujaba 36x27 en el centro de un hueco de 48x48 — un rectangulo de color
   flotando con dos franjas blancas desiguales arriba y abajo, y el radio de
   14px mordiendole las esquinas. Se leia como un logo mal recortado, que es
   justo lo contrario de lo que una franja de confianza tiene que transmitir.

   `cover` no es alternativa: recorta los lados del 4:3 y a Innovasole le come
   "de Centroamerica S.A." y a Ghost las letras de los extremos. Con la baldosa
   en la proporcion real del archivo, `contain` la llena exacta: el logo entero,
   su propio fondo hasta el borde, y nada que recortar. */
.portfolio-logo,
.logo-marca {
  width: 64px;
  height: 48px;
  object-fit: contain;
  /* Radio corto: con el contenido llegando al borde, 14px muerde el logo. */
  border-radius: var(--radius-sm);
  border: 1px solid var(--bd);
  background: var(--card);
  flex-shrink: 0;
}


/* ===========================================================================
   MARQUESINA DE CLIENTES
   ---------------------------------------------------------------------------
   Dos pistas identicas una detras de otra, las dos desplazandose el 100% de su
   propio ancho: cuando la primera termina de salir, la segunda esta exactamente
   donde estaba la primera al empezar, asi que el bucle no tiene salto. La
   segunda va `aria-hidden` en el markup para que un lector de pantalla no lea
   los ocho clientes dos veces.
   =========================================================================== */

.clientes-titulo {
  margin: 0 0 var(--space-xl);
  text-align: center;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--tx3);
}

.marquesina {
  display: flex;
  overflow: hidden;
  /* Los extremos se desvanecen en vez de cortarse en seco: sin esto el ultimo
     logo aparece y desaparece de golpe contra el borde del viewport. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

.marquesina-pista {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  gap: var(--space-3xl);
  margin: 0;
  padding: 0 var(--space-3xl) 0 0;
  list-style: none;
  animation: desfile 46s linear infinite;
}

/* Detenerla al pasar el cursor es lo que permite leer un nombre concreto. */
.marquesina:hover .marquesina-pista,
.marquesina:focus-within .marquesina-pista { animation-play-state: paused; }

@keyframes desfile {
  to { transform: translateX(-100%); }
}

@media (prefers-reduced-motion: reduce) {
  /* Sin movimiento no puede quedar contenido inalcanzable: la pista pasa a
     poder desplazarse a mano. */
  .marquesina { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
  .marquesina-pista { animation: none; }
  .marquesina-pista[aria-hidden="true"] { display: none; }
}

.cliente {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  white-space: nowrap;
}

.cliente span {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--tx);
}

.logo-marca {
  filter: grayscale(1);
  transition: filter var(--dur-med) var(--ease-out);
}

.cliente:hover .logo-marca { filter: none; }

.portfolio.section { padding-bottom: var(--space-2xl); }


/* ===========================================================================
   CABECERA
   ---------------------------------------------------------------------------
   La barra es `fixed-top` en las 94 paginas y nadie la habia auditado. Los
   defectos de abajo estan medidos sobre la hoja de la plantilla, no estimados.
   =========================================================================== */

/* 28. El header media 90px de alto en movil -15px de relleno arriba, un logo
   de 60px, 15px abajo- y no habia UNA sola media query que lo tocara: en una
   pantalla de 740px eso es el 12% del alto, fijo, permanente, en cada scroll
   de cada pagina. Con el logo a 44px (129px de ancho, sigue legible) y 10px de
   relleno son 64px, que es lo que mide la barra de Safari en iOS.

   El corte va en 1199px, que es donde la plantilla cambia a menu hamburguesa:
   el alto de la barra y la forma de la navegacion cambian en el mismo punto. */
@media (max-width: 1199px) {
  .header { padding: 10px 0; }
  .header .logo img { max-height: 44px; }

  /* Consecuencia directa: el ancla de seccion. Con el header en 64px, los 66px
     de la plantilla dejan 2px de aire entre la barra y el borde de la seccion a
     la que acaba de saltar el visitante. 72px dejan 8. Aplica tambien a #main,
     que es el destino de "Saltar al contenido principal": sin esto el atajo
     entrega el h1 tapado por la barra. */
  section, .section, #main { scroll-margin-top: 72px; }

  /* El panel del menu abria a 60px del borde superior, o sea 30px POR DEBAJO
     del arranque del header de 90px: se leia como una prolongacion de la barra
     y no como una capa nueva. Con 64px de header, 72px lo separan. */
  .navmenu ul { inset: 72px 16px 16px; }

  /* La X de cerrar estaba clavada en top:15px, que era su centro contra el
     header de 90px. Contra 64px el centro cae en 10px. */
  .mobile-nav-active .mobile-nav-toggle { top: 10px; }

  /* Los enlaces del panel median 43px de alto (10 + 17px de texto + 10):
     un pixel por debajo del minimo tactil, en el unico menu del sitio. */
  .navmenu ul a { min-height: var(--tap); }

  /* El boton flotante de WhatsApp esta en z-index 99998, por encima del velo
     del menu (9997): con el menu abierto quedaba un circulo verde de 56px
     tapando la esquina inferior derecha del panel. */
  .mobile-nav-active .whatsapp-float { display: none; }
}

/* 29. La subraya que marca el enlace de navegacion activo se pinta con
   --accent-color. La barra NO lleva la clase `.dark-background`, asi que ese
   token resuelve al tema claro: #2A51A3 sobre el #060606 de la barra = 2.71:1,
   por debajo del 3:1 que WCAG 1.4.11 pide para un indicador de estado. Ademas
   no coincidia con el color al que el mismo enlace pone su texto en hover
   (--nav-hover-color), de modo que la palabra y su subrayado decian dos cosas
   distintas. Un solo token: #52C3C4 sobre #060606 = 9.61:1.

   Solo en escritorio: dentro del panel movil el fondo es blanco y ahi ese
   turquesa da 2.11:1. Alli la subraya se queda con el azul de marca (7.49:1). */
@media (min-width: 1200px) {
  .navmenu > ul > li > a::after { background-color: var(--nav-hover-color); }
}

/* 30. Ningun elemento de la cabecera tenia aro de foco propio: el logo -primer
   tabulador de las 94 paginas- y los cinco enlaces del menu dependian del aro
   por defecto del navegador sobre una barra casi negra. La lista de
   :focus-visible de la plantilla (home.css:2204) los habia dejado fuera. */
.header .logo:focus-visible,
.header .navmenu > ul > li > a:focus-visible {
  outline: var(--focus-ring-width) solid var(--nav-hover-color);  /* 9.61:1 sobre la barra */
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

@media (max-width: 1199px) {
  /* Dentro del panel el fondo es blanco y el turquesa cae a 2.11:1. */
  .header .navmenu > ul > li > a:focus-visible {
    outline-color: var(--brand);      /* #2A51A3 sobre #FFFFFF = 7.49:1 */
    outline-offset: -3px;
  }
}

/* 31. "Saltar al contenido principal" es el unico atajo de teclado del sitio y
   estaba en el flujo normal del documento, sin `position` ni `z-index`, encima
   de un header `fixed-top` con z-index 997. Al recibir el foco aparecia DEBAJO
   de la barra -opaca (rgba(0,0,0,.92)) en cuanto la pagina esta desplazada-:
   invisible justo en el momento en que se usa.

   El aro de foco va en doble filo por caja y no en `outline` porque este enlace
   aparece sobre lo que haya: el hero oscuro de la home o el fondo claro de una
   pagina legal. Un aro de un solo color falla en uno de los dos. Blanco
   (20.26:1 sobre el hero) mas --tx #14161C (17.45:1 sobre --bg) funciona en ambos. */
.saltar-contenido:focus {
  position: fixed;
  top: var(--space-sm);
  left: var(--space-sm);
  z-index: 10000;               /* header 997, velo del menu movil 9998 */
  width: auto;
  height: auto;
  padding: var(--space-sm) var(--space-md);
  background: var(--btn-grad);
  color: var(--btn-txt);        /* blanco sobre #3567D8 = 5.14:1 y sobre #244FB0 = 7.45:1 */
  border-radius: var(--radius);
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1.35;
  text-decoration: none;
  /* El filo interior por caja, el exterior por outline: asi no hay que apagar
     el aro del navegador para conseguir los dos colores. */
  box-shadow: 0 0 0 2px #FFFFFF, var(--sh-lg);
  outline: var(--focus-ring-width) solid var(--tx);   /* #14161C, 17.45:1 sobre --bg */
  outline-offset: var(--focus-ring-offset);
}

@media (forced-colors: active) {
  /* En alto contraste la sombra se descarta y solo queda el filo exterior. */
  .saltar-contenido:focus { outline-color: CanvasText; }
}


/* ===========================================================================
   PIE
   ---------------------------------------------------------------------------
   El pie es `.dark-background`, o sea #060606. Todos los ratios de este bloque
   estan calculados contra ese fondo.
   =========================================================================== */

/* 32. Jerarquia. Los titulos de columna median 16px contra un cuerpo de 14px:
   ratio 1.14, por debajo del 1.15 que este mismo archivo fijo como minimo para
   que dos niveles se lean como niveles distintos (ver el bloque de #about). El
   resultado eran tres columnas planas -titulo y enlaces al mismo peso visual- y
   una cuarta, la del logo, sin titulo, que parecia rota al lado de ellas.

   Los titulos pasan a etiqueta, con el mismo tratamiento que `.clientes-titulo`
   ya usa en este archivo: 12px, versalita, tracking .12em. Dejan de competir
   con los enlaces -que es lo que la gente viene a buscar en un pie- y la
   columna del logo deja de ser la rara, porque las etiquetas ya no son sus
   pares visuales. #948F88 sobre #060606 = 6.31:1.

   `padding-bottom` se va: era el hueco del filete decorativo que la plantilla
   ya no dibuja (queda su `position: relative` huerfano en home.css). */
.footer h4 {
  margin: 0 0 var(--space-md);
  padding-bottom: 0;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--tx3);
}

/* 33. Los cinco iconos sociales median 40x40, 4px por debajo del minimo tactil,
   y se separaban con `margin-right: 10px` aplicado a TODOS -incluido el
   ultimo-, que dejaba un hueco muerto al final de la fila. Cinco de 44 con 8 de
   separacion son 252px: entran en los 327px utiles de una pantalla de 375. El
   icono sigue en 16px; lo que crece es el objetivo, no el dibujo. */
.footer .social-links {
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.footer .social-links a {
  width: var(--tap);
  height: var(--tap);
  margin-right: 0;
}

/* 34. El telefono y el correo son enlaces en linea dentro de un parrafo: su
   objetivo tactil era la caja de la linea, unos 21px de alto, por debajo de los
   24px de WCAG 2.5.8. Con interlineado 1.9 la caja mide 26.6px a 14px de texto
   y 30.4px a los 16px de movil, y el enlace pasa a inline-block para ocuparla
   entera. Los dos objetivos quedan por encima del minimo sin solaparse. */
.footer .footer-contact p {
  line-height: 1.9;
  margin-bottom: 0;
}

.footer .footer-contact a {
  display: inline-block;
  min-height: 24px;
}

/* 35. El boton "Suscribete" -lo unico que convierte en todo el pie- se pintaba
   con `background: var(--accent-color)` y `color: var(--contrast-color)`. Sobre
   `.dark-background` eso resuelve a BLANCO sobre #8FB0E8: 2.20:1, menos de la
   mitad del 4.5:1 de AA. Y el hover lo empeoraba de otra forma: aclaraba el
   fondo un 20% contra el negro (#748EBB) y el blanco caia a 3.32:1, o sea que
   ni el reposo ni el hover eran legibles.

   Entra en el mismo boton que el resto del sitio (--btn-grad / --btn-txt), que
   por diseno NO gira con el tema: blanco sobre #3567D8 = 5.14:1 arriba y sobre
   #244FB0 = 7.45:1 abajo. El hover sube a 5.82:1 y 9.24:1. */
.footer .footer-newsletter .newsletter-form { border-radius: var(--radius); }

/* Un <input> no baja de su ancho de contenido minimo, que sale del atributo
   `size` (20 caracteres, ~160px). Con el boton en 128px eso son 302px dentro de
   los 296px utiles de una pantalla de 320: la pildora desbordaba. */
.footer .footer-newsletter .newsletter-form input[type="email"] { min-width: 0; }

.footer .footer-newsletter .newsletter-form input[type="submit"] {
  background: var(--btn-grad);
  color: var(--btn-txt);
  border: 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 0 var(--space-lg);
  font-weight: 600;
  cursor: pointer;
  min-width: var(--tap);
}

.footer .footer-newsletter .newsletter-form input[type="submit"]:hover {
  background: var(--btn-grad-h);
}

/* 36. El placeholder del campo se quedaba con el del navegador. Firefox lo
   deriva de `color` al 54% (#8C8C8C, 6.03:1 - pasa), pero Chrome usa su gris de
   sistema, pensado para campos claros, y sobre este campo negro no llega a AA.
   Se fija con un token del tema oscuro; `opacity: 1` porque Firefox aplica la
   suya por encima del color. */
.footer .footer-newsletter .newsletter-form input[type="email"]::placeholder {
  color: var(--tx3);            /* #948F88 sobre #060606 = 6.31:1 */
  opacity: 1;
}

/* 37. El autorrelleno de Chrome repinta el fondo del campo con su color claro
   de sistema y deja encima el texto blanco heredado del pie: un correo guardado
   salia blanco sobre casi blanco. La sombra interior es la unica manera de
   recuperar el fondo, y `-webkit-text-fill-color` la unica de recuperar la
   tinta. */
.footer .footer-newsletter .newsletter-form input:-webkit-autofill,
.footer .footer-newsletter .newsletter-form input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--default-color);          /* #FFFFFF, 20.26:1 sobre #060606 */
  -webkit-box-shadow: 0 0 0 100px var(--background-color) inset;
  caret-color: var(--default-color);
}

/* 38. Ningun enlace del pie tenia aro de foco propio: 15 enlaces de navegacion,
   las dos formas de contacto directo y el envio del formulario dependian del
   aro por defecto del navegador sobre #060606. #63CCCD -el turquesa del tema
   oscuro, que este archivo ya usa para el foco sobre superficie de marca- da
   10.66:1. */
.footer .footer-links ul a:focus-visible,
.footer .social-links a:focus-visible,
.footer .footer-contact a:focus-visible,
.footer .footer-about .logo:focus-visible,
.footer .footer-newsletter .newsletter-form input[type="submit"]:focus-visible {
  outline: var(--focus-ring-width) solid var(--teal);     /* #63CCCD sobre #060606 = 10.66:1 */
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

/* 39. Estados del formulario. El mensaje de exito era blanco sobre #059652 =
   3.82:1: la unica pantalla que confirma que la suscripcion funciono no cumplia
   AA. El de error estaba en 4.91:1, apenas encima del limite. Los dos pasan a
   los semanticos del sistema -que ya existian y no se usaban aqui-: blanco
   sobre --ok #0D7A4B = 5.38:1 y sobre --dg #C53A31 = 5.22:1.

   Va sin acotar a `.footer` a proposito: es el mismo componente que usa el
   formulario de #contacto, con el mismo fallo y la misma correccion. */
.php-email-form .sent-message { background: var(--ok); }
.php-email-form .error-message { background: var(--dg); }

.php-email-form .sent-message,
.php-email-form .error-message,
.php-email-form .loading {
  border-radius: var(--radius);
  font-size: var(--text-base);
}

/* 40. El texto que explica por que no se pudo suscribir el correo estaba en
   13px, el tamano mas pequeno del pie. Es la unica frase que dice que corregir.
   Su color (#ff6b6b, 7.30:1) ya pasaba y no se toca. */
.footer .php-email-form .invalid-feedback { font-size: var(--text-sm); }

/* 43. Las tarjetas de servicio de la home pasaron de <i class="bi"> a los
   iconos propios. home.css pintaba el icono con `.icon i { color: accent;
   font-size: 40px }`, un selector que un <svg> no cumple: sin esto el icono
   salia gris y a 24px. El tamano lo pone `.ic-lg` en la plantilla; aca solo
   va el color, que es lo que el selector viejo aportaba. */
.services .service-item .icon .ic { color: var(--accent-color); }

/* La flecha quedaba pegada al texto: home.css la separaba con
   `.readmore i { margin-left: 8px }`, otro selector que un <svg> no cumple.
   Se resuelve con el gap del flex que la propia `.readmore` ya declara, que
   ademas no depende de que el icono sea un <i> o un <svg>. */
.services .service-item .readmore { gap: var(--space-sm); }

/* 45. La aclaracion de la guardia se pinta con el mismo peso que el horario,
   y las tres lineas se leen como si fueran el mismo dato. Es una nota al pie
   del horario, no una tercera franja horaria. --muted-color (7.56:1) es el
   unico gris del sistema que pasa AA a este tamano. */
.contact .info-item p.small {
  margin-top: var(--space-sm);
  font-size: var(--text-sm);
  color: var(--muted-color);
}


/* ===========================================================================
   PROCESO — la linea deja de atravesar los nodos y los iconos se ven
   ---------------------------------------------------------------------------
   Cuatro defectos en el mismo componente, que aparece en 12 paginas.
   =========================================================================== */

/* 46. Los iconos salian en color de texto sobre el gradiente azul.
   home.css los pinta con `.node-dot i { color:#fff; font-size:22px }` y las
   plantillas ya migraron a <svg class="ic">: ningun <svg> cumple ese selector,
   asi que el icono heredaba `currentColor` —el gris oscuro del cuerpo— y se
   dibujaba casi invisible dentro del circulo. Es el mismo defecto que este
   archivo corrigio en las tarjetas de servicio.
   26px y no 24: el circulo mide 56 y el icono se veia perdido dentro. */
.node-dot .ic {
  width: 26px;
  height: 26px;
  color: #fff;
}

/* 47. La linea horizontal cruzaba por dentro de los nodos.
   `.node-ring` es un aro de 80px con el interior transparente alrededor de un
   punto de 56: quedan 10px de corona por los que la linea —que pasa por
   detras— se veia perfectamente. El nodo parecia atravesado por un cable.
   El fondo sale de la variable de la seccion, asi que sirve igual sobre el
   blanco de la home y sobre el #f9f9f9 de `.light-background`. */
.node-ring { background: var(--background-color); }

/* 48. La linea no pasaba por el centro de los nodos.
   `top: 52px` es un numero magico que no coincide con nada: el track abre con
   `padding: 20px 0` y el aro mide 80, asi que su centro esta en 60. La linea
   corria 8px por encima y entraba a los circulos en diagonal visual.
   59 = 20 de padding + 40 de medio aro - 1 de media linea. */
.how-we-work .timeline-line,
.how-we-work.light-background .timeline-line { top: 59px; }

/* 49. Los extremos de la linea no caian en el centro de los nodos extremos.
   Con N columnas iguales el centro de la primera esta en 100%/(2N): 16.67%
   para tres nodos, no el 16% redondeado que habia. Se nota porque la linea
   asomaba fuera del ultimo circulo. */
@media (min-width: 992px) {
  .how-we-work.light-background .timeline-line {
    left: calc(100% / 6);
    right: calc(100% / 6);
  }
}

/* 50. El numero de columnas dependia del COLOR DE FONDO de la seccion.
   home.css fija `repeat(4, 1fr)` y servicios.css lo baja a 3 colgando de
   `.light-background`, que es una clase de color. Una pagina de tres pasos sin
   esa clase habria dibujado tres nodos apretados a la izquierda con una cuarta
   columna vacia. Con `grid-auto-flow: column` las columnas las cuenta el
   propio grid a partir de cuantos nodos haya. */
.timeline-steps,
.how-we-work.light-background .timeline-steps {
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}

@media (max-width: 991px) {
  /* En movil los nodos van apilados: se vuelve a filas. */
  .timeline-steps,
  .how-we-work.light-background .timeline-steps {
    grid-auto-flow: row;
    grid-auto-columns: auto;
  }
}


/* 51. El boton "Suscribete" se salia de su propia caja.
   El campo de correo lleva `width: 100%` dentro de un contenedor `display:
   flex` (home.css:707-710). Un flex item de ancho 100% se lleva la caja
   entera, y como los items no se encogen por debajo de su contenido
   (`min-width: auto` es el default), el boton quedaba empujado fuera por la
   derecha. El parche era un margen negativo de -8px que ni siquiera alcanzaba:
   el contenedor suma 8 de padding MAS 1 de borde, asi que el boton se
   desbordaba y su esquina redondeada no coincidia con la del contenedor.

   Se quita el parche entero. Sin padding en el contenedor, el campo y el boton
   lo llenan de borde a borde y el radio del boton es el mismo del contenedor.
   `min-width: 0` es lo que autoriza al campo a encogerse, que es la causa de
   raiz.

   Es el unico elemento que convierte en el pie, y estaba roto en las 94
   paginas. */
.footer .footer-newsletter .newsletter-form {
  padding: 0;
  border-radius: var(--radius);
  align-items: stretch;
}

.footer .footer-newsletter .newsletter-form input[type="email"] {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  padding: 10px 12px;
}

.footer .footer-newsletter .newsletter-form input[type="submit"] {
  flex: 0 0 auto;
  margin: 0;
  padding: 10px 18px;
  white-space: nowrap;
  border-radius: 0 var(--radius) var(--radius) 0;
}


/* 52. La lista de servicios de las categorias tenia DOS disenos peleandose y
   ganaba el que nadie escribio.

   `servicios.css:100` define el tratamiento de pestaña: barra de acento solo
   en la activa y al pasar el cursor, fondo tenue, 12px de aire. Nunca se vio.
   `home.css:1963` —la plantilla Hidayah— trae `.service-details .services-list
   a`, tres selectores contra dos, y gana: pinta una barra gris al 30% en TODAS
   las entradas, incluidas las nueve inactivas, y les mete `margin: 20px 0`.

   Por eso la columna se lee como nueve palitos sueltos y no como una lista con
   una seleccion: si todo esta marcado, nada esta marcado. La barra es la señal
   de "estas aqui" y hay que gastarla en un solo elemento (Von Restorff).

   Se iguala la especificidad desde esta hoja, que carga ultima, y queda un
   solo tratamiento para los dos selectores. */
.service-details .services-list a,
.services-list .service-tab {
  display: block;
  margin: 0 0 2px;
  padding: 10px 16px;
  line-height: 1.35;
  border-left: 3px solid transparent;
  border-radius: 0 var(--radius) var(--radius) 0;
  color: var(--tx2);
  text-decoration: none;
  transition: background-color var(--transition-base), border-color var(--transition-base);
}

.service-details .services-list a:hover,
.services-list .service-tab:hover {
  background: color-mix(in srgb, var(--accent-color), transparent 95%);
  border-left-color: color-mix(in srgb, var(--accent-color), transparent 50%);
  color: var(--tx);
}

.service-details .services-list a.active,
.services-list .service-tab.active {
  background: color-mix(in srgb, var(--accent-color), transparent 90%);
  border-left-color: var(--accent-color);
  color: var(--accent-color);
  font-weight: 600;
}

/* El contenedor traia `padding: 10px 30px` de cuando las entradas no tenian
   padding propio. Sumado a los 16px de cada una, el texto arrancaba a 46px del
   borde de la caja. */
.service-details .services-list {
  padding: var(--space-sm);
  border-radius: var(--radius-lg);
  /* Sin sombra: es un indice de navegacion, no una tarjeta que flota sobre la
     pagina. La sombra la hacia competir con el contenido de al lado, que es
     lo que el visitante vino a leer. */
  box-shadow: none;
  margin-bottom: 0;
}


/* ===========================================================================
   53. PAGINA DE CATEGORIA — lo que vende deja de estar bajo el pliegue
   ---------------------------------------------------------------------------
   Medido sobre /servicios/redes a 1440x900, antes de este bloque:

     barra negra .......... 0 a 193      (193px para dos palabras)
     foto de stock ........ 233 a 593    (360px)
     titulo del servicio .. y = 613
     primer parrafo ....... y = 652

   El visitante gastaba 553px —dos tercios de la pantalla de un portatil— en
   una barra negra y una foto de archivo antes de leer una sola palabra de lo
   que vendemos. La foto sola pesaba 360px; toda la informacion de la columna
   izquierda (ruta, descripcion y contador) pesa 144.

   No se toca el orden del DOM: son 43 paneles de servicio con estructuras que
   no comparten un patron unico, y reordenarlos por CSS es fragil. Se corrige
   el reparto de espacio, que es de donde sale el problema.
   =========================================================================== */

/* La seccion arrancaba con 40px de aire justo debajo de una barra que ya
   aportaba 26 por abajo. */
.service-details.section { padding-top: var(--space-lg); }

/* La foto es apoyo, no argumento: 260 es media pantalla de portatil menos la
   barra, y deja el titulo del servicio y su primer parrafo por encima del
   pliegue. */
.service-details .services-img {
  max-height: 260px;
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-lg);
}

/* En movil las dos columnas se apilan en el orden del DOM, y el DOM pone
   primero la columna lateral: el visitante tenia que pasar la descripcion, el
   contador y los OCHO enlaces del indice antes de leer una linea del servicio
   que vino a ver. En escritorio no se nota porque van una al lado de la otra.
   El indice no desaparece, pasa a ser lo que es en movil: lo que se mira
   despues de leer, para saltar a otro servicio. */
@media (max-width: 991px) {
  .service-details .col-lg-8 { order: 1; }
  .service-details .col-lg-4 { order: 2; }
}

/* 44. Enlace de cierre de seccion. Es el unico camino de la home a /servicios,
   la pagina que lista los 34, y hasta ahora no existia ninguno: ni el menu, ni
   el pie, ni la home la enlazaban. No es un boton — la seccion ya tiene su
   accion primaria mas abajo y dos botones compitiendo es lo que este archivo
   viene deshaciendo. */
.enlace-seccion {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--accent-color);
  text-decoration: none;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  transition: gap var(--transition-base), background-color var(--transition-base);
}

.enlace-seccion:hover {
  color: var(--accent-color);
  gap: var(--space-sm);
  background-color: color-mix(in srgb, var(--accent-color), transparent 92%);
}

.enlace-seccion:focus-visible {
  outline: 2px solid var(--accent-color);
  outline-offset: 3px;
}

@media (max-width: 767px) {
  /* 41. Todo el pie heredaba 14px: los 15 enlaces de navegacion, la direccion,
     el parrafo del newsletter y el copyright. Es el mismo defecto que el punto
     8 corrigio en el hero y en las tarjetas, sin corregir aca - y en el pie
     estan las dos formas de contacto directo del sitio. La etiqueta de columna
     sube a 14px: sigue siendo el nivel de abajo, pero deja de ser diminuta. */
  .footer { font-size: var(--text-base); }
  .footer .footer-about p { font-size: var(--text-base); }
  .footer h4 { font-size: var(--text-sm); }

  /* 42. El logo del pie se dibujaba a 293x100: en una pantalla de 375px eso es
     el 78% del ancho util gastado en repetir una marca que ya esta arriba, en
     una barra fija que no se va nunca. A 64px de alto son 188px. */
  .footer .footer-about .logo img { max-height: 64px; }
  .footer .footer-about .logo { margin-bottom: var(--space-md); }
}


/* ===========================================================================
   54. AVISO DE COOKIES
   ---------------------------------------------------------------------------
   Una barra al pie, no un modal que tape la pagina: el visitante puede leer y
   decidir con el contenido delante, en vez de tener que despachar una ventana
   antes de saber si el sitio le interesa.

   Los dos botones pesan lo mismo a proposito. Rechazar en gris claro, o como
   un enlace de texto al lado de un boton azul, es la forma habitual de
   fabricar un consentimiento que no lo es. Aqui el que acepta lleva el color
   de marca y el que rechaza lleva borde, y los dos tienen el mismo tamano,
   el mismo tipo y el mismo objetivo tactil.
   =========================================================================== */

.aviso-cookies {
  position: fixed;
  /* 1080 NO estaba por encima del boton flotante de WhatsApp, que vive en
     99998 (home.css): el circulo verde se pintaba sobre el aviso. Este valor
     hace cierta la intencion. El solape se resuelve abajo apartando el boton,
     no apilando; esto es la red por si esa regla no llegara a aplicarse. */
  z-index: 99999;
  left: var(--space-md);
  right: var(--space-md);
  bottom: var(--space-md);
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: var(--card);
  border: 1px solid var(--bd);
  border-radius: var(--radius-lg);
  box-shadow: var(--sh-md);
}

.aviso-cookies-texto {
  flex: 1 1 260px;
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--tx2);
}

.aviso-cookies-texto a {
  color: var(--accent-color);
  text-decoration: underline;
}

.aviso-cookies-acciones {
  display: flex;
  gap: var(--space-sm);
  flex: 0 0 auto;
}

.btn-cookies {
  /* 44px de alto: el objetivo tactil minimo. Un boton de consentimiento que
     hay que apuntar con precision empuja a aceptar por comodidad. */
  min-height: 44px;
  padding: 0 var(--space-lg);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: pointer;
  transition: background-color var(--transition-base), border-color var(--transition-base);
}

.btn-cookies-si {
  border: 1px solid var(--brand);
  background: var(--brand);
  color: #FFFFFF;
}

.btn-cookies-si:hover { background: var(--brand-h); border-color: var(--brand-h); }

.btn-cookies-no {
  border: 1px solid var(--bd);
  background: transparent;
  color: var(--tx);
}

.btn-cookies-no:hover { background: color-mix(in srgb, var(--tx), transparent 94%); }

.btn-cookies:focus-visible {
  outline: 2px solid var(--accent-color);
  outline-offset: 2px;
}

@media (max-width: 575px) {
  /* En movil los botones ocupan una fila propia y se reparten el ancho: en
     una sola linea junto al texto quedaban en 90px y compitiendo con el
     boton flotante de WhatsApp. */
  .aviso-cookies { padding: var(--space-md); }
  .aviso-cookies-acciones { flex: 1 1 100%; }
  .btn-cookies { flex: 1 1 0; padding: 0 var(--space-md); }
}

/* El circulo de WhatsApp (56px en la esquina inferior derecha, `position:
   fixed`) caia justo encima del boton "Aceptar" del aviso: dos controles
   superpuestos, y el de arriba lleva a otra aplicacion. Quien queria aceptar
   y apuntaba a la derecha del boton se iba a WhatsApp.

   El corte no es un tamano de dispositivo sino la geometria de estas dos
   cajas, asi que sale de medirlas y no del breakpoint mas cercano. El aviso
   mide como mucho 720px centrados y el boton empieza en vw-71; medido en el
   navegador, "Aceptar" y el circulo se tocan hasta 820px (3px de solape) y a
   840 ya quedan libres por 7px. Siete pixeles entre dos controles no son un
   hueco: a 880 son 27, del orden de --space-md, que es el aire minimo que el
   resto de la hoja deja entre cosas pulsables. De ahi el 880. Por encima el
   CTA no estorba a nadie y se queda siempre visible.

   Se aparta el boton y no se apila el aviso encima porque apilar deja igual
   el problema real: el objetivo tactil de "Aceptar" seguiria mordido por un
   control invisible. El aviso se despacha con un toque y el boton vuelve;
   es el mismo trato que ya recibe con el menu movil abierto (bloque 29). */
@media (max-width: 880px) {
  #aviso-cookies:not([hidden]) ~ .whatsapp-float { display: none; }
}

/* 53. Identidad tipografica. El sitio referenciaba CUATRO familias sin que
   ninguna decision explicara cual era cual: Inter en el cuerpo, Raleway en los
   titulos, Poppins en la navegacion y Montserrat en las cuatro paginas de
   estado. Cuatro sans-serif de proposito general no se leen como un sistema:
   se leen como cuatro sitios distintos pegados, que es exactamente lo que se
   reporto como "rompe la identidad visual".

   Quedan DOS, y el criterio es el rol, no el gusto:

   - Inter (--default-font) para texto y UI. Es una neo-grotesca dibujada para
     pantalla: x-height alta, aperturas amplias y cifras tabulares. Sostiene
     los 13-16px, que es donde vive el 90% del texto del sitio.
   - Raleway (--heading-font) para titulos. Tiene contraste de trazo y
     terminales que Inter no tiene, y por eso a 32px o mas se distingue de ella
     de un vistazo. Esa distancia es la que produce jerarquia percibida.

   Poppins se elimina porque no aportaba esa distancia: es una geometrica de
   trazo monolineal con el mismo esqueleto de proposito general que Inter, y a
   los 13px del menu la diferencia no se percibe como jerarquia sino como
   inconsistencia. Se usaba en exactamente DOS reglas -el menu de escritorio y
   el desplegable movil de home.css- y costaba una familia entera de cuatro
   pesos en la peticion a Google Fonts. Montserrat se elimina en 404.css.

   Resultado medido: la peticion a fonts.googleapis.com pasa de 3 familias / 15
   pesos a 2 familias / 7 pesos, y las cuatro paginas de estado dejan de hacer
   una segunda peticion propia a la API v1 (la que no admite display=swap y por
   tanto bloquea el render). Es tiempo de render, y el tiempo de render es
   factor de posicionamiento.

   Se redefinen aqui y no en home.css porque home.css es la plantilla Hidayah:
   la decision de marca vive en la capa propia. */
:root {
  --heading-font: "Raleway", "Segoe UI", system-ui, sans-serif;
  /* La navegacion es UI, no titulo: usa la misma tinta tipografica que el
     cuerpo. Alias y no un valor propio, para que no vuelva a divergir. */
  --nav-font: var(--default-font);
}

/* 54. Raleway se estaba usando como fuente de TEXTO en dos parrafos de 14px
   -la descripcion de la empresa en el pie y el rotulo de cada cifra de
   #stats-, y ahi trabaja en contra: su contraste de trazo y su x-height, que
   son justo lo que la hace buena a 32px, la vuelven mas dificil de leer a 14px
   que la Inter del parrafo de al lado. El resultado es que dos parrafos
   vecinos del mismo tamano se ven con fuentes distintas sin que nada lo
   justifique. La fuente de titulos se reserva para titulos. */
.footer .footer-about p,
.stats .stats-item p {
  font-family: var(--default-font);
}


/* ===========================================================================
   55. VISTA DE SERVICIO — medida de lectura
   ---------------------------------------------------------------------------
   Medido a 1440: el cuerpo del panel corre a 856px de ancho con texto de 15px,
   o sea **114 caracteres por linea**. El rango legible esta entre 45 y 75, y
   el techo del criterio AAA de WCAG (1.4.8) es 80. Estabamos un 43% por
   encima: a esa medida el ojo pierde el renglon al volver al margen
   izquierdo, y estas paginas son justamente las que hay que leer enteras.

   El tope va sobre el TEXTO, no sobre la columna: si se pusiera en el
   contenedor, la imagen de cabecera y las tarjetas de "Mas servicios" se
   encogerian con el, y esas si tienen que ocupar el ancho disponible.

   `ch` y no `px` porque la unidad del problema son los caracteres: si manana
   cambia el tamano o la familia del cuerpo, el limite se ajusta solo.
   =========================================================================== */

.service-pane article > p,
.service-pane article > ul,
.service-pane article > ol,
.service-pane article > details {
  /* 64ch y no 68: la unidad `ch` mide el ancho del cero, mas ancho que la
     media de las minusculas, asi que 68ch daban 82 caracteres reales medidos
     en el navegador. Con 64ch la medida cae a 77, dentro del techo AAA. */
  max-width: 64ch;
}

/* Los encabezados pueden ir algo mas anchos: son una linea o dos y el ojo no
   tiene que volver al margen tantas veces. */
.service-pane article > h3,
.service-pane article > h4 {
  max-width: 80ch;
}


/* 56. Estado de carga del panel de servicio.
   La pestana ya se marca al instante del clic (serviceTabs.js), pero el panel
   seguia mostrando el contenido anterior sin ninguna senal de que venia otro.
   Con la red lenta de una oficina, el visitante ve la pestana nueva marcada y
   el texto viejo debajo: parece que el sitio se equivoco.

   Se atenua el panel mientras carga. No se vacia ni se pone un esqueleto: el
   contenido anterior sigue siendo legible mientras llega el siguiente, que es
   preferible a un hueco en blanco. */
/* La clase la pone serviceTabs.js sobre el contenedor del panel, que es el
   `.col-lg-8` de la rejilla — no `.service-panes-container`. Se selecciona por
   la clase de estado y no por el contenedor concreto, para que siga valiendo
   si la rejilla cambia. */
.cargando > .service-pane {
  opacity: .45;
  transition: opacity var(--transition-fast);
}

@media (prefers-reduced-motion: reduce) {
  .cargando > .service-pane { transition: none; }
}


/* 57. En movil, el orden de las columnas depende de QUE pagina es.
   El bloque 43 invirtio las columnas en <=991px para que el contenido del
   servicio fuera antes que los ocho enlaces del indice. Correcto en la URL de
   un servicio: el visitante vino a leer ese servicio.

   Pero en la URL de CATEGORIA esa inversion tenia un efecto medido: el indice
   quedaba 2,43 pantallas mas abajo, y el visitante que entraba a "Redes
   Empresariales" nunca se enteraba de que habia ocho servicios salvo que
   bajara a ciegas. Una pagina de categoria es un indice: su trabajo es
   orientar, y para eso el indice tiene que verse.

   La clase la pone la plantilla cuando no hay servicio activo, asi que el
   criterio es el tipo de pagina y no un ancho. */
@media (max-width: 991px) {
  .service-details.vista-categoria .col-lg-4 { order: 1; }
  .service-details.vista-categoria .col-lg-8 { order: 2; }
}


/* ===========================================================================
   58. LA DOBLE BARRA NEGRA
   ---------------------------------------------------------------------------
   El dueno lo señalo tres veces: "sigue sin encantarme la disposicion, y esa
   doble barra negra, debe de haber un mejor approach". Tenia razon las tres.

   Lo que pasaba, medido: NO eran dos barras. Era una sola losa de 206px del
   `.page-title`, con el header flotando encima sin fondo propio
   (`home.css:171`, `--background-color: rgba(0,0,0,0)`). Pero se LEE como dos
   porque el nav ocupa la franja de arriba, el titulo la de abajo, y entre
   medias hay aire vacio. Y esa losa era el 26% de la pantalla de un portatil
   —31% en uno de 1366— sin contener ni una oferta ni un boton.

   El approach nuevo es el que usan los sitios que resuelven bien esto: UNA
   barra de navegacion delgada y solida, y el titulo dentro del contenido,
   donde le corresponde. Deja de leerse como dos zonas oscuras porque deja de
   haber dos.

   Por que no se podia hacer antes: el texto del header es blanco y su fondo
   transparente, asi que sobre claro daba 1,06:1 — invisible. Se resuelve
   dandole fondo propio en las paginas sin hero detras, que es lo que marca la
   clase del <body>.

   Los colores del titulo no se declaran aqui: al quitarle `dark-background` al
   `.page-title`, los tokens vuelven solos a los de fondo claro (--tx #14161C,
   --tx2 #565E6E). Es para lo que existe el sistema de tokens.
   =========================================================================== */

/* Mismo fondo que el header ya toma al hacer scroll, asi la barra no cambia de
   aspecto a mitad de pagina. */
.con-barra .header {
  background-color: rgba(0, 0, 0, .92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .06);
  /* Sin hero detras la barra no necesita los 15px que se dio para respirar
     sobre una foto: 10 bastan y devuelven altura al contenido en 92 paginas. */
  padding: 10px 0;
}

/* El titulo deja de vivir en una losa y pasa a ser el comienzo del contenido.
   El padding-top separa del header fijo; el resto lo pone la seccion. */
.con-barra .page-title {
  padding: 96px 0 0;
}

/* La seccion que sigue ya no tiene que despegarse de una losa oscura: titulo y
   contenido son ahora el mismo bloque. */
.con-barra .service-details.section { padding-top: var(--space-md); }

@media (max-width: 767px) {
  .con-barra .page-title { padding-top: 84px; }
}


/* ===========================================================================
   59. UN SOLO SISTEMA DE ICONOS
   ---------------------------------------------------------------------------
   El dueno lo vio en /servicios: las seis categorias se dibujaban con Bootstrap
   Icons mientras la home dibujaba ESAS MISMAS SEIS con el sprite propio. El
   barrido de las 98 plantillas encontro el mismo defecto en cinco componentes
   mas, y el peor no era /servicios sino el proceso de trabajo: la seccion
   "¿Como funciona?" existe en 13 paginas, y en 12 de ellas ya estaba en <svg>
   mientras la home seguia en <i class="bi">. El bloque 46 de este archivo
   incluso da por hecho que "las plantillas ya migraron", cosa que era cierta en
   todas menos en la primera que ve el visitante.

   Los <i> migrados a <svg> dejan de cumplir los selectores por etiqueta de
   home.css (`.info-item i`, `.stars i`, `.preview-link` por font-size). Este
   bloque devuelve tamaño y color a cada uno; es el mismo trabajo que los
   bloques 43 y 46 ya hicieron para las tarjetas de servicio y los nodos del
   proceso.

   Lo que NO se migro, y por que: WhatsApp, Facebook, Instagram, GitHub,
   LinkedIn y X son marcas de terceros. El propio sprite lo deja escrito en su
   cabecera —"redibujarlas es un error de marca"— asi que esos 84 iconos se
   quedan en Bootstrap Icons a proposito, no por olvido.
   =========================================================================== */

/* Datos de contacto de la home. home.css los pintaba con
   `.contact .info-item i { font-size: 38px; color: accent }`. */
.contact .info-item .ic {
  width: 38px;
  height: 38px;
  color: var(--accent-color);
}

/* Estrellas de los testimonios. El color sigue viviendo en home.css, donde ya
   estaba; aqui solo va el tamaño, que un <svg> no toma del font-size. */
.testimonials .testimonial-item .stars .ic {
  width: 18px;
  height: 18px;
  margin: 0 1px;
}

/* Comillas de los testimonios. `font-size: 26px` no alcanza a un <svg>; el
   color, el desplazamiento y el volteo de la comilla de cierre siguen en
   home.css y se aplican igual porque son selectores de clase. */
.testimonials .testimonial-item .quote-icon-left,
.testimonials .testimonial-item .quote-icon-right {
  width: 26px;
  height: 26px;
}

/* Lupa y enlace del portafolio. Los dos iconos que el sprite dice, en su propio
   comentario, que existen para sustituir a bi-zoom-in y bi-link-45deg.
   `display: block` los saca de la linea base: los enlaces estan posicionados en
   absoluto y uno de ellos hereda `line-height: 0`, que sobre un glifo colocaba
   el icono medio cuerpo por encima de su sitio.

   Los dos miden lo mismo, y eso es un cambio respecto a los glifos: bi-zoom-in
   iba a 26px y bi-link-45deg a 34 porque su tinta ocupaba mucho menos que su
   caja, asi que hacian falta dos numeros para que se vieran del mismo tamaño.
   Los dos simbolos del sprite estan dibujados sobre la misma rejilla de 24, asi
   que una sola medida los deja iguales de verdad — y alineados, que con 26 y 30
   no lo estaban. */
.portfolio .portfolio-content .portfolio-info .preview-link .ic,
.portfolio .portfolio-content .portfolio-info .details-link .ic {
  display: block;
  width: 28px;
  height: 28px;
}

/* Icono dentro de un titulo (Mision y Vision de /sobre-nosotros). Es el unico
   sitio donde el icono acompaña a un texto grande, asi que su tamaño se ata al
   del texto —que es lo que hacia el glifo al que sustituye— y no al token de
   24px. `-.125em` es la misma correccion de linea base que usa Bootstrap Icons,
   o sea que el icono queda exactamente donde estaba. */
.ic-titulo {
  width: 1em;
  height: 1em;
  vertical-align: -.125em;
}


/* ===========================================================================
   60. UN SOLO BOTON SECUNDARIO
   ---------------------------------------------------------------------------
   El mismo par de llamadas a la accion —"Solicitar cotizacion" + "WhatsApp"—
   cierra 78 paginas del sitio, y se dibujaba de tres formas:

     64 paginas de servicio   `btn btn-primary` + `btn btn-outline-primary`
                              Ninguna de las dos entra en el puente del bloque
                              anterior, asi que el primario salia con la forma
                              cruda de Bootstrap —relleno plano, radio .375rem,
                              relleno interior de 6/12— mientras el mismo boton
                              en las otras 14 paginas sale con el degradado y la
                              geometria del sistema. Mismo texto, mismo destino,
                              otro boton.
     14 paginas de categoria  `btn btn-primary btn-lg` + `btn-outline-light btn-lg`
                              El primario si tomaba la forma del sistema, pero
                              home.css:1462 le daba al secundario `border-radius:
                              50px`: una pastilla al lado de un rectangulo de 8px
                              de radio. Dos botones pegados, dos geometrias.

   `.btn-oasys-sec` es el gemelo perfilado de `.btn-oasys`: misma caja, misma
   tipografia, mismo radio. Los 12px/27px son 13/28 menos el pixel del borde, de
   forma que los dos botones del par miden exactamente lo mismo.

   Los colores salen de los tokens y no de `.btn-outline-*`, y por eso el mismo
   nombre de clase sirve sobre claro y sobre `.dark-background`: ahi --brand y
   --brand-txt ya se aclaran solos (ver el bloque de superficies oscuras), que es
   justo lo que `btn-outline-light` hacia a mano con blanco.
   =========================================================================== */

.btn-oasys-sec {
  display: inline-block;
  background: transparent;
  color: var(--brand-txt);
  border: 1px solid var(--brand);
  border-radius: var(--radius);
  padding: 12px 27px;
  font-family: inherit;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.35;
  letter-spacing: normal;
  text-align: center;
  text-decoration: none;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: none;
  transition: background-color var(--transition-base), color var(--transition-base);
}

.btn-oasys-sec:hover,
.btn-oasys-sec:focus {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
  box-shadow: none;
  transform: none;
}
