diff --git a/components/primitives/IlustracionConvergencia.tsx b/components/primitives/IlustracionConvergencia.tsx new file mode 100644 index 0000000..5ea7b64 --- /dev/null +++ b/components/primitives/IlustracionConvergencia.tsx @@ -0,0 +1,45 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { useEnVista } from '@/lib/motion'; + +// Three strands — comunicación, digital, experiencias — meeting and leaving as +// a single gold line. The section's own lead, drawn: "No son la promesa, son +// los medios." The three are grey; only what they produce is gold. +const HEBRAS = [ + 'M6 18 C 60 20, 90 52, 120 60', + 'M6 60 C 60 60, 90 60, 120 60', + 'M6 102 C 60 100, 90 68, 120 60', +]; +const ESTRATEGIA = 'M120 60 L 214 60'; + +export function IlustracionConvergencia({ className = '' }: { className?: string }) { + const { ref, enVista, reduced } = useEnVista(); + + // Plain under reduced motion — motion.path would hide the drawing. + if (reduced) { + return ( + + ); + } + + const dibuja = (delay: number) => ({ + initial: { pathLength: 0 }, + animate: enVista ? { pathLength: 1 } : { pathLength: 0 }, + transition: { duration: 1, delay, ease: [0.22, 1, 0.36, 1] as const }, + }); + + return ( + + ); +} diff --git a/components/primitives/__tests__/IlustracionConvergencia.test.tsx b/components/primitives/__tests__/IlustracionConvergencia.test.tsx new file mode 100644 index 0000000..e203c2c --- /dev/null +++ b/components/primitives/__tests__/IlustracionConvergencia.test.tsx @@ -0,0 +1,17 @@ +import { render } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import { IlustracionConvergencia } from '../IlustracionConvergencia'; + +describe('', () => { + it('is decorative: hidden from assistive tech', () => { + const { container } = render(); + const svg = container.querySelector('svg'); + expect(svg).toHaveAttribute('aria-hidden', 'true'); + expect(svg).toHaveAttribute('focusable', 'false'); + }); + + it('draws one strand per discipline plus the strategy they serve', () => { + const { container } = render(); + expect(container.querySelectorAll('path')).toHaveLength(4); + }); +}); diff --git a/components/sections/Disciplinas.tsx b/components/sections/Disciplinas.tsx index 5702044..f2b573e 100644 --- a/components/sections/Disciplinas.tsx +++ b/components/sections/Disciplinas.tsx @@ -3,6 +3,7 @@ import { motion } from 'framer-motion'; import { Container } from '@/components/primitives/Container'; import { TarjetaSpotlight } from '@/components/primitives/TarjetaSpotlight'; import { Icono, type IconoNombre } from '@/components/primitives/Icono'; +import { IlustracionConvergencia } from '@/components/primitives/IlustracionConvergencia'; import { disciplinas } from '@/lib/content'; import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, motionProps } from '@/lib/motion'; @@ -17,8 +18,11 @@ export function Disciplinas() { className="text-xs tracking-[0.3em] uppercase font-black text-dorado">{disciplinas.eyebrow} {disciplinas.title} - {disciplinas.lead} + +

{disciplinas.lead}

+ +
{disciplinas.items.map((item, i) => (