/*
 * Le portail du donneur d'ordre — la mise en page.
 *
 * Les couleurs viennent de jetons.css, partagé avec le pilotage et
 * l'application agent. Ce fichier-ci ne porte que la forme, et la forme
 * n'appartient qu'au portail : les trois surfaces n'ont aucune raison de se
 * ressembler. Un agent badge en marchant, un exploitant pilote à la souris,
 * un donneur d'ordre relit des preuves — et c'est ce dernier usage qui
 * gouverne ici.
 *
 * D'où le parti pris : le portail a l'allure d'un registre, pas d'un
 * tableau de bord. Des filets plutôt que des cartes flottantes, une seule
 * colonne de lecture, des chiffres en labeur sérif, un cartouche daté au
 * pied de chaque écran. Le CCTP en fait un critère de notation
 * (« capacité de contrôle en temps réel », « fiabilité des outils
 * numériques ») et le CCAP en fait une pièce : un constat, un taux de
 * réalisation, un bordereau d'export finissent sur un bureau, parfois
 * juridique. L'écran doit avoir la tenue de ce qu'il prouve.
 *
 * Il reste lisible sur le téléphone d'un gardien devant une cage comme sur
 * l'écran d'un responsable qualité — d'où le dimensionnement unique et les
 * cibles jamais sous 44 pixels.
 *
 * Le fichier est servi tel quel depuis public/, comme jetons.css : aucune
 * étape de construction à la racine du dépôt, et il se lit en production.
 */

/* --- Le labeur ------------------------------------------------------
 * Source Serif 4 (SIL OFL 1.1), déposée dans public/fonts plutôt
 * qu'appelée chez Google : un portail contractuel n'a pas à faire
 * dépendre son allure d'un tiers, ni à annoncer ses visiteurs à un
 * service de mesure d'audience.
 *
 * C'est une police variable à deux axes — la graisse et la taille
 * optique. Le second fait le travail qu'un labeur fait sur le papier :
 * un « 0 sur 7 » de 40 pixels reçoit des pleins et des déliés plus
 * contrastés qu'un titre de section, sans second fichier à charger.
 *
 * Les deux découpes valent 220 Ko ensemble, mais l'unicode-range fait
 * que la latine seule (120 Ko) part dans la quasi-totalité des cas —
 * l'étendue ne suit que pour un nom polonais ou tchèque, qui existent en
 * Moselle-Est. « swap » assure que le texte se lit avant elle.
 */
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/fonts/source-serif-4-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/fonts/source-serif-4-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Le repli compte autant que le premier nom : tant que la police n'est
   * pas là — première visite, réseau lent devant un immeuble — c'est
   * Georgia qui tient les titres, et elle a la même chasse généreuse. */
  --labeur: 'Source Serif 4', Georgia, 'Times New Roman', serif;
  --courante: system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* La colonne de lecture. 1040 px tient un tableau de six colonnes sans
   * étirer une ligne de texte au-delà de ce que l'œil suit. */
  --colonne: 1040px;
  --gouttiere: 24px;
}

* { box-sizing: border-box }

/* Le papier, pas le bureau : le portail pose son contenu sur la couleur
 * de carte, et non sur le gris de fond des deux autres surfaces. Sans
 * cartes qui flottent, un fond gris n'aurait plus rien à porter — il ne
 * resterait qu'un écran gris. Le gris sert désormais de creux : les
 * champs, les lignes en attente, les aplats d'appoint. */
html, body {
  margin: 0;
  background: var(--carte);
  color: var(--texte);
  font: 400 16px/1.55 var(--courante);
  -webkit-text-size-adjust: 100%;
}

/* --- L'entête -------------------------------------------------------
 * Elle porte deux identités : celle de l'exploitant, en pastille, et
 * celle du client, en toutes lettres. Elles se lisent dans cet ordre —
 * qui fournit, pour qui. La classe s'appelait « marque », le nom du
 * partiel partagé qu'elle n'incluait pas ; l'homonymie était un piège.
 *
 * Le nom du client passe en capitales espacées de labeur : c'est la
 * ligne de titre du registre, et elle doit se distinguer d'un simple
 * libellé d'application.
 */
.entete { border-bottom: 1px solid var(--texte); padding: env(safe-area-inset-top) 0 0 }

.entete-corps {
  max-width: var(--colonne);
  margin: 0 auto;
  padding: 18px var(--gouttiere) 16px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 20px;
}

/* Un enfant de flex refuse par défaut de rétrécir sous la largeur de son
 * contenu — « min-width:auto ». Le sélecteur de marché, dont l'option la
 * plus longue mesure 457 px, imposait donc sa largeur à sa colonne, et la
 * page entière glissait de côté sur un téléphone : 630 px dans une
 * fenêtre de 390. Le « max-width:100% » posé sur le menu ne pouvait rien,
 * puisqu'il se mesurait sur une colonne déjà débordée. */
.entete-corps > * { min-width: 0 }

.exploitant { display: flex; align-items: center; gap: 13px; min-width: 0 }

.client {
  font-family: var(--labeur);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: .045em;
  text-transform: uppercase;
  min-width: 0;
}

.client span {
  display: block;
  font-family: var(--courante);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--attenue);
  margin-top: 3px;
}

.compte { font-size: 13px; color: var(--attenue); text-align: right }
.compte form { display: inline }

