/* Feuille de style du formulaire public de demande d'intervention.
   Ecrite a la main, sans framework et sans etape de construction (ADR 0003).

   ═══════════════════════════════════════════════════════════════════════════
   D'OU VIENT CE DESIGN, ET CE QUI EN A ETE CHANGE
   ═══════════════════════════════════════════════════════════════════════════
   Il reprend la maquette validee par le porteur le 2026-07-29 : noir et blanc,
   un ecran par etape, champs soulignes sans boite, boutons pilule, logo en haut
   a gauche, barre de progression fine.

   TROIS CHOSES ONT ETE REMONTEES PAR RAPPORT A LA MAQUETTE, sur decision du
   porteur, parce que les criteres de recette de la spec 01 les exigent et que le
   public vise a 45-60 ans et travaille debout dans des locaux techniques :

     | Element        | Maquette          | Ici                                |
     |----------------|-------------------|------------------------------------|
     | Libelles       | 10 px, #a0a0a0    | 16 px, 7,0:1 de contraste          |
     | Consignes      | 13,5 px           | 16 px                              |
     | Corps de texte | 15 px             | 17 px                              |
     | Cibles         | ~40 px de haut    | 48 px minimum                      |
     | Boutons radio  | display:none      | visibles et atteignables au clavier|

   ⚠️ LE PIEGE A NE PAS REFAIRE : la maquette masque ses boutons radio avec
   display:none. Un element en display:none SORT DU PARCOURS CLAVIER : la touche
   Tab ne l'atteint plus, et un lecteur d'ecran ne l'annonce pas. Le choix
   « particulier / professionnel » deviendrait impossible sans souris. Ici, les
   radios restent dans le flux, rendus visuellement sous forme d'onglets.

   ⚠️ SECOND PIEGE, DEJA PAYE LE 2026-07-28 : la hauteur d'une cible se pose sur
   le LIBELLE, pas sur le bloc qui l'entoure. Cliquer dans le vide autour d'une
   case ne la coche pas.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══ Les deux themes ══════════════════════════════════════════════════════
   AUCUNE COULEUR N'EST ECRITE EN DUR AILLEURS QUE DANS CES DEUX BLOCS. C'est ce
   qui rend la bascule jour/nuit possible : changer un attribut sur <html> suffit,
   et il n'y a pas un seul endroit oublie qui resterait blanc sur blanc.

   ⚠️ LE CONTRASTE EST UN CRITERE DE RECETTE, ET IL VAUT DANS LES DEUX THEMES.
   Chaque couleur porte son rapport mesure. Une teinte ajoutee sans sa mesure est
   une regression qui ne se verra pas avant qu'un client se plaigne. */

:root {
  /* 🔴 GRIS TRES CLAIR DEPUIS LE 2026-08-12 (demande du porteur : « pas un blanc »,
     meme mouvement que le fond sombre passe de #101010 a #202020 le meme jour) —
     valait #ffffff. CONSEQUENCE MESUREE, PAS ACCESSOIRE : --texte-faible tenait
     4,54:1 sur blanc — LE STRICT MINIMUM au-dessus du seuil de 4,5:1, sans aucune
     marge. Le moindre assombrissement du fond le faisait passer sous le seuil (un
     simple #fafafa suffisait : verifie par script, 4,35:1). --texte-faible est donc
     RE-REGLE en meme temps, sur le meme principe que --carte-fond au back-office le
     2026-08-09 : une couleur de texte se cale sur le fond le plus sombre qu'elle
     rencontre, jamais sur le plus clair. --survol suit aussi, pour rester un cran
     EN DESSOUS du nouveau fond (c'etait deja le sens : #f4f4f4 sous #ffffff). */
  /* 🔴 UN CRAN PLUS GRIS LE 2026-08-13 (demande du porteur) : #f2f2f2 -> #e8e8e8,
     le meme gris que les cartes du back-office. C'EST LA LIMITE BASSE, ET ELLE EST
     MESUREE : a #e5e5e5, la bordure des champs tombe a 2,93:1, sous le seuil de 3:1.
     Descendre plus bas obligerait a foncer d'autres couleurs — ce serait un autre
     travail, pas un reglage de teinte.
     ⚠️ DEUX VALEURS ONT DU SUIVRE, et elles ne sont pas cosmetiques :
       --bordure-champ passe a #7d7d7d (3,01:1 -> 3,36:1), pour garder une marge ;
       --survol passe a #e2e2e2 : a #ececec il serait devenu PLUS CLAIR que le fond,
       donc un survol qui eclaircit au lieu d'assombrir — l'inverse de ce que tout
       le reste du site fait. */
  --fond: #e8e8e8;
  --texte: #101010;            /* 15,53:1 sur le fond */
  /* 🔴 ASSOMBRIS LE 2026-08-12 (demande du porteur), memes deux gris — contraste
     verifie par script, plus loin de tout seuil qu'avant. Valeurs remesurees sur le
     fond du 2026-08-13 :
       texte-doux   : 6,90:1 sur le fond, 6,53:1 sur --survol (valait #595959)
       texte-faible : 5,29:1 sur le fond, 5,01:1 sur --survol (valait #6a6a6a) */
  --texte-doux: #4d4d4d;       /*  6,90:1 */
  --texte-faible: #5e5e5e;     /*  5,29:1 sur le fond, 5,01:1 sur --survol */
  --texte-inverse: #ffffff;    /* ce qui s'ecrit SUR --texte : boutons, onglet actif */
  --texte-survol: #303030;
  --ligne: #d4d4d4;
  /* 🔴 LA BORDURE DES CHAMPS A SA PROPRE COULEUR DEPUIS LE 2026-08-13, ET C'EST
     MESURE, PAS ESTHETIQUE. Un proche du porteur a signale que les champs « ne se
     voyaient pas assez » : `--ligne` (#d4d4d4) ne tient que **1,32:1** sur ce fond,
     tres loin des **3:1** qu'un element d'interface doit atteindre (WCAG 1.4.11).
     Le defaut passait inapercu tant que la bordure n'etait qu'un trait sous le
     champ ; il saute aux yeux des qu'elle en fait le tour.
     ⚠️ VARIABLE SEPAREE, ET NON `--ligne` RELEVEE : cette derniere dessine aussi
     les separateurs, les filets de listes et le cadre des cartes, ou un gris franc
     serait lourd. Deux usages, deux valeurs.
     Mesures sur le fond du 2026-08-13 : 3,36:1 sur le fond, 3,18:1 sur le survol
     (elle valait #858585 quand le fond etait #f2f2f2). */
  --bordure-champ: #7d7d7d;
  --survol: #e2e2e2;           /* #f4f4f4, puis #ececec — voir le fond ci-dessus */
  --erreur: #a3231a;           /*  6,4:1 */
  --erreur-fond: #fdecea;
  --succes: #0a6c3d;           /*  5,1:1 */
  --succes-texte: #0a4d2e;     /*  8,0:1 sur --succes-fond */
  --succes-fond: #e8f5ee;
  --avertissement-fond: #fff8e1;
  --avertissement-bordure: #7a6000;
  /* 🔴 LA TEINTE DES DEUX BOUTONS DE REPONSE AU DEVIS (porteur, 2026-08-26).
     ⚠️ ELLE GARDE LA CLARTE DU FOND (#e8e8e8), et seule la couleur bouge. Le relief des
        boutons est un jeu d'ombres calculees pour ce fond-la : une teinte plus sombre ou
        plus claire l'aurait fait sonner faux. */
  --accepte-fond: #dfece3;     /* 5,2:1 avec --succes */
  --refuse-fond: #f2e4e2;      /* 5,8:1 avec --erreur */
  --inversion-logo: 0;         /* le logo est noir : il va bien sur fond clair */
  --pastille-fond: #ffffff;    /* la pastille de l interrupteur reste claire dans les */
  --pastille-icone: #101010;   /* deux themes, comme sur le modele fourni */

  --hauteur-cible: 48px;       /* au-dessus des 44 px exiges */
  --duree-fondu-de-theme: 1s;

  /* Style neumorphique des boutons (demande du porteur, 2026-08-12 — meme traitement
     que le back-office). Deux teintes qui s'ecartent de --fond dans les deux sens.
     ⚠️ CE N'EST PAS UNE COULEUR DE TEXTE : la regle "chaque couleur porte son rapport
     mesure" (voir en tete de fichier) ne s'y applique donc pas telle quelle — une
     ombre n'est pas lue par un lecteur d'ecran. Ce qui reste un critere de recette,
     et qui n'a pas bouge : le texte des boutons garde --texte-inverse / --texte sur
     --texte / --fond, deja mesures. Et la bordure des boutons reste couleur
     --texte-doux (7,55:1) : sur ce public de 45-60 ans, un bouton doit rester
     identifiable au bord, pas seulement a l'ombre.
     🔴 INTENSITE REHAUSSEE LE 2026-08-13 (demande du porteur, « très légèrement ») :
     0,9 -> 0,95 pour la brillance, 0,15 -> 0,20 pour l'ombre.
     ⚠️ UNE SECONDE ACCENTUATION A ETE FAITE PUIS ANNULEE LE MEME JOUR (1 et 0,26) :
     le porteur l'a jugee excessive. Ne pas la refaire sans qu'il la redemande. */
  --neumorphisme-ombre-claire: rgba(255, 255, 255, 0.95);
  --neumorphisme-ombre-sombre: rgba(0, 0, 0, 0.20);
}

/* Theme sombre. Applique de deux facons, et l'ordre compte :
     1. par le reglage du systeme, pour qui n'a rien demande ;
     2. par l'attribut data-theme, quand le visiteur a clique le bouton — son choix
        prime alors sur le reglage du systeme, dans les deux sens. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) {
    /* 🔴 GRIS TRES FONCE DEPUIS LE 2026-08-12 (demande du porteur : « pas un noir »)
       — valait #101010. --survol bouge avec lui, pour rester un cran AU-DESSUS du
       fond (c'etait deja le cas avant : #1e1e1e sur #101010). --texte-inverse et
       --pastille-icone GARDENT #101010 : ce ne sont pas des fonds de page, mais des
       AVANT-plans sombres poses sur du clair (texte d'un bouton, icone d'une
       pastille claire) — sans lien avec cette teinte de fond. Contrastes
       RECALCULES PAR SCRIPT (formule sRGB, pas a la main) : le plus juste des
       trois (--texte-faible) tient 5,37:1, toujours au-dessus du seuil de 4,5:1
       de la spec 01. */
    --fond: #202020;
    --texte: #f2f2f2;          /* 14,55:1 sur le nouveau fond */
    --texte-doux: #b3b3b3;     /*  7,77:1 */
    --texte-faible: #949494;   /*  5,37:1 */
    --texte-inverse: #101010;
    --texte-survol: #d8d8d8;
    --ligne: #3d3d3d;
    /* Voir le commentaire du bloc clair. Ici : 3,54:1 sur le fond sombre —
       `--ligne` (#3d3d3d) n'y tenait que 1,50:1. */
    --bordure-champ: #757575;
    --survol: #2e2e2e;
    --erreur: #ff9a90;         /*  8,1:1 sur le fond sombre */
    --erreur-fond: #35100c;
    --succes: #57d391;         /*  9,7:1 */
    --succes-texte: #9ae8bf;   /* 10,4:1 sur --succes-fond */
    --succes-fond: #0d2a1c;
    --avertissement-fond: #2b2410;
    --avertissement-bordure: #e3c257;
    --accepte-fond: #1b2b21;   /* 7,8:1 avec --succes */
    --refuse-fond: #2e1e1c;    /* 7,8:1 avec --erreur */
    --inversion-logo: 1;       /* le logo est noir : il faut l'inverser pour le voir */
    --pastille-fond: #f2f2f2;
    --pastille-icone: #101010;

    /* Voir le commentaire complet dans le bloc :root clair. Intensite rehaussee
       le 2026-08-13 : 0,05 -> 0,08 et 0,6 -> 0,65. Une seconde accentuation
       (0,12 et 0,72) a ete faite puis annulee le meme jour, a la demande du
       porteur — ne pas la refaire de soi-meme. */
    --neumorphisme-ombre-claire: rgba(255, 255, 255, 0.08);
    --neumorphisme-ombre-sombre: rgba(0, 0, 0, 0.65);
  }
}

:root[data-theme="sombre"] {
  /* Voir le commentaire complet dans le bloc @media ci-dessus. */
  --fond: #202020;
  --texte: #f2f2f2;
  --texte-doux: #b3b3b3;
  --texte-faible: #949494;
  --texte-inverse: #101010;
  --texte-survol: #d8d8d8;
  --ligne: #3d3d3d;
  /* Voir le commentaire du bloc clair. Ici : 3,54:1 sur le fond sombre. */
  --bordure-champ: #757575;
  --survol: #2e2e2e;
  --erreur: #ff9a90;
  --erreur-fond: #35100c;
  --succes: #57d391;
  --succes-texte: #9ae8bf;
  --succes-fond: #0d2a1c;
  --avertissement-fond: #2b2410;
  --avertissement-bordure: #e3c257;
  --accepte-fond: #1b2b21;
  --refuse-fond: #2e1e1c;
  --inversion-logo: 1;
  --pastille-fond: #f2f2f2;
  --pastille-icone: #101010;

  /* Voir le commentaire complet dans le bloc :root clair. Doit rester IDENTIQUE au
     bloc « prefers-color-scheme » ci-dessus : c'est le meme theme, obtenu autrement.
     Intensite rehaussee le 2026-08-13 : 0,05 -> 0,08 et 0,6 -> 0,65. Une seconde
     accentuation (0,12 et 0,72) a ete faite puis annulee le meme jour. */
  --neumorphisme-ombre-claire: rgba(255, 255, 255, 0.08);
  --neumorphisme-ombre-sombre: rgba(0, 0, 0, 0.65);
}

* { box-sizing: border-box; }

