/* ===========================================================================
   base.css — MOTOR DE ESTILO (archivo de solo lectura para el agente)
   ---------------------------------------------------------------------------
   Reset, primitivas de layout, tipografia, controles y animaciones de entrada.
   El agente compone secciones con estas clases y solo escribe lo especifico
   en css/site.css. Aqui vive la "casa": ritmo, contraste, foco, accesibilidad.
   =========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* La altura real de la cabecera la mide js/site.js; el valor de reserva es
     el del nav por defecto. Sin esto, un ancla deja el titular tapado. */
  scroll-padding-top: calc(var(--header-h, 4.5rem) + 1rem);
}

body {
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, picture, video, canvas, svg { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
ul[class], ol[class] { list-style: none; padding: 0; }
a { color: inherit; }

/* --- Tipografia ----------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 700;
  line-height: var(--leading-heading);
  letter-spacing: -0.02em;
}

.display {
  font-family: var(--font-heading);
  font-size: var(--text-display);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  font-weight: 700;
}

h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: calc(var(--text-h2) * 0.72); }

.lead {
  font-size: clamp(1.0625rem, 1rem + 0.5vw, 1.375rem);
  color: var(--c-text-mute);
  max-width: 62ch;
}

.label {
  font-family: var(--font-label);
  font-size: var(--text-small);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--c-text-mute);
}

.prose { max-width: 68ch; }
.prose p + p { margin-top: var(--space-2); }

.numeric { font-variant-numeric: tabular-nums; }

/* --- Layout --------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-space); }
.section--tight { padding-block: calc(var(--section-space) * 0.6); }
.section--surface { background: var(--c-surface); }

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
}

/* Reparto asimetrico 7/5 por defecto: la simetria es la firma de la plantilla */
.split {
  display: grid;
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 7fr 5fr; }
  .split--reverse { grid-template-columns: 5fr 7fr; }
  .split--reverse > :first-child { order: 2; }
}

.cards {
  display: grid;
  gap: clamp(1rem, 2.5vw, 1.75rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.stack > * + * { margin-top: var(--space-2); }
.stack-lg > * + * { margin-top: var(--space-4); }

.center { text-align: center; margin-inline: auto; }

/* Sangrado completo sin usar 100vw (provoca scroll horizontal en Windows) */
.full-bleed {
  width: 100%;
  margin-inline: 0;
  padding-inline: 0;
}

/* --- Superficies ---------------------------------------------------------- */

.card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-card);
  padding: clamp(1.25rem, 2.5vw, 2rem);
  transition: transform var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.card--interactive { cursor: pointer; }
.card--interactive:hover { transform: translateY(-4px); border-color: var(--c-primary); }

/* --- Acercar el raton: la foto crece, la ficha se levanta --------------------
   Es el gesto que mas separa una pagina viva de una impresa, y no cuesta nada:
   la imagen escala un 6 % dentro de su marco -que la recorta- y la ficha sube
   unos pixeles con la sombra creciendo. Lo importante es que la imagen se mueve
   MAS que la tarjeta: si van a la vez, parece que se desliza toda la pagina.

   Se aplica solo a los contenedores que ya recortan la imagen. Para apagarlo en
   una pieza concreta: class="sin-zoom". */

:where(.card, .plan, .overlay-card, .media, .frame, .zoom):not(.sin-zoom) :where(img, video) {
  transition: transform 0.65s var(--ease-out);
  will-change: transform;
}
:where(.card, .plan, .overlay-card, .zoom):not(.sin-zoom):hover :where(img, video),
:where(.media, .frame):not(.sin-zoom):hover :where(img, video) {
  transform: scale(1.06);
}
/* El marco tiene que recortar, o la imagen se sale al crecer. */
.plan__foto, .media, .frame, .zoom, .overlay-card { overflow: hidden; }

/* El levantamiento de la ficha. `.card` ya lo tenia solo en --interactive; aqui
   pasa a ser el comportamiento normal de cualquier ficha con imagen dentro. */
:where(.card, .overlay-card):not(.sin-zoom):has(img):hover {
  transform: translateY(-5px);
  box-shadow: 0 16px 36px oklch(var(--foreground-950) / 0.14);
}
:where(.card, .overlay-card) { transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out); }

@media (prefers-reduced-motion: reduce) {
  :where(.card, .plan, .overlay-card, .media, .frame, .zoom):hover :where(img, video) { transform: none; }
}

/* --- Tarjeta con tinte propio -----------------------------------------------
   Una rejilla de ocho tarjetas del mismo gris es la definicion de "generico y
   sombrio". La solucion no es inventar ocho colores sueltos —eso rompe la
   paleta— sino girar el TONO manteniendo fijas luminosidad y croma: asi ningun
   par puede desentonar y el contraste esta garantizado por construccion.

     <article class="card card--tinta" style="--tinta: 200">

   El tono se pasa en grados. Reparte los de una rejilla en saltos de 35 a 45
   grados a partir del tono de tu acento; con menos, dos tarjetas seguidas se
   ven iguales. */

.card--tinta {
  /* Comparado con un sitio publicado, nuestro tinte estaba el doble de
     saturado: se veia como papel de colores, no como una ficha con acento.
     Croma 0.018 sobre L 0.975 es justo lo que se nota sin gritar. */
  --tinta-l-fondo: 0.975;
  --tinta-c-fondo: 0.018;
  --tinta-l-marca: 0.50;
  --tinta-c-marca: 0.13;
  background: oklch(var(--tinta-l-fondo) var(--tinta-c-fondo) var(--tinta, 250));
  border-color: oklch(0.93 0.025 var(--tinta, 250));
}
.card--tinta .icon-chip,
.card--tinta .plan__sello { background: oklch(0.94 0.04 var(--tinta, 250)); }
.card--tinta .icon-chip { color: oklch(var(--tinta-l-marca) var(--tinta-c-marca) var(--tinta, 250)); }
.card--tinta h3 { color: oklch(0.42 0.10 var(--tinta, 250)); }
.card--tinta .card__ir {
  border-color: oklch(0.90 0.04 var(--tinta, 250));
  color: oklch(var(--tinta-l-marca) var(--tinta-c-marca) var(--tinta, 250));
}
.card--tinta .card__ir:hover { background: oklch(0.94 0.05 var(--tinta, 250)); }
:root[data-scheme='dark'] .card--tinta {
  --tinta-l-fondo: 0.24;
  --tinta-c-fondo: 0.035;
  --tinta-l-marca: 0.78;
  --tinta-c-marca: 0.12;
  border-color: oklch(0.34 0.05 var(--tinta, 250));
}

/* --- La flecha al pie de la ficha -------------------------------------------
   Un circulito con una flecha abajo a la izquierda. Comparadas lado a lado con
   un sitio publicado, es lo que mas cambiaba: sin ella la ficha es un parrafo
   dentro de un recuadro; con ella promete que hay algo detras. Se mueve al
   acercar el raton a la ficha entera, no solo a la flecha. */
.card__ir {
  margin-top: auto;
  align-self: flex-start;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  border: 1px solid var(--c-border);
  color: var(--c-primary);
  display: grid;
  place-items: center;
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.card__ir .icon { width: 0.95em; height: 0.95em; }
/* La ficha con flecha reparte en columna para que la flecha caiga abajo del
   todo aunque los textos midan distinto: la fila queda pareja. */
.card:has(.card__ir) { display: flex; flex-direction: column; gap: 0.5rem; }
.card:hover .card__ir { transform: translateX(3px); }
:root[data-scheme='dark'] .card--tinta .icon-chip { background: oklch(0.30 0.05 var(--tinta, 250)); }
:root[data-scheme='dark'] .card--tinta h3 { color: oklch(0.90 0.05 var(--tinta, 250)); }

/* --- Ficha que se abre -----------------------------------------------------
   La tarjeta que promete mas de lo que enseña: un renglon al pie que, pulsado,
   despliega el detalle y enciende el icono de arriba.

   Es <details> nativo y no un guion. Eso no es purismo: nativo trae gratis el
   teclado (Intro y Espacio), el lector de pantalla (dice "contraido/expandido"
   sin que nadie se lo cuente), la busqueda del navegador dentro del texto
   cerrado, y funciona aunque el JavaScript no llegue a cargar.

     <article class="card card--tinta" style="--tinta: 200">
       <span class="icon-chip">…</span>
       <h3>Recepcion</h3>
       <p>El punto de entrada del paciente.</p>

       <details class="card__mas">
         <summary>Caracteristicas</summary>
         <ul>
           <li>Agenda por profesional y por consultorio</li>
           <li>Cobro de anticipos en el momento</li>
         </ul>
       </details>

       <p class="card__dato">Citas sin llamada <b>100%</b></p>
     </article>

   Convive con la foto de fondo sin tocarla: .u-fondo pinta en un ::before por
   detras del contenido, y esto es contenido. Al abrirse la tarjeta crece y la
   foto se reencuadra sola, porque va a "cover". */

.card { --acento-ficha: var(--c-primary); }
.card--tinta { --acento-ficha: oklch(var(--tinta-l-marca) var(--tinta-c-marca) var(--tinta, 250)); }

/* Lo que se ancla al fondo NO es el desplegable, es la cifra: si se ancla el
   desplegable, una tarjeta con el texto corto deja un hueco entre el parrafo y
   su propio renglon. Anclando la cifra, el desplegable cuelga de su texto y aun
   asi las cifras de toda la fila caen a la misma altura, que es lo que hace que
   la rejilla se lea de un vistazo. */
.card:has(.card__mas) { display: flex; flex-direction: column; }
.card:has(.card__mas) > .card__dato { margin-top: auto; }
.card:has(.card__mas):not(:has(.card__dato)) > .card__ir { margin-top: auto; }

.card__mas {
  border-top: 1px solid var(--c-border);
  padding-top: 0.85rem;
  margin-top: 1rem;
}
.card__mas > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  list-style: none;
  font-family: var(--font-label);
  font-size: var(--text-small);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--acento-ficha);
}
.card__mas > summary::-webkit-details-marker { display: none; }