.compte button, .compte a {
  background: none; border: 0; color: var(--attenue);
  font: inherit; font-size: 13px; text-decoration: underline;
  text-underline-offset: 2px; cursor: pointer; padding: 0;
}

.compte button:hover, .compte a:hover { color: var(--texte) }

.compte select {
  font: inherit; font-size: 13px; padding: 6px 8px;
  border: 1px solid var(--bord); border-radius: 6px;
  background: var(--fond); color: var(--texte); min-height: 36px;
  /* La largeur maximale n'est pas un détail : sans elle, le libellé le
   * plus long — « LOT-02 — Nettoyage des parties communes — Grands
   * Quartiers » — donnait 457 px au menu, et c'est la page entière qui
   * glissait de côté sur un téléphone, entête et contenu compris. Le
   * défaut ne se voyait que pour un compte suivant plusieurs marchés,
   * c'est-à-dire depuis que SCH en a quatre. */
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}

/* --- Les sections ---------------------------------------------------
 * Une entrée par exigence du CCTP — onze au total.
 *
 * Elles défilaient horizontalement dans un bandeau, avec un dégradé pour
 * signaler qu'il en restait à droite. Mesuré à 390 px : trois entrées
 * visibles sur onze. L'indice disait bien qu'il y avait autre chose,
 * mais il ne disait pas quoi, et une navigation qu'il faut balayer pour
 * découvrir n'est pas une navigation. Deux formes distinctes la
 * remplacent : le sommaire replié sur téléphone, la ligne qui passe à
 * la ligne sur écran large. Aucune entrée n'est plus cachée nulle part.
 */
.onglets { border-bottom: 1px solid var(--bord) }

.onglets-corps {
  max-width: var(--colonne);
  margin: 0 auto;
  padding: 0 var(--gouttiere);
  display: flex;
  flex-wrap: wrap;
  gap: 0 4px;
}

.onglets a {
  flex: none;
  padding: 11px 12px;
  min-height: 46px;
  display: flex;
  align-items: center;
  color: var(--attenue);
  text-decoration: none;
  font-size: 15px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
}

.onglets a:hover { color: var(--texte) }
.onglets a.actif { color: var(--texte); font-weight: 500; border-bottom-color: var(--accent) }

/* Le sommaire du téléphone. Replié, il annonce la section courante ;
 * ouvert, il montre les onze d'un coup, chacune sur une ligne pleine et
 * cliquable au pouce. Aucun script : un « details » suffit, et il
 * fonctionne même si le portail est consulté par un navigateur qui a
 * perdu son JavaScript en zone blanche. */
.sections { display: none; border-bottom: 1px solid var(--bord) }
.sections summary {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 52px; padding: 0 16px; cursor: pointer; list-style: none;
  font-weight: 600;
}
.sections summary::-webkit-details-marker { display: none }
.sections summary .ou {
  display: inline-flex; align-items: center; gap: 9px;
  font-weight: 400; font-size: 13px; color: var(--attenue);
}
.sections summary .chevron {
  display: block; width: 8px; height: 8px; flex: none; margin-bottom: 3px;
  border-right: 2px solid var(--attenue); border-bottom: 2px solid var(--attenue);
  transform: rotate(45deg); transition: transform .15s;
}
.sections[open] summary .chevron { transform: rotate(-135deg); margin: 3px 0 0 }
.sections-liste { display: flex; flex-direction: column }
.sections-liste a {
  display: flex; align-items: center; min-height: 48px; padding: 0 16px;
  border-top: 1px solid var(--bord); color: var(--texte); text-decoration: none;
}
.sections-liste a.actif { color: var(--accent); font-weight: 600 }

/* --- Le corps -------------------------------------------------------- */
main { max-width: var(--colonne); margin: 0 auto; padding: 28px var(--gouttiere) 0 }

h1 {
  font-family: var(--labeur);
  font-size: 30px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -.005em;
  margin: 0 0 4px;
}

h2 {
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .09em;
  color: var(--attenue); margin: 34px 0 0;
}

/* La ligne de sous-titre porte l'explication du chiffre affiché. Elle se
 * mesure en caractères, pas en pixels : au-delà d'une soixantaine, l'œil
 * perd la ligne suivante en revenant à gauche. */
.sous { color: var(--attenue); font-size: 15px; margin: 0 0 4px; max-width: 64ch }

/* Le bloc de registre. C'était une carte blanche à bord arrondi posée sur
 * du gris ; il en résultait que le bilan du jour, une ligne
 * d'intervention et un tableau de trente-quatre lignes avaient tous
 * exactement le même poids visuel. Un filet au-dessus suffit à séparer,
 * et laisse la hiérarchie au texte. */
.carte {
  background: none;
  border: 0;
  border-top: 1px solid var(--bord);
  border-radius: 0;
  padding: 18px 0;
  margin: 0;
  position: relative;
}

/* Un tableau porte déjà son propre filet de tête : le doubler ferait deux
 * traits à trois pixels d'écart. Le tableau est enveloppé d'une boîte
 * défilante dès que le script est passé — les deux cas se valent. */
.carte:has(> table:first-child),
.carte:has(> .defilant:first-child) { border-top: 0; padding-top: 4px }

/* Le premier bloc d'un écran ouvre sous la barre de sections, qui porte
 * déjà son filet : deux traits à vingt-huit pixels d'écart encadraient le
 * vide. Le filet du registre sépare, il n'ouvre pas. */
