🪟 El marco del navegador
Una ventana persistente rodea cada captura de pantalla; es lo que le da el aspecto de "navegador" y disimula que el contenido es una captura.
La ventana #appwin vive en la bg-layer con data-layout-ignore: borde, radio, sombra grande, barra de título con 3 puntos (rojo/amarillo/verde) y una píldora de URL monoespaciada con candado + window.urlLabel. Las capturas de pantalla (1 clip por escena) quedan dentro de ella, en (WIN_L, SHOT_T) con tamaño VW×VH.
"window": {
"top": 96, // WIN_T — topo da janela no canvas
"titleH": 52, // altura da barra de título
"urlLabel": "localhost:8000"
}
// derivados no build-demo.mjs:
WIN_L = (1920 - VW) / 2 // centraliza horizontal
SHOT_T = WIN_T + TITLE_H // topo do screenshot
- ✓Marcar el marco con
data-layout-ignore - ✓Usar la
urlLabelreal de la app capturada - ✓Mantener la ventana persistente (no recrearla en cada escena)
- ✗Dibujar la captura de pantalla fuera del área de la ventana
- ✗Cargar el sitio en vivo dentro del marco (el renderizado es determinista)
- ✗Animar el marco como si fuera una escena
➡️ El cursor animado y el recorrido hasta el objetivo
Un único cursor global se desliza por la línea de tiempo principal y cae justo en el centro de la bounding box real del control.
La captura se ejecuta en un viewport fijo (p. ej., 1280×800) — toda bbox viene en este espacio. Un punto (sx, sy) del screenshot se convierte canvasX = WIN_L + sx, canvasY = SHOT_T + sy. El objetivo del cursor es el centro de la bbox mapeada.
// hotspot na ponta da seta ≈ (6,3) num SVG de 42px
const HOT = { x: 6, y: 3 };
// a PONTA cai no centro do alvo, não o canto
tl.to("#cursor", {
x: alvoX - HOT.x,
y: alvoY - HOT.y,
duration: 0.7,
ease: "power3.inOut"
}, s.start + 0.35); // deixa a cena aparecer antes
La bounding box viene de getBoundingClientRect() en la captura, no a partir de una estimación "a ojo". Eso es exactamente lo que hace que el cursor parezca profesional: cae justo en el píxel del botón o campo.
👆 El clic y el efecto visual
Cuando el cursor «hace clic», un pulso en el propio cursor y un ripple ámbar comunican la acción al espectador.
~start + 0.35scursor parteEl cursor sale de la posición anterior hacia el objetivo, dejando que la escena aparezca primero.
~start + 1.05sllega al objetivoLa punta aterriza en el centro de la bbox; el resaltado ya está pulsando alrededor del control.
~start + 1.15shaz clicEl cursor hace scale:.82 en yoyo (pulse) y el #ripple (el círculo ámbar) se expande y desaparece en el centro del objetivo.
Pasos solo de lectura (sin click:true) no activan el ripple — ideal para la pantalla inicial (intro:true), donde solo presentas la app sin interactuar.
🔍 Zoom y resaltado en la región activa
El resaltado dirige la mirada al control; el zoom hace un push-in cinematográfico en el momento del resultado.
.hlbox (destaque)
Rectángulo solo con box-shadow (anillo ámbar + glow) en el bbox mapeado con ~6px de margen. Aparece con back.out y pulsa el glow unas veces: enfoca sin cubrir nada.
zoom: true
En el paso del resultado, el <img> recibe scale 1 → 1.12 a lo largo de la narración, con transformOrigin en el centro del objetivo (p. ej., el panel de resultados).
{
"shot": "04-result.png",
"target": { "x": 668, "y": 128, "w": 484, "h": 324 },
"zoom": true,
"caption": "O resultado aparece pronto para baixar.",
"narration": "E pronto: o resultado já está disponível."
}
El último paso de contenido suele ser el resultado con zoom:true — un ligero «¡ta-dá!» que cierra el arco del walkthrough antes de la CTA.
🎨 Estilo visual premium oscuro ámbar
Una paleta cerrada de 6 colores con el ámbar como único acento dominante: la identidad visual de todos los videos.
UN acento dominante: el ámbar. Aparece en el ripple del clic, en el anillo del resaltado, en el glow de fondo y en el CTA. El fondo es idéntico en todas las escenas: nunca es plano.
Capa de fondo persistente con data-layout-ignore: resplandor radial ámbar que respira, texto fantasma gigante que se desplaza, cuadrícula de líneas finas y grano. Escala grande: titular de 64–172px, elementos decorativos con 12–25% de opacidad.
🔤 Las fuentes incrustadas
Sora, Inter y JetBrains Mono como .woff2 locales — nunca CDN, porque el render es offline y determinista.
Sora 700–800Títulos y titulares (geométrica, impacto).
Inter 400–600Cuerpo y captions (alta legibilidad).
JetBrains MonoPíldora de URL, valores y código.
El entorno de renderizado puede no tener internet. Las fuentes externas pueden fallar y alterar el aspecto visual. Las fuentes vienen de assets/fonts/ (subset latin cubre PT-BR); el build-demo.mjs inyecta el fonts.css reescribiendo las rutas.
⏱️ Sincronizar el tiempo de cada paso con la narración
El cursor responde a la narración: llega al objetivo antes de la explicación y hace clic en el momento adecuado, con un ritmo definido por LEAD/TAIL/FADE.
LEAD 0.5s — la escena aparece antes de que empiece la voz.
TAIL 0.7s — pausa después de que termina el audio.
FADE 0.4s — transición entre escenas.
El cursor llega al objetivo ~0.7s antes de la explicación principal; el clic ocurre cerca de start + 1.15s.
Además, el video cansa y la captura y edición se convierten en un trabajo pesado. 5–8 pasos + CTA ≈ 35–50s — el tamaño ideal para un walkthrough.
🎯 Lo que aprendiste
- ✓El marco
#appwinrodea el shot y le da el aspecto de navegador - ✓El cursor global apunta al bbox real con el hotspot en la punta y easing curvo
- ✓El clic activa pulse + ripple ámbar; el zoom hace un push-in sobre el resultado
- ✓Paleta dark premium ámbar y fuentes locales Sora/Inter/JetBrains
- ✓La sincronización LEAD/TAIL/FADE ajusta la animación a la narración
Próximo: Módulo 3.2 — cómo generar la narración local con Kokoro y montar todo con el build-demo.mjs.