Initial commit

This commit is contained in:
marciobever 2026-05-15 14:18:07 +00:00
commit 8e32874bf5
51 changed files with 13146 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
Template-01/.DS_Store vendored Normal file

Binary file not shown.

20
Template-01/README.md Normal file
View file

@ -0,0 +1,20 @@
<div align="center">
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
</div>
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
View your app in AI Studio: https://ai.studio/apps/780e049e-cbd9-47bc-85b1-ae1175ffdb47
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

111
Template-01/index.html Normal file
View file

@ -0,0 +1,111 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Primary Meta Tags -->
<title>João Silva | Advogado Especialista - Trabalhista, Civil e Família</title>
<meta name="title" content="João Silva | Advogado Especialista - Trabalhista, Civil e Família">
<meta name="description" content="Atendimento jurídico humanizado e estratégico em São Paulo. Referência em Direito Trabalhista, Previdenciário, Civil, Família, Consumidor e Imobiliário.">
<meta name="keywords" content="advogado, advocacia, advogado especialista, direito trabalhista, direito previdenciário, direito civil, direito de família, direito do consumidor, direito imobiliário, advogado são paulo, consultoria jurídica, advogado online">
<!-- Open Graph / Facebook / WhatsApp (Card Compartilhado) -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://joaosilva.adv.br/">
<meta property="og:title" content="João Silva | Advocacia Premium e Especializada">
<meta property="og:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito Trabalhista, Previdenciário, Civil, Família, Consumidor e Imobiliário. Proteja seus direitos.">
<meta property="og:image" content="https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=1200&auto=format&fit=crop">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://joaosilva.adv.br/">
<meta property="twitter:title" content="João Silva | Advocacia Premium e Especializada">
<meta property="twitter:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito Trabalhista, Previdenciário, Civil, Família, Consumidor e Imobiliário. Proteja seus direitos.">
<meta property="twitter:image" content="https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=1200&auto=format&fit=crop">
<!-- Favicon (SVG) -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23D4AF37' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='M7 21h10'/><path d='M12 3v18'/><path d='M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2'/></svg>" />
<!-- Schema.org LegalService (Google Rich Content) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LegalService",
"name": "João Silva | Advogado Especialista",
"image": "https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=1200&auto=format&fit=crop",
"@id": "https://joaosilva.adv.br/",
"url": "https://joaosilva.adv.br/",
"telephone": "+5511999999999",
"address": {
"@type": "PostalAddress",
"streetAddress": "Av. Paulista, 1000 - Bela Vista",
"addressLocality": "São Paulo",
"addressRegion": "SP",
"postalCode": "01310-100",
"addressCountry": "BR"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": -23.561491,
"longitude": -46.655881
},
"priceRange": "$$",
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday"
],
"opens": "09:00",
"closes": "18:00"
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<!-- Theme Auto-Updater injected by autoblogia -->
<script>
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'UPDATE_APPEARANCE') {
const { primaryColor, backgroundColor, fontFamily } = event.data.settings;
const root = document.documentElement;
const style = root.style;
if (primaryColor) {
style.setProperty('--primary', primaryColor);
style.setProperty('--color-primary', primaryColor);
style.setProperty('--color-tech-primary', primaryColor);
style.setProperty('--color-seo-primary', primaryColor);
style.setProperty('--color-finance-primary', primaryColor);
style.setProperty('--color-recipe-primary', primaryColor);
style.setProperty('--color-health-primary', primaryColor);
style.setProperty('--color-corporate-primary', primaryColor);
}
if (backgroundColor) {
style.setProperty('--background', backgroundColor);
style.setProperty('--color-bg', backgroundColor);
style.setProperty('--color-tech-surface', backgroundColor);
style.setProperty('--color-seo-surface', backgroundColor);
style.backgroundColor = backgroundColor;
}
if (fontFamily) {
const fontString = '"' + fontFamily + '", sans-serif';
style.setProperty('--font-family', fontString);
style.setProperty('--font-sans', fontString);
style.fontFamily = fontString;
}
}
});
// Ping parent window that we are ready
window.parent.postMessage({ type: 'IFRAME_READY' }, '*');
</script>
</body>
</html>

View file

@ -0,0 +1,6 @@
{
"name": "Advocacia Premium",
"description": "Website premium para advogado autônomo com foco em autoridade e captação.",
"requestFramePermissions": [],
"majorCapabilities": []
}

4443
Template-01/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

38
Template-01/package.json Normal file
View file

@ -0,0 +1,38 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port=3000 --host=0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
"express": "^4.21.2",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"react-router-dom": "^7.15.0",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.3"
}
}

40
Template-01/src/App.tsx Normal file
View file

@ -0,0 +1,40 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { Navbar } from './components/layout/Navbar';
import { Footer } from './components/layout/Footer';
import { WhatsAppButton } from './components/layout/WhatsAppButton';
import { CookieBanner } from './components/layout/CookieBanner';
import { Analytics } from './components/layout/Analytics';
import { Home } from './pages/Home';
import { Sobre } from './pages/Sobre';
import { Areas } from './pages/Areas';
import { ComoFunciona } from './pages/ComoFunciona';
import { Blog } from './pages/Blog';
import { BlogPost } from './pages/BlogPost';
import { Contact } from './pages/Contact';
export default function App() {
return (
<Router>
<Analytics />
<div className="flex min-h-screen flex-col font-sans text-gray-800 antialiased selection:bg-brand-gold selection:text-white">
<Navbar />
<div className="flex-1">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/sobre" element={<Sobre />} />
<Route path="/areas" element={<Areas />} />
<Route path="/funcionamento" element={<ComoFunciona />} />
<Route path="/blog" element={<Blog />} />
<Route path="/blog/:id" element={<BlogPost />} />
<Route path="/contato" element={<Contact />} />
</Routes>
</div>
<Footer />
<WhatsAppButton />
<CookieBanner />
</div>
</Router>
);
}

View file

@ -0,0 +1,85 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
declare global {
interface Window {
gtag?: (...args: any[]) => void;
dataLayer?: any[];
fbq?: (...args: any[]) => void;
_fbq?: any;
}
}
export function Analytics() {
const location = useLocation();
// Inicialização dos Scripts (Executado apenas uma vez)
useEffect(() => {
// 1. Google Analytics (GA4)
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
if (gaId && !document.getElementById('ga-script')) {
const script1 = document.createElement('script');
script1.async = true;
script1.src = `https://www.googletagmanager.com/gtag/js?id=${gaId}`;
script1.id = 'ga-script';
document.head.appendChild(script1);
const script2 = document.createElement('script');
script2.innerHTML = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}');
`;
document.head.appendChild(script2);
}
// 2. Facebook Pixel
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
if (fbPixelId && !document.getElementById('fb-pixel-script')) {
const script = document.createElement('script');
script.id = 'fb-pixel-script';
script.innerHTML = `
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '${fbPixelId}');
fbq('track', 'PageView');
`;
document.head.appendChild(script);
}
// 3. Google AdSense
const adsenseId = import.meta.env.VITE_ADSENSE_CLIENT_ID;
if (adsenseId && !document.getElementById('adsense-script')) {
const script = document.createElement('script');
script.id = 'adsense-script';
script.async = true;
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adsenseId}`;
script.crossOrigin = 'anonymous';
document.head.appendChild(script);
}
}, []);
// Rastreamento de Mudança de Rota (Page Views)
useEffect(() => {
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
if (gaId && window.gtag) {
window.gtag('config', gaId, {
page_path: location.pathname + location.search,
});
}
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
if (fbPixelId && typeof window.fbq === 'function') {
window.fbq('track', 'PageView');
}
}, [location]);
return null; // Este componente não renderiza nada na tela
}

View file

@ -0,0 +1,69 @@
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { X } from 'lucide-react';
import { Link } from 'react-router-dom';
export function CookieBanner() {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
// Check if user has already accepted/declined
const consent = localStorage.getItem('lgpd_consent');
if (!consent) {
// Small delay so it doesn't pop up instantly on page load
const timer = setTimeout(() => setIsVisible(true), 1500);
return () => clearTimeout(timer);
}
}, []);
const handleConsent = (accepted: boolean) => {
localStorage.setItem('lgpd_consent', accepted ? 'accepted' : 'declined');
setIsVisible(false);
};
return (
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 100, opacity: 0 }}
className="fixed bottom-0 left-0 right-0 z-[60] bg-brand-blue p-4 shadow-2xl md:p-6"
>
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-4 md:flex-row">
<div className="text-sm text-gray-300 md:max-w-3xl">
<strong className="block text-white mb-1">Privacidade e LGPD</strong>
Utilizamos cookies para melhorar sua experiência em nosso site, personalizar conteúdo e analisar nosso tráfego.
Ao continuar navegando, você concorda com a nossa{' '}
<Link to="/contato" className="text-brand-gold hover:underline">
Política de Privacidade
</Link>.
</div>
<div className="flex w-full flex-shrink-0 flex-row gap-3 md:w-auto">
<button
onClick={() => handleConsent(false)}
className="flex-1 rounded-sm border border-white/30 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-white/10 md:flex-none"
>
Recusar
</button>
<button
onClick={() => handleConsent(true)}
className="flex-1 rounded-sm bg-brand-gold px-6 py-2 text-sm font-bold text-white transition-colors hover:bg-brand-gold-hover md:flex-none"
>
Aceitar
</button>
<button
onClick={() => handleConsent(false)}
className="hidden items-center justify-center text-gray-400 hover:text-white md:flex ml-2"
aria-label="Fechar"
>
<X className="h-5 w-5" />
</button>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
);
}

View file

@ -0,0 +1,75 @@
import React from 'react';
import { Scale, Mail, MapPin, Phone, Instagram, Linkedin } from 'lucide-react';
import { Link } from 'react-router-dom';
export function Footer() {
return (
<footer className="bg-brand-blue py-16 text-white text-opacity-80">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 gap-12 md:grid-cols-4 lg:grid-cols-5">
{/* Brand */}
<div className="lg:col-span-2">
<Link to="/" className="mb-6 flex items-center gap-2">
<Scale className="h-8 w-8 text-brand-gold" />
<div className="flex flex-col">
<span className="font-serif text-2xl font-bold leading-none tracking-tight text-white">
João Silva
</span>
<span className="text-xs font-medium tracking-widest text-brand-gold">
ADVOCACIA E CONSULTORIA
</span>
</div>
</Link>
<p className="mb-6 max-w-xs text-sm leading-relaxed">
Atendimento direto, estratégico e sigiloso focado na defesa incansável dos seus direitos e do seu patrimônio.
</p>
<div className="flex items-center gap-4">
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
<Instagram className="h-4 w-4" />
</a>
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
<Linkedin className="h-4 w-4" />
</a>
</div>
</div>
{/* Links */}
<div>
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Navegação</h3>
<ul className="flex flex-col gap-3 text-sm">
<li><Link to="/sobre" className="hover:text-brand-gold transition-colors">Sobre o Advogado</Link></li>
<li><Link to="/areas" className="hover:text-brand-gold transition-colors">Áreas de Atuação</Link></li>
<li><Link to="/funcionamento" className="hover:text-brand-gold transition-colors">Como Funciona</Link></li>
<li><Link to="/blog" className="hover:text-brand-gold transition-colors">Blog Jurídico</Link></li>
<li><Link to="/contato" className="hover:text-brand-gold transition-colors">Contato</Link></li>
</ul>
</div>
{/* Contact */}
<div className="lg:col-span-2">
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Contato Direto</h3>
<ul className="flex flex-col gap-4 text-sm">
<li className="flex items-start gap-3">
<Phone className="h-5 w-5 shrink-0 text-brand-gold" />
<span>+55 (11) 99999-9999</span>
</li>
<li className="flex items-start gap-3">
<Mail className="h-5 w-5 shrink-0 text-brand-gold" />
<span>contato@joaosilva.adv.br</span>
</li>
<li className="flex items-start gap-3">
<MapPin className="h-5 w-5 shrink-0 text-brand-gold" />
<span>Av. Paulista, 1000 - Bela Vista<br />São Paulo - SP, 01310-100</span>
</li>
</ul>
</div>
</div>
<div className="mt-16 flex flex-col items-center justify-between border-t border-white/10 pt-8 text-xs sm:flex-row">
<p>© {new Date().getFullYear()} João Silva Advocacia. Todos os direitos reservados.</p>
<p className="mt-2 sm:mt-0">OAB/SP 123.456</p>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,161 @@
import React, { useState, useEffect } from 'react';
import { Menu, X, Scale, Search } from 'lucide-react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { cn } from '../../lib/utils';
export function Navbar() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 10);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Handle hash scrolling when navigating across pages
useEffect(() => {
if (location.hash) {
const element = document.querySelector(location.hash);
if (element) {
setTimeout(() => {
element.scrollIntoView({ behavior: 'smooth' });
}, 100);
}
} else {
window.scrollTo(0,0);
}
}, [location]);
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
setIsMobileMenuOpen(false);
if (href.startsWith('/#')) {
const hash = href.substring(1);
if (location.pathname === '/') {
e.preventDefault();
const element = document.querySelector(hash);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
// Update URL hash without jumping
window.history.pushState(null, '', hash);
}
} else {
// Let it navigate to /#hash normally
}
}
};
const navLinks = [
{ name: 'Sobre', href: '/sobre' },
{ name: 'Áreas de Atuação', href: '/areas' },
{ name: 'Como Funciona', href: '/funcionamento' },
{ name: 'Blog', href: '/blog' },
{ name: 'Contato', href: '/contato' },
];
return (
<nav
className={cn(
'fixed left-0 right-0 top-0 z-50 transition-all duration-300',
isScrolled
? 'bg-white shadow-md py-3'
: 'bg-brand-blue py-5 text-white'
)}
>
<div className="mx-auto flex max-w-7xl items-center justify-between px-6 lg:px-8">
<Link to="/" className="flex items-center gap-2">
<Scale className={cn("h-8 w-8", isScrolled ? "text-brand-gold" : "text-brand-gold")} />
<div className="flex flex-col">
<span className={cn("font-serif text-xl font-bold leading-none tracking-tight", isScrolled ? "text-brand-blue" : "text-white")}>
João Silva
</span>
<span className={cn("text-xs font-medium tracking-widest", isScrolled ? "text-gray-500" : "text-gray-300")}>
ADVOGADO
</span>
</div>
</Link>
{/* Desktop Nav */}
<div className="hidden items-center gap-8 md:flex">
{navLinks.map((link) => (
<Link
key={link.name}
to={link.href}
onClick={(e) => handleNavClick(e, link.href)}
className={cn(
'text-sm font-medium transition-colors hover:text-brand-gold',
isScrolled ? 'text-gray-700' : 'text-gray-200'
)}
>
{link.name}
</Link>
))}
<div className="flex items-center gap-6 border-l border-white/20 pl-6">
<Link
to="/blog?focus=true"
title="Buscar Artigos"
className={cn(
'transition-colors hover:text-brand-gold',
isScrolled ? 'text-brand-blue' : 'text-white'
)}
>
<Search className="h-5 w-5" />
</Link>
<Link
to="/contato"
className="rounded-none border-2 border-brand-gold bg-brand-gold px-6 py-2 text-sm font-semibold text-white transition-colors hover:bg-transparent hover:text-brand-gold"
>
Consulta Online
</Link>
</div>
</div>
{/* Mobile Nav Toggle */}
<button
className="md:hidden"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? (
<X className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
) : (
<Menu className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
)}
</button>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="absolute left-0 right-0 top-full flex flex-col items-center gap-6 bg-white py-8 shadow-xl md:hidden">
{navLinks.map((link) => (
<Link
key={link.name}
to={link.href}
onClick={(e) => handleNavClick(e, link.href)}
className="text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
>
{link.name}
</Link>
))}
<Link
to="/blog?focus=true"
onClick={() => setIsMobileMenuOpen(false)}
className="flex items-center gap-2 text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
>
<Search className="h-5 w-5" /> Buscar Artigo
</Link>
<Link
to="/contato"
onClick={() => setIsMobileMenuOpen(false)}
className="mt-4 rounded-none border-2 border-brand-gold bg-brand-gold px-8 py-3 text-base font-semibold text-white transition-colors hover:bg-white hover:text-brand-gold"
>
Consulta Online
</Link>
</div>
)}
</nav>
);
}

