:root{
  /* Marca. --color-brand es fondo, borde, subrayado y sombra, y nunca tinta */
  --color-brand:#FFD700;
  --color-brand-ink-on-dark:#FFD700;
  --color-brand-ink-on-light:#826E00;
  --color-ink-on-brand:#1A1A1A;

  /* Superficies claras: el suelo del sitio, styles.css:18-21 y :1025 */
  --color-ground:#FFFFFF;
  --color-surface:#F9F9F9;

  /* Superficies oscuras: las secciones isla, styles.css:1198 :1660 :1682 :1687 */
  --color-ground-dark:#000000;
  --color-surface-dark:#0A0A0A;
  --color-surface-dark-raised:#111111;

  /* Tintas sobre superficie clara */
  --color-ink-on-light:#1A1A1A;
  --color-ink-on-light-2:#555555;
  --color-ink-on-light-3:#666666;

  /* Tintas sobre superficie oscura. Ninguna de las tres vale sobre claro */
  --color-ink-on-dark:#FFFFFF;
  --color-ink-on-dark-2:#AAAAAA;
  --color-ink-on-dark-3:#999999;

  /* Líneas opacas. Las translúcidas salen de las escaleras de velo de abajo */
  --color-line:#222222;
  --color-line-strong:#000000;

  /* Velo claro: los rgba() blancos que sí tienen quien los consuma en esta tanda (F2.5, DECISIONS.md §15). `-mid` y `-strong` se borraron: sólo servían al menú móvil y al formulario de newsletter, ninguno de esta tanda. `ground-strong` entra el 2026-10-04 con la cabecera: es el suelo casi opaco de la barra pegajosa (styles.css:73, .navbar), y es un velo y no --color-ground porque lo que pasa por debajo tiene que insinuarse */
  --color-veil-light-weak:rgba(255,255,255,.05);
  --color-veil-light-soft:rgba(255,255,255,.10);
  --color-veil-ground-strong:rgba(255,255,255,.97);

  /* Velo de marca: los rgba() amarillos. `-weak`/`-mid` ya tenían consumidor (F2.5, DECISIONS.md §15); `-soft`/`-strong` se habían borrado por no tenerlo ("sólo servían al loading screen, que no corre en esta tanda", §8.5) y la casilla 2 de F6-bis los restaura porque ahora sí corre (DECISIONS.md §63). `-weak-2` y `-strong-2` son peldaños nuevos que esa misma casilla añadió: no existían en la escala original .08/.15/.40/.70 */
  --color-veil-brand-weak:rgba(255,215,0,.08);
  --color-veil-brand-weak-2:rgba(255,215,0,.10);
  --color-veil-brand-soft:rgba(255,215,0,.15);
  --color-veil-brand-mid:rgba(255,215,0,.40);
  --color-veil-brand-strong:rgba(255,215,0,.70);
  --color-veil-brand-strong-2:rgba(255,215,0,.90);

  /* Velo ámbar: un matiz distinto al amarillo de marca (255,200,0 contra 255,215,0 — no es el mismo valor redondeado, DECISIONS.md §63), usado una sola vez por el loading screen (`.orbit-bolt`, el `drop-shadow` exterior de los dos que apila) */
  --color-veil-amber:rgba(255,200,0,.6);

  /* Sombras: los rgba() negros que sí tienen quien los consuma (F2.5, DECISIONS.md §15). `-hard` y `scrim-strong` se habían borrado por no tener consumidor ("lightbox con JS, no portado esta tanda", §15.3) y la casilla 6 de F6-bis los restaura porque el modal de galería ya corre (DECISIONS.md §69). `scrim` (el velo del menú móvil) sigue sin consumidor y sigue sin restaurarse */
  --color-shadow-soft:rgba(0,0,0,.08);
  --color-shadow:rgba(0,0,0,.20);
  --color-shadow-hard:rgba(0,0,0,.50);
  --color-scrim-strong:rgba(0,0,0,.90);

  /* Estado de una convocatoria. Medidos sobre oscuro: sobre blanco dan 2,54 y 2,15 */
  --color-state-open-on-dark:#10B981;
  --color-state-soon-on-dark:#F59E0B;
}

/* ── TIPOGRAFÍA AUTOALOJADA ────────────────────────────────────────────────
   Escrito el 2026-10-05. Sustituye al enlace a Google Fonts que esta hoja
   estrenó el día anterior: el criterio de la casa es que **el sitio no haga ni
   una petición a un tercero**, y es lo que hace el patrón
   (focalius-paginaweb, que autoaloja su Bebas Neue en '/tipos/').

   Cuatro archivos y no diez, que es el hallazgo de traerlas. Se pidieron los
   diez pares (familia, peso) que la hoja usa de verdad y Google devolvió diez
   '.woff2' de los que **sólo cuatro eran distintos**, comprobado por SHA-256:
   Caveat sirvió el mismo archivo para 600 y 700, Inter para 400/500/600 y
   Space Grotesk para los cuatro pesos. Son **fuentes variables**: un archivo
   cubre todo su eje de peso, y por eso cada '@font-face' declara un RANGO
   ('font-weight:400 700') en vez de un número. Declarar diez caras habría
   bajado 397 KB para servir 156.

   Los rangos no se inventan: son los que declara el propio CSS de Google para
   cada familia (Bebas Neue 400 — no es variable —, Caveat 400-700, Inter
   100-900, Space Grotesk 300-700). El 'unicode-range' es su subconjunto
   'latin' tal cual, que es lo que hace que un navegador no descargue nada si
   la página no usa esos caracteres.

   'font-display:swap': el texto se pinta desde el primer momento con la pila
   del sistema y cambia al llegar la fuente. Sin esto, el titular de la portada
   —que es Caveat, el archivo más pesado de los cuatro— se queda invisible
   mientras baja.

   Licencias: las cuatro son OFL 1.1, en 'tipos/LICENCIA.md'. */
