# Reprise du suivi d'audience et mise en conformité RGPD

Dossier de chantier. Dernière mise à jour : 31/08/2026.
Statut : **prêt à démarrer** — comptes neufs créés par le client, identifiants reçus,
accès administrateur accordés. Plus aucun préalable : les lots 1 à 3 (§6) sont
exécutables.

Contexte : le suivi d'audience du site est hébergé dans les comptes d'un ancien
prestataire (Mathieu), avec lequel le client n'a plus de contact. Le bandeau cookies
n'a aucun effet réel. On repart donc sur des comptes neufs au nom de Mostiglass.

---

## 1. Décision

**Option B : comptes neufs.** Ce n'est plus un choix mais une contrainte : l'option A
supposait que l'ancien prestataire attribue les droits puis se retire, elle est
inapplicable puisqu'il est injoignable.

| Produit | Décision | Identifiant |
|---|---|---|
| Google Analytics 4 | propriété neuve, compte au nom de Mostiglass | `G-CX3M4JWVK8` |
| Google Tag Manager | **conteneur neuf** — l'ancien n'était pas au client | `GTM-P6DR8R3L` |
| Microsoft Clarity | **désactivé**, balise conservée en pause (voir §4) | `w7y2zfrmwb` |
| Google Search Console | **traité** — propriété chez le client, validation vérifiée | — |

**Vérification faite le 31/08/2026 : c'est le cas 2 du §5.** Le client n'était pas
administrateur au niveau du compte sur `GTM-NBWJPB2T` — la confusion classique, on crée
un conteneur à l'intérieur du compte de l'agence, on s'en croit propriétaire, et le jour
où l'agence part on découvre qu'on n'était qu'utilisateur invité. Exactement ce qui s'est
produit sur Analytics.

Le client a donc créé lui-même, sur son propre compte, le conteneur `GTM-P6DR8R3L` et la
propriété `G-CX3M4JWVK8`, puis nous a ajoutés en administrateur. Contrôle du nouveau
conteneur côté public (méthode du §8) : `"tags":[]`, il est vide, aucun identifiant de
mesure dedans. Rien à préserver, on construit selon le §7.

Le principe qui avait manqué la première fois est cette fois tenu : **le niveau
« compte », au-dessus de la propriété et du conteneur, appartient au client.** C'est lui
qui porte la propriété juridique. Le prestataire n'a qu'un accès délégué et révocable —
aujourd'hui `limacedric@hotmail.fr`, administrateur **sur** le compte du client, et non
propriétaire de ce compte.

---

## 2. État des lieux constaté

### Dans le code

`resources/views/layouts/default.blade.php` inclut trois partiels :

| Ligne | Partiel | Contenu |
|---|---|---|
| 20 | `_partials/google-tag-manager/head.blade.php` | conteneur `GTM-NBWJPB2T` (prod), `GTM-5WM8FRWJ` (staging) |
| 31 | `_partials/google-tag-manager/body.blade.php` | iframe `noscript` des mêmes conteneurs |
| 38 | `_partials/google-scripts.blade.php` | balise gtag GA4 `G-9WVZ0HFDDF` (prod), `G-KNSVFR7CQH` (staging), **codée en dur** |

`google-scripts.blade.php` contient aussi les deux fonctions
`pushConfigStepView()` et `pushMiseRelationStepView()`, qui poussent les événements
du configurateur et du formulaire de mise en relation dans le `dataLayer`.
**Ces fonctions sont à conserver**, elles font partie du site, pas du prestataire.

### Le double comptage, confirmé

Le conteneur `GTM-NBWJPB2T` déclenche une balise Google tag sur le **même identifiant**
`G-9WVZ0HFDDF` que celui codé en dur dans les pages. La page charge donc GA4 deux fois
et chaque vue est comptée deux fois. Ce n'est pas une hypothèse : c'est lu dans la
configuration du conteneur (§7).

**Conséquence à retenir :** les chiffres de fréquentation antérieurs sont surévalués et
ne constituent pas une base de comparaison fiable. C'est ce qui rend la perte de
l'historique Analytics acceptable.

### Le bandeau cookies n'a aucun effet

`app/Livewire/CookieConsent.php` fait 28 lignes et ne contient **ni** `gtag('consent', …)`,
**ni** push `dataLayer`. `resources/views/livewire/cookie-consent.blade.php` se contente
de basculer `showPopup`. Les boutons Accepter et Refuser ne font que masquer la fenêtre :
les balises se déclenchent avant et indépendamment de la réponse du visiteur.
Refuser n'a littéralement aucun effet.

S'y ajoutent les textes de `lang/{fr,en,es}/cookie_consent.php`, qui présument le
consentement du visiteur — ce que la CNIL interdit. Le texte français dit mot pour mot
« En utilisant le site mostiglass.fr, vous acceptez l'utilisation de cookies fonctionnels ».

L'information sur les cookies, elle, n'est pas une page dédiée : c'est un unique
paragraphe des mentions légales (`resources/views/legal_notices.blade.php:71-77`,
clé `legal_notices.content.content_5`). Le titre « Cookies » y est de plus codé en dur
au lieu d'être traduit, alors que le site est en trois langues.

