/* ===========================================================================
   corder.pro — sistema de diseño
   ===========================================================================

   LA FUENTE DE VERDAD DEL SISTEMA VISUAL ES DESIGN.md, en la raíz del
   proyecto: ahí están los tokens, los componentes y el porqué de cada
   decisión. Esto es sólo lo que hay que saber antes de bajar a leer reglas.

   SaaS claro y confiable, en la línea de Apple, Stripe y Vercel: secciones
   limpias separadas por mucho aire, neutros de la familia slate, un azul que
   sólo significa acción, y las capturas reales de los sistemas de clientes
   como ilustración. La confianza se comunica con claridad y contención, no
   con adorno. La jerarquía la sostienen el peso tipográfico y el espacio.

   El modo oscuro sólo redefine tokens. Ninguna regla de más abajo consulta el
   tema — si una lo hiciera, cada cambio de diseño habría que hacerlo dos
   veces y una de las dos se olvidaría. La única excepción documentada es
   --accent-ink, que cambia por legibilidad medida.

   Los únicos colores saturados del sitio:
     · el azul de acción (--accent), en muy pocos lugares y a propósito;
     · el verde de WhatsApp, porque es un código visual reconocido y perderlo
       cuesta clics;
     · los tres de estado, usados por su significado y nunca como decoración;
     · los logos de los clientes dentro de las capturas, porque son la prueba.

   NOTA DE HISTORIA. Hasta el rediseño, esta cabecera describía un rótulo de
   negocio pintado a mano: papel cálido, óxido, sombra de placa con
   desplazamiento duro. Se sustituyó por decisión del cliente. Si algún
   comentario suelto de más abajo todavía habla de "placas", "rótulo" u
   "óxido", es residuo de aquella etapa y no describe el sistema actual.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Tipografía propia
   ---------------------------------------------------------------------------

   Archivo variable, auto-hospedada. Un solo archivo de 34 KB cubre todo el
   rango de 400 a 900, así que los titulares pesados y el texto corrido salen
   de la misma fuente y no cuesta una petición por peso.

   Se hospeda aquí y no se enlaza a Google Fonts: un tercero en la ruta crítica
   cuesta DNS, TLS y una conexión nueva antes de poder pintar texto, y en un
   teléfono de gama media con datos móviles eso se nota. Mismo origen, ya
   precargada desde el <head>, con cache de un año que da el .htaccess.

   La familia de respaldo lleva métricas ajustadas para que el intercambio de
   fuente no mueva el texto: sin esto, el titular del hero salta al cargar
   Archivo y eso es exactamente lo que mide CLS.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("/assets/fonts/archivo-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Archivo Fallback";
  src: local("Segoe UI"), local("Roboto"), local("Helvetica Neue"), local("Arial");
  size-adjust: 101%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* ---------------------------------------------------------------------------
   Tokens
   --------------------------------------------------------------------------- */

:root {
  /* Neutros de la familia slate. El suelo es un gris azulado muy claro y la
     superficie es blanco puro: esa diferencia de luminosidad es lo único que
     hace flotar una tarjeta sin recurrir a una sombra pesada. No son
     intercambiables.

     Que los grises tengan un punto de azul y no sean neutros puros es
     deliberado: emparentados con el azul principal, el conjunto se lee como
     un sistema y no como un color de marca pegado sobre grises de plantilla. */
  --bg:            #f8fafc;
  --bg-alt:        #f1f5f9;
  --surface:       #ffffff;
  --surface-2:     #f1f5f9;
  --border:        #e2e8f0;
  --border-strong: #cbd5e1;
  --text:          #0f172a;
  --text-mute:     #475569;
  --text-faint:    #64748b;

  /* --- Color de marca ------------------------------------------------------

     Tres, con trabajos distintos y sin intercambiarse:

       --primary    (#2563eb) la acción. Botón principal, enlace, foco, franja
                    de cierre. Es el único color que significa "haz clic aquí".

       --secondary  (#6366f1) apoyo. Segundo plano de un degradado, un dato
                    destacado, un estado intermedio. Nunca un botón.

       --spark      (#22d3ee) el brillo. SÓLO gráfico: remates de degradado,
                    trazos finos, subrayados. NUNCA texto ni fondo de botón en
                    claro: sobre blanco se queda en 1.9:1 y es ilegible. Es un
                    color para mirar, no para leer.

     --accent apunta a --primary y no desaparece porque medio sistema lo
     nombra; renombrarlo en cincuenta reglas no aportaría nada. */
  --primary:   #2563eb;
  --secondary: #6366f1;
  --spark:     #22d3ee;

  --accent:    var(--primary);
  --on-accent: #ffffff;

  /* El acento cuando hace de TINTA en vez de relleno: texto, iconos y bordes
     finos. En claro es el mismo azul; en oscuro se aclara, porque un azul de
     600 sobre una superficie casi negra no llega al mínimo legible. Es el
     único token de color que cambia entre temas, y cambia por legibilidad, no
     por gusto. */
  --accent-ink: var(--primary);

  /* Estado. Se usan por su significado, nunca como decoración. */
  --success: #22c55e;
  --warning: #f59e0b;
  --danger:  #ef4444;

  /* La TINTA del error, separada del color del borde, por el mismo motivo que
     existe --accent-ink: el rojo de estado sirve para un filo de 1 px pero no
     para leer.

     Medido: --danger sobre el suelo claro da 3.76:1, por debajo del 4.5 que
     pide un texto de 13 px. Y el mensaje de error es justo el texto que MÁS
     tiene que leerse: aparece cuando alguien ya falló algo y está buscando
     qué corregir.

     Rojo 600 en claro (5.9:1) y rojo 400 en oscuro, misma familia. El borde y
     el halo siguen usando --danger sin cambios: lo que se oscurece es la
     tinta, no la señal. */
  --danger-ink: #dc2626;

  --whatsapp: #25d366;

  /* Sombra difusa y de dos capas: una corta y casi opaca que apoya el objeto
     en el suelo, y otra larga y muy tenue que le da altura. Es como se
     resuelve la elevación en las interfaces que buscamos parecernos.

     Van teñidas del azul de la tinta (15 23 42), nunca de negro puro: una
     sombra negra sobre un suelo azulado se ve sucia y es de los detalles que
     más delatan que la paleta se armó a ojo. */
  --shadow-sm: 0 1px 2px rgb(15 23 42 / 0.06), 0 1px 3px rgb(15 23 42 / 0.10);
  --shadow-md: 0 4px 6px -1px rgb(15 23 42 / 0.07), 0 12px 24px -8px rgb(15 23 42 / 0.12);
  --shadow-lg: 0 10px 15px -3px rgb(15 23 42 / 0.08), 0 28px 56px -16px rgb(15 23 42 / 0.16);

  /* Filo superior: una línea de luz de un píxel en el canto de arriba. Es el
     detalle que más separa una superficie cuidada de un rectángulo plano. */
  --edge: inset 0 1px 0 rgb(255 255 255 / 0.9);

  /* La franja de cierre es el único bloque de color saturado a pantalla
     completa del sitio. Con un solo momento así, el azul significa algo; con
     cinco, no significa nada. */
  --cta-bg: var(--primary);
  --cta-fg: var(--on-accent);

  /* Esquinas amplias y parejas. La escala anterior era corta a propósito
     (5/9/15) para que todo pareciera cortado a mano; ésta es más generosa,
     que es lo que hace que una superficie se lea como suave y acabada en vez
     de recortada. */
  --radius-sm: 8px;
  --radius:    12px;
  --radius-lg: 18px;

  --font: "Archivo", "Archivo Fallback", ui-sans-serif, system-ui, -apple-system,
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --wrap:    1180px;
  --wrap-sm: 760px;

  --step: clamp(0.75rem, 0.5rem + 1vw, 1.25rem);

  /* Tres curvas con tres trabajos distintos, y la que se elige depende de lo
     que hace la propiedad, no del componente:

       --ease   cambios de color (texto, fondo, borde). No hay desplazamiento
                que acompañar, así que una curva simétrica es la correcta.
       --out    todo lo que entra, sale o se mueve como respuesta a un toque o
                a un cursor. Arranca de golpe, que es lo que hace que un gesto
                se sienta contestado en el instante en que el ojo está mirando.
       --in-out movimiento de ida y vuelta en pantalla, con arranque y frenado.

     Nada de ease-in en interfaz: arranca lento justo en el cuadro que el ojo
     vigila, y hace que 200 ms se sientan como 400.

     --out y --in-out vivían sólo en home.css, así que la portada tenía un
     vocabulario de curvas y las otras veinte páginas tenían una sola curva
     genérica para todo. Viven aquí porque el sistema es uno solo. */
  --ease:   cubic-bezier(0.4, 0, 0.2, 1);
  --out:    cubic-bezier(0.23, 1, 0.32, 1);
  --in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

:root[data-theme="dark"] {
  /* Azul casi negro, nunca negro puro: el negro absoluto contra texto claro
     produce un halo que cansa la vista en cuanto hay un párrafo. El suelo es
     el más oscuro y las superficies suben de valor conforme se acercan al
     lector, que es como se lee la profundidad sin sombras. */
  --bg:            #020617;
  --bg-alt:        #0b1120;
  --surface:       #0f172a;
  --surface-2:     #1e293b;
  --border:        #1e293b;
  --border-strong: #334155;
  --text:          #f1f5f9;
  --text-mute:     #94a3b8;
  --text-faint:    #7e8ea3;

  /* Azul aclarado, misma familia. Sobre la superficie oscura da 7:1, así que
     un enlace, un icono o un mensaje se leen de verdad. El relleno del botón
     sigue usando --primary sin cambios: lo que se aclara es la tinta. */
  --accent-ink: #60a5fa;

  /* Los estados también suben de valor: en oscuro, los tonos de 500 se apagan
     contra el fondo y dejan de comunicar lo que significan. */
  --success: #4ade80;
  --warning: #fbbf24;
  --danger:  #f87171;

  /* En oscuro la tinta del error sube en vez de bajar, que es la misma
     inversión que hace --accent-ink. El 400 sobre la superficie oscura da
     6.8:1; el 600 que sirve en claro se hundiría hasta 2.9:1. */
  --danger-ink: #f87171;

  /* Sobre fondo oscuro una sombra casi no se ve: la elevación la comunica el
     salto de valor entre superficies y el borde. La sombra se queda sólo para
     separar lo que de verdad flota por encima. */
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.4);
  --shadow-md: 0 4px 12px -2px rgb(0 0 0 / 0.5);
  --shadow-lg: 0 16px 40px -12px rgb(0 0 0 / 0.65);

  --edge: inset 0 1px 0 rgb(255 255 255 / 0.06);
}

/* ---------------------------------------------------------------------------
   Reset
   --------------------------------------------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* El header es fijo: sin esto, un enlace a #precios deja el título tapado. */
  scroll-padding-top: 6rem;
}