@font-face{
  font-family:'Bebas Neue';font-style:normal;font-weight:400;font-display:swap;
  src:url('/tipos/bebas-neue-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;
}
@font-face{
  font-family:'Caveat';font-style:normal;font-weight:400 700;font-display:swap;
  src:url('/tipos/caveat-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;
}
@font-face{
  font-family:'Inter';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('/tipos/inter-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;
}
@font-face{
  font-family:'Space Grotesk';font-style:normal;font-weight:300 700;font-display:swap;
  src:url('/tipos/space-grotesk-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;
}

*{box-sizing:border-box}
/* color-scheme light y no dark: el suelo del sitio es blanco (styles.css:18-21).
   Lo que es negro son secciones isla, y ésas se pintan solas. */
/* 'scroll-padding-top' = alto de la cabecera pegajosa. Tres enlaces del menú
   son anclas de la portada ('/#servicios', '/#proyectos', '/#nosotros') y sin
   esto el navegador deja el destino justo DEBAJO de la barra: el visitante
   pulsa «SERVICIOS» y el título de servicios es lo único que no ve. Añadido el
   2026-10-04 con la cabecera. */
html{-webkit-text-size-adjust:100%;color-scheme:light;scroll-padding-top:80px}
/* La tipografía de verdad es de la F3: aquí va la pila del sistema para que el
   paso 6 pueda abrir la portada sin pedirle una fuente a nadie. */
body{
  margin:0; background:var(--color-ground); color:var(--color-ink-on-light);
  font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  font-size:16px; line-height:1.6; -webkit-font-smoothing:antialiased;
}
/* Las 37 <img> de la portada son el hallazgo de peso de la F3; que ninguna se
   salga del ancho es lo único que este paso puede garantizar de ellas. */
img,svg{max-width:100%}

/* El patrón escribe este mismo bloque con su negro de marca DOS veces en
   literal, fuera de su propio bloque de tokens
   (focalius-paginaweb/plantilla.mjs:86-87). Es el vecino incumpliendo la regla
   que aquí es criterio de aceptación, así que se copia la forma y no el valor:
   va con el token. DECISIONS.md §4.4.
   Y en esta hoja no hay ni un hex dentro de un comentario, tampoco: si el
   contador tuviera que ignorar comentarios para dar cero, su cero no valdría. */
.saltar{position:absolute;left:-9999px}
.saltar:focus{
  left:8px;top:8px;z-index:10;
  background:var(--color-brand);color:var(--color-ink-on-brand);
  padding:10px 14px;border:2px solid var(--color-ink-on-brand);
}

/* El enlace se distingue por el subrayado, no por el color: el amarillo sobre
   blanco da 1,40:1 y como tinta es ilegible (F4.5, styles.css:727). El patrón
   fija aquí su tinta con su token de tinta; en quak es inherit porque el mismo
   enlace cae sobre el blanco del body Y dentro de una sección negra, y heredar
   la tinta de su contenedor es lo único que acierta en los dos sitios sin
   duplicar la regla ni elegir mal por defecto. */
a{
  color:inherit;text-decoration:underline;
  text-decoration-color:var(--color-brand);
  text-decoration-thickness:3px;text-underline-offset:3px;
}
a:hover{text-decoration-color:currentColor}
/* currentColor por el mismo motivo: un anillo amarillo se ve dentro de una
   sección negra (14,97:1) y desaparece sobre el blanco del body (1,40:1). Hoy
   hay 0 :focus-visible en las 7 páginas y 2 outline:none. */
a:focus-visible,button:focus-visible,[tabindex]:focus-visible{
  outline:3px solid currentColor;outline-offset:3px;
}

/* Hoy hay 15 animaciones y las 15 son infinite: ni una finita. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   HERO Y SERVICIOS — paso 2 (F2.1 de PASOS.md), escrito el 2026-09-30.
   Portado de styles.css:211-523 (mitad de arriba de la portada vieja). Cada
   valor sale de un token de arriba; la tabla completa de qué literal viejo
   fue a qué token queda en el resumen del paso. Tres decisiones que conviene
   leer aquí y no sólo en esa tabla:

   1. '.scroll-indicator span' traía un gris claro de ~2,85:1 sobre blanco,
      por debajo del mínimo AA de 4,5:1. Sube a --color-ink-on-light-3
      (5,74:1): es un cambio de contraste deliberado, no un mapeo por
      parecido visual.
   2. Los SVG inline (doodles, corona, rayos de la mascota, mouse del scroll,
      iconos de servicio) pintaban su trazo/relleno con el hex literal
      dentro del propio <svg>. Aquí llevan 'currentColor', y el color de
      verdad lo fija la propiedad 'color' del contenedor — el mismo patrón
      que ya usa este archivo en '.saltar' y en los focus rings de arriba.
   3. El filtro invert()/sepia()/hue-rotate(...) que el modo oscuro de
      móvil aplicaba sobre '.service-icon svg' SE RETIRA. Con 'currentColor'
      el icono ya hereda --color-brand en todos los anchos de pantalla, y
      ese filtro era una segunda receta de color —afinada a mano para un hex
      concreto— que ya no cambia nada. Dejarlo sería esconder un literal de
      color dentro de un 'filter', que es justo lo que este archivo existe
      para no tener.
   ════════════════════════════════════════════════════════════════════════ */

/* Elementos 3D flotantes: mecanismo compartido. Hoy lo usan hero y
   servicios; proyectos y nosotros (F2.1, paso 3) añaden sus propias clases
   de posición sobre el mismo '.floating-3d'/'.float-anim-*'. */
.floating-3d{position:absolute;z-index:1;pointer-events:none}
.float-anim-1{animation:float3d 6s ease-in-out infinite}
.float-anim-2{animation:float3dalt 8s ease-in-out infinite}
.float-anim-3{animation:float3d 7s ease-in-out infinite reverse}
.float-anim-4{animation:float3dalt 9s ease-in-out infinite reverse}
@keyframes float3d{
  0%{transform:translateY(0px) rotate(0deg)}
  50%{transform:translateY(-30px) rotate(5deg)}
  100%{transform:translateY(0px) rotate(0deg)}
}
@keyframes float3dalt{
  0%{transform:translateY(0px) rotate(0deg) scale(1)}
  50%{transform:translateY(20px) rotate(-8deg) scale(1.05)}
  100%{transform:translateY(0px) rotate(0deg) scale(1)}
}
.star-hero{top:12%;right:8%;width:140px;opacity:.9}
.torus-hero{bottom:5%;left:4%;width:120px;opacity:.85}
.torus-hero-2{top:35%;left:50%;width:80px;opacity:.7;filter:hue-rotate(140deg)}
.star-hero-3{display:none;bottom:45%;right:15%;width:90px;opacity:.75;filter:hue-rotate(200deg)}
.star-about{top:5%;right:5%;width:130px;opacity:.8}
.torus-srv{bottom:20%;left:10%;width:130px;opacity:.75;filter:hue-rotate(60deg)}
.star-srv-2{top:60%;left:35%;width:90px;opacity:.7;filter:hue-rotate(180deg)}

/* ── CABECERA ──────────────────────────────────────────────────────────────
   Escrito el 2026-10-04, después de abrir el sitio ya publicado y verlo sin
   cabecera. **No es un rediseño: es el hueco de una.** 'cabecera()' emitía
   '.cabecera', '.cabecera-marca', '.cabecera-logo', '.cabecera-menu',
   '.cabecera-enlace', '.activa', '.cabecera-acciones', '.cabecera-accion' y
   '.cabecera-cta' desde la F2, y esta hoja no tenía **ni una** regla para
   ninguna de las nueve. Medido en la página publicada: de las 112 clases que
   usa la portada, 101 tenían regla y 11 no — y 9 de esas 11 son estas. El
   navegador pintaba un '<header>' en 'display:block', 'position:static' y
   fondo transparente, o sea los siete enlaces en fila corrida, subrayados por
   la regla global de 'a', encima del hero.

   El diseño no se inventa aquí: se porta del original, 'styles.css:67-180'
   ('.navbar', '.nav-container', '.nav-logo', '.logo-img', '.nav-links',
   '.nav-link', '.nav-actions', '.portal-btn', '.nav-cta'), que es lo que las
   7 páginas de la raíz llevan desde siempre. Los colores pasan por el token
   que les toca, como todo lo demás de esta hoja.

   ## Tres cosas que SÍ cambian respecto del original, con su motivo

   1. **'sticky' y no 'fixed'.** El original fija la barra y compensa con
      '.hero{padding-top:80px}' — y eso sólo existe en el hero. Las otras seis
      páginas no reservan nada, así que una barra fija les taparía los primeros
      80 px: en 'privacidad' y 'cookies' eso es el título del documento legal.
      'sticky' ocupa su sitio en el flujo, así que **ninguna página necesita
      compensar nada** y no hay una familia de bugs esperando a la séptima
      página que alguien añada. El '.hero' se ajusta abajo en consecuencia.
   2. **No hay '.scrolled'.** El original le da sombra a la barra al bajar, con
      una clase que pone un JS que este sitio no tiene. En vez de dejar una
      regla muerta, la sombra es permanente y suave.
   3. **La activa no se marca sólo con color.** El original pinta '.active' de
      negro sobre el gris oscuro del resto: 1,05:1 entre los dos estados, que
      no es una señal. Aquí lleva el subrayado al 100 % **y** 'font-weight:700',
      y el marcado ya traía 'aria-current="page"', que es lo que de verdad lee
      un lector de pantalla.

   El área táctil de los enlaces sale del 'padding' vertical, no del tamaño de
   letra: 13 px de texto dejan una diana de 16 px de alto y aquí quedan 36.
   ──────────────────────────────────────────────────────────────────────── */
.cabecera{
  position:sticky;top:0;z-index:1000;
  display:flex;align-items:center;justify-content:space-between;gap:32px;
  height:80px;
  /* Barra a sangre, contenido centrado a 1400: el marcado no trae contenedor
     interior (styles.css sí: '.nav-container'), así que el centrado lo hace el
     propio padding en vez de un <div> que habría que meter en 'cabecera()'. */
  padding:0 max(48px,calc((100% - 1400px) / 2));
  background:var(--color-veil-ground-strong);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  border-bottom:1px solid var(--color-shadow-soft);
  box-shadow:0 2px 20px var(--color-shadow-soft);
}
.cabecera-marca{flex-shrink:0;display:flex;align-items:center;text-decoration:none}
/* 'multiply' por lo mismo que en el original: el PNG trae fondo blanco y sin
   esto se ve su caja contra el velo de la barra. */
.cabecera-logo{height:50px;width:auto;display:block;mix-blend-mode:multiply}
.cabecera-menu{display:flex;align-items:center;gap:36px;flex-wrap:wrap}
.cabecera-enlace{
  font-family:'Space Grotesk',sans-serif;
  font-size:13px;font-weight:600;letter-spacing:.12em;
  color:var(--color-ink-on-light);text-decoration:none;
  position:relative;white-space:nowrap;padding:10px 0;
  transition:color .3s ease;
}
.cabecera-enlace::after{
  content:'';position:absolute;bottom:4px;left:0;
  width:0;height:2px;background:var(--color-line-strong);
  transition:width .3s ease;
}
.cabecera-enlace:hover::after,.cabecera-enlace.activa::after{width:100%}
.cabecera-enlace.activa{font-weight:700}
.cabecera-acciones{display:flex;align-items:center;gap:12px;flex-shrink:0}
/* Las dos pastillas comparten borde de 2 px para que midan lo mismo de alto:
   el original le daba borde sólo a una y quedaban desalineadas por 4 px. */
.cabecera-accion,.cabecera-cta{
  font-family:'Space Grotesk',sans-serif;
  font-size:13px;font-weight:600;letter-spacing:.08em;
  padding:10px 24px;border-radius:999px;border:2px solid var(--color-line-strong);
  text-decoration:none;white-space:nowrap;transition:all .3s ease;
}
.cabecera-accion{background:transparent;color:var(--color-ink-on-light)}
.cabecera-accion:hover{background:var(--color-ground-dark);color:var(--color-ink-on-dark)}
.cabecera-cta{background:var(--color-ground-dark);color:var(--color-ink-on-dark)}
.cabecera-cta:hover{
  background:var(--color-brand);color:var(--color-ink-on-brand);
  border-color:var(--color-brand);
}

/* La etiqueta de sección. La usan cinco páginas —portada, eventos,
   convocatorias, expositores y trabajemos— y ninguna tenía regla: salía como
   texto corrido del tamaño del párrafo.

   **Copiada valor por valor de 'styles.css:.section-label'**, que es el
   diseño original: Space Grotesk 12/600, interletraje .2em, versalitas y
   gris '#666'. Ese gris es exactamente '--color-ink-on-light-3' (5,74:1), así
   que el mapeo es directo y no hay nada que decidir.

   Corregido el 2026-10-05, comparando contra 'VersionViejo/styles.css': la
   primera versión de esta regla la pintó de '--color-brand-ink-on-light' —el
   oliva de marca— por analogía con '.about-eyebrow', que vive sobre negro.
   **Era una invención mía, no el diseño**, y cambiaba el tono de la etiqueta
   en las cinco páginas. El interletraje también estaba en .15 y son .2. */
.section-label{
  display:block;
  font-family:'Space Grotesk',sans-serif;
  font-size:12px;font-weight:600;letter-spacing:.2em;
  color:var(--color-ink-on-light-3);text-transform:uppercase;
  margin-bottom:16px;
}

/* ── HERO ── */
/* 'min-height' descuenta los 80 px que la cabecera pegajosa ocupa en el flujo,
   y el 'padding-top:80px' del original desaparece: compensaba una barra fija
   que ya no lo es, y sumado a la pegajosa empujaba el hero 160 px. */
.hero{min-height:calc(100vh - 80px);display:flex;flex-direction:column;justify-content:center;position:relative;overflow:hidden;background:var(--color-ground)}
.hero-container{max-width:1400px;margin:0 auto;padding:60px 48px 40px;display:grid;grid-template-columns:1fr 1fr;gap:40px;align-items:center;width:100%}
.hero-left{position:relative;z-index:2}
.hero-headline{font-family:'Caveat',cursive;font-size:clamp(54px,6.5vw,88px);font-weight:700;line-height:.95;color:var(--color-ink-on-light);margin-bottom:28px;position:relative;transform:rotate(-2.5deg);transform-origin:left center}
.hero-headline .brush-text{font-family:'Caveat',cursive;font-weight:700;text-transform:uppercase;letter-spacing:.02em;display:inline-block;position:relative;color:var(--color-ink-on-light);text-shadow:none;background-image:none;-webkit-background-clip:unset;padding:2px 0}
/* Las dos capas de '.brush-text' (2px/.08 y -1px/.04) colapsan en un solo
   peldaño: --color-shadow-soft ya cubre la primera exacto, y la segunda es
   una textura casi invisible que no justifica un quinto tono de sombra. */
.hero-body{font-size:16px;line-height:1.7;color:var(--color-ink-on-light-2);max-width:440px;margin-bottom:32px}
.hero-body strong{color:var(--color-ink-on-light);font-weight:600}
.hero-actions{display:flex;align-items:flex-end;gap:24px;flex-wrap:wrap}
/* 'text-decoration:none' añadido el 2026-10-04, revisando las 7 páginas ya
   publicadas: estos enlaces con forma de botón heredaban el subrayado de
   marca de la regla global de 'a' -- amarillo y de 3 px -- dentro de una
   pastilla negra, y se leían como un enlace metido en un botón. Eran 12 en
   cuatro páginas: dos '.btn-primary' (portada y trabajemos) y diez
   '.btn-outline-dark' (cinco en eventos y cinco en convocatorias).
   '.about-cta-btn' (línea 714) y las pastillas de la cabecera ya lo traían:
   lo que faltaba era la consistencia, no el criterio. La forma del botón es
   su propia señal; el subrayado es para el texto que es enlace y no lo
   parece. */
.btn-primary{font-family:'Space Grotesk',sans-serif;font-size:14px;font-weight:600;letter-spacing:.08em;color:var(--color-ink-on-dark);background:var(--color-ground-dark);padding:16px 32px;border-radius:999px;display:inline-flex;align-items:center;gap:8px;transition:all .35s ease;border:2px solid var(--color-ground-dark);text-decoration:none}
.btn-primary:hover{background:var(--color-brand);color:var(--color-ink-on-brand);border-color:var(--color-brand);transform:translateY(-2px);box-shadow:0 8px 24px var(--color-shadow)}
.doodle-text{font-family:'Caveat',cursive;font-size:18px;font-weight:600;color:var(--color-ink-on-light);display:flex;align-items:center;gap:8px;line-height:1.2;transform:rotate(-3deg)}
.doodle-arrow{width:40px;height:30px;flex-shrink:0}
.lightning-doodle{position:absolute;width:24px;height:40px;color:var(--color-brand);animation:lightning-flash 2s ease-in-out infinite}
.lightning-1{bottom:20%;right:-10px}
@keyframes lightning-flash{
  0%,100%{opacity:.6}
  50%{opacity:1;transform:scale(1.1)}
}
.hero-right{position:relative;display:flex;align-items:center;justify-content:center}
.mascot-container{position:relative;width:100%;max-width:500px}
.mascot-img{width:100%;height:auto;animation:float 4s ease-in-out infinite;filter:drop-shadow(0 20px 40px var(--color-shadow-soft))}
@keyframes float{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(-16px)}
}
.crown-doodle{position:absolute;top:-20px;right:30%;width:55px;color:var(--color-brand);animation:crown-bob 3s ease-in-out infinite}
@keyframes crown-bob{
  0%,100%{transform:translateY(0) rotate(0deg)}
  50%{transform:translateY(-8px) rotate(5deg)}
}
.lightning-bolt{position:absolute;color:var(--color-brand);animation:lightning-flash 2.5s ease-in-out infinite}
.bolt-1{width:22px;top:15%;right:-10px;animation-delay:.5s}
.bolt-2{width:18px;bottom:25%;right:0;animation-delay:1.2s}
.grandes-ideas-text{font-family:'Caveat',cursive;font-size:22px;font-weight:700;color:var(--color-ink-on-light);position:absolute;right:-30px;top:30%;transform:rotate(-8deg);line-height:1.2;text-align:center;text-shadow:2px 2px 0 var(--color-veil-brand-mid)}
.scroll-indicator{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:30px;margin-top:auto}
.mouse-icon{width:24px;height:38px;color:var(--color-ink-on-light)}
.mouse-dot{animation:scroll-dot 1.5s ease-in-out infinite}
@keyframes scroll-dot{
  0%,100%{cy:10;opacity:1}
  50%{cy:18;opacity:.3}
}
/* --color-ink-on-light-3 (5,74:1): sube desde el gris que traía antes
   (~2,85:1), que fallaba el minimo AA de 4,5:1 para texto pequeño. Cambio de
   contraste deliberado. */
.scroll-indicator span{font-family:'Space Grotesk',sans-serif;font-size:11px;font-weight:600;letter-spacing:.2em;color:var(--color-ink-on-light-3)}

/* ── SERVICIOS ── */
.services{padding:100px 0;background:var(--color-ground);position:relative;overflow:hidden}
.services-container{max-width:1400px;margin:0 auto;padding:0 48px;display:grid;grid-template-columns:1fr 1.4fr;gap:60px;align-items:start}
.services-headline{font-family:'Bebas Neue',sans-serif;font-size:clamp(32px,3.5vw,48px);line-height:1.05;color:var(--color-ink-on-light);letter-spacing:.02em;margin-bottom:20px}
.services-body{font-size:15px;line-height:1.7;color:var(--color-ink-on-light-2);max-width:400px}
.services-grid{display:grid;grid-template-columns:1fr 1fr;gap:24px}
.service-card{padding:32px 28px;border:1px solid var(--color-shadow-soft);border-radius:12px;transition:all .35s ease;cursor:default}
.service-card:hover{border-color:var(--color-brand);transform:translateY(-4px);box-shadow:0 12px 32px var(--color-shadow-soft)}
.service-icon{width:52px;height:52px;margin-bottom:18px;color:var(--color-brand)}
.service-icon svg{width:100%;height:100%}
.service-title{font-family:'Space Grotesk',sans-serif;font-size:16px;font-weight:700;letter-spacing:.08em;color:var(--color-ink-on-light);margin-bottom:10px}
.service-desc{font-size:14px;line-height:1.6;color:var(--color-ink-on-light-3)}

/* ── Responsive: hero y servicios. Las mismas cinco pausas del CSS viejo
   (1200/1024/768/480/968); sólo se traen las reglas de estas dos secciones,
   no las de proyectos/nosotros, que son la F2.1 paso 3. ── */
@media (max-width:1200px){
  .grandes-ideas-text{display:none}
}
@media (max-width:1024px){
  .hero-container{padding:40px 24px}
  .services-container{grid-template-columns:1fr;gap:40px;padding:0 24px}
  /* La cabecera deja de tener alto fijo y pasa a envolverse. No hay
     hamburguesa a propósito: el original la tenía con un JS que este sitio no
     trae, y una hamburguesa sin JS es un botón que no abre nada. Envolver
     mantiene los nueve destinos alcanzables con el dedo y con el tabulador, que
     es lo que de verdad importa; cuesta que la barra crezca de alto, y por eso
     'height' pasa a 'min-height' y el hero deja de descontar un número fijo. */
  .cabecera{
    height:auto;min-height:72px;flex-wrap:wrap;gap:12px 20px;
    padding:12px 24px;
  }
  .cabecera-logo{height:40px}
  .cabecera-menu{gap:20px;order:3;width:100%}
  .cabecera-acciones{margin-left:auto}
  /* Descuenta la barra sin envolver (72). Si envuelve a dos filas el hero se
     pasa de alto por la diferencia, que es poco y no esconde nada: el hero
     centra su contenido y recorta lo que sobra. Un número aquí es preferible a
     medir el alto con JS, que este sitio no tiene. */
  .hero{min-height:calc(100vh - 72px)}
}
@media (max-width:768px){
  /* La cabecera deja de ser pegajosa y se va con el scroll. Medida envuelta a
     390 px da 211 px de alto: pegada, se quedaría comiéndose un cuarto de la
     pantalla en cada scroll de cada página. 'sticky' es una comodidad de
     escritorio, donde mide 80; aquí cuesta más de lo que da. Quien quiera
     volver arriba tiene el gesto del sistema. */
  .cabecera{position:static}
  .hero-container{display:flex;flex-direction:column;text-align:center;padding:40px 24px}
  .hero-left,.hero-right{display:contents}
  .hero-actions{justify-content:center;order:1;margin-bottom:24px;width:100%}
  .mascot-container{max-width:260px;margin:0 auto;order:2;margin-bottom:20px}
  .hero-headline{font-size:42px;margin-bottom:16px;order:3}
  .hero-body{margin:0 auto 24px;font-size:15px;order:4}
  .lightning-1{order:5}
  .doodle-text{display:none}
  .grandes-ideas-text{display:none}

  /* Servicios en modo oscuro, sólo en móvil — igual que el CSS viejo. El
     negro casi puro que traía sube a --color-surface-dark: la diferencia es
     de 5 en 255, imperceptible, y es la misma familia "superficie isla" que
     ya documenta el bloque de TOKENS de arriba citando esta sección. */
  .services{background:var(--color-surface-dark);color:var(--color-ink-on-dark);padding:60px 0}
  .services-headline{color:var(--color-ink-on-dark);text-align:left}
  .services-body{color:var(--color-ink-on-dark-2);text-align:left;margin-bottom:30px}
  .services-grid{grid-template-columns:1fr 1fr;gap:12px}
  .service-card{
    background:var(--color-surface-dark-raised);
    border:1px solid var(--color-line);
    display:flex;flex-direction:column;align-items:center;text-align:center;
    padding:24px 16px;gap:12px;border-radius:16px;
  }
  .service-card:hover{border-color:var(--color-brand);transform:none;box-shadow:0 0 20px var(--color-veil-brand-weak)}
  .service-card .service-icon{margin-bottom:0;flex-shrink:0;width:36px;height:36px}
  .service-card .service-title{color:var(--color-ink-on-dark);margin-bottom:0;font-size:13px;letter-spacing:.06em}
  .service-card .service-desc{display:none}

  /* La etiqueta de sección cae aquí sobre negro, y su gris de cuerpo
     (--color-ink-on-light-3, #666) daría 2,58:1 contra él. El original tiene
     para esto una clase aparte, '.section-label-light', con blanco al 60 %;
     el equivalente en tokens es '--color-ink-on-dark-2' (#AAA, 9,04:1), que es
     el mismo gesto —gris claro sobre oscuro— sin inventar un token. */
  .services .section-label{color:var(--color-ink-on-dark-2)}
}
@media (max-width:480px){
  .hero-headline{font-size:38px}
  /* Ocho destinos en 480 px: se aprieta el interletraje y el relleno de las
     pastillas antes que esconder ninguno. */
  .cabecera{padding:10px 16px;gap:10px 12px}
  .cabecera-menu{gap:14px}
  .cabecera-enlace{font-size:12px;letter-spacing:.08em}
  .cabecera-logo{height:34px}
  /* Medido a 390 px antes de publicar, en dos vueltas.
     Primera: con las dos pastillas en una fila que no envolvía,
     'TRABAJEMOS JUNTOS' terminaba en el píxel 400 de 390 — fuera de la
     pantalla. Segunda: dejándolas envolver ya no se salía nada, pero la
     cabecera medía 265 px de alto, que en un teléfono de 844 es casi un
     tercio de la primera pantalla.

     Lo que queda:

       [ logo ...................... ]
       [ menu, UNA fila deslizable   ]
       [ EXPOSITORES | TRABAJEMOS    ]   (apiladas si no caben)

     Dos piezas, y cada una resuelve una de las dos medidas:

     - **Las pastillas envuelven y pueden encoger.** 'min-width:0' es lo que
       lo permite: sin él, flex respeta su ancho de contenido y desborda
       igual. Si no caben a lo ancho, se apilan -- que es lo que pasa a
       390 px -- en vez de salirse. Vale para cualquier ancho y para un texto
       más largo, en vez de afinar un padding que vuelve a romperse con el
       siguiente teléfono estrecho.
     - **El menú va en una sola fila deslizable**, y es lo que baja la
       cabecera de 265 px a ~215. No esconde ningún destino: los seis siguen
       ahí y siguen alcanzables con el tabulador. 'scrollbar-width:none' quita
       la barra en el móvil, donde manda el gesto.

     Se probó antes una variante que repartía logo y CTA en la misma fila con
     'display:contents' y 'order' -- bajaba a 183 px -- y **no se publica**:
     no pude reproducir el viewport estrecho de verdad en esta máquina para
     comprobarla, y en la emulación los 'order' no se aplicaron como dicen.
     Entre una cabecera 30 px más baja sin verificar y una verificada, gana la
     verificada. Queda escrito por si alguien quiere retomarlo con un
     dispositivo real delante. */
  .cabecera-acciones{order:2;width:100%;margin-left:0;flex-wrap:wrap;gap:10px}
  .cabecera-accion,.cabecera-cta{
    flex:1 1 auto;min-width:0;text-align:center;
    font-size:12px;padding:9px 14px;letter-spacing:.04em;
  }
  .cabecera-menu{
    order:3;flex-wrap:nowrap;overflow-x:auto;
    scrollbar-width:none;-webkit-overflow-scrolling:touch;
  }
  .cabecera-menu::-webkit-scrollbar{display:none}
}
@media (max-width:968px){
  .floating-3d{display:block!important;max-width:70px!important;opacity:.4!important}
  .star-hero{top:15%!important;right:-15px!important}
  .torus-hero{bottom:2%!important;left:-15px!important}
  .torus-hero-2{top:40%!important;left:auto!important;right:-15px!important}
  .star-hero-3{top:10%!important;bottom:auto!important;right:auto!important;left:-15px!important}
  .star-about{top:2%!important;right:-15px!important}
  .torus-srv{display:none!important}
  .star-srv-2{top:50%!important;left:-15px!important}
  .torus-proj-1{top:25%!important;left:-15px!important}
  .star-proj-2{top:auto!important;bottom:2%!important;right:-15px!important}
  .star-nosotros-1{top:2%!important;left:-15px!important}
  .torus-nosotros-2{bottom:2%!important;right:-15px!important}
}

/* ════════════════════════════════════════════════════════════════════════
   PROYECTOS Y NOSOTROS — paso 3 (F2.1 de PASOS.md), escrito el 2026-09-30.
   Portado de styles.css:526-923 (mitad de abajo de la portada vieja). Tres
   decisiones que no están sólo en el resumen del paso:

   1. '.project-category' (la fecha de cada proyecto) YA NO usa el amarillo de
      marca como tinta. styles.css:727 es el ejemplo que el propio bloque de
      TOKENS de arriba cita para la regla del amarillo -- #FFD700 sobre blanco
      da 1,40:1--, así que aquí va '--color-brand-ink-on-light' (5,02:1) en
      superficie clara y '--color-brand-ink-on-dark' (14,97:1, el mismo
      #FFD700 de siempre) en el modo oscuro de móvil, donde sí es legible.
      Mismo criterio que subió el contraste de '.scroll-indicator span' en el
      paso 2.
   2. '.about-left{display:none}' a partir de 1024px SE CONSERVA tal cual
      (styles.css:1341): oculta el titular, el cuerpo y el CTA de "Nosotros"
      en tablet y móvil, dejando sólo las cuatro cifras. Es un hallazgo
      MODERADO de la auditoría ("en móvil no se adapta el contenido: se
      amputa") y el mismo trato que ya recibió '.service-desc{display:none}'
      en el paso 2: portar sin arreglar. Arreglarlo es de una fase de
      accesibilidad que esta tanda no cruza (PASOS.md no le asigna casilla).
   3. '.carousel-container' y '.carousel-btn' NO se portan: existían sólo para
      posicionar dos flechas que ningún HTML emite (0 coincidencias de
      'carousel-btn' en index.html y en script.js), y el botón "VER TODOS"
      tampoco -- estaba oculto con 'style="display:none"' y su 'href="#"' no
      llevaba a ningún sitio, el mismo caso que TikTok en 'comun.mjs'. El
      detalle completo está en el comentario de 'seccionProyectos()'.
   ════════════════════════════════════════════════════════════════════════ */

.torus-proj-1{top:8%;left:55%;width:80px;opacity:.85;filter:hue-rotate(120deg)}
.star-proj-2{top:8%;right:20%;width:120px;opacity:.8;filter:hue-rotate(250deg)}
.star-nosotros-1{top:10%;left:5%;width:100px;opacity:.8}
.torus-nosotros-2{bottom:10%;right:8%;width:120px;opacity:.75;filter:hue-rotate(220deg)}

/* ── PROYECTOS ── */
.projects{padding:80px 0 100px;background:var(--color-ground);position:relative;overflow:hidden}
.projects-container{max-width:1400px;margin:0 auto;padding:0 48px}
.projects-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:40px}
.projects-headline{font-family:'Bebas Neue',sans-serif;font-size:clamp(28px,3vw,40px);letter-spacing:.03em;color:var(--color-ink-on-light)}
.projects-subtitle{font-family:'Inter',sans-serif;font-size:clamp(14px,1.2vw,16px);color:var(--color-ink-on-light-3);font-weight:400;margin-top:4px;letter-spacing:.01em}
.projects-grid{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;gap:20px;padding-bottom:20px;-ms-overflow-style:none;scrollbar-width:none;width:100%}
.projects-grid::-webkit-scrollbar{display:none}
.project-card{flex:0 0 calc(20% - 16px);scroll-snap-align:start;min-width:220px;border-radius:12px;overflow:hidden;transition:transform .4s ease}
.project-card:hover{transform:translateY(-6px)}
.project-img-wrapper{position:relative;aspect-ratio:3/4;overflow:hidden;border-radius:12px}
.project-img{width:100%;height:100%;object-fit:cover;transition:transform .6s ease}
.project-card:hover .project-img{transform:scale(1.08)}
.project-info{padding:16px 4px 8px}
.project-title{font-family:'Space Grotesk',sans-serif;font-size:15px;font-weight:700;letter-spacing:.04em;color:var(--color-ink-on-light);margin-bottom:4px}
.project-category{font-family:'Inter',sans-serif;font-size:13px;font-weight:500;color:var(--color-brand-ink-on-light)}
.project-stats{display:flex;gap:12px;margin-top:8px;flex-wrap:wrap}
.stat-item{font-family:'Space Grotesk',sans-serif;font-size:11px;color:var(--color-ink-on-light-2);letter-spacing:.03em}
.stat-num{font-weight:700;color:var(--color-ink-on-light);font-size:13px}

/* ── NOSOTROS ── */
.about{background:var(--color-ground-dark);padding:100px 0;position:relative;overflow:hidden}
/* Textura sutil de fondo (styles.css:740-753). El paso intermedio
   'rgba(255,255,255,0)' colapsa a la palabra clave 'transparent' -- es alfa
   0, no un color -- así que la escalera queda en dos paradas y no tres. */
.about::before{content:'';position:absolute;inset:0;background-image:repeating-linear-gradient(90deg,var(--color-veil-light-weak) 0px,transparent 2px,transparent 6px);pointer-events:none}
.about-container{max-width:1400px;margin:0 auto;padding:0 48px;display:grid;grid-template-columns:1fr 1fr;gap:80px;align-items:center;position:relative;z-index:1}
.about-left{display:flex;flex-direction:column;align-items:flex-start;gap:24px}
.about-eyebrow{font-family:'Space Grotesk',sans-serif;font-size:12px;font-weight:600;letter-spacing:.15em;color:var(--color-brand-ink-on-dark);text-transform:uppercase}
.about-headline{font-family:'Bebas Neue',sans-serif;font-size:clamp(48px,5.5vw,80px);color:var(--color-ink-on-dark);letter-spacing:.03em;line-height:1.1;margin:0}
.about-headline-accent{color:var(--color-brand-ink-on-dark)}
.about-body{font-size:15px;line-height:1.7;color:var(--color-ink-on-dark-2);margin-bottom:32px;max-width:460px}
.about-cta-btn{display:inline-flex;align-items:center;background:var(--color-brand);color:var(--color-ink-on-brand);font-family:'Space Grotesk',sans-serif;font-size:14px;font-weight:700;letter-spacing:.08em;padding:16px 36px;border-radius:999px;text-decoration:none;transition:all .3s ease;margin-top:8px}
.about-cta-btn:hover{background:var(--color-ground);color:var(--color-ink-on-light);transform:translateY(-3px);box-shadow:0 12px 30px var(--color-veil-brand-mid)}
.about-right{display:flex;align-items:center;justify-content:flex-end}
.about-stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:24px;width:100%}
.about-stat-card{background:var(--color-veil-light-weak);border:1px solid var(--color-veil-light-soft);border-radius:16px;padding:28px 24px;display:flex;flex-direction:column;gap:8px;transition:background .3s ease,border-color .3s ease}
.about-stat-card:hover{background:var(--color-veil-brand-weak);border-color:var(--color-veil-brand-mid)}
.about-stat-num{font-family:'Bebas Neue',sans-serif;font-size:52px;color:var(--color-brand-ink-on-dark);line-height:1;letter-spacing:.02em}
.about-stat-label{font-family:'Space Grotesk',sans-serif;font-size:13px;color:var(--color-ink-on-dark-3);line-height:1.3;font-weight:500}

/* ── Responsive: proyectos y nosotros. Mismas pausas 1024/768 del CSS viejo. ── */
@media (max-width:1024px){
  .projects-container{padding:0 24px}
  .projects-grid{gap:24px}
  .about-container{grid-template-columns:1fr;padding:0 24px;text-align:center}
  .about-left{display:none}
  .about-right{justify-content:center}
  .about-body{margin:0 auto 32px}
}
@media (max-width:768px){
  /* Proyectos en modo oscuro, sólo en móvil -- igual que el CSS viejo.
     #0a0a0a era literalmente NEGRO_DE_MARCA: coincide exacto con
     --color-surface-dark, no es una aproximación. */
  .projects{background:var(--color-surface-dark);color:var(--color-ink-on-dark);padding:10px 0 60px}
  .projects-headline{color:var(--color-ink-on-dark);font-size:32px;margin-bottom:8px}
  .projects-header{flex-direction:column;align-items:flex-start;margin-bottom:24px}
  .projects-grid{gap:24px;flex-direction:column;overflow-x:visible;scroll-snap-type:none}
  .project-card{border-radius:16px;background:var(--color-surface-dark-raised);border:1px solid var(--color-line);overflow:hidden;width:100%;flex:none}
  .project-img-wrapper{aspect-ratio:16/9;border-radius:0}
  .project-info{padding:20px}
  .project-title{color:var(--color-ink-on-dark);font-size:18px;margin-bottom:6px}
  /* Aquí SÍ toca --color-brand-ink-on-dark (el amarillo de siempre): el fondo
     ya es oscuro, así que el mismo amarillo que era ilegible en el modo claro
     de arriba aquí da 14,97:1. Es el mismo dato, dos tokens distintos porque
     la superficie cambió -- exactamente lo que la regla del amarillo pide. */
  .project-category{color:var(--color-brand-ink-on-dark);font-size:12px}
}

/* ════════════════════════════════════════════════════════════════════════
   PIE (footer) — paso 3 (F2.1 de PASOS.md). El marcado de 'pie()' nace en
   'contenido/comun.mjs' desde el paso 5 (F1.3) y ya estaba enganchado en
   'build.mjs' desde el paso 2; hasta este paso no tenía ni una regla CSS
   (DECISIONS.md §5.12, §8.5). La estructura es plana -- sin los dos '<div>'
   envoltorio del footer viejo ('.footer-container'/'.footer-bottom')--
   porque 'pie()' emite un mapa del sitio completo (7 rutas) que el footer
   viejo no tenía, así que el layout no es un calco: es uno nuevo, centrado,
   con los mismos tres colores (superficie oscura, tinta clara, amarillo de
   marca) que el resto del sitio.

   Los enlaces del mapa y de redes van sin subrayado ('text-decoration:none'):
   son una lista densa de 9 enlaces cortos, y el subrayado grueso de 'a{}' de
   arriba —pensado para un enlace suelto en un párrafo— los haría ilegibles.
   El correo de contacto SÍ conserva el subrayado por defecto: es una llamada
   a la acción, no una fila de navegación.
   ════════════════════════════════════════════════════════════════════════ */
.pie{background:var(--color-surface-dark);padding:56px 24px 24px;border-top:1px solid var(--color-line);text-align:center}
.pie-logo{height:56px;width:auto;margin:0 auto 20px}
.pie-lema{font-family:'Space Grotesk',sans-serif;font-size:12px;font-weight:600;letter-spacing:.06em;color:var(--color-ink-on-dark-2);max-width:420px;margin:0 auto 32px}
/* El relleno vertical de los enlaces del pie es área táctil, no aire. Medido
   el 2026-10-04 a 390 px: los diez enlaces del pie median 14 o 16 px de alto
   -- la altura de su línea de texto y nada más -- porque son 'inline' y el
   tamaño de letra es de 12 a 14. En un teléfono eso se falla a dedo. Con
   'inline-block' y relleno pasan a ~38, sin mover ni un píxel el aspecto en
   escritorio: lo que crece es la caja que se puede pulsar, no el texto.
   El 'gap' de la lista baja de 8 a 0 en vertical porque el relleno ya separa
   las filas; sin eso, el pie creceria el doble de alto en móvil. */
.pie-lista{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;gap:0 24px}
.pie-sitio{margin-bottom:28px}
.pie-sitio .pie-lista a{font-family:'Space Grotesk',sans-serif;font-size:13px;font-weight:600;letter-spacing:.04em;color:var(--color-ink-on-dark-2);text-decoration:none;display:inline-block;padding:12px 4px}
.pie-sitio .pie-lista a:hover{color:var(--color-brand-ink-on-dark)}
.pie-redes{list-style:none;display:flex;justify-content:center;gap:16px;margin:0 0 32px;padding:0}
.pie-red{font-family:'Space Grotesk',sans-serif;font-size:13px;font-weight:600;letter-spacing:.06em;color:var(--color-ink-on-dark-2);text-decoration:none;display:inline-block;padding:12px 8px}
.pie-red:hover{color:var(--color-brand-ink-on-dark)}
.pie-contacto{margin:0 0 32px}
.pie-contacto a{font-family:'Space Grotesk',sans-serif;font-size:14px;font-weight:600;color:var(--color-ink-on-dark);display:inline-block;padding:12px 4px}
.pie-legal{border-top:1px solid var(--color-line);padding-top:20px}
.pie-legal .pie-lista{gap:0 20px}
.pie-legal .pie-lista a{font-size:12px;color:var(--color-ink-on-dark-3);text-decoration:none;display:inline-block;padding:12px 4px}
.pie-legal .pie-lista a:hover{color:var(--color-ink-on-dark)}
.pie-cierre{font-size:12px;color:var(--color-ink-on-dark-3);letter-spacing:.04em;margin:16px 0 0}

/* ════════════════════════════════════════════════════════════════════════
   CONVOCATORIAS Y EVENTOS — paso 4 (F2.2 de PASOS.md), escrito el 2026-09-30.
   Portado de styles.css:1024-1150 (bloque ".convocatorias"/".convocatoria-"/
   ".status-"). Una sola hoja de reglas para dos secciones -- '.convocatorias'
   con id="convocatorias" en contenido/convocatorias.mjs y con id="eventos"
   en contenido/eventos.mjs -- porque las dos son visualmente la misma
   rejilla de tarjetas; lo que cambia entre ellas es el copy y el 'id' del
   contenedor raíz, no una sola regla de este bloque.

   El bloque ".newsletter-*" que sigue justo después en el CSS viejo
   (styles.css:1152 en adelante) NO se porta: es el formulario de
   '<form onsubmit="event.preventDefault()">', la misma clase de mentira que
   este generador existe para no repetir (ver el comentario de
   'contenido/convocatorias.mjs'). '.btn-outline-dark.disabled'
   (styles.css:1145-1150) tampoco se porta: cero coincidencias en las cinco
   páginas de hoy -- el único botón deshabilitado de la página vieja era el
   "COMPRAR LUGAR" con estilo inline, no esta clase, y "COMPRAR LUGAR" es el
   bloqueante 1 que esta tanda no toca.

   No hay una sola pausa '@media' que mencione ".convocatoria" en el CSS
   viejo (comprobado por grep sobre el archivo completo): ".convocatorias-
   grid" ya es responsive sin media query, vía 'repeat(auto-fit,minmax(...))'.
   No se inventa una pausa que el original no tenía.

   La única decisión de contraste que no es un mapeo directo:

   1. **El badge de estado** ('.convocatoria-status') pintaba su texto
      -- #10B981/#F59E0B -- directamente sobre la tarjeta blanca: 2,54:1 y
      2,15:1, la regla del amarillo otra vez, con otros dos colores
      (documentado donde TOKENS declara 'state-open-on-dark'/
      'state-soon-on-dark'). Aquí el badge es un chip con fondo
      '--color-surface-dark' y el texto en '--color-state-*-on-dark'
      (8,28:1 / 9,78:1): la superficie cambió a oscura, así que el mismo
      color que era ilegible aquí sí vale como tinta. '.status-dot' pinta su
      círculo con el mismo token, como fondo y no como tinta -- no lleva
      '-on-' en ese uso, pero sigue siendo el criterio de este archivo de
      que ni un color se escriba fuera de un token.
   ════════════════════════════════════════════════════════════════════════ */

/* ── CONVOCATORIAS / EVENTOS ── */
.convocatorias{background:var(--color-surface);padding:100px 0}
.convocatorias-container{max-width:1400px;margin:0 auto;padding:0 48px}
.convocatorias-header{text-align:center;max-width:700px;margin:0 auto 60px}
.convocatorias-headline{font-family:'Bebas Neue',sans-serif;font-size:clamp(36px,4vw,56px);color:var(--color-ink-on-light);margin-bottom:20px;letter-spacing:.03em;line-height:1.1}
.convocatorias-body{font-size:16px;line-height:1.6;color:var(--color-ink-on-light-2)}
/* 'min(350px,100%)' y no '350px' pelado. Medido el 2026-10-04 en un iframe de
   390 px, que es como se comprueban aquí las media queries: con el 350 fijo, el
   documento de 'eventos' y el de 'convocatorias' daban 398 px de ancho en una
   ventana de 390 -- barra horizontal en las dos páginas, en todos los
   teléfonos. La cuenta es exacta: el contenedor deja 390-96 de padding = 294
   de ancho útil, 'minmax' exige 350 porque es un mínimo duro, y 350+48 = 398.

   **Y esto corrige una afirmación de este mismo archivo**: el comentario del
   bloque de abajo dice que '.convocatorias-grid' «ya es responsive sin media
   query, vía repeat(auto-fit,minmax(...))». Lo es de 420 px para arriba. Por
   debajo, 'auto-fit' no puede encoger una pista por debajo de su mínimo, así
   que desborda en vez de envolver. 'min(350px,100%)' hace que el mínimo sea el
   que quepa, que es lo que esa frase daba por hecho. */
.convocatorias-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(350px,100%),1fr));gap:30px;margin-bottom:60px}
.convocatoria-card{background:var(--color-ground);border:2px solid var(--color-line-strong);padding:40px;border-radius:12px;box-shadow:8px 8px 0px var(--color-brand);transition:transform .3s ease,box-shadow .3s ease}
.convocatoria-card:hover{transform:translate(-4px,-4px);box-shadow:12px 12px 0px var(--color-brand)}
.convocatoria-date{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:13px;color:var(--color-ink-on-brand);background:var(--color-brand);display:inline-block;padding:6px 12px;border-radius:4px;margin-bottom:20px;letter-spacing:.1em}
.convocatoria-title{font-family:'Bebas Neue',sans-serif;font-size:32px;letter-spacing:.02em;margin-bottom:12px}
.convocatoria-desc{font-size:15px;color:var(--color-ink-on-light-3);line-height:1.5;margin-bottom:24px}
/* El badge, ya como chip oscuro -- ver la decisión 1 de arriba. */
.convocatoria-status{display:inline-flex;align-items:center;gap:8px;font-family:'Inter',sans-serif;font-size:13px;font-weight:600;margin-bottom:30px;background:var(--color-surface-dark);padding:6px 14px;border-radius:999px}
.status-dot{width:10px;height:10px;border-radius:50%}
.status-open .status-dot{background:var(--color-state-open-on-dark)}
.status-open{color:var(--color-state-open-on-dark)}
.status-soon .status-dot{background:var(--color-state-soon-on-dark)}
.status-soon{color:var(--color-state-soon-on-dark)}
/* 'text-decoration:none' por el mismo motivo que '.btn-primary' (línea 461):
   los cinco 'MÁS INFORMACIÓN' de eventos y los cinco de convocatorias salían
   subrayados en amarillo dentro de su pastilla. */