main > .carte:first-child,
main > .avis + .carte { border-top: 0; padding-top: 0 }

/* --- Les indicateurs du § 7 ------------------------------------------
 * Traités comme les colonnes d'un relevé : un filet franc au-dessus, le
 * chiffre en labeur, la légende dessous. Pas d'encadré — cinq encadrés
 * côte à côte, c'est cinq fois le même bruit autour de ce qui compte.
 */
.chiffres { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0 26px }

.chiffre {
  background: none;
  border: 0;
  border-top: 1px solid var(--texte);
  border-radius: 0;
  padding: 12px 0 18px;
}

.chiffre .valeur {
  font-family: var(--labeur);
  font-size: 34px;
  font-weight: 600;
  line-height: 1.1;
  font-variant-numeric: tabular-nums lining-nums;
  transition: color .15s;
}

.chiffre .nom { font-size: 13.5px; color: var(--texte); margin-top: 4px }
.chiffre .detail { font-size: 12.5px; color: var(--attenue); margin-top: 6px }

.chiffre .engagement {
  font-size: 12.5px; margin-top: 10px; padding-top: 8px;
  border-top: 1px solid var(--bord); color: var(--attenue);
}

.chiffre .engagement b { font-weight: 600 }
.chiffre .engagement .tenu { color: var(--ok-texte) }
.chiffre .engagement .manque { color: var(--alerte-texte) }

/* --- Les tableaux ----------------------------------------------------
 * Le registre proprement dit. Filet franc sous l'en-tête, filet léger
 * entre les lignes, chiffres en chasse fixe pour que deux dates ou deux
 * taux superposés s'alignent à la virgule près.
 */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}

th {
  text-align: left; font-weight: 600; font-size: 11.5px;
  text-transform: uppercase; letter-spacing: .06em; color: var(--attenue);
  padding: 0 10px 8px; border-bottom: 1px solid var(--texte);
}

th:first-child, td:first-child { padding-left: 0 }
th:last-child, td:last-child { padding-right: 0 }
td { padding: 12px 10px; border-bottom: 1px solid var(--bord); vertical-align: top }
tbody tr:last-child td { border-bottom: 0 }
tbody tr:hover td { background: var(--fond) }

/* Un tableau plus large que l'écran défile dans sa propre boîte.
 *
 * Il emmenait la page entière avec lui : « À traiter » débordait de 16
 * pixels sur les lieux, de 85 sur les constats, et c'est tout le
 * document qui glissait — entête comprise. Réservé au téléphone : sur un
 * écran large, la colonne pleine largeur reste la bonne mise en page.
 *
 * Le défilement était posé sur la table elle-même, et il ne s'enclenchait
 * jamais : une table en « display:block » se ramène à la largeur de son
 * parent et comprime ses colonnes plutôt que de déborder. Mesuré à
 * 390 px sur les lieux : « 6 rue des / Alouettes » sur deux lignes,
 * « ven. 11 / sept. à / 09h25 » sur trois, cinq lignes de haut pour une
 * seule entrée. Il faut deux boîtes — celle qui défile et celle qui
 * garde sa largeur — et c'est le script qui enveloppe la seconde. Sans
 * lui, on retombe sur les colonnes comprimées : lisible, simplement
 * moins bien. */
.defilant { position: relative }

@media (max-width: 760px) {
  .defilant { overflow-x: auto; -webkit-overflow-scrolling: touch }
  .defilant > table { min-width: 620px }
}

/* Sauf qu'un tableau sans en-tête n'est pas un tableau de chiffres :
 * c'est une liste de fiches — un constat, un blocage — mise en colonnes
 * parce que l'écran large s'y prête. Les faire défiler ne les sauve pas :
 * à 390 px, la description d'un constat se lisait en colonne de deux
 * mots, sur quinze lignes de haut, et la pastille d'état sortait du
 * cadre. Les lignes redeviennent alors ce qu'elles sont, des fiches
 * empilées, et la largeur redevient celle de l'écran.
 *
 * Les « !important » visent des largeurs et des alignements posés en
 * attribut dans les partiels, qui n'ont de sens qu'en colonnes. Ils
 * tomberont avec eux quand ces écrans seront repris. */
@media (max-width: 640px) {
  .defilant > table:not(:has(thead)) { min-width: 0 }

  table:not(:has(thead)) tr {
    display: block; padding: 14px 0; border-bottom: 1px solid var(--bord);
  }
  table:not(:has(thead)) tr:last-child { border-bottom: 0 }

  table:not(:has(thead)) td {
    display: block; width: auto !important; text-align: left !important;
    padding: 0; border: 0;
  }
  table:not(:has(thead)) td + td { margin-top: 7px }
}

/* Le même indice de défilement que portait la barre d'onglets, posé sur
 * tout tableau qui déborde : rien à l'écran ne dit qu'il reste des
 * colonnes à droite, et un gardien ne fait pas glisser un tableau qui
 * semble complet. */
.tableau-indice {
  position: absolute; top: 0; right: 0; bottom: 1px; width: 24px;
  background: linear-gradient(to right, transparent, var(--carte) 70%);
  pointer-events: none; opacity: 0; transition: opacity .15s;
}

/* --- Les états -------------------------------------------------------
 * La couleur ne code jamais seule : chaque pastille porte son mot. Le
 * rayon est petit — une étiquette collée dans une marge, pas une bulle.
 */
