
/* ---------------------------------------------------------------- jetons */

:root {
  --papier:            #f7f8f7;
  --papier-carte:      #ffffff;
  --papier-creux:      #edf0f1;

  --encre:             #16202a;
  --encre-doux:        #5b6772;
  --encre-tenu:        #8b959c;

  --trait:             #d3d9dc;
  --trait-fort:        #a9b3b9;

  --acier:             #26557f;
  --acier-sombre:      #1b3f5f;
  --acier-voile:       #e7eef4;

  --conforme:          #1b6b48;
  --conforme-voile:    #e6f1eb;
  --limite:            #8a6100;
  --limite-voile:      #f6eedc;
  --non-conforme:      #a82521;
  --non-conforme-voile:#f8e8e7;

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", "JetBrains Mono",
          Menlo, Consolas, "Liberation Mono", monospace;

  --r: 3px;
  --pas: 4px;
  --largeur-texte: 68ch;
  --largeur-page: 1180px;
}

/* -------------------------------------------------------------- socle */

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

/* L'attribut hidden doit l'emporter. Une règle d'auteur qui fixe la
   propriété display — .case en inline-flex, par exemple — masque la
   valeur par défaut du navigateur et laisse l'élément visible. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--papier);
  color: var(--encre);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  text-underline-offset: 0.18em;
}

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.011em; line-height: 1.25; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.125rem; }
h3 { font-size: 1rem; }

p { margin: 0 0 0.75rem; max-width: var(--largeur-texte); }
p:last-child { margin-bottom: 0; }

a { color: var(--acier); text-decoration-thickness: 1px; }
a:hover { color: var(--acier-sombre); }

:focus-visible {
  outline: 2px solid var(--acier);
  outline-offset: 2px;
  border-radius: 1px;
}

/* Chiffres tabulaires partout où une donnée est relevée. */
.donnee, input, select, td .donnee, .mesure__valeur, .mesure__tolerance {
  font-variant-numeric: tabular-nums;
}

.donnee {
  font-family: var(--mono);
  font-size: 0.9375em;
  letter-spacing: -0.01em;
}

.surtitre {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--encre-doux);
  margin: 0 0 calc(var(--pas) * 1.5);
}

.discret { color: var(--encre-doux); font-size: 0.875rem; }

/* ------------------------------------------------------------ bandeau */

.bandeau {
  background: var(--encre);
  color: #fff;
  border-bottom: 3px solid var(--acier);
}

/*
 * La barre passe à la ligne plutôt que de pousser la page.
 *
 * Cinq onglets, le nom de l'utilisateur et le bouton de déconnexion font
 * 574 px sur une seule ligne, la marque en plus. En dessous d'environ
 * 860 px la barre débordait et c'est la PAGE ENTIÈRE qui défilait
 * latéralement — y compris la feuille de saisie, qui n'y était pour
 * rien. Le renvoi à la ligne ne coûte rien tant qu'il reste de la place.
 */
.bandeau__interieur {
  max-width: var(--largeur-page);
  margin: 0 auto;
  padding: 0 calc(var(--pas) * 5);
  min-height: 56px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: calc(var(--pas) * 2) calc(var(--pas) * 5);
}

.bandeau__marque {
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
}

/* Le logo remplace le mot : la barre porte la marque de l'entreprise,
   pas le nom générique du domaine. Version claire, posée sur l'encre. */
.bandeau__logo { height: 26px; width: auto; display: block; }

.bandeau__marque span {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: #9fb4c6;
  display: block;
  font-weight: 400;
}

.bandeau__liens {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--pas) * 2) calc(var(--pas) * 4);
  margin-left: auto;
  align-items: center;
}

.bandeau__lien {
  color: #cdd8e1;
  text-decoration: none;
  font-size: 0.9375rem;
  padding: calc(var(--pas) * 2) 0;
}
button.bandeau__lien {
  background: none;
  border: none;
  font: inherit;
  font-size: 0.9375rem;
  cursor: pointer;
}

.bandeau__lien:hover, .bandeau__lien[aria-current="page"] { color: #fff; }
.bandeau__lien[aria-current="page"] { box-shadow: inset 0 -2px 0 var(--acier); }

/* ----------------------------------------------------------- fil d'ariane */
/* La hiérarchie cabinet › site › local › appareil est le modèle de données
   lui-même : le fil n'est pas un ornement, c'est la structure du parc. */

.fil {
  background: var(--papier-carte);
  border-bottom: 1px solid var(--trait);
}

.fil__interieur {
  max-width: var(--largeur-page);
  margin: 0 auto;
  padding: calc(var(--pas) * 2) calc(var(--pas) * 5);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: calc(var(--pas) * 2);
  font-size: 0.875rem;
}

.fil a { color: var(--encre-doux); text-decoration: none; }
.fil a:hover { color: var(--acier); text-decoration: underline; }
.fil__separateur { color: var(--trait-fort); }
.fil__actuel { color: var(--encre); font-weight: 500; }

/* -------------------------------------------------------------- page */

.page {
  max-width: var(--largeur-page);
  margin: 0 auto;
  padding: calc(var(--pas) * 7) calc(var(--pas) * 5) calc(var(--pas) * 14);
}

.page__tete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: calc(var(--pas) * 4);
  margin-bottom: calc(var(--pas) * 6);
}
.page__tete > div:first-child { flex: 1 1 22rem; }
.page__actions { display: flex; gap: calc(var(--pas) * 2); flex-wrap: wrap; }

.pile > * + * { margin-top: calc(var(--pas) * 5); }

/* ------------------------------------------------------------- cartes */

.carte {
  background: var(--papier-carte);
  border: 1px solid var(--trait);
  border-radius: var(--r);
}

.carte__tete {
  padding: calc(var(--pas) * 3) calc(var(--pas) * 4);
  border-bottom: 1px solid var(--trait);
  display: flex;
  align-items: center;
  gap: calc(var(--pas) * 3);
}
.carte__tete h2 { font-size: 0.9375rem; }
.carte__tete .discret { margin-left: auto; }

.carte__corps { padding: calc(var(--pas) * 4); }
.carte__corps--serre { padding: 0; }

/* ------------------------------------------------------------ verdicts */