/* El galon se dibuja con dos lados de una caja girada: sin dependencia del
   sprite, gira igual en cualquier tamaño y hereda el color del renglon. */
.card__mas > summary::after {
  content: '';
  flex: none;
  width: 0.5em;
  height: 0.5em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-0.15em) rotate(45deg);
  transition: transform var(--dur-fast) var(--ease-out);
}
.card__mas[open] > summary::after { transform: translateY(0.1em) rotate(-135deg); }

.card__mas ul {
  list-style: none;
  padding: 0;
  margin: 0.85rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  font-size: var(--text-small);
  color: var(--c-text-mute);
}
.card__mas li { display: flex; gap: 0.55rem; align-items: baseline; }
/* Un punto del color de la ficha delante de cada linea: la lista se lee como
   lista sin gastar un icono por renglon. */
.card__mas li::before {
  content: '';
  flex: none;
  width: 0.4em;
  height: 0.4em;
  border-radius: 999px;
  background: var(--acento-ficha);
  transform: translateY(-0.1em);
}

/* El icono se enciende cuando la ficha esta abierta: es la señal de que ESTA
   tarjeta es la que se esta mirando, y es lo que hace que una rejilla de ocho
   no se sienta como ocho cosas iguales. */
.card:has(.card__mas[open]) .icon-chip {
  background: var(--acento-ficha);
  color: var(--c-bg);
  box-shadow: 0 0 0 4px oklch(from var(--acento-ficha) l c h / 0.16);
}
.icon-chip {
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}

/* El dato al pie: una cifra que justifica el modulo. El renglon se parte en
   dos columnas y la cifra no se encoge nunca, porque es lo unico que se lee
   de verdad si se pasa la vista por encima. */
.card__dato {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  border-top: 1px solid var(--c-border);
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  font-size: var(--text-small);
  color: var(--c-text-mute);
}
.card__dato b {
  flex: none;
  font-family: var(--font-heading);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--acento-ficha);
}

/* El contenido entra deslizando en todos los navegadores. */
.card__mas[open] ul { animation: ficha-abre var(--dur-base) var(--ease-out); }
@keyframes ficha-abre {
  from { opacity: 0; transform: translateY(-6px); }
}

/* Y donde se puede, tambien crece el alto en vez de dar el salto. Es la receta
   moderna: sin interpolate-size, "auto" no es un valor animable. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }
  .card__mas::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size var(--dur-base) var(--ease-out),
                content-visibility var(--dur-base) allow-discrete;
  }
  .card__mas[open]::details-content { block-size: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .card__mas[open] ul { animation: none; }
  .card__mas::details-content { transition: none; }
}

/* --- Encabezado de banda sobre fotografia ------------------------------------
   "Los fondos de los titulos deben tener imagenes contextuales, NUNCA un color
   plano" — es la queja que mas se repite cuando una pagina se ve barata. Esto
   pone la foto detras de la banda y el velo justo para que el texto se lea.

     <section class="section section--foto" style="--foto: url('/api/image?…')"> */

.section--foto {
  position: relative;
  isolation: isolate;
  background-image: var(--foto);
  background-size: cover;
  background-position: center;
  color: oklch(var(--background-50));
}
.section--foto::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to right,
    oklch(var(--foreground-950) / 0.92),
    oklch(var(--foreground-950) / 0.72) 55%,
    oklch(var(--primary-950) / 0.55)
  );
}
.section--foto :where(h2, h3, p, li, span, a):not(.btn) { color: inherit; }
.section--foto .eyebrow { color: oklch(var(--accent-300)); }
.section--foto .accent { color: oklch(var(--accent-300)); }
.section--foto .lead { color: oklch(var(--background-200)); }

/* --- Tarjeta de plan --------------------------------------------------------
   La banda de precios era la unica del catalogo que se explicaba en prosa y sin
   marcado, y salia siempre igual: una lista de textos con un boton al final.
   Comparada con un sitio publicado le faltaba todo lo que hace que un plan se
   elija: una FOTO que diga a quien va dirigido, el precio en el color de
   acento y del tamano de un titular, el boton DENTRO de cada tarjeta, y la
   opcion recomendada invertida en oscuro para que gane sin discusion. */

.plans { display: grid; gap: clamp(1rem, 2.5vw, 1.75rem); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 900px) { .plans { grid-template-columns: repeat(3, 1fr); } }

.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-card);
  overflow: hidden;
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.plan:hover { transform: translateY(-4px); }

/* La foto del plan: a quien va dirigido se entiende antes de leer el nombre. */
.plan__foto { aspect-ratio: 16 / 10; overflow: hidden; background: oklch(var(--background-200)); }
.plan__foto img { width: 100%; height: 100%; object-fit: cover; display: block; }

.plan__cuerpo { padding: clamp(1.25rem, 2.5vw, 1.75rem); display: flex; flex-direction: column; gap: 0.75rem; flex: 1; }
.plan__nombre { font-family: var(--font-heading); font-size: 1.25rem; line-height: 1.2; }
.plan__para { font-size: var(--text-small); color: var(--c-text-mute); margin-top: -0.4rem; }
.plan__precio {
  font-family: var(--font-heading);
  font-size: clamp(1.8rem, 1.2rem + 1.8vw, 2.5rem);
  line-height: 1.05;
  color: var(--c-primary);
  font-variant-numeric: tabular-nums;
}
.plan__precio small { font-size: 0.42em; font-weight: 400; color: var(--c-text-mute); }
.plan__nota { font-size: var(--text-small); color: var(--c-text-mute); margin-top: -0.35rem; }
/* El boton va dentro de cada tarjeta y a todo el ancho: un solo boton al final
   de la banda obliga a decidir sin tener el plan delante. */
.plan .btn { width: 100%; justify-content: center; margin-top: 0.35rem; }
.plan__lista { list-style: none; margin: 0.35rem 0 0; padding: 0; display: grid; gap: 0.55rem; }
.plan__lista li { display: flex; gap: 0.6rem; align-items: flex-start; font-size: var(--text-small); }
.plan__lista .icon { color: var(--c-accent); flex: none; margin-top: 0.15em; }

/* La etiqueta de "el que casi todos eligen", montada sobre el borde. */
.plan__sello {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
  z-index: 2;
  background: var(--c-primary);
  color: var(--c-on-primary);
  font-family: var(--font-label);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
}

/* El plan recomendado: invertido, elevado y con el borde del primario. */
.plan--destacado {
  border-color: var(--c-primary);
  box-shadow: 0 0 0 1px var(--c-primary), var(--shadow-2);
}
@media (min-width: 900px) { .plan--destacado { transform: translateY(-1.25rem); } }
@media (min-width: 900px) { .plan--destacado:hover { transform: translateY(-1.75rem); } }
.plan--destacado .plan__cuerpo {
  background: oklch(var(--foreground-950));
  color: oklch(var(--background-50));
}
.plan--destacado .plan__para,
.plan--destacado .plan__nota,
.plan--destacado .plan__precio small { color: oklch(var(--background-300)); }
.plan--destacado .plan__precio { color: oklch(var(--accent-300)); }
.plan--destacado .plan__lista .icon { color: oklch(var(--accent-300)); }

/* Ritmo interno: sin esto el h3 queda pegado a su parrafo en cada tarjeta */
.card > * + * { margin-top: var(--space-2); }
.card > .media:first-child { margin-bottom: var(--space-3); }

/* --- Vidrio: la tarjeta translucida ----------------------------------------
   Solo tiene sentido ENCIMA de algo -una foto, un video, un degradado-. Sobre
   un fondo plano se ve como un gris sucio, que es el error tipico.

   El desenfoque de fondo lo paga la tarjeta grafica en cada fotograma, asi que
   va en piezas concretas -la barra de cifras del hero, tres fichas sueltas-,
   nunca en una banda entera ni en una rejilla de doce.

   .glass          sobre fondo oscuro, texto claro
   .glass--light   sobre fondo claro, texto normal                             */

