# Strategie responsive (mobile / tablette)

Ce document decrit la strategie retenue pour rendre le site responsive, sans
regression sur le rendu desktop existant (pixel-perfect vis-a-vis de la
maquette Claude Design, voir CLAUDE.md section 1).

## 1. Pourquoi une strategie "hybride bornee"

Le site est aujourd'hui 100% desktop. Les vues Blade portent des styles
**inline**, herites tels quels de l'export Claude Design (`maquettes/*.dc.html`).
Cette fidelite pixel-perfect au design source est un critere de reussite non
negociable (CLAUDE.md section 1) : on ne peut donc pas se permettre de
tout re-ecrire en classes CSS au risque d'introduire des ecarts, meme minimes,
avec le rendu desktop valide.

Deux options extremes ont ete ecartees :
- **Tout re-ecrire en classes CSS** (approche "propre" classique) : risque de
  regression desktop trop eleve pour un gain immediat limite, et gros chantier
  hors budget du lot M0.
- **Media queries qui dupliquent tout en inline via `<style>` par page** :
  duplication de logique, difficile a maintenir, dilue la source de verite
  (les tokens et l'inline desktop).

La strategie retenue, actee par le chef de projet, est **hybride bornee** :
on extrait en classes CSS utilitaires **uniquement les proprietes qui doivent
changer par breakpoint**, et on laisse tout le reste (couleurs, ombres,
rayons, transitions, tout le decoratif) **inline et intouche**.

### Perimetre exact des proprietes extraites

| Propriete | Pourquoi elle doit changer par breakpoint |
|---|---|
| `display` | Masquer/afficher des blocs selon le viewport (ex : elements non pertinents en mobile) |
| `grid-template-columns` | Le nombre de colonnes d'une grille doit se reduire (5/4/3/2 -> 2 -> 1) |
| `max-width` / `padding` du conteneur | La largeur max de 1280px et le padding lateral de 32px (desktop) sont trop larges sur petit ecran |
| `font-size` des titres | L'echelle typographique desktop (jusqu'a 52px) est illisible/trop imposante sur mobile |
| Position des elements fixes | A traiter lot par lot (sticky-actions, header) — voir "Points d'attention M1+" |

Tout le reste (couleurs de fond, box-shadow, border-radius, transitions,
polices, poids de police...) **reste inline**, car ce sont des tokens de
design actes (voir `docs/design-tokens.md`) qui ne varient pas selon le
viewport dans la maquette.

## 2. Breakpoints actes

| Nom | Plage | Comportement |
|---|---|---|
| Desktop | `> 1024px` | Comportement actuel, **strictement inchange**. C'est la reference pixel-perfect. |
| Tablette | `481px` a `1024px` | 2 colonnes de grille, conteneur resserre (padding 24px), titres reduits une premiere fois. |
| Mobile | `<= 480px` | 1 colonne de grille, conteneur encore plus resserre (padding 16px), titres reduits une seconde fois. |

## 3. Regle d'or : non-regression desktop

**Aucune regle de `public/css/mobile.css` ne s'applique au-dela de 1024px.**
Concretement :
- Toute regle est **obligatoirement** enfermee dans `@media (max-width:1024px)`
  ou `@media (max-width:480px)`.
- Une seule exception, documentee et volontaire : `.biome-visible-mobile`
  utilise `@media (min-width:1025px)` pour se masquer par defaut en desktop
  (voir le catalogue plus bas pour la justification complete). Cette
  exception ne modifie aucun rendu existant : elle garde cache, en desktop,
  un bloc qui n'a de sens qu'en dessous de 1024px.
- `public/css/tokens.css` et `public/css/site.css` ne sont **jamais** modifies
  par ce chantier (voir CLAUDE.md section 4, "Zones NE PAS MODIFIER").

## 4. Le `!important` : pourquoi et ou

Les elements cibles portent des styles inline directement dans le HTML/Blade
(ex: `style="grid-template-columns:1.6fr 1fr 1fr 1fr 1fr"`). Une regle CSS
externe normale ne peut **jamais** surcharger un style inline, quelle que
soit sa specificite de selecteur : la specificite d'un style inline est
toujours superieure. Le `!important` est donc **indispensable** pour que le
responsive fonctionne du tout, tant que l'inline desktop n'est pas retire.

Le `!important` est strictement confine :
- au fichier `public/css/mobile.css` uniquement (jamais dans `site.css` ni
  `tokens.css`) ;
- a l'interieur des media queries `(max-width:1024px)` / `(max-width:480px)` ;
- **jamais actif en desktop** (`>1024px`), ou l'inline reste seul maitre.

## 5. Catalogue des classes utilitaires (`public/css/mobile.css`)

### Grilles

| Classe | Desktop (>1024px) | Tablette (481-1024px) | Mobile (<=480px) |
|---|---|---|---|
| `.biome-grille-2` a `.biome-grille-7` | Aucune regle (inline seul) | `grid-template-columns: repeat(2,1fr) !important` | `grid-template-columns: 1fr !important` |

Une seule famille de regles couvre les 7 classes (2 a 7 colonnes) : le nombre
cible en tablette/mobile est le meme quel que soit le nombre de colonnes
desktop d'origine (2 en tablette, 1 en mobile).

**Correctif associe (lot M2) : `min-width:0` sur les enfants directs.** Par
defaut CSS, un enfant direct d'une grille a `min-width:auto`, ce qui
l'empeche de se retrecir en dessous de la largeur de son propre contenu
(texte insecable, bloc avec padding horizontal, etc.). Constate en recette a
375px : des blocs "placeholder" (texte centre + padding, pages
Qui-sommes-nous et Investir) debordaient du conteneur alors que la grille
etait deja passee a 1 colonne. Regle ajoutee, confinee aux memes media
queries que `.biome-grille-N` :

```css
@media (max-width: 1024px) {
  .biome-grille-2 > *, .biome-grille-3 > *, /* ... */ {
    min-width: 0 !important;
  }
}
```

### Conteneur

| Classe | Desktop | Tablette | Mobile |
|---|---|---|---|
| `.biome-conteneur` | Aucune regle (padding inline, ex 32px, conserve) | `padding-left/right: 24px !important` | `padding-left/right: 16px !important` |

Ne touche pas au padding vertical (`padding-top`/`padding-bottom`), qui reste
gere par l'inline existant.

### Echelle typographique (titres)

| Classe | Desktop (inline, variable selon page) | Tablette | Mobile |
|---|---|---|---|
| `.biome-titre-hero` | 52/50/48/46px | `font-size:40px !important` | `font-size:30px !important` |
| `.biome-titre-section-lg` | 38px | `font-size:30px !important` | `font-size:26px !important` |
| `.biome-titre-section` | 32-34px | `font-size:28px !important` | `font-size:24px !important` |
| `.biome-titre-sous-section` | 24-28px | `font-size:22px !important` | `font-size:20px !important` |