/* ⚠️ LES LIENS DOIVENT ETRE STYLES, ET LA MESURE L'A PROUVE.
   Sans cette regle, ils gardent le bleu par defaut du navigateur : 8:1 sur fond
   blanc — ce qui passait inapercu — mais 2,02:1 sur fond sombre, c'est-a-dire
   illisible. Trouve le 2026-07-29 en mesurant le theme nuit, jamais avant.
   Le soulignement n'est pas decoratif : c'est ce qui distingue un lien d'un mot en
   gras quand la couleur ne le fait plus. */
a {
  color: var(--texte);
  text-decoration: underline;
  text-underline-offset: 3px;
}
a:hover { color: var(--texte-doux); }

body {
  margin: 0;
  background: var(--fond);
  color: var(--texte);
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ═══ Fondu du changement de theme ═══════════════════════════════════════════
   Meme dispositif que le back-office (style_back_office.css) : au lieu de sauter
   d'un jeu de couleurs a l'autre, la page fond de l'un vers l'autre en une seconde.
   ⚠️ CETTE REGLE EST POSEE PUIS RETIREE PAR LE SCRIPT (bascule_jour_nuit.js), pas
   laissee en permanence : sinon chaque survol trainerait une seconde, partout.
   ⚠️ « * » EST ASSUME ICI, ET NULLE PART AILLEURS — meme raison que cote back-office :
   enumerer les elements laisserait un bloc sauter pendant que les autres fondent. */
.transition-de-theme,
.transition-de-theme * {
  transition:
    background-color var(--duree-fondu-de-theme, 1s) ease,
    border-color var(--duree-fondu-de-theme, 1s) ease,
    color var(--duree-fondu-de-theme, 1s) ease,
    fill var(--duree-fondu-de-theme, 1s) ease !important;
}

/* 🔴 CORRIGE LE 2026-08-13 (constat du porteur : la pastille de l'interrupteur ne
   glissait plus, elle sautait d'un bord a l'autre). CAUSE : la regle universelle
   ci-dessus pose un raccourci « transition », qui REMPLACE la liste entiere plutot
   que de s'y ajouter — la transition transform/padding propre a l'interrupteur
   disparaissait donc PENDANT TOUTE LA DUREE DU FONDU, c'est-a-dire A CHAQUE CLIC,
   puisque cliquer l'interrupteur est precisement ce qui declenche le fondu.
   MEME REMEDE QUE POUR LE DEGRADE DU BACK-OFFICE (style_back_office.css, pres de
   ".bloc-qui-defile::after") : les deux transitions doivent vivre dans la MEME
   regle pendant le fondu — mais ICI, CONTRAIREMENT A CE CAS-LA, « !important »
   EST OBLIGATOIRE : ".bloc-qui-defile::after" echappe a la regle universelle
   parce que "*" ne selectionne pas les pseudo-elements, donc rien n'y entrait en
   conflit. La pastille, elle, EST selectionnee par ce "*" — sans "!important" ici,
   la regle universelle aurait quand meme gagne, "!important" battant toujours une
   specificite plus haute mais non "!important" — raisonnement CSS standard, non
   revu dans un navigateur reel depuis ce poste : a confirmer a l'ecran. */
.transition-de-theme .interrupteur-pastille {
  transition:
    background-color var(--duree-fondu-de-theme, 1s) ease,
    border-color var(--duree-fondu-de-theme, 1s) ease,
    transform .25s cubic-bezier(.4, 0, .2, 1) !important;
}
.transition-de-theme .interrupteur-intitule {
  transition:
    color var(--duree-fondu-de-theme, 1s) ease,
    padding .25s ease !important;
}

/* ═══ Barre de progression ═════════════════════════════════════════════════
   Masquee tant que le script n'a pas pris la main : sans lui, la page n'est pas
   decoupee en etapes, et une jauge n'aurait aucun sens. */

/* z-index au-dessus de l'en-tete collant : sinon celui-ci, qui a son propre fond
   opaque, recouvrirait la jauge des le premier defilement. */
.progression {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--ligne);
  z-index: 30;
}
.progression span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--texte);
  transition: width .35s cubic-bezier(.4, 0, .2, 1);
}

/* ═══ Ossature ═════════════════════════════════════════════════════════════
   SANS JAVASCRIPT, cette ossature ne fait rien de special : la page defile
   normalement et les quatre etapes se suivent. C'est le script qui ajoute la
   classe « par-ecrans » au body, et c'est elle qui declenche la mise en page en
   plein ecran. Aucun contenu n'est jamais masque par le CSS seul. */

/* 🔴 LARGEUR ELARGIE LE 2026-08-12 (demande du porteur : reduire d'un tiers les
   marges vides sur les cotes, sur un grand ecran). Valait 1000px — configuration
   precedente gardee ici pour un retour rapide :
     max-width: 1000px;
   ⚠️ CECI REDUIT LA MARGE, PAS LA HAUTEUR DE LA PAGE : sur un ecran large, les
   grilles a plusieurs colonnes (.deux-colonnes, .trois-colonnes) etaient deja au
   nombre de colonnes maximal des 1000px (leurs seuils sont a 860px et 700px) —
   elargir encore ne leur donne pas de colonne supplementaire. Si le porteur veut
   aussi tenir sans defiler en hauteur, c'est un second levier (l'espacement
   vertical, deja resserre et mesure — voir plus bas), pas celui-ci. */
.page {
  max-width: 1300px;
  margin: 0 auto;
  padding: 0 clamp(24px, 5vw, 72px) 32px;
}

/* ═══ Pied de page — mentions légales ═══════════════════════════════════════
   Demande du porteur, 2026-08-09 (LCEN : mentions "facilement, directement et
   en permanence accessibles" — pas necessairement visibles sans defiler).
   ⚠️ HORS DE « .page » DANS index.php, DELIBEREMENT : place dedans, il aurait
   mange de la hauteur sur CHAQUE etape du parcours pagine — la meme contrainte
   au pixel pres que le reste de cette ossature (voir plus haut). Petit et sobre :
   ce n'est pas un element qu'on vient lire, seulement un qu'on doit pouvoir
   atteindre. */
.pied-de-page {
  /* Doit rester identique a .page (voir son commentaire) : c'est ce qui garde le
     pied de page aligne avec le contenu au-dessus. */
  max-width: 1300px;
  margin: 0 auto;
  padding: 20px clamp(24px, 5vw, 72px) 32px;
  display: flex;
  gap: 18px;
  font-size: 13px;
}
.pied-de-page a { color: var(--texte-doux); }

/* ⚠️ LES MARGES DE CETTE OSSATURE SONT SERREES, ET C'EST MESURE.
   L'en-tete et la barre d'actions mangent la hauteur disponible pour les champs.
   Mesure du 2026-07-29 sur une fenetre de 709 px de haut : avec les marges
   confortables du premier jet, il ne restait que 492 px pour le contenu, et les
   quatre ecrans debordaient tous. Chaque pixel repris ici est un pixel rendu au
   formulaire. Ne pas les rouvrir sans remesurer. */

.entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 24px 0 clamp(18px, 3vh, 32px);
}

/* Regroupe le logo et l'accroche pour qu'ils s'empilent SOUS le meme point que le
   logo occupait seul dans .entete (display:flex ; justify-content:space-between) —
   sans ca, l'accroche deviendrait un troisieme enfant du flex et se retrouverait
   coincee entre le logo et l'interrupteur au lieu de se poser sous le logo.
   ⚠️ « align-items: flex-start » N'EST PAS DECORATIF. Sans lui, le comportement par
   defaut de flexbox (stretch) etire le logo sur toute la largeur du bloc pendant que
   sa hauteur reste fixee a 56px (.logo, plus bas) : le logo se deforme. Constate a
   l'ecran le 2026-08-09. */
.bloc-marque {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
}

/* Le logo est un PNG noir sur fond transparent. En theme sombre il serait
   invisible : on l'inverse plutot que de charger un second fichier — 10 Ko de
   moins a telecharger, et surtout aucun risque que les deux versions divergent. */
.logo {
  height: 68px;
  width: auto;
  display: block;
  filter: invert(var(--inversion-logo));
}
@media (max-width: 860px) { .logo { height: 52px; } }

/* ═══ La ligne de positionnement sous le logo ═══════════════════════════════
   Demande du porteur, 2026-08-09 : la page est souvent la toute premiere image de
   KCZ Electronique pour un visiteur venu d'un QR code, sans autre contexte.
   ⚠️ UNE SEULE LIGNE, DELIBEREMENT PETITE : .entete est mesuree au pixel pres
   (voir plus haut) et reste collee en haut (sticky) sur LES QUATRE ecrans en mode
   pagine, pas seulement le premier — chaque pixel ajoute ici se paie quatre fois.
   ⚠️ VISIBLE SUR TELEPHONE AUSSI, ET C'EST VOULU : sous 620 px, la contrainte
   « chaque etape tient dans un ecran » est deja abandonnee (2026-07-31, plus bas
   dans ce fichier) au profit du confort de saisie — c'est justement la ou ajouter
   une ligne coute le moins, et c'est la que la plupart des visiteurs d'un QR code
   arriveront. Si le contenu depasse malgre tout sur un ecran bas, rien n'est
   jamais coupe : le conteneur grandit et la page defile (regle deja en place). */
.accroche {
  margin: 0;
  font-size: 13px;
  line-height: 1.3;
  color: var(--texte-doux);
  max-width: 420px;
}

/* ═══ La phrase de contexte, en tete de la premiere etape ══════════════════
   Demande du porteur, 2026-08-09 : « la premiere phrase que l'on lit ». Retiree
   le 2026-08-12 (elle vivait hors des <section class="etape">, donc pesait sur
   les QUATRE ecrans), puis remise le meme jour — SCOPEE A L'ETAPE 1 cette fois
   (voir index.php) : elle ne pese plus que sur cet ecran-la.
   ⚠️ PLUS GRANDE QUE h1 (30 px au maximum), et c'est voulu : elle doit ressortir
   comme LA phrase d'ouverture de la page, avant meme le numero d'etape qui la
   suit de pres a l'ecran. */
.accroche-principale {
  font-size: clamp(28px, 4.5vw, 40px);
  font-weight: 700;
  letter-spacing: -1px;
  line-height: 1.15;
  margin: clamp(20px, 4vh, 40px) 0 0;
}

/* ═══ Interrupteur jour / nuit ═════════════════════════════════════════════
   Reprend l'interrupteur a bascule fourni par le porteur : une piste en pilule, un
   intitule, et une pastille ronde qui glisse d'un cote a l'autre en changeant
   d'icone. Masque tant que le script n'a pas pris la main.

   ⚠️ C'EST UN <button role="switch">, PAS UNE CASE A COCHER DEGUISEE. Il s'annonce
   comme un interrupteur aux lecteurs d'ecran, il repond a la barre d'espace et a
   Entree, et il reste dans le parcours clavier. L'apparence seule est personnalisee. */

/* ⚠️ Cet interrupteur est DEUX FOIS PLUS PETIT que les cibles du formulaire, sur
   demande du porteur (2026-07-29). C'est assume : il ne fait pas partie du parcours
   de saisie, il ne se manipule qu'une fois, et le laisser a 52 px lui donnait plus
   de poids visuel qu'au bouton « Continuer ». Le critere des 44 px reste entier pour
   tout ce qui sert a remplir la demande. */
/* ⚠️ LA PASTILLE EST EN POSITION ABSOLUE, ET C'EST CE QUI PERMET DE L'ANIMER.
   La premiere version inversait le sens du conteneur (row-reverse) : la pastille
   sautait d'un cote a l'autre sans transition possible, parce qu'on n'anime pas un
   changement de sens de boite. Ici elle glisse par transform, et le texte se decale
   par son remplissage. */

/* 🔴 RELIEF NEUMORPHIQUE AJOUTE LE 2026-08-12 (demande du porteur). Fond passe de
   --survol a --fond (l'interrupteur est molde dans la meme matiere que la page, pas
   dans un survol) ; bordure conservee pour le contraste non textuel, epaisseur
   reduite le 2026-08-13 (2px -> 1.5px), meme raison qu'au .bouton juste au-dessus. */
.interrupteur-theme {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 90px;
  height: 30px;
  padding: 0;
  font-family: inherit;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: .9px;
  text-transform: uppercase;
  color: var(--texte-doux);
  background: var(--fond);
  /* 🔴 PLUS DE BORDURE DEPUIS LE 2026-08-13 (demande du porteur, meme mouvement que
     .bouton : 2px → 1.5px → 1px → 0.8px → aucune, dans la journee). Seul le relief
     dessine desormais la pilule.
     ⚠️ LA COURSE DE LA PASTILLE N'A PAS BOUGE, ET C'EST VOULU : sans bordure, le
     calcul exact donne 64px au lieu de 62 — soit un decalage de 1px a chaque bout,
     invisible sur une pastille de 22px qui glisse sur 60. La retoucher obligerait a
     reverifier les deux largeurs (bureau et telephone) pour un gain nul. */
  border: 0;
  border-radius: 99px;
  cursor: pointer;
  box-shadow:
    3px 3px 6px var(--neumorphisme-ombre-sombre),
    -3px -3px 6px var(--neumorphisme-ombre-claire);
}
.interrupteur-theme:active {
  box-shadow:
    inset 3px 3px 6px var(--neumorphisme-ombre-sombre),
    inset -3px -3px 6px var(--neumorphisme-ombre-claire);
}

/* Sens de lecture repris du modele fourni : en mode CLAIR la pastille est a droite
   avec le soleil, en mode SOMBRE elle glisse a gauche avec la lune. */
.interrupteur-intitule {
  flex: 1 1 auto;
  text-align: center;
  padding-left: 0;
  padding-right: 26px;         /* la pastille occupe la droite */
  white-space: nowrap;
  transition: padding .25s ease;
}
.interrupteur-theme[aria-checked="true"] .interrupteur-intitule {
  padding-left: 26px;
  padding-right: 0;
}