.verdict {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--pas) * 1.5);
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: 3px 8px;
  border-radius: var(--r);
  border: 1px solid;
  white-space: nowrap;
}
.verdict::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.verdict--conforme     { color: var(--conforme);     background: var(--conforme-voile);     border-color: #bcd9c9; }
.verdict--limite       { color: var(--limite);       background: var(--limite-voile);       border-color: #e0cda0; }
.verdict--non-conforme { color: var(--non-conforme); background: var(--non-conforme-voile); border-color: #e8bcba; }
.verdict--neutre       { color: var(--encre-doux);   background: var(--papier-creux);       border-color: var(--trait); }

/* ------------------------------------------------------- état documentaire */

.etat {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--encre-doux);
  border-left: 2px solid var(--trait-fort);
  padding-left: calc(var(--pas) * 2);
}
.etat--finalise  { border-color: var(--conforme); color: var(--conforme); }
.etat--transmis  { border-color: var(--acier);    color: var(--acier); }
.etat--annule    { border-color: var(--non-conforme); color: var(--non-conforme); text-decoration: line-through; }

/*
 * Étalonnage des instruments. Seuls les deux premiers appellent une
 * action : un rendez-vous pris et un instrument parti se lisent au
 * passage, ils ne se traitent pas.
 */
.etat--echu      { border-color: var(--non-conforme); color: var(--non-conforme); }
.etat--a-prendre { border-color: var(--limite);       color: var(--limite); }
.etat--rdv       { border-color: var(--conforme);     color: var(--conforme); }
.etat--parti     { border-color: var(--acier);        color: var(--acier); }

/* ------------------------------------------------------------ tableaux */

table { border-collapse: collapse; width: 100%; }

th {
  text-align: left;
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 500;
  color: var(--encre-doux);
  padding: calc(var(--pas) * 2.5) calc(var(--pas) * 3);
  border-bottom: 1px solid var(--trait-fort);
  white-space: nowrap;
}

td {
  padding: calc(var(--pas) * 2.5) calc(var(--pas) * 3);
  border-bottom: 1px solid var(--trait);
  vertical-align: baseline;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: #fafbfb; }

.nombre { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* Rail de conformité : le profil de l'appareil se lit verticalement. */
.mesures td:first-child,
.mesures th:first-child { border-left: 3px solid transparent; padding-left: calc(var(--pas) * 3); }
.mesures tr.est-conforme     td:first-child { border-left-color: var(--conforme); }
.mesures tr.est-limite       td:first-child { border-left-color: var(--limite); }
.mesures tr.est-non-conforme td:first-child { border-left-color: var(--non-conforme); }
.mesures tr.est-non-conforme { background: var(--non-conforme-voile); }
.mesures tr.est-non-conforme:hover { background: #f4dedd; }

.mesure__tolerance { font-family: var(--mono); font-size: 0.8125rem; color: var(--encre-doux); }

/* ------------------------------------------------------------ formulaire */

form { margin: 0; }

.champ { margin-bottom: calc(var(--pas) * 4); }
.champ:last-child { margin-bottom: 0; }

.etiquette {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  margin-bottom: calc(var(--pas) * 1.5);
}
.etiquette__note { font-weight: 400; color: var(--encre-doux); }

input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="date"], input[type="search"],
select, textarea {
  font: inherit;
  width: 100%;
  min-height: 44px;
  padding: calc(var(--pas) * 2) calc(var(--pas) * 3);
  color: var(--encre);
  background: var(--papier-carte);
  border: 1px solid var(--trait-fort);
  border-radius: var(--r);
}
input[type="number"], input[inputmode="decimal"] {
  font-family: var(--mono);
  text-align: right;
}
textarea { min-height: 6rem; resize: vertical; }

input:focus, select:focus, textarea:focus { border-color: var(--acier); }

input[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--non-conforme);
  background: var(--non-conforme-voile);
}

.aide { font-size: 0.8125rem; color: var(--encre-doux); margin-top: calc(var(--pas) * 1); }
.erreur-champ { font-size: 0.8125rem; color: var(--non-conforme); margin-top: calc(var(--pas) * 1); }
/* Champ dont la saisie est devenue obligatoire en cours de route. */
.champ--exige { border-color: var(--non-conforme); background: var(--non-conforme-voile); }

/* Ecart admis sous reserve. Encadre distinct du reste de la carte : ce
   qui s'y decide n'est pas un releve de plus mais une decision, et elle
   engage la conclusion du proces-verbal. */
.reserve {
  margin-top: calc(var(--pas) * 4);
  padding: calc(var(--pas) * 3) calc(var(--pas) * 4);
  border: 1px dashed var(--non-conforme);
  border-radius: 6px;
  background: var(--non-conforme-voile);
}
.reserve__bascule {
  display: flex; align-items: center; gap: calc(var(--pas) * 2);
  font-weight: 600; cursor: pointer;
}
/*
 * Liste à cocher — instruments de mesure d'un contrôle.
 *
 * Un instrument échu reste affiché, grisé et décochable : le masquer
 * laisserait croire qu'il n'existe pas, alors que le vrai message est
 * qu'il doit repartir à l'IRA.
 */
.case-a-cocher {
  display: flex; align-items: baseline; gap: calc(var(--pas) * 2);
  padding: calc(var(--pas) * 1) 0; cursor: pointer;
}
.case-a-cocher input { margin: 0; flex: none; }
.case-a-cocher input:disabled + span { color: var(--encre-tenu); }

.reserve__aide { margin-top: calc(var(--pas) * 1); }
.reserve__champs { margin-top: calc(var(--pas) * 3); }

/* Photographies du constat. Une grille souple : deux ou trois vignettes
   par ligne selon la largeur, sans reglage fixe. */
/* L'unite de la grandeur, posee a cote des cases de releve comme les
   kV et les ms le sont a cote des conditions. */
.avec-unite { display: flex; align-items: center; gap: calc(var(--pas) * 2);
  flex-wrap: wrap; }
.avec-unite input { flex: 1 1 4.5rem; min-width: 0; }
.unite-champ {
  font-family: var(--mono); font-size: 0.75rem; color: var(--encre-doux);
  white-space: nowrap;
}

.photos { margin-top: calc(var(--pas) * 4); }
.photos__tete { display: flex; align-items: baseline; gap: calc(var(--pas) * 2);
  flex-wrap: wrap; }
.photos__liste {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: calc(var(--pas) * 3);
  margin: calc(var(--pas) * 2) 0;
}
.photos__liste:empty { margin: 0; }
.photo { margin: 0; border: 1px solid var(--trait); border-radius: 6px; overflow: hidden; }
/* La vignette montre la photo entiere, pas un recadrage : c'est ainsi
   qu'on verifie d'un coup d'oeil qu'elle est droite et qu'elle cadre le
   defaut. */
.photo img { display: block; width: 100%; height: 9rem; object-fit: contain;
  background: var(--papier-creux); }
.photo figcaption { padding: calc(var(--pas) * 2); display: grid;
  gap: calc(var(--pas) * 1); }
.photo figcaption input { font-size: 0.8125rem; }
.photos__ajout { display: inline-flex; cursor: pointer; }

/* Pieces versees au dossier : bon de reparation, facture. */
.liste-pieces { list-style: none; margin: calc(var(--pas) * 3) 0 0; padding: 0; }
.liste-pieces li {
  display: flex; align-items: baseline; gap: calc(var(--pas) * 2);
  flex-wrap: wrap; padding: calc(var(--pas) * 2) 0;
  border-top: 1px solid var(--trait);
}

/* Une carte qui porte une obligation, non une information. */
/*
 * Un point obligatoire qui n'a pas encore de verdict.
 *
 * Ambre et non rouge : rien n'est faux, il reste seulement à le faire.
 * Le liseré disparaît de lui-même dès que le point porte un verdict, de
 * sorte que la feuille se vide de ses marques à mesure qu'on avance.
 */
.carte--manquant { border-left: 4px solid var(--limite); }
.carte--manquant .carte__tete { background: var(--limite-voile); }

/*
 * La carte qui porte le geste suivant.
 *
 * Sur un écran encore vide, il y a une chose à faire et une seule ; la
 * distinguer évite qu'on la cherche. Bleu et non rouge : ce n'est pas
 * un défaut, c'est une étape.
 */
.carte--action { border-color: var(--acier); border-left: 4px solid var(--acier); }

.carte--alerte { border-color: var(--non-conforme); border-left-width: 4px; }
.carte--alerte .carte__tete { background: var(--non-conforme-voile); }
.carte--alerte .carte__tete h2 { color: var(--non-conforme); }
.lien-danger {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--non-conforme); font-size: 0.8125rem; text-align: left;
  text-decoration: underline;
}

fieldset { border: none; margin: 0 0 calc(var(--pas) * 5); padding: 0; }
legend { padding: 0; font-weight: 600; margin-bottom: calc(var(--pas) * 3); }

.grille { display: grid; gap: calc(var(--pas) * 4); }
.grille--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grille--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* -------------------------------------------------------------- boutons */

.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--pas) * 2);
  min-height: 44px;
  padding: 0 calc(var(--pas) * 4);
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 500;
  border-radius: var(--r);
  border: 1px solid var(--trait-fort);
  background: var(--papier-carte);
  color: var(--encre);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.bouton:hover { background: var(--papier-creux); }