.btn-outline-dark{font-family:'Space Grotesk',sans-serif;font-size:14px;font-weight:600;letter-spacing:.05em;color:var(--color-ink-on-light);padding:12px 24px;border:2px solid var(--color-line-strong);border-radius:999px;display:inline-block;transition:all .3s;text-align:center;text-decoration:none}
.btn-outline-dark:hover{background:var(--color-ground-dark);color:var(--color-ink-on-dark)}

/* ════════════════════════════════════════════════════════════════════════
   EXPOSITORES — paso 5 (F2.3 de PASOS.md), escrito el 2026-09-30.
   Portado del <style> inline de expositores.html:9-123 -- esta página nunca
   compartió CSS con styles.css, así que no hay línea que citar ahí. Decisión
   3 de Alan (DECISIONS.md §7.2): el portal se queda, con su formulario
   deshabilitado y su aviso.

   Lo que NO se porta, y por qué: el enlace "¿Olvidaste tu contraseña?"
   (href="#", el mismo enlace muerto que ya se quitó en portada.mjs y en
   convocatorias.mjs), y la mascota + estrellas animadas (decorativas, y la
   mascota es una imagen más sin copiar a dist/ -- el gap ya documentado en
   DECISIONS.md §8.5/§9.7, no se agranda aquí).

   Los campos deshabilitados llevan la misma opacidad reducida
   (':disabled{opacity:.55}') como única señal visual de "no funciona": el
   aviso de texto de arriba es el que de verdad lo dice, esto es refuerzo.
   ════════════════════════════════════════════════════════════════════════ */