body {
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;

  /* Cifras de ancho fijo en toda la interfaz. Sin esto, un contador animado o
     un precio cambian de ancho al cambiar de dígito y el texto de al lado
     tiembla. */
  font-variant-numeric: tabular-nums;
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

/* --- Lo que pinta el navegador y no el diseñador --------------------------
   Selección, cursor de texto, controles nativos, barra de desplazamiento y
   anillo de foco vienen con colores de fábrica que no pertenecen a ningún
   sistema. Dejarlos así es lo que hace que una página se sienta ensamblada en
   vez de construida, y es de lo más barato de corregir. */

html {
  /* Tiñe de una sola vez casillas, radios, barras de progreso y el resto de
     controles nativos que el sistema operativo pintaría de azul de fábrica. */
  accent-color: var(--primary);

  /* Pulgar y riel de la barra, en Firefox y en los navegadores que ya
     implementan la propiedad estándar. */
  scrollbar-color: var(--border-strong) transparent;
}

input, textarea, select {
  caret-color: var(--primary);
}

/* WebKit todavía necesita su propia sintaxis. Se mantiene delgada y sin
   flechas: una barra ancha con botones compite con el contenido. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }

::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 2px solid transparent;
  border-radius: 100px;
  background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }

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

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

button { background: none; border: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

/* El recuadro gris que Chrome de Android pinta sobre cualquier cosa que se
   toque. Con estados :active y :hover propios en todos los controles, lo único
   que aporta es un destello que delata que el sitio no se revisó en un
   teléfono. */
a, button, label, summary, [role="button"] { -webkit-tap-highlight-color: transparent; }

ul, ol { list-style: none; padding: 0; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------------------
   Tipografía
   --------------------------------------------------------------------------- */

/* El interletrado se aprieta conforme crece el tamaño. Un titular grande con
   el mismo tracking que el texto corrido se ve suelto y amateur; es el ajuste
   óptico que hace toda tipografía editorial. */
h1, h2, h3, h4 {
  line-height: 1.08;
  letter-spacing: -0.03em;
  font-weight: 700;
  text-wrap: balance;
}

h1 { font-size: clamp(2.4rem, 1.4rem + 4.4vw, 4.75rem); letter-spacing: -0.04em; line-height: 0.98; font-weight: 800; }
h2 { font-size: clamp(1.9rem, 1.3rem + 2.4vw, 3.1rem); letter-spacing: -0.035em; line-height: 1.06; }
h3 { font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.6rem); letter-spacing: -0.025em; }
h4 { font-size: 1.05rem; letter-spacing: -0.015em; }

p { text-wrap: pretty; }

.lead {
  font-size: clamp(1.05rem, 0.98rem + 0.42vw, 1.3rem);
  color: var(--text-mute);
  line-height: 1.6;
  max-width: 46ch;
}

/* La etiqueta va en una placa pequeña, como el rótulo de vidrio que anuncia
   el nombre del negocio antes de que se vea el letrero grande: mismo lenguaje
   de "placa" que el resto de la interfaz, a la escala más chica posible. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.28rem 0.65rem 0.28rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);

  /* Preventivo, no correctivo: hoy ninguna etiqueta del sitio desborda a
     360 px (medido con scrollWidth en iframe, las doce plantillas).

     Pero `inline-flex` no rompe renglón por defecto, así que la placa aguanta
     sólo mientras las etiquetas sigan siendo cortas. La más larga en uso
     —"Cotización sin costo", en /cotizar/— ya ocupa casi toda la caja a
     360 px con 0.1em de tracking y versalitas; una de cuatro palabras
     provocaría barrido lateral, y el barrido lateral no se ve en escritorio
     ni lo detecta ningún linter.

     `flex-wrap` deja que el texto baje de renglón dentro de la placa y
     `max-width` es el cinturón para la palabra que no pueda partirse. */
  flex-wrap: wrap;
  max-width: 100%;
}

.eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

/* La etiqueta usada como sólo-texto (breadcrumb, kicker suelto sin placa) no
   lleva el marco: `nav.eyebrow` y cualquier eyebrow dentro de un enlace de
   ruta se leen mejor lisos. */
nav.eyebrow, .eyebrow--plain {
  padding: 0;
  border: 0;
  background: none;
  border-radius: 0;
}

/* EL PUNTO NO VA EN LA RUTA.

   `.eyebrow::before` pinta un punto de acento de 6 px, y en una etiqueta de
   sección funciona: es una viñeta que abre el renglón. Pero `nav.eyebrow` es
   la ruta de vuelta —"Proyectos", "Soluciones"— y ahí el punto no significa
   nada: es decoración pegada a un enlace, que es exactamente el tic que hace
   que una interfaz se lea como generada. Una ruta pide una FLECHA, que dice
   hacia dónde se vuelve; el icono va en el marcado, con `currentColor`.

   `content: none` y no `display: none`: así el pseudoelemento deja de
   generarse en vez de generarse y esconderse. */
nav.eyebrow::before { content: none; }

/* La ruta ("← Proyectos") que abre una página de detalle. */
nav.eyebrow { margin-bottom: 1.5rem; }

/* La flecha de vuelta. Retrocede un pelo al pasar el cursor, en el mismo
   sentido que apunta: el movimiento dice a dónde lleva el enlace antes de
   leerlo. */
.eyebrow__back {
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
  color: var(--accent-ink);
  transition: transform 0.18s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  nav.eyebrow a:hover .eyebrow__back { transform: translateX(-2px); }
}
nav.eyebrow a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: inherit;
  transition: color 0.15s var(--ease);
}

/* TODO :hover de esta hoja va detrás de @media (hover: hover) and (pointer:
   fine), y ésta es la primera vez que aparece, así que aquí está el porqué:

   un teléfono no tiene cursor, pero sí dispara :hover al tocar, y lo DEJA
   pintado hasta que se toca otra cosa. En un enlace da igual —la página ya se
   fue— pero en un control que se queda en pantalla (un chip de filtro, una
   pastilla de presupuesto) esa marca pegada se lee como "esto está
   seleccionado", que es justo lo que no es. Con la consulta, en táctil sólo
   quedan :active y :checked, que sí dicen la verdad.

   La pareja (pointer: fine) descarta además el lápiz y el mando de una tele,
   que declaran hover pero apuntan grueso.

   home.css ya lo hacía para .pick; el resto del sitio no, y era la misma
   necesidad. */
@media (hover: hover) and (pointer: fine) {
  nav.eyebrow a:hover { color: var(--accent); }
}

/* El titular que sigue a una etiqueta. Repetía como `style="margin-top:.75rem"`
   en catorce lugares; es siempre el mismo hueco porque siempre es la misma
   relación: la etiqueta anuncia, el titular responde. */
.eyebrow + h1, .eyebrow + h2 { margin-top: 0.75rem; }

.muted { color: var(--text-mute); }

/* Ancho de lectura para un bloque de texto suelto que no vive dentro de un
   `.section__head` ni de un `.page-hero`. */
.measure { max-width: 52rem; }
.measure > h2 { margin-bottom: 1.5rem; }

/* Letra chica: la aclaración que va debajo de un dato o de una tarjeta.
   Repetía como `style="font-size:.85rem"` en seis plantillas. */
.note { margin-top: 0.75rem; font-size: 0.85rem; color: var(--text-mute); }
.note--loose { margin-top: 2.5rem; }

/* Cifras leídas como instrumento: precio, tiempo de entrega, estadística.
   Monoespaciada y tabular, para que se lea como un dato medido y no como una
   palabra más del titular — es la precisión que un rótulo pintado no tiene y
   que aquí se toma prestada a propósito. */
.stat-value, .price, .exchange__float strong {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* ---------------------------------------------------------------------------
   Estructura
   --------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(1.1rem, 4vw, 2.5rem);
}

.wrap--narrow { max-width: var(--wrap-sm); }

/* El espacio entre secciones es lo que hace que cada una se lea como una idea
   aparte en vez de como un bloque más de una lista.

   Bajó de 9.5 rem a 7 rem, que era el valor que la portada ya se aplicaba por
   su cuenta desde home.css. El motivo es el mismo en todo el sitio y ahora el
   encabezado también: con el encabezado asimétrico el aire lo da la propia
   composición, y 9.5 rem dejaba bandas vacías de casi 300 px que se leen como
   página hueca, no como página cara. */
.section { padding-block: clamp(3.5rem, 7.5vw, 7rem); }
.section--tight { padding-block: clamp(2.75rem, 5vw, 4.5rem); }
.section--alt { background: var(--bg-alt); }

.section__head { max-width: 44rem; margin-bottom: clamp(2.25rem, 5vw, 3.5rem); }
.section__head p { margin-top: 1rem; color: var(--text-mute); font-size: 1.05rem; }

/* --- Encabezado de página ---------------------------------------------------
   Repetía en nueve plantillas como bloque `style=""` idéntico. Ahora es un
   componente: mismo eyebrow, mismo h1 con tamaño fluido, mismo lead sin
   límite de ancho propio. */
.page-hero { max-width: 52rem; }

/* El bloque se paga su propio aire, pero SÓLO si debajo viene algo suyo.

   Antes no llevaba margen inferior y la vista del cotizador lo resolvía
   pegándole `section__head` encima, que además declara otra anchura: de las
   dos ganaba una por orden de declaración en la hoja, no por decisión.

   El `:not(:last-child)` es lo que impide cambiar eso por el defecto
   contrario. En solución, servicio, caso de estudio y las páginas legales el
   encabezado es lo último de su sección, y ahí un margen inferior sería aire
   colgando debajo de la última línea — exactamente lo que el sistema prohíbe:
   el espacio va ENTRE secciones, no al final de cada una. */
.page-hero:not(:last-child) { margin-bottom: clamp(2.25rem, 5vw, 3.5rem); }

.page-hero .eyebrow { margin-bottom: 0.9rem; }

/* Caso de estudio y página de solución abren con la industria en vez de con
   un eyebrow enmarcado; sin `display:block` el h1 se le pega al renglón. */
.page-hero .work__industry { display: block; margin-bottom: 0.55rem; }
.page-hero h1 { font-size: clamp(2.1rem, 1.4rem + 2.8vw, 3.4rem); margin-bottom: 1.1rem; }
.page-hero .lead { max-width: none; }
.page-hero--center { margin-inline: auto; text-align: center; }
.page-hero--center .lead { margin-inline: auto; }

/* ---------------------------------------------------------------------------
   Encabezado asimétrico
   ===========================================================================

   El componente que fija el ritmo. Vivía en home.css y por eso la portada y el
   resto del sitio se leían como dos sitios distintos: la portada componía en
   banda y las nueve páginas de dentro apilaban etiqueta, título y apoyo en
   tres renglones centrados sobre la columna. Subió aquí sin cambiarle nada.

   En escritorio son dos columnas: a la izquierda la etiqueta de acento y el
   titular a dos voces; a la derecha, alineado al borde derecho y asentado
   sobre la línea base del titular, el texto de apoyo. La banda se lee de un
   golpe, de izquierda a derecha.

   En móvil se apila y el apoyo vuelve a la izquierda: a 360 px un párrafo
   alineado a la derecha con renglones cortos se lee como un error.
   --------------------------------------------------------------------------- */

.lede {
  display: grid;
  gap: 0.75rem;
  margin-bottom: clamp(2rem, 4.5vw, 3.5rem);
}

.lede--tight { margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem); }

/* Un encabezado que es lo último de su SECCIÓN no paga margen inferior.

   Se ve en cuanto se abre /proceso/: la apertura es un `.lede--hero` solo
   dentro de su sección, y el margen quedaba sumado al relleno de la sección,
   colgando bajo la última línea del titular. Es la regla 3 del sistema —el
   aire va ENTRE secciones, no al final de cada una— y la rompía el propio
   componente que la enuncia.

   Los dos `:last-child` importan, y el de `.wrap` es el que no es obvio. La
   marquesina del stack de la portada tiene su encabezado dentro de un `.wrap`
   al que le siguen, ya fuera, la lista accesible y la pista que corre. Ahí el
   encabezado sí es lo último de su `.wrap` pero NO lo último de su sección, y
   sin margen la cinta se le pega al titular. Con `.wrap:last-child` la regla
   distingue los dos casos. */
.wrap:last-child > .lede:last-child { margin-bottom: 0; }

/* Apertura de página. Es el mismo componente con el titular un paso más
   grande: una página de dentro abre con una sola voz fuerte, y si el h1 pesa
   lo mismo que los h2 de abajo la jerarquía se pierde. */
.lede--hero { margin-bottom: clamp(2.5rem, 5.5vw, 4rem); }