.bouton--principal {
  background: var(--acier);
  border-color: var(--acier);
  color: #fff;
}
.bouton--principal:hover { background: var(--acier-sombre); border-color: var(--acier-sombre); }

.bouton--danger { color: var(--non-conforme); border-color: #e0b3b1; }
.bouton--danger:hover { background: var(--non-conforme-voile); }

.bouton[disabled] { opacity: 0.5; cursor: not-allowed; }

/* -------------------------------------------------------------- messages */

.message {
  border: 1px solid var(--trait);
  border-left: 3px solid var(--trait-fort);
  border-radius: var(--r);
  background: var(--papier-carte);
  padding: calc(var(--pas) * 3) calc(var(--pas) * 4);
  font-size: 0.9375rem;
}
.message--erreur  { border-left-color: var(--non-conforme); background: var(--non-conforme-voile); }
.message--succes  { border-left-color: var(--conforme);     background: var(--conforme-voile); }
.message--info    { border-left-color: var(--acier);        background: var(--acier-voile); }
.message--avertissement { border-left-color: var(--limite, #b7791f); background: var(--limite-voile, #fdf3e1); }

/* ---------------------------------------------------- feuille de saisie */
/* Elle reprend la disposition de la feuille papier : le contrôleur
   recopie ligne par ligne ce qu'il a noté sur place. */

.titre-groupe {
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--encre-doux);
  margin: calc(var(--pas) * 2) 0 calc(var(--pas) * -2);
  padding-bottom: calc(var(--pas) * 1.5);
  border-bottom: 1px solid var(--trait);
}

.exigence { margin: 0 0 calc(var(--pas) * 2); font-size: 0.875rem; }
.exigence__etiquette {
  display: inline-block;
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--encre-doux);
  margin-right: calc(var(--pas) * 2);
}

/* La consigne opératoire dit comment mesurer. Elle vient du référentiel
   et s'imprime déjà sur la feuille de terrain. */
.consigne {
  margin: 0 0 calc(var(--pas) * 4);
  padding: calc(var(--pas) * 2.5) calc(var(--pas) * 3);
  background: var(--papier-creux);
  border-left: 3px solid var(--trait-fort);
  border-radius: 0 var(--r) var(--r) 0;
  font-size: 0.875rem;
  color: var(--encre-doux);
  max-width: var(--largeur-texte);
}

table.saisie { width: 100%; border-collapse: collapse; }
table.saisie th {
  font-size: 0.6875rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--encre-doux);
  text-align: left;
  font-weight: 600;
  padding: 0 calc(var(--pas) * 2) calc(var(--pas) * 2) 0;
  border-bottom: 1px solid var(--trait);
  vertical-align: bottom;
  /* Ces intitulés-là sont des phrases — « Relevés en kV — mesures 1, 2,
     3… », « Valeur retenue / écart ». Les tenir sur une seule ligne,
     comme les en-têtes ordinaires, les faisait sortir de leur cellule
     et se superposer au voisin dès que la fenêtre se réduisait. */
  white-space: normal;
}
table.saisie td {
  padding: calc(var(--pas) * 2) calc(var(--pas) * 2) calc(var(--pas) * 2) 0;
  border-bottom: 1px solid var(--papier-creux);
  vertical-align: top;
}
table.saisie tr:last-child td { border-bottom: 0; }

/*
 * Rappel que ces deux colonnes ne se saisissent pas.
 *
 * LA NOTE NE DOIT PAS FIXER LA LARGEUR DE LA COLONNE. Sans plafond,
 * « par l'appareil — reprise des conditions si vide » réclamait 318 px
 * pour une case qui en demande 120, et la table dépassait son cadre de
 * quelques pixels. Le navigateur prend alors la place sur la colonne la
 * plus étroite — celle du verdict — qui tombait de 128 px à 60 px : la
 * pastille « CONFORME » débordait de sa cellule et se retrouvait coupée
 * au bord de la carte. C'est le défaut qu'Edouard a photographié.
 */
.th-note {
  display: block;
  max-width: 12rem;
  /* Un intitulé de colonne tient sur une ligne : la règle générale des
     en-têtes interdit le retour à la ligne. La note, elle, est une
     phrase — sans cette exception elle sortait de sa cellule et se
     superposait à l'intitulé voisin, et le plafond ne servait à rien. */
  white-space: normal;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--encre-tenu);
  font-size: 0.6875rem;
}

