'use client'; import { useState } from 'react'; import { motion, useMotionValueEvent, useScroll, useSpring, useTransform } from 'framer-motion'; import { usePrefersReducedMotion } from '@/lib/motion'; import { BARRAS_SIMBOLO, VIEWBOX_SIMBOLO } from '@/lib/simbolo'; // The gold spine of the page: one continuous line down the vertical centre, // drawing itself as the reader scrolls and resolving into the ≡ at the close. // It is what turns ten sections into a single route — *claridad · dirección · // crecimiento* made visible, per the Manual de Identidad Visual. // // Centred, never in a side gutter: the brand's through-line belongs in the // middle, on every breakpoint. It sits ABOVE the section backgrounds (z-10) // but BELOW the text, which `Container` lifts to z-20 — so the thread reads // as a faint watermark spine behind the reading, never crossing it. // // Page-level useScroll() with no target ref on purpose: no element measuring, // no "container has a static position" warnings, one listener for the page. const TRAMOS = 4; const ALTO = 1200; const PASO = ALTO / TRAMOS; // A band, not a thread. Held in real pixels by vector-effect below, so it never // again collapses to a hairline when the viewBox scale changes. const GROSOR = 14; // Four long swells rather than one page-long curve or ten short ones. Stretched // over the whole document with preserveAspectRatio="none", a single swell reads // as a straight rule and ten read as a fidget; four put roughly one screen's // worth of travel — some 180px across the column — into every viewport. const TRAZO = Array.from({ length: TRAMOS }, (_, i) => { const y = i * PASO; const x = i % 2 === 0 ? 72 : 28; // The final bend straightens as it lands so the thread enters the ≡ vertically. const salida = i === TRAMOS - 1 ? 50 : x; return `C ${x} ${y + PASO * 0.25}, ${salida} ${y + PASO * 0.75}, 50 ${y + PASO}`; }).reduce((d, tramo) => `${d} ${tramo}`, 'M50 0'); // The draw finishes halfway down the document. Tied to raw scrollYProgress the // leading tip sat in mid-viewport for most of the page, reading as an amputated // stub — the "unfinished" complaint. Finishing early lets the tip outrun the // reader within the hero and leaves a complete spine for everything after. const FIN = 0.5; export function HiloConductor({ className = '' }: { className?: string }) { const reduced = usePrefersReducedMotion(); const { scrollYProgress } = useScroll(); const recorrido = useTransform(scrollYProgress, [0, FIN], [0, 1], { clamp: true }); const avance = useSpring(recorrido, { stiffness: 90, damping: 30, restDelta: 0.001 }); const [dibujado, setDibujado] = useState(false); useMotionValueEvent(avance, 'change', (v) => { if (v > 0.98) setDibujado(true); }); const resuelto = reduced || dibujado; const wrapper = `pointer-events-none absolute inset-0 z-10 ${className}`; // Plain for reduced motion: motion.path applies dash scaffolding even // without initial/animate and would leave the thread invisible. // vector-effect keeps the stroke at its declared width — the non-uniform // viewBox scale squeezes a vertical line's stroke by the x factor alone. const trazo = reduced ? ( ) : ( ); return (
{/* Mirrors Container's own centring (`mx-auto max-w-content`) so the thread is measured from the real column edge and stays centred in it. */}
{/* Explicit height, never a top+bottom pair: an carrying a viewBox is a replaced element with an intrinsic 100:1200 ratio, so `height: auto` resolves from the width (1152 → 13824px) and silently wins over `bottom`, adding ~6000px of blank scroll below the footer. */} {/* The ending. The line arrives and becomes the mark — same geometry as the wordmark's ≡, never a lookalike. Uniformly scaled in its own svg, because the band's stretch would flatten the bars. */}
); }