Saltar al contenido
motionframe

Buscar piezas

Categorías

La pieza

Qué hace y dónde encaja

Un hero editorial de pantalla completa construido alrededor de un titular gigante en Fraunces variable. Cada letra sube desde una máscara al cargar y el punto final rebota en color de acento. Al hacer scroll, el hero queda fijo y el titular pasa de un trazo finísimo y aireado (peso 160) a un negro rotundo (peso 900): el espaciado entre letras se recalcula en cada fotograma para que la línea mantenga exactamente su anchura. Un contador de peso con barra acompaña el recorrido. Debajo, subtítulo, dos CTA y una franja de datos. En vertical el titular se recompone en escalera, palabra a palabra. Incluye interruptor claro/oscuro. Encaja como portada de estudios creativos, arquitectura, bodegas, restaurantes o cualquier marca que quiera presentarse con una sola frase contundente. Funciona con cualquier paleta: papel y tinta, verde oliva, azul añil o negro total.

Prompt
≈11.290 tokens · con Lite
Librerías
gsap
Demo
22,7 KB · ligero
Prueba en frío
9,8/10 · 30-09-2026

Antes de copiar

Tu marca, dentro del prompt

Cambia lo que quieras y copia: el prompt sale ya con el nombre, los colores y los textos de tu cliente. Lo que dejes en blanco se queda como en la demo.

Ver 9 campos más
Colores

Sin cambios: se copia con los valores de la demo.

Gratis y entero

El prompt, entero y a la vista

Qué trae el prompt, por apartados

  1. 01Qué es
  2. 02Librerías y esqueleto
  3. 03Tokens de diseño
  4. 04Estructura y contenido
  5. 05Movimiento
  6. 06Código clave
  7. 07Interacción
  8. 08Adaptación a móvil y tablet
  9. 09Accesibilidad y movimiento reducido
  10. 10Rendimiento
  11. 11Personalización
  12. 12Comprobación final
Completo
5.497 palabras · ≈11.290 tokens
Lite
1.464 palabras · ≈2.762 tokens
Librerías
gsap 3.15.0
Peso de la demo
22,7 KB (ligero)
Resolución máxima (DPR)
2
Se pausa fuera de pantalla
sí
Respeta «menos movimiento»
sí

Tokens estimados para español (≈3,6 caracteres por token): sirven para orientarte, no son exactos.

prompt.md
# Recrea en un único archivo HTML: Hero con titular gigante

Un hero editorial de estudio: un titular a sangre en serif variable que entra letra a letra desde una máscara y, al hacer scroll, pasa de un trazo finísimo y aireado a un negro rotundo y apretado sin cambiar de anchura; con versión clara y oscura.

Eres un desarrollador front-end creativo senior. Genera un único `index.html` autocontenido que
reproduzca exactamente la pieza descrita: mismo layout, textos, colores, movimiento e interacción.
HTML, CSS y JS en un solo archivo, sin build ni frameworks. Librerías solo por CDN y en las versiones
indicadas. Todos los valores numéricos de este documento son definitivos: úsalos tal cual.
Responde solo con el archivo completo.

## Qué es

Una sección hero de pantalla completa para la web de **Taller Meridiano** (Estudio de marca y web, con sede en Valdera). Todo gira alrededor de un titular gigante de dos líneas:

- Línea 1, redonda: **Diseño**
- Línea 2, cursiva: **con acento.** (el punto final se pinta en color de acento y entra con un rebote propio).

Cómo se comporta:

1. **Entrada.** Cuando las fuentes están cargadas, cada letra sube desde debajo de su línea (la línea actúa como máscara: por debajo de ella no se ve nada) con un leve giro que se endereza. La línea 1 empieza primero y la 2 la sigue con 0,22 s de retraso. Después aparecen el subtítulo, los dos botones (CTA) y la franja de datos inferior.
2. **Cada línea ocupa exactamente el ancho útil** (de gutter a gutter). El tamaño de fuente de cada línea se calcula en JS midiendo el texto real, de modo que al peso máximo (900) la línea llena el ancho justo (salvo que el tope de alto del punto 5 obligue a reducirlo).
3. **Scroll = peso.** El hero mide 240svh y su contenido está fijado con `position: sticky`. Al recorrerlo, el eje `wght` de la fuente variable pasa de 160 a 900. Para que la línea **no cambie de anchura** mientras engorda, el `letter-spacing` se recalcula en cada fotograma: con peso 160 las letras quedan muy separadas (aire), con 900 quedan con espaciado 0 (apretadas). La anchura de la línea es constante; lo que cambia es la relación negro/blanco.
4. **Lectura de peso.** Abajo a la derecha, un contador «Peso 160 … 900» con cifras tabulares y una barrita de acento que se llena con el mismo progreso.
5. **Composición adaptativa.** En pantallas verticales (ancho/alto < 0,9) cada palabra pasa a su propia línea (queda «Diseño / con / acento.»). La primera línea es el ancla: ninguna línea posterior puede ser más grande que ella; si una línea se queda corta (p. ej. «con»), se alinea a la derecha formando una escalera editorial. Si el titular no cabe en alto, se reduce todo a la vez y las líneas a partir de la segunda se alinean a la derecha (en monitores anchos y bajos queda una diagonal «Diseño» izquierda / «con acento.» derecha).
6. **Tema claro / oscuro** con un interruptor tipo píldora en la navegación («Claro | Oscuro»). Arranca según `prefers-color-scheme` (o la última elección guardada) y la transición de colores dura 0,7 s.
7. Debajo del hero hay una sección corta «Trabajo reciente» con cuatro proyectos en filas y un pie con contacto, para que el hero se vea en contexto al terminar el scroll.

Sensación: papel cálido, tinta casi negra, un único acento rojo almagra; tipografía con intención, mucho aire, detalles finos (grano sutil, filetes de 1 px, cifras tabulares). Nada de sombras, degradados ni glassmorphism.

## Librerías y esqueleto

- GSAP 3.15.0 con ScrollTrigger y SplitText (todos por jsDelivr, al final del `<body>`, en este orden):
  - `https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js`
  - `https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js`
  - `https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/SplitText.min.js`
- Fuentes de Google (ambas con ñ, tildes, ¿ y ¡):
  - **Fraunces** variable, redonda y cursiva, ejes `opsz` 9–144 y `wght` 100–900 → titular y titulares secundarios.
  - **Instrument Sans** variable `wght` 400–700 → texto, navegación, botones, datos.