`line-height` (et `letter-spacing` pour le hero) sont egalement ajustes en
media query pour garder une lisibilite correcte a taille reduite ; ce sont
des valeurs de confort typographique, pas des tokens de la maquette desktop
(qui n'en a pas besoin a ces tailles).

### Affichage / visibilite

| Classe | Comportement |
|---|---|
| `.biome-masquer-mobile` | `display:none !important` en `<=480px` uniquement |
| `.biome-masquer-tablette-mobile` | `display:none !important` en `<=1024px` (tablette + mobile) |
| `.biome-visible-mobile` | Masque par defaut en desktop (`@media (min-width:1025px) { display:none !important }`), visible sinon (tablette + mobile) |

**Justification de l'exception `min-width` sur `.biome-visible-mobile`** :
cette classe sert a des elements qui n'existent QUE pour les petits ecrans
(ex : futur bouton menu burger) et qui n'ont donc **aucun style desktop
inline** pour les masquer. Sans cette regle, l'element serait visible partout
par defaut. C'est la seule classe du fichier avec ce comportement inverse ; a
utiliser avec parcimonie et seulement pour des elements propres au mobile
(jamais pour dupliquer un contenu deja present en desktop sous une autre
forme — voir la consigne "pas de duplication de DOM" plus bas).

## 6. Exemple de migration decision-free : le footer

`resources/views/components/footer.blade.php` a ete migre en premier, comme
reference pour les lots suivants :
- Le conteneur principal des 5 colonnes recoit `class="biome-conteneur
  biome-grille-5"`, **en plus** de son style inline existant
  (`grid-template-columns:1.6fr 1fr 1fr 1fr 1fr`, `padding:64px 32px 40px`).
- La bande de copyright (bas de footer) recoit `class="biome-conteneur"`.
- **Aucun style inline n'a ete retire.** Il reste la source de verite
  desktop.

Comportement attendu (verifie par lecture du CSS calcule, voir section 7) :

| Viewport | Colonnes du footer | Padding lateral |
|---|---|---|
| 1280px (desktop) | 5 colonnes, ratio `1.6fr 1fr 1fr 1fr 1fr` (inline, inchange) | 32px (inline, inchange) |
| 768px (tablette) | 2 colonnes egales | 24px |
| 375px (mobile) | 1 colonne empilee | 16px |

## 7. Methode de validation

### Captures avant/apres a 1280px

Avant toute migration d'une vue, prendre une capture d'ecran a 1280px de
large. Apres migration (ajout des classes utilitaires), reprendre une
capture identique et comparer pixel a pixel (ou au moins visuellement) :
aucune difference ne doit apparaitre, les classes utilitaires n'ayant aucune
regle active au-dela de 1024px.

### DevTools responsive : 3 viewports de reference

Tester systematiquement aux largeurs suivantes (mode responsive du
navigateur) :
- **375px** (mobile, ex. iPhone SE/12/13)
- **768px** (tablette, ex. iPad portrait)
- **1280px** (desktop, largeur du conteneur maquette)

### Checklist par page/composant migre

- [ ] Pas de scroll horizontal a 375px ni 768px.
- [ ] Cibles tactiles (boutons, liens cliquables) d'au moins 44x44px en
      mobile/tablette.
- [ ] Texte lisible (taille suffisante, contraste conserve — les couleurs de
      texte ne changent pas, elles restent les tokens actes).
- [ ] Rendu a 1280px strictement identique a l'existant (aucune media query
      active).
- [ ] Aucun contenu retire du DOM (voir section 8).

### Limite de cette session (lot M0)

Ce lot n'a pas ouvert de navigateur graphique : la verification a ete faite
par lecture du CSS calcule (les media queries de `mobile.css` matchent bien
les bons breakpoints, les classes sont bien posees sur le footer) et par
`curl` du HTML rendu. **La verification visuelle reelle (DevTools
375/768/1280) reste a faire par le testeur** avant validation finale du lot.

## 8. Consigne absolue : pas de suppression de contenu du DOM

Le responsive de ce projet est **purement CSS**. On ne retire **jamais** de
contenu du DOM cote Blade pour l'adapter au mobile (pas de `@if
($estMobile)` cote serveur, pas de version "allegee" du HTML). Toute
adaptation passe par :
- des classes utilitaires CSS (`display:none` via `.biome-masquer-*`),
- ou des ajustements de mise en page (grille, tailles de police, padding).

Si un besoin de contenu réellement different entre desktop et mobile
apparait (pas juste une question d'affichage), il doit etre remonte au chef
de projet plutot qu'improvise.

## 9. Points d'attention pour les lots M1+

- **Elements fixes/sticky** (`<x-sticky-actions>`, header) : leur position
  n'a pas ete traitee dans ce lot M0 (perimetre limite au footer). A
  reprendre specifiquement, car un bandeau fixe a droite de l'ecran en
  desktop n'a probablement pas de sens tel quel en mobile (a arbitrer avec le
  chef de projet, ne pas improviser).
- **Grilles a card unique tres large** (ex. `1.6fr` en premiere colonne du
  footer) : en tablette/mobile, `.biome-grille-N` remplace tout par des
  fractions egales (`1fr`), ce qui change le ratio des colonnes. C'est
  voulu (une grille a 2 colonnes egales est plus lisible qu'une grille avec
  un ratio desktop applique tel quel), mais a garder en tete si une page a un
  besoin different.
- **Navigation / menu** : le header n'a pas ete migre dans ce lot. Un menu
  burger mobile est tres probablement necessaire (nav du haut + nav
  principale actuelles ne tiennent pas a 375px) — a traiter en priorite au
  lot suivant, potentiellement via `.biome-visible-mobile` /
  `.biome-masquer-tablette-mobile` combines a un peu de JS (a discuter, hors
  perimetre CSS pur de ce lot).
- **Images et media** : aucune regle de redimensionnement d'image
  (`max-width:100%`) n'a ete ajoutee dans ce lot ; a verifier page par page
  au moment de la migration (les images de la maquette sont peut-etre deja
  fluides via leurs conteneurs, a confirmer).
- **Formulaires en plusieurs colonnes** (`<x-formulaire-terrain>`,
  `<x-modale-lead>`) : peuvent avoir des grilles de champs a migrer avec les
  memes classes `.biome-grille-N`, a verifier au cas par cas (couples de
  champs cote a cote en desktop).
- **Nouvelles classes eventuelles** : si un nombre de colonnes hors 2-7
  apparait, ou un palier typographique non couvert, ne pas en inventer un
  ad hoc dans la vue : completer le catalogue de `mobile.css` et ce document
  en consequence.

## 10. Lot M2 — pages vitrine (Accueil, Qui-sommes-nous, Investir, Actualites,
Partenaires)

Les 5 pages vitrine ont ete migrees selon la meme methode (ajout de classes
utilitaires du catalogue existant, aucun style inline retire). Nouvelles
classes ajoutees au catalogue a cette occasion :

| Classe | Usage |
|---|---|
| `.biome-hero-fullbleed` / `.biome-hero-fullbleed__texte` | Hero Accueil (image pleine largeur + texte en position absolute) : hauteur minimale forcee et image en `object-fit:cover` sous 1024px pour eviter que le texte ne deborde verticalement quand l'image redimensionnee devient trop basse. |
| `.biome-hero-image-laterale` | Heros Investir/Partenaires (image de fond à droite, 50% de largeur, sous un degrade) : masquee sous 1024px (bande trop etroite pour rester lisible), le fond de section uni prend le relais. |
| `.biome-accueil__parcours-carte--mobile` | Cartes "parcours" de l'Accueil (carte flex-row, hauteur fixe 300px) : passage en colonne sous 1024px, hauteur auto, bloc visuel redimensionne en hauteur fixe (160px) plutot qu'en largeur en %. |
| `.biome-carrousel-realisations` | Carrousel CSS (Qui-sommes-nous) : ralentit l'animation (`animation-duration`) sous 1024px/480px, l'`overflow:hidden` du conteneur (deja inline) suffit a eviter le scroll horizontal parasite. |
| `.biome-filtre-categorie` | Boutons de filtre (Actualites) : cible tactile portee a 44px de haut minimum sous 1024px. |
| `.biome-pagination__item` | Elements de `pagination/biome.blade.php` : cible tactile 44x44px sous 1024px (38x38px en desktop). |
| `.biome-desactiver-sticky-mobile` | Aside sticky (Actualites) : `position:static` sous 1024px, la grille parente etant passee en 1 colonne (le sticky n'a alors plus de sens). |

Correctifs transverses (constates en recette navigateur reelle a 375px,
apres la migration initiale, voir aussi section Grilles ci-dessus pour le
`min-width:0`) :
- `.biome-accueil__parcours-carte--mobile > div` recoit desormais aussi
  `box-sizing:border-box !important` sous 1024px : sans cela, le
  `padding:26px 24px` du bloc texte (non retire, inline) s'ajoutait a la
  largeur au lieu d'en faire partie, provoquant un debordement horizontal
  d'environ 15px a 375px.
- Regle generique images (`.biome-conteneur img`, `.biome-grille-N img`) :
  `max-width:100% !important` sous 1024px, pour les images a largeur fixe
  en pixels (ex. cartes du carrousel realisations, 300px).

## 11. Lot M1 — layout commun mobile (header + sticky-actions)

Decisions client validees (Laurent, 2026-07-03). Ce lot traite les 2 seuls
elements qui debordaient a 375px : le header (nav du haut + nav principale,
~602px de large) et le bandeau sticky-actions (colonne fixe a droite).

### Header — menu burger (`resources/views/components/header.blade.php`)

En dessous de 1024px, la nav du haut et la nav principale (les 4 liens +
les 2 gros boutons ACHETER/VENDRE) sont masquees via
`.biome-masquer-tablette-mobile` (posee directement sur les 2 `<nav>`
existants, aucun style inline retire). A la place : un bandeau logo + bouton
burger (`.biome-visible-mobile`), et un tiroir plein ecran
(`#biome-tiroir-menu`, egalement `.biome-visible-mobile`) qui liste les memes
routes que le header desktop (`$blockDefs` / `$topItems`, aucune duplication
de logique) :
1. ACHETER, VENDRE MON TERRAIN — mis en avant visuellement (fond bleu
   principal, texte blanc, icone), memes routes que les blocs desktop.
