import type { Metadata, Viewport } from 'next'; import PostHogProvider from '@/components/PostHogProvider'; import AuthProvider from '@/contexts/AuthContext'; import ParlayProvider from '@/contexts/ParlayContext'; import ExplainModeProvider from '@/contexts/ExplainModeContext'; import Nav from '@/components/Nav'; import Footer from '@/components/Footer'; import AuthGate from '@/components/AuthGate'; import HashRedirect from '@/components/vyndr/HashRedirect'; import ParlayTray from '@/components/ParlayTray'; import BottomTabBar from '@/components/BottomTabBar'; import InstallPrompt from '@/components/InstallPrompt'; import PushPrompt from '@/components/PushPrompt'; import MFAPrompt from '@/components/MFAPrompt'; import MFAChallenge from '@/components/MFAChallenge'; import CookieConsent from '@/components/CookieConsent'; import SentryInit from '@/components/SentryInit'; import { LocaleProvider } from '@/contexts/LocaleContext'; import { headers } from 'next/headers'; import { LOCALE_HEADER, COUNTRY_HEADER, isLocale, DEFAULT_LOCALE, LOCALE_META } from '@/lib/locales'; import './globals.css'; export const metadata: Metadata = { metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'https://vyndr.app'), title: { default: 'VYNDR — Sports Prop Intelligence', template: '%s · VYNDR', }, description: "Grade your props across every sport with intelligence the books don't want you to have. NBA, MLB, WNBA, and soccer today — NFL and more through 2026. World Cup 2026 intelligence: xG regression, altitude, referee, penalty taker. Built in Detroit.", applicationName: 'VYNDR', authors: [{ name: 'VYNDR', url: 'https://vyndr.app' }], manifest: '/manifest.json', keywords: [ 'sports prop grading', 'NBA prop bet analysis', 'MLB prop intelligence', 'WNBA prop grading', 'soccer prop intelligence', 'World Cup 2026 props', 'xG regression analysis', 'parlay correlation analysis', 'prop betting tools', ], openGraph: { title: "VYNDR — Intelligence the books don't want you to have", description: 'Read player props with Bayesian intelligence. See the factors. Know the kill conditions. Take the edge back.', url: 'https://vyndr.app', siteName: 'VYNDR', images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'VYNDR — Sports Prop Intelligence' }], type: 'website', }, twitter: { card: 'summary_large_image', title: 'VYNDR', description: 'The books have every advantage. We built this to give it back.', images: ['/og-image.png'], creator: '@getvyndr', }, icons: { icon: [ { url: '/favicon.svg', type: 'image/svg+xml' }, { url: '/favicon.ico', sizes: '32x32' }, { url: '/favicon-32.png', sizes: '32x32', type: 'image/png' }, { url: '/favicon-16.png', sizes: '16x16', type: 'image/png' }, ], apple: '/apple-touch-icon.png', }, appleWebApp: { capable: true, statusBarStyle: 'black-translucent', title: 'VYNDR', }, robots: { index: true, follow: true, googleBot: { index: true, follow: true, 'max-image-preview': 'large', 'max-snippet': -1, }, }, }; export const viewport: Viewport = { themeColor: '#06060B', width: 'device-width', initialScale: 1, maximumScale: 5, }; export default async function RootLayout({ children }: { children: React.ReactNode }) { // Session 12 — resolve locale from the middleware-stamped request // header so server components render with the right translations // and the dir attribute is set before paint (no FOUC of // mis-directional text). const hdrs = await headers(); const localeHeader = hdrs.get(LOCALE_HEADER); const locale = isLocale(localeHeader) ? localeHeader : DEFAULT_LOCALE; const dir = LOCALE_META[locale].dir; // Session 13 — country from CF-IPCountry (set by middleware). // Empty string when traffic bypasses Cloudflare (local dev, direct // origin hits). The Africa-tier gate degrades closed on empty. const country = hdrs.get(COUNTRY_HEADER) || ''; return ( {/* VYNDR 2.0 (§2): Inter for chrome/UI, JetBrains Mono for ALL data. IBM Plex Mono + Instrument Sans kept while pages migrate session-by-session. */}