/* =====================================================================
   SmartProcess · pages.css — CSS de shell y páginas concretas
   ---------------------------------------------------------------------
   Requiere tokens.css + components.css cargados antes (ver header.php).
   Solo consume var() de tokens.css — cero HEX/px de escala a pelo.
   ===================================================================== */

/* =====================================================================
   SHELL — topbar, footer, banner de cookies, página 404
   ===================================================================== */

/* --- Topbar ---------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: var(--z-nav);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--card-border);
}
.topbar__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-5);
  padding-top: var(--s-4); padding-bottom: var(--s-4);
}
.logo {
  font-family: var(--font-disp); font-weight: 700; font-size: 1.15rem;
  letter-spacing: var(--ls-head); color: var(--ink); text-decoration: none;
  display: inline-flex; align-items: center;
}
.logo span { color: var(--accent-2); }
.logo img { display: block; }

/* WhatsApp — enlace compartido (footer inverso + tarjeta clara en RRSS) */
.whatsapp-link {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  text-decoration: none;
}
.whatsapp-link svg { width: 18px; height: 18px; flex: none; }
.footer .whatsapp-link { color: var(--ink-inverse-2); margin-top: var(--s-4); }
.footer .whatsapp-link svg { color: var(--c-lila); }
.footer .whatsapp-link:hover { color: var(--ink-inverse); }

.topbar__nav {
  display: flex; align-items: center; gap: var(--s-6);
}
.topbar__nav a:not(.btn) {
  font-family: var(--font-disp); font-weight: 600; font-size: .9375rem;
  color: var(--ink); text-decoration: none;
  padding-bottom: 4px; border-bottom: 2px solid transparent;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.topbar__nav a:not(.btn):hover { color: var(--accent-2); }

/* Estado activo (deriva de $PAGE['slug'] en header.php) */
.topbar__nav > a.is-active,
.topbar__dd-trigger.is-active {
  color: var(--accent-2); border-bottom-color: var(--accent);
}

/* --- Desplegables de navegación (Servicios / Proyectos / Sobre nosotros)
   Patrón disclosure: <button aria-expanded> + panel <ul> posicionado bajo
   el trigger. Abre con clic, hover (desktop) o teclado; el JS de
   header.php solo alterna aria-expanded, toda la transición es CSS. --- */
/* R1 (ROADMAP p.1): display:flex además de relative. El <button> del
   trigger es inline-flex, y como caja inline dentro de un bloque se apoyaba
   en una línea base cuyo strut viene del line-height heredado: eso lo
   bajaba ~1px respecto de los <a> hermanos. Con el padre en flex el botón
   pasa a ser flex item (blockificado) y desaparece la línea base.
   La media query de móvil (L150) redeclara display:flex + column y gana
   por orden, así que el acordeón sigue igual. */
.topbar__dd { position: relative; display: flex; }
.topbar__dd-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-disp); font-weight: 600; font-size: .9375rem;
  /* R1: el shorthand `font` del UA stylesheet de Chrome resetea
     line-height a `normal` (~19px) en <button>, mientras que los <a>
     hermanos heredan --lh-body (24.75px). Esos 5.75px de diferencia de
     caja de línea eran los ~3px de desfase visible del texto. */
  line-height: var(--lh-body);
  color: var(--ink); background: none; border: 0; padding: 0;
  padding-bottom: 4px; border-bottom: 2px solid transparent;
  cursor: pointer;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.topbar__dd-trigger:hover { color: var(--accent-2); }
.topbar__dd-caret { flex: none; transition: transform var(--t-fast) var(--ease-out); }
.topbar__dd-trigger[aria-expanded="true"] .topbar__dd-caret { transform: rotate(180deg); }

.topbar__dd-panel {
  position: absolute; top: 100%; left: 0; margin-top: var(--s-3);
  min-width: 240px; list-style: none; z-index: var(--z-nav);
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--r-sm); box-shadow: var(--shadow-b2);
  padding: var(--s-2);
  opacity: 0; transform: translateY(-6px); pointer-events: none;
  transition: opacity var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-out);
}
.topbar__dd-trigger[aria-expanded="true"] + .topbar__dd-panel {
  opacity: 1; transform: translateY(0); pointer-events: auto;
}
.topbar__dd-panel li + li { margin-top: 2px; }
.topbar__nav .topbar__dd-panel a {
  display: block; padding: var(--s-2) var(--s-3); border-radius: var(--r-xs);
  font-family: var(--font-disp); font-weight: 600; font-size: var(--fs-sm);
  color: var(--ink); text-decoration: none; white-space: nowrap;
  border-bottom: 0;
}
.topbar__nav .topbar__dd-panel a:hover,
.topbar__nav .topbar__dd-panel a:focus-visible { background: var(--bg-calm); color: var(--accent-2); }
.topbar__nav .topbar__dd-panel a.is-active { background: var(--bg-calm); color: var(--accent-2); }
.topbar__dd-soon {
  display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
  padding: var(--s-2) var(--s-3);
  font-family: var(--font-disp); font-weight: 600; font-size: var(--fs-sm);
  color: var(--ink-soft);
}

.topbar__burger {
  display: none;
  flex-direction: column; justify-content: center; gap: 5px;
  width: 40px; height: 40px; padding: 0;
  background: none; border: 0; cursor: pointer;
}
.topbar__burger span {
  display: block; width: 22px; height: 2px; background: var(--ink);
  transition: transform var(--t-base) var(--ease-pop), opacity var(--t-fast);
}
.topbar__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.topbar__burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.topbar__burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Topbar minimal (funnel /diagnostico): solo logo + salir */
.topbar--minimal .topbar__inner { justify-content: space-between; }
.topbar__exit {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  /* --ink-body y no --ink-soft: a 12px el mute roza el 4.5:1 y Lighthouse
     lo tumba (es un control, no un caption). */
  color: var(--ink-body); text-decoration: none;
}
.topbar__exit:hover { color: var(--ink); }

@media (max-width: 880px) {
  /* R2-R1 (Fabio smoke): header mas bajo en movil. El nav colapsado (flex-basis
     100%) envuelve a una 2a linea y el row-gap de 20px reservaba altura fantasma
     aunque el menu este cerrado (max-height:0). row-gap:0 lo elimina; el menu
     abierto trae su propio padding-top. Header ~56px en vez de 80. */
  .topbar__inner {
    flex-wrap: wrap; row-gap: 0;
    padding-top: var(--s-2); padding-bottom: var(--s-2);
  }
  .topbar .logo img { height: 26px; }
  .topbar__burger { display: flex; }
  .topbar__nav {
    flex-basis: 100%; flex-direction: column; align-items: stretch; gap: 0;
    max-height: 0; overflow: hidden;
    transition: max-height var(--t-base) var(--ease-out);
  }
  /* 640px (antes 480px): con los desplegables, el grupo más largo
     (Servicios, 4 hijos) abierto a la vez que el resto del menú móvil
     supera los 480px originales (pensados para la lista plana de antes)
     y recortaba el CTA final contra el overflow:hidden de abajo. */
  .topbar__nav.is-open { max-height: 640px; padding-top: var(--s-3); }
  .topbar__nav a:not(.btn) {
    padding: var(--s-3) 0; border-top: 1px solid var(--card-border);
  }
  .topbar__nav .btn { margin: var(--s-3) 0; text-align: center; }

  /* Desplegables → acordeón dentro del panel móvil. Mismo <button
     aria-expanded>, pero el panel pasa a flujo normal (sin absolute) y la
     transición es max-height, igual que .topbar__nav.is-open arriba. */
  .topbar__dd { display: flex; flex-direction: column; align-items: stretch; }
  .topbar__dd-trigger {
    justify-content: space-between;
    padding: var(--s-3) 0; border-top: 1px solid var(--card-border);
  }
  .topbar__dd-panel {
    position: static; margin-top: 0; z-index: auto;
    opacity: 1; transform: none; pointer-events: auto;
    max-height: 0; overflow: hidden;
    background: none; border: 0; box-shadow: none; padding: 0;
    transition: max-height var(--t-base) var(--ease-out);
  }
  .topbar__dd-trigger[aria-expanded="true"] + .topbar__dd-panel {
    max-height: 320px; padding-bottom: var(--s-2);
  }
  .topbar__nav .topbar__dd-panel a,
  .topbar__dd-soon { padding-left: var(--s-5); min-height: 44px; box-sizing: border-box; }
  .topbar__nav .topbar__dd-panel a { display: flex; align-items: center; }
}

/* --- Footer ------------------------------------------------------------ */
.footer {
  background: var(--c-navy-deep); color: var(--ink-inverse);
  padding: var(--s-8) 0 var(--s-6);
}
.footer .logo { color: var(--ink-inverse); }
.footer .logo span { color: var(--c-lila); }
.footer__brand p {
  color: var(--ink-inverse-2); font-size: var(--fs-sm);
  margin-top: var(--s-3); max-width: 26rem;
}
/* R1 (ROADMAP p.1): 4 columnas. Antes eran 3 con los 11 enlaces apilados en
   una sola, lo que estiraba el footer casi al doble de alto. Ahora los
   enlaces van en 2 columnas temáticas (4 + 6) y "Diagnóstico" sale de la
   lista porque pasa a ser el CTA de la columna de marca. */
.footer__grid {
  display: grid; grid-template-columns: 1.5fr 1fr 1fr 1.4fr; gap: var(--s-7);
  padding-bottom: var(--s-7); border-bottom: 1px solid var(--c-navy-2);
}
.footer__cta { margin-top: var(--s-5); }
.footer__col h3 {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--c-lila); margin-bottom: var(--s-4);
}
.footer__col ul { list-style: none; display: grid; gap: var(--s-2); }
.footer__col a {
  color: var(--ink-inverse-2); text-decoration: none; font-size: var(--fs-sm);
  transition: color var(--t-fast);
}
.footer__col a:hover { color: var(--ink-inverse); }
.footer__legal p { color: var(--ink-inverse-2); font-size: var(--fs-sm); line-height: var(--lh-body); }
.footer__legal a { color: var(--ink-inverse-2); }
.footer__legal a:hover { color: var(--ink-inverse); }

.footer__bottom {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-4); padding-top: var(--s-6);
}
.footer__social { display: flex; gap: var(--s-4); }
.footer__social a { color: var(--ink-inverse-2); transition: color var(--t-fast); }
.footer__social a:hover { color: var(--ink-inverse); }
.footer__social svg { width: 20px; height: 20px; display: block; }

.footer__legal-links { display: flex; gap: var(--s-5); flex-wrap: wrap; align-items: center; }
.footer__legal-links a,
.footer__legal-links .link-btn {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-inverse-2); text-decoration: none;
}
.footer__legal-links a:hover,
.footer__legal-links .link-btn:hover { color: var(--ink-inverse); }
/* Botón con aspecto de enlace (reabrir configuración de cookies, RGPD) */
.link-btn { background: none; border: 0; padding: 0; cursor: pointer; }

@media (max-width: 880px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .footer__brand, .footer__legal { grid-column: 1 / -1; }
  .footer__bottom { flex-direction: column; align-items: flex-start; }
}

/* --- Banner de cookies -------------------------------------------------- */
.cookie-banner {
  position: fixed; left: var(--gutter); right: var(--gutter); bottom: var(--s-5);
  z-index: var(--z-overlay);
  max-width: 640px; margin: 0 auto;
}
.cookie-banner__box, .cookie-panel__box {
  background: var(--bg-surface); border: 2px solid var(--ink);
  border-radius: var(--r-md); box-shadow: var(--shadow-b3);
  padding: var(--s-5);
}
.cookie-banner p { font-size: var(--fs-sm); color: var(--ink-body); margin-bottom: var(--s-4); }
.cookie-banner p a { color: var(--accent-2); font-weight: 600; }
.cookie-banner__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); }
.cookie-banner__config {
  /* --ink-body por contraste AA (control interactivo, ver .topbar__exit) */
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-body);
  background: none; border: 0; text-decoration: underline; cursor: pointer; padding: var(--s-2) 0;
}
.cookie-banner__config:hover { color: var(--ink); }

.cookie-panel {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: rgba(10, 31, 68, .55);
  display: flex; align-items: center; justify-content: center;
  padding: var(--gutter);
}
/* [hidden] empata en especificidad con .cookie-panel (display:flex) y el
   navegador aplicaría el último del cascade (autor) — se fuerza aquí para
   que el atributo hidden (toggleado por cookies.php) siga funcionando. */
.cookie-banner[hidden], .cookie-panel[hidden] { display: none; }
.cookie-panel__box { max-width: 480px; width: 100%; }
.cookie-panel h3 { margin-bottom: var(--s-4); }
.cookie-toggle-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-3) 0; border-top: 1px solid var(--card-border);
}
.cookie-toggle-row:first-of-type { border-top: 0; }
.cookie-toggle-row strong { display: block; font-family: var(--font-disp); font-size: .9375rem; }
.cookie-toggle-row small {
  display: block; color: var(--ink-soft);
  font-family: var(--font-mono); font-size: .75rem; margin-top: 3px;
}
.cookie-toggle-row input[type="checkbox"] { width: 20px; height: 20px; accent-color: var(--accent-2); flex: none; }
.cookie-panel__actions { display: flex; justify-content: flex-end; gap: var(--s-3); margin-top: var(--s-5); }

/* --- Página 404 ---------------------------------------------------------- */
.error-404 { padding: var(--s-9) 0; text-align: center; }
.error-404 .sticker { font-size: 1.1rem; margin-bottom: var(--s-5); }
.error-404 h1 { margin-bottom: var(--s-4); }
.error-404 p { max-width: 34rem; margin: 0 auto var(--s-6); font-size: var(--fs-lead); color: var(--ink-body); }
.error-404__actions { display: flex; gap: var(--s-4); justify-content: center; flex-wrap: wrap; }

@media (max-width: 560px) {
  .cookie-banner { left: var(--s-3); right: var(--s-3); bottom: var(--s-3); }
  .error-404__actions { flex-direction: column; align-items: stretch; }
}

/* =====================================================================
   HOME — index.php
   ===================================================================== */

/* --- Hero -------------------------------------------------------------- */
.home-hero { padding: calc(var(--sec-y) - var(--s-3)) 0 var(--s-8); }
.home-hero__grid {
  display: grid; grid-template-columns: 7fr 5fr;
  gap: var(--s-8); align-items: center;
}
.home-hero__sticker { display: inline-block; margin-bottom: var(--s-5); }
.home-hero h1 { margin-bottom: var(--s-5); }
.home-hero .lead { font-size: var(--fs-lead); max-width: 32rem; margin-bottom: var(--s-6); }
.home-hero .bullets { margin-bottom: var(--s-6); }

/* Collage de 3 media-frame solapadas (R1: fuera el vídeo único del hero).
   data-cp (motion.js) les da parallax ligado a la posición global del
   cursor; profundidades variadas para que no se muevan en bloque. */
.home-hero__media { position: relative; min-height: 460px; }
.hero-collage__f1 {
  position: absolute; top: 0; left: 0; width: 72%; z-index: 1;
}
.hero-collage__f2 {
  position: absolute; top: 32%; right: -6%; width: 56%; z-index: 2;
  box-shadow: var(--shadow-soft-lg);
  /* R1: borde tintado de marca (es el mock más visible del collage); el
     resto del borde (width/style) lo sigue heredando de .media-frame,
     mismo patrón de override que .media-frame--dark en components.css. */
  border-color: var(--accent);
}
.hero-collage__f3 {
  position: absolute; left: 2%; bottom: -6%; width: 42%; z-index: 3;
}
.home-hero__media .sticker--float { z-index: 4; }
.home-hero__media .fico { z-index: 4; }

/* Mini-mock "excel_cliente.xlsx" — hecho en HTML/CSS, sin captura */
.mock-sheet { font-family: var(--font-mono); font-size: .6875rem; }
.mock-sheet__head, .mock-sheet__row {
  display: grid; grid-template-columns: 1.5fr 1fr .8fr .5fr;
  gap: 8px; padding: 7px 4px; align-items: center;
}
.mock-sheet__head {
  color: var(--ink-soft); font-weight: 700;
  border-bottom: 1px solid var(--card-border);
}
.mock-sheet__row { color: var(--ink-body); border-bottom: 1px solid var(--c-hairline); }
.mock-sheet__row .ok { color: var(--ok-text); font-weight: 700; text-align: center; }
.mock-sheet__bar {
  margin-top: 10px; height: 15px; border-radius: var(--r-xs);
  background: linear-gradient(90deg, var(--fam-a-soft) 0 64%, var(--c-hairline) 64% 100%);
}

/* Mini-mock "crm.tuempresa.com" */
.mock-crm { display: flex; gap: 10px; }
.mock-crm__side { width: 24px; border-radius: var(--r-xs); background: var(--accent); flex: none; }
.mock-crm__list { flex: 1; display: flex; flex-direction: column; gap: 7px; }
.mock-crm__row {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-size: .75rem; font-weight: 600; color: var(--ink-body);
  padding: 7px 8px; border: 1px solid var(--card-border); border-radius: var(--r-xs);
}
.mock-crm__row .chip { flex: none; }
.chip--blue { background: var(--fam-a-soft); color: color-mix(in srgb, var(--accent-2) 72%, var(--c-navy)); }
.chip--ok   { background: color-mix(in srgb, var(--ok) 16%, var(--c-white)); color: var(--ok-text); }

/* Mini-mock "smartaudit.app" — fiel a la app real: cabecera navy con el
   logo pequeño + cuerpo BLANCO con una mini-tabla de balance (la app es
   clara con header navy, no un panel navy entero). R2-B, sustituye a las
   barras grises de la v1. */
.mock-audit { display: flex; flex-direction: column; }
.mock-audit__topbar { background: var(--c-navy); padding: 9px 12px; display: flex; align-items: center; }
/* Especificidad ganadora a propósito (dos clases) frente a
   `.media-frame__body img { width:100% }` de components.css: ese ancho
   forzado + el height="…" que traía el <img> del hero era justo lo que
   dejaba el logo "achatado" — aquí se fija el ratio real (height, width:auto). */
.mock-audit .mock-audit__logo { width: auto; height: 15px; display: block; }
.mock-audit__table { font-family: var(--font-mono); font-size: .625rem; background: var(--bg-surface); }
.mock-audit__row {
  display: grid; grid-template-columns: 1fr 1.7fr .9fr .32fr;
  gap: 6px; padding: 7px 12px; align-items: center;
  border-bottom: 1px solid var(--c-hairline);
}
.mock-audit__row:last-child { border-bottom: 0; }
.mock-audit__row--head {
  background: var(--fam-a-soft); color: var(--ink-soft); font-weight: 700;
  border-bottom: 1px solid var(--card-border);
}
.mock-audit__row .num { text-align: right; color: var(--ink-body); }
.mock-audit__row .ok { color: var(--ok-text); font-weight: 700; text-align: center; }

/* R1: marca SmartProcess en el hero. El collage era 100% neutro salvo el
   logo de SmartAudit del mock de abajo, así que leía como cualquier hero
   de plantilla. Sello de logotipo + barra lateral del mock CRM tintada con
   el morado de marca. */