.interrupteur-pastille {
  position: absolute;
  top: 2px;
  left: 2px;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  background: var(--pastille-fond);
  border: 2px solid var(--ligne);
  border-radius: 50%;
  /* 🔴 RECALCULE LE 2026-08-13 : la bordure du bouton (.interrupteur-theme) est
     passee de 2px a 1px — 90 (largeur) − 2 (bordures, 1px de chaque cote) − 22
     (pastille) − 4 (marges, 2px de chaque cote) = 62 px de course. Valait 60px
     avec une bordure a 2px. Cette bordure-ci, celle DE LA PASTILLE elle-meme
     (juste au-dessus), n'a pas bouge : seule celle du bouton qui l'entoure. */
  transform: translateX(62px);
  transition: transform .25s cubic-bezier(.4, 0, .2, 1);
}
.interrupteur-theme[aria-checked="true"] .interrupteur-pastille { transform: translateX(0); }

.interrupteur-pastille svg {
  width: 13px;
  height: 13px;
  stroke: var(--texte-doux);   /* gris, comme le libelle */
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
}

@media (prefers-reduced-motion: reduce) {
  .interrupteur-pastille, .interrupteur-intitule { transition: none; }
}

/* L'icone qui ne correspond pas a l'etat courant n'est pas affichee. Les deux sont
   ecrites dans le HTML pour que la bascule soit instantanee, sans rien recharger. */
.interrupteur-theme[aria-checked="false"] .icone-lune,
.interrupteur-theme[aria-checked="true"]  .icone-soleil { display: none; }

@media (max-width: 620px) {
  /* Sur telephone, l'intitule passe a la trappe : la place manque, et la pastille
     suffit a comprendre. Le nom reste annonce par aria-label. */
  .interrupteur-intitule { display: none; }

  /* 🔴 CORRIGE LE 2026-08-13 (capture du porteur : sur iPhone, la pastille
     s'affichait A COTE du bouton, coupee par le bord de l'ecran, et la pilule
     restait vide).
     DEUX CAUSES, ET IL FALLAIT LES DEUX :
       1. .entete est un « flex » ou le bloc du logo prend toute la place qu'il
          peut ; le bouton, sans « flex-shrink: 0 », se faisait comprimer BIEN
          en dessous de ses 90px. La pastille, elle, est en position absolue avec
          une course ECRITE EN DUR (62px, calculee pour 90px) : elle sortait donc
          d'un bouton devenu deux fois plus etroit qu'elle ne le croyait.
       2. Une fois l'intitule masque (juste au-dessus), ces 90px n'avaient de
          toute facon plus lieu d'etre : il ne reste qu'une pastille a loger.
     ⚠️ LES DEUX VALEURS CI-DESSOUS SE TIENNENT : 52 (largeur) − 2 (bordures,
     1px de chaque cote) − 22 (pastille) − 4 (marges, 2px de chaque cote) = 24px
     de course. Changer l'une sans l'autre remet la pastille de travers. */
  .interrupteur-theme { gap: 0; flex-shrink: 0; width: 52px; }
  .interrupteur-pastille { transform: translateX(24px); }
}

/* ═══ Titres ═══════════════════════════════════════════════════════════════ */

h1 {
  font-size: clamp(22px, 3vw, 30px);
  font-weight: 600;
  letter-spacing: -1px;
  line-height: 1.15;
  margin: 0 0 6px;
}

/* Au changement d'ecran, le script pose le focus sur le titre : c'est ce qui fait
   annoncer la nouvelle etape par un lecteur d'ecran. Le contour visuel, lui, n'a
   aucun sens ici — personne n'a « selectionne » ce titre. */
h1[tabindex="-1"]:focus { outline: none; }

/* 🔴 AJOUTE LE 2026-08-12 : le porteur a demande de retirer le TEXTE VISIBLE du
   <h1> de l'etape 1, sans savoir (ni moi au moment de retirer la phrase) que ce
   titre est aussi celui que le script focalise a chaque changement d'ecran (voir
   juste au-dessus, et formulaire.js ligne ~158). Un <h1> masque par cette classe
   reste dans le DOM et le parcours du clavier — display:none ou visibility:hidden
   l'en auraient sorti, et le focus du script n'aurait plus rien trouve. C'est
   le meme choix que .etiquette-cachee au back-office, pour la meme raison. */
.masque-visuellement {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
h2 { font-size: 18px; font-weight: 600; letter-spacing: -.3px; margin: 0 0 10px; }

.sous-titre {
  color: var(--texte-doux);
  margin: 0 0 clamp(12px, 2.4vh, 22px);
  max-width: 620px;
}

/* Le compteur « ETAPE 1 SUR 4 » au-dessus du titre. En capitales espacees comme la
   maquette, mais a 16 px et en gris lisible : le critere de recette de la spec 01
   nomme les libelles et les consignes, pas seulement les paragraphes. Mesure du
   2026-07-29 : a 13 px, 44 textes de la page tombaient sous le seuil. */
.numero-etape {
  display: block;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--texte-doux);
  margin-bottom: 10px;
}

/* ═══ Champs ═══════════════════════════════════════════════════════════════ */

/* « relative » sert d'ancrage a la liste de propositions d'adresse, qui se place en
   absolu juste sous son champ. Sans cela, elle se positionnerait par rapport a la
   page et se retrouverait n'importe ou des que l'ecran defile. */
.champ { margin: 0 0 clamp(12px, 2vh, 20px); position: relative; }

/* ═══ Propositions d'adresse ═══════════════════════════════════════════════
   ⚠️ CETTE LISTE REMPLACE LE <datalist> NATIF, et ce n'est pas un caprice
   d'apparence. Sur iPhone, Safari affichait la liste native DEUX FOIS : une bulle
   grise flottante qui recouvrait la moitie du formulaire, et les memes propositions
   tronquees dans la barre du clavier (capture du porteur, 2026-07-31). Rien de tout
   cela n'etait stylable — ni la position, ni les couleurs, ni le theme sombre.

   Ecrite ici, la liste s'affiche pareil partout, suit le theme, et tient les 44 px
   de cible. Voir ressources/suggestions_d_adresse.js pour le comportement. */
.propositions-adresse {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 30;              /* au-dessus de l'en-tete et de la barre d'actions (20) */
  margin: 4px 0 0;
  padding: 4px 0;
  list-style: none;
  background: var(--fond);
  border: 1px solid var(--ligne);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  /* Cinq propositions tiennent sans defilement ; au-dela, la liste defile plutot que
     de couvrir l'ecran — c'est exactement le defaut qu'on corrige. */
  max-height: 300px;
  overflow-y: auto;
}

.proposition-adresse {
  /* La cible de 44 px vaut ici comme partout : on choisit une adresse au doigt, sur
     un telephone, souvent debout. */
  min-height: var(--hauteur-cible);
  display: flex;
  align-items: center;
  padding: 10px 14px;
  font-size: 16px;
  color: var(--texte);
  cursor: pointer;
}

/* Le survol a la souris et le parcours au clavier donnent le MEME repere visuel :
   deux apparences differentes pour un meme etat obligeraient a apprendre deux
   langages. */
.proposition-adresse:hover,
.proposition-adresse.proposition-survolee {
  background: var(--survol);
}

/* 16 px, jamais moins : c'est un critere de recette, et il vise nommement les
   libelles. L'espacement des lettres est reduit par rapport a la maquette pour
   compenser la taille — sinon un libelle long passerait a la ligne. */
label, .intitule-groupe {
  display: block;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--texte-doux);
  margin-bottom: 4px;
}

input[type="text"],
input[type="email"],
input[type="tel"],
select,
textarea {
  width: 100%;
  min-height: var(--hauteur-cible);
  padding: 10px 12px;
  font-family: inherit;
  font-size: 16px;
  color: var(--texte);
  background: transparent;
  /* 🔴 BORDURE SUR LES QUATRE COTES DEPUIS LE 2026-08-13 (demande du porteur, apres
     la remarque d'un proche : les champs « ne se voyaient pas assez »). C'etait un
     simple trait sous le champ, herite de la maquette du 2026-07-29.
     ⚠️ LA FORME N'EST PAS SEULE EN CAUSE, ET C'EST LE POINT IMPORTANT : le trait
     etait dessine en `--ligne`, qui ne tient que 1,32:1 sur ce fond — la moitie du
     minimum exige pour un element d'interface. Encadrer un champ avec une couleur
     invisible n'aurait fait qu'un rectangle invisible. D'ou `--bordure-champ`, une
     couleur reglee pour depasser 3:1 (voir sa definition en tete de fichier).
     ⚠️ 1px SUFFIT UNE FOIS LA COULEUR JUSTE : le trait faisait 2px pour compenser sa
     paleur. Un cadre epais ET fonce alourdirait chaque ecran. */
  border: 1px solid var(--bordure-champ);
  /* ⚠️ L'ARRONDI EST PERMANENT, PAS SEULEMENT AU FOCUS. Pose dans la regle de focus,
     il disparaissait a l'instant ou le champ le perdait, pendant que l'ombre, elle,
     s'estompait encore : on voyait un cadre carre finir de disparaitre.
     Il fait desormais le tour, comme la bordure. */
  border-radius: 8px;
}
input::placeholder, textarea::placeholder { color: var(--texte-faible); }

/* ═══ Marque de focus ══════════════════════════════════════════════════════
   ⚠️ CE N'EST PLUS UN « outline », ET LA RAISON EST CONCRETE : un contour epais
   avec un decalage de 3 px DEBORDAIT DE L'ECRAN sur les champs collés au bord —
   constate par le porteur le 2026-07-29. Une ombre portee, elle, reste dans la
   boite, suit les angles arrondis, et s'anime en fondu.

   Le gris est celui des textes secondaires : 4,6:1 sur le fond, au-dessus des 3:1
   qu'un indicateur de focus doit tenir. Descendre plus bas rendrait la navigation au
   clavier impraticable — c'est la limite a ne pas franchir. */

input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible, [role="switch"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--texte-faible);
}

/* 🔴 CETTE REGLE A DISPARU LE 2026-08-13, ET IL NE FAUT PAS LA REECRIRE.
   Elle effacait la ligne du bas pendant le focus (« border-bottom-color:
   transparent ») parce que le trait du champ et l'ombre de focus se superposaient
   en un trait double, epais et sale. Depuis que la bordure fait le tour, les deux
   sont concentriques : le cadre du champ, puis l'ombre autour. Rien ne se
   superpose plus, et effacer un cote laisserait un champ ouvert en bas. */
input, select, textarea, button, a {
  transition: box-shadow .18s ease, border-color .18s ease;
}

/* Au focus, le cadre fonce : l'ombre dit « c'est ici », la bordure le confirme
   meme pour qui ne distingue pas bien les ombres claires. */
input:focus, select:focus, textarea:focus { border-color: var(--texte); }

/* Les cases a cocher n'en portent pas : sur un carre de 16 px, l'ombre faisait une
   tache. Le libelle se souligne a la place — plus discret, et il reste possible de
   savoir ou l'on se trouve au clavier. */
.case input[type="checkbox"]:focus-visible { box-shadow: none; }
.case:has(input:focus-visible) label { text-decoration: underline; text-underline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  input, select, textarea, button, a { transition: none; }
}

/* Un champ desactive garde son cadre, en pointilles : la forme dit « c'est bien un
   champ », le pointille dit « pas maintenant ». (C'etait « border-bottom-style »
   avant le 2026-08-13, quand la bordure n'existait qu'en bas.) */
input:disabled { color: var(--texte-faible); border-style: dashed; }

select {
  appearance: none;
  cursor: pointer;
  padding-right: 30px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--texte) 50%),
    linear-gradient(135deg, var(--texte) 50%, transparent 50%);
  background-position: calc(100% - 14px) 58%, calc(100% - 8px) 58%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

textarea { min-height: clamp(110px, 16vh, 180px); resize: vertical; line-height: 1.55; }

/* 🔴 REDUITS A 2 LIGNES (demande du porteur) : remarques_diverses le 2026-08-12,
   description_symptomes le 2026-08-13 — CE DERNIER DEVAIT « RESTER GRAND »
   SELON UN COMMENTAIRE ECRIT ICI LA VEILLE (spec 01, « pousser le demandeur a
   decrire sa panne en detail ») ; le porteur a tranche l'inverse le lendemain,
   deliberement, pas par oubli de cette regle. Scopes a l'identifiant, pas a la
   balise "textarea" generale ci-dessus, au cas ou un futur champ texte du
   formulaire doive rester grand, lui. Les deux restent agrandissables a la main
   (resize: vertical, herite ci-dessus) pour qui ecrit plus que 2 lignes. */
#remarques_diverses,
#description_symptomes { min-height: calc(2 * 1.55em + 20px); }

/* La consigne permanente sous un champ. Elle fait partie de ce qu'il faut lire :
   jamais en gris pale. */
.consigne {
  display: block;
  font-size: 15px;
  color: var(--texte-doux);
  margin: 6px 0 0;
}
.facultatif { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--texte-doux); }

/* Deux colonnes sur grand ecran, une seule sur telephone. */
.deux-colonnes { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 4vw, 48px); align-items: start; }
@media (max-width: 860px) { .deux-colonnes { grid-template-columns: 1fr; gap: 0; } }

/* Trois colonnes : reserve aux champs qui decrivent une meme chose — type, marque et
   modele de l'appareil. Deux colonnes en dessous de 1000 px, une seule sur telephone. */
.trois-colonnes { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(18px, 3vw, 36px); align-items: start; }
@media (max-width: 1000px) { .trois-colonnes { grid-template-columns: 1fr 1fr; } }
@media (max-width: 700px) { .trois-colonnes { grid-template-columns: 1fr; gap: 0; } }