.glass {
  background: oklch(var(--background-50) / 0.10);
  border: 1px solid oklch(var(--background-50) / 0.22);
  border-radius: var(--radius-card);
  padding: clamp(1.25rem, 2.5vw, 2rem);
  color: oklch(var(--background-50));
  /* saturate(150%) teñia el cristal del color del velo y salia un verde
     sucio; a 120% se ve el fondo, no un filtro. */
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  /* Las dos lineas que hacen que parezca cristal y no un rectangulo
     translucido: un brillo en el canto de arriba y una sombra que lo separa
     del fondo. Sin ellas la ficha flota sin peso. */
  box-shadow:
    inset 0 1px 0 oklch(var(--background-50) / 0.28),
    0 12px 32px oklch(var(--foreground-950) / 0.28);
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.glass--light {
  background: oklch(var(--background-50) / 0.70);
  border-color: oklch(var(--background-50) / 0.85);
  color: var(--c-text);
}
.glass > * + * { margin-top: var(--space-2); }

/* --- Cifra con etiqueta ------------------------------------------------------
   Medido contra sitios publicados: su cifra ocupa el doble que la nuestra. La
   cifra es lo que se lee de lejos; la etiqueta, solo cuando ya te ha parado. Si
   las dos tienen el mismo tamano, la ficha no dice nada a un metro. */
.stat__cifra {
  display: block;
  font-family: var(--font-heading);
  font-size: clamp(2.1rem, 1.4rem + 2.4vw, 3rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.stat__label { display: block; font-size: var(--text-small); opacity: 0.78; margin-top: 0.35rem; }
.glass > .stat__label { margin-top: 0.35rem; }
/* Una sola cifra en color de acento por grupo: si van las tres, no destaca ninguna. */
.stat__cifra--acento { color: oklch(var(--accent-300)); }
.glass--interactive:hover {
  background: oklch(var(--background-50) / 0.18);
  border-color: oklch(var(--background-50) / 0.34);
}

/* Sin desenfoque, un 10 % de opacidad deja el texto ilegible: quien no lo
   admita recibe un fondo solido en vez de una tarjeta fantasma. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass { background: oklch(var(--foreground-950) / 0.58); }
  .glass--light { background: oklch(var(--background-50) / 0.88); }
}

/* Prefijo y sufijo del contador. Nacen pequenos y apagados porque " anos" a
   tamano display parte el renglon y se come la etiqueta de la tarjeta. Pero
   "%", "K" o "+" no son un adorno: son parte de la cifra y a 0.38em parecen
   un fallo. Por eso el tamano y el color salen a variables -las pone el
   editor sobre el propio contador- en vez de quedarse clavados aqui. */
.count-affix {
  font-size: var(--afijo-tamano, 0.38em);
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--afijo-color, var(--c-text-mute));
}

.media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--c-surface);
}
.media > img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.media--wide { aspect-ratio: 16 / 9; }
.media--portrait { aspect-ratio: 4 / 5; }
.media--square { aspect-ratio: 1 / 1; }

/* Velo para texto sobre imagen: sin el, el contraste no se sostiene */
.scrim { position: relative; }
.scrim::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to right,
    oklch(var(--foreground-950) / 0.82),
    oklch(var(--foreground-950) / 0.55) 55%,
    oklch(var(--foreground-950) / 0.3)
  );
  pointer-events: none;
}
.scrim > .container, .scrim > .scrim-content { position: relative; z-index: 1; }

/* --- Portada --------------------------------------------------------------
   El hero reserva la altura de la cabecera fija, asi que su titular nunca
   queda por debajo del menu. El fondo -foto, video o degradado- va en
   .hero__media, siempre detras del contenido y siempre con velo si hay texto
   encima. */

.hero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: center;
  min-height: clamp(30rem, 78svh, 46rem);
  padding-top: calc(var(--header-h, 4.5rem) + clamp(2rem, 6vw, 4.5rem));
  padding-bottom: clamp(2.5rem, 7vw, 5rem);
  overflow: clip;
}
.hero--full { min-height: 100svh; }
.hero--compact { min-height: auto; }

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
}
.hero__media > img,
.hero__media > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Velo del hero sobre imagen o video.
   Dos capas, no una. El velo gris de antes apagaba el video y dejaba un
   rectangulo oscuro; este tine la sombra con el primario y enciende una
   esquina con el acento, que es lo que hace que la portada parezca de la
   marca y no un filtro. La direccion es diagonal: el texto vive abajo a la
   izquierda, y ahi es donde mas cubre. */
.hero--veil::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(
      ellipse at top right,
      oklch(var(--accent-500) / 0.22),
      transparent 55%
    ),
    linear-gradient(
      to top right,
      oklch(var(--primary-950) / 0.9),
      oklch(var(--primary-900) / 0.72) 45%,
      oklch(var(--foreground-950) / 0.4)
    );
}
/* Cuando la marca no quiere color en la portada: el velo neutro de siempre. */
.hero--veil.hero--veil-neutro::after {
  background: linear-gradient(
    to top,
    oklch(var(--foreground-950) / 0.82),
    oklch(var(--foreground-950) / 0.45) 55%,
    oklch(var(--foreground-950) / 0.25)
  );
}
.hero--veil, .hero--veil :where(h1, h2, p, li, span, a):not(.btn) { color: oklch(var(--background-50)); }

/* La palabra destacada del titular, dentro del velo, en el tono CLARO.
   Es el fallo que mas se repetia y el peor de detectar leyendo el codigo: el
   `.accent` que funciona sobre banda blanca es `--c-primary`, oscuro, y encima
   de una portada oscura desaparece —medido, 1,05:1 en un caso real, invisible—.
   Aqui deja de poder pasar: el velo sabe que es oscuro y cambia el tono solo. */
.hero--veil .accent,
.hero--veil .text-gradient {
  color: oklch(var(--accent-300));
  -webkit-text-fill-color: currentColor;
  background: none;
}

/* El signo de un contador dentro del velo. La regla de arriba alcanza a todo
   <span> del hero, asi que un color elegido a mano para la cifra perdia y el
   "+" o el "%" se quedaban del color del velo. Aqui gana lo elegido, y solo
   eso: sin --afijo-color, el velo sigue mandando, que es lo correcto encima
   de una foto. */
.hero--veil .count-affix {
  color: var(--afijo-color, oklch(var(--background-50)));
}

/* Zoom lento del fondo: el unico movimiento admisible en una foto fija */
@media (prefers-reduced-motion: no-preference) {
  .hero__media--zoom > img { animation: hero-zoom 22s var(--ease-out) forwards; }
}
@keyframes hero-zoom { from { transform: scale(1); } to { transform: scale(1.08); } }

/* --- Borde ondulado entre dos bandas ---------------------------------------
   El corte recto entre el video del hero y la banda blanca de debajo es lo que
   delata una plantilla. Esto lo curva.

   Va como HERMANO justo despues del hero, nunca dentro:

     </section>
     <div class="wave" aria-hidden="true"></div>
     <section class="section"> ...

   Se sube sobre el hero con un margen negativo igual a su altura, asi que la
   parte hueca de la onda deja ver el video de arriba y la parte solida ya es
   el fondo de la banda de abajo. Es una mascara SVG en el propio CSS: no
   descarga nada y escala a cualquier ancho sin deformarse.

   Ajustes desde el HTML:
     style="--wave-color: var(--c-surface)"   si la banda de abajo no es --c-bg
     class="wave wave--flip"                  la misma curva al espejo
     class="wave wave--tall"                  mas alta, para un hero a pantalla
     class="wave wave--soft"                  una sola curva, mas serena

   Ojo: tapa los ultimos pixeles del hero. Si ahi hay una flecha de "baja", se
   la come. */

.wave {
  --wave-h: clamp(2.75rem, 6vw, 6rem);
  --wave-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'%3E%3Cpath d='M0,62 C220,8 430,96 720,54 C1010,12 1240,86 1440,34 L1440,100 L0,100 Z' fill='%23000'/%3E%3C/svg%3E");
  position: relative;
  z-index: 2;
  height: var(--wave-h);
  margin-top: calc(var(--wave-h) * -1);
  pointer-events: none;
  background: var(--wave-color, var(--c-bg));
  -webkit-mask: var(--wave-shape) bottom center / 100% 100% no-repeat;
  mask: var(--wave-shape) bottom center / 100% 100% no-repeat;
}
.wave--flip { transform: scaleX(-1); }
.wave--tall { --wave-h: clamp(4rem, 9vw, 9.5rem); }
.wave--soft {
  --wave-shape: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'%3E%3Cpath d='M0,46 C360,110 1080,-16 1440,46 L1440,100 L0,100 Z' fill='%23000'/%3E%3C/svg%3E");
}

