@php
$tone = fn ($v) => $v >= 85 ? 'text-emerald-400' : ($v >= 70 ? 'text-lime-400' : ($v >= 55 ? 'text-amber-400' : 'text-red-400'));
$bar = fn ($v) => $v >= 85 ? 'bg-emerald-400' : ($v >= 70 ? 'bg-lime-400' : ($v >= 55 ? 'bg-amber-400' : 'bg-red-500'));
// Conseil concret par critère GEO.
$hint = [
'contenu lisible sans JS (≥400 mots)' => 'Rendre le contenu clé côté serveur (SSR / pré-rendu / fallback HTML lisible) : les LLM lisent rarement le JavaScript.',
'contenu substantiel (≥600 mots)' => 'Étoffer le contenu texte (≥ 600 mots) : les IA citent les pages riches.',
'titre + description optimisés' => 'Titre clair + meta description de 120–160 caractères.',
'hiérarchie H1 + ≥2 H2' => 'Un seul H1 + au moins 2 sous-titres H2 pour structurer.',
'JSON-LD présent' => 'Ajouter des données structurées JSON-LD (schema.org).',
'entité (Org/LocalBiz)' => 'Déclarer Organization ou LocalBusiness (nom, adresse, téléphone, logo).',
'≥ 3 types schema' => 'Décrire ≥ 3 types (ex. Organization + WebSite + BreadcrumbList).',
'schema Article/FAQ' => 'Structurer les contenus en Article / FAQPage (facilement citables).',
'llms.txt' => 'Publier un /llms.txt qui résume le site et guide les IA.',
'tous les bots IA autorisés' => 'Autoriser GPTBot, ClaudeBot, PerplexityBot, Google-Extended… dans robots.txt.',
'sameAs (réseaux)' => 'Lister tes profils (LinkedIn, X…) via "sameAs" dans le JSON-LD.',
'auteur + date visibles' => 'Afficher auteur + date de publication/màj (crédibilité pour la citation IA).',
];
$avg = count($sites) ? (int) round(collect($sites)->avg(fn ($s) => $s['geo']['score'] ?? 0)) : null;
// Regroupement des critères GEO en 3 axes → barres de progression.
$cats = [
'Contenu' => ['contenu lisible sans JS (≥400 mots)', 'contenu substantiel (≥600 mots)', 'titre + description optimisés', 'hiérarchie H1 + ≥2 H2'],
'Données structurées' => ['JSON-LD présent', 'entité (Org/LocalBiz)', '≥ 3 types schema', 'schema Article/FAQ'],
'Accès & citation IA' => ['llms.txt', 'tous les bots IA autorisés', 'sameAs (réseaux)', 'auteur + date visibles'],
];
$catScore = function (array $checks, array $labels) {
$s = 0; $g = 0;
foreach ($checks as $c) {
if (in_array($c['label'], $labels, true)) { $s += $c['w']; if ($c['ok']) $g += $c['w']; }
}
return $s ? (int) round($g / $s * 100) : 0;
};
@endphp
HubPilotage
@include('partials.nav', ['active' => 'geo'])
@if($avg !== null)
GEO moyen {{ $avg }} /100 @endif
Déconnexion
GEO·IA - visibilité dans les moteurs IA
Optimisation pour les moteurs génératifs (ChatGPT, Perplexity, Gemini, Google AI…). Ce qui compte : un contenu lisible sans JavaScript , des données structurées qui décrivent ton entité, un llms.txt , et des bots IA autorisés .
@if(count($sites))
Trier
À travailler
Meilleur score
Alphabétique
Filtrer
Rendu JS
< 70
✨ Tout optimiser
↻ Tout auditer
@endif
@if(! count($sites))
Pas encore de données GEO. Lance un audit (page SEO → « ↻ Tout auditer ») depuis le hub local - les résultats se synchronisent ici.
@else
@foreach($sites as $s)
@php $g = $s['geo']; @endphp
{{ $g['score'] }}/100
rendu {{ $g['render']==='JS' ? 'JavaScript' : 'HTML' }}
@if($g['render']==='JS')
⚠ Site rendu par JavaScript : les IA lisent surtout le HTML brut. Priorité n°1 : servir le contenu clé côté serveur (SSR / pré-rendu / fallback HTML).
@endif
{{-- Barres de progression par axe GEO --}}
@foreach($cats as $catName => $labels)
@php $v = $catScore($g['checks'], $labels); @endphp
@endforeach
Bots IA : {{ $g['ai_allowed'] }}/{{ $g['ai_total'] }}
👁 Détail
📋 Prompt
⚡ Optimiser
↻ Auditer
@endforeach
{{-- Popup détail des critères --}}
@endif
@php
$geoData = collect($sites)->mapWithKeys(fn ($s) => [$s['key'] => [
'name' => $s['name'], 'url' => $s['url'], 'score' => $s['geo']['score'] ?? 0,
'spa' => ($s['geo']['render'] ?? '') === 'JS',
'fail' => collect($s['geo']['checks'] ?? [])->where('ok', false)->pluck('label')->values()->all(),
'checks' => array_map(fn ($c) => ['label' => $c['label'], 'ok' => $c['ok']], $s['geo']['checks'] ?? []),
]])->all();
@endphp