// Globals
const { useEffect, useState, useRef } = React;
// ===== components/placeholders.jsx =====
// Reusable SVG image placeholders — abstract "studio" imagery
// Different palette/composition per id so each tile reads as a distinct piece of work
const Placeholder = ({ id = 1, label = "image", className = "" }) => {
const variants = [
// 0 – warm dunes
,
// 1 – architectural lines
{Array.from({ length: 24 }).map((_, i) =>
)}
{Array.from({ length: 20 }).map((_, i) =>
)}
,
// 2 – portrait silhouette
,
// 3 – brand mark
M
,
// 4 – chrome curves
,
// 5 – grid product
{Array.from({ length: 16 }).map((_, i) =>
)}
{Array.from({ length: 12 }).map((_, i) =>
)}
,
// 6 – cool wash
,
// 7 – type spec
Aa
TYPOGRAPHIC STUDY · 01
];
const idx = (id % variants.length + variants.length) % variants.length;
return (
{variants[idx]}
{/* Halftone overlay */}
);
};
window.Placeholder = Placeholder;
// ===== components/loading.jsx =====
const LoadingScreen = ({ onComplete }) => {
const [count, setCount] = useState(0);
const [wordIdx, setWordIdx] = useState(0);
const [exiting, setExiting] = useState(false);
const words = ["Design", "Create", "Inspire"];
useEffect(() => {
const start = performance.now();
const duration = 2700;
let raf;
const tick = (t) => {
const elapsed = t - start;
const pct = Math.min(100, Math.floor(elapsed / duration * 100));
setCount(pct);
if (pct < 100) {
raf = requestAnimationFrame(tick);
} else {
setTimeout(() => {
setExiting(true);
setTimeout(() => onComplete && onComplete(), 500);
}, 400);
}
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [onComplete]);
useEffect(() => {
const iv = setInterval(() => setWordIdx((i) => (i + 1) % words.length), 900);
return () => clearInterval(iv);
}, []);
return (
{/* Top-left label */}
Portfolio
'26 — Loading
{/* Center: rotating words */}
{/* Bottom-right counter */}
{String(count).padStart(3, "0")}
{/* Bottom-left mini meta */}
Initialising visual system & assets
{/* Progress bar */}
);
};
window.LoadingScreen = LoadingScreen;
// ===== components/navbar.jsx =====
const Navbar = ({ active = "Home", onNav }) => {
const [scrolled, setScrolled] = React.useState(false);
const [menuOpen, setMenuOpen] = React.useState(false);
React.useEffect(() => {
const fn = () => setScrolled(window.scrollY > 100);
fn();
window.addEventListener('scroll', fn, { passive: true });
return () => window.removeEventListener('scroll', fn);
}, []);
// Lock body scroll while mobile menu is open
React.useEffect(() => {
document.body.style.overflow = menuOpen ? 'hidden' : '';
return () => { document.body.style.overflow = ''; };
}, [menuOpen]);
const links = ["Home", "Áreas de Atuação"];
const handleNav = (label) => {
setMenuOpen(false);
onNav && onNav(label);
};
return (
{/* Logo + wordmark — stacked on mobile, inline on md+ */}
{ e.preventDefault(); handleNav('Home'); }}
className="inline-flex flex-col md:flex-row md:items-center items-start gap-2 md:gap-0 transition-transform duration-300 hover:scale-105"
aria-label="Home"
>
Flavio Cardoso e Advogados
{/* Desktop links — hidden on mobile */}
{/* Hamburger — mobile only */}
setMenuOpen((v) => !v)}
aria-label={menuOpen ? "Close menu" : "Open menu"}
aria-expanded={menuOpen}
className="md:hidden relative z-[60] inline-flex items-center justify-center w-10 h-10 mt-1 rounded-full text-text-primary"
>
{/* Mobile menu overlay — outside so `fixed` is relative to the
viewport (nav's backdrop-filter would otherwise contain it) */}
);
};
window.Navbar = Navbar;
// ===== components/hero.jsx =====
const Hero = () => {
const sectionRef = useRef(null);
const glowRef = useRef(null);
// Mouse-follow glow
useEffect(() => {
const sec = sectionRef.current;
const glow = glowRef.current;
if (!sec || !glow) return;
let raf,tx = 0,ty = 0,x = 0,y = 0;
// Start centered
const init = () => {
const r = sec.getBoundingClientRect();
tx = x = r.width / 2;
ty = y = r.height / 2;
glow.style.transform = `translate3d(${x - 400}px, ${y - 400}px, 0)`;
};
init();
const onMove = (e) => {
const r = sec.getBoundingClientRect();
tx = e.clientX - r.left;
ty = e.clientY - r.top;
};
const tick = () => {
x += (tx - x) * 0.08;
y += (ty - y) * 0.08;
glow.style.transform = `translate3d(${x - 400}px, ${y - 400}px, 0)`;
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
sec.addEventListener('mousemove', onMove);
window.addEventListener('resize', init);
return () => {
cancelAnimationFrame(raf);
sec.removeEventListener('mousemove', onMove);
window.removeEventListener('resize', init);
};
}, []);
const roles = ["empresarial", "societário", "tributário", "civilista"];
const [roleIdx, setRoleIdx] = useState(0);
useEffect(() => {
const iv = setInterval(() => setRoleIdx((i) => (i + 1) % roles.length), 2000);
return () => clearInterval(iv);
}, []);
// Geometric shapes — thin ghost-line work
const shapes = [
// Big outlined circle, upper-left
{ kind: 'circle', top: '8%', left: '6%', size: 380, rot: 0, depth: 0.02, stroke: 'hsl(var(--text) / 0.10)' },
// Diagonal line slicing across
{ kind: 'line', top: '0%', left: '-5%', w: 700, h: 700, rot: 30, depth: 0.015, stroke: 'hsl(var(--text) / 0.08)' },
// Another diagonal, lower-right
{ kind: 'line', top: '30%', left: '40%', w: 1100, h: 4, rot: -8, depth: 0.012, stroke: 'hsl(var(--text) / 0.06)' },
// Tiny periwinkle accent dot
{ kind: 'dot', top: '14%', left: '74%', size: 8, rot: 0, depth: 0.06, fill: '#7D9BDC' },
// Faint smaller circle, mid-right
{ kind: 'circle', top: '55%', left: '82%', size: 120, rot: 0, depth: 0.025, stroke: 'hsl(var(--text) / 0.08)' }];
// Parallax for shapes
const shapesRef = useRef(null);
useEffect(() => {
const sec = sectionRef.current;
const wrap = shapesRef.current;
if (!sec || !wrap) return;
let raf;
let mx = 0,my = 0,cx = 0,cy = 0;
const onMove = (e) => {
const r = sec.getBoundingClientRect();
mx = (e.clientX - r.left) / r.width - 0.5;
my = (e.clientY - r.top) / r.height - 0.5;
};
const tick = () => {
cx += (mx - cx) * 0.06;
cy += (my - cy) * 0.06;
const kids = wrap.children;
for (let i = 0; i < kids.length; i++) {
const d = parseFloat(kids[i].dataset.depth || '0.04');
kids[i].style.transform = `translate3d(${-cx * 80 * (d * 25)}px, ${-cy * 80 * (d * 25)}px, 0) rotate(${kids[i].dataset.rot}deg)`;
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
sec.addEventListener('mousemove', onMove);
return () => {
cancelAnimationFrame(raf);
sec.removeEventListener('mousemove', onMove);
};
}, []);
const renderShape = (s, i) => {
if (s.kind === 'line') {
return (
);
}
if (s.kind === 'dot') {
return (
);
}
// circle (outlined)
return (
);
};
return (
{/* Background color glows — soft periwinkle blob follows the mouse,
with two complementary static blobs (lavender + teal) */}
{/* Static lavender blob — top right */}
{/* Static soft teal blob — bottom left */}
{/* Mouse-follow periwinkle blob */}
{/* Geometric shapes */}
{shapes.map(renderShape)}
{/* Bottom fade into page */}
{/* Content */}
Flavio Cardoso
Advogado{" "}
{roles[roleIdx]}
.
Consultoria integrada e suporte jurídico para negócios e empreendedores Middle.
Seu jurídico consultivo e contencioso com leitura de negócios e soluções eficientes.
{/* Scroll indicator */}
);
};
window.Hero = Hero;
// ===== components/section-header.jsx =====
const useInView = (ref, opts = {}) => {
const [inView, setInView] = React.useState(false);
React.useEffect(() => {
if (!ref.current) return;
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting) {
setInView(true);
obs.disconnect();
}
}, { threshold: 0.1, rootMargin: '-100px', ...opts });
obs.observe(ref.current);
return () => obs.disconnect();
}, []);
return inView;
};
const SectionHeader = ({ eyebrow, title, italic, subtext, cta }) => {
const ref = React.useRef(null);
const inView = useInView(ref);
return (
{eyebrow}
{title}{" "}{italic}
{subtext &&
{subtext}
}
{cta &&
{cta}
→
}
);
};
window.useInView = useInView;
window.SectionHeader = SectionHeader;
// ===== components/works.jsx =====
const WorkCard = ({ title, kind, span, aspect, phId }) => {
const ref = React.useRef(null);
const inView = window.useInView(ref);
return (
{/* Meta in corner */}
{kind}
'26
{/* Hover veil */}
{/* Bottom label (always visible, fades on hover) */}
{title}
↗
);
};
const Works = () => {
const projects = [
{ title: "Automotive Motion", kind: "Motion", span: "md:col-span-7", aspect: "aspect-[7/5]", phId: 4 },
{ title: "Urban Architecture", kind: "Architectural", span: "md:col-span-5", aspect: "aspect-[5/5] md:aspect-[7/5]", phId: 1 },
{ title: "Human Perspective", kind: "Portrait", span: "md:col-span-5", aspect: "aspect-[5/5] md:aspect-[7/5]", phId: 2 },
{ title: "Brand Identity", kind: "Identity", span: "md:col-span-7", aspect: "aspect-[7/5]", phId: 3 }];
return (
{projects.map((p, i) => )}
);
};
window.Works = Works;
// ===== components/services.jsx =====
const ServiceCard = ({ phId, image, tags, title, italic, desc, footer }) => {
const ref = React.useRef(null);
const inView = window.useInView(ref);
return (
{/* Top image */}
{image
?
:
}
{/* Content */}
{/* Category line with vertical accent bar */}
{tags}
{/* Heading */}
{title}{title && " "}{italic}
{/* Description */}
{desc}
{/* Footer */}
{footer}
);
};
const Services = () => {
const items = [
{
phId: 7,
image: "assets/card-1-v2.png",
tags: "Planejamento · Defesa · Recuperação",
title: "",
italic: "Tributário",
desc: "Planejamento fiscal, defesa em execuções e autuações, recuperação de créditos e estratégias para reduzir a carga tributária.",
footer: "01",
},
{
phId: 1,
image: "assets/card-2.png",
tags: "Contratos · Família · Sucessões",
title: "Direito",
italic: "civil",
desc: "Atuação em contratos, responsabilidade civil, sucessões, família e demandas patrimoniais — sempre protegendo os interesses do cliente.",
footer: "02",
},
{
phId: 3,
image: "assets/card-3.png",
tags: "Societário · Contratos · Governança",
title: "Direito",
italic: "empresarial",
desc: "Constituição e reestruturação societária, contratos comerciais, governança e prevenção de litígios para empresas de todos os portes.",
footer: "03",
},
{
phId: 6,
image: "assets/card-4-v2.png",
tags: "Crédito · Garantias · Estruturação",
title: "Contratos",
italic: "financeiros e bancários",
desc: "Estruturação, revisão e negociação de contratos financeiros e bancários, garantias e instrumentos de crédito para empresas e investidores.",
footer: "04",
},
{
phId: 5,
image: "assets/card-5.png",
tags: "Pareceres · Suporte · Estratégia",
title: "Consultoria",
italic: "estratégica",
desc: "Acompanhamento jurídico contínuo, pareceres técnicos e suporte preventivo para decisões de negócio com visão de longo prazo.",
footer: "05",
},
];
return (
{items.map((it, i) => (
))}
);
};
window.Services = Services;
// ===== components/journal.jsx =====
const JournalRow = ({ title, readTime, date, phId }) => {
const ref = React.useRef(null);
const inView = window.useInView(ref);
return (
{date}
{readTime}
{title}
↗
);
};
const Journal = () => {
const entries = [
{ title: "On the patience of building visual systems", date: "Mar 12", readTime: "6 min read", phId: 6 },
{ title: "Notes from a slow studio in March", date: "Feb 28", readTime: "4 min read", phId: 7 },
{ title: "Designing the in-between, ambient interaction", date: "Feb 04", readTime: "8 min read", phId: 5 },
{ title: "Why I keep a paper notebook on the desk", date: "Jan 18", readTime: "3 min read", phId: 0 }];
return (
{entries.map((e, i) => )}
);
};
window.Journal = Journal;
// ===== components/about.jsx =====
const About = () => {
const ref = React.useRef(null);
const inView = window.useInView(ref);
return (
{/* Left column — content */}
{/* Eyebrow */}
Sobre
{/* Heading */}
Sobre mim
{/* Body */}
Há mais de 12 anos trabalhando dentro de estruturas jurídicas de Grandes Empresas e Escritórios, em ambientes onde o erro jurídico tem custo alto e a tolerância para processos lentos e estruturação de negócios de forma ineficiente é zero.
Foi nesse cenário que desenvolvi uma forma diferente de trabalhar com Direito, unindo leitura de negócios e metodologias ao serviço jurídico qualificado, prestando um serviço orientado por dados e processos para resultados eficientes.
Ofereço um jurídico com inteligência, usando metodologias ágeis para resultados eficazes, soluções de Grandes Empresas que também cabem em Empresas de Pequeno e Médio Porte, familiares e Holdings de negócios e patrimoniais que estão crescendo e precisam que sua estrutura jurídica cresça junto.
{/* Right column — portrait */}
);
};
window.About = About;
// ===== components/stats.jsx =====
const Stats = () => {
const items = [
{ num: "+12", label: "de experiência" },
{ num: "+50", label: "Projetos societários e governança" },
{ num: "+3,7M", label: "Tributos recuperados" }];
return (
{items.map((s, i) => {
const ref = React.useRef(null);
const inView = window.useInView(ref);
return (
{s.num}
{s.label}
{i < items.length - 1 &&
}
);
})}
);
};
window.Stats = Stats;
// ===== components/footer.jsx =====
const Contact = () => {
const social = [
{ label: "LinkedIn", href: "https://www.linkedin.com/in/cardosoflavio" },
{ label: "Instagram", href: "https://www.instagram.com/adv.flaviocardoso/" }];
return (
);
};
window.Contact = Contact;
// ===== components/app.jsx =====
const App = () => {
const [active, setActive] = useState('Home');
// Track active section while scrolling
useEffect(() => {
const ids = ['home', 'work', 'contact'];
const labelOf = { home: 'Home', work: 'Áreas de Atuação', contact: 'Contact' };
const fn = () => {
const y = window.scrollY + window.innerHeight / 2;
let cur = 'home';
for (const id of ids) {
const el = document.getElementById(id);
if (el && el.offsetTop <= y) cur = id;
}
setActive(labelOf[cur]);
};
fn();
window.addEventListener('scroll', fn, { passive: true });
return () => window.removeEventListener('scroll', fn);
}, []);
const onNav = (label) => {
const map = { Home: 'home', 'Áreas de Atuação': 'work', Contact: 'contact' };
const el = document.getElementById(map[label]);
if (el) {
window.scrollTo({ top: el.offsetTop - 20, behavior: 'smooth' });
}
setActive(label);
};
return (
<>
>);
};
ReactDOM.createRoot(document.getElementById('root')).render( );