feat: Implement initial portfolio website structure and core components including main page, header, hero, contact, and work modalities.
This commit is contained in:
1 parent
0523052864
commit
6299ae040a
7 files changed
+276
-150
No files matched your search
@@ -39,8 +39,7 @@ export default function Contact() {
|
|||||||
<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">
|
<p className="ds-copy">
|
||||||
Ideal para discovery técnico, revisão de arquitetura, rescue de
|
Ideal para discovery técnico, revisão de arquitetura, rescue de
|
||||||
projeto, otimização de performance ou construção de produto do
|
projeto, otimização de performance ou construção de produto do zero.
|
||||||
zero.
|
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-sp-6 space-y-sp-4 text-[12px] leading-7 text-[rgba(255,255,255,.7)]">
|
<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>WhatsApp: +55 47 99274-2365</div>
|
||||||
@@ -63,7 +62,10 @@ export default function Contact() {
|
|||||||
setFormData({ ...formData, name: e.target.value })
|
setFormData({ ...formData, name: e.target.value })
|
||||||
}
|
}
|
||||||
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"
|
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)" }}
|
style={{
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.06)",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -79,7 +81,10 @@ export default function Contact() {
|
|||||||
setFormData({ ...formData, email: e.target.value })
|
setFormData({ ...formData, email: e.target.value })
|
||||||
}
|
}
|
||||||
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"
|
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)" }}
|
style={{
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.06)",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -114,7 +119,10 @@ export default function Contact() {
|
|||||||
setFormData({ ...formData, message: e.target.value })
|
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,.06)] px-sp-4 py-[14px] text-[12px] 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)" }}
|
style={{
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.06)",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className="ds-link ds-link-accent border-0">
|
<button type="submit" className="ds-link ds-link-accent border-0">
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ export function Header() {
|
|||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header 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">
|
||||||
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="ds-section-shell flex h-full items-center justify-between">
|
<div className="ds-section-shell flex h-full items-center justify-between">
|
||||||
<a href="#home" className="flex items-center gap-3 no-underline">
|
<a href="#home" className="flex items-center gap-3 no-underline">
|
||||||
<Logo variant="mark" size="sm" theme="dark" className="shrink-0" />
|
<Logo variant="mark" size="sm" theme="dark" className="shrink-0" />
|
||||||
@@ -61,9 +59,15 @@ export function Header() {
|
|||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
aria-expanded={mobileMenuOpen}
|
aria-expanded={mobileMenuOpen}
|
||||||
>
|
>
|
||||||
<span className={`h-0.5 w-5 bg-white transition-transform duration-300 ${mobileMenuOpen ? "translate-y-[6px] rotate-45" : ""}`} />
|
<span
|
||||||
<span className={`h-0.5 w-5 bg-white transition-opacity duration-300 ${mobileMenuOpen ? "opacity-0" : ""}`} />
|
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-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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+172
-111
@@ -1,5 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { Logo } from "./Logo";
|
import { Logo } from "./Logo";
|
||||||
|
|
||||||
const SOCIAL_LINKS = [
|
const SOCIAL_LINKS = [
|
||||||
@@ -30,13 +32,103 @@ const SOCIAL_LINKS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const STATS = [
|
const textToType = "evandro.dev.br";
|
||||||
{ value: "4", accent: "px", label: "Base unit" },
|
|
||||||
{ value: "9", accent: "x", label: "Type scale" },
|
|
||||||
{ value: "4", accent: "+", label: "Core tokens" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function Hero() {
|
export default function Hero() {
|
||||||
|
const [phase, setPhase] = useState<"typing" | "idle" | "glitch" | "logo">(
|
||||||
|
"typing",
|
||||||
|
);
|
||||||
|
const [typedCount, setTypedCount] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let timeout: NodeJS.Timeout;
|
||||||
|
|
||||||
|
if (phase === "typing") {
|
||||||
|
if (typedCount < textToType.length) {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
setTypedCount((c) => c + 1);
|
||||||
|
}, 120); // typing speed
|
||||||
|
} else {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
setPhase("idle");
|
||||||
|
}, 1200); // Wait after typing finishes
|
||||||
|
}
|
||||||
|
} else if (phase === "idle") {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
setPhase("glitch");
|
||||||
|
}, 100); // trigger glitch
|
||||||
|
} else if (phase === "glitch") {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
setPhase("logo");
|
||||||
|
}, 600); // Glitch out duration
|
||||||
|
} else if (phase === "logo") {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
setTypedCount(0);
|
||||||
|
setPhase("typing");
|
||||||
|
}, 3500); // Logo display and blink duration
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [phase, typedCount]);
|
||||||
|
|
||||||
|
const renderTypedText = () => {
|
||||||
|
const chars = [];
|
||||||
|
for (let i = 0; i < typedCount; i++) {
|
||||||
|
const char = textToType[i];
|
||||||
|
if (i < 7) {
|
||||||
|
chars.push(
|
||||||
|
<span key={i} style={{ color: "#FFFFFF" }}>
|
||||||
|
{char}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
} else if (i === 7) {
|
||||||
|
chars.push(<br key="br1" />);
|
||||||
|
chars.push(
|
||||||
|
<span key={i} style={{ color: "#E8FF00" }}>
|
||||||
|
{char}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
} else if (i < 11) {
|
||||||
|
chars.push(
|
||||||
|
<span key={i} style={{ color: "#E8FF00" }}>
|
||||||
|
{char}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
} else if (i === 11) {
|
||||||
|
chars.push(<br key="br2" />);
|
||||||
|
chars.push(
|
||||||
|
<span key={i} style={{ color: "#FFFFFF" }}>
|
||||||
|
{char}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
chars.push(
|
||||||
|
<span key={i} style={{ color: "#FFFFFF" }}>
|
||||||
|
{char}
|
||||||
|
</span>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// blinking cursor
|
||||||
|
if (phase === "typing" || phase === "idle") {
|
||||||
|
chars.push(
|
||||||
|
<motion.span
|
||||||
|
key="cursor"
|
||||||
|
animate={{ opacity: [1, 0] }}
|
||||||
|
transition={{ repeat: Infinity, duration: 0.8 }}
|
||||||
|
style={{
|
||||||
|
color: "#E8FF00",
|
||||||
|
display: "inline-block",
|
||||||
|
marginLeft: "4px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
_
|
||||||
|
</motion.span>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return chars;
|
||||||
|
};
|
||||||
|
|
||||||
const hour = new Date().getHours();
|
const hour = new Date().getHours();
|
||||||
const greeting =
|
const greeting =
|
||||||
hour >= 8 && hour < 12
|
hour >= 8 && hour < 12
|
||||||
@@ -53,8 +145,13 @@ export default function Hero() {
|
|||||||
<div className="pointer-events-none absolute inset-0 bg-grid-light opacity-70" />
|
<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="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-8 lg:grid-cols-2">
|
<div className="relative z-10 mx-auto grid min-h-[calc(100vh)] w-full max-w-[1000px] grid-cols-1 items-center gap-8">
|
||||||
<div>
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true, margin: "-100px" }}
|
||||||
|
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||||
|
>
|
||||||
<div className="mb-sp-8 inline-flex items-center gap-sp-2">
|
<div className="mb-sp-8 inline-flex items-center gap-sp-2">
|
||||||
<span
|
<span
|
||||||
className="h-1.5 w-1.5 animate-pulse rounded-full"
|
className="h-1.5 w-1.5 animate-pulse rounded-full"
|
||||||
@@ -68,16 +165,61 @@ export default function Hero() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1
|
<div className="relative mb-sp-6 flex min-h-[40vw] sm:min-h-[220px] md:min-h-[280px] lg:min-h-[350px] items-center">
|
||||||
className="mb-sp-6 text-[13vw] sm:text-7xl md:text-[6rem] lg:text-[7rem] xl:text-[8rem] leading-[0.9] tracking-[-0.01em]"
|
<AnimatePresence mode="wait">
|
||||||
style={{ color: "#FFFFFF", fontFamily: "'Monument Extended', sans-serif", fontWeight: 800 }}
|
{(phase === "typing" || phase === "idle") && (
|
||||||
>
|
<motion.h1
|
||||||
evandro
|
key="text"
|
||||||
<br />
|
initial={{ opacity: 0 }}
|
||||||
<span style={{ color: "#E8FF00" }}>.dev</span>
|
animate={{ opacity: 1, filter: "blur(0px)", scale: 1 }}
|
||||||
<br />
|
exit={{ opacity: 0, filter: "blur(12px)", scale: 1.05 }}
|
||||||
.br
|
transition={{ duration: 0.5, ease: "easeInOut" }}
|
||||||
</h1>
|
className="absolute 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,
|
||||||
|
}}
|
||||||
|
aria-label="evandro.dev.br"
|
||||||
|
>
|
||||||
|
{renderTypedText()}
|
||||||
|
</motion.h1>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{phase === "logo" && (
|
||||||
|
<motion.div
|
||||||
|
key="logo"
|
||||||
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
|
animate={{
|
||||||
|
opacity: [0, 1, 0, 1, 1, 0.5, 1, 0],
|
||||||
|
scale: 1,
|
||||||
|
filter: [
|
||||||
|
"blur(4px)",
|
||||||
|
"blur(0px)",
|
||||||
|
"blur(0px)",
|
||||||
|
"blur(0px)",
|
||||||
|
"blur(0px)",
|
||||||
|
"blur(2px)",
|
||||||
|
"blur(0px)",
|
||||||
|
"blur(10px)",
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 3.2,
|
||||||
|
times: [0, 0.05, 0.1, 0.15, 0.8, 0.85, 0.9, 1],
|
||||||
|
}}
|
||||||
|
className="absolute flex items-center justify-start w-full"
|
||||||
|
>
|
||||||
|
<Logo
|
||||||
|
variant="mark"
|
||||||
|
size="xl"
|
||||||
|
theme="dark"
|
||||||
|
className="w-[80%] max-w-[320px] h-auto drop-shadow-[0_0_20px_rgba(232,255,0,0.3)]"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
className="mb-sp-10 max-w-[460px] text-[13px] leading-7"
|
className="mb-sp-10 max-w-[460px] text-[13px] leading-7"
|
||||||
@@ -88,15 +230,24 @@ export default function Hero() {
|
|||||||
React, Tauri e arquitetura escalavel.
|
React, Tauri e arquitetura escalavel.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mb-sp-8 flex flex-wrap items-center gap-sp-4">
|
<div className="mb-sp-8 mt-sp-6 flex flex-wrap items-center gap-sp-4">
|
||||||
<a
|
<a
|
||||||
href={`https://wa.me/5547992742365?text=${encodeURIComponent(greeting)}`}
|
href={`https://wa.me/5547992742365?text=${encodeURIComponent(greeting)}`}
|
||||||
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)]"
|
className="inline-flex h-11 items-center justify-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" }}
|
style={{ background: "#E8FF00", color: "#0A0A0A" }}
|
||||||
>
|
>
|
||||||
Iniciar projeto
|
Iniciar projeto
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="/assets/Evandro_Fonseca_Junior_CV.pdf"
|
||||||
|
download
|
||||||
|
className="inline-flex h-11 items-center justify-center rounded-sm border px-sp-6 text-[11px] font-semibold uppercase tracking-[0.08em] no-underline transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/5"
|
||||||
|
style={{ borderColor: "rgba(255,255,255,.18)", color: "#FFFFFF" }}
|
||||||
|
>
|
||||||
|
Download CV
|
||||||
|
</a>
|
||||||
|
|
||||||
<div className="flex items-center gap-sp-2">
|
<div className="flex items-center gap-sp-2">
|
||||||
{SOCIAL_LINKS.map((link) => (
|
{SOCIAL_LINKS.map((link) => (
|
||||||
<a
|
<a
|
||||||
@@ -108,9 +259,8 @@ export default function Hero() {
|
|||||||
? "noopener noreferrer"
|
? "noopener noreferrer"
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
className="inline-flex h-11 w-11 items-center justify-center rounded-sm border transition-all duration-200 hover:bg-white/10"
|
className="inline-flex h-11 w-11 items-center justify-center rounded-full transition-all duration-200 hover:bg-white/10 hover:scale-110"
|
||||||
style={{
|
style={{
|
||||||
borderColor: "rgba(255,255,255,.18)",
|
|
||||||
color: "rgba(255,255,255,.84)",
|
color: "rgba(255,255,255,.84)",
|
||||||
}}
|
}}
|
||||||
aria-label={link.label}
|
aria-label={link.label}
|
||||||
@@ -139,96 +289,7 @@ export default function Hero() {
|
|||||||
Garuva, SC | Remote worldwide
|
Garuva, SC | Remote worldwide
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
</motion.div>
|
||||||
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)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{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-sp-1 text-[9px] uppercase tracking-[0.12em]"
|
|
||||||
style={{ color: "rgba(255,255,255,.35)" }}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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={{
|
|
||||||
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)" }}
|
|
||||||
>
|
|
||||||
EV / LOGO MARK
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<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: "var(--slash)", opacity: 0.78 }}
|
|
||||||
>
|
|
||||||
CONCEITO ESCOLHIDO
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
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)" }}
|
|
||||||
/>
|
|
||||||
EV / Full mark · identidade visual
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
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: 280x280 · svg viewbox
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -92,7 +92,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
onBlur={() => setHovered(false)}
|
onBlur={() => setHovered(false)}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
style={{
|
style={{
|
||||||
border: tier.featured ? "2px solid #0A0A0A" : "1px solid rgba(10,10,10,0.1)",
|
border: tier.featured
|
||||||
|
? "2px solid #0A0A0A"
|
||||||
|
: "1px solid rgba(10,10,10,0.1)",
|
||||||
background: tier.featured ? "#0A0A0A" : "#fff",
|
background: tier.featured ? "#0A0A0A" : "#fff",
|
||||||
padding: "var(--sp-6)",
|
padding: "var(--sp-6)",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -108,7 +110,13 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Top row */}
|
{/* Top row */}
|
||||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
fontSize: "9px",
|
fontSize: "9px",
|
||||||
@@ -123,7 +131,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
style={{
|
style={{
|
||||||
fontFamily: "var(--font-display)",
|
fontFamily: "var(--font-display)",
|
||||||
fontSize: "11px",
|
fontSize: "11px",
|
||||||
color: tier.featured ? "rgba(255,255,255,0.15)" : "rgba(10,10,10,0.12)",
|
color: tier.featured
|
||||||
|
? "rgba(255,255,255,0.15)"
|
||||||
|
: "rgba(10,10,10,0.12)",
|
||||||
fontWeight: 800,
|
fontWeight: 800,
|
||||||
letterSpacing: "0.04em",
|
letterSpacing: "0.04em",
|
||||||
}}
|
}}
|
||||||
@@ -150,7 +160,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
style={{
|
style={{
|
||||||
marginTop: "var(--sp-2)",
|
marginTop: "var(--sp-2)",
|
||||||
fontSize: "11px",
|
fontSize: "11px",
|
||||||
color: tier.featured ? "rgba(232,255,0,0.85)" : "rgba(10,10,10,0.5)",
|
color: tier.featured
|
||||||
|
? "rgba(232,255,0,0.85)"
|
||||||
|
: "rgba(10,10,10,0.5)",
|
||||||
lineHeight: 1.6,
|
lineHeight: 1.6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -159,7 +171,15 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<p style={{ fontSize: "12px", color: tier.featured ? "rgba(255,255,255,0.7)" : "rgba(10,10,10,0.65)", lineHeight: 1.8 }}>
|
<p
|
||||||
|
style={{
|
||||||
|
fontSize: "12px",
|
||||||
|
color: tier.featured
|
||||||
|
? "rgba(255,255,255,0.7)"
|
||||||
|
: "rgba(10,10,10,0.65)",
|
||||||
|
lineHeight: 1.8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{tier.description}
|
{tier.description}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -183,7 +203,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
{/* Pricing */}
|
{/* Pricing */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
borderTop: tier.featured ? "1px solid rgba(255,255,255,0.1)" : "1px solid rgba(10,10,10,0.08)",
|
borderTop: tier.featured
|
||||||
|
? "1px solid rgba(255,255,255,0.1)"
|
||||||
|
: "1px solid rgba(10,10,10,0.08)",
|
||||||
paddingTop: "var(--sp-4)",
|
paddingTop: "var(--sp-4)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -198,7 +220,16 @@ function TierCard({ tier }: { tier: Tier }) {
|
|||||||
>
|
>
|
||||||
{tier.pricing}
|
{tier.pricing}
|
||||||
</div>
|
</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" }}>
|
<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}
|
{tier.pricingNote}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// src/components/ui/NumberIcons.tsx
|
// src/components/ui/NumberIcons.tsx
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
interface NumberIconProps extends React.SVGProps<SVGSVGElement> {
|
interface NumberIconProps extends React.SVGProps<SVGSVGElement> {
|
||||||
/** Tamanho em pixels. Escala mantendo o viewBox 80x80 */
|
/** Tamanho em pixels. Escala mantendo o viewBox 80x80 */
|
||||||
@@ -9,9 +9,9 @@ interface NumberIconProps extends React.SVGProps<SVGSVGElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const baseProps = {
|
const baseProps = {
|
||||||
fill: 'none',
|
fill: "none",
|
||||||
xmlns: 'http://www.w3.org/2000/svg',
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
viewBox: '0 0 80 80',
|
viewBox: "0 0 80 80",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +25,11 @@ const GraphicShell = () => (
|
|||||||
|
|
||||||
{/* Borda reativa ao hover */}
|
{/* Borda reativa ao hover */}
|
||||||
<rect
|
<rect
|
||||||
x="1" y="1" width="78" height="78" rx="7"
|
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"
|
className="stroke-white/5 transition-colors duration-400 ease-[var(--ease-out)] group-hover:stroke-white/15"
|
||||||
strokeWidth="1"
|
strokeWidth="1"
|
||||||
/>
|
/>
|
||||||
@@ -43,7 +47,11 @@ const GraphicShell = () => (
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NumberOne = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
export const NumberOne = ({
|
||||||
|
size = 80,
|
||||||
|
className = "",
|
||||||
|
...props
|
||||||
|
}: NumberIconProps) => (
|
||||||
<svg
|
<svg
|
||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
@@ -55,7 +63,8 @@ export const NumberOne = ({ size = 80, className = '', ...props }: NumberIconPro
|
|||||||
>
|
>
|
||||||
<GraphicShell />
|
<GraphicShell />
|
||||||
<text
|
<text
|
||||||
x="40" y="54"
|
x="40"
|
||||||
|
y="54"
|
||||||
fontFamily="Syne, sans-serif"
|
fontFamily="Syne, sans-serif"
|
||||||
fontSize="40"
|
fontSize="40"
|
||||||
fontWeight="800"
|
fontWeight="800"
|
||||||
@@ -67,7 +76,11 @@ export const NumberOne = ({ size = 80, className = '', ...props }: NumberIconPro
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NumberTwo = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
export const NumberTwo = ({
|
||||||
|
size = 80,
|
||||||
|
className = "",
|
||||||
|
...props
|
||||||
|
}: NumberIconProps) => (
|
||||||
<svg
|
<svg
|
||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
@@ -79,7 +92,8 @@ export const NumberTwo = ({ size = 80, className = '', ...props }: NumberIconPro
|
|||||||
>
|
>
|
||||||
<GraphicShell />
|
<GraphicShell />
|
||||||
<text
|
<text
|
||||||
x="40" y="54"
|
x="40"
|
||||||
|
y="54"
|
||||||
fontFamily="Syne, sans-serif"
|
fontFamily="Syne, sans-serif"
|
||||||
fontSize="40"
|
fontSize="40"
|
||||||
fontWeight="800"
|
fontWeight="800"
|
||||||
@@ -91,7 +105,11 @@ export const NumberTwo = ({ size = 80, className = '', ...props }: NumberIconPro
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NumberThree = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
export const NumberThree = ({
|
||||||
|
size = 80,
|
||||||
|
className = "",
|
||||||
|
...props
|
||||||
|
}: NumberIconProps) => (
|
||||||
<svg
|
<svg
|
||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
@@ -103,7 +121,8 @@ export const NumberThree = ({ size = 80, className = '', ...props }: NumberIconP
|
|||||||
>
|
>
|
||||||
<GraphicShell />
|
<GraphicShell />
|
||||||
<text
|
<text
|
||||||
x="40" y="54"
|
x="40"
|
||||||
|
y="54"
|
||||||
fontFamily="Syne, sans-serif"
|
fontFamily="Syne, sans-serif"
|
||||||
fontSize="40"
|
fontSize="40"
|
||||||
fontWeight="800"
|
fontWeight="800"
|
||||||
@@ -115,7 +134,11 @@ export const NumberThree = ({ size = 80, className = '', ...props }: NumberIconP
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NumberFour = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
export const NumberFour = ({
|
||||||
|
size = 80,
|
||||||
|
className = "",
|
||||||
|
...props
|
||||||
|
}: NumberIconProps) => (
|
||||||
<svg
|
<svg
|
||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
@@ -127,7 +150,8 @@ export const NumberFour = ({ size = 80, className = '', ...props }: NumberIconPr
|
|||||||
>
|
>
|
||||||
<GraphicShell />
|
<GraphicShell />
|
||||||
<text
|
<text
|
||||||
x="40" y="54"
|
x="40"
|
||||||
|
y="54"
|
||||||
fontFamily="Syne, sans-serif"
|
fontFamily="Syne, sans-serif"
|
||||||
fontSize="40"
|
fontSize="40"
|
||||||
fontWeight="800"
|
fontWeight="800"
|
||||||
|
|||||||
+6
-6
@@ -1,16 +1,16 @@
|
|||||||
@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");
|
@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-face {
|
||||||
font-family: 'Monument Extended';
|
font-family: "Monument Extended";
|
||||||
src: url('/fonts/MonumentExtended-Regular.otf') format('opentype');
|
src: url("/fonts/MonumentExtended-Regular.otf") format("opentype");
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Monument Extended';
|
font-family: "Monument Extended";
|
||||||
src: url('/fonts/MonumentExtended-Ultrabold.otf') format('opentype');
|
src: url("/fonts/MonumentExtended-Ultrabold.otf") format("opentype");
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
--r-lg: 16px;
|
--r-lg: 16px;
|
||||||
--r-full: 999px;
|
--r-full: 999px;
|
||||||
--font-display: "Syne", sans-serif;
|
--font-display: "Syne", sans-serif;
|
||||||
--font-hero: 'Monument Extended', sans-serif;
|
--font-hero: "Monument Extended", sans-serif;
|
||||||
--font-mono: "Geist Mono", monospace;
|
--font-mono: "Geist Mono", monospace;
|
||||||
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||||
@@ -124,7 +124,7 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ds-main {
|
.ds-main {
|
||||||
padding-top: 52px;
|
/* padding-top: 52px; removed since there is no header now */
|
||||||
}
|
}
|
||||||
|
|
||||||
.ds-section {
|
.ds-section {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import About from "./components/About";
|
import About from "./components/About";
|
||||||
import Contact from "./components/Contact";
|
import Contact from "./components/Contact";
|
||||||
import Experience from "./components/Experience";
|
import Experience from "./components/Experience";
|
||||||
import { Header } from "./components/Header";
|
|
||||||
import Hero from "./components/Hero";
|
import Hero from "./components/Hero";
|
||||||
import Projects from "./components/Projects";
|
import Projects from "./components/Projects";
|
||||||
import Skills from "./components/Skills";
|
import Skills from "./components/Skills";
|
||||||
@@ -10,7 +9,6 @@ import { WorkModalities } from "./components/WorkModalities";
|
|||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col overflow-x-hidden">
|
<div className="flex min-h-screen flex-col overflow-x-hidden">
|
||||||
<Header />
|
|
||||||
<main className="ds-main">
|
<main className="ds-main">
|
||||||
<Hero />
|
<Hero />
|
||||||
<About />
|
<About />
|
||||||
|
|||||||
Reference in new issue
Block a user