/* DB Project — base: reset, escala de espaciado, contenedor, ritmo de sección.
   Reemplaza a css/layout.css (nav/hilo/footer se fueron a css/chrome.css). */

:root {
  /* Múltiplos de 8, sin excepciones. La escala anterior (6/10/16/20/32/40/64/88)
     mezclaba múltiplos de 2 y de 8 y no daba un ritmo vertical consistente. */
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-5: 48px;
  --space-6: 64px;
  --space-7: 96px;
  --space-8: 128px;

  --container-max: 1280px;
  --container-pad: 24px;
  --nav-height: 96px;
  /* Lo que hay que dejar libre arriba para que la barra no tape nada. No es lo
     mismo que --nav-height: esto incluye el recorte de pantalla del teléfono.
     La barra vive en top: --space-3 + safe-area y la píldora mide ~56px; el
     --space-3 final es aire, no cálculo. Lo usa toda sección que pueda quedar
     con su encabezado pegado al tope — hoy, la 05, que se pinea. */
  --nav-clearance: calc(var(--space-3) + env(safe-area-inset-top, 0px) + 56px + var(--space-3));

  /* Escala z semántica — ningún 9999 suelto en todo el sitio. */
  --z-thread: 40;
  --z-nav: 50;
  --z-overlay: 60;
}

@media (min-width: 768px) {
  :root { --container-pad: 56px; }
}

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

/* El navegador declara [hidden] { display: none } con especificidad de tipo, así
   que CUALQUIER regla de clase con display se lo lleva por delante. Pasó con el
   encabezado de la pasarela: con un solo proyecto el JS le pone hidden y el
   contador seguía a la vista, porque .pasarela__cab declara display:flex.
   El !important acá es correcto: hidden no es una preferencia de estilo, es una
   afirmación sobre si el elemento existe para el visitante. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  /* El nav flota con position:fixed y no reserva espacio; esto evita que un
     salto de ancla deje el título de la sección debajo de la píldora. */
  scroll-padding-top: var(--nav-height);
  /* La canaleta de la barra de scroll se reserva siempre. Es lo que evita que
     bloquear el scroll al abrir una ficha desplegable corra la página unos
     píxeles a la derecha: sin esto, la barra desaparece y el contenido se
     recentra en el ancho que sobra. En las páginas largas la canaleta ya estaba
     ocupada, así que no cambia nada de lo que se ve. */
  scrollbar-gutter: stable;
}
/* Mientras una ficha desplegable está abierta, el fondo no scrollea. Va en
   <html> y no en <body>: el sitio tiene secciones pineadas por ScrollTrigger, y
   sacar el body del flujo les cambia el alto del documento debajo de los pies.
   Con overflow el navegador conserva la posición y ScrollTrigger no se entera. */
html.ficha-abierta { overflow: hidden; }

body { margin: 0; }
img { max-width: 100%; display: block; }

/* El padding lateral nunca baja del recorte de la pantalla: en horizontal, en un
   teléfono con notch, el texto quedaría cortado contra el borde. max() elige el
   mayor de los dos, así en pantallas sin recorte queda exactamente como antes. */
.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-left: max(var(--container-pad), env(safe-area-inset-left, 0px));
  padding-right: max(var(--container-pad), env(safe-area-inset-right, 0px));
}

/* Toda sección que aloje una entrada con desplazamiento HORIZONTAL tiene que
   recortar: mientras esos bloques están fuera de pantalla esperando su entrada,
   viven corridos hacia un costado, y si la sección no los recorta ese
   desplazamiento se suma al ancho scrolleable del documento y aparece una barra
   horizontal en toda la página.
   Encontrado midiendo: en 375px, la tarjeta de servicios con data-enter="right"
   quedaba en x=+32 y llevaba el scrollWidth del documento a 383 sobre 375. La
   comparación ya tenía su recorte propio; servicios no, y la regla suelta no
   iba a cubrir la próxima sección que usara una entrada lateral.
   clip y no hidden: hidden convierte a la sección en contenedor de scroll y
   rompe position:sticky de cualquier descendiente. */
.section:has([data-reveal="cross"]),
/* Las tarjetas que entran desde su lado con rotación (secciones 02 y 04) se
   salen unos píxeles MIENTRAS dura la entrada: medido, 1px a 375 y 3,5px a
   414. En reposo no sobran, pero ese desborde transitorio alcanza para que
   parpadee la barra horizontal en un celular. */