/* --- Fondo animado de puntos enlazados -------------------------------------
   La alternativa al video de fondo cuando no hay video que merezca la pena:
   no descarga nada, no se pixela en pantalla grande y no tapa el texto.

     <section class="hero" style="background: var(--c-bg)">
       <div class="bg-net" data-bg-net></div>
       ...

   El color lo hereda del contenedor, asi que se cambia con
   style="color: oklch(var(--primary-400))" sin tocar JavaScript, y la densidad
   con data-bg-net="1.4". js/site.js lo detiene cuando la seccion sale de
   pantalla y con prefers-reduced-motion lo deja quieto. */

.bg-net {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.bg-net > canvas { display: block; width: 100%; height: 100%; }

/* --- Escena que avanza con la rueda ---------------------------------------
   El bloque mide varias pantallas y dentro se queda pegada la escena. Al
   desplazarse, js/site.js hace avanzar el video fotograma a fotograma en vez
   de reproducirlo: el usuario "rebobina" el video con la rueda del raton.
   El video debe ser corto y ligero (6-10 s, 720p, muchos fotogramas clave):
   uno de 40 MB se atasca al buscar posicion. */

.scrollscene { position: relative; height: var(--scene-length, 300svh); }
.scrollscene__pin {
  position: sticky;
  top: 0;
  height: 100svh;
  display: grid;
  align-items: center;
  overflow: clip;
  isolation: isolate;
}
.scrollscene__media { position: absolute; inset: 0; z-index: -2; }
.scrollscene__media > video,
.scrollscene__media > img { width: 100%; height: 100%; object-fit: cover; }
.scrollscene__pin > .container { position: relative; z-index: 1; }

/* Mismo velo que el hero: sobre video en movimiento el texto no se lee sin el */
.scrollscene--veil .scrollscene__pin::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    oklch(var(--foreground-950) / 0.82),
    oklch(var(--foreground-950) / 0.45) 55%,
    oklch(var(--foreground-950) / 0.25)
  );
}
.scrollscene--veil, .scrollscene--veil :where(h1, h2, p, li, span, a):not(.btn) { color: oklch(var(--background-50)); }

/* Sin rueda que valga: en reducir movimiento la escena es una pantalla fija */
@media (prefers-reduced-motion: reduce) {
  .scrollscene { height: auto; }
  .scrollscene__pin { position: relative; height: auto; min-height: 70svh; padding-block: var(--space-6); }
}

/* --- Piezas de composicion ------------------------------------------------
   Los recursos que separan una pagina que respira de un muro de texto:
   etiquetas, fichas de icono, tarjetas flotantes sobre imagen, prueba social
   compacta y rejilla bento. Casi todo lo que en otra web serian tres parrafos,
   aqui es un icono y cuatro palabras. */

/* Etiqueta con punto, encima del titular */
.badge-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.45em 1em;
  border-radius: var(--radius-pill);
  background: oklch(var(--primary-500) / 0.1);
  color: var(--c-primary);
  font-family: var(--font-label);
  font-size: var(--text-small);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}
.badge-pill::before {
  content: '';
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: currentColor;
}

/* Antetitulo con filete: — SERVICIOS */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.9em;
  font-family: var(--font-label);
  font-size: var(--text-small);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--c-primary);
}
.eyebrow::before {
  content: '';
  width: 2.2rem;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
}

/* Palabra acentuada dentro del titular */
.accent { color: var(--c-primary); font-style: italic; }

/* Ficha de icono: cuadrado redondeado con fondo teñido */
.icon-chip {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  border-radius: var(--radius-control);
  background: oklch(var(--primary-500) / 0.12);
  color: var(--c-primary);
}
.icon-chip--accent { background: oklch(var(--accent-500) / 0.16); color: oklch(var(--accent-600)); }
.icon-chip--solid { background: var(--c-primary); color: var(--c-on-primary); }
.icon-chip svg { width: 1.35rem; height: 1.35rem; }

/* Icono suelto del sprite. Toma el color del texto y crece con la letra, asi
   que sirve igual dentro de un boton, de un titular o de una lista. */
.icon {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  vertical-align: -0.22em;
}
.icon--sm { width: 1em; height: 1em; }
.icon--lg { width: 1.75em; height: 1.75em; }

/* Lista con marca de verificacion: el patron mas repetido de toda la pagina */
.ticks { list-style: none; padding: 0; }
.ticks li { display: flex; align-items: flex-start; gap: 0.7rem; }
.ticks li + li { margin-top: 0.6rem; }
.ticks .icon { color: var(--c-primary); margin-top: 0.15em; }

/* Encabezado de seccion asimetrico: titulo a la izquierda, resumen a la derecha */
.section-head {
  display: grid;
  gap: var(--space-3);
  align-items: end;
  margin-bottom: var(--space-5, 3rem);
}
@media (min-width: 900px) {
  .section-head { grid-template-columns: 7fr 4fr; gap: clamp(2rem, 6vw, 5rem); }
  .section-head > :last-child { padding-bottom: 0.4rem; }
}

/* Rejilla bento: una pieza grande y varias pequenas */
.bento { display: grid; gap: clamp(1rem, 2vw, 1.5rem); }
@media (min-width: 900px) {
  .bento { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .bento__feature { grid-column: span 2; grid-row: span 2; }
}

/* Tarjeta sobre imagen, con velo inferior */
.overlay-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-card);
  min-height: 22rem;
  display: flex;
  align-items: flex-end;
  isolation: isolate;
}
.overlay-card > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}
.overlay-card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    oklch(var(--foreground-950) / 0.88),
    oklch(var(--foreground-950) / 0.45) 45%,
    oklch(var(--foreground-950) / 0.08)
  );
}
.overlay-card__body { padding: clamp(1.25rem, 3vw, 2rem); color: oklch(var(--background-50)); }
.overlay-card__body > * + * { margin-top: var(--space-2); }

/* Ficha flotante superpuesta a una imagen (el recurso mas reconocible) */
.float-card {
  position: absolute;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.9rem 1.15rem;
  border-radius: var(--radius-card);
  background: var(--c-bg);
  box-shadow: 0 12px 34px oklch(var(--foreground-950) / 0.16);
}
.float-card--dark { background: oklch(var(--foreground-950)); color: oklch(var(--background-50)); }
.float-card--tl { top: 1.25rem; left: 1.25rem; }
.float-card--tr { top: 1.25rem; right: 1.25rem; }
.float-card--bl { bottom: 1.25rem; left: 1.25rem; }
.float-card--br { bottom: 1.25rem; right: 1.25rem; }
.float-card strong { font-family: var(--font-heading); font-size: 1.15rem; display: block; line-height: 1.1; }
.float-card span { font-size: var(--text-small); color: var(--c-text-mute); }
.float-card--dark span { color: oklch(var(--background-200)); }
.frame { position: relative; }
@media (max-width: 640px) { .float-card { position: static; margin-top: var(--space-2); } }

/* Prueba social compacta: caras solapadas + estrellas + una linea */
.proof { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; }
.avatars { display: flex; }
.avatars img, .avatars span {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  border: 2px solid var(--c-bg);
  object-fit: cover;
  display: grid;
  place-items: center;
  font-size: 0.75rem;
  font-weight: 700;
  background: var(--c-primary);
  color: var(--c-on-primary);
}
.avatars > * + * { margin-left: -0.7rem; }
.stars { display: inline-flex; gap: 0.15rem; color: oklch(var(--accent-500)); }
/* Las estrellas van con #star-solido, no con #star: una estrella de contorno
   no se lee como una valoracion. El relleno lo trae el propio simbolo -la
   clase .solido del sprite-, porque desde fuera no se puede: en un <use> a un
   archivo externo, un var() de la pagina no llega a cruzar.

   El selector lleva el elemento a proposito. ".stars svg.icon" pesa mas que
   cualquier ".loquesea .icon" de la hoja del proyecto, que va despues y si
   empatan gana ella: las estrellas saldrian del tamaño y del color del icono
   que tuvieran al lado. Pasó con .quote-card .icon. */
.stars svg.icon {
  width: 1rem;
  height: 1rem;
  color: inherit;
  --ico-relleno: currentColor;
  --ico-trazo: none;
}

/* Cinta de terminos que se desplaza sola */
.marquee { overflow: hidden; border-block: 1px solid var(--c-border); padding-block: 0.9rem; }
.marquee__track {
  display: flex;
  gap: 3rem;
  width: max-content;
  animation: marquee 38s linear infinite;
}
.marquee__track > span {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--font-label);
  font-size: var(--text-small);
  font-weight: 600;
  color: var(--c-text-mute);
  white-space: nowrap;
}
.marquee__track > span::before {
  content: '';
  width: 0.35rem;
  height: 0.35rem;
  border-radius: 50%;
  background: var(--c-primary);
}
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-50%); } }