/* ── EXPOSITORES ── */
.expositores{background:var(--color-ground);padding:100px 0}
.expositores-container{max-width:1200px;margin:0 auto;padding:0 48px;display:grid;grid-template-columns:1.1fr .9fr;gap:60px;align-items:center}
.expositores-headline{font-family:'Bebas Neue',sans-serif;font-size:clamp(36px,4vw,56px);color:var(--color-ink-on-light);margin:20px 0;letter-spacing:.03em;line-height:1.1}
.expositores-desc{font-size:18px;line-height:1.6;color:var(--color-ink-on-light-2);max-width:500px}
.expositores-login{background:var(--color-surface);padding:40px;border-radius:16px;border:2px solid var(--color-line-strong);box-shadow:8px 8px 0px var(--color-brand)}
/* El aviso: fondo de marca (no tinta) + su tinta propia, el mismo par que ya
   usa .convocatoria-date un poco más arriba. */
.expositores-aviso{background:var(--color-brand);color:var(--color-ink-on-brand);padding:16px 20px;border-radius:8px;font-size:14px;line-height:1.6;margin-bottom:24px}
.expositores-aviso a{color:inherit}
/* La tarjeta del formulario vive dentro de la columna de texto, como en el
   original (expositores.html): texto y formulario a la izquierda, ilustración
   a la derecha. */