/*
 * Une unité ne se met jamais en capitales. Les intitulés de colonne sont
 * en petites capitales par le style, et « µGy » y devenait « ΜGY » : un
 * mu grec majuscule suivi d'un gray majuscule, qui ne désigne plus rien.
 * Le symbole garde sa casse, quel que soit l'entourage.
 */
.unite-titre { text-transform: none; letter-spacing: 0; }

.s-reglage { min-width: 13rem; }
.s-consigne { width: 7.5rem; }
.s-releves { min-width: 16rem; }
.s-releves input { width: 4.75rem; display: inline-block; margin: 0; }
/*
 * Cases de relevé : trois par ligne, toutes de la même largeur.
 *
 * Elles se rangeaient selon la place disponible. Les cinq
 * déclenchements de la reproductibilité tombaient donc en quatre plus
 * une, et la cinquième avait l'air d'un ajout accidentel plutôt que du
 * dernier terme d'une série. Trois colonnes fixes donnent trois puis
 * deux, et alignent les chiffres d'une ligne sur l'autre.
 */
.grille-releves {
  display: grid;
  grid-template-columns: repeat(3, 4.75rem);
  gap: var(--pas);
  justify-content: start;
}
/* La grille ne s'étire pas : sans quoi elle occupe toute la cellule et
   l'unité, poussée à la ligne, se retrouve sous la première case au lieu
   de suivre la série. */
.s-releves .avec-unite { align-items: center; flex-wrap: nowrap; }
.s-releves .grille-releves { flex: none; }
.s-releves .grille-releves input { flex: none; width: 100%; }
/* La valeur affichée est un nombre court — 60, 80, 125. Laisser son
   champ prendre toute la place que la grille des relevés a libérée le
   faisait passer pour une zone de texte. */
.s-consigne .avec-unite input { flex: 0 0 5.5rem; }
/*
 * Ces deux colonnes portent un contenu de largeur connue : un nombre
 * suivi de son unité, et une pastille de verdict. Elles reçoivent donc
 * un plancher, sans quoi le navigateur leur prend la place que les
 * colonnes de saisie réclament — et une pastille comprimée déborde de
 * sa cellule au lieu de se replier.
 */
.s-calcul { width: 11rem; min-width: 9rem; font-size: 0.8125rem; }
.s-verdict { width: 8rem; min-width: 6.5rem; }
.s-verdict .verdict, .s-verdict .etat { white-space: nowrap; }

/*
 * LE VERDICT RESTE VISIBLE, MÊME QUAND LA TABLE DÉFILE.
 *
 * En dessous de ~1180 px la feuille de saisie ne tient plus dans sa
 * carte et défile latéralement. La colonne du verdict est la dernière :
 * c'est donc elle qui disparaît en premier, alors que c'est la seule
 * qu'on cherche du regard en remontant la feuille. Elle est ancrée à
 * droite du cadre.
 *
 * Seulement tant que la table EST une table : en dessous de 720 px elle
 * se déplie en blocs empilés, où « ancré à droite » n'a plus de sens.
 */
@media (min-width: 721px) {
  table.saisie th.s-verdict,
  table.saisie td.s-verdict {
    position: sticky;
    right: 0;
    background: var(--papier-carte);
    box-shadow: -8px 0 8px -8px rgba(22, 32, 42, 0.18);
  }
  table.saisie th.s-verdict { background: var(--papier-carte); }
}

.s-calcul .valeur { font-family: var(--mono); font-variant-numeric: tabular-nums;
                    font-weight: 600; }
.s-calcul .plage { display: block; color: var(--encre-tenu); font-size: 0.75rem; }

/* Conditions d'exposition : elles décrivent le réglage, pas la mesure. */
.conditions { display: flex; gap: calc(var(--pas) * 2); margin-top: calc(var(--pas) * 1.5); }
.conditions label { display: inline-flex; align-items: center; gap: calc(var(--pas));
                    font-size: 0.75rem; color: var(--encre-doux); }
.conditions input { width: 4.25rem; }
.programme { margin-top: calc(var(--pas) * 1.5); font-size: 0.8125rem; }

table.saisie tr.est-conforme     .s-reglage { box-shadow: inset 3px 0 0 var(--conforme); }
table.saisie tr.est-non-conforme .s-reglage { box-shadow: inset 3px 0 0 var(--non-conforme); }
table.saisie tr.est-conforme     .s-reglage,
table.saisie tr.est-non-conforme .s-reglage { padding-left: calc(var(--pas) * 3); }

/*
 * GRILLE DES RÉCEPTEURS D'IMAGE.
 *
 * Dix-huit plaques, quatre constats chacune. La colonne du repère porte
 * le liseré de verdict, comme la colonne du réglage sur la feuille de
 * mesures : d'un coup d'œil sur la marge gauche on voit où la grille
 * est verte et où elle ne l'est pas, sans lire une seule valeur.
 */
.r-repere { min-width: 10rem; }
.r-repere .discret { display: block; font-size: 0.75rem; margin-top: 2px; }
.r-repere select { margin-bottom: var(--pas); }
table.saisie tr.est-conforme     .r-repere { box-shadow: inset 3px 0 0 var(--conforme); }
table.saisie tr.est-non-conforme .r-repere { box-shadow: inset 3px 0 0 var(--non-conforme); }
table.saisie tr.est-ecartee      .r-repere { box-shadow: inset 3px 0 0 var(--encre-tenu); }
table.saisie tr.est-conforme     .r-repere,
table.saisie tr.est-non-conforme .r-repere,
table.saisie tr.est-ecartee      .r-repere { padding-left: calc(var(--pas) * 3); }

/* Un récepteur écarté n'est plus en service : la ligne s'estompe, mais
   elle reste lisible — le procès-verbal doit la nommer. */
table.saisie tr.est-ecartee { opacity: 0.62; }
table.saisie tr.est-ecartee .r-repere strong { text-decoration: line-through; }

/* La résolution attendue, à côté de la case où on la relève. */
table.saisie td .discret { font-size: 0.75rem; }
.r-resolution { width: 11rem; }
.r-resolution .avec-unite { flex-wrap: nowrap; gap: var(--pas); }
.r-resolution input { flex: 0 0 4rem; }
.r-resolution .discret { white-space: nowrap; }

/*
 * « Toutes examinées, conformes », au-dessus de la grille.
 *
 * Ce n'est pas un pré-cochage : le bouton n'agit que sur les cases
 * encore vides, et l'acte est daté par l'enregistrement.
 *
 * Il se tenait en tête de colonne. Sur un téléphone, la table se déplie
 * en blocs et l'en-tête disparaît : le geste qui fait gagner le plus de
 * temps s'évanouissait précisément là où cocher dix-huit plaques une à
 * une fait le plus mal. Il vit donc au-dessus de la grille.
 */