/* ═══ Onglets « Particulier / Professionnel » ══════════════════════════════
   Ce sont de VRAIS boutons radio, jamais masques : ils restent atteignables au
   clavier et annonces par un lecteur d'ecran. Seule leur apparence change. */

.choix-demandeur { display: flex; gap: 8px; margin: 0 0 clamp(20px, 3vh, 30px); flex-wrap: wrap; }

.onglet-demandeur { position: relative; }
.onglet-demandeur input {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  opacity: 0;          /* invisible mais TOUJOURS dans le parcours clavier */
  cursor: pointer;
}
.onglet-demandeur label {
  display: flex;
  align-items: center;
  min-height: var(--hauteur-cible);
  padding: 10px 22px;
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--texte-doux);
  border: 2px solid var(--ligne);
  border-radius: 99px;
  cursor: pointer;
}
.onglet-demandeur:hover label { border-color: var(--texte); color: var(--texte); }
.onglet-demandeur:has(input:checked) label {
  color: var(--texte-inverse);
  background: var(--texte);
  border-color: var(--texte);
}
.onglet-demandeur:has(input:focus-visible) label { box-shadow: 0 0 0 2px var(--texte-faible); }

/* ═══ Cases a cocher ═══════════════════════════════════════════════════════
   La hauteur de 48 px est portee par le LIBELLE : c'est lui la cible. Cliquer
   dans le remplissage du bloc ne coche rien.

   ⚠️ NE PAS remplacer par display:flex sur le libelle : les enfants d'un
   conteneur flex sont transformes en blocs, et le lien « politique de
   confidentialite » au milieu de la phrase partirait a la ligne tout seul. */

.case { display: flex; align-items: flex-start; gap: 14px; }
.case + .case { margin-top: 2px; }

/* ⚠️ CASE DESSINEE A LA MAIN, et pour une seule raison : la coche native n'est pas
   animable — ni sa taille, ni son apparition. Ici le carre est petit, et le ✓ apparait
   en fondu. L'element reste un <input type="checkbox"> : il garde son nom, sa valeur,
   son comportement clavier, et le serveur le lit comme avant. */
.case input[type="checkbox"] {
  flex: 0 0 auto;
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  width: 16px; height: 16px;
  margin: 14px 0 0;
  background: transparent;
  border: 1px solid var(--texte-doux);
  border-radius: 3px;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.case input[type="checkbox"]::after {
  content: "";
  position: absolute;
  left: 4px; top: 0;
  width: 5px; height: 10px;
  border: solid var(--texte-inverse);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
  opacity: 0;
  transition: opacity .15s ease;
}
.case input[type="checkbox"]:checked {
  background: var(--texte);
  border-color: var(--texte);
}
.case input[type="checkbox"]:checked::after { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .case input[type="checkbox"], .case input[type="checkbox"]::after { transition: none; }
}
.case label {
  display: block;
  min-height: 44px;   /* le minimum exige ; 48 px ailleurs, mais ici chaque pixel compte */
  padding: 10px 0;
  margin: 0;
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--texte);
  cursor: pointer;
}
.case label .consigne { margin-top: 2px; }

/* ═══ Interrupteur d'urgence ═══════════════════════════════════════════════
   Remplace la sous-section « Urgence » qui ne contenait qu'une case a cocher, et
   dont le titre repetait le libelle (« Urgence » / « Cette demande est urgente »).

   ⚠️ C'EST UNE VRAIE CASE A COCHER, simplement habillee. L'input garde son nom et sa
   valeur, il part avec le formulaire, et SANS JAVASCRIPT le serveur le lit
   exactement comme avant. Rien ici ne depend d'un script.

   La zone cliquable couvre TOUT le bloc, piste comprise : c'est le <label> qui
   l'entoure. */

/* ⚠️ Ce bloc EST un <label> : sans les quatre neutralisations ci-dessous, il herite
   du style des libelles de champ — capitales, lettres espacees, gris — et
   l'explication du surcout s'affichait EN MAJUSCULES sur deux lignes. */
.bloc-urgence {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 14px 18px;
  margin: 0;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--texte);
  border: 2px solid var(--ligne);
  border-radius: 14px;
  cursor: pointer;
}
.bloc-urgence:hover { border-color: var(--texte); }

/* Le texte occupe la place restante, l'interrupteur garde la sienne : sans cette
   repartition, le texte poussait la piste a la ligne suivante. */
.bloc-urgence > span:first-child { flex: 1 1 auto; min-width: 0; }

/* Coche : elle reste dans le parcours clavier, seule son apparence change. */
.bloc-urgence input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
}

.urgence-intitule {
  display: block;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--texte);
  margin: 0;
}
.urgence-explication { margin-top: 2px; }

.urgence-piste {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  width: 68px;
  height: 36px;
  padding: 4px;
  background: var(--survol);
  border: 2px solid var(--ligne);
  border-radius: 99px;
  transition: background .2s ease;
}
.urgence-piste::before {
  content: "";
  display: block;
  width: 24px;
  height: 24px;
  background: var(--texte-doux);
  border-radius: 50%;
  transition: transform .2s ease, background .2s ease;
}

/* Etat actif : la piste se remplit et la pastille glisse a droite. La couleur seule
   ne suffirait pas — l'intitule change aussi, plus bas, par le script. */
.bloc-urgence:has(input:checked) { border-color: var(--texte); background: var(--survol); }
.bloc-urgence:has(input:checked) .urgence-piste { background: var(--texte); border-color: var(--texte); }
.bloc-urgence:has(input:checked) .urgence-piste::before {
  background: var(--texte-inverse);
  transform: translateX(32px);
}
.bloc-urgence:has(input:focus-visible) { box-shadow: 0 0 0 2px var(--texte-faible); }

@media (prefers-reduced-motion: reduce) {
  .urgence-piste, .urgence-piste::before { transition: none; }
}

/* ═══ Boutons ══════════════════════════════════════════════════════════════ */

/* 🔴 STYLE NEUMORPHIQUE LE 2026-08-12 (demande du porteur, meme traitement que le
   back-office). CONFIGURATION PRECEDENTE, gardee pour un retour rapide :
   .bouton {
     color: var(--texte-inverse);
     background: var(--texte);
     border: 2px solid var(--texte);
   }
   .bouton:hover { background: var(--texte-survol); border-color: var(--texte-survol); }
   .bouton:disabled { background: var(--texte-faible); border-color: var(--texte-faible); }
   .bouton-secondaire { color: var(--texte); background: transparent; }
   .bouton-secondaire:hover { background: var(--survol); }
*/
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 13px 30px;
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: var(--texte);
  background: var(--fond);
  /* 🔴 PLUS DE BORDURE DEPUIS LE 2026-08-13 (demande du porteur, apres l'avoir
     amincie trois fois le même jour : 2px → 1.5px → 1px → 0.8px → aucune). Seul le
     relief dit désormais où commence le bouton.
     ⚠️ CE QUE ÇA COÛTE, ET IL FAUT LE SAVOIR : cette bordure n'était pas
     décorative. Elle tenait un rapport de contraste mesuré (7,55:1), là où une
     ombre douce n'en tient aucun — un relief ne « contraste » pas, il suggère. Sur
     un écran mal réglé, en plein soleil, ou pour un œil qui distingue mal les
     nuances proches, le bouton peut donc devenir difficile à situer. C'est un choix
     d'apparence assumé par le porteur, pas un oubli : ne pas la remettre sans le
     lui redemander.
     ⚠️ LE FOCUS RESTE ENTIER : l'ombre de focus (`box-shadow` de 2px, voir la règle
     `:focus-visible`) ne dépend pas de cette bordure. La navigation au clavier
     continue de se voir. */
  border: 0;
  border-radius: 99px;
  cursor: pointer;
  text-decoration: none;
  box-shadow:
    6px 6px 12px var(--neumorphisme-ombre-sombre),
    -6px -6px 12px var(--neumorphisme-ombre-claire);
}
/* ⚠️ « border-color » A ETE RETIRE DE CES DEUX REGLES LE 2026-08-13, avec la
   bordure elle-meme (voir .bouton) : sur un bouton sans bordure, cette propriete
   ne dessine plus rien. C'est desormais le RELIEF SEUL qui distingue les etats —
   plus marque au repos, resserre au survol, creuse au clic. */
.bouton:hover {
  box-shadow:
    4px 4px 8px var(--neumorphisme-ombre-sombre),
    -4px -4px 8px var(--neumorphisme-ombre-claire);
}
.bouton:active {
  box-shadow:
    inset 4px 4px 8px var(--neumorphisme-ombre-sombre),
    inset -4px -4px 8px var(--neumorphisme-ombre-claire);
}
/* Un bouton desactive perd son relief ET son texte franc : a plat, il ne se
   confond plus avec ceux qui repondent. C'est le seul etat ou la disparition du
   relief est une information, et non une perte. */
.bouton:disabled {
  color: var(--texte-faible);
  box-shadow: none;
  cursor: progress;
}

.bouton-secondaire {
  /* Meme fond neumorphique que .bouton, relief plus leger pour rester en retrait
     du bouton principal — meme logique que .bouton-discret au back-office. */
  box-shadow:
    3px 3px 6px var(--neumorphisme-ombre-sombre),
    -3px -3px 6px var(--neumorphisme-ombre-claire);
}
.bouton-secondaire:hover {
  box-shadow:
    2px 2px 4px var(--neumorphisme-ombre-sombre),
    -2px -2px 4px var(--neumorphisme-ombre-claire);
}
.bouton-secondaire:active {
  box-shadow:
    inset 3px 3px 6px var(--neumorphisme-ombre-sombre),
    inset -3px -3px 6px var(--neumorphisme-ombre-claire);
}

/* Le « ← Retour » : discret, mais assez grand pour le doigt. */
.bouton-retour {
  display: inline-flex;
  align-items: center;
  min-height: var(--hauteur-cible);
  padding: 0 4px;
  font-family: inherit;
  font-size: 17px;
  color: var(--texte-doux);
  background: none;
  border: 0;
  cursor: pointer;
}
.bouton-retour:hover { color: var(--texte); text-decoration: underline; }

/* ═══ Barre d'actions basse ════════════════════════════════════════════════
   Masquee tant que le script n'a pas pris la main : sans navigation par ecrans,
   un bouton « Continuer » ne menerait nulle part. */

.actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  /* 🔴 PASSE EN WRAP LE 2026-08-12 : depuis que la case de confidentialite
     rejoint le bouton (.actions-droite), la barre porte plus de contenu qu'avant
     sur le dernier ecran. Sans "wrap", ce contenu se serait comprime sur les
     ecrans etroits — un bouton de moins de 52px de haut n'est plus une cible
     valide (voir plus bas). Avec "wrap", .actions-droite passe entierement sur sa
     propre ligne plutot que de retrecir. */
  flex-wrap: wrap;
  /* ⚠️ REDUIT LE 2026-08-09 (demande du porteur) : 18/24 -> 10/14. Seul
     l'espace AUTOUR du bouton est repris — sa propre hauteur (.bouton,
     min-height: 52px) ne bouge pas, c'est le plancher d'accessibilite du
     site public et il ne se negocie pas. */
  padding: 10px 0 14px;
  border-top: 1px solid var(--ligne);
}
/* 🔴 PASSE EN COLONNE LE 2026-08-13 (demande du porteur : les liens legaux
   remontent juste sous « Étape X sur N »). ".actions-gauche-haut" porte
   desormais Retour + le compteur (l'ancien contenu direct de .actions-gauche) ;
   le pied de page, deplace ici par formulaire.js, devient le second enfant,
   juste en dessous. */
.actions-gauche { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.actions-gauche-haut { display: flex; align-items: center; gap: 18px; }

/* Le pied de page, une fois ici : plus de largeur de page, plus de centrage, un
   padding retaille pour une ligne discrete sous le compteur d'etapes plutot
   qu'une bande pleine largeur. Sans JavaScript, il NE PORTE PAS ce contexte —
   il reste hors de .actions-gauche, avec son apparence habituelle de pied de
   page (regle plus haut). */
.actions-gauche .pied-de-page {
  max-width: none;
  margin: 0;
  padding: 0;
  font-size: 12px;
  gap: 14px;
}

/* 🔴 AJOUTE LE 2026-08-12 : regroupe la case de confidentialite et le bouton
   d'envoi, cote a cote, a droite de la barre — sans ce conteneur, la regle
   "justify-content: space-between" de .actions les aurait ecartes chacun a un
   bout, au lieu de les tenir ensemble. Voir formulaire.js pour le deplacement du
   noeud DOM, et bloc_confidentialite dans index.php pour la case elle-meme (elle
   y reste, sans JavaScript). Passe sur toute la largeur et s'aligne a droite des
   qu'il ne tient plus a cote de .actions-gauche (le "wrap" est sur .actions). */
.actions-droite {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  flex: 1 1 auto;
  gap: 16px;
}

/* La case, une fois dans la barre du bas : plus de marge basse (elle n'empile
   plus rien en dessous), et le texte d'accompagnement disparait — la barre est
   trop etroite pour lui, et l'essentiel (la case, le lien vers la politique)
   reste lisible sans lui. Sans JavaScript, la case reste a sa place d'origine et
   NE PORTE PAS cette classe : le texte y reste visible en entier.

   🔴 MASQUEE PAR DEFAUT, CORRIGE LE 2026-08-12 (constat du porteur : la case
   restait visible sur les QUATRE ecrans, pas seulement le dernier). Le
   deplacement du noeud DOM (formulaire.js) est fait UNE SEULE FOIS a
   l'initialisation, avant de savoir sur quel ecran on se trouve — masquer/montrer
   est donc le role du CSS, pas du script, et il suit la MEME classe que le reste
   de la mise en page du dernier ecran : ".dernier-ecran", posee par
   afficherEtape() sur .corps-formulaire (voir plus haut, § "Le dernier ecran, sur
   deux colonnes"). #barre_actions est un frere PLUS TARD dans le DOM que
   .corps-formulaire (juste apres </main>) : le combinateur "~" fonctionne dans ce
   sens-la, jamais dans l'autre. */
.actions-droite .champ-consentement { margin: 0; display: none; }
.actions-droite .champ-consentement .consigne { display: none; }
.corps-formulaire.dernier-ecran ~ #barre_actions .actions-droite .champ-consentement {
  display: block;
}

