@font-face{font-family:'IBM Plex Mono';font-style:italic;font-weight:500;font-display:swap;src:url(fonts/ibm-plex-mono-500-italic.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:italic;font-weight:600;font-display:swap;src:url(fonts/ibm-plex-mono-600-italic.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(fonts/ibm-plex-mono-400.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(fonts/ibm-plex-mono-500.woff2) format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url(fonts/ibm-plex-mono-600.woff2) format('woff2')}
@font-face{font-family:'Inter';font-style:normal;font-weight:300;font-display:swap;src:url(fonts/inter-300.woff2) format('woff2')}
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(fonts/inter-400.woff2) format('woff2')}
@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(fonts/inter-500.woff2) format('woff2')}
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(fonts/inter-600.woff2) format('woff2')}
@font-face{font-family:'Space Mono';font-style:normal;font-weight:400;font-display:swap;src:url(fonts/space-mono-400.woff2) format('woff2')}
@font-face{font-family:'Space Mono';font-style:normal;font-weight:700;font-display:swap;src:url(fonts/space-mono-700.woff2) format('woff2')}

/* ─────────────────────────────────────────────────────────────
   Dos registros. El titular tiene dos mitades y la página también.
   La barra negra de «Manos de ingeniero» en el héroe anticipa el
   registro oscuro de la segunda mitad; el mismo texto lo abre.

   Sin prefers-color-scheme a propósito: el contraste claro/oscuro
   ES el argumento. Todo se pinta explícito.
   ───────────────────────────────────────────────────────────── */
:root{
  --brand:#002DA0; --brand-deep:#001B60; --on-brand:#FFFFFF;
  --surface:#FFFFFF; --band:#F7F9FF; --ink:#1A1A1A;
  --muted:#6B7280; --faint:#8E96A8; --line:#DDE2F5;
  /* registro oscuro — base de #speaking, con el gris subido para que contraste */
  --d-surface:#0F0F12; --d-card:#17171A; --d-line:#2E2E33;
  --d-ink:#F2EDE4; --d-muted:#D5D1C8; --d-dim:#9C968B; --d-accent:#6699FF;
  --mark:#B85C3B;
  --r:4px;
  --sans:'Inter',system-ui,-apple-system,sans-serif;
  --head:'IBM Plex Mono',ui-monospace,Menlo,monospace;
  --data:'Space Mono',ui-monospace,monospace;
  --measure:34rem;
}

*{box-sizing:border-box}
html{scroll-behavior:smooth}
/* --tb lo mide el script: alto real de la cabecera pegajosa. Sin esto, un
   salto de ancla deja el titular escondido debajo de la barra.
   Va en el destino y no como scroll-padding en html a propósito: el
   scroll-padding encoge el puerto de scroll y con él el rango «contain»
   del cruce, que arrancaba 175 px antes de que el escenario se clavara. */
[id]{scroll-margin-top:calc(var(--tb,0px) + 1rem)}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{transition:none!important}}
body{margin:0;background:var(--surface);color:var(--ink);font-family:var(--sans);
  font-size:16px;line-height:1.62;-webkit-font-smoothing:antialiased}
.wrap{max-width:70rem;margin:0 auto;padding:0 2rem}
a{color:var(--brand);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:3px}
a:focus-visible{outline:2px solid var(--brand);outline-offset:3px;border-radius:2px}
img{max-width:100%;height:auto}


/* ── cabecera pegajosa: promo + fila del nombre, en bloque ─
   Van dentro de un único contenedor sticky en vez de pegarse por
   separado, que obligaría a conocer la altura de cada una. */
.topbar{position:sticky;top:0;z-index:40;background:var(--surface)}

/* ── barra de productos: logos fijos, mensaje rotativo ───── */
.promo{background:var(--d-surface);color:var(--d-muted);border-bottom:1px solid var(--d-line)}
/* Enseña producto propio, pero no es el asunto de la página: va al 60 %
   del alto que tenía. */
.promo .wrap{display:flex;align-items:center;gap:1rem;padding-top:.25rem;padding-bottom:.25rem}
.promo-logo{flex:none;display:block;line-height:0;opacity:.32;transition:opacity .45s ease}
.promo-svg{width:20px;height:20px;border-radius:4px;display:block}
.promo[data-on="mj"] .promo-logo.mj,.promo[data-on="ms"] .promo-logo.ms{opacity:1}
.promo-msgs{flex:1;min-width:0;position:relative;height:1.25rem;text-align:center}
.promo-msg{position:absolute;inset:0;display:block;line-height:1.25rem;text-align:center;
  font-size:.82rem;color:var(--d-muted);opacity:0;visibility:hidden;
  transition:opacity .45s ease;text-decoration:none;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.promo-msg .q{margin-right:.4rem}
.promo-msg.on{opacity:1;visibility:visible}
.promo-msg:hover{color:var(--d-ink);text-decoration:none}
.promo-msg b{color:var(--d-accent);font-weight:600}
@media (prefers-reduced-motion:reduce){
  .promo-msgs{height:auto;display:flex;flex-direction:column;gap:.3rem}
  .promo-msg{position:static;opacity:1;visibility:visible}
  .promo-logo{opacity:1}
}
/* El tamaño lo fija el script midiendo el hueco real: los saltos por
   ancho de pantalla encogían la letra aunque sobrara sitio. */
.promo-msg{font-size:var(--pf,.82rem)}
@media (max-width:52rem){
  .promo-svg{width:18px;height:18px}
  .promo .wrap{gap:.7rem}
}
@media (max-width:30rem){
  .promo-svg{width:16px;height:16px}
  .promo .wrap{gap:.5rem;padding-left:.9rem;padding-right:.9rem}
}

/* ── cabecera ─────────────────────────────────────── */
/* Una sola línea: desplegable, nombre e idioma. El menú no gasta fila
   propia en ningún ancho. */
.top{display:flex;align-items:center;gap:1rem;padding:.6rem 0;flex-wrap:nowrap;
  position:relative;border-bottom:1px solid var(--line)}
.top .name{font-family:var(--head);font-weight:600;font-size:1.05rem;letter-spacing:-.5px;
  color:var(--brand);white-space:nowrap}
/* Banderas, con el código de idioma al lado. La bandera sola es ambigua
   —marca un país, no una lengua— y no dice cuál está puesto: con las dos
   cosas se reconoce de un vistazo y se sigue leyendo. El nombre completo
   va oculto para quien navega con lector de pantalla. */
.top .lang{margin-left:auto;display:flex;align-items:center;gap:.55rem;white-space:nowrap}
.top .lang>*{display:inline-flex;align-items:center;gap:.35rem;padding:.2rem .35rem;
  border-radius:3px;font-family:var(--data);font-size:.72rem;line-height:1;
  color:var(--faint);border:1px solid transparent}
.top .lang img{width:16px;height:12px;object-fit:cover;border-radius:1px;display:block;
  opacity:.45;transition:opacity .18s ease}
.top .lang .lang-on{color:var(--ink);border-color:var(--line);background:var(--band)}
.top .lang .lang-on img{opacity:1}
.top .lang a:hover{text-decoration:none;color:var(--brand);border-color:var(--line)}
.top .lang a:hover img{opacity:1}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);
  white-space:nowrap}