.expositores-login{margin-top:32px}
.expositores-ilustracion{display:flex;align-items:center;justify-content:center}
.expositores-mascota{width:100%;max-width:420px;height:auto}
.login-form{display:flex;flex-direction:column;gap:20px}
.login-form input{font-family:'Inter',sans-serif;padding:16px 20px;border-radius:8px;border:1px solid var(--color-line);background:var(--color-ground);color:var(--color-ink-on-light-3);font-size:15px}
.login-form input:disabled,.login-form button:disabled{opacity:.55;cursor:not-allowed}
@media (max-width:968px){
  .expositores-container{grid-template-columns:1fr;text-align:center;padding:0 20px}
  .expositores-desc{margin:0 auto}
  /* 'min-width:0' en los dos hijos del grid, y es lo que de verdad arregla el
     desborde. Medido a 320 px el 2026-10-05: el contenedor medía 265 de ancho
     útil y la pista del grid salía de 322,4 -- el documento entero daba 342 en
     una pantalla de 305 y la página se deslizaba de lado.

     No era el padding: un elemento de grid nace con 'min-width:auto', así que
     su pista **no puede encoger por debajo del contenido mínimo del hijo**. Y
     el contenido mínimo aquí no es el texto: un '<input>' sin 'size' trae un
     ancho intrínseco propio (~180 px) que, sumado a los 80 de relleno de la
     tarjeta y sus 4 de borde, da justo esos 322. Por eso tocar sólo el relleno
     no lo cerraba.

     'width:100%' en los campos va con ello: sin él siguen pidiendo su ancho
     intrínseco aunque la pista ya pueda encoger. Y el relleno de la tarjeta
     baja a 24 porque 40 por lado en 265 deja 181 para el formulario. */
  .expositores-info,.expositores-login{min-width:0}
  .expositores-login{padding:24px}
  .login-form input{width:100%;min-width:0}
}

