02 — Landings & Pages

Índice de la página
  1. 01Cliente: Martín Rieznik / LevantArte
  2. Área: Frontend / Quick Funnel + Sales Pages
  3. 020. Definición de "done" para esta checklist
  4. 031. Pre-requisitos
  5. 042. Tareas
  6. 2.1 Routing y layout multi-tenant
  7. 2.2 Quick Funnel — /martin/ (quiz + form + bifurcación)
  8. 2.3 Páginas de gracias
  9. 2.4 Sales page taller — /martin/taller
  10. 2.5 Sales page membresía — /martin/membresia
  11. 2.6 Páginas auxiliares
  12. 2.7 Tracking + APIs internas
  13. 2.8 Performance + QA
  14. 053. Variables y posibilidades a anticipar
  15. 064. Multi-tenant: cómo se replica al cliente #2
  16. 075. Recursos y archivos relacionados
  17. 086. Notas y aprendizajes (post-mortem)
  18. 09CHANGELOG

Cliente: Martín Rieznik / LevantArte

Área: Frontend / Quick Funnel + Sales Pages

Construir el embudo completo en deacademia.com/martin/...: Quick Funnel clone (quiz 5 preguntas + calculating teatral + form 4 pasos + score + bifurcación calificado/no), sales pages del taller (precio dinámico) y membresía (3 precios), replay page gated, gracias-calificado/no-calificado, transferencia, admisión, post-compra. Todo parametrizado para que cliente #2 herede sin tocar código.

Última actualización: 2026-05-05
Responsable principal (R): Eric (build) + Cortex (copy)
Aprobador (A): Jesús
Deadline: 2026-05-31
Depende de: 01 (infra), 03 (copies de páginas), 08 (oferta cerrada para precios y stack), 13 (pixel IDs cargados)
Bloquea a: 14 (ads necesitan landings vivas), 19 (go-live)