/* Por debajo de esto el nombre y el desplegable se comen la fila y el
   código se sale de la pantalla; queda la bandera sola. A 390 caben los
   dos de sobra, así que el corte va donde de verdad aprieta. */
@media (max-width:23rem){
  .top .lang .lang-code{display:none}
  .top .lang{gap:.35rem}
  .top .lang>*{padding:.2rem .3rem}
}
.menu{position:absolute;left:0;right:0;top:100%;background:var(--surface);
  border-bottom:1px solid var(--line);box-shadow:0 12px 24px -18px rgba(0,45,160,.45);
  display:none;flex-direction:column;padding:.4rem 0 .6rem;z-index:30}
.top[data-open="1"] .menu{display:flex}
.menu a{font-size:.95rem;color:var(--ink);padding:.6rem 2rem}
.menu a:hover{background:var(--band);color:var(--brand);text-decoration:none}
/* De ancho completo solo tiene sentido en el móvil. En pantalla grande se
   queda en panel, colgando del botón, que es lo que se espera de un menú. */
@media (min-width:52.01rem){
  .menu{right:auto;min-width:15rem;padding:.5rem 0;border:1px solid var(--line);
    border-radius:var(--r);margin-top:-1px}
  .menu a{padding:.55rem 1.2rem}
}
/* El desplegable va en la línea del nombre en todos los anchos. Antes la
   fila de accesos vivía debajo y, con la barra de productos encima, la
   cabecera pegajosa se comía 159 px de pantalla en el escritorio. Ahora
   son 96, y el menú deja de estar dos veces en el documento. */
.burger{display:inline-flex;align-items:center;background:none;border:1px solid var(--line);
  border-radius:var(--r);padding:.45rem .5rem;font:inherit;color:var(--ink);cursor:pointer;flex:none}
.burger:hover{border-color:var(--brand);color:var(--brand)}
.burger i{display:block;width:16px;height:10px;position:relative;flex:none}
.burger i::before,.burger i::after,.burger i span{content:"";position:absolute;left:0;right:0;
  height:2px;background:currentColor;border-radius:1px;transition:transform .2s,opacity .2s}
.burger i::before{top:0}
.burger i span{top:4px}
.burger i::after{bottom:0}
.top[data-open="1"] .burger i::before{transform:translateY(4px) rotate(45deg)}
.top[data-open="1"] .burger i span{opacity:0}
.top[data-open="1"] .burger i::after{transform:translateY(-4px) rotate(-45deg)}

/* ── héroe ────────────────────────────────────────── */
.hero-top{padding:3rem 0 0}
.eyebrow{font-family:var(--data);font-size:1rem;letter-spacing:.05em;color:var(--muted);
  margin:0 0 1.5rem;display:flex;align-items:center;gap:.75rem}
.eyebrow::before{content:"";width:2.5rem;height:1px;background:var(--brand);flex:none}
.h1a{font-family:var(--head);font-weight:600;font-size:clamp(2.1rem,7.2vw,5.6rem);
  line-height:1.04;letter-spacing:-.045em;margin:0 0 .35rem;color:var(--brand)}
/* la barra: a sangre, anticipa la mitad oscura */
.h1bar{background:var(--d-surface);padding:.5rem 0 .75rem;margin-bottom:3rem}
.h1bar span{display:block;font-family:var(--head);font-weight:500;font-style:italic;
  font-size:clamp(2.1rem,7.2vw,5.6rem);line-height:1.12;letter-spacing:-.045em;color:var(--d-ink)}
.hero-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,28rem);gap:3rem;align-items:start}
.lede{color:var(--muted);margin:0;max-width:var(--measure);font-size:1.02rem}
.lede b{color:var(--ink);font-weight:500}

/* tarjeta del caso */
.case{background:var(--band);border:1px solid var(--line);border-radius:var(--r);padding:1.6rem}
.case-label{font-family:var(--data);font-size:.66rem;letter-spacing:.06em;color:var(--muted);
  text-transform:uppercase;margin:0 0 .8rem}
.case h2{font-family:var(--head);font-weight:500;font-size:1.05rem;line-height:1.35;
  letter-spacing:-.02em;margin:0 0 1.2rem;color:var(--ink);max-width:none}