.pastille {
  display: inline-block; padding: 2px 8px; border-radius: 4px;
  font-size: 12.5px; font-weight: 500; white-space: nowrap;
}

.ok { background: var(--ok-fond); color: var(--ok-texte) }
.attente { background: var(--attente-fond); color: var(--attente-texte) }
.alerte { background: var(--alerte-fond); color: var(--alerte-texte) }
.neutre { background: var(--neutre-fond); color: var(--neutre-texte) }

.attenue { color: var(--attenue); font-size: 14px }
.declare { display: block; font-size: 12px; opacity: .7 }

a { color: var(--accent); text-underline-offset: 2px }
a:hover { color: var(--accent-vif) }

/* --- Les preuves ----------------------------------------------------- */
.vignettes { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px }
.vignettes img {
  width: 72px; height: 72px; object-fit: cover;
  border-radius: var(--rayon-petit); border: 1px solid var(--bord); cursor: zoom-in;
}

.visionneuse {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .9); z-index: 100;
  display: none; align-items: center; justify-content: center; padding: 20px;
}
.visionneuse.ouverte { display: flex }
.visionneuse img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 6px }
.visionneuse .fermer {
  position: absolute; top: calc(12px + env(safe-area-inset-top)); right: 16px;
  width: 48px; height: 48px; border-radius: 50%; border: 0;
  background: rgba(255, 255, 255, .15); color: #fff; font-size: 26px; line-height: 1; cursor: pointer;
}

/* --- Les avis et les écrans vides ------------------------------------ */
.avis {
  border-radius: 6px; padding: 13px 16px; margin-bottom: 20px; font-size: 15px;
  border-left: 3px solid transparent;
}
.avis.reussi {
  background: var(--ok-fond); color: var(--ok-texte);
  border-left-color: var(--succes);
}

.vide { text-align: center; padding: 48px 20px; color: var(--attenue) }

/* --- Les boutons -----------------------------------------------------
 * Rien de cliquable sous 44 pixels : un gardien lit ce portail debout,
 * dehors, le téléphone à bout de bras.
 */
.bouton {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 0 18px; border-radius: 6px; border: 1px solid var(--texte);
  background: var(--texte); color: var(--fond); font: inherit; font-size: 15px;
  text-decoration: none; cursor: pointer;
}
.bouton:hover { color: var(--fond) }
.bouton.clair { background: none; color: var(--texte); border-color: var(--bord) }
.bouton.clair:hover { border-color: var(--texte); color: var(--texte) }
.bouton.desactive { opacity: .5; cursor: default }
.bouton.pleine { width: 100% }

/* Le choix du mois, sur les écrans qui en portent un. Les quatre boutons
 * n'étaient séparés que par l'espace entre deux balises du gabarit : ils
 * se touchaient dès que la police changeait de chasse, et passaient à la
 * ligne n'importe où sur un téléphone. */
.periode { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px }
.periode .bouton { font-size: 14px; min-height: 44px }

/* --- L'écran « Aujourd'hui » -----------------------------------------
 * Une date, un chiffre, une liste d'adresses. C'est l'écran qui ouvre le
 * portail neuf fois sur dix, et la seule question qu'il traite est
 * « est-ce que c'est passé chez moi ? ».
 *
 * Il était bâti à l'inverse : un grand nombre centré au milieu d'une
 * carte, en ambre ou en vert, et les adresses en dessous dans des pilules
 * grises. Le nombre attirait l'oeil, les adresses — la seule chose
 * actionnable de l'écran — étaient reléguées au rang de décor, et la
 * couleur du chiffre portait seule l'information d'état. Ici le chiffre
 * s'aligne à gauche comme un titre, la couleur passe dans une pastille
 * qui porte son mot, et les adresses forment un relevé.
 */
.jour {
  font-size: 12px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--attenue); margin: 0 0 10px;
}

.bilan {
  font-size: 46px;
  line-height: 1.05;
  font-variant-numeric: tabular-nums lining-nums;
  margin-bottom: 8px;
}

.sous .pastille { margin-right: 7px }

/* Le relevé des entrées qui restent. L'heure en premier, atténuée :
 * elle ordonne la tournée de l'agent et n'engage personne — l'engagement
 * porte sur la journée. L'adresse porte le lien, mais c'est la ligne
 * entière qui se clique : viser un nom de rue au pouce, debout devant un
 * immeuble, demande une cible plus large que le texte. */
.entrees { list-style: none; margin: 24px 0 0; padding: 0; border-top: 1px solid var(--texte) }

.entrees li {
  position: relative;
  display: grid; grid-template-columns: 76px 1fr 16px; align-items: center; gap: 12px;
  min-height: 52px; padding: 6px 0; border-bottom: 1px solid var(--bord);
}

.entrees .heure { font-size: 14px; color: var(--attenue); font-variant-numeric: tabular-nums }
.entrees a { color: var(--texte); text-decoration: none; font-weight: 500 }
.entrees a::after { content: ''; position: absolute; inset: 0 }
.entrees .aller { color: var(--bord); font-size: 20px; text-align: right; transition: color .15s }
.entrees li:hover { background: var(--fond) }
.entrees li:hover a { color: var(--accent) }
.entrees li:hover .aller { color: var(--accent) }

.reste { margin: 14px 0 0 }