.section:has([data-enter]),
/* La 03 no tiene tarjetas pero su entrada también es lateral: el interruptor y
   las frases llegan desde la izquierda y el símbolo desde la derecha. */


/* El ritmo vertical entre secciones. Estaba en 96 (mobile) y 128 (desktop) por
   lado, así que entre el último contenido de una sección y la marca de la
   siguiente quedaban 192 y 256px de nada. Medido en la costura 02→03: 268px
   vacíos debajo del borde inferior de un bloque con borde, que se lee como que
   la página se cortó. Baja un escalón de la escala en cada corte: sigue siendo
   espacio negativo generoso (192px son el 21% de un viewport de 900) sin el
   agujero. El cambio es global a propósito — el hueco existía en todas las
   costuras, la 02→03 era sólo donde más se notaba. */
/* Baja otro escalón de la escala. Cada bloque se comía una pantalla y media y
   ninguna sección entraba entera en un viewport de desktop sin scrollear dentro
   de ella. 64px de aire arriba y abajo en desktop siguen siendo espacio
   negativo generoso — el 7% de un viewport de 900 por lado — y devuelven casi
   una pantalla por sección a lo largo de la página. Múltiplos de 8, como toda
   la escala. */
/* Las secciones que cruzan dos bloques recortan su propio desborde lateral.
   data-reveal="cross" desplaza cada lado 200px en desktop, y esos 200px salen de
   la pantalla mientras dura el cruce: medido en Servicios a 1440, .formato__texto
   terminaba en x=1497 sobre un viewport de 1440 — 57px de barra horizontal en
   toda la página, y sólo mientras la animación estaba a mitad de camino, que es
   la peor clase de bug porque no está siempre.
   clip y no hidden: hidden crea un contenedor de scroll y rompe el position:
   sticky de las escenas que viven en otras secciones.
   Antes esta regla existía pero atada a un atributo de UNA sección (data-caminos).
   Va sobre el mecanismo, no sobre el lugar: cualquier sección que cruce, recorta. */
.section:has([data-reveal="cross"]) { overflow-x: clip; }

/* ── El aire de una sección depende de lo que la sección tiene adentro ────────
   Antes era var(--space-5) para todas: medido, 64px arriba y 64 abajo en las
   siete secciones del Inicio y en las siete de landing, sin una excepción. Eso
   es lo que hace que el sitio "respire igual" en todos lados y se lea como una
   plantilla rellenada.

   Ahora hay tres respiraciones, y la decide el contenido —no una lista de
   excepciones escritas a mano sección por sección, que sería el mismo problema
   con más líneas—:

     por defecto   una sección normal, con título y cuerpo
     apretada      la que ya trae una grilla de piezas: la grilla pone su propio
                   ritmo y sumarle el aire de afuera la deja flotando
     amplia        la que es sólo una afirmación: sin grilla que la sostenga,
                   el aire ES el contenido

   Y en clamp, no en un valor fijo: en 375px el respiro grande no es grande, es
   media pantalla perdida. */
.section { padding-block: clamp(2rem, 4.4vw, 3.25rem); }

/* Apretada: ya tiene una grilla adentro. */
.section:has(.card, .rubro, .plan, .paso, .formato, .compara__col, .precio) {
  padding-block: clamp(1.75rem, 3.4vw, 2.5rem);
}

/* Amplia: es una sola afirmación y el aire la sostiene. */
.section.cierre,
.section.cierre-formato,
.section.parana {
  padding-block: clamp(3rem, 6vw, 4.5rem);
}
/* En desktop había un @media que reponía var(--space-6) —64px— para TODAS las
   secciones, y era el origen real de que todo respirara igual: pisaba cualquier
   matiz que se intentara arriba. Ahora sube el techo de las tres respiraciones
   en vez de aplanarlas a un solo número. */
@media (min-width: 1024px) {
  .section { padding-block: var(--space-5); }
  .section:has(.card, .rubro, .plan, .paso, .formato, .compara__col, .precio) {
    padding-block: var(--space-4);
  }
  .section.cierre,
  .section.cierre-formato,
  .section.parana { padding-block: var(--space-7); }
}

h1, h2, h3 {
  text-wrap: balance;
  margin-block: 0 var(--space-3);
}
h2 { font-size: var(--db-fs-h2); }
h3 { font-size: var(--db-fs-h3); }

