/* Manrope, autoalojada.
 *
 * Antes se cargaba desde Google Fonts. Eso mandaba la IP y el User-Agent de
 * CADA visitante a Google LLC (EE.UU.) en CADA visita: una transferencia
 * internacional que la politica de privacidad no declaraba. Autoalojarla
 * elimina el problema en vez de documentarlo, y de paso permite endurecer la
 * CSP a font-src 'self' y quitar dos preconnect.
 *
 * Son los mismos dos subconjuntos que servia Google —latin y latin-ext—, en
 * formato variable, 38 KB en total. Los demas (griego, cirilico, vietnamita)
 * no se traen: el sitio es en espanol.
 *
 * Manrope esta bajo SIL Open Font License 1.1, que permite el autoalojamiento.
 * La licencia viaja con la fuente en fonts/OFL.txt, como exige la propia OFL.
 * La ruta es ../fonts/ porque esta hoja vive en css/.
 */

@font-face{
  font-family:"Manrope";
  font-style:normal;
  font-weight:400 800;          /* variable: cubre los cuatro pesos que usa el sitio */
  font-display:swap;
  src:url("../fonts/manrope-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face{
  font-family:"Manrope";
  font-style:normal;
  font-weight:400 800;          /* variable: cubre los cuatro pesos que usa el sitio */
  font-display:swap;
  src:url("../fonts/manrope-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* BlueNova IT — hoja única del sitio.
 *
 * Se escribe a mano igual que la anterior: no hay build, lo que se edita es lo
 * que se sirve. El orden importa, porque las capas se corrigen entre sí:
 *
 *   1. base            tokens, componentes y las correcciones acumuladas del
 *                      rediseño (halos, separadores, ritmo, apilamiento)
 *   2. páginas         hero corto, migas, dos columnas, formulario, prosa legal
 *   3. diagramas       gramática de los dibujos de servicio y su movimiento
 *   4. sitio real      lo que el prototipo no necesitaba: skip-link, estado
 *                      del formulario, campo trampa
 *
 * Manrope se autoaloja en fonts/ y se declara arriba con @font-face. El sitio
 * no carga ningún recurso de terceros: la CSP no autoriza un solo origen
 * externo. Si algún día vuelve a enlazarse una fuente o un script desde un
 * CDN, hay que añadir su origen a _headers o el navegador lo bloqueará.
 */

/* ══════════════════ 1 · BASE ══════════════════ */


  :root{
    --void:#070D16; --panel:#0C1724; --panel-2:#101F31;
    --line:rgba(255,255,255,.075); --line-2:rgba(255,255,255,.15);
    --ink:#E9F1FA; --ink-2:#92A8C2; --ink-3:#5D7590;
    --azure:#4D9BFF; --azure-2:#8CC0FF; --deep:#0B3B8C;
    --halo-1:rgba(77,155,255,.30); --halo-2:rgba(11,59,140,.34);
    --brillo:rgba(255,255,255,.055);
    --sombra:0 24px 60px -20px rgba(0,0,0,.75);
    --glow:0 0 0 1px rgba(77,155,255,.35), 0 12px 40px -12px rgba(77,155,255,.45);

    --t-xs:.75rem; --t-sm:.875rem; --t-b:1rem; --t-md:1.125rem;
    --t-lg:1.375rem; --t-xl:1.75rem; --t-2xl:2.25rem; --t-3xl:3rem;
    --e1:4px;--e2:8px;--e3:12px;--e4:16px;--e5:20px;--e6:24px;
    --e8:32px;--e10:40px;--e12:48px;--e16:64px;--e20:80px;--e28:112px;--e36:144px;
    --r:14px; --ancho:1140px;
    --sans:'Manrope','Segoe UI',system-ui,sans-serif;
    --mono:'Cascadia Mono',Consolas,'SF Mono',monospace;
  }

  /* Contraparte diurna. No es una inversión: el halo se vuelve un lavado suave
     y el vidrio pasa a papel. Mismo azul, otra luz. */
  :root[data-theme="light"]{
    --void:#F5F8FC; --panel:#FFFFFF; --panel-2:#FFFFFF;
    --line:rgba(10,45,95,.11); --line-2:rgba(10,45,95,.2);
    --ink:#081426; --ink-2:#4B6787; --ink-3:#7B93AE;
    --azure:#0F5FD0; --azure-2:#0B4AA6; --deep:#0B3B8C;
    --halo-1:rgba(77,155,255,.20); --halo-2:rgba(11,59,140,.10);
    --brillo:rgba(10,45,95,.04);
    --sombra:0 20px 50px -24px rgba(8,32,72,.28);
    --glow:0 0 0 1px rgba(15,95,208,.28), 0 12px 34px -14px rgba(15,95,208,.34);
  }
  @media (prefers-color-scheme: light){
    :root:not([data-theme="dark"]){
      --void:#F5F8FC; --panel:#FFFFFF; --panel-2:#FFFFFF;
      --line:rgba(10,45,95,.11); --line-2:rgba(10,45,95,.2);
      --ink:#081426; --ink-2:#4B6787; --ink-3:#7B93AE;
      --azure:#0F5FD0; --azure-2:#0B4AA6;
      --halo-1:rgba(77,155,255,.20); --halo-2:rgba(11,59,140,.10);
      --brillo:rgba(10,45,95,.04);
      --sombra:0 20px 50px -24px rgba(8,32,72,.28);
      --glow:0 0 0 1px rgba(15,95,208,.28), 0 12px 34px -14px rgba(15,95,208,.34);
    }
  }

  *{box-sizing:border-box}

  body{
    margin:0; background:var(--void); color:var(--ink);
    font-family:var(--sans); font-size:16px; line-height:1.65;
    -webkit-font-smoothing:antialiased; position:relative; overflow-x:hidden;
  }

  /* Grano fino sobre todo: es lo que evita que los degradados se vean planos
     y digitales. Ruido SVG, sin imágenes externas. */
  body::after{
    content:""; position:fixed; inset:0; z-index:9; pointer-events:none;
    opacity:.5; mix-blend-mode:overlay;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
  }
  :root[data-theme="light"] body::after{opacity:.28}

  .env{max-width:var(--ancho);margin:0 auto;padding:0 var(--e6)}

  .aviso{
    position:relative;z-index:8;background:var(--panel);border-bottom:1px solid var(--line);
    color:var(--ink-3);font-family:var(--mono);font-size:var(--t-xs);
    letter-spacing:.12em;text-transform:uppercase;text-align:center;padding:var(--e2) var(--e4);
  }

  /* ── nav ─────────────────────────────────────── */
  .nav{position:relative;z-index:8;border-bottom:1px solid var(--line)}
  .nav-int{max-width:var(--ancho);margin:0 auto;padding:var(--e5) var(--e6);
    display:flex;align-items:center;gap:var(--e8)}
  .logo{display:flex;align-items:center;gap:var(--e3);font-weight:800;
    font-size:var(--t-md);letter-spacing:-.025em;color:var(--ink);text-decoration:none;white-space:nowrap}
  .marca{width:9px;height:9px;border-radius:50%;background:var(--azure);
    box-shadow:0 0 12px 2px var(--azure);flex:none}
  .logo b{color:var(--azure);font-weight:800}
  .menu{display:flex;gap:var(--e6);margin-left:auto;align-items:center;flex-wrap:wrap}
  .menu a{font-size:var(--t-sm);font-weight:500;color:var(--ink-2);text-decoration:none;transition:color .16s}
  .menu a:hover,.menu a:focus-visible{color:var(--ink)}

  .btn{display:inline-flex;align-items:center;justify-content:center;min-height:44px;
    padding:var(--e3) var(--e6);font-size:var(--t-sm);font-weight:700;text-decoration:none;
    border-radius:10px;border:1px solid transparent;letter-spacing:-.01em;
    transition:background .18s,border-color .18s,box-shadow .18s,transform .18s}
  .btn-p{background:var(--azure);color:#04101F;box-shadow:0 8px 26px -10px rgba(77,155,255,.7)}
  .btn-p:hover,.btn-p:focus-visible{background:var(--azure-2);transform:translateY(-1px)}
  :root[data-theme="light"] .btn-p{color:#fff}
  @media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .btn-p{color:#fff}}
  .btn-g{background:linear-gradient(180deg,var(--brillo),transparent);border-color:var(--line-2);color:var(--ink)}
  .btn-g:hover,.btn-g:focus-visible{border-color:var(--azure);color:var(--azure)}

  /* ── hero con halo ───────────────────────────── */
  .hero{position:relative;padding:var(--e36) 0 var(--e28);overflow:hidden}
  .hero::before{
    content:"";position:absolute;left:50%;top:-40%;transform:translateX(-50%);
    width:min(1400px,150vw);height:900px;pointer-events:none;
    background:
      radial-gradient(ellipse 46% 42% at 50% 50%, var(--halo-1), transparent 68%),
      radial-gradient(ellipse 70% 55% at 42% 62%, var(--halo-2), transparent 72%);
    filter:blur(12px);
  }
  /* Línea de horizonte: separa el halo del contenido y da profundidad. */
  .hero::after{
    content:"";position:absolute;left:0;right:0;top:0;height:1px;
    background:linear-gradient(90deg,transparent,var(--line-2) 30%,var(--line-2) 70%,transparent);
  }
  .hero .env{position:relative;z-index:2}

  .eyebrow{display:inline-flex;align-items:center;gap:var(--e2);
    font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.14em;text-transform:uppercase;
    color:var(--azure);margin:0 0 var(--e6);
    border:1px solid var(--line-2);border-radius:999px;padding:6px var(--e4);
    background:linear-gradient(180deg,var(--brillo),transparent)}
  .eyebrow i{width:5px;height:5px;border-radius:50%;background:var(--azure);
    box-shadow:0 0 8px 1px var(--azure);font-style:normal;flex:none}

  h1{font-size:clamp(2.75rem,7vw,5rem);font-weight:800;line-height:.98;
    letter-spacing:-.045em;margin:0 0 var(--e6);max-width:15ch;text-wrap:balance}
  h1 em{font-style:normal;color:var(--azure)}
  .entrada{font-size:var(--t-md);color:var(--ink-2);max-width:52ch;margin:0 0 var(--e10);line-height:1.6}
  .acciones{display:flex;gap:var(--e3);flex-wrap:wrap}

  .cifras{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--e8);
    margin-top:var(--e28);padding-top:var(--e8);border-top:1px solid var(--line)}
  .cifra .n{font-size:var(--t-3xl);font-weight:800;letter-spacing:-.045em;line-height:1;
    display:block;margin-bottom:var(--e2);font-variant-numeric:tabular-nums;
    background:linear-gradient(180deg,var(--ink),var(--ink-3));
    -webkit-background-clip:text;background-clip:text;color:transparent}
  .cifra .d{font-size:var(--t-sm);color:var(--ink-2)}

  /* ── secciones ───────────────────────────────── */
  .sec{position:relative;padding:var(--e28) 0}
  .sec-cab{max-width:60ch;margin-bottom:var(--e16)}
  .kicker{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.14em;
    text-transform:uppercase;color:var(--ink-3);margin:0 0 var(--e4)}
  h2{font-size:clamp(1.875rem,3.8vw,2.75rem);font-weight:800;letter-spacing:-.04em;
    line-height:1.1;margin:0 0 var(--e4);text-wrap:balance}
  .sec-cab p{color:var(--ink-2);margin:0;font-size:var(--t-md)}

  /* Tarjetas de vidrio: borde tenue, luz superior interna, halo al pasar. */
  .rejilla{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--e5)}
  .tarjeta{
    position:relative;border:1px solid var(--line);border-radius:var(--r);
    padding:var(--e10);display:flex;flex-direction:column;
    background:linear-gradient(180deg,var(--panel-2),var(--panel));
    box-shadow:inset 0 1px 0 var(--brillo),var(--sombra);
    transition:border-color .22s,transform .22s,box-shadow .22s;
  }
  .tarjeta:hover{border-color:var(--line-2);transform:translateY(-3px);
    box-shadow:inset 0 1px 0 var(--brillo),var(--sombra),0 0 40px -18px var(--azure)}
  .rombo{width:26px;height:26px;border-radius:7px;flex:none;margin-bottom:var(--e6);
    background:linear-gradient(140deg,var(--azure),var(--deep));
    box-shadow:0 0 22px -6px var(--azure)}
  .tarjeta h3{font-size:var(--t-lg);font-weight:800;letter-spacing:-.03em;margin:0 0 var(--e3)}
  .tarjeta>p{color:var(--ink-2);font-size:var(--t-sm);margin:0 0 var(--e6)}
  .lista{list-style:none;padding:0;margin:0 0 var(--e8);display:flex;flex-direction:column;gap:var(--e2)}
  .lista li{font-size:var(--t-sm);color:var(--ink-2);padding-left:var(--e5);position:relative}
  .lista li::before{content:"";position:absolute;left:0;top:.66em;width:7px;height:1px;background:var(--azure);opacity:.8}
  .enlace{margin-top:auto;align-self:flex-start;font-size:var(--t-sm);font-weight:700;
    color:var(--azure);text-decoration:none;transition:gap .18s;display:inline-flex;gap:6px}
  .enlace:hover,.enlace:focus-visible{gap:12px}

  /* Etapas: hairline superior que se enciende al pasar. */
  .pasos{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--e6)}
  .paso{padding-top:var(--e5);border-top:1px solid var(--line-2);transition:border-color .22s}
  .paso:hover{border-top-color:var(--azure)}
  .paso .k{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.1em;color:var(--azure);margin-bottom:var(--e3)}
  .paso h4{font-size:var(--t-b);font-weight:700;margin:0 0 var(--e2);letter-spacing:-.02em}
  .paso p{font-size:var(--t-sm);color:var(--ink-2);margin:0}

  /* ── cierre con halo inferior ────────────────── */
  .cierre{position:relative;border-top:1px solid var(--line);overflow:hidden}
  .cierre::before{content:"";position:absolute;left:50%;bottom:-60%;transform:translateX(-50%);
    width:min(1100px,130vw);height:620px;pointer-events:none;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-1),transparent 70%);filter:blur(10px)}
  .cierre-int{position:relative;z-index:2;padding:var(--e28) 0;text-align:center;
    display:flex;flex-direction:column;align-items:center;gap:var(--e6)}
  .cierre h2{max-width:18ch;margin:0}
  .cierre p{color:var(--ink-2);max-width:52ch;margin:0}

  /* ── pie ─────────────────────────────────────── */
  .pie{position:relative;z-index:2;border-top:1px solid var(--line);padding:var(--e16) 0 var(--e10)}
  .pie-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:var(--e8);margin-bottom:var(--e16)}
  .pie h5{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.12em;text-transform:uppercase;
    color:var(--ink-3);margin:0 0 var(--e4);font-weight:600}
  .pie ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--e2)}
  .pie a{font-size:var(--t-sm);color:var(--ink-2);text-decoration:none;transition:color .16s}
  .pie a:hover,.pie a:focus-visible{color:var(--azure)}
  .pie-txt{font-size:var(--t-sm);color:var(--ink-2);margin:var(--e4) 0 0;max-width:34ch}
  .pie-bajo{border-top:1px solid var(--line);padding-top:var(--e5);display:flex;
    justify-content:space-between;gap:var(--e5);flex-wrap:wrap;
    font-family:var(--mono);font-size:var(--t-xs);color:var(--ink-3);letter-spacing:.04em}

  /* ── notas ───────────────────────────────────── */
  .notas{position:relative;z-index:2;border-top:1px solid var(--line);background:var(--panel);padding:var(--e20) 0}
  .notas .env{max-width:860px}
  .nota{border-top:1px solid var(--line);padding:var(--e6) 0;display:grid;
    grid-template-columns:150px 1fr;gap:var(--e6)}
  .nota dt{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.1em;
    text-transform:uppercase;color:var(--azure)}
  .nota dd{margin:0;font-size:var(--t-sm);color:var(--ink-2)}
  .nota dd b{color:var(--ink);font-weight:700}
  code{font-family:var(--mono);font-size:.86em;color:var(--azure-2);
    background:var(--brillo);border:1px solid var(--line);padding:.1em .34em;border-radius:5px}

  a:focus-visible,.btn:focus-visible{outline:2px solid var(--azure);outline-offset:3px;border-radius:6px}

  .up{opacity:0;transform:translateY(14px);animation:up .62s cubic-bezier(.2,.75,.3,1) forwards}
  .up:nth-child(1){animation-delay:.05s}.up:nth-child(2){animation-delay:.13s}
  .up:nth-child(3){animation-delay:.21s}.up:nth-child(4){animation-delay:.29s}
  .up:nth-child(5){animation-delay:.37s}
  @keyframes up{to{opacity:1;transform:none}}

  @media (prefers-reduced-motion:reduce){
    *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important}
    .up{opacity:1;transform:none}
  }

  @media (max-width:900px){
    .rejilla{grid-template-columns:1fr}
    .pasos{grid-template-columns:1fr 1fr}
    .pie-grid{grid-template-columns:1fr 1fr}
    .hero{padding:var(--e20) 0 var(--e16)}
    .sec{padding:var(--e20) 0}
    .cifras{margin-top:var(--e16)}
  }
  @media (max-width:560px){
    .cifras,.pasos,.pie-grid{grid-template-columns:1fr}
    .nota{grid-template-columns:1fr;gap:var(--e2)}
    .nav-int{flex-wrap:wrap;gap:var(--e4)}
    .menu{margin-left:0;gap:var(--e5)}
    .tarjeta{padding:var(--e8)}
  }

  /* ════════════ SISTEMA DE COLOR POR FUNCIÓN ════════════
     Un color = un trabajo. El azul es la marca y SAP; el verde azulado
     identifica ciberseguridad; el ámbar aparece SOLO en la acción principal.
     Ese aislamiento es lo que lo hace funcionar (von Restorff): si el ámbar
     se usara en dos sitios más, dejaría de significar "aquí se hace clic".
     Nunca se codifica solo por color: cada sección lleva además su rótulo. */
  :root{
    --banda:#0A1521;
    --teal:#34D6C5; --teal-halo:rgba(52,214,197,.22); --teal-deep:#0A5C56;
    --ambar:#FFB020; --ambar-2:#FFC65C;
  }
  :root[data-theme="light"]{
    --banda:#EDF3FA;
    --teal:#0A7A70; --teal-halo:rgba(52,214,197,.16); --teal-deep:#0A5C56;
    --ambar:#9C5D00; --ambar-2:#804C00;
  }
  @media (prefers-color-scheme:light){
    :root:not([data-theme="dark"]){
      --banda:#EDF3FA;
      --teal:#0A7A70; --teal-halo:rgba(52,214,197,.16);
      --ambar:#9C5D00; --ambar-2:#804C00;
    }
  }

  /* Ritmo tonal: las secciones alternan de valor. Es lo que rompe la
     monotonía sin meter más colores. */
  .banda{background:var(--banda)}

  /* Acción principal en ámbar: es el único elemento cálido de la página. */
  .btn-p{background:var(--ambar);color:#1A1000;box-shadow:0 8px 26px -10px rgba(255,176,32,.55)}
  .btn-p:hover,.btn-p:focus-visible{background:var(--ambar-2)}
  :root[data-theme="light"] .btn-p{color:#fff}
  @media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .btn-p{color:#fff}}

  /* Sección de ciberseguridad: hue propio y su propio halo. */
  .sec.teal{position:relative;overflow:hidden}
  .sec.teal::before{
    content:"";position:absolute;left:50%;top:-30%;transform:translateX(-50%);
    width:min(1200px,140vw);height:700px;pointer-events:none;
    background:radial-gradient(ellipse 46% 44% at 50% 50%,var(--teal-halo),transparent 70%);
    filter:blur(14px);
  }
  .sec.teal>.env{position:relative;z-index:2}
  .sec.teal .kicker{color:var(--teal)}
  .sec.teal .kicker::before{content:"";display:inline-block;width:16px;height:1px;
    background:var(--teal);vertical-align:middle;margin-right:8px}
  .sec.teal .ficha{border-left:2px solid transparent;transition:background .2s,border-color .2s}
  .sec.teal .ficha:hover{border-left-color:var(--teal);background:var(--panel-2)}
  .sec.teal .fichas{border-color:var(--line)}

  /* El rótulo de cada sección también se distingue por forma, no solo color. */
  .kicker::before{content:"";display:inline-block;width:16px;height:1px;
    background:var(--ink-3);vertical-align:middle;margin-right:8px}
  #servicios .kicker::before,#portafolio .kicker::before{background:var(--azure)}
  #servicios .kicker,#portafolio .kicker{color:var(--azure)}

  /* Rejilla de tres para los servicios clave. */
  .rejilla.tres{grid-template-columns:repeat(3,minmax(0,1fr))}
  .rejilla.tres .tarjeta{padding:var(--e8)}

  /* Fichas: rejilla a hairline. Densidad alta sin ruido. */
  .fichas{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;
    background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
  .ficha{background:var(--panel);padding:var(--e8);transition:background .2s}
  .ficha:hover{background:var(--panel-2)}
  .ficha h4{font-size:var(--t-b);font-weight:800;letter-spacing:-.025em;margin:0 0 var(--e2)}
  .ficha p{font-size:var(--t-sm);color:var(--ink-2);margin:0}
  .banda .ficha{background:var(--panel)}

  /* El rombo de cada tarjeta hereda el hue de su sección. */
  .sec.teal .rombo{background:linear-gradient(140deg,var(--teal),var(--teal-deep));
    box-shadow:0 0 22px -6px var(--teal)}
  .sec.teal .lista li::before{background:var(--teal)}
  .sec.teal .enlace{color:var(--teal)}

  .sec-linea{border-top:1px solid var(--line)}
  .acciones-c{margin-top:var(--e10)}

  @media (max-width:900px){
    .rejilla.tres{grid-template-columns:1fr}
    .fichas{grid-template-columns:1fr 1fr}
  }
  @media (max-width:560px){ .fichas{grid-template-columns:1fr} }

  /* ════════════ CAPA VISUAL ════════════ */

  /* Retícula de plano técnico, muy tenue, tras el hero. Da textura sin ruido:
     es el papel milimetrado sobre el que se dibuja un landscape. */
  .hero .malla{
    position:absolute;inset:0;pointer-events:none;z-index:1;
    background-image:
      linear-gradient(to right,var(--line) 1px,transparent 1px),
      linear-gradient(to bottom,var(--line) 1px,transparent 1px);
    background-size:64px 64px;
    -webkit-mask-image:radial-gradient(ellipse 70% 60% at 50% 30%,#000 20%,transparent 75%);
    mask-image:radial-gradient(ellipse 70% 60% at 50% 30%,#000 20%,transparent 75%);
    opacity:.7;
  }

  .hero-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
    gap:var(--e16);align-items:center}

  /* ── diagrama de landscape ──────────────────────
     No es un adorno abstracto: son las capas reales de una operación SAP,
     con la seguridad envolviéndolas. Dice a qué se dedican sin una foto. */
  .diagrama{position:relative;width:100%;max-width:440px;margin-left:auto}
  .diagrama svg{width:100%;height:auto;display:block;overflow:visible}

  .cap-caja{fill:var(--panel);stroke:var(--line-2);stroke-width:1}
  .cap-caja.core{stroke:var(--azure)}
  .cap-tit{fill:var(--ink);font-size:13px;font-weight:800;letter-spacing:-.02em}
  .cap-sub{fill:var(--ink-3);font-size:10.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase}
  .cap-pt{fill:var(--azure)}

  /* Flujo entre capas: guion animado. Es el único movimiento continuo y es
     lento a propósito — sugiere operación, no urgencia. */
  .flujo{stroke:var(--azure);stroke-width:1.5;fill:none;stroke-linecap:round;
    stroke-dasharray:5 9;animation:fluir 3.2s linear infinite;opacity:.85}
  @keyframes fluir{to{stroke-dashoffset:-28}}

  /* El anillo de seguridad encierra el stack: es el argumento de la sección
     "dos especialidades, un mismo equipo", dicho en un dibujo. */
  .escudo{fill:none;stroke:var(--teal);stroke-width:1.25;stroke-dasharray:3 7;
    opacity:.8;animation:girar 26s linear infinite;transform-origin:center}
  @keyframes girar{to{stroke-dashoffset:-200}}
  .escudo-tit{fill:var(--teal);font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase}

  .nodo{fill:var(--teal)}
  .nodo-pulso{fill:var(--teal);opacity:.28;animation:latir 3s ease-out infinite}
  @keyframes latir{0%{r:5;opacity:.34}70%{r:13;opacity:0}100%{r:13;opacity:0}}

  /* ── revelado al hacer scroll ─────────────────── */
  .rev{opacity:0;transform:translateY(18px);
    transition:opacity .65s cubic-bezier(.2,.75,.3,1),transform .65s cubic-bezier(.2,.75,.3,1)}
  .rev.visible{opacity:1;transform:none}
  .rev-d1{transition-delay:.08s} .rev-d2{transition-delay:.16s} .rev-d3{transition-delay:.24s}

  /* ── detalle en las fichas: flecha que aparece ── */
  .ficha{position:relative}
  .ficha::after{
    content:"→";position:absolute;right:var(--e6);top:var(--e8);
    color:var(--azure);opacity:0;transform:translateX(-6px);
    transition:opacity .2s,transform .2s;font-weight:700;
  }
  .ficha:hover::after{opacity:1;transform:none}
  .sec.teal .ficha::after{color:var(--teal)}

  @media (max-width:980px){
    .hero-grid{grid-template-columns:1fr;gap:var(--e12)}
    .diagrama{margin:0 auto;max-width:400px}
  }
  @media (prefers-reduced-motion:reduce){
    .flujo,.escudo,.nodo-pulso{animation:none}
    .rev{opacity:1;transform:none;transition:none}
  }


  /* ════════════ AJUSTES v7 ════════════ */

  /* Logotipo: la marca es BlueNova IT. Antes el corte de color caía entre
     "Blue" y "Nova" y eso la hacía leer como tres palabras. La agrupación
     correcta es BlueNova · IT. */
  .logo .wm{color:var(--ink)}
  .logo .wm-it{color:var(--azure);margin-left:.32em}

  /* Acción principal: el ámbar amarillento obligaba a texto casi negro, que a
     14 px se lee turbio. Desplazado a naranja, admite texto BLANCO a 5.18:1.
     Sigue siendo el único elemento cálido de la página. */
  :root{ --accion:#C2410C; --accion-h:#A93A08; --accion-txt:#FF8A3D; }
  :root[data-theme="light"]{ --accion:#BE4708; --accion-h:#A03A05; --accion-txt:#9A3208; }
  @media (prefers-color-scheme:light){
    :root:not([data-theme="dark"]){ --accion:#BE4708; --accion-h:#A03A05; --accion-txt:#9A3208; }
  }
  .btn-p{
    background:var(--accion);color:#fff;font-weight:800;letter-spacing:.005em;
    box-shadow:0 8px 26px -12px rgba(194,65,12,.85);
  }
  .btn-p:hover,.btn-p:focus-visible{
    background:var(--accion-h);color:#fff;
    box-shadow:0 10px 30px -10px rgba(194,65,12,.95);
  }
  :root[data-theme="light"] .btn-p{color:#fff}
  @media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .btn-p{color:#fff}}

  /* ── iconografía ────────────────────────────────
     Trece servicios seguidos son difíciles de barrer solo con texto. Un icono
     por capacidad da un ancla visual para lectura rápida, que es como lee
     alguien que evalúa proveedores. */
  .sprite{position:absolute;width:0;height:0;overflow:hidden}
  .ico{width:22px;height:22px;flex:none;stroke:currentColor;fill:none;
    stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
  .ficha .ico{color:var(--azure);margin-bottom:var(--e4);display:block}
  .sec.teal .ficha .ico{color:var(--teal)}
  .tarjeta .ico{width:26px;height:26px;color:var(--azure);margin-bottom:var(--e6);display:block}

  /* Numeración de sección: el orden es real (recorrido de la página) y sirve
     de orientación en un scroll largo. */
  .kicker .num{color:var(--ink-3);font-weight:700;margin-right:.5em}

  /* Barra de progreso de lectura. Detalle pequeño que hace que la página se
     sienta construida, no maquetada. */
  .progreso{position:fixed;top:0;left:0;right:0;height:2px;z-index:50;
    background:transparent;pointer-events:none}
  .progreso span{display:block;height:100%;width:0;
    background:linear-gradient(90deg,var(--azure),var(--teal));
    box-shadow:0 0 12px rgba(77,155,255,.6);transition:width .08s linear}

  @media (prefers-reduced-motion:reduce){ .progreso span{transition:none} }


  /* ════════════ v8 · PROFUNDIDAD ESTRUCTURAL ════════════ */

  /* Vuelve el ámbar. La legibilidad se arregla por tipografía, no bajando el
     color: texto casi negro a peso 800 y un punto más grande. 10.95:1. */
  :root{ --accion:#FFB020; --accion-h:#FFC65C; --accion-txt:#FFB020; }
  :root[data-theme="light"]{ --accion:#F0A200; --accion-h:#FFB733; --accion-txt:#8A5200; }
  @media (prefers-color-scheme:light){
    :root:not([data-theme="dark"]){ --accion:#F0A200; --accion-h:#FFB733; --accion-txt:#8A5200; }
  }
  .btn-p{
    background:var(--accion);color:#0B0800;font-weight:800;
    font-size:.9375rem;letter-spacing:.004em;
    box-shadow:0 10px 30px -12px rgba(255,176,32,.7);
  }
  .btn-p:hover,.btn-p:focus-visible{background:var(--accion-h);color:#0B0800}
  :root[data-theme="light"] .btn-p{color:#0B0800}
  @media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .btn-p{color:#0B0800}}

  /* ── elevación en capas ─────────────────────────
     Antes todas las superficies estaban al mismo nivel. Ahora hay tres
     alturas y las tarjetas que flotan se salen del límite de su sección. */
  :root{
    --alt-1:0 2px 8px -4px rgba(0,0,0,.5);
    --alt-3:0 30px 70px -24px rgba(0,0,0,.85), 0 8px 24px -12px rgba(0,0,0,.6);
  }
  :root[data-theme="light"]{
    --alt-1:0 2px 8px -4px rgba(8,32,72,.14);
    --alt-3:0 28px 64px -26px rgba(8,32,72,.34), 0 6px 20px -12px rgba(8,32,72,.18);
  }

  /* Las tres tarjetas clave se salen hacia abajo y quedan por encima de la
     banda siguiente. Es el gesto que rompe la sensación de página plana. */
  #servicios{padding-bottom:0}
  #servicios .rejilla.tres{
    position:relative;z-index:4;
    margin-bottom:calc(var(--e16) * -1);
  }
  #servicios .rejilla.tres .tarjeta{box-shadow:inset 0 1px 0 var(--brillo),var(--alt-3)}
  #portafolio{padding-top:calc(var(--e28) + var(--e12))}

  /* ── franja a sangre: el momento tipográfico ────
     Rompe la secuencia de rejillas y deja respirar. El texto es de ellos. */
  .franja{position:relative;overflow:hidden;border-top:1px solid var(--line);
    border-bottom:1px solid var(--line);background:var(--banda)}
  .franja::before{
    content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
    width:min(1200px,150vw);height:600px;pointer-events:none;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-1),transparent 70%);
    filter:blur(16px);opacity:.75;
  }
  .franja .env{position:relative;z-index:2;padding-top:var(--e28);padding-bottom:var(--e28)}
  .franja .marca-cita{width:34px;height:2px;background:var(--accion);margin-bottom:var(--e8)}
  .franja blockquote{
    margin:0;font-size:clamp(1.875rem,5vw,3.5rem);font-weight:800;
    line-height:1.06;letter-spacing:-.045em;max-width:20ch;text-wrap:balance;
  }
  .franja blockquote em{font-style:normal;color:var(--accion-txt)}
  .franja figcaption{margin-top:var(--e8);color:var(--ink-2);max-width:52ch;font-size:var(--t-md)}

  /* ── "por qué": asimetría en vez de rejilla ─────
     Las dos tarjetas se desfasan verticalmente. Deja de leerse como tabla. */
  .split-asim{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--e6);align-items:start}
  .split-asim>*:nth-child(2){margin-top:var(--e16)}
  .split-asim .tarjeta{box-shadow:inset 0 1px 0 var(--brillo),var(--alt-3)}

  /* Ficha de sección: acento superior que la ancla a su color. */
  .tarjeta.acento{border-top:2px solid var(--azure)}
  .tarjeta.acento.t2{border-top-color:var(--teal)}

  @media (max-width:900px){
    .split-asim{grid-template-columns:1fr}
    .split-asim>*:nth-child(2){margin-top:0}
    #servicios .rejilla.tres{margin-bottom:calc(var(--e12) * -1)}
    #portafolio{padding-top:calc(var(--e20) + var(--e12))}
    .franja .env{padding-top:var(--e20);padding-bottom:var(--e20)}
  }


  /* ════════════ v9 · SEPARADORES Y VOLUMEN ════════════ */

  /* Separadores en degradado. No son decorativos: cada uno hace la transición
     entre el color de la sección que termina y el de la que empieza, así que
     el borde entre SAP y ciberseguridad es literalmente azul → verde azulado. */
  .banda{background-color:var(--banda);background-image:none}

  .sec-linea,.franja,.cierre{
    border-top:0 !important;
    background-repeat:no-repeat;
    background-size:100% 2px;
    background-position:top left;
  }
  #portafolio{background-image:linear-gradient(90deg,transparent,var(--azure) 28%,var(--azure) 72%,transparent)}
  #seguridad {background-image:linear-gradient(90deg,transparent,var(--azure) 22%,var(--teal) 78%,transparent)}
  .franja    {background-image:linear-gradient(90deg,transparent,var(--teal) 22%,var(--accion) 78%,transparent)}
  #porque    {background-image:linear-gradient(90deg,transparent,var(--accion) 22%,var(--azure) 78%,transparent)}
  .cierre    {background-image:linear-gradient(90deg,transparent,var(--azure) 28%,var(--teal) 72%,transparent)}
  .pie{border-top:0;background-image:linear-gradient(90deg,transparent,var(--line-2) 30%,var(--line-2) 70%,transparent);
    background-repeat:no-repeat;background-size:100% 1px;background-position:top left}

  /* El degradado necesita el color de fondo aparte, o la banda lo tapaba. */
  #portafolio,#porque{background-color:var(--banda)}
  .franja{background-color:var(--banda)}

  /* ── luz ambiental repartida ─────────────────────
     Antes solo el hero y dos bloques tenían halo; el resto era fondo liso y
     eso aplana. Ahora cada sección tiene su fuente de luz, descentrada y en
     distinta posición, para que el fondo nunca se lea uniforme. */
  /* Solo las tres secciones que lo necesitan: aplicarlo a todas .sec crearía
     bloques absolutos de 900px sin posición definida en las demás. */
  #servicios::after,#portafolio::after,#porque::after{
    content:"";position:absolute;pointer-events:none;z-index:0;
    width:min(900px,90vw);height:520px;filter:blur(20px);opacity:.6;
  }
  #servicios::after{left:-12%;top:6%;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-1),transparent 70%)}
  #portafolio::after{right:-14%;top:24%;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-2),transparent 70%)}
  #porque::after{left:-10%;bottom:0;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-1),transparent 70%)}
  .sec>.env{position:relative;z-index:2}

  /* ── volumen general ────────────────────────────
     Viñeta muy suave: oscurece los bordes del lienzo y hace que el centro
     avance. Es lo que da sensación de volumen sin tocar ningún componente. */
  body::before{
    content:"";position:fixed;inset:0;z-index:1;pointer-events:none;
    background:radial-gradient(ellipse 80% 70% at 50% 40%,transparent 45%,rgba(0,0,0,.42) 100%);
  }
  :root[data-theme="light"] body::before{
    background:radial-gradient(ellipse 80% 70% at 50% 40%,transparent 50%,rgba(8,32,72,.10) 100%);
  }
  @media (prefers-color-scheme:light){
    :root:not([data-theme="dark"]) body::before{
      background:radial-gradient(ellipse 80% 70% at 50% 40%,transparent 50%,rgba(8,32,72,.10) 100%);
    }
  }
  .aviso,.nav,.hero,.sec,.franja,.cierre,.pie,.notas{position:relative;z-index:2}

  /* ── las fichas dejan de ser celdas planas ────── */
  .ficha{background-image:linear-gradient(180deg,var(--brillo),transparent 60%)}
  .ficha:hover{
    background-image:linear-gradient(180deg,var(--brillo),transparent 70%);
    box-shadow:var(--alt-1);z-index:2;position:relative;
  }


  /* ════════════ v10 · SEPARADORES AFINADOS ════════════
     La versión anterior era una barra de 2 px a color pleno: llamaba más la
     atención que la línea que reemplazaba. Ahora son dos capas:
       1. un hairline de 1 px con el color a media opacidad
       2. un resplandor muy difuso debajo, que es lo que da la transición
     El degradado se percibe, pero no marca. */

  .sec-linea,.franja,.cierre{
    background-repeat:no-repeat,no-repeat;
    background-size:100% 1px,100% 140px;
    background-position:top left,top left;
  }

  #portafolio{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 42%,transparent) 34%,color-mix(in srgb,var(--azure) 42%,transparent) 66%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 10%,transparent),transparent 72%)}

  #seguridad{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 40%,transparent) 30%,color-mix(in srgb,var(--teal) 40%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--teal) 9%,transparent),transparent 72%)}

  .franja{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--teal) 38%,transparent) 30%,color-mix(in srgb,var(--accion) 38%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--accion) 8%,transparent),transparent 72%)}

  #porque{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--accion) 38%,transparent) 30%,color-mix(in srgb,var(--azure) 40%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 9%,transparent),transparent 72%)}

  .cierre{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 40%,transparent) 34%,color-mix(in srgb,var(--teal) 40%,transparent) 66%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 9%,transparent),transparent 72%)}

  /* Reserva para navegadores sin color-mix: hairline neutro, nunca una barra. */
  @supports not (background: color-mix(in srgb, red 50%, transparent)){
    .sec-linea,.franja,.cierre{
      background-image:linear-gradient(90deg,transparent,var(--line-2) 30%,var(--line-2) 70%,transparent);
      background-size:100% 1px;
    }
  }


  /* ════════════ v11 · SIN BORDES DUROS ════════════
     El problema no era el separador: era que la banda de fondo cambiaba de
     tono DE GOLPE, y ese corte recorría todo el ancho y además partía las
     tarjetas que flotan sobre él. Ahora la banda entra y sale con degradado
     sobre 180 px, así que no hay ninguna arista horizontal en la página. */

  .banda{background-color:transparent !important;background-image:none}

  #portafolio,#porque,.franja{
    background-color:transparent !important;
    background-repeat:no-repeat,no-repeat,no-repeat;
    background-size:100% 1px,100% 160px,100% 100%;
    background-position:top left,top left,top left;
  }

  /* Capa 1: apenas un susurro de línea. Capa 2: el resplandor de transición.
     Capa 3: la banda, desvanecida arriba y abajo. */
  #portafolio{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 22%,transparent) 34%,color-mix(in srgb,var(--azure) 22%,transparent) 66%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 9%,transparent),transparent 72%),
    linear-gradient(180deg,transparent 0,var(--banda) 180px,var(--banda) calc(100% - 180px),transparent 100%)}

  #porque{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--accion) 20%,transparent) 30%,color-mix(in srgb,var(--azure) 22%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 9%,transparent),transparent 72%),
    linear-gradient(180deg,transparent 0,var(--banda) 180px,var(--banda) calc(100% - 180px),transparent 100%)}

  .franja{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--teal) 20%,transparent) 30%,color-mix(in srgb,var(--accion) 22%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--accion) 8%,transparent),transparent 72%),
    linear-gradient(180deg,transparent 0,var(--banda) 180px,var(--banda) calc(100% - 180px),transparent 100%)}

  /* Las secciones sin banda tampoco llevan arista: solo el resplandor. */
  #seguridad,.cierre{
    background-color:transparent !important;
    background-repeat:no-repeat,no-repeat;
    background-size:100% 1px,100% 160px;
    background-position:top left,top left;
  }
  #seguridad{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 20%,transparent) 30%,color-mix(in srgb,var(--teal) 24%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--teal) 9%,transparent),transparent 72%)}
  .cierre{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 20%,transparent) 34%,color-mix(in srgb,var(--teal) 20%,transparent) 66%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 9%,transparent),transparent 72%)}

  /* El pie tampoco necesita una regla dura. */
  .pie{background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--line-2) 70%,transparent) 30%,color-mix(in srgb,var(--line-2) 70%,transparent) 70%,transparent)}

  /* Las tarjetas que flotan ya no quedan cortadas por ninguna arista. */
  #servicios .rejilla.tres .tarjeta{border-bottom-color:var(--line-2)}

  @supports not (background: color-mix(in srgb, red 50%, transparent)){
    #portafolio,#porque,.franja{
      background-image:linear-gradient(180deg,transparent 0,var(--banda) 180px,var(--banda) calc(100% - 180px),transparent 100%);
      background-size:100% 100%;
    }
    #seguridad,.cierre{background-image:none}
  }


  /* ════════════ v12 · LA ARISTA REAL ════════════
     Causa: la regla que da position:relative + z-index:2 a TODAS las secciones
     hace que cada una cree su propio contexto de apilamiento. Con el mismo
     z-index gana la posterior en el DOM, así que #portafolio se pintaba encima
     de las tarjetas que sobresalen de #servicios y las cortaba en seco.
     El z-index:4 de las tarjetas no servía: está encerrado dentro del contexto
     de su sección y no puede escapar de él.
     Arreglo: #servicios por encima de la sección que le sigue. */
  #servicios{z-index:6}
  #portafolio{z-index:2}

  /* Sin arista, el borde inferior de las tarjetas ya no necesita disimulo. */
  #servicios .rejilla.tres .tarjeta{border-bottom-color:var(--line)}


  /* ════════════ v13 · MOVIMIENTO ════════════
     Criterio: la audiencia es técnica y evalúa proveedores. El movimiento
     tiene que leerse como precisión, no como adorno. Nada rebota, nada gira
     por girar, y todo se apaga con prefers-reduced-motion. */

  /* ── 1. Barra de navegación que se condensa ─────
     Funcional antes que decorativa: el menú deja de ocupar espacio al bajar
     y el fondo se vuelve translúcido para no tapar el contenido. */
  .nav{
    position:sticky;top:0;
    background:color-mix(in srgb,var(--void) 72%,transparent);
    -webkit-backdrop-filter:saturate(160%) blur(14px);
    backdrop-filter:saturate(160%) blur(14px);
    transition:box-shadow .3s ease,background .3s ease;
  }
  .nav-int{transition:padding .3s cubic-bezier(.2,.75,.3,1)}
  .nav.compacta{box-shadow:0 1px 0 var(--line),0 10px 30px -18px rgba(0,0,0,.8)}
  .nav.compacta .nav-int{padding-top:var(--e3);padding-bottom:var(--e3)}
  @supports not (backdrop-filter: blur(1px)){ .nav{background:var(--void)} }

  /* ── 2. El titular sube desde detrás de una máscara ──
     Un solo gesto al cargar, con curva de salida larga. */
  h1.titular{overflow:hidden;padding-bottom:.08em}
  h1.titular>span{
    display:block;transform:translateY(108%);
    animation:emerger .95s cubic-bezier(.16,.84,.28,1) .12s forwards;
  }
  @keyframes emerger{to{transform:none}}

  /* ── 3. El diagrama se arma de abajo hacia arriba ──
     Primero el núcleo, luego las capas que se apoyan en él, y al final el
     flujo y el anillo de seguridad. Cuenta el orden real de una arquitectura. */
  .diagrama svg>g{opacity:0;animation:montar .6s cubic-bezier(.2,.75,.3,1) forwards}
  .diagrama svg>g:nth-of-type(3){animation-delay:.45s}   /* núcleo S/4HANA */
  .diagrama svg>g:nth-of-type(2){animation-delay:.62s}   /* BTP */
  .diagrama svg>g:nth-of-type(1){animation-delay:.79s}   /* Fiori */
  @keyframes montar{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

  .diagrama .flujo{opacity:0;animation:fluir 3.2s linear 1.05s infinite,surgir .5s ease 1.05s forwards}
  .diagrama .escudo,.diagrama .escudo-tit{opacity:0;animation:surgir .7s ease 1.2s forwards}
  .diagrama .escudo{animation:surgir .7s ease 1.2s forwards,girar 26s linear 1.2s infinite}
  .diagrama .nodo,.diagrama .nodo-pulso{opacity:0;animation:surgir .5s ease 1.35s forwards}
  .diagrama .nodo-pulso{animation:surgir .5s ease 1.35s forwards,latir 3s ease-out 1.35s infinite}
  @keyframes surgir{to{opacity:1}}

  /* ── 4. Deriva ambiental de los halos ───────────
     Muy lenta (18-26 s) y de poca amplitud: no se percibe como movimiento,
     se percibe como que la página está viva. */
  .hero::before{animation:deriva-h 22s ease-in-out infinite}
  @keyframes deriva-h{
    0%,100%{transform:translateX(-50%) scale(1)}
    50%    {transform:translateX(-52%) scale(1.07)}
  }
  .sec.teal::before{animation:deriva-t 26s ease-in-out infinite}
  @keyframes deriva-t{
    0%,100%{transform:translateX(-50%) scale(1)}
    50%    {transform:translateX(-47%) scale(1.09)}
  }
  .cierre::before{animation:deriva-c 24s ease-in-out infinite}
  @keyframes deriva-c{
    0%,100%{transform:translateX(-50%) scale(1)}
    50%    {transform:translateX(-53%) scale(1.06)}
  }

  /* ── 5. El filete del rótulo crece al aparecer ──
     Detalle mínimo que marca la entrada de cada sección. Mejora progresiva:
     sin JS la clase .rev no se aplica y el filete se ve completo. */
  .sec-cab.rev .kicker::before{width:0;transition:width .55s cubic-bezier(.2,.75,.3,1) .18s}
  .sec-cab.rev.visible .kicker::before{width:16px}

  /* ── 6. Micro-reacciones ────────────────────────*/
  .ico{transition:transform .22s cubic-bezier(.2,.75,.3,1)}
  .ficha:hover .ico,.tarjeta:hover .ico{transform:translateY(-3px)}
  .btn{transition:background .18s,border-color .18s,box-shadow .18s,transform .18s}
  .btn-p:active,.btn-g:active{transform:translateY(1px)}

  @media (prefers-reduced-motion:reduce){
    h1.titular>span{transform:none;animation:none}
    .diagrama svg>g,.diagrama .flujo,.diagrama .escudo,.diagrama .escudo-tit,
    .diagrama .nodo,.diagrama .nodo-pulso{opacity:1;animation:none}
    .hero::before,.sec.teal::before,.cierre::before{animation:none}
    .sec-cab.rev .kicker::before{width:16px;transition:none}
    .nav-int,.ico,.btn{transition:none}
  }


  /* ════════════ v14 · ANIMACIÓN LIGADA AL SCROLL ════════════
     Axity consigue su efecto con GSAP + ScrollTrigger y `scrub`: el avance de
     la animación va atado a la posición del scroll, no solo se dispara.

     Aquí se hace con `animation-timeline`, nativo de CSS. Motivo: la CSP del
     sitio es script-src 'self', así que un CDN queda bloqueado, y autoalojar
     GSAP + ScrollTrigger sería volver a meter ~70 KB de dependencia sin
     auditar justo después de haber borrado 709 KB de ellas.

     Donde no haya soporte, el revelado por IntersectionObserver sigue
     funcionando: el script detecta el soporte y no se pisan. */

  @supports (animation-timeline: view()) {

    /* Entrada con scrub: progresa mientras la sección sube por la pantalla. */
    .sec-cab,.rejilla,.fichas,.split-asim,.acciones-c,.cierre-int>*{
      animation:entrar-scrub linear both;
      animation-timeline:view();
      animation-range:entry 4% cover 34%;
    }
    @keyframes entrar-scrub{
      from{opacity:0;transform:translateY(30px) scale(.972)}
      to  {opacity:1;transform:none}
    }

    /* Las fichas entran escalonadas, cada una con su propio tramo de scroll. */
    .fichas .ficha{
      animation:ficha-scrub linear both;
      animation-timeline:view();
      animation-range:entry calc(2% + var(--i,0) * 2.5%) cover calc(26% + var(--i,0) * 2.5%);
    }
    @keyframes ficha-scrub{
      from{opacity:0;transform:translateY(22px)}
      to  {opacity:1;transform:none}
    }

    /* Paralaje de la retícula del hero: se desplaza a distinta velocidad. */
    .hero .malla{
      animation:malla-scrub linear both;
      animation-timeline:scroll(root);
      animation-range:0 100vh;
    }
    @keyframes malla-scrub{to{transform:translateY(70px);opacity:.25}}

    /* La cita se revela palabra por palabra, atada al scroll. Es el gesto
       equivalente al revelado de caracteres de Axity. */
    .franja blockquote .w{
      display:inline-block;
      animation:palabra-scrub linear both;
      animation-timeline:view();
      animation-range:entry calc(4% + var(--i,0) * 2.6%) entry calc(58% + var(--i,0) * 2.6%);
    }
    @keyframes palabra-scrub{
      from{opacity:.12;transform:translateY(14px)}
      to  {opacity:1;transform:none}
    }

    /* La barra de progreso deja de necesitar JavaScript. */
    .progreso span{
      width:100%;transform-origin:0 50%;transform:scaleX(0);transition:none;
      animation:progreso-scrub linear both;
      animation-timeline:scroll(root);
    }
    @keyframes progreso-scrub{to{transform:scaleX(1)}}

    /* El diagrama respira levemente al hacer scroll sobre el hero. */
    .diagrama{
      animation:diagrama-scrub linear both;
      animation-timeline:scroll(root);
      animation-range:0 80vh;
    }
    @keyframes diagrama-scrub{to{transform:translateY(-26px) scale(.985);opacity:.72}}
  }

  @media (prefers-reduced-motion:reduce){
    @supports (animation-timeline: view()){
      .sec-cab,.rejilla,.fichas,.split-asim,.acciones-c,.cierre-int>*,
      .fichas .ficha,.hero .malla,.franja blockquote .w,.diagrama{
        animation:none;opacity:1;transform:none;
      }
      .progreso span{animation:none;transform:scaleX(0)}
    }
  }


  /* ════════════ v15 · TRES CORRECCIONES ════════════ */

  /* 1. BARRA OPACA.
     Estaba al 72% con desenfoque de fondo. Donde el desenfoque no se aplica,
     el contenido se ve atravesándola. Ahora es sólida: el desenfoque queda
     solo como refinamiento, no como requisito para que se lea bien. */
  .nav{
    background:var(--void);
    -webkit-backdrop-filter:none;backdrop-filter:none;
    border-bottom:1px solid var(--line);
  }
  .nav.compacta{
    background:color-mix(in srgb,var(--void) 97%,transparent);
    -webkit-backdrop-filter:saturate(150%) blur(12px);
    backdrop-filter:saturate(150%) blur(12px);
    box-shadow:0 8px 28px -20px rgba(0,0,0,.9);
  }
  @supports not (backdrop-filter: blur(1px)){ .nav.compacta{background:var(--void)} }

  /* 2. RANGOS DE SCROLL QUE SÍ TERMINAN.
     Antes iban de `entry` a `cover`. La fase `cover` no se alcanza nunca para
     elementos cercanos al final del documento, así que se quedaban a medio
     revelar. Ahora empiezan y terminan dentro de `entry`: se completan en
     cuanto el elemento acaba de entrar en pantalla, siempre. */
  @supports (animation-timeline: view()) {
    .sec-cab,.rejilla,.fichas,.split-asim,.acciones-c,.cierre-int>*{
      animation-range:entry 0% entry 72%;
    }
    .fichas .ficha{
      animation-range:entry calc(2% + var(--i,0) * 3%) entry calc(62% + var(--i,0) * 3%);
    }
    .franja blockquote .w{
      animation-range:entry calc(2% + var(--i,0) * 2.4%) entry calc(56% + var(--i,0) * 2.4%);
    }
    .capas .capa{
      animation:ficha-scrub linear both;
      animation-timeline:view();
      animation-range:entry calc(2% + var(--i,0) * 3.5%) entry calc(60% + var(--i,0) * 3.5%);
    }
  }

  /* 3. CIBERSEGURIDAD CON OTRO TRATAMIENTO.
     Antes repetía la misma rejilla de tres columnas del portafolio SAP y por
     eso se leía como la misma sección dos veces. Ahora son capas de defensa:
     filas anchas apiladas, con el encabezado fijo a la izquierda mientras
     pasan. La forma dice lo que dice el contenido — defensa en profundidad. */
  .seg-grid{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr);
    gap:var(--e16);align-items:start}
  .seg-cab{position:sticky;top:96px}
  .seg-cab h2{margin-bottom:var(--e4)}
  .seg-cab p{color:var(--ink-2);margin:0;font-size:var(--t-md)}

  .capas{list-style:none;margin:0;padding:0}
  .capa{
    display:grid;grid-template-columns:auto auto 1fr;gap:var(--e5);
    align-items:start;padding:var(--e6) var(--e5) var(--e6) 0;
    border-top:1px solid var(--line);position:relative;
    transition:background .22s ease,padding-left .22s ease;
  }
  .capa:last-child{border-bottom:1px solid var(--line)}
  /* Rail izquierdo que se enciende: marca la capa activa. */
  .capa::before{
    content:"";position:absolute;left:0;top:-1px;bottom:-1px;width:2px;
    background:var(--teal);transform:scaleY(0);transform-origin:50% 0;
    transition:transform .28s cubic-bezier(.2,.75,.3,1);
  }
  .capa:hover{background:var(--brillo);padding-left:var(--e5)}
  .capa:hover::before{transform:scaleY(1)}
  .capa-n{font-family:var(--mono);font-size:var(--t-xs);color:var(--teal);
    letter-spacing:.08em;padding-top:.35em;min-width:2.2em}
  .capa .ico{color:var(--teal);margin:0;width:22px;height:22px;margin-top:.15em}
  .capa h4{font-size:var(--t-b);font-weight:800;letter-spacing:-.025em;margin:0 0 var(--e1)}
  .capa p{font-size:var(--t-sm);color:var(--ink-2);margin:0}

  @media (max-width:900px){
    .seg-grid{grid-template-columns:1fr;gap:var(--e10)}
    .seg-cab{position:static}
    .capa{grid-template-columns:auto 1fr;gap:var(--e4)}
    .capa .ico{display:none}
  }


  /* ════════════ v16 · APILAMIENTO Y DIAGRAMA ════════════ */

  /* 1. LA BARRA POR ENCIMA DE TODO.
     Una regla mía metió .nav en el grupo que recibe z-index:2, y luego
     #servicios subió a 6 para que las tarjetas no quedaran cortadas. Resultado:
     la barra era sólida pero quedaba DEBAJO del contenido, y el titular, el
     diagrama y las tarjetas se pintaban encima de ella.
     Va a 40: sobre todo el contenido y bajo la barra de progreso (50). */
  .nav{z-index:40}
  .aviso{z-index:41}

  /* 2. EL DIAGRAMA, VISIBLE.
     a) Se quita el desvanecido por scroll: reducía la opacidad al bajar y era
        justo lo que impedía verlo.
     b) Los trazos iban a rgba(255,255,255,.15) — demasiado tenues para leerse
        sobre el fondo. Se suben y el núcleo se marca con el azul de marca. */
  @supports (animation-timeline: view()){
    .diagrama{animation:none;transform:none;opacity:1}
  }

  .cap-caja{
    fill:var(--panel);
    stroke:color-mix(in srgb,var(--azure) 34%,transparent);
    stroke-width:1.25;
  }
  .cap-caja.core{stroke:var(--azure);stroke-width:1.6}
  .cap-sub{fill:var(--ink-2);font-weight:700}
  .cap-pt{fill:var(--azure)}
  .flujo{stroke-width:1.8;opacity:1}
  .escudo{stroke:var(--teal);stroke-width:1.5;opacity:.95}
  .escudo-tit{fill:var(--teal);font-weight:800}
  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    .cap-caja{stroke:rgba(255,255,255,.3)}
  }

  /* Un resplandor detrás del diagrama lo despega del fondo. */
  .diagrama::before{
    content:"";position:absolute;inset:-8%;z-index:-1;pointer-events:none;
    background:radial-gradient(ellipse 50% 50% at 55% 45%,
      color-mix(in srgb,var(--azure) 14%,transparent),transparent 70%);
    filter:blur(18px);
  }
  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    .diagrama::before{background:radial-gradient(ellipse 50% 50% at 55% 45%,rgba(77,155,255,.14),transparent 70%)}
  }


  /* ════════════ v17 · DIAGRAMA CONCÉNTRICO ════════════
     Los rectángulos apilados eran la forma más obvia y desaprovechaban el
     espacio. Esta versión dice lo mismo con otra geometría: el núcleo al
     centro, las capacidades orbitándolo y el perímetro de seguridad
     envolviéndolo todo. Se lee de dentro hacia fuera, que es el orden en que
     se piensa una arquitectura. */

  .dg-guia{fill:none;stroke:var(--line-2);stroke-width:1;opacity:.5}
  .dg-guia-2{stroke-dasharray:2 6;opacity:.35}

  .dg-perim circle{
    fill:none;stroke:var(--teal);stroke-width:1.5;
    stroke-dasharray:4 9;opacity:.9;transform-origin:220px 200px;
    animation:dg-girar 34s linear infinite;
  }
  @keyframes dg-girar{to{transform:rotate(360deg)}}
  .dg-perim-tit{fill:var(--teal);font-size:10.5px;font-weight:800;
    letter-spacing:.16em;text-transform:uppercase}

  .dg-link{stroke:var(--azure);stroke-width:1.5;stroke-linecap:round;
    stroke-dasharray:4 7;opacity:.85;animation:dg-fluir 3.4s linear infinite}
  @keyframes dg-fluir{to{stroke-dashoffset:-22}}

  .dg-core-c{
    fill:var(--panel);stroke:var(--azure);stroke-width:1.75;
    filter:drop-shadow(0 0 16px color-mix(in srgb,var(--azure) 45%,transparent));
  }
  .dg-sub{fill:var(--ink-3);font-size:9.5px;font-weight:700;
    letter-spacing:.14em;text-transform:uppercase}
  .dg-tit{fill:var(--ink);font-size:15px;font-weight:800;letter-spacing:-.02em}

  .dg-nodo{fill:var(--azure);stroke:var(--void);stroke-width:3}
  .dg-lbl{fill:var(--ink-2);font-size:11.5px;font-weight:700;letter-spacing:-.01em}

  /* Un orbe recorre el perímetro: única pieza en movimiento continuo, lenta. */
  .dg-orbe{fill:var(--teal);transform-origin:220px 200px;
    filter:drop-shadow(0 0 8px var(--teal));
    animation:dg-orbita 18s linear infinite}
  @keyframes dg-orbita{to{transform:rotate(360deg)}}

  /* Montaje al cargar: de dentro hacia fuera. */
  .dg-core{opacity:0;transform-origin:220px 200px;
    animation:dg-nucleo .7s cubic-bezier(.2,.8,.3,1) .25s forwards}
  @keyframes dg-nucleo{from{opacity:0;transform:scale(.82)}to{opacity:1;transform:none}}

  .dg-links{opacity:0;animation:dg-ap .55s ease .6s forwards}
  .dg-n{opacity:0;transform-origin:220px 200px;
    animation:dg-nodo-ap .5s cubic-bezier(.2,.8,.3,1) forwards;
    animation-delay:calc(.78s + var(--k,0) * .11s)}
  @keyframes dg-nodo-ap{from{opacity:0;transform:scale(.7)}to{opacity:1;transform:none}}
  .dg-perim,.dg-perim-tit,.dg-orbe{opacity:0;animation:dg-ap .8s ease 1.25s forwards}
  .dg-fondo{opacity:0;animation:dg-ap .8s ease .5s forwards}
  @keyframes dg-ap{to{opacity:1}}

  .dg-perim{animation:dg-ap .8s ease 1.25s forwards}
  .dg-orbe{animation:dg-ap .8s ease 1.25s forwards,dg-orbita 18s linear 1.25s infinite}

  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    .dg-core-c{filter:drop-shadow(0 0 16px rgba(77,155,255,.45))}
  }

  @media (prefers-reduced-motion:reduce){
    .dg-perim circle,.dg-link,.dg-orbe,.dg-core,.dg-links,.dg-n,
    .dg-perim,.dg-perim-tit,.dg-fondo{animation:none;opacity:1;transform:none}
  }


  /* ════════════ v18 · REJILLA SIN HUECOS + DIAGRAMA VIVO ════════════ */

  /* 1. SIETE SERVICIOS EN TRES COLUMNAS DEJABAN DOS CELDAS VACÍAS.
     Un 7 no cabe en múltiplos de 3. En vez de rellenar con aire, dos fichas
     pasan a doble ancho: 5 sencillas + 2 dobles = 9 celdas exactas. De paso
     la rejilla deja de ser uniforme y gana jerarquía: la que abre el
     portafolio y la que lo cierra son las que respiran. */
  #portafolio .ficha:nth-child(1),
  #portafolio .ficha:nth-child(7){grid-column:span 2}

  /* Las dobles se tratan como destacadas, no como celdas estiradas. */
  #portafolio .ficha:nth-child(1),
  #portafolio .ficha:nth-child(7){
    padding:var(--e10);
    background-image:linear-gradient(135deg,var(--brillo),transparent 55%);
  }
  #portafolio .ficha:nth-child(1) h4,
  #portafolio .ficha:nth-child(7) h4{font-size:var(--t-lg);letter-spacing:-.03em}
  #portafolio .ficha:nth-child(1) p,
  #portafolio .ficha:nth-child(7) p{max-width:46ch}
  #portafolio .ficha:nth-child(1) .ico,
  #portafolio .ficha:nth-child(7) .ico{width:26px;height:26px}

  @media (max-width:900px){
    #portafolio .ficha:nth-child(1),
    #portafolio .ficha:nth-child(7){grid-column:span 2;padding:var(--e8)}
  }
  @media (max-width:560px){
    #portafolio .ficha:nth-child(1),
    #portafolio .ficha:nth-child(7){grid-column:span 1}
  }

  /* 2. EL DIAGRAMA, CON VIDA.
     Estaba montado y quieto. Ahora hay actividad continua pero lenta: pulsos
     que salen de cada nodo, el núcleo respirando, dos órbitas a distinta
     velocidad y en sentidos opuestos. Nada rápido — sugiere un sistema en
     operación, no una animación reclamando atención. */

  .dg-pulso{
    fill:var(--azure);transform-box:fill-box;transform-origin:center;
    animation:dg-pulso 3.6s ease-out infinite;
    animation-delay:calc(1.4s + var(--k,0) * .85s);
    opacity:0;
  }
  @keyframes dg-pulso{
    0%  {transform:scale(1);opacity:.5}
    70% {transform:scale(3.4);opacity:0}
    100%{transform:scale(3.4);opacity:0}
  }

  /* El núcleo respira: el halo se abre y se cierra en 5 s. */
  .dg-core-c{animation:dg-respirar 5.5s ease-in-out infinite}
  @keyframes dg-respirar{
    0%,100%{filter:drop-shadow(0 0 12px color-mix(in srgb,var(--azure) 38%,transparent))}
    50%    {filter:drop-shadow(0 0 26px color-mix(in srgb,var(--azure) 62%,transparent))}
  }

  /* El círculo guía interior gira al revés que el perímetro: dos velocidades
     opuestas dan sensación de profundidad. */
  .dg-guia-2{transform-origin:220px 200px;animation:dg-contra 28s linear infinite}
  @keyframes dg-contra{to{transform:rotate(-360deg)}}

  /* Segundo orbe, en la órbita interior y en sentido contrario. */
  .dg-orbe-2{
    fill:var(--azure);transform-origin:220px 200px;
    filter:drop-shadow(0 0 7px var(--azure));
    opacity:0;animation:dg-ap .8s ease 1.4s forwards,dg-orbita-inv 13s linear 1.4s infinite;
  }
  @keyframes dg-orbita-inv{to{transform:rotate(-360deg)}}

  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    .dg-core-c{animation:none}
  }

  @media (prefers-reduced-motion:reduce){
    .dg-pulso,.dg-core-c,.dg-guia-2,.dg-orbe-2{animation:none;opacity:1;transform:none}
    .dg-pulso{opacity:0}
  }


  /* ════════════ v19 · GRAFO EN PERSPECTIVA, RITMO DE ALINEACIÓN ════════════ */

  /* 1. DIAGRAMA COMO GRAFO CON PROFUNDIDAD.
     Dos planos orbitales inclinados (elipses = círculos vistos en escorzo) y
     el núcleo elevándose entre ellos. Las aristas son curvas, no radios: eso
     es lo que lo hace leer como grafo y no como diana. */
  .dg-suelo{
    fill:radial-gradient(none);fill:var(--azure);opacity:.05;
    filter:blur(22px);
  }
  .dg-plano{fill:none;stroke:var(--line-2);stroke-width:1;opacity:.55}
  .dg-plano-alto{stroke-dasharray:3 6;opacity:.4;
    transform-origin:220px 152px;animation:dg-contra 30s linear infinite}
  .dg-eje{stroke:var(--azure);stroke-width:1;opacity:.3;stroke-dasharray:2 5}

  .dg-arista{
    fill:none;stroke:var(--azure);stroke-width:1.4;stroke-linecap:round;
    stroke-dasharray:4 8;opacity:.8;animation:dg-fluir 3.6s linear infinite;
  }
  .dg-arista:nth-child(2){animation-delay:-.9s}
  .dg-arista:nth-child(3){animation-delay:-1.8s}
  .dg-arista:nth-child(4){animation-delay:-2.7s}

  .dg-perim ellipse{
    fill:none;stroke:var(--teal);stroke-width:1.5;stroke-dasharray:4 9;
    opacity:.85;transform-origin:220px 258px;animation:dg-girar 40s linear infinite;
  }
  .dg-perim-tit{fill:var(--teal);font-size:10px;font-weight:800;
    letter-spacing:.15em;text-transform:uppercase}

  /* Base elíptica bajo el núcleo: lo apoya en el plano y vende la perspectiva. */
  .dg-core-base{fill:var(--azure);opacity:.18;filter:blur(3px)}
  .dg-core-c{fill:var(--panel);stroke:var(--azure);stroke-width:1.9}
  .dg-orbe{transform-origin:220px 258px}

  @media (max-width:900px){ .dg-lbl{font-size:12.5px} }

  /* 2. RITMO DE ALINEACIÓN.
     Toda la página venía alineada a la izquierda y eso cansa. Dos encabezados
     pasan al centro, de modo que la lectura alterna: izquierda, centro,
     columna fija, centro, centro. */
  #portafolio .sec-cab,#porque .sec-cab{
    max-width:56ch;margin-left:auto;margin-right:auto;text-align:center;
  }
  #portafolio .kicker,#porque .kicker{justify-content:center;display:flex;align-items:center}
  #porque .acciones-c{justify-content:center}

  /* 3. LA FRANJA, CON OTRA DISTRIBUCIÓN.
     Antes: cita a la izquierda, pie debajo, todo en bloque. Ahora es una
     composición a dos columnas — la cita ocupa el ancho principal y el pie
     baja a la derecha, apoyado en una regla vertical. */
  .franja-grid{
    display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,.65fr);
    gap:var(--e16);align-items:end;
  }
  .franja blockquote{max-width:none}
  .franja-lado{
    border-left:1px solid var(--line-2);padding-left:var(--e8);
    padding-bottom:var(--e2);
  }
  .franja-lado .marca-cita{margin-bottom:var(--e5)}
  .franja figcaption{margin:0;font-size:var(--t-b);color:var(--ink-2);max-width:34ch}

  @media (max-width:900px){
    .franja-grid{grid-template-columns:1fr;gap:var(--e8);align-items:start}
    .franja-lado{border-left:0;border-top:1px solid var(--line-2);
      padding-left:0;padding-top:var(--e6)}
    #portafolio .sec-cab,#porque .sec-cab{text-align:left;margin-left:0}
    #portafolio .kicker,#porque .kicker{justify-content:flex-start}
    #porque .acciones-c{justify-content:flex-start}
  }

  /* ── correcciones de la perspectiva ──────────────
     a) Rotar una elipse la hace VOLTEAR, no recorrer su borde: se veía
        tambaleando. El movimiento pasa a viajar por el trazo discontinuo.
     b) El orbe rotado trazaba un círculo de radio 66, metiéndose dentro de la
        elipse. Ahora sigue el contorno real con offset-path.
     c) Los orígenes de transformación apuntaban al centro viejo (200), no al
        nuevo plano (258). */

  .dg-perim ellipse{animation:dg-recorrer 7s linear infinite}
  @keyframes dg-recorrer{to{stroke-dashoffset:-26}}
  .dg-plano-alto{animation:dg-recorrer 9s linear infinite reverse}

  .dg-orbe{
    offset-path:path('M 24 258 a 196 66 0 1 0 392 0 a 196 66 0 1 0 -392 0');
    offset-anchor:center;offset-rotate:0deg;
    animation:dg-ap .8s ease 1.25s forwards,dg-recorrido 22s linear 1.25s infinite;
  }
  @keyframes dg-recorrido{to{offset-distance:100%}}
  @supports not (offset-path: path('M0 0 L1 1')){ .dg-orbe{display:none} }

  .dg-core{transform-origin:220px 258px}
  .dg-n{transform-origin:220px 258px}

  /* Montaje escalonado con los grupos del diagrama nuevo. */
  .dg-suelo{opacity:0;animation:dg-suelo-ap 1s ease .35s forwards}
  @keyframes dg-suelo-ap{to{opacity:.05}}
  .dg-planos{opacity:0;animation:dg-ap .7s ease .45s forwards}
  .dg-eje{opacity:0;animation:dg-eje-ap .6s ease .8s forwards}
  @keyframes dg-eje-ap{to{opacity:.3}}
  .dg-aristas{opacity:0;animation:dg-ap .6s ease .95s forwards}

  @media (prefers-reduced-motion:reduce){
    .dg-perim ellipse,.dg-plano-alto,.dg-orbe,.dg-arista,
    .dg-suelo,.dg-planos,.dg-eje,.dg-aristas{animation:none;opacity:1}
    .dg-suelo{opacity:.05}
    .dg-eje{opacity:.3}
  }


  /* ════════════ v20 · CAMPO GENERATIVO ════════════
     Sustituye al diagrama. No explica nada: es textura viva. Una malla de
     puntos con onda lenta que se agranda y vira al verde azulado cerca del
     cursor. Va en canvas porque son ~360 puntos repintados por fotograma;
     en SVG serían 360 nodos del DOM. */
  .campo{
    position:relative;width:100%;max-width:460px;margin-left:auto;
    aspect-ratio:11/10;
  }
  .campo canvas{position:absolute;inset:0;width:100%;height:100%;display:block}

  /* Resplandor detrás del campo, para que no flote sobre la nada. */
  .campo::before{
    content:"";position:absolute;inset:-12%;pointer-events:none;
    background:radial-gradient(ellipse 46% 46% at 52% 48%,
      color-mix(in srgb,var(--azure) 16%,transparent),transparent 72%);
    filter:blur(24px);
  }
  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    .campo::before{background:radial-gradient(ellipse 46% 46% at 52% 48%,rgba(77,155,255,.16),transparent 72%)}
  }

  .campo{opacity:0;animation:campo-ap 1.1s ease .35s forwards}
  @keyframes campo-ap{to{opacity:1}}

  @media (max-width:980px){ .campo{margin:0 auto;max-width:420px} }
  @media (prefers-reduced-motion:reduce){ .campo{opacity:1;animation:none} }


  /* ════════════ v21 · LA MALLA COMO FONDO DE TODA LA PÁGINA ════════════ */

  /* Canvas fijo al viewport, no al documento: así el número de puntos depende
     del tamaño de la ventana y no del largo de la página. En una portada de
     6000 px de alto, un canvas del documento entero serían decenas de miles de
     puntos; fijo son ~2.600 y no crece nunca. */
  .campo{
    position:fixed;inset:0;z-index:0;pointer-events:none;
    width:100%;max-width:none;margin:0;aspect-ratio:auto;
    opacity:0;animation:campo-ap 1.4s ease .3s forwards;
  }
  .campo::before{display:none}
  .campo canvas{position:absolute;inset:0;width:100%;height:100%}

  /* El hero pasa a una sola columna: el hueco de la derecha ya no hace falta
     porque la malla ocupa el fondo entero. El titular gana tamaño. */
  .hero-grid{display:block}
  .hero h1.titular{max-width:18ch}
  .hero .entrada{max-width:58ch}

  /* Las bandas se vuelven semitransparentes para que la malla siga viéndose
     debajo. Si quedaran opacas, el fondo desaparecería en media página y el
     efecto se leería como un error. */
  #portafolio,#porque,.franja{
    background-image:
      linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 22%,transparent) 34%,color-mix(in srgb,var(--azure) 22%,transparent) 66%,transparent),
      radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--azure) 9%,transparent),transparent 72%),
      linear-gradient(180deg,transparent 0,color-mix(in srgb,var(--banda) 86%,transparent) 180px,color-mix(in srgb,var(--banda) 86%,transparent) calc(100% - 180px),transparent 100%);
  }
  #seguridad{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 20%,transparent) 30%,color-mix(in srgb,var(--teal) 24%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--teal) 9%,transparent),transparent 72%)}
  .franja{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--teal) 20%,transparent) 30%,color-mix(in srgb,var(--accion) 22%,transparent) 70%,transparent),
    radial-gradient(ellipse 55% 100% at 50% 0%,color-mix(in srgb,var(--accion) 8%,transparent),transparent 72%),
    linear-gradient(180deg,transparent 0,color-mix(in srgb,var(--banda) 86%,transparent) 180px,color-mix(in srgb,var(--banda) 86%,transparent) calc(100% - 180px),transparent 100%)}

  /* Las notas y el pie sí tapan: ahí ya no aporta. */
  .notas{background-color:var(--panel)}

  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    #portafolio,#porque,.franja{background-color:var(--banda) !important}
  }

  @media (max-width:980px){ .campo{margin:0} }


  /* ════════════ v22 · FUERA LAS BANDAS ════════════
     Diagnóstico: la línea reaparecía porque las secciones con banda cambian el
     tono del fondo, y el borde de ese cambio se percibe aunque el degradado
     sea largo — un degradado lineal tiene un quiebre de pendiente en sus
     paradas y el ojo lo detecta (bandas de Mach). Suavizarlo más solo lo
     desplazaba.

     Con la malla generativa cubriendo la página, las bandas ya no aportan
     textura: solo aportaban el problema. Se eliminan. La diferencia entre
     secciones queda a cargo de los halos y de los separadores, que no tienen
     borde porque nacen y mueren en transparente. */

  #portafolio,#porque,.franja{
    background-color:transparent !important;
    background-repeat:no-repeat,no-repeat;
    background-size:100% 1px,100% 160px;
    background-position:top left,top left;
  }
  #portafolio{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--azure) 24%,transparent) 34%,color-mix(in srgb,var(--azure) 24%,transparent) 66%,transparent),
    radial-gradient(ellipse 60% 100% at 50% 0%,color-mix(in srgb,var(--azure) 10%,transparent),transparent 74%)}
  #porque{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--accion) 20%,transparent) 30%,color-mix(in srgb,var(--azure) 24%,transparent) 70%,transparent),
    radial-gradient(ellipse 60% 100% at 50% 0%,color-mix(in srgb,var(--azure) 10%,transparent),transparent 74%)}
  .franja{background-image:
    linear-gradient(90deg,transparent,color-mix(in srgb,var(--teal) 20%,transparent) 30%,color-mix(in srgb,var(--accion) 24%,transparent) 70%,transparent),
    radial-gradient(ellipse 60% 100% at 50% 0%,color-mix(in srgb,var(--accion) 9%,transparent),transparent 74%)}

  /* Para compensar la pérdida de las bandas, cada sección refuerza su halo
     ambiental: el ritmo pasa a ser de luz, no de bloques de color. */
  #servicios::after,#portafolio::after,#porque::after{opacity:.85;height:640px}

  /* Las notas son el apéndice de la propuesta, no parte del sitio: ahí sí
     conviene un corte claro, pero se suaviza con un degradado de entrada. */
  .notas{
    background-color:transparent;
    background-image:linear-gradient(180deg,transparent 0,var(--panel) 140px,var(--panel) 100%);
    background-repeat:no-repeat;background-size:100% 100%;
  }

  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    #portafolio,#porque,.franja{background-image:none}
    .notas{background-color:var(--panel);background-image:none}
  }


  /* ════════════ v23 · LA LÍNEA, MEDIDA Y ELIMINADA ════════════
     Se midió sobre la captura, en una columna de fondo fuera del contenido
     (x=40..360). Dentro de la página había UNA sola discontinuidad:

         y=109   brillo 13.0 -> 15.5

     Justo bajo la barra. No eran las bandas de sección: era que la barra
     tiene fondo OPACO y la malla se ve por debajo. El borde entre "malla
     tapada" y "malla visible" es la línea, reforzada por su border-bottom.

     Arreglo: arriba del todo la barra es transparente y la malla la
     atraviesa sin corte. Solo al bajar se vuelve sólida —que es cuando hace
     falta, porque entonces sí pasa contenido por debajo. */

  .nav{
    background:transparent;
    border-bottom:0;
    box-shadow:none;
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }

  .nav.compacta{
    background:color-mix(in srgb,var(--void) 90%,transparent);
    -webkit-backdrop-filter:saturate(150%) blur(14px);
    backdrop-filter:saturate(150%) blur(14px);
    border-bottom:1px solid var(--line);
    box-shadow:0 10px 30px -22px rgba(0,0,0,.9);
  }
  @supports not (backdrop-filter: blur(1px)){
    .nav.compacta{background:var(--void)}
  }

  /* El aviso de propuesta también cortaba la malla: se hace translúcido. */
  .aviso{
    background:color-mix(in srgb,var(--panel) 78%,transparent);
    -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
    border-bottom:0;
  }
  @supports not (backdrop-filter: blur(1px)){ .aviso{background:var(--panel)} }


  /* ════════════ v24 · SEPARADORES FUERA ════════════
     Medición sobre la captura, ahora en tres columnas a la vez. Un cambio de
     tono a todo lo ancho aparece en las tres:

         y=544   brillo 11.9 -> 13.2   (margen izq. y der. por igual)

     Es la franja de resplandor de 160 px que puse en el borde superior de cada
     sección. Aunque el salto es de ~1% de brillo, sobre un fondo oscuro y
     plano el ojo lo lee como una barra horizontal — que es exactamente lo que
     se venía señalando.

     Se eliminan el filete y el resplandor de borde. La separación entre
     secciones queda a cargo del espacio en blanco y de los halos ambientales,
     que son elipses grandes, desenfocadas y descentradas: no tienen borde
     horizontal que detectar. */

  #portafolio,#seguridad,#porque,.franja,.cierre,.sec-linea,.pie{
    background-image:none !important;
    border-top:0 !important;
  }

  /* Los halos ambientales se refuerzan un poco para compensar: el ritmo entre
     secciones ahora es solo de luz. */
  #servicios::after,#portafolio::after,#porque::after{opacity:1;height:700px}

  /* El halo de ciberseguridad se descentra y se agranda, para que no vuelva a
     leerse como una banda centrada en el borde. */
  .sec.teal::before{
    top:-18%;left:38%;width:min(1300px,150vw);height:820px;
    background:radial-gradient(ellipse 44% 42% at 50% 50%,var(--teal-halo),transparent 72%);
  }

  /* Más aire entre secciones, ya que no hay línea que las separe. */
  .sec{padding:calc(var(--e28) + var(--e6)) 0}
  .franja .env{padding-top:var(--e28);padding-bottom:var(--e28)}

  @media (max-width:900px){ .sec{padding:var(--e20) 0} }


  /* ════════════ v25 · HALOS SIN RECORTAR ════════════
     Causa de la línea entre la franja y «Por qué BlueNova»: `.franja` tiene
     overflow:hidden, así que su halo —una elipse desenfocada de 600 px— se
     corta EN SECO en el borde de la sección. Ese corte recto es la línea.

     Le pasaba igual al hero, al cierre y a ciberseguridad: las cuatro
     secciones con halo propio lo estaban recortando.

     El overflow:hidden estaba ahí para evitar scroll horizontal, pero `body`
     ya declara overflow-x:hidden, así que era redundante desde el principio.
     Se quita en las cuatro. Se conserva donde SÍ hace falta: en `.fichas`
     (recorta las celdas al radio del contenedor), en `.sprite` y en el
     titular (la máscara del revelado depende de él). */

  .hero,.cierre,.sec.teal,.franja{overflow:visible}

  /* Con los halos ya sin recortar, se les da un desvanecido propio en los
     bordes para que ni siquiera dependan de dónde termine su sección. */
  .hero::before,.sec.teal::before,.cierre::before,.franja::before{
    -webkit-mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 35%,transparent 78%);
    mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 35%,transparent 78%);
  }
  #servicios::after,#portafolio::after,#porque::after{
    -webkit-mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 30%,transparent 76%);
    mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 30%,transparent 76%);
  }

  /* El apéndice tampoco lleva borde: entra con un desvanecido de 160 px. */
  .notas{
    border-top:0 !important;
    background-color:transparent !important;
    background-image:linear-gradient(180deg,transparent 0,var(--panel) 160px,var(--panel) 100%) !important;
    background-repeat:no-repeat;background-size:100% 100%;
  }


  /* ════════════ v26 · LOS BORDES QUE FALTABAN ════════════
     Medición fina sobre la captura: ambas líneas son de EXACTAMENTE 1 píxel,
     brillo 30-31 sobre un fondo de 12. No eran degradados ni halos recortados:
     son bordes reales.

         y=500  ->  .franja { border-bottom: 1px solid var(--line) }
         y=115  ->  .nav    { border-bottom: 1px solid var(--line) }

     Mi auditoría anterior no los vio porque solo comprobé `border-top`.
     Aquí se anulan TODOS los bordes horizontales de elementos a todo lo ancho,
     de una vez, para no volver a dejarme ninguno. */

  .hero,.sec,#servicios,#portafolio,#seguridad,#porque,
  .franja,.cierre,.pie,.notas,.nav,.aviso{
    border-top:0 !important;
    border-bottom:0 !important;
  }

  /* La barra, al bajar, se separa con sombra difusa en vez de con un filo. */
  .nav.compacta{
    box-shadow:0 14px 34px -26px rgba(0,0,0,.95);
  }

  /* ── el botón del menú ──────────────────────────
     `.menu a` tiene especificidad (0,1,1) y `.btn-p` solo (0,1,0), así que el
     color del texto del menú le ganaba al del botón: se veía lavado sobre el
     ámbar. Ahora el botón de Contacto es idéntico al de Solicitar diagnóstico,
     en fondo y en texto. */
  .nav .menu a.btn-p,
  .nav .menu a.btn-p:hover,
  .nav .menu a.btn-p:focus-visible{
    color:#0B0800;
    font-weight:800;
  }
  .nav .menu a.btn-p{background:var(--accion)}
  .nav .menu a.btn-p:hover,
  .nav .menu a.btn-p:focus-visible{background:var(--accion-h)}

  .nav.compacta{border-bottom:0 !important;border-top:0 !important}


  /* ════════════ v27 · RITMO DE ALINEACIÓN ════════════
     Cada sección recibe un tratamiento distinto del eje horizontal, de modo
     que la vista no caiga siempre en el mismo punto al bajar:

       hero          izquierda, anclando la página
       01 servicios  encabezado PARTIDO: título izquierda, entradilla derecha
       02 portafolio centrado
       03 seguridad  columna fija a la izquierda, filas a la derecha
       franja        cita a la izquierda, pie descolgado a la derecha
       04 por qué    bloque DESPLAZADO a la derecha
       cierre        centrado
       pie           rejilla a la izquierda

     El texto nunca se alinea a la derecha: se desplaza el bloque. Alinear
     párrafos a la derecha en español entorpece la lectura porque el ojo pierde
     el punto de retorno de cada línea. */

  /* ── 01 · encabezado partido ─────────────────── */
  #servicios .sec-cab{
    max-width:none;
    display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
    gap:var(--e6) var(--e16);align-items:end;
  }
  #servicios .sec-cab .kicker{grid-column:1 / -1;margin-bottom:0}
  #servicios .sec-cab h2{margin:0}
  #servicios .sec-cab p{margin:0;padding-bottom:.4em}

  /* ── 04 · bloque desplazado a la derecha ─────── */
  #porque .sec-cab{
    text-align:left;
    max-width:54ch;
    margin-left:auto;margin-right:0;
  }
  #porque .kicker{justify-content:flex-start}
  #porque .acciones-c{justify-content:flex-end}
  /* Las dos tarjetas invierten el desfase para acompañar el desplazamiento. */
  #porque .split-asim>*:nth-child(1){margin-top:var(--e16)}
  #porque .split-asim>*:nth-child(2){margin-top:0}

  /* ── 02 · el centrado se refuerza ────────────── */
  #portafolio .sec-cab{margin-left:auto;margin-right:auto;text-align:center;max-width:54ch}

  @media (max-width:900px){
    #servicios .sec-cab{display:block}
    #servicios .sec-cab h2{margin-bottom:var(--e4)}
    #porque .sec-cab{margin-right:auto;margin-left:0}
    #porque .acciones-c{justify-content:flex-start}
    #porque .split-asim>*:nth-child(1),
    #porque .split-asim>*:nth-child(2){margin-top:0}
    #portafolio .sec-cab{text-align:left;margin-left:0}
  }


  /* ════════════ v28 · «Por qué» vuelve a la izquierda ════════════
     El bloque desplazado a la derecha no funcionaba: se vuelve a alinear a la
     izquierda. El ritmo horizontal se mantiene con el resto —encabezado
     partido en servicios, centrado en portafolio, columna fija en seguridad,
     descolgado en la franja y centrado en el cierre—, que ya da variación
     suficiente sin forzar esta sección. */

  #porque .sec-cab{
    text-align:left;
    max-width:60ch;
    margin-left:0;
    margin-right:auto;
  }
  #porque .kicker{justify-content:flex-start;display:flex;align-items:center}
  #porque .acciones-c{justify-content:flex-start}

  /* Las tarjetas recuperan su desfase original: la segunda baja. */
  #porque .split-asim>*:nth-child(1){margin-top:0}
  #porque .split-asim>*:nth-child(2){margin-top:var(--e16)}

  @media (max-width:900px){
    #porque .split-asim>*:nth-child(2){margin-top:0}
  }



