feat: rediseño «El documento» en curso, contacto real y enlaces externos
Primitivas y contenido del rediseño (escala tipográfica, Revelar, MarcaSeccion, Seccion, Antetitulo, SimboloMarca, Resplandor, ClaridadHero, sección Proceso de ocho etapas, lib/content.ts reestructurado en seis secciones con alias para las viejas). Mecanismo y FormaTrabajar se retiran. Encima, lo de esta sesión: - recorrido: creeer vuelve a responder cada situación en vez de repetirla (el alias apuntaba respuesta a situacion); respuestas en plural. Créditos de las seis fotos de México. - contacto: WhatsApp, email (con mailto:) y LinkedIn reales; iconos de las redes en el cierre y el footer (IconoRed, trazos de Simple Icons, CC0). - enlaces: lo externo abre en otra pestaña con noopener y aviso para lectores de pantalla (lib/enlaces.ts); mailto y anclas se quedan. - Proceso: las ocho tarjetas no aparecían nunca en móvil (amount 0.3 sobre una lista de ~3000px); ahora cada una se revela sola. El ≡ de la 08 ya no tapa un entregable, título equilibrado, etiqueta a 12px, sin elevar al hover lo que no es clicable. - Disciplinas: la frase «No son la promesa — son los medios» gana peso y se agrupa con título e ilustración; sin cortes huérfanos en móvil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3242a8bf71
commit
89e54a6e84
73 files changed
+5261
-592
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,320 @@
|
||||
# «Las Tres Líneas» — Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Reemplazar la escena Three.js del hero ("Astrolabio") por «Las Tres Líneas»: tres barras doradas que pasan de caos → ≡ → escalera → crecimiento, con reposo vivo.
|
||||
|
||||
**Architecture:** Solo se reescribe el interior de `components/primitives/EscenaClaridad.tsx` (misma ruta, misma API `className`, mismo export). El wrapper (IntersectionObserver, `prefers-reduced-motion`, `SiHayWebGL`, props del `Canvas`) queda idéntico. `ClaridadHero.tsx`, `Hero.tsx` y las 4 pruebas existentes no se tocan.
|
||||
|
||||
**Tech Stack:** Next.js 14 (App Router, `output: export`), `three@0.185`, `@react-three/fiber@8`, TypeScript, Vitest.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-08-21-animacion-tres-lineas-design.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Gestor de paquetes: **pnpm** (nunca npm). Comandos: `pnpm test`, `pnpm lint`, `npx tsc --noEmit`, `pnpm build`.
|
||||
- **Comentarios de código en inglés**; copy/docs/commits en español.
|
||||
- Paleta: dorado `#C4942E` (la escena no usa azul — ya no hay anillos ni sombra).
|
||||
- **Staging del repo tiene cambios ajenos staged** (deletes de `FormaTrabajar`/`Mecanismo`, rename `CLAUDE.md`). Al commitear usa **rutas explícitas** (`git add <archivo>`), nunca `git add -A` / `git add .`.
|
||||
- Commits de esta implementación **aprobados** por el usuario en la rama `animacion-tres-lineas`.
|
||||
- `prefers-reduced-motion` debe montar la pose final directamente (patrón `estatico` existente).
|
||||
|
||||
## Fuente de verdad de la coreografía
|
||||
|
||||
Los parámetros aprobados son los del **preview del visual companion** (elección de Erik, concepto A). Nota: el geometry es **centrado** (`BoxGeometry(2.3, 0.3, 0.3)`) y crece simétricamente en X — así se ve en el preview aprobado; ignora la mención a "origen en extremo" heredada del código viejo en una frase del spec.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Reescribir `EscenaClaridad.tsx` con «Las Tres Líneas»
|
||||
|
||||
**Files:**
|
||||
- Modify: `components/primitives/EscenaClaridad.tsx` (reescritura completa del interior)
|
||||
- Test (sin edits): `components/primitives/__tests__/EscenaClaridad.test.tsx`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `usePrefersReducedMotion` de `@/lib/motion` (ya usada), `Canvas`/`useFrame`/`useThree` de `@react-three/fiber`.
|
||||
- Produces: `export function EscenaClaridad({ className = '' }: { className?: string })` — firma idéntica a la actual. `ClaridadHero` y los tests la consumen sin cambios.
|
||||
|
||||
- [ ] **Step 1: Baseline — las 4 pruebas pasan hoy**
|
||||
|
||||
Run: `pnpm test components/primitives/__tests__/EscenaClaridad.test.tsx`
|
||||
Expected: 4 passed (aria-hidden, tamaño del caller, frameloop always, frameloop demand).
|
||||
|
||||
- [ ] **Step 2: Reemplazar el archivo completo por la nueva escena**
|
||||
|
||||
Escribir en `components/primitives/EscenaClaridad.tsx`:
|
||||
|
||||
```tsx
|
||||
'use client';
|
||||
|
||||
import { Component, useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
import { usePrefersReducedMotion } from '@/lib/motion';
|
||||
|
||||
// VISUAL CONCEPT: "Las Tres Líneas" — the brand symbol tells the story.
|
||||
// 1. Caos: three golden bars drift tangled, each on its own frequencies.
|
||||
// 2. Claridad: they settle into the "≡" of cre≡er — level, exact, still.
|
||||
// 3. Dirección: together they tilt ~9° and stagger into a staircase.
|
||||
// 4. Crecimiento: each line extends forward (the top one the most) with a
|
||||
// rising golden glow. The scene then rests, barely alive: a slow camera
|
||||
// drift and an almost imperceptible emissive pulse. No loops, no resets.
|
||||
|
||||
const DORADO = new THREE.Color('#C4942E');
|
||||
|
||||
const suave = (t: number) => 1 - Math.pow(1 - t, 3);
|
||||
const tramo = (t: number, desde: number, dura: number) =>
|
||||
Math.min(1, Math.max(0, (t - desde) / dura));
|
||||
|
||||
// Phase timings (seconds), verbatim from the approved preview.
|
||||
const CAOS_DURA = 3.0;
|
||||
const CLARIDAD = { desde: 3.0, dura: 2.2 };
|
||||
const DIRECCION = { desde: 5.2, dura: 2.0 };
|
||||
const CRECIMIENTO = { desde: 7.2, dura: 2.4 };
|
||||
|
||||
// ── Las Tres Líneas ─────────────────────────────────────────────────────────
|
||||
|
||||
function TresLineas({ estatico }: { estatico: boolean }) {
|
||||
const time = useRef(estatico ? 10 : 0);
|
||||
const m1 = useRef<THREE.Mesh>(null);
|
||||
const m2 = useRef<THREE.Mesh>(null);
|
||||
const m3 = useRef<THREE.Mesh>(null);
|
||||
const meshes = [m1, m2, m3];
|
||||
|
||||
// Decoupled drift frequencies per bar, so the chaos never syncs.
|
||||
const deriva = useMemo(
|
||||
() => [
|
||||
{ fx: 1.1, fy: 0.9, fz: 0.7, rx: 0.8, ry: 1.1 },
|
||||
{ fx: 1.47, fy: 1.4, fz: 1.4, rx: 1.1, ry: 0.9 },
|
||||
{ fx: 1.84, fy: 1.9, fz: 0.7, rx: 1.4, ry: 0.7 },
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const geometria = useMemo(() => new THREE.BoxGeometry(2.3, 0.3, 0.3), []);
|
||||
const materiales = useMemo(
|
||||
() =>
|
||||
[0, 1, 2].map(
|
||||
() =>
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: DORADO,
|
||||
metalness: 0.8,
|
||||
roughness: 0.25,
|
||||
emissive: DORADO,
|
||||
emissiveIntensity: 0,
|
||||
}),
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Final poses: the "≡" (identity rotation) and the tilted staircase.
|
||||
const qSimbolo = useMemo(() => new THREE.Quaternion().identity(), []);
|
||||
const qEscalera = useMemo(
|
||||
() => new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, -0.16)),
|
||||
[],
|
||||
);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (!estatico) time.current += delta;
|
||||
const t = time.current;
|
||||
|
||||
const claridad = suave(tramo(t, CLARIDAD.desde, CLARIDAD.dura));
|
||||
const direccion = suave(tramo(t, DIRECCION.desde, DIRECCION.dura));
|
||||
const crecimiento = suave(tramo(t, CRECIMIENTO.desde, CRECIMIENTO.dura));
|
||||
// "Reposo vivo": a slow emissive breathing once the story is told.
|
||||
const pulso = Math.sin(t * 0.8) * 0.03 * crecimiento;
|
||||
|
||||
meshes.forEach((ref, i) => {
|
||||
const mesh = ref.current;
|
||||
if (!mesh) return;
|
||||
const d = deriva[i];
|
||||
|
||||
// Caos: free drift around the frame.
|
||||
const posCaos = new THREE.Vector3(
|
||||
Math.sin(t * d.fx + i * 2.1) * 1.6,
|
||||
Math.cos(t * d.fy + i) * 1.1 + 0.7,
|
||||
Math.sin(t * d.fz + i * 3) * 0.9,
|
||||
);
|
||||
const rotCaos = new THREE.Quaternion().setFromEuler(
|
||||
new THREE.Euler(t * d.rx, t * (d.ry - i * 0.2), t * 0.5),
|
||||
);
|
||||
|
||||
// Claridad: the "≡" of the wordmark.
|
||||
const posSimbolo = new THREE.Vector3(0, 0.7 - i * 0.7, 0);
|
||||
// Dirección: the ascending staircase.
|
||||
const posEscalera = new THREE.Vector3(i * 1.15 - 1.15, 0.35 + i * 0.8, 0);
|
||||
|
||||
mesh.position.copy(
|
||||
posCaos.lerp(posSimbolo, claridad).lerp(posEscalera, direccion),
|
||||
);
|
||||
mesh.quaternion.slerpQuaternions(rotCaos, qSimbolo, claridad);
|
||||
mesh.quaternion.slerp(qEscalera, direccion);
|
||||
|
||||
// Crecimiento: each line extends, the top one the most.
|
||||
mesh.scale.set(
|
||||
1 + direccion * 0.1 + crecimiento * (0.35 + i * 0.35),
|
||||
1,
|
||||
1,
|
||||
);
|
||||
|
||||
materiales[i].emissiveIntensity = crecimiento * (0.1 + i * 0.06) + pulso;
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<group>
|
||||
{meshes.map((ref, i) => (
|
||||
<mesh key={i} ref={ref} geometry={geometria} material={materiales[i]} />
|
||||
))}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Cámara — frontal, with the preview's gentle drift ───────────────────────
|
||||
|
||||
const CAMARA: [number, number, number] = [0, 0.9, 8];
|
||||
const MIRA: [number, number, number] = [0, 0.7, 0];
|
||||
|
||||
function Camara({ estatico }: { estatico: boolean }) {
|
||||
const { camera, invalidate } = useThree();
|
||||
const time = useRef(estatico ? 10 : 0);
|
||||
|
||||
useEffect(() => {
|
||||
camera.position.set(...CAMARA);
|
||||
camera.lookAt(...MIRA);
|
||||
invalidate();
|
||||
}, [camera, invalidate]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (estatico) return;
|
||||
time.current += delta;
|
||||
camera.position.set(
|
||||
CAMARA[0] + Math.sin(time.current * 0.25) * 0.5,
|
||||
CAMARA[1],
|
||||
CAMARA[2],
|
||||
);
|
||||
camera.lookAt(...MIRA);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Iluminación — the three lights of the approved preview ──────────────────
|
||||
|
||||
function Luces() {
|
||||
return (
|
||||
<>
|
||||
<ambientLight intensity={1.4} color="#ffffff" />
|
||||
<directionalLight position={[4, 8, 5]} intensity={2.2} color="#ffffff" />
|
||||
<pointLight position={[0, 2, 4]} intensity={10} distance={25} color={DORADO} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// ── WebGL Guard ─────────────────────────────────────────────────────────────
|
||||
|
||||
class SiHayWebGL extends Component<{ children: ReactNode }, { falla: boolean }> {
|
||||
state = { falla: false };
|
||||
static getDerivedStateFromError() { return { falla: true }; }
|
||||
render() { return this.state.falla ? null : this.props.children; }
|
||||
}
|
||||
|
||||
// ── El Componente ───────────────────────────────────────────────────────────
|
||||
|
||||
export function EscenaClaridad({ className = '' }: { className?: string }) {
|
||||
const reducido = usePrefersReducedMotion();
|
||||
const contenedor = useRef<HTMLDivElement>(null);
|
||||
const [enVista, setEnVista] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const el = contenedor.current;
|
||||
if (!el || typeof IntersectionObserver === 'undefined') return;
|
||||
const obs = new IntersectionObserver(([entrada]) => setEnVista(entrada.isIntersecting));
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, []);
|
||||
|
||||
const animando = enVista && !reducido;
|
||||
|
||||
return (
|
||||
<div ref={contenedor} className={className} aria-hidden="true">
|
||||
<SiHayWebGL>
|
||||
<Canvas
|
||||
dpr={[1, 2]}
|
||||
frameloop={animando ? 'always' : 'demand'}
|
||||
camera={{ position: CAMARA, fov: 38, near: 0.1, far: 100 }}
|
||||
gl={{ antialias: true, alpha: true, powerPreference: 'low-power' }}
|
||||
style={{ background: 'transparent' }}
|
||||
>
|
||||
<Camara estatico={reducido} />
|
||||
<Luces />
|
||||
<TresLineas estatico={reducido} />
|
||||
</Canvas>
|
||||
</SiHayWebGL>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Diferencias clave vs. el archivo viejo: fuera `Anillos`, `Simbolo`, `Suelo`/`texturaSombra` y la `pointLight` azul; el `fov` pasa de 40 a 38 y `far` de 60 a 100 (los del preview); `Camara` recibe `estatico` para congelar la deriva en reduced-motion.
|
||||
|
||||
- [ ] **Step 3: Correr las pruebas del componente**
|
||||
|
||||
Run: `pnpm test components/primitives/__tests__/EscenaClaridad.test.tsx`
|
||||
Expected: 4 passed — el wrapper no cambió de contrato.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add components/primitives/EscenaClaridad.tsx
|
||||
git commit -m "feat(hero): el astrolabio deja paso a «Las Tres Líneas» — caos, ≡, escalera, crecimiento"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Verificación completa y ajuste de luces
|
||||
|
||||
**Files:**
|
||||
- Modify (solo si hace falta): `components/primitives/EscenaClaridad.tsx`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 1 completo.
|
||||
- Produces: nada nuevo — verificación.
|
||||
|
||||
- [ ] **Step 1: Typecheck y lint**
|
||||
|
||||
Run: `npx tsc --noEmit && pnpm lint`
|
||||
Expected: sin errores. Si `three@0.185` tipa distinto algún prop de luces, ajusta el valor, no el tipo.
|
||||
|
||||
- [ ] **Step 2: Suite completa**
|
||||
|
||||
Run: `pnpm test`
|
||||
Expected: todos pasan (incl. content/contraste/escala, intactos).
|
||||
|
||||
- [ ] **Step 3: Build de export estático**
|
||||
|
||||
Run: `pnpm build`
|
||||
Expected: export exitoso a `out/` sin errores de prerender.
|
||||
|
||||
- [ ] **Step 4: Humo visual**
|
||||
|
||||
Run: `pnpm dev` → abrir `http://localhost:3000`, dejar correr el relato completo (~10 s) y verificar: (a) las tres barras forman el ≡, (b) se vuelven escalera ascendente, (c) crecen con brillo, (d) reposo con deriva sutil sin reiniciar, (e) con DevTools → rendering → emulate prefers-reduced-motion, la escena monta directo en la escalera, quieta.
|
||||
|
||||
Nota de luces: el preview corrió en three 0.160; en 0.185 la caída física de la `pointLight` es igual pero si el dorado se ve apagado o quemado, ajusta `intensity` (rango sano 6–16) en `Luces` — es calibración, no cambio de concepto.
|
||||
|
||||
- [ ] **Step 5: Commit (solo si hubo ajuste)**
|
||||
|
||||
```bash
|
||||
git add components/primitives/EscenaClaridad.tsx
|
||||
git commit -m "fix(hero): calibración de luces de «Las Tres Líneas» para three 0.185"
|
||||
```
|
||||
|
||||
Si no hubo ajuste, este paso se omite.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
- **Cobertura del spec:** concepto/tabla de fases → Task 1 Step 2 (`CAOS_DURA`, `CLARIDAD`, `DIRECCION`, `CRECIMIENTO`, `pulso`, deriva de cámara); reposo vivo → `pulso` + drift de `Camara`; reduced-motion → `estatico` en `Camara`/`TresLineas` + `frameloop: demand`; fuera de vista → IntersectionObserver intacto; eliminaciones (Anillos/Suelo/pointLight azul) → código nuevo no los contiene; pruebas intactas → Steps 1/3; commit en rama → aprobado. ✔
|
||||
- **Placeholders:** ninguno — todo paso tiene código o comando exacto. ✔
|
||||
- **Consistencia de tipos:** `EscenaClaridad({ className }: { className?: string })` idéntico al consumido por `ClaridadHero.tsx:54` y por los tests. `Camara`/`TresLineas` comparten la prop `estatico: boolean`. ✔
|
||||
@@ -0,0 +1,247 @@
|
||||
# Rediseño completo · «El documento»
|
||||
|
||||
**Fecha:** 19/ago/2026
|
||||
**Rama:** a crear desde `fix/contraste-y-hero-visible`
|
||||
**Estado:** aprobado por el usuario (dirección y alcance), pendiente de la dueña
|
||||
|
||||
---
|
||||
|
||||
## 1. Encargo
|
||||
|
||||
Rediseñar la landing por completo. El usuario fijó qué sobrevive:
|
||||
|
||||
| Se conserva | Queda libre |
|
||||
|---|---|
|
||||
| La paleta entera (tokens de `tailwind.config.ts`) | Estructura y orden de secciones |
|
||||
| El wordmark `cre≡er` (SVG trazado del manual) | Copy |
|
||||
| Open Sans | Layout, retícula, escala tipográfica |
|
||||
| | Sistema gráfico, ilustraciones, fotografía |
|
||||
| | Movimiento |
|
||||
| | Arquitectura de componentes |
|
||||
|
||||
**Conflicto declarado y aceptado:** `docs/dueña-directrices.md` registra que la dueña pidió el 30/jul "algo de gráfico y movimiento". Esta dirección reduce ambos. Y su doc dice de los dolores y los puntos de inflexión: "ya están escritos en su voz — usarlos, no reescribirlos"; este spec los conserva verbatim (§10), pero reorganiza todo lo demás. Ambas cosas necesitan el visto bueno de la dueña antes de publicar.
|
||||
|
||||
## 2. Concepto rector
|
||||
|
||||
creeer vende un documento. Su propio deck lo dice: cada etapa "deja algo por escrito", y la etapa 08 *es* un One Year Plan.
|
||||
|
||||
**La página se comporta como el entregable en vez de describirlo.** Un documento estratégico escrito sobre el negocio del lector: secciones numeradas, columna de margen, reglas finas, medida de texto disciplinada, cero decoración ambiental. La página demuestra claridad en lugar de prometerla.
|
||||
|
||||
De ahí sale todo lo demás. Cuando una decisión no esté en este spec, la pregunta es: *¿esto lo haría un documento estratégico bien compuesto?*
|
||||
|
||||
## 3. Estructura: 6 secciones
|
||||
|
||||
| # | Sección | Fondo | Origen |
|
||||
|---|---|---|---|
|
||||
| 01 | Apertura | blanco | `Hero` |
|
||||
| 02 | Dónde estás | blanco | `PuntosInflexion` + `Dolores` |
|
||||
| 03 | Qué cambia | **navy** | `FraseAncla` + lead de `Disciplinas` |
|
||||
| 04 | El trabajo | blanco | `Proceso` |
|
||||
| 05 | Para quién · Quién | blanco | `ParaQuien` + `Estratega` |
|
||||
| 06 | Empezar | **navy** | `ComoEmpezar` + `Cierre` |
|
||||
|
||||
### 01 · Apertura
|
||||
Wordmark (`size="xl"`, con tagline), antetítulo, titular en `display`, una línea de quién, dos CTAs. Sin ilustración, sin campos de puntos. Altura natural — se acabó el `min-h-[80vh]`, que en móvil pelea con la barra del navegador.
|
||||
|
||||
Su columna de margen lleva el número `01` y su `MarcaSeccion` en `paso={0}`: las líneas más dispersas de toda la página. El sistema queda establecido desde la primera pantalla.
|
||||
|
||||
### 02 · Dónde estás
|
||||
Los seis puntos de inflexión como tabla de dos columnas: nombre (700) | situación (`cuerpo`), separados por filete `gris-claro`. Se lee entera en segundos.
|
||||
|
||||
Debajo, un aparte enmarcado con los cuatro dolores en primera persona, a `sumario` en cursiva, uno por línea con filete entre ellos. Cierra el remate de la dueña ("Esto no se resuelve con más esfuerzo…") a `titulo`, con la resolución en dorado.
|
||||
|
||||
**Desaparece el escenario fijado de 600vh**, con su `IntersectionObserver` manual, sus seis fotografías, sus velos, su raíl y su barra de progreso.
|
||||
|
||||
### 03 · Qué cambia — navy
|
||||
El centro emocional y el primer navy. La frase ancla va a `titulo` con la segunda cláusula en dorado — no a `display`, que se gasta una sola vez en toda la página y es del titular de 01 (§4). Debajo, el insight que hoy vive en `Disciplinas`: "No vendemos marketing. Vendemos claridad estratégica". Las tres disciplinas quedan como una línea de texto, no como tres tarjetas con icono.
|
||||
|
||||
### 04 · El trabajo — blanco
|
||||
Las ocho etapas como índice numerado, no como ocho tarjetas con foto. Cada etapa: número tabular, nombre a `sumario`/700, y los entregables como chips `azul-claro` con texto navy. Las tres fases aprobadas (Inmersión → Desafiar supuestos → Estrategia ejecutable) como tira encima del índice.
|
||||
|
||||
La etapa 08 se distingue por ser la última y por llevar su nombre a `titulo` en vez de a `sumario`, no por cambiar de color de fondo ni de tarjeta.
|
||||
|
||||
**Desaparecen las ocho fotografías** y los 27 iconos de entregable (a 18px con trazo efectivo de 1,2px no se distinguen entre sí).
|
||||
|
||||
### 05 · Para quién · Quién — blanco
|
||||
Dos columnas separadas por filete vertical: "Sí es para ti" / "No es para ti", cuatro criterios cada una. Sin losa navy — el navy está reservado a 03 y 06.
|
||||
|
||||
Debajo, quién está detrás: hueco de retrato (`aspect-[4/5]`), el 30+ como cifra estática, bio y la cita institucional. **La cifra ya no cuenta hacia arriba.**
|
||||
|
||||
### 06 · Empezar — navy
|
||||
Los tres pasos como lista numerada, el CTA principal a WhatsApp, y los tres enlaces de contacto. Cierra con el wordmark y el legal. Absorbe el footer.
|
||||
|
||||
## 4. Sistema tipográfico
|
||||
|
||||
Open Sans, cinco roles. Reemplazan a `ancla` y `seccion`, que se eliminan.
|
||||
|
||||
```ts
|
||||
fontSize: {
|
||||
display: ['clamp(3.5rem, 9vw, 7rem)', { lineHeight: '0.94', letterSpacing: '-0.03em' }],
|
||||
titulo: ['clamp(2.25rem, 4.5vw, 3.5rem)', { lineHeight: '1', letterSpacing: '-0.02em' }],
|
||||
sumario: ['clamp(1.375rem, 2vw, 1.75rem)', { lineHeight: '1.35', letterSpacing: '-0.01em' }],
|
||||
cuerpo: ['1.0625rem', { lineHeight: '1.7' }],
|
||||
rotulo: ['0.75rem', { lineHeight: '1.2', letterSpacing: '0.3em' }],
|
||||
}
|
||||
```
|
||||
|
||||
**Reglas duras:**
|
||||
|
||||
- `display` aparece **exactamente una vez** en toda la página: el titular de 01. Hoy hay cinco elementos empatados en 4,5rem, y por eso ninguno es el más grande.
|
||||
- Medida máxima de texto corrido: **68 caracteres** (`max-w-[68ch]`).
|
||||
- Pesos: 400 cuerpo · 600 énfasis y rótulos · 700 títulos · 800 display. **Prohibido `font-black`**: Open Sans llega a 800 y `layout.tsx` carga hasta 800, así que los 34 usos actuales caen a 800 de todos modos. Se elimina el peso 900 del vocabulario.
|
||||
- Números de sección y de etapa con `font-variant-numeric: tabular-nums`.
|
||||
- `text-balance` en titulares, `text-pretty` en párrafos.
|
||||
|
||||
## 5. Retícula
|
||||
|
||||
```
|
||||
│ 02 │ DÓNDE ESTÁS │
|
||||
│ ≡ │ │
|
||||
│ │ Seis situaciones. Reconoce la tuya. │
|
||||
│ │ │
|
||||
│ │ Crecer El siguiente salto │
|
||||
│ │ no tiene mapa. │
|
||||
```
|
||||
|
||||
- Contenedor: `max-w-content` sube de 1152px a **1280px**, con `px-6 md:px-12`.
|
||||
- `Seccion` monta `grid md:grid-cols-[7rem_minmax(0,1fr)] gap-x-10`. Columna izquierda: número tabular + `MarcaSeccion`. Columna derecha: todo el contenido.
|
||||
- En móvil colapsa a una columna; el número y la marca van en línea sobre el antetítulo.
|
||||
- La tensión entre reglas anchas y texto angosto (68ch dentro de 1280px) es deliberada: es lo que hace que la composición se lea editorial.
|
||||
|
||||
**Ritmo vertical**, tres niveles — hoy ocho secciones comparten `py-16 md:py-24` y la página va como un metrónomo:
|
||||
|
||||
| Nivel | Padding | Dónde |
|
||||
|---|---|---|
|
||||
| `compacta` | `py-20 md:py-28` | 04, 05 |
|
||||
| `normal` | `py-28 md:py-40` | 02 |
|
||||
| `momento` | `py-36 md:py-56` | 01, 03, 06 |
|
||||
|
||||
01 va en `momento` y sin `min-height`: la apertura respira por padding, no por alto de viewport.
|
||||
|
||||
## 6. Color
|
||||
|
||||
Los tokens no cambian. Cambia la disciplina de uso.
|
||||
|
||||
- **Un solo blanco.** Se eliminan `hondo-blanco` y `hondo-claro`: sus gradientes se solapan (`hondo-claro` empieza en `#FFFFFF`) y hacían indistinguibles cuatro secciones seguidas. Fondo claro = `#FFFFFF` plano.
|
||||
- **`gris-claro`** deja de ser fondo de sección: solo filetes y rellenos de ficha.
|
||||
- **Navy exactamente dos veces**, en 03 y 06. Se conserva `hondo-navy` para darles relieve.
|
||||
- **Dorado, tres trabajos y ninguno más:** el `≡` del wordmark, `MarcaSeccion`, y una palabra de acento por declaración. Se elimina el dorado como relleno de icono y como campo ambiental. Se eliminan `velo-dorado`, `velo-frio` y `dorado-metal`.
|
||||
- **`azul-claro #C8EEF5`** — hoy declarado y sin usar — pasa a ser el fondo de los chips de entregables en 04, con texto navy: **13,5:1**.
|
||||
- Se conserva `dorado-texto #8A6520` para texto pequeño dorado sobre claro.
|
||||
|
||||
**Contraste mínimo exigido:** 4,5:1 texto normal, 3:1 texto grande y gráficos con significado. Esto cierra los pendientes que quedaron abiertos en la revisión: el acento del titular, el 30+ de Estratega, la flecha entre fases y los iconos dorados sobre blanco — todos desaparecen o se recolorean.
|
||||
|
||||
## 7. `MarcaSeccion`: el ≡ como sistema
|
||||
|
||||
La única idea gráfica de la página, y la respuesta a "le falta gráfico".
|
||||
|
||||
Cada sección lleva su marca en la columna de margen, y **la marca progresa al bajar**: muchas líneas desordenadas al principio, tres barras alineadas al final. La filosofía del manual (Complejidad → Enfoque → Claridad) deja de estar ilustrada en un rincón y pasa a ser el mobiliario del documento.
|
||||
|
||||
**Implementación:** reutiliza la geometría ya probada de `lib/simbolo.ts` (`VIEWBOX_SIMBOLO`, `LINEAS_SIMBOLO`, `BARRAS_SIMBOLO`) y las posiciones dispersas de `SecuenciaClaridad`. `MarcaSeccion({ paso })` con `paso` de 0 a 5 interpola **estáticamente** cada línea entre su posición dispersa y su barra destino con factor `paso/5`; las líneas no supervivientes bajan de opacidad con el mismo factor. En `paso === 5` quedan las tres barras del `≡` exactas.
|
||||
|
||||
Sin animación, sin estado, sin JS: es un SVG calculado en render. Server component.
|
||||
|
||||
**Sustituye a todo el aparato gráfico actual.** Se eliminan diecisiete primitivas: `FondoPuntos`, `PatronPuntos`, `Resplandor`, `IlustracionNudo`, `IlustracionBifurcacion`, `IlustracionInflexion`, `IlustracionConvergencia`, `SecuenciaClaridad`, `SimboloMarca`, `TarjetaSpotlight`, `MarcoRetrato`, `EscenaDialogo`, `ParDialogo`, `FondoFoto`, `TextoDesenfoque`, `ContadorNumero`, `ProgresoLectura`, y el sistema de iconos `Icono` con sus 41 glifos.
|
||||
|
||||
## 8. Movimiento
|
||||
|
||||
**Un solo gesto: `revelar`.** 12px de subida + fade, 400ms, `cubic-bezier(0.22, 1, 0.36, 1)`, una vez, al entrar en viewport, escalonado 60ms entre hermanos.
|
||||
|
||||
Se eliminan: parallax, scroll fijado, contadores, spotlight, hover-lift, blur palabra a palabra, barra de progreso de lectura, compactación del header, dibujado de ilustraciones y puntos reactivos al cursor.
|
||||
|
||||
**Montado al revés de como está hoy — este es el punto crítico.** El export actual manda 112 elementos en `opacity: 0`; si el JS no llega, la página no se lee.
|
||||
|
||||
1. `app/layout.tsx` incluye un script inline bloqueante en `<head>`: `document.documentElement.classList.add('js')`.
|
||||
2. `globals.css`:
|
||||
```css
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.js [data-revelar] { opacity: 0; transform: translateY(12px); }
|
||||
.js [data-revelar][data-visible] { animation: revelar 400ms cubic-bezier(.22,1,.36,1) both;
|
||||
animation-delay: calc(var(--i, 0) * 60ms); }
|
||||
}
|
||||
@keyframes revelar { to { opacity: 1; transform: none; } }
|
||||
```
|
||||
3. `<Revelar indice>` (client) pinta un envoltorio con `data-revelar` y `style={{ '--i': indice }}`. Un único `IntersectionObserver` a nivel de módulo, compartido por todas las instancias, marca `data-visible` al entrar y deja de observar. `indice` lo pasa la sección: es la posición del bloque entre sus hermanos, y sirve solo para escalonar el retardo. Cuando se omite, vale 0.
|
||||
|
||||
**Consecuencias:** sin JS, todo visible. Con `prefers-reduced-motion: reduce`, el estado oculto nunca llega a aplicarse — no hace falta el hook `usePrefersReducedMotion` ni el spread `motionProps` en ninguna sección.
|
||||
|
||||
## 9. Arquitectura
|
||||
|
||||
- **Fuera `framer-motion`** (53,6 kB del bundle compartido) y **fuera `embla-carousel`** + `embla-carousel-autoplay`, que ya no usa nadie.
|
||||
- **Las secciones vuelven a ser server components.** Hoy las diez son `'use client'`. El único componente cliente será `Revelar`.
|
||||
- **Siete primitivas**, frente a las ~20 actuales:
|
||||
|
||||
| Primitiva | Papel |
|
||||
|---|---|
|
||||
| `Container` | ancho y padding horizontal |
|
||||
| `Seccion` | la retícula de margen + el ritmo vertical + fondo |
|
||||
| `MarcaSeccion` | la progresión del ≡ |
|
||||
| `Antetitulo` | el rótulo dorado (ya existe) |
|
||||
| `CTA` | los botones (ya existe) |
|
||||
| `Wordmark` | el logotipo (ya existe, intacto) |
|
||||
| `Revelar` | el único gesto de movimiento |
|
||||
|
||||
- `lib/content.ts` sigue siendo el source of truth del copy.
|
||||
- `lib/motion.ts` se elimina.
|
||||
|
||||
## 10. Copy
|
||||
|
||||
**Verbatim, sin tocar** (son palabras de la dueña, de su deck):
|
||||
|
||||
- Los seis puntos de inflexión (nombre + situación).
|
||||
- Los cuatro dolores en primera persona.
|
||||
- El remate: "Esto no se resuelve con más esfuerzo. Se resuelve con una estrategia clara."
|
||||
- Las frases-ancla aprobadas.
|
||||
- Los nombres de las ocho etapas y sus entregables.
|
||||
- El insight: "No vendemos marketing. Vendemos claridad estratégica."
|
||||
|
||||
**Se escribe nuevo** solo el tejido conectivo que pide la estructura nueva: antetítulos y títulos de las seis secciones, y los sumarios de una línea bajo cada uno. Se recortan las respuestas largas del recorrido, que existían para llenar seis pantallas fijadas.
|
||||
|
||||
**Se mantiene la prohibición de escasez** en 01 (`cupos|cohorte|generación fundadora`), decisión explícita de la dueña.
|
||||
|
||||
Los placeholders `TODO dueña` (WhatsApp, email, LinkedIn, dominio, retrato) siguen siendo placeholders. No se inventan valores.
|
||||
|
||||
## 11. Accesibilidad
|
||||
|
||||
- Skip link al `<main>` — hoy no hay.
|
||||
- Un solo `<h1>` (01); cada sección con su `<h2>`.
|
||||
- `100svh`/`100dvh` en vez de `100vh` donde haga falta.
|
||||
- Foco visible con contraste ≥ 3:1 **contra el fondo sobre el que aparece**: anillo navy sobre claro, dorado sobre navy. El dorado global de hoy da 2,76:1 sobre blanco.
|
||||
- Nada de contenido oculto tras estado de scroll.
|
||||
|
||||
## 12. Tests
|
||||
|
||||
Se mantiene la convención del repo: **copy y contrato de marca, no estilos**, salvo cuando el estilo *es* el contrato (contraste, contenido oculto).
|
||||
|
||||
Se conservan intactos: `simbolo.test.ts`, `Wordmark.test.tsx`, `Antetitulo.test.tsx`, `CTA.test.tsx`, `Container.test.tsx`, `contraste.test.ts` y `content.test.ts`.
|
||||
|
||||
Se reescriben, no se conservan, los tests de sección: las seis secciones nuevas no se corresponden una a una con las diez viejas. Cada una estrena su test de copy, y el test negativo de escasez viaja de `Hero.test.tsx` al de la sección 01.
|
||||
|
||||
Se añaden:
|
||||
|
||||
1. **Guarda de contenido oculto**, generalizada a las seis secciones: ninguna renderiza elementos con `style.opacity === '0'` ni `style.filter` con blur. Es el fallo que ya nos mordió una vez.
|
||||
2. **`MarcaSeccion`**: `paso={5}` produce exactamente las tres barras de `LINEAS_SIMBOLO`; `paso={0}` produce las nueve líneas dispersas.
|
||||
3. **Guarda de escala**: la página monta exactamente un elemento con `text-display`.
|
||||
|
||||
Se eliminan los tests de los componentes eliminados.
|
||||
|
||||
## 13. Criterios de aceptación
|
||||
|
||||
1. `pnpm test`, `npx tsc --noEmit` y `pnpm lint` limpios.
|
||||
2. `pnpm build` correcto, y `out/index.html` con **cero** ocurrencias de `opacity:0` y de `filter:blur`.
|
||||
3. `framer-motion` y `embla-carousel` no aparecen en `package.json` ni en el bundle.
|
||||
4. Exactamente **dos** secciones navy y **un** elemento a escala `display`.
|
||||
5. Todo par texto/fondo cumple 4,5:1 (normal) o 3:1 (grande y gráficos).
|
||||
6. Con JavaScript desactivado, la página se lee completa.
|
||||
|
||||
## 14. Fuera de alcance
|
||||
|
||||
- El retrato real de la estratega y los datos de contacto: siguen pendientes de la dueña.
|
||||
- Los dos casos de proyectos reales que la dueña prometió el 29/jul.
|
||||
- Cualquier decisión sobre si la escasez ("Generación Fundadora") puede vivir fuera del hero.
|
||||
|
||||
## 15. Riesgos
|
||||
|
||||
1. **Va contra el último encargo de la dueña.** Pidió "algo de gráfico y movimiento"; esto reduce ambos. El argumento a favor es que el ≡ progresivo es más gráfico que diez adornos ambientales, y que un solo gesto de movimiento se nota más que doce repartidos. Es su marca: necesita aprobarlo.
|
||||
2. **Se retira trabajo real.** El recorrido fijado, cinco ilustraciones SVG, catorce fotografías acreditadas y el sistema de iconos son semanas de trabajo. Queda todo en git, pero sale de la página.
|
||||
3. **Con una sola familia tipográfica, la distinción depende entera de la composición.** Si el resultado se siente genérico, la salida natural sería una segunda familia para display — y eso choca con la decisión de tipografía que el usuario marcó como intocable.
|
||||
Reference in new issue
Block a user