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
+332
-206
No files matched your search
@@ -39,8 +39,7 @@ export default function Contact() {
|
||||
<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.
|
||||
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>
|
||||
@@ -63,7 +62,10 @@ export default function Contact() {
|
||||
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"
|
||||
style={{ color: "#fff", backgroundColor: "rgba(255,255,255,0.06)" }}
|
||||
style={{
|
||||
color: "#fff",
|
||||
backgroundColor: "rgba(255,255,255,0.06)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -79,7 +81,10 @@ export default function Contact() {
|
||||
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"
|
||||
style={{ color: "#fff", backgroundColor: "rgba(255,255,255,0.06)" }}
|
||||
style={{
|
||||
color: "#fff",
|
||||
backgroundColor: "rgba(255,255,255,0.06)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -114,7 +119,10 @@ export default function Contact() {
|
||||
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"
|
||||
style={{ color: "#fff", backgroundColor: "rgba(255,255,255,0.06)" }}
|
||||
style={{
|
||||
color: "#fff",
|
||||
backgroundColor: "rgba(255,255,255,0.06)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="ds-link ds-link-accent border-0">
|
||||
|
||||
@@ -17,9 +17,7 @@ export function Header() {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<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"
|
||||
>
|
||||
<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">
|
||||
<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" className="shrink-0" />
|
||||
@@ -61,9 +59,15 @@ export function Header() {
|
||||
aria-label="Menu"
|
||||
aria-expanded={mobileMenuOpen}
|
||||
>
|
||||
<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" : ""}`} />
|
||||
<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>
|
||||
|
||||
+172
-111
@@ -1,5 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Logo } from "./Logo";
|
||||
|
||||
const SOCIAL_LINKS = [
|
||||
@@ -30,13 +32,103 @@ const SOCIAL_LINKS = [
|
||||
},
|
||||
];
|
||||
|
||||
const STATS = [
|
||||
{ value: "4", accent: "px", label: "Base unit" },
|
||||
{ value: "9", accent: "x", label: "Type scale" },
|
||||
{ value: "4", accent: "+", label: "Core tokens" },
|
||||
];
|
||||
const textToType = "evandro.dev.br";
|
||||
|
||||
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 greeting =
|
||||
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-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>
|
||||
<div className="relative z-10 mx-auto grid min-h-[calc(100vh)] w-full max-w-[1000px] grid-cols-1 items-center gap-8">
|
||||
<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">
|
||||
<span
|
||||
className="h-1.5 w-1.5 animate-pulse rounded-full"
|
||||
@@ -68,16 +165,61 @@ export default function Hero() {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
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
|
||||
<br />
|
||||
<span style={{ color: "#E8FF00" }}>.dev</span>
|
||||
<br />
|
||||
.br
|
||||
</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">
|
||||
<AnimatePresence mode="wait">
|
||||
{(phase === "typing" || phase === "idle") && (
|
||||
<motion.h1
|
||||
key="text"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, filter: "blur(0px)", scale: 1 }}
|
||||
exit={{ opacity: 0, filter: "blur(12px)", scale: 1.05 }}
|
||||
transition={{ duration: 0.5, ease: "easeInOut" }}
|
||||
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
|
||||
className="mb-sp-10 max-w-[460px] text-[13px] leading-7"
|
||||
@@ -88,15 +230,24 @@ export default function Hero() {
|
||||
React, Tauri e arquitetura escalavel.
|
||||
</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
|
||||
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" }}
|
||||
>
|
||||
Iniciar projeto
|
||||
</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">
|
||||
{SOCIAL_LINKS.map((link) => (
|
||||
<a
|
||||
@@ -108,9 +259,8 @@ export default function Hero() {
|
||||
? "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"
|
||||
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={{
|
||||
borderColor: "rgba(255,255,255,.18)",
|
||||
color: "rgba(255,255,255,.84)",
|
||||
}}
|
||||
aria-label={link.label}
|
||||
@@ -139,96 +289,7 @@ export default function Hero() {
|
||||
Garuva, SC | Remote worldwide
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -92,7 +92,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
onBlur={() => setHovered(false)}
|
||||
tabIndex={0}
|
||||
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",
|
||||
padding: "var(--sp-6)",
|
||||
display: "flex",
|
||||
@@ -108,7 +110,13 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
}}
|
||||
>
|
||||
{/* Top row */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "9px",
|
||||
@@ -123,7 +131,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
style={{
|
||||
fontFamily: "var(--font-display)",
|
||||
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,
|
||||
letterSpacing: "0.04em",
|
||||
}}
|
||||
@@ -150,7 +160,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
style={{
|
||||
marginTop: "var(--sp-2)",
|
||||
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,
|
||||
}}
|
||||
>
|
||||
@@ -159,7 +171,15 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
</div>
|
||||
|
||||
{/* 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}
|
||||
</p>
|
||||
|
||||
@@ -183,7 +203,9 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
{/* Pricing */}
|
||||
<div
|
||||
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)",
|
||||
}}
|
||||
>
|
||||
@@ -198,7 +220,16 @@ function TierCard({ tier }: { tier: Tier }) {
|
||||
>
|
||||
{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" }}>
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/components/ui/NumberIcons.tsx
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
interface NumberIconProps extends React.SVGProps<SVGSVGElement> {
|
||||
/** Tamanho em pixels. Escala mantendo o viewBox 80x80 */
|
||||
@@ -9,9 +9,9 @@ interface NumberIconProps extends React.SVGProps<SVGSVGElement> {
|
||||
}
|
||||
|
||||
const baseProps = {
|
||||
fill: 'none',
|
||||
xmlns: 'http://www.w3.org/2000/svg',
|
||||
viewBox: '0 0 80 80',
|
||||
fill: "none",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
viewBox: "0 0 80 80",
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -22,44 +22,53 @@ 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"
|
||||
<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"
|
||||
<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}
|
||||
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"
|
||||
<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
|
||||
@@ -67,23 +76,28 @@ export const NumberOne = ({ size = 80, className = '', ...props }: NumberIconPro
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NumberTwo = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 2"
|
||||
{...baseProps}
|
||||
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"
|
||||
<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
|
||||
@@ -91,23 +105,28 @@ export const NumberTwo = ({ size = 80, className = '', ...props }: NumberIconPro
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NumberThree = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 3"
|
||||
{...baseProps}
|
||||
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"
|
||||
<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
|
||||
@@ -115,23 +134,28 @@ export const NumberThree = ({ size = 80, className = '', ...props }: NumberIconP
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NumberFour = ({ size = 80, className = '', ...props }: NumberIconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
className={`group ${className}`}
|
||||
role="img"
|
||||
aria-label="Passo 4"
|
||||
{...baseProps}
|
||||
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"
|
||||
<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
|
||||
|
||||
+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");
|
||||
|
||||
@font-face {
|
||||
font-family: 'Monument Extended';
|
||||
src: url('/fonts/MonumentExtended-Regular.otf') format('opentype');
|
||||
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-family: "Monument Extended";
|
||||
src: url("/fonts/MonumentExtended-Ultrabold.otf") format("opentype");
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
@@ -66,7 +66,7 @@
|
||||
--r-lg: 16px;
|
||||
--r-full: 999px;
|
||||
--font-display: "Syne", sans-serif;
|
||||
--font-hero: 'Monument Extended', 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);
|
||||
@@ -124,7 +124,7 @@ textarea {
|
||||
}
|
||||
|
||||
.ds-main {
|
||||
padding-top: 52px;
|
||||
/* padding-top: 52px; removed since there is no header now */
|
||||
}
|
||||
|
||||
.ds-section {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import About from "./components/About";
|
||||
import Contact from "./components/Contact";
|
||||
import Experience from "./components/Experience";
|
||||
import { Header } from "./components/Header";
|
||||
import Hero from "./components/Hero";
|
||||
import Projects from "./components/Projects";
|
||||
import Skills from "./components/Skills";
|
||||
@@ -10,7 +9,6 @@ import { WorkModalities } from "./components/WorkModalities";
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col overflow-x-hidden">
|
||||
<Header />
|
||||
<main className="ds-main">
|
||||
<Hero />
|
||||
<About />
|
||||
|
||||
Reference in new issue
Block a user