/* ── DOS REGISTROS DE TITULAR, NO UNO EN DOS TAMAÑOS ──────────────────────────
   design-system/typography.css les da a h1, h2 y h3 el mismo peso (600) y a h1
   y h2 el mismo interletrado (-0.025em). Con la fuente igual, el peso igual y
   el tracking igual, lo único que distinguía un título de página de uno de
   sección era cuántos píxeles medía — y a 40px un h2 y a 64 un h1 se leen como
   el mismo objeto a dos distancias, no como dos niveles.

   Se separan por dos ejes a la vez, que es lo que hace que la diferencia se lea
   incluso cuando los tamaños se acercan:

     TÍTULO DE PÁGINA (h1, .display)   denso: tracking más cerrado y renglón más
                                       corto. Es una afirmación, y se compacta.
     TÍTULO DE SECCIÓN (h2)            abierto: peso 500 y tracking casi neutro.
                                       Es un rótulo del recorrido, y respira.

   Ni una fuente nueva, ni un peso que no esté ya cargado (el @import trae 400,
   500, 600 y 700). Los tokens de design-system no se tocan: esto es tratamiento
   del sitio, no de la identidad. */
h1, .display {
  letter-spacing: -0.035em;
  line-height: 1.05;
}
h2 {
  font-weight: 500;
  letter-spacing: -0.005em;
  line-height: 1.18;
}
/* h3 se queda en 600: es el nivel de bloque, vive dentro de tarjetas y paneles y
   ahí el peso es lo que lo separa del párrafo que tiene pegado abajo. */

/* El texto de apoyo BAJA. Estaba en --db-gris-claro (8,06:1 contra el grafito),
   casi tan encendido como un título en blanco: con esa distancia, un párrafo y
   un titular pesaban casi lo mismo y ninguna sección tenía jerarquía a
   distancia. Ahora va en --db-gris-medio (4,97:1) y el rango entre título y
   párrafo pasa de 16,9→8,1 a 16,9→5,0. Sigue siendo AA de sobra. */
p {
  text-wrap: pretty;
  max-width: 68ch;
  margin-block: 0 var(--space-2);
  /* La prosa crece de 16 a 20px con el ancho (--db-fs-cuerpo). No es lo mismo
     que --db-fs-body, que quedó para controles: un botón de 20px es un botón
     gordo, un párrafo de 16px al lado de un título de 110 es una nota al pie. */
  font-size: var(--db-fs-cuerpo);
  color: var(--db-gris-medio);
  line-height: 1.65; /* texto claro sobre fondo oscuro pide más aire */
}

a { color: inherit; }

.mono { font-variant-numeric: tabular-nums; }

:focus-visible {
  outline: 2px solid var(--db-acento);
  outline-offset: 3px;
}

/* Texto que existe para el lector de pantalla y no para la vista. Se usa en
   controles cuyo significado ya está dicho por su forma (el botón de pausa de
   la franja es un cuadrado, no una palabra). No se usa display:none ni
   visibility:hidden: las dos lo sacarían también del árbol de accesibilidad,
   que es justo lo contrario de lo que se busca. */
.solo-lectores {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Salto al contenido: para quien navega con teclado y no quiere tabular por
   toda la barra y el índice de secciones en cada página. */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: -100px;
  z-index: var(--z-overlay);
  background: var(--db-acento);
  color: var(--db-grafito);
  font-weight: 600;
  padding: var(--space-2) var(--space-3);
  border-radius: 8px;
  text-decoration: none;
  transition: top 0.2s ease;
}
.skip-link:focus { top: var(--space-2); }

/* ── Tramo claro ─────────────────────────────────────────────────────────────
   El recorrido alterna claro y oscuro por tramos, y un tramo claro no es "lo
   mismo con otro background": cambia el material entero. En vez de escribir una
   variante clara de cada regla que vive adentro, el tramo REDEFINE los tokens
   una sola vez y todo lo que usa var(--db-…) se adapta solo.

   Incluido --db-acento: el teal pleno da 2,83:1 contra el claro y no alcanza ni
   para texto ni para un borde que informe. Adentro de un tramo claro el mismo
   teal vale su versión oscurecida, así "el acento" sigue siendo un solo
   concepto en todo el sitio y no dos colores que hay que recordar cuál va
   dónde. --db-acento-apagado se redeclara con la misma fórmula para que se
   recalcule sobre el acento local, y sube de 34% a 55%: sobre claro, un teal
   al 34% de opacidad directamente no se ve.

   El grano va en un ::before fijo al tramo y con pointer-events:none. Es una
   imagen tileada, no un filtro: se rasteriza una vez. */