/* ════════════════════════════════════════════════════════════════════════
   TRABAJEMOS — paso 6 (F2.3 de PASOS.md), escrito el 2026-09-30. Decisión 2
   de Alan: el contacto es un mailto: de verdad, no un formulario. El <form>
   de seis campos con alert() de éxito simulado (trabajemos.html:176-208) se
   sustituye entero por un único enlace -- no hay nada que deshabilitar ni
   que avisar, el canal ya funciona hoy.

   Layout centrado de una sola columna, sin la tarjeta de dos columnas de
   EXPOSITORES: no queda ningún formulario al que ponerle una tarjeta al
   lado.
   ════════════════════════════════════════════════════════════════════════ */

/* ── TRABAJEMOS ── */
.trabajemos{background:var(--color-surface);padding:100px 0}
.trabajemos-container{max-width:700px;margin:0 auto;padding:0 48px;text-align:center}
.trabajemos-headline{font-family:'Bebas Neue',sans-serif;font-size:clamp(36px,4vw,56px);color:var(--color-ink-on-light);margin:20px 0;letter-spacing:.03em;line-height:1.1}
.trabajemos-desc{font-size:18px;line-height:1.6;color:var(--color-ink-on-light-2);margin-bottom:40px}

/* ════════════════════════════════════════════════════════════════════════
   PRIVACIDAD Y COOKIES — paso 7 (F2.4 de PASOS.md), escrito el 2026-09-30.
   Portado del <style> inline compartido por privacidad.html:9-16 y
   cookies.html:9-16 -- las dos páginas legales traían exactamente el mismo
   bloque, así que hay una sola definición aquí para las dos, igual que
   CONVOCATORIAS/EVENTOS comparten su bloque más arriba.

   Dos colores, y los dos resueltos a un token que ya existía (el detalle,
   con las cifras de contraste, vive en el comentario de cabecera de
   contenido/privacidad.mjs):

   1. El fondo del body viejo, #f9f9f9, es --color-surface exacto.
   2. El texto de párrafo, #444444, no tenía token exacto; se resuelve a
      --color-ink-on-light-2 (7,46:1), el más cercano ya declarado -- más
      claro que el original y muy por encima del mínimo AA.

   El <h1>/<h2> no traían color propio en ese <style>: heredaban el
   color:#1a1a1a de styles.css:20, que es --color-ink-on-light exacto.

   Lo que NO se porta: el navbar reducido ("← VOLVER AL INICIO") y el pie de
   una sola línea de copyright que traían las dos páginas
   (privacidad.html:19-28,66-70). comun.mjs ya decidió que no hay variante
   reducida -- las siete páginas comparten cabecera()/pie() -- así que aquí
   no hay nada que portar de esas dos piezas.
   ════════════════════════════════════════════════════════════════════════ */

