/* ============================================================================
   DEVIS VOCAL NC — Feuille de style
   Deux thèmes : « nuit » (atelier/soir) et « jour » (plein soleil, contraste max)
   Cibles tactiles ≥ 56 px : utilisable avec des gants.
   ========================================================================== */

:root {
  --accent:      #FF7A18;   /* orange chantier */
  --accent-2:    #17C3B2;   /* turquoise lagon */
  --ok:          #2ECC71;
  --warn:        #FFC145;
  --danger:      #FF5A5A;
  --r:           18px;
  --r-s:         12px;
  --safe-b:      env(safe-area-inset-bottom, 0px);
  --tab-h:       calc(74px + var(--safe-b));
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
}

[data-theme="nuit"] {
  /* Le bleu de « Piocher dans mes tarifs » (2026-08-12). Il est défini PAR
     THÈME, et non une fois pour toutes en haut : un bleu lisible sur fond
     blanc est trop sombre sur fond nuit, et l'inverse éblouit de jour. */
  --bleu:      #5AA9FF;
  --bg:        #0B0F14;
  --bg-2:      #0F151C;
  --surface:   #141C25;
  --surface-2: #1B2632;
  --line:      #26333F;
  --text:      #E9EFF6;
  --text-2:    #96A6B8;
  --shadow:    0 10px 30px rgba(0,0,0,.45);
  --glass:     rgba(20,28,37,.86);
  /* Teintes des bandeaux (2026-08-08). Fond sourd + texte clair, plutôt que
     l'aplat saturé d'avant. Les couleurs pleines (--danger, --warn, --ok)
     restent pour le filet de gauche : c'est lui qui donne le niveau d'alerte
     d'un coup d'œil, pas un mur de couleur. Contraste tenu : ce téléphone se
     lit en plein soleil sur un chantier. */
  --fond-danger: #2C1517;  --encre-danger: #FFD9D9;
  --fond-warn:   #2B2312;  --encre-warn:   #FFE9B8;
  --fond-ok:     #10261A;  --encre-ok:     #C8F2D8;
}
[data-theme="jour"] {
  --bleu:      #1B6FD4;   /* voir la note dans [data-theme="nuit"] */
  --bg:        #F2F5F8;
  --bg-2:      #E8EDF3;
  --surface:   #FFFFFF;
  --surface-2: #EEF3F8;
  --line:      #CBD6E2;
  --text:      #0C1620;
  --text-2:    #526074;
  --shadow:    0 8px 24px rgba(16,32,48,.12);
  --glass:     rgba(255,255,255,.9);
  --fond-danger: #FFECEC;  --encre-danger: #8A1B1B;
  --fond-warn:   #FFF4DC;  --encre-warn:   #7A4E00;
  --fond-ok:     #E4F7EC;  --encre-ok:     #0B5A2E;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 17px;
  line-height: 1.45;
  padding-bottom: calc(var(--tab-h) + 12px);
  overscroll-behavior-y: none;
  transition: background .25s, color .25s;
}
svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.9;
      stroke-linecap: round; stroke-linejoin: round; }
button { font-family: inherit; font-size: inherit; color: inherit; cursor: pointer; border: 0; background: none; }
input, select, textarea { font-family: inherit; font-size: 17px; }

/* ── LES BOUTONS-ICÔNES SE CENTRENT VRAIMENT ─────────────────────────────────
   Remonté le 2026-08-07 sur iPhone : micro, poubelle et soleil « pas centrés
   par rapport à leur case ». Ils ne le sont pourtant qu'en apparence — mesurés
   en Chromium, les trois tombent au pixel près (9/9/9/9 pour le soleil,
   12/12/12/12 pour la poubelle). Ce qui restait, et que le style calculé a
   révélé : `-webkit-appearance: auto` et le `padding: 1px 6px` que le
   navigateur pose d'office sur tout <button>. Sur iOS, un bouton laissé en
   apparence native est dessiné par le système, qui recale son contenu à sa
   façon — `place-items: center` n'a alors plus le dernier mot.
   La règle globale ci-dessus neutralise la bordure et le fond, jamais ces
   deux-là. On ne le fait QUE sur les boutons-icônes : les autres tirent leur
   confort tactile de leur propre padding, l'écraser partout les aplatirait. */
.tb-icon, .tb-back, .ligne-micro, .ligne-suppr {
  -webkit-appearance: none; appearance: none; padding: 0;
}

/* ─────────────────────────── Bandeau de démonstration ───────────────────────────
   Contraste fort volontaire (fond plein, jamais une teinte discrète) : c'est
   une protection contre l'erreur, pas une information secondaire. Pas de
   position sticky : la barre du haut (.topbar) l'est déjà en top:0, empiler
   deux éléments sticky sur le même repère les ferait se chevaucher au défilement. */
/* ═══════════════ LES TROIS BANDEAUX (refondus le 2026-08-08) ═══════════════
   Ils étaient trois aplats saturés pleine largeur, tout en 800, texte centré —
   « très agressif et très old school », dit Cédric, et il a raison : empilés,
   licence + version mangeaient plus de la moitié d'un écran de 844 px.

   Ce qui change, et pourquoi :
   · une CARTE posée dans la page (marge + coins arrondis) au lieu d'une barre
     qui traverse l'écran — un bandeau informe, il ne barre pas la route ;
   · un FILET de couleur pleine à gauche porte le niveau d'alerte, à la place
     du mur de couleur. Trois murs côte à côte ne se distinguent plus ;
   · fond sourd, encre foncée (ou claire la nuit) : le CONTRASTE reste élevé —
     ce téléphone se lit en plein soleil sur un chantier, c'est non négociable ;
   · texte à gauche, 14 px, graisse 600 : centré et en 800, un texte de trois
     lignes se lit mal, chaque ligne redémarrant à un endroit différent.

   Charpente commune aux trois ; seules les couleurs et le filet changent. */
.bandeau-demo, .bandeau-licence, .bandeau-version {
  margin: 10px 12px 0;
  padding: 12px 14px;
  border-radius: var(--r-s);
  border-left: 4px solid;
  font-size: 14px; font-weight: 600; line-height: 1.45;
  text-align: left;
}

.bandeau-demo {
  background: var(--fond-danger); color: var(--encre-danger);
  border-left-color: var(--danger);
}

/* Licence — deux niveaux : rouge quand la création est coupée, ambre pour
   l'alerte J-14 où rien n'est encore bloqué. */
.bandeau-licence-bloque {
  background: var(--fond-danger); color: var(--encre-danger);
  border-left-color: var(--danger);
}
.bandeau-licence-alerte {
  background: var(--fond-warn); color: var(--encre-warn);
  border-left-color: var(--warn);
}

/* ── JAUGE DE DICTÉES (2026-08-11) ───────────────────────────────────────────
   Même charpente que les bandeaux ci-dessus — mêmes marges, même filet à
   gauche, même graisse — parce que c'est le même objet pour l'artisan : un
   rappel discret en haut de l'écran d'accueil.

   TROIS TONS — un feu tricolore, seuils fixés par Cédric le 2026-08-11 :
     · calme  — VERT, en dessous de 20/40. C'est le ton de la plus grande
                partie du mois : il doit dire « tout va bien », pas « éteint ».
     · tiède  — ORANGE, de 20/40 à 34/40. Il a le temps de s'organiser.
     · chaude — ROUGE, à partir de 35/40. Là il faut qu'il puisse appeler.
   ⚠️ Ces seuils remplacent ceux de la veille (30 puis 36) et valent pour les
   TROIS emplacements — en-tête, accueil, Réglages. Un feu tricolore qui ne dit
   pas la même chose partout ne sert à rien.
   La jauge est visible en permanence sur l'accueil depuis le 2026-08-11 (choix
   de Cédric, à l'inverse de ce que faisait la première version).
   Le neutre (mois inconnu ou périmé) emprunte le fond de surface : ce n'est
   ni une alerte ni une bonne nouvelle, c'est une absence d'information.

   La piste et son remplissage sont en `currentColor` à opacité réduite : une
   seule déclaration de couleur par ton, donc pas de tons qui divergent. */
.jauge {
  margin: 10px 12px 0;
  padding: 12px 14px;
  border-radius: var(--r-s);
  border-left: 4px solid;
  font-size: 14px; line-height: 1.45; text-align: left;
}
.jauge b { font-weight: 700; display: block; }
.jauge span { font-weight: 600; opacity: .82; }
.jauge-tete { display: flex; flex-wrap: wrap; gap: 2px 10px; align-items: baseline; }
.jauge-piste {
  margin-top: 8px; height: 8px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 18%, transparent);
  overflow: hidden;
}
.jauge-piste i {
  display: block; height: 100%; border-radius: 999px;
  background: currentColor;
  /* Toujours visible même à une dictée sur quarante : une barre invisible
     ferait croire à un rendu cassé. */
  min-width: 3px;
}
/* Vert / orange / rouge — seuils fixés par Cédric le 2026-08-11 : vert en
   dessous de 20, orange de 20 à 34, rouge à partir de 35. Le vert n'est pas
   décoratif : c'est le seul ton que l'artisan voit la plupart du mois, et un
   gris se lit « éteint » alors qu'il veut dire « tout va bien ».
   Neutre (mois inconnu ou périmé, et la bande de remise à zéro) : ni alerte ni
   bonne nouvelle — une information de calendrier, pas un niveau. */
.jauge-calme   { background: var(--fond-ok);     color: var(--encre-ok);     border-left-color: var(--ok); }
.jauge-inconnue{ background: var(--surface-2); color: var(--text-2); border-left-color: var(--line); }
.jauge-neutre  { background: var(--surface-2); color: var(--text-2); border-left-color: var(--line); }
.jauge-tiede   { background: var(--fond-warn);   color: var(--encre-warn);   border-left-color: var(--warn); }
.jauge-chaude  { background: var(--fond-danger); color: var(--encre-danger); border-left-color: var(--danger); }

/* ── LA JAUGE DE L'EN-TÊTE ───────────────────────────────────────────────────
   Même hauteur que le bouton jour/nuit (42 px) pour que la barre du haut reste
   une ligne, et pas deux objets de tailles différentes posés côte à côte.
   Le chiffre au-dessus, la barre en dessous : la barre seule ne dit pas
   « 12 sur 40 », et le chiffre seul ne se lit pas d'un coup d'œil.
   Un seul ton par état, porté par `color` : le texte, la piste et le
   remplissage en descendent tous les trois — donc ils ne peuvent pas diverger. */
.tb-jauge {
  height: 42px; min-width: 52px; padding: 0 9px; border-radius: 12px;
  display: grid; align-content: center; justify-items: center; gap: 5px;
  background: var(--surface-2);
  font-variant-numeric: tabular-nums;   /* « 9/40 » et « 10/40 » ne dansent pas */
}
.tb-jauge b { font-size: 11px; font-weight: 800; line-height: 1; letter-spacing: .2px; }
.tb-jauge-piste {
  display: block; width: 100%; height: 4px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 24%, transparent);
  overflow: hidden;
}
.tb-jauge-piste i {
  display: block; height: 100%; border-radius: 999px;
  background: currentColor;
  min-width: 3px;                       /* une dictée doit se voir */
  transition: width .3s ease;
}
/* ⚠️ FOND TEINTÉ + ENCRE, JAMAIS LA COULEUR PURE EN TEXTE. Premier jet :
   `color: var(--warn)` sur le fond de surface — c'est-à-dire du #FFC145 sur du
   #EEF3F8, autour de 1,6:1. Illisible, et illisible précisément là où ce
   produit se vend : un téléphone tenu à bout de bras en plein soleil sur un
   chantier. Les paires fond/encre existent déjà pour les trois niveaux et sont
   tenues dans les deux thèmes — on les reprend, ce qui accorde en prime la
   pastille de l'en-tête avec la carte de l'accueil, couleur pour couleur. */
.tb-jauge-calme  { background: var(--fond-ok);     color: var(--encre-ok); }
.tb-jauge-tiede  { background: var(--fond-warn);   color: var(--encre-warn); }
.tb-jauge-chaude { background: var(--fond-danger); color: var(--encre-danger); }

/* Mise à jour — vert : rien n'est cassé, rien n'est bloqué, c'est une bonne
   nouvelle. Le bouton reste à 44 px de haut, comme toutes les cibles tactiles
   de l'app, mais il ne prend plus toute la largeur : il se range à gauche sous
   le texte, il n'a pas à crier. */
.bandeau-version {
  background: var(--fond-ok); color: var(--encre-ok);
  border-left-color: var(--ok);
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
}
.bandeau-version button {
  min-height: 44px; padding: 8px 16px; border-radius: var(--r-s);
  background: var(--ok); color: #06210F; border: none;
  font: inherit; font-size: 14px; font-weight: 700;
}

/* ─────────────────────────── En-tête ─────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 10px;
  padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 12px;
  background: var(--glass);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.tb-title { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
.tb-mark {
  width: 40px; height: 40px; border-radius: 12px; flex: none;
  display: grid; place-items: center; font-weight: 800; font-size: 15px; letter-spacing: .5px;
  color: #10161D; background: linear-gradient(140deg, var(--accent), #FFB627);
  box-shadow: 0 4px 14px rgba(255,122,24,.35);
  position: relative; overflow: hidden;
}
.tb-mark-txt { position: relative; z-index: 2; }

/* ═══════════ LA MARQUE SE REMPLIT PENDANT QUE L'APP TRAVAILLE ═══════════
   Le problème, observé sur l'écran de dictée : entre la fin de la parole et
   le devis affiné, il s'écoule 20 à 45 secondes pendant lesquelles le seul
   signe de vie est une phrase de texte. Le gros bouton micro, lui, est inerte
   — 240 px d'orange qui ne disent rien. Un artisan debout sur un chantier n'a
   aucune raison de croire que l'application n'a pas planté.

   La marque devient donc une cuve que le fluide remplit. C'est le même objet
   que le logo, aux deux mêmes couleurs (--accent → #FFB627), et il est déjà
   visible depuis n'importe quel écran : rien à ajouter à l'interface, aucun
   deuxième logo, aucune barre de progression générique.

   HONNÊTETÉ DU NIVEAU — la règle qui commande tout le reste. On ne connaît
   pas l'avancement réel : le modèle ne rend rien avant d'avoir tout rendu.
   Le niveau monte donc de façon asymptotique vers 92 % (voir app.js) et ne
   touche JAMAIS le haut de lui-même. Le remplissage complet n'arrive que
   lorsque le devis est réellement là. Une jauge qui atteindrait 100 % avant
   la fin serait un mensonge de plus à l'écran — c'est exactement ce que ce
   chantier passe son temps à retirer.                                      */

.tb-liquide { display: none; }