- Sin imágenes: el sello del logo, las flechas y el grano son SVG en línea.
- Un script mínimo en el `<head>` fija el tema antes de pintar (evita el parpadeo) y añade la clase `js` al `<html>` (el CSS oculta el titular hasta que el JS lo prepara).

`<head>` literal:

```html
<!doctype html>
<html lang="es" data-tema="claro">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Taller Meridiano — Estudio de marca y web en Valdera</title>
<meta name="description" content="Taller Meridiano: Estudio de marca y web en Valdera. Diseñamos desde Valdera para quien tiene algo que contar y no quiere parecerse a nadie.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,100..900;1,9..144,100..900&family=Instrument+Sans:wght@400..700&display=swap" rel="stylesheet">
<script>
  (function () {
    var d = document.documentElement, t = null;
    try { t = localStorage.getItem("tp-tema"); } catch (e) {}
    if (t !== "claro" && t !== "oscuro") t = matchMedia("(prefers-color-scheme: dark)").matches ? "oscuro" : "claro";
    d.dataset.tema = t;
    d.classList.add("js");
  })();
</script>
<style>
  /* aquí va TODO el CSS de «Tokens de diseño» y «Código clave» */
</style>
</head>
```

## Tokens de diseño

Bloque literal (inicio del `<style>`). El tema oscuro se activa con `data-tema="oscuro"` en `<html>`.

```css
:root {
  --papel: #efe9dd;
  --tinta: #17140f;
  --tinta-suave: #5b5448;
  --linea: rgba(23, 20, 15, .16);
  --acento: #b33810;
  --sobre-acento: #efe9dd;
  --grano: .07;
  --gutter: clamp(14px, 1.6vw, 28px);
  --f-display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --f-texto: "Instrument Sans", "Helvetica Neue", Arial, sans-serif;
  --e-salida: cubic-bezier(.16, 1, .3, 1);
  --e-tema: cubic-bezier(.65, 0, .35, 1);
  color-scheme: light;
}
[data-tema="oscuro"] {
  --papel: #121110;
  --tinta: #efe7d8;
  --tinta-suave: #a39a8a;
  --linea: rgba(239, 231, 216, .16);
  --acento: #ff6b3d;
  --sobre-acento: #121110;
  --grano: .09;
  color-scheme: dark;
}
```

Resumen de uso:

| Token | Claro | Oscuro | Dónde |
|---|---|---|---|
| `--papel` | #efe9dd | #121110 | fondo |
| `--tinta` | #17140f | #efe7d8 | titular, texto fuerte, botón lleno |
| `--tinta-suave` | #5b5448 | #a39a8a | subtítulo, datos, etiquetas |
| `--linea` | tinta al 16 % | tinta al 16 % | filetes de 1 px |
| `--acento` | #b33810 | #ff6b3d | punto final, barra de peso, hover del botón, cursiva del h2 |
| `--grano` | .07 | .09 | opacidad del ruido SVG |

Tipografía:

- Titular: Fraunces, `font-variation-settings: "wght" <160…900>, "opsz" 144`, `line-height: .8`, tamaño calculado por JS (en 1440×900 sale ≈ 439 px en la línea 1 y ≈ 267 px en la 2, ya con el tope de alto aplicado).
- H2 de la sección siguiente: Fraunces 350, `clamp(30px, 3.6vw, 56px)`, `line-height 1.04`, `letter-spacing -.02em`, `opsz` 96.
- Nombres de proyecto: Fraunces 400, `clamp(22px, 2.2vw, 34px)`, `opsz` 72.
- Texto: Instrument Sans 16 px / 1.55; subtítulo `clamp(16px, 1.15vw, 18px)`; datos 12 px en mayúsculas con `letter-spacing .04em` y `tabular-nums`; botones 600 15 px.

## Estructura y contenido

Árbol:

```
body
├─ a.saltar  «Saltar al contenido»
└─ main#contenido
   ├─ section.hero#inicio (240svh; 200svh en móvil)
   │  └─ div.hero__pin (sticky, 100svh, grid de 4 filas: nav · titular · pie · meta)
   │     ├─ header.nav: marca (sello SVG + nombre) · nav (Proyectos, Estudio, Contacto) · botón .tema
   │     ├─ div.hero__titular: p.eyebrow + h1.titular (span.sr con el texto completo + span.titular__lineas aria-hidden con las líneas)
   │     ├─ div.hero__pie: p.sub + div.ctas (a.btn--lleno con flecha, a.btn--linea)
   │     └─ div.hero__meta: coordenadas · pista «Desliza: el titular gana peso» · lectura de peso
   └─ section.trabajo#trabajo: cabecera (etiqueta + h2) · ul.lista (4 filas) · footer.pie
```

`<body>` literal completo (textos incluidos; usa estos textos exactos):