2. Un separateur visuel (ligne fine).
3. Contact, Emplois, Notre entreprise, Partenaires & Sous-traitants — liens
   texte simples, memes routes que la nav du haut desktop.

Fermeture par une croix (`[data-ferme-menu-burger]`) en haut du tiroir.
Ouverture/fermeture geree par `initMenuBurger()` dans `public/js/site.js` :
bloque le scroll du body (`document.body.style.overflow = 'hidden'`) pendant
l'ouverture, ferme sur la croix, sur un clic sur un lien du tiroir, ou la
touche Echap ; gere `aria-expanded` sur le bouton burger et rend le focus au
bouton a la fermeture. Cibles tactiles : bouton burger 44x44px, blocs
ACHETER/VENDRE 56px de haut, liens texte 44px minimum.

Le desktop (`>1024px`) reste strictement inchange : les 2 `<nav>` d'origine
restent affiches tels quels (aucune regle de `mobile.css` active au-dela de
1024px), le bouton burger et le tiroir restent caches
(`.biome-visible-mobile` masque par defaut en desktop, seule exception
`min-width` du fichier).

### Sticky-actions — barre basse mobile (`resources/views/components/sticky-actions.blade.php`)

En desktop (`>1024px`), le composant reste une colonne verticale fixe a
droite, strictement inchangee. En dessous de 1024px, `.biome-sticky-actions`
(classe ajoutee sur le conteneur racine, en plus du style inline existant)
devient une barre horizontale fixe en bas de l'ecran
(`position:fixed;bottom:0;left:0;right:0`), limitee a **2 actions maximum**
par variante. Les blocs non retenus restent dans le DOM (regle d'or : pas de
suppression de contenu) mais recoivent `.biome-masquer-tablette-mobile`
(classe deja existante, reutilisee ici sur des `<a>`/`<button>` plutot que
des sections entieres).

2 actions retenues par variante, en coherence avec ce qu'affiche le
composant desktop :