.tb-mark.travaille { background: #FFE4D1; }        /* la cuve vide */
.tb-mark.travaille .tb-liquide,
.tb-mark.plein     .tb-liquide {
  display: block; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, #FFB627, var(--accent));
  /* translateY plutôt que height : aucune propriété de mise en page animée,
     donc pas de recalcul de rendu à chaque image sur un vieil iPhone. */
  transform: translateY(calc(100% - var(--niveau, 0) * 100%));
  transition: transform .55s linear;
}
.tb-mark.plein .tb-liquide { transition: transform .4s cubic-bezier(.16, 1, .3, 1); }

/* La surface du liquide — deux ondes de périodes et de vitesses différentes,
   dont une à contresens : c'est leur battement qui donne l'impression d'un
   fluide plutôt que d'un rectangle qui monte. Les deux dérivent d'exactement
   une période, donc la boucle est invisible. */
.tb-mark.travaille .tb-liquide::before,
.tb-mark.travaille .tb-liquide::after {
  content: ""; position: absolute; left: 0; bottom: 100%; width: 200%;
}
.tb-mark.travaille .tb-liquide::before {
  height: 6px; background-size: 24px 6px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='6' viewBox='0 0 24 6'%3E%3Cpath d='M0 3 q3 -3 6 0 t6 0 t6 0 t6 0 V6 H0 Z' fill='%23FFB627'/%3E%3C/svg%3E") repeat-x;
  animation: marqueDerive 2.6s linear infinite;
}
.tb-mark.travaille .tb-liquide::after {
  height: 5px; background-size: 18px 5px; opacity: .5;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='5' viewBox='0 0 18 5'%3E%3Cpath d='M0 2.5 q2.25 -2.5 4.5 0 t4.5 0 t4.5 0 t4.5 0 V5 H0 Z' fill='%23FFD79A'/%3E%3C/svg%3E") repeat-x;
  animation: marqueDeriveB 1.7s linear infinite reverse;
}
@keyframes marqueDerive  { to { transform: translateX(-24px); } }
@keyframes marqueDeriveB { to { transform: translateX(-18px); } }

/* Respiration de la cuve : le halo orange enfle et retombe. C'est ce qui
   distingue « ça travaille » de « c'est figé » quand le niveau, lui, monte
   trop lentement pour se voir d'un coup d'œil. */
.tb-mark.pompe { animation: marquePompe 2.4s ease-in-out infinite; }
@keyframes marquePompe {
  0%, 100% { box-shadow: 0 4px 14px rgba(255,122,24,.35); }
  50%      { box-shadow: 0 4px 22px rgba(255,122,24,.6); }
}

/* Le seul moment vraiment mis en scène : le devis est arrivé, la cuve
   déborde. Court, une seule fois, jamais en boucle. */
.tb-mark.plein { animation: marqueDebordement .5s cubic-bezier(.16, 1, .3, 1); }
@keyframes marqueDebordement {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.1); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  /* Le mouvement disparaît, le changement d'état reste : le niveau continue
     de monter, c'est lui qui porte l'information. */
  .tb-mark.pompe, .tb-mark.plein,
  .tb-mark.travaille .tb-liquide::before,
  .tb-mark.travaille .tb-liquide::after { animation: none; }
}
.tb-title > div { min-width: 0; }
.tb-title strong, .tb-title small { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tb-title strong { font-size: 17px; line-height: 1.15; }
.tb-title small { color: var(--text-2); font-size: 12px; }
.tb-actions { display: flex; align-items: center; gap: 8px; }
.tb-icon, .tb-back {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center;
  background: var(--surface-2); color: var(--text-2);
}
.tb-icon:active, .tb-back:active { transform: scale(.94); }
.pastille {
  font-size: 10px; font-weight: 700; letter-spacing: .6px;
  padding: 5px 8px; border-radius: 999px;
  color: var(--accent-2); background: color-mix(in srgb, var(--accent-2) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-2) 35%, transparent);
}

/* ─────────────────────────── Écrans ─────────────────────────── */
.ecran { padding: 18px 16px 8px; max-width: 760px; margin: 0 auto; animation: fadeUp .28s ease both; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(10px);} to { opacity: 1; transform: none;} }

.bloc { margin: 22px 0; }
.bloc-tete { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 12px; gap: 12px; }
.bloc-tete h2, .h2-seul { font-size: 15px; text-transform: uppercase; letter-spacing: 1.1px; color: var(--text-2); margin: 0 0 12px; font-weight: 700; }
.bloc-tete h2 { margin: 0; }
.aide { font-size: 14px; color: var(--text-2); margin: 6px 0 12px; }
.aide code { background: var(--surface-2); padding: 1px 5px; border-radius: 5px; font-size: 12px; }
.lien { color: var(--accent); font-weight: 600; font-size: 14px; }
.lien-sec { display: block; margin: 16px auto 0; color: var(--text-2); text-decoration: underline; text-underline-offset: 4px; font-size: 15px; }
.badge { background: var(--surface-2); color: var(--text-2); border-radius: 999px; padding: 3px 10px; font-size: 13px; font-weight: 700; }
.version { text-align: center; color: var(--text-2); font-size: 12px; margin: 28px 0 0; }

/* ─────────────────────────── Accueil ─────────────────────────── */
.hero { text-align: center; padding: 14px 0 8px; }
.hero-hello { color: var(--text-2); font-size: 15px; margin: 0 0 4px; }
.hero-titre { font-size: 40px; line-height: 1.03; margin: 0 0 12px; letter-spacing: -1.4px; font-weight: 800; }
.hero-titre span { background: linear-gradient(100deg, var(--accent), #FFC145 60%, var(--accent-2)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero-sub { color: var(--text-2); font-size: 15.5px; max-width: 430px; margin: 0 auto 26px; }

.mic-cta {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin: 0 auto; padding: 26px 34px 22px; border-radius: 26px;
  background: linear-gradient(150deg, var(--accent), #FF9E2C);
  color: #14181C; font-weight: 800; font-size: 18px; letter-spacing: -.2px;
  box-shadow: 0 14px 34px rgba(255,122,24,.34);
}
.mic-cta .mic-svg { width: 42px; height: 42px; stroke-width: 2; }
.mic-cta:active { transform: scale(.97); }
.mic-ring { position: absolute; inset: -6px; border-radius: 30px; border: 2px solid rgba(255,122,24,.35); animation: pulse 2.6s ease-out infinite; pointer-events: none; }
.mic-ring.r2 { animation-delay: 1.3s; }
@keyframes pulse { 0% { transform: scale(1); opacity: .8;} 100% { transform: scale(1.16); opacity: 0;} }

.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 28px; }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 14px 12px; text-align: center; }
.stat b { display: block; font-size: 20px; font-weight: 800; letter-spacing: -.5px; }
.stat span { font-size: 11.5px; color: var(--text-2); text-transform: uppercase; letter-spacing: .6px; }
.stat.accent b { color: var(--accent); }
.stat.ok b { color: var(--ok); }

/* Rappel de sauvegarde */
.rappel {
  display: block; width: 100%; text-align: left; margin: 18px 0 0;
  border-radius: var(--r-s); padding: 12px 14px; font-size: 14px; line-height: 1.4;
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--ok) 40%, transparent); color: var(--text);
}
.rappel.danger {
  background: color-mix(in srgb, var(--warn) 16%, transparent);
  border-color: color-mix(in srgb, var(--warn) 50%, transparent);
}
.rappel.argent {
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
}
.rappel.large { padding: 15px 16px; }
.rappel b { display: block; font-size: 15px; margin-bottom: 2px; }
.rappel span { color: var(--text-2); font-size: 13.5px; }
.rappel.large:active { transform: scale(.99); }

/* Bandeau d'INFORMATION — jamais d'action derrière.
   Défaut trouvé par Cédric sur son téléphone (2026-08-17) : l'encadré « une
   dictée n'a pas été terminée » utilisait .rappel, la classe des cartes
   ACTIONNABLES de l'accueil (elles sont toutes des <button>) — un artisan qui
   touchait le texte n'obtenait rien, seule la petite puce en dessous
   marchait. Deux autres blocs du même genre (#etatFileAudio, #etatTranscription,
   #etatSauvegarde) partageaient la même classe pour la même mauvaise raison :
   « ça a la couleur qu'il faut », sans se soucier de ce que la classe PROMET
   visuellement. `.avis` porte le même habillage que `.rappel` — bordure de
   couleur, fond teinté — mais AUCUN de ses appels ne doit jamais devenir un
   bouton : c'est la classe réservée aux `<div>` qui informent sans rien faire
   faire. Un contrôle du corpus de navigateur (tous-les-boutons.mjs) vérifie
   que tout `.rappel` du DOM est bien un bouton (ou en contient un) — cette
   classe existe pour que les blocs sincèrement informatifs n'aient plus à
   tricher avec `.rappel` pour être vus. */
.avis {
  display: block; width: 100%; text-align: left; margin: 18px 0 0;
  border-radius: var(--r-s); padding: 12px 14px; font-size: 14px; line-height: 1.4;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
}
.avis.ok {
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  border-color: color-mix(in srgb, var(--ok) 40%, transparent); color: var(--text);
}
.avis.attention {
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  border-color: color-mix(in srgb, var(--warn) 45%, transparent); color: var(--text);
}
.avis.danger {
  background: color-mix(in srgb, var(--warn) 16%, transparent);
  border-color: color-mix(in srgb, var(--warn) 50%, transparent); color: var(--text);
}

/* Le bouton « Non, la supprimer » de la dictée interrompue vit désormais EN
   DEHORS du bouton de reprise (voir majDicteeInterrompue, app.js) — un peu
   d'espace au-dessus pour qu'il ne colle pas au bouton qui le précède. */
#dicteeInterrompue .lien { display: block; margin-top: 10px; }

.chip {
  border: 1px solid var(--line); background: var(--surface); border-radius: 999px;
  padding: 11px 15px; font-size: 14.5px; font-weight: 600; display: flex; align-items: center; gap: 7px;
}
.chip:active { background: var(--surface-2); }

/* ─────────────────────────── Cartes devis ─────────────────────────── */
.cartes { display: flex; flex-direction: column; gap: 10px; }
.carte {
  display: flex; align-items: center; gap: 12px; text-align: left; width: 100%;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 14px; box-shadow: var(--shadow);
}
.carte:active { transform: scale(.99); }

/* ── Gestes sur les cartes : glissement à gauche + appui prolongé ──
   Le conteneur porte l'action rouge ; la carte glisse par-dessus.
   `overflow:hidden` sur le conteneur suffit à masquer le bouton au repos —
   pas besoin de le déplacer, donc pas de second élément à animer.
   `touch-action: pan-y` laisse le défilement vertical à la page : sans lui,
   le navigateur et notre code se disputent le même doigt.
   `-webkit-touch-callout: none` supprime la loupe et le menu « Copier » que
   Safari iOS ouvre au bout de ~0,5 s — exactement au moment où notre appui
   prolongé doit se déclencher. Vérifié : sans cette ligne, le geste est mangé. */
.glissable { position: relative; overflow: hidden; border-radius: var(--r); }
.glissable-fond { position: absolute; inset: 0; display: flex; justify-content: flex-end; }
.glissable-suppr {
  width: 112px; flex: none; background: var(--danger); color: #fff;
  font-weight: 800; font-size: 14px; border: 0; border-radius: 0;
}
.glissable-suppr:active { filter: brightness(.88); }
.glissable .carte {
  position: relative; transition: transform .18s ease;
  touch-action: pan-y; -webkit-touch-callout: none; user-select: none; -webkit-user-select: none;
}
/* Inline `transform` (posé par le doigt) l'emporte sur toute règle CSS : le
   scale d'appui ne peut donc pas annuler un glissement en cours. */
.glissable.ouvert .carte { box-shadow: -6px 0 12px -8px rgba(0,0,0,.35); }

/* Retour en arrière après suppression. Au-dessus de la barre d'onglets, et
   avec une vraie cible de 44 px : un devis supprimé par erreur doit se
   récupérer d'un doigt, pas d'une manœuvre. */
.barre-annuler {
  position: fixed; left: 12px; right: 12px; bottom: calc(var(--tab-h) + 12px);
  z-index: 190; display: flex; align-items: center; gap: 12px;
  background: var(--text); color: var(--bg); border-radius: 14px;
  padding: 10px 10px 10px 16px; box-shadow: var(--shadow);
  font-size: 14.5px; font-weight: 700;
  animation: monteEnBas .22s ease both;
}
/* Surtout PAS « toastIn » ici : ses keyframes contiennent translate(-50%),
   destiné au toast qui est ancré à left:50%. Sur un élément déjà positionné
   (barre à gauche/droite, feuille centrée en grille), ce -50 % le décale de
   la moitié de sa largeur et le sort de l'écran. Vu au rendu le 2026-08-04. */