.hero-brand-sticker {
  position: absolute; top: -18px; left: -10px; z-index: 6;
  display: inline-flex; background: var(--bg-surface);
  border: 2px solid var(--ink); border-radius: var(--r-sm);
  box-shadow: var(--shadow-b1); padding: 10px 14px;
  transform: rotate(var(--rot, -4deg));
}
.hero-brand-sticker img { width: 104px; height: auto; display: block; }

/* B3: foto del fundador como avatar flotante del collage — cara real que
   aporta calidez sin ensuciar el montaje. Redondo, con un aro fino del
   morado de marca y una sombra suave (no la sólida brutalista: una cara pide
   un trato más amable). Anclado abajo-izquierda, asomando por el borde para
   no tapar el contenido de los mocks; z-index como el sello de marca y
   data-cp le da el mismo parallax por cursor que el resto de piezas. */
.hero-photo-chip {
  position: absolute; right: 1%; bottom: -7%; left: auto; z-index: 7;
  margin: 0; display: flex; flex-direction: column; align-items: center; gap: 7px;
}
.hero-photo-chip img {
  width: 150px; height: 150px; border-radius: 50%;
  object-fit: cover; object-position: center; display: block;
  padding: 3px; background: var(--bg-surface);
  box-shadow: 0 0 0 2px var(--accent), var(--shadow-soft);
}
/* Pie de foto: quién es (corrección Fabio). El rol es largo ("consultor de
   automatización") → deja envolver a 2 líneas con un ancho acotado. */
.hero-photo-chip figcaption {
  background: var(--bg-surface); border: 1px solid var(--c-hairline);
  border-radius: var(--r-sm); padding: 4px 11px; max-width: 150px;
  text-align: center; white-space: normal; box-shadow: var(--shadow-soft);
}
.hero-photo-chip figcaption b {
  display: block; font-family: var(--font-disp); font-weight: 700;
  font-size: .82rem; line-height: 1.15; color: var(--ink); white-space: nowrap;
}
.hero-photo-chip figcaption span {
  display: block; font-family: var(--font-mono); font-size: .66rem;
  color: var(--ink-soft); line-height: 1.3;
}

/* --- Franja de portales, justo debajo de la KPI strip ------------------- */
/* R1 (ROADMAP p.1): fondo NAVY. Los 4 badges de portal son PNG con el
   isotipo a color a la izquierda y la MARCA DENOMINATIVA EN BLANCO a la
   derecha (medido: entre 34% y 73% de sus píxeles opacos son casi-blancos,
   y el bbox del contenido llega a x~1490 de 1600). Sobre el blanco de
   --bg-surface el nombre del portal era invisible y solo se veía el
   isotipo: parecían logos recortados. Mismo bug y mismo fix que las
   tarjetas de .lp-portales en macroning.php. */
.portals-strip {
  background: var(--bg-inverse); color: var(--ink-inverse);
  border-bottom: 1px solid var(--c-navy-2);
  padding: var(--s-5) 0;
}
.portals-strip .badges-portal { gap: var(--s-7); }
.portals-strip .badges-portal .lbl {
  width: auto; margin-right: var(--s-3); color: var(--c-lila);
}

/* --- Familias: cabecera sin sticker, CTA de cierre, teaser C2 ---------- */
.fam__head h3 { margin: 0; }

/* --- Familia B destacada (S3, feedback Fabio "destacar un poco más la
   familia b") — banda enmarcada morada. La familia entera se envuelve en
   un marco --fam-b-soft con borde de acento: es el tramo protagonista
   entre las filas claras de A y las filas navy de C (crescendo A→B→C).
   Solo en la HOME: las páginas de /servicios usan `.svc-card` suelto. --- */
.fam--b {
  background: var(--fam-b-soft);
  border: 2px solid var(--accent); border-radius: var(--r-lg);
  padding: var(--s-6) var(--s-6) var(--s-7);
  box-shadow: var(--shadow-b1);
}
/* Las cards ganan borde navy sólido para separarse del morado de la banda
   (sobre --bg-page el borde hairline bastaba; sobre el soft, no) */
.fam--b .svc-card { border: 1.5px solid var(--ink); box-shadow: var(--shadow-b1); }
.fam--b .svc-card .bullets { margin-top: var(--s-3); margin-bottom: var(--s-4); }
.home-fam-cta { margin-top: var(--s-7); text-align: center; }
.home-fam-teaser {
  margin-top: var(--s-5); font-size: var(--fs-sm); color: var(--ink-soft);
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
}

/* Familia C: badge "own" (producto propio) arriba del panel. margin-top
   deliberado (R2-B): dado el hueco que deja el sticker superpuesto por
   encima (ver abajo), evita que su rótulo quede tapado — comprobado por
   bounding-rect en Chrome real, ver informe de la tarea. */
.product-panel__brand { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); margin-top: 30px; }
.product-panel__brand .own { margin-bottom: 0; }

/* R2-B: el logo real sale del panel y se monta como sticker superpuesto,
   asomando por la esquina superior (mismo lenguaje que [diag]/30min del
   hero). El wrapper da el `position:relative` que necesita el sticker para
   no quedar recortado por el `overflow:hidden` del panel (usado para su
   glow ::after) — al vivir FUERA del panel, el recorte no le afecta. */
.product-panel-wrap { position: relative; }
.product-panel-sticker {
  position: absolute; top: -22px; left: 22px; 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: 10px 14px;
  transform: rotate(var(--rot, -4deg));
}
.product-panel-sticker img { width: 120px; height: auto; display: block; }
/* El isotipo de SmartAudit es una marca cuadrada (no un logotipo apaisado
   como Macroning): a 120px de ancho salía un sticker demasiado alto y
   tapaba el badge "own" de debajo. Ancho menor, misma receta (proporción
   intacta vía height:auto). */
.product-panel-sticker .product-panel-sticker__icon { width: 72px; }

/* --- Familia C: filas de producto propio (S3) --------------------------
   Sustituyen EN LA HOME a los dos .product-panel lado a lado (feedback
   Fabio: "la c no como un cuadrado al lado del otro, sino más parecido a
   la familia a, uno debajo del otro ocupando el 100%, con un fondo oscuro
   algo más llamativo, con textura, y algo más de descripción o kpis").
   Cada producto ocupa el 100% en una fila navy (--fam-c) con la textura
   tex-code (la misma de la sección de tesis), badge `own` en amarillo
   (--fam-c-accent, el acento de familia C desde F1) y 3 KPIs. El sticker
   del logo se mantiene con el mecanismo de siempre: wrap relativo +
   sticker FUERA del overflow de la fila.
   .product-panel* NO se toca: sigue vivo en components.css para las
   páginas de /servicios (sesión 4). --- */
.product-rows { display: flex; flex-direction: column; gap: var(--s-7); }
.product-row-wrap { position: relative; }
.product-row {
  display: grid; grid-template-columns: 1.15fr .85fr; gap: var(--s-7);
  align-items: center;
  background: var(--fam-c); color: var(--ink-inverse);
  border: 2px solid var(--ink); border-radius: var(--r-lg);
  box-shadow: var(--shadow-b3);
  padding: var(--s-7);
  text-decoration: none; position: relative; overflow: hidden;
  transition: box-shadow var(--t-base) var(--ease-pop), border-color var(--t-base);
}
/* Hover SIN transform a propósito: data-scrub (GSAP) deja un transform
   inline en el elemento y un :hover de CSS no puede pisarlo. El hover vive
   en sombra y borde, que sí responden. */
.product-row:hover { box-shadow: var(--shadow-b4); border-color: var(--fam-c-accent); }
/* la textura tex-code::before queda debajo; el contenido, encima */
.product-row__text, .product-row__media { position: relative; z-index: 1; }
/* Hueco para el sticker del logo (S3-R1, feedback Fabio: "los textos de
   macroning y smartaudit en amarillo los tapa el sticker del logo").
   Los badges `.own` se retiraron: el sticker YA dice la marca. El
   padding-top deja que el h3 arranque por debajo del sticker más alto (el
   de Macroning: lockup casi cuadrado). --s-7 medido en Chrome: deja 6px de
   holgura en Macroning y 33px en SmartAudit; con --s-6 el sticker aún
   pisaba el título 10px. Ojo al medir: el sticker va ROTADO, así que su
   caja envolvente es más alta que su caja real (108px de sticker → 119 de
   envolvente a -6deg) y es la envolvente la que tapa. */
.product-row__text { padding-top: var(--s-7); }
.product-row h3 { color: var(--ink-inverse); margin: 0 0 var(--s-3); }
.product-row__text > p { color: var(--ink-inverse-2); max-width: 46ch; }
.product-row__kpis {
  list-style: none; margin: var(--s-5) 0 0; padding: 0;
  display: flex; gap: var(--s-7); flex-wrap: wrap;
}
.product-row__kpis li { display: flex; flex-direction: column; gap: 2px; }
.product-row__kpis b {
  font-family: var(--font-disp); font-weight: 700; font-size: 1.35rem;
  color: var(--fam-c-accent); font-feature-settings: "tnum" 1;
}
.product-row__kpis small { font-size: .7813rem; color: var(--ink-inverse-2); max-width: 18ch; }
.product-row__go {
  display: inline-block; margin-top: var(--s-6);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--fam-c-accent);
}
/* Sticker del logo sobre la fila (S3-R1). Los dos logos son marcas de
   proporción distinta (Macroning = lockup casi cuadrado con wordmark;
   SmartAudit = isotipo apaisado), así que se igualan por ALTURA de caja,
   no por ancho: así los dos stickers pesan lo mismo en la fila y el
   padding-top del texto vale para ambos. */
.product-row-sticker img { width: auto; height: 84px; }
.product-row-sticker .product-panel-sticker__icon { width: auto; height: 62px; }
.product-row:hover .product-row__go { text-decoration: underline; }

/* Vídeo/hueco de vídeo del mismo tamaño en ambos paneles C (y en las filas
   de la home: .product-row .mv hereda el mismo trato) */
.product-panel .mv, .product-row .mv {
  aspect-ratio: 16 / 9; border-radius: var(--r-sm);
}

/* --- Calculadora de tiempo (sec--calm, entre servicios y tesis) --------- */
.calc {
  position: relative;
  background: var(--bg-surface); border-radius: var(--r-lg);
  box-shadow: var(--shadow-b3); padding: var(--s-7);
  display: grid; grid-template-columns: 1.1fr .9fr; gap: var(--s-8); align-items: center;
}
.calc__controls { display: flex; flex-direction: column; gap: var(--s-7); }

/* Frase dinámica (R3, feedback Fabio) — una línea encima de los sliders,
   mono itálica sutil, que cambia según el tramo de horas/mes calculado
   (lógica en el <script> inline del home). Fade suave al cambiar de
   tramo: solo opacity (guardarraíl de motion). */
.calc__phrase {
  font-family: var(--font-mono); font-style: italic; font-size: .9375rem;
  color: var(--ink-soft); margin: 0;
  transition: opacity 180ms var(--ease-out);
}
.calc__phrase.is-fading { opacity: 0; }
.calc__field label {
  display: block; font-family: var(--font-disp); font-weight: 600; font-size: .9375rem;
  color: var(--ink); margin-bottom: var(--s-3);
}
.calc__row { display: flex; align-items: center; gap: var(--s-4); }
.calc__row input[type="range"] { flex: 1; }
.calc__value {
  font-family: var(--font-disp); font-weight: 700; font-size: 1.3rem; color: var(--accent);
  min-width: 3.4ch; text-align: right; font-feature-settings: "tnum" 1;
}

/* Slider — track hairline fino, thumb casi-cuadrado con sombra b1 morada */
.calc__slider {
  appearance: none; -webkit-appearance: none; width: 100%; height: 2px;
  background: var(--c-hairline); border-radius: 2px; cursor: pointer;
}
.calc__slider::-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);
}
.calc__slider::-webkit-slider-thumb:hover { transform: scale(1.1); }
.calc__slider::-moz-range-track { background: var(--c-hairline); height: 2px; border-radius: 2px; }
.calc__slider::-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;
}
.calc__slider:focus-visible { outline: none; }
.calc__slider:focus-visible::-webkit-slider-thumb { box-shadow: var(--shadow-b1), var(--ring); }
.calc__slider:focus-visible::-moz-range-thumb { box-shadow: var(--shadow-b1), var(--ring); }

.calc__result {
  position: relative; z-index: 1;
  background: var(--bg-warm); border-radius: var(--r-md); padding: var(--s-6) var(--s-5);
  text-align: center; display: flex; flex-direction: column; align-items: center;
}
.calc__result-label { font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft); }
.calc__result-value {
  font-family: var(--font-disp); font-weight: 700; font-size: var(--fs-display); color: var(--accent);
  line-height: 1; margin: var(--s-3) 0; font-feature-settings: "tnum" 1;
}
.calc__result-value small {
  font-size: .3em; font-family: var(--font-mono); color: var(--ink-soft); margin-left: 6px;
}
.calc__equiv { font-family: var(--font-disp); font-weight: 600; color: var(--ink); margin-bottom: var(--s-3); }
.calc .fico { z-index: 2; }

/* --- Tesis (sec--inverse + tex-code + data-dotfield) -------------------- */
.home-thesis .wrap { position: relative; z-index: 1; }
.home-thesis .tag { color: var(--c-lila); }
.home-thesis h2 { color: var(--ink-inverse); max-width: 44rem; margin: var(--s-4) 0 var(--s-7); }
.home-thesis h2 em { font-style: normal; color: var(--c-lila); }
.home-thesis .duel { margin: var(--s-7) 0 var(--s-5); }
.home-thesis .mono-note {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--c-lila);
  display: block;
}

/* --- Casos: enlace a /casos ---------------------------------------------- */
.home-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;
}
.home-case-link:hover { color: var(--accent-2); }

/* B4: "escenario" del carrusel de casos — fondo y borde tenues que enmarcan
   el carrusel como la pieza que ROTA y lo separan del resto de la sección
   (deja claro "esto es lo que va cambiando", sobre todo en móvil). El
   carrusel interno ya reserva el hueco de su sombra sólida en cada slide
   (components.css §CARRUSEL), así que el padding del escenario no la recorta;
   los controles y dots que inyecta motion.js §11 viven DENTRO de .carousel y
   quedan dentro del escenario, siempre accesibles. */
.casos-stage {
  margin-top: var(--s-6);
  background: var(--bg-calm);
  border: 1px solid var(--c-hairline);
  border-radius: var(--r-lg);
  padding: var(--s-6) var(--s-6) var(--s-5);
}

/* --- Quién hay detrás (founder) ------------------------------------------ */
.home-founder { display: grid; grid-template-columns: 280px 1fr; gap: var(--s-8); align-items: start; }
.home-founder__text h2 { margin-bottom: var(--s-4); }
.home-founder__text p { max-width: 34rem; margin-bottom: var(--s-4); }
.founder-legal {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin-bottom: var(--s-6) !important;
}
.founder-linkedin {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  color: var(--accent); text-decoration: none;
}
.founder-linkedin svg { width: 18px; height: 18px; }
.founder-linkedin:hover { color: var(--accent-2); }

/* --- Blog: tarjetas + empty-state ----------------------------------------- */
.home-blog { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5); }
.home-blog-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);
}
.home-blog-card:hover { transform: var(--card-hover-move); box-shadow: var(--card-hover-shadow); border-color: var(--ink); }

/* Tarjeta CON la portada generada arriba (2026-08-13, /blog).
   ─────────────────────────────────────────────────────────────────────────
   Es la misma imagen que ya se usaba al compartir el enlace: no hay pieza
   nueva que mantener, solo se enseña donde antes no se enseñaba.

   Va como MODIFICADOR y no cambiando `.home-blog-card` a secas porque esa
   clase la comparte la home, que sigue con sus tarjetas de solo texto. Y un
   artículo sin `cover` (los anteriores al pipeline) se pinta como toda la
   vida, sin `--foto`, sin hueco y sin nada roto.

   `aspect-ratio` con las medidas reales de la portada (1200x630) + los
   `width`/`height` del `<img>`: el navegador reserva el hueco exacto antes de
   descargarla, así que la rejilla no da el salto al cargar. */
.home-blog-card--foto { padding: 0; overflow: hidden; }
.home-blog-card__img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 1200 / 630; object-fit: cover;
  border-bottom: 1px solid var(--card-border);
  background: var(--c-navy);
}
.home-blog-card__body { padding: 26px; }

.home-blog-card h3 { font-size: 1.1rem; margin: var(--s-3) 0 var(--s-2); }
.home-blog-card p { font-size: var(--fs-sm); margin-bottom: var(--s-3); }
.home-blog-card time { font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft); }
.home-blog-empty {
  border: 1px dashed var(--card-border); border-radius: var(--card-radius);
  padding: var(--s-7); text-align: center;
}
.home-blog-empty .chip { margin-bottom: var(--s-4); }
.home-blog-empty p { max-width: 30rem; margin: 0 auto; }

/* --- RRSS: tarjetas + marcos verticales con loop autoplay ------------------ */
/* S3 (feedback Fabio: "los vídeos tienen que ser más anchos, no se ven bien;
   prefiero que se vean sin cortar, con su proporción real, pero más
   pequeños, quizás 2 arriba y 2 abajo, formando un cuadrado que cuadre con
   la altura del resto de sección").
   El fallo de la versión anterior (fila de 4 estirada al alto de la columna
   de tarjetas): cada clip acababa a ~96×381 px, ratio 0.25, mientras el
   archivo es 9:16 = 0.5625 — con `object-fit: cover` se veía solo una tira
   central recortada. Ahora la celda IMPONE el ratio real del vídeo
   (`aspect-ratio: 9/16`), así que cover ya no recorta nada perceptible, y
   los 4 van en rejilla 2×2. Las 3 tarjetas de redes se apilan a lo ancho
   (1 columna) para que la columna izquierda dé una altura parecida a la del
   bloque 2×2 y las dos columnas casen.
   El 1.7fr no es decorativo: es el reparto que iguala las dos alturas
   ("un cuadrado que cuadre con la altura del resto de sección"). Medido en
   Chrome a 1213px: 2fr→73px de desajuste, 1.85fr→40px, 1.7fr→3px. Si algún
   día cambia el contenido de la columna izquierda, se vuelve a medir. */
.home-rrss-grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: var(--s-8); align-items: start; }
.home-rrss-whatsapp {
  margin-top: var(--s-6);
  /* texto en --ok-text (AA); borde/icono/sombra siguen en el verde de marca */
  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);
}
.home-rrss-whatsapp svg { color: var(--ok); }
.home-rrss-whatsapp:hover { color: var(--ok-text); box-shadow: 4px 4px 0 var(--ok); transform: translate(-2px, -2px); }

/* Botón de pausa global de los 4 clips RRSS (WCAG 2.2.2) — mono discreto,
   en su propia línea bajo el enlace de WhatsApp. */
.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;
}
.rrss-pause:hover { border-color: var(--ink); color: var(--ink); }