/* Le chemin de retour vers la liste, au-dessus du titre de la fiche. */
.retour-liste { color: var(--attenue); font-size: 14px; margin: 0 0 8px }

/* Ce que dit une ligne sous son en-tête : la description d'un blocage,
 * la phrase qui suit un bouton. */
.description { margin: 6px 0 0 }

/* Le formulaire posé dans une ligne de tableau : la vérification d'une
 * levée de constat. Il se détache de ce qu'il commente sans devenir un
 * bloc à part. */
.dans-la-ligne { margin: 10px 0 0 }
.dans-la-ligne .question { color: var(--attenue); margin-bottom: 8px }
.dans-la-ligne .champ { margin-bottom: 8px }
.agir { margin: 20px 0 0 }

/* Ce qui se passe à la minute où l'on regarde — § 3.2 d du CCTP. Le
 * mot « en cours » accompagne la pastille : la couleur seule ne dit rien
 * à qui distingue mal le vert. */
.encours { list-style: none; margin: 12px 0 0; padding: 0; border-top: 1px solid var(--bord) }

.encours li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
  padding: 13px 0; border-bottom: 1px solid var(--bord);
}

.encours li:last-child { border-bottom: 0 }
.encours .attenue { font-size: 14px }

/* Ce qui attend une action du donneur d'ordre. Le bloc entier est la
 * cible ; le liseré dit qu'il faut cliquer, et le fond reste discret —
 * un aplat rouge pleine largeur crie au même volume qu'il y ait un
 * blocage ou vingt. */
.attention { display: flex; flex-direction: column; gap: 10px; margin-top: 14px }

.alerte-ligne {
  display: block; padding: 13px 16px;
  border-left: 3px solid var(--alerte); border-radius: 0 6px 6px 0;
  background: var(--alerte-fond); color: var(--alerte-texte); text-decoration: none;
  transition: background .15s;
}

.alerte-ligne strong { display: block; font-weight: 600 }
.alerte-ligne .attenue {
  display: block; color: var(--alerte-texte); opacity: .85; font-size: 14px; margin-top: 2px;
}
.alerte-ligne:hover {
  background: color-mix(in srgb, var(--alerte) 12%, var(--alerte-fond));
  color: var(--alerte-texte);
}

/* --- La courbe d'évolution -------------------------------------------
 * Une ligne, pas des colonnes. Le taux de réalisation vit entre 93 et
 * 100 : des colonnes partant de zéro y avaient toutes la même hauteur, et
 * une colonne ne peut pas partir d'ailleurs sans mentir sur les
 * proportions. Une ligne ne prétend comparer que des écarts, elle admet
 * donc une échelle tronquée — à condition que ses bornes soient écrites,
 * ce qui est fait de part et d'autre du cadre.
 *
 * Le seuil est le seul trait pointillé de l'écran. Les axes sont des
 * filets pleins : un pointillé se lit comme « limite » et n'a pas à être
 * dépensé pour une grille.
 */
.courbe { margin: 14px 0 0; padding-left: 52px }

.cadre {
  position: relative;
  height: 180px;
  border-top: 1px solid var(--bord);
  border-bottom: 1px solid var(--bord);
}