/* ── PRIVACIDAD / COOKIES ── */
.legal{background:var(--color-surface);padding:120px 24px 80px}
.legal-container{max-width:800px;margin:0 auto}
.legal-container h1{font-family:'Bebas Neue',sans-serif;font-size:clamp(32px,4vw,48px);letter-spacing:.03em;color:var(--color-ink-on-light);margin-bottom:20px}
.legal-container h2{font-family:'Bebas Neue',sans-serif;font-size:22px;letter-spacing:.02em;color:var(--color-ink-on-light);margin-top:36px;margin-bottom:14px}
.legal-container p,.legal-container li{font-size:16px;line-height:1.6;color:var(--color-ink-on-light-2);margin-bottom:14px}
.legal-container ul{margin-bottom:14px;padding-left:20px}
.legal-fecha{color:var(--color-ink-on-light-3)}

/* ════════════════════════════════════════════════════════════════════════
   LOADING SCREEN — casilla 2 de la tanda F6-bis (fuera de PASOS.md/PLAN.md
   normal), escrito el 2026-10-01. Portado de styles.css:1797-1939. Es sólo
   de la portada: las otras 6 páginas no lo llevan (DECISIONS.md §63 trae el
   conteo 4/0/0/0/0/0/0 sobre los 7 .html de la raíz que lo confirma); lo
   decide p.pantallaDeCarga en pagina(), más abajo, y build.mjs sólo lo pasa
   en true para la ruta 'inicio'.

   Colores: --color-veil-brand-soft/-strong se restauran aquí (se habían
   borrado en F2.1 por no tener consumidor, §8.5) y se suman dos peldaños
   que no existían en la escala original .08/.15/.40/.70: -weak-2 (.10) y
   -strong-2 (.90). rgba(255,200,0,.6) es un matiz propio, no el amarillo de
   marca redondeado, y vive en --color-veil-amber. La opacidad 1 de
   boltFlicker es sencillamente el color sólido: --color-brand, sin velo.
   La tabla completa literal-viejo -> token está en DECISIONS.md §63. */