.barre-constats {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: calc(var(--pas) * 2);
  margin: calc(var(--pas) * 3) 0 0;
}
.bouton--court {
  padding: calc(var(--pas) * 1.5) calc(var(--pas) * 2.5);
  font-size: 0.8125rem;
}

/* Bouton de service au fil d'un tableau : « retirer » sur une fiche de
   récepteur. Il s'écrit en lien pour ne pas peser autant qu'une action
   principale. */
.lien-bouton {
  display: block;
  margin-top: 2px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  font-size: 0.6875rem;
  color: var(--acier);
  cursor: pointer;
}
.lien-bouton:hover { text-decoration: underline; }

@media (max-width: 720px) {
  .defilable table.saisie { min-width: 52rem; }
}

/*
 * CE QU'ID-DENTAL DOIT, ET CE QU'ELLE FAIT SUR DEMANDE.
 *
 * L'ordonnance rend les quatre contrôles obligatoires, mais ne les
 * confie pas tous à la même personne. Le test de réception incombe au
 * fournisseur, le contrôle d'état à l'entreprise spécialisée : ce sont
 * les deux qu'ID-Dental doit assurer d'elle-même. Le contrôle de
 * stabilité et le protocole d'établissement reviennent légalement à
 * l'exploitant ; ID-Dental les exécute comme tiers mandaté, mais
 * seulement si le client les commande.
 *
 * Confondre les deux fait manquer une obligation, ou facturer une
 * prestation qu'on n'avait pas à rendre. D'où deux traitements bien
 * distincts à l'œil : le dû s'affiche plein et coloré, la prestation
 * sur commande en italique et en gris — on la lit sans la prendre pour
 * une échéance qui court.
 */
.charge {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  border-left: 2px solid var(--trait-fort);
  padding-left: calc(var(--pas) * 2);
  color: var(--encre-doux);
}
.charge--fournisseur { border-color: var(--acier); color: var(--acier); font-weight: 600; }
.charge--specialiste { border-color: var(--conforme); color: var(--conforme); font-weight: 600; }
.charge--demande {
  border-color: var(--trait-fort);
  color: var(--encre-tenu);
  font-style: italic;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}

/* Le titre et la ligne entière suivent, pour que le survol de la liste
   suffise à trier ce qui est dû de ce qui se commande. */
.titre--demande { font-style: italic; color: var(--encre-doux); font-weight: 500; }
.ligne--demande td { font-style: italic; color: var(--encre-doux); }
.ligne--demande td a { font-style: italic; }

/*
 * Aide contextuelle sous un menu : ce que le choix engage.
 *
 * Discrète — c'est un rappel, pas un avertissement — mais nommée assez
 * pour qu'on la lise sans la chercher.
 */
.aide-contexte {
  margin-top: calc(var(--pas) * 2);
  padding: calc(var(--pas) * 3);
  background: var(--papier-creux);
  border-left: 2px solid var(--acier);
  border-radius: 0 var(--r) var(--r) 0;
  font-size: 0.8125rem;
}
.aide-contexte__resume { margin: 0 0 calc(var(--pas) * 2); font-weight: 600; }
.aide-contexte__detail {
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr);
  gap: calc(var(--pas)) calc(var(--pas) * 3);
  margin: 0;
}
.aide-contexte__detail dt {
  font-family: var(--mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--encre-doux);
}
.aide-contexte__detail dd { margin: 0; color: var(--encre-doux); }
.aide-contexte .aide { margin-bottom: 0; }

@media (max-width: 720px) {
  .aide-contexte__detail { grid-template-columns: minmax(0, 1fr); }
  .aide-contexte__detail dd { margin-bottom: calc(var(--pas) * 2); }
}

/* Une opération que la machine ne porte pas : la ligne s'efface sans
   disparaître, pour qu'on voie qu'elle a été examinée et écartée. */
.operation--absente td { opacity: 0.55; }

/* Cases à cocher alignées, pour un choix multiple court. */
.cases { display: flex; flex-wrap: wrap; gap: calc(var(--pas) * 2) calc(var(--pas) * 5); }
.case { display: inline-flex; align-items: center; gap: calc(var(--pas) * 2);
        font-size: 0.9375rem; min-height: 32px; }
.case input { width: auto; margin: 0; }

/* Un écran vide est une invitation à agir, pas un constat. */
.vide {
  padding: calc(var(--pas) * 12) calc(var(--pas) * 4);
  text-align: center;
  color: var(--encre-doux);
}
.vide p { margin-left: auto; margin-right: auto; }

/* ------------------------------------------------------ fil des étapes */
/* Saisie guidée du parc. Le fil dit où l'on en est ; les étapes ne sont
   pas cliquables, un cabinet déjà enregistré ne se recrée pas. */

.etapes {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--pas) * 2) calc(var(--pas) * 6);
  list-style: none;
  margin: 0 0 calc(var(--pas) * 5);
  padding: 0;
  font-size: 0.875rem;
  color: var(--encre-tenu);
}

.etapes__pas { display: flex; align-items: center; gap: calc(var(--pas) * 2); }

.etapes__rang {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1;
}

.etapes__pas--fait { color: var(--encre-doux); }
.etapes__pas--fait .etapes__rang {
  background: var(--encre-doux);
  border-color: var(--encre-doux);
  color: var(--papier-carte);
}

.etapes__pas--courant { color: var(--acier); font-weight: 600; }
.etapes__pas--courant .etapes__rang {
  background: var(--acier);
  border-color: var(--acier);
  color: #fff;
}

/* ---------------------------------------------------------- authentification */

/*
 * Le socle réglementaire sous la carte de connexion.
 *
 * Discret : c'est une mention de conformité, pas un argument de vente.
 * Mais elle est là, avec les numéros de révision — un utilisateur qui
 * se connecte voit sur quoi repose ce qu'il va signer.
 */
.socle-legal {
  max-width: 32rem;
  margin: calc(var(--pas) * 7) auto 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--encre-doux);
}
.socle-legal p { margin: 0 0 calc(var(--pas) * 2); max-width: none; }
.socle-legal ul { margin: 0; padding-left: calc(var(--pas) * 4); }
.socle-legal li { margin-bottom: calc(var(--pas)); }
.socle-legal__note {
  margin-top: calc(var(--pas) * 3);
  padding-top: calc(var(--pas) * 2);
  border-top: 1px solid var(--trait);
  color: var(--encre-tenu);
}