```html
<body>
<a class="saltar" href="#contenido">Saltar al contenido</a>
<main id="contenido">
  <section class="hero" id="inicio" aria-labelledby="titular">
    <div class="hero__pin">
      <header class="nav">
        <a class="marca" href="#inicio" aria-label="Taller Meridiano, inicio">
          <svg class="marca__sello" viewBox="0 0 26 26" aria-hidden="true"><circle cx="13" cy="13" r="12" fill="none" stroke="currentColor" stroke-width="1.4"/><path d="M4 16.5h18" stroke="currentColor" stroke-width="1.4"/><path d="M7.5 16.5a5.5 5.5 0 0 1 11 0" fill="currentColor"/></svg>
          <span>Taller Meridiano</span>
        </a>
        <nav aria-label="Principal">
          <ul class="nav__links">
            <li><a href="#trabajo">Proyectos</a></li>
            <li><a href="#trabajo">Estudio</a></li>
            <li><a href="mailto:hola@tallermeridiano.com">Contacto</a></li>
          </ul>
        </nav>
        <button class="tema" type="button" aria-pressed="false" aria-label="Modo oscuro">
          <span class="tema__claro" aria-hidden="true">Claro</span><span class="tema__oscuro" aria-hidden="true">Oscuro</span>
        </button>
      </header>

      <div class="hero__titular">
        <p class="eyebrow">Estudio de marca y web — Valdera, desde 2014</p>
        <h1 class="titular" id="titular">
          <span class="sr">Diseño con acento.</span>
          <span class="titular__lineas" aria-hidden="true">
            <span class="linea" data-linea>Diseño</span>
            <span class="linea linea--cursiva" data-linea data-cursiva>con acento.</span>
          </span>
        </h1>
      </div>

      <div class="hero__pie">
        <p class="sub">Identidades, webs y packaging para <strong>negocios con alma que quieren sonar a ellos mismos</strong>. Diseñamos desde Valdera para quien tiene algo que contar y no quiere parecerse a nadie.</p>
        <div class="ctas">
          <a class="btn btn--lleno" href="#trabajo">Ver proyectos
            <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M2 8h11M9 4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.6"/></svg>
          </a>
          <a class="btn btn--linea" href="mailto:hola@tallermeridiano.com">Hablemos de tu marca</a>
        </div>
      </div>

      <div class="hero__meta">
        <span>Valdera · 00°00′ N 00°00′ E</span>
        <span class="pista" aria-hidden="true"><span class="pista__raya"></span>Desliza: el titular gana peso</span>
        <span class="peso" aria-hidden="true">Peso <b class="peso__num">160</b><span class="peso__barra"><i></i></span></span>
      </div>
    </div>
  </section>

  <section class="trabajo" id="trabajo" aria-labelledby="trabajo-tit">
    <div class="trabajo__cab">
      <p class="trabajo__eti">(02) Trabajo reciente</p>
      <h2 class="trabajo__tit" id="trabajo-tit">Cuatro marcas con alma que ya <em>suenan a sí mismas.</em></h2>
    </div>
    <ul class="lista">
      <li><span class="n">01</span><h3>Bodega Arrayán</h3><span class="s">Identidad y etiquetas</span><span class="l">Lumera</span><span class="a">2026</span></li>
      <li><span class="n">02</span><h3>Hotel Casa Levante</h3><span class="s">Web y señalética</span><span class="l">Sirmio</span><span class="a">2025</span></li>
      <li><span class="n">03</span><h3>Aceites Sierra Chica</h3><span class="s">Packaging</span><span class="l">Orlana</span><span class="a">2025</span></li>
      <li><span class="n">04</span><h3>Horno La Espiga Dorada</h3><span class="s">Identidad y local</span><span class="l">Valdera</span><span class="a">2024</span></li>
    </ul>
    <footer class="pie">
      <span>© 2026 Taller Meridiano · Calle Álamos 12, Valdera</span>
      <span><a href="mailto:hola@tallermeridiano.com">hola@tallermeridiano.com</a> · <a href="tel:+00 000 000 000">+00 000 000 000</a></span>
    </footer>
  </section>
</main>

<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/SplitText.min.js"></script>
<script>
  /* aquí va el JS de «Código clave» */
</script>
</body>
</html>
```

Notas de contenido:

- En `tel:` escribe el teléfono sin espacios (p. ej. `tel:+000000000000`).
- Los cuatro proyectos de la lista son de ejemplo y se quedan tal cual si no hay otros: Bodega Arrayán (Identidad y etiquetas, Lumera, 2026), Hotel Casa Levante (Web y señalética, Sirmio, 2025), Aceites Sierra Chica (Packaging, Orlana, 2025), Horno La Espiga Dorada (Identidad y local, Valdera, 2024).
- El `span.sr` del h1 contiene el titular completo para lectores de pantalla; las líneas visuales van en `span.titular__lineas` con `aria-hidden="true"`, porque el JS las reescribe y parte en letras.

## Movimiento

Entrada (timeline de GSAP con `delay: 0.15`, `defaults: { ease: "power4.out" }`; posiciones en segundos desde el inicio del timeline). Arranca cuando las fuentes han cargado o, como mucho, a los 2,5 s.

| Elemento | Propiedad | De → a | Duración | Easing | Posición / retardo | Disparador |
|---|---|---|---|---|---|---|
| `.nav` | y, opacity | −16 px, 0 → 0, 1 | 0,9 s | power4.out | 0 | carga |
| `.eyebrow` | opacity | 0 → 1 | 0,8 s | power2.out | 0,2 | carga |
| letras línea *i* (sin el punto) | yPercent, rotate | 180, 7° → 0, 0° (`transformOrigin "0% 100%"`) | 1,2 s | power4.out | 0,1 + i × 0,22; stagger 0,045 | carga |
| punto final `.ch--punto` | scale | 0 → 1 (`transformOrigin "50% 85%"`) | 0,7 s | back.out(3) | 1,15 | carga |
| `.hero__pie > *` (subtítulo y bloque de CTA) | y, opacity | 24 px, 0 → 0, 1 | 1 s | expo.out | 1,0; stagger 0,1 | carga |
| `.hero__meta` | opacity | 0 → 1 | 0,8 s | power2.out | 1,35 | carga |
| titular (todas las líneas) | `wght` + `letter-spacing` | 160 → 900 (espaciado calculado) | ligado al scroll | lineal, `scrub: 0.9` | hero `top top` → `bottom bottom` | scroll |
| `.peso__num` / `.peso__barra i` | texto / scaleX | 160 → 900 / 0 → 1 | ligado al scroll | lineal | igual que arriba | scroll |
| colores de tema | background-color, color, border-color | claro ↔ oscuro | 0,7 s | cubic-bezier(.65, 0, .35, 1) | — | clic en .tema |
| mando de la píldora `.tema::before` | translateX | 0 → 100 % | 0,55 s | cubic-bezier(.16, 1, .3, 1) | — | clic |
| subrayado de enlaces de nav | scaleX | 0 → 1 (origen izq.; sale por la dcha.) | 0,5 s | cubic-bezier(.16, 1, .3, 1) | — | hover |
| flecha del botón lleno | transform | 0 → translateX(4px) rotate(−45°) | 0,5 s | cubic-bezier(.16, 1, .3, 1) | — | hover |
| botones | fondo, color, borde | ver CSS | 0,45 s | cubic-bezier(.16, 1, .3, 1) | — | hover |
| raya de la pista | translateX | −100 % → 100 % (60 % del ciclo), bucle | 2,4 s | cubic-bezier(.16, 1, .3, 1) | infinito, pausado fuera de pantalla | automático |
| nombre de proyecto | translateX, wght | 0 → 10 px, 400 → 640 | 0,6 s | cubic-bezier(.16, 1, .3, 1) | — | hover (solo `hover: hover`) |

