refactor(recorrido): fuera la cabecera; el escenario abre la sección

Con el recorrido en segundo lugar, esa franja clara quedaba como una
lasca entre el azul del hero y el escenario oscuro. Se va: la sección
arranca directamente en la secuencia anclada, y su fondo pasa a azul para
que cualquier costura sea oscura.

Quitarla dejaba la sección sin ningún encabezado, que en una pieza de
5.400px es un agujero en el esquema del documento — para un lector de
pantalla las seis parejas colgarían de nada. El rótulo «¿Te reconoces?»,
que ya estaba en el escenario, pasa a ser el <h2>. Hay una prueba que
exige que exista y que sea h2.

Actualizado también el comentario de orden en page.tsx, que describía una
disposición que ya no es la del archivo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
erikghandClaude Opus 5 committed 2026-08-03 16:20:23 -06:00
1 parent d4bff33e47
commit e0ce0d3b22
3 files changed
+23 -46

No files matched your search

+5 -13
View File
@@ -24,23 +24,15 @@ export default function Home() {
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<Header />
{/* Order matters more since PuntosInflexion became the pinned sequence.
It no longer only names the six inflection points — it answers each
one in creeer's voice, six times over, across six screens. Put third,
as it was, the reader got the whole solution before Dolores had given
them a reason to care and before Mecanismo explained the method, and
the pain landed after the answer.
Dolores now comes first: short, in the client's own words, the ache.
Then the recorrido answers it point by point. Then Mecanismo, which
stops reading as repetition and starts reading as the method behind
the answers the reader just heard — and it keeps creeer's navy ground
straight out of the sequence's last frame. */}
{/* El recorrido va en segundo lugar por decisión de la dueña: el lector
entra por el gancho y cae directo en la secuencia anclada, que plantea
seis situaciones y las contesta. FraseAncla y Dolores llegan después,
y Mecanismo cierra explicando el método detrás de esas respuestas. */}
<main>
<Hero variant="editorial" /> {/* dueña puede cambiar a "hormozi" */}
<PuntosInflexion />
<FraseAncla />
<Dolores />
<PuntosInflexion />
<Mecanismo />
<Disciplinas />
<ParaQuien />
+11 -31
View File
@@ -7,7 +7,6 @@ import { FondoPuntos } from '@/components/primitives/FondoPuntos';
import { FondoFoto } from '@/components/primitives/FondoFoto';
import { ParDialogo } from '@/components/primitives/ParDialogo';
import { EscenaDialogo } from '@/components/primitives/EscenaDialogo';
import { IlustracionInflexion } from '@/components/primitives/IlustracionInflexion';
import { puntosInflexion } from '@/lib/content';
import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, motionProps } from '@/lib/motion';
@@ -32,23 +31,6 @@ const FOTOS = [
'/images/recorrido/portafolio.jpg', // a skyline of towers, every one of them a front
];
function Cabecera({ reduced }: { reduced: boolean }) {
return (
<div className="flex flex-col md:flex-row md:items-center md:justify-between md:gap-12">
<div>
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="text-xs tracking-[0.3em] uppercase font-black text-dorado">{puntosInflexion.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 font-sans font-black text-seccion text-azul-profundo max-w-3xl">{puntosInflexion.title}</motion.h2>
</div>
<motion.div variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-8 md:mt-0 shrink-0">
<IlustracionInflexion className="w-56 md:w-72 h-auto mx-auto" />
</motion.div>
</div>
);
}
// Reduced motion, and the server's first paint, get the whole conversation laid
// out at once. A pinned section that advances on scroll is exactly the thing
// someone asking for less movement is asking to be spared, and hiding five
@@ -58,8 +40,7 @@ function Estatica() {
<section className="bg-gris-claro relative overflow-hidden">
<FondoPuntos className="-bottom-6 -right-12 w-80 md:w-[32rem] text-azul-profundo/10" distance={32} />
<Container className="relative py-16 md:py-24">
<Cabecera reduced />
<p className="mt-14 text-xs tracking-[0.3em] uppercase font-black text-dorado">{puntosInflexion.lead}</p>
<h2 className="text-xs tracking-[0.3em] uppercase font-black text-dorado">{puntosInflexion.lead}</h2>
<ul className="mt-8 list-none p-0 flex flex-col gap-16 md:gap-24">
{items.map((item, i) => (
<ParDialogo key={item.name} n={i + 1} {...item} activo reducido onActivar={() => {}} />
@@ -142,15 +123,10 @@ function Escenificado() {
const fotoY = useTransform(progreso, [0, 1], [0, -70]);
return (
<section className="bg-gris-claro relative">
{/* The pinned stage starts the instant this container ends, so without a
bottom the dark ground began three pixels under the last line of the
title and the intro read as a squeezed strip between two dark blocks
rather than as the section's opening. */}
<Container className="relative pt-16 pb-16 md:pt-24 md:pb-24">
<Cabecera reduced={false} />
</Container>
// Navy, not gris-claro: with no header block left there is no light strip
// to show, and any seam at the section's edges should be dark like the
// stage itself.
<section className="bg-azul-profundo relative">
{/* One viewport of stage, six viewports of scroll to move through it. */}
<div ref={contenedor} className="relative" style={{ height: `${TOTAL * 100}vh` }}>
<div data-escena={activo} data-tiempo={tiempo} className="sticky top-0 h-screen overflow-hidden bg-azul-profundo">
@@ -205,9 +181,13 @@ function Escenificado() {
resolves against Container's padding box, so `left-0` lands on
the inside of the border and ignores the px-6 the rest of the
page is aligned to. On a phone that clipped the first glyph. */}
<p className="absolute top-24 left-6 md:left-12 text-xs tracking-[0.3em] uppercase font-black text-dorado md:top-28">
{/* The section's only heading now. Losing the header block cost
the recorrido its <h2>, and a section this size with no heading
at all is a hole in the document outline — for a screen reader
the six exchanges would hang off nothing. */}
<h2 className="absolute top-24 left-6 md:left-12 text-xs tracking-[0.3em] uppercase font-black text-dorado md:top-28">
{puntosInflexion.lead}
</p>
</h2>
{/* The rail. Where you are in the conversation, and how much is
left — a pinned section without it feels like a page that
@@ -4,9 +4,14 @@ import { PuntosInflexion } from '../PuntosInflexion';
import { puntosInflexion } from '@/lib/content';
describe('<PuntosInflexion />', () => {
it('renders the section title', () => {
it('still has a heading after losing its header block', () => {
render(<PuntosInflexion />);
expect(screen.getByText(/llegan a un punto de inflexión/i)).toBeInTheDocument();
// The eyebrow and the deck's title used to sit in a light strip above the
// pinned stage; with them gone the lead carries the heading level. A
// section this long with no heading at all is a hole in the outline —
// for a screen reader the six exchanges would hang off nothing.
const encabezado = screen.getByRole('heading', { name: /te reconoces/i });
expect(encabezado.tagName).toBe('H2');
});
it('labels both halves of every exchange with its inflection point', () => {