.tramo-claro {
  --db-texto-sobre-oscuro: var(--db-texto-sobre-claro);
  --db-texto-secundario-sobre-oscuro: var(--db-texto-secundario-sobre-claro);
  --db-gris-claro: var(--db-texto-secundario-sobre-claro);
  --db-gris-medio: var(--db-texto-secundario-sobre-claro);
  --db-acento: var(--db-acento-sobre-claro);
  --db-acento-apagado: color-mix(in srgb, var(--db-acento) 55%, transparent);
  --db-linea: color-mix(in srgb, var(--db-grafito) 14%, transparent);
  --db-linea-fuerte: color-mix(in srgb, var(--db-grafito) 30%, transparent);
  --db-superficie: var(--db-blanco);

  position: relative;
  isolation: isolate;
  background: var(--db-tramo-claro);
  color: var(--db-texto-sobre-claro);
}
.tramo-claro::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--db-grano);
  opacity: 0.5;
  pointer-events: none;
}
/* Sobre claro el anillo de foco en teal oscurecido se ve, pero el grafito se ve
   mejor: es el color de máximo contraste de este material. */
.tramo-claro :focus-visible { outline-color: var(--db-grafito); }

/* Botones — compartidos por las cinco páginas */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  /* Piso de área táctil, ronda 2026-09-06. Era 44px —el mínimo de WCAG 2.5.8—
     y el mínimo no es el objetivo: con 44 el botón entra en la norma y sigue
     siendo incómodo de tocar. 48 en desktop, 52 en teléfono (abajo), que es el
     ancho real del pulgar y no el del puntero. Lo hereda TODO .btn, así que
     ningún nivel puede quedar por debajo por olvido. */
  min-height: 48px;
  padding: 0 var(--space-3);
  border: 1px solid transparent;
  /* Fondo explícito, y no heredado. Hasta acá todos los .btn eran <a>, que nacen
     con fondo transparente, así que .btn--ghost no necesitaba declararlo. El
     primer <button class="btn btn--ghost"> del sitio salió con el gris de botón
     del navegador y letras grises adentro. */
  background: none;
  border-radius: 10px;
  color: inherit;
  font-family: inherit;
  font-size: var(--db-fs-body);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  /* Sin `transform` en la transición: el transform del botón lo escribe GSAP —
     la entrada del CTA (data-reveal="cta") y la respuesta al click
     (js/botones.js). Dos dueños del mismo transform hacen que GSAP lea mal el
     destino de sus tweens y el botón quede clavado a mitad de camino. */
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease,
              filter 0.2s ease;
}
/* Respaldo del click sin transform, para cuando GSAP no cargó: el botón igual
   acusa recibo. */
.btn:active { filter: brightness(0.92); }
/* ── TRES NIVELES DE BOTÓN ────────────────────────────────────────────────────
   Antes eran dos nombres del mismo botón: relleno o contorno, y los dos con el
   mismo arco girando en el borde. Con el mismo tratamiento encima, la diferencia
   de peso no se leía, y una página con seis botones tenía seis llamados que
   pesaban igual.

   Ahora cada nivel trae una pieza DISTINTA de db-project-biblioteca,
   references/botones-formularios.md, entrada "Brillo de borde en botón":

   · Nivel 1 · .btn--primary  → variante 2, "Moving Border": una luz recorre el
     perímetro. Es la única acción real de su sección.
   · Nivel 2 · .btn--ghost    → variante 3, "Hover Border Gradient": el borde se
     enciende desde un lado, no da la vuelta. Portado a CSS puro (el original
     rota entre cuatro lados con setInterval; acá el lado lo decide el hover, que
     es la misma idea sin un temporizador por botón en la página).
   · Nivel 3 · .btn--texto    → receta del catálogo "Tailwind CSS buttons": sin
     caja, sólo texto y flecha, con el subrayado que se dibuja. Es el idioma de
     enlace que el sitio ya usa en el pie y en las fichas, ascendido a nivel.

   La diferencia se lee sin comparar: uno tiene relleno, otro tiene contorno, el
   tercero no tiene caja. */

