import type { Metadata, Viewport } from 'next'; import Script from 'next/script'; import { headers } from 'next/headers'; import { Inter, JetBrains_Mono } from 'next/font/google'; import { Toaster } from 'sonner'; import { classifyFormFactor } from '@/lib/form-factor'; import { ReactGrabViewportSync } from '@/components/dev/react-grab-viewport-sync'; import { resolveAuthShellBranding } from '@/lib/email/auth-shell-branding'; import './globals.css'; const inter = Inter({ subsets: ['latin'], variable: '--font-sans', display: 'swap', }); const jetbrainsMono = JetBrains_Mono({ subsets: ['latin'], variable: '--font-mono', display: 'swap', }); export const viewport: Viewport = { width: 'device-width', initialScale: 1, viewportFit: 'cover', themeColor: '#1e2844', }; /** * Resolve the browser tab title from the first-port `branding_app_name` * setting so a tenant's deploy sees their own brand in the title bar * (and in `Cmd+T` browser history). Falls back to a generic label when * the DB hasn't been seeded yet (e.g. fresh `pnpm dev` against an empty * database during onboarding). */ export async function generateMetadata(): Promise { const branding = await resolveAuthShellBranding(); const appName = branding?.appName?.trim() || 'CRM'; return { title: { default: appName, template: `%s | ${appName}`, }, description: `${appName} - marina management system`, appleWebApp: { capable: true, statusBarStyle: 'black-translucent', title: appName, }, icons: { icon: [ { url: '/icon-192.png', sizes: '192x192', type: 'image/png' }, { url: '/icon-512.png', sizes: '512x512', type: 'image/png' }, ], apple: '/apple-touch-icon.png', }, manifest: '/manifest.json', }; } export default async function RootLayout({ children }: { children: React.ReactNode }) { const headerList = await headers(); const formFactor = classifyFormFactor(headerList.get('user-agent')); return ( {process.env.NODE_ENV === 'development' && (