.home-rrss-clips {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4);
  align-content: start;
}
.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);
}
.rrss-clip video { width: 100%; height: 100%; object-fit: cover; display: block; }
.rrss-clip:hover { transform: rotate(0deg) translateY(-4px); box-shadow: var(--shadow-b3); }

/* --- RRSS: tarjetas brutalistas -------------------------------------------- */
/* S3: apiladas (1 columna) en vez de 3 en fila — dan la altura que casa con
   el bloque 2×2 de clips, y de paso cada bloque ocupa su propia fila. */
.home-rrss { display: grid; grid-template-columns: 1fr; gap: var(--s-4); }
.home-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);
}
.home-rrss-card svg { width: 26px; height: 26px; color: var(--accent); margin-bottom: var(--s-4); display: block; }
.home-rrss-card h3 { font-size: 1.05rem; margin-bottom: var(--s-1); }
.home-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);
}
.home-rrss-card p { font-size: var(--fs-sm); }
.home-rrss-card:hover { transform: var(--card-hover-move); box-shadow: var(--card-hover-shadow); border-color: var(--ink); }

/* --- FAQ rejilla 3×4 (S3) --------------------------------------------------
   Sustituye EN LA HOME al acordeón `.faq` de components.css (que sigue vivo
   para las páginas de servicio). Feedback Fabio: "añade más preguntas y haz
   una cuadrícula de 3 de ancho y 4 de alto, y que al pulsar alguna se haga
   más grande sobreponiéndose a las demás, y que al pulsar otra esta se
   cierre, solo pudiendo tener una abierta".
   Cards compactas de pregunta; al pulsar, la respuesta abre en un panel
   ABSOLUTO que se sobrepone a las vecinas sin mover la rejilla (nada de
   reflow). Solo una abierta a la vez (JS inline en index.php).
   Última fila (`n+10` de 12): abre hacia ARRIBA para no desbordar la
   sección. Columnas 2 y 3: el panel se centra / se ancla a la derecha, para
   no salirse por el lado. En ≤880 degrada a acordeón de 1 columna con el
   panel en flujo (sin overlay: en móvil tapar es peor que empujar). --- */
.faq-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.faq-cell { position: relative; }
.faq-cell.is-open { z-index: 31; }
/* Ronda de SEO 2026-07-26: cada pregunta pasa a ser un ENCABEZADO de
   verdad. Antes el texto de la pregunta vivía suelto dentro del <button>,
   así que las 56 preguntas del sitio —que son literalmente lo que la gente
   teclea en el buscador— no aparecían en el esquema de encabezados de
   ninguna página. Ahora el <h3> ENVUELVE al botón (y no al revés: un
   <button> solo admite contenido de frase, un <h3> dentro sería HTML
   inválido; envolverlo es además el patrón de acordeón que documenta la
   W3C). Ni una palabra de copy cambia.
   El h3 no debe verse: hereda la tipografía del botón, no mete margen y
   estira a la altura de la celda, que es de donde `.faq-card{height:100%}`
   saca su 100% — sin este `height:100%` las tarjetas de una misma fila
   dejarían de tener la misma altura. */
.faq-h {
  margin: 0; height: 100%;
  font: inherit; font-size: inherit; font-weight: inherit; line-height: inherit;
  color: inherit; letter-spacing: inherit; text-transform: none;
}
.faq-card {
  display: flex; align-items: flex-start; gap: 10px;
  width: 100%; min-height: 96px; height: 100%; text-align: left;
  background: var(--bg-surface); color: var(--ink);
  border: 1px solid var(--card-border); border-radius: var(--card-radius);
  padding: 18px 20px; cursor: pointer;
  font-family: var(--font-disp); font-weight: 600; font-size: .9375rem; line-height: 1.35;
  transition: border-color var(--t-base), box-shadow var(--t-base) var(--ease-pop), transform var(--t-base) var(--ease-pop);
}
/* El "01_" y el "+" se PINTAN desde CSS, no se escriben en el HTML (ronda
   de SEO 2026-07-26). Se ve exactamente igual, pero el texto del <h3> pasa
   de "01_¿Tengo que rehacer mis archivos desde cero?+" a la pregunta
   limpia: es lo que lee un buscador y lo que puede citar un modelo de
   lenguaje. De paso, la numeración deja de estar escrita a mano — añadir o
   quitar una pregunta ya no obliga a renumerar las demás. */
.faq-grid { counter-reset: faq; }
.faq-cell { counter-increment: faq; }
.faq-card i {
  font-family: var(--font-mono); font-style: normal; font-size: var(--fs-mono-sm);
  color: var(--accent-2); padding-top: 2px; flex: none;
}
.faq-card i::before { content: counter(faq, decimal-leading-zero) "_"; }
.faq-card__x {
  margin-left: auto; flex: none;
  font-family: var(--font-mono); font-weight: 700; font-size: 1.05rem; color: var(--accent);
  transition: rotate var(--t-base) var(--ease-pop);
}
.faq-card__x::before { content: "+"; }
.faq-card:hover { border-color: var(--ink); }
.faq-cell.is-open .faq-card {
  border-color: var(--accent); box-shadow: var(--shadow-b1); transform: translate(-2px, -2px);
}
.faq-cell.is-open .faq-card__x { rotate: 45deg; }
.faq-panel {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: 32;
  width: min(230%, 520px);
  background: var(--bg-surface); border: 2px solid var(--ink); border-radius: var(--r-md);
  box-shadow: var(--shadow-b3); padding: var(--s-5);
}
.faq-panel p { font-size: .9375rem; margin: 0; }
.faq-cell:nth-child(3n-1) .faq-panel { left: 50%; transform: translateX(-50%); }
.faq-cell:nth-child(3n) .faq-panel { left: auto; right: 0; }
.faq-cell:nth-child(n+10) .faq-panel { top: auto; bottom: calc(100% + 10px); }
@media (prefers-reduced-motion: reduce) {
  .faq-card, .faq-card__x { transition: none; }
}

/* --- FAQ: cierre con enlace a contacto ------------------------------------- */
.faq-more {
  margin-top: var(--s-6); font-size: var(--fs-sm); color: var(--ink-soft);
}
.faq-more a { color: var(--accent-2); font-weight: 600; }

/* =====================================================================
   CTA FINAL DE LA HOME — el funnel SECUENCIAL embebido sobre navy
   (corrección Fabio, ROADMAP p.1). Sustituye al antiguo `.cta-form`
   (formulario plano de una sola pantalla, retirado junto con su markup en
   index.php): ahora la sección monta el MISMO partial del funnel de
   diagnóstico (includes/funnel-form.php) en modo 'inline', con prefijo de
   ids `cta` para no chocar con el overlay del footer (prefijo `ov`).

   DECISIÓN DE ESTILO — tarjeta CLARA sobre el navy (opción "a"), no
   variante inversa:
   1. El componente `.fs` está diseñado entero para fondo claro (opciones
      `.fs__opt` blancas con sombra pop, inputs blancos, barra de progreso
      azul→morado, foco azul). Repintarlo en oscuro obligaría a rehacer 15
      reglas y a revalidar AA en cada una; la tarjeta clara hereda un
      contraste ya verificado.
   2. Es LITERALMENTE el mismo formulario que ve el visitante en
      /diagnostico y en el overlay: si aquí se viera en oscuro, sería otro
      formulario a ojos del usuario. Así los tres son reconocibles como uno.
   3. La sección sigue mandando: el navy la enmarca, el `.tag--yellow`, el
      h2 y el párrafo se quedan en claro sobre navy, y la tarjeta se ata a
      la sección con una sombra dura AMARILLA (el acento de la familia C,
      el mismo amarillo del tag) en vez de la sombra navy/morada habitual,
      que sobre navy no se vería.
   Un campo por fila (regla de Fabio) lo da `.field` (display:block) tal
   cual, sin nada extra.

   Reparto de responsabilidades:
   - `.fs--inline` (aquí abajo) = SOLO estructura: neutraliza la altura fija
     `calc(100dvh - …)` de `.fs` y el `position:fixed` del "atrás", que son
     de los modos página/overlay. Sirve para cualquier futuro funnel
     embebido, en fondo claro u oscuro.
   - `.cta-funnel …` = el vestido concreto de ESTA sección navy (chapa de
     tarjeta + reponer los colores de texto que `.sec--inverse` aclara).
   ===================================================================== */

/* --- 1. Estructura del modo inline (neutraliza page/overlay) ------------
   `.fs` fija `height: calc(100dvh - --fs-topbar-h)` a propósito (ver el
   comentario largo de components.css: es lo que hace que cada `.fs__step`
   haga scroll DENTRO de sí mismo en el funnel a pantalla completa). Dentro
   de una sección normal eso es justo lo contrario de lo que hace falta: la
   tarjeta tiene que FLUIR con su contenido. Se revierten las tres piezas
   de ese montaje viewport-fijo (alto, overflow del paso y "atrás" fijo al
   viewport) y NADA más — page y overlay no se tocan, esto solo aplica con
   el modificador. pages.css carga DESPUÉS de components.css, así que gana
   sin necesidad de !important. */
.fs--inline {
  height: auto;            /* mata el calc(100dvh - …) de .fs */
  min-height: 0;
}
.fs--inline .fs__step {
  flex: none;              /* no reparte alto sobrante: no hay alto fijo que repartir */
  overflow: visible;       /* el scroll interno era del modo pantalla completa */
  padding: var(--s-6);     /* padding de tarjeta, no --gutter de página */
  /* Suelo de altura (mismo criterio que el `min-height` de
     `.contact-form-card`): la tarjeta cambia de alto al avanzar porque cada
     paso mide lo suyo (medido a 1280: paso 4 = 584px, paso 1 = 846px, paso
     5 = 974px), y esto evita que el paso más corto la deje raquítica. No
     iguala los pasos a propósito: forzar el alto del más largo dejaría
     media tarjeta en blanco en los cortos. */
  min-height: 24rem;
}
/* El "atrás" es `position:fixed` abajo-izquierda del VIEWPORT en los modos
   página/overlay (ahí el funnel ES la pantalla). Embebido tiene que vivir
   dentro de la tarjeta: si no, quedaría flotando sobre la home mientras se
   hace scroll por cualquier otra sección. */
.fs--inline .fs__nav {
  position: static; z-index: auto;
  padding: 0 var(--s-6) var(--s-6);
}
.fs--inline .fs__head { padding: var(--s-3) var(--s-5); }
/* El iframe de Bookings (paso 6) no tiene 100dvh que rellenar aquí. */
.fs--inline .fs__booking, .fs--inline .fs__booking iframe { min-height: 460px; }

/* --- 2. La tarjeta sobre navy ------------------------------------------ */
.cta-funnel { max-width: 40rem; margin-top: var(--s-3); }
.cta-funnel .fs {
  background: var(--bg-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  /* Sombra dura AMARILLA: el lenguaje brutalista de `--shadow-b*` usa navy
     o morado, invisibles sobre esta sección. El amarillo es el acento de la
     familia C y el mismo del `.tag--yellow` de arriba. */
  box-shadow: 10px 10px 0 var(--hl);
  /* Recorta cabecera y barra de progreso contra el radio de la tarjeta. */
  overflow: hidden;
  /* `.sec--inverse` deja `color: var(--ink-inverse)` (blanco) heredándose:
     los h1/h2 no fijan color propio y quedarían blancos sobre blanco. */
  color: var(--ink);
}
/* `.sec--inverse p` (0,1,1) es MÁS específico que `.fs__hint`/`.micro`/
   `.form-req-note`/`.field .err` (0,1,0 / 0,2,0), así que aclara todos los
   párrafos del funnel hasta `--ink-inverse-2` — ilegible sobre la tarjeta
   blanca. Se repone el color real de cada uno. Mismo problema y misma
   solución que `.lp-ovl__panel p` en components.css.
   OJO CON LA ESPECIFICIDAD (bug real detectado midiendo en el navegador):
   el reseteo genérico `.cta-funnel .fs p` es (0,2,1) y se comía las reglas
   de microcopy y de error, que en su forma corta serían (0,2,0) — el
   contador de errores salía en azul de párrafo en vez de rojo. Por eso las
   específicas llevan `.fs` en medio: (0,3,x) gana siempre a (0,2,1). */
.cta-funnel .fs p { color: var(--ink-body); }
.cta-funnel .fs .fs__hint,
.cta-funnel .fs .fs__head-progress,
.cta-funnel .fs .micro,
.cta-funnel .fs .microtrust,
.cta-funnel .fs .form-req-note,
.cta-funnel .fs .fs__booking-fallback p { color: var(--ink-soft); }
.cta-funnel .fs .err { color: var(--danger); }
/* Jerarquía: el <h2> de la sección manda; la pregunta del paso baja un
   escalón para no competir con él (mismo valor que ya usa la compactación
   de `.fs__q h1, .fs__q h2` en viewports bajos, components.css). */
.cta-funnel .fs__q h1, .cta-funnel .fs__q h2 {
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.1rem);
}
/* El <noscript> vive FUERA de la tarjeta (`.fs` entero se oculta sin JS —
   ver `html:not(.js) .fs` en components.css), así que se pinta sobre navy:
   texto claro y enlaces en AMARILLO, no en el azul de `--link` (que sobre
   este navy da 3,5:1 y no llega a AA). Se le gana la especificidad a
   `.sec--inverse p` (0,1,1) con el `p.` delante, para no depender del
   orden de los archivos. */
.cta-funnel p.cta-funnel__noscript { color: var(--ink-inverse-2); max-width: 34rem; }
.cta-funnel__noscript a { color: var(--hl); font-weight: 600; }

/* B3/B4/B5 — ajustes móvil de los elementos nuevos (media query propia, no
   pisa la del bloque de responsive general). */
@media (max-width: 880px) {
  /* La foto sigue abajo-derecha como en escritorio, algo más pequeña. */
  .hero-photo-chip { right: 0; bottom: -6%; left: auto; top: auto; gap: 5px; }
  .hero-photo-chip img { width: 124px; height: 124px; }
  .hero-photo-chip figcaption { padding: 3px 9px; max-width: 130px; }
  .hero-photo-chip figcaption b { font-size: .74rem; }
  .hero-photo-chip figcaption span { font-size: .6rem; }
  .casos-stage { padding: var(--s-5) var(--s-4) var(--s-4); }
  /* CTA final: la tarjeta del funnel a ancho completo del `.wrap`, con la
     chapa compactada (padding y sombra dura más cortos) para no comerse la
     pantalla en móvil. El suelo de altura baja: con menos ancho, los pasos
     largos ya crecen solos y el salto entre pasos es menor. */
  .cta-funnel { max-width: none; }
  .cta-funnel .fs { box-shadow: 6px 6px 0 var(--hl); }
  .fs--inline .fs__step { padding: var(--s-5) var(--s-4); min-height: 20rem; }
  .fs--inline .fs__nav { padding: 0 var(--s-4) var(--s-5); }
  .fs--inline .fs__head { padding: var(--s-3) var(--s-4); }
  .fs--inline .fs__booking, .fs--inline .fs__booking iframe { min-height: 380px; }
}