/* La etiqueta de acento. Es el único texto en versalitas del sistema y por eso
   funciona: si cada sección tuviera una, dejaría de señalar nada. Ver la regla
   de racionamiento en DESIGN.md.

   Va en --accent-ink y no en --accent: a 0.72rem y en mayúsculas esto es texto
   pequeño, y en modo oscuro el azul de 600 no llega al mínimo legible sobre la
   superficie. --accent-ink es el mismo azul en claro y uno aclarado en oscuro,
   que es exactamente para lo que existe. */
.lede__eyebrow {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-ink);
}

/* El punto que respira, sólo donde se afirma que algo está vivo ahora mismo.

   3.2 s: por debajo de dos segundos un latido en el campo visual periférico
   distrae de leer, y por encima de cuatro deja de leerse como "en vivo".
   Escala y opacidad, las dos propiedades que el compositor resuelve sin volver
   a pintar. */
.lede__dot {
  width: 0.45rem;
  height: 0.45rem;
  flex: none;
  border-radius: 50%;
  background: var(--success);
  animation: live-pulse 3.2s var(--in-out) infinite;
}

@keyframes live-pulse {
  0%, 100% { opacity: 1;    transform: scale(1); }
  50%      { opacity: 0.45; transform: scale(0.78); }
}

/* El tracking va atado al tamaño, no fijo. Una sola cifra de letter-spacing
   está mal en algún lado: a 1.8 rem el texto necesita respirar y a 4 rem las
   letras se ven sueltas. Como el tamaño es fluido, el tracking también. */
.lede__title {
  display: grid;
  font-size: clamp(1.85rem, 1.1rem + 3.4vw, 3.9rem);
  line-height: 0.98;
  letter-spacing: clamp(-0.042em, -0.028em - 0.18vw, -0.03em);
  text-wrap: balance;
}

.lede__title-lead {
  font-weight: 800;
  color: var(--text);
}

/* La segunda voz. Peso 400 sobre 800 y --text-faint sobre --text: dos saltos a
   la vez, que es lo que hace que se lea como otra voz y no como la misma línea
   con menos tinta. */
.lede__title-soft {
  font-weight: 400;
  color: var(--text-faint);
}

.lede__say {
  max-width: 46ch;
  font-size: clamp(0.95rem, 0.9rem + 0.25vw, 1.05rem);
  line-height: 1.55;
  color: var(--text-mute);
  text-wrap: pretty;
}

@media (min-width: 900px) {
  .lede {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr);
    grid-template-areas:
      "eyebrow eyebrow"
      "titulo  apoyo";
    column-gap: clamp(2rem, 5vw, 4rem);
    align-items: end;
  }

  /* Sin etiqueta, la fila "eyebrow" se queda vacía pero el `gap` de la rejilla
     se sigue cobrando: 12 px de aire muerto encima del titular, medidos. Se ve
     poco de a uno y se nota al comparar dos encabezados seguidos, uno con
     etiqueta y otro sin ella, porque los titulares dejan de alinearse con la
     misma lógica.

     Ahora que la etiqueta se raciona —una por cada tres secciones— la mayoría
     de los encabezados del sitio no la llevan, así que éste es el caso normal
     y no la excepción.

     Sin soporte de :has() el único efecto es que vuelven los 12 px: se degrada
     a lo que había, no a algo roto. */
  .lede:not(:has(.lede__eyebrow)) {
    grid-template-areas: "titulo apoyo";
  }

  .lede__eyebrow { grid-area: eyebrow; margin-bottom: 0.4rem; }
  .lede__title   { grid-area: titulo; }

  /* Alineado a la derecha y asentado sobre la línea base del titular: es lo
     que cierra la banda por el otro extremo y lo que convierte dos elementos
     sueltos en una composición. */
  .lede__say {
    grid-area: apoyo;
    justify-self: end;
    text-align: right;
    padding-bottom: 0.35rem;
  }
}

/* --- Par de estadística -----------------------------------------------------
   Precio/entrega repetían como bloque `style=""` en servicio, solución y caso
   de estudio. */
.stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 2.5rem;
  margin-top: 2rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
}

.stat-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.11em; text-transform: uppercase; color: var(--text-faint); }
.stat-value { display: block; margin-top: 0.35rem; font-size: 1.4rem; font-weight: 700; }

/* --- Columnas de exposición ------------------------------------------------
   "El problema / Cómo lo resolvemos / El resultado": dos o tres bloques de
   prosa corta, uno al lado del otro. Repetía como grid inline en la página de
   solución y en el caso de estudio. Auto-fit y no un número fijo de columnas
   porque el caso de estudio a veces trae tres bloques y a veces dos. */
.brief {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr));
  gap: clamp(2rem, 4vw, 3.5rem);
  max-width: 64rem;
}

.brief p { margin-top: 1rem; font-size: 1.02rem; line-height: 1.65; }

/* Cuerpo de texto largo — el de "nosotros" y el de un servicio. Un punto por
   encima del texto de interfaz y con más interlínea: se lee de corrido, no se
   escanea como una tarjeta. */
.prose {
  display: grid;
  gap: 1.25rem;
  max-width: 52rem;
  margin-top: 2rem;
  font-size: 1.02rem;
  line-height: 1.7;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  padding: 0.75rem 1.25rem;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; }

/* ---------------------------------------------------------------------------
   Botones
   --------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 0.85rem 1.6rem;
  /* Píldora. Es la forma que sostiene el resto del sistema flotante: el
     encabezado, el control segmentado de navegación y las etiquetas de las
     tarjetas usan el mismo radio, así que un botón de esquina cuadrada aquí
     rompería la familia. */
  border-radius: 999px;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  white-space: nowrap;
  border: 1px solid transparent;
  /* Color con --ease, movimiento con --out: el relleno cambia de tono y no
     tiene por qué arrancar de golpe, pero la escala del toque y la sombra que
     lo levanta sí, que es lo que se lee como respuesta. */
  transition: background-color 0.15s var(--ease),
              color 0.15s var(--ease),
              border-color 0.15s var(--ease),
              transform 0.14s var(--out),
              box-shadow 0.15s var(--out);
}

/* Escala, no desplazamiento vertical.
   Un botón que baja un píxel se lee como que la página dio un salto; uno que
   se encoge se lee como que acusó recibo del dedo. Es la misma escala que usan
   las placas del cotizador, así que todo lo pulsable del sitio responde igual. */
.btn:active { transform: scale(0.97); }

/* La flecha diagonal de "esto te lleva a otra parte". Se separa un punto al
   pasar el cursor, igual que la horizontal. */
.btn__go {
  width: 14px;
  height: 14px;
  flex: none;
  transition: transform 0.2s var(--out);
}

.btn--primary {
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--shadow-sm);
}
.btn--primary:active { transform: scale(0.97); box-shadow: var(--shadow-sm); }

.btn--ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--border-strong);
}

.btn--wa { background: var(--whatsapp); color: #05300f; border-color: transparent; }

.btn--sm { padding: 0.55rem 1.1rem; font-size: 0.87rem; min-height: 2.75rem; }

/* LA FLECHA VA EN SU PROPIO DISCO, no suelta al lado del texto.

   Una flecha desnuda pegada a una palabra es el remate por defecto de
   cualquier botón; metida en su disco, el botón deja de ser una etiqueta con
   un símbolo y pasa a leerse como una pieza con partes. Es el mismo recurso
   que ya usa la insignia del pictograma en las opciones del cotizador, así que
   no es un invento suelto: es el sistema aplicado otra vez.

   El disco se pega al borde derecho con un margen negativo que se come parte
   del relleno del botón: sin eso quedaría flotando en medio del aire de la
   derecha y el botón se vería descentrado.

   `flex: none` porque un disco que se encoge cuando el texto es largo deja de
   ser un círculo, y ahí se nota más que si no estuviera. */
.btn__arrow {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: 0.15rem;
  margin-right: -0.45rem;
  border-radius: 50%;
  background: currentColor;
  color: inherit;
  font-size: 0.85em;
  line-height: 1;
  transition: transform 0.2s var(--out);
}

/* El glifo se pinta con el color del BOTÓN y el disco con el del texto, así que
   hay que invertir: el disco toma `currentColor` arriba y la flecha de dentro
   se recorta contra él con mezcla, que funciona sobre cualquier fondo sin
   declarar un color nuevo por variante de botón. */
.btn__arrow { mix-blend-mode: normal; }

.btn--primary .btn__arrow { background: var(--on-accent); color: var(--accent); }
.btn--ghost   .btn__arrow { background: var(--surface-2); color: var(--text); }
.btn--wa      .btn__arrow { background: rgb(5 48 15 / 0.14); color: #05300f; }

@media (hover: hover) and (pointer: fine) {
  .btn:hover .btn__go { transform: translate(2px, -2px); }

  /* Tensión interna: el disco se va en diagonal y crece un punto, así que el
     botón no se mueve entero pero algo dentro de él sí. */
  .btn:hover .btn__arrow { transform: translate(2px, -1px) scale(1.06); }
  .btn--primary:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
  .btn--ghost:hover { border-color: var(--accent); background: var(--surface-2); color: var(--accent); }
  .btn--wa:hover { filter: brightness(1.06); }
}

/* Estado de envío. El botón conserva su tamaño y sólo cambia de contenido: uno
   que se encoge mientras esperas parece que se rompió.

   Vive aquí y no en home.css porque lo usan los dos formularios del sitio, el
   de la portada y el de /cotizar/. */
.btn[data-busy="true"] { pointer-events: none; opacity: 0.85; }

/* Rápido a propósito: un giro corto hace que la espera se sienta más corta
   aunque el servidor tarde exactamente lo mismo. */
.spin {
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------------------
   Encabezado
   --------------------------------------------------------------------------- */

/* Barra flotante: una píldora despegada del borde superior, con el contenido
   pasando por detrás y por los lados.

   La franja de borde a borde anterior consumía una tira fija de 4.5 rem en
   todas las páginas y, sobre todo, se leía como el encabezado por defecto de
   cualquier plantilla. Despegada, el encabezado deja de ser una franja y pasa
   a ser un objeto: se apoya sobre la página en vez de cortarla.

   SIGUE SIENDO OPACA, sin backdrop-filter, y es una decisión medida, no un
   olvido: un filtro de fondo sobre un elemento fijo obliga al navegador a
   recomponer la capa en cada cuadro de scroll, y en un teléfono de gama media
   —que es por donde entra la mayoría del tráfico— ahí es exactamente donde
   aparece el tirón al desplazar. La separación la dan el borde, la sombra y el
   aire de los lados, que no cuestan nada. */
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  padding-block: 0.7rem;
  /* El aire de los lados lo pone el <header>, no un .wrap: la píldora tiene su
     propio ancho máximo y no debe atarse al ancho de contenido del sitio. */
  padding-inline: clamp(0.8rem, 3vw, 1.75rem);
  background: transparent;
}

.header__inner {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2vw, 1.5rem);
  height: 3.9rem;
  max-width: min(100%, 64rem);
  margin-inline: auto;
  padding-inline: 0.55rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--edge), var(--shadow-sm);
  /* Ni `height` ni `padding` entran en esta lista, y es deliberado.

     La primera versión encogía la píldora animando su alto y el relleno del
     <header>. Se veía bien y estaba mal: las dos son propiedades de layout, así
     que el navegador tenía que volver a medir la página en CADA cuadro de una
     transición que se dispara justo mientras se hace scroll. Era exactamente
     el tirón que esta misma hoja dice evitar unas líneas más arriba al
     rechazar backdrop-filter, así que se contradecía sola.

     La versión buena hace el mismo gesto con transform y sombra: las dos las
     resuelve el compositor sin tocar el layout. Y como el encabezado ya no
     cambia de alto, el contenido de debajo tampoco se recoloca al desplazar. */
  transition: transform 0.24s var(--out),
              box-shadow 0.24s var(--out),
              border-color 0.24s var(--ease);
}