/* ── Nivel 1 · primario ───────────────────────────────────────────────────── */
.btn--primary {
  background: var(--db-acento);
  color: var(--db-grafito);
  font-weight: 600;
}
.btn--primary:hover { background: color-mix(in srgb, var(--db-acento) 84%, var(--db-blanco)); }

/* ── Nivel 2 · secundario ─────────────────────────────────────────────────── */
.btn--ghost {
  /* 2px y blanco al 42%, no 1px al 22%. El contorno de 1px apenas visible era
     la mitad del problema de "todos los botones se ven iguales": un secundario
     cuyo borde no se lee es un terciario con más padding. El nivel 2 tiene que
     poder señalarse en una foto de los tres juntos sin compararlos. */
  border-width: 2px;
  border-color: color-mix(in srgb, var(--db-blanco) 42%, transparent);
  color: var(--db-texto-sobre-oscuro);
  font-weight: 500;
}
.btn--ghost:hover {
  border-color: var(--db-acento);
  color: var(--db-acento);
  background: color-mix(in srgb, var(--db-acento) 10%, transparent);
}
/* Sobre el tramo claro el blanco al 42% desaparece: ahí el contorno es grafito. */
.tramo-claro .btn--ghost { border-color: color-mix(in srgb, var(--db-grafito) 38%, transparent); }

/* ── Nivel 3 · terciario ──────────────────────────────────────────────────── */
.btn--texto {
  min-height: 48px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--db-acento);
  font-weight: 500;
}
.btn--texto:hover { color: color-mix(in srgb, var(--db-acento) 80%, var(--db-blanco)); }
/* Sin relleno ni contorno, el terciario no puede acusar el click con el fondo:
   lo acusa hundiéndose un pelo. */
.btn--texto:active { filter: none; opacity: 0.7; }
/* 53px y no 48. .btn--sm vive dentro de la píldora de la barra, y la píldora se
   compacta a escala 0.92 al pasar los 100px de scroll. El objetivo táctil que
   cuenta es el que queda EN PANTALLA, y 48 × 0.92 = 44.2 — medido con sonda en
   el Inicio a 1440: el "Contactanos" de la barra era el único botón del sitio
   por debajo del piso de 48. 53 × 0.92 = 48.8, que sí lo cumple compactado.
   Debajo de 860px este botón no existe (.nav__pill > .btn { display: none }),
   así que el piso de 52 de teléfono no lo alcanza nunca. */
.btn--sm { min-height: 53px; padding: 0 var(--space-2); font-size: var(--db-fs-small); }
.btn--lg { min-height: 56px; padding: 0 var(--space-4); font-size: var(--db-fs-lead); }

/* El piso sube a 52 en teléfono, que es donde entra el comprador de este sitio.
   Va en una sola regla y no repartido por variante: cualquier botón nuevo que
   se escriba mañana queda cubierto sin acordarse de nada. */
@media (max-width: 599px) {
  /* .btn--sm y .btn--texto van nombrados porque los dos declaran su propio
     min-height más arriba con la misma especificidad. .ver-mas NO va acá: vive
     en css/components.css, que se carga después, así que esta regla nunca le
     ganaba — su piso de 52 se declara allá, al lado de su definición. */
  .btn,
  .btn--sm,
  .btn--texto { min-height: 52px; }
}

/* ── Ícono de WhatsApp ───────────────────────────────────────────────────────
   Un solo dibujo para todo el sitio: el glifo oficial de WhatsApp (simple-icons,
   CC0) como máscara CSS, no como <svg> repetido en cada botón. Así el ícono se
   declara una vez y cada botón lo usa con un <span> vacío.

   background: currentColor + mask, y no un fill fijo: el ícono toma el color del
   texto del botón, así funciona igual sobre el relleno de acento (grafito) que
   sobre el borde (blanco) sin declarar una variante por cada uno.

   1.15em y no un tamaño en px: el ícono crece con el botón que lo contiene, que
   va de .btn--sm a .btn--lg. */
.ico-wa {
  --ico-wa: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.465 3.488'/%3E%3C/svg%3E");
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--ico-wa) center / contain no-repeat;
  mask: var(--ico-wa) center / contain no-repeat;
}

/* El sobre. Mismo mecanismo que .ico-wa —máscara CSS sobre currentColor, un solo
   dibujo declarado una vez— para que los dos canales del cierre se lean como la
   misma familia y no como un ícono importado al lado de otro. Dibujado acá:
   contorno de 1,6 y esquinas redondeadas, el mismo trazo que el resto del sitio. */