/* ══════════════════ 2 · PÁGINAS ══════════════════ */
  /* ════════════ SITIO NAVEGABLE · capa nueva ════════════
     Todo lo de arriba es v28 sin tocar. Aquí solo va lo que las páginas
     interiores necesitan y la portada no tenía: enrutado, hero corto,
     migas, formulario, texto legal largo.

     Regla que me impuse: NINGUNA de estas reglas redefine un componente
     existente. Si una página interior necesita una tarjeta, usa .tarjeta;
     si necesita una fila de capacidad, usa .capa. Lo que se añade son
     contenedores y variantes por clase — nunca por id, porque los ids
     (#servicios, #portafolio, #seguridad, #porque) ya están tomados por la
     portada y deben seguir siendo únicos en el documento. */

  /* ── enrutado ────────────────────────────────────
     Todas las páginas viven en el mismo documento y solo una está visible.
     display:none, no visibility: así las animaciones ligadas al scroll de la
     página oculta no consumen nada y arrancan limpias al mostrarse. */

  /* Estado activo del menú. Nunca solo por color: lleva además un filete. */
  .menu a:not(.btn){position:relative}
  .menu a:not(.btn)[aria-current="page"]{color:var(--ink)}
  .menu a:not(.btn)[aria-current="page"]::after{
    content:"";position:absolute;left:0;right:0;bottom:-6px;height:1px;
    background:var(--azure);
  }
  .menu a.btn[aria-current="page"]{box-shadow:0 0 0 2px var(--accion-h),0 10px 30px -12px rgba(255,176,32,.7)}

  /* ── hero de página interior ─────────────────────
     El de la portada ocupa una pantalla entera y eso está bien una sola vez.
     En una página de servicio, el contenido tiene que empezar antes. */
  .hero-int{padding:calc(var(--e28) + var(--e2)) 0 var(--e16)}
  .hero-int h1{font-size:clamp(2.25rem,5.2vw,3.5rem);max-width:22ch;margin-bottom:var(--e5)}
  .hero-int .entrada{margin-bottom:var(--e8);max-width:56ch}
  .hero-int.teal .eyebrow{color:var(--teal);}
  .hero-int.teal .eyebrow i{background:var(--teal);box-shadow:0 0 8px 1px var(--teal)}
  @media (max-width:900px){ .hero-int{padding:var(--e20) 0 var(--e12)} }

  /* Migas: orientan en un sitio de 18 páginas sin robar peso al titular. */
  .migas{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.12em;
    text-transform:uppercase;color:var(--ink-3);margin:0 0 var(--e5);
    display:flex;flex-wrap:wrap;gap:var(--e2);align-items:center}
  .migas a{color:var(--ink-2);text-decoration:none}
  .migas a:hover,.migas a:focus-visible{color:var(--azure)}
  .migas span{color:var(--ink-3)}

  /* ── halos ambientales por clase ─────────────────
     La portada los ancla a #servicios/#portafolio/#porque. Aquí la misma
     luz, disponible como clase, para que ninguna página interior tenga el
     fondo liso — que fue justo el diagnóstico de la portada plana. */
  .halo-i,.halo-d,.halo-t{position:relative}
  .halo-i::after,.halo-d::after,.halo-t::after{
    content:"";position:absolute;pointer-events:none;z-index:0;
    width:min(900px,90vw);height:700px;filter:blur(20px);
    -webkit-mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 30%,transparent 76%);
    mask-image:radial-gradient(ellipse 50% 50% at 50% 50%,#000 30%,transparent 76%);
  }
  .halo-i::after{left:-12%;top:4%;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-1),transparent 70%)}
  .halo-d::after{right:-14%;top:18%;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--halo-2),transparent 70%)}
  .halo-t::after{right:-12%;top:10%;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,var(--teal-halo),transparent 70%)}

  /* Rótulo azul para las secciones SAP de páginas interiores. El teal ya lo
     resuelve .sec.teal, que es la clase que usa la portada. */
  .k-azul .kicker{color:var(--azure)}
  .k-azul .kicker::before{background:var(--azure)}

  /* ── dos columnas con lado fijo ──────────────────
     Patrón de las páginas de detalle: el argumento a la izquierda y una
     tarjeta de acción que acompaña el scroll a la derecha. */
  .dos{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,.7fr);
    gap:var(--e16);align-items:start}
  .lado{position:sticky;top:104px;display:flex;flex-direction:column;gap:var(--e5)}
  @media (max-width:900px){
    .dos{grid-template-columns:1fr;gap:var(--e10)}
    .lado{position:static}
  }

  /* Bloque de texto con subtítulos: identidad, legales, alcance. */
  .bloques{display:flex;flex-direction:column;gap:var(--e10)}
  .bloque h3{font-size:var(--t-lg);font-weight:800;letter-spacing:-.03em;margin:0 0 var(--e3)}
  .bloque p{color:var(--ink-2);margin:0;font-size:var(--t-md);max-width:62ch}

  /* Cifra de apoyo sin inventar datos: rótulo + dato que ya existe. */
  .dato{display:flex;flex-direction:column;gap:var(--e1);padding:var(--e5) 0;
    border-top:1px solid var(--line)}
  .dato dt{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.12em;
    text-transform:uppercase;color:var(--ink-3)}
  .dato dd{margin:0;font-size:var(--t-sm);color:var(--ink-2)}

  /* ── formulario ──────────────────────────────────
     Mismo vidrio que las tarjetas. El foco se marca con anillo, no solo con
     cambio de borde: a 1 px un borde de color no es señal suficiente. */
  .form{display:grid;gap:var(--e5);margin:var(--e8) 0 0}
  .form-fila{display:grid;grid-template-columns:1fr 1fr;gap:var(--e5)}
  .campo-f{display:flex;flex-direction:column;gap:var(--e2)}
  .campo-f label{font-family:var(--mono);font-size:var(--t-xs);letter-spacing:.1em;
    text-transform:uppercase;color:var(--ink-3)}
  .campo-f .req{color:var(--accion-txt)}
  .campo-f input,.campo-f select,.campo-f textarea{
    font-family:var(--sans);font-size:var(--t-sm);line-height:1.5;color:var(--ink);
    background:linear-gradient(180deg,var(--brillo),transparent),var(--panel);
    border:1px solid var(--line-2);border-radius:10px;
    padding:var(--e3) var(--e4);min-height:46px;width:100%;
    transition:border-color .18s,box-shadow .18s;
  }
  .campo-f textarea{resize:vertical;min-height:140px}
  .campo-f input::placeholder,.campo-f textarea::placeholder{color:var(--ink-3)}
  .campo-f input:focus,.campo-f select:focus,.campo-f textarea:focus{
    outline:none;border-color:var(--azure);
    box-shadow:0 0 0 3px color-mix(in srgb,var(--azure) 26%,transparent);
  }
  @supports not (color: color-mix(in srgb, red 50%, transparent)){
    .campo-f input:focus,.campo-f select:focus,.campo-f textarea:focus{
      box-shadow:0 0 0 3px rgba(77,155,255,.26)}
  }
  .campo-f select{appearance:none;-webkit-appearance:none;cursor:pointer;
    background-image:linear-gradient(180deg,var(--brillo),transparent),
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%235D7590' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat:no-repeat,no-repeat;
    background-position:center,right var(--e4) center;
    padding-right:var(--e10)}
  .form-nota{font-size:var(--t-xs);color:var(--ink-3);margin:0;max-width:52ch}
  .form-aviso{font-size:var(--t-sm);color:var(--ink-2);margin:0;
    border-left:2px solid var(--accion);padding:var(--e2) 0 var(--e2) var(--e4)}
  .hp-field{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
  @media (max-width:640px){ .form-fila{grid-template-columns:1fr} }

  /* ── prosa legal ─────────────────────────────────
     Medida corta y jerarquía por peso: son las páginas que nadie quiere leer
     y por eso merecen la tipografía más cuidada del sitio. */
  .prosa{max-width:72ch}
  /* h2 y no h3: en una página legal cada apartado es una sección de primer
     nivel bajo el titular, y saltarse el nivel rompe la navegación por
     encabezados de un lector de pantalla. Se le baja el tamaño, no el rango. */
  .prosa h2,.prosa h3{font-size:var(--t-lg);font-weight:800;letter-spacing:-.03em;
    margin:var(--e10) 0 var(--e3);line-height:1.25;scroll-margin-top:104px}
  .prosa h2:first-child,.prosa h3:first-child{margin-top:0}
  .prosa .destacado{font-size:var(--t-md);color:var(--ink-2);
    border-left:2px solid var(--azure);padding-left:var(--e5);margin:0 0 var(--e10)}
  .prosa strong{color:var(--ink);font-weight:700}
  /* Índice lateral de una página legal. */
  .indice{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--e2);
    counter-reset:idx}
  .indice a{font-size:var(--t-sm);color:var(--ink-2);text-decoration:none;
    display:flex;gap:var(--e3);transition:color .16s}
  .indice a::before{counter-increment:idx;content:counter(idx,decimal-leading-zero);
    font-family:var(--mono);font-size:var(--t-xs);color:var(--ink-3);padding-top:.15em}
  .indice a:hover,.indice a:focus-visible{color:var(--azure)}
  .prosa p{color:var(--ink-2);font-size:var(--t-sm);margin:0 0 var(--e4);line-height:1.7}
  .prosa ul{list-style:none;padding:0;margin:0 0 var(--e4);
    display:flex;flex-direction:column;gap:var(--e2)}
  .prosa li{font-size:var(--t-sm);color:var(--ink-2);padding-left:var(--e5);position:relative}
  .prosa li::before{content:"";position:absolute;left:0;top:.72em;width:7px;height:1px;
    background:var(--azure);opacity:.8}
  .prosa a{color:var(--azure)}

  /* ── 404 ─────────────────────────────────────────*/
  .perdido{text-align:center;padding:var(--e36) 0}
  .perdido .cod{font-family:var(--mono);font-size:clamp(4rem,14vw,9rem);font-weight:800;
    line-height:1;letter-spacing:-.04em;margin:0 0 var(--e5);
    background:linear-gradient(180deg,var(--ink),var(--ink-3));
    -webkit-background-clip:text;background-clip:text;color:transparent}
  /* El titular de la 404 es un h1 —es el encabezado de su página—, pero a
     tamaño de h2: el protagonista visual es el número. */
  .perdido h1{font-size:clamp(1.875rem,3.8vw,2.75rem);letter-spacing:-.04em;
    line-height:1.1;max-width:none;margin:0 0 var(--e4)}
  .perdido .eyebrow{margin-bottom:var(--e5)}
  .perdido .acciones{justify-content:center}
  .perdido p{color:var(--ink-2);max-width:46ch;margin:0 auto var(--e8)}

  /* El icono del servicio preside su página: es el mismo símbolo con el que
     aparece en el portafolio, así la ficha y la página se reconocen como la
     misma cosa. */
  .ico-hero{width:36px;height:36px;color:var(--azure);margin:0 0 var(--e5);display:block}
  .hero-int.teal .ico-hero{color:var(--teal)}

  /* Listas de navegación dentro de una tarjeta lateral. */
  .lista a{color:var(--ink-2);text-decoration:none;transition:color .16s}
  .lista a:hover,.lista a:focus-visible{color:var(--azure)}

  /* ── rejilla de siete ────────────────────────────
     Un 7 no cabe en múltiplos de 3 y deja dos celdas vacías. Mismo arreglo
     que la portada (primera y última a doble ancho), pero por clase, para
     que sirva en cualquier página y no solo en #portafolio. */
  .fichas-7 .ficha:nth-child(1),
  .fichas-7 .ficha:nth-child(7){
    grid-column:span 2;padding:var(--e10);
    background-image:linear-gradient(135deg,var(--brillo),transparent 55%);
  }
  .fichas-7 .ficha:nth-child(1) h4,
  .fichas-7 .ficha:nth-child(7) h4{font-size:var(--t-lg);letter-spacing:-.03em}
  .fichas-7 .ficha:nth-child(1) p,
  .fichas-7 .ficha:nth-child(7) p{max-width:46ch}
  .fichas-7 .ficha:nth-child(1) .ico,
  .fichas-7 .ficha:nth-child(7) .ico{width:26px;height:26px}
  @media (max-width:900px){
    .fichas-7 .ficha:nth-child(1),
    .fichas-7 .ficha:nth-child(7){grid-column:span 2;padding:var(--e8)}
  }
  @media (max-width:560px){
    .fichas-7 .ficha:nth-child(1),
    .fichas-7 .ficha:nth-child(7){grid-column:span 1}
  }

  /* ── ficha enlazada ──────────────────────────────
     El enlace lo lleva el título y su ::after cubre la celda entera. Así la
     tarjeta completa es clicable pero hay UN solo destino en el orden de
     tabulación — no seis enlaces al mismo sitio, que es lo que pasa cuando
     se enlazan título, texto y flecha por separado. */
  .ficha h4 .ficha-a{color:inherit;text-decoration:none}
  .ficha-a::after{content:"";position:absolute;inset:0;z-index:1}
  .ficha:hover .ficha-a{color:var(--azure)}
  .sec.teal .ficha:hover .ficha-a{color:var(--teal)}
  .ficha-a:focus-visible{outline:2px solid var(--azure);outline-offset:4px;border-radius:4px}
  /* Donde la tarjeta es un enlace, la flecha no espera al cursor: en pantalla
     táctil no hay hover y sin ella nada indica que se puede entrar. */
  .ficha:has(.ficha-a)::after{opacity:.5;transform:none}
  .ficha:has(.ficha-a):hover::after{opacity:1}

  /* El pie crece: ahora es el índice real del sitio. */
  .pie-grid{grid-template-columns:1.6fr 1fr 1fr 1fr}
  @media (max-width:900px){ .pie-grid{grid-template-columns:1fr 1fr} }
  @media (max-width:560px){ .pie-grid{grid-template-columns:1fr} }