## Código clave

### CSS completo (va después de los tokens, dentro del mismo `<style>`)

```css
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; overflow-x: clip; }
html { scroll-behavior: smooth; }
body {
  background: var(--papel);
  color: var(--tinta);
  font-family: var(--f-texto);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  transition: background-color .7s var(--e-tema), color .7s var(--e-tema);
}
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 50; pointer-events: none;
  opacity: var(--grano);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 .9 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  mix-blend-mode: multiply;
}
[data-tema="oscuro"] body::after { mix-blend-mode: screen; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--acento); outline-offset: 4px; border-radius: 2px; }
.sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.saltar { position: absolute; left: var(--gutter); top: -60px; z-index: 60; background: var(--tinta); color: var(--papel); padding: 10px 16px; border-radius: 999px; text-decoration: none; font-weight: 600; }
.saltar:focus { top: 12px; }

/* ——— Hero ——— */
.hero { position: relative; height: 240svh; }
.hero__pin {
  position: sticky; top: 0;
  height: 100svh; min-height: 560px;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  padding: 0 var(--gutter);
  overflow: clip;
}
.nav {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  height: 72px;
  border-bottom: 1px solid var(--linea);
  position: relative;
}
.marca { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; font-weight: 600; letter-spacing: -.01em; font-size: 15px; }
.marca__sello { width: 26px; height: 26px; flex: none; }
.nav__links { display: flex; align-items: center; gap: 28px; list-style: none; margin: 0; padding: 0; font-size: 14px; }
.nav__links a { text-decoration: none; position: relative; padding: 6px 0; }
.nav__links a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: right; transition: transform .5s var(--e-salida); }
.nav__links a:hover::after { transform: scaleX(1); transform-origin: left; }
.tema {
  position: relative; display: inline-grid; grid-template-columns: 1fr 1fr; align-items: center;
  height: 34px; padding: 3px; border: 1px solid var(--linea); border-radius: 999px;
  background: transparent; color: var(--tinta); font: 500 13px/1 var(--f-texto); cursor: pointer;
  transition: border-color .7s var(--e-tema), color .7s var(--e-tema);
}
.tema span { position: relative; z-index: 1; padding: 0 12px; transition: color .5s var(--e-tema); }
.tema::before {
  content: ""; position: absolute; top: 3px; bottom: 3px; left: 3px; width: calc(50% - 3px);
  border-radius: 999px; background: var(--tinta);
  transition: transform .55s var(--e-salida), background-color .7s var(--e-tema);
}
.tema[aria-pressed="false"] .tema__claro, .tema[aria-pressed="true"] .tema__oscuro { color: var(--papel); }
.tema[aria-pressed="true"]::before { transform: translateX(100%); }

.hero__titular { display: flex; flex-direction: column; justify-content: flex-end; min-height: 0; padding-top: 20px; }
.eyebrow { display: flex; align-items: center; gap: 12px; margin: 0 0 14px; font-size: 13px; letter-spacing: .02em; color: var(--tinta-suave); transition: color .7s var(--e-tema); }
.eyebrow::before { content: ""; width: 28px; height: 1px; background: currentColor; }
.titular { margin: 0; font-family: var(--f-display); font-weight: 400; color: var(--tinta); }
.titular__lineas { display: block; }
.linea {
  display: block; white-space: nowrap;
  font-size: clamp(3.5rem, 17vw, 20rem);
  line-height: .8;
  padding: .12em 0 .2em; margin: -.12em 0 -.2em;
  clip-path: inset(-.6em -.6em 0 -.6em);
}
.linea--cursiva { font-style: italic; }
.linea--derecha { text-align: right; }
.linea__txt { display: inline-block; font-variation-settings: "wght" 160, "opsz" 144; will-change: letter-spacing; }
.ch { display: inline-block; will-change: transform; }
.ch--punto { color: var(--acento); transition: color .7s var(--e-tema); }
.js .titular__lineas { visibility: hidden; }
.js .hero__pie > *, .js .hero__meta, .js .eyebrow { opacity: 0; }

.hero__pie {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 24px 48px;
  padding: 28px 0 24px;
}
.sub { margin: 0; max-width: 46ch; font-size: clamp(16px, 1.15vw, 18px); color: var(--tinta-suave); transition: color .7s var(--e-tema); }
.sub strong { color: var(--tinta); font-weight: 600; transition: color .7s var(--e-tema); }
.ctas { display: flex; gap: 10px; flex-wrap: wrap; }
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  height: 52px; padding: 0 24px; border-radius: 999px;
  font: 600 15px/1 var(--f-texto); text-decoration: none; white-space: nowrap;
  transition: background-color .45s var(--e-salida), color .45s var(--e-salida), border-color .45s var(--e-salida);
}
.btn svg { width: 16px; height: 16px; transition: transform .5s var(--e-salida); }
.btn:hover svg { transform: translateX(4px) rotate(-45deg); }
.btn--lleno { background: var(--tinta); color: var(--papel); border: 1px solid var(--tinta); }
.btn--lleno:hover { background: var(--acento); border-color: var(--acento); color: var(--sobre-acento); }
.btn--linea { border: 1px solid var(--tinta); color: var(--tinta); }
.btn--linea:hover { background: var(--tinta); color: var(--papel); }

.hero__meta {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px;
  height: 56px; border-top: 1px solid var(--linea);
  font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--tinta-suave);
  font-variant-numeric: tabular-nums; transition: color .7s var(--e-tema), border-color .7s var(--e-tema);
}
.pista { display: inline-flex; align-items: center; gap: 12px; }
.pista__raya { position: relative; width: 44px; height: 1px; background: var(--linea); overflow: hidden; }
.pista__raya::after { content: ""; position: absolute; inset: 0; background: var(--tinta); transform: translateX(-100%); animation: raya 2.4s var(--e-salida) infinite; }
.fuera .pista__raya::after { animation-play-state: paused; }
@keyframes raya { 0% { transform: translateX(-100%); } 60%, 100% { transform: translateX(100%); } }
.peso { justify-self: end; display: inline-flex; align-items: center; gap: 12px; }
.peso__num { color: var(--tinta); min-width: 3ch; text-align: right; transition: color .7s var(--e-tema); }
.peso__barra { position: relative; width: 72px; height: 3px; background: var(--linea); border-radius: 3px; overflow: hidden; }
.peso__barra i { position: absolute; inset: 0; background: var(--acento); transform-origin: left; transform: scaleX(0); border-radius: 3px; }

/* ——— Trabajo reciente ——— */
.trabajo { position: relative; padding: clamp(72px, 10vw, 140px) var(--gutter) 40px; border-top: 1px solid var(--linea); }
.trabajo__cab { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 24px 48px; margin-bottom: 48px; }
.trabajo__eti { margin: 0; font-size: 13px; letter-spacing: .02em; color: var(--tinta-suave); }
.trabajo__tit { margin: 0; font: 350 clamp(30px, 3.6vw, 56px)/1.04 var(--f-display); font-variation-settings: "opsz" 96; letter-spacing: -.02em; max-width: 18ch; }
.trabajo__tit em { font-style: italic; color: var(--acento); }
.lista { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--linea); }
.lista li {
  display: grid; grid-template-columns: 3rem minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, .7fr) 4rem; align-items: baseline; gap: 16px;
  padding: 22px 0; border-bottom: 1px solid var(--linea); color: var(--tinta-suave); font-size: 14px;
}
.lista .n { font-variant-numeric: tabular-nums; font-size: 12px; }
.lista h3 { margin: 0; font: 400 clamp(22px, 2.2vw, 34px)/1.1 var(--f-display); font-variation-settings: "opsz" 72; color: var(--tinta); letter-spacing: -.01em; transition: transform .6s var(--e-salida), font-variation-settings .6s var(--e-salida); }
@media (hover: hover) { .lista li:hover h3 { transform: translateX(10px); font-variation-settings: "opsz" 72, "wght" 640; } }
.lista .a { text-align: right; font-variant-numeric: tabular-nums; }
.pie { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px 24px; margin-top: 64px; font-size: 13px; color: var(--tinta-suave); }
.pie a { text-decoration-color: var(--linea); text-underline-offset: 4px; }
.pie a:hover { text-decoration-color: currentColor; }

/* ——— Adaptación ——— */
@media (max-width: 900px) {
  .hero__pie { grid-template-columns: 1fr; }
  .trabajo__cab { grid-template-columns: 1fr; }
  .lista li { grid-template-columns: 2.2rem minmax(0, 1fr) auto; row-gap: 4px; }
  .lista .s { grid-column: 2; grid-row: 2; }
  .lista .l { display: none; }
  .lista .a { grid-column: 3; grid-row: 1; }
}
@media (max-width: 640px) {
  .hero { height: 200svh; }
  .nav { height: 60px; }
  .nav__links { display: none; }
  .hero__pie { padding: 22px 0 18px; gap: 20px; }
  .ctas { display: grid; grid-template-columns: 1fr; }
  .btn { height: 50px; }
  .hero__meta { grid-template-columns: 1fr auto; height: 50px; }
  .pista { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero { height: auto; }
  .hero__pin { position: relative; }
  .pista__raya::after { animation: none; transform: none; }
  .js .titular__lineas { visibility: visible; }
  .js .hero__pie > *, .js .hero__meta, .js .eyebrow { opacity: 1; }
  *, *::before, *::after { transition-duration: .01ms !important; }
}
```