/* Barra superior de utilidad, sobre la navegacion */
.utility {
  background: oklch(var(--foreground-950));
  color: oklch(var(--background-100));
  font-size: var(--text-small);
}
.utility__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 2.5rem; flex-wrap: wrap; }
.utility a { text-decoration: none; }
.utility .sep { display: inline-flex; align-items: center; gap: 0.45rem; }

/* Pasos numerados */
.steps { counter-reset: paso; display: grid; gap: clamp(1rem, 2.5vw, 1.75rem); }
@media (min-width: 780px) { .steps { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); } }
.step-item { counter-increment: paso; position: relative; padding-top: var(--space-3); border-top: 2px solid var(--c-border); }
.step-item::before {
  content: counter(paso, decimal-leading-zero);
  font-family: var(--font-heading);
  font-size: 2.2rem;
  line-height: 1;
  color: oklch(var(--primary-500) / 0.3);
  display: block;
  margin-bottom: var(--space-2);
}
.step-item h3 { margin-bottom: 0.4rem; }

/* --- Flujo: el recorrido, no el catalogo ----------------------------------
   Una rejilla de tarjetas dice QUE piezas hay. Un flujo dice EN QUE ORDEN
   pasan las cosas, y esa es otra afirmacion: el hilo entre azulejos es el
   argumento -"esto no se corta por el camino"-, no un adorno. Por eso van los
   dos en el mismo sitio web y no compiten: el catalogo en su pagina, el
   recorrido en la portada.

   En pantalla estrecha el hilo se pone DE PIE en vez de encogerse. Ocho pasos
   en fila en un movil son ocho iconos de 20px con el nombre partido en tres
   renglones; en columna se leen igual de bien que en el escritorio.

   Cada paso se tinta con --tinta (el mismo grado de color que usan las
   tarjetas), asi que un paso y su tarjeta se reconocen como la misma cosa. */
.flujo {
  --flujo-azulejo: 3.6rem;
  --flujo-hilo: var(--space-3);
  --flujo-l-azulejo: 0.55;
  --flujo-c-azulejo: 0.15;
  --flujo-l-icono: 0.99;
  --flujo-l-nombre: 0.45;
  list-style: none;
  display: grid;
  gap: var(--flujo-hilo);
  margin: 0;
  padding: 0;
}
:root[data-scheme='dark'] .flujo {
  --flujo-l-azulejo: 0.72;
  --flujo-c-azulejo: 0.14;
  --flujo-l-icono: 0.18;
  --flujo-l-nombre: 0.86;
}

.flujo__paso {
  position: relative;
  display: grid;
  grid-template-columns: var(--flujo-azulejo) 1fr;
  align-items: center;
  gap: 0.9rem;
}

/* Dos capas sobre el mismo carril, del paso anterior a este: ::before es el
   hilo quieto y ::after es el destello que lo recorre. Comparten caja para no
   repetir la geometria dos veces. */
.flujo__paso + .flujo__paso::before,
.flujo__paso + .flujo__paso::after {
  content: '';
  position: absolute;
  z-index: 0;
  inset-block-end: 100%;
  inset-inline-start: calc(var(--flujo-azulejo) / 2 - 1px);
  width: 2px;
  height: var(--flujo-hilo);
  background-repeat: no-repeat;
}

/* El hilo NO va de punta a punta: es un guion corto centrado en el hueco, con
   las puntas desvanecidas. Una linea continua ata los azulejos como si fueran
   una barra; el guion los deja respirar y aun asi dice que hay camino. Las
   puntas se cortan con el propio degradado, sin calcular donde acaba nada. */
.flujo__paso + .flujo__paso::before {
  background-image: linear-gradient(
    to bottom,
    transparent 0 16%,
    oklch(var(--flujo-l-azulejo) var(--flujo-c-azulejo) var(--tinta, 250) / 0.5) 32% 68%,
    transparent 84% 100%
  );
}

/* El destello sale de detras del azulejo anterior, cruza el hueco y se mete
   detras del siguiente -los azulejos van por encima y le tapan las puntas-.
   Es el dato pasando de un area a la otra: la unica animacion del bloque, y
   la que dice lo que un dibujo quieto no puede. */
.flujo__paso + .flujo__paso::after {
  background-image: linear-gradient(
    to bottom,
    transparent,
    oklch(var(--flujo-l-azulejo) calc(var(--flujo-c-azulejo) + 0.06) var(--tinta, 250)) 50%,
    transparent
  );
  background-size: 100% 42%;
  background-position: 0 0;
  opacity: 0;
}

.flujo__marca {
  position: relative;
  z-index: 1;
  width: var(--flujo-azulejo);
  height: var(--flujo-azulejo);
  display: grid;
  place-items: center;
  border-radius: var(--radius-card);
  background: oklch(var(--flujo-l-azulejo) var(--flujo-c-azulejo) var(--tinta, 250));
  color: oklch(var(--flujo-l-icono) 0.01 var(--tinta, 250));
  /* Dos sombras: una que apoya el azulejo en la pagina y otra sin desplazar,
     que es el halo de su propio color. El halo es lo que separa esto de ocho
     cuadraditos de colores puestos en fila. */
  box-shadow:
    0 10px 22px oklch(var(--flujo-l-azulejo) var(--flujo-c-azulejo) var(--tinta, 250) / 0.34),
    0 0 26px oklch(var(--flujo-l-azulejo) var(--flujo-c-azulejo) var(--tinta, 250) / 0.28);
}
.flujo__marca .icon { width: 1.4rem; height: 1.4rem; }

/* El punto de la esquina: dice "este paso esta vivo y conectado". El anillo
   del color del fondo es lo que lo separa del azulejo. */
.flujo__marca::after {
  content: '';
  position: absolute;
  top: -0.2rem;
  inset-inline-end: -0.2rem;
  width: 0.62rem;
  height: 0.62rem;
  border-radius: 50%;
  background: oklch(var(--flujo-l-azulejo) calc(var(--flujo-c-azulejo) + 0.05) var(--tinta, 250));
  box-shadow: 0 0 0 3px var(--c-bg);
}

/* El nombre va sobre una chapa de su propio color, muy diluida. Sin ella, ocho
   palabras sueltas debajo de ocho azulejos se leen como pies de foto; con ella
   cada nombre pertenece a su paso. */
.flujo__nombre {
  justify-self: start;
  padding: 0.2em 0.6em;
  border-radius: var(--radius-pill);
  background: oklch(var(--flujo-l-azulejo) var(--flujo-c-azulejo) var(--tinta, 250) / 0.09);
  font-family: var(--font-label);
  font-size: var(--text-small);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  color: oklch(var(--flujo-l-nombre) 0.09 var(--tinta, 250));
}

@media (min-width: 900px) {
  /* Una sola fila, pase lo que pase: con auto-fit, el paso 5 empezaria un
     renglon nuevo y su hilo saldria disparado hacia la izquierda, fuera. */
  .flujo {
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
  }
  .flujo__paso {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: 0.75rem;
    padding-inline: 0.35rem;
  }
  .flujo__nombre { justify-self: center; }
  .flujo__paso + .flujo__paso::before,
  .flujo__paso + .flujo__paso::after {
    inset-block-end: auto;
    inset-inline-start: auto;
    inset-inline-end: 50%;
    top: calc(var(--flujo-azulejo) / 2 - 1px);
    width: 100%;
    height: 2px;
  }
  .flujo__paso + .flujo__paso::before {
    background-image: linear-gradient(
      to right,
      transparent 0 34%,
      oklch(var(--flujo-l-azulejo) var(--flujo-c-azulejo) var(--tinta, 250) / 0.5) 42% 58%,
      transparent 66% 100%
    );
  }
  .flujo__paso + .flujo__paso::after {
    background-image: linear-gradient(
      to right,
      transparent,
      oklch(var(--flujo-l-azulejo) calc(var(--flujo-c-azulejo) + 0.06) var(--tinta, 250)) 50%,
      transparent
    );
    background-size: 34% 100%;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .flujo__paso + .flujo__paso::after { animation: flujo-pulso 2.8s var(--ease-out) infinite; }
  /* Desfasados: con la misma fase son siete luces parpadeando a la vez; con
     retardo se lee una onda que va del portal a la caja, que es justo el
     recorrido que la seccion esta contando. */
  .flujo__paso:nth-child(2) + .flujo__paso::after { animation-delay: 0.24s; }
  .flujo__paso:nth-child(3) + .flujo__paso::after { animation-delay: 0.48s; }
  .flujo__paso:nth-child(4) + .flujo__paso::after { animation-delay: 0.72s; }
  .flujo__paso:nth-child(5) + .flujo__paso::after { animation-delay: 0.96s; }
  .flujo__paso:nth-child(6) + .flujo__paso::after { animation-delay: 1.2s; }
  .flujo__paso:nth-child(7) + .flujo__paso::after { animation-delay: 1.44s; }
  .flujo__paso:nth-child(8) + .flujo__paso::after { animation-delay: 1.68s; }

  @media (min-width: 900px) {
    .flujo__paso + .flujo__paso::after { animation-name: flujo-pulso-h; }
  }
}

@keyframes flujo-pulso {
  0%   { opacity: 0; background-position: 0 0%; }
  18%  { opacity: 1; }
  64%  { opacity: 1; }
  82%,
  100% { opacity: 0; background-position: 0 100%; }
}
@keyframes flujo-pulso-h {
  0%   { opacity: 0; background-position: 0% 0; }
  18%  { opacity: 1; }
  64%  { opacity: 1; }
  82%,
  100% { opacity: 0; background-position: 100% 0; }
}

/* Ficha de apoyo: el icono AL LADO y no encima. Para las tres promesas que
   acompanan a un flujo o a una tabla, donde la ficha normal -icono arriba,
   titular grande, parrafo- pesa mas que la pieza que sostiene. */
.card--nota {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 0.25rem 0.9rem;
  padding: var(--space-3);
}
.card--nota .icon-chip {
  grid-row: span 2;
  width: 2.25rem;
  height: 2.25rem;
}
.card--nota .icon-chip svg { width: 1.1rem; height: 1.1rem; }
.card--nota h3 { font-size: 1.05rem; margin: 0; }
.card--nota p { font-size: var(--text-small); color: var(--c-text-mute); margin: 0; }

/* --- Controles ------------------------------------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  white-space: nowrap;              /* sin excepciones */
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  padding: 0.85em 1.6em;
  min-height: 44px;                 /* objetivo tactil */
  font-family: var(--font-label);
  font-size: var(--text-body);
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--c-primary); color: var(--c-on-primary); }
.btn--primary:hover { background: var(--c-primary-hover); }