---

## 3. Ce qui est conservé, ce qui est perdu

**Conservé**

- l'historique Search Console, jusqu'à 16 mois : il est rattaché au domaine, pas à un
  compte, et s'affiche même sur une propriété validée récemment. **C'est le véritable
  historique SEO**, et c'est la réponse à l'inquiétude du client sur le référencement.
- la configuration des balises : extraite du conteneur public, reconstruite à
  l'identique (§7).
- tout le référencement : URL, contenus, `hreflang`, `sitemap.xml`, redirections.
  Changer de compte Analytics n'a **aucun** effet sur les positions — Google n'utilise
  pas les données Analytics pour classer les sites.

**Perdu, assumé**

- l'historique de fréquentation Analytics, celui qui est faussé par le double comptage.
- les enregistrements de sessions Clarity déjà collectés.

**Rien à faire côté ancien prestataire.** Il n'a rien à supprimer : dès que les snippets
sont retirés du site, son conteneur et sa propriété ne reçoivent plus rien et s'éteignent
d'eux-mêmes.

---

## 4. Microsoft Clarity

Découvert dans le conteneur, projet `w7y2zfrmwb`. Ce n'est pas un outil de mesure
d'audience : il **rejoue les sessions** de navigation (mouvements de souris, scroll,
clics, clics de rage) et produit des cartes de chaleur. Équivalent gratuit de Hotjar,
édité par Microsoft.

Pourquoi il est signalé :

- traitement nettement plus intrusif qu'une mesure d'audience : il capture la page telle
  que le visiteur la voit, formulaires compris si le masquage n'est pas réglé ;
- données transférées chez Microsoft, hors UE ;
- il exige donc un consentement explicite, une mention dans la politique de
  confidentialité et un masquage correct des champs. Aujourd'hui il n'a rien de tout cela
  et se déclenche sur toutes les pages sans condition ;
- les enregistrements déjà collectés sont dans le compte Microsoft de l'ancien
  prestataire : des sessions de visiteurs de Mostiglass sont détenues par un tiers sans
  lien contractuel avec le client.

**Décision : désactivé, pas supprimé.** La balise est recréée à l'identique dans le
nouveau conteneur (`projectId` `w7y2zfrmwb`) mais laissée **en pause**. Elle ne se
déclenche pas, aucune donnée ne part, et la remettre en service tient en un clic si
l'envie de cartes de chaleur revient : la configuration n'est pas perdue.

Rien à faire côté code : Clarity n'a jamais été dans le dépôt, uniquement dans le
conteneur. Une recherche `clarity` / `w7y2zfrmwb` sur `resources`, `app`, `config`,
`public` et `routes` ne remonte rien.

**Ne pas réactiver la balise sans traiter d'abord les trois points qui manquaient** — ce
sera un lot séparé, une demi-journée : consentement explicite et distinct de la mesure
d'audience, mention dans la politique de confidentialité et dans le tableau des traceurs,
masquage des champs de formulaire. Une balise en pause n'a aucune de ces obligations ;
une balise active les a toutes.

À savoir avant de la rallumer : le projet `w7y2zfrmwb` est celui de l'ancien prestataire.
Une réactivation suppose donc au préalable un projet Clarity au nom de Mostiglass, donc un
`projectId` neuf. Les enregistrements déjà collectés, eux, restent chez lui.

Pas de changement silencieux : le client doit être informé que la collecte s'arrête,
c'est son site et ses visiteurs.

---

## 5. Ce que le client devait faire — fait le 31/08/2026

**Section close.** Comptes créés, identifiants reçus (§1), accès administrateur accordés,
méthode de validation Search Console vérifiée côté client. La procédure ci-dessous est
conservée telle quelle : elle documente ce qui a été demandé, et resservira au prochain
changement de prestataire.

Deux choses bloquaient le démarrage : **l'adresse e-mail générique** qui détiendra les
comptes, et **la vérification du conteneur Tag Manager** ci-dessous. L'adresse générique
est requise dans tous les cas, ne serait-ce que pour Analytics. Une adresse rattachée à Mostiglass (`contact@`, `marketing@`…) plutôt qu'une
adresse personnelle : elle survivra aux changements de personnes.

Si ce n'est pas encore un compte Google, pas besoin de Gmail : on crée un compte Google
*avec cette adresse comme identifiant* (accounts.google.com → « Utiliser mon adresse
e-mail actuelle à la place »). Le courrier reste là où il est.

**C'est le client qui clique**, connecté à cette adresse : le créateur devient
propriétaire. Faire la création depuis le compte du prestataire reproduirait exactement
le problème qu'on est en train de corriger. Si une assistance est nécessaire, partage
d'écran — pas de mot de passe conservé.

### Procédure à transmettre telle quelle

**Analytics** — sur analytics.google.com, connecté à l'adresse générique :

1. Administration (roue crantée en bas à gauche) → Créer → Compte.
   Nom du compte : `Mostiglass`. Décocher les options de partage de données.
