=== WP Local SEO Studio — Local SEO, Schema & AI-Assisted Editing ===
Contributors: emakesolution
Tags: seo, local-seo, schema, json-ld, sitemap, redirects, hreflang, core-web-vitals, faq, localbusiness
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 8.0
Stable tag: 2.10.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

WordPress SEO plugin focused on local businesses: LocalBusiness schema, sitemap, redirects, 404 monitor, Core Web Vitals — with an optional AI assistant for meta descriptions and alt text.

== Description ==

**Compatibilité** :

* WordPress : 6.0 minimum, testé jusqu'à 6.8 (la version en cours au moment du release)
* PHP : 8.0 minimum, testé jusqu'à 8.3 (recommandé : 8.2 ou 8.3 pour la perf)
* MySQL : 5.7 minimum / MariaDB 10.3 minimum (pour la clause `cursor` réservée)
* HTTPS : recommandé (les API IA refusent les appels HTTP en clair, et c'est un signal de classement Google)
* Multisite : compatible (les options sont migrées par site)


**WP Local SEO Studio** is a freemium SEO plugin for WordPress, built around two ideas: do the SEO fundamentals well, and use AI as a writing assistant when it actually saves time. It is opinionated about Local SEO (LocalBusiness schema, NAP consistency, multi-location landing pages in Pro).

There is no magic shortcut to AI Overviews or to Perplexity citations. Google's own [AI Search guidance](https://developers.google.com/search/blog/2026/05/ai-features-and-your-website) confirms that AI features pull from the regular index, with the regular ranking signals. WP Local SEO Studio is built on that premise: solid technical SEO, honest content, and AI used as a productivity tool — not as a workaround.

= What WP Local SEO Studio does well =

* **Local SEO**: LocalBusiness JSON-LD, NAP guard, opening hours, geo-coordinates. Multi-location is a Pro upgrade.
* **Technical SEO basics**: XML sitemap, robots.txt editor, hreflang (WPML / Polylang / Multisite aware), canonical URLs, indexing audit.
* **Schema.org**: LocalBusiness, Organization, WebSite, BreadcrumbList, FAQPage, cross-referenced via `@id` for entity disambiguation.
* **On-page SEO**: Title, Meta description, Open Graph, Twitter Cards. Per-post overrides in both Gutenberg and the Classic editor.
* **Redirect manager + 404 monitor**: never lose a visitor to a broken link. AI can suggest a redirect target if you have it connected.
* **Core Web Vitals widget**: PageSpeed Insights integration (works keyless for low volume).
* **Site audit & dashboard**: 0–100 health score with per-category breakdown and realistic timelines for each fix.
* **Page-builder aware content analyzer**: reads Elementor, Bricks, Divi, WPBakery and Beaver Builder content, not just `post_content`.
* **No-conflict mode**: cohabits with Yoast / Rank Math / SEOPress / AIOSEO without duplicating meta tags.

= How the AI features are used =

The AI features are optional and treated as a writing assistant, not a ranking trick. If you connect a provider (Anthropic Claude, OpenAI, Google Gemini or Perplexity), you can:

* Draft meta descriptions from the article content.
* Generate Q&A pairs for the FAQ block.
* Get redirect suggestions for 404s by matching against existing URLs.
* Rerank internal-link candidates by semantic relevance.
* Rewrite intros to be clearer and more direct (the kind of content Google's guidance asks for: specific, non-commodity).

Bring your own key. Calls only happen when you explicitly trigger them. Keys are encrypted at rest with AES-256-CBC using your site's `AUTH_KEY` + `AUTH_SALT`.

= Core (free) features =

* **Onboarding wizard** (4 guided steps).
* **On-page SEO**: Title, Meta description, Open Graph, Twitter Cards.
* **Schema.org JSON-LD**: LocalBusiness, Organization, WebSite, BreadcrumbList, FAQPage.
* **XML sitemap** (extends core wp-sitemap).
* **Robots.txt editor**.
* **Live content analyzer**: Flesch readability + keyword density + 8 placement checks.
* **Site audit & dashboard**: 0–100 health score with per-category breakdown.
* **Core Web Vitals widget**: PageSpeed Insights integration.
* **Redirect manager + 404 monitor** with optional AI-suggested redirects.
* **Internal-link suggestions** (semantic + optional AI rerank).
* **FAQ Gutenberg block** with automatic FAQPage schema.
* **Hreflang**: auto-detects WPML / Polylang / Multisite.
* **WP-CLI**: `wp wp-local-seo-studio audit`, `bulk start/run`, `cwv refresh`.

= Pro features =

* Multi-location: manage multiple business profiles with dynamic landing pages.
* Vision-powered image alt: analyses the actual pixels, not just the filename.
* Google Business Profile review sync and KML feed.
* Advanced schema: Product, Review, Event, HowTo, Article, Recipe.
* Bulk AI runs without batch caps.
* Priority support.

[Learn more about WP Local SEO Studio Pro →](https://emakesolution.com/wp-local-seo-studio-pro)

= What this plugin will not do =

* It will not get you into AI Overviews or Perplexity citations by toggling a checkbox. Those depend on the quality of your content and on Google's index — there is no special channel.
* It will not generate dozens of near-duplicate landing pages. That is content abuse and Google penalises it.
* It will not write a `llms.txt` file. The major AI providers do not fetch it, and Google has confirmed it is not used.

= Privacy =

* AI calls only happen when you explicitly trigger them. Content is sent to the provider you configured (Anthropic, OpenAI, Google, Perplexity).
* API keys are encrypted at rest with AES-256-CBC using your site's `AUTH_KEY` + `AUTH_SALT`.
* No analytics or telemetry. Period.

== Installation ==

1. Upload `wp-local-seo-studio` to `/wp-content/plugins/` (or install from Plugins → Add new).
2. Activate the plugin.
3. Follow the on-screen wizard.
4. (Optional) Connect an AI provider in WP Local SEO Studio → Settings → AI Providers.

== Frequently Asked Questions ==

= Will WP Local SEO Studio conflict with my existing SEO plugin? =

No. If Yoast, Rank Math, SEOPress or All in One SEO are active, WP Local SEO Studio automatically steps out of the way on Title/Description/OG/Twitter to avoid duplicate meta tags. Schema, sitemap, redirects, audit and Local SEO features stay active. Force the take-over with `add_filter( 'wp_lseo_force_meta_output', '__return_true' )`.

= Do I need an AI API key to use the plugin? =

No. Every Core feature works without AI. The AI buttons in the editor sidebar and the bulk runner are disabled until you connect a provider in Settings → AI Providers.

= Will this plugin help me show up in AI Overviews / Perplexity? =

Indirectly, like any solid SEO work would. Google's official guidance is that AI features use the regular index and the regular ranking. WP Local SEO Studio helps you get the fundamentals right (indexable pages, schema, internal links, content quality) which is what every search surface — classic or AI — rewards. There is no separate optimisation path, and any plugin that claims one is selling smoke.

= Does the plugin work with Elementor / Bricks / Divi? =

Yes. The content analyser reads from `_elementor_data`, `_bricks_page_content_2`, `_fl_builder_data` and applies `do_shortcode()` for Divi / WPBakery, so your actual rendered text is what gets scored — not the empty `post_content` shell.

= Where are my API keys stored? =

In the WordPress `wp_options` table, encrypted with AES-256-CBC. The encryption key is derived from your site's `AUTH_KEY` and `AUTH_SALT` (defined in `wp-config.php`).

= How long until I see SEO results? =

The dashboard tells you per fix:

* Title / meta description rewrites: 1–4 weeks for re-crawl.
* New schema markup: 2–6 weeks for rich-results pickup.
* Local Pack (with GBP optimised): 4–8 weeks.
* Knowledge Panel: 4–12 weeks.
* Core Web Vitals: re-evaluated after 28 days of CrUX data.

= Should I expect a traffic boost from AI search? =

Be cautious here. Independent studies (Ahrefs, Pew Research) show that when an AI Overview appears above the organic results, click-through to the source site can drop by 40–60% — even if the source is cited. Strong content and clear schema help you *be* the cited source, but the overall traffic outcome depends on the query mix.

= Can I export my data? =

Yes. WP Local SEO Studio hooks WordPress's Personal Data Exporter (Tools → Export Personal Data), and uninstalling the plugin drops all custom tables, options and cron events.

== Screenshots ==

1. SEO Health Dashboard with the global score gauge and action plan.
2. Onboarding wizard, step 2 (NAP + geo-coordinates).
3. Gutenberg sidebar with AI buttons and live analysis.
4. Redirect manager + 404 log.
5. Bulk AI runner with progress bars.
6. Tutorial page — "Learn SEO in 30 minutes".

== Changelog ==

= 2.10.1 =
* Sidebar Gutenberg : les 14 labels de checks ("Title contains keyword", "Density in range", etc.) étaient en anglais générés par un `humanise()` JS qui faisait juste `replace('_',' ')`. Maintenant chaque check a son label traduit côté PHP et passé via `wp_add_inline_script`. La JS fait un lookup et tombe en fallback `humanise()` seulement si une clé inconnue.
* Sidebar : libellé de difficulté Flesch ("difficult", "very-easy", etc.) maintenant traduit en français.
* Sidebar : ligne densité "0% — 0 hits in 1572 words" reformatée en "0 % · 0 occurrences dans 1572 mots".

= 2.10.0 =
* **Colonne SEO admin transformée en coach** : la pastille de score est désormais cliquable (ouvre l'éditeur de l'article directement) et un tooltip détaillé apparaît au survol avec : le score, ses 3 sous-scores pondérés (mot-clé 40 % + lisibilité 30 % + structurel 30 %), le nombre de checks structurels passés, et **les 3 recommandations actionables prioritaires** pour faire monter le score (« Inclure le mot-clé dans le Title +18 pts », « Ajuster la densité entre 0,5 et 2,5 % », etc.). L'utilisateur ne se demande plus pourquoi il est à 35 : il voit exactement ce qui manque.
* Le breakdown du score (sous-scores + recos) est stocké dans une nouvelle méta `_wp_lseo_score_breakdown` à chaque analyse. Lecture quasi gratuite à l'affichage de la colonne (un seul `get_post_meta`), pas de re-calcul à chaque pageload admin.
* Hover effect sur le badge (lift de 1px + box-shadow) pour signaler que c'est cliquable.

= 2.9.1 =
* **Correctif critique** : la tâche Bulk "Mots-clés cibles manquants" pouvait afficher "44/44 (100 %) terminé" sans avoir écrit un seul mot-clé en base. Cause : le LLM renvoie parfois le JSON dans un fence markdown (` ```json ... ``` `), précédé de texte, ou en simple chaîne au lieu de l'objet attendu. `json_decode` retournait `null`, on `return`ait silencieusement, mais le compteur `processed` s'incrémentait quand même. Conséquence : job marqué succès, métas vides, audit toujours en CRITIQUE. Fix : nouveau parser robuste qui essaye 5 stratégies d'extraction (decode direct, strip code-fence, regex sur fragment JSON, regex sur `"primary":"..."`, fallback ligne unique), et **throw une exception** si rien d'utilisable n'est extrait (le job compte alors `failed` au lieu de `succeeded` et l'utilisateur voit la réalité). Relancez la tâche Bulk Mot-clé cible après upgrade, les 44 articles repassent en candidats puisque la méta n'avait pas été écrite.

= 2.9.0 =
* **Précurseur sur le guide officiel Google « AI features and your website »** (publié le 15 mai 2026) :
* **Nouveau schéma ImageObject** sur les vues singulaires : émission automatique d'un JSON-LD pour la featured image de chaque article / page, avec URL, dimensions, alt et caption. Google : *« Images and videos can appear directly in AI Overviews, making them a visibility surface in their own right. »*
* **Nouveau schéma VideoObject** : détection automatique des embeds YouTube et Vimeo dans le contenu (page-builder aware) et émission d'un VideoObject par vidéo avec embedUrl, contentUrl et thumbnail. Cap à 5 vidéos par page pour éviter le spam sur les pages galerie. Désactivable via filter `wp_lseo_enable_video_schema`.
* **Prompts IA renforcés sur l'angle distinctif** : meta description et Title SEO incluent désormais une directive explicite « DISTINCTIVE: surface what makes THIS page different from a generic competitor ». Aligné avec Google qui souligne que les pages qui ressortent dans les AI Overviews sont celles avec un angle non-commodity ancré dans l'expertise réelle.

= 2.8.1 =
* **Wording corrigé** : la description des findings audit "Articles sans X" disait "44 articles publiés sur 44 (100 %)" qui se lisait comme "100 % optimisé" alors que c'est en fait "100 % de votre catalogue à corriger". Reformulé en "44 articles sur 44 à corriger — soit 100 % de votre catalogue" pour lever l'ambiguïté entre score de complétion et taux d'incidence du problème.
* UX : contours `:focus` orange disgracieux qui restaient autour des `<summary>` (« Comment corriger ») et des boutons après un clic souris. Cause : focus du navigateur par défaut. Fix : `:focus:not(:focus-visible)` enlève le contour au clic souris, `:focus-visible` (clavier uniquement) reçoit un ring subtil amber accessibility-compliant.

= 2.8.0 =
* **Nouvelle tâche Bulk : "Analyser le contenu"** (5e card). Calcule en lot le score SEO de chaque article / page publié sans en avoir un. Analyse 100% locale (Flesch + densité mots-clés + 8 contrôles de placement), zéro tokens IA, ~5 ms par contenu. Batch size augmentable jusqu'à 50 par tick (vs 20 pour les tâches IA). Remplit en quelques secondes la colonne "SEO" de la liste Articles / Pages sans avoir à ouvrir chaque post dans Gutenberg. Couplée au re-audit auto de 2.7.4, votre Dashboard et votre liste de contenus sont à jour en un seul clic.

= 2.7.5 =
* Colonne "SEO" dans la liste Articles / Pages : affichait un badge rouge "0" pour les contenus jamais analysés, ce qui était trompeur. Désormais ces lignes montrent un tiret discret "—" avec tooltip "Contenu jamais analysé. Ouvrez l'article dans Gutenberg avec la sidebar plugin pour calculer son score." Le badge rouge n'apparaît plus que pour un vrai score < 50.

= 2.7.4 =
* **Re-audit auto après un Bulk** : quand un job IA en masse se termine (44 méta-descriptions générées, 255 alts écrits, etc.), le cache audit est invalidé ET un recalcul est programmé en arrière-plan via WP-Cron + loopback (5s plus tard). Conséquence : en revenant sur le Dashboard, vous trouvez un score à jour sans avoir à cliquer "Relancer l'audit". Avant, il fallait attendre 1h (TTL du cache) ou cliquer manuellement. Nouveau hook public `wp_lseo_bulk_job_completed` que le Pro pourra écouter.

= 2.7.3 =
* **Correctif robots.txt** : la directive `Sitemap:` était dupliquée car WordPress core l'ajoute déjà automatiquement (depuis 5.5) et notre plugin l'ajoutait en plus. On vérifie maintenant la présence avant d'ajouter.
* Headers de compatibilité mis à jour : `Tested up to: 6.8`, ajout de la section « Compatibilité » dans la description (WP min/max, PHP min/max, MySQL, HTTPS, multisite).

= 2.7.2 =
* **Alias sitemap** : 3 URLs supplémentaires redirigent en 301 vers `/wp-sitemap.xml` (le sitemap natif WP core). `/sitemap.xml` (standard W3C / sitemaps.org), `/sitemap_index.xml` (compat migration Yoast), `/sitemap-index.xml` (compat AIOSEO). Plus aucun crawler ne tombe dans le vide, et les migrations depuis d'autres plugins SEO préservent l'historique de crawl Search Console. Désactivable via filter `wp_lseo_enable_sitemap_aliases`.

= 2.7.1 =
* **Pass espace global** : padding des cards 26/28 → 32/36, padding `.wplss-app` 24/28 → 36/40, margin-bottom du topbar 28 → 40, espace entre cards consécutives 18 → 28, headings de card un peu plus gros (16 → 17 px), line-height généralisée 1.55-1.65 pour les leads. Le plugin respire enfin.
* **Card "déjà optimisé" individuelle sur la page Bulk** : quand un compteur est à 0 (ex. : 0 méta-descriptions manquantes), la card prend un look distinct (tint vert subtil + badge ✓ vert à côté du titre + message "Tous vos articles ont une méta-description" au lieu du lanceur). Plus de confusion avec un bouton Démarrer grisé sans contexte.
* Refactor : les 4 cards de la grille Bulk passées en data-driven (boucle PHP sur un tableau) au lieu de 4 blocs HTML quasi-identiques. ~150 lignes de redondance en moins, traductions des labels FR-isées au passage (Fournisseur, Défaut, Taille du lot, Démarrer).

= 2.7.0 =
* **Mobile responsive complet** : Dashboard, Bulk, Réglages, Licence, Tutoriel, Wizard. Topbars qui stack en vertical, gauges réduites, CWV en colonne unique, boutons hauts de 44px pour les pouces, tables avec scroll horizontal, form-tables avec labels au-dessus des inputs. Pensé pour les dirigeants en déplacement qui ouvrent l'admin sur smartphone.
* **Aide contextuelle WP-native** : le bouton "Aide" en haut à droite des écrans WP est désormais pré-rempli avec 3 onglets — Vue d'ensemble (par où démarrer), Raccourcis (liens vers toutes les pages plugin), Cache & rafraîchissement (quand le score audit se met à jour). Sidebar avec liens documentation + Pro.
* **Titres de page admin propres** : "WP Local SEO Studio · Tableau de bord — Services Acro Alsace" au lieu du générique "Réglages — Site". Pratique avec 10 onglets WP ouverts.
* Favicon plugin sur le wizard standalone (la mark amber pin) + meta `application-name` pour l'épinglage iOS / Android.

= 2.6.0 =
* **Correctif critique** : les jobs Bulk restaient bloqués au statut "running" indéfiniment. Cause : le mot `cursor` (réservé MySQL 8.0+) n'était pas backtické dans l'UPDATE de `record_progress()`. L'UPDATE plantait silencieusement, le compteur `processed` n'avançait jamais, donc `processed >= total` n'était jamais vrai et `mark_completed` ne tirait jamais. Les meta_descriptions étaient bien écrites côté wp_postmeta, mais le tracking était cassé. Backticks ajoutés (`cursor` = %d). Les jobs déjà bloqués se complèteront automatiquement au prochain tick après upgrade. Même bug racine que 2.2.1 (CREATE TABLE) mais sur le UPDATE cette fois.
* **Sprint 3 audit UX livré** :
* Bouton "Revoir le tour guidé" en haut de la page Tutoriel. Reset le user_meta + localStorage et redirige vers le Dashboard pour relancer le tour. Nouveau endpoint REST `/tour/reset`.
* Carte "Par où commencer" intelligente sur le Dashboard, juste avant le plan d'action. Pointe vers l'action prioritaire selon l'état : terminer le wizard si non fait → connecter une clé IA si absente → lancer le bulk meta si 10+ articles à traiter → premier item critique du plan sinon. Avec CTA direct.
* Badge de notification sur le sous-menu Dashboard maintenant compte UNIQUEMENT les critiques (avant : tous les actionables). Plus de "12" rouge alarmant quand il s'agit de conseils.
* Tooltips explicatifs au survol des métriques LCP / INP / CLS du widget Core Web Vitals (définitions + cibles Google).
* Estimation totale "X leçons · ~Y minutes au total" en haut de la page Tutoriel (calculée auto à partir des `time` de chaque leçon).

= 2.5.0 =
* **Sprint 2 audit UX** — 4 améliorations majeures :
* Couleurs status harmonisées : la pill "en cours" passe en cyan (--wplss-info) au lieu d'amber pour ne plus se confondre avec le brand. Ajout d'une animation de pulse douce pour signaler "il se passe quelque chose en arrière-plan".
* Feedback "Démarrer" : bouton qui affiche "Création…" pendant l'appel REST, flash success flottant en bas à droite "Tâche #N créée", auto-scroll vers la card Tâches avec highlight pulse 2s, et flash error si la création échoue (plus de `alert()` natif moche). Statuts de pills traduits en direct par la JS.
* Empty state pédagogique sur la page IA en masse : quand vos 4 compteurs sont à 0 (rien à corriger), grosse card "Tout est en ordre côté contenu" avec coche verte, message rassurant et CTA retour Dashboard. La grille des lanceurs se masque automatiquement.
* Récap post-wizard : à la première arrivée sur le Dashboard après "Finish setup", card amber "🎉 Configuration terminée !" qui liste concrètement ce que le plugin fait déjà (balises, schéma LocalBusiness pour votre commerce, sitemap, OG, surveillance 404). Affichée une seule fois grâce au query param `?wizard=done`.

= 2.4.0 =
* **Page Réglages restylée en thème sombre** : cohérence visuelle complète avec le reste du plugin. Topbar avec marque + bouton retour Dashboard, onglets en pills amber, form-tables transformées en cards sombres, bouton "Enregistrer" en accent amber. Surcharge non destructive du `.nav-tab-wrapper` et `.form-table` natifs WP — la structure PHP reste inchangée.
* Page Bulk : table "Tâches" qui apparaissait sans style (blanc sur blanc) car `.wplss-table` n'était défini que dans `redirects.css`. Règles remontées dans `theme.css` (loaded partout) pour que toutes les pages aient des tables stylées identiquement.
* i18n : 15 chaînes Bulk encore en anglais traduites (statuts "running" / "queued" / "completed" / "cancelled" / "failed", noms de tâches "meta_description" / "image_alt" / "focus_keyword" / "seo_title", "Missing focus keywords", "Missing SEO titles", "Approx. cost: ~%d tokens per post.", etc.).

= 2.3.2 =
* UX : carte « Tâches en cours » remontée en haut de la page IA en masse, juste sous le Budget IA. Plus besoin de scroller pour voir l'avancement d'un job lancé. Masquée si aucun job historique pour ne pas occuper d'espace inutile.

= 2.3.1 =
* UX : pastille "Mis à jour il y a X minutes" en haut du Dashboard pour signaler la fraîcheur du score audit. Vert si < 1h (frais), orange si plus vieux (cache expiré, sera recalculé à la prochaine visite). Tooltip explique le mécanisme de cache et invite à cliquer « Relancer l'audit » pour forcer un recalcul. Site_Audit enregistre maintenant son heure de calcul dans l'option `wp_lseo_audit_computed_at`.

= 2.3.0 =
* **Nouveau** : tutoriel cron intégré dans la page IA en masse. Card "Configurer un vrai cron système (recommandé)" avec indicateur en temps réel "Dernier tick il y a X minutes", code couleur (vert / orange / gris), et instructions pas à pas par hébergeur : OVH cPanel/Manager, Plesk, cPanel générique, VPS/SSH (avec WP-CLI alternative) et services externes (cron-job.org). Inclut le bout de code pour désactiver WP-Cron natif et la commande exacte à coller.
* Le runner Bulk enregistre un heartbeat à chaque tick (option `wp_lseo_bulk_last_tick`), utilisé par la card pour détecter si le cron tourne ou si quelque chose bloque.

= 2.2.3 =
* **Bulk indépendant du trafic** : le runner Bulk déclenche maintenant lui-même son prochain tick via une requête loopback non bloquante vers `wp-cron.php`. Pattern reproduit d'Action Scheduler / WooCommerce. Avant : sur un site à faible trafic, un job de 44 articles pouvait rester à 0% pendant des heures en attendant qu'un visiteur passe sur le site pour déclencher WP-Cron. Maintenant : dès que le job démarre, le plugin se "ping" lui-même toutes les ~5s pour faire avancer le batch suivant, jusqu'à completion. Transient de débrayage 10s pour éviter le flood si plusieurs jobs lancés en même temps.

= 2.2.2 =
* Correctif : la cellule "Progression" du tableau des tâches Bulk était vide. Le label "X / Y (Z%)" était imbriqué DANS le conteneur de la barre de progression qui a `height: 8px; overflow: hidden;` — le texte était rendu mais coupé. Label sorti du conteneur.

= 2.2.1 =
* **Cause racine du bug "Impossible de créer la tâche"** : la colonne `cursor` de la table `wp_lseo_bulk_jobs` n'était pas backtickée dans le CREATE TABLE. `cursor` est un **mot réservé MySQL 8.0+ / MariaDB 10.3+**, du coup dbDelta échouait silencieusement (il ne throw jamais), la table n'était jamais créée, et chaque tentative de Bulk renvoyait 500. Backticks ajoutés. La table se crée maintenant correctement à l'activation, et le retry défensif de 2.1.2 la créera aussi sur les sites déjà sur 2.1.x.
* i18n : les suffixes "w" (week), "d" (day), "m" (month) du card "À quoi s'attendre, quand" du Dashboard sont maintenant traduits. En français : "sem.", "j", "m".

= 2.2.0 =
* **Nouveau** : widget "Budget IA — mois en cours" en haut de la page IA en masse. Affiche en temps réel les appels, tokens entrée/sortie et coût estimé en € par fournisseur, et le cumul du mois. Permet de piloter son budget IA sans quitter WordPress. Estimation basée sur les tarifs publics Claude / GPT / Gemini / Perplexity, conversion USD→EUR filtrable via `wp_lseo_eur_per_usd` pour les utilisateurs avancés.

= 2.1.2 =
* Correctif : erreur "Impossible de créer la tâche" en lançant un Bulk IA. Cause : `Bulk_Installer::install()` bail si l'option de version DB existe, sans vérifier que la table existe vraiment. Après la cascade rebrand + upgrades successifs, l'option pouvait pointer vers une table qui n'avait pas été correctement créée. Fix : check `SHOW TABLES LIKE` en plus de l'option, et `Bulk_Repository::create()` retente après un install() si l'insert échoue. Même correctif appliqué à l'installer Redirects.

= 2.1.1 =
* Cohérence de marque : le label "Local / GEO" devient simplement "Local" partout (Dashboard breakdown, leçons du tutoriel). Évite la confusion avec le concept marketing "Generative Engine Optimization" démonté par Google.

= 2.1.0 =
* **Important** : la suppression du plugin est maintenant **opt-in**. Désormais, supprimer WP Local SEO Studio depuis WordPress ne détruit plus vos données par défaut (clés IA chiffrées, profil business, licence, tables de redirections, etc.). Cochez "Supprimer toutes les données à la désinstallation" dans Réglages → Général pour rétablir l'ancien comportement. Convention identique à Yoast / Rank Math / AIOSEO.
* **Nouveau** : Bulk IA étendu avec deux tâches : génération automatique de **mots-clés cibles** (l'IA lit le contenu et propose le mot-clé primaire le plus pertinent) et génération automatique de **Title SEO** (30-60 caractères, focus keyword placé en début, marque en fin si elle tient). Couplé aux meta descriptions et alts existants, votre plan d'action passe de 4 critiques manuelles à 4 boutons "Démarrer".

= 2.0.9 =
* Correctif : la bannière "Passez à Pro" ne s'affichait sur aucun écran du plugin. Cause : `Notice_Cleaner` (qui retire les notices des autres extensions pour ne pas polluer nos écrans) faisait un `remove_all_actions('admin_notices')` à `in_admin_header` priority 1, ce qui dégageait AUSSI notre propre bannière. Migration du hook vers `in_admin_header` priority 5 (après le cleaner) pour la rendre persistante.
* Tutoriel : filtre par catégorie (Toutes / Fondamentaux / Contenu / Technique / Autorité) maintenant fonctionnel. La JS manquait. Ajout de `assets/js/learn.js` qui gère le toggle is-active + show/hide des leçons selon leur `data-category`.

= 2.0.8 =
* Correctif définitif du wrap text étape 3 wizard. Root cause identifiée après 4 tentatives infructueuses : la règle `.wp-local-seo-studio-wizard__step { white-space: nowrap; }` (légitime pour les pills de navigation) s'appliquait aussi à la `<section class="wp-local-seo-studio-wizard__step">` du contenu (même nom de classe), et `nowrap` se propageait en cascade jusqu'au `<p>` de l'upsell. Scope sous `.wp-local-seo-studio-wizard__steps` pour limiter aux pills. CSS upsell renforcée avec `!important` en ceinture-bretelles.

= 2.0.7 =
* i18n : 102 chaînes audit (titres, descriptions, fixes, time-to-results) traduites en français. Les tâches du plan d'action sur le Dashboard sont maintenant entièrement en français.

= 2.0.6 =
* Correctif : la card "Passez à la version Pro" du wizard étape 3 débordait encore à droite sur grand écran. Cause : le grid `.wp-local-seo-studio-wizard__connections` n'avait pas de `grid-template-columns` explicite, certains navigateurs dimensionnaient alors les enfants en `min-content` ce qui laissait l'upsell box (texte long) déborder. Ajout d'un `minmax(0, 1fr)` + `min-width: 0` sur tous les enfants de la grille.

= 2.0.5 =
* **Nouveau** : bannière "Passez à Pro" en haut de tous les écrans du plugin tant que la licence Pro n'est pas activée. Masquée automatiquement sur la page Licence (redondant) et une fois Pro activé.
* **Nouveau** : badge de notification (style "mises à jour") sur le sous-menu Dashboard avec le nombre d'actions restantes du plan d'action.
* Tour guidé : marqué dismissed dès le premier affichage (localStorage + REST best-effort). Le tour ne reviendra plus même après refresh, même sans cliquer X / Suivant / Passer. Vous le voyez une fois, fin de l'histoire.
* Tour guidé : positionnement de la carte calculé pour rester TOUJOURS dans le viewport (avant, à l'étape 5 la carte pouvait s'afficher hors écran et bloquer).
* Wizard étape 3 : la card "Passez à la version Pro" et les descriptions des connexions wrap correctement maintenant (`overflow-wrap: anywhere` + `min-width: 0` sur les enfants pour neutraliser les comportements grid par défaut).
* Adminbar : pastille SEO contrainte à 22px de haut pour ne plus déborder de la barre admin (32px en desktop, 46px en mobile).

= 2.0.4 =
* **Nouveau** : pastille de santé SEO dans la barre d'admin WordPress (toutes les pages, admin et front). Affichée pour les utilisateurs `manage_options`, lit le rapport en cache (zéro coût supplémentaire au pageload), cliquable vers le Dashboard.
* Tour guidé : ajout d'un bouton X de fermeture sur chaque étape (était bloquant à la dernière), persistance locale via localStorage (le tour ne revient plus si le REST échoue), clic hors carte pour fermer, 12 chaînes traduites en français.
* Wizard : étapes de navigation (pills colorées en haut) ne débordent plus horizontalement à l'étape 3 (les sélecteurs CSS `.wp-local-seo-studio-wizard__step` étaient partagés entre les pills de nav et les `<section>` du contenu, une règle générique faisait des pills des blobs 100%).
* Bulk AI : `<select>` qui affichaient des chevrons répétés à l'horizontale (WP admin styles qui passaient). Remplacés par une flèche SVG inline propre, single, no-repeat.
* Branding : le logo Emakesolution placeholder (SVG approximation) n'est plus affiché. Tant que vous n'avez pas uploadé `assets/images/logo-emakesolution.png` via FTP, rien ne s'affiche (mieux qu'un placeholder décevant). Le logo plugin garde son fallback SVG.

= 2.0.3 =
* UX : bouton "Re-run audit" du Dashboard rendu lisible (texte jaune sur fond jaune à cause d'une règle `.wplss-app a` qui battait `.wplss-btn--primary` en spécificité).
* UX : espacement vertical ajouté entre les cards empilées dans .wplss-app (page Licence notamment trop tassée).
* i18n : 55 chaînes du tutoriel SEO traduites en français + description du plugin traduite (affichée dans la liste des extensions WordPress).
* Branding : intégration des marques (mark plugin + Emakesolution) dans les topbars admin, la page Welcome, le footer du wizard et le banner WordPress.org. Helper `Brand_Assets` qui privilégie un PNG haute résolution déposé dans `assets/images/` puis retombe sur un SVG livré.

= 2.0.2 =
* Correctif : fatal "Class Audit_Issue not found" en chargeant le Dashboard. Cause : la vue `includes/admin/views/dashboard-page.php` tourne en namespace global (les fichiers inclus n'héritent pas du namespace de l'appelant) et la ligne 267 référait `Audit_Issue::SEVERITY_CRITICAL` sans le préfixe complet. Toutes les autres références dans les vues étaient déjà qualifiées, c'était la seule oubliée.

= 2.0.1 =
* Correctif : fatal "syntax error, unexpected identifier 'string'" sur les hébergeurs PHP 8.0. Cause : nous avions des propriétés `readonly` dans plusieurs value objects (Audit_Issue, Bulk_Job, Redirect, Ai_Request, Ai_Response, Ai_Message, Ai_Exception). Le mot-clé `readonly` requiert PHP 8.1+ alors que notre header annonce PHP 8.0 minimum. Suppression du mot-clé : le code reste 8.0 compatible et la sémantique immutable est documentée plutôt qu'imposée.
* Note : le crash "Allowed memory size exhausted" observé en cascade dans class-meta-tags.php disparaît avec ce correctif. C'était la conséquence de l'autoloader qui retentait à l'infini suite au parse error.

= 2.0.0 =
* **Rebrand : WP-GEO-SEO devient WP Local SEO Studio.** Le terme "GEO" était devenu ambigu avec la sortie du guide officiel Google "AI features and your website" (15 mai 2026) qui démonte le concept de "Generative Engine Optimization". Le plugin reste exactement le même outil — Local SEO, schema, redirects, audit, IA assistant — mais sous un nom qui décrit honnêtement ce qu'il fait.
* Nouveau slug : `wp-local-seo-studio`. Nouveau namespace PHP : `Emakesolution\WP_Local_Seo_Studio\`. Nouveau préfixe d'options : `wp_lseo_`. Nouvelles tables : `{prefix}wp_lseo_redirects`, `{prefix}wp_lseo_404_log`, `{prefix}wp_lseo_bulk_jobs`.
* **Migration automatique** : à l'activation, le plugin détecte les anciennes options / meta / tables `wp_geo_seo_*` laissées par WP-GEO-SEO et les renomme en place (idempotent, atomique sur les tables). Une notice s'affiche pour confirmer la migration et inviter à supprimer l'ancien plugin.
* Préfixe des license keys : `WPGS-*` devient `WPLSS-*`.

= 1.0.2 =
* Correctif : fatal "Class Audit_Issue not found" après soumission du wizard. Cause : OPcache + autoloader stale après upgrade plugin. Solution : require_once défensif des dépendances internes du module Audit.
* Repositionnement éditorial du readme : focus sur les fondamentaux SEO et l'IA comme assistant rédactionnel, suppression du marketing "AEO / AI search era" suite à la publication par Google du guide officiel "AI features and your website" (15 mai 2026) confirmant qu'il n'y a pas d'optimisation distincte pour les AI Overviews.

= 1.0.1 =
* Wizard rendu en pleine page autonome (plus de conflit avec la coquille admin WordPress).
* Champs du wizard visibles (fond éclairci pour ne plus se confondre avec la page).
* Grille des choix : 2 colonnes desktop, 1 colonne mobile (plus de cartes flottantes à droite).
* UI traduite en français (313 chaînes).
* Notices des autres extensions filtrées sur les écrans WP Local SEO Studio.
* License Manager pointe par défaut sur emakesolution.fr/api/license.
* Per-object capability sur register_post_meta (sécurité REST).
* Limiteur de requêtes IA par utilisateur (40/h, configurable).
* Bouclier ReDoS sur les redirections regex.
* Scrubbing des clés API dans les messages d'erreur.

= 1.0.0 =
* Initial public release.
* Onboarding wizard.
* On-page SEO meta tags + Gutenberg / Classic editor integration.
* Schema.org: LocalBusiness, Organization, WebSite, Breadcrumb, FAQPage.
* XML sitemap and robots.txt editor.
* Site audit with per-category scoring and realistic timelines.
* Redirect manager + 404 monitor with AI-suggested redirects.
* Bulk AI runner for meta descriptions and image alts.
* Content analyzer (Flesch + keyword placement).
* Core Web Vitals via PageSpeed Insights.
* FAQ Gutenberg block with auto FAQPage schema.
* Hreflang for WPML / Polylang / Multisite.
* WP-CLI commands.
* Dark theme with light/auto toggle.
* Interactive guided tour.
* Per-user theme appearance preference.

== Upgrade Notice ==

= 2.0.1 =
Hotfix : parse error on PHP 8.0 hosts (we used `readonly` properties which require PHP 8.1+). Upgrade strongly recommended if 2.0.0 caused a fatal error on activation.

= 2.0.0 =
Rebrand from WP-GEO-SEO. The plugin slug changes to `wp-local-seo-studio`. Existing data is migrated automatically on activation, then the old WP-GEO-SEO plugin can be safely deleted.

= 1.0.2 =
Fixes a fatal error after wizard submission. Update strongly recommended.