@media (max-width: 880px) {
  .home-hero__grid, .home-founder, .home-blog, .home-rrss, .home-rrss-grid { grid-template-columns: 1fr; }
  .home-hero { padding-top: var(--s-7); }

  /* Collage del hero en móvil (corrección Fabio: "todo acumulado y ocupando
     la menor altura posible sin restar legibilidad, como en el ordenador").
     Ni carrusel (se retiró la llamada a initHeroScrollCarousel en motion.js)
     ni pila suelta: los 3 marcos SE SOLAPAN con margen negativo y se alternan
     izquierda/derecha, como el collage de escritorio. Cada marco conserva
     ~85% del ancho, así que el mock de dentro se sigue leyendo. El margen
     negativo en % se resuelve contra el ANCHO del contenedor (predecible). */
  .home-hero__media { min-height: 0; display: block; position: relative; }
  .hero-collage__viewport { overflow: visible; }
  .hero-collage__rail { display: flex; flex-direction: column; align-items: stretch; }
  .hero-collage__f1, .hero-collage__f2, .hero-collage__f3 {
    position: static; box-shadow: var(--shadow-soft);
  }
  .hero-collage__f1 { width: 88%; align-self: flex-start; z-index: 3; }
  .hero-collage__f2 { width: 86%; align-self: flex-end;   margin-top: -32%; z-index: 4; }
  .hero-collage__f3 { width: 82%; align-self: flex-start; margin-top: -28%; z-index: 5; }
  /* Los stickers flotantes por DELANTE de los marcos (feedback Fabio: algunos
     quedaban detrás de las pantallas y no se veían). Los marcos van a z 3-5,
     la foto a 7; estos, a 6. */
  .home-hero__media .sticker--float { position: absolute; top: -14px; right: 10px; z-index: 6; }
  /* Iconos flotantes del hero en móvil (feedback Fabio): se mantienen como en
     escritorio pero más pequeños, en vez de ocultarlos, para que el hero no
     quede frío. El sello de marca (logo) sí se oculta: ya está en el header y
     la foto de Fabio hace de sello personal. */
  .home-hero__media .fico { display: grid; transform: scale(.68); z-index: 6; }
  .hero-brand-sticker { display: none; }

  /* R2 (ROADMAP p.2): el mock de Excel del hero tiene min-content ~354px y
     con min-width:auto se sale del ancho util en movil. Se deja encoger el
     frame (min-width:0) y se reduce el mock para que las columnas quepan sin
     recorte (mismo tamano mono que usan las landings, .625rem). El
     .media-frame__body ya recorta (overflow:hidden), asi que cero scroll. */
  .home-hero__media, .home-hero__media .media-frame { min-width: 0; max-width: 100%; }
  .home-hero__grid { grid-template-columns: minmax(0, 1fr); }
  .home-hero__media .mock-sheet { font-size: .625rem; }

  /* R2 (ROADMAP p.2): modo CARRUSEL del hero, gateado por la clase
     .is-carousel que pone motion.js (initHeroScrollCarousel), SOLO con GSAP,
     sin reduced-motion y a <=880px. SIN la clase, el apilado de arriba manda
     (fallback: los 3 frames en columna, sin scroll horizontal). CON la clase,
     los 3 frames pasan a una fila donde cada uno mide el 100% del viewport
     del carril; el viewport recorta (overflow:hidden) y motion.js traslada el
     rail (translateX) ligado al scroll del hero. viewport y rail siguen
     position:static, asi que en desktop (fuera de esta media query) el collage
     absoluto queda intacto. */
  .home-hero__media.is-carousel .hero-collage__viewport { overflow: hidden; width: 100%; }
  .home-hero__media.is-carousel .hero-collage__rail { display: flex; flex-wrap: nowrap; will-change: transform; }
  .home-hero__media.is-carousel .hero-collage__f1,
  .home-hero__media.is-carousel .hero-collage__f2,
  .home-hero__media.is-carousel .hero-collage__f3 { flex: 0 0 100%; width: 100%; margin: 0; }

  .calc { grid-template-columns: 1fr; padding: var(--s-6) var(--s-5); gap: var(--s-6); }
  .calc .fico { display: none; }

  /* Filas de producto (familia C): texto sobre media, sin dos columnas */
  .product-row { grid-template-columns: 1fr; padding: var(--s-6) var(--s-5); gap: var(--s-6); }
  /* KPIs de producto en UNA sola fila en móvil (feedback Fabio): 3 columnas
     encajadas, sin envolver; número arriba y descripción pequeña debajo. */
  .product-row__kpis { gap: var(--s-3); flex-wrap: nowrap; align-items: flex-start; }
  .product-row__kpis li { flex: 1 1 0; min-width: 0; }
  .product-row__kpis b { font-size: 1.3rem; }
  .product-row__kpis small { font-size: .625rem; line-height: 1.25; max-width: none; }

  /* Movil: el sticker del logo se comia el nombre (h3, alineado a la
     izquierda). Lo encogemos y lo anclamos a la esquina superior DERECHA de
     la fila; el nombre vive a la izquierda, asi que ya no lo toca. Ademas,
     al ser mas bajo, su borde inferior queda por encima del h3 (que arranca
     tras el padding-top), asi que tampoco solapa por vertical. */
  .product-row-sticker { top: -12px; left: auto; right: var(--s-4); padding: 6px 9px; }
  .product-row-sticker img { height: 50px; }
  .product-row-sticker .product-panel-sticker__icon { height: 42px; }
  /* Sin sticker a la izquierda (va a la derecha), el título no necesita hueco
     arriba (feedback Fabio: quitar el margin de los títulos de producto). */
  .product-row__text { padding-top: 0; }

  /* Banda de familia B: menos marco, que el ancho útil manda */
  .fam--b { padding: var(--s-5) var(--s-4); }

  /* FAQ: acordeón de 1 columna, panel en flujo (en móvil tapar es peor que
     empujar; además no hay sitio para un overlay del 230%) */
  .faq-grid { grid-template-columns: 1fr; }
  .faq-card { min-height: 0; height: auto; }
  /* 2026-08-04 (foto de Fabio: respuesta despegada de su pregunta y tapando la
     siguiente). El <h3> que envuelve al botón lleva `height:100%` en escritorio
     para que todas las tarjetas de una fila midan igual. En móvil el panel deja
     de ser overlay y pasa a ir EN FLUJO dentro de la misma celda, así que ese
     100% se resuelve contra una celda que YA incluye la altura del panel: el h3
     se estira hasta ~220px, el panel arranca ahí abajo (de ahí el hueco en
     blanco) y la celda se desborda por encima de la pregunta siguiente, que
     queda tapada. Se ve solo con una respuesta abierta, que es justo lo que no
     se mira en un smoke rápido.
     No se toca el `height:100%` de escritorio: ahí es el que iguala la rejilla.
     Bug preexistente desde que el <h3> envuelve al botón (ronda de SEO del
     2026-07-26), no de las preguntas nuevas. */
  .faq-h { height: auto; }
  /* R2-R1 (Fabio smoke): reset con especificidad 0,2,0 para GANAR a los
     nth-child de overlay de escritorio (3n-1 translateX(-50%), 3n right:0),
     que si no empujan la respuesta fuera de pantalla en movil (items 2/5/8/11). */
  .faq-cell:nth-child(n) .faq-panel {
    position: static; width: auto; left: auto; right: auto; top: auto; bottom: auto;
    transform: none; margin-top: var(--s-2); box-shadow: none; border-width: 1px;
  }

  /* Franja de portales (pedido de Fabio, H2): los badges se hacen mas
     pequenos para que los 4 quepan juntos en una sola fila en movil. La
     etiqueta pasa a su propia linea (flex-basis 100%, centrada) para
     dejarle todo el ancho a la fila de badges de abajo; se mantiene el
     flex-wrap para que ese salto de linea funcione (con nowrap la
     etiqueta no se separaria de los badges). El clamp de altura baja de
     34px hasta 16px a 320px: con las proporciones reales de los 4 badges
     (AEAT es el mas ancho) caben de sobra en una fila incluso al ancho
     minimo soportado. */
  .portals-strip .badges-portal { flex-wrap: wrap; justify-content: center; gap: var(--s-3); }
  .portals-strip .badges-portal .lbl {
    flex-basis: 100%; text-align: center; margin-right: 0; margin-bottom: var(--s-2);
  }
  .portals-strip .badges-portal img { height: clamp(16px, 5vw, 22px); width: auto; flex: 0 0 auto; }

  .home-rrss-clips {
    display: flex; overflow-x: auto; gap: var(--s-4); padding-bottom: var(--s-2);
    scrollbar-width: thin;
  }
  /* En móvil vuelve a fila con scroll horizontal: la rejilla 2×2 estirada
     de desktop no aplica, así que el ratio 9:16 lo fija esta media query. */
  .rrss-clip { flex: 0 0 128px; width: auto; height: auto; min-height: 0; aspect-ratio: 9 / 16; }
}

/* =====================================================================
   SERVICIOS — piezas heredadas del hub /servicios (ELIMINADO en Bloque B,
   correcciones cliente: "no le veo mucho sentido, es muy parecido a la
   home"). `.svc-hub-hero`/`.svc-hub-hero__sticker` y `.svc-hub-cta` siguen
   vivas (las reutilizan /nosotros, /casos, /macroning y las 4 páginas de
   servicio). `.svc-compare` (comparativa 3 columnas, DS §4) AHORA la
   consume la HOME, justo tras la tesis ("por qué ahora") — mismas reglas,
   sin duplicar CSS. El resto (`.page-servicios` + filas/cards "ampliadas"
   `.svc-sheet__row__head/__body/__actions`, `.svc-card__actions`,
   `.product-panel__actions`/`.for`: la fila "para quién" + un SEGUNDO
   enlace a /diagnostico?ctx=… que solo tenía el hub) queda huérfano, sin
   ninguna página que lo consuma ya.
   ===================================================================== */

/* --- Hero compacto -------------------------------------------------- */
.svc-hub-hero__sticker { display: inline-block; margin-bottom: var(--s-5); }
.svc-hub-hero .lead { font-size: var(--fs-lead); max-width: 40rem; margin-bottom: var(--s-6); }

/* --- Familia A: filas "ampliadas" -----------------------------------
   El grid compacto del home (76px/1.15fr/1fr/140px) no da sitio a para
   quién + bullets + un segundo CTA. Aquí la fila pasa a 2 columnas
   (índice + contenido) con 3 franjas apiladas vía grid-template-areas,
   scoped a .page-servicios para no tocar el home.
   La fila deja de ser <a> (necesita DOS enlaces reales — servicio y
   diagnóstico contextual — y un <a> no puede anidar otro <a>); el
   hover-shadow de components.css sigue funcionando igual porque :hover
   no depende de la etiqueta del elemento. */
.page-servicios .svc-sheet__row {
  grid-template-columns: 76px 1fr;
  grid-template-areas: "cell head" "cell body" "cell actions";
  row-gap: var(--s-3);
}
.page-servicios .svc-sheet__row .cell { grid-area: cell; }
.svc-sheet__row__head { grid-area: head; }
.svc-sheet__row__body { grid-area: body; }
.svc-sheet__row__body p { max-width: 52rem; }
.svc-sheet__row__body .bullets { margin-top: var(--s-3); }
.svc-sheet__row__actions {
  grid-area: actions; display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-5); margin-top: var(--s-2);
}
.svc-sheet__row__actions .go { padding-top: 0; text-align: left; text-decoration: none; }
.svc-sheet__row .for {
  font-family: var(--font-mono); font-size: .7188rem; color: var(--fam-a);
  background: var(--fam-a-soft); border-radius: var(--r-xs);
  padding: 4px 10px; display: inline-block; margin: 6px 0 2px;
}

/* --- Familia B: cards "ampliadas" ------------------------------------
   .svc-card ya no es <a> (mismo motivo de arriba); .svc-card__go pasa de
   <span> decorativo a <a> real, así que gana su propio text-decoration. */
.svc-card .bullets { margin-top: var(--s-3); }
.svc-card__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-5); margin-top: var(--s-4); }
.svc-card__actions .svc-card__go { margin-top: 0; }
.page-servicios .svc-card__go { text-decoration: none; }

/* --- Familia C: "para quién" + segundo CTA (solo Macroning) ----------
   SmartAudit no lo necesita (sin CTA de compra: sigue siendo un único
   <a>, igual que en el home) — solo el panel de Macroning pasa a
   contenedor con dos enlaces reales dentro. */
.product-panel .for {
  font-family: var(--font-mono); font-size: .7188rem; color: var(--c-lila);
  display: block; margin-bottom: var(--s-3);
}
.product-panel__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-5); }
.product-panel__actions .product-panel__go { margin-top: 0; }
.page-servicios .product-panel__go { text-decoration: none; }

/* CTA contextual de texto (el segundo enlace de cada fila/card, siempre
   → /diagnostico?ctx=…). Azul (--accent-2, "el color del CTA y los
   links") para distinguirlo del enlace "Ver servicio/producto" (ink);
   variante --inverse en lila para el panel navy de Macroning (mismo
   criterio que .product-panel__go: el amarillo nunca es texto, el lila
   sí está aprobado sobre navy).
   BLOQUE C (ronda de correcciones Fabio, GLOBAL — afecta a todas las
   páginas que reutilizan el componente: /casos, /macroning, /smartaudit,
   /nosotros, /blog, todas las de servicio): antes solo cambiaba de color
   en hover, sin subrayado en reposo — "no se destaca el hover ni se nota
   mucho que es un enlace". Ahora lleva subrayado permanente sutil
   (offset 4px, color mezclado al 35% con `color-mix`, nunca opaco del
   todo) y en hover se solidifica: color sólido, subrayado grueso (2px) y
   un micro-desplazamiento horizontal (`translateX`) — mismos tokens de
   color que ya tenía cada variante, ninguno nuevo, así que el contraste
   AA no cambia. `display:inline-block` porque el enlace vive casi
   siempre en medio de una frase (`.svc-inline-cta`) y el `translateX`
   necesita su propia caja; transición SOLO en `transform`/`color` (el
   subrayado cambia de golpe, no se anima: `text-decoration-color`/
   `-thickness` no son propiedades pensadas para transicionar de forma
   fiable entre navegadores). */
.svc-hub-cta {
  display: inline-block;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); font-weight: 700;
  color: var(--accent-2);
  text-decoration: underline; text-underline-offset: 4px;
  text-decoration-color: color-mix(in srgb, var(--accent-2) 35%, transparent);
  transition: color var(--t-base), transform var(--t-base);
}
.svc-hub-cta:hover {
  color: var(--accent);
  text-decoration-color: var(--accent); text-decoration-thickness: 2px;
  transform: translateX(2px);
}
.svc-hub-cta--inverse {
  color: var(--c-lila);
  text-decoration-color: color-mix(in srgb, var(--c-lila) 35%, transparent);
}
.svc-hub-cta--inverse:hover {
  color: var(--ink-inverse);
  text-decoration-color: var(--ink-inverse); text-decoration-thickness: 2px;
}

/* --- Comparativa 3 columnas (.svc-compare, DS §4) ---------------------
   Tabla semántica real (th scope), envuelta en un contenedor con scroll
   horizontal propio (tabindex + aria-labelledby) en vez de trucos de
   tabla responsive. Columna SmartProcess (.is-sp) destacada en morado +
   remate amarillo arriba: mismo lenguaje visual que la columna --sp del
   duelo (home/tesis), pero es una tabla nueva, no el mismo componente. */
.svc-compare { position: relative; }
.svc-compare__scroll {
  overflow-x: auto; border: 1px solid var(--card-border); border-radius: var(--r-md);
  background: var(--bg-surface);
}
.svc-compare table { width: 100%; min-width: 760px; border-collapse: collapse; }
.svc-compare__caption {
  caption-side: top; text-align: left; padding: var(--s-4) var(--s-5);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  border-bottom: 1px solid var(--card-border);
}
.svc-compare th, .svc-compare td {
  text-align: left; padding: var(--s-4) var(--s-5); font-size: var(--fs-sm); color: var(--ink-body);
  border-bottom: 1px solid var(--card-border); vertical-align: top;
}
.svc-compare thead th {
  font-family: var(--font-disp); font-weight: 700; font-size: .9375rem; color: var(--ink);
  border-bottom: 2px solid var(--ink); white-space: nowrap;
}
.svc-compare__tag {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-mono-sm); color: var(--ink-soft);
}
.svc-compare tbody th {
  font-family: var(--font-disp); font-weight: 600; font-size: .9375rem; color: var(--ink);
  white-space: nowrap;
}
.svc-compare tbody tr:last-child th, .svc-compare tbody tr:last-child td { border-bottom: 0; }
.svc-compare .is-sp {
  background: var(--accent); color: var(--ink-inverse);
  border-bottom-color: color-mix(in srgb, var(--accent) 80%, var(--c-navy));
}
.svc-compare thead th.is-sp { border-top: 3px solid var(--hl); }

@media (max-width: 880px) {
  .page-servicios .svc-sheet__row {
    grid-template-columns: 1fr;
    grid-template-areas: "cell" "head" "body" "actions";
  }
  /* R2-R1 (Fabio smoke): la comparativa se REESTRUCTURA en movil (la version
     de 3 columnas a la fuerza salia ilegible con el texto solapado). Cada fila
     de criterio pasa a ocupar el ancho completo como banda-titulo, y debajo
     van sus 3 columnas de valores (SaaS / Agencia / SmartProcess). Se hace con
     grid sobre las <tr>: el th del criterio ocupa las 3 columnas (grid-column
     1/-1) y los 3 td caen cada uno en su columna. La cabecera SaaS/Agencia/
     SmartProcess se muestra una vez arriba. Cero scroll horizontal. */
  .svc-compare__scroll { overflow-x: visible; }
  .svc-compare table, .svc-compare thead, .svc-compare tbody { display: block; min-width: 0; width: 100%; }
  .svc-compare thead tr, .svc-compare tbody tr { display: grid; grid-template-columns: repeat(3, 1fr); }
  .svc-compare thead th:first-child { display: none; }
  .svc-compare thead th { padding: var(--s-2) var(--s-3); font-size: var(--fs-mono-sm); white-space: normal; text-align: center; }
  /* Comparativa en móvil (feedback Fabio): se quitan las filas de criterio
     (los títulos de descripción); quedan solo las 3 columnas SaaS / Agencia /
     SmartProcess. Un borde separa cada grupo para que no se fundan. */
  .svc-compare tbody th { display: none; }
  .svc-compare tbody tr { border-top: 1px solid var(--card-border); }
  .svc-compare tbody tr:first-child { border-top: 0; }
  .svc-compare tbody td {
    padding: var(--s-2) var(--s-3); font-size: var(--fs-mono-sm);
    overflow-wrap: anywhere; text-align: center;
  }
  /* R1-b (Fabio smoke): el caption salia estrecho y muy alto (table-caption
     sobre una tabla display:block se encoge a min-content). A bloque ocupa
     el ancho completo de la tabla; el padding-right reserva la esquina del
     icono decorativo .fico para que no tape el titulo. */
  .svc-compare__caption { display: block; padding-right: calc(var(--s-5) + 60px); }
}

/* =====================================================================
   PÁGINAS DE SERVICIO (esqueleto común) — construido en Tarea 3.2
   (/servicios/blindar-excel, A1) y pensado para que A2/B1/B2 lo
   reutilicen TAL CUAL. Orden de esqueleto: migas → .svc-hero (+
   .svc-meta) → dolores (.dolor) → para quién sí/no (.sino) → qué
   incluye (.inc-grid) → banda (.band, ya existe) → caso (.case, ya
   existe) → FAQ (.faq, ya existe) → CTA final (.sec--inverse).
   Registrado en design-system.md §4.

   Acento de familia parametrizable: cada página fija la custom
   property --svc-accent en un wrapper (p.ej.
   <div class="svc-page" style="--svc-accent:var(--ok)"> en A1, para el
   verde temático de Excel). Se usa SOLO en decoración (sombra de hover,
   barra fx/checks del mock) — nunca como color de texto de lectura
   (los números/índices siguen en el morado de marca, --accent, igual
   que el resto del sitio) ni en el botón (.btn se queda azul siempre).
   Fallback var(--accent) si una página no la fija.
   ===================================================================== */

/* --- Migas de pan ------------------------------------------------------ */
.svc-crumbs {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin-bottom: var(--s-5);
}
.svc-crumbs a { color: var(--ink-soft); text-decoration: none; }
.svc-crumbs a:hover { color: var(--accent-2); text-decoration: underline; }
.svc-crumbs [aria-current] { color: var(--ink); font-weight: 600; }

/* --- Hero de servicio ----------------------------------------------------
   Mismo grid 7fr/5fr que .home-hero__grid; gradiente propio (sutil, de
   superficie a fondo de página) para distinguir el hero de servicio del
   de la home. */
.svc-hero {
  padding: calc(var(--sec-y) - var(--s-4)) 0 var(--s-8);
  background: linear-gradient(180deg, var(--bg-surface) 0%, var(--bg-page) 100%);
}
.svc-hero__grid { display: grid; grid-template-columns: 7fr 5fr; gap: var(--s-8); align-items: center; }
.svc-hero h1 { font-size: var(--fs-h1-svc); margin: var(--s-4) 0 var(--s-5); }
.svc-hero .lead { font-size: var(--fs-lead); max-width: 34rem; margin-bottom: 0; }
.svc-hero__media { position: relative; }

/* Pills de meta (plazo / precio / soporte…) */
.svc-meta { display: flex; flex-wrap: wrap; gap: var(--s-3); margin: var(--s-5) 0; }
.svc-meta span {
  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: var(--r-sm);
  padding: 7px 13px;
}
.svc-meta b { color: var(--accent); }

/* Nota del hueco de vídeo real (cada página pone su propio comentario
   FABIO justo encima, ver blindar-excel.php) */
.svc-hero__video-note {
  margin-top: var(--s-4); text-align: center;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
}

/* CTA de texto suelta entre secciones (reutiliza .svc-hub-cta ya
   registrada en la sección SERVICIOS de este archivo) */
.svc-inline-cta { margin-top: var(--s-6); }

/* Mock de Excel "rico" del hero (extiende .mock-sheet/.mock-sheet__row
   de la home): añade barra fx + tiñe los checks vía --svc-accent (en
   A1, verde Excel). Modificador .mock-sheet--rich para no afectar al
   mock plano que ya usa la home. */
.mock-sheet--rich .mock-sheet__fx {
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono); font-size: .6875rem; color: var(--ink-soft);
  background: var(--bg-calm); border-bottom: 1px solid var(--card-border);
  padding: 6px 4px; margin-bottom: 6px;
}
.mock-sheet--rich .mock-sheet__fx b { color: var(--svc-accent, var(--accent)); font-style: italic; margin-right: 2px; }
.mock-sheet--rich .mock-sheet__row .ok { color: var(--svc-accent, var(--ok)); }