/* Al desplazar, la píldora se asienta tres píxeles y levanta la sombra: se lee
   como que el encabezado se posó sobre la página, sin mover un solo píxel de
   lo que hay debajo. */
.header[data-scrolled="true"] .header__inner {
  transform: translateY(-3px);
  border-color: var(--border-strong);
  box-shadow: var(--edge), var(--shadow-lg);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding-left: 0.5rem;
  font-weight: 800;
  font-size: 1.02rem;
  letter-spacing: -0.035em;
  margin-right: auto;
}

/* La marca es SVG en línea, no un PNG.
   El wordmark corder-logo.png es de 1983×793 —proporción 2.5:1— y estaba
   puesto a 30×30, que lo aplastaba a un cuadrado y además lo emborronaba al
   reducirlo tanto. El SVG es nítido a cualquier tamaño y hereda el tema. */
.mark {
  flex: none;
  display: block;
}

/* La navegación es un control segmentado, no una fila de enlaces sueltos.

   El contenedor se hunde (--surface-2 sobre la píldora blanca) y la página
   activa sube como una plaquita clara con su filo y su sombra. Es el mismo
   par de superficies anidadas que usan las placas del cotizador, y comunica lo
   mismo sin gastar un color: lo hundido es el riel, lo levantado es dónde
   estás. */
.nav {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  padding: 0.25rem;
  border-radius: 999px;
  /* El riel va en --bg y NO en --surface-2, y la diferencia importa en oscuro.

     El par que comunica elevación es suelo/superficie: --bg es siempre el
     valor más lejano al lector y --surface el más cercano, en los dos temas.
     Con --surface-2 el riel funcionaba en claro (gris bajo un chip blanco) y
     se invertía en oscuro: --surface-2 es #1e293b y --surface es #0f172a, así
     que el chip activo quedaba MÁS OSCURO que su riel, o sea hundido justo
     donde tiene que verse levantado.

     Con --bg el orden se mantiene solo en los dos temas y ninguna regla de
     aquí abajo tiene que preguntar cuál está activo. */
  background: var(--bg);
}

.nav a {
  padding: 0.42rem 0.8rem;
  border-radius: 999px;
  font-size: 0.87rem;
  font-weight: 500;
  letter-spacing: -0.012em;
  white-space: nowrap;
  color: var(--text-mute);
  transition: color 0.15s var(--ease),
              background-color 0.15s var(--ease),
              box-shadow 0.15s var(--out),
              transform 0.14s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  .nav a:hover { color: var(--text); }
}

.nav a[aria-current="page"] {
  color: var(--text);
  background: var(--surface);
  box-shadow: var(--edge), var(--shadow-sm);
}

/* Todo lo que se presiona lleva escala de retroalimentación. Un enlace de
   navegación también se presiona. */
.nav a:active { transform: scale(0.97); }

.header__actions { display: flex; align-items: center; gap: 0.45rem; }

/* 2.75rem = 44 px, el mínimo con el que un pulgar acierta de forma fiable.
   Estaba en 2.35rem (37,6 px), que en un teléfono obliga a apuntar. */
.theme-toggle {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1px solid transparent;
  /* Mismo criterio que el riel de la navegación: --bg es el suelo en los dos
     temas, así que el botón se lee hundido en claro y en oscuro sin que la
     regla tenga que consultar cuál está activo. */
  background: var(--bg);
  color: var(--text-mute);
  transition: color 0.15s var(--ease),
              border-color 0.15s var(--ease),
              transform 0.14s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  .theme-toggle:hover { color: var(--text); border-color: var(--border-strong); }
}

.theme-toggle:active { transform: scale(0.94); }
.theme-toggle svg { width: 17px; height: 17px; }

/* Sólo se ve el icono del tema al que se cambiaría. */
.theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun  { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }

.nav-toggle { display: none; }

@media (max-width: 940px) {
  .header__inner { gap: 0.6rem; }
  .header__actions { gap: 0.35rem; }

  .nav-toggle {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    border: 1px solid transparent;
    background: var(--bg);
    transition: transform 0.14s var(--out);
  }

  .nav-toggle:active { transform: scale(0.94); }

  /* LA HAMBURGUESA SE PLIEGA EN UNA EQUIS.

     Antes eran dos SVG cruzándose en opacidad, y el comentario de entonces lo
     justificaba diciendo que interpolar los `path` daría un enredo. Es cierto,
     pero la conclusión no: no hacía falta interpolar trazos, hacía falta que
     las barras fueran elementos propios. Con tres <span> rotables el gesto
     existe de verdad —la figura se pliega— en vez de que una imagen sustituya
     a otra mientras las dos se ven a medias.

     La caja mide 18 px y las barras 2: la de arriba y la de abajo viajan 6 px
     hasta el centro, que es exactamente su separación en reposo.

     Sólo se animan `transform` y `opacity`, y la curva es --out porque las
     barras ENTRAN a su posición nueva; una curva que arranca lenta haría que el
     botón pareciera pesado justo al tocarlo. */
  .nav-toggle__caja {
    position: relative;
    display: block;
    width: 18px;
    height: 14px;
  }

  .nav-toggle__barra {
    position: absolute;
    left: 0;
    width: 100%;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: transform 0.26s var(--out), opacity 0.16s var(--ease);
  }

  .nav-toggle__barra:nth-child(1) { top: 0; }
  .nav-toggle__barra:nth-child(2) { top: 6px; }
  .nav-toggle__barra:nth-child(3) { top: 12px; }

  .header[data-open="true"] .nav-toggle__barra:nth-child(1) {
    transform: translateY(6px) rotate(45deg);
  }

  /* La del medio no se encoge ni se va a un lado: se apaga donde está, que es
     justo donde las otras dos se van a cruzar. */
  .header[data-open="true"] .nav-toggle__barra:nth-child(2) { opacity: 0; }

  .header[data-open="true"] .nav-toggle__barra:nth-child(3) {
    transform: translateY(-6px) rotate(-45deg);
  }

  /* El menú desplegado cuelga de la píldora y repite su forma: mismo ancho,
     mismo radio grande, misma sombra. Antes se pegaba de borde a borde de la
     ventana, y con el encabezado ya despegado eso lo dejaba flotando sin
     relación con nada.

     Va en position: absolute contra el <header>, que es sticky y por lo tanto
     ya es contenedor de posicionamiento.

     TODA la forma del menú se declara aquí, en reposo, y el atributo data-open
     sólo enciende opacidad y desplazamiento. Antes el menú entero vivía dentro
     de `[data-open="true"]` con `display: none` como estado cerrado, y de
     display no se transiciona: el panel aparecía y desaparecía de golpe, que
     era la única pieza interactiva del sitio sin transición — y la más usada,
     porque el grueso del tráfico entra por teléfono.

     El par opacidad + visibilidad es el mismo patrón que ya usa .wa-fab unas
     mil líneas más abajo: visibility no interpola, pero al ocultar respeta la
     duración y se apaga al final, así que el panel no desaparece antes de
     terminar de irse, y al estar oculto sale del orden de tabulación igual que
     lo sacaba display: none. */
  .nav {
    position: absolute;
    top: calc(100% - 0.35rem);
    left: 50%;
    width: min(calc(100% - 2.2rem), 64rem);
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.1rem;
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--edge), var(--shadow-lg);

    opacity: 0;
    visibility: hidden;
    /* -6px y no -12: el panel cuelga a 6 px de la píldora, así que un
       desplazamiento mayor lo hace salir de debajo del encabezado en vez de
       asomarse desde él. */
    transform: translateX(-50%) translateY(-6px);
    /* Esta duración es la de CERRAR: la transición que corre es la del estado
       al que se llega. Salir más rápido que entrar es la regla — lo que el
       usuario quiere ver es la página, no el menú despidiéndose. */
    transition: opacity 0.14s var(--out),
                transform 0.14s var(--out),
                visibility 0.14s;
  }

  .header[data-open="true"] .nav {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%);
    transition: opacity 0.18s var(--out),
                transform 0.18s var(--out),
                visibility 0.18s;
  }

  /* Cada entrada del menú desplegado ocupa una franja completa de 48 px: en un
     menú vertical el objetivo táctil es toda la fila, no sólo el texto. */
  .nav a {
    display: flex;
    align-items: center;
    min-height: 3rem;
    padding: 0 0.9rem;
    border-radius: var(--radius);
    font-size: 0.95rem;
  }

  .nav a:active { transform: scale(0.985); }
}

/* En pantallas muy estrechas el nombre de la marca cede el paso a los
   controles: el símbolo solo sigue identificando el sitio, y lo que no puede
   faltar es el botón de cotizar. */
@media (max-width: 380px) {
  .brand span { display: none; }
}

/* ---------------------------------------------------------------------------
   Fila de acciones y fila de pruebas
   ===========================================================================

   Lo que queda del hero anterior. La portada dejó de usarlo: su apertura vive
   ahora en home.css, que sólo se carga en "/". Estas dos piezas sobreviven
   porque las usan otras cinco vistas: la fila de botones aparece en servicio,
   solución, acuse de recibo y las dos páginas de error, y la fila de pruebas
   en el cotizador.
   --------------------------------------------------------------------------- */

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 0.75rem;
  margin-top: 2.25rem;
}

/* La única pantalla centrada del sitio es el acuse de recibo: ahí no hay nada
   más que leer y centrar es lo correcto. En el resto, centrar es la
   composición por defecto que este rediseño abandonó. */
.hero__cta--center { justify-content: center; }

.hero__proof {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 1.5rem;
  margin-top: 1.5rem;
  font-size: 0.86rem;
  color: var(--text-mute);
}

.hero__proof li { display: flex; align-items: center; gap: 0.4rem; }
.hero__proof svg { width: 15px; height: 15px; flex: none; color: var(--accent); }

@media (max-width: 620px) {
  /* Tres pruebas en fila envuelven en un salto irregular —dos arriba, una
     suelta abajo— que se lee como un descuido. En columna quedan alineadas y
     se leen como una lista. */
  .hero__proof { flex-direction: column; align-items: flex-start; gap: 0.55rem; }
}

@media (max-width: 460px) {
  /* A un solo pulgar, dos botones lado a lado quedan estrechos y el secundario
     acaba recibiendo toques que iban al primario. */
  .hero__cta { flex-direction: column; align-items: stretch; }
  .hero__cta .btn { width: 100%; }
}

/* ---------------------------------------------------------------------------
   Portafolio
   ===========================================================================

   Las capturas son anchas y densas: paneles de administración de 1400 px
   llenos de datos. Encogidas a una tarjeta se vuelven ilegibles, así que en vez
   de encogerlas se enmarcan y se dejan recorrer.
   --------------------------------------------------------------------------- */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 2rem;
}

.chip {
  display: inline-flex;
  align-items: center;
  min-height: 2.6rem;
  padding: 0.5rem 1.05rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--text-mute);
  /* Nunca `transition: all`. Enumerar las propiedades es lo que impide que
     mañana entre a la lista algo que dispara layout sin que nadie lo note —
     aquí `all` alcanzaba también a padding, border-width y font-weight. */
  transition: border-color 0.15s var(--ease),
              background-color 0.15s var(--ease),
              color 0.15s var(--ease);
}

/* Un chip de filtro se queda en pantalla después del toque, así que aquí la
   marca pegada de hover competía de verdad con el chip realmente activo. */
@media (hover: hover) and (pointer: fine) {
  .chip:hover { border-color: var(--border-strong); color: var(--text); }
}

