feat(home): ofertas que vendem o título, WhatsApp no hero e menu do celular acessível

Três ofertas (site que traz pedidos pelo WhatsApp, sistema para tirar a operação da planilha, revisão de sistema existente) com miniatura e formulário com o tipo escolhido; botão principal do hero abre o WhatsApp; moldura da SEV no hero do celular; menu fecha ao navegar, Esc, foco preso incluindo a barra de cookies e espaço para ela; CinematicHome como componente de servidor com ilha cliente; /ia com H1 de oferta e cartão do que você recebe; botão flutuante em aside com transform; AVIF antes de WebP; copy sem repetir contato direto.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 17:01:36 -03:00
1 parent de6af74a0c
commit f8545cd442
44 files changed
+2286 -571

No files matched your search

+69 -38
View File
@@ -1,3 +1,4 @@
import Image from "next/image";
import { getShowcaseAssets } from "@/lib/project-showcase";
import { BrowserFrame } from "./showcase/BrowserFrame";
import { PhoneFrame } from "./showcase/PhoneFrame";
@@ -7,52 +8,82 @@ export type HeroWorkUrl = { text: string; secure: boolean } | null;
const DESKTOP_PROJECT = "sev-modaintima";
const MOBILE_PROJECT = "as-servicos";
/** No celular a moldura mostra o site da SEV, o mesmo projeto do navegador ao lado do título no computador. */
const SMALL_SCREEN_PROJECT = "sev-modaintima";
/** Altura da moldura de celular abaixo dos botões (px); a largura sai da proporção 390x844 do aparelho. */
const SMALL_PHONE_HEIGHT = 180;
/**
* Trabalho real ao lado do título, só em telas de 1024 px ou mais: o pôster do site da SEV Moda Íntima no navegador
* (com o domínio próprio, sevmodaintima.com.br, na barra de endereço) e o do aplicativo da AS Serviços no celular.
* A coluna ocupa cerca de 40% da largura. Ela fica escondida (display: none) abaixo de 1024 px,
* então as imagens (lazy) nem são baixadas no celular.
* Trabalho real do hero, em dois formatos. Em telas de 1024 px ou mais, ao lado do título: o pôster do site da SEV
* Moda Íntima no navegador (com o domínio próprio, sevmodaintima.com.br, na barra de endereço) e o do aplicativo
* da AS Serviços no celular, numa coluna de cerca de 40% da largura. Abaixo de 1024 px, uma moldura de celular
* pequena (180 px de altura) com o pôster mobile da SEV, abaixo dos botões.
* Os pôsteres vão pelo next/image com `sizes` do tamanho em que aparecem (um arquivo de 1440 px para 491 px de tela
* era banda desperdiçada), em `loading="lazy"` e prioridade baixa: o título segue como o maior elemento da dobra (LCP)
* e a imagem que fica escondida pelo breakpoint (display: none) nem é baixada.
*/
export default function HeroWork({ url = null }: { url?: HeroWorkUrl }) {
const desktop = getShowcaseAssets(DESKTOP_PROJECT, "desktop");
const phone = getShowcaseAssets(MOBILE_PROJECT, "mobile");
const smallPhone = getShowcaseAssets(SMALL_SCREEN_PROJECT, "mobile");
return (
<div
data-testid="hero-work"
className="hero-in pointer-events-none absolute top-1/2 right-sp-12 hidden w-[40%] -translate-y-[46%] [--d:.8s] lg:block"
>
<BrowserFrame url={url} className="w-[88%]">
{/* biome-ignore lint/performance/noImgElement: pôster de 1440 px já otimizado em /public/videos; next/image pediria o otimizador para um arquivo menor que o necessário */}
<img
src={desktop.poster}
alt="Site da SEV Moda Íntima aberto no computador"
width={desktop.width}
height={desktop.height}
loading="lazy"
decoding="async"
fetchPriority="low"
className="block h-auto w-full"
/>
</BrowserFrame>
{/* position inline: .ds-phone (showcase.css, sem camada) define position: relative e vence a utilidade "absolute" */}
<PhoneFrame
className="right-0 bottom-[-12%] w-[26%]"
style={{ position: "absolute" }}
<>
<div
data-testid="hero-work"
className="hero-in pointer-events-none absolute top-1/2 right-sp-12 hidden w-[40%] -translate-y-[46%] [--d:.8s] lg:block"
>
{/* biome-ignore lint/performance/noImgElement: mesmo motivo do pôster desktop */}
<img
src={phone.poster}
alt="Site da AS Serviços aberto no celular"
width={phone.width}
height={phone.height}
loading="lazy"
decoding="async"
fetchPriority="low"
className="absolute inset-0 block h-full w-full object-cover object-top"
/>
</PhoneFrame>
</div>
<BrowserFrame url={url} className="w-[88%]">
{/* a moldura mostra ~491 px em 1440 px de tela: 36vw até 1400 px, e fixo dali em diante */}
<Image
src={desktop.poster}
alt="Site da SEV Moda Íntima aberto no computador"
width={desktop.width}
height={desktop.height}
sizes="(min-width: 1400px) 500px, 36vw"
loading="lazy"
fetchPriority="low"
className="block h-auto w-full"
/>
</BrowserFrame>
{/* position inline: .ds-phone (showcase.css, sem camada) define position: relative e vence a utilidade "absolute" */}
<PhoneFrame
className="right-0 bottom-[-12%] w-[26%]"
style={{ position: "absolute" }}
>
<Image
src={phone.poster}
alt="Site da AS Serviços aberto no celular"
width={phone.width}
height={phone.height}
sizes="(min-width: 1400px) 136px, 10vw"
loading="lazy"
fetchPriority="low"
className="absolute inset-0 block h-full w-full object-cover object-top"
/>
</PhoneFrame>
</div>
<div
data-testid="hero-work-mobile"
className="hero-in pointer-events-none mt-sp-5 flex items-center gap-sp-4 [--d:.8s] lg:hidden"
>
<PhoneFrame style={{ height: SMALL_PHONE_HEIGHT }}>
<Image
src={smallPhone.poster}
alt="Site da SEV Moda Íntima aberto no celular"
width={smallPhone.width}
height={smallPhone.height}
sizes="84px"
loading="lazy"
fetchPriority="low"
className="absolute inset-0 block h-full w-full object-cover object-top"
/>
</PhoneFrame>
<p className="max-w-[16em] font-display text-[15px] leading-6 text-[rgba(244,242,237,.72)]">
Site da SEV Moda Íntima, como aparece no celular.
</p>
</div>
</>
);
}