/* --- Dolores — lista de citas con índice mono --------------------------- */
.dolor { list-style: none; border-top: 1px solid var(--card-border); }
.dolor li {
  display: grid; grid-template-columns: 56px 1fr; gap: var(--s-6);
  padding: var(--s-5) 4px; border-bottom: 1px solid var(--card-border);
}
.dolor li i {
  font-family: var(--font-mono); font-style: normal; font-size: var(--fs-mono-md);
  font-weight: 700; color: var(--accent); padding-top: 4px;
}
.dolor li span { font-family: var(--font-disp); font-size: 1.2rem; font-weight: 500; line-height: 1.4; }

/* --- Para quién sí / no ---------------------------------------------------
   Caja "sí": sombra sólida morada de marca (no acento de familia — así
   lo pidió el brief). Caja "no": neutra con × rojo (universal, no se
   tiñe por familia; siempre incluye el cross-link al servicio que sí
   encaja). */
.sino { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-6); }
.sino .box {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: var(--s-7);
}
.sino .box.si { border-color: var(--ink); box-shadow: var(--shadow-b3); }
.sino .box.no { background: var(--bg-calm); }
.sino h3 { font-family: var(--font-mono); font-size: var(--fs-mono-md); margin-bottom: var(--s-5); }
.sino .si h3 { color: var(--accent); }
.sino .no h3 { color: var(--danger); }
.sino ul { list-style: none; font-size: var(--fs-sm); color: var(--ink-body); }
.sino li { margin-bottom: var(--s-4); padding-left: 24px; position: relative; }
.sino li:last-child { margin-bottom: 0; }
.sino .si li::before {
  content: ">"; font-family: var(--font-mono); color: var(--accent);
  position: absolute; left: 0; font-weight: 700;
}
.sino .no li::before {
  content: "\00d7"; font-family: var(--font-mono); color: var(--danger);
  position: absolute; left: 0; font-weight: 700;
}
.sino .no li a { color: var(--ink); font-weight: 600; }
.sino .no li a:hover { color: var(--accent-2); }

/* --- Qué incluye — cards con numeración mono ------------------------------ */
.inc-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
.inc-card {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: var(--s-6);
  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);
}
.inc-card:hover {
  box-shadow: 5px 5px 0 var(--svc-accent, var(--accent));
  transform: var(--card-hover-move); border-color: var(--ink);
}
.inc-card .n {
  font-family: var(--font-mono); font-size: .6875rem; font-weight: 700;
  color: var(--accent); display: block; margin-bottom: var(--s-2);
}
.inc-card b { display: block; font-family: var(--font-disp); color: var(--ink); font-size: .9688rem; margin-bottom: 5px; }

/* --- Packs de alcance (Básico/Premium) — NUEVO, Tarea 3.4 --------------
   Comparativa de DOS packs SIN precios (el ancla es "los dos con precio
   cerrado en la primera llamada", dicho en el hero/FAQ). Self-contained
   como .inc-card (mismos tokens de card --card-radius/--card-hover-*, pero
   sin reutilizar .svc-card a propósito: ese vive en el hub /servicios
   para el listado de familias, este es la comparativa interna de UNA
   página de servicio — contextos distintos, aunque el lenguaje visual sea
   hermano). Premium destaca con sombra sólida FIJA (mismo lenguaje que
   `.sino .box.si`, brand-morado, no acento de familia) — sin badge tipo
   "el que más eligen": ninguna ficha respalda ese dato, así que no se
   inventa. Pensado para que B2 (sistemas de gestión) lo reutilice si algún
   día también vende por packs. */
.svc-packs { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-6); align-items: start; }
.svc-pack {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: var(--s-7);
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
.svc-pack:hover { transform: var(--card-hover-move); box-shadow: var(--card-hover-shadow); }
.svc-pack--premium { border-color: var(--ink); box-shadow: var(--shadow-b3); }
.svc-pack__name {
  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-pack h3 { font-size: 1.3rem; margin-bottom: var(--s-3); }
.svc-pack .bullets { margin-top: var(--s-5); }
/* Lista de extras del Premium: marcador "+" (distinto del ">" de .bullets)
   para que se lea a simple vista qué añade sobre el Básico. */
.svc-pack__extra { list-style: none; margin-top: var(--s-5); }
.svc-pack__extra li {
  padding-left: 26px; position: relative; margin-bottom: 9px;
  color: var(--ink-body); font-weight: 600; font-size: var(--fs-sm);
}
.svc-pack__extra li::before {
  content: "+"; font-family: var(--font-mono); font-weight: 700;
  color: var(--fam-b); position: absolute; left: 0;
}
.svc-pack__plazo {
  display: inline-block; margin-top: var(--s-6);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  background: var(--bg-calm); border-radius: var(--r-xs); padding: 6px 12px;
}

/* --- Cadena bottom-up (metodología) — NUEVO, Tarea 3.5 -----------------
   LA pieza diferencial de /servicios/sistemas-de-gestion (B2): visualiza
   el orden de construcción "de abajo hacia arriba" de la ficha (Procesos
   → Datos → Automatizaciones → Pantallas). Reutiliza `.outline-num`
   (ya existe en components.css) como número gigante de fondo por paso:
   mismo patrón que `.warranty-clause__num` (absolute + opacidad baja,
   recortado por el `overflow:hidden` del paso), aquí en pasos horizontales
   en vez de una rejilla 2×2. El conector `→` es un elemento propio (no un
   `::after`) para poder rotarlo 90° en móvil y convertirlo en conector
   vertical sin duplicar marcado ni depender de un segundo glifo. La capa
   IA de la ficha (entre "Automatizaciones" y "Pantallas" en la cadena
   completa de su §6) NO se dibuja como quinto paso aquí (evita
   redundancia): vive en su bloque propio justo debajo, reutilizando
   `.inc-grid`/`.inc-card`/`.chip` sin CSS nuevo. */
.svc-chain {
  display: flex; align-items: stretch;
  border: 1px solid var(--card-border); border-radius: var(--card-radius);
  background: var(--bg-surface); overflow: hidden;
}
.svc-chain__step {
  position: relative; overflow: hidden;
  flex: 1 1 0; min-width: 0;
  padding: var(--s-6) var(--s-5) var(--s-5);
}
.svc-chain__num {
  position: absolute; top: -10px; left: -6px; z-index: 0;
  opacity: .1; pointer-events: none;
}
.svc-chain__body { position: relative; z-index: 1; }
.svc-chain__body b { display: block; font-family: var(--font-disp); color: var(--ink); font-size: .9688rem; margin-bottom: 6px; }
.svc-chain__body p { font-size: .8125rem; }
.svc-chain__arrow {
  flex: 0 0 auto; width: 34px; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-weight: 700; font-size: 1rem;
  color: var(--svc-accent, var(--accent)); background: var(--bg-calm);
  border-left: 1px solid var(--card-border); border-right: 1px solid var(--card-border);
}

@media (max-width: 880px) {
  .svc-hero { padding-top: var(--s-7); }
  .svc-hero__grid, .sino, .inc-grid, .svc-packs { grid-template-columns: 1fr; }
  .dolor li { grid-template-columns: 40px 1fr; gap: var(--s-4); }
  .svc-chain { flex-direction: column; }
  .svc-chain__arrow {
    width: auto; height: 30px;
    border-left: 0; border-right: 0;
    border-top: 1px solid var(--card-border); border-bottom: 1px solid var(--card-border);
    transform: rotate(90deg);
  }
}

/* =====================================================================
   PRODUCTO — macroning.php (esqueleto de PÁGINA DE SERVICIO reutilizado
   casi tal cual: .svc-hero/.svc-meta/.dolor/.inc-grid/.svc-packs/.band/
   .case/.faq — solo pieza nueva: la lista de puntos de control del
   bloque anti-miedo, que necesita una versión navy-safe de `.bullets`
   (ese usa --ink-body, ilegible sobre --bg-inverse). Mismo patrón de
   marcador que `.bullets`/`.sino li` (padding-left + ::before absolute),
   recoloreado: check ámbar (--hl, icono — nunca texto, respeta §9) +
   término en lila (--c-lila, solo sobre navy, identidad-visual.md §2).
   ===================================================================== */
.mac-checks { list-style: none; display: grid; gap: var(--s-5); margin-top: var(--s-6); max-width: 42rem; }
.mac-checks li { padding-left: 28px; position: relative; }
.mac-checks li::before {
  content: "\2713"; position: absolute; left: 0; top: 2px;
  font-family: var(--font-mono); font-weight: 700; color: var(--c-lila);
}
.mac-checks b { display: block; font-family: var(--font-disp); color: var(--c-lila); font-size: .9688rem; margin-bottom: 2px; }
.mac-checks li > span { display: block; color: var(--ink-inverse-2); font-size: var(--fs-sm); }

/* =====================================================================
   PRODUCTO — smartaudit.php (Tarea 4.2) — reutiliza el esqueleto de
   página de servicio (.svc-hero/.svc-meta/.inc-grid/.band) + el mock
   `.mock-audit` de la home (fiel a la app real, ya definido arriba) +
   `.case`/`.case aside` para "la historia". Pieza nueva: `.sa-story`,
   narrativa de 3-4 párrafos dentro de la columna 2fr de `.case` — el
   sistema aún no tenía un patrón de prosa multi-párrafo (solo citas de
   una línea en `.dolor` o `<blockquote>` cortos en `.case`).
   SIN CTA de compra (decisión 6 del brief): esta página no reutiliza
   `.svc-inline-cta`/`.dolor`/`.sino`/`.svc-packs`/`.faq` a propósito
   (ver comentario de cabecera de smartaudit.php).
   ===================================================================== */
.sa-story p { max-width: 42rem; margin-bottom: var(--s-4); }
.sa-story p:last-child { margin-bottom: 0; }

/* =====================================================================
   CASOS — casos.php (Tarea 5.1). Agrupa las 5 fichas de prueba de valor
   (situación→solución→plazo→resultado) reutilizando `.case`/`.case aside`/
   `.case-terminal` TAL CUAL — ya estaban registrados en design-system.md
   §4 como consumidos por /casos desde el principio (Tarea 3.2). Antes
   cada componente vivía en solitario, uno por página; aquí conviven 5
   seguidos, así que se añade SOLO: el ritmo vertical entre fichas
   (`.caso-ficha`, hairline superior, sin tocar el grid interno de
   `.case`) y la estructura de los 4 bloques con etiqueta mono
   (`.caso-ficha__body`/`__label`). El chip de servicio pasa a ser un
   enlace real a la página de servicio/producto: el resto de usos de
   `.chip` en el sitio son <span> decorativos, nunca <a>, así que aquí
   se le quita el subrayado por defecto del enlace. */
.caso-ficha { padding-top: var(--s-8); border-top: 1px solid var(--card-border); }
.caso-ficha:first-child { padding-top: 0; border-top: 0; }
.caso-ficha__tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.caso-ficha .chip { text-decoration: none; }
.caso-ficha h3 { margin-bottom: var(--s-4); }
.caso-ficha blockquote { margin-bottom: var(--s-4); }
.caso-ficha__body { margin: var(--s-5) 0; display: grid; gap: var(--s-4); }
.caso-ficha__label {
  display: block; font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  font-weight: 700; color: var(--accent); margin-bottom: var(--s-1);
}
.caso-ficha__body p { font-size: var(--fs-sm); }

@media (max-width: 880px) {
  .caso-ficha { padding-top: var(--s-6); }
}

/* --- BLOQUE C (ronda de correcciones Fabio): layout alternado + galería
   de medios reales ------------------------------------------------------
   "Intercalar el orden de los módulos": `.caso-ficha--flip` es un
   modificador SOLO de layout — el DOM no cambia (el texto sigue siendo
   el primer hijo de `.case`, así que un lector de pantalla lo lee antes
   que la media pase lo que pase visualmente). En desktop invierte el
   `grid-template-columns` (1fr/2fr en vez de 2fr/1fr, para que el texto
   siga teniendo la columna ancha aunque visualmente pase a la derecha) y
   coloca los dos hijos directos de `.case` con `grid-column` explícito
   (R1: antes era `order`, ver la nota de la regla) para intercambiar
   su posición. Todo esto SOLO desde 881px: por debajo, `.case` ya cae a
   `grid-template-columns:1fr` (regla mobile de components.css) y aquí no
   tocamos la colocación, así que en móvil las 5 fichas apilan siempre
   texto→media, sea cual sea el modificador — igual que ya pedía la
   propia regla de oro de accesibilidad del bloque.
   "Añadir contenedores con fotos o vídeos, debajo de la tarjeta KPI":
   `.caso-ficha__media` es el wrapper que ahora ocupa esa columna 1fr/2fr
   — agrupa el `aside` de KPIs (`.case-terminal`/`.case aside`, sin
   cambios de contenido ni de CSS) y, debajo, `.caso-media`. El
   `data-scrub` que antes llevaba el `aside` directamente ahora vive en
   este wrapper (mismo valor left/right de siempre, alternaba ya ficha a
   ficha): así la tarjeta KPI y la galería entran juntas en el scrub de
   scroll, en vez de que la galería aparezca suelta sin animación.
   `.caso-media` (RONDA FINAL, corrección "que las fotos se vean enteras,
   tipo stickers superpuestos, no hace falta que estén rectas"): deja de
   ser grid y pasa a columna flex — cada `.caso-media__item` es un marco
   tipo polaroid (borde 2px `--ink` + `--bg-surface` + padding pequeño +
   `--shadow-b1`) con SU PROPIA rotación vía `--rot` inline (rango
   -4deg..4deg, distinto por item) y la imagen SIEMPRE entera
   (`object-fit:contain`, sin recorte ni alto fijo: manda la proporción
   real, aunque el marco salga pequeño en capturas muy anchas o muy
   altas). El solape es de columna, no de rejilla: cada item sube sobre
   el anterior con `margin-top` negativo y alterna `align-self`
   izquierda/derecha (`:nth-child(odd/even)`) para el efecto "tirado
   sobre la mesa", no cuadriculado. Como son flex items, `z-index` les
   aplica sin necesitar `position:relative` (spec CSS: flex/grid items
   respetan z-index aunque sean `position:static`), así que al hacer
   hover el item se endereza (`rotate(0)`), sube de plano (`translateY` +
   `--shadow-b3`) y gana `z-index`, por encima incluso de los items que
   van después en el DOM. `--tall` (sustituye a `--wide`, que ya no tenía
   sentido sin grid de 2 columnas) reduce el ancho al ~70% para las
   portadas verticales de manual (retrato) y para el vídeo de la ficha 3:
   al ser más altos que anchos, un marco más estrecho evita que dominen
   la columna. `:only-child` centra el marco cuando una ficha solo tiene
   una imagen (ficha 4): sin galería de varias piezas no hay "mesa" que
   simular, así que centrado se lee mejor que pegado a un lado. Reutiliza
   el lenguaje de `.rrss-clip` (rotación por variable + hover que
   endereza y sube de plano) en vez de inventar uno nuevo. Ficha 5 sigue
   sin `.caso-ficha__media` ni `.caso-media` (sigue sin haber nada real
   que fotografiar, aunque el texto ya la presente como cerrada — ver
   docblock del PHP). En móvil (`max-width:560px`) se retira el zigzag
   izquierda/derecha (todo centrado) y el solape se reduce a la mitad:
   "apilado con rotaciones suaves", no amontonado. */
@media (min-width: 881px) {
  /* R1 (ROADMAP p.1): la inversión se hace con grid-column, NO con order.
     Con `order` la media pasaba a pintarse ANTES que el texto (los grid
     items se pintan en order-modified document order) y, como
     [data-scrub] deja un transform inline de GSAP que crea contexto de
     apilamiento, el z-index:6 del hover-zoom quedaba encerrado y la foto
     ampliada se metía DEBAJO del texto. Con grid-column el orden de
     pintado vuelve a ser el del DOM (texto, luego media) y la disposición
     visual es exactamente la misma. */
  .caso-ficha--flip { grid-template-columns: 1fr 2fr; }
  .caso-ficha--flip > :first-child { grid-column: 2; grid-row: 1; }
  .caso-ficha--flip > :last-child  { grid-column: 1; grid-row: 1; }
}

.caso-ficha__media { display: flex; flex-direction: column; gap: var(--s-6); }

/* RONDA FINAL 2 (Fabio: "las fotos ocupan más de alto que el propio texto;
   hazlas más pequeñas y júntalas más"): la galería deja de ser una columna
   de marcos grandes y pasa a un MOSAICO de stickers pequeños que fluye en
   filas (wrap). La clave para que las fotos sigan enteras (regla suya
   anterior) siendo bajas: se capa el ALTO de la imagen (`--caso-img-h`) y
   el ancho sale solo del ratio (`width:auto`), nunca al revés — cero
   recortes, cero deformación. Como a ese tamaño no se leen los detalles,
   el hover las endereza y las AMPLÍA sobre el resto (`scale`), que es lo
   que ya hacía `.rrss-clip` con la rotación. */
.caso-media {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start;
  gap: var(--s-2);
  padding: var(--s-4) 0 var(--s-3);
}
.caso-media__item {
  display: block; width: fit-content; max-width: var(--caso-item-w, 190px); z-index: 1;
  border: 2px solid var(--ink); border-radius: var(--r-xs);
  background: var(--bg-surface); overflow: hidden;
  padding: var(--s-2) var(--s-2) 0;
  box-shadow: var(--shadow-b1);
  transform: rotate(var(--rot, 0deg));
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop);
}
/* Solape: márgenes negativos alternos (los pares se montan sobre el
   anterior, los impares suben un poco) — con flex-wrap basta para el
   efecto "pegadas a mano", sin posicionamiento absoluto que rompería el
   flujo del alto. */
.caso-media__item:nth-child(even) { margin-left: calc(var(--s-4) * -1); z-index: 2; }
.caso-media__item:nth-child(3n)   { margin-top: calc(var(--s-3) * -1); }
/* El zoom de hover es generoso a propósito: a este tamaño las capturas se
   leen como "hay software real detrás", y quien quiera mirarlas de verdad
   las levanta con el cursor. El origen centrado mantiene la foto ampliada
   dentro del ancho del contenedor (medido: no genera scroll horizontal). */
.caso-media__item:hover, .caso-media__item:focus-within {
  transform: rotate(0deg) translateY(-4px) scale(2.2);
  box-shadow: var(--shadow-b3);
  z-index: 6;
}
/* `margin-inline:auto`: cuando el pie de foto es más ancho que la imagen
   (apaisadas estrechas), el figure toma el ancho del texto y la imagen se
   quedaba pegada a la izquierda del marco. */
.caso-media__item img,
.caso-media__loop {
  display: block; width: auto; height: auto; margin-inline: auto;
  max-height: var(--caso-img-h, 100px); max-width: 100%;
  object-fit: contain;
}
/* Verticales (portadas de manual, vídeo): a 100px de alto quedarían como
   un sello, así que suben de alto y siguen siendo estrechas. */
.caso-media__item--tall { --caso-img-h: 150px; }
/* Piezas que merecen leerse sin hover (la web de PTA, las pantallas de
   SmartAudit): ocupan el ancho de la columna y suben de alto. Se usan solo
   donde la ficha tiene hueco vertical de sobra (medido, ver docblock de
   casos.php). */
.caso-media__item--big { --caso-img-h: 148px; --caso-item-w: 100%; }

/* La tarjeta KPI de esta página respira menos que en las de servicio: con
   4 métricas (SmartAudit) se comía el alto que necesita la galería de
   abajo. Scoped a `.caso-ficha` para no tocar el mismo componente en
   /smartaudit ni en las páginas de servicio. */
.caso-ficha .case aside:not(.case-terminal),
.caso-ficha aside:not(.case-terminal) { padding: var(--s-6); }
.caso-ficha aside:not(.case-terminal) hr { margin: var(--s-4) 0; }
.caso-media__item figcaption {
  padding: 5px 6px; border-top: 1px solid var(--card-border);
  font-family: var(--font-mono); font-size: .625rem; line-height: 1.3;
  color: var(--ink-soft); text-align: center;
}
.caso-media__item figcaption a { color: var(--accent-2); }
.caso-media__item figcaption a:hover { color: var(--accent); }

/* Enlaces reales dentro de la tarjeta KPI navy (`.glow-tr`, RONDA FINAL:
   potenciatuahorro.es pasa a <a> en las 3 menciones de la ficha PTA) —
   scoped a `.caso-ficha` para no afectar a otras páginas que reutilicen
   `.glow-tr` sin `<a>` dentro. Hereda el color claro del texto (AA sobre
   navy) en vez del azul de enlace por defecto; `--c-lila` al hover, el
   mismo tono ya usado para métricas sobre navy en este componente. */
.caso-ficha .glow-tr a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.caso-ficha .glow-tr a:hover { color: var(--c-lila); }

/* Botón de pausa del loop de vídeo (WCAG 2.2.2): parte del `.rrss-pause`
   del home pero MÁS discreto (petición Fabio: aquí es un apunte al pie de
   una galería, no una acción de sección). `flex-basis:100%` para que rompa
   fila y quede centrado BAJO el mosaico: sin eso es un flex item más y se
   cuela al lado del último sticker. */
.caso-media__pause {
  flex: 0 0 100%; margin: var(--s-2) auto 0;
  justify-content: center;
  font-size: .625rem; padding: 4px 8px;
  border-color: transparent; color: var(--ink-soft);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--card-border);
}
.caso-media__pause:hover { border-color: transparent; color: var(--ink); text-decoration-color: var(--ink-soft); }

