feat: Initialize portfolio project with core pages, UI components, and configuration files.
This commit is contained in:
1 parent
aa07d6faeb
commit
0523052864
38 files changed
+1165
-460
No files matched your search
+13
-13
@@ -5,13 +5,13 @@ const STORY_DATA = [
|
||||
id: "KERNEL_INIT",
|
||||
title: "Engenharia de fundamentos",
|
||||
content:
|
||||
"Projeto sistemas com foco em previsibilidade, manutenção e crescimento real. Escala entra na arquitetura desde o inÃcio.",
|
||||
"Projeto sistemas com foco em previsibilidade, manutenção e crescimento real. Escala entra na arquitetura desde o início.",
|
||||
},
|
||||
{
|
||||
id: "RUNTIME_EXEC",
|
||||
title: "Entrega sob restrições",
|
||||
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.",
|
||||
"Atuo bem em cenários com legado, prazos apertados e decisões de produto em movimento, sem sacrificar legibilidade técnica.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -19,22 +19,22 @@ const VALUES_DATA = [
|
||||
{
|
||||
title: "Pragmatismo arquitetural",
|
||||
description:
|
||||
"Decisões guiadas por ROI, operabilidade e clareza para o time que mantém o sistema depois.",
|
||||
"Decisões guiadas por ROI, operabilidade e clareza para o time que mantém o sistema depois.",
|
||||
},
|
||||
{
|
||||
title: "Fundação técnica",
|
||||
title: "Fundação técnica",
|
||||
description:
|
||||
"Fundamentos fortes em dados, protocolos e modelagem para evitar soluções frágeis e modismos vazios.",
|
||||
"Fundamentos fortes em dados, protocolos e modelagem para evitar soluções frágeis e modismos vazios.",
|
||||
},
|
||||
{
|
||||
title: "Ownership real",
|
||||
description:
|
||||
"Responsabilidade sobre entrega, comunicação e qualidade final, não só sobre o trecho de código escrito.",
|
||||
"Responsabilidade sobre entrega, comunicação e qualidade final, não só sobre o trecho de código escrito.",
|
||||
},
|
||||
{
|
||||
title: "Security by design",
|
||||
description:
|
||||
"Integridade, proteção de dados e conformidade tratadas como parte do produto, não como remendo.",
|
||||
"Integridade, proteção de dados e conformidade tratadas como parte do produto, não como remendo.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -43,12 +43,12 @@ export default function About() {
|
||||
<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."
|
||||
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
|
||||
Garuva, SC · Remote worldwide
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -65,10 +65,10 @@ export default function About() {
|
||||
|
||||
<div className="ds-card">
|
||||
<div className="ds-kicker">System Signature</div>
|
||||
<h3 className="ds-card-title">Base técnica e postura de entrega</h3>
|
||||
<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.
|
||||
React, Node.js, Tauri, integrações e arquitetura pragmática.
|
||||
</p>
|
||||
<div className="ds-grid-2 mt-sp-6">
|
||||
{VALUES_DATA.map((value) => (
|
||||
|
||||
+15
-11
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import type React from "react";
|
||||
import { useState } from "react";
|
||||
@@ -29,23 +29,23 @@ export default function Contact() {
|
||||
<Section
|
||||
id="contact"
|
||||
eyebrow="CONTACT_PROTOCOL.init"
|
||||
title="Vamos discutir o próximo sistema"
|
||||
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>
|
||||
<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
|
||||
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>Base: Garuva, Santa Catarina · atuação remota</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,7 +62,8 @@ export default function Contact() {
|
||||
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"
|
||||
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.06)] px-sp-4 py-[14px] text-[12px] outline-none transition-colors focus:border-slash"
|
||||
style={{ color: "#fff", backgroundColor: "rgba(255,255,255,0.06)" }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -77,7 +78,8 @@ export default function Contact() {
|
||||
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"
|
||||
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.06)] px-sp-4 py-[14px] text-[12px] outline-none transition-colors focus:border-slash"
|
||||
style={{ color: "#fff", backgroundColor: "rgba(255,255,255,0.06)" }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -90,12 +92,13 @@ export default function Contact() {
|
||||
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"
|
||||
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] px-sp-4 py-[14px] text-[12px] outline-none transition-colors focus:border-slash"
|
||||
style={{ color: "#fff", backgroundColor: "#1a1a1a" }}
|
||||
>
|
||||
<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>
|
||||
<option value="consultancy">Consultoria Técnica Sênior</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
@@ -110,7 +113,8 @@ export default function Contact() {
|
||||
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"
|
||||
className="mt-sp-2 w-full resize-none border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.06)] px-sp-4 py-[14px] text-[12px] outline-none transition-colors focus:border-slash"
|
||||
style={{ color: "#fff", backgroundColor: "rgba(255,255,255,0.06)" }}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="ds-link ds-link-accent border-0">
|
||||
|
||||
@@ -12,7 +12,7 @@ interface ContactFormProps {
|
||||
export function ContactForm({ formType, setFormType }: ContactFormProps) {
|
||||
const { data } = usePortfolioData();
|
||||
const [contactMethod, setContactMethod] = useState<"whatsapp" | "email">(
|
||||
"whatsapp"
|
||||
"whatsapp",
|
||||
);
|
||||
const [userName, setUserName] = useState<string>("");
|
||||
const [userEmail, setUserEmail] = useState<string>("");
|
||||
@@ -57,8 +57,8 @@ export function ContactForm({ formType, setFormType }: ContactFormProps) {
|
||||
hour >= 8 && hour < 12
|
||||
? "manhã"
|
||||
: hour >= 12 && hour < 18
|
||||
? "tarde"
|
||||
: "noite";
|
||||
? "tarde"
|
||||
: "noite";
|
||||
|
||||
return `Olá Evandro, espero que esta ${dayPart} esteja sendo produtiva.
|
||||
|
||||
@@ -119,8 +119,8 @@ ${userName}`;
|
||||
hasInternalTeam === "sim"
|
||||
? "Sim"
|
||||
: hasInternalTeam === "nao"
|
||||
? "Não"
|
||||
: "Parcialmente"
|
||||
? "Não"
|
||||
: "Parcialmente"
|
||||
}\n`;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ ${userName}`;
|
||||
};
|
||||
|
||||
const handleTechnicalComplexityChange = (
|
||||
e: ChangeEvent<HTMLSelectElement>
|
||||
e: ChangeEvent<HTMLSelectElement>,
|
||||
) => {
|
||||
setTechnicalComplexity(e.target.value);
|
||||
};
|
||||
@@ -203,7 +203,7 @@ ${userName}`;
|
||||
const message = encodeURIComponent(
|
||||
formType === "advanced"
|
||||
? generateAdvancedMessage()
|
||||
: customMessage || getGreetingMessageWithUserName()
|
||||
: customMessage || getGreetingMessageWithUserName(),
|
||||
);
|
||||
const phone = data.contact.whatsapp.replace(/\D/g, ""); // Remove caracteres não numéricos
|
||||
return `https://wa.me/${phone}?text=${message}`;
|
||||
@@ -216,7 +216,7 @@ ${userName}`;
|
||||
? generateAdvancedMessage()
|
||||
: `Nome: ${userName}\nEmail: ${userEmail}\nTelefone: ${userPhone}\nEmpresa/Posição: ${userCompany}\nComo conheceu: ${howFound}\n\nTipo de Projeto: ${projectType}\nPrazo Desejado: ${desiredDeadline}\nOrçamento Disponível: ${budgetValue}\nComplexidade Técnica: ${technicalComplexity}\nTem equipe interna: ${hasInternalTeam}\n\nMensagem: ${
|
||||
customMessage || getGreetingMessageWithUserName()
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
return `mailto:${data.contact.email}?subject=${subject}&body=${body}`;
|
||||
};
|
||||
@@ -367,7 +367,7 @@ ${userName}`;
|
||||
|
||||
if (totalValue > 0) {
|
||||
return `Estimativa: ${Math.round(
|
||||
hours
|
||||
hours,
|
||||
)}h × R$ ${hourlyRate}/h = R$ ${totalValue.toFixed(0)}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import Script from 'next/script';
|
||||
import Script from "next/script";
|
||||
|
||||
const GA_MEASUREMENT_ID = 'G-QTT9QT0T3S';
|
||||
const GA_MEASUREMENT_ID = "G-QTT9QT0T3S";
|
||||
|
||||
export default function GoogleAnalytics() {
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="/sys/libs/vendor-utils.js"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
<Script src="/sys/libs/vendor-utils.js" strategy="afterInteractive" />
|
||||
<Script id="google-analytics" strategy="afterInteractive">
|
||||
{`
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
+39
-63
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Logo } from "./Logo";
|
||||
@@ -18,31 +18,23 @@ export function Header() {
|
||||
|
||||
return (
|
||||
<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)",
|
||||
}}
|
||||
className="ds-section-dark fixed inset-x-0 top-0 z-50 h-[52px] w-full border-b border-[rgba(255,255,255,0.06)] bg-[var(--ink)]/95 px-sp-6 backdrop-blur-xl lg:px-sp-16"
|
||||
>
|
||||
<div className="mx-auto flex h-[52px] w-full max-w-[1240px] items-center justify-between px-6 lg:px-16">
|
||||
<div className="ds-section-shell flex h-full items-center justify-between">
|
||||
<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" }}
|
||||
>
|
||||
evandro<span style={{ color: "#E8FF00" }}>.dev.br</span>
|
||||
<Logo variant="mark" size="sm" theme="dark" className="shrink-0" />
|
||||
<span className="font-display text-sm font-extrabold tracking-tight text-white">
|
||||
evandro<span className="text-[var(--slash)]">.dev.br</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<nav className="hidden items-center lg:flex">
|
||||
<ul className="flex list-none items-center gap-2">
|
||||
<ul className="flex list-none items-center gap-sp-2">
|
||||
{navItems.map((item) => (
|
||||
<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)" }}
|
||||
className="ds-link rounded-sm px-sp-3 py-sp-2 text-white/85 transition-colors hover:bg-white/10 hover:text-white"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
@@ -51,76 +43,60 @@ export function Header() {
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<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)",
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-sp-4">
|
||||
<span className="ds-metric hidden rounded-full border border-white/10 px-[10px] py-1 text-white/40 lg:inline-flex">
|
||||
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" }}
|
||||
className="ds-link ds-link-accent hidden rounded-sm font-semibold transition-shadow hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)] lg:inline-flex"
|
||||
>
|
||||
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"
|
||||
className="flex h-8 w-8 flex-col items-center justify-center gap-sp-2 border-none bg-transparent lg:hidden"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
aria-label="Menu"
|
||||
aria-expanded={mobileMenuOpen}
|
||||
>
|
||||
<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" : ""}`}
|
||||
/>
|
||||
<span className={`h-0.5 w-5 bg-white transition-transform duration-300 ${mobileMenuOpen ? "translate-y-[6px] rotate-45" : ""}`} />
|
||||
<span className={`h-0.5 w-5 bg-white transition-opacity duration-300 ${mobileMenuOpen ? "opacity-0" : ""}`} />
|
||||
<span className={`h-0.5 w-5 bg-white transition-transform duration-300 ${mobileMenuOpen ? "-translate-y-[6px] -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)",
|
||||
}}
|
||||
className={`absolute inset-x-0 top-[52px] overflow-hidden border-b border-[rgba(255,255,255,0.06)] bg-[var(--ink)]/98 px-sp-6 backdrop-blur-xl transition-all duration-300 lg:hidden lg:px-sp-16 ${mobileMenuOpen ? "max-h-[420px] opacity-100" : "max-h-0 opacity-0"}`}
|
||||
>
|
||||
<nav className="px-6 py-4">
|
||||
<ul className="flex list-none flex-col gap-2">
|
||||
{navItems.map((item) => (
|
||||
<li key={item.href}>
|
||||
<div className="ds-section-shell flex flex-col py-sp-6">
|
||||
<nav className="mb-sp-6">
|
||||
<ul className="flex list-none flex-col gap-sp-2">
|
||||
{navItems.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="ds-link block rounded-sm py-sp-3 text-white/85 transition-all hover:pl-sp-2 hover:text-white"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li>
|
||||
<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)}
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="ds-link ds-link-accent mt-sp-2 inline-flex rounded-sm font-semibold"
|
||||
>
|
||||
{item.label}
|
||||
Download CV
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<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>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
+37
-22
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { Logo } from "./Logo";
|
||||
|
||||
@@ -48,15 +48,14 @@ export default function Hero() {
|
||||
return (
|
||||
<section
|
||||
id="home"
|
||||
className="relative overflow-hidden px-6 py-24 lg:px-16"
|
||||
style={{ background: "#0A0A0A", color: "#FFFFFF" }}
|
||||
className="relative overflow-hidden bg-[#0A0A0A] px-sp-4 py-sp-12 text-white lg:px-sp-8"
|
||||
>
|
||||
<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 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 className="relative z-10 mx-auto grid min-h-[calc(100vh-52px)] w-full max-w-[1240px] grid-cols-1 items-center gap-8 lg:grid-cols-2">
|
||||
<div>
|
||||
<div className="mb-8 inline-flex items-center gap-2">
|
||||
<div className="mb-sp-8 inline-flex items-center gap-sp-2">
|
||||
<span
|
||||
className="h-1.5 w-1.5 animate-pulse rounded-full"
|
||||
style={{ background: "#E8FF00" }}
|
||||
@@ -70,10 +69,10 @@ export default function Hero() {
|
||||
</div>
|
||||
|
||||
<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" }}
|
||||
className="mb-sp-6 text-[13vw] sm:text-7xl md:text-[6rem] lg:text-[7rem] xl:text-[8rem] leading-[0.9] tracking-[-0.01em]"
|
||||
style={{ color: "#FFFFFF", fontFamily: "'Monument Extended', sans-serif", fontWeight: 800 }}
|
||||
>
|
||||
Evandro
|
||||
evandro
|
||||
<br />
|
||||
<span style={{ color: "#E8FF00" }}>.dev</span>
|
||||
<br />
|
||||
@@ -81,7 +80,7 @@ export default function Hero() {
|
||||
</h1>
|
||||
|
||||
<p
|
||||
className="mb-10 max-w-[460px] text-[13px] leading-7"
|
||||
className="mb-sp-10 max-w-[460px] text-[13px] leading-7"
|
||||
style={{ color: "rgba(255,255,255,.72)" }}
|
||||
>
|
||||
Engenheiro de Software especializado em aplicacoes de alta
|
||||
@@ -89,16 +88,16 @@ export default function Hero() {
|
||||
React, Tauri e arquitetura escalavel.
|
||||
</p>
|
||||
|
||||
<div className="mb-8 flex flex-wrap items-center gap-4">
|
||||
<div className="mb-sp-8 flex flex-wrap items-center gap-sp-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)]"
|
||||
className="inline-flex h-11 items-center rounded-sm px-sp-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>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-sp-2">
|
||||
{SOCIAL_LINKS.map((link) => (
|
||||
<a
|
||||
key={link.label}
|
||||
@@ -141,7 +140,7 @@ export default function Hero() {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="mt-10 grid max-w-[440px] grid-cols-3 gap-px border max-sm:grid-cols-1"
|
||||
className="mt-sp-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)",
|
||||
@@ -161,7 +160,7 @@ export default function Hero() {
|
||||
<span style={{ color: "#E8FF00" }}>{item.accent}</span>
|
||||
</div>
|
||||
<div
|
||||
className="mt-1 text-[9px] uppercase tracking-[0.12em]"
|
||||
className="mt-sp-1 text-[9px] uppercase tracking-[0.12em]"
|
||||
style={{ color: "rgba(255,255,255,.35)" }}
|
||||
>
|
||||
{item.label}
|
||||
@@ -171,7 +170,7 @@ export default function Hero() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-end gap-6 max-lg:items-start">
|
||||
<div className="flex flex-col items-end gap-sp-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={{
|
||||
@@ -183,35 +182,51 @@ export default function Hero() {
|
||||
className="absolute left-6 top-5 text-[9px] tracking-[0.14em]"
|
||||
style={{ color: "rgba(255,255,255,.32)" }}
|
||||
>
|
||||
D / SLASH MARK
|
||||
EV / LOGO MARK
|
||||
</span>
|
||||
<Logo variant="mark" size="xl" theme="dark" />
|
||||
|
||||
<svg
|
||||
width="280"
|
||||
height="280"
|
||||
viewBox="0 0 120 120"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="shrink-0 transition-transform duration-700 hover:scale-110"
|
||||
>
|
||||
<rect x="20" y="30" width="12" height="60" fill="currentColor" />
|
||||
<rect x="32" y="30" width="28" height="12" fill="currentColor" />
|
||||
<rect x="32" y="54" width="18" height="12" fill="currentColor" />
|
||||
<rect x="32" y="78" width="28" height="12" fill="currentColor" />
|
||||
<polygon points="62,90 74,90 92,30 80,30" fill="var(--slash)" />
|
||||
<circle cx="104" cy="84" r="6" fill="currentColor" />
|
||||
</svg>
|
||||
|
||||
<span
|
||||
className="absolute bottom-5 right-6 text-[9px] tracking-[0.14em]"
|
||||
style={{ color: "#E8FF00", opacity: 0.78 }}
|
||||
style={{ color: "var(--slash)", opacity: 0.78 }}
|
||||
>
|
||||
CONCEITO ESCOLHIDO
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-3 text-[9px] tracking-[0.12em]"
|
||||
className="flex items-center gap-sp-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
|
||||
EV / Full mark · identidade visual
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-3 text-[9px] tracking-[0.12em]"
|
||||
className="flex items-center gap-sp-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
|
||||
Escala: 280x280 · svg viewbox
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
"use client";
|
||||
|
||||
interface LogoProps {
|
||||
variant?: "mark" | "wordmark" | "icon";
|
||||
size?: "sm" | "md" | "lg" | "xl";
|
||||
theme?: "dark" | "light" | "accent";
|
||||
className?: string;
|
||||
showTagline?: boolean;
|
||||
}
|
||||
|
||||
const sizeMap = {
|
||||
sm: { width: 32, height: 32 },
|
||||
md: { width: 48, height: 48 },
|
||||
lg: { width: 64, height: 64 },
|
||||
xl: { width: 120, height: 80 },
|
||||
};
|
||||
|
||||
export function Logo({
|
||||
variant = "mark",
|
||||
size = "md",
|
||||
theme = "dark",
|
||||
className = "",
|
||||
showTagline = false,
|
||||
}: Readonly<LogoProps>) {
|
||||
const dimensions = sizeMap[size];
|
||||
|
||||
const colors = {
|
||||
dark: {
|
||||
primary: "#FFFFFF",
|
||||
slash: "#E8FF00",
|
||||
},
|
||||
light: {
|
||||
primary: "#0A0A0A",
|
||||
slash: "#0A0A0A",
|
||||
},
|
||||
accent: {
|
||||
primary: "#0A0A0A",
|
||||
slash: "rgba(0,0,0,.3)",
|
||||
},
|
||||
};
|
||||
|
||||
const c = colors[theme];
|
||||
|
||||
if (variant === "icon") {
|
||||
return (
|
||||
<svg
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
viewBox="0 0 64 64"
|
||||
fill="none"
|
||||
className={className}
|
||||
role="img"
|
||||
aria-label="Evandro icon logo"
|
||||
>
|
||||
<title>Evandro icon logo</title>
|
||||
<rect width="64" height="64" rx="14" fill={c.primary} />
|
||||
<rect
|
||||
x="10"
|
||||
y="10"
|
||||
width="7"
|
||||
height="44"
|
||||
fill={theme === "dark" ? "#0A0A0A" : c.primary}
|
||||
/>
|
||||
<rect
|
||||
x="17"
|
||||
y="10"
|
||||
width="18"
|
||||
height="7"
|
||||
fill={theme === "dark" ? "#0A0A0A" : c.primary}
|
||||
/>
|
||||
<rect
|
||||
x="17"
|
||||
y="28.5"
|
||||
width="14"
|
||||
height="7"
|
||||
fill={theme === "dark" ? "#0A0A0A" : c.primary}
|
||||
/>
|
||||
<rect
|
||||
x="17"
|
||||
y="47"
|
||||
width="18"
|
||||
height="7"
|
||||
fill={theme === "dark" ? "#0A0A0A" : c.primary}
|
||||
/>
|
||||
<polygon points="37,38 41,38 50,26 46,26" fill={c.slash} />
|
||||
<circle
|
||||
cx="52"
|
||||
cy="47"
|
||||
r="5"
|
||||
fill={theme === "dark" ? "#0A0A0A" : c.primary}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
if (variant === "wordmark") {
|
||||
return (
|
||||
<div className={`flex items-center gap-3 ${className}`}>
|
||||
<svg
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
viewBox="0 0 48 32"
|
||||
fill="none"
|
||||
role="img"
|
||||
aria-label="Evandro wordmark logo"
|
||||
>
|
||||
<title>Evandro wordmark logo</title>
|
||||
<rect x="2" y="2" width="7" height="28" fill={c.primary} />
|
||||
<rect x="9" y="2" width="20" height="7" fill={c.primary} />
|
||||
<rect x="9" y="12.5" width="16" height="7" fill={c.primary} />
|
||||
<rect x="9" y="23" width="20" height="7" fill={c.primary} />
|
||||
<polygon points="30,22 34,22 42,10 38,10" fill={c.slash} />
|
||||
<circle cx="44" cy="25" r="4" fill={c.primary} />
|
||||
</svg>
|
||||
<div>
|
||||
<div
|
||||
className="font-display text-lg font-extrabold tracking-tight"
|
||||
style={{ color: c.primary }}
|
||||
>
|
||||
evandro<span style={{ color: c.slash }}>.</span>dev
|
||||
<span style={{ color: c.slash }}>.</span>br
|
||||
</div>
|
||||
{showTagline && (
|
||||
<div
|
||||
className="mt-1 text-[9px] tracking-widest"
|
||||
style={{
|
||||
color:
|
||||
theme === "dark"
|
||||
? "rgba(255,255,255,.25)"
|
||||
: "rgba(10,10,10,.4)",
|
||||
}}
|
||||
>
|
||||
DESENVOLVIMENTO DE SOFTWARE
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
viewBox="0 0 120 80"
|
||||
fill="none"
|
||||
className={className}
|
||||
role="img"
|
||||
aria-label="Evandro mark logo"
|
||||
>
|
||||
<title>Evandro mark logo</title>
|
||||
<rect x="10" y="8" width="9" height="64" fill={c.primary} />
|
||||
<rect x="19" y="8" width="28" height="9" fill={c.primary} />
|
||||
<rect x="19" y="35.5" width="22" height="9" fill={c.primary} />
|
||||
<rect x="19" y="63" width="28" height="9" fill={c.primary} />
|
||||
<polygon points="50,55 57,55 74,25 67,25" fill={c.slash} />
|
||||
<circle cx="80" cy="63" r="7" fill={c.primary} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||
@@ -21,8 +21,8 @@ export default function Projects() {
|
||||
<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."
|
||||
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
|
||||
>
|
||||
<div className="ds-grid-2">
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type React from "react";
|
||||
|
||||
interface SectionProps {
|
||||
id?: string;
|
||||
eyebrow?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
dark?: boolean;
|
||||
aside?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Section = ({
|
||||
id,
|
||||
eyebrow,
|
||||
title,
|
||||
description,
|
||||
dark,
|
||||
aside,
|
||||
children,
|
||||
}: SectionProps) => {
|
||||
const baseClass = dark ? "ds-section ds-section-dark" : "ds-section";
|
||||
return (
|
||||
<section id={id} className={baseClass}>
|
||||
<div className="ds-section-shell">
|
||||
<div className="ds-section-header">
|
||||
<div className="ds-section-meta">
|
||||
{eyebrow && <p className="ds-eyebrow">{eyebrow}</p>}
|
||||
<h2 className="ds-section-title">{title}</h2>
|
||||
{description && (
|
||||
<p className="ds-section-description">{description}</p>
|
||||
)}
|
||||
</div>
|
||||
{aside && <aside>{aside}</aside>}
|
||||
</div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -15,8 +15,8 @@ export default function Skills() {
|
||||
<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."
|
||||
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">
|
||||
|
||||
@@ -1,108 +1,252 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Section } from "./Section";
|
||||
|
||||
const WorkModalitiesData = [
|
||||
interface Tier {
|
||||
id: string;
|
||||
badge: string;
|
||||
level: string;
|
||||
title: string;
|
||||
tagline: string;
|
||||
description: string;
|
||||
deliverables: string[];
|
||||
pricing: string;
|
||||
pricingNote: string;
|
||||
featured?: boolean;
|
||||
ctaLabel: string;
|
||||
ctaHref: string;
|
||||
}
|
||||
|
||||
const TIERS: Tier[] = [
|
||||
{
|
||||
title: "Intervenção Ad-hoc",
|
||||
value: "R$ 35 - 70/hora",
|
||||
badge: "Agilidade",
|
||||
idealFor:
|
||||
"Resolução de gargalos técnicos específicos e consultoria de arquitetura imediata.",
|
||||
id: "discovery",
|
||||
badge: "Entrada / Baixo Risco",
|
||||
level: "01",
|
||||
title: "Auditoria & Diagnóstico Técnico",
|
||||
tagline: "Fotografia clara do problema antes de qualquer comprometimento.",
|
||||
description:
|
||||
"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",
|
||||
"Análise profunda de arquitetura, performance ou segurança com entrega de um plano de ação executável. Ideal para quem precisa de direção antes de contratar execução.",
|
||||
deliverables: [
|
||||
"Relatório de gargalos e dívida técnica",
|
||||
"Mapeamento de riscos e dependências",
|
||||
"Sugestão de stack e cronograma de mitigação",
|
||||
"Plano de ação priorizado",
|
||||
],
|
||||
pricing: "Valor Fixo",
|
||||
pricingNote: "Contrato único. Entrega em até 5 dias úteis.",
|
||||
featured: false,
|
||||
ctaLabel: "Solicitar diagnóstico",
|
||||
ctaHref: "#contact",
|
||||
},
|
||||
{
|
||||
title: "Alocação de sprint",
|
||||
value: "Eficiência financeira",
|
||||
badge: "Mais escolhido",
|
||||
idealFor:
|
||||
"Times que precisam de entrega contínua com previsibilidade de orçamento e prioridade técnica.",
|
||||
id: "cycles",
|
||||
badge: "Mais Escolhido",
|
||||
level: "02",
|
||||
title: "Ciclos de Engenharia",
|
||||
tagline: "Capacidade produtiva garantida. Entrega a cada 2 semanas.",
|
||||
description:
|
||||
"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",
|
||||
"Garantia de disponibilidade",
|
||||
"Prioridade em bugs críticos",
|
||||
"Desenvolvimento contínuo com a previsibilidade de orçamento do escopo fechado e a agilidade de sprints. Milestones claros, sem amarras de contrato longo.",
|
||||
deliverables: [
|
||||
"Features em produção a cada ciclo",
|
||||
"Refatoração iterativa com rastreabilidade",
|
||||
"Documentação técnica atualizada",
|
||||
"Capacidade de pivotar prioridades no início de cada sprint",
|
||||
],
|
||||
pricing: "Fixo por Ciclo",
|
||||
pricingNote: "Assinatura quinzenal ou mensal. Cancele a qualquer ciclo.",
|
||||
featured: true,
|
||||
ctaLabel: "Iniciar ciclo de engenharia",
|
||||
ctaHref: "#contact",
|
||||
},
|
||||
{
|
||||
title: "Engenheiro dedicado",
|
||||
value: "A partir de R$ 2.800/mês",
|
||||
badge: "Estratégico",
|
||||
idealFor:
|
||||
"Empresas que precisam de liderança técnica contínua, coordenação de backlog e direção arquitetural.",
|
||||
id: "fractional",
|
||||
badge: "Premium / High-Ticket",
|
||||
level: "03",
|
||||
title: "Fractional Tech Lead",
|
||||
tagline: "Liderança técnica senior. Sem o custo de um CTO full-time.",
|
||||
description:
|
||||
"Parceria recorrente para sustentação, evolução de sistema, revisão de dívida técnica e tomada de decisão.",
|
||||
benefits: [
|
||||
"SLA prioritário",
|
||||
"Gestão de débito técnico",
|
||||
"Consultoria estratégica",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Squad por projeto",
|
||||
value: "Escopo fechado",
|
||||
badge: "Controle total",
|
||||
idealFor:
|
||||
"Produtos novos, replatforming ou migrações com orçamento definido por entregáveis e marcos claros.",
|
||||
description:
|
||||
"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",
|
||||
"Direção arquitetural, gestão de risco e sustentação de operações críticas. Para empresas que precisam de decisões de alto impacto com responsabilidade contínua.",
|
||||
deliverables: [
|
||||
"SLA prioritário Zero-Risk",
|
||||
"Code review e liderança do time interno",
|
||||
"Decisões de infra: Cloud, VPS, CI/CD",
|
||||
"Consultoria estratégica recorrente",
|
||||
],
|
||||
pricing: "Retainer Mensal",
|
||||
pricingNote: "Renovação mensal. Vagas limitadas.",
|
||||
featured: false,
|
||||
ctaLabel: "Agendar conversa estratégica",
|
||||
ctaHref: "#contact",
|
||||
},
|
||||
];
|
||||
|
||||
function TierCard({ tier }: { tier: Tier }) {
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<article
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onFocus={() => setHovered(true)}
|
||||
onBlur={() => setHovered(false)}
|
||||
tabIndex={0}
|
||||
style={{
|
||||
border: tier.featured ? "2px solid #0A0A0A" : "1px solid rgba(10,10,10,0.1)",
|
||||
background: tier.featured ? "#0A0A0A" : "#fff",
|
||||
padding: "var(--sp-6)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "var(--sp-4)",
|
||||
position: "relative",
|
||||
transition: "border-color 0.3s ease, box-shadow 0.3s ease",
|
||||
outline: "none",
|
||||
cursor: "default",
|
||||
boxShadow: hovered
|
||||
? "0 8px 32px rgba(10,10,10,0.1)"
|
||||
: "0 1px 4px rgba(10,10,10,0.04)",
|
||||
}}
|
||||
>
|
||||
{/* Top row */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "9px",
|
||||
letterSpacing: "0.16em",
|
||||
textTransform: "uppercase",
|
||||
color: tier.featured ? "#E8FF00" : "rgba(10,10,10,0.4)",
|
||||
}}
|
||||
>
|
||||
{tier.badge}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
fontSize: "11px",
|
||||
color: tier.featured ? "rgba(255,255,255,0.15)" : "rgba(10,10,10,0.12)",
|
||||
fontWeight: 800,
|
||||
letterSpacing: "0.04em",
|
||||
}}
|
||||
>
|
||||
{tier.level}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div>
|
||||
<h3
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
fontSize: "clamp(18px, 2.2vw, 26px)",
|
||||
lineHeight: 1.1,
|
||||
letterSpacing: "-0.02em",
|
||||
fontWeight: 800,
|
||||
color: tier.featured ? "#fff" : "#0A0A0A",
|
||||
}}
|
||||
>
|
||||
{tier.title}
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
marginTop: "var(--sp-2)",
|
||||
fontSize: "11px",
|
||||
color: tier.featured ? "rgba(232,255,0,0.85)" : "rgba(10,10,10,0.5)",
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
{tier.tagline}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p style={{ fontSize: "12px", color: tier.featured ? "rgba(255,255,255,0.7)" : "rgba(10,10,10,0.65)", lineHeight: 1.8 }}>
|
||||
{tier.description}
|
||||
</p>
|
||||
|
||||
{/* Deliverables */}
|
||||
<ul
|
||||
style={{
|
||||
paddingLeft: "var(--sp-5)",
|
||||
fontSize: "12px",
|
||||
color: tier.featured ? "rgba(255,255,255,0.6)" : "rgba(10,10,10,0.6)",
|
||||
lineHeight: 1.9,
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
{tier.deliverables.map((d) => (
|
||||
<li key={d} style={{ listStyleType: "disc" }}>
|
||||
{d}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Pricing */}
|
||||
<div
|
||||
style={{
|
||||
borderTop: tier.featured ? "1px solid rgba(255,255,255,0.1)" : "1px solid rgba(10,10,10,0.08)",
|
||||
paddingTop: "var(--sp-4)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
fontSize: "clamp(14px, 1.5vw, 18px)",
|
||||
fontWeight: 800,
|
||||
color: tier.featured ? "#E8FF00" : "#0A0A0A",
|
||||
letterSpacing: "-0.01em",
|
||||
}}
|
||||
>
|
||||
{tier.pricing}
|
||||
</div>
|
||||
<div style={{ fontSize: "10px", color: tier.featured ? "rgba(255,255,255,0.3)" : "rgba(10,10,10,0.35)", marginTop: "4px", letterSpacing: "0.06em" }}>
|
||||
{tier.pricingNote}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA — reveals on hover/focus */}
|
||||
<a
|
||||
href={tier.ctaHref}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "12px 18px",
|
||||
fontSize: "10px",
|
||||
letterSpacing: "0.12em",
|
||||
textTransform: "uppercase",
|
||||
textDecoration: "none",
|
||||
fontWeight: 700,
|
||||
background: tier.featured ? "#E8FF00" : "#0A0A0A",
|
||||
color: tier.featured ? "#0A0A0A" : "#fff",
|
||||
transition: "opacity 0.25s ease, transform 0.25s ease",
|
||||
opacity: hovered ? 1 : 0,
|
||||
transform: hovered ? "translateY(0)" : "translateY(6px)",
|
||||
pointerEvents: hovered ? "auto" : "none",
|
||||
}}
|
||||
>
|
||||
{tier.ctaLabel} →
|
||||
</a>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkModalities() {
|
||||
return (
|
||||
<Section
|
||||
id="modalities"
|
||||
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."
|
||||
title="Engenharia sob demanda. Previsibilidade de entrega sem overhead."
|
||||
description="Três níveis de contratação desenhados como uma esteira de valor — do diagnóstico rápido à parceria estratégica de longo prazo."
|
||||
>
|
||||
<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>
|
||||
|
||||
<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
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
|
||||
gap: "var(--sp-6)",
|
||||
}}
|
||||
>
|
||||
{TIERS.map((tier) => (
|
||||
<TierCard key={tier.id} tier={tier} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// src/components/ui/NumberIcons.tsx
|
||||
import React from 'react';
|
||||
|
||||
interface NumberIconProps extends React.SVGProps<SVGSVGElement> {
|
||||
/** Tamanho em pixels. Escala mantendo o viewBox 80x80 */
|
||||
size?: number | string;
|
||||
/** Classe opcional para sobreposição de estilos */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const baseProps = {
|
||||
fill: 'none',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
viewBox: '0 0 80 80',
|
||||
};
|
||||
|
||||
/**
|
||||
* Primitive SVG elements compartilhados.
|
||||
* Depende da classe pai 'group' para engatilhar as micro-interações.
|
||||
*/
|
||||
const GraphicShell = () => (
|
||||
<>
|
||||
{/* Base Background */}
|
||||
<rect width="80" height="80" rx="8" className="fill-[#0A0A0A]" />
|
||||
|
||||
{/* Borda reativa ao hover */}
|
||||
<rect
|
||||
x="1" y="1" width="78" height="78" rx="7"
|
||||
className="stroke-white/5 transition-colors duration-400 ease-[var(--ease-out)] group-hover:stroke-white/15"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
|
||||
{/* Grid de background sutil (Cyberpunk/Tech feel) */}
|
||||
<g className="stroke-white/[0.03]" strokeWidth="1">
|
||||
<path d="M0 20H80M0 60H80M20 0V80M60 0V80" />
|
||||
</g>
|
||||
|
||||
{/* Brand Slash (Acento) com Motion */}
|
||||
<polygon
|
||||
points="58,66 62,66 70,50 66,50"
|
||||
className="fill-[#E8FF00] transition-transform duration-400 ease-[var(--ease-out)] origin-bottom-right group-hover:-translate-y-1 group-hover:scale-110"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
export const NumberOne = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 1"
|
||||
{...baseProps}
|
||||
{...props}
|
||||
>
|
||||
<GraphicShell />
|
||||
<text
|
||||
x="40" y="54"
|
||||
fontFamily="Syne, sans-serif"
|
||||
fontSize="40"
|
||||
fontWeight="800"
|
||||
textAnchor="middle"
|
||||
className="fill-white transition-transform duration-400 ease-[var(--ease-out)] group-hover:-translate-y-0.5"
|
||||
>
|
||||
1
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NumberTwo = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 2"
|
||||
{...baseProps}
|
||||
{...props}
|
||||
>
|
||||
<GraphicShell />
|
||||
<text
|
||||
x="40" y="54"
|
||||
fontFamily="Syne, sans-serif"
|
||||
fontSize="40"
|
||||
fontWeight="800"
|
||||
textAnchor="middle"
|
||||
className="fill-white transition-transform duration-400 ease-[var(--ease-out)] group-hover:-translate-y-0.5"
|
||||
>
|
||||
2
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NumberThree = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 3"
|
||||
{...baseProps}
|
||||
{...props}
|
||||
>
|
||||
<GraphicShell />
|
||||
<text
|
||||
x="40" y="54"
|
||||
fontFamily="Syne, sans-serif"
|
||||
fontSize="40"
|
||||
fontWeight="800"
|
||||
textAnchor="middle"
|
||||
className="fill-white transition-transform duration-400 ease-[var(--ease-out)] group-hover:-translate-y-0.5"
|
||||
>
|
||||
3
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NumberFour = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 4"
|
||||
{...baseProps}
|
||||
{...props}
|
||||
>
|
||||
<GraphicShell />
|
||||
<text
|
||||
x="40" y="54"
|
||||
fontFamily="Syne, sans-serif"
|
||||
fontSize="40"
|
||||
fontWeight="800"
|
||||
textAnchor="middle"
|
||||
className="fill-white transition-transform duration-400 ease-[var(--ease-out)] group-hover:-translate-y-0.5"
|
||||
>
|
||||
4
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
+33
-1
@@ -1,9 +1,40 @@
|
||||
@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");
|
||||
|
||||
@font-face {
|
||||
font-family: 'Monument Extended';
|
||||
src: url('/fonts/MonumentExtended-Regular.otf') format('opentype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Monument Extended';
|
||||
src: url('/fonts/MonumentExtended-Ultrabold.otf') format('opentype');
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@theme {
|
||||
--spacing-sp-1: var(--sp-1);
|
||||
--spacing-sp-2: var(--sp-2);
|
||||
--spacing-sp-3: var(--sp-3);
|
||||
--spacing-sp-4: var(--sp-4);
|
||||
--spacing-sp-5: var(--sp-5);
|
||||
--spacing-sp-6: var(--sp-6);
|
||||
--spacing-sp-8: var(--sp-8);
|
||||
--spacing-sp-10: var(--sp-10);
|
||||
--spacing-sp-12: var(--sp-12);
|
||||
--spacing-sp-16: var(--sp-16);
|
||||
--spacing-sp-20: var(--sp-20);
|
||||
--spacing-sp-24: var(--sp-24);
|
||||
}
|
||||
|
||||
:root {
|
||||
--ink: #0a0a0a;
|
||||
--ink-80: rgba(10, 10, 10, 0.8);
|
||||
@@ -35,6 +66,7 @@
|
||||
--r-lg: 16px;
|
||||
--r-full: 999px;
|
||||
--font-display: "Syne", sans-serif;
|
||||
--font-hero: 'Monument Extended', 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);
|
||||
@@ -100,7 +132,7 @@ textarea {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.ds-section:nth-of-type(even) {
|
||||
.ds-section:nth-of-type(even):not(.ds-section-dark) {
|
||||
background: var(--paper-dark);
|
||||
}
|
||||
|
||||
|
||||
+17
-14
@@ -4,7 +4,8 @@ import GoogleAnalytics from "./components/GoogleAnalytics";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||
title:
|
||||
"Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||
description:
|
||||
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js, React, Tauri e arquitetura escalável.",
|
||||
keywords: [
|
||||
@@ -42,7 +43,8 @@ export const metadata: Metadata = {
|
||||
canonical: "/",
|
||||
},
|
||||
openGraph: {
|
||||
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||
title:
|
||||
"Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||
description:
|
||||
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js, React, Tauri e arquitetura escalável.",
|
||||
url: "https://evandro.dev.br",
|
||||
@@ -60,7 +62,8 @@ export const metadata: Metadata = {
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||
title:
|
||||
"Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||
description:
|
||||
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js, React, Tauri e arquitetura escalável.",
|
||||
creator: "@evandrodevbr",
|
||||
@@ -94,17 +97,17 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "ProfilePage",
|
||||
"mainEntity": {
|
||||
mainEntity: {
|
||||
"@type": "Person",
|
||||
"name": "Evandro Fonseca Junior",
|
||||
"alternateName": "Evandro.dev",
|
||||
"jobTitle": "Engenheiro de Software",
|
||||
"url": "https://evandro.dev.br",
|
||||
"sameAs": [
|
||||
name: "Evandro Fonseca Junior",
|
||||
alternateName: "Evandro.dev",
|
||||
jobTitle: "Engenheiro de Software",
|
||||
url: "https://evandro.dev.br",
|
||||
sameAs: [
|
||||
"https://github.com/evandrodevbr",
|
||||
"https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/",
|
||||
],
|
||||
"knowsAbout": [
|
||||
knowsAbout: [
|
||||
"Software Engineering",
|
||||
"Next.js",
|
||||
"React",
|
||||
@@ -119,11 +122,11 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
|
||||
"PostgreSQL",
|
||||
"MySQL",
|
||||
],
|
||||
"worksFor": {
|
||||
worksFor: {
|
||||
"@type": "Organization",
|
||||
"name": "Freelancer / Consultor Independente",
|
||||
name: "Freelancer / Consultor Independente",
|
||||
},
|
||||
"description":
|
||||
description:
|
||||
"Engenheiro de software especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js 15, React 19, Tauri e arquitetura escalável. Experiência em automação, computação em nuvem e sistemas distribuídos.",
|
||||
},
|
||||
};
|
||||
@@ -143,4 +146,4 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
+20
-19
@@ -1,29 +1,30 @@
|
||||
import { MetadataRoute } from 'next'
|
||||
import { MetadataRoute } from "next";
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: 'Evandro Fonseca Junior - Portfólio',
|
||||
short_name: 'Evandro.dev',
|
||||
description: 'Portfólio profissional de Evandro Fonseca Junior, Desenvolvedor Full-Stack',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
background_color: '#000000',
|
||||
theme_color: '#000000',
|
||||
orientation: 'portrait-primary',
|
||||
categories: ['portfolio', 'developer', 'technology'],
|
||||
lang: 'pt-BR',
|
||||
dir: 'ltr',
|
||||
name: "Evandro Fonseca Junior - Portfólio",
|
||||
short_name: "Evandro.dev",
|
||||
description:
|
||||
"Portfólio profissional de Evandro Fonseca Junior, Desenvolvedor Full-Stack",
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: "#000000",
|
||||
theme_color: "#000000",
|
||||
orientation: "portrait-primary",
|
||||
categories: ["portfolio", "developer", "technology"],
|
||||
lang: "pt-BR",
|
||||
dir: "ltr",
|
||||
icons: [
|
||||
{
|
||||
src: '/favicon.ico',
|
||||
sizes: 'any',
|
||||
type: 'image/x-icon',
|
||||
src: "/favicon.ico",
|
||||
sizes: "any",
|
||||
type: "image/x-icon",
|
||||
},
|
||||
{
|
||||
src: '/icon.svg',
|
||||
sizes: 'any',
|
||||
type: 'image/svg+xml',
|
||||
src: "/icon.svg",
|
||||
sizes: "any",
|
||||
type: "image/svg+xml",
|
||||
},
|
||||
],
|
||||
}
|
||||
};
|
||||
}
|
||||
+4
-2
@@ -7,7 +7,9 @@ export default function NotFound() {
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<div className="mb-8">
|
||||
<Code2 className="w-24 h-24 text-blue-500 mx-auto mb-4 opacity-50" />
|
||||
<h1 className="text-6xl md:text-8xl font-black text-white mb-4">404</h1>
|
||||
<h1 className="text-6xl md:text-8xl font-black text-white mb-4">
|
||||
404
|
||||
</h1>
|
||||
<h2 className="text-2xl md:text-3xl font-bold text-slate-400 mb-4">
|
||||
Página não encontrada
|
||||
</h2>
|
||||
@@ -15,7 +17,7 @@ export default function NotFound() {
|
||||
A página que você está procurando não existe ou foi movida.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<Link
|
||||
href="/"
|
||||
|
||||
+36
-38
@@ -1,51 +1,49 @@
|
||||
import { ImageResponse } from 'next/og'
|
||||
import { ImageResponse } from "next/og";
|
||||
|
||||
export const runtime = 'edge'
|
||||
export const alt = 'Evandro Fonseca Junior - Engenheiro de Software'
|
||||
export const runtime = "edge";
|
||||
export const alt = "Evandro Fonseca Junior - Engenheiro de Software";
|
||||
export const size = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
}
|
||||
export const contentType = 'image/png'
|
||||
};
|
||||
export const contentType = "image/png";
|
||||
|
||||
export default async function Image() {
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
background: "#030712",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "white",
|
||||
fontFamily: "system-ui",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: '#030712',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'white',
|
||||
fontFamily: 'system-ui',
|
||||
fontSize: 64,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 20,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 64,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 20,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Evandro Fonseca Junior
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 32,
|
||||
color: '#3b82f6',
|
||||
marginTop: 10,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
Engenheiro de Software
|
||||
</div>
|
||||
Evandro Fonseca Junior
|
||||
</div>
|
||||
),
|
||||
{ ...size }
|
||||
)
|
||||
<div
|
||||
style={{
|
||||
fontSize: 32,
|
||||
color: "#3b82f6",
|
||||
marginTop: 10,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Engenheiro de Software
|
||||
</div>
|
||||
</div>,
|
||||
{ ...size },
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@ import { WorkModalities } from "./components/WorkModalities";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex min-h-screen flex-col overflow-x-hidden">
|
||||
<Header />
|
||||
<main className="ds-main">
|
||||
<Hero />
|
||||
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
import { MetadataRoute } from 'next'
|
||||
import { MetadataRoute } from "next";
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/_next/', '/admin/'],
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
disallow: ["/api/", "/_next/", "/admin/"],
|
||||
},
|
||||
],
|
||||
sitemap: 'https://evandro.dev.br/sitemap.xml',
|
||||
}
|
||||
sitemap: "https://evandro.dev.br/sitemap.xml",
|
||||
};
|
||||
}
|
||||
+10
-10
@@ -1,45 +1,45 @@
|
||||
import { MetadataRoute } from 'next'
|
||||
import { MetadataRoute } from "next";
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const baseUrl = 'https://evandro.dev.br'
|
||||
const currentDate = new Date().toISOString()
|
||||
const baseUrl = "https://evandro.dev.br";
|
||||
const currentDate = new Date().toISOString();
|
||||
|
||||
return [
|
||||
{
|
||||
url: baseUrl,
|
||||
lastModified: currentDate,
|
||||
changeFrequency: 'monthly',
|
||||
changeFrequency: "monthly",
|
||||
priority: 1.0,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/#about`,
|
||||
lastModified: currentDate,
|
||||
changeFrequency: 'monthly',
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/#skills`,
|
||||
lastModified: currentDate,
|
||||
changeFrequency: 'monthly',
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/#experience`,
|
||||
lastModified: currentDate,
|
||||
changeFrequency: 'monthly',
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/#projects`,
|
||||
lastModified: currentDate,
|
||||
changeFrequency: 'monthly',
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/#contact`,
|
||||
lastModified: currentDate,
|
||||
changeFrequency: 'monthly',
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.8,
|
||||
},
|
||||
]
|
||||
];
|
||||
}
|
||||
Reference in new issue
Block a user