2. Créer une propriété. Nom : `mostiglass.fr`. Fuseau : `(GMT+01:00) Paris`.
   Devise : `Euro`.
3. Renseigner le secteur d'activité et la taille de l'entreprise, puis choisir `Web`.
4. URL : `https://mostiglass.fr`, nom du flux : `Site Mostiglass`.
   Un identifiant `G-…` s'affiche : **le transmettre.**
5. Administration → Conservation des données : passer de 2 à **14 mois**.
6. Administration → Gestion des accès au compte → ajouter `cedric.lima@vts.team`
   en **Administrateur**.

**Tag Manager** — sur tagmanager.google.com. **Vérification d'abord**, connecté au compte
avec lequel le client pense avoir créé le conteneur : le conteneur `GTM-NBWJPB2T`
apparaît-il dans la liste ? Si oui, ouvrir Administration → Gestion des utilisateurs
**au niveau du compte** (colonne de gauche, pas celle du conteneur) et regarder si le
client y figure comme Administrateur.

*Cas 1 — il est administrateur du compte : on garde le conteneur.*

1. Gestion des utilisateurs au niveau du compte → retirer l'ancien prestataire.
2. Ajouter `cedric.lima@vts.team`, **Administrateur** au niveau du compte et **Publier**
   au niveau du conteneur.
3. Rien d'autre : je vide les balises existantes et je reconstruis (§7).

*Cas 2 — le conteneur n'apparaît pas, ou il n'est qu'utilisateur : conteneur neuf,
connecté à l'adresse générique.*

1. Créer un compte. Nom : `Mostiglass`. Pays : `France`.
   Décocher le partage de données anonymes.
2. Nom du conteneur : `mostiglass.fr`, type : **Web**.
3. Un identifiant `GTM-…` s'affiche : **le transmettre.**
4. Administration → Gestion des utilisateurs → ajouter `cedric.lima@vts.team`,
   **Administrateur** au niveau du compte et **Publier** au niveau du conteneur.

### Vérification à faire de son côté

Dans Search Console → Paramètres → Validation de la propriété : s'assurer que la méthode
active est **DNS ou fichier HTML**, et **pas** « Google Analytics » ni « Google Tag
Manager ». Si c'est l'un de ces deux-là, retirer les balises invaliderait la propriété —
il faut alors ajouter une seconde méthode **avant** la bascule.

---

## 6. Plan de travail et chiffrage

Chiffrage annoncé au client : environ 1,75 jour, soit 12,5 heures. Vérifié après
inventaire complet du conteneur et lecture du code : **il tient**. Le détail ci-dessous
donne une fourchette de 10 à 13 heures, l'annonce se situe donc dans le haut de la
fourchette, sans marge excessive. La mise en pause de Clarity ne change pas ce chiffrage ;
sa remise en conformité l'aurait augmenté d'une demi-journée.

| # | Lot | Annoncé | Estimé |
|---|---|---|---|
| 1 | Remise au propre du code de suivi et suppression du double comptage | 3 h | 1,5 – 2 h |
| 2 | Mise en conformité RGPD du consentement | 7 h | 6 – 8 h |
| 3 | Reconstruction des balises, recette et mise en ligne | 2,5 h | 2,5 – 3 h |
| | **Total** | **12,5 h** | **10 – 13 h** |

Détail du lot 2, qui porte l'essentiel de la charge :

| Poste | Charge |
|---|---|
| Mécanisme Consent Mode v2 : `default denied` avant GTM, persistance du choix, `update` au clic, refus aussi accessible que l'acceptation, retrait possible | 2,5 – 3,5 h |
| Rédaction des textes du bandeau et de la section cookies (tableau des traceurs) en français, anglais, espagnol | 2,5 – 3 h |
| Tests automatisés et non-régression | 1 h |

La rédaction pèse presque autant que le code : c'est du texte à valeur juridique, en
trois langues, et l'espagnol demande une relecture. C'est le poste qu'on sous-estime.

**Ce qui a déjà été absorbé :** l'inventaire du conteneur, qui représentait environ une
heure du lot 3 et constituait le principal aléa de l'estimation, est fait (§7 et §8).
C'est ce qui permet de tenir le chiffrage sans réserve.

**Ce qui pourrait le faire déraper**

- une relecture des textes anglais et espagnols par un tiers, ou une validation du
  tableau des traceurs par le client : des allers-retours, pas du développement, mais du
  délai
- ~~la mise en ligne : `deploy.sh` et `DEPLOIEMENT-OVH.md` sont supprimés dans la copie de
  travail~~ — **levé le 31/08/2026** : les deux fichiers sont intacts dans `HEAD`, seule la
  copie de travail les a perdus, un `git checkout` les rétablit. `doc/deploiement.md`
  décrit la procédure. Reste à confirmer quel hébergement porte la production (voir §11.8)
- Clarity réactivé : + 4 h (consentement dédié, textes, masquage, projet Clarity au nom
  de Mostiglass), et cela sort du présent chiffrage