.case h2 b{color:var(--brand);font-weight:600}
/* el SVG estila por clase: sin estas reglas el área sale negra */
.chart{position:relative;height:180px;margin:1.4rem 0 1rem}
.chart svg{width:100%;height:100%;display:block;overflow:visible}
.chart .area-path{fill:url(#areaGrad);opacity:.5}
.chart .line-path{fill:none;stroke:var(--brand);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.chart .dot-end{fill:var(--brand)}
/* El gráfico es el dato más fuerte de la página y estaba quieto. Una luz
   circula por la curva sin parar, y cada hito se enciende justo cuando le
   pasa por encima: el destello hace de reloj de todo el dibujo, en vez de
   tener tres ritmos sueltos peleándose.
   Todo con transformaciones y opacidad, nada que obligue a recalcular. */
.chart{--ciclo:4s}
.chart .eco,.chart .dot{transform-box:fill-box;transform-origin:center}
.chart .eco{opacity:0}
.chart .glow{fill:none;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:.2 .8;stroke-dashoffset:1;opacity:0}
/* Dos capas envolviendo la curva: el halo ancho va detrás y desborda por
   los lados, el filamento va delante y más fino que el propio trazo. Con
   una sola capa encima, el destello tapaba la línea en vez de encenderla. */
.chart .glow-halo{stroke:var(--d-accent);stroke-width:6.5}
.chart .glow-nucleo{stroke:#DCE8FF;stroke-width:.9}
/* El patrón mide justo el largo del trazado, así que cuando la cabeza sale
   por la derecha la cola ya está entrando por la izquierda: circula sin
   pararse y sin necesitar huecos en los fotogramas clave. */
@keyframes circula{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
/* El encendido del hito: un golpe corto y a esperar la vuelta siguiente. */
@keyframes prende{0%{transform:scale(1)}7%{transform:scale(1.28)}
  22%{transform:scale(1)}100%{transform:scale(1)}}
@keyframes eco{0%{transform:scale(.5);opacity:.3}
  26%{transform:scale(1.5);opacity:0}100%{transform:scale(1.5);opacity:0}}
@media (prefers-reduced-motion:no-preference){
  /* Solo mientras se ve. Fuera de pantalla no pinta nada y gasta batería,
     y de paso el recorrido empieza de cero cada vez que se vuelve a él. */
  .chart[data-visto="1"] .glow{animation:circula var(--ciclo) linear infinite;opacity:.3}
  .chart[data-visto="1"] .glow-nucleo{opacity:.75}
  .chart[data-visto="1"] .hito .dot{animation:prende var(--ciclo) ease-out infinite;
    animation-delay:calc(var(--ciclo) * var(--fase))}
  .chart[data-visto="1"] .hito .eco{animation:eco var(--ciclo) ease-out infinite;
    animation-delay:calc(var(--ciclo) * var(--fase))}
  .chart .line-path,.chart .area-path,.chart .glow{
    transition:stroke-width .25s ease,opacity .25s ease}
  /* Con el ratón encima sube el brillo, no la velocidad: cambiar la
     duración descuadraría el desfase de los tres hitos. */
  .chart:hover .line-path{stroke-width:2.6}
  .chart:hover .area-path{opacity:.72}
  .chart:hover .glow-halo{opacity:.5;stroke-width:8.5}
  .chart:hover .glow-nucleo{opacity:1;stroke-width:1.15}
}
.chart .event-line{stroke:var(--ink);stroke-opacity:.25;stroke-width:1;stroke-dasharray:2 2}
.chart .event-dot{fill:var(--brand-deep);stroke:var(--band);stroke-width:1.5}
/* las anotaciones venían a 6,5 px y no se leían */
.chart text{font-family:var(--data);font-size:11px;font-weight:700;letter-spacing:.01em}
.chart text[fill="#888"]{font-size:9px;font-weight:400}
.case-foot{display:flex;flex-direction:column;gap:.3rem;padding-top:.9rem;
  border-top:1px solid var(--line);font-family:var(--data);font-size:.74rem;color:var(--muted)}
.case-foot b{color:var(--brand);font-weight:700}
.case-foot i{font-style:normal;color:var(--faint);margin-right:.5rem}

/* ── cifras ───────────────────────────────────────── */
.figures{margin:3.5rem 0 0;display:grid;grid-template-columns:repeat(4,1fr);gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.fig{background:var(--surface);padding:1.35rem 1.25rem}
.fig .k{font-family:var(--data);font-size:.65rem;color:var(--muted);display:block;margin-bottom:.55rem}
.fig .v{font-family:var(--head);font-weight:600;font-size:2.2rem;line-height:1;color:var(--brand);
  display:block;font-variant-numeric:tabular-nums;letter-spacing:-.03em}
.fig .d{font-size:.79rem;color:var(--muted);display:block;margin-top:.5rem;line-height:1.45}

/* ── secciones claras ─────────────────────────────── */
section{padding:5rem 0 0}
/* el contacto entero vive dentro de la pantalla que entra en el último
   cruce: al soltarse el pegado la página ya se ha acabado */
section.outro{padding:0}
section.outro .cta{margin-top:2.2rem}
.shead{font-family:var(--data);font-size:.98rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);margin:0 0 1.1rem;padding-bottom:.6rem;border-bottom:1px solid var(--line)}
h2{font-family:var(--head);font-weight:600;font-size:clamp(1.65rem,3.5vw,2.3rem);
  letter-spacing:-.032em;line-height:1.16;margin:0 0 1.6rem;max-width:30ch;color:var(--brand)}
h2 em{font-style:italic;font-weight:500;color:var(--ink)}
.prose{max-width:var(--measure)}
.prose p{margin:0 0 1.05rem}
.prose p:last-child{margin-bottom:0}
.prose strong{font-weight:600;color:var(--ink)}
.who{display:grid;grid-template-columns:11rem minmax(0,1fr);gap:2.5rem;align-items:start}
.portrait{border-radius:var(--r);border:1px solid var(--line);display:block;width:100%}

/* ── puestos: carril solo con fechas, specs bajo el título ── */
.role{display:grid;grid-template-columns:9.5rem minmax(0,1fr);gap:0 2.5rem;padding:2.1rem 0;
  border-bottom:1px solid var(--line)}
.role:last-of-type{border-bottom:0}
.rail{font-family:var(--data);font-size:.72rem;line-height:1.6;color:var(--muted);text-align:right}
.now{display:inline-block;background:var(--brand);color:var(--on-brand);font-family:var(--data);
  font-size:.6rem;letter-spacing:.08em;padding:.14rem .45rem;border-radius:2px;margin-top:.45rem}
/* el logo baja bajo el título: así todos arrancan en la misma vertical */
.role-head{margin-bottom:1.1rem}
h3{font-family:var(--head);font-weight:600;font-size:1.75rem;letter-spacing:-.035em;
  line-height:1.2;margin:0 0 .3rem;color:var(--brand)}
.org{font-size:1rem;color:var(--ink);margin:0;line-height:1.5}
.org span{font-family:var(--data);font-size:.8rem;color:var(--faint);letter-spacing:.02em}
/* tamaños de la web anterior: 32 px sueltos, 24 px cuando hay varios.
   El tope de anchura solo afecta a elEconomista, que es muy apaisado. */
.logos{display:flex;align-items:center;gap:1.25rem;flex-wrap:wrap;margin-top:.7rem}
.logos img{height:32px;width:auto;max-width:170px;object-fit:contain;object-position:left;opacity:.95}
.logos.multi img{height:24px;max-width:130px}
.role p{margin:0 0 1rem;max-width:var(--measure)}
.tagrow{display:flex;gap:.6rem;align-items:baseline;margin-top:.7rem;flex-wrap:wrap}
.taglabel{font-family:var(--data);font-size:.6rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);flex:0 0 auto;min-width:4.6rem;padding-top:.28rem;padding-right:.5rem}
.tags{display:flex;flex-wrap:wrap;gap:.4rem;flex:1;min-width:0}
.tag{font-family:var(--data);font-size:.68rem;color:var(--muted);background:var(--band);
  border:1px solid var(--line);border-radius:var(--r);padding:.22rem .55rem}
.tag.biz{color:var(--brand);border-color:var(--brand);font-weight:700;background:var(--surface)}
/* ciudades con bandera */
.tag.city{display:inline-flex;align-items:center;gap:.4rem;padding-left:.35rem}
.tag.city img{height:12px;width:16px;object-fit:cover;border-radius:1px;display:block}

/* ── formación en cuadrados ───────────────────────── */
/* maqueta de la web anterior, con la tipografía nueva */
.edu{display:grid;grid-template-columns:repeat(4,1fr);gap:2rem}
.edu-item{background:var(--band);border:1px solid var(--line);border-radius:var(--r);
  padding:2rem 1.5rem;display:flex;flex-direction:column}
.edu-year{font-family:var(--data);font-size:.7rem;letter-spacing:.12em;color:var(--brand);margin:0 0 .5rem}
.edu-logo{height:38px;max-width:120px;width:auto;object-fit:contain;object-position:left;
  margin:0 0 1.25rem;align-self:flex-start}
.edu-degree{font-family:var(--head);font-weight:600;font-size:1.25rem;letter-spacing:-.03em;
  line-height:1.25;margin:0 0 .5rem;color:var(--ink)}
.edu-school{font-size:.9rem;color:var(--muted);margin:0 0 .5rem}
.edu-note{font-family:var(--data);font-size:.7rem;color:var(--faint);letter-spacing:.02em;
  line-height:1.45;margin:0 0 1rem}
.edu-flags{display:flex;gap:.4rem;margin-top:auto}
.edu-flags img{height:14px;width:19px;object-fit:cover;border:1px solid var(--line);border-radius:1px}
.certs{display:grid;grid-template-columns:220px 1fr;gap:3rem;padding-top:3rem;
  margin-top:1rem;border-top:1px solid var(--line);align-items:start}
.certs-label{font-family:var(--data);font-size:.7rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint)}

/* ── mapa ─────────────────────────────────────────── */
/* Tierra punteada mediante <pattern>: el aspecto estilizado cuesta unos
   cientos de bytes en vez de miles de círculos. Arcos desde Madrid. */
.mapbox{border:1px solid var(--line);border-radius:var(--r);background:var(--band);
  padding:1.6rem 1.6rem 1.1rem;overflow:hidden}
/* overflow:hidden desde que el viewBox va recortado: si no, el resto del
   mundo se sale del dibujo y se cuela por detrás de la leyenda. El rótulo
   de Toronto, que era lo que pedía overflow:visible, cabe de sobra dentro
   del margen del recorte. */
.mapbox svg{width:100%;height:auto;display:block;overflow:hidden}
.map-land{fill:url(#mapDots)}
.map-arc{fill:none;stroke:var(--brand);stroke-width:1.5;stroke-opacity:.45;
  stroke-linecap:round;stroke-dasharray:var(--l);stroke-dashoffset:var(--l);
  animation:trazo 1.5s cubic-bezier(.4,0,.2,1) .35s forwards}
@keyframes trazo{to{stroke-dashoffset:0}}
.mk{cursor:default}
.mk-hit{fill:transparent}
.mk-dot{fill:var(--brand);stroke:var(--band);stroke-width:2;transition:r .18s ease}
.mk.est .mk-dot{fill:var(--band);stroke:var(--brand);stroke-width:2.6}
/* punto partido: mitad trabajo, mitad formación */
.mk.amb .mk-dot{stroke:var(--brand);stroke-width:1.4}
.mk-half{fill:var(--band);stroke:var(--brand);stroke-width:1.4}
.mk-ring{fill:none;stroke:var(--brand);stroke-width:1.4;opacity:0;transition:opacity .18s ease,r .18s ease}
/* el nombre, siempre puesto. El filete del color del fondo lo despega de
   la tierra punteada sin necesidad de caja */
.mk-txt{font-family:var(--data);font-size:11px;fill:var(--ink);text-anchor:middle;
  pointer-events:none;paint-order:stroke;stroke:var(--band);stroke-width:3.5;
  stroke-linejoin:round;transition:opacity .18s ease}
.mk-lab{opacity:0;transition:opacity .18s ease;pointer-events:none}
.mk-lab rect{fill:var(--brand);rx:3}
.mk-lab text{fill:#fff;font-family:var(--data);font-size:13px;font-weight:700}
.mk:hover .mk-ring,.mk:focus-visible .mk-ring,.mk.hl .mk-ring{opacity:.55;r:15}
.mk:hover .mk-lab,.mk:focus-visible .mk-lab,.mk.hl .mk-lab{opacity:1}
/* solo Toronto lleva detalle largo; ahí el nombre corto se aparta para que
   el rótulo no se monte encima. El resto lo mantiene puesto */
.mk.sub:hover .mk-txt,.mk.sub:focus-visible .mk-txt,.mk.sub.hl .mk-txt{opacity:0}
.mk:hover .mk-dot,.mk.hl .mk-dot{r:8}
/* al señalar una ciudad se enciende su arco y los demás se apagan */
.mapbox.act .map-arc{stroke-opacity:.1}
.mapbox.act .map-arc.on{stroke-opacity:.85;stroke-width:2}
.mapbox.act .mk:not(.hl):not(:hover) .mk-dot,
.mapbox.act .mk:not(.hl):not(:hover) .mk-half{opacity:.35}
.map-key{display:flex;flex-wrap:wrap;gap:.6rem 2rem;margin-top:1.1rem;padding-top:.9rem;
  border-top:1px solid var(--line);font-family:var(--data);font-size:.7rem;color:var(--muted)}
.map-key span{display:inline-flex;align-items:center;gap:.45rem}
.dotw,.dote,.dotb{width:11px;height:11px;border-radius:50%;flex:none;border:2px solid var(--brand)}
.dotw{background:var(--brand)}
.dote{background:var(--band)}
.dotb{background:linear-gradient(90deg,var(--brand) 0 50%,var(--band) 50% 100%)}
.map-cities{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:1.1rem}
.map-cities .tag{cursor:default;transition:border-color .18s,color .18s}
.map-cities .tag:hover,.map-cities .tag:focus-visible{border-color:var(--brand);color:var(--brand)}
/* ── las tres zonas: el mismo mapa, partido, para móvil ──
   El mapa ancho a 390 px entra a menos de la mitad de escala: una franja
   de 148 px con puntos de 4 px y sin nombres, que no cuenta nada. Ahí se
   parte en las tres zonas donde hay algo, cada una ampliada y con sus
   ciudades al lado. El trazado del mundo no se duplica: los cuatro
   dibujos referencian el mismo con <use>. */
.svg-defs{position:absolute;width:0;height:0;overflow:hidden}
.zonas{display:none}
.zona{display:grid;grid-template-columns:8.5rem 1fr;gap:0 1rem;align-items:center;
  padding:.9rem 0;border-bottom:1px solid var(--line)}
.zona:last-child{border-bottom:0;padding-bottom:.2rem}
.zona:first-child{padding-top:.2rem}
.zona>svg{width:100%;height:auto;display:block;overflow:hidden;border-radius:3px}
/* la zona va a la mitad de escala que el mapa ancho: el arco necesita más
   trazo para pesar lo mismo */
.zona .map-arc{stroke-width:2.4;stroke-opacity:.55}
.zona-tit{font-family:var(--head);font-weight:600;font-size:.95rem;color:var(--brand);margin:0 0 .5rem}
.zona ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.4rem}
.zona li{display:grid;grid-template-columns:16px 1fr;gap:0 .5rem;align-items:center}
.zona li img{height:12px;width:16px;object-fit:cover;border-radius:1px}
.zona .zc{font-size:.92rem;color:var(--ink)}
.zona .zq{grid-column:2;font-family:var(--data);font-size:.64rem;color:var(--faint)}
@media (max-width:52rem){
  /* debajo de este ancho manda la versión partida: el mapa ancho y las
     pastillas de ciudades sobran, que es lo que decían los nombres */
  /* .mapbox svg gana por especificidad, así que aquí hay que igualarla */
  .mapbox svg.map-ancho,.map-cities{display:none}
  .zonas{display:block}
  .mapbox{padding:.9rem 1.1rem .8rem}
}
@media (prefers-reduced-motion:reduce){
  .map-arc{animation:none;stroke-dashoffset:0}
}

/* ── cruce entre registros ─────────────────────────
   La bisagra entre los dos registros. Cada cruce son dos pantallas que se
   miran: la que sale y la que entra, cada una con su frase. Donde hay
   soporte, el escenario se clava y el scroll vertical se convierte en
   desplazamiento lateral: mientras dura el cruce la página no baja, se
   traslada. Donde no lo hay, las dos pantallas quedan como dos bloques
   apilados y el corte es seco. El texto es el mismo en los dos casos:
   nada de lo que se lee depende de que la animación funcione. */
.shift{position:relative}
.shift-face{padding:4rem 0}
/* Sin traslado, la pantalla que sale no pinta nada: su contenido es el eco
   de un titular que ya se ha leído y suelto quedaría como una repetición
   sin motivo. En el respaldo solo queda el bloque que entra. */
.shift-face.out{display:none;background:var(--surface);color:var(--ink)}
.shift-face.in{background:var(--d-surface);color:var(--d-ink)}
.shift.back .shift-face.out{background:var(--d-surface);color:var(--d-ink)}
.shift.back .shift-face.in{background:var(--surface);color:var(--ink)}
.shift .wrap{width:100%}
/* La pantalla que sale lleva el nombre de la mitad que se deja atrás, con
   las dos mitades del héroe: se sale de «Mente de negocio.» hacia las
   manos de ingeniero, y se sale de «Manos de ingeniero.» hacia el
   contacto. Cada eco va con la tipografía de su mitad —la del héroe
   arriba: azul de palo en la clara, cursiva crema en la oscura— así que
   la bisagra se reconoce sin leerla. */
.hinge-echo{font-family:var(--head);font-weight:600;color:var(--brand);
  font-size:clamp(2.1rem,7.2vw,5.6rem);line-height:1.06;letter-spacing:-.045em;margin:0}
.shift.back .hinge-echo{font-weight:500;font-style:italic;color:var(--d-ink)}
/* el filo que cruza la pantalla y acaba en flecha: la pista de que aquí no
   se baja, se sale por la derecha */
.hinge-go{display:flex;align-items:center;gap:.9rem;margin:1.7rem 0 0;color:var(--muted)}
.hinge-go::before{content:'';flex:1;height:1px;background:currentColor;opacity:.4}
.hinge-go::after{content:'→';font-size:1.15rem;opacity:.6;line-height:1}
.shift.back .hinge-go{color:var(--d-dim)}
/* la coletilla de cada mitad viaja con su titular, en la pantalla que entra */
.shift-face.in .eyebrow{color:var(--d-dim)}
.shift-face.in .eyebrow::before{background:var(--d-accent)}
.shift.back .shift-face.in .eyebrow{color:var(--muted)}
.shift.back .shift-face.in .eyebrow::before{background:var(--brand)}
/* color:inherit — si no, gana la regla general de h2 y la frase sale azul
   marca sobre el negro */
.hinge-b{font-family:var(--head);font-weight:500;font-style:italic;color:inherit;
  font-size:clamp(2.1rem,7.2vw,5.6rem);line-height:1.06;letter-spacing:-.045em;margin:0}
.hinge-b em{font-style:italic;color:var(--d-accent)}
.shift.back .hinge-b em{color:var(--brand)}
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    /* La línea de tiempo se declara en el carril y se referencia por nombre:
       el escenario lleva overflow:hidden y, si la declarásemos ahí, sería
       contenedor de scroll y el progreso se quedaría congelado.
       El rango «contain» dura exactamente lo que dura el pegado: la altura
       del carril menos la pantalla. Por eso encajan al fotograma. */
    /* El carril va en svh —valor fijo, no cambia al plegarse la barra del
       navegador— para que el alto del documento no baile. El escenario, en
       cambio, va en dvh: es el alto que se ve de verdad en cada momento.
       Con svh se quedaba corto en móvil en cuanto la barra se retraía y
       por debajo asomaba una franja del carril, blanca, que luego se comía
       el registro oscuro al seguir bajando.
       Y encaja solo: el pegado dura carril − escenario = 135svh − dvh, y
       el rango «contain» dura carril − pantalla = 135svh − dvh. Lo mismo,
       esté la barra plegada o no. */
    .shift{height:135svh;view-timeline:--cruce block}
    .shift-stage{position:sticky;top:0;height:100dvh;overflow:hidden}
    /* 1 % muerto en cada punta: en el arranque el progreso no cae en un
       cero exacto y quedaba una columna de 1 px de la pantalla que entra
       asomando por el canto derecho, con su texto cortado. Con el margen,
       el carril se queda quieto en 0 y en −50 % de verdad. */
    .shift-track{display:flex;width:200%;height:100%;will-change:transform;
      animation:cruza linear both;animation-timeline:--cruce;
      animation-range:contain 1% contain 99%}
    /* overflow:hidden en cada pantalla, o el texto rezagado se sale de su
       panel y aparece con el color del otro registro sobre el fondo ajeno.
       El padding superior descuenta la cabecera: centra en lo que se ve. */
    .shift-face{flex:0 0 50%;height:100%;display:flex;align-items:center;
      padding:var(--tb,0px) 0 1.25rem;overflow:hidden}
    /* La puerta: una banda del registro que viene, pegada al canto derecho
       de la pantalla que sale. Se ve desde el primer momento, así que esa
       pantalla nunca es un blanco entero —que es lo que se leía como hueco,
       y como final de la web— y se entiende que aquí se sigue de lado.
       Va como pseudoelemento de la cara, no como panel: así se mueve pegada
       a ella sin tocar la maquetación de lo que entra. El filo termina justo
       donde empieza la banda, apuntando a la puerta. */
    .shift-face.out{display:flex;position:relative}
    .shift-face.out::after{content:'';position:absolute;top:0;right:0;bottom:0;
      width:9%;background:var(--d-surface)}
    .shift.back .shift-face.out::after{background:var(--surface)}
    /* solo el filo se recorta contra la banda; la frase conserva el ancho
       entero, que si no parte en dos líneas sin necesidad */
    .shift-face.out .hinge-go{margin-right:9%}
    /* El último cruce es el final de la página: si su contenido se centra,
       queda un vacío entre los botones y el pie. Se apoya abajo. */
    /* El último cruce es el final de la página: el pie viaja dentro de la
       pantalla que entra, empujado abajo con margin-top:auto, y el contacto
       se centra en lo que queda. Antes el bloque iba pegado al pie y dejaba
       media pantalla de blanco vacío encima —el hueco que se veía al pasar
       de negro a blanco— y además sobraba un trozo entre los botones y el
       pie. Así la página acaba justo donde acaba el escenario. */
    .shift.back .shift-face{align-items:stretch}
    .shift.back .shift-face .wrap{display:flex;flex-direction:column;height:100%;
      justify-content:center}
    /* los dos márgenes automáticos reparten el hueco por igual: el contacto
       queda centrado en lo que sobra y el pie se apoya al final */
    .shift.back .outro{margin-top:auto;margin-bottom:auto}
    .shift.back footer{margin-top:0;padding-bottom:1rem}
    /* el texto va un paso por detrás del panel: eso es lo que da fondo al
       traslado en vez de dejarlo en un barrido plano */
    .shift .wrap{animation:linear both;animation-timeline:--cruce;
      animation-range:contain 1% contain 99%}
    .shift-face.out .wrap{animation-name:rezaga-sale}
    .shift-face.in .wrap{animation-name:rezaga-entra}
  }
}
@keyframes cruza{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@keyframes rezaga-sale{from{transform:translateX(0)}to{transform:translateX(18%)}}
@keyframes rezaga-entra{from{transform:translateX(-18%)}to{transform:translateX(0)}}
/* Dentro del cruce no hay scroll: lo que entra tiene que caber en una
   pantalla. Cuando la pantalla es baja —portátil de 720, móvil apaisado—
   la frase y el texto encogen juntos en lugar de salirse por abajo. */
@media (max-height:760px){
  .hinge-b,.hinge-echo{font-size:clamp(1.9rem,5.4vw,3.6rem)}
  .turn .hinge-b{margin-bottom:1.1rem}
  .turn p{font-size:.96rem}
  .shift .eyebrow{margin-bottom:1rem}
}
@media (max-height:620px){
  .hinge-b,.hinge-echo{font-size:clamp(1.6rem,4.2vw,2.5rem)}
  .turn .hinge-b{margin-bottom:.75rem}
  .turn p{font-size:.88rem;line-height:1.5}
  .turn p+p{margin-top:.55rem}
  .shift .eyebrow{font-size:.85rem;margin-bottom:.7rem}
  .hinge-go{margin-top:1.1rem}
}
/* móvil apaisado: la cabecera se come más de la mitad de la pantalla */
@media (max-height:450px){
  .hinge-b,.hinge-echo{font-size:clamp(1.35rem,3.2vw,1.9rem)}
  .turn .hinge-b{margin-bottom:.5rem}
  .turn p{font-size:.8rem;line-height:1.45}
  .turn p+p{margin-top:.4rem}
  .shift .eyebrow{font-size:.78rem;margin-bottom:.5rem}
  .hinge-go{margin-top:.8rem}
}

/* ═══════════════ REGISTRO OSCURO ═══════════════════ */
/* Sigue en negro justo donde lo dejó la pantalla que entra en el cruce:
   sin margen, o se colaría una franja blanca entre las dos. */
.dark{background:var(--d-surface);color:var(--d-ink);padding-bottom:5rem}
.dark a{color:var(--d-accent)}
.dark a:focus-visible{outline-color:var(--d-accent)}
/* «Manos de ingeniero» viaja entero dentro de la pantalla que entra:
   frase y texto llegan juntos, no el titular por un lado y el cuerpo
   esperando abajo */
.turn .hinge-b{margin-bottom:1.4rem}
.turn p{color:var(--d-muted);max-width:46rem;margin:0;font-size:1.02rem}
.turn p+p{margin-top:.9rem}
.turn p b{color:var(--d-ink);font-weight:600}
.dark section{padding:4rem 0 0}
.dark .shead{color:var(--d-dim);border-bottom-color:var(--d-line)}
.dark h2{color:var(--d-ink);font-size:clamp(1.7rem,4vw,2.7rem);margin-bottom:1.4rem}
.dark h2 em{color:var(--d-accent);font-style:italic}
.dark h3{color:var(--d-ink)}

/* escenarios */
/* align-items:start — con el cartel dentro, una tarjeta mide casi el doble
   que la otra y estirarlas dejaba media tarjeta de negro vacío */
.talks{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;align-items:start}
.talk{background:var(--d-card);border:1px solid var(--d-line);border-radius:var(--r);padding:1.6rem}
.talk-top{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem}
.talk .k{font-family:var(--data);font-size:.66rem;letter-spacing:.05em;color:var(--d-accent);
  text-transform:uppercase;display:inline-flex;align-items:center;gap:.5rem}
.talk .k img{height:12px;width:16px;object-fit:cover;border-radius:1px}
.talk-logo{height:40px;width:auto;flex:none}
.talk h3{font-family:var(--head);font-weight:600;font-style:italic;
  font-size:clamp(1.9rem,3.6vw,2.6rem);letter-spacing:-.035em;line-height:1.1;
  margin:1.1rem 0 .5rem;color:var(--d-ink)}
.talk .venue{font-family:var(--data);font-size:.72rem;color:var(--d-dim);
  padding-bottom:.9rem;border-bottom:1px solid var(--d-line);margin-bottom:.9rem}
.talk p{margin:0;font-size:.9rem;color:var(--d-muted)}
/* el cartel del panel: la prueba de que estuvo, con quién y de qué */
.talk-cartel{width:100%;height:auto;display:block;border-radius:3px;
  border:1px solid var(--d-line);margin:0 0 1rem}
/* el cartel se pulsa siempre: si hay grabación lleva a ella, y si no la
   hay se abre a pantalla completa. El botón va siempre visible: un enlace
   que no se ve no lo pulsa nadie. */
.cartel-link{position:relative;display:block;margin:0 0 1rem;border-radius:3px}
.cartel-link .talk-cartel{margin:0;transition:border-color .18s ease}
/* arriba a la derecha: abajo a la izquierda el botón se comía el nombre
   del primer ponente del cartel de Geotab */
.cartel-play{position:absolute;right:.65rem;top:.65rem;width:2.15rem;height:2.15rem;
  display:grid;place-items:center;border-radius:3px;background:rgba(15,15,18,.82);
  border:1px solid var(--d-line);transition:background .18s ease,border-color .18s ease}
.cartel-play svg{width:1.15rem;height:1.15rem;fill:var(--d-ink);transition:fill .18s ease}
.cartel-link:hover .talk-cartel,.cartel-link:focus-visible .talk-cartel{border-color:var(--d-accent)}
.cartel-link:hover .cartel-play,.cartel-link:focus-visible .cartel-play{
  background:var(--d-accent);border-color:var(--d-accent)}
.cartel-link:hover .cartel-play svg,.cartel-link:focus-visible .cartel-play svg{fill:var(--d-surface)}
/* el de ampliar es de trazo, no de relleno: dice «se hace grande», no «se reproduce» */
.cartel-play.zoom svg{fill:none;stroke:var(--d-ink);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;transition:stroke .18s ease}
.cartel-link:hover .cartel-play.zoom svg,.cartel-link:focus-visible .cartel-play.zoom svg{
  fill:none;stroke:var(--d-surface)}
.cartel-link:focus-visible{outline:2px solid var(--d-accent);outline-offset:3px}
.talk-link{display:inline-block;margin-top:1rem;font-family:var(--data);font-size:.72rem;
  color:var(--d-accent);border-bottom:1px solid transparent}
.talk-link:hover{text-decoration:none;border-bottom-color:var(--d-accent)}
.dark .scroller{border-color:var(--d-line)}
.dark td{border-bottom-color:var(--d-line);padding:.85rem .9rem}
.dark tbody tr:nth-child(odd){background:var(--d-card)}
.dark .what{color:var(--d-ink);font-size:.98rem}
/* El aviso de que la fila lleva algo detrás. Enlazar el título con una
   flecha pequeña no se veía; esto va aparte y con su etiqueta, como en la
   tarjeta de TVE. La miniatura dice, además, que lo que hay es una imagen. */
/* flex y no inline-flex: así rompe línea y cae debajo del título en vez de
   pegarse al final de la última palabra */
.rowlink{display:flex;align-items:center;gap:.5rem;margin-top:.5rem;
  font-family:var(--data);font-size:.7rem;line-height:1.2;color:var(--d-accent);
  border-bottom:1px solid transparent;width:fit-content}
.rowlink:hover,.rowlink:focus-visible{text-decoration:none;border-bottom-color:var(--d-accent)}
.rowthumb{width:54px;height:31px;object-fit:cover;flex:none;display:block;
  border:1px solid var(--d-line);border-radius:2px;transition:border-color .18s ease}
.rowlink:hover .rowthumb{border-color:var(--d-accent)}

/* el diálogo de la imagen: encima de la página, sin sacar a nadie de ella */
.lightbox{border:0;padding:0;background:transparent;overflow:visible;
  max-width:min(96vw,1180px);max-height:94vh}
.lightbox::backdrop{background:rgba(8,8,11,.88)}
/* el desplazamiento vive en el cuerpo y no en el diálogo, para que la
   equis se quede quieta cuando la imagen se arrastra */
.lightbox-body{overflow:auto;max-height:88vh;border-radius:var(--r);
  background:var(--d-card);-webkit-overflow-scrolling:touch}
.lightbox img{display:block;width:100%;height:auto}
.lightbox-x{position:absolute;top:-.35rem;right:-.35rem;transform:translate(50%,-50%);
  width:2.1rem;height:2.1rem;border-radius:50%;border:1px solid var(--d-line);
  background:var(--d-card);color:var(--d-ink);font:400 1.1rem/1 var(--sans);
  cursor:pointer;display:grid;place-items:center;z-index:1}
.lightbox-x:hover{border-color:var(--d-accent);color:var(--d-accent)}
@media (max-width:52rem){
  /* Un cartel de 1600 px metido en 374 no se lee. Se manda por el alto y
     se arrastra a lo ancho, que es la única forma de que los nombres del
     ranking se distingan en un móvil. */
  .lightbox{max-width:94vw}
  .lightbox img{height:68vh;width:auto;max-width:none}
  .lightbox-x{transform:none;top:.4rem;right:.4rem;position:fixed}
}
.dark .where{color:var(--d-muted)}
.dark .when{color:var(--d-dim)}
.rank{color:var(--d-accent);font-weight:700}
/* Los logos de los organizadores son de tinta oscura o traen fondo blanco.
   Sobre el registro oscuro van en pastilla clara: legibles y sin recolorear
   marcas ajenas. */
.orgcell{width:9rem}
/* width:100% para que la pastilla llene la celda cuando hay sitio, y
   min-width como suelo: sin él la tabla aplasta la columna del logo hasta
   dejarla en nada cuando el texto aprieta. */
.orgchip{display:flex;align-items:center;justify-content:center;background:#FFFFFF;
  border-radius:3px;padding:5px 8px;width:100%;min-width:6.5rem;min-height:36px}
.orgchip img{height:20px;width:auto;max-width:100%;object-fit:contain;display:block}
/* los logotipos de dos líneas necesitan más alto: a la misma que los de una,
   cada palabra sale a la mitad de tamaño y no se lee */
.orgchip img.dosl{height:26px}
@media (max-width:52rem){
  .orgcell{width:6.5rem}
  .orgchip{min-width:4.6rem;min-height:30px;padding:4px 6px}
  .orgchip img{height:15px}
  .orgchip img.dosl{height:21px}
}

/* la red de agentes: el bloque destacado */
.net{border:1px solid var(--d-accent);border-radius:var(--r);background:var(--d-card);
  padding:1.8rem;margin-bottom:1.5rem}
.net .k{font-family:var(--data);font-size:.66rem;letter-spacing:.07em;text-transform:uppercase;
  color:var(--d-accent);display:block;margin-bottom:.7rem}
.net h3{font-family:var(--head);font-weight:600;font-size:1.4rem;letter-spacing:-.03em;margin:0 0 .8rem}
.net p{color:var(--d-muted);margin:0 0 1.4rem;max-width:52rem}
.net p b{color:var(--d-ink);font-weight:600}
/* separadores por borde, no por gap: con 7 agentes en 4 columnas el hueco
   sobrante mostraba el color de la rejilla y parecía una celda rota */
.agents{display:grid;grid-template-columns:repeat(auto-fit,minmax(13.5rem,1fr));
  background:var(--d-surface);border:1px solid var(--d-line);border-radius:var(--r);overflow:hidden}
.agent{padding:1rem 1.1rem;border-right:1px solid var(--d-line);border-bottom:1px solid var(--d-line)}
.agent .t{font-family:var(--data);font-size:.63rem;letter-spacing:.05em;text-transform:uppercase;
  color:var(--d-accent);display:block;margin-bottom:.35rem}
.agent .d{font-size:.85rem;color:var(--d-muted);line-height:1.5}

/* producto */
.prod{border:1px solid var(--d-line);background:var(--d-card);border-radius:var(--r);
  padding:1.8rem;margin-bottom:1.25rem}
.prod-head{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;flex-wrap:wrap}
.prod h3{font-family:var(--head);font-weight:600;font-size:1.5rem;letter-spacing:-.03em;margin:0}
.prod .kind{font-family:var(--data);font-size:.66rem;letter-spacing:.05em;color:var(--d-accent);
  text-transform:uppercase}
.prod .sum{color:var(--d-muted);margin:.8rem 0 1.4rem;max-width:52rem}
.prod .sum b{color:var(--d-ink);font-weight:600}
.evid{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1px;
  background:var(--d-line);border:1px solid var(--d-line);border-radius:var(--r);overflow:hidden}
.ev{background:var(--d-surface);padding:1.1rem 1.15rem}
.ev .t{font-family:var(--data);font-size:.63rem;letter-spacing:.06em;color:var(--d-accent);
  text-transform:uppercase;display:block;margin-bottom:.5rem}
.ev .h{font-family:var(--head);font-weight:600;font-size:1.02rem;color:var(--d-ink);
  display:block;line-height:1.3;letter-spacing:-.02em}
.ev .d{font-size:.85rem;color:var(--d-muted);display:block;margin-top:.45rem;line-height:1.55}
.dtags{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:1.3rem}
.dtag{font-family:var(--data);font-size:.68rem;color:var(--d-muted);border:1px solid var(--d-line);
  border-radius:var(--r);padding:.24rem .58rem}
.prod-links{display:flex;gap:1.4rem;flex-wrap:wrap;margin-top:1.3rem;
  font-family:var(--data);font-size:.78rem;align-items:center}
.soon{color:var(--d-dim)}

/* contacto */
.cta{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:.5rem}
.btn{font-size:.9rem;font-weight:500;padding:.62rem 1.15rem;border-radius:var(--r);
  border:1px solid var(--brand);color:var(--brand);background:var(--surface)}
.btn.pri{background:var(--brand);color:var(--on-brand);font-weight:600}
.btn:hover{text-decoration:none;background:var(--band);color:var(--brand)}
.btn.pri:hover{background:var(--brand-deep);color:var(--on-brand)}
/* botón con logotipo: el icono toma el color del texto, así sigue el
   botón en reposo y al pasar por encima sin reglas aparte */
.btn.con-ico{display:inline-flex;align-items:center;gap:.5rem}
.btn .ico{width:1.05em;height:1.05em;fill:currentColor;flex:none;display:block}

/* ── botón flotante de WhatsApp ────────────────────────────
   Abierto en la primera pantalla y cada vez que se sube; recogido a
   redondel al bajar, que es cuando estorba. En azul de marca y no en
   verde: el verde lo delataría como widget pegado, y la silueta del
   logotipo ya se reconoce sola.
   El aro claro no se ve sobre blanco y le da filo sobre las pantallas
   negras del cruce, que es por donde también pasa. */
.fab{position:fixed;right:1.25rem;z-index:35;
  bottom:calc(1.25rem + env(safe-area-inset-bottom,0px));
  display:flex;align-items:center;gap:.6rem;height:3.25rem;padding:0 1.1rem;
  border-radius:999px;background:var(--brand);color:var(--on-brand);
  font-family:var(--head);font-weight:600;font-size:.92rem;letter-spacing:-.2px;
  box-shadow:0 0 0 1px rgba(242,237,228,.18),0 10px 26px -10px rgba(0,20,80,.55);
  transition:padding .28s ease,opacity .22s ease,transform .28s ease;overflow:hidden}
.fab:hover{background:var(--brand-deep);color:var(--on-brand);text-decoration:none}
.fab:focus-visible{outline:2px solid var(--brand);outline-offset:3px}
.fab .ico{width:1.5rem;height:1.5rem;fill:currentColor;flex:none;display:block}
/* El texto se estruja en vez de desaparecer de golpe: así el redondel
   sale del propio botón y no parece otro elemento distinto. */
.fab-txt{white-space:nowrap;max-width:11rem;overflow:hidden;
  transition:max-width .28s ease,opacity .18s ease}
/* Redondel es todo lo que no está abierto, escondido incluido. Escrito así
   y no listando estados: cuando el esconderse era un estado aparte, dejaba
   de aplicar el ancho recogido y el botón se ensanchaba al desvanecerse. */
.fab:not([data-fab="abierto"]){padding:0;width:3.25rem;justify-content:center;gap:0}
.fab:not([data-fab="abierto"]) .fab-txt{max-width:0;opacity:0}
/* Al final sobra: ahí ya está el botón de «Hablamos» a tamaño completo. */
.fab[data-fab="oculto"]{opacity:0;transform:translateY(.75rem) scale(.85);
  pointer-events:none}
@media (prefers-reduced-motion:reduce){
  .fab,.fab-txt{transition:none}
}
@media (max-width:30rem){
  .fab{right:.9rem;bottom:calc(.9rem + env(safe-area-inset-bottom,0px));
    height:3rem;font-size:.88rem}
  .fab:not([data-fab="abierto"]){width:3rem}
}
footer{margin-top:4rem;padding:1.3rem 0 3.5rem;border-top:1px solid var(--line);
  font-family:var(--data);font-size:.7rem;color:var(--muted);display:flex;flex-wrap:wrap;gap:.4rem 1.5rem}

/* tablas comunes */
.scroller{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r)}
table{width:100%;border-collapse:collapse;font-variant-numeric:tabular-nums}
caption{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
td{padding:.8rem .9rem;border-bottom:1px solid var(--line);vertical-align:middle;text-align:left}
tr:last-child td{border-bottom:0}
tbody tr:nth-child(odd){background:var(--band)}
td:last-child{text-align:right}
.what{font-weight:500}
.where{color:var(--muted);font-size:.88rem}
.when{font-family:var(--data);font-size:.72rem;color:var(--muted);white-space:nowrap}

@media (max-width:64rem){
  .hero-grid{grid-template-columns:1fr;gap:2.5rem}
  .case{max-width:34rem}
  .edu{grid-template-columns:1fr 1fr}
}
@media (max-width:52rem){
  .certs{grid-template-columns:1fr;gap:1rem;padding-top:2rem}
  .figures{grid-template-columns:1fr 1fr}
  .talks{grid-template-columns:1fr}
  .who{grid-template-columns:8rem minmax(0,1fr);gap:1.5rem}
}
@media (max-width:40rem){
  .wrap{padding:0 1.25rem}
  .role{grid-template-columns:1fr;gap:.7rem}
  .rail{text-align:left}
  .role-head{flex-direction:column}
  .logos{justify-content:flex-start;flex-wrap:wrap}
  .who{grid-template-columns:1fr}
  .portrait{max-width:9rem}
  .edu{grid-template-columns:1fr}
  .taglabel{width:auto}
}

@media print{
  .top nav,.top .lang,.cta,.fab{display:none}
  body,.dark,.h1bar,.shift-face{background:#fff;color:#000;font-size:10pt}
  .h1bar span,.hinge-b,.hinge-b em,.dark h2,.dark h3,.dark *{color:#000!important}
  .hinge-echo,.hinge-go{display:none}
  .shift{height:auto}
  .shift-stage{position:static;height:auto;overflow:visible}
  .shift-track{display:block;width:auto;height:auto;animation:none;transform:none}
  .shift-face{display:block;height:auto;padding:1.2rem 0 0;break-inside:avoid}
  .shift-face.out{display:none}
  .shift .wrap{display:block;height:auto}
  .wrap{max-width:none;padding:0}
  section,.turn{padding-top:1.2rem;break-inside:avoid}
  .h1a,.h1bar span,.hinge-b{font-size:20pt}
  h2{font-size:13pt;color:#000}
  .role,.talk,.prod,.edu-item,.net,.mapbox{break-inside:avoid;background:none;border-color:#999}
  tbody tr:nth-child(odd),.tag,.dtag,.case,.ev,.agent{background:none}
  .dark{border-top:2px solid #000}
  a{color:#000}
  .now{background:#000;color:#fff}
}