Puntos delicados del CSS:

- `.linea` usa `clip-path: inset(-.6em -.6em 0 -.6em)`: recorta **solo por abajo** (así las letras «salen» de debajo de la línea) y deja respirar arriba y a los lados (tildes, la virgulilla de la ñ, los remates de la cursiva). El `padding-bottom: .2em` con `margin-bottom: -.2em` deja ver los descendentes sin abrir el interlineado.
- `line-height: .8` en `.linea` es el mismo valor que la constante `LH = 0.8` del JS: si cambias uno, cambia el otro.
- `html, body { overflow-x: clip }` y `.hero__pin { overflow: clip }` evitan scroll horizontal sin romper el `sticky` (no uses `overflow: hidden` en ancestros del sticky).
- El grano es un `feTurbulence` en un SVG en data URI sobre `body::after`, con `mix-blend-mode: multiply` en claro y `screen` en oscuro.

### JavaScript completo (en un `<script>` normal tras los tres de GSAP)

```js
(() => {
  const html = document.documentElement;
  const reducido = matchMedia("(prefers-reduced-motion: reduce)").matches;
  const hayGsap = !!(window.gsap && window.ScrollTrigger && window.SplitText);

  /* ——— Tema claro / oscuro ——— */
  const btnTema = document.querySelector(".tema");
  const pintarTema = () => btnTema.setAttribute("aria-pressed", String(html.dataset.tema === "oscuro"));
  pintarTema();
  btnTema.addEventListener("click", () => {
    html.dataset.tema = html.dataset.tema === "oscuro" ? "claro" : "oscuro";
    try { localStorage.setItem("tp-tema", html.dataset.tema); } catch (e) {}
    pintarTema();
  });

  /* ——— Motor de peso ——— */
  const WMIN = 160, WMAX = 900, WQUIETO = 720, LH = 0.8;
  const MUESTRAS = [160, 250, 350, 450, 550, 650, 750, 850, 900];
  const hero = document.querySelector(".hero");
  const zona = document.querySelector(".hero__titular");
  const eyebrow = document.querySelector(".eyebrow");
  const cont = document.querySelector(".titular__lineas");
  const num = document.querySelector(".peso__num");
  const barra = document.querySelector(".peso__barra i");
  const FUENTES = [...cont.querySelectorAll("[data-linea]")].map((el) => ({
    texto: el.textContent.trim(), cursiva: el.hasAttribute("data-cursiva"),
  }));
  const estado = { w: reducido ? WQUIETO : WMIN };
  let lineas = [], split = null, vertical = null;

  const esc = (s) => s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
  const fvs = (w) => `"wght" ${w.toFixed(1)}, "opsz" 144`;

  function construir() {
    vertical = innerWidth / innerHeight < 0.9;
    if (split) split.revert();
    const piezas = vertical
      ? FUENTES.flatMap((f) => f.texto.split(/\s+/).map((t) => ({ texto: t, cursiva: f.cursiva })))
      : FUENTES;
    cont.innerHTML = piezas.map((p) =>
      `<span class="linea${p.cursiva ? " linea--cursiva" : ""}"><span class="linea__txt">${esc(p.texto)}</span></span>`).join("");
    const txts = [...cont.querySelectorAll(".linea__txt")];
    if (hayGsap) {
      split = SplitText.create(txts, { type: "chars", charsClass: "ch", tag: "span", aria: "none" });
      const ultimo = split.chars[split.chars.length - 1];
      if (ultimo && /[.!?…]/.test(ultimo.textContent)) ultimo.classList.add("ch--punto");
    }
    lineas = txts.map((el) => ({ el, linea: el.parentElement, n: el.textContent.length, tabla: [], fs: 0, objetivo: 0 }));
    medir();
  }

  function medir() {
    const ancho = cont.clientWidth;
    lineas.forEach((l) => {
      const s = l.el.style;
      s.fontSize = "100px"; s.letterSpacing = "0px"; s.marginRight = "0px";
      l.tabla = MUESTRAS.map((w) => { s.fontVariationSettings = fvs(w); return l.el.getBoundingClientRect().width; });
      l.fs = (ancho / l.tabla[l.tabla.length - 1]) * 100;
    });
    // La primera línea es el ancla: ninguna otra la supera.
    const ancla = lineas[0].fs;
    const cortas = lineas.map((l, i) => i > 0 && l.fs > ancla);
    lineas.forEach((l, i) => { if (cortas[i]) l.fs = ancla; });
    // Tope de alto: todo el titular cabe en su franja.
    const alto = zona.clientHeight - parseFloat(getComputedStyle(zona).paddingTop) - eyebrow.offsetHeight - 14 - 24;
    const total = lineas.reduce((a, l) => a + l.fs * LH, 0);
    const k = total > alto ? Math.max(0.2, alto / total) : 1;
    // Las líneas que no llegan al borde (y todas menos la primera si hubo tope) se alinean a la derecha.
    lineas.forEach((l, i) => l.linea.classList.toggle("linea--derecha", i > 0 && (cortas[i] || k < 0.985)));
    lineas.forEach((l) => {
      l.fs *= k;
      l.el.style.fontSize = l.fs.toFixed(2) + "px";
      l.linea.style.fontSize = l.fs.toFixed(2) + "px";
      l.objetivo = (l.tabla[l.tabla.length - 1] * l.fs) / 100;
    });
    aplicar();
  }

  function anchoNatural(l, w) {
    let i = 0;
    while (i < MUESTRAS.length - 2 && w > MUESTRAS[i + 1]) i++;
    const t = (w - MUESTRAS[i]) / (MUESTRAS[i + 1] - MUESTRAS[i]);
    return ((l.tabla[i] + (l.tabla[i + 1] - l.tabla[i]) * t) * l.fs) / 100;
  }

  function aplicar() {
    const w = estado.w;
    lineas.forEach((l) => {
      const ls = (l.objetivo - anchoNatural(l, w)) / Math.max(1, l.n - 1);
      l.el.style.fontVariationSettings = fvs(w);
      l.el.style.letterSpacing = ls.toFixed(2) + "px";
      l.el.style.marginRight = (-ls).toFixed(2) + "px";
    });
    num.textContent = Math.round(w);
    barra.style.transform = `scaleX(${((w - WMIN) / (WMAX - WMIN)).toFixed(4)})`;
  }

  /* ——— Arranque ——— */
  const fuentes = Promise.race([
    Promise.all([
      document.fonts.load('160 100px "Fraunces"'),
      document.fonts.load('italic 900 100px "Fraunces"'),
      document.fonts.load('500 16px "Instrument Sans"'),
    ]),
    new Promise((r) => setTimeout(r, 2500)),
  ]).catch(() => {});

  fuentes.then(() => {
    construir();
    if (!hayGsap) {
      html.classList.remove("js");
      return;
    }
    gsap.registerPlugin(ScrollTrigger, SplitText);

    if (reducido) {
      gsap.set(cont, { visibility: "visible" });
      return;
    }

    const chars = split.chars;
    const punto = cont.querySelector(".ch--punto");
    gsap.set(chars, { yPercent: 180, rotate: 7, transformOrigin: "0% 100%" });
    if (punto) gsap.set(punto, { yPercent: 0, rotate: 0, scale: 0, transformOrigin: "50% 85%" });
    gsap.set(cont, { visibility: "visible" });

    const porLinea = lineas.map((l) => [...l.el.querySelectorAll(".ch")].filter((c) => c !== punto));
    const tl = gsap.timeline({ defaults: { ease: "power4.out" }, delay: 0.15 });
    tl.from(".nav", { y: -16, opacity: 0, duration: 0.9 }, 0)
      .to(".eyebrow", { opacity: 1, duration: 0.8, ease: "power2.out" }, 0.2);
    porLinea.forEach((grupo, i) => {
      tl.to(grupo, { yPercent: 0, rotate: 0, duration: 1.2, stagger: 0.045 }, 0.1 + i * 0.22);
    });
    if (punto) tl.to(punto, { scale: 1, duration: 0.7, ease: "back.out(3)" }, 1.15);
    tl.fromTo(".hero__pie > *", { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: 1, stagger: 0.1, ease: "expo.out" }, 1.0)
      .to(".hero__meta", { opacity: 1, duration: 0.8, ease: "power2.out" }, 1.35);

    gsap.to(estado, {
      w: WMAX, ease: "none", onUpdate: aplicar,
      scrollTrigger: { trigger: hero, start: "top top", end: "bottom bottom", scrub: 0.9 },
    });

    new IntersectionObserver(([e]) => hero.classList.toggle("fuera", !e.isIntersecting)).observe(hero);
  });

  let t;
  addEventListener("resize", () => {
    clearTimeout(t);
    t = setTimeout(() => {
      if (!lineas.length) return;
      const v = innerWidth / innerHeight < 0.9;
      if (v !== vertical) {
        construir();
        if (split && !reducido) gsap.set(split.chars, { yPercent: 0, rotate: 0, scale: 1 });
      } else medir();
      if (hayGsap) ScrollTrigger.refresh();
    }, 150);
  });
})();
```

