// PLAIN TEXT JS — iam/marketing v5 home // // Banner ensures finfo classifies as text/plain. // D4-prod · Home page // Operator Console direction · production hi-fi // ── Editable copy loader ───────────────────────────────────────── // Strings come from copy.js (window.HOME_COPY). If a key is missing, // the `??` fallback uses the literal default so the page never breaks. const __C = (typeof window !== 'undefined' && window.HOME_COPY) || {}; const HERO = __C.hero || {}; const PROBLEM = __C.problem || {}; const DIVIDER = __C.divider || {}; const GLOBAL = __C.global || {}; // ── useIsMobile · CSS-viewport detection via matchMedia (works in real browsers + preview emulation) ─ function useIsMobile(breakpoint = 900) { const query = `(max-width: ${breakpoint - 1}px)`; const [isMobile, setIsMobile] = React.useState(() => { if (typeof window === 'undefined' || !window.matchMedia) return false; return window.matchMedia(query).matches; }); React.useEffect(() => { if (!window.matchMedia) return; const mql = window.matchMedia(query); const onChange = (e) => setIsMobile(e.matches); if (mql.addEventListener) mql.addEventListener('change', onChange); else mql.addListener(onChange); setIsMobile(mql.matches); return () => { if (mql.removeEventListener) mql.removeEventListener('change', onChange); else mql.removeListener(onChange); }; }, [query]); return isMobile; } // ── ScrollRail · fixed right-edge progress tracker ──────────── // Per spec — 10 sections. Filtered at mount to only those actually present in DOM. const SCROLL_RAIL_SECTIONS = [ ['problem', '01 / PROBLEMA'], ['backend', '01.5 / BACKEND'], ['mechanism', '02 / MECANISME'], ['engine', '03 · MOTOR'], ['who', '04 / EQUIP'], ['receipts', '05 / REBUTS'], ['diff', '06 / DIFERÈNCIA'], ['process', '08 / PROCÉS'], ['command-center', '09 / CENTRE_DE_COMANDAMENT'], ['room-01', '10 / ROOM_01'], ]; const ScrollRail = () => { const [activeIdx, setActiveIdx] = React.useState(0); const [pct, setPct] = React.useState(0); const [items, setItems] = React.useState([]); const rafRef = React.useRef(null); // Resolve only sections that are actually mounted in the DOM React.useEffect(() => { if (typeof window === 'undefined') return; const present = SCROLL_RAIL_SECTIONS.filter(([id]) => document.getElementById(id)); setItems(present); }, []); React.useEffect(() => { if (typeof window === 'undefined' || !items.length) return; let ticking = false; const onScroll = () => { if (ticking) return; ticking = true; rafRef.current = requestAnimationFrame(() => { const max = document.documentElement.scrollHeight - window.innerHeight; const ratio = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0; setPct(Math.round(ratio * 100)); ticking = false; }); }; onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll, { passive: true }); const observed = items .map(([id]) => document.getElementById(id)) .filter(Boolean); const visibility = new Map(); const obs = new IntersectionObserver((entries) => { entries.forEach((entry) => { visibility.set(entry.target.id, entry.intersectionRatio); }); let bestId = null; let bestRatio = 0; visibility.forEach((ratio, id) => { if (ratio > bestRatio) { bestRatio = ratio; bestId = id; } }); if (bestId) { const idx = items.findIndex(([id]) => id === bestId); if (idx >= 0) setActiveIdx(idx); } }, { threshold: [0, 0.25, 0.5, 0.75, 1] }); observed.forEach((el) => obs.observe(el)); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); if (rafRef.current) cancelAnimationFrame(rafRef.current); obs.disconnect(); }; }, [items]); const handleJump = (id) => { const el = document.getElementById(id); if (!el) return; const top = el.getBoundingClientRect().top + window.scrollY - 60; window.scrollTo({ top, behavior: 'smooth' }); }; if (!items.length) return null; return ( <> ); }; const HomePage = ({ lang = 'CA' }) => { const mobile = useIsMobile(); return ( {/* Mobile-only press feedback for magnetic CTAs (no cursor → :active fallback) */} {!mobile && } ); }; // ── HERO ── VSL-first vertical-axis · centered focus ─────────── const HomeHero = ({ mobile }) => { const [playing, setPlaying] = React.useState(false); const [progress, setProgress] = React.useState(0); // 0..1 — drives the bottom rail const videoRef = React.useRef(null); const videoSrc = null; // Drop a real path like './vsl.mp4' to enable the player return (
{/* Top corner labels */} {!mobile && (
// FIELD_REPORT_001 · {new Date().toISOString().slice(0,10)}
)} {!mobile && (
)} {/* CENTERED VERTICAL STACK · clean axial composition */}
{/* THESIS LINE · added above eyebrow */}
// MÚLTIPLES FLUXOS D'INGRÉS — AIXÍ S'ESCALA
{/* Secondary eyebrow */}
// $40M EN INVERSIÓ PUBLICITÀRIA · 10 ANYS · LIDERAT PER OPERADORS
{/* Headline · centered cascade */}

L'equip de creixement 1-a-1 per a marques de consum de 8 xifres.

{/* Sub-headline */}
// No passem anuncis. Operem tota la màquina — pàgines, backend, automatitzacions, anuncis, agents d'IA — i deixem el sistema dins del teu negoci.
{/* VSL eyebrow */} ▶ MIRA EL PLAYBOOK · 12 MIN {/* VSL player · centered focal point */}
); }; // ── MARQUEE ──────────────────────────────────────────────────── const HomeMarquee = () => ( ); // ── PROBLEM ──────────────────────────────────────────────────── const HomeProblem = ({ mobile }) => (

Pagues per anuncis.
Aterren aquí.

La majoria de les agències de "creixement" només compren més clics. Després llencen trànsit cap a un checkout que trenca cada objecció del llibre — pop-ups, falta de confiança, preus confusos, botons lents, sense proves.
El CAC puja. L'LTV baixa. El teu client mai arriba al botó de comprar.
{[ ['→', 'CAC ↑ +38% YoY · perquè la pàgina té fuites'], ['→', 'LTV ↓ −22% · sense backend, compradors d\'una sola vegada'], ['→', 'ROAS 3.2× → 1.4× · mateixa inversió, menys ingressos'], ['→', 'L\'equip se\'n va · sense sistema per heretar'], ].map(([a, t], i) => (
{a} {t}
))}
{/* Beginner dropshipping mobile store — believable but flawed · order 2 on mobile */}
{/* Premium angled-floating product-shot phone treatment */} {/* Ambient chartreuse halo behind the phone */}