"use client"; /** * ServicesJourney — pinned horizontal-scroll track of services. * * On desktop the section pins and the panels scroll sideways as you scroll * down (GSAP ScrollTrigger pin + scrub), with a progress bar. On mobile / when * the pin would be awkward, and under reduced-motion, it degrades to a normal * vertical stack (no pin, panels just stack and reveal). Each panel is a real *
so the content is accessible regardless of layout. */ import { useEffect, useRef } from "react"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); const services = [ { n: "01", t: "Web & Desarrollo", d: "Sitios construidos en código, veloces y 100% editables. Sin plantillas, sin atajos: arquitectura que escala contigo.", tags: ["Next.js", "Headless", "Core Web Vitals"], }, { n: "02", t: "SEO & GEO", d: "Posicionamiento técnico y de contenido — para Google y para los motores de IA. Reporting real, no humo.", tags: ["Técnico", "Contenido", "Answer Engines"], }, { n: "03", t: "Paid Ads", d: "Google, Meta, TikTok y LinkedIn. Creatividad asistida por IA y pujas optimizadas hacia el resultado, no el clic.", tags: ["Performance", "Creatividad IA", "Atribución"], }, { n: "04", t: "Contenido", d: "Piezas pensadas para rankear y para convertir. Producción a escala sin perder la voz de tu marca.", tags: ["Editorial", "Vídeo", "Social"], }, { n: "05", t: "Diseño & Marca", d: "Identidad y dirección de arte con criterio. Sistemas de diseño que viven en producto, no en un PDF.", tags: ["Identidad", "Design System", "Motion"], }, { n: "06", t: "Automatización", d: "Flujos que devuelven horas: captación de leads, reporting automático y operativa conectada de punta a punta.", tags: ["Leads", "Reporting", "Ops"], }, ]; export default function ServicesJourney() { const section = useRef(null); const track = useRef(null); const bar = useRef(null); useEffect(() => { const sec = section.current; const trk = track.current; if (!sec || !trk) return; const mm = gsap.matchMedia(); // Desktop with motion allowed: pin + horizontal scroll. mm.add( "(min-width: 900px) and (prefers-reduced-motion: no-preference)", () => { const distance = () => trk.scrollWidth - window.innerWidth; const tween = gsap.to(trk, { x: () => -distance(), ease: "none", scrollTrigger: { trigger: sec, start: "top top", end: () => "+=" + distance(), scrub: 0.6, pin: true, invalidateOnRefresh: true, onUpdate: (self) => { if (bar.current) { bar.current.style.transform = `scaleX(${self.progress})`; } }, }, }); // Per-panel inner reveal as it slides into center. gsap.utils.toArray(".sjourney__panel").forEach((panel) => { gsap.from(panel.querySelectorAll(".sjourney__reveal"), { y: 40, opacity: 0, duration: 0.6, stagger: 0.06, ease: "power3.out", scrollTrigger: { trigger: panel, containerAnimation: tween, start: "left 80%", }, }); }); return () => { if (bar.current) bar.current.style.transform = "scaleX(0)"; }; } ); // Mobile / reduced-motion: simple stacked reveals. mm.add("(max-width: 899px)", () => { gsap.utils.toArray(".sjourney__panel").forEach((panel) => { gsap.from(panel, { y: 36, opacity: 0, duration: 0.7, ease: "power3.out", scrollTrigger: { trigger: panel, start: "top 88%" }, }); }); }); return () => mm.revert(); }, []); return (

02 — Qué hacemos

Todo tu crecimiento, un mismo sistema.

{services.map((s) => (
{s.n}

{s.t}

{s.d}

    {s.tags.map((tag) => (
  • {tag}
  • ))}
))}
); }