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:
1 parent
d4bff33e47
commit
e0ce0d3b22
3 files changed
+23
-46
No files matched your search
+5
-13
@@ -24,23 +24,15 @@ export default function Home() {
|
|||||||
<>
|
<>
|
||||||
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
||||||
<Header />
|
<Header />
|
||||||
{/* Order matters more since PuntosInflexion became the pinned sequence.
|
{/* El recorrido va en segundo lugar por decisión de la dueña: el lector
|
||||||
It no longer only names the six inflection points — it answers each
|
entra por el gancho y cae directo en la secuencia anclada, que plantea
|
||||||
one in creeer's voice, six times over, across six screens. Put third,
|
seis situaciones y las contesta. FraseAncla y Dolores llegan después,
|
||||||
as it was, the reader got the whole solution before Dolores had given
|
y Mecanismo cierra explicando el método detrás de esas respuestas. */}
|
||||||
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. */}
|
|
||||||
<main>
|
<main>
|
||||||
<Hero variant="editorial" /> {/* dueña puede cambiar a "hormozi" */}
|
<Hero variant="editorial" /> {/* dueña puede cambiar a "hormozi" */}
|
||||||
|
<PuntosInflexion />
|
||||||
<FraseAncla />
|
<FraseAncla />
|
||||||
<Dolores />
|
<Dolores />
|
||||||
<PuntosInflexion />
|
|
||||||
<Mecanismo />
|
<Mecanismo />
|
||||||
<Disciplinas />
|
<Disciplinas />
|
||||||
<ParaQuien />
|
<ParaQuien />
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { FondoPuntos } from '@/components/primitives/FondoPuntos';
|
|||||||
import { FondoFoto } from '@/components/primitives/FondoFoto';
|
import { FondoFoto } from '@/components/primitives/FondoFoto';
|
||||||
import { ParDialogo } from '@/components/primitives/ParDialogo';
|
import { ParDialogo } from '@/components/primitives/ParDialogo';
|
||||||
import { EscenaDialogo } from '@/components/primitives/EscenaDialogo';
|
import { EscenaDialogo } from '@/components/primitives/EscenaDialogo';
|
||||||
import { IlustracionInflexion } from '@/components/primitives/IlustracionInflexion';
|
|
||||||
import { puntosInflexion } from '@/lib/content';
|
import { puntosInflexion } from '@/lib/content';
|
||||||
import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, motionProps } from '@/lib/motion';
|
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
|
'/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
|
// 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
|
// 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
|
// 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">
|
<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} />
|
<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">
|
<Container className="relative py-16 md:py-24">
|
||||||
<Cabecera reduced />
|
<h2 className="text-xs tracking-[0.3em] uppercase font-black text-dorado">{puntosInflexion.lead}</h2>
|
||||||
<p className="mt-14 text-xs tracking-[0.3em] uppercase font-black text-dorado">{puntosInflexion.lead}</p>
|
|
||||||
<ul className="mt-8 list-none p-0 flex flex-col gap-16 md:gap-24">
|
<ul className="mt-8 list-none p-0 flex flex-col gap-16 md:gap-24">
|
||||||
{items.map((item, i) => (
|
{items.map((item, i) => (
|
||||||
<ParDialogo key={item.name} n={i + 1} {...item} activo reducido onActivar={() => {}} />
|
<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]);
|
const fotoY = useTransform(progreso, [0, 1], [0, -70]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-gris-claro relative">
|
// Navy, not gris-claro: with no header block left there is no light strip
|
||||||
{/* The pinned stage starts the instant this container ends, so without a
|
// to show, and any seam at the section's edges should be dark like the
|
||||||
bottom the dark ground began three pixels under the last line of the
|
// stage itself.
|
||||||
title and the intro read as a squeezed strip between two dark blocks
|
<section className="bg-azul-profundo relative">
|
||||||
rather than as the section's opening. */}
|
|
||||||
<Container className="relative pt-16 pb-16 md:pt-24 md:pb-24">
|
|
||||||
<Cabecera reduced={false} />
|
|
||||||
</Container>
|
|
||||||
|
|
||||||
{/* One viewport of stage, six viewports of scroll to move through it. */}
|
{/* One viewport of stage, six viewports of scroll to move through it. */}
|
||||||
<div ref={contenedor} className="relative" style={{ height: `${TOTAL * 100}vh` }}>
|
<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">
|
<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
|
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
|
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. */}
|
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}
|
{puntosInflexion.lead}
|
||||||
</p>
|
</h2>
|
||||||
|
|
||||||
{/* The rail. Where you are in the conversation, and how much is
|
{/* The rail. Where you are in the conversation, and how much is
|
||||||
left — a pinned section without it feels like a page that
|
left — a pinned section without it feels like a page that
|
||||||
|
|||||||
@@ -4,9 +4,14 @@ import { PuntosInflexion } from '../PuntosInflexion';
|
|||||||
import { puntosInflexion } from '@/lib/content';
|
import { puntosInflexion } from '@/lib/content';
|
||||||
|
|
||||||
describe('<PuntosInflexion />', () => {
|
describe('<PuntosInflexion />', () => {
|
||||||
it('renders the section title', () => {
|
it('still has a heading after losing its header block', () => {
|
||||||
render(<PuntosInflexion />);
|
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', () => {
|
it('labels both halves of every exchange with its inflection point', () => {
|
||||||
|
|||||||
Reference in new issue
Block a user