/* global React */ const { useState, useEffect, useRef } = React; /* ====================================================== Cursor — small, mix-blend ====================================================== */ function Cursor() { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; let x = window.innerWidth / 2, y = window.innerHeight / 2; let tx = x, ty = y; const onMove = (e) => { tx = e.clientX; ty = e.clientY; }; const onOver = (e) => { const t = e.target; if (t.closest && t.closest('a, button, .svc, .case, .contact-email')) el.classList.add('hover'); else el.classList.remove('hover'); }; const tick = () => { x += (tx - x) * 0.2; y += (ty - y) * 0.2; el.style.transform = `translate(${x}px, ${y}px) translate(-50%,-50%)`; requestAnimationFrame(tick); }; window.addEventListener('mousemove', onMove); window.addEventListener('mouseover', onOver); tick(); return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseover', onOver); }; }, []); return
; } /* ====================================================== Live clock — São Paulo ====================================================== */ function Clock() { const [t, setT] = useState(''); useEffect(() => { const fmt = () => { const d = new Date(); const opts = { hour: '2-digit', minute: '2-digit', timeZone: 'America/Sao_Paulo', hour12: false }; setT(new Intl.DateTimeFormat('pt-BR', opts).format(d)); }; fmt(); const id = setInterval(fmt, 1000 * 15); return () => clearInterval(id); }, []); return RS / {t}; } /* ====================================================== LANGUAGE SWITCHER — BR · EN · ES (live site translation) ====================================================== */ function LangSwitcher() { const [state, setState] = React.useState({ lang: 'pt', busy: false }); React.useEffect(() => { if (!window.__i18n) return; return window.__i18n.subscribe(setState); }, []); const LANGS = [['pt', 'BR'], ['en', 'EN'], ['es', 'ES']]; return (
{LANGS.map(([code, label]) => ( ))} {state.busy &&
); } /* ====================================================== Reveal on scroll ====================================================== */ function useReveal() { useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: 0.01, rootMargin: '0px 0px 0px 0px' }); els.forEach(el => io.observe(el)); // Safety net: force-reveal anything still hidden after 2.5s const t = setTimeout(() => { document.querySelectorAll('.reveal:not(.in)').forEach(el => el.classList.add('in')); }, 2500); return () => { io.disconnect(); clearTimeout(t); }; }, []); } /* ====================================================== NAV ====================================================== */ function Nav() { return ( ); } /* ====================================================== HERO ====================================================== */ function Hero() { return (
Made in Brazil · Independent Studio

Morse Craft Design

Conectamos estratégia, design, conteúdo e tecnologia para criar experiências relevantes, fortalecer narrativas e construir valor de longo prazo.
// Processo
Menos ruído. Mais intenção.
// Relações
Poucos clientes. Mais presença.
// Tecnologia
IA como ampliação criativa.
); } /* ====================================================== MORSE SIGNAL — live transmitter spelling "MORSE" ====================================================== */ function MorseSignal() { // M O R S E → letters as dot/dash sequences const WORD = [ ['dash', 'dash'], // M ['dash', 'dash', 'dash'], // O ['dot', 'dash', 'dot'], // R ['dot', 'dot', 'dot'], // S ['dot'], // E ]; // flatten with letter index for gap logic const symbols = []; WORD.forEach((letter, li) => letter.forEach((t) => symbols.push({ type: t, li }))); const [active, setActive] = React.useState(-1); React.useEffect(() => { const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduce) { setActive(-1); return; } const unit = 135; let idx = 0; let timer; const run = () => { const sym = symbols[idx]; setActive(idx); const onDur = sym.type === 'dash' ? unit * 3 : unit; timer = setTimeout(() => { setActive(-1); const next = symbols[idx + 1]; let gap = unit; // inter-symbol if (!next) gap = unit * 7; // word reset else if (next.li !== sym.li) gap = unit * 3; // inter-letter timer = setTimeout(() => { idx = (idx + 1) % symbols.length; run(); }, gap); }, onDur); }; run(); return () => clearTimeout(timer); }, []); let gi = -1; return ( ); } function Marquee() { const items = [ 'Strategy', 'Brand Design', 'Editorial & Information Design', 'Culture & Institutional Communication', 'Digital Experience', 'Content & Social Systems', 'AI Creative Studio', ]; const row = ( <> {items.map((it, i) => ( {it} ))} ); return (
{row}{row}{row}
); } window.Cursor = Cursor; window.Clock = Clock; window.Nav = Nav; window.Hero = Hero; window.useReveal = useReveal;