**Lot 1** — retirer la balise gtag GA4 codée en dur de `google-scripts.blade.php` en
conservant les deux fonctions `push…StepView()`, ne garder qu'un seul point d'entrée (le
conteneur), et nettoyer les identifiants de staging `GTM-5WM8FRWJ` / `G-KNSVFR7CQH`, qui
appartiennent aussi à l'ancien prestataire.

**Décision recette :** aucun suivi sur l'environnement de staging. Les blocs
`@env('staging')` sont retirés, pas remplacés — on ne mesure pas un environnement de
test, et cela évite de polluer les données de production.

**Lot 2** — refondre `app/Livewire/CookieConsent.php` en Consent Mode v2 : `default` à
`denied` posé **avant** le snippet GTM dans le `<head>`, `update` au clic, choix persisté
et modifiable — le bouton flottant de réouverture du bandeau existe déjà et sert de
mécanisme de retrait du consentement. Puis reprendre les textes de
`lang/{fr,en,es}/cookie_consent.php`, et remplacer le paragraphe cookies des mentions
légales par une section en règle, avec le tableau des traceurs (nom, finalité, détenteur,
durée) — le tout en **trois langues : français, anglais, espagnol**.

**Lot 3** — reconstruire les six balises utiles selon la spécification du §7, recette,
mise en ligne. Réglages complémentaires côté GA4, une fois l'accès admin obtenu :
désactiver les signaux Google et les fonctionnalités publicitaires personnalisées
(inutiles ici, sensibles côté CNIL), et relier la nouvelle propriété GA4 à la propriété
Search Console existante — cela suppose d'être administrateur des deux, donc de savoir si
Search Console est sur la même adresse générique.

**Ordre d'exécution** : les préalables du §5 sont levés. Lots 1 et 2 immédiatement, ils
ne dépendent d'aucun accès ; lot 3 ensuite, dans `GTM-P6DR8R3L`.

---

## 7. Spécification de reconstruction du conteneur

Configuration relevée dans l'ancien conteneur `GTM-NBWJPB2T`, à reconstruire dans
`GTM-P6DR8R3L`. Dix balises, dont quatre sont de l'infrastructure sans intérêt à
reproduire (un déclencheur de clic et les écouteurs associés). Six comptent, plus Clarity
qui est recréé **en pause** (§4).

| Balise | Type | Déclencheur | Paramètres envoyés |
|---|---|---|---|
| Google tag | configuration GA4 (`G-CX3M4JWVK8`) | Initialisation | — |
| `devis_back` | événement GA4 | clic, `gtm.elementClasses` contient `cta-back` | `bouton_text` = texte de l'élément cliqué |
| `devis_reset` | événement GA4 | clic, `gtm.elementClasses` contient `cta-reset` | `bouton_text` |
| `devis_info` | événement GA4 | clic, `gtm.elementClasses` contient `cta-info` | `bouton_text` |
| `config_step_view` | événement GA4 | événement `dataLayer` `config_step_view` | `step_number`, `step_name`, `step_choice`, `site_language` |
| `mise_en_relation_step_view` | événement GA4 | événement `dataLayer` `mise_en_relation_step_view` | `step_number`, `step_name`, `step_choice`, `site_language` |
| Microsoft Clarity | modèle personnalisé — **en pause** | toutes les pages | `projectId` `w7y2zfrmwb`, `session_id` et `client_id` GA4 |

Variables à recréer : `gtm.elementClasses` et le texte de l'élément cliqué (variables
intégrées), plus quatre variables de couche de données — `step_number`, `step_name`,
`step_choice`, `site_language` — en version 2, sans valeur par défaut.

Les quatre événements `devis_*` et `*_step_view` sont émis par le site lui-même : les
trois premiers reposent sur les classes CSS `cta-back` / `cta-reset` / `cta-info`, les
deux derniers sur les fonctions de `google-scripts.blade.php`. **Ne pas renommer ces
classes ni ces fonctions** sans reprendre le conteneur.

Dans le nouveau conteneur, chaque balise doit en plus être conditionnée au consentement
(`analytics_storage`), ce qui n'était pas le cas dans l'ancienne configuration. Trois
réglages à ne pas oublier au moment de la construction :

- **Paramètres de consentement de chaque balise** → « Nécessite un consentement
  supplémentaire avant le déclenchement » : `analytics_storage`. Le site pose déjà les
  signaux à `denied` par défaut (lot 2), GTM met alors les balises en attente et les
  déclenche de lui-même dès l'acceptation.
- **Durée de vie des cookies GA4** : `cookie_expires` = `34128000` secondes, soit
  13 mois. C'est la durée annoncée aux visiteurs dans le tableau des traceurs des
  mentions légales ; la valeur par défaut de Google est de 2 ans et la contredirait.
- Un déclencheur d'événement personnalisé **`consent_update`** est disponible : le
  bandeau le pousse dans le `dataLayer` au moment du clic. Utile en filet de sécurité si
  une balise doit repartir immédiatement après une acceptation, sans changement de page.

Aucun Google Ads, aucun Floodlight, aucun pixel Meta ou LinkedIn dans le conteneur :
rien d'autre à préserver.

---

## 8. Méthode : auditer un conteneur GTM sans y avoir accès