/* 🔴 AJOUTE LE 2026-08-13 (demande du porteur) : plus de phrase d'erreur separee
   ici — pas assez de place dans cette barre pour « Merci de cocher la case pour
   confirmer... » sans deborder. A la place, le texte du label lui-meme (« J'ai
   pris connaissance de la politique de confidentialité ») passe en rouge, et
   la secousse (.champ-en-erreur, deja posee ailleurs dans ce fichier) continue
   de s'appliquer normalement au bloc entier. Le message texte, lui, REND
   TOUJOURS a sa place d'origine (bloc_confidentialite, sans JavaScript) : c'est
   la seule explication qu'un visiteur sans script y trouve. */
.actions-droite .champ-consentement.champ-en-erreur label { color: var(--erreur); }
.actions-droite .champ-consentement .message-erreur { display: none; }

/* ⚠️ JAMAIS "display: none" ICI, meme sur petit ecran : cette case est
   OBLIGATOIRE (data-obligatoire), et le noeud a ete DEPLACE ici (formulaire.js) —
   pas copie. La masquer la masquerait PARTOUT, sans aucun moyen de cocher la
   case nulle part ailleurs sur la page. Sur petit ecran, la barre passe plutot
   sur deux lignes : voir .actions ci-dessus. */
@media (max-width: 620px) {
  .actions-droite .champ-consentement label { font-size: 14px; }
}
/* 🔴 NOIR ET GRAS DEPUIS LE 2026-08-13 (demande du porteur : « pour qu'on la
   voie bien ») — valait var(--texte-doux), poids normal, plus discret. */
.compteur-etapes { font-size: 15px; font-weight: 600; color: var(--texte); }

/* ═══ Bandeau « Deja client ? » ════════════════════════════════════════════ */

.bandeau-deja-client {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 18px 22px;
  margin: 0 0 clamp(20px, 3vh, 30px);
  border: 2px solid var(--texte);
  border-radius: 16px;
}
.bandeau-deja-client .intitule { display: block; font-size: 17px; font-weight: 700; }
.bandeau-deja-client .consigne { margin-top: 2px; }

.encart-deja-client {
  padding: 22px;
  margin: 0 0 clamp(20px, 3vh, 30px);
  background: var(--survol);
  border-radius: 16px;
}

.badge-client-reconnu {
  display: flex;
  align-items: center;
  gap: 10px;
  width: fit-content;
  max-width: 100%;
  padding: 14px 20px;
  margin: 0 0 clamp(20px, 3vh, 30px);
  font-size: 17px;
  color: var(--succes-texte);
  background: var(--succes-fond);
  border: 2px solid var(--succes);
  border-radius: 14px;
}

.message-deja-client-succes,
.message-deja-client-echec {
  margin: 18px 0 0;
  padding: 14px 18px;
  border-radius: 10px;
  font-size: 17px;
}
.message-deja-client-succes { color: var(--succes-texte); background: var(--succes-fond); border-left: 6px solid var(--succes); }
.message-deja-client-echec {
  color: var(--texte);
  background: var(--avertissement-fond);
  border-left: 6px solid var(--avertissement-bordure);
}

/* ═══ Photos ═══════════════════════════════════════════════════════════════
   Zone de depot inspiree de la maquette. Le champ de fichier reste un vrai
   <input type="file"> : sur telephone, c'est lui qui ouvre l'appareil photo. */

/* 🔴 PADDING VERTICAL REDUIT LE 2026-08-13 (demande du porteur : eviter le
   defilement sur l'etape des photos) : clamp(22px, 4vh, 40px) -> clamp(10px,
   2vh, 18px). Les deux rectangles empiles portaient une bonne partie de la
   hauteur en trop. Le champ de fichier a l'interieur garde son propre
   min-height (--hauteur-cible, 48px) : la cible de clic ne retrecit pas, seul
   l'espace vide autour d'elle se resserre. */
.depot-photo {
  display: block;
  padding: clamp(10px, 2vh, 18px) 20px;
  text-align: center;
  border: 2px dashed var(--ligne);
  border-radius: 16px;
  cursor: pointer;
}
.depot-photo:hover { border-color: var(--texte); background: var(--survol); }
.depot-photo .intitule {
  display: block;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--texte);
  margin-bottom: 6px;
}
/* Le champ de fichier lui-meme est une cible : sur telephone, c'est lui qui ouvre
   l'appareil photo. Sa hauteur naturelle est de 23 px — mesure du 2026-07-29 — d'ou
   le remplissage qui la porte au minimum exige. */
.depot-photo input[type="file"] {
  display: block;
  width: 100%;
  min-height: var(--hauteur-cible);
  margin-top: 12px;
  padding: 12px 0;
  font-size: 16px;
  font-family: inherit;
  cursor: pointer;
}

/* 🔴 LA GALERIE DES PHOTOS CHOISIES (porteur, 2026-08-26). Le champ de fichiers
   n'annonçait que « 3 fichiers sélectionnés » : on ne savait pas lesquels, et rien ne
   permettait d'en retirer un.

   ⚠️ ELLE EST VIDE ET MASQUEE TANT QU'AUCUNE PHOTO N'EST CHOISIE. C'est le script qui lève
      l'attribut « hidden » — un cadre vide sous un champ facultatif ferait croire qu'il
      manque quelque chose. */
.galerie-photos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
  margin-top: 14px;
}

.galerie-photos[hidden] { display: none; }

.vignette-photo {
  position: relative;
  margin: 0;
}

/* ⚠️ UN RAPPORT FIXE, ET « object-fit: cover ». Sans lui, une photo prise en hauteur et une
   autre en largeur donneraient deux tuiles de tailles differentes, et la grille sauterait à
   chaque ajout. */
.cadre-de-la-vignette {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--survol);
  border: 1px solid var(--ligne);
  border-radius: 12px;
}

.cadre-de-la-vignette img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Le repli quand le navigateur ne sait pas afficher le format — le HEIC des iPhone hors
   Safari. La photo partira très bien : la vignette dit seulement qu'elle ne se montre pas. */
.vignette-sans-apercu {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--texte-doux);
}

.legende-de-la-vignette {
  margin-top: 6px;
  font-size: 12px;
  line-height: 17px;
  color: var(--texte-doux);
}

/* Un nom de photo d'iPhone fait quarante caractères : on le coupe plutôt que d'élargir la
   tuile pour lui seul. Le titre de survol porte le nom entier. */
.nom-de-la-photo {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Le poids a sa propre ligne : c'est la seule des deux informations qui serve à décider
   quoi retirer quand le total dépasse le plafond, et il ne doit jamais se faire couper. */
.poids-de-la-photo {
  display: block;
  font-variant-numeric: tabular-nums;
}

/* 🔴 LA CIBLE FAIT 44 px, LE ROND VISIBLE 30. Le remplissage transparent porte la
   zone cliquable a la taille exigee sur le site public, sans poser un enorme bouton sur une
   vignette de cent vingt pixels. C'est un technicien avec des gants qui appuiera dessus.

   ⚠️ IL N'EXISTE QUE SI LE NAVIGATEUR SAIT REECRIRE LA LISTE DE FICHIERS. formulaire.js ne
      le cree pas autrement : un bouton qui ne ferait rien serait pire que pas de bouton. */
.retirer-la-photo {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 44px;
  height: 44px;
  padding: 0;
  font-family: inherit;
  font-size: 20px;
  line-height: 1;
  color: var(--erreur);
  border: 0;
  cursor: pointer;
  /* ⚠️ LE ROND EST PEINT EN DEGRADE, PAS EN « ::before ». Un pseudo-element positionne
     se serait peint AU-DESSUS de la croix — elle aurait disparu sous son propre fond. Un
     degrade est un fond : il reste derriere le texte, sans aucune regle d'empilement. */
  background: radial-gradient(circle at center,
    var(--fond) 0 14px, var(--ligne) 14px 15px, transparent 15px);
}

.retirer-la-photo:hover {
  background: radial-gradient(circle at center,
    var(--erreur-fond) 0 14px, var(--erreur) 14px 15px, transparent 15px);
}

.journal-des-photos:empty { display: none; }

/* 🔴 UNE CASE A COCHER PLACEE DANS UNE GRILLE DE CHAMPS (2026-08-13) se calerait en
   haut de sa cellule — donc a hauteur des INTITULES de ses voisins, pas de leurs
   zones de saisie, ce qui la fait flotter au-dessus de la ligne. Ce decalage vaut
   la hauteur d'un intitule : 15px de police, interligne compris, plus ses 4px de
   marge basse (voir la regle « label, .intitule-groupe »).
   ⚠️ EN « em », PAS EN PIXELS : si la taille des intitules change un jour, le
   decalage suit tout seul. Un « 25px » ecrit en dur serait faux le jour meme.
   ⚠️ SANS EFFET SUR TELEPHONE, et c'est voulu : sous 621px la grille passe a une
   seule colonne, plus rien n'est cote a cote, et il n'y a donc plus rien a aligner. */
@media (min-width: 621px) {
  .champ-aligne-sur-la-saisie { padding-top: calc(1.4em + 4px); }
}

/* L'intitule de la liste de conseils (2026-08-13). Meme apparence que les intitules
   de champs — c'est ce qui le rattache visuellement a la colonne de saisie a cote,
   plutot que d'en faire un titre de section. */
.intitule-conseils {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--texte-doux);
  margin: 0 0 4px;
}

.conseils-photo { list-style: none; padding: 0; margin: 0; }
.conseils-photo li {
  display: flex;
  gap: 14px;
  padding: 12px 0;
  font-size: 16px;
  color: var(--texte-doux);
  border-bottom: 1px solid var(--ligne);
}
.conseils-photo li:last-child { border-bottom: 0; }
.conseils-photo b { min-width: 16px; color: var(--texte); }

/* ═══ Recapitulatif du dernier ecran ═══════════════════════════════════════ */

.recapitulatif { margin: 0; }
.recapitulatif div {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 12px 0;
  font-size: 16px;
  border-bottom: 1px solid var(--ligne);
}
.recapitulatif span:first-child { color: var(--texte-doux); }
.recapitulatif span:last-child { text-align: right; font-weight: 600; }

/* Rappel du dernier ecran, quand le profil n'a pas ete choisi. */
.rappel-profil {
  padding: 14px 18px;
  margin: 0 0 20px;
  font-size: 16px;
  background: var(--avertissement-fond);
  border-left: 6px solid var(--avertissement-bordure);
  border-radius: 10px;
  color: var(--texte);
}
.lien-retour-profil {
  font-family: inherit;
  font-size: 16px;
  color: var(--texte);
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* ═══ Erreurs ══════════════════════════════════════════════════════════════ */

.message-erreur {
  display: block;
  margin: 8px 0 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--erreur);
}
/* ⚠️ TOUT LE CADRE VIRE AU ROUGE depuis le 2026-08-13, plus seulement le trait du
   bas — sans quoi un champ en erreur serait moins visible qu'avant le passage aux
   bordures completes. Le rouge ne va JAMAIS seul : le message ecrit reste, et la
   secousse aussi (voir plus bas). Un homme sur douze distingue mal le rouge. */
.champ-en-erreur input,
.champ-en-erreur select,
.champ-en-erreur textarea { border-color: var(--erreur); border-width: 2px; }

/* 🔴 SECOUSSE SUR ERREUR, AJOUTEE LE 2026-08-12 (demande du porteur). Elle joue
   TOUTE SEULE des que la classe se pose : au premier chargement (erreurs posees par
   le serveur), l'animation part avec la page — rien a faire cote script. Cote
   client, formulaire.js retire puis repose la classe pour forcer une rejoue meme
   si le champ etait DEJA en erreur (deuxieme clic sur « Continuer » sans avoir rien
   corrige) : sans ce va-et-vient, ajouter une classe deja presente ne redeclenche
   aucune animation, et le champ resterait muet la deuxieme fois.
   ⚠️ LE ROUGE, LUI, NE BOUGE PAS : cette regle n'anime QUE le mouvement, jamais la
   couleur ni le texte de l'erreur — l'information reste lisible pendant le mouvement. */
.champ-en-erreur { animation: secousse-champ .4s ease; }

@keyframes secousse-champ {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}

@media (prefers-reduced-motion: reduce) {
  .champ-en-erreur { animation: none; }
}

.resume-erreurs {
  padding: 18px 22px;
  margin: 0 0 24px;
  background: var(--erreur-fond);
  border-left: 6px solid var(--erreur);
  border-radius: 10px;
}
.resume-erreurs h2 { color: var(--erreur); margin: 0 0 10px; font-size: 19px; }
.resume-erreurs ul { margin: 0; padding-left: 22px; }
.resume-erreurs li { margin: 6px 0; }
.resume-erreurs a { color: var(--erreur); }

.alerte-haut-de-page {
  padding: 18px 22px;
  margin: 0 0 24px;
  background: var(--avertissement-fond);
  border-left: 6px solid var(--avertissement-bordure);
  border-radius: 10px;
}

.avertissement {
  padding: 16px 20px;
  margin: 14px 0 0;
  font-size: 16px;
  background: var(--avertissement-fond);
  border-left: 6px solid var(--avertissement-bordure);
  border-radius: 10px;
}

/* ═══ Ecran de confirmation ════════════════════════════════════════════════ */