.borne {
  position: absolute; left: -52px; width: 44px; text-align: right;
  font-size: 11.5px; color: var(--attenue); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.borne.haute { top: -8px }
.borne.basse { bottom: -8px }

/* L'engagement du contrat, à sa hauteur exacte. C'est la seule ligne que
 * le lecteur cherche : elle dit d'un coup d'oeil quels mois sont au-dessus
 * de la promesse et lesquels sont en dessous. */
.seuil {
  position: absolute; left: 0; right: 0;
  border-top: 1px dashed var(--attente);
  pointer-events: none;
}

.seuil span {
  position: absolute; right: 0; top: -9px;
  padding: 0 6px; background: var(--carte);
  font-size: 11.5px; color: var(--attente-texte); white-space: nowrap;
}

.trace { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible }

.trace polyline {
  fill: none; stroke: var(--accent); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
}

/* Le point porte l'état : plein au-dessus de l'engagement, creux en
 * dessous. La forme dit ce que dit la couleur — un lecteur sur douze
 * distingue mal le rouge du vert, et une imprimante ne peint pas les
 * aplats. L'anneau de surface détache le point du trait qui passe
 * dessous. */
.point {
  position: absolute; width: 11px; height: 11px; margin: 0 0 -5.5px -5.5px;
  border-radius: 50%; box-shadow: 0 0 0 2px var(--carte);
  print-color-adjust: exact; -webkit-print-color-adjust: exact;
}

.point.tenu { background: var(--succes) }
.point.manque { background: var(--carte); border: 2px solid var(--alerte) }

/* Onze pixels se voient bien et se visent mal : la cible s'étend sous le
 * doigt sans que le point grossisse. */
.point::after { content: ''; position: absolute; inset: -9px; border-radius: 50% }

/* Ce que le point dit à un lecteur d'écran, qui ne voit ni sa couleur ni
 * sa place dans le cadre. */
.lecture {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.mois { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; margin-top: 12px }

.mois-colonne {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 8px 2px; min-height: 46px; border-radius: 4px;
  text-decoration: none; color: inherit;
}

.mois-colonne:hover { background: var(--fond) }
.valeur-mois { font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums }
.nom-mois { font-size: 12px; color: var(--attenue) }

figcaption { margin: 16px 0 0; font-size: 13px; color: var(--attenue); max-width: 68ch }

/* --- Les lieux -------------------------------------------------------
 * Cent six entrées rangées rue par rue. Le sommaire en tête est la vraie
 * navigation de l'écran : sans lui, trouver la rue des Mésanges demande
 * de parcourir douze écrans de tableau.
 */
.rues { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px }

.rues a {
  align-items: center; display: inline-flex; gap: 7px;
  min-height: 38px; padding: 0 12px;
  border: 1px solid var(--bord); border-radius: 6px;
  color: var(--texte); text-decoration: none; font-size: 14px;
}

.rues a:hover { border-color: var(--texte); color: var(--texte) }
.rues a span { color: var(--attenue); font-size: 12.5px; font-variant-numeric: tabular-nums }

/* L'intertitre d'une rue dans le registre. Il se distingue d'une ligne
 * de données sans crier : même filet, un fond de creux, le nom en
 * demi-gras. « scroll-margin-top » le décolle du haut de la fenêtre quand
 * on y arrive par le sommaire — sinon la première entrée de la rue se
 * retrouve sous le bord de l'écran. */
tr.rue th {
  background: var(--fond);
  border-bottom: 1px solid var(--bord);
  color: var(--texte);
  font-size: 13.5px; font-weight: 600; letter-spacing: 0; text-transform: none;
  padding: 9px 10px;
  scroll-margin-top: 8px;
}

tr.rue th span { color: var(--attenue); font-weight: 400; margin-left: 8px }
tr.rue { scroll-margin-top: 8px }

/* Un lien qui porte une donnée, pas une invitation : l'adresse d'une
 * ligne de tableau, le numéro d'un bon. Il se lit comme le reste de sa
 * colonne — une colonne entière en bleu souligné cesse de se lire — mais
 * il garde un trait, de la couleur d'un filet.
 *
 * Sans ce trait il n'y a plus de lien : rien, ni forme ni couleur, ne
 * distingue l'adresse cliquable de la ville qui la suit. C'est le défaut
 * qu'on répare en enlevant les soulignements « pour faire propre ». */
.entree, .discret {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--bord);
  text-underline-offset: 3px;
}

.entree { color: var(--texte); font-weight: 500 }

.entree:hover, .discret:hover {
  color: var(--accent);
  text-decoration-color: currentColor;
}

/* L'encre d'un chiffre qui porte un état : des lieux sous le seuil, des
 * passages non réalisés, des passages faits. Elle ne se pose que sur un
 * compte non nul — « 0 » en rouge dit le contraire de ce qu'il vaut — et
 * le libellé sous le chiffre nomme toujours ce que la couleur signale.
 *
 * « background: none » parce que ces trois noms servent aussi de
 * pastilles, où ils portent un aplat : sur un chiffre de trente-quatre
 * pixels, l'aplat ferait un pavé. */
.chiffre .valeur.alerte { background: none; color: var(--alerte-texte) }
.chiffre .valeur.ok { background: none; color: var(--ok-texte) }
.chiffre .valeur.attente { background: none; color: var(--attente-texte) }

/* L'écran vide qui est une bonne nouvelle — « aucun manquement sur la
 * période » — le dit de l'encre qui va avec. */
.vide.ok { background: none; color: var(--ok-texte) }

/* La phrase de bas d'écran : ce que l'application fait de ce qu'elle
 * montre. Elle se lit après le tableau, pas à sa place. */
.apres { margin: 20px 0 0; max-width: 68ch }

/* --- Une ligne qui redevient une fiche --------------------------------
 * Quatre colonnes tiennent sur un écran large et nulle part ailleurs : à
 * 390 px, chacune tombe sous quatre-vingts pixels, l'adresse se coupe en
 * deux et la date en trois. Les tables marquées « fiches » abandonnent
 * alors leurs colonnes — chaque valeur reprend le nom de la sienne, qui
 * vient du gabarit par « data-libelle ».
 *
 * C'est le même remède que pour les tableaux sans en-tête, par un autre
 * chemin : ceux-là déduisent leur forme de l'absence de « thead », ceux-ci
 * la déclarent, parce qu'ils ont des colonnes à nommer.
 */
@media (max-width: 640px) {
  table.fiches, table.fiches tbody, table.fiches tr, table.fiches td { display: block }
  table.fiches thead { display: none }
  .defilant > table.fiches { min-width: 0 }

  table.fiches tr {
    border-bottom: 1px solid var(--bord);
    padding: 12px 0;
  }

  table.fiches tbody tr:last-child { border-bottom: 0 }

  /* Le libellé flotte à gauche, la valeur s'aligne à droite. Un « flex »
   * aurait paru plus simple, mais une cellule qui porte deux choses — la
   * date et le nom de l'agent dessous — voit ses deux nœuds devenir deux
   * colonnes : la date à gauche, l'agent à droite, séparés par le vide.
   * Le flottant laisse le contenu couler normalement sous lui. */
  table.fiches td {
    border: 0;
    display: block;
    padding: 3px 0;
    text-align: right;
  }

  table.fiches td::before {
    color: var(--attenue);
    content: attr(data-libelle);
    float: left;
    font-size: 12.5px;
    padding-right: 14px;
    text-align: left;
  }

  /* La première cellule porte l'adresse, qui se nomme elle-même. */
  table.fiches td:first-child { padding-bottom: 8px; text-align: left }
  table.fiches td:first-child::before { content: none }

  /* Une phrase ne se lit pas alignée à droite : sur trois lignes, l'oeil
   * perd le début de la suivante. Les cellules qui portent du texte —
   * une suite donnée, une action décidée — passent leur libellé au-dessus
   * et reprennent le fil à gauche. */
  table.fiches td.texte { text-align: left }

  table.fiches td.texte::before {
    display: block;
    float: none;
    padding: 4px 0 2px;
  }

  /* Les preuves suivent l'alignement de leur cellule. */
  table.fiches .vignettes { justify-content: flex-end }
  table.fiches td.texte .vignettes,
  table.fiches td:first-child .vignettes { justify-content: flex-start }

  table.fiches tr.rue { padding: 0 }
  table.fiches tr.rue th { display: block; padding: 10px }
}

/* --- Les champs ------------------------------------------------------
 * Le portail n'en compte que trois : la recherche des lieux, le dépôt
 * d'un constat, le choix du marché. Ils étaient décrits en attribut dans
 * chaque écran, avec quatre valeurs recopiées à chaque fois — un rayon
 * de 11 px ici, de 8 px là, selon la date d'écriture de l'écran. Un
 * formulaire qui sert à porter une réclamation contractuelle doit avoir
 * la même tenue partout.
 *
 * Le champ est un creux dans le papier : fond gris, filet léger, angle
 * peu marqué. C'est l'inverse d'un bloc de registre, et c'est voulu — on
 * doit voir du premier coup d'œil ce qui se lit et ce qui s'écrit.
 */
.champ {
  width: 100%;
  min-height: 48px;
  padding: 11px 13px;
  font: inherit;
  color: var(--texte);
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: 6px;
}

textarea.champ { resize: vertical; line-height: 1.5 }

.champ:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

/* L'étiquette nomme ce qu'on écrit ; elle ne concurrence pas la valeur
 * saisie, d'où le gris et la petite taille. Deux formes pour le même
 * objet : le label qui enveloppe son champ, et le bloc « saisie » qui les
 * pose côte à côte avec le message d'erreur. Les écrans hérités du
 * gabarit de Breeze — la porte, « mon compte » — emploient la seconde,
 * parce qu'ils lient le label au champ par « for » et qu'ils ont une
 * erreur à loger dessous. */
.etiquette { display: block; margin-bottom: 18px }
.etiquette > span {
  display: block; font-size: 13.5px; color: var(--attenue); margin-bottom: 6px;
}

.saisie + .saisie { margin-top: 16px }

.saisie label {
  display: block; font-size: 13.5px; color: var(--attenue); margin-bottom: 6px;
}

/* L'erreur se place sous le champ qu'elle concerne, et elle est écrite en
 * toutes lettres : « ces identifiants ne correspondent à aucun compte »
 * se comprend, « erreur de validation » non. */
.erreur { color: var(--alerte-texte); font-size: 13.5px; margin: 6px 0 0 }

.recherche { display: flex; gap: 10px; align-items: stretch }
.recherche .champ { flex: 1; min-width: 0 }
.recherche .bouton { flex: none }

/* Le formulaire de constat tient dans une colonne étroite : une ligne de
 * saisie longue de mille pixels se remplit mal, et ce qu'on écrit là
 * devient une pièce du dossier. */
.formulaire { max-width: 620px }

.avis.echec {
  background: var(--alerte-fond); color: var(--alerte-texte);
  border-left-color: var(--alerte);
}

/* --- Le cartouche ----------------------------------------------------
 * Le pied de page d'une pièce : ce que montre l'écran, pour quel marché,
 * arrêté à quelle date. La date est à la forme contractuelle complète —
 * 16/09/2026 à 11 h 42 — parce que c'est celle qu'on recopie dans un
 * courriel ou qu'on imprime en annexe d'un constat.
 *
 * L'export s'y tient aussi. Le mémoire § 2.3 relie cette capacité à la
 * neutralisation de la pénalité de vingt euros par jour ouvré de
 * l'article 20 du CCAP : « le délai ne dépend pas de notre
 * disponibilité ». Un donneur d'ordre qui télécharge lui-même n'attend
 * personne.
 */
/* Deux boîtes, et c'est nécessaire : le filet doit commencer où commence
 * le texte. Posé sur la boîte qui porte la gouttière, il dépassait de
 * 24 px de chaque côté et ne s'alignait avec aucun autre trait de
 * l'écran — le défaut se voit au premier coup d'œil sur un tableau. */
.cartouche {
  max-width: var(--colonne);
  margin: 44px auto 0;
  padding: 0 var(--gouttiere) 52px;
}

.cartouche-corps {
  border-top: 1px solid var(--texte);
  padding-top: 18px;
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 20px; flex-wrap: wrap;
  font-size: 13px; color: var(--attenue);
}

.cartouche .piece { max-width: 56ch }
.cartouche .piece strong { color: var(--texte); font-weight: 600 }
.cartouche .arrete { display: block; margin-top: 3px; font-variant-numeric: tabular-nums }
.cartouche .bouton { font-size: 14px; min-height: 44px }
.cartouche .rien { display: block; margin-top: 8px; text-align: right }

/* « 10 contrôles affichés sur 36 », et le lien pour voir le reste. Une
 * liste coupée qui ne dit pas qu'elle l'est laisse croire à un parc peu
 * contrôlé. */
.affiches { margin: 12px 0 0; font-size: 14px; color: var(--attenue) }

/* Le second export, celui que posent les écrans à deux tableaux : le
 * cartouche ne connaît que le principal. */
.exporter { margin: 22px 0 0 }
.exporter .bouton { font-size: 14px; min-height: 44px }
.exporter .attenue { display: block; margin-top: 8px }

/* Cinq indicateurs sur quatre colonnes laissent le cinquième seul sur sa
 * ligne, à côté d'un vide de trois colonnes. Trois colonnes en donnent
 * 3 + 2 : deux lignes pleines, et la lecture reste horizontale. */
@media (max-width: 900px) {
  .chiffres { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) }
}