Cómo funciona el motor de peso (explicado para que no se «simplifique» mal):

1. `medir()` pone cada línea a 100 px y mide su anchura real con `getBoundingClientRect()` en 9 pesos (`MUESTRAS`). Esa tabla dice cuánto ocupa la línea a cada peso.
2. El tamaño de cada línea es `anchoDisponible / anchoA900 × 100`: al peso 900 y `letter-spacing: 0` la línea llena justo el ancho.
3. `anchoNatural(l, w)` interpola linealmente la tabla para cualquier peso intermedio.
4. `aplicar()` calcula `ls = (objetivo − anchoNatural) / (nLetras − 1)` y lo aplica como `letter-spacing`. Se divide entre `n − 1` porque el espacio tras la última letra no se ve; ese sobrante se anula con `margin-right: −ls` (importante en las líneas alineadas a la derecha).
5. SplitText parte en letras (`type: "chars"`, `tag: "span"`, `aria: "none"`); el `letter-spacing` del contenedor lo heredan las letras, así que no hay que tocar cada una.
6. El scroll no anima CSS directamente: GSAP anima el objeto `estado.w` de 160 a 900 con `scrub: 0.9` y, en cada `onUpdate`, `aplicar()` escribe `font-variation-settings` y `letter-spacing` en las líneas.
7. En `resize` (con 150 ms de espera) se vuelve a medir; si cambia la orientación (vertical ↔ horizontal) se reconstruyen las líneas, se vuelven a partir y las letras se colocan ya en su sitio (sin repetir la entrada).