.chip[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

/* En móvil los filtros no envuelven a dos renglones: se recorren en
   horizontal, sangrados hasta el borde de la pantalla para que se vea que la
   fila continúa. Envolver empuja la primera tarjeta del portafolio fuera de la
   pantalla inicial, que es justo lo que hay que enseñar. */
@media (max-width: 620px) {
  .filters {
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    margin-inline: calc(-1 * clamp(1.1rem, 4vw, 2.5rem));
    padding-inline: clamp(1.1rem, 4vw, 2.5rem);
  }

  .filters::-webkit-scrollbar { display: none; }
  .filters .chip { flex: none; scroll-snap-align: start; }
}

.work-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(1rem, 2.4vw, 1.75rem);
}

/* Ancho alterno: 7 y 5 columnas, invirtiéndose por fila. Rompe la monotonía
   del grid uniforme sin necesidad de imágenes de distinto tamaño. */
.work:nth-child(4n + 1), .work:nth-child(4n + 4) { grid-column: span 7; }
.work:nth-child(4n + 2), .work:nth-child(4n + 3) { grid-column: span 5; }

.work[hidden] { display: none; }

@media (max-width: 900px) {
  .work-grid { grid-template-columns: 1fr; }
  .work:nth-child(n) { grid-column: 1 / -1; }
}

.work {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--edge), var(--shadow-sm);
  overflow: hidden;
  transition: border-color 0.25s var(--ease),
              box-shadow 0.25s var(--out),
              transform 0.25s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  .work:hover {
    border-color: var(--border-strong);
    box-shadow: var(--edge), var(--shadow-lg);
    transform: translateY(-3px);
  }

  /* La misma tarjeta usada sólo como marco de una captura: en el caso de
     estudio y en la prueba de una página de solución no es un enlace, así que
     no debe levantarse al pasar el cursor prometiendo que lo es. */
  .work--static:hover { transform: none; box-shadow: var(--edge), var(--shadow-sm); border-color: var(--border); }
}

/* Tampoco debe robar el clic, y eso no depende del tipo de puntero. */
.work--static { pointer-events: none; }

/* La captura grande que abre un caso de estudio, a ancho de contenedor. */
.case-shot { margin-bottom: clamp(3rem, 7vw, 5.5rem); }

/* El suelo sobre el que se apoya esa captura mientras carga y allí donde la
   imagen no llena el marco.

   Iba en la vista como `style="background:var(--surface-2)"` y estaba mal por
   la regla 2 del sistema: --surface-2 en tema claro es MÁS oscuro que la
   superficie y en oscuro es MÁS claro, así que un hueco pintado con él se
   hunde en un tema y flota en el otro. Para hundir se usa --bg, que es siempre
   el valor más lejano al lector en los dos temas. */
.case-shot__plate { background: var(--bg); }

/* El título de la tarjeta es un enlace, pero el subrayado sólo aparece al
   pasar el cursor: subrayarlo siempre ensucia una rejilla de cuatro tarjetas. */
.work__body h3 a {
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1.5px;
  transition: text-decoration-color 0.15s var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .work:hover .work__body h3 a { text-decoration-color: currentColor; }
}

/* --- El marco de navegador ------------------------------------------------
   Comunica "esto está en producción", no "esto es un mockup". La barra de URL
   lleva el dominio real del cliente. */

.chrome {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

.chrome__dots { display: flex; gap: 0.35rem; flex: none; }

.chrome__dots i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--border-strong);
}

.chrome__url {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 1.35rem;
  padding: 0.22rem 0.7rem;
  border-radius: var(--radius-sm);
  background: var(--bg);
  border: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chrome__url svg { width: 10px; height: 10px; flex: none; }

/* Modo etiqueta: describe qué pantalla se ve, en vez de afirmar un dominio.
   Se distingue de una URL real —sin candado, en la tipografía del sitio y no
   en monoespaciada— para que nadie la lea como una dirección. */
.chrome__url--label {
  font-family: var(--font);
  font-size: 0.72rem;
  letter-spacing: -0.005em;
}

.chrome__url--empty { background: var(--surface-2); }

/* --- El recorrido de la captura -------------------------------------------
   La tarjeta muestra el encuadre superior. Al pasar el cursor, la imagen se
   desplaza dentro del marco y se ve la página completa sin salir de la
   tarjeta. La distancia se calcula por captura en una custom property, porque
   cada una tiene su propio alto. */

.shot {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--surface-2);
}

.shot img {
  width: 100%;
  height: auto;
  /* Explicativo, no interfaz: por eso 2.4 s y no 250 ms. La curva es la misma
     --out que usa el recorrido equivalente de la portada (.proof__frame img),
     así que las dos capturas del sitio se recorren igual. */
  transition: transform 2.4s var(--out);
  will-change: transform;
}

/* El foco y el data-revealed que pone site.js en táctil se quedan fuera de la
   consulta: el primero es teclado y el segundo es precisamente el camino
   táctil, que ya no pasa por hover. */
.work:focus-within .shot img,
.work[data-revealed="true"] .shot img {
  transform: translateY(calc(-1 * var(--scroll-distance, 38%)));
}

@media (hover: hover) and (pointer: fine) {
  .work:hover .shot img {
    transform: translateY(calc(-1 * var(--scroll-distance, 38%)));
  }
}

/* Atado al scroll, igual que el recorrido equivalente de la portada
   (.proof__frame img en home.css, con el porqué escrito allí). Va aquí también
   y no sólo en la portada porque son las MISMAS capturas: si en "/" la pantalla
   del cliente se recorre al bajar y en "/proyectos/" hay que pasar el cursor,
   el mismo contenido se comporta de dos maneras y el sitio se siente cosido de
   dos trozos.

   La línea de tiempo cuelga del marco, no de la imagen: la imagen es más alta
   que su caja y el recorrido empezaría fuera de tiempo. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .shot { view-timeline-name: --marco-shot; }

    .shot img {
      transition: none;
      animation: recorrido-shot linear both;
      animation-timeline: --marco-shot;
      animation-range: cover 25% cover 80%;
    }

    @keyframes recorrido-shot {
      from { transform: translateY(0); }
      to   { transform: translateY(calc(-1 * var(--scroll-distance, 38%))); }
    }
  }
}

.work__body { padding: 1.4rem 1.5rem 1.5rem; }

.work__industry {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.work__body h3 { margin: 0.55rem 0 0.6rem; }

.work__body p {
  font-size: 0.93rem;
  color: var(--text-mute);
  line-height: 1.55;
}

.work__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border);
  font-size: 0.78rem;
  color: var(--text-faint);
}

.work__tags li { display: flex; align-items: center; gap: 0.4rem; }
.work__tags li + li::before { content: "·"; }

.work__more {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 1.1rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--accent);
}

.work__more::after {
  content: "→";
  transition: transform 0.2s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  .work:hover .work__more::after { transform: translateX(3px); }
}

/* --- La prueba dentro de una página de solución ----------------------------
   Captura a la izquierda, texto del caso a la derecha. Vivía en un <style>
   dentro de la vista de solución, que viaja en el HTML de esa página y se
   vuelve a descargar en cada visita en vez de entrar en el CSS que el
   navegador ya tiene cacheado. */
.proof-grid {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
}

.proof-grid__text .work__industry { display: block; }
.proof-grid__text h3 { margin: 0.5rem 0 0.85rem; font-size: 1.6rem; }
.proof-grid__text p { line-height: 1.6; }
.proof-grid__text .btn { margin-top: 1.5rem; }

@media (max-width: 860px) {
  .proof-grid { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------------
   Tarjetas genéricas y listas
   --------------------------------------------------------------------------- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}

.card {
  display: flex;
  flex-direction: column;
  padding: 1.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--edge);
  transition: border-color 0.2s var(--ease),
              transform 0.2s var(--out),
              box-shadow 0.2s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
    box-shadow: var(--edge), var(--shadow-md);
  }
}

.card h3 { margin-bottom: 0.6rem; }
.card p { font-size: 0.94rem; color: var(--text-mute); }

/* La frase de la tarjeta cuando debajo viene un dato duro (precio, plazo).
   Repetía como `style="margin-bottom:1.25rem"` en la vista de precios: un
   estilo en línea es un componente al que le falta una variante. */
.card__say { margin-bottom: 1.25rem; }

/* Tarjeta que abre con una etiqueta ("Paso 01", "Al arrancar") en vez de con
   el título: la etiqueta necesita separarse del título que la sigue. */
.card .work__industry + h3 { margin-top: 0.5rem; }

/* Empuja el enlace de la tarjeta al fondo, para que todas las tarjetas de una
   fila lo alineen aunque su texto tenga distinta longitud. */
.card .work__more { margin-top: auto; padding-top: 1.1rem; }

.price {
  font-size: 1.45rem;
  font-weight: 700;
}

.card__icon {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  margin-bottom: 1.1rem;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
}

.card__icon svg { width: 18px; height: 18px; }

.checklist li {
  position: relative;
  padding-left: 1.75rem;
  font-size: 0.95rem;
  line-height: 1.55;
}

.checklist li + li { margin-top: 0.7rem; }

.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42rem;
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  background: var(--accent);
  /* La palomita se dibuja con una máscara para que herede el color del token
     y funcione igual en los dos temas sin duplicar el SVG. */
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 68% no-repeat;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 68% no-repeat;
}

/* Una lista larga de funciones se lee mal en una columna, pero en dos no cabe
   en un teléfono. Antes esto viajaba como `style="columns:2"` en la vista y el
   CSS lo desarmaba en móvil con un selector de atributo — un truco frágil que
   dependía de que nadie cambiara el texto del style. */
.checklist--cols { columns: 2; column-gap: 3rem; max-width: 56rem; }
.checklist--cols li { break-inside: avoid; }

@media (max-width: 700px) {
  .checklist--cols { columns: 1; }
}

/* ---------------------------------------------------------------------------
   Bento — la rejilla asimétrica
   ===========================================================================

   Sustituye a la rejilla de tarjetas idénticas en soluciones y servicios.

   Rejilla de 12 columnas en pares 7/5 que alternan de lado: la fila impar lleva
   la tarjeta ancha a la izquierda y la par a la derecha. Ese zigzag es lo que
   hace que el bloque se lea compuesto y no clonado, y a diferencia de una fila
   de anchos iguales nunca deja dos tarjetas pidiendo la misma atención.

   El número de tarjetas lo decide la base de datos —hoy ocho soluciones y seis
   servicios— así que una cantidad impar deja la última fila con una sola.

   Como van en pares, un elemento en posición impar siempre abre fila: si además
   es el último, está solo y ocupa las doce columnas. Ese es todo el arreglo, y
   por eso el selector combina :nth-child(odd) con :last-child.

   Aquí antes había un `grid-column: auto / -1` pensando que estiraría la
   huérfana hasta el borde. No lo hace: con el inicio en auto y el final fijo,
   la rejilla le da una sola columna y la ancla a la derecha, que dejaba a la
   última tarjeta convertida en una tira de 90 px.
   --------------------------------------------------------------------------- */

.bento {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(0.85rem, 1.8vw, 1.35rem);
}

/* El zigzag arranca en 900 px y no antes. En una tablet de 768 la columna de 5
   deja la tarjeta en unos 280 px, y ahí el pie ya no puede poner precio y
   entrega uno al lado del otro: parte "$45,000.00 / MXN" en dos renglones. Por
   debajo de 900 conviene más una sola columna con tarjetas anchas que dos
   estrechas con el dato de precio roto, que es justo el que se va a mirar. */
