feat(home): prova no topo, contato sem atrito e conversão medida

Home mais curta (hero, vitrine de clientes, sobre, serviços, IA resumida, depoimentos e contato), faixa de clientes e telas reais no hero, sem números do GitHub. Barra de cookies fina, WhatsApp fixo no celular, formulário com e-mail opcional e alternativa por e-mail, página /ia, eventos de e-mail, redes e currículo e conversão do Ads opcional por variável. Rodapé em 3 colunas. Remove componentes órfãos (LighthouseMetrics, Skills, Experience, contagem do GitHub).
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 03:32:23 -03:00
1 parent 6e1022b58c
commit 8d07b8c6bb
60 files changed
+4392 -2124

No files matched your search

+108 -144
View File
@@ -7,11 +7,13 @@ import {
CONSENT_STORAGE_KEY,
GA_DISABLE_KEY,
GA_MEASUREMENT_ID,
GOOGLE_ADS_ID,
type Gtag,
OPEN_COOKIE_PREFERENCES_EVENT,
readConsent,
} from "@/lib/analytics";
import "./conversion.css";
const GOOGLE_ADS_ID = "AW-18432694647";
const GA_SCRIPT_SRC = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`;
const SITE_DOMAIN = "evandro.dev.br";
@@ -76,17 +78,17 @@ function expireAnalyticsCookies() {
}
}
const panelClassName =
"fixed bottom-sp-4 left-sp-4 right-sp-4 z-[9980] ml-auto max-w-[560px] border border-[var(--slash)] bg-[var(--ink)] p-sp-5 text-white shadow-[8px_8px_0_var(--slash)] sm:left-auto";
const buttonClassName =
"inline-flex min-h-11 min-w-11 items-center justify-center border px-sp-4 py-sp-2 font-mono text-[11px] font-bold uppercase tracking-[0.08em] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]";
const barButtonClassName =
"cv-btn inline-flex min-h-10 min-w-11 items-center justify-center border px-[10px] py-sp-1 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)] motion-reduce:transition-none";
function GoogleAnalyticsConsent() {
const [consent, setConsent] = useState<AnalyticsConsent | null>();
const [preferencesOpen, setPreferencesOpen] = useState(false);
const [consentError, setConsentError] = useState<string | null>(null);
const configuredRef = useRef(false);
const preferencesButtonRef = useRef<HTMLButtonElement>(null);
const barRef = useRef<HTMLElement>(null);
const acceptRef = useRef<HTMLButtonElement>(null);
const openerRef = useRef<HTMLElement | null>(null);
const enableAnalytics = useCallback(() => {
const gtag = getGtag();
@@ -123,12 +125,56 @@ function GoogleAnalyticsConsent() {
}
}, [consent]);
// O link "Preferências de cookies" do rodapé reabre a barra; quem pediu recebe o foco de volta ao fechar.
useEffect(() => {
const reopen = () => {
openerRef.current =
document.activeElement instanceof HTMLElement
? document.activeElement
: null;
setPreferencesOpen(true);
};
window.addEventListener(OPEN_COOKIE_PREFERENCES_EVENT, reopen);
return () =>
window.removeEventListener(OPEN_COOKIE_PREFERENCES_EVENT, reopen);
}, []);
const barOpen = consent === null || preferencesOpen;
// Enquanto a barra está na tela, publica a altura dela para o botão flutuante do WhatsApp subir.
useEffect(() => {
const bar = barRef.current;
if (!barOpen || !bar) return;
const root = document.documentElement;
const publish = () =>
root.style.setProperty("--cookie-bar-height", `${bar.offsetHeight}px`);
publish();
root.setAttribute("data-cookie-bar", "open");
const observer = new ResizeObserver(publish);
observer.observe(bar);
return () => {
observer.disconnect();
root.style.removeProperty("--cookie-bar-height");
root.removeAttribute("data-cookie-bar");
};
}, [barOpen]);
// Reaberta pelo rodapé, a barra está no começo do documento: leva o foco para ela.
useEffect(() => {
if (preferencesOpen) acceptRef.current?.focus();
}, [preferencesOpen]);
function closePreferences() {
setPreferencesOpen(false);
preferencesButtonRef.current?.focus();
openerRef.current?.focus();
openerRef.current = null;
}
function acceptAnalytics() {
if (consent === "granted") {
closePreferences();
return;
}
if (!persistConsent("granted")) {
setConsentError(
"Não foi possível salvar sua aceitação. Tente novamente para concluir.",
@@ -142,92 +188,27 @@ function GoogleAnalyticsConsent() {
}
function denyAnalytics() {
const revoking = consent === "granted";
if (!persistConsent("denied")) {
setConsentError(
"Não foi possível salvar sua recusa. Tente novamente para concluir.",
revoking
? "Não foi possível salvar a revogação. Tente novamente para concluir."
: "Não foi possível salvar sua recusa. Tente novamente para concluir.",
);
return;
}
setConsentError(null);
setConsent("denied");
closePreferences();
}
function revokeAnalytics() {
if (!persistConsent("denied")) {
setConsentError(
"Não foi possível salvar a revogação. Tente novamente para concluir.",
);
return;
if (revoking) {
window[GA_DISABLE_KEY] = true;
window.gtag?.("consent", "update", deniedConsent);
expireAnalyticsCookies();
}
setConsentError(null);
window[GA_DISABLE_KEY] = true;
window.gtag?.("consent", "update", deniedConsent);
expireAnalyticsCookies();
setConsent("denied");
closePreferences();
}
if (consent === undefined) return null;
if (consent === null) {
return (
<section
role="dialog"
aria-modal="false"
aria-labelledby="analytics-consent-title"
aria-describedby="analytics-consent-description"
className={panelClassName}
>
<p
id="analytics-consent-title"
className="font-mono text-[10px] font-bold uppercase tracking-[0.16em] text-[var(--slash)]"
>
{"Privacidade // Analytics"}
</p>
<p
id="analytics-consent-description"
className="mt-sp-2 max-w-[48ch] text-[13px] leading-6 text-white/85"
>
Você decide se permite métricas de navegação com a tag compartilhada
do Google Analytics e Google Ads. Cookies de publicidade e
personalização continuam desativados.
</p>
{consentError && (
<p
role="alert"
aria-label="Falha ao salvar o consentimento"
className="mt-sp-3 text-[13px] leading-6 text-[var(--slash)]"
>
{consentError}
</p>
)}
<div className="mt-sp-4 flex flex-wrap gap-sp-2">
<button
type="button"
className={`${buttonClassName} border-[var(--slash)] bg-[var(--slash)] text-[var(--ink)]`}
onClick={acceptAnalytics}
>
Aceitar analytics
</button>
<button
type="button"
className={`${buttonClassName} border-white/40 bg-transparent text-white`}
onClick={denyAnalytics}
>
Recusar analytics
</button>
</div>
<a
className="ds-link ds-link-accent mt-sp-3 inline-block"
href="/privacidade"
>
Ler política de privacidade
</a>
</section>
);
}
return (
<>
{consent === "granted" && (
@@ -238,82 +219,65 @@ function GoogleAnalyticsConsent() {
strategy="afterInteractive"
/>
)}
<button
type="button"
ref={preferencesButtonRef}
aria-expanded={preferencesOpen}
aria-controls="analytics-preferences"
className={`${buttonClassName} fixed bottom-sp-4 right-sp-4 z-[9980] border-white/25 bg-[var(--ink)] text-white shadow-[4px_4px_0_var(--slash)]`}
onClick={() => setPreferencesOpen((open) => !open)}
>
<span className="max-sm:hidden">Preferências de analytics</span>
<span aria-hidden="true" className="sm:hidden">
Cookies
</span>
</button>
{preferencesOpen && (
{barOpen && (
<section
id="analytics-preferences"
ref={barRef}
role="dialog"
aria-modal="false"
aria-labelledby="analytics-preferences-title"
aria-describedby="analytics-preferences-description"
className={panelClassName}
aria-label="Cookies e privacidade"
aria-describedby="analytics-consent-description"
onKeyDown={(event) => {
if (event.key === "Escape" && preferencesOpen) closePreferences();
}}
className="fixed inset-x-0 bottom-0 z-[9980] border-t border-white/20 bg-[var(--ink)] px-sp-4 pt-sp-2 pb-[max(var(--sp-2),env(safe-area-inset-bottom))] text-white shadow-[0_-8px_24px_rgba(0,0,0,0.35)] sm:px-sp-6"
>
<p
id="analytics-preferences-title"
className="font-mono text-[10px] font-bold uppercase tracking-[0.16em] text-[var(--slash)]"
>
{"Preferências // Analytics"}
</p>
<p
id="analytics-preferences-description"
className="mt-sp-2 text-[13px] leading-6 text-white/85"
>
Analytics está {consent === "granted" ? "ativo" : "desativado"}.
Cookies de publicidade e personalização permanecem negados.
</p>
<div className="mx-auto flex max-w-[1100px] items-center gap-sp-3 sm:gap-sp-6">
<p
id="analytics-consent-description"
className="min-w-0 flex-1 font-display text-[13px] leading-[1.3] text-white/85 sm:text-sm"
>
Usamos métricas de navegação do Google só se você permitir.{" "}
<a
className="whitespace-nowrap underline underline-offset-2 hover:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
href="/privacidade"
>
Política de privacidade
</a>
{preferencesOpen && consent && (
<span className="sr-only">
{" "}
Escolha atual:{" "}
{consent === "granted" ? "permitido" : "recusado"}.
</span>
)}
</p>
<div className="flex shrink-0 gap-sp-2">
<button
type="button"
ref={acceptRef}
className={`${barButtonClassName} border-[#f4f2ed] bg-[#f4f2ed] text-[var(--ink)] hover:bg-white`}
onClick={acceptAnalytics}
>
Aceitar
</button>
<button
type="button"
className={`${barButtonClassName} border-white/45 bg-transparent text-white hover:border-white`}
onClick={denyAnalytics}
>
Recusar
</button>
</div>
</div>
{consentError && (
<p
role="alert"
aria-label="Falha ao salvar o consentimento"
className="mt-sp-3 text-[13px] leading-6 text-[var(--slash)]"
className="mx-auto mt-sp-1 max-w-[1100px] font-display text-[13px] leading-5 text-[#ff9d94]"
>
{consentError}
</p>
)}
<div className="mt-sp-4 flex flex-wrap gap-sp-2">
{consent === "granted" ? (
<button
type="button"
className={`${buttonClassName} border-[var(--slash)] bg-[var(--slash)] text-[var(--ink)]`}
onClick={revokeAnalytics}
>
Revogar analytics
</button>
) : (
<button
type="button"
className={`${buttonClassName} border-[var(--slash)] bg-[var(--slash)] text-[var(--ink)]`}
onClick={acceptAnalytics}
>
Aceitar analytics
</button>
)}
<button
type="button"
className={`${buttonClassName} border-white/40 bg-transparent text-white`}
onClick={closePreferences}
>
Fechar preferências
</button>
</div>
<a
className="ds-link ds-link-accent mt-sp-3 inline-block"
href="/privacidade"
>
Ler política de privacidade
</a>
</section>
)}
</>