## Interacción

- **Scroll:** es la interacción principal. Funciona igual con rueda, trackpad, teclado (espacio, AvPág, flechas) y gesto táctil, porque solo depende de la posición de scroll.
- **Tema:** botón `.tema` con `aria-pressed` (false = claro, true = oscuro) y `aria-label="Modo oscuro"`. Al pulsarlo alterna `data-tema` en `<html>` y guarda la elección en `localStorage` bajo `tp-tema` (dentro de `try/catch`: si no hay almacenamiento, sigue funcionando).
- **CTA:** «Ver proyectos» lleva a `#trabajo` (scroll suave por CSS); «Hablemos de tu marca» abre `mailto:hola@tallermeridiano.com`.
- **Hover (solo punteros finos):** subrayado que crece en los enlaces de la navegación, flecha que gira −45° en el botón lleno, fondo de acento en el botón lleno, relleno de tinta en el botón de contorno, nombre de proyecto que se desplaza 10 px y engorda a 640.
- **Foco:** `:focus-visible` con contorno de 2 px en color de acento y separación de 4 px en todos los controles. Enlace «Saltar al contenido» visible al tabular.

## Adaptación a móvil y tablet

- **Composición del titular:** se decide por proporción, no por ancho: si `innerWidth / innerHeight < 0.9` cada palabra va en su línea (móvil y tablet en vertical: «Diseño / con / acento.», con «con» alineada a la derecha al mismo tamaño que «Diseño»). En horizontal se mantienen las dos líneas originales.
- **≤ 900 px:** el pie del hero pasa a una columna (subtítulo arriba, botones debajo); la cabecera de «Trabajo reciente» a una columna; las filas de proyecto muestran número, nombre y año en la primera fila, servicio debajo del nombre, y se oculta la localidad.
- **≤ 640 px:** hero de 200svh; nav de 60 px de alto y sin enlaces (quedan marca e interruptor de tema); botones a ancho completo, apilados, de 50 px; la franja de datos pierde la pista central y queda en dos columnas (coordenadas · peso).
- `.hero__pin` tiene `min-height: 560px` para pantallas muy bajas; el gutter es `clamp(14px, 1.6vw, 28px)`.
- Probado de 360 a 1920 px sin scroll horizontal: en 390×844 el titular ocupa unos 280 px de alto en tres líneas (≈ 120 / 120 / 113 px de cuerpo); en 1920×900 queda en diagonal.

## Accesibilidad y movimiento reducido

- HTML semántico: `main`, `section` con `aria-labelledby`, `header`, `nav aria-label="Principal"`, `h1` único, `h2`, `h3` por proyecto, `ul` para la lista, `footer`.
- El h1 se lee completo gracias a `span.sr`; las letras partidas están dentro de un contenedor `aria-hidden="true"`. SVG decorativos con `aria-hidden="true"`; la pista y la lectura de peso también son decorativas.
- Contraste AA: tinta suave #5b5448 sobre #efe9dd ≈ 6,3:1; el botón lleno en hover (#efe9dd sobre #b33810) ≈ 5:1; en oscuro, el acento #ff6b3d sobre #121110 ≈ 6,6:1.
- `prefers-reduced-motion: reduce`: no hay entrada ni scroll ligado; el hero mide `auto` (el pin deja de ser sticky), el titular se muestra al instante con peso fijo **720** y su espaciado calculado (ocupa el mismo ancho), la raya de la pista no se anima y las transiciones duran 0,01 ms.
- Si GSAP no carga, se quita la clase `js`, el titular se ajusta igualmente con JS (sin partir en letras) y todo queda visible con peso 160.

## Rendimiento

- Sin WebGL ni bucles propios: solo se trabaja cuando hay scroll (ScrollTrigger con `scrub`) y durante la entrada. Cada fotograma escribe dos propiedades en 2–3 elementos.
- La tabla de anchuras se mide una vez por carga y por `resize` (9 lecturas por línea), nunca durante el scroll.
- La animación CSS de la pista se pausa (`animation-play-state: paused`) cuando el hero sale de pantalla, vía IntersectionObserver (clase `.fuera`).
- `will-change` solo en `.linea__txt` (letter-spacing) y en las letras (transform).
- HTML final de unos 23 KB.

## Personalización

- **Marca y textos:** Taller Meridiano, Estudio de marca y web, Valdera, 2014, 00°00′ N 00°00′ E, Calle Álamos 12, hola@tallermeridiano.com, +00 000 000 000, los botones (Ver proyectos, Hablemos de tu marca) y el subtítulo en tres partes (Identidades, webs y packaging para + **negocios con alma que quieren sonar a ellos mismos** + Diseñamos desde Valdera para quien tiene algo que contar y no quiere parecerse a nadie.).
- **Titular:** Diseño y con acento.. Funciona con cualquier texto: el JS calcula tamaños y espaciados. Mejor 1–2 palabras por línea; la primera línea es la que manda el tamaño. Si la última letra es `.`, `!`, `?` o `…` se pinta en acento y entra con rebote.
- **Colores:** los seis tokens de color (claro y oscuro). Mantén `--sobre-acento` igual al papel del tema.
- **Rango de peso:** constantes `WMIN = 160`, `WMAX = 900` y `WQUIETO = 720` (peso con movimiento reducido). Con otra fuente variable, ajusta `MUESTRAS` a su rango de `wght`.
- **Recorrido:** altura de `.hero` (240svh; 200svh en móvil). Más alto = el peso crece más despacio.
- **Otra fuente:** cualquier variable con eje `wght` amplio sirve (el motor mide, no supone), pero Fraunces con `opsz 144` es la que da el contraste fino/negro de la demo.