@media (max-width: 760px) {
  .onglets { display: none }
  .sections { display: block }
}

@media (max-width: 640px) {
  :root { --gouttiere: 16px }

  /* Sur un téléphone, l'entête ne tient pas sur une ligne : les deux blocs
   * se partagent 390 px, et il ne reste au client que la moitié. Mesuré à
   * cette largeur, son bloc occupait 155 px de haut — le nom sur trois
   * lignes, le libellé du marché sur trois autres — et le sélecteur
   * n'affichait qu'un libellé tronqué.
   *
   * Empilés, ils prennent chacun toute la largeur : le bloc client tombe à
   * 70 px, le libellé du marché tient sur une ligne, et le sélecteur montre
   * son texte entier. L'entête gagne dix pixels au total et devient
   * lisible — c'est le bon échange sur un écran qu'on consulte debout,
   * dans une cage d'escalier. */
  .entete-corps { display: block }
  .client { font-size: 15px }
  .compte { text-align: left; margin-top: 12px }

  main { padding-top: 22px }
  h1 { font-size: 26px }
  .bilan { font-size: 38px }
  .courbe { padding-left: 46px }
  .cadre { height: 150px }
  .borne { left: -46px; width: 40px }
  .seuil .long { display: none }
  .valeur-mois { font-size: 12px }
  .nom-mois { font-size: 11px }
  .entrees li { grid-template-columns: 62px 1fr 14px; gap: 10px }
  .chiffres { gap: 0 20px }
  .chiffre .valeur { font-size: 28px }
  table { font-size: 14px }
  th { padding: 0 8px 8px } td { padding: 11px 8px }
  .cartouche { padding-bottom: 40px }
  .cartouche .rien { text-align: left }
}