@media (max-width: 560px) {
  /* Móvil: las fotos suben de alto (hay ancho de sobra en una columna) y
     se retira el solape lateral; el hover-zoom no aplica en táctil, así
     que a 112px no se leerían. :nth-child(n) empata en especificidad con
     los nth-child de arriba y gana por orden. */
  .caso-media { --caso-img-h: 160px; }
  .caso-media__item--tall { --caso-img-h: 210px; }
  .caso-media__item:nth-child(n) { margin-left: 0; margin-top: 0; }
}

/* G3 (Fabio: que no se salgan de pantalla en movil): el hover-zoom de
   arriba tambien se dispara con un simple toque en tactil, no solo con
   cursor real, y la imagen ampliada podia salirse del viewport. Se anula
   el transform solo donde no hay puntero fino con hover de verdad. */
@media (hover: none) {
  .caso-media__item:hover,
  .caso-media__item:focus-within { transform: none; }
}

/* =====================================================================
   NOSOTROS — nosotros.php (Tarea 5.2; retoques BLOQUE D 2026-07-15).
   Amplía "quién hay detrás" del home: mismo `.founder` (foto real +
   sticker, ya registrado en design-system.md §4 para home Y /nosotros)
   en una rejilla hermana de `.home-founder` pero con más aire para
   varios párrafos de historia. `.founder-legal`/`.founder-linkedin` (ya
   genéricas, no scoped a `.home-founder`) se reutilizan tal cual, sin
   tocarlas.
   BLOQUE D — ritmo de fondos (antes casi toda la página en fondos
   claros, feedback Fabio): equipo pasa a `.sec--inverse glow-bl` (por
   eso deja `.bullets` — ilegible sobre navy, --ink-body/--accent sin
   contraste aprobado ahí — por `.mac-checks`, la variante navy-safe ya
   registrada para `/macroning`) · postura pasa a `.sec--warm` +
   `.nos-principios` (acento por tarjeta sobre el `.inc-card` compartido,
   ver más abajo) · qué NO hacemos pasa a `.sec--calm` (por eso
   `.nos-anti-card` cambia su fill de `--bg-calm` a `--bg-surface`: si
   sección y card comparten el mismo tono, la card se funde y pierde
   definición).
   Única pieza nueva de verdad: `.nos-anti`, la lista "qué NO hacemos"
   (pieza diferencial de la página) — mismo lenguaje que `.sino .no`
   (marcador × rojo, universal) pero como bloque propio a ancho completo,
   sin un "sí" que oponerle (aquí no hay comparativa, solo límites que la
   empresa se pone sola). `.nos-principios` (BLOQUE D) es la segunda:
   capa de acento por tarjeta (`--wc-accent`/borde superior + índice mono
   recoloreado, `--yellow`-safe) sobre `.inc-grid`/`.inc-card`
   compartido — scoped, sin tocar esa base: las páginas de servicio
   siguen consumiéndola tal cual.
   RONDA FINAL (2026-07-16): `.nos-mv` es la tercera pieza nueva — rejilla
   2 col (1 col móvil, mismo breakpoint 880px que `.nos-founder`/
   `.nos-anti`) de dos tarjetas simétricas (misión/visión), recuperando el
   contenido de la web anterior (web-actual/about.php). Tratamiento de
   tarjeta con borde + `--shadow-b2` EN REPOSO (sin hover: es contenido,
   no acción, mismo criterio que `.warranty-clause`), necesario porque la
   sección vive en `.sec--surface` y la tarjeta comparte el mismo fill
   `--bg-surface`: sin sombra sólida a la vista se fundiría con el fondo
   (mismo problema que ya resolvió `.nos-anti-card` con un fill distinto
   en BLOQUE D, aquí resuelto con sombra en vez de con un fill distinto).
   ===================================================================== */
.nos-founder { display: grid; grid-template-columns: 280px 1fr; gap: var(--s-8); align-items: start; }
.nos-founder__text h2 { margin-bottom: var(--s-4); }
.nos-founder__text p { max-width: 38rem; margin-bottom: var(--s-4); }
.nos-founder__text p:last-of-type { margin-bottom: var(--s-5); }

.nos-mv { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-7); }
.nos-mv__card {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); box-shadow: var(--shadow-b2);
  padding: var(--s-7);
}
.nos-mv__card h2,
/* `.nos-origen` (sección "de dónde salen nuestros productos", 2026-08-18)
   reutiliza la rejilla de misión/visión, pero SÍ lleva `.sec-head` con su
   propio h2, así que sus tarjetas titulan con h3 para no dejar tres h2 al
   mismo nivel en una sola sección. Mismo margen, nada más. */
.nos-mv__card h3 { margin: var(--s-3) 0 var(--s-4); }
.nos-mv__card p { margin-bottom: var(--s-4); }
.nos-mv__card p:last-child { margin-bottom: 0; }

/* Postura — acento por tarjeta vía --wc-accent + borde superior, mismo
   patrón que `.guarantees--v2`/`.warranty-clause` (design-system.md §4),
   registrado como capa EXTRA sobre `.inc-card`/`.inc-grid` (pages.css,
   bloque "PÁGINAS DE SERVICIO"). Amarillo (tarjeta /04, `--hl`): nunca
   como color de texto (§9) — el índice mono vuelve a `--ink` en vez de
   heredar `--wc-accent`, igual que ya hace `.guarantee--yellow .n`. */
.nos-principios .inc-card { border-top: 4px solid var(--wc-accent, var(--accent)); }
.nos-principios .inc-card .n { color: var(--wc-accent, var(--accent)); }
.nos-principios .inc-card:hover { box-shadow: 5px 5px 0 var(--wc-accent, var(--accent)); }
.nos-principios .inc-card--yellow .n { color: var(--ink); }

.nos-anti { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
.nos-anti-card {
  display: flex; gap: var(--s-4); align-items: flex-start;
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: var(--s-6);
  transition: transform var(--t-base) var(--ease-pop), box-shadow var(--t-base) var(--ease-pop), border-color var(--t-base);
}
.nos-anti-card:hover { transform: var(--card-hover-move); box-shadow: var(--card-hover-shadow); border-color: var(--ink); }
.nos-anti-card .x {
  font-family: var(--font-mono); font-weight: 700; font-size: 1.1rem; line-height: 1.5;
  color: var(--danger); flex: none;
}
.nos-anti-card p { font-size: var(--fs-sm); }
.nos-anti-card b { display: block; font-family: var(--font-disp); color: var(--ink); font-size: .9688rem; margin-bottom: 3px; }

@media (max-width: 880px) {
  .nos-founder, .nos-anti, .nos-mv { grid-template-columns: 1fr; }
}

/* =====================================================================
   GRACIAS — gracias.php (Tarea 6.4). Página de agradecimiento tras
   /diagnostico o /contacto (redirect 303 de api/lead.php), 3 variantes
   por ?via=. Reutiliza `.sec`/`.wrap`/`.sticker--pop` (mismo patrón que
   `.error-404`, arriba) + `.btn`/`.btn--ghost`/`.bullets` tal cual, sin
   CSS nuevo para ninguno de ellos. Piezas nuevas, mínimas: el contenedor
   centrado angosto (`.gracias__inner`), el párrafo de entrada
   (`.gracias__lead`), el bloque de pasos con su nota-título
   (`.gracias__block`/`__note`/`__steps`; el bloque fuerza texto a la
   izquierda dentro del contenedor centrado, porque una lista de pasos se
   lee peor centrada línea a línea), la fila de acciones
   (`.gracias__actions`, mismo patrón que `.error-404__actions`) y el
   enlace suave a /casos (`.gracias__soft-link`, mismo lenguaje que
   `.home-case-link`: mono, acento morado, hover azul).
   ===================================================================== */
.gracias { padding: var(--s-9) 0; text-align: center; }
.gracias__inner { max-width: 34rem; margin: 0 auto; }
.gracias .sticker { font-size: 1.1rem; margin-bottom: var(--s-5); }
.gracias h1 { margin-bottom: var(--s-4); }
.gracias__lead { font-size: var(--fs-lead); color: var(--ink-body); margin-bottom: var(--s-6); }

.gracias__block { text-align: left; margin-bottom: var(--s-6); }
.gracias__note {
  font-family: var(--font-disp); font-weight: 600; color: var(--ink);
  margin-bottom: var(--s-3);
}
.gracias__steps { margin-bottom: 0; }

.gracias__actions { display: flex; gap: var(--s-4); justify-content: center; flex-wrap: wrap; margin-top: var(--s-2); }
.gracias__soft-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;
}
.gracias__soft-link:hover { color: var(--accent-2); }

@media (max-width: 560px) {
  .gracias__actions { flex-direction: column; align-items: stretch; }
}

/* =====================================================================
   CONTACTO — contacto.php (Tarea 7.1). Formulario de contacto simple y
   directo, alternativa rápida al funnel completo de /diagnostico. Hero
   compacto reutiliza `.chip`/`.brush`/`.lead` tal cual (scoping propio de
   tamaño, mismo patrón que `.svc-hub-hero .lead`/`.home-hero .lead`), sin
   componentes nuevos ahí. Piezas nuevas:
   - `.contact-grid`: rejilla de 2 columnas con columna asignada
     EXPLÍCITAMENTE por CSS (`grid-column`, no `order`) — así el formulario
     puede ir PRIMERO en el DOM (se ve primero en móvil, mejor para
     conversión) y aun así quedar a la derecha en desktop, con los datos a
     la izquierda.
   - `.contact-form-card`: card con sombra suave (mismos tokens que
     `.media-frame`: `--card-border`/`--card-radius`/`--shadow-soft`).
   - `.contact-list`/`.contact-item`: datos de contacto con icono,
     reutilizando `.fico` (ya registrado como icono estándar del sitio,
     identidad-visual.md §9) pero EN FLUJO, no flotante.
   - `.contact-note`: nota de respuesta <24h bajo la rejilla.
   El WhatsApp discreto reutiliza `.whatsapp-link` tal cual (mismo
   componente que footer.php), con SOLO su color adaptado a fondo claro
   (footer.php lo pinta para fondo navy).
   ===================================================================== */
.contact-hero { padding-bottom: var(--s-6); }
.contact-hero .chip { display: inline-block; margin-bottom: var(--s-5); }
.contact-hero .lead { font-size: var(--fs-lead); max-width: 34rem; margin-top: var(--s-4); }

/* R1 (ROADMAP p.1): las dos columnas se alinean de verdad. Antes la de
   datos no tenía ni borde ni padding, así que su <h2> arrancaba 49px por
   encima del encabezado del formulario (1px de borde + 48px de --s-7); y
   con align-items:start la columna corta dejaba un hueco enorme. Ahora
   ambas son tarjetas idénticas, el grid es 1fr 1fr y estira, y la card del
   formulario tiene suelo de altura para no saltar entre paso 1 y paso 2. */
.contact-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-7);
  align-items: stretch;
}
.contact-form-card {
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); box-shadow: var(--shadow-soft);
  padding: var(--s-7);
  /* R1: `grid-row: 1` NO es decorativo, es EL fix del bug que reportó Fabio
     ("está desalineado"). Con `grid-column` pero sin fila explícita, la
     colocación automática de grid es "sparse": la card va primera en el DOM
     y ocupa la columna 2 de la fila 1; cuando le toca a .contact-data, su
     columna 1 queda DETRÁS del cursor y grid no puede retroceder, así que
     la baja a la fila 2. Resultado medido antes del fix: las dos columnas
     apiladas con 772px de desfase entre cabeceras, en un viewport de 1366.
     Fijando la fila en las dos, se colocan de verdad una al lado de otra. */
  grid-column: 2; grid-row: 1;
  /* Suelo = altura del paso 2, que es el más alto, para que la tarjeta no
     encoja al volver al paso 1. El cálculo sobre tokens daba 724 pero la
     medición real en Chrome a 1366 da 738 (las métricas de línea de inputs
     y botones con `line-height: normal` no se pueden sumar sobre papel).
     740 = 738 redondeado al múltiplo de 4. Verificado: paso 1 y paso 2
     miden exactamente lo mismo. */
  min-height: 740px;
}
.contact-form .btn { width: 100%; text-align: center; margin-top: var(--s-2); }

/* Nota de asterisco obligatorio (WCAG 3.3.2) — compartida con el paso 4
   del funnel de /diagnostico. */
.form-req-note { font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft); margin-top: var(--s-2); }

.contact-data {
  grid-column: 1; grid-row: 1;   /* ver la nota de .contact-form-card */
  /* Lo que pidió Fabio es que las dos columnas EMPIECEN a la misma altura,
     y eso ya lo da el grid-row + el borde/padding que replican la cabecera
     del formulario. Estirarla además hasta los 740px del formulario dejaba
     265px de tarjeta blanca vacía (medido), que se ve peor que la
     desigualdad: una ficha de contacto corta al lado de un formulario
     largo es lo normal. Por eso se dimensiona a su contenido. */
  align-self: start;
  background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--s-7);
}
/* El padding-bottom + border-bottom replican el `.fc__head` de la otra
   columna, para que las dos cabeceras casen también por abajo. */
.contact-data h2 {
  font-family: var(--font-mono); font-size: var(--fs-mono-md); font-weight: 700;
  color: var(--ink); margin-bottom: var(--s-5);
  padding-bottom: var(--s-4); border-bottom: 1px solid var(--c-hairline);
}

/* Formulario secuencial de /contacto (R2-T5; ver docblock de contacto.php
   para el porqué): cabecera `.fc__head` calco visual de `.fs__head`
   (funnel `/diagnostico`) — sticker+título a la izquierda, progreso mono a
   la derecha — con clases PROPIAS (no `.fs__*`, pensado para layout
   full-viewport; no `.lp-ovl__*`, que es del overlay a página completa).
   Vive DENTRO de `.contact-form-card`, a ancho de tarjeta: sin
   `position:fixed`/`height:100dvh`, esto es contenido de página normal, no
   una capa. `.fc__paso[hidden]` alterna vía assets/js/contacto.js, mismo
   criterio que `.fs__step[hidden]`/`.lp-ovl__paso[hidden]`. */
.fc__head {
  display: flex; align-items: center; gap: var(--s-4);
  margin-bottom: var(--s-6); padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--c-hairline);
}
.fc__head-id { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s-3); }
.fc__head-title { font-family: var(--font-disp); font-weight: 600; color: var(--ink); }
.fc__head-progress {
  flex: none; font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin: 0;
}

.fc__paso[hidden] { display: none; }
.fc__qtitle {
  font-family: var(--font-disp); font-weight: 600; color: var(--ink);
  font-size: var(--fs-h3); line-height: var(--lh-head); margin-bottom: var(--s-2);
}
/* Frase de apoyo por paso — mismo componente que `.fs__hint` del funnel de
   /diagnostico (mismos tokens), clase propia por vivir en un archivo CSS
   distinto (page-scoped, ver design-system.md §4). */
.fc__hint { font-family: var(--font-body); font-size: var(--fs-sm); color: var(--ink-soft); margin: 0 0 var(--s-5); }

/* Atrás del paso 2 — mismo lenguaje mono que `.fs__back`/`.lp-ovl__backbtn`
   pero EN FLUJO, no `position:fixed`: esta secuencia vive dentro de una
   tarjeta de contenido, no a pantalla completa, así que anclarlo al
   viewport no tendría sentido aquí. */
.fc__back {
  display: inline-flex; align-items: center; margin-bottom: var(--s-4);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  background: none; border: 0; padding: 0; color: var(--ink-soft); cursor: pointer;
}
.fc__back:hover { color: var(--ink); }

/* Micro-transición al cambiar de paso — mismo keyframe/criterio que
   `.fs__q.is-entering`/`.lp-ovl__q.is-entering` (solo transform/opacity). */
@keyframes fcPasoIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.fc__paso.is-entering { animation: fcPasoIn var(--t-reveal) var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .fc__paso.is-entering { animation: none; }
}

/* Sin JS, assets/js/contacto.js no puede alternar `[hidden]`: a diferencia
   de `.fs` (que se oculta ENTERA sin JS, ver components.css — el funnel
   completo no funciona sin su script), aquí basta con enseñar los 2 pasos
   A LA VEZ como un formulario normal de una sola pantalla (progressive
   enhancement de verdad: degrada a exactamente el form pre-R2-T5, todos
   los campos alcanzables y el submit real sigue funcionando). Specificity
   (html + :not(.js) + .fc__paso + [hidden]) gana a `.fc__paso[hidden]`
   de arriba por ser más específica, sin necesitar `!important` (mismo
   principio de cascada que ya documenta `.fs__step[hidden]` en
   components.css). Los botones Continuar/atrás no hacen nada sin JS, así
   que se ocultan. */
