@props([ 'title' => null, 'description' => null, 'ogType' => 'website', 'jsonld' => null, // Dates de publication et de mise à jour (crédibilité pour les moteurs IA). 'publishedTime' => null, 'modifiedTime' => null, ]) @php // Zones privées ou à faible valeur d'index : jamais indexées. $noindex = request()->is('admin*', 'connexion', 'recherche', 'app*', 'partenaires/candidature'); $canonical = request()->url(); // sans query string : 3 niveaux = 1 URL canonique // Title cible 40-60 caractères : le suffixe de marque n'est ajouté que s'il tient // (og:site_name porte la marque dans tous les cas). $brandSuffix = ', SauverMonEntreprise.fr'; $pageTitle = $title ? (mb_strlen($title.$brandSuffix) <= 60 ? $title.$brandSuffix : mb_substr($title, 0, 60)) : 'SauverMonEntreprise.fr : Comprendre, anticiper, agir'; @endphp {{-- Active les animations seulement si JS est disponible (contenu visible par défaut sinon). --}} {{-- Thème AVANT le premier rendu : sombre par défaut, choix mémorisé respecté. Posé ici et non dans app.js (chargé en différé) pour éviter l'éclair de page claire au chargement. Doit rester aligné sur les défauts d'app.js. --}} {{ $pageTitle }} @if($description)@endif {{-- Favicon : logo officiel s'il est déposé (public/img/favicon.png ou logo.svg), sinon badge S de repli généré en SVG inline. --}} @php $favicon = collect(['img/favicon.png', 'img/favicon.svg', 'img/logo.svg']) ->first(fn ($chemin) => is_file(public_path($chemin))); // Le .ico n'est servi que s'il porte réellement quelque chose : un // fichier vide fait afficher une icône cassée plutôt que rien. $ico = is_file(public_path('favicon.ico')) && filesize(public_path('favicon.ico')) > 0; $apple = is_file(public_path('img/apple-touch-icon.png')); @endphp @if ($favicon) {{-- Le .ico multi-tailles répond à /favicon.ico, que les navigateurs demandent d'office ; le PNG sert aux tailles d'affichage modernes. --}} @if ($ico) @endif @else S') }}"> @endif {{-- Open Graph / Twitter --}} @if($description)@endif {{-- Auteur et dates de l'article : signaux de fraîcheur et de paternité repris par les moteurs IA. --}} @if($ogType === 'article') @if($publishedTime)@endif @if($modifiedTime)@endif @endif {{-- Données structurées spécifiques à la page. Deux voies équivalentes : le slot pour les vues classiques, la pile pour les composants Livewire pleine page, qui ne peuvent pas remplir un slot. --}} {{ $jsonld ?? '' }} @stack('jsonld') {{-- Identité globale : Organization + WebSite sur chaque page indexable. Les nœuds portent des @id stables : les graphes de page (Article, BreadcrumbList, FAQPage) s'y rattachent sans doublon. --}} {{-- Sauf sur l'accueil, qui décrit l'éditeur en version enrichie : un seul nœud Organization par page (invariant testé). --}} @unless ($noindex || request()->routeIs('home')) @endunless {{-- Identité globale pour les moteurs (classiques et IA) : l'éditeur et le site sur CHAQUE page, en plus des nœuds propres à la page. L'accueil porte déjà une version enrichie (founder) : on ne double pas. --}} @unless (request()->routeIs('home')) @endunless {{-- Mesure d'audience. Jamais en local, jamais sur les pages privées. Umami (sans cookie, à privilégier) : UMAMI_URL + UMAMI_ID dans .env. Google Analytics 4 : GA_MEASUREMENT_ID dans .env. --}} @unless (app()->environment('local') || $noindex) @if (config('services.umami.url') && config('services.umami.id')) @endif @if (config('services.ga4.id')) @endif @endunless @livewireStyles @vite(['resources/css/app.css', 'resources/js/app.js']) Aller au contenu {{-- Avertissement d'entrée (hors espaces privés et pages d'authentification). --}} @unless (request()->is('admin*', 'espace*', 'connexion*', 'inscription*')) @endunless {{-- En-tête --}}
@php // Menu principal : deux sous-menus (comprendre / agir) et deux // entrées directes. Le comparateur garde sa place propre : // c'est l'outil signature du site. // Le slogan du site fait la structure : Anticiper, Comprendre, Agir. $sousMenus = [ 'Anticiper' => [ ["Diagnostic d'urgence", route('urgence')], ['Comparateur des procédures', route('comparateur')], ['Simulateurs', route('simulateurs.index')], ], 'Comprendre' => [ ['Encyclopédie', route('encyclopedie.index')], ['Glossaire', route('glossaire.index')], ['Textes de loi (Livre VI)', route('livre6.index')], ], 'Agir' => [ ['Guides pratiques', route('guides.index')], ['Modèles de courriers', route('modeles.index')], ['Tuteur', route('tuteur')], ], ]; @endphp
Urgence @auth @if (auth()->user()->isDirigeant()) @endif @else @endauth {{-- Bouton de menu : en dessous de « lg », la navigation principale est masquée. Sans lui, aucune rubrique n'était atteignable depuis l'en-tête sur mobile et sur tablette. --}}
{{-- Panneau de navigation mobile --}}
{{-- Liseré tricolore sous le menu --}}
{{ $slot }}
{{-- Décharge de responsabilité, présente sur toutes les pages. Affichée 15 s après l'ouverture (app.js) ; sans JavaScript, elle reste visible d'emblée, l'information légale prime. --}}
Information générale : ce site ne délivre aucun conseil juridique personnalisé. Avant toute décision, consultez un avocat. Lire l'avertissement Trouver un avocat
{{-- Assistant conversationnel : bulle flottante sur les pages publiques. Volontairement SANS Livewire : Livewire désactive le cache navigateur de toute page qui l'embarque, or ces pages doivent rester cacheables. Le widget est statique et parle à /assistant en fetch(). --}} @unless (request()->is('admin*')) @endunless {{-- Pied de page --}} {{-- Scripts propres à une page, empilés en fin de corps pour ne pas retarder le rendu. Sans cette pile, un @push('scripts') est silencieusement perdu. --}} @stack('scripts') {{-- Livewire (et son Alpine) sur TOUTES les pages : les pop-ups du parcours et la bulle d'assistant en dépendent, même sans composant Livewire sur la page. N'affecte pas la cacheabilité (le no-store de Livewire ne s'active qu'au rendu d'un composant). --}} @livewireScripts