View file

@ -0,0 +1,18 @@
import { Phone } from 'lucide-react';
export function WhatsAppButton() {
return (
<div className="fixed bottom-6 right-6 z-50">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
<a
href="https://wa.me/5511999999999?text=Ol%C3%A1%2C%20gostaria%20de%20agendar%20uma%20consulta%20jur%C3%ADdica."
target="_blank"
rel="noopener noreferrer"
className="relative flex h-14 w-14 items-center justify-center rounded-full bg-green-500 text-white shadow-lg transition-transform hover:scale-110 hover:shadow-xl hover:bg-green-600 focus:outline-none"
aria-label="Falar pelo WhatsApp"
>
<Phone className="h-7 w-7" fill="currentColor" />
</a>
</div>
);
}

View file

@ -0,0 +1,113 @@
export interface BlogPostData {
id: number;
title: string;
excerpt: string;
category: string;
date: string;
imageUrl: string;
content: string;
}
const generateMockPosts = (): BlogPostData[] => {
const categories = [
"Direito Trabalhista", "Direito Previdenciário", "Direito do Consumidor",
"Direito de Família", "Direito Imobiliário", "Direito Civil Empresarial"
];
const titles = [
"Direitos do Trabalhador: O que você precisa saber antes de pedir demissão",
"Revisão da Vida Toda: Entenda se você tem direito a receber os atrasados",
"Voo Cancelado: Quais são os seus direitos como consumidor?",
"Guarda Compartilhada vs Guarda Unilateral: Entendendo as diferenças",
"Contratos de Aluguel: Direitos e Deveres do Inquilino",
"Responsabilidade Civil de Sócios: Até onde vai o risco empresarial?",
"Atraso na Entrega de Imóveis: Direitos e Indenizações",
"Pensão Alimentícia: Como é feito o cálculo?",
"Assédio Moral no Trabalho: Como identificar e reunir provas",
"Golpes no Pix e a Responsabilidade dos Bancos",
"União Estável vs Casamento: Regimes de Bens",
"LGPD nas Pequenas Empresas: O que é preciso adequar?",
"Demissão por Justa Causa: Quando se aplica?",
"Auxílio-Doença Negado pelo INSS: O que fazer?",
"Cobranças Indevidas: Como agir e pedir reparação",
"Inventário Extrajudicial: Requisitos e Prazos",
"Horas Extras: Direitos, Limites e Reflexos",
"Aposentadoria Especial: Exigências e Comprovação",
"Direito de Arrependimento nas Compras Online",
"Alienação Parental: Consequências Jurídicas",
"Direitos Autorais na Era Digital: Protegendo suas Criações"
];
const images = [
"https://images.unsplash.com/photo-1589829085413-56de8ae18c73",
"https://images.unsplash.com/photo-1454165804606-c3d57bc86b40",
"https://images.unsplash.com/photo-1507679799987-c73779587ccf",
"https://images.unsplash.com/photo-1503694978374-8a2fa686963a",
"https://images.unsplash.com/photo-1554224155-6726b3ff858f",
"https://images.unsplash.com/photo-1600880292203-757bb62b4baf",
"https://images.unsplash.com/photo-1524813686514-a57563d77965",
"https://images.unsplash.com/photo-1589391886645-d51941baf7fb",
"https://images.unsplash.com/photo-1450101499163-c8848c66cb85",
"https://images.unsplash.com/photo-1629904853716-f0bc54eea481",
"https://images.unsplash.com/photo-1517486808906-6ca8b3f04846",
"https://images.unsplash.com/photo-1505373877841-8d25f7d46678",
"https://images.unsplash.com/photo-1593642532400-2682810df593",
"https://images.unsplash.com/photo-1436491865332-7a61a109cc05",
"https://images.unsplash.com/photo-1502086223501-7ea6ecd79368",
"https://images.unsplash.com/photo-1521791136064-7986c2920216",
"https://images.unsplash.com/photo-1573164713988-8665fc963095",
"https://images.unsplash.com/photo-1536640712-4d4c36ef0e2c",
"https://images.unsplash.com/photo-1523240795612-9a054b0db644",
"https://images.unsplash.com/photo-1494526585095-c41746248156",
"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab",
];
return titles.map((title, index) => {
const category = categories[index % categories.length];
return {
id: index + 1,
title: title,
excerpt: `Entenda os principais aspectos legais, a jurisprudência atualizada e como garantir seus direitos em relação a ${title.split(':')[0].toLowerCase()}.`,
category: category,
date: `0${(21 - index) % 9 + 1} Mar 2024`,
imageUrl: `${images[index]}?auto=format&fit=crop&q=80&w=800`,
content: `
<p>A área de <strong>${category}</strong> apresenta constantes desafios judiciais e extrajudiciais. O tema <em>"${title}"</em> tem sido objeto de grande debate nos tribunais superiores do Brasil.</p>
<h2>Contexto e Aplicação</h2>
<p>Diante das recentes mudanças legislativas, é fundamental entender seus impactos práticos. O Superior Tribunal de Justiça (STJ) tem firmado entendimentos importantes que afetam diretamente as relações cotidianas.</p>
<blockquote>
<p>"A proteção dos direitos fundamentais exige do operador do direito uma visão sistêmica e atualizada da jurisprudência." - Doutrina Jurídica Contemporânea</p>
</blockquote>
<p>Entre os principais pontos a serem observados na prática, destacamos:</p>
<ul>
<li>Análise preventiva de riscos e <em>compliance</em>.</li>
<li>Atuação extrajudicial para resolução pacífica de conflitos.</li>
<li>Utilização das novas teses firmadas em recursos repetitivos (Temas do STJ e STF).</li>
</ul>
<h2>Exemplo Prático e Decisões Recentes</h2>
<p>Imagine o seguinte cenário: um cliente procura auxílio relatando um problema complexo envolvendo direitos não reconhecidos na esfera pertinente ao caso. A abordagem ideal combina a análise documental detalhada com a jurisprudência mais recente.</p>
<figure>
<img src="${images[index]}?auto=format&fit=crop&q=80&w=800" alt="${title}" style="border-radius: 8px; margin: 20px 0; width: 100%; aspect-ratio: 16/9; object-fit: cover;" />
<figcaption style="text-align: center; font-size: 0.9em; color: gray;">Reflexões e desdobramentos práticos na defesa dos direitos.</figcaption>
</figure>
<h2>Como agir diante desta situação?</h2>
<p>O primeiro passo é reunir todas as provas materiais possíveis: documentos, trocas de e-mails, conversas de WhatsApp, fotografias ou qualquer registro formal da relação firmada entre as partes.</p>
<p>Após reunir a documentação, é vital buscar orientação técnica <strong>antes de assinar qualquer acordo ou recibo de quitação</strong>. Em muitos casos, a aceitação de termos genéricos pode inviabilizar a busca por uma reparação total no futuro.</p>
<p>Portanto, para mais informações e para análise pormenorizada do seu caso concreto, certifique-se de contar com assessoria jurídica especializada e combativa. <a href="/contato">Agende uma consulta conosco</a> para entender as particularidades da sua situação.</p>
`
};
});
};
export const MOCK_POSTSList = generateMockPosts();
export const MOCK_POSTS: Record<string, BlogPostData> = MOCK_POSTSList.reduce((acc, curr) => ({
...acc,
[curr.id.toString()]: curr
}), {});

37
Template-01/src/index.css Normal file
View file

