import type { Metadata, Viewport } from "next"; import { Geist_Mono, Space_Grotesk } from "next/font/google"; import type { ReactNode } from "react"; import { safeJsonLd } from "@/lib/jsonld"; import { buildPersonJsonLd, buildWebSiteJsonLd } from "@/lib/seo"; import { caseStudies, serviceHubLinks, servicePages, siteUrl, } from "@/lib/site-content"; import type { WhatsAppContexts } from "@/lib/whatsapp"; import CinematicOverlays from "./components/cinematic/CinematicOverlays"; import FloatingWhatsApp from "./components/FloatingWhatsApp"; import Footer from "./components/Footer"; import GoogleAnalytics from "./components/GoogleAnalytics"; import { Header } from "./components/Header"; import "./globals.css"; // O CSS da vitrine (molduras de navegador e de celular, vitrine animada) vem uma vez só, aqui, depois do global. // Importado por cada componente que o usa, ele era pré-carregado pelos links do menu em páginas que não o usam // (aviso de CSS pré-carregado e não usado). import "./components/showcase/showcase.css"; const titleSuffix = " | Evandro.dev.br"; /** Título da home: o `title.default` não passa pelo template, então leva o sufixo. Com ele, no máximo 60 caracteres. */ const defaultTitle = `Sites e sistemas em Joinville e SC${titleSuffix}`; const seoDescription = "Sites com pedido pelo WhatsApp e sistemas que tiram sua operação da planilha. Base em Garuva, SC; atendimento em Joinville e remoto em todo o Brasil."; /** Para o WhatsApp falar do serviço ou do case da página aberta: só nomes, sem o conteúdo pesado das páginas. */ const whatsAppContexts: WhatsAppContexts = { services: Object.fromEntries( servicePages.map((service) => [service.slug, service.navLabel]), ), cases: Object.fromEntries( caseStudies.map((caseStudy) => [caseStudy.id, caseStudy.title]), ), }; const spaceGrotesk = Space_Grotesk({ subsets: ["latin"], weight: ["400", "500", "600", "700"], display: "swap", variable: "--font-space-grotesk", }); const geistMono = Geist_Mono({ subsets: ["latin"], weight: ["400", "500", "600"], display: "swap", variable: "--font-geist-mono", }); export const metadata: Metadata = { title: { default: defaultTitle, template: `%s${titleSuffix}`, }, description: seoDescription, authors: [{ name: "Evandro Fonseca Junior" }], creator: "Evandro Fonseca Junior", publisher: "Evandro Fonseca Junior", applicationName: "Evandro.dev.br", category: "Technology", metadataBase: new URL(siteUrl), formatDetection: { email: false, address: false, telephone: false, }, openGraph: { title: defaultTitle, description: seoDescription, url: siteUrl, siteName: "Evandro.dev.br", locale: "pt_BR", type: "website", images: [ { url: "/opengraph-image", width: 1200, height: 630, alt: "Evandro.dev.br | Sites e sistemas para empresas", }, ], }, twitter: { card: "summary_large_image", title: defaultTitle, description: seoDescription, creator: "@evandrodevbr", images: [ { url: "/opengraph-image", alt: "Evandro.dev.br | Sites e sistemas para empresas", }, ], }, robots: { index: true, follow: true, googleBot: { index: true, follow: true, "max-video-preview": -1, "max-image-preview": "large", "max-snippet": -1, }, }, }; export const viewport: Viewport = { themeColor: "#000000", width: "device-width", initialScale: 1, maximumScale: 5, }; interface RootLayoutProps { children: ReactNode; } export default function RootLayout({ children }: Readonly) { const jsonLd = { "@context": "https://schema.org", "@graph": [buildWebSiteJsonLd(), buildPersonJsonLd()], }; return ( {/* Set arrival policy before native restoration and client-side layout changes. */} {/* Sem JavaScript as animações de entrada (opacity:0 inline vindo do servidor e palavras de título em CSS) não rodam: tudo precisa aparecer. */} Pular para o conteúdo {/* Aviso de cookies logo depois do "pular": fica cedo na ordem do Tab, ainda que fixo no rodapé da tela. */}
{children}
{/* Rodapé de todas as páginas; z-10 o mantém acima das cenas da home (relative z-0). */}
); }