.ico-mail {
  --ico-mail: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='4.5' width='19' height='15' rx='2.5'/%3E%3Cpath d='m3 7 8.15 5.44a1.5 1.5 0 0 0 1.7 0L21 7'/%3E%3C/svg%3E");
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--ico-mail) center / contain no-repeat;
  mask: var(--ico-mail) center / contain no-repeat;
}

/* Instagram. Mismo mecanismo que .ico-wa y .ico-mail —máscara CSS sobre
   currentColor, un solo dibujo declarado una vez— y de la misma fuente que el de
   WhatsApp: el glifo oficial de simple-icons (CC0). Los tres canales de contacto
   tienen que leerse como la misma familia, no como tres íconos importados de
   tres lados. */
.ico-ig {
  --ico-ig: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 0C8.74 0 8.333.015 7.053.072 5.775.132 4.905.333 4.14.63c-.789.306-1.459.717-2.126 1.384S.935 3.35.63 4.14C.333 4.905.131 5.775.072 7.053.012 8.333 0 8.74 0 12s.015 3.667.072 4.947c.06 1.277.261 2.148.558 2.913.306.788.717 1.459 1.384 2.126.667.666 1.336 1.079 2.126 1.384.766.296 1.636.499 2.913.558C8.333 23.988 8.74 24 12 24s3.667-.015 4.947-.072c1.277-.06 2.148-.262 2.913-.558.788-.306 1.459-.718 2.126-1.384.666-.667 1.079-1.335 1.384-2.126.296-.765.499-1.636.558-2.913.06-1.28.072-1.687.072-4.947s-.015-3.667-.072-4.947c-.06-1.277-.262-2.149-.558-2.913-.306-.789-.718-1.459-1.384-2.126C21.319 1.347 20.651.935 19.86.63c-.765-.297-1.636-.499-2.913-.558C15.667.012 15.26 0 12 0zm0 2.16c3.203 0 3.585.016 4.85.071 1.17.055 1.805.249 2.227.415.562.217.96.477 1.382.896.419.42.679.819.896 1.381.164.422.36 1.057.413 2.227.057 1.266.07 1.646.07 4.85s-.015 3.585-.074 4.85c-.061 1.17-.256 1.805-.421 2.227-.224.562-.479.96-.899 1.382-.419.419-.824.679-1.38.896-.42.164-1.065.36-2.235.413-1.274.057-1.649.07-4.859.07-3.211 0-3.586-.015-4.859-.074-1.171-.061-1.816-.256-2.236-.421-.569-.224-.96-.479-1.379-.899-.421-.419-.69-.824-.9-1.38-.165-.42-.359-1.065-.42-2.235-.045-1.26-.061-1.649-.061-4.844 0-3.196.016-3.586.061-4.861.061-1.17.255-1.814.42-2.234.21-.57.479-.96.9-1.381.419-.419.81-.689 1.379-.898.42-.166 1.051-.361 2.221-.421 1.275-.045 1.65-.06 4.859-.06l.045.03zm0 3.678a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm7.846-10.405a1.441 1.441 0 01-2.88 0 1.44 1.44 0 012.88 0z'/%3E%3C/svg%3E");
  width: 1.15em;
  height: 1.15em;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--ico-ig) center / contain no-repeat;
  mask: var(--ico-ig) center / contain no-repeat;
}

/* Hover Border Gradient (db-project-biblioteca, references/botones-formularios.md):
   un arco de luz recorre el perímetro del botón. Portado con un conic-gradient
   recortado a un anillo de 1px por mask-composite, en vez de los cuatro
   gradientes que rotan del original.

   Es el tratamiento del NIVEL 1 y de ninguno más. Lo tenían las tres variantes
   por igual, y ahí se perdía la jerarquía: un botón de contorno con la misma luz
   girando que el de relleno pesa lo mismo aunque no lo parezca. Cada nivel tiene
   ahora su propio gesto (ver "TRES NIVELES DE BOTÓN", más arriba).
   Equivalente táctil: en dispositivos sin mouse, js/site.js pone .is-pulsando
   cuando el botón entra en pantalla y el arco da una vuelta sola. */