/* La marque au-dessus de la carte de connexion : c'est la première
   chose qu'on voit du logiciel, et elle dit chez qui l'on entre. */
.marque-connexion {
  display: block;
  width: 220px;
  max-width: 70%;
  height: auto;
  margin: 0 auto calc(var(--pas) * 6);
}

.ecran-connexion {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: calc(var(--pas) * 5);
}
.ecran-connexion .carte { width: 100%; max-width: 25rem; }

.code-totp {
  font-family: var(--mono);
  font-size: 1.5rem;
  letter-spacing: 0.5em;
  text-align: center;
  padding-left: calc(var(--pas) * 4);
}

/* ---------------------------------------------------------------- pied */

.pied {
  border-top: 1px solid var(--trait);
  margin-top: calc(var(--pas) * 12);
  padding: calc(var(--pas) * 5);
  font-size: 0.8125rem;
  color: var(--encre-doux);
}
.pied__interieur {
  max-width: var(--largeur-page);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--pas) * 4);
}
.pied__interieur > :last-child { margin-left: auto; font-family: var(--mono); font-size: 0.75rem; }

/* -------------------------------------------------------------- accès */

.saut-navigation {
  position: absolute;
  left: -9999px;
  background: var(--encre);
  color: #fff;
  padding: calc(var(--pas) * 3);
  z-index: 10;
}
.saut-navigation:focus { left: calc(var(--pas) * 2); top: calc(var(--pas) * 2); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---------------------------------------------------------- adaptatif */

/*
 * Une table plus large que sa carte défile DANS la carte.
 *
 * Cette règle ne valait qu'en dessous de 720 px. Au-delà, une table trop
 * large ne rencontrait aucune limite : elle poussait la carte hors de la
 * page, la colonne « Verdict » sortait du cadre, le bord droit de la
 * carte disparaissait et c'est la PAGE ENTIÈRE qui défilait
 * latéralement. La feuille de saisie a un plancher d'environ 1100 px —
 * sur un écran de 1280 px, fenêtre à peine réduite, le cadre était déjà
 * crevé. Le débordement n'était donc pas un cas limite de petit écran :
 * il se produisait sur un poste de bureau ordinaire.
 *
 * La déclaration min-width: 0 sur la boîte est indispensable : une piste
 * de grille ou une boîte flexible refuse par défaut de devenir plus
 * étroite que son contenu, et le défilement ne s'applique alors jamais.
 */
.defilable {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  min-width: 0;
  max-width: 100%;
}

@media (max-width: 720px) {
  .bandeau__interieur, .fil__interieur, .page { padding-left: calc(var(--pas) * 4); padding-right: calc(var(--pas) * 4); }
  .grille--2, .grille--3 { grid-template-columns: minmax(0, 1fr); }
  .page__actions .bouton { flex: 1 1 auto; }
  .defilable table { min-width: 40rem; }

  .bandeau__interieur {
    padding-top: calc(var(--pas) * 2);
    padding-bottom: calc(var(--pas) * 2);
  }
  .bandeau__liens { margin-left: 0; width: 100%; }
}

/*
 * LA FEUILLE DE SAISIE AU TÉLÉPHONE
 *
 * Faire défiler latéralement une table de 1100 px sur un écran de 390 px
 * n'est pas une saisie : on perd l'intitulé de la colonne dès qu'on
 * atteint la case, et on tape à l'aveugle. Or c'est justement au
 * téléphone, debout devant l'appareil, que le relevé se fait.
 *
 * En dessous de 720 px la table cesse donc d'être une table : chaque
 * réglage devient un bloc, chaque cellule une ligne intitulée par son
 * en-tête de colonne — repris de l'attribut data-intitule, pour que le
 * mot lu au cabinet soit celui lu au bureau.
 *
 * Les cases de relevé passent de trois à deux par ligne : trois cases de
 * 4,75 rem ne tiennent pas dans 390 px moins les marges.
 */
@media (max-width: 720px) {
  .defilable table.saisie { min-width: 0; }
  table.saisie, table.saisie tbody, table.saisie tr, table.saisie td { display: block; }
  table.saisie thead { display: none; }

  table.saisie tr {
    border: 1px solid var(--trait);
    border-radius: var(--r);
    padding: calc(var(--pas) * 2);
    margin-bottom: calc(var(--pas) * 3);
  }
  table.saisie tr:last-child { margin-bottom: 0; }

  table.saisie td {
    border-bottom: 0;
    padding: calc(var(--pas) * 2) 0;
    width: auto;
    min-width: 0;
  }
  table.saisie td + td { border-top: 1px solid var(--papier-creux); }

  /*
   * L'intitulé de colonne, rendu à chaque cellule puisque l'en-tête a
   * disparu.
   *
   * Il ne porte JAMAIS d'unité. Un pseudo-élément ne se compose pas :
   * la mise en capitales s'applique à toute la chaîne, et « Relevés en
   * µGy » y deviendrait « RELEVÉS EN ΜGY » — le mu grec majuscule et le
   * gray majuscule, la faute même que la classe .unite-titre corrige
   * dans l'en-tête. L'unité reste donc à côté de la case, où elle est
   * de toute façon plus près de ce qu'on tape.
   */
  table.saisie td[data-intitule]::before {
    content: attr(data-intitule);
    display: block;
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--encre-doux);
    margin-bottom: calc(var(--pas) * 1.5);
  }

  /*
   * Deux cases par ligne, chacune prenant la moitié de la place.
   *
   * La grille était dimensionnée sur son contenu — flex: none dans une
   * boîte flexible vaut « largeur max-content ». Un champ de saisie sans
   * largeur imposée fait environ 180 px de max-content : deux colonnes
   * en réclamaient 476 sur un écran de 390, et la page partait en
   * biais. Il faut donc rendre la grille compressible (flex: 1 1 0) et
   * lever le plancher implicite (min-width: 0) pour que les fractions
   * se calculent sur la place réelle.
   */
  .grille-releves { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .s-releves .avec-unite { flex-wrap: wrap; }
  .s-releves .grille-releves { flex: 1 1 0; min-width: 0; }
  .s-releves input, .s-releves .grille-releves input { width: 100%; min-width: 0; }
  .s-consigne .avec-unite input { flex: 1 1 auto; }
  .conditions { flex-wrap: wrap; }
  .conditions input { width: 4rem; }

  /* Le liseré de verdict passe du bord gauche du réglage au bord gauche
     du bloc entier : dans une pile, c'est le bloc qui porte l'état. */
  table.saisie tr.est-conforme     { border-left: 3px solid var(--conforme); }
  table.saisie tr.est-non-conforme { border-left: 3px solid var(--non-conforme); }
  table.saisie tr.est-conforme     .s-reglage,
  table.saisie tr.est-non-conforme .s-reglage { box-shadow: none; padding-left: 0; }
}

/*
 * Une cible tactile se rate en dessous de 44 px. Les cases de relevé
 * sont petites par nécessité — il y en a cinq côte à côte — mais elles
 * ne doivent pas être BASSES pour autant.
 */
@media (pointer: coarse) {
  input, select, textarea, .bouton, button { min-height: 44px; }
  /* iOS agrandit la page dès qu'un champ descend sous 16 px : la saisie
     se fait alors sur une page zoomée qu'il faut repousser à la main. */
  input, select, textarea { font-size: 16px; }
}

/*
 * En-tête d'entreprise réservé au papier.
 *
 * Masqué à l'écran, où le bandeau porte déjà la marque. À l'impression
 * le bandeau disparaît, et une note réglementaire sans en-tête ne dit
 * plus d'où elle vient — ce qui lui retire l'essentiel de sa valeur
 * quand elle circule dans un cabinet.
 */
.entete-impression { display: none; }


/* ---------------------------------------------------- rappels et alertes */
/* Le sommaire se lit d'un coup d'oeil a la connexion : cinq compteurs,
   gris quand il n'y a rien, ambre quand une action attend, rouge quand un
   delai est depasse. La couleur garde son sens : elle ne decore pas. */

.rappels__sommaire {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: calc(var(--pas) * 2);
}
.rappels__tuile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--trait);
  border-left: 4px solid var(--trait-fort);
  border-radius: var(--r);
  background: var(--papier-carte);
  color: var(--encre);
  text-decoration: none;
}
a.rappels__tuile:hover { border-color: var(--acier); }
.rappels__nombre {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.1;
}
.rappels__libelle { font-size: 0.8125rem; color: var(--encre-doux); }
.rappels__tuile--nul { color: var(--encre-tenu); }
.rappels__tuile--nul .rappels__nombre { color: var(--encre-tenu); }
.rappels__tuile--attente { border-left-color: var(--limite); background: var(--limite-voile); }
.rappels__tuile--attente .rappels__nombre { color: var(--limite); }
.rappels__tuile--urgent { border-left-color: var(--non-conforme); background: var(--non-conforme-voile); }
.rappels__tuile--urgent .rappels__nombre { color: var(--non-conforme); }

