Python 51.6%
TypeScript 46.7%
CSS 1.7%
1import type { Metadata, Viewport } from "next";2import { Fraunces, Inter, JetBrains_Mono } from "next/font/google";3import Link from "next/link";4import "./globals.css";5import { apiSafe, SITE_URL, type Meta } from "@/lib/api";6import { MobileNav, TopNav, Wordmark } from "@/components/shell";7import { StatusStrip } from "@/components/status-strip";8import { THEME_INIT_SCRIPT } from "@/components/theme";9import { Disclaimer } from "@/components/ui";1011const inter = Inter({ subsets: ["latin"], variable: "--font-inter", display: "swap" });12const fraunces = Fraunces({ subsets: ["latin"], variable: "--font-fraunces", display: "swap", axes: ["opsz", "SOFT"] });13const jetbrains = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains", display: "swap", weight: ["400", "500", "600"] });1415export const metadata: Metadata = {16 metadataBase: new URL(SITE_URL),17 title: { default: "QC26 — Québec 2026. Comprendre. Prévoir. Suivre.", template: "%s | QC26" },18 description: "Prévisions, sondages, marchés de prédiction, presse, boussole électorale et résultats en direct de l'élection québécoise du 5 octobre 2026.",19 applicationName: "QC26",20 openGraph: { type: "website", siteName: "QC26", locale: "fr_CA", url: SITE_URL, title: "QC26 — L'élection québécoise, en données", description: "Prévisions, sondages, signaux alternatifs, boussole électorale et résultats en direct de l'élection québécoise 2026." },21 twitter: { card: "summary_large_image", title: "QC26", description: "L'élection québécoise 2026, en données." },22 robots: { index: true, follow: true },23 alternates: { canonical: "/" },24};25export const viewport: Viewport = { themeColor: "#f6f5f1", width: "device-width", initialScale: 1, viewportFit: "cover" };2627export default async function RootLayout({ children }: { children: React.ReactNode }) {28 const meta = await apiSafe<Meta>("/api/meta", { revalidate: 60 });29 const mode = meta?.mode ?? "forecast";30 const days = meta?.daysToElection ?? Math.ceil((new Date("2026-10-05T00:00:00-04:00").getTime() - Date.now()) / 86400000);31 return (32 <html lang="fr-CA" className={`${inter.variable} ${fraunces.variable} ${jetbrains.variable}`} suppressHydrationWarning>33 <head>34 <script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />35 </head>36 <body className="pb-[calc(var(--bottom-nav-h)+env(safe-area-inset-bottom))] lg:pb-0">37 <TopNav daysToElection={days} mode={mode} modelVersion={meta?.modelVersion} />38 <main className="mx-auto max-w-[1320px] px-4 md:px-6 py-6 md:py-10">{children}</main>39 <footer className="border-t border-border mt-20">40 <div className="mx-auto max-w-[1320px] px-4 md:px-6 py-12 grid gap-10 md:grid-cols-[1.5fr_1fr_1fr_1fr]">41 <div>42 <Wordmark size={28} />43 <p className="text-[14px] text-ink-2 mt-4 max-w-sm leading-relaxed">Québec 2026. Comprendre. Prévoir. Suivre. Plateforme indépendante de données, d'analyse et de prévision consacrée à l'élection générale du 5 octobre 2026.</p>44 <div className="mt-5"><Disclaimer text={meta?.disclaimer} /></div>45 </div>46 <FooterCol title="Prévision" links={[["/prevision", "Prévision"], ["/signaux", "Au-delà des sondages"], ["/sondages", "Sondages"], ["/carte", "Carte"], ["/circonscriptions", "Circonscriptions"], ["/live", mode === "forecast" ? "Soirée électorale" : "Direct"]]} />47 <FooterCol title="Partis" links={[["/partis", "Partis"], ["/partis/comparateur", "Comparateur"], ["/enjeux", "Enjeux"], ["/documents", "Bibliothèque documentaire"], ["/boussole", "Boussole"]]} />48 <FooterCol title="Transparence" links={[["/methodologie", "Méthodologie"], ["/methodologie/changelog", "Changelog du modèle"], ["/statut", "Statut des données"], ["/api/docs", "API"], ["/admin", "Administration"]]} />49 </div>50 <StatusStrip />51 </footer>52 <MobileNav mode={mode} />53 </body>54 </html>55 );56}5758function FooterCol({ title, links }: { title: string; links: [string, string][] }) {59 return (60 <div>61 <div className="eyebrow mb-3">{title}</div>62 <ul className="space-y-2 text-[14px]">63 {links.map(([href, label]) => (<li key={href}><Link href={href} className="text-ink-2 hover:text-ink transition-colors">{label}</Link></li>))}64 </ul>65 </div>66 );67}68