Files
ESPDocs/app/[locale]/page.jsx
T
Lucas RayanandAntigravity 7fce46b9e1
CI / Lint e auditorias (push) Successful in 49s
CI / Trivy (dependências e segredos) (push) Successful in 11s
CI / Imagem Docker (linux/arm64) (push) Successful in 3m14s
feat(i18n): French and German localization (phase 5)
- Add full message catalogs and content for French (fr) and German (de)
- Implement ASCII canonical slugs for French and German routes
- Add language-specific search endpoints (/api/busca/fr, /api/busca/de)
- Configure strict 404 responses for /fr/livro and /de/livro
- Localize SEO, OpenGraph (fr_FR, de_DE), Twitter and JSON-LD schemas
- Add localized code example comments across 14 examples for fr and de
- Enable 'fr' and 'de' in ENABLED_LOCALES with root negotiation and redirect
- Extend test suite in check-i18n, smoke-imagem and verifica-vazamentos

Co-Authored-By: Antigravity <noreply@google.com>
2026-11-01 22:06:36 -03:00

728 lines
31 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import BookCover from "@/components/BookCover";
import Link from "next/link";
import seriesDataPt from "@/public/series.json";
import frameworksDataPt from "@/public/frameworks.json";
import seriesDataEn from "@/data/en/series.json";
import frameworksDataEn from "@/data/en/frameworks.json";
import seriesDataEs from "@/data/es/series.json";
import frameworksDataEs from "@/data/es/frameworks.json";
import seriesDataFr from "@/data/fr/series.json";
import frameworksDataFr from "@/data/fr/frameworks.json";
import seriesDataDe from "@/data/de/series.json";
import frameworksDataDe from "@/data/de/frameworks.json";
import { SITE } from "@/lib/seo";
import { getLocalizedHref } from "@/i18n/links";
import {
AlertTriangle,
ArrowRight,
ArrowUpRight,
BookOpen,
Box,
Cpu,
Scale,
SlidersHorizontal,
Terminal,
} from "lucide-react";
const FEATURED = ["ESP32", "ESP32-S3", "ESP32-C6", "ESP32-C3", "ESP32-P4", "ESP32-H2"];
const TOOLS_PT = [
{
href: "/seletor",
title: "Seletor inteligente",
desc: "Responda oito perguntas sobre o projeto e receba os chips mais compatíveis, com justificativa técnica.",
icon: SlidersHorizontal,
action: "Encontrar meu chip",
},
{
href: "/comparacao",
title: "Comparador de séries",
desc: "Coloque até quatro modelos lado a lado e compare núcleos, rádio, memória, periféricos e segurança.",
icon: Scale,
action: "Comparar agora",
},
{
href: "/diagnostico",
title: "Diagnóstico de falhas",
desc: "Causas e correções para brownout, bootloop, Guru Meditation, watchdog e falhas de flash.",
icon: AlertTriangle,
action: "Abrir diagnóstico",
},
{
href: "/componentes",
title: "Component Registry",
desc: "Drivers, bibliotecas e BSPs do registro oficial da Espressif, com manifesto pronto para o projeto.",
icon: Box,
action: "Explorar componentes",
},
{
href: "/frameworks",
title: "Frameworks e SDKs",
desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython e as ferramentas que ficam em volta do chip.",
icon: Terminal,
action: "Ver frameworks",
},
{
href: "/catalogo",
title: "Catálogo de placas",
desc: "DevKits e módulos reais, com interface USB, sensores integrados e link para a loja oficial.",
icon: Cpu,
action: "Ver placas",
},
];
const TOOLS_EN = [
{
href: "/seletor",
title: "Smart Selector",
desc: "Answer eight questions about your project and find recommended chips, backed by technical rationale.",
icon: SlidersHorizontal,
action: "Find my chip",
},
{
href: "/comparacao",
title: "Series Comparator",
desc: "Place up to four models side by side and compare cores, radio, memory, peripherals, and security.",
icon: Scale,
action: "Compare now",
},
{
href: "/diagnostico",
title: "Fault Diagnostics",
desc: "Root causes and fixes for brownout, bootloop, Guru Meditation, watchdog, and flash errors.",
icon: AlertTriangle,
action: "Open diagnostics",
},
{
href: "/componentes",
title: "Component Registry",
desc: "Drivers, libraries and BSPs from the official Espressif registry, with ready-to-use manifests.",
icon: Box,
action: "Explore components",
},
{
href: "/frameworks",
title: "Frameworks & SDKs",
desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython and the tools built around the silicon.",
icon: Terminal,
action: "View frameworks",
},
{
href: "/catalogo",
title: "Board Catalog",
desc: "Real DevKits and modules, with USB interfaces, integrated sensors and direct store links.",
icon: Cpu,
action: "View boards",
},
];
const TOOLS_ES = [
{
href: "/seletor",
title: "Selector inteligente",
desc: "Responda a ocho preguntas sobre su proyecto y reciba los chips más compatibles, con justificación técnica.",
icon: SlidersHorizontal,
action: "Encontrar mi chip",
},
{
href: "/comparacao",
title: "Comparador de series",
desc: "Compare hasta cuatro modelos frente a frente: núcleos, radio, memoria, periféricos y seguridad.",
icon: Scale,
action: "Comparar ahora",
},
{
href: "/diagnostico",
title: "Diagnóstico de fallos",
desc: "Causas y soluciones para brownout, bootloop, Guru Meditation, watchdog y fallos de flash.",
icon: AlertTriangle,
action: "Abrir diagnóstico",
},
{
href: "/componentes",
title: "Component Registry",
desc: "Drivers, bibliotecas y BSPs del registro oficial de Espressif, con manifiesto listo para su proyecto.",
icon: Box,
action: "Explorar componentes",
},
{
href: "/frameworks",
title: "Frameworks y SDKs",
desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython y las herramientas que rodean al silicio.",
icon: Terminal,
action: "Ver frameworks",
},
{
href: "/catalogo",
title: "Catálogo de placas",
desc: "DevKits y módulos reales, con interfaz USB, sensores integrados y enlace directo a la tienda oficial.",
icon: Cpu,
action: "Ver placas",
},
];
const TOOLS_FR = [
{
href: "/seletor",
title: "Sélecteur intelligent",
desc: "Répondez à huit questions sur votre projet et découvrez les puces recommandées, avec justification technique.",
icon: SlidersHorizontal,
action: "Trouver ma puce",
},
{
href: "/comparacao",
title: "Comparateur de séries",
desc: "Comparez jusqu'à quatre modèles côte à côte : cœurs, radio, mémoire, périphériques et sécurité.",
icon: Scale,
action: "Comparer maintenant",
},
{
href: "/diagnostico",
title: "Diagnostic de pannes",
desc: "Causes et solutions pour brownout, bootloop, Guru Meditation, watchdog et erreurs de flash.",
icon: AlertTriangle,
action: "Ouvrir le diagnostic",
},
{
href: "/componentes",
title: "Component Registry",
desc: "Pilotes, bibliothèques et BSP du registre officiel Espressif, avec manifeste prêt pour votre projet.",
icon: Box,
action: "Explorer les composants",
},
{
href: "/frameworks",
title: "Frameworks et SDKs",
desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython et les outils gravitant autour de la puce.",
icon: Terminal,
action: "Voir les frameworks",
},
{
href: "/catalogo",
title: "Catalogue de cartes",
desc: "DevKits et modules réels, avec interface USB, capteurs intégrés et lien vers la boutique officielle.",
icon: Cpu,
action: "Voir les cartes",
},
];
const TOOLS_DE = [
{
href: "/seletor",
title: "Intelligente Auswahl",
desc: "Beantworten Sie acht Fragen zu Ihrem Projekt und erhalten Sie passende Chip-Empfehlungen mit technischer Begründung.",
icon: SlidersHorizontal,
action: "Meinen Chip finden",
},
{
href: "/comparacao",
title: "Serienvergleich",
desc: "Vergleichen Sie bis zu vier Modelle nebeneinander: Kerne, Funk, Speicher, Peripherie und Sicherheit.",
icon: Scale,
action: "Jetzt vergleichen",
},
{
href: "/diagnostico",
title: "Fehlerdiagnose",
desc: "Ursachen und Lösungen für Brownout, Bootloops, Guru Meditation, Watchdog und Flash-Fehler.",
icon: AlertTriangle,
action: "Diagnose öffnen",
},
{
href: "/componentes",
title: "Component Registry",
desc: "Treiber, Bibliotheken und BSPs aus der offiziellen Espressif-Registry, mit fertigem Manifest für Ihr Projekt.",
icon: Box,
action: "Komponenten erkunden",
},
{
href: "/frameworks",
title: "Frameworks & SDKs",
desc: "ESP-IDF, Arduino, Rust, Matter, MicroPython und die Werkzeuge rund um den Chip.",
icon: Terminal,
action: "Frameworks ansehen",
},
{
href: "/catalogo",
title: "Board-Katalog",
desc: "Reale DevKits und Module, mit USB-Schnittstelle, integrierten Sensoren und Link zum offiziellen Shop.",
icon: Cpu,
action: "Boards ansehen",
},
];
const ERRORS_PT = [
{
code: "Brownout detector was triggered",
cause: "Queda de tensão na inicialização do rádio.",
fix: "Capacitor de 10–100 µF junto ao chip e fonte de 5 V com corrente suficiente.",
},
{
code: "Guru Meditation Error",
cause: "Ponteiro inválido, stack overflow ou uso incorreto de interrupção.",
fix: "Leia o backtrace com addr2line e dimensione a stack da task no FreeRTOS.",
},
{
code: "ADC2 indisponível com Wi-Fi",
cause: "O controlador de rádio reserva o ADC2 enquanto está ativo.",
fix: "Use os pinos do ADC1 para sensores analógicos que convivem com o Wi-Fi.",
},
{
code: "Flash Read err / bootloader",
cause: "Tensão ou modo de flash incorretos, ou pino de strapping preso no boot.",
fix: "Confira 3,3 V, pull-ups de boot e a configuração de flash no esptool.",
},
];
const ERRORS_EN = [
{
code: "Brownout detector was triggered",
cause: "Voltage sag upon radio startup.",
fix: "10–100 µF capacitor close to chip and a 5 V supply with sufficient current.",
},
{
code: "Guru Meditation Error",
cause: "Invalid pointer, stack overflow, or improper ISR usage.",
fix: "Inspect backtrace with addr2line and properly size FreeRTOS task stacks.",
},
{
code: "ADC2 unavailable with Wi-Fi",
cause: "Internal radio controller reserves ADC2 while active.",
fix: "Use ADC1 pins for analog sensors operating alongside Wi-Fi.",
},
{
code: "Flash Read err / bootloader",
cause: "Incorrect flash voltage/mode or strapping pin pulled during boot.",
fix: "Check 3.3 V rail, boot pull-ups, and flash configuration in esptool.",
},
];
const ERRORS_ES = [
{
code: "Brownout detector was triggered",
cause: "Caída de tensión al inicializar la etapa de radio.",
fix: "Condensador de 10–100 µF junto al chip y fuente de 5 V con corriente suficiente.",
},
{
code: "Guru Meditation Error",
cause: "Puntero inválido, desbordamiento de pila o uso incorrecto de interrupciones.",
fix: "Analice el backtrace con addr2line y dimensione la pila de la tarea en FreeRTOS.",
},
{
code: "ADC2 no disponible con Wi-Fi",
cause: "El controlador de radio reserva ADC2 mientras permanece activo.",
fix: "Utilice los pines de ADC1 para sensores analógicos que funcionen junto con Wi-Fi.",
},
{
code: "Flash Read err / bootloader",
cause: "Tensión o modo de flash incorrectos, o pin de strapping forzado en el arranque.",
fix: "Compruebe la línea de 3,3 V, los pull-ups de arranque y la configuración de flash en esptool.",
},
];
const ERRORS_FR = [
{
code: "Brownout detector was triggered",
cause: "Baisse de tension lors du démarrage de la radio.",
fix: "Condensateur de 10 à 100 µF au plus près de la puce et alimentation 5 V avec courant suffisant.",
},
{
code: "Guru Meditation Error",
cause: "Pointeur invalide, dépassement de pile ou mauvaise utilisation des interruptions.",
fix: "Analysez le backtrace avec addr2line et dimensionnez la pile de la tâche sous FreeRTOS.",
},
{
code: "ADC2 indisponible avec le Wi-Fi",
cause: "Le contrôleur radio réserve l'ADC2 tant qu'il est actif.",
fix: "Utilisez les broches de l'ADC1 pour les capteurs analogiques fonctionnant conjointement avec le Wi-Fi.",
},
{
code: "Flash Read err / bootloader",
cause: "Tension ou mode de flash incorrect, ou broche de strapping forcée au démarrage.",
fix: "Vérifiez la ligne 3,3 V, les pull-ups de démarrage et la configuration de flash dans esptool.",
},
];
const ERRORS_DE = [
{
code: "Brownout detector was triggered",
cause: "Spannungseinbruch beim Starten der Funkeinheit.",
fix: "Kondensator mit 10–100 µF nah am Chip und 5-V-Netzteil mit ausreichender Stromstärke.",
},
{
code: "Guru Meditation Error",
cause: "Ungültiger Zeiger, Stack-Überlauf oder fehlerhafte Interrupt-Nutzung.",
fix: "Backtrace mit addr2line analysieren und Stack-Größe der FreeRTOS-Task anpassen.",
},
{
code: "ADC2 nicht verfügbar bei aktivem Wi-Fi",
cause: "Der Funk-Controller reserviert ADC2, solange er aktiv ist.",
fix: "Nutzen Sie ADC1-Pins für analoge Sensoren, die gleichzeitig mit Wi-Fi betrieben werden.",
},
{
code: "Flash Read err / bootloader",
cause: "Falsche Flash-Spannung/-Modus oder beim Booten falsch beschalteter Strapping-Pin.",
fix: "3,3-V-Schiene, Boot-Pull-ups und Flash-Konfiguration in esptool überprüfen.",
},
];
export default async function Home({ params }) {
const { locale } = await params;
const isPt = locale === "pt";
const isEn = locale === "en";
const isEs = locale === "es";
const isFr = locale === "fr";
const isDe = locale === "de";
const getLinkHref = (href) => getLocalizedHref(`/${locale}${href}`, locale);
const seriesData = isPt ? seriesDataPt : isEs ? seriesDataEs : isFr ? seriesDataFr : isDe ? seriesDataDe : seriesDataEn;
const frameworksData = isPt ? frameworksDataPt : isEs ? frameworksDataEs : isFr ? frameworksDataFr : isDe ? frameworksDataDe : frameworksDataEn;
const tools = isPt ? TOOLS_PT : isEs ? TOOLS_ES : isFr ? TOOLS_FR : isDe ? TOOLS_DE : TOOLS_EN;
const errors = isPt ? ERRORS_PT : isEs ? ERRORS_ES : isFr ? ERRORS_FR : isDe ? ERRORS_DE : ERRORS_EN;
const seriesCount = Object.keys(seriesData).length;
const frameworkCount = Object.keys(frameworksData).length;
const featured = FEATURED.map((key) => [key, seriesData[key]]).filter(([, serie]) => Boolean(serie));
return (
<div className="site-canvas min-h-screen">
<Header />
<main id="conteudo">
<section className="shell pb-10 pt-14 sm:pt-20 lg:pb-14 lg:pt-24">
<div className="mx-auto max-w-4xl text-center">
<span className="chip chip-accent">
<Cpu className="h-3.5 w-3.5" aria-hidden="true" />
{isPt ? "Documentação aberta do ESP32" : isEs ? "Documentación abierta de ESP32" : isFr ? "Documentation ouverte de l'ESP32" : isDe ? "Offene ESP32-Dokumentation" : "Open ESP32 documentation"}
</span>
<h1 className="display mt-6 text-[clamp(2.9rem,7.4vw,5.4rem)]">
{isPt ? (
<>
O ESP32,
<br className="sm:hidden" /> sem ruído.
</>
) : isEs ? (
<>
El ESP32,
<br className="sm:hidden" /> sin ruido.
</>
) : isFr ? (
<>
L'ESP32,
<br className="sm:hidden" /> sans bruit.
</>
) : isDe ? (
<>
Der ESP32,
<br className="sm:hidden" /> ohne Umwege.
</>
) : (
<>
The ESP32,
<br className="sm:hidden" /> without noise.
</>
)}
</h1>
<p className="lede mx-auto mt-6 max-w-2xl">
{isPt
? "Especificações, pinagem, comparações e diagnóstico em uma leitura só. Menos tradução de datasheet, mais tempo com o protótipo na bancada."
: isEs
? "Especificaciones, patillaje, comparaciones y diagnóstico en una sola lectura. Menos traducción de hojas de datos, más tiempo con el prototipo en el banco de trabajo."
: isFr
? "Spécifications, brochage, comparaisons et diagnostics en une seule lecture. Moins de traduction de fiches techniques, plus de temps à l'établi."
: isDe
? "Spezifikationen, Pinbelegung, Vergleiche und Diagnose auf einen Blick. Weniger Datenblatt-Übersetzung, mehr Zeit am Prüfplatz."
: "Specifications, pinouts, comparisons and diagnostics in a single read. Less datasheet translation, more workbench time."}
</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
<Link href={getLinkHref("/seletor")} className="btn btn-primary w-full sm:w-auto">
<SlidersHorizontal className="h-4 w-4" aria-hidden="true" />
{isPt ? "Escolher meu chip" : isEs ? "Elegir mi chip" : isFr ? "Choisir ma puce" : isDe ? "Meinen Chip wählen" : "Choose my chip"}
</Link>
<Link href={getLinkHref("/series")} className="btn btn-ghost w-full sm:w-auto">
{isPt ? (
<>Explorar as {seriesCount} séries</>
) : isEs ? (
<>Explorar las {seriesCount} series</>
) : isFr ? (
<>Explorer les {seriesCount} séries</>
) : isDe ? (
<>Die {seriesCount} Serien erkunden</>
) : (
<>Explore the {seriesCount} series</>
)}
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
<p className="mt-7 flex flex-wrap items-center justify-center gap-x-5 gap-y-2 text-sm text-muted">
<span><strong className="font-semibold text-ink">{seriesCount}</strong> {isPt ? "séries" : isEs ? "series" : isFr ? "séries" : isDe ? "Serien" : "series"}</span>
<span aria-hidden="true" className="text-line-strong">·</span>
<span><strong className="font-semibold text-ink">{frameworkCount}</strong> frameworks</span>
{isPt ? (
<>
<span aria-hidden="true" className="text-line-strong">·</span>
<span><strong className="font-semibold text-ink">78</strong> capítulos no livro</span>
<span aria-hidden="true" className="text-line-strong">·</span>
<span>100% em português</span>
</>
) : isEs ? (
<>
<span aria-hidden="true" className="text-line-strong">·</span>
<span>100% en español</span>
</>
) : isFr ? (
<>
<span aria-hidden="true" className="text-line-strong">·</span>
<span>100 % en français</span>
</>
) : isDe ? (
<>
<span aria-hidden="true" className="text-line-strong">·</span>
<span>100 % auf Deutsch</span>
</>
) : (
<>
<span aria-hidden="true" className="text-line-strong">·</span>
<span>100% in English</span>
</>
)}
</p>
</div>
<div className="panel mt-12 overflow-hidden sm:mt-16">
<div className="flex flex-wrap items-center justify-between gap-3 px-5 py-4 sm:px-7">
<div>
<h2 className="text-sm font-semibold text-ink">{isPt ? "Séries em destaque" : isEs ? "Series destacadas" : isFr ? "Séries en vedette" : isDe ? "Vorgestellte Serien" : "Featured series"}</h2>
<p className="mt-0.5 text-xs text-muted">
{isPt ? "O essencial de cada família, antes de abrir o datasheet." : isEs ? "Lo esencial de cada familia, antes de abrir la hoja de datos." : isFr ? "L'essentiel de chaque famille, avant d'ouvrir la fiche technique." : isDe ? "Das Wesentliche jeder Familie, bevor Sie das Datenblatt öffnen." : "The essentials of each family before opening the datasheet."}
</p>
</div>
<Link href={getLinkHref("/series")} className="inline-flex min-h-11 items-center gap-1.5 text-sm font-medium text-accent transition-colors hover:text-accent-strong">
{isPt ? "Índice completo" : isEs ? "Índice completo" : isFr ? "Index complet" : isDe ? "Vollständiger Index" : "Full index"}
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
<div className="grid gap-px border-t border-line bg-line sm:grid-cols-2 lg:grid-cols-3">
{featured.map(([key, serie]) => (
<Link
key={key}
href={getLinkHref(`/series/${key}`)}
className="group flex min-w-0 flex-col bg-surface p-5 transition-colors duration-300 hover:bg-surface-2 sm:p-6"
>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<h3 className="font-display text-xl font-semibold tracking-[-0.035em] text-ink">{key}</h3>
<p className="mt-1 truncate text-xs text-muted">{serie.arquitetura}</p>
</div>
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-accent-soft text-accent transition-transform duration-300 group-hover:scale-105">
<Cpu className="h-4 w-4" aria-hidden="true" />
</span>
</div>
<p className="mt-5 line-clamp-2 text-sm leading-relaxed text-muted">{serie.descricao}</p>
<div className="mt-5 flex items-center justify-between gap-3 border-t border-line pt-3.5">
<span className="truncate font-mono text-[0.6875rem] text-ink-soft">{serie.frequencia}</span>
<span className="inline-flex shrink-0 items-center gap-1 text-xs font-medium text-accent">
{isPt ? "Ficha" : isEs ? "Ficha" : isFr ? "Fiche" : isDe ? "Datenblatt" : "Spec sheet"}
<ArrowUpRight className="h-3.5 w-3.5 transition-transform duration-300 group-hover:-translate-y-0.5 group-hover:translate-x-0.5" aria-hidden="true" />
</span>
</div>
</Link>
))}
</div>
</div>
</section>
{isPt && (
<section className="shell section pt-8 sm:pt-12" aria-labelledby="livro-titulo">
<div className="relative isolate overflow-hidden rounded-[2rem] bg-[#0b0d10] ring-1 ring-white/10 dark:bg-[#05070a] dark:ring-white/15 sm:rounded-[2.5rem]">
<div
className="pointer-events-none absolute -left-24 top-1/2 -z-10 h-80 w-80 -translate-y-1/2 rounded-full bg-[#2dd4bf]/15 blur-3xl"
aria-hidden="true"
/>
<div className="grid lg:grid-cols-[0.82fr_1.18fr]">
<div className="order-1 flex flex-col justify-center p-7 sm:p-10 lg:order-2 lg:p-14">
<span className="inline-flex w-fit items-center gap-2 rounded-full border border-white/15 bg-white/5 px-3 py-1 text-xs font-semibold text-[#75cdc1]">
<BookOpen className="h-3.5 w-3.5" aria-hidden="true" />
Livro novo
</span>
<h2 id="livro-titulo" className="mt-5 font-display text-[clamp(2rem,4.2vw,3.2rem)] font-semibold leading-[1.05] tracking-[-0.04em] text-[#f4f6f8]">
{SITE.livroTitulo}
</h2>
<p className="mt-2 text-base text-[#a6afbd]">{SITE.livroSubtitulo}</p>
<p className="mt-5 max-w-xl text-sm leading-relaxed text-[#c4cbd5]">
Um percurso completo pelo hardware e pelo firmware do ESP32: da arquitetura do chip
à implantação de produtos conectados. São 12 partes e 78 capítulos sobre Arduino,
ESP-IDF, FreeRTOS, Wi-Fi, BLE, Thread e Matter, periféricos, depuração, baixo consumo
e segurança.
</p>
<p className="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs text-[#a6afbd]">
<span>12 partes</span>
<span aria-hidden="true" className="text-white/20">·</span>
<span>78 capítulos</span>
<span aria-hidden="true" className="text-white/20">·</span>
<span>ISBN {SITE.livroIsbn}</span>
</p>
<div className="mt-7 flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
<a
href={SITE.livroAmazon}
target="_blank"
rel="noopener noreferrer"
className="inline-flex min-h-11 items-center justify-center gap-2 rounded-full bg-[#2dd4bf] px-5 text-sm font-semibold text-[#06201d] transition-colors duration-300 hover:bg-[#5eead4]"
>
Comprar digital
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
</a>
<a
href={SITE.livroUiclap}
target="_blank"
rel="noopener noreferrer"
className="inline-flex min-h-11 items-center justify-center gap-2 rounded-full border border-white/20 px-5 text-sm font-semibold text-[#f4f6f8] transition-colors duration-300 hover:border-white/40 hover:bg-white/5"
>
Comprar impresso
<ArrowUpRight className="h-4 w-4" aria-hidden="true" />
</a>
<Link href="/pt/livro" className="inline-flex min-h-11 items-center justify-center gap-1.5 rounded-full px-3 text-sm font-medium text-[#a6afbd] transition-colors duration-300 hover:text-[#f4f6f8]">
Ficha completa
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</div>
<div className="order-2 flex items-center justify-center border-t border-white/10 p-8 sm:p-12 lg:order-1 lg:border-r lg:border-t-0 lg:p-14">
<div className="relative w-[min(15.5rem,68%)] lg:w-[min(17rem,100%)]">
<div className="absolute -inset-10 -z-10 rounded-full bg-[#2dd4bf]/10 blur-3xl" aria-hidden="true" />
<BookCover />
</div>
</div>
</div>
</div>
</section>
)}
<section className="shell section" aria-labelledby="ferramentas-titulo">
<div className="mx-auto max-w-2xl text-center">
<p className="eyebrow">{isPt ? "Ferramentas" : isEs ? "Herramientas" : isFr ? "Outils" : isDe ? "Werkzeuge" : "Tools"}</p>
<h2 id="ferramentas-titulo" className="display mt-4 text-[clamp(2rem,4.8vw,3.4rem)]">
{isPt ? "Comece pelo problema." : isEs ? "Empiece por el problema." : isFr ? "Commencez par le problème." : isDe ? "Beginnen Sie beim Problem." : "Start with the problem."}
</h2>
<p className="lede mt-4">
{isPt
? "Cada ferramenta resolve uma etapa concreta do projeto, sem cadastro e sem rodeio."
: isEs
? "Cada herramienta resuelve una fase concreta del proyecto, sin registros ni rodeos."
: isFr
? "Chaque outil répond à une étape concrète du projet, sans inscription ni détour."
: isDe
? "Jedes Werkzeug löst einen konkreten Projektschritt – ohne Registrierung und ohne Umwege."
: "Each tool addresses a concrete development phase, with no sign-ups and zero fluff."}
</p>
</div>
<div className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{tools.map((tool) => {
const Icon = tool.icon;
return (
<Link key={tool.href} href={getLinkHref(tool.href)} className="card card-interactive group flex flex-col p-5 sm:p-6">
<span className="grid h-11 w-11 place-items-center rounded-2xl bg-accent-soft text-accent">
<Icon className="h-5 w-5" aria-hidden="true" />
</span>
<h3 className="mt-5 font-display text-lg font-semibold tracking-[-0.025em] text-ink">{tool.title}</h3>
<p className="mt-2 flex-1 text-sm leading-relaxed text-muted">{tool.desc}</p>
<span className="mt-5 inline-flex items-center gap-1.5 text-sm font-medium text-accent">
{tool.action}
<ArrowRight className="h-4 w-4 transition-transform duration-300 group-hover:translate-x-0.5" aria-hidden="true" />
</span>
</Link>
);
})}
</div>
</section>
<section className="shell section pt-0" aria-labelledby="diagnostico-titulo">
<div className="grid items-start gap-10 lg:grid-cols-[0.78fr_1.22fr] lg:gap-16">
<div className="lg:sticky lg:top-28">
<p className="eyebrow">{isPt ? "Diagnóstico" : isEs ? "Diagnóstico" : isFr ? "Diagnostic" : isDe ? "Diagnose" : "Diagnostics"}</p>
<h2 id="diagnostico-titulo" className="display mt-4 text-[clamp(2rem,4.8vw,3.4rem)]">
{isPt ? "O primeiro boot não precisa virar noite." : isEs ? "El primer arranque no tiene por qué desvelarlo." : isFr ? "Le premier démarrage n'a pas à vous gâcher la nuit." : isDe ? "Der erste Boot muss nicht die Nacht kosten." : "First boot doesn't have to take all night."}
</h2>
<p className="lede mt-4">
{isPt
? "Os erros mais comuns têm causa conhecida. O guia parte da mensagem no terminal e chega ao ajuste de hardware ou firmware."
: isEs
? "Los errores más comunes tienen una causa conocida. Esta guía parte del mensaje en el terminal y llega al ajuste de hardware o firmware."
: isFr
? "Les erreurs les plus courantes ont une cause connue. Ce guide part du message dans le terminal pour arriver au réglage matériel ou logiciel."
: isDe
? "Die häufigsten Fehler haben bekannte Ursachen. Dieser Leitfaden geht von der Terminalmeldung aus und führt zur passenden Hardware- oder Firmware-Anpassung."
: "Common errors have known causes. This guide starts with the serial terminal log and leads to the hardware or firmware adjustment."}
</p>
<Link href={getLinkHref("/diagnostico")} className="btn btn-accent mt-7">
<AlertTriangle className="h-4 w-4" aria-hidden="true" />
{isPt ? "Abrir os 30+ diagnósticos" : isEs ? "Abrir los 30+ diagnósticos" : isFr ? "Ouvrir les 30+ diagnostics" : isDe ? "Die 30+ Diagnosen öffnen" : "Open the 30+ diagnostics"}
</Link>
</div>
<div className="panel-dark min-w-0 overflow-hidden rounded-[1.75rem]">
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-white/10 px-5 py-4 sm:px-6">
<span className="inline-flex items-center gap-2 font-mono text-xs text-[#75cdc1]">
<Terminal className="h-4 w-4" aria-hidden="true" />
boot.log
</span>
<span className="text-xs text-[#a6afbd]">{isPt ? "4 exemplos de 30+" : isEs ? "4 ejemplos de 30+" : isFr ? "4 exemples sur 30+" : isDe ? "4 Beispiele von 30+" : "4 examples of 30+"}</span>
</div>
<div className="divide-y divide-white/10">
{errors.map((item) => (
<Link key={item.code} href={getLinkHref("/diagnostico")} className="group grid gap-2 px-5 py-5 transition-colors duration-300 hover:bg-white/[0.035] sm:px-6">
<span className="break-words font-mono text-[0.75rem] font-medium leading-relaxed text-[#ffb39b]">{item.code}</span>
<span className="text-sm font-medium text-[#f4f6f8]">{item.cause}</span>
<span className="text-xs leading-relaxed text-[#a6afbd]">{item.fix}</span>
</Link>
))}
</div>
</div>
</div>
</section>
<section className="shell pb-20 pt-4 md:pb-28">
<div className="mx-auto max-w-3xl text-center">
<p className="eyebrow">{isPt ? "Curadoria independente" : isEs ? "Curaduría independiente" : isFr ? "Sélection indépendante" : isDe ? "Unabhängige Kuration" : "Independent curation"}</p>
<h2 className="display mt-4 text-[clamp(2rem,4.5vw,3.2rem)]">
{isPt ? "Feito para consultar no meio do projeto." : isEs ? "Diseñado para consultar en mitad del proyecto." : isFr ? "Conçu pour être consulté en plein projet." : isDe ? "Gemacht für das Nachschlagen mitten im Projekt." : "Built for quick lookup mid-project."}
</h2>
<p className="lede mx-auto mt-4 max-w-2xl">
{isPt
? "O ESPDocs organiza dados públicos da Espressif e aponta sempre para a fonte oficial. Para decisões críticas, o datasheet e o Technical Reference Manual continuam sendo a palavra final."
: isEs
? "ESPDocs organiza datos públicos de Espressif y apunta siempre a la fuente oficial. Para decisiones críticas, la hoja de datos y el Technical Reference Manual continúan teniendo la última palabra."
: isFr
? "ESPDocs organise les données publiques d'Espressif et renvoie systématiquement vers la source officielle. Pour les décisions critiques, la fiche technique et le Technical Reference Manual restent la référence définitive."
: isDe
? "ESPDocs strukturiert öffentliche Espressif-Daten und verweist stets auf die offizielle Quelle. Für kritische Entscheidungen bleiben das Datenblatt und das Technical Reference Manual maßgeblich."
: "ESPDocs organizes public Espressif data and points directly to official sources. For critical engineering decisions, the datasheet and Technical Reference Manual remain the definitive authority."}
</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
<Link href={getLinkHref("/glossario")} className="btn btn-ghost w-full sm:w-auto">
<BookOpen className="h-4 w-4" aria-hidden="true" />
{isPt ? "Glossário" : isEs ? "Glosario" : isFr ? "Glossaire" : isDe ? "Glossar" : "Glossary"}
</Link>
<Link href={getLinkHref("/sobre")} className="btn btn-primary w-full sm:w-auto">
{isPt ? "Sobre o projeto" : isEs ? "Acerca del proyecto" : isFr ? "À propos du projet" : isDe ? "Über das Projekt" : "About the project"}
<ArrowRight className="h-4 w-4" aria-hidden="true" />
</Link>
</div>
</div>
</section>
</main>
<Footer />
</div>
);
}