@keyframes monteEnBas { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
.barre-annuler span { flex: 1; min-width: 0; }
/* Fond clair et texte sombre, et NON l'orange habituel : blanc sur orange
   plafonne à 2,6:1 (calcul WCAG, pas une impression), très en dessous des
   4,5:1 exigés à 14,5 px. Ici on est à 19:1. C'est le seul bouton de
   l'application qu'on presse dans l'urgence d'un faux geste — il doit se
   voir en plein soleil, y compris à travers un écran sale. */
.barre-annuler button {
  flex: none; min-height: 40px; padding: 0 16px; border: 0; border-radius: 10px;
  background: var(--bg); color: var(--text); font-weight: 800; font-size: 14.5px;
}
.barre-annuler button:active { filter: brightness(.9); }

/* Feuille d'envoi : même modale, ancrée en bas — c'est là que se trouve le
   pouce quand l'appui prolongé vient de se produire. */
/* « .modale.feuille » et non « .feuille » seul : la règle .modale plus bas
   pose place-items:center, qui réécrirait align-items à égalité de poids. */
.modale.feuille { align-items: end; padding-bottom: calc(var(--tab-h) + 12px); }
.feuille-boite { max-width: 460px; animation: monteEnBas .22s ease both; }
.carte-ic { width: 44px; height: 44px; border-radius: 13px; flex: none; display: grid; place-items: center; font-size: 20px; background: var(--surface-2); }
.carte-txt { flex: 1; min-width: 0; }
.carte-txt b { display: block; font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Le nom du client tient sur DEUX lignes plutôt que de finir en « … » : sur une
   carte de 343 px, l'icône (44) et le montant à droite ne lui laissent que
   ~180 px, et l'ellipse amputait un patronyme sur deux — remonté du terrain.
   Le clamp garde la carte à hauteur bornée, et .carte est en align-items:center :
   l'icône et le montant restent centrés quand le titre passe à deux lignes. */
.carte-txt b {
  white-space: normal; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.carte-txt small { color: var(--text-2); font-size: 13px; }
.carte-mt { text-align: right; flex: none; }
.carte-mt b { display: block; font-size: 15.5px; font-weight: 800; letter-spacing: -.3px; }
.etiq { font-size: 10.5px; font-weight: 800; letter-spacing: .5px; padding: 3px 8px; border-radius: 999px; text-transform: uppercase; }
.etiq.brouillon { background: color-mix(in srgb, var(--text-2) 18%, transparent); color: var(--text-2); }
.etiq.envoye    { background: color-mix(in srgb, var(--warn) 20%, transparent); color: var(--warn); }
.etiq.accepte   { background: color-mix(in srgb, var(--ok) 20%, transparent); color: var(--ok); }
.etiq.refuse    { background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); }
.etiq.facture   { background: color-mix(in srgb, var(--accent-2) 20%, transparent); color: var(--accent-2); }
/* Avenant : pastille EN PLUS du statut, donc posée sur sa propre ligne pour ne
   pas rétrécir le montant quand les deux cohabitent. Contour plutôt qu'aplat —
   ce n'est pas un état du document, c'est sa nature. */
.etiq.avenant {
  display: inline-block; margin-top: 4px;
  background: transparent; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.vide { text-align: center; color: var(--text-2); padding: 34px 16px; font-size: 15px; }

/* ─────────────────────────── Dictée ─────────────────────────── */
/* ⚠️ PLUS de padding-bottom ICI depuis la fusion des écrans (étape B2,
   2026-08-17) : `.dictee-wrap` n'est plus toute la page, elle n'est qu'un
   bloc parmi d'autres dans `#ecran-devis` (l'éditeur du devis peut la
   suivre juste en dessous). La réserve pour les barres fixes du bas se fait
   maintenant UNE SEULE FOIS, sur `#ecran-devis` lui-même — voir plus bas,
   « .dictee-wrap { padding-bottom } plus haut » n'existe plus, c'est cette
   ligne-ci la référence désormais. */
.dictee-etat { text-align: center; color: var(--text-2); font-size: 14px; letter-spacing: .4px; text-transform: uppercase; font-weight: 700; }
/* Les capitales conviennent aux états courts (« Prêt à écouter », « Transcription… »)
   pour lesquels ce bloc a été dessiné. Elles deviennent illisibles sur une vraie
   phrase — vu le 2026-08-04 sur le message de quota, quarante mots hurlés à un
   artisan qui vient de se faire refuser sa dictée. Au-delà d'une ligne, on
   revient en casse normale, aligné à gauche, avec un interligne de lecture. */
.dictee-etat.etat-long {
  text-transform: none; letter-spacing: 0; font-weight: 600;
  text-align: left; line-height: 1.5; font-size: 15px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-s); padding: 12px 14px;
}
.dictee-etat.etat-long.etat-ko { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); }
.dictee-etat.on { color: var(--accent); }
.scene-mic { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 18px 0 8px; }
.ondes { display: flex; align-items: flex-end; gap: 4px; height: 46px; opacity: .28; transition: opacity .3s; }
.ondes i { width: 5px; height: 8px; border-radius: 3px; background: var(--accent); transition: height .12s ease; }
.ondes.on { opacity: 1; }
.mic-big {
  width: 132px; height: 132px; border-radius: 50%;
  background: radial-gradient(circle at 35% 28%, #FF9E2C, var(--accent) 62%);
  color: #14181C; display: grid; place-items: center;
  box-shadow: 0 12px 40px rgba(255,122,24,.4), inset 0 -4px 12px rgba(0,0,0,.18);
  transition: transform .16s;
}
.mic-big .mic-svg { width: 54px; height: 54px; stroke-width: 2; }
/* Micro au repos, carré « stop » pendant l'enregistrement : le bouton doit dire
   lui-même ce qu'il fait si on le touche. */
.mic-big .stop-svg { display: none; }
.mic-big.on .mic-svg { display: none; }
.mic-big.on .stop-svg { display: block; width: 46px; height: 46px; fill: currentColor; stroke: none; }
.mic-big:active { transform: scale(.95); }
.mic-big.on { background: radial-gradient(circle at 35% 28%, #FF6B6B, #E03131 62%); animation: battement 1.5s ease-in-out infinite; }
@keyframes battement { 0%,100% { box-shadow: 0 12px 40px rgba(224,49,49,.45);} 50% { box-shadow: 0 12px 62px rgba(224,49,49,.8);} }
.chrono { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--text-2); font-size: 15px; }

/* ── PAUSE ET REMISE À ZÉRO PENDANT LA DICTÉE ───────────────────────────────
   Deux gestes de nature opposée, donc deux couleurs et deux tailles. L'orange
   est réversible et large (on y revient souvent) ; le rouge détruit du travail,
   il est nommé en toutes lettres et volontairement plus étroit — on ne l'atteint
   pas par accident avec le pouce en cherchant la pause. Couleurs demandées
   telles quelles au premier test terrain. */
/* Trois boutons depuis le 2026-08-07, donc ils passent à la ligne : à 375 px,
   « ⏸ Pause », « ✓ J'ai fini de parler » et « ⟲ Repartir à zéro » côte à côte
   se réduiraient à des cibles trop étroites pour un pouce ganté. */
.dictee-cmd { display: flex; gap: 10px; margin: 14px 0 4px; flex-wrap: wrap; }
.btn-pause, .btn-zero, .btn-fini { min-height: 52px; border-radius: 12px; font-size: 15.5px;
  font-weight: 800; border: 1.5px solid; }
/* Rangée 1 : « Pause » et « Terminer », les deux gestes réversibles, à égalité.
   Rangée 2 : « Repartir à zéro » seul et en pleine largeur — c'est le seul qui
   détruit du travail, et il était déjà tenu à l'écart pour cette raison. */
.btn-pause { flex: 1 1 0; color: #8A4B00; background: #FFF1E0; border-color: var(--accent); }
.btn-pause.reprise { color: #157A46; background: #E6F7EE; border-color: var(--ok); }
.btn-fini { flex: 1 1 0; color: #157A46; background: #E6F7EE; border-color: var(--ok); }
.btn-zero { flex: 1 0 100%; color: #A32A18; background: #FFF0ED; border-color: var(--danger); }
.btn-pause:active { background: #FFE2C4; }
.btn-zero:active { background: #FFDCD6; }
.btn-fini:active { background: #CFEFDD; }
@media (prefers-color-scheme: dark) {
  .btn-pause { color: #F3B94E; background: #2A2113; }
  .btn-pause.reprise { color: #5BD394; background: #12281C; }
  .btn-zero { color: #F3907C; background: #2B1713; }
  /* Mêmes valeurs que .btn-pause.reprise : les deux disent « on repart /
     on conclut », un fond clair y serait aveuglant de nuit. */
  .btn-fini { color: #5BD394; background: #12281C; }
}

/* Discret tant qu'il ne se passe rien : c'est un rappel, pas une alerte, et il
   est sous les yeux pendant TOUTE la dictée. Il ne prend de la couleur que
   quand il devient utile — dix secondes sans un mot. Sans cette retenue, un
   encadré orange permanent sous les boutons deviendrait invisible en trois
   dictées, comme tout ce qui crie sans raison. */
.astuce-pause {
  margin: 2px 0 0; padding: 9px 12px; font-size: 14px; line-height: 1.4;
  color: var(--text-2); background: transparent;
  border: 1px solid transparent; border-radius: 10px;
  transition: color .25s, background-color .25s, border-color .25s;
}
.astuce-pause b { color: var(--text); }
.astuce-pause.actif {
  color: #8A4B00; background: #FFF1E0; border-color: var(--accent);
}
.astuce-pause.actif b { color: #8A4B00; }
@media (prefers-color-scheme: dark) {
  .astuce-pause.actif { color: #F3B94E; background: #2A2113; }
  .astuce-pause.actif b { color: #F3B94E; }
}
@media (prefers-reduced-motion: reduce) { .astuce-pause { transition: none; } }

.transcript-box { margin-top: 16px; }
.mini-label { display: block; font-size: 11.5px; text-transform: uppercase; letter-spacing: 1px; color: var(--text-2); margin-bottom: 7px; font-weight: 700; }
.transcript {
  min-height: 108px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 15px; font-size: 17px; line-height: 1.5; outline: none;
}
.transcript:focus { border-color: var(--accent); }
.transcript:empty::before { content: attr(data-placeholder); color: var(--text-2); }
.transcript .interim { color: var(--text-2); font-style: italic; }
/* Surlignage des noms de pièce dans la dictée affichée (2026-08-13) — pour
   qu'un artisan qui relit repère « cuisine », « salon », etc. d'un coup
   d'œil. Réutilise --fond-danger/--encre-danger : c'est déjà le rose très
   clair (jour) / rouge doux sur fond sombre (nuit) de la palette — voir
   .jauge-chaude plus bas — pas une couleur nouvelle. Aucun rapport avec une
   erreur ici : c'est simplement le seul « rouge pâle » que le thème connaît,
   dans les deux modes. */
.piece-surlignee { background: var(--fond-danger); color: var(--encre-danger); border-radius: 4px; padding: 0 2px; }
.transcript-actions { display: flex; gap: 8px; margin-top: 10px; }

/* Bandeau « texte corrigé après l'analyse ». Volontairement en jaune d'attention
   et non en rouge d'erreur : rien n'est cassé, l'aperçu est juste — il est
   seulement possible qu'il ignore une prestation ajoutée au clavier. */
.revalid { margin-top: 10px; padding: 12px 14px; border-radius: var(--r-s);
  background: #FFF8E8; border: 1px solid #E8C86A; }
.revalid-txt { margin: 0 0 10px; font-size: 14.5px; line-height: 1.5; color: #7A5A0A; }
.revalid button { width: 100%; min-height: 48px; }
@media (prefers-color-scheme: dark) {
  .revalid { background: #2A2312; border-color: #5C4A18; }
  .revalid-txt { color: #F0D28A; }
}

/* Total de l'aperçu de dictée. Le TTC est la ligne que l'artisan cherche du
   regard avant de valider : elle est nettement plus grosse que les autres, et
   séparée par un trait. Chiffres en tabular-nums pour qu'ils s'alignent. */
.pv-total { margin-top: 12px; padding: 12px 14px; border-radius: var(--r-s);
  background: var(--surface-2); border: 1px solid var(--line); }
.pv-total-l { display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; padding: 3px 0; font-size: 14.5px; color: var(--text-2); }
.pv-total-l b { font-variant-numeric: tabular-nums; color: var(--text); font-weight: 600; }
.pv-total-ttc { margin-top: 6px; padding-top: 9px; border-top: 1px solid var(--line);
  font-size: 16px; color: var(--text); font-weight: 700; }
.pv-total-ttc b { font-size: 22px; font-weight: 800; }
.pv-total-note { margin: 8px 0 0; font-size: 13px; line-height: 1.45; color: #9A5B04; }
@media (prefers-color-scheme: dark) { .pv-total-note { color: #F3B94E; } }

.preview-lignes { display: flex; flex-direction: column; gap: 8px; }
.pv {
  display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s);
  animation: fadeUp .22s ease both;
}
.pv-q { font-weight: 800; color: var(--accent); font-size: 15px; min-width: 62px; }
.pv-d { flex: 1; font-size: 15px; min-width: 0; }
.pv-d small { display: block; color: var(--text-2); font-size: 12px; }
.pv-p { font-weight: 700; font-variant-numeric: tabular-nums; font-size: 14.5px; }
.pv.doute { border-color: color-mix(in srgb, var(--warn) 45%, transparent); }

/* ─────────────────────────── Formulaires ─────────────────────────── */
.grille-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.champ { display: flex; flex-direction: column; gap: 6px; }
.champ-large { grid-column: 1 / -1; }
.champ span { font-size: 12px; text-transform: uppercase; letter-spacing: .7px; color: var(--text-2); font-weight: 700; }
.champ input, .champ select, .champ textarea {
  background: var(--surface); border: 1px solid var(--line); color: var(--text);
  border-radius: var(--r-s); padding: 14px 13px; width: 100%; outline: none;
  min-height: 52px; transition: border-color .15s;
}
.champ textarea { min-height: 74px; resize: vertical; line-height: 1.45; }
.champ input:focus, .champ select:focus, .champ textarea:focus { border-color: var(--accent); }

/* ── LA FLÈCHE DES LISTES DÉROULANTES SE VOIT ────────────────────────────────
   Remonté au premier test terrain : « mettre les flèches des menus déroulants
   plus en évidence, donc plus gros ». La flèche native du navigateur fait
   environ 8 px et se confond avec la bordure : rien ne distinguait un champ à
   remplir au clavier d'une liste à dérouler, donc l'artisan tapait au clavier
   dans des champs qui n'attendaient qu'un doigt.

   La flèche est dessinée par une image de fond, en orange d'accent, à 22 px.
   Deux variables plutôt qu'une seule image : une donnée SVG ne peut pas lire
   `currentColor`, la couleur doit donc être écrite dans l'URL — une par thème.
   `padding-right` réserve sa place pour que le texte ne passe jamais dessous.

   ⚠️ PIÈGE, PAYÉ LE 2026-08-07 — toute règle qui vise un de ces sélecteurs plus
   bas dans ce fichier doit écrire `background-color:`, JAMAIS le raccourci
   `background:`. Le raccourci remet `background-image` à `none` et efface la
   flèche sans le moindre signe. C'est exactement ce qui s'est passé : deux
   règles posées après celle-ci (`.mini select` et `.statut-select`) ont fait
   disparaître la flèche des cases de la ligne de devis et du statut, le jour
   même où on la grossissait. `.champ select`, déclarée AVANT, n'a rien eu —
   d'où l'écran Réglages qui gardait sa flèche et la ligne de devis qui l'avait
   perdue. Le style calculé renvoyait `background-image: none`. */
:root {
  --fleche: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FF7A18' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 9l7 7 7-7'/%3E%3C/svg%3E");
  /* Pavé numérique : le champ « Prix unitaire » n'est plus une liste depuis
     qu'il ouvre le clavier à gros chiffres. Une flèche y annoncerait un menu
     déroulant qui n'existe pas — cette icône-ci dit « ça s'ouvre » sans mentir
     sur ce qui s'ouvre. Même orange, même facture de tracé que la flèche. */
  --pave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FF7A18' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='3.5' width='17' height='17' rx='3'/%3E%3Cpath d='M9 8h.01M15 8h.01M9 12h.01M15 12h.01M9 16h.01M15 16h.01'/%3E%3C/svg%3E");
}
select.select-gros, .champ select, .mini select, .statut-select {
  -webkit-appearance: none; appearance: none;
  background-image: var(--fleche);
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 22px 22px;
  padding-right: 42px;
}
/* La liste des lignes est plus étroite, mais la flèche n'y est pas plus petite
   pour autant : 24 px, au-dessus des 22 px des champs larges. C'est la cible
   que le pouce cherche, et c'est le deuxième rappel de Cédric sur ce point
   (« je t'avais demandé de la mettre plus grosse »). Les colonnes de .ligne-g
   ont été élargies d'autant pour que le texte garde sa place. */
.mini select { background-position: right 5px center; background-size: 24px 24px; padding-right: 32px; }
.statut-select { background-position: right 10px center; background-size: 22px 22px; padding-right: 38px; }
.recherche input, .recherche-modal {
  width: 100%; background: var(--surface); border: 1px solid var(--line); color: var(--text);
  border-radius: var(--r); padding: 15px 16px; outline: none; min-height: 54px;
}
.recherche { margin-bottom: 12px; }

.switch { display: flex; align-items: center; gap: 12px; padding: 12px 0; cursor: pointer; }
.switch input { display: none; }
.switch-piste { width: 54px; height: 32px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line); position: relative; flex: none; transition: background .2s; }
.switch-piste i { position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: 50%; background: var(--text-2); transition: transform .2s, background .2s; }
.switch input:checked + .switch-piste { background: color-mix(in srgb, var(--ok) 30%, transparent); border-color: var(--ok); }
.switch input:checked + .switch-piste i { transform: translateX(22px); background: var(--ok); }
.switch-txt { font-size: 16px; font-weight: 600; }

/* ─────────────────────────── Éditeur de devis ─────────────────────────── */
.devis-tete { margin-bottom: 6px; }
.devis-num { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.devis-num span { font-weight: 800; font-size: 19px; letter-spacing: -.4px; }
/* Le numéro était un <span>. Devenu champ le 2026-08-09, il garde exactement
   la même allure — même graisse, même corps — parce que c'est le titre du
   document, pas un formulaire. Ce qui change : un liseré discret et un fond
   de surface, qui disent « ça se touche » sans transformer l'en-tête en
   saisie. 46 px de haut : c'est une cible pour un pouce, sur un chantier. */
.num-devis {
  flex: 1 1 auto; min-width: 0; min-height: 46px;
  font-weight: 800; font-size: 19px; letter-spacing: -.4px;
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r);
  padding: 8px 12px; outline: none;
}
.num-devis:focus { border-color: var(--accent); }
/* Le champ de date tient sous le numéro, pleine largeur : c'est là que
   l'artisan l'a cherchée. `min-height` explicite car Safari iOS rend les
   input[type=date] plus bas que les autres champs et la ligne sautait. */
/* ── CIVILITÉ + NOM (2026-08-11) ─────────────────────────────────────────────
   Le menu de civilité est étroit et à GAUCHE, comme on l'écrit et comme on le
   lit. `1fr` sur le nom : il garde toute la largeur restante — c'est lui le
   champ long, un patronyme calédonien ne tient pas en quinze caractères (voir
   la note de `champ-large` dans index.html).
   Le menu garde la flèche d'accent posée plus haut sur `.champ select`, avec
   son padding droit : sans lui, « Mme » passerait dessous. */
.client-duo { display: grid; grid-template-columns: 92px 1fr; gap: 8px; }
.client-duo select { padding-left: 11px; }

/* ⚠️ LA FLÈCHE QU'ON ENLÈVE N'EST PAS LA NÔTRE. Un `input list=…` fait dessiner
   au navigateur sa PROPRE flèche de liste, en plus de tout ce que fait ce
   fichier — sur Android elle se collait au bord droit du nom du client et
   laissait croire à un menu déroulant qui n'en est pas un (« enlever la petite
   flèche vers le bas dans le nom du client », Cédric).
   On la masque, on ne retire pas le `list=` : la liste des clients déjà saisis
   reste proposée à la frappe, elle n'a simplement plus de bouton. */
.client-duo input::-webkit-calendar-picker-indicator { display: none !important; opacity: 0; }
.client-duo input::-webkit-list-button { display: none !important; }

.champ-date input { min-height: 50px; }
/* `background-color` et padding droit à 38 px : mêmes deux pièges que sur
   `.mini select` — le raccourci `background` effacerait la flèche, et un
   `padding: 9px 14px` laisserait le texte du statut passer sous elle. */
.statut-select { background-color: var(--surface-2); border: 1px solid var(--line); color: var(--text); border-radius: 999px; padding: 9px 38px 9px 14px; font-size: 14px; font-weight: 700; outline: none; }

.lignes { display: flex; flex-direction: column; gap: 10px; }
.ligne {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 13px 13px 11px; box-shadow: var(--shadow);
}
.ligne.doute { border-color: color-mix(in srgb, var(--warn) 50%, transparent); }

/* ── LE RÉSUMÉ D'UNE LIGNE, ET SON DÉPLIAGE ──────────────────────────────────
   Une ligne dépliée fait ~340 px : c'est un formulaire complet. Repliée, elle
   tient en trois informations — ce qu'on a vendu, en quelle quantité, pour
   combien. Voir le commentaire de rendreLignes() dans app.js pour la mesure.
   La ligne garde son `padding` : « .ligne-gliss » le déborde à gauche et à
   droite (marges négatives) pour que la zone touchable coure d'un bord à
   l'autre — c'était porté par `.ligne-resume` avant le glissement du
   2026-08-13, déplacé sur son enveloppe pour que la même bordure serve aussi
   à masquer (`overflow:hidden`) le bouton Supprimer révélé par le geste.
   Voir GESTES SUR LES LIGNES DE PRESTATION dans app.js pour le geste. */
.ligne-gliss {
  position: relative; overflow: hidden; border-radius: var(--r);
  width: calc(100% + 26px); margin: -13px -13px -11px;
}
/* Dépliée, la ligne n'a plus besoin de son résumé qu'en bandeau du haut : le
   bas de « .ligne-gliss » doit redevenir carré, sinon un bouton Supprimer
   révélé sur une ligne dépliée déborderait avec un coin rond au mauvais
   endroit — le bas du bandeau n'est plus le bas de la carte. */
.ligne.ouverte .ligne-gliss { margin-bottom: 2px; border-radius: var(--r) var(--r) 0 0; }
.ligne-gliss-fond { position: absolute; inset: 0; display: flex; justify-content: flex-end; }
.ligne-gliss-suppr {
  width: 112px; flex: none; background: var(--danger); color: #fff;
  font-weight: 800; font-size: 14px; border: 0; border-radius: 0;
}
.ligne-gliss-suppr:active { filter: brightness(.88); }
.ligne-resume {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 13px;
  /* Fond OPAQUE et obligatoire : ce bouton glisse maintenant par-dessus
     « .ligne-gliss-fond », il doit le cacher entièrement au repos — avant le
     glissement, `background: none` suffisait parce qu'il n'y avait rien
     dessous d'autre que la carte elle-même, de la même couleur. */
  background: var(--surface); border: 0; color: inherit; text-align: left;
  font: inherit; -webkit-appearance: none; appearance: none;
  min-height: 56px;                     /* cible tactile, gants compris */
  /* Position relative : le fait passer devant `.ligne-gliss-fond` (positionné
     lui aussi) dans l'ordre de peinture — même technique que `.glissable
     .carte` pour les cartes de devis. `touch-action: pan-y` laisse le
     défilement vertical de la page ; les deux lignes suivantes coupent la
     loupe/le menu « Copier » de Safari iOS pendant le geste. */
  position: relative; transition: transform .18s ease;
  touch-action: pan-y; -webkit-touch-callout: none; user-select: none; -webkit-user-select: none;
}
.ligne-gliss.ouvert .ligne-resume { box-shadow: -6px 0 12px -8px rgba(0,0,0,.35); }
/* Dépliée, la ligne n'a plus besoin de son résumé : la désignation, le calcul
   et le total sont tous dans le détail juste en dessous. Il ne reste que ce
   qu'il faut pour refermer — et « Replier » en toutes lettres, parce qu'un
   chevron seul n'annonce rien. */
.ligne.ouverte .ligne-resume {
  min-height: 0; padding: 6px 13px 8px; justify-content: flex-end;
}
.ligne.ouverte .ligne-resume .res-txt,
.ligne.ouverte .ligne-resume .res-tot { display: none; }
.res-replier { display: none; font-size: 13px; font-weight: 600; color: var(--text-2); }
.ligne.ouverte .ligne-resume .res-replier { display: inline; }
.ligne-resume .res-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ligne-resume .res-des {
  font-size: 16px; font-weight: 600; line-height: 1.3; color: var(--text);
  /* Deux lignes au maximum : au-delà, la liste redeviendrait un mur de texte.
     Le libellé entier reste lisible dès que la ligne est dépliée. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ligne-resume .res-des i { color: var(--text-2); font-weight: 500; }
.ligne-resume .res-calc { font-size: 13px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.ligne-resume .res-tot {
  font-weight: 800; font-size: 15px; white-space: nowrap; flex: none;
  font-variant-numeric: tabular-nums;
}
.ligne-resume .res-chev {
  width: 20px; height: 20px; flex: none; color: var(--text-2);
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .18s ease;
}
.ligne.ouverte .ligne-resume .res-chev { transform: rotate(180deg); }
/* Mélangé à `--surface`, PAS à `transparent` (2026-08-13) : depuis que
   « .ligne-gliss-fond » loge le bouton rouge juste en dessous, un fond actif
   translucide y laisserait passer un soupçon de rouge sur un simple appui,
   sans même glisser. Le retour visuel reste identique à l'œil, il reste
   simplement OPAQUE. */
.ligne-resume:active { background: color-mix(in srgb, var(--text) 6%, var(--surface)); }
/* Une ligne douteuse est ouverte d'office, mais elle peut être repliée : le
   point orange est alors le seul signal qui reste. Il doit donc exister. */
.ligne.doute .ligne-resume .res-tot::after {
  content: "●"; color: var(--warn); font-size: 11px; margin-left: 6px; vertical-align: 2px;
}
.ligne:not(.ouverte) .ligne-detail { display: none; }

.ligne-h { display: flex; gap: 10px; align-items: flex-start; }
/* ── LA DÉSIGNATION EST UN CHAMP, ET ELLE LE MONTRE (2026-08-11) ─────────────
   Elle était le seul champ modifiable de la ligne sans bordure ni fond :
   quantité, unité, prix, TGC et pièce sont tous encadrés en gris, la
   désignation ressemblait à un titre. « Mets le nom de la prestation dans un
   encadré gris comme les quantités » (Cédric) — et l'enjeu est plus que
   décoratif : c'est le champ qu'on vient corriger quand le vocal s'est trompé
   d'article, donc celui qui doit se signaler en premier.
   Mêmes fond, bordure et rayon que `.mini input`, pour que les six champs de
   la ligne soient une seule famille. */
.ligne-des-zone { flex: 1; position: relative; }
.ligne-des {
  width: 100%; display: block;
  background-color: var(--surface-2); border: 1px solid var(--line);
  border-radius: 10px; color: var(--text); font-size: 16px;
  font-weight: 600; padding: 11px 10px; outline: none; resize: none;
  line-height: 1.35; min-height: 48px;   /* la hauteur des deux boutons voisins */
}
.ligne-des:focus { border-color: var(--accent); }

/* ── PROPOSITIONS DU CATALOGUE ───────────────────────────────────────────────
   Une liste native (`datalist`) est impossible ici : le champ est un
   `<textarea>` — il l'est parce qu'une désignation de devis se replie sur deux
   lignes — et aucun navigateur ne branche `list=` sur un textarea. D'où cette
   liste dessinée à la main.
   Elle est en `position: absolute` pour ne pas pousser la ligne vers le bas à
   chaque frappe : un devis qui saute sous le doigt est inutilisable debout. */
/* `.sugg-zone` : le même socle pour les champs de l'en-tête (client, e-mail).
   Une liste posée en `absolute` a besoin d'un parent positionné, sinon elle se
   place par rapport à l'écran et se retrouve n'importe où au défilement. */
.sugg-zone { position: relative; }
.ligne-sugg, .champ-sugg {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 4px);
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow); overflow: hidden; max-height: 250px; overflow-y: auto;
}
.ligne-sugg button, .champ-sugg button {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  border-bottom: 1px solid var(--line); color: var(--text);
  padding: 11px 12px; font: inherit; font-size: 15px; line-height: 1.3;
  min-height: 48px;                      /* cible tactile, comme partout ailleurs */
}
.ligne-sugg button:last-child, .champ-sugg button:last-child { border-bottom: 0; }
.ligne-sugg button:active, .champ-sugg button:active { background: var(--surface-2); }
.ligne-sugg small, .champ-sugg small { display: block; margin-top: 2px; font-size: 12px; font-weight: 700;
  color: var(--text-2); }
/* Le prix de l'artisan, quand il en a un : c'est ce qui distingue une
   proposition utile d'une liste de noms. */
.ligne-sugg .sg-prix, .champ-sugg .sg-prix { color: var(--accent); }
.ligne-sugg .sg-perso, .champ-sugg .sg-perso { color: var(--ok); }
/* Poubelle : 34 px, c'était sous le minimum tactile et l'artisan la manquait
   (remonté au premier test terrain, « grossir la poubelle »). Portée à 48 px
   — le seuil habituel d'une cible touchée au pouce — et teintée en danger,
   pour qu'elle se distingue du gris des champs sans crier. */
.ligne-suppr { width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;
  color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent); flex: none; }
/* 24 → 28 px le 2026-08-07 : « grossis le logo de la poubelle ». La CASE ne
   bouge pas (48 px, le seuil tactile), seul le dessin dedans. */
.ligne-suppr svg { width: 28px; height: 28px; }
/* Micro de ligne : même gabarit de 48 px que la corbeille sa voisine — c'est la
   cible qu'on touche avec des gants. Teinte accent et non danger : il corrige,
   il ne détruit pas, et les deux boutons sont côte à côte. */
.ligne-micro {
  width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;
  flex: none;
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
}
.ligne-micro svg { width: 26px; height: 26px; }
.ligne-micro:active { background: color-mix(in srgb, var(--accent) 26%, transparent); }
.ligne-suppr:active { background: color-mix(in srgb, var(--danger) 24%, transparent); }
/* Colonnes élargies le 2026-08-07 (74/62 → 92/72) : la flèche est passée à
   24 px et réserve 32 px à droite. Sans cet élargissement, la flèche plus
   grosse aurait coûté la lisibilité qu'elle est censée servir. Les 8 px pris
   en plus sortent de la colonne du prix, mesurée à 48 px de marge : c'est la
   seule des trois qui en avait à céder. La contrainte réelle est dans la
   media query « max-width: 380px » en bas de fichier — c'est elle qui
   s'applique sur un iPhone, pas cette ligne-ci. */
/* 4ᵉ colonne : 96 px et non `auto`. En `auto` elle se serre exactement sur son
   contenu — mesuré à 844 px (iPhone en paysage), « 22 % » n'avait qu'UN pixel
   de marge, donc une police rendue un cheveu plus large sur iOS et le taux se
   tronquait. Une largeur fixe se raisonne, un `auto` se subit. */
.ligne-g { display: grid; grid-template-columns: 100px 72px 1fr 96px; gap: 8px; align-items: end; margin-top: 10px; }
.mini { display: flex; flex-direction: column; gap: 4px; }
.mini label { font-size: 10px; text-transform: uppercase; letter-spacing: .6px; color: var(--text-2); font-weight: 700; }
/* `background-color` et NON `background` : le raccourci effacerait la flèche
   posée plus haut (voir l'avertissement à la règle « LA FLÈCHE … SE VOIT »).
   Même piège sur le padding : `padding: 10px 8px` écraserait le padding-right
   qui réserve la place de la flèche, et le texte passerait dessous. Les 36 px
   à droite sont donc écrits ici, une seule fois, pour les deux. */
.mini input, .mini select {
  background-color: var(--surface-2); border: 1px solid var(--line); color: var(--text);
  border-radius: 10px; padding: 10px 32px 10px 8px; width: 100%; outline: none; min-height: 46px;
  font-variant-numeric: tabular-nums;
}
/* Le champ « Prix unitaire » : icône de pavé numérique, posée aux mêmes
   position et taille que la flèche de ses voisines. `[data-pave]` et non un
   id : l'attribut est déjà celui qui branche le clavier à gros chiffres dans
   app.js, donc l'icône ne peut pas apparaître sur un champ qui n'ouvre rien. */
.mini input[data-pave] { background-image: var(--pave); background-repeat: no-repeat;
  background-position: right 5px center; background-size: 24px 24px; }
.mini input:focus, .mini select:focus { border-color: var(--accent); }
.ligne-total { text-align: right; font-weight: 800; font-size: 16px; font-variant-numeric: tabular-nums; padding-bottom: 12px; white-space: nowrap; }
.ligne-dicte { font-size: 12px; color: var(--text-2); font-style: italic; margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line); }
.ligne-dicte b { color: var(--warn); font-style: normal; }
/* « Ajouter à mes tarifs » : cible tactile confortable, pas un lien perdu */
.ligne-dicte .lien { font-style: normal; display: inline-block; margin-top: 6px; padding: 8px 0; line-height: 1.35; text-align: left; }

/* ── DEUX GESTES OPPOSÉS, DEUX CÔTÉS, DEUX COULEURS ─────────────────────────
   « Ce n'est pas ça » rejette ce que l'application a compris ; « Mémoriser ce
   prix » l'entérine. Présentés côte à côte comme deux liens gris séparés d'un
   point médian, ils se lisaient comme une seule phrase — l'artisan ne voyait
   pas qu'il avait là deux décisions contraires. Rouge à gauche, vert à droite,
   chacun collé à son bord : la distinction se fait avant la lecture. */
/* TROIS gestes depuis le 2026-08-12, et c'est ce qui impose `flex-wrap`.
   Mesuré : à 375 px, la rangée offre ~349 px utiles ; les deux anciens boutons
   la remplissaient déjà presque entièrement. Un troisième débordait — d'où le
   passage à la ligne, et `flex: 1 1 auto` pour que deux boutons esseulés sur
   une seconde rangée occupent toute la largeur au lieu de flotter au milieu.
   `min-width` garde une cible franche : sur un chantier, au soleil, un bouton
   écrasé à 60 px ne se touche pas. */
.ligne-gestes { display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: stretch; gap: 8px; margin-top: 9px; font-style: normal; }
.ligne-gestes .geste { flex: 1 1 auto; min-width: 104px; }
.ligne-gestes.une-seule { justify-content: stretch; }
.ligne-gestes.une-seule .geste { flex: 1; }
.geste { font-size: 13.5px; font-weight: 700; line-height: 1.3; text-align: center;
  padding: 11px 12px; border-radius: 10px; min-height: 44px; border: 1px solid; }
.geste-non { color: var(--danger); background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-color: color-mix(in srgb, var(--danger) 32%, transparent); }
.geste-oui { color: #157A46; background: color-mix(in srgb, var(--ok) 14%, transparent);
  border-color: color-mix(in srgb, var(--ok) 36%, transparent); }
/* « Enregistrer la prestation » — troisième geste (2026-08-12). Ni vert ni
   rouge : il ne juge pas la ligne, il l'apprend. L'accent le distingue des
   deux décisions contraires sans entrer dans leur code couleur. */
.geste-memo { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent) 34%, transparent); }
.geste-non:active { background: color-mix(in srgb, var(--danger) 22%, transparent); }
.geste-oui:active { background: color-mix(in srgb, var(--ok) 26%, transparent); }
.geste-memo:active { background: color-mix(in srgb, var(--accent) 24%, transparent); }
/* L'état FAIT. Il ne se contente pas de changer le mot : le bouton s'éteint
   visuellement, pour que l'œil trouve d'un coup ce qui reste à traiter dans un
   devis de vingt lignes. Il reste cliquable — réenregistrer après avoir changé
   un prix est légitime, et l'état se recalcule tout seul. */
.geste.fait { color: var(--text-2); background: transparent; border-color: var(--line);
  font-weight: 600; }
@media (prefers-color-scheme: dark) { .geste-oui { color: #5BD394; } }

/* Centré depuis le 2026-08-07 : le bouton est désormais seul de sa rangée, et
   collé à gauche il avait l'air d'un reste plutôt que d'une action. */
.ajout-rapide { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; justify-content: center; }
/* Action posée sous le titre d'un bloc, au-dessus de son contenu. Pleine
   largeur : c'est un point d'entrée, pas un réglage discret dans un coin. */
.bloc-action { width: 100%; margin-bottom: 12px; }

/* ── LES DEUX POINTS D'ENTRÉE, CÔTE À CÔTE (2026-08-12) ─────────────────────
   Demande de Cédric. Ils répondent à la même question — « il manque une
   prestation » — et empilés ils se lisaient comme deux ÉTAPES l'une après
   l'autre. Côte à côte, ce sont deux RÉPONSES au choix.

   `1fr 1fr` et non `flex` : les deux colonnes font exactement la même largeur
   quelle que soit la longueur du texte. Avec flex, « Piocher dans mes tarifs »
   étant plus long, il aurait mangé la place de « Ajouter » — et c'est
   « Ajouter » que Cédric a fait grossir le matin même.

   ⚠️ LE TEXTE PASSE SUR DEUX LIGNES, ET C'EST PRÉVU. Sur un écran de 375 px,
   chaque bouton fait environ 164 px : aucun des deux libellés ne tient sur une
   ligne. `align-items: stretch` (par défaut en grille) leur donne la MÊME
   hauteur, sinon celui qui tient sur une ligne serait visiblement plus court
   que son voisin. On ne raccourcit pas les libellés : « Piocher » seul ne dit
   pas dans quoi on pioche. */
.bloc-actions-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.bloc-actions-2 .bloc-action { padding-left: 10px; padding-right: 10px; text-align: center; }

/* Plus gros que « Piocher dans mes tarifs », et c'est voulu : c'est le geste
   qu'on cherche quand la dictée a manqué une prestation. 56 px, la cible
   posée en tête de ce fichier — celle à laquelle ce bouton échappait seul.
   ⚠️ 16 px et non 17 : à 17 px, « prestation » dépassait de sa colonne une
   fois les deux boutons mis côte à côte. */
#btnAjoutLigne { font-size: 16px; min-height: 56px; }
#btnCatalogue  { font-size: 15px; min-height: 56px; }

.totaux { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 16px; margin: 22px 0; }
.tot-l { display: flex; justify-content: space-between; align-items: baseline; padding: 7px 0; font-size: 15px; }
.tot-l span { color: var(--text-2); }
.tot-l b { font-variant-numeric: tabular-nums; font-weight: 700; }
.tot-l.remise b { color: var(--ok); }
.tot-l.grand { border-top: 1px solid var(--line); margin-top: 8px; padding-top: 14px; font-size: 19px; }
.tot-l.grand span { color: var(--text); font-weight: 700; }
.tot-l.grand b { font-size: 26px; font-weight: 800; letter-spacing: -.8px; color: var(--accent); }
.tot-l.acompte { border-top: 1px dashed var(--line); margin-top: 8px; padding-top: 12px; }
.tot-l.acompte b { color: var(--accent-2); }

.actions-devis { margin: 20px 0 12px; }
.actions-sec { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }

.btn-principal {
  width: 100%; background: linear-gradient(150deg, var(--accent), #FF9E2C); color: #14181C;
  font-weight: 800; font-size: 17.5px; padding: 18px; border-radius: var(--r);
  box-shadow: 0 8px 24px rgba(255,122,24,.3);
}
.btn-principal:disabled { opacity: .4; box-shadow: none; }
.btn-principal:active:not(:disabled) { transform: scale(.985); }
.btn-ghost {
  background: var(--surface); border: 1px solid var(--line); color: var(--text);
  padding: 14px 16px; border-radius: var(--r-s); font-weight: 600; font-size: 15px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 52px;
}
.btn-ghost:active { background: var(--surface-2); }
.btn-ghost.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }

/* ── LA COULEUR DES DEUX POINTS D'ENTRÉE (2026-08-12) ───────────────────────
   « Le piocher en bleu et ajouter une presta en orange » (Cédric).

   ⚠️ CES TROIS RÈGLES DOIVENT RESTER APRÈS `.btn-ghost`, ET C'EST TOUT LE
   PIÈGE. Écrites plus haut dans le fichier — à côté de `.bloc-actions-2`, là
   où on les cherche pourtant naturellement — elles ont la MÊME spécificité
   que `.btn-ghost` (une classe), qui repose ensuite son propre `background`,
   `color` et `border-color` par-dessus : les boutons se rangeaient bien côte
   à côte, et restaient gris. Constaté en mesurant dans un vrai Chromium, pas
   en relisant le fichier — la lecture ne montre pas l'ordre de cascade.

   ⚠️ TEINTE ET BORDURE, PAS D'APLAT PLEIN, et c'est un choix. Un bouton
   orange PLEIN ici entrerait en concurrence directe avec « Aperçu & PDF »,
   l'aplat orange de la barre du bas, qui est LA sortie de l'écran. Deux
   aplats orange sur le même écran, et plus aucun des deux n'est le bouton
   principal. La teinte donne la couleur demandée sans voler la vedette. */
.act-ajout {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
}
.act-tarifs {
  color: var(--bleu);
  border-color: color-mix(in srgb, var(--bleu) 55%, transparent);
  background: color-mix(in srgb, var(--bleu) 10%, var(--surface));
}
.act-ajout:active  { background: color-mix(in srgb, var(--accent) 22%, var(--surface)); }
.act-tarifs:active { background: color-mix(in srgb, var(--bleu) 22%, var(--surface)); }
.bloc-btn { width: 100%; margin-top: 8px; }

/* Fond OPAQUE et aucun `backdrop-filter` — même correctif que `.tabbar`, même
   défaut : c'est cette barre-ci que Cédric voyait flotter au milieu de l'écran
   du devis, juste au-dessus du menu. Voir le pavé au-dessus de `.tabbar` pour
   le raisonnement complet. */
.barre-bas {
  position: fixed; left: 0; right: 0; bottom: var(--tab-h); z-index: 30;
  padding: 12px 16px; background: var(--surface);
  border-top: 1px solid var(--line);
}
.barre-bas .btn-principal { max-width: 728px; margin: 0 auto; display: block; }
/* Rappel « relisez avant de créer » : 13 px, encre secondaire. Il doit se lire
   sans concurrencer le bouton qui est juste dessous — deux lignes au maximum,
   y compris sur un écran de 320 px. Toute ligne ajoutée ici se paie dans
   .dictee-wrap { padding-bottom }, sinon elle mange le « Total TTC ». */
.avant-creer {
  margin: 0 auto 9px; max-width: 728px;
  font-size: 13px; line-height: 1.35; color: var(--text-2); text-align: center;
}
/* Le motif du blocage de licence, à la place de la phrase ci-dessus. Même
   gabarit — donc même hauteur, donc aucun saut de mise en page à la bascule —
   et l'encre du danger, parce que ce n'est pas un conseil mais un mur. */
.avant-creer-bloque { color: var(--danger); font-weight: 600; }

/* ── « Ce que vous avez dicté » : en tête du devis, toujours visible ───────
   Tiroir repliable du 2026-08-12 au 2026-08-17, redevenu un bloc ordinaire le
   soir même sur retour de Cédric (« je veux le texte modifiable tout de suite
   en haut »). D'où la disparition de `.ds-tete`/`.ds-chev`/`.ds-corps` : plus
   rien à ouvrir, donc plus de bouton d'ouverture ni de chevron. */
.dictee-source { padding: 16px; }
.ds-tete-fixe {
  display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
  font-size: 15px; font-weight: 700;
}
.ds-titre { flex: 1; min-width: 0; }
.ds-etat { font-size: 12.5px; font-weight: 600; color: var(--text-2); flex: none; }
/* Le micro discret (2026-08-17) réemploie `.ligne-micro`, la pastille déjà
   posée sur chaque prestation : même taille, même couleur, même geste appris
   une seule fois. Rien à redéfinir ici que sa place dans la rangée. */
.ds-micro { flex: none; }
.ds-texte {
  margin: 0; padding: 12px 14px; border-radius: 12px;
  background: var(--surface-2); color: var(--text);
  font-size: 15px; line-height: 1.5; white-space: pre-wrap;
  border: 1px solid transparent; outline: none;
}
/* Modifiable (étape B1, 2026-08-17) : un cadre qui répond au doigt, comme
   `.transcript`. Lecture seule (T1, devis né du repli regex) : ni curseur
   texte ni contour au focus — rien n'invite à taper dans un champ qui ne
   répercutera rien. */
.ds-texte[contenteditable="true"] { cursor: text; }
.ds-texte[contenteditable="true"]:focus { border-color: var(--accent); }
/* La dictée purgée par l'âge : c'est une explication, pas le texte de
   l'artisan. L'italique et l'encre secondaire évitent qu'on la lise comme sa
   parole — un message système qui ressemble à une dictée serait pire que rien. */
.ds-texte.ds-absent { font-style: italic; color: var(--text-2); background: transparent;
  border: 1px dashed var(--line); }
/* Explique pourquoi le texte ne se répercute pas (T1) : sobre, sous le cadre,
   jamais dans le cadre lui-même — pour ne jamais se confondre avec la parole
   de l'artisan (même principe que .ds-absent ci-dessus). */
.ds-note { margin: 10px 0 0; font-size: 13px; line-height: 1.4; color: var(--text-2); }

/* ── Barre de total du devis ─────────────────────────────────────────────── */
/* Le total et le bouton d'envoi côte à côte, toujours sous le pouce. Le montant
   ne se laisse pas comprimer (il porte l'information) : c'est le bouton qui
   rétrécit, jusqu'à 44 px de haut — la cible tactile minimale. */
.barre-devis .bd-rang {
  display: flex; align-items: center; gap: 12px;
  max-width: 728px; margin: 0 auto;
}
.barre-devis .bd-tot { display: flex; flex-direction: column; line-height: 1.15; flex: 0 1 auto; min-width: 0; }
.barre-devis .bd-tot span { font-size: 11px; text-transform: uppercase; letter-spacing: .8px; color: var(--text-2); font-weight: 700; }
.barre-devis .bd-tot b {
  font-size: 21px; font-weight: 800; color: var(--accent);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.barre-devis .btn-principal { flex: 1 1 0; width: auto; min-height: 44px; padding: 12px 14px; margin: 0; }
/* La barre est en `position: fixed` : sans cette réserve, elle recouvrirait le
   bouton « Supprimer », le dernier de l'écran. Portée à 140px (au lieu de
   104px) depuis la fusion des écrans (étape B2, 2026-08-17) : cette RÉSERVE
   UNIQUE couvre désormais aussi bien #barreDevis (104px) que #barreDictee
   (140px, plus haute — elle porte deux paragraphes) puisque les deux
   partagent maintenant le même `#ecran-devis`, jamais affichées ensemble
   mais jamais garanties d'être la même d'un rendu à l'autre. Un peu de
   marge perdue avec #barreDevis vaut mieux qu'un bouton caché avec
   #barreDictee. */
#ecran-devis { padding-bottom: 140px; }
@media (max-width: 360px) {
  .barre-devis .bd-tot b { font-size: 18px; }
  .barre-devis .btn-principal { font-size: 15px; padding: 12px 8px; }
}

/* ─────────────────────────── Pavé numérique du prix ─────────────────────── */
/* Le montant en cours de frappe, en gros : c'est ce que l'artisan doit relire
   avant de valider, et « 145 000 » se vérifie d'un coup d'œil là où « 145000 »
   se recompte chiffre par chiffre. */
.pave-ecran {
  text-align: right; padding: 10px 14px; margin-bottom: 12px;
  background: var(--surface-2); border-radius: 12px;
  font-size: 30px; font-weight: 800; letter-spacing: -.5px;
  font-variant-numeric: tabular-nums;
}
.pave-ecran small { font-size: 16px; font-weight: 700; color: var(--text-2); }
.pave { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
/* 64 px de haut : les touches du clavier iOS font ~32 px et se manquent avec
   des gants. C'est toute la raison d'être de ce pavé. */
.pave button {
  min-height: 64px; font-size: 26px; font-weight: 700;
  border: 1px solid var(--line); border-radius: 14px; background: var(--surface);
  font-variant-numeric: tabular-nums;
}
.pave button:active { background: var(--surface-2); }
/* « 000 » : en francs CFP, presque tout prix se termine par trois zéros. */
.pave .pave-mille { font-size: 21px; }
.pave .pave-eff {
  font-size: 22px; color: var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
  border-color: color-mix(in srgb, var(--danger) 28%, transparent);
}
/* Le champ prix n'ouvre plus le clavier du système : il se touche comme un
   bouton, donc il doit s'en donner l'air. */
.mini input[data-f="pu"] { cursor: pointer; }

/* ─────────────────────────── Avenant ─────────────────────────── */
/* Le bandeau du marché : la réponse à la seule question que le client et
   l'artisan se posent tous les deux — « ça fait combien, finalement ? ».
   Dessiné comme le bloc des totaux du devis, pour être lu sans apprentissage. */
.marche { margin: 12px 0 4px; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.marche-l {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 10px 12px; font-size: 14.5px; color: var(--text-2);
}
.marche-l + .marche-l { border-top: 1px solid var(--line); }
.marche-l b { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }
.marche-l.total { background: var(--surface-2); color: var(--text); font-weight: 700; }
.marche-l.total b { font-size: 17px; color: var(--accent); letter-spacing: -.3px; }
/* Ligne d'avenant sur la fiche du devis d'origine : appuyable, elle ouvre
   l'avenant. C'est aussi la réponse à « c'était combien, ce chantier ? »,
   posée six mois plus tard. */
.avenant-l {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  width: 100%; min-height: 56px; padding: 10px 12px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  text-align: left;
}
.avenant-l .d { display: flex; flex-direction: column; min-width: 0; }
.avenant-l .d b { font-size: 15px; }
.avenant-l .d small { color: var(--text-2); font-size: 12.5px; }
.avenant-l .m { font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums; }
.avenant-l:active { background: var(--surface-2); }

/* ─────────────────────────── Filtres / tarifs ─────────────────────────── */
.filtres { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 6px; margin-bottom: 12px; scrollbar-width: none; }
.filtres::-webkit-scrollbar { display: none; }
.filtre { white-space: nowrap; padding: 10px 15px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); font-size: 14px; font-weight: 600; }
.filtre.actif { background: var(--accent); border-color: var(--accent); color: #14181C; }

/* ── Écran Documents : période libre et accès à la synthèse (2026-08-13) ──
   Les deux champs de date côte à côte : sur un téléphone, « Du » et « Au »
   empilés poussent la liste sous la ligne de flottaison alors qu'ils tiennent
   largement en largeur. */
.periode-libre { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 12px; }
.periode-libre input { min-height: 52px; }
#btnRecapTGC { margin-top: 18px; min-height: 56px; font-size: 15.5px; }

/* Le tableau de la synthèse : une ligne par taux, en colonnes alignées.
   `tabular-nums` est ce qui rend une colonne de montants ADDITIONNABLE à l'œil
   — sans lui les chiffres n'ont pas la même largeur et la colonne ondule. */
.recap-l { display: flex; align-items: baseline; gap: 10px; padding: 11px 12px; font-size: 14.5px; }
.recap-l + .recap-l { border-top: 1px solid var(--line); }
.recap-l .cat { flex: 1; min-width: 0; }
.recap-l .base, .recap-l .taxe { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.recap-l .base { color: var(--text-2); min-width: 92px; }
.recap-l .taxe { font-weight: 800; min-width: 82px; }
.recap-tete { font-size: 12px; text-transform: uppercase; letter-spacing: .6px; color: var(--text-2); font-weight: 700; }
.recap-tete .taxe, .recap-tete .base { font-weight: 700; }
.recap-tab { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }

/* L'encadré permanent, non masquable : cette page aide à préparer une
   déclaration, elle ne la remplace pas. Sobre à dessein — un bandeau d'alerte
   se ferme des yeux au bout de deux visites. */
.recap-avis { font-size: 13.5px; color: var(--text-2); background: var(--surface-2);
  border-left: 3px solid var(--line); border-radius: var(--r-s); padding: 12px 14px; margin: 18px 0; }
.recap-manque { border-left-color: var(--warn); }

/* Même géométrie que #btnRecapTGC plus haut dans ce fichier, pour la même
   raison : dernier élément d'un écran qui défile, juste devant la barre du
   bas fixe — et il suit systématiquement un encadré .recap-avis (même sur une
   période vide, voir rendreRecap()), qu'un `.bloc-btn` ordinaire (8px) collerait. */
#btnExportRecap { margin-top: 18px; min-height: 56px; font-size: 15.5px; }

/* Même raison, et même valeur : `.btn-ghost` s'arrête à 52px de `min-height`,
   ce qui rend 55px à l'écran — sous la règle « cibles tactiles ≥ 56 px » posée
   en tête de ce fichier. Mesuré à 55 par le corpus de navigateur, pas déduit
   de la lecture. Ce bouton-ci se touche avec des gants, sur un chantier, et
   c'est celui qui produit le document remis au comptable. */
#btnDossierAnnuel { min-height: 56px; font-size: 15.5px; }

/* Compteur d'avancement de l'écran Démarrage rapide */
.demar-compteur { font-size: 15px; font-weight: 700; color: var(--text-2); margin: 0 0 12px; }

.tarifs { display: flex; flex-direction: column; gap: 8px; }
.tarif { display: flex; align-items: center; gap: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-s); padding: 12px 13px; }
.tarif-t { flex: 1; min-width: 0; }
.tarif-t b { display: block; font-size: 15px; font-weight: 600; }
.tarif-t small { color: var(--text-2); font-size: 12px; }
.tarif-p { display: flex; align-items: center; gap: 5px; }
.tarif-p input { width: 104px; text-align: right; background: var(--surface-2); border: 1px solid var(--line); color: var(--text); border-radius: 10px; padding: 11px 9px; outline: none; font-variant-numeric: tabular-nums; font-weight: 700; }
.tarif-p input:focus { border-color: var(--accent); }
.tarif-p em { color: var(--text-2); font-style: normal; font-size: 12px; min-width: 40px; }
.tarif.perso { border-left: 3px solid var(--accent-2); }

.logo-zone { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.logo-preview { width: 82px; height: 82px; border-radius: var(--r-s); border: 1px dashed var(--line); display: grid; place-items: center; color: var(--text-2); font-size: 12px; overflow: hidden; background: var(--surface); flex: none; }
.logo-preview img { width: 100%; height: 100%; object-fit: contain; }

/* ───────── Repli clavier (iPhone installé : pas de micro navigateur) ─────────
   Le gros bouton micro deviendrait un piège : il ne peut rien faire ici.
   On l'efface et on met le champ de saisie directement sous la consigne. */
.dictee-wrap.mode-clavier .scene-mic { display: none; }
.dictee-wrap.mode-clavier .dictee-etat { margin-bottom: 14px; }
.astuce-clavier {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
  border-radius: var(--r); padding: 16px; margin-bottom: 14px;
}
.astuce-clavier b { display: block; font-size: 15.5px; margin-bottom: 6px; }
.astuce-txt { font-size: 14px; color: var(--text-2); line-height: 1.5; margin: 0 0 12px; }
.astuce-txt b { display: inline; font-size: inherit; color: var(--text); }
.astuce-clavier .btn-ghost { width: 100%; }
/* Une fois le geste compris, l'encadré se réduit à un simple rappel */
.astuce-clavier.compact { padding: 11px 14px; }
.astuce-clavier.compact b { font-size: 14px; margin: 0; }
.astuce-clavier.compact .astuce-txt, .astuce-clavier.compact .btn-ghost { display: none; }

/* ─────────────────── Suivi : trésorerie, relances, encaissements ─────────────────── */
.tresorerie { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 4px; }
.treso {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 15px 14px; text-align: left;
}
.treso b { display: block; font-size: 22px; font-weight: 800; letter-spacing: -.6px; }
.treso span { font-size: 11.5px; color: var(--text-2); text-transform: uppercase; letter-spacing: .6px; }
.treso small { display: block; font-size: 12.5px; margin-top: 5px; color: var(--text-2); }
.treso.alerte { border-color: var(--danger); }
.treso.alerte b { color: var(--danger); }
.treso.alerte small { color: var(--danger); font-weight: 700; }
.treso.ok b { color: var(--ok); }
.treso.pleine { grid-column: 1 / -1; }

.etiq.paye { background: color-mix(in srgb, var(--ok) 22%, transparent); color: var(--ok); }
.etiq.retard { background: color-mix(in srgb, var(--danger) 20%, transparent); color: var(--danger); }
.etiq.partiel { background: color-mix(in srgb, var(--warn) 20%, transparent); color: var(--warn); }
.carte.retard { border-color: color-mix(in srgb, var(--danger) 55%, var(--line)); }

.suivi-item { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.suivi-item.retard { border-color: color-mix(in srgb, var(--danger) 55%, var(--line)); }
.suivi-item .carte { border: none; background: none; width: 100%; }
.suivi-actions { display: flex; gap: 8px; padding: 0 12px 12px; }
.suivi-actions button { flex: 1; padding: 11px 8px; border-radius: var(--r-s); font-size: 13.5px; font-weight: 700;
  background: var(--surface-2); color: var(--text); border: 1px solid var(--line); }
.suivi-actions button.majeur { background: var(--accent); border-color: var(--accent); color: #14181C; }
.suivi-actions button.vert { background: color-mix(in srgb, var(--ok) 20%, transparent); border-color: var(--ok); color: var(--ok); }

/* Bloc facture / paiements dans l'écran devis */
.paie-tete {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  background: var(--surface-2); border-radius: var(--r-s); padding: 14px; margin-bottom: 12px;
}
.paie-tete .mt { font-size: 21px; font-weight: 800; letter-spacing: -.5px; }
.paie-tete .lb { font-size: 12px; text-transform: uppercase; letter-spacing: .7px; color: var(--text-2); font-weight: 700; }
.paie-tete small { display: block; font-size: 12.5px; color: var(--text-2); margin-top: 3px; }
.paie-tete.du .mt { color: var(--accent); }
.paie-tete.retard { background: color-mix(in srgb, var(--danger) 14%, var(--surface-2)); }
.paie-tete.retard .mt { color: var(--danger); }
.paie-tete.solde { background: color-mix(in srgb, var(--ok) 14%, var(--surface-2)); }
.paie-tete.solde .mt { color: var(--ok); }
.paiements { display: flex; flex-direction: column; gap: 6px; margin: 12px 0; }
.paie-l {
  display: flex; align-items: center; gap: 10px; font-size: 14px;
  background: var(--surface-2); border-radius: var(--r-s); padding: 10px 12px;
}
.paie-l .d { flex: 1; min-width: 0; }
.paie-l .d small { display: block; color: var(--text-2); font-size: 12.5px; }
.paie-l .m { font-weight: 800; }
.paie-l .x { width: 34px; height: 34px; border-radius: 9px; flex: none; color: var(--text-2); font-size: 19px; background: transparent; }

/* Fiche client rappelée sous le champ « Client » */
.fiche-client {
  width: 100%; text-align: left; margin-top: 10px; padding: 12px 14px;
  background: color-mix(in srgb, var(--accent-2) 12%, var(--surface)); border: 1px solid color-mix(in srgb, var(--accent-2) 45%, var(--line));
  border-radius: var(--r-s); font-size: 13.5px; color: var(--text); line-height: 1.45;
}
.fiche-client b { color: var(--accent-2); }
.histo-l { display: flex; gap: 10px; align-items: center; background: var(--surface-2); border-radius: var(--r-s); padding: 11px 12px; margin-bottom: 6px; width: 100%; text-align: left; }
.histo-l .d { flex: 1; min-width: 0; }
.histo-l .d b { display: block; font-size: 14.5px; }
.histo-l .d small { color: var(--text-2); font-size: 12.5px; }

.choix-restaure { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.choix { text-align: left; padding: 14px; border-radius: var(--r-s); background: var(--surface-2); border: 1px solid var(--line); }
.choix b { display: block; font-size: 15px; margin-bottom: 3px; }
.choix span { font-size: 13px; color: var(--text-2); line-height: 1.45; }
.choix:first-child { border-color: var(--accent); }
/* Question sans réponse recommandée (le choix TGC) : les deux options partent à
   égalité, et seule celle que l'artisan touche s'allume. Sans ce bloc, « Je
   facture la TGC » serait souligné d'office et orienterait une réponse fiscale. */
.choix-restaure.neutre .choix { border-color: var(--line); }
.choix-restaure.neutre .choix.actif {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* ─────────────────────────── Navigation basse ─────────────────────────── */
/* ⚠️ PAS DE `backdrop-filter` ICI — ET C'EST UN CORRECTIF, PAS UN OUBLI.
   (2026-08-12) Cédric, capture d'écran à l'appui sur son iPhone : « lorsque je
   fais glisser la page d'accueil, le menu du bas bouge au milieu de la
   feuille », et pareil sur le devis. Les deux barres qui décrochaient étaient
   exactement les deux qui cumulaient `position: fixed` et `backdrop-filter` :
   celle-ci et `.barre-bas`. Les autres éléments fixes de la feuille —
   `.barre-annuler`, le toast — sont opaques et n'ont jamais bougé ;
   `.topbar` est en `sticky` et n'a pas été signalée.
   WebKit promeut un fixe flouté dans sa propre couche et ne la garde pas
   épinglée pendant le geste de défilement : la barre se pose au milieu de la
   page. On ENLÈVE DONC LA CAUSE plutôt que de maquiller l'effet par un
   `translateZ(0)` qui dépendrait du bon vouloir du navigateur — c'est la règle
   posée le 2026-08-11 en retirant l'attribut `list` du champ client.
   La teinte ne bouge pas : `--glass` EST `--surface` à 86 % (sombre) et 90 %
   (clair). On perd la transparence, pas la couleur.
   ⚠️ Non vérifiable ici : Chromium ne reproduit pas ce défaut. La preuve est
   sur un vrai iPhone. Le corpus fige la CAUSE (voir barres-fixes.mjs). */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: grid; grid-template-columns: repeat(6, 1fr); align-items: center;
  height: var(--tab-h); padding-bottom: var(--safe-b);
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.tabbar button { position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px; color: var(--text-2); padding: 6px 0; font-size: 10px; font-weight: 600; }
.tabbar button svg { width: 22px; height: 22px; }
.tabbar button.actif { color: var(--accent); }
/* Pastille « des factures attendent » sur l'onglet Suivi */
.tab-pastille {
  position: absolute; top: 2px; left: 50%; margin-left: 6px;
  width: 9px; height: 9px; border-radius: 50%; background: var(--danger);
  box-shadow: 0 0 0 2px var(--bg);
}
@media (max-width: 340px) { .tabbar button { font-size: 9px; } }
.tab-fab {
  width: 60px; height: 60px; border-radius: 50%; justify-self: center; margin-top: -22px;
  background: linear-gradient(150deg, var(--accent), #FF9E2C); color: #14181C !important;
  box-shadow: 0 8px 24px rgba(255,122,24,.45); display: grid !important; place-items: center;
}
.tab-fab svg { width: 27px !important; height: 27px !important; stroke-width: 2; }
.tab-fab:active { transform: scale(.93); }

/* ─────────────────────────── Modales & toast ─────────────────────────── */
.modale { position: fixed; inset: 0; z-index: 100; background: rgba(0,0,0,.62); backdrop-filter: blur(4px); display: grid; place-items: center; padding: 16px; }
/* max-height + défilement : sans eux, une modale plus haute que l'écran pousse
   ses boutons d'action hors du champ, et l'artisan se retrouve coincé dans une
   fenêtre qu'il ne peut ni valider ni fermer. Mesuré sur le rappel des mentions :
   797 px de contenu pour 720 px de fenêtre. « dvh » d'abord pour les mobiles,
   où la barre d'URL rétractable fausse « vh ». */
.modale-boite { background: var(--surface); border: 1px solid var(--line); border-radius: 22px; padding: 20px; width: 100%; max-width: 520px; box-shadow: var(--shadow); max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); overflow-y: auto; }
.modale-boite.grand { max-width: 620px; }
.modale-boite h3 { margin: 0 0 6px; font-size: 19px; }
.modale-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; flex-wrap: wrap; }
/* .btn-principal fait 100 % de large par défaut : dans une modale il doit rester en ligne */
.modale-actions .btn-principal { width: auto; flex: 1 1 auto; font-size: 16px; padding: 15px 22px; }
#canvasSign { width: 100%; height: 190px; background: #fff; border-radius: var(--r-s); border: 1px solid var(--line); touch-action: none; margin: 10px 0; display: block; }
.modal-liste { max-height: 52vh; overflow: auto; margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
/* flex: none — sans ça, dans un conteneur en colonne, un libellé qui passe sur
   deux lignes se fait comprimer et le prix disparaît sous le texte. */
.modal-item { flex: none; text-align: left; width: 100%; padding: 13px; border-radius: var(--r-s); background: var(--surface-2); border: 1px solid transparent; }
.modal-item:active { border-color: var(--accent); }
.modal-item b { display: block; font-size: 15px; font-weight: 600; }
.modal-item small { color: var(--text-2); font-size: 12.5px; }


.toast {
  position: fixed; left: 50%; bottom: calc(var(--tab-h) + 18px); transform: translateX(-50%);
  background: var(--text); color: var(--bg); padding: 13px 20px; border-radius: 999px;
  font-weight: 700; font-size: 14.5px; z-index: 200; box-shadow: var(--shadow);
  animation: toastIn .25s ease both; max-width: 90vw; text-align: center;
  /* Un toast INFORME, il n'attrape jamais un doigt. Trouvé par le corpus de
     navigateur le 2026-08-08 : posé en `z-index: 200` au-dessus du bas de
     l'écran, il recouvrait « Appliquer le changement » de la fenêtre de
     correction. Pendant les 3,6 s d'un « 1 ligne corrigée », l'appui suivant
     tombait DANS le toast et ne faisait rien — sans le moindre signe. Sur un
     chantier, ça se lit « l'application ne répond pas ». */
  pointer-events: none;
}
@keyframes toastIn { from { opacity: 0; transform: translate(-50%, 12px);} to { opacity: 1; transform: translate(-50%, 0);} }

/* ─────────────────────────── Aperçu A4 ─────────────────────────── */
.ecran-apercu { max-width: 900px; }
/* La barre coulisse SUR ELLE-MÊME, l'écran ne bouge pas (2026-08-07).
   Depuis l'ajout d'« Envoyer au client », les quatre tuiles demandaient 427 px
   pour 343 px de place. En `overflow-x: visible`, ce sont la barre ET la page
   qui débordaient : la page passait à 442 px de large sur un écran de 375, et
   tout l'écran se décalait quand on cherchait le quatrième bouton — « comme si
   je décalais carrément l'image de l'application ».
   `flex: 0 0 auto` + `nowrap` : chaque tuile garde sa largeur naturelle sur une
   seule ligne, au lieu d'être comprimée jusqu'à passer sur trois lignes.
   L'ancien `.btn-principal { flex: 1 }` faisait exactement l'inverse : il
   étirait « Envoyer » et écrasait les trois autres. */
.apercu-barre {
  display: flex; gap: 8px; margin-bottom: 14px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.apercu-barre::-webkit-scrollbar { display: none; }
/* `width: auto` explicite : `.btn-principal` est déclaré `width: 100%` — il
   est fait pour barrer un écran, pas pour vivre dans une rangée. Sans ça, à
   375 px, « Envoyer au client » occupait à lui seul les 343 px visibles et
   repoussait les trois autres hors de vue. */
.apercu-barre > button { flex: 0 0 auto; width: auto; white-space: nowrap; }
/* Paliers de zoom : mêmes pastilles que les filtres de la liste, portées à la
   cible de 56 px — c'est un bouton qu'on presse avec des gants. `white-space`
   remis à normal : .filtre est en `nowrap`, ce qui ferait déborder « Très
   gros » dans une colonne de ~105 px. */
.a4-zoom { display: flex; gap: 8px; margin-bottom: 12px; }
.a4-zoom .filtre { flex: 1; min-height: 56px; white-space: normal; }
/* -webkit-overflow-scrolling : sur un vieil iPhone (iOS < 13) c'est ce qui
   donne l'inertie au défilement, sans quoi balayer une page zoomée est râpeux. */
.a4-scroll { overflow-x: auto; padding-bottom: 20px; -webkit-overflow-scrolling: touch; }
/* Un `transform: scale()` agrandit le RENDU, pas la boîte de mise en page : la
   moitié droite d'une feuille zoomée serait donc inatteignable, à commencer par
   la colonne des montants. Ce pseudo-élément, large de la feuille réelle
   (variable posée par ajusterA4()), matérialise le débordement en mise en page.
   À 0 tant que rien n'est mesuré : il ne peut pas créer de scroll parasite. */
.a4-scroll::after { content: ""; display: block; height: 1px; width: var(--a4-large, 0); }
.a4 {
  width: 210mm; min-height: 297mm; background: #fff; color: #16202B;
  padding: 15mm 14mm; margin: 0 auto; box-shadow: 0 14px 44px rgba(0,0,0,.35);
  font-size: 10.5pt; line-height: 1.42; transform-origin: top left;
}
.a4 * { box-sizing: border-box; }
.pdf-tete { display: flex; justify-content: space-between; gap: 16mm; padding-bottom: 6mm; border-bottom: 2.5px solid var(--accent); }
.pdf-logo { max-width: 44mm; max-height: 24mm; object-fit: contain; margin-bottom: 3mm; display: block; }
.pdf-ent b { font-size: 14pt; display: block; letter-spacing: -.3px; }
.pdf-ent div { font-size: 9pt; color: #4A5A6B; }
.pdf-titre { text-align: right; flex: none; }
.pdf-titre h1 { margin: 0; font-size: 26pt; letter-spacing: -1px; color: var(--accent); line-height: .95; }
.pdf-titre .num { font-size: 12pt; font-weight: 800; margin-top: 1mm; }
.pdf-titre .meta { font-size: 9pt; color: #4A5A6B; margin-top: 2mm; }
.pdf-parties { display: grid; grid-template-columns: 1fr 1fr; gap: 8mm; margin: 7mm 0 5mm; }
.pdf-bloc { background: #F4F7FA; border-radius: 3mm; padding: 4mm 5mm; }
.pdf-bloc h3 { margin: 0 0 2mm; font-size: 8pt; text-transform: uppercase; letter-spacing: 1px; color: #6B7C8F; }
.pdf-bloc b { font-size: 11pt; }
.pdf-bloc div { font-size: 9.5pt; color: #33455A; }
.pdf-objet { margin: 0 0 5mm; font-size: 10.5pt; }
.pdf-objet b { color: #6B7C8F; text-transform: uppercase; font-size: 8pt; letter-spacing: 1px; display: block; margin-bottom: 1mm; }
table.pdf-t { width: 100%; border-collapse: collapse; margin-bottom: 3.5mm; }
table.pdf-t th { background: #16202B; color: #fff; font-size: 8.5pt; text-transform: uppercase; letter-spacing: .6px; padding: 2.1mm 2.5mm; text-align: left; font-weight: 700; }
table.pdf-t th.r, table.pdf-t td.r { text-align: right; }
table.pdf-t td { padding: 1.9mm 2.5mm; border-bottom: 1px solid #E1E8EF; font-size: 9.5pt; vertical-align: top; }
table.pdf-t tr:nth-child(even) td { background: #FAFCFD; }
table.pdf-t td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Filets verticaux entre colonnes — le pendant à l'écran de `SEPARATIONS`
   (pdf.js). Demandés au premier test terrain : « mettre dans le visuel du
   devis des lignes verticales fines pour délimiter les colonnes ». Le PDF les
   avait, l'aperçu non — or c'est l'aperçu que l'artisan relit AVANT d'envoyer,
   et c'est lui qu'imprime le bouton « Imprimer ».
   Deux exclusions, pour coller exactement au PDF, qui ne trace ces filets que
   sur les lignes d'article (pdf.js:469) : ni dans le bandeau d'en-tête sombre,
   ni sur les bandeaux de pièce et de sous-total, où un trait entre deux
   cellules fusionnées ne délimiterait rien et couperait le titre en deux. */
table.pdf-t tr:not(.pdf-piece):not(.pdf-sous-total) td:not(:last-child) {
  border-right: 1px solid #E8EFF4;
}
/* Bandeau de pièce et son sous-total, à l'image du PDF (voir pdf.js). Le
   « !important » sur le fond n'est pas de la paresse : la règle nth-child(even)
   ci-dessus s'applique à toutes les lignes du tableau, bandeaux compris, et un
   titre de section sur deux serait autrement blanc. */
table.pdf-t tr.pdf-piece td {
  background: #EDF1F7 !important; font-weight: 700; font-size: 10pt;
  border-bottom: 1px solid #CFD9E4; padding-top: 2.2mm; padding-bottom: 2.2mm;
}
table.pdf-t tr.pdf-piece td:first-child { border-left: 2.5px solid var(--accent); }
table.pdf-t tr.pdf-piece td.num { color: #4A5A6B; }
/* Le sous-total FERME la pièce (2026-08-07) : même bandeau que le titre, mais
   le montant y est en encre pleine — c'est la ligne que le client cherche.
   `.pdf-recap` a disparu avec le récapitulatif par poste, qui redonnait ces
   mêmes montants une seconde fois en fin de devis. */
table.pdf-t tr.pdf-sous-total td {
  background: #EDF1F7 !important; font-weight: 700; font-size: 9.4pt;
  border-bottom: 1px solid #CFD9E4; padding-top: 1.9mm; padding-bottom: 1.9mm;
}
table.pdf-t tr.pdf-sous-total td:first-child { border-left: 2.5px solid var(--accent); }
/* Comment on paie : encadré, en pleine largeur, pas une note de bas de page. */
/* Remonté en haut à gauche le 2026-08-08 (voir le gabarit dans app.js) :
   `max-width` le cale sur la largeur du bloc entreprise juste au-dessus, comme
   dans le PDF. Pleine largeur, il aurait la tête d'un bandeau, pas d'une
   information qu'on lit. */
.pdf-reglement { margin: 4mm 0 5mm; max-width: 56%; border: 1.5px solid var(--accent);
  border-radius: 3mm; background: #FFF7EF; padding: 2.5mm 4mm; }
.pdf-reglement h4 { margin: 0 0 1mm; font-size: 7.5pt; text-transform: uppercase;
  letter-spacing: 1px; color: #B24A00; }
.pdf-reglement p { margin: 0; font-size: 10pt; font-weight: 700; color: #16202B; }
/* Mention de franchise, sous le bloc des totaux qu'elle explique. */
.pdf-franchise { margin: 2mm 0 0; font-size: 7.8pt; line-height: 1.45; color: #4A5A6B; }
.pdf-bas { display: grid; grid-template-columns: 1fr 78mm; gap: 8mm; align-items: start; }
.pdf-tot { border: 1.5px solid #16202B; border-radius: 3mm; overflow: hidden; }
.pdf-tot .l { display: flex; justify-content: space-between; padding: 2.2mm 4mm; font-size: 10pt; }
.pdf-tot .l.sep { border-top: 1px solid #E1E8EF; }
.pdf-tot .l.ttc { background: #16202B; color: #fff; font-weight: 800; font-size: 12.5pt; padding: 3.4mm 4mm; }
.pdf-tot .l.ac { background: #FFF4E8; font-weight: 700; }
.pdf-lettres { font-size: 9pt; font-style: italic; color: #33455A; margin: 3mm 0 5mm; }
/* Pleine largeur depuis le 2026-08-08 : elles vivaient dans la colonne de
   gauche du bloc à deux colonnes, 164 px sur 310, ce qui en faisait un pavé. */
.pdf-mentions { margin-top: 5mm; font-size: 8.2pt; color: #4A5A6B;
  white-space: pre-line; line-height: 1.5; }
.pdf-sign { margin-top: 6mm; display: grid; grid-template-columns: 1fr 1fr; gap: 8mm; }
.pdf-sign .box { border: 1px dashed #9FB0C2; border-radius: 3mm; padding: 3mm; min-height: 30mm; font-size: 8.5pt; color: #6B7C8F; }
.pdf-sign .box img { max-width: 100%; max-height: 20mm; display: block; margin-top: 1mm; }
/* ⛔ .photos / .photo-item / .photo-x / .pdf-photos ont été retirés le
   2026-08-11 avec les photos de chantier (demande de Cédric). */
.pdf-pied { margin-top: 6mm; padding-top: 3mm; border-top: 1px solid #E1E8EF; font-size: 7.6pt; color: #6B7C8F; text-align: center; }

/* ─────────────────────────── Impression ─────────────────────────── */
@media print {
  @page { size: A4; margin: 0; }
  body { background: #fff; padding: 0; }
  .topbar, .tabbar, .apercu-barre, .a4-zoom, .barre-bas, .modale, .toast, .bandeau-demo, .bandeau-licence { display: none !important; }
  .ecran:not(#ecran-apercu) { display: none !important; }
  .ecran-apercu { padding: 0; max-width: none; }
  .a4-scroll { overflow: visible; padding: 0; height: auto !important; }
  /* La cale de débordement du zoom écran n'a rien à faire sur du papier : en
     la laissant, sa largeur pourrait provoquer une seconde page vide. */
  .a4-scroll::after { display: none !important; }
  /* l'aperçu écran est mis à l'échelle : il faut l'annuler à l'impression */
  .a4 { box-shadow: none; margin: 0; width: 210mm; padding: 12mm 12mm; transform: none !important; }
  table.pdf-t tr { break-inside: avoid; }
  .pdf-bas, .pdf-sign { break-inside: avoid; }
}

/* ─────────────────────────── Grands écrans ─────────────────────────── */
@media (min-width: 720px) {
  .hero-titre { font-size: 52px; }
  .actions-sec { grid-template-columns: repeat(4, 1fr); }
}
/* ── LES CASES DE LA LIGNE DE DEVIS, SUR UN TÉLÉPHONE ────────────────────────
   Trois colonnes, donc TGC et Pièce passent à la ligne. À quatre sur une seule
   rangée, la colonne du prix tombe à 88 px : mesuré le 2026-08-07 à 414 px de
   large (iPhone 14/15), un prix à huit chiffres y débordait de 33 px et la TGC
   n'avait plus qu'un pixel de marge. Une rangée de plus ne coûte rien ; un
   montant tronqué est ce qui part chez le client.

   Le seuil est passé de 380 à 560 px le 2026-08-07. À 380 il ne couvrait que
   les petits iPhone — or les iPhone courants font 390 à 430 px et tombaient
   donc dans la mise en page à quatre colonnes, la plus serrée des deux. Une
   media query réglée sous la taille des appareils qu'elle vise ne protège
   personne.

   62/56 → 100/72 pour les deux premières colonnes : la flèche est passée à
   24 px et réserve 32 px à droite ; une colonne de 62 px ne laissait plus que
   16 px de texte, et « 12,5 » comme « m² » y étaient tronqués. 100 px tient le
   pire cas réel — le métré arrondit à deux décimales (metre.js:156), donc
   « 123.45 ». */
@media (max-width: 560px) {
  .ligne-g { grid-template-columns: 100px 72px 1fr; }
  .ligne-total { grid-column: 1 / -1; text-align: right; padding: 4px 0 0; }
}
/* Écrans très étroits (iPhone SE de 2016, 320 px — le « vieil iPhone » que ce
   produit doit servir). Mesuré le 2026-08-07 : à trois colonnes, il ne restait
   que 38 px pour le prix, et même 200 000 F — un montant parfaitement ordinaire
   — débordait de 23 px. Le prix prend donc sa rangée entière : c'est le champ
   qu'on relit avant d'envoyer, il ne peut pas être celui qu'on rogne.
   Une classe `.mini-prix` plutôt qu'un `:has(input[data-pave])` : `:has()`
   n'existe pas sur les Safari anciens, c'est-à-dire précisément sur les
   téléphones que cette règle vise. */
@media (max-width: 360px) {
  .ligne-g { grid-template-columns: 100px 1fr; }
  .mini-prix { grid-column: 1 / -1; }
}
@media (max-width: 380px) {
  .hero-titre { font-size: 34px; }
}

/* ═══════════ Mémoire pleine : blocage franc, pas un toast fugace ═══════════
   Un toast de 2,4 s laisse l'artisan travailler sur des données qui ne
   s'enregistrent plus. Ici on barre l'écran jusqu'à ce qu'il ait sauvegardé
   ou fait de la place. */
.blocage {
  position: fixed; inset: 0; z-index: 1200; background: rgba(0,0,0,.62);
  display: flex; align-items: center; justify-content: center; padding: 20px;
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  overflow-y: auto;
}
.blocage-boite {
  width: 100%; max-width: 400px; background: var(--surface);
  border: 2px solid var(--danger); border-radius: var(--r); padding: 24px 20px 20px;
  box-shadow: var(--shadow); text-align: center;
}
.blocage-ic { font-size: 38px; margin: 0 0 8px; }
.blocage-boite h2 { font-size: 18px; margin: 0 0 12px; line-height: 1.3; }
.blocage-boite p { color: var(--text-2); font-size: 15px; margin: 0 0 12px; line-height: 1.5; text-align: left; }
.blocage-boite p b { color: var(--text); }
.blocage-boite button { margin-top: 8px; }
.blocage-boite .btn-principal { width: 100%; }

/* ═══════════ Jauge d'espace (Réglages) ═══════════ */
.espace { margin-top: 10px; }
.espace-t { display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
            font-size: 14px; color: var(--text-2); margin-bottom: 6px; }
.espace-t b { color: var(--text); font-size: 14px; white-space: nowrap; }
.espace-b { height: 9px; border-radius: 20px; background: var(--line); overflow: hidden; }
.espace-b i { display: block; height: 100%; border-radius: 20px; transition: width .35s ease; }
.espace-b i.ok        { background: var(--ok); }
.espace-b i.attention { background: var(--accent); }
.espace-b i.danger    { background: var(--danger); }
.espace small { display: block; margin-top: 7px; font-size: 13px; color: var(--text-2); line-height: 1.45; }

/* ═══════════ Métré vocal ═══════════
   Le calcul doit être lisible d'un coup d'œil, pas seulement juste :
   l'artisan vérifie ce que l'app a entendu, il ne refait pas la
   multiplication de tête. */
.ligne-metre {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  flex-wrap: wrap; margin-top: 8px; padding: 9px 11px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 11px;
  font-size: 14.5px; color: var(--text-2);
}
.ligne-metre .metre-calc b { color: var(--text); }
.ligne-metre .lien { padding: 6px 2px; }
.mes-champs { display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 4px; }
.mes-champs input.mes-d {
  width: 92px; min-height: 56px; text-align: center; font-size: 18px; font-weight: 600;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface); color: var(--text);
}
.mes-x { font-size: 18px; color: var(--text-2); }
.mes-note { font-size: 15px; color: var(--text-2); }
.mes-res { text-align: center; font-size: 19px; margin: 10px 0 2px; color: var(--text-2); }
.mes-res b { color: var(--accent-2, var(--accent)); font-size: 22px; }

/* ═══════════ Correction d'une ligne ═══════════ */
.cor-dit { font-size: 15.5px; color: var(--text); margin: 2px 0 6px; line-height: 1.45; }
.cor-actuel { font-size: 14px; color: var(--text-2); margin: 0 0 12px; }
.cor-prix { width: 100%; margin-bottom: 12px; text-align: left; }
.cor-entete { font-size: 13px; text-transform: uppercase; letter-spacing: .05em;
              color: var(--text-2); margin: 2px 0 6px; }
/* Cibles confortables : on choisit debout, sur un chantier. */
#corListe .modal-item { min-height: 56px; }
#corListe .modal-item b { font-size: 16px; }

/* ═══════════ Confirmation d'apprentissage ═══════════ */
.app-expl { text-align: center; font-size: 16px; color: var(--text-2); margin: 6px 0; }
.app-expl b { color: var(--text); }
.app-mot {
  width: 100%; min-height: 56px; text-align: center; font-size: 19px; font-weight: 700;
  border: 2px solid var(--accent); border-radius: 13px;
  background: var(--surface); color: var(--text); padding: 0 12px;
}
.app-conflit {
  font-size: 14px; line-height: 1.45; color: var(--text-2); margin: 12px 0 0;
  padding: 10px 12px; background: var(--bg); border: 1px solid var(--line); border-radius: 11px;
}

/* ═══════════ Mots appris (écran Tarifs) ═══════════ */
.mots { display: flex; flex-direction: column; gap: 8px; }
.mot {
  display: flex; align-items: center; gap: 10px; padding: 11px 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
}
.mot-t { flex: 1; min-width: 0; }
.mot-t b { display: block; font-size: 15.5px; }
.mot-t small { display: block; font-size: 13px; color: var(--text-2); margin-top: 2px; }
.mot-x {
  flex: none; width: 44px; height: 44px; border-radius: 12px;
  border: 1px solid var(--line); background: transparent; color: var(--text-2); font-size: 20px;
}
.ligne-appris {
  margin-top: 6px; font-size: 13.5px; line-height: 1.45; color: var(--text-2);
}

/* ═══════════ Chantiers types ═══════════ */

/* ── Bandeau d'enregistrement (iPhone) ──────────────────────────────────────
   Affiché seulement quand la reconnaissance en direct est refusée par la
   plateforme. Il remplace le retour visuel que l'artisan n'a pas : sans lui,
   l'écran reste vide du début à la fin de la dictée et rien ne distingue un
   enregistrement en cours d'une application plantée. */
.enr-bandeau{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  margin:0 0 14px;padding:13px 15px;border-radius:12px;
  background:#FFF4F2;border:1px solid #F6C9C0}
.enr-pastille{width:12px;height:12px;border-radius:50%;background:#D64530;
  flex:0 0 auto;animation:enrPulse 1.15s ease-in-out infinite}
@keyframes enrPulse{0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.35;transform:scale(.8)}}
.enr-texte{font-weight:700;color:#8A2B1B}
.enr-duree{margin-left:auto;font-variant-numeric:tabular-nums;font-weight:700;
  font-size:19px;color:#8A2B1B}
.enr-aide{flex:1 0 100%;margin:2px 0 0;font-size:13px;line-height:1.45;color:#7A4034}

/* Jauge de niveau sonore. Volontairement large et très contrastée : elle se lit
   en plein soleil, à bout de bras, sans lâcher ce qu'on tient. */
.niveau{margin:0 0 14px}
.niveau-piste{height:14px;border-radius:7px;background:var(--fond2,#E7ECF2);
  overflow:hidden;border:1px solid rgba(0,0,0,.08)}
.niveau-jauge{display:block;height:100%;width:0;border-radius:7px;
  background:#9AA5B1;transition:width .1s linear,background .2s}
.niveau-txt{margin-top:6px;font-size:14px;font-weight:700;color:var(--texte2,#5B6672)}

.niveau-bon    .niveau-jauge{background:#1B9E5A}
.niveau-bon    .niveau-txt  {color:#157A46}
.niveau-faible .niveau-jauge{background:#E08A1E}
.niveau-faible .niveau-txt  {color:#9A5B04}
.niveau-sature .niveau-jauge{background:#D64530}
.niveau-sature .niveau-txt  {color:#A32A18}
.niveau-muet   .niveau-jauge{background:#8892A0}
.niveau-muet   .niveau-txt  {color:#A32A18}

/* Bilan après l'arrêt : plus une jauge, un avertissement à lire avant de créer
   le devis. La barre disparaît, le message reste. */
.niveau-alerte .niveau-piste{display:none}
.niveau-alerte{padding:12px 14px;border-radius:12px;
  background:#FFF4F2;border:1px solid #F6C9C0}
.niveau-alerte .niveau-txt{margin:0;font-size:14px;line-height:1.45;color:#8A2B1B}

@media (prefers-color-scheme:dark){
  .niveau-piste{background:#232A31;border-color:rgba(255,255,255,.1)}
  .niveau-bon    .niveau-txt{color:#5BD394}
  .niveau-faible .niveau-txt{color:#F3B94E}
  .niveau-sature .niveau-txt,.niveau-muet .niveau-txt{color:#F3907C}
  .niveau-alerte{background:#2B1713;border-color:#5A2A20}
  .niveau-alerte .niveau-txt{color:#F3B5A6}
}

/* Ton du message d'état : une couleur vaut mieux qu'une phrase de plus. */
.dictee-etat.etat-ok{color:#1B7F4B;font-weight:700}
.dictee-etat.etat-ko{color:#B3341F;font-weight:700}

@media (prefers-color-scheme: dark){
  .enr-bandeau{background:#2B1713;border-color:#5A2A20}
  .enr-texte,.enr-duree{color:#F3B5A6}
  .enr-aide{color:#D8A093}
}

/* Titre de pièce dans l'aperçu de dictée — même structure que sur le PDF,
   pour que l'artisan vérifie ce que son client verra. */
.pv-piece{margin:12px 0 4px;padding:5px 10px;border-radius:7px;
  background:var(--fond2,#EEF2F7);font-weight:800;font-size:13px;
  letter-spacing:.03em;text-transform:uppercase}
.pv-piece:first-child{margin-top:2px}

/* Le sélecteur de pièce occupe toute la largeur sous les quatre champs
   chiffrés : un nom de pièce est plus long qu'une unité ou un taux. */
.ligne-g .mini-large{grid-column:1 / -1}
/* Sur téléphone, la TGC passe seule à la ligne (trois colonnes) et laissait
   les deux tiers de sa rangée vides. La pièce vient s'y ranger à côté d'elle :
   elle garde largement la place qu'il lui faut, et la ligne de devis perd un
   trou qui ne servait à rien. Déclarée ICI, après la règle ci-dessus, sans
   quoi celle-là gagnerait — à spécificité égale, c'est la dernière écrite qui
   l'emporte, et la media query est plus haut dans le fichier. */
@media (max-width: 560px) {
  .ligne-g .mini-large { grid-column: 2 / -1; }
}

/* Dictée qui n'est pas un devis (voir demandeHorsSujet dans parser.js).
   Ton neutre, pas alarmant : l'artisan n'a rien fait de mal, il a juste
   demandé quelque chose que l'application ne sait pas faire. */
.hors-sujet { border: 1px solid var(--line); border-radius: var(--r); padding: 16px; background: var(--surface-2); }
.hors-sujet b { display: block; font-size: 16px; margin-bottom: 6px; }
.hors-sujet p { margin: 0; color: var(--text-2); font-size: 14.5px; line-height: 1.5; }

/* Retouches dictées : la couleur dit le sens de l'opération avant même la
   lecture — rouge on enlève, ambre on change, vert on ajoute. Bordure à
   gauche seulement, pas de fond teinté : ces lignes se lisent en série. */
.op-suppr { border-left: 3px solid var(--danger); }
.op-qte, .op-pu, .op-des { border-left: 3px solid var(--warn); }
.op-ajout { border-left: 3px solid var(--ok); }
/* « c'est déjà le prix » (2026-08-12) : ni une modification, ni une erreur —
   une confirmation. Bordure neutre, pour qu'elle ne se lise pas comme une
   alerte au milieu d'une série de corrections. */
.op-deja { border-left: 3px solid var(--text-2); }
#retoucheListe { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
#retoucheListe .modal-item small { display: block; color: var(--text-2); font-size: 12.5px; margin-top: 2px; }

/* ── PRIX OU QUANTITÉ ? LA QUESTION QU'ON POSE AU LIEU DE DEVINER (2026-08-08)
   Un nombre dicté sans marqueur (« quatre-vingt-cinq mille », le doigt posé sur
   une ligne) n'est plus deviné — il est demandé. Bordure ambre comme les autres
   modifications, et deux cibles franches : cette fenêtre s'ouvre sur un chantier,
   au soleil, avec des mains sales. 56 px de haut, comme #corListe plus haut. */
.op-choix { border-left: 3px solid var(--warn); }
/* Empilés, pas côte à côte : à 390 px, deux boutons de 139 px coupaient
   « 95 000 F → 85 000 F » sur deux lignes avec le « F » orphelin (mesuré).
   Pleine largeur, c'est aussi une cible plus franche pour un pouce sale. */
.choix-boutons { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.btn-choix {
  min-height: 56px; padding: 10px; border-radius: var(--r-s);
  background: var(--surface); border: 2px solid var(--line);
  color: var(--text); font: inherit; font-weight: 600; text-align: center;
}
/* 12 px et pas de coupure du symbole : à 13 px, « 95 000 F → 85 000 F »
   renvoyait le « F » seul à la ligne suivante (vu à l'écran, 390 px). */
.btn-choix small { display: block; color: var(--text-2); font-weight: 400;
  margin-top: 3px; font-size: 12px; line-height: 1.35; }
/* Le fond NE CHANGE PAS à la sélection. Première version : il passait à
   --surface-2, qui est exactement le fond de la carte derrière — la case
   choisie se fondait dedans au lieu de ressortir (vu à l'écran). C'est
   l'anneau qui la désigne, pas un aplat. */
.btn-choix.choisi { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.btn-choix.choisi small { color: var(--text); }

/* ── CORRIGER UNE PRESTATION À LA VOIX, EN RESTANT SUR LE DEVIS ──────────────
   Feuille par le bas, plafonnée à 76 % de la hauteur : le devis doit rester
   visible au-dessus, sinon on a juste refait un changement de page avec des
   coins arrondis. C'est la demande même de Cédric — « que l'application ne
   change pas de page ». */
/* Fond nettement plus clair que les autres modales (.62 → .30) et flou réduit :
   celles-là veulent qu'on ne regarde plus qu'elles ; celle-ci veut exactement
   l'inverse — la prestation qu'on corrige doit rester lisible derrière, c'est
   la demande. */
#modaleLigneVocale { background: rgba(0,0,0,.30); backdrop-filter: blur(1.5px); }
/* 72 dvh, et pas moins : mesuré, le contenu de la feuille (micro, ondes,
   chrono, texte entendu) réclame ~340 px. En dessous, la feuille se met à
   défiler et le micro sort du champ — or c'est précisément le « visuel » que
   cette fenêtre existe pour montrer. Le devis reste lisible au-dessus ; la
   ligne complète, elle, ne peut de toute façon pas tenir dans la bande libre
   (351 px de haut à elle seule), on n'en montre donc que le haut. */
.lignvoc { max-width: 520px; max-height: 72dvh; display: flex; flex-direction: column; gap: 8px; }
/* Les deux boutons côte à côte, jamais l'un sous l'autre : « Appliquer le
   changement » est assez long pour déclencher le retour à la ligne de
   .modale-actions, et la feuille y gagnait une rangée de haut pour rien. */
.lignvoc .modale-actions { flex-wrap: nowrap; margin-top: 0; }
.lignvoc .modale-actions .btn-ghost { flex: none; }
.lignvoc .modale-actions .btn-principal { font-size: 15px; padding: 15px 12px; }
.lignvoc-tete h3 { margin: 0; }
/* Le nom de la prestation visée. Il manquait totalement à l'ancien écran :
   on corrigeait « une ligne » sans jamais savoir laquelle. */
.lignvoc-cible { margin: 4px 0 0; font-weight: 800; font-size: 16px; color: var(--accent); }
.lignvoc-corps { overflow-y: auto; min-height: 0; }
.lignvoc-apercu { display: flex; flex-direction: column; gap: 8px; }
.lignvoc-apercu:empty { display: none; }
/* Le micro plein écran fait 132 px : dans une feuille, il mangerait toute la
   place qui doit rester au devis. On le réduit ICI seulement — l'écran de
   dictée entier n'est pas touché, et ce sont les MÊMES nœuds, déplacés. */
.lignvoc-corps .mic-big { width: 78px; height: 78px; }
.lignvoc-corps .mic-big .mic-svg { width: 32px; height: 32px; }
.lignvoc-corps .mic-big.on .stop-svg { width: 28px; height: 28px; }
.lignvoc-corps .scene-mic { padding: 4px 0 2px; gap: 6px; }
.lignvoc-corps .ondes { height: 24px; }
.lignvoc-corps .transcript { min-height: 52px; }
.lignvoc-corps .transcript-actions { margin-top: 6px; }
/* L'exemple n'a aucun sens quand on corrige UNE ligne : il collerait une
   dictée de devis entier dans un champ qui n'attend que « six ». */
.lignvoc-corps #btnExemple { display: none; }
/* L'astuce clavier n'est PAS masquée en CSS ici, et c'est délibéré : elle est
   affichée à titre préventif sur l'écran de dictée (où elle remplissait toute
   la feuille et repoussait le micro hors de vue), mais elle est aussi le seul
   mode d'emploi affiché quand le micro est réellement refusé par l'appareil.
   Un `display: none` aurait supprimé les deux — on laisserait alors l'artisan
   devant « dictez avec la touche 🎤 » sans lui dire où elle est. La distinction
   se fait donc en JS, dans reinitialiserCapture(). */
/* Rien d'entendu, rien de compris : on le dit, plutôt que de laisser une
   zone vide qui ressemble à une panne. */
.lignvoc-rien { margin: 0; padding: 12px 13px; border-radius: var(--r-s);
  background: var(--surface-2); color: var(--text-2); font-size: 14px; line-height: 1.45; }
.lignvoc-rien b { color: var(--text); }
/* La prestation en cours de correction, mise en évidence derrière la feuille :
   c'est ce qui relie ce qu'on dit à ce qu'on regarde. */
.ligne.en-correction { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Moteur V2 : signal du remplacement d'aperçu (2026-08-06) ──────────────
   Quand l'analyse complète remplace l'aperçu regex, les lignes se
   surlignent brièvement puis s'estompent — assez pour dire « ça vient de
   changer », jamais un voile ni un blocage. */
#apercuLignes.affine .pv { animation: affinage 1.4s ease-out; }
@keyframes affinage {
  0%   { background: color-mix(in srgb, var(--accent) 16%, transparent); }
  100% { background: transparent; }
}

/* ═══════════════════════════════ PAYSAGE ═══════════════════════════════════
   « Permettre à l'appli le mode paysage » — premier test terrain.

   Le manifeste ne l'a JAMAIS interdit (`"orientation": "any"`) : ce qui
   manquait, c'est une mise en page qui en tienne compte. En 844 × 390, la
   hauteur utile tombe à ~250 px une fois la barre du haut et les onglets
   déduits — or le seul micro de l'écran de dictée en occupait 132, les ondes
   46, et le titre d'accueil 40 px de corps.

   On ne réorganise rien et on ne cache rien : on comprime ce qui est vertical
   et décoratif, et le micro passe à CÔTÉ des ondes au lieu d'être au-dessus.
   Les cibles tactiles restent au-dessus du seuil du pouce — 92 px pour le
   micro, 48 px pour les touches du pavé, contre un minimum usuel de 44.

   `max-height` et pas seulement `orientation` : une tablette en paysage a la
   place, elle garde la mise en page normale. C'est la HAUTEUR qui manque, pas
   l'orientation. */
@media (orientation: landscape) and (max-height: 500px) {
  .ecran { padding-top: 10px; }
  .bloc { margin: 14px 0; }
  .hero { padding: 6px 0 4px; }
  .hero-titre { font-size: 26px; margin-bottom: 8px; letter-spacing: -.8px; }
  .mic-cta { padding: 14px 26px 12px; font-size: 16px; }
  .mic-cta .mic-svg { width: 30px; height: 30px; }

  /* Le micro à côté des ondes et du chrono : trois éléments sur une ligne
     plutôt qu'une colonne de 250 px. */
  .scene-mic { flex-direction: row; justify-content: center; gap: 20px; padding: 6px 0; }
  .ondes { height: 28px; }
  .mic-big { width: 92px; height: 92px; }
  .mic-big .mic-svg { width: 38px; height: 38px; }
  .mic-big.on .stop-svg { width: 32px; height: 32px; }

  /* 64 px par touche ne tiennent pas dans 390 px de haut, même avec le
     défilement de la modale : on tombe à 48, au-dessus du seuil tactile. */
  .pave button { min-height: 48px; font-size: 22px; }
  .pave .pave-mille { font-size: 19px; }
  #canvasSign { height: 130px; }
}