.marque-succes {
  display: grid;
  place-items: center;
  width: 64px; height: 64px;
  margin: 0 0 24px;
  font-size: 30px;
  color: var(--succes);
  border: 2px solid var(--succes);
  border-radius: 50%;
}

/* 🔴 LA CONFIRMATION DE REPONSE AU DEVIS : coche a gauche, texte a droite, et de l'air
   au-dessus comme en dessous (porteur, 2026-08-26 : « pour que ça ressorte un peu plus »).

   ⚠️ CES REGLES SONT PORTEES PAR `.confirmation-de-reponse`, ET SEULEMENT PAR ELLE.
      `.marque-succes` sert aussi au recu du formulaire d'intervention, ou la coche est posee
      AU-DESSUS du titre : la deplacer la-bas n'a ete demande par personne. */
.confirmation-de-reponse {
  display: flex;
  align-items: center;
  gap: 20px;
  margin: 40px 0 44px;
}

/* La coche ne se laisse ni retrecir ni ecraser par un titre qui passe sur trois lignes. */
.confirmation-de-reponse .marque-succes {
  flex: 0 0 auto;
  margin: 0;
}

.confirmation-de-reponse h1 { margin-top: 0; }
.confirmation-de-reponse p:last-child { margin-bottom: 0; }

/* ⚠️ SUR TELEPHONE LA COCHE MAIGRIT, ELLE NE PASSE PAS AU-DESSUS. A 64 px elle prenait le
   cinquieme de la largeur et laissait le titre se casser en cinq lignes. */
@media (max-width: 560px) {
  .confirmation-de-reponse { gap: 14px; margin: 28px 0 32px; }
  .confirmation-de-reponse .marque-succes {
    width: 46px; height: 46px;
    font-size: 22px;
  }
}

.numero-dossier {
  font-family: ui-monospace, "Courier New", monospace;
  font-size: clamp(26px, 5vw, 36px);
  font-weight: 700;
  letter-spacing: 0.06em;
  margin: 10px 0 14px;
}

.encadre-numero {
  padding: 24px;
  margin: 0 0 28px;
  border: 2px solid var(--texte);
  border-radius: 16px;
}

/* ═══ Piege anti-robot ═════════════════════════════════════════════════════
   Repousse hors de l'ecran, jamais display:none : certains robots ignorent les
   champs en display:none, ce qui viderait le piege de son interet. */

.champ-piege {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* ═══ Fenetre modale du surcout d'urgence ══════════════════════════════════ */

.fond-modale {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(0, 0, 0, .55);
  z-index: 100;
}
.modale {
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow-y: auto;
  padding: 28px;
  background: var(--fond);
  border-radius: 18px;
}
.modale h2 { margin-top: 0; }
.modale-boutons { display: grid; gap: 12px; margin-top: 24px; }
@media (min-width: 34rem) { .modale-boutons { grid-template-columns: 1fr 1fr; } }

/* ═══ Utilitaire ═══════════════════════════════════════════════════════════ */

.masque { display: none !important; }

/* ═══ Mise en page « un ecran par etape » ══════════════════════════════════
   ⚠️ TOUT CE QUI SUIT NE S'APPLIQUE QUE SI LE SCRIPT A POSE LA CLASSE
   « par-ecrans » SUR LE BODY. Sans JavaScript, rien de ceci n'existe : les
   quatre etapes s'affichent a la suite et la page defile normalement. C'est la
   regle non negociable du projet — le formulaire reste entierement utilisable
   sans JavaScript. */

/* ⚠️ C'EST LA PAGE QUI DEFILE, PAS UN BLOC A L'INTERIEUR. En-tete et barre d'actions
   sont COLLANTS ; entre les deux, le contenu suit le defilement normal de la fenetre.
   Ce montage a remplace le precedent le 2026-07-29, et il corrige trois defauts d'un
   coup, tous constates par le porteur sur des captures :

     1. LA BARRE DE DEFILEMENT ETAIT COLLEE AU FORMULAIRE, au milieu de la page. Elle
        est desormais celle de la fenetre, a l'extreme droite, la ou on l'attend.
     2. LE HAUT DU CONTENU ETAIT INACCESSIBLE. Un bloc qui defile ET dont le contenu
        est centre verticalement coupe son propre debut : ce qui depasse par le haut
        ne peut pas etre atteint. C'est un piege classique, et il ne se voit qu'avec
        un contenu plus grand que la fenetre.
     3. LA COLONNE DE DROITE PASSAIT SOUS LA BARRE. Plus de barre a cet endroit, donc
        plus de recouvrement, et le remplissage de compensation disparait.

   Le centrage vertical, lui, est conserve : quand le contenu tient, il reste centre ;
   quand il depasse, le conteneur grandit et rien n'est jamais coupe. */

body.par-ecrans .page {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

body.par-ecrans .entete {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--fond);
}

/* ⚠️ PAS DE REMPLISSAGE BAS POUR « COMPENSER » LA BARRE D'ACTIONS. J'en avais ajoute
   96 px en croyant que le dernier element resterait cache derriere elle : c'est faux,
   et cela allongeait la page de 96 px sur TOUS les ecrans, defilement compris.
   Un element « sticky » ne sort jamais de son conteneur : arrive en bas de page, la
   barre reprend sa place naturelle, sous le contenu, qui redevient entierement
   visible. C'est toute la difference avec « fixed ». */
/* 🔴 REMPLISSAGE BAS REDUIT LE 2026-08-13 (constat du porteur : une marge
   restait visible sous "Remarques diverses" sur le dernier ecran, forcant
   encore un defilement une fois les autres sources reglees le meme jour —
   bloc_confidentialite vide, grille du dernier champ). Celui-ci etait le
   dernier restant : 24px -> 8px. Le haut, lui, ne bouge pas : c'est ce qui
   espace le contenu de l'en-tete collant. */
body.par-ecrans .corps-formulaire {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 24px 0 8px;
}

/* ⚠️ LE PIED EST VERROUILLE EN BAS, ET IL NE BOUGE PLUS D'UN ECRAN A L'AUTRE.
   Avant : la page portait un remplissage bas de 32 px, si bien que le pied se
   trouvait a 32 px du bord quand le contenu tenait, et a 0 px quand il collait. Il
   sautait donc a chaque changement d'ecran. Le remplissage est desormais DANS le
   pied, ou il vaut la meme chose dans les deux cas. */
body.par-ecrans .page { padding-bottom: 0; }

body.par-ecrans .actions {
  position: sticky;
  bottom: 0;
  z-index: 20;
  padding: 18px 0 24px;
  background: var(--fond);
}

/* La marge basse du dernier bloc d'une etape ne sert a rien : la barre d'actions
   apporte deja sa propre separation. Mesure du 2026-07-29 : ce seul reglage a fait
   passer les ecrans 1 et 2 sous la barre du « tient sans defilement ». */
body.par-ecrans .etape > *:last-child { margin-bottom: 0; }
/* 🔴 AJOUTE LE 2026-08-13 (constat du porteur : un espace inutile subsistait
   quand meme, juste au-dessus de la barre d'actions — d'ou une barre de defilement
   qui n'aurait pas dû exister). CAUSE : quand le dernier bloc d'une etape est une
   grille (.deux-colonnes / .trois-colonnes), la regle ci-dessus retire la marge de
   LA GRILLE elle-meme — qui n'en a jamais eu — et non celle de ses CHAMPS internes
   (.champ), qui, eux, portent chacun leur propre marge basse. Une grille NE
   FUSIONNE PAS les marges de ses enfants avec celle du conteneur suivant (a la
   difference du flux normal) : la marge du dernier champ restait donc pleine,
   invisible a l'oeil comme "une marge du dernier bloc" parce qu'elle etait UNE
   grille plus bas. Retiree explicitement ici — SANS RIEN CHANGER AU RENDU : la
   barre d'actions apportait deja la meme separation visuelle par son propre
   padding-top, cette marge-ci ne faisait que s'y ajouter en double. */
body.par-ecrans .etape > *:last-child .champ { margin-bottom: 0; }
body.par-ecrans .etape { display: none; }
body.par-ecrans .etape.etape-visible { display: block; animation: apparition .3s ease; }

/* ═══ Le dernier ecran, sur deux colonnes ══════════════════════════════════
   Le recapitulatif remonte a hauteur du premier champ, dans la colonne de droite,
   au lieu de s'empiler sous la confidentialite. Mesure du 2026-07-29 : empile, il
   allongeait l'ecran de pres de deux cents pixels et obligeait a defiler pour lire
   quatre lignes. La classe est posee par le script, qui seul sait quel ecran est le
   dernier — il depend du profil. */

@media (min-width: 861px) {
  /* ⚠️ LA GRILLE SE POSE SUR LE FORMULAIRE, PAS SUR LE CORPS. Les sections sont
     enfants du <form>, lui-meme enfant du corps : posee sur le corps, la grille
     n'avait qu'un seul enfant a placer — le formulaire entier — qui se retrouvait
     comprime dans la premiere colonne, tandis que le recapitulatif restait empile
     dessous. */
  body.par-ecrans .corps-formulaire.dernier-ecran #formulaire_demande {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(240px, 320px);
    gap: clamp(24px, 4vw, 56px);
    align-items: start;
  }
  /* ⚠️ SEUL LE RECAPITULATIF EST PLACE EXPLICITEMENT. Tout le reste garde la
     premiere colonne et S'EMPILE, sans numero de ligne impose.

     Le premier jet fixait « grid-row: 1 » a toutes les sections de champs. Or il
     peut y en avoir DEUX a l'ecran : tant qu'aucun profil n'est choisi, les
     parcours particulier et professionnel sont tous deux visibles — c'est voulu,
     c'est le serveur qui tranche. Elles atterrissaient alors dans la MEME cellule
     et s'affichaient l'une par-dessus l'autre, titres et champs melanges.
     Constate par le porteur le 2026-07-29, capture a l'appui. */
  body.par-ecrans .corps-formulaire.dernier-ecran #formulaire_demande > .etape-visible:not(#recapitulatif) {
    grid-column: 1;
  }
  /* ⚠️ LE RECAPITULATIF RESTE A LA MEME HAUTEUR PENDANT TOUT LE DEFILEMENT
     (demande du porteur du 2026-07-31). C'est le dernier ecran qui defile le plus —
     coordonnees, remarques et confidentialite empilees — et sans cela, le resume de
     la demande disparaissait par le haut des le premier mouvement.

     Trois conditions, et il faut les trois :
       - « align-self: start » : sans lui, l'element de grille est etire sur toute la
         hauteur de la ligne, et un element qui remplit deja son conteneur n'a aucune
         place pour coller. La grille porte « align-items: start », qui suffit ; c'est
         redit ici parce que c'est la condition la moins evidente des trois, et la
         premiere qu'on casse en modifiant la grille.
       - « top » SOUS L'EN-TETE COLLANT. Mesure du 2026-07-31 : avec top: 24px,
         l'en-tete descendait a 104 px et recouvrait 70 px du recapitulatif, titre
         compris. La regle avait l'air de marcher tout en masquant ce qu'elle devait
         garder visible. La hauteur de l'en-tete n'est pas fixe — le logo suit la
         largeur — donc elle est mesuree par formulaire.js, qui pose
         « --hauteur-entete ». La valeur de repli couvre le cas ou le script n'aurait
         pas encore mesure ; sans script, cette regle ne s'applique pas du tout,
         faute de classe « dernier-ecran ».
       - un z-index inferieur a 20, celui de l'en-tete et de la barre d'actions : le
         recapitulatif passe DERRIERE elles, jamais devant. */
  body.par-ecrans .corps-formulaire.dernier-ecran #formulaire_demande > #recapitulatif {
    grid-column: 2;
    grid-row: 1;
    position: sticky;
    top: calc(var(--hauteur-entete, 110px) + 8px);
    align-self: start;
    z-index: 10;
  }

  /* ⚠️ Les champs repassent sur UNE colonne PAR DEFAUT des 861px. La place de la
     seconde est prise par le recapitulatif : la garder revenait a couper la colonne
     de gauche en deux, et les champs tombaient a 220 px de large — voir la regle
     plus bas qui la reactive au-dela de 1100px, une fois qu'il y a assez de place. */
  body.par-ecrans .corps-formulaire.dernier-ecran #formulaire_demande > .etape-visible .deux-colonnes {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

/* 🔴 AJOUTE LE 2026-08-12 (demande du porteur : « 2 champs par ligne », y compris
   sur le dernier ecran). La regle ci-dessus force une seule colonne des 861px,
   parce qu'a l'epoque (page a 1000px de large) la colonne principale, une fois le
   recapitulatif deduit, ne laissait que ~220px par champ en la redivisant — trop
   etroit. Depuis l'elargissement de .page a 1300px (meme session), cette colonne a
   repris assez de place au-dela de 1100px pour se rediviser sans repasser sous les
   ~280px mesures a la main. En dessous de 1100px, c'est la regle du dessus qui
   continue de s'appliquer : UNE seule colonne, deliberement. */
@media (min-width: 1100px) {
  body.par-ecrans .corps-formulaire.dernier-ecran #formulaire_demande > .etape-visible .deux-colonnes {
    grid-template-columns: 1fr 1fr;
    gap: clamp(20px, 4vw, 48px);
  }
}

