feat(cases): prova certa por serviço, páginas internas com trabalho no topo

Diagnóstico sem case que não cabe; desktop com o GCC Installer como projeto próprio; IoT com o Twins Lab e a nota da Apolo. Mosaico de pôsteres em /servicos e /cases, faixa baixa em uma coluna no currículo, privacidade e laboratório; Laboratório como lista compacta; SEV começando por catálogo, pedido no WhatsApp e painel; Joinville e SC nos títulos; imagens do LCP com fetchPriority e sizes reais; pré-carregamento só no hover; 404 com Open Graph próprio.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 17:01:35 -03:00
1 parent 7b633f0f4a
commit de6af74a0c
37 files changed
+1062 -254

No files matched your search

+71 -45
View File
@@ -1,6 +1,14 @@
import type { Metadata } from "next";
import Link from "next/link";
import { coverSizes } from "@/app/cases/_components/CaseCover";
import { CaseCoverLink } from "@/app/cases/_components/CaseCoverLink";
import {
compactHero,
heroWithMediaGrid,
} from "@/app/cases/_components/hero-styles";
import {
getMosaicPosters,
PosterMosaic,
} from "@/app/cases/_components/PosterMosaic";
import { Breadcrumb } from "@/app/components/Breadcrumb";
import {
pageTop,
@@ -8,15 +16,17 @@ import {
secondaryButtonDark,
textLink,
} from "@/app/components/button-styles";
import HoverPrefetchLink from "@/app/components/HoverPrefetchLink";
import WhatsAppLink from "@/app/components/WhatsAppLink";
import { safeJsonLd } from "@/lib/jsonld";
import { buildBreadcrumbList, buildPageSocialMetadata } from "@/lib/seo";
import { clientCases, serviceHubLinks, siteUrl } from "@/lib/site-content";
import { SERVICES_SUBJECT } from "@/lib/whatsapp";
const pageUrl = `${siteUrl}/servicos`;
const pageTitle = "Sites, sistemas e aplicativos sob medida";
const pageDescription =
"Sites, sistemas web e aplicativos sob medida para apresentar seu negócio e organizar a operação. Atendimento de Garuva-SC para todo o Brasil.";
"Sites, sistemas web e aplicativos sob medida para apresentar seu negócio e organizar a operação. Garuva e Joinville (SC), remoto em todo o Brasil.";
const businessIntents = [
{
@@ -60,6 +70,9 @@ export const metadata: Metadata = {
};
export default function ServicesIndexPage() {
// três pôsteres dos clientes na coluna ao lado do título, que ficaria vazia
const posters = getMosaicPosters(clientCases.map(({ id }) => id));
const hasMosaic = posters.length > 0;
const jsonLd = {
"@context": "https://schema.org",
"@graph": [
@@ -90,41 +103,53 @@ export default function ServicesIndexPage() {
<main>
<script type="application/ld+json">{safeJsonLd(jsonLd)}</script>
<section className={`ds-section ds-section-dark ${pageTop}`}>
<div className="ds-section-shell">
<Breadcrumb
items={[
{ label: "Início", href: "/" },
{ label: "Serviços", href: "/servicos" },
]}
/>
<h1 className="ds-section-title max-w-[22ch] text-balance">
Sites, sistemas e aplicativos sob medida.
</h1>
<p className="ds-copy max-w-[62ch]">
Apresente seu negócio, organize a operação ou conecte ferramentas
que hoje dão trabalho. Desenvolvo a solução a partir do que você
precisa resolver, não de uma lista de tecnologias.
</p>
<div className="mt-sp-6 flex flex-wrap items-center gap-sp-4">
<WhatsAppLink
className={primaryButton}
service="Sites, sistemas e aplicativos"
placement="service_hub_hero"
>
Falar com Evandro no WhatsApp
</WhatsAppLink>
<a
href="#projetos"
className={`${secondaryButtonDark} focus-visible:outline-2 focus-visible:outline-offset-4`}
>
Ver projetos realizados
</a>
<section
className={`ds-section ds-section-dark ${pageTop} ${
hasMosaic ? "" : compactHero
}`}
>
<div
className={`ds-section-shell ${hasMosaic ? heroWithMediaGrid : ""}`}
>
<div>
<Breadcrumb
items={[
{ label: "Início", href: "/" },
{ label: "Serviços", href: "/servicos" },
]}
/>
<h1 className="ds-section-title max-w-[22ch] text-balance">
Sites, sistemas e aplicativos sob medida.
</h1>
<p className="ds-copy max-w-[62ch]">
Apresente seu negócio, organize a operação ou conecte ferramentas
que hoje dão trabalho. Desenvolvo a solução a partir do que você
precisa resolver, não de uma lista de tecnologias.
</p>
<div className="mt-sp-6 flex flex-wrap items-center gap-sp-4">
<WhatsAppLink
className={primaryButton}
service={SERVICES_SUBJECT}
placement="service_hub_hero"
>
Falar com Evandro no WhatsApp
</WhatsAppLink>
<a
href="#projetos"
className={`${secondaryButtonDark} focus-visible:outline-2 focus-visible:outline-offset-4`}
>
Ver projetos realizados
</a>
</div>
<p className="ds-copy max-w-[62ch]">
Sou Evandro, desenvolvedor de software, e a conversa é direta
comigo.
</p>
<p className="mt-sp-2 text-[15px] leading-6 text-white/70">
Garuva e Joinville (SC) · atendimento remoto em todo o Brasil
</p>
</div>
<p className="ds-copy max-w-[62ch]">
Sou Evandro, desenvolvedor em Garuva, Santa Catarina. Atendimento
remoto para todo o Brasil, com conversa direta comigo.
</p>
<PosterMosaic posters={posters} />
</div>
</section>
@@ -155,12 +180,12 @@ export default function ServicesIndexPage() {
>
{intent.ctaLabel}
</WhatsAppLink>
<Link
<HoverPrefetchLink
href={intent.href}
className={`${textLink} focus-visible:outline-2 focus-visible:outline-offset-4`}
>
{intent.linkLabel}
</Link>
</HoverPrefetchLink>
</div>
</article>
))}
@@ -191,16 +216,17 @@ export default function ServicesIndexPage() {
id={caseStudy.id}
title={caseStudy.title}
demoUrl={caseStudy.demoUrl}
sizes={coverSizes.column}
/>
<p className="ds-kicker">{caseStudy.title}</p>
<h3 className="ds-card-title">{caseStudy.headline}</h3>
<p className="ds-copy">{caseStudy.summary}</p>
<Link
<HoverPrefetchLink
href={`/cases/${caseStudy.id}`}
className={`${textLink} mt-sp-4 focus-visible:outline-2 focus-visible:outline-offset-4`}
>
Ver o case {caseStudy.title}
</Link>
</HoverPrefetchLink>
</article>
))}
</div>
@@ -230,7 +256,7 @@ export default function ServicesIndexPage() {
</div>
<div>
<h2 className="ds-card-title">Antes de contratar</h2>
<dl className="mt-sp-5 space-y-sp-6 text-[15px] md:text-sm">
<dl className="mt-sp-5 space-y-sp-6 text-[15px]">
<div>
<dt className="font-semibold">
Quanto custa e quanto tempo leva?
@@ -243,7 +269,7 @@ export default function ServicesIndexPage() {
</div>
<div>
<dt className="font-semibold">
Preciso estar perto de Garuva?
Preciso estar em Joinville ou em Garuva?
</dt>
<dd className="ds-copy !mt-sp-2">
Não. O atendimento é remoto, com conversas, demonstrações e
@@ -280,12 +306,12 @@ export default function ServicesIndexPage() {
key={service.href}
className="grid items-start gap-sp-3 py-sp-5 md:grid-cols-[minmax(0,1fr)_minmax(0,2fr)] md:gap-sp-8"
>
<Link
<HoverPrefetchLink
href={service.href}
className="inline-flex min-h-11 items-center font-semibold underline underline-offset-4 focus-visible:outline-2 focus-visible:outline-offset-4"
>
{service.label}
</Link>
</HoverPrefetchLink>
<p className="ds-copy !mt-0">{service.description}</p>
</li>
))}
@@ -305,7 +331,7 @@ export default function ServicesIndexPage() {
</p>
<WhatsAppLink
className={`${primaryButton} mt-sp-6`}
service="Sites, sistemas e aplicativos"
service={SERVICES_SUBJECT}
placement="service_hub_footer"
>
Conversar com Evandro no WhatsApp