// File: layout.tsx // Path: apps/web/app/layout.tsx // Project: AI Risk Index — airiskindex.io // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // Copyright © 2026 Simon-Pierre Boucher. All rights reserved. // // Description: Root layout: sticky header, mobile drawer menu, preview banner, footer. import type { Metadata, Viewport } from "next"; import Link from "next/link"; import type { ReactNode } from "react"; import { prisma } from "@airiskindex/db"; import { INDEX_VERSION } from "@airiskindex/scoring"; import { LogoMark } from "@/components/logo"; import { MobileMenu } from "@/components/mobile-menu"; import "./globals.css"; export const metadata: Metadata = { metadataBase: new URL("https://www.airiskindex.io"), title: "AI Risk Index — task-based AI exposure scores for every occupation", description: "Transparent, versioned, task-based scores of how occupations are exposed to AI — with separate exposure, substitution and augmentation sub-scores and confidence intervals. Adaptation guidance, not doom.", openGraph: { title: "AI Risk Index", description: "How is your occupation exposed to AI — task by task? Three sub-scores, confidence intervals, fully open methodology.", url: "https://www.airiskindex.io", siteName: "AI Risk Index", }, }; export const viewport: Viewport = { width: "device-width", initialScale: 1, themeColor: [ { media: "(prefers-color-scheme: light)", color: "#f9f9f7" }, { media: "(prefers-color-scheme: dark)", color: "#0d0d0d" }, ], }; async function isDemoRun(): Promise { try { const run = await prisma.scoreRun.findFirst({ orderBy: { createdAt: "desc" } }); return run ? run.raterModels.some((model) => model.startsWith("demo-")) : true; } catch { return true; } } const NAV = [ ["/ranking", "Full index"], ["/insights", "Insights"], ["/occupations", "Browse"], ["/methodology", "Methodology"], ["/api/v1/methodology", "API"], ] as const; const MOBILE_NAV = [ ...NAV, ["/terms", "Terms of Service"], ["/privacy", "Privacy Policy"], ] as const; export default async function RootLayout({ children, }: { children: ReactNode; }): Promise { const demo = await isDemoRun(); return ( {demo && (
Preview — scores are computed from a demonstration dataset while the first published index run ({INDEX_VERSION.replace("-draft.1", "")}) is in progress.
)}
{children} ); }