@property --db-angulo {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

.btn {
  position: relative;
  isolation: isolate;
}
.btn--primary::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--db-angulo),
    transparent 0deg 250deg,
    var(--db-acento) 310deg,
    transparent 360deg
  );
  /* El mask recorta el gradiente a un anillo del ancho del padding (1px): lo
     que queda pintado es solo el borde, no el relleno del botón. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.btn--primary:hover::after,
.btn--primary:focus-visible::after,
.btn--primary.is-pulsando::after {
  opacity: 1;
  animation: db-girar 3.2s linear infinite;
}
.btn--primary.is-pulsando::after { animation-iteration-count: 1; }

/* ── Nivel 2 · el borde se enciende desde un lado ──────────────────────────────
   "Hover Border Gradient" portado a CSS: en vez de un punto que da la vuelta,
   una luz entra por la izquierda y barre el contorno. El gradiente vive en el
   borde recortado con la misma técnica de máscara que el arco, pero es lineal y
   no cónico: el gesto se lee distinto de lejos, que es todo el punto. */
.btn--ghost::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(100deg,
    transparent 0%,
    var(--db-acento) 18%,
    color-mix(in srgb, var(--db-acento) 45%, transparent) 42%,
    transparent 62%);
  background-size: 260% 100%;
  background-position: 130% 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  transition: opacity 0.25s ease, background-position 0.65s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.btn--ghost:hover::after,
.btn--ghost:focus-visible::after,
.btn--ghost.is-pulsando::after,
.card--formato:hover .btn--ghost::after,
.formato:hover .btn--ghost::after {
  opacity: 1;
  background-position: -30% 0;
}

/* ── Nivel 3 · el subrayado que se dibuja ─────────────────────────────────────
   El mismo gesto que .ver-mas y los links del pie: un solo idioma de enlace en
   todo el sitio, ahora también disponible como nivel de botón. */
.btn--texto::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 8px;
  width: calc(100% - 1.4em);
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.btn--texto:hover::after,
.btn--texto:focus-visible::after,
.btn--texto.is-pulsando::after { transform: scaleX(1); }

@keyframes db-girar {
  to { --db-angulo: 360deg; }
}

/* Área táctil mínima en todo lo que se toca */
button, .thread__dot { min-height: 44px; }

@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;
    scroll-behavior: auto !important;
  }
}

/* ── Transición entre páginas ────────────────────────────────────────────────
   API nativa del navegador: cero JS, cero librería, y sin build step. Es lo que
   recomienda la ficha de transiciones de página de db-project-biblioteca
   (references/codrops.md, "astro-shop-view-transitions"): lo más moderno y lo
   más barato. Donde no está soportada, la navegación es la de siempre y no
   parpadea nada.

   La declaración sola ya no alcanzaba: el cruce por defecto del navegador es
   tan corto que no se percibe, y las cinco páginas seguían sintiéndose como
   cinco sitios distintos. Acá se le pone una coreografía explícita: la que se
   va se desvanece hacia arriba en 0,22s y la que llega sube desde abajo en
   0,34s. La entrada más lenta que la salida es lo que hace que se lea como una
   página que reemplaza a otra y no como dos que se cruzan.

   La barra queda afuera del cruce: tiene view-transition-name propio
   (css/chrome.css) y el navegador la continúa como el mismo objeto. */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: db-pagina-sale 0.22s cubic-bezier(0.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: db-pagina-entra 0.34s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes db-pagina-sale  { to   { opacity: 0; transform: translateY(-10px); } }
@keyframes db-pagina-entra { from { opacity: 0; transform: translateY(16px); } }

/* La regla global de prefers-reduced-motion actúa sobre `*`, y los
   pseudo-elementos de la transición no son alcanzados por el selector
   universal: hay que apagarlos a mano. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* La flecha de los botones que llevan a otro lado. Es el mismo gesto que ya
   tenían los links de las tarjetas (.card__flecha, css/components.css): un
   <span> propio que se adelanta al pasar el mouse o al recibir foco. Un "→"
   suelto adentro del botón no se puede animar — hace falta el elemento.
   Va sobre el ::before/transform del span, no sobre el botón: el transform del
   botón lo escribe GSAP en el hundido del click (js/botones.js), y dos dueños
   del mismo transform hacen que el tween arranque de un valor contaminado. */
.btn__flecha {
  display: inline-block;
  transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.btn:hover .btn__flecha,
.btn:focus-visible .btn__flecha { transform: translateX(6px); }
@media (prefers-reduced-motion: reduce) {
  .btn__flecha { transition: none; }
  .btn:hover .btn__flecha { transform: none; }
}