@keyframes apparition {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* Sur un ecran bas — un portable, ou une fenetre encombree de barres d'outils — le
   centrage n'a plus de sens : le contenu commence sous l'en-tete et la page defile. */
@media (max-height: 700px) {
  body.par-ecrans .corps-formulaire { justify-content: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  .progression span, body.par-ecrans .etape.etape-visible { transition: none; animation: none; }
}

/* ═══ TELEPHONE : ON AERE, PARCE QU'ON A RENONCE A FAIRE TENIR ═════════════
   Postulat pose par le porteur le 2026-07-31 : sur un telephone, l'ecran est
   trop petit, le defilement est de toute facon inevitable. Tout ce qui etait
   comprime pour gagner quelques pixels ne gagnait donc rien — et rendait la
   saisie penible pour des techniciens de 45-60 ans, souvent debout, parfois
   avec des gants.

   ⚠️ CE BLOC VA DANS LE SENS INVERSE DE CELUI DU 2026-07-29, ET C'EST VOULU.
   L'exigence « chaque etape tient dans un ecran » reste valable sur ordinateur,
   ou elle est tenue. Sous 620 px, elle est abandonnee au profit du confort de
   saisie. Ne pas « re-optimiser » ces valeurs a la baisse en croyant bien faire :
   ce serait defaire une decision, pas corriger un oubli.

   Les tailles de police, les cibles de 44 px et les contrastes ne bougent pas :
   ce sont des criteres de recette, ils s'appliquent partout. */
@media (max-width: 620px) {

  /* 🔴 AJOUTE LE 2026-08-13 (captures du porteur sur iPhone/Safari) : l'en-tete
     "sticky" se recalait avec un temps de retard chaque fois que Safari iOS
     retracte ou redeploie sa barre d'adresse en cours de defilement — le texte
     deja defile restait visible un instant derriere le logo. Sans "sticky",
     l'en-tete defile normalement avec le reste : le defaut n'a plus l'occasion
     de se produire. Va dans le meme sens que tout ce bloc : sur telephone, on
     ne cherche plus a "tenir" quoi que ce soit a l'ecran. */
  body.par-ecrans .entete { position: static; }

  /* 🔴 LA BARRE DU BAS NE COLLE PLUS : ELLE EST EN BAS DE PAGE, DANS LE FLUX
     (decision du porteur, 2026-08-13, apres plusieurs essais le meme jour).

     TOUT CE FIL VIENT D'UN SEUL DEFAUT : sur Safari iOS, « bottom: 0 » designe
     le bas du viewport de MISE EN PAGE, pas celui de la zone reellement visible
     — plus petite, la barre d'outils du navigateur en mangeant le bas. Il
     restait donc une bande ou le contenu defilait a decouvert, SOUS le bouton.
     Un fond opaque, puis un masque d'une hauteur d'ecran entiere, n'ont rien
     change. On a ensuite retire le fond (l'ecran « etouffait »), ce qui a rendu
     visibles deux details que le fond cachait : un filet gris en travers du
     contenu, et l'ombre du bouton qui grisait le texte defilant derriere.

     ⚠️ SANS COLLAGE, CES TROIS PROBLEMES N'EXISTENT PLUS DU TOUT : plus rien ne
     passe derriere la barre, donc plus rien a masquer, aucun filet a retirer, et
     l'ombre du bouton retombe sur le fond de la page comme celle de n'importe
     quel autre bouton. C'est la seule des quatre tentatives qui supprime la
     cause au lieu d'en traiter les effets.
     ⚠️ NE PAS RETABLIR « position: sticky » ICI EN CROYANT CORRIGER UN OUBLI :
     c'est une decision, et elle a coute quatre allers-retours. Sur grand ecran,
     en revanche, la barre reste collee — cette mise en page est validee. */
  body.par-ecrans .actions { position: static; }

  /* Des marges laterales qui laissent respirer le texte, sans le coller au bord. */
  .page { padding-left: 28px; padding-right: 28px; }

  /* L'ecart entre deux champs passe d'environ 17 px a 30 px. C'est le reglage qui
     fait le plus de difference : c'est lui qui donnait l'impression de tassement. */
  .champ { margin-bottom: 30px; }

  /* Un intitule colle a son champ se lit mal ; quatre pixels ne suffisaient pas. */
  label, .intitule-groupe { margin-bottom: 8px; }

  /* Les champs eux-memes gagnent en hauteur de frappe. */
  input[type="text"],
  input[type="email"],
  input[type="tel"],
  select,
  textarea { padding: 14px 12px; }

  /* Les cases a cocher etaient separees de 2 px : elles formaient un pave. Douze
     pixels les rendent distinctes, et la cible de 44 px reste ce qu'elle etait. */
  .case + .case { margin-top: 12px; }
  .case { gap: 16px; }

  /* Une consigne collee sous son champ se confond avec lui. */
  .consigne { margin-top: 10px; }

  /* 🔴 LES BLOCS DU DERNIER ECRAN SE TOUCHAIENT — signale par le porteur le
     2026-08-17, capture a l'appui : « la derniere page a oublie des marges ».

     LA CAUSE N'EST PAS UNE MARGE OUBLIEE, C'EST UNE MARGE RETIREE EXPRES :
     « body.par-ecrans .etape > *:last-child { margin-bottom: 0 } », mesuree le
     2026-07-29, supprime la marge basse du dernier element de CHAQUE etape. Elle
     etait juste tant qu'UN SEUL bloc s'affichait a la fois — la barre d'actions
     apportait la separation, et cette marge s'y ajoutait en double.

     ⚠️ MAIS LE DERNIER ECRAN EN AFFICHE QUATRE : le rappel de profil, les
     coordonnees, le recapitulatif, les remarques et la confidentialite portent tous
     « data-etape="derniere" ». Un seul touche la barre d'actions ; les autres se
     retrouvaient colles a leur voisin — le champ e-mail contre « Votre demande », le
     recapitulatif contre « Remarques diverses », le textarea contre la case a cocher.

     ⚠️ LE SELECTEUR EST EN « ~ » ET NON EN « + », ET C'EST INDISPENSABLE : ces blocs
     ne sont PAS voisins dans le document — les etapes 1 a 3, masquees, sont
     intercalees entre eux. Le combinateur adjacent ne les verrait jamais. « ~ »
     designe tout bloc visible precede d'un autre bloc visible, quels que soient les
     blocs masques entre les deux.

     ⚠️ ON POSE LA MARGE EN HAUT, PAS EN BAS. En bas, elle serait reprise par la regle
     ci-dessus, qu'on ne veut pas defaire : elle reste juste pour le bloc qui touche
     effectivement la barre d'actions.

     Les 30 px sont ceux de « .champ » sur telephone, valides le 2026-08-13. */
  body.par-ecrans .etape.etape-visible ~ .etape.etape-visible { margin-top: 30px; }

  /* 🔴 LES CHAMPS DU DERNIER BLOC N'AVAIENT PLUS AUCUN ECART — signale par le porteur
     le 2026-08-17 : « espace et aere les champs ». Mesure sur sa capture : 5 a 6 px
     entre le bas d'un champ et l'intitule du suivant, la ou « .champ » en prevoit 30.

     DEUX REGLES JUSTES SE CUMULAIENT EN UNE FAUSSE :

       1. « @media (max-width: 860px) { .deux-colonnes { gap: 0 } } » — la grille passe
          a une colonne et rend l'espacement aux champs, qui portent deja leur marge.
          Sans ce zero, gap et marge s'additionneraient.
       2. « body.par-ecrans .etape > *:last-child .champ { margin-bottom: 0 } », posee le
          2026-08-13 pour supprimer l'espace inutile AU-DESSUS DE LA BARRE D'ACTIONS.

     La seconde vise « tous les .champ du dernier bloc », alors qu'elle ne voulait que
     LE DERNIER. Avec une grille a une colonne — donc sans gap — il ne restait plus rien
     entre deux champs que les 8 px de l'intitule.

     ⚠️ SUR GRAND ECRAN, RIEN NE SE VOYAIT : la grille y garde un gap de 20 a 48 px, qui
     masquait l'absence de marge. Le defaut n'existait qu'a l'endroit ou personne ne
     regardait le CSS.

     ⚠️ « :not(:last-child) » PLUTOT QUE DE DEFAIRE LA REGLE DE 2026-08-13 : son intention
     reste valable pour le dernier champ, celui qui touche la barre d'actions. On lui
     rend sa portee d'origine au lieu de l'annuler. */
  body.par-ecrans .etape > *:last-child .champ:not(:last-child) { margin-bottom: 30px; }

  /* Le titre et son sous-titre respirent, et se detachent du bloc qui suit. */
  h1 { margin-bottom: 10px; }
  .sous-titre { margin-bottom: 26px; }

  /* Le corps ne se centre plus verticalement : il commence sous l'en-tete et
     defile. Centre, il produisait des vides en haut et en bas sur les ecrans
     courts, et rien du tout sur les longs. */
  body.par-ecrans .corps-formulaire {
    justify-content: flex-start;
    padding: 20px 0 12px;
  }

  /* ⚠️ LE COMPTEUR « Etape 2 sur 5 » DISPARAIT DE LA BARRE DU BAS, et ce n'est pas
     une perte : il s'y cassait sur deux lignes, ce qui poussait le bouton et
     donnait la barre tassee que le porteur a vue. L'information reste a l'ecran,
     deux fois — dans la barre de progression en haut, et dans l'intitule
     « ETAPE 2 SUR 5 — LA PANNE » au-dessus du titre. */
  .actions .compteur-etapes { display: none; }
  .actions { gap: 12px; padding: 16px 0 20px; }

  /* L'encart « Deja client ? » etait le bloc le plus dense de l'ecran 1. */
  .encart-deja-client { padding: 20px; }

  /* Les deux boutons de profil passent l'un sous l'autre a cette largeur : 8 px
     entre eux les faisaient presque se toucher. Ce sont deux choix opposes, ils
     doivent se distinguer au premier coup d'oeil. */
  .choix-demandeur { gap: 12px; }

  /* ═══ LA PROGRESSION ENTRE DANS LE BOUTON « CONTINUER » ═══════════════════
     Demande du porteur, 2026-08-13 : « une facon elegante d'integrer la barre
     de progression DANS le bouton continuer ». Elle etait en haut de l'ecran,
     ou elle se confondait avec l'ombre de la barre d'adresse de Safari.

     LA FORME RETENUE : un liseré qui epouse le bas du bouton et se remplit de
     gauche a droite au fil des etapes. Il est A L'INTERIEUR — c'est
     « overflow: hidden » sur le bouton qui le fait suivre l'arrondi de la
     pilule, sans avoir a redessiner ce rayon une seconde fois ici.

     ⚠️ LE NOEUD EST DEPLACE DANS LE BOUTON PAR formulaire.js, sur telephone
     seulement. Sur grand ecran il reste en haut de l'ecran : c'est la mise en
     page que le porteur a validee, et elle ne doit plus bouger.
     ⚠️ « pointer-events: none » : ce liseré couvre toute la largeur du bouton,
     il ne doit jamais intercepter le clic qui doit atteindre le bouton lui-meme.
     ⚠️ Le fond du liseré (--ligne) reste discret : c'est la portion REMPLIE qui
     porte l'information, et elle est en --texte, la couleur la plus contrastee
     du theme. */
  body.par-ecrans .bouton { position: relative; overflow: hidden; }
  body.par-ecrans .bouton > .progression {
    position: absolute;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    height: 4px;
    pointer-events: none;
  }
}

/* ═══ LE FOND DU THEME CLAIR EST ECLAIRCI SUR TELEPHONE ════════════════════
   Demande du porteur, 2026-08-13 : #e8e8e8 -> #f0f0f0, sur petit ecran seulement.

   POURQUOI UNE VALEUR DIFFERENTE POUR LE MEME THEME : le code envoie la meme
   couleur partout, c'est l'ECRAN qui la rend autrement. Sur l'iPhone du porteur,
   ce gris parait « beaucoup plus sombre » que sur son ordinateur — dalle,
   luminosite, et sur iOS le reglage True Tone, qui rechauffe et assombrit
   l'affichage selon la lumiere ambiante. Aucune regle CSS ne peut connaitre cet
   ecart : on le compense a la main, la ou il se voit.

   ⚠️ SANS RISQUE POUR LES CONTRASTES, ET C'EST VERIFIE PAR CALCUL : eclaircir le
   fond ne fait qu'AUGMENTER l'ecart avec les textes sombres et la bordure des
   champs (3,36:1 -> 3,61:1 pour la plus juste d'entre elles). C'est l'inverse qui
   aurait oblige a tout remesurer.

   🔴 DEUX REGLES, ET IL FAUT LES DEUX — c'est ce qui evite d'eclaircir le fond
   d'un ecran en mode SOMBRE. Un simple « :root:not([data-theme="sombre"]) »
   aurait touche l'utilisateur dont le systeme est en sombre sans qu'il ait rien
   clique : il n'a pas d'attribut data-theme, donc la regle l'aurait attrape et
   lui aurait pose un fond clair sur un theme sombre.
     1. systeme en clair, sauf si le visiteur a choisi « sombre » ;
     2. visiteur ayant explicitement choisi « clair », meme si son systeme est en
        sombre.
   ⚠️ ELLES VIVENT EN FIN DE FICHIER, APRES les blocs de theme : a specificite
   egale, c'est la derniere regle lue qui gagne. Les remonter plus haut les
   rendrait sans effet. */
@media (max-width: 620px) and (prefers-color-scheme: light) {
  :root:not([data-theme="sombre"]) { --fond: #f0f0f0; }
}
@media (max-width: 620px) {
  :root[data-theme="clair"] { --fond: #f0f0f0; }
}

/* ═══ PAGE DE PRÉSENTATION (ébauche, 2026-08-14) ═══════════════════════════
   Demande du porteur : une page d'accueil « avec comme objet central le bouton de
   demande d'intervention ». Tout ce bloc ne sert qu'à site-public/accueil.php.

   ⚠️ AUCUNE COULEUR NOUVELLE ICI, ET C'EST DÉLIBÉRÉ : la page réutilise les
   variables du thème (--texte, --texte-doux, --bordure-champ…), déjà mesurées et
   valables dans les deux thèmes. Une teinte inventée pour l'occasion aurait échappé
   à ces mesures.
   ⚠️ ELLE RÉUTILISE AUSSI .bouton TEL QUEL — même relief, même forme que partout
   ailleurs. Seule sa TAILLE change ici (.bouton-appel), parce que c'est le seul
   geste attendu de la page. */

/* Le bouton d'appel : c'est le même bouton que partout, en plus grand. */
.bouton-appel {
  min-height: 60px;
  padding: 16px 40px;
  font-size: 18px;
}

/* ── 1. L'ouverture ──────────────────────────────────────────────────────── */
.ouverture {
  padding: clamp(28px, 6vh, 56px) 0 clamp(32px, 7vh, 64px);
  text-align: center;
  border-bottom: 1px solid var(--ligne);
}
.ouverture h1 {
  font-size: clamp(28px, 5vw, 44px);
  margin: 0 0 16px;
}
/* La largeur de lecture est bornée : une ligne de texte trop longue se relit mal,
   et l'œil perd le début de la suivante. */
.ouverture-texte {
  max-width: 44rem;
  margin: 0 auto clamp(24px, 4vh, 36px);
  font-size: 18px;
  color: var(--texte-doux);
}
.ouverture-precision {
  margin: 18px 0 0;
  font-size: 15px;
  color: var(--texte-faible);
}

/* ── 2. Les prestations ──────────────────────────────────────────────────── */
.prestations, .deroule { padding: clamp(28px, 6vh, 52px) 0; }
.prestations h2, .deroule h2, .rappel-appel h2 {
  font-size: clamp(20px, 3vw, 26px);
  margin: 0 0 clamp(20px, 3vh, 28px);
}

/* « auto-fit » plutôt qu'un nombre de colonnes fixe : la grille se réorganise
   d'elle-même selon la place, sans qu'on ait à écrire un point de rupture par
   largeur d'écran. Sur téléphone, les quatre cartes s'empilent naturellement. */
.grille-prestations {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(16px, 2.5vw, 28px);
}
.prestation {
  padding: 22px;
  border-radius: 12px;
  background: var(--survol);
}
.prestation h3 { font-size: 17px; margin: 0 0 10px; }
.prestation p { margin: 0; color: var(--texte-doux); }

/* ── 3. Le déroulé ───────────────────────────────────────────────────────── */
.etapes-deroule { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.etapes-deroule li {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  padding: 16px 0;
  border-bottom: 1px solid var(--ligne);
}
.etapes-deroule li:last-child { border-bottom: 0; }
/* Le numéro d'étape, dans une pastille : même repère visuel que les conseils de
   l'étape photos du formulaire. */
.etapes-deroule b {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--texte);
  color: var(--texte-inverse);
  font-size: 16px;
}
.etapes-deroule h3 { font-size: 17px; margin: 4px 0 6px; }
.etapes-deroule p { margin: 0; color: var(--texte-doux); }

/* ── 4. Le rappel ────────────────────────────────────────────────────────── */
.rappel-appel {
  padding: clamp(28px, 6vh, 52px) 0 clamp(36px, 7vh, 60px);
  text-align: center;
  border-top: 1px solid var(--ligne);
}
.rappel-appel p {
  margin: 0 0 clamp(20px, 3vh, 28px);
  color: var(--texte-doux);
  font-size: 17px;
}

/* Le logo du formulaire est un lien vers l'accueil (2026-08-14). « display: block »
   evite l'espace fantome qu'un navigateur reserve sous une image en ligne, qui
   decalait l'accroche juste en dessous. */
.lien-logo { display: block; }

/* ═══ Un bloc révélé par une case — la facturation Chorus Pro (2026-08-22) ═══ */

/* ⚠️ LA MARGE HAUTE EST CELLE QUI MANQUAIT : le bloc apparaît collé sous sa case, et sans
   elle on ne voit pas qu'il s'agit d'un contenu neuf apparu à l'instant. */
.bloc-revele {
  margin-top: 14px;
  padding-left: 14px;
  border-left: 3px solid var(--ligne);
}

/* ⚠️ « [hidden] » NE SUFFIT PAS TOUJOURS : une règle d'affichage posée ailleurs sur le
   sélecteur peut l'emporter sur le style par défaut du navigateur. On le dit explicitement
   plutôt que de compter dessus. */
.bloc-revele[hidden] {
  display: none;
}

/* La phrase qui accompagne les deux champs, plus discrète qu'un libellé. */
.note-de-champ {
  margin: 10px 0 0;
  font-size: .9rem;
  color: var(--texte-doux);
}

/* ═══ Le panneau d'adresses : titre, état, roue (2026-08-22) ════════════════ */

/* 🔴 UN TITRE, DEMANDÉ PAR LE PORTEUR. Le panneau s'ouvre désormais dès la première lettre,
   parfois sur une seule phrase d'état : sans titre, un rectangle apparaît sous le champ sans
   qu'on sache ce qu'il est. */
.titre-propositions {
  padding: 6px 14px 4px;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--texte-faible);
  /* ⚠️ Il n'est pas cliquable, et rien ne doit laisser croire le contraire. */
  cursor: default;
}

/* La ligne d'état — « Continuez à saisir », « Recherche des adresses », un échec.
   ⚠️ ELLE N'A PAS LA CIBLE DE 44 px des propositions : on ne clique pas dessus. Lui donner
   la même hauteur la ferait passer pour un choix possible. */
.etat-propositions {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px 12px;
  color: var(--texte-doux);
  cursor: default;
}

.roue-de-chargement {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: 2px solid var(--ligne);
  border-top-color: var(--texte-doux);
  border-radius: 50%;
  animation: rotation-de-la-roue .7s linear infinite;
}

@keyframes rotation-de-la-roue {
  to { transform: rotate(360deg); }
}

/* ⚠️ UNE ANIMATION QUI TOURNE SANS FIN EST EXACTEMENT CE QUE « prefers-reduced-motion »
   désigne. On ne la supprime pas — la roue est le signal qu'il se passe quelque chose — on
   la ralentit assez pour qu'elle cesse d'être un mouvement continu agressif. */
@media (prefers-reduced-motion: reduce) {
  .roue-de-chargement {
    animation-duration: 2.4s;
  }
}

/* ══ La page du devis (spec 11 § 7.2) ═════════════════════════════════════════
   🔴 ELLE EST LUE PAR UN CLIENT, PAS PAR LE PORTEUR. C'est le seul écran du produit où
      quelqu'un engage de l'argent, et il y arrive par un lien reçu dans un e-mail.

   🔴 ELLE REPREND LA MISE EN PAGE DE LA VITRINE (porteur, 2026-08-26) : `.page`, `.entete`,
      `.bloc-marque`, `.etape`, `.encadre-numero`, `.bouton`, `.bloc-revele` — toutes déjà
      définies plus haut dans ce fichier. Les quelques règles ci-dessous ne couvrent que ce
      qui n'existait nulle part : la rangée de boutons et la liste des devis précédents.

   ⚠️ UN BLOC ENTIER A ETE RETIRE ICI LE 2026-08-26. Il inventait `.bandeau-information`,
      `.bandeau-succes` et `.bandeau-erreur` pour cette seule page — c'est exactement ce que
      « se caler sur la vitrine » interdit. */

/* 🔴 TROIS CINQUIEMES POUR LE DOCUMENT, DEUX POUR LE RESTE (porteur, 2026-08-26). Le devis
   se lit sur la page ; tout ce qui se décide — le montant, les boutons, le transfert — se
   range à droite.

   ⚠️ UNE SEULE COLONNE SOUS 900 px, et le document PASSE EN PREMIER. C'est l'ordre du
   document HTML, et c'est le bon : on lit ce qu'on nous propose avant de décider. */
/* 🔴 « minmax(0, …) » ET NON « 1fr » TOUT COURT, DANS LES DEUX CAS. Une colonne de grille
   refuse par defaut de devenir plus etroite que son contenu — et le lecteur de PDF integre
   au navigateur reclame quatre cents pixels pour sa barre d'outils. Sur un telephone de 390,
   la page entiere debordait vers la droite et « Je refuse » sortait de l'ecran. */
.devis-en-deux-colonnes {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  margin-top: 20px;
}

@media (min-width: 900px) {
  .devis-en-deux-colonnes {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    align-items: start;
  }
}

.cote-du-devis { min-width: 0; }

/* ⚠️ LA HAUTEUR EST EN dvh, PAS EN vh : sur mobile, 100vh compte la barre d'adresse même
   rétractée, et le cadre déborderait du bas de l'écran. */
.apercu-du-devis {
  min-height: 60dvh;
  border: 1px solid var(--ligne);
  border-radius: 12px;
  overflow: hidden;
  background: #525659;   /* le gris des lecteurs de PDF : la page blanche s'y détache */
}

@media (min-width: 900px) {
  .apercu-du-devis { min-height: 78dvh; }
}

.apercu-du-devis iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-height: inherit;
  border: 0;
}

/* Les repères du devis : des couples courts, alignés, sans encadré ni gros chiffre. */
.reperes-du-devis {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 8px 16px;
  margin: 0 0 18px;
}

.reperes-du-devis > div { display: contents; }

.reperes-du-devis dt {
  color: var(--texte-doux);
  font-size: .9rem;
}

.reperes-du-devis dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* ⚠️ LE MONTANT EST EN GRAS, PAS EN GRAND. Il doit se trouver du regard sans être la
   première chose qu'on voit — c'est le document qui doit l'être. */
.montant-du-devis { font-weight: 600; }

/* Les deux boutons de réponse. Sur téléphone ils s'empilent et prennent toute la largeur :
   viser un bouton de la moitié de l'écran, au pouce, dans un couloir, est le cas réel. */
/* ⚠️ 20 px D'ECART, ET NON 10 (porteur, 2026-08-26 : « ça minimise l'ombrage de je refuse »).
   Il avait vu juste : le relief de ces boutons est une ombre portee de 8 px de flou de chaque
   cote. A dix pixels d'ecart, celle du bouton de gauche mange celle du bouton de droite, et
   « Je refuse » paraissait plat — donc moins cliquable que son voisin. */
.actions-du-devis {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin: 18px 0 0;
}

.actions-du-devis[hidden] { display: none; }

/* 🔴 LES DEUX REPONSES SE DISTINGUENT A LA COULEUR (porteur, 2026-08-26). Sur la seule page
   du site ou un clic engage, deux boutons gris cote a cote se ressemblent trop : on peut
   refuser en croyant accepter.

   ⚠️ LA TEINTE EST LEGERE, ET C'EST DEMANDE. Elle ne remplace pas le libelle — c'est
      « J'accepte » et « Je refuse » qui informent, la couleur ne fait que confirmer. Un
      daltonien lit les mots, et rien ne se perd. */
.bouton-accepte {
  color: var(--succes);
  background: var(--accepte-fond);
}

.bouton-refuse {
  color: var(--erreur);
  background: var(--refuse-fond);
}
.actions-du-devis .bouton { flex: 1 1 200px; text-align: center; }

@media (min-width: 620px) {
  .actions-du-devis .bouton { flex: 0 0 auto; }
}

/* ⚠️ `.bandeau-information` EST RESTEE, ELLE. Les mentions légales et la politique de
   confidentialité l'emploient depuis le 2026-08-09 ; la retirer les aurait ramenées à un
   <div> nu, pour un problème qui ne venait pas d'elles. */
.bandeau-information {
  margin: 16px 0;
  padding: 14px 16px;
  border: 1px solid var(--ligne);
  border-left-width: 4px;
  border-radius: 10px;
}

.bandeau-information p { margin: 6px 0; }
.bandeau-information p:first-child { margin-top: 0; }
.bandeau-information p:last-child { margin-bottom: 0; }

/* Les devis précédents : une ligne par devis, lisible sans tableau. */
.devis-precedents {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
}

.devis-precedents li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  padding: 10px 0;
  border-top: 1px solid var(--ligne);
}

.devis-precedent-numero { font-weight: 600; }
.devis-precedent-montant { font-variant-numeric: tabular-nums; }
.devis-precedent-etat { color: var(--texte-doux); flex: 1 1 auto; }

/* Le transfert à un autre décideur : un bloc à part, sous les deux boutons de réponse.
   ⚠️ IL EST VISUELLEMENT SECONDAIRE, ET IL LE FAUT : cette page existe pour obtenir une
      réponse, pas pour faire circuler un document. */
.bloc-transfert {
  margin: 32px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--ligne);
}

