feat: initialize portfolio site with core components, pages, and content structure, and update Next.js configuration.
This commit is contained in:
1 parent
223746b898
commit
d00258dcd1
22 files changed
+1512
-8038
No files matched your search
+107
-117
@@ -30,21 +30,27 @@ const SOCIAL_LINKS = [
|
||||
},
|
||||
];
|
||||
|
||||
const HERO_POINTS = [
|
||||
"Produtos conectados, integracoes complexas e operacoes criticas.",
|
||||
"Arquitetura mais clara para evoluir sem aumentar o caos tecnico.",
|
||||
"Execucao ponta a ponta, do discovery a entrega em producao.",
|
||||
];
|
||||
|
||||
const PROOF_ITEMS = [
|
||||
{
|
||||
label: "Especialidade",
|
||||
label: "Quem eu ajudo",
|
||||
value:
|
||||
"Produtos conectados, interfaces de alta performance e sistemas criticos.",
|
||||
"Empresas que dependem de integracoes, hardware ou fluxos criticos para operar com seguranca e previsibilidade.",
|
||||
},
|
||||
{
|
||||
label: "Escopo",
|
||||
label: "Como entrego",
|
||||
value:
|
||||
"Da integracao com hardware e APIs complexas ao frontend e operacao.",
|
||||
"Traduzo complexidade tecnica em backlog claro, arquitetura legivel e decisao mais rapida para o time.",
|
||||
},
|
||||
{
|
||||
label: "Formato",
|
||||
label: "Por que conversar",
|
||||
value:
|
||||
"Consultoria, execucao ponta a ponta e colaboracao remota para squads.",
|
||||
"Voce ganha um parceiro tecnico capaz de conectar produto, backend, interface e operacao sem perder contexto.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -73,39 +79,44 @@ export default function Hero() {
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-40 bg-gradient-to-t from-[var(--ink)] to-transparent" />
|
||||
|
||||
<div className="relative z-10 mx-auto flex w-full max-w-[1240px] flex-col px-sp-6 py-sp-16 lg:px-sp-16 lg:py-0 pointer-events-none">
|
||||
<div className="relative z-10 mx-auto flex w-full max-w-[1240px] flex-col px-sp-6 py-sp-16 lg:px-sp-16 lg:py-sp-12">
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 24 }}
|
||||
animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="grid items-end gap-sp-10 lg:grid-cols-[minmax(0,1.2fr)_minmax(320px,0.8fr)] pointer-events-auto"
|
||||
className="grid items-start gap-sp-8 lg:grid-cols-[minmax(0,1.1fr)_minmax(340px,0.9fr)] lg:gap-sp-10"
|
||||
>
|
||||
<div className="max-w-[720px]">
|
||||
<div className="mb-sp-6 inline-flex items-center gap-sp-3 border border-[rgba(255,255,255,0.12)] bg-[rgba(10,10,10,0.4)] px-4 py-2 backdrop-blur-xl">
|
||||
<motion.span
|
||||
className="h-2 w-2 bg-[var(--slash)]"
|
||||
animate={
|
||||
shouldReduceMotion
|
||||
? { opacity: 1 }
|
||||
: { opacity: [1, 0.25, 1] }
|
||||
}
|
||||
transition={
|
||||
shouldReduceMotion
|
||||
? { duration: 0 }
|
||||
: {
|
||||
duration: 1.5,
|
||||
ease: "easeInOut",
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<span className="font-mono text-[10px] uppercase tracking-[0.2em] text-[rgba(244,242,237,.82)] drop-shadow-md">
|
||||
Available for selected builds {"//"} Remote worldwide
|
||||
</span>
|
||||
<div className="max-w-[760px]">
|
||||
<div className="mb-sp-5 flex flex-wrap items-center gap-sp-3">
|
||||
<div className="inline-flex items-center gap-sp-3 border border-[rgba(255,255,255,0.12)] bg-[rgba(10,10,10,0.42)] px-4 py-2 backdrop-blur-xl">
|
||||
<motion.span
|
||||
className="h-2 w-2 bg-[var(--slash)]"
|
||||
animate={
|
||||
shouldReduceMotion
|
||||
? { opacity: 1 }
|
||||
: { opacity: [1, 0.25, 1] }
|
||||
}
|
||||
transition={
|
||||
shouldReduceMotion
|
||||
? { duration: 0 }
|
||||
: {
|
||||
duration: 1.5,
|
||||
ease: "easeInOut",
|
||||
repeat: Number.POSITIVE_INFINITY,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<span className="font-mono text-[10px] uppercase tracking-[0.12em] text-[rgba(244,242,237,.82)]">
|
||||
Available for selected builds
|
||||
</span>
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-sp-2 border border-[rgba(255,255,255,0.08)] bg-[rgba(255,255,255,0.02)] px-4 py-2 text-[11px] text-[rgba(244,242,237,.72)] backdrop-blur-xl">
|
||||
Produtos conectados, integracoes criticas e entrega ponta a ponta
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.p
|
||||
className="mb-sp-4 max-w-[220px] font-mono text-[10px] uppercase tracking-[0.22em] text-[var(--slash)] drop-shadow-lg"
|
||||
className="mb-sp-4 max-w-[540px] font-mono text-[10px] uppercase tracking-[0.12em] text-[var(--slash)]"
|
||||
initial={
|
||||
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 12 }
|
||||
}
|
||||
@@ -118,19 +129,18 @@ export default function Hero() {
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
Software engineering for products, operations & critical flows
|
||||
Evandro Fonseca Junior | Engenheiro de software full-stack
|
||||
</motion.p>
|
||||
|
||||
<h1
|
||||
className="max-w-[920px] text-balance font-display text-[clamp(2.9rem,7vw,6.4rem)] font-extrabold leading-[0.92] tracking-[-0.04em] text-transparent bg-clip-text bg-gradient-to-br from-[#ffffff] to-[#a3a3a3]"
|
||||
aria-label="evandro.dev.br"
|
||||
className="max-w-[920px] text-balance font-display text-[clamp(3rem,7vw,6.2rem)] font-extrabold leading-[0.9] tracking-[-0.055em] text-transparent bg-clip-text bg-gradient-to-br from-[#ffffff] to-[#a7a7a7]"
|
||||
aria-label="Engenharia para sistemas que nao podem falhar"
|
||||
>
|
||||
Engenharia de software para produtos conectados, interfaces de
|
||||
alta performance e sistemas criticos.
|
||||
Engenharia de software para sistemas que nao podem falhar.
|
||||
</h1>
|
||||
|
||||
<motion.p
|
||||
className="mt-sp-6 max-w-[640px] text-[15px] leading-7 text-[rgba(244,242,237,.9)] lg:text-[16px] drop-shadow-md"
|
||||
className="mt-sp-6 max-w-[680px] text-[16px] leading-8 text-[rgba(244,242,237,.88)] lg:text-[17px]"
|
||||
initial={
|
||||
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 16 }
|
||||
}
|
||||
@@ -139,15 +149,40 @@ export default function Hero() {
|
||||
}
|
||||
transition={{
|
||||
duration: 0.8,
|
||||
delay: 0.16,
|
||||
delay: 0.14,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
Da integracao com hardware e APIs complexas ao frontend moderno,
|
||||
construo sistemas estaveis, operaveis e prontos para crescer sem
|
||||
sacrificar clareza tecnica.
|
||||
Ajudo empresas com produtos conectados, integracoes complexas e
|
||||
operacoes criticas a transformar arquitetura confusa em software
|
||||
estavel, mais rapido de evoluir e muito mais facil de operar.
|
||||
</motion.p>
|
||||
|
||||
<motion.ul
|
||||
className="mt-sp-8 grid gap-sp-3 sm:grid-cols-3"
|
||||
initial={
|
||||
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 16 }
|
||||
}
|
||||
animate={
|
||||
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
|
||||
}
|
||||
transition={{
|
||||
duration: 0.8,
|
||||
delay: 0.22,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
{HERO_POINTS.map((point) => (
|
||||
<li
|
||||
key={point}
|
||||
className="flex min-w-0 gap-sp-3 rounded-[8px] border border-[rgba(255,255,255,0.08)] bg-[rgba(255,255,255,0.02)] px-sp-4 py-sp-4 text-[13px] leading-6 text-[rgba(244,242,237,.82)] backdrop-blur-sm"
|
||||
>
|
||||
<span className="mt-[8px] h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--slash)]" />
|
||||
<span>{point}</span>
|
||||
</li>
|
||||
))}
|
||||
</motion.ul>
|
||||
|
||||
<motion.div
|
||||
className="mt-sp-8 flex flex-wrap items-center gap-sp-4"
|
||||
initial={
|
||||
@@ -158,32 +193,32 @@ export default function Hero() {
|
||||
}
|
||||
transition={{
|
||||
duration: 0.8,
|
||||
delay: 0.24,
|
||||
delay: 0.3,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
<motion.a
|
||||
href={`https://wa.me/5547992742365?text=${encodeURIComponent(greeting)}`}
|
||||
className="inline-flex min-h-12 items-center justify-center bg-[var(--slash)] px-sp-8 font-mono text-[11px] font-bold uppercase tracking-[0.12em] text-[#0A0A0A] no-underline transition-[transform,background-color,box-shadow] duration-300 hover:bg-white hover:shadow-[0_0_20px_rgba(204,255,0,0.4)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
className="inline-flex min-h-12 items-center justify-center bg-[var(--slash)] px-sp-8 font-mono text-[11px] font-bold uppercase tracking-[0.1em] text-[#0A0A0A] no-underline transition-[transform,background-color,box-shadow] duration-300 hover:bg-white hover:shadow-[0_0_20px_rgba(204,255,0,0.4)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
whileHover={shouldReduceMotion ? undefined : { scale: 1.02 }}
|
||||
whileTap={shouldReduceMotion ? undefined : { scale: 0.98 }}
|
||||
>
|
||||
Iniciar projeto
|
||||
Agendar conversa tecnica
|
||||
</motion.a>
|
||||
|
||||
<motion.a
|
||||
href="#projects"
|
||||
className="inline-flex min-h-12 items-center justify-center border border-[rgba(244,242,237,.28)] bg-[rgba(10,10,10,0.4)] backdrop-blur-md px-sp-8 font-mono text-[11px] font-bold uppercase tracking-[0.12em] text-[#f4f2ed] no-underline transition-[transform,border-color,background-color,color] duration-300 hover:border-[var(--slash)] hover:bg-[rgba(204,255,0,0.08)] hover:text-[var(--slash)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
href="/cases"
|
||||
className="inline-flex min-h-12 items-center justify-center border border-[rgba(244,242,237,.24)] bg-[rgba(10,10,10,0.4)] px-sp-8 font-mono text-[11px] font-bold uppercase tracking-[0.1em] text-[#f4f2ed] no-underline transition-[transform,border-color,background-color,color] duration-300 hover:border-[var(--slash)] hover:bg-[rgba(204,255,0,0.08)] hover:text-[var(--slash)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
whileHover={shouldReduceMotion ? undefined : { scale: 1.02 }}
|
||||
whileTap={shouldReduceMotion ? undefined : { scale: 0.98 }}
|
||||
>
|
||||
Ver projetos
|
||||
Ver casos selecionados
|
||||
</motion.a>
|
||||
|
||||
<a
|
||||
href="/assets/Evandro_Fonseca_Junior_CV.pdf"
|
||||
download
|
||||
className="inline-flex min-h-12 items-center justify-center px-sp-2 text-[12px] font-medium text-[rgba(244,242,237,.9)] no-underline transition-colors duration-300 hover:text-[var(--slash)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)] drop-shadow-md"
|
||||
className="inline-flex min-h-12 items-center justify-center px-sp-2 text-[12px] font-medium text-[rgba(244,242,237,.76)] no-underline transition-colors duration-300 hover:text-[var(--slash)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
@@ -199,15 +234,15 @@ export default function Hero() {
|
||||
}
|
||||
transition={{
|
||||
duration: 0.8,
|
||||
delay: 0.32,
|
||||
delay: 0.38,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
<div className="inline-flex items-center gap-sp-3 text-[11px] uppercase tracking-[0.12em] text-[rgba(244,242,237,.72)]">
|
||||
<div className="inline-flex items-center gap-sp-3 text-[11px] uppercase tracking-[0.08em] text-[rgba(244,242,237,.68)]">
|
||||
<span className="h-2 w-2 rounded-full bg-[var(--slash)] shadow-[0_0_8px_var(--slash)]" />
|
||||
Garuva, SC / Brasil
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-sp-3 text-[11px] uppercase tracking-[0.12em] text-[rgba(244,242,237,.72)]">
|
||||
<div className="inline-flex items-center gap-sp-3 text-[11px] uppercase tracking-[0.08em] text-[rgba(244,242,237,.68)]">
|
||||
<span className="h-[1px] w-6 bg-[rgba(255,255,255,.2)]" />
|
||||
Next.js, Tauri, Rust, Node.js
|
||||
</div>
|
||||
@@ -222,7 +257,7 @@ export default function Hero() {
|
||||
? "noopener noreferrer"
|
||||
: undefined
|
||||
}
|
||||
className="inline-flex h-11 w-11 items-center justify-center rounded-full border border-[rgba(244,242,237,0.18)] bg-[rgba(10,10,10,0.4)] backdrop-blur-md text-[rgba(244,242,237,.88)] transition-[transform,border-color,color,background-color] duration-300 hover:border-[var(--slash)] hover:bg-[rgba(204,255,0,0.1)] hover:text-[var(--slash)] hover:shadow-[0_0_15px_rgba(204,255,0,0.2)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
className="inline-flex h-11 w-11 items-center justify-center rounded-full border border-[rgba(244,242,237,0.16)] bg-[rgba(10,10,10,0.34)] text-[rgba(244,242,237,.86)] transition-[transform,border-color,color,background-color] duration-300 hover:border-[var(--slash)] hover:bg-[rgba(204,255,0,0.1)] hover:text-[var(--slash)] hover:shadow-[0_0_15px_rgba(204,255,0,0.18)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
|
||||
aria-label={link.label}
|
||||
>
|
||||
<svg
|
||||
@@ -232,8 +267,7 @@ export default function Hero() {
|
||||
fill={link.stroke ? "none" : "currentColor"}
|
||||
stroke={link.stroke ? "currentColor" : "none"}
|
||||
strokeWidth={link.stroke ? "2" : undefined}
|
||||
role="img"
|
||||
aria-label={link.label}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{link.icon}
|
||||
</svg>
|
||||
@@ -244,28 +278,26 @@ export default function Hero() {
|
||||
</div>
|
||||
|
||||
<motion.aside
|
||||
className="relative overflow-hidden border border-[rgba(255,255,255,0.15)] bg-[linear-gradient(180deg,rgba(10,10,10,0.6),rgba(10,10,10,0.3))] p-sp-6 backdrop-blur-xl lg:p-sp-8"
|
||||
className="relative overflow-hidden border border-[rgba(255,255,255,0.12)] bg-[linear-gradient(180deg,rgba(10,10,10,0.58),rgba(10,10,10,0.36))] p-sp-6 backdrop-blur-xl lg:p-sp-8"
|
||||
initial={
|
||||
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, x: 24 }
|
||||
}
|
||||
animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.9, delay: 0.22, ease: [0.16, 1, 0.3, 1] }}
|
||||
>
|
||||
<div className="absolute inset-x-0 top-0 h-px bg-[linear-gradient(90deg,transparent,rgba(204,255,0,0.9),transparent)]" />
|
||||
<div className="absolute inset-y-0 left-0 w-px bg-[linear-gradient(180deg,rgba(204,255,0,0.4),transparent)]" />
|
||||
<div className="absolute inset-x-0 top-0 h-px bg-[linear-gradient(90deg,transparent,rgba(204,255,0,0.8),transparent)]" />
|
||||
|
||||
<div className="flex items-start justify-between gap-sp-4 border-b border-[rgba(255,255,255,0.08)] pb-sp-5">
|
||||
<div>
|
||||
<div className="font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--slash)]">
|
||||
Execution Brief
|
||||
</div>
|
||||
<h2 className="mt-sp-2 font-display text-[clamp(1.35rem,3vw,2rem)] font-extrabold leading-[1.02] tracking-[-0.03em] text-[#f7f4ee]">
|
||||
Clareza de arquitetura antes de complexidade.
|
||||
</h2>
|
||||
</div>
|
||||
<div className="shrink-0 border border-[rgba(204,255,0,0.3)] bg-[rgba(204,255,0,0.05)] px-3 py-2 font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--slash)]">
|
||||
2026
|
||||
<div className="border-b border-[rgba(255,255,255,0.08)] pb-sp-5">
|
||||
<div className="font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--slash)]">
|
||||
Why this positioning works
|
||||
</div>
|
||||
<h2 className="mt-sp-3 font-display text-[clamp(1.35rem,3vw,2rem)] font-extrabold leading-[1.04] tracking-[-0.03em] text-[#f7f4ee]">
|
||||
Menos atrito entre produto, engenharia e operacao.
|
||||
</h2>
|
||||
<p className="mt-sp-4 text-[14px] leading-7 text-[rgba(244,242,237,.78)]">
|
||||
Entro onde a complexidade cresce e ajudo a devolver clareza,
|
||||
ritmo de entrega e confiabilidade para o sistema e para o time.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-sp-6 space-y-sp-5">
|
||||
@@ -281,68 +313,26 @@ export default function Hero() {
|
||||
}
|
||||
transition={{
|
||||
duration: 0.65,
|
||||
delay: 0.28 + index * 0.08,
|
||||
delay: 0.3 + index * 0.08,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
<div className="font-mono text-[10px] uppercase tracking-[0.18em] text-[rgba(244,242,237,.7)]">
|
||||
<div className="font-mono text-[10px] uppercase tracking-[0.12em] text-[rgba(244,242,237,.56)]">
|
||||
{item.label}
|
||||
</div>
|
||||
<p className="mt-sp-2 text-[14px] leading-6 text-[rgba(244,242,237,.94)]">
|
||||
<p className="mt-sp-2 text-[14px] leading-7 text-[rgba(244,242,237,.92)]">
|
||||
{item.value}
|
||||
</p>
|
||||
</motion.article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="mt-sp-8 grid gap-sp-4 sm:grid-cols-2"
|
||||
initial={
|
||||
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 16 }
|
||||
}
|
||||
animate={
|
||||
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
|
||||
}
|
||||
transition={{
|
||||
duration: 0.75,
|
||||
delay: 0.48,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
>
|
||||
<div className="border border-[rgba(255,255,255,0.1)] bg-[rgba(10,10,10,0.4)] p-sp-4 hover:border-[var(--slash)] transition-colors duration-300">
|
||||
<div className="font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--slash)]">
|
||||
Focus
|
||||
</div>
|
||||
<p className="mt-sp-2 text-[13px] leading-6 text-[rgba(244,242,237,.9)]">
|
||||
Sistemas com baixa latencia, alta previsibilidade e manutencao
|
||||
sustentavel.
|
||||
</p>
|
||||
</div>
|
||||
<div className="border border-[rgba(255,255,255,0.1)] bg-[rgba(10,10,10,0.4)] p-sp-4 hover:border-[var(--slash)] transition-colors duration-300">
|
||||
<div className="font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--slash)]">
|
||||
Availability
|
||||
</div>
|
||||
<p className="mt-sp-2 text-[13px] leading-6 text-[rgba(244,242,237,.9)]">
|
||||
Aberto a novos projetos com ownership tecnico, discovery e
|
||||
entrega iterativa.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.aside>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="absolute bottom-10 left-1/2 flex -translate-x-1/2 flex-col items-center gap-3 opacity-50 z-10 pointer-events-none"
|
||||
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0 }}
|
||||
animate={shouldReduceMotion ? { opacity: 0.5 } : { opacity: 0.5 }}
|
||||
transition={{ delay: 1, duration: 1 }}
|
||||
>
|
||||
<span className="font-mono text-[9px] uppercase tracking-widest text-[var(--slash)]">
|
||||
Scroll
|
||||
</span>
|
||||
<div className="h-10 w-[1px] bg-gradient-to-b from-[var(--slash)] to-transparent" />
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user