@media (min-width: 900px) {
  .bento { grid-template-columns: repeat(12, 1fr); }

  .bento > :nth-child(4n + 1) { grid-column: span 7; }
  .bento > :nth-child(4n + 2) { grid-column: span 5; }
  .bento > :nth-child(4n + 3) { grid-column: span 5; }
  .bento > :nth-child(4n + 4) { grid-column: span 7; }

  .bento > :nth-child(odd):last-child { grid-column: span 12; }
}

.bento__item {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(1.3rem, 2.4vw, 1.75rem);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--edge);
  transition: border-color 0.2s var(--ease),
              transform 0.2s var(--out),
              box-shadow 0.2s var(--out);
}

@media (hover: hover) and (pointer: fine) {
  .bento__item:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
    box-shadow: var(--edge), var(--shadow-md);
  }
}

.bento__title {
  margin: 0.55rem 0 0.6rem;
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.2;
}

/* Las anchas también pesan más tipográficamente. Sólo desde 760 px, que es
   donde empieza la rejilla de 12: por debajo todas ocupan el ancho completo y
   un tamaño distinto se leería como un error, no como jerarquía. */
@media (min-width: 760px) {
  .bento > :nth-child(4n + 1) .bento__title,
  .bento > :nth-child(4n + 4) .bento__title { font-size: 1.45rem; }
}

.bento__text {
  font-size: 0.93rem;
  line-height: 1.55;
  color: var(--text-mute);
}

/* Empuja el pie al fondo para que todas las tarjetas de una fila lo alineen,
   aunque su texto tenga distinta longitud. */
.bento__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.9rem;
  margin-top: auto;
  padding-top: 1.15rem;
  border-top: 1px solid var(--border);
}

.bento__foot + .bento__foot { border-top: 0; padding-top: 0.5rem; }

.bento__stat { font-size: 0.8rem; color: var(--text-mute); }
.bento__stat b { display: block; font-family: var(--mono); font-size: 0.95rem; font-weight: 700; color: var(--text); letter-spacing: -0.02em; }

.bento__go {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  flex: none;
  margin-left: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-mute);
  transition: background-color 0.2s var(--ease),
              color 0.2s var(--ease),
              border-color 0.2s var(--ease);
}

.bento__go svg { width: 14px; height: 14px; }

@media (hover: hover) and (pointer: fine) {
  .bento__item:hover .bento__go {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
  }
}

/* ---------------------------------------------------------------------------
   Preguntas frecuentes
   --------------------------------------------------------------------------- */

.faq { border-top: 1px solid var(--border); }

.faq details { border-bottom: 1px solid var(--border); }

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.3rem 0;
  font-size: 1.03rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: "";
  width: 1rem;
  height: 1rem;
  flex: none;
  background: currentColor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat;
  /* Gira en su sitio, ni entra ni sale: --in-out, que arranca y frena. */
  transition: transform 0.2s var(--in-out);
  color: var(--accent);
}

.faq details[open] summary::after { transform: rotate(45deg); }

/* LA RESPUESTA SE DESPLIEGA, Y ANTES APARECÍA DE GOLPE.
   El icono giraba en 200 ms —está ahí arriba— y la respuesta debajo se plantaba
   sin transición. Esa mezcla es peor que no animar nada: el ojo sigue el giro y
   el texto ya está puesto antes de que el giro termine.

   Se anima `::details-content`, que es la caja que el propio navegador pone
   alrededor del contenido de un <details>. Eso importa porque evita las dos
   alternativas, que eran peores: envolver la respuesta en un div —hay que
   tocar home.php, pricing.php y solution.php para lo mismo— o escribir un
   acordeón a mano en JavaScript y perder el comportamiento nativo, con su
   accesibilidad y su búsqueda dentro de la página.

   `interpolate-size` es lo que permite transicionar HACIA `auto`: sin él, una
   altura automática no se puede interpolar y no habría nada que animar.

   HASTA DÓNDE LLEGA, dicho claro: `::details-content` e `interpolate-size` son
   de Chromium. Donde no existan, las dos reglas se ignoran enteras y la
   respuesta sigue apareciendo de golpe — o sea, exactamente lo que hace hoy en
   todos los navegadores. Es una mejora progresiva, no un arreglo universal, y
   ninguna regla de aquí puede dejar el contenido inaccesible: `block-size: auto`
   es válido en cualquier navegador que entienda el pseudo-elemento. */
:root { interpolate-size: allow-keywords; }

.faq details::details-content {
  block-size: 0;
  overflow: hidden;
  /* 240 ms: dentro del presupuesto de un desplegable (150-250). --out porque
     el bloque ENTRA y sale; una curva que arranca lenta se siente pesada justo
     en el cuadro que el visitante está mirando. */
  transition: block-size 0.24s var(--out),
              content-visibility 0.24s allow-discrete;
}

.faq details[open]::details-content { block-size: auto; }

.faq__answer {
  padding-bottom: 1.4rem;
  max-width: 62ch;
  color: var(--text-mute);
  font-size: 0.97rem;
}

/* ---------------------------------------------------------------------------
   Formularios
   ===========================================================================

   Vive aquí y no en un <style> dentro de la vista del cotizador: en la vista
   viajaba en el HTML de esa página y se volvía a descargar en cada visita,
   mientras que aquí entra en el archivo que el navegador ya tiene cacheado. Es
   además donde tiene que estar el día que haya un segundo formulario.

   El tamaño de fuente de los campos no baja nunca de 16 px: por debajo de eso
   Safari en iOS hace zoom al enfocar un campo y deja la página descuadrada, y
   el visitante tiene que volver a alejarla a mano a media captura.
   --------------------------------------------------------------------------- */

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

@media (max-width: 620px) { .field-row { grid-template-columns: 1fr; } }

.field + .field, .field-row + .field, .field + .field-row, .field-row + .field-row {
  margin-top: 1.1rem;
}

.field label {
  display: block;
  margin-bottom: 0.45rem;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.field input, .field select, .field textarea {
  width: 100%;
  min-height: 2.9rem;
  padding: 0.75rem 0.95rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--out);
}

.field input::placeholder, .field textarea::placeholder { color: var(--text-faint); }

@media (hover: hover) and (pointer: fine) {
  .field input:hover, .field select:hover, .field textarea:hover { border-color: var(--border-strong); }
}

.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}

.field textarea { resize: vertical; min-height: 8.5rem; line-height: 1.55; }

.field-note { margin-top: 0.45rem; font-size: 0.82rem; color: var(--text-mute); }
.field-note a { color: var(--accent); text-decoration: underline; text-underline-offset: 0.15em; }
.field-note--center { text-align: center; }

.form-error {
  padding: 0.95rem 1.15rem;
  margin-bottom: 1.5rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent);
  background: var(--surface-2);
  font-size: 0.92rem;
  font-weight: 500;
}

/* --- Opciones en pastilla ---------------------------------------------------
   Reemplazan a los <select> de presupuesto y tiempos. Un desplegable en el
   teléfono abre la rueda nativa del sistema, que tapa el formulario y obliga a
   confirmar; las pastillas se contestan de un toque y dejan ver todas las
   opciones a la vez, que además es lo que hace que alguien se anime a marcar
   un presupuesto en vez de saltárselo.

   El input real sigue ahí, encima de la etiqueta y transparente: así el clic,
   el teclado y los lectores de pantalla funcionan sin JavaScript. */

.opts { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.opt { position: relative; display: inline-flex; }

.opt input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.opt label {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  margin: 0;
  padding: 0.55rem 1.05rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text-mute);
  cursor: pointer;
  transition: background-color 0.15s var(--ease),
              border-color 0.15s var(--ease),
              color 0.15s var(--ease),
              transform 0.14s var(--out);
}

/* Todo lo que se pulsa lleva escala de retroalimentación, y una pastilla de
   presupuesto se pulsa igual que un botón. Faltaba aquí y estaba en su gemela
   de la portada (.pick), que es el mismo control con otro nombre: el
   formulario largo se sentía muerto justo en los dos campos que más cuesta
   conseguir que alguien conteste. */
.opt input:active + label { transform: scale(0.97); }

/* Éste es el caso que más daño hacía: en un teléfono, la pastilla tocada antes
   se quedaba con el borde y el texto del hover, o sea vestida de elegida al
   lado de la que de verdad lo está. */
@media (hover: hover) and (pointer: fine) {
  .opt input:hover + label { border-color: var(--border-strong); color: var(--text); }
}

.opt input:checked + label {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.opt input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
   Cotizador
   --------------------------------------------------------------------------- */

.quote-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 18.5rem;
  gap: clamp(1.75rem, 3.5vw, 3rem);
  align-items: start;
}

@media (max-width: 940px) {
  .quote-layout { grid-template-columns: minmax(0, 1fr); }
}

/* Grupos numerados. El formulario sigue siendo de una sola página —cada paso
   de un asistente es una oportunidad de abandonar— pero se lee por bloques, que
   es lo que evita que siete campos seguidos parezcan un trámite. */
.qgroup { border: 0; padding: 0; margin: 0; min-width: 0; }

.qgroup + .qgroup {
  margin-top: 1.9rem;
  padding-top: 1.9rem;
  border-top: 1px solid var(--border);
}

.qgroup__head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 1.1rem;
}

.qgroup__n {
  display: grid;
  place-items: center;
  width: 1.6rem;
  height: 1.6rem;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 600;
}

.qgroup__title { font-size: 1.02rem; font-weight: 700; letter-spacing: -0.02em; }

/* Subgrupo con leyenda: lo usan presupuesto y tiempos, que son grupos de radios
   y necesitan un <legend> de verdad para que un lector de pantalla anuncie de
   qué son opciones. Lleva clase propia y no `.field` a propósito: `.field input`
   da a los campos alto, borde y relleno, y aplicado al radio invisible de cada
   pastilla le deja un área de clic más alta que la pastilla misma. */
.qsub { border: 0; padding: 0; margin: 0; min-width: 0; }

.field + .qsub, .field-row + .qsub, .qsub + .qsub { margin-top: 1.15rem; }

.qsub legend {
  padding: 0;
  margin-bottom: 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* El pie de envío se pega al fondo en móvil: con el teclado abierto y el
   formulario desplazado, el botón queda siempre a la vista y no hay que
   buscarlo. En escritorio no hace falta y se queda quieto. */
.quote__actions { margin-top: 1.75rem; }

.quote__actions .btn { width: 100%; }

@media (max-width: 940px) {
  .quote__actions {
    position: sticky;
    bottom: 0;
    z-index: 20;
    margin-top: 1.5rem;
    padding: 0.85rem 0 calc(0.85rem + env(safe-area-inset-bottom, 0px));
    background: var(--bg);
    border-top: 1px solid var(--border);
  }
}

/* --- Panel de garantías --------------------------------------------------- */

.trust {
  position: sticky;
  top: 5.75rem;
  padding: 1.4rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--edge), var(--shadow-sm);
}

@media (max-width: 940px) {
  .trust { position: static; }
}

.trust h2 {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 1.1rem;
}

.trust__row { display: flex; align-items: flex-start; gap: 0.75rem; }
.trust__row + .trust__row { margin-top: 0.95rem; }

.trust__row svg { width: 16px; height: 16px; flex: none; margin-top: 0.2rem; color: var(--accent); }

/* Los dos son <span> dentro de un <span>: sin display:block se imprimen
   seguidos en el mismo renglón y el rótulo se pega al texto. */
.trust__label { display: block; font-size: 0.88rem; font-weight: 600; letter-spacing: -0.01em; }
.trust__value { display: block; font-size: 0.82rem; color: var(--text-mute); line-height: 1.45; }

.trust__split {
  margin: 1.25rem 0;
  border: 0;
  border-top: 1px solid var(--border);
}

/* El botón de WhatsApp del panel ocupa todo el ancho: es la salida alterna
   del formulario y tiene que leerse como un bloque, no como un enlace suelto. */
.trust__cta { width: 100%; }