0. Definición de "done" para esta checklist

  • Las 11 URLs /martin/* listadas en plan interno § 1.6 funcionan end-to-end en producción deacademia.com/martin/... desde dispositivo limpio (mobile + desktop, sin glitches).
  • Quiz completo dispara Lead evento, redirige a WhatsApp con mensaje pre-llenado y guarda fila en Supabase con UTMs persistidas.
  • Precio dinámico del taller cambia automáticamente por reloj de servidor (ART) en lunes/martes/mié-vie sin necesidad de deploy.
  • Lighthouse score ≥ 85 en /martin/ (mobile) y todas las pages cargan en < 2.5s LCP.
  • Headlines de sales pages aprobadas con score Promise Engineer ≥ 40/50.

1. Pre-requisitos

# Pre-requisito Provisto por Estado
1 Infra técnica done (DNS verde, repo creado, Supabase + Railway corriendo) Eric (checklist 01) ⚠️
2 Pixel IDs Meta + TikTok + Google Ads cargados en clients.config_json Eric + Martín ⚠️
3 Copy de quiz (5 preguntas), CTA WhatsApp, FAQs de objeciones Cortex (checklist 03) ⚠️
4 Headline + body + bonus + stack del taller cerrados Martín + Cortex (checklist 08) ⚠️
5 Headline + body + stack de membresía cerrados Martín + Cortex (checklist 08) ⚠️
6 Datos de transferencia (alias / CBU / titular) para /martin/transferencia Martín (checklist 17) ⚠️
7 Logo + paleta de colores LevantArte (al menos primary + secondary) Cortex (R/Brand assets) ⚠️
8 Links de las 5 pasarelas por SKU (5 × 6 = 30 URLs) Martín + Cortex (checklist 16) ⚠️

2. Tareas

2.1 Routing y layout multi-tenant

  • Crear src/app/[client_slug]/layout.tsx que lee clients.config_json desde Supabase server-side y pasa branding (colores, logo, voz) via context al árbol. R: Eric. Done cuando: import en cualquier page accede a useClientConfig() con tipos.
  • Crear src/app/[client_slug]/middleware.ts (o middleware global) que valida slug existe en DB; si no, retorna 404 amigable. R: Eric. Done cuando: /cliente-inexistente/ retorna 404 con mensaje claro.
  • Definir paleta de colores LevantArte en clients.config_json (primary, secondary, accent, bg, text) y aplicar via CSS vars al layout para que Tailwind las use. R: Eric + Cortex. Done cuando: cambiar el config refresca el branding sin tocar código.
  • Configurar metadata SEO dinámica por cliente (title, description, OG image). R: Eric. Done cuando: <head> de cada page muestra metadata correcta + preview en redes muestra OG image de Martín.
  • Configurar favicon dinámico por cliente desde public/clients/[slug]/favicon.ico. R: Eric. Done cuando: tab del navegador muestra favicon LevantArte en URLs /martin/*.

2.2 Quick Funnel — /martin/ (quiz + form + bifurcación)

  • Página /martin/page.tsx: hero con headline + sub + CTA "Empezá el test" + indicador de progreso 0/5. R: Cortex (copy) + Eric (build). Done cuando: hero responsive + CTA scrollea suave a quiz.
  • Quiz de 5 preguntas con UI tipo cards selectables (1 pregunta a la vez, transición animada). R: Eric. Done cuando: usuario navega 5 preguntas, cada respuesta dispara evento quiz_started (primera) y avanza el step.
  • Pantalla "calculating" teatral entre quiz y form (3-5s con animación tipo loading bar + texto cambiante "Analizando tu perfil..."). R: Eric. Done cuando: animación se ve fluida en mobile + no se puede skipear.
  • Form de 4 pasos: (1) email + nombre, (2) WhatsApp con país, (3) edad + estado civil, (4) "qué buscás resolver" (textarea). R: Eric. Done cuando: validaciones por step (zod), persistencia entre pasos (no se pierde al hacer back), submit final dispara quiz_completed.
  • Score de calificación 96-99: lógica simple (combinación de respuestas pondera score). Mostrar en pantalla con efecto wow ("Tu compatibilidad es 97/100"). R: Eric + Cortex (lógica). Done cuando: score se calcula consistente + se muestra grande tras submit.
  • Bifurcación calificado (>= 96 + edad >= 25 + país soportado) → redirect a /martin/gracias-calificado con lead_id en query. R: Eric. Done cuando: caso calificado redirige correctamente + evento registered_calificado en Supabase.
  • Bifurcación no calificado → redirect a /martin/gracias-no-calificado. R: Eric. Done cuando: caso no calificado redirige + evento registered_no_calificado.
  • Persistir UTMs (utm_source, medium, campaign, content) desde query string al cookie ta_utms (90 días) y guardar en leads.utm_*. R: Eric. Done cuando: lead que viene de ?utm_source=meta&utm_campaign=test aparece con esos valores en Supabase.
  • Generar ta_uid (UUID v4) por sesión, persistir en cookie 1 año, usar como key cross-page. R: Eric. Done cuando: mismo dispositivo siempre tiene mismo ta_uid, visible en Supabase.
  • A/B variant: cookie ab_variant + ab_source asignados via /api/redirect, log en leads.ab_variant. R: Eric. Done cuando: 50/50 split observable en Supabase tras 100 leads de prueba.

2.3 Páginas de gracias

  • /martin/gracias-calificado/page.tsx: mensaje cálido "Sos uno de los nuestros" + botón grande "Hablar con Martín por WhatsApp" con link wa.me/<number>?text=... pre-llenado. R: Eric + Cortex (copy + mensaje pre-llenado). Done cuando: click abre WA con mensaje "Hola Martín, terminé el test, soy {nombre}".
  • Disparar pixel CompleteRegistration Meta (browser-side) + Lead server-side via CAPI + TikTok CompleteRegistration en gracias-calificado. R: Eric (coord con checklist 13). Done cuando: events manager Meta + TikTok muestran eventos.
  • /martin/gracias-no-calificado/page.tsx: mensaje educado + opción soft de WhatsApp (no botón gigante) + sugerencia de seguir Instagram. R: Cortex (copy) + Eric. Done cuando: tono no rechaza al usuario pero no le da el mismo trato VIP.
  • Pixel más débil en no-calificado: Lead event con value=0 (para que Meta no lo optimice como lead caliente). R: Eric. Done cuando: event aparece en pixel con value 0.

2.4 Sales page taller — /martin/taller

  • src/lib/precio_dinamico.ts: función getPrecioTaller(now: Date, tz='America/Argentina/Buenos_Aires') retorna 5/10/15 según día (lunes 5, martes 10, mié-vie 15). R: Eric. Done cuando: tests unitarios cubren los 7 días + edge case medianoche ART.
  • Página /martin/taller/page.tsx server-rendered con precio dinámico calculado a request time (no cliente). R: Eric. Done cuando: refresh en horarios distintos cambia el precio sin deploy.
  • Estructura de la sales page (9 Pasos Carta de Venta + Stack Slides + 4-Legged Stool): Hero → Promesa → Mecanismo → Stack visual → Bonus → Garantía → Urgencia → CTA → FAQs. R: Cortex (copy) + Eric (build). Done cuando: cada bloque presente + scroll fluido en mobile.
  • Headline auditada por Promise Engineer con score ≥ 40/50. R: Cortex. Done cuando: PDF de scorecard archivado + headline aprobada por Jesús.
  • Stack de valor visual (cards con valores percibidos + valor total tachado + precio actual). R: Cortex (diseño) + Eric (build). Done cuando: stack legible mobile + suma matemática correcta.
  • 5 botones de pasarela (dLocal / Shopify / Hotmart / Skool / Transferencia) más botón "Hablar con asesor" → /martin/admision. R: Eric. Done cuando: 6 botones presentes + cada uno con link correcto leído de clients.config_json.checkouts.taller.
  • Cada click en botón de pasarela dispara evento checkout_clicked con pasarela + sku + precio_actual. R: Eric. Done cuando: evento en Supabase + pixel InitiateCheckout Meta + TikTok.
  • Bloque FAQs con 8-12 objeciones rotas (extraídas del análisis de contenido de Martín). R: Cortex. Done cuando: FAQs con accordion + ninguna pregunta queda sin respuesta sólida.
  • Countdown visual al cambio de precio ("$5 hasta hoy 23:59 ARG"), updated server-side. R: Eric. Done cuando: countdown se ve correcto + al pasar la hora cambia precio + mensaje.

2.5 Sales page membresía — /martin/membresia

  • Página /martin/membresia/page.tsx con lógica de mostrar precios según día actual (jueves: solo $97; viernes y posteriores: $97 + $17/mes + $67×6). R: Eric. Done cuando: helper similar a getPrecioTaller pero para membresía + tests.
  • Estructura: Hero (pitch membresía) + Stack del año (qué incluye) + Bonus + Comparación contra Mentoría 90 días + Garantía + 3 cards de precio + 5 pasarelas por precio + admisión. R: Cortex (copy) + Eric (build). Done cuando: bloques presentes + tabla comparativa clara.
  • Embed del video de pitch de Martín (grabado en Sesión 4 — checklist 09). R: Eric. Done cuando: video reproduce inline + autoplay muted + controles visibles.
  • 3 cards de precio con badges ("Mejor valor", "Más popular", "Acceso flexible"). R: Cortex (textos) + Eric. Done cuando: cards visualmente diferenciadas + badges legibles mobile.
  • 5 pasarelas × 3 precios = 15 botones (no abrumar UI: usar dropdown o tabs por pasarela). R: Eric + Cortex (UX). Done cuando: usuario elige precio → ve 5 opciones de pago + botón admisión visible siempre.
  • Headline auditada Promise Engineer score ≥ 40/50. R: Cortex. Done cuando: aprobado por Jesús.

2.6 Páginas auxiliares

  • /martin/replay/page.tsx: gated por email (formulario "Recibí mi link" → email verifica + redirect a página con video privado). R: Eric + Cortex (copy). Done cuando: solo emails registrados como attended_webinar o dropped_webinar acceden + el resto ve form de registro.
  • /martin/transferencia/page.tsx: instructivo bancario claro (alias, CBU, titular, monto a transferir según SKU) + form "Ya transferí" con upload de comprobante. R: Cortex (instructivo + copy) + Eric (build + upload). Done cuando: comprobante se sube a Supabase storage + notificación a Jesús + compras row con status pending.
  • /martin/admision/page.tsx: form pre-handoff a closer (nombre, WhatsApp, qué te interesa, presupuesto). Output: link a WhatsApp del closer + lead marcado en Supabase. R: Cortex (copy) + Eric. Done cuando: form 4 campos + redirect WA closer + lead tipo=admision en eventos.
  • /martin/checkout/[sku]/page.tsx: selector visual de pasarela (5 opciones grandes con logos), redirect tras click. R: Eric. Done cuando: navegando a /martin/checkout/TALLER_LUNES se ven 5 botones que llevan al checkout correcto.
  • /martin/taller/gracias/page.tsx: post-compra. "Bienvenido al taller. Te agregamos al grupo. Acá tu acceso." + dispara evento purchase_completed. R: Cortex (copy) + Eric. Done cuando: page muestra info de acceso + Pixel Purchase server-side disparado + bot WA agrega al grupo compradores.
  • /martin/membresia/gracias/page.tsx: post-compra membresía. Bienvenida + acceso + agregar a grupo miembros. R: Cortex (copy) + Eric. Done cuando: análogo a /taller/gracias pero para membresía.

2.7 Tracking + APIs internas

  • API route /api/track POST: recibe {lead_id, evento, data} y escribe en Supabase eventos. R: Eric. Done cuando: cualquier page puede loguear eventos custom + rate limit 100 req/min por IP.
  • API route /api/redirect: asigna A/B variant + setea cookie + redirige a destino. R: Eric. Done cuando: ads pueden apuntar a /api/redirect?to=/martin/&variant=auto y distribuye 50/50.
  • API route /api/lead: crea/upserta lead en Supabase + retorna lead_id para que frontend siga track. R: Eric. Done cuando: form del quiz llama /api/lead y recibe ID.
  • API route /api/precio-taller: retorna {precio, dia, prox_cambio} para UI countdown. R: Eric. Done cuando: endpoint público + cacheable 60s.

2.8 Performance + QA

  • Lighthouse mobile ≥ 85 en /martin/, /martin/taller, /martin/membresia. R: Eric. Done cuando: 3 reportes archivados con score ≥ 85 cada uno.
  • Imágenes optimizadas con next/image + AVIF/WebP automatic. R: Eric. Done cuando: ningún PNG > 200KB sirviéndose.
  • Test cross-browser: Chrome, Safari (iOS), Firefox, Samsung Internet. R: Eric. Done cuando: 4 browsers OK + screenshots archivados.
  • Test cross-device: iPhone SE (small), iPhone 14, Android medio (Pixel 5), iPad, desktop 1440px. R: Eric. Done cuando: 5 dispositivos OK + screenshots.
  • End-to-end test: dispositivo limpio + IP nueva → quiz → calificado → WhatsApp → todo trackeado en Supabase. R: Eric + Jesús. Done cuando: log completo en Supabase + match con expected events.

3. Variables y posibilidades a anticipar

Escenario Plan B
Cambio de precio del taller falla en medianoche ART (timezone bug) Cron job de Railway 00:01 ART verifica precio actual + dispara warning si discrepa con día. Tests unitarios con mocks de fechas.
Form del quiz se rompe en Safari iOS (autofill o input zoom) Disable autofill en campos no necesarios + meta viewport user-scalable=no + font-size mínimo 16px en inputs.
Lead duplica registros si refresca la página Constraint unique (client_id, email) en leads + handle 23505 con upsert.
WhatsApp link wa.me no funciona en algunos navegadores in-app (Instagram, TikTok) Detectar UA in-app + mostrar instrucción "Abrí en navegador" + botón copy número.
Precio dinámico se desincroniza entre páginas (cliente cachea) Server-render todo + Cache-Control: no-store en pages con precio + revalidate 60s en API.
Headlines de Promise Engineer no llegan a 40/50 Iteración: 3 rondas con feedback de Jesús + Martín. Si tras 3 rondas no llega, escalar a Jesús para decisión.
Stack visual no se ve bien en mobile (cards muy grandes) Layout vertical mobile-first + cards apiladas + max 1 col mobile, 3 col desktop.
Replay page accede usuarios no registrados (link compartido) Token único por email + expiración 7 días + log de IPs accediendo cada token (alerta si > 3 IPs).
Comprobante de transferencia upload pesa > 10MB (PDF escaneado mal) Limite 5MB en frontend + compresión client-side + msj de error claro + alternativa "envianos por WA".
Closer no recibe handoff de /martin/admision (notificación se pierde) Doble notificación: WA + email + log en Supabase con tag pending_handoff. Cron 1h chequea pending.
Botones de pasarela rotos (links incorrectos cargados en config) Test smoke automático cada hora: GET HEAD a cada link de checkout + alert si 404.
OG image no se renderiza en preview Meta/WA Validar con https://developers.facebook.com/tools/debug/ + cache buster en URL.

4. Multi-tenant: cómo se replica al cliente #2

Esta sección alimenta sop/lanzar-cliente/02_LANDINGS_NEW_CLIENT.md.

  • Variables a externalizar (todo en clients.config_json del cliente):
  • quiz.preguntas[] — array de 5 preguntas con opciones + scoring
  • quiz.umbral_calificado — score mínimo para bifurcar a calificado
  • quiz.umbral_edad — edad mínima
  • quiz.paises_soportados[]
  • oferta.taller.precios_por_dia[][{dia: 'lunes', precio: 5}, ...]
  • oferta.taller.headline, subhead, bonus[], stack[], garantia, faqs[]
  • oferta.membresia.precios[] — array con tipo + monto + cuotas
  • oferta.membresia.headline, stack[], comparacion, bonus[]
  • pasarelas.taller{dlocal: url, shopify: url, hotmart: url, skool: url, transferencia: '/transferencia'}
  • pasarelas.membresia — análogo
  • whatsapp.numero_principal, whatsapp.mensaje_pre_llenado_template
  • whatsapp.numero_closer
  • transferencia.alias, cbu, titular, cuit
  • voz.tono, voz.no_decir[], voz.frases_clave[] — para copies
  • branding.colores, branding.logo, branding.favicon, branding.og_image

  • Templates a guardar en sop/lanzar-cliente/templates/landings/:

  • app-client-slug-template/ — todas las páginas con placeholders {{CLIENT_*}}
  • quiz-config.template.json — esqueleto del quiz
  • sales-page-taller.template.tsx — estructura genérica con slots
  • sales-page-membresia.template.tsx — análogo
  • gracias-calificado.template.tsx
  • gracias-no-calificado.template.tsx
  • replay.template.tsx
  • admision.template.tsx
  • transferencia.template.tsx
  • checkout-sku.template.tsx

  • Sustituciones automáticas (clone_client.py extiende):

  • Copia carpeta app/martin/app/[nuevo_slug]/
  • Reemplaza placeholders en imports + textos visibles que sean de marca
  • Genera nuevo clients.config_json desde template + datos del nuevo cliente
  • Asigna pixel IDs nuevos en config
  • Vercel re-deploy automático tras commit

  • Tiempo objetivo cliente #2 para landings: < 4 días desde infra-listo a landings-vivas (asume copy + oferta del cliente #2 ya cerrados).


5. Recursos y archivos relacionados


6. Notas y aprendizajes (post-mortem)

Llenar después del primer ciclo. Estos aprendizajes son los que se llevan al SOP.

  • (vacío hasta primera ejecución)

CHANGELOG

  • 2026-05-05 — Cortex (agente A) — Creación inicial con base en plan interno § 1.6 + § 6.