.bloc-transfert h2 { margin: 0 0 6px; font-size: 1.05rem; }
.bloc-transfert p { margin: 0 0 12px; color: var(--texte-doux); }

.formulaire-transfert label {
  display: block;
  margin-bottom: 6px;
  font-size: .95rem;
}

.ligne-de-transfert {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.ligne-de-transfert input { flex: 1 1 220px; min-width: 0; }
.ligne-de-transfert .bouton { flex: 0 0 auto; }

/* La fenêtre du mot d'accompagnement d'un transfert.
   ⚠️ Même géométrie que la modale du surcoût d'urgence du formulaire : deux fenêtres du même
      site n'ont aucune raison de ne pas se ressembler. */
.fenetre-du-mot {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(0, 0, 0, .55);
}

.fenetre-du-mot[hidden] { display: none; }

.fenetre-du-mot-cadre {
  width: 100%;
  max-width: 520px;
  max-height: 92dvh;
  overflow: auto;
  padding: 22px;
  background: var(--fond);
  border-radius: 16px;
}

.fenetre-du-mot-cadre h3 { margin: 0 0 8px; }
.fenetre-du-mot-cadre p { margin: 0 0 12px; }
.fenetre-du-mot-cadre label { display: block; margin-bottom: 6px; }

/* 🔴 L'AVERTISSEMENT SE VOIT, SANS CRIER. Il est encadré et non peint en rouge : ce n'est pas
   une erreur, c'est une consigne — et un rouge employé ici affaiblirait celui des vraies
   erreurs, ailleurs sur le site. */
.avertissement-du-mot {
  padding: 12px 14px;
  border: 1px solid var(--ligne);
  border-left: 4px solid var(--texte);
  border-radius: 10px;
  font-size: .92rem;
}