/* --- Acuse de recibo -------------------------------------------------------
   La página de gracias. La placa de acento confirma que algo sí pasó: es el
   único lugar del sitio donde el acento aparece como sello y no como acción. */
/* EL ÚNICO SITIO DEL SISTEMA DONDE SE GASTA PRESUPUESTO DE CELEBRACIÓN.

   Esta marca sale una sola vez, justo después de que alguien mandó su
   solicitud: es el momento más raro y más cargado de toda la visita, y era lo
   único quieto en una página de texto quieto. En cualquier otro control del
   sitio una animación así sobraría —se ven decenas de veces al día y cansan—,
   pero la regla de la frecuencia dice que es aquí donde esto se permite.

   Keyframes y no transición, al revés que en el resto del archivo: esto
   dispara una vez al cargar y nadie lo puede interrumpir a mitad, que es el
   único caso en que los keyframes no tienen la desventaja de reiniciarse desde
   cero. `both` deja el estado final puesto.

   No arranca de scale(0): nada en el mundo real aparece de la nada. Desde 0.92
   se lee como que la pieza se asienta, no como que se materializa. */
.success-mark {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  margin: 0 auto 1.5rem;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--shadow-md);
  animation: sello-entra 0.42s var(--out) both;
}

@keyframes sello-entra {
  from { opacity: 0; transform: scale(0.92); }
  to   { opacity: 1; transform: none; }
}

/* La paloma se TRAZA, de izquierda a derecha, como se hace una marca a mano.
   Es literalmente la misma técnica que .pick__tick en quoter.css: misma idea,
   mismo clip-path, y por eso las dos confirmaciones del sitio —elegir una
   opción y recibir la solicitud— se sienten de la misma familia.

   Entra a los 0.2s, con el sello ya casi asentado: primero llega la pieza,
   después se firma. Al revés se leen como dos cosas sueltas. */
.success-mark svg {
  width: 22px;
  height: 22px;
  animation: sello-paloma 0.4s var(--out) 0.2s both;
}

@keyframes sello-paloma {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* Honeypot: fuera de pantalla en vez de display:none. Algunos bots ignoran los
   campos con display:none justamente porque saben que son trampas. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------------------------------------------------------------------------
   Franja de CTA — el rótulo encendido
   ===========================================================================

   Antes era un bloque de tinta. Ahora es literalmente el rótulo del negocio:
   mismo acento que el botón principal, en toda superficie. De noche funciona
   todavía mejor: es el letrero que sigue prendido con la cortina metálica
   bajada alrededor.
   --------------------------------------------------------------------------- */

.cta {
  position: relative;
  padding: clamp(2.5rem, 6vw, 4.5rem);
  border-radius: var(--radius-lg);
  background: var(--cta-bg);
  color: var(--cta-fg);
  text-align: center;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

/* Filo de luz de 1px en el borde superior: la luz entra por arriba y se ve en
   el canto, no en una nube difusa. */
.cta::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: color-mix(in srgb, var(--cta-fg) 30%, transparent);
  pointer-events: none;
}

.cta > * { position: relative; }

.cta h2 { margin-bottom: 1rem; }
.cta p { max-width: 38rem; margin-inline: auto; color: color-mix(in srgb, var(--cta-fg) 82%, transparent); }

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* El botón dentro del rótulo encendido es la placa de papel, no otra vez el
   acento: necesita su propio contraste contra el fondo saturado.

   LA PLACA VA EN --on-accent, NO EN --surface. La intención siempre fue papel
   blanco, pero --surface se invierte con el tema: es #ffffff en claro y
   #0f172a en oscuro. En oscuro la "placa de papel" salía azul marino con el
   texto en el azul de acento encima.

   Medido: 3.45:1, por debajo del 4.5 que pide un texto de 15 px. El botón
   principal del cierre de /soluciones/<slug>/ y de /proyectos/<slug>/ era el
   menos legible de la página, justo donde se pide el clic.

   --on-accent sólo se declara en el bloque claro y el oscuro no lo redefine,
   así que es #ffffff en los dos. Sobre él, el acento da 5.31:1. Es la misma
   familia de error que la regla de --surface-2 en "Do's and Don'ts": usar un
   token que se invierte donde se quería un valor fijo. */
.cta .btn--primary {
  background: var(--on-accent);
  color: var(--accent);
  box-shadow: none;
}
.cta .btn--ghost {
  color: var(--cta-fg);
  border-color: color-mix(in srgb, var(--cta-fg) 40%, transparent);
}

@media (hover: hover) and (pointer: fine) {
  .cta .btn--primary:hover { background: var(--on-accent); }

  .cta .btn--ghost:hover {
    background: color-mix(in srgb, var(--cta-fg) 14%, transparent);
    border-color: var(--cta-fg);
  }
}

/* --- Etapas de trabajo ------------------------------------------------------
   ===========================================================================

   Las cuatro etapas de /proceso/. Existe para NO volver a dibujarlas como
   cuatro tarjetas iguales en fila: la portada ya cuenta los mismos cuatro
   pasos en su propia rejilla, y con el mismo tratamiento las dos páginas se
   leen como la misma página servida dos veces.

   Aquí la lectura es vertical y ancha: el número grande a la izquierda fija la
   columna, el contenido ocupa el resto, y lo único que separa una etapa de la
   siguiente es una regla de un píxel EN EL HUECO, no un marco alrededor de
   cada una. Cuatro cajas con borde son cuatro objetos; cuatro bloques con una
   línea entre ellos son una secuencia, que es lo que se está describiendo.

   El número no lleva la palabra "Paso" delante. El título de la etapa ya dice
   lo que pasa en ella, y "Paso 02: Te cotizamos" gasta un renglón en decir dos
   veces lo mismo.
   --------------------------------------------------------------------------- */

.stage + .stage {
  margin-top: clamp(1.75rem, 3.5vw, 2.75rem);
  padding-top: clamp(1.75rem, 3.5vw, 2.75rem);
  border-top: 1px solid var(--border);
}

.stage__n {
  display: block;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--accent-ink);
}

.stage h3 {
  margin-top: 0.35rem;
  font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.75rem);
  letter-spacing: -0.02em;
}

.stage p {
  margin-top: 0.7rem;
  max-width: 62ch;
  color: var(--text-mute);
}

@media (min-width: 760px) {
  .stage {
    display: grid;
    grid-template-columns: 5rem minmax(0, 1fr);
    column-gap: clamp(1.5rem, 4vw, 3rem);
  }

  /* El número se alinea con la línea base del título, no con el borde superior
     del bloque: colgado arriba se lee como una viñeta suelta. */
  .stage__n { padding-top: 0.5rem; }
  .stage__body { grid-column: 2; }
  .stage h3 { margin-top: 0; }
}

/* --- Cierre de página, registro bajo ----------------------------------------
   ===========================================================================

   El segundo cierre, y existe para que el primero vuelva a significar algo.

   Hasta ahora las ocho páginas de dentro terminaban con el MISMO rótulo
   encendido: panel de acento, h2 centrado, párrafo centrado, dos botones. Un
   panel que aparece ocho veces seguidas deja de ser un remate y pasa a ser
   papel tapiz — el visitante lo reconoce al segundo y deja de leerlo. Y el
   bloque centrado es, además, exactamente el patrón que la portada tiene
   prohibido repetir.

   Así que ahora hay dos registros y se reparten por intención:

     .cta      rótulo encendido. Se reserva para donde el visitante YA está
               decidiendo: página de solución y caso de estudio. Ahí el panel
               de acento gana.
     .handoff  este. Regla fina arriba, titular a dos voces pegado a la
               izquierda y los botones alineados a la derecha, en la misma
               banda. Es el encabezado asimétrico leído al revés, y cierra la
               página con la misma gramática con la que la abrió.

   Nada de fondo propio: el cierre bajo se apoya en el suelo de la sección que
   lo contiene. Lo que lo separa del contenido de arriba es la regla y el aire,
   no una caja más.
   --------------------------------------------------------------------------- */

.handoff {
  display: grid;
  gap: clamp(1.25rem, 3vw, 1.75rem);
  padding-top: clamp(2rem, 4.5vw, 3rem);
  border-top: 1px solid var(--border);
}

.handoff__title {
  display: grid;
  font-size: clamp(1.6rem, 1.1rem + 2.4vw, 2.9rem);
  line-height: 1.02;
  letter-spacing: clamp(-0.04em, -0.026em - 0.16vw, -0.028em);
  text-wrap: balance;
}

/* Mismas dos voces que `.lede__title`, mismo motivo. No reutiliza aquellas
   clases porque el cierre no es un encabezado de sección y no debe heredar sus
   tamaños si mañana el encabezado cambia. */
.handoff__lead { font-weight: 800; color: var(--text); }
.handoff__soft { font-weight: 400; color: var(--text-faint); }

.handoff__say {
  max-width: 48ch;
  color: var(--text-mute);
  text-wrap: pretty;
}

.handoff__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

@media (min-width: 860px) {
  .handoff {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "titulo acciones"
      "apoyo  acciones";
    column-gap: clamp(2rem, 5vw, 4rem);
    align-items: end;
  }

  .handoff__title   { grid-area: titulo; }
  .handoff__say     { grid-area: apoyo; }

  /* Asentados abajo, no centrados en la banda: los botones se alinean con la
     última línea del bloque de texto, que es lo que hace que los dos extremos
     cierren juntos en vez de flotar cada uno a su altura. */
  .handoff__actions {
    grid-area: acciones;
    justify-content: flex-end;
    padding-bottom: 0.2rem;
  }
}

/* ---------------------------------------------------------------------------
   Pie
   --------------------------------------------------------------------------- */

.footer {
  padding-block: clamp(3rem, 6vw, 4.5rem) 2rem;
  border-top: 1px solid var(--border);
  font-size: 0.9rem;
}

.footer__grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(3, 1fr);
  gap: 2.5rem;
  margin-bottom: 3rem;
}

@media (max-width: 780px) {
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 2rem; }
}

.footer h4 {
  margin-bottom: 1rem;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.footer__tagline { margin-top: 1rem; max-width: 26rem; color: var(--text-mute); }

.footer li + li { margin-top: 0.55rem; }
.footer a { color: var(--text-mute); transition: color 0.15s var(--ease); }

@media (hover: hover) and (pointer: fine) {
  .footer a:hover { color: var(--accent); }
}

/* En el pie los enlaces van uno debajo de otro y muy juntos: sin una altura
   mínima, en un teléfono se toca el de arriba queriendo el de abajo. */
@media (hover: none) {
  .footer a { display: inline-flex; align-items: center; min-height: 2.5rem; }
  .footer li + li { margin-top: 0; }
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
  color: var(--text-faint);
  font-size: 0.83rem;
}

/* ---------------------------------------------------------------------------
   Botón flotante de WhatsApp
   ===========================================================================

   Aparece tras 600 px de scroll u 8 s. Se oculta cuando el formulario de
   cotización está en pantalla: ahí el CTA correcto es enviar el formulario, no
   abandonar la página.
   --------------------------------------------------------------------------- */

.wa-fab {
  position: fixed;
  right: clamp(1rem, 3vw, 1.75rem);
  /* env() respeta la barra de gestos del iPhone. Sin esto el botón queda medio
     tapado por ella y el toque se lo lleva el sistema, no la página. */
  bottom: max(clamp(1rem, 3vw, 1.75rem), env(safe-area-inset-bottom, 0px));
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.75rem 1.25rem 0.75rem 0.85rem;
  /* 999px, no 100px: es el valor de píldora del sistema y lo usan el
     encabezado, los botones y el riel de progreso. A este tamaño los dos se
     ven idénticos, y por eso la deriva llevaba tiempo sin verse — pero dos
     valores para la misma forma es como empiezan las escalas rotas. */
  border-radius: 999px;
  background: var(--whatsapp);
  color: #05300f;
  font-size: 0.92rem;
  font-weight: 600;
  box-shadow: 0 6px 28px rgb(37 211 102 / 0.4);
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px) scale(0.96);
  transition: opacity 0.28s var(--out),
              transform 0.28s var(--out),
              visibility 0.28s;
}

