/* =====================================================================
   SmartProcess · Design System — COMPONENTES (firma P3 híbrida corregida)
   ---------------------------------------------------------------------
   Requiere tokens.css cargado antes. Solo consume var() — cero HEX.
   Capa 3 (tokens de componente) arriba; luego, componentes por bloques:
   layout · acciones · firma brutalista · familias A/B/C · prueba ·
   proceso · FAQ · formulario · UI espacial · motion hooks.
   Dosis de firma por página: ver design-system.md §5 (no saturar).

   R4 (2026-07-23, feedback móvil Fabio) — componentes compartidos:
    · .band: fuera la inclinación con el scroll; ahora va RECTA y su texto
      se desplaza en horizontal (--band-x, motion.js). Cambio GLOBAL.
    · .kpi-strip.lp-kpis en ≤880px: marquee infinito de una línea (bloque
      nuevo al final del archivo; la strip de la home NO se toca).
    · .lp-versus__foto: la carta del logo pasa POR ENCIMA del panel de
      soluciones (z-index 1 → 3).
   ===================================================================== */

:root {
  /* --- 3 · TOKENS DE COMPONENTE ------------------------------------ */
  --btn-bg:            var(--accent-2);
  --btn-ink:           var(--ink-inverse);
  --btn-shadow:        var(--shadow-b2);
  --btn-shadow-hover:  var(--shadow-b3);
  --btn-radius:        var(--r-sm);

  --sticker-bg:        var(--bg-surface);
  --sticker-ink:       var(--ink);
  --sticker-border:    var(--ink);
  --sticker-shadow:    var(--shadow-b1);

  --card-border:       var(--c-hairline);
  --card-radius:       var(--r-md);
  --card-hover-shadow: var(--shadow-b3);
  --card-hover-move:   translate(-3px, -3px);

  --fs-progress:       var(--grad-progress);
}

/* =====================================================================
   LAYOUT — contenedor, secciones, cabeceras de sección
   ===================================================================== */
.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

.sec { padding: var(--sec-y) 0; }
.sec--surface { background: var(--bg-surface); border-top: 1px solid var(--card-border); }
.sec--warm    { background: var(--bg-warm); }
.sec--calm    { background: var(--bg-calm); }
.sec--inverse { background: var(--bg-inverse); color: var(--ink-inverse); position: relative; overflow: hidden; }
.sec--inverse p { color: var(--ink-inverse-2); }

.sec-head {
  display: grid; grid-template-columns: 5fr 7fr;
  gap: var(--s-7); margin-bottom: 56px; align-items: start;
}
.sec-head h2 { margin-top: var(--s-3); }
.sec-head > p { font-size: var(--fs-body); max-width: 36rem; padding-top: 34px; }

/* =====================================================================
   ACCIONES — botón (firma: sombra sólida), estados completos
   ===================================================================== */
.btn {
  display: inline-block;
  font-family: var(--font-disp); font-weight: 700; font-size: .9375rem;
  background: var(--btn-bg); color: var(--btn-ink); text-decoration: none;
  padding: 14px 28px; border: 0; border-radius: var(--btn-radius);
  box-shadow: var(--btn-shadow);
  cursor: pointer;
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
.btn:hover  { transform: translate(-3px, -3px); box-shadow: var(--btn-shadow-hover); }
.btn:active { transform: translate(1px, 1px); box-shadow: var(--shadow-b1); }
.btn:focus-visible { box-shadow: var(--btn-shadow), var(--ring); }
.btn[disabled], .btn.is-disabled {
  background: var(--c-mute); box-shadow: none; transform: none;
  cursor: not-allowed; opacity: .7;
}

.btn--sm { padding: 10px 18px; font-size: .8438rem; box-shadow: var(--shadow-b1); }
.btn--sm:hover { box-shadow: 5px 5px 0 var(--c-pop); }

/* Ghost: para acciones secundarias sobre claro */
.btn--ghost {
  background: var(--bg-surface); color: var(--ink);
  border: 2px solid var(--ink); box-shadow: none;
}
.btn--ghost:hover { box-shadow: 4px 4px 0 var(--c-pop); }

/* Sobre fondos navy (CTA final, paneles producto) */
.btn--inverse { box-shadow: 5px 5px 0 var(--c-pop); }
.btn--inverse:hover { box-shadow: 8px 8px 0 var(--c-lila); }

/* Microcopy prompt bajo el CTA:  $ gratuito · sin compromiso · 30 min */
.micro {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-soft); margin-top: var(--s-4);
}
.micro b { color: var(--ok); }

/* Micro-trust bajo formularios (checklist CRO §4): candado + RGPD */
.microtrust {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-soft); margin-top: var(--s-3);
}

/* =====================================================================
   FIRMA BRUTALISTA — tags //, stickers [ ], brush, marker, chip, bullets
   ===================================================================== */

/* Section-tag como comentario de código. Máx 2 cursores por página:
   usa .tag--static en el resto. */
.tag {
  font-family: var(--font-mono); font-size: var(--fs-mono-md);
  color: var(--accent); font-weight: 500;
}
.tag::after {
  content: ""; display: inline-block; width: .5em; height: .95em;
  background: var(--accent); margin-left: 6px; vertical-align: -2px;
  animation: blink 1.1s steps(2) infinite;
}
.tag--static::after { content: none; }
.tag--yellow { color: var(--hl); }
.tag--yellow::after { background: var(--hl); }
@keyframes blink { 50% { opacity: 0; } }

/* Sticker [lowercase] con rotación por variable (--rot) */
.sticker {
  display: inline-block;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  background: var(--sticker-bg); color: var(--sticker-ink);
  border: 2px solid var(--sticker-border);
  box-shadow: var(--sticker-shadow);
  padding: 6px 13px; border-radius: var(--r-xs);
  transform: rotate(var(--rot, -3deg));
}
.sticker--pop    { background: var(--accent); color: var(--ink-inverse); }
.sticker--yellow { background: var(--hl); color: var(--ink); }
.sticker--float  { position: absolute; top: -16px; right: -8px; --rot: 5deg; z-index: 2; }

/* Brush amarillo bajo UNA palabra clave del H1 (1 por página).
   Se "pinta" al entrar en viewport si lleva data-brush (motion.js). */
.brush { position: relative; white-space: nowrap; }
.brush::after {
  content: ""; position: absolute; left: -2%; bottom: 8%;
  width: 104%; height: .3em; background: var(--hl); z-index: -1;
  transform: skewX(-6deg);
}
[data-brush]::after {
  transform: skewX(-6deg) scaleX(0); transform-origin: left;
  transition: transform var(--t-brush) var(--ease-out);
}
[data-brush].in::after { transform: skewX(-6deg) scaleX(1); }

/* Marker amarillo 60% en citas (no en headlines) */
mark, .marker {
  background: linear-gradient(transparent 55%, var(--hl) 55%);
  color: inherit; padding: 0 2px;
}

/* Chip morado suave */
.chip {
  display: inline-block;
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 700;
  background: var(--fam-b-soft); color: var(--accent);
  padding: 4px 10px; border-radius: var(--r-xs);
}

/* Bullets con > mono (sustituyen al check tradicional) */
.bullets { list-style: none; }
.bullets li {
  padding-left: 26px; position: relative; margin-bottom: 9px;
  color: var(--ink-body); font-weight: 500; font-size: var(--fs-sm);
}
.bullets li::before {
  content: ">"; font-family: var(--font-mono); font-weight: 700;
  color: var(--accent); position: absolute; left: 0;
}

/* Número mega outline (proceso, listados). Sobre navy: usa .outline-num--lila */
.outline-num {
  font-family: var(--font-disp); font-weight: 700; font-size: var(--fs-outline);
  letter-spacing: var(--ls-tight); line-height: 1;
  -webkit-text-stroke: 2px var(--ink); color: transparent;
  font-feature-settings: "tnum" 1;
}
.outline-num--lila { -webkit-text-stroke: 2px var(--c-lila); }

/* =====================================================================
   KPI STRIP — contadores de confianza (fallback sin JS: valor ya escrito)
   ===================================================================== */
.kpi-strip {
  border-top: 1px solid var(--card-border);
  border-bottom: 1px solid var(--card-border);
  background: var(--bg-surface);
}
.kpi-strip .wrap { display: grid; grid-template-columns: repeat(3, 1fr); padding: 0; }
.kpi-strip .kpi {
  padding: 26px var(--gutter); border-right: 1px solid var(--card-border);
  display: flex; align-items: baseline; gap: var(--s-4);
}
.kpi-strip .kpi:last-child { border-right: 0; }
.kpi b {
  font-family: var(--font-disp); font-size: 2rem; font-weight: 700;
  letter-spacing: var(--ls-tight); color: var(--ink);
  /* R1-A fix: "+10 años" partía "años" a otra línea (el número y el sufijo
     son spans distintos). display:flex + nowrap los mantiene en una línea. */
  white-space: nowrap; display: flex; align-items: baseline; gap: .25em;
}
.kpi b .u { font-family: var(--font-mono); font-size: .55em; color: var(--accent); font-weight: 700; }
.kpi small { font-size: .8125rem; color: var(--ink-soft); }

/* =====================================================================
   FAMILIAS DEL CATÁLOGO — cada familia con su propio lenguaje visual
   (corrección Fabio: bloques diferenciados, como en 02_Servicios/)
   ===================================================================== */

/* Cabecera común de bloque de familia */
.fam { margin-bottom: var(--s-8); }
.fam__head {
  display: flex; align-items: center; gap: var(--s-4);
  margin-bottom: var(--s-5); flex-wrap: wrap;
}
.fam__head h3 { font-size: 1.05rem; }
.fam__head .fam-note {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
}

/* --- Familia A · Excel — feel de hoja de cálculo -------------------- */
/* Filas con rejilla fina, columna-índice tipo celda (A1, A2), barra fx. */
.fam--a .fxbar {
  display: flex; align-items: center; gap: var(--s-3);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  background: var(--fam-a-soft); border: 1px solid var(--card-border);
  border-bottom: 0; border-radius: var(--r-xs) var(--r-xs) 0 0;
  padding: 8px 14px;
}
.fam--a .fxbar b { color: var(--ok); font-style: italic; } /* R1-A: verde Excel, no azul de familia */
.svc-sheet { border: 1px solid var(--card-border); background: var(--bg-surface); }
.svc-sheet__row {
  display: grid; grid-template-columns: 76px 1.15fr 1fr 140px;
  gap: var(--s-6); padding: 30px 18px; align-items: start;
  border-bottom: 1px solid var(--card-border);
  text-decoration: none; color: inherit;
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop), background var(--t-base);
  position: relative;
}
.svc-sheet__row:last-child { border-bottom: 0; }
.svc-sheet__row .cell {
  font-family: var(--font-mono); font-size: var(--fs-mono-md); font-weight: 700;
  color: var(--ok); /* R1-A: familia A en verde (feel Excel), no azul */
  border: 1px solid var(--ok); background: var(--fam-a-soft);
  border-radius: var(--r-xs);
  width: 52px; height: 36px; display: grid; place-items: center;
}
.svc-sheet__row h3 { font-size: 1.25rem; margin-bottom: 5px; }
.svc-sheet__row .sub { font-family: var(--font-mono); font-size: .7188rem; color: var(--ink-soft); }
.svc-sheet__row p { font-size: var(--fs-sm); }
.svc-sheet__row .go {
  font-family: var(--font-disp); font-weight: 600; font-size: .875rem;
  color: var(--ink); text-align: right; padding-top: 8px;
}
.svc-sheet__row:hover {
  background: var(--bg-surface); transform: var(--card-hover-move);
  box-shadow: 6px 6px 0 var(--ok); z-index: 1; /* R1-A: sombra verde Excel */
}
.svc-sheet__row:hover .go { color: var(--fam-a); }

/* --- Familia B · Verticales — cards a medida ------------------------ */
.svc-cards { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.svc-card {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: 30px 30px 26px;
  text-decoration: none; color: inherit; position: relative;
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop), border-color var(--t-base);
}
.svc-card .idx {
  font-family: var(--font-mono); font-size: var(--fs-mono-md); font-weight: 700;
  color: var(--fam-b); display: block; margin-bottom: var(--s-3);
}
.svc-card h3 { font-size: 1.25rem; margin-bottom: 8px; }
.svc-card p { font-size: var(--fs-sm); margin-bottom: var(--s-4); }
.svc-card .for {
  font-family: var(--font-mono); font-size: .7188rem; color: var(--accent);
  background: var(--fam-b-soft); border-radius: var(--r-xs);
  padding: 4px 10px; display: inline-block;
}
/* CTA de refuerzo (R3, feedback Fabio: B/C no dejaban claro que la card
   entera ya es un <a> a la página de servicio/producto). Mismo lenguaje
   que `.svc-sheet__row .go` de la familia A, adaptado a card vertical. */
.svc-card__go {
  display: block; margin-top: var(--s-4);
  font-family: var(--font-disp); font-weight: 600; font-size: .875rem;
  color: var(--ink);
  transition: color var(--t-base), transform var(--t-base) var(--ease-pop);
}
.svc-card:hover {
  transform: var(--card-hover-move); box-shadow: var(--card-hover-shadow);
  border-color: var(--ink);
}
.svc-card:hover .svc-card__go { color: var(--fam-b); transform: translateX(3px); }

/* --- Familia C · Producto propio — paneles navy con producto real --- */
.product-panels { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.product-panel {
  display: block; /* R2-B: puede vivir dentro de .product-panel-wrap (ya no
    es hijo directo del grid), sin blockify automático de grid-item */
  background: var(--fam-c); color: var(--ink-inverse);
  border-radius: var(--card-radius); padding: 32px;
  position: relative; overflow: hidden; text-decoration: none;
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
.product-panel::after {
  content: ""; position: absolute; right: -90px; top: -90px;
  width: 260px; height: 260px; border-radius: 50%;
  background: var(--glow-pop); pointer-events: none;
}
.product-panel .own {
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 700;
  color: var(--fam-c); background: var(--fam-c-accent);
  padding: 4px 10px; border-radius: var(--r-xs); display: inline-block;
  margin-bottom: var(--s-4);
}
.product-panel h3 { color: var(--ink-inverse); font-size: 1.35rem; margin-bottom: 8px; }
.product-panel p { color: var(--ink-inverse-2); font-size: var(--fs-sm); margin-bottom: var(--s-4); }
.product-panel .shot {
  border-radius: var(--r-sm); border: 1px solid var(--c-navy-2);
  box-shadow: var(--shadow-soft); position: relative; z-index: 1;
}
/* CTA de refuerzo (R3, feedback Fabio) — lila sobre navy: legible (§9,
   el amarillo nunca es color de texto; el lila sí está aprobado sobre
   navy, es el mismo tono que ya usan los contadores/métricas del caso). */
.product-panel__go {
  display: inline-flex; align-items: center; gap: 6px; margin-top: var(--s-4);
  font-family: var(--font-disp); font-weight: 700; font-size: .875rem;
  color: var(--c-lila); position: relative; z-index: 1;
  transition: transform var(--t-base) var(--ease-pop), color var(--t-base);
}
.product-panel:hover { transform: var(--card-hover-move); box-shadow: var(--shadow-b3); }
.product-panel:hover .product-panel__go { color: var(--ink-inverse); transform: translateX(3px); }

/* =====================================================================
   DUELO [vs] — SaaS genérico contra SmartProcess (tesis)
   ===================================================================== */
.duel {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--c-navy-2); border: 1px solid var(--c-navy-2);
  border-radius: var(--r-md); overflow: hidden; position: relative;
}
.duel .col { background: var(--bg-inverse); padding: 38px 34px; }
/* R1-A "duelo con contraste": columnas destacadas del fondo en vez de
   ambas sobre el mismo navy — el [vs] queda de bisagra entre las dos. */
.duel .col--saas {
  background: var(--c-navy-2); border-top: 3px solid var(--danger);
  filter: saturate(.85); /* ligera desaturación: es la columna "mala" */
}
.duel .col--sp {
  background: var(--accent); color: var(--ink-inverse);
  border-top: 3px solid var(--hl); box-shadow: var(--shadow-b2);
}
.duel h3 { font-size: 1.05rem; margin-bottom: var(--s-4); color: var(--ink-inverse); }
.duel ul { list-style: none; font-size: var(--fs-sm); }
/* R1 (ROADMAP p.1): el tachado pasa de permanente a scroll-linked. El
   line-through de text-decoration no es animable, así que se sustituye por
   una línea propia en ::after que crece de izquierda a derecha con
   scaleX. Solo transform + opacity, nada de layout: la altura de la fila
   no cambia y la lista no salta. Sin GSAP o con reduced-motion,
   spScrubList añade .is-struck a todas de golpe y queda igual que antes. */
.duel .col--saas li {
  color: var(--c-mute-inverse); margin-bottom: 10px;
  position: relative;
  transition: opacity var(--t-base) var(--ease-out);
}
.duel .col--saas li::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 2px; background: currentColor; border-radius: 1px;
  transform: scaleX(0); transform-origin: left center;
  transition: transform var(--t-base) var(--ease-out);
}
.duel .col--saas li.is-struck::after { transform: scaleX(1); }
.duel .col--saas li.is-struck { opacity: .62; }
@media (prefers-reduced-motion: reduce) {
  .duel .col--saas li::after { transition: none; }
}
/* R1: en móvil los bullets envuelven a 2 líneas (medido a 375px: el primero
   ocupa 48px con line-height 24), y entonces la raya de ::after cae en el
   hueco ENTRE las dos líneas y se lee como separador, no como tachado.
   Ahí se vuelve a text-decoration, que sí tacha multilínea. Sigue siendo
   scroll-linked: manda la misma clase .is-struck, solo cambia el pintado, y
   text-decoration no provoca salto de layout. */
@media (max-width: 880px) {
  .duel .col--saas li::after { display: none; }
  .duel .col--saas li.is-struck { text-decoration: line-through; }
}
.duel .col--sp li {
  color: var(--ink-inverse); /* blanco sobre #5d46ef ≈ contraste AA (§9) */
  margin-bottom: 10px; padding-left: 22px; position: relative;
}
.duel .col--sp li::before {
  content: "✓"; color: var(--hl); position: absolute; left: 0; font-weight: 700;
}
.duel .vs {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%) rotate(-6deg);
  /* Antes .accent/blanco: con .col--sp ahora morada, el [vs] se perdía.
     Amarillo + navy (nunca amarillo como texto en fondos claros — aquí es
     fondo, no texto, así que respeta §9) para que siga leyéndose de bisagra. */
  background: var(--hl); color: var(--ink);
  font-family: var(--font-mono); font-weight: 700; font-size: .875rem;
  width: 54px; height: 54px; border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 3px 3px 0 rgba(0, 0, 0, .3); z-index: 2;
}

/* =====================================================================
   PROCESO / TIMELINE — de la llamada al sistema funcionando
   R2-A: `.tl-line` la inyecta motion.js §14 dentro de `.timeline` y anima
   una única variable `--tl-p` (0→1) ligada al scroll (GSAP/ScrollTrigger).
   La orientación (horizontal en grid, vertical en móvil apilado) la decide
   ESTE archivo por media query — JS no necesita saber en qué breakpoint
   está, solo empuja el progreso.
   ===================================================================== */
.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-6); position: relative; }
.tstep {
  border-top: 2px solid var(--c-hairline); padding-top: var(--s-4);
  transition: border-color var(--t-base);
}
.tstep i {
  font-family: var(--font-mono); font-style: normal; font-size: var(--fs-mono-md);
  color: var(--ink-soft); font-weight: 700; display: block; margin-bottom: var(--s-3);
  transition: color var(--t-base);
}
.tstep h3 { font-size: 1.05rem; margin-bottom: 8px; }
.tstep p { font-size: .875rem; }
.tstep .hl-pill {
  background: var(--fam-b-soft); font-family: var(--font-mono); font-size: .6875rem;
  color: var(--accent); padding: 3px 9px; border-radius: var(--r-xs);
  display: inline-block; margin-top: var(--s-3);
}
/* La línea "se rellena": el paso pasa de gris a morado según --tl-p avanza
   (motion.js §14 hace el cálculo por paso y añade/quita esta clase). */
.tstep.is-on { border-top-color: var(--accent); }
.tstep.is-on i { color: var(--accent); }

/* Línea de progreso — horizontal por encima de la rejilla de 4 columnas.
   `transform-origin: left` + `scaleX(var(--tl-p))`: crece de izquierda a
   derecha con el scroll (motion.js anima --tl-p, no el transform directo,
   para que el mismo JS sirva también en la variante vertical de abajo). */
.tl-line {
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: var(--grad-progress);
  transform-origin: left center;
  transform: scaleX(var(--tl-p, 0));
  pointer-events: none;
}
.tl-line.is-filled { transform: scaleX(1); }

@media (max-width: 560px) {
  /* Timeline apilada (1 columna, ver media query de más abajo): la línea
     pasa a vertical, pegada al borde izquierdo, y crece hacia abajo. */
  .timeline { padding-left: var(--s-5); }
  .tstep { border-top: 0; padding-top: 0; padding-left: var(--s-4); border-left: 2px solid var(--c-hairline); }
  .tstep.is-on { border-left-color: var(--accent); border-top-color: transparent; }
  .tl-line {
    left: 0; top: 0; right: auto; bottom: 0; width: 2px; height: 100%;
    background: linear-gradient(180deg, var(--c-blue) 0%, var(--c-pop) 100%);
    transform-origin: top center;
    transform: scaleY(var(--tl-p, 0));
  }
  .tl-line.is-filled { transform: scaleY(1); }
}

/* Garantías — "qué pasa si sale mal" (CRO §3.2, sección propia) */
.guarantees { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
.guarantee {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: 24px 26px;
  font-size: var(--fs-sm); color: var(--ink-body);
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop), border-color var(--t-base);
}
.guarantee .n {
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 700;
  color: var(--accent); display: block; margin-bottom: 8px;
}
.guarantee b { display: block; font-family: var(--font-disp); color: var(--ink); font-size: .9688rem; margin-bottom: 5px; }
.guarantee:hover { box-shadow: 5px 5px 0 var(--c-pop); transform: translate(-2px, -2px); border-color: var(--ink); }

/* Garantías v2 (R1-A) — variante con más protagonismo, pensada para fondo
   crema (.sec--warm). No toca la v1 (.guarantees a secas) en ningún selector.
   Cada .guarantee fija su acento inline: style="--g-accent: var(--c-pop)"
   (o --c-blue / --c-green / --c-yellow). Con amarillo, añade TAMBIÉN la
   clase .guarantee--yellow: el amarillo nunca es color de texto (§9), así
   que el número y el título se quedan navy en esa tarjeta. */
.guarantees--v2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
.guarantees--v2 .guarantee { border-top: 4px solid var(--g-accent, var(--accent)); }
.guarantees--v2 .guarantee .n { color: var(--g-accent, var(--accent)); }
.guarantees--v2 .guarantee b { font-size: 1.05rem; } /* v1 = .9688rem: un pelín más grande */
.guarantees--v2 .guarantee:hover {
  transform: translate(-3px, -3px);
  box-shadow: 7px 7px 0 var(--g-accent, var(--accent));
  border-top-color: var(--g-accent, var(--accent)); /* re-afirma el acento: .guarantee:hover pisa border-color */
}
.guarantees--v2 .guarantee--yellow .n,
.guarantees--v2 .guarantee--yellow b { color: var(--ink); }

/* ============================================================
   CONTRATO DE GARANTÍAS (home) — R1/ROADMAP p.1
   Sustituye a .warranty-doc/.warranty-clause (rejilla 2x2 de tarjetas).
   Fabio pidió que "parezca un contrato que se firma, no tarjetas":
   columna única, ordinales en mono, sangría por hairlines, y sello
   estampado al entrar en viewport.
   Regla de la casa que se conserva del bloque anterior: el folio lleva
   overflow:hidden para recortar la esquina doblada, así que el .fico de
   llaves vive FUERA (hermano), no dentro.
   ============================================================ */
.warranty-doc-wrap { position: relative; }
.warranty-doc-wrap > .fico { z-index: 2; }

.contract {
  position: relative; overflow: hidden;
  background: var(--bg-surface);
  border: 2px solid var(--ink); border-radius: var(--card-radius);
  box-shadow: var(--shadow-b3);
  padding: var(--s-7);
}
/* esquina doblada del folio (heredada del diseño anterior) */
.contract::before {
  content: ""; position: absolute; top: 0; right: 0;
  border-width: 0 44px 44px 0; border-style: solid;
  border-color: transparent var(--bg-warm) transparent transparent;
  filter: drop-shadow(-2px 2px 1px rgb(0 0 0 / .10));
}

.contract__head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s-3) var(--s-5);
  padding-bottom: var(--s-4); margin-bottom: var(--s-5);
  border-bottom: 2px solid var(--ink);
}
.contract__ref {
  font-family: var(--font-mono); font-size: var(--fs-mono-md); font-weight: 700;
  color: var(--accent);
}
.contract__parties {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-soft);
}

.contract__clauses { list-style: none; margin: 0; padding: 0; }
.contract__clause {
  padding: var(--s-5) 0 var(--s-5) var(--s-6);
  border-left: 3px solid var(--accent);
  margin-left: 2px;
}
.contract__clause + .contract__clause { border-top: 1px dashed var(--card-border); }
.contract__ord {
  display: block; margin-bottom: var(--s-2);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-soft);
}
.contract__clause b {
  display: block; font-family: var(--font-disp); font-size: 1.05rem;
  color: var(--ink); margin-bottom: var(--s-2);
}
.contract__clause p { font-size: var(--fs-sm); }

.contract__sign {
  display: flex; align-items: flex-end; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-5);
  margin-top: var(--s-6); padding-top: var(--s-5);
  border-top: 2px solid var(--ink);
}
.contract__sign-line { min-width: 0; }
/* La raya de firma es HORIZONTAL y vive en el hueco de la rúbrica, NO en
   `.contract__sign-line`: ahí caería por debajo del nombre impreso y se
   leería como un subrayado, no como una línea de firma. Lo único que se
   inclina es el trazo, que así cruza una recta como en un documento
   firmado de verdad. */
.contract__signature {
  display: block; width: fit-content; max-width: 100%;
  padding-right: var(--s-6);
  border-bottom: 2px solid var(--ink);
}
.contract__rubric {
  display: block; width: 210px; max-width: 100%; height: auto;
  margin-bottom: -14px;   /* el swash rebasa la raya y la cruza */
  transform: rotate(-3deg); transform-origin: left bottom;
  overflow: visible;
}
.contract__rubric path {
  fill: none; stroke: var(--ink); stroke-width: 3;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dashoffset: 0;   /* estado final: sin JS la firma ya está puesta */
}
.contract__sign-label {
  display: block; margin-top: var(--s-3);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
}

/* Sello de tinta: aro doble, redondo y en `multiply` para que se lea como
   tinta ABSORBIDA por el papel y no como una caja pegada encima. Nace
   grande, girado y transparente; cuando el pie entra en viewport el JS
   pone .is-stamped y cae a su sitio con --ease-pop. Solo transform +
   opacity.
   Las cajas base llevan ya el estado FINAL (firma dibujada, sello puesto):
   los estados iniciales se cuelgan de `.js`, guardarraíl de la casa (§8
   del sistema de diseño, mismo patrón que `.js [data-reveal]`) — sin JS
   se ve el contrato firmado, no un folio en blanco para siempre. */
.contract__seal {
  display: inline-flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px;
  width: 128px; height: 128px; flex: 0 0 auto;
  border: 3px double var(--accent); border-radius: 50%;
  color: var(--accent); mix-blend-mode: multiply;
  opacity: .92; transform: rotate(-9deg) scale(1);
  transition: opacity var(--t-reveal) var(--ease-pop), transform var(--t-reveal) var(--ease-pop);
}
.contract__seal img { width: 76px; height: auto; display: block; opacity: .9; }
.contract__seal em {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  font-style: normal; text-transform: uppercase; letter-spacing: .12em;
  /* dentro de un círculo, partir en dos líneas se ve roto: si una fuente
     de reserva viniera más ancha, que rebase el aro antes que envolver */
  white-space: nowrap;
}

/* Secuencia al entrar el pie en viewport: se dibuja el trazo, lo remata el
   swash y SOLO entonces cae el sello. Todo el encadenado es
   transition-delay sobre un único `.is-stamped`, sin máquina de estados. */
.js .contract__rubric path { stroke-dasharray: 100; stroke-dashoffset: 100; }
.js .contract__rubric-main {
  transition: stroke-dashoffset var(--t-reveal) var(--ease-out);
}
.js .contract__rubric-swash {
  transition: stroke-dashoffset calc(var(--t-reveal) * .5) var(--ease-out) calc(var(--t-reveal) * .6);
}
.js .contract__seal { opacity: 0; transform: rotate(-22deg) scale(1.7); }
.js .contract__sign.is-stamped .contract__rubric path { stroke-dashoffset: 0; }
.js .contract__sign.is-stamped .contract__seal {
  opacity: .92; transform: rotate(-9deg) scale(1);
  transition-delay: calc(var(--t-reveal) * 1.1);   /* justo al acabar de firmar */
}

@media (prefers-reduced-motion: reduce) {
  .contract__seal, .contract__rubric path { transition: none; }
  /* estado final visible aunque el JS no llegue a estampar */
  .js .contract__seal { opacity: .92; transform: rotate(-9deg) scale(1); }
  .js .contract__rubric path { stroke-dashoffset: 0; }
}
@media (max-width: 880px) {
  .contract { padding: var(--s-6) var(--s-5); }
  .contract__sign { align-items: flex-start; }
  .contract__seal { align-self: flex-end; }
}

/* SUPERVIVIENTE del bloque `.warranty-doc` anterior: la firma mono NO se
   borra aunque el home ya no la use. `.warranty-doc__signature` la
   consumen 6 landings a través de `.lp-mapa__sign` (macroning, smartaudit
   y los 4 /servicios/*) para firmar su mapa. Es una clase compartida, no
   una regla huérfana: si desaparece, esas 6 páginas pierden la firma. */
.warranty-doc__signature {
  position: relative; padding-bottom: 5px;
  font-family: var(--font-mono); font-style: italic; font-size: 1.05rem; color: var(--ink);
}
.warranty-doc__signature::after {
  content: ""; position: absolute; left: -2%; bottom: -1px;
  width: 104%; height: .26em; background: var(--hl); z-index: -1;
  transform: skewX(-6deg);
}

/* =====================================================================
   BANDA GIGANTE + TICKER — firma en movimiento
   R4 (2026-07-23, feedback móvil Fabio): la banda YA NO se inclina con el
   scroll — se retira `rotate(var(--band-rot))`. Ahora va RECTA y lo que se
   mueve es el TEXTO, que se desplaza en horizontal tipo carrusel ligado al
   scroll: motion.js alimenta `--band-x` con los píxeles que el texto
   desborda del raíl (.band ya trae overflow:hidden, que es el raíl).
   El cambio es GLOBAL a propósito: .band vive en home, casos, nosotros,
   macroning, smartaudit y las 4 landings. Sin JS, --band-x cae a 0px:
   texto pegado a la izquierda y el resto recortado (correcto).
   ===================================================================== */
.band {
  background: var(--bg-warm); padding: 46px 0; overflow: hidden;
  border-top: 1px solid var(--c-cream-2); border-bottom: 1px solid var(--c-cream-2);
}
.band > div {
  font-family: var(--font-disp); font-weight: 700; font-size: var(--fs-mega);
  letter-spacing: -.03em; white-space: nowrap;
  -webkit-text-stroke: 1.5px var(--ink); color: transparent;
  transform: translate3d(var(--band-x, 0px), 0, 0); /* motion.js: data-band lo liga al scroll */
  will-change: transform;
}
.band b { -webkit-text-stroke: 0; color: var(--accent); }
.band--inverse { background: var(--bg-inverse); border-color: var(--c-navy-2); }
.band--inverse > div { -webkit-text-stroke-color: var(--c-lila); }
.band--inverse b { color: var(--hl); }

.ticker {
  background: var(--bg-inverse); overflow: hidden;
  height: 30px; display: flex; align-items: center;
}
.ticker__track {
  display: flex; gap: 2.5rem; white-space: nowrap;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-inverse-2);
  animation: ticker 34s linear infinite; will-change: transform;
}
.ticker__track span::before { content: "› "; color: var(--accent); font-weight: 700; }
.ticker:hover .ticker__track { animation-play-state: paused; }
@keyframes ticker { to { transform: translateX(-50%); } }

/* =====================================================================
   PRUEBA — caso real (cita + métricas), founder, badges de portales
   ===================================================================== */
.case { display: grid; grid-template-columns: 2fr 1fr; gap: var(--s-8); align-items: start; }
.case blockquote {
  font-family: var(--font-disp); font-weight: 500;
  font-size: clamp(1.4rem, 1rem + 1.5vw, 2rem); line-height: 1.35;
}
.case .src { margin-top: var(--s-5); font-size: .875rem; color: var(--ink-soft); }

/* Aside navy simple de .case (documentado en design-system.md §4 desde el
   principio, pero nunca implementado: el home saltó directo a la variante
   .case-terminal de abajo). Cubre el hueco para páginas de servicio/casos
   que quieran la cita + métricas sin la ventana de terminal completa
   (usado por primera vez en /servicios/blindar-excel, Tarea 3.2). */
/* :not(.case-terminal) — sin él, `.case aside` (0,1,1) pisa el padding:0 y
   el fondo propios de la variante terminal (0,1,0) allí donde el terminal
   vive dentro de un .case (carrusel de la home, fichas de /casos). */
.case aside:not(.case-terminal) {
  background: var(--bg-inverse); color: var(--ink-inverse);
  border-radius: var(--r-lg); padding: var(--s-7);
  box-shadow: var(--shadow-b3); position: relative; overflow: hidden;
}
.case aside:not(.case-terminal) b {
  font-family: var(--font-disp); font-weight: 700; font-size: 2rem;
  color: var(--c-lila); display: block; font-feature-settings: "tnum" 1;
}
.case aside:not(.case-terminal) small { font-size: .7813rem; color: var(--ink-inverse-2); display: block; }
.case aside:not(.case-terminal) hr { border: 0; border-top: 1px solid var(--c-navy-2); margin: var(--s-5) 0; }

/* =====================================================================
   TARJETA KPI "TERMINAL" (R3, rediseño pedido por Fabio) — `.case-terminal`
   sustituye al aside navy plano de antes: ahora es una ventana de terminal
   real (barra con 3 dots + título mono `resultados_reales.log`), cada
   métrica es una línea de log con prefijo `>` morado + valor grande lila
   (tabular) + descripción en gris claro, separadas por hairlines navy-2.
   Deliberadamente MUY distinta de cualquier otra card navy de la página.
   ===================================================================== */
.case-terminal {
  background: var(--c-navy-deep); color: var(--ink-inverse);
  border: 1px solid var(--c-lila); border-radius: var(--r-lg);
  box-shadow: var(--shadow-b3); /* sombra sólida morada (--c-pop) */
  padding: 0; overflow: hidden; position: relative;
}
.case-terminal__bar {
  display: flex; align-items: center; gap: 6px;
  padding: 12px 18px; background: var(--c-navy-2);
  border-bottom: 1px solid var(--c-navy-2);
}
.case-terminal__bar i { width: 9px; height: 9px; border-radius: 50%; display: block; }
.case-terminal__bar i:nth-child(1) { background: var(--danger); }
.case-terminal__bar i:nth-child(2) { background: var(--hl); }
.case-terminal__bar i:nth-child(3) { background: var(--ok); }
.case-terminal__title {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-inverse-2);
  margin-left: 8px;
}
.case-terminal__body { padding: 24px 22px; }
.case-terminal__line {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 15px 0; border-bottom: 1px solid var(--c-navy-2);
}
.case-terminal__line:last-child { border-bottom: 0; }
.case-terminal__prefix {
  font-family: var(--font-mono); font-weight: 700; font-size: 1.05rem;
  color: var(--c-lila); line-height: 1; padding-top: 6px; flex: none;
}
.case-terminal__line b {
  font-family: var(--font-disp); font-weight: 700; font-size: 1.7rem; color: var(--c-lila);
  display: block; font-feature-settings: "tnum" 1;
}
.case-terminal__line small { font-size: .7813rem; color: var(--ink-inverse-2); display: block; margin-top: 3px; }

/* Declaración sin comillas, para casos SIN blockquote (S3, caso Gabriel):
   jamás se fabrica una cita atribuida a alguien real — regla de /casos.
   Mismo cuerpo tipográfico que `.case blockquote`, sin semántica de cita. */
.case-statement {
  font-family: var(--font-disp); font-weight: 500;
  font-size: clamp(1.4rem, 1rem + 1.5vw, 2rem); line-height: 1.35;
}

/* Foto real del fundador — borde morado (brand-guide §1) */
.founder { position: relative; display: inline-block; }
.founder img {
  border: 3px solid var(--ink); border-radius: var(--r-md);
  box-shadow: var(--shadow-b3);
}
.founder .sticker { position: absolute; bottom: -14px; left: -10px; --rot: -5deg; }
.founder-caption { margin-top: var(--s-5); font-size: .875rem; color: var(--ink-soft); }
.founder-caption b { display: block; font-family: var(--font-disp); color: var(--ink); font-size: 1rem; }

/* Portales que ya automatizamos (AEAT, TGSS, DEHú, Catastro…) */
.badges-portal {
  display: flex; gap: var(--s-7); align-items: center; flex-wrap: wrap; /* R2-A: gap acorde a los badges más grandes */
}
.badges-portal img {
  /* R1-b (ROADMAP p.1): 48 -> 34. Al recortar los PNG al bbox alfa (los
     lienzos venían casi vacíos) el contenido real creció a igual height, y
     a 48px se veían demasiado grandes (feedback Fabio). 34 los deja con la
     presencia justa sobre la tira navy. */
  height: 34px; width: auto;
  transition: transform var(--t-base) var(--ease-pop);
}
.badges-portal img:hover { transform: translateY(-4px) rotate(-2deg); }
.badges-portal .lbl { font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft); width: 100%; }

/* =====================================================================
   MEDIA-FRAME — ventana de app con MATERIAL REAL dentro
   (corrección Fabio: mock ajustado a nuestros servicios, no barras grises)
   ===================================================================== */
.media-frame {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--r-lg); padding: 14px;
  box-shadow: var(--shadow-soft); position: relative;
  will-change: transform;                 /* motion.js: data-tilt */
}
.media-frame__bar { display: flex; gap: 6px; align-items: center; margin-bottom: 12px; }
.media-frame__bar i {
  width: 10px; height: 10px; border-radius: 50%; background: var(--card-border); display: block;
}
.media-frame__bar .title {
  font-family: var(--font-mono); font-size: .6875rem; color: var(--ink-soft); margin-left: 8px;
}
.media-frame__body { border-radius: var(--r-sm); overflow: hidden; }
.media-frame__body img, .media-frame__body video { width: 100%; }
.media-frame__status {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  /* --ok-text (no --ok): es texto informativo, el verde decorativo se
     queda a 3.5:1 y no pasa AA (F8-QA). */
  color: var(--ok-text); margin-top: 12px;
}
.media-frame--dark { background: var(--c-navy-2); border-color: var(--c-navy-2); }
.media-frame--dark .media-frame__bar .title { color: var(--ink-inverse-2); }

/* =====================================================================
   CARRUSEL (R1-A, fixes R2-A, R3) — [data-carousel] > .carousel__track >
   .carousel__slide. Controles y dots generados por motion.js §11.
   R2-A / fix Fabio: el slide inactivo se veía superpuesto al activo. Causa
   clásica de flex: sin `min-width` explícito, un flex-item usa `min-width:
   auto` (= el max-content de su contenido), así que si el contenido interno
   (p.ej. `.case`, un grid) no se encoge a la fuerza, el slide acaba más
   ancho que el 100% de `flex-basis` y el `translateX` por slide deja de
   coincidir con el ancho real → solape. `min-width: 100%` fija el ancho del
   slide con independencia de su contenido; `min-width: 0` en `.case`
   permite que ESE contenido sí se encoja dentro del slide.
   R3 / fix Fabio (reportó el mismo solape otra vez, con captura): causa
   raíz real localizada en Chrome real con computed styles — el `padding-
   top`/`margin-top` negativos de más abajo (versión anterior de este
   comentario) compensaban a un `.sticker` con `--rot` que YA NO EXISTE en
   este carrusel (los 3 `.case` solo llevan un `.chip` sin rotar dentro de
   la slide 1; verificado por querySelectorAll('.sticker').length === 0
   aquí dentro). Era CSS muerto de una versión anterior del markup: código
   vestigial, nunca limpiado. Retirado por completo — `overflow:hidden`
   directo, sin compensación de aire falso. min-width/flex-basis (arriba)
   ya garantizan que cada slide mide EXACTAMENTE el 100% del carrusel, sin
   gap en `.carousel__track` (confirmado computado: `gap: normal` = 0) —
   la aritmética de `translateX(-i*100%)` cuadra siempre.

   S3 / CAUSA RAÍZ del solape (tercer reporte de Fabio, con captura) — no
   era el LAYOUT, era la PINTURA, y por eso los dos intentos anteriores no
   dieron con ello: `--shadow-b3` es una sombra SÓLIDA y opaca de
   `8px 8px 0 #5d46ef`, y el borde derecho de la `.case-terminal` coincide
   EXACTAMENTE con el borde derecho de su slide (medido en Chrome:
   termRight === slideRight en los tres). Con `overflow: visible` en el
   slide, esos 8px de sombra morada se pintaban DENTRO del slide vecino,
   justo encima del arranque de su texto: la barra morada de la captura.
   El `overflow:hidden` del carrusel no lo tapaba porque el sangrado
   ocurre dentro de su caja, no fuera. Y ninguna sonda de geometría podía
   verlo: `getBoundingClientRect()` NO incluye la box-shadow.
   Arreglo en dos piezas:
   (1) `padding-right`/`padding-bottom` de --shadow-b-offset en el slide:
       la tarjeta deja de tocar el borde y su sombra cae DENTRO de su
       propio slide (de paso, la sombra vuelve a verse: hasta ahora la del
       slide activo caía fuera del carrusel y la recortaba su overflow).
   (2) `contain: paint` en el slide: cinturón definitivo — recorta a la
       caja de padding CUALQUIER pintura de sus descendientes (sombras
       incluidas), venga de donde venga. Se prefiere a `overflow:hidden`
       porque no crea contenedor de scroll (un enlace enfocado dentro de
       un slide oculto podría desplazarlo).
   Complemento en motion.js §11: los slides no activos pasan a
   `visibility:hidden` al acabar la transición (además de a11y: sus
   enlaces eran tabulables). */
.carousel {
  position: relative; overflow: hidden;
}
.carousel__track {
  display: flex; align-items: stretch; /* altura = la del slide más alto */
  transition: transform var(--t-reveal) var(--ease-out);
  /* R1-b (Fabio smoke): permite el arrastre horizontal con el dedo. Sin
     esto, el navegador se queda el gesto como scroll y nunca llega el
     pointerup al JS del swipe (el pan vertical de pagina sigue funcionando). */
  touch-action: pan-y;
}
.carousel__slide {
  flex: 0 0 100%; min-width: 100%;
  /* hueco para que la sombra sólida de la tarjeta caiga dentro del slide */
  padding: 0 var(--shadow-b-offset) var(--shadow-b-offset) 0;
  contain: paint; /* ningún descendiente puede pintar fuera de su slide */
}
.carousel__slide .case { min-width: 0; }
/* Controles al PIE, flanqueando los dots (S3-R1, feedback Fabio: "pon las
   flechas por fuera, o que solo se muestren al hacer hover; ahora mismo no
   permiten ver parte del texto").
   Por qué al pie y no las dos opciones literales: "por fuera" del wrap no
   cabe (a 1213px de viewport solo hay ~16px de margen fuera del wrap de
   1180, y el botón mide 40); y solo-en-hover deja las flechas invisibles
   en táctil, donde no hay hover. Al pie no tapan NUNCA (ni en móvil, donde
   el .case se apila y los botones caían a media altura sobre el texto),
   se ven siempre y siguen siendo enfocables con teclado.
   motion.js §11 los mete en un `.carousel__nav` junto a los dots. */
.carousel__nav {
  display: flex; align-items: center; justify-content: center; gap: var(--s-4);
  margin-top: var(--s-5);
}
.carousel__btn {
  width: 40px; height: 40px; display: grid; place-items: center; flex: none;
  background: var(--bg-surface); color: var(--ink);
  border: 1.5px solid var(--ink); border-radius: var(--r-sm);
  box-shadow: var(--shadow-b1);
  font-family: var(--font-disp); font-size: 1.3rem; font-weight: 700;
  line-height: 1; cursor: pointer;
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
.carousel__btn:hover { box-shadow: var(--shadow-b3); transform: translate(-2px, -2px); }
.carousel__dots {
  display: flex; justify-content: center; gap: var(--s-2);
}
.carousel__dot {
  /* "Alargado" vía scaleX (no width): guardarraíl solo transform/opacity.
     Caja siempre 22×8, en reposo escalada en X al tamaño de un punto. */
  width: 22px; height: 8px; padding: 0; border-radius: var(--r-pill);
  background: var(--c-white); border: 1.5px solid var(--ink); cursor: pointer;
  transform: scaleX(.364); transform-origin: center;
  transition: transform var(--t-base) var(--ease-pop), background var(--t-base), border-color var(--t-base);
}
.carousel__dot.is-active { transform: scaleX(1); background: var(--accent); border-color: var(--accent); } /* activo = morado alargado */
@media (prefers-reduced-motion: reduce) {
  .carousel__track { transition: none; }
}

/* =====================================================================
   MINI-VIDEO (R1-A) — [data-minivideo] con <video muted> ya en el HTML.
   Overlay de controles propios generado por motion.js §12: sin control
   de volumen (siempre muted).
   ===================================================================== */
.mv { position: relative; border-radius: var(--r-sm); overflow: hidden; }
.mv video { width: 100%; display: block; }
.mv__toggle {
  position: absolute; left: var(--s-3); bottom: var(--s-3); z-index: 2;
  width: 36px; height: 36px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--c-navy) 85%, transparent);
  color: var(--c-white); border: 0; border-radius: 50%; cursor: pointer;
}
.mv__toggle svg { display: block; }
.mv__bar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  height: 4px; background: color-mix(in srgb, var(--c-white) 25%, transparent);
  cursor: pointer;
}
.mv__fill {
  height: 100%; width: 100%; transform: scaleX(0); transform-origin: left;
  background: var(--grad-progress); /* gradiente azul→morado */
}

/* =====================================================================
   FAQ — cuadrícula expandible (R3, rediseño pedido por Fabio). Cards en
   grid de 2 columnas con índice mono `01_`; sigue usando <details><summary>
   por debajo (a11y/teclado gratis) — la card entera es lo que "se expande"
   visualmente, no solo el texto. La abierta gana sombra morada + fondo
   blanco + borde marcado. 1 columna en móvil (ver pages.css).
   ===================================================================== */
.faq { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.faq details {
  align-self: start;
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  transition: box-shadow var(--t-base), transform var(--t-base), border-color var(--t-base), background var(--t-base);
}
.faq summary {
  cursor: pointer; font-family: var(--font-disp); font-weight: 600;
  font-size: 1.05rem; padding: 24px 26px; list-style: none;
  display: flex; gap: var(--s-3); align-items: baseline;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary i {
  font-family: var(--font-mono); font-style: normal; font-size: .8rem; color: var(--accent); flex: none;
}
.faq summary::after {
  content: "+"; margin-left: auto; padding-left: var(--s-3);
  font-family: var(--font-mono); font-weight: 700; color: var(--ink-soft); flex: none;
}
.faq details[open] summary::after { content: "\2212"; color: var(--accent); }
.faq details p { padding: 0 26px 24px 26px; font-size: .9375rem; }
.faq details[open] {
  background: var(--bg-surface); box-shadow: var(--shadow-b3);
  transform: translate(-2px, -2px); border-color: var(--ink);
}

/* =====================================================================
   FORMULARIO — base de inputs + funnel full-screen /diagnostico
   Regla fija (feedback Fabio): un input por fila, ancho completo.
   ===================================================================== */
.field { display: block; width: 100%; margin-bottom: var(--s-4); }
.field label {
  display: block; font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-body); margin-bottom: 6px;
}
.input {
  width: 100%; font-family: var(--font-body); font-size: var(--fs-body);
  color: var(--ink); background: var(--bg-surface);
  border: 1px solid var(--c-border); border-radius: var(--r-sm);
  padding: 13px 14px;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.input:focus { outline: none; border-color: var(--accent-2); box-shadow: var(--ring); }
.input[aria-invalid="true"] { border-color: var(--danger); }
.field .err { font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--danger); margin-top: 5px; }

/* Funnel full-screen: una pregunta por pantalla + progreso gradiente.
   R1 Tarea U7 (feedback Fabio 2026-07-16): sin barra global de
   scroll-progress en esta página (header.php la omite si $isDiagnostico),
   todo cabe en pantalla sin scroll DE PÁGINA y "atrás" queda fijo y
   siempre visible.
   `height` (no solo `min-height`): OJO, esta es la pieza que de verdad
   arregla el bug de Fabio, no solo `.fs__step{overflow-y:auto}` — un
   `min-height` es un SUELO, nunca un TECHO: con solo `min-height`, un
   contenedor flex sin `height` propio se sigue estirando a lo que pida
   el contenido en cuanto ese contenido supera el suelo (comprobado: con
   `min-height` a secas, el paso de contacto, más largo, empujaba `.fs`
   8px por encima del viewport pese a `.fs__step{min-height:0;
   overflow-y:auto}` — ese overflow nunca llegaba a activarse porque
   `.fs` no tenía un techo rígido contra el que recortar). Con `height`
   fijo, `.fs` YA NO puede crecer con el contenido: el paso que no quepa
   hace scroll DENTRO de sí mismo (`.fs__step{min-height:0;
   overflow-y:auto}`) en vez de estirar la página.
   `--fs-topbar-h`: `.topbar--minimal` (el "← salir" de esta página, ver
   header.php) es `position:sticky`, así que SIGUE ocupando su alto en el
   flujo normal — sin este descuento la página entera quedaba ~65px más
   alta que el viewport SIEMPRE, incluso con los pasos vacíos (la otra
   mitad de la causa raíz del scroll que señalaba Fabio). El valor sale
   del propio box model del topbar (`.topbar__inner` con
   `padding: var(--s-4) 0` + logo `height:32px` + `border-bottom:1px`,
   ver pages.css) para no repetir un píxel mágico sin origen. `.fs` es
   exclusivo de esta página (design-system.md), así que este ajuste no
   toca ninguna otra. */
.fs {
  --fs-topbar-h: calc(32px + var(--s-4) * 2 + 1px);
  height: calc(100dvh - var(--fs-topbar-h));
  display: flex; flex-direction: column; background: var(--bg-page);
}
/* Cabecera compacta del funnel (R2-T5, restyle "estilo overlay": feedback
   Fabio "me gusta bastante el estilo de formularios que has hecho, con
   algunas frases e indicaciones por paso, con el nombre del servicio
   arriba a la izquierda"): sticker + título corto del contexto a la
   izquierda, progreso mono a la derecha — mismo lenguaje visual que
   `.lp-ovl__head` del overlay de demo (más abajo en este archivo), con
   clases PROPIAS `.fs__head*` (no se reutiliza `.lp-ovl__*` aquí ni al
   revés: mismo criterio de desacoplo ya documentado para ese overlay).
   Vive DENTRO de `.fs` (que ya es `display:flex;flex-direction:column` con
   `height` fija — ver comentario arriba), como hijo `flex:none` ANTES de
   `.fs__progress`: la altura que ocupa la resta sola del `flex:1` de
   `.fs__step`, sin tocar el cálculo de `--fs-topbar-h` (ese descuenta el
   topbar EXTERNO `.topbar--minimal`, sticky y fuera de `.fs`; esto es
   interno y el layout flex ya lo absorbe). */
.fs__head {
  flex: none; display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--gutter);
  border-bottom: 1px solid var(--c-hairline);
}
.fs__head-id { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s-3); }
.fs__head-title {
  font-family: var(--font-disp); font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fs__head-progress {
  flex: none; font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin: 0;
}
.fs__progress { height: 4px; background: var(--c-hairline); flex: none; }
.fs__progress i {
  display: block; height: 100%; background: var(--fs-progress);
  width: var(--p, 25%); transition: width var(--t-reveal) var(--ease-out);
}
.fs__step {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; padding: var(--s-6) var(--gutter);
}
/* [hidden] empata en ORIGEN con .fs__step (fija `display` como regla de
   autor, que gana siempre al `display:none` del user-agent para [hidden],
   sea cual sea la especificidad — mismo caso ya resuelto en este archivo
   para .cookie-banner/.cookie-panel, ver más abajo). Sin esto, funnel.js
   podría marcar `hidden` en los pasos y `.fs__step` seguiría pintándose
   igual: los 6 pasos se verían apilados a la vez. `.fs__nav[hidden]`
   entra en la misma regla por explicitud, aunque desde que es
   `position:fixed` (ver más abajo) ya no autoría ningún `display` propio
   que pueda pisar el `[hidden]` del user-agent. */
.fs__step[hidden], .fs__nav[hidden] { display: none; }
/* Centrado que sobrevive al overflow: `margin:auto` en el hijo único en
   vez de `place-items`/`justify-content:center` en `.fs__step` — con
   alignment en el padre, un contenido más alto que el hueco disponible
   deja su INICIO inalcanzable por scroll en la mayoría de motores (bug
   conocido de centrado + overflow en flex/grid). Con auto-margins en el
   hijo, lo que no cabe se pega arriba y el `overflow-y:auto` de
   `.fs__step` lo deja alcanzable entero — nada queda inaccesible. */
.fs__q { width: min(640px, 100%); margin: auto; }
.fs__q .tag { display: block; margin-bottom: var(--s-3); }
/* h1 en el paso 1, h2 en el resto (un solo <h1>/página): mismo estilo
   visual para ambos niveles de heading (Tarea 6.2). */
.fs__q h1, .fs__q h2 { font-size: var(--fs-h2); margin-bottom: var(--s-5); }
/* Frase de apoyo por paso (R2-T5, la pieza que Fabio señaló del overlay:
   "algunas frases e indicaciones por paso"): una línea corta bajo el
   enunciado, antes de las opciones/campos. Margen superior negativo (en
   vez de reducir el `margin-bottom` del h1/h2, que también usa el paso 6
   sin frase de apoyo) para acercarla al titular sin tocar esa regla
   compartida. */
.fs__hint {
  font-family: var(--font-body); font-size: var(--fs-sm); color: var(--ink-soft);
  margin: calc(-1 * (var(--s-5) - var(--s-3))) 0 var(--s-5);
}
.fs__opts { display: grid; gap: var(--s-3); }
.fs__opt {
  display: flex; align-items: center; gap: var(--s-4);
  width: 100%; text-align: left;
  background: var(--bg-surface); border: 1px solid var(--c-border);
  border-radius: var(--r-sm); padding: 16px 18px;
  font-family: var(--font-body); font-size: var(--fs-body); color: var(--ink);
  cursor: pointer;
  transition: transform var(--t-fast) var(--ease-pop), box-shadow var(--t-fast) var(--ease-pop), border-color var(--t-fast);
}
.fs__opt .key {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  color: var(--accent); border: 1px solid var(--c-border); border-radius: var(--r-xs);
  width: 26px; height: 26px; display: grid; place-items: center; flex: none;
}
/* R1 U7.4 (feedback Fabio "paso 1 con servicios claros"): título del
   servicio + subtítulo de dolor, un bloque de texto por opción (antes
   una frase suelta). `.fs__opt-text` es OBLIGATORIO como envoltorio: sin
   él, `<b>` y `<small>` son hijos flex directos de `.fs__opt` (fila) y
   quedan uno AL LADO del otro (cada uno con su propia columna estrecha
   compitiendo con el badge `.key`) en vez de apilados — bug real
   detectado en verificación (título partiéndose en 2-3 líneas raras a
   360-390px de ancho). `.fs__opt-text{flex:1;min-width:0}` lo convierte
   en EL único hijo flexible de la fila (el `min-width:0` es necesario
   para que un título largo pueda envolver dentro de su columna en vez de
   forzar el ancho del botón); dentro de él, `<b>`/`<small>` sí son
   bloques normales y se apilan. `<b>` en tipografía de display para que
   pese como un título de verdad; `<small>` apagado debajo. */
.fs__opt-text { flex: 1; min-width: 0; }
.fs__opt b {
  display: block; font-family: var(--font-disp); font-weight: 600;
  color: var(--ink); line-height: var(--lh-head);
}
.fs__opt small {
  display: block; font-family: var(--font-body); font-weight: 400;
  font-size: var(--fs-sm); color: var(--ink-soft); margin-top: 2px;
  /* `.fs__opt` no fija line-height propio y heredaría `--lh-body` (1.65,
     pensado para párrafos largos) — excesivo para una microcopy de 1-2
     líneas y el mayor contribuyente al alto de la opción en pantallas
     estrechas (títulos largos + subtítulo ya envuelven de por sí). */
  line-height: 1.35;
}
.fs__opt:hover, .fs__opt[aria-checked="true"], .fs__opt[aria-pressed="true"] {
  border-color: var(--ink); transform: translate(-2px, -2px);
  box-shadow: 5px 5px 0 var(--c-pop);
}
/* R2-T5: detalle de `.lp-ovl__opt-arrow` calcado con un pseudo-elemento
   (SOLO CSS, sin tocar el marcado que funnel.js espera ni renombrar
   clases) — mismo hint visual de "esto avanza solo" en ambos formularios.
   `.fs__opt` ya es `display:flex`, así que `::after` entra como un hijo
   flex más, igual que `.lp-ovl__opt-arrow` dentro de `.lp-ovl__opt-card`. */
.fs__opt::after {
  content: "\2192"; flex: none; font-family: var(--font-mono); color: var(--ink-soft);
  opacity: 0; transform: translateX(-4px);
  transition: opacity var(--t-fast), transform var(--t-fast);
}
.fs__opt:hover::after, .fs__opt[aria-pressed="true"]::after {
  opacity: 1; transform: translateX(0);
}
/* R1 U7.3 (feedback Fabio "atrás siempre visible"): fijo en la esquina
   inferior izquierda del VIEWPORT (no del paso), fuera del flujo — así
   no compite por alto con el contenido del paso y sigue alcanzable pase
   lo que pase con el overflow interno de arriba. `--z-nav` (mismo nivel
   que el topbar sticky) a propósito por DEBAJO de `--z-overlay` (banner
   de cookies): mismo criterio que ya usa `.lp-sticky` con ese mismo
   banner (z-index:60 < --z-overlay:80) — si algún día coinciden en
   pantalla en un móvil bajo, gana el aviso legal, nunca al revés. */
.fs__nav {
  position: fixed; left: var(--s-4);
  bottom: calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-nav);
}
.fs__back {
  font-family: var(--font-mono); font-size: var(--fs-mono-md);
  background: var(--bg-page); border: 1px solid var(--c-hairline);
  border-radius: var(--r-sm); color: var(--ink-soft); cursor: pointer;
  padding: var(--s-3) var(--s-4); min-height: 44px;
  display: inline-flex; align-items: center;
}
.fs__back:hover { color: var(--ink); border-color: var(--c-border); }

/* Checkbox RGPD del paso de contacto — un `.field` más (un input por
   fila se mantiene: el checkbox + su texto son la fila). Reutilizable por
   /contacto cuando exista. */
.check-row {
  display: flex; align-items: flex-start; gap: var(--s-3);
  font-size: var(--fs-sm); color: var(--ink-body); cursor: pointer;
}
.check-row input[type="checkbox"] {
  width: 20px; height: 20px; flex: none; margin-top: 2px;
  accent-color: var(--accent-2);
}
.check-row a { color: var(--accent-2); font-weight: 600; }

/* Honeypot anti-bot: mismo campo `website` que ya usan api/lead.php y las
   landings de 04_LandingPages. Oculto a personas vía CSS; el HTML añade
   tabindex="-1" (fuera del tabulado) y aria-hidden (fuera del árbol de
   accesibilidad) en el contenedor. */
.hp { position: absolute; left: -9999px; top: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Paso ancho (salida reserva: el iframe de Outlook Bookings necesita más
   aire que una pregunta de una línea). */
.fs__q--wide { width: min(880px, 100%); }

/* Embed de Outlook Bookings (decisión 22 del brief) — se carga
   perezosamente (data-src) solo al llegar a este paso, ver funnel.js.
   R1 U7.2 (feedback Fabio "sin scroll"): 640px de alto base ya no cabía
   sin scroll de página en 1280×800/390×844 junto al resto del paso
   (tag+h2+p+acciones) — bajado a 420px (sigue siendo un calendario
   usable) y compactado aún más bajo `max-height:700px`. */
.fs__booking {
  width: 100%; min-height: 420px; margin: var(--s-4) 0;
  border: 1px solid var(--c-border); border-radius: var(--r-md);
  overflow: hidden; background: var(--bg-calm);
}
.fs__booking iframe { width: 100%; height: 100%; min-height: 420px; border: 0; display: block; }
/* Respaldo visible del calendario (R2-T5, ver diagnostico.php: Bookings
   solo se deja embeber desde HTTPS — dev bloquea SIEMPRE, prod funciona).
   Microcopy mono + botón `.btn--ghost` (variante hueca, misma jerarquía
   visual que un CTA secundario en cualquier otra parte del sitio) — antes
   era un enlace de texto discreto DENTRO de `.fs__booking-actions`, ahora
   vive en su propio bloque, más visible, justo encima del CTA primario
   "He terminado". */
.fs__booking-fallback { margin-bottom: var(--s-4); }
.fs__booking-fallback p {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin: 0 0 var(--s-3);
}
.fs__booking-actions {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-4);
}

/* R1 U7.2 (feedback Fabio "todo cabe en pantalla sin scroll"): compacta
   paddings/tipografía del funnel en viewports bajos (portátiles pequeños,
   móvil en horizontal, y el combo estrecho+bajo de gama de entrada tipo
   360×740). `.fs__step` ya contiene su overflow en TODAS las alturas
   (regla base de arriba); esta media query solo reduce cuánto contenido
   hace falta encoger para que, en los viewports objetivo (1280×800,
   390×844 y similares), quepa sin necesitar ese scroll interno. 760px
   (no 700) para cubrir también 740 de alto sin tocar 768/800, que ya
   caben con los valores base. Los targets táctiles de `.fs__opt` se
   quedan ≥44px incluso compactados (título+subtítulo de dos líneas ya
   superan ese alto). */
@media (max-height: 760px) {
  .fs__step { padding: var(--s-4) var(--gutter); }
  .fs__q .tag { margin-bottom: var(--s-2); }
  .fs__q h1, .fs__q h2 { font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.1rem); margin-bottom: var(--s-3); }
  .fs__hint { margin: calc(-1 * var(--s-1)) 0 var(--s-3); }
  .fs__opts { gap: var(--s-2); }
  .fs__opt { padding: 11px 14px; gap: var(--s-3); }
  .fs__opt b { font-size: .9375rem; }
  .fs__opt small { font-size: .8125rem; }
  .fs__booking { min-height: 300px; margin: var(--s-3) 0; }
  .fs__booking iframe { min-height: 300px; }
}

/* Micro-transición al cambiar de paso (solo transform/opacity, guardarraíl
   del brief). prefers-reduced-motion ya la neutraliza vía la regla global
   de tokens.css (fuerza animation-duration:.01ms en todo el sitio), la
   media query de abajo es cinturón y tirantes. */
@keyframes fsStepIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.fs__q.is-entering { animation: fsStepIn var(--t-reveal) var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .fs__q.is-entering { animation: none; }
}

/* Sin JS, el funnel no funciona (los botones no tienen handler nativo):
   se oculta entero y solo queda visible el <noscript> de diagnostico.php.
   header.php añade la clase .js a <html> con un script síncrono; sin JS
   esa clase nunca llega y esta regla se activa. */
html:not(.js) .fs { display: none; }

/* =====================================================================
   CALCULADORA DRAMÁTICA (R2-A, fuego/humo retirados en R3) —
   `.calc-card[data-heat="0|1|2|3"]`. motion.js §15 (initCalcHeat) añade
   la clase `.calc-card` a cualquier [data-calc]; la página solo pone/
   actualiza `data-heat` según el resultado. heat 0 = estado actual
   (hairline + sombra suave), heat 1 ámbar, heat 2 rojo + shake sutil,
   heat 3 rojo + shake MÁS fuerte (±3px, feedback Fabio: fuera las llamas
   y el humo — quedaban demasiado literales; el nivel 3 se siente solo con
   color + vibración). Solo transform/opacity en las animaciones (guardarraíl).
   ===================================================================== */
.calc-card {
  transition: border-color var(--t-base), box-shadow var(--t-base);
  position: relative;
}

.calc-card[data-heat="1"] {
  --heat-c: color-mix(in srgb, var(--hl) 60%, var(--danger));
  border-color: var(--heat-c);
  box-shadow: 0 0 0 1px var(--heat-c), 0 20px 44px -26px var(--heat-c);
}
.calc-card[data-heat="1"] .calc__result-value { color: var(--heat-c); }

.calc-card[data-heat="2"],
.calc-card[data-heat="3"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 1.5px var(--danger), 0 22px 48px -20px var(--danger);
}
.calc-card[data-heat="2"] .calc__result-value,
.calc-card[data-heat="3"] .calc__result-value { color: var(--danger); }
.calc-card[data-heat="2"] { animation: calcShake .12s ease-in-out infinite; }
.calc-card[data-heat="3"] {
  box-shadow: 0 0 0 2px var(--danger), 0 26px 54px -16px var(--danger);
  animation: calcShakeHard .12s ease-in-out infinite;
}

@keyframes calcShake {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(1px); }
}
@keyframes calcShakeHard {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-3px); }
  75%      { transform: translateX(3px); }
}

@media (prefers-reduced-motion: reduce) {
  /* Reduce: los colores del nivel se quedan (no son "movimiento"), pero
     nada de shake — así lo pidió el brief explícitamente. */
  .calc-card[data-heat="2"], .calc-card[data-heat="3"] { animation: none; }
}

/* =====================================================================
   UI ESPACIAL — capas, texturas, glow, flotación (corrección Fabio #2)
   Uso dosificado: 1 textura por sección como máximo. Detalle en doc §6.
   ===================================================================== */

/* Grid de puntos (capa decorativa universal) */
.dots-bg { position: relative; }
.dots-bg::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: radial-gradient(currentColor 1px, transparent 1px);
  background-size: 22px 22px; opacity: .10;
}
.sec--inverse.dots-bg::before { opacity: .07; }

/* Pseudo-código tenue en fondos navy (feel empresa de software) */
.tex-code { position: relative; }
.tex-code::before {
  content: "const automatizar = (proceso) => {\A   if (documento.formatoIrregular) { aplicarIA(documento); }\A   schedule.everyMonday(cierreMensual);\A   return sistema.aMedida(cliente);\A }\A function generarInforme(cliente) {\A   const datos = leerExcel(cliente.archivo);\A   return render(datos).enviarPorCorreo();\A }";
  white-space: pre-wrap; position: absolute; inset: 0; padding: 40px;
  font-family: var(--font-mono); font-size: .8125rem; line-height: 2.2;
  color: rgba(153, 137, 255, .07); pointer-events: none; overflow: hidden;
}

/* Glow radial morado en esquinas de bloques navy */
.glow-tr::after, .glow-bl::after {
  content: ""; position: absolute; width: 380px; height: 380px;
  border-radius: 50%; background: var(--glow-pop); pointer-events: none;
}
.glow-tr::after { right: -120px; top: -120px; }
.glow-bl::after { left: -120px; bottom: -120px; }

/* Elementos flotando con levitación sutil (solo transform) */
.float-1, .float-2 { animation: floaty 7s var(--ease-out) infinite alternate; will-change: transform; }
.float-2 { animation-duration: 9s; animation-delay: -3s; }
@keyframes floaty { from { transform: translateY(0) rotate(var(--rot, 0deg)); }
                    to   { transform: translateY(-10px) rotate(var(--rot, 0deg)); } }

/* Capas de profundidad — motion.js les da parallax vía data-depth */
[data-depth] { will-change: transform; }

/* Halo del cursor (petición Fabio 2026-07-13, ref. mastermind.ac).
   motion.js mueve --vx/--vy en rAF (fallback -50%/-50% = fuera de flujo,
   centrado en 0,0 antes de que motion.js lo posicione). Solo desktop de
   puntero fino y sin prefers-reduced-motion. Ver design-system.md §8. */
.cursor-veil {
  display: none;
  position: fixed; inset: 0 auto auto 0;
  width: 620px; height: 620px;
  /* rgba(93,70,239,.10) equivalente a var(--c-pop) al 10% de opacidad */
  background: radial-gradient(circle, color-mix(in srgb, var(--c-pop) 10%, transparent), transparent 60%);
  transform: translate3d(var(--vx, -50%), var(--vy, -50%), 0);
  pointer-events: none;
  z-index: 1;
  will-change: transform;
}
@media (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .cursor-veil { display: block; }
}

/* Code-bg (R1-A, motion.js §9): fondo de página global, pseudo-código muy
   sutil que motion.js "reescribe" cerca del cursor. Creado por JS como
   primer hijo de <body> si no existe ya en el HTML. El fondo real de página
   vive en <html> (tokens.css) para que este canvas, con z-index negativo,
   quede siempre por encima de él. Las secciones con fondo propio (blanco,
   navy, crema) lo tapan por completo — es la textura del "hueco" entre
   secciones, nunca compite con el contenido. */
.code-bg {
  position: fixed; inset: 0; z-index: -1;
  pointer-events: none; width: 100%; height: 100%;
}

/* Iconos flotantes decorativos (R1-A). El posicionamiento absoluto (top/
   left/right/bottom) lo da la página; .fico solo resuelve tamaño/marco/trazo.
   Copiar cualquiera de estos 6 SVG de trazo propio (viewBox 24×24,
   stroke-linecap/linejoin ya vienen de la clase, no repetir aquí):

   Reloj:
   <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>

   Rayo:
   <svg viewBox="0 0 24 24"><path d="M13 2 4 14h6l-1 8 9-12h-6z"/></svg>

   Código </>:
   <svg viewBox="0 0 24 24"><path d="M8 6 3 12l5 6M16 6l5 6-5 6M14 4l-4 16"/></svg>

   Llaves {}:
   <svg viewBox="0 0 24 24"><path d="M9 3c-2 0-3 1-3 3v3c0 1-1 2-2 2 1 0 2 1 2 2v3c0 2 1 3 3 3M15 3c2 0 3 1 3 3v3c0 1 1 2 2 2-1 0-2 1-2 2v3c0 2-1 3-3 3"/></svg>

   Gráfica de barras:
   <svg viewBox="0 0 24 24"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></svg>

   Check en círculo:
   <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M8 12l3 3 5-6"/></svg>
*/
.fico {
  position: absolute; /* la página fija top/left/right/bottom concretos */
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; /* rango 40-52px */
  border: 1.5px solid var(--c-border); background: var(--bg-surface);
  border-radius: var(--r-sm); box-shadow: var(--shadow-b1);
}
.fico svg {
  width: 58%; height: 58%; fill: none; stroke: var(--accent);
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.fico--alt svg { stroke: var(--accent-2); } /* variante azul en vez de morado */

/* =====================================================================
   MOTION HOOKS — estados iniciales de reveal (motion.js añade .in)
   Sin JS: todo visible (los estados iniciales solo aplican con .js en <html>)
   ===================================================================== */
.js [data-reveal] {
  opacity: 0;
  transition: opacity var(--t-reveal) var(--ease-out) var(--reveal-delay, 0ms),
              transform var(--t-reveal) var(--ease-out) var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}
.js [data-reveal="up"]    { transform: translateY(var(--reveal-y)); }
.js [data-reveal="left"]  { transform: translateX(calc(var(--reveal-x) * -1)); }
.js [data-reveal="right"] { transform: translateX(var(--reveal-x)); }
.js [data-reveal="zoom"]  { transform: scale(.94); }
.js [data-reveal].in      { opacity: 1; transform: none; }

/* Barra de progreso de scroll (gradiente azul→morado, motion.js) */
.scroll-progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 100%;
  transform: scaleX(var(--sp, 0)); transform-origin: left;
  background: var(--grad-progress); z-index: var(--z-toast);
  pointer-events: none; will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  .float-1, .float-2 { animation: none; }
  .ticker__track { animation: none; }
  /* R4 (2026-07-23): antes anulaba la inclinación; ahora anula el
     desplazamiento horizontal (--band-x) — misma regla, sigue valiendo. */
  .band > div { transform: none; }
}

/* =====================================================================
   RESPONSIVE — apilado básico de los componentes del sistema
   ===================================================================== */
@media (max-width: 880px) {
  .sec-head, .case, .duel, .svc-cards, .product-panels, .guarantees, .guarantees--v2, .faq { grid-template-columns: 1fr; }
  .sec-head > p { padding-top: 0; }
  .sec-head { gap: var(--s-4); margin-bottom: var(--s-6); }
  .timeline { grid-template-columns: 1fr 1fr; gap: var(--s-5); }
  /* KPIs compactos en móvil (feedback Fabio): una sola fila de 3 columnas,
     número arriba y descripción pequeña debajo. Baja a ~1/3 del alto y lee
     como una banda de datos, en línea con el ticker de debajo. */
  .kpi-strip .wrap { grid-template-columns: repeat(3, 1fr); }
  .kpi-strip .kpi {
    flex-direction: column; align-items: center; text-align: center;
    gap: 3px; padding: var(--s-4) var(--s-2);
    border-right: 1px solid var(--card-border); border-bottom: 0;
  }
  .kpi-strip .kpi:last-child { border-right: 0; }
  .kpi-strip .kpi b { font-size: clamp(1.2rem, 5.2vw, 1.6rem); }
  .kpi-strip .kpi b .u { font-size: .5em; }
  .kpi-strip .kpi small { font-size: .625rem; line-height: 1.25; max-width: 16ch; }
  .svc-sheet__row { grid-template-columns: 1fr; gap: var(--s-3); }
  .svc-sheet__row .go { text-align: left; padding-top: 0; }
  .duel .vs { display: none; }
  /* R2 (ROADMAP p.2): el brush del H1 nace con white-space:nowrap; en movil
     una frase de varias palabras no cabe y se sale. Se deja romper.
     R1-c (Fabio smoke): con el texto en varias lineas la barra absoluta
     ::after solo subrayaba la ultima linea y salia descuadrada. En movil el
     subrayado se pinta como FONDO por-linea (background + box-decoration-break
     clone) que sigue cada renglon; se apaga el ::after (no vale multilinea). */
  .brush {
    white-space: normal;
    background-image: linear-gradient(var(--hl), var(--hl));
    background-repeat: no-repeat;
    background-position: 0 92%;
    background-size: 100% .2em;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
  }
  .brush::after { display: none; }
  /* H5 (pedido Fabio): en movil los 3 KPIs de la .case-terminal (carrusel
     de casos de la home) pasaban apilados en vertical con mucho padding;
     la tarjeta de caso quedaba muy alta y el nav del carrusel (flechas
     40x40 + dots) caia muy por debajo. Se convierten en una fila de 3
     columnas compactas: la tarjeta baja de alto y las flechas quedan
     cerca. Solo movil, el grid 2fr/1fr de escritorio no se toca. */
  .case-terminal__body{ display:grid; grid-template-columns:repeat(3, 1fr); gap:var(--s-3); padding:var(--s-4); }
  .case-terminal__line{ flex-direction:column; align-items:center; text-align:center; gap:2px; padding:0; border-bottom:0; }
  .case-terminal__prefix{ display:none; }
  .case-terminal__line b{ font-size:1.35rem; }
  .case-terminal__line small{ margin-top:2px; }
}
@media (max-width: 560px) {
  .timeline { grid-template-columns: 1fr; }
}

/* ==== LANDINGS FAMILIA A (sesión 4) ==== */

/* Topbar en modo anuncio (?src=ads o utm_source, ver includes/lp.php):
   logo sin enlace + único CTA. Hereda position:sticky/fondo/borde de
   .topbar y el flex space-between de .topbar__inner; solo se refuerza
   que el logo no encoja si el CTA no cabe en una fila estrecha. */
.topbar--lp .logo { flex: none; }
/* Nav de anclas (R3, corrección Fabio "tiene que tener botones que
   permitan navegar a lo largo de la landing"): mono pequeño, sin
   subrayado permanente, hover a --accent-2 (mismo criterio de
   "interacción" que el resto del sistema). .topbar__inner hereda
   display:flex + justify-content:space-between de .topbar (pages.css):
   con 3 hijos (logo/nav/CTA) reparte igual los 2 huecos, la nav queda
   centrada entre logo y CTA sin flex-grow propio. */
.topbar--lp__nav{ display:flex; align-items:center; gap:var(--s-5); }
.topbar--lp__nav a{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  color:var(--ink-body); text-decoration:none; white-space:nowrap;
  transition:color var(--t-fast);
}
.topbar--lp__nav a:hover{ color:var(--accent-2); }
/* Móvil: logo + CTA en UNA fila compacta, nav de anclas OCULTA (R3: no
   cabe en la fila compacta y el sticky CTA ya cubre la conversión). Sin
   el flex-wrap:nowrap de abajo, el wrap del .topbar__inner apilaba logo
   y botón en columna y la cabecera sticky se comía ~230px de un
   viewport de 844 (visto en QA S4, captura a1-mov-ads-sticky). El 24px
   del logo es tamaño de componente, mismo criterio que el 32px inline
   que ya fija header.php en escritorio. */
@media (max-width:880px){
  .topbar--lp .topbar__inner{ flex-wrap:nowrap; justify-content:space-between; gap:var(--s-3); }
  .topbar--lp .logo img{ height:24px !important; }
  .topbar--lp .btn{ white-space:nowrap; padding:var(--s-2) var(--s-4); font-size:var(--fs-sm); }
  .topbar--lp__nav{ display:none; }
  /* R2 (ROADMAP p.2): en ads el topbar es logo + CTA en una fila que no
     encoge; a <=360 el CTA se salia ~14px (mas a 320). Se recorta el padding
     lateral del inner y del boton y se permite que el boton ceda (min-width:0)
     para que la fila quepa sin scroll. Copy intacto. */
  .topbar--lp .topbar__inner{ padding-left: var(--s-4); padding-right: var(--s-4); }
  .topbar--lp .btn{ padding-left: var(--s-3); padding-right: var(--s-3); min-width: 0; }
}

/* Objetivos de ancla de la nav de arriba: compensan el topbar sticky
   (R3). Alto real del topbar = logo 32px + padding var(--s-4)*2 (32) +
   border-bottom 1px = 65px (mismo box model que --fs-topbar-h, arriba);
   + var(--s-6) de aire para que el título no quede pegado al borde.
   #lp-calc lo pinta includes/lp-calc.php; los otros 3 IDs los ponen
   blindar-excel.php/migrar-excel-a-web.php directamente en la sección. */
#para-quien, #lp-calc, #como-funciona, #faq{ scroll-margin-top: calc(65px + var(--s-6)); }

/* Footer en modo anuncio: mínimo legal, sin columnas de navegación, RRSS
   ni CTA. Hereda fondo navy-deep / color inverso de .footer. */
.footer--lp .wrap {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--s-4);
  padding-top: var(--s-6); padding-bottom: var(--s-6);
}
.footer--lp p {
  color: var(--ink-inverse-2); font-size: var(--fs-sm); line-height: var(--lh-body);
}
.footer--lp p a { color: var(--ink-inverse-2); text-decoration: underline; }
.footer--lp p a:hover { color: var(--ink-inverse); }
.footer--lp__copy {
  display: block; font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-inverse-2);
}

/* Sticky CTA móvil de landing: solo ≤880px, aparece con scroll (landing.js
   alterna [hidden]). Patrón del análisis 2026-07-16: +11% conversión, el
   móvil es el 81% del tráfico del sector. */
.lp-sticky{
  position:fixed; left:0; right:0; bottom:0; z-index:60;
  padding:var(--s-3) var(--s-4);
  padding-bottom:calc(var(--s-3) + env(safe-area-inset-bottom, 0px));
  background:var(--bg-surface);
  border-top:1px solid var(--card-border);
  box-shadow:0 -6px 24px rgba(10,31,68,.12);
  display:none;
}
.lp-sticky .btn{width:100%; text-align:center}
@media (max-width:880px){ .lp-sticky:not([hidden]){display:block} }
/* G4 · texto rotativo del CTA sticky (landing.js, initLpStickyRotate): el
   label funde a opacidad 0 (.is-swapping) para cambiar de mensaje. Sin JS /
   reduce se queda estático con el texto server-render (el CTA real). */
.lp-sticky__label{ display:inline-block; transition:opacity var(--t-fast); }
.lp-sticky__label.is-swapping{ opacity:0; }
@media (prefers-reduced-motion: reduce){ .lp-sticky__label{ transition:none; } }

/* Tarea 2 (blindar-excel.php, landing A1) — KPI strip de 4 valores: la de
   la home es fija a 3 columnas (.kpi-strip .wrap), así que este modificador
   sube a 4 SOLO en escritorio. Guardado tras un min-width para no competir
   en especificidad con el colapso a 1 columna de ≤880px (misma regla,
   mismo archivo): sin el guard, al estar más abajo en el archivo ganaría
   también en móvil y rompería el apilado. */
@media (min-width:881px){
  .lp-kpis .wrap{ grid-template-columns:repeat(4, 1fr); }
}

/* Founder de landing: mismo patrón que .home-founder/.nos-founder (grid
   280px/1fr), nombre propio para que A2 (Tarea 3) lo reutilice sin CSS
   nuevo. .founder/.founder-legal/.founder-linkedin ya son genéricos. */
.lp-founder{ display:grid; grid-template-columns:280px 1fr; gap:var(--s-8); align-items:start; }
.lp-founder__text h2{ margin-bottom:var(--s-4); }
.lp-founder__text p{ max-width:34rem; margin-bottom:var(--s-4); }
.lp-founder__text p:last-child{ margin-bottom:0; }
@media (max-width:880px){ .lp-founder{ grid-template-columns:1fr; } }

/* R1 (ROADMAP p.1): la firma legal + LinkedIn de la home se replica en las 6
   páginas con .lp-founder. Como esas van sobre sec--inverse (navy) y
   .founder-linkedin nace en --accent (morado), aquí se retinta a --c-lila,
   que es el lila claro que el sistema ya usa para enlaces sobre navy
   (mismo criterio que .footer .whatsapp-link svg en pages.css:39). Las
   clases .founder-legal / .founder-linkedin NO están scoped a la home:
   se pegan tal cual dentro de .lp-founder__text sin CSS nuevo de layout. */
.sec--inverse .founder-legal { color: var(--ink-inverse-2); }
.sec--inverse .founder-linkedin { color: var(--c-lila); }
.sec--inverse .founder-linkedin:hover { color: var(--ink-inverse); }

/* Garantías de pago (3 .inc-card en fila, acento --wc-accent por tarjeta):
   mismo patrón que .nos-principios sobre .inc-grid/.inc-card (pages.css),
   con nombre propio de landing. .inc-grid ya colapsa a 1fr en ≤880px
   (pages.css); el guard min-width evita competir con esa regla en móvil. */
@media (min-width:881px){
  /* .inc-grid.lp-guarantees (0,2,0) a propósito: la regla base .inc-grid
     de pages.css carga DESPUÉS de components.css y con .lp-guarantees a
     secas (misma especificidad) ganaría el 2-col por orden de hoja. */
  .inc-grid.lp-guarantees{ grid-template-columns:repeat(3, 1fr); }
}
.lp-guarantees .inc-card{ border-top:4px solid var(--wc-accent, var(--accent)); }
.lp-guarantees .inc-card:hover{ box-shadow:5px 5px 0 var(--wc-accent, var(--accent)); }

/* Calculadora de landing — card base + piezas que sigue reutilizando el
   rediseño "hoja de Excel" (R1 Tarea U3, más abajo): fondo/borde/sombra
   de reposo, tratamiento del slider (mismo trato visual que .calc__slider
   del home, pero sobre el selector genérico input[type=range] porque el
   HTML de lp-calc.php no lleva esa clase) y el output/frase/CTA que no
   cambian de forma con el rediseño. El tamaño (max-width/padding) y el
   layout de campos SÍ cambiaron en U3: ver `.lp-calc.lp-calc--sheet` en
   la sección LANDINGS FAMILIA A — Tarea U3 (las reglas de ahí ganan por
   selector compuesto, mismo criterio que `.svc-hero.lp-hero`). */
.lp-calc{
  background:var(--bg-surface); border:1px solid var(--card-border);
  border-radius:var(--r-lg); box-shadow:var(--shadow-b2);
  padding:var(--s-7); max-width:38rem; margin:0 auto;
}
.lp-calc input[type="range"]{
  appearance:none; -webkit-appearance:none; width:100%; height:2px;
  background:var(--c-hairline); border-radius:2px; cursor:pointer;
  accent-color:var(--accent);
}
.lp-calc input[type="range"]::-webkit-slider-thumb{
  appearance:none; -webkit-appearance:none;
  width:20px; height:20px; border-radius:4px;
  background:var(--accent); border:2px solid var(--ink); box-shadow:var(--shadow-b1);
  cursor:pointer; transition:transform var(--t-fast) var(--ease-pop);
}
.lp-calc input[type="range"]::-webkit-slider-thumb:hover{ transform:scale(1.1); }
.lp-calc input[type="range"]::-moz-range-track{ background:var(--c-hairline); height:2px; border-radius:2px; }
.lp-calc input[type="range"]::-moz-range-thumb{
  width:18px; height:18px; border-radius:4px;
  background:var(--accent); border:2px solid var(--ink); box-shadow:var(--shadow-b1); cursor:pointer;
}
.lp-calc input[type="range"]:focus-visible{ outline:none; }
.lp-calc input[type="range"]:focus-visible::-webkit-slider-thumb{ box-shadow:var(--shadow-b1), var(--ring); }
.lp-calc input[type="range"]:focus-visible::-moz-range-thumb{ box-shadow:var(--shadow-b1), var(--ring); }
.lp-calc output{
  display:block; margin-top:var(--s-2);
  font-family:var(--font-mono); font-size:13px; color:var(--c-mute);
}
/* .lp-calc__result (b/>span/small) de la ronda anterior RETIRADA en U3:
   el resultado vive ahora dentro de `.lp-calc--sheet__result-body`, con
   el mismo tratamiento del número grande pero --fs-display en vez de
   --fs-h2 (celda más grande, ver sección U3). */
.lp-calc__frase{
  margin-top:var(--s-4);
  font-family:var(--font-mono); font-style:italic; font-size:.9375rem; color:var(--ink-soft);
  transition:opacity var(--t-base) var(--ease-out);
  /* R2 Tarea V1.1 (fix Fabio "la calculadora debe tener alto fijo"): SOLO
     colchón — el min-height de verdad lo fija landing.js inline, en px
     exactos medidos al ancho REAL de la celda C (fixFraseHeight/initLpCalc).
     Una reserva fija en líneas no aguanta cualquier ancho: la frase más
     larga de lpTramoFor ocupa 2 líneas a 1280px, 4 a 390px y 6 a 320px —
     el nº de líneas depende del ancho de columna, que cambia de forma
     CONTINUA con el viewport, no solo en el breakpoint de layout (880px).
     Estas 4 líneas son un valor de sobra para el rango 390-1280 (cubre el
     instante antes de que el JS mida, o si llegara a fallar el JS del
     todo — aunque entonces la frase tampoco cambiaría nunca, sin JS no
     hay sliders que la muevan). Verificado por JS recorriendo los 4
     tramos: getBoundingClientRect().height de [data-lp-calc] IDÉNTICO a
     1280 y 390 con el min-height inline puesto. */
  min-height:calc(var(--lh-body) * 4em);
}
.lp-calc__frase.is-fading{ opacity:0; }
.lp-calc__cta{ margin-top:var(--s-5); text-align:center; }

/* =====================================================================
   R1 (2026-07-16) — Tarea U1: parte alta de A1 (fxbar + hero navy con
   collage + KPIs pegados). Registrado en design-system.md §4.
   ===================================================================== */

/* --- Fórmula bar decorativa (includes/lp-fxbar.php) --------------------
   Bajo el topbar, aria-hidden. Celda + fx + fórmula coloreada (función en
   --accent-2, string en --ok, resto en --ink-soft, mismo criterio de
   "función/dato" que .mock-sheet--rich) + pestañas de hoja a la derecha. */
.lp-fxbar{ background:var(--bg-calm); border-bottom:1px solid var(--card-border); }
.lp-fxbar__row{
  display:flex; align-items:center; gap:var(--s-3);
  padding-top:9px; padding-bottom:9px; /* .wrap ya da el padding horizontal */
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
}
.lp-fxbar__cell{
  flex:none; font-weight:700; color:var(--ink);
  background:var(--bg-surface); border:1px solid var(--card-border); border-radius:var(--r-xs);
  padding:2px 9px;
}
.lp-fxbar__fx{ flex:none; color:var(--ink-soft); font-style:italic; }
.lp-fxbar__formula{
  flex:1 1 auto; min-width:0; color:var(--ink-soft);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.lp-fxbar__formula .fn{ color:var(--accent-2); font-weight:700; }
.lp-fxbar__formula .str{ color:var(--ok); }
.lp-fxbar__tabs{ flex:none; display:flex; gap:2px; margin-left:auto; }
.lp-fxbar__tab{
  padding:5px 12px; color:var(--ink-soft); border-bottom:2px solid transparent;
}
.lp-fxbar__tab.is-active{
  background:var(--bg-surface); color:var(--ink);
  /* R1 Tarea U6: var(--svc-accent, var(--ok)) en vez de var(--ok) a
     secas — A1 fija --svc-accent:var(--ok) (mismo valor, cero cambio
     visual) y A2 fija --svc-accent:var(--fam-a), así la pestaña activa
     "de navegador" (app.tuempresa.com) se pinta en azul en vez de verde
     Excel: el guiño al cambio que pide U6.2. */
  border-bottom-color:var(--svc-accent, var(--ok)); border-radius:var(--r-xs) var(--r-xs) 0 0;
}
@media (max-width:880px){
  .lp-fxbar__tabs{ display:none; }
}

/* --- Hero de landing NAVY (svc-hero + lp-hero) --------------------------
   Selector COMPUESTO a propósito (0,2,0): `.svc-hero` fija su propio
   `background` en pages.css, que carga DESPUÉS de este archivo — con
   `.lp-hero` a secas (misma especificidad, 0,1,0) ganaría esa regla por
   orden de hoja y el hero se quedaría con el degradado claro. Mismo
   patrón ya usado en `.inc-grid.lp-guarantees` (más abajo). */
.svc-hero.lp-hero{
  position:relative; overflow:hidden;
  background:var(--bg-inverse); color:var(--ink-inverse);
}
.lp-hero p{ color:var(--ink-inverse-2); }
.lp-hero .micro{ color:var(--ink-inverse-2); }
.lp-hero .svc-crumbs{ color:var(--ink-inverse-2); }
.lp-hero .svc-crumbs a{ color:var(--ink-inverse-2); }
.lp-hero .svc-crumbs a:hover{ color:var(--c-lila); }
.lp-hero .svc-crumbs [aria-current]{ color:var(--ink-inverse); }
.lp-hero .svc-meta span{
  color:var(--ink-inverse-2);
  background:color-mix(in srgb, var(--ink-inverse) 10%, transparent);
  border-color:color-mix(in srgb, var(--ink-inverse) 30%, transparent);
}
.lp-hero .svc-meta b{ color:var(--c-lila); }
/* R3 (corrección Fabio): .lp-hero .svc-hero__video-note{color:var(--c-lila)}
   retirada aquí — A1 (único sitio que anidaba .svc-hero__video-note
   dentro de .lp-hero) quitó la nota "demo en vídeo (próximamente)" del
   hero. La clase BASE .svc-hero__video-note (pages.css) sigue viva: la
   usa /smartaudit (fuera de alcance de esta sesión, no se toca). */

/* --- Collage del hero (sustituye al media-frame único) ------------------
   3 piezas absolutas superpuestas con rotación suave (--rot vía transform,
   compone bien con data-cp porque motion.js mueve ese hook con la
   propiedad `translate`, independiente de `transform` — ver design-
   system.md §8). Trampa §3 (sombras sólidas sangran 8px): ninguna pieza
   toca el borde de `.svc-hero.lp-hero` (que recorta con overflow:hidden),
   todas quedan con aire de sobra, así que no hace falta reservar
   --shadow-b-offset aquí. Sombra en --shadow-b3 (morado) y no en el navy
   b1/b2 de serie: un desplazamiento navy sobre fondo navy sería invisible
   (mismo motivo por el que el CTA usa .btn--inverse en vez de .btn). */
.lp-hero__collage{ position:relative; min-height:400px; }
.lp-hero__collage .media-frame{
  position:absolute; top:0; left:0; width:62%; z-index:1;
  padding:10px; transform:rotate(-2deg);
}
.lp-hero__collage .media-frame .mock-sheet{ font-size:.625rem; }
.lp-hero__collage .media-frame .mock-sheet__fx{ font-size:.625rem; padding:5px 4px; }
.lp-hero__collage > .sticker{ position:absolute; top:-16px; left:34%; z-index:4; }

.lp-hero-card{
  position:absolute; top:24%; right:0; left:auto; bottom:auto;
  width:52%; z-index:2;
  background:var(--bg-surface); color:var(--ink);
  border:2px solid var(--ink); border-radius:var(--r-md);
  box-shadow:var(--shadow-b3); padding:var(--s-4);
  transform:rotate(2deg);
}
.lp-hero-card .fico{ position:static; margin-bottom:var(--s-3); }
.lp-hero-card b{
  display:block; font-family:var(--font-mono); font-size:.8125rem; font-weight:700;
  color:var(--ink); margin-bottom:4px; word-break:break-word;
}
.lp-hero-card small{ display:block; font-family:var(--font-mono); font-size:.6875rem; color:var(--ink-soft); }
.lp-hero-card .ok{ color:var(--ok-text); font-weight:700; }

.lp-hero-card--dash{
  top:52%; right:auto; left:2%; bottom:auto;
  width:46%; z-index:3;
  transform:rotate(-3deg);
}
.lp-hero-card__bars{ display:flex; align-items:flex-end; gap:6px; height:52px; margin-bottom:var(--s-3); }
.lp-hero-card__bars span{
  display:block; flex:1; height:var(--h,50%); background:var(--bc,var(--accent-2));
  border-radius:2px 2px 0 0;
}

@media (max-width:880px){
  .lp-hero__collage{ min-height:0; display:flex; flex-direction:column; gap:var(--s-5); padding-top:28px; }
  .lp-hero__collage .media-frame, .lp-hero-card{ position:static; width:100%; transform:none; }
  .lp-hero-card{ box-shadow:var(--shadow-soft); }
  .lp-hero__collage > .sticker{ position:absolute; top:0; left:auto; right:10px; }
}
@media (max-width:560px){
  .lp-hero-card--dash{ display:none; }
}

/* =====================================================================
   R1 (2026-07-16) — Tarea U2: A1 versus dolor/solución + sí/no con piel
   de hoja. Registrado en design-system.md §4.
   ===================================================================== */

/* --- Versus dolor/solución (.lp-versus) y Antes/después con asa
   (.lp-slider) — RETIRADOS y BORRADOS en la limpieza de R7 (2026-07-24).
   Los sustituyó `.lp-chat` (te suena v2) en las 6 landings. Si alguna vez
   hacen falta, están en el historial de git: R6/R7 de esta misma jornada.
   Con ellos se fueron `initLpVersusRows()` e `initLpSlider()` de landing.js
   y las reglas sueltas de `.page-smartaudit .lp-versus__sol …` y del
   bloque de anti-desborde móvil. */

/* --- Sí/no con piel de hoja + negritas -----------------------------
   Mismo .sino/.box/data-scrub de pages.css (Tarea 3.2): la sombra sólida
   del .box.si y el fondo neutro del .box.no NO se tocan. Selector
   COMPUESTO .sino.sino--sheet (0,2,x) a propósito, mismo motivo que
   .inc-grid.lp-guarantees (arriba): pages.css define `.sino li` (0,1,1)
   y `.sino .si/.no li::before` (0,2,2) y carga DESPUÉS de este archivo —
   con un selector de igual especificidad ganaría por orden de hoja. */
.sino.sino--sheet li{
  display:grid; grid-template-columns:34px 1fr; gap:var(--s-3);
  padding-left:0;
}
.sino.sino--sheet .si li::before,
.sino.sino--sheet .no li::before{ content:none; }
.sino.sino--sheet li i{
  font-family:var(--font-mono); font-style:normal; font-weight:700;
  font-size:var(--fs-mono-sm); padding-top:2px;
}
.sino.sino--sheet .si li i{ color:var(--ok-text); }
.sino.sino--sheet .no li i{ color:var(--danger); }

/* Cabecera tipo pestaña de hoja dentro del <h3> (verde activa en sí,
   roja en no); sin background propio a propósito: deja pasar el fondo
   de .box.si/.box.no para que la pestaña se lea "soldada" al panel,
   como una pestaña real de hoja de cálculo (mismo criterio de esquinas
   redondeadas arriba/rectas abajo que .lp-fxbar__tab.is-active). */
.lp-sino-tab{
  display:inline-flex; align-items:center;
  font-family:var(--font-mono); font-size:var(--fs-mono-md); font-weight:700;
  color:var(--ink);
  padding:8px 16px;
  border:1px solid var(--card-border); border-bottom:3px solid var(--ok-text);
  border-radius:var(--r-xs) var(--r-xs) 0 0;
}
.lp-sino-tab--no{ border-bottom-color:var(--danger); }

/* --- Sí/no como HOJA DE EXCEL real (R2 Tarea V3.1, corrección Fabio
   "que se vea como hojas de Excel, con el encabezado sí/no bien
   visible") ---------------------------------------------------------
   Bloque NUEVO en PARALELO al de arriba (.sino--sheet/.lp-sino-tab): NO
   sustituye nada in-place porque A2 sigue usando esas clases tal cual
   hasta su propia Tarea V4 — renombrar ahí habría roto A2 a mitad de
   sesión. Aquí la cabecera deja de ser una pestaña pequeña dentro del
   <h3> y pasa a SER el <h3> entero: una fila a todo el ancho (padding
   propio, el .box pierde su padding general para que la fila llegue a
   los 4 bordes) con fondo de color (--ok-soft en sí / rojo suave via
   color-mix en no, mismo 14% que .lp-calc--sheet__err.is-err, unas
   secciones más abajo, para reutilizar un contraste ya validado) y texto
   mono bold bien visible. Cada <li> pasa de "marcador ::before + texto"
   a FILA de tabla real: celda índice (i) + celda texto (span) separadas
   por un hairline VERTICAL (border-left en la celda de texto) y cada
   fila con hairline HORIZONTAL (border-top) — mismo lenguaje de rejilla
   que .lp-calc--sheet__cellA/__cellB. Selector COMPUESTO
   .sino.lp-sino-sheet (0,2/3,x), mismo motivo que .sino.sino--sheet de
   arriba: pages.css define .sino .box/li/li::before y carga DESPUÉS de
   este archivo, así que hace falta más especificidad de CLASE (no de
   tag) para ganar siempre, sin depender del orden de hoja — ver
   design-system.md §4. La sombra sólida de .box.si (pages.css,
   --shadow-b3) y el único enlace cruzado a /servicios/migrar-excel-a-web
   NO se tocan. */
.sino.lp-sino-sheet .box{ padding:0; overflow:hidden; }
.lp-sino-sheet__head{ margin:0; }
.sino.lp-sino-sheet .lp-sino-sheet__head{
  padding:var(--s-4) var(--s-6);
  font-family:var(--font-mono); font-size:var(--fs-mono-md); font-weight:700;
  letter-spacing:.01em;
}
.sino.lp-sino-sheet .lp-sino-sheet__head--si{ background:var(--ok-soft); color:var(--ok-text); }
.sino.lp-sino-sheet .lp-sino-sheet__head--no{
  background:color-mix(in srgb, var(--danger) 14%, var(--bg-surface));
  color:var(--danger);
}
.sino.lp-sino-sheet ul{ font-size:var(--fs-sm); color:var(--ink-body); }
.sino.lp-sino-sheet li{
  /* 48px de columna con padding-left de 16: al glifo D1/C1 (~20px) le
     quedan ~28px reales. Con 34px y padding de 32 (QA R2) el índice
     desbordaba su celda y quedaba pegado al texto ("D1Te vale..."). */
  display:grid; grid-template-columns:48px 1fr; gap:0;
  margin:0; padding:0;
  border-top:1px solid var(--card-border);
}
.sino.lp-sino-sheet .si li::before,
.sino.lp-sino-sheet .no li::before{ content:none; }
.sino.lp-sino-sheet li i{
  display:block; padding:var(--s-4) 0 var(--s-4) var(--s-4);
  font-family:var(--font-mono); font-style:normal; font-weight:700;
  font-size:var(--fs-mono-sm);
}
.sino.lp-sino-sheet li span{
  display:block; padding:var(--s-4) var(--s-6) var(--s-4) var(--s-3);
  border-left:1px solid var(--card-border);
}
.sino.lp-sino-sheet .si li i{ color:var(--ok-text); }
.sino.lp-sino-sheet .no li i{ color:var(--danger); }

/* --- Sí/no MIXTO: [no] hoja + [sí] CRM (R2 Tarea V4.1, corrección Fabio
   "que cuente la migración en el propio diseño") ------------------------
   /servicios/migrar-excel-a-web (A2): el wrapper sigue siendo
   .sino.lp-sino-sheet (el [no] reutiliza ese componente TAL CUAL, fila de
   arriba) pero el [sí] (.box.si) suma .lp-sino-crm, que SUSTITUYE la piel
   de hoja por una fila de app real: cabecera SIN corchete ("es para ti",
   a propósito — el corchete `[sí]`/`[no]` es lenguaje de código/hoja,
   aquí se quita para que se lea como un título de app de verdad) en
   --font-disp (no mono) sobre --fam-a-soft, y cada <li> pasa de "celda
   índice + celda texto" a fila flex: dot de estado 8px (--ok, mismo
   lenguaje que .lp-mapa__dot pero en verde: "esto SÍ encaja") + texto
   (con su negrita intacta) + chip mono "apto" a la derecha (--ok/
   --ok-text, radio --r-md en vez del --r-xs de .lp-mapa__chip: más
   "pill" de app, contraste a propósito con las celdas de esquina recta
   del lado hoja). Selectores compuestos .sino .box.si.lp-sino-crm...
   (mismo motivo de especificidad que .sino.lp-sino-sheet: pages.css
   define .sino h3/.sino .si h3/.sino.lp-sino-sheet li y carga después —
   hace falta más clases para ganar siempre, no depender del orden de
   hoja). El box [sí] sigue con su sombra sólida de énfasis (.sino
   .box.si, pages.css, --shadow-b3, sin tocar) y el padding:0/
   overflow:hidden ya los hereda de .sino.lp-sino-sheet .box (arriba,
   genérico para cualquier .box del wrapper). Ver design-system.md §4.
   Sesión 5 Tarea 2 (parametrización): el fondo de la cabecera pasa de
   var(--fam-a-soft) a secas a var(--lp-crm-soft, var(--fam-a-soft)) para
   que una futura landing de familia B (morado) pueda pintar este mismo
   componente en su propio tinte fijando --lp-crm-soft en su .svc-page,
   sin tocar A2 (que no fija esa custom property y hereda el fallback
   --fam-a-soft, idéntico byte a byte a como estaba). No hay ningún uso de
   var(--fam-a) a secas (acento) dentro de este bloque — el dot/tick de
   estado usan --ok/--ok-text, que NO se tocan (verde de estado, no color
   de familia) — así que --lp-crm-accent queda declarada en B1
   (onboarding-clientes.php) para cuando este componente se adopte allí,
   pero no tiene ningún consumidor todavía en este archivo. */
.sino .box.si.lp-sino-crm .lp-sino-crm__head{
  margin:0; padding:var(--s-4) var(--s-6);
  background:var(--lp-crm-soft, var(--fam-a-soft)); color:var(--ink);
  font-family:var(--font-disp); font-size:1.05rem; font-weight:700;
  border-bottom:1px solid var(--card-border);
}
.sino .box.si.lp-sino-crm li{
  display:flex; align-items:center; gap:var(--s-3);
  margin:0; padding:var(--s-4) var(--s-6);
  border-top:1px solid var(--card-border);
}
.sino .box.si.lp-sino-crm li .lp-sino-crm__dot{
  width:8px; height:8px; border-radius:50%; flex:none;
  padding:0; border-left:0;
  background:var(--ok);
}
.sino .box.si.lp-sino-crm li .lp-sino-crm__text{
  flex:1; min-width:0; padding:0; border-left:0;
}
/* R3 (corrección Fabio: el chip mono "apto" pasa a un TICK verde, mismo
   lugar de la fila): sin fondo de chip, solo el glifo en --ok a tamaño
   legible — más ligero que el chip mono, coherente con __dot (mismo
   verde) a la izquierda de la fila. decorativo (aria-hidden en el HTML,
   el __dot de estado ya comunica "esto sí encaja" sin el tick). */
.sino .box.si.lp-sino-crm li .lp-sino-crm__tick{
  flex:none; padding:0; border-left:0;
  color:var(--ok); font-size:1.15rem; font-weight:700; line-height:1;
}

/* Generalización (Sesión 5, Tarea 2 — /servicios/onboarding-clientes, B1):
   en A2 el `.box.si.lp-sino-crm` vive dentro de `.sino.lp-sino-sheet`, y
   ese wrapper YA cancela el padding del `.box` genérico y el marcador
   `>`/`×` de `.sino .si/.no li::before` (reglas de arriba, líneas ~1817 y
   ~1838). B1 NO usa `.lp-sino-sheet` (su lado [no] se queda `.box.no`
   genérico, sin piel de Excel) — sin estas 2 reglas, el `.box.si.lp-sino-
   crm` de B1 heredaría el padding:var(--s-7) de `.sino .box` (la cabecera
   no llegaría a los 4 bordes) y cada `<li>` mostraría el `>` de `.sino
   .si li::before` por detrás del dot/texto/tick. Especificidad mayor
   (0,4,x) que ambas reglas de origen: en A2 no cambia nada (mismo
   content:none/padding:0 que ya aplicaba `.lp-sino-sheet`, verificado con
   curl antes/después). Ver design-system.md §4. */
.sino .box.si.lp-sino-crm{ padding:0; overflow:hidden; }
.sino .box.si.lp-sino-crm li::before{ content:none; }

/* .sino--filas — la fila N de las dos columnas comparte alto (2026-08-18,
   corrección Fabio sobre la caja sí/no de A1 y A2: con 5 filas por lado, la
   columna de textos más cortos dejaba hueco muerto abajo y las filas no se
   leían enfrentadas).
   Las dos `.box` ya son celdas de un grid `1fr 1fr` con `align-items:stretch`,
   así que comparten altura de caja; esto reparte esa altura entre las filas.
   `flex:1 1 0` + el `min-height:auto` que traen los items flex por defecto =
   ninguna fila baja de su propio contenido y el sobrante se reparte, así que
   con el MISMO número de filas por lado quedan enfrentadas una a una.
   `min-height:0` en el `<ul>` es obligatorio: sin él el `<ul>` no baja de su
   altura de contenido y el reparto no llega a aplicarse.

   DOS INTENTOS DESCARTADOS, para que nadie los repita:
   1. Centrar el texto poniendo `display:flex` en el `<span>` de la fila. En un
      contenedor flex cada `<b>` y cada trozo de texto suelto se convierte en
      un item anónimo y se va a su propia línea: el texto dejaba de ocupar el
      ancho y el punto final quedaba colgando solo. El `<span>` se queda en
      flujo normal; el texto arranca arriba y ya está.
   2. Subgrid (`grid-template-rows:subgrid` en `.box` y en el `<ul>`). Es la
      herramienta teóricamente correcta, pero las dos cajas llevan
      `overflow:hidden` de sus pieles (`.lp-sino-sheet`, `.lp-sino-crm`) y las
      pistas del padre dejaban de dimensionar con el contenido: filas de 49px
      para textos de 105px. Medido, no supuesto.

   MODIFICADOR APARTE a propósito: `.sino` lo usan cinco landings con tres
   pieles distintas (`.lp-sino-sheet`, `.lp-sino-crm` y `.box.no` pelada).
   Metiendo esto en las reglas base cambiaría las tres a la vez; aquí solo
   afecta a quien pone la clase en el wrapper. Hoy: A1 y A2, las dos que
   tienen 5 filas por lado. B1/B2/Macroning están en 4/4 y no la necesitan.

   Solo a partir de 881px: por debajo, pages.css apila `.sino` en una columna,
   cada caja mide lo que mide su contenido y no hay sobrante que repartir. */
@media (min-width: 881px) {
  .sino--filas .box{ display:flex; flex-direction:column; }
  .sino--filas .box > ul{ display:flex; flex-direction:column; flex:1 1 auto; min-height:0; }
  .sino--filas .box > ul > li{ flex:1 1 0; }
}

/* .lp-sino-crm--no — el lado [no] con la MISMA geometría que el [sí]
   (2026-08-19, corrección Fabio: "no tienen alineados los puntos y tienen
   espacios irregulares", en las tres landings que no son de Excel).

   EL PROBLEMA: en B1, B2 y /macroning las dos columnas se pintaban con
   mecánicas distintas. El [sí] adopta `.lp-sino-crm`: caja sin padding,
   cabecera en banda a todo el ancho y filas con `border-top`. El [no] se
   quedaba en `.box.no` pelada: padding de --s-7, el `<h3>` como un título
   suelto dentro de ese padding y los `<li>` separados por `margin-bottom`
   con el aspa en un `::before` absoluto. Dos ritmos verticales distintos
   uno al lado del otro: ni las cabeceras empiezan a la misma altura, ni las
   filas se enfrentan, ni los espacios entre puntos coinciden.
   A1 y A2 no lo sufren porque ahí las dos columnas comparten
   `.lp-sino-sheet`.

   LA CORRECCIÓN: el [no] recibe la misma geometría de banda + filas. La
   tipografía de su banda se queda en mono y en `--danger`, que es el mismo
   lenguaje que ya usa `.lp-sino-sheet__head--no` en A1/A2, para que el lado
   negativo siga leyendo como negativo y no como una segunda lista de apto.
   Con las dos columnas al mismo ritmo, `.sino--filas` ya puede enfrentar las
   filas una a una (las tres páginas van 4 y 4).

   El aspa pasa de `::before` absoluto a item del flex: en una fila flex, un
   marcador posicionado sobre el padding-left de la regla base se queda
   descolgado en cuanto el texto se centra en vertical. */
.sino .box.no.lp-sino-crm--no{
  padding:0; overflow:hidden; background:var(--bg-surface);
}
.sino .box.no.lp-sino-crm--no > h3{
  margin:0; padding:var(--s-4) var(--s-6);
  background:color-mix(in srgb, var(--danger) 10%, var(--bg-surface));
  color:var(--danger);
  font-family:var(--font-mono); font-size:var(--fs-mono-md); font-weight:700;
  border-bottom:1px solid var(--card-border);
}
.sino .box.no.lp-sino-crm--no > ul > li{
  display:flex; align-items:center; gap:var(--s-3);
  margin:0; padding:var(--s-4) var(--s-6);
  border-top:1px solid var(--card-border);
}
.sino .box.no.lp-sino-crm--no > ul > li::before{
  position:static; flex:none; align-self:center; line-height:1;
}
/* El contenido de la fila va envuelto en `.lp-sino-crm__text`, igual que en
   el lado [sí] (2026-08-19, corrección Fabio: "pon ambos paneles con el
   mismo estilo, título y debajo subtítulo").
   Sin el envoltorio, el `<b>` y el `<small>` de la fila son HERMANOS dentro
   del flex, así que se convierten en dos items y se reparten el ancho: el
   título a la izquierda y el subtítulo a la derecha, en vez de apilados.
   Es el mismo mecanismo que partió el texto de A1 en su día. Con el
   envoltorio son UN item y `.lp-sino-crm__sub` (display:block) vuelve a
   bajar de línea. */
.sino .box.no.lp-sino-crm--no > ul > li > .lp-sino-crm__text{
  flex:1; min-width:0; padding:0; border-left:0;
}

/* .lp-sino-crm__sub — sub-línea de contexto bajo la línea principal de
   cada fila (Sesión 6, R1-T2: `/macroning`, "para quién es" desarrollado
   de 3/2 a 4/3 filas, feedback Fabio "muy corto"). Selector `.sino li
   .lp-sino-crm__sub` (NO scoped a `.lp-sino-crm`): sirve tanto en el
   lado [sí] (fila de app de arriba) como en el lado [no] genérico
   `.box.no` (que NO adopta `.lp-sino-crm`, mismo criterio que B1/B2) —
   una sola regla para los dos contextos en vez de duplicarla.
   display:block para que baje de línea sola, sin depender de un <br>.
   --ink-soft es el token de microcopy ya usado en toda la web (nunca uno
   nuevo), con contraste de sobra tanto sobre --bg-surface (fila de app
   del [sí]) como sobre --bg-calm (`.sino .box.no`, pages.css). Regla
   solo ADITIVA: A2/B1/B2 no usan esta clase en su HTML, no cambian ni
   un píxel. */
.sino li .lp-sino-crm__sub{
  display:block; margin-top:2px;
  font-family:var(--font-body); font-weight:400;
  font-size:var(--fs-sm); color:var(--ink-soft);
}

/* =====================================================================
   R1 (2026-07-16) — Tarea U3: calculadora compartida (A1/A2) rediseñada
   como HOJA DE EXCEL (más ancha, menos alta) + contador siempre vivo.
   Registrado en design-system.md §4.
   ===================================================================== */

/* Fondo de sección de la calculadora: cada página fija el tinte con la
   custom property --lp-calc-bg en el wrapper .svc-page (A1:
   --ok-soft verde suave; A2 la fija U6 con --fam-a-soft). Fallback
   --bg-calm mientras A2 no la fije todavía. */
.sec--sheet{ background:var(--lp-calc-bg, var(--bg-calm)); }

/* --- Card exterior: tamaño/forma propios de la hoja (gana por selector
   compuesto sobre la .lp-calc base de arriba, mismo criterio que
   .svc-hero.lp-hero). MÁS ANCHA (60rem vs 38rem) Y MENOS ALTA: el
   contenido ya no se apila en un único bloque vertical, ver __grid. */
.lp-calc.lp-calc--sheet{
  max-width:60rem; padding:var(--s-6);
}

/* Barra de ventana (mismo lenguaje visual que .media-frame__bar: puntos +
   título mono) como clase PROPIA en vez de reutilizar .media-frame: esta
   card ya trae su propio padding/sombra/max-width arriba y pelearía la
   cascada con .media-frame (14px/--shadow-soft) sin ganar nada a cambio,
   ver comentario del bloque .lp-calc de más arriba. */
.lp-calc--sheet__winbar{ display:flex; gap:6px; align-items:center; margin-bottom:var(--s-4); }
.lp-calc--sheet__winbar i{ width:10px; height:10px; border-radius:50%; background:var(--card-border); display:block; }
.lp-calc--sheet__winbar .title{ font-family:var(--font-mono); font-size:.6875rem; color:var(--ink-soft); margin-left:8px; }

/* Barra fx FUNCIONAL (no decorativa, a diferencia de includes/lp-fxbar.php
   bajo el topbar): landing.js reescribe __fx-formula en cada input.
   aria-hidden en el HTML porque es un eco visual de valores YA accesibles
   vía los <output for> de cada slider y el aria-live del resultado. */
.lp-calc--sheet__fx{
  display:flex; align-items:center; gap:var(--s-2);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  background:var(--bg-calm); border:1px solid var(--card-border); border-radius:var(--r-xs);
  padding:7px 10px; margin-bottom:var(--s-5);
}
.lp-calc--sheet__fx-cell{
  flex:none; font-weight:700; color:var(--ink);
  background:var(--bg-surface); border:1px solid var(--card-border); border-radius:var(--r-xs);
  padding:2px 8px;
}
.lp-calc--sheet__fx-fx{ flex:none; color:var(--ink-soft); font-style:italic; }
.lp-calc--sheet__fx-formula{
  flex:1 1 auto; min-width:0; color:var(--svc-accent, var(--ok)); font-weight:700;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* --- Grid 2 zonas (≥880px): izquierda (gutter+A+B) | derecha (C,
   resultado). Un borde exterior único (overflow:hidden recorta las
   esquinas de la cabecera al radio) + una sola hairline interior entre
   zonas (border-right de __left): así no hay doble línea en el punto de
   unión. ≤880px las zonas apilan a 1 columna (regla de abajo). */
.lp-calc--sheet__grid{
  display:grid; grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);
  border:1px solid var(--card-border); border-radius:var(--r-md); overflow:hidden;
}
.lp-calc--sheet__left{
  display:grid; grid-template-columns:26px minmax(90px,1fr) minmax(150px,1.3fr);
  border-right:1px solid var(--card-border);
}

/* Cabecera de columnas A|B|C + gutter de filas numeradas 1-3: puramente
   decorativos (aria-hidden en el HTML), se ocultan ≤880px con el resto
   de cromo de hoja (regla .lp-calc--sheet__deco de abajo). */
.lp-calc--sheet__colhead{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--ink-soft); text-align:center;
  padding:6px 0; border-bottom:1px solid var(--card-border);
}
.lp-calc--sheet__idx{
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft);
  border-top:1px solid var(--card-border);
}

/* Fila = una celda A (etiqueta) + una celda B (control). Rejilla con
   hairlines como .mock-sheet pero FUNCIONAL: aquí hay inputs reales, no
   una captura. Un control por fila (regla de formularios de Fabio). */
.lp-calc--sheet__cellA,
.lp-calc--sheet__cellB{ border-top:1px solid var(--card-border); border-left:1px solid var(--card-border); }
.lp-calc--sheet__cellA{
  display:flex; align-items:center; padding:var(--s-3) var(--s-4);
}
.lp-calc--sheet__cellA label{
  font-family:var(--font-disp); font-weight:600; font-size:.9375rem; color:var(--ink); line-height:1.3;
}
.lp-calc--sheet__cellB{
  position:relative;
  display:flex; flex-direction:column; justify-content:center;
  padding:var(--s-3) var(--s-4);
}
/* B1/B2 "celda seleccionada" de Excel: borde de acento 2px (verde en A1,
   azul en A2 vía --svc-accent, mismo fallback que .mock-sheet--rich) +
   esquinita de arrastre. La esquinita se queda DENTRO del padding-box
   (nunca con right/bottom negativos) para no arriesgarse a un recorte si
   algún ancestro futuro añade overflow:hidden. */
.lp-calc--sheet__cellB.is-selected{
  border:2px solid var(--svc-accent, var(--ok)); border-radius:var(--r-xs);
  background:var(--bg-surface);
}
.lp-calc--sheet__cellB.is-selected::after{
  content:""; position:absolute; right:3px; bottom:3px;
  width:7px; height:7px; background:var(--svc-accent, var(--ok));
  border:1px solid var(--bg-surface);
}
/* .lp-calc input[type=range] / .lp-calc output (bloque de arriba) siguen
   aplicando dentro de esta celda: el wrapper conserva la clase .lp-calc. */

/* --- Celda de error Excel (fila 3, R2 Tarea V1.2: fix Fabio "una celda
   con el típico error de Excel cuando el contador se pone rojo") ---
   [data-lp-calc-err] SIEMPRE está en el DOM (nunca `__deco`, a
   diferencia de sus vecinas `__idx`/`__cellA` de esta misma fila, que
   siguen ocultándose ≤880px): min-height reserva 1 línea de mono aunque
   esté vacía, así el alto de la fila no cambia entre tramos. landing.js
   solo escribe texto y añade `.is-err` con heat 3 (el resto la deja
   vacía) — SIN transición: es un error de hoja, aparece/desaparece en
   seco, no se anima.
   El min-height cuenta TODO el border-box (box-sizing:border-box global):
   no basta con el alto de línea del texto, hay que sumar el padding
   vertical de `.lp-calc--sheet__cellB` (var(--s-3) arriba y abajo, la
   clase que esta celda también lleva) + 1px del border-top que comparte
   con sus vecinas — si no, el padding solo (24px) ya supera el alto de
   una línea (~19.8px) y el min-height nunca llega a aplicar: vacía mide
   menos que con texto y la fila SÍ cambiaba de alto (bug real, detectado
   midiendo a 390px: __left crecía ~20px al aparecer el error porque en
   móvil __grid apila en 1 columna y ya no lo absorbe el estirado con
   __result como en escritorio). Verificado: idéntico con/sin texto. */
.lp-calc--sheet__err{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  min-height:calc(var(--s-3) * 2 + var(--fs-mono-sm) * var(--lh-body) + 1px);
}
.lp-calc--sheet__err.is-err{
  color:var(--danger); font-weight:700;
  background:color-mix(in srgb, var(--danger) 14%, var(--bg-surface));
}

/* --- Celda RESULTADO (columna C, "rowspan visual" sobre las 3 filas de
   la zona izquierda vía stretch por defecto del grid exterior: como
   __left y __result son las dos únicas celdas de esa fila del grid, se
   estiran a la misma altura sin necesidad de grid-row a mano). data-heat
   tiñe SOLO el fondo de esta celda (antes era toda la card). */
.lp-calc--sheet__result{
  display:flex; flex-direction:column; align-items:center;
  background:var(--fam-b-soft); /* heat 0: "morado" suave (ya existe, ver design-system.md) */
  transition:background var(--t-base) var(--ease-out);
}
.lp-calc--sheet__result[data-heat="1"]{ background:color-mix(in srgb, var(--hl) 24%, var(--bg-surface)); }
.lp-calc--sheet__result[data-heat="2"]{ background:color-mix(in srgb, var(--danger) 16%, var(--bg-surface)); }
.lp-calc--sheet__result[data-heat="3"]{ background:color-mix(in srgb, var(--danger) 26%, var(--bg-surface)); }
.lp-calc--sheet__result > .lp-calc--sheet__colhead{ width:100%; border-left:0; }
.lp-calc--sheet__result-body{
  flex:1; width:100%; display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; padding:var(--s-6) var(--s-5);
}
.lp-calc--sheet__result-label{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft); margin-bottom:var(--s-2);
}
.lp-calc--sheet__result-body b{
  font-family:var(--font-disp); font-size:var(--fs-display); font-weight:700; line-height:1;
  font-feature-settings:"tnum" 1; color:var(--ink);
}
.lp-calc--sheet__result-body small{
  display:block; margin-top:var(--s-3);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft);
}
/* .lp-calc__frase (bloque de arriba) sigue aplicando tal cual dentro de
   __result-body: mismo mono itálico + fade, sin cambios. */

@media (max-width:880px){
  /* Zonas apiladas: quedan "como ahora" (flujo vertical, inputs y luego
     resultado) pero con la piel de hoja (winbar+fx se mantienen). Se
     oculta TODO el cromo puramente decorativo (cabecera A|B|C y gutter
     1-3) para recuperar espacio, igual que .lp-fxbar__tabs ≤880px. */
  .lp-calc.lp-calc--sheet{ max-width:34rem; padding:var(--s-5); }
  .lp-calc--sheet__grid{ grid-template-columns:1fr; border-radius:var(--r-md); }
  .lp-calc--sheet__left{ grid-template-columns:1fr; border-right:0; }
  .lp-calc--sheet__deco{ display:none; }
  .lp-calc--sheet__cellA,
  .lp-calc--sheet__cellB{ border-left:0; }
  .lp-calc--sheet__result{ border-top:1px solid var(--card-border); }
}

/* =====================================================================
   R1 (2026-07-16) — Tarea U4: A1 ventajas navy densas (ex-"qué incluye"),
   mapa de pasos con línea SVG discontinua que se rellena con el scroll y
   garantías como sello de goma. Registrado en design-system.md §4.
   ===================================================================== */

/* --- Ventajas NAVY densas (sustituye .inc-grid en "qué incluye") -------
   Rejilla tipo hoja 4x2 (2x4 móvil): un único hairline compartido por
   borde (container: border-top+left; cada celda: border-right+bottom —
   mismo criterio "una sola línea en el punto de unión" que
   .lp-calc--sheet__grid/__left de la Tarea U3, arriba) en lila
   translúcido (color-mix, mismo criterio que .lp-hero .svc-meta span).
   Check en --ok: verde Excel, decorativo PERO también pasa como texto
   (fórmula WCAG relativa calculada a mano): 4.65:1 sobre --bg-inverse,
   MEJOR que --ok-text (cae a 3:1 sobre navy — ese tono se oscureció a
   propósito para fondos CLAROS, aquí sobra). El amarillo (--hl) se queda
   fuera del check a propósito: sería decorativo con contraste altísimo
   (10.45:1) pero es un glifo de fuente real (texto), no un icono aparte,
   y "amarillo nunca es color de texto" (§9) no distingue por tamaño — se
   reserva al tag (.tag--yellow, ya aprobado sobre navy en el CTA final
   de esta misma página). Fila-suma al pie: grid-column 1/-1, mono,
   border-top DOUBLE (el subrayado doble contable real de una celda
   TOTAL de hoja de cálculo, no un simple text-decoration). */
.lp-ventajas{
  display:grid; grid-template-columns:repeat(4, 1fr);
  border-top:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  border-left:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
}
.lp-ventajas__item{
  display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-5);
  border-right:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  border-bottom:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
}
.lp-ventajas__item i{ flex:none; font-style:normal; font-weight:700; font-size:1.15rem; color:var(--ok); }
.lp-ventajas__item b{
  font-family:var(--font-disp); font-weight:600; font-size:.9375rem;
  color:var(--ink-inverse); line-height:1.3;
}
.lp-ventajas__total{
  grid-column:1 / -1;
  padding:var(--s-5);
  font-family:var(--font-mono); font-size:var(--fs-sm); font-weight:700;
  color:var(--ink-inverse);
  border-right:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  border-bottom:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  border-top:4px double var(--ok);
}
@media (max-width:880px){
  .lp-ventajas{ grid-template-columns:minmax(0, 1fr) minmax(0, 1fr); }
  .lp-ventajas__item{ min-width:0; }
}
/* Bajo 400px la columna de 2 se queda con muy poco ancho de texto
   (palabras largas como "Multidispositivo" no caben en la celda) y se
   apila a 1 columna. */
@media (max-width:400px){
  .lp-ventajas{ grid-template-columns:1fr; }
}

/* --- Mapa de pasos (sustituye .timeline/[data-timeline] en "cómo
   funciona"; ESE mecanismo — motion.js initTimeline, --tl-p, .tl-line —
   sigue intacto y en uso por index.php/home, no se toca) --------------
   Grid 2 columnas boustrofedon: el orden natural del DOM (1,2,3,4) en 2
   columnas YA dibuja el zigzag (1 arriba-izq, 2 arriba-dcha, 3 abajo-izq,
   4 abajo-dcha) sin reordenar nada — .lp-mapa__stop--r solo añade un
   desplazamiento vertical a las paradas de la derecha (2 y 4) para que
   el escalonado se LEA como zigzag y no como una rejilla alineada.
   SVG decorativo detrás (position:absolute; inset:0): viewBox 0 0 100
   100 + preserveAspectRatio="none" para que los 4 puntos de anclaje se
   mantengan en la misma posición RELATIVA del contenedor en cualquier
   proporción; vector-effect="non-scaling-stroke" (atributo SVG, en
   AMBOS <path>) para que ese preserveAspectRatio="none" no adelgace ni
   engorde el trazo — el grosor se queda fijo en unidades de pantalla.
   Los 4 puntos son una aproximación decorativa (cerca de la esquina de
   cada outline-num, no pixel-perfect al centro de la card): de sobra
   para una línea que solo SUGIERE la ruta entre paradas. */
.lp-mapa{ position:relative; margin-top:var(--s-7); }
.lp-mapa__svg{ position:absolute; inset:0; width:100%; height:100%; z-index:0; pointer-events:none; }
/* Punteado en unidades NORMALIZADAS: pathLength="100" en el HTML, así que
   ".25 .75" = 100 puntos exactos a lo largo del path, sin getTotalLength().
   Base (gris, este .lp-mapa__line sin --fill) = camino de fondo fijo. */
.lp-mapa__line{
  stroke:color-mix(in srgb, var(--ink) 35%, transparent);
  stroke-width:3.5; stroke-linecap:round; stroke-dasharray:.25 .75;
}
/* R2 Tarea V2.1 + QA (fix Fabio "la línea quedaba coloreada a la mitad"):
   trazo SÓLIDO revelado por CLIP-PATH vertical (inset animado por
   landing.js de `inset(0 0 100% 0)` a `inset(0)` con el scroll). NADA de
   stroke-dashoffset para revelar: con preserveAspectRatio="none" el path
   se estira de forma NO uniforme y el reparto del dash (que va por la
   longitud del path SIN estirar, pathLength=100) no casa con lo que se
   ve — por eso el relleno salía "a la mitad" o empezando por el final,
   con cualquier signo del offset. El clip rectangular avanza de arriba
   abajo en PANTALLA (natural con el zigzag) y es inmune al estiramiento.
   Default aquí = sin clip (línea COMPLETA): fallback sin JS/GSAP/reduce. */
.lp-mapa__line--fill{
  stroke:var(--accent); stroke-width:3; stroke-linecap:round;
}
.lp-mapa__stops{
  position:relative; z-index:1;
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s-8) var(--s-7);
  /* R2 V2.2/V2.3 (fix Fabio "algunas eran más altas que el texto"): el
     stretch por defecto de align-items en grid igualaba cada pareja de
     fila a la card más alta de las dos. Con start, cada card mide su
     propio contenido; las 4 pueden acabar con alturas distintas, mejor
     eso que aire de sobra bajo un texto corto. */
  align-items:start;
}
/* Parada = CARD OPACA (fix revisión R1: la línea cruzaba el texto de los
   pasos cuando eran texto suelto — ahora la card la TAPA a su paso y la
   línea solo asoma por los pasillos). width:100% + max-width para que la
   caja mida 30rem exactos también con justify-self:end (sin el width,
   una alineación no-stretch dejaría la card a su fit-content). El
   overflow:hidden recorta la franja __head (ancho completo) a las
   esquinas redondeadas de la card (R3: antes también recortaba el
   outline-num decorativo del fondo, ya retirado, ver más abajo). R2
   V2.2/V2.3: el padding fijo
   de toda la card se retira de aquí (pasa a __head/__body por separado)
   para que la franja de encabezado llegue a los 4 bordes de la card. */
.lp-mapa__stop{
  position:relative; overflow:hidden;
  width:100%; max-width:30rem;
  background:var(--bg-surface); border:1px solid var(--card-border);
  border-radius:var(--r-md); box-shadow:var(--shadow-b1);
  transition:box-shadow var(--t-base);
}
/* Las paradas de la derecha (2 y 4) bajan y se pegan al borde derecho:
   el escalonado se lee como zigzag y el pasillo central se ensancha
   (la línea serpentea por ahí). */
.lp-mapa__stop--r{ margin-top:var(--s-8); justify-self:end; }
.lp-mapa__stop:hover{ box-shadow:var(--shadow-b2); }
/* R3 (corrección Fabio "quitar los números grandes de fondo de las
   tarjetas"): .lp-mapa__num retirada (antes un .outline-num 01..04
   decorativo por detrás del __body, opacity:.1). .outline-num (la clase
   BASE) sigue viva y en uso por index.php/sistemas-de-gestion.php: solo
   se retira esta variante compuesta, no el átomo compartido. */
.lp-mapa__body{ position:relative; z-index:1; padding:var(--s-4) var(--s-5) var(--s-5); }
.lp-mapa__body p{ font-size:.875rem; margin:0; }

/* --- Franja de encabezado (R2 V2.2/V2.3, fix Fabio "tarjetas con franja
   de color en el encabezado") -----------------------------------------
   .lp-mapa__head = barra superior opaca a ancho completo (fuera del
   padding de __body, a diferencia del <h3> de antes que vivía dentro).
   Base compartida entre las 2 variantes de página; cada una decide
   fondo/adorno lateral (celda-ref mono vs. dot+chip). */
.lp-mapa__head{
  position:relative; z-index:1;
  display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-3) var(--s-5);
  transition:background var(--t-base), color var(--t-base);
}
.lp-mapa__head h3{ font-size:1.05rem; margin:0; flex:1; min-width:0; }

/* Variante A1 = celda de hoja Excel: franja verde suave + borde inferior
   sólido + celda-ref mono B1..B4 (mismo lenguaje de índice que
   .lp-sino-sheet/.lp-calc--sheet__idx). Hover: la franja se INVIERTE a
   --ok sólido con texto en --ink-inverse (la card entera ya sube a
   --shadow-b2 vía la regla genérica de arriba) — SOLO
   background/color en transición, CERO transform: esta card lleva
   data-reveal (GSAP deja un transform inline al terminar el reveal, un
   :hover con transform perdería contra ese inline). */
.lp-mapa__stop--sheet .lp-mapa__head{ background:var(--ok-soft); border-bottom:2px solid var(--ok); }
.lp-mapa__ref{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--ok-text); flex:none;
}
.lp-mapa__stop--sheet:hover .lp-mapa__head{ background:var(--ok); color:var(--ink-inverse); }
.lp-mapa__stop--sheet:hover .lp-mapa__ref{ color:var(--ink-inverse); }

/* Variante A2 = fila de app CRM/gestor documental: franja azul suave +
   dot de estado + chip mono paso_01..04 a la derecha + borde izquierdo
   de acento (lenguaje "fila de lista de una app", no "celda de hoja").
   Hover: dot y chip se intensifican, misma regla de "nada de transform"
   que la variante sheet.
   Sesión 5 Tarea 4 (parametrización, mismo patrón que --lp-crm-soft/
   .lp-sino-crm de Tarea 2): franja y acento pasan de var(--fam-a-soft)/
   var(--fam-a) a secas a var(--lp-crm-soft, var(--fam-a-soft))/
   var(--lp-crm-accent, var(--fam-a)), así B1 (que fija ambas custom
   properties en morado en su .svc-page desde Tarea 2) pinta este mismo
   mapa de pasos en --fam-b sin CSS propio. A2 no fija ninguna de las
   dos, hereda el fallback --fam-a/--fam-a-soft, idéntico byte a byte a
   como estaba (verificado por diff). --ok/--ok-text del hover de estado
   en otros componentes NO se tocan, esto es solo el acento de familia. */
.lp-mapa__stop--crm{ border-left:3px solid var(--lp-crm-accent, var(--fam-a)); }
.lp-mapa__stop--crm .lp-mapa__head{ background:var(--lp-crm-soft, var(--fam-a-soft)); }
.lp-mapa__dot{
  width:8px; height:8px; border-radius:50%; flex:none;
  background:var(--lp-crm-accent, var(--fam-a)); transition:background var(--t-base), box-shadow var(--t-base);
}
.lp-mapa__chip{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--lp-crm-accent, var(--fam-a)); background:var(--bg-surface); border:1px solid var(--lp-crm-accent, var(--fam-a));
  border-radius:var(--r-xs); padding:2px 8px; flex:none;
  transition:background var(--t-base), color var(--t-base);
}
.lp-mapa__stop--crm:hover .lp-mapa__dot{ background:var(--ink); box-shadow:0 0 0 3px color-mix(in srgb, var(--lp-crm-accent, var(--fam-a)) 35%, transparent); }
.lp-mapa__stop--crm:hover .lp-mapa__chip{ background:var(--lp-crm-accent, var(--fam-a)); color:var(--ink-inverse); }

/* Móvil (≤880px): el SVG se oculta ENTERO (display:none). El borde lateral
   discontinuo de la columna apilada queda de PISTA de fondo y encima crece
   un relleno sólido con el scroll (mismo gesto que la línea de "cómo
   trabajamos" del home), en vez del borde estático de antes. Las paradas
   conservan la piel de card (franja incluida). */
@media (max-width:880px){
  .lp-mapa__svg{ display:none; }
  .lp-mapa__stops{
    position:relative;
    grid-template-columns:1fr; gap:var(--s-5);
    border-left:2px dashed color-mix(in srgb, var(--ink) 35%, transparent);
    margin-left:6px; padding-left:var(--s-5);
  }
  /* Relleno que crece de arriba abajo según --mapa-p (0 a 1), la variable
     que landing.js publica en .lp-mapa (heredada aquí) en cada tick del
     scroll. Se superpone sobre el borde discontinuo izquierdo. Fallback:
     sin JS/GSAP o con reduce, --mapa-p nunca se fija y var(--mapa-p, 1)
     deja la línea LLENA (mismo criterio que el SVG de escritorio). */
  .lp-mapa__stops::before{
    content:""; position:absolute; left:-2px; top:0; width:2px;
    height:calc(var(--mapa-p, 1) * 100%);
    background:var(--lp-crm-accent, var(--fam-a));
  }
  .lp-mapa__stop--r{ margin-top:0; justify-self:auto; }
}

/* --- Sellos de goma (sustituyen a .inc-grid.lp-guarantees/.inc-card en
   A1; esas reglas de arriba NO se tocan, A2 las sigue usando hasta su
   propia Tarea U6) ------------------------------------------------------
   Círculo con doble borde (border:double), tinta por sello vía
   --sello-ink inline (verde --ok-text / morado --accent / navy --ink:
   los tres sobre CREMA, sec--warm, no navy — --ok-text es exactamente el
   tono pensado para fondos claros, al revés que en .lp-ventajas arriba).
   MAYÚSCULAS por CSS (text-transform), nunca reescrito en el HTML, para
   que un lector de pantalla conserve la capitalización natural del
   texto. Sin mix-blend-mode a propósito (pedido explícito: "simple"),
   solo opacity para el efecto de tinta de tampón. Hover "estampa" (scale
   1.03 + opacity 1): sin conflicto con data-scrub porque esta sección no
   tiene ningún ancestro con ese atributo. */
.lp-mapa__sellos{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:var(--s-7) var(--s-6);
}
.lp-sello{
  width:150px; height:150px; flex:none;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:4px; text-align:center;
  border-radius:50%; border:5px double var(--sello-ink, var(--ink));
  color:var(--sello-ink, var(--ink));
  font-family:var(--font-mono); text-transform:uppercase;
  opacity:.85;
  transform:rotate(var(--rot, -4deg));
  transition:transform var(--t-base) var(--ease-pop), opacity var(--t-base);
}
.lp-sello:hover{ opacity:1; transform:rotate(var(--rot, -4deg)) scale(1.03); }
.lp-sello__line{ font-size:.9375rem; font-weight:700; line-height:1.25; padding:0 var(--s-3); }
.lp-sello__sub{ display:block; font-size:.6875rem; font-weight:500; opacity:.85; padding:0 var(--s-4); margin-top:2px; }
.lp-mapa__sign{ margin-left:var(--s-3); }

/* Móvil (≤880px): los 3 sellos en UNA sola fila y la firma en su propia
   fila debajo. Se MANTIENE flex-wrap:wrap (no nowrap): así el
   .lp-mapa__sign, con flex-basis:100%, salta a la línea siguiente, mientras
   los sellos (ya reducidos con clamp) entran los 3 juntos sin partirse a
   320/360/390. El clamp escala con el viewport y topa en 106px, de modo
   que este bloque de 880 ya cubre lo que antes hacía el escalón de 560
   (retirado: con 126px fijos los 3 sellos no cabían a 320). Va DESPUÉS de
   las reglas base de .lp-sello para ganar el cascade a igual
   especificidad. */
@media (max-width:880px){
  .lp-mapa__sellos{ flex-wrap:wrap; gap:var(--s-4) var(--s-2); }
  .lp-sello{ width:clamp(76px, 24vw, 106px); height:clamp(76px, 24vw, 106px); }
  .lp-sello__line{ font-size:var(--fs-mono-sm); }
  .lp-sello__sub{ font-size:.625rem; }
  .lp-mapa__sign{ flex-basis:100%; margin-left:0; margin-top:var(--s-4); text-align:center; }
}

/* =====================================================================
   R1 (2026-07-16) — Tarea U5: A1 casos ×2 (+ServiTaxi), founder navy con
   el responsable de proyecto y FAQ en rejilla (patrón de la home).
   Registrado en design-system.md §4.
   ===================================================================== */

/* --- Casos ×2 (sustituye a `.case` con blockquote en "un caso real") ---
   Dos `.lp-caso-card` lado a lado (1 col ≤880): sticker esquinero +
   historia corta en 3ª persona SIN cita atribuida (regla de esta
   landing: nada de blockquote con nombre) + métrica display grande
   (mismo tratamiento tipográfico que `.lp-calc--sheet__result-body b`:
   --fs-display + tnum, --ink como el número) + etiqueta mono. Card
   blanca con sombra sólida de reposo, mismo lenguaje que `.lp-mapa__stop`
   (--shadow-b1, sin riesgo de sangrado entre cards: el gap de --s-6
   dobla el offset de --shadow-b-offset). */
.lp-casos{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s-6);
}
.lp-caso-card{
  position:relative;
  background:var(--bg-surface); border:1px solid var(--card-border);
  border-radius:var(--card-radius); box-shadow:var(--shadow-b1);
  padding:var(--s-7) var(--s-6) var(--s-6);
}
.lp-caso-card__sticker{ position:absolute; top:-14px; left:var(--s-6); z-index:2; }
.lp-caso-card p{ font-size:1.05rem; line-height:1.5; margin:0 0 var(--s-6); }
.lp-caso-card__metric{ border-top:1px solid var(--card-border); padding-top:var(--s-5); }
.lp-caso-card__metric b{
  display:block; font-family:var(--font-disp); font-size:var(--fs-display); font-weight:700;
  line-height:1; color:var(--ink); font-feature-settings:"tnum" 1;
}
.lp-caso-card__metric small{
  display:block; margin-top:var(--s-2);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft);
}
@media (max-width:880px){
  .lp-casos{ grid-template-columns:1fr; gap:var(--s-5); }
}

/* --- Casos formato home (R2 Tarea V3.3, corrección Fabio "formato de la
   home, sin carrusel") -------------------------------------------------
   A1 deja `.lp-casos`/`.lp-caso-card` de arriba (sigue vivo tal cual para
   A2 hasta su propia Tarea V4) y reutiliza `.case`/`.case-terminal` YA
   GLOBALES (sección "PRUEBA — caso real", más arriba en este archivo) sin
   duplicar una sola línea de esas clases. Lo único nuevo es el
   ENVOLTORIO: `.lp-casos-home` apila los dos `.case` a lo ancho (columna,
   sin carrusel) con gap generoso (--s-8, el doble del offset de sombra
   --shadow-b-offset de la trampa §3: la sombra sólida de `.case-terminal`
   nunca llega a rozar el `.case` siguiente) y `.lp-case-link` (duplicado
   mínimo de `.home-case-link`, pages.css: mismo mono/acento/hover, sin
   depender de una clase con prefijo `home-`) sustituye al chip
   (`.caso-ficha__tags`, que en los slides de origen apuntaba a esta misma
   landing o a /macroning) y al enlace `.home-case-link` de cada slide por
   un único CTA de línea al funnel. */
.lp-casos-home{ display:flex; flex-direction:column; gap:var(--s-8); }
.lp-case-link{
  display:inline-block; margin-top:var(--s-5);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--accent); text-decoration:none;
}
.lp-case-link:hover{ color:var(--accent-2); }

/* --- Founder sobre NAVY (sección "quién está detrás" pasa a sec--inverse)
   `.founder img` trae `border:3px solid var(--ink)` de serie (arriba en
   este archivo) — mismo HEX que `--bg-inverse` (#0a1f44): invisible
   sobre navy. Selector genérico `.sec--inverse .founder img` (no
   `.lp-founder`) para que cualquier founder futuro sobre navy quede
   cubierto igual, no solo el de landing. La sombra `--shadow-b3` (morada)
   ya se lee bien sobre navy (mismo criterio que `.lp-hero__collage
   .media-frame`), no se toca. `.sticker--pop` del [founder] tampoco
   cambia: ya se usa sobre navy en el sticker [servicio]·A1 del hero de
   esta misma página. */
.sec--inverse .founder img{ border-color:var(--ink-inverse); }

/* --- FAQ en rejilla de landing (`.lp-faq`) -----------------------------
   Reutiliza `.faq-grid`/`.faq-cell`/`.faq-card`/`.faq-card__x`/
   `.faq-panel` de pages.css (fila "FAQ en rejilla" de design-system.md
   §4) TAL CUAL: esas reglas NO están scoped a `.page-home` — son clases
   globales y pages.css se carga en todas las páginas — así que ya
   heredamos fondo/borde/hover/reduced-motion/degradado móvil ≤880px sin
   escribir nada. pages.css e index.php NO se tocan.
   Lo único que cambia de verdad es la GEOMETRÍA: la home tiene 12
   preguntas en 3 columnas y esta landing 8 en 4 — el nº de columnas y
   los `nth-child` que anclan el panel overlay (por columna/fila) no
   valen igual. Selector compuesto `.lp-faq.faq-grid`/`.lp-faq .faq-cell`
   (especificidad más alta que la `.faq-grid`/`.faq-cell` de pages.css,
   mismo criterio que `.svc-hero.lp-hero`/`.inc-grid.lp-guarantees`) para
   ganar SIEMPRE sin depender del orden de carga, y se pisan EXPLÍCITAMENTE
   las 4 columnas (1/5 izq, 2-3/6-7 centro, 4/8 derecha) — no basta con
   sobrescribir solo lo distinto, porque algunos nth-child de 3 columnas
   de la home (p.ej. `nth-child(3n-1)`, que cae también en la celda 5 de
   una rejilla de 8) seguirían ganando por especificidad si no se anulan
   uno a uno. Fila 2 (celdas 5-8) abre hacia arriba, igual que la última
   fila de la home. */
.lp-faq.faq-grid{ grid-template-columns:repeat(4, 1fr); }
.lp-faq .faq-cell:nth-child(4n+1) .faq-panel{ left:0; right:auto; transform:none; }
.lp-faq .faq-cell:nth-child(4n+2) .faq-panel,
.lp-faq .faq-cell:nth-child(4n+3) .faq-panel{ left:50%; right:auto; transform:translateX(-50%); }
.lp-faq .faq-cell:nth-child(4n) .faq-panel{ left:auto; right:0; transform:none; }
.lp-faq .faq-cell:nth-child(n+5) .faq-panel{ top:auto; bottom:calc(100% + 10px); }
@media (max-width:880px){
  .lp-faq.faq-grid{ grid-template-columns:1fr; }
  /* R2-R1: en movil el panel va en flujo bajo la tarjeta; hay que anular los
     nth-child de overlay (left/right/transform) que si no lo empujan fuera. */
  .lp-faq .faq-cell:nth-child(n) .faq-panel{
    position:static; left:auto; right:auto; top:auto; bottom:auto; transform:none; width:auto;
  }
}

/* =====================================================================
   RONDA DE SEO 2026-07-26 — vocabulario de búsqueda en las 6 landings
   =====================================================================
   Diagnóstico de partida: el copy está aprobado y convierte, pero no dice
   ni una vez las palabras que la gente teclea (`automatizar Excel`,
   `macros`, `VBA`, `software de gestión`, `software de auditoría`,
   `certificado digital` aparecían CERO veces en todo el sitio). No se
   reescribe nada de lo aprobado: se añaden tres piezas.

   1. `.svc-servicio` — una línea bajo el H1 que dice, en llano, qué
      servicio es esto. El H1 es de marca ("Convierte ese Excel que da
      miedo tocar…") y funciona; esta línea es la traducción a lo que se
      busca. Va en mono y pequeña, para que se lea como un dato técnico y
      no compita con el `.lead`, que es el que tiene que enamorar.
   2. `.lp-ficha` — la ficha de datos duros al pie de la FAQ (servicio,
      alcance, plazo, precio, qué incluye, permanencia). Es lo que un
      modelo de lenguaje puede citar entero sin equivocarse, y lo que un
      visitante con prisa busca sin leerse la página.
   3. `.lp-alias` — la línea de sinónimos. Cubre de un plumazo el hueco
      léxico de cada servicio sin tocar una sola frase del copy.

   Las tres van sobre fondo claro (`.sec--surface`) salvo `.svc-servicio`,
   que vive en el hero NAVY: de ahí los dos juegos de color. */
/* Mismo cuerpo que el `.lead` que va justo debajo (correccion de Fabio,
   2026-07-27): con `--fs-mono-sm` esta linea se leia como microcopia y no
   como lo que es, la definicion del servicio. Dos parrafos seguidos a
   tamaños distintos se ven descuadrados. Se mantiene la mono y el color mas
   apagado: eso ya basta para que no compita con el lead. */
/* Misma tipografia y mismo cuerpo que el `.lead` que va justo debajo
   (correccion de Fabio, 2026-07-27): en mono y a otro tamaño, los dos
   parrafos parecian de dos sitios distintos y encima iban pegados. Ahora
   solo se diferencian por lo que toca: el sintagma en lila y el aire que
   los separa. */
.svc-servicio {
  font-size: var(--fs-lead); line-height: 1.5;
  color: var(--ink-inverse-2); max-width: 34rem;
  margin: var(--s-4) 0 var(--s-4);
}
.svc-servicio b { color: var(--c-lila); font-weight: 600; }

.lp-ficha { margin-top: var(--s-8); }
.lp-ficha__dl {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 0;
  border: 1px solid var(--card-border); border-radius: var(--card-radius);
  background: var(--bg-surface); overflow: hidden;
}
.lp-ficha__dl > div {
  display: flex; gap: var(--s-3); align-items: baseline;
  padding: 13px var(--s-5); border-top: 1px solid var(--c-hairline);
}
/* Las dos primeras celdas no llevan raya arriba: la del borde ya está. */
.lp-ficha__dl > div:nth-child(-n+2) { border-top: 0; }
/* Hairline vertical solo en la columna derecha, como una tabla contable. */
.lp-ficha__dl > div:nth-child(2n) { border-left: 1px solid var(--c-hairline); }
.lp-ficha__dl dt {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-soft); flex: none; min-width: 6.5rem;
}
.lp-ficha__dl dd { margin: 0; font-size: .9375rem; color: var(--ink-body); }
.lp-alias {
  margin-top: var(--s-4); font-size: var(--fs-sm); color: var(--ink-soft);
}
.lp-alias b { font-family: var(--font-mono); color: var(--ink); font-weight: 600; }
/* "Ver también": el enlazado interno de cada landing en un solo sitio. La
   regla que fija esta ronda es que toda landing enlace a (1) otro
   servicio, (2) un producto y (3) /casos. /macroning y /smartaudit tenían
   CERO enlaces salientes a ninguna de las tres cosas. */
.lp-alias--ver { margin-top: var(--s-3); }
.lp-alias--ver a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }
.lp-alias--ver a:hover { color: var(--accent); }
@media (max-width: 720px) {
  .lp-ficha__dl { grid-template-columns: 1fr; }
  .lp-ficha__dl > div:nth-child(2) { border-top: 1px solid var(--c-hairline); }
  .lp-ficha__dl > div:nth-child(2n) { border-left: 0; }
  .lp-ficha__dl > div { flex-direction: column; gap: 2px; }
}

/* =====================================================================
   PIELES DE LA FICHA (2026-08-18) — rediseño aprobado por Fabio sobre la
   propuesta de 4 direcciones.
   La ficha entró en la ronda 2 de SEO (2026-07-26) sin piel propia: una
   `<dl>` con borde, al pie de la FAQ. Leía como el pie legal de un
   formulario y no como información útil. Se le dan DOS pieles, ambas
   sobre el MISMO HTML de datos (la `.lp-ficha__dl` de arriba no se toca):

     .lp-ficha--modulo  → las cuatro landings que NO son de Excel
     .lp-ficha--hoja    → A1 y A2

   ⚠️ El contenido sigue SIEMPRE visible y en el mismo orden en las seis.
   Nada de `<details>`: si hay que hacer clic para verlo, se pierde medio
   valor para búsqueda y para que un modelo lo cite. Esa es la razón de
   ser del bloque, no un detalle de estilo.
   --------------------------------------------------------------------- */

/* --- C · módulo con cabecera navy -----------------------------------
   Deja de ser un pie y pasa a ser un módulo de cierre con nombre propio.
   La banda navy es lenguaje que la web ya usa en `.sec--inverse`, así que
   no inventa nada. El borde exterior es el propio navy: sin él, la banda
   flota sobre el cuerpo blanco y se ve un escalón en las esquinas. */
.lp-ficha--modulo {
  border: 1px solid var(--c-navy); border-radius: var(--r-lg); overflow: hidden;
}
.lp-ficha--modulo .lp-ficha__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  background: var(--c-navy); color: var(--ink-inverse);
}
.lp-ficha--modulo .lp-ficha__head h3 {
  margin: 0; font-size: 1.05rem; font-weight: 600; letter-spacing: -.01em;
}
.lp-ficha--modulo .lp-ficha__head span {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--c-lila);
}
/* La `<dl>` pierde su propio marco: aquí el marco lo pone el módulo. */
.lp-ficha--modulo .lp-ficha__dl { border: 0; border-radius: 0; }
/* Sinónimos como chips en un pie propio, en vez de un párrafo gris que no
   lee nadie. Mismo texto y mismo orden: solo cambia el envoltorio. */
.lp-ficha__chips { padding: var(--s-4) var(--s-5); background: var(--bg-calm); border-top: 1px solid var(--card-border); }
.lp-ficha__chips > b {
  display: block; margin-bottom: var(--s-2);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft); font-weight: 700;
}
.lp-ficha__chips ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.lp-ficha__chips li {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-body);
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: 999px; padding: 3px 10px;
}

/* --- D · hoja de cálculo (A1 y A2) ----------------------------------
   La ficha servida como un Excel: barra de columnas, barra de fórmulas y
   referencia de celda por fila. Es la única piel que solo puede ser de
   esta casa, y reaprovecha la metáfora que A1 y A2 ya usan en su caja de
   sí/no (`.lp-sino-sheet`). NO se pone en /macroning, /smartaudit, B1 ni
   B2: ahí el guiño al Excel está fuera de sitio.
   Las referencias A2..A9 salen de un contador CSS, no del HTML: así el
   bloque de datos sigue siendo el mismo en las seis páginas y no hay
   numeración que mantener a mano. Arrancan en A2 porque A1 es la fila de
   encabezado de la hoja. */
.lp-ficha--hoja { border: 1px solid var(--c-border); border-radius: var(--r-md); overflow: hidden; }
.lp-ficha--hoja .lp-ficha__hoja-bar,
.lp-ficha--hoja .lp-ficha__fx { display: grid; grid-template-columns: 44px 1fr; align-items: center; }
.lp-ficha--hoja .lp-ficha__hoja-bar {
  background: var(--bg-calm); border-bottom: 1px solid var(--card-border);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
}
.lp-ficha--hoja .lp-ficha__hoja-bar i { font-style: normal; }
.lp-ficha--hoja .lp-ficha__hoja-bar b {
  font-weight: 400; padding: 7px var(--s-4); border-left: 1px solid var(--card-border);
}
.lp-ficha--hoja .lp-ficha__fx {
  border-bottom: 1px solid var(--card-border); background: var(--bg-surface);
  font-family: var(--font-mono); font-size: .78rem;
}
.lp-ficha--hoja .lp-ficha__fx i {
  font-style: normal; text-align: center; color: var(--ink-soft); font-weight: 700; padding: 8px 0;
}
.lp-ficha--hoja .lp-ficha__fx code {
  padding: 8px var(--s-4); border-left: 1px solid var(--card-border); color: var(--ok-text);
}
/* La `<dl>` pasa de 2 columnas a filas de hoja: ref + campo + valor. */
.lp-ficha--hoja .lp-ficha__dl {
  border: 0; border-radius: 0; display: block; counter-reset: sp-fila 1;
}
.lp-ficha--hoja .lp-ficha__dl > div {
  display: grid; grid-template-columns: 44px 8.5rem 1fr;
  gap: 0; padding: 0; border-top: 1px solid var(--card-border); border-left: 0;
}
.lp-ficha--hoja .lp-ficha__dl > div::before {
  counter-increment: sp-fila; content: "A" counter(sp-fila);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  text-align: center; padding: 11px 0; background: var(--bg-calm);
}
.lp-ficha--hoja .lp-ficha__dl dt {
  min-width: 0; padding: 11px var(--s-3);
  border-left: 1px solid var(--card-border);
  font-size: .78rem; color: var(--ink); font-weight: 500;
}
.lp-ficha--hoja .lp-ficha__dl dd {
  padding: 11px var(--s-4); border-left: 1px solid var(--card-border);
}
@media (max-width: 640px) {
  /* En estrecho no caben tres columnas: la celda-ref se queda, el campo y
     el valor se apilan en la segunda. */
  .lp-ficha--hoja .lp-ficha__dl > div { grid-template-columns: 34px 1fr; }
  .lp-ficha--hoja .lp-ficha__dl > div::before { grid-row: 1 / span 2; }
  .lp-ficha--hoja .lp-ficha__dl dt { padding-bottom: 0; }
  .lp-ficha--hoja .lp-ficha__dl dd { padding-top: 2px; }
}

/* La FAQ de landing pasa de 8 a 10 preguntas en las cuatro páginas de
   servicio (rejilla de 4 columnas → última fila de 2). Se centran, para
   que no queden colgando a la izquierda con dos huecos a la derecha. */
.lp-faq .faq-cell:nth-child(9):nth-last-child(2) { grid-column: 2; }
@media (max-width: 880px) {
  .lp-faq .faq-cell:nth-child(9):nth-last-child(2) { grid-column: auto; }
}

/* =====================================================================
   RRSS de landing (R2 Tarea V3.4, petición Fabio: "ese módulo tiene que
   estar en casi todas las páginas") — sección "también en redes" entre
   FAQ y el CTA final de A1/A2. Duplicado MÍNIMO bajo `.lp-rrss-*` de las
   clases home-scoped de pages.css (`.home-rrss-grid`/`.home-rrss`/
   `.home-rrss-card`/`.home-rrss-whatsapp`/`.rrss-pause`/
   `.home-rrss-clips`/`.rrss-clip`, sección "RRSS: tarjetas + marcos
   verticales" de pages.css): mismo diseño 1:1 (rejilla 1.7fr/1fr, 2×2 de
   clips a `aspect-ratio:9/16`), nombres de clase propios para que un
   cambio futuro en el home NO arrastre a las landings (y viceversa).
   `.whatsapp-link` (la clase BASE sin prefijo `home-`) SÍ es un
   componente compartido de verdad — la usa `footer.php` en toda la web,
   documentado como tal en su propio comentario de pages.css — así que se
   reutiliza tal cual vía `.whatsapp-link.lp-rrss-whatsapp`, sin duplicar
   layout base. Nombres de clase nuevos (no reutiliza `.rrss-clip`/
   `.home-rrss-grid` literales) así que, a diferencia de `.lp-sino-sheet`/
   `.lp-faq`, no hace falta pelear ninguna cascada de pages.css: cero
   selectores compuestos. `[data-lp-rrss]` ancla el JS de pausa
   (`landing.js`, `initLpRrss()`), puerto 1:1 del script inline de
   index.php. ===================================================== */
.lp-rrss-grid{ display:grid; grid-template-columns:1.7fr 1fr; gap:var(--s-8); align-items:start; }

.lp-rrss-cards{ display:grid; grid-template-columns:1fr; gap:var(--s-4); }
.lp-rrss-card{
  display:block; background:var(--bg-surface); border:1px solid var(--card-border);
  border-radius:var(--card-radius); padding:26px; text-decoration:none; color:inherit;
  transition:transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop), border-color var(--t-base);
}
.lp-rrss-card svg{ width:26px; height:26px; color:var(--accent); margin-bottom:var(--s-4); display:block; }
.lp-rrss-card h3{ font-size:1.05rem; margin-bottom:var(--s-1); }
.lp-rrss-card .handle{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--accent-2);
  display:block; margin-bottom:var(--s-3);
}
.lp-rrss-card p{ font-size:var(--fs-sm); }
.lp-rrss-card:hover{ transform:var(--card-hover-move); box-shadow:var(--card-hover-shadow); border-color:var(--ink); }

.lp-rrss-whatsapp{
  margin-top:var(--s-6);
  color:var(--ok-text); background:var(--bg-surface); border:1.5px solid var(--ok);
  padding:11px 18px; border-radius:var(--r-sm);
  transition:transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
.lp-rrss-whatsapp svg{ color:var(--ok); }
.lp-rrss-whatsapp:hover{ color:var(--ok-text); box-shadow:4px 4px 0 var(--ok); transform:translate(-2px, -2px); }

/* Botón de pausa (WCAG 2.2.2) de los 4 clips: mismo aspecto mono discreto
   que `.rrss-pause` del home, bajo el enlace de WhatsApp. */
.lp-rrss-pause{
  display:flex; align-items:center; gap:8px; width:fit-content;
  margin-top:var(--s-4);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-body);
  background:transparent; border:1px solid var(--card-border); border-radius:var(--r-xs);
  padding:10px 14px; cursor:pointer;
}
.lp-rrss-pause:hover{ border-color:var(--ink); color:var(--ink); }

.lp-rrss-clips{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s-4); align-content:start; }
.lp-rrss-clip{
  display:block; position:relative; overflow:hidden;
  aspect-ratio:9 / 16; /* = ratio real del archivo: cover no recorta */
  border:2px solid var(--ink); border-radius:var(--r-sm); box-shadow:var(--shadow-b1);
  background:var(--c-navy-2);
  transform:rotate(var(--rot, -2deg));
  transition:transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
.lp-rrss-clip video{ width:100%; height:100%; object-fit:cover; display:block; }
.lp-rrss-clip:hover{ transform:rotate(0deg) translateY(-4px); box-shadow:var(--shadow-b3); }

@media (max-width:880px){
  .lp-rrss-grid{ grid-template-columns:1fr; }
  .lp-rrss-clips{
    display:flex; overflow-x:auto; gap:var(--s-4); padding-bottom:var(--s-2);
    scrollbar-width:thin;
  }
  .lp-rrss-clip{ flex:0 0 128px; width:auto; height:auto; min-height:0; aspect-ratio:9 / 16; }
}

/* =====================================================================
   R1 (2026-07-16) — Tarea U6: A2 completa. ÚNICO CSS de verdad nuevo de
   esta tarea (todo lo demás de la página reutiliza tal cual lo
   construido en U1-U5: fxbar, kpis, versus, calculadora, mapa, sellos,
   casos, founder, faq — cero cambios ahí). Registrado en
   design-system.md §4.
   ===================================================================== */

/* --- Collage del CAMBIO (hero A2, U6.3) --------------------------------
   Composición DISTINTA a la de A1 (3 piezas absolutas libres sobre un
   lienzo de altura fija): aquí es una secuencia antes→flecha→después, así
   que esta variante NO combina con la clase base `.lp-hero__collage` —
   esa base fuerza CUALQUIER `.media-frame` descendiente a
   position:absolute/width:62%/top:0/left:0 (pensado para su composición
   de 3 piezas propia): heredarla habría obligado a pelear esa regla para
   las DOS media-frame de aquí. Vive en su propio bloque, reutilizando
   `.media-frame`/`.mock-sheet--rich`/`.mock-crm`/`.chip` sin tocarlos: lo
   único nuevo es el ENVOLTORIO. */
.lp-hero__collage--cambio{ position:relative; padding-top:28px; }
/* R3 (corrección Fabio): el sticker [en la nube] pasa de left:6% (sobre
   la pieza __antes, la hoja de Excel) a right:6% (sobre __despues, el
   browser frame app.tuempresa.com) — es la pieza a la que se refiere "en
   la nube". Único hijo .sticker de este contenedor, cambio seguro sin
   tocar el resto del collage. */
/* R2-R1 (Fabio smoke movil): el sticker [en la nube] se ancla a
   .lp-cambio__despues (la pantalla de la app web) en vez de al collage, para
   que en movil (donde el collage se apila) siga cayendo sobre la app y no
   sobre la hoja de Excel. .lp-cambio__despues ya es position:relative. */
.lp-cambio__despues > .sticker{ position:absolute; top:-14px; right:6%; z-index:4; }

.lp-cambio{ display:flex; align-items:center; gap:var(--s-4); }
.lp-cambio__antes{ flex:1 1 0; min-width:0; }
.lp-cambio__antes .media-frame{ transform:rotate(-2deg); }
/* Chip "v3_FINAL_def.xlsx" tachado: copia duplicada y obsoleta del mismo
   archivo que la mini-hoja de al lado — el guiño de caos de versiones que
   resuelve la migración (U6.3). */
.lp-cambio__strike{
  display:inline-block; margin-top:var(--s-3);
  text-decoration:line-through; text-decoration-color:var(--danger); text-decoration-thickness:2px;
}
.lp-cambio__arrow{ flex:none; width:44px; height:24px; }
.lp-cambio__arrow path{ fill:none; stroke:var(--hl); stroke-width:7; stroke-linecap:round; stroke-linejoin:round; }
.lp-cambio__despues{ position:relative; flex:1 1 0; min-width:0; }
.lp-cambio__despues .media-frame{ transform:rotate(1deg); }
/* Mini-frame de MÓVIL solapando la esquina del browser (guiño de
   multidispositivo, U6.3): mismo lenguaje que `.lp-hero-card` de A1
   (borde --ink 2px + --shadow-b3 morada, ya probada sobre navy), nombre
   propio porque aquí ancla al browser de ESTA variante, no al collage de
   A1. Sin riesgo de sangrado de sombra (trampa §3 del design-system): el
   offset negativo se queda dentro de `.svc-hero.lp-hero`, que recorta con
   overflow:hidden y deja aire de sobra alrededor — mismo razonamiento ya
   documentado para el collage de A1. */
.lp-cambio__mobile{
  position:absolute; right:-8%; bottom:-10%; z-index:2;
  width:30%; padding:8px 7px;
  background:var(--bg-surface); border:2px solid var(--ink); border-radius:var(--r-md);
  box-shadow:var(--shadow-b3);
  transform:rotate(-4deg);
}
.lp-cambio__mobile-row{ display:block; width:100%; height:8px; margin-bottom:5px; border-radius:2px; background:var(--c-hairline); }
.lp-cambio__mobile-row:last-child{ margin-bottom:0; }

/* Fila de chips de ventaja bajo el collage (multiusuario · multidispositivo
   · trazable · sin versiones, U6.3): reutiliza `.chip` (ya mono, "chips
   mono tipo terminal" del brief) sin tocarlo, solo el layout de fila. */
.lp-cambio__chips{ display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-5); }

@media (max-width:880px){
  .lp-cambio{ flex-direction:column; }
  .lp-cambio__arrow{ width:24px; height:44px; transform:rotate(90deg); }
}
@media (max-width:560px){
  .lp-cambio__mobile{ display:none; }
}

/* --- Ventajas A2: check con mini-icono de trazo + celda destacada
   (U6.6) --------------------------------------------------------------
   Pedido explícito de Fabio: "cada una con mini-icono de trazo, .fico
   style" en vez del glifo `<i>✓</i>` de A1. NO reutiliza `.fico` tal
   cual (esa clase trae una caja de 46px + fondo + sombra pensada para un
   icono flotante SUELTO: repetida 8 veces dentro de una rejilla densa
   pesaría demasiado) — solo su lenguaje de TRAZO (stroke sin relleno,
   mismo stroke-linecap/linejoin que `.fico svg`). Color --ok: mismo
   4.65:1 sobre --bg-inverse ya verificado a mano para el check de A1
   (ver comentario de `.lp-ventajas` más arriba), no se repite el cálculo.
   Las DOS primeras ventajas (multiusuario/multidispositivo) llevan
   además `--top`: celda con acento --hl mezclado sobre el navy de la
   sección (no sobre transparent, para que el tinte sea previsible pase
   lo que pase detrás) — SON LAS QUE FABIO SUBRAYA, pedido explícito
   ("celda doble o acento --hl"); se elige el acento de fondo y no la
   celda doble para no romper el 4×2 en un 4×2×… irregular. Fondo, no
   texto: "amarillo nunca es color de texto" (§9) no aplica aquí.
   Verificado en pantalla (QA orquestador-agente): 18% de tinte a secas
   quedaba demasiado sutil para "MUY destacadas" — se sube a 30% + un
   borde interior sólido en --hl (`box-shadow: inset`, no `border`, para
   no desplazar la rejilla de hairlines de `.lp-ventajas` con un box
   distinto). */
.lp-ventajas__ico{ flex:none; width:20px; height:20px; }
.lp-ventajas__ico svg{ width:100%; height:100%; fill:none; stroke:var(--ok); stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.lp-ventajas__item--top{
  background:color-mix(in srgb, var(--hl) 30%, var(--bg-inverse));
  box-shadow:inset 0 0 0 2px var(--hl);
}

/* ---- LANDINGS FAMILIA B (sesión 5) ----
   .lp-bar-b — barra temática decorativa bajo el topbar de B1/B2
   (includes/lp-bar-b.php), equivalente ESTRUCTURAL de .lp-fxbar (mismo
   hueco, mismo aria-hidden, mismo tamaño de fuente --fs-mono-sm) pero con
   lenguaje propio de B: checklist de expediente (B1) o estado de montaje
   por módulos (B2), nunca fórmulas de Excel. Token de texto tenue: el
   sistema no tiene --ink-mute, el real es --ink-soft (alias de --c-mute,
   ver tokens.css §3), así que is-off usa --ink-soft. */
.lp-bar-b{ background:var(--bg-calm); border-bottom:1px solid var(--card-border); font-family:var(--font-mono); font-size:var(--fs-mono-sm); }
.lp-bar-b__row{
  display:flex; align-items:center; gap:var(--s-4);
  padding-top:9px; padding-bottom:9px; /* .wrap ya da el padding horizontal, calco de .lp-fxbar__row */
}
.lp-bar-b__label{ color:var(--accent); }
.lp-bar-b__item{ display:inline-flex; align-items:center; gap:var(--s-2); color:var(--ink-body); }
.lp-bar-b__mark{ width:8px; height:8px; border-radius:50%; flex:none; }
.lp-bar-b__item.is-ok{ color:var(--ok-text); }
.lp-bar-b__item.is-ok .lp-bar-b__mark{ background:var(--ok); }
.lp-bar-b__item.is-curso{ color:var(--ink); }
.lp-bar-b__item.is-curso .lp-bar-b__mark{ background:var(--accent); }
.lp-bar-b__item.is-off{ color:var(--ink-soft); }
.lp-bar-b__item.is-off .lp-bar-b__mark{ background:transparent; border:1.5px solid var(--ink-soft); }
/* Separador visual SOLO en la variante módulos (B2): toda la barra es
   aria-hidden, así que un ::before decorativo no molesta a lectores. */
.lp-bar-b--modulos .lp-bar-b__item + .lp-bar-b__item::before{ content:"\00b7"; margin-right:var(--s-2); color:var(--ink-soft); }
@media (max-width:880px){
  /* Carrusel compacto: la tira pasa a una ventana de 1 linea; los pasos se
     apilan absolutos y se muestran de uno en uno. Fallback (sin is-live):
     el primer paso fijo. */
  .lp-bar-b__items{ position:relative; display:block; height:1.5em; overflow:hidden; flex:1 1 auto; min-width:0; }
  .lp-bar-b__item{ position:absolute; left:0; top:0; white-space:nowrap; opacity:0; }
  .lp-bar-b__item:first-child{ opacity:1; }                 /* fallback: primer paso */
  .lp-bar-b__items.is-live .lp-bar-b__item{ opacity:0; transition:opacity var(--t-base); }
  .lp-bar-b__items.is-live .lp-bar-b__item.is-cur{ opacity:1; }
  /* el paso pendiente (is-off) YA no se oculta: participa en el carrusel */
  /* el separador de la variante modulos (punto entre items) sobra cuando
     cada paso se muestra suelto en su ventana de 1 linea */
  .lp-bar-b--modulos .lp-bar-b__item + .lp-bar-b__item::before{ content:none; }
}

/* =====================================================================
   Sesión 5 (2026-07-17) — Tarea 2: B1 (onboarding-clientes) hero
   collage-flujo propio, KPIs pegados y versus dolor/solución.
   Registrado en design-system.md §4.
   ===================================================================== */

/* --- Collage-flujo del hero (B1) ----------------------------------------
   Variante PROPIA de familia B: ni hoja Excel (A1) ni antes/después de
   navegador (A2) — la metáfora es un ALTA que se monta sola. El HTML lleva
   AMBAS clases (`lp-hero__collage lp-hero__collage--flow`): se reutiliza
   el contenedor `position:relative`/`min-height` de la base tal cual
   (redeclarado aquí por claridad, sin coste — mismo valor de fondo que
   pretende dar la base), pero la composición de piezas es 100% propia,
   igual que `.lp-hero__collage--cambio` no combina composición con la
   base (esa base fuerza CUALQUIER `.media-frame` a su propia disposición
   de 3 piezas absolutas; aquí no hay `.media-frame`, así que esa regla no
   aplicaría de todos modos, pero el POSICIONAMIENTO de `.lp-hero-card` sí
   es genérico y global — hace falta ganarlo con selectores propios, ver
   abajo). `.lp-flow-card` hace de pieza ANCLA grande (mismo rol
   compositivo que el `.media-frame` de A1: arriba-izquierda, la más
   ancha) y los DOS `.lp-hero-card` (YA existen, de A1, reutilizados TAL
   CUAL — solo re-posicionados) asoman por las esquinas superior-derecha e
   inferior-izquierda del ancla, mismo lenguaje de "solapado equilibrado"
   que la composición original de A1 (top:24%/top:52%), con valores
   propios. El HTML no lleva modificador tipo `--dash` en el segundo
   `.lp-hero-card` (ambos son `.lp-hero-card` a secas): se diferencian
   vía :first-of-type/:last-of-type, scope bajo `.lp-hero__collage--flow`
   para no tocar ni la variante de A1 ni la de A2. Sombra en --shadow-b3
   (morado, visible sobre navy), mismo criterio que el resto de piezas de
   collage del sitio — trampa §3 (sombras sólidas sangran 8px): ninguna
   pieza toca el borde de `.svc-hero.lp-hero` (overflow:hidden), igual que
   A1/A2. */
.lp-hero__collage--flow{ position:relative; min-height:330px; }
/* R2-R1 (Fabio smoke movil): [en automatico] se ancla a la .lp-flow-card
   (el alta que se monta sola) en vez de al collage, para que en movil (apilado)
   caiga sobre la tarjeta del automatismo y no sobre la de "pago recibido".
   R5 (2026-07-23, feedback Fabio: "la etiqueta [en automático] está cortada,
   ponla por encima"): la .lp-flow-card llevaba overflow:hidden (para recortar
   su franja superior a las esquinas redondeadas), y eso RECORTABA el sticker
   asomado a top:-14px. Se retira ese overflow y en su lugar la propia franja
   __bar redondea sus esquinas superiores (ver .lp-flow-card__bar) — así la
   franja sigue ceñida a la card y el sticker se ve ENTERO por encima del
   borde. Se sube a top:-18px para que asome del todo, no a medias. */
.lp-flow-card > .sticker{ position:absolute; top:-18px; right:6%; z-index:5; }

.lp-hero__collage--flow > .lp-flow-card{
  position:absolute; top:0; left:0; width:64%; z-index:1;
  transform:rotate(-2deg);
}
.lp-hero__collage--flow > .lp-hero-card:first-of-type{
  position:absolute; top:22%; right:0; left:auto; bottom:auto;
  width:50%; z-index:2;
  transform:rotate(3deg);
}
.lp-hero__collage--flow > .lp-hero-card:last-of-type{
  position:absolute; top:59%; left:4%; right:auto; bottom:auto;
  width:46%; z-index:3;
  transform:rotate(-3deg);
}

/* Card central del flujo (nueva, pieza ancla): franja de estado superior
   (mono, fondo --fam-b-soft, texto --accent) + lista de pasos con guía
   punteada vertical morada. Blanca sobre navy, mismo lenguaje de tarjeta
   que `.lp-hero-card` (borde --ink 2px + --shadow-b3 + --r-md).
   R5: se retira el `overflow:hidden` que recortaba el sticker [en automático]
   (ver .lp-flow-card > .sticker). La franja superior ya no la recorta la card:
   la propia __bar redondea sus dos esquinas de arriba para seguir ceñida al
   borde (calc(--r-md - 2px) descuenta el grosor del borde de la card). */
.lp-flow-card{
  background:var(--bg-surface); color:var(--ink);
  border:2px solid var(--ink); border-radius:var(--r-md);
  box-shadow:var(--shadow-b3);
}
.lp-flow-card__bar{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--accent); background:var(--fam-b-soft);
  padding:8px 16px;
  border-radius:calc(var(--r-md) - 2px) calc(var(--r-md) - 2px) 0 0;
}
/* Lista de pasos: guía punteada vertical (border-left en el propio `ul`,
   morada al 40% vía color-mix, mismo criterio de translucidez que
   `.lp-hero .svc-meta span`) + marcador de estado por CADA `li` (nunca en
   el `ul`) vía ::before a la izquierda. is-ok = check `✓` en --ok, mismo
   glifo/color que ya usa el codebase para checks verdes de listas
   (`.lp-ventajas__item i`/`.lp-hero-card .ok`: glifo real de fuente en
   --ok, bold, no un icono SVG aparte); is-curso = punto morado --accent,
   mismo lenguaje que `.lp-bar-b__item.is-curso .lp-bar-b__mark` (8px,
   B1 ya lo usa en la barra decorativa de la Tarea 1). */
.lp-flow-card__list{
  list-style:none; margin:0 0 0 var(--s-4); padding:var(--s-3) var(--s-4) var(--s-4) 22px;
  border-left:2px dashed color-mix(in srgb, var(--accent) 40%, transparent);
}
.lp-flow-card__list li{
  position:relative;
  padding:7px 0 7px 4px;
  border-top:1px solid var(--card-border);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-body);
}
.lp-flow-card__list li:first-child{ border-top:0; }
.lp-flow-card__list li::before{
  position:absolute; left:-18px; top:50%; transform:translateY(-50%);
}
.lp-flow-card__list li.is-ok::before{
  content:"✓"; color:var(--ok); font-weight:700; font-size:.75rem;
}
.lp-flow-card__list li.is-curso::before{
  content:""; width:7px; height:7px; border-radius:50%; background:var(--accent);
}

@media (max-width:880px){
  .lp-hero__collage--flow{ min-height:0; display:flex; flex-direction:column; gap:var(--s-5); padding-top:28px; }
  /* :first-of-type/:last-of-type (0,3,0) en las reglas de escritorio de
     arriba pesan MÁS que un `.lp-hero-card` a secas (0,2,0): sin repetir
     aquí los mismos selectores con pseudo-clase, este reset de móvil
     perdería la cascada y el position:absolute de escritorio se colaría
     en el apilado — comprobado en vivo (390px) antes de este ajuste. */
  /* flow-card en position:relative (no static) para anclar el sticker
     [en automatico] que ahora vive dentro de ella; sigue apilada en el flujo.
     overflow:visible para que el sticker SOBRESALGA por arriba (flotando sobre
     la tarjeta, como el resto de stickers) en vez de quedar recortado por el
     overflow:hidden de la card. */
  .lp-hero__collage--flow > .lp-flow-card{ position:relative; width:100%; transform:none; overflow:visible; }
  .lp-hero__collage--flow > .lp-hero-card:first-of-type,
  .lp-hero__collage--flow > .lp-hero-card:last-of-type{ position:static; width:100%; transform:none; }
  .lp-hero__collage--flow > .lp-hero-card:first-of-type,
  .lp-hero__collage--flow > .lp-hero-card:last-of-type{ box-shadow:var(--shadow-soft); }
}
@media (max-width:560px){
  .lp-hero__collage--flow > .lp-hero-card:last-of-type{ display:none; }
}

/* =====================================================================
   Sesión 5 (2026-07-17) — Tarea 3: B1 demo del alta en vivo (pieza
   interactiva "teatro", sin cálculo), la base 360 (`.lp-ventajas`
   reutilizado TAL CUAL, cero CSS aquí) y los dos packs (`.lp-packs`,
   NUEVO — sustituye a `.svc-packs`/`.svc-pack` de pages.css en esta
   página; esas clases no se tocan). Registrado en design-system.md §4.
   ===================================================================== */

/* --- Simulación del flujo de alta [data-lp-sim] (sesión 5 R1) ----------
   Corrección Fabio sobre la demo-log anterior: "un botón que ponga
   cliente nuevo... y que se vea cómo se va sucediendo el proceso de
   IZQUIERDA A DERECHA". Pipeline de nodos-etapa dentro de una
   .media-frame: nodo origen (trigger, siempre encendido, morado) y 4
   etapas conectadas por conectores con PULSO (un dot morado viaja por la
   línea y a su llegada la etapa se enciende: borde discontinuo→sólido,
   pop de escala y check verde). Dos etapas son DUALES (2 nodos en
   paralelo: correos / CRM+factura) con stagger corto entre ambos.
   Apagado = mismo lenguaje "hueco" que .lp-mods-mini .is-hueco (borde
   dashed, atenuado). El transform del pop no choca con nada: el
   data-reveal vive en el CONTENEDOR .lp-sim, no en los nodos. Móvil
   ≤880px: el pipeline gira a VERTICAL (columna; los conectores pasan a
   segmentos verticales y el pulso viaja hacia abajo). Sin JS o con
   reduce: todo encendido y conectores llenos (estático, nunca una caja
   vacía); el botón solo existe con JS. JS: initLpSim() en landing.js. */
.lp-sim{
  display:flex; flex-direction:column; align-items:center;
  margin:0 auto;
}
.lp-sim__frame{ width:100%; }
.lp-sim__stage{
  display:flex; align-items:center;
  padding:var(--s-4) 0 var(--s-3);
}
.lp-sim__col{
  flex:1 1 0; min-width:0;
  display:flex; flex-direction:column; gap:var(--s-3);
}
.lp-sim__node{
  position:relative;
  border:2px dashed color-mix(in srgb, var(--ink) 28%, transparent);
  border-radius:var(--r-sm);
  background:transparent;
  padding:var(--s-3) var(--s-4);
  opacity:.55;
  transition:opacity var(--t-base), background var(--t-base), border-color var(--t-base);
}
.lp-sim__node b{
  display:block; font-family:var(--font-disp); font-weight:700;
  font-size:.92rem; line-height:1.25; color:var(--ink);
}
.lp-sim__node small{
  display:block; font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  color:var(--ink-body); margin-top:2px;
}
.lp-sim__check{
  position:absolute; top:6px; right:8px;
  font-style:normal; font-weight:700; color:var(--ok);
  opacity:0; transition:opacity var(--t-fast);
}
.lp-sim__node.is-on{
  opacity:1; border:2px solid var(--ink); background:var(--bg-surface);
  box-shadow:var(--shadow-b1);
  animation:lpSimPop .32s var(--ease-pop);
}
.lp-sim__node.is-on .lp-sim__check{ opacity:1; }
.lp-sim__node--trigger{
  opacity:1; border:2px solid var(--accent); background:var(--fam-b-soft);
}
@keyframes lpSimPop{
  0%{ transform:scale(.95); }
  60%{ transform:scale(1.02); }
  100%{ transform:scale(1); }
}
/* Conector: línea discontinua neutra; al dispararse, un dot morado viaja
   (translate) y al completarse la línea se pinta sólida morada. */
.lp-sim__link{
  flex:0 0 2.25rem; position:relative; height:2px;
  --lp-sim-run: calc(2.25rem - 8px);
}
.lp-sim__link::before{
  content:""; position:absolute; inset:0;
  border-top:2px dashed color-mix(in srgb, var(--ink) 25%, transparent);
}
.lp-sim__link::after{
  content:""; position:absolute; left:0; top:0; height:2px; width:0;
  background:var(--accent);
  transition:width .3s linear;
}
.lp-sim__link.is-done::after{ width:100%; }
.lp-sim__link i{
  position:absolute; left:0; top:-3px; width:8px; height:8px;
  border-radius:50%; background:var(--accent); opacity:0;
}
.lp-sim__link.is-firing i{
  opacity:1; animation:lpSimRun .32s linear forwards;
}
@keyframes lpSimRun{
  from{ transform:translate(0,0); }
  to{ transform:translate(var(--lp-sim-run), 0); }
}
.lp-sim__total{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--ink);
  border-top:4px double var(--accent);
  padding-top:var(--s-4); margin-top:var(--s-2);
}
/* R1 (ROADMAP p.1): el total se oculta SIN colapsar (mantiene su caja) y
   el cuerpo del marco tiene un suelo de altura, para que el contenedor no
   cambie de tamaño ni al replay ni al cambiar de caso.

   Los suelos están MEDIDOS, no estimados: se recorrieron los 5 SIM_CASES
   con el total visible y los min-height sueltos, a varios anchos. La
   variación entre casos es de una línea (~20px) porque los textos de cada
   caso envuelven distinto, y además hay dos regímenes según el ancho:

     ancho de .lp-sim  |  alto máximo del body  |  del stage
     <= 1020px         |        320,3           |    272,5
     >= 1100px         |        263,8           |    216,0

   Por eso el suelo es responsivo: con un único valor, o el contenedor
   saltaba en pantallas medianas, o sobraban 60px de hueco en grandes. La
   frontera se pone en 1160px de viewport (= ~1100 de .lp-sim, contando los
   28px de --gutter a cada lado del .wrap). Debajo de 881 no hay suelo: las
   columnas se apilan y el simulador cambia de forma. */
.lp-sim__total{ transition:opacity var(--t-base) var(--ease-out); }
.lp-sim__total.is-pending{ opacity:0; }
.lp-sim__frame .media-frame__body{ min-height:324px; }
.lp-sim__stage{ min-height:276px; }
@media (min-width: 1160px){
  .lp-sim__frame .media-frame__body{ min-height:264px; }
  .lp-sim__stage{ min-height:216px; }
}
@media (prefers-reduced-motion: reduce){
  .lp-sim__total{ transition:none; }
}
@media (max-width: 880px){
  .lp-sim__frame .media-frame__body{ min-height:0; }
  .lp-sim__stage{ min-height:0; }
}
/* Botón "+ Nuevo cliente" (R2): estilo alta-de-CRM. Animación de
   invitación: cada ~3s el botón "se ofrece" (sube con su sombra de
   énfasis un instante) — solo sin hover/active para no pelear con los
   estados del .btn base, y nunca con reduce (media query). */
.lp-sim__play{ display:block; margin:var(--s-5) auto 0; }
.lp-sim__plus{
  font-style:normal; font-family:var(--font-mono); font-weight:700;
  margin-right:4px;
}
@media (prefers-reduced-motion: no-preference){
  .lp-sim__play:not(:hover):not(:active){ animation:lpSimCall 3.2s var(--ease-out) infinite; }
}
@keyframes lpSimCall{
  0%, 82%, 100%{ box-shadow:var(--shadow-b2); transform:none; }
  88%{ box-shadow:var(--shadow-b3); transform:translate(-2px,-2px); }
  94%{ box-shadow:var(--shadow-b2); transform:none; }
}
/* CTA + micro tras `.lp-sim` (sin wrapper propio en el HTML): centrados
   vía combinador de hermanos, mismo criterio que tenía `.lp-demo`. */
.lp-sim + .btn{ display:table; margin:var(--s-5) auto 0; }
.lp-sim + .btn + .micro{ text-align:center; }
/* Móvil: pipeline VERTICAL (columna). Los conectores giran: segmento
   vertical centrado y el pulso viaja hacia abajo. */
@media (max-width: 880px){
  .lp-sim__stage{ flex-direction:column; align-items:stretch; gap:0; }
  .lp-sim__link{
    flex:none; width:2px; height:1.75rem; margin:0 auto;
    --lp-sim-run: calc(1.75rem - 8px);
  }
  .lp-sim__link::before{ border-top:0; border-left:2px dashed color-mix(in srgb, var(--ink) 25%, transparent); }
  .lp-sim__link::after{ width:2px; height:0; transition:height .3s linear; }
  .lp-sim__link.is-done::after{ height:100%; }
  .lp-sim__link i{ left:-3px; top:0; }
  .lp-sim__link.is-firing i{ animation-name:lpSimRunV; }
  @keyframes lpSimRunV{
    from{ transform:translate(0,0); }
    to{ transform:translate(0, var(--lp-sim-run)); }
  }
}
/* Sin JS: todo encendido y quieto (nunca una caja a medias). */
html:not(.js) .lp-sim__node{ opacity:1; border-style:solid; border-color:var(--ink); background:var(--bg-surface); }
html:not(.js) .lp-sim__node--trigger{ border-color:var(--accent); background:var(--fam-b-soft); }
html:not(.js) .lp-sim__check{ opacity:1; }
html:not(.js) .lp-sim__link::after{ width:100%; }
html:not(.js) .lp-sim__play{ display:none; }
/* Estático por reduce (lo aplica initLpSim con .is-static): mismos
   estados finales, sin animaciones. */
.lp-sim.is-static .lp-sim__node{ animation:none; }

/* --- Los dos packs `.lp-packs`/`.lp-pack` (NUEVO) -----------------------
   Protagonistas: sensación de servicio 360, no precio — franja de
   cabecera (`__head`, fondo `--fam-b-soft`) + claim + lista con check
   verde (base) o `+` morado (`is-plus`, lo que SUMA el Premium sobre el
   Básico — mismo contraste de color que `.svc-pack__extra li::before`,
   distinto marcador para que se lea a simple vista qué es nuevo).
   `.lp-pack__todo` (primer ítem del Premium, "Todo lo del Pack Básico")
   lleva un doble borde inferior fino en vez de check: no es una pieza
   más de la lista, es un remate que resume TODO lo anterior, mismo
   lenguaje contable que `.lp-ventajas__total`/`__total` pero en miniatura
   (3px en vez de 4px, no compite con esas filas-suma). `--premium` con
   borde+sombra sólida FIJA (`--shadow-b3`, morada — mismo token que
   `.svc-pack--premium`, `.lp-hero-card`, etc.): siempre encendida, no
   solo al hover (es la card que queremos que gane sin depender de que el
   cursor pase por encima). Sin badge "el que más eligen": la ficha no
   respalda ese dato. Hover SOLO de sombra (nunca transform: las cards
   llevan `data-reveal`, que deja un `transform` inline al terminar el
   reveal — un `:hover` con transform perdería contra ese inline, mismo
   razonamiento que `.lp-mapa__stop`). */
.lp-packs{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s-6);
  align-items:start;
}
.lp-pack{
  background:var(--bg-surface); border:1px solid var(--card-border);
  border-radius:var(--r-md); overflow:hidden;
  box-shadow:var(--shadow-soft);
  transition:box-shadow var(--t-base) var(--ease-out);
}
.lp-pack:hover{ box-shadow:var(--shadow-b2); }
/* Sesión 6, Tarea 4 (parametrización, mismo patrón que --lp-crm-soft/
   --lp-crm-accent de .lp-sino-crm/.lp-mapa__stop--crm): el fondo del
   header y los 3 acentos morados a secas pasan a var(--lp-crm-soft,
   var(--fam-b-soft))/var(--lp-crm-accent, var(--accent)) — así /macroning
   (familia C) puede tintar este componente en navy sin duplicarlo. B1 no
   cambia ni un píxel: fija --lp-crm-soft:var(--fam-b-soft) y
   --lp-crm-accent:var(--fam-b) en su wrapper .svc-page (Sesión 5 Tarea 2),
   y --fam-b === --accent (mismo primitivo --c-pop, tokens.css), así que
   el valor computado es idéntico al hardcodeado anterior. --shadow-b3 NO
   se toca: es el énfasis sólido de TODA la web (`.svc-pack--premium`,
   `.warranty-doc`, etc.), no una seña de familia B. */
.lp-pack__head{
  display:flex; align-items:center; gap:var(--s-3);
  background:var(--lp-crm-soft, var(--fam-b-soft)); padding:var(--s-5) var(--s-6);
}
.lp-pack__head b{ font-family:var(--font-disp); font-size:var(--fs-h3); color:var(--ink); }
.lp-pack__plazo{
  margin-left:auto; flex:none;
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--lp-crm-accent, var(--accent));
}
.lp-pack__claim{ padding:var(--s-5) var(--s-6) 0; color:var(--ink-body); }
.lp-pack__list{ list-style:none; padding:var(--s-4) var(--s-6) var(--s-6); }
.lp-pack__list li{
  position:relative; padding:var(--s-3) 0 var(--s-3) 26px;
  border-bottom:1px solid var(--card-border);
  color:var(--ink-body); font-size:var(--fs-sm);
}
.lp-pack__list li:last-child{ border-bottom:0; }
.lp-pack__list li::before{
  content:"✓"; position:absolute; left:0; top:var(--s-3);
  font-weight:700; color:var(--ok);
}
.lp-pack__list li.is-plus::before{
  content:"+"; font-family:var(--font-mono); color:var(--lp-crm-accent, var(--accent));
}
.lp-pack__todo{ font-weight:700; border-bottom:3px double var(--card-border); }
.lp-pack__todo::before{ color:var(--ink-soft); }
.lp-pack--premium{ border-color:var(--lp-crm-accent, var(--accent)); box-shadow:var(--shadow-b3); }
.lp-pack--premium:hover{ box-shadow:var(--shadow-b3); }

@media (max-width:880px){
  .lp-packs{ grid-template-columns:1fr; }
  /* El head del pack es flex-row con `.lp-pack__plazo` en `flex:none` +
     `margin-left:auto`: en escritorio eso empuja el subtítulo a la derecha
     en una sola línea, pero en móvil no cabe y `.lp-pack` (overflow:hidden)
     lo recorta. Apilamos título y subtítulo y dejamos que el subtítulo
     envuelva a ancho completo. */
  .lp-pack__head{ flex-direction:column; align-items:flex-start; gap:var(--s-2); }
  .lp-pack__plazo{ margin-left:0; flex:initial; white-space:normal; }
}

/* =====================================================================
   Sesión 5 (2026-07-17) — Tarea 6: B2 (sistemas-de-gestion) hero
   collage-módulos propio, KPIs y versus/sí-no calcados de B1 en morado.
   Registrado en design-system.md §4.
   ===================================================================== */

/* --- Collage-módulos del hero (B2) --------------------------------------
   Metáfora propia de B2: un MÓDULO que encaja en un sistema ya montado
   (ni hoja Excel de A1, ni antes/después de A2, ni el alta que se monta
   sola de B1). El HTML lleva AMBAS clases (`lp-hero__collage
   lp-hero__collage--mods`); a diferencia de `--flow` (B1, sin
   `.media-frame` en su composición, así que la base no tenía nada que
   ganar) esta variante SÍ trae un `.media-frame` propio
   (`.lp-hero__sistema`) dentro del collage: la base `.lp-hero__collage
   .media-frame` (A1, más arriba) lo alcanzaría igual
   (position:absolute/width:62%/padding:10px/rotate(-2deg)) si no se gana
   con un selector más específico. `.lp-hero__collage--mods
   .lp-hero__sistema.media-frame` suma 3 clases (0,3,0) contra las 2 de
   la base (0,2,0): gana SIEMPRE, sin depender de qué regla carga
   después — mismo criterio de especificidad explícita que `.sino .box
   .si.lp-sino-crm` o el mapa parametrizado de la Tarea 4, en vez de
   fiarlo al orden de la hoja. (El `padding:10px` de la base SÍ se
   hereda a propósito, sin pelearlo: es un ajuste correcto para
   cualquier `.media-frame` dentro de un collage, mods incluido.) El
   sticker reutiliza el mismo truco que `--flow` (2 clases contra las 2
   de la base `.lp-hero__collage > .sticker`: especificidad IGUAL, gana
   por ir después en el archivo — mismo mecanismo ya en uso, no una
   técnica nueva).
   Composición: `.lp-hero__sistema` (frame, pieza central, ancla
   top:0/left:0/70%) + `.lp-mod-card` (pieza suelta NUEVA, sin ninguna
   regla previa que ganar — nombre de clase propio) rotada 4° y
   solapando la esquina inferior derecha del frame: "el módulo
   acercándose a su hueco". Sombra `--shadow-b3` (morada, mismo criterio
   que el resto de piezas de collage del sitio); trampa §3 (sombras
   sólidas sangran 8px) ya cubierta: el collage vive dentro de
   `.svc-hero.lp-hero` con `overflow:hidden` y aire de sobra alrededor,
   igual que A1/A2/B1. */
/* min-height RECALCULADO en R1 (ROADMAP p.1) al pasar el frame de 4 a 6
   filas + fila de estética: como TODOS los hijos del collage van en
   absolute, este min-height ES la altura de la caja, y de ella cuelga el
   `bottom:-8%` de la .lp-mod-card. Cuentas (tokens actuales, lh-body
   1.65): fila .is-on = 8+19.8+8 = 35.8px, .is-hueco = +2px de borde =
   37.8px, gap 6px, fila estética = 12+12+1+19.8 = 44.8px → cuerpo 293.6px;
   + barra del frame 30.15px + padding/borde 30px = frame ~354px (antes
   ~223px). La card mide ~85px y su borde superior cae en 1.08·H − 85: con
   los 340px de antes quedaba en ~283px y el frame terminaba en ~226px, es
   decir ~49px de aire entre ambos. Para CONSERVAR ese mismo aire con el
   frame ya crecido hace falta 1.08·H ≈ 497px → 460px. Con los 340px
   viejos la card se habría comido las dos últimas filas y los swatches. */
/* R1-b: min-height sube de 460 a 540 porque el layout pasó de "frame +
   card colgando" a "frame arriba + pantalla y card apiladas abajo": hace
   falta alto para las dos piezas inferiores bajo el frame (~354px) sin que
   se solapen con él. Valor calibrado con medición en Chrome. */
.lp-hero__collage--mods{ position:relative; min-height:540px; }
.lp-hero__collage--mods > .sticker{ position:absolute; top:-16px; left:30%; z-index:5; }

/* R1-b: el frame va arriba a lo ancho (70%); debajo, apiladas lado a lado,
   la pantalla de facturación (izq) y la card del módulo suelto (der). Antes
   solo colgaba la card; la segunda pantalla no cabía a la derecha del frame
   sin tapar el texto de los módulos, así que el layout pasa a "frame arriba
   + dos piezas abajo" y el collage crece de alto. */
.lp-hero__collage--mods .lp-hero__sistema.media-frame{
  position:absolute; top:0; left:0; width:70%; z-index:1;
  transform:rotate(-1deg);
}
.lp-hero__collage--mods .lp-mod-card{
  position:absolute; right:2%; bottom:-4%; width:44%; z-index:3;
  transform:rotate(4deg);
}
/* Segunda pantalla flotante (módulo de facturación), abajo-izquierda, bajo
   el frame y al lado de la card. Da la sensación de "varias pantallas del
   programa a la vez" que pedía Fabio. */
.lp-hero__collage--mods .lp-mod-screen{
  position:absolute; left:1%; bottom:0; width:44%; z-index:2;
  transform:rotate(-2deg);
  background:var(--bg-surface); border:2px solid var(--ink);
  border-radius:var(--r-md); box-shadow:var(--shadow-b3); overflow:hidden;
}
.lp-mod-screen__bar{
  display:flex; align-items:center; gap:5px;
  padding:7px 10px; background:var(--c-navy);
}
.lp-mod-screen__bar i{ width:7px; height:7px; border-radius:50%; background:var(--c-navy-2); flex:none; }
.lp-mod-screen__bar span{
  margin-left:6px; font-family:var(--font-mono); font-size:.625rem; color:var(--ink-inverse-2);
}
.lp-mod-screen__rows{
  list-style:none; margin:0; padding:9px 11px;
  display:flex; flex-direction:column; gap:7px;
}
.lp-mod-screen__rows li{
  display:flex; align-items:baseline; justify-content:space-between; gap:8px;
  font-family:var(--font-mono); font-size:.6875rem; color:var(--ink-body);
}
.lp-mod-screen__rows b{ font-weight:700; color:var(--ink-soft); }
.lp-mod-screen__rows b.ok{ color:var(--ok-text); }

/* Card suelta: mismo lenguaje de tarjeta que `.lp-hero-card`/
   `.lp-flow-card` (borde `--ink` 2px + `--shadow-b3` + `--r-md`), título
   en `--font-disp` (no mono: es el NOMBRE de un módulo, no un timestamp
   de terminal) y sub-línea mono en `--accent` (morado de marca,
   coherente con el resto de acentos de B2). */
.lp-mod-card{
  background:var(--bg-surface); color:var(--ink);
  border:2px solid var(--ink); border-radius:var(--r-md);
  box-shadow:var(--shadow-b3); padding:var(--s-4);
}
.lp-mod-card b{
  display:block; font-family:var(--font-disp); font-size:1rem; font-weight:700;
  color:var(--ink); margin-bottom:4px;
}
.lp-mod-card small{
  display:block; font-family:var(--font-mono); font-size:.6875rem; color:var(--accent);
}

/* Mini-lista de apps dentro del frame (sustituye al `.mock-crm` que
   traía el hero anterior de B2): cada fila es un módulo ya encendido
   (`.is-on`, dot `--ok`) o el hueco que espera el siguiente módulo
   (`.is-hueco`, borde punteado). OJO contraste: `.media-frame__body`
   pinta sobre `--bg-surface` (blanco, confirmado en tokens.css — NO
   sobre navy), así que el texto usa `--ink-body` normal (nunca un tono
   pensado para navy) y el hueco usa `--accent` (el morado DE MARCA,
   pensado para fondos claros) en vez de `--c-lila` (ese token es,
   textual, "counters/métricas SOBRE NAVY con glow" — tokens.css:24 — se
   leería plano/apagado aquí). */
.lp-mods-mini{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px; }
.lp-mods-mini li{
  display:flex; align-items:center; gap:var(--s-3);
  padding:8px 10px; border-radius:var(--r-xs);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
}
.lp-mods-mini li i{ width:8px; height:8px; border-radius:50%; flex:none; font-style:normal; }
.lp-mods-mini li.is-on{ color:var(--ink-body); }
.lp-mods-mini li.is-on i{ background:var(--ok); }
.lp-mods-mini li.is-hueco{
  color:var(--accent); border:1px dashed var(--accent);
}
.lp-mods-mini li.is-hueco i{ background:none; border:1.5px dashed var(--accent); }

/* R1 (ROADMAP p.1): fila de "estética" en el mock del hero, para que el
   panel lea como una pantalla de configuración de un programa y no como
   una lista suelta. Decorativa (aria-hidden): los swatches funcionales de
   verdad viven en el configurador de #lp-mods, más abajo en la página. */
.lp-mods-mini__theme {
  display: flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-3); padding-top: var(--s-3);
  border-top: 1px dashed var(--card-border);
}
.lp-mods-mini__theme-lbl {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-soft); margin-right: var(--s-2);
}
.lp-mods-mini__sw {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--sw, var(--accent));
  border: 1px solid var(--card-border);
}
.lp-mods-mini__sw.is-sel { box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 3px var(--ink); }

@media (max-width:880px){
  .lp-hero__collage--mods{ min-height:0; display:flex; flex-direction:column; gap:var(--s-5); padding-top:28px; }
  .lp-hero__collage--mods .lp-hero__sistema.media-frame,
  .lp-hero__collage--mods .lp-mod-screen,
  .lp-hero__collage--mods .lp-mod-card{ position:static; width:100%; transform:none; }
  .lp-hero__collage--mods .lp-mod-card{ box-shadow:var(--shadow-soft); }
  .lp-hero__collage--mods > .sticker{ position:absolute; top:0; left:auto; right:10px; }
}

/* =====================================================================
   Sesión 5 (2026-07-17) — Tarea 7: B2 contador de herramientas→módulos,
   pieza INTERACTIVA estrella de B2 (pedida por Fabio literalmente),
   sustituye a "módulos componibles" (9 inc-card, retirada). Registrado en
   design-system.md §4.
   ===================================================================== */

/* --- Contador de herramientas [data-lp-mods] -----------------------------
   Al marcar dónde vive hoy la operación (columna izquierda, checkboxes
   nativos) se van encendiendo en paralelo los módulos de UN ÚNICO
   sistema (columna derecha, `.media-frame` reutilizado tal cual): la
   mayor ventaja de B2 hecha interactiva. `landing.js` (`initLpMods`)
   alterna `.is-checked` en cada `.lp-mods__tool` (clase JS, no `:has()`:
   mismo criterio prudente que el resto de estados "marcado" del sitio,
   p.ej. `.lp-bar-b__item.is-ok`) y `.is-on` en cada `<li>` de
   `.lp-mods__sistema` cuyo módulo tenga al menos un checkbox marcado
   (varias herramientas pueden compartir `data-mod`: el módulo se
   enciende una sola vez). Cifra `.lp-mods__n[data-heat]` REUTILIZA los
   mismos `color-mix` de `.lp-calc--sheet__result[data-heat]` (heat 0
   morado suave de marca, 1 ámbar suave, 2/3 rojo creciente): mismo
   lenguaje de "celda que se tiñe según el dato", aquí sobre la cifra en
   vez de sobre toda una celda de hoja. */
.lp-mods__count{ text-align:center; margin-bottom:var(--s-6); }
.lp-mods__count p{ margin:0; }
.lp-mods__n{
  display:inline-block; font-family:var(--font-disp); font-size:var(--fs-display);
  font-weight:700; line-height:1; font-feature-settings:"tnum" 1;
  padding:2px var(--s-3); border-radius:var(--r-xs);
  background:var(--fam-b-soft);
  transition:background var(--t-base) var(--ease-out);
}
.lp-mods__n[data-heat="1"]{ background:color-mix(in srgb, var(--hl) 24%, var(--bg-surface)); }
.lp-mods__n[data-heat="2"]{ background:color-mix(in srgb, var(--danger) 16%, var(--bg-surface)); }
.lp-mods__n[data-heat="3"]{ background:color-mix(in srgb, var(--danger) 26%, var(--bg-surface)); }
.lp-mods__n-label{ font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft); }
.lp-mods__arrow{ font-family:var(--font-mono); font-weight:700; color:var(--accent); margin:0 4px; }
.lp-mods__frase{
  font-family:var(--font-mono); font-style:italic; color:var(--ink-body);
  margin-top:var(--s-3);
  /* Colchón anti-salto (mismo criterio que `.lp-calc--sheet__frase`, sin
     el JS de medición: las 4 frases de esta pieza son cortas, 3 líneas
     cubren de sobra 390px — si a 320px la más larga excediera 3 líneas,
     es ajuste fino de copy, no de mecanismo). */
  min-height:calc(var(--lh-body) * 3em);
}

.lp-mods__grid{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s-6); align-items:start; }
/* R2: el mini configurador va ENCIMA del panel en la columna derecha; la
   columna de herramientas abarca las dos filas. En móvil (1 col) el orden
   natural del DOM ya es tools → configurador → panel. */
@media (min-width: 881px){
  .lp-mods__tools{ grid-column:1; grid-row:1 / span 2; }
  .lp-mods__custom{ grid-column:2; grid-row:1; }
  .lp-mods__panel{ grid-column:2; grid-row:2; }
}
/* R2 (Fabio, "dale un poco de espacio por arriba al CTA"): el botón tras
   el contador respira más y queda centrado como en la sim de B1. */
.lp-mods + .btn{ display:table; margin:var(--s-8) auto 0; }
.lp-mods + .btn + .micro{ text-align:center; }

/* Columna izquierda: checkboxes, UN bloque por fila (regla del sistema:
   nunca dos inputs comparten línea). */
.lp-mods__tools{ border:0; padding:0; margin:0; display:flex; flex-direction:column; gap:var(--s-3); }
.lp-mods__tool{
  display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-4); border:1px solid var(--card-border); border-radius:var(--r-sm);
  font-family:var(--font-mono); font-size:var(--fs-sm); color:var(--ink-body);
  background:var(--bg-surface);
  transition:background var(--t-base) var(--ease-out), border-color var(--t-base) var(--ease-out);
}
.lp-mods__tool input{ accent-color:var(--accent); width:17px; height:17px; flex:none; }
.lp-mods__tool.is-checked{ background:var(--fam-b-soft); border-color:var(--accent); }

/* Columna derecha: panel `.media-frame` con la lista de módulos del
   sistema único. El body del frame es BLANCO real (`--bg-surface`, ver
   Tarea 6), así que el texto usa `--ink-body`/`--ink` normales, nunca un
   tono pensado para navy. Apagado por defecto (opacidad baja + dot hueco
   + chip sin pintar); `.is-on` enciende los tres a la vez, transición
   SOLO opacity/color (pedido explícito del guion, sin transform). */
/* Placa del sistema (S5 R1, corrección Fabio "más dinámica y creativa...
   módulos en tarjetas sueltas que se van encendiendo"): barra-core con
   contador de módulos activos (dot verde) + rejilla 2 col de
   tarjetas-slot. Apagada = "slot libre": borde DISCONTINUO morado tenue y
   atenuada (continuidad exacta con .lp-mods-mini .is-hueco del hero: el
   hueco que espera su módulo). Encendida = instalada: borde sólido
   morado + anillo glow (color-mix) + dot verde + chip activo + pop de
   encaje (transform propio del li, sin conflicto: el data-reveal vive en
   el contenedor .lp-mods, no aquí). */
/* R2: la placa consume --lp-app-accent/--lp-app-radius (custom properties
   que el mini configurador fija inline en el panel) con fallback al
   morado/r-sm de serie: el visitante "personaliza su app" y TODO el
   recuadro cambia de acento y de esquinas. El texto nunca toma el acento
   elegido (el ámbar --hl jamás puede ser texto): solo bordes/glow/dots. */
.lp-mods__core{
  display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-3) var(--s-4);
  border:2px solid var(--lp-app-accent, var(--accent));
  border-radius:var(--lp-app-radius, var(--r-sm));
  background:color-mix(in srgb, var(--lp-app-accent, var(--accent)) 12%, var(--bg-surface));
  margin-bottom:var(--s-4);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  transition:border-color var(--t-base), background var(--t-base), border-radius var(--t-base);
}
.lp-mods__core i{ width:8px; height:8px; border-radius:50%; background:var(--ok); flex:none; }
.lp-mods__core b{ color:var(--ink); font-weight:700; }
.lp-mods__core small{ margin-left:auto; color:var(--ink); font-weight:700; font-size:inherit; }
/* Mini configurador "// hazla tuya" (R2): nombre + color + esquinas. Vive
   en la columna del panel, encima del frame (grid-column 2; la columna de
   herramientas abarca las dos filas). */
.lp-mods__custom{
  display:flex; align-items:center; flex-wrap:wrap; gap:var(--s-3);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  margin-bottom:var(--s-3);
}
.lp-mods__custom-tag{ color:var(--accent); font-weight:700; }
.lp-mods__custom-name input{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  color:var(--ink); background:var(--bg-surface);
  border:1px solid var(--card-border); border-radius:var(--r-xs);
  padding:4px 8px; width:11.5em;
}
.lp-mods__custom-name input:focus-visible{ outline:var(--ring); outline-offset:2px; }
.lp-mods__custom-group{ display:inline-flex; align-items:center; gap:var(--s-2); }
.lp-mods__swatch{
  width:22px; height:22px; border-radius:50%; cursor:pointer;
  background:var(--sw); border:2px solid var(--bg-surface);
  box-shadow:0 0 0 1px var(--card-border);
  padding:0;
}
.lp-mods__swatch.is-active{ box-shadow:0 0 0 2px var(--ink); }
.lp-mods__shape{
  width:22px; height:22px; cursor:pointer; padding:0;
  background:var(--bg-surface); border:2px solid var(--ink-soft);
}
.lp-mods__shape--sq{ border-radius:var(--r-xs); }
.lp-mods__shape--rd{ border-radius:10px; }
.lp-mods__shape.is-active{ border-color:var(--ink); box-shadow:0 0 0 1px var(--ink); }
.lp-mods__sistema{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s-3);
}
.lp-mods__sistema li{
  position:relative;
  border:2px dashed color-mix(in srgb, var(--lp-app-accent, var(--accent)) 38%, transparent);
  border-radius:var(--lp-app-radius, var(--r-sm));
  padding:var(--s-3) var(--s-4) var(--s-3) calc(var(--s-4) + 18px);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-body);
  opacity:.5;
  transition:opacity var(--t-base) var(--ease-out), color var(--t-base) var(--ease-out),
    border-color var(--t-base) var(--ease-out), background var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out);
}
.lp-mods__sistema li::before{
  content:""; position:absolute; left:var(--s-4); top:calc(var(--s-3) + 4px);
  width:8px; height:8px; border-radius:50%; flex:none;
  border:1.5px solid var(--ink-soft); background:transparent;
  transition:background var(--t-base) var(--ease-out), border-color var(--t-base) var(--ease-out);
}
.lp-mods__sistema li b{ display:block; font-weight:700; color:inherit; line-height:1.35; }
.lp-mods__free{
  display:block; font-style:normal; color:var(--ink-soft); margin-top:3px;
}
.lp-mods__sistema li.is-on{
  opacity:1; color:var(--ink);
  border:2px solid var(--lp-app-accent, var(--accent));
  background:var(--bg-surface);
  box-shadow:0 0 0 2px color-mix(in srgb, var(--lp-app-accent, var(--accent)) 22%, transparent);
  animation:lpModsPop .32s var(--ease-pop);
}
.lp-mods__sistema li.is-on::before{ background:var(--ok); border-color:var(--ok); }
.lp-mods__chip{
  display:none; margin-top:3px;
  font-family:var(--font-mono); font-size:.6875rem; font-weight:700;
  color:var(--ok-text); border:1px solid var(--ok); border-radius:var(--r-xs);
  padding:2px 8px;
}
.lp-mods__sistema li.is-on .lp-mods__chip{ display:inline-block; }
.lp-mods__sistema li.is-on .lp-mods__free{ display:none; }
@keyframes lpModsPop{
  0%{ transform:scale(.94); }
  60%{ transform:scale(1.02); }
  100%{ transform:scale(1); }
}
@media (max-width:560px){
  .lp-mods__sistema{ grid-template-columns:1fr; }
}
.lp-mods__nota{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft);
  border-top:1px solid var(--card-border); margin-top:var(--s-4); padding-top:var(--s-3);
}

@media (max-width:880px){
  .lp-mods__grid{ grid-template-columns:1fr; }
}

/* Sin JS: no hay `initLpMods` que encienda nada, así que la placa se
   muestra COMPLETA y estática (los 7 módulos ya instalados de fábrica),
   el contador grande se oculta y el contador del core también (ambos
   dependerían de un cálculo que nunca llega). */
html:not(.js) .lp-mods__count{ display:none; }
html:not(.js) .lp-mods__core small{ display:none; }
html:not(.js) .lp-mods__custom{ display:none; }
html:not(.js) .lp-mods__sistema li{ opacity:1; color:var(--ink); border:2px solid var(--accent); background:var(--bg-surface); }
html:not(.js) .lp-mods__sistema li::before{ background:var(--ok); border-color:var(--ok); }
html:not(.js) .lp-mods__chip{ display:inline-block; }
html:not(.js) .lp-mods__free{ display:none; }

/* =====================================================================
   Sesión 5 (2026-07-17) — Tarea 8: B2 cadena bottom-up en NAVY con capa
   IA compactada + mapa de pago por hitos. Registrado en design-system.md
   §4. El mapa de hitos y los sellos NO llevan CSS nuevo: son `.lp-mapa`/
   `.lp-sello` (Sesión 4/5, ya parametrizados en `--lp-crm-soft`/
   `--lp-crm-accent`) consumidos tal cual — solo la cadena necesita reglas
   propias.
   ===================================================================== */

/* --- Cadena bottom-up en navy (.lp-chain, clase en la propia <section>) -
   `.svc-chain` (pages.css) es la misma pieza de siempre (pensada para
   fondo claro: card `--bg-surface` + borde `--card-border` + número de
   fondo en tinta `--ink`); aquí NO se reescribe, se RETINTA por completo
   vía selectores compuestos bajo `.lp-chain`, mismo criterio que
   `.lp-ventajas` (hairlines en `color-mix(...--c-lila 25%...)`, cero
   fondo de card — el navy de la sección hace de fondo). `--c-lila` es el
   lila ya reservado a contadores/métricas sobre navy (tokens.css), aquí
   se reutiliza para flechas + outline-num (equivalente a `.outline-num--
   lila`, ya existente, pero aplicado por selector para no tocar el
   markup del `.svc-chain` original). */
.lp-chain .svc-chain{
  background:transparent;
  border-color:color-mix(in srgb, var(--c-lila) 25%, transparent);
}
.lp-chain .svc-chain__num{ -webkit-text-stroke:2px var(--c-lila); }
.lp-chain .svc-chain__body b{ color:var(--ink-inverse); }
.lp-chain .svc-chain__body p{ color:var(--ink-inverse-2); }
.lp-chain .svc-chain__arrow{
  color:var(--c-lila); background:transparent;
  border-left-color:color-mix(in srgb, var(--c-lila) 25%, transparent);
  border-right-color:color-mix(in srgb, var(--c-lila) 25%, transparent);
}
@media (max-width:880px){
  .lp-chain .svc-chain__arrow{
    border-top-color:color-mix(in srgb, var(--c-lila) 25%, transparent);
    border-bottom-color:color-mix(in srgb, var(--c-lila) 25%, transparent);
  }
}

/* R1: fondo de cada paso encendido por scroll. Solo background-color
   (excepción explícita a la regla de solo transform/opacity: aquí es un
   tinte de fondo, no un movimiento, y no provoca layout). El numeral de
   contorno gana opacidad al encenderse (su opacity:.1 de base ya la
   declara `.svc-chain__num` en pages.css: aquí SOLO la transición y el
   estado encendido, para no duplicar la base). Sin GSAP o con
   reduced-motion, el helper enciende los 4 de golpe: queda el estado
   final, legible. */
.lp-chain .svc-chain__step {
  transition: background-color var(--t-base) var(--ease-out);
}
.lp-chain .svc-chain__step.is-on {
  background: color-mix(in srgb, var(--c-lila) 14%, transparent);
}
.lp-chain .svc-chain__num {
  transition: opacity var(--t-base) var(--ease-out);
}
.lp-chain .svc-chain__step.is-on .svc-chain__num { opacity: .28; }
@media (prefers-reduced-motion: reduce) {
  .lp-chain .svc-chain__step,
  .lp-chain .svc-chain__num { transition: none; }
}

/* S2 (pedido Fabio): en movil la fila de 4 columnas de .svc-chain (pages.css)
   no aplica bien como tarjetas sueltas: cada paso pasa a verse como su propia
   tarjeta (fondo tenue + borde lila + radio), separadas entre si por `gap`.
   La iluminacion al scroll (`.is-on`, arriba) sigue funcionando: cae DESPUES
   en la cascada y con mas especificidad (mismo selector base + clase), asi
   que su fondo al 14% gana sobre el 6% de reposo definido aqui. Se conserva
   el `overflow:hidden` propio de `.svc-chain__step` (pages.css) para que el
   numeral de fondo quede recortado dentro del radio de la tarjeta. */
@media (max-width:880px){
  .lp-chain .svc-chain{ border:0; background:transparent; overflow:visible; gap:var(--s-3); }
  .lp-chain .svc-chain__step{
    border:1px solid color-mix(in srgb, var(--c-lila) 30%, transparent);
    border-radius:var(--card-radius);
    background:color-mix(in srgb, var(--c-lila) 6%, transparent);
  }
  .lp-chain .svc-chain__arrow{ border:0; background:transparent; height:20px; }
}

/* --- Fila-nota "capa IA" compacta (sustituye a la sección propia con 2
   `.inc-card`, retirada): un único párrafo bajo la cadena, dentro de la
   misma sección navy. `.chip` NO necesita override: es un badge con su
   propio fondo claro (`--fam-b-soft`), ya se apoya sobre navy sin retoque
   en el teaser de familia C del home (`.home-fam-teaser .chip`). */
.lp-chain__ia{
  margin-top:var(--s-6); padding-top:var(--s-5);
  border-top:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  color:var(--ink-inverse-2); font-size:var(--fs-sm);
}
/* S2 (pedido Fabio): el chip "extra opcional" pasa a bloque en su propia
   linea, y el texto que le sigue en el markup cae debajo (antes iban
   seguidos en la misma linea via `vertical-align`). `width:max-content`
   evita que el bloque se estire a todo el ancho, conserva forma de badge. */
.lp-chain__ia .chip{ display:block; width:max-content; margin:0 0 var(--s-2) 0; }

/* ===== LANDINGS FAMILIA C (S6) ===== */
/* .lp-bar-c — barra temática decorativa bajo el topbar de las landings de
   familia C (includes/lp-bar-c.php), equivalente ESTRUCTURAL de .lp-bar-b
   (mismo hueco aria-hidden, mismo --fs-mono-sm) pero con el lenguaje propio
   de C: cola de ejecución de producto REAL, siempre sobre navy (--fam-c),
   nunca Excel (A) ni checklist morado de expediente (B). Hairline y label
   en --c-lila (el lila reservado a "counters/métricas sobre navy con
   glow", tokens.css:24) en vez de --accent (B, fondo claro): mismo
   criterio que .lp-chain/.lp-ventajas, para no repetir HEX a pelo.
   Trampa de contraste ya documentada en este mismo fichero (nota de
   .lp-ventajas__ico, S3): --ok-text cae a 3:1 sobre navy, así que el
   check de is-ok usa --ok (4.65:1) y el texto "hecho" sube a
   --ink-inverse (nunca --ok-text aquí). El hollow de is-off usa
   --ink-inverse-2 (párrafo sobre navy) como equivalente navy-safe del
   --ink-soft que usa .lp-bar-b sobre fondo claro. Check de is-ok
   renderizado como glifo mono (✓) en vez de dot relleno, mismo patrón que
   .lp-sino-crm__tick (glifo suelto, sin fondo de chip) — así el mark
   comunica "hecho" sin depender solo del color. */
.lp-bar-c{
  background:var(--fam-c); color:var(--ink-inverse-2);
  border-bottom:1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
}
.lp-bar-c__row{
  display:flex; align-items:center; gap:var(--s-4);
  padding-top:9px; padding-bottom:9px; /* .wrap ya da el padding horizontal, calco de .lp-bar-b__row */
}
.lp-bar-c__label{ color:var(--c-lila); }
.lp-bar-c__item{ display:inline-flex; align-items:center; gap:var(--s-2); color:var(--ink-inverse-2); }
.lp-bar-c__mark{ width:8px; height:8px; border-radius:50%; flex:none; }
.lp-bar-c__item.is-ok{ color:var(--ink-inverse); }
.lp-bar-c__item.is-ok .lp-bar-c__mark{
  width:auto; height:auto; border-radius:0; line-height:1;
  font-weight:700; color:var(--ok);
}
.lp-bar-c__item.is-ok .lp-bar-c__mark::before{ content:"\2713"; }
.lp-bar-c__item.is-run{ color:var(--c-lila); }
.lp-bar-c__item.is-run .lp-bar-c__mark{
  background:var(--c-lila);
  animation:lpBarCPulse 1.4s ease-in-out infinite;
}
.lp-bar-c__item.is-off{ color:var(--ink-inverse-2); opacity:.7; }
.lp-bar-c__item.is-off .lp-bar-c__mark{ background:transparent; border:1.5px solid var(--ink-inverse-2); }
@keyframes lpBarCPulse{ 50%{ opacity:.35; } }
@media (prefers-reduced-motion: reduce){
  .lp-bar-c__item.is-run .lp-bar-c__mark{ animation:none; }
}
@media (max-width:880px){
  /* Carrusel compacto: la tira pasa a una ventana de 1 linea; los pasos se
     apilan absolutos y se muestran de uno en uno. Fallback (sin is-live):
     el primer paso fijo. */
  .lp-bar-c__items{ position:relative; display:block; height:1.5em; overflow:hidden; flex:1 1 auto; min-width:0; }
  .lp-bar-c__item{ position:absolute; left:0; top:0; white-space:nowrap; opacity:0; }
  .lp-bar-c__item:first-child{ opacity:1; }                 /* fallback: primer paso */
  .lp-bar-c__items.is-live .lp-bar-c__item{ opacity:0; transition:opacity var(--t-base); }
  .lp-bar-c__items.is-live .lp-bar-c__item.is-cur{ opacity:1; }
  /* el paso pendiente (is-off) YA no se oculta: participa en el carrusel */
}

/* .lp-ovl — overlay de demo a PÁGINA COMPLETA (S6 Tarea 3, sobre
   includes/lp-demo-c.php + initLpOvl en landing.js). Al pulsar cualquier
   CTA de una landing de familia C se abre esta capa full-viewport SIN
   cambiar de URL ("quiero que el formulario sea la página entera al
   pulsar un CTA", decisión literal de Fabio).
   Z-INDEX: se reutiliza `--z-overlay` (80, tokens.css) — el MISMO token
   que ya usan `.cookie-banner`/`.cookie-panel` — en vez de inventar un
   valor nuevo: ya cae por encima de `--z-nav` (50, topbar sticky) y del
   `z-index:60` fijo de `.lp-sticky`, justo el requisito del guion, sin
   ampliar la escala existente. Si algún día coincide en pantalla con el
   banner de cookies (mismo z-index), mismo criterio que ya usa
   `.fs__nav`/`.lp-sticky` con ese banner: es un empate asumido, no un bug.
   R1-T1 (feedback Fabio: "quiero que sean secuenciales, con el mismo
   diseño que las otras landings, ahora mismo son bastante feos") NO toca
   esta capa base: sigue `position:fixed;inset:0`, navy, `.dots-bg`,
   `overflow-y:auto` de seguridad para móvil (ver nota de `.lp-ovl__panel`
   más abajo sobre por qué ya casi nunca hace falta). Lo que cambia es TODO
   lo de dentro: `.lp-ovl__panel` deja de ser una tarjeta flotante sobre
   navy con un formulario de 2 columnas, y pasa a ser una capa CLARA a
   pantalla completa con el mismo lenguaje visual que el funnel full-screen
   `/diagnostico` (`.fs`/`.fs__step`/`.fs__q`/`.fs__opt`, ver más arriba en
   este archivo): una pregunta por pantalla, tarjetas grandes que avanzan
   solas, progreso arriba, atrás fijo abajo. */
.lp-ovl {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  overflow-y: auto;
  background: var(--fam-c);
  color: var(--ink-inverse-2); /* para que .dots-bg (currentColor) se vea sobre navy, mismo criterio que .sec--inverse */
}
/* Opacidad reducida sobre navy, mismo valor que .sec--inverse.dots-bg — la
   textura de puntos nace pensada para currentColor sobre fondo CLARO.
   R1-T1: en la práctica ya no se VE (el panel claro de abajo cubre el
   100% del viewport y la tapa entera) — se deja intacta a propósito,
   mismo criterio de "la capa se conserva" que el resto de esta regla. */
.lp-ovl.dots-bg::before { opacity: .07; }

/* Animación de entrada SOLO transform/opacity (guardarraíl del brief).
   `animation` (no `transition`): el elemento pasa de [hidden] (fuera del
   render) a pintado, y una transición no puede animar ese salto de forma
   fiable — mismo criterio que `.fs__q.is-entering`/`fsStepIn` un poco más
   arriba en este archivo. `prefers-reduced-motion` ya la neutraliza vía
   la regla global de tokens.css (fuerza animation-duration:.01ms), la
   media query de abajo es cinturón y tirantes, mismo patrón que esa
   misma regla de `.fs__q.is-entering`. */
@keyframes lpOvlIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
/* R1-T1: panel a PÁGINA COMPLETA clara (`position:absolute;inset:0` sobre
   `.lp-ovl`, que ya es `position:fixed` — hace de containing block), en
   vez de tarjeta flotante con margen. `height:100dvh` (no solo
   `min-height`) por el MISMO motivo ya documentado para `.fs` más arriba
   en este archivo: con un padre `display:flex;flex-direction:column` de
   altura fija, cada `.lp-ovl__paso` (`flex:1;min-height:0;overflow-y:auto`)
   hace scroll DENTRO de sí mismo en vez de estirar la capa entera. La
   `overflow-y:auto` de `.lp-ovl` (arriba) queda como cinturón y tirantes
   para el caso límite de un dvh de móvil que mida corto un instante. */
.lp-ovl__panel {
  position: absolute; inset: 0; height: 100dvh;
  display: flex; flex-direction: column;
  background: var(--bg-page); color: var(--ink);
  animation: lpOvlIn var(--t-base) var(--ease-out);
}
.lp-ovl__panel p { color: var(--ink-body); }
@media (prefers-reduced-motion: reduce) {
  .lp-ovl__panel { animation: none; }
}

/* Cabecera compacta fija (calco funcional de `.topbar--minimal` del
   funnel: logo+salir ahí, sticker+producto+progreso+cerrar aquí). Vive
   FUERA de cualquier `.lp-ovl__paso`: no se oculta nunca mientras la capa
   esté abierta, en cualquier paso. */
.lp-ovl__head {
  flex: none; display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4) var(--gutter);
  border-bottom: 1px solid var(--c-hairline);
}
.lp-ovl__head-id { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s-3); }
.lp-ovl__producto {
  font-family: var(--font-disp); font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lp-ovl__progress {
  flex: none; font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
}
.lp-ovl__close {
  flex: none; width: 40px; height: 40px; display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 1.35rem; line-height: 1;
  background: var(--bg-surface); border: 1px solid var(--c-hairline); border-radius: var(--r-sm);
  color: var(--ink-soft); cursor: pointer;
}
.lp-ovl__close:hover { color: var(--ink); border-color: var(--c-border); }

/* ---- Funnel de diagnóstico montado como overlay (Fase 3) ------------
   El mismo #fsForm de /diagnostico vive dentro de .lp-ovl__panel
   (footer.php -> funnel-overlay.php). En página, .fs usa
   height:calc(100dvh - topbar) porque hay un topbar externo; dentro del
   panel (ya 100dvh, flex-column) debe RELLENAR en vez de fijar altura, para
   que cada .fs__step haga su propio scroll (mismo patrón que .lp-ovl__paso).
   .lp-ovl--funnel neutraliza el fondo navy de .lp-ovl (el panel del funnel
   es claro, como /diagnostico) — solo se vería en el instante de la
   animación de entrada del panel. El botón .fs__close solo existe en modo
   overlay (en página lo cubre el "salir" del topbar minimal). */
.lp-ovl--funnel { background: var(--bg-page); }
[data-funnel-ovl] .fs { height: auto; flex: 1 1 auto; min-height: 0; }
.fs__close {
  flex: none; width: 40px; height: 40px; display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 1.35rem; line-height: 1;
  background: var(--bg-surface); border: 1px solid var(--c-hairline); border-radius: var(--r-sm);
  color: var(--ink-soft); cursor: pointer;
}
.fs__close:hover { color: var(--ink); border-color: var(--c-border); }

/* Barra de progreso — mismo componente visual que `.fs__progress` del
   funnel (gradiente `--fs-progress`, 4px), token e implementación
   reutilizados 1:1; solo cambia el nombre de clase para no acoplar este
   overlay al DOM del funnel. */
.lp-ovl__progressbar { flex: none; height: 4px; background: var(--c-hairline); }
.lp-ovl__progressbar i {
  display: block; height: 100%; background: var(--fs-progress);
  width: var(--p, 33%); transition: width var(--t-reveal) var(--ease-out);
}

/* `.lp-ovl__form` ocupa el resto de la capa (calco de `.fs`, que ES
   directamente el `<form>` del funnel) y es `display:flex;column` para
   que sus 4 `.lp-ovl__paso` se comporten como `.fs__step`: uno visible
   cada vez, `flex:1` sobre la altura ya fija del padre. */
.lp-ovl__form { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* [hidden] empata en ORIGEN con `.lp-ovl__paso` (fija `display:flex` como
   regla de autor, que gana siempre al `display:none` del user-agent para
   `[hidden]`, sea cual sea la especificidad) — mismo motivo ya documentado
   para `.fs__step[hidden]` más arriba en este archivo. Sin esto, initLpOvl
   podría marcar `hidden` en los pasos y `.lp-ovl__paso` seguiría
   pintándose igual: los 4 pasos se verían apilados a la vez. */
.lp-ovl__paso[hidden] { display: none; }
.lp-ovl__paso {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; padding: var(--s-6) var(--gutter);
}
/* Centrado que sobrevive al overflow: `margin:auto` en el hijo único en
   vez de alignment en el padre — mismo motivo ya documentado para
   `.fs__q` más arriba (un `align-items:center` en el padre deja el inicio
   de un contenido más alto que el hueco disponible inalcanzable por
   scroll en la mayoría de motores). */
.lp-ovl__q { width: min(640px, 100%); margin: auto; }
.lp-ovl__q--wide { width: min(880px, 100%); } /* paso 4: el iframe de Bookings necesita más aire, mismo ancho que .fs__q--wide */

.lp-ovl__qtitle, .lp-ovl__legend {
  display: block; font-family: var(--font-disp); font-weight: 600;
  color: var(--ink); font-size: var(--fs-h2); line-height: var(--lh-head);
  margin-bottom: var(--s-5); padding: 0; border: 0;
}
.lp-ovl__group { border: 0; padding: 0; margin: 0; }

.lp-ovl__opts { display: grid; gap: var(--s-3); }
/* Tarjeta-opción grande, mismo look que `.fs__opt` (borde, hover con
   desplazamiento + sombra sólida) — el `<input>` real vive oculto
   (accesible, focuseable) y el `<span>` hermano ES la tarjeta completa,
   así el estado :checked/:hover/:focus-visible puede teñirla ENTERA con
   un combinador `~`/selector de hermano, sin recurrir a `:has()` (mismo
   criterio prudente que ya documenta landing.js para `.lp-mods__tool`).
   Un bloque por fila SIEMPRE (regla de Fabio): cada tarjeta ocupa el
   ancho completo, nunca dos en la misma fila. */
.lp-ovl__opt { display: block; cursor: pointer; }
.lp-ovl__opt-input {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.lp-ovl__opt-card {
  display: flex; align-items: center; gap: var(--s-4); width: 100%;
  padding: 16px 18px; border: 1px solid var(--c-border); border-radius: var(--r-sm);
  background: var(--bg-surface);
  font-family: var(--font-body); font-size: var(--fs-body); color: var(--ink);
  transition: transform var(--t-fast) var(--ease-pop), box-shadow var(--t-fast) var(--ease-pop), border-color var(--t-fast);
}
.lp-ovl__opt-text { flex: 1; min-width: 0; }
/* Flecha: pista de "esto avanza solo", oculta en reposo y revelada en
   hover/checked/focus — SOLO transform/opacity (guardarraíl del brief). */
.lp-ovl__opt-arrow {
  flex: none; font-family: var(--font-mono); color: var(--ink-soft);
  opacity: 0; transform: translateX(-4px);
  transition: opacity var(--t-fast), transform var(--t-fast);
}
.lp-ovl__opt:hover .lp-ovl__opt-card,
.lp-ovl__opt-input:checked ~ .lp-ovl__opt-card {
  border-color: var(--ink); transform: translate(-2px, -2px);
  box-shadow: 5px 5px 0 var(--c-pop);
}
.lp-ovl__opt:hover .lp-ovl__opt-arrow,
.lp-ovl__opt-input:checked ~ .lp-ovl__opt-card .lp-ovl__opt-arrow {
  opacity: 1; transform: translateX(0);
}
.lp-ovl__opt-input:focus-visible ~ .lp-ovl__opt-card { box-shadow: var(--ring); }

/* Agenda de la demo: apoyo, no protagonista — chips pequeños DEBAJO de
   las tarjetas de opción (la rejilla de 2 columnas con una barra lateral
   propia es justo lo que R1-T1 quita). */
.lp-ovl__agenda { margin-top: var(--s-6); padding-top: var(--s-5); border-top: 1px solid var(--c-hairline); }
.lp-ovl__agenda .tag { display: block; margin-bottom: var(--s-3); }
.lp-ovl__agenda-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); list-style: none; }
.lp-ovl__agenda-chips li {
  font-size: var(--fs-sm); color: var(--ink-body);
  background: var(--bg-calm); border: 1px solid var(--c-hairline); border-radius: var(--r-pill);
  padding: 6px 14px;
}

/* Error de envío (fallo de red/servidor tras el submit real — distinto de
   la validación nativa de campos obligatorios, que el navegador ya
   gestiona sin JS propio: este form NO lleva `novalidate`). Mismo 14%
   de mezcla de --danger que .lp-sino-sheet__head/no (ver nota "R2 Tarea
   V3.1" en design-system.md §4), contraste ya validado ahí. */
.lp-ovl__err {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, var(--bg-surface));
  border: 1px solid var(--danger); border-radius: var(--r-sm);
  padding: var(--s-3) var(--s-4); margin-top: var(--s-3);
}

/* Paso calendario: misma receta de alto que .fs__booking/.fs__booking
   iframe (diagnostico.php, salida "reserva") — 420px de base, 300px en
   viewports bajos, calcada 1:1 porque es el MISMO calendario embebido. */
.lp-ovl__cal h3 { margin-bottom: var(--s-2); }
.lp-ovl__cal > p { margin-bottom: var(--s-2); }
.lp-ovl__frame {
  display: block; width: 100%; min-height: 420px; margin: var(--s-4) 0;
  border: 1px solid var(--c-border); border-radius: var(--r-md);
}
/* Respaldo visible del calendario (R2-T5, ver includes/lp-demo-c.php:
   Bookings solo se deja embeber desde HTTPS — dev bloquea SIEMPRE, prod
   funciona). Mismo patrón que `.fs__booking-fallback` del funnel: antes
   era un enlace de texto discreto dentro de `.lp-ovl__cal-actions`, ahora
   microcopy + botón `.btn--ghost` en su propio bloque, más visible. */
.lp-ovl__cal-fallback { margin-bottom: var(--s-4); }
.lp-ovl__cal-fallback p {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin: 0 0 var(--s-3);
}
.lp-ovl__cal-actions {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-4);
}

/* Atrás — mismo patrón que `.fs__nav`/`.fs__back` del funnel: fijo en la
   esquina inferior izquierda del VIEWPORT (no del paso), fuera del flujo
   y FUERA de `.lp-ovl__panel` en el DOM (ver comentario en
   includes/lp-demo-c.php: así no le afecta el containing-block que crea
   la animación `transform` de entrada del panel). `--z-nav` (mismo nivel
   que el topbar sticky, por DEBAJO de `--z-overlay`/banner de cookies),
   igual criterio que `.fs__nav`. */
.lp-ovl__back {
  position: fixed; left: var(--s-4);
  bottom: calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-nav);
}
.lp-ovl__backbtn {
  font-family: var(--font-mono); font-size: var(--fs-mono-md);
  background: var(--bg-page); border: 1px solid var(--c-hairline);
  border-radius: var(--r-sm); color: var(--ink-soft); cursor: pointer;
  padding: var(--s-3) var(--s-4); min-height: 44px;
  display: inline-flex; align-items: center;
}
.lp-ovl__backbtn:hover { color: var(--ink); border-color: var(--c-border); }

/* Micro-transición al cambiar de paso — mismo keyframe/criterio que
   `.fs__q.is-entering` del funnel (solo transform/opacity). */
@keyframes lpOvlQIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.lp-ovl__q.is-entering { animation: lpOvlQIn var(--t-reveal) var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .lp-ovl__q.is-entering { animation: none; }
}

/* R1-T1: compacta paddings/tipografía en viewports bajos para que quepa
   sin scroll interno en los objetivos habituales (1280x800, portátiles
   pequeños) — mismos valores que la media query gemela de `.fs__step`
   más arriba en este archivo. */
@media (max-height: 760px) {
  .lp-ovl__paso { padding: var(--s-4) var(--gutter); }
  .lp-ovl__qtitle, .lp-ovl__legend { font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.1rem); margin-bottom: var(--s-3); }
  .lp-ovl__opts { gap: var(--s-2); }
  .lp-ovl__opt-card { padding: 11px 14px; gap: var(--s-3); }
  .lp-ovl__frame { min-height: 300px; }
}

@media (max-width: 560px) {
  .lp-ovl__head { padding: var(--s-3) var(--s-3); gap: var(--s-3); }
  .lp-ovl__paso { padding: var(--s-4) var(--s-3); }
}

/* --- Collage-producto del hero (Macroning, S6 Tarea 4) ------------------
   A diferencia de --flow (B1, sin .media-frame) y --mods (B2, dos piezas:
   frame ancla + card suelta), aquí NO hay una segunda pieza con la que
   componer un collage de verdad: el vídeo real es el ÚNICO protagonista,
   a ancho completo ("producto ya hecho, míralo funcionar"). La única pieza
   flotante es el chip superior, que hereda gratis `.lp-hero__collage >
   .sticker` de la base (top:-16px/left:34%, mismo peeking que el resto de
   variantes) — cero regla propia. Lo que SÍ hay que neutralizar es la
   regla base `.lp-hero__collage .media-frame` (position:absolute/
   width:62%/rotate(-2deg), pensada para UNA pieza entre varias): el vídeo
   vive dentro de `.product-panel-wrap` (mismo componente que la home, con
   `.product-panel-sticker` del logo asomando por su esquina superior), así
   que sigue siendo descendiente de `.lp-hero__collage` y heredaría esa
   regla si no se gana con más especificidad — mismo criterio explícito
   que `.lp-hero__collage--mods .lp-hero__sistema.media-frame` (3 clases
   contra las 2 de la base, gana siempre sin depender del orden de carga).
   Al volver a `position:static;width:100%`, el panel simplemente FLUYE
   dentro del collage (igual que ya hace la base en móvil ≤880px de
   serie): no hace falta ninguna regla de móvil aparte, ni distinguir
   escritorio de táctil — es la misma composición a cualquier ancho. */
.lp-hero__collage--producto .product-panel-wrap .media-frame {
  position: static; width: 100%; transform: none;
}
/* R1 (ROADMAP p.1): el logo de Macroning del hero se comía el panel. Baja
   solo aquí; .product-panel-sticker img (pages.css) sigue en 120px para el
   resto, y el isotipo cuadrado de SmartAudit conserva su propia excepción. */
.page-macroning .product-panel-wrap .product-panel-sticker img { width: 86px; }
/* Movil: al apilar el collage, el sticker (top-left de serie) se montaba
   sobre la barra del frame y tapaba el titulo "macroning_demo.mp4". R1-b
   (Fabio smoke): la esquina superior derecha se cortaba. R1-c: en la esquina
   inferior derecha DENTRO del frame tapaba la linea amarilla "asi ejecuta
   Macroning..." (el estado va pegado al fondo del frame). Se BAJA hasta salir
   por debajo del frame (bottom negativo), sobre el hueco navy libre de la
   seccion (64px de aire hasta la siguiente), despejando el texto.
   Selector de 3 clases (0,3,0) para ganar a la base .product-panel-sticker
   sin afectar a las paginas de /servicios. */
@media (max-width: 880px) {
  .page-macroning .product-panel-wrap .product-panel-sticker {
    top: auto; left: auto; bottom: -52px; right: var(--s-2); padding: 5px 7px;
  }
  .page-macroning .product-panel-wrap .product-panel-sticker img { width: 38px; }
}

/* R2-T3 (SmartAudit, feedback Fabio: "el vídeo del hero se ve demasiado
   alto el contenedor, debería de ser como el de macroning"). El vídeo del
   tour (`#saTourVideo`, smartaudit.php) declaraba un aspect ratio en sus
   atributos `width`/`height` que NO era el real del archivo (corregido en
   el PHP, ver su docblock R2-T3) — con esa altura falsa, el alto natural
   del frame (vídeo+padding+barra) quedaba muy por debajo del
   `min-height:400px` que la regla base `.lp-hero__collage` (más arriba en
   este archivo) reserva para el collage MULTI-PIEZA de A1/A2/B1/B2 (piezas
   `position:absolute` que sí necesitan un lienzo mínimo) — un suelo ajeno
   a esta variante de una sola pieza a ancho completo, que dejaba el hueco
   blanco arriba/abajo del marco claro. SCOPED a `.page-smartaudit` (body
   class de esta página, no del `.lp-hero__collage--producto` genérico)
   para no tocar el collage de Macroning: su vídeo real (1920×1080) ya
   llena ese lienzo sin sobrante visible, así que no necesita este ajuste
   y no debe perderlo. `display:block` en el vídeo evita el hueco de línea
   base de los reemplazados inline que Macroning ya evita gratis vía
   `.mv video{display:block}` (aquí no hay `.mv`, el vídeo vive
   directamente bajo `.media-frame__body`). Ver design-system.md §4. */
.page-smartaudit .lp-hero__collage.lp-hero__collage--producto { min-height: 0; }
/* R3: width 100% + height:auto OBLIGATORIOS. El diagnostico R2 se quedo
   corto: sin height:auto, el atributo height="492" del <video> sigue
   mandando como alto USADO aunque el ancho baje al de la columna (~344px),
   y el marco salia alto con huecos (medido: video 344x492 en vez de
   344x176). Con esto el video escala con su aspect real como el de
   Macroning (.mv video ya lleva width:100%;height:auto de serie). */
.page-smartaudit .lp-hero__collage--producto .media-frame__body video {
  display: block; width: 100%; height: auto;
}
/* R1 (ROADMAP p.1 — feedback Fabio: "el vídeo se tiene que poder ver un
   poco más grande... ahora mismo casi ni se ve"). El marco YA ocupa el
   100% de su columna (regla `--producto` de arriba), así que el único
   ancho que queda por ganar está en el REPARTO del hero: la columna de
   media pasa de 5fr a 6.5fr (7fr/5fr → 5.5fr/6.5fr) SOLO en esta página.
   Justificación del trato de favor: este es el hero de un producto YA
   HECHO donde el vídeo ES la prueba ("producto ya hecho, míralo
   funcionar"), no una ilustración de apoyo; el resto de páginas de
   servicio conservan intacto el 7fr/5fr de `.svc-hero__grid` (pages.css).
   Descartado ensanchar el panel por su cuenta (width>100% + margen
   negativo): la única dirección libre es hacia la izquierda y se comería
   el gap de 64px antes de crecer nada apreciable, pisando el texto.
   El `min-width:881px` es OBLIGATORIO, no decorativo: la media query de
   pages.css que colapsa `.svc-hero__grid` a una sola columna en móvil
   tiene MENOS especificidad (0,1,0) que este selector (0,2,0) y perdería,
   dejando el hero a dos columnas en el móvil. Ver design-system.md §4. */
@media (min-width: 881px) {
  .page-smartaudit .svc-hero__grid { grid-template-columns: 5.5fr 6.5fr; }
}

/* --- Cola de trámites `.lp-tramites` (Sesión 6, Tarea 5; rediseño TOTAL
   del envoltorio en R1-T4) -------------------------------------------
   Pieza interactiva ESTRELLA de `/macroning`. R1-T4 (feedback Fabio
   literal: "preferiría que la encajaras en un contenedor, y no me gusta
   mucho el diseño... ocupa mucho espacio. Quizás estaría bien poner a la
   izquierda las barras con los contadores... y que se vayan acumulando
   todos los clics... y al activar Macroning se destruyeran a 2"):
   fórmulas/tramos/frases de la Tarea 5 (`landing.js`: `lpBlendMap`,
   `lpTramoFor`, `fixFraseHeight`) NO cambian, solo el envoltorio.
   VENTANA: `.lp-tramites__win` es la única tarjeta-contenedor (borde +
   `--shadow-b2`, max-width ~1000px) con winbar PROPIO
   `.lp-tramites__winbar` (dots + título mono) — calca la FÓRMULA de
   `.lp-calc--sheet__winbar` (misma composición visual) pero como clase
   propia en vez de envolver en `.media-frame`: la ventana ya trae su
   propio borde/sombra/padding y pelearía la cascada con `.media-frame`
   (14px/--shadow-soft) sin ganar nada, mismo criterio ya documentado para
   `.lp-calc--sheet__winbar`/`.lp-portales__bar`. Sombra sólida
   `--shadow-b2` sangra 5px derecha/abajo: `margin`/`padding` de la propia
   ventana y el `.lp-calc__cta` de debajo (ya trae `margin-top:var(--s-5)`)
   dejan de sobra ese margen, sin ningún ancestro `overflow:hidden` que la
   recorte.
   GRID (`.lp-tramites__body`): 3 piezas por `grid-template-areas` —
   `ctrl` (sliders), `res` (cifras+barras+frase+nota), `clics` (nube +
   interruptor) — para reordenar SOLO por CSS en el punto de corte, sin
   duplicar markup: ≥881px `"ctrl clics" / "res clics"` (clics ocupa
   las dos filas); ≤880px `"ctrl" / "clics" / "res"` (sliders → nube de
   clics → resultado, el orden pedido por Fabio).
   BARRAS (`.lp-tramites__bar`/`__bar-fill`): el ancho SIEMPRE sale del
   mismo número que pinta la cifra hermana — nunca un cálculo aparte (la
   trampa de la calculadora vieja de S3, ya evitada en la Tarea 5, tampoco
   se repite aquí): `anchoAmano = horasAno/techo`,
   `anchoMacro = horasMacro/techo`, con
   `techo = (2000 * minsActual/60) * 12` (el techo del slider de volumen
   con los minutos ACTUALES de la otra celda — así la barra se recalibra
   sola si cambian los minutos). Suelo visual 2%/0.8% (`Math.max` en JS)
   para que la barra nunca desaparezca en volúmenes bajos. El fill "a
   mano" REUTILIZA el mismo heat que la cifra (`lpTramoFor`, `[data-heat]`
   en el propio `__bar-fill`) pero en color SÓLIDO (`--c-lila`/`--hl`/
   `--danger`) en vez del `color-mix` muy suave de la cifra — una franja
   de 8px con esa opacidad baja no se leería. La barra "con Macroning" es
   siempre verde `--ok` (constante, sin heat: es la meta, no un dato que
   empeora).
   NUBE DE CLICS (`.lp-tramites__clics`): panel navy (`--fam-c`/`--r-md`)
   con un pool FIJO de 80 `.lp-tramites__dot` (nunca se reconstruye el DOM,
   a diferencia de las fichas de la Tarea 5 que ya no existen) — el JS
   solo alterna qué dots llevan `.is-shown`, así el jitter pseudo-aleatorio
   de posición (mezcla de `nth-child` módulo 5/7/11 más abajo) queda FIJO
   dot a dot entre renders. Forma "cursor con anillo": borde + relleno
   suave en `--c-lila` (el lila reservado a "métricas sobre navy con
   glow", tokens.css:24, aquí perfecto porque el panel es navy);
   `.is-super` (las 2 que sobreviven a la destrucción) cambia a `--ok`
   con un punto relleno central, mismo lenguaje que ya usaba `.is-super`
   en las fichas de la Tarea 5. `data-clics-por-tramite="15"` en el HTML
   es la ÚNICA fuente de la constante (JS la lee del atributo, no la
   duplica).
   MOTION: destrucción de dots y pulso de invitación del switch SOLO
   transform/opacity (guardarraíl del brief) — con GSAP,
   `stagger:{each:.008,from:'random'}` sobre scale/opacity (~0.9s con 80
   dots); sin GSAP, `.is-out` por lotes (`setTimeout`, patrón ya usado en
   `initLpSim`/Tarea 5); con `reduce`, instantáneo. La cifra+barra "con
   Macroning" SÍ transicionan `width`/`background` además de
   transform/opacity (igual que el resto del sitio: `.hero-progress`/
   `.lp-mapa` ya animan `width`, no es una excepción nueva) — con `reduce`
   esa transición también se apaga.
   R2-T1: la cifra+barra "a mano" YA NO cuentan hacia abajo al activar
   (retirado `countMano`/rAF de landing.js) — mantienen SIEMPRE `horasAno`
   real y solo cambian de PESO visual (`.is-dim`/`.is-hero` por fila, ver
   bloque "Estado activo" más abajo junto a `__bar--macro`): Macroning ya
   no "vacía" el dato de antes, hace crecer el de después. Sticker de logo
   (`.lp-tramites__logo`) y winbar en acento morado documentados junto a
   `.lp-tramites__win`/`__winbar` más abajo.
   R3-T2 (feedback Fabio: ventana en morado oscuro tipo splash de
   Macroning, logo sin tapar el título, output del slider siempre en la
   misma línea que la barra, contador "con Macroning" activo sin crecer
   pero con fondo verde) — 4 cambios sobre el envoltorio, documentados
   junto a cada regla concreta más abajo (`__win` para la receta del
   gradiente/adaptación de tinta, `__logo`/`__winbar` para la reubicación,
   `__field`/`__control` para el output en línea, `.is-hero` para el
   contador). La mecánica de fórmulas/dots de arriba sigue intacta. */
.lp-tramites {
  /* Fondo propio OBLIGATORIO: un `.sec` a pelo es transparente y deja ver
     el `.code-bg` global (la textura de pseudo-código del "hueco" entre
     secciones, ver comentario de `.code-bg`) justo detrás de los sliders y
     las cifras — detectado en QA de orquestador S6. Mismo criterio que la
     calculadora de A1 (`.sec--sheet` con fallback `--bg-calm`): la pieza
     interactiva siempre lleva fondo sólido propio. */
  background: var(--bg-calm);
}

/* --- Ventana contenedora ---------------------------------------------
   R3-T2 (feedback Fabio: "me gustaría que tuviera un parecido a la
   interfaz de Macroning, que es el morado del logo... el recuadro de la
   calculadora en un morado oscuro, o algo parecido al splash de
   macroning"): de tarjeta clara a gradiente morado oscuro. RECETA: 2
   paradas del mismo `color-mix(--c-pop/--fam-c)` a distinto porcentaje
   (135deg, 25%→45% de `--c-pop` sobre `--fam-c`/navy) — con los HEX
   reales de esos 2 tokens da rgb(31,41,111)→rgb(47,49,145), un morado
   oscuro profundo (muy cerca del #2a1a6e del splash real de Macroning)
   virando a un morado más azulado (el `--c-pop` de marca, #5d46ef, es
   literalmente el otro extremo del splash) — cero HEX nuevo, solo
   `color-mix` de 2 tokens ya existentes. Contraste verificado (script
   node, fórmula WCAG): `--ink-inverse` (blanco) da 13.08:1/10.66:1 sobre
   las 2 paradas, `--ink-inverse-2` da 8.50:1/6.93:1 — AA de sobra en
   ambas. Border en lila muy tenue (antes `--card-border` gris, invisible
   sobre morado). */
.lp-tramites__win {
  position: relative; /* ancla del sticker de logo, ver __logo debajo */
  max-width: 62.5rem; margin: var(--s-6) auto 0;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--c-pop) 25%, var(--fam-c)),
    color-mix(in srgb, var(--c-pop) 45%, var(--fam-c)));
  border: 1px solid color-mix(in srgb, var(--c-lila) 30%, transparent);
  border-radius: var(--r-lg); box-shadow: var(--shadow-b2);
  padding: var(--s-6);
}
/* Sticker del logo (R2-T1, reubicado en R3-T2): mismo lenguaje que
   `.product-panel-sticker`/`.lp-portales__badge` (marco blanco + borde
   `--ink` + `--shadow-b1` + rotación), clase propia porque el patrón
   vive en pages.css/otra sección de components.css y esta tarea no las
   toca. R3-T2 (feedback Fabio: "el título de la sección macroning.exe
   está tapado por el logo"): asomaba por la esquina superior IZQUIERDA,
   justo donde empieza el título del winbar — pasa a la esquina superior
   DERECHA, fuera del padding de la ventana, cero solape con
   `macroning.exe`. El nombre "Macroning" ya lo dice el winbar de al
   lado, por eso aria-hidden. */
.lp-tramites__logo {
  position: absolute; top: -20px; right: 24px; z-index: 3;
  display: inline-flex; background: var(--bg-surface);
  border: 2px solid var(--ink); border-radius: var(--r-sm);
  box-shadow: var(--shadow-b1); padding: 8px 12px;
  transform: rotate(3deg);
}
.lp-tramites__logo img { height: 44px; width: auto; display: block; }
/* Winbar con acento morado de marca (R2-T1) + fondo propio más oscuro
   (R3-T2, "ambiéntalo como si fuera la interfaz de macroning") — el
   morado del logotipo ES `--accent`/`--c-pop`, el acento de marca de
   toda la web, cero token nuevo. R3-T2: antes solo llevaba un borde
   inferior sobre la ventana clara; ahora la ventana entera es oscura, así
   que el winbar gana su PROPIO fondo aún más oscuro (`--c-navy-deep`,
   el token de footer, con un 12% de `--c-pop` mezclado para no perder el
   tinte morado) — se lee como una barra de título diferenciada, y el
   título pasa de `--accent` a `--c-lila` (más legible sobre ese fondo muy
   oscuro: 5.72:1 verificado, `--accent` a pelo rondaba peor contraste ahí
   por ser más saturado/oscuro). Dots sin cambios: el `color-mix` con
   `--card-border` ya daba 6.96–11.81:1 sobre el nuevo fondo (verificado),
   de sobra. */
.lp-tramites__winbar {
  display: flex; gap: 6px; align-items: center;
  margin: 0 0 var(--s-5); padding: var(--s-3) var(--s-4);
  background: color-mix(in srgb, var(--c-navy-deep) 88%, var(--c-pop) 12%);
  border: 1px solid color-mix(in srgb, var(--c-lila) 25%, transparent);
  border-radius: var(--r-sm);
}
.lp-tramites__winbar i { width: 10px; height: 10px; border-radius: 50%; display: block; }
.lp-tramites__winbar i:nth-child(1) { background: color-mix(in srgb, var(--accent) 45%, var(--card-border)); }
.lp-tramites__winbar i:nth-child(2) { background: color-mix(in srgb, var(--c-lila) 45%, var(--card-border)); }
.lp-tramites__winbar i:nth-child(3) { background: color-mix(in srgb, var(--hl) 45%, var(--card-border)); }
.lp-tramites__winbar .title { font-family: var(--font-mono); font-size: .6875rem; color: var(--c-lila); font-weight: 700; margin-left: 8px; }

/* --- Grid 3 piezas, reordenable por breakpoint (ver comentario arriba) */
.lp-tramites__body {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  grid-template-areas: "ctrl clics" "res clics";
  gap: var(--s-5) var(--s-7);
  align-items: start;
}
.lp-tramites__ctrl { grid-area: ctrl; display: flex; flex-direction: column; gap: var(--s-4); }
.lp-tramites__res { grid-area: res; display: flex; flex-direction: column; gap: var(--s-3); }
.lp-tramites__rightcol { grid-area: clics; display: flex; flex-direction: column; gap: var(--s-4); }

/* --- Sliders compactos: label + slider + valor SIEMPRE en línea (Fabio:
   "no display gigante"; R3-T2: "cuando el contador se pone en 3 cifras,
   se pasa a una segunda línea, no queda bien, déjalo siempre en la misma
   línea que la barra deslizadora") — sigue siendo UN control por fila
   (regla de Fabio de "un bloque por fila"): `__field` es la fila entera,
   el layout horizontal de dentro no cuenta como "dos inputs". R3-T2
   añade `.lp-tramites__control` (slider+output, cero hook/data-attr
   nuevo, JS no lo toca) para poder desacoplar el punto de fuga del label
   en móvil sin que el output llegue a saltar NUNCA de la fila del
   slider: por defecto `__field` es una fila `nowrap` (label fijo +
   control flexible); el thumb reescopado calca `.lp-calc
   input[type=range]`. */
.lp-tramites__field {
  display: flex; align-items: center; flex-wrap: nowrap; gap: var(--s-3);
}
.lp-tramites__field label {
  flex: 0 0 auto; min-width: 9.5em;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  color: var(--ink-inverse-2);
}
.lp-tramites__control {
  display: flex; align-items: center; gap: var(--s-3);
  flex: 1 1 auto; min-width: 0;
}
.lp-tramites__field input[type="range"] {
  flex: 1 1 120px; min-width: 80px;
  appearance: none; -webkit-appearance: none; height: 2px;
  /* Pista clara translúcida (R3-T2: ventana morada, ya no `--c-hairline`
     gris que se perdía sobre el gradiente). */
  background: color-mix(in srgb, var(--ink-inverse) 30%, transparent);
  border-radius: 2px; cursor: pointer;
  accent-color: var(--accent);
}
.lp-tramites__field input[type="range"]::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  width: 18px; height: 18px; border-radius: 4px;
  /* Thumb `--c-pop` con borde CLARO (R3-T2): el borde `--ink` (navy)
     original se fundía con el fondo morado oscuro. */
  background: var(--accent); border: 2px solid var(--ink-inverse); box-shadow: var(--shadow-b1);
  cursor: pointer; transition: transform var(--t-fast) var(--ease-pop);
}
.lp-tramites__field input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.1); }
.lp-tramites__field input[type="range"]::-moz-range-track {
  background: color-mix(in srgb, var(--ink-inverse) 30%, transparent);
  height: 2px; border-radius: 2px;
}
.lp-tramites__field input[type="range"]::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 4px;
  background: var(--accent); border: 2px solid var(--ink-inverse); box-shadow: var(--shadow-b1); cursor: pointer;
}
.lp-tramites__field input[type="range"]:focus-visible { outline: none; }
.lp-tramites__field input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: var(--shadow-b1), var(--ring); }
.lp-tramites__field input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: var(--shadow-b1), var(--ring); }
.lp-tramites__value {
  /* flex-shrink:0 YA impide el salto de línea del texto (nunca se
     encoge) — el min-width en ch (R3-T2) es además un colchón de ancho
     para que el bloque no "salte" de tamaño entre 1 y 4 cifras, con
     tabular-nums para que los dígitos midan siempre igual. */
  flex: 0 0 auto; white-space: nowrap; min-width: 8ch; text-align: right;
  font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-mono-sm);
  color: var(--ink-inverse); font-feature-settings: "tnum" 1;
  font-variant-numeric: tabular-nums;
}

/* --- Resultado: cifra + barra coherente, por fila (ver comentario de
   arriba sobre `techo`/suelos) --------------------------------------- */
.lp-tramites__row { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s-3); }
.lp-tramites__row-label {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-inverse-2);
  min-width: 7em;
}
/* `[data-heat]` REUTILIZA el color-mix de `.lp-calc--sheet__result`/
   `.lp-mods__n` (mismo criterio, selector propio) — heat 0 usa lila muy
   suave (no existe `--fam-c-soft`, ver docblock de macroning.php). Fondo
   mezclado con `--bg-surface` (blanco): sigue siendo un chip CLARO con
   texto navy heredado (`.lp-tramites__cifra` no fija `color` propio),
   ahora flotando sobre el morado de `__win` en vez de sobre blanco — R3-T2
   verificó que el contraste navy/chip claro no cambia, cero retoque
   aquí (punto 5 del encargo). */
.lp-tramites__cifra {
  display: inline-block; font-family: var(--font-disp); font-weight: 700;
  font-size: var(--fs-h3); line-height: 1; font-feature-settings: "tnum" 1;
  padding: 2px var(--s-3); border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--c-lila) 16%, var(--bg-surface));
  transition: background var(--t-base) var(--ease-out);
}
.lp-tramites__cifra[data-heat="1"] { background: color-mix(in srgb, var(--hl) 24%, var(--bg-surface)); }
.lp-tramites__cifra[data-heat="2"] { background: color-mix(in srgb, var(--danger) 16%, var(--bg-surface)); }
.lp-tramites__cifra[data-heat="3"] { background: color-mix(in srgb, var(--danger) 26%, var(--bg-surface)); }
/* Sin chip (background:none): esta cifra vive DIRECTAMENTE sobre el
   morado de `__win`, así que R3-T2 cambia `--ok-text` (verde para texto
   sobre CLARO, aquí ilegible) por un verde aclarado con `--ink-inverse`
   — sigue leyéndose como "el número bueno" sin perder AA (6.76:1/5.51:1
   verificados sobre las 2 paradas del gradiente). En `.is-hero` (más
   abajo) se sobreescribe a chip sólido, ver esa regla. */
.lp-tramites__cifra--macro {
  font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-h3);
  color: color-mix(in srgb, var(--ok) 55%, var(--ink-inverse)); background: none; padding: 0;
}
.lp-tramites__bar {
  height: 8px; border-radius: 999px; overflow: hidden;
  /* Pista clara translúcida (R3-T2), mismo criterio que el track del
     slider — `--c-hairline` gris se perdía sobre el morado. */
  background: color-mix(in srgb, var(--ink-inverse) 22%, transparent);
  margin-top: 4px;
}
.lp-tramites__bar-fill {
  display: block; height: 100%; border-radius: inherit; width: 0%;
  background: var(--c-lila);
  transition: width var(--t-reveal) var(--ease-out);
}
.lp-tramites__bar-fill[data-heat="1"] { background: var(--hl); }
.lp-tramites__bar-fill[data-heat="2"],
.lp-tramites__bar-fill[data-heat="3"] { background: var(--danger); }
.lp-tramites__bar--macro .lp-tramites__bar-fill { background: var(--ok); }

/* --- Estado activo (R2-T1): ya no hay cuenta atrás de la cifra "a mano"
   (Fabio: "no disminuyas el contador de la barra de a mano, sino destaca
   el contador de la barra de con macroning") — el número sigue mostrando
   SIEMPRE horasAno real (initLpTramites ya no lo toca al activar), y el
   único cambio visual es de PROTAGONISMO: la fila "a mano" se atenúa
   (`.is-dim`, sobre `.lp-tramites__row`/`.lp-tramites__bar`, los 2
   elementos hermanos de esa fila) y la fila "con Macroning" pasa a ser la
   destacada (`.is-hero`, mismos 2 elementos hermanos de esa otra fila).
   `initLpTramites()` solo alterna estas 2 clases por fila, nunca
   reconstruye ni recalcula nada aparte. */
.lp-tramites__row,
.lp-tramites__bar { transition: opacity var(--t-base) var(--ease-out); }
.lp-tramites__row.is-dim,
.lp-tramites__bar.is-dim { opacity: .45; }
/* La cifra "a mano" pierde su fondo de heat (selector con [data-heat]
   suma especificidad sobre `.lp-tramites__cifra[data-heat="N"]`, sin
   !important) y su barra se desatura a gris neutro. */
.lp-tramites__row.is-dim .lp-tramites__cifra[data-heat] {
  background: var(--bg-surface); color: var(--ink-soft);
}
.lp-tramites__bar.is-dim .lp-tramites__bar-fill[data-heat] { background: var(--ink-soft); }
/* La cifra "con Macroning" gana fondo sólido verde + check delante,
   opcional del guion, vía ::before (cero HTML nuevo). La barra suma un
   anillo --ok; su relleno ya transiciona `width` por defecto
   (`.lp-tramites__bar-fill`, `--t-reveal`), sin regla nueva. Sin
   transition propia en background/box-shadow (guardarraíl del brief:
   motion SOLO transform/opacity + width de barras ya establecido) —
   cambian al instante con la clase, como el resto de estados
   `[data-heat]` de esta misma pieza.
   R3-T2 (feedback Fabio literal: "no hagas tan grande el contador de
   macroning... simplemente ponle el background en verde"): se retira el
   salto a `--fs-h2` (la cifra mantiene SIEMPRE el tamaño base de
   `.lp-tramites__cifra--macro`) y el fondo pasa de un tinte suave sobre
   blanco a `--ok` SÓLIDO — con `--ok` a pelo el blanco encima da 3.49:1
   (AA de texto grande, pero raspando), así que se atenúa un 20% con
   `--ink` (mismo criterio de "cero HEX nuevo, solo color-mix de tokens")
   para subir a 4.70:1, AA de sobra incluso para texto normal, sin perder
   el verde reconocible que pidió Fabio. */
.lp-tramites__row.is-hero .lp-tramites__cifra--macro {
  padding: 2px var(--s-4); border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--ok) 80%, var(--ink));
  color: var(--ink-inverse);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--ok) 45%, transparent);
}
.lp-tramites__row.is-hero .lp-tramites__cifra--macro::before { content: "✓ "; }
.lp-tramites__bar--macro.is-hero {
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--ok) 32%, transparent);
}
.lp-tramites__frase {
  margin-top: var(--s-2);
  /* R3-T2: ventana morada, `--ink-body` (gris-navy para CLARO) pasa a
     `--ink-inverse-2` (lavanda claro sobre navy, 8.50:1/6.93:1 verificados
     sobre el gradiente). */
  font-family: var(--font-mono); font-style: italic; font-size: .9375rem; color: var(--ink-inverse-2);
  transition: opacity var(--t-base) var(--ease-out);
  /* Colchón SOLO — el min-height real lo fija landing.js inline
     (fixFraseHeight local de initLpTramites, mismo patrón que initLpCalc,
     midiendo las 4 frases reales de fraseFor() al ancho actual). */
  min-height: calc(var(--lh-body) * 3em);
}
.lp-tramites__nota {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-inverse-2);
  /* Hairline lila (R3-T2) — `--card-border` gris era invisible sobre el
     morado. */
  border-top: 1px solid color-mix(in srgb, var(--c-lila) 30%, transparent);
  margin-top: var(--s-3); padding-top: var(--s-3);
}

/* --- Nube de clics (panel navy) + interruptor ----------------------- */
.lp-tramites__clics {
  background: var(--fam-c); border-radius: var(--r-md);
  padding: var(--s-5); color: var(--ink-inverse-2);
  display: flex; flex-direction: column; gap: var(--s-3);
}
.lp-tramites__dots {
  display: flex; flex-wrap: wrap; gap: 7px; align-content: flex-start;
  /* Colchón de alto: entre 1 y 80 dots visibles el panel no debe saltar
     de tamaño de golpe. */
  min-height: 92px;
}
.lp-tramites__dot {
  --jx: 0px; --jy: 0px; --jr: 0deg;
  position: relative; flex: none;
  width: 12px; height: 12px; border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--c-lila) 55%, transparent);
  background: color-mix(in srgb, var(--c-lila) 22%, transparent);
  opacity: 0; transform: scale(.3) translate(var(--jx), var(--jy)) rotate(var(--jr));
  transition: transform var(--t-fast) var(--ease-pop), opacity var(--t-fast) var(--ease-out);
}
.lp-tramites__dot.is-shown { opacity: 1; transform: scale(1) translate(var(--jx), var(--jy)) rotate(var(--jr)); }
.lp-tramites__dot.is-out { opacity: 0; transform: scale(0) translate(var(--jx), var(--jy)) rotate(var(--jr)); }
.lp-tramites__dot.is-super {
  border-color: var(--ok);
  background: color-mix(in srgb, var(--ok) 32%, transparent);
}
.lp-tramites__dot.is-super::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 4px; height: 4px; border-radius: 50%; background: var(--ok);
}
/* Jitter pseudo-aleatorio FIJO por nth-child (nunca por JS): mezcla de
   módulos 5/7/11 para que 80 dots no se vean en rejilla perfecta, sin
   necesitar 80 reglas ni posicionamiento absoluto (el panel sigue
   creciendo por flujo normal, compacto). */
.lp-tramites__dot:nth-child(5n+1) { --jy: -4px; }
.lp-tramites__dot:nth-child(5n+2) { --jy: 3px; --jr: 6deg; }
.lp-tramites__dot:nth-child(5n+3) { --jx: 3px; }
.lp-tramites__dot:nth-child(5n+4) { --jx: -3px; --jr: -8deg; }
.lp-tramites__dot:nth-child(7n) { --jy: 5px; --jx: 2px; }
.lp-tramites__dot:nth-child(11n) { --jr: 12deg; }
.lp-tramites__label { margin: 0; font-family: var(--font-mono); font-size: var(--fs-mono-sm); }
.lp-tramites__label span { display: block; color: var(--ink-inverse); font-weight: 700; font-size: .9375rem; }
.lp-tramites__label small { display: block; margin-top: 4px; color: var(--ink-inverse-2); }

/* Interruptor: mono sobre CLARO en reposo (vive fuera del panel navy, en
   `__rightcol`) que se solidifica a navy + anillo lila al activar
   (`[aria-pressed="true"]`) — mismo idioma de familia C que el resto de la
   página. Ancho completo bajo la nube (antes centrado bajo un panel
   angosto; ahora es la columna derecha entera). Hover SOLO
   background/border/color (trampa del §3 "hover sin transform"), nunca
   `transform`. */
.lp-tramites__switch {
  display: block; width: 100%; text-align: center; padding: 10px 22px;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  color: var(--ink); background: var(--bg-surface);
  border: 2px solid var(--ink); border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out),
    color var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}
.lp-tramites__switch:hover { background: var(--fam-c); color: var(--ink-inverse); border-color: var(--fam-c); }
.lp-tramites__switch:focus-visible { outline: var(--ring); outline-offset: 2px; }
.lp-tramites__switch[aria-pressed="true"] {
  background: var(--fam-c); color: var(--ink-inverse); border-color: var(--c-lila);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--c-lila) 35%, transparent);
}
/* Pulso de invitación (IO sobre el propio botón, ver landing.js): SOLO
   opacity (guardarraíl "transform/opacity" del brief), 2 iteraciones y se
   para sola, sin necesidad de escuchar `animationend`. */
@keyframes lpTramInvite { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
.lp-tramites__switch.is-invite { animation: lpTramInvite 650ms ease-in-out 2; }

@media (max-width: 880px) {
  /* G12 (Fabio: la calculadora ocupa demasiado alto en movil, quiere ver
     sliders + contadores sin tanto scroll): recorta paddings verticales de
     la ventana y del panel de clics, y la nube de dots pasa a un tamano
     compacto (menos gap, dot mas pequeno) con `max-height`+`overflow:hidden`
     como techo si aun asi no caben las 80 unidades en pantalla. Logica JS
     intacta: solo cambia CSS de tamano, ningun selector ni data-attr. */
  .lp-tramites__win { padding: var(--s-4); }
  .lp-tramites__winbar { margin-bottom: var(--s-4); }
  .lp-tramites__body {
    grid-template-columns: 1fr;
    grid-template-areas: "ctrl" "clics" "res";
    gap: var(--s-4);
  }
  .lp-tramites__rightcol { gap: var(--s-3); }
  .lp-tramites__clics { padding: var(--s-4); gap: var(--s-2); }
  /* R2-R1 (Fabio smoke): sin recorte de clics. El max-height + overflow:hidden
     cortaba la fila superior (agravado por el jitter --jy negativo de los dots).
     Se quita el tope y se deja aire arriba; dots algo mas pequenos para que las
     ~80 piezas quepan en filas completas sin cortar. */
  .lp-tramites__dots {
    gap: 5px;
    min-height: 0;
    padding-top: 6px;
  }
  .lp-tramites__dot { width: 8px; height: 8px; }
}
/* R3-T2: por debajo de 560px, label+slider+output ya no caben en una
   sola fila — el LABEL pasa arriba (`.lp-tramites__field` se vuelve
   `wrap`, el label reclama toda la línea con `flex-basis:100%`), pero
   `.lp-tramites__control` (slider+output) sigue siendo un flex `nowrap`
   propio: comparten SIEMPRE la misma línea, el output nunca se separa
   del slider a ningún ancho. */
@media (max-width: 560px) {
  .lp-tramites__field { flex-wrap: wrap; }
  .lp-tramites__field label { flex-basis: 100%; }
  .lp-tramites__control { flex: 1 1 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .lp-tramites__dot { transition: none; }
  .lp-tramites__bar-fill { transition: none; }
  .lp-tramites__switch { transition: none; }
  .lp-tramites__switch.is-invite { animation: none; }
  /* R2-T1: is-dim/is-hero cambian de clase al instante — apaga la
     transición de opacity añadida arriba en fila/barra, y la de
     `background` que `.lp-tramites__cifra--macro` hereda de la regla base
     `.lp-tramites__cifra` (sin tocarla: esa regla sigue viva para el resto
     de estados `[data-heat]`, aquí solo se anula para el propio hero). */
  .lp-tramites__row,
  .lp-tramites__bar,
  .lp-tramites__row.is-hero .lp-tramites__cifra--macro { transition: none; }
}

/* --- SmartAudit: tokens de marca del PRODUCTO (Sesión 6, R1-T5) --------
   colores de marca del PRODUCTO SmartAudit, verificados de su
   SISTEMA-DISENO.md §1 (02_Servicios/C_SaaS/SmartAudit/SISTEMA-DISENO.md),
   scoped a esta página (`.page-smartaudit`, body class ya fijada por
   $PAGE['body_class'] en smartaudit.php), NO son tokens de la web — solo
   para mocks/decoración del producto dentro de /smartaudit (badges de
   tipo de papel, hoy en `.sa-viewer`, más abajo). EXCEPCIÓN DOCUMENTADA a
   la regla "sin HEX a pelo" de design-system.md §1: son aproximaciones hex
   de los OKLCH canónicos de un producto EXTERNO a la paleta de esta web
   (el propio SmartAudit tiene su propio sistema de diseño, con su propio
   acento teal, sus propios tintes de tipo de papel), mismo criterio que
   un badge de portal oficial (AEAT/TGSS/DEHú/Catastro, `.badges-portal`)
   — nunca se usan fuera de esta página ni se prometen como primitivos
   nuevos del design-system.md de esta web.
   R3-T5 (feedback Fabio: "que simulara una carpeta de papeles de trabajo
   de smartaudit... básicamente como si fuera un visor o iframe de
   smartaudit"): se suman los NEUTROS y la SEMÁNTICA de estado del propio
   SISTEMA-DISENO.md §1.1/§1.3 que el visor `.sa-viewer` necesita para
   leerse como la app real (fondo frío, texto grisazulado, éxito/aviso) —
   misma excepción documentada arriba, mismo scope. `--sa-text`/`--sa-
   text-2`/`--sa-border`/`--sa-bg`/`--sa-surface` son los hex EXACTOS que
   ya trae la tabla §1.1 del sistema de diseño del producto (no una
   aproximación nueva); `--sa-surface-hover` sí es aproximación de
   `oklch(0.97 0.014 200)`. `--sa-success`/`--sa-warn` (+ `-weak`)
   aproximan `--success`/`--warn` de §1.3 (esa tabla no trae hex, solo
   OKLCH). */
.page-smartaudit {
  --sa-accent: #1F9E96; --sa-accent-weak: #E1F3F1;
  --sa-word: #4867D6; --sa-word-bg: #EDF1FD;
  --sa-excel: #2E8B57; --sa-excel-bg: #EAF6EF;
  --sa-tb: #8A4FD3; --sa-tb-bg: #F3ECFB;
  --sa-sumaria: #1F8E9E; --sa-sumaria-bg: #E8F4F6;
  --sa-text: #38404D; --sa-text-2: #6A7280;
  --sa-border: #E5E8ED; --sa-bg: #F7F8FA;
  --sa-surface: #FDFEFF; --sa-surface-hover: #EDF2F5;
  --sa-success: #1F9E62; --sa-success-weak: #E6F5EC;
  --sa-warn: #CE8A2A; --sa-warn-weak: #FBF1DC;
}

/* --- Botonera del tour del hero (Sesión 6, R1-T5; fila en R1/ROADMAP p.1)
   Los dos botones reutilizan `.rrss-pause` (pages.css, ya cubre WCAG 2.2.2
   con el MISMO patrón que casos.php: id del vídeo + id del botón,
   `aria-pressed`, texto que alterna pausar/reanudar) y solo ajustan el
   margen para vivir DENTRO de `.media-frame`, bajo `.media-frame__body`,
   en vez de bajo una rejilla de clips — mismo criterio de "extender, no
   reinventar" que ya aplicó `.caso-media__pause` (pages.css) a esta misma
   clase base.
   R1 (ROADMAP p.1) suma `.sa-tour__zoom` ("ver más grande", dispara el
   lightbox genérico `[data-lightbox]` de motion.js) y por eso los envuelve
   en `.sa-tour__acts`: antes `.sa-tour__pause` se centraba sola con
   `margin:auto` y un segundo botón se le habría apilado DEBAJO. El margen
   superior sube al contenedor a propósito — dentro de un flex centrado, un
   `margin-inline:auto` en cada hijo los empujaría a los extremos opuestos
   en vez de centrarlos juntos. */
.sa-tour__acts {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--s-3); margin-top: var(--s-3);
}
.sa-tour__pause, .sa-tour__zoom { margin: 0; }

/* --- Visor real de SmartAudit `.sa-viewer` (Sesión 6, R3-T5, /smartaudit)
   ------------------------------------------------------------------------
   SUSTITUYE ENTERO el mosaico de cards `.lp-suite` v1/v2 (histórico: Tarea
   7 card-navy de catálogo → R1-T5 fichas de papel en rejilla 3×3 → R2-T3
   fotos+hover-zoom, ver design-system.md §4 para el historial completo).
   Feedback LITERAL de Fabio: "en lugar de una cuadrícula, me gustaría más
   que simulara una carpeta de papeles de trabajo de smartaudit, como una
   recreación de la interfaz, y que cada módulo fuera una fila, como si
   fuera un papel de trabajo, con la referencia, el título, descripción, y
   tipo de papel, también si cabe puedes añadir último editor, o alguno
   que se esté editando, y la foto a la derecha del todo. En esa parte usa
   misma tipografía, colores e iconos que SmartAudit si puedes...
   básicamente como si fuera un visor o iframe de smartaudit en esa
   parte." + corrección: "no recortes las fotos, porque algunas partes de
   la captura se ve cortada".
   ESTRUCTURA (recreación del shell de la app, de arriba abajo):
   `__top` (barra navy `--c-navy-2`, MISMO navy que el header real y que
   `.media-frame--dark`/`.lp-vigia`, no un HEX nuevo — marca+avatar) →
   `__crumb` (migas mono-copy tipo cockpit) → `__section` (la CARPETA raíz:
   cabecera tintada `--sa-tb-bg` = el "índigo suave" pedido, más cercano de
   los 5 tintes de producto ya existentes; Folder + código `1000` + título
   uppercase + CountPill) → `__cols` (cabecera de columnas text-label) →
   `__rows` (9 `__row`, una por módulo) → `__foot` (atribución SmartProcess,
   SIN enlace: es decorativo dentro del visor, el enlace real ya vive en el
   footer de verdad de la página).
   TIPOGRAFÍA: SOLO dentro de `.sa-viewer` cambia a IBM Plex (fonts.css,
   misma sesión) vía tokens LOCALES `--sa-font-sans`/`--sa-font-mono`, que
   encadenan la fuente del producto con la de la web como fallback (nunca
   FOUT roto si el .woff2 tarda). Fuera de este bloque, cero rastro de
   IBM Plex en la web.
   RADIOS: nunca `--r-lg`/`--r-md` de esta web (14px/10px, demasiado
   redondeados) — SmartAudit es "casi cuadrado" (SISTEMA-DISENO.md §5,
   4-6px), así que el marco entero y las piezas internas usan `--r-sm`
   (6px) / `--r-xs` (3px), ya dentro de ese rango, sin inventar un radio
   nuevo.
   MARCO "COMO UNA VENTANA": `--shadow-soft` + borde `--card-border`.
   R1 (ROADMAP p.1) RETIRA el `overflow:visible` explícito que llevaba
   `.sa-viewer`: se declaraba para que el marco no recortara el hover-zoom
   de las fotos, y ese zoom ya no existe (ahora la foto se amplía en el
   lightbox `[data-lightbox]`, fuera del flujo). Al ser `visible` el valor
   inicial de `overflow`, quitarlo no cambia nada pintado — solo deja de
   documentar como deliberado un guardarraíl que ya no defiende nada. Lo
   que SÍ sigue haciendo falta, e independiente de aquello: como el marco
   no recorta, `__top` y `__foot` (las dos piezas con fondo propio que
   tocan el borde exterior) redondean SOLO sus dos esquinas externas a mano
   (`calc(var(--r-sm) - 1px)`, resta el grosor del borde exterior); sin eso
   sus fondos cuadrarían las esquinas del marco.
   FOTOS SIN RECORTAR (la corrección de Fabio): a diferencia de
   `.lp-suite__mod img` (v1/v2, `object-fit:cover` + alto fijo 180px, que
   SÍ recortaba), aquí `width:150px;height:auto` — el aspect ratio real de
   cada captura se respeta entero, así que las filas NO miden lo mismo
   (`avisos`, 1099×759, es más alta que el resto a 1000×48x-513) — a
   propósito, es la foto ENTERA o nada.
   AMPLIAR LA FOTO — R1 (ROADMAP p.1), feedback Fabio LITERAL: "quita que
   se haga zoom a la imagen con el hover en la fila. Pon que se pueda
   aumentar la foto haciendo clic en ella, nada más. Y dale una buena
   calidad a las fotos". SALE el hover-zoom (un `transform:scale` de 2.2
   sobre el `<img>`, con `transform-origin:right center` y `z-index` en la
   fila para que no la taparan las vecinas) y ENTRA un
   `<button data-lightbox>` que envuelve cada `<img>`: la miniatura sigue a
   150px pero al pulsarla se abre `.sp-lightbox` (final de este archivo,
   `initLightbox()` de motion.js) con el PNG a su tamaño nativo de 1000px.
   Es mejor por tres motivos, no solo por gusto: (1) el hover no existía ni
   en táctil ni por teclado, un `<button>` sí; (2) el zoom in-place se
   comía las filas vecinas y obligaba a `z-index`+`overflow:visible` en
   toda la cadena de ancestros, ahora innecesarios; (3) 150px×2.2 = 330px
   seguía siendo una interpolación, mientras que el lightbox pinta los
   1000px reales del archivo. Techo de calidad conocido: los PNG son de
   1000px de ancho, así que el lightbox los enseña nítidos pero no da para
   más — subir de ahí exige RECAPTURAR la app a mayor resolución (pendiente
   de Fabio, no de CSS). El hover se queda como mera pista de que la cosa
   se pulsa: `box-shadow` (`--shadow-b1`) también en `:focus-visible`.
   ICONOS: 7 glifos de `lucide-react` (MIT — ver comentario de atribución
   en el HTML) calcados EXACTOS del propio SmartAudit (`apps/web/src/
   components/ui/icons.tsx`: word→FileText, excel→Sheet, generic→
   FileType2, trial_balance→Scale, excel_report→Columns3, lead_sheet→
   ListChecks, carpeta→Folder), inline `stroke-width:1.7` (SISTEMA-
   DISENO.md §7). 9 filas para 7 iconos: 2 pares reutilizados a propósito
   siguiendo la MISMA agrupación por afinidad que ya usaban los badges de
   tipo de la v1/v2 (`papeles`+`avisos`→word/FileText, `balance`+
   `firmas`→tb/Scale) — refuerza visualmente el mismo agrupamiento en vez
   de contradecirlo.
   REFERENCIAS: códigos plausibles estilo grupo del PGC (1100/1200/2240/
   7110/3100/5100/6100/8100/9100) en vez de los índices heterogéneos de la
   v1/v2 (`0.2`/`B.7`/`X-REF`...) — más cerca del lenguaje real de
   referencia de auditoría. `2240` (firmas) y `7110` (roles) se REPITEN
   literales de la v1/v2 a propósito (esos dos módulos ya tenían código
   plausible); el resto son nuevos.
   ESTADO: 7/9 filas `Disponible` (`--sa-success`, sin pulso) — 1 fila
   (`balance`) `AVA está editando` (`--sa-warn`, punto late, reutiliza
   `@keyframes lpBarCPulse` ya existente en este archivo con la duración
   PROPIA de SmartAudit, 1.6s, SISTEMA-DISENO.md §6 — no una animación
   nueva) — 1 fila (`office365`) `Editado por ADM · hoy` (texto plano +
   chip de iniciales pequeño, sin punto: heurística `recent` del producto
   real, SISTEMA-DISENO.md §8, nunca afirma quién edita ahora mismo).
   `prefers-reduced-motion`: el punto deja de latir, sin desaparecer.
   RESPONSIVE (≤880px, mismo corte que el resto del sitio): `grid-
   template-areas` reordena la fila (ref+tipo en una línea, título+
   descripción debajo, estado debajo, foto al final a ancho natural) SIN
   marcado nuevo — las mismas 5 piezas (`ref`/`main`/`tipo`/`estado`/
   `photo`) solo cambian de área. La cabecera de columnas se oculta (ya no
   hay columnas que rotular). */
.sa-viewer {
  --sa-font-sans: 'IBM Plex Sans', var(--font-body);
  --sa-font-mono: 'IBM Plex Mono', var(--font-mono);
  font-family: var(--sa-font-sans);
  margin-top: var(--s-6);
  background: var(--sa-surface);
  border: 1px solid var(--card-border);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-soft);
}
.sa-viewer__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3);
  background: var(--c-navy-2);
  padding: var(--s-3) var(--s-5);
  border-top-left-radius: calc(var(--r-sm) - 1px);
  border-top-right-radius: calc(var(--r-sm) - 1px);
}
.sa-viewer__brand { font-size: .9375rem; font-weight: 600; color: var(--ink-inverse); letter-spacing: -.01em; }
.sa-viewer__avatar {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 28px; height: 28px; border-radius: var(--r-xs);
  background: var(--sa-accent-weak); color: var(--sa-accent);
  font-family: var(--sa-font-mono); font-size: .6875rem; font-weight: 600; font-style: normal;
}
.sa-viewer__crumb {
  padding: var(--s-2) var(--s-5);
  background: var(--sa-bg); border-bottom: 1px solid var(--sa-border);
  font-size: .78125rem; color: var(--sa-text-2);
}
.sa-viewer__section { padding: var(--s-4) var(--s-5) var(--s-5); background: var(--sa-bg); }
.sa-viewer__section-head {
  display: flex; align-items: center; gap: var(--s-3);
  margin-bottom: var(--s-3); padding: var(--s-3) var(--s-4);
  background: var(--sa-tb-bg); border: 1px solid var(--sa-border); border-radius: var(--r-xs);
}
.sa-viewer__section-head svg { width: 18px; height: 18px; color: var(--sa-tb); flex: none; }
.sa-viewer__section-code { font-family: var(--sa-font-mono); font-size: .8125rem; font-weight: 600; color: var(--sa-tb); }
.sa-viewer__section-title {
  flex: 1; min-width: 0;
  font-size: .625rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--sa-text);
}
.sa-viewer__count {
  flex: none;
  font-family: var(--sa-font-mono); font-size: .6875rem; font-weight: 600;
  color: var(--sa-tb); background: var(--sa-surface); border: 1px solid var(--sa-border);
  padding: 2px 8px; border-radius: var(--r-xs);
}
.sa-viewer__cols {
  display: grid; grid-template-columns: 64px 1fr 130px 180px 150px; gap: var(--s-4);
  padding: 0 var(--s-2) var(--s-2);
  font-size: .625rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--sa-text-2);
}
.sa-viewer__rows { list-style: none; margin: 0; padding: 0; }
.sa-viewer__row {
  display: grid; grid-template-columns: 64px 1fr 130px 180px 150px;
  grid-template-areas: "ref main tipo estado photo";
  align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--s-2);
  background: var(--sa-surface); border-top: 1px solid var(--sa-border);
  transition: background var(--t-fast) var(--ease-out);
}
.sa-viewer__row:last-child { border-bottom: 1px solid var(--sa-border); }
/* R1 (ROADMAP p.1): sin `z-index:5; position:relative` — existían SOLO
   para levantar la foto ampliada por encima de las filas vecinas, y ya no
   hay foto ampliada dentro de la fila (ahora se abre en el lightbox). */
.sa-viewer__row:hover { background: var(--sa-surface-hover); }
.sa-viewer__ref { grid-area: ref; font-family: var(--sa-font-mono); font-size: .8125rem; font-weight: 600; color: var(--sa-text-2); }
.sa-viewer__main { grid-area: main; display: flex; align-items: flex-start; gap: var(--s-3); min-width: 0; }
.sa-viewer__icon { width: 18px; height: 18px; margin-top: 2px; color: var(--sa-text-2); flex: none; }
.sa-viewer__title { margin: 0; font-size: .8125rem; font-weight: 600; color: var(--sa-text); }
.sa-viewer__desc { margin: 2px 0 0; font-size: .78125rem; color: var(--sa-text-2); }
.sa-viewer__badge {
  grid-area: tipo; justify-self: start;
  font-family: var(--sa-font-mono); font-size: .6875rem; font-weight: 700;
  color: var(--sa-tipo); background: var(--sa-tipo-bg);
  padding: 3px 8px; border-radius: var(--r-xs);
}
.sa-viewer__status { grid-area: estado; display: flex; align-items: center; gap: var(--s-2); font-size: .78125rem; color: var(--sa-text); }
.sa-viewer__dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--sa-success); }
.sa-viewer__dot--editing { background: var(--sa-warn); animation: lpBarCPulse 1.6s ease-in-out infinite; }
.sa-viewer__chip {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 20px; height: 20px; border-radius: var(--r-xs);
  background: var(--sa-accent-weak); color: var(--sa-accent);
  font-family: var(--sa-font-mono); font-size: .5625rem; font-weight: 700; font-style: normal;
}
/* R1 (ROADMAP p.1): fuera el hover-zoom (Fabio: "quita que se haga zoom
   con el hover en la fila; que se pueda aumentar haciendo clic en ella").
   La miniatura pasa a ser un botón real: accesible por teclado y táctil,
   que era justo lo que el hover no daba. */
.sa-viewer__photo { grid-area: photo; justify-self: end; }
.sa-viewer__photo button {
  display: block; padding: 0; background: none; border: 0; cursor: zoom-in;
  border-radius: var(--r-xs);
}
.sa-viewer__photo img {
  display: block; width: 150px; height: auto;
  border: 1px solid var(--sa-border); border-radius: var(--r-xs);
  transition: box-shadow var(--t-base) var(--ease-out);
}
.sa-viewer__photo button:hover img,
.sa-viewer__photo button:focus-visible img { box-shadow: var(--shadow-b1); }
.sa-viewer__foot {
  padding: var(--s-3) var(--s-5);
  background: var(--sa-surface); border-top: 1px solid var(--sa-border);
  border-bottom-left-radius: calc(var(--r-sm) - 1px);
  border-bottom-right-radius: calc(var(--r-sm) - 1px);
  font-size: .6875rem; color: var(--sa-text-2);
}
.sa-viewer__total {
  margin-top: var(--s-6); padding-top: var(--s-5);
  font-family: var(--font-mono); font-size: var(--fs-sm); font-weight: 700;
  color: var(--ink);
  border-top: 4px double var(--sa-accent);
}
@media (prefers-reduced-motion: reduce) {
  .sa-viewer__photo img { transition: none; }
  .sa-viewer__dot--editing { animation: none; }
}
@media (max-width: 880px) {
  .sa-viewer__cols { display: none; }
  .sa-viewer__row {
    grid-template-columns: auto 1fr;
    grid-template-areas: "ref tipo" "main main" "estado estado" "photo photo";
    row-gap: var(--s-2);
  }
  .sa-viewer__badge { justify-self: end; }
  .sa-viewer__photo { grid-area: photo; justify-self: stretch; }
  .sa-viewer__photo img { width: 100%; max-width: 280px; }
  /* R1 (ROADMAP p.1): retirada la anulación del hover-zoom en este escalón
     (`transform:none;box-shadow:none`) — ya no hay hover-zoom que anular, y
     el móvil es justo donde más falta hacía poder ampliar la foto: aquí el
     botón nuevo SÍ funciona, cosa que el hover nunca hizo. */
}

/* --- Tematización final de /smartaudit (Sesión 6, R1-T6) --------------
   Dosis mínima (2 detalles) para que la página "huela" a auditoría sin
   sumar stickers nuevos. `.sa-ref`: código mono decorativo aria-hidden
   junto al `.tag` de un `.sec-head` (mismo lenguaje de referencia de
   auditoría que `.lp-suite__code`, ver bloque de arriba, pero vive suelto
   en la cabecera de sección, no dentro de esa rejilla), tintado con
   `--sa-accent` (marca del PRODUCTO, custom property ya scoped a
   `.page-smartaudit` más arriba en este archivo) a opacidad reducida para
   que quede subordinado al `.tag` real, nunca compitiendo con él. Usado 3
   veces: `#casos` (`B.7`), el versus (`2240`) y `#como-funciona`
   (`7110`) — códigos de ejemplo, decorativos, sin correspondencia con
   ningún papel real de la app. Reutiliza el `<span>` inline que ya sigue
   al `.tag` en el HTML, cero marcado nuevo. */
.sa-ref {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  color: var(--sa-accent); opacity: .6; margin-left: var(--s-3);
}
/* (R7: aquí vivía el retintado a `--sa-accent` del tickmark del versus.
   Se fue con `.lp-versus` en la limpieza; el chat que lo sustituye usa el
   verde genérico `--ok`, que sobre blanco y sobre navy ya está medido.) */

/* --- Feed de actividad `.sa-feed` (Sesión 6, R2-T4, /smartaudit) -------
   Feedback Fabio: "añade alguna animación parecida a la de la macro de
   macroning con el scroll, pero en este caso que sean los pasos que va
   pasando un papel de trabajo: creo el papel, modifico, firmo
   preparador, firmo revisor... los pasos como si fueran creándose en el
   feed de actividad de smartaudit, con las iniciales del usuario que lo
   hizo, los iconos, la fecha".
   La sección "a tu medida" (`.mac-checks`, arriba en este archivo) se
   envuelve en `.mac-antimiedo` — EL MISMO wrapper de grid 2 columnas de
   macroning.php, más abajo en este archivo (cero CSS nuevo de layout: ya
   genérico, `1fr` en móvil y `1fr 1fr` desde 881px, `align-items:start`)
   — y gana un panel decorativo a la derecha, `.sa-feed`, con
   `align-self:stretch` (mismo mecanismo que ya usa `.lp-vigia`, más abajo,
   para igualar el alto de la columna de texto vecina).
   A diferencia de `.lp-vigia` (log mono oscuro, estética de terminal),
   `.sa-feed` es una TARJETA CLARA (`--bg-surface`/`--c-hairline`/`--r-md`/
   `--shadow-soft`, mismo lenguaje de `.media-frame` sobre fondo navy):
   replica el feed de actividad REAL de la app (`sa-actividad.png`: filas
   con chip de iniciales cuadrado + icono de acción + texto + hora) en vez
   del terminal que reutiliza Macroning — aquí la ficción es "una ventana
   de la app real, abierta encima de la web".
   7 filas en `[data-sa-feed]` (contenedor `aria-hidden="true"` entero: el
   contenido real y accesible sigue siendo `.mac-checks`, a la izquierda;
   un `<p class="sr-only">` describe el panel para quien no lo ve) narran
   el ciclo de vida de un papel: alta → edición → firma preparador → nota
   de revisión → respuesta → firma revisor → cierre trazado NIA-ES —
   iniciales FICTICIAS genéricas (`ADM`/`AVA`, mismas que la demo real de
   la captura de referencia). La última fila (cierre) lleva chip `SA` (el
   sistema, no un usuario) y tinte `--sa-accent-weak` (`.sa-feed__row--fin`)
   para destacarla como hito final, sin usar ninguna raya larga.
   REVELADO CON EL SCROLL: cada `.sa-feed__row` arranca apagada (opacity
   ~.15 + `translateY` pequeño, nunca display/altura) y gana `.is-on`
   según el scroll recorre `.sa-feed` — MISMO helper genérico
   `lpScrubList()` (landing.js) que ya usa `initLpVigia()`; aquí detrás de
   `initSaFeed()`, capa fina que solo localiza `[data-sa-feed]`/
   `.sa-feed__row` y delega. Sin GSAP o con `reduce`, las 7 arrancan
   `.is-on` de golpe (mismo criterio que el resto del archivo). Ver
   design-system.md §4. */
.sa-feed{
  display:flex; flex-direction:column; align-self:stretch;
  background:var(--bg-surface); border:1px solid var(--c-hairline);
  border-radius:var(--r-md); box-shadow:var(--shadow-soft);
  padding:var(--s-5);
}
.sa-feed__head{
  display:flex; align-items:center; gap:var(--s-2);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--ink-soft); text-transform:uppercase; letter-spacing:.04em;
  padding-bottom:var(--s-3); margin-bottom:var(--s-3);
  border-bottom:1px solid var(--c-hairline);
}
.sa-feed__dot{
  width:7px; height:7px; border-radius:50%; background:var(--sa-accent); flex:none;
}
.sa-feed__body{
  flex:1; display:flex; flex-direction:column; justify-content:space-between; gap:var(--s-1);
}
.sa-feed__row{
  display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-2) 0;
  opacity:.1; transform:translateY(12px);
  transition:opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.sa-feed__row.is-on{ opacity:1; transform:translateY(0); }
.sa-feed__row + .sa-feed__row{ border-top:1px solid var(--c-hairline); }
.sa-feed__row--fin{
  background:var(--sa-accent-weak); border-radius:var(--r-sm);
  padding:var(--s-2) var(--s-3);
}
.sa-feed__chip{
  display:flex; align-items:center; justify-content:center; flex:none;
  width:26px; height:26px; border-radius:var(--r-xs);
  font-family:var(--font-mono); font-size:.6875rem; font-weight:700;
  color:var(--sa-accent); background:var(--sa-accent-weak);
}
.sa-feed__glifo{ flex:none; width:1.1em; text-align:center; color:var(--sa-accent); font-weight:700; }
.sa-feed__text{ flex:1; min-width:0; font-size:var(--fs-sm); color:var(--ink-body); }
.sa-feed__hora{
  flex:none; font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft);
}
@media (prefers-reduced-motion: reduce){
  .sa-feed__row{ transition:none; }
}

/* --- "Antes de que te preocupes" en 2 columnas + `.lp-vigia` (Sesión 6,
   R1-T2, /macroning — feedback Fabio: "mucho hueco a la derecha del
   texto, añadir una pieza visual") -------------------------------------
   `.mac-antimiedo`: envuelve la columna de texto ya existente (tag/h2/p/
   `.mac-checks`, TODOS literales, sin tocar ni un byte) para poder
   emparejarla con un panel decorativo a la derecha — 1 columna hasta
   880px (mismo corte que el resto del sitio), 2 columnas desde 881px.
   `.lp-vigia` (`aria-hidden="true"` en el HTML: es puramente
   ilustrativo, el contenido real ya está en `.mac-checks` a la
   izquierda) reutiliza el MISMO winbar que el hero/calculadora de esta
   página (`.media-frame.media-frame--dark` + `.media-frame__bar`, ya
   genéricos) en vez de inventar un componente de "ventana" nuevo — solo
   `max-width:24rem` para que quede "pequeño" (petición explícita del
   guion, nunca un sticker) dentro de una columna de grid más ancha.
   R2-T2 (feedback Fabio: "que la infografía se fuera ejecutando con el
   scroll, que fueran saliendo los pasos que se van ejecutando, hasta
   completar todo el alto del texto") reescribe la v1 en dos frentes:
   1) ALTURA — `.mac-antimiedo` se queda en `align-items:start` (regla
      genérica, no cambia), pero `.lp-vigia` gana `align-self:stretch` +
      `display:flex;flex-direction:column` PROPIOS para estirarse al alto
      real de la fila de grid (= alto de la columna de texto); `__frame`
      y `__body` heredan `flex:1` en cascada para que el log llegue de
      verdad hasta abajo. `__body` reparte las filas con
      `justify-content:space-between` (el `gap` declarado es el SUELO,
      nunca se comprime por debajo de `--s-3`; el espacio extra hasta
      completar la altura se reparte solo, sin alturas fijas inventadas).
   2) EJECUCIÓN — pasa de 4 líneas estáticas a 8 que se "ejecutan" con el
      scroll: cada `.lp-vigia__line` arranca apagada (opacity ~.15 +
      translateY pequeño, NUNCA display/altura — el layout no puede
      saltar) y gana `.is-on` progresivamente. Motion SOLO transform/
      opacity, transición corta (`--t-fast`). El progreso lo calcula
      `initLpVigia()` sobre `[data-lp-vigia]` con el helper GENÉRICO
      `lpScrubList()` (landing.js — agnóstico a esta sección, se
      reutiliza en /smartaudit): con GSAP, ScrollTrigger+scrub sobre el
      panel; sin GSAP o con `reduce`, las 8 arrancan `.is-on` de golpe
      (mismo criterio que el resto del archivo).
   Marcas por fila: `[ok]` (6 de 8) como check `✓` en `--ok` dentro de
   `<mark>` (glifo literal, mismo idioma que los `✓` de index.php/
   `.lp-flow-card__list li.is-ok::before` — nunca `--ok-text`, que cae a
   3:1 sobre navy, misma trampa de contraste ya documentada para
   `.lp-bar-c`); `[ajustada]` como `.lp-vigia__dot` ESTÁTICO en
   `--c-lila` (DEHú cambió, la macro ya está al día); `[vigilando]`
   (última fila) como el mismo dot + modificador `.lp-vigia__dot--pulse`,
   que reutiliza el MISMO `@keyframes lpBarCPulse` que
   `.lp-bar-c__item.is-run` (arriba en este archivo) — no una animación
   nueva, solo ahora expresada como modificador en vez de aplicarse
   siempre. `<mark>` se despoja de su fondo/color nativos (amarillo/negro
   de UA stylesheet): aquí es solo un contenedor semántico para el color
   de estado, nunca un resaltador literal.
   Colchón `.lp-vigia{padding-top}` + `overflow:hidden` que `.sec--inverse`
   ya aplica (regla genérica, arriba de este archivo): la sombra
   `--shadow-soft` del `.media-frame` nunca sangra sobre el borde de la
   sección. Ver design-system.md §4. */
.mac-antimiedo{
  display:grid; grid-template-columns:1fr; gap:var(--s-7);
  align-items:start;
}
@media (min-width:881px){
  .mac-antimiedo{ grid-template-columns:1fr 1fr; }
}
.lp-vigia{
  display:flex; flex-direction:column; align-self:stretch;
  padding-top:var(--s-2);
}
.lp-vigia__frame{ max-width:24rem; display:flex; flex-direction:column; flex:1; }
.lp-vigia__body{
  flex:1; display:flex; flex-direction:column; justify-content:space-between;
  gap:var(--s-3);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
}
.lp-vigia__line{
  margin:0; color:var(--ink-inverse-2);
  display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--s-2);
  opacity:.1; transform:translateY(12px);
  transition:opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.lp-vigia__line.is-on{ opacity:1; transform:translateY(0); }
.lp-vigia__mark{ font-weight:700; background:transparent; padding:0; }
.lp-vigia__mark--ok{ color:var(--ok); }
.lp-vigia__dot{
  width:7px; height:7px; border-radius:50%; flex:none;
  align-self:center;
  background:var(--c-lila);
}
.lp-vigia__dot--pulse{ animation:lpBarCPulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce){
  .lp-vigia__line{ transition:none; }
  .lp-vigia__dot--pulse{ animation:none; }
}

/* --- Ventanas de portal que se completan solas `.lp-portales` (Sesión 6
   + REDISEÑO R1-b/ROADMAP p.1, /macroning) ----------------------------
   Sección "lo que Macroning ya hace hoy": 8 mini VENTANAS DE NAVEGADOR,
   una por trámite, donde Macroning ya ha hecho el trabajo. La sección es
   clara (`sec--surface`) y las ventanas navy (`--bg-inverse`), que es
   donde hace falta el fondo oscuro: los badges de portal llevan el nombre
   del organismo en texto BLANCO (mismo motivo que `.portals-strip` en
   pages.css).

   REDISEÑO R1-b (Fabio: "me gusta la idea de las tarjetas de los portales,
   pero no me gusta como está ejecutada; dale una vuelta al diseño, que se
   mantenga la idea de simular el sitio web de cada trámite"). El diseño
   viejo (badge flotando fuera + rotado, barra de 3 dots + url, 2 campos
   abstractos) se sustituye por una ventana de navegador CREÍBLE:
   · `__chrome`: barra del navegador con semáforos (`__dots`) y una barra
     de direcciones REAL en pill (`__addr`) con candado (`__lock`, en
     `--ok`) + url mono (`__url`).
   · `__page`: el "contenido" de la página del portal. `__site` es el
     encabezado del sitio con el LOGO del organismo INTEGRADO (`__logo`,
     24px — ya no flota fuera; resuelve a la vez "logos más pequeños") a la
     izquierda y el índice `/0X` (`__num`) a la derecha. Las ventanas 05-08
     no son portales oficiales sino procesos internos: en vez de logo
     muestran `__host` (favicon `M` de Macroning + dominio mono).
   · `__task`/`__desc`: título y descripción del trámite — el ÚNICO
     contenido real (el resto es decorativo, `aria-hidden`). Copy 1:1 del
     diseño anterior, solo reorganizado.
   · `__run`: la ejecución. `__track`/`__fill` = barra de progreso que se
     LLENA (scaleX 0→1) al entrar en viewport; `__foot` = `[✓ hecho solo]`
     (`__done`, verde) + la métrica (`__count`).

   MOTION: `initLpPortales()` (landing.js) añade `.is-done` a cada `__win`
   con stagger (~120ms) al entrar la primera en viewport (IO threshold .6,
   disconnect). Solo `transform`/`opacity`. Los estados iniciales cuelgan
   de `html.js` (sin JS se ve la barra llena y el estado completo, no un
   progreso congelado a medias). `reduce`: `is-done` a las 8 de golpe y la
   media query de abajo apaga la transición.

   GRID: 4 columnas ≥1101px (2 filas exactas), 2 en 881-1100px, 1 ≤880px.
   Contraste sobre navy: `__task`/`--ink-inverse`, `__desc`/`__url`/`__host`
   /`__count` `--ink-inverse-2`, `__num` `--c-lila`, `__done`/`__fill`/
   `__lock` `--ok` (4.65:1, no `--ok-text` que es para fondos claros),
   favicon sobre `--accent`. */
[data-lp-portales] .tag{ color:var(--accent); }
.lp-portales__lbl{
  display:block; margin-top:var(--s-5); margin-bottom:var(--s-2);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-soft);
}
.lp-portales__grid{
  /* minmax(0,1fr) y NO 1fr a secas: las urls mono con nowrap tienen un
     min-content mayor que la columna y con 1fr (= minmax(auto,1fr)) el
     grid se ensancha y la 4a ventana se sale del wrap (blowout medido en
     QA de orquestador R1: win04 hasta x=1385 en un viewport de 1366). */
  display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:var(--s-5);
  margin-top:var(--s-8);
}
/* R1-b: la ventana ES una mini-ventana de navegador. `overflow:hidden`
   para que el chrome (barra oscura) llegue a los bordes sin asomar el
   radio. Sombra sólida de la casa; hover la agranda y la tiñe de acento. */
.lp-portales__win{
  position:relative; overflow:hidden;
  background:var(--bg-inverse); color:var(--ink-inverse);
  border:1px solid var(--ink); border-radius:var(--card-radius);
  box-shadow:3px 3px 0 var(--ink);
  transition:box-shadow var(--t-base) var(--ease-out), border-color var(--t-base) var(--ease-out), transform var(--t-base) var(--ease-out);
}
.lp-portales__win:hover{
  box-shadow:6px 6px 0 var(--accent); border-color:var(--accent);
  transform:translateY(-3px);
}
/* --- chrome del navegador --- */
.lp-portales__chrome{
  display:flex; align-items:center; gap:var(--s-3);
  padding:9px var(--s-4);
  background:var(--c-navy-2); border-bottom:1px solid var(--ink);
}
.lp-portales__dots{ display:flex; gap:5px; flex:none; }
.lp-portales__dots i{ width:8px; height:8px; border-radius:50%; background:var(--c-navy); display:block; }
.lp-portales__addr{
  flex:1 1 auto; min-width:0;
  display:flex; align-items:center; gap:6px;
  background:var(--c-navy); border-radius:999px;
  padding:4px 10px;
}
.lp-portales__lock{ width:11px; height:11px; flex:none; color:var(--ok); }
.lp-portales__url{
  min-width:0; font-family:var(--font-mono); font-size:.6875rem; color:var(--ink-inverse-2);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* --- cuerpo = página del portal --- */
.lp-portales__page{ padding:var(--s-4) var(--s-4) var(--s-5); }
.lp-portales__site{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-3);
  padding-bottom:var(--s-3); margin-bottom:var(--s-3);
  border-bottom:1px solid var(--c-navy-2);
}
.lp-portales__logo{ height:24px; width:auto; max-width:62%; display:block; }
.lp-portales__host{
  display:inline-flex; align-items:center; gap:6px; min-width:0;
  font-family:var(--font-mono); font-size:.6875rem; color:var(--ink-inverse-2);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.lp-portales__favicon{
  width:16px; height:16px; flex:none; border-radius:4px;
  background:var(--accent); color:var(--ink-inverse);
  font-family:var(--font-disp); font-style:normal; font-weight:700; font-size:.625rem;
  display:inline-flex; align-items:center; justify-content:center;
}
.lp-portales__num{
  flex:none; font-family:var(--font-mono); font-size:.6875rem; font-weight:700; color:var(--c-lila);
}
.lp-portales__task{
  font-family:var(--font-disp); font-weight:700; font-size:.9688rem;
  color:var(--ink-inverse); line-height:1.25; margin:0 0 5px;
}
.lp-portales__desc{ font-size:var(--fs-sm); color:var(--ink-inverse-2); margin:0 0 var(--s-4); }
/* --- ejecución: barra que se llena + estado --- */
.lp-portales__track{
  height:6px; border-radius:3px; overflow:hidden;
  background:var(--c-navy-2);
}
.lp-portales__fill{
  display:block; height:100%; width:100%;
  background:var(--ok); border-radius:3px;
  transform-origin:left center; transform:scaleX(1);
}
/* estados iniciales SOLO con JS (sin JS se ve la barra llena y el estado
   completo, no un progreso a medias congelado). initLpPortales pone
   .is-done a cada ventana con stagger al entrar en viewport. */
.js .lp-portales__fill{ transform:scaleX(0); transition:transform 700ms var(--ease-out); }
.js .is-done .lp-portales__fill{ transform:scaleX(1); }
.lp-portales__foot{
  display:flex; align-items:baseline; justify-content:space-between; gap:var(--s-3);
  margin-top:var(--s-3);
}
.lp-portales__done{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700; color:var(--ok);
}
.js .lp-portales__done{ opacity:0; transition:opacity var(--t-base) var(--ease-out); }
.js .is-done .lp-portales__done{ opacity:1; }
.lp-portales__count{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); color:var(--ink-inverse-2);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; text-align:right; min-width:0;
}
@media (max-width:880px){
  .lp-portales__grid{ grid-template-columns:1fr; }
}
@media (min-width:881px) and (max-width:1100px){
  .lp-portales__grid{ grid-template-columns:repeat(2, minmax(0,1fr)); }
}
@media (prefers-reduced-motion: reduce){
  .js .lp-portales__fill, .js .lp-portales__done{ transition:none; }
}

/* R2 (ROADMAP p.2): movil estrecho (320-400px). Piezas de landing cuyo
   min-content (~278-331px) supera el ancho util a 320 y asoman unos px. Ya
   estan a 1 columna por el colapso de <=880; esto solo evita el ultimo
   desborde dejandolas encoger (min-width:0). Sin tocar copy. */
@media (max-width: 400px) {
  /* R7: las tres piezas de `.lp-versus` que se listaban aquí se fueron con el
     componente. El `.lp-chat` que lo sustituye no las necesita: sus columnas
     ya son `1fr` a partir de 880 y sus burbujas no tienen min-content ancho. */
  .kpi { min-width: 0; }
  .kpi b, .kpi small { overflow-wrap: anywhere; }
  .lp-tramites__ctrl, .lp-tramites__res, .lp-tramites__rightcol { min-width: 0; max-width: 100%; }
}

/* ============================================================
   .sp-lightbox — capa de imagen o vídeo ampliados (R1/ROADMAP p.1)
   Genérica: la usan las 9 fotos de "qué incluye" de SmartAudit (modo
   imagen) y el botón de ampliar del vídeo del hero (modo vídeo, que abre
   el tour ENTERO y no un fotograma quieto). z-index por encima del topbar
   sticky, igual que .lp-ovl.
   ============================================================ */
.sp-lightbox {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  display: grid; place-items: center;
  padding: var(--s-6);
  background: color-mix(in srgb, var(--c-navy-deep) 92%, transparent);
}
.sp-lightbox[hidden] { display: none; }
.sp-lightbox__fig { margin: 0; max-width: min(100%, 1100px); }
/* `width:auto` (NO `width:100%`) a propósito: las capturas de "qué incluye"
   miden 1000px y el poster del tour 960px — con `width:100%` el navegador
   las ESTIRARÍA hasta los 1100px del marco y volverían a verse blandas,
   justo lo que este lightbox viene a arreglar ("dale una buena calidad a
   las fotos, no se ven nada bien"). Así cada imagen se pinta a su tamaño
   nativo y solo los dos `max-*` la reducen si no cabe (el algoritmo de
   sizing de reemplazados conserva el ratio, no hace falta tocar height). */
.sp-lightbox__fig img {
  display: block; width: auto; max-width: 100%; height: auto;
  max-height: calc(100dvh - var(--s-8) * 2);
  object-fit: contain;
  border: 2px solid var(--ink-inverse); border-radius: var(--r-sm);
  background: var(--bg-surface);
}
/* OBLIGATORIO, no defensivo: `initLightbox()` alterna los dos medios con
   la propiedad `hidden`, y la regla de arriba (`display:block`, 0,2,0)
   GANA a la del navegador para `[hidden]` (0,1,0), así que sin esto el
   medio "oculto" se seguiría pintando y los dos saldrían a la vez. */
.sp-lightbox__fig img[hidden],
.sp-lightbox__fig video[hidden] { display: none; }
/* En modo vídeo la figura fija su ancho en vez de encogerse al contenido:
   un `<video>` no tiene tamaño intrínseco hasta que llegan los metadatos
   (arranca en los 300x150 de serie), así que dejarla ajustarse al
   contenido daría un salto de 300px al ancho real en cuanto cargara el
   primer fotograma. Aquí SÍ se permite escalar hasta los 1100px del marco
   (el tour es de 960px): en vídeo ese estirón es imperceptible y "más
   grande" era justo lo que pedía Fabio, al revés que en las capturas PNG
   de "qué incluye", donde interpolar se nota y por eso van a tamaño
   nativo. Fondo navy y no `--bg-surface`: mientras carga el póster, un
   rectángulo blanco a pantalla completa deslumbra sobre la capa oscura. */
.sp-lightbox__fig--video { width: min(100%, 1100px); }
.sp-lightbox__fig video {
  display: block; width: 100%; height: auto;
  max-height: calc(100dvh - var(--s-8) * 2);
  object-fit: contain;
  border: 2px solid var(--ink-inverse); border-radius: var(--r-sm);
  background: var(--c-navy-deep);
}
.sp-lightbox__fig figcaption {
  margin-top: var(--s-3);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-inverse-2); text-align: center;
}
.sp-lightbox__close {
  position: absolute; top: var(--s-5); right: var(--s-5);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-inverse); background: transparent;
  border: 1px solid var(--ink-inverse-2); border-radius: var(--r-xs);
  padding: 8px 14px; cursor: pointer;
}
.sp-lightbox__close:hover { border-color: var(--ink-inverse); }

/* =====================================================================
   TOPBAR FLOTANTE — header PRINCIPAL (estilo PTA, identidad SmartProcess)
   ---------------------------------------------------------------------
   Solo el header principal (header.php rama else) lleva `.topbar--float`.
   Las variantes `.topbar--minimal` (/diagnostico) y `.topbar--lp` (ads) NO
   la llevan → conservan el topbar sólido de pages.css intacto. Todo va
   scopado a `.topbar--float` o a clases propias `.sp-*` que pages.css no
   estiliza; los overrides del shell usan `.topbar.topbar--float` (0,2,0)
   para ganar a `.topbar` de pages.css, que se carga DESPUÉS de este archivo.
   ===================================================================== */

/* Reserva de hueco: el header pasa a position:fixed, así que el <main>
   contiguo necesita margen o su primera sección quedaría debajo. El
   selector solo matchea en páginas con el header flotante — nunca en
   /diagnostico ni en las landings de ads (no tienen `.topbar--float`).
   El valor sustituye a la altura que ocupaba el topbar sticky anterior. */
.topbar--float ~ main { padding-top: 84px; }
.topbar--float ~ main :target { scroll-margin-top: 100px; }

.topbar.topbar--float {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  z-index: 60;
  width: min(var(--wrap), calc(100% - var(--s-5)));
  display: flex; align-items: center; gap: var(--s-4);
  padding: 9px 10px 9px 18px;
  background: color-mix(in srgb, var(--c-white) 80%, transparent);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  backdrop-filter: saturate(140%) blur(14px);
  border: 1px solid var(--c-hairline);
  border-radius: var(--r-lg);
  /* Sombra suave permanente + entrada/salida amable (feedback Fabio: "como
     en PTA"): curva de salida larga en el transform y fundido de opacidad,
     para que el header no dé un tirón al ocultarse ni al reaparecer. */
  box-shadow: 0 6px 22px rgba(10, 31, 68, .08);
  /* Ocultarse/reaparecer aún más suave (feedback Fabio: "no tan rápida"):
     desplazamiento largo y ease gentil, opacidad acompañando. */
  transition: transform .68s cubic-bezier(.33, 0, .2, 1),
              opacity .5s var(--ease-out),
              box-shadow .35s var(--ease-out),
              background .35s var(--ease-out);
}
.topbar.topbar--float.is-compact {
  background: color-mix(in srgb, var(--c-white) 92%, transparent);
  box-shadow: 0 14px 40px rgba(10, 31, 68, .16);
}
.topbar.topbar--float.is-hidden {
  transform: translateX(-50%) translateY(-140%);
  opacity: 0;
}
.topbar--float .logo { flex: 0 0 auto; }
/* `.topbar.topbar--float` (no solo `.topbar--float`) para ganar en
   especificidad a `.topbar .logo img{height:26px}` de la media query
   ≤880px de pages.css (que carga después) y mantener el logo estable. */
.topbar.topbar--float .logo img { height: 30px; width: auto; display: block; }

/* --- Nav desktop --- */
.sp-nav { display: flex; align-items: center; gap: 2px; margin-inline: auto; }
.sp-nav__item { position: relative; display: flex; }
.sp-nav__link {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-disp); font-weight: 600; font-size: .9375rem;
  color: var(--ink); text-decoration: none; white-space: nowrap;
  padding: 9px 12px; border: 0; background: none; cursor: pointer;
  border-radius: var(--r-sm); line-height: 1;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.sp-nav__link.is-active { color: var(--accent-2); background: var(--fam-a-soft); }
.sp-nav__link:hover,
.sp-nav__item--has-menu:hover > .sp-nav__link,
.sp-nav__trigger[aria-expanded="true"] {
  background: color-mix(in srgb, var(--c-navy) 6%, transparent);
  color: var(--accent-2);
}
.sp-nav__caret { flex: none; opacity: .6; transition: transform var(--t-fast) var(--ease-out); }
.sp-nav__item--has-menu:hover .sp-nav__caret,
.sp-nav__trigger[aria-expanded="true"] .sp-nav__caret { transform: rotate(180deg); }

/* --- Mega-menú (Servicios / Proyectos) --- */
.sp-menu {
  position: absolute; top: calc(100% + 12px); left: 0;
  min-width: 300px; padding: var(--s-2); display: grid; gap: 2px;
  background: color-mix(in srgb, var(--c-white) 97%, transparent);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  backdrop-filter: saturate(140%) blur(14px);
  border: 1px solid var(--c-hairline); border-radius: var(--r-md);
  box-shadow: var(--shadow-soft-lg);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(8px); transform-origin: top left;
  transition: opacity var(--t-fast) var(--ease-out),
              transform var(--t-base) var(--ease-out),
              visibility var(--t-base);
  z-index: 5;
}
.sp-menu::before { content: ""; position: absolute; left: 0; right: 0; top: -12px; height: 12px; } /* puente de hover */
.sp-nav__item--has-menu:last-child .sp-menu { left: auto; right: 0; transform-origin: top right; }
.sp-menu.is-open { opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0); }
.sp-menu > a, .sp-menu__soon {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 9px 10px; border-radius: var(--r-sm);
  text-decoration: none; color: var(--ink);
  transition: background var(--t-fast) var(--ease-out);
}
.sp-menu > a:hover, .sp-menu > a:focus-visible { background: var(--bg-calm); }
.sp-menu > a.is-active { background: var(--bg-calm); }
.sp-menu > a.is-active .sp-menu__tx b { color: var(--accent-2); }
.sp-menu__ic {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: var(--r-sm);
  display: grid; place-items: center;
  background: var(--fam-a-soft); color: var(--accent-2);
}
.sp-menu__ic svg { width: 20px; height: 20px; }
.sp-menu__ic--b { background: var(--fam-b-soft); color: var(--accent); }
/* Tinte del desplegable "Sobre nosotros" (feedback Fabio: también con iconos) */
.sp-menu__ic--c { background: var(--bg-calm); color: var(--accent-2); }
.sp-menu__ic--muted { background: var(--bg-calm); color: var(--ink-soft); }
.sp-menu__tx b { display: block; font-family: var(--font-disp); font-weight: 700; font-size: .9375rem; line-height: 1.2; color: var(--ink); }
.sp-menu__tx small { display: block; font-size: .78rem; color: var(--ink-soft); margin-top: 2px; line-height: 1.3; }
.sp-menu__soon { cursor: default; }
.sp-menu__soon .sp-menu__tx b { display: flex; align-items: center; gap: var(--s-2); color: var(--ink-soft); font-weight: 600; }

/* "Sobre nosotros" usa el mismo patrón con icono que los otros desplegables
   (feedback Fabio), solo que un pelín más estrecho. */
.sp-menu--simple { min-width: 260px; }

/* --- CTA + burger --- */
.sp-nav__cta { margin-left: auto; display: flex; align-items: center; gap: var(--s-3); flex: 0 0 auto; }
.sp-burger {
  display: none; flex-direction: column; justify-content: center; gap: 5px;
  width: 42px; height: 42px; padding: 0;
  background: none; border: 1px solid var(--c-hairline); border-radius: var(--r-sm);
  cursor: pointer;
}
.sp-burger span { display: block; width: 20px; height: 2px; background: var(--ink); margin: 0 auto; transition: transform var(--t-base) var(--ease-pop), opacity var(--t-fast); }
.sp-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.sp-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.sp-burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* --- Menú móvil full-screen (navy, identidad SP) --- */
/* Entrada/salida animada (feedback Fabio: "que no aparezca de la nada").
   No se usa display:none —no se puede transicionar—: la capa vive siempre en
   el DOM oculta por visibility+opacity (que además la saca del orden de
   tabulación) y entra con un fundido + deslizamiento corto. El retardo de
   `visibility` al cerrar deja que se vea el fundido antes de ocultarla. */
.sp-mnav {
  position: fixed; inset: 0; z-index: 55; display: block;
  background: var(--bg-inverse); color: var(--ink-inverse);
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-12px);
  transition: opacity .26s var(--ease-out),
              transform .34s cubic-bezier(.22, .61, .36, 1),
              visibility 0s linear .34s;
}
.sp-mnav.is-open {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: none;
  transition: opacity .26s var(--ease-out),
              transform .34s cubic-bezier(.22, .61, .36, 1),
              visibility 0s;
}
/* Los bloques entran escalonados detrás de la capa */
.sp-mnav__inner > * {
  opacity: 0; transform: translateY(10px);
  transition: opacity .3s var(--ease-out), transform .3s var(--ease-out);
}
.sp-mnav.is-open .sp-mnav__inner > * { opacity: 1; transform: none; }
.sp-mnav.is-open .sp-mnav__inner > *:nth-child(1) { transition-delay: .07s; }
.sp-mnav.is-open .sp-mnav__inner > *:nth-child(2) { transition-delay: .12s; }
.sp-mnav.is-open .sp-mnav__inner > *:nth-child(3) { transition-delay: .17s; }
.sp-mnav.is-open .sp-mnav__inner > *:nth-child(4) { transition-delay: .22s; }
.sp-mnav.is-open .sp-mnav__inner > *:nth-child(5) { transition-delay: .27s; }
.sp-mnav__inner {
  min-height: 100%; display: flex; flex-direction: column; gap: var(--s-5);
  padding: 86px var(--gutter) var(--s-7);
  max-width: var(--wrap); margin: 0 auto;
}
.sp-mnav__solos { display: grid; gap: 0; }
.sp-mnav__solo {
  font-family: var(--font-disp); font-weight: 700; font-size: 1.4rem;
  color: var(--ink-inverse); text-decoration: none;
  padding: var(--s-3) 0;
  border-bottom: 1px solid color-mix(in srgb, var(--c-white) 14%, transparent);
}
.sp-mnav__solo:first-child { border-top: 1px solid color-mix(in srgb, var(--c-white) 14%, transparent); }
.sp-mnav__solo.is-active, .sp-mnav__group a.is-active { color: var(--c-lila); }
.sp-mnav__group { display: grid; gap: 2px; }
.sp-mnav__title {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--c-lila); margin-bottom: var(--s-2);
}
.sp-mnav__group a {
  font-family: var(--font-disp); font-weight: 600; font-size: 1.15rem;
  color: var(--ink-inverse-2); text-decoration: none; padding: 7px 0;
}
.sp-mnav__group a:hover, .sp-mnav__group a:focus-visible { color: var(--ink-inverse); }
.sp-mnav__soon {
  display: flex; align-items: center; gap: var(--s-2); padding: 7px 0;
  font-family: var(--font-disp); font-weight: 600; font-size: 1.15rem;
  color: var(--c-mute-inverse);
}
.sp-mnav__cta { margin-top: auto; padding-top: var(--s-4); }
.sp-mnav__cta .btn { display: block; width: 100%; text-align: center; }

body.sp-menu-open { overflow: hidden; }

/* --- Breakpoint: colapsar a móvil (logo + CTA + burger) --- */
@media (max-width: 980px) {
  .topbar--float ~ main { padding-top: 76px; }
  .topbar.topbar--float {
    top: 10px; width: calc(100% - var(--s-4));
    padding: 8px 8px 8px 14px; gap: var(--s-3);
  }
  .sp-nav { display: none; }
  .sp-burger { display: inline-flex; }
}
/* Muy estrecho: solo logo + burger; el CTA vive dentro del menú móvil */
@media (max-width: 440px) {
  .sp-nav__cta .btn--sm { display: none; }
}

/* Menos movimiento: el header no se desliza fuera de pantalla al ocultarse
   (ni se desvanece), y el menú móvil aparece sin deslizamiento. */
@media (prefers-reduced-motion: reduce) {
  .topbar.topbar--float.is-hidden { transform: translateX(-50%); opacity: 1; }
  .sp-mnav, .sp-mnav__inner > * { transform: none; }
}

/* =====================================================================
   KPIs DE LANDING EN MÓVIL — marquee infinito de UNA línea
   R4 (2026-07-23, feedback móvil Fabio: "preferiría que los KPIs
   estuvieran más compactos, o en una sola línea si fuera posible, quizás
   un carrusel infinito como la banda del home"). El modelo es .ticker
   (arriba): pista duplicada + @keyframes a translateX(-50%) en bucle.
   AFECTA SOLO A LAS LANDINGS (.lp-kpis: blindar-excel, migrar-excel-a-web,
   onboarding-clientes, sistemas-de-gestion). La .kpi-strip de la home /
   casos / macroning / smartaudit NO se toca: con 3 KPIs la rejilla de 3
   columnas de ≤880px ya encaja; el problema era 4 KPIs en 3 columnas
   (3 + 1 huérfano).
   Bloque AL FINAL del archivo y con selectores COMPUESTOS (0,3,x)
   `.kpi-strip.lp-kpis.is-marquee …` a propósito: ganan a las reglas
   generales de `.kpi-strip …` (0,2,x) pase lo que pase con el orden del
   archivo. Ninguna regla existente de .kpi-strip se modifica.
   `.is-marquee` la pone landing.js (initLpKpisMarquee) SOLO cuando ha
   montado la pista: sin JS se queda la rejilla actual, que funciona.
   ===================================================================== */

/* Pista montada por JS. Fuera del marquee (escritorio, o un resize que
   suba de ≤880px con la pista ya montada) el track debe ser TRANSPARENTE
   para que la rejilla de 4 columnas de `.lp-kpis .wrap` siga viendo los
   .kpi como hijos directos: display:contents. El clon se oculta del todo
   (si no, duplicaría los 4 KPIs dentro de la rejilla). */
.lp-kpis__track { display: contents; }
.lp-kpis__track--clone { display: none; }

@media (max-width: 880px) {
  /* La strip es el raíl: recorta lo que se sale. */
  .kpi-strip.lp-kpis.is-marquee { overflow: hidden; }
  /* El .wrap pasa a ser la CINTA: fila de ancho natural con las dos pistas
     (original + clon) una detrás de otra. Al ser exactamente dos pistas
     idénticas, translateX(-50%) equivale a un ciclo justo → bucle continuo. */
  .kpi-strip.lp-kpis.is-marquee .wrap {
    display: flex; width: max-content; max-width: none;
    margin: 0; padding: 0;
    animation: lpKpisMarquee 30s linear infinite;
    will-change: transform;
  }
  .kpi-strip.lp-kpis.is-marquee .lp-kpis__track { display: flex; flex: 0 0 auto; }
  .kpi-strip.lp-kpis.is-marquee .lp-kpis__track--clone { display: flex; }
  /* Cada KPI en una sola línea: cifra + texto pequeño al lado, sin cortes,
     y un separador vertical entre ellos (también en el último: la cinta
     sigue, así que el patrón tiene que continuar sin salto). */
  .kpi-strip.lp-kpis.is-marquee .kpi {
    flex: 0 0 auto;
    flex-direction: row; align-items: baseline; text-align: left;
    gap: .5rem; padding: 11px 16px; white-space: nowrap;
    border-right: 1px solid var(--card-border); border-bottom: 0;
  }
  .kpi-strip.lp-kpis.is-marquee .kpi:last-child { border-right: 1px solid var(--card-border); }
  .kpi-strip.lp-kpis.is-marquee .kpi b { font-size: 1.05rem; }
  .kpi-strip.lp-kpis.is-marquee .kpi b .u { font-size: .55em; }
  .kpi-strip.lp-kpis.is-marquee .kpi small {
    font-size: .6875rem; line-height: 1.2; max-width: none; white-space: nowrap;
  }
  /* Pausa al interactuar, igual que `.ticker:hover .ticker__track` (el
     componente que esta cinta toma como modelo). Aquí vive solo en ≤880px,
     así que el hover casi nunca dispara en un táctil: el que hace el trabajo
     de verdad es `:focus-within` (navegación por teclado en móvil/tablet con
     teclado externo, y lectores de pantalla que mueven el foco). */
  .kpi-strip.lp-kpis.is-marquee:hover .wrap,
  .kpi-strip.lp-kpis.is-marquee:focus-within .wrap { animation-play-state: paused; }
}

@keyframes lpKpisMarquee { to { transform: translateX(-50%); } }

/* Menos movimiento: nada se mueve solo. En su lugar la cinta se puede
   recorrer A MANO (scroller horizontal con snap), así se ven igual los 4
   KPIs. Bajo `reduce` landing.js NO clona la pista, así que aquí no hay
   contenido duplicado que recorrer. */
@media (max-width: 880px) and (prefers-reduced-motion: reduce) {
  .kpi-strip.lp-kpis.is-marquee {
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
  }
  .kpi-strip.lp-kpis.is-marquee .wrap { animation: none; will-change: auto; }
  .kpi-strip.lp-kpis.is-marquee .kpi { scroll-snap-align: start; }
}

/* =====================================================================
   ASÍ SE EMPIEZA (.lp-arranque) — R5.2 (2026-07-23, petición Fabio)
   Franja de proceso PRE-venta de B2 (sistemas-de-gestion.php, sección
   #asi-se-empieza): 3 pasos sobre una guía discontinua (llamada gratis →
   propuesta con presupuesto cerrado → decides sin compromiso), dos
   garantías de entrega bajo hairline y la foto real del fundador como
   sello-polaroid en el paso 01 (mismo lenguaje de carta que
   .lp-versus__foto). No es el lp-mapa: aquel cuenta la construcción y el
   pago DESPUÉS de contratar; este quita el miedo a dar el primer paso.
   ===================================================================== */
.lp-arranque__pasos{
  list-style:none; position:relative;
  display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s-7);
  align-items:start;
}
/* Guía discontinua tras los chips de número: nace y muere en el centro de
   los chips (44px de alto → top 22px). Los chips llevan fondo opaco y
   z-index para "posarse" encima. */
.lp-arranque__pasos::before{
  content:""; position:absolute; top:22px; left:8px; right:8px;
  border-top:2px dashed color-mix(in srgb, var(--ink) 30%, transparent);
}
.lp-arranque__num{
  position:relative; z-index:1;
  display:inline-flex; align-items:center; justify-content:center;
  min-width:44px; height:44px; padding:0 10px;
  font-family:var(--font-mono); font-weight:700; font-size:var(--fs-mono-md);
  color:var(--accent); background:var(--bg-surface);
  border:2px solid var(--ink); border-radius:var(--r-xs);
  box-shadow:var(--shadow-b1);
  margin-bottom:var(--s-4);
}
.lp-arranque__paso b{
  display:block; font-family:var(--font-disp); font-size:1.15rem;
  line-height:1.3; margin-bottom:var(--s-3);
}
.lp-arranque__paso p{ font-size:.95rem; line-height:1.55; color:var(--ink-soft); max-width:34ch; }
/* R6 (corrección Fabio): la carta-polaroid con su foto (.lp-arranque__foto)
   se RETIRÓ del paso 01; el CSS se borra con ella. Trampa que enseñó
   mientras vivió: el atributo height="NNN" del <img> es hint presentacional
   y GANA a aspect-ratio (solo actúa con un eje en auto) → hace falta
   height:auto explícito. Queda apuntada por si vuelve una foto en flujo. */
.lp-arranque__garantias{
  list-style:none; margin-top:var(--s-7);
  border-top:1px solid var(--card-border); padding-top:var(--s-6);
  display:grid; grid-template-columns:1fr 1fr; gap:var(--s-5) var(--s-8);
}
.lp-arranque__garantias li{ display:grid; grid-template-columns:24px 1fr; gap:var(--s-3); }
.lp-arranque__garantias i{
  font-style:normal; font-family:var(--font-mono); font-weight:700;
  color:var(--ok-text); padding-top:2px;
}
.lp-arranque__garantias span{ font-size:1rem; line-height:1.55; }
@media (max-width:880px){
  .lp-arranque__pasos{ grid-template-columns:1fr; gap:var(--s-6); }
  .lp-arranque__pasos::before{ display:none; }
  .lp-arranque__garantias{ grid-template-columns:1fr; }
}

/* =====================================================================
   QUÉ CONSTRUIMOS (.lp-que / .lp-pet) — R5.3 (2026-07-23)
   Rediseño total de la sección #que-construimos de B2 (Fabio descartó las
   tarjetas .inc-card: "llamativo y original, no simples tarjetas").
   Concepto B elegido sobre wireframes: sección NAVY con la definición
   como núcleo (párrafos + 3 claims con check) y una ventana BLANCA
   "peticiones_reales.txt" (.media-frame reutilizado) con encargos de
   cliente en primera persona → qué construimos para cada uno; remata un
   cierre a todo el ancho con la frase estrella y CTA. SIN prompt "$"
   (petición expresa de Fabio: ids petición_01.. en su lugar).
   OJO especificidad: `.sec--inverse p` (0,1,1) aclara los párrafos a
   --ink-inverse-2 (la misma trampa documentada en .cta-funnel): todo
   selector de <p> de la ventana blanca y del cierre va prefijado con
   .lp-que (0,2,0) para ganarle. Los párrafos de la definición NO se
   retintan: el claro de .sec--inverse p es justo el que quieren.
   ===================================================================== */
.lp-que{ display:grid; grid-template-columns:1fr 1.08fr; gap:var(--s-8); align-items:start; }
/* R6 (corrección Fabio sobre captura): la cabecera vive DENTRO de la columna
   izquierda (nada de .sec-head a todo el ancho) para que la ventana de
   peticiones arranque a la altura del tag y no quede navy muerto arriba a
   la derecha. */
.lp-que .lp-que__h2{ margin:var(--s-4) 0 var(--s-6); }
.lp-que__def p{ font-size:1.0625rem; line-height:1.65; max-width:36rem; margin-bottom:var(--s-5); }
.lp-que__def p b{ color:var(--ink-inverse); }
.lp-que__claims{ list-style:none; margin-top:var(--s-6); border-top:1px solid color-mix(in srgb, var(--c-lila) 35%, transparent); }
.lp-que__claims li{
  display:grid; grid-template-columns:22px 1fr; gap:var(--s-3); align-items:start;
  padding:var(--s-4) 2px;
  border-bottom:1px solid color-mix(in srgb, var(--c-lila) 35%, transparent);
  font-family:var(--font-disp); font-weight:500; font-size:1.05rem; color:var(--ink-inverse);
}
/* Check en --ok (verde): 4.65:1 sobre navy, el MISMO criterio medido que
   .lp-ventajas__item i (--ok-text caería a 3:1 aquí). */
.lp-que__claims li i{ font-style:normal; font-family:var(--font-mono); font-weight:700; color:var(--ok); padding-top:2px; }
/* La ventana de peticiones: chrome de .media-frame, cuerpo blanco explícito
   (el body del frame no trae fondo propio). */
.lp-que__win .media-frame__body{ background:var(--bg-surface); }
.lp-pet{ padding:var(--s-5) var(--s-6); border-top:1px solid var(--card-border); }
.lp-pet:first-child{ border-top:0; }
.lp-pet__id{
  display:block; font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  font-weight:700; color:var(--accent); margin-bottom:var(--s-2);
}
/* R6 (corrección Fabio "el tamaño del texto y de las peticiones es muy
   diferente"): la cita sube a 1.15rem y la respuesta a 1rem, a juego con
   el 1.0625rem de la definición de la izquierda. La petición "La tuya"
   (--tuya) se RETIRÓ: el cierre de abajo ya lleva ese mensaje. */
.lp-pet q{
  quotes:'"' '"';
  font-family:var(--font-disp); font-weight:500; font-size:1.15rem;
  line-height:1.4; color:var(--ink);
}
.lp-que .lp-pet__res{ margin-top:var(--s-2); font-size:1rem; line-height:1.5; color:var(--ink-body); }
.lp-que .lp-pet__res i{ font-style:normal; font-family:var(--font-mono); font-weight:700; color:var(--accent); margin-right:8px; }
/* Cierre a todo el ancho: la frase estrella con brush amarillo + CTA. */
.lp-que__cierre{
  grid-column:1/-1; text-align:center;
  margin-top:var(--s-5); padding-top:var(--s-8);
  border-top:1px solid color-mix(in srgb, var(--c-lila) 35%, transparent);
}
.lp-que .lp-que__frase{
  font-family:var(--font-disp); font-weight:700; letter-spacing:var(--ls-tight);
  font-size:clamp(1.6rem, 1.15rem + 2vw, 2.5rem); line-height:1.22;
  color:var(--ink-inverse); max-width:24ch; margin:0 auto var(--s-7);
}
.lp-que__micro{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  color:var(--ink-inverse-2); margin-top:var(--s-4);
}
@media (max-width:880px){
  .lp-que{ grid-template-columns:1fr; gap:var(--s-7); }
  .lp-pet{ padding:var(--s-4) var(--s-5); }
}

/* --- Alternancia de lados en los casos (.case--flip) — R5.3 (2026-07-23,
   pedido Fabio: "en el primero texto a la izq y kpis a la derecha, y en el
   segundo al revés, y así"). Se aplica al segundo bloque (y sucesivos
   alternos) de las listas de casos de las landings. Solo ≥881px: en móvil
   la columna única mantiene SIEMPRE el texto primero. Además de invertir
   el orden se invierte la plantilla (2fr 1fr → 1fr 2fr) para que el texto
   conserve su columna ancha al cambiar de lado. Gana a `.case` (misma
   especificidad) por orden de archivo: este bloque vive al final. */
@media (min-width:881px){
  .case--flip{ grid-template-columns:1fr 2fr; }
  .case--flip > div:first-of-type{ order:2; }
}

/* =====================================================================
   TE SUENA v2 (.lp-chat / .lp-slider) — R6 (2026-07-23)
   Rediseño de la sección "// te suena, ¿verdad?" de las 6 landings
   (Fabio: "la veo muy básica… dale una vuelta de creatividad… tematizada
   con el servicio… lo más pequeña en altura posible"). Dos conceptos
   elegidos sobre wireframes en chat, repartidos por landing:
   - .lp-chat  (concepto A, "el chat del equipo"): A1, B1, B2 — dolores
     como mensajes internos del equipo del lead, ventana "hoy" vs ventana
     "con smartprocess".
   - .lp-slider (concepto B, "antes/después con asa"): A2, Macroning,
     SmartAudit — un artefacto temático partido en dos, capa navy "con
     [producto]" recortada por clip-path sobre la capa "hoy", asa
     arrastrable (input range real: teclado y táctil gratis).
   El versus anterior (.lp-versus + logo polaroid) queda SIN USO en las
   landings pero su CSS NO se borra todavía (Fabio: "luego podemos hacer
   cambios" — se conserva para poder revertir barato; borrar en limpieza).
   Los mensajes del chat se encienden con el scroll vía el helper genérico
   spScrubList (motion.js) — initLpChat en landing.js; sin GSAP o con
   reduce, spScrubList las enciende todas de golpe; sin JS, el estado
   apagado NO aplica (guard .js) y todo es legible.
   ===================================================================== */
.lp-chat{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s-6); align-items:start; }
.lp-chat__win{
  background:var(--bg-surface);
  border:2px solid var(--ink); border-radius:var(--r-md);
  box-shadow:var(--shadow-b2); overflow:hidden;
}
.lp-chat__bar{
  display:flex; align-items:center; gap:6px;
  background:var(--bg-inverse); padding:7px 12px;
}
.lp-chat__bar i{ width:8px; height:8px; border-radius:50%; background:var(--c-lila); opacity:.55; flex:none; }
.lp-chat__bar .title{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--ink-inverse); margin-left:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* El "· con smartprocess" del título en verde --ok (4.65:1 sobre navy, el
   criterio medido de .lp-ventajas__item i; --ok-text caería a 3:1). */
.lp-chat__bar .title b{ color:var(--ok); }
.lp-chat__body{ padding:var(--s-4) var(--s-5); display:flex; flex-direction:column; gap:var(--s-3); }
.lp-chat__msg{
  max-width:92%;
  background:var(--bg-warm); border:1px solid var(--c-cream-2);
  border-radius:10px 10px 10px 3px; padding:7px 12px;
}
/* Estado apagado SOLO bajo .js (sin JS todo legible: los dolores son copy
   principal, no decoración — a diferencia de .lp-vigia, que es aria-hidden).
   spScrubList los va encendiendo con el scroll (is-on); sin GSAP/reduce el
   helper los enciende todos de golpe. Solo opacity/transform: cero saltos
   de layout. */
.js .lp-chat__msg{
  opacity:.12; transform:translateY(10px);
  transition:opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.js .lp-chat__msg.is-on{ opacity:1; transform:translateY(0); }
.lp-chat__who{
  display:block; font-family:var(--font-mono); font-size:var(--fs-mono-sm);
  font-weight:700; color:var(--ink-soft); margin-bottom:2px;
}
.lp-chat .lp-chat__msg p{ font-size:.95rem; line-height:1.45; color:var(--ink); margin:0; }
/* Mensajes del sistema (ventana "con smartprocess"): verde suave. */
.lp-chat__msg--sys{ background:var(--ok-soft); border-color:color-mix(in srgb, var(--ok) 30%, transparent); }
.lp-chat__msg--sys .lp-chat__who{ color:var(--ok-text); }
/* La persona, ya en calma, contesta desde la derecha. */
.lp-chat__msg--right{
  align-self:flex-end;
  background:var(--bg-surface); border:1px solid var(--card-border);
  border-radius:10px 10px 3px 10px;
}
@media (max-width:880px){
  .lp-chat{ grid-template-columns:1fr; gap:var(--s-5); }
  .lp-chat__body{ padding:var(--s-3) var(--s-4); }
}
@media (prefers-reduced-motion: reduce){
  .js .lp-chat__msg{ transition:none; }
}


/* --- Te suena v2 · sellos ANTES/AHORA + flecha central — R7 (2026-07-24)
   Fabio: "añade entre o en ambas pantallas una flecha, etiqueta o algo por
   el estilo, que permita destacar más como era antes y cómo es ahora con
   smartprocess". La rejilla pasa a 3 columnas (1fr auto 1fr) en escritorio
   con la flecha viviendo en la calle central; en movil vuelve a 1 columna y
   la flecha se tumba entre las dos ventanas apiladas.
   Los sellos van SOBRE cada ventana (no dentro): asoman por la esquina
   superior izquierda como una pegatina, mismo lenguaje que .sticker. */
.lp-chat{ grid-template-columns:1fr auto 1fr; column-gap:var(--s-5); }
.lp-chat__col{ position:relative; }
.lp-chat__sello{
  display:inline-block; margin-bottom:var(--s-3);
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  padding:4px 10px; border:2px solid var(--ink); border-radius:var(--r-xs);
  background:var(--bg-surface); box-shadow:var(--shadow-b1);
  transform:rotate(-2deg);
}
.lp-chat__sello--antes{ color:var(--danger); }
.lp-chat__sello--ahora{ color:var(--ok-text); transform:rotate(2deg); }
/* Flecha de la calle central: circulo morado con la punta hacia la derecha.
   aria-hidden en el markup (es refuerzo visual de los sellos, que ya lo
   dicen en texto). En movil rota 90deg para apuntar hacia abajo. */
.lp-chat__vs{
  align-self:center; justify-self:center;
  display:flex; flex-direction:column; align-items:center; gap:var(--s-2);
}
.lp-chat__vs i{
  font-style:normal; font-family:var(--font-mono); font-weight:700;
  width:44px; height:44px; display:flex; align-items:center; justify-content:center;
  color:var(--ink-inverse); background:var(--accent);
  border:2px solid var(--ink); border-radius:50%; box-shadow:var(--shadow-b1);
}
.lp-chat__vs b{
  font-family:var(--font-mono); font-size:var(--fs-mono-sm); font-weight:700;
  color:var(--ink-soft); writing-mode:vertical-rl; text-orientation:mixed;
  letter-spacing:.04em;
}
@media (max-width:880px){
  .lp-chat{ grid-template-columns:1fr; }
  .lp-chat__vs{ flex-direction:row; }
  .lp-chat__vs i{ transform:rotate(90deg); width:38px; height:38px; }
  .lp-chat__vs b{ writing-mode:horizontal-tb; }
}

/* --- Cabecera del funnel en movil: el servicio, en su propia fila -------
   R7 (2026-07-24, correccion Fabio con captura): a 375 el titulo del
   servicio se truncaba a "Blind..." porque compartia fila con el sticker
   [diagnostico], el contador "pregunta N de 5" y la X de cerrar. Debajo de
   700px la cabecera pasa a DOS filas: arriba sticker + contador + cerrar
   (la fila de control), abajo el servicio ENTERO a todo el ancho. Se lee
   sin truncar y sin movimiento (un carrusel de texto en una cabecera
   obliga a esperar para leer). Solo layout: cero cambios de markup. */
@media (max-width:700px){
  /* Rejilla de 2 filas. El título NO es hijo directo de `.fs__head` (vive
     dentro de `.fs__head-id`, junto al sticker), así que `display:contents`
     promueve a los dos a hijos del grid y permite colocarlos por separado
     sin tocar el markup — que es compartido por los tres modos del funnel
     (página, overlay e inline) y no se puede reestructurar solo para móvil. */
  .fs__head{
    display:grid; grid-template-columns:auto 1fr auto;
    align-items:center; column-gap:var(--s-3); row-gap:var(--s-2);
  }
  .fs__head-id{ display:contents; }
  .fs__head .sticker{ grid-column:1; grid-row:1; }
  .fs__head-progress{ grid-column:2; grid-row:1; justify-self:end; }
  .fs__close{ grid-column:3; grid-row:1; }
  .fs__head-title{
    grid-column:1 / -1; grid-row:2;
    font-size:1.05rem; white-space:normal; overflow:visible; text-overflow:clip;
  }
}
