SPB Git

spb/airiskindex Public

The most methodologically rigorous, fully transparent AI job-exposure index.

TypeScript 88% Python 6.1% SQL 2.7% CSS 1.2% JavaScript 0.9% Shell 0.8%
6.9 KB · 186 lines tsx
Raw Blame History
1//  File:    layout.tsx2//  Path:    apps/web/app/layout.tsx3//  Project: AI Risk Index — airiskindex.io4//  Author:  Simon-Pierre Boucher5//  Contact: contact@spboucher.ai6//  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.7//8//  Description: Root layout: sticky header, mobile drawer menu, preview banner, footer.910import type { Metadata, Viewport } from "next";11import Link from "next/link";12import type { ReactNode } from "react";13import { prisma } from "@airiskindex/db";14import { INDEX_VERSION } from "@airiskindex/scoring";15import { LogoMark } from "@/components/logo";16import { MobileMenu } from "@/components/mobile-menu";17import "./globals.css";1819export const metadata: Metadata = {20  metadataBase: new URL("https://www.airiskindex.io"),21  title: "AI Risk Index — task-based AI exposure scores for every occupation",22  description:23    "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.",24  openGraph: {25    title: "AI Risk Index",26    description:27      "How is your occupation exposed to AI — task by task? Three sub-scores, confidence intervals, fully open methodology.",28    url: "https://www.airiskindex.io",29    siteName: "AI Risk Index",30  },31};3233export const viewport: Viewport = {34  width: "device-width",35  initialScale: 1,36  themeColor: [37    { media: "(prefers-color-scheme: light)", color: "#f9f9f7" },38    { media: "(prefers-color-scheme: dark)", color: "#0d0d0d" },39  ],40};4142async function isDemoRun(): Promise<boolean> {43  try {44    const run = await prisma.scoreRun.findFirst({ orderBy: { createdAt: "desc" } });45    return run ? run.raterModels.some((model) => model.startsWith("demo-")) : true;46  } catch {47    return true;48  }49}5051const NAV = [52  ["/ranking", "Full index"],53  ["/insights", "Insights"],54  ["/occupations", "Browse"],55  ["/methodology", "Methodology"],56  ["/api/v1/methodology", "API"],57] as const;5859const MOBILE_NAV = [60  ...NAV,61  ["/terms", "Terms of Service"],62  ["/privacy", "Privacy Policy"],63] as const;6465export default async function RootLayout({66  children,67}: {68  children: ReactNode;69}): Promise<JSX.Element> {70  const demo = await isDemoRun();71  return (72    <html lang="en">73      <body className="min-h-screen antialiased">74        {demo && (75          <div className="bg-[var(--seq)] px-4 py-2 text-center text-xs font-medium text-white">76            Preview — scores are computed from a demonstration dataset while the first published77            index run ({INDEX_VERSION.replace("-draft.1", "")}) is in progress.78          </div>79        )}80        <header className="sticky top-0 z-40 border-b border-[var(--border)] backdrop-blur-md [background:var(--header)]">81          <nav className="relative mx-auto flex max-w-5xl items-center justify-between gap-3 px-4 py-3 sm:px-6">82            <Link href="/" className="flex shrink-0 items-center gap-2.5 font-semibold tracking-tight">83              <LogoMark size={24} />84              AI Risk Index85            </Link>86            <div className="hidden items-center gap-6 text-sm text-[var(--ink-2)] sm:flex">87              {NAV.map(([href, label]) => (88                <Link89                  key={href}90                  href={href}91                  className="transition-colors hover:text-[var(--ink)]"92                >93                  {label}94                </Link>95              ))}96            </div>97            <MobileMenu items={MOBILE_NAV} />98          </nav>99        </header>100        {children}101        <footer className="mt-20 border-t border-[var(--border)] bg-[var(--surface-1)]">102          <div className="mx-auto grid max-w-5xl gap-8 px-4 py-10 sm:grid-cols-3 sm:px-6">103            <div>104              <p className="flex items-center gap-2.5 font-semibold tracking-tight">105                <LogoMark size={20} />106                AI Risk Index107              </p>108              <p className="mt-2 text-xs leading-relaxed text-[var(--muted)]">109                The transparent, task-based AI job-exposure index. Methodology {INDEX_VERSION} —110                every score traces to a stored, immutable computation run.111              </p>112            </div>113            <div className="text-sm">114              <p className="text-xs font-semibold uppercase tracking-wide text-[var(--muted)]">115                Explore116              </p>117              <ul className="mt-2 space-y-1.5 text-[var(--ink-2)]">118                <li>119                  <Link href="/ranking" className="hover:text-[var(--ink)] hover:underline">120                    Full index121                  </Link>122                </li>123                <li>124                  <Link href="/insights" className="hover:text-[var(--ink)] hover:underline">125                    Insights126                  </Link>127                </li>128                <li>129                  <Link href="/occupations" className="hover:text-[var(--ink)] hover:underline">130                    All occupations131                  </Link>132                </li>133                <li>134                  <Link href="/methodology" className="hover:text-[var(--ink)] hover:underline">135                    Methodology136                  </Link>137                </li>138                <li>139                  <Link140                    href="/api/v1/methodology"141                    className="hover:text-[var(--ink)] hover:underline"142                  >143                    Public API144                  </Link>145                </li>146              </ul>147            </div>148            <div className="text-sm">149              <p className="text-xs font-semibold uppercase tracking-wide text-[var(--muted)]">150                Legal & contact151              </p>152              <ul className="mt-2 space-y-1.5 text-[var(--ink-2)]">153                <li>154                  <Link href="/terms" className="hover:text-[var(--ink)] hover:underline">155                    Terms of Service156                  </Link>157                </li>158                <li>159                  <Link href="/privacy" className="hover:text-[var(--ink)] hover:underline">160                    Privacy Policy161                  </Link>162                </li>163                <li>164                  <a165                    href="mailto:contact@spboucher.ai"166                    className="hover:text-[var(--ink)] hover:underline"167                  >168                    contact@spboucher.ai169                  </a>170                </li>171              </ul>172            </div>173          </div>174          <div className="border-t border-[var(--grid)]">175            <p className="mx-auto max-w-5xl px-4 py-4 text-xs leading-relaxed text-[var(--muted)] sm:px-6">176              Occupation and task data from O*NET (CC BY 4.0, U.S. Department of Labor) · wages177              from BLS OEWS. This index measures task-level pressure, not certainty of job loss —178              read it as adaptation guidance. © 2026 Simon-Pierre Boucher.179            </p>180          </div>181        </footer>182      </body>183    </html>184  );185}186