html:not(.js) .fc__paso[hidden] { display: block; }
html:not(.js) #fcContinueBtn,
html:not(.js) .fc__back {
  display: none;
}
.contact-list { list-style: none; display: grid; gap: var(--s-6); margin: 0 0 var(--s-6); }
.contact-item { display: flex; gap: var(--s-4); align-items: flex-start; min-width: 0; }
/* `.fico` nace decorativo/flotante (position:absolute; la página fija
   top/left concretos) — aquí es un icono de fila normal, en flujo, así
   que se sobreescribe la posición en este contexto concreto. */
.contact-item .fico { position: static; flex: none; }
.contact-item__label {
  display: block; font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--ink-soft); margin-bottom: var(--s-1);
}
.contact-item a, .contact-item p {
  display: block; font-family: var(--font-disp); font-weight: 600; font-size: 1.05rem;
  color: var(--ink); text-decoration: none;
}
.contact-item a, .contact-item p { overflow-wrap: anywhere; }
.contact-item a:hover { color: var(--accent-2); }

.contact-data .whatsapp-link { color: var(--ink-soft); margin-top: var(--s-2); }
.contact-data .whatsapp-link svg { color: var(--ok); }
.contact-data .whatsapp-link:hover { color: var(--ink); }

.contact-note {
  display: flex; align-items: center; justify-content: center; gap: var(--s-3);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin-top: var(--s-8);
}
.contact-note svg { width: 18px; height: 18px; flex: none; color: var(--accent); }

/* R1: el módulo de RRSS de contacto reutiliza .home-rrss-* (mismas clases,
   mismo CSS) y añade solo la línea de "qué publicamos" en cada tarjeta,
   que es lo que lo hace más desarrollado que el de la home. */
.home-rrss-card__what {
  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) {
  .contact-grid { grid-template-columns: 1fr; gap: var(--s-6); }
  /* `.contact-list` no declara `grid-template-columns` en la regla base (ni
     existe otra regla que lo haga para desktop): su pista es la implícita
     `auto`, que toma el max-content del `.contact-item` más ancho (un
     email/dirección larga, ~355px medido con sonda) y desborda el viewport
     móvil (+48px a 360, +88px a 320). Se fija aquí, solo en móvil, para no
     tocar el comportamiento de desktop, que también usaba esa pista
     implícita pero sin desbordar por tener más ancho disponible. */
  .contact-list { grid-template-columns: minmax(0, 1fr); }
  /* Hay que soltar TAMBIÉN grid-row, no solo grid-column: con la fila
     forzada a 1 en las dos, apilarlas es imposible y grid les inventaba una
     segunda columna implícita (medido: la tarjeta de datos salía a 405px en
     un viewport de 375 y desbordaba). */
  .contact-form-card, .contact-data { grid-column: auto; grid-row: auto; }
  .contact-data { align-self: auto; }
  /* El <textarea> tiene ancho intrínseco por su `cols` por defecto, y como
     los items de grid nacen con `min-width: auto` no pueden encoger por
     debajo del min-content de su contenido: arrastraba la tarjeta entera a
     356px dentro de un wrap de 319 (medido a 375 de viewport). Se veía como
     desbordamiento lateral, tapado por el `overflow-x: clip` global. Es un
     bug preexistente, no de esta ronda, pero cae aquí al tocar el bloque. */
  .contact-form-card, .contact-data { min-width: 0; }
  .contact-form .input { min-width: 0; max-width: 100%; }
  .contact-form-card { padding: var(--s-6) var(--s-5); }
  .contact-data { padding: var(--s-6) var(--s-5); }
  .contact-form-card { min-height: 0; }
  .contact-hero { padding-top: var(--s-7); }
}

/* =====================================================================
   PÁGINAS LEGALES — legal.php, privacidad.php, cookies-politica.php,
   terminos.php (Tarea 7.2). Plantilla compartida deliberadamente sobria:
   `.legal-hero` (chip + H1 + fecha/versión, SIN brush/sticker/banda: son
   páginas legales, cero firma brutalista salvo el chip mono) + `.legal-body`
   (primer patrón de "documento largo" del sitio: varias secciones h2/h3,
   listas y tabla — `.sa-story` ya cubría 3-4 párrafos sueltos, esto es
   más denso). Ancho de lectura ~46rem, más angosto que el resto del sitio
   a propósito (texto de referencia, no marketing). Tabla de cookies:
   contenedor con scroll horizontal propio (`.legal-table-scroll`), mismo
   principio que `.svc-compare__scroll` (evitar trucos de tabla responsive).
   ===================================================================== */
.legal-hero { padding-bottom: var(--s-6); }
.legal-hero .chip { display: inline-block; margin-bottom: var(--s-5); }
.legal-updated {
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft);
  margin-top: var(--s-4);
}

.legal-body { max-width: 46rem; }
.legal-body > p:first-child { font-size: var(--fs-sm); }
.legal-body h2 {
  font-size: 1.5rem; margin-top: var(--s-8); margin-bottom: var(--s-4);
  padding-top: var(--s-7); border-top: 1px solid var(--card-border);
}
.legal-body h3 { font-size: 1.1rem; margin-top: var(--s-6); margin-bottom: var(--s-3); }
.legal-body p { margin-bottom: var(--s-4); font-size: var(--fs-sm); }
.legal-body ul, .legal-body ol { margin: 0 0 var(--s-4) var(--s-5); font-size: var(--fs-sm); }
.legal-body li { margin-bottom: var(--s-2); color: var(--ink-body); }
.legal-body strong { color: var(--ink); }
.legal-body a { text-decoration: underline; text-underline-offset: 2px; }

.legal-table-scroll {
  overflow-x: auto; border: 1px solid var(--card-border); border-radius: var(--r-md);
  margin: var(--s-5) 0 var(--s-6);
}
.legal-table-scroll table { width: 100%; min-width: 640px; border-collapse: collapse; font-size: var(--fs-sm); }
.legal-table-scroll th, .legal-table-scroll td {
  text-align: left; padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--card-border);
  vertical-align: top; color: var(--ink-body);
}
.legal-table-scroll thead th {
  font-family: var(--font-disp); font-weight: 700; font-size: .9375rem; color: var(--ink);
  background: var(--bg-calm); border-bottom: 2px solid var(--ink); white-space: nowrap;
}
.legal-table-scroll tbody tr:last-child th, .legal-table-scroll tbody tr:last-child td { border-bottom: 0; }

@media (max-width: 560px) {
  .legal-body h2 { font-size: 1.3rem; padding-top: var(--s-6); margin-top: var(--s-7); }
}

/* =====================================================================
   BLOG — blog/index.php (Tarea 7.3). "El blog es sobrio"
   (identidad-visual.md §4): sin stickers extra, sin banda, sin duelo,
   un único chip funcional en el hero. El listado reutiliza TAL CUAL
   `.home-blog`/`.home-blog-card`/`.home-blog-empty` (ya registradas en
   design-system.md §4 para el home, sección HOME de este archivo) en vez
   de duplicar CSS casi idéntico bajo un nombre nuevo — mismo criterio de
   categoría/título/extracto/fecha, ahora también consumido por /blog (el
   colapso a 1 columna en móvil ya está cubierto por la media query de
   esa misma sección, sin tocarla). Piezas nuevas de verdad: el hero
   compacto propio (mismo lenguaje que `.svc-hub-hero`/`.legal-hero`,
   pero con su propia clase — cada página tiene la suya en este sistema)
   y la paginación `?p=` (primer componente de este tipo en el sitio).
   ===================================================================== */
.blog-hero { padding-bottom: var(--s-6); }
.blog-hero .chip { display: inline-block; margin-bottom: var(--s-5); }
.blog-hero .lead { font-size: var(--fs-lead); max-width: 34rem; margin-top: var(--s-4); }

.blog-pager {
  display: flex; align-items: center; justify-content: center; gap: var(--s-5);
  margin-top: var(--s-8);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
}
.blog-pager__link {
  color: var(--ink); font-weight: 700; text-decoration: none;
  border: 1px solid var(--card-border); border-radius: var(--r-sm);
  padding: 8px 16px;
  transition: border-color var(--t-fast), color var(--t-fast);
}
.blog-pager__link:hover { border-color: var(--ink); color: var(--accent-2); }
.blog-pager__status { color: var(--ink-soft); }
/* Mantiene "página N de M" centrado aunque falte uno de los dos enlaces
   (primera o última página) — sin esto el status se desplazaría hacia el
   lado que sí tiene enlace. */
.blog-pager__spacer { width: 1px; }

@media (max-width: 560px) {
  .blog-pager { flex-wrap: wrap; gap: var(--s-3); }
}

/* =====================================================================
   BLOG — blog/articulo.php (Tarea 7.3). Reutiliza `.svc-crumbs` TAL CUAL
   para las migas (ya era un átomo genérico de tipografía mono sin nada
   específico de "servicio" en su CSS — ahora consumido también por
   /blog/<slug>, ver design-system.md §4). Piezas nuevas: el hero del
   artículo (chip + H1 + fecha, sin brush: el H1 es texto dinámico del
   admin, no se puede recortar de forma fiable a "una palabra" para
   pintarla), el contenedor de lectura (`.article-container`, ancho de
   prosa ~42rem, mismo principio que `.legal-body`/`.sa-story`), el
   cuerpo de prosa admin (`.article-body`: primeros estilos de h2/h3/p/
   ul/img/a/blockquote para HTML de confianza libre, no una plantilla de
   secciones fijas como el resto del sitio), la caja de CTA destacada
   (`.article-cta`, mismo fondo cálido que `.calc__result`/garantías) y
   los relacionados (`.article-related`, grid de 3 tarjetas PEQUEÑAS a
   propósito — no reutiliza `.home-blog-card`, que es más grande/densa).
   ===================================================================== */
/* ─── EL TITULAR DEL ARTÍCULO, CON EL LENGUAJE DE LA PORTADA (2026-08-13) ───
   Antes: `.article-hero` (chip + h1 + entradilla) DENTRO del panel blanco y,
   justo debajo, la imagen `portada.png` con su propio titular dibujado. El
   titular salía dos veces, parecido pero no igual, y eso lee como descuido.

   Ahora el hero vive FUERA del panel, sobre el navy con trama de puntos que ya
   pintaba `body.page-blog-articulo::before`, y lleva las mismas piezas que el
   PNG. Los números no son a ojo: salen de `tools/tarjeta.py`, que es quien
   dibuja la imagen, y por eso coinciden los colores (los mismos tokens), la
   tipografía (Space Grotesk / Inter / JetBrains Mono) y las proporciones.

   `.article-hero` y `.article-cover` se retiran: ya no las usa nadie.

   ⚠️ `position: relative; z-index: 0` NO es adorno. Crea contexto de apilado
   para que el `::after` del `.brush` (que va a `z-index: -1`) se pinte ENCIMA
   del fondo de la página y DEBAJO del texto. Sin él, el amarillo queda a
   merced del orden de pintado con el `::before` fijo del body, que también
   está a -1. Ninguno de los dos offsets cambia la maquetación. */
.art-portada { position: relative; z-index: 0; padding: var(--s-5) 0 var(--s-7); }

.art-portada__sticker {
  display: inline-block;
  font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-mono-sm);
  line-height: 1; color: var(--c-white); background: var(--c-pop);
  border: 2px solid var(--c-navy); box-shadow: 4px 4px 0 var(--c-navy);
  padding: 10px 17px; border-radius: 3px; transform: rotate(-2deg);
}

.art-portada__tag {
  font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-mono-sm);
  color: var(--c-lila); letter-spacing: .01em; margin: var(--s-5) 0 0;
}
/* El cursor del final se DIBUJA con una caja, no con un glifo: si la fuente no
   trajera ese carácter saldría un cuadrado vacío. Calcado de tarjeta.py. */
.art-portada__tag::after {
  content: ""; display: inline-block; width: .5em; height: .95em;
  background: currentColor; margin-left: .4em; vertical-align: -.12em;
}

/* 17em de ancho relativo a SU PROPIO tamaño: es la proporción exacta de la
   portada (1200 px de lienzo / 72 px de titular), así que el titular parte por
   donde parte en la imagen, sea cual sea el tamaño de pantalla. */
.art-portada h1 {
  max-width: 17em; margin: var(--s-4) 0 0;
  font-size: clamp(2rem, 1.25rem + 3.2vw, 3.5rem);
  line-height: 1.05; letter-spacing: -.028em; font-weight: 700;
  color: var(--c-white);
}

.art-portada__lead {
  max-width: 44rem; margin: var(--s-4) 0 0;
  font-size: var(--fs-lead); line-height: 1.36; color: var(--ink-inverse-2);
}

.art-portada__datos {
  display: flex; flex-wrap: wrap; gap: 14px;
  list-style: none; margin: var(--s-5) 0 0; padding: 0;
}
.art-portada__datos li {
  font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-mono-sm);
  line-height: 1; color: var(--ink-inverse-2);
  background: rgba(255, 255, 255, .10);
  border: 1px solid rgba(255, 255, 255, .30);
  border-radius: 7px; padding: 13px 19px;
}
/* La palabra marcada con *asteriscos* en la ficha. Lila y no amarillo: el
   amarillo de esta identidad es SOLO fondo de subrayado, nunca color de
   texto. */
.art-portada__al { color: var(--c-lila); font-weight: 700; }

@media (max-width: 700px) {
  .art-portada { padding: var(--s-4) 0 var(--s-6); }
  .art-portada__datos { gap: 10px; }
  .art-portada__datos li { padding: 10px 14px; }
}
/* El extracto sigue siendo la entradilla (2026-07-31), pero ahora se pinta
   sobre el navy y por eso vive arriba, en `.art-portada__lead`. Aquí se queda
   la nota de por qué existe: ya se escribía cada semana para el listado y la
   meta description, y es la única frase del artículo pensada para leerse sola.
   `.article-hero__date` desapareció antes que él: la fecha se fue a la ficha
   del autor, y tenerla en dos sitios de la misma pantalla solo daba ocasión de
   que discreparan. */

.article-container { max-width: 42rem; margin: 0 auto; }

.article-body { font-size: var(--fs-body); color: var(--ink-body); }
/* Filete de acento sobre cada h2 (2026-07-31): marca el cambio de pregunta sin
   meter texto decorativo. Se descarto el clasico `content: "// "` justamente
   por eso: un lector de pantalla lo lee, y "barra barra" antes de cada titular
   es ruido para quien no puede verlo. `scroll-margin-top` es lo que hace que
   los enlaces del indice no dejen el titular debajo del topbar sticky. */
.article-body h2 {
  position: relative; font-size: 1.5rem; margin: var(--s-7) 0 var(--s-4);
  padding-top: var(--s-4); color: var(--ink);
  scroll-margin-top: 84px;
}
.article-body h2::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 34px; height: 3px; background: var(--accent);
}
.article-body h3 { font-size: 1.1rem; margin: var(--s-6) 0 var(--s-3); scroll-margin-top: 84px; }
.article-body p { margin-bottom: var(--s-4); }
.article-body ul, .article-body ol { margin: 0 0 var(--s-4) var(--s-5); }
.article-body li { margin-bottom: var(--s-2); }
/* Vinetas con el acento de marca en vez del punto gris del navegador. Solo en
   las listas del CUERPO: `list-style` fuera de aqui lo fija cada componente. */
.article-body ul { list-style: none; margin-left: 0; }
.article-body ul > li { position: relative; padding-left: var(--s-5); }
.article-body ul > li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 7px; height: 7px; border-radius: 2px; background: var(--accent-2);
}
.article-body ol { margin-left: 0; list-style: none; counter-reset: paso; }
.article-body ol > li { position: relative; padding-left: var(--s-6); counter-increment: paso; }
.article-body ol > li::before {
  content: counter(paso, decimal-leading-zero);
  position: absolute; left: 0; top: .1em;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--accent);
  font-weight: 700;
}
.article-body img { max-width: 100%; height: auto; border-radius: var(--r-md); margin: var(--s-5) 0; display: block; }
.article-body a { color: var(--accent-2); text-decoration: underline; text-underline-offset: 2px; }
.article-body blockquote {
  border-left: 3px solid var(--accent); padding-left: var(--s-5); margin: var(--s-5) 0;
  font-family: var(--font-disp); font-style: italic; font-size: 1.05rem; color: var(--ink);
}

.article-cta {
  margin-top: var(--s-8);
  background: var(--bg-warm); border-radius: var(--r-lg);
  padding: var(--s-7); text-align: center;
}
.article-cta h2 { margin-bottom: var(--s-3); }
.article-cta p { color: var(--ink-body); margin-bottom: var(--s-5); }

.article-related { margin-top: var(--s-8); padding-top: var(--s-7); border-top: 1px solid var(--card-border); }
.article-related h2 { font-size: 1.3rem; margin-bottom: var(--s-5); }
.article-related-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.article-related-card {
  display: block; background: var(--bg-surface); border: 1px solid var(--card-border);
  border-radius: var(--card-radius); padding: var(--s-5);
  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);
}
.article-related-card:hover { transform: var(--card-hover-move); box-shadow: var(--card-hover-shadow); border-color: var(--ink); }
.article-related-card .chip { margin-bottom: var(--s-2); }
.article-related-card h3 { font-size: .95rem; margin: 0 0 var(--s-1); }
.article-related-card time { font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--ink-soft); }

@media (max-width: 880px) {
  .article-related-grid { grid-template-columns: 1fr; }
}

/* ── Caja de suscripcion al correo semanal (includes/caja-newsletter.php) ──
   En /blog y al pie de cada articulo. Sobria por identidad-visual §4: el blog
   no lleva stickers ni bandas, solo el chip funcional.

   Mismo fondo calido que .article-cta para que las dos cajas destacadas del
   articulo se lean como familia y no como dos parches distintos. */
.news {
  margin-top: var(--s-8);
  background: var(--bg-warm);
  border-radius: var(--r-lg);
  padding: var(--s-7);
  scroll-margin-top: var(--s-8);   /* el ancla #suscribir no queda bajo la cabecera */
}
.news__t { font-size: 1.35rem; margin: var(--s-4) 0 var(--s-3); }
.news__p { color: var(--ink-body); margin-bottom: var(--s-5); max-width: 38rem; }

.news__form { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-4); }

/* Una fila por campo, a lo ancho: nunca dos inputs en la misma linea. */
.news__fila { width: 100%; max-width: 26rem; }
.news__label {
  display: block; margin-bottom: var(--s-2);
  font-family: var(--font-mono); font-size: .8125rem; color: var(--ink-soft);
}
.news__input {
  width: 100%; padding: 12px 14px;
  font-family: var(--font-body); font-size: 1rem; color: var(--ink);
  background: var(--bg-surface);
  border: 2px solid var(--card-border); border-radius: var(--r-sm);
}
.news__input:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--ring); }

.news__check { display: flex; gap: var(--s-3); align-items: flex-start; max-width: 34rem; cursor: pointer; }
.news__check input { margin-top: .25rem; flex: none; width: 1.05rem; height: 1.05rem; accent-color: var(--accent); }
.news__check span { font-size: .875rem; line-height: 1.5; color: var(--ink-body); }

.news__micro { font-family: var(--font-mono); font-size: .75rem; color: var(--ink-soft); margin: 0; }
.news__micro b { color: var(--ok); }
.news__micro a { color: inherit; }