.wa-fab[data-visible="true"] {
  opacity: 1;
  visibility: visible;
  transform: none;
}

@media (hover: hover) and (pointer: fine) {
  .wa-fab:hover { filter: brightness(1.06); }
}

/* La otra excepción a la regla de los controles. Y la peor de las dos: en
   teléfono es el botón de conversión más tocado del sitio, y al pulsarlo no
   devolvía nada — el único acuse era que se abriera otra aplicación encima,
   con lo que tarda.

   El selector lleva el atributo a propósito: el estado visible de la línea de
   arriba declara `transform: none`, y sin `[data-visible="true"]` esta regla
   tiene menos especificidad y no llega a aplicarse nunca. */
.wa-fab[data-visible="true"]:active { transform: scale(0.97); }

.wa-fab svg { width: 21px; height: 21px; flex: none; }

@media (max-width: 560px) {
  .wa-fab { padding: 0.85rem; }
  .wa-fab span { display: none; }
}

/* ---------------------------------------------------------------------------
   Peso de trazo de los iconos
   ---------------------------------------------------------------------------

   EL GROSOR SE FIJA AQUÍ Y NO EN CADA SVG, y no es por comodidad.

   Un `stroke-width` escrito en el marcado está en unidades del viewBox, no en
   píxeles: el mismo 2.5 pesa 1.75 px en un icono dibujado a 17 px y 1.04 px en
   uno de 10. Medidos los 29 iconos del sitio, el grosor REAL iba de 1.04 a
   2.10 px —el doble— aunque en el HTML sólo hubiera cinco valores. Eso es lo
   que se veía como falta de oficio: no cinco números, sino iconos que pesan
   distinto sin motivo.

   La regla es un peso constante en PANTALLA de ~1.4 px, así que el valor del
   viewBox sale de una cuenta y no del gusto:

       trazo = 1.4 × ancho del viewBox ÷ tamaño al que se dibuja

   Por eso los números suben según el icono es más pequeño. Un icono de 10 px
   necesita 3.4 para pesar lo mismo que uno de 21 px con 1.6.

   En CSS y no en el marcado porque `stroke-width` como propiedad gana al
   atributo: se corrige en un sitio, no en 25 archivos, y no puede volver a
   separarse. Si mañana cambia el tamaño al que se dibuja un icono, aquí está
   la cuenta para recalcularlo.
   --------------------------------------------------------------------------- */

.pick__glyph svg          { stroke-width: 1.6; }   /* 21 px */

.icon-sun,
.icon-moon,
.opening__fact-badge svg,
.shift__list svg          { stroke-width: 2; }     /* 17 px */

.btn__go,
.proof__go svg            { stroke-width: 2.4; }   /* 14 px */

.quoter__sure svg         { stroke-width: 2.6; }   /* 13 px */

.chrome__url svg          { stroke-width: 3.4; }   /* 10 px */

/* ---------------------------------------------------------------------------
   Entradas al hacer scroll
   --------------------------------------------------------------------------- */

[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s var(--out), transform 0.6s var(--out);
}

[data-reveal][data-in="true"] { opacity: 1; transform: none; }

/* --- El encabezado de sección entra por voces, no de una pieza ---------------

   Un `.lede` son tres cosas distintas —etiqueta, titular y frase de apoyo— y
   moverlas juntas las convierte en un bloque. Escalonadas 60 ms, se leen en el
   orden en que están escritas, que es el orden en que hacen falta.

   Los 60 ms no son un número suelto: es el mismo paso que ya usan las tarjetas
   de proyecto y las de precios. Si aquí fuera otro, la página tendría dos
   ritmos distintos para el mismo gesto.

   El PADRE deja de moverse y se mueven los hijos. Si se movieran los dos, el
   desplazamiento se sumaría y el titular saldría del doble de lejos que la
   etiqueta. Por eso la primera regla le devuelve al `.lede` su estado normal:
   está aquí para que la maquinaria de `data-in` siga sirviendo tal cual, sin
   un segundo observador ni una línea de JavaScript nueva. */
.lede[data-reveal] {
  opacity: 1;
  transform: none;
  transition: none;
}

.lede[data-reveal] > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.55s var(--out), transform 0.55s var(--out);
}

.lede[data-reveal][data-in="true"] > * { opacity: 1; transform: none; }

.lede[data-reveal][data-in="true"] > :nth-child(2) { transition-delay: 0.06s; }
.lede[data-reveal][data-in="true"] > :nth-child(3) { transition-delay: 0.12s; }

/* ---------------------------------------------------------------------------
   El cambio de tema
   ===========================================================================

   Dos caminos, y el navegador elige solo. La lógica está en site.js; aquí van
   las dos coreografías.

   CAMINO 1 — el círculo que crece (Chrome, Edge, Safari 18+)

   La API de View Transitions toma una foto del antes y del después y las
   superpone. Por defecto las cruza con una fundida; lo que se hace aquí es
   sustituir esa fundida por un círculo que se abre desde el botón del tema,
   con las coordenadas y el radio que site.js deja en --vt-x/y/r.

   El truco está en qué capa se anima. La foto del tema VIEJO se queda quieta y
   la del NUEVO se recorta con un círculo que crece por encima: así el tema que
   llega parece derramarse sobre el que se va. Al revés —animando la vieja— se
   ve como si la página se vaciara, que es la misma animación contada al revés
   y se siente mal sin que uno sepa por qué.

   z-index en las dos capas porque el orden de pintado por defecto deja la foto
   nueva DEBAJO, y entonces el recorte no se ve.
   --------------------------------------------------------------------------- */

::view-transition-old(root),
::view-transition-new(root) {
  /* Se apagan las animaciones que la API pone por su cuenta: sin esto, su
     fundida corre a la vez que el círculo y se ve un parpadeo a media
     apertura. */
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-old(root) { z-index: 0; }
::view-transition-new(root) { z-index: 1; }

:root[data-theme] {
  view-transition-name: root;
}

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-new(root) {
    animation: tema-abre 0.52s var(--in-out) both;
  }
}

@keyframes tema-abre {
  from { clip-path: circle(0 at var(--vt-x, 50%) var(--vt-y, 50%)); }
  to   { clip-path: circle(var(--vt-r, 150vmax) at var(--vt-x, 50%) var(--vt-y, 50%)); }
}

/* CAMINO 2 — la fundida de colores (Firefox, por ahora)

   Sólo vive mientras site.js mantiene la bandera, que son unos 400 ms. Fuera
   de ese rato no existe ni una transición de más: si esto estuviera declarado
   siempre, CADA cambio de color de la página —un :hover, un campo enfocado—
   arrastraría medio segundo de fundida y la interfaz entera se sentiría
   pastosa.

   Se enumeran las cuatro propiedades que de verdad cambian con el tema. Un
   `transition: all` aquí sería peor que en ningún otro sitio: pillaría también
   transform y clip-path de todo lo que hay en pantalla. */
:root[data-theme-anim] *,
:root[data-theme-anim] *::before,
:root[data-theme-anim] *::after {
  transition: background-color 0.38s var(--ease),
              border-color 0.38s var(--ease),
              color 0.38s var(--ease),
              fill 0.38s var(--ease) !important;
}

/* El telón de la intro queda fuera: tiene su propia coreografía con clip-path
   y una fundida de color encima la haría tropezar. */
:root[data-theme-anim] .intro,
:root[data-theme-anim] .intro * {
  transition: none !important;
}

/* ---------------------------------------------------------------------------
   Movimiento reducido
   ===========================================================================

   No es un extra de accesibilidad: sin esto, el recorrido de las capturas y
   las entradas escalonadas marean de verdad a quien tiene un trastorno
   vestibular. La página funciona igual de bien quieta.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  [data-reveal] { opacity: 1; transform: none; }

  /* Los hijos del encabezado tienen su propio estado de partida, y el reset de
     `*` de aquí arriba sólo acorta la duración: sin esta línea se quedarían
     invisibles para quien pide menos movimiento, que es lo contrario de lo que
     pide. Lo mismo para las dos columnas del antes/después. */
  .lede[data-reveal] > * { opacity: 1; transform: none; }

  .shift__col[data-reveal] { opacity: 1; transform: none; filter: none; }

  .work:hover .shot img,
  .work[data-revealed="true"] .shot img { transform: none; }

  /* Una animación atada al scroll no tiene duración propia —la marca el
     desplazamiento—, así que el reset de `animation-duration` de aquí arriba no
     la detiene. Hay que apagarla por nombre. */
  .shot img { animation: none; }

  /* El reset universal de aquí arriba llega a `*`, `*::before` y `*::after`, y
     ::details-content no es ninguno de los tres: sin esta línea, quien pide
     menos movimiento se quedaba con el único despliegue de 240 ms del sitio
     todavía corriendo. El resto de lo que se añadió con él —el sello de la
     página de gracias y la entrada de las tarjetas al filtrar— sí lo cubre el
     reset, y además termina en su estado visible, así que se planta puesto en
     vez de desaparecer. */
  .faq details::details-content { transition-duration: 0.01ms !important; }

  /* El spinner es información, no adorno: es la única señal de que el envío
     está en curso. Se frena a un tercio de velocidad, no se apaga.

     Lleva !important porque si no, el reset universal de aquí arriba le gana:
     `*` con !important vence a cualquier regla de clase sin él, por más
     específica que sea. La versión sin !important vivía en home.css y llevaba
     tiempo sin aplicarse — el resultado real era un aro congelado, que se lee
     como que el botón se trabó. */
  .spin {
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
}

/* ---------------------------------------------------------------------------
   Páginas legales
   ===========================================================================

   Términos y aviso de privacidad. Antes esta clase se usaba en las dos vistas
   pero no existía en el CSS: los <h2> se pegaban al párrafo anterior y —peor—
   las listas salían sin viñeta y sin sangría, porque el reset global apaga
   `list-style` para toda la interfaz. En un aviso de privacidad esas listas
   son las que enumeran qué datos se recaban y para qué: tienen que leerse
   como lista.
   --------------------------------------------------------------------------- */

.legal { max-width: 46rem; }

.legal .eyebrow { margin-bottom: 0.9rem; }

/* Más chico que un h1 de página comercial a propósito: un documento legal no
   compite por atención, se consulta. */
.legal h1 {
  margin-bottom: 0.5rem;
  font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.9rem);
}

.legal__updated { margin-bottom: 2.5rem; color: var(--text-mute); font-size: 0.9rem; }

.legal h2 {
  margin-top: 2.75rem;
  margin-bottom: 0.75rem;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
}

/* El primer encabezado va pegado a la fecha de actualización, no separado por
   el mismo hueco que separa dos secciones. */
.legal h2:first-of-type { margin-top: 0; }

.legal p { margin-bottom: 1rem; line-height: 1.7; }

.legal ul {
  margin: 0 0 1.25rem;
  padding-left: 1.2rem;
  list-style: disc;
}

.legal li { margin-bottom: 0.4rem; line-height: 1.65; }
.legal li::marker { color: var(--accent); }

.legal a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

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

/* Visible sólo para lectores de pantalla. Se usa para completar el texto de
   enlaces como "Ver caso", que fuera de contexto no dicen de qué caso. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   Impresión
   --------------------------------------------------------------------------- */

@media print {
  .header, .wa-fab, .cta, .filters { display: none; }
  body { background: #fff; color: #000; }
}