.btn--ghost { background: transparent; color: var(--c-text); border-color: var(--c-border); }
.btn--ghost:hover { border-color: var(--c-text); }

.btn--pill { border-radius: var(--radius-pill); }

a, button, [role='button'], summary, label[for] { cursor: pointer; }

:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 3px solid var(--c-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* --- Navegacion -----------------------------------------------------------
   Regla dura: NADA del sitio puede quedar por debajo de la cabecera fija.
   Dos textos cruzados en la portada -la barra de horarios asomando entre los
   enlaces del menu- es el defecto mas caro que puede tener una pagina, y pasa
   siempre por lo mismo: una cabecera transparente encima de contenido que no
   le pertenece.

   Por eso la cabecera es OPACA desde el primer pixel. Solo con
   `.nav--over-hero` se vuelve transparente, y entonces lo unico que puede
   haber debajo es el hero, que ya reserva la altura con --header-h.
   La barra de utilidad (horario, ciudad, telefono) va DENTRO de <header>,
   nunca antes: fuera queda tapada. */

.nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 50;
  background: var(--c-bg);
  border-bottom: 1px solid var(--c-border);
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 4.5rem;
}

/* js/site.js reserva el hueco: el contenido empieza bajo la cabecera. */
body.has-fixed-nav { padding-top: var(--header-h, 4.5rem); }
body.has-fixed-nav.nav-over-hero { padding-top: 0; }  /* lo reserva el hero */

/* Cabecera transparente sobre el hero. El velo mantiene legibles los enlaces
   sobre cualquier fotografia; al fijarse, se cambia por el fondo solido. */
.nav--over-hero { background: transparent; border-bottom-color: transparent; }
.nav--over-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom, oklch(var(--foreground-950) / 0.55), transparent);
  transition: opacity var(--dur-base) var(--ease-out);
}
.nav--over-hero.is-stuck::before { opacity: 0; }

.nav.is-stuck {
  background: oklch(var(--background-50) / 0.88);
  backdrop-filter: blur(12px);
  border-bottom-color: var(--c-border);
}
:root[data-scheme='dark'] .nav.is-stuck { background: oklch(var(--background-950) / 0.85); }

.nav__links { display: none; gap: clamp(1rem, 2.5vw, 2rem); align-items: center; }
.nav__links a { text-decoration: none; font-size: var(--text-small); font-weight: 600; letter-spacing: 0.02em; }
.nav__links a:hover { color: var(--c-primary); }

.nav__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  background: transparent;
  border: 1px solid var(--c-border);
  border-radius: var(--radius-control);
}

/* El cruce móvil/escritorio va al final para que gane sin !important */
@media (min-width: 860px) {
  .nav__links { display: flex; }
  .nav__toggle { display: none; }
  .nav__mobile { display: none !important; }
}

.nav__mobile {
  display: none;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--gutter) var(--space-4);
  background: var(--c-bg);
  border-bottom: 1px solid var(--c-border);
}
.nav__mobile.is-open { display: flex; }
.nav__mobile a { text-decoration: none; font-size: 1.125rem; font-weight: 600; }

/* --- Formularios ---------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: 0.5rem; }
.field > label { font-size: var(--text-small); font-weight: 600; }

.input, .textarea, .select {
  width: 100%;
  padding: 0.8rem 1rem;
  min-height: 44px;
  background: var(--c-bg);
  color: var(--c-text);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-control);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.input:focus, .textarea:focus, .select:focus { border-color: var(--c-primary); }
.textarea { min-height: 8rem; resize: vertical; }

.field__error { color: oklch(0.55 0.19 25); font-size: var(--text-small); }
.form__status { border-radius: var(--radius-control); padding: 0.9rem 1.1rem; font-size: var(--text-small); }
.form__status--ok { background: oklch(var(--accent-500) / 0.14); color: var(--c-text); }
.form__status--err { background: oklch(0.55 0.19 25 / 0.12); color: var(--c-text); }
.char-count { font-size: var(--text-small); color: var(--c-text-mute); text-align: right; }

/* Honeypot anti-spam: oculto por hoja de estilos, nunca con hidden ni type=hidden */
.hp-field {
  position: absolute !important;
  left: -9999px !important;
  top: auto !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
}

/* --- Animaciones de entrada ----------------------------------------------- */

[data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out),
              filter var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}
[data-reveal='left'] { transform: translateX(-28px); }
[data-reveal='right'] { transform: translateX(28px); }
[data-reveal='scale'] { transform: scale(0.96); }

/* Entra enfocandose, sin moverse de sitio. Para el titular del hero, donde el
   desplazamiento se ve tosco y el desenfoque se ve caro. Con data-stagger en
   el contenedor y una linea por span, el titular aparece verso a verso. */
[data-reveal='blur'] { transform: none; filter: blur(14px); }
[data-reveal='blur'].is-visible { filter: blur(0); }

/* Recorrido largo, para una pieza sola que tiene que notarse: la tarjeta de
   cifras del hero, la imagen grande de una banda. Medido en sitios publicados,
   el desplazamiento tipico es de 60 a 120px, tres veces el nuestro; a 22px la
   entrada es elegante pero casi no se ve. En una rejilla de nueve tarjetas
   sigue mandando el corto: nueve piezas cruzando media pantalla marean.
   El overflow-x:hidden del body evita la barra horizontal. */
[data-reveal][data-amplio] { transform: translateY(68px); }
[data-reveal='left'][data-amplio] { transform: translateX(-72px); }
[data-reveal='right'][data-amplio] { transform: translateX(72px); }
[data-reveal='scale'][data-amplio] { transform: scale(0.84); }
[data-reveal][data-amplio] { transition-duration: var(--dur-slower, 900ms); }

[data-reveal].is-visible { opacity: 1; transform: none; }

@keyframes float-soft {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-10px); }
}
.float-soft { animation: float-soft 6s var(--ease-out) infinite; }

/* Varias piezas flotando a la vez tienen que ir DESFASADAS. Con la misma fase
   suben y bajan en bloque y parece que se mueve la pagina; desfasadas parece
   que flotan. Los retardos se aplican solos entre hermanos. */
.float-soft:nth-child(2) { animation-delay: -2s; animation-duration: 7s; }
.float-soft:nth-child(3) { animation-delay: -4s; animation-duration: 6.5s; }
.float-soft:nth-child(4) { animation-delay: -1s; animation-duration: 7.5s; }

/* --- Orbes: manchas de color desenfocadas detras del contenido -------------
   Dos circulos enormes con blur, del primario y del acento, moviendose muy
   despacio. Cuesta dos divs vacios y es lo que separa un fondo plano de un
   fondo con profundidad. Van dentro de un contenedor con position:relative;
   sobre foto o video se ven poco: su sitio es una banda de color liso. */

.orbe {
  position: absolute;
  width: var(--orbe-tam, 22rem);
  aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(64px);
  pointer-events: none;
  z-index: 0;
  background: oklch(var(--primary-400) / 0.22);
  animation: float-soft 9s var(--ease-out) infinite;
}
.orbe--acento { background: oklch(var(--accent-500) / 0.2); animation-duration: 12s; animation-delay: -3s; }
.orbe--tr { top: -4rem; right: -3rem; }
.orbe--tl { top: -4rem; left: -3rem; }
.orbe--br { bottom: -4rem; right: -3rem; }
.orbe--bl { bottom: -4rem; left: -3rem; }
/* El contenido tiene que quedar por encima del orbe, no debajo. */
.orbe ~ * { position: relative; z-index: 1; }

