Versão inicial do site Avanzato
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { Check, Award, Users, Clock } from 'lucide-react';
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
interface CounterProps {
|
||||
end: number;
|
||||
suffix?: string;
|
||||
prefix?: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
const Counter = ({ end, suffix = '', prefix = '', duration = 2 }: CounterProps) => {
|
||||
const [count, setCount] = useState(0);
|
||||
const counterRef = useRef<HTMLDivElement>(null);
|
||||
const hasAnimated = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting && !hasAnimated.current) {
|
||||
hasAnimated.current = true;
|
||||
let startTime: number;
|
||||
const animate = (timestamp: number) => {
|
||||
if (!startTime) startTime = timestamp;
|
||||
const progress = Math.min((timestamp - startTime) / (duration * 1000), 1);
|
||||
setCount(Math.floor(progress * end));
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
};
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
|
||||
if (counterRef.current) {
|
||||
observer.observe(counterRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [end, duration]);
|
||||
|
||||
return (
|
||||
<div ref={counterRef} className="counter text-4xl md:text-5xl font-black text-[#cbf400]">
|
||||
{prefix}{count}{suffix}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const About = () => {
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
const imageRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const statsRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
// Image reveal animation
|
||||
gsap.fromTo(
|
||||
imageRef.current,
|
||||
{ clipPath: 'circle(0% at 50% 50%)', opacity: 0 },
|
||||
{
|
||||
clipPath: 'circle(100% at 50% 50%)',
|
||||
opacity: 1,
|
||||
duration: 1.2,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: {
|
||||
trigger: imageRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Content animation
|
||||
const contentElements = contentRef.current?.querySelectorAll('.animate-item');
|
||||
if (contentElements) {
|
||||
gsap.fromTo(
|
||||
contentElements,
|
||||
{ y: 30, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.6,
|
||||
stagger: 0.1,
|
||||
ease: 'back.out(1.7)',
|
||||
scrollTrigger: {
|
||||
trigger: contentRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Stats animation
|
||||
gsap.fromTo(
|
||||
statsRef.current,
|
||||
{ y: 50, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.8,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: statsRef.current,
|
||||
start: 'top 85%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
}, sectionRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
const differentials = [
|
||||
'Atendimento personalizado e próximo',
|
||||
'Equipe certificada e especializada',
|
||||
'SLA garantido em todos os serviços',
|
||||
'Tecnologia de ponta sempre atualizada',
|
||||
'Relatórios detalhados e transparentes',
|
||||
'Foco em resultados e produtividade',
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
id="about"
|
||||
ref={sectionRef}
|
||||
className="section-padding bg-[#1a1a1a] relative overflow-hidden"
|
||||
>
|
||||
{/* Background decoration */}
|
||||
<div className="absolute top-1/2 left-0 w-[600px] h-[600px] bg-[#cbf400]/5 rounded-full blur-3xl -translate-y-1/2 -translate-x-1/2" />
|
||||
|
||||
<div className="container-custom relative z-10">
|
||||
<div className="grid lg:grid-cols-2 gap-12 lg:gap-20 items-center">
|
||||
{/* Image */}
|
||||
<div ref={imageRef} className="relative">
|
||||
<div className="relative rounded-2xl overflow-hidden shadow-2xl border border-white/5">
|
||||
<img
|
||||
src="/about-image.jpg"
|
||||
alt="Equipe Avanzato"
|
||||
className="w-full h-auto object-cover"
|
||||
/>
|
||||
|
||||
{/* Glassmorphism overlay card */}
|
||||
<div className="absolute bottom-6 left-6 right-6 glass rounded-xl p-6 border border-[#cbf400]/20">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 rounded-full bg-[#cbf400]/20 flex items-center justify-center border border-[#cbf400]/30">
|
||||
<Award className="w-7 h-7 text-[#cbf400]" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-white font-bold text-lg">+23 anos</div>
|
||||
<div className="text-gray-400 text-sm">de experiência no mercado</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Decorative elements */}
|
||||
<div className="absolute -top-4 -right-4 w-24 h-24 border-2 border-[#cbf400]/20 rounded-xl" />
|
||||
<div className="absolute -bottom-4 -left-4 w-32 h-32 bg-[#cbf400]/10 rounded-xl -z-10" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div ref={contentRef}>
|
||||
<span className="animate-item inline-block px-4 py-2 rounded-full bg-[#cbf400]/10 text-[#cbf400] text-sm font-semibold mb-4 border border-[#cbf400]/20">
|
||||
Sobre a Avanzato
|
||||
</span>
|
||||
|
||||
<h2 className="animate-item text-3xl md:text-4xl lg:text-5xl font-black text-white mb-6">
|
||||
Por que escolher a{' '}
|
||||
<span className="text-gradient">Avanzato?</span>
|
||||
</h2>
|
||||
|
||||
<p className="animate-item text-gray-400 text-lg mb-6 leading-relaxed">
|
||||
Com mais de 23 anos de experiência, somos o parceiro estratégico que sua empresa
|
||||
precisa para navegar na transformação digital. Não vendemos tecnologia,
|
||||
<strong className="text-white"> entregamos resultados</strong>.
|
||||
</p>
|
||||
|
||||
<p className="animate-item text-gray-400 mb-8 leading-relaxed">
|
||||
Nossa missão é capacitar empresas de todos os tamanhos a alcançar seus objetivos,
|
||||
assegurando que a TI seja uma aliada estratégica. Permitimos que nossos clientes
|
||||
se concentrem no que fazem de melhor - conduzir seus negócios com sucesso.
|
||||
</p>
|
||||
|
||||
{/* Differentials */}
|
||||
<div className="animate-item grid sm:grid-cols-2 gap-3 mb-8">
|
||||
{differentials.map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<div className="w-5 h-5 rounded-full bg-[#cbf400]/10 flex items-center justify-center flex-shrink-0 mt-0.5 border border-[#cbf400]/30">
|
||||
<Check className="w-3 h-3 text-[#cbf400]" />
|
||||
</div>
|
||||
<span className="text-sm text-gray-300">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<a
|
||||
href="https://api.whatsapp.com/send?phone=551148101704&text=Olá!%20Gostaria%20de%20conhecer%20mais%20sobre%20a%20Avanzato"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="animate-item btn-primary inline-flex items-center gap-2"
|
||||
>
|
||||
Conheça nossa história
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div
|
||||
ref={statsRef}
|
||||
className="grid grid-cols-2 md:grid-cols-4 gap-6 mt-20 pt-12 border-t border-white/10"
|
||||
>
|
||||
<div className="text-center">
|
||||
<Counter end={500} suffix="+" />
|
||||
<div className="flex items-center justify-center gap-2 mt-2 text-gray-500">
|
||||
<Users className="w-4 h-4" />
|
||||
<span className="text-sm">Clientes atendidos</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<Counter end={99} suffix=".9%" />
|
||||
<div className="flex items-center justify-center gap-2 mt-2 text-gray-500">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span className="text-sm">Uptime garantido</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<Counter end={23} suffix="+" />
|
||||
<div className="flex items-center justify-center gap-2 mt-2 text-gray-500">
|
||||
<Award className="w-4 h-4" />
|
||||
<span className="text-sm">Anos de experiência</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<Counter end={24} suffix="/7" />
|
||||
<div className="flex items-center justify-center gap-2 mt-2 text-gray-500">
|
||||
<Users className="w-4 h-4" />
|
||||
<span className="text-sm">Suporte técnico</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -0,0 +1,180 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { Building2, Phone, Server, ArrowRight, CheckCircle2, Shield, Wifi } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const CondominiosCTA = () => {
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
const elements = contentRef.current?.querySelectorAll('.animate-item');
|
||||
if (elements && elements.length > 0) {
|
||||
gsap.fromTo(
|
||||
elements,
|
||||
{ y: 50, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.7,
|
||||
stagger: 0.1,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: sectionRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}, sectionRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
const beneficios = [
|
||||
'Redução de até 90% no investimento inicial',
|
||||
'Independência de empresas de portaria',
|
||||
'Ramais gratuitos para áreas comuns',
|
||||
'Monitoramento 24/7',
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={sectionRef}
|
||||
className="section-padding bg-[#141414] relative overflow-hidden"
|
||||
>
|
||||
{/* Background decorations */}
|
||||
<div className="absolute top-0 right-0 w-[500px] h-[500px] bg-[#cbf400]/5 rounded-full blur-[120px] -translate-y-1/2 translate-x-1/4" />
|
||||
<div className="absolute bottom-0 left-0 w-[400px] h-[400px] bg-[#cbf400]/3 rounded-full blur-[100px] translate-y-1/2 -translate-x-1/4" />
|
||||
|
||||
<div className="container-custom relative z-10">
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="bg-gradient-to-br from-[#1a1a1a] to-[#0e0e0e] rounded-3xl p-8 md:p-12 lg:p-16 border border-[#cbf400]/20 relative overflow-hidden"
|
||||
>
|
||||
{/* Badge */}
|
||||
<div className="animate-item inline-flex items-center gap-2 px-4 py-2 rounded-full bg-[#cbf400]/10 border border-[#cbf400]/20 mb-6">
|
||||
<Building2 className="w-4 h-4 text-[#cbf400]" />
|
||||
<span className="text-[#cbf400] text-sm font-medium">Nova Solução</span>
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Content */}
|
||||
<div>
|
||||
<h2 className="animate-item text-3xl md:text-4xl lg:text-5xl font-black text-white mb-6 leading-tight">
|
||||
Tecnologia para{' '}
|
||||
<span className="text-gradient">Condomínios</span>
|
||||
</h2>
|
||||
|
||||
<p className="animate-item text-gray-400 text-lg mb-8">
|
||||
Infraestrutura própria, VOIP moderno e redução de custos.
|
||||
Seu condomínio com tecnologia que não para.
|
||||
</p>
|
||||
|
||||
{/* Benefícios */}
|
||||
<div className="animate-item space-y-3 mb-8">
|
||||
{beneficios.map((item, i) => (
|
||||
<div key={i} className="flex items-center gap-3">
|
||||
<CheckCircle2 className="w-5 h-5 text-[#cbf400] flex-shrink-0" />
|
||||
<span className="text-gray-300">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className="animate-item flex flex-col sm:flex-row gap-4">
|
||||
<Link to="/tecnologia-para-condominios">
|
||||
<Button className="bg-[#cbf400] text-[#0e0e0e] hover:bg-[#b8e000] px-8 py-6 text-lg font-bold rounded-xl transition-all duration-300 hover:scale-105 w-full sm:w-auto">
|
||||
Conhecer Solução
|
||||
<ArrowRight className="ml-2 w-5 h-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link to="/contato">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-white/20 text-white hover:bg-white/10 px-8 py-6 text-lg font-medium rounded-xl transition-all duration-300 w-full sm:w-auto"
|
||||
>
|
||||
Falar com Especialista
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Visual */}
|
||||
<div className="animate-item relative">
|
||||
<div className="absolute inset-0 bg-[#cbf400]/10 rounded-3xl blur-3xl" />
|
||||
<div className="relative bg-[#0e0e0e] rounded-3xl p-8 border border-white/10">
|
||||
{/* Cards de serviço */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4 p-4 bg-[#1a1a1a] rounded-xl border border-[#cbf400]/20">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#cbf400]/10 flex items-center justify-center">
|
||||
<Phone className="w-6 h-6 text-[#cbf400]" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-white font-bold">VOIP para Condomínios</h4>
|
||||
<p className="text-gray-500 text-sm">Comunicação moderna e econômica</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 p-4 bg-[#1a1a1a] rounded-xl border border-white/10">
|
||||
<div className="w-12 h-12 rounded-xl bg-blue-500/10 flex items-center justify-center">
|
||||
<Server className="w-6 h-6 text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-white font-bold">Gestão de CPD</h4>
|
||||
<p className="text-gray-500 text-sm">Infraestrutura profissional</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 p-4 bg-[#1a1a1a] rounded-xl border border-white/10">
|
||||
<div className="w-12 h-12 rounded-xl bg-purple-500/10 flex items-center justify-center">
|
||||
<Wifi className="w-6 h-6 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-white font-bold">Rede e Conectividade</h4>
|
||||
<p className="text-gray-500 text-sm">Alta disponibilidade</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 p-4 bg-[#1a1a1a] rounded-xl border border-white/10">
|
||||
<div className="w-12 h-12 rounded-xl bg-red-500/10 flex items-center justify-center">
|
||||
<Shield className="w-6 h-6 text-red-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-white font-bold">Segurança</h4>
|
||||
<p className="text-gray-500 text-sm">Monitoramento 24/7</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-3 gap-4 mt-6 pt-6 border-t border-white/10">
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-black text-[#cbf400]">90%</div>
|
||||
<div className="text-gray-500 text-xs">Economia</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-black text-[#cbf400]">24/7</div>
|
||||
<div className="text-gray-500 text-xs">Operação</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-black text-[#cbf400]">500+</div>
|
||||
<div className="text-gray-500 text-xs">Clientes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default CondominiosCTA;
|
||||
@@ -0,0 +1,408 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { Mail, Phone, MapPin, Send, Check, Loader2, Clock } from 'lucide-react';
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const Contact = () => {
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
const formRef = useRef<HTMLDivElement>(null);
|
||||
const infoRef = useRef<HTMLDivElement>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
company: '',
|
||||
message: '',
|
||||
});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
|
||||
// Particle network animation
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const resizeCanvas = () => {
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
};
|
||||
resizeCanvas();
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
|
||||
const particles: Array<{
|
||||
x: number;
|
||||
y: number;
|
||||
vx: number;
|
||||
vy: number;
|
||||
radius: number;
|
||||
}> = [];
|
||||
|
||||
for (let i = 0; i < 30; i++) {
|
||||
particles.push({
|
||||
x: Math.random() * canvas.width,
|
||||
y: Math.random() * canvas.height,
|
||||
vx: (Math.random() - 0.5) * 0.3,
|
||||
vy: (Math.random() - 0.5) * 0.3,
|
||||
radius: Math.random() * 2 + 1,
|
||||
});
|
||||
}
|
||||
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
let isMouseInCanvas = false;
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
mouseX = e.clientX - rect.left;
|
||||
mouseY = e.clientY - rect.top;
|
||||
isMouseInCanvas = true;
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
isMouseInCanvas = false;
|
||||
};
|
||||
|
||||
canvas.addEventListener('mousemove', handleMouseMove);
|
||||
canvas.addEventListener('mouseleave', handleMouseLeave);
|
||||
|
||||
let animationId: number;
|
||||
const animate = () => {
|
||||
ctx.fillStyle = 'rgba(26, 26, 26, 0.1)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
particles.forEach((particle, i) => {
|
||||
particle.x += particle.vx;
|
||||
particle.y += particle.vy;
|
||||
|
||||
if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1;
|
||||
if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = 'rgba(203, 244, 0, 0.6)';
|
||||
ctx.fill();
|
||||
|
||||
// Connect to mouse
|
||||
if (isMouseInCanvas) {
|
||||
const dx = particle.x - mouseX;
|
||||
const dy = particle.y - mouseY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
if (distance < 150) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(particle.x, particle.y);
|
||||
ctx.lineTo(mouseX, mouseY);
|
||||
ctx.strokeStyle = `rgba(203, 244, 0, ${0.2 * (1 - distance / 150)})`;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Connect particles
|
||||
particles.slice(i + 1).forEach((other) => {
|
||||
const dx = particle.x - other.x;
|
||||
const dy = particle.y - other.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < 100) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(particle.x, particle.y);
|
||||
ctx.lineTo(other.x, other.y);
|
||||
ctx.strokeStyle = `rgba(203, 244, 0, ${0.15 * (1 - distance / 100)})`;
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
animationId = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', resizeCanvas);
|
||||
canvas.removeEventListener('mousemove', handleMouseMove);
|
||||
canvas.removeEventListener('mouseleave', handleMouseLeave);
|
||||
cancelAnimationFrame(animationId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// GSAP animations
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
gsap.fromTo(
|
||||
formRef.current,
|
||||
{ y: 50, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.8,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: formRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
infoRef.current,
|
||||
{ x: 100, opacity: 0 },
|
||||
{
|
||||
x: 0,
|
||||
opacity: 1,
|
||||
duration: 0.8,
|
||||
delay: 0.2,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: infoRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
}, sectionRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
setIsSubmitting(false);
|
||||
setIsSubmitted(true);
|
||||
setFormData({ name: '', email: '', company: '', message: '' });
|
||||
|
||||
// Reset success message after 5 seconds
|
||||
setTimeout(() => setIsSubmitted(false), 5000);
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[e.target.name]: e.target.value,
|
||||
}));
|
||||
};
|
||||
|
||||
const contactInfo = [
|
||||
{
|
||||
icon: <Mail className="w-5 h-5" />,
|
||||
label: 'Email',
|
||||
value: 'contato@avanzato.com.br',
|
||||
href: 'mailto:contato@avanzato.com.br',
|
||||
},
|
||||
{
|
||||
icon: <Phone className="w-5 h-5" />,
|
||||
label: 'Telefone',
|
||||
value: '(11) 4810-1704',
|
||||
href: 'tel:+551148101704',
|
||||
},
|
||||
{
|
||||
icon: <MapPin className="w-5 h-5" />,
|
||||
label: 'Endereço',
|
||||
value: 'Av. Dr. Carlos de Campos, 43 - Guarulhos/SP',
|
||||
href: 'https://maps.google.com/?q=Av.+Dr.+Carlos+de+Campos,+43+Guarulhos+SP',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
id="contact"
|
||||
ref={sectionRef}
|
||||
className="section-padding bg-[#0e0e0e] relative overflow-hidden"
|
||||
>
|
||||
<div className="container-custom">
|
||||
<div className="grid lg:grid-cols-2 gap-0 rounded-2xl overflow-hidden shadow-2xl border border-white/5">
|
||||
{/* Form section */}
|
||||
<div ref={formRef} className="bg-[#1a1a1a] p-8 md:p-12">
|
||||
<span className="inline-block px-4 py-2 rounded-full bg-[#cbf400]/10 text-[#cbf400] text-sm font-semibold mb-4 border border-[#cbf400]/20">
|
||||
Contato
|
||||
</span>
|
||||
|
||||
<h2 className="text-3xl md:text-4xl font-black text-white mb-4">
|
||||
Vamos <span className="text-gradient">conversar</span>
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-400 mb-8">
|
||||
Preencha o formulário abaixo e nossa equipe entrará em contato em até 24 horas.
|
||||
</p>
|
||||
|
||||
{isSubmitted ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<div className="w-16 h-16 rounded-full bg-[#cbf400]/20 flex items-center justify-center mb-4 border border-[#cbf400]/30">
|
||||
<Check className="w-8 h-8 text-[#cbf400]" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-white mb-2">Mensagem enviada!</h3>
|
||||
<p className="text-gray-400">Entraremos em contato em breve.</p>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid sm:grid-cols-2 gap-6">
|
||||
<div className="input-focus">
|
||||
<label className="block text-sm font-semibold text-gray-300 mb-2">
|
||||
Nome
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 rounded-lg bg-[#0e0e0e] border border-white/10 text-white focus:border-[#cbf400] focus:ring-2 focus:ring-[#cbf400]/20 outline-none transition-all"
|
||||
placeholder="Seu nome"
|
||||
/>
|
||||
</div>
|
||||
<div className="input-focus">
|
||||
<label className="block text-sm font-semibold text-gray-300 mb-2">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full px-4 py-3 rounded-lg bg-[#0e0e0e] border border-white/10 text-white focus:border-[#cbf400] focus:ring-2 focus:ring-[#cbf400]/20 outline-none transition-all"
|
||||
placeholder="seu@email.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="input-focus">
|
||||
<label className="block text-sm font-semibold text-gray-300 mb-2">
|
||||
Empresa
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="company"
|
||||
value={formData.company}
|
||||
onChange={handleChange}
|
||||
className="w-full px-4 py-3 rounded-lg bg-[#0e0e0e] border border-white/10 text-white focus:border-[#cbf400] focus:ring-2 focus:ring-[#cbf400]/20 outline-none transition-all"
|
||||
placeholder="Nome da sua empresa"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="input-focus">
|
||||
<label className="block text-sm font-semibold text-gray-300 mb-2">
|
||||
Mensagem
|
||||
</label>
|
||||
<textarea
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
rows={4}
|
||||
className="w-full px-4 py-3 rounded-lg bg-[#0e0e0e] border border-white/10 text-white focus:border-[#cbf400] focus:ring-2 focus:ring-[#cbf400]/20 outline-none transition-all resize-none"
|
||||
placeholder="Como podemos ajudar?"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="btn-primary w-full flex items-center justify-center gap-2 disabled:opacity-70"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<>
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
Enviando...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Enviar mensagem
|
||||
<Send className="w-5 h-5" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Info section */}
|
||||
<div ref={infoRef} className="relative bg-[#0e0e0e] p-8 md:p-12 overflow-hidden">
|
||||
{/* Particle canvas */}
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="absolute inset-0 w-full h-full"
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10">
|
||||
<h3 className="text-2xl font-bold text-white mb-6">
|
||||
Informações de contato
|
||||
</h3>
|
||||
|
||||
<div className="space-y-6 mb-12">
|
||||
{contactInfo.map((item, index) => (
|
||||
<a
|
||||
key={index}
|
||||
href={item.href}
|
||||
target={item.href.startsWith('http') ? '_blank' : undefined}
|
||||
rel={item.href.startsWith('http') ? 'noopener noreferrer' : undefined}
|
||||
className="flex items-start gap-4 group"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-[#cbf400]/10 flex items-center justify-center text-[#cbf400] group-hover:bg-[#cbf400] group-hover:text-[#0e0e0e] transition-colors border border-[#cbf400]/20">
|
||||
{item.icon}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm text-gray-500">{item.label}</div>
|
||||
<div className="text-white group-hover:text-[#cbf400] transition-colors font-medium">
|
||||
{item.value}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* WhatsApp CTA */}
|
||||
<div className="bg-[#1a1a1a] backdrop-blur-sm rounded-xl p-6 border border-white/5">
|
||||
<h4 className="text-white font-semibold mb-2">
|
||||
Prefere falar pelo WhatsApp?
|
||||
</h4>
|
||||
<p className="text-gray-400 text-sm mb-4">
|
||||
Nossa equipe está disponível para atendimento imediato.
|
||||
</p>
|
||||
<a
|
||||
href="https://api.whatsapp.com/send?phone=551148101704&text=Olá!%20Gostaria%20de%20saber%20mais%20sobre%20os%20serviços%20da%20Avanzato"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 text-[#cbf400] hover:text-[#d4ff1a] transition-colors font-medium"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
|
||||
</svg>
|
||||
Iniciar conversa
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Hours */}
|
||||
<div className="mt-8 pt-8 border-t border-white/10">
|
||||
<div className="flex items-center gap-2 text-gray-500 text-sm mb-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>Horário de atendimento</span>
|
||||
</div>
|
||||
<div className="text-white font-medium">
|
||||
Segunda a Sexta: 8h às 18h
|
||||
</div>
|
||||
<div className="text-[#cbf400] text-sm mt-1 font-medium">
|
||||
Suporte técnico: 24/7
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
@@ -0,0 +1,192 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Facebook, Youtube, Linkedin, Instagram, ArrowUp, MapPin } from 'lucide-react';
|
||||
import { LOGO, SOCIAL_LINKS, ADDRESSES, FOOTER_LINKS, COMPANY } from '../config/site';
|
||||
|
||||
// Mapa de icones das redes sociais
|
||||
const socialIcons: Record<string, React.ReactNode> = {
|
||||
facebook: <Facebook className="w-5 h-5" />,
|
||||
youtube: <Youtube className="w-5 h-5" />,
|
||||
linkedin: <Linkedin className="w-5 h-5" />,
|
||||
instagram: <Instagram className="w-5 h-5" />,
|
||||
};
|
||||
|
||||
const Footer = () => {
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
// Filtra apenas redes sociais com URL preenchida
|
||||
const activeSocials = Object.entries(SOCIAL_LINKS)
|
||||
.filter(([, data]) => data.url)
|
||||
.map(([key, data]) => ({
|
||||
key,
|
||||
icon: data.iconSvg
|
||||
? <div dangerouslySetInnerHTML={{ __html: data.iconSvg }} />
|
||||
: (socialIcons[key] || null),
|
||||
href: data.url,
|
||||
label: data.label,
|
||||
}));
|
||||
|
||||
// Renderiza o logo (SVG inline ou imagem)
|
||||
const renderLogo = () => {
|
||||
if (LOGO.imageUrl) {
|
||||
return <img src={LOGO.imageUrl} alt={COMPANY.name} className="h-10 w-auto" />;
|
||||
}
|
||||
return (
|
||||
<div dangerouslySetInnerHTML={{ __html: LOGO.svgInline }} className="flex-shrink-0" />
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<footer className="bg-[#0e0e0e] text-white relative border-t border-white/5">
|
||||
{/* Main footer */}
|
||||
<div className="container-custom py-16">
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-5 gap-12">
|
||||
{/* Brand */}
|
||||
<div className="lg:col-span-2">
|
||||
<Link to={LOGO.href} className="flex items-center gap-3 mb-6">
|
||||
{renderLogo()}
|
||||
{LOGO.text && (
|
||||
<span className="text-2xl font-bold tracking-tight">
|
||||
{LOGO.text}
|
||||
<span className="text-[#cbf400]">{LOGO.textHighlight}</span>
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
<p className="text-gray-400 mb-6 max-w-sm">
|
||||
{COMPANY.description}
|
||||
</p>
|
||||
|
||||
{/* Enderecos - importados de config/site.ts */}
|
||||
{ADDRESSES.length > 0 && (
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<MapPin className="w-4 h-4 text-[#cbf400]" />
|
||||
<span className="text-white font-medium text-sm">Nossos Escritorios</span>
|
||||
</div>
|
||||
<div className="space-y-2 text-xs text-gray-400">
|
||||
{ADDRESSES.map((addr, i) => (
|
||||
<p key={i}>{addr.street} - {addr.city}/{addr.state}</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Social links - importados de config/site.ts */}
|
||||
{activeSocials.length > 0 && (
|
||||
<div className="flex gap-3">
|
||||
{activeSocials.map((social) => (
|
||||
<a
|
||||
key={social.key}
|
||||
href={social.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={social.label}
|
||||
className="w-10 h-10 rounded-lg bg-white/5 flex items-center justify-center text-gray-400 hover:bg-[#cbf400] hover:text-[#0e0e0e] transition-colors border border-white/5"
|
||||
>
|
||||
{social.icon}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Services */}
|
||||
<div>
|
||||
<h4 className="font-bold mb-4 text-white">Servicos</h4>
|
||||
<ul className="space-y-3">
|
||||
{FOOTER_LINKS.servicos.map((link, index) => (
|
||||
<li key={index}>
|
||||
<Link
|
||||
to={link.href}
|
||||
className="text-gray-400 hover:text-[#cbf400] transition-colors text-sm"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Company */}
|
||||
<div>
|
||||
<h4 className="font-bold mb-4 text-white">Empresa</h4>
|
||||
<ul className="space-y-3">
|
||||
{FOOTER_LINKS.empresa.map((link, index) => (
|
||||
<li key={index}>
|
||||
<Link
|
||||
to={link.href}
|
||||
className="text-gray-400 hover:text-[#cbf400] transition-colors text-sm"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Support */}
|
||||
<div>
|
||||
<h4 className="font-bold mb-4 text-white">Suporte</h4>
|
||||
<ul className="space-y-3">
|
||||
{FOOTER_LINKS.suporte.map((link, index) => (
|
||||
<li key={index}>
|
||||
{link.href.startsWith('http') ? (
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-400 hover:text-[#cbf400] transition-colors text-sm"
|
||||
>
|
||||
{link.name}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
to={link.href}
|
||||
className="text-gray-400 hover:text-[#cbf400] transition-colors text-sm"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<div className="border-t border-white/5">
|
||||
<div className="container-custom py-6">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||
<p className="text-gray-500 text-sm">
|
||||
© {new Date().getFullYear()} {COMPANY.name}. Todos os direitos reservados.
|
||||
</p>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
{FOOTER_LINKS.legais.map((link, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
to={link.href}
|
||||
className="text-gray-500 hover:text-gray-300 text-sm transition-colors"
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className="w-10 h-10 rounded-full bg-[#cbf400]/10 flex items-center justify-center text-[#cbf400] hover:bg-[#cbf400] hover:text-[#0e0e0e] transition-all"
|
||||
aria-label="Voltar ao topo"
|
||||
>
|
||||
<ArrowUp className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -0,0 +1,292 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ArrowRight, Play } from 'lucide-react';
|
||||
import GlowButton from '../components/GlowButton';
|
||||
|
||||
const Hero = () => {
|
||||
const heroRef = useRef<HTMLDivElement>(null);
|
||||
const titleRef = useRef<HTMLHeadingElement>(null);
|
||||
const subtitleRef = useRef<HTMLParagraphElement>(null);
|
||||
const ctaRef = useRef<HTMLDivElement>(null);
|
||||
const imageRef = useRef<HTMLDivElement>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
|
||||
|
||||
// Animated background
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
const resizeCanvas = () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
};
|
||||
resizeCanvas();
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
|
||||
// Particles
|
||||
const particles: Array<{
|
||||
x: number;
|
||||
y: number;
|
||||
vx: number;
|
||||
vy: number;
|
||||
radius: number;
|
||||
opacity: number;
|
||||
}> = [];
|
||||
|
||||
for (let i = 0; i < 50; i++) {
|
||||
particles.push({
|
||||
x: Math.random() * canvas.width,
|
||||
y: Math.random() * canvas.height,
|
||||
vx: (Math.random() - 0.5) * 0.5,
|
||||
vy: (Math.random() - 0.5) * 0.5,
|
||||
radius: Math.random() * 2 + 1,
|
||||
opacity: Math.random() * 0.5 + 0.2,
|
||||
});
|
||||
}
|
||||
|
||||
let animationId: number;
|
||||
const animate = () => {
|
||||
ctx.fillStyle = 'rgba(14, 14, 14, 0.05)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
particles.forEach((particle, i) => {
|
||||
particle.x += particle.vx;
|
||||
particle.y += particle.vy;
|
||||
|
||||
if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1;
|
||||
if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = `rgba(203, 244, 0, ${particle.opacity})`;
|
||||
ctx.fill();
|
||||
|
||||
// Connect particles
|
||||
particles.slice(i + 1).forEach((other) => {
|
||||
const dx = particle.x - other.x;
|
||||
const dy = particle.y - other.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < 150) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(particle.x, particle.y);
|
||||
ctx.lineTo(other.x, other.y);
|
||||
ctx.strokeStyle = `rgba(203, 244, 0, ${0.1 * (1 - distance / 150)})`;
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
animationId = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', resizeCanvas);
|
||||
cancelAnimationFrame(animationId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// GSAP animations
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
// Title animation
|
||||
gsap.fromTo(
|
||||
titleRef.current,
|
||||
{ y: 100, opacity: 0 },
|
||||
{ y: 0, opacity: 1, duration: 0.8, delay: 0.2, ease: 'expo.out' }
|
||||
);
|
||||
|
||||
// Subtitle animation
|
||||
gsap.fromTo(
|
||||
subtitleRef.current,
|
||||
{ y: 50, opacity: 0 },
|
||||
{ y: 0, opacity: 1, duration: 0.6, delay: 0.4, ease: 'expo.out' }
|
||||
);
|
||||
|
||||
// CTA animation
|
||||
gsap.fromTo(
|
||||
ctaRef.current,
|
||||
{ scale: 0.8, opacity: 0 },
|
||||
{ scale: 1, opacity: 1, duration: 0.6, delay: 0.6, ease: 'back.out(1.7)' }
|
||||
);
|
||||
|
||||
// Image animation
|
||||
gsap.fromTo(
|
||||
imageRef.current,
|
||||
{ rotateX: 45, opacity: 0, y: 50 },
|
||||
{ rotateX: 0, opacity: 1, y: 0, duration: 1.2, delay: 0.4, ease: 'elastic.out(1, 0.5)' }
|
||||
);
|
||||
}, heroRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
// Mouse parallax effect
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
const x = (e.clientX / window.innerWidth - 0.5) * 20;
|
||||
const y = (e.clientY / window.innerHeight - 0.5) * 20;
|
||||
setMousePos({ x, y });
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove, { passive: true });
|
||||
return () => window.removeEventListener('mousemove', handleMouseMove);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
id="hero"
|
||||
ref={heroRef}
|
||||
className="relative min-h-screen flex items-center overflow-hidden bg-[#0e0e0e]"
|
||||
>
|
||||
{/* Animated background canvas */}
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="absolute inset-0 w-full h-full"
|
||||
style={{ opacity: 0.8 }}
|
||||
/>
|
||||
|
||||
{/* Gradient overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#cbf400]/5 via-transparent to-transparent" />
|
||||
|
||||
{/* Content */}
|
||||
<div className="container-custom relative z-10 pt-32 pb-20">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left content */}
|
||||
<div className="text-white">
|
||||
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-[#cbf400]/10 backdrop-blur-sm mb-6 border border-[#cbf400]/20">
|
||||
<span className="w-2 h-2 rounded-full bg-[#cbf400] animate-pulse" />
|
||||
<span className="text-sm font-semibold text-[#cbf400]">+23 anos de experiência</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
ref={titleRef}
|
||||
className="text-4xl md:text-5xl lg:text-6xl font-black leading-tight mb-6"
|
||||
style={{ fontFamily: 'Urbanist, sans-serif' }}
|
||||
>
|
||||
Tecnologia que{' '}
|
||||
<span className="text-gradient">impulsiona</span> seu negócio
|
||||
</h1>
|
||||
|
||||
<p
|
||||
ref={subtitleRef}
|
||||
className="text-lg md:text-xl text-gray-400 mb-8 max-w-xl font-medium"
|
||||
>
|
||||
Soluções em TI Gerenciada, Cloud e Segurança para empresas que não podem parar.
|
||||
Seu parceiro estratégico em transformação digital.
|
||||
</p>
|
||||
|
||||
<div ref={ctaRef} className="flex flex-wrap gap-4">
|
||||
<a
|
||||
href="https://api.whatsapp.com/send?phone=551148101704&text=Olá!%20Visitei%20o%20site%20e%20gostaria%20de%20saber%20mais%20informações%20sobre%20os%20serviços"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn-primary inline-flex items-center gap-2"
|
||||
>
|
||||
Fale com um especialista
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</a>
|
||||
<a
|
||||
href="#services"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
document.querySelector('#services')?.scrollIntoView({ behavior: 'smooth' });
|
||||
}}
|
||||
className="btn-secondary inline-flex items-center gap-2"
|
||||
>
|
||||
<Play className="w-5 h-5" />
|
||||
Conheça nossos serviços
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Botao Avaliacao TI - Super destacado */}
|
||||
<div className="mt-6">
|
||||
<GlowButton
|
||||
to="/avaliacao"
|
||||
text="Avalie a maturidade da sua TI"
|
||||
size="lg"
|
||||
/>
|
||||
<p className="text-gray-500 text-sm mt-2 ml-1">
|
||||
Descubra o nivel de maturidade tecnologica da sua empresa em 2 minutos
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-3 gap-6 mt-12 pt-8 border-t border-white/10">
|
||||
<div>
|
||||
<div className="text-3xl md:text-4xl font-black text-[#cbf400]">+500</div>
|
||||
<div className="text-sm text-gray-500 font-medium">Clientes atendidos</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl md:text-4xl font-black text-[#cbf400]">99.9%</div>
|
||||
<div className="text-sm text-gray-500 font-medium">Uptime garantido</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-3xl md:text-4xl font-black text-[#cbf400]">24/7</div>
|
||||
<div className="text-sm text-gray-500 font-medium">Suporte técnico</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right content - Dashboard Image */}
|
||||
<div
|
||||
ref={imageRef}
|
||||
className="relative lg:pl-8"
|
||||
style={{
|
||||
transform: `perspective(1000px) rotateY(${mousePos.x * 0.5}deg) rotateX(${-mousePos.y * 0.5}deg)`,
|
||||
transition: 'transform 0.1s ease-out',
|
||||
}}
|
||||
>
|
||||
<div className="relative animate-float">
|
||||
{/* Glow effect */}
|
||||
<div className="absolute -inset-4 bg-gradient-to-r from-[#cbf400]/20 to-[#cbf400]/10 rounded-2xl blur-2xl opacity-50" />
|
||||
|
||||
{/* Dashboard image */}
|
||||
<div className="relative rounded-2xl overflow-hidden shadow-2xl border border-[#cbf400]/20">
|
||||
<img
|
||||
src="/hero-dashboard.jpg"
|
||||
alt="Dashboard Avanzato"
|
||||
className="w-full h-auto"
|
||||
/>
|
||||
|
||||
{/* Glossy overlay */}
|
||||
<div
|
||||
className="absolute inset-0 bg-gradient-to-br from-white/10 via-transparent to-transparent pointer-events-none"
|
||||
style={{
|
||||
transform: `translateX(${mousePos.x}%) translateY(${mousePos.y}%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Floating badge */}
|
||||
<div className="absolute -bottom-4 -left-4 bg-[#1a1a1a] rounded-xl p-4 shadow-xl border border-[#cbf400]/30">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-[#cbf400]/20 flex items-center justify-center">
|
||||
<svg className="w-5 h-5 text-[#cbf400]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-white font-bold text-sm">Sistema operacional</div>
|
||||
<div className="text-gray-500 text-xs">Todos os serviços ativos</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom gradient fade */}
|
||||
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-[#0e0e0e] to-transparent" />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
@@ -0,0 +1,184 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Menu, X, Phone, ChevronDown } from 'lucide-react';
|
||||
import { LOGO, NAV_LINKS, CONTACT } from '../config/site';
|
||||
|
||||
const Navigation = () => {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [isServicesOpen, setIsServicesOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 50);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Fechar menu mobile ao mudar de rota
|
||||
useEffect(() => {
|
||||
setIsMobileMenuOpen(false);
|
||||
setIsServicesOpen(false);
|
||||
}, [location]);
|
||||
|
||||
const isActive = (path: string) => {
|
||||
if (path === '/') {
|
||||
return location.pathname === '/';
|
||||
}
|
||||
return location.pathname.startsWith(path);
|
||||
};
|
||||
|
||||
// Renderiza o logo (SVG inline ou imagem)
|
||||
const renderLogo = () => {
|
||||
if (LOGO.imageUrl) {
|
||||
return <img src={LOGO.imageUrl} alt={LOGO.text} className="h-10 w-auto" />;
|
||||
}
|
||||
return (
|
||||
<div dangerouslySetInnerHTML={{ __html: LOGO.svgInline }} className="flex-shrink-0" />
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className={`nav-sticky ${isScrolled ? 'scrolled' : ''}`}>
|
||||
<div className="container-custom">
|
||||
<div className="flex items-center justify-between h-20">
|
||||
{/* Logo - importado de config/site.ts */}
|
||||
<Link to={LOGO.href} className="flex items-center gap-3">
|
||||
{renderLogo()}
|
||||
{LOGO.text && (
|
||||
<span className="text-2xl font-bold text-white tracking-tight">
|
||||
{LOGO.text}
|
||||
<span className="text-[#cbf400]">{LOGO.textHighlight}</span>
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation - importado de config/site.ts */}
|
||||
<div className="hidden lg:flex items-center gap-8">
|
||||
{NAV_LINKS.map((link) => (
|
||||
<div key={link.name} className="relative">
|
||||
{link.submenu ? (
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={() => setIsServicesOpen(true)}
|
||||
onMouseLeave={() => setIsServicesOpen(false)}
|
||||
>
|
||||
<button
|
||||
className={`flex items-center gap-1 nav-link text-sm font-medium transition-colors ${
|
||||
link.submenu.some(s => location.pathname.startsWith(s.href))
|
||||
? 'text-[#cbf400]'
|
||||
: 'text-gray-300 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{isServicesOpen && (
|
||||
<div className="absolute top-full left-0 mt-2 w-56 bg-[#1a1a1a] rounded-xl border border-white/10 shadow-xl py-2 z-50">
|
||||
{link.submenu.map((sub) => (
|
||||
<Link
|
||||
key={sub.name}
|
||||
to={sub.href}
|
||||
className="block px-4 py-2.5 text-sm text-gray-300 hover:text-[#cbf400] hover:bg-white/5 transition-colors"
|
||||
>
|
||||
{sub.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Link
|
||||
to={link.href}
|
||||
className={`nav-link text-sm font-medium transition-colors ${
|
||||
isActive(link.href) ? 'text-[#cbf400]' : 'text-gray-300 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Desktop CTA - importado de config/site.ts */}
|
||||
<div className="hidden lg:flex items-center gap-4">
|
||||
<a
|
||||
href={`tel:${CONTACT.phoneRaw}`}
|
||||
className="flex items-center gap-2 text-sm text-gray-400 hover:text-[#cbf400] transition-colors"
|
||||
>
|
||||
<Phone className="w-4 h-4" />
|
||||
{CONTACT.phone}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="lg:hidden p-2 text-gray-300 hover:text-white"
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu - importado de config/site.ts */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className="lg:hidden py-4 border-t border-white/10">
|
||||
<div className="flex flex-col gap-4">
|
||||
{NAV_LINKS.map((link) => (
|
||||
<div key={link.name}>
|
||||
{link.submenu ? (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setIsServicesOpen(!isServicesOpen)}
|
||||
className="flex items-center justify-between w-full text-gray-300 hover:text-[#cbf400] transition-colors py-2"
|
||||
>
|
||||
<span className="font-medium">{link.name}</span>
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{isServicesOpen && (
|
||||
<div className="pl-4 mt-2 space-y-2">
|
||||
{link.submenu.map((sub) => (
|
||||
<Link
|
||||
key={sub.name}
|
||||
to={sub.href}
|
||||
className="block text-sm text-gray-400 hover:text-[#cbf400] py-1"
|
||||
>
|
||||
{sub.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Link
|
||||
to={link.href}
|
||||
className={`block py-2 font-medium transition-colors ${
|
||||
isActive(link.href) ? 'text-[#cbf400]' : 'text-gray-300 hover:text-[#cbf400]'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<a
|
||||
href={`tel:${CONTACT.phoneRaw}`}
|
||||
className="flex items-center gap-2 text-sm text-gray-400 hover:text-[#cbf400] pt-4 border-t border-white/10"
|
||||
>
|
||||
<Phone className="w-4 h-4" />
|
||||
{CONTACT.phone}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
||||
@@ -0,0 +1,298 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { ArrowRight, Server, Cloud, Shield, Headphones, Network, Phone, HardDrive, Cpu, ClipboardCheck, Monitor } from 'lucide-react';
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
interface Service {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
icon: React.ReactNode;
|
||||
features: string[];
|
||||
link: string;
|
||||
}
|
||||
|
||||
const services: Service[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'TI Gerenciada',
|
||||
description: 'Monitoramento 24/7 de toda sua infraestrutura. Foco no seu negócio enquanto cuidamos da sua tecnologia.',
|
||||
image: '/service-ti.png',
|
||||
icon: <Server className="w-6 h-6" />,
|
||||
features: ['Monitoramento proativo', 'Gestão de infraestrutura', 'Backup automatizado', 'Relatórios mensais'],
|
||||
link: '/servicos/ti-gerenciada',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Cloud Computing',
|
||||
description: 'Migração e gestão de ambientes em nuvem. Escalabilidade e segurança para seu crescimento.',
|
||||
image: '/service-cloud.png',
|
||||
icon: <Cloud className="w-6 h-6" />,
|
||||
features: ['Migração para nuvem', 'AWS/Azure/GCP', 'Otimização de custos', 'Arquitetura serverless'],
|
||||
link: '/servicos/cloud-computing',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Segurança',
|
||||
description: 'Proteção contra ameaças digitais. Segurança de ponta a ponta para seus dados e sistemas.',
|
||||
image: '/service-security.png',
|
||||
icon: <Shield className="w-6 h-6" />,
|
||||
features: ['Firewall gerenciado', 'Antivírus corporativo', 'VPN segura', 'Auditoria de segurança'],
|
||||
link: '/servicos/seguranca',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Suporte Técnico',
|
||||
description: 'Atendimento ágil para seus colaboradores. Resolução rápida de problemas técnicos.',
|
||||
image: '/service-support.png',
|
||||
icon: <Headphones className="w-6 h-6" />,
|
||||
features: ['Help desk 24/7', 'Suporte remoto', 'Atendimento presencial', 'Base de conhecimento'],
|
||||
link: '/servicos/suporte',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Infraestrutura de Redes',
|
||||
description: 'Redes corporativas seguras, rápidas e escaláveis. Cabeamento e configuração profissional.',
|
||||
image: '/service-ti.png',
|
||||
icon: <Network className="w-6 h-6" />,
|
||||
features: ['Cabeamento estruturado', 'Wi-Fi corporativo', 'VLANs', 'Switches gerenciáveis'],
|
||||
link: '/servicos/redes',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'PABX em Nuvem',
|
||||
description: 'Telefonia IP moderna e econômica. Reduza custos e modernize sua comunicação.',
|
||||
image: '/service-support.png',
|
||||
icon: <Phone className="w-6 h-6" />,
|
||||
features: ['Ramais remotos', 'URA', 'Gravação de chamadas', 'Integração CRM'],
|
||||
link: '/servicos/pabx',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: 'Backup Corporativo',
|
||||
description: 'Proteção total contra perda de dados. Backup em nuvem e recuperação de desastres.',
|
||||
image: '/service-cloud.png',
|
||||
icon: <HardDrive className="w-6 h-6" />,
|
||||
features: ['Backup automático', 'Proteção ransomware', 'Recuperação rápida', 'DRaaS'],
|
||||
link: '/servicos/backup',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: 'Servidores',
|
||||
description: 'Implantação e virtualização de servidores. Alta performance e disponibilidade.',
|
||||
image: '/service-ti.png',
|
||||
icon: <Cpu className="w-6 h-6" />,
|
||||
features: ['Virtualização', 'Servidor de arquivos', 'Alta disponibilidade', 'Redução de custos'],
|
||||
link: '/servicos/servidores',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: 'Consultoria em TI',
|
||||
description: 'Planejamento estratégico tecnológico. Diagnóstico e roadmap para sua empresa.',
|
||||
image: '/about-image.jpg',
|
||||
icon: <ClipboardCheck className="w-6 h-6" />,
|
||||
features: ['Diagnóstico', 'Planejamento', 'Auditoria', 'Roadmap estratégico'],
|
||||
link: '/servicos/consultoria',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: 'Monitoramento e NOC',
|
||||
description: 'Visibilidade total da sua infraestrutura. Monitoramento 24/7 com alertas em tempo real.',
|
||||
image: '/service-ti.png',
|
||||
icon: <Monitor className="w-6 h-6" />,
|
||||
features: ['Monitoramento 24/7', 'Alertas em tempo real', 'Painéis personalizados', 'Prevenção de falhas'],
|
||||
link: '/servicos/noc',
|
||||
},
|
||||
];
|
||||
|
||||
const ServiceCard = ({ service }: { service: Service }) => {
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!cardRef.current) return;
|
||||
const rect = cardRef.current.getBoundingClientRect();
|
||||
const x = (e.clientX - rect.left - rect.width / 2) / 20;
|
||||
const y = (e.clientY - rect.top - rect.height / 2) / 20;
|
||||
setMousePos({ x, y });
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={cardRef}
|
||||
className="service-card-border group relative bg-[#1a1a1a] rounded-2xl overflow-hidden shadow-lg cursor-pointer border border-white/5"
|
||||
style={{
|
||||
transform: isHovered
|
||||
? `perspective(1000px) rotateX(${-mousePos.y}deg) rotateY(${mousePos.x}deg) translateZ(20px)`
|
||||
: 'perspective(1000px) rotateX(0) rotateY(0) translateZ(0)',
|
||||
transition: 'transform 0.3s ease-out',
|
||||
}}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => {
|
||||
setIsHovered(false);
|
||||
setMousePos({ x: 0, y: 0 });
|
||||
}}
|
||||
>
|
||||
{/* Image container */}
|
||||
<div className="relative h-48 bg-gradient-to-br from-[#2a2a2a] to-[#1a1a1a] overflow-hidden">
|
||||
<img
|
||||
src={service.image}
|
||||
alt={service.title}
|
||||
className="w-full h-full object-contain p-6 transition-transform duration-500 group-hover:scale-110"
|
||||
/>
|
||||
|
||||
{/* Icon badge */}
|
||||
<div className="absolute top-4 left-4 w-12 h-12 rounded-xl bg-[#0e0e0e] shadow-lg flex items-center justify-center text-[#cbf400] border border-[#cbf400]/20">
|
||||
{service.icon}
|
||||
</div>
|
||||
|
||||
{/* Gradient overlay on hover */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-[#cbf400]/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-5">
|
||||
<h3 className="text-lg font-bold text-white mb-2 group-hover:text-[#cbf400] transition-colors">
|
||||
{service.title}
|
||||
</h3>
|
||||
<p className="text-gray-400 text-sm mb-4 line-clamp-2">
|
||||
{service.description}
|
||||
</p>
|
||||
|
||||
{/* Features list */}
|
||||
<ul className="space-y-1.5 mb-4">
|
||||
{service.features.slice(0, 3).map((feature, i) => (
|
||||
<li key={i} className="flex items-center gap-2 text-xs text-gray-300">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-[#cbf400]" />
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* CTA */}
|
||||
<a
|
||||
href={service.link}
|
||||
className="inline-flex items-center gap-2 text-[#cbf400] font-semibold text-sm group/link"
|
||||
>
|
||||
Saiba mais
|
||||
<ArrowRight className="w-4 h-4 transition-transform group-hover/link:translate-x-1" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Glow border effect */}
|
||||
<div className="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none">
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-r from-[#cbf400]/20 via-[#cbf400]/10 to-[#cbf400]/20 blur-sm" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Services = () => {
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
const titleRef = useRef<HTMLDivElement>(null);
|
||||
const cardsRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
gsap.fromTo(
|
||||
titleRef.current,
|
||||
{ y: 50, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.8,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: titleRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const cards = cardsRef.current?.querySelectorAll('.service-card-border');
|
||||
if (cards) {
|
||||
gsap.fromTo(
|
||||
cards,
|
||||
{ y: 100, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.8,
|
||||
stagger: 0.08,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: cardsRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}, sectionRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
id="services"
|
||||
ref={sectionRef}
|
||||
className="section-padding bg-[#0e0e0e] relative overflow-hidden"
|
||||
>
|
||||
{/* Background decoration */}
|
||||
<div className="absolute top-0 right-0 w-96 h-96 bg-[#cbf400]/5 rounded-full blur-3xl -translate-y-1/2 translate-x-1/2" />
|
||||
<div className="absolute bottom-0 left-0 w-96 h-96 bg-[#cbf400]/5 rounded-full blur-3xl translate-y-1/2 -translate-x-1/2" />
|
||||
|
||||
<div className="container-custom relative z-10">
|
||||
{/* Section header */}
|
||||
<div ref={titleRef} className="text-center max-w-2xl mx-auto mb-16">
|
||||
<span className="inline-block px-4 py-2 rounded-full bg-[#cbf400]/10 text-[#cbf400] text-sm font-semibold mb-4 border border-[#cbf400]/20">
|
||||
Nossas Soluções
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl lg:text-5xl font-black text-white mb-4">
|
||||
Serviços completos em{' '}
|
||||
<span className="text-gradient">Tecnologia</span>
|
||||
</h2>
|
||||
<p className="text-gray-400 text-lg">
|
||||
Oferecemos soluções integradas para transformar a infraestrutura de TI da sua empresa
|
||||
em um diferencial competitivo.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Services grid */}
|
||||
<div
|
||||
ref={cardsRef}
|
||||
className="grid md:grid-cols-2 lg:grid-cols-5 gap-5"
|
||||
>
|
||||
{services.map((service) => (
|
||||
<ServiceCard key={service.id} service={service} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bottom CTA */}
|
||||
<div className="text-center mt-12">
|
||||
<p className="text-gray-400 mb-4">
|
||||
Precisa de uma solução personalizada?
|
||||
</p>
|
||||
<a
|
||||
href="https://api.whatsapp.com/send?phone=551148101704&text=Olá!%20Gostaria%20de%20uma%20solução%20personalizada"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn-primary inline-flex items-center gap-2"
|
||||
>
|
||||
Fale com um consultor
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Services;
|
||||
@@ -0,0 +1,371 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { Star, Building2, MapPin } from 'lucide-react';
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
interface Testimonial {
|
||||
id: number;
|
||||
cliente: string;
|
||||
responsavel: string;
|
||||
cargo?: string;
|
||||
segmento: string;
|
||||
cidade: string;
|
||||
quote: string;
|
||||
destaque: string;
|
||||
icone: string;
|
||||
}
|
||||
|
||||
const testimonials: Testimonial[] = [
|
||||
{
|
||||
id: 1,
|
||||
cliente: "Moraes & Moraes Advogados",
|
||||
responsavel: "Edilson Fernando de Moraes",
|
||||
segmento: "Advocacia",
|
||||
cidade: "São Paulo",
|
||||
quote: "Evitamos um investimento de mais de R$ 15.000 com a solução aplicada.",
|
||||
destaque: "Economia de R$ 15.000",
|
||||
icone: "⚖️"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
cliente: "Divas Car",
|
||||
responsavel: "Joelma Rodrigues",
|
||||
segmento: "Centro Automotivo",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Hoje todo o sistema está estruturado com backup em nuvem e funcionando perfeitamente.",
|
||||
destaque: "Backup em nuvem implementado",
|
||||
icone: "🚘"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
cliente: "Toyota Arquitetura",
|
||||
responsavel: "Elio Toyota",
|
||||
segmento: "Arquitetura",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Fui atendido prontamente, mesmo fora do horário, salvando um contrato importante.",
|
||||
destaque: "Atendimento fora do horário",
|
||||
icone: "🪵"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
cliente: "R2 Car",
|
||||
responsavel: "R2 Car Centro Automotivo",
|
||||
segmento: "Pintura Automotiva",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Em menos de uma hora todos os problemas foram resolvidos e tudo voltou a funcionar perfeitamente.",
|
||||
destaque: "Resolvido em menos de 1 hora",
|
||||
icone: "🚗"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
cliente: "Simpercom Impermeabilizações",
|
||||
responsavel: "João de Paula",
|
||||
segmento: "Impermeabilização",
|
||||
cidade: "Guarulhos",
|
||||
quote: "O problema foi resolvido com excelência, fortalecendo ainda mais nossa parceria.",
|
||||
destaque: "Resolvido com excelência",
|
||||
icone: "🧱"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
cliente: "Summa Desenvolvimento Humano",
|
||||
responsavel: "Silvia Pedrosa Luiz",
|
||||
segmento: "Coaching",
|
||||
cidade: "São Paulo",
|
||||
quote: "Os problemas foram resolvidos com rapidez e eficiência, mesmo para quem não entende de tecnologia.",
|
||||
destaque: "Rapidez e eficiência",
|
||||
icone: "🧠"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
cliente: "Visão Contabilidade",
|
||||
responsavel: "Antonio Gonçalves",
|
||||
segmento: "Contabilidade",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Passei a indicar os serviços com total confiança pela qualidade e agilidade.",
|
||||
destaque: "Total confiança",
|
||||
icone: "📊"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
cliente: "SEG1 Corretora de Seguros",
|
||||
responsavel: "Jorge Luiz Braga",
|
||||
segmento: "Seguros",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Melhoramos todos os computadores com apenas 10% do investimento necessário.",
|
||||
destaque: "90% de economia",
|
||||
icone: "🛡️"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
cliente: "Apoio 24 Horas",
|
||||
responsavel: "Roberto",
|
||||
cargo: "Sócio Proprietário",
|
||||
segmento: "Segurança / Monitoramento",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Garantimos a operação 24h mesmo com falhas de internet e energia, graças à estrutura implementada pela Avanzato.",
|
||||
destaque: "Operação 24h garantida",
|
||||
icone: "🛡️"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
cliente: "Lucio Seguros",
|
||||
responsavel: "Lucio Benevenuto",
|
||||
segmento: "Seguros",
|
||||
cidade: "Guarulhos",
|
||||
quote: "O equipamento ganhou performance e o atendimento foi rápido e eficiente.",
|
||||
destaque: "Performance melhorada",
|
||||
icone: "🛡️"
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
cliente: "Caroni Reis Advogados",
|
||||
responsavel: "Jorge Paulo Caroni Reis",
|
||||
segmento: "Advocacia",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Fui atendido com dedicação e profissionalismo, sempre dentro do prazo e orçamento.",
|
||||
destaque: "Dentro do prazo",
|
||||
icone: "⚖️"
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
cliente: "Trilha Empregos",
|
||||
responsavel: "Suelia Luz",
|
||||
segmento: "Recrutamento",
|
||||
cidade: "Guarulhos",
|
||||
quote: "Superou minhas expectativas com um atendimento atencioso, competente e eficiente.",
|
||||
destaque: "Superou expectativas",
|
||||
icone: "🧩"
|
||||
}
|
||||
];
|
||||
|
||||
// Lista de empresas que confiam na Avanzato (logos/nomes no final da seção)
|
||||
const empresasQueConfiam = [
|
||||
"Apoio24horas",
|
||||
"GateOneServitel",
|
||||
"Alamo",
|
||||
"Quantica",
|
||||
"Mourad Nadi",
|
||||
"RMA",
|
||||
"Ganiko",
|
||||
"Dcarvalho",
|
||||
"Divas Car",
|
||||
"Thalls",
|
||||
"Garuppy",
|
||||
"Moraes",
|
||||
"Dellacont",
|
||||
"Madri",
|
||||
"EFLAW",
|
||||
"BST Security",
|
||||
"Piso.com",
|
||||
"RollPrime"
|
||||
];
|
||||
|
||||
// Schema.org JSON-LD para SEO
|
||||
const generateSchemaOrg = () => {
|
||||
const reviews = testimonials.map(t => ({
|
||||
"@type": "Review",
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": t.responsavel
|
||||
},
|
||||
"reviewRating": {
|
||||
"@type": "Rating",
|
||||
"ratingValue": 5,
|
||||
"bestRating": 5
|
||||
},
|
||||
"reviewBody": t.quote,
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"name": t.cliente
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "Avanzato Tecnologia",
|
||||
"url": "https://avanzato.com.br",
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": "5",
|
||||
"bestRating": "5",
|
||||
"ratingCount": String(reviews.length),
|
||||
"reviewCount": String(reviews.length)
|
||||
},
|
||||
"review": reviews
|
||||
};
|
||||
};
|
||||
|
||||
const TestimonialCard = ({ testimonial }: { testimonial: Testimonial }) => {
|
||||
return (
|
||||
<div className="testimonial-card bg-[#1a1a1a] rounded-2xl p-6 border border-white/5 hover:border-[#cbf400]/30 transition-all duration-300 h-full flex flex-col">
|
||||
{/* Icon and stars */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className="text-2xl">{testimonial.icone}</span>
|
||||
<div className="flex gap-0.5">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<Star key={i} className="w-4 h-4 text-[#cbf400] fill-[#cbf400]" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quote */}
|
||||
<blockquote className="text-gray-300 text-sm leading-relaxed mb-4 flex-1">
|
||||
"{testimonial.quote}"
|
||||
</blockquote>
|
||||
|
||||
{/* Destaque badge */}
|
||||
<div className="mb-4">
|
||||
<span className="inline-block px-3 py-1 bg-[#cbf400]/10 text-[#cbf400] text-xs font-medium rounded-full border border-[#cbf400]/20">
|
||||
{testimonial.destaque}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Client info */}
|
||||
<div className="flex items-center gap-3 pt-4 border-t border-white/5">
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-[#cbf400]/20 to-[#cbf400]/5 flex items-center justify-center text-[#cbf400] font-bold">
|
||||
{testimonial.responsavel.charAt(0)}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-white font-semibold text-sm truncate">{testimonial.responsavel}</p>
|
||||
<div className="flex items-center gap-2 text-gray-500 text-xs">
|
||||
<Building2 className="w-3 h-3 flex-shrink-0" />
|
||||
<span className="truncate">{testimonial.cliente}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-gray-600 text-xs">
|
||||
<MapPin className="w-3 h-3 flex-shrink-0" />
|
||||
<span>{testimonial.cidade}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Testimonials = () => {
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
const titleRef = useRef<HTMLDivElement>(null);
|
||||
const cardsRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
gsap.fromTo(
|
||||
titleRef.current,
|
||||
{ y: 50, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.8,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: titleRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const cards = cardsRef.current?.querySelectorAll('.testimonial-card');
|
||||
if (cards) {
|
||||
gsap.fromTo(
|
||||
cards,
|
||||
{ y: 60, opacity: 0 },
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 0.7,
|
||||
stagger: 0.08,
|
||||
ease: 'expo.out',
|
||||
scrollTrigger: {
|
||||
trigger: cardsRef.current,
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reverse',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}, sectionRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
const schemaData = generateSchemaOrg();
|
||||
|
||||
return (
|
||||
<section
|
||||
id="depoimentos"
|
||||
ref={sectionRef}
|
||||
className="section-padding bg-[#0e0e0e] relative overflow-hidden"
|
||||
>
|
||||
{/* Schema.org JSON-LD */}
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaData) }}
|
||||
/>
|
||||
|
||||
{/* Background decoration */}
|
||||
<div className="absolute top-0 left-0 w-[500px] h-[500px] bg-[#cbf400]/5 rounded-full blur-3xl -translate-y-1/2 -translate-x-1/2" />
|
||||
<div className="absolute bottom-0 right-0 w-[500px] h-[500px] bg-[#cbf400]/5 rounded-full blur-3xl translate-y-1/2 translate-x-1/2" />
|
||||
|
||||
<div className="container-custom relative z-10">
|
||||
{/* Section header */}
|
||||
<div ref={titleRef} className="text-center max-w-3xl mx-auto mb-16">
|
||||
<span className="inline-block px-4 py-2 rounded-full bg-[#cbf400]/10 text-[#cbf400] text-sm font-semibold mb-4 border border-[#cbf400]/20">
|
||||
Depoimentos
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl lg:text-5xl font-black text-white mb-4">
|
||||
O que nossos <span className="text-gradient">clientes dizem</span>
|
||||
</h2>
|
||||
<p className="text-gray-400 text-lg">
|
||||
Mais de 500 empresas confiam na Avanzato para cuidar da sua tecnologia.
|
||||
Veja o que dizem sobre nossos serviços.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Stats bar */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 mb-16">
|
||||
{[
|
||||
{ valor: "500+", label: "Clientes atendidos" },
|
||||
{ valor: "11", label: "Anos de experiência" },
|
||||
{ valor: "4.9", label: "Nota média" },
|
||||
{ valor: "98%", label: "Taxa de satisfação" },
|
||||
].map((stat, i) => (
|
||||
<div key={i} className="text-center bg-[#1a1a1a] rounded-xl p-6 border border-white/5">
|
||||
<div className="text-3xl md:text-4xl font-black text-[#cbf400] mb-1">{stat.valor}</div>
|
||||
<div className="text-gray-500 text-sm">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Testimonials grid */}
|
||||
<div
|
||||
ref={cardsRef}
|
||||
className="grid md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5"
|
||||
>
|
||||
{testimonials.map((testimonial) => (
|
||||
<TestimonialCard key={testimonial.id} testimonial={testimonial} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Client logos section */}
|
||||
<div className="mt-20">
|
||||
<p className="text-center text-gray-500 text-sm mb-8 uppercase tracking-wider">
|
||||
Empresas que confiam na Avanzato
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center items-center gap-x-8 gap-y-4 opacity-60">
|
||||
{empresasQueConfiam.map((empresa, i) => (
|
||||
<span key={i} className="text-gray-400 font-medium text-sm hover:text-[#cbf400] transition-colors">
|
||||
{empresa}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Testimonials;
|
||||
Reference in New Issue
Block a user