@ -0,0 +1,37 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap');
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-serif: "Playfair Display", ui-serif, Georgia, serif;
--color-brand-blue: #0A192F;
--color-brand-blue-light: #112240;
--color-brand-gold: #D4AF37;
--color-brand-gold-hover: #C5A030;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-sans);
background-color: #FAFAFA;
}
/* Modern clean scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #0A192F;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #D4AF37;
}

View file

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

10
Template-01/src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

View file

@ -0,0 +1,92 @@
import React from 'react';
import { Users, ShieldCheck, Scale, FileText, Home as HomeIcon, ArrowRight } from 'lucide-react';
import { motion } from 'motion/react';
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function Areas() {
const areas = [
{
title: "Direito Trabalhista",
icon: Users,
desc: "Defesa dos seus direitos no ambiente de trabalho. Atuamos com rescisões indiretas, cobrança de horas extras não pagas, assédio moral no trabalho, reconhecimento de vínculo empregatício e doenças ocupacionais. Orientação tanto para empregados quanto para empresas em compliance preventivo."
},
{
title: "Direito Previdenciário",
icon: ShieldCheck,
desc: "Garantia do seu benefício justo. Realizamos planejamento previdenciário, pedidos de aposentadoria por tempo de contribuição ou idade, aposentadorias especiais, benefícios assistenciais (BPC/LOAS), pensão por morte e reversão de auxílio-doença negado pelo INSS."
},
{
title: "Direito Civil",
icon: Scale,
desc: "Proteção do seu patrimônio e interesses civis. Elaboração e revisão de contratos, ações de indenizações por danos morais e materiais, responsabilidade civil, regularização de propriedades e processos de cobranças diversas para recuperação de crédito."
},
{
title: "Direito de Família",
icon: Users,
desc: "Soluções ágeis com a sensibilidade que o caso exige. Orientação em divórcios consensuais ou litigiosos, fixação e revisão de pensão alimentícia, regulamentação de guarda e convivência (visitas), além de atuação em inventários judiciais e extrajudiciais."
},
{
title: "Direito do Consumidor",
icon: FileText,
desc: "Defesa intransigente contra abusos de empresas e prestadoras de serviço. Atuamos em casos de negativações indevidas (SPC/Serasa), falhas em prestação de serviços (telefonia, planos de saúde), atrasos e cancelamentos de voos, e defeitos não solucionados em produtos novos."
},
{
title: "Direito Imobiliário",
icon: HomeIcon,
desc: "Segurança total em transações de imóveis. Assessoramos em usucapião para legalização de titularidade, distratos imobiliários com construtoras por atraso de obras, confecção de contratos de aluguel e venda protegidos, e ações de despejo."
}
];
return (
<main className="w-full bg-[#f9fafb] pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mb-16 text-center">
<FadeIn>
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl sm:text-5xl">
Áreas de Atuação
</h1>
<p className="mx-auto max-w-2xl text-lg text-gray-600">
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
</p>
</FadeIn>
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{areas.map((area, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
<div>
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-blue-50 text-brand-blue transition-colors group-hover:bg-brand-gold group-hover:text-white">
<area.icon className="h-6 w-6" />
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
{area.title}
</h3>
<p className="mb-8 text-gray-600 leading-relaxed text-sm">
{area.desc}
</p>
</div>
<a
href="https://wa.me/5511999999999"
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
>
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
</a>
</div>
</FadeIn>
))}
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,120 @@
import React, { useState, useRef, useEffect } from 'react';
import { ArrowLeft, Search, Calendar, User } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
import { motion } from 'motion/react';
import { MOCK_POSTSList } from '../data/posts';
export function Blog() {
const [activeCategory, setActiveCategory] = useState("Todos");
const [searchQuery, setSearchQuery] = useState("");
const location = useLocation();
const searchInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (location.state?.focusSearch || location.search.includes('focus=true')) {
setTimeout(() => {
searchInputRef.current?.focus();
}, 100);
}
}, [location]);
const filteredPosts = MOCK_POSTSList.filter(post => {
const matchesCategory = activeCategory === "Todos" || post.category === activeCategory;
const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
post.excerpt.toLowerCase().includes(searchQuery.toLowerCase());
return matchesCategory && matchesSearch;
});
const categories = ["Todos", ...Array.from(new Set(MOCK_POSTSList.map(p => p.category)))];
return (
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
{/* Header */}
<div className="mb-16">
<Link to="/" className="mb-6 inline-flex items-center gap-2 text-sm text-brand-gold hover:underline">
<ArrowLeft className="h-4 w-4" /> Voltar para o início
</Link>
<div className="flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
<div>
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
Blog Jurídico
</h1>
<p className="max-w-xl text-lg text-gray-600">
Artigos desmistificando o direito de forma clara. Saiba como se proteger nas mais diversas situações do dia a dia.
</p>
</div>
{/* Search */}
<div className="relative w-full max-w-md">
<input
ref={searchInputRef}
type="text"
placeholder="Buscar palavra-chave..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full border-b-2 border-gray-200 bg-transparent py-2 pl-2 pr-10 text-brand-blue outline-none transition-colors focus:border-brand-gold"
/>
<Search className="absolute right-2 top-2 h-5 w-5 text-gray-400" />
</div>
</div>
</div>
{/* Categories (Mock) */}
<div className="mb-12 flex flex-wrap gap-3">
{categories.map(cat => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={`rounded-full px-5 py-2 text-sm font-medium transition-colors ${cat === activeCategory ? 'bg-brand-blue text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-brand-gold hover:text-brand-gold'}`}
>
{cat}
</button>
))}
</div>
{/* Posts Grid */}
<div className="grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3">
{filteredPosts.map((post, index) => (
<motion.article
key={post.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="group flex cursor-pointer flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
>
<div className="aspect-[16/10] w-full overflow-hidden">
<img
src={post.imageUrl}
alt={post.title}
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
/>
</div>
<div className="flex flex-1 flex-col p-6">
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
{post.category}
</span>
<Link to={`/blog/${post.id}`} className="mb-3 font-serif text-xl font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
{post.title}
</Link>
<p className="mb-6 flex-1 text-sm text-gray-600 line-clamp-3">
{post.excerpt}
</p>
<div className="mt-auto flex items-center justify-between border-t border-gray-100 pt-4 text-xs text-gray-500">
<div className="flex items-center gap-2">
<User className="h-3 w-3" /> João Silva
</div>
<div className="flex items-center gap-2">
<Calendar className="h-3 w-3" /> {post.date}
</div>
</div>
</div>
</motion.article>
))}
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,212 @@
import React, { useEffect, useState } from 'react';
import { ArrowLeft, User, Calendar, Share2, Copy, Check, MessageCircle, Linkedin, Facebook, Twitter, X } from 'lucide-react';
import { Link, useParams } from 'react-router-dom';
import { motion, AnimatePresence } from 'motion/react';
import { MOCK_POSTS, MOCK_POSTSList } from '../data/posts';
export function BlogPost() {
const { id } = useParams();
const post = id ? MOCK_POSTS[id] : null;
const [showShareMenu, setShowShareMenu] = useState(false);
const [copied, setCopied] = useState(false);
useEffect(() => {
window.scrollTo({ top: 0, behavior: 'instant' });
}, [id]);
if (!post) {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-50 pt-20">
<div className="text-center">
<h2 className="mb-4 text-2xl font-bold text-brand-blue">Artigo não encontrado</h2>
<Link to="/blog" className="text-brand-gold hover:underline">
Voltar para o Blog
</Link>
</div>
</div>
);
}
const url = window.location.href;
const encodedUrl = encodeURIComponent(url);
const encodedTitle = encodeURIComponent(post.title);
const copyToClipboard = () => {
navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const shareLinks = [
{
name: 'WhatsApp',
icon: MessageCircle,
href: `https://api.whatsapp.com/send?text=${encodedTitle}%20${encodedUrl}`,
color: 'hover:text-green-500 hover:bg-green-50'
},
{
name: 'LinkedIn',
icon: Linkedin,
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
color: 'hover:text-blue-600 hover:bg-blue-50'
},
{
name: 'Facebook',
icon: Facebook,
href: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
color: 'hover:text-blue-500 hover:bg-blue-50'
},
{
name: 'X (Twitter)',
icon: Twitter,
href: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,
color: 'hover:text-gray-900 hover:bg-gray-100'
}
];
const relatedPosts = MOCK_POSTSList.filter(p => p.category === post.category && p.id !== post.id).slice(0, 3);
if (relatedPosts.length < 3) {
const morePosts = MOCK_POSTSList.filter(p => !relatedPosts.includes(p) && p.id !== post.id);
relatedPosts.push(...morePosts.slice(0, 3 - relatedPosts.length));
}
return (
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
<article className="mx-auto max-w-4xl px-6 lg:px-8">
{/* ... (rest of the content is unchanged) */}
<Link to="/blog" className="mb-10 inline-flex items-center gap-2 text-sm text-gray-500 transition-colors hover:text-brand-gold">
<ArrowLeft className="h-4 w-4" /> Todos os artigos
</Link>
{/* Header */}
<header className="mb-12 text-center">
<span className="mb-4 inline-block bg-brand-blue px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand-gold">
{post.category}
</span>
<h1 className="mb-6 font-serif text-3xl font-bold leading-tight text-brand-blue md:text-5xl">
{post.title}
</h1>
<div className="flex flex-wrap items-center justify-center gap-6 border-b border-gray-200 pb-8 text-sm text-gray-500">
<div className="flex items-center gap-2">
<User className="h-4 w-4" /> Dr. João Silva
</div>
<div className="flex items-center gap-2">
<Calendar className="h-4 w-4" /> {post.date}
</div>
<button
onClick={copyToClipboard}
className={`flex items-center gap-2 transition-colors ${copied ? 'text-green-600' : 'hover:text-brand-blue'}`}
>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
{copied ? 'Copiado!' : 'Copiar Link'}
</button>
<div className="relative">
<button
onClick={() => setShowShareMenu(!showShareMenu)}
className="flex items-center gap-2 hover:text-brand-blue transition-colors"
>
{showShareMenu ? <X className="h-4 w-4" /> : <Share2 className="h-4 w-4" />}
Compartilhar
</button>
<AnimatePresence>
{showShareMenu && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ duration: 0.15 }}
className="absolute right-1/2 top-full mt-2 w-48 translate-x-1/2 sm:translate-x-0 sm:right-0 sm:left-auto rounded-lg border border-gray-100 bg-white p-2 shadow-xl z-10"
>
<div className="flex flex-col text-left">
{shareLinks.map((link) => (
<a
key={link.name}
href={link.href}
target="_blank"
rel="noopener noreferrer"
className={`flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-gray-700 transition-colors ${link.color}`}
onClick={() => setShowShareMenu(false)}
>
<link.icon className="h-4 w-4" /> {link.name}
</a>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</header>
{/* Featured Image */}
<motion.div
initial={{ opacity: 0, scale: 0.98 }}
animate={{ opacity: 1, scale: 1 }}
className="mb-14 overflow-hidden rounded-sm"
>
<img
src={post.imageUrl}
alt={post.title}
className="aspect-[2/1] w-full object-cover"
/>
</motion.div>
{/* Article Body */}
<div className="mx-auto max-w-3xl prose prose-blue prose-lg prose-headings:font-serif prose-headings:text-brand-blue prose-a:text-brand-gold prose-a:no-underline hover:prose-a:underline">
{/* Render HTML content safely since it's hardcoded mock, but natively prefer Markdown if CMS */}
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</div>
{/* CTA Section */}
<div className="mx-auto mt-20 max-w-3xl bg-brand-blue p-8 text-center sm:p-12 mb-20">
<h3 className="mb-4 font-serif text-2xl font-bold text-white">Este conteúdo ajudou em sua situação?</h3>
<p className="mb-8 text-gray-300">Cada caso demanda uma análise específica. Caso esteja passando por esta situação, avaliarei o seu cenário de forma técnica e sigilosa.</p>
<Link to="/contato" className="inline-block bg-brand-gold px-8 py-3 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue">
Solicitar Análise de Caso
</Link>
</div>
{/* Artigos Relacionados */}
{relatedPosts.length > 0 && (
<div className="mx-auto max-w-5xl border-t border-gray-100 pt-16">
<h3 className="mb-8 font-serif text-2xl font-bold text-brand-blue text-center">
Você também pode se interessar por:
</h3>
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
{relatedPosts.map((relatedPost, index) => (
<motion.article
key={relatedPost.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="group flex flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
>
<Link to={`/blog/${relatedPost.id}`} className="aspect-[16/10] w-full overflow-hidden block">
<img
src={relatedPost.imageUrl}
alt={relatedPost.title}
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
/>
</Link>
<div className="flex flex-1 flex-col p-6">
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
{relatedPost.category}
</span>
<Link to={`/blog/${relatedPost.id}`} className="font-serif text-lg font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
{relatedPost.title}
</Link>
</div>
</motion.article>
))}
</div>
</div>
)}
</article>
</main>
);
}

View file

@ -0,0 +1,96 @@
import React from 'react';
import { MessageCircle } from 'lucide-react';
import { Link } from 'react-router-dom';
import { motion } from 'motion/react';
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function ComoFunciona() {
const steps = [
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu. Não há barreiras burocráticas no primeiro contato." },
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso, informando as chances de êxito." },
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, os custos de forma totalmente transparente e enviamos o termo de prestação de serviços." },
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado ativamente em cada etapa e movimentação chave do processo." }
];
const points = [
{ title: "Atendimento Direto", desc: "Você fala diretamente com o advogado responsável pelo caso, sem secretárias intermediando." },
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples que qualquer um entende." },
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
];
return (
<main className="w-full bg-white pb-24 pt-32">
<section className="bg-brand-blue py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<FadeIn>
<h1 className="mb-6 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-5xl">
Como funciona o acompanhamento
</h1>
<p className="mx-auto mb-16 max-w-2xl text-center text-lg text-gray-300">
Minha prioridade é oferecer um método de atendimento moderno, simplificado e altamente eficiente.
</p>
</FadeIn>
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[15px] z-0"></div>
{steps.map((step, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="relative z-10 flex flex-col items-center text-center">
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
{step.num}
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
<p className="text-gray-300 text-sm leading-relaxed">{step.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</section>
<section className="py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
<FadeIn>
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Por que me escolher para defender você?
</h2>
<p className="mb-10 text-lg text-gray-600 text-balance">
Acredito que a advocacia moderna não precisa ser lenta, cheia de protocolos e distante do cliente. Meu modelo de atuação é focado no resultado e na tranquilidade do cliente.
</p>
<Link
to="/contato"
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
>
<MessageCircle className="h-5 w-5" /> Agendar Consulta
</Link>
</FadeIn>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
{points.map((point, i) => (
<FadeIn key={i} delay={i * 0.1}>
<div className="border-l-4 border-brand-gold pl-6">
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</div>
</section>
</main>
);
}

View file

@ -0,0 +1,216 @@
import React, { useState, useEffect } from 'react';
import { motion } from 'motion/react';
import { MapPin, Phone, Mail, Clock, Send, CheckCircle2 } from 'lucide-react';
import { Link } from 'react-router-dom';
export function Contact() {
const [formState, setFormState] = useState<'idle' | 'submitting' | 'success'>('idle');
useEffect(() => {
window.scrollTo(0, 0);
}, []);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setFormState('submitting');
// Simulate form submission
setTimeout(() => {
setFormState('success');
}, 1500);
};
return (
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
{/* Header */}
<div className="mb-16 text-center">
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
Entre em Contato
</h1>
<p className="mx-auto max-w-2xl text-lg text-gray-600">
Estamos prontos para ouvir você e encontrar a melhor solução jurídica para o seu caso. Escolha a forma de contato de sua preferência.
</p>
</div>
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2">
{/* Informações de Contato */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="flex flex-col justify-between"
>
<div>
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Informações de Atendimento</h2>
<div className="mb-12 space-y-8">
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<Phone className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">WhatsApp & Telefone</h3>
<p className="text-gray-600">+55 (11) 99999-9999</p>
<p className="text-gray-600">Atendimento prioritário via WhatsApp</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<Mail className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">E-mail</h3>
<p className="text-gray-600">contato@joaosilva.adv.br</p>
<p className="text-gray-600">Para envio de documentos e propostas</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<MapPin className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Escritório Físico</h3>
<p className="text-gray-600">Av. Paulista, 1000 - Bela Vista</p>
<p className="text-gray-600">São Paulo - SP, 01310-100</p>
<p className="mt-1 text-sm text-brand-gold">Atendimento presencial apenas com agendamento.</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<Clock className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Horário de Funcionamento</h3>
<p className="text-gray-600">Segunda a Sexta: 09h às 18h</p>
<p className="text-gray-600">Plantão para urgências criminais 24h</p>
</div>
</div>
</div>
</div>
{/* Map (Placeholder using generic image or iframe) */}
<div className="aspect-[16/9] w-full overflow-hidden bg-gray-100 filter grayscale">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3657.1973618684784!2d-46.658097023769186!3d-23.56134956158784!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x94ce59c8da0aa315%3A0xd59f9431f2c9776a!2sAv.%20Paulista%2C%201000%20-%20Bela%20Vista%2C%20S%C3%A3o%20Paulo%20-%20SP%2C%2001310-100!5e0!3m2!1spt-BR!2sbr!4v1714571987515!5m2!1spt-BR!2sbr"
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen={false}
loading="lazy"
referrerPolicy="no-referrer-when-downgrade">
</iframe>
</div>
</motion.div>
{/* Formulário de Contato */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="flex flex-col justify-center bg-white p-8 shadow-xl md:p-12 border border-gray-100"
>
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Envie sua Mensagem</h2>
{formState === 'success' ? (
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="flex flex-col items-center justify-center p-8 text-center"
>
<CheckCircle2 className="mb-4 h-16 w-16 text-green-500" />
<h3 className="mb-2 font-serif text-xl font-bold text-brand-blue">Mensagem Enviada!</h3>
<p className="mb-8 text-gray-600">Recebemos seu contato. Retornaremos o mais breve possível para o e-mail ou telefone informado.</p>
<button
onClick={() => setFormState('idle')}
className="bg-brand-blue px-6 py-3 font-semibold text-white transition-colors hover:bg-brand-gold"
>
Enviar Nova Mensagem
</button>
</motion.div>
) : (
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<label htmlFor="name" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Seu Nome Completo</label>
<input
type="text"
id="name"
required
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="Ex: Carlos Silva"
/>
</div>
<div className="flex flex-col gap-6 md:flex-row">
<div className="flex w-full flex-col gap-2">
<label htmlFor="email" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">E-mail</label>
<input
type="email"
id="email"
required
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="seu@email.com"
/>
</div>
<div className="flex w-full flex-col gap-2">
<label htmlFor="phone" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Telefone / WhatsApp</label>
<input
type="tel"
id="phone"
required
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="(11) 99999-9999"
/>
</div>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="area" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Área de Interesse (Opcional)</label>
<select
id="area"
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold text-gray-600"
>
<option value="">Selecione a área...</option>
<option value="trabalhista">Direito Trabalhista</option>
<option value="previdenciario">Direito Previdenciário</option>
<option value="civil">Direito Civil</option>
<option value="familia">Direito de Família</option>
<option value="consumidor">Direito do Consumidor</option>
<option value="outros">Outros</option>
</select>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="message" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Resumo do Caso</label>
<textarea
id="message"
rows={4}
required
className="resize-none border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="Descreva brevemente o que aconteceu..."
></textarea>
</div>
<button
type="submit"
disabled={formState === 'submitting'}
className="mt-4 flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-all hover:bg-brand-blue disabled:opacity-70 disabled:hover:bg-brand-gold"
>
{formState === 'submitting' ? 'Enviando...' : (
<>
Enviar Mensagem <Send className="h-4 w-4" />
</>
)}
</button>
<p className="text-xs text-gray-500 text-center">
Garantimos sigilo absoluto sobre todas as informações prestadas neste formulário.
</p>
</form>
)}
</motion.div>
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,471 @@
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { Link } from 'react-router-dom';
import {
ArrowRight, ShieldCheck, Scale, FileText,
Users, Building, Home as HomeIcon, CheckCircle2,
MessageCircle, Plus, Minus
} from 'lucide-react';
import { cn } from '../lib/utils';
// Reusable animated container
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function Home() {
return (
<main className="w-full">
<HeroSection />
<AboutSection />
<PracticeAreasSection />
<HowItWorksSection />
<DifferentiatorsSection />
<TestimonialsSection />
<FAQSection />
<FinalCTASection />
</main>
);
}
function HeroSection() {
return (
<section className="relative flex min-h-[90vh] items-center bg-brand-blue pt-20">
{/* Background Pattern / Overlay */}
<div className="absolute inset-0 z-0 opacity-10 bg-[url('https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
<div className="relative z-10 mx-auto grid w-full max-w-7xl grid-cols-1 gap-12 px-6 lg:grid-cols-2 lg:px-8">
<motion.div
className="flex flex-col justify-center pt-10 pb-16 lg:py-24"
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
>
<div className="mb-6 flex items-center gap-4">
<span className="h-[1px] w-12 bg-brand-gold"></span>
<span className="text-sm font-semibold tracking-widest text-brand-gold uppercase">
OAB/SP 123.456
</span>
</div>
<h1 className="mb-6 font-serif text-4xl leading-tight font-bold text-white md:text-5xl lg:text-6xl">
Advocacia estratégica e <span className="text-brand-gold">atendimento direto</span> para proteger seus direitos.
</h1>
<p className="mb-10 max-w-lg text-lg text-gray-300 leading-relaxed">
Atendimento humanizado, sem intermediários. Foco em soluções ágeis e máxima segurança jurídica para você e seu patrimônio.
</p>
<div className="flex flex-col gap-4 sm:flex-row">
<a
href="https://wa.me/5511999999999"
target="_blank"
rel="noreferrer"
className="flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 text-center font-semibold text-white transition-all hover:bg-brand-gold-hover hover:-translate-y-1"
>
Falar com o Advogado <ArrowRight className="h-5 w-5" />
</a>
<Link
to="/areas"
className="flex items-center justify-center gap-2 border border-white/30 px-8 py-4 text-center font-semibold text-white backdrop-blur-sm transition-all hover:bg-white hover:text-brand-blue"
>
Conheça as Áreas
</Link>
</div>
</motion.div>
<motion.div
className="relative hidden items-end justify-center lg:flex"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
>
{/* Lawyer Photo - High quality professional portrait */}
<div className="relative h-[85%] w-full max-w-md">
<div className="absolute -inset-4 border border-brand-gold/30 translate-x-4 -translate-y-4"></div>
<img
src="https://images.unsplash.com/photo-1556157382-97eda2d62296?auto=format&fit=crop&q=80&w=800"
alt="Dr. João Silva"
className="relative h-full w-full object-cover object-top shadow-2xl grayscale-[20%] contrast-125"
/>
{/* Experience Badge */}
<div className="absolute -left-12 bottom-12 flex items-center gap-4 bg-white p-4 shadow-xl">
<div className="flex h-12 w-12 items-center justify-center bg-brand-blue text-brand-gold font-serif font-bold text-xl">
15
</div>
<p className="font-serif text-sm font-bold leading-tight text-brand-blue uppercase">
Anos de<br/>Experiência
</p>
</div>
</div>
</motion.div>
</div>
</section>
);
}
function AboutSection() {
return (
<section id="sobre" className="overflow-hidden bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
<FadeIn>
<div className="relative">
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md">
<img
src="https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&q=80&w=800"
alt="Advogado João Silva em seu escritório"
className="h-full w-full object-cover grayscale-[10%]"
/>
</div>
<div className="absolute -right-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10"></div>
</div>
</FadeIn>
<FadeIn delay={0.2}>
<div className="max-w-xl">
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Dr. João Silva
</h2>
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
Especialista & Fundador
</p>
<div className="mb-8 space-y-4 text-lg text-gray-600">
<p>
Com mais de uma década de dedicação exclusiva à advocacia, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela transparência e proximidade.
</p>
<p>
Não sou apenas um intermediário em processos. Sou um parceiro estratégico focado em defender seus interesses. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, do início ao fim.
</p>
</div>
<ul className="mb-10 space-y-4">
{[
"Formação em Direito pela Universidade de São Paulo (USP)",
"Pós-graduado em Direito Civil e Processo Civil",
"Atuação ética focada na resolução rápida de conflitos",
"Atendimento 100% humanizado e sem burocracias"
].map((item, i) => (
<li key={i} className="flex items-start gap-3 text-gray-700">
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
<span>{item}</span>
</li>
))}
</ul>
</div>
</FadeIn>
</div>
</div>
</section>
);
}
function PracticeAreasSection() {
const areas = [
{
title: "Direito Trabalhista",
icon: Users,
desc: "Defesa dos seus direitos no ambiente de trabalho. Rescisões, horas extras, assédio moral e doenças ocupacionais."
},
{
title: "Direito Previdenciário",
icon: ShieldCheck,
desc: "Garantia do seu benefício justo. Aposentadorias, BPC/LOAS, pensão por morte e auxílio-doença."
},
{
title: "Direito Civil",
icon: Scale,
desc: "Proteção do seu patrimônio e interesses. Contratos, indenizações, responsabilidade civil e cobranças."
},
{
title: "Direito de Família",
icon: Users,
desc: "Soluções com sensibilidade. Divórcios, pensão alimentícia, guarda, inventários e partilha de bens."
},
{
title: "Direito do Consumidor",
icon: FileText,
desc: "Contra abusos de empresas. Negativações indevidas, problemas com planos de saúde e voos, produtos com defeito."
},
{
title: "Direito Imobiliário",
icon: HomeIcon,
desc: "Segurança em transações. Regularização de imóveis, usucapião, contratos de aluguel e distratos."
}
];
return (
<section id="areas" className="bg-[#f9fafb] py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mb-16 text-center">
<FadeIn>
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Áreas de Especialidade
</h2>
<p className="mx-auto max-w-2xl text-lg text-gray-600">
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
</p>
</FadeIn>
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{areas.map((area, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
<div>
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-blue-50 text-brand-blue transition-colors group-hover:bg-brand-gold group-hover:text-white">
<area.icon className="h-6 w-6" />
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
{area.title}
</h3>
<p className="mb-8 text-gray-600">
{area.desc}
</p>
</div>
<a
href="https://wa.me/5511999999999"
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
>
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
</a>
</div>
</FadeIn>
))}
</div>
</div>
</section>
);
}
function HowItWorksSection() {
const steps = [
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu." },
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso." },
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, custos transparentes e fechamos o acordo." },
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado em cada etapa." }
];
return (
<section id="funcionamento" className="bg-brand-blue py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<FadeIn>
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl">
Como funciona meu atendimento
</h2>
</FadeIn>
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[45px] z-0"></div>
{steps.map((step, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="relative z-10 flex flex-col items-center text-center">
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
{step.num}
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
<p className="text-gray-300">{step.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</section>
);
}
function DifferentiatorsSection() {
const points = [
{ title: "Atendimento Direto", desc: "Você fala diretamente com o advogado responsável pelo caso, sem secretárias intermediando." },
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples que qualquer um entende." },
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
];
return (
<section className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
<FadeIn>
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Por que me escolher para defender você?
</h2>
<p className="mb-10 text-lg text-gray-600 text-balance">
Advocacia não precisa ser lenta e distante. Meu modelo de atuação é focado no resultado e no cliente.
</p>
<Link
to="/contato"
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
>
<MessageCircle className="h-5 w-5" /> Agendar Consulta
</Link>
</FadeIn>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
{points.map((point, i) => (
<FadeIn key={i} delay={i * 0.1}>
<div className="border-l-4 border-brand-gold pl-6">
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</div>
</section>
);
}
function TestimonialsSection() {
const testimonials = [
{ quote: "O Dr. João foi excepcional. Resolveu meu problema trabalhista muito mais rápido do que eu esperava. Ele me manteve informada pelo WhatsApp durante todo o processo.", author: "Maria C.", role: "Cliente" },
{ quote: "Senti muita confiança desde a primeira reunião. Ele explicou tudo sem usar aquele monte de palavras difíceis e foi muito transparente sobre os custos. Recomendo muito.", author: "Carlos R.", role: "Cliente" },
{ quote: "Meu processo de aposentadoria estava travado, e em pouco tempo após ele assumir conseguimos destravar. Agradeço pela paciência e excelente trabalho.", author: "Antônio P.", role: "Cliente" }
];
return (
<section className="bg-[#fdfbf6] py-24 sm:py-32 border-y border-amber-900/5">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<FadeIn>
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
A opinião de quem confia
</h2>
</FadeIn>
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
{testimonials.map((item, i) => (
<FadeIn key={i} delay={i * 0.1}>
<div className="flex h-full flex-col justify-between bg-white p-8 shadow-sm border border-gray-100">
<div>
<div className="mb-6 text-brand-gold">
{"★".repeat(5)}
</div>
<p className="mb-8 text-gray-700 font-medium italic">"{item.quote}"</p>
</div>
<div className="flex items-center gap-3 border-t border-gray-100 pt-4">
<div className="h-10 w-10 shrink-0 bg-gray-200 rounded-full"></div>
<div>
<h4 className="font-serif font-bold text-brand-blue">{item.author}</h4>
<p className="text-xs text-gray-500 uppercase tracking-widest">{item.role}</p>
</div>
</div>
</div>
</FadeIn>
))}
</div>
</div>
</section>
);
}
function FAQSection() {
const faqs = [
{
q: "Preciso ir até o escritório para ser atendido?",
a: "Não é necessário. Todo o atendimento pode ser realizado de forma 100% online via WhatsApp ou videoconferência. Você assina e envia os documentos pelo celular com validade jurídica."
},
{
q: "A primeira consulta é cobrada?",
a: "Avaliamos rapidamente o seu caso sem compromisso. Caso seja necessária uma análise documental profunda ou parecer jurídico detalhado, a consulta formal poderá ser cobrada, mas tudo é informado previamente."
},
{
q: "Quanto custa um processo?",
a: "Cada caso tem uma complexidade diferente. Meus honorários seguem a tabela ética da OAB e são acordados de forma clara antes de qualquer assinatura de contrato. Em muitos casos (trabalhista/previdenciário), o pagamento ocorre apenas no sucesso da ação."
},
{
q: "Como saberei o andamento do meu processo?",
a: "Você receberá atualizações periódicas por WhatsApp. Além disso, sempre que o juiz der uma movimentação importante, entrarei em contato para avisar."
}
];
return (
<section className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-3xl px-6 lg:px-8">
<FadeIn>
<h2 className="mb-12 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Perguntas Frequentes
</h2>
</FadeIn>
<div className="space-y-4">
{faqs.map((faq, i) => (
<FAQItem key={i} question={faq.q} answer={faq.a} delay={i * 0.1}/>
))}
</div>
</div>
</section>
);
}
function FAQItem({ question, answer, delay }: { question: string, answer: string, delay: number }) {
const [isOpen, setIsOpen] = useState(false);
return (
<FadeIn delay={delay}>
<div className="border border-gray-200 bg-white">
<button
className="flex w-full items-center justify-between px-6 py-4 text-left font-serif text-lg font-semibold text-brand-blue transition-colors hover:bg-gray-50"
onClick={() => setIsOpen(!isOpen)}
>
{question}
<span className="ml-6 flex shrink-0 text-brand-gold">
{isOpen ? <Minus className="h-5 w-5" /> : <Plus className="h-5 w-5" />}
</span>
</button>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="overflow-hidden"
>
<div className="px-6 pb-4 pt-2 text-gray-600">
{answer}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</FadeIn>
);
}
function FinalCTASection() {
return (
<section className="relative overflow-hidden bg-brand-blue py-24 sm:py-32">
<div className="absolute inset-0 z-0 opacity-10 bg-[url('https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
<div className="relative z-10 mx-auto max-w-4xl px-6 text-center lg:px-8">
<FadeIn>
<Scale className="mx-auto mb-6 h-12 w-12 text-brand-gold" />
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-white sm:text-5xl">
Precisa de orientação jurídica?
</h2>
<p className="mx-auto mb-10 max-w-2xl text-lg text-gray-300">
Não deixe para depois a proteção dos seus direitos e do seu patrimônio. Entre em contato agora e vamos encontrar a melhor solução para o seu caso.
</p>
<div className="flex flex-col gap-4 justify-center sm:flex-row">
<a
href="https://wa.me/5511999999999"
className="inline-flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue"
>
Falar pelo WhatsApp
</a>
<Link
to="/contato"
className="inline-flex items-center justify-center gap-2 border border-white/50 px-8 py-4 font-semibold text-white transition-colors hover:bg-white/10"
>
Enviar e-mail
</Link>
</div>
</FadeIn>
</div>
</section>
);
}

View file

@ -0,0 +1,75 @@
import React from 'react';
import { CheckCircle2 } from 'lucide-react';
import { motion } from 'motion/react';
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function Sobre() {
return (
<main className="w-full bg-white pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
<FadeIn>
<div className="relative">
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md">
<img
src="https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&q=80&w=800"
alt="Advogado João Silva em seu escritório"
className="h-full w-full object-cover grayscale-[10%]"
/>
</div>
<div className="absolute -right-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10"></div>
</div>
</FadeIn>
<FadeIn delay={0.2}>
<div className="max-w-xl">
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Dr. João Silva
</h1>
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
Especialista & Fundador
</p>
<div className="mb-8 space-y-4 text-lg text-gray-600">
<p>
Com mais de uma década de dedicação exclusiva à advocacia, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela transparência e proximidade.
</p>
<p>
Acredito que a advocacia de excelência é baseada na confiança mútua. Por isso, não sou apenas um intermediário em processos. Sou um parceiro estratégico focado em defender seus interesses. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, do início ao fim.
</p>
<p>
Minha abordagem é pragmática e voltada para a solução. Seja na esfera trabalhista, civil ou familiar, meu compromisso é garantir que os direitos dos meus clientes sejam integralmente respeitados, com ética e determinação.
</p>
</div>
<ul className="mb-10 space-y-4">
{[
"Formação em Direito pela Universidade de São Paulo (USP)",
"Pós-graduado em Direito Civil e Processo Civil",
"Atuação ética focada na resolução rápida de conflitos",
"Atendimento 100% humanizado e sem burocracias",
"Membro da Ordem dos Advogados do Brasil Seção São Paulo",
"Autor de diversos artigos jurídicos e palestras sobre direitos fundamentais"
].map((item, i) => (
<li key={i} className="flex items-start gap-3 text-gray-700">
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
<span>{item}</span>
</li>
))}
</ul>
</div>
</FadeIn>
</div>
</div>
</main>
);
}

26
Template-01/tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

View file

@ -0,0 +1,24 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});

20
Template-02/README.md Normal file
View file

@ -0,0 +1,20 @@
<div align="center">
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
</div>
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
View your app in AI Studio: https://ai.studio/apps/780e049e-cbd9-47bc-85b1-ae1175ffdb47
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

111
Template-02/index.html Normal file
View file

@ -0,0 +1,111 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Primary Meta Tags -->
<title>Dra. Laura Silva | Advocacia Humanizada e Estratégica</title>
<meta name="title" content="Dra. Laura Silva | Advocacia Humanizada e Estratégica">
<meta name="description" content="Atendimento jurídico humanizado e estratégico para mulheres e famílias em São Paulo. Referência em Direito de Família, Civil e Trabalhista.">
<meta name="keywords" content="advogada, advocacia para mulheres, advogada especialista, direito de família, direito civil, direito do consumidor, divórcio, advogada são paulo, consultoria jurídica para mulheres">
<!-- Open Graph / Facebook / WhatsApp (Card Compartilhado) -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://laurasilva.adv.br/">
<meta property="og:title" content="Dra. Laura Silva | Advocacia Humanizada">
<meta property="og:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito de Família, Civil e Trabalhista. Proteja seus direitos com quem entende.">
<meta property="og:image" content="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=1200&auto=format&fit=crop">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://laurasilva.adv.br/">
<meta property="twitter:title" content="Dra. Laura Silva | Advocacia Humanizada">
<meta property="twitter:description" content="Advocacia estratégica e humanizada. Atendimento direto e especializado em Direito de Família, Civil e Trabalhista. Proteja seus direitos com quem entende.">
<meta property="twitter:image" content="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=1200&auto=format&fit=crop">
<!-- Favicon (SVG) using pastel pink theme -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23DDA7A5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z'/><path d='M7 21h10'/><path d='M12 3v18'/><path d='M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2'/></svg>" />
<!-- Schema.org LegalService (Google Rich Content) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LegalService",
"name": "Dra. Laura Silva | Advogada Especialista",
"image": "https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=1200&auto=format&fit=crop",
"@id": "https://laurasilva.adv.br/",
"url": "https://laurasilva.adv.br/",
"telephone": "+5511999999999",
"address": {
"@type": "PostalAddress",
"streetAddress": "Av. Paulista, 1000 - Bela Vista",
"addressLocality": "São Paulo",
"addressRegion": "SP",
"postalCode": "01310-100",
"addressCountry": "BR"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": -23.561491,
"longitude": -46.655881
},
"priceRange": "$$",
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday"
],
"opens": "09:00",
"closes": "18:00"
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<!-- Theme Auto-Updater injected by autoblogia -->
<script>
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'UPDATE_APPEARANCE') {
const { primaryColor, backgroundColor, fontFamily } = event.data.settings;
const root = document.documentElement;
const style = root.style;
if (primaryColor) {
style.setProperty('--primary', primaryColor);
style.setProperty('--color-primary', primaryColor);
style.setProperty('--color-tech-primary', primaryColor);
style.setProperty('--color-seo-primary', primaryColor);
style.setProperty('--color-finance-primary', primaryColor);
style.setProperty('--color-recipe-primary', primaryColor);
style.setProperty('--color-health-primary', primaryColor);
style.setProperty('--color-corporate-primary', primaryColor);
}
if (backgroundColor) {
style.setProperty('--background', backgroundColor);
style.setProperty('--color-bg', backgroundColor);
style.setProperty('--color-tech-surface', backgroundColor);
style.setProperty('--color-seo-surface', backgroundColor);
style.backgroundColor = backgroundColor;
}
if (fontFamily) {
const fontString = '"' + fontFamily + '", sans-serif';
style.setProperty('--font-family', fontString);
style.setProperty('--font-sans', fontString);
style.fontFamily = fontString;
}
}
});
// Ping parent window that we are ready
window.parent.postMessage({ type: 'IFRAME_READY' }, '*');
</script>
</body>
</html>

View file

@ -0,0 +1,6 @@
{
"name": "Dra. Laura | Advocacia Humanizada",
"description": "Website e blog para advogada com design suave, elegante e feminino.",
"requestFramePermissions": [],
"majorCapabilities": []
}

4443
Template-02/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

38
Template-02/package.json Normal file
View file

@ -0,0 +1,38 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port=3000 --host=0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
"express": "^4.21.2",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"react-router-dom": "^7.15.0",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.3"
}
}

42
Template-02/src/App.tsx Normal file
View file

@ -0,0 +1,42 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { ScrollToTop } from './components/ScrollToTop';
import { Navbar } from './components/layout/Navbar';
import { Footer } from './components/layout/Footer';
import { WhatsAppButton } from './components/layout/WhatsAppButton';
import { CookieBanner } from './components/layout/CookieBanner';
import { Analytics } from './components/layout/Analytics';
import { Home } from './pages/Home';
import { Sobre } from './pages/Sobre';
import { Areas } from './pages/Areas';
import { ComoFunciona } from './pages/ComoFunciona';
import { Blog } from './pages/Blog';
import { BlogPost } from './pages/BlogPost';
import { Contact } from './pages/Contact';
export default function App() {
return (
<Router>
<ScrollToTop />
<Analytics />
<div className="flex min-h-screen flex-col font-sans text-gray-800 antialiased selection:bg-brand-gold selection:text-white">
<Navbar />
<div className="flex-1">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/sobre" element={<Sobre />} />
<Route path="/areas" element={<Areas />} />
<Route path="/funcionamento" element={<ComoFunciona />} />
<Route path="/blog" element={<Blog />} />
<Route path="/blog/:id" element={<BlogPost />} />
<Route path="/contato" element={<Contact />} />
</Routes>
</div>
<Footer />
<WhatsAppButton />
<CookieBanner />
</div>
</Router>
);
}

View file

@ -0,0 +1,16 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo({
top: 0,
left: 0,
behavior: 'instant',
});
}, [pathname]);
return null;
}

View file

@ -0,0 +1,85 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
declare global {
interface Window {
gtag?: (...args: any[]) => void;
dataLayer?: any[];
fbq?: (...args: any[]) => void;
_fbq?: any;
}
}
export function Analytics() {
const location = useLocation();
// Inicialização dos Scripts (Executado apenas uma vez)
useEffect(() => {
// 1. Google Analytics (GA4)
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
if (gaId && !document.getElementById('ga-script')) {
const script1 = document.createElement('script');
script1.async = true;
script1.src = `https://www.googletagmanager.com/gtag/js?id=${gaId}`;
script1.id = 'ga-script';
document.head.appendChild(script1);
const script2 = document.createElement('script');
script2.innerHTML = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}');
`;
document.head.appendChild(script2);
}
// 2. Facebook Pixel
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
if (fbPixelId && !document.getElementById('fb-pixel-script')) {
const script = document.createElement('script');
script.id = 'fb-pixel-script';
script.innerHTML = `
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '${fbPixelId}');
fbq('track', 'PageView');
`;
document.head.appendChild(script);
}
// 3. Google AdSense
const adsenseId = import.meta.env.VITE_ADSENSE_CLIENT_ID;
if (adsenseId && !document.getElementById('adsense-script')) {
const script = document.createElement('script');
script.id = 'adsense-script';
script.async = true;
script.src = `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adsenseId}`;
script.crossOrigin = 'anonymous';
document.head.appendChild(script);
}
}, []);
// Rastreamento de Mudança de Rota (Page Views)
useEffect(() => {
const gaId = import.meta.env.VITE_GA_MEASUREMENT_ID;
if (gaId && window.gtag) {
window.gtag('config', gaId, {
page_path: location.pathname + location.search,
});
}
const fbPixelId = import.meta.env.VITE_FB_PIXEL_ID;
if (fbPixelId && typeof window.fbq === 'function') {
window.fbq('track', 'PageView');
}
}, [location]);
return null; // Este componente não renderiza nada na tela
}

View file

@ -0,0 +1,69 @@
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { X } from 'lucide-react';
import { Link } from 'react-router-dom';
export function CookieBanner() {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
// Check if user has already accepted/declined
const consent = localStorage.getItem('lgpd_consent');
if (!consent) {
// Small delay so it doesn't pop up instantly on page load
const timer = setTimeout(() => setIsVisible(true), 1500);
return () => clearTimeout(timer);
}
}, []);
const handleConsent = (accepted: boolean) => {
localStorage.setItem('lgpd_consent', accepted ? 'accepted' : 'declined');
setIsVisible(false);
};
return (
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 100, opacity: 0 }}
className="fixed bottom-0 left-0 right-0 z-[60] bg-brand-blue p-4 shadow-2xl md:p-6"
>
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-4 md:flex-row">
<div className="text-sm text-gray-300 md:max-w-3xl">
<strong className="block text-white mb-1">Privacidade e LGPD</strong>
Utilizamos cookies para melhorar sua experiência em nosso site, personalizar conteúdo e analisar nosso tráfego.
Ao continuar navegando, você concorda com a nossa{' '}
<Link to="/contato" className="text-brand-gold hover:underline">
Política de Privacidade
</Link>.
</div>
<div className="flex w-full flex-shrink-0 flex-row gap-3 md:w-auto">
<button
onClick={() => handleConsent(false)}
className="flex-1 rounded-sm border border-white/30 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-white/10 md:flex-none"
>
Recusar
</button>
<button
onClick={() => handleConsent(true)}
className="flex-1 rounded-sm bg-brand-gold px-6 py-2 text-sm font-bold text-white transition-colors hover:bg-brand-gold-hover md:flex-none"
>
Aceitar
</button>
<button
onClick={() => handleConsent(false)}
className="hidden items-center justify-center text-gray-400 hover:text-white md:flex ml-2"
aria-label="Fechar"
>
<X className="h-5 w-5" />
</button>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
);
}

View file

@ -0,0 +1,75 @@
import React from 'react';
import { Scale, Mail, MapPin, Phone, Instagram, Linkedin } from 'lucide-react';
import { Link } from 'react-router-dom';
export function Footer() {
return (
<footer className="bg-brand-blue py-16 text-white text-opacity-80">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 gap-12 md:grid-cols-4 lg:grid-cols-5">
{/* Brand */}
<div className="lg:col-span-2">
<Link to="/" className="mb-6 flex items-center gap-2">
<Scale className="h-8 w-8 text-brand-gold" />
<div className="flex flex-col">
<span className="font-serif text-2xl font-bold leading-none tracking-tight text-white">
Dra. Laura
</span>
<span className="text-xs font-medium tracking-widest text-brand-gold">
ADVOCACIA HUMANIZADA
</span>
</div>
</Link>
<p className="mb-6 max-w-xs text-sm leading-relaxed">
Atendimento direto, acolhedor e estratégico focado na defesa dos seus direitos com sensibilidade e força.
</p>
<div className="flex items-center gap-4">
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
<Instagram className="h-4 w-4" />
</a>
<a href="#" className="flex h-10 w-10 items-center justify-center rounded-full border border-white/20 transition-colors hover:border-brand-gold hover:text-brand-gold">
<Linkedin className="h-4 w-4" />
</a>
</div>
</div>
{/* Links */}
<div>
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Navegação</h3>
<ul className="flex flex-col gap-3 text-sm">
<li><Link to="/sobre" className="hover:text-brand-gold transition-colors">Sobre a Advogada</Link></li>
<li><Link to="/areas" className="hover:text-brand-gold transition-colors">Áreas de Atuação</Link></li>
<li><Link to="/funcionamento" className="hover:text-brand-gold transition-colors">Como Funciona</Link></li>
<li><Link to="/blog" className="hover:text-brand-gold transition-colors">Blog Jurídico</Link></li>
<li><Link to="/contato" className="hover:text-brand-gold transition-colors">Contato</Link></li>
</ul>
</div>
{/* Contact */}
<div className="lg:col-span-2">
<h3 className="mb-6 font-serif text-lg font-semibold text-white">Contato Direto</h3>
<ul className="flex flex-col gap-4 text-sm">
<li className="flex items-start gap-3">
<Phone className="h-5 w-5 shrink-0 text-brand-gold" />
<span>+55 (11) 99999-9999</span>
</li>
<li className="flex items-start gap-3">
<Mail className="h-5 w-5 shrink-0 text-brand-gold" />
<span>contato@laurasilva.adv.br</span>
</li>
<li className="flex items-start gap-3">
<MapPin className="h-5 w-5 shrink-0 text-brand-gold" />
<span>Av. Paulista, 1000 - Bela Vista<br />São Paulo - SP, 01310-100</span>
</li>
</ul>
</div>
</div>
<div className="mt-16 flex flex-col items-center justify-between border-t border-white/10 pt-8 text-xs sm:flex-row">
<p>© {new Date().getFullYear()} Laura Silva Advocacia. Todos os direitos reservados.</p>
<p className="mt-2 sm:mt-0">OAB/SP 123.456</p>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,161 @@
import React, { useState, useEffect } from 'react';
import { Menu, X, Scale, Search } from 'lucide-react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { cn } from '../../lib/utils';
export function Navbar() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 10);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Handle hash scrolling when navigating across pages
useEffect(() => {
if (location.hash) {
const element = document.querySelector(location.hash);
if (element) {
setTimeout(() => {
element.scrollIntoView({ behavior: 'smooth' });
}, 100);
}
} else {
window.scrollTo(0,0);
}
}, [location]);
const handleNavClick = (e: React.MouseEvent<HTMLAnchorElement>, href: string) => {
setIsMobileMenuOpen(false);
if (href.startsWith('/#')) {
const hash = href.substring(1);
if (location.pathname === '/') {
e.preventDefault();
const element = document.querySelector(hash);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
// Update URL hash without jumping
window.history.pushState(null, '', hash);
}
} else {
// Let it navigate to /#hash normally
}
}
};
const navLinks = [
{ name: 'Sobre', href: '/sobre' },
{ name: 'Áreas de Atuação', href: '/areas' },
{ name: 'Como Funciona', href: '/funcionamento' },
{ name: 'Blog', href: '/blog' },
{ name: 'Contato', href: '/contato' },
];
return (
<nav
className={cn(
'fixed left-0 right-0 top-0 z-50 transition-all duration-300',
isScrolled
? 'bg-white shadow-md py-3'
: 'bg-brand-blue py-5 text-white'
)}
>
<div className="mx-auto flex max-w-7xl items-center justify-between px-6 lg:px-8">
<Link to="/" className="flex items-center gap-2">
<Scale className={cn("h-8 w-8", isScrolled ? "text-brand-gold" : "text-brand-gold")} />
<div className="flex flex-col">
<span className={cn("font-serif text-xl font-bold leading-none tracking-tight", isScrolled ? "text-brand-blue" : "text-white")}>
Dra. Laura
</span>
<span className={cn("text-xs font-medium tracking-widest", isScrolled ? "text-gray-500" : "text-gray-300")}>
ADVOGADA
</span>
</div>
</Link>
{/* Desktop Nav */}
<div className="hidden items-center gap-8 md:flex">
{navLinks.map((link) => (
<Link
key={link.name}
to={link.href}
onClick={(e) => handleNavClick(e, link.href)}
className={cn(
'text-sm font-medium transition-colors hover:text-brand-gold',
isScrolled ? 'text-gray-700' : 'text-gray-200'
)}
>
{link.name}
</Link>
))}
<div className="flex items-center gap-6 border-l border-white/20 pl-6">
<Link
to="/blog?focus=true"
title="Buscar Artigos"
className={cn(
'transition-colors hover:text-brand-gold',
isScrolled ? 'text-brand-blue' : 'text-white'
)}
>
<Search className="h-5 w-5" />
</Link>
<Link
to="/contato"
className="rounded-none border-2 border-brand-gold bg-brand-gold px-6 py-2 text-sm font-semibold text-white transition-colors hover:bg-transparent hover:text-brand-gold"
>
Consulta Online
</Link>
</div>
</div>
{/* Mobile Nav Toggle */}
<button
className="md:hidden"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? (
<X className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
) : (
<Menu className={cn("h-6 w-6", isScrolled ? "text-brand-blue" : "text-white")} />
)}
</button>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="absolute left-0 right-0 top-full flex flex-col items-center gap-6 bg-white py-8 shadow-xl md:hidden">
{navLinks.map((link) => (
<Link
key={link.name}
to={link.href}
onClick={(e) => handleNavClick(e, link.href)}
className="text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
>
{link.name}
</Link>
))}
<Link
to="/blog?focus=true"
onClick={() => setIsMobileMenuOpen(false)}
className="flex items-center gap-2 text-base font-medium text-gray-800 transition-colors hover:text-brand-gold"
>
<Search className="h-5 w-5" /> Buscar Artigo
</Link>
<Link
to="/contato"
onClick={() => setIsMobileMenuOpen(false)}
className="mt-4 rounded-none border-2 border-brand-gold bg-brand-gold px-8 py-3 text-base font-semibold text-white transition-colors hover:bg-white hover:text-brand-gold"
>
Consulta Online
</Link>
</div>
)}
</nav>
);
}

View file

@ -0,0 +1,18 @@
import { Phone } from 'lucide-react';
export function WhatsAppButton() {
return (
<div className="fixed bottom-6 right-6 z-50">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
<a
href="https://wa.me/5511999999999?text=Ol%C3%A1%2C%20gostaria%20de%20agendar%20uma%20consulta%20jur%C3%ADdica."
target="_blank"
rel="noopener noreferrer"
className="relative flex h-14 w-14 items-center justify-center rounded-full bg-green-500 text-white shadow-lg transition-transform hover:scale-110 hover:shadow-xl hover:bg-green-600 focus:outline-none"
aria-label="Falar pelo WhatsApp"
>
<Phone className="h-7 w-7" fill="currentColor" />
</a>
</div>
);
}

View file

@ -0,0 +1,113 @@
export interface BlogPostData {
id: number;
title: string;
excerpt: string;
category: string;
date: string;
imageUrl: string;
content: string;
}
const generateMockPosts = (): BlogPostData[] => {
const categories = [
"Direito Trabalhista", "Direito de Família", "Direito do Consumidor",
"Direito Civil", "Contratos & Acordos", "Planejamento Sucessório"
];
const titles = [
"Direitos do Trabalhador: O que você precisa saber antes de pedir demissão",
"Revisão da Vida Toda: Entenda se você tem direito a receber os atrasados",
"Voo Cancelado: Quais são os seus direitos como consumidor?",
"Guarda Compartilhada vs Guarda Unilateral: Entendendo as diferenças",
"Contratos de Aluguel: Direitos e Deveres do Inquilino",
"Responsabilidade Civil de Sócios: Até onde vai o risco empresarial?",
"Atraso na Entrega de Imóveis: Direitos e Indenizações",
"Pensão Alimentícia: Como é feito o cálculo?",
"Assédio Moral no Trabalho: Como identificar e reunir provas",
"Golpes no Pix e a Responsabilidade dos Bancos",
"União Estável vs Casamento: Regimes de Bens",
"LGPD nas Pequenas Empresas: O que é preciso adequar?",
"Demissão por Justa Causa: Quando se aplica?",
"Auxílio-Doença Negado pelo INSS: O que fazer?",
"Cobranças Indevidas: Como agir e pedir reparação",
"Inventário Extrajudicial: Requisitos e Prazos",
"Horas Extras: Direitos, Limites e Reflexos",
"Aposentadoria Especial: Exigências e Comprovação",
"Direito de Arrependimento nas Compras Online",
"Alienação Parental: Consequências Jurídicas",
"Direitos Autorais na Era Digital: Protegendo suas Criações"
];
const images = [
"https://images.unsplash.com/photo-1589829085413-56de8ae18c73",
"https://images.unsplash.com/photo-1454165804606-c3d57bc86b40",
"https://images.unsplash.com/photo-1507679799987-c73779587ccf",
"https://images.unsplash.com/photo-1503694978374-8a2fa686963a",
"https://images.unsplash.com/photo-1554224155-6726b3ff858f",
"https://images.unsplash.com/photo-1600880292203-757bb62b4baf",
"https://images.unsplash.com/photo-1524813686514-a57563d77965",
"https://images.unsplash.com/photo-1589391886645-d51941baf7fb",
"https://images.unsplash.com/photo-1450101499163-c8848c66cb85",
"https://images.unsplash.com/photo-1629904853716-f0bc54eea481",
"https://images.unsplash.com/photo-1517486808906-6ca8b3f04846",
"https://images.unsplash.com/photo-1505373877841-8d25f7d46678",
"https://images.unsplash.com/photo-1593642532400-2682810df593",
"https://images.unsplash.com/photo-1436491865332-7a61a109cc05",
"https://images.unsplash.com/photo-1502086223501-7ea6ecd79368",
"https://images.unsplash.com/photo-1521791136064-7986c2920216",
"https://images.unsplash.com/photo-1573164713988-8665fc963095",
"https://images.unsplash.com/photo-1536640712-4d4c36ef0e2c",
"https://images.unsplash.com/photo-1523240795612-9a054b0db644",
"https://images.unsplash.com/photo-1494526585095-c41746248156",
"https://images.unsplash.com/photo-1486406146926-c627a92ad1ab",
];
return titles.map((title, index) => {
const category = categories[index % categories.length];
return {
id: index + 1,
title: title,
excerpt: `Entenda os principais aspectos legais, a jurisprudência atualizada e como garantir seus direitos em relação a ${title.split(':')[0].toLowerCase()}.`,
category: category,
date: `0${(21 - index) % 9 + 1} Mar 2024`,
imageUrl: `${images[index]}?auto=format&fit=crop&q=80&w=800`,
content: `
<p>A área de <strong>${category}</strong> apresenta constantes desafios judiciais e extrajudiciais. O tema <em>"${title}"</em> tem sido objeto de grande debate nos tribunais superiores do Brasil.</p>
<h2>Contexto e Aplicação</h2>
<p>Diante das recentes mudanças legislativas, é fundamental entender seus impactos práticos. O Superior Tribunal de Justiça (STJ) tem firmado entendimentos importantes que afetam diretamente as relações cotidianas.</p>
<blockquote>
<p>"A proteção dos direitos fundamentais exige do operador do direito uma visão sistêmica e atualizada da jurisprudência." - Doutrina Jurídica Contemporânea</p>
</blockquote>
<p>Entre os principais pontos a serem observados na prática, destacamos:</p>
<ul>
<li>Análise preventiva de riscos e <em>compliance</em>.</li>
<li>Atuação extrajudicial para resolução pacífica de conflitos.</li>
<li>Utilização das novas teses firmadas em recursos repetitivos (Temas do STJ e STF).</li>
</ul>
<h2>Exemplo Prático e Decisões Recentes</h2>
<p>Imagine o seguinte cenário: um cliente procura auxílio relatando um problema complexo envolvendo direitos não reconhecidos na esfera pertinente ao caso. A abordagem ideal combina a análise documental detalhada com a jurisprudência mais recente.</p>
<figure>
<img src="${images[index]}?auto=format&fit=crop&q=80&w=800" alt="${title}" style="border-radius: 8px; margin: 20px 0; width: 100%; aspect-ratio: 16/9; object-fit: cover;" />
<figcaption style="text-align: center; font-size: 0.9em; color: gray;">Reflexões e desdobramentos práticos na defesa dos direitos.</figcaption>
</figure>
<h2>Como agir diante desta situação?</h2>
<p>O primeiro passo é reunir todas as provas materiais possíveis: documentos, trocas de e-mails, conversas de WhatsApp, fotografias ou qualquer registro formal da relação firmada entre as partes.</p>
<p>Após reunir a documentação, é vital buscar orientação técnica <strong>antes de assinar qualquer acordo ou recibo de quitação</strong>. Em muitos casos, a aceitação de termos genéricos pode inviabilizar a busca por uma reparação total no futuro.</p>
<p>Portanto, para mais informações e para análise pormenorizada do seu caso concreto, certifique-se de contar com assessoria jurídica especializada e combativa. <a href="/contato">Agende uma consulta conosco</a> para entender as particularidades da sua situação.</p>
`
};
});
};
export const MOCK_POSTSList = generateMockPosts();
export const MOCK_POSTS: Record<string, BlogPostData> = MOCK_POSTSList.reduce((acc, curr) => ({
...acc,
[curr.id.toString()]: curr
}), {});

37
Template-02/src/index.css Normal file
View file

@ -0,0 +1,37 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap');
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-serif: "Playfair Display", ui-serif, Georgia, serif;
--color-brand-blue: #3E3636;
--color-brand-blue-light: #524747;
--color-brand-gold: #DDA7A5;
--color-brand-gold-hover: #C28F8D;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-sans);
background-color: #FDFAFA; /* Very subtle warm white/pink */
}
/* Modern clean scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #FCF9F9;
}
::-webkit-scrollbar-thumb {
background: #3E3636;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #DDA7A5;
}

View file

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

10
Template-02/src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

View file

@ -0,0 +1,92 @@
import React from 'react';
import { Users, ShieldCheck, Scale, FileText, Home as HomeIcon, ArrowRight } from 'lucide-react';
import { motion } from 'motion/react';
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function Areas() {
const areas = [
{
title: "Direito de Família",
icon: Users,
desc: "Soluções acolhedoras com a sensibilidade que o caso exige. Orientação em divórcios consensuais ou litigiosos, fixação e revisão de pensão alimentícia, guarda, investigação de paternidade e inventários judiciais ou extrajudiciais."
},
{
title: "Direito Civil",
icon: Scale,
desc: "Proteção do seu patrimônio e interesses civis. Elaboração e revisão de contratos, ações de indenizações por danos morais e materiais, responsabilidade civil, regularização de propriedades e processos de cobranças diversas para recuperação de crédito."
},
{
title: "Direito Trabalhista",
icon: Users,
desc: "Defesa dos seus direitos no ambiente de trabalho. Atuamos com rescisões indiretas, cobrança de horas extras, combate ao assédio moral, reconhecimento de vínculo e proteção à gestante."
},
{
title: "Direito do Consumidor",
icon: FileText,
desc: "Defesa firme contra abusos de fornecedores e planos de saúde. Atuamos em casos de negativações indevidas (SPC/Serasa), falhas em prestação de serviços, recusas abusivas de convênios médicos e defeitos não solucionados."
},
{
title: "Contratos & Acordos",
icon: ShieldCheck,
desc: "Segurança total em transações, negócios e parcerias. Assessoramos na confecção e revisão de contratos particulares, locação, distratos, acordos extrajudiciais e documentações essenciais para proteção mútua."
},
{
title: "Planejamento Sucessório",
icon: HomeIcon,
desc: "Organização do patrimônio com visão de futuro. Auxílio em testamentos, doações em vida, união estável (pacto antenupcial) e arranjos patrimoniais para evitar conflitos familiares futuros."
}
];
return (
<main className="w-full bg-[#f9fafb] pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mb-16 text-center">
<FadeIn>
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl sm:text-5xl">
Áreas de Atuação
</h1>
<p className="mx-auto max-w-2xl text-lg text-gray-600">
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
</p>
</FadeIn>
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{areas.map((area, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
<div>
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-brand-gold/10 text-brand-gold transition-colors group-hover:bg-brand-gold group-hover:text-white rounded-full">
<area.icon className="h-6 w-6" />
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
{area.title}
</h3>
<p className="mb-8 text-gray-600 leading-relaxed text-sm">
{area.desc}
</p>
</div>
<a
href="https://wa.me/5511999999999"
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
>
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
</a>
</div>
</FadeIn>
))}
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,120 @@
import React, { useState, useRef, useEffect } from 'react';
import { ArrowLeft, Search, Calendar, User } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
import { motion } from 'motion/react';
import { MOCK_POSTSList } from '../data/posts';
export function Blog() {
const [activeCategory, setActiveCategory] = useState("Todos");
const [searchQuery, setSearchQuery] = useState("");
const location = useLocation();
const searchInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (location.state?.focusSearch || location.search.includes('focus=true')) {
setTimeout(() => {
searchInputRef.current?.focus();
}, 100);
}
}, [location]);
const filteredPosts = MOCK_POSTSList.filter(post => {
const matchesCategory = activeCategory === "Todos" || post.category === activeCategory;
const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
post.excerpt.toLowerCase().includes(searchQuery.toLowerCase());
return matchesCategory && matchesSearch;
});
const categories = ["Todos", ...Array.from(new Set(MOCK_POSTSList.map(p => p.category)))];
return (
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
{/* Header */}
<div className="mb-16">
<Link to="/" className="mb-6 inline-flex items-center gap-2 text-sm text-brand-gold hover:underline">
<ArrowLeft className="h-4 w-4" /> Voltar para o início
</Link>
<div className="flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
<div>
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
Blog Jurídico
</h1>
<p className="max-w-xl text-lg text-gray-600">
Artigos desmistificando o direito de forma clara. Saiba como se proteger nas mais diversas situações do dia a dia.
</p>
</div>
{/* Search */}
<div className="relative w-full max-w-md">
<input
ref={searchInputRef}
type="text"
placeholder="Buscar palavra-chave..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full border-b-2 border-gray-200 bg-transparent py-2 pl-2 pr-10 text-brand-blue outline-none transition-colors focus:border-brand-gold"
/>
<Search className="absolute right-2 top-2 h-5 w-5 text-gray-400" />
</div>
</div>
</div>
{/* Categories (Mock) */}
<div className="mb-12 flex flex-wrap gap-3">
{categories.map(cat => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={`rounded-full px-5 py-2 text-sm font-medium transition-colors ${cat === activeCategory ? 'bg-brand-blue text-white' : 'bg-white border border-gray-200 text-gray-700 hover:border-brand-gold hover:text-brand-gold'}`}
>
{cat}
</button>
))}
</div>
{/* Posts Grid */}
<div className="grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-3">
{filteredPosts.map((post, index) => (
<motion.article
key={post.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="group flex cursor-pointer flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
>
<div className="aspect-[16/10] w-full overflow-hidden">
<img
src={post.imageUrl}
alt={post.title}
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
/>
</div>
<div className="flex flex-1 flex-col p-6">
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
{post.category}
</span>
<Link to={`/blog/${post.id}`} className="mb-3 font-serif text-xl font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
{post.title}
</Link>
<p className="mb-6 flex-1 text-sm text-gray-600 line-clamp-3">
{post.excerpt}
</p>
<div className="mt-auto flex items-center justify-between border-t border-gray-100 pt-4 text-xs text-gray-500">
<div className="flex items-center gap-2">
<User className="h-3 w-3" /> Dra. Laura Silva
</div>
<div className="flex items-center gap-2">
<Calendar className="h-3 w-3" /> {post.date}
</div>
</div>
</div>
</motion.article>
))}
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,212 @@
import React, { useEffect, useState } from 'react';
import { ArrowLeft, User, Calendar, Share2, Copy, Check, MessageCircle, Linkedin, Facebook, Twitter, X } from 'lucide-react';
import { Link, useParams } from 'react-router-dom';
import { motion, AnimatePresence } from 'motion/react';
import { MOCK_POSTS, MOCK_POSTSList } from '../data/posts';
export function BlogPost() {
const { id } = useParams();
const post = id ? MOCK_POSTS[id] : null;
const [showShareMenu, setShowShareMenu] = useState(false);
const [copied, setCopied] = useState(false);
useEffect(() => {
window.scrollTo({ top: 0, behavior: 'instant' });
}, [id]);
if (!post) {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-50 pt-20">
<div className="text-center">
<h2 className="mb-4 text-2xl font-bold text-brand-blue">Artigo não encontrado</h2>
<Link to="/blog" className="text-brand-gold hover:underline">
Voltar para o Blog
</Link>
</div>
</div>
);
}
const url = window.location.href;
const encodedUrl = encodeURIComponent(url);
const encodedTitle = encodeURIComponent(post.title);
const copyToClipboard = () => {
navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const shareLinks = [
{
name: 'WhatsApp',
icon: MessageCircle,
href: `https://api.whatsapp.com/send?text=${encodedTitle}%20${encodedUrl}`,
color: 'hover:text-green-500 hover:bg-green-50'
},
{
name: 'LinkedIn',
icon: Linkedin,
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
color: 'hover:text-blue-600 hover:bg-blue-50'
},
{
name: 'Facebook',
icon: Facebook,
href: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
color: 'hover:text-blue-500 hover:bg-blue-50'
},
{
name: 'X (Twitter)',
icon: Twitter,
href: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}`,
color: 'hover:text-gray-900 hover:bg-gray-100'
}
];
const relatedPosts = MOCK_POSTSList.filter(p => p.category === post.category && p.id !== post.id).slice(0, 3);
if (relatedPosts.length < 3) {
const morePosts = MOCK_POSTSList.filter(p => !relatedPosts.includes(p) && p.id !== post.id);
relatedPosts.push(...morePosts.slice(0, 3 - relatedPosts.length));
}
return (
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
<article className="mx-auto max-w-4xl px-6 lg:px-8">
{/* ... (rest of the content is unchanged) */}
<Link to="/blog" className="mb-10 inline-flex items-center gap-2 text-sm text-gray-500 transition-colors hover:text-brand-gold">
<ArrowLeft className="h-4 w-4" /> Todos os artigos
</Link>
{/* Header */}
<header className="mb-12 text-center">
<span className="mb-4 inline-block bg-brand-blue px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand-gold">
{post.category}
</span>
<h1 className="mb-6 font-serif text-3xl font-bold leading-tight text-brand-blue md:text-5xl">
{post.title}
</h1>
<div className="flex flex-wrap items-center justify-center gap-6 border-b border-gray-200 pb-8 text-sm text-gray-500">
<div className="flex items-center gap-2">
<User className="h-4 w-4" /> Dra. Laura Silva
</div>
<div className="flex items-center gap-2">
<Calendar className="h-4 w-4" /> {post.date}
</div>
<button
onClick={copyToClipboard}
className={`flex items-center gap-2 transition-colors ${copied ? 'text-green-600' : 'hover:text-brand-blue'}`}
>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
{copied ? 'Copiado!' : 'Copiar Link'}
</button>
<div className="relative">
<button
onClick={() => setShowShareMenu(!showShareMenu)}
className="flex items-center gap-2 hover:text-brand-blue transition-colors"
>
{showShareMenu ? <X className="h-4 w-4" /> : <Share2 className="h-4 w-4" />}
Compartilhar
</button>
<AnimatePresence>
{showShareMenu && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ duration: 0.15 }}
className="absolute right-1/2 top-full mt-2 w-48 translate-x-1/2 sm:translate-x-0 sm:right-0 sm:left-auto rounded-lg border border-gray-100 bg-white p-2 shadow-xl z-10"
>
<div className="flex flex-col text-left">
{shareLinks.map((link) => (
<a
key={link.name}
href={link.href}
target="_blank"
rel="noopener noreferrer"
className={`flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-gray-700 transition-colors ${link.color}`}
onClick={() => setShowShareMenu(false)}
>
<link.icon className="h-4 w-4" /> {link.name}
</a>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</header>
{/* Featured Image */}
<motion.div
initial={{ opacity: 0, scale: 0.98 }}
animate={{ opacity: 1, scale: 1 }}
className="mb-14 overflow-hidden rounded-sm"
>
<img
src={post.imageUrl}
alt={post.title}
className="aspect-[2/1] w-full object-cover"
/>
</motion.div>
{/* Article Body */}
<div className="mx-auto max-w-3xl prose prose-blue prose-lg prose-headings:font-serif prose-headings:text-brand-blue prose-a:text-brand-gold prose-a:no-underline hover:prose-a:underline">
{/* Render HTML content safely since it's hardcoded mock, but natively prefer Markdown if CMS */}
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</div>
{/* CTA Section */}
<div className="mx-auto mt-20 max-w-3xl bg-brand-blue p-8 text-center sm:p-12 mb-20">
<h3 className="mb-4 font-serif text-2xl font-bold text-white">Este conteúdo ajudou em sua situação?</h3>
<p className="mb-8 text-gray-300">Cada caso demanda uma análise específica. Caso esteja passando por esta situação, avaliarei o seu cenário de forma técnica e sigilosa.</p>
<Link to="/contato" className="inline-block bg-brand-gold px-8 py-3 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue">
Solicitar Análise de Caso
</Link>
</div>
{/* Artigos Relacionados */}
{relatedPosts.length > 0 && (
<div className="mx-auto max-w-5xl border-t border-gray-100 pt-16">
<h3 className="mb-8 font-serif text-2xl font-bold text-brand-blue text-center">
Você também pode se interessar por:
</h3>
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
{relatedPosts.map((relatedPost, index) => (
<motion.article
key={relatedPost.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="group flex flex-col overflow-hidden bg-white shadow-sm border border-gray-100 transition-all hover:-translate-y-1 hover:shadow-xl hover:border-brand-gold/30"
>
<Link to={`/blog/${relatedPost.id}`} className="aspect-[16/10] w-full overflow-hidden block">
<img
src={relatedPost.imageUrl}
alt={relatedPost.title}
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
/>
</Link>
<div className="flex flex-1 flex-col p-6">
<span className="mb-3 text-xs font-bold uppercase tracking-wider text-brand-gold">
{relatedPost.category}
</span>
<Link to={`/blog/${relatedPost.id}`} className="font-serif text-lg font-bold leading-snug text-brand-blue group-hover:text-brand-gold-hover">
{relatedPost.title}
</Link>
</div>
</motion.article>
))}
</div>
</div>
)}
</article>
</main>
);
}

View file

@ -0,0 +1,96 @@
import React from 'react';
import { MessageCircle } from 'lucide-react';
import { Link } from 'react-router-dom';
import { motion } from 'motion/react';
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function ComoFunciona() {
const steps = [
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu. Não há barreiras burocráticas no primeiro contato." },
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso, informando as chances de êxito." },
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, os custos de forma totalmente transparente e enviamos o termo de prestação de serviços." },
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado ativamente em cada etapa e movimentação chave do processo." }
];
const points = [
{ title: "Atendimento Direto", desc: "Você fala diretamente com a advogada responsável pelo caso, sem secretárias intermediando." },
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples e acolhedora." },
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
];
return (
<main className="w-full bg-white pb-24 pt-32">
<section className="bg-brand-blue py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<FadeIn>
<h1 className="mb-6 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-5xl">
Como funciona o acompanhamento
</h1>
<p className="mx-auto mb-16 max-w-2xl text-center text-lg text-gray-300">
Minha prioridade é oferecer um método de atendimento moderno, simplificado e altamente eficiente.
</p>
</FadeIn>
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[15px] z-0"></div>
{steps.map((step, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="relative z-10 flex flex-col items-center text-center">
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
{step.num}
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
<p className="text-gray-300 text-sm leading-relaxed">{step.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</section>
<section className="py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
<FadeIn>
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Por que me escolher para defender você?
</h2>
<p className="mb-10 text-lg text-gray-600 text-balance">
Acredito que a advocacia moderna não precisa ser lenta, cheia de protocolos e distante do cliente. Meu modelo de atuação é focado no resultado e na tranquilidade do cliente.
</p>
<Link
to="/contato"
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
>
<MessageCircle className="h-5 w-5" /> Agendar Consulta
</Link>
</FadeIn>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
{points.map((point, i) => (
<FadeIn key={i} delay={i * 0.1}>
<div className="border-l-4 border-brand-gold pl-6">
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</div>
</section>
</main>
);
}

View file

@ -0,0 +1,216 @@
import React, { useState, useEffect } from 'react';
import { motion } from 'motion/react';
import { MapPin, Phone, Mail, Clock, Send, CheckCircle2 } from 'lucide-react';
import { Link } from 'react-router-dom';
export function Contact() {
const [formState, setFormState] = useState<'idle' | 'submitting' | 'success'>('idle');
useEffect(() => {
window.scrollTo(0, 0);
}, []);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setFormState('submitting');
// Simulate form submission
setTimeout(() => {
setFormState('success');
}, 1500);
};
return (
<main className="w-full bg-[#fcfcfc] pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
{/* Header */}
<div className="mb-16 text-center">
<h1 className="mb-4 font-serif text-4xl font-bold tracking-tight text-brand-blue md:text-5xl">
Entre em Contato
</h1>
<p className="mx-auto max-w-2xl text-lg text-gray-600">
Estamos prontos para ouvir você e encontrar a melhor solução jurídica para o seu caso. Escolha a forma de contato de sua preferência.
</p>
</div>
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2">
{/* Informações de Contato */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="flex flex-col justify-between"
>
<div>
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Informações de Atendimento</h2>
<div className="mb-12 space-y-8">
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<Phone className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">WhatsApp & Telefone</h3>
<p className="text-gray-600">+55 (11) 99999-9999</p>
<p className="text-gray-600">Atendimento prioritário via WhatsApp</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<Mail className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">E-mail</h3>
<p className="text-gray-600">contato@laurasilva.adv.br</p>
<p className="text-gray-600">Para envio de documentos e propostas</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<MapPin className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Escritório Físico</h3>
<p className="text-gray-600">Av. Paulista, 1000 - Bela Vista</p>
<p className="text-gray-600">São Paulo - SP, 01310-100</p>
<p className="mt-1 text-sm text-brand-gold">Atendimento presencial apenas com agendamento.</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center bg-brand-blue text-brand-gold">
<Clock className="h-6 w-6" />
</div>
<div>
<h3 className="mb-1 font-serif text-lg font-bold text-brand-blue">Horário de Funcionamento</h3>
<p className="text-gray-600">Segunda a Sexta: 09h às 18h</p>
<p className="text-gray-600">Plantão para urgências criminais 24h</p>
</div>
</div>
</div>
</div>
{/* Map (Placeholder using generic image or iframe) */}
<div className="aspect-[16/9] w-full overflow-hidden bg-gray-100 filter grayscale">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3657.1973618684784!2d-46.658097023769186!3d-23.56134956158784!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x94ce59c8da0aa315%3A0xd59f9431f2c9776a!2sAv.%20Paulista%2C%201000%20-%20Bela%20Vista%2C%20S%C3%A3o%20Paulo%20-%20SP%2C%2001310-100!5e0!3m2!1spt-BR!2sbr!4v1714571987515!5m2!1spt-BR!2sbr"
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen={false}
loading="lazy"
referrerPolicy="no-referrer-when-downgrade">
</iframe>
</div>
</motion.div>
{/* Formulário de Contato */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="flex flex-col justify-center bg-white p-8 shadow-xl md:p-12 border border-gray-100"
>
<h2 className="mb-8 font-serif text-2xl font-bold text-brand-blue">Envie sua Mensagem</h2>
{formState === 'success' ? (
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="flex flex-col items-center justify-center p-8 text-center"
>
<CheckCircle2 className="mb-4 h-16 w-16 text-green-500" />
<h3 className="mb-2 font-serif text-xl font-bold text-brand-blue">Mensagem Enviada!</h3>
<p className="mb-8 text-gray-600">Recebemos seu contato. Retornaremos o mais breve possível para o e-mail ou telefone informado.</p>
<button
onClick={() => setFormState('idle')}
className="bg-brand-blue px-6 py-3 font-semibold text-white transition-colors hover:bg-brand-gold"
>
Enviar Nova Mensagem
</button>
</motion.div>
) : (
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
<div className="flex flex-col gap-2">
<label htmlFor="name" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Seu Nome Completo</label>
<input
type="text"
id="name"
required
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="Ex: Carlos Silva"
/>
</div>
<div className="flex flex-col gap-6 md:flex-row">
<div className="flex w-full flex-col gap-2">
<label htmlFor="email" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">E-mail</label>
<input
type="email"
id="email"
required
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="seu@email.com"
/>
</div>
<div className="flex w-full flex-col gap-2">
<label htmlFor="phone" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Telefone / WhatsApp</label>
<input
type="tel"
id="phone"
required
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="(11) 99999-9999"
/>
</div>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="area" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Área de Interesse (Opcional)</label>
<select
id="area"
className="border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold text-gray-600"
>
<option value="">Selecione a área...</option>
<option value="trabalhista">Direito Trabalhista</option>
<option value="previdenciario">Direito Previdenciário</option>
<option value="civil">Direito Civil</option>
<option value="familia">Direito de Família</option>
<option value="consumidor">Direito do Consumidor</option>
<option value="outros">Outros</option>
</select>
</div>
<div className="flex flex-col gap-2">
<label htmlFor="message" className="text-sm font-semibold text-brand-blue uppercase tracking-wider">Resumo do Caso</label>
<textarea
id="message"
rows={4}
required
className="resize-none border-b-2 border-gray-200 bg-transparent py-2 outline-none transition-colors focus:border-brand-gold"
placeholder="Descreva brevemente o que aconteceu..."
></textarea>
</div>
<button
type="submit"
disabled={formState === 'submitting'}
className="mt-4 flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-all hover:bg-brand-blue disabled:opacity-70 disabled:hover:bg-brand-gold"
>
{formState === 'submitting' ? 'Enviando...' : (
<>
Enviar Mensagem <Send className="h-4 w-4" />
</>
)}
</button>
<p className="text-xs text-gray-500 text-center">
Garantimos sigilo absoluto sobre todas as informações prestadas neste formulário.
</p>
</form>
)}
</motion.div>
</div>
</div>
</main>
);
}

View file

@ -0,0 +1,471 @@
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { Link } from 'react-router-dom';
import {
ArrowRight, ShieldCheck, Scale, FileText,
Users, Building, Home as HomeIcon, CheckCircle2,
MessageCircle, Plus, Minus
} from 'lucide-react';
import { cn } from '../lib/utils';
// Reusable animated container
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function Home() {
return (
<main className="w-full">
<HeroSection />
<AboutSection />
<PracticeAreasSection />
<HowItWorksSection />
<DifferentiatorsSection />
<TestimonialsSection />
<FAQSection />
<FinalCTASection />
</main>
);
}
function HeroSection() {
return (
<section className="relative flex min-h-[90vh] items-center bg-brand-blue pt-20">
{/* Background Pattern / Overlay */}
<div className="absolute inset-0 z-0 opacity-20 bg-[url('https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
<div className="relative z-10 mx-auto grid w-full max-w-7xl grid-cols-1 gap-12 px-6 lg:grid-cols-2 lg:px-8">
<motion.div
className="flex flex-col justify-center pt-10 pb-16 lg:py-24"
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
>
<div className="mb-6 flex items-center gap-4">
<span className="h-[1px] w-12 bg-brand-gold"></span>
<span className="text-sm font-semibold tracking-widest text-brand-gold uppercase">
OAB/SP 123.456
</span>
</div>
<h1 className="mb-6 font-serif text-4xl leading-tight font-bold text-white md:text-5xl lg:text-6xl">
Advocacia estratégica e <span className="text-brand-gold">atendimento direto</span> para proteger seus direitos.
</h1>
<p className="mb-10 max-w-lg text-lg text-gray-300 leading-relaxed">
Atendimento humanizado, sem intermediários. Foco em soluções ágeis e máxima segurança jurídica para você e seu patrimônio.
</p>
<div className="flex flex-col gap-4 sm:flex-row">
<a
href="https://wa.me/5511999999999"
target="_blank"
rel="noreferrer"
className="flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 text-center font-semibold text-white transition-all hover:bg-brand-gold-hover hover:-translate-y-1"
>
Falar com a Advogada <ArrowRight className="h-5 w-5" />
</a>
<Link
to="/areas"
className="flex items-center justify-center gap-2 border border-white/30 px-8 py-4 text-center font-semibold text-white backdrop-blur-sm transition-all hover:bg-white hover:text-brand-blue"
>
Conheça as Áreas
</Link>
</div>
</motion.div>
<motion.div
className="relative hidden items-end justify-center lg:flex"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.2 }}
>
{/* Lawyer Photo - High quality professional portrait */}
<div className="relative h-[85%] w-full max-w-md">
<div className="absolute -inset-4 border border-brand-gold/30 translate-x-4 -translate-y-4"></div>
<img
src="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?auto=format&fit=crop&q=80&w=800"
alt="Dra. Laura Silva"
className="relative h-full w-full object-cover object-top shadow-2xl grayscale-[20%] contrast-125 rounded-tl-3xl rounded-br-3xl"
/>
{/* Experience Badge */}
<div className="absolute -left-12 bottom-12 flex items-center gap-4 bg-white p-4 shadow-xl">
<div className="flex h-12 w-12 items-center justify-center bg-brand-blue text-brand-gold font-serif font-bold text-xl">
15
</div>
<p className="font-serif text-sm font-bold leading-tight text-brand-blue uppercase">
Anos de<br/>Experiência
</p>
</div>
</div>
</motion.div>
</div>
</section>
);
}
function AboutSection() {
return (
<section id="sobre" className="overflow-hidden bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
<FadeIn>
<div className="relative">
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md rounded-t-full">
<img
src="https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?auto=format&fit=crop&q=80&w=800"
alt="Dra. Laura Silva em seu escritório"
className="h-full w-full object-cover grayscale-[10%]"
/>
</div>
<div className="absolute -right-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10"></div>
</div>
</FadeIn>
<FadeIn delay={0.2}>
<div className="max-w-xl">
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Dra. Laura Silva
</h2>
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
Especialista & Fundadora
</p>
<div className="mb-8 space-y-4 text-lg text-gray-600">
<p>
Com mais de uma década de dedicação exclusiva à advocacia, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela transparência e proximidade.
</p>
<p>
Não sou apenas um intermediário em processos. Sou um parceiro estratégico focado em defender seus interesses. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, do início ao fim.
</p>
</div>
<ul className="mb-10 space-y-4">
{[
"Formação em Direito pela Universidade de São Paulo (USP)",
"Pós-graduado em Direito Civil e Processo Civil",
"Atuação ética focada na resolução rápida de conflitos",
"Atendimento 100% humanizado e sem burocracias"
].map((item, i) => (
<li key={i} className="flex items-start gap-3 text-gray-700">
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
<span>{item}</span>
</li>
))}
</ul>
</div>
</FadeIn>
</div>
</div>
</section>
);
}
function PracticeAreasSection() {
const areas = [
{
title: "Direito Trabalhista",
icon: Users,
desc: "Defesa dos seus direitos no ambiente de trabalho. Rescisões, horas extras, assédio moral e doenças ocupacionais."
},
{
title: "Direito Previdenciário",
icon: ShieldCheck,
desc: "Garantia do seu benefício justo. Aposentadorias, BPC/LOAS, pensão por morte e auxílio-doença."
},
{
title: "Direito Civil",
icon: Scale,
desc: "Proteção do seu patrimônio e interesses. Contratos, indenizações, responsabilidade civil e cobranças."
},
{
title: "Direito de Família",
icon: Users,
desc: "Soluções com sensibilidade. Divórcios, pensão alimentícia, guarda, inventários e partilha de bens."
},
{
title: "Direito do Consumidor",
icon: FileText,
desc: "Contra abusos de empresas. Negativações indevidas, problemas com planos de saúde e voos, produtos com defeito."
},
{
title: "Direito Imobiliário",
icon: HomeIcon,
desc: "Segurança em transações. Regularização de imóveis, usucapião, contratos de aluguel e distratos."
}
];
return (
<section id="areas" className="bg-[#f9fafb] py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mb-16 text-center">
<FadeIn>
<h2 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Áreas de Especialidade
</h2>
<p className="mx-auto max-w-2xl text-lg text-gray-600">
Atuação jurídica ampla e estratégica focada nos ramos mais críticos para a defesa dos seus direitos processuais e patrimoniais.
</p>
</FadeIn>
</div>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{areas.map((area, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="group relative flex h-full flex-col justify-between border border-gray-200 bg-white p-8 transition-all hover:border-brand-gold hover:shadow-xl">
<div>
<div className="mb-6 inline-flex h-12 w-12 items-center justify-center bg-blue-50 text-brand-blue transition-colors group-hover:bg-brand-gold group-hover:text-white">
<area.icon className="h-6 w-6" />
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-brand-blue">
{area.title}
</h3>
<p className="mb-8 text-gray-600">
{area.desc}
</p>
</div>
<a
href="https://wa.me/5511999999999"
className="mt-auto inline-flex items-center text-sm font-semibold text-brand-gold hover:underline"
>
Falar sobre o caso <ArrowRight className="ml-1 h-4 w-4" />
</a>
</div>
</FadeIn>
))}
</div>
</div>
</section>
);
}
function HowItWorksSection() {
const steps = [
{ num: "01", title: "Envie sua Dúvida", desc: "Entre em contato via WhatsApp e explique brevemente o que aconteceu." },
{ num: "02", title: "Avaliação Inicial", desc: "Faço uma análise rápida e honesta da viabilidade jurídica do seu caso." },
{ num: "03", title: "Orientação e Estratégia", desc: "Apresento a melhor estratégia, custos transparentes e fechamos o acordo." },
{ num: "04", title: "Acompanhamento", desc: "Inicio as medidas necessárias, mantendo você atualizado em cada etapa." }
];
return (
<section id="funcionamento" className="bg-brand-blue py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<FadeIn>
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-white sm:text-4xl">
Como funciona meu atendimento
</h2>
</FadeIn>
<div className="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4 relative">
<div className="absolute top-1/2 left-0 right-0 h-[1px] bg-white/20 hidden lg:block -translate-y-[45px] z-0"></div>
{steps.map((step, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="relative z-10 flex flex-col items-center text-center">
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full border-4 border-brand-blue bg-brand-gold text-2xl font-serif font-bold text-white shadow-xl">
{step.num}
</div>
<h3 className="mb-3 font-serif text-xl font-bold text-white">{step.title}</h3>
<p className="text-gray-300">{step.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</section>
);
}
function DifferentiatorsSection() {
const points = [
{ title: "Atendimento Direto", desc: "Você fala diretamente com a advogada responsável pelo caso, sem secretárias intermediando." },
{ title: "Linguagem Clara", desc: "Sem 'juridiquês' complicado. Explico sua situação de forma simples e acolhedora." },
{ title: "Transparência Total", desc: "Honorários acordados no início. Sem surpresas ou cobranças ocultas no meio do processo." },
{ title: "Agilidade Online", desc: "Tenha todo o suporte por WhatsApp ou videoconferência, sem precisar sair de casa." }
];
return (
<section className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 gap-16 lg:grid-cols-2 lg:items-center">
<FadeIn>
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Por que me escolher para defender você?
</h2>
<p className="mb-10 text-lg text-gray-600 text-balance">
Advocacia não precisa ser lenta e distante. Meu modelo de atuação é focado no resultado e no cliente.
</p>
<Link
to="/contato"
className="inline-flex items-center justify-center gap-2 bg-brand-blue px-6 py-3 font-semibold text-white transition hover:bg-brand-blue-light"
>
<MessageCircle className="h-5 w-5" /> Agendar Consulta
</Link>
</FadeIn>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2">
{points.map((point, i) => (
<FadeIn key={i} delay={i * 0.1}>
<div className="border-l-4 border-brand-gold pl-6">
<h3 className="mb-2 font-serif text-lg font-bold text-brand-blue">{point.title}</h3>
<p className="text-gray-600 leading-relaxed text-sm">{point.desc}</p>
</div>
</FadeIn>
))}
</div>
</div>
</div>
</section>
);
}
function TestimonialsSection() {
const testimonials = [
{ quote: "A Dra. Laura foi excepcional. Resolveu meu problema na área de família com muita empatia. Ela me manteve informada pelo WhatsApp durante todo o processo.", author: "Maria C.", role: "Cliente" },
{ quote: "Senti muita confiança desde a primeira reunião. Ela explicou tudo sem usar aquele monte de palavras difíceis e foi muito transparente sobre os custos. Recomendo muito.", author: "Fernanda R.", role: "Cliente" },
{ quote: "Meu processo de inventário estava travado, e em pouco tempo após ela assumir conseguimos destravar. Agradeço pela paciência e excelente trabalho.", author: "Juliana P.", role: "Cliente" }
];
return (
<section className="bg-[#fdfbf6] py-24 sm:py-32 border-y border-amber-900/5">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<FadeIn>
<h2 className="mb-16 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
A opinião de quem confia
</h2>
</FadeIn>
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
{testimonials.map((item, i) => (
<FadeIn key={i} delay={i * 0.1}>
<div className="flex h-full flex-col justify-between bg-white p-8 shadow-sm border border-gray-100">
<div>
<div className="mb-6 text-brand-gold">
{"★".repeat(5)}
</div>
<p className="mb-8 text-gray-700 font-medium italic">"{item.quote}"</p>
</div>
<div className="flex items-center gap-3 border-t border-gray-100 pt-4">
<div className="h-10 w-10 shrink-0 bg-gray-200 rounded-full"></div>
<div>
<h4 className="font-serif font-bold text-brand-blue">{item.author}</h4>
<p className="text-xs text-gray-500 uppercase tracking-widest">{item.role}</p>
</div>
</div>
</div>
</FadeIn>
))}
</div>
</div>
</section>
);
}
function FAQSection() {
const faqs = [
{
q: "Preciso ir até o escritório para ser atendido?",
a: "Não é necessário. Todo o atendimento pode ser realizado de forma 100% online via WhatsApp ou videoconferência. Você assina e envia os documentos pelo celular com validade jurídica."
},
{
q: "A primeira consulta é cobrada?",
a: "Avaliamos rapidamente o seu caso sem compromisso. Caso seja necessária uma análise documental profunda ou parecer jurídico detalhado, a consulta formal poderá ser cobrada, mas tudo é informado previamente."
},
{
q: "Quanto custa um processo?",
a: "Cada caso tem uma complexidade diferente. Meus honorários seguem a tabela ética da OAB e são acordados de forma clara antes de qualquer assinatura de contrato. Em muitos casos (trabalhista/previdenciário), o pagamento ocorre apenas no sucesso da ação."
},
{
q: "Como saberei o andamento do meu processo?",
a: "Você receberá atualizações periódicas por WhatsApp. Além disso, sempre que o juiz der uma movimentação importante, entrarei em contato para avisar."
}
];
return (
<section className="bg-white py-24 sm:py-32">
<div className="mx-auto max-w-3xl px-6 lg:px-8">
<FadeIn>
<h2 className="mb-12 text-center font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Perguntas Frequentes
</h2>
</FadeIn>
<div className="space-y-4">
{faqs.map((faq, i) => (
<FAQItem key={i} question={faq.q} answer={faq.a} delay={i * 0.1}/>
))}
</div>
</div>
</section>
);
}
function FAQItem({ question, answer, delay }: { question: string, answer: string, delay: number }) {
const [isOpen, setIsOpen] = useState(false);
return (
<FadeIn delay={delay}>
<div className="border border-gray-200 bg-white">
<button
className="flex w-full items-center justify-between px-6 py-4 text-left font-serif text-lg font-semibold text-brand-blue transition-colors hover:bg-gray-50"
onClick={() => setIsOpen(!isOpen)}
>
{question}
<span className="ml-6 flex shrink-0 text-brand-gold">
{isOpen ? <Minus className="h-5 w-5" /> : <Plus className="h-5 w-5" />}
</span>
</button>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="overflow-hidden"
>
<div className="px-6 pb-4 pt-2 text-gray-600">
{answer}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</FadeIn>
);
}
function FinalCTASection() {
return (
<section className="relative overflow-hidden bg-brand-blue py-24 sm:py-32">
<div className="absolute inset-0 z-0 opacity-20 bg-[url('https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center mix-blend-overlay"></div>
<div className="relative z-10 mx-auto max-w-4xl px-6 text-center lg:px-8">
<FadeIn>
<Scale className="mx-auto mb-6 h-12 w-12 text-brand-gold" />
<h2 className="mb-6 font-serif text-3xl font-bold tracking-tight text-white sm:text-5xl">
Precisa de orientação jurídica?
</h2>
<p className="mx-auto mb-10 max-w-2xl text-lg text-gray-300">
Não deixe para depois a proteção dos seus direitos e do seu patrimônio. Entre em contato agora e vamos encontrar a melhor solução para o seu caso.
</p>
<div className="flex flex-col gap-4 justify-center sm:flex-row">
<a
href="https://wa.me/5511999999999"
className="inline-flex items-center justify-center gap-2 bg-brand-gold px-8 py-4 font-semibold text-white transition-colors hover:bg-white hover:text-brand-blue"
>
Falar pelo WhatsApp
</a>
<Link
to="/contato"
className="inline-flex items-center justify-center gap-2 border border-white/50 px-8 py-4 font-semibold text-white transition-colors hover:bg-white/10"
>
Enviar e-mail
</Link>
</div>
</FadeIn>
</div>
</section>
);
}

View file

@ -0,0 +1,75 @@
import React from 'react';
import { CheckCircle2 } from 'lucide-react';
import { motion } from 'motion/react';
const FadeIn = ({ children, delay = 0 }: { children: React.ReactNode, delay?: number }) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay }}
>
{children}
</motion.div>
);
export function Sobre() {
return (
<main className="w-full bg-white pb-24 pt-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid grid-cols-1 items-center gap-y-16 gap-x-16 lg:grid-cols-2">
<FadeIn>
<div className="relative">
<div className="aspect-[3/4] w-full overflow-hidden bg-gray-100 lg:max-w-md rounded-t-full">
<img
src="https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?auto=format&fit=crop&q=80&w=800"
alt="Dra. Laura Silva em seu escritório"
className="h-full w-full object-cover grayscale-[10%]"
/>
</div>
<div className="absolute -left-4 -bottom-4 h-32 w-32 bg-brand-gold opacity-20 -z-10 rounded-full"></div>
</div>
</FadeIn>
<FadeIn delay={0.2}>
<div className="max-w-xl">
<h1 className="mb-4 font-serif text-3xl font-bold tracking-tight text-brand-blue sm:text-4xl">
Dra. Laura Silva
</h1>
<p className="mb-6 text-sm font-medium tracking-widest text-brand-gold uppercase">
Especialista & Fundadora
</p>
<div className="mb-8 space-y-4 text-lg text-gray-600">
<p>
Com mais de uma década de dedicação exclusiva à advocacia moderna, meu objetivo sempre foi o mesmo: entregar resultados concretos com um atendimento pautado pela escuta ativa, empatia e proximidade.
</p>
<p>
Acredito que a advocacia de excelência é baseada na confiança mútua e na sensibilidade. Por isso, não sou apenas uma intermediária em processos. Sou uma parceira estratégica focada em defender seus direitos de forma inteligente. Diferente dos grandes escritórios onde você é apenas um número, aqui o seu caso é tratado diretamente por mim, sempre.
</p>
<p>
Minha abordagem é acolhedora na escuta e pragmática na solução. Seja na esfera familiar, civil ou do consumidor, meu compromisso é garantir que os direitos dos meus clientes sejam integralmente respeitados, com ética e determinação.
</p>
</div>
<ul className="mb-10 space-y-4">
{[
"Formação em Direito pela Universidade de São Paulo (USP)",
"Pós-graduada em Direito de Família e Sucessões",
"Atuação estratégica focada no acolhimento e acordo",
"Atendimento 100% humanizado e direcionado",
"Membro da Ordem dos Advogados do Brasil Seção São Paulo",
"Presidente da comissão regional de defesa da mulher"
].map((item, i) => (
<li key={i} className="flex items-start gap-3 text-gray-700">
<CheckCircle2 className="mt-1 h-5 w-5 shrink-0 text-brand-gold" />
<span>{item}</span>
</li>
))}
</ul>
</div>
</FadeIn>
</div>
</div>
</main>
);
}

26
Template-02/tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

View file

@ -0,0 +1,24 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});