À conserver, c'est ce qui a permis de se passer de l'ancien prestataire. La configuration
d'un conteneur GTM est servie publiquement par Google. On la récupère et on la lit :

```bash
curl -s "https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX" -o gtm.js

# identifiants de mesure présents dans le conteneur
grep -oE "G-[A-Z0-9]{9,}|AW-[0-9]{6,}|UA-[0-9]+-[0-9]+|DC-[0-9]+" gtm.js | sort -u
```

Le détail — balises, déclencheurs, variables — se lit dans le bloc `var data = {…};` du
fichier, en JSON : `resource.tags`, `resource.predicates`, `resource.macros` et
`resource.rules`, qui associe les conditions aux balises. Les types sont encodés
(`__gaawe` = événement GA4, `__googtag` = configuration Google tag, `__cl` = écouteur de
clic, `__cvt_…` = modèle personnalisé de la galerie).

Cette lecture n'exige aucun droit sur le conteneur, et fonctionne pour n'importe quel
site. Corollaire à garder en tête : **notre propre conteneur sera lisible de la même
façon.** Rien de confidentiel — clé d'API, identifiant interne, logique métier — ne doit
y être placé.

---

## 9. Point de vigilance à porter au client

Tant que le conteneur d'un tiers est en place sur le site, la personne qui l'administre
peut y ajouter n'importe quel script, qui s'exécutera chez tous les visiteurs, sans
passer ni par le client ni par nous. Rien de tel n'est présent aujourd'hui, et rien
n'indique une telle intention. Mais c'est un accès qu'il faut refermer, et c'est un
argument pour ne pas laisser traîner la bascule.

---

## 10. Journal d'exécution

### Lot 1 — fait le 31/08/2026

Un seul point d'entrée pour le suivi : le conteneur Tag Manager. Le double comptage est
supprimé à la source, puisqu'il n'existe plus de gtag dans les pages.

| Fichier | Changement |
|---|---|
| `config/services.php` | entrée `google_tag_manager.container_id`, alimentée par `GTM_CONTAINER_ID` |
| `_partials/google-tag-manager/head.blade.php` | identifiant unique lu en configuration ; ne rend rien si vide |
| `_partials/google-tag-manager/body.blade.php` | idem pour l'iframe `noscript` |
| `_partials/google-scripts.blade.php` | **balise gtag GA4 supprimée** ; les deux fonctions `push…StepView()` sont conservées à l'identique |
| `.env.example` | variable documentée |

Les identifiants de l'ancien prestataire — `G-9WVZ0HFDDF`, `G-KNSVFR7CQH`,
`GTM-NBWJPB2T`, `GTM-5WM8FRWJ` — ne sont plus nulle part dans le code. Les blocs
`@env('production')` / `@env('staging')` sont remplacés par une simple condition sur la
valeur configurée : environnement sans identifiant, aucun suivi. C'est la décision recette
du §6.

`window.dataLayer` est désormais initialisé dans `google-scripts.blade.php` : sans cette
garde, un appel à `pushConfigStepView()` lèverait une erreur en local, où le conteneur
n'est pas chargé.

Contrôlé par rendu des trois partiels, avec et sans identifiant configuré.

### Deux points à traiter à la mise en ligne

1. **`GTM_CONTAINER_ID=GTM-P6DR8R3L` doit être posé dans le `.env` de production.** Sans
   cette ligne le site ne mesure plus rien, silencieusement. À vérifier après déploiement
   par un `curl` sur la page d'accueil, en cherchant `GTM-P6DR8R3L` dans le HTML.
2. **Ordre de bascule : publier les balises du §7 dans `GTM-P6DR8R3L` *avant* de déployer
   le code.** Le nouveau conteneur n'est pas encore appelé par le site, publier dedans est
   donc sans effet visible ; l'inverse — déployer d'abord — laisserait une fenêtre sans
   aucune mesure.

### Dette repérée au passage, non traitée

`.env.example` est couvert par la règle `.env.*` du `.gitignore` : il n'est pas versionné.
La documentation des variables d'environnement n'est donc visible que sur les machines qui
possèdent déjà le fichier. Hors périmètre de ce chantier, mais à corriger un jour par une
exception `!.env.example`.

### Lot 2 — fait le 31/08/2026

Le consentement a désormais un effet réel. Avant : les balises partaient au chargement,
« Refuser » ne faisait que masquer la fenêtre.

**Le mécanisme.** Un partiel `_partials/consent-mode.blade.php`, placé dans le `<head>`
**avant** le conteneur — l'ordre est la seule chose qui rende le refus effectif. Il pose
les sept signaux de Consent Mode v2 à `denied` (sauf `functionality_storage` et
`security_storage`, exemptés), active `ads_data_redaction`, puis relit le choix du
visiteur et l'applique. Il expose `window.mostiglassConsent` : `state`, `set()`,
`revoke()`.

Le choix est conservé dans un cookie premier lieu `mostiglass_consent`, 6 mois, `SameSite=Lax`,
`Secure` en HTTPS, versionné — incrémenter `VERSION` invalide tous les choix passés, c'est
le levier à actionner si la liste des traceurs change. Lu de façon **synchrone** dans le
`<head>` : pas de clignotement de consentement, pas d'appel réseau avant la décision.

