/* ===========================================================================
   Design system Sooluo

   Contraintes issues du cahier des charges, section « Contexte terrain » :
   l'utilisateur est un technicien debout dans une chaufferie ou des combles,
   tablette à la main, devant un client qui attend.

     1. Cibles tactiles larges       -> --touch (48 px minimum)
     2. Pas de survol                -> aucun état ne dépend de :hover seul
     3. Lisibilité en faible lumière -> contrastes élevés, corps de texte 17 px
     4. Mode client                  -> voir .client-mode en fin de fichier
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Polices embarquées

   Aucune police n'est chargée depuis un CDN : le §9.2 prévoit des connexions
   dégradées en sous-sol ou en combles, et une police manquante ferait basculer
   tous les écrans sur un rendu de secours au pire moment.

   Inter est une police VARIABLE : un seul fichier couvre toutes les graisses.
   Les trois fichiers livrés par Google Fonts étaient identiques à l'octet près.

   font-display: swap — le texte s'affiche immédiatement dans la police de
   secours puis bascule. Sur le terrain, lire tout de suite prime sur voir la
   bonne police.
--------------------------------------------------------------------------- */

@font-face {
  font-family: "Inter var";
  src: url("/assets/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Space Grotesk pour le titrage. Poppins, sa géométrie ronde et ses graisses
   800, donnaient à l'application l'air d'un gabarit de présentation ; Space
   Grotesk est une grotesque technique, dessinée pour les chiffres et les
   étiquettes, et c'est ce que lit un installateur chez ses concurrents. Une
   seule fabrique variable de 400 à 700, sous-ensemble latin : 22 Ko, et sa
   plage couvre l'euro, le vrai signe moins des températures et l'espace fine
   insécable des montants. */
@font-face {
  font-family: "Space Grotesk";
  src: url("/assets/fonts/space-grotesk-variable.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

/* IBM Plex Mono pour TOUT ce qui se compare : montant, date, référence,
   étiquette de section (D-178).
 *
 * POURQUOI UNE TROISIÈME POLICE. Un prix, une puissance, une date ne se lisent
 * pas, ils se comparent. Dans Inter, le 1 est plus étroit que le 8 : deux
 * montants l'un sous l'autre ne s'alignent pas, et l'œil doit relire chiffre
 * par chiffre au lieu de voir lequel est le plus grand. C'est tout l'écran des
 * tarifs, tous les KPI, toutes les estimations.
 *
 * `tabular-nums` seul n'y suffisait pas : il aligne les colonnes d'une même
 * police, mais ne donne pas ce que donne une mono — un chiffre qui se
 * distingue du texte qui l'entoure, sans gras ni couleur.
 *
 * TROIS GRAISSES, PAS SEPT, ET LE SOUS-ENSEMBLE LATIN SEULEMENT : 45 ko au
 * total, embarqués comme les deux autres. Aucun CDN à l'exécution (§9.2).
 * Licence OFL. */
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/assets/fonts/ibm-plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("/assets/fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("/assets/fonts/ibm-plex-mono-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  /* --- Charte Sooluo — codes exacts, ne pas approximer -------------------

     LA CHARTE VERTE A ÉTÉ ESSAYÉE PUIS ÉCARTÉE (D-189). Le vert profond du
     handoff de Kelvin a été posé en aperçu basculable (D-184), regardé sur
     téléphone, jugé, et refusé. Le bleu reste la charte. L'aperçu existait
     pour que la question se tranche en voyant plutôt qu'en imaginant : il a
     rempli son office.

     UNE SEULE COULEUR D'ACTION, ET ELLE NE SERT QU'À ÇA.
     Le bleu porte le bouton, le lien, l'onglet retenu, le champ au focus.
     Rien d'autre n'est bleu — ni un titre, ni un filet, ni une icône
     décorative. C'est ce qui fait qu'on sait, sans réfléchir, où appuyer.

     Le vert ne décore plus : il ne dit que le POSITIF CHIFFRÉ — une aide
     obtenue, une économie, une classe énergétique gagnée. L'ambre dit la
     réserve, le rouge l'erreur. Trois signaux, jamais mélangés à l'action.

     Les trois couleurs de zone du logo restent réservées au logo. */
  --encre: #0b1422;
  --fond-clair: #f1f2f4;
  --action: #1b4de4;
  --action-fonce: #1339b0;
  --action-pale: #eaefff;
  --vert: #0f9d63;
  --vert-fonce: #0b7d4e;
  --vert-pale: #e4f5ed;
  --sable: #9c5d05;
  --sable-pale: #fff0da;
  --gris-texte: #55606a;
  --gris-tertiaire: #626d77;
  --filet: #e1e4e7;
  --filet-fort: #cbd2d9;

  --zone-bleu: #2b8ca6;
  --zone-orange: #d98a2b;
  --zone-violet: #6a5aa8;

  /* --- Rôles applicatifs -------------------------------------------------
     Les noms utilisés dans les composants pointent vers la charte. */
  --brand-50: var(--action-pale);
  --brand-100: #d7e0ff;
  --brand-200: #aec1ff;
  --brand-500: var(--action);
  --brand-600: var(--action);
  --brand-700: var(--action-fonce);
  --brand-900: var(--encre);

  --surface: #ffffff;
  --surface-voile: #f9fafb;
  --surface-muted: var(--fond-clair);
  --border: var(--filet);
  --ink: var(--encre);
  --ink-muted: var(--gris-texte);
  --ink-faible: var(--gris-tertiaire);

  --success: var(--vert-fonce);
  --success-pale: var(--vert-pale);
  --warning: var(--sable);
  --sable-fonce: #8a5510;
  --danger: #b42318;
  --danger-pale: #fee4e2;

  /* Deux teintes de plus, et elles ne servent qu'à CLASSER, jamais à agir :
     le bleu-vert d'une information neutre, le violet d'une provenance. Les
     confondre avec le bleu d'action ferait croire qu'on peut appuyer dessus. */
  --info: #1d6e85;
  --info-pale: #e2f1f6;
  /* Le prune de classement. Les teintes codées en dur qui l'accompagnaient
     ont été remplacées par un jeton pâle (D-187) : c'est le seul reste utile
     de l'essai de charte verte. */
  --violet: #4d3f85;
  --violet-pale: #eeebf5;

  /* --- Familles réglementaires ------------------------------------------
     Ces couleurs ne sont pas les nôtres : elles viennent de barèmes publics
     et l'installateur comme le client les reconnaissent. On ne les harmonise
     pas avec la charte, on les cite.

     Les quatre profils MaPrimeRénov' portent les couleurs de l'Anah. */
  --profil-bleu: #2b6cb0;
  --profil-jaune: #d9a62b;
  --profil-violet: #6a5aa8;
  --profil-rose: #c25a8a;

  /* L'échelle du diagnostic de performance énergétique, du vert au rouge. */
  --dpe-a: #2f9e44;
  --dpe-b: #66a80f;
  --dpe-c: #c9a800;
  --dpe-d: #f08c00;
  --dpe-e: #e8590c;
  --dpe-f: #d9480f;
  --dpe-g: #c92a2a;

  /* --- Séries d'un graphique --------------------------------------------
     Elles CLASSENT, elles n'agissent pas : le vert d'action en est absent à
     dessein, sinon une barre se prend pour un bouton. Quatre suffisent — au
     delà, on regroupe en « autres » plutôt que d'inventer une cinquième
     teinte que personne ne distinguera.

     */
  --serie-1: var(--info);
  --serie-2: var(--sable);
  --serie-3: var(--violet);
  --serie-4: var(--gris-tertiaire);

  /* --- Statuts d'un dossier ---------------------------------------------
     Cinq états, cinq teintes, et le suivi les porte en filet haut de colonne.
     Le validé est le seul vert : c'est un aboutissement, pas une action. */
  --statut-brouillon: var(--gris-tertiaire);
  --statut-pret: var(--info);
  --statut-envoye: var(--sable);
  --statut-valide: var(--vert-fonce);
  --statut-annule: var(--danger);

  /* --- Typographie -------------------------------------------------------
     Space Grotesk pour les titres et les chiffres, Inter pour les textes.
     Corps à 17 px : au-dessus du défaut de 16 px, pour rester lisible sur
     tablette tenue à bout de bras dans un local mal éclairé.

     SEPT TAILLES, PAS UNE DE PLUS. Le fichier en comptait vingt, posées au
     coup par coup : c'est ce qui fait qu'aucun écran ne ressemble au suivant
     et que rien ne saute aux yeux, parce que tout est presque de la même
     taille. Une taille absente de cette échelle est une taille interdite. */
  --font-titre: "Space Grotesk", "Inter var", system-ui, sans-serif;
  --font-sans: "Inter var", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* La pile de secours reste une mono du système : si le woff2 tarde ou
     échoue, les chiffres restent alignés, ce qui est le seul point qui
     compte. */
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono",
    "Segoe UI Mono", Consolas, monospace;

  --t-xs: 0.75rem;      /* 12 px — étiquettes en capitales, mentions légales */
  --t-sm: 0.875rem;     /* 14 px — annotations, motifs d'écartement */
  --t-md: 0.9375rem;    /* 15 px — tableaux, listes denses */
  --t-body: 1.0625rem;  /* 17 px — corps de texte, plancher terrain */
  --t-lg: 1.3125rem;    /* 21 px — sous-titres, montants de ligne */
  --t-xl: 1.75rem;      /* 28 px — titre d'écran */
  --t-2xl: 2.5rem;      /* 40 px — le chiffre qui décide, un par écran */

  --text-body: var(--t-body);
  --line-body: 1.6;
  --serrage-titre: -0.02em;

  /* --- Espacements -------------------------------------------------------
     Une grille de 4 px. Un écart qui n'est pas sur cette échelle se voit. */
  --e-1: 0.25rem;
  --e-2: 0.5rem;
  --e-3: 0.75rem;
  --e-4: 1rem;
  --e-5: 1.5rem;
  --e-6: 2rem;
  --e-7: 3rem;

  /* --- Cibles tactiles ---------------------------------------------------
     48 px : minimum pour une utilisation debout, éventuellement avec des
     gants. Ne jamais descendre en dessous. */
  --touch: 3rem;
  --touch-lg: 3.5rem;

  /* --- Formes ------------------------------------------------------------
     La charte n'autorise que trois rayons.

     ILS SE RESSERRENT (D-178) : 8 / 12 / 18 px devient 4 / 6 / 8. Dix-huit
     pixels sur une carte, c'est la grammaire d'une application grand public ;
     l'outil qu'on ouvre devant un client pour lui montrer des chiffres gagne à
     ressembler à un instrument. Le même raisonnement que le monogramme de
     D-130, appliqué cette fois aux angles.

     Les pastilles de statut gardent leur forme de gélule — elles ne passent
     pas par ces jetons, et une gélule n'est pas un angle arrondi : c'est une
     forme qui dit « étiquette ». */
  --radius-sm: 4px;
  --radius: 6px;
  --radius-lg: 8px;
  --gutter: 1rem;

  /* DEUX OMBRES, ET LA PLUPART DES CARTES N'EN PORTENT AUCUNE.
     Le fichier en comptait treize. Quand tout est soulevé, plus rien ne l'est :
     une carte posée se distingue par son FILET, pas par son ombre. L'ombre est
     réservée à ce qui flotte réellement au-dessus du reste — le panneau
     latéral, la barre d'action collée en bas, un menu ouvert. */
  --ombre-douce: 0 1px 2px rgb(11 20 34 / 0.04), 0 1px 3px rgb(11 20 34 / 0.06);
  --ombre-portee: 0 4px 6px rgb(11 20 34 / 0.04), 0 12px 28px rgb(11 20 34 / 0.10);
}

/* ---------------------------------------------------------------------------
   Réinitialisation minimale
--------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  /* Empêche iOS/Safari d'agrandir le texte en paysage. */
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--surface-muted);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--line-body);
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

img,
svg {
  display: block;
  max-width: 100%;
}

/* Titres en Space Grotesk, interlettrage serré — la signature de la charte.
   Graisse 600 et non 800 : au-delà, une grotesque technique devient bruyante,
   et c'est la HIÉRARCHIE DES TAILLES qui doit porter la lecture, pas l'épaisseur
   du trait. `text-wrap: balance` évite le mot orphelin en fin de titre. */
h1,
h2,
h3 {
  font-family: var(--font-titre);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
  line-height: 1.15;
  text-wrap: balance;
}

h1 { font-size: var(--t-xl); }
h2 { font-size: var(--t-lg); }
h3 { font-size: var(--t-body); }

/* Chiffres et prix en chasse fixe : dans un tableau de prix, des colonnes de
   chiffres qui ne s'alignent pas se lisent mal et se comparent moins vite. */
.tableau__nombre,
.champ-tableau,
.resultat__valeur,
.produit__puissance,
.marge,
.hypotheses__ligne dd {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Toute cible interactive respecte la hauteur minimale, sans exception. */
button,
[role="button"],
select,
input:not([type="checkbox"]):not([type="radio"]),
textarea {
  font: inherit;
  min-height: var(--touch);
}

/* Focus visible au clavier ET au toucher : le survol n'existe pas ici. */
:focus-visible {
  outline: 3px solid var(--brand-600);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   Mise en page
--------------------------------------------------------------------------- */

/* La colonne de travail.
   48rem convenait à la tablette portrait, cible principale, mais laissait deux
   tiers d'un écran de bureau vides — l'installateur qui prépare ses dossiers
   assis y perdait la moitié de ses cartes sous la ligne de flottaison. 62rem
   garde une longueur de ligne lisible pour le texte et donne aux grilles de
   quoi tenir trois colonnes. */
.screen {
  width: 100%;
  max-width: 62rem;
  margin-inline: auto;
  padding: 2rem var(--gutter);
}

/* Le surtitre et le titre d'écran sont définis UNE SEULE FOIS, plus bas.
   Ils l'étaient deux fois, ici et là, et seule la seconde s'appliquait : la
   première décrivait un écran que personne ne voyait. */
.screen__eyebrow {
  text-transform: uppercase;
}

/* UNE CARTE POSÉE SE DISTINGUE PAR SON FILET, PAS PAR SON OMBRE.
   Toutes les cartes portaient la même ombre : quand tout est soulevé, plus
   rien ne l'est, et l'écran prend cet aspect de maquette où chaque bloc
   flotte. L'ombre est rendue à ce qui flotte vraiment — panneaux, barres
   collantes, menus. */
.card {
  margin-top: 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
}

.muted {
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------------------
   Contrôles
--------------------------------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch);
  padding-inline: 1.5rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--brand-600);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.button:active {
  background: var(--brand-700);
}

.button--secondary {
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink);
}

/* ---------------------------------------------------------------------------
   Mode client — écran retourné vers le prospect

   Règle de sécurité du cahier des charges : le mode client ne doit jamais
   laisser fuir un prix d'achat ou une marge.

   ATTENTION : filet de sécurité VISUEL uniquement. La protection réelle est
   assurée par les politiques RLS Supabase (lot 7) — un technicien ne doit pas
   recevoir la donnée du tout, même masquée côté client.
--------------------------------------------------------------------------- */

.client-mode [data-sensitive] {
  display: none !important;
}

/* ---------------------------------------------------------------------------
   … ET CE N'ÉTAIT QUE ÇA.

   Le mode client se résumait à la règle ci-dessus : le même écran, en plus
   pauvre. Or ce n'est pas un écran qu'on ampute, c'est un MOMENT — le
   technicien retourne la tablette, le client se penche, et pendant trente
   secondes l'application n'est plus un outil de saisie mais un argument.

   Trois choses changent alors, et rien d'autre :

     1. LA MACHINERIE DISPARAÎT. Les boutons de suppression, les compteurs de
        quantité, le panneau « ajouter une prestation » : ce sont les
        commandes de l'opérateur. Un client qui voit une croix rouge à côté
        de sa pompe à chaleur se demande ce qu'on va lui retirer.
     2. TOUT GROSSIT. Le client lit de plus loin, souvent debout, souvent
        par-dessus une épaule. Le corps passe de 17 à 19 px et le chiffre qui
        décide gagne un cran.
     3. LE TECHNICIEN SAIT QU'IL Y EST. Un liseré bleu ferme le haut de
        l'écran en permanence. Oublier qu'on est en mode client est sans
        gravité ; croire qu'on y est alors qu'on n'y est pas ne l'est pas.
--------------------------------------------------------------------------- */

/* 1. La machinerie de l'opérateur. */
.client-mode .ligne__retrait,
.client-mode .nombre__bouton,
.client-mode .ajout,
.client-mode .piece-ligne__pas {
  display: none !important;
}

/* La quantité reste lue, elle n'est plus modifiable : le champ redevient du
   texte, sans bordure ni cible tactile qui invite à y toucher. */
.client-mode .ligne__quantite input,
.client-mode .ligne__unitaire input,
.client-mode .nombre input {
  border: 0;
  background: transparent;
  min-height: 0;
  pointer-events: none;
  text-align: right;
  font-weight: 600;
}

/* 2. Tout grossit d'un cran. Les jetons sont redéfinis sur la racine : chaque
   règle qui les emploie suit, sans en réécrire aucune. */
.client-mode {
  --t-md: 1rem;
  --t-body: 1.1875rem;
  --t-lg: 1.4375rem;
  --t-xl: 1.9375rem;
  --t-2xl: 2.875rem;
}

/* 3. Le liseré qui dit que la tablette est tournée. Il est posé sur la racine
   et non dans un écran : il survit à toute navigation, et il ne peut pas être
   recouvert par une carte. */
.client-mode::before {
  content: "";
  position: fixed;
  inset: 0 0 auto 0;
  height: 4px;
  background: var(--action);
  z-index: 1000;
  pointer-events: none;
}

/* L'apparence du bouton lui-même est définie une seule fois, plus bas, avec
   le reste de l'en-tête. Trois règles se la disputaient — une ambre, une
   bleue pleine, une bleue pâle — et seule la dernière du fichier s'appliquait
   réellement : les deux autres étaient mortes depuis des lots, et leurs
   commentaires décrivaient un écran que personne ne voyait. */

/* ---------------------------------------------------------------------------
   En-tête applicatif — lot 1
--------------------------------------------------------------------------- */

/* UNE SEULE DÉCLARATION. Ce bloc existait quatre fois — lot 1, en-tête sombre,
   densité, mode client — chacune corrigeant la précédente. Voici la valeur
   finale de la cascade, écrite une fois. */
.app-header {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .5rem var(--gutter);
  background: linear-gradient(180deg, var(--encre) 0%, var(--encre-clair) 140%);
  border-bottom: 0;
  box-shadow: 0 1px 0 rgb(255 255 255 / 0.06), 0 8px 24px rgb(11 20 34 / 0.16);
}

.app-header__brand {
  color: var(--ink);
  text-decoration: none;
  /* Cible tactile : le logo sert de retour au tableau de bord. */
  display: inline-flex;
  align-items: center;
  min-height: var(--touch);
  padding-inline: 0.25rem;
}

.app-header__identity {
  display: flex;
  flex-direction: column;
  margin-inline-start: auto;
  text-align: end;
  line-height: 1.25;
  min-width: 0;
}

.app-header__tenant {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-header__role {
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------------------
   Formulaires — lot 1
--------------------------------------------------------------------------- */

.screen--narrow {
  max-width: 30rem;
}

.form__legend {
  font-size: var(--t-lg);
  font-weight: 600;
  margin-bottom: 1.25rem;
}

.field {
  margin-bottom: 1.25rem;
}

.field__label {
  display: block;
  font-weight: 500;
  margin-bottom: 0.375rem;
}

.field__input {
  display: block;
  width: 100%;
  min-height: var(--touch);
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--surface);
  color: var(--ink);
}

.field__input:focus {
  border-color: var(--brand-600);
}

.field__hint {
  margin-top: 0.375rem;
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.form__error {
  margin-bottom: 1rem;
  padding: 0.75rem;
  border-radius: 0.5rem;
  background: var(--danger-pale);
  border: 1px solid var(--danger);
  color: var(--danger);
  font-weight: 500;
}

/* ---------------------------------------------------------------------------
   Variantes de bouton — lot 1
--------------------------------------------------------------------------- */

.button--block {
  display: flex;
  width: 100%;
}

.button--compact {
  padding-inline: 1rem;
}

.button--link {
  background: none;
  border: none;
  color: var(--brand-700);
  text-decoration: underline;
  padding-inline: 0.25rem;
}

.switch-mode {
  margin-top: 1rem;
  text-align: center;
}

/* ---------------------------------------------------------------------------
   Formulaires longs — lot 2
--------------------------------------------------------------------------- */

.form-section {
  margin-top: 1.5rem;
}

.form-section__title {
  font-size: var(--t-lg);
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.form-section__description {
  color: var(--ink-muted);
  font-size: var(--t-md);
  margin-bottom: 1.25rem;
}

.form-section__title + .field,
.form-section__description + .field {
  margin-top: 1.25rem;
}

.field__required {
  color: var(--danger);
}

.field__error {
  margin-top: 0.375rem;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--danger);
}

.field__input--error {
  border-color: var(--danger);
  border-width: 2px;
}

.field__input:disabled {
  background: var(--surface-muted);
  color: var(--ink-muted);
}

.field__textarea {
  min-height: 6rem;
  resize: vertical;
  line-height: var(--line-body);
}

/* Deux champs côte à côte — code postal et ville. Repasse en colonne sous
   480 px : à cette largeur, deux champs deviennent trop étroits pour une
   saisie au doigt. */
.field-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: 1rem;
}

@media (max-width: 480px) {
  .field-row {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

/* ---------------------------------------------------------------------------
   Interrupteur
--------------------------------------------------------------------------- */

.toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  min-height: var(--touch);
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-align: start;
  cursor: pointer;
}

.toggle__label {
  font-weight: 500;
}

.toggle__track {
  flex: 0 0 auto;
  width: 3rem;
  height: 1.75rem;
  padding: 0.1875rem;
  border-radius: 999px;
  background: var(--border);
  transition: background 120ms ease;
}

.toggle__thumb {
  display: block;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  background: var(--surface);
  transition: transform 120ms ease;
}

.toggle--on {
  border-color: var(--brand-600);
}

.toggle--on .toggle__track {
  background: var(--brand-600);
}

.toggle--on .toggle__thumb {
  transform: translateX(1.25rem);
}

/* ---------------------------------------------------------------------------
   Messages
--------------------------------------------------------------------------- */

.notice {
  margin-top: 1.5rem;
  padding: 0.875rem 1rem;
  border-radius: var(--radius);
  border: 1px solid;
  font-weight: 500;
}

.notice--info {
  background: var(--brand-50);
  border-color: var(--brand-200);
  color: var(--brand-900);
}

.notice--warning {
  background: #fffbeb;
  border-color: var(--sable);
  color: var(--sable-fonce);
}

.notice--danger {
  background: var(--danger-pale);
  border-color: var(--danger);
  color: var(--danger);
}

.notice--success {
  background: var(--vert-pale);
  border-color: var(--success);
  color: var(--success);
}

/* ---------------------------------------------------------------------------
   Aperçu du logo
--------------------------------------------------------------------------- */

.logo-preview {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.logo-preview__image {
  max-width: 12rem;
  max-height: 6rem;
  object-fit: contain;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--surface);
}

/* ---------------------------------------------------------------------------
   Clients et projets — lot 3
--------------------------------------------------------------------------- */

/* Deux champs de largeur égale — nom et prénom. */
.field-row--egal {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.recap {
  margin-top: 1.5rem;
}

.recap__nom {
  font-weight: 600;
  font-size: var(--t-lg);
}

.choix-consigne {
  margin-top: 2rem;
  margin-bottom: 0.75rem;
  font-weight: 500;
}

/* --- Les trois métiers, écran 4 ------------------------------------------

   Côte à côte plutôt qu'empilées : trois cartes pleine largeur occupaient
   l'écran entier d'une tablette et il fallait défiler pour voir la troisième.
   Le dessin porte le choix avant la lecture — sur un chantier, le technicien
   sait déjà lequel il vient étudier, il n'a pas à lire trois descriptions.

   `auto-fit` les remet en colonne sous 42rem, sans point de rupture écrit à
   la main : en mobile portrait, trois cartes côte à côte n'auraient plus de
   place pour leur libellé. */
.projets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .75rem;
}

.projet-carte {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  min-height: var(--touch-lg);
  padding: 1.25rem 1rem;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-align: center;
  cursor: pointer;
  font: inherit;
}

.projet-carte:active,
.projet-carte--actif {
  border-color: var(--brand-600);
  background: var(--brand-50);
}

.projet-carte:disabled { opacity: .6; }

.projet-carte__dessin {
  display: block;
  width: 100%;
  max-width: 8rem;
  color: var(--brand-600);
}

.projet-carte__dessin svg { display: block; width: 100%; height: auto; }

.projet-carte__libelle {
  font-size: var(--t-body);
  font-weight: 600;
  line-height: 1.25;
}

.projet-carte__description {
  color: var(--ink-muted);
  font-size: var(--t-md);
  line-height: 1.35;
}

/* Cartes de choix — écran 4.
   Pleine largeur et hauteur généreuse : ce choix se fait debout, tablette en
   main, devant le client. */
.choix {
  display: grid;
  gap: 0.75rem;
}

.choix__carte {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  width: 100%;
  min-height: var(--touch-lg);
  padding: 1.25rem;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-align: start;
  cursor: pointer;
}

.choix__carte:active {
  border-color: var(--brand-600);
  background: var(--brand-50);
}

.choix__carte--actif {
  border-color: var(--brand-600);
  background: var(--brand-50);
}

.choix__carte:disabled {
  opacity: 0.6;
}

.choix__libelle {
  font-size: var(--t-body);
  font-weight: 600;
}

/* ---------------------------------------------------------------------------
   Relevé de visite — lot 4
--------------------------------------------------------------------------- */

/* Cases à cocher pleine largeur. Viser une case de 20 px avec une tablette
   tenue debout est une source d'erreur : toute la ligne est cliquable. */
.cases {
  border: 0;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.5rem;
}

.cases > .field__label {
  margin-bottom: 0.25rem;
}

.case {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: var(--touch);
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  text-align: start;
  cursor: pointer;
}

.case__marque {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 2px solid var(--border);
  border-radius: 0.375rem;
  font-weight: 700;
  line-height: 1;
}

.case--cochee {
  border-color: var(--brand-600);
  background: var(--brand-50);
}

.case--cochee .case__marque {
  border-color: var(--brand-600);
  background: var(--brand-600);
  color: #fff;
}

/* --- Photos --------------------------------------------------------------- */

.photo-bloc {
  margin-bottom: 1.5rem;
}

.photo-galerie {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-block: 0.5rem;
}

.photo-vignette {
  position: relative;
  width: 6rem;
  height: 6rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--surface-muted);
  overflow: hidden;
}

.photo-vignette__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Bouton de retrait : 2 rem suffisent ici, la cible reste isolée au coin d'une
   vignette et l'action est réversible (la photo peut être reprise). */
.photo-vignette__retirer {
  position: absolute;
  top: 0.25rem;
  inset-inline-end: 0.25rem;
  width: 2rem;
  height: 2rem;
  min-height: 2rem;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgb(11 20 34 / 0.75);
  color: #fff;
  font-size: var(--t-lg);
  line-height: 1;
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   Note de dimensionnement — lot 5
--------------------------------------------------------------------------- */

/* Le résultat est ce que le technicien montre au client : il domine l'écran. */
.resultat {
  margin-top: 1.5rem;
  padding: 1.5rem;
  border-radius: var(--radius);
  background: var(--brand-600);
  color: #fff;
  text-align: center;
}

.resultat__intitule {
  font-size: var(--t-md);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.85;
}

.resultat__valeur {
  font-size: var(--t-2xl);
  font-weight: 700;
  line-height: 1.1;
  margin-top: 0.25rem;
}

.resultat__unite {
  font-size: var(--t-xl);
  font-weight: 500;
  margin-inline-start: 0.25rem;
}

/* Mention de méthode : elle doit rester lisible, pas se cacher en petits
   caractères. Une estimation présentée comme un calcul réglementaire
   exposerait l'installateur. */
.resultat__mention {
  margin-top: 0.75rem;
  font-size: var(--t-sm);
  opacity: 0.9;
}

/* --- Contrôles ------------------------------------------------------------ */

.controles {
  list-style: none;
  padding: 0;
  margin-top: 1rem;
  display: grid;
  gap: 0.75rem;
}

.controle {
  padding: 0.875rem 1rem;
  border-inline-start: 4px solid var(--border);
  border-radius: 0.375rem;
  background: var(--surface-muted);
}

.controle--alerte {
  border-inline-start-color: var(--danger);
  background: var(--danger-pale);
}

.controle--attention {
  border-inline-start-color: var(--warning);
  background: #fffbeb;
}

.controle--info {
  border-inline-start-color: var(--brand-500);
  background: var(--brand-50);
}

.controle__titre {
  font-weight: 600;
}

.controle__detail {
  margin-top: 0.125rem;
  font-size: var(--t-md);
  color: var(--ink-muted);
}

/* --- Hypothèses ----------------------------------------------------------- */

.hypotheses {
  margin-top: 1rem;
}

.hypotheses__ligne {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: 1rem;
  padding-block: 0.625rem;
  border-top: 1px solid var(--border);
}

.hypotheses__ligne:first-child {
  border-top: 0;
}

.hypotheses__ligne dt {
  color: var(--ink-muted);
}

.hypotheses__ligne dd {
  margin: 0;
  font-weight: 500;
}

@media (max-width: 480px) {
  .hypotheses__ligne {
    grid-template-columns: 1fr;
    gap: 0.125rem;
  }
}

/* --- Barre d'actions ------------------------------------------------------ */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

.actions > * {
  flex: 1 1 12rem;
}

/* ---------------------------------------------------------------------------
   Choix du matériel — lot 6
--------------------------------------------------------------------------- */

.section-espacee {
  margin-top: 2rem;
}

.criteres {
  margin-top: 1.5rem;
}

.criteres__titre {
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.criteres__liste {
  margin: 0;
  padding-inline-start: 1.25rem;
  color: var(--ink-muted);
}

.criteres__liste li {
  padding-block: 0.125rem;
}

/* --- Fiches produit ------------------------------------------------------- */

.produits {
  display: grid;
  gap: 1rem;
  margin-top: 1rem;
}

.produit {
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.produit--choisi {
  border-color: var(--brand-600);
  border-width: 2px;
  background: var(--brand-50);
}

.produit__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.produit__marque {
  font-size: var(--t-lg);
  font-weight: 700;
}

.produit__reference {
  color: var(--ink-muted);
  font-size: var(--t-md);
}

.produit__puissance {
  flex: 0 0 auto;
  font-size: var(--t-lg);
  font-weight: 700;
  color: var(--brand-700);
}

/* Deux colonnes de caractéristiques : lisible sur tablette, une seule sur
   téléphone où la largeur ne permet pas de couple libellé/valeur. */
.produit__caracteristiques {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem 1rem;
  margin-block: 1rem;
}

.produit__caracteristiques dt {
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.produit__caracteristiques dd {
  margin: 0;
  font-weight: 500;
}

@media (max-width: 480px) {
  .produit__caracteristiques {
    grid-template-columns: 1fr;
  }
}

/* --- Produits écartés ----------------------------------------------------- */

.ecartes {
  list-style: none;
  padding: 0;
  margin-top: 1rem;
  display: grid;
  gap: 0.75rem;
}

.ecarte {
  padding: 0.75rem 0;
  border-top: 1px solid var(--border);
}

.ecarte__nom {
  font-weight: 500;
}

.ecarte__motifs {
  margin: 0.25rem 0 0;
  padding-inline-start: 1.25rem;
  font-size: var(--t-md);
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------------------
   Grille tarifaire — lot 7
--------------------------------------------------------------------------- */

.screen--large {
  max-width: 64rem;
}

/* --- Tableaux ------------------------------------------------------------- */

/* Le tableau défile horizontalement plutôt que de comprimer ses colonnes :
   sur tablette, des prix tronqués sont pires qu'un défilement. */
.tableau-enveloppe {
  overflow-x: auto;
  margin-top: 1rem;
}

.tableau {
  width: 100%;
  border-collapse: collapse;
  white-space: nowrap;
}

.tableau th,
.tableau td {
  padding: 0.625rem 0.75rem;
  text-align: start;
  border-bottom: 1px solid var(--border);
}

.tableau th {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.tableau__nombre {
  text-align: end;
}

.tableau__actions {
  text-align: end;
}

.tableau tbody tr:last-child td {
  border-bottom: 0;
}

.champ-tableau {
  width: 7rem;
  min-height: 2.5rem;
  padding: 0.375rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 0.375rem;
  background: var(--surface);
  color: var(--ink);
  text-align: end;
  font: inherit;
}

.marge {
  font-weight: 600;
  color: var(--success);
}

.marge--negative {
  color: var(--danger);
}

.marge__pourcentage {
  font-weight: 500;
  font-size: var(--t-sm);
  opacity: 0.8;
}

/* ---------------------------------------------------------------------------
   Logo — charte Sooluo
--------------------------------------------------------------------------- */

.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.logo__marque {
  width: var(--logo-taille, 28px);
  height: var(--logo-taille, 28px);
  flex: 0 0 auto;
}

.logo__mot {
  font-family: var(--font-titre);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
  font-size: calc(var(--logo-taille, 28px) * 0.82);
  color: var(--encre);
  line-height: 1;
}

.logo__mot--sombre {
  color: #ffffff;
}

/* L'écran de connexion porte le logo complet plutôt qu'un titre texte : c'est
   la première chose vue à l'ouverture de l'application. */
.connexion__logo {
  margin-bottom: 0.25rem;
}

/* ===========================================================================
   REFONTE VISUELLE — en-tête sombre, échelle typographique, parcours
   =========================================================================== */

:root {
  --encre-clair: #1c2637;
  --encre-voile: rgb(255 255 255 / 0.72);
  --hauteur-nav: 5rem;
}

/* ---------------------------------------------------------------------------
   Fond de page — un voile très léger plutôt qu'un aplat mort
--------------------------------------------------------------------------- */

body {
  /* UN FOND PLAT. Les deux voiles radiaux verts et bleus donnaient à chaque
     écran un dégradé de fond de présentation, et surtout ils teintaient les
     cartes blanches par contraste. Un gris froid uni laisse les cartes être
     blanches et les chiffres être lisibles — c'est ce que font les deux
     applications prises pour référence. */
  background: var(--fond-clair);
  background-attachment: fixed;
  /* La barre de navigation basse est fixe : sans cette réserve, elle
     recouvrirait la fin du contenu. */
  padding-bottom: var(--hauteur-nav);
}

/* ---------------------------------------------------------------------------
   Échelle typographique — du contraste, pas une suite de tailles voisines
--------------------------------------------------------------------------- */

.screen {
  max-width: 52rem;
  padding-block: 2.5rem 1.5rem;
}

.screen__eyebrow {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--action-fonce);
}

/* UN TITRE D'ÉCRAN NE MONTE PAS À LA TAILLE DU CHIFFRE DÉCISIF.
   Il plafonnait à `--t-2xl`, soit 40 px : exactement la taille réservée au
   seul nombre qui décide (D-094). Sur une tablette, « Comparateur de gammes »
   occupait alors 800 px de large et écrasait tout ce qui suivait. Le titre
   plafonne à `--t-xl` ; il reste le plus gros texte de l'écran, sans disputer
   sa place au montant. */
.screen__title {
  margin-top: 0.375rem;
  font-size: clamp(var(--t-lg), 4.5vw, var(--t-xl));
  line-height: 1.1;
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
}

.screen__title + .muted {
  margin-top: 0.5rem;
  font-size: var(--t-body);
}

.form-section__title {
  font-size: var(--t-lg);
  letter-spacing: -0.02em;
}

/* ---------------------------------------------------------------------------
   En-tête sombre
--------------------------------------------------------------------------- */

.app-header__brand .logo__mot {
  color: #ffffff;
}

.app-header__tenant {
  color: #ffffff;
}

.app-header__role {
  color: var(--encre-voile);
}

/* Sur fond sombre, un bouton blanc bordé de gris disparaît : on inverse. */
.app-header .button--secondary {
  background: rgb(255 255 255 / 0.08);
  border-color: rgb(255 255 255 / 0.18);
  color: #ffffff;
}

.app-header .button--secondary:active {
  background: rgb(255 255 255 / 0.16);
}

/* ---------------------------------------------------------------------------
   Barre de parcours
--------------------------------------------------------------------------- */

.parcours {
  position: sticky;
  top: 4.5rem;
  z-index: 10;
  padding: 0.875rem var(--gutter) 0.75rem;
  background: rgb(241 242 244 / 0.9);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--filet);
}

.parcours__rail {
  height: 3px;
  border-radius: 999px;
  background: var(--filet);
  overflow: hidden;
}

.parcours__jauge {
  height: 100%;
  border-radius: 999px;
  background: var(--action);
  transition: width 320ms cubic-bezier(0.22, 1, 0.36, 1);
}

.parcours__etapes {
  display: flex;
  gap: 0.25rem;
  list-style: none;
  margin: 0.625rem 0 0;
  padding: 0;
  /* Neuf étapes ne tiennent pas sur un téléphone : on défile plutôt que de
     comprimer les libellés jusqu'à l'illisible. */
  overflow-x: auto;
  scrollbar-width: none;
}

.parcours__etapes::-webkit-scrollbar {
  display: none;
}

.parcours__etape {
  flex: 0 0 auto;
}

.parcours__lien {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.5rem;
  /* La zone de toucher déborde du dessin : les neuf pastilles doivent tenir
     sur 375 px, on ne peut pas les grossir, mais rien n'empêche la cible
     d'être plus large que le trait. */
  position: relative;
  border-radius: var(--radius-sm);
  color: var(--gris-texte);
  text-decoration: none;
  font-size: var(--t-sm);
  font-weight: 500;
  white-space: nowrap;
}

.parcours__pastille {
  display: grid;
  place-items: center;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  background: var(--filet);
  color: var(--gris-texte);
  font-size: var(--t-xs);
  font-weight: 700;
  font-family: var(--font-titre);
}

.parcours__etape--franchie .parcours__pastille {
  background: var(--action-pale);
  color: var(--action-fonce);
}

.parcours__etape--franchie .parcours__lien {
  color: var(--action-fonce);
}

.parcours__etape--active .parcours__pastille {
  background: var(--action);
  color: #ffffff;
  box-shadow: 0 0 0 4px rgb(27 77 228 / 0.15);
}

.parcours__etape--active .parcours__lien {
  color: var(--encre);
  font-weight: 700;
}

.parcours__etape--avenir .parcours__lien {
  opacity: 0.55;
}

/* ---------------------------------------------------------------------------
   Navigation précédent / suivant — fixée en bas

   Sur une tablette tenue à deux mains, le pouce atteint le bas de l'écran.
   C'est là que doivent se trouver les actions répétées.
--------------------------------------------------------------------------- */

.nav-etapes {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 30;
  padding: 0.75rem var(--gutter);
  padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
  background: rgb(255 255 255 / 0.86);
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--filet);
  box-shadow: 0 -4px 24px rgb(11 20 34 / 0.06);
}

.nav-etapes__contenu {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  max-width: 52rem;
  margin-inline: auto;
}

.nav-etapes__retour {
  gap: 0.375rem;
}

.nav-etapes__suite {
  gap: 0.5rem;
  min-width: 12rem;
  box-shadow: 0 2px 8px rgb(27 77 228 / 0.28);
}

@media (max-width: 520px) {
  .nav-etapes__suite {
    min-width: 0;
    flex: 1 1 auto;
  }
}

/* ---------------------------------------------------------------------------
   Surfaces — un filet net, pas de profondeur empruntée

   Cette règle disait l'inverse : « moins de trait, plus de profondeur ». Elle
   voilait le filet à 70 % et rajoutait une ombre de 20 px sous chaque carte,
   par-dessus celle de la règle de base. Deux ombres cumulées sur tous les
   blocs de tous les écrans : c'est ce qui donnait ce rendu de maquette où
   chaque bloc flotte au-dessus d'un fond dégradé.
--------------------------------------------------------------------------- */

.card {
  border-color: var(--filet);
}

.button {
  transition: transform 120ms ease, background 120ms ease;
}

.button:active {
  transform: translateY(1px);
}

/* Respect du réglage système : une animation peut gêner, voire rendre malade. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------------------
   Logos de marques sur les fiches produit
--------------------------------------------------------------------------- */

.produit__identite {
  min-width: 0;
}

/* Hauteur fixe, largeur libre : les logos de marques ont des proportions très
   différentes — Daikin est large et bas, Toshiba plus compact. Contraindre la
   largeur les déformerait ou les rendrait minuscules. */
.produit__logo {
  height: 1.5rem;
  width: auto;
  max-width: 9rem;
  object-fit: contain;
  object-position: left center;
  margin-bottom: 0.25rem;
}

@media (max-width: 480px) {
  .produit__logo {
    height: 1.25rem;
    max-width: 7rem;
  }
}

/* ---------------------------------------------------------------------------
   En-tête — replis aux largeurs intermédiaires

   Cinq éléments sur une ligne débordent bien avant le format téléphone : sur
   une tablette en portrait, les libellés se chevauchaient. On retire par ordre
   d'importance décroissante, plutôt que de tout comprimer.
--------------------------------------------------------------------------- */

.app-header__identity {
  /* Sans cette limite, un nom d'entreprise long pousse les boutons hors écran. */
  max-width: 14rem;
}

/* Le lien vers la grille tarifaire est le premier à disparaître : il reste
   atteignable par son adresse, et n'est pas utilisé en visite. */

/* Puis l'identité de l'entreprise : le technicien sait pour qui il travaille.
   Le rôle, lui, change ce qu'il voit — on le garde. */
@media (max-width: 720px) {
  .app-header__identity {
    max-width: 8rem;
  }

  .app-header__tenant {
    display: none;
  }
}

/* Enfin les libellés des boutons, réduits à l'essentiel. */
@media (max-width: 560px) {
  .app-header {
    padding-inline: 0.75rem;
  }

  .app-header .button--compact {
    padding-inline: 0.75rem;
  }

  .logo__mot {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   Aides et reste à charge — lot 8
--------------------------------------------------------------------------- */

.profil {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: 1.5rem;
  padding: 0.875rem 1.125rem;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--filet);
  font-size: var(--t-body);
}

/* Les quatre couleurs officielles des catégories de revenus. Elles ne viennent
   pas de la charte Sooluo : ce sont celles du dispositif, et le client les
   reconnaît. */
.profil__pastille {
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  flex: 0 0 auto;
}

.profil--bleu .profil__pastille { background: var(--profil-bleu); }
.profil--jaune .profil__pastille { background: var(--profil-jaune); }
.profil--violet .profil__pastille { background: var(--profil-violet); }
.profil--rose .profil__pastille { background: var(--profil-rose); }

/* --- Bilan ---------------------------------------------------------------- */

.bilan {
  margin-top: 1rem;
  padding: 1.25rem 1.5rem;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--filet);
}

.bilan__ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.625rem;
  border-top: 1px solid var(--filet);
  font-variant-numeric: tabular-nums;
}

.bilan__ligne:first-child {
  border-top: 0;
}

.bilan__ligne--aide strong {
  color: var(--action-fonce);
}

/* LE CHIFFRE QUI DÉCIDE, ET UN SEUL PAR ÉCRAN.
   Le reste à charge est ce que le client retient de tout le rendez-vous. Il
   était à peine plus gros que la ligne de TVA au-dessus : à 21 px contre 17,
   l'œil ne le trouvait pas, et le technicien devait le montrer du doigt. Il
   passe à 40 px, en Space Grotesk, sur un fond qui le détache du reste du
   bilan. C'est la seule ligne de l'écran traitée ainsi. */
.bilan__ligne--total {
  align-items: center;
  margin-top: var(--e-3);
  padding: var(--e-4) var(--e-4);
  border-top: 0;
  border-radius: var(--radius);
  background: var(--fond-clair);
}

.bilan__ligne--total > :first-child {
  font-weight: 500;
  color: var(--ink-muted);
}

.bilan__ligne--total strong,
.bilan__ligne--total > :last-child {
  font-family: var(--font-titre);
  font-size: var(--t-2xl);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: var(--serrage-titre);
  color: var(--encre);
}

/* Une colonne de chiffres se lit de haut en bas : elle doit être alignée à
   droite et en chasse tabulaire, sinon on compare des virgules qui se
   promènent. */
.bilan__ligne > :last-child {
  margin-left: auto;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Une aide est un montant qui va dans le bon sens : c'est le seul endroit du
   bilan où le vert apparaît, et il ne veut dire que ça. */
.bilan__ligne--aide > :last-child,
.bilan__ligne--aide strong {
  color: var(--vert-fonce);
}

.bilan__ligne--total strong {
  font-family: var(--font-titre);
  font-weight: 600;
  font-size: var(--t-xl);
  letter-spacing: -0.02em;
}

/* Mention obligatoire du §5.4 : jamais en petits caractères illisibles. */
.mention-aides {
  margin-top: 0.75rem;
  font-size: var(--t-md);
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------------------
   Simulation d'économies — lot 9
--------------------------------------------------------------------------- */

/* Le chiffre que le client retient de l'écran 9. Même traitement que la note de
   dimensionnement : il domine, et il est lisible à un mètre — la tablette est
   souvent retournée vers le prospect à ce moment-là. */
.economie {
  margin-top: 1.5rem;
  padding: 1.5rem;
  border-radius: var(--radius-lg);
  background: var(--action-fonce);
  color: #fff;
  text-align: center;
  box-shadow: var(--ombre-portee);
}

/* Une PAC n'est pas toujours gagnante — remplacer du bois bûche, par exemple.
   Le cas défavorable change de couleur au lieu de disparaître : le taire
   serait malhonnête, et l'installateur doit le voir avant le client. */
.economie--defavorable {
  background: var(--danger);
}

.economie__intitule {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.85;
}

.economie__valeur {
  margin-top: 0.25rem;
  font-family: var(--font-titre);
  font-weight: 600;
  font-size: var(--t-2xl);
  line-height: 1.1;
  letter-spacing: var(--serrage-titre);
  font-variant-numeric: tabular-nums;
}

.economie__unite {
  margin-inline-start: 0.375rem;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--t-lg);
  letter-spacing: normal;
}

.economie__secondaire {
  margin-top: 0.5rem;
  opacity: 0.9;
}

@media (max-width: 480px) {
  .economie__valeur {
    font-size: var(--t-2xl);
  }
}

/* Retour sur investissement : phrase pleine plutôt que chiffre isolé. « 7,3 »
   ne veut rien dire hors contexte ; « amorti en 7,3 ans » se lit à voix haute
   devant le client. */
.retour {
  font-size: var(--t-body);
  line-height: 1.5;
}

.retour strong {
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   Détail de l'estimation — lot 10
--------------------------------------------------------------------------- */

/* Lien en ligne dans un encart : il doit rester repérable sans survol, la
   tablette n'en a pas. */
.lien {
  color: var(--action-fonce);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Étiquette réservée aux lecteurs d'écran. Chaque champ de quantité doit en
   porter une : « 1 » sans libellé ne veut rien dire à la lecture vocale. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.lignes {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
}

.lignes__groupe + .lignes__groupe {
  margin-top: 1.25rem;
}

.lignes__categorie {
  font-size: var(--t-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
  padding-bottom: 0.375rem;
  border-bottom: 1px solid var(--filet);
}

.ligne {
  display: grid;
  grid-template-columns: 1fr 5.5rem auto var(--touch);
  align-items: center;
  gap: 0.75rem;
  padding-block: 0.75rem;
  border-bottom: 1px solid var(--filet);
}

.ligne__intitule {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

.ligne__libelle {
  font-weight: 500;
}

.ligne__unitaire {
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.ligne__input {
  text-align: center;
  padding-inline: 0.5rem;
}

.ligne__montant {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Le retrait est une action destructive : cible pleine, mais discrète tant
   qu'on ne la vise pas. La ligne « matériel » est désactivée — le matériel se
   change à l'écran 7, pas en supprimant sa ligne de prix. */
.ligne__retrait {
  inline-size: var(--touch);
  block-size: var(--touch);
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--danger);
  font-size: var(--t-lg);
  line-height: 1;
  cursor: pointer;
}

.ligne__retrait:disabled {
  color: var(--filet);
  cursor: not-allowed;
}

.ajout-ligne .field {
  flex: 1;
  min-width: 0;
  margin: 0;
}

.ajout-ligne .button {
  flex-shrink: 0;
}

/* En mobile, quatre colonnes ne tiennent pas : l'intitulé prend toute la
   largeur et la quantité passe dessous avec le montant. */
@media (max-width: 560px) {
  .ligne {
    grid-template-columns: 1fr auto var(--touch);
    grid-template-areas:
      "intitule intitule retrait"
      "quantite montant  retrait";
    row-gap: 0.5rem;
  }

  .ligne__intitule { grid-area: intitule; }
  .ligne__quantite { grid-area: quantite; }
  .ligne__montant { grid-area: montant; text-align: end; }
  .ligne__retrait { grid-area: retrait; align-self: center; }
}

/* ---------------------------------------------------------------------------
   Composition du dossier — lot 10
--------------------------------------------------------------------------- */

.pieces {
  list-style: none;
  padding: 0;
  margin: 1rem 0 1.25rem;
  display: grid;
  gap: 0.75rem;
}

.piece {
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

/* La case est le bouton tout entier : sur tablette, viser un carré de 20 px
   avec un gant de chantier ne marche pas. */
.piece__case {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
  inline-size: 100%;
  min-block-size: var(--touch-lg);
  padding: 0.875rem 1rem;
  border: 0;
  background: transparent;
  text-align: start;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.piece__case:disabled {
  cursor: not-allowed;
}

.piece__coche {
  flex-shrink: 0;
  inline-size: 1.625rem;
  block-size: 1.625rem;
  margin-block-start: 0.125rem;
  border: 2px solid var(--filet);
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  font-weight: 700;
  line-height: 1;
  color: #fff;
}

.piece__coche--on {
  background: var(--action);
  border-color: var(--action);
}

.piece__texte {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

.piece__libelle {
  font-weight: 600;
}

.piece__description {
  font-size: var(--t-md);
  color: var(--ink-muted);
}

.piece--indisponible {
  background: var(--surface-muted);
}

.piece--indisponible .piece__libelle {
  color: var(--ink-muted);
}

.piece__fichier {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.625rem 1rem;
  border-top: 1px solid var(--filet);
  background: var(--action-pale);
}

.piece__meta {
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.button--petit {
  min-block-size: var(--touch);
  padding-inline: 1rem;
  font-size: var(--t-md);
}

/* Largeur de champ constante d'une ligne à l'autre : des cases de tailles
   différentes dans une même colonne donnent un document bâclé. */
@media (max-width: 560px) {
  .ligne__input {
    max-inline-size: 7rem;
  }
}

/* ---------------------------------------------------------------------------
   Statuts et journal — lot 11
--------------------------------------------------------------------------- */

.etat-dossier {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
  padding: 0.875rem 1rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
}

.etat-dossier__pastille {
  inline-size: 0.75rem;
  block-size: 0.75rem;
  border-radius: 50%;
  background: var(--gris-texte);
  flex-shrink: 0;
}

.etat-dossier--pret .etat-dossier__pastille { background: var(--statut-pret); }
.etat-dossier--envoye .etat-dossier__pastille { background: var(--sable); }
.etat-dossier--valide .etat-dossier__pastille { background: var(--action); }
.etat-dossier--annule .etat-dossier__pastille { background: var(--danger); }

.etat-dossier__texte {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.etat-dossier__depuis {
  font-size: var(--t-md);
  color: var(--ink-muted);
}

/* Les actions de statut sont des décisions, pas des réglages : boutons pleins,
   côte à côte tant que la largeur le permet, empilés ensuite. */
.actions-statut {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1rem;
}

.actions-statut .button {
  flex: 1 1 12rem;
}

/* Une pièce sans en-tête cochable : l'écran 11 ne fait que les remettre. */
.piece__fichier--seul {
  border-top: 0;
  background: var(--surface);
}

.journal {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
}

.journal__ligne {
  display: flex;
  gap: 0.875rem;
  padding-block: 0.875rem;
  border-top: 1px solid var(--filet);
}

.journal__ligne:first-child {
  border-top: 0;
  padding-top: 0;
}

.journal__pastille {
  inline-size: 0.625rem;
  block-size: 0.625rem;
  margin-block-start: 0.4375rem;
  border-radius: 50%;
  background: var(--gris-texte);
  flex-shrink: 0;
}

.journal__pastille--pret { background: var(--statut-pret); }
.journal__pastille--envoye { background: var(--sable); }
.journal__pastille--valide { background: var(--action); }
.journal__pastille--annule { background: var(--danger); }

.journal__contenu {
  min-width: 0;
}

.journal__titre {
  font-weight: 600;
}

.journal__meta {
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.journal__commentaire {
  margin-top: 0.375rem;
  font-size: var(--t-md);
}

/* ---------------------------------------------------------------------------
   Tableau de bord — lot 12
--------------------------------------------------------------------------- */

/* Les compteurs sont aussi les filtres : sur une tablette, un chiffre qu'on
   touche vaut mieux qu'un chiffre suivi d'un menu déroulant. */
.compteurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
  gap: 0.625rem;
  margin-top: 1.5rem;
}

.compteur {
  min-block-size: var(--touch-lg);
  padding: 0.75rem 0.5rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

/* --- Chiffres de synthèse ------------------------------------------------- */

.chiffres {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1.25rem;
}

.chiffre__libelle .muted {
  display: block;
  font-size: var(--t-sm);
}

.recherche {
  margin-top: 1.25rem;
}

/* --- Liste des dossiers --------------------------------------------------- */

.dossiers {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
  display: grid;
  gap: 0.625rem;
}

.dossier {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 1rem;
  min-block-size: var(--touch-lg);
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  text-decoration: none;
  color: inherit;
}

.dossier__nom {
  font-weight: 600;
  font-size: var(--t-body);
}

.dossier__lieu {
  font-size: var(--t-md);
  color: var(--ink-muted);
}

.dossier__pied {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: 0.25rem;
}

.dossier__montant {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  margin-inline-end: 0.5rem;
}

.dossier__date {
  font-size: var(--t-sm);
  color: var(--ink-muted);
  white-space: nowrap;
}

/* --- Pastille de statut --------------------------------------------------- */

.badge {
  flex-shrink: 0;
  padding: 0.1875rem 0.625rem;
  border-radius: 999px;
  font-size: var(--t-sm);
  font-weight: 600;
  background: var(--surface-muted);
  color: var(--ink-muted);
  white-space: nowrap;
}

/* --- Résultat par pièce --------------------------------------------------- */

.pieces-calcul {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
}

.piece-calcul {
  padding-block: 0.75rem;
  border-top: 1px solid var(--filet);
}

.piece-calcul:first-child {
  border-top: 0;
  padding-top: 0;
}

.piece-calcul__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.piece-calcul__nom {
  font-weight: 600;
}

.piece-calcul__puissance {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.piece-calcul__detail {
  margin-top: 0.25rem;
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

/* Une pièce incomplète n'est pas comptée dans le total : elle doit se voir. */
.piece-calcul--incomplete .piece-calcul__nom,
.piece-calcul--incomplete .piece-calcul__puissance {
  color: var(--sable);
}

/* ---------------------------------------------------------------------------
   Choix du matériel climatisation — lot 17
--------------------------------------------------------------------------- */

/* Le choix de la marque passe par les logos : un technicien reconnaît un
   logo plus vite qu'il ne lit une liste déroulante. */
/* Une rangée par famille de marques. Les logos constructeurs arrivent dans des
   ratios sans rapport — un Panasonic carré, un Thermor cinq fois plus large.
   Caler la seule hauteur, comme avant, les rendait incomparables : le carré
   devenait minuscule, l'allongé écrasait la rangée. C'est la BOÎTE qui est
   fixe désormais, et chaque logo s'y inscrit sans se déformer. */
/* --- Les étapes du choix, numérotées -------------------------------------

   L'écran empilait quatre blocs sans hiérarchie, et l'on ne savait pas par où
   commencer. Un numéro, un titre, et la valeur retenue à droite : le
   technicien voit d'un coup d'œil ce qu'il a déjà décidé et ce qui reste. */

.etape-choix { margin-top: 1.5rem; }

.etape-choix__titre {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: 0 0 .625rem;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 700;
}

.etape-choix__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
  border-radius: 999px;
  background: var(--action);
  color: #fff;
  font-size: var(--t-md);
  font-weight: 700;
}

/* Ce qui est déjà décidé, à droite du titre. */
.etape-choix__valeur {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--action-fonce);
}

.etape-choix__reste {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: var(--t-md);
  font-weight: 400;
  color: var(--gris-texte);
}

.marques-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: .5rem;
}

.marques-groupe {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: .5rem;
  margin-top: .5rem;
}

.marques-groupe:first-of-type { margin-top: 1.25rem; }

.marque {
  min-height: 3.25rem;
  padding: .5rem .75rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  font: inherit;
  display: grid;
  place-items: center;
}

.marque--active {
  border-color: var(--action);
  box-shadow: inset 0 0 0 1px var(--action);
}

/* Le logo seul quand on l'a, le nom seul quand on ne l'a pas. Les logos
   portent déjà le nom dans l'image : l'écrire une seconde fois dessous
   doublait la hauteur de chaque carte sans rien apprendre.

   NOM DE CLASSE DISTINCT DU BOUTON. Ce span s'appelait `.marque`, comme le
   bouton qui le contient : il héritait donc de sa bordure, de son padding et
   de sa hauteur minimale. On voyait un cadre dans le cadre, et le logo
   Sinclair — le plus large du dossier, 738 px — sortait de sa boîte : la
   carte affichait « SINCLA ». */
.marque-visuel {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-width: 0;
}

/* `contain` fait tenir le logo dans la boîte quel que soit son ratio, sans
   jamais le rogner : une marque doit rester reconnaissable. La hauteur est
   plus généreuse maintenant que le nom ne prend plus la moitié de la place. */
.marque__logo {
  width: 100%;
  max-width: 100%;
  height: 2.25rem;
  object-fit: contain;
  object-position: center;
  display: block;
}

.marque__nom {
  font-size: var(--t-md);
  font-weight: 600;
  text-align: center;
}

/* --- Une pièce, ses unités possibles ------------------------------------- */

.piece-materiel { margin-top: 1rem; }

.piece-materiel__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: .75rem;
  border-bottom: 1px solid var(--filet);
}

.piece-materiel__nom {
  font-family: var(--font-titre);
  font-weight: 700;
  font-size: var(--t-body);
  margin: 0;
}

.piece-materiel__besoin {
  margin: 0;
  font-size: var(--t-md);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.gammes { display: flex; flex-wrap: wrap; gap: .375rem; margin-top: .875rem; }

.gamme {
  min-height: var(--touch);
  padding: .375rem .75rem;
  border: 1px solid var(--filet);
  border-radius: 999px;
  background: var(--surface);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-muted);
  cursor: pointer;
}

.gamme-carte {
  scroll-snap-align: start;
  flex: 0 0 11.5rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .375rem;
  min-height: var(--touch);
  padding: .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  font: inherit;
  text-align: left;
  color: var(--encre);
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}

.gamme-carte--choisie {
  border-color: var(--action);
  box-shadow: inset 0 0 0 1px var(--action);
}

.gamme-carte__nom { font-size: var(--t-body); font-weight: 700; line-height: 1.2; }

.gamme-carte__detail { font-size: var(--t-sm); color: var(--gris-texte); }

/* La silhouette dit la forme d'installation, pas le modèle. Voir le
   commentaire de `silhouette-unite.js`. */
.silhouette {
  width: 100%;
  height: 4.5rem;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.silhouette__paroi { stroke: var(--filet); stroke-width: 2.5; }
.silhouette__paroi--pointille { stroke-dasharray: 3 4; }
.silhouette__corps { stroke: var(--encre); stroke-width: 2.5; fill: var(--surface); }
.silhouette__flux { stroke: var(--action); stroke-width: 2; opacity: .9; }

/* --- Options du chantier -------------------------------------------------

   Elles existent en grille depuis le lot 20 mais n'avaient jamais eu d'écran.
   Une case à cocher, un prix, et une quantité seulement quand l'unité l'exige :
   la goulotte se vend au mètre, le cache-groupe au forfait. Demander « combien »
   pour un forfait n'aurait aucun sens et ferait hésiter le technicien. */

.options__liste { list-style: none; margin: .875rem 0 0; padding: 0; }

.option {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
  padding: .625rem 0;
  border-top: 1px solid var(--filet);
}
.option:first-child { border-top: none; }

/* Devant le client, ce qui a été ajouté doit se repérer sans lire : la teinte
   fait le travail que la case à cocher seule ne fait pas d'un coup d'œil. */
.option--cochee {
  background: var(--action-pale);
  border-radius: var(--radius-sm);
  padding-left: .625rem;
  padding-right: .625rem;
  margin: 0 -.625rem;
}
.option--cochee + .option { border-top-color: transparent; }

/* La cible tactile est le libellé entier, pas la case : au doigt, sur une
   tablette tenue d'une main, viser une case de 16 px est un exercice. */
.option__choix {
  display: flex;
  align-items: center;
  gap: .625rem;
  flex: 1 1 10rem;
  min-height: var(--touch);
  min-width: 0;
  cursor: pointer;
}
.option__choix input { width: 1.25rem; height: 1.25rem; flex: none; accent-color: var(--action); }

.option__texte { display: flex; flex-direction: column; min-width: 0; }
.option__libelle { font-weight: 600; line-height: 1.3; }
.option__detail { font-size: var(--t-sm); color: var(--gris-texte); line-height: 1.35; }

.option__chiffre {
  display: flex;
  align-items: center;
  gap: .625rem;
  margin-left: auto;
}

.option__prix { font-weight: 700; white-space: nowrap; }
.option--cochee .option__prix { color: var(--action-fonce); }

.option__quantite { display: flex; align-items: center; gap: .375rem; }
.option__quantite-libelle { font-size: var(--t-sm); color: var(--gris-texte); }
.option__quantite input {
  width: 4.5rem;
  min-height: var(--touch);
  padding: .375rem .5rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  font: inherit;
  text-align: right;
}

/* --- Les autres marques chiffrées, en lignes ----------------------------- */

.solutions-autres { list-style: none; margin: .75rem 0 0; padding: 0; }

.solution-ligne {
  display: flex;
  align-items: center;
  gap: .875rem;
  width: 100%;
  min-height: var(--touch);
  padding: .625rem .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
  margin-bottom: .375rem;
}

.solution-ligne--choisie {
  border-color: var(--action);
  box-shadow: inset 0 0 0 1px var(--action);
  background: var(--action-pale);
}

/* Le logo est CALÉ À GAUCHE dans une boîte de largeur fixe. Centré, chaque
   marque partait de son propre bord — les logos larges débordaient vers la
   gauche, les étroits flottaient au milieu — et la colonne des rendements
   devenait un escalier. Ancrés à gauche, les dix logos s alignent. */
.solution-ligne__marque { flex: 0 0 6.5rem; min-width: 0; }
.solution-ligne__marque .marque-visuel { justify-content: flex-start; }
.solution-ligne__marque .marque__logo {
  height: 1.75rem; width: auto; max-width: 100%; object-position: left center;
}
.solution-ligne__marque .marque__nom { font-size: var(--t-sm); }

.solution-ligne__perf {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: .5rem;
  font-weight: 600;
}

/* La classe énergétique en pastille : c est le repère que le client connaît,
   celui de l étiquette qu il a déjà vue sur un lave-linge. */
.solution-ligne__classe {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .02em;
  padding: .125rem .4rem; border-radius: 999px;
  background: var(--action-pale); color: var(--action-fonce);
  white-space: nowrap;
}

.solution-ligne__prix { font-weight: 700; white-space: nowrap; }

/* --- Filtre par forme d'unité --------------------------------------------

   Remplace les six grandes cartes de gammes, qui occupaient un écran entier
   par pièce pour un critère que personne ne cherchait. La forme, elle, est la
   contrainte du chantier : un mur libre, un faux plafond, une allège. Elle
   tient sur une ligne de puces. */

.formes {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
  margin-top: .875rem;
}

.forme {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  min-height: var(--touch);
  padding: .375rem .75rem;
  border: 1.5px solid var(--filet);
  border-radius: 999px;
  background: var(--surface);
  color: var(--encre);
  font: inherit;
  font-size: var(--t-md);
  cursor: pointer;
}

.forme--choisie {
  border-color: var(--action);
  background: var(--action-pale);
  color: var(--action-fonce);
  font-weight: 600;
}

.forme__dessin { width: 1.5rem; height: 1.5rem; flex: none; }

.forme__nb {
  font-size: var(--t-sm);
  color: var(--gris-texte);
  font-weight: 400;
}
.forme--choisie .forme__nb { color: inherit; }

/* --- Rappel des unités retenues, en tête de l'écran groupe --------------- */

.unites-retenues__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.unites-retenues__liste { list-style: none; margin: .625rem 0 0; padding: 0; }

.unites-retenues__liste li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: .375rem 0;
  border-top: 1px solid var(--filet);
}
.unites-retenues__liste li:first-child { border-top: none; }

.unites-retenues__piece { color: var(--gris-texte); }
.unites-retenues__ref { font-weight: 600; text-align: right; }

/* --- Puissance délivrée, pièce par pièce -------------------------------- */

.delivrees__liste { list-style: none; margin: .625rem 0 0; padding: 0; }

.delivrees__ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: .375rem 0;
  border-top: 1px solid var(--filet);
}
.delivrees__ligne:first-child { border-top: none; }

.delivrees__piece { color: var(--gris-texte); }
.delivrees__valeur { font-weight: 600; }
/* Seul le chiffre délivré passe au rouge. Le besoin, lui, est un repère : le
   teinter aussi ferait lire « 3 800 W » comme une anomalie. */
.delivrees__ligne--faible .delivrees__valeur { color: var(--danger); }
.delivrees__ligne--faible .delivrees__valeur .muted { color: var(--gris-texte); }

/* --- Les unités, rangées par gamme ---------------------------------------

   Une liste triée par prix seul intercalait une Aura entre deux Save IN, puis
   une Harmonia : quatre modèles qui vont ensemble se retrouvaient à vingt
   lignes d'écart. Le titre de gamme les rassemble sans rien décider — le
   technicien voit toujours tout, dans le même ordre de prix. */

.gamme-bloc + .gamme-bloc { margin-top: .375rem; }

/* Le titre est un BOUTON : replié, il porte de quoi comparer sans ouvrir —
   la forme, le nom, le nombre de modèles, le prix d'entrée. */
.gamme-bloc__titre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
  width: 100%;
  min-height: var(--touch);
  margin: 0;
  padding: .5rem .75rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: inherit;
  font: inherit;
  font-weight: 700;
  font-size: var(--t-md);
  text-align: start;
  cursor: pointer;
}

.gamme-bloc--ouverte .gamme-bloc__titre {
  border-color: var(--action);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.gamme-bloc__chiffres {
  display: flex;
  align-items: center;
  gap: .625rem;
  flex: 0 1 auto;
  min-width: 0;
}

.gamme-bloc__prix {
  font-weight: 400;
  font-size: var(--t-sm);
  white-space: nowrap;
}
.gamme-bloc__prix strong { font-weight: 700; }

/* La gamme qui porte l'unité déjà retenue, repérable repliée. */
.gamme-bloc__coche {
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--action-pale);
  color: var(--action-fonce);
  font-size: var(--t-xs);
  font-weight: 700;
  white-space: nowrap;
}

.gamme-bloc__fleche {
  color: var(--gris-texte);
  font-size: var(--t-xs);
  flex: none;
}

/* Les unités dépliées prolongent le bouton : un seul bloc à l'œil. */
.gamme-bloc--ouverte .unites {
  margin-top: 0;
  padding: .5rem;
  border: 1px solid var(--action);
  border-top: none;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

/* `min-width: 0` sur les deux moitiés : sans lui, un nom de gamme long ne se
   comprime pas et vient chevaucher les chiffres au lieu de passer à la ligne. */
.gamme-bloc__nom {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  flex: 1 1 9rem;
  min-width: 0;
}

.gamme-bloc__dessin { width: 1.25rem; height: 1.25rem; flex: none; }

/* La forme se lit avant la gamme, en plus discret : elle situe le bloc, elle
   ne le nomme pas. */
.gamme-bloc__forme {
  font-weight: 400;
  color: var(--gris-texte);
}
.gamme-bloc__forme::after { content: " ·"; }

.gamme-bloc__nb {
  font-weight: 400;
  font-size: var(--t-sm);
  color: var(--gris-texte);
  white-space: nowrap;
}

.unites { display: flex; flex-direction: column; gap: .5rem; margin-top: .875rem; }
.gamme-bloc .unites { margin-top: .5rem; }

/* La silhouette dans la liste des unités : reconnaître la forme sans lire. */
.unite__dessin { width: 2.25rem; height: 2.25rem; flex: none; }

.unite {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: var(--touch);
  padding: .75rem .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: left;
  width: 100%;
}

.unite--choisie { border-color: var(--action); box-shadow: inset 0 0 0 1px var(--action); }

.unite__texte { display: flex; flex-direction: column; gap: .125rem; min-width: 0; }
.unite__ref { font-family: var(--font-data, inherit); font-weight: 600; }
.unite__meta { font-size: var(--t-sm); color: var(--ink-muted); }

.unite__prix {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}

.produit__note {
  display: block;
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--sable);
}

/* La puissance délivrée est la valeur qui compte, pas l'étiquette de l'unité :
   elle mérite d'être posée sous le choix, pas noyée dans les métadonnées. */
.delivree {
  margin: .875rem 0 0;
  padding: .625rem .75rem;
  border-radius: var(--radius-sm);
  background: var(--surface-muted);
  font-size: var(--t-md);
}

.delivree--inconnue {
  background: var(--sable-pale);
  color: var(--encre);
}

/* Ce que le catalogue ne dit pas d'un produit. Il reste proposable — masquer
   un matériel parce que NOTRE donnée manque revient à répondre « il n'apparaît
   pas » au client qui le demande. */
.reserves {
  margin: .625rem 0 0;
  padding: .5rem .625rem;
  border-radius: var(--radius-sm);
  background: var(--sable-pale);
  color: var(--sable);
  font-size: var(--t-sm);
  line-height: 1.4;
}

/* ---------------------------------------------------------------------------
   Relevé en sous-étapes — lot 18
--------------------------------------------------------------------------- */

/* Une colonne plus étroite : un formulaire qui court sur toute la largeur
   d'une tablette se lit mal, et rien ici ne demande la pleine mesure. */
/* Le relevé garde une colonne resserrée — une question à la fois, pas un
   formulaire à balayer des yeux — mais 34rem laissait les choix en file
   indienne là où deux tenaient côte à côte. */
.screen--etroit {
  max-width: 44rem;
}

/* Le fil des sous-étapes. Chacune est atteignable directement : un technicien
   qui se souvient d'un oubli sur la pièce 2 ne doit pas revenir en arrière
   trois fois. */
.sous-etapes {
  display: flex;
  gap: .375rem;
  overflow-x: auto;
  margin: 1rem -1rem .25rem;
  padding: 0 1rem .5rem;
  scrollbar-width: none;
}

.sous-etapes::-webkit-scrollbar { display: none; }

/* 48 PX, COMME TOUTE CIBLE TACTILE. Ces puces faisaient 32 px de haut : sous
   la règle de la maison (--touch), et sous les 44 px d'Apple. Un technicien
   debout, tablette dans une main, les manquait une fois sur trois. */
.sous-etape {
  flex: 0 0 auto;
  min-height: var(--touch);
  padding: .5rem .875rem;
  border: 1px solid var(--filet);
  border-radius: 999px;
  background: var(--surface);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
}

.sous-etape--faite {
  color: var(--action-fonce);
  border-color: color-mix(in srgb, var(--action) 35%, transparent);
  background: var(--action-pale);
}

.sous-etape--active {
  background: var(--encre);
  border-color: var(--encre);
  color: #fff;
  font-weight: 600;
}

/* --- Récapitulatif des pièces -------------------------------------------- */

.recap { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; gap: .5rem; }

.recap__acces {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  width: 100%;
  min-height: var(--touch-lg);
  padding: .75rem .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}

.recap__ligne--incomplete .recap__acces {
  border-color: color-mix(in srgb, var(--sable) 45%, transparent);
  background: var(--sable-pale);
}

.recap__nom { font-weight: 600; }
.recap__detail { font-size: var(--t-sm); color: var(--ink-muted); }
.recap__alerte {
  align-self: flex-start;
  margin-top: .25rem;
  font-size: var(--t-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--sable);
}

/* L'écran de relevé vise une contrainte précise : tenir sur une tablette sans
   défiler. Soixante-quinze pixels de trop suffisent à rendre le pouce
   nécessaire, donc l'espacement y est resserré — et seulement là. */
.screen--etroit .screen__title {
  font-size: clamp(var(--t-lg), 4vw, var(--t-xl));
  margin-top: .25rem;
  margin-bottom: .5rem;
}

.screen--etroit .screen__eyebrow {
  margin-bottom: .25rem;
}

.screen--etroit .form-section {
  padding-block: 1rem;
}

.screen--etroit .field {
  margin-bottom: .75rem;
}

.screen--etroit .field__hint {
  margin-top: .1875rem;
  line-height: 1.35;
}

.screen--etroit .sous-etapes {
  margin-top: .75rem;
}

/* Un produit proposé sous réserve se distingue sans se cacher : le liseré
   sable dit qu'il manque une information, la carte reste cliquable. */
.produit--reserve {
  border-color: color-mix(in srgb, var(--sable) 40%, var(--filet));
  background: color-mix(in srgb, var(--sable-pale) 45%, var(--surface));
}

/* ---------------------------------------------------------------------------
   Prix manquant — écran matériel
   Un produit retenu sans tarif fausse le total en silence. Le bloc le nomme
   là où l'installateur peut encore agir, avant de quitter l'écran.
--------------------------------------------------------------------------- */

.carte--prix-manquant {
  border-left: 4px solid var(--accent, var(--sable));
}

.prix-manquants {
  list-style: none;
  margin: 0 0 var(--espace-3, 12px);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espace-3, 12px);
}

.prix-manquant {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-2, 8px);
}

.prix-manquant__nom {
  font-weight: 600;
  flex: 1 1 12rem;
}

.prix-manquant__saisie {
  display: flex;
  align-items: center;
  gap: var(--espace-2, 8px);
}

/* Assez large pour un montant à quatre chiffres, pas plus : au-delà, le champ
   passe sous le bouton en mobile et la ligne se casse pour rien. */
.input--prix {
  width: 7rem;
  min-height: var(--touch);
  text-align: right;
}

.prix-manquant__unite {
  color: var(--texte-doux, var(--gris-texte));
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Choix en cartes

   Remplace le menu déroulant sur les listes courtes et fermées. Un déroulant
   demande trois gestes et cache les options ; la carte les montre, fait la
   largeur du pouce, et se lit sans rien ouvrir.
--------------------------------------------------------------------------- */

.champ-choix {
  border: 0;
  padding: 0;
  margin: 0 0 1.5rem;
  min-inline-size: 0;
}

.champ-choix__label {
  padding: 0;
  margin-bottom: .625rem;
  font-size: var(--t-body);
  font-weight: 600;
  color: var(--encre);
}

/* Plusieurs colonnes dès qu'il y a la place : sur une tablette tenue en
   largeur, une colonne unique gaspille la moitié de l'écran et rallonge le
   défilement. Le seuil vaut pour un libellé long — « Bonne — récente ou
   rénovée » — car un libellé coupé en trois lignes ne se lit pas plus vite
   qu'une liste. */
.choix {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .5rem;
}

.choix--2 { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }
.choix--3 { grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }

.choix__carte {
  position: relative;
  display: flex;
  align-items: center;
  gap: .75rem;
  min-height: var(--touch);
  padding: .875rem 1rem;
  border: 1.5px solid var(--filet);
  border-radius: var(--radius-md, .75rem);
  background: var(--surface, #fff);
  cursor: pointer;
  /* La transition ne porte que sur la couleur : une carte qui bouge sous le
     doigt donne l'impression d'avoir raté sa cible. */
  transition: border-color .12s ease, background-color .12s ease;
}

.choix__carte--actif {
  border-color: var(--action);
  background: var(--action-pale);
}

/* Le point vert de sélection, à droite, toujours au même endroit. */
.choix__carte--actif::after {
  content: "";
  position: absolute;
  right: 1rem;
  width: .625rem;
  height: .625rem;
  border-radius: 50%;
  background: var(--action);
}

/* Le radio reste dans le flux pour le clavier et les lecteurs d'écran, mais
   n'est pas dessiné : c'est la carte entière qui joue son rôle. */
.choix__radio {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}

.choix__radio:focus-visible + .choix__icone,
.choix__radio:focus-visible ~ .choix__texte { outline: none; }

.choix__carte:has(.choix__radio:focus-visible) {
  outline: 2px solid var(--action);
  outline-offset: 2px;
}

.choix__icone { font-size: var(--t-lg); line-height: 1; flex: none; }

.choix__texte {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  padding-right: 1.25rem;
}

.choix__libelle { font-weight: 600; font-size: var(--t-body); color: var(--encre); }

.choix__detail {
  font-size: var(--t-sm);
  line-height: 1.35;
  color: var(--gris-texte);
}

/* ---------------------------------------------------------------------------
   Barre d'onglets — navigation principale, fixée en bas

   Le pouce atteint le bas de l'écran, pas le haut : c'est la raison pour
   laquelle toutes les applications mobiles y ont mis leur navigation. Elle
   s'efface pendant un dossier, où le bas appartient à « précédent / suivant ».
--------------------------------------------------------------------------- */

.onglets {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: stretch;
  background: var(--surface, #fff);
  border-top: 1px solid var(--filet);
  /* Sur iPhone, la barre de gestes mange le bas de l'écran. */
  padding-bottom: env(safe-area-inset-bottom, 0);
  box-shadow: 0 -1px 12px rgb(11 20 34 / 6%);
}

.onglet {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .125rem;
  min-height: var(--touch);
  padding: .5rem .25rem .625rem;
  text-decoration: none;
  color: var(--gris-texte);
  font: inherit;
}

/* L'icône est un tracé, pas un caractère : elle suit la couleur du texte, donc
   l'état actif la teinte sans une règle de plus. */
.onglet__icone {
  display: grid;
  place-items: center;
  line-height: 0;
}

.onglet__libelle {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .01em;
}

.onglet--actif { color: var(--action-fonce); }

/* Le trait est au-dessus, contre la bordure : c'est là que l'œil suit
   l'onglet courant, et il ne concurrence pas le libellé. */
.onglet--actif::before {
  content: "";
  position: absolute;
  top: -1px;
  width: 2.25rem;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--action);
}

.onglet { position: relative; }

/* « Nouveau » n'est pas une destination mais l'action principale : elle est
   traitée comme telle plutôt que noyée parmi les autres onglets. */
.onglet--action .onglet__icone {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  margin-bottom: .0625rem;
  border-radius: 50%;
  background: var(--action);
  color: #fff;
}

.onglet--action { color: var(--encre); }
.onglet--action::before { display: none; }

/* La barre recouvre le bas de la page : sans cette réserve, la dernière ligne
   de chaque écran passe dessous et devient illisible. */
body:has(.onglets) .screen { padding-bottom: 5.5rem; }

/* ---------------------------------------------------------------------------
   En-tête allégé

   La navigation est passée dans les onglets du bas, la déconnexion sur l'écran
   Société. Ne reste en tête que ce qu'on presse en urgence : le mode client,
   quand on retourne l'écran vers le prospect.
--------------------------------------------------------------------------- */

.app-header__identity { font-size: var(--t-sm); }
.app-header__tenant { font-size: var(--t-md); }
.app-header__role { font-size: var(--t-xs); }

.mode-client {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  flex: none;
  min-height: var(--touch);
  padding: .375rem .75rem;
  border: 1.5px solid var(--filet);
  border-radius: 999px;
  background: var(--surface);
  color: var(--gris-texte);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

.mode-client__pastille {
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  background: var(--filet);
  flex: none;
}

/* LE MODE CLIENT SE VOIT, ET IL SE VOIT DEUX FOIS.
   Un bouton discret serait laissé actif par erreur — ou pire, cru actif alors
   qu'il ne l'est pas, et c'est ainsi qu'un prix d'achat finit sous les yeux
   d'un client. Le bouton porte donc l'état en clair, et un liseré bleu ferme
   le haut de l'écran tant que le mode dure. Le bouton se lit quand on tient
   la tablette ; le liseré se voit d'en face. */
.mode-client--actif {
  border-color: var(--action);
  background: var(--action-pale);
  color: var(--action-fonce);
}

.mode-client--actif .mode-client__pastille { background: var(--action); }

/* Sous 380 px, le nom de la société et le bouton ne tiennent pas ensemble.
   C'est le libellé qui cède : la pastille suffit à lire l'état. */
@media (max-width: 380px) {
  .mode-client__texte { display: none; }
  .mode-client { padding-inline: .625rem; }
}

.carte--compte { margin-top: 1.5rem; }

/* ---------------------------------------------------------------------------
   Les pièces sur un seul écran

   Reprend la structure du simulateur public : une ligne par pièce, le besoin
   sous la ligne, le total en bas, une seule action. Sept champs par pièce sur
   autant d'écrans devenaient neuf écrans pour un six-pièces.
--------------------------------------------------------------------------- */

.hint--ecran { margin: -.5rem 0 1.25rem; color: var(--gris-texte); }

.pieces-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }

.piece-ligne {
  padding: .875rem 1rem 1rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-md, .75rem);
  background: var(--surface, #fff);
}

.piece-ligne__saisie { display: flex; align-items: center; gap: .625rem; }

.piece-ligne__num {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--gris-texte);
  flex: none;
}

.piece-ligne__usage {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--touch);
  padding: .5rem .75rem;
  border: 1.5px solid var(--filet);
  border-radius: var(--radius-sm, .5rem);
  background: var(--surface, #fff);
  font: inherit;
  font-size: var(--t-body);
  color: var(--encre);
}

.piece-ligne__surface { display: flex; align-items: center; gap: .375rem; flex: none; }

.piece-ligne__surface input {
  width: 4.5rem;
  min-height: var(--touch);
  padding: .5rem .5rem;
  border: 1.5px solid var(--filet);
  border-radius: var(--radius-sm, .5rem);
  font: inherit;
  font-size: var(--t-body);
  font-weight: 700;
  text-align: center;
  color: var(--encre);
}

.piece-ligne__unite { color: var(--gris-texte); font-size: var(--t-md); }

/* La jauge n'est pas une décoration : elle rend comparables des pièces dont
   on lirait mal les watts les uns sous les autres. */
.piece-ligne__jauge {
  height: 4px;
  margin-top: .75rem;
  border-radius: 2px;
  background: var(--filet);
  overflow: hidden;
}

.piece-ligne__jauge span { display: block; height: 100%; background: var(--action); }

.piece-ligne__mesure {
  display: flex;
  justify-content: space-between;
  margin-top: .4375rem;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.piece-ligne__mesure strong { color: var(--encre); }

.piece-ligne__actions {
  display: flex;
  gap: 1rem;
  margin-top: .625rem;
}

.piece-ligne__detail {
  margin-top: .875rem;
  padding-top: .875rem;
  border-top: 1px dashed var(--filet);
}

.lien--discret {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--action-fonce);
  text-decoration: underline;
  cursor: pointer;
}

.lien--danger { color: var(--danger, var(--danger)); }

.ajouter-piece {
  display: block;
  width: 100%;
  min-height: var(--touch);
  margin-top: .75rem;
  padding: .875rem;
  border: 1.5px dashed var(--filet);
  border-radius: var(--radius-md, .75rem);
  background: none;
  font: inherit;
  font-size: var(--t-body);
  font-weight: 600;
  color: var(--action-fonce);
  cursor: pointer;
}

.total-besoin {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 1.25rem;
  padding: 1rem 1.125rem;
  border-radius: var(--radius-md, .75rem);
  background: var(--fond-clair);
}

.total-besoin__label { font-weight: 600; color: var(--encre); }

.total-besoin__valeur { color: var(--gris-texte); }

.total-besoin__valeur strong {
  font-family: var(--font-titre, inherit);
  font-size: var(--t-xl);
  font-weight: 600;
  color: var(--encre);
  margin-right: .1875rem;
}

/* Un élément de grille ou de flex vaut `min-width: auto` par défaut : il refuse
   de rétrécir sous la largeur de son contenu. Ici, la liste déroulante « Séjour,
   salle à manger » poussait la carte 53 px hors de l'écran en 375 px de large.
   La remise à zéro doit être posée sur CHAQUE niveau, sinon le plus interne
   continue de pousser. */
.pieces-liste > li,
.piece-ligne,
.piece-ligne__saisie,
.piece-ligne__usage { min-width: 0; }

.piece-ligne__saisie > * { min-width: 0; }

/* La bande des sous-étapes défile horizontalement ; sans cette réserve elle
   entraîne la page entière avec elle. */
.sous-etapes {
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.sous-etapes::-webkit-scrollbar { display: none; }

/* Le titre de sous-étape reprend le rôle du titre d'écran une fois le relevé
   découpé : c'est lui qui dit où l'on est, pas l'intitulé du parcours. */
.screen h2.screen__title { margin-top: .75rem; }

/* Ceinture et bretelles : si une barre d'onglets se retrouvait malgré tout sur
   un écran de parcours, c'est « précédent / suivant » qui doit rester
   cliquable. Le bouton qui fait avancer le dossier prime sur la navigation. */
.nav-etapes { z-index: 50; }

/* ---------------------------------------------------------------------------
   Trois solutions, chiffrées posées

   L'écran demandait de composer avant de voir un prix : une marque, un groupe,
   une gamme et une unité par pièce. Devant un client qui attend, ce n'est pas
   un choix, c'est un inventaire. Ces trois cartes donnent un prix en un coup
   d'œil ; la composition reste possible en dessous.
--------------------------------------------------------------------------- */

.solutions { margin: 1.5rem 0 2rem; }

.solutions__titre {
  font-family: var(--font-titre, inherit);
  font-size: var(--t-lg);
  font-weight: 600;
  margin: 0 0 .25rem;
  color: var(--encre);
}

.solutions__intro,
.solutions__note {
  margin: 0 0 1rem;
  font-size: var(--t-sm);
  line-height: 1.45;
  color: var(--gris-texte);
}

.solutions__note { margin: .875rem 0 0; }

.solutions__liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .75rem;
}

.solution {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  width: 100%;
  min-width: 0;
  padding: 1.5rem 1.125rem 1.125rem;
  border: 1.5px solid var(--filet);
  /* La couleur du palier n'est portée que par un liseré à gauche : trois
     cartes entièrement colorées se concurrenceraient au lieu de se comparer. */
  border-left-width: 4px;
  border-radius: var(--radius-md, .75rem);
  background: var(--surface, #fff);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.solution--premium { border-left-color: var(--sable); }
.solution--recommande { border-left-color: var(--action); }
.solution--budget { border-left-color: var(--info); }

.solution--choisie {
  border-color: var(--action);
  border-left-color: var(--action);
  box-shadow: inset 0 0 0 1px var(--action);
}

.solution__palier {
  position: absolute;
  top: -.6875rem;
  left: 1rem;
  padding: .1875rem .625rem;
  border-radius: 999px;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  background: var(--fond-clair);
  border: 1px solid var(--filet);
  color: var(--gris-texte);
}

.solution--premium .solution__palier { background: var(--sable-pale); color: var(--sable-fonce); border-color: transparent; }
.solution--recommande .solution__palier { background: var(--action-pale); color: var(--action-fonce); border-color: transparent; }
.solution--budget .solution__palier { background: var(--info-pale); color: var(--info); border-color: transparent; }

.solution__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-width: 0;
}

.solution__marque { flex: 0 1 9rem; min-width: 0; }
.solution__marque .marque { align-items: flex-start; gap: .375rem; }
/* Hauteur généreuse et alignement à gauche : un logotype large — Panasonic,
   General — a besoin de la largeur pour rester lisible, là où une marque
   compacte comme Daikin n'occupe que le haut de la boîte.
   `justify-content` doit suivre `object-position` : sans lui, l'image est
   posée à gauche DANS une boîte elle-même centrée, et le logo se retrouve au
   milieu malgré tout. Les deux règles ne font qu'une. */
.solution__marque .marque-visuel { justify-content: flex-start; }
.solution__marque .marque__logo {
  height: 1.75rem; width: auto; max-width: 8rem; object-position: left center;
}
.solution__marque .marque__nom { font-weight: 700; font-size: var(--t-body); text-align: left; }

.solution__prix { display: flex; flex-direction: column; align-items: flex-end; flex: none; }

.solution__prix strong {
  font-family: var(--font-titre, inherit);
  font-size: var(--t-xl);
  font-weight: 600;
  color: var(--encre);
  line-height: 1.1;
}

.solution__prix small { font-size: var(--t-xs); color: var(--gris-texte); }

/* `.solution__reference` a été retiré du balisage : la référence du groupe
   n'est pas un argument de vente, et cet écran se montre au client. La règle
   part avec elle plutôt que de rester à traîner. */

.solution__arguments {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .25rem;
}

.solution__arguments li {
  position: relative;
  padding-left: 1.125rem;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--encre);
}

.solution__arguments li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--action);
  font-weight: 700;
}

/* Les libellés de navigation ne se coupent pas. « Valider le matériel » passait
   sur trois lignes en 375 px, ce qui triplait la hauteur de la barre et rognait
   l'écran d'autant. Au besoin le texte se rétrécit, il ne se plie pas. */
.nav-etapes__suite,
.nav-etapes__retour {
  white-space: nowrap;
  min-width: 0;
}

.nav-etapes__contenu { gap: .625rem; }

@media (max-width: 420px) {
  .nav-etapes__suite,
  .nav-etapes__retour { font-size: var(--t-md); padding-inline: .875rem; }
}

/* ---------------------------------------------------------------------------
   Le bandeau d'économie

   Le vert est la couleur d'ACTION dans la charte : un bandeau vert de la
   largeur de l'écran entrait en concurrence avec les boutons. L'encre isole
   mieux le chiffre, et laisse le vert aux gestes.
--------------------------------------------------------------------------- */

.economie {
  background: var(--encre);
  text-align: left;
  padding: 1.75rem 1.5rem;
}

.economie__intitule { opacity: .7; font-size: var(--t-sm); }

.economie__valeur { font-size: var(--t-2xl); }

.economie__secondaire {
  margin-top: .5rem;
  font-size: var(--t-md);
  line-height: 1.45;
  opacity: .8;
}

/* Les hypothèses sont repliées par défaut : elles nourrissent la question
   « comment vous arrivez à ça ? », elles ne doivent pas l'ouvrir. */
.hypotheses[hidden] { display: none; }
.hypotheses { margin-top: .875rem; }

/* ---------------------------------------------------------------------------
   Les lots — un groupe extérieur et les pièces qu'il dessert

   Un seul lot ne s'affiche pas : l'installation à groupe unique est le cas
   courant, et l'écran ne doit pas porter la complexité de l'exception.
--------------------------------------------------------------------------- */

.ajouter-lot {
  display: block;
  width: 100%;
  margin: 1.25rem 0 0;
  padding: .875rem 1rem;
  border: 1.5px dashed var(--filet);
  border-radius: var(--radius-md, .75rem);
  background: none;
  font: inherit;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--action-fonce);
  text-align: left;
  cursor: pointer;
}

.ajouter-lot span {
  display: block;
  margin-top: .1875rem;
  font-weight: 400;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.lots {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin: 1.25rem 0 1rem;
}

.lot {
  flex: 1 1 9rem;
  min-width: 0;
  min-height: var(--touch);
  padding: .625rem .875rem;
  border: 1.5px solid var(--filet);
  border-radius: var(--radius-md, .75rem);
  background: var(--surface, #fff);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.lot--actif { border-color: var(--action); background: var(--action-pale); }

.lot__nom { display: block; font-weight: 700; font-size: var(--t-md); color: var(--encre); }

.lot__detail {
  display: block;
  margin-top: .0625rem;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.piece-materiel__lot {
  margin: .5rem 0 0;
  font-size: var(--t-sm);
  color: var(--gris-texte);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: baseline;
}

/* Photo du produit, quand un visuel a été déposé.
   La boîte est celle de la silhouette qu'elle remplace : `PhotoProduit`
   retombe sur le dessin quand aucun fichier ne charge, et les deux doivent
   occuper exactement la même place, sinon la liste sautille au chargement. */
.gamme-bloc__dessin,
.unite__dessin { object-fit: contain; background: #fff; border-radius: 4px; }

/* Le groupe extérieur n'a pas de silhouette de repli : sans fichier, rien ne
   s'affiche et la carte garde sa mise en page. */
.produit__photo {
  width: 3.5rem; height: 3.5rem; flex: none;
  object-fit: contain; background: #fff;
  border-radius: 6px; margin-right: .75rem;
}

/* Photo d'une ligne de prestation, sur l'écran d'estimation.
   Petite : elle illustre, elle ne prend pas la place du montant, qui reste ce
   que le client regarde en premier. */
.ligne__photo {
  width: 2.5rem; height: 2.5rem; flex: none;
  object-fit: contain; background: #fff;
  border-radius: 4px; margin-right: .6rem;
}

/* --- Formulaires : cibles plus larges, plus d'air -------------------------
   Le contexte du §9.3 vaut pour la saisie autant que pour les boutons : une
   tablette tenue debout, une main occupée, un client qui parle. Les champs
   avaient la hauteur de cible minimale et rien de plus ; ils passent à une
   taille où l'on vise sans regarder.

   `font-size` à 17 px n'est pas cosmétique : sous 16 px, iOS zoome sur le
   champ à la mise au point et décale toute la page. Le technicien perd sa
   place à chaque champ. */
.field__input {
  min-height: 3.25rem;
  padding: .75rem 1rem;
  font-size: var(--t-body);
}

.field__textarea { min-height: 5.5rem; }

.field { margin-bottom: 1.5rem; }

.field__label {
  font-size: var(--t-body);
  font-weight: 600;
  margin-bottom: .5rem;
}

/* Bloc de champs, dans une carte. Remplace `form-section` sur l'écran client :
   le titre est celui de la carte, pas celui de l'écran, qui porte déjà le nom
   de la sous-étape. */
.form-bloc__titre {
  font-size: var(--t-body);
  font-weight: 700;
  margin: 0 0 .25rem;
}

.form-bloc__aide {
  color: var(--gris-texte, var(--ink-muted));
  font-size: var(--t-md);
  margin: 0 0 1.25rem;
}

/* Le dernier champ d'une carte ne creuse pas le bas : la carte a déjà son
   propre rembourrage. */
.form-bloc .field:last-child { margin-bottom: 0; }

/* Le champ de quantité d'une ligne d'estimation reprend la hauteur et la
   police des autres — c'est une cible tactile comme les autres — mais garde
   son rembourrage latéral serré : il n'accueille qu'un ou deux chiffres, et
   la ligne doit laisser la place au libellé et au montant.
   Cette règle vient APRÈS l'agrandissement général, sinon le raccourci
   `padding` de celui-ci écraserait le `padding-inline` d'origine. */
.ligne__input { padding-inline: .5rem; }

/* `field-row--egal` était déclaré APRÈS la requête média qui fait passer les
   rangées sur une colonne : sa grille à deux colonnes égales gagnait, et le
   couple nom / prénom restait côte à côte à 147 px chacun sur un téléphone.
   La règle est rejouée ici, après la variante, pour que l'empilement
   s'applique aux deux. */
@media (max-width: 480px) {
  .field-row,
  .field-row--egal {
    grid-template-columns: 1fr;
  }
}

/* --- Bloc repliable (profil société) -------------------------------------
   Six blocs dépliés faisaient quatre écrans de défilement. Repliés, ils
   tiennent sur un écran : on voit tout ce qu'on peut régler, et l'on n'ouvre
   que ce qu'on vient changer. */
.bloc-pliant {
  margin-top: .75rem;
  background: var(--surface);
  border: 1px solid var(--filet, var(--border));
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.bloc-pliant__entete {
  display: flex;
  align-items: center;
  gap: .75rem;
  width: 100%;
  min-height: 3.5rem;
  padding: .875rem 1.25rem;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.bloc-pliant__titre {
  flex: 1 1 auto;
  font-size: var(--t-body);
  font-weight: 700;
}

/* Ce que le bloc contient déjà, lisible sans l'ouvrir. */
.bloc-pliant__resume {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-md);
  color: var(--gris-texte, var(--ink-muted));
}

.bloc-pliant__manque {
  flex: none;
  font-size: var(--t-xs);
  font-weight: 700;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--rouge-pale, var(--danger-pale));
  color: var(--rouge, var(--danger));
  white-space: nowrap;
}

.bloc-pliant__fleche { flex: none; color: var(--gris-texte, var(--ink-muted)); }

.bloc-pliant__corps { padding: 0 1.25rem 1.25rem; }

/* Un bloc qui retient un champ obligatoire vide se voit de loin : il est
   ouvert de force, autant qu'il soit aussi signalé. */
.bloc-pliant--alerte { border-color: var(--rouge, var(--danger)); }

/* --- Nombre avec boutons -------------------------------------------------
   Les deux boutons font la même hauteur que le champ et ne rétrécissent
   jamais : ce sont eux qu'on vise, pas le champ. */
.nombre {
  display: flex;
  align-items: stretch;
  gap: .5rem;
}

.nombre__bouton {
  flex: 0 0 3.25rem;
  min-height: 3.25rem;
  border: 1px solid var(--filet, var(--border));
  border-radius: .5rem;
  background: var(--surface);
  color: var(--encre, var(--ink));
  font: inherit;
  font-size: var(--t-xl);
  line-height: 1;
  cursor: pointer;
}

.nombre__bouton:active { background: var(--action-pale, var(--action-pale)); }
.nombre__bouton:disabled { opacity: .4; cursor: default; }

.nombre__champ {
  flex: 1 1 auto;
  min-width: 0;
  text-align: center;
}

/* Les flèches natives de `input[type=number]` doublent les boutons et sont
   trop petites pour un doigt. */
.nombre__champ::-webkit-outer-spin-button,
.nombre__champ::-webkit-inner-spin-button { appearance: none; margin: 0; }
.nombre__champ { appearance: textfield; -moz-appearance: textfield; }

.field__unite { font-weight: 400; color: var(--gris-texte, var(--ink-muted)); }

/* Boutons de pas sur la surface d'une pièce. Compacts — ils partagent la
   ligne avec le nom, la forme d'unité et l'unité de mesure — mais à la
   hauteur tactile, qui compte plus que la largeur : on vise vers le bas, pas
   sur le côté. */
.piece-ligne__pas {
  flex: none;
  width: 2.5rem;
  min-height: var(--touch);
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm, .5rem);
  background: var(--surface, #fff);
  color: var(--encre);
  font: inherit;
  font-size: var(--t-lg);
  line-height: 1;
  cursor: pointer;
}

.piece-ligne__pas:active { background: var(--action-pale, var(--action-pale)); }

/* Sous 380 px, la ligne d'une pièce n'a plus la place : les boutons partent,
   le champ reste. Mieux vaut un clavier qu'une ligne qui déborde. */
@media (max-width: 380px) {
  .piece-ligne__pas { display: none; }
}

/* --- Ajout d'une prestation ----------------------------------------------
   Remplace un `select` d'une trentaine d'options dont chaque libellé cumulait
   la catégorie, le nom et le prix. Sur une tablette, un menu déroulant de
   cette taille s'ouvre en liste modale qu'on fait défiler à l'aveugle — et le
   §9.3 l'interdit. Ici : une catégorie qu'on ouvre, des rangées qu'on touche. */
.ajout { margin-top: 2rem; }

.ajout__titre {
  font-size: var(--t-body);
  font-weight: 700;
  margin: 0 0 .5rem;
}

.ajout__liste { list-style: none; margin: 0; padding: 0; }

.ajout__option {
  display: flex;
  align-items: center;
  gap: .75rem;
  width: 100%;
  min-height: var(--touch);
  padding: .75rem .25rem;
  border: 0;
  border-top: 1px solid var(--filet, var(--border));
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.ajout__liste li:first-child .ajout__option { border-top: 0; }
.ajout__option:active { background: var(--action-pale, var(--action-pale)); }
.ajout__option:disabled { opacity: .5; cursor: default; }

.ajout__libelle { flex: 1 1 auto; min-width: 0; }

.ajout__prix {
  flex: none;
  font-weight: 700;
  white-space: nowrap;
}

.ajout__prix small {
  display: block;
  font-weight: 400;
  font-size: var(--t-xs);
  color: var(--gris-texte, var(--ink-muted));
}

/* Le « + » dit que toucher la rangée AJOUTE, au lieu de simplement
   sélectionner. Sans lui, une rangée cliquable ressemble à un choix qu'il
   faudra ensuite valider — ce qui était vrai avant, et ne l'est plus. */
.ajout__plus {
  flex: none;
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--action-pale, var(--action-pale));
  color: var(--action-fonce, var(--action-fonce));
  font-size: var(--t-lg);
  font-weight: 700;
}

/* --- Remises et marge, dans la grille tarifaire --------------------------
   Une ligne par marque : le nom, la remise, le coefficient, ce que ça donne,
   et le bouton qui recalcule. Le bloc porte `data-sensitive` — le mode client
   le masque entièrement quand l'écran est retourné vers le prospect. */
/* LE BLOC EST DÉSORMAIS DANS UNE CARTE PLIANTE (D-170) : elle porte le cadre,
   le fond et le rayon. Le bloc ne garde que son rythme interne — sans quoi on
   verrait deux bordures concentriques et le double des marges. */
.remises { margin: 0; }

.remises__titre { font-size: var(--t-body); font-weight: 700; margin: 0 0 .25rem; }

.remise-ligne {
  display: grid;
  grid-template-columns: minmax(7rem, 1fr) 7rem 7rem minmax(0, 1.2fr) auto;
  align-items: end;
  gap: .75rem;
  padding: .75rem 0;
  border-top: 1px solid var(--filet, var(--border));
}

/* Le réglage général se distingue : c'est celui qui écrase tous les autres. */
.remise-ligne--general {
  border-top: 0;
  margin-top: .5rem;
  padding: 1rem;
  border-radius: var(--radius-sm, .5rem);
  background: var(--action-pale, var(--action-pale));
}

.remise__marque { font-weight: 700; align-self: center; }

.remise__champ { display: flex; align-items: center; gap: .375rem; }
.remise__champ > span:not(.sr-only) { font-size: var(--t-sm); color: var(--gris-texte, var(--ink-muted)); }
.remise__champ input { text-align: center; }
.remise__champ small { color: var(--gris-texte, var(--ink-muted)); }

.remise__apercu {
  align-self: center;
  font-size: var(--t-sm);
  color: var(--gris-texte, var(--ink-muted));
}

/* VENDRE AU-DESSUS DU TARIF PUBLIC SE VOIT (D-168). Ce n'est pas interdit,
   mais ce n'est jamais voulu : c'est le signe qu'on a monté le coefficient
   sans remonter la remise. L'aperçu le dit en toutes lettres et prend la
   couleur d'alerte — la même que les montants négatifs de la colonne Marge. */
.remise__apercu--alerte { color: var(--danger); }

/* Sous 720 px, la ligne se replie : le nom sur toute la largeur, puis les
   deux champs côte à côte, puis l'aperçu et le bouton. */
@media (max-width: 720px) {
  .remise-ligne {
    grid-template-columns: 1fr 1fr;
    align-items: center;
  }
  .remise__marque,
  .remise__apercu { grid-column: 1 / -1; }
}

/* --- Tableau de bord en colonnes ----------------------------------------
   Une colonne par statut, dans l'ordre de la vie d'un dossier. Ce qu'on veut
   savoir en ouvrant l'écran n'est pas « quels dossiers ai-je » mais « où en
   sont-ils » : combien attendent d'être envoyés, combien sont partis sans
   réponse. La liste unique ne le disait pas. */
.colonnes {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(15rem, 1fr);
  gap: .75rem;
  margin: 1rem -1rem 0;
  padding: 0 1rem 1rem;
  overflow-x: auto;
  align-items: start;
  /* Sur tablette, cinq colonnes ne tiennent pas : on fait défiler, et chaque
     colonne s'accroche pour tomber juste. */
  scroll-snap-type: x proximity;
}

.colonne {
  /* LA COLONNE EST SON PROPRE REPÈRE, SINON LA PAGE DÉFILE DE CÔTÉ (D-173).
     `.colonnes` fait défiler ses colonnes horizontalement (`overflow-x: auto`),
     mais il est en position statique : il n'est donc le conteneur de référence
     d'aucun descendant en position absolue, et `overflow` ne découpe que ce
     dont on est le repère. Les libellés `.sr-only` de chaque colonne — un
     pixel, invisibles — se plaçaient donc par rapport à la PAGE. Celui de la
     cinquième colonne tombait à 1 111 px : mesuré à 900 px de large, l'accueil
     défilait horizontalement de 227 px, la page entière, pas le tableau.
     Un pixel invisible, et toute la règle « jamais de défilement horizontal »
     tombait. `relative` remet chaque colonne au rang de repère : ses enfants
     absolus rentrent dans le conteneur qui défile, et s'y font découper. */
  position: relative;
  scroll-snap-align: start;
  min-width: 0;
  background: var(--fond-doux, var(--fond-clair));
  border: 1px solid var(--filet, var(--border));
  border-radius: var(--radius-lg);
  padding: .75rem;
}

.colonne__entete {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .625rem;
}

.colonne__titre {
  flex: 1 1 auto;
  font-size: var(--t-md);
  font-weight: 700;
  margin: 0;
}

/* Le compte de la colonne, en pastille : c'est le premier chiffre qu'on lit. */
.colonne__compte {
  flex: none;
  min-width: 1.75rem;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--surface, #fff);
  border: 1px solid var(--filet, var(--border));
  font-size: var(--t-sm);
  font-weight: 700;
  text-align: center;
}

/* Une bande de couleur par statut, sur le bord haut : la colonne se reconnaît
   sans lire son titre, ce qui compte quand on fait défiler. */
/* Le filet haut dit le statut, et il le dit par un jeton nommé d'après le
   statut — pas par une teinte choisie à la main. Deux fautes vivaient ici :
   « prêt » empruntait `--zone-bleu`, réservé au logo, et « validé » prenait
   la couleur d'action, ce qui laissait croire qu'un dossier validé était un
   bouton. Un aboutissement est vert, une action est bleue. */
/* TROIS COLONNES DE TRAVAIL, UNE DE MEMOIRE (D-193). Le filet haut garde
   la teinte de l'etat qu'il represente ; « Arbitres » en melange deux --
   valide et annule -- et prend donc le gris du neutre : la colonne ne dit
   plus un etat, elle dit « plus rien a faire ». */
.colonne--a_finir { border-top: 3px solid var(--statut-brouillon); }
.colonne--a_envoyer { border-top: 3px solid var(--statut-pret); }
.colonne--chez_le_client { border-top: 3px solid var(--statut-envoye); }
.colonne--arbitre { border-top: 3px solid var(--filet-fort); }

.colonne__vide {
  margin: 0;
  padding: .75rem .25rem;
  font-size: var(--t-sm);
  color: var(--gris-texte, var(--ink-muted));
}

.colonne__liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .5rem;
}

.colonne .dossier {
  display: grid;
  gap: .25rem;
  padding: .75rem;
  background: var(--surface, #fff);
  border: 1px solid var(--filet, var(--border));
  border-radius: var(--radius-sm, .5rem);
  text-decoration: none;
  color: inherit;
  min-height: var(--touch);
}

.colonne .dossier__nom { font-weight: 700; }

.colonne .dossier__lieu,
.colonne .dossier__pied {
  font-size: var(--t-sm);
  color: var(--gris-texte, var(--ink-muted));
}

.colonne .dossier__pied {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem .5rem;
  align-items: baseline;
}

.colonne .dossier__montant { font-weight: 700; color: var(--encre, var(--ink)); }

/* Ce que la ligne contient, sous son libellé. En petit et en gris : la
   description explique, elle ne dispute pas au libellé la place qu'il occupe
   dans la lecture. */
.ligne__description {
  display: block;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte, var(--ink-muted));
  margin: .125rem 0 .25rem;
}

/* CE QUI N'EST PAS COMPRIS SE LIT COMME UNE LIMITE, PAS COMME UNE ALERTE.
   Le sable est la couleur des réserves du dimensionnement : il dit « attention
   à ceci » sans crier au danger. Un forfait qui s'arrête quelque part n'est pas
   un défaut — ne pas le dire en serait un. */
.ligne__limite {
  display: block;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--sable-fonce);
  margin: 0 0 .375rem;
}

/* ---------------------------------------------------------------------------
   Choix du matériel — pompe à chaleur air/eau

   L'écran à trois temps : la gamme, la configuration d'eau chaude, la
   référence. Il existait depuis un lot mais n'était monté par aucune route :
   son habillage n'avait jamais été écrit, et il s'affichait nu.

   TOUT EST PORTÉ PAR LE CONTENEUR, ET CE N'EST PAS UN CAPRICE. `.gamme-carte`
   appartient déjà à la CLIMATISATION, où c'est une vignette de carrousel
   horizontal — `flex: 0 0 11.5rem`, scroll-snap. Le même nom, une autre
   sémantique. Redéfinir la classe seule aurait cassé le carrousel clim ; tout
   passe donc par `.gammes-liste`, qui n'existe que côté PAC.
--------------------------------------------------------------------------- */

.gammes-liste {
  list-style: none;
  margin: .5rem 0 1rem;
  padding: 0;
  display: grid;
  gap: .5rem;
}

/* La vignette de carrousel redevient une ligne pleine largeur. */
.gammes-liste .gamme-carte {
  flex: 1 1 auto;
  width: 100%;
  gap: .25rem;
  padding: .875rem 1rem;
  min-height: var(--touch);
}

.gamme-carte__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .25rem .75rem;
  width: 100%;
}

/* Sans cette règle le nom et la plage se collent — « Wellea M HT R2904 à
   16 kW » — parce que deux <span> voisins n'ont pas d'espace entre eux. */
.gamme-carte__plage {
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--action-fonce);
  white-space: nowrap;
}

.gamme-carte__details {
  font-size: var(--t-sm);
  line-height: 1.45;
  color: var(--gris-texte);
}

/* Une réserve dit ce qu'on IGNORE, un motif dit pourquoi c'est écarté. Les
   deux se lisent, mais la réserve ne doit pas alarmer : la machine reste
   proposable. D'où le gris pour l'une, et le fond ambré pour l'autre. */
.gamme-carte__reserve {
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
  font-style: italic;
}

.gammes-liste--ecartees .gamme-carte--ecartee {
  display: flex;
  flex-direction: column;
  gap: .1875rem;
  width: 100%;
  padding: .75rem 1rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  opacity: .72;
  cursor: default;
}

.gamme-carte__motif {
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

/* --- Configuration d'eau chaude et référence -------------------------------
   Deux listes de boutons de même facture. Elles partagent tout sauf leur
   contenu, mais gardent des noms distincts : ce sont deux décisions
   différentes, et confondre leurs classes recommencerait la collision que ce
   fichier vient de contourner. */

.configurations,
.references {
  list-style: none;
  margin: .5rem 0 0;
  padding: 0;
  display: grid;
  gap: .5rem;
}

.configuration,
.reference {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .125rem .75rem;
  width: 100%;
  min-height: var(--touch);
  padding: .75rem 1rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  font: inherit;
  text-align: left;
  color: var(--encre);
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}

.configuration--choisie,
.reference--choisie {
  border-color: var(--action);
  box-shadow: inset 0 0 0 1px var(--action);
}

.configuration__nom,
.reference__nom {
  flex: 1 1 12rem;
  font-weight: 700;
  line-height: 1.25;
}

.configuration__ecs,
.reference__puissance {
  font-size: var(--t-sm);
  color: var(--gris-texte);
  white-space: nowrap;
}

/* Le prix ferme la ligne, à droite, parce que c'est sur lui que se pose l'œil
   quand on compare deux références. */
.reference__prix {
  margin-left: auto;
  font-size: var(--t-body);
  font-weight: 600;
  white-space: nowrap;
}

.configuration__usage {
  flex: 1 1 100%;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

/* ---------------------------------------------------------------------------
   Grille tarifaire — les paquets « marque — gamme »

   Mille références triées par libellé restaient mille lignes en vrac. La
   bande de regroupement colle en haut du tableau pendant le défilement : celui
   qui pose ses prix travaille marque par marque, et perdait sa place.
--------------------------------------------------------------------------- */

.tableau__paquet th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: .5rem .75rem;
  background: var(--action-pale);
  border-top: 1px solid var(--filet);
  border-bottom: 1px solid var(--filet);
  text-align: left;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--action-fonce);
}

/* La gamme QUALIFIE la marque : même ligne, poids moindre. Deux bandes
   « Daikin » qui ne se distinguent que par leur gamme doivent se lire d'un
   coup d'œil, sans relire le mot Daikin. */
.tableau__paquet-gamme {
  font-weight: 600;
  color: var(--encre);
}

.tableau__paquet-gamme::before {
  content: " — ";
  color: var(--gris-texte);
  font-weight: 400;
}

.tableau__paquet-compte {
  float: right;
  min-width: 1.75rem;
  padding: 0 .375rem;
  border-radius: 999px;
  background: var(--surface);
  font-size: var(--t-xs);
  font-weight: 700;
  text-align: center;
  color: var(--gris-texte);
}

/* ---------------------------------------------------------------------------
   Filtres marque puis gamme de la grille tarifaire

   Des puces, pas un menu déroulant : le §UX interdit le survol et les menus à
   plusieurs niveaux, et sur une tablette une puce se touche là où une liste
   déroulante se manque. Chaque puce porte son compte — savoir qu'une marque
   pèse 14 références ou 250 change l'ordre dans lequel on s'y met.
--------------------------------------------------------------------------- */

.filtres-marques {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
  margin: .75rem 0 .5rem;
}

.filtres-marques--gammes {
  padding-top: .5rem;
  border-top: 1px solid var(--filet);
}

.filtre-puce {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  min-height: var(--touch);
  padding: .5rem .75rem;
  border: 1px solid var(--filet);
  border-radius: 999px;
  background: var(--surface);
  font: inherit;
  font-size: var(--t-md);
  color: var(--encre);
  cursor: pointer;
  transition: border-color .12s, background .12s;
}

.filtre-puce--actif {
  border-color: var(--action);
  background: var(--action-pale);
  font-weight: 700;
  color: var(--action-fonce);
}

.filtre-puce__compte {
  min-width: 1.5rem;
  padding: 0 .3125rem;
  border-radius: 999px;
  background: var(--action-pale);
  font-size: var(--t-xs);
  font-weight: 700;
  text-align: center;
  color: var(--gris-texte);
}

.filtre-puce--actif .filtre-puce__compte {
  background: var(--surface);
  color: var(--action-fonce);
}

/* ===========================================================================
   Parcours de visite — reprise de la hiérarchie

   Ce que l'écran disait avant : tout au même poids. Une question, une réponse,
   une note explicative et un avertissement se ressemblaient. Le technicien
   devait LIRE pour savoir ce qu'on attendait de lui, au lieu de le voir.

   Trois corrections, dans cet ordre d'importance.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. UNE NOTE N'EST PAS UNE RÉPONSE COCHÉE

   `.notice--info` prenait `--brand-50` en fond et `--brand-200` en bordure :
   exactement le vert pâle d'une carte sélectionnée. « Zone H1 déduite du code
   postal » s'affichait donc comme si le technicien venait de la choisir.

   Le vert reste la couleur de CE QU'ON A DÉCIDÉ. Une note prend un fond neutre
   et un filet vertical : elle se distingue sans crier, et ne peut plus être
   confondue avec une sélection.
--------------------------------------------------------------------------- */

.notice--info {
  background: rgb(11 20 34 / 0.035);
  border: 1px solid rgb(11 20 34 / 0.08);
  border-left: 3px solid var(--info);
  color: var(--encre);
  font-weight: 400;
}

/* Même raison côté avertissement : le filet porte la couleur, le fond reste
   discret. Un bandeau jaune vif à chaque écran finit par ne plus alerter. */
.notice--warning {
  background: rgb(178 107 6 / 0.07);
  border: 1px solid rgb(178 107 6 / 0.18);
  border-left: 3px solid var(--sable);
  color: var(--encre);
  font-weight: 400;
}

/* ---------------------------------------------------------------------------
   2. LA SÉLECTION SE MARQUE D'UNE COCHE, PAS D'UN POINT

   Un point vert ne dit rien : il peut signaler une nouveauté, une alerte, un
   état. La coche ne veut dire qu'une chose, et c'est celle-là. Elle est
   dessinée en CSS — deux bords d'un carré pivoté — pour ne dépendre d'aucune
   police d'icônes.
--------------------------------------------------------------------------- */

.choix__carte--actif {
  border-color: var(--action);
  background: var(--action-pale);
  box-shadow: inset 0 0 0 1px var(--action);
}

.choix__carte--actif::after {
  content: "";
  position: absolute;
  right: 1rem;
  top: 50%;
  width: .3125rem;
  height: .625rem;
  margin-top: -.375rem;
  border: solid var(--action-fonce);
  border-width: 0 2px 2px 0;
  border-radius: 1px;
  transform: rotate(45deg);
  background: none;
}

/* Le rayon suivait un jeton qui n'existe pas — `--radius-md` n'est pas dans la
   charte, qui n'autorise que 8, 12 et 18 px. La carte prenait donc sa valeur
   de repli. */
.choix__carte { border-radius: var(--radius); }

/* ---------------------------------------------------------------------------
   3. LA QUESTION DOIT PESER PLUS QUE SA RÉPONSE

   Le libellé « Niveau d'isolation » avait la même taille et presque la même
   graisse que « Bonne — récente ou rénovée ». Rien ne disait lequel des deux
   posait la question.

   Le libellé monte d'un cran et prend de l'air AU-DESSUS, pas en dessous :
   c'est l'espace qui sépare deux questions, pas celui qui sépare une question
   de ses réponses. Un champ qui respire des deux côtés flotte.
--------------------------------------------------------------------------- */

.champ-choix + .champ-choix,
.champ-choix + .field,
.field + .champ-choix {
  margin-top: 1.75rem;
}

.champ-choix__label,
.field__label {
  font-size: var(--t-body);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--encre);
  margin-bottom: .5rem;
}

/* L'aide se colle à ce qu'elle explique. Un texte d'aide à mi-chemin entre
   deux champs se rattache visuellement au mauvais. */
.field__aide,
.champ-choix__aide,
.field__hint {
  margin-top: .375rem;
  font-size: var(--t-sm);
  line-height: 1.45;
  color: var(--gris-texte);
}

/* L'astérisque du champ requis ne doit pas hurler : il informe, il n'alerte
   pas. Le rouge est reserve aux erreurs reelles. */
.field__required {
  color: var(--gris-texte);
  font-weight: 400;
}

/* ---------------------------------------------------------------------------
   4. DEUX COUPURES QUI FONT « CASSÉ »

   a) La barre d'étapes se coupait en plein mot à droite — « Dossier », puis un
      « 1 » orphelin. Elle défile, c'est voulu : neuf étapes ne tiennent pas sur
      un téléphone, et comprimer les libellés jusqu'à l'illisible serait pire.
      Mais rien ne DISAIT qu'elle défile, et une phrase tranchée net ressemble
      à un bug, pas à une invitation.

      Un dégradé sur le bord droit règle les deux : il masque la coupure et
      annonce la suite. Il ne s'affiche que sur le débordement réel, grâce à
      `animation-timeline: scroll()` là où c'est supporté ; ailleurs, le voile
      reste discret et ne gêne pas.

   b) Le contenu passait SOUS la barre de navigation basse. Le corps de page
      réserve la hauteur de la barre d'onglets, mais les écrans du parcours
      posent par-dessus leur propre barre « précédent / suivant », plus haute.
      La dernière ligne d'un formulaire finissait dessous — sur un écran de
      relevé, c'est la question qu'on oublie de poser.
--------------------------------------------------------------------------- */

.parcours__etapes {
  position: relative;
  gap: .125rem;
  /* Le premier et le dernier ne collent plus au bord : une pastille tronquée
     par le masque se lit mal. */
  padding-inline: .125rem 1.5rem;
  scroll-padding-inline: 1.5rem;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent 100%);
          mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent 100%);
}

/* Sur une tablette tenue en largeur, les neuf étapes tiennent presque : on
   resserre plutôt que de faire défiler pour trois pixels. */
@media (min-width: 46rem) {
  .parcours__lien { padding-inline: .375rem; font-size: var(--t-sm); }
  .parcours__etapes { padding-inline: .125rem; }
  .parcours__etapes { -webkit-mask-image: none; mask-image: none; }
}

/* La réserve du bas vaut la barre du parcours, pas celle des onglets. Les deux
   ne coexistent jamais : un écran de parcours remplace la barre d'onglets. */
.screen:has(~ .nav-etapes),
body:has(.nav-etapes) .screen {
  padding-bottom: 6.5rem;
}

/* ---------------------------------------------------------------------------
   Tableau de bord — l'en-tête de colonne devient le filtre

   Les compteurs « Tous / Brouillon » ont disparu : ils répétaient le nombre
   que la colonne affichait déjà juste en dessous. Leur rôle de filtre revient
   à l'en-tête, qui portait déjà le nom et le compte — un contrôle au lieu de
   deux, et il se trouve là où l'œil est déjà posé.
--------------------------------------------------------------------------- */

/* DEUX GESTES, DEUX BOUTONS. Le nom replie la colonne, la flèche l'isole.
   Un seul bouton pour les deux obligeait à choisir entre ranger ce qu'on ne
   regarde pas et ne voir que ce qu'on regarde. */
.colonne__replier {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--touch);
  padding: 0;
  border: 0;
  /* La bande de couleur du statut vit sur `.colonne` : le bouton ne doit pas
     la recouvrir, d'où un fond transparent plutôt qu'une surface. */
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}

/* Le chevron garde sa place quand il tourne : sans largeur fixe, replier une
   colonne décalait son titre d'un pixel ou deux, et toute la rangée bougeait. */
.colonne__chevron {
  flex: none;
  width: .875rem;
  font-size: var(--t-xs);
  color: var(--gris-texte, var(--ink-muted));
}

.colonne__replier .colonne__titre {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.colonne__isoler {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch);
  height: var(--touch);
  /* La cible reste à 48 px, le dessin tient dans 28 : un carré de 48 px visible
     à côté du titre pèserait plus que le titre lui-même. */
  margin-inline-end: -.5rem;
  border: 0;
  border-radius: var(--radius-sm, .5rem);
  background: none;
  color: var(--gris-texte, var(--ink-muted));
  font-size: var(--t-body);
  cursor: pointer;
}

.colonne__isoler[aria-pressed="true"] {
  background: var(--action);
  color: #fff;
}

/* Une colonne repliée n'est plus qu'un en-tête : on lui retire le creux qui
   attendait sa liste, sans quoi cinq colonnes rangées laissent cinq trous. */
.colonne:has(.colonne__replier[aria-expanded="false"]) .colonne__entete {
  margin-bottom: 0;
}

/* Une colonne seule occupe toute la largeur : garder cinq gouttières pour une
   seule liste laisserait les cartes étroites sans raison. */
.colonnes:has(.colonne--seule) { grid-template-columns: 1fr; }

/* ---------------------------------------------------------------------------
   Parcours — la jauge mène, les pastilles suivent

   Direction « Signal », retenue seule : on garde les cartes et le sans-serif,
   on ne change que le repère de progression.

   La barre d'étapes se coupait en plein mot — neuf pastilles libellées ne
   tiennent sur aucune tablette. Les libellés disparaissent SAUF celui de
   l'étape en cours : neuf points et un nom tiennent partout, et la jauge dit
   le reste. Rien n'est retiré du composant : les étapes franchies restent
   cliquables.

   LE BLEU PORTE LA PROGRESSION, LE VERT RESTE L'ACTION. Aucune couleur
   importée : ce sont celles de la charte, réparties selon ce qu'elles disent.
--------------------------------------------------------------------------- */

.parcours { padding-block: .75rem .5rem; }

.parcours__rail { height: 5px; background: rgb(11 20 34 / 0.08); }

.parcours__jauge {
  background: var(--action);
  transition: width .3s ease;
}

.parcours__libelle { display: none; }

.parcours__etape--active .parcours__libelle {
  display: inline;
  font-size: var(--t-md);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--encre);
}

.parcours__etapes {
  gap: .1875rem;
  padding-inline: 0;
  -webkit-mask-image: none;
          mask-image: none;
}

.parcours__lien { padding: .25rem .1875rem; }

.parcours__pastille {
  width: 1.375rem;
  height: 1.375rem;
  display: grid;
  place-items: center;
  border-radius: 999px;
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

.parcours__etape--active .parcours__pastille { background: var(--action); color: #fff; }
.parcours__etape--avenir .parcours__pastille { background: rgb(11 20 34 / 0.06); color: var(--gris-texte); }

/* La case elle-même : 24 px dessinés, 48 px touchables grâce au libellé qui
   l'entoure. Jamais de case seule au milieu d'un blanc.

   SÉLECTEUR PROPRE À L'ÉCRAN, ET PAS `.case` : ce nom appartient déjà aux
   cases à cocher pleine largeur du relevé (FormCheckboxGroup). Le réutiliser
   ici les réduisait à 22 px de large — les libellés « Copropriété — accord
   nécessaire » se chevauchaient à l'étape « Le chantier ». Vu à l'écran. */
.catalogue-gamme__case {
  flex: 0 0 auto;
  width: 1.375rem;
  height: 1.375rem;
  accent-color: var(--action);
  cursor: pointer;
}

.catalogue-gamme__case:disabled { cursor: not-allowed; opacity: .5; }

/* Le composant de marque centre son logo — c'est juste dans une carte produit,
   faux dans une ligne de liste, où l'œil descend une colonne de logos et a
   besoin d'un bord commun. Mesuré : le logo partait à 270 px sur une ligne qui
   commence à 17. */
.marque-bloc__choix .marque-visuel {
  flex: 0 0 auto;
  justify-content: flex-start;
}

/* ---------------------------------------------------------------------------
   Choix d'une unité — les atouts et les filtres

   L'écran affichait déjà « SEER 8,5 · 19 dB(A) · A+++ ». Un technicien sait
   lire ça ; le client assis à côté de lui, non. L'atout traduit la donnée en
   la raison qu'on donne à voix haute : « celle-ci, c'est la silencieuse ».
--------------------------------------------------------------------------- */

.filtres-unite {
  display: grid;
  gap: .375rem;
  margin: .5rem 0 .75rem;
}

.filtres-unite__ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .375rem;
}

/* Le nom du critère, à largeur fixe : deux lignes de filtres alignent alors
   leurs puces, et l'œil descend une colonne au lieu de chercher. */
.filtres-unite__titre {
  flex: 0 0 5.5rem;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--gris-texte);
}

.filtres-unite .filtre-puce {
  min-height: 2.25rem;
  padding: .3125rem .625rem;
  font-size: var(--t-sm);
}

/* Sur un rang de filtres secondaires, 48 px par puce mangerait l'écran. La
   cible reste confortable parce qu'elles sont groupées et espacées ; la règle
   des 48 px vaut pour les cibles ISOLÉES du parcours, pas pour un peigne. */

.unite__atouts {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin-top: .1875rem;
}

.atout {
  padding: .0625rem .375rem;
  border-radius: 999px;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .01em;
  white-space: nowrap;
}

/* Quatre atouts, quatre couleurs de la charte, et aucune n'est le vert plein
   de l'action : une étiquette n'est pas un bouton, elle ne se touche pas. */
.atout--economie { background: var(--action-pale); color: var(--action-fonce); }
.atout--prix { background: var(--sable-pale); color: var(--sable-fonce); }
.atout--silence { background: rgb(29 110 133 / 0.12); color: var(--info); }
.atout--fluide { background: var(--violet-pale); color: var(--violet); }

/* ---------------------------------------------------------------------------
   Tableau de bord — chiffres clés, répartition, marge par chantier

   « Vraiment avoir des infos vite et rapide. » Trois étages, du plus dense au
   plus détaillé : quatre chiffres qu'on lit sans s'arrêter, trois répartitions
   en barres qu'on compare d'un coup d'œil, puis les dossiers eux-mêmes. Rien
   n'est en tableau : un tableau se lit, une barre se voit.
--------------------------------------------------------------------------- */

/* --- Les quatre chiffres --------------------------------------------------
   Une seule surface, découpée par des filets, et non quatre cartes : quatre
   cartes disent « quatre objets », alors que ce sont quatre lectures du même
   portefeuille. Le libellé au-dessus, en capitales espacées ; la valeur en
   en Space Grotesk, grande ; le détail dessous, en gris. La même colonne vertébrale
   dans chaque case, pour que l'œil ne cherche pas. */
.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
  margin-top: .25rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

.kpi {
  display: grid;
  align-content: start;
  gap: .125rem;
  padding: .875rem 1.125rem 1rem;
  border-inline-start: 1px solid var(--filet);
  /* Le premier filet tomberait sur le bord de la carte : on l'annule, et sur
     une rangée repliée en deux lignes, le filet du haut le remplace. */
}

.kpi:first-child { border-inline-start: 0; }

.kpi__libelle {
  margin: 0;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gris-texte);
}

.kpi__valeur {
  margin: .125rem 0 0;
  font-family: var(--font-titre);
  font-weight: 700;
  font-size: var(--t-xl);
  line-height: 1.1;
  letter-spacing: var(--serrage-titre);
  font-variant-numeric: tabular-nums;
  color: var(--encre);
}

.kpi__detail {
  margin: 0;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

/* Ce qui réclame un geste se voit sans qu'on le cherche : l'ambre de la
   charte, réservé aux alertes, jamais aux boutons. */
.kpi__alerte {
  color: var(--sable);
  font-weight: 700;
}

/* Les cases se replient en 2 × 2 sur un écran étroit ; un filet horizontal
   sépare alors les rangées, là où le filet vertical ne suffit plus. */
@media (max-width: 43rem) {
  .kpis { grid-template-columns: 1fr 1fr; }
  .kpi:nth-child(odd) { border-inline-start: 0; }
  .kpi:nth-child(n + 3) { border-top: 1px solid var(--filet); }
}

/* --- La répartition -------------------------------------------------------- */

.pilotage {
  margin-top: .75rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

.pilotage__bascule {
  display: flex;
  align-items: baseline;
  gap: .625rem;
  width: 100%;
  min-height: var(--touch);
  padding: .75rem 1.125rem;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}

.pilotage__titre {
  font-family: var(--font-titre);
  font-weight: 600;
  white-space: nowrap;
}

/* Le résumé dit ce que le panneau contient AVANT qu'on l'ouvre : replié, un
   titre seul ne dit pas s'il vaut le geste. */
.pilotage__resume {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--t-sm);
  color: var(--gris-texte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pilotage__chevron {
  flex: none;
  color: var(--gris-texte);
}

.pilotage__contenu {
  display: grid;
  /* Trois blocs côte à côte quand l'écran les tient, empilés sinon. `auto-fit`
     s'en charge sans point de rupture écrit à la main. */
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: .5rem 2rem;
  padding: .25rem 1.125rem 1.125rem;
  border-top: 1px solid var(--filet);
}

.ventilation { min-width: 0; }

.ventilation__titre {
  margin: .875rem 0 .5rem;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gris-texte);
}

.ventilation__lignes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .5rem;
}

/* Une ligne : libellé et nombre sur la première rangée, la barre et le
   détail sur la seconde. Deux rangées plutôt qu'une, parce qu'un libellé
   comme « Pompe à chaleur air/eau » et un montant ne tiennent pas de front
   dans un tiers d'écran de tablette. */
.ventilation__ligne {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "libelle nombre"
    "jauge   detail";
  column-gap: .625rem;
  row-gap: .25rem;
  align-items: baseline;
  font-size: var(--t-sm);
}

.ventilation__libelle {
  grid-area: libelle;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  color: var(--encre);
}

.ventilation__nombre {
  grid-area: nombre;
  font-family: var(--font-titre);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--encre);
}

.ventilation__jauge {
  grid-area: jauge;
  align-self: center;
  display: block;
  height: .5rem;
  border-radius: 999px;
  background: var(--fond-clair);
  overflow: hidden;
}

.ventilation__barre {
  display: block;
  height: 100%;
  min-width: .5rem;
  border-radius: inherit;
  background: var(--jauge, var(--action));
}

/* UNE BARRE N'EST PAS UN BOUTON.
   Ces jauges prenaient les trois couleurs du logo pour les types de projet et
   le bleu d'action pour les marques. Deux fautes : les couleurs de zone sont
   réservées au logo, et une barre peinte en couleur d'action se prend pour un
   contrôle sur lequel on peut appuyer. Les séries classent, elles n'agissent
   pas — d'où une palette à part, dont le bleu d'action est absent à dessein.
   Dans un bloc, le rang décline la teinte : on lit d'abord la première barre,
   et l'œil descend. */
.ventilation--type .ventilation__barre[data-rang="0"] { --jauge: var(--serie-1); }
.ventilation--type .ventilation__barre[data-rang="1"] { --jauge: var(--serie-2); }
.ventilation--type .ventilation__barre[data-rang="2"] { --jauge: var(--serie-3); }

.ventilation--marque .ventilation__barre { --jauge: var(--serie-1); }
.ventilation--marque .ventilation__barre[data-rang="1"] { --jauge: var(--serie-3); }
.ventilation--marque .ventilation__barre[data-rang="2"],
.ventilation--marque .ventilation__barre[data-rang="3"],
.ventilation--marque .ventilation__barre[data-rang="4"],
.ventilation--marque .ventilation__barre[data-rang="5"] { --jauge: var(--serie-4); }

.ventilation--departement .ventilation__barre { --jauge: var(--serie-1); }
.ventilation--departement .ventilation__barre[data-rang="1"],
.ventilation--departement .ventilation__barre[data-rang="2"],
.ventilation--departement .ventilation__barre[data-rang="3"],
.ventilation--departement .ventilation__barre[data-rang="4"],
.ventilation--departement .ventilation__barre[data-rang="5"] { --jauge: var(--serie-4); }

.ventilation__detail {
  grid-area: detail;
  display: flex;
  gap: .5rem;
  font-size: var(--t-xs);
  color: var(--gris-texte);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ventilation__taux { font-weight: 600; color: var(--action-fonce); }

/* Sur combien de dossiers arbitrés porte le taux. En plus petit et en gris :
   c'est une précaution de lecture, pas une donnée qu'on compare. */
.ventilation__base {
  font-weight: 400;
  color: var(--gris-texte);
}

/* --- La marge, sur la carte du chantier ------------------------------------
   Elle est marquée `data-sensitive` dans le gabarit : le mode client la fait
   disparaître quand l'écran se retourne vers le prospect. Ce n'est qu'un filet
   visuel — la protection réelle est que la RLS n'envoie jamais les prix
   d'achat à un technicien, et que cet écran ne les demande même pas. */
.dossier__marge {
  display: inline-flex;
  align-items: baseline;
  gap: .25rem;
  padding: 0 .5rem;
  border-radius: 999px;
  background: var(--action-pale);
  color: var(--action-fonce);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.dossier__marge--negative {
  background: rgb(180 35 24 / 0.1);
  color: var(--danger);
}

/* La part du chantier que la marge couvre. Sans elle, « 38 % » lu sur un
   chantier dont deux lignes sur dix ont un prix d'achat serait un chiffre
   faux — et personne ne le saurait. */
.dossier__couverture {
  font-weight: 400;
  font-size: var(--t-xs);
  opacity: .85;
}

/* --- À relancer ------------------------------------------------------------
   Un dossier envoyé et sans réponse depuis quinze jours. L'ambre le signale
   à côté du nom, et un liseré du même ton sur la carte le fait ressortir de
   la colonne sans qu'on lise quoi que ce soit. */
.colonne .dossier__nom {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}

.dossier__relance {
  padding: .0625rem .5rem;
  border-radius: 999px;
  background: var(--sable-pale);
  color: var(--sable);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.colonne .dossier--relance {
  border-color: var(--sable);
  box-shadow: inset 3px 0 0 var(--sable);
}

/* ---------------------------------------------------------------------------
   Tableau de bord — trois modes : téléphone, tablette, ordinateur

   Le même écran sert au gérant sur son ordinateur le lundi matin, au
   technicien sur sa tablette dans la camionnette, et à l'un comme à l'autre
   sur un téléphone entre deux visites. Ce n'est pas la même lecture :

     ORDINATEUR (> 64 rem) — le gérant PILOTE. L'écran s'élargit à 80 rem pour
       que cinq colonnes de statut tiennent de front, sans défilement.
     TABLETTE (43 à 64 rem) — le technicien TRAVAILLE. Les colonnes défilent
       à l'horizontale, aimantées une par une ; la répartition se met en deux.
     TÉLÉPHONE (< 43 rem) — on CONSULTE. Les colonnes s'empilent, repliables :
       cinq couloirs de 15 rem sur un écran de 23 n'en montraient qu'un et
       demi, et l'on faisait défiler à l'aveugle.

   Les seuils sont ceux du reste de l'application (43 rem pour le passage en
   une colonne des formulaires), pas des largeurs d'appareils : un iPad en
   portrait et un Chromebook tombent du bon côté sans qu'on ait à les nommer.
--------------------------------------------------------------------------- */

.screen--pilotage { max-width: 80rem; }

/* Un groupe sans valeur — pas de marque parce que pas encore chiffré — se
   distingue par le gris, quel que soit son rang. */
.ventilation__barre[data-rang="vide"] { --jauge: var(--filet); }

/* La part du chantier couverte par la marge : une ligne à part entière sous
   le montant, pas un mot collé à la pastille où il ne tenait pas. */
.colonne .dossier__couverture {
  flex-basis: 100%;
  font-size: var(--t-xs);
  color: var(--gris-texte);
}

/* --- Téléphone -------------------------------------------------------------- */
@media (max-width: 43rem) {
  .colonnes {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: 1fr;
    gap: .625rem;
    margin-inline: 0;
    padding: 0;
    overflow: visible;
    scroll-snap-type: none;
  }

  .colonne { scroll-snap-align: none; }

  /* Le résumé de la répartition prend toute la ligne sous le titre : un
     titre et un résumé de front ne tiennent pas sur 23 rem. */
  .pilotage__bascule { flex-wrap: wrap; row-gap: 0; }
  .pilotage__titre { flex: 1 1 auto; }
  .pilotage__resume { flex-basis: 100%; white-space: normal; }

  .pilotage__contenu { grid-template-columns: 1fr; }
}

/* --- Tablette --------------------------------------------------------------- */
@media (min-width: 43.01rem) and (max-width: 64rem) {
  .colonnes { grid-auto-columns: minmax(17rem, 1fr); scroll-snap-type: x mandatory; }
  .pilotage__contenu { grid-template-columns: 1fr 1fr; }
}

/* --- Ordinateur ------------------------------------------------------------- */
@media (min-width: 64.01rem) {
  .colonnes {
    grid-auto-flow: row;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    overflow: visible;
    margin-inline: 0;
    padding: 0;
  }
  /* Quand « Arbitres » n'est pas rendue -- un compte qui n'a encore rien
     arbitre -- les trois colonnes de travail se partagent la place, et
     chacune passe de 288 a 384 px sur un ecran de 1 152. */
  .colonnes:not(:has(.colonne--arbitre)) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .colonnes:has(.colonne--seule) { grid-template-columns: 1fr; }
  .pilotage__contenu { grid-template-columns: repeat(3, 1fr); }
}

/* ---------------------------------------------------------------------------
   Estimation — la ligne avec photo a une colonne de plus

   La grille de `.ligne` compte quatre colonnes : intitulé, quantité, montant,
   retrait. Quand la ligne de pose ou d'accessoires reçoit sa photo, elle a
   CINQ enfants : la photo prenait la colonne de l'intitulé (1fr), l'intitulé
   se retrouvait dans les 5,5 rem de la quantité — d'où « Forfait / main /
   d'œuvre / bisplit » empilés sur quatre lignes — et le bouton de retrait
   passait à la ligne. Une colonne `auto` s'ouvre pour la photo, et seulement
   quand elle est là : les lignes de matériel n'en ont pas.
--------------------------------------------------------------------------- */
.ligne:has(.ligne__photo) {
  grid-template-columns: auto 1fr 5.5rem auto var(--touch);
}

/* L'écart de la grille suffit ; la marge de la photo venait s'y ajouter. */
.ligne .ligne__photo { margin-right: 0; }

@media (max-width: 560px) {
  .ligne:has(.ligne__photo) {
    grid-template-columns: auto 1fr auto var(--touch);
    grid-template-areas:
      "photo intitule intitule retrait"
      "photo quantite montant  retrait";
  }
  .ligne__photo { grid-area: photo; align-self: start; }
}

/* ---------------------------------------------------------------------------
   Grille tarifaire — la désignation cède, pas les prix

   `white-space: nowrap` sur tout le tableau le faisait défiler dès la
   tablette : la colonne « Vente HT » coupée au bord, « Achat » et « Marge »
   hors champ, sans rien qui dise qu'il y avait quelque chose à droite. Les
   chiffres restent sur une ligne — un prix coupé est pire qu'un défilement —
   mais la désignation, elle, peut prendre deux lignes : « Forfait main
   d'œuvre — pompe à chaleur avec ballon déporté » se lit aussi bien.
--------------------------------------------------------------------------- */
.tableau td:first-child,
.tableau th:first-child {
  white-space: normal;
  min-width: 11rem;
}

/* ---------------------------------------------------------------------------
   Mon catalogue — refonte : des marques qu'on reconnaît, des gammes qu'on
   comprend

   L'écran précédent alignait des cases à cocher devant des codes : « KGTH ·
   12 ». Un installateur ne vend pas des codes, il vend des gammes qu'il
   présente en une phrase — « la murale R32 de 2 à 4 kW ». C'est cette phrase
   qu'on écrit sous chaque nom, tirée des références elles-mêmes.

   Trois niveaux de lecture, du plus large au plus fin :
     la BARRE — quel type d'installation, combien de références, quel état ;
     la MARQUE — logo, nombre de gammes, formes vendues, un interrupteur ;
     la GAMME — nom, portrait, nombre, une case.

   Les anciennes classes `.marque-bloc` et `.gamme-case` restent au-dessus
   pour l'historique ; plus aucun gabarit ne les utilise.
--------------------------------------------------------------------------- */

.catalogue__intro {
  max-width: 40rem;
  color: var(--gris-texte);
}

/* --- La barre ---------------------------------------------------------------- */

.catalogue__barre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem 1.5rem;
  margin-top: 1.25rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--filet);
}

.catalogue__barre .filtres-marques { margin: 0; }

.catalogue__compte {
  margin: 0;
  font-size: var(--t-md);
  color: var(--gris-texte);
  font-variant-numeric: tabular-nums;
}

.catalogue__compte strong {
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  color: var(--encre);
}

.catalogue__compte-sep { margin: 0 .5rem; }

/* L'état se lit en couleur avant de se lire en mots : ambre tant que tout
   est proposé — ce n'est pas une erreur, mais ce n'est pas un réglage —,
   vert dès qu'une sélection existe. */
.catalogue__etat--tout { color: var(--sable); }
.catalogue__etat--choisi { color: var(--action-fonce); font-weight: 600; }

/* --- La marque --------------------------------------------------------------- */

.catalogue-marques {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: .625rem;
}

.catalogue-marque {
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

/* Une marque déployée se signale par un filet vert à gauche, pas par un fond :
   quinze cartes vert pâle feraient un écran illisible. */
.catalogue-marque--deployee { box-shadow: inset 3px 0 0 var(--action); }

.catalogue-marque__tete {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding-inline-end: 1rem;
}

.catalogue-marque__ouvrir {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 4.25rem;
  padding: .75rem 1rem;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}

/* Le logo dans une boîte fixe : les logos ont tous des proportions
   différentes, et sans boîte commune le texte à leur droite partait à une
   abscisse différente sur chaque ligne. */
.catalogue-marque__identite {
  flex: 0 0 8rem;
  height: 2.25rem;
  display: flex;
  align-items: center;
}

.catalogue-marque__identite .marque-visuel { justify-content: flex-start; }

.catalogue-marque__logo {
  max-height: 2.25rem;
  max-width: 8rem;
  object-fit: contain;
  object-position: left center;
}

.catalogue-marque__nom {
  font-family: var(--font-titre);
  font-weight: 700;
  font-size: var(--t-body);
}

.catalogue-marque__resume {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: .125rem;
}

.catalogue-marque__chiffres {
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--encre);
  font-variant-numeric: tabular-nums;
}

/* Les formes vendues, en un mot chacune : « murale · gainable · cassette ».
   C'est ce qu'un installateur demande en premier d'une marque qu'il ne
   connaît pas. */
.catalogue-marque__formes {
  font-size: var(--t-sm);
  color: var(--gris-texte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.catalogue-marque__chevron {
  flex: none;
  font-size: var(--t-xs);
  color: var(--gris-texte);
}

.catalogue-marque__deploiement {
  flex: none;
  display: flex;
  align-items: center;
  gap: .625rem;
}

.catalogue-marque__partiel {
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--sable-pale);
  color: var(--sable);
  font-size: var(--t-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.catalogue-marque__attente {
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

/* --- L'interrupteur ---------------------------------------------------------
   Un `role="switch"` dessiné à la main : 48 px de haut pour le doigt, 2,75 rem
   de large pour l'œil. Trois états — coché, décoché, mixte —, le mixte avec le
   curseur au milieu et la piste ambre : une marque dont on ne vend que deux
   gammes sur cinq n'est ni « oui » ni « non ». */
.interrupteur {
  position: relative;
  flex: none;
  width: 2.75rem;
  height: 1.625rem;
  margin-block: calc((var(--touch) - 1.625rem) / 2);
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgb(11 20 34 / 0.18);
  cursor: pointer;
  transition: background .15s ease;
}

/* La cible touchable déborde du dessin, sans qu'on la voie. */
.interrupteur::before {
  content: "";
  position: absolute;
  inset: calc((1.625rem - var(--touch)) / 2) -.5rem;
}

.interrupteur__curseur {
  position: absolute;
  top: .1875rem;
  left: .1875rem;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgb(11 20 34 / 0.3);
  transition: transform .15s ease;
}

.interrupteur[aria-checked="true"] { background: var(--action); }
.interrupteur[aria-checked="true"] .interrupteur__curseur { transform: translateX(1.125rem); }

.interrupteur[aria-checked="mixed"] { background: var(--sable); }
.interrupteur[aria-checked="mixed"] .interrupteur__curseur { transform: translateX(.5625rem); }

.interrupteur:disabled { cursor: not-allowed; opacity: .5; }

.interrupteur:focus-visible {
  outline: 3px solid var(--action);
  outline-offset: 2px;
}

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

/* --- Les gammes -------------------------------------------------------------- */

.catalogue-gammes {
  list-style: none;
  margin: 0;
  padding: .75rem 1rem 1rem;
  border-top: 1px solid var(--filet);
  background: var(--fond-clair);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: .5rem;
}

.catalogue-gamme {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  min-height: var(--touch);
  padding: .75rem .875rem;
  margin: 0;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
}

.catalogue-gamme--retenue { border-color: var(--action); }
.catalogue-gamme--sans { cursor: default; background: none; border-style: dashed; }

.catalogue-gamme__case { margin-top: .125rem; }

.catalogue-gamme__corps {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: .125rem;
}

.catalogue-gamme__nom {
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Le portrait : la phrase qu'on dit en présentant la gamme. */
.catalogue-gamme__portrait {
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

.catalogue-gamme__nombre {
  flex: 0 0 auto;
  min-width: 2rem;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--fond-clair);
  font-size: var(--t-xs);
  font-weight: 700;
  text-align: center;
  color: var(--gris-texte);
  font-variant-numeric: tabular-nums;
}

.catalogue-gamme--retenue .catalogue-gamme__nombre { background: var(--action-pale); color: var(--action-fonce); }

/* --- Téléphone --------------------------------------------------------------- */
@media (max-width: 43rem) {
  .catalogue-marque__ouvrir { gap: .75rem; padding-inline: .875rem; }
  .catalogue-marque__identite { flex-basis: 6rem; }
  .catalogue-marque__logo { max-width: 6rem; }
  /* Les formes passent sous le compte, sur deux lignes si besoin : les couper
     à la première ferait disparaître « cassette » derrière des points. */
  .catalogue-marque__formes { white-space: normal; }
  .catalogue-gammes { grid-template-columns: 1fr; padding-inline: .75rem; }
}

/* Deux règles générales des boutons contrariaient la refonte, mesurées :
   `justify-content: center` posait le logo à 162 px du bord au lieu de 16, et
   `min-height: var(--touch)` faisait de l'interrupteur un galet de 44 × 48. */
.catalogue-marque__ouvrir { justify-content: flex-start; }
button.interrupteur { min-height: 0; height: 1.625rem; }

/* Téléphone : le logo et l'interrupteur sur la première ligne, le résumé
   dessous sur toute la largeur. De front, « 24 gammes · 254 références » se
   cassait en trois lignes à côté d'un logo de 6 rem. */
@media (max-width: 43rem) {
  .catalogue-marque__ouvrir { flex-wrap: wrap; row-gap: .25rem; }
  .catalogue-marque__identite { flex: 1 1 auto; }
  .catalogue-marque__resume { flex-basis: 100%; order: 3; }
  .catalogue-marque__chevron { order: 2; font-size: var(--t-sm); }
}

/* ---------------------------------------------------------------------------
   Champ d'adresse — suggestions BAN

   La liste flotte SOUS le champ, par-dessus ce qui suit, au lieu de pousser
   le formulaire : un formulaire qui saute de cinq lignes à chaque frappe fait
   perdre le doigt au technicien. Le conteneur est en `position: relative`
   pour que la liste s'aligne sur le champ et non sur la carte.

   Chaque option est un bouton pleine largeur d'au moins --touch : c'est un
   pouce qui choisit, debout, souvent avec la tablette dans l'autre main. Le
   libellé BAN en gras, le « code postal ville » en dessous et en gris : la
   commune est ce qu'on vérifie d'un coup d'œil, la rue ce qu'on lit.

   L'état désigné au clavier est porté par `aria-selected` sur l'option, pas
   par une classe : ce que le lecteur d'écran annonce et ce que l'écran
   montre ne peuvent pas diverger. Aucune animation : la liste apparaît, point.
   Une transition sur un menu qui se rouvre à chaque frappe n'ajoute que du
   retard.
--------------------------------------------------------------------------- */

.champ-adresse { position: relative; }

.suggestions {
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  margin: .25rem 0 0;
  padding: .25rem 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  box-shadow: var(--ombre-portee);
  /* Cinq options de deux lignes tiennent dans 22 rem ; au-delà (libellés
     longs qui passent à la ligne), la liste défile plutôt que de disparaître
     sous la barre de navigation du bas. */
  max-height: 22rem;
  overflow-y: auto;
}

.suggestions li + li { border-top: 1px solid var(--filet); }

.suggestion {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: .125rem;
  width: 100%;
  min-height: var(--touch);
  margin: 0;
  padding: .625rem 1rem;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--encre);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

/* Le fond désigne l'option : au clavier (aria-selected), au doigt (:active),
   ou à la souris — le survol reste un complément, jamais le seul indice. */
.suggestion[aria-selected="true"],
.suggestion:hover,
.suggestion:active,
.suggestion:focus-visible {
  background: var(--action-pale);
  outline: none;
}

.suggestion__libelle {
  font-weight: 600;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

.suggestion__commune {
  font-size: var(--t-sm);
  line-height: 1.35;
  color: var(--gris-texte);
}

/* L'adresse reconnue se signale en vert : c'est une bonne nouvelle, pas une
   aide ordinaire. */
.champ-adresse__reconnue { color: var(--action-fonce); }

/* Téléphone : la liste prend toute la largeur du champ, déjà pleine largeur ;
   on serre seulement le rembourrage pour que « 12 rue de la République »
   tienne sur une ligne. */
@media (max-width: 43rem) {
  .suggestion { padding-inline: .875rem; }
}

/* ---------------------------------------------------------------------------
   Matériel PAC — les références écartées se montrent, avec leur motif

   Mesuré sur un chantier de 5,3 kW : six Altherma 4 H au catalogue, toutes
   écartées « surdimensionné : 8 kW », et l'écran disait « aucune référence ».
   Une machine écartée n'est pas un bouton : on ne la choisit pas, on lit
   pourquoi elle ne convient pas — même dessin que les gammes écartées.
--------------------------------------------------------------------------- */

.references--ecartees .reference--ecartee {
  cursor: default;
  border-style: dashed;
  background: var(--fond-clair);
  color: var(--gris-texte);
}

.references--ecartees .reference__nom { color: var(--encre); font-weight: 600; }

.reference__motif {
  flex: 1 1 100%;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

/* ---------------------------------------------------------------------------
   Matériel PAC — la puissance comparée se dit, et à quelle température

   Le besoin est calculé à la température de base (−9,5 °C en H1 plaine), et le
   filtre comparait la puissance nominale à +7 °C : sur un chantier de 5,3 kW,
   six Altherma 4 H écartées « surdimensionné : 8 kW » alors qu'une 8 kW
   nominale donne 5 à 6 kW à −7 °C. Mesuré sur 210 machines, la puissance à
   −7 °C n'est connue que pour 97 (D-087). La carte dit donc sur QUOI la machine
   a été retenue, et ce qu'on ignore.

   Une réserve n'est pas une alerte : la machine reste proposable. D'où le sable
   foncé, jamais le rouge — même logique que .gamme-carte__reserve.
--------------------------------------------------------------------------- */

/* Ligne secondaire sous nom · nominale · prix : elle prend toute la largeur
   pour ne pas se glisser entre la puissance et le prix. */
.reference__froid {
  flex: 1 1 100%;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

.reference__reserve {
  flex: 1 1 100%;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--sable-fonce);
  font-style: italic;
}

/* Le taux de couverture est le chiffre que le technicien cherche : en gras,
   dans la même encre que la ligne pour rester une précision, pas un titre. */
.reference__couverture {
  font-weight: 700;
  color: var(--encre);
}

/* L'aide ferme la liste des références, dans la carte : pas de creux sous
   elle, la carte a déjà son rembourrage. */
.reference__aide {
  margin: .75rem 0 0;
  font-size: var(--t-sm);
}

/* ---------------------------------------------------------------------------
   Provenance — d'où vient une valeur

   Observé chez kelvin° (D-088) : chaque champ prérempli porte un petit badge
   « DPE » ou « IA » à droite du libellé. C'est ce badge qui autorise un
   préremplissage massif sans perdre la confiance du pro. Sooluo préremplit
   déjà quatre valeurs (adresse BAN, altitude IGN, zone déduite du code postal,
   hauteur par défaut) sans le montrer autrement que par des phrases d'aide
   dispersées sous les champs.

   Une étiquette, pas un bouton : elle se lit, elle ne se touche pas. Elle vit
   dans le label — un appui dessus focalise le champ. Les couleurs reprennent
   les zones du logo : bleu pour ce qui vient d'un service public (BAN, IGN),
   sable pour ce qui est déduit ou repris d'un diagnostic, violet pour le
   catalogue, gris pour une valeur par défaut, la moins fiable de toutes.
--------------------------------------------------------------------------- */

/* Le label devient une ligne : libellé à gauche, étiquette à droite. Seuls
   les labels qui portent une provenance reçoivent la classe, les autres
   restent des blocs — aucun champ existant ne bouge. */
.field__label--avec-provenance {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem;
}

.provenance {
  display: inline-block;
  flex: none;
  margin-inline-start: .5rem;
  padding: 0 .375rem;
  border-radius: 999px;
  font-size: var(--t-xs);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: .04em;
  text-transform: uppercase;
  vertical-align: middle;
  white-space: nowrap;
  background: var(--fond-clair);
  color: var(--gris-texte);
}

/* Dans une ligne flex, le gap fait déjà l'écart : pas de marge en double. */
.field__label--avec-provenance .provenance { margin-inline-start: 0; }

/* Bleu zone : ce qui vient d'un service public (BAN, IGN). */
.provenance--ban,
.provenance--ign {
  background: rgb(29 110 133 / .12);
  color: var(--info);
}

/* Sable pâle : déduit, ou repris d'un DPE — vrai le plus souvent, à vérifier. */
.provenance--deduit,
.provenance--dpe {
  background: var(--sable-pale);
  color: var(--sable-fonce);
}

/* Gris : valeur par défaut, faute de relevé — la moins fiable, la plus discrète. */
.provenance--defaut {
  background: var(--fond-clair);
  color: var(--gris-texte);
}

/* Violet zone : donnée du catalogue constructeur ou distributeur. */
.provenance--catalogue {
  background: var(--violet-pale);
  color: var(--violet);
}

/* Variante compacte, pour un récapitulatif où le badge suit une valeur en
   ligne plutôt qu'un libellé de champ. */
.provenance--compact {
  font-size: var(--t-xs);
  padding: 0 .3125rem;
}

/* ---------------------------------------------------------------------------
   DPE — le diagnostic rattaché au chantier (D-089)

   UNE AIDE EN TÊTE DU RELEVÉ, PAS L'ÉCRAN PRINCIPAL. La carte reprend le
   fond clair et le filet des blocs secondaires ; seule la pastille de
   l'étiquette est colorée, aux teintes officielles atténuées pour ne pas
   crier au-dessus des champs. Les candidats sont des boutons pleine largeur
   à la hauteur tactile du dépôt : on choisit un logement d'un pouce, debout.
--------------------------------------------------------------------------- */

.dpe-carte {
  margin: 0 0 1.5rem;
  padding: .875rem 1rem 1rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--fond-clair);
}

.dpe-carte__titre {
  margin: 0 0 .625rem;
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gris-texte);
}

.dpe-carte__entete {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: .5rem;
}

.dpe-carte__texte { min-width: 0; }

.dpe-carte__resume {
  margin: 0;
  font-weight: 600;
}

.dpe-carte__champs {
  margin: 0 0 .375rem;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.dpe-carte__question {
  margin: 0 0 .625rem;
  font-weight: 600;
}

.dpe-carte .button { margin-top: .5rem; }

/* La lettre, carrée, blanche sur la couleur officielle atténuée. */
.dpe-etiquette {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: var(--gris-texte);
}

.dpe-etiquette--A { background: var(--dpe-a); }
.dpe-etiquette--B { background: var(--dpe-b); }
.dpe-etiquette--C { background: var(--dpe-c); }
.dpe-etiquette--D { background: var(--dpe-d); }
.dpe-etiquette--E { background: var(--dpe-e); }
.dpe-etiquette--F { background: var(--dpe-f); }
.dpe-etiquette--G { background: var(--dpe-g); }
.dpe-etiquette--inconnue { background: var(--gris-texte); }

.dpe-candidats {
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

.dpe-candidat {
  display: flex;
  align-items: center;
  gap: .75rem;
  width: 100%;
  min-height: var(--touch);
  padding: .5rem .75rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.dpe-candidat:active { background: var(--fond-clair); }

/* Le numéro et son bouton sur une ligne ; le bouton reste à hauteur du champ. */
.dpe-recherche {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
}

.dpe-recherche .field {
  flex: 1;
  min-width: 0;
  margin-bottom: 0;
}

/* Le bouton s'aligne sur le champ, sous son libellé : la hauteur du label
   plus son écart, mesurés sur .field__label. */
.dpe-recherche .button {
  flex: none;
  margin-top: 1.9rem;
}

@media (max-width: 560px) {
  .dpe-recherche { flex-direction: column; align-items: stretch; }
  .dpe-recherche .button { margin-top: .25rem; }
}

/* L'adresse sur laquelle un DPE candidat a été trouvé, sous son libellé : le
   géocodage est flou, et c'est là que le technicien voit qu'on lui parle
   d'une autre rue. */
.dpe-candidat__adresse {
  display: block;
  font-size: var(--t-xs);
  color: var(--gris-texte);
}

/* ---------------------------------------------------------------------------
   Comparateur de gammes — deux ou trois colonnes, et une note qu'on démonte

   La colonne est l'unité de lecture : on lit une gamme de haut en bas, puis on
   passe à la suivante. Sous 720 px, elles s'empilent — comparer en défilant
   horizontalement sur une tablette tenue debout ne marche pas.
--------------------------------------------------------------------------- */

.comparateur__aide { margin-bottom: var(--e-4); }

.comparateur__curseurs { margin-top: var(--e-4); }

.critere + .critere { margin-top: var(--e-5); }

.critere__tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e-3);
}

.critere__libelle { font-weight: 600; }

.critere__part {
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}

/* Le curseur garde la cible tactile de 48 px : il se règle avec un pouce. */
.critere__curseur {
  width: 100%;
  margin-top: var(--e-2);
  min-height: var(--touch);
  accent-color: var(--action);
}

.critere__aide {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--ink-faible);
}

.comparateur__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-3);
  margin-top: var(--e-5);
}

.comparateur__gammes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: var(--e-3);
  margin: var(--e-4) 0 0;
  padding: 0;
  list-style: none;
}

.comparateur__gamme {
  display: grid;
  gap: var(--e-1);
  width: 100%;
  min-height: var(--touch-lg);
  padding: var(--e-3);
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink);
}

/* Retenue : un filet épais, pas une teinte de fond — le logo de la marque doit
   rester lisible sur du blanc. */
.comparateur__gamme--retenue {
  border-color: var(--action);
  box-shadow: inset 0 0 0 1px var(--action);
}

.comparateur__gamme[disabled] { opacity: .45; }

.comparateur__gamme-nom { font-weight: 600; }

.comparateur__gamme-detail {
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.comparateur__verdict {
  margin: 0 0 var(--e-5);
  padding: var(--e-4);
  background: var(--fond-clair);
  border-left: 3px solid var(--action);
  border-radius: var(--radius);
  font-size: var(--t-lg);
}

/* Aucune gagnante : le filet perd sa couleur d'action, parce qu'il n'y a rien
   à retenir. */
.comparateur__verdict--nul {
  border-left-color: var(--filet-fort);
  font-size: var(--t-body);
  color: var(--ink-muted);
}

.comparateur__colonnes {
  display: grid;
  grid-template-columns: repeat(var(--colonnes, 2), minmax(0, 1fr));
  gap: var(--e-4);
}

@media (max-width: 720px) {
  .comparateur__colonnes { grid-template-columns: 1fr; }
}

.comparateur__colonne {
  padding: var(--e-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.comparateur__colonne--tete { border-color: var(--action); }

.comparateur__entete { margin-bottom: var(--e-4); }

.comparateur__entete h3 {
  margin: var(--e-2) 0 0;
  font-size: var(--t-body);
}

.comparateur__note {
  margin: var(--e-2) 0 0;
  font-family: var(--font-titre);
  letter-spacing: var(--serrage-titre);
  font-variant-numeric: tabular-nums;
}

.comparateur__note strong {
  font-size: var(--t-2xl);
  font-weight: 600;
  line-height: 1;
}

.comparateur__note span {
  font-size: var(--t-md);
  color: var(--ink-muted);
}

.comparateur__lignes {
  margin: 0;
  padding: 0;
  list-style: none;
}

.comparateur__ligne {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0 var(--e-3);
  padding: var(--e-2) 0;
  border-top: 1px solid var(--filet);
}

.comparateur__ligne--hors { opacity: .5; }

.comparateur__critere { font-size: var(--t-sm); }

.comparateur__valeur {
  font-size: var(--t-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* La jauge est la note du critère sur cent, rapportée au marché — pas à la
   gamme d'en face. Elle occupe toute la largeur sous le libellé. */
.comparateur__jauge {
  grid-column: 1 / -1;
  height: 4px;
  margin-top: var(--e-1);
  background: var(--filet);
  border-radius: 999px;
  overflow: hidden;
}

.comparateur__jauge-remplie {
  display: block;
  height: 100%;
  background: var(--serie-1);
  border-radius: inherit;
}

.comparateur__apport {
  grid-column: 1 / -1;
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faible);
}

.comparateur__apport--absent { font-style: italic; }

.comparateur__points {
  margin-top: var(--e-4);
  padding: var(--e-3);
  border-radius: var(--radius-sm);
  font-size: var(--t-sm);
}

.comparateur__points ul { margin: var(--e-2) 0 0; padding-left: 1.1rem; }
.comparateur__points li + li { margin-top: var(--e-1); }

.comparateur__points-titre {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  margin: 0;
  font-weight: 600;
}

/* Le vert ne dit que le positif, l'ambre que la réserve : la règle de D-095
   vaut ici comme ailleurs. */
.comparateur__points--forts {
  background: var(--vert-pale);
  color: var(--vert-fonce);
}

.comparateur__points--faibles {
  background: var(--sable-pale);
  color: var(--sable-fonce);
}

.comparateur__manquants,
.comparateur__mention {
  margin-top: var(--e-4);
  font-size: var(--t-sm);
  color: var(--ink-faible);
}

/* --- Comparateur : ce que le design system ne fournit pas déjà ------------- */

.comparateur__reglage { margin-top: var(--e-5); }

.comparateur__recherche {
  width: 100%;
  margin-top: var(--e-3);
}

/* LE LOGO D'UNE MARQUE N'EST PAS UNE IMAGE COMME UNE AUTRE.
   `.marque__logo` occupe toute la largeur de son conteneur et se centre : c'est
   ce qu'il faut dans une grille de marques, où chaque case ne contient que lui.
   Ici il coiffe un nom de gamme, aligné à gauche : il doit être petit, calé à
   gauche, et de HAUTEUR CONSTANTE d'une marque à l'autre — sinon les cartes
   ondulent, et un logo large comme Mitsubishi Heavy Industries écrase un logo
   carré comme Bosch. La largeur est libre, la hauteur ne l'est pas. */
.comparateur__gamme .marque__logo,
.comparateur__entete .marque__logo {
  width: auto;
  max-width: 7.5rem;
  height: 1.5rem;
  object-position: left center;
}

.comparateur__gamme .marque__nom,
.comparateur__entete .marque__nom {
  display: block;
  text-align: left;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--ink-muted);
  /* Même hauteur que le logo : une carte à nom et une carte à logo doivent
     s'aligner sur la même ligne de base. */
  line-height: 1.5rem;
}

/* Sous 560 px, une gamme par ligne : deux colonnes de 10 rem tronqueraient les
   noms longs, et « Wellea Mono / Split R32 » ne se devine pas. */
@media (max-width: 560px) {
  .comparateur__gammes { grid-template-columns: 1fr; }
}

/* Le verdict est ce qu'on lit en premier : il ne rétrécit pas sur mobile, il
   perd seulement sa marge. */
@media (max-width: 560px) {
  .comparateur__verdict { padding: var(--e-3); }
  .comparateur__colonne { padding: var(--e-3); }
}

/* SIX ONGLETS SOUS 400 PX.
   La barre en portait cinq ; le comparateur en fait un sixième. À 375 px, six
   cibles de 48 px ne tiennent qu'en supprimant le libellé — la règle existait
   déjà sous 380 px pour le bouton du mode client, elle vaut ici aussi. Les
   icônes sont dessinées pour rester lisibles seules (D-094). */
@media (max-width: 400px) {
  .onglet__libelle { font-size: var(--t-xs); }
  .onglet { padding-inline: .25rem; }
}

/* --- Comparateur : le choix, par marque puis par gamme -------------------- */

/* Les gammes déjà retenues, hors des groupes : replier une marque ne doit pas
   faire disparaître un choix fait. */
.comparateur__retenues {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin: var(--e-4) 0 0;
  padding: 0;
  list-style: none;
}

.comparateur__retenue {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  min-height: var(--touch);
  padding: 0 var(--e-3);
  background: var(--action-pale);
  border: 1px solid var(--action);
  border-radius: 999px;
  color: var(--action-fonce);
  font-size: var(--t-sm);
}

.comparateur__retenue-croix {
  font-size: var(--t-lg);
  line-height: 1;
}

.comparateur__marques {
  margin: var(--e-4) 0 0;
  padding: 0;
  list-style: none;
}

.comparateur__marque {
  border-top: 1px solid var(--filet);
}

.comparateur__marque:last-child { border-bottom: 1px solid var(--filet); }

/* LA TÊTE DE MARQUE EST UNE GRILLE, PAS UNE RANGÉE SOUPLE.
   En flex, chaque marque calait son logo où sa largeur naturelle le menait :
   « Airwell » se retrouvait au milieu de la ligne, « Ariston » — qui n a pas
   de fichier et s affiche en toutes lettres — restait à gauche, et la colonne
   des noms dansait d une marque à l autre. Une largeur FIXE pour le logo aligne
   les trente-deux marques sur une même verticale, quelle que soit la forme de
   leur logotype. */
.comparateur__marque-tete {
  display: grid;
  grid-template-columns: 7.5rem 1fr auto;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  min-height: var(--touch);
  padding: var(--e-3) 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

/* Une recherche déplie tout : la tête n'est alors plus un bouton utile, mais
   elle reste lisible — un contrôle grisé qui garde son contraste. */
.comparateur__marque-tete[disabled] { cursor: default; opacity: 1; }

.comparateur__marque-tete .marque__logo {
  justify-self: start;
  width: auto;
  max-width: 100%;
  height: 1.5rem;
  object-fit: contain;
  object-position: left center;
}

.comparateur__marque-tete .marque__nom {
  justify-self: start;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
  text-align: left;
  font-size: var(--t-body);
  font-weight: 600;
  color: var(--ink);
  line-height: 1.5rem;
}

.comparateur__marque-compte {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  white-space: nowrap;
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

.comparateur__marque-retenues {
  padding: .0625rem .5rem;
  border-radius: 999px;
  background: var(--action-pale);
  color: var(--action-fonce);
  font-size: var(--t-xs);
  font-weight: 600;
}

.comparateur__chevron {
  flex: none;
  color: var(--ink-faible);
  transition: transform 160ms ease;
}

.comparateur__chevron--ouvert { transform: rotate(180deg); }

/* Les gammes d'une marque : décalées sous leur tête, jamais en pleine largeur,
   pour qu'on voie d'un coup d'œil à qui elles appartiennent. */
.comparateur__marque .comparateur__gammes {
  margin: 0 0 var(--e-3) 0;
  padding-left: var(--e-4);
  border-left: 2px solid var(--filet);
}

@media (prefers-reduced-motion: reduce) {
  .comparateur__chevron { transition: none; }
}

/* --- Comparateur : ce que le premier essai a montré ------------------------ */

/* L'invitation occupe la place du verdict, pour qu'on sache où il paraîtra. */
.comparateur__invite-titre {
  margin: 0 0 var(--e-2);
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
}

/* SOUS 480 PX, LE LOGO CÈDE SA COLONNE.
   Une case de 7,5 rem sur un écran de 420 px mange plus du tiers de la ligne
   et pousse « 2 gammes » à la ligne suivante. La colonne se réduit ; les logos
   restent alignés entre eux, simplement plus petits. */
@media (max-width: 480px) {
  .comparateur__marque-tete { grid-template-columns: 5.5rem 1fr auto; }
  .comparateur__marque-tete .marque__logo { height: 1.25rem; }
  .comparateur__marque-tete .marque__nom { font-size: var(--t-md); }
}

/* L'entête du réglage : titre et résumé sur une ligne chacun sous 560 px.
   Empilés, ils cessent de se disputer la largeur et le résumé n'est plus
   tronqué en « poids par déf… ». */
@media (max-width: 560px) {
  .comparateur__reglage .bloc-pliant__entete {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    row-gap: 0;
  }
  .comparateur__reglage .bloc-pliant__titre { grid-column: 1; }
  .comparateur__reglage .bloc-pliant__resume {
    grid-column: 1;
    grid-row: 2;
    white-space: normal;
  }
  .comparateur__reglage .icone {
    grid-column: 2;
    grid-row: 1 / span 2;
  }
}

/* LE CONTENEUR DU LOGO CENTRE, ET C'EST LUI QU'IL FAUT REPRENDRE.
   `MarqueLogo` enveloppe son image dans `.marque-visuel`, qui centre en flex
   sur toute la largeur : caler l'image à gauche avec `object-position` ne
   servait à rien, elle restait au milieu d'une boîte centrée. Partout où la
   marque coiffe du texte aligné à gauche — la tête d'une marque, l'en-tête
   d'une colonne de comparaison — c'est le conteneur qui doit cesser de
   centrer. */
.comparateur__marque-tete .marque-visuel,
.comparateur__entete .marque-visuel {
  justify-content: flex-start;
}

/* Dans une colonne de résultat, le logo coiffe le nom de la gamme et la note :
   il annonce la marque, il ne fait pas la vedette. */
.comparateur__entete .marque__logo {
  height: 1.5rem;
  width: auto;
  max-width: 60%;
}

/* La cible tactile d'une pastille du parcours : 44 px minimum, sans changer
   le dessin ni la place qu'elle prend. */
.parcours__lien::before {
  content: '';
  position: absolute;
  inset: -0.5rem -0.25rem;
}

/* --- À faire aujourd'hui ------------------------------------------------- */

/* Le premier bloc du tableau de bord quand un geste attend. Chaque ligne est
   une cible pleine largeur : on la touche, on arrive sur la remise du dossier
   avec le téléphone du client sous les yeux. */
.aujourdhui__titre {
  margin: 0 0 var(--e-3);
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
}

.aujourdhui__liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.aujourdhui__tache {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--e-3);
  align-items: baseline;
  min-height: var(--touch);
  padding: var(--e-2) 0 var(--e-2) var(--e-3);
  border-left: 3px solid var(--filet);
  color: inherit;
  text-decoration: none;
}

.aujourdhui__liste li + li .aujourdhui__tache { border-top: 1px solid var(--filet); }

/* Le verbe porte la couleur du geste : un rappel qui attend est un signal
   d'attention, un envoi qui attend est une action. */
.aujourdhui__verbe {
  font-weight: 600;
  font-size: var(--t-md);
}
.aujourdhui__tache--relance { border-left-color: var(--sable); }
.aujourdhui__tache--relance .aujourdhui__verbe { color: var(--sable-fonce); }
.aujourdhui__tache--envoi { border-left-color: var(--action); }
.aujourdhui__tache--envoi .aujourdhui__verbe { color: var(--action); }

.aujourdhui__qui {
  font-size: var(--t-body);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.aujourdhui__quand {
  grid-column: 2;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

/* --- Premiers pas d'un compte neuf ---------------------------------------- */

.premiers-pas {
  list-style: none;
  margin: var(--e-4) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--e-2);
}

.premiers-pas__lien {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  min-height: var(--touch);
  padding: var(--e-2) var(--e-3);
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  color: inherit;
  text-decoration: none;
  background: var(--surface);
}

.premiers-pas__numero {
  flex: none;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--action-pale);
  color: var(--action);
  font-family: var(--font-titre);
  font-weight: 600;
}

.premiers-pas__texte {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  font-size: var(--t-md);
}

/* Une ligne d'estimation imposée par le constructeur : le mot le dit, et le
   bouton de retrait reste grisé. */
.ligne__impose {
  align-self: center;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--action-pale);
  color: var(--action);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* --- Zone d'action irréversible ------------------------------------------ */

/* UNE CARTE QUI SE DISTINGUE SANS CRIER.
   Elle porte le seul geste que rien ne rattrape. Un liseré rouge à gauche
   suffit à la séparer des autres cartes ; un fond rouge ferait de la
   suppression d'un dossier d'essai un événement dramatique, ce qu'elle n'est
   pas. La gravité est dans le texte et dans les deux temps du bouton. */
.card--danger {
  border-left: 3px solid var(--danger);
}

.card--danger .form-section__title {
  color: var(--danger);
}

/* Le bouton rouge n'apparaît qu'au SECOND temps, quand la confirmation est
   armée : avant, le geste se propose en secondaire, comme tout le reste. */
.button--danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #ffffff;
}

.button--danger:active {
  background: #8f1a13;
  border-color: #8f1a13;
}

/* Un texte de loi cité mot pour mot — celui qui figure sur le document remis
   au client. Il se lit comme une citation, pas comme une consigne : on le
   reconnaît quand on le retrouve imprimé. */
.citation-legale {
  margin: var(--e-3) 0;
  padding: var(--e-3);
  border-left: 3px solid var(--filet);
  background: var(--fond-clair);
  font-size: var(--t-sm);
  line-height: 1.55;
  color: var(--gris-texte);
}

/* Un prix qui n'est pas un tarif.
   Il se lit, il n'alerte pas : le sable des réserves, en pointillé sous le
   montant. Le mot « env. » porte l'essentiel ; le trait dit qu'il y a une
   explication à survoler ou à toucher. */
.prix-estime {
  border-bottom: 1px dotted var(--sable);
  color: var(--sable-fonce);
  cursor: help;
}

/* --- Une pièce déjà servie, repliée --------------------------------------- */

/* Ce qui est fait s'efface sans disparaître : le fond reste blanc comme les
   autres cartes, mais la bordure gauche verte dit que c'est réglé, et la
   hauteur tombe d'une liste entière à une ligne. */
.piece-materiel--servie {
  border-left: 3px solid var(--vert);
  padding-block: var(--e-2);
}

.piece-servie {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--e-3);
  min-height: var(--touch);
}

.piece-servie__coche {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--vert);
  color: #ffffff;
  font-size: var(--t-sm);
  font-weight: 700;
}

.piece-servie__texte {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  min-width: 0;
}

.piece-servie__nom {
  font-size: var(--t-body);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.piece-servie__unite {
  font-size: var(--t-sm);
  color: var(--gris-texte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Sous 480 px, le bouton passe sous le texte plutôt que de l'écraser. */
@media (max-width: 480px) {
  .piece-servie {
    grid-template-columns: auto 1fr;
    row-gap: var(--e-2);
  }
  .piece-servie .button { grid-column: 2; justify-self: start; }
}

/* --- Ce qu'il reste à régler ---------------------------------------------- */

/* Le panneau qui ouvre le tableau de bord tant qu'un réglage manque.

   IL SE DISTINGUE DES DOSSIERS SANS CRIER. Un bandeau rouge pleine largeur
   serait lu une fois puis ignoré — et il siégerait sur l'écran d'accueil
   pendant des semaines. Un filet épais sur le bord gauche suffit à le sortir
   du flux : c'est la même grammaire que « À faire aujourd'hui », un cran
   au-dessus.

   La couleur vient du point le plus grave, pas du panneau : un compte dont il
   ne reste que des points de confort n'a pas à voir de l'orange. */
.preparation {
  margin-top: var(--e-5);
  padding: var(--e-4) var(--e-5);
  background: var(--surface);
  border: 1px solid var(--filet);
  border-left: 4px solid var(--sable);
  border-radius: var(--radius);
  box-shadow: var(--ombre-douce);
}

.preparation__entete { margin-bottom: var(--e-4); }

.preparation__titre {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
  text-wrap: balance;
}

.preparation__resume {
  margin: var(--e-1) 0 0;
  font-size: var(--t-md);
  color: var(--gris-texte);
}

.preparation__liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

/* Le texte à gauche, l'action à droite, alignée en haut : sur trois lignes
   d'explication, un bouton centré verticalement flotte au milieu de nulle
   part. */
.preparation__point {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--e-3);
  align-items: start;
  padding: var(--e-3) 0;
}

.preparation__point + .preparation__point { border-top: 1px solid var(--filet); }

.preparation__point-titre {
  margin: 0;
  font-size: var(--t-body);
  font-weight: 600;
}

/* La pastille qui dit la gravité. Un carré plein de 8 px avant le titre : il
   se lit en périphérie, sans obliger à lire le texte pour trier. */
.preparation__point-titre::before {
  content: "";
  display: inline-block;
  width: .5rem;
  height: .5rem;
  margin-right: var(--e-2);
  border-radius: 2px;
  vertical-align: .05em;
  background: var(--filet-fort);
}
.preparation__point--bloquant .preparation__point-titre::before { background: var(--danger); }
.preparation__point--important .preparation__point-titre::before { background: var(--sable); }

.preparation__effet {
  margin: var(--e-1) 0 0;
  font-size: var(--t-md);
  color: var(--gris-texte);
  max-width: 58ch;
}

.preparation__action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch);
  padding: 0 var(--e-4);
  border: 1px solid var(--action);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--action);
  font-size: var(--t-md);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.preparation__action:hover { background: var(--action-pale); }

.preparation__plus {
  margin-top: var(--e-3);
  min-height: var(--touch);
  padding: 0 var(--e-3);
  border: 0;
  background: none;
  color: var(--action);
  font-size: var(--t-md);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

/* Sur un téléphone, l'action passe sous son texte et prend toute la largeur :
   un bouton de 9 rem à droite d'un paragraphe de 3 lignes ne laisse plus de
   place au paragraphe. */
@media (max-width: 560px) {
  .preparation { padding: var(--e-4); }
  .preparation__point {
    grid-template-columns: 1fr;
    gap: var(--e-2);
  }
  .preparation__action { justify-self: stretch; }
}

/* Deux temps pour retirer une ligne de grille : « Confirmer » et « Annuler »
   se posent côte à côte, et passent l'un sous l'autre quand la colonne est
   étroite. Sans ce flex, ils se collaient bord à bord — deux cibles tactiles
   mitoyennes, dont l'une est irréversible. */
.tableau__actions {
  text-align: end;
}

.tableau__actions:has(> .button + .button) {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: var(--e-2);
}

/* --- Chercher le DPE par l'adresse du chantier ---------------------------- */

/* Le geste principal : il est au-dessus du numéro de DPE parce qu'il ne
   demande rien au technicien, là où le numéro suppose d'avoir le diagnostic
   sous les yeux. Le trait le sépare de la saisie, qui reste le recours. */
.dpe-adresse {
  margin-bottom: var(--e-4);
  padding-bottom: var(--e-4);
  border-bottom: 1px solid var(--filet);
}

.dpe-adresse .button {
  min-height: var(--touch);
}

.dpe-adresse .dpe-carte__champs {
  margin: var(--e-2) 0 0;
}

/* Sur un téléphone, le bouton prend la largeur : « Chercher par l'adresse du
   chantier » ne tient pas sur une ligne à 390 px. */
@media (max-width: 560px) {
  .dpe-adresse .button { width: 100%; }
}

/* La marge d'erreur d'un prix estimé, sous le montant.

   Elle vivait dans un attribut `title` : une info-bulle, donc rien du tout
   sur une tablette. Le technicien lisait « env. 4 200 € » sans savoir si
   l'écart annoncé était de 10 % ou de 50 %. */
.prix-estime__marge {
  display: block;
  font-size: var(--t-xs);
  font-weight: 500;
  color: var(--sable-fonce);
  white-space: nowrap;
}

/* La marge d'erreur sur une solution clim : sous le total, plus discrète que
   « posé, tout compris » — c'est une précision sur le chiffre, pas un second
   argument de vente. */
.solution__estime {
  display: block;
  color: var(--sable-fonce);
  font-weight: 500;
  white-space: nowrap;
}

/* =============================================================================
   REGISTRE PROFESSIONNEL (D-129)

   « Je trouve l'appli encore trop enfantine, va vraiment dans le design type
   Kelvin. »

   Ce qui faisait « application grand public », et qui n'a rien à voir avec le
   goût :

     1. UNE BARRE D'ONGLETS EN BAS AVEC UN GROS « + » ROND AU CENTRE. C'est la
        grammaire d'Instagram, pas celle d'un outil professionnel. Elle se
        justifie sur un téléphone tenu d'une main — le pouce atteint le bas —
        et nulle part ailleurs. Sur tablette et sur écran, la navigation d'un
        SaaS est en haut, en toutes lettres.

     2. UNE PASTILLE BLEUE CIRCULAIRE pour l'action principale. Un bouton
        rectangulaire dit la même chose sans l'infantiliser.

     3. DES OMBRES SOUS L'EN-TÊTE. Kelvin pose ses surfaces sur un filet d'un
        pixel ; l'ombre portée date des interfaces qui imitaient le papier.

   Ce bloc ne change AUCUN balisage : il ne fait que redéfinir l'apparence de
   ce qui existe déjà, et se retire d'un seul tenant.

   Ce qui ne bouge pas : la cible de 48 px, le corps à 17 px, l'absence d'état
   dépendant du survol. Le contexte terrain ne se négocie pas contre une mode.
   ========================================================================== */

/* --- L'en-tête se pose, il ne flotte plus -------------------------------- */

.app-header {
  background: var(--encre);
  box-shadow: none;
  border-bottom: 1px solid rgb(255 255 255 / 0.08);
}

/* --- La navigation passe en haut dès la tablette ------------------------- */

@media (min-width: 768px) {
  .onglets {
    /* EN FLEX, PAS EN GRILLE. Une grille donne sa piste à chaque onglet et
       aucune marge automatique n'en pousse un à droite : « Nouveau » restait
       collé au deuxième rang. */
    position: static;
    display: flex;
    align-items: center;
    gap: 0;
    padding: 0 var(--gutter);
    border-top: 0;
    border-bottom: 1px solid var(--filet);
    box-shadow: none;
  }

  /* Icône et libellé sur une ligne : c'est ce qui distingue une barre d'outils
     d'une barre d'onglets mobile. */
  .onglet {
    flex-direction: row;
    gap: .5rem;
    min-height: 3.25rem;
    padding: 0 var(--e-4);
    font-size: var(--t-md);
  }

  .onglet__libelle {
    font-size: var(--t-md);
    font-weight: 500;
    letter-spacing: 0;
  }

  .onglet:hover { color: var(--encre); }

  /* Le trait passe SOUS l'onglet, comme dans tout outil de bureau, et il fait
     toute la largeur de la cible plutôt qu'un tiret centré. */
  .onglet--actif { color: var(--encre); font-weight: 600; }
  .onglet--actif::before {
    top: auto;
    bottom: -1px;
    left: 0;
    width: 100%;
    height: 2px;
    border-radius: 0;
  }

  /* « Nouveau » redevient un bouton, à droite, où l'œil cherche l'action. */
  .onglet--action {
    /* « Nouveau » est DEUXIÈME dans la liste — sa place sur téléphone, sous
       le pouce, au centre. `order` le renvoie en dernier sans toucher au
       balisage, et la marge automatique fait le reste. */
    order: 1;
    margin-inline-start: auto;
    margin-block: .5rem;
    padding: 0 var(--e-4);
    min-height: 2.5rem;
    border-radius: var(--radius-sm);
    /* La même encre que tous les boutons : un seul bleu dans l'écran, et
       c'est celui de l'onglet courant — ce qui se touche pour aller
       ailleurs. */
    background: var(--action-encre);
    color: #fff;
  }
  .onglet--action:hover { background: var(--action-encre-fonce); color: #fff; }
  .onglet--action .onglet__icone {
    width: auto;
    height: auto;
    margin: 0;
    border-radius: 0;
    background: none;
    color: inherit;
  }
  .onglet--action .onglet__libelle { color: inherit; font-weight: 600; }
  .onglet--action::before { display: none; }
}

/* --- Sur téléphone, la barre reste en bas, sans la pastille ------------- */

@media (max-width: 767px) {
  /* La pastille circulaire devient un onglet comme les autres, teinté. Le
     geste reste au même endroit, sous le pouce ; c'est sa mise en scène qui
     s'efface. */
  .onglet--action .onglet__icone {
    width: auto;
    height: auto;
    margin-bottom: 0;
    border-radius: 0;
    background: none;
    color: var(--action);
  }
  .onglet--action .onglet__libelle { color: var(--action); font-weight: 600; }
}

/* --- Les surfaces : un filet, pas une ombre ----------------------------- */

.card {
  border-radius: var(--radius);
  box-shadow: none;
}

/* --- Le titre d'écran cesse de crier ------------------------------------ */

/* Il montait à 28 px sur un écran large, au-dessus d'un contenu en 17 :
   un rapport de 1,65 qui écrase tout ce qui suit. Kelvin tient ses titres de
   section à 21. */
.screen__title {
  font-size: clamp(var(--t-lg), 3.2vw, 1.625rem);
  letter-spacing: -0.02em;
}

/* --- Le bouton principal cesse d'être bleu ------------------------------- */

/* CE QUE J'AI RELEVÉ CHEZ ECAIR, ET QUI TRANCHE LE PLUS.
   Leur écran de connexion ne porte AUCUNE couleur saturée. Le bouton
   « Continuer » est presque noir — rgb(33,33,38) —, le fond gris très clair,
   la police unique (Inter), et la hiérarchie se fait au gras, pas à la taille :
   leur h1 pèse 17 px quand le corps en fait 16.

   Un bleu vif sur chaque bouton, c'est ce qui range une interface du côté des
   applications grand public. L'encre y range un outil de travail.

   Le bleu ne disparaît pas : il reste la couleur du LIEN et de l'onglet actif,
   c'est-à-dire de ce qui se touche pour aller ailleurs. Il cesse d'être la
   couleur de tout ce qui se presse. */
:root {
  --action-encre: #1c2331;
  --action-encre-fonce: #0b1422;
}

.button {
  background: var(--action-encre);
  border-radius: var(--radius-sm);
  font-weight: 500;
  letter-spacing: 0;
}

.button:active,
.button:hover { background: var(--action-encre-fonce); }

/* Le bouton secondaire garde son filet, et son texte redevient de l'encre :
   un contour bleu à côté d'un bouton noir donnait deux accents pour une seule
   décision. */
.button--secondary {
  background: var(--surface);
  border-color: var(--filet-fort);
  color: var(--encre);
  font-weight: 500;
}
.button--secondary:hover { background: var(--surface-voile); }

/* LE BOUTON-LIEN N'EN EST PAS UN, ET IL AVAIT REPRIS UN FOND.

   `.button--link` est déclaré ligne 566, mille lignes avant ce bloc : il
   posait `background: none`, et mon écrasement de `.button` l'a rattrapé —
   même spécificité, déclaré plus loin, il gagne. « Annuler » s'affichait
   donc en pavé d'encre à côté du « Confirmer » rouge, deux boutons pleins
   pour une décision et son refus.

   La leçon vaut pour tout ce bloc : redéfinir une classe de base oblige à
   réaffirmer chacune de ses variantes déclarées plus haut. Les variantes
   qui ne touchent pas au fond — `--compact`, `--petit`, `--block` —
   n'ont rien à réaffirmer ; vérifié une à une. */
.button--link {
  background: none;
  color: var(--action-fonce);
}
.button--link:hover { background: none; }

/* Le danger reste rouge, et l'action irréversible reste la seule à crier. */
.button--danger { background: var(--danger); }
.button--danger:hover { background: #8f1a13; }

/* Les boutons de la préparation et de la carte DPE suivent la même règle :
   ils proposaient un contour bleu épais là où un filet suffit. */
.preparation__action {
  border-color: var(--filet-fort);
  color: var(--encre);
  font-weight: 500;
}
.preparation__action:hover { background: var(--surface-voile); }

/* --- Le fond refroidit d'un ton ----------------------------------------- */

/* #f1f2f4 tirait sur le bleu ; Ecair pose ses cartes sur un gris neutre très
   clair, ce qui laisse la couleur aux seuls endroits qui en portent une. */
:root { --fond-clair: #f4f5f7; }

/* --- La marque : un monogramme dans un carré arrondi (D-130) ------------- */

/* Deux couleurs, et le contexte décide laquelle va où. `currentColor` ne
   pouvait pas servir : dans l'en-tête, la couleur héritée vaut l'encre —
   c'est-à-dire exactement la couleur du fond. */
:root {
  --logo-fond: var(--encre);
  --logo-glyphe: #ffffff;
}

.logo__pave { fill: var(--logo-fond); }

.logo__monogramme {
  fill: var(--logo-glyphe);
  font-family: var(--font-titre);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* Sur l'en-tête sombre, le carré s'inverse : blanc, lettre encre. C'est la
   construction d'Ecair, et c'est ce qui tient le mieux sur un bandeau foncé —
   un carré encre sur fond encre ne serait qu'un trou. */
.app-header {
  --logo-fond: #ffffff;
  --logo-glyphe: var(--encre);
}

/* L'avertissement d'une adresse reconnue automatiquement : en sable, la
   couleur des réserves — ce n'est ni une erreur ni un détail, c'est une
   interprétation qu'il faut confirmer. */
.dpe-carte__reconnue {
  color: var(--sable-fonce);
}

/* La réserve sous le total de l'estimation : en sable, comme toutes les
   limites de ce produit — ce n'est ni une alerte ni un détail. */
.bilan__reserve {
  margin: var(--e-3) 0 0;
  font-size: var(--t-sm);
  color: var(--sable-fonce);
  max-width: 52ch;
}

/* --- Pourquoi la liste est maigre (D-136) -------------------------------- */

/* Le panneau qui explique l'écrémage, au-dessus des gammes. Il porte le sable
   des réserves : ce n'est pas une erreur, c'est un chantier contraint — et il
   disparaît dès que la liste est fournie. */
.diagnostic {
  margin: var(--e-4) 0;
  padding: var(--e-4) var(--e-5);
  background: var(--surface);
  border: 1px solid var(--filet);
  border-left: 4px solid var(--sable);
  border-radius: var(--radius);
}

.diagnostic__titre {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--t-body);
  font-weight: 600;
}

.diagnostic__intro {
  margin: var(--e-1) 0 var(--e-3);
  font-size: var(--t-md);
  color: var(--gris-texte);
  max-width: 62ch;
}

.diagnostic__pistes {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* La cause à gauche, ce qu'on y gagne à droite : on lit le coût et le remède
   sur la même ligne, sans avoir à les rapprocher soi-même. */
.diagnostic__piste {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--e-2) var(--e-4);
  align-items: baseline;
  padding: var(--e-2) 0;
  font-size: var(--t-md);
}

.diagnostic__piste + .diagnostic__piste { border-top: 1px solid var(--filet); }

.diagnostic__cause { color: var(--gris-texte); }
.diagnostic__gain { color: var(--encre); font-variant-numeric: tabular-nums; white-space: nowrap; }
.diagnostic__gain strong { color: var(--vert-fonce); }

.diagnostic__puissance {
  margin: var(--e-3) 0 0;
  font-size: var(--t-sm);
  color: var(--gris-tertiaire);
  max-width: 62ch;
}

.diagnostic .lien { display: inline-block; margin-top: var(--e-3); }

/* Sur téléphone, le gain passe sous sa cause. */
@media (max-width: 560px) {
  .diagnostic { padding: var(--e-4); }
  .diagnostic__piste { grid-template-columns: 1fr; gap: var(--e-1); }
}

/* =============================================================================
   TROIS BARRES SUR UN ÉCRAN COURT (D-137)

   Mesuré sur une fenêtre de 235 px de haut, au premier écran du parcours :

     en-tête          65 px   collé en haut
     barre d'étapes   68 px   collée à 72 px
     précédent/suivant 73 px  fixée en bas
     ─────────────────────────
     206 px de chrome, 29 px pour le contenu.

   Le formulaire défilait dans une fente. Et entre l'en-tête (65 px) et la barre
   d'étapes (collée à 4,5 rem = 72 px) s'ouvrait un interstice de SEPT PIXELS
   par lequel on voyait passer le texte — c'est ce qui donne l'impression que
   l'écran se déchire au défilement.
   ========================================================================== */

/* --- L'interstice se referme -------------------------------------------- */

/* La barre d'étapes se colle à la hauteur RÉELLE de l'en-tête, pas à une
   valeur ronde choisie de loin. Un pixel de recouvrement vaut mieux que sept
   pixels de vide : il passe sous la bordure, le vide laissait défiler le
   contenu entre deux barres. */
:root { --haut-entete: 4rem; }

.parcours { top: var(--haut-entete); }

/* --- Écran court : une barre de moins ------------------------------------ */

/* En dessous de 640 px de haut — un portable posé sur un bureau en visio, une
   tablette en paysage, une fenêtre réduite — la barre d'étapes cesse de se
   coller. Elle est informative : savoir qu'on est à l'étape 1 sur 9 ne vaut
   pas le tiers de l'écran. « Précédent / suivant » reste fixée : c'est elle
   qu'on presse. */
@media (max-height: 640px) {
  .parcours {
    position: static;
    padding-block: .5rem .375rem;
  }

  .app-header { padding-block: .25rem; }
  .app-header__brand { min-height: 2.5rem; }

  .nav-etapes { padding-block: .5rem; }

  /* Le contenu remonte : deux rems de respiration en haut d'écran n'ont pas de
     sens quand il n'en reste que trois en tout. */
  .screen { padding-block: 1rem; }
}

/* --- Écran très court ---------------------------------------------------- */

/* Sous 480 px, l'en-tête lui-même se décolle. Il ne porte que la marque, le
   nom de la maison et le mode client — rien qu'on presse en urgence pendant
   qu'on remplit un champ. */
@media (max-height: 480px) {
  .app-header { position: static; }
  .screen { padding-block: .75rem; }
}

/* ---------------------------------------------------------------------------
   LE PRIX UNITAIRE SE SAISIT (D-141)

   Il n'était qu'un texte gris sous le libellé. Le technicien lisait « env.
   3 200 € à 50 % près » devant son client sans pouvoir écrire le prix qu'il
   connaît. Le champ reste discret — c'est une correction ponctuelle, pas le
   centre de l'écran — mais il respecte `--touch` comme toute cible tactile.
--------------------------------------------------------------------------- */

.ligne__unitaire {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin-block-start: 0.375rem;
}

.ligne__prix-saisie {
  inline-size: 7rem;
  text-align: end;
  font-variant-numeric: tabular-nums;
}

.ligne__unitaire-suffixe {
  white-space: nowrap;
}

/* La mention d'estimation passe à la ligne entière : accolée au suffixe, elle
   poussait le champ hors de la carte en mobile. */
.ligne__unitaire .prix-estime__marge {
  flex-basis: 100%;
}

/* ---------------------------------------------------------------------------
   LES DEUX MÉTIERS SE CHOISISSENT AVANT LA MARQUE (D-142)

   Le sélecteur de famille commande tout ce qui suit — marques, gammes,
   lignes. Il est donc posé plus haut, détaché du reste par un filet, et ses
   jetons sont plus larges : ce n'est pas un filtre parmi d'autres, c'est la
   question qu'on tranche en premier.
--------------------------------------------------------------------------- */

.filtres-marques--familles {
  padding-block-end: .75rem;
  margin-block-end: .25rem;
  border-block-end: 1px solid var(--filet);
}

.filtres-marques--familles .filtre-puce {
  font-weight: 600;
}

/* ---------------------------------------------------------------------------
   UN CRITÈRE QUI NE DÉPARTAGE PAS SE VOIT (D-144)

   Ni vert ni rouge : gris. La jauge reste à sa place pour que les lignes
   gardent leur alignement, mais elle s'efface — il n'y a rien à lire dedans.
--------------------------------------------------------------------------- */

.comparateur__jauge--plate .comparateur__jauge-remplie {
  background: var(--filet);
}

.comparateur__apport--plat {
  color: var(--ink-muted);
  font-weight: 400;
  font-style: italic;
}

/* ---------------------------------------------------------------------------
   LE SÉLECTEUR DE MARQUES NE MANGE PLUS L'ÉCRAN DU TÉLÉPHONE (D-145)

   Mesuré à 375 x 812 : la grille de treize marques occupait 421 px, sur
   606 px de contenu utile une fois les barres déduites. Le technicien ouvrait
   l'écran matériel et voyait sept rangées de logos — pas une gamme, pas un
   prix. Il fallait faire défiler pour atteindre ce qu'il venait chercher.

   Quatre colonnes au lieu de deux, et des logos d'un cran plus bas : la
   hauteur tombe à 216 px et trois gammes passent au-dessus de la ligne de
   flottaison.

   UNE BANDE QUI DÉFILE DE CÔTÉ A ÉTÉ ESSAYÉE ET REJETÉE. Elle tombait à
   61 px — le meilleur résultat — mais faisait déborder la PAGE ENTIÈRE
   horizontalement : la barre de navigation basse, en `position: fixed`,
   s'étirait à 1 501 px et le document se mettait à défiler de côté. Mesuré,
   reproduit, et non corrigé par `overflow: hidden` sur la bande. Une démo se
   fait sur un écran qui ne part pas en crabe ; la grille compacte donne
   l'essentiel du gain sans ce risque.

   LA CIBLE TACTILE NE SE NÉGOCIE PAS. Les tuiles restent à 48 px : c'est la
   règle `--touch` du design system, et un technicien avec des gants ne vise
   pas mieux parce que l'écran est petit.
--------------------------------------------------------------------------- */

@media (max-width: 600px) {
  .marques-groupe {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: .375rem;
  }

  .marque {
    min-height: 3rem;
    padding: .375rem .25rem;
  }

  .marque__logo {
    height: 1.75rem;
  }

  /* Le nom en repli — les marques sans logo — doit rester lisible dans une
     colonne de quatre-vingts pixels. */
  .marque__nom {
    font-size: var(--t-sm);
    line-height: 1.15;
    overflow-wrap: anywhere;
  }

  /* Le liseré seul se perd sur un écran de six centimètres. */
  .marque--active {
    background: var(--action-pale);
  }
}

/* ---------------------------------------------------------------------------
   LA GRILLE TARIFAIRE DIT D'ABORD OÙ ON EN EST (D-147)

   L'écran ouvrait sur onze lignes de remise, dont sept de marques qui ne
   vendent pas d'air/eau, puis un tableau de mille deux cent soixante-seize
   lignes. La question qu'on lui pose — « que me reste-t-il à chiffrer ? » —
   n'avait de réponse nulle part.

   La carte du métier prend la tête : deux onglets, leurs comptes, ce qui
   reste sans prix, et le bouton d'import du métier choisi. Tout le reste en
   découle.
--------------------------------------------------------------------------- */

.metier {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.metier__choix {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .5rem;
}

.metier__onglet {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .125rem;
  min-height: var(--touch, 3rem);
  padding: .625rem .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.metier__onglet--actif {
  border-color: var(--action);
  background: var(--action-pale);
  box-shadow: inset 0 0 0 1px var(--action);
}

.metier__libelle {
  font-weight: 600;
  color: var(--encre);
}

.metier__compte {
  font-size: var(--t-sm);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.metier__etat {
  display: flex;
  flex-direction: column;
  gap: .625rem;
}

/* Le reste à faire se lit comme un fait, pas comme une alarme : c'est du
   travail en cours, pas une erreur. L'ambre le dit sans crier. */
.metier__reste {
  margin: 0;
  padding: .625rem .75rem;
  border-inline-start: 3px solid var(--sable);
  background: var(--sable-pale);
  border-radius: var(--radius-sm);
  color: var(--encre);
}

.metier__reste strong { font-variant-numeric: tabular-nums; }

.metier__ok {
  margin: 0;
  color: var(--vert-fonce);
  font-weight: 600;
}

.actions--serrees { margin: 0; }

.remises__regle {
  margin: 0 0 .75rem;
  color: var(--ink-muted);
  font-size: var(--t-sm);
  line-height: 1.5;
}

/* La pastille de marque porte son reste à faire. Elle passe sous le nom pour
   ne pas allonger la pastille au point de casser la ligne sur un téléphone. */
.filtre-puce__manque {
  display: block;
  width: 100%;
  margin-top: .125rem;
  font-size: var(--t-xs, .75rem);
  font-weight: 600;
  color: var(--sable);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   LA GRILLE DEVIENT DES FICHES SUR TÉLÉPHONE (D-147)

   Six colonnes dans 293 px : le tableau mesurait 684 px et se lisait en le
   poussant de côté, une main sur l'écran, l'autre sur le champ. On posait un
   prix d'achat sans voir à quelle machine il se rapportait.

   Chaque ligne devient une fiche : la désignation en tête, puis les trois
   valeurs libellées. Les en-têtes du tableau disparaissent — ils sont repris
   par les libellés de chaque champ, portés par `data-colonne`.

   Le tableau RESTE un tableau dans le HTML : c'est la bonne sémantique pour
   un lecteur d'écran, et seule sa présentation change.
--------------------------------------------------------------------------- */

@media (max-width: 600px) {
  .tableau-enveloppe { overflow-x: visible; }

  .tableau,
  .tableau tbody,
  .tableau tr,
  .tableau td {
    display: block;
    width: auto;
  }

  .tableau thead { display: none; }

  .tableau tbody tr {
    margin-block-end: .625rem;
    padding: .75rem;
    border: 1px solid var(--filet);
    border-radius: var(--radius-sm);
    background: var(--surface);
  }

  /* Les bandes de titre — « Atlantic — Alfea » — restent des bandes. */
  .tableau tbody tr.tableau__bande,
  .tableau tbody tr:has(> th) {
    padding: .5rem .25rem 0;
    border: 0;
    background: none;
    margin-block-end: .25rem;
  }

  .tableau tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .25rem 0;
    border: 0;
    text-align: start;
  }

  .tableau tbody td[data-colonne]::before {
    content: attr(data-colonne);
    flex: 0 0 auto;
    color: var(--ink-muted);
    font-size: var(--t-sm);
  }

  /* La désignation porte la fiche : elle passe en tête, sans libellé, et en
     gras — c'est elle qu'on cherche du regard. */
  .tableau tbody td[data-colonne="Désignation"] {
    display: block;
    font-weight: 600;
    padding-block-end: .5rem;
    margin-block-end: .375rem;
    border-block-end: 1px solid var(--filet);
  }

  .tableau tbody td[data-colonne="Désignation"]::before { content: none; }

  .tableau tbody td[data-colonne="Unité"] { display: none; }

  .tableau tbody td .champ-tableau {
    inline-size: 8.5rem;
    text-align: end;
  }

  .tableau tbody td.tableau__actions {
    justify-content: flex-end;
    padding-block-start: .5rem;
  }

  .tableau tbody td.tableau__actions::before { content: none; }
}

/* ---------------------------------------------------------------------------
   L'ETAS S'AFFICHE PARTOUT OÙ LE SCOP L'EST (D-150)

   C'est l'efficacité saisonnière ErP qui ouvre MaPrimeRénov' et les CEE, pas
   le SCOP. Sur la carte de référence, elle prend une pastille sobre — un
   fait, pas une promotion — et la valeur dérivée du SCOP s'écrit avec « ≈ ».
--------------------------------------------------------------------------- */

.reference__etas {
  display: inline-block;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--fond-clair);
  color: var(--encre);
  font-size: var(--t-xs, .75rem);
  font-weight: 600;
  letter-spacing: .02em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   LE PANNEAU DE RÉGLAGES SE REPLIE SUR TÉLÉPHONE (D-151)

   Mesuré à 375 x 812 : ses trois points occupaient tout le premier écran du
   tableau de bord, avant les chiffres et avant les dossiers — pour lesquels
   on ouvre l'application. Replié, il tient en une ligne et un bouton ; il
   reste visible, il ne prend plus la place.
--------------------------------------------------------------------------- */

.preparation--replie .preparation__liste,
.preparation--replie .preparation__plus { display: none; }

.preparation--replie .preparation__entete { margin-bottom: 0; }

.preparation__deplier {
  margin-top: .625rem;
  min-height: var(--touch, 3rem);
  padding: .5rem .875rem;
  border: 1px solid var(--filet-fort);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--encre);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   LES MODÈLES SE COMPARENT SOUS LEUR GAMME (D-155)

   Un repli natif (details/summary) : pas d'état à gérer, une cible tactile
   de 48 px, et le clavier le connaît. Les modèles se lisent en retrait sous
   la gamme, avec leur puissance et leur rendement — de quoi choisir sans
   ouvrir la fiche.
--------------------------------------------------------------------------- */

.comparateur__modeles {
  margin: .25rem 0 .5rem .5rem;
  border-inline-start: 2px solid var(--filet);
  padding-inline-start: .625rem;
}

.comparateur__modeles-tete {
  display: flex;
  align-items: center;
  min-height: var(--touch, 3rem);
  cursor: pointer;
  color: var(--action);
  font-weight: 600;
  font-size: var(--t-sm);
  list-style: none;
}

.comparateur__modeles-tete::-webkit-details-marker { display: none; }
.comparateur__modeles-tete::before { content: '▸'; margin-inline-end: .5rem; font-size: .8em; }
.comparateur__modeles[open] > .comparateur__modeles-tete::before { content: '▾'; }

.comparateur__modeles-liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .25rem;
}

.comparateur__modele {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem .75rem;
  width: 100%;
  min-height: var(--touch, 3rem);
  padding: .5rem .75rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.comparateur__modele:disabled { opacity: .5; cursor: not-allowed; }

.comparateur__modele--retenu {
  border-color: var(--action);
  background: var(--action-pale);
  box-shadow: inset 0 0 0 1px var(--action);
}

.comparateur__modele-ref {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.comparateur__modele-detail {
  font-size: var(--t-sm);
  color: var(--ink-muted);
}

/* La référence sous le nom de gamme, dans la colonne de résultat. */
.comparateur__reference {
  margin: -.25rem 0 .25rem;
  font-size: var(--t-sm);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   LA MAISON SE VOIT DÈS QUE L'ADRESSE EST RECONNUE (D-156)

   Neuf tuiles IGN dans une grille carrée, un repère au centre. La mosaïque
   prend la largeur de la carte et jamais plus : sur un téléphone, ~150 m de
   côté en 340 px suffisent à lire l'emprise, le jardin et la mitoyenneté.
--------------------------------------------------------------------------- */

.vue-aerienne {
  margin: .75rem 0 0;
}

.vue-aerienne__mosaique {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  aspect-ratio: 1;
  max-width: 100%;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--fond-clair);
}

.vue-aerienne__mosaique img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
}

.vue-aerienne__repere {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 14px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--action);
  box-shadow: 0 0 0 3px #fff, 0 0 0 5px rgb(27 77 228 / .45);
}

.vue-aerienne__legende {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: .25rem .75rem;
  margin-top: .375rem;
  font-size: var(--t-xs, .75rem);
  color: var(--ink-muted);
}

/* ---------------------------------------------------------------------------
   LA RUBRIQUE AIDE (D-157)

   Deux rangées de filtres — le type de demande, puis le sujet — et des
   réponses prêtes. La zone de réponse est un simple champ de texte que la
   réponse retenue pré-remplit : l'artisan ajuste, copie, envoie depuis son
   outil de messagerie. Pas de chat : un pré-remplissage honnête.
--------------------------------------------------------------------------- */

.aide__filtres {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
  margin: .5rem 0 .75rem;
}

.aide__reponses {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: .5rem;
}

.aide__reponse {
  display: block;
  width: 100%;
  padding: .75rem .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.aide__reponse--retenue {
  border-color: var(--action);
  background: var(--action-pale);
  box-shadow: inset 0 0 0 1px var(--action);
}

.aide__question {
  display: block;
  font-weight: 600;
  margin-bottom: .25rem;
}

.aide__extrait {
  display: block;
  font-size: var(--t-sm);
  color: var(--ink-muted);
  line-height: 1.45;
}

.aide__composer textarea {
  width: 100%;
  min-height: 11rem;
  padding: .75rem;
  border: 1px solid var(--filet-fort);
  border-radius: var(--radius-sm);
  font: inherit;
  line-height: 1.5;
  resize: vertical;
}

.aide__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .625rem;
}

/* ===========================================================================
   FINITION PREMIUM, TÉLÉPHONE D'ABORD (D-161)

   Ce bloc ne redessine rien : il resserre. Les mêmes jetons, la même charte,
   mais une hiérarchie plus nette là où l'écran de six centimètres la perdait :
   un chiffre qui commande par écran, des cartes qui respirent sans gaspiller,
   des avis qui informent sans crier, un en-tête qui dit pour qui l'on
   travaille. Tout est mesuré à 375 px avant d'être gardé.
=========================================================================== */

/* --- L'en-tête dit l'entreprise, même sur téléphone ------------------------
   Le nom disparaissait sous 720 px pour ne garder que le rôle. Or c'est le
   nom que l'artisan veut voir, et le rôle qu'il connaît. Le nom revient en
   une ligne, le rôle devient une pastille discrète sous lui. */
@media (max-width: 720px) {
  .app-header {
    padding-block: .375rem;
  }
  .app-header__identity {
    max-width: 11rem;
    gap: .125rem;
  }
  .app-header__tenant {
    display: block;
    font-size: var(--t-sm);
    font-weight: 600;
    letter-spacing: .01em;
  }
  .app-header__role {
    align-self: flex-end;
    padding: .0625rem .5rem;
    border-radius: 999px;
    background: rgb(255 255 255 / 0.10);
    color: rgb(255 255 255 / 0.85);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
  }
}

/* --- Un chiffre qui commande par écran : le tableau de bord -----------------
   Quatre cases égales se lisent comme un tableau. La première — ce qui est en
   jeu — prend toute la largeur et le fond d'action pâle ; les trois autres
   suivent sur une rangée, plus petites. La hiérarchie est celle de la
   décision, pas celle de la grille. */
@media (max-width: 600px) {
  .kpis {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    border: 0;
    background: none;
    overflow: visible;
    gap: .5rem;
  }
  .kpi {
    border: 1px solid var(--filet);
    border-radius: var(--radius);
    background: var(--surface);
    padding: .75rem .75rem .875rem;
    gap: .0625rem;
  }
  .kpi:first-child {
    grid-column: 1 / -1;
    padding: 1rem 1.125rem 1.125rem;
    background: var(--action-pale);
    border-color: var(--brand-100);
  }
  .kpi:first-child .kpi__valeur {
    font-size: var(--t-2xl);
    color: var(--action-fonce);
  }
  .kpi:first-child .kpi__libelle { color: var(--action-fonce); }
  .kpi:not(:first-child) .kpi__valeur { font-size: var(--t-lg); }
  .kpi:not(:first-child) .kpi__libelle {
    font-size: .625rem;
    letter-spacing: .06em;
  }
  .kpi:not(:first-child) .kpi__detail { font-size: var(--t-xs); }
}

/* --- Les cartes respirent sans gaspiller ------------------------------------
   Un rembourrage de vingt-quatre pixels de chaque côté laisse 327 px de
   contenu sur 375. Dix-huit suffisent, et la colonne gagne douze pixels — un
   mot par ligne, sur chaque ligne. */
@media (max-width: 600px) {
  .card {
    padding: 1.125rem;
    margin-top: 1rem;
  }
  .screen {
    padding-block: 1.25rem;
  }
}

/* --- Les titres de section marquent le rythme ------------------------------
   Une section commence par un titre qu'on repère en défilant vite : un cran
   plus bas que le titre d'écran, une lettre plus serrée, et le filet qui suit
   appartient au contenu, pas au titre. */
.form-section__title {
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
  line-height: 1.2;
  margin-bottom: .5rem;
}

/* --- La gamme retenue se voit du coin de l'œil ------------------------------
   Le liseré bleu d'un pixel se perd entre vingt cartes. Une barre d'accent à
   gauche et un fond pâle disent « c'est celle-ci » avant même de lire. La
   plage de puissance passe en pastille : c'est la donnée qu'on compare d'une
   carte à l'autre, elle mérite sa propre forme. */
.gamme-carte {
  position: relative;
  border-radius: var(--radius);
}
.gamme-carte--choisie {
  background: var(--action-pale);
  box-shadow: inset 3px 0 0 var(--action), inset 0 0 0 1px var(--action);
}
.gamme-carte__plage {
  display: inline-block;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--fond-clair);
  color: var(--action-fonce);
  font-size: var(--t-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.gamme-carte--choisie .gamme-carte__plage { background: #fff; }

/* --- Les avis informent, ils ne crient pas ---------------------------------
   Un encadré plein sur quatre côtés, en gras, pour dire qu'un barème n'est
   pas encore confirmé : l'écran des aides en portait deux avant le premier
   chiffre. Un filet à gauche, la teinte en fond, le texte en corps normal :
   on lit l'avis, il ne barre plus la route. */
.notice {
  border-width: 0;
  border-inline-start: 3px solid;
  border-radius: var(--radius-sm);
  padding: .75rem .875rem;
  font-weight: 400;
  font-size: var(--t-md);
  line-height: 1.45;
}
.notice--info { border-color: var(--info); }
.notice--warning { border-color: var(--sable); }
.notice--danger { border-color: var(--danger); font-weight: 500; }
.notice + .notice { margin-top: .625rem; }

/* --- Le champ actif se voit sans chercher -----------------------------------
   Un changement de couleur de bordure d'un pixel ne se voit pas sous le
   soleil. Un halo pâle de trois pixels, si. */
.field__input:focus,
.champ-tableau:focus,
.ligne__input:focus,
textarea:focus {
  outline: none;
  border-color: var(--action);
  box-shadow: 0 0 0 3px var(--action-pale);
}

/* --- Les montants de l'estimation pèsent leur poids -------------------------
   Le montant d'une ligne est ce que le client lit en premier : un cran plus
   grand, en encre pleine, à chiffres tabulaires. */
.ligne__montant {
  font-size: var(--t-md);
  color: var(--encre);
}

/* --- Le bouton principal se détache du plan de la page ----------------------
   Une ombre courte, portée par la couleur d'action : le bouton n'est pas
   posé, il est en relief. Les secondaires restent à plat, en filet. */
.button:not(.button--secondary):not(.button--link):not(.button--danger) {
  box-shadow: 0 1px 2px rgb(27 77 228 / 0.20), 0 4px 12px rgb(27 77 228 / 0.18);
}
.button:not(.button--secondary):not(.button--link):not(.button--danger):active {
  box-shadow: 0 1px 2px rgb(27 77 228 / 0.20);
}

/* ===========================================================================
   PILOTAGE COMMERCIAL (D-162)

   Trois lectures du même travail — la période, les personnes, les territoires
   — et la même forme pour les trois : un nom à gauche, le chiffre signé à
   droite, une barre de proportion, puis les mesures en dessous. Une seule
   forme répétée se compare d'un coup d'œil ; trois mises en page différentes
   obligeraient à réapprendre à lire à chaque bloc.
=========================================================================== */

.pilotage__periodes {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
  margin-top: 1rem;
}

/* --- La bande de tête ------------------------------------------------------
   Le signé commande : il prend la largeur et le fond d'action pâle. Les trois
   autres suivent, plus petits. Même hiérarchie que le tableau de bord, pour
   que passer d'un écran à l'autre ne demande aucun effort. */
.pilotage__bande {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem;
  margin-top: 1.25rem;
}

.pilotage__chiffre {
  padding: .875rem 1rem 1rem;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: var(--radius);
}

.pilotage__chiffre--fort {
  grid-column: 1 / -1;
  padding: 1.125rem 1.25rem 1.25rem;
  background: var(--action-pale);
  border-color: var(--brand-100);
}

.pilotage__libelle {
  margin: 0;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gris-texte);
}

.pilotage__chiffre--fort .pilotage__libelle { color: var(--action-fonce); }

.pilotage__valeur {
  margin: .1875rem 0 0;
  font-family: var(--font-titre);
  font-weight: 700;
  font-size: var(--t-lg);
  line-height: 1.1;
  letter-spacing: var(--serrage-titre);
  font-variant-numeric: tabular-nums;
  color: var(--encre);
}

.pilotage__chiffre--fort .pilotage__valeur {
  font-size: var(--t-2xl);
  color: var(--action-fonce);
}

.pilotage__detail {
  margin: .25rem 0 0;
  font-size: var(--t-sm);
  line-height: 1.4;
  color: var(--gris-texte);
}

.pilotage__chiffre:not(.pilotage__chiffre--fort) .pilotage__detail {
  font-size: var(--t-xs);
}

/* --- Les rangs -------------------------------------------------------------
   Pas de tableau : sur un téléphone, six colonnes de chiffres se lisent en
   poussant l'écran de côté. Un rang par ligne, et les mesures en dessous du
   nom, dans l'ordre où on les cherche. */
.pilotage__liste {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: .625rem;
}

.pilotage__rang {
  padding: .875rem 1rem 1rem;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: var(--radius);
}

.pilotage__tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
}

.pilotage__identite {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .25rem .5rem;
  min-width: 0;
}

.pilotage__nom {
  font-weight: 600;
  font-size: var(--t-body);
  color: var(--encre);
}

.pilotage__role {
  padding: .0625rem .4375rem;
  border-radius: 999px;
  background: var(--fond-clair);
  color: var(--gris-texte);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.pilotage__villes {
  color: var(--ink-muted);
  font-size: var(--t-sm);
}

.pilotage__montant {
  flex: none;
  font-family: var(--font-titre);
  font-weight: 700;
  font-size: var(--t-lg);
  letter-spacing: var(--serrage-titre);
  font-variant-numeric: tabular-nums;
  color: var(--encre);
  white-space: nowrap;
}

/* La barre dit la proportion, pas la valeur : elle se lit d'un rang à l'autre
   sans lire les chiffres. Le plus haut du classement fait toute la largeur. */
.pilotage__jauge {
  display: block;
  height: 6px;
  margin-top: .625rem;
  border-radius: 999px;
  background: var(--fond-clair);
  overflow: hidden;
}

.pilotage__jauge-remplie {
  display: block;
  height: 100%;
  border-radius: 999px;
}

.pilotage__mesures {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem 1rem;
  margin-top: .625rem;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.pilotage__mesures b {
  color: var(--encre);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.pilotage__alerte {
  color: var(--sable);
  font-weight: 600;
}

.pilotage__renommer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .375rem;
  width: 100%;
}

.pilotage__renommer .field__input {
  flex: 1 1 10rem;
  min-width: 0;
}

.pilotage__renommer-lien { font-size: var(--t-sm); }

/* Le lien vers le pilotage, depuis le tableau de bord : une ligne posée, pas
   un bouton — ce n'est pas l'action du jour, c'est la lecture de la semaine. */
.lien--pilotage {
  display: block;
  margin-top: .75rem;
  padding: .75rem 1rem;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  font-weight: 600;
  text-decoration: none;
}

@media (min-width: 40rem) {
  .pilotage__bande { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .pilotage__chiffre--fort { grid-column: span 1; }
}

/* --- Les trois mesures secondaires passent en lignes sur téléphone ----------
   En cases d'un tiers de largeur, « TRANSFORMATION » — un seul mot de
   quatorze lettres — se coupait à « TRANSFORMAT ». Abréger un terme de métier
   pour qu'il rentre dans une grille, c'est laisser la grille commander le
   vocabulaire. La grille cède : libellé à gauche, valeur à droite, une ligne
   chacune. Le chiffre de tête garde sa case pleine largeur. */
@media (max-width: 600px) {
  .pilotage__bande { grid-template-columns: 1fr; gap: .375rem; }

  .pilotage__chiffre:not(.pilotage__chiffre--fort) {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    gap: .125rem .75rem;
    padding: .75rem 1rem;
  }

  .pilotage__chiffre:not(.pilotage__chiffre--fort) .pilotage__libelle {
    grid-column: 1;
  }

  .pilotage__chiffre:not(.pilotage__chiffre--fort) .pilotage__valeur {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
    font-size: var(--t-lg);
  }

  .pilotage__chiffre:not(.pilotage__chiffre--fort) .pilotage__detail {
    grid-column: 1;
    margin-top: 0;
  }
}

/* Le montant dit sur quoi il porte — signé, ou encore en jeu. Sans ce mot,
   un classement change de sens sans prévenir le jour du premier chantier
   signé (D-162). */
.pilotage__montant-quoi {
  display: block;
  margin-top: .0625rem;
  font-family: var(--font-sans);
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--gris-texte);
  text-align: end;
}

/* ===========================================================================
   LES MOMENTS QUE L'ON VOIT LE PLUS (D-163)

   Douze écrans affichaient « Chargement… » en gris clair, seuls au milieu du
   blanc, une à deux secondes à chaque navigation. Un technicien traverse ces
   écrans neuf fois par visite : c'était la surface la plus vue et la moins
   soignée de l'application.

   Deux gestes, aucun sur le contenu : le squelette qui dessine la forme à
   venir, et l'arrivée en douceur du contenu réel. Une application ne devient
   pas plus rapide ; elle cesse de paraître lente.
=========================================================================== */

.squelette { pointer-events: none; }

.squelette__entete {
  display: grid;
  gap: .625rem;
  margin-bottom: 1.5rem;
}

.squelette__blocs {
  display: grid;
  gap: .75rem;
}

.squelette__bloc {
  display: grid;
  gap: .5rem;
  padding: 1.125rem;
  background: var(--surface);
  border: 1px solid var(--filet);
  border-radius: var(--radius-lg);
}

.squelette__grille {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: .375rem;
  margin-bottom: 1.25rem;
}

.squelette__tuile {
  height: 3rem;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--filet);
}

/* LE BALAYAGE N'EST PAS UN ORNEMENT : c'est ce qui dit « ça travaille » plutôt
   que « c'est cassé ». Lent — 1,6 s — pour ne pas presser l'œil ; en dégradé
   très léger, pour ne pas attirer le regard sur un vide. */
.squelette__barre,
.squelette__tuile {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  /* LE FILET, PAS LE FOND CLAIR. Une barre de la couleur de la page est une
     barre invisible : le titre du squelette ne se voyait pas du tout hors des
     cartes, mesuré à l'écran. */
  background: var(--filet);
}

.squelette__barre::after,
.squelette__tuile::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgb(255 255 255 / 0.65) 50%,
    transparent 100%
  );
  animation: squelette-balayage 1.6s ease-in-out infinite;
}

@keyframes squelette-balayage {
  100% { transform: translateX(100%); }
}

/* L'ARRIVÉE DU CONTENU. Dix-huit centièmes de seconde, six pixels de montée :
   assez pour que l'œil enregistre une arrivée, trop court pour qu'on attende.
   Au-delà, on fait patienter au lieu de rassurer. */
@keyframes ecran-arrivee {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.screen:not(.squelette) {
  animation: ecran-arrivee 180ms ease-out both;
}

/* La barre d'onglets débordait de dix pixels à 375 px : le septième onglet
   mordait le bord. Les libellés perdent un dixième de leur chasse plutôt
   qu'un onglet sa place. */
@media (max-width: 400px) {
  .onglet { padding-inline: .25rem; }
  .onglet__libelle { font-size: .6875rem; letter-spacing: 0; }
}

/* ---------------------------------------------------------------------------
   L'ACCUEIL DIT BONJOUR, ET DIT LE JOUR (D-164)

   « Mes dossiers » au même endroit chaque matin n'apprenait rien. Un salut,
   la date en toutes lettres, et la journée en une ligne : c'est le seul écran
   qu'on ouvre tous les jours, et le seul qui puisse se permettre d'accueillir.

   Le salut est petit, la date est grande : ce qu'on cherche du regard, c'est
   le repère temporel, pas la politesse.
--------------------------------------------------------------------------- */

.accueil {
  margin-bottom: 1.25rem;
}

/* UN TITRE DE PAGE, PAS UNE ENSEIGNE (D-190). La date s'affichait en display
   de 32 px : une typographie d'affiche pour une information que le téléphone
   donne déjà dans sa barre d'état. Un titre de page se lit, il ne s'annonce
   pas -- taille du corps de texte augmentée d'un tiers, graisse 600, et la
   police du texte plutôt que celle des titres, qui appelle trop l'attention
   à cet endroit. */
.accueil__titre-ligne {
  display: flex;
  align-items: center;
  gap: .625rem;
}

.accueil__titre {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--encre);
}

/* Le compteur est une pastille, pas un mot : il se compte du regard. */
.accueil__compte {
  display: inline-grid;
  place-items: center;
  min-width: 1.625rem;
  height: 1.5rem;
  padding-inline: .4375rem;
  border-radius: var(--radius-sm);
  background: var(--action-pale);
  color: var(--action-fonce);
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.accueil__resume {
  margin: .3125rem 0 0;
  color: var(--ink-muted);
  font-size: var(--t-md);
  line-height: 1.45;
}

/* ===========================================================================
   L'ÉCRAN DE CONNEXION PORTE LA MARQUE (D-165)

   Il ne portait rien : un carré noir de quarante pixels, une ligne grise, un
   formulaire, et quarante-cinq pour cent de vide sous le pli. C'est pourtant
   le seul écran qu'un visiteur voit avant de décider s'il ouvre un compte.

   Le bleu nuit de l'en-tête prend toute la page : l'identité est immédiate,
   et la carte blanche du formulaire s'y détache au lieu de flotter sur du
   gris. Pas d'image — une photo de chaufferie générique ferait plus de mal
   que de bien, et le produit se défend par ce qu'il dit.
=========================================================================== */

.connexion {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1.25rem;
  padding-block: 2.5rem;
}

/* Le fond déborde la colonne : la page entière est en encre, pas seulement
   la largeur du formulaire. Le halo était un bleu écrit en dur, jamais passé
   par un jeton -- il est reste bleu quand tout le reste est devenu vert
   (D-185). C'est ce qu'un changement de charte met au jour. */
.connexion::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(1200px 600px at 50% -10%, #17233d 0%, transparent 60%),
    var(--encre);
}

.connexion__marque {
  text-align: center;
}

.connexion__nom {
  margin: .625rem 0 0;
  font-family: var(--font-titre);
  font-size: clamp(2rem, 9vw, 2.75rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: #fff;
}

.connexion__promesse {
  margin: .625rem auto 0;
  max-width: 24rem;
  font-size: var(--t-body);
  line-height: 1.5;
  color: rgb(255 255 255 / 0.72);
}

/* Trois preuves, pas dix : ce qui tient sous le pli et se lit en six
   secondes. Le tiret plutôt qu'une coche verte — une promesse n'est pas une
   fonctionnalité cochée. */
.connexion__preuves {
  list-style: none;
  margin: 1rem auto 0;
  padding: 0;
  max-width: 24rem;
  display: grid;
  gap: .375rem;
  text-align: start;
}

.connexion__preuves li {
  position: relative;
  padding-left: 1rem;
  font-size: var(--t-sm);
  line-height: 1.45;
  color: rgb(255 255 255 / 0.62);
}

.connexion__preuves li::before {
  content: "—";
  position: absolute;
  left: 0;
  color: rgb(255 255 255 / 0.32);
}

/* La carte du formulaire : blanche, posée, légèrement soulevée — c'est le
   seul objet de l'écran sur lequel on agit. */
.connexion .card {
  margin-top: 0;
  box-shadow: 0 1px 2px rgb(0 0 0 / .20), 0 18px 40px rgb(0 0 0 / .28);
  border-color: transparent;
}

.connexion .switch-mode .button--link {
  color: rgb(255 255 255 / 0.78);
}

.connexion .switch-mode .button--link:hover {
  color: #fff;
}

/* Sur un grand écran, la marque et le formulaire se partagent la largeur :
   on lit la promesse et on ouvre un compte sans déplacer le regard. */
@media (min-width: 62rem) {
  .connexion {
    max-width: 62rem;
    display: grid;
    grid-template-columns: 1fr 26rem;
    align-items: center;
    gap: 4rem;
  }

  .connexion__marque { text-align: start; }
  .connexion__promesse,
  .connexion__preuves { margin-inline: 0; }
  .connexion .card,
  .connexion .switch-mode { grid-column: 2; }
  .connexion__marque { grid-column: 1; grid-row: 1 / span 2; }
}

/* Sur le bleu nuit, un pavé sombre ne se voit pas : le monogramme s'inverse.
   Les deux jetons suffisent — le composant n'a pas à savoir où il est posé. */
.connexion .logo {
  --logo-fond: #ffffff;
  --logo-glyphe: var(--encre);
}

/* LE NOM ÉTAIT ÉCRIT DEUX FOIS. Le composant `Logo` pose le monogramme ET le
   mot ; le bloc de marque ajoutait `Sooluo` en 2,75 rem juste en dessous. Sur
   le bleu nuit, le mot du composant passait en gris pâle à droite du pavé —
   un fantôme, sur le seul écran qu'un visiteur voit avant de décider. Le
   monogramme seul, et le nom une fois, à sa vraie taille. */
.connexion__marque .logo__mot { display: none; }

/* ===========================================================================
   LES CARTES DU TABLEAU DE BORD SE PLIENT, ET S'EN SOUVIENNENT (D-166)

   L'accueil empile des blocs que tout le monde ne veut pas voir tous les
   jours. Le panneau de réglages se dépliait sans pouvoir se replier — un
   aller sans retour — et « À faire aujourd'hui » ne se pliait pas du tout :
   sur un téléphone, trois blocs ouverts repoussent les dossiers sous la ligne
   de flottaison, et ce sont eux qu'on vient chercher.

   L'en-tête devient la cible : toute la largeur, pas une flèche de douze
   pixels qu'on vise deux fois.
=========================================================================== */

.carte-pliante { padding: 0; }

.carte-pliante__entete {
  display: flex;
  align-items: center;
  gap: .625rem;
  width: 100%;
  min-height: var(--touch, 3rem);
  padding: 1rem 1.125rem;
  border: 0;
  border-radius: var(--radius-lg);
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.carte-pliante__titre {
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
  color: var(--encre);
}

/* Repliée, la carte garde son compte : « À faire aujourd'hui · 7 » en dit
   assez pour savoir s'il faut la rouvrir. */
.carte-pliante__resume {
  min-width: 1.5rem;
  padding: .0625rem .5rem;
  border-radius: 999px;
  background: var(--action-pale);
  color: var(--action-fonce);
  font-size: var(--t-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.carte-pliante__fleche {
  margin-left: auto;
  width: .5rem;
  height: .5rem;
  border-right: 2px solid var(--gris-texte);
  border-bottom: 2px solid var(--gris-texte);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 160ms ease;
}

.carte-pliante--repliee .carte-pliante__fleche {
  transform: rotate(-45deg) translate(-2px, 2px);
}

.carte-pliante__corps {
  padding: 0 1.125rem 1.125rem;
}

/* Le panneau de réglages : « Replier » à côté de « Voir les autres points ».
   Deux actions de même nature, sur la même ligne. */
.preparation__pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem 1rem;
}

.preparation__replier {
  min-height: var(--touch, 3rem);
  padding: .375rem 0;
  border: 0;
  background: none;
  color: var(--gris-texte);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
}

.preparation__replier:hover { color: var(--encre); }

.preparation--replie .preparation__pied { display: none; }

/* ---------------------------------------------------------------------------
   LE FIL DU PARCOURS SE LIT D'UN COUP D'ŒIL (D-167)

   Étapes franchies en bleu très pâle, étapes à venir en gris : neuf pastilles
   presque identiques, et aucune lecture de la progression. Le franchi devient
   plein — la course bleue s'arrête à l'étape courante, qui garde son halo et
   son libellé gras. Trois états, trois aspects.
--------------------------------------------------------------------------- */

.parcours__etape--franchie .parcours__pastille {
  background: var(--action);
  color: #fff;
  box-shadow: none;
}

.parcours__etape--franchie .parcours__lien {
  color: var(--gris-texte);
  font-weight: 500;
}

/* L'étape courante reste la plus visible : plus grande, et détachée du fil. */
.parcours__etape--active .parcours__pastille {
  width: 1.625rem;
  height: 1.625rem;
  font-size: var(--t-sm);
}

/* ---------------------------------------------------------------------------
   UN COMPTE NEUF EST ACCUEILLI, PAS CONSTATÉ VIDE (D-167)
--------------------------------------------------------------------------- */

.bienvenue__titre {
  margin: 0 0 .5rem;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: var(--serrage-titre);
  color: var(--encre);
}

.bienvenue .premiers-pas__lien {
  transition: border-color 140ms ease, transform 140ms ease;
}

.bienvenue .premiers-pas__lien:hover {
  border-color: var(--action);
  transform: translateX(2px);
}

/* ===========================================================================
   TROUVER UNE RÉFÉRENCE SANS CONNAÎTRE SON CHEMIN (D-169)

   Le champ passe avant les pastilles de marque : c'est le chemin le plus
   court vers une ligne précise, et sur un téléphone c'est le seul qui ne
   demande pas de faire défiler une grille de onze marques.

   Le bouton « Effacer » ne s'affiche que lorsqu'il y a quelque chose à
   effacer — la croix native de `input[type=search]` n'existe pas partout, et
   elle est trop petite pour le pouce de toute façon (--touch).
=========================================================================== */

.recherche-tarifs {
  display: flex;
  gap: .5rem;
  align-items: stretch;
  margin: 0 0 .75rem;
}

.recherche-tarifs__champ {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--touch, 3rem);
}

.recherche-tarifs__effacer {
  flex: 0 0 auto;
  min-height: var(--touch, 3rem);
  padding-inline: .875rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--fond-carte, #fff);
  font: inherit;
  font-size: var(--t-sm);
  color: var(--gris-texte);
  cursor: pointer;
}

.recherche-tarifs__effacer:hover { color: var(--encre); border-color: var(--encre); }

/* Le bilan tient sur une ligne quand il le peut, et se replie sinon : le
   renvoi vers l'autre métier est un bouton, pas une phrase. */
.recherche-tarifs__bilan {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .375rem .625rem;
  margin: 0 0 .75rem;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.recherche-tarifs__bilan .button--link {
  font-size: inherit;
  padding: 0;
  min-height: 0;
}

/* ===========================================================================
   L'ATELIER DES PRIX MANQUANTS (D-171)

   Un écran, une référence, un champ. Tout est dimensionné pour le pouce :
   le prix en gros chiffres parce que c'est la seule chose à lire, et les deux
   boutons pleine largeur l'un sous l'autre parce qu'à 375 px, deux boutons
   côte à côte se touchent.
=========================================================================== */

.atelier { display: grid; gap: .875rem; }

.atelier__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
}

.atelier__compteur {
  font-family: var(--font-titre);
  font-size: var(--t-body);
  color: var(--gris-texte);
  font-variant-numeric: tabular-nums;
}

.atelier__compteur strong {
  font-size: var(--t-lg);
  color: var(--encre);
}

.atelier__fermer { padding: 0; min-height: 0; font-size: var(--t-sm); }

.atelier__barre {
  height: 6px;
  border-radius: 999px;
  background: var(--filet);
  overflow: hidden;
}

.atelier__barre-jauge {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--action);
  transition: width 220ms ease;
}

.atelier__reference {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: var(--serrage-titre);
  color: var(--encre);
  text-wrap: balance;
}

.atelier__identite {
  margin: -.5rem 0 0;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

/* Les faits connus de la référence : deux colonnes tant qu'elles tiennent,
   une seule dès que le libellé déborde. */
.atelier__faits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .5rem .875rem;
  margin: 0;
  padding: .75rem .875rem;
  border-radius: var(--radius);
  background: var(--surface);
}

.atelier__faits dt {
  font-size: var(--t-xs, .8125rem);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--gris-texte);
}

.atelier__faits dd {
  margin: .125rem 0 0;
  font-size: var(--t-sm);
  color: var(--encre);
  font-variant-numeric: tabular-nums;
}

.atelier__champ { display: grid; gap: .375rem; }

.atelier__champ-libelle {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--encre);
}

/* LE PRIX EST LA SEULE CHOSE À LIRE SUR CET ÉCRAN. Il se lit à bout de bras,
   sur une tablette posée sur un plan de travail. */
.atelier__saisie {
  min-height: 3.5rem;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-variant-numeric: tabular-nums;
}

.atelier__proposition {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--action);
}

.atelier__proposition--vide { color: var(--gris-texte); }

.atelier__actions { display: grid; gap: .5rem; }

.atelier__reste { margin: 0; font-size: var(--t-sm); text-align: center; }

.atelier--fini { gap: .75rem; text-align: center; justify-items: center; }

/* À partir de la tablette, les deux boutons tiennent côte à côte — l'action
   principale d'abord, à gauche, là où le regard revient. */
@media (min-width: 600px) {
  .atelier__actions { grid-template-columns: 1fr auto; align-items: center; }
  .atelier__actions .button--block { width: auto; }
}

/* Les voisines déjà tarifées : trois lignes de trois colonnes alignées sur
   les chiffres — c'est une comparaison, elle doit se lire en colonne. */
.atelier__voisines {
  padding: .75rem .875rem;
  border-radius: var(--radius);
  border: 1px solid var(--filet);
}

.atelier__voisines-titre {
  margin: 0 0 .5rem;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--gris-texte);
}

.atelier__voisines-liste { margin: 0; padding: 0; list-style: none; display: grid; gap: .25rem; }

.atelier__voisines-liste li {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  column-gap: .75rem;
  padding-block: .25rem;
  border-top: 1px solid var(--filet);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}

.atelier__voisines-liste li:first-child { border-top: 0; }

.atelier__voisine-kw { color: var(--encre); font-weight: 600; }
.atelier__voisine-prix { color: var(--encre); font-weight: 600; text-align: end; }

/* La machine et son ratio passent en dessous, en gris : on lit d'abord la
   puissance et le prix, on vérifie ensuite de quoi il s'agit. */
.atelier__voisine-nom {
  color: var(--gris-texte);
  font-size: var(--t-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.atelier__voisine-ratio {
  color: var(--gris-texte);
  font-size: var(--t-xs);
  text-align: end;
}

/* ===========================================================================
   SUR ORDINATEUR, LA NAVIGATION PASSE SUR LE CÔTÉ (D-172)

   La barre d'onglets est née du pouce : en bas, parce que c'est là qu'il
   arrive. Sur un écran de bureau, ce raisonnement ne tient plus — la souris
   atteint tout aussi vite le haut à gauche, et une barre collée en bas d'un
   écran de 1 400 px coupe la page en deux pour rien, à 70 px du bord, là où
   personne ne regarde.

   Au-delà de 1024 px, la même barre devient un rail vertical : les sept
   destinations tiennent alors les unes sous les autres, avec leur libellé
   lisible, et la colonne de contenu récupère sa hauteur entière. Aucun
   changement de balisage — la barre est déjà une grille, elle change
   simplement de sens.
=========================================================================== */

@media (min-width: 1024px) {
  :root { --rail: 15rem; }

  .onglets {
    /* `position` ET `display` sont repris explicitement : la règle 768 px
       remet la barre en `static` + `flex` pour en faire un bandeau horizontal
       sous l'en-tête. Sans ces deux lignes, le rail héritait de ce bandeau —
       mesuré : position statique, 111 px de haut, poussé à 240 px par le
       rembourrage du corps, c'est-à-dire tout sauf un rail. */
    position: fixed;
    display: grid;
    top: 0;
    left: 0;
    right: auto;
    bottom: 0;
    width: var(--rail);
    grid-auto-flow: row;
    grid-auto-rows: max-content;
    align-content: start;
    gap: .125rem;
    padding: 1rem .625rem;
    border-top: 0;
    border-right: 1px solid var(--filet);
    box-shadow: none;
    overflow-y: auto;
  }

  /* LE RAIL VA DU HAUT EN BAS, ET LE RESTE SE DÉCALE.
     Première tentative : rail sous l'en-tête, et `padding-left` sur `.screen`.
     Raté — `.screen` est une boîte à largeur maximale centrée par des marges
     automatiques : un rembourrage intérieur ne la pousse pas, il lui mange sa
     place. Mesuré à 1440 px, le contenu commençait à 72 px, SOUS le rail.
     Le décalage appartient au conteneur, pas à la boîte centrée : il passe sur
     `body`. Le rail, lui, est en position fixe — il ignore ce rembourrage et
     reste collé au bord, ce qui est exactement ce qu'on veut. */
  body:has(.onglets) { padding-left: var(--rail); }

  /* Un onglet redevient une ligne : icône puis libellé, alignés à gauche —
     une pile de mots centrés se lit mal en colonne. */
  .onglet {
    flex-direction: row;
    justify-content: flex-start;
    gap: .625rem;
    min-height: 2.75rem;
    padding: .5rem .75rem;
    border-radius: var(--radius);
  }

  .onglet__libelle { font-size: var(--t-sm); }

  .onglet:hover { background: var(--surface); color: var(--encre); }

  /* Le trait passe du haut au côté : dans un rail, c'est le bord gauche qui
     dit « vous êtes ici ». Et l'onglet actif prend un fond, sans quoi un
     filet de 3 px se perd sur une colonne de 240 px. */
  .onglet--actif { background: var(--surface); }

  .onglet--actif::before {
    top: 50%;
    bottom: auto;          /* la règle 768 px pose `bottom: -1px` */
    left: 0;
    width: 3px;
    height: 1.5rem;
    transform: translateY(-50%);
    border-radius: 0 3px 3px 0;
  }

  /* « NOUVEAU » REMONTE EN TÊTE DU RAIL.
     La règle 768 px le renvoie en dernier (`order: 1`) avec une marge
     automatique : sa place à droite d'un bandeau horizontal, là où l'œil
     cherche l'action. Dans une colonne, la même place devient le bas de
     l'écran — l'endroit où l'on ne regarde pas. En tête, il redevient ce
     qu'il est : ce qu'on vient faire ici. */
  .onglet--action {
    order: -1;
    margin-inline-start: 0;
    margin-block: 0 .625rem;
    padding-block: .625rem;
    min-height: 2.75rem;
    border-radius: var(--radius);
    background: var(--action-encre);
    color: #fff;
  }

  .onglet--action:hover { background: var(--action-encre-fonce); color: #fff; }

  .onglet--action .onglet__icone {
    width: 1.375rem;
    height: 1.375rem;
    background: none;
    border-radius: 0;
    margin-bottom: 0;
  }

  .onglet--action .onglet__libelle { font-size: var(--t-body); }

  /* Plus de réserve en bas pour une barre qui n'y est plus. Le contenu reste
     centré — dans l'espace qui lui reste, puisque `body` est décalé. */
  body:has(.onglets) .screen { padding-bottom: 2.5rem; }
}

/* ===========================================================================
   SEPT ONGLETS NE TIENNENT PAS SUR UN TÉLÉPHONE (D-174)

   Mesuré à 375 px : sept cibles de 47 à 62 px, dont trois SOUS les 48 px que
   le design system s'impose. « Aide » et « Société » — l'une consultée à
   l'occasion, l'autre réglée une fois — passent derrière « Plus », qui ouvre
   une feuille de lignes pleine largeur. Pas un menu déroulant : des cibles à
   la bonne taille, posées là où le pouce est déjà.
=========================================================================== */

@media (max-width: 767.98px) {
  .onglet--secondaire { display: none; }
}

/* Dès la tablette, les sept tiennent : le bouton et sa feuille disparaissent,
   et les deux onglets secondaires reprennent leur place. */
@media (min-width: 768px) {
  .onglet--plus,
  .onglets__feuille,
  .onglets__voile { display: none; }
}

.onglet--plus {
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

/* Le voile ferme la feuille d'un appui n'importe où : sur un téléphone, viser
   une croix de douze pixels au coin d'une feuille est une punition. */
.onglets__voile {
  position: fixed;
  inset: 0;
  z-index: 39;
  border: 0;
  padding: 0;
  background: rgb(11 20 34 / 0.32);
  cursor: pointer;
}

/* La feuille se pose SUR la barre, pas à sa place : on doit continuer de voir
   où l'on était. */
.onglets__feuille {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(60px + env(safe-area-inset-bottom, 0px));
  z-index: 41;
  display: grid;
  padding: .375rem;
  background: var(--surface, #fff);
  border-top: 1px solid var(--filet);
  box-shadow: 0 -8px 24px rgb(11 20 34 / 12%);
  animation: feuille-monte 160ms ease both;
}

/* L'ANIMATION NE TOUCHE PLUS À LA GÉOMÉTRIE (D-174). Elle décalait la feuille
   de 8 px vers le bas au départ ; mesuré après coup, le `transform` restait
   figé sur cet état initial et la feuille mordait de 8 px sur la barre
   d'onglets. Une apparition n'a pas besoin de déplacer quoi que ce soit :
   l'opacité suffit, et elle ne peut pas fausser une position. */
@keyframes feuille-monte {
  from { opacity: 0; }
  to   { opacity: 1; }
}

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

.onglets__feuille-ligne {
  display: flex;
  align-items: center;
  gap: .75rem;
  min-height: var(--touch, 3rem);
  padding: .75rem 1rem;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--encre);
  font-size: var(--t-body);
  font-weight: 500;
}

.onglets__feuille-ligne:active { background: var(--fond-doux, var(--fond-clair)); }

/* ===========================================================================
   LES ÉCRANS DE DONNÉES REPRENNENT LA LARGEUR QU'ON LEUR AVAIT PROMISE (D-175)

   `.screen--large` était MORT. Déclaré à 64 rem ligne 1254, il était écrasé
   par la règle `.screen { max-width: 52rem }` posée 140 lignes plus bas — même
   spécificité, la dernière gagne. Mesuré à 1440 px sur la grille tarifaire :
   832 px de contenu pour 1 200 px disponibles à droite du rail, soit **69 %
   d'occupation** et 368 px de vide réparti de part et d'autre. Les trois
   écrans censés être larges — catalogue, tarifs, pilotage commercial —
   rendaient à la largeur d'un écran de lecture.

   La déclaration est reprise APRÈS la règle de base, et elle s'ouvre encore au
   -delà de 1024 px. 72 rem, pas plus : un tableau de prix plus large que
   1 150 px cesse de se balayer d'un regard, et l'œil perd sa ligne entre la
   désignation et la marge. La largeur n'est pas une fin — la lisibilité si.

   Les écrans de lecture, eux, ne bougent pas : un formulaire ou une fiche
   restent à 52 rem, parce que 65 caractères par ligne est une limite du
   lecteur, pas de l'écran.
=========================================================================== */

.screen--large { max-width: 64rem; }

@media (min-width: 1024px) {
  .screen--large { max-width: 72rem; }
}

/* ===========================================================================
   TARIFER UNE GAMME ENTIÈRE (D-176)

   Le tableau d'aperçu est le cœur de l'écran : c'est lui qui donne le courage
   d'appuyer. La colonne « Deviendra » porte donc la couleur d'action, et le
   prix actuel passe en gris — on lit le changement, pas l'état.
=========================================================================== */

.saisie-gamme { display: grid; gap: .875rem; }

.saisie-gamme__entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: .75rem;
}

.saisie-gamme__marque {
  margin: 0;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--gris-texte);
}

.saisie-gamme__titre {
  margin: .125rem 0 0;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: var(--serrage-titre);
  color: var(--encre);
}

.saisie-gamme__entete .button--link { padding: 0; min-height: 0; font-size: var(--t-sm); }

.saisie-gamme__champ { display: grid; gap: .375rem; }

.saisie-gamme__champ-libelle {
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--encre);
}

.saisie-gamme__saisie-ligne { display: flex; align-items: center; gap: .5rem; }

.saisie-gamme__saisie {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 3.5rem;
  font-family: var(--font-titre);
  font-size: var(--t-lg);
  font-variant-numeric: tabular-nums;
}

.saisie-gamme__unite {
  flex: 0 0 auto;
  font-size: var(--t-body);
  color: var(--gris-texte);
}

.saisie-gamme__repere { margin: 0; font-size: var(--t-sm); color: var(--gris-texte); }

/* La case à cocher qui écrase des prix négociés : cible pleine, jamais un
   carré de treize pixels perdu dans une phrase. */
.saisie-gamme__bascule {
  display: flex;
  align-items: center;
  gap: .625rem;
  min-height: var(--touch, 3rem);
  padding: .5rem .75rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  font-size: var(--t-sm);
  color: var(--encre);
  cursor: pointer;
}

.saisie-gamme__bascule input { width: 1.125rem; height: 1.125rem; }

/* L'aperçu ne doit pas pousser le bouton hors de l'écran : au-delà de huit
   lignes il défile dans sa propre fenêtre. */
.saisie-gamme__apercu { max-height: 22rem; overflow: auto; }

.saisie-gamme__futur { color: var(--action-fonce); font-weight: 600; }

.saisie-gamme__ecartees { margin: 0; font-size: var(--t-sm); }

/* ===========================================================================
   « MON MATÉRIEL » — DEUX ONGLETS, UN SEUL ÉCRAN (D-177)

   Des onglets, pas des boutons : ils portent leur sous-titre, parce que
   « Mes prix » ne dit pas à lui seul qu'on y trouve aussi les remises et les
   marges. Le trait sous l'onglet courant plutôt qu'un fond plein — le fond
   est déjà pris par les cartes qui suivent, et deux surfaces empilées ne
   disent plus laquelle porte l'attention.
=========================================================================== */

.materiel__onglets {
  display: flex;
  gap: .25rem;
  margin: 1rem 0 1.25rem;
  border-bottom: 1px solid var(--filet);
}

.materiel__onglet {
  display: grid;
  gap: .0625rem;
  padding: .625rem .875rem;
  min-height: var(--touch, 3rem);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  text-decoration: none;
  color: var(--gris-texte);
}

.materiel__onglet-libelle {
  font-family: var(--font-titre);
  font-size: var(--t-body);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
}

.materiel__onglet-aide {
  font-size: var(--t-xs);
  color: var(--gris-texte);
}

.materiel__onglet:hover { color: var(--encre); }

.materiel__onglet--actif {
  color: var(--encre);
  border-bottom-color: var(--action);
}

.materiel__onglet--actif .materiel__onglet-aide { color: var(--action-fonce); }

/* Sous 380 px, le sous-titre passe à la trappe avant le libellé : mieux vaut
   deux onglets lisibles qu'un mot coupé. */
@media (max-width: 380px) {
  .materiel__onglet { padding-inline: .625rem; }
  .materiel__onglet-aide { display: none; }
}

/* Le titre de l'écran ne se répète pas : chaque onglet garde son propre
   chapeau, qui devient le sous-titre de la page. */
.materiel .catalogue__intro { margin-top: 0; }

/* ===========================================================================
   LES CHIFFRES PASSENT EN CHASSE FIXE (D-178)

   Un prix, une puissance, une date ne se lisent pas : ils se comparent. Dans
   une police de texte, le 1 est plus étroit que le 8 — deux montants l'un sous
   l'autre ne s'alignent pas, et l'œil relit chiffre par chiffre au lieu de
   voir tout de suite lequel est le plus grand. C'est l'écran des tarifs
   entier, tous les indicateurs du tableau de bord, toutes les estimations.

   `tabular-nums` était déjà posé aux bons endroits : il aligne les colonnes
   d'une même police, mais ne détache pas le chiffre du texte qui l'entoure.
   La mono le fait, sans gras et sans couleur — deux ressources qu'on garde
   pour dire autre chose.

   CE QUI N'EN PREND PAS : les phrases qui contiennent un nombre. « 319
   références », « 5 marques · remise 54 % », « 12 sans prix » sont du texte,
   pas des valeurs ; en chasse fixe au milieu d'une phrase, un nombre se lit
   comme une coquille. La règle porte sur les valeurs isolées et sur les
   étiquettes de section, jamais sur la prose.
=========================================================================== */

/* `td` seulement : un en-tête de colonne est une étiquette, pas une valeur.
   « Vente HT » en chasse fixe à côté de « Désignation » en Inter, c'est une
   incohérence que l'œil attrape avant de lire le tableau. */
td.tableau__nombre,
.champ-tableau,
.resultat__valeur,
.produit__puissance,
.marge,
.atelier__compteur,
.atelier__faits dd,
.atelier__saisie,
.atelier__voisines-liste li,
.bilan__ligne > :last-child,
.catalogue-gamme__nombre,
.catalogue-marque__partiel,
.comparateur__apport,
.comparateur__modele-ref,
.comparateur__reference,
.comparateur__valeur,
.critere__part,
.diagnostic__gain,
.dossier__marge,
.dossier__montant,
.economie__valeur,
.gamme-carte__plage,
.hypotheses__ligne dd,
.kpi__valeur,
.ligne__montant,
.ligne__prix-saisie,
.piece-calcul__puissance,
.piece-ligne__num,
.piece-materiel__besoin,
.pilotage__montant,
.pilotage__valeur,
.reference__etas,
.saisie-gamme__saisie,
.unite__prix,
.ventilation__detail,
.ventilation__nombre {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* L'ÉTIQUETTE DE SECTION AUSSI. En capitales, interlettrée, elle joue déjà le
   rôle d'un repère plutôt que d'un texte : la chasse fixe le confirme. IBM
   Plex Mono rend plus grand qu'Inter à taille égale — d'où le demi-point en
   moins, mesuré pour que « RÉGLAGES » occupe la même place qu'avant. */
.screen__eyebrow,
.atelier__faits dt,
.atelier__voisines-titre,
.saisie-gamme__marque {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
}

/* ===========================================================================
   OÙ EN SONT VOS DOSSIERS — LE PIPELINE EN UNE BARRE (D-179)

   La rampe va du bleu très pâle au bleu plein : la couleur dit la distance
   qui reste jusqu'à la signature, elle ne décore pas. Quatre paliers et non
   un dégradé continu, parce qu'il y a quatre étapes et qu'on doit pouvoir
   les distinguer, pas seulement voir une progression.
=========================================================================== */

:root {
  --pipeline-1: #e8ecf7;
  --pipeline-2: #c2cff1;
  --pipeline-3: #6e8fe4;
  --pipeline-4: var(--action);
}

.pipeline-barre { display: grid; gap: .75rem; }

.pipeline-barre__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .375rem .875rem;
}

.pipeline-barre__titre {
  margin: 0;
  font-family: var(--font-titre);
  font-size: var(--t-body);
  font-weight: 600;
  letter-spacing: var(--serrage-titre);
  color: var(--encre);
}

.pipeline-barre__sous-titre {
  margin: .125rem 0 0;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.pipeline-barre__meta {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
  color: var(--encre);
}

.pipeline-barre__abandons { color: var(--gris-texte); }

/* 34 px : assez haut pour qu'un compteur tienne dedans, assez bas pour rester
   une barre et non un graphique. */
.pipeline-barre__piste {
  display: grid;
  gap: 3px;
  height: 34px;
  border-radius: var(--radius);
  overflow: hidden;
}

.pipeline-barre__segment {
  display: grid;
  place-items: center;
  min-width: 0;
  padding: 0;
  border: 0;
  font: inherit;
  cursor: pointer;
  color: var(--encre);
}

.pipeline-barre__segment--sombre { color: #fff; }

.pipeline-barre__compte {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Le segment retenu se creuse plutôt qu'il ne s'éclaire : sur une rampe de
   bleus, un bleu de plus ne se remarquerait pas. */
.pipeline-barre__segment--retenu {
  box-shadow: inset 0 0 0 2px var(--encre);
}

.pipeline-barre__segment:hover { filter: brightness(0.96); }

.pipeline-barre__legende { display: grid; gap: 3px; }

.pipeline-barre__etiquette {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-xs);
  color: var(--gris-texte);
  text-align: center;
}

/* Les deux légendes cohabitent, le CSS n'en montre qu'une. */
.pipeline-barre__liste { display: none; }

@media (max-width: 480px) {
  .pipeline-barre__legende { display: none; }

  .pipeline-barre__liste {
    display: flex;
    flex-wrap: wrap;
    gap: .375rem .875rem;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .pipeline-barre__liste li {
    display: flex;
    align-items: center;
    gap: .375rem;
    font-size: var(--t-xs);
    color: var(--gris-texte);
  }

  .pipeline-barre__pastille {
    width: .625rem;
    height: .625rem;
    border-radius: 2px;
    flex: 0 0 auto;
  }

  .pipeline-barre__liste-compte {
    font-family: var(--font-mono);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--encre);
  }
}

/* ===========================================================================
   LES CHIFFRES CLÉS DEVIENNENT QUATRE CARTES (D-180)

   C'était une seule bande, découpée par des filets intérieurs : quatre
   chiffres du même poids, posés côte à côte comme les cases d'un tableau. Ils
   se lisaient comme une ligne de tableur, pas comme quatre réponses à quatre
   questions.

   Quatre cartes bordées, avec une gouttière entre elles : chacune devient un
   objet, et l'œil peut en retenir une. L'étiquette passe en chasse fixe
   (D-178) — c'est un repère, pas un texte — et la valeur gagne en taille.

   LA GRILLE SUIT LE CONTENU. `auto-fit` avec un minimum de 11,5 rem : quatre
   de front sur un écran large, deux sur une tablette, une seule sur un
   téléphone. Aucun palier écrit à la main.
=========================================================================== */

/* LES CARTES NE S'ÉTIRENT PLUS (D-190). Avec `1fr`, quatre cases se
   partageaient 1 280 px : 426 px chacune pour porter un montant et deux mots,
   et l'oeil traversait l'écran entre deux chiffres qu'il fallait comparer.
   Elles ont maintenant une largeur maximale et se rangent à gauche : la
   rangée garde la densité qu'elle a sur un téléphone, quelle que soit la
   taille de l'écran. */
.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11.5rem, 1fr));
  gap: .75rem;
  margin-top: .25rem;
  /* La bande n'est plus un objet : ce sont les cartes qui le sont. */
  border: 0;
  border-radius: 0;
  background: none;
  overflow: visible;
}

.kpi {
  display: grid;
  align-content: start;
  gap: .125rem;
  padding: 1rem 1.125rem 1.0625rem;
  border: 1px solid var(--filet);
  border-radius: var(--radius);
  background: var(--surface);
}

/* Le filet intérieur n'a plus lieu d'être : chaque carte porte le sien. */
.kpi:first-child { border-inline-start: 1px solid var(--filet); }

.kpi__libelle {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: .1em;
}

/* LA VARIATION EST UNE LIGNE À PART, ET ELLE N'APPARAÎT QUE SI ELLE EXISTE.
   Un « +0 % » calculé sur une fenêtre vide n'informe pas : il occupe la place
   d'une information et fait croire qu'on mesure quelque chose. */
.kpi__variation {
  margin: .3125rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--gris-texte);
}

.kpi__variation--hausse { color: var(--vert-fonce); }
.kpi__variation--baisse { color: var(--danger); }

.kpi__variation-note {
  font-weight: 400;
  color: var(--gris-texte);
  letter-spacing: 0;
}
/* ===========================================================================
   LE PANNEAU DE RÉGLAGES NE CRIE QUE S'IL A UNE RAISON DE CRIER (D-183)

   « Quelques réglages amélioreraient vos documents » est, par construction,
   une phrase qui n'annonce rien d'urgent. Elle s'affichait pourtant dans un
   encadré haut de trois cartes, bordé d'orange, ombré, avec un titre écrit
   plus gros que les chiffres du jour — chaque matin, sur le premier écran,
   pour dire qu'il n'y avait rien de grave.

   Sans point bloquant, le panneau arrive replié sur tous les écrans et prend
   l'aspect d'une note : filet ordinaire, pas d'ombre, titre à la taille du
   corps de texte. Le traitement fort reste entier pour un point bloquant —
   une prime CEE à zéro se découvre devant le client, et là, interrompre est
   exactement ce qu'il faut faire.
=========================================================================== */

.preparation--discret {
  border-left: 1px solid var(--filet);
  box-shadow: none;
  background: none;
  padding-block: .75rem;
}

.preparation--discret .preparation__titre {
  font-family: var(--font-sans);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--gris-texte);
}

.preparation--discret .preparation__resume {
  font-size: var(--t-sm);
}

/* Replié ET discret, le panneau tient sur deux lignes : la phrase, et le
   bouton qui ouvre. C'est tout ce qu'il mérite tant que rien ne bloque. */
.preparation--discret.preparation--replie .preparation__resume { display: none; }

.preparation--discret .preparation__deplier {
  font-size: var(--t-sm);
}

/* Replié et sans bloquant : une seule ligne, titre à gauche, bouton à droite.
   Le `flex-wrap` protège les langues longues et les très petits écrans — il
   ne se déclenche pas à 375 px avec « Réglages recommandés · Voir les 2 ». */
.preparation--discret.preparation--replie .preparation__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .25rem .75rem;
}

.preparation--discret.preparation--replie .preparation__deplier {
  margin: 0;
  white-space: nowrap;
}

/* ===========================================================================
   SUR ORDINATEUR, L'EN-TÊTE N'EST PLUS UN BANDEAU SOMBRE (D-191)

   Un bandeau d'encre sur toute la largeur d'un écran de 1 600 px, c'est
   quatre-vingts pixels de noir au-dessus de chaque page, et c'est la première
   chose qu'on voit. Sur un téléphone il tient sa place — il compense
   l'absence de rail et pose la marque. Sur un poste de bureau, le rail fait
   déjà ce travail sur toute la hauteur : le bandeau ne fait plus que peser.

   Il devient une barre claire, séparée par un filet. La marque reprend son
   pavé sombre, le nom de l'entreprise passe en encre, et « Mode client »
   reste ce qu'il doit rester : atteignable en un geste, sur tous les écrans,
   parce qu'on le presse quand on retourne la tablette vers le client.
=========================================================================== */

@media (min-width: 1024px) {
  .app-header {
    background: var(--surface);
    border-bottom: 1px solid var(--filet);
    box-shadow: none;
    /* Le pavé du monogramme redevient sombre sur fond clair. */
    --logo-fond: var(--encre);
    --logo-glyphe: #ffffff;
  }

  .app-header,
  .app-header__tenant { color: var(--encre); }

  .app-header__role {
    background: var(--fond-clair);
    color: var(--gris-texte);
  }

  .app-header__brand .logo__mot { color: var(--encre); }

  /* Le bouton de mode client était blanc sur sombre : sur clair il lui faut
     son propre contour, sinon il flotte sans limite. */
  .mode-client {
    background: var(--surface);
    border: 1px solid var(--filet-fort);
    color: var(--encre);
  }

  .mode-client:hover { border-color: var(--encre); }
}

/* Le plafond ne vaut que sur grand écran. Sur un téléphone, une carte qui
   s'arrête à 256 px sur une colonne de 343 laisse un bord déchiqueté à
   droite : là, l'étirement est la bonne réponse. */
@media (min-width: 700px) {
  .kpis {
    grid-template-columns: repeat(auto-fit, minmax(11.5rem, 16rem));
    justify-content: start;
  }
}

/* ===========================================================================
   LA CARTE DE DOSSIER SE DENSIFIE, ET PORTE SON GESTE (D-192)

   Nom et âge sur la même ligne — « il y a 18 jours » ne se lit pas après un
   prix, il se lit AVEC le nom : c'est ce couple qui dit s'il faut s'en
   occuper. Puis un fait par ligne, chacun avec son pictogramme : « 78320 La
   Verrière · Climatisation » sur une seule ligne, séparés par un point médian,
   ce sont deux informations de nature différente lues comme une phrase.

   Et en bas, le geste suivant, écrit. La carte entière était un lien : elle
   menait quelque part sans dire où ni pourquoi. « Envoyer au client » change
   une liste de dossiers en liste de choses à faire.
=========================================================================== */

.dossier {
  display: flex;
  flex-direction: column;
  gap: .25rem;
  padding: .875rem;
}

.dossier__tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .625rem;
}

.dossier__nom {
  font-weight: 600;
  font-size: var(--t-body);
  color: var(--encre);
  text-decoration: none;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dossier__nom:hover { text-decoration: underline; }

.dossier__age {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  flex: 0 0 auto;
  font-size: var(--t-xs);
  color: var(--gris-texte);
  white-space: nowrap;
}

.dossier__age svg { flex: 0 0 auto; }

/* Un fait, une ligne, un pictogramme. L'icône est alignée sur la première
   ligne du texte et non centrée : une adresse qui passe à la ligne ferait
   glisser le repère au milieu de nulle part. */
.dossier__fait {
  display: flex;
  align-items: flex-start;
  gap: .4375rem;
  margin: 0;
  font-size: var(--t-sm);
  color: var(--gris-texte);
}

.dossier__fait svg { flex: 0 0 auto; margin-top: .125rem; color: var(--filet-fort); }

.dossier__pied { margin-top: .375rem; }

/* Le bouton ne hurle pas : c'est le geste de CETTE carte, pas l'action
   principale de l'écran — celle-là est « Nouveau », dans la barre. Un bouton
   plein par dossier ferait dix aplats bleus sur une colonne. */
.dossier__action {
  margin-top: .5rem;
  min-height: 2.375rem;
  background: var(--action-pale);
  color: var(--action-fonce);
  border: 1px solid transparent;
  font-size: var(--t-sm);
  font-weight: 600;
}

.dossier__action:hover {
  background: var(--surface);
  border-color: var(--action);
  color: var(--action-fonce);
}

/* La relance est la seule qui s'affiche en plein : c'est de l'argent qui
   dort, et c'est le seul geste de l'écran qui ait une urgence. */
.dossier--relance .dossier__action {
  background: var(--action);
  color: #fff;
}

.dossier--relance .dossier__action:hover { background: var(--action-fonce); color: #fff; }

/* ===========================================================================
   TOUTE LA CARTE REDEVIENT CLIQUABLE (D-194)

   En D-192, la carte a cessé d'être un lien pour pouvoir porter un bouton :
   un lien dans un lien est du balisage invalide. Mais l'habitude est plus
   forte que la règle — sur une tablette, on appuie sur la carte, pas sur les
   quatorze caractères de son nom.

   Le lien du nom s'étire donc sur toute la carte par un pseudo-élément
   absolu : un seul `<a>` dans le balisage, une surface de la taille de la
   carte. C'est le motif dit du « lien étiré », et il a l'avantage de ne rien
   changer à ce que lit un lecteur d'écran — il y a toujours un lien, et il
   porte toujours le nom du client.

   LE BOUTON PASSE AU-DESSUS. Il est le seul élément à remonter d'un cran :
   sans cela, la surface étirée l'avalerait et « Envoyer au client » mènerait
   au même endroit que le reste, ce qui viderait le geste de son sens.
=========================================================================== */

.dossier { position: relative; }

.dossier__nom::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.dossier__action { position: relative; z-index: 1; }

/* La carte entière réagit, pas seulement le nom : sans retour visuel sur la
   surface, rien ne dit qu'elle est devenue une cible. */
.dossier:hover { border-color: var(--filet-fort); }
.dossier:hover .dossier__nom { text-decoration: underline; }
