01 · El concepto
Un mercado donde el activo es el talento.
CyberSenseAI vende algo insólito: Personas IA que se incorporan a un equipo como un miembro más. La metáfora bursátil lo traduce a un lenguaje que un CTO, un CISO o un consejo de administración entienden de forma instintiva: activos que cotizan, resultados que se auditan, una casa que se regula.
- El hero es un parqué: una ciudad de velas japonesas que crece en tiempo real — cada vela es una Persona IA rindiendo en sesión continua.
- Cada sección es una pieza del mercado: el cierre de sesión (cifras), la orden de incorporación (producto), el valor estrella (SoulBot), la cámara acorazada (XAIOS), la mesa de contratación (equipos híbridos), los corros (verticales) y los estatutos (valores CREI).
- Espectáculo con precisión, nunca circo: la estética de banca clásica — reglas dobles, sellos, papel rayado — mantiene la credibilidad enterprise mientras el canvas hace el espectáculo.
02 · Paleta y tipografía
Verde bosque, latón y papel de cotización.
El crema domina como papel de fondo; el verde bosque profundo se reserva para los «tablones» (hero, XAIOS, CREI, cierre); el latón es el único metal — acentos, itálicas y botones. El rojo teja aparece una sola vez con intención: el sello de caucho «Admitida a cotización».
Bodoni Moda — la didona del membrete
Display · titulares, códigos de corro, iniciales CREIIBM PLEX MONO — LA CINTA DEL TELETIPO · 171,30 ▲
Texto, datos, navegación, odómetros, cintaPar deliberadamente polar: la didona aporta la solemnidad del grabado bancario del XIX; la mono, el pulso mecánico del teletipo. No hay tercera familia — la tensión entre ambas es el sistema.
La textura se genera con un feTurbulence SVG embebido como data-URI, fijado a pantalla completa con mix-blend-mode: multiply al 7% — grano de papel constante sin pesar un solo byte de imagen. Las secciones claras añaden un rayado de libro mayor con repeating-linear-gradient.
03 · La ciudad de velas
Proyección 3D a mano sobre canvas 2D.
Sin WebGL, sin librerías: 360 velas (30×12) viven en un mundo 3D propio y se proyectan a 2D con una cámara en perspectiva de un punto. Cada vela hace un paseo aleatorio con deriva alcista — la ciudad crece, como el índice.
La campana de apertura: al cargar, la ciudad no existe. Cada vela tiene una hora de nacimiento (birth, escalonada por fila y distancia al eje) y se levanta desde el suelo en una oleada de ~2,5 s; las mechas crecen con el cuerpo para que nada flote antes de nacer. Mientras tanto, la cifra del tablón cuenta de 100,00 a 171,30 — y a partir de ahí el índice ES la ciudad: la lectura de cabecera se liga a la media real de las velas en pantalla, con la misma deriva alcista contenida.
// cámara: foco fijo, deriva senoidal + parallax de puntero
function project(x, y, z){
var s = FOCAL / z;
return { x: W*.5 + (x - camX)*s, y: H*.33 + (camY - y)*s, s: s };
}
// cada vela: cuerpo 3D (frente + tapa + lado visible) y mecha
var fTL = project(c.x - hw, c.h, zF), fBR = project(c.x + hw, 0, zF);
var bTL = project(c.x - hw, c.h, zB), bTR = project(c.x + hw, c.h, zB);
ctx.fillStyle = shade(F, fog); // niebla: lerp hacia el fondo
ctx.fillRect(fTL.x, fTL.y, fBR.x - fTL.x, fBR.y - fTL.y);
- Algoritmo del pintor: filas de atrás hacia delante; dentro de cada fila, del borde hacia el eje de cámara, para que las caras laterales se solapen bien.
- Niebla de profundidad: cada color se interpola hacia el verde del fondo según la fila (
shade(rgb, fog)) — da lectura atmosférica sin gradientes por vela. - Línea del índice: una regla discontinua de latón a la altura media de la ciudad, recalculada cada frame.
- Calor de mercado: el resplandor del horizonte no es estático — su intensidad se liga al mejor repunte vivo de la sesión (el máximo
flashde las velas), de modo que un tirón fuerte calienta brevemente todo el tablón. Coste: dos stops de gradiente por frame. - Impresiones de mercado (pasada 3): cuando una vela repunta fuerte, además del destello imprime su operación — un pequeño «▲ 171,42» en latón claro que asciende ~55 unidades de mundo y se disuelve en 1,7 s, proyectado con la misma cámara (el cuerpo de texto escala con la profundidad, 8–15 px). Máximo 12 impresiones vivas; desactivadas con
prefers-reduced-motion. - 60 fps: contexto
{alpha:false}, DPR limitado a 1,5 en el tablón (2 en el resto de canvas), ~1.200 trazados simples por frame. El bucle se pausa conIntersectionObserveral salir de pantalla y convisibilitychangeal cambiar de pestaña. - Degradación: con
prefers-reduced-motionse simulan 90 pasos y se pinta un único fotograma estático: misma ciudad, sin movimiento.
04 · Odómetros mecánicos
Cifras que ruedan como un contador de latón.
Cada dígito es una rueda física: una tira vertical de dígitos 0–9 repetidos N vueltas dentro de una ventana con sombras internas de tambor. Al entrar en viewport, cada rueda recibe su destino como variable CSS y la transición hace el resto — cuanto más a la derecha la rueda, más vueltas da y más tarda en asentarse.
var spins = 2 + reels.length; // cada rueda gira más que la anterior
var target = spins * 10 + (+ch); // p. ej. «7» → 47 posiciones
r.strip.style.setProperty('--t', (1.7 + i*.45) + 's');
r.strip.style.setProperty('--n', r.target);
/* CSS: la tira baja n·altura-de-dígito con curva de asentamiento */
.odo-strip{ transform: translateY(calc(var(--n,0) * var(--dh) * -1)) }
.odo.armed .odo-strip{ transition: transform var(--t) cubic-bezier(.14,.84,.16,1) }
Los símbolos (−, ×, /, %) son separadores estáticos: solo los dígitos ruedan, como en un contador real. Sin JS los valores aparecen impresos en texto plano; con prefers-reduced-motion, las ruedas saltan directamente a su posición.
05 · La cinta y el resto del atrezo
Teletipo continuo, ticket perforado, bóveda giratoria.
- Cinta de cotización: el contenido se clona hasta superar 1,15× el ancho del viewport y luego se duplica exacto; un keyframe
translateX(-50%)da bucle perfecto sin costura. La duración se calcula del ancho real (≈60 px/s) para que la velocidad sea constante en cualquier pantalla. La inferior corre en sentido contrario. - Orden de incorporación: papel rayado con
repeating-linear-gradient, perforación conradial-gradientrepetido en el borde, margen rojo de cuaderno y sello de caucho con máscarafeTurbulencepara la tinta desgastada. Rotado −1,1° — el único papel torcido de la página, a propósito. - Libro de órdenes: bajo los CTAs del hero, un libro de compra/venta en vivo (tres niveles por lado, barras de profundidad en verde y latón) alimentado por el mismo PRNG determinista. Cumple la promesa del cierre: «el libro de órdenes, a la vista». Se congela con
prefers-reduced-motiony no trabaja con la pestaña oculta. - Bóveda XAIOS: SVG inline con dos anillos concéntricos girando en sentidos opuestos (90 s y 140 s por vuelta) más una aguja de escaneo que barre el perímetro cada 14 s, con lectura de estado («sin brechas») — la cámara acorazada respira sin robar atención.
- Reprise final: tras el CTA de cierre, un horizonte tenue de velas (canvas estático con máscara de desvanecido) devuelve la técnica central como eco — la página termina donde empezó, en el parqué.
- Sparklines: mini-gráficos de velas deterministas (PRNG mulberry32 con semilla fija por sector) — el de SoulBot se dibuja progresivamente al entrar en viewport.
- Micro-señales de mercado: cada corro cotiza con su propio delta (▲ +34 %, +21 %…) y los puestos «Persona IA» de la mesa llevan un piloto «en sesión» latiendo — la verosimilitud vive en los detalles pequeños, no solo en el tablón grande.
- Carga orquestada: un solo sistema de reveals —
IntersectionObserverañade.is-iny cada elemento lleva su retardo en--d. El hero se escalona en 5 tiempos al cargar. Los titulares llevan tiras de papel crema (box-decoration-break: clone) con una sombra dura de offset que las convierte en etiquetas recortadas: pueden solaparse sobre el tablón oscuro sin perder contraste y la superposición se lee como collage deliberado, no como accidente.
06 · Decisiones y verificación
Reglas de la casa.
| Decisión | Por qué |
|---|---|
| Cero imágenes | Todo es procedural: canvas, SVG y CSS. La página pesa lo que pesa su tipografía. |
| Un solo archivo | CSS y JS inline; la única dependencia externa es Google Fonts. |
| ES5, sin transpiladores | Funciona en cualquier navegador razonable; con JS apagado, todo el contenido sigue legible. |
| Reduced motion digno | Cinta estática envolvente, ciudad congelada, ruedas en destino, reveals visibles. Nada desaparece. |
| Datos etiquetados | Toda cotización simulada lo declara («cotización simulada»); las cifras reales — 40%, ×10, 24/7, 171% — son las del producto. |
| Verificación real | Headless Chrome + Playwright: cero errores de consola, cero scroll horizontal a 1440 y 390 px, odómetros y cintas comprobados en DOM renderizado. |
| Tintas AA (pasada 2) | Dos tokens de texto — --laton-ink #7a5a22 y --alza-ink #23663f — sustituyen a latón y verde alza en toda microtipografía sobre crema; las cremas translúcidas sobre bosque suben a α ≥ .62. Todo texto pequeño cumple 4,5:1; latón y alza originales quedan para display y elementos gráficos. |
| Foco = hover (pasada 2) | El subrayado de la navegación responde también a :focus-visible; los botones fantasma tienen estado :active; la flecha del índice tiene estilo propio también a la baja (.board-read .dn). |
| Coherencia de datos (pasada 2) | El índice del tablón se acota a 158–184 para no contradecir al libro de órdenes (~171); los 8 criterios del comité revelan en cascada (60 ms por asiento) con el mismo easing de la casa. |
| Craft fino (pasada 3) | Subrayado de navegación por transform: scaleX (sale por donde entró, sin relayout); border-color incluido en la transición de botones; sellos y marca con hover propio; el libro de órdenes «imprime» cada actualización con un fundido de 500 ms en vez de saltar; regla entre columnas del examen del comité; contrastes de microetiquetas (cabeceras del libro, códigos de corro, numeración de puestos) subidos a AA. |
| Accesibilidad de odómetros (pasada 3) | Cada odómetro recibe role="img" al montar las ruedas: el lector de pantalla anuncia el aria-label («40 por ciento de reducción…») y nunca los 20–40 dígitos internos de las tiras. |
| Datos estructurados (pasada 3) | JSON-LD Organization en el <head> (nombre, URL, eslogan, productos) completa el bloque de metadatos junto a OG/Twitter y el favicon SVG. |
| Robustez (pasada 3) | Si getContext('2d') devuelve null (contextos agotados, render por software), cada canvas se rinde en silencio y el resto de la página sigue íntegro. Las cintas se reconstruyen al cambiar el ancho de ventana (rotación incluida): sin huecos en el bucle infinito. El libro de órdenes solo trabaja si está en viewport y la pestaña visible. |
| Táctil sin bandazos (pasada 3) | El parallax de cámara del tablón solo se activa con pointer: fine: en móvil el arrastre del scroll ya no zarandea la ciudad. La cabecera de la tabla de corros se realinea a su rejilla de 3 columnas en ≤640 px. |
| Campana coherente (pasada 3) | El índice se fija en «100,00» en el instante en que arranca la sesión — sin destello del valor final antes de la cuenta — y el «calor de mercado» que enciende el horizonte enciende también la línea discontinua del índice: un solo pulso para todo el tablón. |