.news__aviso {
  margin: 0 0 var(--s-4); padding: 12px 14px; border-radius: var(--r-sm);
  font-size: .9375rem; line-height: 1.5; max-width: 38rem;
  border-left: 4px solid var(--ok); background: var(--bg-surface); color: var(--ink-body);
}
.news__aviso--error { border-left-color: var(--danger); }

/* La trampa de bots. Fuera de pantalla y no `display:none`: hay bots que
   saltan lo que esta oculto de la forma obvia, y los lectores de pantalla ya
   la ignoran por el aria-hidden del contenedor. */
.news__hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden;
}

@media (max-width: 600px) {
  .news { padding: var(--s-5); }
  .news__t { font-size: 1.2rem; }
}

/* ── La respuesta directa que abre cada articulo del blog ───────────────────
   Todo articulo del pipeline semanal empieza con la respuesta a la pregunta
   del titulo en 40-60 palabras, escrita para que se entienda SACADA DE
   CONTEXTO: asi es como la trocea y la cita un buscador o un modelo de IA.

   Tiene clase propia y no es adorno: es una pieza estructural que aparece en
   todos los articulos, y sin estilo se lee como un parrafo mas y pierde la
   funcion de responder de un golpe. Sobria, como manda identidad-visual §4
   para el blog: un filete morado y el fondo calido de la casa, nada mas. */
.article-body .respuesta {
  margin: 0 0 var(--s-6);
  padding: var(--s-5);
  background: var(--bg-warm);
  border-left: 4px solid var(--c-pop);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
}
.article-body .respuesta b,
.article-body .respuesta strong { font-weight: 600; }

@media (max-width: 600px) {
  .article-body .respuesta { padding: var(--s-4); font-size: 1rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ARTICULO DEL BLOG SOBRE FONDO OSCURO  (peticion de Fabio, 2026-07-30)
   ═══════════════════════════════════════════════════════════════════════════
   La pagina pasa a fondo navy con la textura de pseudo-codigo, y el articulo
   va dentro de un panel blanco translucido.

   POR QUE EL PANEL ES TRANSLUCIDO Y NO BLANCO A SECAS: para que la textura se
   intuya por detras sin competir con el texto. `.tex-code` se diseño para
   fondos navy (lila al 7%), y sobre blanco puro se leia como texto gris de
   verdad, que era el problema de antes.

   Solo afecta a `.page-blog-articulo`. El listado /blog se queda como esta:
   alli las tarjetas ya cubren el fondo y el cambio no aporta nada. */

/* ⚠️ EL `z-index: 0` NO ES DECORATIVO: SIN ÉL NO SE VE NINGUNA TEXTURA.
   ─────────────────────────────────────────────────────────────────────────
   Un elemento posicionado con `z-index: auto` NO crea contexto de apilado, así
   que las capas de abajo (`::before` y `::after`, las dos con `z-index: -1`)
   se iban a apilar en el contexto de <html> y se pintaban en el paso 2 del
   orden de pintado, o sea ANTES del paso donde se pinta el fondo del propio
   <body>. Resultado: el navy del body tapaba las dos texturas y el fondo se
   veía completamente liso. Fue exactamente la queja de Fabio ("el fondo navy
   es demasiado sobrio"): no es que la textura fuese tenue, es que no llegaba a
   pintarse ni un píxel de ella.

   Con `z-index: 0` sobre un elemento posicionado, <body> SÍ crea contexto de
   apilado: primero se pinta su propio fondo y encima van sus descendientes de
   z-index negativo. Que es lo que se quería desde el principio.

   Esta es la forma correcta de subir una capa por detrás del contenido. La
   INCORRECTA, y ya mordió una vez, es `body.page-blog-articulo > * { position:
   relative; z-index: 1 }`: eso reasigna `position` a TODOS los hijos del body,
   incluidos los que son `fixed` a propósito (el menú móvil `.sp-mnav` y el velo
   del cursor), que pasan a ocupar sitio en el flujo y empujan la página 1.550px
   hacia abajo. La página se ve VACÍA y no hay ningún error en la consola. */
body.page-blog-articulo {
  background: var(--c-navy);
  position: relative;
  z-index: 0;
}

/* ── La textura del fondo (2026-07-31, peticion Fabio "el fondo navy es
   demasiado sobrio") ────────────────────────────────────────────────────────
   La trama de puntos de `.dots-bg`, que es la misma que dibuja tools/tarjeta.py
   en la portada de cada articulo, mas dos halos morado y azul.

   SOLO PUNTOS. Llego a haber una segunda capa `::after` con el pseudo-codigo de
   `.tex-code` encima, y Fabio la mando quitar el mismo dia: en los margenes del
   navy se leia como texto de verdad y la vista se iba a intentar leerlo, que es
   lo contrario de lo que hace una textura. Los puntos se sienten sin pedir
   atencion. Si algun dia se quiere recuperar, el sitio de esa capa es un
   `::after` con la misma receta de aqui abajo (fixed, inset 0, z-index -1).

   Es `fixed` para que no suba con el texto: en una pagina larga, una textura
   que se desplaza con el scroll marea. Y `z-index: -1`, o sea que se pinta
   sobre el fondo del body y por debajo de todo el contenido.

   ⚠️⚠️ NUNCA se arregla esto con `body.page-blog-articulo > * { position: ... }`.
   Eso reasigna `position` a TODOS los hijos del body, incluidos los que son
   `fixed` a proposito: el menu movil (`.sp-mnav`, 971 px) y el velo del cursor
   pasan a ocupar sitio en el flujo y empujan la pagina 1.550 px hacia abajo. La
   pagina se ve VACIA y no hay ningun error en ninguna consola. Ya pasó una vez.

   Los halos son mas CLAROS que el navy, asi que solo pueden mejorar el
   contraste del texto del panel, nunca empeorarlo (ver la cuenta, mas abajo). */
body.page-blog-articulo::before {
  content: "";
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    radial-gradient(circle at 88% -12%, rgba(93, 70, 239, .42), transparent 52%),
    radial-gradient(circle at -8% 108%, rgba(58, 102, 255, .30), transparent 46%),
    radial-gradient(rgba(153, 137, 255, .30) 1.2px, transparent 1.2px);
  /* El ultimo tamaño es el de la trama de puntos: 22px, el mismo valor que
     `.dots-bg` en components.css y el mismo que dibuja tools/tarjeta.py en la
     portada. Si cambia alli, cambia aqui. */
  background-size: auto, auto, 22px 22px;
}

/* El panel. Envuelve hero + cuerpo + todo lo que va detras.
   ─────────────────────────────────────────────────────────────────────────
   ⚠️ EL .90 NO SE BAJA SIN VOLVER A ECHAR LA CUENTA. Es el suelo de contraste,
   no un gusto. El microcopy del sitio (`--ink-soft`, #5b688b: fechas, `<time>`
   de los relacionados, las etiquetas de la caja del correo) compuesto sobre
   este blanco translucido con el navy detras da:

       .93 -> 4.82:1     .90 -> 4.53:1     .88 -> 4.35:1  ← ya NO cumple AA

   El minimo de WCAG AA para texto pequeño es 4.5:1. En .90 se nota la textura
   por detras, que es lo que se pedia, y el microcopy sigue siendo legible para
   quien no tiene vista de veinte años. En .88 deja de serlo. */
.page-blog-articulo .art-panel {
  /* La MISMA trama de puntos del fondo, en navy muy bajito y en la misma
     rejilla de 22px, pintada ENCIMA del blanco translucido.
     Por que, si el panel ya deja pasar el fondo: porque no deja pasar lo
     suficiente. Con el 90% de blanco delante, la trama de detras se queda en
     un 10% de algo que ya era tenue, o sea en nada, y el panel se leia como un
     rectangulo gris. Con la trama propia, la textura ATRAVIESA el panel y es
     lo que hace que se lea como una capa translucida y no como una caja.
     Los puntos ocupan el 0,2% de la superficie a un 5% de opacidad: no mueven
     el contraste del texto, que se pinta encima. */
  background-color: rgba(255, 255, 255, .90);
  background-image: radial-gradient(rgba(10, 31, 68, .05) 1.2px, transparent 1.2px);
  background-size: 22px 22px;
  border: 1px solid rgba(255, 255, 255, .55);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 70px rgba(3, 12, 30, .38);
  padding: var(--s-7) 0 var(--s-8);
  margin: var(--s-6) auto var(--s-8);
  max-width: 60rem;
  backdrop-filter: blur(6px) saturate(112%);
}
@supports not (backdrop-filter: blur(6px)) {
  /* Sin desenfoque, mas opaco: si no, la textura de detras se lee POR ENCIMA
     del texto. La trama propia del panel se queda: es la que hace el trabajo. */
  .page-blog-articulo .art-panel { background-color: rgba(255, 255, 255, .965); }
}

/* Barra de progreso de lectura: NO se pone aquí. Ya existe y es global
   (`.scroll-progress` en components.css, movida por motion.js §3, presente en
   todas las páginas menos /diagnostico). Queda anotado porque la referencia de
   PTA que inspiró este rediseño sí lleva la suya en el artículo, y copiarla tal
   cual deja dos barras de gradiente pegadas en el borde de arriba. */
.page-blog-articulo .art-panel > .sec { padding-top: 0; }
.page-blog-articulo .art-panel .wrap { padding-left: var(--s-6); padding-right: var(--s-6); }

@media (max-width: 860px) {
  .page-blog-articulo .art-panel {
    margin: 0 auto; border-radius: 0; border-left: 0; border-right: 0;
    max-width: none; box-shadow: none;
    /* ⚠️ `background-color` y NO el atajo `background`: el atajo reinicia
       `background-image`, y con él se llevaba por delante la trama de puntos
       del panel. En móvil el panel ocupa el ancho entero, así que esa trama es
       LA ÚNICA textura que se ve: sin ella el artículo entero vuelve a ser un
       rectángulo liso, que es justo lo que se estaba arreglando.
       .93 en vez de .97 por lo mismo (y sigue cumpliendo AA: 4.82:1). */
    background-color: rgba(255, 255, 255, .93);
  }
}

/* Las migas y el pie viven FUERA del panel, sobre el navy: hay que aclararlas. */
.page-blog-articulo .art-fuera { max-width: 60rem; margin: 0 auto; padding: var(--s-6) var(--s-5) 0; }
.page-blog-articulo .art-fuera .svc-crumbs,
.page-blog-articulo .art-fuera .svc-crumbs a { color: var(--ink-inverse-2); }
.page-blog-articulo .art-fuera .svc-crumbs a:hover { color: #fff; }
/* El ultimo escalon (el titulo) lleva `color: var(--ink)` de la regla general,
   que es navy: sobre el fondo oscuro desaparecia y la miga se leia "inicio /
   blog /" y nada mas. */
.page-blog-articulo .art-fuera .svc-crumbs [aria-current] { color: #fff; }

/* ── Quien firma el articulo ────────────────────────────────────────────────
   Foto real, nombre enlazado a la pagina de autor y una linea de metadatos.
   No es adorno: es la unica pieza de la pagina que dice que detras hay una
   persona concreta, y va justo antes del indice para que se lea "esto lo dice
   este, y trata de esto" antes de empezar. */
.art-autor {
  display: flex; align-items: center; gap: var(--s-4);
  margin-bottom: var(--s-6); padding: var(--s-4) 0;
  border-top: 1px solid var(--card-border);
  border-bottom: 1px solid var(--card-border);
}
.art-autor__foto {
  width: 52px; height: 52px; flex: none;
  border-radius: 50%; object-fit: cover;
  /* El navy de marca en vez de un borde gris: son 2px, pero es lo que hace que
     la foto se lea como parte del sistema y no como un avatar pegado. */
  border: 2px solid var(--ink);
}
.art-autor__txt b {
  display: block; font-family: var(--font-disp); font-size: 1rem; color: var(--ink);
}
.art-autor__txt b a { color: inherit; text-decoration: none; }
.art-autor__txt b a:hover { color: var(--accent-2); text-decoration: underline; }
.art-autor__txt small {
  display: block; margin-top: 2px;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); line-height: 1.6;
  color: var(--ink-soft);
}
@media (max-width: 480px) {
  /* El mono a 13px con tres datos y un separador no cabe en 320px sin partirse
     por donde no debe. Se apila y se deja respirar. */
  .art-autor { align-items: flex-start; }
  .art-autor__foto { width: 44px; height: 44px; }
}

/* ── En este articulo (indice de anclas) ────────────────────────────────────
   Numerado con `counter` y no con la numeracion del <ol>: asi los numeros
   pueden llevar la tipografia mono y el color de acento sin tocar el texto del
   enlace. */
.art-indice {
  margin-bottom: var(--s-6); padding: var(--s-5);
  background: var(--bg-calm);
  border: 1px solid var(--card-border); border-radius: var(--r-md);
}
.art-indice b {
  display: block; margin-bottom: var(--s-3);
  font-family: var(--font-disp); font-size: 1rem; color: var(--ink);
}
.art-indice ol { list-style: none; margin: 0; padding: 0; counter-reset: idx; display: grid; gap: 8px; }
.art-indice li { counter-increment: idx; display: flex; gap: var(--s-3); align-items: baseline; margin: 0; }
.art-indice li::before {
  content: counter(idx, decimal-leading-zero);
  flex: none;
  font-family: var(--font-mono); font-size: var(--fs-mono-sm); color: var(--accent);
}
.art-indice a {
  color: var(--ink-body); text-decoration: none; font-size: .9375rem; line-height: 1.5;
}
.art-indice a:hover { color: var(--accent-2); text-decoration: underline; }

/* ── El recuadro de datos de en medio (includes/datos-sp.php) ───────────────
   Borde solido y sombra brutalista, el lenguaje de tarjeta destacada de este
   sitio (`.inc-card`, `.warranty-clause`, `.nos-mv__card`).

   Se probo antes con el mismo patron que `.respuesta` (filete a la izquierda y
   fondo de color plano) y NO valia: sobre un panel que ya es blanco al 90%, un
   azul palido se funde con el fondo y la caja desaparecia. Y tiene que
   distinguirse justamente porque no es contenido del articulo: es la empresa
   hablando de si misma, y eso el lector tiene derecho a verlo venir. */
.article-body .dato-sp {
  display: block; margin: var(--s-7) 0;
  padding: var(--s-5);
  background: var(--c-blue-soft);
  border: 2px solid var(--ink);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-b2);
}
.article-body .dato-sp__et {
  display: inline-block; margin-bottom: var(--s-2);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  text-transform: uppercase; letter-spacing: .06em; font-weight: 700;
  color: var(--accent-2);
}
.article-body .dato-sp p { margin: 0; color: var(--ink); line-height: 1.6; }
.article-body .dato-sp p strong,
.article-body .dato-sp p b { color: var(--ink); font-weight: 700; }
.article-body .dato-sp__ver {
  display: inline-block; margin-top: var(--s-3);
  font-family: var(--font-mono); font-size: var(--fs-mono-sm);
  color: var(--accent-2); font-weight: 700; text-decoration: none;
}
.article-body .dato-sp__ver:hover { text-decoration: underline; }
@media (max-width: 600px) {
  .article-body .dato-sp { padding: var(--s-4); }
}

/* ── CTA dentro del texto ───────────────────────────────────────────────────
   Va EN MEDIO del articulo, no al final, y su copy lo escribe quien redacta:
   tiene que hablar de lo que se acaba de leer. Un "pide tu diagnostico" suelto
   a mitad de parrafo se salta con la vista; uno que dice "esto que acabamos de
   describir es lo que hacemos" se lee. */
.article-body .cta-inline {
  display: block;
  margin: var(--s-7) 0;
  padding: var(--s-5) var(--s-6);
  background: var(--bg-warm);
  border-left: 4px solid var(--c-blue);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  text-decoration: none; color: inherit;
  transition: transform .18s var(--ease-out), box-shadow .18s var(--ease-out);
}
.article-body a.cta-inline:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 var(--c-pop);
}
.article-body .cta-inline b {
  display: block; font-family: var(--font-disp); font-weight: 600;
  font-size: 1.08rem; line-height: 1.35; color: var(--ink); margin-bottom: .3em;
}
.article-body .cta-inline span {
  display: block; font-size: .95rem; color: var(--ink-body);
}
.article-body .cta-inline em {
  display: block; margin-top: .55em; font-style: normal;
  font-family: var(--font-mono); font-size: .8rem; color: var(--accent-2);
}

/* ── El caso real del servicio del que trata el articulo ────────────────── */
.caso-rel { margin-top: var(--s-8); }
.caso-rel__card {
  display: block; margin-top: var(--s-4);
  background: var(--c-navy); color: #fff;
  border-radius: var(--r-lg); padding: var(--s-6);
  text-decoration: none;
  background-image: var(--glow-pop); background-repeat: no-repeat;
  background-position: 115% -60%; background-size: 380px 380px;
  transition: transform .2s var(--ease-out), box-shadow .2s var(--ease-out);
}
.caso-rel__card:hover { transform: translate(-3px, -3px); box-shadow: 10px 10px 0 var(--c-pop); }
.caso-rel__card .chip { background: rgba(255,255,255,.12); color: var(--c-lila); border-color: rgba(255,255,255,.22); }
.caso-rel__t { color: #fff; font-size: 1.3rem; margin: var(--s-4) 0 var(--s-3); }
.caso-rel__cita { color: var(--ink-inverse-2); font-size: 1rem; line-height: 1.6; margin: 0; }
.caso-rel__pie {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  margin: var(--s-5) 0 0;
  font-family: var(--font-mono); font-size: .8rem; color: var(--ink-soft);
}
.caso-rel__dato { color: var(--c-yellow); }
.caso-rel__ver { margin-left: auto; color: var(--c-lila); font-weight: 700; }
@media (max-width: 600px) { .caso-rel__ver { margin-left: 0; } }

/* ── Las redes al final del articulo ────────────────────────────────────── */
.art-rrss { margin-top: var(--s-8); padding-top: var(--s-7); border-top: 1px solid var(--card-border); }
.art-rrss__t { font-size: 1.3rem; margin: var(--s-3) 0 var(--s-2); }
.art-rrss__p { color: var(--ink-body); margin-bottom: var(--s-5); max-width: 40rem; }
/* En la home, `.home-rrss` es de UNA columna a proposito: vive en la columna
   estrecha de `.home-rrss-grid`, al lado de la rejilla de videos. Aqui tiene
   el ancho entero del panel, y tres tarjetas apiladas anaden pantalla y media
   justo al final del articulo, que es donde menos apetece seguir bajando. */
.art-rrss .home-rrss { margin: 0; grid-template-columns: repeat(3, 1fr); }
.art-rrss .home-rrss-card { padding: 20px; }
.art-rrss .home-rrss-card svg { margin-bottom: var(--s-3); }
.art-rrss .home-rrss-card p { font-size: .875rem; }
@media (max-width: 760px) {
  .art-rrss .home-rrss { grid-template-columns: 1fr; }
}
.art-rrss__wa { margin-top: var(--s-5); }