**Le bandeau.** `app/Livewire/CookieConsent.php` est supprimé. Il stockait le choix en
session PHP — perdu à chaque nouvelle session — et faisait un aller-retour serveur avant
d'appliquer une décision qui est purement navigateur. Remplacé par
`_partials/cookie-consent.blade.php`, en Alpine seul : le choix s'applique au clic, sans
latence, et fonctionne même si Livewire tombe.

- refuser et accepter : même taille, même largeur, un clic chacun, sur le même écran ;
- le bandeau s'ouvre immédiatement quand aucun choix n'est enregistré, au lieu des
  5 secondes de délai précédentes ;
- le bouton flottant reste accessible en permanence et rappelle le choix en cours : c'est
  le mécanisme de retrait ;
- lien vers le détail des traceurs, ancre `#cookies` des mentions légales.

**Les textes**, réécrits en français, anglais et espagnol. Les anciens présumaient le
consentement — « En utilisant le site mostiglass.fr, vous acceptez l'utilisation de
cookies fonctionnels », ce que la CNIL interdit. Les nouveaux annoncent la finalité,
précisent que refuser est sans conséquence, et que le choix est modifiable.

**La section cookies des mentions légales** remplace le paragraphe unique
`legal_notices.content.content_5`, supprimé. Titre traduit (il était codé en dur),
ancre `id="cookies"`, et un tableau des traceurs — nom, finalité, détenteur, durée,
régime de consentement — dans les trois langues.

**Deux traceurs tiers découverts en cours de lot**, absents de l'inventaire du §2 :

- **sept vidéos YouTube** chargées depuis `youtube.com`, qui déposent des cookies à
  l'affichage de la page. Basculées sur `youtube-nocookie.com` : plus rien n'est déposé
  tant que le visiteur ne lance pas la lecture ;
- **une carte Google Maps** sur « Qui sommes-nous », chargée automatiquement. Placée
  derrière un clic explicite : l'iframe n'existe que dans un `<template>` inerte, le
  navigateur ne la requête pas avant que le visiteur ne demande l'affichage.

Les deux figurent au tableau des traceurs avec leur régime réel.

**Fichiers touchés**

| Fichier | Changement |
|---|---|
| `_partials/consent-mode.blade.php` | **nouveau** — Consent Mode v2, cookie de choix, API `window.mostiglassConsent` |
| `_partials/cookie-consent.blade.php` | **nouveau** — bandeau Alpine, sans état serveur |
| `app/Livewire/CookieConsent.php` | **supprimé** |
| `resources/views/livewire/cookie-consent.blade.php` | **supprimé** |
| `layouts/default.blade.php` | Consent Mode avant le conteneur ; bandeau en `@include` |
| `lang/{fr,en,es}/cookie_consent.php` | textes réécrits, plus de consentement présumé |
| `lang/{fr,en,es}/legal_notices.php` | section cookies + tableau des traceurs ; `content_5` supprimé |
| `lang/{fr,en,es}/embeds.php` | **nouveau** — textes du chargement à la demande de la carte |
| `legal_notices.blade.php` | section cookies, titre traduit, ancre `#cookies` |
| `about_us/presentation.blade.php` | carte Google Maps derrière un clic |
| 8 vues produit et pages | `youtube.com` → `youtube-nocookie.com` |
| `EncryptCookies.php` | `mostiglass_consent` exempté de chiffrement |
| `scss/custom/_helper.scss` | règle `[x-cloak]` |
| `tests/Feature/CookieConsentTest.php` | **nouveau** — 16 tests |

**Recette.** 16 tests dédiés, dont l'ordre Consent Mode / conteneur, l'absence
d'identifiant de mesure dans les pages, la parité refuser/accepter, la présence de chaque
traceur au tableau dans les trois langues, et la non-régression des fonctions
`push…StepView()` dont dépendent les balises du §7. Suite complète : 326 tests au vert.
`npm run build` passe.

### Ce qu'il reste à vérifier à la main

Les tests couvrent le rendu, pas le comportement du navigateur. Après déploiement, et
conteneur publié :

1. arriver sur le site, ouvrir les outils de développement → aucun appel à
   `google-analytics.com` avant le clic ;
2. cliquer « Refuser » → toujours aucun appel, et le cookie `mostiglass_consent`
   contient `"analytics":false` ;
3. rouvrir le bandeau par le bouton flottant, cliquer « Accepter » → les appels partent,
   la visite apparaît dans GA4 → Temps réel ;
4. recharger la page → le choix tient, le bandeau ne se rouvre pas ;
5. mode aperçu de GTM : vérifier que les balises sont bien retenues tant que
   `analytics_storage` est `denied`.

### Dette repérée au passage, non traitée