/* ── LOADING SCREEN ── */
#loading-screen{
  /* display:none por defecto: sin JavaScript nunca se pinta un velo de
     pantalla completa -- el <script> que pagina() emite junto al marcado
     es quien añade .is-active para mostrarlo. */
  display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;
  background:var(--color-ground);align-items:center;justify-content:center;
  z-index:99999;transition:opacity .8s ease,visibility .8s ease;
}
#loading-screen.is-active{display:flex}
#loading-screen.fade-out{opacity:0;visibility:hidden}
.loader-content{display:flex;flex-direction:column;align-items:center;gap:30px}
.loader-logo-wrapper{position:relative;width:220px;height:220px;display:flex;align-items:center;justify-content:center}
.loader-logo{
  width:150px;height:150px;object-fit:contain;
  filter:drop-shadow(0 0 25px var(--color-veil-brand-mid));
  animation:logoPulse 2s ease-in-out infinite;
}
@keyframes logoPulse{
  0%,100%{transform:scale(1);filter:drop-shadow(0 0 25px var(--color-veil-brand-mid))}
  50%{transform:scale(1.05);filter:drop-shadow(0 0 40px var(--color-veil-brand-strong))}
}
/* Orbit ring 1 — tilted ellipse */
.orbit-ring{
  position:absolute;top:50%;left:50%;width:260px;height:260px;
  margin-top:-130px;margin-left:-130px;
  border:2px solid var(--color-veil-brand-soft);border-radius:50%;
  transform:rotateX(65deg) rotateZ(-25deg);
  animation:orbitSpin 1.8s linear infinite;transform-style:preserve-3d;
}
/* Orbit ring 2 — opposite tilt */
.orbit-ring-2{
  width:240px;height:240px;margin-top:-120px;margin-left:-120px;
  border-color:var(--color-veil-brand-weak-2);
  transform:rotateX(65deg) rotateZ(55deg);
  animation:orbitSpin2 2.4s linear infinite;
}
@keyframes orbitSpin{
  0%{transform:rotateX(65deg) rotateZ(-25deg) rotate(0deg)}
  100%{transform:rotateX(65deg) rotateZ(-25deg) rotate(360deg)}
}
@keyframes orbitSpin2{
  0%{transform:rotateX(65deg) rotateZ(55deg) rotate(0deg)}
  100%{transform:rotateX(65deg) rotateZ(55deg) rotate(360deg)}
}
/* Lightning bolt on the orbit */
.orbit-bolt{
  position:absolute;top:-14px;left:50%;margin-left:-10px;font-size:24px;
  filter:drop-shadow(0 0 8px var(--color-veil-brand-strong-2)) drop-shadow(0 0 20px var(--color-veil-amber));
  animation:boltFlicker .4s ease-in-out infinite alternate;
}
.orbit-bolt-2{top:auto;bottom:-14px;font-size:20px;animation-delay:.2s}
@keyframes boltFlicker{
  0%{opacity:.7;filter:drop-shadow(0 0 6px var(--color-veil-brand-strong))}
  100%{opacity:1;filter:drop-shadow(0 0 14px var(--color-brand))}
}
/* Loading text */
.loader-text{
  font-family:'Space Grotesk',sans-serif;font-size:14px;font-weight:600;
  letter-spacing:6px;color:var(--color-ink-on-light);text-transform:uppercase;
}
.loader-dots::after{content:'';animation:dots 1.5s steps(4,end) infinite}
@keyframes dots{
  0%{content:''}
  25%{content:'.'}
  50%{content:'..'}
  75%{content:'...'}
  100%{content:''}
}
/* Modal de galeria (casilla 6 / F6-bis, portado de styles.css:1941-1992 --
   DECISIONS.md entrada 43 ya verifico este comportamiento en la raiz). Sin
   .gallery-btn/.gallery-prev/.gallery-next: cada tarjeta pinta una sola
   imagen (data-gallery con las 3-4 fotos extra nunca se porto,
   contenido/portada.mjs:198-205), asi que no hay nada que un prev/next
   recorriera -- mismo criterio que ya descarto .carousel-btn en
   seccionProyectos(). El fondo semitransparente y la sombra de la imagen
   NO son texto, asi que la regla del amarillo de arriba (que solo gobierna
   la propiedad color) no les aplica -- pero eso no los exime de usar un
   token: --color-scrim-strong y --color-shadow-hard se restauraron en esta
   misma casilla (TOKENS de arriba, DECISIONS.md §15.3/§69) especificamente
   PARA que este modal los consumiera. Antes consumian rgba(0,0,0,.9)/
   rgba(0,0,0,.5) a mano -- el token existia y nadie lo leia -- corregido
   abajo (DECISIONS.md §104). */
.gallery-modal{
  display:none;position:fixed;z-index:10000;top:0;left:0;width:100%;height:100%;
  background-color:var(--color-scrim-strong);justify-content:center;align-items:center;
  opacity:0;transition:opacity .3s ease;
}
.gallery-modal.show{display:flex;opacity:1}
.gallery-content{position:relative;max-width:90%;max-height:90%}
.gallery-content img{max-width:100%;max-height:90vh;border-radius:8px;box-shadow:0 10px 30px var(--color-shadow-hard);object-fit:contain}
.gallery-close{
  position:absolute;top:20px;right:30px;color:var(--color-ink-on-dark);font-size:40px;font-weight:700;
  background:transparent;border:none;cursor:pointer;z-index:10001;transition:color .2s ease;
}
.gallery-close:hover{color:var(--color-brand-ink-on-dark)}

/* ── RITMO VERTICAL EN MÓVIL ───────────────────────────────────────────────
   Escrito el 2026-10-04 al revisar el sitio en anchos de teléfono. Cuatro
   secciones no tenían NI UNA regla responsiva -- 'convocatorias',
   'trabajemos', 'legal' y el pie --, así que se servían con el relleno de
   escritorio: 100 px arriba y abajo y 48 a los lados en una pantalla de 390.
   No rompía nada, pero obligaba a bajar media pantalla antes de leer la
   primera línea de cada página.

   Va todo en una pausa y no repartido por sección, porque es un solo cambio
   -- apretar el aire -- y repartirlo haría que la próxima sección nueva se
   olvidara de la suya. 'expositores' ya tenía la suya en 968 y no se toca.

   El lateral baja a 20 y no a 24 porque las tarjetas de convocatorias llevan
   una sombra dura de 8 px a la derecha: con 24 el borde visual quedaba pegado
   al filo de la pantalla. */
@media (max-width:768px){
  .convocatorias{padding:64px 0}
  .convocatorias-container{padding:0 20px}
  .convocatorias-header{margin-bottom:40px}
  .convocatoria-card{padding:28px 24px}
  .trabajemos{padding:64px 0}
  .trabajemos-container{padding:0 20px}
  .expositores{padding:64px 0}
  .legal{padding:72px 20px 56px}
  .legal-container h2{margin-top:28px}
  .pie{padding:40px 20px 20px}
}