/* --- Le papier -------------------------------------------------------
 * Un écran du portail s'imprime et se range : c'est le geste qu'attend un
 * responsable qualité avant une réunion de chantier. Ce qui n'a pas de
 * sens sur papier disparaît — la navigation, le bouton d'export, le
 * sélecteur de marché — et le cartouche, lui, reste : c'est ce qui date
 * la pièce.
 *
 * Les liens perdent leur bleu : l'encre couleur coûte, et un souligné
 * dans un tableau imprimé se lit comme une rature.
 */
@media print {
  /* Un bouton est une action, et une feuille de papier n'en offre
   * aucune : « Consulter », « Chercher », « Exporter cet écran » ne
   * laissent qu'un cadre vide au lecteur. Les liens, eux, restent —
   * ils disent au moins qu'une suite existe. */
  .onglets, .sections, .compte form, .bouton, .cartouche .rien,
  .visionneuse, .exporter { display: none !important }

  html, body { background: #fff; font-size: 10.5pt }
  main { padding-top: 12px }

  /* Une feuille A4 offre moins de largeur qu'un écran : la pastille
   * d'état, insécable, s'y réservait deux cents pixels et la colonne de
   * description tombait à deux mots par ligne. Sur papier, l'état se
   * replie sur deux lignes — personne ne le cherche en diagonale. */
  .pastille { white-space: normal }
  td { padding: 8px }

  /* La marque garde son encre. Le carré est un aplat sombre au sigle
   * clair : sans cette exception, une imprimante qui ne peint pas les
   * fonds rend un sigle blanc sur du blanc, et la pièce sort sans dire
   * qui l'a produite. La propriété s'hérite, la poser sur le bloc
   * suffit. */
  .exploitant > span:first-child { print-color-adjust: exact; -webkit-print-color-adjust: exact }
  a { color: inherit; text-decoration: none }
  /* « break-inside » sur le bloc entier renvoyait un tableau de trente
   * lignes à la page suivante et laissait une demi-page blanche : un
   * registre s'interrompt au bas d'une feuille et reprend en haut de
   * la suivante. Seule la ligne reste insécable — une entrée coupée en
   * deux par un saut de page ne se relit pas. */
  tr, .chiffre { break-inside: avoid }

  /* L'intertitre d'une rue se distinguait par son fond de creux, et une
   * imprimante ne peint pas les fonds : sur le papier, il redevenait une
   * ligne comme les autres au milieu de cent six. Un filet le tient. */
  tr.rue th { background: none; border-top: 1px solid #000 }

  /* Le sommaire des rues renvoie à des ancres : sur papier, il ne mène
   * nulle part. Le champ de recherche non plus — un cadre vide au milieu
   * d'une pièce imprimée ne fait que poser une question. */
  .rues, .recherche { display: none }
  h1, h2 { break-after: avoid }
  thead { display: table-header-group }
  .entete, .cartouche { border-color: #000 }
}
