import type { Metadata, Viewport } from 'next'; import { Schibsted_Grotesk, Literata } from 'next/font/google'; import Script from 'next/script'; import { Navigation } from '@/components/Navigation'; import { Footer } from '@/components/Footer'; import { ComparisonToast } from '@/components/ComparisonToast'; import { ComparisonProvider } from '@/context/ComparisonProvider'; import './globals.css'; // Schibsted Grotesk carries the whole interface — headings, labels, and every // figure. It was drawn for news brands, so its tabular numerals hold a column // properly, which matters on a page that is mostly percentages. const schibsted = Schibsted_Grotesk({ subsets: ['latin'], weight: ['400', '500', '600', '700'], variable: '--font-schibsted', display: 'swap', }); // Literata is for prose only — the admissions guide, methodology notes, the // editorial sections. It never touches a number or a control. const literata = Literata({ subsets: ['latin'], weight: ['400', '600'], style: ['normal', 'italic'], variable: '--font-literata', display: 'swap', }); export const viewport: Viewport = { width: 'device-width', initialScale: 1, // viewport-fit=cover lets us paint behind the notch / Dynamic Island so // env(safe-area-inset-*) values resolve to real numbers on iPhone. viewportFit: 'cover', // These must match --bg-primary in globals.css for each theme, or the // browser chrome and the page disagree at the top edge of the screen. themeColor: [ { media: '(prefers-color-scheme: light)', color: '#f2f1ed' }, { media: '(prefers-color-scheme: dark)', color: '#10151b' }, ], }; export const metadata: Metadata = { appleWebApp: { capable: true, title: 'SchoolCompare', statusBarStyle: 'default', }, title: { default: 'SchoolCompare | Compare School Performance', template: '%s | SchoolCompare', }, description: 'Compare primary and secondary school SATs and GCSE performance across England', keywords: 'school comparison, KS2 results, KS4 results, primary school, secondary school, England schools, SATs results, GCSE results', authors: [{ name: 'SchoolCompare' }], manifest: '/manifest.json', // No `icons` key on purpose: setting it here would override the file // conventions. app/icon.svg and app/apple-icon.tsx are the source, and // app/opengraph-image.tsx supplies og:image and twitter:image. metadataBase: new URL('https://schoolcompare.co.uk'), openGraph: { type: 'website', title: 'SchoolCompare | Compare School Performance', description: 'Compare primary and secondary school SATs and GCSE performance across England', url: 'https://schoolcompare.co.uk', siteName: 'SchoolCompare', }, twitter: { // summary_large_image now that there is an image worth showing. card: 'summary_large_image', title: 'SchoolCompare | Compare School Performance', description: 'Compare primary and secondary school SATs and GCSE performance across England', }, }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( // The font variable classes must sit on , not
. globals.css // declares --font-display/--font-ui on :root as var(--font-schibsted), // and a custom property's var() references resolve on the element that // declares it. With the classes on , --font-schibsted was undefined // at :root, so --font-display computed to the guaranteed-invalid value and // every font-family that referenced it silently fell back — the whole site // rendered in Times. {/* data-domains: the tracker only fires on the production hostnames, so staging (same image, different host) never pollutes Umami */} {/* Heatmap / session recorder — same data-domains guard so staging (same image, different host) never feeds the heatmap. */}