.rappels__explication p { max-width: var(--largeur-texte); }
.rappels__explication { padding-bottom: 0; }
[data-rubrique] { scroll-margin-top: 16px; }
[data-rubrique] td .bouton { white-space: nowrap; }
[data-rubrique] td.donnee .verdict { margin-top: 4px; white-space: nowrap; }

.rappels__repli > summary { cursor: pointer; list-style: none; }
.rappels__repli > summary::-webkit-details-marker { display: none; }
.rappels__repli > summary h2::before { content: "\25B8\00A0"; color: var(--encre-tenu); }
.rappels__repli[open] > summary h2::before { content: "\25BE\00A0"; }

@media (max-width: 720px) {
  .rappels__sommaire { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* ------------------------------------------- bas de page des longues pages */
/* Fleche de retour en haut : invisible tant qu'on n'a pas defile, puis
   fixee en bas a droite, au-dessus du contenu, cible tactile de 48 px. */
.haut-de-page {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 50;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--acier);
  color: #fff;
  font-size: 1.375rem;
  line-height: 1;
  text-decoration: none;
  box-shadow: 0 2px 8px rgba(22, 32, 42, 0.25);
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity 0.2s, transform 0.2s, visibility 0.2s;
}
.haut-de-page--visible { opacity: 1; visibility: visible; transform: none; }
/* Le pied de page laisse la place de la fleche a droite. */
.pied__interieur { padding-right: 64px; }
.haut-de-page:hover, .haut-de-page:focus-visible { background: var(--acier-sombre); color: #fff; }

/* Boutons de la feuille de saisie repris en bas : alignes a droite comme
   en tete, avec la place de la fleche pour qu'elle ne les recouvre pas. */
.barre-bas {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: calc(var(--pas) * 2);
  margin-top: calc(var(--pas) * 5);
  padding: calc(var(--pas) * 4) 0;
  padding-right: 64px;
  border-top: 1px solid var(--trait);
}
@media (max-width: 720px) {
  .barre-bas { justify-content: stretch; padding-right: 0; padding-bottom: 72px; }
  .barre-bas .bouton { flex: 1 1 100%; }
  .haut-de-page { right: 14px; bottom: 14px; }
}


/* ------------------------------------- réserves sur la fiche d'un appareil */
/* 053 — aperçu d'un courriel avant confirmation. */
.apercu-courriel { white-space: pre-wrap; font-family: inherit; font-size: 0.9rem; line-height: 1.5;
  background: var(--papier-creux, #f4f4f2); border: 1px solid var(--trait); padding: 14px 16px; margin: 6px 0 0;
  max-height: 28rem; overflow: auto; }
/* 052 — renvoi à la directive écrit en clair, sous l'exigence. */
.renvoi { margin-top: 6px; padding-top: 5px; border-top: 1px dotted var(--trait); font-size: 0.78rem; line-height: 1.35; color: var(--encre-douce, #5b6570); font-style: normal; }
.reserves-fiche { list-style: none; margin: 0; padding: 0; }
.reserves-fiche > li {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: calc(var(--pas) * 2) calc(var(--pas) * 4);
  padding: calc(var(--pas) * 3) calc(var(--pas) * 4);
  border-bottom: 1px solid var(--trait);
}
.reserves-fiche > li:last-child { border-bottom: 0; }
.reserves-fiche__point { flex: 1 1 26rem; min-width: 0; }
.reserves-fiche__libelle { font-size: 0.8125rem; color: var(--encre-doux); }
.reserves-fiche__constat { font-weight: 600; margin-top: 2px; overflow-wrap: anywhere; }
.reserves-fiche__meta { display: flex; flex-wrap: wrap; gap: 4px calc(var(--pas) * 4);
  margin-top: 6px; font-size: 0.875rem; color: var(--encre-doux); }
.reserves-fiche__action { flex: 0 0 auto; }
@media (max-width: 720px) { .reserves-fiche__action { flex-basis: 100%; } }

/* ------------------------------------------------- journal des relances */
.journal { list-style: none; margin: 0; padding: 0; }
.journal > li {
  display: grid;
  grid-template-columns: 9.5rem 1fr;
  gap: calc(var(--pas) * 3);
  padding: calc(var(--pas) * 3) 0;
  border-bottom: 1px solid var(--trait);
}
.journal > li:last-child { border-bottom: 0; }
.journal__date { color: var(--encre-doux); font-size: 0.8125rem; }
.journal__tete { margin-bottom: 4px; }
.journal__texte summary { cursor: pointer; color: var(--acier); }
.journal__texte pre {
  white-space: pre-wrap;
  font-family: var(--sans);
  font-size: 0.875rem;
  background: var(--papier-creux);
  border-left: 3px solid var(--trait-fort);
  padding: 10px 12px;
  margin: 8px 0;
  max-width: var(--largeur-texte);
}
.actions-reserve form { min-width: 18rem; margin-top: 8px; }
@media (max-width: 720px) {
  .journal > li { grid-template-columns: 1fr; gap: 2px; }
}
@media print {
  .carte--action, .actions-reserve, [data-role="archivage"] { display: none !important; }
}


/* --------------------------------------------- recherche des controles */
.recherche { padding: calc(var(--pas) * 3); }
.recherche__ligne { display: flex; flex-wrap: wrap; gap: calc(var(--pas) * 2); align-items: center; }
.recherche__champ { flex: 1 1 22rem; min-width: 0; }
.recherche__ligne select { flex: 0 1 auto; width: auto; }
.recherche__vues { display: flex; flex-wrap: wrap; gap: calc(var(--pas) * 4); align-items: center;
  margin-top: calc(var(--pas) * 3); }
.recherche__vues .discret { margin-left: auto; }
.vue { text-decoration: none; color: var(--encre-doux); padding-bottom: 2px;
  border-bottom: 2px solid transparent; }
.vue--active { color: var(--encre); border-bottom-color: var(--acier); font-weight: 600; }
.groupes > * + * { margin-top: calc(var(--pas) * 2); }
.groupe > summary { list-style: none; cursor: pointer; }
.groupe > summary::-webkit-details-marker { display: none; }
.groupe__tete { display: flex; flex-wrap: wrap; gap: calc(var(--pas) * 2); align-items: center;
  justify-content: space-between; padding: calc(var(--pas) * 3) calc(var(--pas) * 4); min-height: 44px; }
.groupe__nom::before { content: "\25B8\00A0"; color: var(--encre-tenu); }
.groupe[open] .groupe__nom::before { content: "\25BE\00A0"; }
.groupe[open] > summary { border-bottom: 1px solid var(--trait); }
.groupe__infos { display: flex; flex-wrap: wrap; gap: calc(var(--pas) * 2); align-items: center; }
.groupe__pied { padding: calc(var(--pas) * 2) calc(var(--pas) * 4); }
.pagination { display: flex; gap: calc(var(--pas) * 3); align-items: center; justify-content: center;
  margin-top: calc(var(--pas) * 4); }
@media (max-width: 720px) {
  .recherche__ligne select, .recherche__ligne .bouton { flex: 1 1 100%; }
  .recherche__vues .discret { margin-left: 0; flex-basis: 100%; }
}

/* ------------------------------------------------------------ impression */
/* Les procès-verbaux définitifs sortent en PDF via Playwright, mais une page
   consultée à l'écran doit rester imprimable proprement en cabinet. */

@media print {
  :root { --papier: #fff; --papier-carte: #fff; }
  .bandeau, .fil, .pied, .page__actions, .bouton, .rappels__sommaire, .haut-de-page, .barre-bas { display: none !important; }
  .page { max-width: none; padding: 0; }
  .carte { border-color: #000; break-inside: avoid; }

  .entete-impression {
    display: flex;
    align-items: center;
    gap: 12px;
    border-bottom: 2px solid #000;
    padding-bottom: 8px;
    margin-bottom: 14px;
    font-size: 10pt;
  }
  .entete-impression img { height: 46px; width: auto; }

  /* Une fiche par contrôle, sans coupure au milieu d'un tableau. */
  .carte { margin-bottom: 10px; }
  .aide-contexte, .message { break-inside: avoid; }
  /* Le défilement horizontal n'existe pas sur du papier : la table doit
     tenir dans la largeur plutôt que d'être coupée au bord de la page. */
  .defilable { overflow: visible !important; }
  .defilable table { min-width: 0 !important; }
  tbody tr { break-inside: avoid; }
  .verdict { border-color: #000; }
  a[href]::after { content: ""; }
}

/* 061 — offres : sur téléphone, les prestations s'empilent et le prix garde désignation et montant. */
@media (max-width: 640px) {
  .table-empilee th, .table-empilee td { display: block; width: auto !important; }
  .table-empilee th { border-bottom: 0; padding-bottom: 0; white-space: normal; }
  [data-role="prix"] th, [data-role="prix"] td { white-space: normal; }
  .defilable table.table-empilee, .defilable table[data-role="prix"] { min-width: 0; }
  .col-detail { display: none; }
}

/* 063 — Console de calcul. */
.case-option { display: flex; gap: 8px; align-items: flex-start; margin: 10px 0; }
.case-option input { margin-top: 4px; }
.colonne-choisie { background: #eef4fa; }
th.colonne-choisie .lien-bouton, th.colonne-choisie { font-weight: 700; }
[data-role="comparaison"] td.donnee, [data-role="comparaison"] th.donnee { text-align: right; }
[data-role="quantites"] th[scope="row"], [data-role="grille-categories"] th[scope="row"], [data-role="grille-options"] th[scope="row"] {
  text-transform: none; letter-spacing: normal; font-family: inherit; font-size: 0.95rem; font-weight: 600; color: var(--encre, #1d2327); white-space: normal; }
[data-role="quantites"] th .aide, [data-role="grille-categories"] th .aide { text-transform: none; letter-spacing: normal; font-family: inherit; font-weight: 400; }

/* 064 — Colonnes de nombres : en-tête, champ et valeur alignés à droite,
   sur le même bord ; filets des en-têtes de ligne comme ceux des cellules. */
th.col-nombre, td.col-nombre { text-align: right; }
td.col-nombre input { display: block; margin-left: auto; text-align: right; }
[data-role="quantites"] td, [data-role="grille-categories"] td, [data-role="grille-options"] td { vertical-align: middle; }
tbody th[scope="row"] { border-bottom-color: var(--trait); }
tbody tr:last-child th[scope="row"] { border-bottom: none; }
.pied a { color: inherit; text-decoration: underline; text-decoration-color: rgba(0,0,0,.25); }

/* 068 — Paragraphes de texte justifiés, avec césure française. */
main p, .carte__corps li { text-align: justify; -webkit-hyphens: auto; hyphens: auto; }
main .message, main .actions p, main td p, main p.discret, .pied p, .page__tete p { text-align: left; -webkit-hyphens: manual; hyphens: manual; }