| Variante | Page(s) | Actions retenues en barre basse |
|---|---|---|
| Standard, sans callback | Accueil, Actualites, Investir, Qui-sommes-nous | "Nous contacter" + "Decouvrez nos biens" (tel: masque) |
| Standard, `hide-biens` sans callback | (aucune page actuelle dans ce cas exact) | "Nous contacter" + "Estimez votre terrain" |
| Standard, `hide-terrain` | Vendre-mon-terrain | "Nous contacter" + "Decouvrez nos biens" (tel: masque) |
| Standard, `callback` actif | Acheter (+ `hide-biens`), Contact | "Nous contacter" + "Etre rappele" (biens/terrain masques s'ils existent) |
| `emplois` | Offre, Offres, Recrutement | "Voir les offres" + "Postuler spontanement" (2 seuls blocs de la variante, rien a masquer) |
| `partner` | Partenaires | "Devenir partenaire" + "Etre contacte" (2 seuls blocs de la variante, rien a masquer) |

Logique portee en PHP dans le composant (`$isCallback`, `$showBiens`,
`$mobileGardeTerrain`) : "Nous contacter" est toujours la 1ere action
retenue en variante standard ; la 2e est "Etre rappele" si `callback` est
actif, sinon "Decouvrez nos biens" (ou "Estimez votre terrain" si
`hide-biens`).

Cibles tactiles : chaque item `flex:1` (largeur egale, `min-height:44px`).
Une bordure de separation verticale (`border-left`, remplace le `border-top`
desktop qui n'a plus de sens en ligne) est posee uniquement entre les 2
items reellement visibles, via un selecteur `~` combine a `:not()` (ignore
la position DOM des items masques).

#### Correctif debordement (constate en recette navigateur reelle a 375px, lot M3)

Verification navigateur reelle sur `/acheter` et `/contact` (variante
`callback`) : `scrollWidth` de 528px au lieu de 375px. Cause precise mesuree :
`min-width:0` manquait sur `.biome-sticky-actions > .biome-sticky__item` — un
enfant flex a `min-width:auto` par defaut (meme piege deja rencontre et
corrige sur `.biome-grille-N`, voir section 5), ce qui l'empeche de se
comprimer sous la largeur intrinseque de son contenu (icone SVG 26-28px +
texte). Consequence : les 2 items visibles de la variante `callback` ("Etre
rappele" + "Nous contacter") faisaient 264px et 265px (529px cumules) au lieu
de se partager les 375px disponibles a parts egales malgre `flex:1 1 0` deja
pose. La variante standard (Accueil) ne presentait pas le probleme par
coincidence de contenu (textes/icones legerement plus courts), mais restait
tout aussi vulnerable au meme defaut structurel.

Correctif (confine a `mobile.css`, `sticky-actions.blade.php` non modifie) :
- `min-width:0 !important` ajoute sur `.biome-sticky-actions > .biome-sticky__item`
  (meme correctif que `.biome-grille-N > *`) : les items peuvent desormais se
  comprimer a parts egales sur la largeur du viewport, quel que soit leur
  contenu.
- `box-sizing:border-box !important` ajoute en complement (le style inline
  ne le posait pas explicitement sur ces items) : le padding lateral reste
  inclus dans la largeur flexible calculee plutot que de s'y ajouter.
- Padding lateral reduit de 8px (inline desktop) a 6px sous 1024px : marge de
  securite supplementaire sur les tres petits ecrans (ex. 320px).
- `overflow-wrap:break-word` ajoute sur le `<span>` de libelle : filet de
  securite si un mot isole restait malgre tout plus large que l'espace
  disponible sur un ecran extremement etroit.

Verifie : `scrollWidth === 375` sur `/acheter` et `/contact` a 375px (les 2
pages en variante `callback`), Accueil (variante standard) et desktop
(`>1024px`, colonne verticale a droite) non affectes — toutes les regles
ajoutees restent confinees a `@media (max-width:1024px)`.

### Nouvelles classes ajoutees au catalogue

| Classe | Usage |
|---|---|
| `.biome-header__ligne-principale` | Ligne logo + nav principale du header (grille `1fr auto 1fr` desktop) : passe en `1fr auto` sous 1024px (la colonne centrale nav principale est masquee) et reduit sa hauteur fixe (108px -> 72px) pour un bandeau mobile compact. |
| `.biome-header__conteneur` | Conteneur interne du header (`max-width` + padding lateral 32px inline) : padding reduit a 16px sous 1024px (voir correctif transverse ci-dessous). |
| `.biome-header__logo` | Lien logo du header : l'image passe de 90px a 44px de haut sous 1024px (voir correctif transverse ci-dessous). |
| `.biome-sticky-actions` | Conteneur racine de `<x-sticky-actions>` : transforme la colonne verticale desktop en barre horizontale fixe en bas sous 1024px (voir detail ci-dessus). |

`.biome-visible-mobile` et `.biome-masquer-tablette-mobile` (deja au
catalogue, section 5) sont reutilisees telles quelles sur le bouton burger /
le tiroir et sur les items sticky-actions non retenus : aucune nouvelle
classe de visibilite necessaire.

### Correctif transverse (constate en recette navigateur reelle a 375px)

Premiere verification navigateur du testeur : header mobile plus de
debordement de 602px (grille passee en `1fr auto`, bien), mais un
debordement horizontal residuel de ~10px (`scrollWidth` 385px vs viewport
375px) subsistait. Cause mesuree precisement : `.biome-header__conteneur`
gardait son padding lateral desktop de 32px (`--padding-lateral-conteneur`,
inline) meme sous 1024px, ne laissant que 311px utiles a 375px de large pour
le logo (285px, "BIOME CONSTRUCTIONS") + le bouton burger (44px + 8px de
marge) = 337px necessaires > 311px disponibles.

Correctif retenu (le plus fidele et le plus coherent avec le reste du
catalogue mobile) :
- `.biome-header__conteneur` : padding lateral reduit a 16px sous 1024px
  (meme valeur que `.biome-conteneur` en mobile pur, `<=480px` — applique
  ici des `<=1024px` sans palier tablette intermediaire, le header n'ayant
  pas de contenu variable justifiant un palier a 24px). Conteneur utile
  remonte a 343px (375 - 2*16), marge suffisante pour les 337px necessaires.
- `.biome-header__logo img` : hauteur reduite en complement de 90px a 44px
  sous 1024px (ratio naturel conserve, `width:auto` inline non modifie),
  pour un bandeau mobile plus compact et une marge de securite renforcee,
  coherente avec la reduction de `.biome-header__ligne-principale` a 72px
  de haut.

Aucun style inline retire, aucune regle active au-dela de 1024px : le
desktop (1280px) reste strictement identique (logo 90px, padding 32px).

### Z-index — choix documente

Valeurs deja en jeu ailleurs dans le site (inline, non modifiees) :
header sticky desktop `z-index:50`, sticky-actions desktop `z-index:40`,
modales plein ecran (`<x-modale-lead>`, `<x-modale-candidature>`,
`<x-formulaire-terrain>` variante modale) `z-index:70`.

Choix retenus pour le lot M1 :
- **Tiroir menu burger** (`#biome-tiroir-menu`) : `z-index:60`, pose en
  inline (actif a tout breakpoint ou le tiroir peut s'afficher, pas besoin
  d'une regle dans `mobile.css`). Au-dessus du header (50) pour bien le
  recouvrir a l'ouverture, en dessous des modales (70) : si un lien du
  tiroir ouvrait une modale (cas non present actuellement, mais anticipe),
  la modale doit passer au-dessus, jamais l'inverse.
- **Sticky-actions mobile** (`.biome-sticky-actions` sous 1024px) :
  `z-index:45`, confine a `mobile.css`. Entre le sticky-actions desktop (40,
  qui ne s'applique plus a ce breakpoint) et le header (50) : reste visible
  en permanence au-dessus du contenu de page, mais sous le tiroir (60) et
  les modales (70, y compris les futures modales plein ecran du lot M4) —
  une modale ouverte doit toujours recouvrir entierement la barre basse.

### Padding-bottom du body (compense la barre basse fixe)

La barre basse fixe masquerait le bas de page / le footer sans compensation
(regle d'or : ne pas cacher de contenu). Regle ajoutee, confinee a la media
query `(max-width:1024px)` :

```css
body:has(.biome-sticky-actions) {
  padding-bottom: 100px;
}
```

Le selecteur `:has()` limite l'effet aux seules pages qui incluent
effectivement `<x-sticky-actions>` (ex : la page Bien, qui n'en a pas dans
la maquette, n'est pas impactee). Support navigateur : Chrome/Edge/Safari
depuis 2023, Firefox >=121 ; sur un navigateur plus ancien, seul ce confort
de marge est perdu (aucune regression fonctionnelle, la barre basse reste
utilisable, juste potentiellement collee au contenu qui suit).

### JavaScript ajoute

`initMenuBurger()` dans `public/js/site.js` (voir aussi
`docs/frontend-composants.md`) : cible `[data-ouvre-menu-burger]` (bouton
du header) et `[data-tiroir-menu-burger]` (le tiroir). Ne fait rien si l'un
des deux est absent de la page (comme toutes les fonctions de `site.js`).

## 12. Lot M3 — pages Acheter et Bien

Decisions client validees (Laurent, 2026-07-03). Ces 2 pages sont les plus
riches du site et debordaient encore lourdement en mobile (Acheter ~700px de
large a 375px). Priorite absolue donnee a la page Acheter.

### Page Acheter (`resources/views/pages/acheter.blade.php`)

**1 bien par ligne.** La grille de resultats recoit `.biome-grille-3`
(catalogue standard section 5 : 3 colonnes desktop inchange -> 2 colonnes
tablette -> 1 colonne mobile). `<x-property-card>` n'a pas eu besoin de
modification structurelle : deja en `flex-direction:column` avec des boutons
`flex:1`, elle s'adapte naturellement a une largeur pleine.

**Filtres en tiroir plein ecran.** Le `<form>` de filtres desktop (barre
horizontale) est repris **tel quel** et transforme en tiroir sous 1024px via
la nouvelle classe `.biome-filtres-tiroir-mobile` : aucune duplication de
DOM, un seul formulaire, un seul submit GET vers `route('acheter')` (comme le
desktop). Un bouton "Filtrer" (`.biome-visible-mobile`, badge "actifs" si des
filtres sont poses) ouvre le tiroir ; une croix de fermeture et un bouton
"Voir les X biens" (remplace visuellement le bouton "Rechercher" desktop,
masque en mobile via `.biome-masquer-tablette-mobile`) le referment/soumettent.
Nouvelle fonction JS `initTiroirFiltres()` dans `site.js` : meme pattern que
`initMenuBurger()` (classe togglee, scroll du body bloque a l'ouverture,
fermeture par croix ou touche Echap, focus rendu au bouton).

**Liste de provinces a cocher (au lieu du SVG).** Le panneau
`data-panneau-carte-provinces` existait deja avec une double interface (SVG
cliquable + labels a cocher, tous deux pilotant la meme case reelle
`[data-province-case]`) : aucune duplication necessaire. Nouvelle classe
`.biome-panneau-provinces-mobile` force son affichage (`display:grid
!important`, independant du `display:none` inline pilote par le JS desktop)
et le passe en 1 colonne sous 1024px, dans le tiroir de filtres. Le SVG est
masque (`.biome-masquer-tablette-mobile` pose directement sur le `<svg>`) et
la liste de labels recoit `.biome-grille-2` (2 colonnes -> 1 en mobile pur).
Le bouton toggle carte (`data-toggle-carte-provinces`, ouverture desktop du
panneau) est egalement masque en mobile : inutile, le panneau est toujours
visible dans le tiroir.

**Vue carte masquee.** La bascule liste/carte (boutons `data-vue-liste` /
`data-vue-carte`) et le panneau `data-panneau-vue-carte` recoivent
`.biome-masquer-tablette-mobile` : seule la vue liste reste disponible en
mobile/tablette, conforme a la decision client.

### Page Bien (`resources/views/pages/bien.blade.php`)

**Grille principale.** Nouvelle classe `.biome-bien-grille-principale`
(ratio `1fr 360px` specifique, hors catalogue `.biome-grille-N` a fractions
egales) passe en 1 colonne sous 1024px. L'ordre DOM (contenu principal puis
`<aside>`) place naturellement la sidebar sous le contenu, aucune
reorganisation JS necessaire.

**Galerie en scroll tactile.** Le H1 de la galerie recoit
`.biome-titre-section-lg` (38px desktop, coherent avec le catalogue existant,
pas `.biome-titre-hero` qui est pense pour des H1 plus grands 46-52px). Le
bandeau `heroFacts` (5 blocs en ligne) recoit une nouvelle classe
`.biome-bien-hero-facts` : passe en grille 2 colonnes (tablette) puis 1
colonne (mobile pur), bordures/paddings de separation laterale retires (plus
de sens hors alignement en ligne). La grille de vignettes (5 colonnes fixes)
recoit `.biome-bien-vignettes` : bascule en `display:flex` avec
`overflow-x:auto` + `scroll-snap-type:x proximity`, chaque vignette a une
largeur fixe de 140px. Le swipe tactile est **gere nativement par le
navigateur** (aucun JS ajoute) ; le tap ouvre toujours la lightbox plein
ecran existante (`data-galerie-item`, comportement JS `initGalerieBien()`
inchange). Les 2 boutons precedent/suivant decoratifs (deja
`aria-hidden`/`tabindex="-1"` desktop) sont masques en mobile.

**Sidebar sous le contenu + barre prix sticky basse.** La sidebar (prix, CTA,
formulaire "Interesse par ce bien ?") passe sous le contenu principal via la
grille a 1 colonne ci-dessus (aucune classe propre necessaire sur `<aside>`).
Un nouveau bloc `.biome-bien-barre-prix-mobile` (prix + bouton "Planifier une
visite", cible la meme modale `data-ouvre-modale="visite"` que les CTA de la
sidebar) est ajoute juste apres la section MAIN GRID : `display:none` par
defaut (inline), passe en barre fixe basse (`position:fixed;bottom:0`) sous
1024px. Ce n'est **pas** une duplication de la sidebar (formulaire et autres
CTA restent uniquement dans `<aside>`, plus haut dans le DOM) : seul un
recapitulatif compact prix + 1 CTA est ajoute pour rester visible en
permanence, la sidebar complete etant deplacee sous le contenu.

**Toutes les grilles multi-colonnes restantes** recoivent soit le catalogue
standard (`.biome-grille-2`, `.biome-grille-4`, `.biome-grille-6` pour
Description+Infos, sous-grille Confiance, Pourquoi choisir), soit une
nouvelle classe dediee quand le ratio desktop est specifique et non couvert
par `.biome-grille-N` (fractions egales uniquement) :
- `.biome-bien-grille-plans` (Plans, `repeat(3,1fr) 1.1fr` -> 2 colonnes
  tablette -> 1 mobile) ;
- `.biome-bien-grille-situation` (Situation, `0.9fr 1.4fr 0.9fr` -> 1 colonne
  directement des la tablette, 3 blocs heterogenes non appariables en 2
  colonnes) ;
- `.biome-bien-grille-similaires` (Biens similaires, `repeat(3,1fr) 1fr` -> 2
  colonnes tablette -> 1 mobile) ;
- `.biome-bien-grille-confiance` (bloc Confiance, `1.6fr 1fr` -> 1 colonne ;
  le second bloc "Des questions ?" perd sa bordure/padding gauche de
  separation au profit d'une bordure/padding superieurs, meme logique que la
  bordure du sticky-actions mobile qui passe de `border-top` a
  `border-left`).

### Nouvelles classes ajoutees au catalogue

| Classe | Usage |
|---|---|
| `.biome-filtres-tiroir-mobile` | Formulaire de filtres Acheter : transforme en tiroir plein ecran sous 1024px (`z-index:60`, meme niveau que le tiroir menu burger). Ouvert/ferme par `initTiroirFiltres()`. |
| `.biome-panneau-provinces-mobile` | Panneau carte des provinces (Acheter) : force son affichage en grille 1 colonne dans le tiroir mobile, independamment du toggle JS desktop. |
| `.biome-bien-grille-principale` | Grille principale de la page Bien (`1fr 360px`) : 1 colonne sous 1024px. |
| `.biome-bien-hero-image` | Photo principale de la galerie (Bien) : hauteur reduite progressivement (540 -> 440 -> 380px). |
| `.biome-bien-hero-facts` | Bandeau surface/chambres/sdb/terrain/PEB (Bien) : grille 2 colonnes (tablette) puis 1 (mobile). |
| `.biome-bien-vignettes` | Grille de vignettes de la galerie (Bien) : scroll horizontal tactile natif (`overflow-x:auto` + `scroll-snap`), vignettes a largeur fixe 140px. |
| `.biome-bien-grille-plans` | Grille "Plans de la maison" (Bien, `repeat(3,1fr) 1.1fr`). |
| `.biome-bien-grille-situation` | Grille "Situation du bien" (Bien, `0.9fr 1.4fr 0.9fr`) : 1 colonne des la tablette. |
| `.biome-bien-grille-similaires` | Grille "Biens similaires" (Bien, `repeat(3,1fr) 1fr`). |
| `.biome-bien-grille-confiance` | Bloc "Pourquoi faire confiance" (Bien, `1.6fr 1fr`). |
| `.biome-bien-barre-prix-mobile` | Recapitulatif prix + CTA "Planifier une visite" en barre fixe basse (Bien), `z-index:45`, compense par `body:has(.biome-bien-barre-prix-mobile){padding-bottom:90px}`. |

### Z-index — recapitulatif complet (tous lots confondus)

| Element | z-index | Confinement |
|---|---|---|
| Sticky-actions desktop | 40 | inline, `>1024px` uniquement actif |
| Sticky-actions mobile (`.biome-sticky-actions`) | 45 | `mobile.css`, `<=1024px` |
| Barre prix mobile Bien (`.biome-bien-barre-prix-mobile`) | 45 | `mobile.css`, `<=1024px` (page Bien uniquement, pas de conflit avec le sticky-actions puisque cette page n'en a pas) |
| Header sticky desktop | 50 | inline, tous breakpoints |
| Tiroir menu burger (`#biome-tiroir-menu`) | 60 | inline, tous breakpoints ou actif |
| Tiroir de filtres Acheter (`.biome-filtres-tiroir-mobile`) | 60 | `mobile.css`, `<=1024px` |
| Modales de demande / candidature | 70 | inline, tous breakpoints |
| Lightbox galerie Bien (`[data-lightbox]`) | 80 | inline, tous breakpoints |

### JavaScript ajoute

`initTiroirFiltres()` dans `public/js/site.js` : cible
`[data-ouvre-tiroir-filtres]` (bouton "Filtrer" du header de page Acheter) et
`[data-tiroir-filtres]` (le formulaire de filtres lui-meme). Meme pattern que
`initMenuBurger()` : classe togglee (`biome-filtres-tiroir-mobile--ouvert`),
scroll du body bloque a l'ouverture, fermeture par `[data-ferme-tiroir-filtres]`
ou la touche Echap, focus rendu au bouton a la fermeture. Ne gere que
l'affichage du tiroir, jamais la soumission du formulaire (le bouton "Voir
les X biens" est un `type="submit"` natif).

## 13. Lot M4 — formulaires transverses (modales et pages Contact / Vendre-mon-terrain)

Decisions client validees (Laurent, 2026-07-03). Perimetre : les 3 composants
qui ouvrent une modale de formulaire (`<x-modale-lead>`, `<x-modale-candidature>`,
`<x-formulaire-terrain>` variante `modale`) et les 2 pages qui les utilisent le
plus (Contact, Vendre-mon-terrain).

### Modales plein ecran sous 1024px

En desktop (`>1024px`), aucun changement : carte centree (`max-width:520-560px`,
`border-radius:18px`, overlay `position:fixed;inset:0;display:flex`), tout en
inline, strictement inchange.

Sous 1024px, 2 classes ajoutees en plus de l'inline existant sur les 3
composants :
- `.biome-modale-lead` sur le conteneur racine (l'overlay `position:fixed;inset:0`) —
  purement documentaire/reperage, aucune regle CSS dessus (l'overlay n'a pas
  besoin de changer, seule la boite interne est adaptee).
- `.biome-modale-lead__boite` sur la boite interne (carte blanche) : passe en
  plein ecran (`width:100%;height:100%;max-width:100%;max-height:100%`,
  `border-radius:0`, `box-shadow:none`), en `display:flex;flex-direction:column`
  pour que le formulaire (dernier enfant) puisse grandir et rester scrollable
  (`overflow-y:auto` deja present en inline, conserve).
- Le bandeau titre + croix de fermeture (premier enfant direct de la boite,
  deja un `<div style="display:flex;justify-content:space-between">` inline
  dans les 3 composants) devient `position:sticky;top:0` avec un fond blanc
  ajoute : reste visible en permanence pendant le scroll du contenu (utile
  sur les formulaires longs comme `visite` ou l'etape 2 de candidature/terrain).
- Le `<form>` (second enfant direct de la boite) recoit `flex:1 1 auto` pour
  occuper l'espace restant : le bouton de soumission, en pied de formulaire,
  reste atteignable en scrollant tout le contenu de la modale (jamais coupe
  hors-ecran).

Pour `<x-formulaire-terrain>`, ces 2 classes ne sont posees **que** sur la
variante `modale` (jamais sur `section-claire`/`section-sombre`, qui ne sont
pas des overlays) : ajoutees conditionnellement (`@if ($estModale)`) au meme
endroit que les autres attributs `data-modale-lead`/`data-type-modale`.

**Z-index** : 70, deja pose en inline sur les 3 composants (aucun changement).
Toujours au-dessus du tiroir menu burger (60), du tiroir de filtres Acheter
(60) et de la barre sticky-actions mobile (45) — voir tableau recapitulatif
section 12.

**Blocage du scroll body** : deja gere par le JS existant
(`initModaleCandidature()` et `initModalesLead()` dans `site.js` — cette
derniere couvre aussi la variante modale de `<x-formulaire-terrain>` via le
meme attribut `[data-modale-lead]`) : `document.body.style.overflow = 'hidden'`
a l'ouverture, restaure a la fermeture. **Aucun JS ajoute pour ce lot.**

### Champs de formulaire en 1 colonne

Toutes les grilles de champs `1fr 1fr` des 3 composants recoivent
`.biome-grille-2` (catalogue existant, section 5) : nom/telephone, email/date,
CV/lettre de motivation, disponibilite/experience (candidature), nom/adresse/
commune (terrain), etc. Empilement automatique sous 1024px, `min-width:0`
deja fourni par la regle generique `.biome-grille-2 > *` du catalogue.

### Formulaires 2 etapes : mecanisme JS inchange

`initFormulairesTerrainEtapes()` (navigation etape 1/2 de `<x-formulaire-terrain>`)
et la navigation interne de `<x-modale-candidature>` ne sont pas modifies : le
lot ne touche qu'a l'empilement visuel des champs a l'interieur de chaque
etape, jamais a la logique de bascule d'etape elle-meme.

### Page Contact (`resources/views/pages/contact.blade.php`)

Migration au catalogue existant (`.biome-conteneur`, `.biome-grille-2/3`,
`.biome-titre-hero/-section-lg/-sous-section`) pour le hero, les 3 blocs
coordonnees, les 3 acces rapides, le bloc "Pourquoi contacter Biome", la
grille de champs standard (maison/question), le bloc callback et le bandeau
final. Nouvelles classes dediees (ratios hors catalogue standard) :

| Classe | Usage |
|---|---|
| `.biome-boutons-hero-wrap` | Les 3 CTA du hero (`flex-wrap:nowrap` desktop) passent en `wrap` sous 1024px : sans cela, ils debordaient des la tablette. |
| `.biome-contact-grille-form` | Grille "form + bloc Pourquoi contacter" (`1.4fr 1fr`) : 1 colonne sous 1024px. |
| `.biome-contact-grille-carte` | Grille "carte + coordonnees bureaux" (`1.6fr 1fr`) : 1 colonne sous 1024px. |
| `.biome-contact-grille-banner` | Grille du bandeau final (`38% 1fr`, image + texte) : 1 colonne sous 1024px, le bloc image recoit une hauteur fixe reduite (200px) au lieu d'heriter d'une hauteur de ligne de grille. |

Le hero (image de fond a droite en `width:52%`, meme pattern que les heros
Investir/Partenaires) recoit `.biome-hero-image-laterale` (catalogue existant,
section 10) : masquee sous 1024px.

Les 3 boutons de selection du besoin ("Je cherche une maison" / "Je vends un
terrain" / "J'ai une question", petits boutons icone+libelle compacts)
recoivent `.biome-grille-3` par coherence avec le reste du catalogue (memes
regles, m^eme comportement d'empilement que toute autre grille de cartes) :
ils s'empilent verticalement sous 1024px, restent pleinement cliquables.

### Page Vendre mon terrain (`resources/views/pages/vendre-mon-terrain.blade.php`)

Migration au catalogue existant pour le hero (`.biome-conteneur`,
`.biome-titre-hero`), les sections "Pourquoi vendre" et "Process" (4 colonnes,
`.biome-grille-4`), "Quels terrains" (`.biome-grille-2`), "Analyse pertinente"
(`.biome-grille-4`), "Temoignages" (`.biome-grille-3`) et la FAQ
(`.biome-grille-2`). La FAQ accordeon reutilise `initAccordeonFaq()` (site.js,
inchange) : boutons deja a 17px de padding vertical (>44px de cible tactile
avec le texte), aucun correctif necessaire.

Nouvelle classe dediee :

| Classe | Usage |
|---|---|
| `.biome-terrain-grille-form` | Grille "form terrain (section sombre) + bloc reassurance" (`1.25fr 1fr`) : 1 colonne sous 1024px, padding interne du cadre reduit (44px desktop -> 28px/20px, meme logique que le correctif newsletter Acheter du lot M3, sinon trop large en mobile). |

Barre sticky-actions basse (variante `hide-terrain`, deja geree par M1) :
verifiee, ne masque pas le bas de la section formulaire terrain sombre grace
au `padding-bottom` du body deja pose par M1
(`body:has(.biome-sticky-actions)`).

### Nouvelles classes ajoutees au catalogue (recapitulatif M4)

| Classe | Usage |
|---|---|
| `.biome-modale-lead` | Conteneur racine des 3 modales de formulaire (marqueur, aucune regle CSS propre). |
| `.biome-modale-lead__boite` | Boite interne des 3 modales : plein ecran sous 1024px (voir detail plus haut). |
| `.biome-boutons-hero-wrap` | Hero Contact : 3 CTA passent en `flex-wrap:wrap` sous 1024px. |
| `.biome-contact-grille-form` | Contact, grille form + "Pourquoi contacter" (`1.4fr 1fr`). |
| `.biome-contact-grille-carte` | Contact, grille carte + coordonnees (`1.6fr 1fr`). |
| `.biome-contact-grille-banner` | Contact, grille bandeau final (`38% 1fr`). |
| `.biome-terrain-grille-form` | Vendre-mon-terrain, grille form sombre + reassurance (`1.25fr 1fr`). |

## 14. Lot M5 — pages emploi (Recrutement, Offres, Offre)

Decisions client validees (Laurent, 2026-07-03). Perimetre : les 3 pages du
parcours emploi (`resources/views/pages/recrutement.blade.php`,
`offres.blade.php`, `offre.blade.php`). La modale de candidature (2 etapes,
`components/modale-candidature.blade.php`) est deja rendue plein ecran par le
lot M4 (`.biome-modale-lead__boite`) : non retouchee ici.

### Page Recrutement

Migration au catalogue existant uniquement, aucune classe dediee necessaire :
`.biome-conteneur` sur le hero et les 5 sections, `.biome-titre-hero` (H1),
`.biome-titre-section` (34/32/28px : "Pourquoi rejoindre Biome ?", "Nos
offres d'emploi"), `.biome-titre-section-lg` (30px : "La vie chez Biome"),
`.biome-grille-4` sur les 4 grilles a 4 colonnes ("Pourquoi rejoindre" (4
valeurs), "La vie chez Biome" (4 images), "Nos valeurs" (4 items), bloc des
offres resumees).

### Page Offres (liste)

Catalogue existant pour le hero (`.biome-conteneur`, `.biome-titre-hero`) et
la bande finale (`.biome-conteneur`). Sidebar candidature spontanee
(`position:sticky`) desactivee via `.biome-desactiver-sticky-mobile`
(catalogue existant, meme logique que l'aside Actualites) : elle se retrouve
naturellement sous la liste d'offres une fois la grille corps passee en 1
colonne.

Nouvelles classes dediees :

| Classe | Usage |
|---|---|
| `.biome-offres-grille-body` | Grille corps (`1fr 320px`, liste + sidebar candidature) : 1 colonne sous 1024px. |
| `.biome-offres-carte` | Carte offre horizontale (cercle initiale + infos + bouton "Voir l'offre") : passe en colonne sous 1024px, cercle centre au-dessus, bouton etire en pleine largeur (cible tactile confortable). Voir detail ci-dessous. |
| `.biome-offres-select-tri` | Select "Trier par" : hauteur portee a 44px sous 1024px (cible tactile, desktop 42px). |

**Carte offre horizontale.** La carte desktop est un `display:flex;
align-items:center` a 3 blocs (cercle initiale 84px, bloc infos `flex:1`,
bloc action `flex:none` aligne a droite). Sous 1024px, `.biome-offres-carte`
passe en `flex-direction:column`, le cercle initiale (premier enfant) se
recentre via `align-self:center`, le bloc infos (`.biome-offres-carte__infos`,
pose sur le 2e enfant direct) s'etire en pleine largeur (le paragraphe
description perd son `max-width:480px` desktop) et se centre (les lignes
lieu/contrat), le bloc action (`.biome-offres-carte__action`, pose sur le 3e
enfant direct) s'etire aussi, le bouton "Voir l'offre" devenant pleine
largeur pour une cible tactile confortable.

### Page Offre (detail)

Catalogue existant pour le hero (`.biome-conteneur`, `.biome-titre-hero`), la
meta bar (5 infos en ligne, `.biome-grille-5`), la section "Pourquoi
rejoindre" (5 colonnes, `.biome-grille-5`, `.biome-titre-section-lg`), les
sections missions/profil (`.biome-grille-2`, ratio `1fr 1fr` egal donc
catalogue standard, `.biome-titre-sous-section`), "Ce que nous vous offrons"
(5 colonnes avantages, `.biome-grille-5`, `.biome-titre-sous-section`),
"Notre processus de recrutement" (4 etapes, `.biome-grille-4`,
`.biome-titre-sous-section`) et "D'autres opportunites" (4 colonnes,
`.biome-grille-4`, `.biome-titre-sous-section`).

La ligne de connexion horizontale du bloc "Process" (`position:absolute`
entre les 4 puces numerotees, pensee pour un alignement en ligne) n'a plus de
sens une fois la grille passee en colonnes sous 1024px : masquee via
`.biome-masquer-tablette-mobile` (catalogue existant), sans retirer l'element
du DOM.

Nouvelle classe dediee :

| Classe | Usage |
|---|---|
| `.biome-offre-grille-faq` | Grille FAQ (`1.3fr 1fr`, accordeon + visuel placeholder) : 1 colonne sous 1024px. L'accordeon FAQ reste pilote par `initAccordeonFaq()` (site.js, inchange), deja tactile. |

### Nouvelles classes ajoutees au catalogue (recapitulatif M5)

| Classe | Usage |
|---|---|
| `.biome-offres-grille-body` | Offres (liste), grille corps `1fr 320px`. |
| `.biome-offres-carte` | Offres (liste), carte offre horizontale -> colonne sous 1024px. |
| `.biome-offres-select-tri` | Offres (liste), select de tri : cible tactile 44px. |
| `.biome-offre-grille-faq` | Offre (detail), grille FAQ `1.3fr 1fr`. |

## 15. Recette mobile (M6) — synthese

Le chantier mobile est **termine**. Les lots M0 a M5 sont fusionnes sur
`main`. Ce lot M6 est une recette finale au navigateur reel (pas de nouveau
code), qui cloture le chantier.

### Rappel de la strategie

Strategie **hybride bornee** (voir section 1) : on garde tout l'inline
desktop existant intact (couleurs, ombres, rayons, polices — les tokens de
la maquette), et on extrait en classes CSS utilitaires, confinees a
`public/css/mobile.css` et a des media queries `max-width:1024px` /
`max-width:480px`, uniquement les proprietes structurelles qui doivent
changer par breakpoint (grille, conteneur, taille de titre, affichage). Le
desktop (`>1024px`) n'a **jamais** de regle active dans `mobile.css` : c'est
la reference pixel-perfect, strictement inchangee du debut a la fin du
chantier.

### Tableau recapitulatif des lots

| Lot | Perimetre |
|---|---|
| M0 | Fondations : `public/css/mobile.css`, breakpoints actes (480/1024), catalogue de classes utilitaires de base (`.biome-grille-N`, `.biome-conteneur`, echelle de titres, visibilite), migration du footer en exemple de reference. |
| M1 | Layout commun : header (menu burger + tiroir plein ecran sous 1024px) et `<x-sticky-actions>` (colonne desktop -> barre basse fixe mobile/tablette, 2 actions max par variante). |
| M2 | Pages vitrine : Accueil, Qui-sommes-nous, Investir, Actualites, Partenaires (heros, grilles de cartes, carrousel realisations, filtres, pagination). |
| M3 | Pages Acheter (filtres en tiroir, liste de provinces a cocher, vue carte masquee, grille de resultats 1 colonne) et Bien (galerie en scroll tactile, sidebar sous le contenu, barre prix sticky basse). |
| M4 | Formulaires transverses : les 3 modales de formulaire (`<x-modale-lead>`, `<x-modale-candidature>`, `<x-formulaire-terrain>` variante modale) passent plein ecran sous 1024px ; pages Contact et Vendre-mon-terrain migrees au catalogue. |
| M5 | Pages emploi : Recrutement, Offres (liste, carte offre horizontale -> colonne), Offre (detail, meta bar, FAQ). |
| M6 | Recette finale au navigateur reel (ce lot) : aucun code ajoute, verification systematique des 13 pages. |

### Resultat de la recette

**Les 13 pages publiques sont propres a 375px, 768px et 1280px.**

- **375px (mobile)** : `scrollWidth` du document egal a la largeur du
  viewport sur les 13 pages (aucun debordement horizontal residuel).
- **768px (tablette)** : verifie en detail sur la page Acheter (2 colonnes de
  resultats, filtres en tiroir, aucun debordement) — comportement identique
  attendu et coherent sur le reste du site, qui partage le meme catalogue de
  classes utilitaires.
- **1280px (desktop)** : rendu strictement intact sur l'ensemble du site,
  verifie lot par lot au fil du chantier (aucune regle de `mobile.css`
  active au-dela de 1024px) et reconfirme en synthese lors de cette recette.

**Methode** : verification au navigateur reel (pas seulement DevTools/CSS
calcule comme au lot M0) — mesure du `scrollWidth` du document a chaque
largeur de reference (375/768/1280) sur les 13 pages, comparee au
`innerWidth` du viewport ; non-regression desktop controlee page par page a
chaque lot (voir sections 7 et suivantes), puis re-verifiee de maniere
transversale a ce stade final.

### Points connus / assumes qui restent

- **Optimisation des images** : hors perimetre de ce chantier responsive.
  Aucune regle de compression, de `srcset`/`sizes`, ou de format moderne
  (WebP/AVIF) n'a ete ajoutee. C'est un lot separe a venir, a planifier avec
  le chef de projet.
- **Maquette source desktop-only** : la maquette Claude Design d'origine n'a
  jamais eu de version mobile ou tablette. Les choix d'adaptation (empilement
  des grilles, menu burger, barre sticky basse a 2 actions, modales plein
  ecran, etc.) sont donc des **decisions de conception validees par le
  client (Laurent, 2026-07-03)**, documentees lot par lot dans ce fichier —
  pas la reproduction pixel-perfect d'une maquette mobile qui n'existe pas.
  Toute evolution future de ces choix doit rester une decision explicite,
  pas une improvisation au fil d'une autre tache.

## Voir aussi

- `docs/design-tokens.md` — tokens de design (couleurs, polices, espacements),
  non dupliques dans `mobile.css` : seules les proprietes structurelles
  (grille, conteneur, taille de titre, affichage) sont extraites.
- `docs/frontend-composants.md` — composants Blade partages (header, footer,
  sticky-actions...) a migrer au fil des lots suivants.
- `public/css/mobile.css` — implementation de ce document.
