feat: Implement initial portfolio website structure with core components and a new post creator tool.

This commit is contained in:
Evandro Fonseca Junior committed 2026-03-07 06:50:56 -03:00
1 parent 036363b521
commit aa07d6faeb
15 files changed
+1337 -1855

No files matched your search

+62 -166
View File
@@ -1,194 +1,90 @@
"use client";
import { Section } from "./Section";
import React from "react";
import { motion } from "framer-motion";
import { LucideIcon, Brain, Zap, Target, Rocket, Users, Lightbulb, Code, Shield, Terminal, Cpu } from "lucide-react";
import Squares from "@/components/Squares";
// --- Types & Interfaces (Tipagem Forte para Previsibilidade) ---
interface StoryStep {
icon: LucideIcon;
title: string;
content: string;
gradient: string;
accent: string;
id: string;
}
interface CoreValue {
icon: LucideIcon;
title: string;
description: string;
}
// --- Data (Desacoplado para facilitar manutenção/CMS) ---
const STORY_DATA: StoryStep[] = [
const STORY_DATA = [
{
id: "KERNEL_INIT",
icon: Brain,
title: "Engenharia de Fundamentos",
content: "Minha mentalidade é pautada pela redução de entropia. Não apenas escrevo código; projeto sistemas onde a escalabilidade é uma propriedade intrínseca da arquitetura, não um ajuste posterior.",
gradient: "from-blue-600 to-cyan-500",
accent: "text-blue-400",
title: "Engenharia de fundamentos",
content:
"Projeto sistemas com foco em previsibilidade, manutenção e crescimento real. Escala entra na arquitetura desde o início.",
},
{
id: "RUNTIME_EXEC",
icon: Zap,
title: "Entrega sob Restrições",
content: "Em 3 anos de atuação profissional, especializei-me em cenários de alta criticidade. Atuei na ponte entre sistemas legados e microserviços, garantindo a resolução do problema.",
gradient: "from-purple-600 to-pink-600",
accent: "text-purple-400",
title: "Entrega sob restrições",
content:
"Atuo bem em cenários com legado, prazos apertados e decisões de produto em movimento, sem sacrificar legibilidade técnica.",
},
];
const VALUES_DATA: CoreValue[] = [
const VALUES_DATA = [
{
icon: Code,
title: "Pragmatismo Arquitetural",
description: "Priorizo a manutenibilidade e a resiliência. Código limpo é o meio, sistemas que geram ROI são o fim."
title: "Pragmatismo arquitetural",
description:
"Decisões guiadas por ROI, operabilidade e clareza para o time que mantém o sistema depois.",
},
{
icon: Brain,
title: "Fundacionalismo Técnico",
description: "Domínio profundo de estruturas de dados e protocolos. Tecnologias mudam, fundamentos são perenes."
title: "Fundação técnica",
description:
"Fundamentos fortes em dados, protocolos e modelagem para evitar soluções frágeis e modismos vazios.",
},
{
icon: Users,
title: "Propriedade e Ownership",
description: "Comunicação de baixa latência e responsabilidade radical sobre cada entrega e decisão técnica."
title: "Ownership real",
description:
"Responsabilidade sobre entrega, comunicação e qualidade final, não só sobre o trecho de código escrito.",
},
{
icon: Shield,
title: "Security by Design",
description: "Integridade e conformidade técnica (GDPR/LGPD) tratadas como requisitos funcionais, não opcionais."
title: "Security by design",
description:
"Integridade, proteção de dados e conformidade tratadas como parte do produto, não como remendo.",
},
];
// --- Micro-componentes (Feynman Technique: Simplicidade e Reuso) ---
const SectionHeader = () => (
<div className="text-center md:text-left mb-24 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-6"
>
<Terminal className="w-3 h-3" />
<span>WHOAMI.sys</span>
</motion.div>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
>
Engenharia <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-cyan-400">Nuclear</span>
</motion.h2>
<p className="mt-6 text-lg text-slate-400 max-w-2xl leading-relaxed font-light border-l-2 border-slate-800 pl-6">
"Transformando complexidade técnica em valor de negócio através da engenharia."
</p>
</div>
);
const StoryCard = ({ step, index }: { step: StoryStep; index: number }) => {
const isEven = index % 2 === 0;
return (
<motion.div
initial={{ opacity: 0, x: isEven ? -30 : 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: "-100px" }}
className={`flex flex-col ${isEven ? "md:flex-row" : "md:flex-row-reverse"} items-stretch gap-8 mb-20 relative`}
>
<div className="flex-1 w-full">
<div className="group relative h-full p-8 rounded-3xl bg-zinc-900/40 border border-white/5 hover:border-blue-500/30 transition-all duration-500 shadow-xl backdrop-blur-xl overflow-hidden">
{/* Background Glow */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="relative z-10">
<div className="flex items-center gap-4 mb-6">
<div className="p-3 rounded-xl bg-blue-500/10 border border-blue-500/20 text-blue-400 group-hover:scale-110 transition-transform duration-300">
<step.icon className="w-6 h-6" />
</div>
<span className="text-xs font-mono text-slate-500 uppercase tracking-widest">
{step.id}
</span>
</div>
<h3 className="text-2xl font-bold text-white mb-4 group-hover:text-blue-400 transition-colors">{step.title}</h3>
<p className="text-slate-400 leading-relaxed text-sm">{step.content}</p>
</div>
</div>
</div>
{/* Connection Line & Node */}
<div className="hidden md:flex flex-col items-center justify-center w-12 relative">
<div className="absolute top-0 bottom-0 w-[1px] bg-slate-800 group-hover:bg-blue-500/50 transition-colors" />
<div className="w-4 h-4 rounded-full bg-slate-900 border-2 border-slate-700 z-10 group-hover:border-blue-500 transition-colors" />
</div>
<div className="flex-1 hidden md:block" />
</motion.div>
);
};
export default function About() {
return (
<section id="about" className="relative py-32">
{/* Background Gradients */}
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
<div className="absolute top-[-10%] right-[-10%] w-[600px] h-[600px] bg-blue-900/10 rounded-full blur-[120px]" />
<div className="absolute bottom-[-10%] left-[-10%] w-[600px] h-[600px] bg-purple-900/10 rounded-full blur-[120px]" />
</div>
<div className="container max-w-6xl mx-auto px-6 relative z-10">
<SectionHeader />
<div className="relative mt-20">
{/* Center Line for Mobile/Desktop */}
<div className="absolute left-6 md:left-1/2 top-0 bottom-0 w-[1px] bg-slate-800 md:-translate-x-1/2 hidden md:block" />
{STORY_DATA.map((step, idx) => (
<StoryCard key={idx} step={step} index={idx} />
<Section
id="about"
eyebrow="WHOAMI.sys"
title="Engenharia orientada por clareza, não por ruído"
description="Transformo complexidade técnica em sistemas estáveis, operáveis e alinhados ao negócio."
aside={
<div className="mt-sp-4 ds-metric">
<span className="inline-block h-1.5 w-1.5 rounded-full bg-slash" />
Garuva, SC · Remote worldwide
</div>
}
>
<div className="ds-grid-2">
<div className="ds-grid-2">
{STORY_DATA.map((step) => (
<article key={step.id} className="ds-card">
<div className="ds-kicker">{step.id}</div>
<h3 className="ds-card-title">{step.title}</h3>
<p className="ds-copy">{step.content}</p>
</article>
))}
</div>
{/* Grid de Valores com Pareto 80/20 */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mt-32">
{VALUES_DATA.map((val, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: i * 0.1 }}
className="p-6 rounded-2xl bg-zinc-900/30 border border-white/5 hover:border-blue-500/20 hover:bg-zinc-900/50 transition-all duration-300 group"
>
<div className="mb-4 p-2 w-fit rounded-lg bg-slate-800/50 text-slate-400 group-hover:text-blue-400 group-hover:bg-blue-500/10 transition-colors">
<val.icon className="w-5 h-5" />
</div>
<h4 className="text-white font-bold mb-2 text-sm uppercase tracking-wide group-hover:text-blue-200 transition-colors">{val.title}</h4>
<p className="text-slate-500 text-xs leading-relaxed group-hover:text-slate-400 transition-colors">{val.description}</p>
</motion.div>
))}
</div>
{/* SEO Semantic Content - Visible but subtle */}
<div className="mt-24 pt-8 border-t border-slate-900">
<div className="flex items-center justify-center gap-2 text-slate-700 text-xs font-mono uppercase tracking-widest mb-4">
<Cpu className="w-3 h-3" />
<span>System_Signature</span>
</div>
<p className="text-[10px] text-slate-700 text-center max-w-2xl mx-auto leading-relaxed font-mono">
Evandro Fonseca Junior :: Engenheiro Full-Stack <br />
Next.js / React / Angular / Node.js / Tauri / PostgreSQL / IoT <br />
Garuva, Santa Catarina, Brasil :: Lat: -26.0264, Long: -48.8544
<div className="ds-card">
<div className="ds-kicker">System Signature</div>
<h3 className="ds-card-title">Base técnica e postura de entrega</h3>
<p className="ds-copy">
Evandro Fonseca Junior. Engenheiro Full-Stack com foco em Next.js,
React, Node.js, Tauri, integrações e arquitetura pragmática.
</p>
<div className="ds-grid-2 mt-sp-6">
{VALUES_DATA.map((value) => (
<div
key={value.title}
className="border-t border-[var(--border)] pt-sp-4"
>
<div className="text-[11px] font-medium uppercase tracking-[0.08em]">
{value.title}
</div>
<p className="ds-copy !mt-sp-2">{value.description}</p>
</div>
))}
</div>
</div>
</div>
</section>
</Section>
);
}
}
+98 -198
View File
@@ -1,223 +1,123 @@
"use client";
"use client";
import React, { useState } from "react";
import { motion } from "framer-motion";
import { Send, User, Mail, MessageSquare, Sparkles } from "lucide-react";
import { ShimmerButton as MagicShimmerButton } from "@/components/magicui/shimmer-button";
import Squares from "@/components/Squares";
import type React from "react";
import { useState } from "react";
import { Section } from "./Section";
// --- CONFIGURAÇÃO DE ENGENHARIA ---
// Substitua pelo seu número real: CódigoPaís + DDD + Número (sem espaços ou símbolos)
const WHATSAPP_NUMBER = "5547992742365";
interface FormData {
name: string;
email: string;
subject: string;
message: string;
}
/**
* Wrapper Component: Resolve o erro de tipagem TS2322 (IntrinsicAttributes)
* Estendemos a interface para aceitar propriedades de estilo customizadas.
*/
interface ExtendedShimmerProps extends React.ComponentProps<typeof MagicShimmerButton> {
shimmerColor?: string;
shimmerSize?: string;
type?: "button" | "submit" | "reset";
disabled?: boolean;
}
const CustomShimmerButton = ({
children,
className,
shimmerColor = "#3b82f6",
shimmerSize = "0.1em",
...props
}: ExtendedShimmerProps) => {
return (
<MagicShimmerButton
{...props}
className={className}
// @ts-ignore - Ignora restrição de tipos da lib original se necessário
shimmerColor={shimmerColor}
shimmerSize={shimmerSize}
>
{children}
</MagicShimmerButton>
);
};
export default function Contact() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [formData, setFormData] = useState<FormData>({
const [formData, setFormData] = useState({
name: "",
email: "",
subject: "full-stack",
message: "",
});
const handleSubmit = async (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
await new Promise((resolve) => setTimeout(resolve, 800));
// Definimos os emojis via Unicode Escape para máxima compatibilidade
const icons = {
rocket: "\u{1F680}",
person: "\u{1F464}",
email: "\u{1F4E7}",
tools: "\u{1F6E0}",
chat: "\u{1F4AC}"
};
const messageTemplate = `*NOVA SOLICITAÇÃO DE PROJETO*
*Origem:* Portfólio Profissional
*Prioridade:* Alta (Lead Qualificado)
---
*CLIENTE:* ${formData.name}
*EMAIL:* ${formData.email}
*ÁREA:* ${formData.subject.toUpperCase()}
*DESCRIÇÃO:*
"${formData.message}"
---
_Mensagem gerada via Evandro.dev | Garuva, SC_`;
const messageTemplate = `*NOVA SOLICITACAO DE PROJETO*\n\n*Origem:* Portfolio Profissional\n*Prioridade:* Alta\n\n---\n*CLIENTE:* ${formData.name}\n*EMAIL:* ${formData.email}\n*AREA:* ${formData.subject.toUpperCase()}\n\n*DESCRICAO:*\n"${formData.message}"\n---\n_Mensagem gerada via Evandro.dev | Garuva, SC_`;
const encodedMessage = encodeURIComponent(messageTemplate);
const whatsappUrl = `https://wa.me/${WHATSAPP_NUMBER}?text=${encodedMessage}`;
window.open(whatsappUrl, "_blank", "noopener,noreferrer");
setIsSubmitting(false);
};
const inputStyles =
"w-full bg-black/40 border border-white/5 rounded-xl px-4 py-3 text-slate-200 placeholder:text-slate-600 focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/10 focus:bg-black/60 outline-none transition-all duration-300 font-mono text-sm backdrop-blur-sm group-hover:border-white/10";
const labelStyles =
"text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-2 flex items-center gap-2 transition-colors group-hover:text-slate-400";
return (
<section id="contact" className="w-full py-24 relative">
<div className="w-full max-w-4xl mx-auto px-6 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative bg-zinc-900/40 backdrop-blur-xl border border-white/10 rounded-3xl p-8 md:p-12 shadow-2xl overflow-hidden"
>
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500/0 via-blue-500/50 to-blue-500/0" />
<div className="mb-10 text-center space-y-2">
<h2 className="text-3xl md:text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-b from-white to-white/60">
Vamos Construir o Futuro
</h2>
<p className="text-slate-400 text-sm md:text-base max-w-lg mx-auto">
Descreva seu desafio técnico. Retorno com uma arquitetura sólida em até 24h.
</p>
<Section
id="contact"
eyebrow="CONTACT_PROTOCOL.init"
title="Vamos discutir o próximo sistema"
description="Descreva o contexto, o gargalo ou o produto. O contato abre direto no WhatsApp com briefing estruturado."
dark
>
<div className="ds-grid-2">
<div className="ds-card">
<div className="ds-kicker">Response SLA</div>
<h3 className="ds-card-title">Retorno inicial em até 24h</h3>
<p className="ds-copy">
Ideal para discovery técnico, revisão de arquitetura, rescue de
projeto, otimização de performance ou construção de produto do
zero.
</p>
<div className="mt-sp-6 space-y-sp-4 text-[12px] leading-7 text-[rgba(255,255,255,.7)]">
<div>WhatsApp: +55 47 99274-2365</div>
<div>E-mail: evandrofonsecajunior@gmail.com</div>
<div>Base: Garuva, Santa Catarina · atuação remota</div>
</div>
</div>
<form onSubmit={handleSubmit} className="space-y-8 relative">
<div className="grid md:grid-cols-2 gap-8">
<div className="space-y-3 group">
<label className={labelStyles}>
<User className="w-3 h-3 text-blue-500" /> NOME COMPLETO
</label>
<input
required
type="text"
placeholder="Ex: Alan Turing"
className={inputStyles}
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
/>
</div>
<div className="space-y-3 group">
<label className={labelStyles}>
<Mail className="w-3 h-3 text-blue-500" /> E-MAIL CORPORATIVO
</label>
<input
required
type="email"
placeholder="[email protected]"
className={inputStyles}
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
/>
</div>
</div>
<div className="space-y-3 group">
<label className={labelStyles}>
<Sparkles className="w-3 h-3 text-blue-500" /> TIPO DE PROJETO
</label>
<div className="relative">
<select
className={`${inputStyles} appearance-none cursor-pointer`}
value={formData.subject}
onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
>
<option value="full-stack">Desenvolvimento Full-Stack</option>
<option value="cloud">Cloud & Infrastructure</option>
<option value="iot">Internet of Things (IoT)</option>
<option value="consultancy">Consultoria Técnica Sênior</option>
</select>
<div className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none text-slate-500">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6" /></svg>
</div>
</div>
</div>
<div className="space-y-3 group">
<label className={labelStyles}>
<MessageSquare className="w-3 h-3 text-blue-500" /> DETALHES DO PROJETO
</label>
<textarea
required
rows={5}
placeholder="Descreva os requisitos técnicos e objetivos do sistema..."
className={`${inputStyles} resize-none`}
value={formData.message}
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
/>
</div>
<motion.div whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.99 }} className="pt-6">
<CustomShimmerButton
type="submit"
className="w-full h-14 rounded-xl shadow-lg shadow-blue-500/20"
shimmerColor="#60a5fa"
shimmerSize="0.1em"
disabled={isSubmitting}
>
<div className="flex flex-row items-center justify-center gap-3 w-full h-full">
{isSubmitting ? (
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : (
<>
<span className="text-white font-bold tracking-widest uppercase text-xs md:text-sm">
Iniciar Protocolo de Contato
</span>
<Send className="w-4 h-4 text-white" />
</>
)}
</div>
</CustomShimmerButton>
</motion.div>
<footer className="pt-2">
<p className="text-[10px] text-center text-slate-600 font-mono uppercase tracking-widest opacity-60">
Secured by End-to-End Encryption
</p>
</footer>
</form>
</motion.div>
<form onSubmit={handleSubmit} className="ds-card space-y-sp-5">
<div>
<label htmlFor="contact-name" className="ds-kicker block">
Nome completo
</label>
<input
id="contact-name"
required
type="text"
value={formData.name}
onChange={(e) =>
setFormData({ ...formData, name: e.target.value })
}
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
/>
</div>
<div>
<label htmlFor="contact-email" className="ds-kicker block">
E-mail corporativo
</label>
<input
id="contact-email"
required
type="email"
value={formData.email}
onChange={(e) =>
setFormData({ ...formData, email: e.target.value })
}
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
/>
</div>
<div>
<label htmlFor="contact-subject" className="ds-kicker block">
Tipo de projeto
</label>
<select
id="contact-subject"
value={formData.subject}
onChange={(e) =>
setFormData({ ...formData, subject: e.target.value })
}
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
>
<option value="full-stack">Desenvolvimento Full-Stack</option>
<option value="cloud">Cloud & Infrastructure</option>
<option value="iot">Internet of Things (IoT)</option>
<option value="consultancy">Consultoria Técnica Sênior</option>
</select>
</div>
<div>
<label htmlFor="contact-message" className="ds-kicker block">
Detalhes do projeto
</label>
<textarea
id="contact-message"
required
rows={6}
value={formData.message}
onChange={(e) =>
setFormData({ ...formData, message: e.target.value })
}
className="mt-sp-2 w-full resize-none border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
/>
</div>
<button type="submit" className="ds-link ds-link-accent border-0">
Iniciar protocolo de contato
</button>
</form>
</div>
</section>
</Section>
);
}
}
+44 -170
View File
@@ -1,194 +1,68 @@
"use client";
import React, { useMemo } from "react";
import { motion } from "framer-motion";
import {
ChevronRight,
Terminal,
Cpu,
Network,
Code2
} from "lucide-react";
import { useMemo } from "react";
import { usePortfolioData } from "@/lib/usePortfolioData";
// --- Tipagem e Configuração (Otimização Pareto 80/20) ---
const CONFIG = {
"innovo-tecnologia": { color: "text-blue-400", bg: "bg-blue-400/10", border: "border-blue-400/20", icon: Cpu },
"apolo-energia": { color: "text-emerald-400", bg: "bg-emerald-400/10", border: "border-emerald-400/20", icon: Network },
default: { color: "text-slate-400", bg: "bg-slate-400/10", border: "border-slate-400/20", icon: Code2 }
};
import { Section } from "./Section";
export default function Experience() {
const { data, loading, error } = usePortfolioData();
// Memória: Evita re-processamento de regex em cada re-render (Princípio de Eficiência)
const sortedExperiences = useMemo(() => {
if (!data?.experiences) return [];
return [...data.experiences]
.filter((exp) => exp.featured)
.sort((a, b) => {
const yearA = parseInt(a.period.match(/\d{4}/)?.[0] || "0");
const yearB = parseInt(b.period.match(/\d{4}/)?.[0] || "0");
return yearB - yearA; // Da mais recente para a mais antiga (Padrão Senior)
const yearA = parseInt(a.period.match(/\d{4}/)?.[0] || "0", 10);
const yearB = parseInt(b.period.match(/\d{4}/)?.[0] || "0", 10);
return yearB - yearA;
});
}, [data]);
if (loading) return <ExperienceSkeleton />;
if (loading) return <div className="ds-loading">Loading system logs</div>;
if (error || !data)
return <div className="ds-error">Error loading system logs</div>;
return (
<section id="experience" className="relative py-32">
{/* Background Gradients */}
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
<div className="absolute top-[20%] left-[-10%] w-[500px] h-[500px] bg-blue-900/10 rounded-full blur-[100px]" />
<div className="absolute bottom-[20%] right-[-10%] w-[500px] h-[500px] bg-indigo-900/10 rounded-full blur-[100px]" />
</div>
<div className="max-w-6xl mx-auto px-6 relative z-10">
{/* Header com Identidade Visual */}
<div className="mb-24 text-center md:text-left">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-6"
>
<Terminal className="w-3 h-3" />
<span>SYSTEM_LOGS.history</span>
</motion.div>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
>
Trajetória <span className="bg-gradient-to-r from-blue-500 to-indigo-500 bg-clip-text text-transparent">Profissional</span>
</motion.h2>
</div>
<div className="relative">
{/* Timeline Desktop (Linha Central com Gradiente) */}
<div className="absolute left-[50%] top-0 bottom-0 w-[2px] bg-gradient-to-b from-blue-500/50 via-indigo-500/20 to-transparent hidden lg:block translate-x-[-50%]" />
<div className="space-y-24">
{sortedExperiences.map((exp, index) => (
<ExperienceCard key={exp.id} exp={exp} index={index} />
))}
</div>
</div>
</div>
</section>
);
}
// --- Sub-componente Card (Encapsulamento de Lógica de Transição) ---
function ExperienceCard({ exp, index }: { exp: any, index: number }) {
const isEven = index % 2 === 0;
const config = (CONFIG as any)[exp.company.toLowerCase().replace(/\s+/g, "-")] || CONFIG.default;
const Icon = config.icon;
return (
<div className={`relative flex flex-col ${isEven ? 'lg:flex-row' : 'lg:flex-row-reverse'} items-center gap-8 lg:gap-16`}>
{/* Node Central (O "Ponto" da Timeline) */}
<motion.div
initial={{ scale: 0, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
viewport={{ once: true }}
transition={{ delay: 0.2, duration: 0.5 }}
className="hidden lg:flex absolute left-[50%] top-0 translate-x-[-50%] w-4 h-4 rounded-full bg-blue-500 shadow-[0_0_20px_rgba(59,130,246,0.5)] z-20 items-center justify-center"
>
<div className="w-2 h-2 rounded-full bg-white animate-pulse" />
</motion.div>
{/* Indicador de Data na Timeline */}
<div className="hidden lg:flex flex-1 justify-end items-start pt-2">
<motion.div
initial={{ opacity: 0, x: isEven ? -20 : 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.3 }}
className={`${isEven ? 'text-right' : 'text-left'} flex-1`}
>
<span className="text-4xl font-black text-transparent bg-clip-text bg-gradient-to-b from-slate-700 to-slate-800 tracking-tighter opacity-50 select-none">
{exp.period.split(' ')[0]}
</span>
</motion.div>
</div>
{/* Card Principal */}
<div className="flex-1 w-full relative group perspective-1000">
<motion.div
initial={{ opacity: 0, x: isEven ? 50 : -50 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay: 0.1 * index }}
whileHover={{ y: -5, transition: { duration: 0.2 } }}
className={`p-8 rounded-3xl bg-zinc-900/40 border border-white/5 backdrop-blur-md relative overflow-hidden transition-all duration-300 hover:shadow-[0_0_30px_rgba(59,130,246,0.1)] hover:border-blue-500/20`}
>
{/* Background Glow Effect on Hover */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
{/* Badge de Empresa & Header */}
<div className="relative z-10">
<div className="flex flex-wrap justify-between items-start gap-4 mb-6">
<div className="space-y-2">
<div className="flex items-center gap-3">
<div className={`p-2 rounded-lg ${config.bg} ${config.color}`}>
<Icon className="w-5 h-5" />
</div>
<span className={`text-xs font-bold uppercase tracking-widest ${config.color}`}>
{exp.company}
</span>
<Section
id="experience"
eyebrow="SYSTEM_LOGS.history"
title="Trajetória focada em produto, legado e crescimento sustentável"
description="Experiências recentes onde combinei entrega rápida com responsabilidade arquitetural."
>
<div className="space-y-sp-6">
{sortedExperiences.map((exp) => (
<article key={exp.id} className="ds-card">
<div className="ds-grid-2">
<div>
<div className="ds-kicker">{exp.period}</div>
<h3 className="ds-card-title">{exp.title}</h3>
<div className="mt-sp-3 text-[11px] uppercase tracking-[0.1em] text-meta">
{exp.company} · {exp.type}
</div>
<h3 className="text-2xl font-bold text-white group-hover:text-blue-400 transition-colors">
{exp.title}
</h3>
</div>
<div className="px-3 py-1 rounded-full bg-white/5 border border-white/10 text-[10px] font-mono font-bold text-slate-400 uppercase tracking-wide">
{exp.period}
</div>
</div>
<p className="text-slate-400 leading-relaxed mb-8 text-sm">
{exp.description}
</p>
{/* Conquistas (Checklist Técnico) */}
<div className="grid grid-cols-1 gap-3 mb-8">
{exp.achievements?.map((ach: string, i: number) => (
<div key={i} className="flex gap-3 text-sm text-slate-300 items-start">
<div className="mt-1.5 w-1.5 h-1.5 rounded-full bg-blue-500 flex-shrink-0" />
<span>{ach}</span>
</div>
))}
</div>
{/* Tags de Tech */}
<div className="flex flex-wrap gap-2 pt-6 border-t border-white/5">
{exp.technologies?.map((tech: string) => (
<span key={tech} className="px-3 py-1 bg-black/20 border border-white/5 text-slate-500 text-[10px] rounded-full font-mono hover:text-blue-400 hover:border-blue-400/30 transition-all cursor-default">
{tech}
<div className="flex flex-wrap items-start justify-start gap-sp-3 lg:justify-end">
<span className="ds-tag">{exp.location}</span>
<span className="ds-tag">
{exp.technologies.length} tecnologias
</span>
))}
</div>
</div>
</div>
</motion.div>
<p className="ds-copy">{exp.description}</p>
<ul className="ds-list">
{exp.achievements?.map((achievement) => (
<li key={achievement}>{achievement}</li>
))}
</ul>
<ul className="ds-tags mt-sp-6">
{exp.technologies?.map((tech) => (
<li key={tech} className="ds-tag">
{tech}
</li>
))}
</ul>
</article>
))}
</div>
{/* Spacer para Timeline Desktop */}
<div className="hidden lg:block flex-1" />
</div>
</Section>
);
}
function ExperienceSkeleton() {
return (
<div className="h-96 flex flex-col items-center justify-center gap-4 text-slate-600 font-mono animate-pulse">
<Cpu className="w-8 h-8 animate-spin" />
<span>BOOTING_SYSTEM_HISTORY...</span>
</div>
);
}
+106 -150
View File
@@ -1,171 +1,127 @@
"use client";
"use client";
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { MenuIcon, XIcon } from "lucide-react";
import Image from "next/image";
import { cn } from "@/lib/utils";
import { useState } from "react";
import { Logo } from "./Logo";
// Definição de Tipos para consistência de dados
interface NavItem {
href: string;
label: string;
}
const navItems: NavItem[] = [
const navItems = [
{ href: "#home", label: "Home" },
{ href: "#about", label: "Sobre" },
{ href: "#skills", label: "Habilidades" },
{ href: "#experience", label: "Experiência" },
{ href: "#experience", label: "Experiencia" },
{ href: "#projects", label: "Projetos" },
{ href: "#modalities", label: "Contratacao" },
{ href: "#contact", label: "Contato" },
];
export function Header() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
// Hook de detecção de scroll para mudança de estado do Header
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<>
<motion.header
initial={{ y: -100 }}
animate={{ y: 0 }}
className={cn(
"fixed top-0 left-0 right-0 z-[100] transition-all duration-500 ease-in-out px-6 py-4",
scrolled
? "bg-black/40 backdrop-blur-xl border-b border-white/5 py-3"
: "bg-transparent py-6"
)}
>
<nav className="max-w-7xl mx-auto flex items-center justify-between">
{/* Logo com Identidade Visual de Engenheiro */}
<motion.a
href="#home"
onClick={(e) => {
e.preventDefault();
const element = document.querySelector("#home");
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}}
whileHover={{ scale: 1.05 }}
className="flex items-center gap-2 cursor-pointer"
<header
className="fixed left-0 right-0 top-0 z-50 border-b backdrop-blur-xl"
style={{
borderColor: "rgba(255,255,255,.1)",
background: "rgba(10,10,10,.96)",
}}
>
<div className="mx-auto flex h-[52px] w-full max-w-[1240px] items-center justify-between px-6 lg:px-16">
<a href="#home" className="flex items-center gap-3 no-underline">
<Logo variant="mark" size="sm" theme="dark" />
<span
className="font-display text-sm font-extrabold tracking-tight"
style={{ color: "#FFFFFF" }}
>
<div className="w-8 h-8 flex items-center justify-center">
<Image
src="/edev_logo.svg"
alt="Evandro.dev Logo"
width={32}
height={32}
className="w-8 h-8"
priority
/>
</div>
<span className="text-xl font-black tracking-tighter text-white">
EVANDRO<span className="text-blue-500">.DEV</span>
</span>
</motion.a>
evandro<span style={{ color: "#E8FF00" }}>.dev.br</span>
</span>
</a>
{/* Desktop Navigation - Focada em Espaçamento Ótimo */}
<div className="hidden md:flex items-center gap-1 bg-white/5 p-1 rounded-full border border-white/10 backdrop-blur-md">
<nav className="hidden items-center lg:flex">
<ul className="flex list-none items-center gap-2">
{navItems.map((item) => (
<a
key={item.href}
href={item.href}
onClick={(e) => {
e.preventDefault();
const element = document.querySelector(item.href);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}}
className="px-5 py-2 text-xs font-bold uppercase tracking-widest text-slate-300 hover:text-white hover:bg-white/10 rounded-full transition-all duration-300 cursor-pointer"
>
{item.label}
</a>
<li key={item.href}>
<a
href={item.href}
className="rounded-sm px-3 py-2 text-[10px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
style={{ color: "rgba(255,255,255,.86)" }}
>
{item.label}
</a>
</li>
))}
</div>
{/* Botão de Ação CTA (Call to Action) */}
<div className="hidden md:block">
<a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
className="px-6 py-2 bg-white text-black text-xs font-bold uppercase tracking-widest rounded-full hover:bg-blue-500 hover:text-white transition-all duration-500 inline-block"
>
Download CV
</a>
</div>
{/* Mobile Toggle */}
<button
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="md:hidden p-2 text-white bg-white/5 rounded-lg border border-white/10"
>
{isMenuOpen ? <XIcon size={20} /> : <MenuIcon size={20} />}
</button>
</ul>
</nav>
</motion.header>
{/* Mobile Menu Overlay com Framer Motion para fluidez */}
<AnimatePresence>
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, x: "100%" }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: "100%" }}
transition={{ type: "spring", damping: 25, stiffness: 200 }}
className="fixed inset-0 z-[110] bg-black/95 backdrop-blur-2xl md:hidden flex flex-col items-center justify-center gap-8"
<div className="flex items-center gap-4">
<span
className="hidden rounded-full border px-[10px] py-1 text-[9px] uppercase tracking-[0.12em] lg:inline-block"
style={{
borderColor: "rgba(255,255,255,.15)",
color: "rgba(255,255,255,.35)",
}}
>
<button
onClick={() => setIsMenuOpen(false)}
className="absolute top-8 right-8 p-4 text-white/50 hover:text-white"
>
<XIcon size={32} />
</button>
{navItems.map((item, i) => (
<motion.a
key={item.href}
href={item.href}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.1 }}
onClick={(e) => {
e.preventDefault();
setIsMenuOpen(false);
const element = document.querySelector(item.href);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}}
className="text-3xl font-black text-white uppercase tracking-tighter hover:text-blue-500 transition-colors cursor-pointer"
>
{item.label}
</motion.a>
v1.0 / 2026
</span>
<a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
className="hidden rounded-sm px-4 py-2 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline transition-all duration-200 hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)] lg:inline-flex"
style={{ background: "#E8FF00", color: "#0A0A0A" }}
>
Download CV
</a>
<button
type="button"
className="flex h-8 w-8 flex-col items-center justify-center gap-1.5 border-none bg-transparent lg:hidden"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
aria-label="Menu"
>
<span
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "translate-y-2 rotate-45" : ""}`}
/>
<span
className={`h-0.5 w-5 bg-white transition-opacity duration-200 ${mobileMenuOpen ? "opacity-0" : ""}`}
/>
<span
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "-translate-y-2 -rotate-45" : ""}`}
/>
</button>
</div>
</div>
<div
className={`absolute left-0 right-0 top-[52px] overflow-hidden border-b backdrop-blur-xl transition-all duration-300 lg:hidden ${mobileMenuOpen ? "max-h-[420px] opacity-100" : "max-h-0 opacity-0"}`}
style={{
borderColor: "rgba(255,255,255,.1)",
background: "rgba(10,10,10,.98)",
}}
>
<nav className="px-6 py-4">
<ul className="flex list-none flex-col gap-2">
{navItems.map((item) => (
<li key={item.href}>
<a
href={item.href}
className="block rounded-sm px-3 py-3 text-[11px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
style={{ color: "rgba(255,255,255,.9)" }}
onClick={() => setMobileMenuOpen(false)}
>
{item.label}
</a>
</li>
))}
<motion.a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: navItems.length * 0.1 }}
className="mt-8 px-8 py-4 bg-white text-black text-xl font-black uppercase tracking-tighter rounded-full hover:bg-blue-500 hover:text-white transition-all duration-300"
>
Download CV
</motion.a>
</motion.div>
)}
</AnimatePresence>
</>
<li>
<a
href="/Profile.pdf"
download="Evandro_Fonseca_Junior_CV.pdf"
className="mt-2 inline-flex rounded-sm px-4 py-3 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline"
style={{ background: "#E8FF00", color: "#0A0A0A" }}
>
Download CV
</a>
</li>
</ul>
</nav>
</div>
</header>
);
}
}
+199 -124
View File
@@ -1,145 +1,220 @@
"use client";
"use client";
import { motion } from "motion/react";
import Image from "next/image";
import { AuroraText } from "@/components/magicui/aurora-text";
import { AnimatedGradientText } from "@/components/magicui/animated-gradient-text";
import { Github, Linkedin, MessageCircle, Mail, MapPin, Terminal } from "lucide-react";
import TextType from "@/components/TextType";
import { Logo } from "./Logo";
const SOCIAL_LINKS = [
{
href: "https://github.com/evandrodevbr",
label: "GitHub",
icon: (
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
),
},
{
href: "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/",
label: "LinkedIn",
icon: (
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
),
},
{
href: "mailto:[email protected]",
label: "Email",
icon: (
<>
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
<polyline points="22,6 12,13 2,6" />
</>
),
stroke: true,
},
];
const STATS = [
{ value: "4", accent: "px", label: "Base unit" },
{ value: "9", accent: "x", label: "Type scale" },
{ value: "4", accent: "+", label: "Core tokens" },
];
export default function Hero() {
const getGreetingMessage = () => {
const hour = new Date().getHours();
if (hour >= 8 && hour < 12) return "Olá! Vi seu portfólio. Podemos conversar?";
if (hour >= 13 && hour < 18) return "Boa tarde! Vi seu portfólio. Podemos conversar?";
return "Olá! Vi seu portfólio. Podemos conversar quando estiver disponível?";
};
const generateWhatsAppUrl = () => {
const message = encodeURIComponent(getGreetingMessage());
return `https://wa.me/5547992742365?text=${message}`;
};
const hour = new Date().getHours();
const greeting =
hour >= 8 && hour < 12
? "Ola! Vi seu portfolio. Podemos conversar?"
: hour >= 13 && hour < 18
? "Boa tarde! Vi seu portfolio. Podemos conversar?"
: "Ola! Vi seu portfolio. Podemos conversar quando estiver disponivel?";
return (
<section id="home" className="relative min-h-screen flex items-center justify-center overflow-hidden pt-28 md:pt-20">
{/* 1. Background Engineering: Grid Pattern & Radial Shading */}
<div className="absolute inset-0 z-0 pointer-events-none">
<div className="absolute inset-0 bg-radial-at-t from-blue-500/10 via-transparent to-transparent" />
</div>
<section
id="home"
className="relative overflow-hidden px-6 py-24 lg:px-16"
style={{ background: "#0A0A0A", color: "#FFFFFF" }}
>
<div className="pointer-events-none absolute inset-0 bg-grid-light opacity-70" />
<div className="pointer-events-none absolute inset-x-0 top-0 h-40 bg-[radial-gradient(circle_at_top,rgba(232,255,0,0.18),transparent_60%)]" />
<div className="relative z-10 w-full max-w-6xl mx-auto px-6 grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
<div className="relative z-10 mx-auto grid min-h-[calc(100vh-52px)] w-full max-w-[1240px] grid-cols-1 items-center gap-16 lg:grid-cols-2">
<div>
<div className="mb-8 inline-flex items-center gap-2">
<span
className="h-1.5 w-1.5 animate-pulse rounded-full"
style={{ background: "#E8FF00" }}
/>
<span
className="text-[9px] uppercase tracking-[0.2em]"
style={{ color: "rgba(255,255,255,.38)" }}
>
Systems engineer available
</span>
</div>
{/* Lado Esquerdo: Conteúdo Textual (80/20 do Impacto) */}
<div className="lg:col-span-7 text-left">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
<h1
className="mb-6 font-display text-5xl font-extrabold leading-[0.92] tracking-[-0.04em] md:text-6xl lg:text-7xl xl:text-[5.4rem]"
style={{ color: "#FFFFFF" }}
>
<AnimatedGradientText className="mb-6 inline-flex items-center gap-2 px-4 py-2 rounded-full border border-blue-500/20 bg-blue-500/5 backdrop-blur-md">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-cyan-500"></span>
</span>
<span className="text-sm font-medium text-cyan-300 tracking-wide uppercase">
Systems Engineer available
</span>
</AnimatedGradientText>
Evandro
<br />
<span style={{ color: "#E8FF00" }}>.dev</span>
<br />
.br
</h1>
<h1 className="text-5xl md:text-7xl font-black text-white mb-4 tracking-tight leading-none">
Evandro <br />
<AuroraText className="italic">Fonseca Junior</AuroraText>
</h1>
<p
className="mb-10 max-w-[460px] text-[13px] leading-7"
style={{ color: "rgba(255,255,255,.72)" }}
>
Engenheiro de Software especializado em aplicacoes de alta
performance, interfaces de produto e sistemas criticos com Next.js,
React, Tauri e arquitetura escalavel.
</p>
<div className="h-10 flex items-center gap-2 mb-6">
<Terminal className="w-5 h-5 text-blue-400" />
<TextType
text={["Engenheiro de Software", "Next.js & Tauri Specialist", "High Performance Systems Architect"]}
className="text-2xl font-mono text-slate-400"
typingSpeed={60}
/>
</div>
<div className="mb-8 flex flex-wrap items-center gap-4">
<a
href={`https://wa.me/5547992742365?text=${encodeURIComponent(greeting)}`}
className="inline-flex h-11 items-center rounded-sm px-6 text-[11px] font-semibold uppercase tracking-[0.08em] no-underline transition-all duration-200 hover:-translate-y-0.5 hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)]"
style={{ background: "#E8FF00", color: "#0A0A0A" }}
>
Iniciar projeto
</a>
<p className="text-lg text-slate-400 max-w-xl mb-10 leading-relaxed">
Engenheiro de Software especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com <span className="text-white font-medium">Next.js 15, React 19, Tauri</span> e arquitetura escalável. Transformo requisitos complexos em software robusto.
</p>
<div className="flex flex-wrap gap-4 items-center">
<motion.a
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
href={generateWhatsAppUrl()}
className="px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold transition-all shadow-lg shadow-blue-600/20 flex items-center gap-2"
>
<MessageCircle className="w-5 h-5" />
Iniciar Projeto
</motion.a>
<div className="flex gap-4 px-4">
{[
{ icon: Github, link: "https://github.com/evandrodevbr", rel: "me" },
{ icon: Linkedin, link: "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/", rel: "me" },
{ icon: Mail, link: "mailto:[email protected]" }
].map((social, i) => (
<a
key={i}
href={social.link}
rel={social.rel ? `${social.rel} noopener noreferrer` : "noopener noreferrer"}
target={social.link.startsWith("mailto:") ? undefined : "_blank"}
className="p-3 rounded-lg border border-slate-800 text-slate-400 hover:text-white hover:border-slate-600 transition-all"
<div className="flex items-center gap-2">
{SOCIAL_LINKS.map((link) => (
<a
key={link.label}
href={link.href}
target={link.href.startsWith("http") ? "_blank" : undefined}
rel={
link.href.startsWith("http")
? "noopener noreferrer"
: undefined
}
className="inline-flex h-11 w-11 items-center justify-center rounded-sm border transition-all duration-200 hover:bg-white/10"
style={{
borderColor: "rgba(255,255,255,.18)",
color: "rgba(255,255,255,.84)",
}}
aria-label={link.label}
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill={link.stroke ? "none" : "currentColor"}
stroke={link.stroke ? "currentColor" : "none"}
strokeWidth={link.stroke ? "2" : undefined}
role="img"
aria-label={link.label}
>
<social.icon className="w-5 h-5" />
</a>
))}
</div>
{link.icon}
</svg>
</a>
))}
</div>
</div>
<div className="mt-8 flex items-center gap-2 text-slate-500 text-sm">
<MapPin className="w-4 h-4" />
<span>Garuva, SC | Remote Worldwide</span>
</div>
</motion.div>
</div>
{/* Lado Direito: Visual (A "Âncora" do Design) */}
<div className="lg:col-span-5 relative flex justify-center">
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="relative w-72 h-72 md:w-96 md:h-96"
<div
className="text-[10px] uppercase tracking-[0.14em]"
style={{ color: "rgba(255,255,255,.38)" }}
>
{/* Elementos Orbitais (Vibe Engenharia) */}
<div className="absolute inset-0 rounded-full border border-dashed border-blue-500/30 animate-[spin_20s_linear_infinite]" />
<div className="absolute inset-10 rounded-full border border-blue-500/10 animate-[spin_15s_linear_infinite_reverse]" />
Garuva, SC | Remote worldwide
</div>
{/* Foto de Perfil com Masking de Engenharia */}
<div className="relative z-10 w-full h-full rounded-[2.5rem] overflow-hidden border-2 border-slate-800 shadow-2xl">
<div className="absolute inset-0 bg-blue-500/10 z-10 hover:opacity-0 transition-opacity duration-500" />
<Image
src="/edev.jpeg"
alt="Evandro Fonseca Junior"
fill
className="object-cover grayscale hover:grayscale-0 transition-all duration-700 scale-105 hover:scale-100"
priority
fetchPriority="high"
/>
</div>
{/* Floating Stats Badge */}
<div className="absolute -bottom-6 -right-6 bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-2xl z-20 backdrop-blur-xl">
<p className="text-xs text-slate-500 uppercase font-bold tracking-tighter">Exp. Level</p>
<p className="text-xl font-mono text-blue-400">Mid-Level Engineer</p>
</div>
</motion.div>
<div
className="mt-10 grid max-w-[440px] grid-cols-3 gap-px border max-sm:grid-cols-1"
style={{
borderColor: "rgba(255,255,255,.12)",
background: "rgba(255,255,255,.08)",
}}
>
{STATS.map((item) => (
<div
key={item.label}
className="p-5"
style={{ background: "rgba(255,255,255,.03)" }}
>
<div
className="font-display text-2xl font-extrabold tracking-[-0.03em]"
style={{ color: "#FFFFFF" }}
>
{item.value}
<span style={{ color: "#E8FF00" }}>{item.accent}</span>
</div>
<div
className="mt-1 text-[9px] uppercase tracking-[0.12em]"
style={{ color: "rgba(255,255,255,.35)" }}
>
{item.label}
</div>
</div>
))}
</div>
</div>
</div>
{/* Scroll Indicator (Refinado) */}
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2">
<span className="text-[10px] text-slate-600 uppercase tracking-[0.2em]">Scroll</span>
<div className="w-[1px] h-12 bg-gradient-to-b from-blue-500 to-transparent" />
<div className="flex flex-col items-end gap-6 max-lg:items-start">
<div
className="relative flex aspect-square w-full max-w-[440px] items-center justify-center border max-lg:max-w-[320px]"
style={{
borderColor: "rgba(255,255,255,.12)",
background: "rgba(255,255,255,.03)",
}}
>
<span
className="absolute left-6 top-5 text-[9px] tracking-[0.14em]"
style={{ color: "rgba(255,255,255,.32)" }}
>
D / SLASH MARK
</span>
<Logo variant="mark" size="xl" theme="dark" />
<span
className="absolute bottom-5 right-6 text-[9px] tracking-[0.14em]"
style={{ color: "#E8FF00", opacity: 0.78 }}
>
CONCEITO ESCOLHIDO
</span>
</div>
<div
className="flex items-center gap-3 text-[9px] tracking-[0.12em]"
style={{ color: "rgba(255,255,255,.32)" }}
>
<span
className="h-px w-6"
style={{ background: "rgba(255,255,255,.18)" }}
/>
D / Slash mark · conceito final
</div>
<div
className="flex items-center gap-3 text-[9px] tracking-[0.12em]"
style={{ color: "rgba(255,255,255,.32)" }}
>
<span
className="h-px w-6"
style={{ background: "rgba(255,255,255,.18)" }}
/>
Escala: 280x200 · viewbox
</div>
</div>
</div>
</section>
);
}
}
+73 -194
View File
@@ -1,207 +1,86 @@
"use client";
"use client";
import React, { useMemo } from "react";
import { motion } from "motion/react";
import { BentoGrid, BentoGridItem } from "@/components/magicui/bento-grid";
import {
ExternalLink,
Github,
Star,
Zap,
Globe,
Cpu,
Code2,
Terminal,
} from "lucide-react";
import { useMemo } from "react";
import { usePortfolioData } from "@/lib/usePortfolioData";
// --- Configuração de Cores (Harmonizado com a seção anterior) ---
const CATEGORY_THEMES: Record<string, any> = {
"full-stack": { color: "text-blue-400", bg: "bg-blue-500/10", border: "border-blue-500/20", icon: Globe },
iot: { color: "text-purple-400", bg: "bg-purple-500/10", border: "border-purple-500/20", icon: Cpu },
api: { color: "text-orange-400", bg: "bg-orange-500/10", border: "border-orange-500/20", icon: Code2 },
web: { color: "text-green-400", bg: "bg-green-500/10", border: "border-green-500/20", icon: Globe },
desktop: { color: "text-emerald-400", bg: "bg-emerald-500/10", border: "border-emerald-500/20", icon: Terminal },
};
// Funções auxiliares para Schema
function getApplicationCategory(category: string): string {
const map: Record<string, string> = {
"full-stack": "WebApplication",
"iot": "DesktopApplication",
"api": "WebApplication",
"web": "WebApplication",
"desktop": "DesktopApplication"
};
return map[category.toLowerCase()] || "WebApplication";
}
function getOperatingSystem(category: string): string {
if (category.toLowerCase() === "desktop") return "Windows, Linux";
return "Web Browser";
}
import { Section } from "./Section";
export default function Projects() {
const { data, loading, error } = usePortfolioData();
const projects = useMemo(() =>
data?.projects.filter((p) => p.featured) || [],
[data]);
const projectsJsonLd = useMemo(() => {
if (!projects || projects.length === 0) return null;
return {
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": projects.map((project, index) => ({
"@type": "ListItem",
"position": index + 1,
"item": {
"@type": "SoftwareApplication",
"name": project.title,
"description": project.description,
"applicationCategory": getApplicationCategory(project.category),
"operatingSystem": getOperatingSystem(project.category),
"url": project.demoUrl || project.githubUrl || `https://evandro.dev.br/#projects`,
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "BRL"
},
"softwareVersion": "1.0.0",
"author": {
"@type": "Person",
"name": "Evandro Fonseca Junior",
"url": "https://evandro.dev.br"
}
}
}))
};
}, [projects]);
if (loading) return <div className="py-20 text-center animate-pulse text-slate-500 font-mono">Loading_Assets...</div>;
if (error) return <div className="py-20 text-center text-red-500">System_Error: Failed to sync projects.</div>;
return (
<section id="projects" className="py-32 px-6 relative">
{/* Background Decor */}
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-blue-500/5 via-transparent to-transparent pointer-events-none" />
<div className="max-w-7xl mx-auto relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-left mb-20"
>
<span className="text-blue-500 font-mono text-sm tracking-[0.3em] uppercase block mb-4">Produção_Intelectual</span>
<h2 className="text-5xl md:text-7xl font-black text-white tracking-tighter">
Projetos <span className="text-slate-700 italic">Principais</span>
</h2>
</motion.div>
<BentoGrid className="grid-cols-1 md:grid-cols-3 gap-4">
{projects.map((project, index) => {
const theme = CATEGORY_THEMES[project.category.toLowerCase()] || CATEGORY_THEMES.web;
// Lógica de Grid Inteligente:
// Index 0: NutriScan (Destaque - 2 colunas)
// Index 1: Twins Lab (Complemento - 1 coluna)
// Demais: 1 coluna cada (Preenche a linha 2 perfeitamente)
const colSpan = index === 0 ? "md:col-span-2" : "md:col-span-1";
return (
<BentoGridItem
key={project.id}
className={`${colSpan} group relative flex flex-col justify-between overflow-hidden rounded-3xl border border-slate-800 bg-slate-900/40 backdrop-blur-xl p-8 transition-all duration-500 hover:border-blue-500/30 min-h-[22rem]`}
>
{/* Header: Icon & Links */}
<div className="relative z-10">
<div className="flex items-start justify-between mb-8">
<div className="flex items-center gap-4">
<div className={`p-4 rounded-2xl ${theme.bg} border ${theme.border} group-hover:scale-110 transition-transform duration-500`}>
<theme.icon className={`w-6 h-6 ${theme.color}`} />
</div>
<div>
<h3 className="text-2xl font-bold text-white tracking-tight">{project.title}</h3>
<p className="text-sm text-slate-500 font-medium">{project.subtitle}</p>
</div>
</div>
<div className="flex gap-2">
{project.githubUrl && <ProjectLink href={project.githubUrl} icon={Github} />}
{project.demoUrl && <ProjectLink href={project.demoUrl} icon={ExternalLink} highlight />}
</div>
</div>
{/* Badges de Tecnologias */}
<div className="flex flex-wrap gap-2 mb-6">
{project.technologies.slice(0, 5).map((tech: string) => (
<span key={tech} className="px-2 py-1 text-[10px] font-mono font-bold bg-slate-950 text-slate-400 border border-slate-800 rounded-md">
{tech}
</span>
))}
</div>
<p className="text-slate-400 text-sm leading-relaxed mb-6 line-clamp-3 group-hover:line-clamp-none transition-all duration-500">
{project.description}
</p>
</div>
{/* Footer: Metrics & Status */}
<div className="relative z-10 pt-6 border-t border-slate-800/50 mt-auto flex items-center justify-between">
<div className="flex items-center gap-3">
<span className="text-[10px] uppercase tracking-widest text-slate-600 font-bold">Complexity</span>
<ComplexityStars rating={project.metrics?.complexity || 3} />
</div>
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full animate-pulse ${project.status === 'Em Produção' ? 'bg-emerald-500' : 'bg-blue-500'}`} />
<span className="text-[10px] font-bold text-slate-400 uppercase">{project.status}</span>
</div>
</div>
{/* Background Hover Glow */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700" />
</BentoGridItem>
);
})}
</BentoGrid>
{/* JSON-LD Schema for Projects */}
{projectsJsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(projectsJsonLd) }}
/>
)}
</div>
</section>
const projects = useMemo(
() => data?.projects.filter((p) => p.featured) || [],
[data],
);
}
// --- Subcomponentes Auxiliares ---
if (loading)
return <div className="ds-loading">Loading featured projects</div>;
if (error)
return <div className="ds-error">Error loading featured projects</div>;
function ProjectLink({ href, icon: Icon, highlight }: { href: string; icon: any; highlight?: boolean }) {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className={`p-2 rounded-xl transition-all duration-300 hover:scale-110 ${highlight ? 'bg-blue-600 text-white shadow-lg shadow-blue-600/20' : 'bg-slate-900 border border-slate-800 text-slate-500 hover:text-white'
}`}
<Section
id="projects"
eyebrow="PRODUCAO_INTELECTUAL"
title="Projetos com recorte técnico claro e impacto observável"
description="Seleção de produtos e ferramentas onde performance, confiabilidade e clareza de arquitetura foram requisitos reais."
dark
>
<Icon className="w-4 h-4" />
</a>
<div className="ds-grid-2">
{projects.map((project) => (
<article key={project.id} className="ds-card">
<div className="flex items-start justify-between gap-sp-6">
<div>
<div className="ds-kicker">{project.category}</div>
<h3 className="ds-card-title">{project.title}</h3>
<p className="mt-sp-2 text-[11px] uppercase tracking-[0.1em] text-[rgba(255,255,255,.36)]">
{project.subtitle}
</p>
</div>
<span className="ds-tag">{project.status}</span>
</div>
<p className="ds-copy">{project.description}</p>
<div className="mt-sp-6 flex flex-wrap gap-sp-3">
{project.githubUrl && (
<a
href={project.githubUrl}
target="_blank"
rel="noreferrer"
className="ds-link ds-link-muted"
>
GitHub
</a>
)}
{project.demoUrl && (
<a
href={project.demoUrl}
target="_blank"
rel="noreferrer"
className="ds-link ds-link-accent"
>
Live demo
</a>
)}
</div>
<ul className="ds-tags mt-sp-6">
{project.technologies.slice(0, 6).map((tech: string) => (
<li key={tech} className="ds-tag">
{tech}
</li>
))}
</ul>
<div className="mt-sp-6 flex flex-wrap gap-sp-4">
<span className="ds-metric">
Complexity {project.metrics?.complexity || 3}/5
</span>
{project.metrics?.teamSize && (
<span className="ds-metric">
Team {project.metrics.teamSize}
</span>
)}
</div>
</article>
))}
</div>
</Section>
);
}
function ComplexityStars({ rating }: { rating: number }) {
return (
<div className="flex gap-1">
{[...Array(5)].map((_, i) => (
<div key={i} className={`w-1.5 h-1.5 rounded-full ${i < rating ? 'bg-yellow-500' : 'bg-slate-800'}`} />
))}
</div>
);
}
+29 -121
View File
@@ -1,134 +1,42 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
import {
Code, Database, Cloud, Cpu, Smartphone,
Zap, GitBranch, TestTube, Terminal, Layers
} from "lucide-react";
import { usePortfolioData } from "@/lib/usePortfolioData";
const ICON_MAP: Record<string, React.ElementType> = {
Code, Database, Cloud, Cpu, Smartphone, Zap, GitBranch, TestTube, Terminal, Layers
};
const getCategoryStyles = (color: string) => {
const base = color.replace("text-", "");
return {
icon: color,
bg: `bg-${base}/10`,
border: `border-slate-800/50`,
};
};
import { Section } from "./Section";
export default function Skills() {
const { data, loading, error } = usePortfolioData();
if (loading) return <LoadingState />;
if (error || !data) return <ErrorState />;
if (loading) return <div className="ds-loading">Loading skill registry</div>;
if (error || !data) {
return <div className="ds-error">Error loading skill registry</div>;
}
return (
<section id="skills" className="relative py-32 px-6">
{/* Background Decoration */}
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
<div className="absolute top-[40%] left-[50%] -translate-x-1/2 w-[800px] h-[800px] bg-blue-900/5 rounded-full blur-[100px]" />
</div>
<div className="max-w-7xl mx-auto relative z-10">
{/* Header Seção */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
className="flex flex-col md:flex-row justify-between items-end mb-20 gap-8"
>
<div className="text-left">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-4">
<Layers className="w-3 h-3" />
<span>MODULE_REGISTRY.lib</span>
<Section
id="skills"
eyebrow="MODULE_REGISTRY.lib"
title="Stack modular para produto, operação e escala"
description="Ferramentas escolhidas por previsibilidade operacional, velocidade de entrega e facilidade de manutenção."
dark
>
<div className="ds-grid-2">
{data.skills.technical.map((category, index) => (
<article key={category.category} className="ds-card">
<div className="flex items-center justify-between gap-sp-4">
<div className="ds-kicker">ID_0{index + 1}X</div>
<div className="ds-metric">{category.skills.length} items</div>
</div>
<h3 className="text-4xl md:text-6xl font-black text-white tracking-tighter">
Arsenal <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-indigo-500">Tecnológico</span>
</h3>
</div>
<p className="text-slate-400 max-w-md text-sm md:text-base font-light leading-relaxed border-l-2 border-slate-800 pl-6">
Sistemas resilientes construídos com precisão matemática e rigor arquitetural.
</p>
</motion.div>
{/* Grid de Habilidades */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{data.skills.technical.map((category, index) => {
const Icon = ICON_MAP[category.icon] || Code;
const styles = getCategoryStyles(category.color);
return (
<motion.div
key={category.category}
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.2 }}
transition={{
duration: 0.5,
delay: index * 0.05,
ease: "easeOut"
}}
className="h-full"
>
<div
className="group relative h-full p-8 flex flex-col bg-zinc-900/40 border border-white/5 rounded-2xl backdrop-blur-sm hover:border-blue-500/20 hover:bg-zinc-900/60 transition-all duration-300 overflow-hidden"
>
{/* Scanning Line Effect */}
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-blue-500/50 to-transparent -translate-x-full group-hover:animate-scan" />
<div className="flex items-start justify-between mb-8">
<div className={`p-4 rounded-xl bg-slate-800/50 border border-white/5 group-hover:bg-blue-500/10 group-hover:border-blue-500/20 transition-all duration-300`}>
<Icon className={`w-8 h-8 text-slate-400 group-hover:text-blue-400 transition-colors`} />
</div>
<span className="text-[10px] font-mono text-slate-600 uppercase tracking-widest font-bold group-hover:text-blue-500/50 transition-colors">
ID_0{index}X
</span>
</div>
<h3 className="text-xl font-bold text-white mb-6 group-hover:text-blue-200 transition-colors">
{category.category}
</h3>
<div className="flex flex-wrap gap-2 mt-auto">
{category.skills.map((skill, sIdx) => (
<span
key={sIdx}
className="px-2.5 py-1 text-[10px] font-mono bg-black/40 text-slate-400 rounded border border-white/5 group-hover:border-blue-500/20 group-hover:text-blue-300 transition-all duration-300 cursor-default"
>
{skill}
</span>
))}
</div>
</div>
</motion.div>
);
})}
</div>
<h3 className="ds-card-title">{category.category}</h3>
<ul className="ds-tags mt-sp-6">
{category.skills.map((skill) => (
<li key={skill} className="ds-tag">
{skill}
</li>
))}
</ul>
</article>
))}
</div>
</section>
</Section>
);
}
// --- Componentes de Suporte (Manter no mesmo arquivo para contexto ou mover para /components) ---
function LoadingState() {
return (
<div className="py-32 flex flex-col items-center justify-center space-y-4 bg-[#030712]">
<div className="w-12 h-12 border-4 border-blue-500/20 border-t-blue-500 rounded-full animate-spin" />
<span className="text-slate-500 font-mono text-sm animate-pulse uppercase tracking-tighter">Initializing Systems...</span>
</div>
);
}
function ErrorState() {
return (
<div className="py-32 text-center text-red-400 font-mono bg-[#030712]">
[FATAL_ERROR]: Failed to synchronize portfolio data.
</div>
);
}
+67 -229
View File
@@ -1,272 +1,110 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
import {
Briefcase,
Clock,
HardHat,
Lightbulb,
Building2,
Users,
User,
Star,
CheckCircle2,
Terminal,
ShieldCheck,
Zap,
} from "lucide-react";
import { Section } from "./Section";
// --- 1. DATA ---
const WorkModalitiesData = [
{
title: "Intervenção Ad-hoc",
value: "R$ 35 - 70/hora",
badge: "AGILIDADE",
badge: "Agilidade",
idealFor:
"Resolução de gargalos técnicos específicos e consultoria de arquitetura imediata.",
description:
"Engenharia sob demanda com foco em troubleshooting e otimização de performance. Rastreabilidade total via timesheet detalhado.",
"Engenharia sob demanda com foco em troubleshooting, performance e decisões rápidas com rastreabilidade.",
benefits: [
"Diagnóstico Sênior Imediato",
"Sem Compromisso de Longo Prazo",
"Documentação de Alterações",
"Diagnóstico sênior imediato",
"Sem compromisso de longo prazo",
"Documentação de alterações",
],
icon: Zap,
color: "blue",
featured: false,
},
{
title: "Alocação de Sprint",
value: "Eficiência Financeira",
badge: "MAIS ESCOLHIDO",
title: "Alocação de sprint",
value: "Eficiência financeira",
badge: "Mais escolhido",
idealFor:
"Desenvolvimento contínuo de features com previsibilidade de budget e prioridade técnica.",
"Times que precisam de entrega contínua com previsibilidade de orçamento e prioridade técnica.",
description:
"Pacotes de horas pré-pagas que garantem disponibilidade na agenda e redução do custo unitário por hora. Ideal para scaling.",
"Pacotes de horas pré-pagas com redução de custo unitário e espaço reservado na agenda para evolução constante.",
benefits: [
"Taxa horária reduzida (Lock-in)",
"Garantia de Disponibilidade",
"Prioridade em Critical Bugs",
"Taxa horária reduzida",
"Garantia de disponibilidade",
"Prioridade em bugs críticos",
],
icon: Briefcase,
color: "green",
featured: true,
},
{
title: "Engenheiro Dedicado",
title: "Engenheiro dedicado",
value: "A partir de R$ 2.800/mês",
badge: "VIP & ESTRATÉGICO",
badge: "Estratégico",
idealFor:
"Empresas que necessitam de um 'Fractional CTO' ou Lead Engineer para governança técnica.",
"Empresas que precisam de liderança técnica contínua, coordenação de backlog e direção arquitetural.",
description:
"Parceria estratégica com foco em sustentabilidade do sistema, manutenção preventiva e evolução arquitetural contínua.",
"Parceria recorrente para sustentação, evolução de sistema, revisão de dívida técnica e tomada de decisão.",
benefits: [
"SLA de Resposta Prioritária",
"Gestão de Débito Técnico",
"Consultoria Estratégica",
"SLA prioritário",
"Gestão de débito técnico",
"Consultoria estratégica",
],
icon: ShieldCheck,
color: "purple",
featured: false,
},
{
title: "Squad por Projeto",
value: "Escopo Fechado",
badge: "CONTROLE TOTAL",
title: "Squad por projeto",
value: "Escopo fechado",
badge: "Controle total",
idealFor:
"Produtos novos ou migrações de sistemas onde o orçamento é rigorosamente definido pelo escopo.",
"Produtos novos, replatforming ou migrações com orçamento definido por entregáveis e marcos claros.",
description:
"Desenvolvimento orientado a entregáveis (milestones). Mitigação de riscos através de definições claras de 'Done'.",
"Execução orientada a milestones com redução de risco através de definição objetiva de escopo e critérios de done.",
benefits: [
"Estimativa de Riscos Inclusa",
"Cronograma de Entregas",
"Garantia Pós-Entrega",
"Estimativa de riscos inclusa",
"Cronograma de entregas",
"Garantia pós-entrega",
],
icon: HardHat,
color: "orange",
featured: false,
},
];
// --- 2. COMPONENTS ---
export function WorkModalities() {
return (
<section
<Section
id="modalities"
className="py-32 px-6 relative"
eyebrow="CONTRACT_PROTOCOLS.sys"
title="Formatos de contratação para contextos técnicos distintos"
description="Modelos pensados para reduzir overhead, preservar velocidade de decisão e manter previsibilidade."
>
{/* Background Decor handled globally now, avoiding overflow-hidden here allows bleed */}
<div className="max-w-7xl mx-auto relative z-10">
{/* Header Profissional */}
<div className="mb-24 text-center md:text-left">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-6"
>
<Terminal className="w-3 h-3" />
<span>CONTRACT_PROTOCOLS.sys</span>
</motion.div>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
>
Flexibilidade <br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-cyan-400">Contratual</span>
</motion.h2>
<p className="mt-6 text-lg text-slate-400 max-w-2xl leading-relaxed font-light border-l-2 border-slate-800 pl-6">
Modelos de engajamento adaptáveis para diferentes estágios de maturidade tecnológica e orçamentária.
</p>
</div>
{/* Comparação de Mercado (Anchoring Effect) */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-24">
<MarketCard
title="Agências Tradicionais"
price="Alto Overhead"
icon={Building2}
color="text-red-500"
delay={0}
best={false}
/>
<MarketCard
title="Consultorias Big Tech"
price="Custo Inflacionado"
icon={Users}
color="text-orange-500"
delay={0.1}
best={false}
/>
<MarketCard
title="Freelancers Generalistas"
price="Risco Técnico"
icon={User}
color="text-yellow-500"
delay={0.2}
best={false}
/>
<MarketCard
title="Engenheiro Especialista"
price="High ROI"
icon={Star}
color="text-emerald-400"
best={true}
delay={0.3}
/>
</div>
{/* Grid de Modalidades */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{WorkModalitiesData.map((modality, index) => (
<ModalityCard key={modality.title} modality={modality} index={index} />
))}
</div>
<div className="ds-grid-4 mb-sp-6">
{[
["Agências tradicionais", "Alto overhead"],
["Consultorias big tech", "Custo inflacionado"],
["Freelancers generalistas", "Risco técnico"],
["Engenheiro especialista", "High ROI"],
].map(([label, value]) => (
<div key={label} className="ds-panel">
<div className="ds-kicker">{label}</div>
<div className="mt-sp-3 font-display text-2xl font-extrabold tracking-[-0.02em]">
{value}
</div>
</div>
))}
</div>
</section>
);
}
function MarketCard({ title, price, icon: Icon, color, best, delay }: any) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay }}
className={`p-6 rounded-2xl border backdrop-blur-sm transition-all duration-300 ${best
? "bg-emerald-500/5 border-emerald-500/20 shadow-[0_0_20px_rgba(16,185,129,0.1)]"
: "bg-zinc-900/40 border-white/5 opacity-70 hover:opacity-100"
}`}
>
<div className="flex items-center gap-3 mb-3">
<Icon className={`w-4 h-4 ${color}`} />
<h4 className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">
{title}
</h4>
<div className="ds-grid-2">
{WorkModalitiesData.map((modality) => (
<article key={modality.title} className="ds-card">
<div className="flex items-center justify-between gap-sp-4">
<span className="ds-kicker">{modality.badge}</span>
<span className="ds-tag">{modality.value}</span>
</div>
<h3 className="ds-card-title">{modality.title}</h3>
<p className="ds-copy">{modality.description}</p>
<div className="mt-sp-5 rounded-[var(--r-md)] bg-[rgba(10,10,10,.03)] p-sp-4 text-[11px] leading-7 text-ink-80">
Ideal para: {modality.idealFor}
</div>
<ul className="ds-list">
{modality.benefits.map((benefit) => (
<li key={benefit}>{benefit}</li>
))}
</ul>
</article>
))}
</div>
<p className={`text-sm font-bold ${best ? "text-emerald-400" : "text-slate-500"}`}>
{price}
</p>
</motion.div>
);
}
function ModalityCard({ modality, index }: { modality: any; index: number }) {
const Icon = modality.icon;
const isFeatured = modality.featured;
return (
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 0.5, delay: index * 0.1 }}
className={`group relative flex flex-col p-8 rounded-3xl border transition-all duration-500 overflow-hidden ${isFeatured
? "bg-zinc-900/60 border-blue-500/30 shadow-2xl shadow-blue-900/10"
: "bg-zinc-900/40 border-white/5 hover:border-white/10"
}`}
>
{/* Hover Gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
{/* Badge */}
{modality.badge && (
<div className="absolute top-6 right-6">
<span className={`px-3 py-1 rounded-full text-[10px] font-bold tracking-widest uppercase border ${isFeatured
? "bg-blue-500/10 border-blue-500/20 text-blue-300"
: "bg-slate-800/50 border-slate-700 text-slate-400"
}`}>
{modality.badge}
</span>
</div>
)}
{/* Header */}
<div className="relative z-10 mb-8">
<div className={`w-12 h-12 rounded-xl flex items-center justify-center mb-6 transition-transform group-hover:scale-110 ${isFeatured ? "bg-blue-500/10 text-blue-400" : "bg-slate-800/50 text-slate-400"
}`}>
<Icon className="w-6 h-6" />
</div>
<h3 className="text-2xl font-bold text-white mb-2 group-hover:text-blue-200 transition-colors">
{modality.title}
</h3>
<p className="font-mono text-sm text-slate-500">
{modality.value}
</p>
</div>
{/* Content */}
<div className="relative z-10 flex-1">
<p className="text-slate-400 text-sm leading-relaxed mb-8">
{modality.description}
</p>
<ul className="space-y-3 mb-8">
{modality.benefits.map((benefit: string, i: number) => (
<li key={i} className="flex items-start gap-3 text-xs text-slate-300">
<CheckCircle2 className={`w-4 h-4 flex-shrink-0 mt-0.5 ${isFeatured ? "text-blue-400" : "text-slate-500"}`} />
<span>{benefit}</span>
</li>
))}
</ul>
</div>
{/* Footer */}
<div className="relative z-10 pt-6 border-t border-white/5 mt-auto">
<p className="text-[10px] uppercase tracking-wider text-slate-500">
<span className="text-slate-600 font-bold mb-1 block">Ideal Para:</span>
{modality.idealFor}
</p>
</div>
</motion.div>
</Section>
);
}
+309 -159
View File
@@ -1,168 +1,318 @@
@import "tailwindcss";
@import "tw-animate-css";
@import url("https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=Geist+Mono:wght@300;400;500;600&display=swap");
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--animate-aurora: aurora 8s ease-in-out infinite alternate;
@keyframes aurora {
0% {
background-position: 0% 50%;
transform: rotate(-5deg) scale(0.9);
}
25% {
background-position: 50% 100%;
transform: rotate(5deg) scale(1.1);
}
50% {
background-position: 100% 50%;
transform: rotate(-3deg) scale(0.95);
}
75% {
background-position: 50% 0%;
transform: rotate(3deg) scale(1.05);
}
100% {
background-position: 0% 50%;
transform: rotate(-5deg) scale(0.9);
}
}
}
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--blue-primary: #4facfe; /* Nova cor azul primária */
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--ink: #0a0a0a;
--ink-80: rgba(10, 10, 10, 0.8);
--ink-40: rgba(10, 10, 10, 0.4);
--ink-12: rgba(10, 10, 10, 0.12);
--ink-06: rgba(10, 10, 10, 0.06);
--paper: #f4f2ed;
--paper-dark: #eceae4;
--white: #ffffff;
--slash: #e8ff00;
--slash-dim: rgba(232, 255, 0, 0.15);
--meta: #777;
--border: rgba(10, 10, 10, 0.1);
--border-light: rgba(10, 10, 10, 0.06);
--sp-1: 4px;
--sp-2: 8px;
--sp-3: 12px;
--sp-4: 16px;
--sp-5: 20px;
--sp-6: 24px;
--sp-8: 32px;
--sp-10: 40px;
--sp-12: 48px;
--sp-16: 64px;
--sp-20: 80px;
--sp-24: 96px;
--r-sm: 4px;
--r-md: 8px;
--r-lg: 16px;
--r-full: 999px;
--font-display: "Syne", sans-serif;
--font-mono: "Geist Mono", monospace;
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--blue-primary: #4facfe; /* Nova cor azul primária */
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
html,
body {
@apply bg-[#030712] text-foreground overflow-x-hidden max-w-[100vw];
}
}
@layer utilities {
.animate-shimmer {
animation: shimmer 2s linear infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth; /* Adicionar rolagem suave */
scroll-padding-top: 64px; /* Altura do cabeçalho fixo */
scroll-behavior: smooth;
}
body {
background: var(--paper);
color: var(--ink);
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.6;
overflow-x: hidden;
}
a {
color: inherit;
}
button,
input,
select,
textarea {
font: inherit;
}
::selection {
background: var(--slash);
color: var(--ink);
}
.bg-grid {
background-image:
linear-gradient(rgba(10, 10, 10, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(10, 10, 10, 0.03) 1px, transparent 1px);
background-size: 48px 48px;
}
.bg-grid-light {
background-image:
linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
background-size: 48px 48px;
}
.ds-main {
padding-top: 52px;
}
.ds-section {
padding: var(--sp-20) var(--sp-16);
border-bottom: 1px solid var(--border);
}
.ds-section:nth-of-type(even) {
background: var(--paper-dark);
}
.ds-section-dark {
background: var(--ink);
border-bottom-color: rgba(255, 255, 255, 0.06);
color: var(--white);
}
.ds-section-shell,
.ds-loading,
.ds-error {
width: min(1240px, 100%);
margin: 0 auto;
}
.ds-section-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--sp-8);
margin-bottom: var(--sp-12);
}
.ds-section-meta {
max-width: 400px;
}
.ds-eyebrow,
.ds-kicker {
font-size: 9px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--meta);
}
.ds-eyebrow {
margin-bottom: var(--sp-2);
}
.ds-section-title {
font-family: var(--font-display);
font-size: clamp(24px, 4vw, 40px);
line-height: 1.05;
font-weight: 800;
letter-spacing: -0.03em;
}
.ds-section-description {
color: var(--meta);
font-size: 11px;
line-height: 1.8;
}
.ds-grid-2,
.ds-grid-4 {
display: grid;
gap: var(--sp-6);
}
.ds-grid-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.ds-grid-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.ds-card,
.ds-panel {
border: 1px solid var(--border);
min-width: 0;
}
.ds-card {
background: rgba(255, 255, 255, 0.55);
padding: var(--sp-6);
}
.ds-panel {
background: rgba(10, 10, 10, 0.02);
padding: var(--sp-5);
}
.ds-card-title {
font-family: var(--font-display);
font-size: clamp(18px, 2.4vw, 28px);
line-height: 1.1;
letter-spacing: -0.02em;
margin-top: var(--sp-3);
}
.ds-copy {
margin-top: var(--sp-4);
color: var(--ink-80);
font-size: 12px;
line-height: 1.8;
}
.ds-tags {
display: flex;
flex-wrap: wrap;
gap: var(--sp-2);
list-style: none;
}
.ds-tag {
padding: 6px 10px;
border: 1px solid var(--border);
border-radius: var(--r-full);
background: rgba(10, 10, 10, 0.03);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.ds-metric {
display: inline-flex;
align-items: center;
gap: var(--sp-2);
color: var(--meta);
font-size: 10px;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.ds-link {
display: inline-flex;
align-items: center;
gap: var(--sp-2);
text-decoration: none;
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
transition:
transform 0.2s ease,
background 0.2s ease,
border-color 0.2s ease;
}
.ds-link:hover {
transform: translateY(-1px);
}
.ds-link-accent {
color: var(--ink);
background: var(--slash);
padding: 12px 18px;
}
.ds-link-muted {
padding: 12px 18px;
border: 1px solid var(--border);
}
.ds-list {
margin-top: var(--sp-6);
padding-left: var(--sp-5);
font-size: 12px;
line-height: 1.8;
}
.ds-list li::marker {
color: var(--slash);
}
.ds-loading,
.ds-error {
padding: var(--sp-10) var(--sp-16);
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.ds-section-dark .ds-eyebrow,
.ds-section-dark .ds-kicker,
.ds-section-dark .ds-metric {
color: rgba(255, 255, 255, 0.3);
}
.ds-section-dark .ds-section-description {
color: rgba(255, 255, 255, 0.38);
}
.ds-section-dark .ds-card,
.ds-section-dark .ds-panel {
background: rgba(255, 255, 255, 0.02);
border-color: rgba(255, 255, 255, 0.08);
}
.ds-section-dark .ds-copy,
.ds-section-dark .ds-list {
color: rgba(255, 255, 255, 0.68);
}
.ds-section-dark .ds-tag {
border-color: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.04);
}
.ds-section-dark .ds-link-muted {
border-color: rgba(255, 255, 255, 0.12);
}
@media (max-width: 1024px) {
.ds-section {
padding: var(--sp-16) var(--sp-6);
}
.ds-section-header,
.ds-grid-2,
.ds-grid-4 {
grid-template-columns: 1fr;
}
}
+2 -55
View File
@@ -1,25 +1,8 @@
import type { Metadata, Viewport } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import Script from "next/script";
// Importação necessária para corrigir o erro de namespace e tipagem do children
import { ReactNode } from "react";
import GoogleAnalytics from "./components/GoogleAnalytics";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
display: "swap",
adjustFontFallback: true,
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
display: "swap",
adjustFontFallback: true,
});
export const metadata: Metadata = {
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
description:
@@ -97,13 +80,12 @@ export const metadata: Metadata = {
};
export const viewport: Viewport = {
themeColor: "#000000",
themeColor: "#ffffff",
width: "device-width",
initialScale: 1,
maximumScale: 5,
};
// Interface tipada corretamente
interface RootLayoutProps {
children: ReactNode;
}
@@ -149,49 +131,14 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
return (
<html lang="pt-BR">
<head>
{/* Preconnect to external origins for better performance */}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="anonymous"
/>
<link rel="preconnect" href="https://wa.me" />
<link rel="preconnect" href="https://www.linkedin.com" />
<link rel="preconnect" href="https://github.com" />
{/* DNS prefetch for additional performance */}
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
<link rel="dns-prefetch" href="//fonts.gstatic.com" />
<link rel="dns-prefetch" href="//wa.me" />
<link rel="dns-prefetch" href="//www.linkedin.com" />
<link rel="dns-prefetch" href="//github.com" />
{/* SEO and Crawling */}
<link rel="canonical" href="https://evandro.dev.br" />
{/* Theme color for mobile browsers */}
<meta name="msapplication-TileColor" content="#000000" />
<meta
name="apple-mobile-web-app-status-bar-style"
content="black-translucent"
/>
<meta name="apple-mobile-web-app-title" content="Evandro.dev" />
</head>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<body>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{children}
<Script
src="/analytics/api/script.js"
data-site-id="1"
data-debounce="100"
strategy="afterInteractive"
/>
<GoogleAnalytics />
</body>
</html>
+10 -65
View File
@@ -1,78 +1,23 @@
import dynamic from "next/dynamic";
import About from "./components/About";
import Contact from "./components/Contact";
import Experience from "./components/Experience";
import { Header } from "./components/Header";
import Squares from "@/components/Squares";
import { WorkModalities } from "./components/WorkModalities"; // Importar WorkModalities
import DotGrid from "@/components/DotGrid";
// Lazy load components for better performance
const Hero = dynamic(() => import("./components/Hero"), {
loading: () => (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-pulse text-white">Carregando...</div>
</div>
),
});
const About = dynamic(() => import("./components/About"), {
loading: () => (
<div className="py-20 px-4 text-center">
<div className="animate-pulse text-white">Carregando seção...</div>
</div>
),
});
const Skills = dynamic(() => import("./components/Skills"), {
loading: () => (
<div className="py-20 px-4 text-center">
<div className="animate-pulse text-white">Carregando habilidades...</div>
</div>
),
});
const Experience = dynamic(() => import("./components/Experience"), {
loading: () => (
<div className="py-20 px-4 text-center">
<div className="animate-pulse text-white">Carregando experiência...</div>
</div>
),
});
const Projects = dynamic(() => import("./components/Projects"), {
loading: () => (
<div className="py-20 px-4 text-center">
<div className="animate-pulse text-white">Carregando projetos...</div>
</div>
),
});
const Contact = dynamic(() => import("./components/Contact"), {
loading: () => (
<div className="py-20 px-4 text-center">
<div className="animate-pulse text-white">Carregando contato...</div>
</div>
),
});
import Hero from "./components/Hero";
import Projects from "./components/Projects";
import Skills from "./components/Skills";
import { WorkModalities } from "./components/WorkModalities";
export default function Home() {
return (
<div className="min-h-screen text-white relative">
<div className="fixed inset-0 z-0 opacity-50">
<Squares
speed={0.5}
squareSize={40}
direction="diagonal" // up, down, left, right, diagonal
borderColor="#271E37"
hoverFillColor="#222222"
/>
</div>
<div>
<Header />
<main className="relative z-10">
<main className="ds-main">
<Hero />
<About />
<Skills />
<Experience />
<Projects />
<WorkModalities /> {/* Adicionar WorkModalities aqui */}
<WorkModalities />
<Contact />
</main>
</div>
+245 -217
View File
@@ -4,6 +4,15 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
next@>=10.0.0 <15.5.10: '>=15.5.10'
next@>=15.5.1-canary.0 <15.5.10: '>=15.5.10'
tar@<7.5.7: '>=7.5.7'
tar@<7.5.8: '>=7.5.8'
tar@<=7.5.2: '>=7.5.3'
tar@<=7.5.3: '>=7.5.4'
tar@<=7.5.9: '>=7.5.10'
importers:
.:
@@ -30,8 +39,8 @@ importers:
specifier: ^12.23.12
version: 12.23.12([email protected]([email protected]))([email protected])
next:
specifier: 15.5.9
version: 15.5.9([email protected]([email protected]))([email protected])
specifier: '>=15.5.10'
version: 16.1.6([email protected]([email protected]))([email protected])
ogl:
specifier: ^1.0.11
version: 1.0.11
@@ -101,24 +110,28 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@biomejs/[email protected]':
resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@biomejs/[email protected]':
resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [musl]
'@biomejs/[email protected]':
resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@biomejs/[email protected]':
resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==}
@@ -132,127 +145,158 @@ packages:
cpu: [x64]
os: [win32]
'@emnapi/runtime@1.5.0':
resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
'@emnapi/runtime@1.8.1':
resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==}
'@img/[email protected]':
resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==}
'@img/[email protected]':
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
'@img/[email protected]':
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [darwin]
'@img/[email protected].3':
resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==}
'@img/[email protected].5':
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
'@img/[email protected].0':
resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==}
'@img/[email protected].4':
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
cpu: [arm64]
os: [darwin]
'@img/[email protected].0':
resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==}
'@img/[email protected].4':
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
cpu: [x64]
os: [darwin]
'@img/[email protected].0':
resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
'@img/[email protected].4':
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/[email protected].0':
resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
'@img/[email protected].4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/[email protected].0':
resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
'@img/[email protected].4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.0':
resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/[email protected]':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/[email protected].0':
resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
'@img/[email protected].4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/[email protected].0':
resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
'@img/[email protected].4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/[email protected].0':
resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
'@img/[email protected].4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/[email protected].3':
resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
'@img/[email protected].5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/[email protected].3':
resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
'@img/[email protected].5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/[email protected].3':
resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
'@img/[email protected].5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.3':
resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/[email protected]':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/[email protected].3':
resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
'@img/[email protected].5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/[email protected].3':
resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
'@img/[email protected].5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/[email protected].3':
resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
'@img/[email protected].5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/[email protected].3':
resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
'@img/[email protected].5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
'@img/[email protected].3':
resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==}
'@img/[email protected].5':
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [win32]
'@img/[email protected].3':
resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==}
'@img/[email protected].5':
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
'@img/[email protected].3':
resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==}
'@img/[email protected].5':
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
@@ -277,53 +321,57 @@ packages:
'@jridgewell/[email protected]':
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
'@next/env@15.5.9':
resolution: {integrity: sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==}
'@next/env@16.1.6':
resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==}
'@next/swc-darwin-arm64@15.5.7':
resolution: {integrity: sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==}
'@next/swc-darwin-arm64@16.1.6':
resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.5.7':
resolution: {integrity: sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==}
'@next/swc-darwin-x64@16.1.6':
resolution: {integrity: sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.5.7':
resolution: {integrity: sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==}
'@next/swc-linux-arm64-gnu@16.1.6':
resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@15.5.7':
resolution: {integrity: sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==}
'@next/swc-linux-arm64-musl@16.1.6':
resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@15.5.7':
resolution: {integrity: sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==}
'@next/swc-linux-x64-gnu@16.1.6':
resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@15.5.7':
resolution: {integrity: sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==}
'@next/swc-linux-x64-musl@16.1.6':
resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@15.5.7':
resolution: {integrity: sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==}
'@next/swc-win32-arm64-msvc@16.1.6':
resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.5.7':
resolution: {integrity: sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==}
'@next/swc-win32-x64-msvc@16.1.6':
resolution: {integrity: sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -369,24 +417,28 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==}
@@ -430,6 +482,11 @@ packages:
'@types/[email protected]':
resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==}
[email protected]:
resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==}
engines: {node: '>=6.0.0'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==}
@@ -450,20 +507,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-huuGFnDoXLy/tsCZYYa/H35BBRs9cxsS0XKJ3BXjRp699cQKuoEVrvKlAQMx0DKXG7+VUv4jsHVrS7yPbkLSkQ==}
[email protected]:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
[email protected]:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
[email protected]:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
[email protected]:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
[email protected]:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
@@ -471,6 +514,10 @@ packages:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
engines: {node: '>=10.13.0'}
@@ -495,9 +542,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==}
[email protected]:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
[email protected]:
resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
hasBin: true
@@ -531,24 +575,28 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
[email protected]:
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
[email protected]:
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
[email protected]:
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
[email protected]:
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
@@ -578,15 +626,10 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
minizlib@3.0.2:
resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
minizlib@3.1.0:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'}
[email protected]:
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
engines: {node: '>=10'}
hasBin: true
[email protected]:
resolution: {integrity: sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==}
@@ -612,9 +655,9 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
next@15.5.9:
resolution: {integrity: sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
next@16.1.6:
resolution: {integrity: sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==}
engines: {node: '>=20.9.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
@@ -662,18 +705,15 @@ packages:
[email protected]:
resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==}
[email protected].2:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
[email protected].4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
[email protected].3:
resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
[email protected].5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
[email protected]:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
[email protected]:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -706,8 +746,8 @@ packages:
resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==}
engines: {node: '>=6'}
tar@7.4.3:
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
tar@7.5.10:
resolution: {integrity: sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==}
engines: {node: '>=18'}
[email protected]:
@@ -767,95 +807,106 @@ snapshots:
'@biomejs/[email protected]':
optional: true
'@emnapi/runtime@1.5.0':
'@emnapi/runtime@1.8.1':
dependencies:
tslib: 2.8.1
optional: true
'@img/[email protected]':
'@img/[email protected]':
optional: true
'@img/[email protected]':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.2.0
'@img/sharp-libvips-darwin-arm64': 1.2.4
optional: true
'@img/[email protected].3':
'@img/[email protected].5':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.2.0
'@img/sharp-libvips-darwin-x64': 1.2.4
optional: true
'@img/[email protected].0':
'@img/[email protected].4':
optional: true
'@img/[email protected].0':
'@img/[email protected].4':
optional: true
'@img/[email protected].0':
'@img/[email protected].4':
optional: true
'@img/[email protected].0':
'@img/[email protected].4':
optional: true
'@img/[email protected].0':
'@img/[email protected].4':
optional: true
'@img/sharp-libvips-linux-s390x@1.2.0':
'@img/sharp-libvips-linux-riscv64@1.2.4':
optional: true
'@img/sharp-libvips-linux-x64@1.2.0':
'@img/sharp-libvips-linux-s390x@1.2.4':
optional: true
'@img/sharp-libvips-linuxmusl-arm[email protected].0':
'@img/sharp-libvips-linux-x[email protected].4':
optional: true
'@img/sharp-libvips-linuxmusl-x[email protected].0':
'@img/sharp-libvips-linuxmusl-arm[email protected].4':
optional: true
'@img/sharp-li[email protected]':
'@img/sharp-li[email protected]':
optional: true
'@img/[email protected]':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.2.0
'@img/sharp-libvips-linux-arm64': 1.2.4
optional: true
'@img/[email protected].3':
'@img/[email protected].5':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.2.0
'@img/sharp-libvips-linux-arm': 1.2.4
optional: true
'@img/[email protected].3':
'@img/[email protected].5':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.2.0
'@img/sharp-libvips-linux-ppc64': 1.2.4
optional: true
'@img/sharp-linux-s390x@0.34.3':
'@img/sharp-linux-riscv64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.2.0
'@img/sharp-libvips-linux-riscv64': 1.2.4
optional: true
'@img/sharp-linux-x64@0.34.3':
'@img/sharp-linux-s390x@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.2.0
'@img/sharp-libvips-linux-s390x': 1.2.4
optional: true
'@img/sharp-linuxmusl-arm[email protected].3':
'@img/sharp-linux-x[email protected].5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
'@img/sharp-libvips-linux-x64': 1.2.4
optional: true
'@img/sharp-linuxmusl-x[email protected].3':
'@img/sharp-linuxmusl-arm[email protected].5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
optional: true
'@img/sharp-wasm32@0.34.3':
'@img/sharp-linuxmusl-x64@0.34.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
optional: true
'@img/[email protected]':
dependencies:
'@emnapi/runtime': 1.5.0
'@emnapi/runtime': 1.8.1
optional: true
'@img/[email protected].3':
'@img/[email protected].5':
optional: true
'@img/[email protected].3':
'@img/[email protected].5':
optional: true
'@img/[email protected].3':
'@img/[email protected].5':
optional: true
'@isaacs/[email protected]':
@@ -881,30 +932,30 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@next/env@15.5.9': {}
'@next/env@16.1.6': {}
'@next/swc-darwin-arm64@15.5.7':
'@next/swc-darwin-arm64@16.1.6':
optional: true
'@next/swc-darwin-x64@15.5.7':
'@next/swc-darwin-x64@16.1.6':
optional: true
'@next/swc-linux-arm64-gnu@15.5.7':
'@next/swc-linux-arm64-gnu@16.1.6':
optional: true
'@next/swc-linux-arm64-musl@15.5.7':
'@next/swc-linux-arm64-musl@16.1.6':
optional: true
'@next/swc-linux-x64-gnu@15.5.7':
'@next/swc-linux-x64-gnu@16.1.6':
optional: true
'@next/swc-linux-x64-musl@15.5.7':
'@next/swc-linux-x64-musl@16.1.6':
optional: true
'@next/swc-win32-arm64-msvc@15.5.7':
'@next/swc-win32-arm64-msvc@16.1.6':
optional: true
'@next/swc-win32-x64-msvc@15.5.7':
'@next/swc-win32-x64-msvc@16.1.6':
optional: true
'@swc/[email protected]':
@@ -960,7 +1011,7 @@ snapshots:
'@tailwindcss/[email protected]':
dependencies:
detect-libc: 2.0.4
tar: 7.4.3
tar: 7.5.10
optionalDependencies:
'@tailwindcss/oxide-android-arm64': 4.1.12
'@tailwindcss/oxide-darwin-arm64': 4.1.12
@@ -995,6 +1046,8 @@ snapshots:
dependencies:
csstype: 3.1.3
[email protected]: {}
[email protected]: {}
[email protected]: {}
@@ -1011,30 +1064,13 @@ snapshots:
dependencies:
phenomenon: 1.6.0
[email protected]:
dependencies:
color-name: 1.1.4
optional: true
[email protected]:
optional: true
[email protected]:
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
optional: true
[email protected]:
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
optional: true
[email protected]: {}
[email protected]: {}
[email protected]:
optional: true
[email protected]:
dependencies:
graceful-fs: 4.2.11
@@ -1053,9 +1089,6 @@ snapshots:
[email protected]: {}
[email protected]:
optional: true
[email protected]: {}
[email protected]:
@@ -1113,12 +1146,10 @@ snapshots:
[email protected]: {}
minizlib@3.0.2:
minizlib@3.1.0:
dependencies:
minipass: 7.1.2
[email protected]: {}
[email protected]:
dependencies:
motion-utils: 12.23.6
@@ -1135,25 +1166,26 @@ snapshots:
[email protected]: {}
next@15.5.9([email protected]([email protected]))([email protected]):
next@16.1.6([email protected]([email protected]))([email protected]):
dependencies:
'@next/env': 15.5.9
'@next/env': 16.1.6
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.0
caniuse-lite: 1.0.30001739
postcss: 8.4.31
react: 19.1.0
react-dom: 19.1.0([email protected])
styled-jsx: 5.1.6([email protected])
optionalDependencies:
'@next/swc-darwin-arm64': 15.5.7
'@next/swc-darwin-x64': 15.5.7
'@next/swc-linux-arm64-gnu': 15.5.7
'@next/swc-linux-arm64-musl': 15.5.7
'@next/swc-linux-x64-gnu': 15.5.7
'@next/swc-linux-x64-musl': 15.5.7
'@next/swc-win32-arm64-msvc': 15.5.7
'@next/swc-win32-x64-msvc': 15.5.7
sharp: 0.34.3
'@next/swc-darwin-arm64': 16.1.6
'@next/swc-darwin-x64': 16.1.6
'@next/swc-linux-arm64-gnu': 16.1.6
'@next/swc-linux-arm64-musl': 16.1.6
'@next/swc-linux-x64-gnu': 16.1.6
'@next/swc-linux-x64-musl': 16.1.6
'@next/swc-win32-arm64-msvc': 16.1.6
'@next/swc-win32-x64-msvc': 16.1.6
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@@ -1185,42 +1217,39 @@ snapshots:
[email protected]: {}
[email protected].2:
[email protected].4:
optional: true
[email protected].3:
[email protected].5:
dependencies:
color: 4.2.3
detect-libc: 2.0.4
semver: 7.7.2
'@img/colour': 1.1.0
detect-libc: 2.1.2
semver: 7.7.4
optionalDependencies:
'@img/sharp-darwin-arm64': 0.34.3
'@img/sharp-darwin-x64': 0.34.3
'@img/sharp-libvips-darwin-arm64': 1.2.0
'@img/sharp-libvips-darwin-x64': 1.2.0
'@img/sharp-libvips-linux-arm': 1.2.0
'@img/sharp-libvips-linux-arm64': 1.2.0
'@img/sharp-libvips-linux-ppc64': 1.2.0
'@img/sharp-libvips-linux-s390x': 1.2.0
'@img/sharp-libvips-linux-x64': 1.2.0
'@img/sharp-libvips-linuxmusl-arm64': 1.2.0
'@img/sharp-libvips-linuxmusl-x64': 1.2.0
'@img/sharp-linux-arm': 0.34.3
'@img/sharp-linux-arm64': 0.34.3
'@img/sharp-linux-ppc64': 0.34.3
'@img/sharp-linux-s390x': 0.34.3
'@img/sharp-linux-x64': 0.34.3
'@img/sharp-linuxmusl-arm64': 0.34.3
'@img/sharp-linuxmusl-x64': 0.34.3
'@img/sharp-wasm32': 0.34.3
'@img/sharp-win32-arm64': 0.34.3
'@img/sharp-win32-ia32': 0.34.3
'@img/sharp-win32-x64': 0.34.3
optional: true
[email protected]:
dependencies:
is-arrayish: 0.3.2
'@img/sharp-darwin-arm64': 0.34.5
'@img/sharp-darwin-x64': 0.34.5
'@img/sharp-libvips-darwin-arm64': 1.2.4
'@img/sharp-libvips-darwin-x64': 1.2.4
'@img/sharp-libvips-linux-arm': 1.2.4
'@img/sharp-libvips-linux-arm64': 1.2.4
'@img/sharp-libvips-linux-ppc64': 1.2.4
'@img/sharp-libvips-linux-riscv64': 1.2.4
'@img/sharp-libvips-linux-s390x': 1.2.4
'@img/sharp-libvips-linux-x64': 1.2.4
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
'@img/sharp-linux-arm': 0.34.5
'@img/sharp-linux-arm64': 0.34.5
'@img/sharp-linux-ppc64': 0.34.5
'@img/sharp-linux-riscv64': 0.34.5
'@img/sharp-linux-s390x': 0.34.5
'@img/sharp-linux-x64': 0.34.5
'@img/sharp-linuxmusl-arm64': 0.34.5
'@img/sharp-linuxmusl-x64': 0.34.5
'@img/sharp-wasm32': 0.34.5
'@img/sharp-win32-arm64': 0.34.5
'@img/sharp-win32-ia32': 0.34.5
'@img/sharp-win32-x64': 0.34.5
optional: true
[email protected]: {}
@@ -1240,13 +1269,12 @@ snapshots:
[email protected]: {}
tar@7.4.3:
tar@7.5.10:
dependencies:
'@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0
minipass: 7.1.2
minizlib: 3.0.2
mkdirp: 3.0.1
minizlib: 3.1.0
yallist: 5.0.0
[email protected]: {}
+10 -1
View File
@@ -1,6 +1,15 @@
packages:
- '.'
- .
onlyBuiltDependencies:
- '@tailwindcss/oxide'
- sharp
overrides:
next@>=10.0.0 <15.5.10: '>=15.5.10'
next@>=15.5.1-canary.0 <15.5.10: '>=15.5.10'
tar@<7.5.7: '>=7.5.7'
tar@<7.5.8: '>=7.5.8'
tar@<=7.5.2: '>=7.5.3'
tar@<=7.5.3: '>=7.5.4'
tar@<=7.5.9: '>=7.5.10'
+64 -1
View File
@@ -18,7 +18,65 @@ module.exports = {
},
extend: {
colors: {
"blue-primary": "#4facfe", // Cor azul personalizada
// Primary colors
ink: {
DEFAULT: "#0A0A0A",
80: "rgba(10,10,10,.8)",
40: "rgba(10,10,10,.4)",
12: "rgba(10,10,10,.12)",
6: "rgba(10,10,10,.06)",
},
paper: {
DEFAULT: "#F4F2ED",
dark: "#ECEAE4",
},
white: "#FFFFFF",
// Accent
slash: {
DEFAULT: "#E8FF00",
dim: "rgba(232,255,0,.15)",
},
// Neutrals
meta: "#777",
border: "rgba(10,10,10,.1)",
"border-light": "rgba(10,10,10,.06)",
},
fontFamily: {
display: ["Syne", "sans-serif"],
mono: ["Geist Mono", "monospace"],
},
fontSize: {
"ds-xs": ["9px", { lineHeight: "1.4", letterSpacing: "0.14em" }],
"ds-sm": ["10px", { lineHeight: "1.5", letterSpacing: "0.12em" }],
"ds-base": ["11px", { lineHeight: "1.6" }],
"ds-md": ["12px", { lineHeight: "1.7" }],
"ds-lg": ["13px", { lineHeight: "1.6" }],
},
spacing: {
"sp-1": "4px",
"sp-2": "8px",
"sp-3": "12px",
"sp-4": "16px",
"sp-5": "20px",
"sp-6": "24px",
"sp-8": "32px",
"sp-10": "40px",
"sp-12": "48px",
"sp-16": "64px",
"sp-20": "80px",
"sp-24": "96px",
},
borderRadius: {
"r-sm": "4px",
"r-md": "8px",
"r-lg": "16px",
"r-full": "999px",
},
transitionTimingFunction: {
"ease-out": "cubic-bezier(.16,1,.3,1)",
"ease-in": "cubic-bezier(.4,0,1,1)",
},
keyframes: {
"accordion-down": {
@@ -29,10 +87,15 @@ module.exports = {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
pulse: {
"0%, 100%": { opacity: "1", transform: "scale(1)" },
"50%": { opacity: "0.5", transform: "scale(0.75)" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
pulse: "pulse 2s ease-in-out infinite",
},
},
},
+19 -5
View File
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -11,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -19,9 +23,19 @@
}
],
"paths": {
"@/*": ["./*"]
"@/*": [
"./*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}