@php($activeTheme = app()->bound('active_theme') ? app('active_theme') : 'default')
@php($activeThemeColor = app()->bound('active_theme_color') ? app('active_theme_color') : 'neutral')
@php($siteName = site_name())
@php($smoothNavigation = (bool) setting('smooth_navigation', true))
{{-- Favicon resolution chain (most → least specific):
1. theme_favicon_{slug} — admin-uploaded per-theme favicon
2. site_favicon — admin-uploaded global favicon
3. /favicon.png — bundled fallback that ships with the app
The first present value wins; that file is used as the primary , the apple-touch-icon, and the legacy shortcut icon. --}}
@php($themeFavicon = (string) (setting('theme_favicon_'.$activeTheme) ?: ''))
@php($siteFavicon = (string) (setting('site_favicon') ?: ''))
@php($favicon = $themeFavicon ?: ($siteFavicon ?: 'favicon.png'))
@php($themeColor = '#0f172a')
@php($pageProps = (array) ($page['props'] ?? []))
@php($og = is_array($pageProps['og'] ?? null) ? $pageProps['og'] : null)
@php($jsonLd = $pageProps['jsonLd'] ?? null)
@php($hreflang = is_array($pageProps['hreflang'] ?? null) ? $pageProps['hreflang'] : [])
@php($shouldNoindex = (bool) ($og['noindex'] ?? false))
@php($twitterCard = $og['twitter_card'] ?? (! empty($og['image']) ? 'summary_large_image' : 'summary'))
@php($activeDir = isset($pageProps['locale']['isRtl']) ? (($pageProps['locale']['isRtl'] ?? false) ? 'rtl' : 'ltr') : 'ltr')
{{-- ─── SEO meta (server-rendered) ─────────────────────────────────────
The OG / Twitter Card / canonical / robots / JSON-LD / hreflang
payload is built by SocialMetaService + JsonLdService in PHP and
shipped via Inertia props. We render it from Blade (rather than
React's ) so non-JS social-share crawlers — Twitter card
validator, Facebook scraper (Messenger/WhatsApp), Discord embed
bot, Slack/LinkedIn/Telegram preview fetchers — see the full
preview payload in the initial HTML response, before any JS
executes. Googlebot also reads it directly without waiting for
hydration. After client-side Inertia navigation the head goes
"stale" relative to the new URL, but nothing meaningful reads
head meta post-hydration (crawlers always fetch fresh, browsers
track which Inertia auto-updates, the Web Share API
uses document.title not OG). --}}
@if($og)
@if(! empty($og['description']))
@endif
@if(! empty($og['url']))
@endif
@if($shouldNoindex)
{{-- Match the X-Robots-Tag header AddNoindexHeader emits and
the meta tag on auth pages — `nofollow` is stronger than
`follow` and the three signal sources should agree. --}}
@endif
{{-- Open Graph --}}
@if(! empty($og['type']))
@endif
@if(! empty($og['site_name']))
@endif
@if(! empty($og['title']))
@endif
@if(! empty($og['description']))
@endif
@if(! empty($og['url']))
@endif
@if(! empty($og['locale']))
@endif
@if(! empty($og['image']))
@if(! empty($og['image_width']))
@endif
@if(! empty($og['image_height']))
@endif
@if(! empty($og['image_alt']))
@endif
@if(! empty($og['image_type']))
@endif
@endif
{{-- Twitter Cards --}}
@if(! empty($og['title']))
@endif
@if(! empty($og['description']))
@endif
@if(! empty($og['image']))
@if(! empty($og['image_alt']))
@endif
@endif
@endif
{{-- hreflang alternates — suppressed on noindexed pages because
Google's guidance is that hreflang on a noindexed URL is ignored
and emitting an asymmetric alternate set creates a confusing
signal. --}}
@if(! $shouldNoindex && ! empty($hreflang))
@foreach($hreflang as $alt)
@if(is_array($alt) && ! empty($alt['code']) && ! empty($alt['url']))
@endif
@endforeach
@php($defaultAlt = collect($hreflang)->firstWhere('is_default', true))
@if($defaultAlt && ! empty($defaultAlt['url']))
@endif
@endif
{{-- JSON-LD structured data. JSON_HEX_TAG escapes < and > to the
Unicode hex sequences < and >, so any ""
embedded in data becomes "" and cannot
terminate the enclosing script element — defense-in-depth
against user content (game titles, profile bios) breaking
out. JSON_UNESCAPED_SLASHES is safe because the < guard
already neutralizes every closing-tag sequence. --}}
@if($jsonLd)
@endif
{{-- Server-rendered title — gives non-JS bots a per-page title rather
than the site-name placeholder. Inertia's client manages the
`` element on subsequent client-side navigation
(the marker attribute is what Inertia tracks; the initial
content is overwritten on hydration). --}}
@if($og && ! empty($og['title']))
{{ $og['title'] }}
@else
{{ $siteName }}
@endif
@routes
@viteReactRefresh
@vite(['resources/css/app.css', 'resources/js/app.tsx'])
@inertiaHead
@inertia