## Comprobación final

1. Al cargar, las letras suben desde debajo de su línea, sin verse antes de tiempo, y el punto final aparece con un rebote de acento.
2. En 1440×900 el titular cabe entero entre la nav y el subtítulo: la línea 1 arranca en el gutter izquierdo (23 px) y la línea 2 termina en el derecho (alineada a la derecha, con el tope de alto activo); el contador marca 160.
3. Al hacer scroll, el titular pasa de finísimo y espaciado a negro y apretado **sin cambiar de anchura**; el contador llega a 900 y la barra se llena.
4. El hero se queda fijo (sticky) mientras dura el recorrido de 240svh y luego aparece «Trabajo reciente».
5. En 390×844 el titular se ve en tres líneas «Diseño / con / acento.», con «con» a la derecha; botones apilados a ancho completo.
6. En 1920×900 el titular queda en diagonal (línea 1 a la izquierda, línea 2 a la derecha) y cabe entero en alto.
7. El interruptor alterna claro/oscuro con transición de 0,7 s, `aria-pressed` cambia y la elección se recuerda.
8. Con `prefers-reduced-motion: reduce` no hay animaciones; el titular aparece a peso 720 y el hero no es sticky.
9. Tildes, ñ y el punto de acento se ven completos (no los recorta la máscara).
10. Cero errores en consola, sin scroll horizontal de 360 a 1920 px.
11. Foco visible en todos los enlaces y botones; «Saltar al contenido» aparece al tabular.
12. Al redimensionar o girar el móvil, el titular se recompone sin repetir la entrada.

Prueba en frío

Lo que salió al pegarlo en frío

Pegamos el prompt en Claude Sonnet 5.5, sin ayuda ni contexto, y comparamos el resultado con la demo. Prueba hecha el 30-09-2026.

Lo que generó la IA con el prompt Completo de Hero con titular gigante
Prompt CompletoFiel a la demo9,8/10

Calco prácticamente exacto: las capturas de escritorio y móvil coinciden píxel a píxel, y la entrada letra a letra, el paso de peso 160 a 900 sin cambiar de anchura, el tema y la adaptación son idénticos. Solo cambia la meta description.

Lo que generó la IA con el prompt Lite de Hero con titular gigante
Prompt LiteAceptable6,8/10

El efecto principal está bien (entrada con máscara, peso 160→900 a anchura constante, contador y barra, escalera en móvil), pero en escritorio el eyebrow se monta sobre el filete de la nav y la segunda línea no queda alineada a la derecha. También cambian detalles de la sección de trabajo: el hover pinta el nombre en acento en vez de engordarlo, el h2 sale más grande y el grano y el logo son distintos.

Solo publicamos lo que hemos probado. Otras herramientas (ChatGPT, Gemini, Lovable…) pueden dar resultados distintos.

Paso a paso

Cómo usarlo en 4 pasos

  1. 01

    Personaliza

    Rellena el formulario de arriba con el nombre, los colores y los textos de tu cliente. Se meten en el prompt al copiar.
  2. 02

    Copia el Completo o el Lite

    El Completo, para Claude, ChatGPT, Gemini o Cursor. El Lite, más corto, para Lovable, Bolt o v0, que aceptan menos texto.
  3. 03

    Pega e itera

    Pégalo en una conversación nueva. Si quieres cambiar algo, pídelo con una frase corta:
    • «Que el titular pase de negro a fino en vez de fino a negro»
    • «Pon el titular en tres líneas también en escritorio»
    • «Cambia el acento a verde oliva y el papel a blanco roto»
    • «Haz que el peso crezca más despacio alargando el recorrido»
  4. 04

    Publica

    Guarda el resultado como HTML (o en tu proyecto) y súbelo a Vercel, Netlify o tu hosting de siempre. Tienes los pasos en la guía.

Guías por herramientaClaudeChatGPTGeminiCursorLovableBoltv0

Licencia en claro

Qué puedes hacer con esta pieza

Todas las piezas incluyen licencia de uso comercial. Usa el resultado tal cual en tus productos, encargos freelance o webs de clientes, sin atribución.

Puedes

  • Usarlo en tus webs y en las de tus clientes, también si cobras por ellas.
  • Cambiarlo todo lo que quieras.
  • Publicar el resultado sin citarnos.

No puedes

  • Revender o regalar el prompt, la demo o el código como plantilla o biblioteca.
  • Compartir tu cuenta Pro.

Es la misma licencia para lo gratis y lo Pro. La tienes entera en licencia de uso.

Preguntas frecuentes

Lo que suele preguntarse

¿Puedo usar un titular más largo?
Sí. El JS mide el texto real y calcula tamaño y espaciado por línea. Funciona mejor con una o dos palabras por línea; con frases largas el cuerpo baja y se pierde el efecto de titular gigante.
¿Sirve con otra fuente?
Con cualquier fuente variable que tenga un eje de peso amplio (por ejemplo de 100 a 900). Ajusta las constantes WMIN, WMAX y la lista MUESTRAS a su rango. Con fuentes estáticas no hay transición de peso.
¿Qué ve alguien con movimiento reducido activado?
El titular aparece al instante, con peso fijo 720 y el mismo ancho; no hay entrada letra a letra, el hero no se queda fijo y el resto de transiciones son instantáneas.
¿Puedo quitar la sección de proyectos de abajo?
Sí, es contexto. Si la quitas, deja algo de contenido después del hero para que el recorrido de scroll de 240svh tenga sentido.
¿Qué diferencia hay entre el prompt Completo y el Lite?
El Completo (≈11290 tokens) describe la pieza con todo detalle y es el que mejor resultado da en Claude, ChatGPT, Gemini o Cursor. El Lite (≈2762 tokens) resume lo esencial para herramientas que aceptan menos texto, como Lovable, Bolt o v0.

Versión 1.0 · publicada el 30-09-2026

Cópialo desde aquí

Tu navegador no nos deja escribir en el portapapeles. El texto ya está seleccionado: pulsa Ctrl + C (o ⌘ + C) o el botón.