`lang/en/routes.php` déclare la clé `legal-notices` alors que `routes/web.php` demande
`legale-notices`. La traduction anglaise n'est donc jamais trouvée et la page anglaise est
servie sur `/en/mentions-legales`, un slug français. Corriger l'URL suppose une
redirection 301 et une reprise du `sitemap.xml` : à traiter comme un point SEO à part,
hors de ce chantier.

---

## 11. Feuille de construction du conteneur `GTM-P6DR8R3L` (lot 3)

À suivre dans l'ordre, connecté au conteneur avec le droit **Publier**. Tout se passe
dans l'interface GTM : rien de ce lot ne touche au code. Ne rien publier avant la recette
du §11.6.

### 11.0 Raccourci : importer la configuration

`doc/gtm-P6DR8R3L-import.json` contient les 6 balises, 6 déclencheurs et 5 variables au
format d'export GTM, réglages de consentement et `cookie_expires` compris. Il évite une
heure de saisie et, surtout, toute faute de frappe sur les noms d'événements et de
paramètres, qui doivent correspondre exactement à ce que le code émet.

**Procédure.** Administration → **Importer un conteneur** → choisir le fichier → espace de
travail existant → option **« Fusionner »** puis « Conserver les éléments en conflit »
(le conteneur est vide, il n'y aura aucun conflit) → l'aperçu liste ce qui va être créé →
Confirmer.

L'import ne publie rien. Le conteneur reste inerte jusqu'à la publication du §11.8.

**Deux réserves à connaître.**

1. Ce format n'est pas documenté par Google ; il est écrit d'après la structure de ses
   propres exports. Si l'import est refusé, ou si un champ arrive vide, la saisie manuelle
   des §11.2 à 11.5 reste la référence — les deux décrivent la même configuration.
2. **La balise Microsoft Clarity n'est pas dans le fichier.** Elle repose sur un modèle de
   la galerie, qui ne s'importe pas de cette façon. À ajouter à la main : Balises →
   Nouvelle → Découvrir d'autres types de balises dans la galerie → « Microsoft Clarity »
   → `projectId` `w7y2zfrmwb`, déclencheur Toutes les pages, consentement
   `analytics_storage`, puis **Pause** (§4).

**À vérifier après import**, avant la recette :

- le réglage du §11.1 est bien actif, sinon les consentements importés ne sont pas visibles
  dans l'interface ;
- les cinq balises d'événement pointent bien sur `GA4 - configuration` comme identifiant
  de mesure ;
- `GA4 - configuration` porte bien `cookie_expires` = `34128000` ;
- les quatre variables `DLV - *` sont en version 2, sans valeur par défaut.

### 11.1 Réglage préalable du conteneur

Administration → Paramètres du conteneur → cocher **« Activer la vue d'ensemble du
consentement »**. Sans cela, l'onglet « Consentement » n'apparaît pas dans les balises et
tout le §11.5 est impossible à régler.

### 11.2 Variables

**Variables intégrées** — Variables → Configurer, cocher :

| Variable | Sert à |
|---|---|
| `Click Classes` | reconnaître `cta-back`, `cta-reset`, `cta-info` |
| `Click Text` | remplir le paramètre `bouton_text` |

**Variables définies par l'utilisateur** — quatre variables de couche de données, toutes
en **version 2**, **sans valeur par défaut** (une valeur par défaut masquerait les
événements mal émis au lieu de les rendre visibles) :

| Nom de la variable | Nom de la variable de couche de données |
|---|---|
| `DLV - step_number` | `step_number` |
| `DLV - step_name` | `step_name` |
| `DLV - step_choice` | `step_choice` |
| `DLV - site_language` | `site_language` |

Plus une constante, pour ne pas recopier l'identifiant dans six balises :

| Nom | Type | Valeur |
|---|---|---|
| `CONST - GA4` | Constante | `G-CX3M4JWVK8` |

### 11.3 Déclencheurs

| Nom | Type | Condition |
|---|---|---|
| `Init - toutes les pages` | Initialisation — toutes les pages | — (déclencheur intégré) |
| `Clic - cta-back` | Clic — tous les éléments | `Click Classes` contient `cta-back` |
| `Clic - cta-reset` | Clic — tous les éléments | `Click Classes` contient `cta-reset` |
| `Clic - cta-info` | Clic — tous les éléments | `Click Classes` contient `cta-info` |
| `Evt - config_step_view` | Événement personnalisé | nom de l'événement `config_step_view` |
| `Evt - mise_en_relation_step_view` | Événement personnalisé | nom de l'événement `mise_en_relation_step_view` |

Pas besoin de créer d'écouteur de clic : GTM l'ajoute de lui-même dès qu'un déclencheur de
clic existe. C'est ce qu'étaient les quatre balises d'infrastructure de l'ancien conteneur.

Attention au type : **« Clic — tous les éléments »**, pas « Clic — liens uniquement ». Les
trois `cta-*` sont des boutons, pas des liens.

### 11.4 Balises

| Nom | Type | Déclencheur | Réglages |
|---|---|---|---|
| `GA4 - configuration` | Google tag | `Init - toutes les pages` | ID `{{CONST - GA4}}` ; paramètre de configuration `cookie_expires` = `34128000` |
| `GA4 - devis_back` | Événement GA4 | `Clic - cta-back` | événement `devis_back`, paramètre `bouton_text` = `{{Click Text}}` |
| `GA4 - devis_reset` | Événement GA4 | `Clic - cta-reset` | événement `devis_reset`, `bouton_text` = `{{Click Text}}` |
| `GA4 - devis_info` | Événement GA4 | `Clic - cta-info` | événement `devis_info`, `bouton_text` = `{{Click Text}}` |
| `GA4 - config_step_view` | Événement GA4 | `Evt - config_step_view` | événement `config_step_view`, paramètres `step_number`, `step_name`, `step_choice`, `site_language` = les quatre `DLV` |
| `GA4 - mise_en_relation_step_view` | Événement GA4 | `Evt - mise_en_relation_step_view` | événement `mise_en_relation_step_view`, mêmes quatre paramètres |
| `Clarity` | modèle de la galerie « Microsoft Clarity » | Toutes les pages | `projectId` `w7y2zfrmwb` — **balise mise en pause**, voir §4 |

Les six balises GA4 prennent `{{CONST - GA4}}` comme identifiant de mesure, pas la valeur
en clair.

`cookie_expires` = 34128000 secondes, soit 13 mois : c'est la durée annoncée aux visiteurs
dans le tableau des traceurs des mentions légales. La valeur par défaut de Google est de
2 ans et contredirait la page.

Pour Clarity : créer la balise, régler le `projectId`, puis **Pause** dans le menu de la
balise. Une balise en pause est conservée dans le conteneur et ne se déclenche jamais.

### 11.5 Consentement, sur chacune des sept balises

Onglet **Consentement** → « Nécessite un consentement supplémentaire avant le
déclenchement » → `analytics_storage`.

Y compris sur `GA4 - configuration` : c'est ce qui empêche GA4 de se charger du tout avant
l'accord du visiteur. Conséquence assumée — on renonce aux « pings sans cookie » et à la
modélisation de Google sur les visiteurs qui refusent. C'est le choix le plus sûr côté
CNIL, et le seul cohérent avec ce que le bandeau annonce.

Le site pose déjà les signaux à `denied` par défaut (lot 2). GTM met alors les balises en
attente et les déclenche de lui-même dès l'acceptation, sans changement de page.

### 11.6 Recette en mode Aperçu, avant publication

Aperçu sur `https://mostiglass.fr` — le code du lot 2 doit donc être déployé, ou testé
depuis un environnement où `GTM_CONTAINER_ID` est renseigné.

1. arrivée sur le site, aucun choix enregistré → **aucune** balise déclenchée, les sept
   apparaissent en « non déclenchées » avec le motif consentement ;
2. clic sur **Refuser** → toujours aucune balise ;
3. clic sur **Accepter** depuis le bouton flottant → `GA4 - configuration` part
   immédiatement, sans rechargement ;
4. parcours du configurateur → `config_step_view` avec ses quatre paramètres remplis, pas
   de `undefined` ;
5. clics sur les boutons retour / réinitialiser / info → les trois `devis_*` avec
   `bouton_text` non vide ;
6. formulaire de mise en relation → `mise_en_relation_step_view` ;
7. `Clarity` reste en pause, jamais déclenchée ;
8. GA4 → Rapports → Temps réel : la visite apparaît.

### 11.7 Réglages GA4, une fois admin sur la propriété

- Conservation des données → **14 mois** (2 mois par défaut) ;
- désactiver les **signaux Google** ;
- désactiver les **fonctionnalités publicitaires personnalisées** — inutiles ici, et
  sensibles côté CNIL ;
- relier la propriété à la propriété **Search Console** existante (suppose d'être
  administrateur des deux).

### 11.8 Publication

Nom de version : `Reconstruction Mostiglass — GA4 G-CX3M4JWVK8 + consentement`. Description :
renvoyer vers ce dossier.

Puis, dans cet ordre : **publier le conteneur, ensuite déployer le code** avec
`GTM_CONTAINER_ID=GTM-P6DR8R3L` dans le `.env` de production. L'inverse laisserait une
fenêtre sans aucune mesure (§10).

**Avant de déployer, deux points à régler.** `deploy.sh` et `DEPLOIEMENT-OVH.md` sont
supprimés dans la copie de travail mais présents dans `HEAD` : `git checkout -- deploy.sh
DEPLOIEMENT-OVH.md` les rétablit. Et il existe deux procédures — `doc/deploiement.md`
décrit une VM Debian (`gocap-mostiglass-2`, nginx + PHP-FPM, `sudo -u www-data
./deploy.sh --ref origin/main`), `DEPLOIEMENT-OVH.md` un hébergement mutualisé OVH. Il
faut savoir laquelle des deux porte `mostiglass.fr` aujourd'hui avant de lancer quoi que
ce soit : la variable `GTM_CONTAINER_ID` doit être posée dans le bon `.env`.

Le dernier geste du chantier, une fois les données confirmées dans GA4 : prévenir le
client que la collecte Clarity est arrêtée (§4), et lui rappeler de retirer l'ancien
prestataire des comptes où il figure encore.