/* El orbe sobresale por los bordes a proposito, y eso creaba barra de
   desplazamiento horizontal: medido, 1328px de ancho en una ventana de 1280.
   El overflow-x:hidden del body no basta para un hijo posicionado. Se recorta
   en el padre, y se hace solo con :has() para que no dependa de acordarse.
   `clip` y no `hidden`: hidden crearia un contenedor de scroll y romperia
   cualquier position:sticky de dentro. */
:where(section, header, footer, div):has(> .orbe) {
  position: relative;
  overflow: clip;
}

/* --- Texturas de fondo -----------------------------------------------------
   Una banda de color liso detras de tarjetas blancas se ve barata. Un punteado
   del 8 % no se nota y sin embargo se nota: la superficie deja de ser plana.
   `--light` es para fondos oscuros, donde el punto tiene que ser claro. */

.texture-dots {
  background-image: radial-gradient(oklch(var(--foreground-950) / 0.08) 1px, transparent 1px);
  background-size: 22px 22px;
}
.texture-dots--light {
  background-image: radial-gradient(oklch(var(--background-50) / 0.14) 1px, transparent 1px);
  background-size: 26px 26px;
}
.texture-grid {
  background-image:
    linear-gradient(oklch(var(--foreground-950) / 0.05) 1px, transparent 1px),
    linear-gradient(90deg, oklch(var(--foreground-950) / 0.05) 1px, transparent 1px);
  background-size: 44px 44px;
}
/* La textura se desvanece hacia abajo en vez de cortarse a media banda. */
.texture-fade {
  -webkit-mask-image: linear-gradient(to bottom, #000, transparent);
  mask-image: linear-gradient(to bottom, #000, transparent);
}

/* --- Titular con degradado -------------------------------------------------
   Solo para una o dos palabras dentro del h1, nunca para un parrafo: el texto
   con degradado pierde contraste medible y deja de poder auditarse. */
.text-gradient {
  background: linear-gradient(135deg, oklch(var(--primary-500)), oklch(var(--accent-500)));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* --- Indicador de "sigue bajando" ------------------------------------------
   Abajo del hero a pantalla completa. Es un enlace de verdad al ancla de la
   siguiente banda, no un adorno: quien lo pulsa espera que lleve a algun sitio. */
.scroll-cue {
  position: absolute;
  bottom: var(--space-3);
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  color: oklch(var(--background-50) / 0.6);
  font-size: 0.7rem;
  font-family: var(--font-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
.scroll-cue:hover { color: oklch(var(--background-50)); }
.scroll-cue svg { width: 1.1rem; height: 1.1rem; animation: cue-bounce 1.8s var(--ease-out) infinite; }
@keyframes cue-bounce {
  0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(5px); }
  60% { transform: translateY(2px); }
}

/* --- Latidos y giros lentos ------------------------------------------------ */

/* Respira: para un icono dentro de un circulo, o el logo de la carga. */
@keyframes breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.04); } }
.breathe { animation: breathe 4s var(--ease-out) infinite; }

/* Halo que late: para el punto de "en linea", "en vivo", "plazas libres". */
@keyframes pulse-glow {
  0%, 100% { box-shadow: 0 0 0 0 oklch(var(--accent-500) / 0.45); }
  50% { box-shadow: 0 0 0 12px oklch(var(--accent-500) / 0); }
}
.pulse-glow { animation: pulse-glow 2.2s var(--ease-out) infinite; }

/* Giro muy lento: para un anillo o un sello detras de una imagen. */
@keyframes spin-slow { to { transform: rotate(360deg); } }
.spin-slow { animation: spin-slow 26s linear infinite; }

/* Brillo que cruza la tarjeta al pasar por encima. El contenedor necesita
   position:relative y overflow:hidden. */
.shine { position: relative; overflow: hidden; }
.shine::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-120%) skewX(-15deg);
  background: linear-gradient(90deg, transparent, oklch(var(--background-50) / 0.28), transparent);
  transition: transform 0.7s var(--ease-out);
  pointer-events: none;
}
.shine:hover::after { transform: translateX(120%) skewX(-15deg); }

/* --- Pantalla de carga -----------------------------------------------------
   Cubre la pagina hasta que esta presentable: nadie ve entrar las fuentes, ni
   la foto del hero a medias, ni el rectangulo negro del video antes de su
   primer fotograma.

   Va en los pseudo-elementos de <html>, no en marcado: asi aparece con la
   primera linea de CSS -antes de que se pinte nada- y el agente no puede
   olvidarse de ponerla. js/site.js la retira con .is-ready; si el JS no llegara
   a ejecutarse, la animacion de seguridad la quita sola a los 6 s. Una pagina
   tapada para siempre por un script que fallo seria mucho peor que el problema
   que resuelve.

   Ajustes desde el HTML:
     data-preloader="off"     no la quiero en este sitio
     data-preloader="marca"   con el logo del negocio, via --preloader-logo   */

html::before,
html::after {
  position: fixed;
  z-index: 9999;
  pointer-events: none;
}
html::before {
  content: '';
  inset: 0;
  background: var(--preloader-bg, var(--c-bg));
  animation: preloader-failsafe 500ms var(--ease-out) 6s forwards;
}
html::after {
  content: '';
  top: 50%;
  left: 50%;
  width: 2.75rem;
  height: 2.75rem;
  margin: -1.375rem 0 0 -1.375rem;
  border-radius: 50%;
  border: 2px solid oklch(var(--foreground-950) / 0.16);
  border-top-color: var(--c-primary);
  animation: preloader-spin 800ms linear infinite,
             preloader-failsafe 500ms var(--ease-out) 6s forwards;
}

/* Retirada normal: la pone js/site.js cuando la pagina esta lista */
html.is-ready::before,
html.is-ready::after {
  opacity: 0;
  visibility: hidden;
  transition: opacity 450ms var(--ease-out), visibility 0s linear 450ms;
}

/* Con el logo del negocio: la marca manda y el aro baja */
html[data-preloader='marca']::before {
  background-image: var(--preloader-logo, none);
  background-repeat: no-repeat;
  background-position: center;
  background-size: min(38vw, 190px) auto;
}
html[data-preloader='marca']::after { top: calc(50% + 5.5rem); }

/* data-preloader="aro": la version que se lee como una pieza y no como tres
   cosas apiladas. El aro rodea el icono en vez de colgar debajo, el wordmark
   va aparte y cabe un rotulo. Es lo que separa una cortina de carga con
   criterio de un spinner suelto sobre fondo liso.

     --preloader-icono   url() del simbolo, el que va dentro del aro
     --preloader-marca   url() del wordmark, debajo
     --preloader-texto   '"Cargando..."' entre comillas, o nada

   Con un logotipo ancho de una sola pieza no uses esta: un aro alrededor de
   una palabra queda raro. Para eso esta data-preloader="marca". */
html[data-preloader='aro']::before {
  content: var(--preloader-texto, '');
  background-image: var(--preloader-icono, none), var(--preloader-marca, none);
  background-repeat: no-repeat, no-repeat;
  background-position:
    center calc(50% - var(--preloader-sube, 3rem)),
    center calc(50% + var(--preloader-marca-y, 3.2rem));
  background-size:
    var(--preloader-icono-tam, 58px) auto,
    var(--preloader-marca-tam, min(46vw, 200px)) auto;
  display: grid;
  place-items: center;
  align-content: center;
  padding-top: var(--preloader-texto-y, 11.5rem);
  font-family: var(--font-body);
  font-size: var(--text-small);
  color: var(--c-text-mute);
  letter-spacing: 0.02em;
}
html[data-preloader='aro']::after {
  top: calc(50% - var(--preloader-sube, 3rem));
  left: 50%;
  width: var(--preloader-aro-tam, 6.5rem);
  height: var(--preloader-aro-tam, 6.5rem);
  margin:
    calc(var(--preloader-aro-tam, 6.5rem) / -2) 0 0
    calc(var(--preloader-aro-tam, 6.5rem) / -2);
  border-width: 2px;
}

/* Apagada del todo */
html[data-preloader='off']::before,
html[data-preloader='off']::after { content: none; }

@keyframes preloader-spin { to { transform: rotate(360deg); } }
@keyframes preloader-failsafe { to { opacity: 0; visibility: hidden; } }

@media (prefers-reduced-motion: reduce) {
  html::after { animation: preloader-failsafe 1ms linear 6s forwards; border-top-color: transparent; }
}

/* Un bloque entero convertido en enlace desde la vista previa. Sin esto, la
   tarjeta que se hace pulsable se llena de subrayados y del color de enlace, y
   deja de parecerse a la que era. */
.enlace-bloque {
  display: block;
  color: inherit;
  text-decoration: none;
}
.enlace-bloque:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 3px; }