/* ══════════════════ 3 · DIAGRAMAS ══════════════════ */
  /* ════════════ DIAGRAMAS DE SERVICIO ════════════
     Un dibujo por página de detalle. Criterio, el mismo que el del hero de
     v28: no ilustrar, EXPLICAR. Cada diagrama dibuja el argumento de su
     página —qué se evalúa, qué queda protegido, dónde se acumula el tiempo—
     y por eso no son intercambiables entre páginas.

     Gramática común, para que los diez se lean como un sistema:
       · lienzo 640×340, trazo 1–1.8, extremos redondeados
       · el azul es SAP, el verde azulado es seguridad, el ámbar SOLO marca
         el problema (riesgo, conflicto, desvío) — mismo reparto de color que
         el resto del sitio, donde el ámbar es lo único cálido
       · un único elemento en movimiento por diagrama, lento
       · cero imágenes: todo pesa menos de 3 KB y sigue al tema claro/oscuro
         porque los colores salen de las variables CSS

     Accesibilidad: el <svg> va aria-hidden y quien lo describe es el
     <figcaption>. Un diagrama con veinte <text> sueltos leído en voz alta es
     ruido; la frase del pie dice lo mismo y se entiende. */

  .d2{margin:0;position:relative}
  .d2 svg{width:100%;height:auto;display:block;overflow:visible}
  .d2 figcaption{margin-top:var(--e5);font-size:var(--t-sm);color:var(--ink-2);
    max-width:56ch;border-left:2px solid var(--line-2);padding-left:var(--e4)}
  .d2-ancho{max-width:860px;margin:0 auto}

  /* Resplandor propio: despega el dibujo del fondo, igual que en el hero. */
  .d2::before{
    content:"";position:absolute;inset:-8% -5%;z-index:0;pointer-events:none;
    background:radial-gradient(ellipse 50% 50% at 50% 45%,var(--halo-1),transparent 70%);
    filter:blur(22px);opacity:.75;
  }
  .d2 svg,.d2 figcaption{position:relative;z-index:1}

  /* ── piezas ────────────────────────────────────── */
  .d2-caja{fill:var(--panel);stroke:var(--line-2);stroke-width:1.25}
  .d2-caja.n{stroke:var(--azure);stroke-width:1.7}          /* núcleo / lo importante */
  .d2-caja.t{stroke:var(--teal);stroke-width:1.4}
  .d2-caja.v{fill:none}                                      /* solo contorno */

  .d2-tit{fill:var(--ink);font-size:12.5px;font-weight:800;letter-spacing:-.015em}
  .d2-sub{fill:var(--ink-3);font-size:9.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
  .d2-lbl{fill:var(--ink-2);font-size:11px;font-weight:700}
  .d2-num{fill:var(--ink-3);font-size:10px;font-weight:700;font-family:var(--mono);letter-spacing:.06em}
  .d2-lbl.a{fill:var(--accion-txt)}
  .d2-lbl.t{fill:var(--teal)}

  .d2-linea{fill:none;stroke:var(--line-2);stroke-width:1}
  .d2-guia{fill:none;stroke:var(--line-2);stroke-width:1;stroke-dasharray:3 5;opacity:.75}
  .d2-solido{fill:none;stroke:var(--azure);stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
  .d2-solido.t{stroke:var(--teal)}

  /* El flujo es lo único que se mueve, y a paso de operación. */
  .d2-flujo{fill:none;stroke:var(--azure);stroke-width:1.6;stroke-linecap:round;
    stroke-dasharray:4 8;animation:d2-fluir 3.8s linear infinite}
  .d2-flujo.t{stroke:var(--teal)}
  .d2-flujo.tenue{opacity:.5;stroke-width:1.3}
  @keyframes d2-fluir{to{stroke-dashoffset:-24}}

  .d2-nodo{fill:var(--azure)}
  .d2-nodo.t{fill:var(--teal)}
  .d2-nodo.a{fill:var(--accion)}
  .d2-halo{fill:var(--azure);opacity:.09}
  .d2-halo.t{fill:var(--teal);opacity:.11}
  .d2-halo.a{fill:var(--accion);opacity:.14}

  /* Recorte de un subconjunto: lo que un rol ve dentro de un catálogo.
     Las hormigas marchando dicen «esta selección está activa». */
  .d2-sel{fill:none;stroke:var(--azure);stroke-width:1.4;stroke-dasharray:6 5;opacity:.85;
    animation:d2-hormigas 3.6s linear infinite}
  @keyframes d2-hormigas{to{stroke-dashoffset:-22}}

  /* Ámbar: siempre señala el problema, nunca decora. */
  .d2-alerta{fill:none;stroke:var(--accion);stroke-width:1.6;stroke-linecap:round}
  .d2-tenue{opacity:.4}

  .d2-pulso{fill:var(--azure);opacity:.3;animation:d2-latir 4.2s ease-out infinite}
  @keyframes d2-latir{0%{r:7;opacity:.32}70%{r:22;opacity:0}100%{r:22;opacity:0}}

  .d2-pulso.a{fill:var(--accion)}

  /* ════════════ MOVIMIENTO ════════════
     Criterio heredado de v28: el movimiento se lee como precisión, no como
     adorno. Nada rebota y nada gira por girar. Cada cosa que se mueve aquí
     dice algo que el dibujo quieto no diría. */

  /* ── montaje por capas ───────────────────────────
     El diagrama se arma en el orden en que se explica: primero la
     estructura, después el flujo, al final lo que hay que mirar.

     `backwards` y no `forwards` a propósito: el estado normal del elemento
     es VISIBLE y la animación solo lo oculta hacia atrás durante su espera.
     Si por lo que sea la animación no llegara a correr, el diagrama aparece
     entero — nunca en blanco. Con `forwards` sobre opacity:0 un fallo de
     animación deja el dibujo invisible, y ese es un modo de fallo que no
     vale la pena arriesgar por una entrada bonita. */
  .d2 .ap{animation:d2-ap .55s cubic-bezier(.2,.75,.3,1) backwards;
    animation-delay:calc(.12s + var(--k,0) * .15s)}
  @keyframes d2-ap{from{opacity:0}to{opacity:1}}

  /* ── un dato recorriendo su camino ───────────────
     Sustituye al guion discontinuo cuando importa QUÉ viaja, no solo que
     algo fluye: una solicitud que entra al núcleo, un dato que llega a la
     capa común, la operación que no se detiene. */
  /* transform-box:fill-box es obligatorio, no cosmético: sin él la caja de
     referencia de un elemento SVG es el viewBox entero, así que `center`
     sería el centro del lienzo (320,170) y el token aparecería desplazado
     esa distancia respecto de su camino. Con fill-box la referencia es el
     propio círculo y `center` es su centro, que es lo que se quiere. */
  .d2-token{fill:var(--azure);transform-box:fill-box;
    offset-anchor:center;offset-rotate:0deg;
    animation:d2-recorrer 6s linear infinite}
  .d2-token.t{fill:var(--teal)}
  @keyframes d2-recorrer{from{offset-distance:0%}to{offset-distance:100%}}
  @supports not (offset-path: path('M0 0 L1 1')){ .d2-token{display:none} }

  /* El token del proceso SE ATASCA en el cuello de botella: recorre medio
     camino, se arrastra un tercio del tiempo avanzando un 7%, y después
     recupera. La demora se ve; no hace falta explicarla al lado. */
  .d2-token.atasco{animation-name:d2-atasco;animation-duration:8s}
  @keyframes d2-atasco{
    0%  {offset-distance:0%}
    38% {offset-distance:46%}
    66% {offset-distance:53%}
    100%{offset-distance:100%}
  }

  /* ── destello que recorre una traza ──────────────
     Es el barrido de un monitor: la línea ya está dibujada y lo que pasa por
     encima es la lectura. Necesita pathLength="1000" en el trazo. */
  .d2-gleam{fill:none;stroke:var(--azure);stroke-width:2.6;stroke-linecap:round;
    stroke-dasharray:40 960;opacity:.9;animation:d2-barrer 4.6s linear infinite}
  @keyframes d2-barrer{from{stroke-dashoffset:1000}to{stroke-dashoffset:0}}

  /* ── trazo que se dibuja una vez ─────────────────
     Para la tendencia: se dibuja al entrar y se queda. pathLength="100". */
  .d2-trazo{stroke-dasharray:100;animation:d2-trazar 1.5s ease .6s backwards}
  @keyframes d2-trazar{from{stroke-dashoffset:100}to{stroke-dashoffset:0}}

  /* ── eco de un anillo ────────────────────────────
     Va SIEMPRE duplicando un anillo fijo: el fijo marca el sitio y el eco
     llama la atención sin que el sitio parpadee. */
  .d2-anillo{transform-box:fill-box;transform-origin:center;
    animation:d2-eco 3.2s ease-out infinite}
  @keyframes d2-eco{0%{transform:scale(1);opacity:.9}70%{transform:scale(1.6);opacity:0}100%{transform:scale(1.6);opacity:0}}
  .d2-eco{fill:none;stroke:var(--azure);stroke-width:1.5}

  /* ── barras que crecen desde su base ─────────────*/
  .d2-crece{transform-box:fill-box;transform-origin:bottom;
    animation:d2-crecer .85s cubic-bezier(.2,.8,.3,1) backwards;
    animation-delay:calc(.75s + var(--k,0) * .1s)}
  @keyframes d2-crecer{from{transform:scaleY(.04)}to{transform:scaleY(1)}}

  /* ── barrido de auditoría sobre la matriz ────────
     Recorre las filas como las recorre una revisión de accesos: despacio y
     entera. Es el único movimiento del diagrama de SoD, aparte del latido
     de los conflictos. */
  .d2-scan{fill:var(--teal);opacity:.14;animation:d2-barrido 7.5s ease-in-out infinite}
  @keyframes d2-barrido{0%{transform:translateY(0)}50%{transform:translateY(196px)}100%{transform:translateY(0)}}

  /* ── latido de un problema ───────────────────────*/
  .d2-late{animation:d2-latido 3.4s ease-in-out infinite}
  @keyframes d2-latido{0%,100%{opacity:.05}50%{opacity:.2}}

  /* ── la fase activa avanza ───────────────────────
     Tres realces encadenados: 01 → 02 → 03, en bucle. Dice «por etapas»
     mejor que la palabra «etapas». */
  .d2-fase{opacity:0;animation:d2-fase 6.6s ease-in-out infinite;
    animation-delay:calc(var(--k,0) * 2.2s)}
  @keyframes d2-fase{0%{opacity:0}6%{opacity:1}28%{opacity:1}36%{opacity:0}100%{opacity:0}}

  @media (prefers-reduced-motion:reduce){
    .d2-flujo,.d2-pulso,.d2-sel,.d2 .ap,.d2-token,.d2-gleam,.d2-trazo,
    .d2-anillo,.d2-crece,.d2-scan,.d2-late,.d2-fase{animation:none}
    .d2-pulso,.d2-fase{opacity:0}
    .d2-token,.d2-gleam,.d2-scan,.d2-anillo{display:none}
    .d2-trazo{stroke-dasharray:none}
  }

  /* ── la ficha deja de ser tres tarjetas en fila ──
     El diagrama manda y los tres datos lo acompañan en columna. De paso
     rompe el patrón "encabezado + rejilla" que era lo que aplanaba. */
  .ficha-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
    gap:var(--e12);align-items:center}
  .ficha-grid .col{display:flex;flex-direction:column;gap:var(--e4)}
  .ficha-grid .tarjeta{padding:var(--e6)}
  .ficha-grid .tarjeta h3{font-size:var(--t-b);margin-bottom:var(--e2)}
  .ficha-grid .tarjeta>p{font-size:var(--t-sm);margin:0}
  @media (max-width:900px){
    .ficha-grid{grid-template-columns:1fr;gap:var(--e10)}
  }


/* ══════════════════ 4 · SITIO REAL ══════════════════ */

  /* ════════════ AJUSTES DEL SITIO REAL ════════════
     Lo que el artefacto no necesitaba y el sitio sí. */

  /* Salto al contenido: primer tabulable de cada página. Invisible hasta que
     recibe foco, y entonces tiene que verse sobre la barra (z-index 40). */
  .skip-link{
    position:absolute;left:var(--e4);top:-100px;z-index:60;
    background:var(--accion);color:#0B0800;font-weight:800;font-size:var(--t-sm);
    padding:var(--e3) var(--e5);border-radius:10px;text-decoration:none;
    transition:top .18s;
  }
  .skip-link:focus{top:var(--e4)}

  /* Estado del envío del formulario. Lo pinta js/contact-status.js leyendo
     ?status= de la URL con la que vuelve la Function de /enviar.
     Vacío no ocupa sitio: sin :empty, dejaría un hueco en la primera visita. */
  .form-status{
    font-size:var(--t-sm);border-radius:10px;margin:0 0 var(--e5);
    padding:var(--e4) var(--e5);border:1px solid var(--line-2);
    background:linear-gradient(180deg,var(--brillo),transparent);
  }
  .form-status:empty{display:none}
  .form-status.success{border-color:var(--teal);color:var(--ink)}
  .form-status.error{border-color:var(--accion);color:var(--ink)}

  /* El campo trampa: oculto para quien ve y para quien escucha, pero
     rellenable por un bot. Nunca display:none — algunos bots lo detectan. */
  .hp-field{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}