/* --- Ajustes del editor visual ---------------------------------------------
   Las clases que el usuario aplica desde la vista previa con la herramienta
   "Ajustar". Son propiedades de flujo -alineacion, espacio, ancho, orden-:
   la pagina se recompone sola y por construccion no puede haber solapes.
   El unico que si puede solapar es .u-shift, y por eso el editor comprueba
   colisiones antes de dejarlo pasar. */

.u-align-start  { text-align: left;   justify-content: flex-start; justify-items: start;  margin-inline: 0 auto; }
.u-align-center { text-align: center; justify-content: center;     justify-items: center; margin-inline: auto; }
.u-align-end    { text-align: right;  justify-content: flex-end;   justify-items: end;    margin-inline: auto 0; }
.u-align-center > :where(.lead, .prose, .section-head, p) { margin-inline: auto; }

.u-mt-0 { margin-top: 0; }
.u-mt-1 { margin-top: var(--space-2); }
.u-mt-2 { margin-top: var(--space-4); }
.u-mt-3 { margin-top: var(--space-5); }
.u-mt-4 { margin-top: var(--space-6); }
.u-mt-5 { margin-top: var(--space-7); }
.u-mb-0 { margin-bottom: 0; }
.u-mb-1 { margin-bottom: var(--space-2); }
.u-mb-2 { margin-bottom: var(--space-4); }
.u-mb-3 { margin-bottom: var(--space-5); }
.u-mb-4 { margin-bottom: var(--space-6); }
.u-mb-5 { margin-bottom: var(--space-7); }

.u-w-sm   { max-width: 34rem; }
.u-w-md   { max-width: 46rem; }
.u-w-lg   { max-width: 62rem; }
.u-w-full { max-width: none; }

/* Desplazamiento fino. Va despues de [data-reveal].is-visible y con la misma
   fuerza, si no la animacion de entrada lo anularia al terminar. */
.u-shift,
[data-reveal].u-shift.is-visible { transform: translate(var(--tx, 0px), var(--ty, 0px)); }

/* Encuadre de una foto dentro de su cuadro: que parte se ve y cuanto se acerca.
   Un marco de proporcion fija recorta por donde le toca, y lo que interesa casi
   nunca queda en el centro; esto lo coloca sin tocar el archivo.

   Que no pueda quedar un hueco no es cuidado, es construccion: object-fit pinta
   dentro de la caja del propio elemento -mover no desborda- y la escala crece
   desde el mismo punto de anclaje, asi que la caja ampliada siempre contiene a
   la de antes. Acercar solo puede tapar mas, nunca destapar.

   Va con !important, y es a proposito. Esto no es un estilo mas compitiendo
   con los otros: es donde el usuario ha puesto la foto con la mano. Una regla
   cualquiera del sitio -".media--corta img { object-position: center 40% }"-
   empata en fuerza y, por cargarse despues, ganaba: los numeros se guardaban
   bien y la foto no se movia. Lo que se coloca a mano manda sobre la hoja de
   estilos, o encuadrar no es encuadrar. */
img.u-encuadre,
video.u-encuadre {
  object-fit: cover !important;
  object-position: var(--enc-x, 50%) var(--enc-y, 50%) !important;
  transform: scale(var(--enc-z, 1)) !important;
  transform-origin: var(--enc-x, 50%) var(--enc-y, 50%) !important;
  transition: transform 0.18s var(--ease-out);
}

/* Alto del cuadro. Quien recorta una foto no es la foto: es la proporcion fija
   del hueco donde va. Con esto ese hueco deja de ser una decision del andamio
   y pasa a ser del usuario, que es quien sabe si esa foto se entiende apaisada
   o necesita respirar a lo alto. Con !important por lo mismo que el encuadre:
   cualquier ".media--corta { aspect-ratio: 16/5 }" del sitio empataria y
   ganaria por ir despues, y el boton no haria nada. */
.u-marco { aspect-ratio: var(--marco-ratio, 16 / 9) !important; height: auto !important; }

/* Ventana: la foto quieta y la pagina pasando por delante. El recorrido lo
   lleva site.js moviendo --enc-y; aqui solo se garantiza que el cuadro corte y
   que el navegador se prepare para animar esa propiedad. */
[data-ventana] { overflow: hidden; }
[data-ventana] > :where(img, video) { will-change: object-position; }

/* --- Fondo de una pieza ----------------------------------------------------
   Una foto detras del contenido de una tarjeta, una banda o una ficha.

   La capa va en un ::before y no en el background del propio elemento, y esa
   es toda la diferencia: la opacidad de un pseudoelemento afecta SOLO a la
   imagen. Si se pusiera en el elemento, el texto se desvaneceria con ella.

   El orden de pintado hace el resto del trabajo:

       color de la pieza  ->  imagen al X %  ->  contenido

   La imagen queda ENCIMA del color y DEBAJO del texto. Al 14 % lo que se ve
   es 86 % del tono de la tarjeta y 14 % de foto: el tinte no se pierde porque
   es lo que hay debajo, y la letra no se toca nunca. Por eso la fuerza esta
   limitada; pasado cierto punto ya no es un fondo, es una foto con texto
   encima, y eso es otra cosa que se hace con .scrim. */

.u-fondo { position: relative; isolation: isolate; }
.u-fondo::before {
  content: '';
  position: absolute;
  /* La capa se sale por los cuatro lados lo que vaya a desenfocarse. Un blur
     toma color de lo que tiene alrededor, y si la capa acabara justo en el
     borde, ese borde se desvaneceria y se veria un halo claro enmarcandolo. */
  inset: calc(-2 * var(--fondo-difuso, 0px));
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background-image: var(--fondo-img);
  background-size: cover;
  background-position: var(--fondo-x, 50%) var(--fondo-y, 50%);
  background-repeat: no-repeat;
  /* Menos saturacion y, si se pide, desenfoque: una foto nitida detras de un
     parrafo compite con el aunque este al 14 %, porque el ojo va al detalle.
     Desenfocada deja de tener detalle al que ir y pasa a ser lo que es, un
     ambiente. */
  filter: saturate(0.85) blur(var(--fondo-difuso, 0px));
  opacity: var(--fondo-fuerza, 0.14);
}
/* Solo cuando hay desenfoque: es el sobrante de la capa lo que hay que
   recortar, y recortar por defecto podria comerse algo que sobresalga a
   proposito de la pieza. */
.u-fondo--difuso { overflow: hidden; }

/* --- Realce del texto ------------------------------------------------------
   Con una foto detras, el texto compite con ella. Estas son las cuatro
   maneras que se usan de verdad, y cada una resuelve un caso distinto:

     halo      letra oscura sobre claro. No se ve; solo se lee mejor.
     contorno  lo mismo con filo. Aguanta fotos movidas y titulares grandes.
     sombra    letra clara sobre oscuro, que es el caso contrario.
     placa     cuando la foto es tan movida que no hay halo que la salve.

   Los grosores van en em y no en px a proposito: asi un titular de 100 px y
   un parrafo de 16 px reciben el mismo realce en proporcion, y no hace falta
   ningun mando de intensidad que ajustar pieza por pieza.

   El color por defecto es --c-bg, el del papel, porque es sobre lo que el
   texto ya estaba pensado para leerse, y ademas cambia solo en modo oscuro.
   Sobre una superficie que no sea la del papel se puede fijar a mano:

       <p class="u-realce--halo" style="--realce-color: var(--c-surface)"> */

.u-realce--halo {
  text-shadow:
    0 0 0.12em var(--realce-color, var(--c-bg)),
    0 0 0.28em var(--realce-color, var(--c-bg)),
    0 0 0.5em var(--realce-color, var(--c-bg));
}

.u-realce--contorno {
  -webkit-text-stroke: 0.055em var(--realce-color, var(--c-bg));
  /* Sin esto el trazo se pinta ENCIMA del relleno y se come medio grosor por
     dentro de cada letra: el texto adelgaza en vez de destacarse. */
  paint-order: stroke fill;
}

.u-realce--sombra {
  text-shadow: 0 0.04em 0.09em rgb(0 0 0 / 0.42), 0 0.08em 0.35em rgb(0 0 0 / 0.3);
}

/* La placa va en un ::after por lo mismo que el fondo va en un ::before: la
   opacidad de un pseudoelemento no toca al texto. Y en ::after y no en
   ::before para que, si la pieza ademas lleva foto de fondo, la placa quede
   por delante de la foto y no debajo. */
.u-realce--placa {
  position: relative;
  isolation: isolate;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-card);
}
.u-realce--placa::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: var(--realce-color, var(--c-bg));
  opacity: 0.78;
  backdrop-filter: blur(3px);
}

/* --- Accesibilidad --------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--c-primary);
  color: var(--c-on-primary);
  padding: 0.75rem 1.25rem;
  border-radius: 0 0 var(--radius-control) 0;
}
.skip-link:focus { left: 0; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@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 !important; transform: none !important; filter: none !important; }
}
